source: protocols/jabber/iq.c @ 6266fca

Last change on this file since 6266fca was 995913b, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-02T17:46:57Z

Added some error handling for the (not very complete yet) privacy list code.

  • Property mode set to 100644
File size: 12.4 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Jabber module - IQ packets                                               *
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
26xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )
27{
28        struct gaim_connection *gc = data;
29        struct jabber_data *jd = gc->proto_data;
30        struct xt_node *query, *reply = NULL, *orig = NULL, *c;
31        char *s, *type, *xmlns;
32        int st;
33       
34        query = xt_find_node( node->children, "query" );
35        type = xt_find_attr( node, "type" );
36       
37        if( !type )
38                return XT_HANDLED;      /* Ignore it for now, don't know what's best... */
39       
40        xmlns = xt_find_attr( query, "xmlns" );
41       
42        if( ( s = xt_find_attr( node, "id" ) ) )
43                orig = jabber_packet_from_cache( gc, s );
44       
45        if( strcmp( type, "result" ) == 0 && xmlns && strcmp( xmlns, "jabber:iq:auth" ) == 0 )
46        {
47                /* Time to authenticate ourselves! */
48                reply = xt_new_node( "query", NULL, NULL );
49                xt_add_attr( reply, "xmlns", "jabber:iq:auth" );
50                xt_add_child( reply, xt_new_node( "username", jd->username, NULL ) );
51                xt_add_child( reply, xt_new_node( "resource", set_getstr( &gc->acc->set, "resource" ), NULL ) );
52               
53                if( xt_find_node( query->children, "digest" ) && ( s = xt_find_attr( jd->xt->root, "id" ) ) )
54                {
55                        /* We can do digest authentication, it seems, and of
56                           course we prefer that. */
57                        SHA_CTX sha;
58                        char hash_hex[40];
59                        unsigned char hash[20];
60                        int i;
61                       
62                        shaInit( &sha );
63                        shaUpdate( &sha, (unsigned char*) s, strlen( s ) );
64                        shaUpdate( &sha, (unsigned char*) gc->acc->pass, strlen( gc->acc->pass ) );
65                        shaFinal( &sha, hash );
66                       
67                        for( i = 0; i < 20; i ++ )
68                                sprintf( hash_hex + i * 2, "%02x", hash[i] );
69                       
70                        xt_add_child( reply, xt_new_node( "digest", hash_hex, NULL ) );
71                }
72                else if( xt_find_node( query->children, "password" ) )
73                {
74                        /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
75                        xt_add_child( reply, xt_new_node( "password", gc->acc->pass, NULL ) );
76                }
77                else
78                {
79                        xt_free_node( reply );
80                       
81                        hide_login_progress( gc, "Can't find suitable authentication method" );
82                        signoff( gc );
83                        return XT_ABORT;
84                }
85               
86                reply = jabber_make_packet( "iq", "set", NULL, reply );
87                jabber_cache_packet( gc, reply );
88                st = jabber_write_packet( gc, reply );
89               
90                return st ? XT_HANDLED : XT_ABORT;
91        }
92        if( strcmp( type, "result" ) == 0 && xmlns && strcmp( xmlns, "jabber:iq:roster" ) == 0 )
93        {
94                struct xt_node *node;
95               
96                node = query->children;
97                while( ( node = xt_find_node( node, "item" ) ) )
98                {
99                        char *jid = xt_find_attr( node, "jid" );
100                        char *name = xt_find_attr( node, "name" );
101                        char *sub = xt_find_attr( node, "subscription" );
102                       
103                        if( jid && sub && ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) )
104                                add_buddy( gc, NULL, jid, name );
105                       
106                        node = node->next;
107                }
108               
109                account_online( gc );
110        }
111        if( strcmp( type, "result" ) == 0 && xmlns && strcmp( xmlns, "jabber:iq:privacy" ) == 0 )
112        {
113                struct xt_node *node;
114               
115                /* When receiving a list of lists: */
116                if( ( node = xt_find_node( query->children, "active" ) ) )
117                {
118                        if( ( s = xt_find_attr( node, "name" ) ) )
119                        {
120                                set_t *set;
121                               
122                                g_free( jd->privacy_active );
123                                jd->privacy_active = g_strdup( s );
124                               
125                                /* Save it so the user can see it. */
126                                if( ( set = set_find( &gc->acc->set, "privacy_list" ) ) )
127                                {
128                                        g_free( set->value );
129                                        set->value = g_strdup( s );
130                                }
131                               
132                                if( !jabber_get_privacy( gc ) )
133                                        return XT_ABORT;
134                        }
135                }
136                /* When receiving an actual list: */
137                else if( ( node = xt_find_node( query->children, "list" ) ) )
138                {
139                        xt_free_node( jd->privacy_list );
140                        jd->privacy_list = xt_dup( node );
141                }
142                else if( query->children == NULL )
143                {
144                        /* What to do here if there is no privacy list defined yet... */
145                }
146        }
147        else if( strcmp( type, "result" ) == 0 && orig )
148        {
149                struct xt_node *c;
150               
151                if( !( jd->flags & JFLAG_AUTHENTICATED ) &&
152                    ( c = xt_find_node( orig->children, "query" ) ) &&
153                    ( c = xt_find_node( c->children, "username" ) ) &&
154                    c->text_len )
155                {
156                        /* This happens when we just successfully authenticated
157                           the old (non-SASL) way. */
158                        jd->flags |= JFLAG_AUTHENTICATED;
159                        if( !jabber_get_roster( gc ) || !jabber_get_privacy( gc ) )
160                                return XT_ABORT;
161                }
162                /* Tricky: Look for <bind> in the reply, because the server
163                   should confirm the chosen resource string there. For
164                   <session>, however, look in the cache, because the server
165                   will probably not include it in its reply. */
166                else if( ( c = xt_find_node( node->children, "bind" ) ) ||
167                         ( c = xt_find_node( orig->children, "session" ) ) )
168                {
169                        if( strcmp( c->name, "bind" ) == 0 )
170                        {
171                                c = xt_find_node( c->children, "jid" );
172                                if( c && c->text_len && ( s = strchr( c->text, '/' ) ) &&
173                                    strcmp( s + 1, set_getstr( &gc->acc->set, "resource" ) ) != 0 )
174                                        serv_got_crap( gc, "Server changed session resource string to `%s'", s + 1 );
175                               
176                                jd->flags &= ~JFLAG_WAIT_BIND;
177                        }
178                        else if( strcmp( c->name, "session" ) == 0 )
179                                jd->flags &= ~JFLAG_WAIT_SESSION;
180                       
181                        if( ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
182                        {
183                                if( !jabber_get_roster( gc ) || !jabber_get_privacy( gc ) )
184                                        return XT_ABORT;
185                        }
186                }
187                else if( ( c = xt_find_node( orig->children, "query" ) ) &&
188                         ( c = xt_find_node( c->children, "active" ) ) )
189                {
190                        /* We just successfully activated a (different)
191                           privacy list. Fetch it now. */
192                        g_free( jd->privacy_active );
193                        jd->privacy_active = g_strdup( xt_find_attr( c, "name" ) );
194                       
195                        if( !jabber_get_privacy( gc ) )
196                                return XT_ABORT;
197                }
198        }
199        else if( strcmp( type, "error" ) == 0 )
200        {
201                if( !( jd->flags & JFLAG_AUTHENTICATED ) &&
202                      orig &&
203                    ( c = xt_find_node( orig->children, "query" ) ) &&
204                    ( c = xt_find_node( c->children, "username" ) ) &&
205                    c->text_len )
206                {
207                        hide_login_progress( gc, "Authentication failure" );
208                        signoff( gc );
209                        return XT_ABORT;
210                }
211                else if( ( xmlns && strcmp( xmlns, "jabber:iq:privacy" ) == 0 ) ||
212                         ( orig &&
213                           ( c = xt_find_node( orig->children, "query" ) ) &&
214                           ( s = xt_find_attr( c, "xmlns" ) ) &&
215                           strcmp( s, "jabber:iq:privacy" ) == 0 ) )
216                {
217                        /* All errors related to privacy lists. */
218                        if( ( c = xt_find_node( node->children, "error" ) ) == NULL )
219                        {
220                                hide_login_progress_error( gc, "Received malformed error packet" );
221                                signoff( gc );
222                                return XT_ABORT;
223                        }
224                       
225                        if( xt_find_node( c->children, "item-not-found" ) )
226                        {
227                                serv_got_crap( gc, "Error while activating privacy list, maybe it doesn't exist" );
228                                /* Should I do anything else here? */
229                        }
230                        else if( xt_find_node( c->children, "feature-not-implemented" ) )
231                        {
232                                jd->flags |= JFLAG_PRIVACY_BROKEN;
233                                /* Probably there's no need to inform the user.
234                                   We can do that if the user ever tries to use
235                                   the block/allow commands. */
236                        }
237                }
238        }
239       
240        return XT_HANDLED;
241}
242
243int jabber_start_iq_auth( struct gaim_connection *gc )
244{
245        struct jabber_data *jd = gc->proto_data;
246        struct xt_node *node;
247        int st;
248       
249        node = xt_new_node( "query", NULL, xt_new_node( "username", jd->username, NULL ) );
250        xt_add_attr( node, "xmlns", "jabber:iq:auth" );
251        node = jabber_make_packet( "iq", "get", NULL, node );
252       
253        st = jabber_write_packet( gc, node );
254       
255        xt_free_node( node );
256        return st;
257}
258
259int jabber_get_roster( struct gaim_connection *gc )
260{
261        struct xt_node *node;
262        int st;
263       
264        set_login_progress( gc, 1, "Authenticated, requesting buddy list" );
265       
266        node = xt_new_node( "query", NULL, NULL );
267        xt_add_attr( node, "xmlns", "jabber:iq:roster" );
268        node = jabber_make_packet( "iq", "get", NULL, node );
269       
270        st = jabber_write_packet( gc, node );
271       
272        xt_free_node( node );
273        return st;
274}
275
276int jabber_add_to_roster( struct gaim_connection *gc, char *handle, char *name )
277{
278        struct xt_node *node;
279        int st;
280       
281        /* Build the item entry */
282        node = xt_new_node( "item", NULL, NULL );
283        xt_add_attr( node, "jid", handle );
284        if( name )
285                xt_add_attr( node, "name", name );
286       
287        /* And pack it into a roster-add packet */
288        node = xt_new_node( "query", NULL, node );
289        xt_add_attr( node, "xmlns", "jabber:iq:roster" );
290        node = jabber_make_packet( "iq", "set", NULL, node );
291       
292        st = jabber_write_packet( gc, node );
293       
294        xt_free_node( node );
295        return st;
296}
297
298int jabber_remove_from_roster( struct gaim_connection *gc, char *handle )
299{
300        struct xt_node *node;
301        int st;
302       
303        /* Build the item entry */
304        node = xt_new_node( "item", NULL, NULL );
305        xt_add_attr( node, "jid", handle );
306        xt_add_attr( node, "subscription", "remove" );
307       
308        /* And pack it into a roster-add packet */
309        node = xt_new_node( "query", NULL, node );
310        xt_add_attr( node, "xmlns", "jabber:iq:roster" );
311        node = jabber_make_packet( "iq", "set", NULL, node );
312       
313        st = jabber_write_packet( gc, node );
314       
315        xt_free_node( node );
316        return st;
317}
318
319/* Request the privacy list from the server. We need this, because every
320   time we remove/add something we have to send the whole new list to the
321   server again... If no privacy list is specified yet, this function will
322   first ask for the list of lists (XMPP supports multiple "privacy lists",
323   don't ask me why), later we can then fetch the list we want to use. */
324int jabber_get_privacy( struct gaim_connection *gc )
325{
326        struct jabber_data *jd = gc->proto_data;
327        struct xt_node *node = NULL;
328        char *name;
329        int st;
330       
331        if( jd->privacy_active )
332        {
333                /* If we know what is the active list right now, fetch it. */
334                node = xt_new_node( "list", NULL, NULL );
335                xt_add_attr( node, "name", jd->privacy_active );
336        }
337        /* Okay, we don't know yet. If the user set a specific list, we'll
338           activate that one. Otherwise, we should figure out which list is
339           currently active. */
340        else if( ( name = set_getstr( &gc->acc->set, "privacy_list" ) ) )
341        {
342                return jabber_set_privacy( gc, name );
343        }
344        /* else: sending this packet without a <list/> element will give
345           a list of available lists and information about the currently
346           active list. */
347       
348        node = xt_new_node( "query", NULL, node );
349        xt_add_attr( node, "xmlns", "jabber:iq:privacy" );
350        node = jabber_make_packet( "iq", "get", NULL, node );
351       
352        jabber_cache_packet( gc, node );
353        st = jabber_write_packet( gc, node );
354       
355        return st;
356}
357
358int jabber_set_privacy( struct gaim_connection *gc, char *name )
359{
360        struct xt_node *node;
361       
362        node = xt_new_node( "active", NULL, NULL );
363        xt_add_attr( node, "name", name );
364        node = xt_new_node( "query", NULL, node );
365        xt_add_attr( node, "xmlns", "jabber:iq:privacy" );
366       
367        node = jabber_make_packet( "iq", "set", NULL, node );
368        jabber_cache_packet( gc, node );
369       
370        return jabber_write_packet( gc, node );
371}
372
373char *set_eval_privacy_list( set_t *set, char *value )
374{
375        account_t *acc = set->data;
376        struct jabber_data *jd = acc->gc->proto_data;
377       
378        if( jd->flags & JFLAG_PRIVACY_BROKEN )
379        {
380                serv_got_crap( acc->gc, "Privacy lists not supported by this server" );
381                return NULL;
382        }
383       
384        /* If we're on-line, return NULL and let the server decide if the
385           chosen list is valid. If we're off-line, just accept it and we'll
386           see later (when we connect). */
387        if( acc->gc )
388                jabber_set_privacy( acc->gc, value );
389       
390        return acc->gc ? NULL : value;
391}
Note: See TracBrowser for help on using the repository browser.