]> Pileus Git - ~andy/lamechat/blob - xmpp.c
XMPP OpenSSL
[~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 /* XMPP Constants */
34 #define BUF_LEN 1024
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_ENCRYPT,
44         XMPP_READY,
45 } xmpp_state_t;
46
47 typedef struct xmpp_server_t {
48         const char  *name;
49         const char  *protocol;
50         int          connect;
51         const char  *host;
52         int          port;
53         const char  *jid;
54         const char  *pass;
55
56         net_t        net;
57         XML_Parser   expat;
58         xmpp_state_t state;
59         buf_t        buf;
60         int          indent;
61
62         struct xmpp_server_t *next;
63 } xmpp_server_t;
64
65 typedef struct xmpp_channel_t {
66         const char  *name;
67         const char  *channel;
68         const char  *server;
69         int          join;
70         struct xmpp_channel_t *next;
71 } xmpp_channel_t;
72
73 /* Local data */
74 static xmpp_server_t  *servers;
75 static xmpp_channel_t *channels;
76
77 /* Local functions */
78 static xmpp_server_t *find_server(const char *name, int create)
79 {
80         xmpp_server_t *cur = NULL, *last = NULL;
81         for (cur = servers; cur; last = cur, cur = cur->next)
82                 if (match(cur->name, name))
83                         break;
84         if (!cur && create) {
85                 cur = new0(xmpp_server_t);
86                 cur->name = get_name(name);
87                 if (last)
88                         last->next = cur;
89                 else
90                         servers = cur;
91         }
92         return cur;
93 }
94
95 static xmpp_channel_t *find_channel(const char *name, int create)
96 {
97         xmpp_channel_t *cur = NULL, *last = NULL;
98         for (cur = channels; cur; last = cur, cur = cur->next)
99                 if (match(cur->name, name))
100                         break;
101         if (!cur && create) {
102                 cur = new0(xmpp_channel_t);
103                 cur->name = get_name(name);
104                 if (last)
105                         last->next = cur;
106                 else
107                         channels = cur;
108         }
109         return cur;
110 }
111
112 static void on_start(void *_srv, const char *tag, const char **attrs)
113 {
114         xmpp_server_t *srv = _srv;
115
116         /* Debug print */
117         debug("%*s<%s>",
118                 srv->indent*4, "", tag);
119         for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
120                 debug("%*s%s=\"%s\"%s",
121                         srv->indent*4+8, "",
122                         attrs[i+0], attrs[i+1],
123                         attrs[i+2] ? "" : ">");
124         }
125         srv->indent++;
126
127         /* Start TLS */
128         if (srv->state == XMPP_RECV_FEATURES) {
129                 if (match(tag, "starttls")) {
130                         debug("xmpp: features -> starttls");
131                         srv->state = XMPP_SEND_STARTTLS;
132                 }
133         }
134         if (srv->state == XMPP_RECV_PROCEED) {
135                 if (match(tag, "proceed")) {
136                         debug("xmpp: proceed -> encrypt");
137                         srv->state = XMPP_ENCRYPT;
138                 }
139         }
140 }
141
142 static void on_end(void *_srv, const char *tag)
143 {
144         xmpp_server_t *srv = _srv;
145
146         /* Debug print */
147         if (srv->buf.len) {
148                 debug("%*s \"%s\"",
149                         srv->indent*4, "",
150                         (char*)srv->buf.data);
151                 srv->buf.len = 0;
152         }
153         srv->indent--;
154 }
155
156 static void on_data(void *_srv, const char *data, int len)
157 {
158         xmpp_server_t *srv = _srv;
159
160         /* Debug print */
161         append(&srv->buf, data, len);
162 }
163
164 static void on_recv(void *_srv, char *buf, int len)
165 {
166         xmpp_server_t *srv = _srv;
167
168         /* Parse input */
169         if (len > 0)
170                 XML_Parse(srv->expat, buf, len, 0);
171
172         /* State machine */
173 output:
174         if (srv->state == XMPP_SEND_STREAM) {
175                 if (net_print(&srv->net,
176                     "<?xml version='1.0'?>"
177                     "<stream:stream"
178                     " from='%s'"
179                     " to='%s'"
180                     " version='1.0'"
181                     " xml:lang='en'"
182                     " xmlns='jabber:client'"
183                     " xmlns:stream='http://etherx.jabber.org/streams'>",
184                     srv->jid, srv->host)) {
185                         debug("xmpp: stream -> features");
186                         srv->state = XMPP_RECV_FEATURES;
187                 }
188         }
189         if (srv->state == XMPP_SEND_STARTTLS) {
190                 if (net_print(&srv->net,
191                     "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
192                         debug("xmpp: startls -> proceed");
193                         srv->state = XMPP_RECV_PROCEED;
194                 }
195         }
196         if (srv->state == XMPP_ENCRYPT) {
197                 /* Encrypt connection */
198                 net_encrypt(&srv->net);
199
200                 /* Reset Expat */
201                 if (!(XML_ParserReset(srv->expat, NULL)))
202                         error("Error resetting XML parser");
203                 XML_SetUserData(srv->expat, srv);
204                 XML_SetStartElementHandler(srv->expat, on_start);
205                 XML_SetEndElementHandler(srv->expat, on_end);
206                 XML_SetCharacterDataHandler(srv->expat, on_data);
207
208                 /* Reset server */
209                 debug("xmpp: encrypt -> stream");
210                 srv->state = XMPP_SEND_STREAM;
211                 goto output;
212         }
213 }
214
215 static void xmpp_connect(xmpp_server_t *srv)
216 {
217         /* Net connect */
218         srv->net.recv = on_recv;
219         srv->net.data = srv;
220         net_open(&srv->net, srv->host, srv->port);
221
222         /* Setup Expat */
223         if (!(srv->expat = XML_ParserCreate(NULL)))
224                 error("Error creating XML parser");
225         XML_SetUserData(srv->expat, srv);
226         XML_SetStartElementHandler(srv->expat, on_start);
227         XML_SetEndElementHandler(srv->expat, on_end);
228         XML_SetCharacterDataHandler(srv->expat, on_data);
229
230         /* Setup server */
231         srv->state = XMPP_SEND_STREAM;
232 }
233
234 /* XMPP functions */
235 void xmpp_init(void)
236 {
237         for (xmpp_server_t *cur = servers; cur; cur = cur->next) {
238                 if (!match(cur->protocol, "xmpp"))
239                         continue;
240                 if (!cur->port)
241                         cur->port = 5222;
242                 if (!cur->jid)
243                         error("jid is required");
244                 if (cur->connect)
245                         xmpp_connect(cur);
246         }
247 }
248
249 void xmpp_config(const char *group, const char *name, const char *key, const char *value)
250 {
251         xmpp_server_t  *srv;
252         xmpp_channel_t *chan;
253
254         if (match(group, "server")) {
255                 srv = find_server(name, 1);
256                 if (match(key, "protocol") &&
257                     match(value, "xmpp"))
258                         srv->protocol = get_string(value);
259                 if (match(srv->protocol, "xmpp")) {
260                         if (match(key, "connect"))
261                                 srv->connect = get_bool(value);
262                         else if (match(key, "host"))
263                                 srv->host = get_string(value);
264                         else if (match(key, "port"))
265                                 srv->port = get_number(value);
266                         else if (match(key, "jid"))
267                                 srv->jid = get_string(value);
268                         else if (match(key, "pass"))
269                                 srv->pass = get_string(value);
270                 }
271         } else if (match(group, "channel")) {
272                 chan = find_channel(name, 1);
273                 if (match(key, "server") &&
274                     find_server(value, 0))
275                         chan->server = get_string(value);
276                 if (chan->server) {
277                         if (match(key, "channel"))
278                                 chan->channel = get_string(value);
279                         else if (match(key, "join"))
280                                 chan->join = get_bool(value);
281                 }
282         }
283 }
284
285 void xmpp_send(const char *channel, const char *msg)
286 {
287 }
288
289 void xmpp_exit(void)
290 {
291 }