]> Pileus Git - ~andy/lamechat/blob - xmpp.c
XMPP SASL
[~andy/lamechat] / xmpp.c
1 /*
2  * Copyright (C) 2017 Andy Spencer <andy753421@gmail.com>
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <netdb.h>
27 #include <expat.h>
28
29 #include "util.h"
30 #include "conf.h"
31 #include "net.h"
32
33 /* Constants */
34 #define AUTH_LEN 512
35
36 /* XMPP types */
37 typedef enum {
38         XMPP_DEAD,
39         XMPP_SEND_STREAM,
40         XMPP_RECV_FEATURES,
41         XMPP_SEND_STARTTLS,
42         XMPP_RECV_PROCEED,
43         XMPP_SEND_AUTH,
44         XMPP_RECV_SUCCESS,
45         XMPP_ENCRYPT,
46         XMPP_RESTART,
47         XMPP_READY,
48 } xmpp_state_t;
49
50 typedef struct xmpp_server_t {
51         const char  *name;
52         const char  *protocol;
53         int          connect;
54         const char  *host;
55         int          port;
56         const char  *jid;
57         const char  *user;
58         const char  *pass;
59
60         net_t        net;
61         XML_Parser   expat;
62         xmpp_state_t state;
63         buf_t        buf;
64         int          indent;
65
66         struct xmpp_server_t *next;
67 } xmpp_server_t;
68
69 typedef struct xmpp_channel_t {
70         const char  *name;
71         const char  *channel;
72         const char  *server;
73         int          join;
74         struct xmpp_channel_t *next;
75 } xmpp_channel_t;
76
77 /* Local data */
78 static xmpp_server_t  *servers;
79 static xmpp_channel_t *channels;
80
81 /* Local functions */
82 static xmpp_server_t *find_server(const char *name, int create)
83 {
84         xmpp_server_t *cur = NULL, *last = NULL;
85         for (cur = servers; cur; last = cur, cur = cur->next)
86                 if (match(cur->name, name))
87                         break;
88         if (!cur && create) {
89                 cur = new0(xmpp_server_t);
90                 cur->name = get_name(name);
91                 if (last)
92                         last->next = cur;
93                 else
94                         servers = cur;
95         }
96         return cur;
97 }
98
99 static xmpp_channel_t *find_channel(const char *name, int create)
100 {
101         xmpp_channel_t *cur = NULL, *last = NULL;
102         for (cur = channels; cur; last = cur, cur = cur->next)
103                 if (match(cur->name, name))
104                         break;
105         if (!cur && create) {
106                 cur = new0(xmpp_channel_t);
107                 cur->name = get_name(name);
108                 if (last)
109                         last->next = cur;
110                 else
111                         channels = cur;
112         }
113         return cur;
114 }
115
116 static void on_start(void *_srv, const char *tag, const char **attrs)
117 {
118         xmpp_server_t *srv = _srv;
119
120         /* Debug print */
121         debug("%*s<%s>",
122                 srv->indent*4, "", tag);
123         for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
124                 debug("%*s%s=\"%s\"%s",
125                         srv->indent*4+8, "",
126                         attrs[i+0], attrs[i+1],
127                         attrs[i+2] ? "" : ">");
128         }
129         srv->indent++;
130
131         /* Start TLS */
132         if (srv->state == XMPP_RECV_FEATURES) {
133                 if (match(tag, "starttls")) {
134                         debug("xmpp: features -> starttls");
135                         srv->state = XMPP_SEND_STARTTLS;
136                 }
137                 if (match(tag, "mechanisms")) {
138                         debug("xmpp: features -> auth");
139                         srv->state = XMPP_SEND_AUTH;
140                 }
141         }
142         if (srv->state == XMPP_RECV_PROCEED) {
143                 if (match(tag, "proceed")) {
144                         debug("xmpp: proceed -> encrypt");
145                         srv->state = XMPP_ENCRYPT;
146                 }
147         }
148         if (srv->state == XMPP_RECV_SUCCESS) {
149                 if (match(tag, "success")) {
150                         debug("xmpp: success -> restart");
151                         srv->state = XMPP_RESTART;
152                 }
153         }
154 }
155
156 static void on_end(void *_srv, const char *tag)
157 {
158         xmpp_server_t *srv = _srv;
159
160         /* Debug print */
161         if (srv->buf.len) {
162                 debug("%*s \"%s\"",
163                         srv->indent*4, "",
164                         (char*)srv->buf.data);
165                 srv->buf.len = 0;
166         }
167         srv->indent--;
168 }
169
170 static void on_data(void *_srv, const char *data, int len)
171 {
172         xmpp_server_t *srv = _srv;
173
174         /* Debug print */
175         append(&srv->buf, data, len);
176 }
177
178 static void on_recv(void *_srv, char *buf, int len)
179 {
180         static char plain[AUTH_LEN];
181         static char coded[AUTH_LEN];
182
183         xmpp_server_t *srv = _srv;
184
185         /* Parse input */
186         if (len > 0)
187                 XML_Parse(srv->expat, buf, len, 0);
188
189         /* State machine */
190 output:
191         if (srv->state == XMPP_SEND_STREAM) {
192                 if (net_print(&srv->net,
193                     "<?xml version='1.0'?>"
194                     "<stream:stream"
195                     " from='%s'"
196                     " to='%s'"
197                     " version='1.0'"
198                     " xml:lang='en'"
199                     " xmlns='jabber:client'"
200                     " xmlns:stream='http://etherx.jabber.org/streams'>",
201                     srv->jid, srv->host)) {
202                         debug("xmpp: stream -> features");
203                         srv->state = XMPP_RECV_FEATURES;
204                 }
205         }
206         if (srv->state == XMPP_SEND_STARTTLS) {
207                 if (net_print(&srv->net,
208                     "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
209                         debug("xmpp: startls -> proceed");
210                         srv->state = XMPP_RECV_PROCEED;
211                 }
212         }
213         if (srv->state == XMPP_SEND_AUTH) {
214                 len = snprintf(plain, AUTH_LEN, "%s%c%s%c%s",
215                                 srv->user, '\0', srv->user, '\0', srv->pass);
216                 len = base64(plain, len, coded, AUTH_LEN);
217                 if (net_print(&srv->net,
218                     "<auth"
219                     " xmlns='urn:ietf:params:xml:ns:xmpp-sasl'"
220                     " mechanism='PLAIN'>%.*s</auth>",
221                         len, coded)) {
222                         debug("xmpp: auth -> success");
223                         srv->state = XMPP_RECV_SUCCESS;
224                 }
225         }
226         if (srv->state == XMPP_ENCRYPT) {
227                 /* Encrypt connection */
228                 net_encrypt(&srv->net);
229
230                 /* Reset Expat */
231                 if (!(XML_ParserReset(srv->expat, NULL)))
232                         error("Error resetting XML parser");
233                 XML_SetUserData(srv->expat, srv);
234                 XML_SetStartElementHandler(srv->expat, on_start);
235                 XML_SetEndElementHandler(srv->expat, on_end);
236                 XML_SetCharacterDataHandler(srv->expat, on_data);
237
238                 /* Reset server */
239                 debug("xmpp: encrypt -> stream");
240                 srv->state = XMPP_SEND_STREAM;
241                 goto output;
242         }
243         if (srv->state == XMPP_RESTART) {
244                 /* Reset Expat */
245                 if (!(XML_ParserReset(srv->expat, NULL)))
246                         error("Error resetting XML parser");
247                 XML_SetUserData(srv->expat, srv);
248                 XML_SetStartElementHandler(srv->expat, on_start);
249                 XML_SetEndElementHandler(srv->expat, on_end);
250                 XML_SetCharacterDataHandler(srv->expat, on_data);
251
252                 /* Reset server */
253                 debug("xmpp: restart -> stream");
254                 srv->state = XMPP_SEND_STREAM;
255                 goto output;
256         }
257 }
258
259 static void xmpp_connect(xmpp_server_t *srv)
260 {
261         /* Net connect */
262         srv->net.recv = on_recv;
263         srv->net.data = srv;
264         net_open(&srv->net, srv->host, srv->port);
265
266         /* Setup Expat */
267         if (!(srv->expat = XML_ParserCreate(NULL)))
268                 error("Error creating XML parser");
269         XML_SetUserData(srv->expat, srv);
270         XML_SetStartElementHandler(srv->expat, on_start);
271         XML_SetEndElementHandler(srv->expat, on_end);
272         XML_SetCharacterDataHandler(srv->expat, on_data);
273
274         /* Setup server */
275         srv->state = XMPP_SEND_STREAM;
276 }
277
278 /* XMPP functions */
279 void xmpp_init(void)
280 {
281         for (xmpp_server_t *cur = servers; cur; cur = cur->next) {
282                 if (!match(cur->protocol, "xmpp"))
283                         continue;
284                 if (!cur->port)
285                         cur->port = 5222;
286                 if (!cur->jid)
287                         error("jid is required");
288                 if (cur->connect)
289                         xmpp_connect(cur);
290         }
291 }
292
293 void xmpp_config(const char *group, const char *name, const char *key, const char *value)
294 {
295         xmpp_server_t  *srv;
296         xmpp_channel_t *chan;
297
298         if (match(group, "server")) {
299                 srv = find_server(name, 1);
300                 if (match(key, "protocol") &&
301                     match(value, "xmpp"))
302                         srv->protocol = get_string(value);
303                 if (match(srv->protocol, "xmpp")) {
304                         if (match(key, "connect"))
305                                 srv->connect = get_bool(value);
306                         else if (match(key, "host"))
307                                 srv->host = get_string(value);
308                         else if (match(key, "port"))
309                                 srv->port = get_number(value);
310                         else if (match(key, "jid"))
311                                 srv->jid = get_string(value);
312                         else if (match(key, "user"))
313                                 srv->user = get_string(value);
314                         else if (match(key, "pass"))
315                                 srv->pass = get_string(value);
316                 }
317         } else if (match(group, "channel")) {
318                 chan = find_channel(name, 1);
319                 if (match(key, "server") &&
320                     find_server(value, 0))
321                         chan->server = get_string(value);
322                 if (chan->server) {
323                         if (match(key, "channel"))
324                                 chan->channel = get_string(value);
325                         else if (match(key, "join"))
326                                 chan->join = get_bool(value);
327                 }
328         }
329 }
330
331 void xmpp_send(const char *channel, const char *msg)
332 {
333 }
334
335 void xmpp_exit(void)
336 {
337 }