source: protocols/jabber/io.c @ 8d74291

Last change on this file since 8d74291 was 8d74291, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-09-22T16:56:58Z

Fixed return value on incomplete write()s in write handler, protection
against write()ing to sockets that are closed already, hopefully sane
detection for SASL support, and only sending type=unavailable presence
tag to logged in sessions.

  • Property mode set to 100644
File size: 8.9 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
26static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond );
27
28int jabber_write_packet( struct gaim_connection *gc, struct xt_node *node )
29{
30        char *buf;
31        int st;
32       
33        buf = xt_to_string( node );
34        st = jabber_write( gc, buf, strlen( buf ) );
35        g_free( buf );
36       
37        return st;
38}
39
40int jabber_write( struct gaim_connection *gc, char *buf, int len )
41{
42        struct jabber_data *jd = gc->proto_data;
43       
44        if( jd->tx_len == 0 )
45        {
46                /* If the queue is empty, allocate a new buffer. */
47                jd->tx_len = len;
48                jd->txq = g_memdup( buf, len );
49               
50                /* Try if we can write it immediately so we don't have to do
51                   it via the event handler. If not, add the handler. (In
52                   most cases it probably won't be necessary.) */
53                if( jabber_write_callback( gc, jd->fd, GAIM_INPUT_WRITE ) )
54                        jd->w_inpa = b_input_add( jd->fd, GAIM_INPUT_WRITE, jabber_write_callback, gc );
55        }
56        else
57        {
58                /* Just add it to the buffer if it's already filled. The
59                   event handler is already set. */
60                jd->txq = g_renew( char, jd->txq, jd->tx_len + len );
61                memcpy( jd->txq + jd->tx_len, buf, len );
62                jd->tx_len += len;
63        }
64       
65        /* FIXME: write_callback could've generated a real error! */
66        return 1;
67}
68
69static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond )
70{
71        struct gaim_connection *gc = data;
72        struct jabber_data *jd = gc->proto_data;
73        int st;
74       
75        if( jd->fd == -1 )
76                return FALSE;
77       
78        st = write( jd->fd, jd->txq, jd->tx_len );
79       
80        if( st == jd->tx_len )
81        {
82                /* We wrote everything, clear the buffer. */
83                g_free( jd->txq );
84                jd->txq = NULL;
85                jd->tx_len = 0;
86               
87                return FALSE;
88        }
89        else if( st == 0 || ( st < 0 && !sockerr_again() ) )
90        {
91                /* Set fd to -1 to make sure we won't write to it anymore. */
92                closesocket( jd->fd );  /* Shouldn't be necessary after errors? */
93                jd->fd = -1;
94               
95                hide_login_progress_error( gc, "Short write() to server" );
96                signoff( gc );
97                return FALSE;
98        }
99        else if( st > 0 )
100        {
101                char *s;
102               
103                s = g_memdup( jd->txq + st, jd->tx_len - st );
104                jd->tx_len -= st;
105                g_free( jd->txq );
106                jd->txq = s;
107               
108                return TRUE;
109        }
110        else
111        {
112                /* Just in case we had EINPROGRESS/EAGAIN: */
113               
114                return TRUE;
115        }
116}
117
118static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition cond )
119{
120        struct gaim_connection *gc = data;
121        struct jabber_data *jd = gc->proto_data;
122        char buf[512];
123        int st;
124       
125        if( jd->fd == -1 )
126                return FALSE;
127       
128        st = read( fd, buf, sizeof( buf ) );
129       
130        if( st > 0 )
131        {
132                /* Parse. */
133                if( !xt_feed( jd->xt, buf, st ) )
134                {
135                        hide_login_progress_error( gc, "XML stream error" );
136                        signoff( gc );
137                        return FALSE;
138                }
139               
140                /* Execute all handlers. */
141                if( !xt_handle( jd->xt, NULL ) )
142                {
143                        /* Don't do anything, the handlers should have
144                           aborted the connection already... Or not? FIXME */
145                        return FALSE;
146                }
147               
148                if( jd->flags & JFLAG_STREAM_RESTART )
149                {
150                        jd->flags &= ~JFLAG_STREAM_RESTART;
151                        jabber_start_stream( gc );
152                }
153               
154                /* Garbage collection. */
155                xt_cleanup( jd->xt, NULL );
156               
157                /* This is a bit hackish, unfortunately. Although xmltree
158                   has nifty event handler stuff, it only calls handlers
159                   when nodes are complete. Since the server should only
160                   send an opening <stream:stream> tag, we have to check
161                   this by hand. :-( */
162                if( !( jd->flags & JFLAG_STREAM_STARTED ) && jd->xt && jd->xt->root )
163                {
164                        if( g_strcasecmp( jd->xt->root->name, "stream:stream" ) == 0 )
165                        {
166                                jd->flags |= JFLAG_STREAM_STARTED;
167                               
168                                /* If there's no version attribute, assume
169                                   this is an old server that can't do SASL
170                                   authentication. */
171                                if( !sasl_supported( gc ) )
172                                        return jabber_start_iq_auth( gc );
173                        }
174                        else
175                        {
176                                hide_login_progress_error( gc, "XML stream error" );
177                                signoff( gc );
178                                return FALSE;
179                        }
180                }
181        }
182        else if( st == 0 || ( st < 0 && !sockerr_again() ) )
183        {
184                closesocket( jd->fd );
185                jd->fd = -1;
186               
187                hide_login_progress_error( gc, "Error while reading from server" );
188                signoff( gc );
189                return FALSE;
190        }
191       
192        /* EAGAIN/etc or a successful read. */
193        return TRUE;
194}
195
196gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond )
197{
198        struct gaim_connection *gc = data;
199       
200        if( source == -1 )
201        {
202                hide_login_progress( gc, "Could not connect to server" );
203                signoff( gc );
204                return FALSE;
205        }
206       
207        set_login_progress( gc, 1, "Connected to server, logging in" );
208       
209        return jabber_start_stream( gc );
210}
211
212static xt_status jabber_end_of_stream( struct xt_node *node, gpointer data )
213{
214        return XT_ABORT;
215}
216
217static xt_status jabber_pkt_features( struct xt_node *node, gpointer data )
218{
219        struct gaim_connection *gc = data;
220        struct jabber_data *jd = gc->proto_data;
221        struct xt_node *c;
222       
223        c = xt_find_node( node->children, "starttls" );
224        if( c )
225        {
226                /*
227                jd->flags |= JFLAG_SUPPORTS_TLS;
228                if( xt_find_node( c->children, "required" ) )
229                        jd->flags |= JFLAG_REQUIRES_TLS;
230                */
231        }
232       
233        /* This flag is already set if we authenticated via SASL, so now
234           we can resume the session in the new stream. */
235        if( jd->flags & JFLAG_AUTHENTICATED )
236        {
237                if( !jabber_get_roster( gc ) )
238                        return XT_ABORT;
239        }
240       
241        return XT_HANDLED;
242}
243
244static xt_status jabber_pkt_misc( struct xt_node *node, gpointer data )
245{
246        printf( "Received unknown packet:\n" );
247        xt_print( node );
248       
249        return XT_HANDLED;
250}
251
252static const struct xt_handler_entry jabber_handlers[] = {
253        { "stream:stream",      "<root>",               jabber_end_of_stream },
254        { "message",            "stream:stream",        jabber_pkt_message },
255        { "presence",           "stream:stream",        jabber_pkt_presence },
256        { "iq",                 "stream:stream",        jabber_pkt_iq },
257        { "stream:features",    "stream:stream",        jabber_pkt_features },
258        { "mechanisms",         "stream:features",      sasl_pkt_mechanisms },
259        { "challenge",          "stream:stream",        sasl_pkt_challenge },
260        { "success",            "stream:stream",        sasl_pkt_result },
261        { "failure",            "stream:stream",        sasl_pkt_result },
262        { NULL,                 "stream:stream",        jabber_pkt_misc },
263        { NULL,                 NULL,                   NULL }
264};
265
266gboolean jabber_start_stream( struct gaim_connection *gc )
267{
268        struct jabber_data *jd = gc->proto_data;
269        int st;
270        char *greet;
271       
272        /* We'll start our stream now, so prepare everything to receive one
273           from the server too. */
274        xt_free( jd->xt );      /* In case we're RE-starting. */
275        jd->xt = xt_new( gc );
276        jd->xt->handlers = (struct xt_handler_entry*) jabber_handlers;
277       
278        if( jd->r_inpa <= 0 )
279                jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, gc );
280       
281        greet = g_strdup_printf( "<?xml version='1.0' ?>"
282                                 "<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
283                                  "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">", jd->server );
284       
285        st = jabber_write( gc, greet, strlen( greet ) );
286       
287        g_free( greet );
288       
289        return st;
290}
291
292void jabber_end_stream( struct gaim_connection *gc )
293{
294        struct jabber_data *jd = gc->proto_data;
295       
296        /* Let's only do this if the queue is currently empty, otherwise it'd
297           take too long anyway. */
298        if( jd->tx_len == 0 )
299        {
300                char eos[] = "</stream:stream>";
301                struct xt_node *node;
302                int st = 1;
303               
304                if( gc->flags & OPT_LOGGED_IN )
305                {
306                        node = jabber_make_packet( "presence", "unavailable", NULL, NULL );
307                        st = jabber_write_packet( gc, node );
308                        xt_free_node( node );
309                }
310               
311                if( st )
312                        jabber_write( gc, eos, strlen( eos ) );
313        }
314}
Note: See TracBrowser for help on using the repository browser.