source: protocols/jabber/iq.c @ e618d85

Last change on this file since e618d85 was 68286eb, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-12-20T16:45:53Z

Detect JID changes at login time and warn the user about them.

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