source: protocols/jabber/iq.c @ ca0981a

Last change on this file since ca0981a was aed152f, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-21T01:02:03Z

Killed the last str*cmp(xml_find_attr) combos.

  • 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                        imcb_log( ic, "Warning: Received incomplete IQ-%s packet", type );
139                        return XT_HANDLED;
140                }
141                else if( strcmp( s, XMLNS_ROSTER ) == 0 )
142                {
143                /* This is a roster push. XMPP servers send this when someone
144                   was added to (or removed from) the buddy list. AFAIK they're
145                   sent even if we added this buddy in our own session. */
146                        int bare_len = strlen( ic->acc->user );
147                       
148                        if( ( s = xt_find_attr( node, "from" ) ) == NULL ||
149                            ( strncmp( s, ic->acc->user, bare_len ) == 0 &&
150                              ( s[bare_len] == 0 || s[bare_len] == '/' ) ) )
151                        {
152                                jabber_parse_roster( ic, node, NULL );
153                               
154                                /* Should we generate a reply here? Don't think it's
155                                   very important... */
156                        }
157                        else
158                        {
159                                imcb_log( ic, "Warning: %s tried to fake a roster push!", s ? s : "(unknown)" );
160                               
161                                xt_free_node( reply );
162                                reply = jabber_make_error_packet( node, "not-allowed", "cancel", NULL );
163                                pack = 0;
164                        }
165                }
166                else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 )
167                {
168                        /* Bytestream Request (stage 2 of file transfer) */
169                        return jabber_bs_recv_request( ic, node, c );
170                }
171                else
172                {
173                        xt_free_node( reply );
174                        reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL );
175                        pack = 0;
176                }
177        }
178       
179        /* If we recognized the xmlns and managed to generate a reply,
180           finish and send it. */
181        if( reply )
182        {
183                /* Normally we still have to pack it into an iq-result
184                   packet, but for errors, for example, we don't. */
185                if( pack )
186                {
187                        reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), reply );
188                        if( ( s = xt_find_attr( node, "id" ) ) )
189                                xt_add_attr( reply, "id", s );
190                }
191               
192                st = jabber_write_packet( ic, reply );
193                xt_free_node( reply );
194                if( !st )
195                        return XT_ABORT;
196        }
197       
198        return XT_HANDLED;
199}
200
201static xt_status jabber_do_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
202static xt_status jabber_finish_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
203
204int jabber_init_iq_auth( struct im_connection *ic )
205{
206        struct jabber_data *jd = ic->proto_data;
207        struct xt_node *node;
208        int st;
209       
210        node = xt_new_node( "query", NULL, xt_new_node( "username", jd->username, NULL ) );
211        xt_add_attr( node, "xmlns", XMLNS_AUTH );
212        node = jabber_make_packet( "iq", "get", NULL, node );
213       
214        jabber_cache_add( ic, node, jabber_do_iq_auth );
215        st = jabber_write_packet( ic, node );
216       
217        return st;
218}
219
220static xt_status jabber_do_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
221{
222        struct jabber_data *jd = ic->proto_data;
223        struct xt_node *reply, *query;
224        xt_status st;
225        char *s;
226       
227        if( !( query = xt_find_node( node->children, "query" ) ) )
228        {
229                imcb_log( ic, "Warning: Received incomplete IQ packet while authenticating" );
230                imc_logout( ic, FALSE );
231                return XT_HANDLED;
232        }
233       
234        /* Time to authenticate ourselves! */
235        reply = xt_new_node( "query", NULL, NULL );
236        xt_add_attr( reply, "xmlns", XMLNS_AUTH );
237        xt_add_child( reply, xt_new_node( "username", jd->username, NULL ) );
238        xt_add_child( reply, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
239       
240        if( xt_find_node( query->children, "digest" ) && ( s = xt_find_attr( jd->xt->root, "id" ) ) )
241        {
242                /* We can do digest authentication, it seems, and of
243                   course we prefer that. */
244                sha1_state_t sha;
245                char hash_hex[41];
246                unsigned char hash[20];
247                int i;
248               
249                sha1_init( &sha );
250                sha1_append( &sha, (unsigned char*) s, strlen( s ) );
251                sha1_append( &sha, (unsigned char*) ic->acc->pass, strlen( ic->acc->pass ) );
252                sha1_finish( &sha, hash );
253               
254                for( i = 0; i < 20; i ++ )
255                        sprintf( hash_hex + i * 2, "%02x", hash[i] );
256               
257                xt_add_child( reply, xt_new_node( "digest", hash_hex, NULL ) );
258        }
259        else if( xt_find_node( query->children, "password" ) )
260        {
261                /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
262                xt_add_child( reply, xt_new_node( "password", ic->acc->pass, NULL ) );
263        }
264        else
265        {
266                xt_free_node( reply );
267               
268                imcb_error( ic, "Can't find suitable authentication method" );
269                imc_logout( ic, FALSE );
270                return XT_ABORT;
271        }
272       
273        reply = jabber_make_packet( "iq", "set", NULL, reply );
274        jabber_cache_add( ic, reply, jabber_finish_iq_auth );
275        st = jabber_write_packet( ic, reply );
276       
277        return st ? XT_HANDLED : XT_ABORT;
278}
279
280static xt_status jabber_finish_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
281{
282        struct jabber_data *jd = ic->proto_data;
283        char *type;
284       
285        if( !( type = xt_find_attr( node, "type" ) ) )
286        {
287                imcb_log( ic, "Warning: Received incomplete IQ packet while authenticating" );
288                imc_logout( ic, FALSE );
289                return XT_HANDLED;
290        }
291       
292        if( strcmp( type, "error" ) == 0 )
293        {
294                imcb_error( ic, "Authentication failure" );
295                imc_logout( ic, FALSE );
296                return XT_ABORT;
297        }
298        else if( strcmp( type, "result" ) == 0 )
299        {
300                /* This happens when we just successfully authenticated the
301                   old (non-SASL) way. */
302                jd->flags |= JFLAG_AUTHENTICATED;
303                if( !jabber_get_roster( ic ) )
304                        return XT_ABORT;
305        }
306       
307        return XT_HANDLED;
308}
309
310xt_status jabber_pkt_bind_sess( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
311{
312        struct jabber_data *jd = ic->proto_data;
313        struct xt_node *c, *reply = NULL;
314        char *s;
315       
316        if( node && ( c = xt_find_node( node->children, "bind" ) ) )
317        {
318                c = xt_find_node( c->children, "jid" );
319                if( c && c->text_len && ( s = strchr( c->text, '/' ) ) &&
320                    strcmp( s + 1, set_getstr( &ic->acc->set, "resource" ) ) != 0 )
321                        imcb_log( ic, "Server changed session resource string to `%s'", s + 1 );
322        }
323       
324        if( jd->flags & JFLAG_WANT_BIND )
325        {
326                reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
327                xt_add_attr( reply, "xmlns", XMLNS_BIND );
328                jd->flags &= ~JFLAG_WANT_BIND;
329        }
330        else if( jd->flags & JFLAG_WANT_SESSION )
331        {
332                reply = xt_new_node( "session", NULL, NULL );
333                xt_add_attr( reply, "xmlns", XMLNS_SESSION );
334                jd->flags &= ~JFLAG_WANT_SESSION;
335        }
336       
337        if( reply != NULL )
338        {
339                reply = jabber_make_packet( "iq", "set", NULL, reply );
340                jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
341               
342                if( !jabber_write_packet( ic, reply ) )
343                        return XT_ABORT;
344        }
345        else if( ( jd->flags & ( JFLAG_WANT_BIND | JFLAG_WANT_SESSION ) ) == 0 )
346        {
347                if( !jabber_get_roster( ic ) )
348                        return XT_ABORT;
349        }
350       
351        return XT_HANDLED;
352}
353
354int jabber_get_roster( struct im_connection *ic )
355{
356        struct xt_node *node;
357        int st;
358       
359        imcb_log( ic, "Authenticated, requesting buddy list" );
360       
361        node = xt_new_node( "query", NULL, NULL );
362        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
363        node = jabber_make_packet( "iq", "get", NULL, node );
364       
365        jabber_cache_add( ic, node, jabber_parse_roster );
366        st = jabber_write_packet( ic, node );
367       
368        return st;
369}
370
371static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
372{
373        struct xt_node *query, *c;
374        int initial = ( orig != NULL );
375       
376        if( !( query = xt_find_node( node->children, "query" ) ) )
377        {
378                imcb_log( ic, "Warning: Received NULL roster packet" );
379                return XT_HANDLED;
380        }
381       
382        c = query->children;
383        while( ( c = xt_find_node( c, "item" ) ) )
384        {
385                struct xt_node *group = xt_find_node( node->children, "group" );
386                char *jid = xt_find_attr( c, "jid" );
387                char *name = xt_find_attr( c, "name" );
388                char *sub = xt_find_attr( c, "subscription" );
389               
390                if( jid && sub )
391                {
392                        if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) )
393                        {
394                                if( initial || imcb_find_buddy( ic, jid ) == NULL )
395                                        imcb_add_buddy( ic, jid, ( group && group->text_len ) ?
396                                                                   group->text : NULL );
397                               
398                                if( name )
399                                        imcb_rename_buddy( ic, jid, name );
400                        }
401                        else if( strcmp( sub, "remove" ) == 0 )
402                        {
403                                jabber_buddy_remove_bare( ic, jid );
404                                imcb_remove_buddy( ic, jid, NULL );
405                        }
406                }
407               
408                c = c->next;
409        }
410       
411        if( initial )
412                imcb_connected( ic );
413       
414        return XT_HANDLED;
415}
416
417int jabber_get_vcard( struct im_connection *ic, char *bare_jid )
418{
419        struct xt_node *node;
420       
421        if( strchr( bare_jid, '/' ) )
422                return 1;       /* This was an error, but return 0 should only be done if the connection died... */
423       
424        node = xt_new_node( "vCard", NULL, NULL );
425        xt_add_attr( node, "xmlns", XMLNS_VCARD );
426        node = jabber_make_packet( "iq", "get", bare_jid, node );
427       
428        jabber_cache_add( ic, node, jabber_iq_display_vcard );
429        return jabber_write_packet( ic, node );
430}
431
432static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
433{
434        struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
435        GString *reply;
436        char *s;
437       
438        if( ( s = xt_find_attr( node, "type" ) ) == NULL ||
439            strcmp( s, "result" ) != 0 ||
440            ( vc = xt_find_node( node->children, "vCard" ) ) == NULL )
441        {
442                s = xt_find_attr( orig, "to" ); /* If this returns NULL something's wrong.. */
443                imcb_log( ic, "Could not retrieve vCard of %s", s ? s : "(NULL)" );
444                return XT_HANDLED;
445        }
446       
447        s = xt_find_attr( orig, "to" );
448        reply = g_string_new( "vCard information for " );
449        reply = g_string_append( reply, s ? s : "(NULL)" );
450        reply = g_string_append( reply, ":\n" );
451       
452        /* I hate this format, I really do... */
453       
454        if( ( c = xt_find_node( vc->children, "FN" ) ) && c->text_len )
455                g_string_append_printf( reply, "Name: %s\n", c->text );
456       
457        if( ( c = xt_find_node( vc->children, "N" ) ) && c->children )
458        {
459                reply = g_string_append( reply, "Full name:" );
460               
461                if( ( sc = xt_find_node( c->children, "PREFIX" ) ) && sc->text_len )
462                        g_string_append_printf( reply, " %s", sc->text );
463                if( ( sc = xt_find_node( c->children, "GIVEN" ) ) && sc->text_len )
464                        g_string_append_printf( reply, " %s", sc->text );
465                if( ( sc = xt_find_node( c->children, "MIDDLE" ) ) && sc->text_len )
466                        g_string_append_printf( reply, " %s", sc->text );
467                if( ( sc = xt_find_node( c->children, "FAMILY" ) ) && sc->text_len )
468                        g_string_append_printf( reply, " %s", sc->text );
469                if( ( sc = xt_find_node( c->children, "SUFFIX" ) ) && sc->text_len )
470                        g_string_append_printf( reply, " %s", sc->text );
471               
472                reply = g_string_append_c( reply, '\n' );
473        }
474       
475        if( ( c = xt_find_node( vc->children, "NICKNAME" ) ) && c->text_len )
476                g_string_append_printf( reply, "Nickname: %s\n", c->text );
477       
478        if( ( c = xt_find_node( vc->children, "BDAY" ) ) && c->text_len )
479                g_string_append_printf( reply, "Date of birth: %s\n", c->text );
480       
481        /* Slightly alternative use of for... ;-) */
482        for( c = vc->children; ( c = xt_find_node( c, "EMAIL" ) ); c = c->next )
483        {
484                if( ( sc = xt_find_node( c->children, "USERID" ) ) == NULL || sc->text_len == 0 )
485                        continue;
486               
487                if( xt_find_node( c->children, "HOME" ) )
488                        s = "Home";
489                else if( xt_find_node( c->children, "WORK" ) )
490                        s = "Work";
491                else
492                        s = "Misc.";
493               
494                g_string_append_printf( reply, "%s e-mail address: %s\n", s, sc->text );
495        }
496       
497        if( ( c = xt_find_node( vc->children, "URL" ) ) && c->text_len )
498                g_string_append_printf( reply, "Homepage: %s\n", c->text );
499       
500        /* Slightly alternative use of for... ;-) */
501        for( c = vc->children; ( c = xt_find_node( c, "ADR" ) ); c = c->next )
502        {
503                if( xt_find_node( c->children, "HOME" ) )
504                        s = "Home";
505                else if( xt_find_node( c->children, "WORK" ) )
506                        s = "Work";
507                else
508                        s = "Misc.";
509               
510                g_string_append_printf( reply, "%s address: ", s );
511               
512                if( ( sc = xt_find_node( c->children, "STREET" ) ) && sc->text_len )
513                        g_string_append_printf( reply, "%s ", sc->text );
514                if( ( sc = xt_find_node( c->children, "EXTADR" ) ) && sc->text_len )
515                        g_string_append_printf( reply, "%s, ", sc->text );
516                if( ( sc = xt_find_node( c->children, "PCODE" ) ) && sc->text_len )
517                        g_string_append_printf( reply, "%s, ", sc->text );
518                if( ( sc = xt_find_node( c->children, "LOCALITY" ) ) && sc->text_len )
519                        g_string_append_printf( reply, "%s, ", sc->text );
520                if( ( sc = xt_find_node( c->children, "REGION" ) ) && sc->text_len )
521                        g_string_append_printf( reply, "%s, ", sc->text );
522                if( ( sc = xt_find_node( c->children, "CTRY" ) ) && sc->text_len )
523                        g_string_append_printf( reply, "%s", sc->text );
524               
525                if( reply->str[reply->len-2] == ',' )
526                        reply = g_string_truncate( reply, reply->len-2 );
527               
528                reply = g_string_append_c( reply, '\n' );
529        }
530       
531        for( c = vc->children; ( c = xt_find_node( c, "TEL" ) ); c = c->next )
532        {
533                if( ( sc = xt_find_node( c->children, "NUMBER" ) ) == NULL || sc->text_len == 0 )
534                        continue;
535               
536                if( xt_find_node( c->children, "HOME" ) )
537                        s = "Home";
538                else if( xt_find_node( c->children, "WORK" ) )
539                        s = "Work";
540                else
541                        s = "Misc.";
542               
543                g_string_append_printf( reply, "%s phone number: %s\n", s, sc->text );
544        }
545       
546        if( ( c = xt_find_node( vc->children, "DESC" ) ) && c->text_len )
547                g_string_append_printf( reply, "Other information:\n%s", c->text );
548       
549        /* *sigh* */
550       
551        imcb_log( ic, "%s", reply->str );
552        g_string_free( reply, TRUE );
553       
554        return XT_HANDLED;
555}
556
557static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
558
559int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name )
560{
561        struct xt_node *node;
562        int st;
563       
564        /* Build the item entry */
565        node = xt_new_node( "item", NULL, NULL );
566        xt_add_attr( node, "jid", handle );
567        if( name )
568                xt_add_attr( node, "name", name );
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( imcb_find_buddy( 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.