source: protocols/jabber/iq.c @ f920d9e

Last change on this file since f920d9e was 1991be6, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-18T17:47:08Z

get_info() now displays vCard information too.

  • Property mode set to 100644
File size: 15.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
26xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )
27{
28        struct gaim_connection *gc = data;
29        struct jabber_data *jd = gc->proto_data;
30        struct xt_node *c, *reply = NULL;
31        char *type, *s;
32        int st, pack = 1;
33       
34        type = xt_find_attr( node, "type" );
35       
36        if( !type )
37        {
38                hide_login_progress_error( gc, "Received IQ packet without type." );
39                signoff( gc );
40                return XT_ABORT;
41        }
42       
43        if( strcmp( type, "result" ) == 0 || strcmp( type, "error" ) == 0 )
44        {
45                struct jabber_cache_entry *entry;
46               
47                if( ( s = xt_find_attr( node, "id" ) ) == NULL )
48                {
49                        /* Silently ignore it, without an ID we don't know
50                           how to handle the packet, but it doesn't have
51                           to be a serious problem. */
52                        return XT_HANDLED;
53                }
54               
55                entry = g_hash_table_lookup( jd->node_cache, s );
56               
57                if( entry == NULL )
58                        serv_got_crap( gc, "WARNING: Received IQ %s packet with unknown/expired ID %s!", type, s );
59                else if( entry->func )
60                        return entry->func( gc, node, entry->node );
61        }
62        else if( strcmp( type, "get" ) == 0 )
63        {
64                if( !( c = xt_find_node( node->children, "query" ) ) ||
65                    !( s = xt_find_attr( c, "xmlns" ) ) )
66                {
67                        serv_got_crap( gc, "WARNING: Received incomplete IQ-get packet" );
68                        return XT_HANDLED;
69                }
70               
71                reply = xt_new_node( "query", NULL, NULL );
72                xt_add_attr( reply, "xmlns", s );
73               
74                /* Of course this is a very essential query to support. ;-) */
75                if( strcmp( s, "jabber:iq:version" ) == 0 )
76                {
77                        xt_add_child( reply, xt_new_node( "name", "BitlBee", NULL ) );
78                        xt_add_child( reply, xt_new_node( "version", BITLBEE_VERSION, NULL ) );
79                        xt_add_child( reply, xt_new_node( "os", ARCH, NULL ) );
80                }
81                else if( strcmp( s, "jabber:iq:time" ) == 0 )
82                {
83                        time_t time_ep;
84                        char buf[1024];
85                       
86                        buf[sizeof(buf)-1] = 0;
87                        time_ep = time( NULL );
88                       
89                        strftime( buf, sizeof( buf ) - 1, "%Y%m%dT%H:%M:%S", gmtime( &time_ep ) );
90                        xt_add_child( reply, xt_new_node( "utc", buf, NULL ) );
91                       
92                        strftime( buf, sizeof( buf ) - 1, "%Z", localtime( &time_ep ) );
93                        xt_add_child( reply, xt_new_node( "tz", buf, NULL ) );
94                }
95                else if( strcmp( s, "http://jabber.org/protocol/disco#info" ) == 0 )
96                {
97                        c = xt_new_node( "identity", NULL, NULL );
98                        xt_add_attr( c, "category", "client" );
99                        xt_add_attr( c, "type", "pc" );
100                        xt_add_attr( c, "name", "BitlBee" );
101                        xt_add_child( reply, c );
102                       
103                        c = xt_new_node( "feature", NULL, NULL );
104                        xt_add_attr( c, "var", "jabber:iq:version" );
105                        xt_add_child( reply, c );
106                       
107                        c = xt_new_node( "feature", NULL, NULL );
108                        xt_add_attr( c, "var", "jabber:iq:time" );
109                        xt_add_child( reply, c );
110                       
111                        c = xt_new_node( "feature", NULL, NULL );
112                        xt_add_attr( c, "var", "http://jabber.org/protocol/chatstates" );
113                        xt_add_child( reply, c );
114                       
115                        /* Later this can be useful to announce things like
116                           MUC support. */
117                }
118                else
119                {
120                        xt_free_node( reply );
121                        reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel" );
122                        pack = 0;
123                }
124        }
125        else if( strcmp( type, "set" ) == 0 )
126        {
127                xt_free_node( reply );
128                reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel" );
129                pack = 0;
130        }
131       
132        /* If we recognized the xmlns and managed to generate a reply,
133           finish and send it. */
134        if( reply )
135        {
136                /* Normally we still have to pack it into an iq-result
137                   packet, but for errors, for example, we don't. */
138                if( pack )
139                {
140                        reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), reply );
141                        if( ( s = xt_find_attr( node, "id" ) ) )
142                                xt_add_attr( reply, "id", s );
143                }
144               
145                st = jabber_write_packet( gc, reply );
146                xt_free_node( reply );
147                if( !st )
148                        return XT_ABORT;
149        }
150       
151        return XT_HANDLED;
152}
153
154static xt_status jabber_do_iq_auth( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
155static xt_status jabber_finish_iq_auth( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
156
157int jabber_init_iq_auth( struct gaim_connection *gc )
158{
159        struct jabber_data *jd = gc->proto_data;
160        struct xt_node *node;
161        int st;
162       
163        node = xt_new_node( "query", NULL, xt_new_node( "username", jd->username, NULL ) );
164        xt_add_attr( node, "xmlns", "jabber:iq:auth" );
165        node = jabber_make_packet( "iq", "get", NULL, node );
166       
167        jabber_cache_add( gc, node, jabber_do_iq_auth );
168        st = jabber_write_packet( gc, node );
169       
170        return st;
171}
172
173static xt_status jabber_do_iq_auth( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
174{
175        struct jabber_data *jd = gc->proto_data;
176        struct xt_node *reply, *query;
177        xt_status st;
178        char *s;
179       
180        query = xt_find_node( node->children, "query" );
181       
182        /* Time to authenticate ourselves! */
183        reply = xt_new_node( "query", NULL, NULL );
184        xt_add_attr( reply, "xmlns", "jabber:iq:auth" );
185        xt_add_child( reply, xt_new_node( "username", jd->username, NULL ) );
186        xt_add_child( reply, xt_new_node( "resource", set_getstr( &gc->acc->set, "resource" ), NULL ) );
187       
188        if( xt_find_node( query->children, "digest" ) && ( s = xt_find_attr( jd->xt->root, "id" ) ) )
189        {
190                /* We can do digest authentication, it seems, and of
191                   course we prefer that. */
192                SHA_CTX sha;
193                char hash_hex[41];
194                unsigned char hash[20];
195                int i;
196               
197                shaInit( &sha );
198                shaUpdate( &sha, (unsigned char*) s, strlen( s ) );
199                shaUpdate( &sha, (unsigned char*) gc->acc->pass, strlen( gc->acc->pass ) );
200                shaFinal( &sha, hash );
201               
202                for( i = 0; i < 20; i ++ )
203                        sprintf( hash_hex + i * 2, "%02x", hash[i] );
204               
205                xt_add_child( reply, xt_new_node( "digest", hash_hex, NULL ) );
206        }
207        else if( xt_find_node( query->children, "password" ) )
208        {
209                /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
210                xt_add_child( reply, xt_new_node( "password", gc->acc->pass, NULL ) );
211        }
212        else
213        {
214                xt_free_node( reply );
215               
216                hide_login_progress( gc, "Can't find suitable authentication method" );
217                signoff( gc );
218                return XT_ABORT;
219        }
220       
221        reply = jabber_make_packet( "iq", "set", NULL, reply );
222        jabber_cache_add( gc, reply, jabber_finish_iq_auth );
223        st = jabber_write_packet( gc, reply );
224       
225        return st ? XT_HANDLED : XT_ABORT;
226}
227
228static xt_status jabber_finish_iq_auth( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
229{
230        char *type = xt_find_attr( node, "type" );
231        struct jabber_data *jd = gc->proto_data;
232       
233        if( strcmp( type, "error" ) == 0 )
234        {
235                hide_login_progress( gc, "Authentication failure" );
236                signoff( gc );
237                return XT_ABORT;
238        }
239        else if( strcmp( type, "result" ) == 0 )
240        {
241                /* This happens when we just successfully authenticated the
242                   old (non-SASL) way. */
243                jd->flags |= JFLAG_AUTHENTICATED;
244                if( !jabber_get_roster( gc ) )
245                        return XT_ABORT;
246        }
247       
248        return XT_HANDLED;
249}
250
251xt_status jabber_pkt_bind_sess( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
252{
253        struct jabber_data *jd = gc->proto_data;
254        struct xt_node *c;
255        char *s;
256       
257        if( ( c = xt_find_node( node->children, "bind" ) ) )
258        {
259                c = xt_find_node( c->children, "jid" );
260                if( c && c->text_len && ( s = strchr( c->text, '/' ) ) &&
261                    strcmp( s + 1, set_getstr( &gc->acc->set, "resource" ) ) != 0 )
262                        serv_got_crap( gc, "Server changed session resource string to `%s'", s + 1 );
263               
264                jd->flags &= ~JFLAG_WAIT_BIND;
265        }
266        else
267        {
268                jd->flags &= ~JFLAG_WAIT_SESSION;
269        }
270       
271        if( ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
272        {
273                if( !jabber_get_roster( gc ) )
274                        return XT_ABORT;
275        }
276       
277        return XT_HANDLED;
278}
279
280static xt_status jabber_parse_roster( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
281
282int jabber_get_roster( struct gaim_connection *gc )
283{
284        struct xt_node *node;
285        int st;
286       
287        set_login_progress( gc, 1, "Authenticated, requesting buddy list" );
288       
289        node = xt_new_node( "query", NULL, NULL );
290        xt_add_attr( node, "xmlns", "jabber:iq:roster" );
291        node = jabber_make_packet( "iq", "get", NULL, node );
292       
293        jabber_cache_add( gc, node, jabber_parse_roster );
294        st = jabber_write_packet( gc, node );
295       
296        return st;
297}
298
299static xt_status jabber_parse_roster( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
300{
301        struct xt_node *query, *c;
302       
303        query = xt_find_node( node->children, "query" );
304       
305        c = query->children;
306        while( ( c = xt_find_node( c, "item" ) ) )
307        {
308                char *jid = xt_find_attr( c, "jid" );
309                char *name = xt_find_attr( c, "name" );
310                char *sub = xt_find_attr( c, "subscription" );
311               
312                if( jid && sub && ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) )
313                        add_buddy( gc, NULL, jid, name );
314               
315                c = c->next;
316        }
317       
318        account_online( gc );
319       
320        return XT_HANDLED;
321}
322
323static xt_status jabber_iq_display_vcard( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig );
324
325int jabber_get_vcard( struct gaim_connection *gc, char *bare_jid )
326{
327        struct xt_node *node;
328       
329        if( strchr( bare_jid, '/' ) )
330                return 1;       /* This was an error, but return 0 should only be done if the connection died... */
331       
332        node = xt_new_node( "vCard", NULL, NULL );
333        xt_add_attr( node, "xmlns", "vcard-temp" );
334        node = jabber_make_packet( "iq", "get", bare_jid, node );
335       
336        jabber_cache_add( gc, node, jabber_iq_display_vcard );
337        return jabber_write_packet( gc, node );
338}
339
340static xt_status jabber_iq_display_vcard( struct gaim_connection *gc, struct xt_node *node, struct xt_node *orig )
341{
342        struct xt_node *vc, *c, *sc; /* subchild, gc is already in use ;-) */
343        GString *reply;
344        char *s;
345       
346        if( ( s = xt_find_attr( node, "type" ) ) == NULL ||
347            strcmp( s, "result" ) != 0 ||
348            ( vc = xt_find_node( node->children, "vCard" ) ) == NULL )
349        {
350                s = xt_find_attr( orig, "to" ); /* If this returns NULL something's wrong.. */
351                serv_got_crap( gc, "Could not retrieve vCard of %s", s ? s : "(NULL)" );
352                return XT_HANDLED;
353        }
354       
355        s = xt_find_attr( orig, "to" );
356        reply = g_string_new( "vCard information for " );
357        reply = g_string_append( reply, s ? s : "(NULL)" );
358        reply = g_string_append( reply, ":\n" );
359       
360        /* I hate this format, I really do... */
361       
362        if( ( c = xt_find_node( vc->children, "FN" ) ) && c->text_len )
363                g_string_append_printf( reply, "Name: %s\n", c->text );
364       
365        if( ( c = xt_find_node( vc->children, "N" ) ) && c->children )
366        {
367                reply = g_string_append( reply, "Full name:" );
368               
369                if( ( sc = xt_find_node( c->children, "PREFIX" ) ) && sc->text_len )
370                        g_string_append_printf( reply, " %s", sc->text );
371                if( ( sc = xt_find_node( c->children, "GIVEN" ) ) && sc->text_len )
372                        g_string_append_printf( reply, " %s", sc->text );
373                if( ( sc = xt_find_node( c->children, "MIDDLE" ) ) && sc->text_len )
374                        g_string_append_printf( reply, " %s", sc->text );
375                if( ( sc = xt_find_node( c->children, "FAMILY" ) ) && sc->text_len )
376                        g_string_append_printf( reply, " %s", sc->text );
377                if( ( sc = xt_find_node( c->children, "SUFFIX" ) ) && sc->text_len )
378                        g_string_append_printf( reply, " %s", sc->text );
379               
380                reply = g_string_append_c( reply, '\n' );
381        }
382       
383        if( ( c = xt_find_node( vc->children, "NICKNAME" ) ) && c->text_len )
384                g_string_append_printf( reply, "Nickname: %s\n", c->text );
385       
386        if( ( c = xt_find_node( vc->children, "BDAY" ) ) && c->text_len )
387                g_string_append_printf( reply, "Date of birth: %s\n", c->text );
388       
389        /* Slightly alternative use of for... ;-) */
390        for( c = vc->children; ( c = xt_find_node( c, "EMAIL" ) ); c = c->next )
391        {
392                if( ( sc = xt_find_node( c->children, "USERID" ) ) == NULL || sc->text_len == 0 )
393                        continue;
394               
395                if( xt_find_node( c->children, "HOME" ) )
396                        s = "Home";
397                else if( xt_find_node( c->children, "WORK" ) )
398                        s = "Work";
399                else
400                        s = "Misc.";
401               
402                g_string_append_printf( reply, "%s e-mail address: %s\n", s, sc->text );
403        }
404       
405        if( ( c = xt_find_node( vc->children, "URL" ) ) && c->text_len )
406                g_string_append_printf( reply, "Homepage: %s\n", c->text );
407       
408        /* Slightly alternative use of for... ;-) */
409        for( c = vc->children; ( c = xt_find_node( c, "ADR" ) ); c = c->next )
410        {
411                if( xt_find_node( c->children, "HOME" ) )
412                        s = "Home";
413                else if( xt_find_node( c->children, "WORK" ) )
414                        s = "Work";
415                else
416                        s = "Misc.";
417               
418                g_string_append_printf( reply, "%s address: ", s );
419               
420                if( ( sc = xt_find_node( c->children, "STREET" ) ) && sc->text_len )
421                        g_string_append_printf( reply, "%s ", sc->text );
422                if( ( sc = xt_find_node( c->children, "EXTADR" ) ) && sc->text_len )
423                        g_string_append_printf( reply, "%s, ", sc->text );
424                if( ( sc = xt_find_node( c->children, "PCODE" ) ) && sc->text_len )
425                        g_string_append_printf( reply, "%s, ", sc->text );
426                if( ( sc = xt_find_node( c->children, "LOCALITY" ) ) && sc->text_len )
427                        g_string_append_printf( reply, "%s, ", sc->text );
428                if( ( sc = xt_find_node( c->children, "REGION" ) ) && sc->text_len )
429                        g_string_append_printf( reply, "%s, ", sc->text );
430                if( ( sc = xt_find_node( c->children, "CTRY" ) ) && sc->text_len )
431                        g_string_append_printf( reply, "%s", sc->text );
432               
433                if( reply->str[reply->len-2] == ',' )
434                        reply = g_string_truncate( reply, reply->len-2 );
435               
436                reply = g_string_append_c( reply, '\n' );
437        }
438       
439        for( c = vc->children; ( c = xt_find_node( c, "TEL" ) ); c = c->next )
440        {
441                if( ( sc = xt_find_node( c->children, "NUMBER" ) ) == NULL || sc->text_len == 0 )
442                        continue;
443               
444                if( xt_find_node( c->children, "HOME" ) )
445                        s = "Home";
446                else if( xt_find_node( c->children, "WORK" ) )
447                        s = "Work";
448                else
449                        s = "Misc.";
450               
451                g_string_append_printf( reply, "%s phone number: %s\n", s, sc->text );
452        }
453       
454        if( ( c = xt_find_node( vc->children, "DESC" ) ) && c->text_len )
455                g_string_append_printf( reply, "Other information:\n%s", c->text );
456       
457        /* *sigh* */
458       
459        serv_got_crap( gc, reply->str );
460        g_string_free( reply, TRUE );
461       
462        return XT_HANDLED;
463}
464
465int jabber_add_to_roster( struct gaim_connection *gc, char *handle, char *name )
466{
467        struct xt_node *node;
468        int st;
469       
470        /* Build the item entry */
471        node = xt_new_node( "item", NULL, NULL );
472        xt_add_attr( node, "jid", handle );
473        if( name )
474                xt_add_attr( node, "name", name );
475       
476        /* And pack it into a roster-add packet */
477        node = xt_new_node( "query", NULL, node );
478        xt_add_attr( node, "xmlns", "jabber:iq:roster" );
479        node = jabber_make_packet( "iq", "set", NULL, node );
480       
481        st = jabber_write_packet( gc, node );
482       
483        xt_free_node( node );
484        return st;
485}
486
487int jabber_remove_from_roster( struct gaim_connection *gc, char *handle )
488{
489        struct xt_node *node;
490        int st;
491       
492        /* Build the item entry */
493        node = xt_new_node( "item", NULL, NULL );
494        xt_add_attr( node, "jid", handle );
495        xt_add_attr( node, "subscription", "remove" );
496       
497        /* And pack it into a roster-add packet */
498        node = xt_new_node( "query", NULL, node );
499        xt_add_attr( node, "xmlns", "jabber:iq:roster" );
500        node = jabber_make_packet( "iq", "set", NULL, node );
501       
502        st = jabber_write_packet( gc, node );
503       
504        xt_free_node( node );
505        return st;
506}
Note: See TracBrowser for help on using the repository browser.