aboutsummaryrefslogtreecommitdiffstats
path: root/protocols
diff options
context:
space:
mode:
authorWilmer van der Gaast <wilmer@gaast.net>2010-10-02 19:45:26 -0700
committerWilmer van der Gaast <wilmer@gaast.net>2010-10-02 19:45:26 -0700
commit8e9e2b7d9e54744cee32b3724391bf0ad04e8aff (patch)
tree6cafdc57bb8143306daf8ef8288d09754f39b451 /protocols
parent88de0c96136313e553113f69cfc6ae544a5f5954 (diff)
parent2af3e232ff468b288dd4e0dbdab1a17312d801c5 (diff)
Merging mainline, which includes a huge msnp13 merge.
Not 100% sure about the OpenSSL merge, should double check that but I'm currently offline.
Diffstat (limited to 'protocols')
-rw-r--r--protocols/bee.h1
-rw-r--r--protocols/bee_chat.c2
-rw-r--r--protocols/bee_user.c29
-rw-r--r--protocols/jabber/conference.c18
-rw-r--r--protocols/msn/Makefile2
-rw-r--r--protocols/msn/msn.c129
-rw-r--r--protocols/msn/msn.h122
-rw-r--r--protocols/msn/msn_util.c291
-rw-r--r--protocols/msn/ns.c640
-rw-r--r--protocols/msn/passport.c172
-rw-r--r--protocols/msn/passport.h113
-rw-r--r--protocols/msn/sb.c94
-rw-r--r--protocols/msn/soap.c1078
-rw-r--r--protocols/msn/soap.h350
-rw-r--r--protocols/msn/tables.c4
-rw-r--r--protocols/nogaim.c18
-rw-r--r--protocols/nogaim.h2
-rw-r--r--protocols/oscar/ssi.c2
-rw-r--r--protocols/purple/purple.c44
-rw-r--r--protocols/twitter/twitter.c15
20 files changed, 2302 insertions, 824 deletions
diff --git a/protocols/bee.h b/protocols/bee.h
index b99c8de7..077c3661 100644
--- a/protocols/bee.h
+++ b/protocols/bee.h
@@ -150,6 +150,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_chat.c b/protocols/bee_chat.c
index 0314cae5..349e0547 100644
--- a/protocols/bee_chat.c
+++ b/protocols/bee_chat.c
@@ -195,7 +195,7 @@ void imcb_chat_remove_buddy( struct groupchat *c, const char *handle, const char
bu = bee_user_by_handle( bee, ic, handle );
}
- if( bee->ui->chat_remove_user )
+ if( bee->ui->chat_remove_user && bu )
bee->ui->chat_remove_user( bee, c, bu );
}
diff --git a/protocols/bee_user.c b/protocols/bee_user.c
index 33853a1e..4ea538a9 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( bu->status == NULL && ( flags & OPT_MOBILE ) &&
set_getbool( &bee->set, "mobile_is_away" ) )
@@ -204,6 +209,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 = message && *message ? g_strdup( message ) : NULL;
+
+ 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/jabber/conference.c b/protocols/jabber/conference.c
index e04b9792..0c2db0b3 100644
--- a/protocols/jabber/conference.c
+++ b/protocols/jabber/conference.c
@@ -311,6 +311,15 @@ void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud
struct jabber_chat *jc = chat ? chat->data : NULL;
char *s;
+ if( subject && chat )
+ {
+ s = bud ? strchr( bud->ext_jid, '/' ) : NULL;
+ if( s ) *s = 0;
+ imcb_chat_topic( chat, bud ? bud->ext_jid : NULL, subject->text_len > 0 ?
+ subject->text : NULL, jabber_get_timestamp( node ) );
+ if( s ) *s = '/';
+ }
+
if( bud == NULL || ( jc && ~jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me ) )
{
char *nick;
@@ -365,15 +374,6 @@ void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud
now or just wait for the OS to do it. :-) */
return;
}
-
- if( subject )
- {
- s = strchr( bud->ext_jid, '/' );
- if( s ) *s = 0;
- imcb_chat_topic( chat, bud->ext_jid, subject->text_len > 0 ?
- subject->text : NULL, jabber_get_timestamp( node ) );
- if( s ) *s = '/';
- }
if( body && body->text_len > 0 )
{
s = strchr( bud->ext_jid, '/' );
diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile
index 068d7e98..8845d41b 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
diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c
index 29b7cecc..5b2c53f7 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 )
@@ -46,7 +52,6 @@ static void msn_login( account_t *acc )
struct msn_data *md = g_new0( struct msn_data, 1 );
ic->proto_data = md;
- md->fd = -1;
if( strchr( acc->user, '@' ) == NULL )
{
@@ -55,26 +60,22 @@ static void msn_login( account_t *acc )
return;
}
- imcb_log( ic, "Connecting" );
-
- md->fd = proxy_connect( "messenger.hotmail.com", 1863, msn_ns_connected, ic );
- if( md->fd < 0 )
- {
- imcb_error( ic, "Could not connect to server" );
- imc_logout( ic, TRUE );
- return;
- }
-
md->ic = ic;
md->away_state = msn_away_state_list;
+ md->domaintree = g_tree_new( msn_domaintree_cmp );
+ md->ns->fd = -1;
- msn_connections = g_slist_append( msn_connections, ic );
+ msn_connections = g_slist_prepend( msn_connections, ic );
+
+ imcb_log( ic, "Connecting" );
+ msn_ns_connect( ic, md->ns, MSN_NS_HOST, MSN_NS_PORT );
}
static void msn_logout( struct im_connection *ic )
{
struct msn_data *md = ic->proto_data;
GSList *l;
+ int i;
if( md )
{
@@ -84,24 +85,30 @@ static void msn_logout( struct im_connection *ic )
}
*/
- if( md->fd >= 0 )
- closesocket( md->fd );
-
- if( md->handler )
- {
- if( md->handler->rxq ) g_free( md->handler->rxq );
- if( md->handler->cmd_text ) g_free( md->handler->cmd_text );
- g_free( md->handler );
- }
+ msn_ns_close( md->ns );
while( md->switchboards )
msn_sb_destroy( md->switchboards->data );
msn_msgq_purge( ic, &md->msgq );
+ msn_soapq_flush( ic, FALSE );
+
+ for( i = 0; i < sizeof( md->tokens ) / sizeof( md->tokens[0] ); i ++ )
+ g_free( md->tokens[i] );
+ g_free( md->lock_key );
+ g_free( md->pp_policy );
- while( md->groupcount > 0 )
- g_free( md->grouplist[--md->groupcount] );
- g_free( md->grouplist );
+ while( md->groups )
+ {
+ struct msn_group *mg = md->groups->data;
+ g_free( mg->id );
+ g_free( mg->name );
+ g_free( mg );
+ md->groups = g_slist_remove( md->groups, mg );
+ }
+
+ g_tree_destroy( md->domaintree );
+ md->domaintree = NULL;
while( md->grpq )
{
@@ -133,8 +140,7 @@ static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, in
#ifdef DEBUG
if( strcmp( who, "raw" ) == 0 )
{
- msn_write( ic, message, strlen( message ) );
- msn_write( ic, "\r\n", 2 );
+ msn_ns_write( ic, -1, "%s\r\n", message );
}
else
#endif
@@ -172,7 +178,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 )
@@ -180,13 +186,13 @@ static void msn_set_away( struct im_connection *ic, char *state, char *message )
else if( ( md->away_state = msn_away_state_by_name( state ) ) == NULL )
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_ns_write( ic, -1, "CHG %d %s\r\n", ++md->trId, md->away_state->code ) )
+ return;
+
+ uux = g_markup_printf_escaped( "<Data><PSM>%s</PSM><CurrentMedia></CurrentMedia>"
+ "</Data>", message ? message : "" );
+ msn_ns_write( ic, -1, "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux );
+ g_free( uux );
}
static void msn_get_info(struct im_connection *ic, char *who)
@@ -199,14 +205,14 @@ static void msn_add_buddy( struct im_connection *ic, char *who, char *group )
{
struct bee_user *bu = bee_user_by_handle( ic->bee, ic, who );
- msn_buddy_list_add( ic, "FL", who, who, group );
+ msn_buddy_list_add( ic, MSN_BUDDY_FL, who, who, group );
if( bu && bu->group )
- msn_buddy_list_remove( ic, "FL", who, bu->group->name );
+ msn_buddy_list_remove( ic, MSN_BUDDY_FL, who, bu->group->name );
}
static void msn_remove_buddy( struct im_connection *ic, char *who, char *group )
{
- msn_buddy_list_remove( ic, "FL", who, NULL );
+ msn_buddy_list_remove( ic, MSN_BUDDY_FL, who, NULL );
}
static void msn_chat_msg( struct groupchat *c, char *message, int flags )
@@ -266,24 +272,24 @@ static struct groupchat *msn_chat_with( struct im_connection *ic, char *who )
static void msn_keepalive( struct im_connection *ic )
{
- msn_write( ic, "PNG\r\n", strlen( "PNG\r\n" ) );
+ msn_ns_write( ic, -1, "PNG\r\n" );
}
static void msn_add_permit( struct im_connection *ic, char *who )
{
- msn_buddy_list_add( ic, "AL", who, who, NULL );
+ msn_buddy_list_add( ic, MSN_BUDDY_AL, who, who, NULL );
}
static void msn_rem_permit( struct im_connection *ic, char *who )
{
- msn_buddy_list_remove( ic, "AL", who, NULL );
+ msn_buddy_list_remove( ic, MSN_BUDDY_AL, who, NULL );
}
static void msn_add_deny( struct im_connection *ic, char *who )
{
struct msn_switchboard *sb;
- msn_buddy_list_add( ic, "BL", who, who, NULL );
+ msn_buddy_list_add( ic, MSN_BUDDY_BL, who, who, NULL );
/* If there's still a conversation with this person, close it. */
if( ( sb = msn_sb_by_handle( ic, who ) ) )
@@ -294,7 +300,7 @@ static void msn_add_deny( struct im_connection *ic, char *who )
static void msn_rem_deny( struct im_connection *ic, char *who )
{
- msn_buddy_list_remove( ic, "BL", who, NULL );
+ msn_buddy_list_remove( ic, MSN_BUDDY_BL, who, NULL );
}
static int msn_send_typing( struct im_connection *ic, char *who, int typing )
@@ -313,10 +319,7 @@ 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;
if( strlen( value ) > 129 )
{
@@ -324,10 +327,26 @@ 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;
+ if( md->flags & MSN_GOT_PROFILE_DN )
+ imcb_log( ic, "Warning: Persistent name changes for this account have to be done "
+ "in the profile. BitlBee doesn't currently support this." );
+
+ msn_soap_addressbook_set_display_name( ic, value );
+ return msn_ns_set_display_name( ic, value ) ? 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()
@@ -343,7 +362,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;
@@ -357,6 +375,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..829bbd4c 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"
+*/
+
+#define MSN_NS_HOST "messenger.hotmail.com"
+#define MSN_NS_PORT 1863
+
+/* Some other version.
+#define MSNP11_PROD_KEY "O4BG@C7BWLYQX?5G"
+#define MSNP11_PROD_ID "PROD01065C%ZFN6F"
+*/
+
+#define MSNP11_PROD_KEY "ILTXC!4IXB5FB*PX"
+#define MSNP11_PROD_ID "PROD0119GSJUC$18"
+#define MSNP_VER "MSNP15"
+#define MSNP_BUILD "8.5.1288"
#define MSN_SB_NEW -24062002
@@ -60,31 +75,61 @@
#define PROFILE_URL "http://members.msn.com/"
+typedef enum
+{
+ MSN_GOT_PROFILE = 1,
+ MSN_GOT_PROFILE_DN = 2,
+ MSN_DONE_ADL = 4,
+ MSN_REAUTHING = 8,
+} msn_flags_t;
+
+struct msn_handler_data
+{
+ int fd, inpa;
+ int rxlen;
+ char *rxq;
+
+ int msglen;
+ char *cmd_text;
+
+ /* Either ic or sb */
+ gpointer data;
+
+ int (*exec_command) ( struct msn_handler_data *handler, char **cmd, int count );
+ int (*exec_message) ( struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int count );
+};
+
struct msn_data
{
struct im_connection *ic;
- int fd;
- struct msn_handler_data *handler;
+ struct msn_handler_data ns[1];
+ msn_flags_t flags;
int trId;
+ char *tokens[4];
+ char *lock_key, *pp_policy;
- GSList *msgq, *grpq;
+ GSList *msgq, *grpq, *soapq;
GSList *switchboards;
int sb_failures;
time_t first_sb_failure;
- GSList *filetransfers;
const struct msn_away_state *away_state;
- int buddycount;
- int groupcount;
- char **grouplist;
+ GSList *groups;
+
+ /* Mostly used for sending the ADL command; since MSNP13 the client
+ is responsible for downloading the contact list and then sending
+ it to the MSNP server. */
+ GTree *domaintree;
+ int adl_todo;
};
struct msn_switchboard
{
struct im_connection *ic;
+ /* The following two are also in the handler. TODO: Clean up. */
int fd;
gint inp;
struct msn_handler_data *handler;
@@ -126,19 +171,26 @@ struct msn_groupadd
char *group;
};
-struct msn_handler_data
+typedef enum
{
- int fd;
- int rxlen;
- char *rxq;
-
- int msglen;
- char *cmd_text;
-
- gpointer data;
-
- int (*exec_command) ( gpointer data, char **cmd, int count );
- int (*exec_message) ( gpointer data, char *msg, int msglen, char **cmd, int count );
+ 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_ADL_SYNCED = 256,
+} msn_buddy_flags_t;
+
+struct msn_buddy_data
+{
+ char *cid;
+ msn_buddy_flags_t flags;
+};
+
+struct msn_group
+{
+ char *name;
+ char *id;
};
/* Bitfield values for msn_status_code.flags */
@@ -160,20 +212,27 @@ extern GSList *msn_connections;
extern GSList *msn_switchboards;
/* ns.c */
-gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond );
+int msn_ns_write( struct im_connection *ic, int fd, const char *fmt, ... );
+gboolean msn_ns_connect( struct im_connection *ic, struct msn_handler_data *handler, const char *host, int port );
+void msn_ns_close( struct msn_handler_data *handler );
+void msn_auth_got_passport_token( struct im_connection *ic, const char *token, const char *error );
+void msn_auth_got_contact_list( struct im_connection *ic );
+int msn_ns_finish_login( struct im_connection *ic );
/* msn_util.c */
-int msn_write( struct im_connection *ic, char *s, int len );
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 );
-int msn_buddy_list_remove( struct im_connection *ic, char *list, const char *who, const char *group );
-void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname );
+int msn_buddy_list_add( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *realname_, const char *group );
+int msn_buddy_list_remove( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *group );
+void msn_buddy_ask( bee_user_t *bu );
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_ );
+struct msn_group *msn_group_by_name( struct im_connection *ic, const char *name );
+struct msn_group *msn_group_by_id( struct im_connection *ic, const char *id );
+int msn_ns_set_display_name( struct im_connection *ic, const char *value );
/* tables.c */
const struct msn_away_state *msn_away_state_by_number( int number );
@@ -182,7 +241,7 @@ const struct msn_away_state *msn_away_state_by_name( char *name );
const struct msn_status_code *msn_status_by_number( int number );
/* sb.c */
-int msn_sb_write( struct msn_switchboard *sb, char *s, int len );
+int msn_sb_write( struct msn_switchboard *sb, const char *fmt, ... );
struct msn_switchboard *msn_sb_create( struct im_connection *ic, char *host, int port, char *key, int session );
struct msn_switchboard *msn_sb_by_handle( struct im_connection *ic, char *handle );
struct msn_switchboard *msn_sb_by_chat( struct groupchat *c );
@@ -195,7 +254,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..7fa68915 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,37 +25,41 @@
#include "nogaim.h"
#include "msn.h"
+#include "md5.h"
+#include "soap.h"
#include <ctype.h>
-int msn_write( struct im_connection *ic, char *s, int len )
+int msn_logged_in( struct im_connection *ic )
{
- struct msn_data *md = ic->proto_data;
- int st;
-
- st = write( md->fd, s, len );
- if( st != len )
- {
- imcb_error( ic, "Short write() to main server" );
- imc_logout( ic, TRUE );
- return 0;
- }
+ imcb_connected( ic );
- return 1;
+ return( 0 );
}
-int msn_logged_in( struct im_connection *ic )
+static char *adlrml_entry( const char *handle_, msn_buddy_flags_t list )
{
- imcb_connected( ic );
+ char *domain, handle[strlen(handle_)+1];
- return( 0 );
+ strcpy( handle, handle_ );
+ if( ( domain = strchr( handle, '@' ) ) )
+ *(domain++) = '\0';
+ else
+ return NULL;
+
+ return g_markup_printf_escaped( "<ml><d n=\"%s\"><c n=\"%s\" l=\"%d\" t=\"1\"/></d></ml>",
+ domain, handle, list );
}
-int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group )
+int msn_buddy_list_add( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *realname, const char *group )
{
struct msn_data *md = ic->proto_data;
- char buf[1024], *realname, groupid[8];
+ char groupid[8];
+ bee_user_t *bu;
+ struct msn_buddy_data *bd;
+ char *adl;
*groupid = '\0';
+#if 0
if( group )
{
int i;
@@ -86,9 +90,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
@@ -100,20 +105,42 @@ int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *
}
}
}
+#endif
+
+ if( !( ( bu = bee_user_by_handle( ic->bee, ic, who ) ) ||
+ ( bu = bee_user_new( ic->bee, ic, who, 0 ) ) ) ||
+ !( bd = bu->data ) || bd->flags & list )
+ return 1;
+
+ bd->flags |= list;
- realname = msn_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 );
+ if( list == MSN_BUDDY_FL )
+ msn_soap_ab_contact_add( ic, bu );
+ else
+ msn_soap_memlist_edit( ic, who, TRUE, list );
- return msn_write( ic, buf, strlen( buf ) );
+ if( ( adl = adlrml_entry( who, list ) ) )
+ {
+ int st = msn_ns_write( ic, -1, "ADL %d %zd\r\n%s",
+ ++md->trId, strlen( adl ), adl );
+ g_free( adl );
+
+ return st;
+ }
+
+ return 1;
}
-int msn_buddy_list_remove( struct im_connection *ic, char *list, const char *who, const char *group )
+int msn_buddy_list_remove( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *group )
{
struct msn_data *md = ic->proto_data;
- char buf[1024], groupid[8];
+ char groupid[8];
+ bee_user_t *bu;
+ struct msn_buddy_data *bd;
+ char *adl;
*groupid = '\0';
+#if 0
if( group )
{
int i;
@@ -124,12 +151,29 @@ int msn_buddy_list_remove( struct im_connection *ic, char *list, const char *who
break;
}
}
+#endif
- g_snprintf( buf, sizeof( buf ), "REM %d %s %s%s\r\n", ++md->trId, list, who, groupid );
- if( msn_write( ic, buf, strlen( buf ) ) )
- return( 1 );
+ if( !( bu = bee_user_by_handle( ic->bee, ic, who ) ) ||
+ !( bd = bu->data ) || !( bd->flags & list ) )
+ return 1;
- return( 0 );
+ bd->flags &= ~list;
+
+ if( list == MSN_BUDDY_FL )
+ msn_soap_ab_contact_del( ic, bu );
+ else
+ msn_soap_memlist_edit( ic, who, FALSE, list );
+
+ if( ( adl = adlrml_entry( who, list ) ) )
+ {
+ int st = msn_ns_write( ic, -1, "RML %d %zd\r\n%s",
+ ++md->trId, strlen( adl ), adl );
+ g_free( adl );
+
+ return st;
+ }
+
+ return 1;
}
struct msn_buddy_ask_data
@@ -143,7 +187,7 @@ static void msn_buddy_ask_yes( void *data )
{
struct msn_buddy_ask_data *bla = data;
- msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname, NULL );
+ msn_buddy_list_add( bla->ic, MSN_BUDDY_AL, bla->handle, bla->realname, NULL );
imcb_ask_add( bla->ic, bla->handle, NULL );
@@ -156,26 +200,31 @@ static void msn_buddy_ask_no( void *data )
{
struct msn_buddy_ask_data *bla = data;
- msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname, NULL );
+ msn_buddy_list_add( bla->ic, MSN_BUDDY_BL, bla->handle, bla->realname, NULL );
g_free( bla->handle );
g_free( bla->realname );
g_free( bla );
}
-void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname )
+void msn_buddy_ask( bee_user_t *bu )
{
- struct msn_buddy_ask_data *bla = g_new0( struct msn_buddy_ask_data, 1 );
+ struct msn_buddy_ask_data *bla;
+ struct msn_buddy_data *bd = bu->data;
char buf[1024];
- bla->ic = ic;
- bla->handle = g_strdup( handle );
- bla->realname = g_strdup( realname );
+ if( ( bd->flags & 30 ) != 8 && ( bd->flags & 30 ) != 16 )
+ return;
+
+ bla = g_new0( struct msn_buddy_ask_data, 1 );
+ bla->ic = bu->ic;
+ bla->handle = g_strdup( bu->handle );
+ bla->realname = g_strdup( bu->fullname );
g_snprintf( buf, sizeof( buf ),
"The user %s (%s) wants to add you to his/her buddy list.",
- handle, realname );
- imcb_ask( ic, buf, bla, msn_buddy_ask_yes, msn_buddy_ask_no );
+ bu->handle, bu->fullname );
+ imcb_ask( bu->ic, buf, bla, msn_buddy_ask_yes, msn_buddy_ask_no );
}
char *msn_findheader( char *text, char *header, int len )
@@ -279,6 +328,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;
@@ -295,7 +350,7 @@ int msn_handler( struct msn_handler_data *h )
cmd_text = g_strndup( h->rxq, i );
cmd = msn_linesplit( cmd_text );
for( count = 0; cmd[count]; count ++ );
- st = h->exec_command( h->data, cmd, count );
+ st = h->exec_command( h, cmd, count );
g_free( cmd_text );
/* If the connection broke, don't continue. We don't even exist anymore. */
@@ -330,7 +385,7 @@ int msn_handler( struct msn_handler_data *h )
cmd = msn_linesplit( h->cmd_text );
for( count = 0; cmd[count]; count ++ );
- st = h->exec_message( h->data, msg, h->msglen, cmd, count );
+ st = h->exec_message( h, msg, h->msglen, cmd, count );
g_free( msg );
g_free( h->cmd_text );
h->cmd_text = NULL;
@@ -366,32 +421,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 +461,130 @@ 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 *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;
+
+ newHashParts[0] ^= nHigh;
+ newHashParts[1] ^= nLow;
+ newHashParts[2] ^= nHigh;
+ newHashParts[3] ^= nLow;
+
+ /* 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;
+}
+
+struct msn_group *msn_group_by_name( struct im_connection *ic, const char *name )
{
- char *fn = msn_http_encode( rawname );
struct msn_data *md = ic->proto_data;
- char buf[1024];
+ GSList *l;
+
+ for( l = md->groups; l; l = l->next )
+ {
+ struct msn_group *mg = l->data;
+
+ if( g_strcasecmp( mg->name, name ) == 0 )
+ return mg;
+ }
+
+ return NULL;
+}
+
+struct msn_group *msn_group_by_id( struct im_connection *ic, const char *id )
+{
+ struct msn_data *md = ic->proto_data;
+ GSList *l;
+
+ for( l = md->groups; l; l = l->next )
+ {
+ struct msn_group *mg = l->data;
+
+ if( g_strcasecmp( mg->id, id ) == 0 )
+ return mg;
+ }
+
+ return NULL;
+}
+
+int msn_ns_set_display_name( struct im_connection *ic, const char *value )
+{
+ struct msn_data *md = ic->proto_data;
+ char fn[strlen(value)*3+1];
- g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, ic->acc->user, fn );
- g_free( fn );
+ strcpy( fn, value );
+ http_encode( fn );
- return msn_write( ic, buf, strlen( buf ) ) != 0;
+ /* 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_ns_write( ic, -1, "PRP %d MFN %s\r\n", ++md->trId, fn );
}
diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c
index 0be9e727..d64a71ac 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,68 +26,122 @@
#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_connected( gpointer data, gint source, b_input_condition cond );
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 int msn_ns_command( struct msn_handler_data *handler, char **cmd, int num_parts );
+static int msn_ns_message( struct msn_handler_data *handler, 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_start( struct im_connection *ic );
+static void msn_ns_send_adl( struct im_connection *ic );
-gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond )
+int msn_ns_write( struct im_connection *ic, int fd, const char *fmt, ... )
{
- struct im_connection *ic = data;
- struct msn_data *md;
- char s[1024];
+ struct msn_data *md = ic->proto_data;
+ va_list params;
+ char *out;
+ size_t len;
+ int st;
- if( !g_slist_find( msn_connections, ic ) )
- return FALSE;
+ va_start( params, fmt );
+ out = g_strdup_vprintf( fmt, params );
+ va_end( params );
- if( source == -1 )
+ if( fd < 0 )
+ fd = md->ns->fd;
+
+ if( getenv( "BITLBEE_DEBUG" ) )
+ fprintf( stderr, "->NS%d:%s", fd, out );
+
+ len = strlen( out );
+ st = write( fd, out, len );
+ g_free( out );
+ if( st != len )
+ {
+ imcb_error( ic, "Short write() to main server" );
+ imc_logout( ic, TRUE );
+ return 0;
+ }
+
+ return 1;
+}
+
+gboolean msn_ns_connect( struct im_connection *ic, struct msn_handler_data *handler, const char *host, int port )
+{
+ if( handler->fd >= 0 )
+ closesocket( handler->fd );
+
+ handler->exec_command = msn_ns_command;
+ handler->exec_message = msn_ns_message;
+ handler->data = ic;
+ handler->fd = proxy_connect( host, port, msn_ns_connected, handler );
+ if( handler->fd < 0 )
{
imcb_error( ic, "Could not connect to server" );
imc_logout( ic, TRUE );
return FALSE;
}
+ return TRUE;
+}
+
+static gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond )
+{
+ struct msn_handler_data *handler = data;
+ struct im_connection *ic = handler->data;
+ struct msn_data *md;
+
+ if( !g_slist_find( msn_connections, ic ) )
+ return FALSE;
+
md = ic->proto_data;
- if( !md->handler )
- {
- md->handler = g_new0( struct msn_handler_data, 1 );
- md->handler->data = ic;
- md->handler->exec_command = msn_ns_command;
- md->handler->exec_message = msn_ns_message;
- }
- else
+ if( source == -1 )
{
- if( md->handler->rxq )
- g_free( md->handler->rxq );
-
- md->handler->rxlen = 0;
+ imcb_error( ic, "Could not connect to server" );
+ imc_logout( ic, TRUE );
+ return FALSE;
}
- md->handler->fd = md->fd;
- md->handler->rxq = g_new0( char, 1 );
+ g_free( handler->rxq );
+ handler->rxlen = 0;
+ handler->rxq = g_new0( char, 1 );
- g_snprintf( s, sizeof( s ), "VER %d MSNP8 CVR0\r\n", ++md->trId );
- if( msn_write( ic, s, strlen( s ) ) )
+ if( msn_ns_write( ic, source, "VER %d %s CVR0\r\n", ++md->trId, MSNP_VER ) )
{
- ic->inpa = b_input_add( md->fd, B_EV_IO_READ, msn_ns_callback, ic );
+ handler->inpa = b_input_add( handler->fd, B_EV_IO_READ, msn_ns_callback, handler );
imcb_log( ic, "Connected to server, waiting for reply" );
}
return FALSE;
}
+void msn_ns_close( struct msn_handler_data *handler )
+{
+ if( handler->fd >= 0 )
+ {
+ closesocket( handler->fd );
+ b_event_remove( handler->inpa );
+ }
+
+ handler->fd = handler->inpa = -1;
+ g_free( handler->rxq );
+ g_free( handler->cmd_text );
+
+ handler->rxlen = 0;
+ handler->rxq = NULL;
+ handler->cmd_text = NULL;
+}
+
static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition cond )
{
- struct im_connection *ic = data;
- struct msn_data *md = ic->proto_data;
+ struct msn_handler_data *handler = data;
+ struct im_connection *ic = handler->data;
- if( msn_handler( md->handler ) == -1 ) /* Don't do this on ret == 0, it's already done then. */
+ if( msn_handler( handler ) == -1 ) /* Don't do this on ret == 0, it's already done then. */
{
imcb_error( ic, "Error while reading from server" );
imc_logout( ic, TRUE );
@@ -98,11 +152,10 @@ static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition c
return TRUE;
}
-static int msn_ns_command( gpointer data, char **cmd, int num_parts )
+static int msn_ns_command( struct msn_handler_data *handler, char **cmd, int num_parts )
{
- struct im_connection *ic = data;
+ struct im_connection *ic = handler->data;
struct msn_data *md = ic->proto_data;
- char buf[1024];
if( num_parts == 0 )
{
@@ -112,33 +165,30 @@ 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 );
return( 0 );
}
- g_snprintf( buf, sizeof( buf ), "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s\r\n",
- ++md->trId, ic->acc->user );
- return( msn_write( ic, buf, strlen( buf ) ) );
+ return( msn_ns_write( ic, handler->fd, "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s\r\n",
+ ++md->trId, ic->acc->user ) );
}
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 );
- return( msn_write( ic, buf, strlen( buf ) ) );
+ return msn_ns_write( ic, handler->fd, "USR %d SSO I %s\r\n", ++md->trId, ic->acc->user );
}
else if( strcmp( cmd[0], "XFR" ) == 0 )
{
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;
- closesocket( md->fd );
+ b_event_remove( handler->inpa );
+ handler->inpa = -1;
server = strchr( cmd[3], ':' );
if( !server )
@@ -152,10 +202,9 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )
server = cmd[3];
imcb_log( ic, "Transferring to other server" );
-
- md->fd = proxy_connect( server, port, msn_ns_connected, ic );
+ return msn_ns_connect( ic, handler, server, port );
}
- 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 +268,17 @@ 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 );
- }
+ g_free( md->pp_policy );
+ md->pp_policy = g_strdup( cmd[4] );
+ msn_soap_passport_sso_request( ic, 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,163 +289,76 @@ 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 );
return( 0 );
}
- md->handler->msglen = atoi( cmd[3] );
+ handler->msglen = atoi( cmd[3] );
- if( md->handler->msglen <= 0 )
+ if( handler->msglen <= 0 )
{
imcb_error( ic, "Syntax error" );
imc_logout( ic, TRUE );
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_start( ic );
+ return msn_ns_finish_login( ic );
}
- else if( strcmp( cmd[0], "LST" ) == 0 )
+ else if( strcmp( cmd[0], "ADL" ) == 0 )
{
- int list;
-
- if( num_parts != 4 && num_parts != 5 )
+ if( num_parts >= 3 && strcmp( cmd[2], "OK" ) == 0 )
{
- imcb_error( ic, "Syntax error" );
- imc_logout( ic, TRUE );
- return( 0 );
+ msn_ns_send_adl( ic );
+ return msn_ns_finish_login( ic );
}
-
- http_decode( cmd[2] );
- list = atoi( cmd[3] );
-
- if( list & 1 ) /* FL */
+ else if( num_parts >= 3 )
{
- char *group = NULL;
- int num;
-
- if( cmd[4] != NULL && sscanf( cmd[4], "%d", &num ) == 1 && num < md->groupcount )
- group = md->grouplist[num];
-
- 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 );
- }
+ handler->msglen = atoi( cmd[2] );
}
}
- 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;
+ int st;
- 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 );
+ resp = msn_p11_challenge( cmd[2] );
- 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] );
-
- return( msn_write( ic, buf, strlen( buf ) ) );
+ st = msn_ns_write( ic, -1, "QRY %d %s %zd\r\n%s",
+ ++md->trId, MSNP11_PROD_ID,
+ strlen( resp ), resp );
+ g_free( resp );
+ return st;
}
else if( strcmp( cmd[0], "ILN" ) == 0 )
{
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 )
@@ -431,16 +383,18 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )
else if( strcmp( cmd[0], "NLN" ) == 0 )
{
const struct msn_away_state *st;
+ int cap;
- if( num_parts != 5 )
+ if( num_parts < 6 )
{
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] );
+ cap = atoi( cmd[5] );
+ imcb_rename_buddy( ic, cmd[2], cmd[4] );
st = msn_away_state_by_code( cmd[1] );
if( !st )
@@ -450,7 +404,8 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )
}
imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN |
- ( st != msn_away_state_list ? OPT_AWAY : 0 ),
+ ( st != msn_away_state_list ? OPT_AWAY : 0 ) |
+ ( cap & 1 ? OPT_MOBILE : 0 ),
st->name, NULL );
msn_sb_stop_keepalives( msn_sb_by_handle( ic, cmd[2] ) );
@@ -461,7 +416,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 );
@@ -503,46 +458,6 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )
sb->who = g_strdup( cmd[5] );
}
}
- else if( strcmp( cmd[0], "ADD" ) == 0 )
- {
- if( num_parts == 6 && strcmp( cmd[2], "RL" ) == 0 )
- {
- GSList *l;
-
- http_decode( cmd[5] );
-
- if( strchr( cmd[4], '@' ) == NULL )
- {
- imcb_error( ic, "Syntax error" );
- imc_logout( ic, TRUE );
- return 0;
- }
-
- /* We got added by someone. If we don't have this
- person in permit/deny yet, inform the user. */
- for( l = ic->permit; l; l = l->next )
- if( g_strcasecmp( l->data, cmd[4] ) == 0 )
- return 1;
-
- for( l = ic->deny; l; l = l->next )
- if( g_strcasecmp( l->data, cmd[4] ) == 0 )
- return 1;
-
- msn_buddy_ask( ic, cmd[4], cmd[5] );
- }
- else if( num_parts >= 6 && strcmp( cmd[2], "FL" ) == 0 )
- {
- const char *group = NULL;
- int num;
-
- if( cmd[6] != NULL && sscanf( cmd[6], "%d", &num ) == 1 && num < md->groupcount )
- group = md->grouplist[num];
-
- http_decode( cmd[5] );
- imcb_add_buddy( ic, cmd[4], group );
- imcb_rename_buddy( ic, cmd[4], cmd[5] );
- }
- }
else if( strcmp( cmd[0], "OUT" ) == 0 )
{
int allow_reconnect = TRUE;
@@ -564,53 +479,20 @@ 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." );
- md->handler->msglen = atoi( cmd[1] );
+ handler->msglen = atoi( cmd[1] );
- if( md->handler->msglen <= 0 )
+ if( handler->msglen <= 0 )
{
imcb_error( ic, "Syntax error" );
imc_logout( ic, TRUE );
return( 0 );
}
}
+#if 0
else if( strcmp( cmd[0], "ADG" ) == 0 )
{
char *group = g_strdup( cmd[3] );
@@ -655,6 +537,26 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )
}
}
}
+#endif
+ else if( strcmp( cmd[0], "GCF" ) == 0 )
+ {
+ /* Coming up is cmd[2] bytes of stuff we're supposed to
+ censore. Meh. */
+ handler->msglen = atoi( cmd[2] );
+ }
+ else if( strcmp( cmd[0], "UBX" ) == 0 )
+ {
+ /* Status message. */
+ if( num_parts >= 4 )
+ handler->msglen = atoi( cmd[3] );
+ }
+ else if( strcmp( cmd[0], "NOT" ) == 0 )
+ {
+ /* Some kind of notification, poorly documented but
+ apparently used to announce address book changes. */
+ if( num_parts >= 2 )
+ handler->msglen = atoi( cmd[1] );
+ }
else if( isdigit( cmd[0][0] ) )
{
int num = atoi( cmd[0] );
@@ -667,6 +569,10 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )
imc_logout( ic, TRUE );
return( 0 );
}
+
+ /* Oh yes, errors can have payloads too now. Discard them for now. */
+ if( num_parts >= 3 )
+ handler->msglen = atoi( cmd[2] );
}
else
{
@@ -676,9 +582,9 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )
return( 1 );
}
-static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts )
+static int msn_ns_message( struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int num_parts )
{
- struct im_connection *ic = data;
+ struct im_connection *ic = handler->data;
char *body;
int blen = 0;
@@ -764,13 +670,75 @@ 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 );
+ }
+ else if( strcmp( cmd[0], "ADL" ) == 0 )
+ {
+ struct xt_node *adl, *d, *c;
+
+ if( !( adl = xt_from_string( msg ) ) )
+ return 1;
+
+ for( d = adl->children; d; d = d->next )
+ {
+ char *dn;
+ if( strcmp( d->name, "d" ) != 0 ||
+ ( dn = xt_find_attr( d, "n" ) ) == NULL )
+ continue;
+ for( c = d->children; c; c = c->next )
+ {
+ bee_user_t *bu;
+ struct msn_buddy_data *bd;
+ char *cn, *handle, *f, *l;
+ int flags;
+
+ if( strcmp( c->name, "c" ) != 0 ||
+ ( l = xt_find_attr( c, "l" ) ) == NULL ||
+ ( cn = xt_find_attr( c, "n" ) ) == NULL )
+ continue;
+
+ handle = g_strdup_printf( "%s@%s", cn, dn );
+ if( !( ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) ||
+ ( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) )
+ {
+ g_free( handle );
+ continue;
+ }
+ g_free( handle );
+ bd = bu->data;
+
+ if( ( f = xt_find_attr( c, "f" ) ) )
+ {
+ http_decode( f );
+ imcb_rename_buddy( ic, bu->handle, f );
+ }
+
+ flags = atoi( l ) & 15;
+ if( bd->flags != flags )
+ {
+ bd->flags = flags;
+ msn_buddy_ask( bu );
+ }
+ }
+ }
+ }
return( 1 );
}
-static void msn_auth_got_passport_token( struct msn_auth_data *mad )
+void msn_auth_got_passport_token( struct im_connection *ic, const char *token, const char *error )
{
- struct im_connection *ic = mad->data;
struct msn_data *md;
/* Dead connection? */
@@ -778,61 +746,129 @@ static void msn_auth_got_passport_token( struct msn_auth_data *mad )
return;
md = ic->proto_data;
- if( mad->token )
+
+ if( token )
{
- char buf[1024];
-
- g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, mad->token );
- msn_write( ic, buf, strlen( buf ) );
+ msn_ns_write( ic, -1, "USR %d SSO S %s %s\r\n", ++md->trId, md->tokens[0], token );
}
else
{
- imcb_error( ic, "Error during Passport authentication: %s", mad->error );
+ imcb_error( ic, "Error during Passport authentication: %s", 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;
+ 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;
+ msn_ns_write( ic, -1, "BLP %d %s\r\n", ++md->trId, "BL" );
+}
+
+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;
+ struct msn_data *md = bu->ic->proto_data;
+ char handle[strlen(bu->handle)];
+ char *domain;
+ char l[4];
- if( s->value && strcmp( s->value, name ) == 0 )
+ if( ( bd->flags & 7 ) == 0 || ( bd->flags & MSN_BUDDY_ADL_SYNCED ) )
+ return FALSE;
+
+ 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 )
{
- return TRUE;
- /* The names match, nothing to worry about. */
+ d = xt_new_node( "d", NULL, NULL );
+ xt_add_attr( d, "n", domain );
+ xt_insert_child( adl, d );
}
- 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 );
+
+ 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 );
+
+ /* Do this in batches of 100. */
+ bd->flags |= MSN_BUDDY_ADL_SYNCED;
+ return (--md->adl_todo % 140) == 0;
+}
+
+static void msn_ns_send_adl( struct im_connection *ic )
+{
+ struct xt_node *adl;
+ struct msn_data *md = ic->proto_data;
+ char *adls;
+
+ adl = xt_new_node( "ml", NULL, NULL );
+ xt_add_attr( adl, "l", "1" );
+ g_tree_foreach( md->domaintree, msn_ns_send_adl_1, adl );
+ if( adl->children == NULL )
+ {
+ /* This tells the caller that we're done now. */
+ md->adl_todo = -1;
+ xt_free_node( adl );
+ return;
}
- else
+
+ adls = xt_to_string( adl );
+ msn_ns_write( ic, -1, "ADL %d %zd\r\n%s", ++md->trId, strlen( adls ), adls );
+ g_free( adls );
+}
+
+static void msn_ns_send_adl_start( struct im_connection *ic )
+{
+ struct msn_data *md;
+ GSList *l;
+
+ /* Dead connection? */
+ if( g_slist_find( msn_connections, ic ) == NULL )
+ return;
+
+ md = ic->proto_data;
+ md->adl_todo = 0;
+ for( l = ic->bee->users; l; l = l->next )
{
- 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 );
+ bee_user_t *bu = l->data;
+ struct msn_buddy_data *bd = bu->data;
- 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" );
- }
+ if( bu->ic != ic || ( bd->flags & 7 ) == 0 )
+ continue;
- return TRUE;
+ bd->flags &= ~MSN_BUDDY_ADL_SYNCED;
+ md->adl_todo++;
}
+
+ msn_ns_send_adl( ic );
+}
+
+int msn_ns_finish_login( struct im_connection *ic )
+{
+ struct msn_data *md = ic->proto_data;
+
+ if( ic->flags & OPT_LOGGED_IN )
+ return 1;
+
+ if( md->adl_todo < 0 )
+ md->flags |= MSN_DONE_ADL;
+
+ if( ( md->flags & MSN_DONE_ADL ) && ( md->flags & MSN_GOT_PROFILE ) )
+ return msn_ns_set_display_name( ic, set_getstr( &ic->acc->set, "display_name" ) );
+ else
+ return 1;
}
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..898fb34f 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,33 +26,44 @@
#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 );
-static int msn_sb_command( gpointer data, char **cmd, int num_parts );
-static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts );
+static int msn_sb_command( struct msn_handler_data *handler, char **cmd, int num_parts );
+static int msn_sb_message( struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int num_parts );
-int msn_sb_write( struct msn_switchboard *sb, char *s, int len )
+int msn_sb_write( struct msn_switchboard *sb, const char *fmt, ... )
{
+ va_list params;
+ char *out;
+ size_t len;
int st;
- st = write( sb->fd, s, len );
+ va_start( params, fmt );
+ out = g_strdup_vprintf( fmt, params );
+ va_end( params );
+
+ if( getenv( "BITLBEE_DEBUG" ) )
+ fprintf( stderr, "->SB%d:%s", sb->fd, out );
+
+ len = strlen( out );
+ st = write( sb->fd, out, len );
+ g_free( out );
if( st != len )
{
msn_sb_destroy( sb );
- return( 0 );
+ return 0;
}
- return( 1 );
+ return 1;
}
int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m )
{
struct msn_data *md = ic->proto_data;
struct msn_switchboard *sb;
- char buf[1024];
/* FIXME: *CHECK* the reliability of using spare sb's! */
if( ( sb = msn_sb_spare( ic ) ) )
@@ -60,8 +71,7 @@ int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m )
debug( "Trying to use a spare switchboard to message %s", m->who );
sb->who = g_strdup( m->who );
- g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, m->who );
- if( msn_sb_write( sb, buf, strlen( buf ) ) )
+ if( msn_sb_write( sb, "CAL %d %s\r\n", ++sb->trId, m->who ) )
{
/* He/She should join the switchboard soon, let's queue the message. */
sb->msgq = g_slist_append( sb->msgq, m );
@@ -72,8 +82,7 @@ int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m )
debug( "Creating a new switchboard to message %s", m->who );
/* If we reach this line, there was no spare switchboard, so let's make one. */
- g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
- if( !msn_write( ic, buf, strlen( buf ) ) )
+ if( !msn_ns_write( ic, -1, "XFR %d SB\r\n", ++md->trId ) )
{
g_free( m->who );
g_free( m->text );
@@ -164,7 +173,7 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text )
{
if( sb->ready )
{
- char *packet, *buf;
+ char *buf;
int i, j;
/* Build the message. Convert LF to CR-LF for normal messages. */
@@ -200,17 +209,15 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text )
}
/* Build the final packet (MSG command + the message). */
- packet = g_strdup_printf( "MSG %d N %d\r\n%s", ++sb->trId, i, buf );
- g_free( buf );
- if( msn_sb_write( sb, packet, strlen( packet ) ) )
+ if( msn_sb_write( sb, "MSG %d N %d\r\n%s", ++sb->trId, i, buf ) )
{
- g_free( packet );
- return( 1 );
+ g_free( buf );
+ return 1;
}
else
{
- g_free( packet );
- return( 0 );
+ g_free( buf );
+ return 0;
}
}
else if( sb->who )
@@ -325,7 +332,7 @@ gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond )
else
g_snprintf( buf, sizeof( buf ), "ANS %d %s %s %d\r\n", ++sb->trId, ic->acc->user, sb->key, sb->session );
- if( msn_sb_write( sb, buf, strlen( buf ) ) )
+ if( msn_sb_write( sb, "%s", buf ) )
sb->inp = b_input_add( sb->fd, B_EV_IO_READ, msn_sb_callback, sb );
else
debug( "Error %d while connecting to switchboard server", 2 );
@@ -345,7 +352,6 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c
if( sb->msgq != NULL )
{
time_t now = time( NULL );
- char buf[1024];
if( now - md->first_sb_failure > 600 )
{
@@ -377,8 +383,7 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c
debug( "Moved queued messages back to the main queue, "
"creating a new switchboard to retry." );
- g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
- if( !msn_write( ic, buf, strlen( buf ) ) )
+ if( !msn_ns_write( ic, -1, "XFR %d SB\r\n", ++md->trId ) )
return FALSE;
}
@@ -386,11 +391,10 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c
return FALSE;
}
-static int msn_sb_command( gpointer data, char **cmd, int num_parts )
+static int msn_sb_command( struct msn_handler_data *handler, char **cmd, int num_parts )
{
- struct msn_switchboard *sb = data;
+ struct msn_switchboard *sb = handler->data;
struct im_connection *ic = sb->ic;
- char buf[1024];
if( !num_parts )
{
@@ -406,7 +410,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 );
@@ -419,20 +423,15 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )
}
if( sb->who )
- {
- g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, sb->who );
- return( msn_sb_write( sb, buf, strlen( buf ) ) );
- }
+ return msn_sb_write( sb, "CAL %d %s\r\n", ++sb->trId, sb->who );
else
- {
debug( "Just created a switchboard, but I don't know what to do with it." );
- }
}
else if( strcmp( cmd[0], "IRO" ) == 0 )
{
int num, tot;
- if( num_parts != 6 )
+ if( num_parts < 6 )
{
msn_sb_destroy( sb );
return( 0 );
@@ -469,7 +468,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 +487,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 +497,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 +558,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 +623,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 )
{
@@ -660,9 +664,9 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )
return( 1 );
}
-static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts )
+static int msn_sb_message( struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int num_parts )
{
- struct msn_switchboard *sb = data;
+ struct msn_switchboard *sb = handler->data;
struct im_connection *ic = sb->ic;
char *body;
int blen = 0;
@@ -740,8 +744,8 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int
#endif
else if( g_strncasecmp( ct, "application/x-msnmsgrp2p", 24 ) == 0 )
{
- imcb_error( sb->ic, "Cannot receive file from %s: BitlBee does not "
- "support msnmsgrp2p yet.", sb->who );
+ /* Not currently implemented. Don't warn about it since
+ this seems to be used for avatars now. */
g_free( ct );
}
else if( g_strncasecmp( ct, "text/x-msmsgscontrol", 20 ) == 0 )
diff --git a/protocols/msn/soap.c b/protocols/msn/soap.c
new file mode 100644
index 00000000..909f6ad9
--- /dev/null
+++ b/protocols/msn/soap.c
@@ -0,0 +1,1078 @@
+ /********************************************************************\
+ * 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_REAUTH,
+ 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 void msn_soap_free( struct msn_soap_req_data *soap_req );
+static void msn_soap_debug_print( const char *headers, const char *payload );
+
+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 );
+
+ msn_soap_debug_print( http_req, 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( g_slist_find( msn_connections, soap_req->ic ) == NULL )
+ {
+ msn_soap_free( soap_req );
+ return;
+ }
+
+ msn_soap_debug_print( http_req->reply_headers, http_req->reply_body );
+
+ if( http_req->body_size > 0 )
+ {
+ struct xt_parser *parser;
+ struct xt_node *err;
+
+ parser = xt_new( soap_req->xml_parser, soap_req );
+ xt_feed( parser, http_req->reply_body, http_req->body_size );
+ if( http_req->status_code == 500 &&
+ ( err = xt_find_path( parser->root, "soap:Body/soap:Fault/detail/errorcode" ) ) &&
+ err->text_len > 0 )
+ {
+ if( strcmp( err->text, "PassportAuthFail" ) == 0 )
+ {
+ xt_free( parser );
+ st = MSN_SOAP_REAUTH;
+ goto fail;
+ }
+ /* TODO: Handle/report other errors. */
+ }
+
+ xt_handle( parser, NULL, -1 );
+ xt_free( parser );
+ }
+
+ st = soap_req->handle_response( soap_req );
+
+fail:
+ 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 if( st == MSN_SOAP_REAUTH )
+ {
+ struct msn_data *md = soap_req->ic->proto_data;
+
+ if( !( md->flags & MSN_REAUTHING ) )
+ {
+ /* Nonce shouldn't actually be touched for re-auths. */
+ msn_soap_passport_sso_request( soap_req->ic, "blaataap" );
+ md->flags |= MSN_REAUTHING;
+ }
+ md->soapq = g_slist_append( md->soapq, soap_req );
+ }
+ else
+ {
+ soap_req->free_data( soap_req );
+ g_free( soap_req );
+ }
+}
+
+static char *msn_soap_abservice_build( const char *body_fmt, const char *scenario, const char *ticket, ... )
+{
+ va_list params;
+ char *ret, *format, *body;
+
+ format = g_markup_printf_escaped( SOAP_ABSERVICE_PAYLOAD, scenario, ticket );
+
+ va_start( params, ticket );
+ body = g_strdup_vprintf( body_fmt, params );
+ va_end( params );
+
+ ret = g_strdup_printf( format, body );
+ g_free( body );
+ g_free( format );
+
+ return ret;
+}
+
+static void msn_soap_debug_print( const char *headers, const char *payload )
+{
+ char *s;
+
+ if( !getenv( "BITLBEE_DEBUG" ) )
+ return;
+
+ if( ( s = strstr( headers, "\r\n\r\n" ) ) )
+ write( 1, s, s - headers + 4 );
+ else
+ write( 1, headers, strlen( headers ) );
+
+#ifdef DEBUG
+ {
+ struct xt_node *xt = xt_from_string( payload );
+ if( xt )
+ xt_print( xt );
+ xt_free_node( xt );
+ }
+#endif
+}
+
+int msn_soapq_flush( struct im_connection *ic, gboolean resend )
+{
+ struct msn_data *md = ic->proto_data;
+
+ while( md->soapq )
+ {
+ if( resend )
+ msn_soap_send_request( (struct msn_soap_req_data*) md->soapq->data );
+ else
+ msn_soap_free( (struct msn_soap_req_data*) md->soapq->data );
+ md->soapq = g_slist_remove( md->soapq, md->soapq->data );
+ }
+
+ return MSN_SOAP_OK;
+}
+
+static void msn_soap_free( struct msn_soap_req_data *soap_req )
+{
+ soap_req->free_data( soap_req );
+ g_free( soap_req->url );
+ g_free( soap_req->action );
+ g_free( soap_req->payload );
+ g_free( soap_req );
+}
+
+
+/* passport_sso: Authentication MSNP15+ */
+
+struct msn_soap_passport_sso_data
+{
+ char *nonce;
+ char *secret;
+ char *error;
+ char *redirect;
+};
+
+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;
+ struct msn_data *md = ic->proto_data;
+ char pass[MAX_PASSPORT_PWLEN+1];
+
+ if( sd->redirect )
+ {
+ soap_req->url = sd->redirect;
+ sd->redirect = NULL;
+ }
+ else 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 );
+
+ strncpy( pass, ic->acc->pass, MAX_PASSPORT_PWLEN );
+ soap_req->payload = g_markup_printf_escaped( SOAP_PASSPORT_SSO_PAYLOAD,
+ ic->acc->user, pass, md->pp_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 = xt_find_path( node, "../../wst:RequestedProofToken/wst:BinarySecret" ) ) &&
+ p->text )
+ sd->secret = g_strdup( p->text );
+
+ *id -= '1';
+ if( *id >= 0 && *id < sizeof( md->tokens ) / sizeof( md->tokens[0] ) )
+ {
+ g_free( md->tokens[(int)*id] );
+ md->tokens[(int)*id] = g_strdup( node->text );
+ }
+
+ return XT_HANDLED;
+}
+
+static xt_status msn_soap_passport_failure( 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 xt_node *code = xt_find_node( node->children, "faultcode" );
+ struct xt_node *string = xt_find_node( node->children, "faultstring" );
+ struct xt_node *url;
+
+ if( code == NULL || code->text_len == 0 )
+ sd->error = g_strdup( "Unknown error" );
+ else if( strcmp( code->text, "psf:Redirect" ) == 0 &&
+ ( url = xt_find_node( node->children, "psf:redirectUrl" ) ) &&
+ url->text_len > 0 )
+ sd->redirect = g_strdup( url->text );
+ else
+ sd->error = g_strdup_printf( "%s (%s)", code->text, string && string->text_len ?
+ string->text : "no description available" );
+
+ return XT_HANDLED;
+}
+
+static const struct xt_handler_entry msn_soap_passport_sso_parser[] = {
+ { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token },
+ { "S:Fault", "S:Envelope", msn_soap_passport_failure },
+ { 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;
+ struct msn_data *md = ic->proto_data;
+ 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 ),
+ };
+
+ if( sd->redirect )
+ return MSN_SOAP_RETRY;
+
+ if( md->soapq )
+ return msn_soapq_flush( ic, TRUE );
+
+ if( sd->secret == NULL )
+ {
+ msn_auth_got_passport_token( ic, NULL, sd->error );
+ return MSN_SOAP_OK;
+ }
+
+ 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, NULL );
+
+ 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->nonce );
+ g_free( sd->secret );
+ g_free( sd->error );
+ g_free( sd->redirect );
+
+ return MSN_SOAP_OK;
+}
+
+int msn_soap_passport_sso_request( struct im_connection *ic, const char *nonce )
+{
+ struct msn_soap_passport_sso_data *sd = g_new0( struct msn_soap_passport_sso_data, 1 );
+
+ 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;
+ msn_soap_result_t 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 );
+ oim->need_retry = MSN_SOAP_OK;
+
+ return MSN_SOAP_OK;
+}
+
+static xt_status msn_soap_oim_reauth( 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;
+ struct xt_node *c;
+
+ if( ( c = xt_find_node( node->children, "LockKeyChallenge" ) ) && c->text_len > 0 )
+ {
+ g_free( md->lock_key );
+ md->lock_key = msn_p11_challenge( c->text );
+ oim->need_retry = MSN_SOAP_RETRY;
+ }
+ if( xt_find_node( node->children, "RequiredAuthPolicy" ) )
+ {
+ oim->need_retry = MSN_SOAP_REAUTH;
+ }
+
+ return XT_HANDLED;
+}
+
+static const struct xt_handler_entry msn_soap_oim_send_parser[] = {
+ { "detail", "soap:Fault", msn_soap_oim_reauth },
+ { 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 )
+ {
+ return oim->need_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 = msn_soap_abservice_build( SOAP_MEMLIST_PAYLOAD, "Initial", 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 = xt_find_path( node, "../../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;
+ ic->permit = g_slist_prepend( ic->permit, g_strdup( handle ) );
+ }
+ else if( strcmp( role, "Block" ) == 0 )
+ {
+ bd->flags |= MSN_BUDDY_BL;
+ ic->deny = g_slist_prepend( ic->deny, g_strdup( handle ) );
+ }
+ else if( strcmp( role, "Reverse" ) == 0 )
+ bd->flags |= MSN_BUDDY_RL;
+ else if( strcmp( role, "Pending" ) == 0 )
+ bd->flags |= MSN_BUDDY_PL;
+
+ if( getenv( "BITLBEE_DEBUG" ) )
+ printf( "%p %s %d\n", bu, handle, bd->flags );
+
+ 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 );
+}
+
+/* Variant: Adding/Removing people */
+struct msn_soap_memlist_edit_data
+{
+ char *handle;
+ gboolean add;
+ msn_buddy_flags_t list;
+};
+
+static int msn_soap_memlist_edit_build_request( struct msn_soap_req_data *soap_req )
+{
+ struct msn_data *md = soap_req->ic->proto_data;
+ struct msn_soap_memlist_edit_data *med = soap_req->data;
+ char *add, *scenario, *list;
+
+ soap_req->url = g_strdup( SOAP_MEMLIST_URL );
+ if( med->add )
+ {
+ soap_req->action = g_strdup( SOAP_MEMLIST_ADD_ACTION );
+ add = "Add";
+ }
+ else
+ {
+ soap_req->action = g_strdup( SOAP_MEMLIST_DEL_ACTION );
+ add = "Delete";
+ }
+ switch( med->list )
+ {
+ case MSN_BUDDY_AL:
+ scenario = "BlockUnblock";
+ list = "Allow";
+ break;
+ case MSN_BUDDY_BL:
+ scenario = "BlockUnblock";
+ list = "Block";
+ break;
+ case MSN_BUDDY_RL:
+ scenario = "Timer";
+ list = "Reverse";
+ break;
+ case MSN_BUDDY_PL:
+ default:
+ scenario = "Timer";
+ list = "Pending";
+ break;
+ }
+ soap_req->payload = msn_soap_abservice_build( SOAP_MEMLIST_EDIT_PAYLOAD,
+ scenario, md->tokens[1], add, list, med->handle, add );
+
+ return 1;
+}
+
+static int msn_soap_memlist_edit_handle_response( struct msn_soap_req_data *soap_req )
+{
+ return MSN_SOAP_OK;
+}
+
+static int msn_soap_memlist_edit_free_data( struct msn_soap_req_data *soap_req )
+{
+ struct msn_soap_memlist_edit_data *med = soap_req->data;
+
+ g_free( med->handle );
+ g_free( med );
+
+ return 0;
+}
+
+int msn_soap_memlist_edit( struct im_connection *ic, const char *handle, gboolean add, int list )
+{
+ struct msn_soap_memlist_edit_data *med;
+
+ med = g_new0( struct msn_soap_memlist_edit_data, 1 );
+ med->handle = g_strdup( handle );
+ med->add = add;
+ med->list = list;
+
+ return msn_soap_start( ic, med, msn_soap_memlist_edit_build_request,
+ NULL,
+ msn_soap_memlist_edit_handle_response,
+ msn_soap_memlist_edit_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 = msn_soap_abservice_build( SOAP_ADDRESSBOOK_PAYLOAD, "Initial", 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;
+ struct msn_data *md = soap_req->ic->proto_data;
+
+ if( ( p = xt_find_path( node, "../groupId" ) ) )
+ id = p->text;
+
+ if( ( p = xt_find_node( node->children, "name" ) ) )
+ name = p->text;
+
+ if( id && name )
+ {
+ struct msn_group *mg = g_new0( struct msn_group, 1 );
+ mg->id = g_strdup( id );
+ mg->name = g_strdup( name );
+ md->groups = g_slist_prepend( md->groups, mg );
+ }
+
+ if( getenv( "BITLBEE_DEBUG" ) )
+ 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, *is_msgr = "false",
+ *display_name = NULL, *group_id = NULL;
+ struct msn_soap_req_data *soap_req = data;
+ struct im_connection *ic = soap_req->ic;
+ struct msn_group *group;
+
+ if( ( p = xt_find_path( node, "../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( ( p = xt_find_node( node->children, "isMessengerUser" ) ) )
+ is_msgr = p->text;
+ if( ( p = xt_find_path( node, "groupIds/guid" ) ) )
+ group_id = 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 );
+
+ /* Try to fetch the profile; if the user has one, that's where
+ we can find the persistent display_name. */
+ if( ( p = xt_find_node( node->children, "CID" ) ) && p->text )
+ msn_soap_profile_get( ic, p->text );
+
+ return XT_HANDLED;
+ }
+
+ if( !bool2int( is_msgr ) || handle == NULL )
+ 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 );
+
+ if( group_id && ( group = msn_group_by_id( ic, group_id ) ) )
+ imcb_add_buddy( ic, handle, group->name );
+
+ if( getenv( "BITLBEE_DEBUG" ) )
+ 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 )
+{
+ GSList *l;
+
+ for( l = soap_req->ic->bee->users; l; l = l->next )
+ {
+ struct bee_user *bu = l->data;
+
+ if( bu->ic == soap_req->ic )
+ msn_buddy_ask( bu );
+ }
+
+ 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 = msn_soap_abservice_build( SOAP_AB_NAMECHANGE_PAYLOAD,
+ "Timer", 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 );
+}
+
+/* Add a contact. */
+static int msn_soap_ab_contact_add_build_request( struct msn_soap_req_data *soap_req )
+{
+ struct msn_data *md = soap_req->ic->proto_data;
+ bee_user_t *bu = soap_req->data;
+
+ soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
+ soap_req->action = g_strdup( SOAP_AB_CONTACT_ADD_ACTION );
+ soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_ADD_PAYLOAD,
+ "ContactSave", md->tokens[1], bu->handle, bu->fullname ? bu->fullname : bu->handle );
+
+ return 1;
+}
+
+static xt_status msn_soap_ab_contact_add_cid( struct xt_node *node, gpointer data )
+{
+ struct msn_soap_req_data *soap_req = data;
+ bee_user_t *bu = soap_req->data;
+ struct msn_buddy_data *bd = bu->data;
+
+ g_free( bd->cid );
+ bd->cid = g_strdup( node->text );
+
+ return XT_HANDLED;
+}
+
+static const struct xt_handler_entry msn_soap_ab_contact_add_parser[] = {
+ { "guid", "ABContactAddResult", msn_soap_ab_contact_add_cid },
+ { NULL, NULL, NULL }
+};
+
+static int msn_soap_ab_contact_add_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_contact_add_free_data( struct msn_soap_req_data *soap_req )
+{
+ return 0;
+}
+
+int msn_soap_ab_contact_add( struct im_connection *ic, bee_user_t *bu )
+{
+ return msn_soap_start( ic, bu,
+ msn_soap_ab_contact_add_build_request,
+ msn_soap_ab_contact_add_parser,
+ msn_soap_ab_contact_add_handle_response,
+ msn_soap_ab_contact_add_free_data );
+}
+
+/* Remove a contact. */
+static int msn_soap_ab_contact_del_build_request( struct msn_soap_req_data *soap_req )
+{
+ struct msn_data *md = soap_req->ic->proto_data;
+ const char *cid = soap_req->data;
+
+ soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
+ soap_req->action = g_strdup( SOAP_AB_CONTACT_DEL_ACTION );
+ soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_DEL_PAYLOAD,
+ "Timer", md->tokens[1], cid );
+
+ return 1;
+}
+
+static int msn_soap_ab_contact_del_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_contact_del_free_data( struct msn_soap_req_data *soap_req )
+{
+ g_free( soap_req->data );
+ return 0;
+}
+
+int msn_soap_ab_contact_del( struct im_connection *ic, bee_user_t *bu )
+{
+ struct msn_buddy_data *bd = bu->data;
+
+ return msn_soap_start( ic, g_strdup( bd->cid ),
+ msn_soap_ab_contact_del_build_request,
+ NULL,
+ msn_soap_ab_contact_del_handle_response,
+ msn_soap_ab_contact_del_free_data );
+}
+
+
+
+/* Storage stuff: Fetch profile. */
+static int msn_soap_profile_get_build_request( struct msn_soap_req_data *soap_req )
+{
+ struct msn_data *md = soap_req->ic->proto_data;
+
+ soap_req->url = g_strdup( SOAP_STORAGE_URL );
+ soap_req->action = g_strdup( SOAP_PROFILE_GET_ACTION );
+ soap_req->payload = g_markup_printf_escaped( SOAP_PROFILE_GET_PAYLOAD,
+ md->tokens[3], (char*) soap_req->data );
+
+ return 1;
+}
+
+static xt_status msn_soap_profile_get_result( struct xt_node *node, gpointer data )
+{
+ struct msn_soap_req_data *soap_req = data;
+ struct im_connection *ic = soap_req->ic;
+ struct msn_data *md = soap_req->ic->proto_data;
+ struct xt_node *dn;
+
+ if( ( dn = xt_find_node( node->children, "DisplayName" ) ) && dn->text )
+ {
+ set_t *set = set_find( &ic->acc->set, "display_name" );
+ g_free( set->value );
+ set->value = g_strdup( dn->text );
+
+ md->flags |= MSN_GOT_PROFILE_DN;
+ }
+
+ return XT_HANDLED;
+}
+
+static const struct xt_handler_entry msn_soap_profile_get_parser[] = {
+ { "ExpressionProfile", "GetProfileResult", msn_soap_profile_get_result },
+ { NULL, NULL, NULL }
+};
+
+static int msn_soap_profile_get_handle_response( struct msn_soap_req_data *soap_req )
+{
+ struct msn_data *md = soap_req->ic->proto_data;
+
+ md->flags |= MSN_GOT_PROFILE;
+ msn_ns_finish_login( soap_req->ic );
+
+ return MSN_SOAP_OK;
+}
+
+static int msn_soap_profile_get_free_data( struct msn_soap_req_data *soap_req )
+{
+ g_free( soap_req->data );
+ return 0;
+}
+
+int msn_soap_profile_get( struct im_connection *ic, const char *cid )
+{
+ return msn_soap_start( ic, g_strdup( cid ),
+ msn_soap_profile_get_build_request,
+ msn_soap_profile_get_parser,
+ msn_soap_profile_get_handle_response,
+ msn_soap_profile_get_free_data );
+}
diff --git a/protocols/msn/soap.h b/protocols/msn/soap.h
new file mode 100644
index 00000000..fccdebc6
--- /dev/null
+++ b/protocols/msn/soap.h
@@ -0,0 +1,350 @@
+ /********************************************************************\
+ * 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"
+
+
+int msn_soapq_flush( struct im_connection *ic, gboolean resend );
+
+
+#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/pp800/RST.srf"
+#define MAX_PASSPORT_PWLEN 16
+
+#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>" \
+ "<wst:RequestSecurityToken Id=\"RST4\">" \
+ "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
+ "<wsp:AppliesTo>" \
+ "<wsa:EndpointReference>" \
+ "<wsa:Address>storage.msn.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 *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_ABSERVICE_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\">%s</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/\">" \
+ "%%s" \
+ "</soap:Body>" \
+"</soap:Envelope>"
+
+#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 \
+ "<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>"
+
+#define SOAP_MEMLIST_ADD_ACTION "http://www.msn.com/webservices/AddressBook/AddMember"
+#define SOAP_MEMLIST_DEL_ACTION "http://www.msn.com/webservices/AddressBook/DeleteMember"
+
+#define SOAP_MEMLIST_EDIT_PAYLOAD \
+ "<%sMember xmlns=\"http://www.msn.com/webservices/AddressBook\">" \
+ "<serviceHandle>" \
+ "<Id>0</Id>" \
+ "<Type>Messenger</Type>" \
+ "<ForeignId></ForeignId>" \
+ "</serviceHandle>" \
+ "<memberships>" \
+ "<Membership>" \
+ "<MemberRole>%s</MemberRole>" \
+ "<Members>" \
+ "<Member xsi:type=\"PassportMember\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" \
+ "<Type>Passport</Type>" \
+ "<State>Accepted</State>" \
+ "<PassportName>%s</PassportName>" \
+ "</Member>" \
+ "</Members>" \
+ "</Membership>" \
+ "</memberships>" \
+ "</%sMember>"
+
+int msn_soap_memlist_request( struct im_connection *ic );
+int msn_soap_memlist_edit( struct im_connection *ic, const char *handle, gboolean add, int list );
+
+
+#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 \
+ "<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>"
+
+#define SOAP_AB_NAMECHANGE_ACTION "http://www.msn.com/webservices/AddressBook/ABContactUpdate"
+
+#define SOAP_AB_NAMECHANGE_PAYLOAD \
+ "<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>"
+
+#define SOAP_AB_CONTACT_ADD_ACTION "http://www.msn.com/webservices/AddressBook/ABContactAdd"
+
+#define SOAP_AB_CONTACT_ADD_PAYLOAD \
+ "<ABContactAdd 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>LivePending</contactType>" \
+ "<passportName>%s</passportName>" \
+ "<isMessengerUser>true</isMessengerUser>" \
+ "<MessengerMemberInfo>" \
+ "<DisplayName>%s</DisplayName>" \
+ "</MessengerMemberInfo>" \
+ "</contactInfo>" \
+ "</Contact>" \
+ "</contacts>" \
+ "<options>" \
+ "<EnableAllowListManagement>true</EnableAllowListManagement>" \
+ "</options>" \
+ "</ABContactAdd>"
+
+#define SOAP_AB_CONTACT_DEL_ACTION "http://www.msn.com/webservices/AddressBook/ABContactDelete"
+
+#define SOAP_AB_CONTACT_DEL_PAYLOAD \
+ "<ABContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\">" \
+ "<abId>00000000-0000-0000-0000-000000000000</abId>" \
+ "<contacts>" \
+ "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">" \
+ "<contactId>%s</contactId>" \
+ "</Contact>" \
+ "</contacts>" \
+ "</ABContactDelete>"
+
+int msn_soap_addressbook_request( struct im_connection *ic );
+int msn_soap_addressbook_set_display_name( struct im_connection *ic, const char *new );
+int msn_soap_ab_contact_add( struct im_connection *ic, bee_user_t *bu );
+int msn_soap_ab_contact_del( struct im_connection *ic, bee_user_t *bu );
+
+
+#define SOAP_STORAGE_URL "https://storage.msn.com/storageservice/SchematizedStore.asmx"
+#define SOAP_PROFILE_GET_ACTION "http://www.msn.com/webservices/storage/w10/GetProfile"
+
+#define SOAP_PROFILE_GET_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/\">" \
+ "<StorageApplicationHeader xmlns=\"http://www.msn.com/webservices/storage/w10\">" \
+ "<ApplicationID>Messenger Client 9.0</ApplicationID>" \
+ "<Scenario>Initial</Scenario>" \
+ "</StorageApplicationHeader>" \
+ "<StorageUserHeader xmlns=\"http://www.msn.com/webservices/storage/w10\">" \
+ "<Puid>0</Puid>" \
+ "<TicketToken>%s</TicketToken>" \
+ "</StorageUserHeader>" \
+ "</soap:Header>" \
+ "<soap:Body xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \
+ "<GetProfile xmlns=\"http://www.msn.com/webservices/storage/w10\">" \
+ "<profileHandle>" \
+ "<Alias>" \
+ "<Name>%s</Name>" \
+ "<NameSpace>MyCidStuff</NameSpace>" \
+ "</Alias>" \
+ "<RelationshipName>MyProfile</RelationshipName>" \
+ "</profileHandle>" \
+ "<profileAttributes>" \
+ "<ResourceID>true</ResourceID>" \
+ "<DateModified>true</DateModified>" \
+ "<ExpressionProfileAttributes>" \
+ "<ResourceID>true</ResourceID>" \
+ "<DateModified>true</DateModified>" \
+ "<DisplayName>true</DisplayName>" \
+ "<DisplayNameLastModified>true</DisplayNameLastModified>" \
+ "<PersonalStatus>true</PersonalStatus>" \
+ "<PersonalStatusLastModified>true</PersonalStatusLastModified>" \
+ "<StaticUserTilePublicURL>true</StaticUserTilePublicURL>" \
+ "<Photo>true</Photo>" \
+ "<Flags>true</Flags>" \
+ "</ExpressionProfileAttributes>" \
+ "</profileAttributes>" \
+ "</GetProfile>" \
+ "</soap:Body>" \
+"</soap:Envelope>"
+
+int msn_soap_profile_get( struct im_connection *ic, const char *cid );
+
+
+#endif /* __SOAP_H__ */
diff --git a/protocols/msn/tables.c b/protocols/msn/tables.c
index 42b12aa9..273d291e 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 */
@@ -82,6 +82,8 @@ const struct msn_status_code msn_status_code_list[] =
{ 229, "Group name too long", 0 },
{ 230, "Cannot remove that group", 0 },
{ 231, "Invalid group", 0 },
+ { 240, "ADL/RML command with corrupted payload", STATUS_FATAL },
+ { 241, "ADL/RML command with invalid modification", 0 },
{ 280, "Switchboard failed", STATUS_SB_FATAL },
{ 281, "Transfer to switchboard failed", 0 },
diff --git a/protocols/nogaim.c b/protocols/nogaim.c
index 7943e026..aa3ad5bb 100644
--- a/protocols/nogaim.c
+++ b/protocols/nogaim.c
@@ -251,7 +251,7 @@ void imcb_error( struct im_connection *ic, char *format, ... )
if( ic->flags & OPT_LOGGED_IN )
serv_got_crap( ic, "Error: %s", text );
else
- serv_got_crap( ic, "Couldn't log in: %s", text );
+ serv_got_crap( ic, "Login error: %s", text );
g_free( text );
}
@@ -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 cf6cdc30..a09a3f67 100644
--- a/protocols/nogaim.h
+++ b/protocols/nogaim.h
@@ -199,6 +199,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/protocols/oscar/ssi.c b/protocols/oscar/ssi.c
index 76b5b427..f37d98e5 100644
--- a/protocols/oscar/ssi.c
+++ b/protocols/oscar/ssi.c
@@ -414,7 +414,7 @@ int aim_ssi_cleanlist(aim_session_t *sess, aim_conn_t *conn)
for (parentgroup=sess->ssi.items; ((parentgroup) && (parentgroup->type!=AIM_SSI_TYPE_GROUP) && (parentgroup->gid==0x0000)); parentgroup=parentgroup->next);
if (!parentgroup) {
char *newgroup;
- newgroup = (char*)g_malloc(strlen("Unknown")*sizeof(char));
+ newgroup = (char*)g_malloc(strlen("Unknown")+1);
strcpy(newgroup, "Unknown");
aim_ssi_addgroups(sess, conn, &newgroup, 1);
}
diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c
index 5522c0fc..d38f99fb 100644
--- a/protocols/purple/purple.c
+++ b/protocols/purple/purple.c
@@ -79,6 +79,28 @@ static void purple_init( account_t *acc )
set_t *s;
char help_title[64];
GString *help;
+ static gboolean dir_fixed = FALSE;
+
+ /* Layer violation coming up: Making an exception for libpurple here.
+ Dig in the IRC state a bit to get a username. Ideally we should
+ check if s/he identified but this info doesn't seem *that* important.
+ It's just that fecking libpurple can't *not* store this shit.
+
+ Remember that libpurple is not really meant to be used on public
+ servers anyway! */
+ if( !dir_fixed )
+ {
+ irc_t *irc = acc->bee->ui_data;
+ char *dir;
+
+ dir = g_strdup_printf( "%s/purple/%s", global.conf->configdir, irc->user->nick );
+ purple_util_set_user_dir( dir );
+ g_free( dir );
+
+ purple_blist_load();
+ purple_prefs_load();
+ dir_fixed = TRUE;
+ }
help = g_string_new( "" );
g_string_printf( help, "BitlBee libpurple module %s (%s).\n\nSupported settings:",
@@ -253,7 +275,8 @@ static void purple_login( account_t *acc )
struct im_connection *ic = imcb_new( acc );
PurpleAccount *pa;
- if( local_bee != NULL && local_bee != acc->bee )
+ if( ( local_bee != NULL && local_bee != acc->bee ) ||
+ ( global.conf->runmode == RUNMODE_DAEMON && !getenv( "BITLBEE_DEBUG" ) ) )
{
imcb_error( ic, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! "
"Please use inetd or ForkDaemon mode instead." );
@@ -351,6 +374,9 @@ static char *set_eval_display_name( set_t *set, char *value )
account_t *acc = set->data;
struct im_connection *ic = acc->ic;
+ if( ic )
+ imcb_log( ic, "Changing display_name not currently supported with libpurple!" );
+
return NULL;
}
@@ -516,7 +542,7 @@ void purple_chat_leave( struct groupchat *gc )
purple_conversation_destroy( pc );
}
-struct groupchat *purple_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password )
+struct groupchat *purple_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password, set_t **sets )
{
PurpleAccount *pa = ic->proto_data;
PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id );
@@ -1131,6 +1157,7 @@ void purple_initmodule()
struct prpl funcs;
GList *prots;
GString *help;
+ char *dir;
if( B_EV_IO_READ != PURPLE_INPUT_READ ||
B_EV_IO_WRITE != PURPLE_INPUT_WRITE )
@@ -1139,7 +1166,10 @@ void purple_initmodule()
exit( 1 );
}
- purple_util_set_user_dir( "/tmp" );
+ dir = g_strdup_printf( "%s/purple", global.conf->configdir );
+ purple_util_set_user_dir( dir );
+ g_free( dir );
+
purple_debug_set_enabled( FALSE );
purple_core_set_ui_ops( &bee_core_uiops );
purple_eventloop_set_ui_ops( &glib_eventloops );
@@ -1150,12 +1180,7 @@ void purple_initmodule()
abort();
}
- /* This seems like stateful shit we don't want... */
purple_set_blist( purple_blist_new() );
- purple_blist_load();
-
- /* Meh? */
- purple_prefs_load();
/* No, really. So far there were ui_ops for everything, but now suddenly
one needs to use signals for typing notification stuff. :-( */
@@ -1220,7 +1245,8 @@ void purple_initmodule()
}
g_string_append( help, "\n\nFor used protocols, more information about available "
- "settings can be found using \x02help purple <protocol name>\x02" );
+ "settings can be found using \x02help purple <protocol name>\x02 "
+ "(create an account using that protocol first!)" );
/* Add a simple dynamically-generated help item listing all
the supported protocols. */
diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c
index 1bc596eb..4ece97db 100644
--- a/protocols/twitter/twitter.c
+++ b/protocols/twitter/twitter.c
@@ -28,6 +28,16 @@
#include "twitter_lib.h"
#include "url.h"
+#define twitter_msg( ic, fmt... ) \
+ do { \
+ struct twitter_data *td = ic->proto_data; \
+ if( td->home_timeline_gc ) \
+ imcb_chat_log( td->home_timeline_gc, fmt ); \
+ else \
+ imcb_log( ic, fmt ); \
+ } while( 0 );
+
+
/**
* Main loop function
*/
@@ -435,6 +445,8 @@ static void twitter_handle_command( struct im_connection *ic, char *message )
/* TODO: User feedback. */
if( id )
twitter_status_destroy( ic, id );
+ else
+ twitter_msg( ic, "Could not undo last action" );
g_free( cmds );
return;
@@ -466,6 +478,9 @@ static void twitter_handle_command( struct im_connection *ic, char *message )
td->last_status_id = 0;
if( id )
twitter_status_retweet( ic, id );
+ else
+ twitter_msg( ic, "User `%s' does not exist or didn't "
+ "post any statuses recently", cmd[1] );
g_free( cmds );
return;