source: protocols/jabber/iq.c @ e0e2a71

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

Fixed retarded use of strcpy() and no longer using Jabber fullnames for
nickname generation. IM fullnames and IRC nicknames are just *different*.

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