]> Pileus Git - ~andy/lamechat/blob - xmpp.c
87ff36d9236ecf9df2bef172439d5129beba9f5b
[~andy/lamechat] / xmpp.c
1 /*
2  * Copyright (C) 2017 Andy Spencer <andy753421@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <stdint.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <expat.h>
28 #include <time.h>
29
30 #include <sys/timerfd.h>
31
32 #include "util.h"
33 #include "conf.h"
34 #include "chat.h"
35 #include "net.h"
36
37 /* Constants */
38 #define ID_LEN   256
39 #define AUTH_LEN 512
40 #define JID_LEN  256
41
42 /* XMPP types */
43 typedef enum {
44         XMPP_CONNECT,
45         XMPP_ENCRYPT,
46         XMPP_RESTART,
47         XMPP_SEND_STREAM,
48         XMPP_RECV_FEATURES,
49         XMPP_SEND_STARTTLS,
50         XMPP_RECV_PROCEED,
51         XMPP_SEND_AUTH,
52         XMPP_RECV_SUCCESS,
53         XMPP_SEND_BIND,
54         XMPP_RECV_JID,
55         XMPP_SEND_SESSION,
56         XMPP_SEND_PRESENCE,
57         XMPP_SEND_JOIN,
58         XMPP_READY,
59         XMPP_IN_IQ,
60         XMPP_IN_VCARD,
61         XMPP_IN_MESSAGE,
62         XMPP_IN_PRESENCE,
63 } xmpp_state_t;
64
65 typedef struct {
66         user_t          user;
67         char            dest[JID_LEN];
68         int             muc;
69         char           *full_name;
70 } xmpp_user_t;
71
72 typedef struct {
73         channel_t       channel;
74         char            dest[JID_LEN];
75         const char     *type;
76         int             muc;
77
78         const char     *room;
79         int             join;
80 } xmpp_channel_t;
81
82 typedef struct {
83         server_t        server;
84         xmpp_user_t     myself;
85         xmpp_channel_t  system;
86
87         int             connect;
88         int             timeout;
89         int             noverify;
90         const char     *host;
91         int             port;
92         const char     *srv;
93         const char     *muc;
94         const char     *nick;
95         const char     *jid;
96         const char     *user;
97         const char     *pass;
98         int             quiet;
99
100         int             timer;
101         poll_t          poll;
102
103         net_t           net;
104         XML_Parser      expat;
105         xmpp_state_t    state;
106         buf_t           buf;
107         int             level;
108         int             id;
109
110         char            msg_id[ID_LEN];
111         xmpp_channel_t *msg_chan;
112         xmpp_user_t    *msg_usr;
113         char           *msg_body;
114         stamp_t         msg_stamp;
115
116         int             in_error;
117 } xmpp_server_t;
118
119 /* Helper functions */
120 static void srv_notice(xmpp_server_t *srv, const char *fmt, ...)
121 {
122         static char buf[1024];
123
124         va_list ap;
125         va_start(ap, fmt);
126         vsnprintf(buf, sizeof(buf), fmt, ap);
127         va_end(ap);
128
129         debug("xmpp: srv_notice: [%s]", buf);
130         chat_recv(&srv->system.channel, NULL, buf);
131 }
132
133 static void chan_notice(xmpp_channel_t *chan, const char *fmt, ...)
134 {
135         static char buf[1024];
136
137         va_list ap;
138         va_start(ap, fmt);
139         vsnprintf(buf, sizeof(buf), fmt, ap);
140         va_end(ap);
141
142         debug("xmpp: chan_notice -- %s [%s]",
143                         chan->channel.name, buf);
144         chat_recv(&chan->channel, NULL, buf);
145 }
146
147 static void split_jid(const char *jid, char *usr, char *srv, char *res)
148 {
149         char *ptr = usr;
150         int   pos = 0;
151
152         if (usr) usr[0] = '\0';
153         if (srv) srv[0] = '\0';
154         if (res) res[0] = '\0';
155
156         for (int i = 0; jid && jid[i]; i++) {
157                 switch (jid[i]) {
158                         case '@':
159                                 ptr = srv;
160                                 pos = 0;
161                                 continue;
162                         case '/':
163                                 ptr = res;
164                                 pos = 0;
165                                 continue;
166                 }
167                 if (ptr && (pos+1) < JID_LEN) {
168                         ptr[pos++] = jid[i];
169                         ptr[pos] = '\0';
170                 }
171         }
172
173         //debug("JID: '%s' usr=[%s] srv=[%s] res=[%s]",
174         //              jid, usr, srv, res);
175 }
176
177 static xmpp_channel_t *find_channel(xmpp_server_t *srv,
178                 const char *jid, int is_muc)
179 {
180         static char jid_usr[JID_LEN];
181         static char jid_srv[JID_LEN];
182         static char dest[JID_LEN];
183         xmpp_channel_t *chan;
184
185         /* Server channels */
186         if (!jid || match(jid, srv->srv))
187                 return &srv->system;
188
189         /* Parse JID and check for MUC */
190         split_jid(jid, jid_usr, jid_srv, NULL);
191         if (match(jid_srv, srv->muc))
192                 is_muc = 1;
193
194         /* Find resource-less JID */
195         snprintf(dest, JID_LEN, "%s@%s", jid_usr,
196                  jid_srv[0] ? jid_srv  :
197                  is_muc     ? srv->muc : srv->srv);
198
199         /* Find existing channels */
200         for (channel_t *cur = channels; cur; cur = cur->next) {
201                 if (cur->server != &srv->server)
202                         continue;
203                 chan = (xmpp_channel_t *)cur;
204                 if (match(chan->dest, dest))
205                         return chan;
206         }
207
208         /* Create a new channel */
209         chan = new0(xmpp_channel_t);
210         chan->muc = is_muc;
211         chan->type = is_muc ? "muc" : "usr";
212         chan->channel.server = &srv->server;
213         chan->channel.name = strcopy(jid_usr);
214         strncpy(chan->dest, dest, JID_LEN);
215         add_channel(&chan->channel);
216         return chan;
217 }
218
219 static xmpp_user_t *find_user(xmpp_server_t *srv,
220                 const char *jid, int is_muc)
221 {
222         static char jid_usr[JID_LEN];
223         static char jid_srv[JID_LEN];
224         static char jid_res[JID_LEN];
225         static char dest[JID_LEN];
226         xmpp_user_t *usr;
227
228         /* Server channels */
229         if (!jid || match(jid, srv->srv))
230                 return NULL;
231
232         /* Parse JID and check for MUC */
233         split_jid(jid, jid_usr, jid_srv, jid_res);
234         if (match(jid_srv, srv->muc))
235                 is_muc = 1;
236
237         /* Channel notices have no resource */
238         if (is_muc && !jid_res[0])
239                 return NULL;
240
241         /* Ignore resources for real users */
242         if (is_muc)
243                 snprintf(dest, JID_LEN, "%s@%s/%s",
244                          jid_usr,
245                          jid_srv[0] ? jid_srv : srv->muc,
246                          jid_res);
247         else
248                 snprintf(dest, JID_LEN, "%s@%s",
249                          jid_usr,
250                          jid_srv[0] ? jid_srv : srv->srv);
251
252         /* Find existing users */
253         for (user_t *cur = users; cur; cur = cur->next) {
254                 if (cur->server != &srv->server)
255                         continue;
256                 usr = (xmpp_user_t *)cur;
257                 if (match(usr->dest, dest)) {
258                         debug("xmpp: found user: \"%s\" -> "
259                                "name=[%s] dest=[%s] alias=[%s]",
260                                         jid, usr->user.name, usr->dest,
261                                         usr->user.alias ? usr->user.alias->name : "(none)");
262                         return usr;
263                 }
264         }
265
266         /* Create a new user */
267         usr = new0(xmpp_user_t);
268         usr->user.server = &srv->server;
269         if (is_muc) {
270                 usr->user.name = strcopy(jid_res);
271                 usr->full_name = strcopy(jid_res);
272         } else {
273                 usr->user.name = strcopy(jid_usr);
274         }
275         usr->muc = is_muc;
276         strncpy(usr->dest, dest, JID_LEN);
277         add_user(&usr->user);
278
279         /* Send vcard probe */
280         debug("xmpp: added user: \"%s\"%s -> name=[%s] dest=[%s]",
281                         jid, is_muc ? " (muc)" : "",
282                         usr->user.name, usr->dest);
283         if (!usr->muc)
284                 net_print(&srv->net,
285                         "<iq id='auto-vcard' type='get' from='%s' to='%s'>"
286                         "<vCard xmlns='vcard-temp'/>"
287                         "</iq>",
288                         srv->jid, usr->dest);
289
290         return usr;
291 }
292
293 static const char *find_attr(const char **attrs, const char *name)
294 {
295         for (int i = 0; attrs[i] && attrs[i+1]; i += 2)
296                 if (match(attrs[i+0], name))
297                         return attrs[i+1];
298         return NULL;
299 }
300
301 /* Callback functions */
302 static void xmpp_run(xmpp_server_t *srv, int idle,
303                 const char *start, const char **attrs,
304                 const char *end, const char *data);
305
306 static void on_start(void *_srv, const char *tag, const char **attrs)
307 {
308         xmpp_server_t *srv = _srv;
309         xmpp_run(srv, 0, tag, attrs, NULL, reset(&srv->buf));
310 }
311
312 static void on_data(void *_srv, const char *data, int len)
313 {
314         xmpp_server_t *srv = _srv;
315         append(&srv->buf, data, len);
316 }
317
318 static void on_end(void *_srv, const char *tag)
319 {
320         xmpp_server_t *srv = _srv;
321         xmpp_run(srv, 0, NULL, NULL, tag, reset(&srv->buf));
322 }
323
324 static void on_send(void *_srv)
325 {
326         xmpp_server_t *srv = _srv;
327         xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
328 }
329
330 static void on_recv(void *_srv, char *buf, int len)
331 {
332         xmpp_server_t *srv = _srv;
333         if (len > 0)
334                 XML_Parse(srv->expat, buf, len, 0);
335         xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
336 }
337
338 static void on_err(void *_srv, int errno)
339 {
340         xmpp_server_t *srv = _srv;
341         xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
342 }
343
344 static void on_timer(void *_srv)
345 {
346         uint64_t buf;
347         xmpp_server_t *srv = _srv;
348         while (read(srv->timer, &buf, sizeof(buf)) > 0)
349                 xmpp_run(srv, 1, NULL, NULL, NULL, NULL);
350 }
351
352 /* XMPP State machine */
353 static void xmpp_run(xmpp_server_t *srv, int idle,
354                 const char *start, const char **attrs,
355                 const char *end, const char *data)
356 {
357         /* Debug print */
358         if (data)
359                 debug("%*s \"%s\"", srv->level*4, "", data);
360         if (start) {
361                 debug("%*s<%s>", srv->level*4, "", start);
362                 for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
363                         debug("%*s%s=\"%s\"%s",
364                                 srv->level*4+8, "",
365                                 attrs[i+0], attrs[i+1],
366                                 attrs[i+2] ? "" : ">");
367                 }
368         }
369         if (start)
370                 srv->level++;
371         if (end)
372                 srv->level--;
373
374         /* Connection Handling */
375         if (srv->state == XMPP_CONNECT && !start && !end) {
376                 srv->net.send = on_send;
377                 srv->net.recv = on_recv;
378                 srv->net.err  = on_err;
379                 srv->net.data = srv;
380                 net_open(&srv->net, srv->host, srv->port);
381
382                 srv->timer = timerfd_create(CLOCK_MONOTONIC,
383                                             TFD_NONBLOCK|TFD_CLOEXEC);
384                 if (srv->timer < 0)
385                         error("creating timer fd");
386                 struct timespec tspec = {srv->timeout, 0};
387                 struct itimerspec itspec = {tspec, tspec};
388                 timerfd_settime(srv->timer, 0, &itspec, NULL);
389                 poll_add(&srv->poll, srv->timer, on_timer, srv);
390                 poll_ctl(&srv->poll, 1, 0, 1);
391
392                 if (!(srv->expat = XML_ParserCreate(NULL)))
393                         error("Error creating XML parser");
394                 XML_SetUserData(srv->expat, srv);
395                 XML_SetStartElementHandler(srv->expat, on_start);
396                 XML_SetEndElementHandler(srv->expat, on_end);
397                 XML_SetCharacterDataHandler(srv->expat, on_data);
398
399                 debug("xmpp: connect -> stream");
400                 srv->level = 0;
401                 srv->state = XMPP_SEND_STREAM;
402         }
403         if (srv->state == XMPP_ENCRYPT && !start && !end) {
404                 net_encrypt(&srv->net, srv->noverify ? NET_NOVERIFY : 0);
405
406                 if (!(XML_ParserReset(srv->expat, NULL)))
407                         error("Error resetting XML parser");
408                 XML_SetUserData(srv->expat, srv);
409                 XML_SetStartElementHandler(srv->expat, on_start);
410                 XML_SetEndElementHandler(srv->expat, on_end);
411                 XML_SetCharacterDataHandler(srv->expat, on_data);
412
413                 debug("xmpp: encrypt -> stream");
414                 srv->level = 0;
415                 srv->state = XMPP_SEND_STREAM;
416         }
417         if (srv->state == XMPP_RESTART && !start && !end) {
418                 if (!(XML_ParserReset(srv->expat, NULL)))
419                         error("Error resetting XML parser");
420                 XML_SetUserData(srv->expat, srv);
421                 XML_SetStartElementHandler(srv->expat, on_start);
422                 XML_SetEndElementHandler(srv->expat, on_end);
423                 XML_SetCharacterDataHandler(srv->expat, on_data);
424
425                 debug("xmpp: restart -> stream");
426                 srv->level = 0;
427                 srv->state = XMPP_SEND_STREAM;
428         }
429
430         /* Stream Start */
431         if (srv->state == XMPP_SEND_STREAM) {
432                 if (net_print(&srv->net,
433                     "<?xml version='1.0'?>"
434                     "<stream:stream"
435                     " from='%s'"
436                     " to='%s'"
437                     " version='1.0'"
438                     " xml:lang='en'"
439                     " xmlns='jabber:client'"
440                     " xmlns:stream='http://etherx.jabber.org/streams'>",
441                     srv->jid, srv->srv)) {
442                         debug("xmpp: stream -> features");
443                         srv->state = XMPP_RECV_FEATURES;
444                 }
445         }
446         if (srv->state == XMPP_RECV_FEATURES) {
447                 if (match(start, "starttls")) {
448                         debug("xmpp: features -> starttls");
449                         srv->state = XMPP_SEND_STARTTLS;
450                 }
451                 if (match(start, "mechanisms")) {
452                         debug("xmpp: features -> auth");
453                         srv->state = XMPP_SEND_AUTH;
454                 }
455                 if (match(start, "bind")) {
456                         debug("xmpp: features -> bind");
457                         srv->state = XMPP_SEND_BIND;
458                 }
459         }
460
461         /* Start TLS */
462         if (srv->state == XMPP_SEND_STARTTLS) {
463                 if (net_print(&srv->net,
464                     "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
465                         debug("xmpp: startls -> proceed");
466                         srv->state = XMPP_RECV_PROCEED;
467                 }
468         }
469         if (srv->state == XMPP_RECV_PROCEED) {
470                 if (match(start, "proceed")) {
471                         debug("xmpp: proceed -> encrypt");
472                         srv->state = XMPP_ENCRYPT;
473                 }
474         }
475
476         /* Authentication */
477         if (srv->state == XMPP_SEND_AUTH) {
478                 static char plain[AUTH_LEN];
479                 static char coded[AUTH_LEN];
480                 int len;
481                 len = snprintf(plain, AUTH_LEN, "%s%c%s%c%s",
482                                 srv->user, '\0', srv->user, '\0', srv->pass);
483                 len = base64(plain, len, coded, AUTH_LEN);
484                 if (net_print(&srv->net,
485                     "<auth"
486                     " xmlns='urn:ietf:params:xml:ns:xmpp-sasl'"
487                     " mechanism='PLAIN'>%.*s</auth>",
488                         len, coded)) {
489                         debug("xmpp: auth -> success");
490                         srv->state = XMPP_RECV_SUCCESS;
491                 }
492         }
493         if (srv->state == XMPP_RECV_SUCCESS) {
494                 if (match(start, "success")) {
495                         debug("xmpp: success -> restart");
496                         srv->state = XMPP_RESTART;
497                 }
498         }
499
500         /* Binding */
501         if (srv->state == XMPP_SEND_BIND) {
502                 const char *resource = srv->jid;
503                 while (*resource && *resource != '/')
504                         resource++;
505                 while (*resource && *resource == '/')
506                         resource++;
507                 if (net_print(&srv->net,
508                     "<iq id='bind' type='set'>"
509                     "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
510                     "<resource>%s</resource>"
511                     "</bind>"
512                     "</iq>",
513                     resource)) {
514                         debug("xmpp: bind -> jid");
515                         srv->state = XMPP_RECV_JID;
516                 }
517         }
518         if (srv->state == XMPP_RECV_JID) {
519                 if (match(start, "jid")) {
520                         debug("xmpp: jid -> session");
521                         srv->state = XMPP_SEND_SESSION;
522                 }
523         }
524         if (srv->state == XMPP_SEND_SESSION) {
525                 if (net_print(&srv->net,
526                     "<iq id='session' type='set' to='%s'>"
527                     "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
528                     "</iq>",
529                     srv->srv)) {
530                         debug("xmpp: session -> presence");
531                         srv->state = XMPP_SEND_PRESENCE;
532                 }
533         }
534         if (srv->state == XMPP_SEND_PRESENCE) {
535                 if (net_print(&srv->net, "<presence/>")) {
536                         debug("xmpp: presence -> join");
537                         srv->state = XMPP_SEND_JOIN;
538                 }
539         }
540         if (srv->state == XMPP_SEND_JOIN) {
541                 for (channel_t *cur = channels; cur; cur = cur->next) {
542                         if (cur->server != &srv->server)
543                                 continue;
544                         xmpp_channel_t *chan = (xmpp_channel_t *)cur;
545                         if (!chan->join)
546                                 continue;
547                         chan_notice(chan, "XMPP Channel: %s", chan->channel.name);
548                         net_print(&srv->net,
549                                 "<presence id='join' from='%s' to='%s/%s'>"
550                                 "<x xmlns='http://jabber.org/protocol/muc'/>"
551                                 "</presence>",
552                                 srv->jid, chan->dest, srv->nick);
553                 }
554                 debug("xmpp: join -> ready");
555                 srv->state = XMPP_READY;
556         }
557
558         /* Start message */
559         if (srv->state >= XMPP_READY && idle) {
560                 debug("xmpp: idle");
561                 net_print(&srv->net, " ");
562         }
563
564         if (srv->state == XMPP_READY && start) {
565                 const char *id   = find_attr(attrs, "id");
566                 const char *from = find_attr(attrs, "from");
567                 const char *type = find_attr(attrs, "type");
568                 int is_muc = match(type, "groupchat");
569
570                 /* Ignore presence errors (federated remote timeout, etc) */
571                 if (match(type, "error"))
572                         return;
573
574                 strncpy(srv->msg_id, id ?: "", ID_LEN);
575
576                 srv->msg_chan = find_channel(srv, from, is_muc);
577                 srv->msg_usr  = find_user(srv, from, is_muc);
578
579                 if (match(start, "iq"))
580                         srv->state = XMPP_IN_IQ;
581                 if (match(start, "message"))
582                         srv->state = XMPP_IN_MESSAGE;
583                 if (match(start, "presence"))
584                         srv->state = XMPP_IN_PRESENCE;
585
586                 if (srv->state != XMPP_READY)
587                         debug("xmpp: ready -> in_%s -- "
588                               "from=[%s] -> chan=[%s:%s] user=[%s]",
589                                 start, from,
590                                 srv->msg_chan->type,
591                                 srv->msg_chan->channel.name,
592                                 srv->msg_usr ? srv->msg_usr->user.name : "(none)");
593         }
594
595         /* Shorthand Message Data */
596         xmpp_channel_t *chan  = NULL;
597         xmpp_user_t    *usr   = NULL;
598         xmpp_user_t    *alias = NULL;
599
600         if (srv->state > XMPP_READY) {
601                 chan = srv->msg_chan;
602                 usr  = srv->msg_usr;
603                 if (usr)
604                         alias = (xmpp_user_t*)usr->user.alias;
605         }
606
607         /* Info/Queries */
608         if (srv->state == XMPP_IN_IQ) {
609                 if (match(start, "item")) {
610                         if (chan == &srv->system) {
611                                 srv_notice(srv, "item: [%s] %s",
612                                                 find_attr(attrs, "jid"),
613                                                 find_attr(attrs, "name"));
614                         } else if (chan->muc) {
615                                 const char  *jid = find_attr(attrs, "jid");
616                                 xmpp_user_t *usr = find_user(srv, jid, 1);
617                                 chan_notice(chan, "User: %s (%s)",
618                                                 usr->user.name, jid);
619                         } else {
620                                 chan_notice(chan, "item: [%s] %s",
621                                                 find_attr(attrs, "jid"),
622                                                 find_attr(attrs, "name"));
623                         }
624                 }
625                 if (match(start, "identity")) {
626                         srv_notice(srv, "identity: %s",
627                                         find_attr(attrs, "name"));
628                 }
629                 if (match(start, "feature")) {
630                         srv_notice(srv, "feature: %s",
631                                         find_attr(attrs, "var"));
632                 }
633                 if (match(start, "field")) {
634                         debug("xmpp: %s -- type=[%s] label=[%s]", end,
635                                 find_attr(attrs, "type"),
636                                 find_attr(attrs, "label"));
637                         if (!find_attr(attrs, "label"))
638                                 return;
639                         chan_notice(chan, "%-36s -- %s (%s)",
640                                 find_attr(attrs, "var"),
641                                 find_attr(attrs, "label"),
642                                 find_attr(attrs, "type"));
643                 }
644                 if (match(end, "title")) {
645                         debug("xmpp: title -- chan=[%s]",
646                                 end, chan->dest);
647                         chan_notice(chan, "Title: %s", data);
648                 }
649                 if (match(end, "instructions")) {
650                         debug("xmpp: instructions -- chan=[%s]",
651                                 end, chan->dest);
652                         chan_notice(chan, "%s", data);
653                 }
654         }
655
656         /* vCards */
657         if (srv->state == XMPP_IN_IQ) {
658                 if (match(start, "vCard")) {
659                         if (!match(srv->msg_id, "auto-vcard"))
660                                 chan_notice(chan, "Begin vCard (%s)",
661                                                 usr ?  usr->user.name :
662                                                 chan->channel.name);
663                         srv->state = XMPP_IN_VCARD;
664                 }
665         }
666         if (srv->state == XMPP_IN_VCARD) {
667                 if (end && srv->level == 3) {
668                         if (!match(srv->msg_id, "auto-vcard") &&
669                             !match(end, "BINVAL"))
670                                 chan_notice(chan, "  %s -> %s", end, data ?: "...");
671                 }
672                 if (usr) {
673                         if (match(end, "FN")) {
674                                 strset(&usr->user.name, data);
675                                 strset(&usr->full_name, data);
676                         }
677                 }
678         }
679         if (srv->state == XMPP_IN_VCARD) {
680                 if (match(end, "vCard")) {
681                         if (!match(srv->msg_id, "auto-vcard"))
682                                 chan_notice(chan, "End vCard");
683                         srv->state = XMPP_IN_IQ;
684                 }
685         }
686
687         /* Messages */
688         if (srv->state == XMPP_IN_MESSAGE) {
689                 if (match(start, "delay")) {
690                         const char *ts = find_attr(attrs, "stamp");
691                         if (ts) {
692                                 struct tm tm = {};
693                                 strptime(ts, "%Y-%m-%dT%H:%M:%S", &tm);
694                                 srv->msg_stamp = timegm(&tm);
695                         }
696                 }
697                 if (match(end, "subject")) {
698                         strset(&chan->channel.topic, data);
699                 }
700                 if (match(end, "body") && data) {
701                         strset(&srv->msg_body, data);
702                 }
703                 if (match(end, "message")) {
704                         debug("xmpp: body (%s) -- chan=[%s] from=[%s]",
705                                 chan->type,
706                                 chan->channel.name,
707                                 usr ? usr->user.name : "(none)");
708                         if (srv->msg_body) {
709                                 chat_recv(&chan->channel,
710                                           &usr->user,
711                                           srv->msg_body);
712                                 message_t *msg = &messages[history-1];
713                                 msg->when = srv->msg_stamp ?: msg->when;
714                         }
715                         srv->msg_stamp = 0;
716                         strset(&srv->msg_body, NULL);
717                 }
718         }
719
720         /* Presence */
721         if (srv->state == XMPP_IN_PRESENCE) {
722                 if (match(start, "item") && usr) {
723                         const char  *jid   = find_attr(attrs, "jid");
724                         xmpp_user_t *alias = find_user(srv, jid, 0);
725                         if (jid && alias)
726                                 usr->user.alias = &alias->user;
727                 }
728                 if (match(end, "presence") && !srv->quiet) {
729                         if (alias)
730                                 chan_notice(chan, "%s (%s) entered room.",
731                                                 usr->user.name, alias->user.name);
732                         else if (usr)
733                                 chan_notice(chan, "%s entered room.",
734                                                 usr->user.name);
735                 }
736         }
737
738         /* End messages */
739         if (srv->state == XMPP_IN_IQ)
740                 if (match(end, "iq"))
741                         srv->state = XMPP_READY;
742         if (srv->state == XMPP_IN_MESSAGE)
743                 if (match(end, "message"))
744                         srv->state = XMPP_READY;
745         if (srv->state == XMPP_IN_PRESENCE)
746                 if (match(end, "presence"))
747                         srv->state = XMPP_READY;
748
749         /* Error handling */
750         if (match(start, "stream:error"))
751                 srv->in_error = 1;
752         if (match(end, "stream:error"))
753                 srv->in_error = 0;
754         if (srv->in_error) {
755                 if (match(end, "text")) {
756                         debug("xmpp: error: %s", data);
757                         srv_notice(srv, "error: %s", data);
758                 }
759         }
760 }
761
762 /* XMPP functions */
763 void xmpp_init(void)
764 {
765         static char jid_usr[JID_LEN];
766         static char jid_srv[JID_LEN];
767         static char jid_res[JID_LEN];
768
769         for (server_t *cur = servers; cur; cur = cur->next) {
770                 if (cur->protocol != XMPP)
771                         continue;
772
773                 xmpp_server_t *srv = (xmpp_server_t*)cur;
774                 split_jid(srv->jid, jid_usr, jid_srv, jid_res);
775
776                 if (!srv->jid)
777                         error("jid is required");
778                 if (!srv->host)
779                         srv->host = strcopy(jid_srv);
780                 if (!srv->port)
781                         srv->port = 5222;
782                 if (!srv->muc)
783                         srv->muc = strcopy(srv->host);
784                 if (!srv->srv)
785                         srv->srv = strcopy(srv->host);
786                 if (!srv->user)
787                         srv->user = strcopy(jid_usr);
788                 if (!srv->nick)
789                         srv->nick = strcopy(jid_usr);
790                 if (!srv->timeout)
791                         srv->timeout = 60;
792
793                 srv->system.type  = "sys";
794                 srv->system.channel.server = &srv->server;
795                 srv->system.channel.name = strcopy(srv->server.name);
796
797                 strncpy(srv->myself.dest, srv->jid, JID_LEN);
798                 srv->myself.user.server = &srv->server;
799                 srv->myself.user.name = strcopy(srv->nick);
800
801                 if (srv->connect) {
802                         srv_notice(srv, "XMPP Server: %s", srv->server.name);
803                         xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
804                 }
805         }
806         for (channel_t *cur = channels; cur; cur = cur->next) {
807                 if (cur->server->protocol != XMPP)
808                         continue;
809
810                 xmpp_channel_t *chan = (xmpp_channel_t*)cur;
811                 xmpp_server_t *srv = (xmpp_server_t*)cur->server;
812                 chan->muc = 1;
813                 chan->type = "muc";
814                 if (!chan->room)
815                         chan->room = strcopy(cur->name);
816                 snprintf(chan->dest, JID_LEN, "%s@%s",
817                          chan->room, srv->muc);
818         }
819 }
820
821 void xmpp_config(server_t *server, channel_t *channel,
822                  const char *group, const char *name,
823                  const char *key, const char *value)
824 {
825         xmpp_server_t  *srv  = (xmpp_server_t*)server;
826         xmpp_channel_t *chan = (xmpp_channel_t*)channel;
827
828         if (match(group, "server")) {
829                 if (match(key, "protocol")) {
830                         xmpp_server_t *srv = new0(xmpp_server_t);
831                         srv->server.protocol = XMPP;
832                         srv->server.name = strcopy(get_name(name));
833                         add_server(&srv->server);
834                 }
835                 else if (match(key, "connect"))
836                         srv->connect = get_bool(value);
837                 else if (match(key, "timeout"))
838                         srv->timeout = get_number(value);
839                 else if (match(key, "noverify"))
840                         srv->noverify = get_bool(value);
841                 else if (match(key, "host"))
842                         srv->host = get_string(value);
843                 else if (match(key, "port"))
844                         srv->port = get_number(value);
845                 else if (match(key, "srv"))
846                         srv->srv = get_string(value);
847                 else if (match(key, "muc"))
848                         srv->muc = get_string(value);
849                 else if (match(key, "nick"))
850                         srv->nick = get_string(value);
851                 else if (match(key, "jid"))
852                         srv->jid = get_string(value);
853                 else if (match(key, "user"))
854                         srv->user = get_string(value);
855                 else if (match(key, "pass"))
856                         srv->pass = get_string(value);
857                 else if (match(key, "quiet"))
858                         srv->quiet = get_bool(value);
859         }
860         if (match(group, "channel")) {
861                 if (match(key, "server")) {
862                         xmpp_channel_t *chan = new0(xmpp_channel_t);
863                         chan->channel.server = &srv->server;
864                         chan->channel.name = strcopy(get_name(name));
865                         add_channel(&chan->channel);
866                 }
867                 else if (match(key, "room"))
868                         chan->room = get_string(value);
869                 else if (match(key, "join"))
870                         chan->join = get_bool(value);
871         }
872 }
873
874 void xmpp_send(channel_t *channel, const char *text)
875 {
876         xmpp_channel_t *chan = (xmpp_channel_t*)channel;
877         xmpp_server_t  *srv  = (xmpp_server_t*)channel->server;
878         const char *arg;
879
880         /* Handle commands */
881         if (text[0] == '/') {
882                 if (prefix(text, "/items", &arg)) {
883                         net_print(&srv->net,
884                                 "<iq id='items' type='get' from='%s' to='%s'>"
885                                 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
886                                 "</iq>",
887                                 srv->jid, arg ?: srv->srv);
888                 }
889                 else if (prefix(text, "/info", &arg)) {
890                         net_print(&srv->net,
891                                 "<iq id='info' type='get' from='%s' to='%s'>"
892                                 "<query xmlns='http://jabber.org/protocol/disco#info'/>"
893                                 "</iq>",
894                                 srv->jid, arg ?: srv->srv);
895                 }
896                 else if (prefix(text, "/names", &arg)) {
897                         if (arg)
898                                 chan = find_channel(srv, arg, 1);
899                         if (chan == &srv->system) {
900                                 chan_notice(chan, "Cannot get names from server");
901                                 return;
902                         }
903                         net_print(&srv->net,
904                                 "<iq id='list' type='get' from='%s' to='%s'>"
905                                 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
906                                 "</iq>",
907                                 srv->jid, chan->dest);
908                 }
909                 else if (prefix(text, "/join", &arg)) {
910                         if (!arg) {
911                                 chan_notice(chan, "usage: /join <channel>");
912                                 return;
913                         }
914                         net_print(&srv->net,
915                                 "<presence id='join' from='%s' to='%s/%s'>"
916                                 "<x xmlns='http://jabber.org/protocol/muc'/>"
917                                 "</presence>",
918                                 srv->jid, chan->dest, srv->nick);
919                         chan = find_channel(srv, arg, 1);
920                         chan_notice(chan, "Room: %s", arg);
921                 }
922                 else if (prefix(text, "/config", &arg)) {
923                         if (arg) {
924                                 chan_notice(chan, "Unimplemented: /config <arg>");
925                                 return;
926                         }
927                         net_print(&srv->net,
928                                 "<iq id='config' type='get' from='%s' to='%s'>"
929                                 "<query xmlns='http://jabber.org/protocol/muc#owner'/>"
930                                 "</iq>",
931                                 srv->jid, chan->dest);
932                 }
933                 else if (prefix(text, "/query", &arg)) {
934                         if (!arg) {
935                                 chan_notice(chan, "usage: /query <user>");
936                                 return;
937                         }
938                         chan = find_channel(srv, arg, 0);
939                         chan_notice(chan, "User: %s", arg);
940                 }
941                 else if (prefix(text, "/vcard", &arg)) {
942                         if (arg)
943                                 chan = find_channel(srv, arg, 0);
944                         if (chan)
945                                 net_print(&srv->net,
946                                         "<iq id='vcard' type='get' from='%s' to='%s'>"
947                                         "<vCard xmlns='vcard-temp'/>"
948                                         "</iq>",
949                                         srv->jid, chan->dest);
950                 }
951                 else {
952                         chan_notice(chan, "Unknown command %s", text);
953                 }
954         } else {
955                 debug("message: [%s]", text);
956                 if (chan == &srv->system) {
957                         chan_notice(chan, "Cannot send to server");
958                 }
959                 else if (!chan->dest) {
960                         chan_notice(chan, "No destination for message");
961                 }
962                 else if (chan->muc) {
963                         net_print(&srv->net,
964                                 "<message id='chat%d' from='%s' to='%s' type='groupchat'>"
965                                 "<body>%s</body>"
966                                 "</message>",
967                                 srv->id++, srv->jid, chan->dest, text);
968                 } else {
969                         net_print(&srv->net,
970                                 "<message id='chat%d' from='%s' to='%s' type='chat'>"
971                                 "<body>%s</body>"
972                                 "</message>",
973                                 srv->id++, srv->jid, chan->dest, text);
974                         chat_recv(channel, &srv->myself.user, text);
975                 }
976         }
977 }
978
979 void xmpp_exit(void)
980 {
981 }