source: protocols/jabber/io.c @ ee5c355

Last change on this file since ee5c355 was 259edd4, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-12T17:48:58Z

Special message when the XMPP session is ended because of a concurrent
login, and now sending proper error responses to IQ packets we can't
handle.

  • Property mode set to 100644
File size: 16.0 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 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 "ssl_client.h"
26
27static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond );
28static gboolean jabber_write_queue( struct gaim_connection *gc );
29
30int jabber_write_packet( struct gaim_connection *gc, struct xt_node *node )
31{
32        char *buf;
33        int st;
34       
35        buf = xt_to_string( node );
36        st = jabber_write( gc, buf, strlen( buf ) );
37        g_free( buf );
38       
39        return st;
40}
41
42int jabber_write( struct gaim_connection *gc, char *buf, int len )
43{
44        struct jabber_data *jd = gc->proto_data;
45        gboolean ret;
46       
47        if( jd->tx_len == 0 )
48        {
49                /* If the queue is empty, allocate a new buffer. */
50                jd->tx_len = len;
51                jd->txq = g_memdup( buf, len );
52               
53                /* Try if we can write it immediately so we don't have to do
54                   it via the event handler. If not, add the handler. (In
55                   most cases it probably won't be necessary.) */
56                if( ( ret = jabber_write_queue( gc ) ) && jd->tx_len > 0 )
57                        jd->w_inpa = b_input_add( jd->fd, GAIM_INPUT_WRITE, jabber_write_callback, gc );
58        }
59        else
60        {
61                /* Just add it to the buffer if it's already filled. The
62                   event handler is already set. */
63                jd->txq = g_renew( char, jd->txq, jd->tx_len + len );
64                memcpy( jd->txq + jd->tx_len, buf, len );
65                jd->tx_len += len;
66               
67                /* The return value for write() doesn't necessarily mean
68                   that everything got sent, it mainly means that the
69                   connection (officially) still exists and can still
70                   be accessed without hitting SIGSEGV. IOW: */
71                ret = TRUE;
72        }
73       
74        return ret;
75}
76
77/* Splitting up in two separate functions: One to use as a callback and one
78   to use in the function above to escape from having to wait for the event
79   handler to call us, if possible.
80   
81   Two different functions are necessary because of the return values: The
82   callback should only return TRUE if the write was successful AND if the
83   buffer is not empty yet (ie. if the handler has to be called again when
84   the socket is ready for more data). */
85static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond )
86{
87        struct jabber_data *jd = ((struct gaim_connection *)data)->proto_data;
88       
89        return jd->fd != -1 &&
90               jabber_write_queue( data ) &&
91               jd->tx_len > 0;
92}
93
94static gboolean jabber_write_queue( struct gaim_connection *gc )
95{
96        struct jabber_data *jd = gc->proto_data;
97        int st;
98       
99        if( jd->ssl )
100                st = ssl_write( jd->ssl, jd->txq, jd->tx_len );
101        else
102                st = write( jd->fd, jd->txq, jd->tx_len );
103       
104//      if( st > 0 ) write( 1, jd->txq, st );
105       
106        if( st == jd->tx_len )
107        {
108                /* We wrote everything, clear the buffer. */
109                g_free( jd->txq );
110                jd->txq = NULL;
111                jd->tx_len = 0;
112               
113                return TRUE;
114        }
115        else if( st == 0 || ( st < 0 && !sockerr_again() ) )
116        {
117                /* Set fd to -1 to make sure we won't write to it anymore. */
118                closesocket( jd->fd );  /* Shouldn't be necessary after errors? */
119                jd->fd = -1;
120               
121                hide_login_progress_error( gc, "Short write() to server" );
122                signoff( gc );
123                return FALSE;
124        }
125        else if( st > 0 )
126        {
127                char *s;
128               
129                s = g_memdup( jd->txq + st, jd->tx_len - st );
130                jd->tx_len -= st;
131                g_free( jd->txq );
132                jd->txq = s;
133               
134                return TRUE;
135        }
136        else
137        {
138                /* Just in case we had EINPROGRESS/EAGAIN: */
139               
140                return TRUE;
141        }
142}
143
144static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition cond )
145{
146        struct gaim_connection *gc = data;
147        struct jabber_data *jd = gc->proto_data;
148        char buf[512];
149        int st;
150       
151        if( jd->fd == -1 )
152                return FALSE;
153       
154        if( jd->ssl )
155                st = ssl_read( jd->ssl, buf, sizeof( buf ) );
156        else
157                st = read( jd->fd, buf, sizeof( buf ) );
158       
159//      if( st > 0 ) write( 1, buf, st );
160       
161        if( st > 0 )
162        {
163                /* Parse. */
164                if( xt_feed( jd->xt, buf, st ) < 0 )
165                {
166                        hide_login_progress_error( gc, "XML stream error" );
167                        signoff( gc );
168                        return FALSE;
169                }
170               
171                /* Execute all handlers. */
172                if( !xt_handle( jd->xt, NULL, 1 ) )
173                {
174                        /* Don't do anything, the handlers should have
175                           aborted the connection already... Or not? FIXME */
176                        return FALSE;
177                }
178               
179                if( jd->flags & JFLAG_STREAM_RESTART )
180                {
181                        jd->flags &= ~JFLAG_STREAM_RESTART;
182                        jabber_start_stream( gc );
183                }
184               
185                /* Garbage collection. */
186                xt_cleanup( jd->xt, NULL, 1 );
187               
188                /* This is a bit hackish, unfortunately. Although xmltree
189                   has nifty event handler stuff, it only calls handlers
190                   when nodes are complete. Since the server should only
191                   send an opening <stream:stream> tag, we have to check
192                   this by hand. :-( */
193                if( !( jd->flags & JFLAG_STREAM_STARTED ) && jd->xt && jd->xt->root )
194                {
195                        if( g_strcasecmp( jd->xt->root->name, "stream:stream" ) == 0 )
196                        {
197                                jd->flags |= JFLAG_STREAM_STARTED;
198                               
199                                /* If there's no version attribute, assume
200                                   this is an old server that can't do SASL
201                                   authentication. */
202                                if( !sasl_supported( gc ) )
203                                {
204                                        /* If there's no version= tag, we suppose
205                                           this server does NOT implement: XMPP 1.0,
206                                           SASL and TLS. */
207                                        if( set_getbool( &gc->acc->set, "tls" ) )
208                                        {
209                                                hide_login_progress( gc, "TLS is turned on for this "
210                                                          "account, but is not supported by this server" );
211                                                signoff( gc );
212                                                return FALSE;
213                                        }
214                                        else
215                                        {
216                                                return jabber_init_iq_auth( gc );
217                                        }
218                                }
219                        }
220                        else
221                        {
222                                hide_login_progress( gc, "XML stream error" );
223                                signoff( gc );
224                                return FALSE;
225                        }
226                }
227        }
228        else if( st == 0 || ( st < 0 && !sockerr_again() ) )
229        {
230                closesocket( jd->fd );
231                jd->fd = -1;
232               
233                hide_login_progress_error( gc, "Error while reading from server" );
234                signoff( gc );
235                return FALSE;
236        }
237       
238        /* EAGAIN/etc or a successful read. */
239        return TRUE;
240}
241
242gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond )
243{
244        struct gaim_connection *gc = data;
245       
246        if( source == -1 )
247        {
248                hide_login_progress( gc, "Could not connect to server" );
249                signoff( gc );
250                return FALSE;
251        }
252       
253        set_login_progress( gc, 1, "Connected to server, logging in" );
254       
255        return jabber_start_stream( gc );
256}
257
258gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond )
259{
260        struct gaim_connection *gc = data;
261        struct jabber_data *jd = gc->proto_data;
262       
263        if( source == NULL )
264        {
265                /* The SSL connection will be cleaned up by the SSL lib
266                   already, set it to NULL here to prevent a double cleanup: */
267                jd->ssl = NULL;
268               
269                hide_login_progress( gc, "Could not connect to server" );
270                signoff( gc );
271                return FALSE;
272        }
273       
274        set_login_progress( gc, 1, "Connected to server, logging in" );
275       
276        return jabber_start_stream( gc );
277}
278
279static xt_status jabber_end_of_stream( struct xt_node *node, gpointer data )
280{
281        signoff( data );
282        return XT_ABORT;
283}
284
285static xt_status jabber_pkt_features( struct xt_node *node, gpointer data )
286{
287        struct gaim_connection *gc = data;
288        struct jabber_data *jd = gc->proto_data;
289        struct xt_node *c, *reply;
290        int trytls;
291       
292        trytls = g_strcasecmp( set_getstr( &gc->acc->set, "tls" ), "try" ) == 0;
293        c = xt_find_node( node->children, "starttls" );
294        if( c && !jd->ssl )
295        {
296                /* If the server advertises the STARTTLS feature and if we're
297                   not in a secure connection already: */
298               
299                c = xt_find_node( c->children, "required" );
300               
301                if( c && ( !trytls && !set_getbool( &gc->acc->set, "tls" ) ) )
302                {
303                        hide_login_progress( gc, "Server requires TLS connections, but TLS is turned off for this account" );
304                        signoff( gc );
305                       
306                        return XT_ABORT;
307                }
308               
309                /* Only run this if the tls setting is set to true or try: */
310                if( ( trytls || set_getbool( &gc->acc->set, "tls" ) ) )
311                {
312                        reply = xt_new_node( "starttls", NULL, NULL );
313                        xt_add_attr( reply, "xmlns", "urn:ietf:params:xml:ns:xmpp-tls" );
314                        if( !jabber_write_packet( gc, reply ) )
315                        {
316                                xt_free_node( reply );
317                                return XT_ABORT;
318                        }
319                        xt_free_node( reply );
320                       
321                        return XT_HANDLED;
322                }
323        }
324        else if( !c && !jd->ssl )
325        {
326                /* If the server does not advertise the STARTTLS feature and
327                   we're not in a secure connection already: (Servers have a
328                   habit of not advertising <starttls/> anymore when already
329                   using SSL/TLS. */
330               
331                if( !trytls && set_getbool( &gc->acc->set, "tls" ) )
332                {
333                        hide_login_progress( gc, "TLS is turned on for this account, but is not supported by this server" );
334                        signoff( gc );
335                       
336                        return XT_ABORT;
337                }
338        }
339       
340        /* This one used to be in jabber_handlers[], but it has to be done
341           from here to make sure the TLS session will be initialized
342           properly before we attempt SASL authentication. */
343        if( ( c = xt_find_node( node->children, "mechanisms" ) ) )
344        {
345                if( sasl_pkt_mechanisms( c, data ) == XT_ABORT )
346                        return XT_ABORT;
347        }
348        /* If the server *SEEMS* to support SASL authentication but doesn't
349           support it after all, we should try to do authentication the
350           other way. jabber.com doesn't seem to do SASL while it pretends
351           to be XMPP 1.0 compliant! */
352        else if( !( jd->flags & JFLAG_AUTHENTICATED ) && sasl_supported( gc ) )
353        {
354                if( !jabber_init_iq_auth( gc ) )
355                        return XT_ABORT;
356        }
357       
358        if( ( c = xt_find_node( node->children, "bind" ) ) )
359        {
360                reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &gc->acc->set, "resource" ), NULL ) );
361                xt_add_attr( reply, "xmlns", "urn:ietf:params:xml:ns:xmpp-bind" );
362                reply = jabber_make_packet( "iq", "set", NULL, reply );
363                jabber_cache_add( gc, reply, jabber_pkt_bind_sess );
364               
365                if( !jabber_write_packet( gc, reply ) )
366                        return XT_ABORT;
367               
368                jd->flags |= JFLAG_WAIT_BIND;
369        }
370       
371        if( ( c = xt_find_node( node->children, "session" ) ) )
372        {
373                reply = xt_new_node( "session", NULL, NULL );
374                xt_add_attr( reply, "xmlns", "urn:ietf:params:xml:ns:xmpp-session" );
375                reply = jabber_make_packet( "iq", "set", NULL, reply );
376                jabber_cache_add( gc, reply, jabber_pkt_bind_sess );
377               
378                if( !jabber_write_packet( gc, reply ) )
379                        return XT_ABORT;
380               
381                jd->flags |= JFLAG_WAIT_SESSION;
382        }
383       
384        /* This flag is already set if we authenticated via SASL, so now
385           we can resume the session in the new stream, if we don't have
386           to bind/initialize the session. */
387        if( jd->flags & JFLAG_AUTHENTICATED && ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
388        {
389                if( !jabber_get_roster( gc ) )
390                        return XT_ABORT;
391        }
392       
393        return XT_HANDLED;
394}
395
396static xt_status jabber_pkt_proceed_tls( struct xt_node *node, gpointer data )
397{
398        struct gaim_connection *gc = data;
399        struct jabber_data *jd = gc->proto_data;
400        char *xmlns;
401       
402        xmlns = xt_find_attr( node, "xmlns" );
403       
404        /* Just ignore it when it doesn't seem to be TLS-related (is that at
405           all possible??). */
406        if( !xmlns || strcmp( xmlns, "urn:ietf:params:xml:ns:xmpp-tls" ) != 0 )
407                return XT_HANDLED;
408       
409        /* We don't want event handlers to touch our TLS session while it's
410           still initializing! */
411        b_event_remove( jd->r_inpa );
412        if( jd->tx_len > 0 )
413        {
414                /* Actually the write queue should be empty here, but just
415                   to be sure... */
416                b_event_remove( jd->w_inpa );
417                g_free( jd->txq );
418                jd->txq = NULL;
419                jd->tx_len = 0;
420        }
421        jd->w_inpa = jd->r_inpa = 0;
422       
423        set_login_progress( gc, 1, "Converting stream to TLS" );
424       
425        jd->ssl = ssl_starttls( jd->fd, jabber_connected_ssl, gc );
426       
427        return XT_HANDLED;
428}
429
430static xt_status jabber_pkt_stream_error( struct xt_node *node, gpointer data )
431{
432        struct gaim_connection *gc = data;
433        struct xt_node *c;
434        char *s, *type = NULL, *text = NULL;
435       
436        for( c = node->children; c; c = c->next )
437        {
438                if( !( s = xt_find_attr( c, "xmlns" ) ) ||
439                    strcmp( s, "urn:ietf:params:xml:ns:xmpp-streams" ) != 0 )
440                        continue;
441               
442                if( strcmp( c->name, "text" ) != 0 )
443                {
444                        type = c->name;
445                }
446                /* Only use the text if it doesn't have an xml:lang attribute,
447                   if it's empty or if it's set to something English. */
448                else if( !( s = xt_find_attr( c, "xml:lang" ) ) ||
449                         !*s || strncmp( s, "en", 2 ) == 0 )
450                {
451                        text = c->text;
452                }
453        }
454       
455        /* Tssk... */
456        if( type == NULL )
457        {
458                hide_login_progress_error( gc, "Unknown stream error reported by server" );
459                signoff( gc );
460                return XT_ABORT;
461        }
462       
463        /* We know that this is a fatal error. If it's a "conflict" error, we
464           should turn off auto-reconnect to make sure we won't get some nasty
465           infinite loop! */
466        if( strcmp( type, "conflict" ) == 0 )
467        {
468                hide_login_progress( gc, "Account and resource used from a different location" );
469                gc->wants_to_die = TRUE;
470        }
471        else
472        {
473                s = g_strdup_printf( "Stream error: %s%s%s", type, text ? ": " : "", text ? text : "" );
474                hide_login_progress_error( gc, s );
475                g_free( s );
476        }
477       
478        signoff( gc );
479       
480        return XT_ABORT;
481}
482
483static xt_status jabber_pkt_misc( struct xt_node *node, gpointer data )
484{
485        printf( "Received unknown packet:\n" );
486        xt_print( node );
487       
488        return XT_HANDLED;
489}
490
491static const struct xt_handler_entry jabber_handlers[] = {
492        { "stream:stream",      "<root>",               jabber_end_of_stream },
493        { "message",            "stream:stream",        jabber_pkt_message },
494        { "presence",           "stream:stream",        jabber_pkt_presence },
495        { "iq",                 "stream:stream",        jabber_pkt_iq },
496        { "stream:features",    "stream:stream",        jabber_pkt_features },
497        { "stream:error",       "stream:stream",        jabber_pkt_stream_error },
498        { "proceed",            "stream:stream",        jabber_pkt_proceed_tls },
499        { "challenge",          "stream:stream",        sasl_pkt_challenge },
500        { "success",            "stream:stream",        sasl_pkt_result },
501        { "failure",            "stream:stream",        sasl_pkt_result },
502        { NULL,                 "stream:stream",        jabber_pkt_misc },
503        { NULL,                 NULL,                   NULL }
504};
505
506gboolean jabber_start_stream( struct gaim_connection *gc )
507{
508        struct jabber_data *jd = gc->proto_data;
509        int st;
510        char *greet;
511       
512        /* We'll start our stream now, so prepare everything to receive one
513           from the server too. */
514        xt_free( jd->xt );      /* In case we're RE-starting. */
515        jd->xt = xt_new( gc );
516        jd->xt->handlers = (struct xt_handler_entry*) jabber_handlers;
517       
518        if( jd->r_inpa <= 0 )
519                jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, gc );
520       
521        greet = g_strdup_printf( "<?xml version='1.0' ?>"
522                                 "<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
523                                  "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">", jd->server );
524       
525        st = jabber_write( gc, greet, strlen( greet ) );
526       
527        g_free( greet );
528       
529        return st;
530}
531
532void jabber_end_stream( struct gaim_connection *gc )
533{
534        struct jabber_data *jd = gc->proto_data;
535       
536        /* Let's only do this if the queue is currently empty, otherwise it'd
537           take too long anyway. */
538        if( jd->tx_len == 0 )
539        {
540                char eos[] = "</stream:stream>";
541                struct xt_node *node;
542                int st = 1;
543               
544                if( gc->flags & OPT_LOGGED_IN )
545                {
546                        node = jabber_make_packet( "presence", "unavailable", NULL, NULL );
547                        st = jabber_write_packet( gc, node );
548                        xt_free_node( node );
549                }
550               
551                if( st )
552                        jabber_write( gc, eos, strlen( eos ) );
553        }
554}
Note: See TracBrowser for help on using the repository browser.