source: protocols/jabber/io.c @ eab2ac4

Last change on this file since eab2ac4 was e101506, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-09-24T17:22:08Z

Better handling of user tls setting.

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