1
0
Fork 0
forked from fte/fteqw
fteqw/plugins/jabber/xml.c
Spoike f08489f141 .po support for qc.
rewrote messagemode to support utf8 properly, as well as left-arrow etc keys.
support for mouse-over images on console links and stuff.
added r_lerpmuzzlehack for certain viewmodels.
use libtool's dlopen stuff on cygwin to try to cover some cygwin path differences.
try to support utf-8 in filenames even in windows (only in nt, 9x is still ascii only).
added certificate validation for gnutls. gnutls now enabled by default in linux.
d3d11 tweaks. shadowmapping works.
tweaks for updated terrain format, to try to fix some inefficiencies/limitations.
xmpp plugin can now display avatars (xmpp /set avatars 1)
xmpp file transfers supported by default, but capability is disabled by default (can be enabled by hacking config).

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4523 fc73d0e0-1445-4013-8a0c-d673dee63da5
2013-11-21 23:02:28 +00:00

699 lines
14 KiB
C

#include "../plugin.h"
#include "xml.h"
//fixme
void (*Con_TrySubPrint)(const char *conname, const char *message);
void XML_Destroy(xmltree_t *t);
char *XML_GetParameter(xmltree_t *t, char *paramname, char *def)
{
xmlparams_t *p;
if (t)
{
for (p = t->params; p; p = p->next)
if (!strcmp(p->name, paramname))
return p->val;
}
return def;
}
void XML_AddParameter(xmltree_t *t, char *paramname, char *value)
{
xmlparams_t *p = malloc(sizeof(xmlparams_t));
Q_strlcpy(p->name, paramname, sizeof(p->name));
Q_strlcpy(p->val, value, sizeof(p->val));
if (t->params) //reverse insert
{
xmlparams_t *prev;
for(prev = t->params; prev->next; prev = prev->next)
;
prev->next = p;
p->next = NULL;
}
else
{
p->next = t->params;
t->params = p;
}
}
void XML_AddParameteri(xmltree_t *t, char *paramname, int value)
{
char svalue[64];
Q_snprintf(svalue, sizeof(svalue), "%i", value);
XML_AddParameter(t, paramname, svalue);
}
xmltree_t *XML_CreateNode(xmltree_t *parent, char *name, char *xmlns, char *body)
{
int bodylen = strlen(body);
struct subtree_s *node = malloc(sizeof(*node));
//clear out links
node->params = NULL;
node->child = NULL;
node->sibling = NULL;
//link into parent if we actually have a parent.
if (parent)
{
if (parent->child)
{ //add at tail
xmltree_t *prev;
for(prev = parent->child; prev->sibling; prev = prev->sibling)
;
prev->sibling = node;
node->sibling = NULL;
}
else
{
node->sibling = parent->child;
parent->child = node;
}
}
Q_strlcpy(node->name, name, sizeof(node->name));
Q_strlcpy(node->xmlns, xmlns, sizeof(node->xmlns));
Q_strlcpy(node->xmlns_dflt, xmlns, sizeof(node->xmlns_dflt));
node->body = malloc(bodylen+1);
memcpy(node->body, body, bodylen+1);
if (*xmlns)
XML_AddParameter(node, "xmlns", xmlns);
return node;
}
const struct
{
char code;
int namelen;
char *name;
} xmlchars[] =
{
{'<', 2, "lt"},
{'>', 2, "gt"},
{'&', 3, "amp"},
{'\'', 4, "apos"},
{'\"', 4, "quot"},
{0, 0, NULL}
};
//converts < to &lt; etc.
//returns the end of d.
char *XML_Markup(char *s, char *d, int dlen)
{
int i;
dlen--;
while(*s)
{
for(i = 0; xmlchars[i].name; i++)
{
if (*s == xmlchars[i].code)
break;
}
if (xmlchars[i].name)
{
if (dlen < xmlchars[i].namelen+2)
break;
*d++ = '&';
memcpy(d, xmlchars[i].name, xmlchars[i].namelen);
d+=xmlchars[i].namelen;
*d++ = ';';
s++;
dlen -= xmlchars[i].namelen+2;
}
else
{
if (!dlen)
break;
dlen--;
*d++ = *s++;
}
}
*d = 0;
return d;
}
//inplace. result will always be same length or shorter.
//converts &lt; etc to their original chars
void XML_Unmark(char *s)
{
char *d;
int i;
for (d = s; *s; )
{
if (*s == '&')
{
s++;
for (i = 0; xmlchars[i].name; i++)
{
if (!strncmp(s, xmlchars[i].name, xmlchars[i].namelen) && s[xmlchars[i].namelen] == ';')
break;
}
if (xmlchars[i].name)
{
s += xmlchars[i].namelen+1;
*d++ = xmlchars[i].code;
}
else
{
*d++ = '&';
}
}
else
*d++ = *s++;
}
*d = 0;
}
struct buf_ctx
{
char *buf;
int len;
int maxlen;
};
static void buf_cat(struct buf_ctx *buf, char *data, int datalen)
{
int newlen = buf->len + datalen+1;
if (newlen > buf->maxlen)
{
char *newd;
newlen *= 2;
newd = malloc(newlen);
memcpy(newd, buf->buf, buf->len);
free(buf->buf);
buf->buf = newd;
buf->maxlen = newlen;
}
memcpy(buf->buf + buf->len, data, datalen);
buf->len += datalen;
}
static void XML_DumpToBuf(struct buf_ctx *buf, xmltree_t *t, int indent)
{
xmltree_t *c;
xmlparams_t *p;
int i;
for (i = 0; i < indent; i++)
buf_cat(buf, " ", 1);
buf_cat(buf, "<", 1);
buf_cat(buf, t->name, strlen(t->name));
for (p = t->params; p; p = p->next)
{
buf_cat(buf, " ", 1);
buf_cat(buf, p->name, strlen(p->name));
buf_cat(buf, "=\'", 2);
buf_cat(buf, p->val, strlen(p->val));
buf_cat(buf, "\'", 1);
}
if (t->child)
{
buf_cat(buf, ">", 1);
if (indent>=0)
buf_cat(buf, "\n", 1);
for (c = t->child; c; c = c->sibling)
XML_DumpToBuf(buf, c, ((indent<0)?indent:(indent+2)));
for (i = 0; i < indent; i++)
buf_cat(buf, " ", 1);
buf_cat(buf, "</", 2);
buf_cat(buf, t->name, strlen(t->name));
buf_cat(buf, ">", 1);
}
else if (*t->body)
{
buf_cat(buf, ">", 1);
buf_cat(buf, t->body, strlen(t->body));
buf_cat(buf, "</", 2);
buf_cat(buf, t->name, strlen(t->name));
buf_cat(buf, ">", 1);
}
else
{
buf_cat(buf, "/>", 2);
}
if (indent>=0)
buf_cat(buf, "\n", 1);
}
char *XML_GenerateString(xmltree_t *root, qboolean readable)
{
struct buf_ctx buf = {NULL, 0, 0};
XML_DumpToBuf(&buf, root, readable?0:-1);
buf_cat(&buf, "", 1);
return buf.buf;
}
xmltree_t *XML_Parse(char *buffer, int *startpos, int maxpos, qboolean headeronly, char *defaultnamespace)
{
xmlparams_t *p;
xmltree_t *child;
xmltree_t *ret;
int bodypos;
int bodymax = 0;
int pos, i;
char *tagend;
char *tagstart;
char *ns;
char token[1024];
pos = *startpos;
while (buffer[pos] >= '\0' && buffer[pos] <= ' ')
{
if (pos >= maxpos)
break;
pos++;
}
if (pos == maxpos)
{
*startpos = pos;
return NULL; //nothing anyway.
}
//expect a <
if (buffer[pos] != '<')
{
Con_Printf("Missing open bracket\n");
return NULL; //should never happen
}
if (buffer[pos+1] == '/')
{
Con_Printf("Unexpected close tag.\n");
return NULL; //err, terminating a parent tag
}
tagend = strchr(buffer+pos, '>');
if (!tagend)
{
Con_Printf("Missing close bracket\n");
return NULL; //should never happen
}
*tagend = '\0';
tagend++;
//assume no nulls in the tag header.
tagstart = buffer+pos+1;
while (*tagstart == ' ' || *tagstart == '\n' || *tagstart == '\r' || *tagstart == '\t')
tagstart++;
for (i = 0; i < sizeof(token)-1 && *tagstart; )
{
if (*tagstart == ' ' || (i&&*tagstart == '/') || (i&&*tagstart == '?') || *tagstart == '\n' || *tagstart == '\r' || *tagstart == '\t')
break;
token[i++] = *tagstart++;
}
token[i] = 0;
pos = tagend - buffer;
ret = malloc(sizeof(xmltree_t));
memset(ret, 0, sizeof(*ret));
ns = strchr(token, ':');
if (ns)
{
*ns = 0;
ns++;
memcpy(ret->xmlns, "xmlns:", 6);
Q_strlcpy(ret->xmlns+6, token, sizeof(ret->xmlns)-6);
Q_strlcpy(ret->name, ns, sizeof(ret->name));
}
else
{
Q_strlcpy(ret->xmlns, "xmlns", sizeof(ret->xmlns));
Q_strlcpy(ret->name, token, sizeof(ret->name));
}
while(*tagstart)
{
int nlen;
while(*(unsigned char*)tagstart <= ' ' && *tagstart)
tagstart++; //skip whitespace (note that we know there is a null terminator before the end of the buffer)
if (!*tagstart)
break;
p = malloc(sizeof(xmlparams_t));
nlen = 0;
while (nlen < sizeof(p->name)-2)
{
if (*(unsigned char*)tagstart <= ' ' || *tagstart == '/' || *tagstart == '?')
break;
if (*tagstart == '=')
break;
p->name[nlen++] = *tagstart++;
}
p->name[nlen++] = '\0';
while(*(unsigned char*)tagstart <= ' ' && *tagstart)
tagstart++; //skip whitespace (note that we know there is a null terminator before the end of the buffer)
if (*tagstart != '=')
break;
tagstart++;
while(*(unsigned char*)tagstart <= ' ' && *tagstart)
tagstart++; //skip whitespace (note that we know there is a null terminator before the end of the buffer)
nlen = 0;
if (*tagstart == '\'')
{
tagstart++;
while (*tagstart && nlen < sizeof(p->name)-2)
{
if(*tagstart == '\'')
break;
p->val[nlen++] = *tagstart++;
}
tagstart++;
p->val[nlen++] = '\0';
}
else if (*tagstart == '\"')
{
tagstart++;
while (*tagstart && nlen < sizeof(p->name)-2)
{
if(*tagstart == '\"')
break;
p->val[nlen++] = *tagstart++;
}
tagstart++;
p->val[nlen++] = '\0';
}
else
{
while (*tagstart && nlen < sizeof(p->name)-2)
{
if(*tagstart <= ' ')
break;
p->val[nlen++] = *tagstart++;
}
p->val[nlen++] = '\0';
}
XML_Unmark(p->val);
p->next = ret->params;
ret->params = p;
}
ns = XML_GetParameter(ret, ret->xmlns, "");
Q_strlcpy(ret->xmlns, ns, sizeof(ret->xmlns));
ns = XML_GetParameter(ret, "xmlns", NULL);
Q_strlcpy(ret->xmlns_dflt, ns?ns:defaultnamespace, sizeof(ret->xmlns_dflt));
tagend[-1] = '>';
if (tagend[-2] == '/')
{ //no body
ret->body = malloc(1);
*ret->body = 0;
*startpos = pos;
return ret;
}
if (ret->name[0] == '?')
{
//no body either
if (tagend[-2] == '?')
{
ret->body = malloc(1);
*ret->body = 0;
*startpos = pos;
return ret;
}
}
if (headeronly)
{
*startpos = pos;
return ret;
}
//does it have a body, or is it child tags?
bodypos = 0;
while(1)
{
if (pos == maxpos)
{ //malformed
Con_Printf("tree is malfored\n");
XML_Destroy(ret);
return NULL;
}
if (buffer[pos] == '<')
{
if (buffer[pos+1] == '/')
{ //the end of this block
//FIXME: check name
tagend = strchr(buffer+pos, '>');
if (!tagend)
{
Con_Printf("No close tag\n");
XML_Destroy(ret);
return NULL; //should never happen
}
tagend++;
pos = tagend - buffer;
break;
}
child = XML_Parse(buffer, &pos, maxpos, false, ret->xmlns_dflt);
if (!child)
{
Con_Printf("Child block is unparsable\n");
XML_Destroy(ret);
return NULL;
}
child->sibling = ret->child;
ret->child = child;
}
else
{
char c = buffer[pos++];
if (bodypos == bodymax)
{
int nlen = bodypos*2 + 64;
char *nb = malloc(nlen);
memcpy(nb, ret->body, bodypos);
free(ret->body);
ret->body = nb;
bodymax = nlen;
}
ret->body[bodypos++] = c;
}
}
if (bodypos == bodymax)
{
int nlen = bodypos+1;
char *nb = malloc(nlen);
memcpy(nb, ret->body, bodypos);
free(ret->body);
ret->body = nb;
bodymax = nlen;
}
ret->body[bodypos++] = '\0';
XML_Unmark(ret->body);
*startpos = pos;
return ret;
}
void XML_Destroy(xmltree_t *t)
{
xmlparams_t *p, *np;
if (t->child)
XML_Destroy(t->child);
if (t->sibling)
XML_Destroy(t->sibling);
for (p = t->params; p; p = np)
{
np = p->next;
free(p);
}
free(t->body);
free(t);
}
xmltree_t *XML_ChildOfTree(xmltree_t *t, char *name, int childnum)
{
if (t)
{
for (t = t->child; t; t = t->sibling)
{
if (!strcmp(t->name, name))
{
if (childnum-- == 0)
return t;
}
}
}
return NULL;
}
xmltree_t *XML_ChildOfTreeNS(xmltree_t *t, char *xmlns, char *name, int childnum)
{
if (t)
{
for (t = t->child; t; t = t->sibling)
{
if (!strcmp(t->xmlns, xmlns) && !strcmp(t->name, name))
{
if (childnum-- == 0)
return t;
}
}
}
return NULL;
}
char *XML_GetChildBody(xmltree_t *t, char *paramname, char *def)
{
xmltree_t *c = XML_ChildOfTree(t, paramname, 0);
if (c)
return c->body;
return def;
}
void XML_ConPrintTree(xmltree_t *t, char *subconsole, int indent)
{
int start, c, chunk;
struct buf_ctx buf = {NULL, 0, 0};
XML_DumpToBuf(&buf, t, indent);
buf_cat(&buf, "", 1);
for (start = 0; start < buf.len; )
{
chunk = buf.len - start;
if (chunk > 128)
chunk = 128;
c = buf.buf[start+chunk];
buf.buf[start+chunk] = 0;
Con_TrySubPrint(subconsole, buf.buf+start);
buf.buf[start+chunk] = c;
start += chunk;
}
free(buf.buf);
}
static void XML_SkipWhite(char *msg, int *pos, int max)
{
while (*pos < max && (
msg[*pos] == ' ' ||
msg[*pos] == '\t' ||
msg[*pos] == '\r' ||
msg[*pos] == '\n'
))
*pos+=1;
}
static qboolean XML_ParseString(char *msg, int *pos, int max, char *out, int outlen)
{
*out = 0;
if (*pos < max && msg[*pos] == '\"')
{
*pos+=1;
outlen--;
while (*pos < max && msg[*pos] != '\"')
{
if (!outlen)
return false;
*out = msg[*pos];
out++;
outlen--;
*pos+=1;
}
if (*pos < max && msg[*pos] == '\"')
{
*out = 0;
*pos+=1;
return true;
}
}
else
{
outlen--;
while (*pos < max
&& msg[*pos] != ' '
&& msg[*pos] != '\t'
&& msg[*pos] != '\r'
&& msg[*pos] != '\n'
&& msg[*pos] != ':'
&& msg[*pos] != ','
&& msg[*pos] != '}'
&& msg[*pos] != '{')
{
if (!outlen)
return false;
*out = msg[*pos];
out++;
outlen--;
*pos+=1;
}
*out = 0;
return true;
}
return false;
}
xmltree_t *XML_FromJSON(xmltree_t *t, char *name, char *json, int *jsonpos, int jsonlen)
{
char child[4096];
XML_SkipWhite(json, jsonpos, jsonlen);
if (*jsonpos < jsonlen && json[*jsonpos] == '{')
{
*jsonpos+=1;
XML_SkipWhite(json, jsonpos, jsonlen);
t = XML_CreateNode(t, name, "", "");
while (*jsonpos < jsonlen && json[*jsonpos] == '\"')
{
if (!XML_ParseString(json, jsonpos, jsonlen, child, sizeof(child)))
break;
XML_SkipWhite(json, jsonpos, jsonlen);
if (*jsonpos < jsonlen && json[*jsonpos] == ':')
{
*jsonpos+=1;
if (!XML_FromJSON(t, child, json, jsonpos, jsonlen))
break;
}
XML_SkipWhite(json, jsonpos, jsonlen);
if (*jsonpos < jsonlen && json[*jsonpos] == ',')
{
*jsonpos+=1;
XML_SkipWhite(json, jsonpos, jsonlen);
continue;
}
break;
}
if (*jsonpos < jsonlen && json[*jsonpos] == '}')
{
*jsonpos+=1;
return t;
}
XML_Destroy(t);
}
else if (*jsonpos < jsonlen)
{
if (XML_ParseString(json, jsonpos, jsonlen, child, sizeof(child)))
return XML_CreateNode(t, name, "", child);
}
return NULL;
}