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