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/>.
69 xmpp_channel_t system;
92 char msg_jid[JID_LEN];
93 char msg_usr[JID_LEN];
94 char msg_srv[JID_LEN];
95 char msg_res[JID_LEN];
97 xmpp_channel_t *msg_chan;
100 /* Helper functions */
101 static void srv_notice(xmpp_server_t *srv, const char *fmt, ...)
103 static char buf[1024];
107 vsnprintf(buf, sizeof(buf), fmt, ap);
110 chat_recv(&srv->system.channel, NULL, buf);
113 static void chan_notice(xmpp_channel_t *chan, const char *fmt, ...)
115 static char buf[1024];
119 vsnprintf(buf, sizeof(buf), fmt, ap);
122 chat_recv(&chan->channel, NULL, buf);
125 static void split_jid(const char *jid, char *usr, char *srv, char *res)
130 if (usr) usr[0] = '\0';
131 if (srv) srv[0] = '\0';
132 if (res) res[0] = '\0';
134 for (int i = 0; jid && jid[i]; i++) {
145 if (ptr && (pos+1) < JID_LEN) {
151 //debug("JID: '%s' usr=[%s] srv=[%s] res=[%s]",
152 // jid, usr, srv, res);
155 static xmpp_channel_t *find_dest(xmpp_server_t *srv,
156 const char *jid, int is_muc)
158 static char jid_usr[JID_LEN];
159 static char jid_srv[JID_LEN];
160 static char dest[JID_LEN];
161 xmpp_channel_t *chan;
163 split_jid(jid, jid_usr, jid_srv, NULL);
164 snprintf(dest, JID_LEN, "%s@%s", jid_usr,
165 jid_srv[0] ? jid_srv :
166 is_muc ? srv->muc : srv->host);
168 /* Server channels */
169 if (match(jid, srv->host))
172 /* Find existing channels */
173 for (channel_t *cur = channels; cur; cur = cur->next) {
174 if (cur->server != &srv->server)
176 chan = (xmpp_channel_t *)cur;
177 if (match(chan->dest, dest))
181 /* Create a new channel */
182 chan = (xmpp_channel_t *)add_channel(jid_usr, &srv->server);
183 strncpy(chan->dest, dest, JID_LEN);
187 static const char *find_attr(const char **attrs, const char *name)
189 for (int i = 0; attrs[i] && attrs[i+1]; i += 2)
190 if (match(attrs[i+0], name))
195 /* Callback functions */
196 static void xmpp_run(xmpp_server_t *srv,
197 const char *start, const char **attrs,
198 const char *end, const char *data);
200 static void on_start(void *_srv, const char *tag, const char **attrs)
202 xmpp_server_t *srv = _srv;
203 xmpp_run(srv, tag, attrs, NULL, reset(&srv->buf));
206 static void on_data(void *_srv, const char *data, int len)
208 xmpp_server_t *srv = _srv;
209 append(&srv->buf, data, len);
212 static void on_end(void *_srv, const char *tag)
214 xmpp_server_t *srv = _srv;
215 xmpp_run(srv, NULL, NULL, tag, reset(&srv->buf));
218 static void on_send(void *_srv)
220 xmpp_server_t *srv = _srv;
221 xmpp_run(srv, NULL, NULL, NULL, NULL);
224 static void on_recv(void *_srv, char *buf, int len)
226 xmpp_server_t *srv = _srv;
228 XML_Parse(srv->expat, buf, len, 0);
229 xmpp_run(srv, NULL, NULL, NULL, NULL);
232 static void on_err(void *_srv, int errno)
234 xmpp_server_t *srv = _srv;
235 xmpp_run(srv, NULL, NULL, NULL, NULL);
238 /* XMPP State machine */
239 static void xmpp_run(xmpp_server_t *srv,
240 const char *start, const char **attrs,
241 const char *end, const char *data)
245 debug("%*s \"%s\"", srv->indent*4, "", data);
247 debug("%*s<%s>", srv->indent*4, "", start);
248 for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
249 debug("%*s%s=\"%s\"%s",
251 attrs[i+0], attrs[i+1],
252 attrs[i+2] ? "" : ">");
260 /* Connection Handling */
261 if (srv->state == XMPP_CONNECT && !start && !end) {
262 srv->net.send = on_send;
263 srv->net.recv = on_recv;
264 srv->net.err = on_err;
266 net_open(&srv->net, srv->host, srv->port);
268 if (!(srv->expat = XML_ParserCreate(NULL)))
269 error("Error creating XML parser");
270 XML_SetUserData(srv->expat, srv);
271 XML_SetStartElementHandler(srv->expat, on_start);
272 XML_SetEndElementHandler(srv->expat, on_end);
273 XML_SetCharacterDataHandler(srv->expat, on_data);
275 debug("xmpp: connect -> stream");
276 srv->state = XMPP_SEND_STREAM;
278 if (srv->state == XMPP_ENCRYPT && !start && !end) {
279 net_encrypt(&srv->net);
281 if (!(XML_ParserReset(srv->expat, NULL)))
282 error("Error resetting XML parser");
283 XML_SetUserData(srv->expat, srv);
284 XML_SetStartElementHandler(srv->expat, on_start);
285 XML_SetEndElementHandler(srv->expat, on_end);
286 XML_SetCharacterDataHandler(srv->expat, on_data);
288 debug("xmpp: encrypt -> stream");
289 srv->state = XMPP_SEND_STREAM;
291 if (srv->state == XMPP_RESTART && !start && !end) {
292 if (!(XML_ParserReset(srv->expat, NULL)))
293 error("Error resetting XML parser");
294 XML_SetUserData(srv->expat, srv);
295 XML_SetStartElementHandler(srv->expat, on_start);
296 XML_SetEndElementHandler(srv->expat, on_end);
297 XML_SetCharacterDataHandler(srv->expat, on_data);
299 debug("xmpp: restart -> stream");
300 srv->state = XMPP_SEND_STREAM;
304 if (srv->state == XMPP_SEND_STREAM) {
305 if (net_print(&srv->net,
306 "<?xml version='1.0'?>"
312 " xmlns='jabber:client'"
313 " xmlns:stream='http://etherx.jabber.org/streams'>",
314 srv->jid, srv->host)) {
315 debug("xmpp: stream -> features");
316 srv->state = XMPP_RECV_FEATURES;
319 if (srv->state == XMPP_RECV_FEATURES) {
320 if (match(start, "starttls")) {
321 debug("xmpp: features -> starttls");
322 srv->state = XMPP_SEND_STARTTLS;
324 if (match(start, "mechanisms")) {
325 debug("xmpp: features -> auth");
326 srv->state = XMPP_SEND_AUTH;
328 if (match(start, "bind")) {
329 debug("xmpp: features -> bind");
330 srv->state = XMPP_SEND_BIND;
335 if (srv->state == XMPP_SEND_STARTTLS) {
336 if (net_print(&srv->net,
337 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
338 debug("xmpp: startls -> proceed");
339 srv->state = XMPP_RECV_PROCEED;
342 if (srv->state == XMPP_RECV_PROCEED) {
343 if (match(start, "proceed")) {
344 debug("xmpp: proceed -> encrypt");
345 srv->state = XMPP_ENCRYPT;
350 if (srv->state == XMPP_SEND_AUTH) {
351 static char plain[AUTH_LEN];
352 static char coded[AUTH_LEN];
354 len = snprintf(plain, AUTH_LEN, "%s%c%s%c%s",
355 srv->user, '\0', srv->user, '\0', srv->pass);
356 len = base64(plain, len, coded, AUTH_LEN);
357 if (net_print(&srv->net,
359 " xmlns='urn:ietf:params:xml:ns:xmpp-sasl'"
360 " mechanism='PLAIN'>%.*s</auth>",
362 debug("xmpp: auth -> success");
363 srv->state = XMPP_RECV_SUCCESS;
366 if (srv->state == XMPP_RECV_SUCCESS) {
367 if (match(start, "success")) {
368 debug("xmpp: success -> restart");
369 srv->state = XMPP_RESTART;
374 if (srv->state == XMPP_SEND_BIND) {
375 const char *resource = srv->jid;
376 while (*resource && *resource != '/')
378 while (*resource && *resource == '/')
380 if (net_print(&srv->net,
381 "<iq id='bind' type='set'>"
382 "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
383 "<resource>%s</resource>"
387 debug("xmpp: bind -> jid");
388 srv->state = XMPP_RECV_JID;
391 if (srv->state == XMPP_RECV_JID) {
392 if (match(start, "jid")) {
393 debug("xmpp: jid -> session");
394 srv->state = XMPP_SEND_SESSION;
397 if (srv->state == XMPP_SEND_SESSION) {
398 if (net_print(&srv->net,
399 "<iq id='session' type='set' to='%s'>"
400 "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
403 debug("xmpp: session -> presence");
404 srv->state = XMPP_SEND_PRESENCE;
407 if (srv->state == XMPP_SEND_PRESENCE) {
408 if (net_print(&srv->net, "<presence/>")) {
409 debug("xmpp: presence -> join");
410 srv->state = XMPP_SEND_JOIN;
413 if (srv->state == XMPP_SEND_JOIN) {
414 for (channel_t *cur = channels; cur; cur = cur->next) {
415 if (cur->server != &srv->server)
417 xmpp_channel_t *chan = (xmpp_channel_t *)cur;
421 "<presence id='join' from='%s' to='%s/%s'>"
422 "<x xmlns='http://jabber.org/protocol/muc'/>"
424 srv->jid, chan->dest, srv->nick);
426 debug("xmpp: join -> ready");
427 srv->state = XMPP_READY;
431 xmpp_channel_t *chan = NULL;
433 if (srv->state == XMPP_READY) {
434 srv->state = match(start, "iq") ? XMPP_IN_IQ :
435 match(start, "message") ? XMPP_IN_MESSAGE :
436 match(start, "presence") ? XMPP_IN_PRESENCE :
438 if (srv->state != XMPP_READY) {
439 const char *from = find_attr(attrs, "from") ?: "";
440 strncpy(srv->msg_jid, from, JID_LEN);
441 split_jid(srv->msg_jid, srv->msg_usr,
442 srv->msg_srv, srv->msg_res);
444 if (match(srv->msg_srv, srv->muc)) {
445 srv->msg_from = srv->msg_res[0] ? srv->msg_res : NULL;
446 srv->msg_chan = find_dest(srv, srv->msg_jid, 1);
448 srv->msg_from = srv->msg_usr[0] ? srv->msg_usr : NULL;
449 srv->msg_chan = find_dest(srv, srv->msg_jid, 0);
452 debug("xmpp: %s -- jid=[%s] from=[%s] chan=[%s]",
454 srv->msg_jid, srv->msg_from,
455 srv->msg_chan->channel.name);
458 if (srv->state == XMPP_IN_IQ ||
459 srv->state == XMPP_IN_MESSAGE ||
460 srv->state == XMPP_IN_PRESENCE) {
461 if (srv->msg_chan && srv->msg_chan != &srv->system)
462 chan = srv->msg_chan;
466 if (srv->state == XMPP_IN_IQ) {
467 if (match(start, "item") && chan) {
468 static char res[JID_LEN];
469 split_jid(find_attr(attrs, "jid"),
471 chan_notice(chan, "user: %s", res);
473 if (match(start, "item") && !chan) {
474 srv_notice(srv, "item: [%s] %s",
475 find_attr(attrs, "jid"),
476 find_attr(attrs, "name"));
478 if (match(start, "identity")) {
479 srv_notice(srv, "identity: %s",
480 find_attr(attrs, "name"));
482 if (match(start, "feature")) {
483 srv_notice(srv, "feature: %s",
484 find_attr(attrs, "var"));
486 if (match(start, "field")) {
487 debug("xmpp: %s -- type=[%s] label=[%s]", end,
488 find_attr(attrs, "type"),
489 find_attr(attrs, "label"));
490 if (!find_attr(attrs, "label"))
492 chan_notice(chan, "%-36s -- %s (%s)",
493 find_attr(attrs, "var"),
494 find_attr(attrs, "label"),
495 find_attr(attrs, "type"));
497 if (match(end, "title")) {
498 debug("xmpp: title -- jid=[%s]",
500 chan_notice(chan, "Title: %s", data);
502 if (match(end, "instructions")) {
503 debug("xmpp: instructions -- jid=[%s]",
505 chan_notice(chan, "%s", data);
510 if (srv->state == XMPP_IN_MESSAGE) {
511 if (match(start, "delay")) {
512 const char *ts = find_attr(attrs, "stamp");
515 strptime(ts, "%Y-%m-%dT%H:%M:%S", &tm);
516 srv->stamp = timegm(&tm);
519 if (match(end, "body")) {
520 strset(&srv->body, data);
522 if (match(end, "message")) {
523 debug("xmpp: body (%s) -- chan=[%s] jid=[%s] from=[%s]",
524 srv->msg_from == srv->msg_usr ? "user" : "chat" ,
525 srv->msg_chan->channel.name,
526 srv->msg_jid, srv->msg_from);
528 chat_recv(&chan->channel, srv->msg_from, srv->body);
529 message_t *msg = &messages[history-1];
530 msg->when = srv->stamp ?: msg->when;
533 strset(&srv->body, NULL);
538 if (srv->state == XMPP_IN_PRESENCE) {
539 static char alias[JID_LEN];
541 if (match(start, "item")) {
542 if ((jid = find_attr(attrs, "jid")))
543 strncpy(alias, jid, JID_LEN);
545 if (match(end, "presence") && chan) {
546 if (alias[0] && !srv->quiet)
547 chan_notice(chan, "%s (%s) entered room.",
548 srv->msg_from, alias);
549 else if (!srv->quiet)
550 chan_notice(chan, "%s entered room.",
557 if (srv->state == XMPP_IN_IQ ||
558 srv->state == XMPP_IN_MESSAGE ||
559 srv->state == XMPP_IN_PRESENCE) {
560 if (match(end, "iq") ||
561 match(end, "message") ||
562 match(end, "presence")) {
563 srv->state = XMPP_READY;
565 srv->msg_jid[0] = '\0';
566 srv->msg_usr[0] = '\0';
567 srv->msg_srv[0] = '\0';
568 srv->msg_res[0] = '\0';
569 srv->msg_from = NULL;
570 srv->msg_chan = NULL;
572 static time_t due = 0;
573 static time_t now = 0;
576 net_print(&srv->net, "<presence/>");
582 if (match(start, "stream:error"))
584 if (match(end, "stream:error"))
587 if (match(end, "text")) {
588 debug("xmpp: error: %s", data);
589 srv_notice(srv, "error: %s", data);
597 for (server_t *cur = servers; cur; cur = cur->next) {
598 if (cur->protocol != XMPP)
601 xmpp_server_t *srv = (xmpp_server_t*)cur;
602 srv->system.channel.server = &srv->server;
603 srv->system.channel.name = srv->server.name;
604 srv_notice(srv, "XMPP Server: %s", srv->server.name);
609 error("jid is required");
611 xmpp_run(srv, NULL, NULL, NULL, NULL);
613 for (channel_t *cur = channels; cur; cur = cur->next) {
614 if (cur->server->protocol != XMPP)
617 xmpp_channel_t *chan = (xmpp_channel_t*)cur;
618 xmpp_server_t *srv = (xmpp_server_t*)cur->server;
620 chan->room = strcopy(cur->name);
621 snprintf(chan->dest, JID_LEN, "%s@%s",
622 chan->room, srv->muc);
626 server_t *xmpp_server(void)
628 return new0(xmpp_server_t);
631 channel_t *xmpp_channel(void)
633 return new0(xmpp_channel_t);
636 void xmpp_config(server_t *server, channel_t *channel,
637 const char *group, const char *name,
638 const char *key, const char *value)
640 xmpp_server_t *srv = (xmpp_server_t*)server;
641 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
644 if (match(key, "connect"))
645 srv->connect = get_bool(value);
646 else if (match(key, "host"))
647 srv->host = get_string(value);
648 else if (match(key, "port"))
649 srv->port = get_number(value);
650 else if (match(key, "muc"))
651 srv->muc = get_string(value);
652 else if (match(key, "nick"))
653 srv->nick = get_string(value);
654 else if (match(key, "jid"))
655 srv->jid = get_string(value);
656 else if (match(key, "user"))
657 srv->user = get_string(value);
658 else if (match(key, "pass"))
659 srv->pass = get_string(value);
660 else if (match(key, "quiet"))
661 srv->quiet = get_bool(value);
664 if (match(key, "room"))
665 chan->room = get_string(value);
666 else if (match(key, "join"))
667 chan->join = get_bool(value);
671 void xmpp_send(channel_t *channel, const char *text)
673 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
674 xmpp_server_t *srv = (xmpp_server_t*)channel->server;
677 /* Handle commands */
678 if (text[0] == '/') {
679 if (prefix(text, "/items", &arg)) {
681 "<iq id='items' type='get' from='%s' to='%s'>"
682 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
684 srv->jid, arg ?: srv->host);
686 else if (prefix(text, "/info", &arg)) {
688 "<iq id='info' type='get' from='%s' to='%s'>"
689 "<query xmlns='http://jabber.org/protocol/disco#info'/>"
691 srv->jid, arg ?: srv->host);
693 else if (prefix(text, "/names", &arg)) {
695 chan = find_dest(srv, arg, 1);
696 if (chan == &srv->system) {
697 chan_notice(chan, "Cannot get names from server");
701 "<iq id='list' type='get' from='%s' to='%s'>"
702 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
704 srv->jid, chan->dest);
706 else if (prefix(text, "/join", &arg)) {
708 chan_notice(chan, "usage: /join <channel>");
712 "<presence id='join' from='%s' to='%s/%s'>"
713 "<x xmlns='http://jabber.org/protocol/muc'/>"
715 srv->jid, chan->dest, srv->nick);
716 chan = find_dest(srv, arg, 1);
717 chan_notice(chan, "Room: %s", arg);
719 else if (prefix(text, "/config", &arg)) {
721 chan_notice(chan, "Unimplemented: /config <arg>");
725 "<iq id='config' type='get' from='%s' to='%s'>"
726 "<query xmlns='http://jabber.org/protocol/muc#owner'/>"
728 srv->jid, chan->dest);
730 else if (prefix(text, "/query", &arg)) {
732 chan_notice(chan, "usage: /query <user>");
735 chan = find_dest(srv, arg, 0);
736 chan_notice(chan, "User: %s", arg);
739 chan_notice(chan, "Unknown command %s", text);
742 debug("message: [%s]", text);
743 if (chan == &srv->system) {
744 chan_notice(chan, "Cannot send to server");
746 else if (!chan->dest) {
747 chan_notice(chan, "No destination for message");
749 else if (chan->room) {
751 "<message id='chat' from='%s' to='%s' type='groupchat'>"
754 srv->jid, chan->dest, text);
757 "<message id='chat' from='%s' to='%s'>"
760 srv->jid, chan->dest, text);
761 chat_recv(channel, srv->nick, text);