source: protocols/jabber/iq.c @ dfa41a4

Last change on this file since dfa41a4 was dfa41a4, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-20T19:12:14Z

Now all IQ packets get an ID and cached packets get a "special" ID. This
makes it easier to find out if an event handler has to be called for a
reply packet.

  • Property mode set to 100644
File size: 16.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 *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                    strncmp( s, JABBER_CACHED_ID, strlen( JABBER_CACHED_ID ) ) != 0 )
49                {
50                        /* Silently ignore it, without an ID (or an non-cache
51                           ID) we don't know how to handle the packet and we
52                           probably don't have to. */
53                        return XT_HANDLED;
54                }
55               
56                entry = g_hash_table_lookup( jd->node_cache, s );
57               
58                if( entry == NULL )
59                        serv_got_crap( gc, "WARNING: Received IQ %s packet with unknown/expired ID %s!", type, s );
60                else if( entry->func )
61                        return entry->func( gc, node, entry->node );
62        }
63        else if( strcmp( type, "get" ) == 0 )
64        {
65                if( !( c = xt_find_node( node->children, "query" ) ) ||
66                    !( s = xt_find_attr( c, "xmlns" ) ) )
67                {
68                        serv_got_crap( gc, "WARNING: Received incomplete IQ-%s packet", type );
69                        return XT_HANDLED;
70                }
71               
72                reply = xt_new_node( "query", NULL, NULL );
73                xt_add_attr( reply, "xmlns", s );
74               
75                /* Of course this is a very essential query to support. ;-) */
76                if( strcmp( s, "jabber:iq:version" ) == 0 )
77                {
78                        xt_add_child( reply, xt_new_node( "name", "BitlBee", NULL ) );
79                        xt_add_child( reply, xt_new_node( "version", BITLBEE_VERSION, NULL ) );
80                        xt_add_child( reply, xt_new_node( "os", ARCH, NULL ) );
81                }
82                else if( strcmp( s, "jabber:iq:time" ) == 0 )
83                {
84                        time_t time_ep;
85                        char buf[1024];
86                       
87                        buf[sizeof(buf)-1] = 0;
88                        time_ep = time( NULL );
89                       
90                        strftime( buf, sizeof( buf ) - 1, "%Y%m%dT%H:%M:%S", gmtime( &time_ep ) );
91                        xt_add_child( reply, xt_new_node( "utc", buf, NULL ) );
92                       
93                        strftime( buf, sizeof( buf ) - 1, "%Z", localtime( &time_ep ) );
94                        xt_add_child( reply, xt_new_node( "tz", buf, NULL ) );
95                }
96                else if( strcmp( s, "http://jabber.org/protocol/disco#info" ) == 0 )
97                {
98                        c = xt_new_node( "identity", NULL, NULL );
99                        xt_add_attr( c, "category", "client" );
100                        xt_add_attr( c, "type", "pc" );
101                        xt_add_attr( c, "name", "BitlBee" );
102                        xt_add_child( reply, c );
103                       
104                        c = xt_new_node( "feature", NULL, NULL );
105                        xt_add_attr( c, "var", "jabber:iq:version" );
106                        xt_add_child( reply, c );
107                       
108                        c = xt_new_node( "feature", NULL, NULL );
109                        xt_add_attr( c, "var", "jabber:iq:time" );
110                        xt_add_child( reply, c );
111                       
112                        c = xt_new_node( "feature", NULL, NULL );
113                        xt_add_attr( c, "var", "http://jabber.org/protocol/chatstates" );
114                        xt_add_child( reply, c );
115                       
116                        /* Later this can be useful to announce things like
117                           MUC support. */
118                }
119                else
120                {
121                        xt_free_node( reply );
122                        reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel" );
123                        pack = 0;
124                }
125        }
126        else if( strcmp( type, "set" ) == 0 )
127        {
128                if( !( c = xt_find_node( node->children, "query" ) ) ||
129                    !( s = xt_find_attr( c, "xmlns" ) ) )
130                {
131                        serv_got_crap( gc, "WARNING: Received incomplete IQ-%s packet", type );
132                        return XT_HANDLED;
133                }
134               
135                if( strcmp( s, "jabber:iq:roster" ) == 0 )
136                {
137                        /* This is a roster push packet, probably. Here we
138                           should check if the packet is legitimate by
139                           checking if it really comes from the user's JID
140                           and, if so, process it. */
141                }
142                else
143                {
144                        xt_free_node( reply );
145                        reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel" );
146                        pack = 0;
147                }
148        }
149       
150        /* If we recognized the xmlns and managed to generate a reply,
151           finish and send it. */
152        if( reply )
153        {
154                /* Normally we still have to pack it into an iq-result
155                   packet, but for errors, for example, we don't. */
156                if( pack )
157                {
158                        reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), reply );
159                        if( ( s = xt_find_attr( node, "id" ) ) )
160                                xt_add_attr( reply, "id", s );
161                }
162               
163                st = jabber_write_packet( gc, reply );
164                xt_free_node( reply );
165                if( !st )
166                        return XT_ABORT;
167        }
168       
169        return XT_HANDLED;
170}
171
172static xt_status jabber_do_iq_auth( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
173static xt_status jabber_finish_iq_auth( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
174
175int jabber_init_iq_auth( struct gaim_connection *gc )
176{
177        struct jabber_data *jd = gc->proto_data;
178        struct xt_node *node;
179        int st;
180       
181        node = xt_new_node( "query", NULL, xt_new_node( "username", jd->username, NULL ) );
182        xt_add_attr( node, "xmlns", "jabber:iq:auth" );
183        node = jabber_make_packet( "iq", "get", NULL, node );
184       
185        jabber_cache_add( gc, node, jabber_do_iq_auth );
186        st = jabber_write_packet( gc, node );
187       
188        return st;
189}
190
191static xt_status jabber_do_iq_auth( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
192{
193        struct jabber_data *jd = gc->proto_data;
194        struct xt_node *reply, *query;
195        xt_status st;
196        char *s;
197       
198        query = xt_find_node( node->children, "query" );
199       
200        /* Time to authenticate ourselves! */
201        reply = xt_new_node( "query", NULL, NULL );
202        xt_add_attr( reply, "xmlns", "jabber:iq:auth" );
203        xt_add_child( reply, xt_new_node( "username", jd->username, NULL ) );
204        xt_add_child( reply, xt_new_node( "resource", set_getstr( &gc->acc->set, "resource" ), NULL ) );
205       
206        if( xt_find_node( query->children, "digest" ) && ( s = xt_find_attr( jd->xt->root, "id" ) ) )
207        {
208                /* We can do digest authentication, it seems, and of
209                   course we prefer that. */
210                SHA_CTX sha;
211                char hash_hex[41];
212                unsigned char hash[20];
213                int i;
214               
215                shaInit( &sha );
216                shaUpdate( &sha, (unsigned char*) s, strlen( s ) );
217                shaUpdate( &sha, (unsigned char*) gc->acc->pass, strlen( gc->acc->pass ) );
218                shaFinal( &sha, hash );
219               
220                for( i = 0; i < 20; i ++ )
221                        sprintf( hash_hex + i * 2, "%02x", hash[i] );
222               
223                xt_add_child( reply, xt_new_node( "digest", hash_hex, NULL ) );
224        }
225        else if( xt_find_node( query->children, "password" ) )
226        {
227                /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
228                xt_add_child( reply, xt_new_node( "password", gc->acc->pass, NULL ) );
229        }
230        else
231        {
232                xt_free_node( reply );
233               
234                hide_login_progress( gc, "Can't find suitable authentication method" );
235                signoff( gc );
236                return XT_ABORT;
237        }
238       
239        reply = jabber_make_packet( "iq", "set", NULL, reply );
240        jabber_cache_add( gc, reply, jabber_finish_iq_auth );
241        st = jabber_write_packet( gc, reply );
242       
243        return st ? XT_HANDLED : XT_ABORT;
244}
245
246static xt_status jabber_finish_iq_auth( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
247{
248        char *type = xt_find_attr( node, "type" );
249        struct jabber_data *jd = gc->proto_data;
250       
251        if( strcmp( type, "error" ) == 0 )
252        {
253                hide_login_progress( gc, "Authentication failure" );
254                signoff( gc );
255                return XT_ABORT;
256        }
257        else if( strcmp( type, "result" ) == 0 )
258        {
259                /* This happens when we just successfully authenticated the
260                   old (non-SASL) way. */
261                jd->flags |= JFLAG_AUTHENTICATED;
262                if( !jabber_get_roster( gc ) )
263                        return XT_ABORT;
264        }
265       
266        return XT_HANDLED;
267}
268
269xt_status jabber_pkt_bind_sess( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
270{
271        struct jabber_data *jd = gc->proto_data;
272        struct xt_node *c;
273        char *s;
274       
275        if( ( c = xt_find_node( node->children, "bind" ) ) )
276        {
277                c = xt_find_node( c->children, "jid" );
278                if( c && c->text_len && ( s = strchr( c->text, '/' ) ) &&
279                    strcmp( s + 1, set_getstr( &gc->acc->set, "resource" ) ) != 0 )
280                        serv_got_crap( gc, "Server changed session resource string to `%s'", s + 1 );
281               
282                jd->flags &= ~JFLAG_WAIT_BIND;
283        }
284        else
285        {
286                jd->flags &= ~JFLAG_WAIT_SESSION;
287        }
288       
289        if( ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
290        {
291                if( !jabber_get_roster( gc ) )
292                        return XT_ABORT;
293        }
294       
295        return XT_HANDLED;
296}
297
298static xt_status jabber_parse_roster( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
299
300int jabber_get_roster( struct gaim_connection *gc )
301{
302        struct xt_node *node;
303        int st;
304       
305        set_login_progress( gc, 1, "Authenticated, requesting buddy list" );
306       
307        node = xt_new_node( "query", NULL, NULL );
308        xt_add_attr( node, "xmlns", "jabber:iq:roster" );
309        node = jabber_make_packet( "iq", "get", NULL, node );
310       
311        jabber_cache_add( gc, node, jabber_parse_roster );
312        st = jabber_write_packet( gc, node );
313       
314        return st;
315}
316
317static xt_status jabber_parse_roster( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
318{
319        struct xt_node *query, *c;
320       
321        query = xt_find_node( node->children, "query" );
322       
323        c = query->children;
324        while( ( c = xt_find_node( c, "item" ) ) )
325        {
326                char *jid = xt_find_attr( c, "jid" );
327                char *name = xt_find_attr( c, "name" );
328                char *sub = xt_find_attr( c, "subscription" );
329               
330                if( jid && sub && ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) )
331                        add_buddy( gc, NULL, jid, name );
332               
333                c = c->next;
334        }
335       
336        account_online( gc );
337       
338        return XT_HANDLED;
339}
340
341static xt_status jabber_iq_display_vcard( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
342
343int jabber_get_vcard( struct gaim_connection *gc, char *bare_jid )
344{
345        struct xt_node *node;
346       
347        if( strchr( bare_jid, '/' ) )
348                return 1;       /* This was an error, but return 0 should only be done if the connection died... */
349       
350        node = xt_new_node( "vCard", NULL, NULL );
351        xt_add_attr( node, "xmlns", "vcard-temp" );
352        node = jabber_make_packet( "iq", "get", bare_jid, node );
353       
354        jabber_cache_add( gc, node, jabber_iq_display_vcard );
355        return jabber_write_packet( gc, node );
356}
357
358static xt_status jabber_iq_display_vcard( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
359{
360        struct xt_node *vc, *c, *sc; /* subchild, gc is already in use ;-) */
361        GString *reply;
362        char *s;
363       
364        if( ( s = xt_find_attr( node, "type" ) ) == NULL ||
365            strcmp( s, "result" ) != 0 ||
366            ( vc = xt_find_node( node->children, "vCard" ) ) == NULL )
367        {
368                s = xt_find_attr( orig, "to" ); /* If this returns NULL something's wrong.. */
369                serv_got_crap( gc, "Could not retrieve vCard of %s", s ? s : "(NULL)" );
370                return XT_HANDLED;
371        }
372       
373        s = xt_find_attr( orig, "to" );
374        reply = g_string_new( "vCard information for " );
375        reply = g_string_append( reply, s ? s : "(NULL)" );
376        reply = g_string_append( reply, ":\n" );
377       
378        /* I hate this format, I really do... */
379       
380        if( ( c = xt_find_node( vc->children, "FN" ) ) && c->text_len )
381                g_string_append_printf( reply, "Name: %s\n", c->text );
382       
383        if( ( c = xt_find_node( vc->children, "N" ) ) && c->children )
384        {
385                reply = g_string_append( reply, "Full name:" );
386               
387                if( ( sc = xt_find_node( c->children, "PREFIX" ) ) && sc->text_len )
388                        g_string_append_printf( reply, " %s", sc->text );
389                if( ( sc = xt_find_node( c->children, "GIVEN" ) ) && sc->text_len )
390                        g_string_append_printf( reply, " %s", sc->text );
391                if( ( sc = xt_find_node( c->children, "MIDDLE" ) ) && sc->text_len )
392                        g_string_append_printf( reply, " %s", sc->text );
393                if( ( sc = xt_find_node( c->children, "FAMILY" ) ) && sc->text_len )
394                        g_string_append_printf( reply, " %s", sc->text );
395                if( ( sc = xt_find_node( c->children, "SUFFIX" ) ) && sc->text_len )
396                        g_string_append_printf( reply, " %s", sc->text );
397               
398                reply = g_string_append_c( reply, '\n' );
399        }
400       
401        if( ( c = xt_find_node( vc->children, "NICKNAME" ) ) && c->text_len )
402                g_string_append_printf( reply, "Nickname: %s\n", c->text );
403       
404        if( ( c = xt_find_node( vc->children, "BDAY" ) ) && c->text_len )
405                g_string_append_printf( reply, "Date of birth: %s\n", c->text );
406       
407        /* Slightly alternative use of for... ;-) */
408        for( c = vc->children; ( c = xt_find_node( c, "EMAIL" ) ); c = c->next )
409        {
410                if( ( sc = xt_find_node( c->children, "USERID" ) ) == NULL || sc->text_len == 0 )
411                        continue;
412               
413                if( xt_find_node( c->children, "HOME" ) )
414                        s = "Home";
415                else if( xt_find_node( c->children, "WORK" ) )
416                        s = "Work";
417                else
418                        s = "Misc.";
419               
420                g_string_append_printf( reply, "%s e-mail address: %s\n", s, sc->text );
421        }
422       
423        if( ( c = xt_find_node( vc->children, "URL" ) ) && c->text_len )
424                g_string_append_printf( reply, "Homepage: %s\n", c->text );
425       
426        /* Slightly alternative use of for... ;-) */
427        for( c = vc->children; ( c = xt_find_node( c, "ADR" ) ); c = c->next )
428        {
429                if( xt_find_node( c->children, "HOME" ) )
430                        s = "Home";
431                else if( xt_find_node( c->children, "WORK" ) )
432                        s = "Work";
433                else
434                        s = "Misc.";
435               
436                g_string_append_printf( reply, "%s address: ", s );
437               
438                if( ( sc = xt_find_node( c->children, "STREET" ) ) && sc->text_len )
439                        g_string_append_printf( reply, "%s ", sc->text );
440                if( ( sc = xt_find_node( c->children, "EXTADR" ) ) && sc->text_len )
441                        g_string_append_printf( reply, "%s, ", sc->text );
442                if( ( sc = xt_find_node( c->children, "PCODE" ) ) && sc->text_len )
443                        g_string_append_printf( reply, "%s, ", sc->text );
444                if( ( sc = xt_find_node( c->children, "LOCALITY" ) ) && sc->text_len )
445                        g_string_append_printf( reply, "%s, ", sc->text );
446                if( ( sc = xt_find_node( c->children, "REGION" ) ) && sc->text_len )
447                        g_string_append_printf( reply, "%s, ", sc->text );
448                if( ( sc = xt_find_node( c->children, "CTRY" ) ) && sc->text_len )
449                        g_string_append_printf( reply, "%s", sc->text );
450               
451                if( reply->str[reply->len-2] == ',' )
452                        reply = g_string_truncate( reply, reply->len-2 );
453               
454                reply = g_string_append_c( reply, '\n' );
455        }
456       
457        for( c = vc->children; ( c = xt_find_node( c, "TEL" ) ); c = c->next )
458        {
459                if( ( sc = xt_find_node( c->children, "NUMBER" ) ) == NULL || sc->text_len == 0 )
460                        continue;
461               
462                if( xt_find_node( c->children, "HOME" ) )
463                        s = "Home";
464                else if( xt_find_node( c->children, "WORK" ) )
465                        s = "Work";
466                else
467                        s = "Misc.";
468               
469                g_string_append_printf( reply, "%s phone number: %s\n", s, sc->text );
470        }
471       
472        if( ( c = xt_find_node( vc->children, "DESC" ) ) && c->text_len )
473                g_string_append_printf( reply, "Other information:\n%s", c->text );
474       
475        /* *sigh* */
476       
477        serv_got_crap( gc, reply->str );
478        g_string_free( reply, TRUE );
479       
480        return XT_HANDLED;
481}
482
483int jabber_add_to_roster( struct gaim_connection *gc, char *handle, char *name )
484{
485        struct xt_node *node;
486        int st;
487       
488        /* Build the item entry */
489        node = xt_new_node( "item", NULL, NULL );
490        xt_add_attr( node, "jid", handle );
491        if( name )
492                xt_add_attr( node, "name", name );
493       
494        /* And pack it into a roster-add packet */
495        node = xt_new_node( "query", NULL, node );
496        xt_add_attr( node, "xmlns", "jabber:iq:roster" );
497        node = jabber_make_packet( "iq", "set", NULL, node );
498       
499        st = jabber_write_packet( gc, node );
500       
501        xt_free_node( node );
502        return st;
503}
504
505int jabber_remove_from_roster( struct gaim_connection *gc, char *handle )
506{
507        struct xt_node *node;
508        int st;
509       
510        /* Build the item entry */
511        node = xt_new_node( "item", NULL, NULL );
512        xt_add_attr( node, "jid", handle );
513        xt_add_attr( node, "subscription", "remove" );
514       
515        /* And pack it into a roster-add packet */
516        node = xt_new_node( "query", NULL, node );
517        xt_add_attr( node, "xmlns", "jabber:iq:roster" );
518        node = jabber_make_packet( "iq", "set", NULL, node );
519       
520        st = jabber_write_packet( gc, node );
521       
522        xt_free_node( node );
523        return st;
524}
Note: See TracBrowser for help on using the repository browser.