source: protocols/msn/soap.c @ 660cb00

Last change on this file since 660cb00 was 660cb00, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-15T13:23:03Z

Parse authentication errors.

  • Property mode set to 100644
File size: 24.2 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* MSN module - All the SOAPy XML stuff.
8   Some manager at Microsoft apparently thought MSNP wasn't XMLy enough so
9   someone stepped up and changed that. This is the result. Kilobytes and
10   more kilobytes of XML vomit to transfer tiny bits of informaiton. */
11
12/*
13  This program is free software; you can redistribute it and/or modify
14  it under the terms of the GNU General Public License as published by
15  the Free Software Foundation; either version 2 of the License, or
16  (at your option) any later version.
17
18  This program is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  GNU General Public License for more details.
22
23  You should have received a copy of the GNU General Public License with
24  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
25  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
26  Suite 330, Boston, MA  02111-1307  USA
27*/
28
29#include "http_client.h"
30#include "soap.h"
31#include "msn.h"
32#include "bitlbee.h"
33#include "url.h"
34#include "misc.h"
35#include "sha1.h"
36#include "base64.h"
37#include "xmltree.h"
38#include <ctype.h>
39#include <errno.h>
40
41/* This file tries to make SOAP stuff pretty simple to do by letting you just
42   provide a function to build a request, a few functions to parse various
43   parts of the response, and a function to run when the full response was
44   received and parsed. See the various examples below. */
45
46typedef enum
47{
48        MSN_SOAP_OK,
49        MSN_SOAP_RETRY,
50        MSN_SOAP_ABORT,
51} msn_soap_result_t;
52
53struct msn_soap_req_data;
54typedef int (*msn_soap_func) ( struct msn_soap_req_data * );
55
56struct msn_soap_req_data
57{
58        void *data;
59        struct im_connection *ic;
60        int ttl;
61       
62        char *url, *action, *payload;
63        struct http_request *http_req;
64       
65        const struct xt_handler_entry *xml_parser;
66        msn_soap_func build_request, handle_response, free_data;
67};
68
69static int msn_soap_send_request( struct msn_soap_req_data *req );
70
71static int msn_soap_start( struct im_connection *ic,
72                    void *data,
73                    msn_soap_func build_request,
74                    const struct xt_handler_entry *xml_parser,
75                    msn_soap_func handle_response,
76                    msn_soap_func free_data )
77{
78        struct msn_soap_req_data *req = g_new0( struct msn_soap_req_data, 1 );
79       
80        req->ic = ic;
81        req->data = data;
82        req->xml_parser = xml_parser;
83        req->build_request = build_request;
84        req->handle_response = handle_response;
85        req->free_data = free_data;
86        req->ttl = 3;
87       
88        return msn_soap_send_request( req );
89}
90
91static void msn_soap_handle_response( struct http_request *http_req );
92
93static int msn_soap_send_request( struct msn_soap_req_data *soap_req )
94{
95        char *http_req;
96        char *soap_action = NULL;
97        url_t url;
98       
99        soap_req->build_request( soap_req );
100       
101        if( soap_req->action )
102                soap_action = g_strdup_printf( "SOAPAction: \"%s\"\r\n", soap_req->action );
103       
104        url_set( &url, soap_req->url );
105        http_req = g_strdup_printf( SOAP_HTTP_REQUEST, url.file, url.host,
106                soap_action ? soap_action : "",
107                strlen( soap_req->payload ), soap_req->payload );
108       
109        soap_req->http_req = http_dorequest( url.host, url.port, url.proto == PROTO_HTTPS,
110                http_req, msn_soap_handle_response, soap_req );
111       
112        g_free( http_req );
113        g_free( soap_action );
114       
115        return soap_req->http_req != NULL;
116}
117
118static void msn_soap_handle_response( struct http_request *http_req )
119{
120        struct msn_soap_req_data *soap_req = http_req->data;
121        int st;
122       
123        if( http_req->body_size > 0 )
124        {
125                struct xt_parser *parser;
126               
127                parser = xt_new( soap_req->xml_parser, soap_req );
128                xt_feed( parser, http_req->reply_body, http_req->body_size );
129                xt_handle( parser, NULL, -1 );
130                xt_free( parser );
131        }
132       
133        st = soap_req->handle_response( soap_req );
134       
135        g_free( soap_req->url );
136        g_free( soap_req->action );
137        g_free( soap_req->payload );
138        soap_req->url = soap_req->action = soap_req->payload = NULL;
139       
140        if( st == MSN_SOAP_RETRY && --soap_req->ttl )
141                msn_soap_send_request( soap_req );
142        else
143        {
144                soap_req->free_data( soap_req );
145                g_free( soap_req );
146        }
147}
148
149static char *msn_soap_abservice_build( const char *body_fmt, const char *scenario, const char *ticket, ... )
150{
151        va_list params;
152        char *ret, *format, *body;
153       
154        format = g_markup_printf_escaped( SOAP_ABSERVICE_PAYLOAD, scenario, ticket );
155       
156        va_start( params, ticket );
157        body = g_strdup_vprintf( body_fmt, params );
158        va_end( params );
159       
160        ret = g_strdup_printf( format, body );
161        g_free( body );
162        g_free( format );
163       
164        return ret;
165}
166
167
168/* passport_sso: Authentication MSNP15+ */
169
170struct msn_soap_passport_sso_data
171{
172        char *policy;
173        char *nonce;
174        char *secret;
175        char *error;
176};
177
178static int msn_soap_passport_sso_build_request( struct msn_soap_req_data *soap_req )
179{
180        struct msn_soap_passport_sso_data *sd = soap_req->data;
181        struct im_connection *ic = soap_req->ic;
182       
183        if( g_str_has_suffix( ic->acc->user, "@msn.com" ) )
184                soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL_MSN );
185        else
186                soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL );
187       
188        soap_req->payload = g_markup_printf_escaped( SOAP_PASSPORT_SSO_PAYLOAD,
189                ic->acc->user, ic->acc->pass, sd->policy );
190       
191        return MSN_SOAP_OK;
192}
193
194static xt_status msn_soap_passport_sso_token( struct xt_node *node, gpointer data )
195{
196        struct msn_soap_req_data *soap_req = data;
197        struct msn_soap_passport_sso_data *sd = soap_req->data;
198        struct msn_data *md = soap_req->ic->proto_data;
199        struct xt_node *p;
200        char *id;
201       
202        if( ( id = xt_find_attr( node, "Id" ) ) == NULL )
203                return XT_HANDLED;
204        id += strlen( id ) - 1;
205        if( *id == '1' &&
206            ( p = xt_find_path( node, "../../wst:RequestedProofToken/wst:BinarySecret" ) ) &&
207            p->text )
208                sd->secret = g_strdup( p->text );
209       
210        *id -= '1';
211        if( *id >= 0 && *id <= 2 )
212        {
213                g_free( md->tokens[(int)*id] );
214                md->tokens[(int)*id] = g_strdup( node->text );
215        }
216       
217        return XT_HANDLED;
218}
219
220static xt_status msn_soap_passport_failure( struct xt_node *node, gpointer data )
221{
222        struct msn_soap_req_data *soap_req = data;
223        struct msn_soap_passport_sso_data *sd = soap_req->data;
224        struct xt_node *code = xt_find_node( node->children, "faultcode" );
225        struct xt_node *string = xt_find_node( node->children, "faultstring" );
226       
227        if( code == NULL || code->text_len == 0 )
228                sd->error = g_strdup( "Unknown error" );
229        else
230                sd->error = g_strdup_printf( "%s (%s)", code->text, string && string->text_len ?
231                                             string->text : "no description available" );
232       
233        return XT_HANDLED;
234}
235
236static const struct xt_handler_entry msn_soap_passport_sso_parser[] = {
237        { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token },
238        { "S:Fault", "S:Envelope", msn_soap_passport_failure },
239        { NULL, NULL, NULL }
240};
241
242static char *msn_key_fuckery( char *key, int key_len, char *type )
243{
244        unsigned char hash1[20+strlen(type)+1];
245        unsigned char hash2[20];
246        char *ret;
247       
248        sha1_hmac( key, key_len, type, 0, hash1 );
249        strcpy( (char*) hash1 + 20, type );
250        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash2 );
251       
252        /* This is okay as hash1 is read completely before it's overwritten. */
253        sha1_hmac( key, key_len, (char*) hash1, 20, hash1 );
254        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash1 );
255       
256        ret = g_malloc( 24 );
257        memcpy( ret, hash2, 20 );
258        memcpy( ret + 20, hash1, 4 );
259        return ret;
260}
261
262static int msn_soap_passport_sso_handle_response( struct msn_soap_req_data *soap_req )
263{
264        struct msn_soap_passport_sso_data *sd = soap_req->data;
265        struct im_connection *ic = soap_req->ic;
266        char *key1, *key2, *key3, *blurb64;
267        int key1_len;
268        unsigned char *padnonce, *des3res;
269        struct
270        {
271                unsigned int uStructHeaderSize; // 28. Does not count data
272                unsigned int uCryptMode; // CRYPT_MODE_CBC (1)
273                unsigned int uCipherType; // TripleDES (0x6603)
274                unsigned int uHashType; // SHA1 (0x8004)
275                unsigned int uIVLen;    // 8
276                unsigned int uHashLen;  // 20
277                unsigned int uCipherLen; // 72
278                unsigned char iv[8];
279                unsigned char hash[20];
280                unsigned char cipherbytes[72];
281        } blurb = {
282                GUINT32_TO_LE( 28 ),
283                GUINT32_TO_LE( 1 ),
284                GUINT32_TO_LE( 0x6603 ),
285                GUINT32_TO_LE( 0x8004 ),
286                GUINT32_TO_LE( 8 ),
287                GUINT32_TO_LE( 20 ),
288                GUINT32_TO_LE( 72 ),
289        };
290       
291        if( sd->secret == NULL )
292        {
293                msn_auth_got_passport_token( ic, NULL, sd->error );
294                return MSN_SOAP_OK;
295        }
296
297        key1_len = base64_decode( sd->secret, (unsigned char**) &key1 );
298       
299        key2 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY HASH" );
300        key3 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY ENCRYPTION" );
301       
302        sha1_hmac( key2, 24, sd->nonce, 0, blurb.hash );
303        padnonce = g_malloc( strlen( sd->nonce ) + 8 );
304        strcpy( (char*) padnonce, sd->nonce );
305        memset( padnonce + strlen( sd->nonce ), 8, 8 );
306       
307        random_bytes( blurb.iv, 8 );
308       
309        ssl_des3_encrypt( (unsigned char*) key3, 24, padnonce, strlen( sd->nonce ) + 8, blurb.iv, &des3res );
310        memcpy( blurb.cipherbytes, des3res, 72 );
311       
312        blurb64 = base64_encode( (unsigned char*) &blurb, sizeof( blurb ) );
313        msn_auth_got_passport_token( ic, blurb64, NULL );
314       
315        g_free( padnonce );
316        g_free( blurb64 );
317        g_free( des3res );
318        g_free( key1 );
319        g_free( key2 );
320        g_free( key3 );
321       
322        return MSN_SOAP_OK;
323}
324
325static int msn_soap_passport_sso_free_data( struct msn_soap_req_data *soap_req )
326{
327        struct msn_soap_passport_sso_data *sd = soap_req->data;
328       
329        g_free( sd->policy );
330        g_free( sd->nonce );
331        g_free( sd->secret );
332        g_free( sd->error );
333       
334        return MSN_SOAP_OK;
335}
336
337int msn_soap_passport_sso_request( struct im_connection *ic, const char *policy, const char *nonce )
338{
339        struct msn_soap_passport_sso_data *sd = g_new0( struct msn_soap_passport_sso_data, 1 );
340       
341        sd->policy = g_strdup( policy );
342        sd->nonce = g_strdup( nonce );
343       
344        return msn_soap_start( ic, sd, msn_soap_passport_sso_build_request,
345                                       msn_soap_passport_sso_parser,
346                                       msn_soap_passport_sso_handle_response,
347                                       msn_soap_passport_sso_free_data );
348}
349
350
351/* oim_send: Sending offline messages */
352
353struct msn_soap_oim_send_data
354{
355        char *to;
356        char *msg;
357        int number;
358        int need_retry;
359};
360
361static int msn_soap_oim_build_request( struct msn_soap_req_data *soap_req )
362{
363        struct msn_soap_oim_send_data *oim = soap_req->data;
364        struct im_connection *ic = soap_req->ic;
365        struct msn_data *md = ic->proto_data;
366        char *display_name_b64;
367       
368        display_name_b64 = tobase64( set_getstr( &ic->acc->set, "display_name" ) );
369       
370        soap_req->url = g_strdup( SOAP_OIM_SEND_URL );
371        soap_req->action = g_strdup( SOAP_OIM_SEND_ACTION );
372        soap_req->payload = g_markup_printf_escaped( SOAP_OIM_SEND_PAYLOAD,
373                ic->acc->user, display_name_b64, MSNP_VER, MSNP_BUILD,
374                oim->to, md->tokens[2],
375                MSNP11_PROD_ID, md->lock_key ? md->lock_key : "",
376                oim->number, oim->number, oim->msg );
377       
378        g_free( display_name_b64 );
379       
380        return MSN_SOAP_OK;
381}
382
383static xt_status msn_soap_oim_send_challenge( struct xt_node *node, gpointer data )
384{
385        struct msn_soap_req_data *soap_req = data;
386        struct msn_soap_oim_send_data *oim = soap_req->data;
387        struct im_connection *ic = soap_req->ic;
388        struct msn_data *md = ic->proto_data;
389       
390        g_free( md->lock_key );
391        md->lock_key = msn_p11_challenge( node->text );
392       
393        oim->need_retry = 1;
394       
395        return XT_HANDLED;
396}
397
398static const struct xt_handler_entry msn_soap_oim_send_parser[] = {
399        { "LockKeyChallenge", "detail", msn_soap_oim_send_challenge },
400        { NULL,               NULL,     NULL                        }
401};
402
403static int msn_soap_oim_handle_response( struct msn_soap_req_data *soap_req )
404{
405        struct msn_soap_oim_send_data *oim = soap_req->data;
406       
407        if( soap_req->http_req->status_code == 500 && oim->need_retry && soap_req->ttl > 0 )
408        {
409                oim->need_retry = 0;
410                return MSN_SOAP_RETRY;
411        }
412        else if( soap_req->http_req->status_code == 200 )
413        {
414                imcb_log( soap_req->ic, "Offline message successfully delivered to %s", oim->to );
415                return MSN_SOAP_OK;
416        }
417        else
418        {
419                imcb_log( soap_req->ic, "Failed to deliver offline message to %s:\n%s", oim->to, oim->msg );
420                return MSN_SOAP_ABORT;
421        }
422}
423
424static int msn_soap_oim_free_data( struct msn_soap_req_data *soap_req )
425{
426        struct msn_soap_oim_send_data *oim = soap_req->data;
427       
428        g_free( oim->to );
429        g_free( oim->msg );
430        g_free( oim );
431       
432        return MSN_SOAP_OK;
433}
434
435int msn_soap_oim_send( struct im_connection *ic, const char *to, const char *msg )
436{
437        struct msn_soap_oim_send_data *data;
438       
439        data = g_new0( struct msn_soap_oim_send_data, 1 );
440        data->to = g_strdup( to );
441        data->msg = tobase64( msg );
442        data->number = 1;
443       
444        return msn_soap_start( ic, data, msn_soap_oim_build_request,
445                                         msn_soap_oim_send_parser,
446                                         msn_soap_oim_handle_response,
447                                         msn_soap_oim_free_data );
448}
449
450int msn_soap_oim_send_queue( struct im_connection *ic, GSList **msgq )
451{
452        GSList *l;
453        char *n = NULL;
454       
455        for( l = *msgq; l; l = l->next )
456        {
457                struct msn_message *m = l->data;
458               
459                if( n == NULL )
460                        n = m->who;
461                if( strcmp( n, m->who ) == 0 )
462                        msn_soap_oim_send( ic, m->who, m->text );
463        }
464       
465        while( *msgq != NULL )
466        {
467                struct msn_message *m = (*msgq)->data;
468               
469                g_free( m->who );
470                g_free( m->text );
471                g_free( m );
472               
473                *msgq = g_slist_remove( *msgq, m );
474        }
475       
476        return 1;
477}
478
479
480/* memlist: Fetching the membership list (NOT address book) */
481
482static int msn_soap_memlist_build_request( struct msn_soap_req_data *soap_req )
483{
484        struct msn_data *md = soap_req->ic->proto_data;
485       
486        soap_req->url = g_strdup( SOAP_MEMLIST_URL );
487        soap_req->action = g_strdup( SOAP_MEMLIST_ACTION );
488        soap_req->payload = msn_soap_abservice_build( SOAP_MEMLIST_PAYLOAD, "Initial", md->tokens[1] );
489       
490        return 1;
491}
492
493static xt_status msn_soap_memlist_member( struct xt_node *node, gpointer data )
494{
495        bee_user_t *bu;
496        struct msn_buddy_data *bd;
497        struct xt_node *p;
498        char *role = NULL, *handle = NULL;
499        struct msn_soap_req_data *soap_req = data;
500        struct im_connection *ic = soap_req->ic;
501       
502        if( ( p = xt_find_path( node, "../../MemberRole" ) ) )
503                role = p->text;
504       
505        if( ( p = xt_find_node( node->children, "PassportName" ) ) )
506                handle = p->text;
507       
508        if( !role || !handle || 
509            !( ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) ||
510               ( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) )
511                return XT_HANDLED;
512       
513        bd = bu->data;
514        if( strcmp( role, "Allow" ) == 0 )
515                bd->flags |= MSN_BUDDY_AL;
516        else if( strcmp( role, "Block" ) == 0 )
517                bd->flags |= MSN_BUDDY_BL;
518        else if( strcmp( role, "Reverse" ) == 0 )
519        {
520                bd->flags |= MSN_BUDDY_RL;
521                msn_buddy_ask( bu );
522        }
523        else if( strcmp( role, "Pending" ) == 0 )
524        {
525                bd->flags |= MSN_BUDDY_PL;
526                msn_buddy_ask( bu );
527        }
528       
529        printf( "%s %d\n", handle, bd->flags );
530       
531        return XT_HANDLED;
532}
533
534static const struct xt_handler_entry msn_soap_memlist_parser[] = {
535        { "Member", "Members", msn_soap_memlist_member },
536        { NULL,               NULL,     NULL                        }
537};
538
539static int msn_soap_memlist_handle_response( struct msn_soap_req_data *soap_req )
540{
541        msn_soap_addressbook_request( soap_req->ic );
542       
543        return MSN_SOAP_OK;
544}
545
546static int msn_soap_memlist_free_data( struct msn_soap_req_data *soap_req )
547{
548        return 0;
549}
550
551int msn_soap_memlist_request( struct im_connection *ic )
552{
553        return msn_soap_start( ic, NULL, msn_soap_memlist_build_request,
554                                         msn_soap_memlist_parser,
555                                         msn_soap_memlist_handle_response,
556                                         msn_soap_memlist_free_data );
557}
558
559/* Variant: Adding/Removing people */
560struct msn_soap_memlist_edit_data
561{
562        char *handle;
563        gboolean add;
564        msn_buddy_flags_t list;
565};
566
567static int msn_soap_memlist_edit_build_request( struct msn_soap_req_data *soap_req )
568{
569        struct msn_data *md = soap_req->ic->proto_data;
570        struct msn_soap_memlist_edit_data *med = soap_req->data;
571        char *add, *scenario, *list;
572       
573        soap_req->url = g_strdup( SOAP_MEMLIST_URL );
574        if( med->add )
575        {
576                soap_req->action = g_strdup( SOAP_MEMLIST_ADD_ACTION );
577                add = "Add";
578        }
579        else
580        {
581                soap_req->action = g_strdup( SOAP_MEMLIST_DEL_ACTION );
582                add = "Delete";
583        }
584        switch( med->list )
585        {
586        case MSN_BUDDY_AL:
587                scenario = "BlockUnblock";
588                list = "Allow";
589                break;
590        case MSN_BUDDY_BL:
591                scenario = "BlockUnblock";
592                list = "Block";
593                break;
594        case MSN_BUDDY_RL:
595                scenario = "Timer";
596                list = "Reverse";
597                break;
598        case MSN_BUDDY_PL:
599        default:
600                scenario = "Timer";
601                list = "Pending";
602                break;
603        }
604        soap_req->payload = msn_soap_abservice_build( SOAP_MEMLIST_EDIT_PAYLOAD,
605                scenario, md->tokens[1], add, list, med->handle, add );
606       
607        return 1;
608}
609
610static int msn_soap_memlist_edit_handle_response( struct msn_soap_req_data *soap_req )
611{
612        return MSN_SOAP_OK;
613}
614
615static int msn_soap_memlist_edit_free_data( struct msn_soap_req_data *soap_req )
616{
617        struct msn_soap_memlist_edit_data *med = soap_req->data;
618       
619        g_free( med->handle );
620        g_free( med );
621       
622        return 0;
623}
624
625int msn_soap_memlist_edit( struct im_connection *ic, const char *handle, gboolean add, int list )
626{
627        struct msn_soap_memlist_edit_data *med;
628       
629        med = g_new0( struct msn_soap_memlist_edit_data, 1 );
630        med->handle = g_strdup( handle );
631        med->add = add;
632        med->list = list;
633       
634        return msn_soap_start( ic, med, msn_soap_memlist_edit_build_request,
635                                        NULL,
636                                        msn_soap_memlist_edit_handle_response,
637                                        msn_soap_memlist_edit_free_data );
638}
639
640
641/* addressbook: Fetching the membership list (NOT address book) */
642
643static int msn_soap_addressbook_build_request( struct msn_soap_req_data *soap_req )
644{
645        struct msn_data *md = soap_req->ic->proto_data;
646       
647        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
648        soap_req->action = g_strdup( SOAP_ADDRESSBOOK_ACTION );
649        soap_req->payload = msn_soap_abservice_build( SOAP_ADDRESSBOOK_PAYLOAD, "Initial", md->tokens[1] );
650       
651        return 1;
652}
653
654static xt_status msn_soap_addressbook_group( struct xt_node *node, gpointer data )
655{
656        struct xt_node *p;
657        char *id = NULL, *name = NULL;
658        struct msn_soap_req_data *soap_req = data;
659        struct msn_data *md = soap_req->ic->proto_data;
660       
661        if( ( p = xt_find_path( node, "../groupId" ) ) )
662                id = p->text;
663       
664        if( ( p = xt_find_node( node->children, "name" ) ) )
665                name = p->text;
666       
667        if( id && name )
668        {
669                struct msn_group *mg = g_new0( struct msn_group, 1 );
670                mg->id = g_strdup( id );
671                mg->name = g_strdup( name );
672                md->groups = g_slist_prepend( md->groups, mg );
673        }
674       
675        printf( "%s %s\n", id, name );
676       
677        return XT_HANDLED;
678}
679
680static xt_status msn_soap_addressbook_contact( struct xt_node *node, gpointer data )
681{
682        bee_user_t *bu;
683        struct msn_buddy_data *bd;
684        struct xt_node *p;
685        char *id = NULL, *type = NULL, *handle = NULL,
686             *display_name = NULL, *group_id = NULL;
687        struct msn_soap_req_data *soap_req = data;
688        struct im_connection *ic = soap_req->ic;
689        struct msn_group *group;
690       
691        if( ( p = xt_find_path( node, "../contactId" ) ) )
692                id = p->text;
693        if( ( p = xt_find_node( node->children, "contactType" ) ) )
694                type = p->text;
695        if( ( p = xt_find_node( node->children, "passportName" ) ) )
696                handle = p->text;
697        if( ( p = xt_find_node( node->children, "displayName" ) ) )
698                display_name = p->text;
699        if( ( p = xt_find_path( node, "groupIds/guid" ) ) )
700                group_id = p->text;
701       
702        if( type && g_strcasecmp( type, "me" ) == 0 )
703        {
704                set_t *set = set_find( &ic->acc->set, "display_name" );
705                g_free( set->value );
706                set->value = g_strdup( display_name );
707               
708                return XT_HANDLED;
709        }
710       
711        if( handle == NULL )
712                return XT_HANDLED;
713       
714        if( !( bu = bee_user_by_handle( ic->bee, ic, handle ) ) &&
715            !( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) )
716                return XT_HANDLED;
717       
718        bd = bu->data;
719        bd->flags |= MSN_BUDDY_FL;
720        g_free( bd->cid );
721        bd->cid = g_strdup( id );
722       
723        imcb_rename_buddy( ic, handle, display_name );
724       
725        if( group_id && ( group = msn_group_by_id( ic, group_id ) ) )
726                imcb_add_buddy( ic, handle, group->name );
727       
728        printf( "%s %s %s %s\n", id, type, handle, display_name );
729       
730        return XT_HANDLED;
731}
732
733static const struct xt_handler_entry msn_soap_addressbook_parser[] = {
734        { "contactInfo", "Contact", msn_soap_addressbook_contact },
735        { "groupInfo", "Group", msn_soap_addressbook_group },
736        { NULL,               NULL,     NULL                        }
737};
738
739static int msn_soap_addressbook_handle_response( struct msn_soap_req_data *soap_req )
740{
741        msn_auth_got_contact_list( soap_req->ic );
742        return MSN_SOAP_OK;
743}
744
745static int msn_soap_addressbook_free_data( struct msn_soap_req_data *soap_req )
746{
747        return 0;
748}
749
750int msn_soap_addressbook_request( struct im_connection *ic )
751{
752        return msn_soap_start( ic, NULL, msn_soap_addressbook_build_request,
753                                         msn_soap_addressbook_parser,
754                                         msn_soap_addressbook_handle_response,
755                                         msn_soap_addressbook_free_data );
756}
757
758/* Variant: Change our display name. */
759static int msn_soap_ab_namechange_build_request( struct msn_soap_req_data *soap_req )
760{
761        struct msn_data *md = soap_req->ic->proto_data;
762       
763        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
764        soap_req->action = g_strdup( SOAP_AB_NAMECHANGE_ACTION );
765        soap_req->payload = msn_soap_abservice_build( SOAP_AB_NAMECHANGE_PAYLOAD,
766                "Initial", md->tokens[1], (char *) soap_req->data );
767       
768        return 1;
769}
770
771static int msn_soap_ab_namechange_handle_response( struct msn_soap_req_data *soap_req )
772{
773        /* TODO: Ack the change? Not sure what the NAKs look like.. */
774        return MSN_SOAP_OK;
775}
776
777static int msn_soap_ab_namechange_free_data( struct msn_soap_req_data *soap_req )
778{
779        g_free( soap_req->data );
780        return 0;
781}
782
783int msn_soap_addressbook_set_display_name( struct im_connection *ic, const char *new )
784{
785        return msn_soap_start( ic, g_strdup( new ),
786                               msn_soap_ab_namechange_build_request,
787                               NULL,
788                               msn_soap_ab_namechange_handle_response,
789                               msn_soap_ab_namechange_free_data );
790}
791
792/* Add a contact. */
793static int msn_soap_ab_contact_add_build_request( struct msn_soap_req_data *soap_req )
794{
795        struct msn_data *md = soap_req->ic->proto_data;
796        bee_user_t *bu = soap_req->data;
797       
798        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
799        soap_req->action = g_strdup( SOAP_AB_CONTACT_ADD_ACTION );
800        soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_ADD_PAYLOAD,
801                "ContactSave", md->tokens[1], bu->handle, bu->fullname ? bu->fullname : bu->handle );
802       
803        return 1;
804}
805
806static xt_status msn_soap_ab_contact_add_cid( struct xt_node *node, gpointer data )
807{
808        struct msn_soap_req_data *soap_req = data;
809        bee_user_t *bu = soap_req->data;
810        struct msn_buddy_data *bd = bu->data;
811       
812        g_free( bd->cid );
813        bd->cid = g_strdup( node->text );
814       
815        return XT_HANDLED;
816}
817
818static const struct xt_handler_entry msn_soap_ab_contact_add_parser[] = {
819        { "guid", "ABContactAddResult", msn_soap_ab_contact_add_cid },
820        { NULL,               NULL,     NULL                        }
821};
822
823static int msn_soap_ab_contact_add_handle_response( struct msn_soap_req_data *soap_req )
824{
825        /* TODO: Ack the change? Not sure what the NAKs look like.. */
826        return MSN_SOAP_OK;
827}
828
829static int msn_soap_ab_contact_add_free_data( struct msn_soap_req_data *soap_req )
830{
831        return 0;
832}
833
834int msn_soap_ab_contact_add( struct im_connection *ic, bee_user_t *bu )
835{
836        return msn_soap_start( ic, bu,
837                               msn_soap_ab_contact_add_build_request,
838                               msn_soap_ab_contact_add_parser,
839                               msn_soap_ab_contact_add_handle_response,
840                               msn_soap_ab_contact_add_free_data );
841}
842
843/* Remove a contact. */
844static int msn_soap_ab_contact_del_build_request( struct msn_soap_req_data *soap_req )
845{
846        struct msn_data *md = soap_req->ic->proto_data;
847        bee_user_t *bu = soap_req->data;
848        struct msn_buddy_data *bd = bu->data;
849       
850        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
851        soap_req->action = g_strdup( SOAP_AB_CONTACT_DEL_ACTION );
852        soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_DEL_PAYLOAD,
853                "Timer", md->tokens[1], bd->cid );
854       
855        return 1;
856}
857
858static int msn_soap_ab_contact_del_handle_response( struct msn_soap_req_data *soap_req )
859{
860        /* TODO: Ack the change? Not sure what the NAKs look like.. */
861        return MSN_SOAP_OK;
862}
863
864static int msn_soap_ab_contact_del_free_data( struct msn_soap_req_data *soap_req )
865{
866        return 0;
867}
868
869int msn_soap_ab_contact_del( struct im_connection *ic, bee_user_t *bu )
870{
871        return msn_soap_start( ic, bu,
872                               msn_soap_ab_contact_del_build_request,
873                               NULL,
874                               msn_soap_ab_contact_del_handle_response,
875                               msn_soap_ab_contact_del_free_data );
876}
Note: See TracBrowser for help on using the repository browser.