source: protocols/jabber/iq.c @ d06eabf

Last change on this file since d06eabf was d06eabf, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-06-04T13:22:05Z

Added imcb_buddy_nick_hint so the Jabber conference module can suggest sane
nicknames for chatroom participants. There'll probably be a lot of
underscores now, but this is by far the cleanest way to implement this, I
think. At least now whispers will work properly. Also using this function
call to set names for ICQ contacts in a slightly saner way.

  • 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                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                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                        imcb_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                        imcb_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                        imcb_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                                imcb_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                imcb_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                imcb_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                imcb_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                imcb_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                        imcb_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        imcb_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                imcb_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                struct xt_node *group = xt_find_node( node->children, "group" );
363                char *jid = xt_find_attr( c, "jid" );
364                char *name = xt_find_attr( c, "name" );
365                char *sub = xt_find_attr( c, "subscription" );
366               
367                if( jid && sub )
368                {
369                        if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) )
370                        {
371                                if( initial || imcb_find_buddy( ic, jid ) == NULL )
372                                        imcb_add_buddy( ic, jid, ( group && group->text_len ) ?
373                                                                   group->text : NULL );
374                               
375                                if( name )
376                                {
377                                        imcb_rename_buddy( ic, jid, name );
378                                        imcb_buddy_nick_hint( ic, jid, name );
379                                }
380                        }
381                        else if( strcmp( sub, "remove" ) == 0 )
382                        {
383                                /* Don't have any API call for this yet! So let's
384                                   just try to handle this as well as we can. */
385                                jabber_buddy_remove_bare( ic, jid );
386                                imcb_buddy_status( ic, jid, 0, NULL, NULL );
387                                /* FIXME! */
388                        }
389                }
390               
391                c = c->next;
392        }
393       
394        if( initial )
395                imcb_connected( ic );
396       
397        return XT_HANDLED;
398}
399
400int jabber_get_vcard( struct im_connection *ic, char *bare_jid )
401{
402        struct xt_node *node;
403       
404        if( strchr( bare_jid, '/' ) )
405                return 1;       /* This was an error, but return 0 should only be done if the connection died... */
406       
407        node = xt_new_node( "vCard", NULL, NULL );
408        xt_add_attr( node, "xmlns", XMLNS_VCARD );
409        node = jabber_make_packet( "iq", "get", bare_jid, node );
410       
411        jabber_cache_add( ic, node, jabber_iq_display_vcard );
412        return jabber_write_packet( ic, node );
413}
414
415static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
416{
417        struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
418        GString *reply;
419        char *s;
420       
421        if( ( s = xt_find_attr( node, "type" ) ) == NULL ||
422            strcmp( s, "result" ) != 0 ||
423            ( vc = xt_find_node( node->children, "vCard" ) ) == NULL )
424        {
425                s = xt_find_attr( orig, "to" ); /* If this returns NULL something's wrong.. */
426                imcb_log( ic, "Could not retrieve vCard of %s", s ? s : "(NULL)" );
427                return XT_HANDLED;
428        }
429       
430        s = xt_find_attr( orig, "to" );
431        reply = g_string_new( "vCard information for " );
432        reply = g_string_append( reply, s ? s : "(NULL)" );
433        reply = g_string_append( reply, ":\n" );
434       
435        /* I hate this format, I really do... */
436       
437        if( ( c = xt_find_node( vc->children, "FN" ) ) && c->text_len )
438                g_string_append_printf( reply, "Name: %s\n", c->text );
439       
440        if( ( c = xt_find_node( vc->children, "N" ) ) && c->children )
441        {
442                reply = g_string_append( reply, "Full name:" );
443               
444                if( ( sc = xt_find_node( c->children, "PREFIX" ) ) && sc->text_len )
445                        g_string_append_printf( reply, " %s", sc->text );
446                if( ( sc = xt_find_node( c->children, "GIVEN" ) ) && sc->text_len )
447                        g_string_append_printf( reply, " %s", sc->text );
448                if( ( sc = xt_find_node( c->children, "MIDDLE" ) ) && sc->text_len )
449                        g_string_append_printf( reply, " %s", sc->text );
450                if( ( sc = xt_find_node( c->children, "FAMILY" ) ) && sc->text_len )
451                        g_string_append_printf( reply, " %s", sc->text );
452                if( ( sc = xt_find_node( c->children, "SUFFIX" ) ) && sc->text_len )
453                        g_string_append_printf( reply, " %s", sc->text );
454               
455                reply = g_string_append_c( reply, '\n' );
456        }
457       
458        if( ( c = xt_find_node( vc->children, "NICKNAME" ) ) && c->text_len )
459                g_string_append_printf( reply, "Nickname: %s\n", c->text );
460       
461        if( ( c = xt_find_node( vc->children, "BDAY" ) ) && c->text_len )
462                g_string_append_printf( reply, "Date of birth: %s\n", c->text );
463       
464        /* Slightly alternative use of for... ;-) */
465        for( c = vc->children; ( c = xt_find_node( c, "EMAIL" ) ); c = c->next )
466        {
467                if( ( sc = xt_find_node( c->children, "USERID" ) ) == NULL || sc->text_len == 0 )
468                        continue;
469               
470                if( xt_find_node( c->children, "HOME" ) )
471                        s = "Home";
472                else if( xt_find_node( c->children, "WORK" ) )
473                        s = "Work";
474                else
475                        s = "Misc.";
476               
477                g_string_append_printf( reply, "%s e-mail address: %s\n", s, sc->text );
478        }
479       
480        if( ( c = xt_find_node( vc->children, "URL" ) ) && c->text_len )
481                g_string_append_printf( reply, "Homepage: %s\n", c->text );
482       
483        /* Slightly alternative use of for... ;-) */
484        for( c = vc->children; ( c = xt_find_node( c, "ADR" ) ); c = c->next )
485        {
486                if( xt_find_node( c->children, "HOME" ) )
487                        s = "Home";
488                else if( xt_find_node( c->children, "WORK" ) )
489                        s = "Work";
490                else
491                        s = "Misc.";
492               
493                g_string_append_printf( reply, "%s address: ", s );
494               
495                if( ( sc = xt_find_node( c->children, "STREET" ) ) && sc->text_len )
496                        g_string_append_printf( reply, "%s ", sc->text );
497                if( ( sc = xt_find_node( c->children, "EXTADR" ) ) && sc->text_len )
498                        g_string_append_printf( reply, "%s, ", sc->text );
499                if( ( sc = xt_find_node( c->children, "PCODE" ) ) && sc->text_len )
500                        g_string_append_printf( reply, "%s, ", sc->text );
501                if( ( sc = xt_find_node( c->children, "LOCALITY" ) ) && sc->text_len )
502                        g_string_append_printf( reply, "%s, ", sc->text );
503                if( ( sc = xt_find_node( c->children, "REGION" ) ) && sc->text_len )
504                        g_string_append_printf( reply, "%s, ", sc->text );
505                if( ( sc = xt_find_node( c->children, "CTRY" ) ) && sc->text_len )
506                        g_string_append_printf( reply, "%s", sc->text );
507               
508                if( reply->str[reply->len-2] == ',' )
509                        reply = g_string_truncate( reply, reply->len-2 );
510               
511                reply = g_string_append_c( reply, '\n' );
512        }
513       
514        for( c = vc->children; ( c = xt_find_node( c, "TEL" ) ); c = c->next )
515        {
516                if( ( sc = xt_find_node( c->children, "NUMBER" ) ) == NULL || sc->text_len == 0 )
517                        continue;
518               
519                if( xt_find_node( c->children, "HOME" ) )
520                        s = "Home";
521                else if( xt_find_node( c->children, "WORK" ) )
522                        s = "Work";
523                else
524                        s = "Misc.";
525               
526                g_string_append_printf( reply, "%s phone number: %s\n", s, sc->text );
527        }
528       
529        if( ( c = xt_find_node( vc->children, "DESC" ) ) && c->text_len )
530                g_string_append_printf( reply, "Other information:\n%s", c->text );
531       
532        /* *sigh* */
533       
534        imcb_log( ic, "%s", reply->str );
535        g_string_free( reply, TRUE );
536       
537        return XT_HANDLED;
538}
539
540int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name )
541{
542        struct xt_node *node;
543        int st;
544       
545        /* Build the item entry */
546        node = xt_new_node( "item", NULL, NULL );
547        xt_add_attr( node, "jid", handle );
548        if( name )
549                xt_add_attr( node, "name", name );
550       
551        /* And pack it into a roster-add packet */
552        node = xt_new_node( "query", NULL, node );
553        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
554        node = jabber_make_packet( "iq", "set", NULL, node );
555       
556        st = jabber_write_packet( ic, node );
557       
558        xt_free_node( node );
559        return st;
560}
561
562int jabber_remove_from_roster( struct im_connection *ic, char *handle )
563{
564        struct xt_node *node;
565        int st;
566       
567        /* Build the item entry */
568        node = xt_new_node( "item", NULL, NULL );
569        xt_add_attr( node, "jid", handle );
570        xt_add_attr( node, "subscription", "remove" );
571       
572        /* And pack it into a roster-add packet */
573        node = xt_new_node( "query", NULL, node );
574        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
575        node = jabber_make_packet( "iq", "set", NULL, node );
576       
577        st = jabber_write_packet( ic, node );
578       
579        xt_free_node( node );
580        return st;
581}
Note: See TracBrowser for help on using the repository browser.