source: protocols/jabber/iq.c @ 998b103

Last change on this file since 998b103 was 998b103, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-06-13T23:31:39Z

Added imcb_remove_buddy() so deletions in Jabber roster pushes actually
work. This also solves the issue of underscores appearing and disappearing
in their nicknames when people leave/join a chat.

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