source: protocols/jabber/io.c @ deff040

Last change on this file since deff040 was 5bcf70a, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-09-21T18:44:34Z

Now also sending <presence type="unavailable"/> tag on disconnect, as
recommended by rfc3921/5.1.5.

  • Property mode set to 100644
File size: 7.4 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        st = write( jd->fd, jd->txq, jd->tx_len );
76       
77        if( st == jd->tx_len )
78        {
79                /* We wrote everything, clear the buffer. */
80                g_free( jd->txq );
81                jd->txq = NULL;
82                jd->tx_len = 0;
83               
84                return FALSE;
85        }
86        else if( st == 0 || ( st < 0 && !sockerr_again() ) )
87        {
88                hide_login_progress_error( gc, "Short write() to server" );
89                signoff( gc );
90                return FALSE;
91        }
92        else if( st > 0 )
93        {
94                char *s;
95               
96                s = g_memdup( jd->txq + st, jd->tx_len - st );
97                jd->tx_len -= st;
98                g_free( jd->txq );
99                jd->txq = s;
100               
101                return FALSE;
102        }
103        else
104        {
105                /* Just in case we had EINPROGRESS/EAGAIN: */
106               
107                return TRUE;
108        }
109}
110
111static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition cond )
112{
113        struct gaim_connection *gc = data;
114        struct jabber_data *jd = gc->proto_data;
115        char buf[512];
116        int st;
117       
118        st = read( fd, buf, sizeof( buf ) );
119       
120        if( st > 0 )
121        {
122                /* Parse. */
123                if( !xt_feed( jd->xt, buf, st ) )
124                {
125                        hide_login_progress_error( gc, "XML stream error" );
126                        signoff( gc );
127                        return FALSE;
128                }
129               
130                /* Execute all handlers. */
131                if( !xt_handle( jd->xt, NULL ) )
132                {
133                        /* Don't do anything, the handlers should have
134                           aborted the connection already... Or not? FIXME */
135                        return FALSE;
136                }
137               
138                /* Garbage collection. */
139                xt_cleanup( jd->xt, NULL );
140               
141                /* This is a bit hackish, unfortunately. Although xmltree
142                   has nifty event handler stuff, it only calls handlers
143                   when nodes are complete. Since the server should only
144                   send an opening <stream:stream> tag, we have to check
145                   this by hand. :-( */
146                if( !( jd->flags & JFLAG_STREAM_STARTED ) && jd->xt && jd->xt->root )
147                {
148                        if( g_strcasecmp( jd->xt->root->name, "stream:stream" ) == 0 )
149                        {
150                                jd->flags |= JFLAG_STREAM_STARTED;
151                                return jabber_start_auth( gc );
152                        }
153                        else
154                        {
155                                hide_login_progress_error( gc, "XML stream error" );
156                                signoff( gc );
157                                return FALSE;
158                        }
159                }
160        }
161        else if( st == 0 || ( st < 0 && !sockerr_again() ) )
162        {
163                hide_login_progress_error( gc, "Error while reading from server" );
164                signoff( gc );
165                return FALSE;
166        }
167       
168        /* EAGAIN/etc or a successful read. */
169        return TRUE;
170}
171
172gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond )
173{
174        struct gaim_connection *gc = data;
175       
176        if( source == -1 )
177        {
178                hide_login_progress( gc, "Could not connect to server" );
179                signoff( gc );
180                return FALSE;
181        }
182       
183        set_login_progress( gc, 1, "Connected to server, logging in" );
184       
185        return jabber_start_stream( gc );
186}
187
188static xt_status jabber_end_of_stream( struct xt_node *node, gpointer data )
189{
190        return XT_ABORT;
191}
192
193static xt_status jabber_pkt_misc( struct xt_node *node, gpointer data )
194{
195        printf( "Received unknown packet:\n" );
196        xt_print( node );
197       
198        return XT_HANDLED;
199}
200
201static const struct xt_handler_entry jabber_handlers[] = {
202        { "stream:stream",      "<root>",               jabber_end_of_stream },
203        { "iq",                 "stream:stream",        jabber_pkt_iq },
204        { "message",            "stream:stream",        jabber_pkt_message },
205        { "presence",           "stream:stream",        jabber_pkt_presence },
206        { NULL,                 "stream:stream",        jabber_pkt_misc },
207        { NULL,                 NULL,                   NULL }
208};
209
210gboolean jabber_start_stream( struct gaim_connection *gc )
211{
212        struct jabber_data *jd = gc->proto_data;
213        int st;
214        char *greet;
215       
216        /* We'll start our stream now, so prepare everything to receive one
217           from the server too. */
218        xt_free( jd->xt );      /* In case we're RE-starting. */
219        jd->xt = xt_new( gc );
220        jd->xt->handlers = (struct xt_handler_entry*) jabber_handlers;
221       
222        jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, gc );
223       
224        greet = g_strdup_printf( "<?xml version='1.0' ?>"
225                                 "<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
226                                  "xmlns:stream=\"http://etherx.jabber.org/streams\">", jd->server );
227        /* Add this when TLS and SASL are supported? */
228        // version=\"1.0\">"
229       
230        st = jabber_write( gc, greet, strlen( greet ) );
231       
232        g_free( greet );
233       
234        return st;
235}
236
237void jabber_end_stream( struct gaim_connection *gc )
238{
239        struct jabber_data *jd = gc->proto_data;
240       
241        /* Let's only do this if the queue is currently empty, otherwise it'd
242           take too long anyway. */
243        if( jd->tx_len == 0 )
244        {
245                char eos[] = "</stream:stream>";
246                struct xt_node *node;
247                int st;
248               
249                node = jabber_make_packet( "presence", "unavailable", NULL, NULL );
250                st = jabber_write_packet( gc, node );
251                xt_free_node( node );
252               
253                if( st )
254                        jabber_write( gc, eos, strlen( eos ) );
255        }
256}
Note: See TracBrowser for help on using the repository browser.