source: protocols/msn/soap.c @ 9bf02f8

Last change on this file since 9bf02f8 was 0e788f5, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-02-21T19:15:59Z

I'm still bored on a long flight. Wrote a script to automatically update
my copyright mentions since some were getting pretty stale. Left files not
touched since before 2012 alone so that this change doesn't touch almost
EVERY source file.

  • Property mode set to 100644
File size: 28.7 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2012 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_REAUTH,
51        MSN_SOAP_ABORT,
52} msn_soap_result_t;
53
54struct msn_soap_req_data;
55typedef int (*msn_soap_func) ( struct msn_soap_req_data * );
56
57struct msn_soap_req_data
58{
59        void *data;
60        struct im_connection *ic;
61        int ttl;
62        char *error;
63       
64        char *url, *action, *payload;
65        struct http_request *http_req;
66       
67        const struct xt_handler_entry *xml_parser;
68        msn_soap_func build_request, handle_response, free_data;
69};
70
71static int msn_soap_send_request( struct msn_soap_req_data *req );
72static void msn_soap_free( struct msn_soap_req_data *soap_req );
73static void msn_soap_debug_print( const char *headers, const char *payload );
74
75static int msn_soap_start( struct im_connection *ic,
76                    void *data,
77                    msn_soap_func build_request,
78                    const struct xt_handler_entry *xml_parser,
79                    msn_soap_func handle_response,
80                    msn_soap_func free_data )
81{
82        struct msn_soap_req_data *req = g_new0( struct msn_soap_req_data, 1 );
83       
84        req->ic = ic;
85        req->data = data;
86        req->xml_parser = xml_parser;
87        req->build_request = build_request;
88        req->handle_response = handle_response;
89        req->free_data = free_data;
90        req->ttl = 3;
91       
92        return msn_soap_send_request( req );
93}
94
95static void msn_soap_handle_response( struct http_request *http_req );
96
97static int msn_soap_send_request( struct msn_soap_req_data *soap_req )
98{
99        char *http_req;
100        char *soap_action = NULL;
101        url_t url;
102       
103        soap_req->build_request( soap_req );
104       
105        if( soap_req->action )
106                soap_action = g_strdup_printf( "SOAPAction: \"%s\"\r\n", soap_req->action );
107       
108        url_set( &url, soap_req->url );
109        http_req = g_strdup_printf( SOAP_HTTP_REQUEST, url.file, url.host,
110                soap_action ? soap_action : "",
111                strlen( soap_req->payload ), soap_req->payload );
112       
113        msn_soap_debug_print( http_req, soap_req->payload );
114       
115        soap_req->http_req = http_dorequest( url.host, url.port, url.proto == PROTO_HTTPS,
116                http_req, msn_soap_handle_response, soap_req );
117       
118        g_free( http_req );
119        g_free( soap_action );
120       
121        return soap_req->http_req != NULL;
122}
123
124static void msn_soap_handle_response( struct http_request *http_req )
125{
126        struct msn_soap_req_data *soap_req = http_req->data;
127        int st;
128       
129        if( g_slist_find( msn_connections, soap_req->ic ) == NULL )
130        {
131                msn_soap_free( soap_req );
132                return;
133        }
134       
135        msn_soap_debug_print( http_req->reply_headers, http_req->reply_body );
136       
137        if( http_req->body_size > 0 )
138        {
139                struct xt_parser *parser;
140                struct xt_node *err;
141               
142                parser = xt_new( soap_req->xml_parser, soap_req );
143                xt_feed( parser, http_req->reply_body, http_req->body_size );
144                if( http_req->status_code == 500 &&
145                    ( err = xt_find_path( parser->root, "soap:Body/soap:Fault/detail/errorcode" ) ) &&
146                    err->text_len > 0 )
147                {
148                        if( strcmp( err->text, "PassportAuthFail" ) == 0 )
149                        {
150                                xt_free( parser );
151                                st = MSN_SOAP_REAUTH;
152                                goto fail;
153                        }
154                        /* TODO: Handle/report other errors. */
155                }
156               
157                xt_handle( parser, NULL, -1 );
158                xt_free( parser );
159        }
160       
161        if( http_req->status_code != 200 )
162                soap_req->error = g_strdup( http_req->status_string );
163       
164        st = soap_req->handle_response( soap_req );
165
166fail:   
167        g_free( soap_req->url );
168        g_free( soap_req->action );
169        g_free( soap_req->payload );
170        g_free( soap_req->error );
171        soap_req->url = soap_req->action = soap_req->payload = soap_req->error = NULL;
172       
173        if( st == MSN_SOAP_RETRY && --soap_req->ttl )
174        {
175                msn_soap_send_request( soap_req );
176        }
177        else if( st == MSN_SOAP_REAUTH )
178        {
179                struct msn_data *md = soap_req->ic->proto_data;
180               
181                if( !( md->flags & MSN_REAUTHING ) )
182                {
183                        /* Nonce shouldn't actually be touched for re-auths. */
184                        msn_soap_passport_sso_request( soap_req->ic, "blaataap" );
185                        md->flags |= MSN_REAUTHING; 
186                }
187                md->soapq = g_slist_append( md->soapq, soap_req );
188        }
189        else
190        {
191                soap_req->free_data( soap_req );
192                g_free( soap_req );
193        }
194}
195
196static char *msn_soap_abservice_build( const char *body_fmt, const char *scenario, const char *ticket, ... )
197{
198        va_list params;
199        char *ret, *format, *body;
200       
201        format = g_markup_printf_escaped( SOAP_ABSERVICE_PAYLOAD, scenario, ticket );
202       
203        va_start( params, ticket );
204        body = g_strdup_vprintf( body_fmt, params );
205        va_end( params );
206       
207        ret = g_strdup_printf( format, body );
208        g_free( body );
209        g_free( format );
210       
211        return ret;
212}
213
214static void msn_soap_debug_print( const char *headers, const char *payload )
215{
216        char *s;
217       
218        if( !getenv( "BITLBEE_DEBUG" ) )
219                return;
220       
221        if( headers )
222        {
223                if( ( s = strstr( headers, "\r\n\r\n" ) ) )
224                        write( 2, headers, s - headers + 4 );
225                else
226                        write( 2, headers, strlen( headers ) );
227        }
228       
229        if( payload )
230        {
231                struct xt_node *xt = xt_from_string( payload, 0 );
232                if( xt )
233                        xt_print( xt );
234                xt_free_node( xt );
235        }
236}
237
238int msn_soapq_flush( struct im_connection *ic, gboolean resend )
239{
240        struct msn_data *md = ic->proto_data;
241       
242        while( md->soapq )
243        {
244                if( resend )
245                        msn_soap_send_request( (struct msn_soap_req_data*) md->soapq->data );
246                else
247                        msn_soap_free( (struct msn_soap_req_data*) md->soapq->data );
248                md->soapq = g_slist_remove( md->soapq, md->soapq->data );
249        }
250       
251        return MSN_SOAP_OK;
252}
253
254static void msn_soap_free( struct msn_soap_req_data *soap_req )
255{
256        soap_req->free_data( soap_req );
257        g_free( soap_req->url );
258        g_free( soap_req->action );
259        g_free( soap_req->payload );
260        g_free( soap_req->error );
261        g_free( soap_req );
262}
263
264
265/* passport_sso: Authentication MSNP15+ */
266
267struct msn_soap_passport_sso_data
268{
269        char *nonce;
270        char *secret;
271        char *error;
272        char *redirect;
273};
274
275static int msn_soap_passport_sso_build_request( struct msn_soap_req_data *soap_req )
276{
277        struct msn_soap_passport_sso_data *sd = soap_req->data;
278        struct im_connection *ic = soap_req->ic;
279        struct msn_data *md = ic->proto_data;
280        char pass[MAX_PASSPORT_PWLEN+1];
281       
282        if( sd->redirect )
283        {
284                soap_req->url = sd->redirect;
285                sd->redirect = NULL;
286        }
287        /* MS changed this URL and broke the old MSN-specific one. The generic
288           one works, forwarding us to a msn.com URL that works. Takes an extra
289           second, but that's better than not being able to log in at all. :-/
290        else if( g_str_has_suffix( ic->acc->user, "@msn.com" ) )
291                soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL_MSN );
292        */
293        else
294                soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL );
295       
296        strncpy( pass, ic->acc->pass, MAX_PASSPORT_PWLEN );
297        pass[MAX_PASSPORT_PWLEN] = '\0';
298        soap_req->payload = g_markup_printf_escaped( SOAP_PASSPORT_SSO_PAYLOAD,
299                ic->acc->user, pass, md->pp_policy );
300       
301        return MSN_SOAP_OK;
302}
303
304static xt_status msn_soap_passport_sso_token( struct xt_node *node, gpointer data )
305{
306        struct msn_soap_req_data *soap_req = data;
307        struct msn_soap_passport_sso_data *sd = soap_req->data;
308        struct msn_data *md = soap_req->ic->proto_data;
309        struct xt_node *p;
310        char *id;
311       
312        if( ( id = xt_find_attr( node, "Id" ) ) == NULL )
313                return XT_HANDLED;
314        id += strlen( id ) - 1;
315        if( *id == '1' &&
316            ( p = xt_find_path( node, "../../wst:RequestedProofToken/wst:BinarySecret" ) ) &&
317            p->text )
318                sd->secret = g_strdup( p->text );
319       
320        *id -= '1';
321        if( *id >= 0 && *id < sizeof( md->tokens ) / sizeof( md->tokens[0] ) )
322        {
323                g_free( md->tokens[(int)*id] );
324                md->tokens[(int)*id] = g_strdup( node->text );
325        }
326       
327        return XT_HANDLED;
328}
329
330static xt_status msn_soap_passport_failure( struct xt_node *node, gpointer data )
331{
332        struct msn_soap_req_data *soap_req = data;
333        struct msn_soap_passport_sso_data *sd = soap_req->data;
334        struct xt_node *code = xt_find_node( node->children, "faultcode" );
335        struct xt_node *string = xt_find_node( node->children, "faultstring" );
336        struct xt_node *url;
337       
338        if( code == NULL || code->text_len == 0 )
339                sd->error = g_strdup( "Unknown error" );
340        else if( strcmp( code->text, "psf:Redirect" ) == 0 &&
341                 ( url = xt_find_node( node->children, "psf:redirectUrl" ) ) &&
342                 url->text_len > 0 )
343                sd->redirect = g_strdup( url->text );
344        else
345                sd->error = g_strdup_printf( "%s (%s)", code->text, string && string->text_len ?
346                                             string->text : "no description available" );
347       
348        return XT_HANDLED;
349}
350
351static const struct xt_handler_entry msn_soap_passport_sso_parser[] = {
352        { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token },
353        { "S:Fault", "S:Envelope", msn_soap_passport_failure },
354        { NULL, NULL, NULL }
355};
356
357static char *msn_key_fuckery( char *key, int key_len, char *type )
358{
359        unsigned char hash1[20+strlen(type)+1];
360        unsigned char hash2[20];
361        char *ret;
362       
363        sha1_hmac( key, key_len, type, 0, hash1 );
364        strcpy( (char*) hash1 + 20, type );
365        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash2 );
366       
367        /* This is okay as hash1 is read completely before it's overwritten. */
368        sha1_hmac( key, key_len, (char*) hash1, 20, hash1 );
369        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash1 );
370       
371        ret = g_malloc( 24 );
372        memcpy( ret, hash2, 20 );
373        memcpy( ret + 20, hash1, 4 );
374        return ret;
375}
376
377static int msn_soap_passport_sso_handle_response( struct msn_soap_req_data *soap_req )
378{
379        struct msn_soap_passport_sso_data *sd = soap_req->data;
380        struct im_connection *ic = soap_req->ic;
381        struct msn_data *md = ic->proto_data;
382        char *key1, *key2, *key3, *blurb64;
383        int key1_len;
384        unsigned char *padnonce, *des3res;
385        struct
386        {
387                unsigned int uStructHeaderSize; // 28. Does not count data
388                unsigned int uCryptMode; // CRYPT_MODE_CBC (1)
389                unsigned int uCipherType; // TripleDES (0x6603)
390                unsigned int uHashType; // SHA1 (0x8004)
391                unsigned int uIVLen;    // 8
392                unsigned int uHashLen;  // 20
393                unsigned int uCipherLen; // 72
394                unsigned char iv[8];
395                unsigned char hash[20];
396                unsigned char cipherbytes[72];
397        } blurb = {
398                GUINT32_TO_LE( 28 ),
399                GUINT32_TO_LE( 1 ),
400                GUINT32_TO_LE( 0x6603 ),
401                GUINT32_TO_LE( 0x8004 ),
402                GUINT32_TO_LE( 8 ),
403                GUINT32_TO_LE( 20 ),
404                GUINT32_TO_LE( 72 ),
405        };
406       
407        if( sd->redirect )
408                return MSN_SOAP_RETRY;
409       
410        if( md->soapq )
411        {
412                md->flags &= ~MSN_REAUTHING; 
413                return msn_soapq_flush( ic, TRUE );
414        }
415       
416        if( sd->secret == NULL )
417        {
418                msn_auth_got_passport_token( ic, NULL, sd->error ? sd->error : soap_req->error );
419                return MSN_SOAP_OK;
420        }
421
422        key1_len = base64_decode( sd->secret, (unsigned char**) &key1 );
423       
424        key2 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY HASH" );
425        key3 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY ENCRYPTION" );
426       
427        sha1_hmac( key2, 24, sd->nonce, 0, blurb.hash );
428        padnonce = g_malloc( strlen( sd->nonce ) + 8 );
429        strcpy( (char*) padnonce, sd->nonce );
430        memset( padnonce + strlen( sd->nonce ), 8, 8 );
431       
432        random_bytes( blurb.iv, 8 );
433       
434        ssl_des3_encrypt( (unsigned char*) key3, 24, padnonce, strlen( sd->nonce ) + 8, blurb.iv, &des3res );
435        memcpy( blurb.cipherbytes, des3res, 72 );
436       
437        blurb64 = base64_encode( (unsigned char*) &blurb, sizeof( blurb ) );
438        msn_auth_got_passport_token( ic, blurb64, NULL );
439       
440        g_free( padnonce );
441        g_free( blurb64 );
442        g_free( des3res );
443        g_free( key1 );
444        g_free( key2 );
445        g_free( key3 );
446       
447        return MSN_SOAP_OK;
448}
449
450static int msn_soap_passport_sso_free_data( struct msn_soap_req_data *soap_req )
451{
452        struct msn_soap_passport_sso_data *sd = soap_req->data;
453       
454        g_free( sd->nonce );
455        g_free( sd->secret );
456        g_free( sd->error );
457        g_free( sd->redirect );
458        g_free( sd );
459       
460        return MSN_SOAP_OK;
461}
462
463int msn_soap_passport_sso_request( struct im_connection *ic, const char *nonce )
464{
465        struct msn_soap_passport_sso_data *sd = g_new0( struct msn_soap_passport_sso_data, 1 );
466       
467        sd->nonce = g_strdup( nonce );
468       
469        return msn_soap_start( ic, sd, msn_soap_passport_sso_build_request,
470                                       msn_soap_passport_sso_parser,
471                                       msn_soap_passport_sso_handle_response,
472                                       msn_soap_passport_sso_free_data );
473}
474
475
476/* memlist: Fetching the membership list (NOT address book) */
477
478static int msn_soap_memlist_build_request( struct msn_soap_req_data *soap_req )
479{
480        struct msn_data *md = soap_req->ic->proto_data;
481       
482        soap_req->url = g_strdup( SOAP_MEMLIST_URL );
483        soap_req->action = g_strdup( SOAP_MEMLIST_ACTION );
484        soap_req->payload = msn_soap_abservice_build( SOAP_MEMLIST_PAYLOAD, "Initial", md->tokens[1] );
485       
486        return 1;
487}
488
489static xt_status msn_soap_memlist_member( struct xt_node *node, gpointer data )
490{
491        bee_user_t *bu;
492        struct msn_buddy_data *bd;
493        struct xt_node *p;
494        char *role = NULL, *handle = NULL;
495        struct msn_soap_req_data *soap_req = data;
496        struct im_connection *ic = soap_req->ic;
497       
498        if( ( p = xt_find_path( node, "../../MemberRole" ) ) )
499                role = p->text;
500       
501        if( ( p = xt_find_node( node->children, "PassportName" ) ) )
502                handle = p->text;
503       
504        if( !role || !handle || 
505            !( ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) ||
506               ( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) )
507                return XT_HANDLED;
508       
509        bd = bu->data;
510        if( strcmp( role, "Allow" ) == 0 )
511        {
512                bd->flags |= MSN_BUDDY_AL;
513                ic->permit = g_slist_prepend( ic->permit, g_strdup( handle ) );
514        }
515        else if( strcmp( role, "Block" ) == 0 )
516        {
517                bd->flags |= MSN_BUDDY_BL;
518                ic->deny = g_slist_prepend( ic->deny, g_strdup( handle ) );
519        }
520        else if( strcmp( role, "Reverse" ) == 0 )
521                bd->flags |= MSN_BUDDY_RL;
522        else if( strcmp( role, "Pending" ) == 0 )
523                bd->flags |= MSN_BUDDY_PL;
524       
525        if( getenv( "BITLBEE_DEBUG" ) )
526                fprintf( stderr, "%p %s %d\n", bu, handle, bd->flags );
527       
528        return XT_HANDLED;
529}
530
531static const struct xt_handler_entry msn_soap_memlist_parser[] = {
532        { "Member", "Members", msn_soap_memlist_member },
533        { NULL,               NULL,     NULL                        }
534};
535
536static int msn_soap_memlist_handle_response( struct msn_soap_req_data *soap_req )
537{
538        msn_soap_addressbook_request( soap_req->ic );
539       
540        return MSN_SOAP_OK;
541}
542
543static int msn_soap_memlist_free_data( struct msn_soap_req_data *soap_req )
544{
545        return 0;
546}
547
548int msn_soap_memlist_request( struct im_connection *ic )
549{
550        return msn_soap_start( ic, NULL, msn_soap_memlist_build_request,
551                                         msn_soap_memlist_parser,
552                                         msn_soap_memlist_handle_response,
553                                         msn_soap_memlist_free_data );
554}
555
556/* Variant: Adding/Removing people */
557struct msn_soap_memlist_edit_data
558{
559        char *handle;
560        gboolean add;
561        msn_buddy_flags_t list;
562};
563
564static int msn_soap_memlist_edit_build_request( struct msn_soap_req_data *soap_req )
565{
566        struct msn_data *md = soap_req->ic->proto_data;
567        struct msn_soap_memlist_edit_data *med = soap_req->data;
568        char *add, *scenario, *list;
569       
570        soap_req->url = g_strdup( SOAP_MEMLIST_URL );
571        if( med->add )
572        {
573                soap_req->action = g_strdup( SOAP_MEMLIST_ADD_ACTION );
574                add = "Add";
575        }
576        else
577        {
578                soap_req->action = g_strdup( SOAP_MEMLIST_DEL_ACTION );
579                add = "Delete";
580        }
581        switch( med->list )
582        {
583        case MSN_BUDDY_AL:
584                scenario = "BlockUnblock";
585                list = "Allow";
586                break;
587        case MSN_BUDDY_BL:
588                scenario = "BlockUnblock";
589                list = "Block";
590                break;
591        case MSN_BUDDY_RL:
592                scenario = "Timer";
593                list = "Reverse";
594                break;
595        case MSN_BUDDY_PL:
596        default:
597                scenario = "Timer";
598                list = "Pending";
599                break;
600        }
601        soap_req->payload = msn_soap_abservice_build( SOAP_MEMLIST_EDIT_PAYLOAD,
602                scenario, md->tokens[1], add, list, med->handle, add );
603       
604        return 1;
605}
606
607static int msn_soap_memlist_edit_handle_response( struct msn_soap_req_data *soap_req )
608{
609        return MSN_SOAP_OK;
610}
611
612static int msn_soap_memlist_edit_free_data( struct msn_soap_req_data *soap_req )
613{
614        struct msn_soap_memlist_edit_data *med = soap_req->data;
615       
616        g_free( med->handle );
617        g_free( med );
618       
619        return 0;
620}
621
622int msn_soap_memlist_edit( struct im_connection *ic, const char *handle, gboolean add, int list )
623{
624        struct msn_soap_memlist_edit_data *med;
625       
626        med = g_new0( struct msn_soap_memlist_edit_data, 1 );
627        med->handle = g_strdup( handle );
628        med->add = add;
629        med->list = list;
630       
631        return msn_soap_start( ic, med, msn_soap_memlist_edit_build_request,
632                                        NULL,
633                                        msn_soap_memlist_edit_handle_response,
634                                        msn_soap_memlist_edit_free_data );
635}
636
637
638/* addressbook: Fetching the membership list (NOT address book) */
639
640static int msn_soap_addressbook_build_request( struct msn_soap_req_data *soap_req )
641{
642        struct msn_data *md = soap_req->ic->proto_data;
643       
644        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
645        soap_req->action = g_strdup( SOAP_ADDRESSBOOK_ACTION );
646        soap_req->payload = msn_soap_abservice_build( SOAP_ADDRESSBOOK_PAYLOAD, "Initial", md->tokens[1] );
647       
648        return 1;
649}
650
651static xt_status msn_soap_addressbook_group( struct xt_node *node, gpointer data )
652{
653        struct xt_node *p;
654        char *id = NULL, *name = NULL;
655        struct msn_soap_req_data *soap_req = data;
656        struct msn_data *md = soap_req->ic->proto_data;
657       
658        if( ( p = xt_find_path( node, "../groupId" ) ) )
659                id = p->text;
660       
661        if( ( p = xt_find_node( node->children, "name" ) ) )
662                name = p->text;
663       
664        if( id && name )
665        {
666                struct msn_group *mg = g_new0( struct msn_group, 1 );
667                mg->id = g_strdup( id );
668                mg->name = g_strdup( name );
669                md->groups = g_slist_prepend( md->groups, mg );
670        }
671       
672        if( getenv( "BITLBEE_DEBUG" ) )
673                fprintf( stderr, "%s %s\n", id, name );
674       
675        return XT_HANDLED;
676}
677
678static xt_status msn_soap_addressbook_contact( struct xt_node *node, gpointer data )
679{
680        bee_user_t *bu;
681        struct msn_buddy_data *bd;
682        struct xt_node *p;
683        char *id = NULL, *type = NULL, *handle = NULL, *is_msgr = "false",
684             *display_name = NULL, *group_id = NULL;
685        struct msn_soap_req_data *soap_req = data;
686        struct im_connection *ic = soap_req->ic;
687        struct msn_group *group;
688       
689        if( ( p = xt_find_path( node, "../contactId" ) ) )
690                id = p->text;
691        if( ( p = xt_find_node( node->children, "contactType" ) ) )
692                type = p->text;
693        if( ( p = xt_find_node( node->children, "passportName" ) ) )
694                handle = p->text;
695        if( ( p = xt_find_node( node->children, "displayName" ) ) )
696                display_name = p->text;
697        if( ( p = xt_find_node( node->children, "isMessengerUser" ) ) )
698                is_msgr = 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                /* Try to fetch the profile; if the user has one, that's where
709                   we can find the persistent display_name. */
710                if( ( p = xt_find_node( node->children, "CID" ) ) && p->text )
711                        msn_soap_profile_get( ic, p->text );
712               
713                return XT_HANDLED;
714        }
715       
716        if( !bool2int( is_msgr ) || handle == NULL )
717                return XT_HANDLED;
718       
719        if( !( bu = bee_user_by_handle( ic->bee, ic, handle ) ) &&
720            !( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) )
721                return XT_HANDLED;
722       
723        bd = bu->data;
724        bd->flags |= MSN_BUDDY_FL;
725        g_free( bd->cid );
726        bd->cid = g_strdup( id );
727       
728        imcb_rename_buddy( ic, handle, display_name );
729       
730        if( group_id && ( group = msn_group_by_id( ic, group_id ) ) )
731                imcb_add_buddy( ic, handle, group->name );
732       
733        if( getenv( "BITLBEE_DEBUG" ) )
734                fprintf( stderr, "%s %s %s %s\n", id, type, handle, display_name );
735       
736        return XT_HANDLED;
737}
738
739static const struct xt_handler_entry msn_soap_addressbook_parser[] = {
740        { "contactInfo", "Contact", msn_soap_addressbook_contact },
741        { "groupInfo", "Group", msn_soap_addressbook_group },
742        { NULL,               NULL,     NULL                        }
743};
744
745static int msn_soap_addressbook_handle_response( struct msn_soap_req_data *soap_req )
746{
747        GSList *l;
748        int wtf = 0;
749       
750        for( l = soap_req->ic->bee->users; l; l = l->next )
751        {
752                struct bee_user *bu = l->data;
753                struct msn_buddy_data *bd = bu->data;
754               
755                if( bu->ic == soap_req->ic && bd )
756                {
757                        msn_buddy_ask( bu );
758                       
759                        if( ( bd->flags & ( MSN_BUDDY_AL | MSN_BUDDY_BL ) ) ==
760                                          ( MSN_BUDDY_AL | MSN_BUDDY_BL ) )
761                        {
762                                bd->flags &= ~MSN_BUDDY_BL;
763                                wtf++;
764                        }
765                }
766        }
767       
768        if( wtf )
769                imcb_log( soap_req->ic, "Warning: %d contacts were in both your "
770                          "block and your allow list. Assuming they're all "
771                          "allowed. Use the official WLM client once to fix "
772                          "this.", wtf );
773       
774        msn_auth_got_contact_list( soap_req->ic );
775       
776        return MSN_SOAP_OK;
777}
778
779static int msn_soap_addressbook_free_data( struct msn_soap_req_data *soap_req )
780{
781        return 0;
782}
783
784int msn_soap_addressbook_request( struct im_connection *ic )
785{
786        return msn_soap_start( ic, NULL, msn_soap_addressbook_build_request,
787                                         msn_soap_addressbook_parser,
788                                         msn_soap_addressbook_handle_response,
789                                         msn_soap_addressbook_free_data );
790}
791
792/* Variant: Change our display name. */
793static int msn_soap_ab_namechange_build_request( struct msn_soap_req_data *soap_req )
794{
795        struct msn_data *md = soap_req->ic->proto_data;
796       
797        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
798        soap_req->action = g_strdup( SOAP_AB_NAMECHANGE_ACTION );
799        soap_req->payload = msn_soap_abservice_build( SOAP_AB_NAMECHANGE_PAYLOAD,
800                "Timer", md->tokens[1], (char *) soap_req->data );
801       
802        return 1;
803}
804
805static int msn_soap_ab_namechange_handle_response( struct msn_soap_req_data *soap_req )
806{
807        /* TODO: Ack the change? Not sure what the NAKs look like.. */
808        return MSN_SOAP_OK;
809}
810
811static int msn_soap_ab_namechange_free_data( struct msn_soap_req_data *soap_req )
812{
813        g_free( soap_req->data );
814        return 0;
815}
816
817int msn_soap_addressbook_set_display_name( struct im_connection *ic, const char *new )
818{
819        return msn_soap_start( ic, g_strdup( new ),
820                               msn_soap_ab_namechange_build_request,
821                               NULL,
822                               msn_soap_ab_namechange_handle_response,
823                               msn_soap_ab_namechange_free_data );
824}
825
826/* Add a contact. */
827static int msn_soap_ab_contact_add_build_request( struct msn_soap_req_data *soap_req )
828{
829        struct msn_data *md = soap_req->ic->proto_data;
830        bee_user_t *bu = soap_req->data;
831       
832        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
833        soap_req->action = g_strdup( SOAP_AB_CONTACT_ADD_ACTION );
834        soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_ADD_PAYLOAD,
835                "ContactSave", md->tokens[1], bu->handle, bu->fullname ? bu->fullname : bu->handle );
836       
837        return 1;
838}
839
840static xt_status msn_soap_ab_contact_add_cid( struct xt_node *node, gpointer data )
841{
842        struct msn_soap_req_data *soap_req = data;
843        bee_user_t *bu = soap_req->data;
844        struct msn_buddy_data *bd = bu->data;
845       
846        g_free( bd->cid );
847        bd->cid = g_strdup( node->text );
848       
849        return XT_HANDLED;
850}
851
852static const struct xt_handler_entry msn_soap_ab_contact_add_parser[] = {
853        { "guid", "ABContactAddResult", msn_soap_ab_contact_add_cid },
854        { NULL,               NULL,     NULL                        }
855};
856
857static int msn_soap_ab_contact_add_handle_response( struct msn_soap_req_data *soap_req )
858{
859        /* TODO: Ack the change? Not sure what the NAKs look like.. */
860        return MSN_SOAP_OK;
861}
862
863static int msn_soap_ab_contact_add_free_data( struct msn_soap_req_data *soap_req )
864{
865        return 0;
866}
867
868int msn_soap_ab_contact_add( struct im_connection *ic, bee_user_t *bu )
869{
870        return msn_soap_start( ic, bu,
871                               msn_soap_ab_contact_add_build_request,
872                               msn_soap_ab_contact_add_parser,
873                               msn_soap_ab_contact_add_handle_response,
874                               msn_soap_ab_contact_add_free_data );
875}
876
877/* Remove a contact. */
878static int msn_soap_ab_contact_del_build_request( struct msn_soap_req_data *soap_req )
879{
880        struct msn_data *md = soap_req->ic->proto_data;
881        const char *cid = soap_req->data;
882       
883        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
884        soap_req->action = g_strdup( SOAP_AB_CONTACT_DEL_ACTION );
885        soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_DEL_PAYLOAD,
886                "Timer", md->tokens[1], cid );
887       
888        return 1;
889}
890
891static int msn_soap_ab_contact_del_handle_response( struct msn_soap_req_data *soap_req )
892{
893        /* TODO: Ack the change? Not sure what the NAKs look like.. */
894        return MSN_SOAP_OK;
895}
896
897static int msn_soap_ab_contact_del_free_data( struct msn_soap_req_data *soap_req )
898{
899        g_free( soap_req->data );
900        return 0;
901}
902
903int msn_soap_ab_contact_del( struct im_connection *ic, bee_user_t *bu )
904{
905        struct msn_buddy_data *bd = bu->data;
906       
907        return msn_soap_start( ic, g_strdup( bd->cid ),
908                               msn_soap_ab_contact_del_build_request,
909                               NULL,
910                               msn_soap_ab_contact_del_handle_response,
911                               msn_soap_ab_contact_del_free_data );
912}
913
914
915
916/* Storage stuff: Fetch profile. */
917static int msn_soap_profile_get_build_request( struct msn_soap_req_data *soap_req )
918{
919        struct msn_data *md = soap_req->ic->proto_data;
920       
921        soap_req->url = g_strdup( SOAP_STORAGE_URL );
922        soap_req->action = g_strdup( SOAP_PROFILE_GET_ACTION );
923        soap_req->payload = g_markup_printf_escaped( SOAP_PROFILE_GET_PAYLOAD,
924                md->tokens[3], (char*) soap_req->data );
925       
926        return 1;
927}
928
929static xt_status msn_soap_profile_get_result( struct xt_node *node, gpointer data )
930{
931        struct msn_soap_req_data *soap_req = data;
932        struct im_connection *ic = soap_req->ic;
933        struct msn_data *md = soap_req->ic->proto_data;
934        struct xt_node *dn;
935       
936        if( ( dn = xt_find_node( node->children, "DisplayName" ) ) && dn->text )
937        {
938                set_t *set = set_find( &ic->acc->set, "display_name" );
939                g_free( set->value );
940                set->value = g_strdup( dn->text );
941               
942                md->flags |= MSN_GOT_PROFILE_DN;
943        }
944       
945        return XT_HANDLED;
946}
947
948static xt_status msn_soap_profile_get_rid( struct xt_node *node, gpointer data )
949{
950        struct msn_soap_req_data *soap_req = data;
951        struct msn_data *md = soap_req->ic->proto_data;
952       
953        g_free( md->profile_rid );
954        md->profile_rid = g_strdup( node->text );
955       
956        return XT_HANDLED;
957}
958
959static const struct xt_handler_entry msn_soap_profile_get_parser[] = {
960        { "ExpressionProfile", "GetProfileResult", msn_soap_profile_get_result },
961        { "ResourceID",        "GetProfileResult", msn_soap_profile_get_rid },
962        { NULL,               NULL,     NULL                        }
963};
964
965static int msn_soap_profile_get_handle_response( struct msn_soap_req_data *soap_req )
966{
967        struct msn_data *md = soap_req->ic->proto_data;
968       
969        md->flags |= MSN_GOT_PROFILE;
970        msn_ns_finish_login( soap_req->ic );
971       
972        return MSN_SOAP_OK;
973}
974
975static int msn_soap_profile_get_free_data( struct msn_soap_req_data *soap_req )
976{
977        g_free( soap_req->data );
978        return 0;
979}
980
981int msn_soap_profile_get( struct im_connection *ic, const char *cid )
982{
983        return msn_soap_start( ic, g_strdup( cid ),
984                               msn_soap_profile_get_build_request,
985                               msn_soap_profile_get_parser,
986                               msn_soap_profile_get_handle_response,
987                               msn_soap_profile_get_free_data );
988}
989
990/* Update profile (display name). */
991static int msn_soap_profile_set_dn_build_request( struct msn_soap_req_data *soap_req )
992{
993        struct msn_data *md = soap_req->ic->proto_data;
994       
995        soap_req->url = g_strdup( SOAP_STORAGE_URL );
996        soap_req->action = g_strdup( SOAP_PROFILE_SET_DN_ACTION );
997        soap_req->payload = g_markup_printf_escaped( SOAP_PROFILE_SET_DN_PAYLOAD,
998                md->tokens[3], md->profile_rid, (char*) soap_req->data );
999       
1000        return 1;
1001}
1002
1003static const struct xt_handler_entry msn_soap_profile_set_dn_parser[] = {
1004        { NULL,               NULL,     NULL                        }
1005};
1006
1007static int msn_soap_profile_set_dn_handle_response( struct msn_soap_req_data *soap_req )
1008{
1009        return MSN_SOAP_OK;
1010}
1011
1012static int msn_soap_profile_set_dn_free_data( struct msn_soap_req_data *soap_req )
1013{
1014        g_free( soap_req->data );
1015        return 0;
1016}
1017
1018int msn_soap_profile_set_dn( struct im_connection *ic, const char *dn )
1019{
1020        return msn_soap_start( ic, g_strdup( dn ),
1021                               msn_soap_profile_set_dn_build_request,
1022                               msn_soap_profile_set_dn_parser,
1023                               msn_soap_profile_set_dn_handle_response,
1024                               msn_soap_profile_set_dn_free_data );
1025}
Note: See TracBrowser for help on using the repository browser.