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>
73 xmpp_channel_t system;
100 char msg_jid[JID_LEN];
101 char msg_usr[JID_LEN];
102 char msg_srv[JID_LEN];
103 char msg_res[JID_LEN];
105 xmpp_channel_t *msg_chan;
108 /* Helper functions */
109 static void srv_notice(xmpp_server_t *srv, const char *fmt, ...)
111 static char buf[1024];
115 vsnprintf(buf, sizeof(buf), fmt, ap);
118 chat_recv(&srv->system.channel, NULL, buf);
121 static void chan_notice(xmpp_channel_t *chan, const char *fmt, ...)
123 static char buf[1024];
127 vsnprintf(buf, sizeof(buf), fmt, ap);
130 chat_recv(&chan->channel, NULL, buf);
133 static void split_jid(const char *jid, char *usr, char *srv, char *res)
138 if (usr) usr[0] = '\0';
139 if (srv) srv[0] = '\0';
140 if (res) res[0] = '\0';
142 for (int i = 0; jid && jid[i]; i++) {
153 if (ptr && (pos+1) < JID_LEN) {
159 //debug("JID: '%s' usr=[%s] srv=[%s] res=[%s]",
160 // jid, usr, srv, res);
163 static xmpp_channel_t *find_dest(xmpp_server_t *srv,
164 const char *jid, int is_muc)
166 static char jid_usr[JID_LEN];
167 static char jid_srv[JID_LEN];
168 static char dest[JID_LEN];
169 xmpp_channel_t *chan;
171 split_jid(jid, jid_usr, jid_srv, NULL);
172 snprintf(dest, JID_LEN, "%s@%s", jid_usr,
173 jid_srv[0] ? jid_srv :
174 is_muc ? srv->muc : srv->host);
176 /* Server channels */
177 if (match(jid, srv->host))
180 /* Find existing channels */
181 for (channel_t *cur = channels; cur; cur = cur->next) {
182 if (cur->server != &srv->server)
184 chan = (xmpp_channel_t *)cur;
185 if (match(chan->dest, dest))
189 /* Create a new channel */
190 chan = (xmpp_channel_t *)add_channel(jid_usr, &srv->server);
191 strncpy(chan->dest, dest, JID_LEN);
195 static const char *find_attr(const char **attrs, const char *name)
197 for (int i = 0; attrs[i] && attrs[i+1]; i += 2)
198 if (match(attrs[i+0], name))
203 /* Callback functions */
204 static void xmpp_run(xmpp_server_t *srv, int idle,
205 const char *start, const char **attrs,
206 const char *end, const char *data);
208 static void on_start(void *_srv, const char *tag, const char **attrs)
210 xmpp_server_t *srv = _srv;
211 xmpp_run(srv, 0, tag, attrs, NULL, reset(&srv->buf));
214 static void on_data(void *_srv, const char *data, int len)
216 xmpp_server_t *srv = _srv;
217 append(&srv->buf, data, len);
220 static void on_end(void *_srv, const char *tag)
222 xmpp_server_t *srv = _srv;
223 xmpp_run(srv, 0, NULL, NULL, tag, reset(&srv->buf));
226 static void on_send(void *_srv)
228 xmpp_server_t *srv = _srv;
229 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
232 static void on_recv(void *_srv, char *buf, int len)
234 xmpp_server_t *srv = _srv;
236 XML_Parse(srv->expat, buf, len, 0);
237 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
240 static void on_err(void *_srv, int errno)
242 xmpp_server_t *srv = _srv;
243 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
246 static void on_timer(void *_srv)
249 xmpp_server_t *srv = _srv;
250 while (read(srv->timer, &buf, sizeof(buf)) > 0)
251 xmpp_run(srv, 1, NULL, NULL, NULL, NULL);
254 /* XMPP State machine */
255 static void xmpp_run(xmpp_server_t *srv, int idle,
256 const char *start, const char **attrs,
257 const char *end, const char *data)
261 debug("%*s \"%s\"", srv->indent*4, "", data);
263 debug("%*s<%s>", srv->indent*4, "", start);
264 for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
265 debug("%*s%s=\"%s\"%s",
267 attrs[i+0], attrs[i+1],
268 attrs[i+2] ? "" : ">");
276 /* Connection Handling */
277 if (srv->state == XMPP_CONNECT && !start && !end) {
278 srv->net.send = on_send;
279 srv->net.recv = on_recv;
280 srv->net.err = on_err;
282 net_open(&srv->net, srv->host, srv->port);
284 srv->timer = timerfd_create(CLOCK_MONOTONIC,
285 TFD_NONBLOCK|TFD_CLOEXEC);
287 error("creating timer fd");
288 struct timespec tspec = {srv->timeout, 0};
289 struct itimerspec itspec = {tspec, tspec};
290 timerfd_settime(srv->timer, 0, &itspec, NULL);
291 poll_add(&srv->poll, srv->timer, on_timer, srv);
292 poll_ctl(&srv->poll, 1, 0, 1);
294 if (!(srv->expat = XML_ParserCreate(NULL)))
295 error("Error creating XML parser");
296 XML_SetUserData(srv->expat, srv);
297 XML_SetStartElementHandler(srv->expat, on_start);
298 XML_SetEndElementHandler(srv->expat, on_end);
299 XML_SetCharacterDataHandler(srv->expat, on_data);
301 debug("xmpp: connect -> stream");
302 srv->state = XMPP_SEND_STREAM;
304 if (srv->state == XMPP_ENCRYPT && !start && !end) {
305 net_encrypt(&srv->net);
307 if (!(XML_ParserReset(srv->expat, NULL)))
308 error("Error resetting XML parser");
309 XML_SetUserData(srv->expat, srv);
310 XML_SetStartElementHandler(srv->expat, on_start);
311 XML_SetEndElementHandler(srv->expat, on_end);
312 XML_SetCharacterDataHandler(srv->expat, on_data);
314 debug("xmpp: encrypt -> stream");
315 srv->state = XMPP_SEND_STREAM;
317 if (srv->state == XMPP_RESTART && !start && !end) {
318 if (!(XML_ParserReset(srv->expat, NULL)))
319 error("Error resetting XML parser");
320 XML_SetUserData(srv->expat, srv);
321 XML_SetStartElementHandler(srv->expat, on_start);
322 XML_SetEndElementHandler(srv->expat, on_end);
323 XML_SetCharacterDataHandler(srv->expat, on_data);
325 debug("xmpp: restart -> stream");
326 srv->state = XMPP_SEND_STREAM;
330 if (srv->state == XMPP_SEND_STREAM) {
331 if (net_print(&srv->net,
332 "<?xml version='1.0'?>"
338 " xmlns='jabber:client'"
339 " xmlns:stream='http://etherx.jabber.org/streams'>",
340 srv->jid, srv->host)) {
341 debug("xmpp: stream -> features");
342 srv->state = XMPP_RECV_FEATURES;
345 if (srv->state == XMPP_RECV_FEATURES) {
346 if (match(start, "starttls")) {
347 debug("xmpp: features -> starttls");
348 srv->state = XMPP_SEND_STARTTLS;
350 if (match(start, "mechanisms")) {
351 debug("xmpp: features -> auth");
352 srv->state = XMPP_SEND_AUTH;
354 if (match(start, "bind")) {
355 debug("xmpp: features -> bind");
356 srv->state = XMPP_SEND_BIND;
361 if (srv->state == XMPP_SEND_STARTTLS) {
362 if (net_print(&srv->net,
363 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
364 debug("xmpp: startls -> proceed");
365 srv->state = XMPP_RECV_PROCEED;
368 if (srv->state == XMPP_RECV_PROCEED) {
369 if (match(start, "proceed")) {
370 debug("xmpp: proceed -> encrypt");
371 srv->state = XMPP_ENCRYPT;
376 if (srv->state == XMPP_SEND_AUTH) {
377 static char plain[AUTH_LEN];
378 static char coded[AUTH_LEN];
380 len = snprintf(plain, AUTH_LEN, "%s%c%s%c%s",
381 srv->user, '\0', srv->user, '\0', srv->pass);
382 len = base64(plain, len, coded, AUTH_LEN);
383 if (net_print(&srv->net,
385 " xmlns='urn:ietf:params:xml:ns:xmpp-sasl'"
386 " mechanism='PLAIN'>%.*s</auth>",
388 debug("xmpp: auth -> success");
389 srv->state = XMPP_RECV_SUCCESS;
392 if (srv->state == XMPP_RECV_SUCCESS) {
393 if (match(start, "success")) {
394 debug("xmpp: success -> restart");
395 srv->state = XMPP_RESTART;
400 if (srv->state == XMPP_SEND_BIND) {
401 const char *resource = srv->jid;
402 while (*resource && *resource != '/')
404 while (*resource && *resource == '/')
406 if (net_print(&srv->net,
407 "<iq id='bind' type='set'>"
408 "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
409 "<resource>%s</resource>"
413 debug("xmpp: bind -> jid");
414 srv->state = XMPP_RECV_JID;
417 if (srv->state == XMPP_RECV_JID) {
418 if (match(start, "jid")) {
419 debug("xmpp: jid -> session");
420 srv->state = XMPP_SEND_SESSION;
423 if (srv->state == XMPP_SEND_SESSION) {
424 if (net_print(&srv->net,
425 "<iq id='session' type='set' to='%s'>"
426 "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
429 debug("xmpp: session -> presence");
430 srv->state = XMPP_SEND_PRESENCE;
433 if (srv->state == XMPP_SEND_PRESENCE) {
434 if (net_print(&srv->net, "<presence/>")) {
435 debug("xmpp: presence -> join");
436 srv->state = XMPP_SEND_JOIN;
439 if (srv->state == XMPP_SEND_JOIN) {
440 for (channel_t *cur = channels; cur; cur = cur->next) {
441 if (cur->server != &srv->server)
443 xmpp_channel_t *chan = (xmpp_channel_t *)cur;
447 "<presence id='join' from='%s' to='%s/%s'>"
448 "<x xmlns='http://jabber.org/protocol/muc'/>"
450 srv->jid, chan->dest, srv->nick);
452 debug("xmpp: join -> ready");
453 srv->state = XMPP_READY;
457 xmpp_channel_t *chan = NULL;
459 if (srv->state >= XMPP_READY && idle) {
461 net_print(&srv->net, " ");
464 if (srv->state == XMPP_READY) {
465 srv->state = match(start, "iq") ? XMPP_IN_IQ :
466 match(start, "message") ? XMPP_IN_MESSAGE :
467 match(start, "presence") ? XMPP_IN_PRESENCE :
469 if (srv->state != XMPP_READY) {
470 const char *from = find_attr(attrs, "from") ?: "";
471 strncpy(srv->msg_jid, from, JID_LEN);
472 split_jid(srv->msg_jid, srv->msg_usr,
473 srv->msg_srv, srv->msg_res);
475 if (match(srv->msg_srv, srv->muc)) {
476 srv->msg_from = srv->msg_res[0] ? srv->msg_res : NULL;
477 srv->msg_chan = find_dest(srv, srv->msg_jid, 1);
479 srv->msg_from = srv->msg_usr[0] ? srv->msg_usr : NULL;
480 srv->msg_chan = find_dest(srv, srv->msg_jid, 0);
483 debug("xmpp: %s -- jid=[%s] from=[%s] chan=[%s]",
485 srv->msg_jid, srv->msg_from,
486 srv->msg_chan->channel.name);
489 if (srv->state == XMPP_IN_IQ ||
490 srv->state == XMPP_IN_MESSAGE ||
491 srv->state == XMPP_IN_PRESENCE) {
492 if (srv->msg_chan && srv->msg_chan != &srv->system)
493 chan = srv->msg_chan;
497 if (srv->state == XMPP_IN_IQ) {
498 if (match(start, "item") && chan) {
499 static char res[JID_LEN];
500 split_jid(find_attr(attrs, "jid"),
502 chan_notice(chan, "user: %s", res);
504 if (match(start, "item") && !chan) {
505 srv_notice(srv, "item: [%s] %s",
506 find_attr(attrs, "jid"),
507 find_attr(attrs, "name"));
509 if (match(start, "identity")) {
510 srv_notice(srv, "identity: %s",
511 find_attr(attrs, "name"));
513 if (match(start, "feature")) {
514 srv_notice(srv, "feature: %s",
515 find_attr(attrs, "var"));
517 if (match(start, "field")) {
518 debug("xmpp: %s -- type=[%s] label=[%s]", end,
519 find_attr(attrs, "type"),
520 find_attr(attrs, "label"));
521 if (!find_attr(attrs, "label"))
523 chan_notice(chan, "%-36s -- %s (%s)",
524 find_attr(attrs, "var"),
525 find_attr(attrs, "label"),
526 find_attr(attrs, "type"));
528 if (match(end, "title")) {
529 debug("xmpp: title -- jid=[%s]",
531 chan_notice(chan, "Title: %s", data);
533 if (match(end, "instructions")) {
534 debug("xmpp: instructions -- jid=[%s]",
536 chan_notice(chan, "%s", data);
541 if (srv->state == XMPP_IN_MESSAGE) {
542 if (match(start, "delay")) {
543 const char *ts = find_attr(attrs, "stamp");
546 strptime(ts, "%Y-%m-%dT%H:%M:%S", &tm);
547 srv->stamp = timegm(&tm);
550 if (match(end, "body")) {
551 strset(&srv->body, data);
553 if (match(end, "message")) {
554 debug("xmpp: body (%s) -- chan=[%s] jid=[%s] from=[%s]",
555 srv->msg_from == srv->msg_usr ? "user" : "chat" ,
556 srv->msg_chan->channel.name,
557 srv->msg_jid, srv->msg_from);
559 chat_recv(&chan->channel, srv->msg_from, srv->body);
560 message_t *msg = &messages[history-1];
561 msg->when = srv->stamp ?: msg->when;
564 strset(&srv->body, NULL);
569 if (srv->state == XMPP_IN_PRESENCE) {
570 static char alias[JID_LEN];
572 if (match(start, "item")) {
573 if ((jid = find_attr(attrs, "jid")))
574 strncpy(alias, jid, JID_LEN);
576 if (match(end, "presence") && chan) {
577 if (alias[0] && !srv->quiet)
578 chan_notice(chan, "%s (%s) entered room.",
579 srv->msg_from, alias);
580 else if (!srv->quiet)
581 chan_notice(chan, "%s entered room.",
588 if (srv->state == XMPP_IN_IQ ||
589 srv->state == XMPP_IN_MESSAGE ||
590 srv->state == XMPP_IN_PRESENCE) {
591 if (match(end, "iq") ||
592 match(end, "message") ||
593 match(end, "presence")) {
594 srv->state = XMPP_READY;
596 srv->msg_jid[0] = '\0';
597 srv->msg_usr[0] = '\0';
598 srv->msg_srv[0] = '\0';
599 srv->msg_res[0] = '\0';
600 srv->msg_from = NULL;
601 srv->msg_chan = NULL;
605 if (match(start, "stream:error"))
607 if (match(end, "stream:error"))
610 if (match(end, "text")) {
611 debug("xmpp: error: %s", data);
612 srv_notice(srv, "error: %s", data);
620 for (server_t *cur = servers; cur; cur = cur->next) {
621 if (cur->protocol != XMPP)
624 xmpp_server_t *srv = (xmpp_server_t*)cur;
625 srv->system.channel.server = &srv->server;
626 srv->system.channel.name = srv->server.name;
627 srv_notice(srv, "XMPP Server: %s", srv->server.name);
632 error("jid is required");
636 xmpp_run(srv, 0, NULL, NULL, NULL, NULL);
638 for (channel_t *cur = channels; cur; cur = cur->next) {
639 if (cur->server->protocol != XMPP)
642 xmpp_channel_t *chan = (xmpp_channel_t*)cur;
643 xmpp_server_t *srv = (xmpp_server_t*)cur->server;
645 chan->room = strcopy(cur->name);
646 snprintf(chan->dest, JID_LEN, "%s@%s",
647 chan->room, srv->muc);
651 server_t *xmpp_server(void)
653 return new0(xmpp_server_t);
656 channel_t *xmpp_channel(void)
658 return new0(xmpp_channel_t);
661 void xmpp_config(server_t *server, channel_t *channel,
662 const char *group, const char *name,
663 const char *key, const char *value)
665 xmpp_server_t *srv = (xmpp_server_t*)server;
666 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
669 if (match(key, "connect"))
670 srv->connect = get_bool(value);
671 else if (match(key, "timeout"))
672 srv->timeout = get_number(value);
673 else if (match(key, "host"))
674 srv->host = get_string(value);
675 else if (match(key, "port"))
676 srv->port = get_number(value);
677 else if (match(key, "muc"))
678 srv->muc = get_string(value);
679 else if (match(key, "nick"))
680 srv->nick = get_string(value);
681 else if (match(key, "jid"))
682 srv->jid = get_string(value);
683 else if (match(key, "user"))
684 srv->user = get_string(value);
685 else if (match(key, "pass"))
686 srv->pass = get_string(value);
687 else if (match(key, "quiet"))
688 srv->quiet = get_bool(value);
691 if (match(key, "room"))
692 chan->room = get_string(value);
693 else if (match(key, "join"))
694 chan->join = get_bool(value);
698 void xmpp_send(channel_t *channel, const char *text)
700 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
701 xmpp_server_t *srv = (xmpp_server_t*)channel->server;
704 /* Handle commands */
705 if (text[0] == '/') {
706 if (prefix(text, "/items", &arg)) {
708 "<iq id='items' type='get' from='%s' to='%s'>"
709 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
711 srv->jid, arg ?: srv->host);
713 else if (prefix(text, "/info", &arg)) {
715 "<iq id='info' type='get' from='%s' to='%s'>"
716 "<query xmlns='http://jabber.org/protocol/disco#info'/>"
718 srv->jid, arg ?: srv->host);
720 else if (prefix(text, "/names", &arg)) {
722 chan = find_dest(srv, arg, 1);
723 if (chan == &srv->system) {
724 chan_notice(chan, "Cannot get names from server");
728 "<iq id='list' type='get' from='%s' to='%s'>"
729 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
731 srv->jid, chan->dest);
733 else if (prefix(text, "/join", &arg)) {
735 chan_notice(chan, "usage: /join <channel>");
739 "<presence id='join' from='%s' to='%s/%s'>"
740 "<x xmlns='http://jabber.org/protocol/muc'/>"
742 srv->jid, chan->dest, srv->nick);
743 chan = find_dest(srv, arg, 1);
744 chan_notice(chan, "Room: %s", arg);
746 else if (prefix(text, "/config", &arg)) {
748 chan_notice(chan, "Unimplemented: /config <arg>");
752 "<iq id='config' type='get' from='%s' to='%s'>"
753 "<query xmlns='http://jabber.org/protocol/muc#owner'/>"
755 srv->jid, chan->dest);
757 else if (prefix(text, "/query", &arg)) {
759 chan_notice(chan, "usage: /query <user>");
762 chan = find_dest(srv, arg, 0);
763 chan_notice(chan, "User: %s", arg);
766 chan_notice(chan, "Unknown command %s", text);
769 debug("message: [%s]", text);
770 if (chan == &srv->system) {
771 chan_notice(chan, "Cannot send to server");
773 else if (!chan->dest) {
774 chan_notice(chan, "No destination for message");
776 else if (chan->room) {
778 "<message id='chat' from='%s' to='%s' type='groupchat'>"
781 srv->jid, chan->dest, text);
784 "<message id='chat' from='%s' to='%s'>"
787 srv->jid, chan->dest, text);
788 chat_recv(channel, srv->nick, text);