source: protocols/jabber/iq.c @ 4306d8b

Last change on this file since 4306d8b was 4306d8b, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-12-02T16:43:57Z

Removed retarded printf() (ARGH) and moved the event handling handling of
IQ packets to jabber_util so I can reuse it for certain presence packets.

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