source: protocols/jabber/iq.c @ 16b5f86

Last change on this file since 16b5f86 was 47d3ac4, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-31T08:25:41Z

Added #defines for XML namespaces.

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