source: protocols/jabber/iq.c @ f9258ae

Last change on this file since f9258ae was bb2d198, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-02-10T15:14:38Z

Probing for Google Talk servers, based on iq-discovery responses. I'll need
this for automatically generating a sane name for groupchats.

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