source: protocols/jabber/iq.c @ 46d215d

Last change on this file since 46d215d was 46d215d, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-08T23:43:13Z

Allow moving contacts around between groups. Works with at least Jabber,
will check the others now.

  • Property mode set to 100644
File size: 23.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#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, const char *handle, const char *name, const char *group )
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        if( group )
568                xt_add_child( node, xt_new_node( "group", group, NULL ) );
569       
570        /* And pack it into a roster-add packet */
571        node = xt_new_node( "query", NULL, node );
572        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
573        node = jabber_make_packet( "iq", "set", NULL, node );
574        jabber_cache_add( ic, node, jabber_add_to_roster_callback );
575       
576        st = jabber_write_packet( ic, node );
577       
578        return st;
579}
580
581static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
582{
583        char *s, *jid = NULL;
584        struct xt_node *c;
585       
586        if( ( c = xt_find_node( orig->children, "query" ) ) &&
587            ( c = xt_find_node( c->children, "item" ) ) &&
588            ( jid = xt_find_attr( c, "jid" ) ) &&
589            ( s = xt_find_attr( node, "type" ) ) &&
590            strcmp( s, "result" ) == 0 )
591        {
592                if( bee_user_by_handle( ic->bee, ic, jid ) == NULL )
593                        imcb_add_buddy( ic, jid, NULL );
594        }
595        else
596        {
597                imcb_log( ic, "Error while adding `%s' to your contact list.",
598                          jid ? jid : "(unknown handle)" );
599        }
600       
601        return XT_HANDLED;
602}
603
604int jabber_remove_from_roster( struct im_connection *ic, char *handle )
605{
606        struct xt_node *node;
607        int st;
608       
609        /* Build the item entry */
610        node = xt_new_node( "item", NULL, NULL );
611        xt_add_attr( node, "jid", handle );
612        xt_add_attr( node, "subscription", "remove" );
613       
614        /* And pack it into a roster-add packet */
615        node = xt_new_node( "query", NULL, node );
616        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
617        node = jabber_make_packet( "iq", "set", NULL, node );
618       
619        st = jabber_write_packet( ic, node );
620       
621        xt_free_node( node );
622        return st;
623}
624
625xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
626
627xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid )
628{
629        struct xt_node *node, *query;
630        struct jabber_buddy *bud;
631       
632        if( ( bud = jabber_buddy_by_jid( ic, bare_jid , 0 ) ) == NULL )
633        {
634                /* Who cares about the unknown... */
635                imcb_log( ic, "Couldn't find buddy: %s", bare_jid);
636                return XT_HANDLED;
637        }
638       
639        if( bud->features ) /* been here already */
640                return XT_HANDLED;
641       
642        node = xt_new_node( "query", NULL, NULL );
643        xt_add_attr( node, "xmlns", XMLNS_DISCO_INFO );
644       
645        if( !( query = jabber_make_packet( "iq", "get", bare_jid, node ) ) )
646        {
647                imcb_log( ic, "WARNING: Couldn't generate feature query" );
648                xt_free_node( node );
649                return XT_HANDLED;
650        }
651
652        jabber_cache_add( ic, query, jabber_iq_parse_features );
653
654        return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT;
655}
656
657xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
658{
659        struct xt_node *c;
660        struct jabber_buddy *bud;
661        char *feature, *xmlns, *from;
662
663        if( !( from = xt_find_attr( node, "from" ) ) ||
664            !( c = xt_find_node( node->children, "query" ) ) ||
665            !( xmlns = xt_find_attr( c, "xmlns" ) ) ||
666            !( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) )
667        {
668                imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" );
669                return XT_HANDLED;
670        }
671        if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL )
672        {
673                /* Who cares about the unknown... */
674                imcb_log( ic, "Couldn't find buddy: %s", from );
675                return XT_HANDLED;
676        }
677       
678        c = c->children;
679        while( ( c = xt_find_node( c, "feature" ) ) )
680        {
681                feature = xt_find_attr( c, "var" );
682                if( feature )
683                        bud->features = g_slist_append( bud->features, g_strdup( feature ) );
684                c = c->next;
685        }
686
687        return XT_HANDLED;
688}
689
690xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
691
692xt_status jabber_iq_query_server( struct im_connection *ic, char *jid, char *xmlns )
693{
694        struct xt_node *node, *query;
695        struct jabber_data *jd = ic->proto_data;
696       
697        node = xt_new_node( "query", NULL, NULL );
698        xt_add_attr( node, "xmlns", xmlns );
699       
700        if( !( query = jabber_make_packet( "iq", "get", jid, node ) ) )
701        {
702                imcb_log( ic, "WARNING: Couldn't generate server query" );
703                xt_free_node( node );
704        }
705
706        jd->have_streamhosts--;
707        jabber_cache_add( ic, query, jabber_iq_parse_server_features );
708
709        return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT;
710}
711
712/*
713 * Query the server for "items", query each "item" for identities, query each "item" that's a proxy for it's bytestream info
714 */
715xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
716{
717        struct xt_node *c;
718        struct jabber_data *jd = ic->proto_data;
719        char *xmlns, *from;
720
721        if( !( c = xt_find_node( node->children, "query" ) ) ||
722            !( from = xt_find_attr( node, "from" ) ) ||
723            !( xmlns = xt_find_attr( c, "xmlns" ) ) )
724        {
725                imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" );
726                return XT_HANDLED;
727        }
728
729        jd->have_streamhosts++;
730
731        if( strcmp( xmlns, XMLNS_DISCO_ITEMS ) == 0 )
732        {
733                char *itemjid;
734
735                /* answer from server */
736       
737                c = c->children;
738                while( ( c = xt_find_node( c, "item" ) ) )
739                {
740                        itemjid = xt_find_attr( c, "jid" );
741                       
742                        if( itemjid )
743                                jabber_iq_query_server( ic, itemjid, XMLNS_DISCO_INFO );
744
745                        c = c->next;
746                }
747        }
748        else if( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 )
749        {
750                char *category, *type;
751
752                /* answer from potential proxy */
753
754                c = c->children;
755                while( ( c = xt_find_node( c, "identity" ) ) )
756                {
757                        category = xt_find_attr( c, "category" );
758                        type = xt_find_attr( c, "type" );
759
760                        if( type && ( strcmp( type, "bytestreams" ) == 0 ) &&
761                            category && ( strcmp( category, "proxy" ) == 0 ) )
762                                jabber_iq_query_server( ic, from, XMLNS_BYTESTREAMS );
763
764                        c = c->next;
765                }
766        }
767        else if( strcmp( xmlns, XMLNS_BYTESTREAMS ) == 0 )
768        {
769                char *host, *jid, *port_s;
770                int port;
771
772                /* answer from proxy */
773
774                if( ( c = xt_find_node( c->children, "streamhost" ) ) &&
775                    ( host = xt_find_attr( c, "host" ) ) &&
776                    ( port_s = xt_find_attr( c, "port" ) ) &&
777                    ( sscanf( port_s, "%d", &port ) == 1 ) &&
778                    ( jid = xt_find_attr( c, "jid" ) ) )
779                {
780                        jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 );
781                       
782                        sh->jid = g_strdup( jid );
783                        sh->host = g_strdup( host );
784                        g_snprintf( sh->port, sizeof( sh->port ), "%u", port );
785
786                        imcb_log( ic, "Proxy found: jid %s host %s port %u", jid, host, port );
787                        jd->streamhosts = g_slist_append( jd->streamhosts, sh );
788                }
789        }
790
791        if( jd->have_streamhosts == 0 )
792                jd->have_streamhosts++;
793
794        return XT_HANDLED;
795}
Note: See TracBrowser for help on using the repository browser.