source: protocols/msn/soap.c @ ff27648

Last change on this file since ff27648 was ff27648, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-15T00:05:49Z

Read group info.

  • Property mode set to 100644
File size: 23.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};
176
177static int msn_soap_passport_sso_build_request( struct msn_soap_req_data *soap_req )
178{
179        struct msn_soap_passport_sso_data *sd = soap_req->data;
180        struct im_connection *ic = soap_req->ic;
181       
182        if( g_str_has_suffix( ic->acc->user, "@msn.com" ) )
183                soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL_MSN );
184        else
185                soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL );
186       
187        soap_req->payload = g_markup_printf_escaped( SOAP_PASSPORT_SSO_PAYLOAD,
188                ic->acc->user, ic->acc->pass, sd->policy );
189       
190        return MSN_SOAP_OK;
191}
192
193static xt_status msn_soap_passport_sso_token( struct xt_node *node, gpointer data )
194{
195        struct msn_soap_req_data *soap_req = data;
196        struct msn_soap_passport_sso_data *sd = soap_req->data;
197        struct msn_data *md = soap_req->ic->proto_data;
198        struct xt_node *p;
199        char *id;
200       
201        if( ( id = xt_find_attr( node, "Id" ) ) == NULL )
202                return XT_HANDLED;
203        id += strlen( id ) - 1;
204        if( *id == '1' &&
205            ( p = xt_find_path( node, "../../wst:RequestedProofToken/wst:BinarySecret" ) ) &&
206            p->text )
207                sd->secret = g_strdup( p->text );
208       
209        *id -= '1';
210        if( *id >= 0 && *id <= 2 )
211        {
212                g_free( md->tokens[(int)*id] );
213                md->tokens[(int)*id] = g_strdup( node->text );
214        }
215       
216        return XT_HANDLED;
217}
218
219static const struct xt_handler_entry msn_soap_passport_sso_parser[] = {
220        { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token },
221        { NULL, NULL, NULL }
222};
223
224static char *msn_key_fuckery( char *key, int key_len, char *type )
225{
226        unsigned char hash1[20+strlen(type)+1];
227        unsigned char hash2[20];
228        char *ret;
229       
230        sha1_hmac( key, key_len, type, 0, hash1 );
231        strcpy( (char*) hash1 + 20, type );
232        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash2 );
233       
234        /* This is okay as hash1 is read completely before it's overwritten. */
235        sha1_hmac( key, key_len, (char*) hash1, 20, hash1 );
236        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash1 );
237       
238        ret = g_malloc( 24 );
239        memcpy( ret, hash2, 20 );
240        memcpy( ret + 20, hash1, 4 );
241        return ret;
242}
243
244static int msn_soap_passport_sso_handle_response( struct msn_soap_req_data *soap_req )
245{
246        struct msn_soap_passport_sso_data *sd = soap_req->data;
247        struct im_connection *ic = soap_req->ic;
248        char *key1, *key2, *key3, *blurb64;
249        int key1_len;
250        unsigned char *padnonce, *des3res;
251        struct
252        {
253                unsigned int uStructHeaderSize; // 28. Does not count data
254                unsigned int uCryptMode; // CRYPT_MODE_CBC (1)
255                unsigned int uCipherType; // TripleDES (0x6603)
256                unsigned int uHashType; // SHA1 (0x8004)
257                unsigned int uIVLen;    // 8
258                unsigned int uHashLen;  // 20
259                unsigned int uCipherLen; // 72
260                unsigned char iv[8];
261                unsigned char hash[20];
262                unsigned char cipherbytes[72];
263        } blurb = {
264                GUINT32_TO_LE( 28 ),
265                GUINT32_TO_LE( 1 ),
266                GUINT32_TO_LE( 0x6603 ),
267                GUINT32_TO_LE( 0x8004 ),
268                GUINT32_TO_LE( 8 ),
269                GUINT32_TO_LE( 20 ),
270                GUINT32_TO_LE( 72 ),
271        };
272
273        key1_len = base64_decode( sd->secret, (unsigned char**) &key1 );
274       
275        key2 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY HASH" );
276        key3 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY ENCRYPTION" );
277       
278        sha1_hmac( key2, 24, sd->nonce, 0, blurb.hash );
279        padnonce = g_malloc( strlen( sd->nonce ) + 8 );
280        strcpy( (char*) padnonce, sd->nonce );
281        memset( padnonce + strlen( sd->nonce ), 8, 8 );
282       
283        random_bytes( blurb.iv, 8 );
284       
285        ssl_des3_encrypt( (unsigned char*) key3, 24, padnonce, strlen( sd->nonce ) + 8, blurb.iv, &des3res );
286        memcpy( blurb.cipherbytes, des3res, 72 );
287       
288        blurb64 = base64_encode( (unsigned char*) &blurb, sizeof( blurb ) );
289        msn_auth_got_passport_token( ic, blurb64 );
290       
291        g_free( padnonce );
292        g_free( blurb64 );
293        g_free( des3res );
294        g_free( key1 );
295        g_free( key2 );
296        g_free( key3 );
297       
298        return MSN_SOAP_OK;
299}
300
301static int msn_soap_passport_sso_free_data( struct msn_soap_req_data *soap_req )
302{
303        struct msn_soap_passport_sso_data *sd = soap_req->data;
304       
305        g_free( sd->policy );
306        g_free( sd->nonce );
307        g_free( sd->secret );
308       
309        return MSN_SOAP_OK;
310}
311
312int msn_soap_passport_sso_request( struct im_connection *ic, const char *policy, const char *nonce )
313{
314        struct msn_soap_passport_sso_data *sd = g_new0( struct msn_soap_passport_sso_data, 1 );
315       
316        sd->policy = g_strdup( policy );
317        sd->nonce = g_strdup( nonce );
318       
319        return msn_soap_start( ic, sd, msn_soap_passport_sso_build_request,
320                                       msn_soap_passport_sso_parser,
321                                       msn_soap_passport_sso_handle_response,
322                                       msn_soap_passport_sso_free_data );
323}
324
325
326/* oim_send: Sending offline messages */
327
328struct msn_soap_oim_send_data
329{
330        char *to;
331        char *msg;
332        int number;
333        int need_retry;
334};
335
336static int msn_soap_oim_build_request( struct msn_soap_req_data *soap_req )
337{
338        struct msn_soap_oim_send_data *oim = soap_req->data;
339        struct im_connection *ic = soap_req->ic;
340        struct msn_data *md = ic->proto_data;
341        char *display_name_b64;
342       
343        display_name_b64 = tobase64( set_getstr( &ic->acc->set, "display_name" ) );
344       
345        soap_req->url = g_strdup( SOAP_OIM_SEND_URL );
346        soap_req->action = g_strdup( SOAP_OIM_SEND_ACTION );
347        soap_req->payload = g_markup_printf_escaped( SOAP_OIM_SEND_PAYLOAD,
348                ic->acc->user, display_name_b64, MSNP_VER, MSNP_BUILD,
349                oim->to, md->tokens[2],
350                MSNP11_PROD_ID, md->lock_key ? md->lock_key : "",
351                oim->number, oim->number, oim->msg );
352       
353        g_free( display_name_b64 );
354       
355        return MSN_SOAP_OK;
356}
357
358static xt_status msn_soap_oim_send_challenge( struct xt_node *node, gpointer data )
359{
360        struct msn_soap_req_data *soap_req = data;
361        struct msn_soap_oim_send_data *oim = soap_req->data;
362        struct im_connection *ic = soap_req->ic;
363        struct msn_data *md = ic->proto_data;
364       
365        g_free( md->lock_key );
366        md->lock_key = msn_p11_challenge( node->text );
367       
368        oim->need_retry = 1;
369       
370        return XT_HANDLED;
371}
372
373static const struct xt_handler_entry msn_soap_oim_send_parser[] = {
374        { "LockKeyChallenge", "detail", msn_soap_oim_send_challenge },
375        { NULL,               NULL,     NULL                        }
376};
377
378static int msn_soap_oim_handle_response( struct msn_soap_req_data *soap_req )
379{
380        struct msn_soap_oim_send_data *oim = soap_req->data;
381       
382        if( soap_req->http_req->status_code == 500 && oim->need_retry && soap_req->ttl > 0 )
383        {
384                oim->need_retry = 0;
385                return MSN_SOAP_RETRY;
386        }
387        else if( soap_req->http_req->status_code == 200 )
388        {
389                imcb_log( soap_req->ic, "Offline message successfully delivered to %s", oim->to );
390                return MSN_SOAP_OK;
391        }
392        else
393        {
394                imcb_log( soap_req->ic, "Failed to deliver offline message to %s:\n%s", oim->to, oim->msg );
395                return MSN_SOAP_ABORT;
396        }
397}
398
399static int msn_soap_oim_free_data( struct msn_soap_req_data *soap_req )
400{
401        struct msn_soap_oim_send_data *oim = soap_req->data;
402       
403        g_free( oim->to );
404        g_free( oim->msg );
405        g_free( oim );
406       
407        return MSN_SOAP_OK;
408}
409
410int msn_soap_oim_send( struct im_connection *ic, const char *to, const char *msg )
411{
412        struct msn_soap_oim_send_data *data;
413       
414        data = g_new0( struct msn_soap_oim_send_data, 1 );
415        data->to = g_strdup( to );
416        data->msg = tobase64( msg );
417        data->number = 1;
418       
419        return msn_soap_start( ic, data, msn_soap_oim_build_request,
420                                         msn_soap_oim_send_parser,
421                                         msn_soap_oim_handle_response,
422                                         msn_soap_oim_free_data );
423}
424
425int msn_soap_oim_send_queue( struct im_connection *ic, GSList **msgq )
426{
427        GSList *l;
428        char *n = NULL;
429       
430        for( l = *msgq; l; l = l->next )
431        {
432                struct msn_message *m = l->data;
433               
434                if( n == NULL )
435                        n = m->who;
436                if( strcmp( n, m->who ) == 0 )
437                        msn_soap_oim_send( ic, m->who, m->text );
438        }
439       
440        while( *msgq != NULL )
441        {
442                struct msn_message *m = (*msgq)->data;
443               
444                g_free( m->who );
445                g_free( m->text );
446                g_free( m );
447               
448                *msgq = g_slist_remove( *msgq, m );
449        }
450       
451        return 1;
452}
453
454
455/* memlist: Fetching the membership list (NOT address book) */
456
457static int msn_soap_memlist_build_request( struct msn_soap_req_data *soap_req )
458{
459        struct msn_data *md = soap_req->ic->proto_data;
460       
461        soap_req->url = g_strdup( SOAP_MEMLIST_URL );
462        soap_req->action = g_strdup( SOAP_MEMLIST_ACTION );
463        soap_req->payload = msn_soap_abservice_build( SOAP_MEMLIST_PAYLOAD, "Initial", md->tokens[1] );
464       
465        return 1;
466}
467
468static xt_status msn_soap_memlist_member( struct xt_node *node, gpointer data )
469{
470        bee_user_t *bu;
471        struct msn_buddy_data *bd;
472        struct xt_node *p;
473        char *role = NULL, *handle = NULL;
474        struct msn_soap_req_data *soap_req = data;
475        struct im_connection *ic = soap_req->ic;
476       
477        if( ( p = xt_find_path( node, "../../MemberRole" ) ) )
478                role = p->text;
479       
480        if( ( p = xt_find_node( node->children, "PassportName" ) ) )
481                handle = p->text;
482       
483        if( !role || !handle || 
484            !( ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) ||
485               ( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) )
486                return XT_HANDLED;
487       
488        bd = bu->data;
489        if( strcmp( role, "Allow" ) == 0 )
490                bd->flags |= MSN_BUDDY_AL;
491        else if( strcmp( role, "Block" ) == 0 )
492                bd->flags |= MSN_BUDDY_BL;
493        else if( strcmp( role, "Reverse" ) == 0 )
494        {
495                bd->flags |= MSN_BUDDY_RL;
496                msn_buddy_ask( bu );
497        }
498        else if( strcmp( role, "Pending" ) == 0 )
499        {
500                bd->flags |= MSN_BUDDY_PL;
501                msn_buddy_ask( bu );
502        }
503       
504        printf( "%s %d\n", handle, bd->flags );
505       
506        return XT_HANDLED;
507}
508
509static const struct xt_handler_entry msn_soap_memlist_parser[] = {
510        { "Member", "Members", msn_soap_memlist_member },
511        { NULL,               NULL,     NULL                        }
512};
513
514static int msn_soap_memlist_handle_response( struct msn_soap_req_data *soap_req )
515{
516        msn_soap_addressbook_request( soap_req->ic );
517       
518        return MSN_SOAP_OK;
519}
520
521static int msn_soap_memlist_free_data( struct msn_soap_req_data *soap_req )
522{
523        return 0;
524}
525
526int msn_soap_memlist_request( struct im_connection *ic )
527{
528        return msn_soap_start( ic, NULL, msn_soap_memlist_build_request,
529                                         msn_soap_memlist_parser,
530                                         msn_soap_memlist_handle_response,
531                                         msn_soap_memlist_free_data );
532}
533
534/* Variant: Adding/Removing people */
535struct msn_soap_memlist_edit_data
536{
537        char *handle;
538        gboolean add;
539        msn_buddy_flags_t list;
540};
541
542static int msn_soap_memlist_edit_build_request( struct msn_soap_req_data *soap_req )
543{
544        struct msn_data *md = soap_req->ic->proto_data;
545        struct msn_soap_memlist_edit_data *med = soap_req->data;
546        char *add, *scenario, *list;
547       
548        soap_req->url = g_strdup( SOAP_MEMLIST_URL );
549        if( med->add )
550        {
551                soap_req->action = g_strdup( SOAP_MEMLIST_ADD_ACTION );
552                add = "Add";
553        }
554        else
555        {
556                soap_req->action = g_strdup( SOAP_MEMLIST_DEL_ACTION );
557                add = "Delete";
558        }
559        switch( med->list )
560        {
561        case MSN_BUDDY_AL:
562                scenario = "BlockUnblock";
563                list = "Allow";
564                break;
565        case MSN_BUDDY_BL:
566                scenario = "BlockUnblock";
567                list = "Block";
568                break;
569        case MSN_BUDDY_RL:
570                scenario = "Timer";
571                list = "Reverse";
572                break;
573        case MSN_BUDDY_PL:
574        default:
575                scenario = "Timer";
576                list = "Pending";
577                break;
578        }
579        soap_req->payload = msn_soap_abservice_build( SOAP_MEMLIST_EDIT_PAYLOAD,
580                scenario, md->tokens[1], add, list, med->handle, add );
581       
582        return 1;
583}
584
585static int msn_soap_memlist_edit_handle_response( struct msn_soap_req_data *soap_req )
586{
587        return MSN_SOAP_OK;
588}
589
590static int msn_soap_memlist_edit_free_data( struct msn_soap_req_data *soap_req )
591{
592        struct msn_soap_memlist_edit_data *med = soap_req->data;
593       
594        g_free( med->handle );
595        g_free( med );
596       
597        return 0;
598}
599
600int msn_soap_memlist_edit( struct im_connection *ic, const char *handle, gboolean add, int list )
601{
602        struct msn_soap_memlist_edit_data *med;
603       
604        med = g_new0( struct msn_soap_memlist_edit_data, 1 );
605        med->handle = g_strdup( handle );
606        med->add = add;
607        med->list = list;
608       
609        return msn_soap_start( ic, med, msn_soap_memlist_edit_build_request,
610                                        NULL,
611                                        msn_soap_memlist_edit_handle_response,
612                                        msn_soap_memlist_edit_free_data );
613}
614
615
616/* addressbook: Fetching the membership list (NOT address book) */
617
618static int msn_soap_addressbook_build_request( struct msn_soap_req_data *soap_req )
619{
620        struct msn_data *md = soap_req->ic->proto_data;
621       
622        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
623        soap_req->action = g_strdup( SOAP_ADDRESSBOOK_ACTION );
624        soap_req->payload = msn_soap_abservice_build( SOAP_ADDRESSBOOK_PAYLOAD, "Initial", md->tokens[1] );
625       
626        return 1;
627}
628
629static xt_status msn_soap_addressbook_group( struct xt_node *node, gpointer data )
630{
631        struct xt_node *p;
632        char *id = NULL, *name = NULL;
633        struct msn_soap_req_data *soap_req = data;
634        struct msn_data *md = soap_req->ic->proto_data;
635       
636        if( ( p = xt_find_path( node, "../groupId" ) ) )
637                id = p->text;
638       
639        if( ( p = xt_find_node( node->children, "name" ) ) )
640                name = p->text;
641       
642        if( id && name )
643        {
644                struct msn_group *mg = g_new0( struct msn_group, 1 );
645                mg->id = g_strdup( id );
646                mg->name = g_strdup( name );
647                md->groups = g_slist_prepend( md->groups, mg );
648        }
649       
650        printf( "%s %s\n", id, name );
651       
652        return XT_HANDLED;
653}
654
655static xt_status msn_soap_addressbook_contact( struct xt_node *node, gpointer data )
656{
657        bee_user_t *bu;
658        struct msn_buddy_data *bd;
659        struct xt_node *p;
660        char *id = NULL, *type = NULL, *handle = NULL,
661             *display_name = NULL, *group_id = NULL;
662        struct msn_soap_req_data *soap_req = data;
663        struct im_connection *ic = soap_req->ic;
664        struct msn_group *group;
665       
666        if( ( p = xt_find_path( node, "../contactId" ) ) )
667                id = p->text;
668        if( ( p = xt_find_node( node->children, "contactType" ) ) )
669                type = p->text;
670        if( ( p = xt_find_node( node->children, "passportName" ) ) )
671                handle = p->text;
672        if( ( p = xt_find_node( node->children, "displayName" ) ) )
673                display_name = p->text;
674        if( ( p = xt_find_path( node, "groupIds/guid" ) ) )
675                group_id = p->text;
676       
677        if( type && g_strcasecmp( type, "me" ) == 0 )
678        {
679                set_t *set = set_find( &ic->acc->set, "display_name" );
680                g_free( set->value );
681                set->value = g_strdup( display_name );
682               
683                return XT_HANDLED;
684        }
685       
686        if( handle == NULL )
687                return XT_HANDLED;
688       
689        if( !( bu = bee_user_by_handle( ic->bee, ic, handle ) ) &&
690            !( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) )
691                return XT_HANDLED;
692       
693        bd = bu->data;
694        bd->flags |= MSN_BUDDY_FL;
695        g_free( bd->cid );
696        bd->cid = g_strdup( id );
697       
698        imcb_rename_buddy( ic, handle, display_name );
699       
700        if( group_id && ( group = msn_group_by_id( ic, group_id ) ) )
701                imcb_add_buddy( ic, handle, group->name );
702       
703        printf( "%s %s %s %s\n", id, type, handle, display_name );
704       
705        return XT_HANDLED;
706}
707
708static const struct xt_handler_entry msn_soap_addressbook_parser[] = {
709        { "contactInfo", "Contact", msn_soap_addressbook_contact },
710        { "groupInfo", "Group", msn_soap_addressbook_group },
711        { NULL,               NULL,     NULL                        }
712};
713
714static int msn_soap_addressbook_handle_response( struct msn_soap_req_data *soap_req )
715{
716        msn_auth_got_contact_list( soap_req->ic );
717        return MSN_SOAP_OK;
718}
719
720static int msn_soap_addressbook_free_data( struct msn_soap_req_data *soap_req )
721{
722        return 0;
723}
724
725int msn_soap_addressbook_request( struct im_connection *ic )
726{
727        return msn_soap_start( ic, NULL, msn_soap_addressbook_build_request,
728                                         msn_soap_addressbook_parser,
729                                         msn_soap_addressbook_handle_response,
730                                         msn_soap_addressbook_free_data );
731}
732
733/* Variant: Change our display name. */
734static int msn_soap_ab_namechange_build_request( struct msn_soap_req_data *soap_req )
735{
736        struct msn_data *md = soap_req->ic->proto_data;
737       
738        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
739        soap_req->action = g_strdup( SOAP_AB_NAMECHANGE_ACTION );
740        soap_req->payload = msn_soap_abservice_build( SOAP_AB_NAMECHANGE_PAYLOAD,
741                "Initial", md->tokens[1], (char *) soap_req->data );
742       
743        return 1;
744}
745
746static int msn_soap_ab_namechange_handle_response( struct msn_soap_req_data *soap_req )
747{
748        /* TODO: Ack the change? Not sure what the NAKs look like.. */
749        return MSN_SOAP_OK;
750}
751
752static int msn_soap_ab_namechange_free_data( struct msn_soap_req_data *soap_req )
753{
754        g_free( soap_req->data );
755        return 0;
756}
757
758int msn_soap_addressbook_set_display_name( struct im_connection *ic, const char *new )
759{
760        return msn_soap_start( ic, g_strdup( new ),
761                               msn_soap_ab_namechange_build_request,
762                               NULL,
763                               msn_soap_ab_namechange_handle_response,
764                               msn_soap_ab_namechange_free_data );
765}
766
767/* Add a contact. */
768static int msn_soap_ab_contact_add_build_request( struct msn_soap_req_data *soap_req )
769{
770        struct msn_data *md = soap_req->ic->proto_data;
771        bee_user_t *bu = soap_req->data;
772       
773        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
774        soap_req->action = g_strdup( SOAP_AB_CONTACT_ADD_ACTION );
775        soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_ADD_PAYLOAD,
776                "ContactSave", md->tokens[1], bu->handle, bu->fullname ? bu->fullname : bu->handle );
777       
778        return 1;
779}
780
781static xt_status msn_soap_ab_contact_add_cid( struct xt_node *node, gpointer data )
782{
783        struct msn_soap_req_data *soap_req = data;
784        bee_user_t *bu = soap_req->data;
785        struct msn_buddy_data *bd = bu->data;
786       
787        g_free( bd->cid );
788        bd->cid = g_strdup( node->text );
789       
790        return XT_HANDLED;
791}
792
793static const struct xt_handler_entry msn_soap_ab_contact_add_parser[] = {
794        { "guid", "ABContactAddResult", msn_soap_ab_contact_add_cid },
795        { NULL,               NULL,     NULL                        }
796};
797
798static int msn_soap_ab_contact_add_handle_response( struct msn_soap_req_data *soap_req )
799{
800        /* TODO: Ack the change? Not sure what the NAKs look like.. */
801        return MSN_SOAP_OK;
802}
803
804static int msn_soap_ab_contact_add_free_data( struct msn_soap_req_data *soap_req )
805{
806        return 0;
807}
808
809int msn_soap_ab_contact_add( struct im_connection *ic, bee_user_t *bu )
810{
811        return msn_soap_start( ic, bu,
812                               msn_soap_ab_contact_add_build_request,
813                               msn_soap_ab_contact_add_parser,
814                               msn_soap_ab_contact_add_handle_response,
815                               msn_soap_ab_contact_add_free_data );
816}
817
818/* Remove a contact. */
819static int msn_soap_ab_contact_del_build_request( struct msn_soap_req_data *soap_req )
820{
821        struct msn_data *md = soap_req->ic->proto_data;
822        bee_user_t *bu = soap_req->data;
823        struct msn_buddy_data *bd = bu->data;
824       
825        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
826        soap_req->action = g_strdup( SOAP_AB_CONTACT_DEL_ACTION );
827        soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_DEL_PAYLOAD,
828                "Timer", md->tokens[1], bd->cid );
829       
830        return 1;
831}
832
833static int msn_soap_ab_contact_del_handle_response( struct msn_soap_req_data *soap_req )
834{
835        /* TODO: Ack the change? Not sure what the NAKs look like.. */
836        return MSN_SOAP_OK;
837}
838
839static int msn_soap_ab_contact_del_free_data( struct msn_soap_req_data *soap_req )
840{
841        return 0;
842}
843
844int msn_soap_ab_contact_del( struct im_connection *ic, bee_user_t *bu )
845{
846        return msn_soap_start( ic, bu,
847                               msn_soap_ab_contact_del_build_request,
848                               NULL,
849                               msn_soap_ab_contact_del_handle_response,
850                               msn_soap_ab_contact_del_free_data );
851}
Note: See TracBrowser for help on using the repository browser.