331 lines
8.2 KiB
C
331 lines
8.2 KiB
C
/* iksemel (XML parser for Jabber)
|
|
** Copyright (C) 2000-2003 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"
|
|
|
|
iksid *
|
|
iks_id_new (ikstack *s, const char *jid)
|
|
{
|
|
iksid *id;
|
|
char *src, *tmp;
|
|
|
|
/* FIXME: add jabber id validity checks to this function */
|
|
/* which characters are allowed in id parts? */
|
|
|
|
if (!jid) return NULL;
|
|
id = iks_stack_alloc (s, sizeof (iksid));
|
|
if (!id) return NULL;
|
|
memset (id, 0, sizeof (iksid));
|
|
|
|
/* skip scheme */
|
|
if (strncmp ("jabber:", jid, 7) == 0) jid += 7;
|
|
|
|
id->full = iks_stack_strdup (s, jid, 0);
|
|
src = id->full;
|
|
|
|
/* split resource */
|
|
tmp = strchr (src, '/');
|
|
if (tmp) {
|
|
id->partial = iks_stack_strdup (s, src, tmp - src);
|
|
id->resource = tmp + 1;
|
|
src = id->partial;
|
|
} else {
|
|
id->partial = src;
|
|
}
|
|
|
|
/* split user */
|
|
tmp = strchr (src, '@');
|
|
if (tmp) {
|
|
id->user = iks_stack_strdup (s, src, tmp - src);
|
|
src = ++tmp;
|
|
}
|
|
|
|
id->server = src;
|
|
|
|
return id;
|
|
}
|
|
|
|
int
|
|
iks_id_cmp (iksid *a, iksid *b, int parts)
|
|
{
|
|
int diff;
|
|
|
|
if (!a || !b) return (IKS_ID_RESOURCE | IKS_ID_USER | IKS_ID_SERVER);
|
|
diff = 0;
|
|
if (parts & IKS_ID_RESOURCE && !(!a->resource && !b->resource) && iks_strcmp (a->resource, b->resource) != 0)
|
|
diff += IKS_ID_RESOURCE;
|
|
if (parts & IKS_ID_USER && !(!a->user && !b->user) && iks_strcasecmp (a->user, b->user) != 0)
|
|
diff += IKS_ID_USER;
|
|
if (parts & IKS_ID_SERVER && !(!a->server && !b->server) && iks_strcmp (a->server, b->server) != 0)
|
|
diff += IKS_ID_SERVER;
|
|
return diff;
|
|
}
|
|
|
|
ikspak *
|
|
iks_packet (iks *x)
|
|
{
|
|
ikspak *pak;
|
|
ikstack *s;
|
|
char *tmp;
|
|
|
|
s = iks_stack (x);
|
|
pak = iks_stack_alloc (s, sizeof (ikspak));
|
|
if (!pak) return NULL;
|
|
memset (pak, 0, sizeof (ikspak));
|
|
pak->x = x;
|
|
tmp = iks_find_attrib (x, "from");
|
|
if (tmp) pak->from = iks_id_new (s, tmp);
|
|
pak->id = iks_find_attrib (x, "id");
|
|
|
|
tmp = iks_find_attrib (x, "type");
|
|
if (strcmp (iks_name (x), "message") == 0) {
|
|
pak->type = IKS_PAK_MESSAGE;
|
|
if (tmp) {
|
|
if (strcmp (tmp, "chat") == 0)
|
|
pak->subtype = IKS_TYPE_CHAT;
|
|
else if (strcmp (tmp, "groupchat") == 0)
|
|
pak->subtype = IKS_TYPE_GROUPCHAT;
|
|
else if (strcmp (tmp, "headline") == 0)
|
|
pak->subtype = IKS_TYPE_HEADLINE;
|
|
else if (strcmp (tmp, "error") == 0)
|
|
pak->subtype = IKS_TYPE_ERROR;
|
|
}
|
|
} else if (strcmp (iks_name (x), "presence") == 0) {
|
|
pak->type = IKS_PAK_S10N;
|
|
if (tmp) {
|
|
if (strcmp (tmp, "unavailable") == 0) {
|
|
pak->type = IKS_PAK_PRESENCE;
|
|
pak->subtype = IKS_TYPE_UNAVAILABLE;
|
|
pak->show = IKS_SHOW_UNAVAILABLE;
|
|
} else if (strcmp (tmp, "probe") == 0) {
|
|
pak->type = IKS_PAK_PRESENCE;
|
|
pak->subtype = IKS_TYPE_PROBE;
|
|
} else if(strcmp(tmp, "subscribe") == 0)
|
|
pak->subtype = IKS_TYPE_SUBSCRIBE;
|
|
else if(strcmp(tmp, "subscribed") == 0)
|
|
pak->subtype = IKS_TYPE_SUBSCRIBED;
|
|
else if(strcmp(tmp, "unsubscribe") == 0)
|
|
pak->subtype = IKS_TYPE_UNSUBSCRIBE;
|
|
else if(strcmp(tmp, "unsubscribed") == 0)
|
|
pak->subtype = IKS_TYPE_UNSUBSCRIBED;
|
|
else if(strcmp(tmp, "error") == 0)
|
|
pak->subtype = IKS_TYPE_ERROR;
|
|
} else {
|
|
pak->type = IKS_PAK_PRESENCE;
|
|
pak->subtype = IKS_TYPE_AVAILABLE;
|
|
tmp = iks_find_cdata (x, "show");
|
|
pak->show = IKS_SHOW_AVAILABLE;
|
|
if (tmp) {
|
|
if (strcmp (tmp, "chat") == 0)
|
|
pak->show = IKS_SHOW_CHAT;
|
|
else if (strcmp (tmp, "away") == 0)
|
|
pak->show = IKS_SHOW_AWAY;
|
|
else if (strcmp (tmp, "xa") == 0)
|
|
pak->show = IKS_SHOW_XA;
|
|
else if (strcmp (tmp, "dnd") == 0)
|
|
pak->show = IKS_SHOW_DND;
|
|
}
|
|
}
|
|
} else if (strcmp (iks_name (x), "iq") == 0) {
|
|
iks *q;
|
|
pak->type = IKS_PAK_IQ;
|
|
if (tmp) {
|
|
if (strcmp (tmp, "get") == 0)
|
|
pak->subtype = IKS_TYPE_GET;
|
|
else if (strcmp (tmp, "set") == 0)
|
|
pak->subtype = IKS_TYPE_SET;
|
|
else if (strcmp (tmp, "result") == 0)
|
|
pak->subtype = IKS_TYPE_RESULT;
|
|
else if (strcmp (tmp, "error") == 0)
|
|
pak->subtype = IKS_TYPE_ERROR;
|
|
}
|
|
for (q = iks_child (x); q; q = iks_next (q)) {
|
|
if (IKS_TAG == iks_type (q)) {
|
|
char *ns;
|
|
ns = iks_find_attrib (q, "xmlns");
|
|
if (ns) {
|
|
pak->query = q;
|
|
pak->ns = ns;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return pak;
|
|
}
|
|
|
|
iks *
|
|
iks_make_auth (iksid *id, const char *pass, const char *sid)
|
|
{
|
|
iks *x, *y;
|
|
|
|
x = iks_new ("iq");
|
|
iks_insert_attrib (x, "type", "set");
|
|
y = iks_insert (x, "query");
|
|
iks_insert_attrib (y, "xmlns", IKS_NS_AUTH);
|
|
iks_insert_cdata (iks_insert (y, "username"), id->user, 0);
|
|
iks_insert_cdata (iks_insert (y, "resource"), id->resource, 0);
|
|
if(sid) {
|
|
char buf[41];
|
|
iksha *sha;
|
|
sha = iks_sha_new ();
|
|
iks_sha_hash (sha, (const unsigned char*)sid, strlen (sid), 0);
|
|
iks_sha_hash (sha, (const unsigned char*)pass, strlen (pass), 1);
|
|
iks_sha_print (sha, buf);
|
|
iks_sha_delete (sha);
|
|
iks_insert_cdata (iks_insert (y, "digest"), buf, 40);
|
|
} else {
|
|
iks_insert_cdata (iks_insert (y, "password"), pass, 0);
|
|
}
|
|
return x;
|
|
}
|
|
|
|
iks *
|
|
iks_make_msg (enum iksubtype type, const char *to, const char *body)
|
|
{
|
|
iks *x;
|
|
char *t = NULL;
|
|
|
|
x = iks_new ("message");
|
|
switch (type) {
|
|
case IKS_TYPE_CHAT: t = "chat"; break;
|
|
case IKS_TYPE_GROUPCHAT: t = "groupchat"; break;
|
|
case IKS_TYPE_HEADLINE: t = "headline"; break;
|
|
default: break;
|
|
}
|
|
if (t) iks_insert_attrib (x, "type", t);
|
|
if (to) iks_insert_attrib (x, "to", to);
|
|
if (body) iks_insert_cdata (iks_insert (x, "body"), body, 0);
|
|
return x;
|
|
}
|
|
|
|
iks *
|
|
iks_make_s10n (enum iksubtype type, const char *to, const char *msg)
|
|
{
|
|
iks *x;
|
|
char *t;
|
|
|
|
x = iks_new ("presence");
|
|
switch (type) {
|
|
case IKS_TYPE_SUBSCRIBE: t = "subscribe"; break;
|
|
case IKS_TYPE_SUBSCRIBED: t = "subscribed"; break;
|
|
case IKS_TYPE_UNSUBSCRIBE: t = "unsubscribe"; break;
|
|
case IKS_TYPE_UNSUBSCRIBED: t = "unsubscribed"; break;
|
|
case IKS_TYPE_PROBE: t = "probe"; break;
|
|
default: t = NULL; break;
|
|
}
|
|
if (t) iks_insert_attrib (x, "type", t);
|
|
if (to) iks_insert_attrib (x, "to", to);
|
|
if (msg) iks_insert_cdata(iks_insert (x, "status"), msg, 0);
|
|
return x;
|
|
}
|
|
|
|
iks *
|
|
iks_make_pres (enum ikshowtype show, const char *status)
|
|
{
|
|
iks *x;
|
|
char *t;
|
|
|
|
x = iks_new ("presence");
|
|
switch (show) {
|
|
case IKS_SHOW_CHAT: t = "chat"; break;
|
|
case IKS_SHOW_AWAY: t = "away"; break;
|
|
case IKS_SHOW_XA: t = "xa"; break;
|
|
case IKS_SHOW_DND: t = "dnd"; break;
|
|
case IKS_SHOW_UNAVAILABLE:
|
|
t = NULL;
|
|
iks_insert_attrib (x, "type", "unavailable");
|
|
break;
|
|
default: t = NULL; break;
|
|
}
|
|
if (t) iks_insert_cdata (iks_insert (x, "show"), t, 0);
|
|
if (status) iks_insert_cdata(iks_insert (x, "status"), status, 0);
|
|
return x;
|
|
}
|
|
|
|
iks *
|
|
iks_make_iq (enum iksubtype type, const char *xmlns)
|
|
{
|
|
iks *x;
|
|
char *t = NULL;
|
|
|
|
x = iks_new ("iq");
|
|
switch (type) {
|
|
case IKS_TYPE_GET: t = "get"; break;
|
|
case IKS_TYPE_SET: t = "set"; break;
|
|
case IKS_TYPE_RESULT: t = "result"; break;
|
|
case IKS_TYPE_ERROR: t = "error"; break;
|
|
default: break;
|
|
}
|
|
if (t) iks_insert_attrib (x, "type", t);
|
|
iks_insert_attrib (iks_insert (x, "query"), "xmlns", xmlns);
|
|
|
|
return x;
|
|
}
|
|
|
|
iks *
|
|
iks_make_resource_bind (iksid *id)
|
|
{
|
|
iks *x, *y, *z;
|
|
|
|
x = iks_new("iq");
|
|
iks_insert_attrib(x, "type", "set");
|
|
y = iks_insert(x, "bind");
|
|
iks_insert_attrib(y, "xmlns", IKS_NS_XMPP_BIND);
|
|
if (id->resource && iks_strcmp(id->resource, "")) {
|
|
z = iks_insert(y, "resource");
|
|
iks_insert_cdata(z, id->resource, 0);
|
|
}
|
|
return x;
|
|
}
|
|
|
|
iks *
|
|
iks_make_session (void)
|
|
{
|
|
iks *x, *y;
|
|
|
|
x = iks_new ("iq");
|
|
iks_insert_attrib (x, "type", "set");
|
|
y = iks_insert (x, "session");
|
|
iks_insert_attrib (y, "xmlns", IKS_NS_XMPP_SESSION);
|
|
return x;
|
|
}
|
|
|
|
static int
|
|
iks_sasl_mechanisms (iks *x)
|
|
{
|
|
int sasl_mech = 0;
|
|
|
|
while (x) {
|
|
if (!iks_strcmp(iks_cdata(iks_child(x)), "DIGEST-MD5"))
|
|
sasl_mech |= IKS_STREAM_SASL_MD5;
|
|
else if (!iks_strcmp(iks_cdata(iks_child(x)), "PLAIN"))
|
|
sasl_mech |= IKS_STREAM_SASL_PLAIN;
|
|
x = iks_next_tag(x);
|
|
}
|
|
return sasl_mech;
|
|
}
|
|
|
|
int
|
|
iks_stream_features (iks *x)
|
|
{
|
|
int features = 0;
|
|
|
|
if (iks_strcmp(iks_name(x), "stream:features"))
|
|
return 0;
|
|
for (x = iks_child(x); x; x = iks_next_tag(x))
|
|
if (!iks_strcmp(iks_name(x), "starttls"))
|
|
features |= IKS_STREAM_STARTTLS;
|
|
else if (!iks_strcmp(iks_name(x), "bind"))
|
|
features |= IKS_STREAM_BIND;
|
|
else if (!iks_strcmp(iks_name(x), "session"))
|
|
features |= IKS_STREAM_SESSION;
|
|
else if (!iks_strcmp(iks_name(x), "mechanisms"))
|
|
features |= iks_sasl_mechanisms(iks_child(x));
|
|
return features;
|
|
}
|