source: protocols/jabber/iq.c @ b9f8b87

Last change on this file since b9f8b87 was 40ef702, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-07-01T13:08:47Z

Less copy-pasting in the service discovery reply and added MUC support to
that list. And adding some const stuff in the xmltree functions.

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