source: protocols/jabber/iq.c @ 420d80d1

Last change on this file since 420d80d1 was d76e12f, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-12-06T00:18:27Z

Add support for XEP 202 (replacement for jabber:iq:time).

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