source: protocols/jabber/iq.c @ 186bd04

Last change on this file since 186bd04 was 186bd04, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-23T14:14:16Z

Don't whine on "incomplete" incoming IQ-set packets either, those also happen.

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