source: protocols/msn/soap.c @ ca7de3a

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

Successful login (including contact list sync). \o/

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