source: protocols/msn/soap.c @ 80175a1

Last change on this file since 80175a1 was 80175a1, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-18T19:21:44Z

Fetch the user's profile to see if there's a display name set there. If
there is, the one in the address book should be ignored. No support for
changing the profile yet though.

  • Property mode set to 100644
File size: 26.4 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 < sizeof( md->tokens ) / sizeof( md->tokens[0] ) )
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, *is_msgr = "false",
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_node( node->children, "isMessengerUser" ) ) )
700                is_msgr = p->text;
701        if( ( p = xt_find_path( node, "groupIds/guid" ) ) )
702                group_id = p->text;
703       
704        if( type && g_strcasecmp( type, "me" ) == 0 )
705        {
706                set_t *set = set_find( &ic->acc->set, "display_name" );
707                g_free( set->value );
708                set->value = g_strdup( display_name );
709               
710                /* Try to fetch the profile; if the user has one, that's where
711                   we can find the persistent display_name. */
712                if( ( p = xt_find_node( node->children, "CID" ) ) && p->text )
713                        msn_soap_profile_get( ic, p->text );
714               
715                return XT_HANDLED;
716        }
717       
718        if( !bool2int( is_msgr ) || handle == NULL )
719                return XT_HANDLED;
720       
721        if( !( bu = bee_user_by_handle( ic->bee, ic, handle ) ) &&
722            !( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) )
723                return XT_HANDLED;
724       
725        bd = bu->data;
726        bd->flags |= MSN_BUDDY_FL;
727        g_free( bd->cid );
728        bd->cid = g_strdup( id );
729       
730        imcb_rename_buddy( ic, handle, display_name );
731       
732        if( group_id && ( group = msn_group_by_id( ic, group_id ) ) )
733                imcb_add_buddy( ic, handle, group->name );
734       
735        printf( "%s %s %s %s\n", id, type, handle, display_name );
736       
737        return XT_HANDLED;
738}
739
740static const struct xt_handler_entry msn_soap_addressbook_parser[] = {
741        { "contactInfo", "Contact", msn_soap_addressbook_contact },
742        { "groupInfo", "Group", msn_soap_addressbook_group },
743        { NULL,               NULL,     NULL                        }
744};
745
746static int msn_soap_addressbook_handle_response( struct msn_soap_req_data *soap_req )
747{
748        msn_auth_got_contact_list( soap_req->ic );
749        return MSN_SOAP_OK;
750}
751
752static int msn_soap_addressbook_free_data( struct msn_soap_req_data *soap_req )
753{
754        return 0;
755}
756
757int msn_soap_addressbook_request( struct im_connection *ic )
758{
759        return msn_soap_start( ic, NULL, msn_soap_addressbook_build_request,
760                                         msn_soap_addressbook_parser,
761                                         msn_soap_addressbook_handle_response,
762                                         msn_soap_addressbook_free_data );
763}
764
765/* Variant: Change our display name. */
766static int msn_soap_ab_namechange_build_request( struct msn_soap_req_data *soap_req )
767{
768        struct msn_data *md = soap_req->ic->proto_data;
769       
770        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
771        soap_req->action = g_strdup( SOAP_AB_NAMECHANGE_ACTION );
772        soap_req->payload = msn_soap_abservice_build( SOAP_AB_NAMECHANGE_PAYLOAD,
773                "Timer", md->tokens[1], (char *) soap_req->data );
774       
775        return 1;
776}
777
778static int msn_soap_ab_namechange_handle_response( struct msn_soap_req_data *soap_req )
779{
780        /* TODO: Ack the change? Not sure what the NAKs look like.. */
781        return MSN_SOAP_OK;
782}
783
784static int msn_soap_ab_namechange_free_data( struct msn_soap_req_data *soap_req )
785{
786        g_free( soap_req->data );
787        return 0;
788}
789
790int msn_soap_addressbook_set_display_name( struct im_connection *ic, const char *new )
791{
792        return msn_soap_start( ic, g_strdup( new ),
793                               msn_soap_ab_namechange_build_request,
794                               NULL,
795                               msn_soap_ab_namechange_handle_response,
796                               msn_soap_ab_namechange_free_data );
797}
798
799/* Add a contact. */
800static int msn_soap_ab_contact_add_build_request( struct msn_soap_req_data *soap_req )
801{
802        struct msn_data *md = soap_req->ic->proto_data;
803        bee_user_t *bu = soap_req->data;
804       
805        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
806        soap_req->action = g_strdup( SOAP_AB_CONTACT_ADD_ACTION );
807        soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_ADD_PAYLOAD,
808                "ContactSave", md->tokens[1], bu->handle, bu->fullname ? bu->fullname : bu->handle );
809       
810        return 1;
811}
812
813static xt_status msn_soap_ab_contact_add_cid( struct xt_node *node, gpointer data )
814{
815        struct msn_soap_req_data *soap_req = data;
816        bee_user_t *bu = soap_req->data;
817        struct msn_buddy_data *bd = bu->data;
818       
819        g_free( bd->cid );
820        bd->cid = g_strdup( node->text );
821       
822        return XT_HANDLED;
823}
824
825static const struct xt_handler_entry msn_soap_ab_contact_add_parser[] = {
826        { "guid", "ABContactAddResult", msn_soap_ab_contact_add_cid },
827        { NULL,               NULL,     NULL                        }
828};
829
830static int msn_soap_ab_contact_add_handle_response( struct msn_soap_req_data *soap_req )
831{
832        /* TODO: Ack the change? Not sure what the NAKs look like.. */
833        return MSN_SOAP_OK;
834}
835
836static int msn_soap_ab_contact_add_free_data( struct msn_soap_req_data *soap_req )
837{
838        return 0;
839}
840
841int msn_soap_ab_contact_add( struct im_connection *ic, bee_user_t *bu )
842{
843        return msn_soap_start( ic, bu,
844                               msn_soap_ab_contact_add_build_request,
845                               msn_soap_ab_contact_add_parser,
846                               msn_soap_ab_contact_add_handle_response,
847                               msn_soap_ab_contact_add_free_data );
848}
849
850/* Remove a contact. */
851static int msn_soap_ab_contact_del_build_request( struct msn_soap_req_data *soap_req )
852{
853        struct msn_data *md = soap_req->ic->proto_data;
854        bee_user_t *bu = soap_req->data;
855        struct msn_buddy_data *bd = bu->data;
856       
857        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
858        soap_req->action = g_strdup( SOAP_AB_CONTACT_DEL_ACTION );
859        soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_DEL_PAYLOAD,
860                "Timer", md->tokens[1], bd->cid );
861       
862        return 1;
863}
864
865static int msn_soap_ab_contact_del_handle_response( struct msn_soap_req_data *soap_req )
866{
867        /* TODO: Ack the change? Not sure what the NAKs look like.. */
868        return MSN_SOAP_OK;
869}
870
871static int msn_soap_ab_contact_del_free_data( struct msn_soap_req_data *soap_req )
872{
873        return 0;
874}
875
876int msn_soap_ab_contact_del( struct im_connection *ic, bee_user_t *bu )
877{
878        return msn_soap_start( ic, bu,
879                               msn_soap_ab_contact_del_build_request,
880                               NULL,
881                               msn_soap_ab_contact_del_handle_response,
882                               msn_soap_ab_contact_del_free_data );
883}
884
885
886
887/* Storage stuff: Fetch profile. */
888static int msn_soap_profile_get_build_request( struct msn_soap_req_data *soap_req )
889{
890        struct msn_data *md = soap_req->ic->proto_data;
891       
892        soap_req->url = g_strdup( SOAP_STORAGE_URL );
893        soap_req->action = g_strdup( SOAP_PROFILE_GET_ACTION );
894        soap_req->payload = g_markup_printf_escaped( SOAP_PROFILE_GET_PAYLOAD,
895                md->tokens[3], (char*) soap_req->data );
896       
897        return 1;
898}
899
900static xt_status msn_soap_profile_get_result( struct xt_node *node, gpointer data )
901{
902        struct msn_soap_req_data *soap_req = data;
903        struct im_connection *ic = soap_req->ic;
904        struct msn_data *md = soap_req->ic->proto_data;
905        struct xt_node *dn;
906       
907        if( ( dn = xt_find_node( node->children, "DisplayName" ) ) && dn->text )
908        {
909                set_t *set = set_find( &ic->acc->set, "display_name" );
910                g_free( set->value );
911                set->value = g_strdup( dn->text );
912               
913                md->flags |= MSN_GOT_PROFILE_DN;
914        }
915       
916        return XT_HANDLED;
917}
918
919static const struct xt_handler_entry msn_soap_profile_get_parser[] = {
920        { "ExpressionProfile", "GetProfileResult", msn_soap_profile_get_result },
921        { NULL,               NULL,     NULL                        }
922};
923
924static int msn_soap_profile_get_handle_response( struct msn_soap_req_data *soap_req )
925{
926        struct msn_data *md = soap_req->ic->proto_data;
927       
928        md->flags |= MSN_GOT_PROFILE;
929        msn_ns_finish_login( soap_req->ic );
930       
931        return MSN_SOAP_OK;
932}
933
934static int msn_soap_profile_get_free_data( struct msn_soap_req_data *soap_req )
935{
936        g_free( soap_req->data );
937        return 0;
938}
939
940int msn_soap_profile_get( struct im_connection *ic, const char *cid )
941{
942        return msn_soap_start( ic, g_strdup( cid ),
943                               msn_soap_profile_get_build_request,
944                               msn_soap_profile_get_parser,
945                               msn_soap_profile_get_handle_response,
946                               msn_soap_profile_get_free_data );
947}
Note: See TracBrowser for help on using the repository browser.