source: protocols/jabber/iq.c @ 2c2df7d

Last change on this file since 2c2df7d was 2c2df7d, checked in by ulim <a.sporto+bee@…>, at 2007-11-28T21:07:30Z

Initial import of jabber file receive and DCC send support. This introduces
only a few changes to bitlbees code, mainly the addition of the "transfers"
command.

This is known to work with Kopete, Psi, and Pidgin (formerly known as gaim).
At least with Pidgin also over a proxy. DCC has only been tested with irssi.
IPV6 is untested but should work.

Currently, only receiving via SOCKS5BYTESREAMS is implemented. I'm not sure if
the alternative(in-band bytestreams IBB) is worth implementing since I didn't
see a client yet that can do it. Additionally, it is probably very slow and
needs support by the server as well.

  • Property mode set to 100644
File size: 18.1 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 );
[abbd8ed]29
[f06894d]30xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )
31{
[0da65d5]32        struct im_connection *ic = data;
33        struct jabber_data *jd = ic->proto_data;
[58b5f62]34        struct xt_node *c, *reply = NULL;
[861c199]35        char *type, *s;
[259edd4]36        int st, pack = 1;
[21167d2]37       
[70f6aab8]38        type = xt_find_attr( node, "type" );
[f06894d]39       
[70f6aab8]40        if( !type )
[861c199]41        {
[84b045d]42                imcb_error( ic, "Received IQ packet without type." );
[c2fb3809]43                imc_logout( ic, TRUE );
[861c199]44                return XT_ABORT;
45        }
[21167d2]46       
[58b5f62]47        if( strcmp( type, "result" ) == 0 || strcmp( type, "error" ) == 0 )
[861c199]48        {
49                struct jabber_cache_entry *entry;
50               
[dfa41a4]51                if( ( s = xt_find_attr( node, "id" ) ) == NULL ||
[608f8cf]52                    strncmp( s, jd->cached_id_prefix, strlen( jd->cached_id_prefix ) ) != 0 )
[58b5f62]53                {
[9bcbe48]54                        /* Silently ignore it, without an ID (or a non-cache
[dfa41a4]55                           ID) we don't know how to handle the packet and we
56                           probably don't have to. */
[58b5f62]57                        return XT_HANDLED;
58                }
59               
[861c199]60                entry = g_hash_table_lookup( jd->node_cache, s );
61               
62                if( entry == NULL )
[84b045d]63                        imcb_log( ic, "WARNING: Received IQ-%s packet with unknown/expired ID %s!", type, s );
[861c199]64                else if( entry->func )
[0da65d5]65                        return entry->func( ic, node, entry->node );
[861c199]66        }
[58b5f62]67        else if( strcmp( type, "get" ) == 0 )
68        {
69                if( !( c = xt_find_node( node->children, "query" ) ) ||
70                    !( s = xt_find_attr( c, "xmlns" ) ) )
71                {
[84b045d]72                        imcb_log( ic, "WARNING: Received incomplete IQ-%s packet", type );
[58b5f62]73                        return XT_HANDLED;
74                }
75               
76                reply = xt_new_node( "query", NULL, NULL );
77                xt_add_attr( reply, "xmlns", s );
78               
79                /* Of course this is a very essential query to support. ;-) */
[47d3ac4]80                if( strcmp( s, XMLNS_VERSION ) == 0 )
[58b5f62]81                {
82                        xt_add_child( reply, xt_new_node( "name", "BitlBee", NULL ) );
83                        xt_add_child( reply, xt_new_node( "version", BITLBEE_VERSION, NULL ) );
84                        xt_add_child( reply, xt_new_node( "os", ARCH, NULL ) );
85                }
[47d3ac4]86                else if( strcmp( s, XMLNS_TIME ) == 0 )
[a4effbf]87                {
88                        time_t time_ep;
89                        char buf[1024];
90                       
91                        buf[sizeof(buf)-1] = 0;
92                        time_ep = time( NULL );
93                       
94                        strftime( buf, sizeof( buf ) - 1, "%Y%m%dT%H:%M:%S", gmtime( &time_ep ) );
95                        xt_add_child( reply, xt_new_node( "utc", buf, NULL ) );
96                       
97                        strftime( buf, sizeof( buf ) - 1, "%Z", localtime( &time_ep ) );
98                        xt_add_child( reply, xt_new_node( "tz", buf, NULL ) );
99                }
[47d3ac4]100                else if( strcmp( s, XMLNS_DISCOVER ) == 0 )
[58b5f62]101                {
[40ef702]102                        const char *features[] = { XMLNS_VERSION,
103                                                   XMLNS_TIME,
104                                                   XMLNS_CHATSTATES,
105                                                   XMLNS_MUC,
[2c2df7d]106                                                   XMLNS_SI,
107                                                   XMLNS_BYTESTREAMS,
108                                                   XMLNS_FILETRANSFER,
[40ef702]109                                                   NULL };
110                        const char **f;
111                       
[58b5f62]112                        c = xt_new_node( "identity", NULL, NULL );
113                        xt_add_attr( c, "category", "client" );
114                        xt_add_attr( c, "type", "pc" );
115                        xt_add_attr( c, "name", "BitlBee" );
116                        xt_add_child( reply, c );
117                       
[40ef702]118                        for( f = features; *f; f ++ )
119                        {
120                                c = xt_new_node( "feature", NULL, NULL );
121                                xt_add_attr( c, "var", *f );
122                                xt_add_child( reply, c );
123                        }
[58b5f62]124                }
125                else
126                {
127                        xt_free_node( reply );
[2c2df7d]128                        reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL );
[259edd4]129                        pack = 0;
[58b5f62]130                }
[259edd4]131        }
132        else if( strcmp( type, "set" ) == 0 )
133        {
[2c2df7d]134                if(  ( c = xt_find_node( node->children, "si" ) ) &&
135                     ( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_SI ) == 0 ) )
136                {
137                        return jabber_si_handle_request( ic, node, c );
138                } else if( !( c = xt_find_node( node->children, "query" ) ) ||
[dfa41a4]139                    !( s = xt_find_attr( c, "xmlns" ) ) )
140                {
[84b045d]141                        imcb_log( ic, "WARNING: Received incomplete IQ-%s packet", type );
[dfa41a4]142                        return XT_HANDLED;
[2c2df7d]143                } else if( strcmp( s, XMLNS_ROSTER ) == 0 )
144                {
[9bcbe48]145                /* This is a roster push. XMPP servers send this when someone
146                   was added to (or removed from) the buddy list. AFAIK they're
147                   sent even if we added this buddy in our own session. */
[0da65d5]148                        int bare_len = strlen( ic->acc->user );
[abbd8ed]149                       
150                        if( ( s = xt_find_attr( node, "from" ) ) == NULL ||
[0da65d5]151                            ( strncmp( s, ic->acc->user, bare_len ) == 0 &&
[abbd8ed]152                              ( s[bare_len] == 0 || s[bare_len] == '/' ) ) )
153                        {
[0da65d5]154                                jabber_parse_roster( ic, node, NULL );
[abbd8ed]155                               
156                                /* Should we generate a reply here? Don't think it's
157                                   very important... */
158                        }
159                        else
160                        {
[84b045d]161                                imcb_log( ic, "WARNING: %s tried to fake a roster push!", s ? s : "(unknown)" );
[abbd8ed]162                               
163                                xt_free_node( reply );
[2c2df7d]164                                reply = jabber_make_error_packet( node, "not-allowed", "cancel", NULL );
[abbd8ed]165                                pack = 0;
166                        }
[2c2df7d]167                } else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 )
168                {
169                        /* Bytestream Request (stage 2 of file transfer) */
170                        return jabber_bs_request( ic, node, c );
171                } else
[dfa41a4]172                {
173                        xt_free_node( reply );
[2c2df7d]174                        reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL );
[dfa41a4]175                        pack = 0;
176                }
[259edd4]177        }
178       
179        /* If we recognized the xmlns and managed to generate a reply,
180           finish and send it. */
181        if( reply )
182        {
183                /* Normally we still have to pack it into an iq-result
184                   packet, but for errors, for example, we don't. */
185                if( pack )
[58b5f62]186                {
187                        reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), reply );
188                        if( ( s = xt_find_attr( node, "id" ) ) )
189                                xt_add_attr( reply, "id", s );
190                }
[259edd4]191               
[0da65d5]192                st = jabber_write_packet( ic, reply );
[259edd4]193                xt_free_node( reply );
194                if( !st )
195                        return XT_ABORT;
[58b5f62]196        }
[70f6aab8]197       
[861c199]198        return XT_HANDLED;
199}
200
[0da65d5]201static xt_status jabber_do_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
202static xt_status jabber_finish_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
[861c199]203
[0da65d5]204int jabber_init_iq_auth( struct im_connection *ic )
[861c199]205{
[0da65d5]206        struct jabber_data *jd = ic->proto_data;
[861c199]207        struct xt_node *node;
208        int st;
[fe7a554]209       
[861c199]210        node = xt_new_node( "query", NULL, xt_new_node( "username", jd->username, NULL ) );
[47d3ac4]211        xt_add_attr( node, "xmlns", XMLNS_AUTH );
[861c199]212        node = jabber_make_packet( "iq", "get", NULL, node );
213       
[0da65d5]214        jabber_cache_add( ic, node, jabber_do_iq_auth );
215        st = jabber_write_packet( ic, node );
[861c199]216       
217        return st;
218}
219
[0da65d5]220static xt_status jabber_do_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
[861c199]221{
[0da65d5]222        struct jabber_data *jd = ic->proto_data;
[861c199]223        struct xt_node *reply, *query;
224        xt_status st;
225        char *s;
226       
[9bcbe48]227        if( !( query = xt_find_node( node->children, "query" ) ) )
228        {
[84b045d]229                imcb_log( ic, "WARNING: Received incomplete IQ packet while authenticating" );
[c2fb3809]230                imc_logout( ic, FALSE );
[9bcbe48]231                return XT_HANDLED;
232        }
[861c199]233       
234        /* Time to authenticate ourselves! */
235        reply = xt_new_node( "query", NULL, NULL );
[47d3ac4]236        xt_add_attr( reply, "xmlns", XMLNS_AUTH );
[861c199]237        xt_add_child( reply, xt_new_node( "username", jd->username, NULL ) );
[0da65d5]238        xt_add_child( reply, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
[861c199]239       
240        if( xt_find_node( query->children, "digest" ) && ( s = xt_find_attr( jd->xt->root, "id" ) ) )
[21167d2]241        {
[861c199]242                /* We can do digest authentication, it seems, and of
243                   course we prefer that. */
[77bfd07]244                sha1_state_t sha;
[e727608]245                char hash_hex[41];
[861c199]246                unsigned char hash[20];
247                int i;
[21167d2]248               
[77bfd07]249                sha1_init( &sha );
250                sha1_append( &sha, (unsigned char*) s, strlen( s ) );
251                sha1_append( &sha, (unsigned char*) ic->acc->pass, strlen( ic->acc->pass ) );
252                sha1_finish( &sha, hash );
[21167d2]253               
[861c199]254                for( i = 0; i < 20; i ++ )
255                        sprintf( hash_hex + i * 2, "%02x", hash[i] );
[21167d2]256               
[861c199]257                xt_add_child( reply, xt_new_node( "digest", hash_hex, NULL ) );
[21167d2]258        }
[861c199]259        else if( xt_find_node( query->children, "password" ) )
[0b4a0db]260        {
[861c199]261                /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
[0da65d5]262                xt_add_child( reply, xt_new_node( "password", ic->acc->pass, NULL ) );
[0b4a0db]263        }
[861c199]264        else
[70f6aab8]265        {
[861c199]266                xt_free_node( reply );
[995913b]267               
[84b045d]268                imcb_error( ic, "Can't find suitable authentication method" );
[c2fb3809]269                imc_logout( ic, FALSE );
[861c199]270                return XT_ABORT;
[70f6aab8]271        }
[861c199]272       
273        reply = jabber_make_packet( "iq", "set", NULL, reply );
[0da65d5]274        jabber_cache_add( ic, reply, jabber_finish_iq_auth );
275        st = jabber_write_packet( ic, reply );
[861c199]276       
277        return st ? XT_HANDLED : XT_ABORT;
278}
279
[0da65d5]280static xt_status jabber_finish_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
[861c199]281{
[0da65d5]282        struct jabber_data *jd = ic->proto_data;
[9bcbe48]283        char *type;
284       
285        if( !( type = xt_find_attr( node, "type" ) ) )
286        {
[84b045d]287                imcb_log( ic, "WARNING: Received incomplete IQ packet while authenticating" );
[c2fb3809]288                imc_logout( ic, FALSE );
[9bcbe48]289                return XT_HANDLED;
290        }
[861c199]291       
292        if( strcmp( type, "error" ) == 0 )
[70f6aab8]293        {
[84b045d]294                imcb_error( ic, "Authentication failure" );
[c2fb3809]295                imc_logout( ic, FALSE );
[861c199]296                return XT_ABORT;
297        }
298        else if( strcmp( type, "result" ) == 0 )
299        {
300                /* This happens when we just successfully authenticated the
301                   old (non-SASL) way. */
302                jd->flags |= JFLAG_AUTHENTICATED;
[0da65d5]303                if( !jabber_get_roster( ic ) )
[70f6aab8]304                        return XT_ABORT;
305        }
[f06894d]306       
307        return XT_HANDLED;
308}
309
[0da65d5]310xt_status jabber_pkt_bind_sess( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
[21167d2]311{
[0da65d5]312        struct jabber_data *jd = ic->proto_data;
[861c199]313        struct xt_node *c;
314        char *s;
[21167d2]315       
[861c199]316        if( ( c = xt_find_node( node->children, "bind" ) ) )
317        {
318                c = xt_find_node( c->children, "jid" );
319                if( c && c->text_len && ( s = strchr( c->text, '/' ) ) &&
[0da65d5]320                    strcmp( s + 1, set_getstr( &ic->acc->set, "resource" ) ) != 0 )
[84b045d]321                        imcb_log( ic, "Server changed session resource string to `%s'", s + 1 );
[861c199]322               
323                jd->flags &= ~JFLAG_WAIT_BIND;
324        }
325        else
326        {
327                jd->flags &= ~JFLAG_WAIT_SESSION;
328        }
[21167d2]329       
[861c199]330        if( ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
331        {
[0da65d5]332                if( !jabber_get_roster( ic ) )
[861c199]333                        return XT_ABORT;
334        }
[21167d2]335       
[861c199]336        return XT_HANDLED;
[21167d2]337}
[70f6aab8]338
[0da65d5]339int jabber_get_roster( struct im_connection *ic )
[70f6aab8]340{
341        struct xt_node *node;
342        int st;
343       
[84b045d]344        imcb_log( ic, "Authenticated, requesting buddy list" );
[70f6aab8]345       
346        node = xt_new_node( "query", NULL, NULL );
[47d3ac4]347        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
[70f6aab8]348        node = jabber_make_packet( "iq", "get", NULL, node );
349       
[0da65d5]350        jabber_cache_add( ic, node, jabber_parse_roster );
351        st = jabber_write_packet( ic, node );
[70f6aab8]352       
353        return st;
354}
[cfbb3a6]355
[0da65d5]356static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
[861c199]357{
358        struct xt_node *query, *c;
[abbd8ed]359        int initial = ( orig != NULL );
[861c199]360       
[9bcbe48]361        if( !( query = xt_find_node( node->children, "query" ) ) )
362        {
[84b045d]363                imcb_log( ic, "WARNING: Received NULL roster packet" );
[9bcbe48]364                return XT_HANDLED;
365        }
[861c199]366       
367        c = query->children;
368        while( ( c = xt_find_node( c, "item" ) ) )
369        {
[f0cb961]370                struct xt_node *group = xt_find_node( node->children, "group" );
[861c199]371                char *jid = xt_find_attr( c, "jid" );
372                char *name = xt_find_attr( c, "name" );
373                char *sub = xt_find_attr( c, "subscription" );
374               
[f0cb961]375                if( jid && sub )
[abbd8ed]376                {
377                        if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) )
378                        {
[f0cb961]379                                if( initial || imcb_find_buddy( ic, jid ) == NULL )
380                                        imcb_add_buddy( ic, jid, ( group && group->text_len ) ?
381                                                                   group->text : NULL );
382                               
[d06eabf]383                                if( name )
384                                        imcb_rename_buddy( ic, jid, name );
[abbd8ed]385                        }
386                        else if( strcmp( sub, "remove" ) == 0 )
387                        {
[0da65d5]388                                jabber_buddy_remove_bare( ic, jid );
[998b103]389                                imcb_remove_buddy( ic, jid, NULL );
[abbd8ed]390                        }
391                }
[861c199]392               
393                c = c->next;
394        }
395       
[abbd8ed]396        if( initial )
[84b045d]397                imcb_connected( ic );
[861c199]398       
399        return XT_HANDLED;
400}
401
[0da65d5]402int jabber_get_vcard( struct im_connection *ic, char *bare_jid )
[1991be6]403{
404        struct xt_node *node;
405       
406        if( strchr( bare_jid, '/' ) )
407                return 1;       /* This was an error, but return 0 should only be done if the connection died... */
408       
409        node = xt_new_node( "vCard", NULL, NULL );
[47d3ac4]410        xt_add_attr( node, "xmlns", XMLNS_VCARD );
[1991be6]411        node = jabber_make_packet( "iq", "get", bare_jid, node );
412       
[0da65d5]413        jabber_cache_add( ic, node, jabber_iq_display_vcard );
414        return jabber_write_packet( ic, node );
[1991be6]415}
416
[0da65d5]417static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
[1991be6]418{
[0da65d5]419        struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
[1991be6]420        GString *reply;
421        char *s;
422       
423        if( ( s = xt_find_attr( node, "type" ) ) == NULL ||
424            strcmp( s, "result" ) != 0 ||
425            ( vc = xt_find_node( node->children, "vCard" ) ) == NULL )
426        {
427                s = xt_find_attr( orig, "to" ); /* If this returns NULL something's wrong.. */
[84b045d]428                imcb_log( ic, "Could not retrieve vCard of %s", s ? s : "(NULL)" );
[1991be6]429                return XT_HANDLED;
430        }
431       
432        s = xt_find_attr( orig, "to" );
433        reply = g_string_new( "vCard information for " );
434        reply = g_string_append( reply, s ? s : "(NULL)" );
435        reply = g_string_append( reply, ":\n" );
436       
437        /* I hate this format, I really do... */
438       
439        if( ( c = xt_find_node( vc->children, "FN" ) ) && c->text_len )
440                g_string_append_printf( reply, "Name: %s\n", c->text );
441       
442        if( ( c = xt_find_node( vc->children, "N" ) ) && c->children )
443        {
444                reply = g_string_append( reply, "Full name:" );
445               
446                if( ( sc = xt_find_node( c->children, "PREFIX" ) ) && sc->text_len )
447                        g_string_append_printf( reply, " %s", sc->text );
448                if( ( sc = xt_find_node( c->children, "GIVEN" ) ) && sc->text_len )
449                        g_string_append_printf( reply, " %s", sc->text );
450                if( ( sc = xt_find_node( c->children, "MIDDLE" ) ) && sc->text_len )
451                        g_string_append_printf( reply, " %s", sc->text );
452                if( ( sc = xt_find_node( c->children, "FAMILY" ) ) && sc->text_len )
453                        g_string_append_printf( reply, " %s", sc->text );
454                if( ( sc = xt_find_node( c->children, "SUFFIX" ) ) && sc->text_len )
455                        g_string_append_printf( reply, " %s", sc->text );
456               
457                reply = g_string_append_c( reply, '\n' );
458        }
459       
460        if( ( c = xt_find_node( vc->children, "NICKNAME" ) ) && c->text_len )
461                g_string_append_printf( reply, "Nickname: %s\n", c->text );
462       
463        if( ( c = xt_find_node( vc->children, "BDAY" ) ) && c->text_len )
464                g_string_append_printf( reply, "Date of birth: %s\n", c->text );
465       
466        /* Slightly alternative use of for... ;-) */
467        for( c = vc->children; ( c = xt_find_node( c, "EMAIL" ) ); c = c->next )
468        {
469                if( ( sc = xt_find_node( c->children, "USERID" ) ) == NULL || sc->text_len == 0 )
470                        continue;
471               
472                if( xt_find_node( c->children, "HOME" ) )
473                        s = "Home";
474                else if( xt_find_node( c->children, "WORK" ) )
475                        s = "Work";
476                else
477                        s = "Misc.";
478               
479                g_string_append_printf( reply, "%s e-mail address: %s\n", s, sc->text );
480        }
481       
482        if( ( c = xt_find_node( vc->children, "URL" ) ) && c->text_len )
483                g_string_append_printf( reply, "Homepage: %s\n", c->text );
484       
485        /* Slightly alternative use of for... ;-) */
486        for( c = vc->children; ( c = xt_find_node( c, "ADR" ) ); c = c->next )
487        {
488                if( xt_find_node( c->children, "HOME" ) )
489                        s = "Home";
490                else if( xt_find_node( c->children, "WORK" ) )
491                        s = "Work";
492                else
493                        s = "Misc.";
494               
495                g_string_append_printf( reply, "%s address: ", s );
496               
497                if( ( sc = xt_find_node( c->children, "STREET" ) ) && sc->text_len )
498                        g_string_append_printf( reply, "%s ", sc->text );
499                if( ( sc = xt_find_node( c->children, "EXTADR" ) ) && sc->text_len )
500                        g_string_append_printf( reply, "%s, ", sc->text );
501                if( ( sc = xt_find_node( c->children, "PCODE" ) ) && sc->text_len )
502                        g_string_append_printf( reply, "%s, ", sc->text );
503                if( ( sc = xt_find_node( c->children, "LOCALITY" ) ) && sc->text_len )
504                        g_string_append_printf( reply, "%s, ", sc->text );
505                if( ( sc = xt_find_node( c->children, "REGION" ) ) && sc->text_len )
506                        g_string_append_printf( reply, "%s, ", sc->text );
507                if( ( sc = xt_find_node( c->children, "CTRY" ) ) && sc->text_len )
508                        g_string_append_printf( reply, "%s", sc->text );
509               
510                if( reply->str[reply->len-2] == ',' )
511                        reply = g_string_truncate( reply, reply->len-2 );
512               
513                reply = g_string_append_c( reply, '\n' );
514        }
515       
516        for( c = vc->children; ( c = xt_find_node( c, "TEL" ) ); c = c->next )
517        {
518                if( ( sc = xt_find_node( c->children, "NUMBER" ) ) == NULL || sc->text_len == 0 )
519                        continue;
520               
521                if( xt_find_node( c->children, "HOME" ) )
522                        s = "Home";
523                else if( xt_find_node( c->children, "WORK" ) )
524                        s = "Work";
525                else
526                        s = "Misc.";
527               
528                g_string_append_printf( reply, "%s phone number: %s\n", s, sc->text );
529        }
530       
531        if( ( c = xt_find_node( vc->children, "DESC" ) ) && c->text_len )
532                g_string_append_printf( reply, "Other information:\n%s", c->text );
533       
534        /* *sigh* */
535       
[84b045d]536        imcb_log( ic, "%s", reply->str );
[1991be6]537        g_string_free( reply, TRUE );
538       
539        return XT_HANDLED;
540}
541
[0da65d5]542int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name )
[cfbb3a6]543{
544        struct xt_node *node;
545        int st;
546       
547        /* Build the item entry */
548        node = xt_new_node( "item", NULL, NULL );
549        xt_add_attr( node, "jid", handle );
550        if( name )
551                xt_add_attr( node, "name", name );
552       
553        /* And pack it into a roster-add packet */
554        node = xt_new_node( "query", NULL, node );
[47d3ac4]555        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
[cfbb3a6]556        node = jabber_make_packet( "iq", "set", NULL, node );
557       
[0da65d5]558        st = jabber_write_packet( ic, node );
[cfbb3a6]559       
560        xt_free_node( node );
561        return st;
562}
563
[0da65d5]564int jabber_remove_from_roster( struct im_connection *ic, char *handle )
[cfbb3a6]565{
566        struct xt_node *node;
567        int st;
568       
569        /* Build the item entry */
570        node = xt_new_node( "item", NULL, NULL );
571        xt_add_attr( node, "jid", handle );
572        xt_add_attr( node, "subscription", "remove" );
573       
574        /* And pack it into a roster-add packet */
575        node = xt_new_node( "query", NULL, node );
[47d3ac4]576        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
[cfbb3a6]577        node = jabber_make_packet( "iq", "set", NULL, node );
578       
[0da65d5]579        st = jabber_write_packet( ic, node );
[cfbb3a6]580       
581        xt_free_node( node );
582        return st;
583}
Note: See TracBrowser for help on using the repository browser.