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/>.
90 xmpp_channel_t system;
117 xmpp_channel_t *msg_chan;
118 xmpp_user_t *msg_usr;
127 /* Helper functions */
128 static void srv_notice(xmpp_server_t *srv, const char *fmt, ...)
130 static char buf[1024];
134 vsnprintf(buf, sizeof(buf), fmt, ap);
137 debug("xmpp: srv_notice: [%s]", buf);
138 chat_recv(&srv->system.channel, NULL, buf);
141 static void chan_notice(xmpp_channel_t *chan, const char *fmt, ...)
143 static char buf[1024];
147 vsnprintf(buf, sizeof(buf), fmt, ap);
150 debug("xmpp: chan_notice -- %s [%s]",
151 chan->channel.name, buf);
152 chat_recv(&chan->channel, NULL, buf);
155 static void split_jid(const char *jid, char *usr, char *srv, char *res)
160 if (usr) usr[0] = '\0';
161 if (srv) srv[0] = '\0';
162 if (res) res[0] = '\0';
164 while (jid && *jid == ' ')
166 for (int i = 0; jid && jid[i]; i++) {
177 if (ptr && (pos+1) < JID_LEN) {
182 while (ptr && pos >= 1 && ptr[pos-1] == ' ')
185 //debug("JID: '%s' usr=[%s] srv=[%s] res=[%s]",
186 // jid, usr, srv, res);
189 static xmpp_channel_t *find_channel(xmpp_server_t *srv,
190 const char *jid, int is_muc)
192 static char jid_usr[JID_LEN];
193 static char jid_srv[JID_LEN];
194 static char dest[JID_LEN];
195 xmpp_channel_t *chan;
197 /* Server channels */
198 if (!jid || match(jid, srv->srv))
201 /* Parse JID and check for MUC */
202 split_jid(jid, jid_usr, jid_srv, NULL);
203 if (match(jid_srv, srv->muc))
206 /* Find resource-less JID */
207 snprintf(dest, JID_LEN, "%s@%s", jid_usr,
208 jid_srv[0] ? jid_srv :
209 is_muc ? srv->muc : srv->srv);
211 /* Find existing channels */
212 for (channel_t *cur = channels; cur; cur = cur->next) {
213 if (cur->server != &srv->server)
215 chan = (xmpp_channel_t *)cur;
216 if (match(chan->dest, dest))
220 /* Create a new channel */
221 chan = new0(xmpp_channel_t);
223 chan->type = is_muc ? "muc" : "usr";
224 chan->channel.server = &srv->server;
225 chan->channel.name = strcopy(jid_usr);
226 strncpy(chan->dest, dest, JID_LEN);
227 add_channel(&chan->channel);
231 static xmpp_user_t *find_user(xmpp_server_t *srv,
232 const char *jid, int is_muc)
234 static char jid_usr[JID_LEN];
235 static char jid_srv[JID_LEN];
236 static char jid_res[JID_LEN];
237 static char dest[JID_LEN];
240 /* Server channels */
241 if (!jid || match(jid, srv->srv))
244 /* Parse JID and check for MUC */
245 split_jid(jid, jid_usr, jid_srv, jid_res);
246 if (match(jid_srv, srv->muc))
249 /* Channel notices have no resource */
250 if (is_muc && !jid_res[0])
253 /* Ignore resources for real users */
255 snprintf(dest, JID_LEN, "%s@%s/%s",
257 jid_srv[0] ? jid_srv : srv->muc,
260 snprintf(dest, JID_LEN, "%s@%s",
262 jid_srv[0] ? jid_srv : srv->srv);
264 /* Find existing users */
265 for (user_t *cur = users; cur; cur = cur->next) {
266 if (cur->server != &srv->server)
268 usr = (xmpp_user_t *)cur;
269 if (match(usr->dest, dest)) {
270 debug("xmpp: found user: \"%s\" -> "
271 "name=[%s] dest=[%s] alias=[%s]",
272 jid, usr->user.name, usr->dest,
273 usr->user.alias ? usr->user.alias->name : "(none)");
278 /* Create a new user */
279 usr = new0(xmpp_user_t);
280 usr->user.server = &srv->server;
282 usr->user.name = strcopy(jid_res);
283 usr->full_name = strcopy(jid_res);
285 usr->user.name = strcopy(jid_usr);
288 strncpy(usr->dest, dest, JID_LEN);
289 add_user(&usr->user);
291 /* Send vcard probe */
292 debug("xmpp: added user: \"%s\"%s -> name=[%s] dest=[%s]",
293 jid, is_muc ? " (muc)" : "",
294 usr->user.name, usr->dest);
299 static int match_user(xmpp_user_t *usr, const char *ptrn)
303 const char *user = usr->dest;
304 const char *full = usr->full_name;
305 const char *nick = usr->mention_name;
307 /* Match all users */
308 if (!ptrn || !ptrn[0])
311 /* Match user id and mention name */
312 if (user && strcasestr(user, ptrn) == user)
314 if (nick && strcasestr(nick, ptrn) == nick)
317 /* Full name matching */
318 if (!full || !full[0])
321 /* Match first name */
322 if (strcasestr(full, ptrn) == full)
325 /* Match last name */
326 snprintf(tmp, sizeof(tmp), " %s", ptrn);
327 if (strcasestr(full, tmp))
329 snprintf(tmp, sizeof(tmp), "-%s", ptrn);
330 if (strcasestr(full, tmp))
333 /* Match first initial last name */
334 if (tolower(full[0]) == tolower(ptrn[0])) {
335 snprintf(tmp, sizeof(tmp), " %s", &ptrn[1]);
336 if (strcasestr(full, tmp))
338 snprintf(tmp, sizeof(tmp), "-%s", &ptrn[1]);
339 if (strcasestr(full, tmp))
346 static int match_channel(xmpp_channel_t *chan, const char *ptrn)
348 const char *dest = chan->dest;
349 const char *room = chan->room;
351 /* Match all users */
352 if (!ptrn || !ptrn[0])
355 /* Match dest and channel name */
356 if (dest && strcasestr(dest, ptrn))
358 if (room && strcasestr(room, ptrn))
364 static void complete_xmpp_user(xmpp_server_t *srv, const char *prefix, int mention)
366 for (user_t *cur = users; cur; cur = cur->next) {
367 if (cur->server != &srv->server)
369 xmpp_user_t *usr = (xmpp_user_t *)cur;
372 if (match_user(usr, prefix)) {
374 complete_item(prefix, usr->dest, usr->full_name);
375 else if (usr->mention_name)
376 complete_item(prefix, usr->mention_name, usr->full_name);
378 complete_item(prefix, usr->full_name, "");
383 static void complete_xmpp_channel(xmpp_server_t *srv, const char *prefix)
385 for (channel_t *cur = channels; cur; cur = cur->next) {
386 if (cur->server != &srv->server)
388 xmpp_channel_t *chan = (xmpp_channel_t *)cur;
391 if (match_channel(chan, prefix))
392 complete_item(prefix, chan->dest, chan->name ?: chan->room);
396 static const char *find_attr(const char **attrs, const char *name)
398 for (int i = 0; attrs[i] && attrs[i+1]; i += 2)
399 if (match(attrs[i+0], name))
404 static void lookup_user(xmpp_server_t *srv, xmpp_user_t *usr)
409 "<iq id='auto-vcard' type='get' from='%s' to='%s'>"
410 "<vCard xmlns='vcard-temp'/>"
412 srv->bind, usr->dest);
415 /* Callback functions */
416 static void xmpp_run(xmpp_server_t *srv, int idle,
417 const char *start, const char **attrs,
418 const char *end, const char *data);
420 static void on_start(void *_srv, const char *tag, const char **attrs)
422 xmpp_server_t *srv = _srv;
423 xmpp_run(srv, 0, tag, attrs, NULL, reset(&srv->buf));
426 static void on_data(void *_srv, const char *data, int len)
428 xmpp_server_t *srv = _srv;
429 append(&srv->buf, data, len);
432 static void on_end(void *_srv, const char *tag)
434 xmpp_server_t *srv = _srv;
435 xmpp_run(srv, 0, NULL, NULL, tag, reset(&srv->buf));
438 static void on_send(void *_srv)
440 xmpp_server_t *srv = _srv;
441 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
444 static void on_recv(void *_srv, char *buf, int len)
446 xmpp_server_t *srv = _srv;
448 XML_Parse(srv->expat, buf, len, 0);
449 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
452 static void on_err(void *_srv, int err)
454 xmpp_server_t *srv = _srv;
455 srv_notice(srv, "Server disconnected");
456 srv->state = XMPP_DEAD;
459 static void on_timer(void *_srv)
461 xmpp_server_t *srv = _srv;
462 xmpp_run(srv, 1, NULL, NULL, NULL, NULL);
465 /* XMPP State machine */
466 static void xmpp_run(xmpp_server_t *srv, int idle,
467 const char *start, const char **attrs,
468 const char *end, const char *data)
472 debug("%*s \"%s\"", srv->level*4, "", data);
474 debug("%*s<%s>", srv->level*4, "", start);
475 for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
476 debug("%*s%s=\"%s\"%s",
478 attrs[i+0], attrs[i+1],
479 attrs[i+2] ? "" : ">");
487 /* Connection Handling */
488 if (srv->state == XMPP_CONNECT && !start && !end) {
489 srv->net.send = on_send;
490 srv->net.recv = on_recv;
491 srv->net.err = on_err;
494 srv->idle.timer = on_timer;
495 srv->idle.data = srv;
497 net_open(&srv->net, srv->host, srv->port);
498 idle_add(&srv->idle);
499 idle_set(&srv->idle, srv->timeout, srv->timeout);
501 if (!(srv->expat = XML_ParserCreate(NULL)))
502 error("creating XML parser");
503 XML_SetUserData(srv->expat, srv);
504 XML_SetStartElementHandler(srv->expat, on_start);
505 XML_SetEndElementHandler(srv->expat, on_end);
506 XML_SetCharacterDataHandler(srv->expat, on_data);
508 debug("xmpp: connect -> stream");
510 srv->state = XMPP_SEND_STREAM;
512 if (srv->state == XMPP_ENCRYPT && !start && !end) {
513 net_encrypt(&srv->net, srv->noverify ? NET_NOVERIFY : 0);
515 if (!(XML_ParserReset(srv->expat, NULL)))
516 error("resetting XML parser");
517 XML_SetUserData(srv->expat, srv);
518 XML_SetStartElementHandler(srv->expat, on_start);
519 XML_SetEndElementHandler(srv->expat, on_end);
520 XML_SetCharacterDataHandler(srv->expat, on_data);
522 debug("xmpp: encrypt -> stream");
524 srv->state = XMPP_SEND_STREAM;
526 if (srv->state == XMPP_RESTART && !start && !end) {
527 if (!(XML_ParserReset(srv->expat, NULL)))
528 error("resetting XML parser");
529 XML_SetUserData(srv->expat, srv);
530 XML_SetStartElementHandler(srv->expat, on_start);
531 XML_SetEndElementHandler(srv->expat, on_end);
532 XML_SetCharacterDataHandler(srv->expat, on_data);
534 debug("xmpp: restart -> stream");
536 srv->state = XMPP_SEND_STREAM;
540 if (srv->state > XMPP_CONNECT && idle) {
542 net_print(&srv->net, " ");
546 if (srv->state == XMPP_SEND_STREAM) {
547 if (net_print(&srv->net,
548 "<?xml version='1.0'?>"
554 " xmlns='jabber:client'"
555 " xmlns:stream='http://etherx.jabber.org/streams'>",
556 srv->jid, srv->srv)) {
557 debug("xmpp: stream -> features");
558 srv->state = XMPP_RECV_FEATURES;
561 if (srv->state == XMPP_RECV_FEATURES) {
562 if (match(start, "starttls")) {
563 debug("xmpp: features -> starttls");
564 srv->state = XMPP_SEND_STARTTLS;
566 if (match(start, "mechanisms")) {
567 debug("xmpp: features -> auth");
568 srv->state = XMPP_SEND_AUTH;
570 if (match(start, "bind")) {
571 debug("xmpp: features -> bind");
572 srv->state = XMPP_SEND_BIND;
577 if (srv->state == XMPP_SEND_STARTTLS) {
578 if (net_print(&srv->net,
579 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
580 debug("xmpp: startls -> proceed");
581 srv->state = XMPP_RECV_PROCEED;
584 if (srv->state == XMPP_RECV_PROCEED) {
585 if (match(start, "proceed")) {
586 debug("xmpp: proceed -> encrypt");
587 srv->state = XMPP_ENCRYPT;
592 if (srv->state == XMPP_SEND_AUTH) {
593 static char plain[AUTH_LEN];
594 static char coded[AUTH_LEN];
596 len = snprintf(plain, AUTH_LEN, "%s%c%s%c%s",
597 srv->user, '\0', srv->user, '\0', srv->pass);
598 len = base64(plain, len, coded, AUTH_LEN);
599 if (net_print(&srv->net,
601 " xmlns='urn:ietf:params:xml:ns:xmpp-sasl'"
602 " mechanism='PLAIN'>%.*s</auth>",
604 debug("xmpp: auth -> success");
605 srv->state = XMPP_RECV_SUCCESS;
608 if (srv->state == XMPP_RECV_SUCCESS) {
609 if (match(start, "failure")) {
610 debug("xmpp: success -> dead");
611 srv_notice(srv, "Authentication failure");
612 srv->state = XMPP_DEAD;
614 if (match(start, "success")) {
615 debug("xmpp: success -> restart");
616 srv_notice(srv, "Authentication success");
617 srv->state = XMPP_RESTART;
622 if (srv->state == XMPP_SEND_BIND) {
623 const char *resource = srv->jid;
624 while (*resource && *resource != '/')
626 while (*resource && *resource == '/')
628 if (net_print(&srv->net,
629 "<iq id='bind' type='set'>"
630 "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
631 "<resource>%s</resource>"
635 debug("xmpp: bind -> jid");
636 srv->state = XMPP_RECV_JID;
639 if (srv->state == XMPP_RECV_JID) {
640 if (match(end, "jid")) {
641 debug("xmpp: jid -> session");
642 strset(&srv->bind, data ?: srv->jid);
643 srv->state = XMPP_SEND_SESSION;
646 if (srv->state == XMPP_SEND_SESSION) {
647 if (net_print(&srv->net,
648 "<iq id='session' type='set' to='%s'>"
649 "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
652 debug("xmpp: session -> presence");
653 srv->state = XMPP_SEND_PRESENCE;
656 if (srv->state == XMPP_SEND_PRESENCE) {
657 if (net_print(&srv->net, "<presence/>")) {
658 debug("xmpp: presence -> roster");
659 srv->state = XMPP_SEND_ROSTER;
662 if (srv->state == XMPP_SEND_ROSTER) {
663 if (net_print(&srv->net,
664 "<iq id='roster' type='get'>"
665 "<query xmlns='jabber:iq:roster'/>"
667 debug("xmpp: roster -> rooms");
668 srv->state = XMPP_SEND_ROOMS;
671 if (srv->state == XMPP_SEND_ROOMS) {
672 if (net_print(&srv->net,
673 "<iq id='rooms' type='get' to='%s'>"
674 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
677 debug("xmpp: rooms -> join");
678 srv->state = XMPP_SEND_JOIN;
681 if (srv->state == XMPP_SEND_JOIN) {
682 for (channel_t *cur = channels; cur; cur = cur->next) {
683 if (cur->server != &srv->server)
685 xmpp_channel_t *chan = (xmpp_channel_t *)cur;
689 chan_notice(chan, "XMPP Channel: %s", chan->channel.name);
691 "<presence id='join' from='%s' to='%s/%s'>"
692 "<x xmlns='http://jabber.org/protocol/muc'/>"
694 srv->bind, chan->dest, srv->nick);
696 debug("xmpp: join -> ready");
697 srv->state = XMPP_READY;
701 if (srv->state == XMPP_READY && start) {
702 const char *id = find_attr(attrs, "id");
703 const char *from = find_attr(attrs, "from");
704 const char *type = find_attr(attrs, "type");
705 int is_muc = match(type, "groupchat");
707 /* Ignore presence errors (federated remote timeout, etc) */
708 if (match(type, "error"))
711 strncpy(srv->msg_id, id ?: "", ID_LEN);
713 srv->msg_chan = find_channel(srv, from, is_muc);
714 srv->msg_usr = find_user(srv, from, is_muc);
716 if (match(start, "iq"))
717 srv->state = XMPP_IN_IQ;
718 if (match(start, "message"))
719 srv->state = XMPP_IN_MESSAGE;
720 if (match(start, "presence"))
721 srv->state = XMPP_IN_PRESENCE;
723 if (srv->state != XMPP_READY)
724 debug("xmpp: ready -> in_%s -- "
725 "from=[%s] -> chan=[%s:%s] user=[%s]",
728 srv->msg_chan->channel.name,
729 srv->msg_usr ? srv->msg_usr->user.name : "(none)");
732 /* Shorthand Message Data */
733 xmpp_channel_t *chan = NULL;
734 xmpp_user_t *usr = NULL;
735 xmpp_user_t *alias = NULL;
737 if (srv->state > XMPP_READY) {
738 chan = srv->msg_chan;
741 alias = (xmpp_user_t*)usr->user.alias;
745 if (srv->state == XMPP_IN_IQ) {
746 if (match(start, "item")) {
747 if (chan == &srv->system) {
748 srv_notice(srv, "item: [%s] %s",
749 find_attr(attrs, "jid"),
750 find_attr(attrs, "name"));
751 } else if (chan->muc) {
752 const char *jid = find_attr(attrs, "jid");
753 xmpp_user_t *usr = find_user(srv, jid, 1);
755 chan_notice(chan, "User: %s (%s)",
756 usr->user.name, jid);
758 chan_notice(chan, "item: [%s] %s",
759 find_attr(attrs, "jid"),
760 find_attr(attrs, "name"));
763 if (match(start, "identity")) {
764 srv_notice(srv, "identity: %s",
765 find_attr(attrs, "name"));
767 if (match(start, "feature")) {
768 srv_notice(srv, "feature: %s",
769 find_attr(attrs, "var"));
771 if (match(start, "field")) {
772 debug("xmpp: %s -- type=[%s] label=[%s]", end,
773 find_attr(attrs, "type"),
774 find_attr(attrs, "label"));
775 if (!find_attr(attrs, "label"))
777 chan_notice(chan, "%-36s -- %s (%s)",
778 find_attr(attrs, "var"),
779 find_attr(attrs, "label"),
780 find_attr(attrs, "type"));
782 if (match(end, "title")) {
783 debug("xmpp: title -- chan=[%s]",
785 chan_notice(chan, "Title: %s", data);
787 if (match(end, "instructions")) {
788 debug("xmpp: instructions -- chan=[%s]",
790 chan_notice(chan, "%s", data);
795 if (srv->state == XMPP_IN_IQ) {
796 if (match(start, "vCard")) {
797 if (!match(srv->msg_id, "auto-vcard"))
798 chan_notice(chan, "Begin vCard (%s)",
799 usr ? usr->user.name :
801 srv->state = XMPP_IN_VCARD;
804 if (srv->state == XMPP_IN_VCARD) {
805 if (end && srv->level == 3) {
806 if (!match(srv->msg_id, "auto-vcard") &&
807 !match(end, "BINVAL"))
808 chan_notice(chan, " %s -> %s", end, data ?: "...");
811 if (match(end, "FN")) {
812 strset(&chan->channel.name, data);
813 strset(&usr->user.name, data);
814 strset(&usr->full_name, data);
819 if (srv->state == XMPP_IN_VCARD) {
820 if (match(end, "vCard")) {
821 if (!match(srv->msg_id, "auto-vcard"))
822 chan_notice(chan, "End vCard");
823 srv->state = XMPP_IN_IQ;
828 if (srv->state == XMPP_IN_IQ) {
829 if (match(srv->msg_id, "roster"))
830 srv->state = XMPP_IN_ROSTER;
832 if (srv->state == XMPP_IN_ROSTER) {
833 if (match(start, "item")) {
834 // Todo: cleanup name setting
835 const char *jid = find_attr(attrs, "jid");
836 const char *fname = find_attr(attrs, "name");
837 const char *mname = find_attr(attrs, "mention_name");
838 xmpp_user_t *usr = find_user(srv, jid, 0);
839 xmpp_channel_t *chan = find_channel(srv, jid, 0);
841 strset(&usr->full_name, fname);
842 strset(&usr->user.name, fname);
843 strset(&usr->mention_name, mname);
846 strset(&chan->channel.name, fname);
850 if (srv->state == XMPP_IN_ROSTER) {
851 if (match(end, "iq"))
852 srv->state = XMPP_IN_IQ;
856 if (srv->state == XMPP_IN_IQ) {
857 if (match(srv->msg_id, "rooms"))
858 srv->state = XMPP_IN_ROOMS;
860 if (srv->state == XMPP_IN_ROOMS) {
861 if (match(start, "item")) {
862 const char *jid = find_attr(attrs, "jid");
863 xmpp_channel_t *chan = find_channel(srv, jid, 1);
866 find_attr(attrs, "name"));
868 find_attr(attrs, "name"));
872 if (srv->state == XMPP_IN_ROOMS) {
873 if (match(end, "iq"))
874 srv->state = XMPP_IN_IQ;
878 if (srv->state == XMPP_IN_MESSAGE) {
879 if (match(start, "delay")) {
880 const char *ts = find_attr(attrs, "stamp");
883 strptime(ts, "%Y-%m-%dT%H:%M:%S", &tm);
884 srv->msg_stamp = timegm(&tm);
887 if (match(end, "subject")) {
888 strset(&chan->channel.topic, data);
890 chan_notice(chan, "Topic: %s", data);
892 if (match(end, "body") && data) {
893 strset(&srv->msg_body, data);
895 if (match(start, "html")) {
898 if (srv->in_html && data) {
899 append(&srv->html, data, strlen(data));
901 if (match(end, "html")) {
902 strset(&srv->msg_html, reset(&srv->html));
905 if (match(end, "message")) {
906 debug("xmpp: body (%s) -- chan=[%s] from=[%s]",
909 usr ? usr->user.name : "(none)");
910 char *content = srv->msg_body;
912 content = despace(srv->msg_html);
913 if (usr && !usr->muc)
914 lookup_user(srv, usr);
916 chat_recv(&chan->channel, &usr->user, content);
917 message_t *msg = &messages[history-1];
918 msg->when = srv->msg_stamp ?: msg->when;
921 strset(&srv->msg_body, NULL);
922 strset(&srv->msg_html, NULL);
928 if (srv->state == XMPP_IN_PRESENCE) {
929 if (match(start, "item") && usr) {
930 const char *jid = find_attr(attrs, "jid");
931 xmpp_user_t *alias = find_user(srv, jid, 0);
933 usr->user.alias = &alias->user;
934 if (alias && !alias->full_name && usr->full_name) {
935 strset(&alias->user.name, usr->full_name);
936 strset(&alias->full_name, usr->full_name);
938 xmpp_channel_t *chan = find_channel(srv, jid, 0);
939 if (alias && alias->full_name && chan) {
940 strset(&chan->channel.name, alias->full_name);
943 if (match(end, "presence") && !srv->quiet) {
945 chan_notice(chan, "%s (%s) entered room.",
946 usr->user.name, alias->user.name);
948 chan_notice(chan, "%s entered room.",
954 if (srv->state == XMPP_IN_IQ)
955 if (match(end, "iq"))
956 srv->state = XMPP_READY;
957 if (srv->state == XMPP_IN_MESSAGE)
958 if (match(end, "message"))
959 srv->state = XMPP_READY;
960 if (srv->state == XMPP_IN_PRESENCE)
961 if (match(end, "presence"))
962 srv->state = XMPP_READY;
965 if (match(start, "stream:error"))
967 if (match(end, "stream:error"))
970 if (match(end, "text")) {
971 debug("xmpp: error: %s", data);
972 srv_notice(srv, "error: %s", data);
980 static char jid_usr[JID_LEN];
981 static char jid_srv[JID_LEN];
982 static char jid_res[JID_LEN];
984 for (server_t *cur = servers; cur; cur = cur->next) {
985 if (cur->protocol != XMPP)
988 xmpp_server_t *srv = (xmpp_server_t*)cur;
989 split_jid(srv->jid, jid_usr, jid_srv, jid_res);
992 error("jid is required");
994 srv->host = strcopy(jid_srv);
998 srv->muc = strcopy(srv->host);
1000 srv->srv = strcopy(srv->host);
1002 srv->user = strcopy(jid_usr);
1004 srv->nick = strcopy(jid_usr);
1008 srv->system.type = "sys";
1009 srv->system.channel.server = &srv->server;
1010 srv->system.channel.name = strcopy(srv->server.name);
1012 strncpy(srv->myself.dest, srv->jid, JID_LEN);
1013 srv->myself.user.server = &srv->server;
1014 srv->myself.user.name = strcopy(srv->nick);
1016 if (srv->connect && !srv->quiet)
1017 srv_notice(srv, "XMPP Server: %s", srv->server.name);
1019 srv->state = XMPP_CONNECT;
1020 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
1023 for (channel_t *cur = channels; cur; cur = cur->next) {
1024 if (cur->server->protocol != XMPP)
1027 xmpp_channel_t *chan = (xmpp_channel_t*)cur;
1028 xmpp_server_t *srv = (xmpp_server_t*)cur->server;
1032 chan->room = strcopy(cur->name);
1033 snprintf(chan->dest, JID_LEN, "%s@%s",
1034 chan->room, srv->muc);
1038 void xmpp_config(server_t *server, channel_t *channel,
1039 const char *group, const char *name,
1040 const char *key, const char *value)
1042 xmpp_server_t *srv = (xmpp_server_t*)server;
1043 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
1045 if (match(group, "server")) {
1046 if (match(key, "protocol")) {
1047 xmpp_server_t *srv = new0(xmpp_server_t);
1048 srv->server.protocol = XMPP;
1049 srv->server.name = strcopy(get_name(name));
1050 add_server(&srv->server);
1052 else if (match(key, "connect"))
1053 srv->connect = get_bool(value);
1054 else if (match(key, "timeout"))
1055 srv->timeout = get_number(value);
1056 else if (match(key, "noverify"))
1057 srv->noverify = get_bool(value);
1058 else if (match(key, "host"))
1059 srv->host = get_string(value);
1060 else if (match(key, "port"))
1061 srv->port = get_number(value);
1062 else if (match(key, "srv"))
1063 srv->srv = get_string(value);
1064 else if (match(key, "muc"))
1065 srv->muc = get_string(value);
1066 else if (match(key, "nick"))
1067 srv->nick = get_string(value);
1068 else if (match(key, "jid"))
1069 srv->jid = get_string(value);
1070 else if (match(key, "user"))
1071 srv->user = get_string(value);
1072 else if (match(key, "pass"))
1073 srv->pass = get_string(value);
1074 else if (match(key, "quiet"))
1075 srv->quiet = get_bool(value);
1077 if (match(group, "channel")) {
1078 if (match(key, "server")) {
1079 xmpp_channel_t *chan = new0(xmpp_channel_t);
1080 chan->channel.server = &srv->server;
1081 chan->channel.name = strcopy(get_name(name));
1082 add_channel(&chan->channel);
1084 else if (match(key, "room"))
1085 chan->room = get_string(value);
1086 else if (match(key, "join"))
1087 chan->join = get_bool(value);
1089 if (match(group, "autojoin")) {
1090 xmpp_channel_t *chan = new0(xmpp_channel_t);
1091 chan->channel.server = &srv->server;
1092 chan->channel.name = strcopy(key);
1093 chan->room = get_string(value);
1095 add_channel(&chan->channel);
1099 void xmpp_complete(channel_t *channel, const char *text)
1101 xmpp_server_t *srv = (xmpp_server_t*)channel->server;
1104 if (suffix(text, "@", &arg)) {
1105 complete_xmpp_user(srv, arg, 1);
1107 else if (prefix(text, "/join ", &arg)) {
1108 complete_xmpp_channel(srv, arg);
1110 else if (prefix(text, "/query ", &arg)) {
1111 complete_xmpp_user(srv, arg, 0);
1113 else if (prefix(text, "/vcard ", &arg)) {
1114 complete_xmpp_user(srv, arg, 0);
1118 "/items ", "Query XMPP server 'items'",
1119 "/info ", "Query XMPP server 'info'",
1120 "/names ", "Query XMPP channel users",
1121 "/join", "Join XMPP (muc) room",
1122 "/config ", "Configure XMPP (muc) room",
1123 "/query ", "Open XMPP user chat",
1124 "/vcard ", "Send XMPP vCard query",
1129 void xmpp_send(channel_t *channel, const char *text)
1131 static char buf[4096];
1133 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
1134 xmpp_server_t *srv = (xmpp_server_t*)channel->server;
1138 const char *raw = text;
1139 escape(buf, text, sizeof(buf));
1142 /* Handle commands */
1143 if (text[0] == '/' && text[1] != '/') {
1144 if (prefix(text, "/items", &arg)) {
1145 net_print(&srv->net,
1146 "<iq id='items' type='get' from='%s' to='%s'>"
1147 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
1149 srv->bind, arg ?: srv->srv);
1151 else if (prefix(text, "/info", &arg)) {
1152 net_print(&srv->net,
1153 "<iq id='info' type='get' from='%s' to='%s'>"
1154 "<query xmlns='http://jabber.org/protocol/disco#info'/>"
1156 srv->bind, arg ?: srv->srv);
1158 else if (prefix(text, "/names", &arg)) {
1160 chan = find_channel(srv, arg, 1);
1161 if (chan == &srv->system) {
1162 chan_notice(chan, "Cannot get names from server");
1165 net_print(&srv->net,
1166 "<iq id='names' type='get' from='%s' to='%s'>"
1167 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
1169 srv->bind, chan->dest);
1171 else if (prefix(text, "/join", &arg)) {
1173 chan_notice(chan, "usage: /join <channel>");
1176 chan = find_channel(srv, arg, 1);
1177 net_print(&srv->net,
1178 "<presence id='join' from='%s' to='%s/%s'>"
1179 "<x xmlns='http://jabber.org/protocol/muc'/>"
1181 srv->bind, chan->dest, srv->nick);
1182 chan_notice(chan, "Room: %s", arg);
1184 else if (prefix(text, "/config", &arg)) {
1186 chan_notice(chan, "Unimplemented: /config <arg>");
1189 if (chan == &srv->system) {
1190 chan_notice(chan, "Cannot get config from server");
1193 net_print(&srv->net,
1194 "<iq id='config' type='get' from='%s' to='%s'>"
1195 "<query xmlns='http://jabber.org/protocol/muc#owner'/>"
1197 srv->bind, chan->dest);
1199 else if (prefix(text, "/query", &arg)) {
1201 chan_notice(chan, "usage: /query <user>");
1204 chan = find_channel(srv, arg, 0);
1205 chan_notice(chan, "User: %s", arg);
1207 else if (prefix(text, "/vcard", &arg)) {
1209 chan = find_channel(srv, arg, 0);
1211 net_print(&srv->net,
1212 "<iq id='vcard' type='get' from='%s' to='%s'>"
1213 "<vCard xmlns='vcard-temp'/>"
1215 srv->bind, chan->dest);
1218 chan_notice(chan, "Unknown command %s", text);
1221 debug("message: [%s]", text);
1224 if (chan == &srv->system) {
1225 chan_notice(chan, "Cannot send to server");
1227 else if (chan->muc) {
1228 net_print(&srv->net,
1229 "<message id='chat%d' from='%s' to='%s' type='groupchat'>"
1232 srv->id++, srv->bind, chan->dest, text);
1234 net_print(&srv->net,
1235 "<message id='chat%d' from='%s' to='%s' type='chat'>"
1238 srv->id++, srv->bind, chan->dest, text);
1239 chat_recv(channel, &srv->myself.user, raw);
1244 void xmpp_exit(void)