source: protocols/msn/soap.c @ 4e4af1b

Last change on this file since 4e4af1b was 4e4af1b, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-14T08:48:46Z

Remove some old Passport stuff, this is all in soap.[ch] now.

  • Property mode set to 100644
File size: 16.9 KB
RevLine 
[4e4af1b]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*/
[e5a8118]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"
[523fb23]35#include "sha1.h"
[e5a8118]36#include "base64.h"
37#include "xmltree.h"
38#include <ctype.h>
39#include <errno.h>
40
[4e4af1b]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
[e5a8118]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
54{
55        void *data;
56        struct im_connection *ic;
57        int ttl;
58       
59        char *url, *action, *payload;
60        struct http_request *http_req;
61       
62        const struct xt_handler_entry *xml_parser;
63        msn_soap_func build_request, handle_response, free_data;
64};
65
[4e4af1b]66typedef int (*msn_soap_func) ( struct msn_soap_req_data * );
67
[e5a8118]68static int msn_soap_send_request( struct msn_soap_req_data *req );
69
70static int msn_soap_start( struct im_connection *ic,
71                    void *data,
72                    msn_soap_func build_request,
73                    const struct xt_handler_entry *xml_parser,
74                    msn_soap_func handle_response,
75                    msn_soap_func free_data )
76{
77        struct msn_soap_req_data *req = g_new0( struct msn_soap_req_data, 1 );
78       
79        req->ic = ic;
80        req->data = data;
81        req->xml_parser = xml_parser;
82        req->build_request = build_request;
83        req->handle_response = handle_response;
84        req->free_data = free_data;
85        req->ttl = 3;
86       
87        return msn_soap_send_request( req );
88}
89
90static void msn_soap_handle_response( struct http_request *http_req );
91
92static int msn_soap_send_request( struct msn_soap_req_data *soap_req )
93{
94        char *http_req;
[523fb23]95        char *soap_action = NULL;
[e5a8118]96        url_t url;
97       
98        soap_req->build_request( soap_req );
99       
[523fb23]100        if( soap_req->action )
101                soap_action = g_strdup_printf( "SOAPAction: \"%s\"\r\n", soap_req->action );
[7db65b7]102       
[e5a8118]103        url_set( &url, soap_req->url );
104        http_req = g_strdup_printf( SOAP_HTTP_REQUEST, url.file, url.host,
[523fb23]105                soap_action ? soap_action : "",
[7db65b7]106                strlen( soap_req->payload ), soap_req->payload );
[e5a8118]107       
108        soap_req->http_req = http_dorequest( url.host, url.port, url.proto == PROTO_HTTPS,
109                http_req, msn_soap_handle_response, soap_req );
110       
[ffb6dea]111        g_free( http_req );
[523fb23]112        g_free( soap_action );
[ffb6dea]113       
[e5a8118]114        return soap_req->http_req != NULL;
115}
116
117static void msn_soap_handle_response( struct http_request *http_req )
118{
119        struct msn_soap_req_data *soap_req = http_req->data;
120        int st;
121       
122        if( http_req->body_size > 0 )
123        {
124                struct xt_parser *parser;
125               
126                parser = xt_new( soap_req->xml_parser, soap_req );
127                xt_feed( parser, http_req->reply_body, http_req->body_size );
128                xt_handle( parser, NULL, -1 );
129                xt_free( parser );
130        }
131       
132        st = soap_req->handle_response( soap_req );
133       
[ffb6dea]134        g_free( soap_req->url );
135        g_free( soap_req->action );
136        g_free( soap_req->payload );
137        soap_req->url = soap_req->action = soap_req->payload = NULL;
138       
[e5a8118]139        if( st == MSN_SOAP_RETRY && --soap_req->ttl )
140                msn_soap_send_request( soap_req );
141        else
142        {
143                soap_req->free_data( soap_req );
144                g_free( soap_req );
145        }
146}
147
[bc090f0]148
[523fb23]149/* passport_sso: Authentication MSNP15+ */
150
151struct msn_soap_passport_sso_data
152{
153        char *policy;
154        char *nonce;
155        char *secret;
156};
157
158static int msn_soap_passport_sso_build_request( struct msn_soap_req_data *soap_req )
159{
160        struct msn_soap_passport_sso_data *sd = soap_req->data;
161        struct im_connection *ic = soap_req->ic;
162       
163        soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL );
164        soap_req->payload = g_markup_printf_escaped( SOAP_PASSPORT_SSO_PAYLOAD,
165                ic->acc->user, ic->acc->pass, sd->policy );
166       
167        return MSN_SOAP_OK;
168}
169
170static xt_status msn_soap_passport_sso_token( struct xt_node *node, gpointer data )
171{
172        struct msn_soap_req_data *soap_req = data;
173        struct msn_soap_passport_sso_data *sd = soap_req->data;
174        struct msn_data *md = soap_req->ic->proto_data;
175        struct xt_node *p;
176        char *id;
177       
178        if( ( id = xt_find_attr( node, "Id" ) ) == NULL )
179                return XT_HANDLED;
180        id += strlen( id ) - 1;
181        if( *id == '1' &&
182            ( p = node->parent ) && ( p = p->parent ) &&
183            ( p = xt_find_node( p->children, "wst:RequestedProofToken" ) ) &&
184            ( p = xt_find_node( p->children, "wst:BinarySecret" ) ) &&
185            p->text )
186                sd->secret = g_strdup( p->text );
187       
[91d6e91]188        *id -= '1';
189        if( *id >= 0 && *id <= 2 )
190        {
191                g_free( md->tokens[(int)*id] );
192                md->tokens[(int)*id] = g_strdup( node->text );
193        }
[523fb23]194       
195        return XT_HANDLED;
196}
197
198static const struct xt_handler_entry msn_soap_passport_sso_parser[] = {
199        { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token },
200        { NULL, NULL, NULL }
201};
202
203static char *msn_key_fuckery( char *key, int key_len, char *type )
204{
205        unsigned char hash1[20+strlen(type)+1];
206        unsigned char hash2[20];
207        char *ret;
208       
209        sha1_hmac( key, key_len, type, 0, hash1 );
210        strcpy( (char*) hash1 + 20, type );
211        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash2 );
212       
213        /* This is okay as hash1 is read completely before it's overwritten. */
214        sha1_hmac( key, key_len, (char*) hash1, 20, hash1 );
215        sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash1 );
216       
217        ret = g_malloc( 24 );
218        memcpy( ret, hash2, 20 );
219        memcpy( ret + 20, hash1, 4 );
220        return ret;
221}
222
223static int msn_soap_passport_sso_handle_response( struct msn_soap_req_data *soap_req )
224{
225        struct msn_soap_passport_sso_data *sd = soap_req->data;
226        struct im_connection *ic = soap_req->ic;
227        char *key1, *key2, *key3, *blurb64;
228        int key1_len;
229        unsigned char *padnonce, *des3res;
230        struct
231        {
232                unsigned int uStructHeaderSize; // 28. Does not count data
233                unsigned int uCryptMode; // CRYPT_MODE_CBC (1)
234                unsigned int uCipherType; // TripleDES (0x6603)
235                unsigned int uHashType; // SHA1 (0x8004)
236                unsigned int uIVLen;    // 8
237                unsigned int uHashLen;  // 20
238                unsigned int uCipherLen; // 72
239                unsigned char iv[8];
240                unsigned char hash[20];
241                unsigned char cipherbytes[72];
242        } blurb = {
243                GUINT32_TO_LE( 28 ),
244                GUINT32_TO_LE( 1 ),
245                GUINT32_TO_LE( 0x6603 ),
246                GUINT32_TO_LE( 0x8004 ),
247                GUINT32_TO_LE( 8 ),
248                GUINT32_TO_LE( 20 ),
249                GUINT32_TO_LE( 72 ),
250        };
251
252        key1_len = base64_decode( sd->secret, (unsigned char**) &key1 );
253       
254        key2 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY HASH" );
255        key3 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY ENCRYPTION" );
256       
257        sha1_hmac( key2, 24, sd->nonce, 0, blurb.hash );
258        padnonce = g_malloc( strlen( sd->nonce ) + 8 );
259        strcpy( (char*) padnonce, sd->nonce );
260        memset( padnonce + strlen( sd->nonce ), 8, 8 );
261       
262        random_bytes( blurb.iv, 8 );
263       
264        ssl_des3_encrypt( (unsigned char*) key3, 24, padnonce, strlen( sd->nonce ) + 8, blurb.iv, &des3res );
265        memcpy( blurb.cipherbytes, des3res, 72 );
266       
267        blurb64 = base64_encode( (unsigned char*) &blurb, sizeof( blurb ) );
268        msn_auth_got_passport_token( ic, blurb64 );
269       
270        g_free( padnonce );
271        g_free( blurb64 );
272        g_free( des3res );
273        g_free( key1 );
274        g_free( key2 );
275        g_free( key3 );
276       
277        return MSN_SOAP_OK;
278}
279
280static int msn_soap_passport_sso_free_data( struct msn_soap_req_data *soap_req )
281{
282        struct msn_soap_passport_sso_data *sd = soap_req->data;
283       
284        g_free( sd->policy );
285        g_free( sd->nonce );
286        g_free( sd->secret );
287       
288        return MSN_SOAP_OK;
289}
290
291int msn_soap_passport_sso_request( struct im_connection *ic, const char *policy, const char *nonce )
292{
293        struct msn_soap_passport_sso_data *sd = g_new0( struct msn_soap_passport_sso_data, 1 );
294       
295        sd->policy = g_strdup( policy );
296        sd->nonce = g_strdup( nonce );
297       
298        return msn_soap_start( ic, sd, msn_soap_passport_sso_build_request,
299                                       msn_soap_passport_sso_parser,
300                                       msn_soap_passport_sso_handle_response,
301                                       msn_soap_passport_sso_free_data );
302}
303
304
[bc090f0]305/* oim_send: Sending offline messages */
306
307struct msn_soap_oim_send_data
308{
309        char *to;
310        char *msg;
311        int number;
312        int need_retry;
313};
314
[e5a8118]315static int msn_soap_oim_build_request( struct msn_soap_req_data *soap_req )
316{
317        struct msn_soap_oim_send_data *oim = soap_req->data;
318        struct im_connection *ic = soap_req->ic;
319        struct msn_data *md = ic->proto_data;
320        char *display_name_b64;
321       
[91d6e91]322        display_name_b64 = tobase64( set_getstr( &ic->acc->set, "display_name" ) );
[e5a8118]323       
324        soap_req->url = g_strdup( SOAP_OIM_SEND_URL );
[7db65b7]325        soap_req->action = g_strdup( SOAP_OIM_SEND_ACTION );
[e5a8118]326        soap_req->payload = g_markup_printf_escaped( SOAP_OIM_SEND_PAYLOAD,
[91d6e91]327                ic->acc->user, display_name_b64, MSNP_VER, MSNP_BUILD,
328                oim->to, md->tokens[2],
[5fecede]329                MSNP11_PROD_ID, md->lock_key ? md->lock_key : "",
330                oim->number, oim->number, oim->msg );
[e5a8118]331       
332        g_free( display_name_b64 );
333       
[523fb23]334        return MSN_SOAP_OK;
[e5a8118]335}
336
337static xt_status msn_soap_oim_send_challenge( struct xt_node *node, gpointer data )
338{
339        struct msn_soap_req_data *soap_req = data;
340        struct msn_soap_oim_send_data *oim = soap_req->data;
341        struct im_connection *ic = soap_req->ic;
342        struct msn_data *md = ic->proto_data;
343       
344        g_free( md->lock_key );
345        md->lock_key = msn_p11_challenge( node->text );
346       
347        oim->need_retry = 1;
348       
349        return XT_HANDLED;
350}
351
352static const struct xt_handler_entry msn_soap_oim_send_parser[] = {
353        { "LockKeyChallenge", "detail", msn_soap_oim_send_challenge },
354        { NULL,               NULL,     NULL                        }
355};
356
357static int msn_soap_oim_handle_response( struct msn_soap_req_data *soap_req )
358{
359        struct msn_soap_oim_send_data *oim = soap_req->data;
360       
[bc090f0]361        if( soap_req->http_req->status_code == 500 && oim->need_retry && soap_req->ttl > 0 )
[e5a8118]362        {
363                oim->need_retry = 0;
364                return MSN_SOAP_RETRY;
365        }
366        else if( soap_req->http_req->status_code == 200 )
[bc090f0]367        {
368                imcb_log( soap_req->ic, "Offline message successfully delivered to %s", oim->to );
[e5a8118]369                return MSN_SOAP_OK;
[bc090f0]370        }
[e5a8118]371        else
[bc090f0]372        {
373                imcb_log( soap_req->ic, "Failed to deliver offline message to %s:\n%s", oim->to, oim->msg );
[e5a8118]374                return MSN_SOAP_ABORT;
[bc090f0]375        }
[e5a8118]376}
377
378static int msn_soap_oim_free_data( struct msn_soap_req_data *soap_req )
379{
380        struct msn_soap_oim_send_data *oim = soap_req->data;
381       
382        g_free( oim->to );
383        g_free( oim->msg );
384        g_free( oim );
385       
[523fb23]386        return MSN_SOAP_OK;
[e5a8118]387}
388
389int msn_soap_oim_send( struct im_connection *ic, const char *to, const char *msg )
390{
391        struct msn_soap_oim_send_data *data;
392       
393        data = g_new0( struct msn_soap_oim_send_data, 1 );
394        data->to = g_strdup( to );
395        data->msg = tobase64( msg );
396        data->number = 1;
397       
398        return msn_soap_start( ic, data, msn_soap_oim_build_request,
399                                         msn_soap_oim_send_parser,
400                                         msn_soap_oim_handle_response,
401                                         msn_soap_oim_free_data );
402}
[bc090f0]403
404int msn_soap_oim_send_queue( struct im_connection *ic, GSList **msgq )
405{
406        GSList *l;
407        char *n = NULL;
408       
409        for( l = *msgq; l; l = l->next )
410        {
411                struct msn_message *m = l->data;
412               
413                if( n == NULL )
414                        n = m->who;
415                if( strcmp( n, m->who ) == 0 )
416                        msn_soap_oim_send( ic, m->who, m->text );
417        }
418       
419        while( *msgq != NULL )
420        {
421                struct msn_message *m = (*msgq)->data;
422               
423                g_free( m->who );
424                g_free( m->text );
425                g_free( m );
426               
427                *msgq = g_slist_remove( *msgq, m );
428        }
[523fb23]429       
430        return 1;
[bc090f0]431}
[7db65b7]432
433
434/* memlist: Fetching the membership list (NOT address book) */
435
436static int msn_soap_memlist_build_request( struct msn_soap_req_data *soap_req )
437{
[7f34ce2]438        struct msn_data *md = soap_req->ic->proto_data;
439       
[7db65b7]440        soap_req->url = g_strdup( SOAP_MEMLIST_URL );
441        soap_req->action = g_strdup( SOAP_MEMLIST_ACTION );
[7f34ce2]442        soap_req->payload = g_markup_printf_escaped( SOAP_MEMLIST_PAYLOAD, md->tokens[1] );
[7db65b7]443       
444        return 1;
445}
446
[7f34ce2]447static xt_status msn_soap_memlist_member( struct xt_node *node, gpointer data )
448{
449        bee_user_t *bu;
450        struct msn_buddy_data *bd;
451        struct xt_node *p;
452        char *role = NULL, *handle = NULL;
453        struct msn_soap_req_data *soap_req = data;
454        struct im_connection *ic = soap_req->ic;
455       
456        if( ( p = node->parent ) && ( p = p->parent ) &&
457            ( p = xt_find_node( p->children, "MemberRole" ) ) )
458                role = p->text;
459       
460        if( ( p = xt_find_node( node->children, "PassportName" ) ) )
461                handle = p->text;
462       
463        if( !role || !handle || 
464            !( ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) ||
465               ( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) )
466                return XT_HANDLED;
467       
468        bd = bu->data;
469        if( strcmp( role, "Allow" ) == 0 )
470                bd->flags |= MSN_BUDDY_AL;
471        else if( strcmp( role, "Block" ) == 0 )
472                bd->flags |= MSN_BUDDY_BL;
473        else if( strcmp( role, "Reverse" ) == 0 )
474                bd->flags |= MSN_BUDDY_RL;
475        else if( strcmp( role, "Pending" ) == 0 )
476                bd->flags |= MSN_BUDDY_PL;
477       
478        return XT_HANDLED;
479}
480
[7db65b7]481static const struct xt_handler_entry msn_soap_memlist_parser[] = {
[7f34ce2]482        { "Member", "Members", msn_soap_memlist_member },
[7db65b7]483        { NULL,               NULL,     NULL                        }
484};
485
486static int msn_soap_memlist_handle_response( struct msn_soap_req_data *soap_req )
487{
[7f34ce2]488        msn_soap_addressbook_request( soap_req->ic );
489       
490        return MSN_SOAP_OK;
[7db65b7]491}
492
493static int msn_soap_memlist_free_data( struct msn_soap_req_data *soap_req )
494{
495        return 0;
496}
497
498int msn_soap_memlist_request( struct im_connection *ic )
499{
500        return msn_soap_start( ic, NULL, msn_soap_memlist_build_request,
501                                         msn_soap_memlist_parser,
502                                         msn_soap_memlist_handle_response,
503                                         msn_soap_memlist_free_data );
504}
[7f34ce2]505
506
507/* addressbook: Fetching the membership list (NOT address book) */
508
509static int msn_soap_addressbook_build_request( struct msn_soap_req_data *soap_req )
510{
511        struct msn_data *md = soap_req->ic->proto_data;
512       
513        soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
514        soap_req->action = g_strdup( SOAP_ADDRESSBOOK_ACTION );
515        soap_req->payload = g_markup_printf_escaped( SOAP_ADDRESSBOOK_PAYLOAD, md->tokens[1] );
516       
517        return 1;
518}
519
520static xt_status msn_soap_addressbook_group( struct xt_node *node, gpointer data )
521{
522        struct xt_node *p;
523        char *id = NULL, *name = NULL;
524        struct msn_soap_req_data *soap_req = data;
525        struct im_connection *ic = soap_req->ic;
526       
527        if( ( p = node->parent ) &&
528            ( p = xt_find_node( p->children, "groupId" ) ) )
529                id = p->text;
530       
531        if( ( p = xt_find_node( node->children, "name" ) ) )
532                name = p->text;
533       
534        printf( "%s %s\n", id, name );
535       
536        return XT_HANDLED;
537}
538
539static xt_status msn_soap_addressbook_contact( struct xt_node *node, gpointer data )
540{
541        bee_user_t *bu;
542        struct msn_buddy_data *bd;
543        struct xt_node *p;
544        char *id = NULL, *type = NULL, *handle = NULL, *display_name = NULL;
545        struct msn_soap_req_data *soap_req = data;
546        struct im_connection *ic = soap_req->ic;
547       
548        if( ( p = node->parent ) &&
549            ( p = xt_find_node( p->children, "contactId" ) ) )
550                id = p->text;
551        if( ( p = xt_find_node( node->children, "contactType" ) ) )
552                type = p->text;
553        if( ( p = xt_find_node( node->children, "passportName" ) ) )
554                handle = p->text;
555        if( ( p = xt_find_node( node->children, "displayName" ) ) )
556                display_name = p->text;
557       
558        if( type && g_strcasecmp( type, "me" ) == 0 )
559        {
560                set_t *set = set_find( &ic->acc->set, "display_name" );
561                g_free( set->value );
562                set->value = g_strdup( display_name );
563               
564                return XT_HANDLED;
565        }
566       
567        if( !( bu = bee_user_by_handle( ic->bee, ic, handle ) ) &&
568            !( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) )
569                return XT_HANDLED;
570       
571        bd = bu->data;
572        bd->flags |= MSN_BUDDY_FL;
573        g_free( bd->cid );
574        bd->cid = g_strdup( id );
575       
576        imcb_rename_buddy( ic, handle, display_name );
577       
578        printf( "%s %s %s %s\n", id, type, handle, display_name );
579       
580        return XT_HANDLED;
581}
582
583static const struct xt_handler_entry msn_soap_addressbook_parser[] = {
584        { "contactInfo", "Contact", msn_soap_addressbook_contact },
585        { "groupInfo", "Group", msn_soap_addressbook_group },
586        { NULL,               NULL,     NULL                        }
587};
588
589static int msn_soap_addressbook_handle_response( struct msn_soap_req_data *soap_req )
590{
[ca7de3a]591        msn_auth_got_contact_list( soap_req->ic );
[7f34ce2]592        return MSN_SOAP_OK;
593}
594
595static int msn_soap_addressbook_free_data( struct msn_soap_req_data *soap_req )
596{
597        return 0;
598}
599
600int msn_soap_addressbook_request( struct im_connection *ic )
601{
602        return msn_soap_start( ic, NULL, msn_soap_addressbook_build_request,
603                                         msn_soap_addressbook_parser,
604                                         msn_soap_addressbook_handle_response,
605                                         msn_soap_addressbook_free_data );
606}
Note: See TracBrowser for help on using the repository browser.