/* * Copyright (C) 2017 Andy Spencer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "util.h" #include "conf.h" #include "net.h" /* Constants */ #define AUTH_LEN 512 /* XMPP types */ typedef enum { XMPP_DEAD, XMPP_SEND_STREAM, XMPP_RECV_FEATURES, XMPP_SEND_STARTTLS, XMPP_RECV_PROCEED, XMPP_SEND_AUTH, XMPP_RECV_SUCCESS, XMPP_ENCRYPT, XMPP_RESTART, XMPP_READY, } xmpp_state_t; typedef struct xmpp_server_t { const char *name; const char *protocol; int connect; const char *host; int port; const char *jid; const char *user; const char *pass; net_t net; XML_Parser expat; xmpp_state_t state; buf_t buf; int indent; struct xmpp_server_t *next; } xmpp_server_t; typedef struct xmpp_channel_t { const char *name; const char *channel; const char *server; int join; struct xmpp_channel_t *next; } xmpp_channel_t; /* Local data */ static xmpp_server_t *servers; static xmpp_channel_t *channels; /* Local functions */ static xmpp_server_t *find_server(const char *name, int create) { xmpp_server_t *cur = NULL, *last = NULL; for (cur = servers; cur; last = cur, cur = cur->next) if (match(cur->name, name)) break; if (!cur && create) { cur = new0(xmpp_server_t); cur->name = get_name(name); if (last) last->next = cur; else servers = cur; } return cur; } static xmpp_channel_t *find_channel(const char *name, int create) { xmpp_channel_t *cur = NULL, *last = NULL; for (cur = channels; cur; last = cur, cur = cur->next) if (match(cur->name, name)) break; if (!cur && create) { cur = new0(xmpp_channel_t); cur->name = get_name(name); if (last) last->next = cur; else channels = cur; } return cur; } static void on_start(void *_srv, const char *tag, const char **attrs) { xmpp_server_t *srv = _srv; /* Debug print */ debug("%*s<%s>", srv->indent*4, "", tag); for (int i = 0; attrs[i] && attrs[i+1]; i += 2) { debug("%*s%s=\"%s\"%s", srv->indent*4+8, "", attrs[i+0], attrs[i+1], attrs[i+2] ? "" : ">"); } srv->indent++; /* Start TLS */ if (srv->state == XMPP_RECV_FEATURES) { if (match(tag, "starttls")) { debug("xmpp: features -> starttls"); srv->state = XMPP_SEND_STARTTLS; } if (match(tag, "mechanisms")) { debug("xmpp: features -> auth"); srv->state = XMPP_SEND_AUTH; } } if (srv->state == XMPP_RECV_PROCEED) { if (match(tag, "proceed")) { debug("xmpp: proceed -> encrypt"); srv->state = XMPP_ENCRYPT; } } if (srv->state == XMPP_RECV_SUCCESS) { if (match(tag, "success")) { debug("xmpp: success -> restart"); srv->state = XMPP_RESTART; } } } static void on_end(void *_srv, const char *tag) { xmpp_server_t *srv = _srv; /* Debug print */ if (srv->buf.len) { debug("%*s \"%s\"", srv->indent*4, "", (char*)srv->buf.data); srv->buf.len = 0; } srv->indent--; } static void on_data(void *_srv, const char *data, int len) { xmpp_server_t *srv = _srv; /* Debug print */ append(&srv->buf, data, len); } static void on_recv(void *_srv, char *buf, int len) { static char plain[AUTH_LEN]; static char coded[AUTH_LEN]; xmpp_server_t *srv = _srv; /* Parse input */ if (len > 0) XML_Parse(srv->expat, buf, len, 0); /* State machine */ output: if (srv->state == XMPP_SEND_STREAM) { if (net_print(&srv->net, "" "", srv->jid, srv->host)) { debug("xmpp: stream -> features"); srv->state = XMPP_RECV_FEATURES; } } if (srv->state == XMPP_SEND_STARTTLS) { if (net_print(&srv->net, "")) { debug("xmpp: startls -> proceed"); srv->state = XMPP_RECV_PROCEED; } } if (srv->state == XMPP_SEND_AUTH) { len = snprintf(plain, AUTH_LEN, "%s%c%s%c%s", srv->user, '\0', srv->user, '\0', srv->pass); len = base64(plain, len, coded, AUTH_LEN); if (net_print(&srv->net, "%.*s", len, coded)) { debug("xmpp: auth -> success"); srv->state = XMPP_RECV_SUCCESS; } } if (srv->state == XMPP_ENCRYPT) { /* Encrypt connection */ net_encrypt(&srv->net); /* Reset Expat */ if (!(XML_ParserReset(srv->expat, NULL))) error("Error resetting XML parser"); XML_SetUserData(srv->expat, srv); XML_SetStartElementHandler(srv->expat, on_start); XML_SetEndElementHandler(srv->expat, on_end); XML_SetCharacterDataHandler(srv->expat, on_data); /* Reset server */ debug("xmpp: encrypt -> stream"); srv->state = XMPP_SEND_STREAM; goto output; } if (srv->state == XMPP_RESTART) { /* Reset Expat */ if (!(XML_ParserReset(srv->expat, NULL))) error("Error resetting XML parser"); XML_SetUserData(srv->expat, srv); XML_SetStartElementHandler(srv->expat, on_start); XML_SetEndElementHandler(srv->expat, on_end); XML_SetCharacterDataHandler(srv->expat, on_data); /* Reset server */ debug("xmpp: restart -> stream"); srv->state = XMPP_SEND_STREAM; goto output; } } static void xmpp_connect(xmpp_server_t *srv) { /* Net connect */ srv->net.recv = on_recv; srv->net.data = srv; net_open(&srv->net, srv->host, srv->port); /* Setup Expat */ if (!(srv->expat = XML_ParserCreate(NULL))) error("Error creating XML parser"); XML_SetUserData(srv->expat, srv); XML_SetStartElementHandler(srv->expat, on_start); XML_SetEndElementHandler(srv->expat, on_end); XML_SetCharacterDataHandler(srv->expat, on_data); /* Setup server */ srv->state = XMPP_SEND_STREAM; } /* XMPP functions */ void xmpp_init(void) { for (xmpp_server_t *cur = servers; cur; cur = cur->next) { if (!match(cur->protocol, "xmpp")) continue; if (!cur->port) cur->port = 5222; if (!cur->jid) error("jid is required"); if (cur->connect) xmpp_connect(cur); } } void xmpp_config(const char *group, const char *name, const char *key, const char *value) { xmpp_server_t *srv; xmpp_channel_t *chan; if (match(group, "server")) { srv = find_server(name, 1); if (match(key, "protocol") && match(value, "xmpp")) srv->protocol = get_string(value); if (match(srv->protocol, "xmpp")) { if (match(key, "connect")) srv->connect = get_bool(value); else if (match(key, "host")) srv->host = get_string(value); else if (match(key, "port")) srv->port = get_number(value); else if (match(key, "jid")) srv->jid = get_string(value); else if (match(key, "user")) srv->user = get_string(value); else if (match(key, "pass")) srv->pass = get_string(value); } } else if (match(group, "channel")) { chan = find_channel(name, 1); if (match(key, "server") && find_server(value, 0)) chan->server = get_string(value); if (chan->server) { if (match(key, "channel")) chan->channel = get_string(value); else if (match(key, "join")) chan->join = get_bool(value); } } } void xmpp_send(const char *channel, const char *msg) { } void xmpp_exit(void) { }