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/>.
53 typedef struct xmpp_server_t {
69 struct xmpp_server_t *next;
72 typedef struct xmpp_channel_t {
77 struct xmpp_channel_t *next;
81 static xmpp_server_t *servers;
82 static xmpp_channel_t *channels;
85 static xmpp_server_t *find_server(const char *name, int create)
87 xmpp_server_t *cur = NULL, *last = NULL;
88 for (cur = servers; cur; last = cur, cur = cur->next)
89 if (match(cur->name, name))
92 cur = new0(xmpp_server_t);
93 cur->name = get_name(name);
102 static xmpp_channel_t *find_channel(const char *name, int create)
104 xmpp_channel_t *cur = NULL, *last = NULL;
105 for (cur = channels; cur; last = cur, cur = cur->next)
106 if (match(cur->name, name))
108 if (!cur && create) {
109 cur = new0(xmpp_channel_t);
110 cur->name = get_name(name);
119 static void on_start(void *_srv, const char *tag, const char **attrs)
121 xmpp_server_t *srv = _srv;
125 srv->indent*4, "", tag);
126 for (int i = 0; attrs[i] && attrs[i+1]; i += 2) {
127 debug("%*s%s=\"%s\"%s",
129 attrs[i+0], attrs[i+1],
130 attrs[i+2] ? "" : ">");
135 if (srv->state == XMPP_RECV_FEATURES) {
136 if (match(tag, "starttls")) {
137 debug("xmpp: features -> starttls");
138 srv->state = XMPP_SEND_STARTTLS;
140 if (match(tag, "mechanisms")) {
141 debug("xmpp: features -> auth");
142 srv->state = XMPP_SEND_AUTH;
144 if (match(tag, "bind")) {
145 debug("xmpp: features -> bind");
146 srv->state = XMPP_SEND_BIND;
149 if (srv->state == XMPP_RECV_PROCEED) {
150 if (match(tag, "proceed")) {
151 debug("xmpp: proceed -> encrypt");
152 srv->state = XMPP_ENCRYPT;
157 if (srv->state == XMPP_RECV_SUCCESS) {
158 if (match(tag, "success")) {
159 debug("xmpp: success -> restart");
160 srv->state = XMPP_RESTART;
163 if (srv->state == XMPP_RECV_JID) {
164 if (match(tag, "jid")) {
165 debug("xmpp: jid -> ready");
166 srv->state = XMPP_READY;
171 static void on_end(void *_srv, const char *tag)
173 xmpp_server_t *srv = _srv;
179 (char*)srv->buf.data);
184 /* Receive messages */
185 if (srv->state == XMPP_READY) {
186 if (match(tag, "body")) {
187 debug("xmpp: jid -> ready");
188 chat_recv("#test", NULL, data);
193 static void on_data(void *_srv, const char *data, int len)
195 xmpp_server_t *srv = _srv;
198 append(&srv->buf, data, len);
201 static void on_recv(void *_srv, char *buf, int len)
203 static char plain[AUTH_LEN];
204 static char coded[AUTH_LEN];
205 const char *resource;
207 xmpp_server_t *srv = _srv;
211 XML_Parse(srv->expat, buf, len, 0);
215 if (srv->state == XMPP_SEND_STREAM) {
216 if (net_print(&srv->net,
217 "<?xml version='1.0'?>"
223 " xmlns='jabber:client'"
224 " xmlns:stream='http://etherx.jabber.org/streams'>",
225 srv->jid, srv->host)) {
226 debug("xmpp: stream -> features");
227 srv->state = XMPP_RECV_FEATURES;
230 if (srv->state == XMPP_SEND_STARTTLS) {
231 if (net_print(&srv->net,
232 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>")) {
233 debug("xmpp: startls -> proceed");
234 srv->state = XMPP_RECV_PROCEED;
237 if (srv->state == XMPP_SEND_AUTH) {
238 len = snprintf(plain, AUTH_LEN, "%s%c%s%c%s",
239 srv->user, '\0', srv->user, '\0', srv->pass);
240 len = base64(plain, len, coded, AUTH_LEN);
241 if (net_print(&srv->net,
243 " xmlns='urn:ietf:params:xml:ns:xmpp-sasl'"
244 " mechanism='PLAIN'>%.*s</auth>",
246 debug("xmpp: auth -> success");
247 srv->state = XMPP_RECV_SUCCESS;
250 if (srv->state == XMPP_SEND_BIND) {
252 while (*resource && *resource != '/')
254 while (*resource && *resource == '/')
256 if (net_print(&srv->net,
257 "<iq id='bind' type='set'>"
258 "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
259 "<resource>%s</resource>"
263 debug("xmpp: bind -> jid");
264 srv->state = XMPP_RECV_JID;
267 if (srv->state == XMPP_ENCRYPT) {
268 /* Encrypt connection */
269 net_encrypt(&srv->net);
272 if (!(XML_ParserReset(srv->expat, NULL)))
273 error("Error resetting XML parser");
274 XML_SetUserData(srv->expat, srv);
275 XML_SetStartElementHandler(srv->expat, on_start);
276 XML_SetEndElementHandler(srv->expat, on_end);
277 XML_SetCharacterDataHandler(srv->expat, on_data);
280 debug("xmpp: encrypt -> stream");
281 srv->state = XMPP_SEND_STREAM;
284 if (srv->state == XMPP_RESTART) {
286 if (!(XML_ParserReset(srv->expat, NULL)))
287 error("Error resetting XML parser");
288 XML_SetUserData(srv->expat, srv);
289 XML_SetStartElementHandler(srv->expat, on_start);
290 XML_SetEndElementHandler(srv->expat, on_end);
291 XML_SetCharacterDataHandler(srv->expat, on_data);
294 debug("xmpp: restart -> stream");
295 srv->state = XMPP_SEND_STREAM;
300 static void xmpp_connect(xmpp_server_t *srv)
303 srv->net.recv = on_recv;
305 net_open(&srv->net, srv->host, srv->port);
308 if (!(srv->expat = XML_ParserCreate(NULL)))
309 error("Error creating XML parser");
310 XML_SetUserData(srv->expat, srv);
311 XML_SetStartElementHandler(srv->expat, on_start);
312 XML_SetEndElementHandler(srv->expat, on_end);
313 XML_SetCharacterDataHandler(srv->expat, on_data);
316 srv->state = XMPP_SEND_STREAM;
322 for (xmpp_server_t *cur = servers; cur; cur = cur->next) {
323 if (!match(cur->protocol, "xmpp"))
328 error("jid is required");
334 void xmpp_config(const char *group, const char *name, const char *key, const char *value)
337 xmpp_channel_t *chan;
339 if (match(group, "server")) {
340 srv = find_server(name, 1);
341 if (match(key, "protocol") &&
342 match(value, "xmpp"))
343 srv->protocol = get_string(value);
344 if (match(srv->protocol, "xmpp")) {
345 if (match(key, "connect"))
346 srv->connect = get_bool(value);
347 else if (match(key, "host"))
348 srv->host = get_string(value);
349 else if (match(key, "port"))
350 srv->port = get_number(value);
351 else if (match(key, "jid"))
352 srv->jid = get_string(value);
353 else if (match(key, "user"))
354 srv->user = get_string(value);
355 else if (match(key, "pass"))
356 srv->pass = get_string(value);
358 } else if (match(group, "channel")) {
359 chan = find_channel(name, 1);
360 if (match(key, "server") &&
361 find_server(value, 0))
362 chan->server = get_string(value);
364 if (match(key, "channel"))
365 chan->channel = get_string(value);
366 else if (match(key, "join"))
367 chan->join = get_bool(value);
372 void xmpp_send(const char *channel, const char *msg)
374 xmpp_channel_t *chan = find_channel(channel, 0);
377 xmpp_server_t *srv = find_server(chan->server, 0);
378 if (!srv || !srv->protocol)
382 "<message to='%s'><body>%s</body></message>",
383 "andy@pileus.org", msg);