source: protocols/jabber/iq.c @ 18da20b

Last change on this file since 18da20b was f5d87ea, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-05T18:26:17Z

Pick up group changes in the middle of a Jabber session, now that we care
about that info.

  • Property mode set to 100644
File size: 23.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#include "sha1.h"
26
27static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
28static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
29
30xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )
31{
32        struct im_connection *ic = data;
33        struct xt_node *c, *reply = NULL;
34        char *type, *s;
35        int st, pack = 1;
36       
37        type = xt_find_attr( node, "type" );
38       
39        if( !type )
40        {
41                imcb_error( ic, "Received IQ packet without type." );
42                imc_logout( ic, TRUE );
43                return XT_ABORT;
44        }
45       
46        if( strcmp( type, "result" ) == 0 || strcmp( type, "error" ) == 0 )
47        {
48                return jabber_cache_handle_packet( ic, node );
49        }
50        else if( strcmp( type, "get" ) == 0 )
51        {
52                if( !( ( c = xt_find_node( node->children, "query" ) ) ||
53                       ( c = xt_find_node( node->children, "ping" ) ) ) ||
54                    !( s = xt_find_attr( c, "xmlns" ) ) )
55                {
56                        /* Sigh. Who decided to suddenly invent new elements
57                           instead of just sticking with <query/>? */
58                        return XT_HANDLED;
59                }
60               
61                reply = xt_new_node( "query", NULL, NULL );
62                xt_add_attr( reply, "xmlns", s );
63               
64                /* Of course this is a very essential query to support. ;-) */
65                if( strcmp( s, XMLNS_VERSION ) == 0 )
66                {
67                        xt_add_child( reply, xt_new_node( "name", "BitlBee", NULL ) );
68                        xt_add_child( reply, xt_new_node( "version", BITLBEE_VERSION, NULL ) );
69                        xt_add_child( reply, xt_new_node( "os", ARCH, NULL ) );
70                }
71                else if( strcmp( s, XMLNS_TIME ) == 0 )
72                {
73                        time_t time_ep;
74                        char buf[1024];
75                       
76                        buf[sizeof(buf)-1] = 0;
77                        time_ep = time( NULL );
78                       
79                        strftime( buf, sizeof( buf ) - 1, "%Y%m%dT%H:%M:%S", gmtime( &time_ep ) );
80                        xt_add_child( reply, xt_new_node( "utc", buf, NULL ) );
81                       
82                        strftime( buf, sizeof( buf ) - 1, "%Z", localtime( &time_ep ) );
83                        xt_add_child( reply, xt_new_node( "tz", buf, NULL ) );
84                }
85                else if( strcmp( s, XMLNS_PING ) == 0 )
86                {
87                        xt_free_node( reply );
88                        reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), NULL );
89                        if( ( s = xt_find_attr( node, "id" ) ) )
90                                xt_add_attr( reply, "id", s );
91                        pack = 0;
92                }
93                else if( strcmp( s, XMLNS_DISCO_INFO ) == 0 )
94                {
95                        const char *features[] = { XMLNS_DISCO_INFO,
96                                                   XMLNS_VERSION,
97                                                   XMLNS_TIME,
98                                                   XMLNS_CHATSTATES,
99                                                   XMLNS_MUC,
100                                                   XMLNS_PING,
101                                                   XMLNS_SI,
102                                                   XMLNS_BYTESTREAMS,
103                                                   XMLNS_FILETRANSFER,
104                                                   NULL };
105                        const char **f;
106                       
107                        c = xt_new_node( "identity", NULL, NULL );
108                        xt_add_attr( c, "category", "client" );
109                        xt_add_attr( c, "type", "pc" );
110                        xt_add_attr( c, "name", "BitlBee" );
111                        xt_add_child( reply, c );
112                       
113                        for( f = features; *f; f ++ )
114                        {
115                                c = xt_new_node( "feature", NULL, NULL );
116                                xt_add_attr( c, "var", *f );
117                                xt_add_child( reply, c );
118                        }
119                }
120                else
121                {
122                        xt_free_node( reply );
123                        reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL );
124                        pack = 0;
125                }
126        }
127        else if( strcmp( type, "set" ) == 0 )
128        {
129                if( ( c = xt_find_node( node->children, "si" ) ) &&
130                    ( s = xt_find_attr( c, "xmlns" ) ) &&
131                    ( strcmp( s, XMLNS_SI ) == 0 ) )
132                {
133                        return jabber_si_handle_request( ic, node, c );
134                }
135                else if( !( c = xt_find_node( node->children, "query" ) ) ||
136                         !( s = xt_find_attr( c, "xmlns" ) ) )
137                {
138                        return XT_HANDLED;
139                }
140                else if( strcmp( s, XMLNS_ROSTER ) == 0 )
141                {
142                /* This is a roster push. XMPP servers send this when someone
143                   was added to (or removed from) the buddy list. AFAIK they're
144                   sent even if we added this buddy in our own session. */
145                        int bare_len = strlen( ic->acc->user );
146                       
147                        if( ( s = xt_find_attr( node, "from" ) ) == NULL ||
148                            ( strncmp( s, ic->acc->user, bare_len ) == 0 &&
149                              ( s[bare_len] == 0 || s[bare_len] == '/' ) ) )
150                        {
151                                jabber_parse_roster( ic, node, NULL );
152                               
153                                /* Should we generate a reply here? Don't think it's
154                                   very important... */
155                        }
156                        else
157                        {
158                                imcb_log( ic, "Warning: %s tried to fake a roster push!", s ? s : "(unknown)" );
159                               
160                                xt_free_node( reply );
161                                reply = jabber_make_error_packet( node, "not-allowed", "cancel", NULL );
162                                pack = 0;
163                        }
164                }
165                else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 )
166                {
167                        /* Bytestream Request (stage 2 of file transfer) */
168                        return jabber_bs_recv_request( ic, node, c );
169                }
170                else
171                {
172                        xt_free_node( reply );
173                        reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL );
174                        pack = 0;
175                }
176        }
177       
178        /* If we recognized the xmlns and managed to generate a reply,
179           finish and send it. */
180        if( reply )
181        {
182                /* Normally we still have to pack it into an iq-result
183                   packet, but for errors, for example, we don't. */
184                if( pack )
185                {
186                        reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), reply );
187                        if( ( s = xt_find_attr( node, "id" ) ) )
188                                xt_add_attr( reply, "id", s );
189                }
190               
191                st = jabber_write_packet( ic, reply );
192                xt_free_node( reply );
193                if( !st )
194                        return XT_ABORT;
195        }
196       
197        return XT_HANDLED;
198}
199
200static xt_status jabber_do_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
201static xt_status jabber_finish_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
202
203int jabber_init_iq_auth( struct im_connection *ic )
204{
205        struct jabber_data *jd = ic->proto_data;
206        struct xt_node *node;
207        int st;
208       
209        node = xt_new_node( "query", NULL, xt_new_node( "username", jd->username, NULL ) );
210        xt_add_attr( node, "xmlns", XMLNS_AUTH );
211        node = jabber_make_packet( "iq", "get", NULL, node );
212       
213        jabber_cache_add( ic, node, jabber_do_iq_auth );
214        st = jabber_write_packet( ic, node );
215       
216        return st;
217}
218
219static xt_status jabber_do_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
220{
221        struct jabber_data *jd = ic->proto_data;
222        struct xt_node *reply, *query;
223        xt_status st;
224        char *s;
225       
226        if( !( query = xt_find_node( node->children, "query" ) ) )
227        {
228                imcb_log( ic, "Warning: Received incomplete IQ packet while authenticating" );
229                imc_logout( ic, FALSE );
230                return XT_HANDLED;
231        }
232       
233        /* Time to authenticate ourselves! */
234        reply = xt_new_node( "query", NULL, NULL );
235        xt_add_attr( reply, "xmlns", XMLNS_AUTH );
236        xt_add_child( reply, xt_new_node( "username", jd->username, NULL ) );
237        xt_add_child( reply, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
238       
239        if( xt_find_node( query->children, "digest" ) && ( s = xt_find_attr( jd->xt->root, "id" ) ) )
240        {
241                /* We can do digest authentication, it seems, and of
242                   course we prefer that. */
243                sha1_state_t sha;
244                char hash_hex[41];
245                unsigned char hash[20];
246                int i;
247               
248                sha1_init( &sha );
249                sha1_append( &sha, (unsigned char*) s, strlen( s ) );
250                sha1_append( &sha, (unsigned char*) ic->acc->pass, strlen( ic->acc->pass ) );
251                sha1_finish( &sha, hash );
252               
253                for( i = 0; i < 20; i ++ )
254                        sprintf( hash_hex + i * 2, "%02x", hash[i] );
255               
256                xt_add_child( reply, xt_new_node( "digest", hash_hex, NULL ) );
257        }
258        else if( xt_find_node( query->children, "password" ) )
259        {
260                /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
261                xt_add_child( reply, xt_new_node( "password", ic->acc->pass, NULL ) );
262        }
263        else
264        {
265                xt_free_node( reply );
266               
267                imcb_error( ic, "Can't find suitable authentication method" );
268                imc_logout( ic, FALSE );
269                return XT_ABORT;
270        }
271       
272        reply = jabber_make_packet( "iq", "set", NULL, reply );
273        jabber_cache_add( ic, reply, jabber_finish_iq_auth );
274        st = jabber_write_packet( ic, reply );
275       
276        return st ? XT_HANDLED : XT_ABORT;
277}
278
279static xt_status jabber_finish_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
280{
281        struct jabber_data *jd = ic->proto_data;
282        char *type;
283       
284        if( !( type = xt_find_attr( node, "type" ) ) )
285        {
286                imcb_log( ic, "Warning: Received incomplete IQ packet while authenticating" );
287                imc_logout( ic, FALSE );
288                return XT_HANDLED;
289        }
290       
291        if( strcmp( type, "error" ) == 0 )
292        {
293                imcb_error( ic, "Authentication failure" );
294                imc_logout( ic, FALSE );
295                return XT_ABORT;
296        }
297        else if( strcmp( type, "result" ) == 0 )
298        {
299                /* This happens when we just successfully authenticated the
300                   old (non-SASL) way. */
301                jd->flags |= JFLAG_AUTHENTICATED;
302                if( !jabber_get_roster( ic ) )
303                        return XT_ABORT;
304        }
305       
306        return XT_HANDLED;
307}
308
309xt_status jabber_pkt_bind_sess( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
310{
311        struct jabber_data *jd = ic->proto_data;
312        struct xt_node *c, *reply = NULL;
313        char *s;
314       
315        if( node && ( c = xt_find_node( node->children, "bind" ) ) )
316        {
317                c = xt_find_node( c->children, "jid" );
318                if( c && c->text_len && ( s = strchr( c->text, '/' ) ) &&
319                    strcmp( s + 1, set_getstr( &ic->acc->set, "resource" ) ) != 0 )
320                        imcb_log( ic, "Server changed session resource string to `%s'", s + 1 );
321        }
322       
323        if( jd->flags & JFLAG_WANT_BIND )
324        {
325                reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
326                xt_add_attr( reply, "xmlns", XMLNS_BIND );
327                jd->flags &= ~JFLAG_WANT_BIND;
328        }
329        else if( jd->flags & JFLAG_WANT_SESSION )
330        {
331                reply = xt_new_node( "session", NULL, NULL );
332                xt_add_attr( reply, "xmlns", XMLNS_SESSION );
333                jd->flags &= ~JFLAG_WANT_SESSION;
334        }
335       
336        if( reply != NULL )
337        {
338                reply = jabber_make_packet( "iq", "set", NULL, reply );
339                jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
340               
341                if( !jabber_write_packet( ic, reply ) )
342                        return XT_ABORT;
343        }
344        else if( ( jd->flags & ( JFLAG_WANT_BIND | JFLAG_WANT_SESSION ) ) == 0 )
345        {
346                if( !jabber_get_roster( ic ) )
347                        return XT_ABORT;
348        }
349       
350        return XT_HANDLED;
351}
352
353int jabber_get_roster( struct im_connection *ic )
354{
355        struct xt_node *node;
356        int st;
357       
358        imcb_log( ic, "Authenticated, requesting buddy list" );
359       
360        node = xt_new_node( "query", NULL, NULL );
361        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
362        node = jabber_make_packet( "iq", "get", NULL, node );
363       
364        jabber_cache_add( ic, node, jabber_parse_roster );
365        st = jabber_write_packet( ic, node );
366       
367        return st;
368}
369
370static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
371{
372        struct xt_node *query, *c;
373        int initial = ( orig != NULL );
374       
375        if( !( query = xt_find_node( node->children, "query" ) ) )
376        {
377                imcb_log( ic, "Warning: Received NULL roster packet" );
378                return XT_HANDLED;
379        }
380       
381        c = query->children;
382        while( ( c = xt_find_node( c, "item" ) ) )
383        {
384                struct xt_node *group = xt_find_node( c->children, "group" );
385                char *jid = xt_find_attr( c, "jid" );
386                char *name = xt_find_attr( c, "name" );
387                char *sub = xt_find_attr( c, "subscription" );
388               
389                if( jid && sub )
390                {
391                        if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) )
392                        {
393                                imcb_add_buddy( ic, jid, ( group && group->text_len ) ?
394                                                           group->text : NULL );
395                               
396                                if( name )
397                                        imcb_rename_buddy( ic, jid, name );
398                        }
399                        else if( strcmp( sub, "remove" ) == 0 )
400                        {
401                                jabber_buddy_remove_bare( ic, jid );
402                                imcb_remove_buddy( ic, jid, NULL );
403                        }
404                }
405               
406                c = c->next;
407        }
408       
409        if( initial )
410                imcb_connected( ic );
411       
412        return XT_HANDLED;
413}
414
415int jabber_get_vcard( struct im_connection *ic, char *bare_jid )
416{
417        struct xt_node *node;
418       
419        if( strchr( bare_jid, '/' ) )
420                return 1;       /* This was an error, but return 0 should only be done if the connection died... */
421       
422        node = xt_new_node( "vCard", NULL, NULL );
423        xt_add_attr( node, "xmlns", XMLNS_VCARD );
424        node = jabber_make_packet( "iq", "get", bare_jid, node );
425       
426        jabber_cache_add( ic, node, jabber_iq_display_vcard );
427        return jabber_write_packet( ic, node );
428}
429
430static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
431{
432        struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
433        GString *reply;
434        char *s;
435       
436        if( ( s = xt_find_attr( node, "type" ) ) == NULL ||
437            strcmp( s, "result" ) != 0 ||
438            ( vc = xt_find_node( node->children, "vCard" ) ) == NULL )
439        {
440                s = xt_find_attr( orig, "to" ); /* If this returns NULL something's wrong.. */
441                imcb_log( ic, "Could not retrieve vCard of %s", s ? s : "(NULL)" );
442                return XT_HANDLED;
443        }
444       
445        s = xt_find_attr( orig, "to" );
446        reply = g_string_new( "vCard information for " );
447        reply = g_string_append( reply, s ? s : "(NULL)" );
448        reply = g_string_append( reply, ":\n" );
449       
450        /* I hate this format, I really do... */
451       
452        if( ( c = xt_find_node( vc->children, "FN" ) ) && c->text_len )
453                g_string_append_printf( reply, "Name: %s\n", c->text );
454       
455        if( ( c = xt_find_node( vc->children, "N" ) ) && c->children )
456        {
457                reply = g_string_append( reply, "Full name:" );
458               
459                if( ( sc = xt_find_node( c->children, "PREFIX" ) ) && sc->text_len )
460                        g_string_append_printf( reply, " %s", sc->text );
461                if( ( sc = xt_find_node( c->children, "GIVEN" ) ) && sc->text_len )
462                        g_string_append_printf( reply, " %s", sc->text );
463                if( ( sc = xt_find_node( c->children, "MIDDLE" ) ) && sc->text_len )
464                        g_string_append_printf( reply, " %s", sc->text );
465                if( ( sc = xt_find_node( c->children, "FAMILY" ) ) && sc->text_len )
466                        g_string_append_printf( reply, " %s", sc->text );
467                if( ( sc = xt_find_node( c->children, "SUFFIX" ) ) && sc->text_len )
468                        g_string_append_printf( reply, " %s", sc->text );
469               
470                reply = g_string_append_c( reply, '\n' );
471        }
472       
473        if( ( c = xt_find_node( vc->children, "NICKNAME" ) ) && c->text_len )
474                g_string_append_printf( reply, "Nickname: %s\n", c->text );
475       
476        if( ( c = xt_find_node( vc->children, "BDAY" ) ) && c->text_len )
477                g_string_append_printf( reply, "Date of birth: %s\n", c->text );
478       
479        /* Slightly alternative use of for... ;-) */
480        for( c = vc->children; ( c = xt_find_node( c, "EMAIL" ) ); c = c->next )
481        {
482                if( ( sc = xt_find_node( c->children, "USERID" ) ) == NULL || sc->text_len == 0 )
483                        continue;
484               
485                if( xt_find_node( c->children, "HOME" ) )
486                        s = "Home";
487                else if( xt_find_node( c->children, "WORK" ) )
488                        s = "Work";
489                else
490                        s = "Misc.";
491               
492                g_string_append_printf( reply, "%s e-mail address: %s\n", s, sc->text );
493        }
494       
495        if( ( c = xt_find_node( vc->children, "URL" ) ) && c->text_len )
496                g_string_append_printf( reply, "Homepage: %s\n", c->text );
497       
498        /* Slightly alternative use of for... ;-) */
499        for( c = vc->children; ( c = xt_find_node( c, "ADR" ) ); c = c->next )
500        {
501                if( xt_find_node( c->children, "HOME" ) )
502                        s = "Home";
503                else if( xt_find_node( c->children, "WORK" ) )
504                        s = "Work";
505                else
506                        s = "Misc.";
507               
508                g_string_append_printf( reply, "%s address: ", s );
509               
510                if( ( sc = xt_find_node( c->children, "STREET" ) ) && sc->text_len )
511                        g_string_append_printf( reply, "%s ", sc->text );
512                if( ( sc = xt_find_node( c->children, "EXTADR" ) ) && sc->text_len )
513                        g_string_append_printf( reply, "%s, ", sc->text );
514                if( ( sc = xt_find_node( c->children, "PCODE" ) ) && sc->text_len )
515                        g_string_append_printf( reply, "%s, ", sc->text );
516                if( ( sc = xt_find_node( c->children, "LOCALITY" ) ) && sc->text_len )
517                        g_string_append_printf( reply, "%s, ", sc->text );
518                if( ( sc = xt_find_node( c->children, "REGION" ) ) && sc->text_len )
519                        g_string_append_printf( reply, "%s, ", sc->text );
520                if( ( sc = xt_find_node( c->children, "CTRY" ) ) && sc->text_len )
521                        g_string_append_printf( reply, "%s", sc->text );
522               
523                if( reply->str[reply->len-2] == ',' )
524                        reply = g_string_truncate( reply, reply->len-2 );
525               
526                reply = g_string_append_c( reply, '\n' );
527        }
528       
529        for( c = vc->children; ( c = xt_find_node( c, "TEL" ) ); c = c->next )
530        {
531                if( ( sc = xt_find_node( c->children, "NUMBER" ) ) == NULL || sc->text_len == 0 )
532                        continue;
533               
534                if( xt_find_node( c->children, "HOME" ) )
535                        s = "Home";
536                else if( xt_find_node( c->children, "WORK" ) )
537                        s = "Work";
538                else
539                        s = "Misc.";
540               
541                g_string_append_printf( reply, "%s phone number: %s\n", s, sc->text );
542        }
543       
544        if( ( c = xt_find_node( vc->children, "DESC" ) ) && c->text_len )
545                g_string_append_printf( reply, "Other information:\n%s", c->text );
546       
547        /* *sigh* */
548       
549        imcb_log( ic, "%s", reply->str );
550        g_string_free( reply, TRUE );
551       
552        return XT_HANDLED;
553}
554
555static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
556
557int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name )
558{
559        struct xt_node *node;
560        int st;
561       
562        /* Build the item entry */
563        node = xt_new_node( "item", NULL, NULL );
564        xt_add_attr( node, "jid", handle );
565        if( name )
566                xt_add_attr( node, "name", name );
567       
568        /* And pack it into a roster-add packet */
569        node = xt_new_node( "query", NULL, node );
570        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
571        node = jabber_make_packet( "iq", "set", NULL, node );
572        jabber_cache_add( ic, node, jabber_add_to_roster_callback );
573       
574        st = jabber_write_packet( ic, node );
575       
576        return st;
577}
578
579static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
580{
581        char *s, *jid = NULL;
582        struct xt_node *c;
583       
584        if( ( c = xt_find_node( orig->children, "query" ) ) &&
585            ( c = xt_find_node( c->children, "item" ) ) &&
586            ( jid = xt_find_attr( c, "jid" ) ) &&
587            ( s = xt_find_attr( node, "type" ) ) &&
588            strcmp( s, "result" ) == 0 )
589        {
590                if( bee_user_by_handle( ic->bee, ic, jid ) == NULL )
591                        imcb_add_buddy( ic, jid, NULL );
592        }
593        else
594        {
595                imcb_log( ic, "Error while adding `%s' to your contact list.",
596                          jid ? jid : "(unknown handle)" );
597        }
598       
599        return XT_HANDLED;
600}
601
602int jabber_remove_from_roster( struct im_connection *ic, char *handle )
603{
604        struct xt_node *node;
605        int st;
606       
607        /* Build the item entry */
608        node = xt_new_node( "item", NULL, NULL );
609        xt_add_attr( node, "jid", handle );
610        xt_add_attr( node, "subscription", "remove" );
611       
612        /* And pack it into a roster-add packet */
613        node = xt_new_node( "query", NULL, node );
614        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
615        node = jabber_make_packet( "iq", "set", NULL, node );
616       
617        st = jabber_write_packet( ic, node );
618       
619        xt_free_node( node );
620        return st;
621}
622
623xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
624
625xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid )
626{
627        struct xt_node *node, *query;
628        struct jabber_buddy *bud;
629       
630        if( ( bud = jabber_buddy_by_jid( ic, bare_jid , 0 ) ) == NULL )
631        {
632                /* Who cares about the unknown... */
633                imcb_log( ic, "Couldn't find buddy: %s", bare_jid);
634                return XT_HANDLED;
635        }
636       
637        if( bud->features ) /* been here already */
638                return XT_HANDLED;
639       
640        node = xt_new_node( "query", NULL, NULL );
641        xt_add_attr( node, "xmlns", XMLNS_DISCO_INFO );
642       
643        if( !( query = jabber_make_packet( "iq", "get", bare_jid, node ) ) )
644        {
645                imcb_log( ic, "WARNING: Couldn't generate feature query" );
646                xt_free_node( node );
647                return XT_HANDLED;
648        }
649
650        jabber_cache_add( ic, query, jabber_iq_parse_features );
651
652        return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT;
653}
654
655xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
656{
657        struct xt_node *c;
658        struct jabber_buddy *bud;
659        char *feature, *xmlns, *from;
660
661        if( !( from = xt_find_attr( node, "from" ) ) ||
662            !( c = xt_find_node( node->children, "query" ) ) ||
663            !( xmlns = xt_find_attr( c, "xmlns" ) ) ||
664            !( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) )
665        {
666                imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" );
667                return XT_HANDLED;
668        }
669        if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL )
670        {
671                /* Who cares about the unknown... */
672                imcb_log( ic, "Couldn't find buddy: %s", from );
673                return XT_HANDLED;
674        }
675       
676        c = c->children;
677        while( ( c = xt_find_node( c, "feature" ) ) )
678        {
679                feature = xt_find_attr( c, "var" );
680                if( feature )
681                        bud->features = g_slist_append( bud->features, g_strdup( feature ) );
682                c = c->next;
683        }
684
685        return XT_HANDLED;
686}
687
688xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
689
690xt_status jabber_iq_query_server( struct im_connection *ic, char *jid, char *xmlns )
691{
692        struct xt_node *node, *query;
693        struct jabber_data *jd = ic->proto_data;
694       
695        node = xt_new_node( "query", NULL, NULL );
696        xt_add_attr( node, "xmlns", xmlns );
697       
698        if( !( query = jabber_make_packet( "iq", "get", jid, node ) ) )
699        {
700                imcb_log( ic, "WARNING: Couldn't generate server query" );
701                xt_free_node( node );
702        }
703
704        jd->have_streamhosts--;
705        jabber_cache_add( ic, query, jabber_iq_parse_server_features );
706
707        return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT;
708}
709
710/*
711 * Query the server for "items", query each "item" for identities, query each "item" that's a proxy for it's bytestream info
712 */
713xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
714{
715        struct xt_node *c;
716        struct jabber_data *jd = ic->proto_data;
717        char *xmlns, *from;
718
719        if( !( c = xt_find_node( node->children, "query" ) ) ||
720            !( from = xt_find_attr( node, "from" ) ) ||
721            !( xmlns = xt_find_attr( c, "xmlns" ) ) )
722        {
723                imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" );
724                return XT_HANDLED;
725        }
726
727        jd->have_streamhosts++;
728
729        if( strcmp( xmlns, XMLNS_DISCO_ITEMS ) == 0 )
730        {
731                char *itemjid;
732
733                /* answer from server */
734       
735                c = c->children;
736                while( ( c = xt_find_node( c, "item" ) ) )
737                {
738                        itemjid = xt_find_attr( c, "jid" );
739                       
740                        if( itemjid )
741                                jabber_iq_query_server( ic, itemjid, XMLNS_DISCO_INFO );
742
743                        c = c->next;
744                }
745        }
746        else if( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 )
747        {
748                char *category, *type;
749
750                /* answer from potential proxy */
751
752                c = c->children;
753                while( ( c = xt_find_node( c, "identity" ) ) )
754                {
755                        category = xt_find_attr( c, "category" );
756                        type = xt_find_attr( c, "type" );
757
758                        if( type && ( strcmp( type, "bytestreams" ) == 0 ) &&
759                            category && ( strcmp( category, "proxy" ) == 0 ) )
760                                jabber_iq_query_server( ic, from, XMLNS_BYTESTREAMS );
761
762                        c = c->next;
763                }
764        }
765        else if( strcmp( xmlns, XMLNS_BYTESTREAMS ) == 0 )
766        {
767                char *host, *jid, *port_s;
768                int port;
769
770                /* answer from proxy */
771
772                if( ( c = xt_find_node( c->children, "streamhost" ) ) &&
773                    ( host = xt_find_attr( c, "host" ) ) &&
774                    ( port_s = xt_find_attr( c, "port" ) ) &&
775                    ( sscanf( port_s, "%d", &port ) == 1 ) &&
776                    ( jid = xt_find_attr( c, "jid" ) ) )
777                {
778                        jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 );
779                       
780                        sh->jid = g_strdup( jid );
781                        sh->host = g_strdup( host );
782                        g_snprintf( sh->port, sizeof( sh->port ), "%u", port );
783
784                        imcb_log( ic, "Proxy found: jid %s host %s port %u", jid, host, port );
785                        jd->streamhosts = g_slist_append( jd->streamhosts, sh );
786                }
787        }
788
789        if( jd->have_streamhosts == 0 )
790                jd->have_streamhosts++;
791
792        return XT_HANDLED;
793}
Note: See TracBrowser for help on using the repository browser.