]> Pileus Git - ~andy/lamechat/blob - xmpp.c
Defer vCard query.
[~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 <string.h>
24 #include <errno.h>
25 #include <expat.h>
26 #include <time.h>
27
28 #include "util.h"
29 #include "conf.h"
30 #include "chat.h"
31 #include "net.h"
32
33 /* Constants */
34 #define ID_LEN   256
35 #define AUTH_LEN 512
36 #define JID_LEN  256
37
38 /* XMPP types */
39 typedef enum {
40         XMPP_DEAD,
41         XMPP_CONNECT,
42         XMPP_ENCRYPT,
43         XMPP_RESTART,
44         XMPP_SEND_STREAM,
45         XMPP_RECV_FEATURES,
46         XMPP_SEND_STARTTLS,
47         XMPP_RECV_PROCEED,
48         XMPP_SEND_AUTH,
49         XMPP_RECV_SUCCESS,
50         XMPP_SEND_BIND,
51         XMPP_RECV_JID,
52         XMPP_SEND_SESSION,
53         XMPP_SEND_PRESENCE,
54         XMPP_SEND_JOIN,
55         XMPP_READY,
56         XMPP_IN_IQ,
57         XMPP_IN_VCARD,
58         XMPP_IN_MESSAGE,
59         XMPP_IN_PRESENCE,
60 } xmpp_state_t;
61
62 typedef struct {
63         user_t          user;
64         char            dest[JID_LEN];
65         int             muc;
66         char           *full_name;
67 } xmpp_user_t;
68
69 typedef struct {
70         channel_t       channel;
71         char            dest[JID_LEN];
72         const char     *type;
73         int             muc;
74
75         const char     *room;
76         int             join;
77 } xmpp_channel_t;
78
79 typedef struct {
80         server_t        server;
81         xmpp_user_t     myself;
82         xmpp_channel_t  system;
83
84         int             connect;
85         int             timeout;
86         int             noverify;
87         const char     *host;
88         int             port;
89         const char     *srv;
90         const char     *muc;
91         const char     *nick;
92         const char     *jid;
93         const char     *user;
94         const char     *pass;
95         int             quiet;
96
97         idle_t          idle;
98
99         net_t           net;
100         XML_Parser      expat;
101         xmpp_state_t    state;
102         buf_t           buf;
103         buf_t           html;
104         int             level;
105         int             id;
106         char           *bind;
107
108         char            msg_id[ID_LEN];
109         xmpp_channel_t *msg_chan;
110         xmpp_user_t    *msg_usr;
111         char           *msg_body;
112         char           *msg_html;
113         stamp_t         msg_stamp;
114
115         int             in_html;
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
284         return usr;
285 }
286
287 static const char *find_attr(const char **attrs, const char *name)
288 {
289         for (int i = 0; attrs[i] && attrs[i+1]; i += 2)
290                 if (match(attrs[i+0], name))
291                         return attrs[i+1];
292         return NULL;
293 }
294
295 static void lookup_user(xmpp_server_t *srv, xmpp_user_t *usr)
296 {
297         if (usr->full_name)
298                 return;
299         net_print(&srv->net,
300                 "<iq id='auto-vcard' type='get' from='%s' to='%s'>"
301                 "<vCard xmlns='vcard-temp'/>"
302                 "</iq>",
303                 srv->bind, usr->dest);
304 }
305
306 /* Callback functions */
307 static void xmpp_run(xmpp_server_t *srv, int idle,
308                 const char *start, const char **attrs,
309                 const char *end, const char *data);
310
311 static void on_start(void *_srv, const char *tag, const char **attrs)
312 {
313         xmpp_server_t *srv = _srv;
314         xmpp_run(srv, 0, tag, attrs, NULL, reset(&srv->buf));
315 }
316
317 static void on_data(void *_srv, const char *data, int len)
318 {
319         xmpp_server_t *srv = _srv;
320         append(&srv->buf, data, len);
321 }
322
323 static void on_end(void *_srv, const char *tag)
324 {
325         xmpp_server_t *srv = _srv;
326         xmpp_run(srv, 0, NULL, NULL, tag, reset(&srv->buf));
327 }
328
329 static void on_send(void *_srv)
330 {
331         xmpp_server_t *srv = _srv;
332         xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
333 }
334
335 static void on_recv(void *_srv, char *buf, int len)
336 {
337         xmpp_server_t *srv = _srv;
338         if (len > 0)
339                 XML_Parse(srv->expat, buf, len, 0);
340         xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
341 }
342
343 static void on_err(void *_srv, int err)
344 {
345         xmpp_server_t *srv = _srv;
346         srv_notice(srv, "Server disconnected");
347         srv->state = XMPP_DEAD;
348 }
349
350 static void on_timer(void *_srv)
351 {
352         xmpp_server_t *srv = _srv;
353         xmpp_run(srv, 1, NULL, NULL, NULL, NULL);
354 }
355
356 /* XMPP State machine */
357 static void xmpp_run(xmpp_server_t *srv, int idle,
358                 const char *start, const char **attrs,
359                 const char *end, const char *data)
360 {
361         /* Debug print */
362         if (data)
363                 debug("%*s \"%s\"", srv->level*4, "", data);
364         if (start) {
365                 debug("%*s<%s>", srv->level*4, "", start);
366                 for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
367                         debug("%*s%s=\"%s\"%s",
368                                 srv->level*4+8, "",
369                                 attrs[i+0], attrs[i+1],
370                                 attrs[i+2] ? "" : ">");
371                 }
372         }
373         if (start)
374                 srv->level++;
375         if (end)
376                 srv->level--;
377
378         /* Connection Handling */
379         if (srv->state == XMPP_CONNECT && !start && !end) {
380                 srv->net.send = on_send;
381                 srv->net.recv = on_recv;
382                 srv->net.err  = on_err;
383                 srv->net.data = srv;
384
385                 srv->idle.timer = on_timer;
386                 srv->idle.data  = srv;
387
388                 net_open(&srv->net, srv->host, srv->port);
389                 idle_add(&srv->idle);
390                 idle_set(&srv->idle, srv->timeout, srv->timeout);
391
392                 if (!(srv->expat = XML_ParserCreate(NULL)))
393                         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("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("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         /* Idle handling */
431         if (srv->state > XMPP_CONNECT && idle) {
432                 debug("xmpp: idle");
433                 net_print(&srv->net, " ");
434         }
435
436         /* Stream Start */
437         if (srv->state == XMPP_SEND_STREAM) {
438                 if (net_print(&srv->net,
439                     "<?xml version='1.0'?>"
440                     "<stream:stream"
441                     " from='%s'"
442                     " to='%s'"
443                     " version='1.0'"
444                     " xml:lang='en'"
445                     " xmlns='jabber:client'"
446                     " xmlns:stream='http://etherx.jabber.org/streams'>",
447                     srv->jid, srv->srv)) {
448                         debug("xmpp: stream -> features");
449                         srv->state = XMPP_RECV_FEATURES;
450                 }
451         }
452         if (srv->state == XMPP_RECV_FEATURES) {
453                 if (match(start, "starttls")) {
454                         debug("xmpp: features -> starttls");
455                         srv->state = XMPP_SEND_STARTTLS;
456                 }
457                 if (match(start, "mechanisms")) {
458                         debug("xmpp: features -> auth");
459                         srv->state = XMPP_SEND_AUTH;
460                 }
461                 if (match(start, "bind")) {
462                         debug("xmpp: features -> bind");
463                         srv->state = XMPP_SEND_BIND;
464                 }
465         }
466
467         /* Start TLS */
468         if (srv->state == XMPP_SEND_STARTTLS) {
469                 if (net_print(&srv->net,
470                     "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
471                         debug("xmpp: startls -> proceed");
472                         srv->state = XMPP_RECV_PROCEED;
473                 }
474         }
475         if (srv->state == XMPP_RECV_PROCEED) {
476                 if (match(start, "proceed")) {
477                         debug("xmpp: proceed -> encrypt");
478                         srv->state = XMPP_ENCRYPT;
479                 }
480         }
481
482         /* Authentication */
483         if (srv->state == XMPP_SEND_AUTH) {
484                 static char plain[AUTH_LEN];
485                 static char coded[AUTH_LEN];
486                 int len;
487                 len = snprintf(plain, AUTH_LEN, "%s%c%s%c%s",
488                                 srv->user, '\0', srv->user, '\0', srv->pass);
489                 len = base64(plain, len, coded, AUTH_LEN);
490                 if (net_print(&srv->net,
491                     "<auth"
492                     " xmlns='urn:ietf:params:xml:ns:xmpp-sasl'"
493                     " mechanism='PLAIN'>%.*s</auth>",
494                         len, coded)) {
495                         debug("xmpp: auth -> success");
496                         srv->state = XMPP_RECV_SUCCESS;
497                 }
498         }
499         if (srv->state == XMPP_RECV_SUCCESS) {
500                 if (match(start, "failure")) {
501                         debug("xmpp: success -> dead");
502                         srv_notice(srv, "Authentication failure");
503                         srv->state = XMPP_DEAD;
504                 }
505                 if (match(start, "success")) {
506                         debug("xmpp: success -> restart");
507                         srv_notice(srv, "Authentication success");
508                         srv->state = XMPP_RESTART;
509                 }
510         }
511
512         /* Binding */
513         if (srv->state == XMPP_SEND_BIND) {
514                 const char *resource = srv->jid;
515                 while (*resource && *resource != '/')
516                         resource++;
517                 while (*resource && *resource == '/')
518                         resource++;
519                 if (net_print(&srv->net,
520                     "<iq id='bind' type='set'>"
521                     "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
522                     "<resource>%s</resource>"
523                     "</bind>"
524                     "</iq>",
525                     resource)) {
526                         debug("xmpp: bind -> jid");
527                         srv->state = XMPP_RECV_JID;
528                 }
529         }
530         if (srv->state == XMPP_RECV_JID) {
531                 if (match(end, "jid")) {
532                         debug("xmpp: jid -> session");
533                         strset(&srv->bind, data ?: srv->jid);
534                         srv->state = XMPP_SEND_SESSION;
535                 }
536         }
537         if (srv->state == XMPP_SEND_SESSION) {
538                 if (net_print(&srv->net,
539                     "<iq id='session' type='set' to='%s'>"
540                     "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
541                     "</iq>",
542                     srv->srv)) {
543                         debug("xmpp: session -> presence");
544                         srv->state = XMPP_SEND_PRESENCE;
545                 }
546         }
547         if (srv->state == XMPP_SEND_PRESENCE) {
548                 if (net_print(&srv->net, "<presence/>")) {
549                         debug("xmpp: presence -> join");
550                         srv->state = XMPP_SEND_JOIN;
551                 }
552         }
553         if (srv->state == XMPP_SEND_JOIN) {
554                 for (channel_t *cur = channels; cur; cur = cur->next) {
555                         if (cur->server != &srv->server)
556                                 continue;
557                         xmpp_channel_t *chan = (xmpp_channel_t *)cur;
558                         if (!chan->join)
559                                 continue;
560                         if (!srv->quiet)
561                                 chan_notice(chan, "XMPP Channel: %s", chan->channel.name);
562                         net_print(&srv->net,
563                                 "<presence id='join' from='%s' to='%s/%s'>"
564                                 "<x xmlns='http://jabber.org/protocol/muc'/>"
565                                 "</presence>",
566                                 srv->bind, chan->dest, srv->nick);
567                 }
568                 debug("xmpp: join -> ready");
569                 srv->state = XMPP_READY;
570         }
571
572         /* Start message */
573         if (srv->state == XMPP_READY && start) {
574                 const char *id   = find_attr(attrs, "id");
575                 const char *from = find_attr(attrs, "from");
576                 const char *type = find_attr(attrs, "type");
577                 int is_muc = match(type, "groupchat");
578
579                 /* Ignore presence errors (federated remote timeout, etc) */
580                 if (match(type, "error"))
581                         return;
582
583                 strncpy(srv->msg_id, id ?: "", ID_LEN);
584
585                 srv->msg_chan = find_channel(srv, from, is_muc);
586                 srv->msg_usr  = find_user(srv, from, is_muc);
587
588                 if (match(start, "iq"))
589                         srv->state = XMPP_IN_IQ;
590                 if (match(start, "message"))
591                         srv->state = XMPP_IN_MESSAGE;
592                 if (match(start, "presence"))
593                         srv->state = XMPP_IN_PRESENCE;
594
595                 if (srv->state != XMPP_READY)
596                         debug("xmpp: ready -> in_%s -- "
597                               "from=[%s] -> chan=[%s:%s] user=[%s]",
598                                 start, from,
599                                 srv->msg_chan->type,
600                                 srv->msg_chan->channel.name,
601                                 srv->msg_usr ? srv->msg_usr->user.name : "(none)");
602         }
603
604         /* Shorthand Message Data */
605         xmpp_channel_t *chan  = NULL;
606         xmpp_user_t    *usr   = NULL;
607         xmpp_user_t    *alias = NULL;
608
609         if (srv->state > XMPP_READY) {
610                 chan = srv->msg_chan;
611                 usr  = srv->msg_usr;
612                 if (usr)
613                         alias = (xmpp_user_t*)usr->user.alias;
614         }
615
616         /* Info/Queries */
617         if (srv->state == XMPP_IN_IQ) {
618                 if (match(start, "item")) {
619                         if (chan == &srv->system) {
620                                 srv_notice(srv, "item: [%s] %s",
621                                                 find_attr(attrs, "jid"),
622                                                 find_attr(attrs, "name"));
623                         } else if (chan->muc) {
624                                 const char  *jid = find_attr(attrs, "jid");
625                                 xmpp_user_t *usr = find_user(srv, jid, 1);
626                                 chan_notice(chan, "User: %s (%s)",
627                                                 usr->user.name, jid);
628                         } else {
629                                 chan_notice(chan, "item: [%s] %s",
630                                                 find_attr(attrs, "jid"),
631                                                 find_attr(attrs, "name"));
632                         }
633                 }
634                 if (match(start, "identity")) {
635                         srv_notice(srv, "identity: %s",
636                                         find_attr(attrs, "name"));
637                 }
638                 if (match(start, "feature")) {
639                         srv_notice(srv, "feature: %s",
640                                         find_attr(attrs, "var"));
641                 }
642                 if (match(start, "field")) {
643                         debug("xmpp: %s -- type=[%s] label=[%s]", end,
644                                 find_attr(attrs, "type"),
645                                 find_attr(attrs, "label"));
646                         if (!find_attr(attrs, "label"))
647                                 return;
648                         chan_notice(chan, "%-36s -- %s (%s)",
649                                 find_attr(attrs, "var"),
650                                 find_attr(attrs, "label"),
651                                 find_attr(attrs, "type"));
652                 }
653                 if (match(end, "title")) {
654                         debug("xmpp: title -- chan=[%s]",
655                                 end, chan->dest);
656                         chan_notice(chan, "Title: %s", data);
657                 }
658                 if (match(end, "instructions")) {
659                         debug("xmpp: instructions -- chan=[%s]",
660                                 end, chan->dest);
661                         chan_notice(chan, "%s", data);
662                 }
663         }
664
665         /* vCards */
666         if (srv->state == XMPP_IN_IQ) {
667                 if (match(start, "vCard")) {
668                         if (!match(srv->msg_id, "auto-vcard"))
669                                 chan_notice(chan, "Begin vCard (%s)",
670                                                 usr ?  usr->user.name :
671                                                 chan->channel.name);
672                         srv->state = XMPP_IN_VCARD;
673                 }
674         }
675         if (srv->state == XMPP_IN_VCARD) {
676                 if (end && srv->level == 3) {
677                         if (!match(srv->msg_id, "auto-vcard") &&
678                             !match(end, "BINVAL"))
679                                 chan_notice(chan, "  %s -> %s", end, data ?: "...");
680                 }
681                 if (usr) {
682                         if (match(end, "FN")) {
683                                 strset(&chan->channel.name, data);
684                                 strset(&usr->user.name, data);
685                                 strset(&usr->full_name, data);
686                                 chat_update();
687                         }
688                 }
689         }
690         if (srv->state == XMPP_IN_VCARD) {
691                 if (match(end, "vCard")) {
692                         if (!match(srv->msg_id, "auto-vcard"))
693                                 chan_notice(chan, "End vCard");
694                         srv->state = XMPP_IN_IQ;
695                 }
696         }
697
698         /* Messages */
699         if (srv->state == XMPP_IN_MESSAGE) {
700                 if (match(start, "delay")) {
701                         const char *ts = find_attr(attrs, "stamp");
702                         if (ts) {
703                                 struct tm tm = {};
704                                 strptime(ts, "%Y-%m-%dT%H:%M:%S", &tm);
705                                 srv->msg_stamp = timegm(&tm);
706                         }
707                 }
708                 if (match(end, "subject")) {
709                         strset(&chan->channel.topic, data);
710                         chan_notice(chan, "Topic: %s", data);
711                 }
712                 if (match(end, "body") && data) {
713                         strset(&srv->msg_body, data);
714                 }
715                 if (match(start, "html")) {
716                         srv->in_html = 1;
717                 }
718                 if (srv->in_html && data) {
719                         append(&srv->html, data, strlen(data));
720                 }
721                 if (match(end, "html")) {
722                         strset(&srv->msg_html, reset(&srv->html));
723                         srv->in_html = 0;
724                 }
725                 if (match(end, "message")) {
726                         debug("xmpp: body (%s) -- chan=[%s] from=[%s]",
727                                 chan->type,
728                                 chan->channel.name,
729                                 usr ? usr->user.name : "(none)");
730                         char *content = srv->msg_body;
731                         if (srv->msg_html)
732                                 content = despace(srv->msg_html);
733                         if (usr && !usr->muc)
734                                 lookup_user(srv, usr);
735                         if (content) {
736                                 chat_recv(&chan->channel, &usr->user, content);
737                                 message_t *msg = &messages[history-1];
738                                 msg->when = srv->msg_stamp ?: msg->when;
739                         }
740                         srv->msg_stamp = 0;
741                         strset(&srv->msg_body, NULL);
742                         strset(&srv->msg_html, NULL);
743                 }
744
745         }
746
747         /* Presence */
748         if (srv->state == XMPP_IN_PRESENCE) {
749                 if (match(start, "item") && usr) {
750                         const char  *jid   = find_attr(attrs, "jid");
751                         xmpp_user_t *alias = find_user(srv, jid, 0);
752                         if (jid && alias)
753                                 usr->user.alias = &alias->user;
754                 }
755                 if (match(end, "presence") && !srv->quiet) {
756                         if (alias)
757                                 chan_notice(chan, "%s (%s) entered room.",
758                                                 usr->user.name, alias->user.name);
759                         else if (usr)
760                                 chan_notice(chan, "%s entered room.",
761                                                 usr->user.name);
762                 }
763         }
764
765         /* End messages */
766         if (srv->state == XMPP_IN_IQ)
767                 if (match(end, "iq"))
768                         srv->state = XMPP_READY;
769         if (srv->state == XMPP_IN_MESSAGE)
770                 if (match(end, "message"))
771                         srv->state = XMPP_READY;
772         if (srv->state == XMPP_IN_PRESENCE)
773                 if (match(end, "presence"))
774                         srv->state = XMPP_READY;
775
776         /* Error handling */
777         if (match(start, "stream:error"))
778                 srv->in_error = 1;
779         if (match(end, "stream:error"))
780                 srv->in_error = 0;
781         if (srv->in_error) {
782                 if (match(end, "text")) {
783                         debug("xmpp: error: %s", data);
784                         srv_notice(srv, "error: %s", data);
785                 }
786         }
787 }
788
789 /* XMPP functions */
790 void xmpp_init(void)
791 {
792         static char jid_usr[JID_LEN];
793         static char jid_srv[JID_LEN];
794         static char jid_res[JID_LEN];
795
796         for (server_t *cur = servers; cur; cur = cur->next) {
797                 if (cur->protocol != XMPP)
798                         continue;
799
800                 xmpp_server_t *srv = (xmpp_server_t*)cur;
801                 split_jid(srv->jid, jid_usr, jid_srv, jid_res);
802
803                 if (!srv->jid)
804                         error("jid is required");
805                 if (!srv->host)
806                         srv->host = strcopy(jid_srv);
807                 if (!srv->port)
808                         srv->port = 5222;
809                 if (!srv->muc)
810                         srv->muc = strcopy(srv->host);
811                 if (!srv->srv)
812                         srv->srv = strcopy(srv->host);
813                 if (!srv->user)
814                         srv->user = strcopy(jid_usr);
815                 if (!srv->nick)
816                         srv->nick = strcopy(jid_usr);
817                 if (!srv->timeout)
818                         srv->timeout = 60;
819
820                 srv->system.type  = "sys";
821                 srv->system.channel.server = &srv->server;
822                 srv->system.channel.name = strcopy(srv->server.name);
823
824                 strncpy(srv->myself.dest, srv->jid, JID_LEN);
825                 srv->myself.user.server = &srv->server;
826                 srv->myself.user.name = strcopy(srv->nick);
827
828                 if (srv->connect && !srv->quiet)
829                         srv_notice(srv, "XMPP Server: %s", srv->server.name);
830                 if (srv->connect) {
831                         srv->state = XMPP_CONNECT;
832                         xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
833                 }
834         }
835         for (channel_t *cur = channels; cur; cur = cur->next) {
836                 if (cur->server->protocol != XMPP)
837                         continue;
838
839                 xmpp_channel_t *chan = (xmpp_channel_t*)cur;
840                 xmpp_server_t *srv = (xmpp_server_t*)cur->server;
841                 chan->muc = 1;
842                 chan->type = "muc";
843                 if (!chan->room)
844                         chan->room = strcopy(cur->name);
845                 snprintf(chan->dest, JID_LEN, "%s@%s",
846                          chan->room, srv->muc);
847         }
848 }
849
850 void xmpp_config(server_t *server, channel_t *channel,
851                  const char *group, const char *name,
852                  const char *key, const char *value)
853 {
854         xmpp_server_t  *srv  = (xmpp_server_t*)server;
855         xmpp_channel_t *chan = (xmpp_channel_t*)channel;
856
857         if (match(group, "server")) {
858                 if (match(key, "protocol")) {
859                         xmpp_server_t *srv = new0(xmpp_server_t);
860                         srv->server.protocol = XMPP;
861                         srv->server.name = strcopy(get_name(name));
862                         add_server(&srv->server);
863                 }
864                 else if (match(key, "connect"))
865                         srv->connect = get_bool(value);
866                 else if (match(key, "timeout"))
867                         srv->timeout = get_number(value);
868                 else if (match(key, "noverify"))
869                         srv->noverify = get_bool(value);
870                 else if (match(key, "host"))
871                         srv->host = get_string(value);
872                 else if (match(key, "port"))
873                         srv->port = get_number(value);
874                 else if (match(key, "srv"))
875                         srv->srv = get_string(value);
876                 else if (match(key, "muc"))
877                         srv->muc = get_string(value);
878                 else if (match(key, "nick"))
879                         srv->nick = get_string(value);
880                 else if (match(key, "jid"))
881                         srv->jid = get_string(value);
882                 else if (match(key, "user"))
883                         srv->user = get_string(value);
884                 else if (match(key, "pass"))
885                         srv->pass = get_string(value);
886                 else if (match(key, "quiet"))
887                         srv->quiet = get_bool(value);
888         }
889         if (match(group, "channel")) {
890                 if (match(key, "server")) {
891                         xmpp_channel_t *chan = new0(xmpp_channel_t);
892                         chan->channel.server = &srv->server;
893                         chan->channel.name = strcopy(get_name(name));
894                         add_channel(&chan->channel);
895                 }
896                 else if (match(key, "room"))
897                         chan->room = get_string(value);
898                 else if (match(key, "join"))
899                         chan->join = get_bool(value);
900         }
901 }
902
903 void xmpp_send(channel_t *channel, const char *text)
904 {
905         static char buf[4096];
906
907         xmpp_channel_t *chan = (xmpp_channel_t*)channel;
908         xmpp_server_t  *srv  = (xmpp_server_t*)channel->server;
909         const char *arg;
910
911         /* Escape HTML */
912         escape(buf, text, sizeof(buf));
913         text = buf;
914
915         /* Handle commands */
916         if (text[0] == '/') {
917                 if (prefix(text, "/items", &arg)) {
918                         net_print(&srv->net,
919                                 "<iq id='items' type='get' from='%s' to='%s'>"
920                                 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
921                                 "</iq>",
922                                 srv->bind, arg ?: srv->srv);
923                 }
924                 else if (prefix(text, "/info", &arg)) {
925                         net_print(&srv->net,
926                                 "<iq id='info' type='get' from='%s' to='%s'>"
927                                 "<query xmlns='http://jabber.org/protocol/disco#info'/>"
928                                 "</iq>",
929                                 srv->bind, arg ?: srv->srv);
930                 }
931                 else if (prefix(text, "/names", &arg)) {
932                         if (arg)
933                                 chan = find_channel(srv, arg, 1);
934                         if (chan == &srv->system) {
935                                 chan_notice(chan, "Cannot get names from server");
936                                 return;
937                         }
938                         net_print(&srv->net,
939                                 "<iq id='names' type='get' from='%s' to='%s'>"
940                                 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
941                                 "</iq>",
942                                 srv->bind, chan->dest);
943                 }
944                 else if (prefix(text, "/join", &arg)) {
945                         if (!arg) {
946                                 chan_notice(chan, "usage: /join <channel>");
947                                 return;
948                         }
949                         chan = find_channel(srv, arg, 1);
950                         net_print(&srv->net,
951                                 "<presence id='join' from='%s' to='%s/%s'>"
952                                 "<x xmlns='http://jabber.org/protocol/muc'/>"
953                                 "</presence>",
954                                 srv->bind, chan->dest, srv->nick);
955                         chan_notice(chan, "Room: %s", arg);
956                 }
957                 else if (prefix(text, "/config", &arg)) {
958                         if (arg) {
959                                 chan_notice(chan, "Unimplemented: /config <arg>");
960                                 return;
961                         }
962                         if (chan == &srv->system) {
963                                 chan_notice(chan, "Cannot get config from server");
964                                 return;
965                         }
966                         net_print(&srv->net,
967                                 "<iq id='config' type='get' from='%s' to='%s'>"
968                                 "<query xmlns='http://jabber.org/protocol/muc#owner'/>"
969                                 "</iq>",
970                                 srv->bind, chan->dest);
971                 }
972                 else if (prefix(text, "/query", &arg)) {
973                         if (!arg) {
974                                 chan_notice(chan, "usage: /query <user>");
975                                 return;
976                         }
977                         chan = find_channel(srv, arg, 0);
978                         chan_notice(chan, "User: %s", arg);
979                 }
980                 else if (prefix(text, "/vcard", &arg)) {
981                         if (arg)
982                                 chan = find_channel(srv, arg, 0);
983                         if (chan)
984                                 net_print(&srv->net,
985                                         "<iq id='vcard' type='get' from='%s' to='%s'>"
986                                         "<vCard xmlns='vcard-temp'/>"
987                                         "</iq>",
988                                         srv->bind, chan->dest);
989                 }
990                 else {
991                         chan_notice(chan, "Unknown command %s", text);
992                 }
993         } else {
994                 debug("message: [%s]", text);
995                 if (chan == &srv->system) {
996                         chan_notice(chan, "Cannot send to server");
997                 }
998                 else if (chan->muc) {
999                         net_print(&srv->net,
1000                                 "<message id='chat%d' from='%s' to='%s' type='groupchat'>"
1001                                 "<body>%s</body>"
1002                                 "</message>",
1003                                 srv->id++, srv->bind, chan->dest, text);
1004                 } else {
1005                         net_print(&srv->net,
1006                                 "<message id='chat%d' from='%s' to='%s' type='chat'>"
1007                                 "<body>%s</body>"
1008                                 "</message>",
1009                                 srv->id++, srv->bind, chan->dest, text);
1010                         chat_recv(channel, &srv->myself.user, text);
1011                 }
1012         }
1013 }
1014
1015 void xmpp_exit(void)
1016 {
1017 }