2 * Copyright (C) 2017 Andy Spencer <andy753421@gmail.com>
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.
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.
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/>.
82 xmpp_channel_t system;
109 xmpp_channel_t *msg_chan;
110 xmpp_user_t *msg_usr;
119 /* Helper functions */
120 static void srv_notice(xmpp_server_t *srv, const char *fmt, ...)
122 static char buf[1024];
126 vsnprintf(buf, sizeof(buf), fmt, ap);
129 debug("xmpp: srv_notice: [%s]", buf);
130 chat_recv(&srv->system.channel, NULL, buf);
133 static void chan_notice(xmpp_channel_t *chan, const char *fmt, ...)
135 static char buf[1024];
139 vsnprintf(buf, sizeof(buf), fmt, ap);
142 debug("xmpp: chan_notice -- %s [%s]",
143 chan->channel.name, buf);
144 chat_recv(&chan->channel, NULL, buf);
147 static void split_jid(const char *jid, char *usr, char *srv, char *res)
152 if (usr) usr[0] = '\0';
153 if (srv) srv[0] = '\0';
154 if (res) res[0] = '\0';
156 for (int i = 0; jid && jid[i]; i++) {
167 if (ptr && (pos+1) < JID_LEN) {
173 //debug("JID: '%s' usr=[%s] srv=[%s] res=[%s]",
174 // jid, usr, srv, res);
177 static xmpp_channel_t *find_channel(xmpp_server_t *srv,
178 const char *jid, int is_muc)
180 static char jid_usr[JID_LEN];
181 static char jid_srv[JID_LEN];
182 static char dest[JID_LEN];
183 xmpp_channel_t *chan;
185 /* Server channels */
186 if (!jid || match(jid, srv->srv))
189 /* Parse JID and check for MUC */
190 split_jid(jid, jid_usr, jid_srv, NULL);
191 if (match(jid_srv, srv->muc))
194 /* Find resource-less JID */
195 snprintf(dest, JID_LEN, "%s@%s", jid_usr,
196 jid_srv[0] ? jid_srv :
197 is_muc ? srv->muc : srv->srv);
199 /* Find existing channels */
200 for (channel_t *cur = channels; cur; cur = cur->next) {
201 if (cur->server != &srv->server)
203 chan = (xmpp_channel_t *)cur;
204 if (match(chan->dest, dest))
208 /* Create a new channel */
209 chan = new0(xmpp_channel_t);
211 chan->type = is_muc ? "muc" : "usr";
212 chan->channel.server = &srv->server;
213 chan->channel.name = strcopy(jid_usr);
214 strncpy(chan->dest, dest, JID_LEN);
215 add_channel(&chan->channel);
219 static xmpp_user_t *find_user(xmpp_server_t *srv,
220 const char *jid, int is_muc)
222 static char jid_usr[JID_LEN];
223 static char jid_srv[JID_LEN];
224 static char jid_res[JID_LEN];
225 static char dest[JID_LEN];
228 /* Server channels */
229 if (!jid || match(jid, srv->srv))
232 /* Parse JID and check for MUC */
233 split_jid(jid, jid_usr, jid_srv, jid_res);
234 if (match(jid_srv, srv->muc))
237 /* Channel notices have no resource */
238 if (is_muc && !jid_res[0])
241 /* Ignore resources for real users */
243 snprintf(dest, JID_LEN, "%s@%s/%s",
245 jid_srv[0] ? jid_srv : srv->muc,
248 snprintf(dest, JID_LEN, "%s@%s",
250 jid_srv[0] ? jid_srv : srv->srv);
252 /* Find existing users */
253 for (user_t *cur = users; cur; cur = cur->next) {
254 if (cur->server != &srv->server)
256 usr = (xmpp_user_t *)cur;
257 if (match(usr->dest, dest)) {
258 debug("xmpp: found user: \"%s\" -> "
259 "name=[%s] dest=[%s] alias=[%s]",
260 jid, usr->user.name, usr->dest,
261 usr->user.alias ? usr->user.alias->name : "(none)");
266 /* Create a new user */
267 usr = new0(xmpp_user_t);
268 usr->user.server = &srv->server;
270 usr->user.name = strcopy(jid_res);
271 usr->full_name = strcopy(jid_res);
273 usr->user.name = strcopy(jid_usr);
276 strncpy(usr->dest, dest, JID_LEN);
277 add_user(&usr->user);
279 /* Send vcard probe */
280 debug("xmpp: added user: \"%s\"%s -> name=[%s] dest=[%s]",
281 jid, is_muc ? " (muc)" : "",
282 usr->user.name, usr->dest);
287 static const char *find_attr(const char **attrs, const char *name)
289 for (int i = 0; attrs[i] && attrs[i+1]; i += 2)
290 if (match(attrs[i+0], name))
295 static void lookup_user(xmpp_server_t *srv, xmpp_user_t *usr)
300 "<iq id='auto-vcard' type='get' from='%s' to='%s'>"
301 "<vCard xmlns='vcard-temp'/>"
303 srv->bind, usr->dest);
306 /* Callback functions */
307 static void xmpp_run(xmpp_server_t *srv, int idle,
308 const char *start, const char **attrs,
309 const char *end, const char *data);
311 static void on_start(void *_srv, const char *tag, const char **attrs)
313 xmpp_server_t *srv = _srv;
314 xmpp_run(srv, 0, tag, attrs, NULL, reset(&srv->buf));
317 static void on_data(void *_srv, const char *data, int len)
319 xmpp_server_t *srv = _srv;
320 append(&srv->buf, data, len);
323 static void on_end(void *_srv, const char *tag)
325 xmpp_server_t *srv = _srv;
326 xmpp_run(srv, 0, NULL, NULL, tag, reset(&srv->buf));
329 static void on_send(void *_srv)
331 xmpp_server_t *srv = _srv;
332 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
335 static void on_recv(void *_srv, char *buf, int len)
337 xmpp_server_t *srv = _srv;
339 XML_Parse(srv->expat, buf, len, 0);
340 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
343 static void on_err(void *_srv, int err)
345 xmpp_server_t *srv = _srv;
346 srv_notice(srv, "Server disconnected");
347 srv->state = XMPP_DEAD;
350 static void on_timer(void *_srv)
352 xmpp_server_t *srv = _srv;
353 xmpp_run(srv, 1, NULL, NULL, NULL, NULL);
356 /* XMPP State machine */
357 static void xmpp_run(xmpp_server_t *srv, int idle,
358 const char *start, const char **attrs,
359 const char *end, const char *data)
363 debug("%*s \"%s\"", srv->level*4, "", data);
365 debug("%*s<%s>", srv->level*4, "", start);
366 for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
367 debug("%*s%s=\"%s\"%s",
369 attrs[i+0], attrs[i+1],
370 attrs[i+2] ? "" : ">");
378 /* Connection Handling */
379 if (srv->state == XMPP_CONNECT && !start && !end) {
380 srv->net.send = on_send;
381 srv->net.recv = on_recv;
382 srv->net.err = on_err;
385 srv->idle.timer = on_timer;
386 srv->idle.data = srv;
388 net_open(&srv->net, srv->host, srv->port);
389 idle_add(&srv->idle);
390 idle_set(&srv->idle, srv->timeout, srv->timeout);
392 if (!(srv->expat = XML_ParserCreate(NULL)))
393 error("creating XML parser");
394 XML_SetUserData(srv->expat, srv);
395 XML_SetStartElementHandler(srv->expat, on_start);
396 XML_SetEndElementHandler(srv->expat, on_end);
397 XML_SetCharacterDataHandler(srv->expat, on_data);
399 debug("xmpp: connect -> stream");
401 srv->state = XMPP_SEND_STREAM;
403 if (srv->state == XMPP_ENCRYPT && !start && !end) {
404 net_encrypt(&srv->net, srv->noverify ? NET_NOVERIFY : 0);
406 if (!(XML_ParserReset(srv->expat, NULL)))
407 error("resetting XML parser");
408 XML_SetUserData(srv->expat, srv);
409 XML_SetStartElementHandler(srv->expat, on_start);
410 XML_SetEndElementHandler(srv->expat, on_end);
411 XML_SetCharacterDataHandler(srv->expat, on_data);
413 debug("xmpp: encrypt -> stream");
415 srv->state = XMPP_SEND_STREAM;
417 if (srv->state == XMPP_RESTART && !start && !end) {
418 if (!(XML_ParserReset(srv->expat, NULL)))
419 error("resetting XML parser");
420 XML_SetUserData(srv->expat, srv);
421 XML_SetStartElementHandler(srv->expat, on_start);
422 XML_SetEndElementHandler(srv->expat, on_end);
423 XML_SetCharacterDataHandler(srv->expat, on_data);
425 debug("xmpp: restart -> stream");
427 srv->state = XMPP_SEND_STREAM;
431 if (srv->state > XMPP_CONNECT && idle) {
433 net_print(&srv->net, " ");
437 if (srv->state == XMPP_SEND_STREAM) {
438 if (net_print(&srv->net,
439 "<?xml version='1.0'?>"
445 " xmlns='jabber:client'"
446 " xmlns:stream='http://etherx.jabber.org/streams'>",
447 srv->jid, srv->srv)) {
448 debug("xmpp: stream -> features");
449 srv->state = XMPP_RECV_FEATURES;
452 if (srv->state == XMPP_RECV_FEATURES) {
453 if (match(start, "starttls")) {
454 debug("xmpp: features -> starttls");
455 srv->state = XMPP_SEND_STARTTLS;
457 if (match(start, "mechanisms")) {
458 debug("xmpp: features -> auth");
459 srv->state = XMPP_SEND_AUTH;
461 if (match(start, "bind")) {
462 debug("xmpp: features -> bind");
463 srv->state = XMPP_SEND_BIND;
468 if (srv->state == XMPP_SEND_STARTTLS) {
469 if (net_print(&srv->net,
470 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
471 debug("xmpp: startls -> proceed");
472 srv->state = XMPP_RECV_PROCEED;
475 if (srv->state == XMPP_RECV_PROCEED) {
476 if (match(start, "proceed")) {
477 debug("xmpp: proceed -> encrypt");
478 srv->state = XMPP_ENCRYPT;
483 if (srv->state == XMPP_SEND_AUTH) {
484 static char plain[AUTH_LEN];
485 static char coded[AUTH_LEN];
487 len = snprintf(plain, AUTH_LEN, "%s%c%s%c%s",
488 srv->user, '\0', srv->user, '\0', srv->pass);
489 len = base64(plain, len, coded, AUTH_LEN);
490 if (net_print(&srv->net,
492 " xmlns='urn:ietf:params:xml:ns:xmpp-sasl'"
493 " mechanism='PLAIN'>%.*s</auth>",
495 debug("xmpp: auth -> success");
496 srv->state = XMPP_RECV_SUCCESS;
499 if (srv->state == XMPP_RECV_SUCCESS) {
500 if (match(start, "failure")) {
501 debug("xmpp: success -> dead");
502 srv_notice(srv, "Authentication failure");
503 srv->state = XMPP_DEAD;
505 if (match(start, "success")) {
506 debug("xmpp: success -> restart");
507 srv_notice(srv, "Authentication success");
508 srv->state = XMPP_RESTART;
513 if (srv->state == XMPP_SEND_BIND) {
514 const char *resource = srv->jid;
515 while (*resource && *resource != '/')
517 while (*resource && *resource == '/')
519 if (net_print(&srv->net,
520 "<iq id='bind' type='set'>"
521 "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
522 "<resource>%s</resource>"
526 debug("xmpp: bind -> jid");
527 srv->state = XMPP_RECV_JID;
530 if (srv->state == XMPP_RECV_JID) {
531 if (match(end, "jid")) {
532 debug("xmpp: jid -> session");
533 strset(&srv->bind, data ?: srv->jid);
534 srv->state = XMPP_SEND_SESSION;
537 if (srv->state == XMPP_SEND_SESSION) {
538 if (net_print(&srv->net,
539 "<iq id='session' type='set' to='%s'>"
540 "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
543 debug("xmpp: session -> presence");
544 srv->state = XMPP_SEND_PRESENCE;
547 if (srv->state == XMPP_SEND_PRESENCE) {
548 if (net_print(&srv->net, "<presence/>")) {
549 debug("xmpp: presence -> join");
550 srv->state = XMPP_SEND_JOIN;
553 if (srv->state == XMPP_SEND_JOIN) {
554 for (channel_t *cur = channels; cur; cur = cur->next) {
555 if (cur->server != &srv->server)
557 xmpp_channel_t *chan = (xmpp_channel_t *)cur;
561 chan_notice(chan, "XMPP Channel: %s", chan->channel.name);
563 "<presence id='join' from='%s' to='%s/%s'>"
564 "<x xmlns='http://jabber.org/protocol/muc'/>"
566 srv->bind, chan->dest, srv->nick);
568 debug("xmpp: join -> ready");
569 srv->state = XMPP_READY;
573 if (srv->state == XMPP_READY && start) {
574 const char *id = find_attr(attrs, "id");
575 const char *from = find_attr(attrs, "from");
576 const char *type = find_attr(attrs, "type");
577 int is_muc = match(type, "groupchat");
579 /* Ignore presence errors (federated remote timeout, etc) */
580 if (match(type, "error"))
583 strncpy(srv->msg_id, id ?: "", ID_LEN);
585 srv->msg_chan = find_channel(srv, from, is_muc);
586 srv->msg_usr = find_user(srv, from, is_muc);
588 if (match(start, "iq"))
589 srv->state = XMPP_IN_IQ;
590 if (match(start, "message"))
591 srv->state = XMPP_IN_MESSAGE;
592 if (match(start, "presence"))
593 srv->state = XMPP_IN_PRESENCE;
595 if (srv->state != XMPP_READY)
596 debug("xmpp: ready -> in_%s -- "
597 "from=[%s] -> chan=[%s:%s] user=[%s]",
600 srv->msg_chan->channel.name,
601 srv->msg_usr ? srv->msg_usr->user.name : "(none)");
604 /* Shorthand Message Data */
605 xmpp_channel_t *chan = NULL;
606 xmpp_user_t *usr = NULL;
607 xmpp_user_t *alias = NULL;
609 if (srv->state > XMPP_READY) {
610 chan = srv->msg_chan;
613 alias = (xmpp_user_t*)usr->user.alias;
617 if (srv->state == XMPP_IN_IQ) {
618 if (match(start, "item")) {
619 if (chan == &srv->system) {
620 srv_notice(srv, "item: [%s] %s",
621 find_attr(attrs, "jid"),
622 find_attr(attrs, "name"));
623 } else if (chan->muc) {
624 const char *jid = find_attr(attrs, "jid");
625 xmpp_user_t *usr = find_user(srv, jid, 1);
626 chan_notice(chan, "User: %s (%s)",
627 usr->user.name, jid);
629 chan_notice(chan, "item: [%s] %s",
630 find_attr(attrs, "jid"),
631 find_attr(attrs, "name"));
634 if (match(start, "identity")) {
635 srv_notice(srv, "identity: %s",
636 find_attr(attrs, "name"));
638 if (match(start, "feature")) {
639 srv_notice(srv, "feature: %s",
640 find_attr(attrs, "var"));
642 if (match(start, "field")) {
643 debug("xmpp: %s -- type=[%s] label=[%s]", end,
644 find_attr(attrs, "type"),
645 find_attr(attrs, "label"));
646 if (!find_attr(attrs, "label"))
648 chan_notice(chan, "%-36s -- %s (%s)",
649 find_attr(attrs, "var"),
650 find_attr(attrs, "label"),
651 find_attr(attrs, "type"));
653 if (match(end, "title")) {
654 debug("xmpp: title -- chan=[%s]",
656 chan_notice(chan, "Title: %s", data);
658 if (match(end, "instructions")) {
659 debug("xmpp: instructions -- chan=[%s]",
661 chan_notice(chan, "%s", data);
666 if (srv->state == XMPP_IN_IQ) {
667 if (match(start, "vCard")) {
668 if (!match(srv->msg_id, "auto-vcard"))
669 chan_notice(chan, "Begin vCard (%s)",
670 usr ? usr->user.name :
672 srv->state = XMPP_IN_VCARD;
675 if (srv->state == XMPP_IN_VCARD) {
676 if (end && srv->level == 3) {
677 if (!match(srv->msg_id, "auto-vcard") &&
678 !match(end, "BINVAL"))
679 chan_notice(chan, " %s -> %s", end, data ?: "...");
682 if (match(end, "FN")) {
683 strset(&chan->channel.name, data);
684 strset(&usr->user.name, data);
685 strset(&usr->full_name, data);
690 if (srv->state == XMPP_IN_VCARD) {
691 if (match(end, "vCard")) {
692 if (!match(srv->msg_id, "auto-vcard"))
693 chan_notice(chan, "End vCard");
694 srv->state = XMPP_IN_IQ;
699 if (srv->state == XMPP_IN_MESSAGE) {
700 if (match(start, "delay")) {
701 const char *ts = find_attr(attrs, "stamp");
704 strptime(ts, "%Y-%m-%dT%H:%M:%S", &tm);
705 srv->msg_stamp = timegm(&tm);
708 if (match(end, "subject")) {
709 strset(&chan->channel.topic, data);
710 chan_notice(chan, "Topic: %s", data);
712 if (match(end, "body") && data) {
713 strset(&srv->msg_body, data);
715 if (match(start, "html")) {
718 if (srv->in_html && data) {
719 append(&srv->html, data, strlen(data));
721 if (match(end, "html")) {
722 strset(&srv->msg_html, reset(&srv->html));
725 if (match(end, "message")) {
726 debug("xmpp: body (%s) -- chan=[%s] from=[%s]",
729 usr ? usr->user.name : "(none)");
730 char *content = srv->msg_body;
732 content = despace(srv->msg_html);
733 if (usr && !usr->muc)
734 lookup_user(srv, usr);
736 chat_recv(&chan->channel, &usr->user, content);
737 message_t *msg = &messages[history-1];
738 msg->when = srv->msg_stamp ?: msg->when;
741 strset(&srv->msg_body, NULL);
742 strset(&srv->msg_html, NULL);
748 if (srv->state == XMPP_IN_PRESENCE) {
749 if (match(start, "item") && usr) {
750 const char *jid = find_attr(attrs, "jid");
751 xmpp_user_t *alias = find_user(srv, jid, 0);
753 usr->user.alias = &alias->user;
755 if (match(end, "presence") && !srv->quiet) {
757 chan_notice(chan, "%s (%s) entered room.",
758 usr->user.name, alias->user.name);
760 chan_notice(chan, "%s entered room.",
766 if (srv->state == XMPP_IN_IQ)
767 if (match(end, "iq"))
768 srv->state = XMPP_READY;
769 if (srv->state == XMPP_IN_MESSAGE)
770 if (match(end, "message"))
771 srv->state = XMPP_READY;
772 if (srv->state == XMPP_IN_PRESENCE)
773 if (match(end, "presence"))
774 srv->state = XMPP_READY;
777 if (match(start, "stream:error"))
779 if (match(end, "stream:error"))
782 if (match(end, "text")) {
783 debug("xmpp: error: %s", data);
784 srv_notice(srv, "error: %s", data);
792 static char jid_usr[JID_LEN];
793 static char jid_srv[JID_LEN];
794 static char jid_res[JID_LEN];
796 for (server_t *cur = servers; cur; cur = cur->next) {
797 if (cur->protocol != XMPP)
800 xmpp_server_t *srv = (xmpp_server_t*)cur;
801 split_jid(srv->jid, jid_usr, jid_srv, jid_res);
804 error("jid is required");
806 srv->host = strcopy(jid_srv);
810 srv->muc = strcopy(srv->host);
812 srv->srv = strcopy(srv->host);
814 srv->user = strcopy(jid_usr);
816 srv->nick = strcopy(jid_usr);
820 srv->system.type = "sys";
821 srv->system.channel.server = &srv->server;
822 srv->system.channel.name = strcopy(srv->server.name);
824 strncpy(srv->myself.dest, srv->jid, JID_LEN);
825 srv->myself.user.server = &srv->server;
826 srv->myself.user.name = strcopy(srv->nick);
828 if (srv->connect && !srv->quiet)
829 srv_notice(srv, "XMPP Server: %s", srv->server.name);
831 srv->state = XMPP_CONNECT;
832 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
835 for (channel_t *cur = channels; cur; cur = cur->next) {
836 if (cur->server->protocol != XMPP)
839 xmpp_channel_t *chan = (xmpp_channel_t*)cur;
840 xmpp_server_t *srv = (xmpp_server_t*)cur->server;
844 chan->room = strcopy(cur->name);
845 snprintf(chan->dest, JID_LEN, "%s@%s",
846 chan->room, srv->muc);
850 void xmpp_config(server_t *server, channel_t *channel,
851 const char *group, const char *name,
852 const char *key, const char *value)
854 xmpp_server_t *srv = (xmpp_server_t*)server;
855 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
857 if (match(group, "server")) {
858 if (match(key, "protocol")) {
859 xmpp_server_t *srv = new0(xmpp_server_t);
860 srv->server.protocol = XMPP;
861 srv->server.name = strcopy(get_name(name));
862 add_server(&srv->server);
864 else if (match(key, "connect"))
865 srv->connect = get_bool(value);
866 else if (match(key, "timeout"))
867 srv->timeout = get_number(value);
868 else if (match(key, "noverify"))
869 srv->noverify = get_bool(value);
870 else if (match(key, "host"))
871 srv->host = get_string(value);
872 else if (match(key, "port"))
873 srv->port = get_number(value);
874 else if (match(key, "srv"))
875 srv->srv = get_string(value);
876 else if (match(key, "muc"))
877 srv->muc = get_string(value);
878 else if (match(key, "nick"))
879 srv->nick = get_string(value);
880 else if (match(key, "jid"))
881 srv->jid = get_string(value);
882 else if (match(key, "user"))
883 srv->user = get_string(value);
884 else if (match(key, "pass"))
885 srv->pass = get_string(value);
886 else if (match(key, "quiet"))
887 srv->quiet = get_bool(value);
889 if (match(group, "channel")) {
890 if (match(key, "server")) {
891 xmpp_channel_t *chan = new0(xmpp_channel_t);
892 chan->channel.server = &srv->server;
893 chan->channel.name = strcopy(get_name(name));
894 add_channel(&chan->channel);
896 else if (match(key, "room"))
897 chan->room = get_string(value);
898 else if (match(key, "join"))
899 chan->join = get_bool(value);
903 void xmpp_send(channel_t *channel, const char *text)
905 static char buf[4096];
907 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
908 xmpp_server_t *srv = (xmpp_server_t*)channel->server;
912 escape(buf, text, sizeof(buf));
915 /* Handle commands */
916 if (text[0] == '/') {
917 if (prefix(text, "/items", &arg)) {
919 "<iq id='items' type='get' from='%s' to='%s'>"
920 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
922 srv->bind, arg ?: srv->srv);
924 else if (prefix(text, "/info", &arg)) {
926 "<iq id='info' type='get' from='%s' to='%s'>"
927 "<query xmlns='http://jabber.org/protocol/disco#info'/>"
929 srv->bind, arg ?: srv->srv);
931 else if (prefix(text, "/names", &arg)) {
933 chan = find_channel(srv, arg, 1);
934 if (chan == &srv->system) {
935 chan_notice(chan, "Cannot get names from server");
939 "<iq id='names' type='get' from='%s' to='%s'>"
940 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
942 srv->bind, chan->dest);
944 else if (prefix(text, "/join", &arg)) {
946 chan_notice(chan, "usage: /join <channel>");
949 chan = find_channel(srv, arg, 1);
951 "<presence id='join' from='%s' to='%s/%s'>"
952 "<x xmlns='http://jabber.org/protocol/muc'/>"
954 srv->bind, chan->dest, srv->nick);
955 chan_notice(chan, "Room: %s", arg);
957 else if (prefix(text, "/config", &arg)) {
959 chan_notice(chan, "Unimplemented: /config <arg>");
962 if (chan == &srv->system) {
963 chan_notice(chan, "Cannot get config from server");
967 "<iq id='config' type='get' from='%s' to='%s'>"
968 "<query xmlns='http://jabber.org/protocol/muc#owner'/>"
970 srv->bind, chan->dest);
972 else if (prefix(text, "/query", &arg)) {
974 chan_notice(chan, "usage: /query <user>");
977 chan = find_channel(srv, arg, 0);
978 chan_notice(chan, "User: %s", arg);
980 else if (prefix(text, "/vcard", &arg)) {
982 chan = find_channel(srv, arg, 0);
985 "<iq id='vcard' type='get' from='%s' to='%s'>"
986 "<vCard xmlns='vcard-temp'/>"
988 srv->bind, chan->dest);
991 chan_notice(chan, "Unknown command %s", text);
994 debug("message: [%s]", text);
995 if (chan == &srv->system) {
996 chan_notice(chan, "Cannot send to server");
998 else if (chan->muc) {
1000 "<message id='chat%d' from='%s' to='%s' type='groupchat'>"
1003 srv->id++, srv->bind, chan->dest, text);
1005 net_print(&srv->net,
1006 "<message id='chat%d' from='%s' to='%s' type='chat'>"
1009 srv->id++, srv->bind, chan->dest, text);
1010 chat_recv(channel, &srv->myself.user, text);
1015 void xmpp_exit(void)