source: protocols/jabber/iq.c @ 8ff0a61

Last change on this file since 8ff0a61 was 8c1eb80, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-02-03T16:59:39Z

Implemented XEP-0115. This adds some info to the <presence/> tags so
clients interested in capabilities can cache discovery info, so they don't
have to ask about it every time you/they log in.

  • Property mode set to 100644
File size: 17.5 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" ) ) ) || /* O_o WHAT is wrong with just <query/> ????? */
54                    !( s = xt_find_attr( c, "xmlns" ) ) )
55                {
56                        imcb_log( ic, "Warning: Received incomplete IQ-%s packet", type );
57                        return XT_HANDLED;
58                }
59               
60                reply = xt_new_node( "query", NULL, NULL );
61                xt_add_attr( reply, "xmlns", s );
62               
63                /* Of course this is a very essential query to support. ;-) */
64                if( strcmp( s, XMLNS_VERSION ) == 0 )
65                {
66                        xt_add_child( reply, xt_new_node( "name", "BitlBee", NULL ) );
67                        xt_add_child( reply, xt_new_node( "version", BITLBEE_VERSION, NULL ) );
68                        xt_add_child( reply, xt_new_node( "os", ARCH, NULL ) );
69                }
70                else if( strcmp( s, XMLNS_TIME ) == 0 )
71                {
72                        time_t time_ep;
73                        char buf[1024];
74                       
75                        buf[sizeof(buf)-1] = 0;
76                        time_ep = time( NULL );
77                       
78                        strftime( buf, sizeof( buf ) - 1, "%Y%m%dT%H:%M:%S", gmtime( &time_ep ) );
79                        xt_add_child( reply, xt_new_node( "utc", buf, NULL ) );
80                       
81                        strftime( buf, sizeof( buf ) - 1, "%Z", localtime( &time_ep ) );
82                        xt_add_child( reply, xt_new_node( "tz", buf, NULL ) );
83                }
84                else if( strcmp( s, XMLNS_PING ) == 0 )
85                {
86                        xt_free_node( reply );
87                        reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), NULL );
88                        if( ( s = xt_find_attr( node, "id" ) ) )
89                                xt_add_attr( reply, "id", s );
90                        pack = 0;
91                }
92                else if( strcmp( s, XMLNS_DISCOVER ) == 0 )
93                {
94                        const char *features[] = { XMLNS_DISCOVER,
95                                                   XMLNS_VERSION,
96                                                   XMLNS_TIME,
97                                                   XMLNS_CHATSTATES,
98                                                   XMLNS_MUC,
99                                                   XMLNS_PING,
100                                                   NULL };
101                        const char **f;
102                       
103                        c = xt_new_node( "identity", NULL, NULL );
104                        xt_add_attr( c, "category", "client" );
105                        xt_add_attr( c, "type", "pc" );
106                        xt_add_attr( c, "name", "BitlBee" );
107                        xt_add_child( reply, c );
108                       
109                        for( f = features; *f; f ++ )
110                        {
111                                c = xt_new_node( "feature", NULL, NULL );
112                                xt_add_attr( c, "var", *f );
113                                xt_add_child( reply, c );
114                        }
115                }
116                else
117                {
118                        xt_free_node( reply );
119                        reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel" );
120                        pack = 0;
121                }
122        }
123        else if( strcmp( type, "set" ) == 0 )
124        {
125                if( !( c = xt_find_node( node->children, "query" ) ) ||
126                    !( s = xt_find_attr( c, "xmlns" ) ) )
127                {
128                        imcb_log( ic, "Warning: Received incomplete IQ-%s packet", type );
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;
300        char *s;
301       
302        if( ( 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                jd->flags &= ~JFLAG_WAIT_BIND;
310        }
311        else
312        {
313                jd->flags &= ~JFLAG_WAIT_SESSION;
314        }
315       
316        if( ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
317        {
318                if( !jabber_get_roster( ic ) )
319                        return XT_ABORT;
320        }
321       
322        return XT_HANDLED;
323}
324
325int jabber_get_roster( struct im_connection *ic )
326{
327        struct xt_node *node;
328        int st;
329       
330        imcb_log( ic, "Authenticated, requesting buddy list" );
331       
332        node = xt_new_node( "query", NULL, NULL );
333        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
334        node = jabber_make_packet( "iq", "get", NULL, node );
335       
336        jabber_cache_add( ic, node, jabber_parse_roster );
337        st = jabber_write_packet( ic, node );
338       
339        return st;
340}
341
342static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
343{
344        struct xt_node *query, *c;
345        int initial = ( orig != NULL );
346       
347        if( !( query = xt_find_node( node->children, "query" ) ) )
348        {
349                imcb_log( ic, "Warning: Received NULL roster packet" );
350                return XT_HANDLED;
351        }
352       
353        c = query->children;
354        while( ( c = xt_find_node( c, "item" ) ) )
355        {
356                struct xt_node *group = xt_find_node( node->children, "group" );
357                char *jid = xt_find_attr( c, "jid" );
358                char *name = xt_find_attr( c, "name" );
359                char *sub = xt_find_attr( c, "subscription" );
360               
361                if( jid && sub )
362                {
363                        if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) )
364                        {
365                                if( initial || imcb_find_buddy( ic, jid ) == NULL )
366                                        imcb_add_buddy( ic, jid, ( group && group->text_len ) ?
367                                                                   group->text : NULL );
368                               
369                                if( name )
370                                        imcb_rename_buddy( ic, jid, name );
371                        }
372                        else if( strcmp( sub, "remove" ) == 0 )
373                        {
374                                jabber_buddy_remove_bare( ic, jid );
375                                imcb_remove_buddy( ic, jid, NULL );
376                        }
377                }
378               
379                c = c->next;
380        }
381       
382        if( initial )
383                imcb_connected( ic );
384       
385        return XT_HANDLED;
386}
387
388int jabber_get_vcard( struct im_connection *ic, char *bare_jid )
389{
390        struct xt_node *node;
391       
392        if( strchr( bare_jid, '/' ) )
393                return 1;       /* This was an error, but return 0 should only be done if the connection died... */
394       
395        node = xt_new_node( "vCard", NULL, NULL );
396        xt_add_attr( node, "xmlns", XMLNS_VCARD );
397        node = jabber_make_packet( "iq", "get", bare_jid, node );
398       
399        jabber_cache_add( ic, node, jabber_iq_display_vcard );
400        return jabber_write_packet( ic, node );
401}
402
403static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
404{
405        struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
406        GString *reply;
407        char *s;
408       
409        if( ( s = xt_find_attr( node, "type" ) ) == NULL ||
410            strcmp( s, "result" ) != 0 ||
411            ( vc = xt_find_node( node->children, "vCard" ) ) == NULL )
412        {
413                s = xt_find_attr( orig, "to" ); /* If this returns NULL something's wrong.. */
414                imcb_log( ic, "Could not retrieve vCard of %s", s ? s : "(NULL)" );
415                return XT_HANDLED;
416        }
417       
418        s = xt_find_attr( orig, "to" );
419        reply = g_string_new( "vCard information for " );
420        reply = g_string_append( reply, s ? s : "(NULL)" );
421        reply = g_string_append( reply, ":\n" );
422       
423        /* I hate this format, I really do... */
424       
425        if( ( c = xt_find_node( vc->children, "FN" ) ) && c->text_len )
426                g_string_append_printf( reply, "Name: %s\n", c->text );
427       
428        if( ( c = xt_find_node( vc->children, "N" ) ) && c->children )
429        {
430                reply = g_string_append( reply, "Full name:" );
431               
432                if( ( sc = xt_find_node( c->children, "PREFIX" ) ) && sc->text_len )
433                        g_string_append_printf( reply, " %s", sc->text );
434                if( ( sc = xt_find_node( c->children, "GIVEN" ) ) && sc->text_len )
435                        g_string_append_printf( reply, " %s", sc->text );
436                if( ( sc = xt_find_node( c->children, "MIDDLE" ) ) && sc->text_len )
437                        g_string_append_printf( reply, " %s", sc->text );
438                if( ( sc = xt_find_node( c->children, "FAMILY" ) ) && sc->text_len )
439                        g_string_append_printf( reply, " %s", sc->text );
440                if( ( sc = xt_find_node( c->children, "SUFFIX" ) ) && sc->text_len )
441                        g_string_append_printf( reply, " %s", sc->text );
442               
443                reply = g_string_append_c( reply, '\n' );
444        }
445       
446        if( ( c = xt_find_node( vc->children, "NICKNAME" ) ) && c->text_len )
447                g_string_append_printf( reply, "Nickname: %s\n", c->text );
448       
449        if( ( c = xt_find_node( vc->children, "BDAY" ) ) && c->text_len )
450                g_string_append_printf( reply, "Date of birth: %s\n", c->text );
451       
452        /* Slightly alternative use of for... ;-) */
453        for( c = vc->children; ( c = xt_find_node( c, "EMAIL" ) ); c = c->next )
454        {
455                if( ( sc = xt_find_node( c->children, "USERID" ) ) == NULL || sc->text_len == 0 )
456                        continue;
457               
458                if( xt_find_node( c->children, "HOME" ) )
459                        s = "Home";
460                else if( xt_find_node( c->children, "WORK" ) )
461                        s = "Work";
462                else
463                        s = "Misc.";
464               
465                g_string_append_printf( reply, "%s e-mail address: %s\n", s, sc->text );
466        }
467       
468        if( ( c = xt_find_node( vc->children, "URL" ) ) && c->text_len )
469                g_string_append_printf( reply, "Homepage: %s\n", c->text );
470       
471        /* Slightly alternative use of for... ;-) */
472        for( c = vc->children; ( c = xt_find_node( c, "ADR" ) ); c = c->next )
473        {
474                if( xt_find_node( c->children, "HOME" ) )
475                        s = "Home";
476                else if( xt_find_node( c->children, "WORK" ) )
477                        s = "Work";
478                else
479                        s = "Misc.";
480               
481                g_string_append_printf( reply, "%s address: ", s );
482               
483                if( ( sc = xt_find_node( c->children, "STREET" ) ) && sc->text_len )
484                        g_string_append_printf( reply, "%s ", sc->text );
485                if( ( sc = xt_find_node( c->children, "EXTADR" ) ) && sc->text_len )
486                        g_string_append_printf( reply, "%s, ", sc->text );
487                if( ( sc = xt_find_node( c->children, "PCODE" ) ) && sc->text_len )
488                        g_string_append_printf( reply, "%s, ", sc->text );
489                if( ( sc = xt_find_node( c->children, "LOCALITY" ) ) && sc->text_len )
490                        g_string_append_printf( reply, "%s, ", sc->text );
491                if( ( sc = xt_find_node( c->children, "REGION" ) ) && sc->text_len )
492                        g_string_append_printf( reply, "%s, ", sc->text );
493                if( ( sc = xt_find_node( c->children, "CTRY" ) ) && sc->text_len )
494                        g_string_append_printf( reply, "%s", sc->text );
495               
496                if( reply->str[reply->len-2] == ',' )
497                        reply = g_string_truncate( reply, reply->len-2 );
498               
499                reply = g_string_append_c( reply, '\n' );
500        }
501       
502        for( c = vc->children; ( c = xt_find_node( c, "TEL" ) ); c = c->next )
503        {
504                if( ( sc = xt_find_node( c->children, "NUMBER" ) ) == NULL || sc->text_len == 0 )
505                        continue;
506               
507                if( xt_find_node( c->children, "HOME" ) )
508                        s = "Home";
509                else if( xt_find_node( c->children, "WORK" ) )
510                        s = "Work";
511                else
512                        s = "Misc.";
513               
514                g_string_append_printf( reply, "%s phone number: %s\n", s, sc->text );
515        }
516       
517        if( ( c = xt_find_node( vc->children, "DESC" ) ) && c->text_len )
518                g_string_append_printf( reply, "Other information:\n%s", c->text );
519       
520        /* *sigh* */
521       
522        imcb_log( ic, "%s", reply->str );
523        g_string_free( reply, TRUE );
524       
525        return XT_HANDLED;
526}
527
528int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name )
529{
530        struct xt_node *node;
531        int st;
532       
533        /* Build the item entry */
534        node = xt_new_node( "item", NULL, NULL );
535        xt_add_attr( node, "jid", handle );
536        if( name )
537                xt_add_attr( node, "name", name );
538       
539        /* And pack it into a roster-add packet */
540        node = xt_new_node( "query", NULL, node );
541        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
542        node = jabber_make_packet( "iq", "set", NULL, node );
543       
544        st = jabber_write_packet( ic, node );
545       
546        xt_free_node( node );
547        return st;
548}
549
550int jabber_remove_from_roster( struct im_connection *ic, char *handle )
551{
552        struct xt_node *node;
553        int st;
554       
555        /* Build the item entry */
556        node = xt_new_node( "item", NULL, NULL );
557        xt_add_attr( node, "jid", handle );
558        xt_add_attr( node, "subscription", "remove" );
559       
560        /* And pack it into a roster-add packet */
561        node = xt_new_node( "query", NULL, node );
562        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
563        node = jabber_make_packet( "iq", "set", NULL, node );
564       
565        st = jabber_write_packet( ic, node );
566       
567        xt_free_node( node );
568        return st;
569}
Note: See TracBrowser for help on using the repository browser.