source: protocols/jabber/iq.c @ 4469e7e

Last change on this file since 4469e7e was 704dd38, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-01T21:51:27Z

Merging head.

  • 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                                if( initial || bee_user_by_handle( ic->bee, ic, jid ) == NULL )
394                                        imcb_add_buddy( ic, jid, ( group && group->text_len ) ?
395                                                                   group->text : NULL );
396                               
397                                if( name )
398                                        imcb_rename_buddy( ic, jid, name );
399                        }
400                        else if( strcmp( sub, "remove" ) == 0 )
401                        {
402                                jabber_buddy_remove_bare( ic, jid );
403                                imcb_remove_buddy( ic, jid, NULL );
404                        }
405                }
406               
407                c = c->next;
408        }
409       
410        if( initial )
411                imcb_connected( ic );
412       
413        return XT_HANDLED;
414}
415
416int jabber_get_vcard( struct im_connection *ic, char *bare_jid )
417{
418        struct xt_node *node;
419       
420        if( strchr( bare_jid, '/' ) )
421                return 1;       /* This was an error, but return 0 should only be done if the connection died... */
422       
423        node = xt_new_node( "vCard", NULL, NULL );
424        xt_add_attr( node, "xmlns", XMLNS_VCARD );
425        node = jabber_make_packet( "iq", "get", bare_jid, node );
426       
427        jabber_cache_add( ic, node, jabber_iq_display_vcard );
428        return jabber_write_packet( ic, node );
429}
430
431static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
432{
433        struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
434        GString *reply;
435        char *s;
436       
437        if( ( s = xt_find_attr( node, "type" ) ) == NULL ||
438            strcmp( s, "result" ) != 0 ||
439            ( vc = xt_find_node( node->children, "vCard" ) ) == NULL )
440        {
441                s = xt_find_attr( orig, "to" ); /* If this returns NULL something's wrong.. */
442                imcb_log( ic, "Could not retrieve vCard of %s", s ? s : "(NULL)" );
443                return XT_HANDLED;
444        }
445       
446        s = xt_find_attr( orig, "to" );
447        reply = g_string_new( "vCard information for " );
448        reply = g_string_append( reply, s ? s : "(NULL)" );
449        reply = g_string_append( reply, ":\n" );
450       
451        /* I hate this format, I really do... */
452       
453        if( ( c = xt_find_node( vc->children, "FN" ) ) && c->text_len )
454                g_string_append_printf( reply, "Name: %s\n", c->text );
455       
456        if( ( c = xt_find_node( vc->children, "N" ) ) && c->children )
457        {
458                reply = g_string_append( reply, "Full name:" );
459               
460                if( ( sc = xt_find_node( c->children, "PREFIX" ) ) && sc->text_len )
461                        g_string_append_printf( reply, " %s", sc->text );
462                if( ( sc = xt_find_node( c->children, "GIVEN" ) ) && sc->text_len )
463                        g_string_append_printf( reply, " %s", sc->text );
464                if( ( sc = xt_find_node( c->children, "MIDDLE" ) ) && sc->text_len )
465                        g_string_append_printf( reply, " %s", sc->text );
466                if( ( sc = xt_find_node( c->children, "FAMILY" ) ) && sc->text_len )
467                        g_string_append_printf( reply, " %s", sc->text );
468                if( ( sc = xt_find_node( c->children, "SUFFIX" ) ) && sc->text_len )
469                        g_string_append_printf( reply, " %s", sc->text );
470               
471                reply = g_string_append_c( reply, '\n' );
472        }
473       
474        if( ( c = xt_find_node( vc->children, "NICKNAME" ) ) && c->text_len )
475                g_string_append_printf( reply, "Nickname: %s\n", c->text );
476       
477        if( ( c = xt_find_node( vc->children, "BDAY" ) ) && c->text_len )
478                g_string_append_printf( reply, "Date of birth: %s\n", c->text );
479       
480        /* Slightly alternative use of for... ;-) */
481        for( c = vc->children; ( c = xt_find_node( c, "EMAIL" ) ); c = c->next )
482        {
483                if( ( sc = xt_find_node( c->children, "USERID" ) ) == NULL || sc->text_len == 0 )
484                        continue;
485               
486                if( xt_find_node( c->children, "HOME" ) )
487                        s = "Home";
488                else if( xt_find_node( c->children, "WORK" ) )
489                        s = "Work";
490                else
491                        s = "Misc.";
492               
493                g_string_append_printf( reply, "%s e-mail address: %s\n", s, sc->text );
494        }
495       
496        if( ( c = xt_find_node( vc->children, "URL" ) ) && c->text_len )
497                g_string_append_printf( reply, "Homepage: %s\n", c->text );
498       
499        /* Slightly alternative use of for... ;-) */
500        for( c = vc->children; ( c = xt_find_node( c, "ADR" ) ); c = c->next )
501        {
502                if( xt_find_node( c->children, "HOME" ) )
503                        s = "Home";
504                else if( xt_find_node( c->children, "WORK" ) )
505                        s = "Work";
506                else
507                        s = "Misc.";
508               
509                g_string_append_printf( reply, "%s address: ", s );
510               
511                if( ( sc = xt_find_node( c->children, "STREET" ) ) && sc->text_len )
512                        g_string_append_printf( reply, "%s ", sc->text );
513                if( ( sc = xt_find_node( c->children, "EXTADR" ) ) && sc->text_len )
514                        g_string_append_printf( reply, "%s, ", sc->text );
515                if( ( sc = xt_find_node( c->children, "PCODE" ) ) && sc->text_len )
516                        g_string_append_printf( reply, "%s, ", sc->text );
517                if( ( sc = xt_find_node( c->children, "LOCALITY" ) ) && sc->text_len )
518                        g_string_append_printf( reply, "%s, ", sc->text );
519                if( ( sc = xt_find_node( c->children, "REGION" ) ) && sc->text_len )
520                        g_string_append_printf( reply, "%s, ", sc->text );
521                if( ( sc = xt_find_node( c->children, "CTRY" ) ) && sc->text_len )
522                        g_string_append_printf( reply, "%s", sc->text );
523               
524                if( reply->str[reply->len-2] == ',' )
525                        reply = g_string_truncate( reply, reply->len-2 );
526               
527                reply = g_string_append_c( reply, '\n' );
528        }
529       
530        for( c = vc->children; ( c = xt_find_node( c, "TEL" ) ); c = c->next )
531        {
532                if( ( sc = xt_find_node( c->children, "NUMBER" ) ) == NULL || sc->text_len == 0 )
533                        continue;
534               
535                if( xt_find_node( c->children, "HOME" ) )
536                        s = "Home";
537                else if( xt_find_node( c->children, "WORK" ) )
538                        s = "Work";
539                else
540                        s = "Misc.";
541               
542                g_string_append_printf( reply, "%s phone number: %s\n", s, sc->text );
543        }
544       
545        if( ( c = xt_find_node( vc->children, "DESC" ) ) && c->text_len )
546                g_string_append_printf( reply, "Other information:\n%s", c->text );
547       
548        /* *sigh* */
549       
550        imcb_log( ic, "%s", reply->str );
551        g_string_free( reply, TRUE );
552       
553        return XT_HANDLED;
554}
555
556static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
557
558int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name )
559{
560        struct xt_node *node;
561        int st;
562       
563        /* Build the item entry */
564        node = xt_new_node( "item", NULL, NULL );
565        xt_add_attr( node, "jid", handle );
566        if( name )
567                xt_add_attr( node, "name", name );
568       
569        /* And pack it into a roster-add packet */
570        node = xt_new_node( "query", NULL, node );
571        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
572        node = jabber_make_packet( "iq", "set", NULL, node );
573        jabber_cache_add( ic, node, jabber_add_to_roster_callback );
574       
575        st = jabber_write_packet( ic, node );
576       
577        return st;
578}
579
580static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
581{
582        char *s, *jid = NULL;
583        struct xt_node *c;
584       
585        if( ( c = xt_find_node( orig->children, "query" ) ) &&
586            ( c = xt_find_node( c->children, "item" ) ) &&
587            ( jid = xt_find_attr( c, "jid" ) ) &&
588            ( s = xt_find_attr( node, "type" ) ) &&
589            strcmp( s, "result" ) == 0 )
590        {
591                if( bee_user_by_handle( ic->bee, ic, jid ) == NULL )
592                        imcb_add_buddy( ic, jid, NULL );
593        }
594        else
595        {
596                imcb_log( ic, "Error while adding `%s' to your contact list.",
597                          jid ? jid : "(unknown handle)" );
598        }
599       
600        return XT_HANDLED;
601}
602
603int jabber_remove_from_roster( struct im_connection *ic, char *handle )
604{
605        struct xt_node *node;
606        int st;
607       
608        /* Build the item entry */
609        node = xt_new_node( "item", NULL, NULL );
610        xt_add_attr( node, "jid", handle );
611        xt_add_attr( node, "subscription", "remove" );
612       
613        /* And pack it into a roster-add packet */
614        node = xt_new_node( "query", NULL, node );
615        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
616        node = jabber_make_packet( "iq", "set", NULL, node );
617       
618        st = jabber_write_packet( ic, node );
619       
620        xt_free_node( node );
621        return st;
622}
623
624xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
625
626xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid )
627{
628        struct xt_node *node, *query;
629        struct jabber_buddy *bud;
630       
631        if( ( bud = jabber_buddy_by_jid( ic, bare_jid , 0 ) ) == NULL )
632        {
633                /* Who cares about the unknown... */
634                imcb_log( ic, "Couldn't find buddy: %s", bare_jid);
635                return XT_HANDLED;
636        }
637       
638        if( bud->features ) /* been here already */
639                return XT_HANDLED;
640       
641        node = xt_new_node( "query", NULL, NULL );
642        xt_add_attr( node, "xmlns", XMLNS_DISCO_INFO );
643       
644        if( !( query = jabber_make_packet( "iq", "get", bare_jid, node ) ) )
645        {
646                imcb_log( ic, "WARNING: Couldn't generate feature query" );
647                xt_free_node( node );
648                return XT_HANDLED;
649        }
650
651        jabber_cache_add( ic, query, jabber_iq_parse_features );
652
653        return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT;
654}
655
656xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
657{
658        struct xt_node *c;
659        struct jabber_buddy *bud;
660        char *feature, *xmlns, *from;
661
662        if( !( from = xt_find_attr( node, "from" ) ) ||
663            !( c = xt_find_node( node->children, "query" ) ) ||
664            !( xmlns = xt_find_attr( c, "xmlns" ) ) ||
665            !( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) )
666        {
667                imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" );
668                return XT_HANDLED;
669        }
670        if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL )
671        {
672                /* Who cares about the unknown... */
673                imcb_log( ic, "Couldn't find buddy: %s", from );
674                return XT_HANDLED;
675        }
676       
677        c = c->children;
678        while( ( c = xt_find_node( c, "feature" ) ) )
679        {
680                feature = xt_find_attr( c, "var" );
681                if( feature )
682                        bud->features = g_slist_append( bud->features, g_strdup( feature ) );
683                c = c->next;
684        }
685
686        return XT_HANDLED;
687}
688
689xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
690
691xt_status jabber_iq_query_server( struct im_connection *ic, char *jid, char *xmlns )
692{
693        struct xt_node *node, *query;
694        struct jabber_data *jd = ic->proto_data;
695       
696        node = xt_new_node( "query", NULL, NULL );
697        xt_add_attr( node, "xmlns", xmlns );
698       
699        if( !( query = jabber_make_packet( "iq", "get", jid, node ) ) )
700        {
701                imcb_log( ic, "WARNING: Couldn't generate server query" );
702                xt_free_node( node );
703        }
704
705        jd->have_streamhosts--;
706        jabber_cache_add( ic, query, jabber_iq_parse_server_features );
707
708        return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT;
709}
710
711/*
712 * Query the server for "items", query each "item" for identities, query each "item" that's a proxy for it's bytestream info
713 */
714xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
715{
716        struct xt_node *c;
717        struct jabber_data *jd = ic->proto_data;
718        char *xmlns, *from;
719
720        if( !( c = xt_find_node( node->children, "query" ) ) ||
721            !( from = xt_find_attr( node, "from" ) ) ||
722            !( xmlns = xt_find_attr( c, "xmlns" ) ) )
723        {
724                imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" );
725                return XT_HANDLED;
726        }
727
728        jd->have_streamhosts++;
729
730        if( strcmp( xmlns, XMLNS_DISCO_ITEMS ) == 0 )
731        {
732                char *itemjid;
733
734                /* answer from server */
735       
736                c = c->children;
737                while( ( c = xt_find_node( c, "item" ) ) )
738                {
739                        itemjid = xt_find_attr( c, "jid" );
740                       
741                        if( itemjid )
742                                jabber_iq_query_server( ic, itemjid, XMLNS_DISCO_INFO );
743
744                        c = c->next;
745                }
746        }
747        else if( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 )
748        {
749                char *category, *type;
750
751                /* answer from potential proxy */
752
753                c = c->children;
754                while( ( c = xt_find_node( c, "identity" ) ) )
755                {
756                        category = xt_find_attr( c, "category" );
757                        type = xt_find_attr( c, "type" );
758
759                        if( type && ( strcmp( type, "bytestreams" ) == 0 ) &&
760                            category && ( strcmp( category, "proxy" ) == 0 ) )
761                                jabber_iq_query_server( ic, from, XMLNS_BYTESTREAMS );
762
763                        c = c->next;
764                }
765        }
766        else if( strcmp( xmlns, XMLNS_BYTESTREAMS ) == 0 )
767        {
768                char *host, *jid, *port_s;
769                int port;
770
771                /* answer from proxy */
772
773                if( ( c = xt_find_node( c->children, "streamhost" ) ) &&
774                    ( host = xt_find_attr( c, "host" ) ) &&
775                    ( port_s = xt_find_attr( c, "port" ) ) &&
776                    ( sscanf( port_s, "%d", &port ) == 1 ) &&
777                    ( jid = xt_find_attr( c, "jid" ) ) )
778                {
779                        jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 );
780                       
781                        sh->jid = g_strdup( jid );
782                        sh->host = g_strdup( host );
783                        g_snprintf( sh->port, sizeof( sh->port ), "%u", port );
784
785                        imcb_log( ic, "Proxy found: jid %s host %s port %u", jid, host, port );
786                        jd->streamhosts = g_slist_append( jd->streamhosts, sh );
787                }
788        }
789
790        if( jd->have_streamhosts == 0 )
791                jd->have_streamhosts++;
792
793        return XT_HANDLED;
794}
Note: See TracBrowser for help on using the repository browser.