source: protocols/jabber/io.c @ 88591fd

Last change on this file since 88591fd was 88591fd, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-01T16:15:46Z

Better fix for servers that report to comply with XMPP 1.0 but don't offer
SASL authentication options. Previous fix tried to do IQ authentication
even after successful SASL authentications.

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