source: protocols/jabber/iq.c @ b051d39

Last change on this file since b051d39 was 74349eb, checked in by Wilmer van der Gaast <wilmer@…>, at 2009-10-17T15:13:40Z

If you can't find what you expect in an IQ packet, it doesn't mean it's
incorrect, so stop spamming. Ideally it should return a feature-not-
implemented but I'll do that later.

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