From 51377b07cd5a2db1e447a220c8224c4c3cc8d73f Mon Sep 17 00:00:00 2001 From: Andy Spencer Date: Mon, 30 Oct 2017 02:38:01 +0000 Subject: [PATCH] Cleanup xmpp users. --- chat.h | 2 +- xmpp.c | 365 +++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 237 insertions(+), 130 deletions(-) diff --git a/chat.h b/chat.h index 1ed14e8..38bcfda 100644 --- a/chat.h +++ b/chat.h @@ -45,7 +45,7 @@ typedef struct channel_t { typedef struct user_t { server_t *server; char *name; - char *full; + user_t *alias; user_t *next; } user_t; diff --git a/xmpp.c b/xmpp.c index 5534561..87ff36d 100644 --- a/xmpp.c +++ b/xmpp.c @@ -35,6 +35,7 @@ #include "net.h" /* Constants */ +#define ID_LEN 256 #define AUTH_LEN 512 #define JID_LEN 256 @@ -63,12 +64,16 @@ typedef enum { typedef struct { user_t user; - char jid[JID_LEN]; + char dest[JID_LEN]; + int muc; + char *full_name; } xmpp_user_t; typedef struct { channel_t channel; char dest[JID_LEN]; + const char *type; + int muc; const char *room; int join; @@ -99,20 +104,16 @@ typedef struct { XML_Parser expat; xmpp_state_t state; buf_t buf; - int indent; + int level; int id; - char *body; - stamp_t stamp; + char msg_id[ID_LEN]; + xmpp_channel_t *msg_chan; + xmpp_user_t *msg_usr; + char *msg_body; + stamp_t msg_stamp; int in_error; - - char msg_jid[JID_LEN]; - char msg_usr[JID_LEN]; - char msg_srv[JID_LEN]; - char msg_res[JID_LEN]; - char *msg_from; - xmpp_channel_t *msg_chan; } xmpp_server_t; /* Helper functions */ @@ -125,6 +126,7 @@ static void srv_notice(xmpp_server_t *srv, const char *fmt, ...) vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); + debug("xmpp: srv_notice: [%s]", buf); chat_recv(&srv->system.channel, NULL, buf); } @@ -137,6 +139,8 @@ static void chan_notice(xmpp_channel_t *chan, const char *fmt, ...) vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); + debug("xmpp: chan_notice -- %s [%s]", + chan->channel.name, buf); chat_recv(&chan->channel, NULL, buf); } @@ -170,7 +174,7 @@ static void split_jid(const char *jid, char *usr, char *srv, char *res) // jid, usr, srv, res); } -static xmpp_channel_t *find_dest(xmpp_server_t *srv, +static xmpp_channel_t *find_channel(xmpp_server_t *srv, const char *jid, int is_muc) { static char jid_usr[JID_LEN]; @@ -178,15 +182,20 @@ static xmpp_channel_t *find_dest(xmpp_server_t *srv, static char dest[JID_LEN]; xmpp_channel_t *chan; + /* Server channels */ + if (!jid || match(jid, srv->srv)) + return &srv->system; + + /* Parse JID and check for MUC */ split_jid(jid, jid_usr, jid_srv, NULL); + if (match(jid_srv, srv->muc)) + is_muc = 1; + + /* Find resource-less JID */ snprintf(dest, JID_LEN, "%s@%s", jid_usr, jid_srv[0] ? jid_srv : is_muc ? srv->muc : srv->srv); - /* Server channels */ - if (match(jid, srv->srv)) - return &srv->system; - /* Find existing channels */ for (channel_t *cur = channels; cur; cur = cur->next) { if (cur->server != &srv->server) @@ -198,6 +207,8 @@ static xmpp_channel_t *find_dest(xmpp_server_t *srv, /* Create a new channel */ chan = new0(xmpp_channel_t); + chan->muc = is_muc; + chan->type = is_muc ? "muc" : "usr"; chan->channel.server = &srv->server; chan->channel.name = strcopy(jid_usr); strncpy(chan->dest, dest, JID_LEN); @@ -205,25 +216,77 @@ static xmpp_channel_t *find_dest(xmpp_server_t *srv, return chan; } -static xmpp_user_t *find_jid(xmpp_server_t *srv, const char *jid, int create) +static xmpp_user_t *find_user(xmpp_server_t *srv, + const char *jid, int is_muc) { + static char jid_usr[JID_LEN]; + static char jid_srv[JID_LEN]; + static char jid_res[JID_LEN]; + static char dest[JID_LEN]; xmpp_user_t *usr; + /* Server channels */ + if (!jid || match(jid, srv->srv)) + return NULL; + + /* Parse JID and check for MUC */ + split_jid(jid, jid_usr, jid_srv, jid_res); + if (match(jid_srv, srv->muc)) + is_muc = 1; + + /* Channel notices have no resource */ + if (is_muc && !jid_res[0]) + return NULL; + + /* Ignore resources for real users */ + if (is_muc) + snprintf(dest, JID_LEN, "%s@%s/%s", + jid_usr, + jid_srv[0] ? jid_srv : srv->muc, + jid_res); + else + snprintf(dest, JID_LEN, "%s@%s", + jid_usr, + jid_srv[0] ? jid_srv : srv->srv); + /* Find existing users */ for (user_t *cur = users; cur; cur = cur->next) { if (cur->server != &srv->server) continue; usr = (xmpp_user_t *)cur; - if (match(usr->jid, jid)) + if (match(usr->dest, dest)) { + debug("xmpp: found user: \"%s\" -> " + "name=[%s] dest=[%s] alias=[%s]", + jid, usr->user.name, usr->dest, + usr->user.alias ? usr->user.alias->name : "(none)"); return usr; + } } /* Create a new user */ usr = new0(xmpp_user_t); usr->user.server = &srv->server; - usr->user.name = strcopy(jid); - strncpy(usr->jid, jid, JID_LEN); + if (is_muc) { + usr->user.name = strcopy(jid_res); + usr->full_name = strcopy(jid_res); + } else { + usr->user.name = strcopy(jid_usr); + } + usr->muc = is_muc; + strncpy(usr->dest, dest, JID_LEN); add_user(&usr->user); + + /* Send vcard probe */ + debug("xmpp: added user: \"%s\"%s -> name=[%s] dest=[%s]", + jid, is_muc ? " (muc)" : "", + usr->user.name, usr->dest); + if (!usr->muc) + net_print(&srv->net, + "" + "" + "", + srv->jid, usr->dest); + return usr; } @@ -293,20 +356,20 @@ static void xmpp_run(xmpp_server_t *srv, int idle, { /* Debug print */ if (data) - debug("%*s \"%s\"", srv->indent*4, "", data); + debug("%*s \"%s\"", srv->level*4, "", data); if (start) { - debug("%*s<%s>", srv->indent*4, "", start); + debug("%*s<%s>", srv->level*4, "", start); for (int i = 0; attrs[i] && attrs[i+1]; i += 2) { debug("%*s%s=\"%s\"%s", - srv->indent*4+8, "", + srv->level*4+8, "", attrs[i+0], attrs[i+1], attrs[i+2] ? "" : ">"); } } if (start) - srv->indent++; + srv->level++; if (end) - srv->indent--; + srv->level--; /* Connection Handling */ if (srv->state == XMPP_CONNECT && !start && !end) { @@ -334,6 +397,7 @@ static void xmpp_run(xmpp_server_t *srv, int idle, XML_SetCharacterDataHandler(srv->expat, on_data); debug("xmpp: connect -> stream"); + srv->level = 0; srv->state = XMPP_SEND_STREAM; } if (srv->state == XMPP_ENCRYPT && !start && !end) { @@ -347,6 +411,7 @@ static void xmpp_run(xmpp_server_t *srv, int idle, XML_SetCharacterDataHandler(srv->expat, on_data); debug("xmpp: encrypt -> stream"); + srv->level = 0; srv->state = XMPP_SEND_STREAM; } if (srv->state == XMPP_RESTART && !start && !end) { @@ -358,6 +423,7 @@ static void xmpp_run(xmpp_server_t *srv, int idle, XML_SetCharacterDataHandler(srv->expat, on_data); debug("xmpp: restart -> stream"); + srv->level = 0; srv->state = XMPP_SEND_STREAM; } @@ -478,6 +544,7 @@ static void xmpp_run(xmpp_server_t *srv, int idle, xmpp_channel_t *chan = (xmpp_channel_t *)cur; if (!chan->join) continue; + chan_notice(chan, "XMPP Channel: %s", chan->channel.name); net_print(&srv->net, "" "" @@ -489,55 +556,71 @@ static void xmpp_run(xmpp_server_t *srv, int idle, } /* Start message */ - xmpp_channel_t *chan = NULL; - if (srv->state >= XMPP_READY && idle) { debug("xmpp: idle"); net_print(&srv->net, " "); } - if (srv->state == XMPP_READY) { - srv->state = match(start, "iq") ? XMPP_IN_IQ : - match(start, "message") ? XMPP_IN_MESSAGE : - match(start, "presence") ? XMPP_IN_PRESENCE : - XMPP_READY; - if (srv->state != XMPP_READY) { - const char *from = find_attr(attrs, "from") ?: ""; - strncpy(srv->msg_jid, from, JID_LEN); - split_jid(srv->msg_jid, srv->msg_usr, - srv->msg_srv, srv->msg_res); - - if (match(srv->msg_srv, srv->muc)) { - srv->msg_from = srv->msg_res[0] ? srv->msg_res : NULL; - srv->msg_chan = find_dest(srv, srv->msg_jid, 1); - } else { - srv->msg_from = srv->msg_usr[0] ? srv->msg_usr : NULL; - srv->msg_chan = find_dest(srv, srv->msg_jid, 0); - } + if (srv->state == XMPP_READY && start) { + const char *id = find_attr(attrs, "id"); + const char *from = find_attr(attrs, "from"); + const char *type = find_attr(attrs, "type"); + int is_muc = match(type, "groupchat"); - debug("xmpp: %s -- jid=[%s] from=[%s] chan=[%s]", - start, - srv->msg_jid, srv->msg_from, - srv->msg_chan->channel.name); - } + /* Ignore presence errors (federated remote timeout, etc) */ + if (match(type, "error")) + return; + + strncpy(srv->msg_id, id ?: "", ID_LEN); + + srv->msg_chan = find_channel(srv, from, is_muc); + srv->msg_usr = find_user(srv, from, is_muc); + + if (match(start, "iq")) + srv->state = XMPP_IN_IQ; + if (match(start, "message")) + srv->state = XMPP_IN_MESSAGE; + if (match(start, "presence")) + srv->state = XMPP_IN_PRESENCE; + + if (srv->state != XMPP_READY) + debug("xmpp: ready -> in_%s -- " + "from=[%s] -> chan=[%s:%s] user=[%s]", + start, from, + srv->msg_chan->type, + srv->msg_chan->channel.name, + srv->msg_usr ? srv->msg_usr->user.name : "(none)"); } + + /* Shorthand Message Data */ + xmpp_channel_t *chan = NULL; + xmpp_user_t *usr = NULL; + xmpp_user_t *alias = NULL; + if (srv->state > XMPP_READY) { - if (srv->msg_chan && srv->msg_chan != &srv->system) - chan = srv->msg_chan; + chan = srv->msg_chan; + usr = srv->msg_usr; + if (usr) + alias = (xmpp_user_t*)usr->user.alias; } /* Info/Queries */ if (srv->state == XMPP_IN_IQ) { - if (match(start, "item") && chan) { - static char res[JID_LEN]; - split_jid(find_attr(attrs, "jid"), - NULL, NULL, res); - chan_notice(chan, "user: %s", res); - } - if (match(start, "item") && !chan) { - srv_notice(srv, "item: [%s] %s", - find_attr(attrs, "jid"), - find_attr(attrs, "name")); + if (match(start, "item")) { + if (chan == &srv->system) { + srv_notice(srv, "item: [%s] %s", + find_attr(attrs, "jid"), + find_attr(attrs, "name")); + } else if (chan->muc) { + const char *jid = find_attr(attrs, "jid"); + xmpp_user_t *usr = find_user(srv, jid, 1); + chan_notice(chan, "User: %s (%s)", + usr->user.name, jid); + } else { + chan_notice(chan, "item: [%s] %s", + find_attr(attrs, "jid"), + find_attr(attrs, "name")); + } } if (match(start, "identity")) { srv_notice(srv, "identity: %s", @@ -559,13 +642,13 @@ static void xmpp_run(xmpp_server_t *srv, int idle, find_attr(attrs, "type")); } if (match(end, "title")) { - debug("xmpp: title -- jid=[%s]", - end, srv->msg_jid); + debug("xmpp: title -- chan=[%s]", + end, chan->dest); chan_notice(chan, "Title: %s", data); } if (match(end, "instructions")) { - debug("xmpp: instructions -- jid=[%s]", - end, srv->msg_jid); + debug("xmpp: instructions -- chan=[%s]", + end, chan->dest); chan_notice(chan, "%s", data); } } @@ -573,21 +656,30 @@ static void xmpp_run(xmpp_server_t *srv, int idle, /* vCards */ if (srv->state == XMPP_IN_IQ) { if (match(start, "vCard")) { - if (chan) - chan_notice(chan, "vCard for %s", chan->dest); + if (!match(srv->msg_id, "auto-vcard")) + chan_notice(chan, "Begin vCard (%s)", + usr ? usr->user.name : + chan->channel.name); srv->state = XMPP_IN_VCARD; } } if (srv->state == XMPP_IN_VCARD) { - if (end && chan && data) { - xmpp_user_t *usr = NULL; - chan_notice(chan, "%-12s -- %s", end, data); - if (srv->msg_from) - usr = find_jid(srv, srv->msg_from, 1); - if (usr && match(end, "FN")) - strset(&usr->user.full, data); + if (end && srv->level == 3) { + if (!match(srv->msg_id, "auto-vcard") && + !match(end, "BINVAL")) + chan_notice(chan, " %s -> %s", end, data ?: "..."); + } + if (usr) { + if (match(end, "FN")) { + strset(&usr->user.name, data); + strset(&usr->full_name, data); + } } + } + if (srv->state == XMPP_IN_VCARD) { if (match(end, "vCard")) { + if (!match(srv->msg_id, "auto-vcard")) + chan_notice(chan, "End vCard"); srv->state = XMPP_IN_IQ; } } @@ -599,69 +691,61 @@ static void xmpp_run(xmpp_server_t *srv, int idle, if (ts) { struct tm tm = {}; strptime(ts, "%Y-%m-%dT%H:%M:%S", &tm); - srv->stamp = timegm(&tm); + srv->msg_stamp = timegm(&tm); } } - if (match(end, "subject") && chan) { + if (match(end, "subject")) { strset(&chan->channel.topic, data); } - if (match(end, "body")) { - strset(&srv->body, data); + if (match(end, "body") && data) { + strset(&srv->msg_body, data); } if (match(end, "message")) { - debug("xmpp: body (%s) -- chan=[%s] jid=[%s] from=[%s]", - srv->msg_from == srv->msg_usr ? "user" : "chat" , - srv->msg_chan->channel.name, - srv->msg_jid, srv->msg_from); - if (srv->body) { - xmpp_user_t *usr = NULL; - if (srv->msg_from) - usr = find_jid(srv, srv->msg_from, 1); - chat_recv(&chan->channel, &usr->user, srv->body); + debug("xmpp: body (%s) -- chan=[%s] from=[%s]", + chan->type, + chan->channel.name, + usr ? usr->user.name : "(none)"); + if (srv->msg_body) { + chat_recv(&chan->channel, + &usr->user, + srv->msg_body); message_t *msg = &messages[history-1]; - msg->when = srv->stamp ?: msg->when; + msg->when = srv->msg_stamp ?: msg->when; } - srv->stamp = 0; - strset(&srv->body, NULL); + srv->msg_stamp = 0; + strset(&srv->msg_body, NULL); } } /* Presence */ if (srv->state == XMPP_IN_PRESENCE) { - static char alias[JID_LEN]; - const char *jid; - if (match(start, "item")) { - if ((jid = find_attr(attrs, "jid"))) - strncpy(alias, jid, JID_LEN); - } - if (match(end, "presence") && chan) { - if (alias[0] && !srv->quiet) + if (match(start, "item") && usr) { + const char *jid = find_attr(attrs, "jid"); + xmpp_user_t *alias = find_user(srv, jid, 0); + if (jid && alias) + usr->user.alias = &alias->user; + } + if (match(end, "presence") && !srv->quiet) { + if (alias) chan_notice(chan, "%s (%s) entered room.", - srv->msg_from, alias); - else if (!srv->quiet) + usr->user.name, alias->user.name); + else if (usr) chan_notice(chan, "%s entered room.", - srv->msg_from); - alias[0] = '\0'; + usr->user.name); } } /* End messages */ - if (srv->state == XMPP_IN_IQ || - srv->state == XMPP_IN_MESSAGE || - srv->state == XMPP_IN_PRESENCE) { - if (match(end, "iq") || - match(end, "message") || - match(end, "presence")) { + if (srv->state == XMPP_IN_IQ) + if (match(end, "iq")) + srv->state = XMPP_READY; + if (srv->state == XMPP_IN_MESSAGE) + if (match(end, "message")) + srv->state = XMPP_READY; + if (srv->state == XMPP_IN_PRESENCE) + if (match(end, "presence")) srv->state = XMPP_READY; - srv->msg_jid[0] = '\0'; - srv->msg_usr[0] = '\0'; - srv->msg_srv[0] = '\0'; - srv->msg_res[0] = '\0'; - srv->msg_from = NULL; - srv->msg_chan = NULL; - } - } /* Error handling */ if (match(start, "stream:error")) srv->in_error = 1; @@ -678,25 +762,46 @@ static void xmpp_run(xmpp_server_t *srv, int idle, /* XMPP functions */ void xmpp_init(void) { + static char jid_usr[JID_LEN]; + static char jid_srv[JID_LEN]; + static char jid_res[JID_LEN]; + for (server_t *cur = servers; cur; cur = cur->next) { if (cur->protocol != XMPP) continue; xmpp_server_t *srv = (xmpp_server_t*)cur; - srv->system.channel.server = &srv->server; - srv->system.channel.name = srv->server.name; - srv_notice(srv, "XMPP Server: %s", srv->server.name); + split_jid(srv->jid, jid_usr, jid_srv, jid_res); + if (!srv->jid) + error("jid is required"); + if (!srv->host) + srv->host = strcopy(jid_srv); if (!srv->port) srv->port = 5222; + if (!srv->muc) + srv->muc = strcopy(srv->host); if (!srv->srv) srv->srv = strcopy(srv->host); - if (!srv->jid) - error("jid is required"); + if (!srv->user) + srv->user = strcopy(jid_usr); + if (!srv->nick) + srv->nick = strcopy(jid_usr); if (!srv->timeout) srv->timeout = 60; - if (srv->connect) + + srv->system.type = "sys"; + srv->system.channel.server = &srv->server; + srv->system.channel.name = strcopy(srv->server.name); + + strncpy(srv->myself.dest, srv->jid, JID_LEN); + srv->myself.user.server = &srv->server; + srv->myself.user.name = strcopy(srv->nick); + + if (srv->connect) { + srv_notice(srv, "XMPP Server: %s", srv->server.name); xmpp_run(srv, 0, NULL, NULL, NULL, NULL); + } } for (channel_t *cur = channels; cur; cur = cur->next) { if (cur->server->protocol != XMPP) @@ -704,6 +809,8 @@ void xmpp_init(void) xmpp_channel_t *chan = (xmpp_channel_t*)cur; xmpp_server_t *srv = (xmpp_server_t*)cur->server; + chan->muc = 1; + chan->type = "muc"; if (!chan->room) chan->room = strcopy(cur->name); snprintf(chan->dest, JID_LEN, "%s@%s", @@ -788,7 +895,7 @@ void xmpp_send(channel_t *channel, const char *text) } else if (prefix(text, "/names", &arg)) { if (arg) - chan = find_dest(srv, arg, 1); + chan = find_channel(srv, arg, 1); if (chan == &srv->system) { chan_notice(chan, "Cannot get names from server"); return; @@ -809,7 +916,7 @@ void xmpp_send(channel_t *channel, const char *text) "" "", srv->jid, chan->dest, srv->nick); - chan = find_dest(srv, arg, 1); + chan = find_channel(srv, arg, 1); chan_notice(chan, "Room: %s", arg); } else if (prefix(text, "/config", &arg)) { @@ -828,12 +935,12 @@ void xmpp_send(channel_t *channel, const char *text) chan_notice(chan, "usage: /query "); return; } - chan = find_dest(srv, arg, 0); + chan = find_channel(srv, arg, 0); chan_notice(chan, "User: %s", arg); } else if (prefix(text, "/vcard", &arg)) { if (arg) - chan = find_dest(srv, arg, 0); + chan = find_channel(srv, arg, 0); if (chan) net_print(&srv->net, "" @@ -852,7 +959,7 @@ void xmpp_send(channel_t *channel, const char *text) else if (!chan->dest) { chan_notice(chan, "No destination for message"); } - else if (chan->room) { + else if (chan->muc) { net_print(&srv->net, "" "%s" @@ -860,7 +967,7 @@ void xmpp_send(channel_t *channel, const char *text) srv->id++, srv->jid, chan->dest, text); } else { net_print(&srv->net, - "" + "" "%s" "", srv->id++, srv->jid, chan->dest, text); -- 2.43.2