diff options
-rw-r--r-- | lib/misc.c | 7 | ||||
-rw-r--r-- | lib/sha1.c | 47 | ||||
-rw-r--r-- | lib/sha1.h | 1 | ||||
-rw-r--r-- | lib/ssl_client.h | 2 | ||||
-rw-r--r-- | lib/ssl_gnutls.c | 4 | ||||
-rw-r--r-- | lib/ssl_openssl.c | 30 | ||||
-rw-r--r-- | lib/xmltree.c | 50 | ||||
-rw-r--r-- | lib/xmltree.h | 4 | ||||
-rw-r--r-- | protocols/bee.h | 1 | ||||
-rw-r--r-- | protocols/bee_user.c | 29 | ||||
-rw-r--r-- | protocols/msn/Makefile | 4 | ||||
-rw-r--r-- | protocols/msn/msn.c | 76 | ||||
-rw-r--r-- | protocols/msn/msn.h | 47 | ||||
-rw-r--r-- | protocols/msn/msn_util.c | 137 | ||||
-rw-r--r-- | protocols/msn/ns.c | 371 | ||||
-rw-r--r-- | protocols/msn/passport.c | 172 | ||||
-rw-r--r-- | protocols/msn/passport.h | 113 | ||||
-rw-r--r-- | protocols/msn/sb.c | 29 | ||||
-rw-r--r-- | protocols/msn/soap.c | 644 | ||||
-rw-r--r-- | protocols/msn/soap.h | 258 | ||||
-rw-r--r-- | protocols/msn/tables.c | 2 | ||||
-rw-r--r-- | protocols/nogaim.c | 16 | ||||
-rw-r--r-- | protocols/nogaim.h | 2 | ||||
-rw-r--r-- | storage_xml.c | 10 |
24 files changed, 1450 insertions, 606 deletions
@@ -297,11 +297,10 @@ void http_decode( char *s ) /* This fuction is safe, but make sure you call it safely as well! */ void http_encode( char *s ) { - char *t; + char t[strlen(s)+1]; int i, j; - t = g_strdup( s ); - + strcpy( t, s ); for( i = j = 0; t[i]; i ++, j ++ ) { /* Warning: isalnum() is locale-aware, so don't use it here! */ @@ -319,8 +318,6 @@ void http_encode( char *s ) } } s[j] = 0; - - g_free( t ); } /* Strip newlines from a string. Modifies the string passed to it. */ @@ -35,6 +35,7 @@ * */ +#include <string.h> #include "sha1.h" /* @@ -373,3 +374,49 @@ static void sha1_pad(sha1_state_t * context) sha1_process_block(context); } + +#define HMAC_BLOCK_SIZE 64 + +/* BitlBee addition: */ +void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, uint8_t Message_Digest[sha1_hash_size]) +{ + sha1_state_t sha1; + uint8_t hash[sha1_hash_size]; + uint8_t key[HMAC_BLOCK_SIZE+1]; + int i; + + if( key_len == 0 ) + key_len = strlen( key_ ); + if( payload_len == 0 ) + payload_len = strlen( payload ); + + /* Create K. If our current key is >64 chars we have to hash it, + otherwise just pad. */ + memset( key, 0, HMAC_BLOCK_SIZE + 1 ); + if( key_len > HMAC_BLOCK_SIZE ) + { + sha1_init( &sha1 ); + sha1_append( &sha1, (uint8_t*) key_, key_len ); + sha1_finish( &sha1, key ); + } + else + { + memcpy( key, key_, key_len ); + } + + /* Inner part: H(K XOR 0x36, text) */ + sha1_init( &sha1 ); + for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) + key[i] ^= 0x36; + sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); + sha1_append( &sha1, (const uint8_t*) payload, payload_len ); + sha1_finish( &sha1, hash ); + + /* Final result: H(K XOR 0x5C, inner stuff) */ + sha1_init( &sha1 ); + for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) + key[i] ^= 0x36 ^ 0x5c; + sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); + sha1_append( &sha1, hash, sha1_hash_size ); + sha1_finish( &sha1, Message_Digest ); +} @@ -66,5 +66,6 @@ typedef struct SHA1Context { G_MODULE_EXPORT int sha1_init(sha1_state_t *); G_MODULE_EXPORT int sha1_append(sha1_state_t *, const uint8_t *, unsigned int); G_MODULE_EXPORT int sha1_finish(sha1_state_t *, uint8_t Message_Digest[sha1_hash_size]); +G_MODULE_EXPORT void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, uint8_t Message_Digest[sha1_hash_size]); #endif diff --git a/lib/ssl_client.h b/lib/ssl_client.h index 0a8e82d8..787d528a 100644 --- a/lib/ssl_client.h +++ b/lib/ssl_client.h @@ -77,3 +77,5 @@ G_MODULE_EXPORT int ssl_getfd( void *conn ); adding an event handler to the queue. (And it should perform exactly the same action as the handler that just received the SSL_AGAIN.) */ G_MODULE_EXPORT b_input_condition ssl_getdirection( void *conn ); + +G_MODULE_EXPORT size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res); diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index 5a14b825..15c23dbb 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -188,6 +188,8 @@ int ssl_read( void *conn, char *buf, int len ) if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) ssl_errno = SSL_AGAIN; + if( getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st ); + return st; } @@ -207,6 +209,8 @@ int ssl_write( void *conn, const char *buf, int len ) if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) ssl_errno = SSL_AGAIN; + if( getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st ); + return st; } diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c index 8abff390..0feed4ca 100644 --- a/lib/ssl_openssl.c +++ b/lib/ssl_openssl.c @@ -115,7 +115,9 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con if( !initialized ) { initialized = TRUE; - SSLeay_add_ssl_algorithms(); + SSL_library_init(); + //SSLeay_add_ssl_algorithms(); + //OpenSSL_add_all_algorithms(); } meth = TLSv1_client_method(); @@ -204,6 +206,8 @@ int ssl_read( void *conn, char *buf, int len ) ssl_errno = SSL_AGAIN; } + if( getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st ); + return st; } @@ -219,6 +223,8 @@ int ssl_write( void *conn, const char *buf, int len ) st = SSL_write( ((struct scd*)conn)->ssl, buf, len ); + if( getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st ); + ssl_errno = SSL_OK; if( st <= 0 ) { @@ -271,3 +277,25 @@ b_input_condition ssl_getdirection( void *conn ) { return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ ); } + +size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res) +{ + int output_length = 0; + EVP_CIPHER_CTX ctx; + + *res = g_new0(unsigned char, 72); + + /* Don't set key or IV because we will modify the parameters */ + EVP_CIPHER_CTX_init(&ctx); + EVP_CipherInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, NULL, NULL, 1); + EVP_CIPHER_CTX_set_key_length(&ctx, key_len); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + /* We finished modifying parameters so now we can set key and IV */ + EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, 1); + EVP_CipherUpdate(&ctx, *res, &output_length, input, input_len); + EVP_CipherFinal_ex(&ctx, *res, &output_length); + EVP_CIPHER_CTX_cleanup(&ctx); + //EVP_cleanup(); + + return output_length; +} diff --git a/lib/xmltree.c b/lib/xmltree.c index b0a945ce..5fd43014 100644 --- a/lib/xmltree.c +++ b/lib/xmltree.c @@ -140,7 +140,7 @@ void xt_reset( struct xt_parser *xt ) /* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on end-of-stream and 1 otherwise. */ -int xt_feed( struct xt_parser *xt, char *text, int text_len ) +int xt_feed( struct xt_parser *xt, const char *text, int text_len ) { if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) ) { @@ -173,20 +173,20 @@ int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth ) if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) ) { - for( i = 0; xt->handlers[i].func; i ++ ) + if( xt->handlers ) for( i = 0; xt->handlers[i].func; i ++ ) { /* This one is fun! \o/ */ - /* If handler.name == NULL it means it should always match. */ + /* If handler.name == NULL it means it should always match. */ if( ( xt->handlers[i].name == NULL || - /* If it's not, compare. There should always be a name. */ + /* If it's not, compare. There should always be a name. */ g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) && - /* If handler.parent == NULL, it's a match. */ + /* If handler.parent == NULL, it's a match. */ ( xt->handlers[i].parent == NULL || - /* If there's a parent node, see if the name matches. */ + /* If there's a parent node, see if the name matches. */ ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : - /* If there's no parent, the handler should mention <root> as a parent. */ - g_strcasecmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) ) + /* If there's no parent, the handler should mention <root> as a parent. */ + strcmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) ) { st = xt->handlers[i].func( node, xt->data ); @@ -259,6 +259,20 @@ void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth ) } } +struct xt_node *xt_from_string( const char *in ) +{ + struct xt_parser *parser; + struct xt_node *ret; + + parser = xt_new( NULL, NULL ); + xt_feed( parser, in, strlen( in ) ); + ret = parser->root; + parser->root = NULL; + xt_free( parser ); + + return ret; +} + static void xt_to_string_real( struct xt_node *node, GString *str ) { char *buf; @@ -549,6 +563,26 @@ void xt_add_child( struct xt_node *parent, struct xt_node *child ) } } +/* Same, but at the beginning. */ +void xt_insert_child( struct xt_node *parent, struct xt_node *child ) +{ + struct xt_node *node, *last; + + for( node = child; node; node = node->next ) + { + if( node->parent != NULL ) + { + /* ERROR CONDITION: They seem to have a parent already??? */ + } + + node->parent = parent; + last = node; + } + + last->next = parent->children; + parent->children = child; +} + void xt_add_attr( struct xt_node *node, const char *key, const char *value ) { int i; diff --git a/lib/xmltree.h b/lib/xmltree.h index 34e3be68..c1697ff5 100644 --- a/lib/xmltree.h +++ b/lib/xmltree.h @@ -78,9 +78,10 @@ struct xt_parser struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data ); void xt_reset( struct xt_parser *xt ); -int xt_feed( struct xt_parser *xt, char *text, int text_len ); +int xt_feed( struct xt_parser *xt, const char *text, int text_len ); int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth ); void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth ); +struct xt_node *xt_from_string( const char *in ); char *xt_to_string( struct xt_node *node ); void xt_print( struct xt_node *node ); struct xt_node *xt_dup( struct xt_node *node ); @@ -91,6 +92,7 @@ char *xt_find_attr( struct xt_node *node, const char *key ); struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children ); void xt_add_child( struct xt_node *parent, struct xt_node *child ); +void xt_insert_child( struct xt_node *parent, struct xt_node *child ); void xt_add_attr( struct xt_node *node, const char *key, const char *value ); int xt_remove_attr( struct xt_node *node, const char *key ); diff --git a/protocols/bee.h b/protocols/bee.h index 5792e988..6e3cf563 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -147,6 +147,7 @@ void bee_group_free( bee_t *bee ); * OPT_LOGGED_IN and OPT_AWAY. * - 'state' and 'message' can be NULL */ G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message ); +G_MODULE_EXPORT void imcb_buddy_status_msg( struct im_connection *ic, const char *handle, const char *message ); G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ); /* Call when a handle says something. 'flags' and 'sent_at may be just 0. */ G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, guint32 flags, time_t sent_at ); diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 5acd5b83..86f87f86 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -186,8 +186,13 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, /* TODO(wilmer): OPT_AWAY, or just state == NULL ? */ bu->flags = flags; - bu->status = g_strdup( ( flags & OPT_AWAY ) && state == NULL ? "Away" : state ); bu->status_msg = g_strdup( message ); + if( state && *state ) + bu->status = g_strdup( state ); + else if( flags & OPT_AWAY ) + bu->status = g_strdup( "Away" ); + else + bu->status = NULL; if( bee->ui->user_status ) bee->ui->user_status( bee, bu, old ); @@ -197,6 +202,28 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, g_free( old ); } +/* Same, but only change the away/status message, not any away/online state info. */ +void imcb_buddy_status_msg( struct im_connection *ic, const char *handle, const char *message ) +{ + bee_t *bee = ic->bee; + bee_user_t *bu, *old; + + if( !( bu = bee_user_by_handle( bee, ic, handle ) ) ) + { + return; + } + + old = g_memdup( bu, sizeof( bee_user_t ) ); + + bu->status_msg = g_strdup( message ); + + if( bee->ui->user_status ) + bee->ui->user_status( bee, bu, old ); + + g_free( old->status_msg ); + g_free( old ); +} + void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle ) { bee_t *bee = ic->bee; diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 781482f5..a14225d1 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -12,7 +12,7 @@ SRCDIR := $(SRCDIR)protocols/msn/ endif # [SH] Program variables -objects = msn.o msn_util.o ns.o passport.o sb.o tables.o +objects = msn.o msn_util.o ns.o sb.o soap.o tables.o LFLAGS += -r @@ -42,4 +42,4 @@ msn_mod.o: $(objects) @echo '*' Linking msn_mod.o @$(LD) $(LFLAGS) $(objects) -o msn_mod.o - +soap.o: soap.h soap.c diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 60d58532..ed0881d1 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* MSN module - Main file; functions to be called from BitlBee */ @@ -24,6 +24,7 @@ */ #include "nogaim.h" +#include "soap.h" #include "msn.h" int msn_chat_id; @@ -34,10 +35,15 @@ static char *set_eval_display_name( set_t *set, char *value ); static void msn_init( account_t *acc ) { - set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc ); - set_add( &acc->set, "local_display_name", "false", set_eval_bool, acc ); + set_t *s; + + s = set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc ); + s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY; + set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); set_add( &acc->set, "switchboard_keepalives", "false", set_eval_bool, acc ); + + acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE; } static void msn_login( account_t *acc ) @@ -67,6 +73,7 @@ static void msn_login( account_t *acc ) md->ic = ic; md->away_state = msn_away_state_list; + md->domaintree = g_tree_new( msn_domaintree_cmp ); msn_connections = g_slist_append( msn_connections, ic ); } @@ -102,6 +109,13 @@ static void msn_logout( struct im_connection *ic ) while( md->groupcount > 0 ) g_free( md->grouplist[--md->groupcount] ); g_free( md->grouplist ); + g_free( md->tokens[0] ); + g_free( md->tokens[1] ); + g_free( md->tokens[2] ); + g_free( md->lock_key ); + + g_tree_destroy( md->domaintree ); + md->domaintree = NULL; while( md->grpq ) { @@ -173,6 +187,7 @@ static GList *msn_away_states( struct im_connection *ic ) static void msn_set_away( struct im_connection *ic, char *state, char *message ) { char buf[1024]; + char *uux; struct msn_data *md = ic->proto_data; if( state == NULL ) @@ -181,12 +196,14 @@ static void msn_set_away( struct im_connection *ic, char *state, char *message ) md->away_state = msn_away_state_list + 1; g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, md->away_state->code ); - msn_write( ic, buf, strlen( buf ) ); -} - -static void msn_set_my_name( struct im_connection *ic, char *info ) -{ - msn_set_display_name( ic, info ); + if( !msn_write( ic, buf, strlen( buf ) ) ) + return; + + uux = g_markup_printf_escaped( "<Data><PSM>%s</PSM><CurrentMedia></CurrentMedia>" + "</Data>", message ? message : "" ); + g_snprintf( buf, sizeof( buf ), "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux ); + if( !msn_write( ic, buf, strlen( buf ) ) ) + return; } static void msn_get_info(struct im_connection *ic, char *who) @@ -313,10 +330,9 @@ static char *set_eval_display_name( set_t *set, char *value ) { account_t *acc = set->data; struct im_connection *ic = acc->ic; - - /* Allow any name if we're offline. */ - if( ic == NULL ) - return value; + struct msn_data *md = ic->proto_data; + char buf[512]; + char *fn; if( strlen( value ) > 129 ) { @@ -324,10 +340,32 @@ static char *set_eval_display_name( set_t *set, char *value ) return NULL; } - /* Returning NULL would be better, because the server still has to - confirm the name change. However, it looks a bit confusing to the - user. */ - return msn_set_display_name( ic, value ) ? value : NULL; + msn_soap_addressbook_set_display_name( ic, value ); + + fn = g_malloc( strlen( value ) * 3 + 1 ); + strcpy( fn, value ); + http_encode( fn ); + g_snprintf( buf, sizeof( buf ), "PRP %d MFN %s\r\n", + ++md->trId, fn ); + g_free( fn ); + + /* Note: We don't actually know if the server accepted the new name, + and won't give proper feedback yet if it doesn't. */ + return msn_write( ic, buf, strlen( buf ) ) ? value : NULL; +} + +static void msn_buddy_data_add( bee_user_t *bu ) +{ + struct msn_data *md = bu->ic->proto_data; + bu->data = g_new0( struct msn_buddy_data, 1 ); + g_tree_insert( md->domaintree, bu->handle, bu ); +} + +static void msn_buddy_data_free( bee_user_t *bu ) +{ + struct msn_data *md = bu->ic->proto_data; + g_tree_remove( md->domaintree, bu->handle ); + g_free( bu->data ); } void msn_initmodule() @@ -342,7 +380,6 @@ void msn_initmodule() ret->away_states = msn_away_states; ret->set_away = msn_set_away; ret->get_info = msn_get_info; - ret->set_my_name = msn_set_my_name; ret->add_buddy = msn_add_buddy; ret->remove_buddy = msn_remove_buddy; ret->chat_msg = msn_chat_msg; @@ -356,6 +393,9 @@ void msn_initmodule() ret->rem_deny = msn_rem_deny; ret->send_typing = msn_send_typing; ret->handle_cmp = g_strcasecmp; + ret->buddy_data_add = msn_buddy_data_add; + ret->buddy_data_free = msn_buddy_data_free; + //ret->transfer_request = msn_ftp_transfer_request; register_protocol(ret); diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index 31683cb5..1828cc8a 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* MSN module */ @@ -38,8 +38,23 @@ #define debug( text... ) #endif -#define QRY_NAME "msmsgs@msnmsgr.com" -#define QRY_CODE "Q1P7W2E4J9R8U3S5" +/* This should be MSN Messenger 7.0.0813 +#define MSNP11_PROD_KEY "CFHUR$52U_{VIX5T" +#define MSNP11_PROD_ID "PROD0101{0RM?UBW" +*/ + +/* Some other version. +#define MSNP11_PROD_KEY "O4BG@C7BWLYQX?5G" +#define MSNP11_PROD_ID "PROD01065C%ZFN6F" +*/ + +#define MSNP11_PROD_KEY "ILTXC!4IXB5FB*PX" +//PK}_A_0N_K%O?A9S" +#define MSNP11_PROD_ID "PROD0119GSJUC$18" +//PROD0114ES4Z%Q5W" +#define MSNP_VER "MSNP15" +#define MSNP_BUILD "8.5.1288" +//"8.1.0178" #define MSN_SB_NEW -24062002 @@ -68,6 +83,8 @@ struct msn_data struct msn_handler_data *handler; int trId; + char *tokens[3]; + char *lock_key; GSList *msgq, *grpq; GSList *switchboards; @@ -79,6 +96,7 @@ struct msn_data int buddycount; int groupcount; char **grouplist; + GTree *domaintree; }; struct msn_switchboard @@ -141,6 +159,21 @@ struct msn_handler_data int (*exec_message) ( gpointer data, char *msg, int msglen, char **cmd, int count ); }; +typedef enum +{ + MSN_BUDDY_FL = 1, /* Warning: FL,AL,BL *must* be 1,2,4. */ + MSN_BUDDY_AL = 2, + MSN_BUDDY_BL = 4, + MSN_BUDDY_RL = 8, + MSN_BUDDY_PL = 16, +} msn_buddy_flags_t; + +struct msn_buddy_data +{ + char *cid; + msn_buddy_flags_t flags; +}; + /* Bitfield values for msn_status_code.flags */ #define STATUS_FATAL 1 #define STATUS_SB_FATAL 2 @@ -161,6 +194,8 @@ extern GSList *msn_switchboards; /* ns.c */ gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ); +void msn_auth_got_passport_token( struct im_connection *ic, char *token ); +void msn_auth_got_contact_list( struct im_connection *ic ); /* msn_util.c */ int msn_write( struct im_connection *ic, char *s, int len ); @@ -171,9 +206,10 @@ void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname ); char *msn_findheader( char *text, char *header, int len ); char **msn_linesplit( char *line ); int msn_handler( struct msn_handler_data *h ); -char *msn_http_encode( const char *input ); void msn_msgq_purge( struct im_connection *ic, GSList **list ); gboolean msn_set_display_name( struct im_connection *ic, const char *rawname ); +char *msn_p11_challenge( char *challenge ); +gint msn_domaintree_cmp( gconstpointer a_, gconstpointer b_ ); /* tables.c */ const struct msn_away_state *msn_away_state_by_number( int number ); @@ -195,7 +231,4 @@ int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m ); void msn_sb_start_keepalives( struct msn_switchboard *sb, gboolean initial ); void msn_sb_stop_keepalives( struct msn_switchboard *sb ); -/* invitation.c */ -void msn_ftp_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who ); - #endif //_MSN_H diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index 23447403..bd1bea42 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* MSN module - Miscellaneous utilities */ @@ -25,6 +25,7 @@ #include "nogaim.h" #include "msn.h" +#include "md5.h" #include <ctype.h> int msn_write( struct im_connection *ic, char *s, int len ) @@ -32,6 +33,12 @@ int msn_write( struct im_connection *ic, char *s, int len ) struct msn_data *md = ic->proto_data; int st; + if( getenv( "BITLBEE_DEBUG" ) ) + { + write( 2, "->NS:", 5 ); + write( 2, s, len ); + } + st = write( md->fd, s, len ); if( st != len ) { @@ -53,7 +60,7 @@ int msn_logged_in( struct im_connection *ic ) int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group ) { struct msn_data *md = ic->proto_data; - char buf[1024], *realname, groupid[8]; + char buf[1024], realname[strlen(realname_)*3+1], groupid[8]; *groupid = '\0'; if( group ) @@ -86,9 +93,10 @@ int msn_buddy_list_add( struct im_connection *ic, const char *list, const char * if( l == NULL ) { - char *groupname = msn_http_encode( group ); + char groupname[strlen(group)+1]; + strcpy( groupname, group ); + http_encode( groupname ); g_snprintf( buf, sizeof( buf ), "ADG %d %s %d\r\n", ++md->trId, groupname, 0 ); - g_free( groupname ); return msn_write( ic, buf, strlen( buf ) ); } else @@ -101,9 +109,9 @@ int msn_buddy_list_add( struct im_connection *ic, const char *list, const char * } } - realname = msn_http_encode( realname_ ); + strcpy( realname, realname_ ); + http_encode( realname ); g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s%s\r\n", ++md->trId, list, who, realname, groupid ); - g_free( realname ); return msn_write( ic, buf, strlen( buf ) ); } @@ -279,6 +287,12 @@ int msn_handler( struct msn_handler_data *h ) if( st <= 0 ) return( -1 ); + if( getenv( "BITLBEE_DEBUG" ) ) + { + write( 2, "->C:", 4 ); + write( 2, h->rxq + h->rxlen - st, st ); + } + while( st ) { int i; @@ -366,32 +380,6 @@ int msn_handler( struct msn_handler_data *h ) return( 1 ); } -/* The difference between this function and the normal http_encode() function - is that this one escapes every 7-bit ASCII character because this is said - to avoid some lame server-side checks when setting a real-name. Also, - non-ASCII characters are not escaped because MSN servers don't seem to - appreciate that! */ -char *msn_http_encode( const char *input ) -{ - char *ret, *s; - int i; - - ret = s = g_new0( char, strlen( input ) * 3 + 1 ); - for( i = 0; input[i]; i ++ ) - if( input[i] & 128 ) - { - *s = input[i]; - s ++; - } - else - { - g_snprintf( s, 4, "%%%02X", input[i] ); - s += 3; - } - - return ret; -} - void msn_msgq_purge( struct im_connection *ic, GSList **list ) { struct msn_message *m; @@ -432,14 +420,85 @@ void msn_msgq_purge( struct im_connection *ic, GSList **list ) g_string_free( ret, TRUE ); } -gboolean msn_set_display_name( struct im_connection *ic, const char *rawname ) +/* Copied and heavily modified from http://tmsnc.sourceforge.net/chl.c */ +char *msn_p11_challenge( char *challenge ) { - char *fn = msn_http_encode( rawname ); - struct msn_data *md = ic->proto_data; - char buf[1024]; + char *output, buf[256]; + md5_state_t md5c; + unsigned char md5Hash[16], *newHash; + unsigned int *md5Parts, *chlStringParts, newHashParts[5]; + long long nHigh = 0, nLow = 0; + int i, n; + + /* Create the MD5 hash */ + md5_init(&md5c); + md5_append(&md5c, (unsigned char*) challenge, strlen(challenge)); + md5_append(&md5c, (unsigned char*) MSNP11_PROD_KEY, strlen(MSNP11_PROD_KEY)); + md5_finish(&md5c, md5Hash); + + /* Split it into four integers */ + md5Parts = (unsigned int *)md5Hash; + for (i = 0; i < 4; i ++) + { + md5Parts[i] = GUINT32_TO_LE(md5Parts[i]); + + /* & each integer with 0x7FFFFFFF */ + /* and save one unmodified array for later */ + newHashParts[i] = md5Parts[i]; + md5Parts[i] &= 0x7FFFFFFF; + } + + /* make a new string and pad with '0' */ + n = g_snprintf(buf, sizeof(buf)-5, "%s%s00000000", challenge, MSNP11_PROD_ID); + /* truncate at an 8-byte boundary */ + buf[n&=~7] = '\0'; + + /* split into integers */ + chlStringParts = (unsigned int *)buf; + + /* this is magic */ + for (i = 0; i < (n / 4) - 1; i += 2) + { + long long temp; + + chlStringParts[i] = GUINT32_TO_LE(chlStringParts[i]); + chlStringParts[i+1] = GUINT32_TO_LE(chlStringParts[i+1]); + + temp = (md5Parts[0] * (((0x0E79A9C1 * (long long)chlStringParts[i]) % 0x7FFFFFFF)+nHigh) + md5Parts[1])%0x7FFFFFFF; + nHigh = (md5Parts[2] * (((long long)chlStringParts[i+1]+temp) % 0x7FFFFFFF) + md5Parts[3]) % 0x7FFFFFFF; + nLow = nLow + nHigh + temp; + } + nHigh = (nHigh+md5Parts[1]) % 0x7FFFFFFF; + nLow = (nLow+md5Parts[3]) % 0x7FFFFFFF; - g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, ic->acc->user, fn ); - g_free( fn ); + newHashParts[0] ^= nHigh; + newHashParts[1] ^= nLow; + newHashParts[2] ^= nHigh; + newHashParts[3] ^= nLow; - return msn_write( ic, buf, strlen( buf ) ) != 0; + /* swap more bytes if big endian */ + for (i = 0; i < 4; i ++) + newHashParts[i] = GUINT32_TO_LE(newHashParts[i]); + + /* make a string of the parts */ + newHash = (unsigned char *)newHashParts; + + /* convert to hexadecimal */ + output = g_new(char, 33); + for (i = 0; i < 16; i ++) + sprintf(output + i * 2, "%02x", newHash[i]); + + return output; +} + +gint msn_domaintree_cmp( gconstpointer a_, gconstpointer b_ ) +{ + const char *a = a_, *b = b_; + gint ret; + + if( !( a = strchr( a, '@' ) ) || !( b = strchr( b, '@' ) ) || + ( ret = strcmp( a, b ) ) == 0 ) + ret = strcmp( a_, b_ ); + + return ret; } diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index 0be9e727..a1b88ae1 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* MSN module - Notification server callbacks */ @@ -26,15 +26,15 @@ #include <ctype.h> #include "nogaim.h" #include "msn.h" -#include "passport.h" #include "md5.h" +#include "soap.h" +#include "xmltree.h" static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition cond ); static int msn_ns_command( gpointer data, char **cmd, int num_parts ); static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ); -static void msn_auth_got_passport_token( struct msn_auth_data *mad ); -static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name ); +static void msn_ns_send_adl( struct im_connection *ic ); gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ) { @@ -72,7 +72,7 @@ gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ) md->handler->fd = md->fd; md->handler->rxq = g_new0( char, 1 ); - g_snprintf( s, sizeof( s ), "VER %d MSNP8 CVR0\r\n", ++md->trId ); + g_snprintf( s, sizeof( s ), "VER %d %s CVR0\r\n", ++md->trId, MSNP_VER ); if( msn_write( ic, s, strlen( s ) ) ) { ic->inpa = b_input_add( md->fd, B_EV_IO_READ, msn_ns_callback, ic ); @@ -112,7 +112,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) if( strcmp( cmd[0], "VER" ) == 0 ) { - if( cmd[2] && strncmp( cmd[2], "MSNP8", 5 ) != 0 ) + if( cmd[2] && strncmp( cmd[2], MSNP_VER, 5 ) != 0 ) { imcb_error( ic, "Unsupported protocol" ); imc_logout( ic, FALSE ); @@ -126,7 +126,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) else if( strcmp( cmd[0], "CVR" ) == 0 ) { /* We don't give a damn about the information we just received */ - g_snprintf( buf, sizeof( buf ), "USR %d TWN I %s\r\n", ++md->trId, ic->acc->user ); + g_snprintf( buf, sizeof( buf ), "USR %d SSO I %s\r\n", ++md->trId, ic->acc->user ); return( msn_write( ic, buf, strlen( buf ) ) ); } else if( strcmp( cmd[0], "XFR" ) == 0 ) @@ -134,7 +134,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) char *server; int port; - if( num_parts == 6 && strcmp( cmd[2], "NS" ) == 0 ) + if( num_parts >= 6 && strcmp( cmd[2], "NS" ) == 0 ) { b_event_remove( ic->inpa ); ic->inpa = 0; @@ -155,7 +155,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) md->fd = proxy_connect( server, port, msn_ns_connected, ic ); } - else if( num_parts == 6 && strcmp( cmd[2], "SB" ) == 0 ) + else if( num_parts >= 6 && strcmp( cmd[2], "SB" ) == 0 ) { struct msn_switchboard *sb; @@ -219,27 +219,15 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "USR" ) == 0 ) { - if( num_parts == 5 && strcmp( cmd[2], "TWN" ) == 0 && strcmp( cmd[3], "S" ) == 0 ) + if( num_parts >= 6 && strcmp( cmd[2], "SSO" ) == 0 && + strcmp( cmd[3], "S" ) == 0 ) { - /* Time for some Passport black magic... */ - if( !passport_get_token( msn_auth_got_passport_token, ic, ic->acc->user, ic->acc->pass, cmd[4] ) ) - { - imcb_error( ic, "Error while contacting Passport server" ); - imc_logout( ic, TRUE ); - return( 0 ); - } + msn_soap_passport_sso_request( ic, cmd[4], cmd[5] ); } - else if( num_parts >= 7 && strcmp( cmd[2], "OK" ) == 0 ) + else if( strcmp( cmd[2], "OK" ) == 0 ) { - if( num_parts == 7 ) - msn_ns_got_display_name( ic, cmd[4] ); - else - imcb_log( ic, "Warning: Friendly name in server response was corrupted" ); - imcb_log( ic, "Authenticated, getting buddy list" ); - - g_snprintf( buf, sizeof( buf ), "SYN %d 0\r\n", ++md->trId ); - return( msn_write( ic, buf, strlen( buf ) ) ); + msn_soap_memlist_request( ic ); } else { @@ -250,7 +238,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "MSG" ) == 0 ) { - if( num_parts != 4 ) + if( num_parts < 4 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); @@ -266,131 +254,51 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) return( 0 ); } } - else if( strcmp( cmd[0], "SYN" ) == 0 ) + else if( strcmp( cmd[0], "BLP" ) == 0 ) { - if( num_parts == 5 ) - { - int i, groupcount; - - groupcount = atoi( cmd[4] ); - if( groupcount > 0 ) - { - /* valgrind says this is leaking memory, I'm guessing - that this happens during server redirects. */ - if( md->grouplist ) - { - for( i = 0; i < md->groupcount; i ++ ) - g_free( md->grouplist[i] ); - g_free( md->grouplist ); - } - - md->groupcount = groupcount; - md->grouplist = g_new0( char *, md->groupcount ); - } - - md->buddycount = atoi( cmd[3] ); - if( !*cmd[3] || md->buddycount == 0 ) - msn_logged_in( ic ); - } - else - { - /* Hrrm... This SYN reply doesn't really look like something we expected. - Let's assume everything is okay. */ - - msn_logged_in( ic ); - } + msn_ns_send_adl( ic ); } - else if( strcmp( cmd[0], "LST" ) == 0 ) + else if( strcmp( cmd[0], "ADL" ) == 0 ) { - int list; - - if( num_parts != 4 && num_parts != 5 ) - { - imcb_error( ic, "Syntax error" ); - imc_logout( ic, TRUE ); - return( 0 ); - } - - http_decode( cmd[2] ); - list = atoi( cmd[3] ); - - if( list & 1 ) /* FL */ + if( num_parts >= 3 && strcmp( cmd[2], "OK" ) == 0 ) { - char *group = NULL; - int num; + char buf[1024]; + char *fn_raw = set_getstr( &ic->acc->set, "display_name" ); + char *fn; - if( cmd[4] != NULL && sscanf( cmd[4], "%d", &num ) == 1 && num < md->groupcount ) - group = md->grouplist[num]; + if( fn_raw == NULL ) + fn_raw = ic->acc->user; + fn = g_malloc( strlen( fn_raw ) * 3 + 1 ); + strcpy( fn, fn_raw ); + http_encode( fn ); - imcb_add_buddy( ic, cmd[1], group ); - imcb_rename_buddy( ic, cmd[1], cmd[2] ); - } - if( list & 2 ) /* AL */ - { - ic->permit = g_slist_append( ic->permit, g_strdup( cmd[1] ) ); - } - if( list & 4 ) /* BL */ - { - ic->deny = g_slist_append( ic->deny, g_strdup( cmd[1] ) ); - } - if( list & 8 ) /* RL */ - { - if( ( list & 6 ) == 0 ) - msn_buddy_ask( ic, cmd[1], cmd[2] ); - } - - if( --md->buddycount == 0 ) - { - if( ic->flags & OPT_LOGGED_IN ) - { - imcb_log( ic, "Successfully transferred to different server" ); - g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, md->away_state->code, 0 ); - return( msn_write( ic, buf, strlen( buf ) ) ); - } - else - { - msn_logged_in( ic ); - } + g_snprintf( buf, sizeof( buf ), "PRP %d MFN %s\r\n", + ++md->trId, fn ); + g_free( fn ); + + msn_write( ic, buf, strlen( buf ) ); } } - else if( strcmp( cmd[0], "LSG" ) == 0 ) + else if( strcmp( cmd[0], "PRP" ) == 0 ) { - int num; - - if( num_parts != 4 ) - { - imcb_error( ic, "Syntax error" ); - imc_logout( ic, TRUE ); - return( 0 ); - } - - http_decode( cmd[2] ); - num = atoi( cmd[1] ); - - if( num < md->groupcount ) - md->grouplist[num] = g_strdup( cmd[2] ); + imcb_connected( ic ); } else if( strcmp( cmd[0], "CHL" ) == 0 ) { - md5_state_t state; - md5_byte_t digest[16]; - int i; + char *resp; - if( num_parts != 3 ) + if( num_parts < 3 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } - md5_init( &state ); - md5_append( &state, (const md5_byte_t *) cmd[2], strlen( cmd[2] ) ); - md5_append( &state, (const md5_byte_t *) QRY_CODE, strlen( QRY_CODE ) ); - md5_finish( &state, digest ); - - g_snprintf( buf, sizeof( buf ), "QRY %d %s %d\r\n", ++md->trId, QRY_NAME, 32 ); - for( i = 0; i < 16; i ++ ) - g_snprintf( buf + strlen( buf ), 3, "%02x", digest[i] ); + resp = msn_p11_challenge( cmd[2] ); + g_snprintf( buf, sizeof( buf ), "QRY %d %s %zd\r\n%s", + ++md->trId, MSNP11_PROD_ID, + strlen( resp ), resp ); + g_free( resp ); return( msn_write( ic, buf, strlen( buf ) ) ); } @@ -398,15 +306,15 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) { const struct msn_away_state *st; - if( num_parts != 6 ) + if( num_parts < 6 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } - http_decode( cmd[4] ); - imcb_rename_buddy( ic, cmd[3], cmd[4] ); + http_decode( cmd[5] ); + imcb_rename_buddy( ic, cmd[3], cmd[5] ); st = msn_away_state_by_code( cmd[2] ); if( !st ) @@ -432,15 +340,15 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) { const struct msn_away_state *st; - if( num_parts != 5 ) + if( num_parts < 5 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); return( 0 ); } - http_decode( cmd[3] ); - imcb_rename_buddy( ic, cmd[2], cmd[3] ); + http_decode( cmd[4] ); + imcb_rename_buddy( ic, cmd[2], cmd[4] ); st = msn_away_state_by_code( cmd[1] ); if( !st ) @@ -461,7 +369,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) char *server; int session, port; - if( num_parts != 7 ) + if( num_parts < 7 ) { imcb_error( ic, "Syntax error" ); imc_logout( ic, TRUE ); @@ -505,7 +413,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "ADD" ) == 0 ) { - if( num_parts == 6 && strcmp( cmd[2], "RL" ) == 0 ) + if( num_parts >= 6 && strcmp( cmd[2], "RL" ) == 0 ) { GSList *l; @@ -564,40 +472,6 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) imc_logout( ic, allow_reconnect ); return( 0 ); } -#if 0 - /* Discard this one completely for now since I don't care about the ack - and since MSN servers can apparently screw up the formatting. */ - else if( strcmp( cmd[0], "REA" ) == 0 ) - { - if( num_parts != 5 ) - { - imcb_error( ic, "Syntax error" ); - imc_logout( ic, TRUE ); - return( 0 ); - } - - if( g_strcasecmp( cmd[3], ic->acc->user ) == 0 ) - { - set_t *s; - - http_decode( cmd[4] ); - strncpy( ic->displayname, cmd[4], sizeof( ic->displayname ) ); - ic->displayname[sizeof(ic->displayname)-1] = 0; - - if( ( s = set_find( &ic->acc->set, "display_name" ) ) ) - { - g_free( s->value ); - s->value = g_strdup( cmd[4] ); - } - } - else - { - /* This is not supposed to happen, but let's handle it anyway... */ - http_decode( cmd[4] ); - imcb_rename_buddy( ic, cmd[3], cmd[4] ); - } - } -#endif else if( strcmp( cmd[0], "IPG" ) == 0 ) { imcb_error( ic, "Received IPG command, we don't handle them yet." ); @@ -655,6 +529,25 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } } } + else if( strcmp( cmd[0], "GCF" ) == 0 ) + { + /* Coming up is cmd[2] bytes of stuff we're supposed to + censore. Meh. */ + md->handler->msglen = atoi( cmd[2] ); + } + else if( strcmp( cmd[0], "UBX" ) == 0 ) + { + /* Status message. Parser coming soon. */ + if( num_parts >= 4 ) + md->handler->msglen = atoi( cmd[3] ); + } + else if( strcmp( cmd[0], "NOT" ) == 0 ) + { + /* Some kind of notification, not sure if it still exists + but we have to skip the payload or stuff breaks. */ + if( num_parts >= 3 ) + md->handler->msglen = atoi( cmd[2] ); + } else if( isdigit( cmd[0][0] ) ) { int num = atoi( cmd[0] ); @@ -764,13 +657,25 @@ static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int g_free( ct ); } } + else if( strcmp( cmd[0], "UBX" ) == 0 ) + { + struct xt_node *psm; + char *psm_text = NULL; + + psm = xt_from_string( msg ); + if( psm && strcmp( psm->name, "Data" ) == 0 && + ( psm = xt_find_node( psm->children, "PSM" ) ) ) + psm_text = psm->text; + + imcb_buddy_status_msg( ic, cmd[1], psm_text ); + xt_free_node( psm ); + } return( 1 ); } -static void msn_auth_got_passport_token( struct msn_auth_data *mad ) +void msn_auth_got_passport_token( struct im_connection *ic, char *token ) { - struct im_connection *ic = mad->data; struct msn_data *md; /* Dead connection? */ @@ -778,61 +683,85 @@ static void msn_auth_got_passport_token( struct msn_auth_data *mad ) return; md = ic->proto_data; - if( mad->token ) + { char buf[1024]; - g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, mad->token ); + g_snprintf( buf, sizeof( buf ), "USR %d SSO S %s %s\r\n", ++md->trId, md->tokens[0], token ); msn_write( ic, buf, strlen( buf ) ); } - else - { - imcb_error( ic, "Error during Passport authentication: %s", mad->error ); - imc_logout( ic, TRUE ); - } } -static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name ) +void msn_auth_got_contact_list( struct im_connection *ic ) { - set_t *s; + char buf[64]; + struct msn_data *md; - if( ( s = set_find( &ic->acc->set, "display_name" ) ) == NULL ) - return FALSE; /* Shouldn't happen.. */ + /* Dead connection? */ + if( g_slist_find( msn_connections, ic ) == NULL ) + return; - http_decode( name ); + md = ic->proto_data; - if( s->value && strcmp( s->value, name ) == 0 ) - { - return TRUE; - /* The names match, nothing to worry about. */ - } - else if( s->value != NULL && - ( strcmp( name, ic->acc->user ) == 0 || - set_getbool( &ic->acc->set, "local_display_name" ) ) ) - { - /* The server thinks our display name is our e-mail address - which is probably wrong, or the user *wants* us to do this: - Always use the locally set display_name. */ - return msn_set_display_name( ic, s->value ); - } - else + + g_snprintf( buf, sizeof( buf ), "BLP %d %s\r\n", ++md->trId, "BL" ); + msn_write( ic, buf, strlen( buf ) ); +} + +static gboolean msn_ns_send_adl_1( gpointer key, gpointer value, gpointer data ) +{ + struct xt_node *adl = data, *d, *c; + struct bee_user *bu = value; + struct msn_buddy_data *bd = bu->data; + char handle[strlen(bu->handle)]; + char *domain; + char l[4]; + + strcpy( handle, bu->handle ); + if( ( domain = strchr( handle, '@' ) ) == NULL ) /* WTF */ + return FALSE; + *domain = '\0'; + domain ++; + + if( ( d = adl->children ) == NULL || + g_strcasecmp( xt_find_attr( d, "n" ), domain ) != 0 ) { - if( s->value && *s->value ) - imcb_log( ic, "BitlBee thinks your display name is `%s' but " - "the MSN server says it's `%s'. Using the MSN " - "server's name. Set local_display_name to true " - "to use the local name.", s->value, name ); - - if( g_utf8_validate( name, -1, NULL ) ) - { - g_free( s->value ); - s->value = g_strdup( name ); - } - else - { - imcb_log( ic, "Warning: Friendly name in server response was corrupted" ); - } - - return TRUE; + d = xt_new_node( "d", NULL, NULL ); + xt_add_attr( d, "n", domain ); + xt_insert_child( adl, d ); } + + g_snprintf( l, sizeof( l ), "%d", bd->flags & 7 ); + c = xt_new_node( "c", NULL, NULL ); + xt_add_attr( c, "n", handle ); + xt_add_attr( c, "l", l ); + xt_add_attr( c, "t", "1" ); /* 1 means normal, 4 means mobile? */ + xt_insert_child( d, c ); + + return FALSE; +} + +static void msn_ns_send_adl( struct im_connection *ic ) +{ + struct xt_node *adl; + struct msn_data *md; + char *adls, buf[64]; + + /* Dead connection? */ + if( g_slist_find( msn_connections, ic ) == NULL ) + return; + + md = ic->proto_data; + + adl = xt_new_node( "ml", NULL, NULL ); + xt_add_attr( adl, "l", "1" ); + g_tree_foreach( md->domaintree, msn_ns_send_adl_1, adl ); + adls = xt_to_string( adl ); + + g_snprintf( buf, sizeof( buf ), "ADL %d %zd\r\n", ++md->trId, strlen( adls ) ); + if( msn_write( ic, buf, strlen( buf ) ) ) + msn_write( ic, adls, strlen( adls ) ); + + g_free( adls ); + xt_free_node( adl ); } diff --git a/protocols/msn/passport.c b/protocols/msn/passport.c deleted file mode 100644 index 7c896db1..00000000 --- a/protocols/msn/passport.c +++ /dev/null @@ -1,172 +0,0 @@ -/** passport.c - * - * Functions to login to Microsoft Passport service for Messenger - * Copyright (C) 2004-2008 Wilmer van der Gaast <wilmer@gaast.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation - * - * This program is distributed in the hope that is will be useful, - * bit WITHOU ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "http_client.h" -#include "passport.h" -#include "msn.h" -#include "bitlbee.h" -#include "url.h" -#include "misc.h" -#include "xmltree.h" -#include <ctype.h> -#include <errno.h> - -static int passport_get_token_real( struct msn_auth_data *mad ); -static void passport_get_token_ready( struct http_request *req ); - -int passport_get_token( gpointer func, gpointer data, char *username, char *password, char *cookie ) -{ - struct msn_auth_data *mad = g_new0( struct msn_auth_data, 1 ); - int i; - - mad->username = g_strdup( username ); - mad->password = g_strdup( password ); - mad->cookie = g_strdup( cookie ); - - mad->callback = func; - mad->data = data; - - mad->url = g_strdup( SOAP_AUTHENTICATION_URL ); - mad->ttl = 3; /* Max. # of redirects. */ - - /* HTTP-escape stuff and s/,/&/ */ - http_decode( mad->cookie ); - for( i = 0; mad->cookie[i]; i ++ ) - if( mad->cookie[i] == ',' ) - mad->cookie[i] = '&'; - - /* Microsoft doesn't allow password longer than 16 chars and silently - fails authentication if you give the "full version" of your passwd. */ - if( strlen( mad->password ) > MAX_PASSPORT_PWLEN ) - mad->password[MAX_PASSPORT_PWLEN] = 0; - - return passport_get_token_real( mad ); -} - -static int passport_get_token_real( struct msn_auth_data *mad ) -{ - char *post_payload, *post_request; - struct http_request *req; - url_t url; - - url_set( &url, mad->url ); - - post_payload = g_markup_printf_escaped( SOAP_AUTHENTICATION_PAYLOAD, - mad->username, - mad->password, - mad->cookie ); - - post_request = g_strdup_printf( SOAP_AUTHENTICATION_REQUEST, - url.file, url.host, - (int) strlen( post_payload ), - post_payload ); - - req = http_dorequest( url.host, url.port, 1, post_request, - passport_get_token_ready, mad ); - - g_free( post_request ); - g_free( post_payload ); - - return req != NULL; -} - -static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data ); -static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data ); - -static const struct xt_handler_entry passport_xt_handlers[] = { - { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", passport_xt_extract_token }, - { "S:Fault", "S:Envelope", passport_xt_handle_fault }, - { NULL, NULL, NULL } -}; - -static void passport_get_token_ready( struct http_request *req ) -{ - struct msn_auth_data *mad = req->data; - struct xt_parser *parser; - - g_free( mad->url ); - g_free( mad->error ); - mad->url = mad->error = NULL; - - if( req->status_code == 200 ) - { - parser = xt_new( passport_xt_handlers, mad ); - xt_feed( parser, req->reply_body, req->body_size ); - xt_handle( parser, NULL, -1 ); - xt_free( parser ); - } - else - { - mad->error = g_strdup_printf( "HTTP error %d (%s)", req->status_code, - req->status_string ? req->status_string : "unknown" ); - } - - if( mad->error == NULL && mad->token == NULL ) - mad->error = g_strdup( "Could not parse Passport server response" ); - - if( mad->url && mad->token == NULL ) - { - passport_get_token_real( mad ); - } - else - { - mad->callback( mad ); - - g_free( mad->url ); - g_free( mad->username ); - g_free( mad->password ); - g_free( mad->cookie ); - g_free( mad->token ); - g_free( mad->error ); - g_free( mad ); - } -} - -static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data ) -{ - struct msn_auth_data *mad = data; - char *s; - - if( ( s = xt_find_attr( node, "Id" ) ) && - ( strncmp( s, "Compact", 7 ) == 0 || - strncmp( s, "PPToken", 7 ) == 0 ) ) - mad->token = g_memdup( node->text, node->text_len + 1 ); - - return XT_HANDLED; -} - -static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data ) -{ - struct msn_auth_data *mad = data; - struct xt_node *code = xt_find_node( node->children, "faultcode" ); - struct xt_node *string = xt_find_node( node->children, "faultstring" ); - struct xt_node *redirect = xt_find_node( node->children, "psf:redirectUrl" ); - - if( redirect && redirect->text_len && mad->ttl-- > 0 ) - mad->url = g_memdup( redirect->text, redirect->text_len + 1 ); - - if( code == NULL || code->text_len == 0 ) - mad->error = g_strdup( "Unknown error" ); - else - mad->error = g_strdup_printf( "%s (%s)", code->text, string && string->text_len ? - string->text : "no description available" ); - - return XT_HANDLED; -} diff --git a/protocols/msn/passport.h b/protocols/msn/passport.h deleted file mode 100644 index 517d2e91..00000000 --- a/protocols/msn/passport.h +++ /dev/null @@ -1,113 +0,0 @@ -/* passport.h - * - * Functions to login to Microsoft Passport service for Messenger - * Copyright (C) 2004-2008 Wilmer van der Gaast <wilmer@gaast.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation - * - * This program is distributed in the hope that is will be useful, - * bit WITHOU ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* Thanks to http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener - for the specs! */ - -#ifndef __PASSPORT_H__ -#define __PASSPORT_H__ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#ifndef _WIN32 -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <unistd.h> -#endif -#include "nogaim.h" - -#define MAX_PASSPORT_PWLEN 16 - -struct msn_auth_data -{ - char *url; - int ttl; - - char *username; - char *password; - char *cookie; - - /* The end result, the only thing we'll really be interested in - once finished. */ - char *token; - char *error; /* Yeah, or that... */ - - void (*callback)( struct msn_auth_data *mad ); - gpointer data; -}; - -#define SOAP_AUTHENTICATION_URL "https://loginnet.passport.com/RST.srf" - -#define SOAP_AUTHENTICATION_REQUEST \ -"POST %s HTTP/1.0\r\n" \ -"Accept: text/*\r\n" \ -"User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \ -"Host: %s\r\n" \ -"Content-Length: %d\r\n" \ -"Cache-Control: no-cache\r\n" \ -"\r\n" \ -"%s" - -#define SOAP_AUTHENTICATION_PAYLOAD \ -"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" \ -"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">" \ - "<Header>" \ - "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">" \ - "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>" \ - "<ps:BinaryVersion>4</ps:BinaryVersion>" \ - "<ps:UIVersion>1</ps:UIVersion>" \ - "<ps:Cookies></ps:Cookies>" \ - "<ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>" \ - "</ps:AuthInfo>" \ - "<wsse:Security>" \ - "<wsse:UsernameToken Id=\"user\">" \ - "<wsse:Username>%s</wsse:Username>" \ - "<wsse:Password>%s</wsse:Password>" \ - "</wsse:UsernameToken>" \ - "</wsse:Security>" \ - "</Header>" \ - "<Body>" \ - "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">" \ - "<wst:RequestSecurityToken Id=\"RST0\">" \ - "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ - "<wsp:AppliesTo>" \ - "<wsa:EndpointReference>" \ - "<wsa:Address>http://Passport.NET/tb</wsa:Address>" \ - "</wsa:EndpointReference>" \ - "</wsp:AppliesTo>" \ - "</wst:RequestSecurityToken>" \ - "<wst:RequestSecurityToken Id=\"RST1\">" \ - "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ - "<wsp:AppliesTo>" \ - "<wsa:EndpointReference>" \ - "<wsa:Address>messenger.msn.com</wsa:Address>" \ - "</wsa:EndpointReference>" \ - "</wsp:AppliesTo>" \ - "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>" \ - "</wst:RequestSecurityToken>" \ - "</ps:RequestMultipleSecurityTokens>" \ - "</Body>" \ -"</Envelope>" - -int passport_get_token( gpointer func, gpointer data, char *username, char *password, char *cookie ); - -#endif /* __PASSPORT_H__ */ diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index cb5789b8..fdad2882 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2005 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* MSN module - Switchboard server callbacks and utilities */ @@ -26,8 +26,8 @@ #include <ctype.h> #include "nogaim.h" #include "msn.h" -#include "passport.h" #include "md5.h" +#include "soap.h" #include "invitation.h" static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition cond ); @@ -38,6 +38,12 @@ int msn_sb_write( struct msn_switchboard *sb, char *s, int len ) { int st; + if( getenv( "BITLBEE_DEBUG" ) ) + { + write( 2, "->SB:", 5 ); + write( 2, s, len ); + } + st = write( sb->fd, s, len ); if( st != len ) { @@ -406,7 +412,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "USR" ) == 0 ) { - if( num_parts != 5 ) + if( num_parts < 5 ) { msn_sb_destroy( sb ); return( 0 ); @@ -432,7 +438,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) { int num, tot; - if( num_parts != 6 ) + if( num_parts < 6 ) { msn_sb_destroy( sb ); return( 0 ); @@ -469,7 +475,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "ANS" ) == 0 ) { - if( num_parts != 3 ) + if( num_parts < 3 ) { msn_sb_destroy( sb ); return( 0 ); @@ -488,7 +494,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "CAL" ) == 0 ) { - if( num_parts != 4 || !isdigit( cmd[3][0] ) ) + if( num_parts < 4 || !isdigit( cmd[3][0] ) ) { msn_sb_destroy( sb ); return( 0 ); @@ -498,7 +504,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "JOI" ) == 0 ) { - if( num_parts != 3 ) + if( num_parts < 3 ) { msn_sb_destroy( sb ); return( 0 ); @@ -559,7 +565,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "MSG" ) == 0 ) { - if( num_parts != 4 ) + if( num_parts < 4 ) { msn_sb_destroy( sb ); return( 0 ); @@ -624,7 +630,12 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) int num = atoi( cmd[0] ); const struct msn_status_code *err = msn_status_by_number( num ); - imcb_error( ic, "Error reported by switchboard server: %s", err->text ); + /* If the person is offline, send an offline message instead, + and don't report an error. */ + if( num == 217 ) + msn_soap_oim_send_queue( ic, &sb->msgq ); + else + imcb_error( ic, "Error reported by switchboard server: %s", err->text ); if( err->flags & STATUS_SB_FATAL ) { diff --git a/protocols/msn/soap.c b/protocols/msn/soap.c new file mode 100644 index 00000000..cd54858f --- /dev/null +++ b/protocols/msn/soap.c @@ -0,0 +1,644 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2010 Wilmer van der Gaast and others * + \********************************************************************/ + +/* MSN module - All the SOAPy XML stuff. + Some manager at Microsoft apparently thought MSNP wasn't XMLy enough so + someone stepped up and changed that. This is the result. Kilobytes and + more kilobytes of XML vomit to transfer tiny bits of informaiton. */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "http_client.h" +#include "soap.h" +#include "msn.h" +#include "bitlbee.h" +#include "url.h" +#include "misc.h" +#include "sha1.h" +#include "base64.h" +#include "xmltree.h" +#include <ctype.h> +#include <errno.h> + +/* This file tries to make SOAP stuff pretty simple to do by letting you just + provide a function to build a request, a few functions to parse various + parts of the response, and a function to run when the full response was + received and parsed. See the various examples below. */ + +typedef enum +{ + MSN_SOAP_OK, + MSN_SOAP_RETRY, + MSN_SOAP_ABORT, +} msn_soap_result_t; + +struct msn_soap_req_data; +typedef int (*msn_soap_func) ( struct msn_soap_req_data * ); + +struct msn_soap_req_data +{ + void *data; + struct im_connection *ic; + int ttl; + + char *url, *action, *payload; + struct http_request *http_req; + + const struct xt_handler_entry *xml_parser; + msn_soap_func build_request, handle_response, free_data; +}; + +static int msn_soap_send_request( struct msn_soap_req_data *req ); + +static int msn_soap_start( struct im_connection *ic, + void *data, + msn_soap_func build_request, + const struct xt_handler_entry *xml_parser, + msn_soap_func handle_response, + msn_soap_func free_data ) +{ + struct msn_soap_req_data *req = g_new0( struct msn_soap_req_data, 1 ); + + req->ic = ic; + req->data = data; + req->xml_parser = xml_parser; + req->build_request = build_request; + req->handle_response = handle_response; + req->free_data = free_data; + req->ttl = 3; + + return msn_soap_send_request( req ); +} + +static void msn_soap_handle_response( struct http_request *http_req ); + +static int msn_soap_send_request( struct msn_soap_req_data *soap_req ) +{ + char *http_req; + char *soap_action = NULL; + url_t url; + + soap_req->build_request( soap_req ); + + if( soap_req->action ) + soap_action = g_strdup_printf( "SOAPAction: \"%s\"\r\n", soap_req->action ); + + url_set( &url, soap_req->url ); + http_req = g_strdup_printf( SOAP_HTTP_REQUEST, url.file, url.host, + soap_action ? soap_action : "", + strlen( soap_req->payload ), soap_req->payload ); + + soap_req->http_req = http_dorequest( url.host, url.port, url.proto == PROTO_HTTPS, + http_req, msn_soap_handle_response, soap_req ); + + g_free( http_req ); + g_free( soap_action ); + + return soap_req->http_req != NULL; +} + +static void msn_soap_handle_response( struct http_request *http_req ) +{ + struct msn_soap_req_data *soap_req = http_req->data; + int st; + + if( http_req->body_size > 0 ) + { + struct xt_parser *parser; + + parser = xt_new( soap_req->xml_parser, soap_req ); + xt_feed( parser, http_req->reply_body, http_req->body_size ); + xt_handle( parser, NULL, -1 ); + xt_free( parser ); + } + + st = soap_req->handle_response( soap_req ); + + g_free( soap_req->url ); + g_free( soap_req->action ); + g_free( soap_req->payload ); + soap_req->url = soap_req->action = soap_req->payload = NULL; + + if( st == MSN_SOAP_RETRY && --soap_req->ttl ) + msn_soap_send_request( soap_req ); + else + { + soap_req->free_data( soap_req ); + g_free( soap_req ); + } +} + + +/* passport_sso: Authentication MSNP15+ */ + +struct msn_soap_passport_sso_data +{ + char *policy; + char *nonce; + char *secret; +}; + +static int msn_soap_passport_sso_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_soap_passport_sso_data *sd = soap_req->data; + struct im_connection *ic = soap_req->ic; + + if( g_str_has_suffix( ic->acc->user, "@msn.com" ) ) + soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL_MSN ); + else + soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL ); + + soap_req->payload = g_markup_printf_escaped( SOAP_PASSPORT_SSO_PAYLOAD, + ic->acc->user, ic->acc->pass, sd->policy ); + + return MSN_SOAP_OK; +} + +static xt_status msn_soap_passport_sso_token( struct xt_node *node, gpointer data ) +{ + struct msn_soap_req_data *soap_req = data; + struct msn_soap_passport_sso_data *sd = soap_req->data; + struct msn_data *md = soap_req->ic->proto_data; + struct xt_node *p; + char *id; + + if( ( id = xt_find_attr( node, "Id" ) ) == NULL ) + return XT_HANDLED; + id += strlen( id ) - 1; + if( *id == '1' && + ( p = node->parent ) && ( p = p->parent ) && + ( p = xt_find_node( p->children, "wst:RequestedProofToken" ) ) && + ( p = xt_find_node( p->children, "wst:BinarySecret" ) ) && + p->text ) + sd->secret = g_strdup( p->text ); + + *id -= '1'; + if( *id >= 0 && *id <= 2 ) + { + g_free( md->tokens[(int)*id] ); + md->tokens[(int)*id] = g_strdup( node->text ); + } + + return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_passport_sso_parser[] = { + { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token }, + { NULL, NULL, NULL } +}; + +static char *msn_key_fuckery( char *key, int key_len, char *type ) +{ + unsigned char hash1[20+strlen(type)+1]; + unsigned char hash2[20]; + char *ret; + + sha1_hmac( key, key_len, type, 0, hash1 ); + strcpy( (char*) hash1 + 20, type ); + sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash2 ); + + /* This is okay as hash1 is read completely before it's overwritten. */ + sha1_hmac( key, key_len, (char*) hash1, 20, hash1 ); + sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash1 ); + + ret = g_malloc( 24 ); + memcpy( ret, hash2, 20 ); + memcpy( ret + 20, hash1, 4 ); + return ret; +} + +static int msn_soap_passport_sso_handle_response( struct msn_soap_req_data *soap_req ) +{ + struct msn_soap_passport_sso_data *sd = soap_req->data; + struct im_connection *ic = soap_req->ic; + char *key1, *key2, *key3, *blurb64; + int key1_len; + unsigned char *padnonce, *des3res; + struct + { + unsigned int uStructHeaderSize; // 28. Does not count data + unsigned int uCryptMode; // CRYPT_MODE_CBC (1) + unsigned int uCipherType; // TripleDES (0x6603) + unsigned int uHashType; // SHA1 (0x8004) + unsigned int uIVLen; // 8 + unsigned int uHashLen; // 20 + unsigned int uCipherLen; // 72 + unsigned char iv[8]; + unsigned char hash[20]; + unsigned char cipherbytes[72]; + } blurb = { + GUINT32_TO_LE( 28 ), + GUINT32_TO_LE( 1 ), + GUINT32_TO_LE( 0x6603 ), + GUINT32_TO_LE( 0x8004 ), + GUINT32_TO_LE( 8 ), + GUINT32_TO_LE( 20 ), + GUINT32_TO_LE( 72 ), + }; + + key1_len = base64_decode( sd->secret, (unsigned char**) &key1 ); + + key2 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY HASH" ); + key3 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY ENCRYPTION" ); + + sha1_hmac( key2, 24, sd->nonce, 0, blurb.hash ); + padnonce = g_malloc( strlen( sd->nonce ) + 8 ); + strcpy( (char*) padnonce, sd->nonce ); + memset( padnonce + strlen( sd->nonce ), 8, 8 ); + + random_bytes( blurb.iv, 8 ); + + ssl_des3_encrypt( (unsigned char*) key3, 24, padnonce, strlen( sd->nonce ) + 8, blurb.iv, &des3res ); + memcpy( blurb.cipherbytes, des3res, 72 ); + + blurb64 = base64_encode( (unsigned char*) &blurb, sizeof( blurb ) ); + msn_auth_got_passport_token( ic, blurb64 ); + + g_free( padnonce ); + g_free( blurb64 ); + g_free( des3res ); + g_free( key1 ); + g_free( key2 ); + g_free( key3 ); + + return MSN_SOAP_OK; +} + +static int msn_soap_passport_sso_free_data( struct msn_soap_req_data *soap_req ) +{ + struct msn_soap_passport_sso_data *sd = soap_req->data; + + g_free( sd->policy ); + g_free( sd->nonce ); + g_free( sd->secret ); + + return MSN_SOAP_OK; +} + +int msn_soap_passport_sso_request( struct im_connection *ic, const char *policy, const char *nonce ) +{ + struct msn_soap_passport_sso_data *sd = g_new0( struct msn_soap_passport_sso_data, 1 ); + + sd->policy = g_strdup( policy ); + sd->nonce = g_strdup( nonce ); + + return msn_soap_start( ic, sd, msn_soap_passport_sso_build_request, + msn_soap_passport_sso_parser, + msn_soap_passport_sso_handle_response, + msn_soap_passport_sso_free_data ); +} + + +/* oim_send: Sending offline messages */ + +struct msn_soap_oim_send_data +{ + char *to; + char *msg; + int number; + int need_retry; +}; + +static int msn_soap_oim_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_soap_oim_send_data *oim = soap_req->data; + struct im_connection *ic = soap_req->ic; + struct msn_data *md = ic->proto_data; + char *display_name_b64; + + display_name_b64 = tobase64( set_getstr( &ic->acc->set, "display_name" ) ); + + soap_req->url = g_strdup( SOAP_OIM_SEND_URL ); + soap_req->action = g_strdup( SOAP_OIM_SEND_ACTION ); + soap_req->payload = g_markup_printf_escaped( SOAP_OIM_SEND_PAYLOAD, + ic->acc->user, display_name_b64, MSNP_VER, MSNP_BUILD, + oim->to, md->tokens[2], + MSNP11_PROD_ID, md->lock_key ? md->lock_key : "", + oim->number, oim->number, oim->msg ); + + g_free( display_name_b64 ); + + return MSN_SOAP_OK; +} + +static xt_status msn_soap_oim_send_challenge( struct xt_node *node, gpointer data ) +{ + struct msn_soap_req_data *soap_req = data; + struct msn_soap_oim_send_data *oim = soap_req->data; + struct im_connection *ic = soap_req->ic; + struct msn_data *md = ic->proto_data; + + g_free( md->lock_key ); + md->lock_key = msn_p11_challenge( node->text ); + + oim->need_retry = 1; + + return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_oim_send_parser[] = { + { "LockKeyChallenge", "detail", msn_soap_oim_send_challenge }, + { NULL, NULL, NULL } +}; + +static int msn_soap_oim_handle_response( struct msn_soap_req_data *soap_req ) +{ + struct msn_soap_oim_send_data *oim = soap_req->data; + + if( soap_req->http_req->status_code == 500 && oim->need_retry && soap_req->ttl > 0 ) + { + oim->need_retry = 0; + return MSN_SOAP_RETRY; + } + else if( soap_req->http_req->status_code == 200 ) + { + imcb_log( soap_req->ic, "Offline message successfully delivered to %s", oim->to ); + return MSN_SOAP_OK; + } + else + { + imcb_log( soap_req->ic, "Failed to deliver offline message to %s:\n%s", oim->to, oim->msg ); + return MSN_SOAP_ABORT; + } +} + +static int msn_soap_oim_free_data( struct msn_soap_req_data *soap_req ) +{ + struct msn_soap_oim_send_data *oim = soap_req->data; + + g_free( oim->to ); + g_free( oim->msg ); + g_free( oim ); + + return MSN_SOAP_OK; +} + +int msn_soap_oim_send( struct im_connection *ic, const char *to, const char *msg ) +{ + struct msn_soap_oim_send_data *data; + + data = g_new0( struct msn_soap_oim_send_data, 1 ); + data->to = g_strdup( to ); + data->msg = tobase64( msg ); + data->number = 1; + + return msn_soap_start( ic, data, msn_soap_oim_build_request, + msn_soap_oim_send_parser, + msn_soap_oim_handle_response, + msn_soap_oim_free_data ); +} + +int msn_soap_oim_send_queue( struct im_connection *ic, GSList **msgq ) +{ + GSList *l; + char *n = NULL; + + for( l = *msgq; l; l = l->next ) + { + struct msn_message *m = l->data; + + if( n == NULL ) + n = m->who; + if( strcmp( n, m->who ) == 0 ) + msn_soap_oim_send( ic, m->who, m->text ); + } + + while( *msgq != NULL ) + { + struct msn_message *m = (*msgq)->data; + + g_free( m->who ); + g_free( m->text ); + g_free( m ); + + *msgq = g_slist_remove( *msgq, m ); + } + + return 1; +} + + +/* memlist: Fetching the membership list (NOT address book) */ + +static int msn_soap_memlist_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_data *md = soap_req->ic->proto_data; + + soap_req->url = g_strdup( SOAP_MEMLIST_URL ); + soap_req->action = g_strdup( SOAP_MEMLIST_ACTION ); + soap_req->payload = g_markup_printf_escaped( SOAP_MEMLIST_PAYLOAD, md->tokens[1] ); + + return 1; +} + +static xt_status msn_soap_memlist_member( struct xt_node *node, gpointer data ) +{ + bee_user_t *bu; + struct msn_buddy_data *bd; + struct xt_node *p; + char *role = NULL, *handle = NULL; + struct msn_soap_req_data *soap_req = data; + struct im_connection *ic = soap_req->ic; + + if( ( p = node->parent ) && ( p = p->parent ) && + ( p = xt_find_node( p->children, "MemberRole" ) ) ) + role = p->text; + + if( ( p = xt_find_node( node->children, "PassportName" ) ) ) + handle = p->text; + + if( !role || !handle || + !( ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) || + ( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) ) + return XT_HANDLED; + + bd = bu->data; + if( strcmp( role, "Allow" ) == 0 ) + bd->flags |= MSN_BUDDY_AL; + else if( strcmp( role, "Block" ) == 0 ) + bd->flags |= MSN_BUDDY_BL; + else if( strcmp( role, "Reverse" ) == 0 ) + bd->flags |= MSN_BUDDY_RL; + else if( strcmp( role, "Pending" ) == 0 ) + bd->flags |= MSN_BUDDY_PL; + + return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_memlist_parser[] = { + { "Member", "Members", msn_soap_memlist_member }, + { NULL, NULL, NULL } +}; + +static int msn_soap_memlist_handle_response( struct msn_soap_req_data *soap_req ) +{ + msn_soap_addressbook_request( soap_req->ic ); + + return MSN_SOAP_OK; +} + +static int msn_soap_memlist_free_data( struct msn_soap_req_data *soap_req ) +{ + return 0; +} + +int msn_soap_memlist_request( struct im_connection *ic ) +{ + return msn_soap_start( ic, NULL, msn_soap_memlist_build_request, + msn_soap_memlist_parser, + msn_soap_memlist_handle_response, + msn_soap_memlist_free_data ); +} + + +/* addressbook: Fetching the membership list (NOT address book) */ + +static int msn_soap_addressbook_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_data *md = soap_req->ic->proto_data; + + soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL ); + soap_req->action = g_strdup( SOAP_ADDRESSBOOK_ACTION ); + soap_req->payload = g_markup_printf_escaped( SOAP_ADDRESSBOOK_PAYLOAD, md->tokens[1] ); + + return 1; +} + +static xt_status msn_soap_addressbook_group( struct xt_node *node, gpointer data ) +{ + struct xt_node *p; + char *id = NULL, *name = NULL; + struct msn_soap_req_data *soap_req = data; + + if( ( p = node->parent ) && + ( p = xt_find_node( p->children, "groupId" ) ) ) + id = p->text; + + if( ( p = xt_find_node( node->children, "name" ) ) ) + name = p->text; + + printf( "%s %s\n", id, name ); + + return XT_HANDLED; +} + +static xt_status msn_soap_addressbook_contact( struct xt_node *node, gpointer data ) +{ + bee_user_t *bu; + struct msn_buddy_data *bd; + struct xt_node *p; + char *id = NULL, *type = NULL, *handle = NULL, *display_name = NULL; + struct msn_soap_req_data *soap_req = data; + struct im_connection *ic = soap_req->ic; + + if( ( p = node->parent ) && + ( p = xt_find_node( p->children, "contactId" ) ) ) + id = p->text; + if( ( p = xt_find_node( node->children, "contactType" ) ) ) + type = p->text; + if( ( p = xt_find_node( node->children, "passportName" ) ) ) + handle = p->text; + if( ( p = xt_find_node( node->children, "displayName" ) ) ) + display_name = p->text; + + if( type && g_strcasecmp( type, "me" ) == 0 ) + { + set_t *set = set_find( &ic->acc->set, "display_name" ); + g_free( set->value ); + set->value = g_strdup( display_name ); + + return XT_HANDLED; + } + + if( !( bu = bee_user_by_handle( ic->bee, ic, handle ) ) && + !( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) + return XT_HANDLED; + + bd = bu->data; + bd->flags |= MSN_BUDDY_FL; + g_free( bd->cid ); + bd->cid = g_strdup( id ); + + imcb_rename_buddy( ic, handle, display_name ); + + printf( "%s %s %s %s\n", id, type, handle, display_name ); + + return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_addressbook_parser[] = { + { "contactInfo", "Contact", msn_soap_addressbook_contact }, + { "groupInfo", "Group", msn_soap_addressbook_group }, + { NULL, NULL, NULL } +}; + +static int msn_soap_addressbook_handle_response( struct msn_soap_req_data *soap_req ) +{ + msn_auth_got_contact_list( soap_req->ic ); + return MSN_SOAP_OK; +} + +static int msn_soap_addressbook_free_data( struct msn_soap_req_data *soap_req ) +{ + return 0; +} + +int msn_soap_addressbook_request( struct im_connection *ic ) +{ + return msn_soap_start( ic, NULL, msn_soap_addressbook_build_request, + msn_soap_addressbook_parser, + msn_soap_addressbook_handle_response, + msn_soap_addressbook_free_data ); +} + +/* Variant: Change our display name. */ +static int msn_soap_ab_namechange_build_request( struct msn_soap_req_data *soap_req ) +{ + struct msn_data *md = soap_req->ic->proto_data; + + soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL ); + soap_req->action = g_strdup( SOAP_AB_NAMECHANGE_ACTION ); + soap_req->payload = g_markup_printf_escaped( SOAP_AB_NAMECHANGE_PAYLOAD, + md->tokens[1], (char *) soap_req->data ); + + return 1; +} + +static int msn_soap_ab_namechange_handle_response( struct msn_soap_req_data *soap_req ) +{ + /* TODO: Ack the change? Not sure what the NAKs look like.. */ + return MSN_SOAP_OK; +} + +static int msn_soap_ab_namechange_free_data( struct msn_soap_req_data *soap_req ) +{ + g_free( soap_req->data ); + return 0; +} + +int msn_soap_addressbook_set_display_name( struct im_connection *ic, const char *new ) +{ + return msn_soap_start( ic, g_strdup( new ), + msn_soap_ab_namechange_build_request, + NULL, + msn_soap_ab_namechange_handle_response, + msn_soap_ab_namechange_free_data ); +} diff --git a/protocols/msn/soap.h b/protocols/msn/soap.h new file mode 100644 index 00000000..5673583a --- /dev/null +++ b/protocols/msn/soap.h @@ -0,0 +1,258 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2010 Wilmer van der Gaast and others * + \********************************************************************/ + +/* MSN module - All the SOAPy XML stuff. + Some manager at Microsoft apparently thought MSNP wasn't XMLy enough so + someone stepped up and changed that. This is the result. Kilobytes and + more kilobytes of XML vomit to transfer tiny bits of informaiton. */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Thanks to http://msnpiki.msnfanatic.com/ for lots of info on this! */ + +#ifndef __SOAP_H__ +#define __SOAP_H__ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#endif +#include "nogaim.h" + + +#define SOAP_HTTP_REQUEST \ +"POST %s HTTP/1.0\r\n" \ +"Host: %s\r\n" \ +"Accept: */*\r\n" \ +"User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \ +"Content-Type: text/xml; charset=utf-8\r\n" \ +"%s" \ +"Content-Length: %zd\r\n" \ +"Cache-Control: no-cache\r\n" \ +"\r\n" \ +"%s" + + +#define SOAP_PASSPORT_SSO_URL "https://login.live.com/RST.srf" +#define SOAP_PASSPORT_SSO_URL_MSN "https://msnia.login.live.com/pp550/RST.srf" + +#define SOAP_PASSPORT_SSO_PAYLOAD \ +"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" " \ + "xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" " \ + "xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" " \ + "xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" " \ + "xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" " \ + "xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" " \ + "xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" " \ + "xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">" \ + "<Header>" \ + "<ps:AuthInfo " \ + "xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" " \ + "Id=\"PPAuthInfo\">" \ + "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>" \ + "<ps:BinaryVersion>4</ps:BinaryVersion>" \ + "<ps:UIVersion>1</ps:UIVersion>" \ + "<ps:Cookies></ps:Cookies>" \ + "<ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>" \ + "</ps:AuthInfo>" \ + "<wsse:Security>" \ + "<wsse:UsernameToken Id=\"user\">" \ + "<wsse:Username>%s</wsse:Username>" \ + "<wsse:Password>%s</wsse:Password>" \ + "</wsse:UsernameToken>" \ + "</wsse:Security>" \ + "</Header>" \ + "<Body>" \ + "<ps:RequestMultipleSecurityTokens " \ + "xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" " \ + "Id=\"RSTS\">" \ + "<wst:RequestSecurityToken Id=\"RST0\">" \ + "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ + "<wsp:AppliesTo>" \ + "<wsa:EndpointReference>" \ + "<wsa:Address>http://Passport.NET/tb</wsa:Address>" \ + "</wsa:EndpointReference>" \ + "</wsp:AppliesTo>" \ + "</wst:RequestSecurityToken>" \ + "<wst:RequestSecurityToken Id=\"RST1\">" \ + "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ + "<wsp:AppliesTo>" \ + "<wsa:EndpointReference>" \ + "<wsa:Address>messengerclear.live.com</wsa:Address>" \ + "</wsa:EndpointReference>" \ + "</wsp:AppliesTo>" \ + "<wsse:PolicyReference URI=\"%s\"></wsse:PolicyReference>" \ + "</wst:RequestSecurityToken>" \ + "<wst:RequestSecurityToken Id=\"RST2\">" \ + "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ + "<wsp:AppliesTo>" \ + "<wsa:EndpointReference>" \ + "<wsa:Address>contacts.msn.com</wsa:Address>" \ + "</wsa:EndpointReference>" \ + "</wsp:AppliesTo>" \ + "<wsse:PolicyReference xmlns=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" URI=\"MBI\"></wsse:PolicyReference>" \ + "</wst:RequestSecurityToken>" \ + "<wst:RequestSecurityToken Id=\"RST3\">" \ + "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ + "<wsp:AppliesTo>" \ + "<wsa:EndpointReference>" \ + "<wsa:Address>messengersecure.live.com</wsa:Address>" \ + "</wsa:EndpointReference>" \ + "</wsp:AppliesTo>" \ + "<wsse:PolicyReference xmlns=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" URI=\"MBI_SSL\"></wsse:PolicyReference>" \ + "</wst:RequestSecurityToken>" \ + "</ps:RequestMultipleSecurityTokens>" \ + "</Body>" \ +"</Envelope>" + +int msn_soap_passport_sso_request( struct im_connection *ic, const char *policy, const char *nonce ); + + +#define SOAP_OIM_SEND_URL "https://ows.messenger.msn.com/OimWS/oim.asmx" +#define SOAP_OIM_SEND_ACTION "http://messenger.live.com/ws/2006/09/oim/Store2" + +#define SOAP_OIM_SEND_PAYLOAD \ +"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \ +"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \ +"<soap:Header>" \ + "<From memberName=\"%s\" friendlyName=\"=?utf-8?B?%s?=\" xml:lang=\"nl-nl\" proxy=\"MSNMSGR\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\" msnpVer=\"%s\" buildVer=\"%s\"/>" \ + "<To memberName=\"%s\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\"/>" \ + "<Ticket passport=\"%s\" appid=\"%s\" lockkey=\"%s\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\"/>" \ + "<Sequence xmlns=\"http://schemas.xmlsoap.org/ws/2003/03/rm\">" \ + "<Identifier xmlns=\"http://schemas.xmlsoap.org/ws/2002/07/utility\">http://messenger.msn.com</Identifier>" \ + "<MessageNumber>%d</MessageNumber>" \ + "</Sequence>" \ +"</soap:Header>" \ +"<soap:Body>" \ + "<MessageType xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\">text</MessageType>" \ + "<Content xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\">" \ + "MIME-Version: 1.0\r\n" \ + "Content-Type: text/plain; charset=UTF-8\r\n" \ + "Content-Transfer-Encoding: base64\r\n" \ + "X-OIM-Message-Type: OfflineMessage\r\n" \ + "X-OIM-Run-Id: {F9A6C9DD-0D94-4E85-9CC6-F9D118CC1CAF}\r\n" \ + "X-OIM-Sequence-Num: %d\r\n" \ + "\r\n" \ + "%s" \ + "</Content>" \ +"</soap:Body>" \ +"</soap:Envelope>" + +int msn_soap_oim_send( struct im_connection *ic, const char *to, const char *msg ); +int msn_soap_oim_send_queue( struct im_connection *ic, GSList **msgq ); + + +#define SOAP_MEMLIST_URL "http://contacts.msn.com/abservice/SharingService.asmx" +#define SOAP_MEMLIST_ACTION "http://www.msn.com/webservices/AddressBook/FindMembership" + +#define SOAP_MEMLIST_PAYLOAD \ +"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \ +"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \ + "<soap:Header xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \ + "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<ApplicationId xmlns=\"http://www.msn.com/webservices/AddressBook\">CFE80F9D-180F-4399-82AB-413F33A1FA11</ApplicationId>" \ + "<IsMigration xmlns=\"http://www.msn.com/webservices/AddressBook\">false</IsMigration>" \ + "<PartnerScenario xmlns=\"http://www.msn.com/webservices/AddressBook\">Initial</PartnerScenario>" \ + "</ABApplicationHeader>" \ + "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<ManagedGroupRequest xmlns=\"http://www.msn.com/webservices/AddressBook\">false</ManagedGroupRequest>" \ + "<TicketToken>%s</TicketToken>" \ + "</ABAuthHeader>" \ + "</soap:Header>" \ + "<soap:Body xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \ + "<FindMembership xmlns=\"http://www.msn.com/webservices/AddressBook\"><serviceFilter xmlns=\"http://www.msn.com/webservices/AddressBook\"><Types xmlns=\"http://www.msn.com/webservices/AddressBook\"><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Messenger</ServiceType><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Invitation</ServiceType><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">SocialNetwork</ServiceType><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Space</ServiceType><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Profile</ServiceType></Types></serviceFilter>" \ + "</FindMembership>" \ + "</soap:Body>" \ +"</soap:Envelope>" + +int msn_soap_memlist_request( struct im_connection *ic ); + + +#define SOAP_ADDRESSBOOK_URL "http://contacts.msn.com/abservice/abservice.asmx" +#define SOAP_ADDRESSBOOK_ACTION "http://www.msn.com/webservices/AddressBook/ABFindAll" + +#define SOAP_ADDRESSBOOK_PAYLOAD \ +"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \ +"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">" \ + "<soap:Header>" \ + "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<ApplicationId>CFE80F9D-180F-4399-82AB-413F33A1FA11</ApplicationId>" \ + "<IsMigration>false</IsMigration>" \ + "<PartnerScenario>Initial</PartnerScenario>" \ + "</ABApplicationHeader>" \ + "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<ManagedGroupRequest>false</ManagedGroupRequest>" \ + "<TicketToken>%s</TicketToken>" \ + "</ABAuthHeader>" \ + "</soap:Header>" \ + "<soap:Body>" \ + "<ABFindAll xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<abId>00000000-0000-0000-0000-000000000000</abId>" \ + "<abView>Full</abView>" \ + "<deltasOnly>false</deltasOnly>" \ + "<lastChange>0001-01-01T00:00:00.0000000-08:00</lastChange>" \ + "</ABFindAll>" \ + "</soap:Body>" \ +"</soap:Envelope>" + +#define SOAP_AB_NAMECHANGE_ACTION "http://www.msn.com/webservices/AddressBook/ABContactUpdate" + +#define SOAP_AB_NAMECHANGE_PAYLOAD \ +"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \ +"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">" \ + "<soap:Header>" \ + "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<ApplicationId>CFE80F9D-180F-4399-82AB-413F33A1FA11</ApplicationId>" \ + "<IsMigration>false</IsMigration>" \ + "<PartnerScenario>Initial</PartnerScenario>" \ + "</ABApplicationHeader>" \ + "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<ManagedGroupRequest>false</ManagedGroupRequest>" \ + "<TicketToken>%s</TicketToken>" \ + "</ABAuthHeader>" \ + "</soap:Header>" \ + "<soap:Body>" \ + "<ABContactUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<abId>00000000-0000-0000-0000-000000000000</abId>" \ + "<contacts>" \ + "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ + "<contactInfo>" \ + "<contactType>Me</contactType>" \ + "<displayName>%s</displayName>" \ + "</contactInfo>" \ + "<propertiesChanged>DisplayName</propertiesChanged>" \ + "</Contact>" \ + "</contacts>" \ + "</ABContactUpdate>" \ + "</soap:Body>" \ +"</soap:Envelope>" + +int msn_soap_addressbook_request( struct im_connection *ic ); +int msn_soap_addressbook_set_display_name( struct im_connection *ic, const char *new ); + + +#endif /* __SOAP_H__ */ diff --git a/protocols/msn/tables.c b/protocols/msn/tables.c index 42b12aa9..fd7eca41 100644 --- a/protocols/msn/tables.c +++ b/protocols/msn/tables.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2004 Wilmer van der Gaast and others * + * Copyright 2002-2010 Wilmer van der Gaast and others * \********************************************************************/ /* MSN module - Some tables with useful data */ diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 7943e026..10ffd843 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -325,14 +325,6 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) imcb_log( ic, "Signing off.." ); - b_event_remove( ic->keepalive ); - ic->keepalive = 0; - ic->acc->prpl->logout( ic ); - b_event_remove( ic->inpa ); - - g_free( ic->away ); - ic->away = NULL; - for( l = bee->users; l; ) { bee_user_t *bu = l->data; @@ -344,6 +336,14 @@ void imc_logout( struct im_connection *ic, int allow_reconnect ) l = next; } + b_event_remove( ic->keepalive ); + ic->keepalive = 0; + ic->acc->prpl->logout( ic ); + b_event_remove( ic->inpa ); + + g_free( ic->away ); + ic->away = NULL; + query_del_by_conn( (irc_t*) ic->bee->ui_data, ic ); for( a = bee->accounts; a; a = a->next ) diff --git a/protocols/nogaim.h b/protocols/nogaim.h index be67bb24..90254508 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -193,6 +193,8 @@ struct prpl { /* Request profile info. Free-formatted stuff, the IM module gives back this info via imcb_log(). Implementing these are optional. */ void (* get_info) (struct im_connection *, char *who); + /* set_my_name is *DEPRECATED*, not used by the UI anymore. Use the + display_name setting instead. */ void (* set_my_name) (struct im_connection *, char *name); void (* set_name) (struct im_connection *, char *who, char *name); diff --git a/storage_xml.c b/storage_xml.c index 1d6757ae..5035e214 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -319,6 +319,16 @@ static void xml_text( GMarkupParseContext *ctx, const gchar *text_orig, gsize te } else if( g_strcasecmp( g_markup_parse_context_get_element( ctx ), "setting" ) == 0 && xd->current_setting ) { + if( xd->current_account ) + { + set_t *s = set_find( xd->current_set_head, xd->current_setting ); + if( s && ( s->flags & ACC_SET_ONLINE_ONLY ) ) + { + g_free( xd->current_setting ); + xd->current_setting = NULL; + return; + } + } set_setstr( xd->current_set_head, xd->current_setting, (char*) text ); g_free( xd->current_setting ); xd->current_setting = NULL; |