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>
74 xmpp_channel_t system;
103 char msg_jid[JID_LEN];
104 char msg_usr[JID_LEN];
105 char msg_srv[JID_LEN];
106 char msg_res[JID_LEN];
108 xmpp_channel_t *msg_chan;
111 /* Helper functions */
112 static void srv_notice(xmpp_server_t *srv, const char *fmt, ...)
114 static char buf[1024];
118 vsnprintf(buf, sizeof(buf), fmt, ap);
121 chat_recv(&srv->system.channel, NULL, buf);
124 static void chan_notice(xmpp_channel_t *chan, const char *fmt, ...)
126 static char buf[1024];
130 vsnprintf(buf, sizeof(buf), fmt, ap);
133 chat_recv(&chan->channel, NULL, buf);
136 static void split_jid(const char *jid, char *usr, char *srv, char *res)
141 if (usr) usr[0] = '\0';
142 if (srv) srv[0] = '\0';
143 if (res) res[0] = '\0';
145 for (int i = 0; jid && jid[i]; i++) {
156 if (ptr && (pos+1) < JID_LEN) {
162 //debug("JID: '%s' usr=[%s] srv=[%s] res=[%s]",
163 // jid, usr, srv, res);
166 static xmpp_channel_t *find_dest(xmpp_server_t *srv,
167 const char *jid, int is_muc)
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;
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);
179 /* Server channels */
180 if (match(jid, srv->srv))
183 /* Find existing channels */
184 for (channel_t *cur = channels; cur; cur = cur->next) {
185 if (cur->server != &srv->server)
187 chan = (xmpp_channel_t *)cur;
188 if (match(chan->dest, dest))
192 /* Create a new channel */
193 chan = (xmpp_channel_t *)add_channel(jid_usr, &srv->server);
194 strncpy(chan->dest, dest, JID_LEN);
198 static const char *find_attr(const char **attrs, const char *name)
200 for (int i = 0; attrs[i] && attrs[i+1]; i += 2)
201 if (match(attrs[i+0], name))
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);
211 static void on_start(void *_srv, const char *tag, const char **attrs)
213 xmpp_server_t *srv = _srv;
214 xmpp_run(srv, 0, tag, attrs, NULL, reset(&srv->buf));
217 static void on_data(void *_srv, const char *data, int len)
219 xmpp_server_t *srv = _srv;
220 append(&srv->buf, data, len);
223 static void on_end(void *_srv, const char *tag)
225 xmpp_server_t *srv = _srv;
226 xmpp_run(srv, 0, NULL, NULL, tag, reset(&srv->buf));
229 static void on_send(void *_srv)
231 xmpp_server_t *srv = _srv;
232 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
235 static void on_recv(void *_srv, char *buf, int len)
237 xmpp_server_t *srv = _srv;
239 XML_Parse(srv->expat, buf, len, 0);
240 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
243 static void on_err(void *_srv, int errno)
245 xmpp_server_t *srv = _srv;
246 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
249 static void on_timer(void *_srv)
252 xmpp_server_t *srv = _srv;
253 while (read(srv->timer, &buf, sizeof(buf)) > 0)
254 xmpp_run(srv, 1, NULL, NULL, NULL, NULL);
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)
264 debug("%*s \"%s\"", srv->indent*4, "", data);
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",
270 attrs[i+0], attrs[i+1],
271 attrs[i+2] ? "" : ">");
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;
285 net_open(&srv->net, srv->host, srv->port);
287 srv->timer = timerfd_create(CLOCK_MONOTONIC,
288 TFD_NONBLOCK|TFD_CLOEXEC);
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);
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);
304 debug("xmpp: connect -> stream");
305 srv->state = XMPP_SEND_STREAM;
307 if (srv->state == XMPP_ENCRYPT && !start && !end) {
308 net_encrypt(&srv->net);
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);
317 debug("xmpp: encrypt -> stream");
318 srv->state = XMPP_SEND_STREAM;
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);
328 debug("xmpp: restart -> stream");
329 srv->state = XMPP_SEND_STREAM;
333 if (srv->state == XMPP_SEND_STREAM) {
334 if (net_print(&srv->net,
335 "<?xml version='1.0'?>"
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;
348 if (srv->state == XMPP_RECV_FEATURES) {
349 if (match(start, "starttls")) {
350 debug("xmpp: features -> starttls");
351 srv->state = XMPP_SEND_STARTTLS;
353 if (match(start, "mechanisms")) {
354 debug("xmpp: features -> auth");
355 srv->state = XMPP_SEND_AUTH;
357 if (match(start, "bind")) {
358 debug("xmpp: features -> bind");
359 srv->state = XMPP_SEND_BIND;
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;
371 if (srv->state == XMPP_RECV_PROCEED) {
372 if (match(start, "proceed")) {
373 debug("xmpp: proceed -> encrypt");
374 srv->state = XMPP_ENCRYPT;
379 if (srv->state == XMPP_SEND_AUTH) {
380 static char plain[AUTH_LEN];
381 static char coded[AUTH_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,
388 " xmlns='urn:ietf:params:xml:ns:xmpp-sasl'"
389 " mechanism='PLAIN'>%.*s</auth>",
391 debug("xmpp: auth -> success");
392 srv->state = XMPP_RECV_SUCCESS;
395 if (srv->state == XMPP_RECV_SUCCESS) {
396 if (match(start, "success")) {
397 debug("xmpp: success -> restart");
398 srv->state = XMPP_RESTART;
403 if (srv->state == XMPP_SEND_BIND) {
404 const char *resource = srv->jid;
405 while (*resource && *resource != '/')
407 while (*resource && *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>"
416 debug("xmpp: bind -> jid");
417 srv->state = XMPP_RECV_JID;
420 if (srv->state == XMPP_RECV_JID) {
421 if (match(start, "jid")) {
422 debug("xmpp: jid -> session");
423 srv->state = XMPP_SEND_SESSION;
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'/>"
432 debug("xmpp: session -> presence");
433 srv->state = XMPP_SEND_PRESENCE;
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;
442 if (srv->state == XMPP_SEND_JOIN) {
443 for (channel_t *cur = channels; cur; cur = cur->next) {
444 if (cur->server != &srv->server)
446 xmpp_channel_t *chan = (xmpp_channel_t *)cur;
450 "<presence id='join' from='%s' to='%s/%s'>"
451 "<x xmlns='http://jabber.org/protocol/muc'/>"
453 srv->jid, chan->dest, srv->nick);
455 debug("xmpp: join -> ready");
456 srv->state = XMPP_READY;
460 xmpp_channel_t *chan = NULL;
462 if (srv->state >= XMPP_READY && idle) {
464 net_print(&srv->net, " ");
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 :
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);
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);
482 srv->msg_from = srv->msg_usr[0] ? srv->msg_usr : NULL;
483 srv->msg_chan = find_dest(srv, srv->msg_jid, 0);
486 debug("xmpp: %s -- jid=[%s] from=[%s] chan=[%s]",
488 srv->msg_jid, srv->msg_from,
489 srv->msg_chan->channel.name);
492 if (srv->state > XMPP_READY) {
493 if (srv->msg_chan && srv->msg_chan != &srv->system)
494 chan = srv->msg_chan;
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"),
503 chan_notice(chan, "user: %s", res);
505 if (match(start, "item") && !chan) {
506 srv_notice(srv, "item: [%s] %s",
507 find_attr(attrs, "jid"),
508 find_attr(attrs, "name"));
510 if (match(start, "identity")) {
511 srv_notice(srv, "identity: %s",
512 find_attr(attrs, "name"));
514 if (match(start, "feature")) {
515 srv_notice(srv, "feature: %s",
516 find_attr(attrs, "var"));
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"))
524 chan_notice(chan, "%-36s -- %s (%s)",
525 find_attr(attrs, "var"),
526 find_attr(attrs, "label"),
527 find_attr(attrs, "type"));
529 if (match(end, "title")) {
530 debug("xmpp: title -- jid=[%s]",
532 chan_notice(chan, "Title: %s", data);
534 if (match(end, "instructions")) {
535 debug("xmpp: instructions -- jid=[%s]",
537 chan_notice(chan, "%s", data);
542 if (srv->state == XMPP_IN_IQ) {
543 if (match(start, "vCard")) {
545 chan_notice(chan, "vCard for %s", chan->dest);
546 srv->state = XMPP_IN_VCARD;
549 if (srv->state == XMPP_IN_VCARD) {
550 if (end && chan && data) {
551 chan_notice(chan, "%-12s -- %s", end, data);
553 if (match(end, "vCard")) {
554 srv->state = XMPP_IN_IQ;
559 if (srv->state == XMPP_IN_MESSAGE) {
560 if (match(start, "delay")) {
561 const char *ts = find_attr(attrs, "stamp");
564 strptime(ts, "%Y-%m-%dT%H:%M:%S", &tm);
565 srv->stamp = timegm(&tm);
568 if (match(end, "subject") && chan) {
569 strset(&chan->channel.topic, data);
571 if (match(end, "body")) {
572 strset(&srv->body, data);
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);
580 chat_recv(&chan->channel, srv->msg_from, srv->body);
581 message_t *msg = &messages[history-1];
582 msg->when = srv->stamp ?: msg->when;
585 strset(&srv->body, NULL);
590 if (srv->state == XMPP_IN_PRESENCE) {
591 static char alias[JID_LEN];
593 if (match(start, "item")) {
594 if ((jid = find_attr(attrs, "jid")))
595 strncpy(alias, jid, JID_LEN);
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.",
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;
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;
626 if (match(start, "stream:error"))
628 if (match(end, "stream:error"))
631 if (match(end, "text")) {
632 debug("xmpp: error: %s", data);
633 srv_notice(srv, "error: %s", data);
641 for (server_t *cur = servers; cur; cur = cur->next) {
642 if (cur->protocol != XMPP)
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);
653 srv->srv = strcopy(srv->host);
655 error("jid is required");
659 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
661 for (channel_t *cur = channels; cur; cur = cur->next) {
662 if (cur->server->protocol != XMPP)
665 xmpp_channel_t *chan = (xmpp_channel_t*)cur;
666 xmpp_server_t *srv = (xmpp_server_t*)cur->server;
668 chan->room = strcopy(cur->name);
669 snprintf(chan->dest, JID_LEN, "%s@%s",
670 chan->room, srv->muc);
674 server_t *xmpp_server(void)
676 return new0(xmpp_server_t);
679 channel_t *xmpp_channel(void)
681 return new0(xmpp_channel_t);
684 void xmpp_config(server_t *server, channel_t *channel,
685 const char *group, const char *name,
686 const char *key, const char *value)
688 xmpp_server_t *srv = (xmpp_server_t*)server;
689 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
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);
716 if (match(key, "room"))
717 chan->room = get_string(value);
718 else if (match(key, "join"))
719 chan->join = get_bool(value);
723 void xmpp_send(channel_t *channel, const char *text)
725 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
726 xmpp_server_t *srv = (xmpp_server_t*)channel->server;
729 /* Handle commands */
730 if (text[0] == '/') {
731 if (prefix(text, "/items", &arg)) {
733 "<iq id='items' type='get' from='%s' to='%s'>"
734 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
736 srv->jid, arg ?: srv->srv);
738 else if (prefix(text, "/info", &arg)) {
740 "<iq id='info' type='get' from='%s' to='%s'>"
741 "<query xmlns='http://jabber.org/protocol/disco#info'/>"
743 srv->jid, arg ?: srv->srv);
745 else if (prefix(text, "/names", &arg)) {
747 chan = find_dest(srv, arg, 1);
748 if (chan == &srv->system) {
749 chan_notice(chan, "Cannot get names from server");
753 "<iq id='list' type='get' from='%s' to='%s'>"
754 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
756 srv->jid, chan->dest);
758 else if (prefix(text, "/join", &arg)) {
760 chan_notice(chan, "usage: /join <channel>");
764 "<presence id='join' from='%s' to='%s/%s'>"
765 "<x xmlns='http://jabber.org/protocol/muc'/>"
767 srv->jid, chan->dest, srv->nick);
768 chan = find_dest(srv, arg, 1);
769 chan_notice(chan, "Room: %s", arg);
771 else if (prefix(text, "/config", &arg)) {
773 chan_notice(chan, "Unimplemented: /config <arg>");
777 "<iq id='config' type='get' from='%s' to='%s'>"
778 "<query xmlns='http://jabber.org/protocol/muc#owner'/>"
780 srv->jid, chan->dest);
782 else if (prefix(text, "/query", &arg)) {
784 chan_notice(chan, "usage: /query <user>");
787 chan = find_dest(srv, arg, 0);
788 chan_notice(chan, "User: %s", arg);
790 else if (prefix(text, "/vcard", &arg)) {
792 chan = find_dest(srv, arg, 0);
795 "<iq id='vcard' type='get' from='%s' to='%s'>"
796 "<vCard xmlns='vcard-temp'/>"
798 srv->jid, chan->dest);
801 chan_notice(chan, "Unknown command %s", text);
804 debug("message: [%s]", text);
805 if (chan == &srv->system) {
806 chan_notice(chan, "Cannot send to server");
808 else if (!chan->dest) {
809 chan_notice(chan, "No destination for message");
811 else if (chan->room) {
813 "<message id='chat%d' from='%s' to='%s' type='groupchat'>"
816 srv->id++, srv->jid, chan->dest, text);
819 "<message id='chat%d' from='%s' to='%s'>"
822 srv->id++, srv->jid, chan->dest, text);
823 chat_recv(channel, srv->nick, text);