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