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/>.
30 #include <sys/timerfd.h>
85 xmpp_channel_t system;
111 xmpp_channel_t *msg_chan;
112 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);
285 "<iq id='auto-vcard' type='get' from='%s' to='%s'>"
286 "<vCard xmlns='vcard-temp'/>"
288 srv->jid, usr->dest);
293 static const char *find_attr(const char **attrs, const char *name)
295 for (int i = 0; attrs[i] && attrs[i+1]; i += 2)
296 if (match(attrs[i+0], name))
301 /* Callback functions */
302 static void xmpp_run(xmpp_server_t *srv, int idle,
303 const char *start, const char **attrs,
304 const char *end, const char *data);
306 static void on_start(void *_srv, const char *tag, const char **attrs)
308 xmpp_server_t *srv = _srv;
309 xmpp_run(srv, 0, tag, attrs, NULL, reset(&srv->buf));
312 static void on_data(void *_srv, const char *data, int len)
314 xmpp_server_t *srv = _srv;
315 append(&srv->buf, data, len);
318 static void on_end(void *_srv, const char *tag)
320 xmpp_server_t *srv = _srv;
321 xmpp_run(srv, 0, NULL, NULL, tag, reset(&srv->buf));
324 static void on_send(void *_srv)
326 xmpp_server_t *srv = _srv;
327 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
330 static void on_recv(void *_srv, char *buf, int len)
332 xmpp_server_t *srv = _srv;
334 XML_Parse(srv->expat, buf, len, 0);
335 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
338 static void on_err(void *_srv, int errno)
340 xmpp_server_t *srv = _srv;
341 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
344 static void on_timer(void *_srv)
347 xmpp_server_t *srv = _srv;
348 while (read(srv->timer, &buf, sizeof(buf)) > 0)
349 xmpp_run(srv, 1, NULL, NULL, NULL, NULL);
352 /* XMPP State machine */
353 static void xmpp_run(xmpp_server_t *srv, int idle,
354 const char *start, const char **attrs,
355 const char *end, const char *data)
359 debug("%*s \"%s\"", srv->level*4, "", data);
361 debug("%*s<%s>", srv->level*4, "", start);
362 for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
363 debug("%*s%s=\"%s\"%s",
365 attrs[i+0], attrs[i+1],
366 attrs[i+2] ? "" : ">");
374 /* Connection Handling */
375 if (srv->state == XMPP_CONNECT && !start && !end) {
376 srv->net.send = on_send;
377 srv->net.recv = on_recv;
378 srv->net.err = on_err;
380 net_open(&srv->net, srv->host, srv->port);
382 srv->timer = timerfd_create(CLOCK_MONOTONIC,
383 TFD_NONBLOCK|TFD_CLOEXEC);
385 error("creating timer fd");
386 struct timespec tspec = {srv->timeout, 0};
387 struct itimerspec itspec = {tspec, tspec};
388 timerfd_settime(srv->timer, 0, &itspec, NULL);
389 poll_add(&srv->poll, srv->timer, on_timer, srv);
390 poll_ctl(&srv->poll, 1, 0, 1);
392 if (!(srv->expat = XML_ParserCreate(NULL)))
393 error("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("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("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_SEND_STREAM) {
432 if (net_print(&srv->net,
433 "<?xml version='1.0'?>"
439 " xmlns='jabber:client'"
440 " xmlns:stream='http://etherx.jabber.org/streams'>",
441 srv->jid, srv->srv)) {
442 debug("xmpp: stream -> features");
443 srv->state = XMPP_RECV_FEATURES;
446 if (srv->state == XMPP_RECV_FEATURES) {
447 if (match(start, "starttls")) {
448 debug("xmpp: features -> starttls");
449 srv->state = XMPP_SEND_STARTTLS;
451 if (match(start, "mechanisms")) {
452 debug("xmpp: features -> auth");
453 srv->state = XMPP_SEND_AUTH;
455 if (match(start, "bind")) {
456 debug("xmpp: features -> bind");
457 srv->state = XMPP_SEND_BIND;
462 if (srv->state == XMPP_SEND_STARTTLS) {
463 if (net_print(&srv->net,
464 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
465 debug("xmpp: startls -> proceed");
466 srv->state = XMPP_RECV_PROCEED;
469 if (srv->state == XMPP_RECV_PROCEED) {
470 if (match(start, "proceed")) {
471 debug("xmpp: proceed -> encrypt");
472 srv->state = XMPP_ENCRYPT;
477 if (srv->state == XMPP_SEND_AUTH) {
478 static char plain[AUTH_LEN];
479 static char coded[AUTH_LEN];
481 len = snprintf(plain, AUTH_LEN, "%s%c%s%c%s",
482 srv->user, '\0', srv->user, '\0', srv->pass);
483 len = base64(plain, len, coded, AUTH_LEN);
484 if (net_print(&srv->net,
486 " xmlns='urn:ietf:params:xml:ns:xmpp-sasl'"
487 " mechanism='PLAIN'>%.*s</auth>",
489 debug("xmpp: auth -> success");
490 srv->state = XMPP_RECV_SUCCESS;
493 if (srv->state == XMPP_RECV_SUCCESS) {
494 if (match(start, "success")) {
495 debug("xmpp: success -> restart");
496 srv->state = XMPP_RESTART;
501 if (srv->state == XMPP_SEND_BIND) {
502 const char *resource = srv->jid;
503 while (*resource && *resource != '/')
505 while (*resource && *resource == '/')
507 if (net_print(&srv->net,
508 "<iq id='bind' type='set'>"
509 "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
510 "<resource>%s</resource>"
514 debug("xmpp: bind -> jid");
515 srv->state = XMPP_RECV_JID;
518 if (srv->state == XMPP_RECV_JID) {
519 if (match(start, "jid")) {
520 debug("xmpp: jid -> session");
521 srv->state = XMPP_SEND_SESSION;
524 if (srv->state == XMPP_SEND_SESSION) {
525 if (net_print(&srv->net,
526 "<iq id='session' type='set' to='%s'>"
527 "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
530 debug("xmpp: session -> presence");
531 srv->state = XMPP_SEND_PRESENCE;
534 if (srv->state == XMPP_SEND_PRESENCE) {
535 if (net_print(&srv->net, "<presence/>")) {
536 debug("xmpp: presence -> join");
537 srv->state = XMPP_SEND_JOIN;
540 if (srv->state == XMPP_SEND_JOIN) {
541 for (channel_t *cur = channels; cur; cur = cur->next) {
542 if (cur->server != &srv->server)
544 xmpp_channel_t *chan = (xmpp_channel_t *)cur;
547 chan_notice(chan, "XMPP Channel: %s", chan->channel.name);
549 "<presence id='join' from='%s' to='%s/%s'>"
550 "<x xmlns='http://jabber.org/protocol/muc'/>"
552 srv->jid, chan->dest, srv->nick);
554 debug("xmpp: join -> ready");
555 srv->state = XMPP_READY;
559 if (srv->state >= XMPP_READY && idle) {
561 net_print(&srv->net, " ");
564 if (srv->state == XMPP_READY && start) {
565 const char *id = find_attr(attrs, "id");
566 const char *from = find_attr(attrs, "from");
567 const char *type = find_attr(attrs, "type");
568 int is_muc = match(type, "groupchat");
570 /* Ignore presence errors (federated remote timeout, etc) */
571 if (match(type, "error"))
574 strncpy(srv->msg_id, id ?: "", ID_LEN);
576 srv->msg_chan = find_channel(srv, from, is_muc);
577 srv->msg_usr = find_user(srv, from, is_muc);
579 if (match(start, "iq"))
580 srv->state = XMPP_IN_IQ;
581 if (match(start, "message"))
582 srv->state = XMPP_IN_MESSAGE;
583 if (match(start, "presence"))
584 srv->state = XMPP_IN_PRESENCE;
586 if (srv->state != XMPP_READY)
587 debug("xmpp: ready -> in_%s -- "
588 "from=[%s] -> chan=[%s:%s] user=[%s]",
591 srv->msg_chan->channel.name,
592 srv->msg_usr ? srv->msg_usr->user.name : "(none)");
595 /* Shorthand Message Data */
596 xmpp_channel_t *chan = NULL;
597 xmpp_user_t *usr = NULL;
598 xmpp_user_t *alias = NULL;
600 if (srv->state > XMPP_READY) {
601 chan = srv->msg_chan;
604 alias = (xmpp_user_t*)usr->user.alias;
608 if (srv->state == XMPP_IN_IQ) {
609 if (match(start, "item")) {
610 if (chan == &srv->system) {
611 srv_notice(srv, "item: [%s] %s",
612 find_attr(attrs, "jid"),
613 find_attr(attrs, "name"));
614 } else if (chan->muc) {
615 const char *jid = find_attr(attrs, "jid");
616 xmpp_user_t *usr = find_user(srv, jid, 1);
617 chan_notice(chan, "User: %s (%s)",
618 usr->user.name, jid);
620 chan_notice(chan, "item: [%s] %s",
621 find_attr(attrs, "jid"),
622 find_attr(attrs, "name"));
625 if (match(start, "identity")) {
626 srv_notice(srv, "identity: %s",
627 find_attr(attrs, "name"));
629 if (match(start, "feature")) {
630 srv_notice(srv, "feature: %s",
631 find_attr(attrs, "var"));
633 if (match(start, "field")) {
634 debug("xmpp: %s -- type=[%s] label=[%s]", end,
635 find_attr(attrs, "type"),
636 find_attr(attrs, "label"));
637 if (!find_attr(attrs, "label"))
639 chan_notice(chan, "%-36s -- %s (%s)",
640 find_attr(attrs, "var"),
641 find_attr(attrs, "label"),
642 find_attr(attrs, "type"));
644 if (match(end, "title")) {
645 debug("xmpp: title -- chan=[%s]",
647 chan_notice(chan, "Title: %s", data);
649 if (match(end, "instructions")) {
650 debug("xmpp: instructions -- chan=[%s]",
652 chan_notice(chan, "%s", data);
657 if (srv->state == XMPP_IN_IQ) {
658 if (match(start, "vCard")) {
659 if (!match(srv->msg_id, "auto-vcard"))
660 chan_notice(chan, "Begin vCard (%s)",
661 usr ? usr->user.name :
663 srv->state = XMPP_IN_VCARD;
666 if (srv->state == XMPP_IN_VCARD) {
667 if (end && srv->level == 3) {
668 if (!match(srv->msg_id, "auto-vcard") &&
669 !match(end, "BINVAL"))
670 chan_notice(chan, " %s -> %s", end, data ?: "...");
673 if (match(end, "FN")) {
674 strset(&usr->user.name, data);
675 strset(&usr->full_name, data);
679 if (srv->state == XMPP_IN_VCARD) {
680 if (match(end, "vCard")) {
681 if (!match(srv->msg_id, "auto-vcard"))
682 chan_notice(chan, "End vCard");
683 srv->state = XMPP_IN_IQ;
688 if (srv->state == XMPP_IN_MESSAGE) {
689 if (match(start, "delay")) {
690 const char *ts = find_attr(attrs, "stamp");
693 strptime(ts, "%Y-%m-%dT%H:%M:%S", &tm);
694 srv->msg_stamp = timegm(&tm);
697 if (match(end, "subject")) {
698 strset(&chan->channel.topic, data);
700 if (match(end, "body") && data) {
701 strset(&srv->msg_body, data);
703 if (match(end, "message")) {
704 debug("xmpp: body (%s) -- chan=[%s] from=[%s]",
707 usr ? usr->user.name : "(none)");
709 chat_recv(&chan->channel,
712 message_t *msg = &messages[history-1];
713 msg->when = srv->msg_stamp ?: msg->when;
716 strset(&srv->msg_body, NULL);
721 if (srv->state == XMPP_IN_PRESENCE) {
722 if (match(start, "item") && usr) {
723 const char *jid = find_attr(attrs, "jid");
724 xmpp_user_t *alias = find_user(srv, jid, 0);
726 usr->user.alias = &alias->user;
728 if (match(end, "presence") && !srv->quiet) {
730 chan_notice(chan, "%s (%s) entered room.",
731 usr->user.name, alias->user.name);
733 chan_notice(chan, "%s entered room.",
739 if (srv->state == XMPP_IN_IQ)
740 if (match(end, "iq"))
741 srv->state = XMPP_READY;
742 if (srv->state == XMPP_IN_MESSAGE)
743 if (match(end, "message"))
744 srv->state = XMPP_READY;
745 if (srv->state == XMPP_IN_PRESENCE)
746 if (match(end, "presence"))
747 srv->state = XMPP_READY;
750 if (match(start, "stream:error"))
752 if (match(end, "stream:error"))
755 if (match(end, "text")) {
756 debug("xmpp: error: %s", data);
757 srv_notice(srv, "error: %s", data);
765 static char jid_usr[JID_LEN];
766 static char jid_srv[JID_LEN];
767 static char jid_res[JID_LEN];
769 for (server_t *cur = servers; cur; cur = cur->next) {
770 if (cur->protocol != XMPP)
773 xmpp_server_t *srv = (xmpp_server_t*)cur;
774 split_jid(srv->jid, jid_usr, jid_srv, jid_res);
777 error("jid is required");
779 srv->host = strcopy(jid_srv);
783 srv->muc = strcopy(srv->host);
785 srv->srv = strcopy(srv->host);
787 srv->user = strcopy(jid_usr);
789 srv->nick = strcopy(jid_usr);
793 srv->system.type = "sys";
794 srv->system.channel.server = &srv->server;
795 srv->system.channel.name = strcopy(srv->server.name);
797 strncpy(srv->myself.dest, srv->jid, JID_LEN);
798 srv->myself.user.server = &srv->server;
799 srv->myself.user.name = strcopy(srv->nick);
802 srv_notice(srv, "XMPP Server: %s", srv->server.name);
803 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
806 for (channel_t *cur = channels; cur; cur = cur->next) {
807 if (cur->server->protocol != XMPP)
810 xmpp_channel_t *chan = (xmpp_channel_t*)cur;
811 xmpp_server_t *srv = (xmpp_server_t*)cur->server;
815 chan->room = strcopy(cur->name);
816 snprintf(chan->dest, JID_LEN, "%s@%s",
817 chan->room, srv->muc);
821 void xmpp_config(server_t *server, channel_t *channel,
822 const char *group, const char *name,
823 const char *key, const char *value)
825 xmpp_server_t *srv = (xmpp_server_t*)server;
826 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
828 if (match(group, "server")) {
829 if (match(key, "protocol")) {
830 xmpp_server_t *srv = new0(xmpp_server_t);
831 srv->server.protocol = XMPP;
832 srv->server.name = strcopy(get_name(name));
833 add_server(&srv->server);
835 else if (match(key, "connect"))
836 srv->connect = get_bool(value);
837 else if (match(key, "timeout"))
838 srv->timeout = get_number(value);
839 else if (match(key, "noverify"))
840 srv->noverify = get_bool(value);
841 else if (match(key, "host"))
842 srv->host = get_string(value);
843 else if (match(key, "port"))
844 srv->port = get_number(value);
845 else if (match(key, "srv"))
846 srv->srv = get_string(value);
847 else if (match(key, "muc"))
848 srv->muc = get_string(value);
849 else if (match(key, "nick"))
850 srv->nick = get_string(value);
851 else if (match(key, "jid"))
852 srv->jid = get_string(value);
853 else if (match(key, "user"))
854 srv->user = get_string(value);
855 else if (match(key, "pass"))
856 srv->pass = get_string(value);
857 else if (match(key, "quiet"))
858 srv->quiet = get_bool(value);
860 if (match(group, "channel")) {
861 if (match(key, "server")) {
862 xmpp_channel_t *chan = new0(xmpp_channel_t);
863 chan->channel.server = &srv->server;
864 chan->channel.name = strcopy(get_name(name));
865 add_channel(&chan->channel);
867 else if (match(key, "room"))
868 chan->room = get_string(value);
869 else if (match(key, "join"))
870 chan->join = get_bool(value);
874 void xmpp_send(channel_t *channel, const char *text)
876 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
877 xmpp_server_t *srv = (xmpp_server_t*)channel->server;
880 /* Handle commands */
881 if (text[0] == '/') {
882 if (prefix(text, "/items", &arg)) {
884 "<iq id='items' type='get' from='%s' to='%s'>"
885 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
887 srv->jid, arg ?: srv->srv);
889 else if (prefix(text, "/info", &arg)) {
891 "<iq id='info' type='get' from='%s' to='%s'>"
892 "<query xmlns='http://jabber.org/protocol/disco#info'/>"
894 srv->jid, arg ?: srv->srv);
896 else if (prefix(text, "/names", &arg)) {
898 chan = find_channel(srv, arg, 1);
899 if (chan == &srv->system) {
900 chan_notice(chan, "Cannot get names from server");
904 "<iq id='list' type='get' from='%s' to='%s'>"
905 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
907 srv->jid, chan->dest);
909 else if (prefix(text, "/join", &arg)) {
911 chan_notice(chan, "usage: /join <channel>");
915 "<presence id='join' from='%s' to='%s/%s'>"
916 "<x xmlns='http://jabber.org/protocol/muc'/>"
918 srv->jid, chan->dest, srv->nick);
919 chan = find_channel(srv, arg, 1);
920 chan_notice(chan, "Room: %s", arg);
922 else if (prefix(text, "/config", &arg)) {
924 chan_notice(chan, "Unimplemented: /config <arg>");
928 "<iq id='config' type='get' from='%s' to='%s'>"
929 "<query xmlns='http://jabber.org/protocol/muc#owner'/>"
931 srv->jid, chan->dest);
933 else if (prefix(text, "/query", &arg)) {
935 chan_notice(chan, "usage: /query <user>");
938 chan = find_channel(srv, arg, 0);
939 chan_notice(chan, "User: %s", arg);
941 else if (prefix(text, "/vcard", &arg)) {
943 chan = find_channel(srv, arg, 0);
946 "<iq id='vcard' type='get' from='%s' to='%s'>"
947 "<vCard xmlns='vcard-temp'/>"
949 srv->jid, chan->dest);
952 chan_notice(chan, "Unknown command %s", text);
955 debug("message: [%s]", text);
956 if (chan == &srv->system) {
957 chan_notice(chan, "Cannot send to server");
959 else if (!chan->dest) {
960 chan_notice(chan, "No destination for message");
962 else if (chan->muc) {
964 "<message id='chat%d' from='%s' to='%s' type='groupchat'>"
967 srv->id++, srv->jid, chan->dest, text);
970 "<message id='chat%d' from='%s' to='%s' type='chat'>"
973 srv->id++, srv->jid, chan->dest, text);
974 chat_recv(channel, &srv->myself.user, text);