source: protocols/jabber/io.c @ 5997488

Last change on this file since 5997488 was 5997488, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-09-22T12:04:35Z

Basic SASL (PLAIN only ATM) authentication code. Doesn't log in completely
yet.

  • Property mode set to 100644
File size: 7.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
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                if( jd->flags & JFLAG_STREAM_RESTART )
139                {
140                        jd->flags &= ~JFLAG_STREAM_RESTART;
141                        xt_reset( jd->xt );
142                        jabber_start_stream( gc );
143                }
144               
145                /* Garbage collection. */
146                xt_cleanup( jd->xt, NULL );
147               
148                /* This is a bit hackish, unfortunately. Although xmltree
149                   has nifty event handler stuff, it only calls handlers
150                   when nodes are complete. Since the server should only
151                   send an opening <stream:stream> tag, we have to check
152                   this by hand. :-( */
153                if( !( jd->flags & JFLAG_STREAM_STARTED ) && jd->xt && jd->xt->root )
154                {
155                        if( g_strcasecmp( jd->xt->root->name, "stream:stream" ) == 0 )
156                        {
157                                jd->flags |= JFLAG_STREAM_STARTED;
158                                return jabber_start_auth( gc );
159                        }
160                        else
161                        {
162                                hide_login_progress_error( gc, "XML stream error" );
163                                signoff( gc );
164                                return FALSE;
165                        }
166                }
167        }
168        else if( st == 0 || ( st < 0 && !sockerr_again() ) )
169        {
170                hide_login_progress_error( gc, "Error while reading from server" );
171                signoff( gc );
172                return FALSE;
173        }
174       
175        /* EAGAIN/etc or a successful read. */
176        return TRUE;
177}
178
179gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond )
180{
181        struct gaim_connection *gc = data;
182       
183        if( source == -1 )
184        {
185                hide_login_progress( gc, "Could not connect to server" );
186                signoff( gc );
187                return FALSE;
188        }
189       
190        set_login_progress( gc, 1, "Connected to server, logging in" );
191       
192        return jabber_start_stream( gc );
193}
194
195static xt_status jabber_end_of_stream( struct xt_node *node, gpointer data )
196{
197        return XT_ABORT;
198}
199
200static xt_status jabber_pkt_misc( struct xt_node *node, gpointer data )
201{
202        printf( "Received unknown packet:\n" );
203        xt_print( node );
204       
205        return XT_HANDLED;
206}
207
208static const struct xt_handler_entry jabber_handlers[] = {
209        { "stream:stream",      "<root>",               jabber_end_of_stream },
210        { "iq",                 "stream:stream",        jabber_pkt_iq },
211        { "message",            "stream:stream",        jabber_pkt_message },
212        { "presence",           "stream:stream",        jabber_pkt_presence },
213        { "mechanisms",         "stream:features",      sasl_pkt_mechanisms },
214        { "challenge",          "stream:stream",        sasl_pkt_challenge },
215        { "success",            "stream:stream",        sasl_pkt_result },
216        { "failure",            "stream:stream",        sasl_pkt_result },
217        { NULL,                 "stream:stream",        jabber_pkt_misc },
218        { NULL,                 NULL,                   NULL }
219};
220
221gboolean jabber_start_stream( struct gaim_connection *gc )
222{
223        struct jabber_data *jd = gc->proto_data;
224        int st;
225        char *greet;
226       
227        /* We'll start our stream now, so prepare everything to receive one
228           from the server too. */
229        xt_free( jd->xt );      /* In case we're RE-starting. */
230        jd->xt = xt_new( gc );
231        jd->xt->handlers = (struct xt_handler_entry*) jabber_handlers;
232       
233        jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, gc );
234       
235        greet = g_strdup_printf( "<?xml version='1.0' ?>"
236                                 "<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
237                                  "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">", jd->server );
238       
239        st = jabber_write( gc, greet, strlen( greet ) );
240       
241        g_free( greet );
242       
243        return st;
244}
245
246void jabber_end_stream( struct gaim_connection *gc )
247{
248        struct jabber_data *jd = gc->proto_data;
249       
250        /* Let's only do this if the queue is currently empty, otherwise it'd
251           take too long anyway. */
252        if( jd->tx_len == 0 )
253        {
254                char eos[] = "</stream:stream>";
255                struct xt_node *node;
256                int st;
257               
258                node = jabber_make_packet( "presence", "unavailable", NULL, NULL );
259                st = jabber_write_packet( gc, node );
260                xt_free_node( node );
261               
262                if( st )
263                        jabber_write( gc, eos, strlen( eos ) );
264        }
265}
Note: See TracBrowser for help on using the repository browser.