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/>.
66 xmpp_channel_t system;
85 char msg_jid[JID_LEN];
86 char msg_usr[JID_LEN];
87 char msg_srv[JID_LEN];
88 char msg_res[JID_LEN];
90 xmpp_channel_t *msg_chan;
93 /* Helper functions */
94 static void srv_notice(xmpp_server_t *srv, const char *fmt, ...)
96 static char buf[1024];
100 vsnprintf(buf, sizeof(buf), fmt, ap);
103 chat_recv(&srv->system.channel, NULL, buf);
106 static void chan_notice(xmpp_channel_t *chan, const char *fmt, ...)
108 static char buf[1024];
112 vsnprintf(buf, sizeof(buf), fmt, ap);
115 chat_recv(&chan->channel, NULL, buf);
118 static void split_jid(const char *jid, char *usr, char *srv, char *res)
123 if (usr) usr[0] = '\0';
124 if (srv) srv[0] = '\0';
125 if (res) res[0] = '\0';
127 for (int i = 0; jid && jid[i]; i++) {
138 if (ptr && (pos+1) < JID_LEN) {
144 //debug("JID: '%s' usr=[%s] srv=[%s] res=[%s]",
145 // jid, usr, srv, res);
148 static xmpp_channel_t *find_dest(xmpp_server_t *srv,
149 const char *jid, int is_muc)
151 static char jid_usr[JID_LEN];
152 static char jid_srv[JID_LEN];
153 static char dest[JID_LEN];
154 xmpp_channel_t *chan;
156 split_jid(jid, jid_usr, jid_srv, NULL);
157 snprintf(dest, JID_LEN, "%s@%s", jid_usr,
158 jid_srv[0] ? jid_srv :
159 is_muc ? srv->muc : srv->host);
161 /* Server channels */
162 if (match(jid, srv->host))
165 /* Find existing channels */
166 for (channel_t *cur = channels; cur; cur = cur->next) {
167 if (cur->server != &srv->server)
169 chan = (xmpp_channel_t *)cur;
170 if (match(chan->dest, dest))
174 /* Create a new channel */
175 chan = (xmpp_channel_t *)add_channel(jid_usr, &srv->server);
176 strncpy(chan->dest, dest, JID_LEN);
180 static const char *find_attr(const char **attrs, const char *name)
182 for (int i = 0; attrs[i] && attrs[i+1]; i += 2)
183 if (match(attrs[i+0], name))
188 /* Callback functions */
189 static void xmpp_run(xmpp_server_t *srv,
190 const char *start, const char **attrs,
191 const char *end, const char *data);
193 static void on_start(void *_srv, const char *tag, const char **attrs)
195 xmpp_server_t *srv = _srv;
196 xmpp_run(srv, tag, attrs, NULL, reset(&srv->buf));
199 static void on_data(void *_srv, const char *data, int len)
201 xmpp_server_t *srv = _srv;
202 append(&srv->buf, data, len);
205 static void on_end(void *_srv, const char *tag)
207 xmpp_server_t *srv = _srv;
208 xmpp_run(srv, NULL, NULL, tag, reset(&srv->buf));
211 static void on_send(void *_srv)
213 xmpp_server_t *srv = _srv;
214 xmpp_run(srv, NULL, NULL, NULL, NULL);
217 static void on_recv(void *_srv, char *buf, int len)
219 xmpp_server_t *srv = _srv;
221 XML_Parse(srv->expat, buf, len, 0);
222 xmpp_run(srv, NULL, NULL, NULL, NULL);
225 static void on_err(void *_srv, int errno)
227 xmpp_server_t *srv = _srv;
228 xmpp_run(srv, NULL, NULL, NULL, NULL);
231 /* XMPP State machine */
232 static void xmpp_run(xmpp_server_t *srv,
233 const char *start, const char **attrs,
234 const char *end, const char *data)
236 xmpp_channel_t *chan = NULL;
240 debug("%*s \"%s\"", srv->indent*4, "", data);
242 debug("%*s<%s>", srv->indent*4, "", start);
243 for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
244 debug("%*s%s=\"%s\"%s",
246 attrs[i+0], attrs[i+1],
247 attrs[i+2] ? "" : ">");
255 /* Connection Handling */
256 if (srv->state == XMPP_CONNECT && !start && !end) {
257 srv->net.send = on_send;
258 srv->net.recv = on_recv;
259 srv->net.err = on_err;
261 net_open(&srv->net, srv->host, srv->port);
263 if (!(srv->expat = XML_ParserCreate(NULL)))
264 error("Error creating XML parser");
265 XML_SetUserData(srv->expat, srv);
266 XML_SetStartElementHandler(srv->expat, on_start);
267 XML_SetEndElementHandler(srv->expat, on_end);
268 XML_SetCharacterDataHandler(srv->expat, on_data);
270 debug("xmpp: connect -> stream");
271 srv->state = XMPP_SEND_STREAM;
273 if (srv->state == XMPP_ENCRYPT && !start && !end) {
274 net_encrypt(&srv->net);
276 if (!(XML_ParserReset(srv->expat, NULL)))
277 error("Error resetting XML parser");
278 XML_SetUserData(srv->expat, srv);
279 XML_SetStartElementHandler(srv->expat, on_start);
280 XML_SetEndElementHandler(srv->expat, on_end);
281 XML_SetCharacterDataHandler(srv->expat, on_data);
283 debug("xmpp: encrypt -> stream");
284 srv->state = XMPP_SEND_STREAM;
286 if (srv->state == XMPP_RESTART && !start && !end) {
287 if (!(XML_ParserReset(srv->expat, NULL)))
288 error("Error resetting XML parser");
289 XML_SetUserData(srv->expat, srv);
290 XML_SetStartElementHandler(srv->expat, on_start);
291 XML_SetEndElementHandler(srv->expat, on_end);
292 XML_SetCharacterDataHandler(srv->expat, on_data);
294 debug("xmpp: restart -> stream");
295 srv->state = XMPP_SEND_STREAM;
299 if (srv->state == XMPP_SEND_STREAM) {
300 if (net_print(&srv->net,
301 "<?xml version='1.0'?>"
307 " xmlns='jabber:client'"
308 " xmlns:stream='http://etherx.jabber.org/streams'>",
309 srv->jid, srv->host)) {
310 debug("xmpp: stream -> features");
311 srv->state = XMPP_RECV_FEATURES;
314 if (srv->state == XMPP_RECV_FEATURES) {
315 if (match(start, "starttls")) {
316 debug("xmpp: features -> starttls");
317 srv->state = XMPP_SEND_STARTTLS;
319 if (match(start, "mechanisms")) {
320 debug("xmpp: features -> auth");
321 srv->state = XMPP_SEND_AUTH;
323 if (match(start, "bind")) {
324 debug("xmpp: features -> bind");
325 srv->state = XMPP_SEND_BIND;
330 if (srv->state == XMPP_SEND_STARTTLS) {
331 if (net_print(&srv->net,
332 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
333 debug("xmpp: startls -> proceed");
334 srv->state = XMPP_RECV_PROCEED;
337 if (srv->state == XMPP_RECV_PROCEED) {
338 if (match(start, "proceed")) {
339 debug("xmpp: proceed -> encrypt");
340 srv->state = XMPP_ENCRYPT;
345 if (srv->state == XMPP_SEND_AUTH) {
346 static char plain[AUTH_LEN];
347 static char coded[AUTH_LEN];
349 len = snprintf(plain, AUTH_LEN, "%s%c%s%c%s",
350 srv->user, '\0', srv->user, '\0', srv->pass);
351 len = base64(plain, len, coded, AUTH_LEN);
352 if (net_print(&srv->net,
354 " xmlns='urn:ietf:params:xml:ns:xmpp-sasl'"
355 " mechanism='PLAIN'>%.*s</auth>",
357 debug("xmpp: auth -> success");
358 srv->state = XMPP_RECV_SUCCESS;
361 if (srv->state == XMPP_RECV_SUCCESS) {
362 if (match(start, "success")) {
363 debug("xmpp: success -> restart");
364 srv->state = XMPP_RESTART;
369 if (srv->state == XMPP_SEND_BIND) {
370 const char *resource = srv->jid;
371 while (*resource && *resource != '/')
373 while (*resource && *resource == '/')
375 if (net_print(&srv->net,
376 "<iq id='bind' type='set'>"
377 "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
378 "<resource>%s</resource>"
382 debug("xmpp: bind -> jid");
383 srv->state = XMPP_RECV_JID;
386 if (srv->state == XMPP_RECV_JID) {
387 if (match(start, "jid")) {
388 debug("xmpp: jid -> presence");
389 srv->state = XMPP_SEND_PRESENCE;
392 if (srv->state == XMPP_SEND_PRESENCE) {
393 for (channel_t *cur = channels; cur; cur = cur->next) {
394 xmpp_channel_t *chan = (xmpp_channel_t*)cur;
395 if (cur->server != &srv->server || !chan->join)
398 "<presence id='join' from='%s' to='%s/%s'>"
399 "<x xmlns='http://jabber.org/protocol/muc'/>"
401 srv->jid, chan->dest, srv->nick);
403 srv->state = XMPP_READY;
407 if (srv->state == XMPP_READY) {
408 srv->state = match(start, "iq") ? XMPP_IN_IQ :
409 match(start, "message") ? XMPP_IN_MESSAGE :
410 match(start, "presence") ? XMPP_IN_PRESENCE :
412 if (srv->state != XMPP_READY) {
413 strncpy(srv->msg_jid, find_attr(attrs, "from"), JID_LEN);
414 split_jid(srv->msg_jid, srv->msg_usr,
415 srv->msg_srv, srv->msg_res);
417 if (match(srv->msg_srv, srv->muc)) {
418 srv->msg_from = srv->msg_res[0] ? srv->msg_res : NULL;
419 srv->msg_chan = find_dest(srv, srv->msg_jid, 1);
421 srv->msg_from = srv->msg_usr[0] ? srv->msg_usr : NULL;
422 srv->msg_chan = find_dest(srv, srv->msg_jid, 0);
425 debug("xmpp: %s -- jid=[%s] from=[%s] chan=[%s]",
427 srv->msg_jid, srv->msg_from,
428 srv->msg_chan->channel.name);
431 if (srv->state == XMPP_IN_IQ ||
432 srv->state == XMPP_IN_MESSAGE ||
433 srv->state == XMPP_IN_PRESENCE) {
434 if (srv->msg_chan && srv->msg_chan != &srv->system)
435 chan = srv->msg_chan;
439 if (srv->state == XMPP_IN_IQ) {
440 if (match(start, "item") && chan) {
441 static char res[JID_LEN];
442 split_jid(find_attr(attrs, "jid"),
444 chan_notice(chan, "user: %s", res);
446 if (match(start, "item") && !chan) {
447 srv_notice(srv, "item: [%s] %s",
448 find_attr(attrs, "jid"),
449 find_attr(attrs, "name"));
451 if (match(start, "identity")) {
452 srv_notice(srv, "identity: %s",
453 find_attr(attrs, "name"));
455 if (match(start, "feature")) {
456 srv_notice(srv, "feature: %s",
457 find_attr(attrs, "var"));
459 if (match(start, "field")) {
460 debug("xmpp: %s -- type=[%s] label=[%s]", end,
461 find_attr(attrs, "type"),
462 find_attr(attrs, "label"));
463 if (!find_attr(attrs, "label"))
465 chan_notice(chan, "%-36s -- %s (%s)",
466 find_attr(attrs, "var"),
467 find_attr(attrs, "label"),
468 find_attr(attrs, "type"));
470 if (match(end, "title")) {
471 debug("xmpp: title -- jid=[%s]",
473 chan_notice(chan, "Title: %s", data);
475 if (match(end, "instructions")) {
476 debug("xmpp: instructions -- jid=[%s]",
478 chan_notice(chan, "%s", data);
483 if (srv->state == XMPP_IN_MESSAGE) {
484 if (match(end, "body") && data) {
485 debug("xmpp: body (%s) -- chan=[%s] jid=[%s] from=[%s]",
486 srv->msg_from == srv->msg_usr ? "user" : "chat" ,
487 srv->msg_chan->channel.name,
488 srv->msg_jid, srv->msg_from);
489 chat_recv(&chan->channel, srv->msg_from, data);
494 if (srv->state == XMPP_IN_PRESENCE) {
495 static char alias[JID_LEN];
497 if (match(start, "item")) {
498 if ((jid = find_attr(attrs, "jid")))
499 strncpy(alias, jid, JID_LEN);
501 if (match(end, "presence") && chan) {
503 chan_notice(chan, "%s (%s) entered room.",
504 srv->msg_from, alias);
506 chan_notice(chan, "%s entered room.",
513 if (srv->state == XMPP_IN_IQ ||
514 srv->state == XMPP_IN_MESSAGE ||
515 srv->state == XMPP_IN_PRESENCE) {
516 if (match(end, "iq") ||
517 match(end, "message") ||
518 match(end, "presence")) {
519 srv->state = XMPP_READY;
521 srv->msg_jid[0] = '\0';
522 srv->msg_usr[0] = '\0';
523 srv->msg_srv[0] = '\0';
524 srv->msg_res[0] = '\0';
525 srv->msg_from = NULL;
526 srv->msg_chan = NULL;
530 if (match(start, "stream:error"))
532 if (match(end, "stream:error"))
535 if (match(end, "text")) {
536 debug("xmpp: error: %s", data);
537 srv_notice(srv, "error: %s", data);
545 for (server_t *cur = servers; cur; cur = cur->next) {
546 if (cur->protocol != XMPP)
549 xmpp_server_t *srv = (xmpp_server_t*)cur;
550 srv->system.channel.server = &srv->server;
551 srv->system.channel.name = srv->server.name;
552 srv_notice(srv, "XMPP Server: %s", srv->server.name);
557 error("jid is required");
559 xmpp_run(srv, NULL, NULL, NULL, NULL);
561 for (channel_t *cur = channels; cur; cur = cur->next) {
562 if (cur->server->protocol != XMPP)
565 xmpp_channel_t *chan = (xmpp_channel_t*)cur;
566 xmpp_server_t *srv = (xmpp_server_t*)cur->server;
568 chan->room = strcopy(cur->name);
569 snprintf(chan->dest, JID_LEN, "%s@%s",
570 chan->room, srv->muc);
574 server_t *xmpp_server(void)
576 return new0(xmpp_server_t);
579 channel_t *xmpp_channel(void)
581 return new0(xmpp_channel_t);
584 void xmpp_config(server_t *server, channel_t *channel,
585 const char *group, const char *name,
586 const char *key, const char *value)
588 xmpp_server_t *srv = (xmpp_server_t*)server;
589 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
592 if (match(key, "connect"))
593 srv->connect = get_bool(value);
594 else if (match(key, "host"))
595 srv->host = get_string(value);
596 else if (match(key, "port"))
597 srv->port = get_number(value);
598 else if (match(key, "muc"))
599 srv->muc = get_string(value);
600 else if (match(key, "nick"))
601 srv->nick = get_string(value);
602 else if (match(key, "jid"))
603 srv->jid = get_string(value);
604 else if (match(key, "user"))
605 srv->user = get_string(value);
606 else if (match(key, "pass"))
607 srv->pass = get_string(value);
610 if (match(key, "room"))
611 chan->room = get_string(value);
612 else if (match(key, "join"))
613 chan->join = get_bool(value);
617 void xmpp_send(channel_t *channel, const char *text)
619 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
620 xmpp_server_t *srv = (xmpp_server_t*)channel->server;
623 /* Handle commands */
624 if (text[0] == '/') {
625 if (prefix(text, "/items", &arg)) {
627 "<iq id='items' type='get' from='%s' to='%s'>"
628 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
630 srv->jid, arg ?: srv->host);
632 else if (prefix(text, "/info", &arg)) {
634 "<iq id='info' type='get' from='%s' to='%s'>"
635 "<query xmlns='http://jabber.org/protocol/disco#info'/>"
637 srv->jid, arg ?: srv->host);
639 else if (prefix(text, "/names", &arg)) {
641 chan = find_dest(srv, arg, 1);
642 if (chan == &srv->system) {
643 chan_notice(chan, "Cannot get names from server");
647 "<iq id='list' type='get' from='%s' to='%s'>"
648 "<query xmlns='http://jabber.org/protocol/disco#items'/>"
650 srv->jid, chan->dest);
652 else if (prefix(text, "/join", &arg)) {
654 chan_notice(chan, "usage: /join <channel>");
658 "<presence id='join' from='%s' to='%s/%s'>"
659 "<x xmlns='http://jabber.org/protocol/muc'/>"
661 srv->jid, chan->dest, srv->nick);
662 chan = find_dest(srv, arg, 1);
663 chan_notice(chan, "Room: %s", arg);
665 else if (prefix(text, "/config", &arg)) {
667 chan_notice(chan, "Unimplemented: /config <arg>");
671 "<iq id='config' type='get' from='%s' to='%s'>"
672 "<query xmlns='http://jabber.org/protocol/muc#owner'/>"
674 srv->jid, chan->dest);
676 else if (prefix(text, "/query", &arg)) {
678 chan_notice(chan, "usage: /query <user>");
681 chan = find_dest(srv, arg, 0);
682 chan_notice(chan, "User: %s", arg);
685 chan_notice(chan, "Unknown command %s", text);
688 debug("message: [%s]", text);
689 if (chan == &srv->system) {
690 chan_notice(chan, "Cannot send to server");
692 else if (!chan->dest) {
693 chan_notice(chan, "No destination for message");
695 else if (chan->room) {
697 "<message id='chat' from='%s' to='%s' type='groupchat'>"
700 srv->jid, chan->dest, text);
703 "<message id='chat' from='%s' to='%s'>"
706 srv->jid, chan->dest, text);
707 chat_recv(channel, srv->nick, text);