source: protocols/jabber/iq.c @ 529078a

Last change on this file since 529078a was 529078a, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-12-16T16:35:38Z

Implemented XEP-0199 (patch from misc@…).

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