source: protocols/jabber/iq.c @ be1efa3

Last change on this file since be1efa3 was be1efa3, checked in by dequis <dx@…>, at 2015-01-26T02:43:34Z

Add handle_is_self() prpl function to fix JID mismatch confusion bugs

When bee_chat needs to check for self messages, it can call this
function to let the protocol implementation do the comparison.

In the case of jabber, sometimes the server reports a different username
after login, this one is stored in jd->internal_jid, and the one that is
used for login isn't changed

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