source: protocols/jabber/iq.c @ c2fb3809

Last change on this file since c2fb3809 was c2fb3809, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-04-15T22:39:35Z

Cleaned up struct im_connection. No more username/password stuff since
it's in acc too. wants_to_die is now an argument to imc_logout().

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