source: protocols/msn/soap.c @ 07874be

Last change on this file since 07874be was 4452e69, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-14T13:06:11Z

Allow changing the display_name, now permanently!

  • Property mode set to 100644
File size: 18.1 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
149
150/* passport_sso: Authentication MSNP15+ */
151
152struct msn_soap_passport_sso_data
153{
154        char *policy;
155        char *nonce;
156        char *secret;
157};
158
159static int msn_soap_passport_sso_build_request( struct msn_soap_req_data *soap_req )
160{
161        struct msn_soap_passport_sso_data *sd = soap_req->data;
162        struct im_connection *ic = soap_req->ic;
163       
164        if( g_str_has_suffix( ic->acc->user, "@msn.com" ) )
165                soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL_MSN );
166        else
167                soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL );
168       
169        soap_req->payload = g_markup_printf_escaped( SOAP_PASSPORT_SSO_PAYLOAD,
170                ic->acc->user, ic->acc->pass, sd->policy );
171       
172        return MSN_SOAP_OK;
173}
174
175static xt_status msn_soap_passport_sso_token( struct xt_node *node, gpointer data )
176{
177        struct msn_soap_req_data *soap_req = data;
178        struct msn_soap_passport_sso_data *sd = soap_req->data;
179        struct msn_data *md = soap_req->ic->proto_data;
180        struct xt_node *p;
181        char *id;
182       
183        if( ( id = xt_find_attr( node, "Id" ) ) == NULL )
184                return XT_HANDLED;
185        id += strlen( id ) - 1;
186        if( *id == '1' &&
187            ( p = node->parent ) && ( p = p->parent ) &&
188            ( p = xt_find_node( p->children, "wst:RequestedProofToken" ) ) &&
189            ( p = xt_find_node( p->children, "wst:BinarySecret" ) ) &&
190            p->text )
191                sd->secret = g_strdup( p->text );
192       
193        *id -= '1';
194        if( *id >= 0 && *id <= 2 )
195        {
196                g_free( md->tokens[(int)*id] );
197                md->tokens[(int)*id] = g_strdup( node->text );
198        }
199       
200        return XT_HANDLED;
201}
202
203static const struct xt_handler_entry msn_soap_passport_sso_parser[] = {
204        { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token },
205        { NULL, NULL, NULL }
206};
207
208static char *msn_key_fuckery( char *key, int key_len, char *type )
209{
210        unsigned char hash1[20+strlen(type)+1];
211        unsigned char hash2[20];
212        char *ret;
213       
214        sha1_hmac( key, key_len, type, 0, hash1 );
215        strcpy( (char*) hash1 + 20, type );
216        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash2 );
217       
218        /* This is okay as hash1 is read completely before it's overwritten. */
219        sha1_hmac( key, key_len, (char*) hash1, 20, hash1 );
220        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash1 );
221       
222        ret = g_malloc( 24 );
223        memcpy( ret, hash2, 20 );
224        memcpy( ret + 20, hash1, 4 );
225        return ret;
226}
227
228static int msn_soap_passport_sso_handle_response( struct msn_soap_req_data *soap_req )
229{
230        struct msn_soap_passport_sso_data *sd = soap_req->data;
231        struct im_connection *ic = soap_req->ic;
232        char *key1, *key2, *key3, *blurb64;
233        int key1_len;
234        unsigned char *padnonce, *des3res;
235        struct
236        {
237                unsigned int uStructHeaderSize; // 28. Does not count data
238                unsigned int uCryptMode; // CRYPT_MODE_CBC (1)
239                unsigned int uCipherType; // TripleDES (0x6603)
240                unsigned int uHashType; // SHA1 (0x8004)
241                unsigned int uIVLen;    // 8
242                unsigned int uHashLen;  // 20
243                unsigned int uCipherLen; // 72
244                unsigned char iv[8];
245                unsigned char hash[20];
246                unsigned char cipherbytes[72];
247        } blurb = {
248                GUINT32_TO_LE( 28 ),
249                GUINT32_TO_LE( 1 ),
250                GUINT32_TO_LE( 0x6603 ),
251                GUINT32_TO_LE( 0x8004 ),
252                GUINT32_TO_LE( 8 ),
253                GUINT32_TO_LE( 20 ),
254                GUINT32_TO_LE( 72 ),
255        };
256
257        key1_len = base64_decode( sd->secret, (unsigned char**) &key1 );
258       
259        key2 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY HASH" );
260        key3 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY ENCRYPTION" );
261       
262        sha1_hmac( key2, 24, sd->nonce, 0, blurb.hash );
263        padnonce = g_malloc( strlen( sd->nonce ) + 8 );
264        strcpy( (char*) padnonce, sd->nonce );
265        memset( padnonce + strlen( sd->nonce ), 8, 8 );
266       
267        random_bytes( blurb.iv, 8 );
268       
269        ssl_des3_encrypt( (unsigned char*) key3, 24, padnonce, strlen( sd->nonce ) + 8, blurb.iv, &des3res );
270        memcpy( blurb.cipherbytes, des3res, 72 );
271       
272        blurb64 = base64_encode( (unsigned char*) &blurb, sizeof( blurb ) );
273        msn_auth_got_passport_token( ic, blurb64 );
274       
275        g_free( padnonce );
276        g_free( blurb64 );
277        g_free( des3res );
278        g_free( key1 );
279        g_free( key2 );
280        g_free( key3 );
281       
282        return MSN_SOAP_OK;
283}
284
285static int msn_soap_passport_sso_free_data( struct msn_soap_req_data *soap_req )
286{
287        struct msn_soap_passport_sso_data *sd = soap_req->data;
288       
289        g_free( sd->policy );
290        g_free( sd->nonce );
291        g_free( sd->secret );
292       
293        return MSN_SOAP_OK;
294}
295
296int msn_soap_passport_sso_request( struct im_connection *ic, const char *policy, const char *nonce )
297{
298        struct msn_soap_passport_sso_data *sd = g_new0( struct msn_soap_passport_sso_data, 1 );
299       
300        sd->policy = g_strdup( policy );
301        sd->nonce = g_strdup( nonce );
302       
303        return msn_soap_start( ic, sd, msn_soap_passport_sso_build_request,
304                                       msn_soap_passport_sso_parser,
305                                       msn_soap_passport_sso_handle_response,
306                                       msn_soap_passport_sso_free_data );
307}
308
309
310/* oim_send: Sending offline messages */
311
312struct msn_soap_oim_send_data
313{
314        char *to;
315        char *msg;
316        int number;
317        int need_retry;
318};
319
320static int msn_soap_oim_build_request( struct msn_soap_req_data *soap_req )
321{
322        struct msn_soap_oim_send_data *oim = soap_req->data;
323        struct im_connection *ic = soap_req->ic;
324        struct msn_data *md = ic->proto_data;
325        char *display_name_b64;
326       
327        display_name_b64 = tobase64( set_getstr( &ic->acc->set, "display_name" ) );
328       
329        soap_req->url = g_strdup( SOAP_OIM_SEND_URL );
330        soap_req->action = g_strdup( SOAP_OIM_SEND_ACTION );
331        soap_req->payload = g_markup_printf_escaped( SOAP_OIM_SEND_PAYLOAD,
332                ic->acc->user, display_name_b64, MSNP_VER, MSNP_BUILD,
333                oim->to, md->tokens[2],
334                MSNP11_PROD_ID, md->lock_key ? md->lock_key : "",
335                oim->number, oim->number, oim->msg );
336       
337        g_free( display_name_b64 );
338       
339        return MSN_SOAP_OK;
340}
341
342static xt_status msn_soap_oim_send_challenge( struct xt_node *node, gpointer data )
343{
344        struct msn_soap_req_data *soap_req = data;
345        struct msn_soap_oim_send_data *oim = soap_req->data;
346        struct im_connection *ic = soap_req->ic;
347        struct msn_data *md = ic->proto_data;
348       
349        g_free( md->lock_key );
350        md->lock_key = msn_p11_challenge( node->text );
351       
352        oim->need_retry = 1;
353       
354        return XT_HANDLED;
355}
356
357static const struct xt_handler_entry msn_soap_oim_send_parser[] = {
358        { "LockKeyChallenge", "detail", msn_soap_oim_send_challenge },
359        { NULL,               NULL,     NULL                        }
360};
361
362static int msn_soap_oim_handle_response( struct msn_soap_req_data *soap_req )
363{
364        struct msn_soap_oim_send_data *oim = soap_req->data;
365       
366        if( soap_req->http_req->status_code == 500 && oim->need_retry && soap_req->ttl > 0 )
367        {
368                oim->need_retry = 0;
369                return MSN_SOAP_RETRY;
370        }
371        else if( soap_req->http_req->status_code == 200 )
372        {
373                imcb_log( soap_req->ic, "Offline message successfully delivered to %s", oim->to );
374                return MSN_SOAP_OK;
375        }
376        else
377        {
378                imcb_log( soap_req->ic, "Failed to deliver offline message to %s:\n%s", oim->to, oim->msg );
379                return MSN_SOAP_ABORT;
380        }
381}
382
383static int msn_soap_oim_free_data( struct msn_soap_req_data *soap_req )
384{
385        struct msn_soap_oim_send_data *oim = soap_req->data;
386       
387        g_free( oim->to );
388        g_free( oim->msg );
389        g_free( oim );
390       
391        return MSN_SOAP_OK;
392}
393
394int msn_soap_oim_send( struct im_connection *ic, const char *to, const char *msg )
395{
396        struct msn_soap_oim_send_data *data;
397       
398        data = g_new0( struct msn_soap_oim_send_data, 1 );
399        data->to = g_strdup( to );
400        data->msg = tobase64( msg );
401        data->number = 1;
402       
403        return msn_soap_start( ic, data, msn_soap_oim_build_request,
404                                         msn_soap_oim_send_parser,
405                                         msn_soap_oim_handle_response,
406                                         msn_soap_oim_free_data );
407}
408
409int msn_soap_oim_send_queue( struct im_connection *ic, GSList **msgq )
410{
411        GSList *l;
412        char *n = NULL;
413       
414        for( l = *msgq; l; l = l->next )
415        {
416                struct msn_message *m = l->data;
417               
418                if( n == NULL )
419                        n = m->who;
420                if( strcmp( n, m->who ) == 0 )
421                        msn_soap_oim_send( ic, m->who, m->text );
422        }
423       
424        while( *msgq != NULL )
425        {
426                struct msn_message *m = (*msgq)->data;
427               
428                g_free( m->who );
429                g_free( m->text );
430                g_free( m );
431               
432                *msgq = g_slist_remove( *msgq, m );
433        }
434       
435        return 1;
436}
437
438
439/* memlist: Fetching the membership list (NOT address book) */
440
441static int msn_soap_memlist_build_request( struct msn_soap_req_data *soap_req )
442{
443        struct msn_data *md = soap_req->ic->proto_data;
444       
445        soap_req->url = g_strdup( SOAP_MEMLIST_URL );
446        soap_req->action = g_strdup( SOAP_MEMLIST_ACTION );
447        soap_req->payload = g_markup_printf_escaped( SOAP_MEMLIST_PAYLOAD, md->tokens[1] );
448       
449        return 1;
450}
451
452static xt_status msn_soap_memlist_member( struct xt_node *node, gpointer data )
453{
454        bee_user_t *bu;
455        struct msn_buddy_data *bd;
456        struct xt_node *p;
457        char *role = NULL, *handle = NULL;
458        struct msn_soap_req_data *soap_req = data;
459        struct im_connection *ic = soap_req->ic;
460       
461        if( ( p = node->parent ) && ( p = p->parent ) &&
462            ( p = xt_find_node( p->children, "MemberRole" ) ) )
463                role = p->text;
464       
465        if( ( p = xt_find_node( node->children, "PassportName" ) ) )
466                handle = p->text;
467       
468        if( !role || !handle || 
469            !( ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) ||
470               ( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) )
471                return XT_HANDLED;
472       
473        bd = bu->data;
474        if( strcmp( role, "Allow" ) == 0 )
475                bd->flags |= MSN_BUDDY_AL;
476        else if( strcmp( role, "Block" ) == 0 )
477                bd->flags |= MSN_BUDDY_BL;
478        else if( strcmp( role, "Reverse" ) == 0 )
479                bd->flags |= MSN_BUDDY_RL;
480        else if( strcmp( role, "Pending" ) == 0 )
481                bd->flags |= MSN_BUDDY_PL;
482       
483        return XT_HANDLED;
484}
485
486static const struct xt_handler_entry msn_soap_memlist_parser[] = {
487        { "Member", "Members", msn_soap_memlist_member },
488        { NULL,               NULL,     NULL                        }
489};
490
491static int msn_soap_memlist_handle_response( struct msn_soap_req_data *soap_req )
492{
493        msn_soap_addressbook_request( soap_req->ic );
494       
495        return MSN_SOAP_OK;
496}
497
498static int msn_soap_memlist_free_data( struct msn_soap_req_data *soap_req )
499{
500        return 0;
501}
502
503int msn_soap_memlist_request( struct im_connection *ic )
504{
505        return msn_soap_start( ic, NULL, msn_soap_memlist_build_request,
506                                         msn_soap_memlist_parser,
507                                         msn_soap_memlist_handle_response,
508                                         msn_soap_memlist_free_data );
509}
510
511
512/* addressbook: Fetching the membership list (NOT address book) */
513
514static int msn_soap_addressbook_build_request( struct msn_soap_req_data *soap_req )
515{
516        struct msn_data *md = soap_req->ic->proto_data;
517       
518        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
519        soap_req->action = g_strdup( SOAP_ADDRESSBOOK_ACTION );
520        soap_req->payload = g_markup_printf_escaped( SOAP_ADDRESSBOOK_PAYLOAD, md->tokens[1] );
521       
522        return 1;
523}
524
525static xt_status msn_soap_addressbook_group( struct xt_node *node, gpointer data )
526{
527        struct xt_node *p;
528        char *id = NULL, *name = NULL;
529        struct msn_soap_req_data *soap_req = data;
530       
531        if( ( p = node->parent ) &&
532            ( p = xt_find_node( p->children, "groupId" ) ) )
533                id = p->text;
534       
535        if( ( p = xt_find_node( node->children, "name" ) ) )
536                name = p->text;
537       
538        printf( "%s %s\n", id, name );
539       
540        return XT_HANDLED;
541}
542
543static xt_status msn_soap_addressbook_contact( struct xt_node *node, gpointer data )
544{
545        bee_user_t *bu;
546        struct msn_buddy_data *bd;
547        struct xt_node *p;
548        char *id = NULL, *type = NULL, *handle = NULL, *display_name = NULL;
549        struct msn_soap_req_data *soap_req = data;
550        struct im_connection *ic = soap_req->ic;
551       
552        if( ( p = node->parent ) &&
553            ( p = xt_find_node( p->children, "contactId" ) ) )
554                id = p->text;
555        if( ( p = xt_find_node( node->children, "contactType" ) ) )
556                type = p->text;
557        if( ( p = xt_find_node( node->children, "passportName" ) ) )
558                handle = p->text;
559        if( ( p = xt_find_node( node->children, "displayName" ) ) )
560                display_name = p->text;
561       
562        if( type && g_strcasecmp( type, "me" ) == 0 )
563        {
564                set_t *set = set_find( &ic->acc->set, "display_name" );
565                g_free( set->value );
566                set->value = g_strdup( display_name );
567               
568                return XT_HANDLED;
569        }
570       
571        if( !( bu = bee_user_by_handle( ic->bee, ic, handle ) ) &&
572            !( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) )
573                return XT_HANDLED;
574       
575        bd = bu->data;
576        bd->flags |= MSN_BUDDY_FL;
577        g_free( bd->cid );
578        bd->cid = g_strdup( id );
579       
580        imcb_rename_buddy( ic, handle, display_name );
581       
582        printf( "%s %s %s %s\n", id, type, handle, display_name );
583       
584        return XT_HANDLED;
585}
586
587static const struct xt_handler_entry msn_soap_addressbook_parser[] = {
588        { "contactInfo", "Contact", msn_soap_addressbook_contact },
589        { "groupInfo", "Group", msn_soap_addressbook_group },
590        { NULL,               NULL,     NULL                        }
591};
592
593static int msn_soap_addressbook_handle_response( struct msn_soap_req_data *soap_req )
594{
595        msn_auth_got_contact_list( soap_req->ic );
596        return MSN_SOAP_OK;
597}
598
599static int msn_soap_addressbook_free_data( struct msn_soap_req_data *soap_req )
600{
601        return 0;
602}
603
604int msn_soap_addressbook_request( struct im_connection *ic )
605{
606        return msn_soap_start( ic, NULL, msn_soap_addressbook_build_request,
607                                         msn_soap_addressbook_parser,
608                                         msn_soap_addressbook_handle_response,
609                                         msn_soap_addressbook_free_data );
610}
611
612/* Variant: Change our display name. */
613static int msn_soap_ab_namechange_build_request( struct msn_soap_req_data *soap_req )
614{
615        struct msn_data *md = soap_req->ic->proto_data;
616       
617        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
618        soap_req->action = g_strdup( SOAP_AB_NAMECHANGE_ACTION );
619        soap_req->payload = g_markup_printf_escaped( SOAP_AB_NAMECHANGE_PAYLOAD,
620                md->tokens[1], (char *) soap_req->data );
621       
622        return 1;
623}
624
625static int msn_soap_ab_namechange_handle_response( struct msn_soap_req_data *soap_req )
626{
627        /* TODO: Ack the change? Not sure what the NAKs look like.. */
628        return MSN_SOAP_OK;
629}
630
631static int msn_soap_ab_namechange_free_data( struct msn_soap_req_data *soap_req )
632{
633        g_free( soap_req->data );
634        return 0;
635}
636
637int msn_soap_addressbook_set_display_name( struct im_connection *ic, const char *new )
638{
639        return msn_soap_start( ic, g_strdup( new ),
640                               msn_soap_ab_namechange_build_request,
641                               NULL,
642                               msn_soap_ab_namechange_handle_response,
643                               msn_soap_ab_namechange_free_data );
644}
Note: See TracBrowser for help on using the repository browser.