source: protocols/jabber/iq.c @ a4effbf

Last change on this file since a4effbf was a4effbf, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-12T21:01:51Z

Replies to jabber:iq:time packets.

  • Property mode set to 100644
File size: 11.0 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 *c, *reply = NULL;
31        char *type, *s;
32        int st, pack = 1;
33       
34        type = xt_find_attr( node, "type" );
35       
36        if( !type )
37        {
38                hide_login_progress_error( gc, "Received IQ packet without type." );
39                signoff( gc );
40                return XT_ABORT;
41        }
42       
43        if( strcmp( type, "result" ) == 0 || strcmp( type, "error" ) == 0 )
44        {
45                struct jabber_cache_entry *entry;
46               
47                if( ( s = xt_find_attr( node, "id" ) ) == NULL )
48                {
49                        /* Silently ignore it, without an ID we don't know
50                           how to handle the packet, but it doesn't have
51                           to be a serious problem. */
52                        return XT_HANDLED;
53                }
54               
55                entry = g_hash_table_lookup( jd->node_cache, s );
56               
57                if( entry == NULL )
58                        serv_got_crap( gc, "WARNING: Received IQ %s packet with unknown/expired ID %s!", type, s );
59                else if( entry->func )
60                        return entry->func( gc, node, entry->node );
61        }
62        else if( strcmp( type, "get" ) == 0 )
63        {
64                if( !( c = xt_find_node( node->children, "query" ) ) ||
65                    !( s = xt_find_attr( c, "xmlns" ) ) )
66                {
67                        serv_got_crap( gc, "WARNING: Received incomplete IQ-get packet" );
68                        return XT_HANDLED;
69                }
70               
71                reply = xt_new_node( "query", NULL, NULL );
72                xt_add_attr( reply, "xmlns", s );
73               
74                /* Of course this is a very essential query to support. ;-) */
75                if( strcmp( s, "jabber:iq:version" ) == 0 )
76                {
77                        xt_add_child( reply, xt_new_node( "name", "BitlBee", NULL ) );
78                        xt_add_child( reply, xt_new_node( "version", BITLBEE_VERSION, NULL ) );
79                        xt_add_child( reply, xt_new_node( "os", ARCH, NULL ) );
80                }
81                else if( strcmp( s, "jabber:iq:time" ) == 0 )
82                {
83                        time_t time_ep;
84                        char buf[1024];
85                       
86                        buf[sizeof(buf)-1] = 0;
87                        time_ep = time( NULL );
88                       
89                        strftime( buf, sizeof( buf ) - 1, "%Y%m%dT%H:%M:%S", gmtime( &time_ep ) );
90                        xt_add_child( reply, xt_new_node( "utc", buf, NULL ) );
91                       
92                        strftime( buf, sizeof( buf ) - 1, "%Z", localtime( &time_ep ) );
93                        xt_add_child( reply, xt_new_node( "tz", buf, NULL ) );
94                }
95                else if( strcmp( s, "http://jabber.org/protocol/disco#info" ) == 0 )
96                {
97                        c = xt_new_node( "identity", NULL, NULL );
98                        xt_add_attr( c, "category", "client" );
99                        xt_add_attr( c, "type", "pc" );
100                        xt_add_attr( c, "name", "BitlBee" );
101                        xt_add_child( reply, c );
102                       
103                        c = xt_new_node( "feature", NULL, NULL );
104                        xt_add_attr( c, "var", "jabber:iq:version" );
105                        xt_add_child( reply, c );
106                       
107                        c = xt_new_node( "feature", NULL, NULL );
108                        xt_add_attr( c, "var", "jabber:iq:time" );
109                        xt_add_child( reply, c );
110                       
111                        c = xt_new_node( "feature", NULL, NULL );
112                        xt_add_attr( c, "var", "http://jabber.org/protocol/chatstates" );
113                        xt_add_child( reply, c );
114                       
115                        /* Later this can be useful to announce things like
116                           MUC support. */
117                }
118                else
119                {
120                        xt_free_node( reply );
121                        reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel" );
122                        pack = 0;
123                }
124        }
125        else if( strcmp( type, "set" ) == 0 )
126        {
127                xt_free_node( reply );
128                reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel" );
129                pack = 0;
130        }
131       
132        /* If we recognized the xmlns and managed to generate a reply,
133           finish and send it. */
134        if( reply )
135        {
136                /* Normally we still have to pack it into an iq-result
137                   packet, but for errors, for example, we don't. */
138                if( pack )
139                {
140                        reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), reply );
141                        if( ( s = xt_find_attr( node, "id" ) ) )
142                                xt_add_attr( reply, "id", s );
143                }
144               
145                st = jabber_write_packet( gc, reply );
146                xt_free_node( reply );
147                if( !st )
148                        return XT_ABORT;
149        }
150       
151        return XT_HANDLED;
152}
153
154static xt_status jabber_do_iq_auth( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
155static xt_status jabber_finish_iq_auth( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
156
157int jabber_init_iq_auth( struct gaim_connection *gc )
158{
159        struct jabber_data *jd = gc->proto_data;
160        struct xt_node *node;
161        int st;
162       
163        node = xt_new_node( "query", NULL, xt_new_node( "username", jd->username, NULL ) );
164        xt_add_attr( node, "xmlns", "jabber:iq:auth" );
165        node = jabber_make_packet( "iq", "get", NULL, node );
166       
167        jabber_cache_add( gc, node, jabber_do_iq_auth );
168        st = jabber_write_packet( gc, node );
169       
170        return st;
171}
172
173static xt_status jabber_do_iq_auth( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
174{
175        struct jabber_data *jd = gc->proto_data;
176        struct xt_node *reply, *query;
177        xt_status st;
178        char *s;
179       
180        query = xt_find_node( node->children, "query" );
181       
182        /* Time to authenticate ourselves! */
183        reply = xt_new_node( "query", NULL, NULL );
184        xt_add_attr( reply, "xmlns", "jabber:iq:auth" );
185        xt_add_child( reply, xt_new_node( "username", jd->username, NULL ) );
186        xt_add_child( reply, xt_new_node( "resource", set_getstr( &gc->acc->set, "resource" ), NULL ) );
187       
188        if( xt_find_node( query->children, "digest" ) && ( s = xt_find_attr( jd->xt->root, "id" ) ) )
189        {
190                /* We can do digest authentication, it seems, and of
191                   course we prefer that. */
192                SHA_CTX sha;
193                char hash_hex[40];
194                unsigned char hash[20];
195                int i;
196               
197                shaInit( &sha );
198                shaUpdate( &sha, (unsigned char*) s, strlen( s ) );
199                shaUpdate( &sha, (unsigned char*) gc->acc->pass, strlen( gc->acc->pass ) );
200                shaFinal( &sha, hash );
201               
202                for( i = 0; i < 20; i ++ )
203                        sprintf( hash_hex + i * 2, "%02x", hash[i] );
204               
205                xt_add_child( reply, xt_new_node( "digest", hash_hex, NULL ) );
206        }
207        else if( xt_find_node( query->children, "password" ) )
208        {
209                /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
210                xt_add_child( reply, xt_new_node( "password", gc->acc->pass, NULL ) );
211        }
212        else
213        {
214                xt_free_node( reply );
215               
216                hide_login_progress( gc, "Can't find suitable authentication method" );
217                signoff( gc );
218                return XT_ABORT;
219        }
220       
221        reply = jabber_make_packet( "iq", "set", NULL, reply );
222        jabber_cache_add( gc, reply, jabber_finish_iq_auth );
223        st = jabber_write_packet( gc, reply );
224       
225        return st ? XT_HANDLED : XT_ABORT;
226}
227
228static xt_status jabber_finish_iq_auth( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
229{
230        char *type = xt_find_attr( node, "type" );
231        struct jabber_data *jd = gc->proto_data;
232       
233        if( strcmp( type, "error" ) == 0 )
234        {
235                hide_login_progress( gc, "Authentication failure" );
236                signoff( gc );
237                return XT_ABORT;
238        }
239        else if( strcmp( type, "result" ) == 0 )
240        {
241                /* This happens when we just successfully authenticated the
242                   old (non-SASL) way. */
243                jd->flags |= JFLAG_AUTHENTICATED;
244                if( !jabber_get_roster( gc ) )
245                        return XT_ABORT;
246        }
247       
248        return XT_HANDLED;
249}
250
251xt_status jabber_pkt_bind_sess( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
252{
253        struct jabber_data *jd = gc->proto_data;
254        struct xt_node *c;
255        char *s;
256       
257        if( ( c = xt_find_node( node->children, "bind" ) ) )
258        {
259                c = xt_find_node( c->children, "jid" );
260                if( c && c->text_len && ( s = strchr( c->text, '/' ) ) &&
261                    strcmp( s + 1, set_getstr( &gc->acc->set, "resource" ) ) != 0 )
262                        serv_got_crap( gc, "Server changed session resource string to `%s'", s + 1 );
263               
264                jd->flags &= ~JFLAG_WAIT_BIND;
265        }
266        else
267        {
268                jd->flags &= ~JFLAG_WAIT_SESSION;
269        }
270       
271        if( ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
272        {
273                if( !jabber_get_roster( gc ) )
274                        return XT_ABORT;
275        }
276       
277        return XT_HANDLED;
278}
279
280static xt_status jabber_parse_roster( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
281
282int jabber_get_roster( struct gaim_connection *gc )
283{
284        struct xt_node *node;
285        int st;
286       
287        set_login_progress( gc, 1, "Authenticated, requesting buddy list" );
288       
289        node = xt_new_node( "query", NULL, NULL );
290        xt_add_attr( node, "xmlns", "jabber:iq:roster" );
291        node = jabber_make_packet( "iq", "get", NULL, node );
292       
293        jabber_cache_add( gc, node, jabber_parse_roster );
294        st = jabber_write_packet( gc, node );
295       
296        return st;
297}
298
299static xt_status jabber_parse_roster( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
300{
301        struct xt_node *query, *c;
302       
303        query = xt_find_node( node->children, "query" );
304       
305        c = query->children;
306        while( ( c = xt_find_node( c, "item" ) ) )
307        {
308                char *jid = xt_find_attr( c, "jid" );
309                char *name = xt_find_attr( c, "name" );
310                char *sub = xt_find_attr( c, "subscription" );
311               
312                if( jid && sub && ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) )
313                        add_buddy( gc, NULL, jid, name );
314               
315                c = c->next;
316        }
317       
318        account_online( gc );
319       
320        return XT_HANDLED;
321}
322
323int jabber_add_to_roster( struct gaim_connection *gc, char *handle, char *name )
324{
325        struct xt_node *node;
326        int st;
327       
328        /* Build the item entry */
329        node = xt_new_node( "item", NULL, NULL );
330        xt_add_attr( node, "jid", handle );
331        if( name )
332                xt_add_attr( node, "name", name );
333       
334        /* And pack it into a roster-add packet */
335        node = xt_new_node( "query", NULL, node );
336        xt_add_attr( node, "xmlns", "jabber:iq:roster" );
337        node = jabber_make_packet( "iq", "set", NULL, node );
338       
339        st = jabber_write_packet( gc, node );
340       
341        xt_free_node( node );
342        return st;
343}
344
345int jabber_remove_from_roster( struct gaim_connection *gc, char *handle )
346{
347        struct xt_node *node;
348        int st;
349       
350        /* Build the item entry */
351        node = xt_new_node( "item", NULL, NULL );
352        xt_add_attr( node, "jid", handle );
353        xt_add_attr( node, "subscription", "remove" );
354       
355        /* And pack it into a roster-add packet */
356        node = xt_new_node( "query", NULL, node );
357        xt_add_attr( node, "xmlns", "jabber:iq:roster" );
358        node = jabber_make_packet( "iq", "set", NULL, node );
359       
360        st = jabber_write_packet( gc, node );
361       
362        xt_free_node( node );
363        return st;
364}
Note: See TracBrowser for help on using the repository browser.