source: protocols/jabber/iq.c @ 06eef80

Last change on this file since 06eef80 was 1444be5, checked in by Michael Stapelberg <michael@…>, at 2012-10-17T07:23:00Z

Implement jabber message receipts (XEP-0184)

This change will make BitlBee acknowledge messages when requested.
It will not request message receipts from other clients, mainly because I am
not sure if this feature can be mapped to IRC cleanly.

  • Property mode set to 100644
File size: 28.0 KB
RevLine 
[f06894d]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"
[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                }
354                else if( strncmp( jd->me, c->text, strlen( jd->me ) ) != 0 )
355                {
356                        s = strchr( c->text, '/' );
357                        if( s )
358                                *s = '\0';
359                        jabber_set_me( ic, c->text );
360                        imcb_log( ic, "Server claims your JID is `%s' instead of `%s'. "
361                                  "This mismatch may cause problems with groupchats "
362                                  "and possibly other things.",
363                                  c->text, ic->acc->user );
364                        if( s )
365                                *s = '/';
366                }
367                else if( c && c->text_len && ( s = strchr( c->text, '/' ) ) &&
368                         strcmp( s + 1, set_getstr( &ic->acc->set, "resource" ) ) != 0 )
[84b045d]369                        imcb_log( ic, "Server changed session resource string to `%s'", s + 1 );
[861c199]370        }
[21167d2]371       
[8fb1263]372        if( jd->flags & JFLAG_WANT_BIND )
373        {
374                reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
375                xt_add_attr( reply, "xmlns", XMLNS_BIND );
[315dd4c]376                jd->flags &= ~JFLAG_WANT_BIND;
[8fb1263]377        }
378        else if( jd->flags & JFLAG_WANT_SESSION )
[861c199]379        {
[8fb1263]380                reply = xt_new_node( "session", NULL, NULL );
381                xt_add_attr( reply, "xmlns", XMLNS_SESSION );
[315dd4c]382                jd->flags &= ~JFLAG_WANT_SESSION;
[861c199]383        }
[21167d2]384       
[8fb1263]385        if( reply != NULL )
386        {
387                reply = jabber_make_packet( "iq", "set", NULL, reply );
388                jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
389               
390                if( !jabber_write_packet( ic, reply ) )
391                        return XT_ABORT;
392        }
393        else if( ( jd->flags & ( JFLAG_WANT_BIND | JFLAG_WANT_SESSION ) ) == 0 )
[861c199]394        {
[0da65d5]395                if( !jabber_get_roster( ic ) )
[861c199]396                        return XT_ABORT;
[bb2d198]397                if( !jabber_iq_disco_server( ic ) )
398                        return XT_ABORT;
[861c199]399        }
[21167d2]400       
[861c199]401        return XT_HANDLED;
[21167d2]402}
[70f6aab8]403
[0da65d5]404int jabber_get_roster( struct im_connection *ic )
[70f6aab8]405{
406        struct xt_node *node;
407        int st;
408       
[84b045d]409        imcb_log( ic, "Authenticated, requesting buddy list" );
[70f6aab8]410       
411        node = xt_new_node( "query", NULL, NULL );
[47d3ac4]412        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
[70f6aab8]413        node = jabber_make_packet( "iq", "get", NULL, node );
414       
[0da65d5]415        jabber_cache_add( ic, node, jabber_parse_roster );
416        st = jabber_write_packet( ic, node );
[70f6aab8]417       
418        return st;
419}
[cfbb3a6]420
[0da65d5]421static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
[861c199]422{
423        struct xt_node *query, *c;
[abbd8ed]424        int initial = ( orig != NULL );
[861c199]425       
[9bcbe48]426        if( !( query = xt_find_node( node->children, "query" ) ) )
427        {
[43462708]428                imcb_log( ic, "Warning: Received NULL roster packet" );
[9bcbe48]429                return XT_HANDLED;
430        }
[861c199]431       
432        c = query->children;
433        while( ( c = xt_find_node( c, "item" ) ) )
434        {
[dcd16c5]435                struct xt_node *group = xt_find_node( c->children, "group" );
[861c199]436                char *jid = xt_find_attr( c, "jid" );
437                char *name = xt_find_attr( c, "name" );
438                char *sub = xt_find_attr( c, "subscription" );
439               
[f0cb961]440                if( jid && sub )
[abbd8ed]441                {
442                        if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) )
443                        {
[f5d87ea]444                                imcb_add_buddy( ic, jid, ( group && group->text_len ) ?
445                                                           group->text : NULL );
[f0cb961]446                               
[d06eabf]447                                if( name )
448                                        imcb_rename_buddy( ic, jid, name );
[abbd8ed]449                        }
450                        else if( strcmp( sub, "remove" ) == 0 )
451                        {
[0da65d5]452                                jabber_buddy_remove_bare( ic, jid );
[998b103]453                                imcb_remove_buddy( ic, jid, NULL );
[abbd8ed]454                        }
455                }
[861c199]456               
457                c = c->next;
458        }
459       
[abbd8ed]460        if( initial )
[84b045d]461                imcb_connected( ic );
[861c199]462       
463        return XT_HANDLED;
464}
465
[0da65d5]466int jabber_get_vcard( struct im_connection *ic, char *bare_jid )
[1991be6]467{
468        struct xt_node *node;
469       
470        if( strchr( bare_jid, '/' ) )
471                return 1;       /* This was an error, but return 0 should only be done if the connection died... */
472       
473        node = xt_new_node( "vCard", NULL, NULL );
[47d3ac4]474        xt_add_attr( node, "xmlns", XMLNS_VCARD );
[1991be6]475        node = jabber_make_packet( "iq", "get", bare_jid, node );
476       
[0da65d5]477        jabber_cache_add( ic, node, jabber_iq_display_vcard );
478        return jabber_write_packet( ic, node );
[1991be6]479}
480
[0da65d5]481static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
[1991be6]482{
[0da65d5]483        struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
[1991be6]484        GString *reply;
485        char *s;
486       
487        if( ( s = xt_find_attr( node, "type" ) ) == NULL ||
488            strcmp( s, "result" ) != 0 ||
489            ( vc = xt_find_node( node->children, "vCard" ) ) == NULL )
490        {
491                s = xt_find_attr( orig, "to" ); /* If this returns NULL something's wrong.. */
[84b045d]492                imcb_log( ic, "Could not retrieve vCard of %s", s ? s : "(NULL)" );
[1991be6]493                return XT_HANDLED;
494        }
495       
496        s = xt_find_attr( orig, "to" );
497        reply = g_string_new( "vCard information for " );
498        reply = g_string_append( reply, s ? s : "(NULL)" );
499        reply = g_string_append( reply, ":\n" );
500       
501        /* I hate this format, I really do... */
502       
503        if( ( c = xt_find_node( vc->children, "FN" ) ) && c->text_len )
504                g_string_append_printf( reply, "Name: %s\n", c->text );
505       
506        if( ( c = xt_find_node( vc->children, "N" ) ) && c->children )
507        {
508                reply = g_string_append( reply, "Full name:" );
509               
510                if( ( sc = xt_find_node( c->children, "PREFIX" ) ) && sc->text_len )
511                        g_string_append_printf( reply, " %s", sc->text );
512                if( ( sc = xt_find_node( c->children, "GIVEN" ) ) && sc->text_len )
513                        g_string_append_printf( reply, " %s", sc->text );
514                if( ( sc = xt_find_node( c->children, "MIDDLE" ) ) && sc->text_len )
515                        g_string_append_printf( reply, " %s", sc->text );
516                if( ( sc = xt_find_node( c->children, "FAMILY" ) ) && sc->text_len )
517                        g_string_append_printf( reply, " %s", sc->text );
518                if( ( sc = xt_find_node( c->children, "SUFFIX" ) ) && sc->text_len )
519                        g_string_append_printf( reply, " %s", sc->text );
520               
521                reply = g_string_append_c( reply, '\n' );
522        }
523       
524        if( ( c = xt_find_node( vc->children, "NICKNAME" ) ) && c->text_len )
525                g_string_append_printf( reply, "Nickname: %s\n", c->text );
526       
527        if( ( c = xt_find_node( vc->children, "BDAY" ) ) && c->text_len )
528                g_string_append_printf( reply, "Date of birth: %s\n", c->text );
529       
530        /* Slightly alternative use of for... ;-) */
531        for( c = vc->children; ( c = xt_find_node( c, "EMAIL" ) ); c = c->next )
532        {
533                if( ( sc = xt_find_node( c->children, "USERID" ) ) == NULL || sc->text_len == 0 )
534                        continue;
535               
536                if( xt_find_node( c->children, "HOME" ) )
537                        s = "Home";
538                else if( xt_find_node( c->children, "WORK" ) )
539                        s = "Work";
540                else
541                        s = "Misc.";
542               
543                g_string_append_printf( reply, "%s e-mail address: %s\n", s, sc->text );
544        }
545       
546        if( ( c = xt_find_node( vc->children, "URL" ) ) && c->text_len )
547                g_string_append_printf( reply, "Homepage: %s\n", c->text );
548       
549        /* Slightly alternative use of for... ;-) */
550        for( c = vc->children; ( c = xt_find_node( c, "ADR" ) ); c = c->next )
551        {
552                if( xt_find_node( c->children, "HOME" ) )
553                        s = "Home";
554                else if( xt_find_node( c->children, "WORK" ) )
555                        s = "Work";
556                else
557                        s = "Misc.";
558               
559                g_string_append_printf( reply, "%s address: ", s );
560               
561                if( ( sc = xt_find_node( c->children, "STREET" ) ) && sc->text_len )
562                        g_string_append_printf( reply, "%s ", sc->text );
563                if( ( sc = xt_find_node( c->children, "EXTADR" ) ) && sc->text_len )
564                        g_string_append_printf( reply, "%s, ", sc->text );
565                if( ( sc = xt_find_node( c->children, "PCODE" ) ) && sc->text_len )
566                        g_string_append_printf( reply, "%s, ", sc->text );
567                if( ( sc = xt_find_node( c->children, "LOCALITY" ) ) && sc->text_len )
568                        g_string_append_printf( reply, "%s, ", sc->text );
569                if( ( sc = xt_find_node( c->children, "REGION" ) ) && sc->text_len )
570                        g_string_append_printf( reply, "%s, ", sc->text );
571                if( ( sc = xt_find_node( c->children, "CTRY" ) ) && sc->text_len )
572                        g_string_append_printf( reply, "%s", sc->text );
573               
574                if( reply->str[reply->len-2] == ',' )
575                        reply = g_string_truncate( reply, reply->len-2 );
576               
577                reply = g_string_append_c( reply, '\n' );
578        }
579       
580        for( c = vc->children; ( c = xt_find_node( c, "TEL" ) ); c = c->next )
581        {
582                if( ( sc = xt_find_node( c->children, "NUMBER" ) ) == NULL || sc->text_len == 0 )
583                        continue;
584               
585                if( xt_find_node( c->children, "HOME" ) )
586                        s = "Home";
587                else if( xt_find_node( c->children, "WORK" ) )
588                        s = "Work";
589                else
590                        s = "Misc.";
591               
592                g_string_append_printf( reply, "%s phone number: %s\n", s, sc->text );
593        }
594       
595        if( ( c = xt_find_node( vc->children, "DESC" ) ) && c->text_len )
596                g_string_append_printf( reply, "Other information:\n%s", c->text );
597       
598        /* *sigh* */
599       
[84b045d]600        imcb_log( ic, "%s", reply->str );
[1991be6]601        g_string_free( reply, TRUE );
602       
603        return XT_HANDLED;
604}
605
[a73e91a]606static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
607
[46d215d]608int jabber_add_to_roster( struct im_connection *ic, const char *handle, const char *name, const char *group )
[cfbb3a6]609{
610        struct xt_node *node;
611        int st;
612       
613        /* Build the item entry */
614        node = xt_new_node( "item", NULL, NULL );
615        xt_add_attr( node, "jid", handle );
616        if( name )
617                xt_add_attr( node, "name", name );
[46d215d]618        if( group )
619                xt_add_child( node, xt_new_node( "group", group, NULL ) );
[cfbb3a6]620       
621        /* And pack it into a roster-add packet */
622        node = xt_new_node( "query", NULL, node );
[47d3ac4]623        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
[cfbb3a6]624        node = jabber_make_packet( "iq", "set", NULL, node );
[a73e91a]625        jabber_cache_add( ic, node, jabber_add_to_roster_callback );
[cfbb3a6]626       
[0da65d5]627        st = jabber_write_packet( ic, node );
[cfbb3a6]628       
629        return st;
630}
631
[a73e91a]632static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
633{
634        char *s, *jid = NULL;
635        struct xt_node *c;
636       
637        if( ( c = xt_find_node( orig->children, "query" ) ) &&
638            ( c = xt_find_node( c->children, "item" ) ) &&
639            ( jid = xt_find_attr( c, "jid" ) ) &&
640            ( s = xt_find_attr( node, "type" ) ) &&
641            strcmp( s, "result" ) == 0 )
642        {
[17a6ee9]643                if( bee_user_by_handle( ic->bee, ic, jid ) == NULL )
[a73e91a]644                        imcb_add_buddy( ic, jid, NULL );
645        }
646        else
647        {
648                imcb_log( ic, "Error while adding `%s' to your contact list.",
649                          jid ? jid : "(unknown handle)" );
650        }
651       
652        return XT_HANDLED;
653}
654
[0da65d5]655int jabber_remove_from_roster( struct im_connection *ic, char *handle )
[cfbb3a6]656{
657        struct xt_node *node;
658        int st;
659       
660        /* Build the item entry */
661        node = xt_new_node( "item", NULL, NULL );
662        xt_add_attr( node, "jid", handle );
663        xt_add_attr( node, "subscription", "remove" );
664       
665        /* And pack it into a roster-add packet */
666        node = xt_new_node( "query", NULL, node );
[47d3ac4]667        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
[cfbb3a6]668        node = jabber_make_packet( "iq", "set", NULL, node );
669       
[0da65d5]670        st = jabber_write_packet( ic, node );
[cfbb3a6]671       
672        xt_free_node( node );
673        return st;
674}
[dc0ba9c]675
676xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
677
678xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid )
679{
680        struct xt_node *node, *query;
681        struct jabber_buddy *bud;
682       
683        if( ( bud = jabber_buddy_by_jid( ic, bare_jid , 0 ) ) == NULL )
684        {
685                /* Who cares about the unknown... */
[4358b10]686                imcb_log( ic, "Couldn't find buddy: %s", bare_jid);
[54a2014]687                return XT_HANDLED;
[dc0ba9c]688        }
689       
690        if( bud->features ) /* been here already */
691                return XT_HANDLED;
692       
693        node = xt_new_node( "query", NULL, NULL );
694        xt_add_attr( node, "xmlns", XMLNS_DISCO_INFO );
695       
696        if( !( query = jabber_make_packet( "iq", "get", bare_jid, node ) ) )
697        {
698                imcb_log( ic, "WARNING: Couldn't generate feature query" );
699                xt_free_node( node );
[54a2014]700                return XT_HANDLED;
[dc0ba9c]701        }
702
703        jabber_cache_add( ic, query, jabber_iq_parse_features );
704
[54a2014]705        return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT;
[dc0ba9c]706}
707
708xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
709{
710        struct xt_node *c;
711        struct jabber_buddy *bud;
[c1a3c27]712        char *feature, *xmlns, *from;
[dc0ba9c]713
[699376f7]714        if( !( from = xt_find_attr( node, "from" ) ) ||
715            !( c = xt_find_node( node->children, "query" ) ) ||
[c1a3c27]716            !( xmlns = xt_find_attr( c, "xmlns" ) ) ||
717            !( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) )
[dc0ba9c]718        {
719                imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" );
720                return XT_HANDLED;
721        }
[c1a3c27]722        if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL )
[dc0ba9c]723        {
724                /* Who cares about the unknown... */
[c1a3c27]725                imcb_log( ic, "Couldn't find buddy: %s", from );
[54a2014]726                return XT_HANDLED;
[dc0ba9c]727        }
728       
729        c = c->children;
[1c3008a]730        while( ( c = xt_find_node( c, "feature" ) ) )
731        {
[dc0ba9c]732                feature = xt_find_attr( c, "var" );
[c1a3c27]733                if( feature )
734                        bud->features = g_slist_append( bud->features, g_strdup( feature ) );
[dc0ba9c]735                c = c->next;
736        }
737
738        return XT_HANDLED;
739}
740
741xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
742
743xt_status jabber_iq_query_server( struct im_connection *ic, char *jid, char *xmlns )
744{
745        struct xt_node *node, *query;
746        struct jabber_data *jd = ic->proto_data;
747       
748        node = xt_new_node( "query", NULL, NULL );
749        xt_add_attr( node, "xmlns", xmlns );
750       
751        if( !( query = jabber_make_packet( "iq", "get", jid, node ) ) )
752        {
753                imcb_log( ic, "WARNING: Couldn't generate server query" );
754                xt_free_node( node );
755        }
756
757        jd->have_streamhosts--;
758        jabber_cache_add( ic, query, jabber_iq_parse_server_features );
759
[54a2014]760        return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT;
[dc0ba9c]761}
762
763/*
764 * Query the server for "items", query each "item" for identities, query each "item" that's a proxy for it's bytestream info
765 */
766xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
767{
768        struct xt_node *c;
769        struct jabber_data *jd = ic->proto_data;
[c1a3c27]770        char *xmlns, *from;
[dc0ba9c]771
772        if( !( c = xt_find_node( node->children, "query" ) ) ||
[c1a3c27]773            !( from = xt_find_attr( node, "from" ) ) ||
774            !( xmlns = xt_find_attr( c, "xmlns" ) ) )
[dc0ba9c]775        {
776                imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" );
777                return XT_HANDLED;
778        }
779
780        jd->have_streamhosts++;
781
[c1a3c27]782        if( strcmp( xmlns, XMLNS_DISCO_ITEMS ) == 0 )
[dc0ba9c]783        {
[c1a3c27]784                char *itemjid;
[dc0ba9c]785
786                /* answer from server */
787       
788                c = c->children;
789                while( ( c = xt_find_node( c, "item" ) ) )
790                {
791                        itemjid = xt_find_attr( c, "jid" );
[c1a3c27]792                       
793                        if( itemjid )
794                                jabber_iq_query_server( ic, itemjid, XMLNS_DISCO_INFO );
[dc0ba9c]795
796                        c = c->next;
797                }
[1c3008a]798        }
[42fc5b6]799        else if( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 )
[dc0ba9c]800        {
801                char *category, *type;
802
803                /* answer from potential proxy */
804
805                c = c->children;
806                while( ( c = xt_find_node( c, "identity" ) ) )
807                {
808                        category = xt_find_attr( c, "category" );
809                        type = xt_find_attr( c, "type" );
810
811                        if( type && ( strcmp( type, "bytestreams" ) == 0 ) &&
812                            category && ( strcmp( category, "proxy" ) == 0 ) )
[c1a3c27]813                                jabber_iq_query_server( ic, from, XMLNS_BYTESTREAMS );
[dc0ba9c]814
815                        c = c->next;
816                }
[1c3008a]817        }
[42fc5b6]818        else if( strcmp( xmlns, XMLNS_BYTESTREAMS ) == 0 )
[dc0ba9c]819        {
[c1a3c27]820                char *host, *jid, *port_s;
[dc0ba9c]821                int port;
822
823                /* answer from proxy */
824
825                if( ( c = xt_find_node( c->children, "streamhost" ) ) &&
826                    ( host = xt_find_attr( c, "host" ) ) &&
[c1a3c27]827                    ( port_s = xt_find_attr( c, "port" ) ) &&
828                    ( sscanf( port_s, "%d", &port ) == 1 ) &&
[dc0ba9c]829                    ( jid = xt_find_attr( c, "jid" ) ) )
830                {
831                        jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 );
[c1a3c27]832                       
[dc0ba9c]833                        sh->jid = g_strdup( jid );
834                        sh->host = g_strdup( host );
[c1a3c27]835                        g_snprintf( sh->port, sizeof( sh->port ), "%u", port );
[dc0ba9c]836
837                        imcb_log( ic, "Proxy found: jid %s host %s port %u", jid, host, port );
838                        jd->streamhosts = g_slist_append( jd->streamhosts, sh );
839                }
840        }
841
842        if( jd->have_streamhosts == 0 )
843                jd->have_streamhosts++;
[1c3008a]844
[dc0ba9c]845        return XT_HANDLED;
846}
[d88c92a]847
848static xt_status jabber_iq_version_response( struct im_connection *ic,
849        struct xt_node *node, struct xt_node *orig );
850
851void jabber_iq_version_send( struct im_connection *ic, struct jabber_buddy *bud, void *data )
852{
853        struct xt_node *node, *query;
854       
855        node = xt_new_node( "query", NULL, NULL );
856        xt_add_attr( node, "xmlns", XMLNS_VERSION );
857        query = jabber_make_packet( "iq", "get", bud->full_jid, node );
858        jabber_cache_add( ic, query, jabber_iq_version_response );
859
860        jabber_write_packet( ic, query );
861}
862
863static xt_status jabber_iq_version_response( struct im_connection *ic,
864        struct xt_node *node, struct xt_node *orig )
865{
866        struct xt_node *query;
867        GString *rets;
868        char *s;
869        char *ret[2] = {};
870        bee_user_t *bu;
871        struct jabber_buddy *bud = NULL;
872       
873        if( ( s = xt_find_attr( node, "from" ) ) &&
874            ( bud = jabber_buddy_by_jid( ic, s, 0 ) ) &&
875            ( query = xt_find_node( node->children, "query" ) ) &&
876            ( bu = bee_user_by_handle( ic->bee, ic, bud->bare_jid ) ) )
877        {
878                rets = g_string_new( "Resource " );
879                g_string_append( rets, bud->resource );
880        }
881        else
882                return XT_HANDLED;
883       
884        for( query = query->children; query; query = query->next )
885                if( query->text_len > 0 )
886                        g_string_append_printf( rets, " %s: %s,", query->name, query->text );
887       
888        g_string_truncate( rets, rets->len - 1 );
889        ret[0] = rets->str;
890        imcb_buddy_action_response( bu, "VERSION", ret, NULL );
891        g_string_free( rets, TRUE );
892       
893        return XT_HANDLED;
894}
[bb2d198]895
896static xt_status jabber_iq_disco_server_response( struct im_connection *ic,
897        struct xt_node *node, struct xt_node *orig );
898
899static int jabber_iq_disco_server( struct im_connection *ic )
900{
901        struct xt_node *node, *iq;
902        struct jabber_data *jd = ic->proto_data;
903       
904        node = xt_new_node( "query", NULL, NULL );
905        xt_add_attr( node, "xmlns", XMLNS_DISCO_INFO );
906        iq = jabber_make_packet( "iq", "get", jd->server, node );
907       
908        jabber_cache_add( ic, iq, jabber_iq_disco_server_response );
909        return jabber_write_packet( ic, iq );
910}
911
912static xt_status jabber_iq_disco_server_response( struct im_connection *ic,
913        struct xt_node *node, struct xt_node *orig )
914{
915        struct jabber_data *jd = ic->proto_data;
916        struct xt_node *id;
917       
918        if( ( id = xt_find_path( node, "query/identity" ) ) )
919        {
920                char *cat, *type, *name;
921               
922                if( !( cat = xt_find_attr( id, "category" ) ) ||
923                    !( type = xt_find_attr( id, "type" ) ) ||
924                    !( name = xt_find_attr( id, "name" ) ) )
925                        return XT_HANDLED;
926               
927                if( strcmp( cat, "server" ) == 0 && strcmp( type, "im" ) == 0 &&
928                    strstr( name, "Google" ) != NULL )
929                        jd->flags |= JFLAG_GTALK;
930        }
931       
932        return XT_HANDLED;
933}
Note: See TracBrowser for help on using the repository browser.