source: protocols/jabber/io.c @ 0b9daac

Last change on this file since 0b9daac was 0b9daac, checked in by dequis <dx@…>, at 2015-02-20T23:16:08Z

Reorganize include files to avoid conflicts with other libs

  • Change all header includes to be relative to the project root
  • Remove -I${includedir} from bitlbee.pc Cflags
  • Install lib and protocols headers to their own directories. So now it is:

/usr/include/bitlbee/*.h
/usr/include/bitlbee/lib/*.h
/usr/include/bitlbee/protocols/*.h

This breaks backwards compatibility of third party plugins, but now
they don't have to do ambiguous includes like #include <proxy.h>

This also fixes trac ticket 1170 - conflicts when libproxy and liboauth
are installed at the same time bitlbee is built, which the macports
project ran into several times.

  • Property mode set to 100644
File size: 16.8 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Jabber module - I/O stuff (plain, SSL), queues, etc                      *
5*                                                                           *
6*  Copyright 2006-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24#include "jabber.h"
25#include "lib/ssl_client.h"
26
27static gboolean jabber_write_callback(gpointer data, gint fd, b_input_condition cond);
28static gboolean jabber_write_queue(struct im_connection *ic);
29
30int jabber_write_packet(struct im_connection *ic, struct xt_node *node)
31{
32        char *buf;
33        int st;
34
35        buf = xt_to_string(node);
36        st = jabber_write(ic, buf, strlen(buf));
37        g_free(buf);
38
39        return st;
40}
41
42int jabber_write(struct im_connection *ic, char *buf, int len)
43{
44        struct jabber_data *jd = ic->proto_data;
45        gboolean ret;
46
47        if (jd->flags & JFLAG_XMLCONSOLE && !(ic->flags & OPT_LOGGING_OUT)) {
48                char *msg, *s;
49
50                msg = g_strdup_printf("TX: %s", buf);
51                /* Don't include auth info in XML logs. */
52                if (strncmp(msg, "TX: <auth ", 10) == 0 && (s = strchr(msg, '>'))) {
53                        s++;
54                        while (*s && *s != '<') {
55                                *(s++) = '*';
56                        }
57                }
58                imcb_buddy_msg(ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0);
59                g_free(msg);
60        }
61
62        if (jd->tx_len == 0) {
63                /* If the queue is empty, allocate a new buffer. */
64                jd->tx_len = len;
65                jd->txq = g_memdup(buf, len);
66
67                /* Try if we can write it immediately so we don't have to do
68                   it via the event handler. If not, add the handler. (In
69                   most cases it probably won't be necessary.) */
70                if ((ret = jabber_write_queue(ic)) && jd->tx_len > 0) {
71                        jd->w_inpa = b_input_add(jd->fd, B_EV_IO_WRITE, jabber_write_callback, ic);
72                }
73        } else {
74                /* Just add it to the buffer if it's already filled. The
75                   event handler is already set. */
76                jd->txq = g_renew(char, jd->txq, jd->tx_len + len);
77                memcpy(jd->txq + jd->tx_len, buf, len);
78                jd->tx_len += len;
79
80                /* The return value for write() doesn't necessarily mean
81                   that everything got sent, it mainly means that the
82                   connection (officially) still exists and can still
83                   be accessed without hitting SIGSEGV. IOW: */
84                ret = TRUE;
85        }
86
87        return ret;
88}
89
90/* Splitting up in two separate functions: One to use as a callback and one
91   to use in the function above to escape from having to wait for the event
92   handler to call us, if possible.
93
94   Two different functions are necessary because of the return values: The
95   callback should only return TRUE if the write was successful AND if the
96   buffer is not empty yet (ie. if the handler has to be called again when
97   the socket is ready for more data). */
98static gboolean jabber_write_callback(gpointer data, gint fd, b_input_condition cond)
99{
100        struct jabber_data *jd = ((struct im_connection *) data)->proto_data;
101
102        return jd->fd != -1 &&
103               jabber_write_queue(data) &&
104               jd->tx_len > 0;
105}
106
107static gboolean jabber_write_queue(struct im_connection *ic)
108{
109        struct jabber_data *jd = ic->proto_data;
110        int st;
111
112        if (jd->ssl) {
113                st = ssl_write(jd->ssl, jd->txq, jd->tx_len);
114        } else {
115                st = write(jd->fd, jd->txq, jd->tx_len);
116        }
117
118        if (st == jd->tx_len) {
119                /* We wrote everything, clear the buffer. */
120                g_free(jd->txq);
121                jd->txq = NULL;
122                jd->tx_len = 0;
123
124                return TRUE;
125        } else if (st == 0 || (st < 0 && !ssl_sockerr_again(jd->ssl))) {
126                /* Set fd to -1 to make sure we won't write to it anymore. */
127                closesocket(jd->fd);    /* Shouldn't be necessary after errors? */
128                jd->fd = -1;
129
130                imcb_error(ic, "Short write() to server");
131                imc_logout(ic, TRUE);
132                return FALSE;
133        } else if (st > 0) {
134                char *s;
135
136                s = g_memdup(jd->txq + st, jd->tx_len - st);
137                jd->tx_len -= st;
138                g_free(jd->txq);
139                jd->txq = s;
140
141                return TRUE;
142        } else {
143                /* Just in case we had EINPROGRESS/EAGAIN: */
144
145                return TRUE;
146        }
147}
148
149static gboolean jabber_read_callback(gpointer data, gint fd, b_input_condition cond)
150{
151        struct im_connection *ic = data;
152        struct jabber_data *jd = ic->proto_data;
153        char buf[512];
154        int st;
155
156        if (jd->fd == -1) {
157                return FALSE;
158        }
159
160        if (jd->ssl) {
161                st = ssl_read(jd->ssl, buf, sizeof(buf));
162        } else {
163                st = read(jd->fd, buf, sizeof(buf));
164        }
165
166        if (st > 0) {
167                /* Parse. */
168                if (xt_feed(jd->xt, buf, st) < 0) {
169                        imcb_error(ic, "XML stream error");
170                        imc_logout(ic, TRUE);
171                        return FALSE;
172                }
173
174                /* Execute all handlers. */
175                if (!xt_handle(jd->xt, NULL, 1)) {
176                        /* Don't do anything, the handlers should have
177                           aborted the connection already. */
178                        return FALSE;
179                }
180
181                if (jd->flags & JFLAG_STREAM_RESTART) {
182                        jd->flags &= ~JFLAG_STREAM_RESTART;
183                        jabber_start_stream(ic);
184                }
185
186                /* Garbage collection. */
187                xt_cleanup(jd->xt, NULL, 1);
188
189                /* This is a bit hackish, unfortunately. Although xmltree
190                   has nifty event handler stuff, it only calls handlers
191                   when nodes are complete. Since the server should only
192                   send an opening <stream:stream> tag, we have to check
193                   this by hand. :-( */
194                if (!(jd->flags & JFLAG_STREAM_STARTED) && jd->xt && jd->xt->root) {
195                        if (g_strcasecmp(jd->xt->root->name, "stream:stream") == 0) {
196                                jd->flags |= JFLAG_STREAM_STARTED;
197
198                                /* If there's no version attribute, assume
199                                   this is an old server that can't do SASL
200                                   authentication. */
201                                if (!set_getbool(&ic->acc->set, "sasl") || !sasl_supported(ic)) {
202                                        /* If there's no version= tag, we suppose
203                                           this server does NOT implement: XMPP 1.0,
204                                           SASL and TLS. */
205                                        if (set_getbool(&ic->acc->set, "tls")) {
206                                                imcb_error(ic, "TLS is turned on for this "
207                                                           "account, but is not supported by this server");
208                                                imc_logout(ic, FALSE);
209                                                return FALSE;
210                                        } else {
211                                                return jabber_init_iq_auth(ic);
212                                        }
213                                }
214                        } else {
215                                imcb_error(ic, "XML stream error");
216                                imc_logout(ic, TRUE);
217                                return FALSE;
218                        }
219                }
220        } else if (st == 0 || (st < 0 && !ssl_sockerr_again(jd->ssl))) {
221                closesocket(jd->fd);
222                jd->fd = -1;
223
224                imcb_error(ic, "Error while reading from server");
225                imc_logout(ic, TRUE);
226                return FALSE;
227        }
228
229        if (ssl_pending(jd->ssl)) {
230                /* OpenSSL empties the TCP buffers completely but may keep some
231                   data in its internap buffers. select() won't see that, but
232                   ssl_pending() does. */
233                return jabber_read_callback(data, fd, cond);
234        } else {
235                return TRUE;
236        }
237}
238
239gboolean jabber_connected_plain(gpointer data, gint source, b_input_condition cond)
240{
241        struct im_connection *ic = data;
242
243        if (g_slist_find(jabber_connections, ic) == NULL) {
244                return FALSE;
245        }
246
247        if (source == -1) {
248                imcb_error(ic, "Could not connect to server");
249                imc_logout(ic, TRUE);
250                return FALSE;
251        }
252
253        imcb_log(ic, "Connected to server, logging in");
254
255        return jabber_start_stream(ic);
256}
257
258gboolean jabber_connected_ssl(gpointer data, int returncode, void *source, b_input_condition cond)
259{
260        struct im_connection *ic = data;
261        struct jabber_data *jd;
262
263        if (g_slist_find(jabber_connections, ic) == NULL) {
264                return FALSE;
265        }
266
267        jd = ic->proto_data;
268
269        if (source == NULL) {
270                /* The SSL connection will be cleaned up by the SSL lib
271                   already, set it to NULL here to prevent a double cleanup: */
272                jd->ssl = NULL;
273
274                if (returncode != 0) {
275                        char *err = ssl_verify_strerror(returncode);
276                        imcb_error(ic, "Certificate verification problem 0x%x: %s",
277                                   returncode, err ? err : "Unknown");
278                        g_free(err);
279                        imc_logout(ic, FALSE);
280                } else {
281                        imcb_error(ic, "Could not connect to server");
282                        imc_logout(ic, TRUE);
283                }
284
285                return FALSE;
286        }
287
288        imcb_log(ic, "Connected to server, logging in");
289
290        return jabber_start_stream(ic);
291}
292
293static xt_status jabber_end_of_stream(struct xt_node *node, gpointer data)
294{
295        imc_logout(data, TRUE);
296        return XT_ABORT;
297}
298
299static xt_status jabber_pkt_features(struct xt_node *node, gpointer data)
300{
301        struct im_connection *ic = data;
302        struct jabber_data *jd = ic->proto_data;
303        struct xt_node *c, *reply;
304        int trytls;
305
306        trytls = g_strcasecmp(set_getstr(&ic->acc->set, "tls"), "try") == 0;
307        c = xt_find_node(node->children, "starttls");
308        if (c && !jd->ssl) {
309                /* If the server advertises the STARTTLS feature and if we're
310                   not in a secure connection already: */
311
312                c = xt_find_node(c->children, "required");
313
314                if (c && (!trytls && !set_getbool(&ic->acc->set, "tls"))) {
315                        imcb_error(ic, "Server requires TLS connections, but TLS is turned off for this account");
316                        imc_logout(ic, FALSE);
317
318                        return XT_ABORT;
319                }
320
321                /* Only run this if the tls setting is set to true or try: */
322                if ((trytls || set_getbool(&ic->acc->set, "tls"))) {
323                        reply = xt_new_node("starttls", NULL, NULL);
324                        xt_add_attr(reply, "xmlns", XMLNS_TLS);
325                        if (!jabber_write_packet(ic, reply)) {
326                                xt_free_node(reply);
327                                return XT_ABORT;
328                        }
329                        xt_free_node(reply);
330
331                        return XT_HANDLED;
332                }
333        } else if (!c && !jd->ssl) {
334                /* If the server does not advertise the STARTTLS feature and
335                   we're not in a secure connection already: (Servers have a
336                   habit of not advertising <starttls/> anymore when already
337                   using SSL/TLS. */
338
339                if (!trytls && set_getbool(&ic->acc->set, "tls")) {
340                        imcb_error(ic, "TLS is turned on for this account, but is not supported by this server");
341                        imc_logout(ic, FALSE);
342
343                        return XT_ABORT;
344                }
345        }
346
347        /* This one used to be in jabber_handlers[], but it has to be done
348           from here to make sure the TLS session will be initialized
349           properly before we attempt SASL authentication. */
350        if ((c = xt_find_node(node->children, "mechanisms"))) {
351                if (sasl_pkt_mechanisms(c, data) == XT_ABORT) {
352                        return XT_ABORT;
353                }
354        }
355        /* If the server *SEEMS* to support SASL authentication but doesn't
356           support it after all, we should try to do authentication the
357           other way. jabber.com doesn't seem to do SASL while it pretends
358           to be XMPP 1.0 compliant! */
359        else if (!(jd->flags & JFLAG_AUTHENTICATED) && set_getbool(&ic->acc->set, "sasl") && sasl_supported(ic)) {
360                if (!jabber_init_iq_auth(ic)) {
361                        return XT_ABORT;
362                }
363        }
364
365        if ((c = xt_find_node(node->children, "bind"))) {
366                jd->flags |= JFLAG_WANT_BIND;
367        }
368
369        if ((c = xt_find_node(node->children, "session"))) {
370                jd->flags |= JFLAG_WANT_SESSION;
371        }
372
373        if (jd->flags & JFLAG_AUTHENTICATED) {
374                return jabber_pkt_bind_sess(ic, NULL, NULL);
375        }
376
377        return XT_HANDLED;
378}
379
380static xt_status jabber_pkt_proceed_tls(struct xt_node *node, gpointer data)
381{
382        struct im_connection *ic = data;
383        struct jabber_data *jd = ic->proto_data;
384        char *xmlns, *tlsname;
385
386        xmlns = xt_find_attr(node, "xmlns");
387
388        /* Just ignore it when it doesn't seem to be TLS-related (is that at
389           all possible??). */
390        if (!xmlns || strcmp(xmlns, XMLNS_TLS) != 0) {
391                return XT_HANDLED;
392        }
393
394        /* We don't want event handlers to touch our TLS session while it's
395           still initializing! */
396        b_event_remove(jd->r_inpa);
397        if (jd->tx_len > 0) {
398                /* Actually the write queue should be empty here, but just
399                   to be sure... */
400                b_event_remove(jd->w_inpa);
401                g_free(jd->txq);
402                jd->txq = NULL;
403                jd->tx_len = 0;
404        }
405        jd->w_inpa = jd->r_inpa = 0;
406
407        imcb_log(ic, "Converting stream to TLS");
408
409        jd->flags |= JFLAG_STARTTLS_DONE;
410
411        /* If the user specified a server for the account, use this server as the
412         * hostname in the certificate verification. Else we use the domain from
413         * the username. */
414        if (ic->acc->server && *ic->acc->server) {
415                tlsname = ic->acc->server;
416        } else {
417                tlsname = jd->server;
418        }
419
420        jd->ssl = ssl_starttls(jd->fd, tlsname, set_getbool(&ic->acc->set, "tls_verify"),
421                               jabber_connected_ssl, ic);
422
423        return XT_HANDLED;
424}
425
426static xt_status jabber_pkt_stream_error(struct xt_node *node, gpointer data)
427{
428        struct im_connection *ic = data;
429        struct jabber_data *jd = ic->proto_data;
430        int allow_reconnect = TRUE;
431        struct jabber_error *err;
432        struct xt_node *host;
433
434        if (!(ic->flags & OPT_LOGGED_IN) &&
435            (host = xt_find_node(node->children, "see-other-host")) &&
436            host->text) {
437                char *s;
438                int port = set_getint(&ic->acc->set, "port");
439
440                /* Let's try to obey this request, if we're not logged
441                   in yet (i.e. not have too much state yet). */
442                if (jd->ssl) {
443                        ssl_disconnect(jd->ssl);
444                }
445                closesocket(jd->fd);
446                b_event_remove(jd->r_inpa);
447                b_event_remove(jd->w_inpa);
448
449                jd->ssl = NULL;
450                jd->r_inpa = jd->w_inpa = 0;
451                jd->flags &= JFLAG_XMLCONSOLE;
452
453                s = strchr(host->text, ':');
454                if (s != NULL) {
455                        sscanf(s + 1, "%d", &port);
456                }
457
458                imcb_log(ic, "Redirected to %s", host->text);
459                jd->fd = proxy_connect(host->text, port, jabber_connected_plain, ic);
460
461                return XT_ABORT;
462        }
463
464        err = jabber_error_parse(node, XMLNS_STREAM_ERROR);
465
466        /* Tssk... */
467        if (err->code == NULL) {
468                imcb_error(ic, "Unknown stream error reported by server");
469                imc_logout(ic, allow_reconnect);
470                jabber_error_free(err);
471                return XT_ABORT;
472        }
473
474        /* We know that this is a fatal error. If it's a "conflict" error, we
475           should turn off auto-reconnect to make sure we won't get some nasty
476           infinite loop! */
477        if (strcmp(err->code, "conflict") == 0) {
478                imcb_error(ic, "Account and resource used from a different location");
479                allow_reconnect = FALSE;
480        } else if (strcmp(err->code, "not-authorized") == 0) {
481                imcb_error(ic, "Not authorized");
482                allow_reconnect = FALSE;
483        } else {
484                imcb_error(ic, "Stream error: %s%s%s", err->code, err->text ? ": " : "",
485                           err->text ? err->text : "");
486        }
487
488        jabber_error_free(err);
489        imc_logout(ic, allow_reconnect);
490
491        return XT_ABORT;
492}
493
494static xt_status jabber_xmlconsole(struct xt_node *node, gpointer data)
495{
496        struct im_connection *ic = data;
497        struct jabber_data *jd = ic->proto_data;
498
499        if (jd->flags & JFLAG_XMLCONSOLE) {
500                char *msg, *pkt;
501
502                pkt = xt_to_string(node);
503                msg = g_strdup_printf("RX: %s", pkt);
504                imcb_buddy_msg(ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0);
505                g_free(msg);
506                g_free(pkt);
507        }
508
509        return XT_NEXT;
510}
511
512static const struct xt_handler_entry jabber_handlers[] = {
513        { NULL,                 "stream:stream",        jabber_xmlconsole },
514        { "stream:stream",      "<root>",               jabber_end_of_stream },
515        { "message",            "stream:stream",        jabber_pkt_message },
516        { "presence",           "stream:stream",        jabber_pkt_presence },
517        { "iq",                 "stream:stream",        jabber_pkt_iq },
518        { "stream:features",    "stream:stream",        jabber_pkt_features },
519        { "stream:error",       "stream:stream",        jabber_pkt_stream_error },
520        { "proceed",            "stream:stream",        jabber_pkt_proceed_tls },
521        { "challenge",          "stream:stream",        sasl_pkt_challenge },
522        { "success",            "stream:stream",        sasl_pkt_result },
523        { "failure",            "stream:stream",        sasl_pkt_result },
524        { NULL,                 NULL,                   NULL }
525};
526
527gboolean jabber_start_stream(struct im_connection *ic)
528{
529        struct jabber_data *jd = ic->proto_data;
530        int st;
531        char *greet;
532
533        /* We'll start our stream now, so prepare everything to receive one
534           from the server too. */
535        xt_free(jd->xt);        /* In case we're RE-starting. */
536        jd->xt = xt_new(jabber_handlers, ic);
537
538        if (jd->r_inpa <= 0) {
539                jd->r_inpa = b_input_add(jd->fd, B_EV_IO_READ, jabber_read_callback, ic);
540        }
541
542        greet = g_strdup_printf("%s<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
543                                "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">",
544                                (jd->flags & JFLAG_STARTTLS_DONE) ? "" : "<?xml version='1.0' ?>",
545                                jd->server);
546
547        st = jabber_write(ic, greet, strlen(greet));
548
549        g_free(greet);
550
551        return st;
552}
553
554void jabber_end_stream(struct im_connection *ic)
555{
556        struct jabber_data *jd = ic->proto_data;
557
558        /* Let's only do this if the queue is currently empty, otherwise it'd
559           take too long anyway. */
560        if (jd->tx_len == 0) {
561                char eos[] = "</stream:stream>";
562                struct xt_node *node;
563                int st = 1;
564
565                if (ic->flags & OPT_LOGGED_IN) {
566                        node = jabber_make_packet("presence", "unavailable", NULL, NULL);
567                        st = jabber_write_packet(ic, node);
568                        xt_free_node(node);
569                }
570
571                if (st) {
572                        jabber_write(ic, eos, strlen(eos));
573                }
574        }
575}
Note: See TracBrowser for help on using the repository browser.