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

Last change on this file since 2ff2076 was 2ff2076, checked in by ulim <a.sporto+bee@…>, at 2007-12-03T14:28:45Z

Intermediate commit. Sending seems to work. TODOs:

  • move from out_of_data to is_writable, eliminate buffers
  • implement "transfers reject [id]"
  • documentation in commands.xml
  • implement throughput and cummulative throughput boundaries
  • feature discovery before sending
  • implement sending over a proxy (proxy discovery, socks5 client handshake for sending, activate message)
  • integrate toxik-mek-ft
  • Property mode set to 100644
File size: 18.1 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Jabber module - IQ packets                                               *
5*                                                                           *
6*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24#include "jabber.h"
25#include "sha1.h"
26
27static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
28static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
29
30xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )
31{
32        struct im_connection *ic = data;
33        struct jabber_data *jd = ic->proto_data;
34        struct xt_node *c, *reply = NULL;
35        char *type, *s;
36        int st, pack = 1;
37       
38        type = xt_find_attr( node, "type" );
39       
40        if( !type )
41        {
42                imcb_error( ic, "Received IQ packet without type." );
43                imc_logout( ic, TRUE );
44                return XT_ABORT;
45        }
46       
47        if( strcmp( type, "result" ) == 0 || strcmp( type, "error" ) == 0 )
48        {
49                struct jabber_cache_entry *entry;
50               
51                if( ( s = xt_find_attr( node, "id" ) ) == NULL ||
52                    strncmp( s, jd->cached_id_prefix, strlen( jd->cached_id_prefix ) ) != 0 )
53                {
54                        /* Silently ignore it, without an ID (or a non-cache
55                           ID) we don't know how to handle the packet and we
56                           probably don't have to. */
57                        return XT_HANDLED;
58                }
59               
60                entry = g_hash_table_lookup( jd->node_cache, s );
61               
62                if( entry == NULL )
63                        imcb_log( ic, "WARNING: Received IQ-%s packet with unknown/expired ID %s!", type, s );
64                else if( entry->func )
65                        return entry->func( ic, node, entry->node );
66        }
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                {
72                        imcb_log( ic, "WARNING: Received incomplete IQ-%s packet", type );
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. ;-) */
80                if( strcmp( s, XMLNS_VERSION ) == 0 )
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                }
86                else if( strcmp( s, XMLNS_TIME ) == 0 )
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                }
100                else if( strcmp( s, XMLNS_DISCOVER ) == 0 )
101                {
102                        const char *features[] = { XMLNS_VERSION,
103                                                   XMLNS_TIME,
104                                                   XMLNS_CHATSTATES,
105                                                   XMLNS_MUC,
106                                                   XMLNS_SI,
107                                                   XMLNS_BYTESTREAMS,
108                                                   XMLNS_FILETRANSFER,
109                                                   NULL };
110                        const char **f;
111                       
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                       
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                        }
124                }
125                else
126                {
127                        xt_free_node( reply );
128                        reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL );
129                        pack = 0;
130                }
131        }
132        else if( strcmp( type, "set" ) == 0 )
133        {
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" ) ) ||
139                    !( s = xt_find_attr( c, "xmlns" ) ) )
140                {
141                        imcb_log( ic, "WARNING: Received incomplete IQ-%s packet", type );
142                        return XT_HANDLED;
143                } else if( strcmp( s, XMLNS_ROSTER ) == 0 )
144                {
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. */
148                        int bare_len = strlen( ic->acc->user );
149                       
150                        if( ( s = xt_find_attr( node, "from" ) ) == NULL ||
151                            ( strncmp( s, ic->acc->user, bare_len ) == 0 &&
152                              ( s[bare_len] == 0 || s[bare_len] == '/' ) ) )
153                        {
154                                jabber_parse_roster( ic, node, NULL );
155                               
156                                /* Should we generate a reply here? Don't think it's
157                                   very important... */
158                        }
159                        else
160                        {
161                                imcb_log( ic, "WARNING: %s tried to fake a roster push!", s ? s : "(unknown)" );
162                               
163                                xt_free_node( reply );
164                                reply = jabber_make_error_packet( node, "not-allowed", "cancel", NULL );
165                                pack = 0;
166                        }
167                } else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 )
168                {
169                        /* Bytestream Request (stage 2 of file transfer) */
170                        return jabber_bs_recv_request( ic, node, c );
171                } else
172                {
173                        xt_free_node( reply );
174                        reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL );
175                        pack = 0;
176                }
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 )
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                }
191               
192                st = jabber_write_packet( ic, reply );
193                xt_free_node( reply );
194                if( !st )
195                        return XT_ABORT;
196        }
197       
198        return XT_HANDLED;
199}
200
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 );
203
204int jabber_init_iq_auth( struct im_connection *ic )
205{
206        struct jabber_data *jd = ic->proto_data;
207        struct xt_node *node;
208        int st;
209       
210        node = xt_new_node( "query", NULL, xt_new_node( "username", jd->username, NULL ) );
211        xt_add_attr( node, "xmlns", XMLNS_AUTH );
212        node = jabber_make_packet( "iq", "get", NULL, node );
213       
214        jabber_cache_add( ic, node, jabber_do_iq_auth );
215        st = jabber_write_packet( ic, node );
216       
217        return st;
218}
219
220static xt_status jabber_do_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
221{
222        struct jabber_data *jd = ic->proto_data;
223        struct xt_node *reply, *query;
224        xt_status st;
225        char *s;
226       
227        if( !( query = xt_find_node( node->children, "query" ) ) )
228        {
229                imcb_log( ic, "WARNING: Received incomplete IQ packet while authenticating" );
230                imc_logout( ic, FALSE );
231                return XT_HANDLED;
232        }
233       
234        /* Time to authenticate ourselves! */
235        reply = xt_new_node( "query", NULL, NULL );
236        xt_add_attr( reply, "xmlns", XMLNS_AUTH );
237        xt_add_child( reply, xt_new_node( "username", jd->username, NULL ) );
238        xt_add_child( reply, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
239       
240        if( xt_find_node( query->children, "digest" ) && ( s = xt_find_attr( jd->xt->root, "id" ) ) )
241        {
242                /* We can do digest authentication, it seems, and of
243                   course we prefer that. */
244                sha1_state_t sha;
245                char hash_hex[41];
246                unsigned char hash[20];
247                int i;
248               
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 );
253               
254                for( i = 0; i < 20; i ++ )
255                        sprintf( hash_hex + i * 2, "%02x", hash[i] );
256               
257                xt_add_child( reply, xt_new_node( "digest", hash_hex, NULL ) );
258        }
259        else if( xt_find_node( query->children, "password" ) )
260        {
261                /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
262                xt_add_child( reply, xt_new_node( "password", ic->acc->pass, NULL ) );
263        }
264        else
265        {
266                xt_free_node( reply );
267               
268                imcb_error( ic, "Can't find suitable authentication method" );
269                imc_logout( ic, FALSE );
270                return XT_ABORT;
271        }
272       
273        reply = jabber_make_packet( "iq", "set", NULL, reply );
274        jabber_cache_add( ic, reply, jabber_finish_iq_auth );
275        st = jabber_write_packet( ic, reply );
276       
277        return st ? XT_HANDLED : XT_ABORT;
278}
279
280static xt_status jabber_finish_iq_auth( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
281{
282        struct jabber_data *jd = ic->proto_data;
283        char *type;
284       
285        if( !( type = xt_find_attr( node, "type" ) ) )
286        {
287                imcb_log( ic, "WARNING: Received incomplete IQ packet while authenticating" );
288                imc_logout( ic, FALSE );
289                return XT_HANDLED;
290        }
291       
292        if( strcmp( type, "error" ) == 0 )
293        {
294                imcb_error( ic, "Authentication failure" );
295                imc_logout( ic, FALSE );
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;
303                if( !jabber_get_roster( ic ) )
304                        return XT_ABORT;
305        }
306       
307        return XT_HANDLED;
308}
309
310xt_status jabber_pkt_bind_sess( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
311{
312        struct jabber_data *jd = ic->proto_data;
313        struct xt_node *c;
314        char *s;
315       
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, '/' ) ) &&
320                    strcmp( s + 1, set_getstr( &ic->acc->set, "resource" ) ) != 0 )
321                        imcb_log( ic, "Server changed session resource string to `%s'", s + 1 );
322               
323                jd->flags &= ~JFLAG_WAIT_BIND;
324        }
325        else
326        {
327                jd->flags &= ~JFLAG_WAIT_SESSION;
328        }
329       
330        if( ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
331        {
332                if( !jabber_get_roster( ic ) )
333                        return XT_ABORT;
334        }
335       
336        return XT_HANDLED;
337}
338
339int jabber_get_roster( struct im_connection *ic )
340{
341        struct xt_node *node;
342        int st;
343       
344        imcb_log( ic, "Authenticated, requesting buddy list" );
345       
346        node = xt_new_node( "query", NULL, NULL );
347        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
348        node = jabber_make_packet( "iq", "get", NULL, node );
349       
350        jabber_cache_add( ic, node, jabber_parse_roster );
351        st = jabber_write_packet( ic, node );
352       
353        return st;
354}
355
356static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
357{
358        struct xt_node *query, *c;
359        int initial = ( orig != NULL );
360       
361        if( !( query = xt_find_node( node->children, "query" ) ) )
362        {
363                imcb_log( ic, "WARNING: Received NULL roster packet" );
364                return XT_HANDLED;
365        }
366       
367        c = query->children;
368        while( ( c = xt_find_node( c, "item" ) ) )
369        {
370                struct xt_node *group = xt_find_node( node->children, "group" );
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               
375                if( jid && sub )
376                {
377                        if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) )
378                        {
379                                if( initial || imcb_find_buddy( ic, jid ) == NULL )
380                                        imcb_add_buddy( ic, jid, ( group && group->text_len ) ?
381                                                                   group->text : NULL );
382                               
383                                if( name )
384                                        imcb_rename_buddy( ic, jid, name );
385                        }
386                        else if( strcmp( sub, "remove" ) == 0 )
387                        {
388                                jabber_buddy_remove_bare( ic, jid );
389                                imcb_remove_buddy( ic, jid, NULL );
390                        }
391                }
392               
393                c = c->next;
394        }
395       
396        if( initial )
397                imcb_connected( ic );
398       
399        return XT_HANDLED;
400}
401
402int jabber_get_vcard( struct im_connection *ic, char *bare_jid )
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 );
410        xt_add_attr( node, "xmlns", XMLNS_VCARD );
411        node = jabber_make_packet( "iq", "get", bare_jid, node );
412       
413        jabber_cache_add( ic, node, jabber_iq_display_vcard );
414        return jabber_write_packet( ic, node );
415}
416
417static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
418{
419        struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
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.. */
428                imcb_log( ic, "Could not retrieve vCard of %s", s ? s : "(NULL)" );
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       
536        imcb_log( ic, "%s", reply->str );
537        g_string_free( reply, TRUE );
538       
539        return XT_HANDLED;
540}
541
542int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name )
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 );
555        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
556        node = jabber_make_packet( "iq", "set", NULL, node );
557       
558        st = jabber_write_packet( ic, node );
559       
560        xt_free_node( node );
561        return st;
562}
563
564int jabber_remove_from_roster( struct im_connection *ic, char *handle )
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 );
576        xt_add_attr( node, "xmlns", XMLNS_ROSTER );
577        node = jabber_make_packet( "iq", "set", NULL, node );
578       
579        st = jabber_write_packet( ic, node );
580       
581        xt_free_node( node );
582        return st;
583}
Note: See TracBrowser for help on using the repository browser.