mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-08-13 01:26:58 +00:00
add iksemel to in tree libs
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3756 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
662
libs/iksemel/src/iks.c
Normal file
662
libs/iksemel/src/iks.c
Normal file
@@ -0,0 +1,662 @@
|
||||
/* iksemel (XML parser for Jabber)
|
||||
** Copyright (C) 2000-2004 Gurer Ozen <madcat@e-kolay.net>
|
||||
** This code is free software; you can redistribute it and/or
|
||||
** modify it under the terms of GNU Lesser General Public License.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "iksemel.h"
|
||||
|
||||
#define IKS_COMMON \
|
||||
struct iks_struct *next, *prev; \
|
||||
struct iks_struct *parent; \
|
||||
enum ikstype type; \
|
||||
ikstack *s
|
||||
|
||||
struct iks_struct {
|
||||
IKS_COMMON;
|
||||
};
|
||||
|
||||
struct iks_tag {
|
||||
IKS_COMMON;
|
||||
struct iks_struct *children, *last_child;
|
||||
struct iks_struct *attribs, *last_attrib;
|
||||
char *name;
|
||||
};
|
||||
|
||||
#define IKS_TAG_NAME(x) ((struct iks_tag *) (x) )->name
|
||||
#define IKS_TAG_CHILDREN(x) ((struct iks_tag *) (x) )->children
|
||||
#define IKS_TAG_LAST_CHILD(x) ((struct iks_tag *) (x) )->last_child
|
||||
#define IKS_TAG_ATTRIBS(x) ((struct iks_tag *) (x) )->attribs
|
||||
#define IKS_TAG_LAST_ATTRIB(x) ((struct iks_tag *) (x) )->last_attrib
|
||||
|
||||
struct iks_cdata {
|
||||
IKS_COMMON;
|
||||
char *cdata;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
#define IKS_CDATA_CDATA(x) ((struct iks_cdata *) (x) )->cdata
|
||||
#define IKS_CDATA_LEN(x) ((struct iks_cdata *) (x) )->len
|
||||
|
||||
struct iks_attrib {
|
||||
IKS_COMMON;
|
||||
char *name;
|
||||
char *value;
|
||||
};
|
||||
|
||||
#define IKS_ATTRIB_NAME(x) ((struct iks_attrib *) (x) )->name
|
||||
#define IKS_ATTRIB_VALUE(x) ((struct iks_attrib *) (x) )->value
|
||||
|
||||
/***** Node Creating & Deleting *****/
|
||||
|
||||
iks *
|
||||
iks_new (const char *name)
|
||||
{
|
||||
ikstack *s;
|
||||
iks *x;
|
||||
|
||||
s = iks_stack_new (sizeof (struct iks_tag) * 6, 256);
|
||||
if (!s) return NULL;
|
||||
x = iks_new_within (name, s);
|
||||
if (!x) {
|
||||
iks_stack_delete (s);
|
||||
return NULL;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_new_within (const char *name, ikstack *s)
|
||||
{
|
||||
iks *x;
|
||||
size_t len;
|
||||
|
||||
if (name) len = sizeof (struct iks_tag); else len = sizeof (struct iks_cdata);
|
||||
x = iks_stack_alloc (s, len);
|
||||
if (!x) return NULL;
|
||||
memset (x, 0, len);
|
||||
x->s = s;
|
||||
x->type = IKS_TAG;
|
||||
if (name) {
|
||||
IKS_TAG_NAME (x) = iks_stack_strdup (s, name, 0);
|
||||
if (!IKS_TAG_NAME (x)) return NULL;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_insert (iks *x, const char *name)
|
||||
{
|
||||
iks *y;
|
||||
|
||||
if (!x) return NULL;
|
||||
|
||||
y = iks_new_within (name, x->s);
|
||||
if (!y) return NULL;
|
||||
y->parent = x;
|
||||
if (!IKS_TAG_CHILDREN (x)) IKS_TAG_CHILDREN (x) = y;
|
||||
if (IKS_TAG_LAST_CHILD (x)) {
|
||||
IKS_TAG_LAST_CHILD (x)->next = y;
|
||||
y->prev = IKS_TAG_LAST_CHILD (x);
|
||||
}
|
||||
IKS_TAG_LAST_CHILD (x) = y;
|
||||
return y;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_insert_cdata (iks *x, const char *data, size_t len)
|
||||
{
|
||||
iks *y;
|
||||
|
||||
if(!x || !data) return NULL;
|
||||
if(len == 0) len = strlen (data);
|
||||
|
||||
y = IKS_TAG_LAST_CHILD (x);
|
||||
if (y && y->type == IKS_CDATA) {
|
||||
IKS_CDATA_CDATA (y) = iks_stack_strcat (x->s, IKS_CDATA_CDATA (y), IKS_CDATA_LEN (y), data, len);
|
||||
IKS_CDATA_LEN (y) += len;
|
||||
} else {
|
||||
y = iks_insert (x, NULL);
|
||||
if (!y) return NULL;
|
||||
y->type = IKS_CDATA;
|
||||
IKS_CDATA_CDATA (y) = iks_stack_strdup (x->s, data, len);
|
||||
if (!IKS_CDATA_CDATA (y)) return NULL;
|
||||
IKS_CDATA_LEN (y) = len;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_insert_attrib (iks *x, const char *name, const char *value)
|
||||
{
|
||||
iks *y;
|
||||
|
||||
if (!x) return NULL;
|
||||
|
||||
y = IKS_TAG_ATTRIBS (x);
|
||||
while (y) {
|
||||
if (strcmp (name, IKS_ATTRIB_NAME (y)) == 0) break;
|
||||
y = y->next;
|
||||
}
|
||||
if (NULL == y) {
|
||||
if (!value) return NULL;
|
||||
y = iks_stack_alloc (x->s, sizeof (struct iks_attrib));
|
||||
if (!y) return NULL;
|
||||
memset (y, 0, sizeof (struct iks_attrib));
|
||||
y->type = IKS_ATTRIBUTE;
|
||||
y->s = x->s;
|
||||
IKS_ATTRIB_NAME (y) = iks_stack_strdup (x->s, name, 0);
|
||||
if (!IKS_ATTRIB_NAME (y)) return NULL;
|
||||
y->parent = x;
|
||||
if (!IKS_TAG_ATTRIBS (x)) IKS_TAG_ATTRIBS (x) = y;
|
||||
if (IKS_TAG_LAST_ATTRIB (x)) {
|
||||
IKS_TAG_LAST_ATTRIB (x)->next = y;
|
||||
y->prev = IKS_TAG_LAST_ATTRIB (x);
|
||||
}
|
||||
IKS_TAG_LAST_ATTRIB (x) = y;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
IKS_ATTRIB_VALUE (y) = iks_stack_strdup (x->s, value, 0);
|
||||
if (!IKS_ATTRIB_VALUE (y)) return NULL;
|
||||
} else {
|
||||
if (y->next) y->next->prev = y->prev;
|
||||
if (y->prev) y->prev->next = y->next;
|
||||
if (IKS_TAG_ATTRIBS (x) == y) IKS_TAG_ATTRIBS (x) = y->next;
|
||||
if (IKS_TAG_LAST_ATTRIB (x) == y) IKS_TAG_LAST_ATTRIB (x) = y->prev;
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_insert_node (iks *x, iks *y)
|
||||
{
|
||||
y->parent = x;
|
||||
if (!IKS_TAG_CHILDREN (x)) IKS_TAG_CHILDREN (x) = y;
|
||||
if (IKS_TAG_LAST_CHILD (x)) {
|
||||
IKS_TAG_LAST_CHILD (x)->next = y;
|
||||
y->prev = IKS_TAG_LAST_CHILD (x);
|
||||
}
|
||||
IKS_TAG_LAST_CHILD (x) = y;
|
||||
return y;
|
||||
}
|
||||
|
||||
void
|
||||
iks_hide (iks *x)
|
||||
{
|
||||
iks *y;
|
||||
|
||||
if (!x) return;
|
||||
|
||||
if (x->prev) x->prev->next = x->next;
|
||||
if (x->next) x->next->prev = x->prev;
|
||||
y = x->parent;
|
||||
if (y) {
|
||||
if (IKS_TAG_CHILDREN (y) == x) IKS_TAG_CHILDREN (y) = x->next;
|
||||
if (IKS_TAG_LAST_CHILD (y) == x) IKS_TAG_LAST_CHILD (y) = x->prev;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
iks_delete (iks *x)
|
||||
{
|
||||
if (x) iks_stack_delete (x->s);
|
||||
}
|
||||
|
||||
/***** Node Traversing *****/
|
||||
|
||||
iks *
|
||||
iks_next (iks *x)
|
||||
{
|
||||
if (x) return x->next;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_next_tag (iks *x)
|
||||
{
|
||||
if (x) {
|
||||
while (1) {
|
||||
x = x->next;
|
||||
if (NULL == x) break;
|
||||
if (IKS_TAG == x->type) return x;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_prev (iks *x)
|
||||
{
|
||||
if (x) return x->prev;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_prev_tag (iks *x)
|
||||
{
|
||||
if (x) {
|
||||
while (1) {
|
||||
x = x->prev;
|
||||
if (NULL == x) break;
|
||||
if (IKS_TAG == x->type) return x;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_parent (iks *x)
|
||||
{
|
||||
if (x) return x->parent;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_root (iks *x)
|
||||
{
|
||||
if (x) {
|
||||
while (x->parent)
|
||||
x = x->parent;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_child (iks *x)
|
||||
{
|
||||
if (x && IKS_TAG == x->type) return IKS_TAG_CHILDREN (x);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_first_tag (iks *x)
|
||||
{
|
||||
if (x) {
|
||||
x = IKS_TAG_CHILDREN (x);
|
||||
while (x) {
|
||||
if (IKS_TAG == x->type) return x;
|
||||
x = x->next;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_attrib (iks *x)
|
||||
{
|
||||
if (x) return IKS_TAG_ATTRIBS (x);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_find (iks *x, const char *name)
|
||||
{
|
||||
iks *y;
|
||||
|
||||
if (!x) return NULL;
|
||||
y = IKS_TAG_CHILDREN (x);
|
||||
while (y) {
|
||||
if (IKS_TAG == y->type && IKS_TAG_NAME (y) && strcmp (IKS_TAG_NAME (y), name) == 0) return y;
|
||||
y = y->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
iks_find_cdata (iks *x, const char *name)
|
||||
{
|
||||
iks *y;
|
||||
|
||||
y = iks_find (x, name);
|
||||
if (!y) return NULL;
|
||||
y = IKS_TAG_CHILDREN (y);
|
||||
if (!y || IKS_CDATA != y->type) return NULL;
|
||||
return IKS_CDATA_CDATA (y);
|
||||
}
|
||||
|
||||
char *
|
||||
iks_find_attrib (iks *x, const char *name)
|
||||
{
|
||||
iks *y;
|
||||
|
||||
if (!x) return NULL;
|
||||
|
||||
y = IKS_TAG_ATTRIBS (x);
|
||||
while (y) {
|
||||
if (IKS_ATTRIB_NAME (y) && strcmp (IKS_ATTRIB_NAME (y), name) == 0)
|
||||
return IKS_ATTRIB_VALUE (y);
|
||||
y = y->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_find_with_attrib (iks *x, const char *tagname, const char *attrname, const char *value)
|
||||
{
|
||||
iks *y;
|
||||
|
||||
if (NULL == x) return NULL;
|
||||
|
||||
if (tagname) {
|
||||
for (y = IKS_TAG_CHILDREN (x); y; y = y->next) {
|
||||
if (IKS_TAG == y->type
|
||||
&& strcmp (IKS_TAG_NAME (y), tagname) == 0
|
||||
&& iks_strcmp (iks_find_attrib (y, attrname), value) == 0) {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (y = IKS_TAG_CHILDREN (x); y; y = y->next) {
|
||||
if (IKS_TAG == y->type
|
||||
&& iks_strcmp (iks_find_attrib (y, attrname), value) == 0) {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/***** Node Information *****/
|
||||
|
||||
ikstack *
|
||||
iks_stack (iks *x)
|
||||
{
|
||||
if (x) return x->s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enum ikstype
|
||||
iks_type (iks *x)
|
||||
{
|
||||
if (x) return x->type;
|
||||
return IKS_NONE;
|
||||
}
|
||||
|
||||
char *
|
||||
iks_name (iks *x)
|
||||
{
|
||||
if (x) {
|
||||
if (IKS_TAG == x->type)
|
||||
return IKS_TAG_NAME (x);
|
||||
else
|
||||
return IKS_ATTRIB_NAME (x);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
iks_cdata (iks *x)
|
||||
{
|
||||
if (x) {
|
||||
if (IKS_CDATA == x->type)
|
||||
return IKS_CDATA_CDATA (x);
|
||||
else
|
||||
return IKS_ATTRIB_VALUE (x);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t
|
||||
iks_cdata_size (iks *x)
|
||||
{
|
||||
if (x) return IKS_CDATA_LEN (x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iks_has_children (iks *x)
|
||||
{
|
||||
if (x && IKS_TAG == x->type && IKS_TAG_CHILDREN (x)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iks_has_attribs (iks *x)
|
||||
{
|
||||
if (x && IKS_TAG == x->type && IKS_TAG_ATTRIBS (x)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***** Serializing *****/
|
||||
|
||||
static size_t
|
||||
escape_size (char *src, size_t len)
|
||||
{
|
||||
size_t sz;
|
||||
char c;
|
||||
int i;
|
||||
|
||||
sz = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
c = src[i];
|
||||
switch (c) {
|
||||
case '&': sz += 5; break;
|
||||
case '\'': sz += 6; break;
|
||||
case '"': sz += 6; break;
|
||||
case '<': sz += 4; break;
|
||||
case '>': sz += 4; break;
|
||||
default: sz++; break;
|
||||
}
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
static char *
|
||||
my_strcat (char *dest, char *src, size_t len)
|
||||
{
|
||||
if (0 == len) len = strlen (src);
|
||||
memcpy (dest, src, len);
|
||||
return dest + len;
|
||||
}
|
||||
|
||||
static char *
|
||||
escape (char *dest, char *src, size_t len)
|
||||
{
|
||||
char c;
|
||||
int i;
|
||||
int j = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
c = src[i];
|
||||
if ('&' == c || '<' == c || '>' == c || '\'' == c || '"' == c) {
|
||||
if (i - j > 0) dest = my_strcat (dest, src + j, i - j);
|
||||
j = i + 1;
|
||||
switch (c) {
|
||||
case '&': dest = my_strcat (dest, "&", 5); break;
|
||||
case '\'': dest = my_strcat (dest, "'", 6); break;
|
||||
case '"': dest = my_strcat (dest, """, 6); break;
|
||||
case '<': dest = my_strcat (dest, "<", 4); break;
|
||||
case '>': dest = my_strcat (dest, ">", 4); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i - j > 0) dest = my_strcat (dest, src + j, i - j);
|
||||
return dest;
|
||||
}
|
||||
|
||||
char *
|
||||
iks_string (ikstack *s, iks *x)
|
||||
{
|
||||
size_t size;
|
||||
int level, dir;
|
||||
iks *y, *z;
|
||||
char *ret, *t;
|
||||
|
||||
if (!x) return NULL;
|
||||
|
||||
if (x->type == IKS_CDATA) {
|
||||
if (s) {
|
||||
return iks_stack_strdup (s, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
|
||||
} else {
|
||||
ret = iks_malloc (IKS_CDATA_LEN (x));
|
||||
memcpy (ret, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
size = 0;
|
||||
level = 0;
|
||||
dir = 0;
|
||||
y = x;
|
||||
while (1) {
|
||||
if (dir==0) {
|
||||
if (y->type == IKS_TAG) {
|
||||
size++;
|
||||
size += strlen (IKS_TAG_NAME (y));
|
||||
for (z = IKS_TAG_ATTRIBS (y); z; z = z->next) {
|
||||
size += 4 + strlen (IKS_ATTRIB_NAME (z))
|
||||
+ escape_size (IKS_ATTRIB_VALUE (z), strlen (IKS_ATTRIB_VALUE (z)));
|
||||
}
|
||||
if (IKS_TAG_CHILDREN (y)) {
|
||||
size++;
|
||||
y = IKS_TAG_CHILDREN (y);
|
||||
level++;
|
||||
continue;
|
||||
} else {
|
||||
size += 2;
|
||||
}
|
||||
} else {
|
||||
size += escape_size (IKS_CDATA_CDATA (y), IKS_CDATA_LEN (y));
|
||||
}
|
||||
}
|
||||
z = y->next;
|
||||
if (z) {
|
||||
if (0 == level) {
|
||||
if (IKS_TAG_CHILDREN (y)) size += 3 + strlen (IKS_TAG_NAME (y));
|
||||
break;
|
||||
}
|
||||
y = z;
|
||||
dir = 0;
|
||||
} else {
|
||||
y = y->parent;
|
||||
level--;
|
||||
if (level >= 0) size += 3 + strlen (IKS_TAG_NAME (y));
|
||||
if (level < 1) break;
|
||||
dir = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (s) ret = iks_stack_alloc (s, size + 1);
|
||||
else ret = iks_malloc (size + 1);
|
||||
|
||||
if (!ret) return NULL;
|
||||
|
||||
t = ret;
|
||||
level = 0;
|
||||
dir = 0;
|
||||
while (1) {
|
||||
if (dir==0) {
|
||||
if (x->type == IKS_TAG) {
|
||||
*t++ = '<';
|
||||
t = my_strcat (t, IKS_TAG_NAME (x), 0);
|
||||
y = IKS_TAG_ATTRIBS (x);
|
||||
while (y) {
|
||||
*t++ = ' ';
|
||||
t = my_strcat (t, IKS_ATTRIB_NAME (y), 0);
|
||||
*t++ = '=';
|
||||
*t++ = '\'';
|
||||
t = escape (t, IKS_ATTRIB_VALUE (y), strlen (IKS_ATTRIB_VALUE (y)));
|
||||
*t++ = '\'';
|
||||
y = y->next;
|
||||
}
|
||||
if (IKS_TAG_CHILDREN (x)) {
|
||||
*t++ = '>';
|
||||
x = IKS_TAG_CHILDREN (x);
|
||||
level++;
|
||||
continue;
|
||||
} else {
|
||||
*t++ = '/';
|
||||
*t++ = '>';
|
||||
}
|
||||
} else {
|
||||
t = escape (t, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
|
||||
}
|
||||
}
|
||||
y = x->next;
|
||||
if (y) {
|
||||
if (0 == level) {
|
||||
if (IKS_TAG_CHILDREN (x)) {
|
||||
*t++ = '<';
|
||||
*t++ = '/';
|
||||
t = my_strcat (t, IKS_TAG_NAME (x), 0);
|
||||
*t++ = '>';
|
||||
}
|
||||
break;
|
||||
}
|
||||
x = y;
|
||||
dir = 0;
|
||||
} else {
|
||||
x = x->parent;
|
||||
level--;
|
||||
if (level >= 0) {
|
||||
*t++ = '<';
|
||||
*t++ = '/';
|
||||
t = my_strcat (t, IKS_TAG_NAME (x), 0);
|
||||
*t++ = '>';
|
||||
}
|
||||
if (level < 1) break;
|
||||
dir = 1;
|
||||
}
|
||||
}
|
||||
*t = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***** Copying *****/
|
||||
|
||||
iks *
|
||||
iks_copy_within (iks *x, ikstack *s)
|
||||
{
|
||||
int level=0, dir=0;
|
||||
iks *copy = NULL;
|
||||
iks *cur = NULL;
|
||||
iks *y;
|
||||
|
||||
while (1) {
|
||||
if (dir == 0) {
|
||||
if (x->type == IKS_TAG) {
|
||||
if (copy == NULL) {
|
||||
copy = iks_new_within (IKS_TAG_NAME (x), s);
|
||||
cur = copy;
|
||||
} else {
|
||||
cur = iks_insert (cur, IKS_TAG_NAME (x));
|
||||
}
|
||||
for (y = IKS_TAG_ATTRIBS (x); y; y = y->next) {
|
||||
iks_insert_attrib (cur, IKS_ATTRIB_NAME (y), IKS_ATTRIB_VALUE (y));
|
||||
}
|
||||
if (IKS_TAG_CHILDREN (x)) {
|
||||
x = IKS_TAG_CHILDREN (x);
|
||||
level++;
|
||||
continue;
|
||||
} else {
|
||||
cur = cur->parent;
|
||||
}
|
||||
} else {
|
||||
iks_insert_cdata (cur, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
|
||||
}
|
||||
}
|
||||
y = x->next;
|
||||
if (y) {
|
||||
if (0 == level) break;
|
||||
x = y;
|
||||
dir = 0;
|
||||
} else {
|
||||
if (level < 2) break;
|
||||
level--;
|
||||
x = x->parent;
|
||||
cur = cur->parent;
|
||||
dir = 1;
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
iks *
|
||||
iks_copy (iks *x)
|
||||
{
|
||||
return iks_copy_within (x, iks_stack_new (sizeof (struct iks_tag) * 6, 256));
|
||||
}
|
Reference in New Issue
Block a user