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

Last change on this file since 0e2d97f was 0e2d97f, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-01T09:40:55Z

Can now log in to the jabber.com server (which pretends to support XMPP 1.0
but does NOT (seem to) support SASL authentication).

  • 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        else
308        {
309                /* If the server *SEEMS* to support SASL authentication but
310                   doesn't support it after all, we should try to do
311                   authentication the other way. jabber.com doesn't seem to
312                   do SASL while it pretends to be XMPP 1.0 compliant! */
313                if( sasl_supported( gc ) )
314                        if( !jabber_start_iq_auth( gc ) )
315                                return XT_ABORT;
316        }
317       
318        if( ( c = xt_find_node( node->children, "bind" ) ) )
319        {
320                reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &gc->acc->set, "resource" ), NULL ) );
321                xt_add_attr( reply, "xmlns", "urn:ietf:params:xml:ns:xmpp-bind" );
322                reply = jabber_make_packet( "iq", "set", NULL, reply );
323                jabber_cache_packet( gc, reply );
324               
325                if( !jabber_write_packet( gc, reply ) )
326                        return XT_ABORT;
327               
328                jd->flags |= JFLAG_WAIT_BIND;
329        }
330       
331        if( ( c = xt_find_node( node->children, "session" ) ) )
332        {
333                reply = xt_new_node( "session", NULL, NULL );
334                xt_add_attr( reply, "xmlns", "urn:ietf:params:xml:ns:xmpp-session" );
335                reply = jabber_make_packet( "iq", "set", NULL, reply );
336                jabber_cache_packet( gc, reply );
337               
338                if( !jabber_write_packet( gc, reply ) )
339                        return XT_ABORT;
340               
341                jd->flags |= JFLAG_WAIT_SESSION;
342        }
343       
344        /* This flag is already set if we authenticated via SASL, so now
345           we can resume the session in the new stream, if we don't have
346           to bind/initialize the session. */
347        if( jd->flags & JFLAG_AUTHENTICATED && ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
348        {
349                if( !jabber_get_roster( gc ) || !jabber_get_privacy( gc ) )
350                        return XT_ABORT;
351        }
352       
353        return XT_HANDLED;
354}
355
356static xt_status jabber_pkt_proceed_tls( struct xt_node *node, gpointer data )
357{
358        struct gaim_connection *gc = data;
359        struct jabber_data *jd = gc->proto_data;
360        char *xmlns;
361       
362        xmlns = xt_find_attr( node, "xmlns" );
363       
364        /* Just ignore it when it doesn't seem to be TLS-related (is that at
365           all possible??). */
366        if( !xmlns || strcmp( xmlns, "urn:ietf:params:xml:ns:xmpp-tls" ) != 0 )
367                return XT_HANDLED;
368       
369        /* We don't want event handlers to touch our TLS session while it's
370           still initializing! */
371        b_event_remove( jd->r_inpa );
372        if( jd->tx_len > 0 )
373        {
374                /* Actually the write queue should be empty here, but just
375                   to be sure... */
376                b_event_remove( jd->w_inpa );
377                g_free( jd->txq );
378                jd->txq = NULL;
379                jd->tx_len = 0;
380        }
381        jd->w_inpa = jd->r_inpa = 0;
382       
383        set_login_progress( gc, 1, "Converting stream to TLS" );
384       
385        jd->ssl = ssl_starttls( jd->fd, jabber_connected_ssl, gc );
386       
387        return XT_HANDLED;
388}
389
390static xt_status jabber_pkt_misc( struct xt_node *node, gpointer data )
391{
392        printf( "Received unknown packet:\n" );
393        xt_print( node );
394       
395        return XT_HANDLED;
396}
397
398static const struct xt_handler_entry jabber_handlers[] = {
399        { "stream:stream",      "<root>",               jabber_end_of_stream },
400        { "message",            "stream:stream",        jabber_pkt_message },
401        { "presence",           "stream:stream",        jabber_pkt_presence },
402        { "iq",                 "stream:stream",        jabber_pkt_iq },
403        { "stream:features",    "stream:stream",        jabber_pkt_features },
404        { "proceed",            "stream:stream",        jabber_pkt_proceed_tls },
405        { "challenge",          "stream:stream",        sasl_pkt_challenge },
406        { "success",            "stream:stream",        sasl_pkt_result },
407        { "failure",            "stream:stream",        sasl_pkt_result },
408        { NULL,                 "stream:stream",        jabber_pkt_misc },
409        { NULL,                 NULL,                   NULL }
410};
411
412gboolean jabber_start_stream( struct gaim_connection *gc )
413{
414        struct jabber_data *jd = gc->proto_data;
415        int st;
416        char *greet;
417       
418        /* We'll start our stream now, so prepare everything to receive one
419           from the server too. */
420        xt_free( jd->xt );      /* In case we're RE-starting. */
421        jd->xt = xt_new( gc );
422        jd->xt->handlers = (struct xt_handler_entry*) jabber_handlers;
423       
424        if( jd->r_inpa <= 0 )
425                jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, gc );
426       
427        greet = g_strdup_printf( "<?xml version='1.0' ?>"
428                                 "<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
429                                  "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">", jd->server );
430       
431        st = jabber_write( gc, greet, strlen( greet ) );
432       
433        g_free( greet );
434       
435        return st;
436}
437
438void jabber_end_stream( struct gaim_connection *gc )
439{
440        struct jabber_data *jd = gc->proto_data;
441       
442        /* Let's only do this if the queue is currently empty, otherwise it'd
443           take too long anyway. */
444        if( jd->tx_len == 0 )
445        {
446                char eos[] = "</stream:stream>";
447                struct xt_node *node;
448                int st = 1;
449               
450                if( gc->flags & OPT_LOGGED_IN )
451                {
452                        node = jabber_make_packet( "presence", "unavailable", NULL, NULL );
453                        st = jabber_write_packet( gc, node );
454                        xt_free_node( node );
455                }
456               
457                if( st )
458                        jabber_write( gc, eos, strlen( eos ) );
459        }
460}
Note: See TracBrowser for help on using the repository browser.