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