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/>.
73 char msg_from[JID_LEN];
74 char msg_usr[JID_LEN];
75 char msg_srv[JID_LEN];
76 char msg_res[JID_LEN];
87 /* Helper functions */
88 static void split_jid(const char *jid, char *usr, char *srv, char *res)
93 if (usr) usr[0] = '\0';
94 if (srv) srv[0] = '\0';
95 if (res) res[0] = '\0';
97 for (int i = 0; jid && jid[i]; i++) {
108 if (ptr && (pos+1) < JID_LEN) {
114 debug("JID: '%s' usr=[%s] srv=[%s] res=[%s]",
118 static xmpp_channel_t *find_dest(xmpp_server_t *srv,
119 const char *jid, int is_muc)
121 static char jid_usr[JID_LEN];
122 static char jid_srv[JID_LEN];
123 static char dest[JID_LEN];
124 xmpp_channel_t *chan;
126 split_jid(jid, jid_usr, jid_srv, NULL);
127 snprintf(dest, JID_LEN, "%s@%s", jid_usr,
128 jid_srv[0] ? jid_srv :
129 is_muc ? srv->muc : srv->host);
131 /* Find existing channels */
132 for (channel_t *cur = channels; cur; cur = cur->next) {
133 if (cur->server != &srv->server)
135 chan = (xmpp_channel_t *)cur;
136 if (match(chan->dest, dest))
140 /* Create a new channel */
141 chan = (xmpp_channel_t *)add_channel(jid_usr, &srv->server);
142 strncpy(chan->dest, dest, JID_LEN);
146 static const char *find_attr(const char **attrs, const char *name)
148 for (int i = 0; attrs[i] && attrs[i+1]; i += 2)
149 if (match(attrs[i+0], name))
154 /* Callback functions */
155 static void xmpp_run(xmpp_server_t *srv,
156 const char *start, const char **attrs,
157 const char *end, const char *data);
159 static void on_start(void *_srv, const char *tag, const char **attrs)
161 xmpp_server_t *srv = _srv;
162 xmpp_run(srv, tag, attrs, NULL, reset(&srv->buf));
165 static void on_data(void *_srv, const char *data, int len)
167 xmpp_server_t *srv = _srv;
168 append(&srv->buf, data, len);
171 static void on_end(void *_srv, const char *tag)
173 xmpp_server_t *srv = _srv;
174 xmpp_run(srv, NULL, NULL, tag, reset(&srv->buf));
177 static void on_send(void *_srv)
179 xmpp_server_t *srv = _srv;
180 xmpp_run(srv, NULL, NULL, NULL, NULL);
183 static void on_recv(void *_srv, char *buf, int len)
185 xmpp_server_t *srv = _srv;
187 XML_Parse(srv->expat, buf, len, 0);
188 xmpp_run(srv, NULL, NULL, NULL, NULL);
191 static void on_err(void *_srv, int errno)
193 xmpp_server_t *srv = _srv;
194 xmpp_run(srv, NULL, NULL, NULL, NULL);
197 /* XMPP State machine */
198 static void xmpp_run(xmpp_server_t *srv,
199 const char *start, const char **attrs,
200 const char *end, const char *data)
204 debug("%*s \"%s\"", srv->indent*4, "", data);
206 debug("%*s<%s>", srv->indent*4, "", start);
207 for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
208 debug("%*s%s=\"%s\"%s",
210 attrs[i+0], attrs[i+1],
211 attrs[i+2] ? "" : ">");
219 /* Connection Handling */
220 if (srv->state == XMPP_CONNECT && !start && !end) {
221 srv->net.send = on_send;
222 srv->net.recv = on_recv;
223 srv->net.err = on_err;
225 net_open(&srv->net, srv->host, srv->port);
227 if (!(srv->expat = XML_ParserCreate(NULL)))
228 error("Error creating XML parser");
229 XML_SetUserData(srv->expat, srv);
230 XML_SetStartElementHandler(srv->expat, on_start);
231 XML_SetEndElementHandler(srv->expat, on_end);
232 XML_SetCharacterDataHandler(srv->expat, on_data);
234 debug("xmpp: connect -> stream");
235 srv->state = XMPP_SEND_STREAM;
237 if (srv->state == XMPP_ENCRYPT && !start && !end) {
238 net_encrypt(&srv->net);
240 if (!(XML_ParserReset(srv->expat, NULL)))
241 error("Error resetting XML parser");
242 XML_SetUserData(srv->expat, srv);
243 XML_SetStartElementHandler(srv->expat, on_start);
244 XML_SetEndElementHandler(srv->expat, on_end);
245 XML_SetCharacterDataHandler(srv->expat, on_data);
247 debug("xmpp: encrypt -> stream");
248 srv->state = XMPP_SEND_STREAM;
250 if (srv->state == XMPP_RESTART && !start && !end) {
251 if (!(XML_ParserReset(srv->expat, NULL)))
252 error("Error resetting XML parser");
253 XML_SetUserData(srv->expat, srv);
254 XML_SetStartElementHandler(srv->expat, on_start);
255 XML_SetEndElementHandler(srv->expat, on_end);
256 XML_SetCharacterDataHandler(srv->expat, on_data);
258 debug("xmpp: restart -> stream");
259 srv->state = XMPP_SEND_STREAM;
263 if (srv->state == XMPP_SEND_STREAM) {
264 if (net_print(&srv->net,
265 "<?xml version='1.0'?>"
271 " xmlns='jabber:client'"
272 " xmlns:stream='http://etherx.jabber.org/streams'>",
273 srv->jid, srv->host)) {
274 debug("xmpp: stream -> features");
275 srv->state = XMPP_RECV_FEATURES;
278 if (srv->state == XMPP_RECV_FEATURES) {
279 if (match(start, "starttls")) {
280 debug("xmpp: features -> starttls");
281 srv->state = XMPP_SEND_STARTTLS;
283 if (match(start, "mechanisms")) {
284 debug("xmpp: features -> auth");
285 srv->state = XMPP_SEND_AUTH;
287 if (match(start, "bind")) {
288 debug("xmpp: features -> bind");
289 srv->state = XMPP_SEND_BIND;
294 if (srv->state == XMPP_SEND_STARTTLS) {
295 if (net_print(&srv->net,
296 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
297 debug("xmpp: startls -> proceed");
298 srv->state = XMPP_RECV_PROCEED;
301 if (srv->state == XMPP_RECV_PROCEED) {
302 if (match(start, "proceed")) {
303 debug("xmpp: proceed -> encrypt");
304 srv->state = XMPP_ENCRYPT;
309 if (srv->state == XMPP_SEND_AUTH) {
310 static char plain[AUTH_LEN];
311 static char coded[AUTH_LEN];
313 len = snprintf(plain, AUTH_LEN, "%s%c%s%c%s",
314 srv->user, '\0', srv->user, '\0', srv->pass);
315 len = base64(plain, len, coded, AUTH_LEN);
316 if (net_print(&srv->net,
318 " xmlns='urn:ietf:params:xml:ns:xmpp-sasl'"
319 " mechanism='PLAIN'>%.*s</auth>",
321 debug("xmpp: auth -> success");
322 srv->state = XMPP_RECV_SUCCESS;
325 if (srv->state == XMPP_RECV_SUCCESS) {
326 if (match(start, "success")) {
327 debug("xmpp: success -> restart");
328 srv->state = XMPP_RESTART;
333 if (srv->state == XMPP_SEND_BIND) {
334 const char *resource = srv->jid;
335 while (*resource && *resource != '/')
337 while (*resource && *resource == '/')
339 if (net_print(&srv->net,
340 "<iq id='bind' type='set'>"
341 "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
342 "<resource>%s</resource>"
346 debug("xmpp: bind -> jid");
347 srv->state = XMPP_RECV_JID;
350 if (srv->state == XMPP_RECV_JID) {
351 if (match(start, "jid")) {
352 debug("xmpp: jid -> presence");
353 srv->state = XMPP_SEND_PRESENCE;
356 if (srv->state == XMPP_SEND_PRESENCE) {
357 for (channel_t *cur = channels; cur; cur = cur->next) {
358 xmpp_channel_t *chan = (xmpp_channel_t*)cur;
359 if (cur->server != &srv->server || !chan->join)
362 "<presence id='join' from='%s' to='%s/%s'>"
363 "<x xmlns='http://jabber.org/protocol/muc'/>"
365 srv->jid, chan->dest, srv->nick);
367 srv->state = XMPP_READY;
371 if (srv->state == XMPP_READY) {
372 if (match(start, "item"))
373 chat_notice(NULL, NULL, "item: [%s] %s",
374 find_attr(attrs, "jid"),
375 find_attr(attrs, "name"));
376 if (match(start, "identity"))
377 chat_notice(NULL, NULL, "identity: %s",
378 find_attr(attrs, "name"));
379 if (match(start, "feature"))
380 chat_notice(NULL, NULL, "feature: %s",
381 find_attr(attrs, "var"));
384 /* Receive messages */
385 if (srv->state == XMPP_READY) {
386 if (match(start, "message")) {
387 strncpy(srv->msg_from, find_attr(attrs,"from"),
389 split_jid(srv->msg_from, srv->msg_usr,
390 srv->msg_srv, srv->msg_res);
392 if (match(end, "message")) {
393 srv->msg_from[0] = '\0';
394 srv->msg_usr[0] = '\0';
395 srv->msg_srv[0] = '\0';
396 srv->msg_res[0] = '\0';
398 if (match(end, "body") && data) {
399 xmpp_channel_t *chan;
400 debug("xmpp: jid -> ready");
401 if (match(srv->msg_srv, srv->muc)) {
402 chan = find_dest(srv, srv->msg_from, 1);
403 chat_recv(&chan->channel, srv->msg_res, data);
405 chan = find_dest(srv, srv->msg_from, 0);
406 chat_recv(&chan->channel, srv->msg_usr, data);
412 if (match(start, "stream:error"))
414 if (match(end, "stream:error"))
417 if (match(end, "text")) {
418 debug("xmpp: error: %s", data);
419 chat_notice(NULL, NULL, "error: %s", data);
427 for (server_t *cur = servers; cur; cur = cur->next) {
428 if (cur->protocol != XMPP)
431 xmpp_server_t *srv = (xmpp_server_t*)cur;
435 error("jid is required");
437 xmpp_run(srv, NULL, NULL, NULL, NULL);
439 for (channel_t *cur = channels; cur; cur = cur->next) {
440 if (cur->server->protocol != XMPP)
443 xmpp_channel_t *chan = (xmpp_channel_t*)cur;
444 xmpp_server_t *srv = (xmpp_server_t*)cur->server;
446 chan->room = strcopy(cur->name);
447 snprintf(chan->dest, JID_LEN, "%s@%s",
448 chan->room, srv->muc);
452 server_t *xmpp_server(void)
454 return new0(xmpp_server_t);
457 channel_t *xmpp_channel(void)
459 return new0(xmpp_channel_t);
462 void xmpp_config(server_t *server, channel_t *channel,
463 const char *group, const char *name,
464 const char *key, const char *value)
466 xmpp_server_t *srv = (xmpp_server_t*)server;
467 xmpp_channel_t *chan = (xmpp_channel_t*)channel;
470 if (match(key, "connect"))
471 srv->connect = get_bool(value);
472 else if (match(key, "host"))
473 srv->host = get_string(value);
474 else if (match(key, "port"))
475 srv->port = get_number(value);
476 else if (match(key, "muc"))
477 srv->muc = get_string(value);
478 else if (match(key, "nick"))
479 srv->nick = get_string(value);
480 else if (match(key, "jid"))
481 srv->jid = get_string(value);
482 else if (match(key, "user"))
483 srv->user = get_string(value);
484 else if (match(key, "pass"))
485 srv->pass = get_string(value);
488 if (match(key, "room"))
489 chan->room = get_string(value);
490 else if (match(key, "join"))
491 chan->join = get_bool(value);
495 void xmpp_send(message_t *msg)
497 xmpp_channel_t *chan = (xmpp_channel_t*)msg->channel;
498 xmpp_server_t *srv = (xmpp_server_t*)msg->channel->server;
499 const char *txt = msg->text;
502 /* Handle commands */
504 if (prefix(txt, "/items", &arg)) {
506 "<iq id='items' type='get' from='%s' to='%s'>"
507 "<query xmlns='http://jabber.org/protocol/disco#items' />"
509 srv->jid, arg ?: srv->host);
511 else if (prefix(txt, "/info", &arg)) {
513 "<iq id='info' type='get' from='%s' to='%s'>"
514 "<query xmlns='http://jabber.org/protocol/disco#info' />"
516 srv->jid, arg ?: srv->host);
518 else if (prefix(txt, "/list", &arg)) {
520 "<iq id='list' type='get' from='%s' to='%s'>"
521 "<query xmlns='http://jabber.org/protocol/disco#items' />"
523 srv->jid, arg ?: srv->muc);
525 else if (prefix(txt, "/join", &arg)) {
527 chat_notice(NULL, NULL, "usage: /join <channel>");
530 chan = find_dest(srv, arg, 1);
532 "<presence id='join' from='%s' to='%s/%s'>"
533 "<x xmlns='http://jabber.org/protocol/muc'/>"
535 srv->jid, chan->dest, srv->nick);
537 else if (prefix(txt, "/query", &arg)) {
539 chat_notice(NULL, NULL, "usage: /query <user>");
542 chan = find_dest(srv, arg, 0);
546 debug("unknown: [%s]", txt);
547 chat_notice(NULL, NULL,
548 "unknown command %s", txt);
551 debug("message: [%s]", txt);
554 "<message id='chat' from='%s' to='%s' type='groupchat'>"
557 srv->jid, chan->dest, txt);
560 "<message id='chat' from='%s' to='%s'>"
563 srv->jid, chan->dest, txt);