source: protocols/msn/soap.c @ 91d6e91

Last change on this file since 91d6e91 was 91d6e91, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-13T09:12:31Z

Sending offline messages works now ... but Pidgin doesn't seem to receive them. :-/

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