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