diff libpurple/protocols/msn/nexus.c @ 20685:02df6998b466

propagate from branch 'im.pidgin.rlaager.merging.2_2_1_conflicts' (head 4ad1081695d083df424898e6e7091f731b401265) to branch 'im.pidgin.pidgin' (head d33243e8f5347776c81f81a0e4ba3a76ae5505a5)
author Richard Laager <rlaager@wiktel.com>
date Fri, 28 Sep 2007 16:34:43 +0000
parents 2c8c6d77f12c
children 48ee7ec3426d
line wrap: on
line diff
--- a/libpurple/protocols/msn/nexus.c	Fri Sep 28 16:32:28 2007 +0000
+++ b/libpurple/protocols/msn/nexus.c	Fri Sep 28 16:34:43 2007 +0000
@@ -22,9 +22,15 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 #include "msn.h"
+#include "soap.h"
 #include "nexus.h"
 #include "notification.h"
 
+#undef NEXUS_LOGIN_TWN
+
+/*Local Function Prototype*/
+static gboolean nexus_login_connect_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc);
+
 /**************************************************************************
  * Main
  **************************************************************************/
@@ -36,6 +42,9 @@
 
 	nexus = g_new0(MsnNexus, 1);
 	nexus->session = session;
+	/*we must use SSL connection to do Windows Live ID authentication*/
+	nexus->soapconn = msn_soap_new(session,nexus,1);
+
 	nexus->challenge_data = g_hash_table_new_full(g_str_hash,
 		g_str_equal, g_free, g_free);
 
@@ -45,24 +54,14 @@
 void
 msn_nexus_destroy(MsnNexus *nexus)
 {
-	if (nexus->gsc)
-		purple_ssl_close(nexus->gsc);
-
-	g_free(nexus->login_host);
-
-	g_free(nexus->login_path);
-
 	if (nexus->challenge_data != NULL)
 		g_hash_table_destroy(nexus->challenge_data);
 
-	if (nexus->input_handler > 0)
-		purple_input_remove(nexus->input_handler);
-	g_free(nexus->write_buf);
-	g_free(nexus->read_buf);
-
+	msn_soap_destroy(nexus->soapconn);
 	g_free(nexus);
 }
 
+#if 0 /* khc */
 /**************************************************************************
  * Util
  **************************************************************************/
@@ -121,285 +120,237 @@
 	nexus->written_cb(nexus, source, 0);
 }
 
+#endif
 /**************************************************************************
  * Login
  **************************************************************************/
-
 static void
-login_connect_cb(gpointer data, PurpleSslConnection *gsc,
-				 PurpleInputCondition cond);
-
-static void
-login_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
+nexus_login_error_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc, PurpleSslErrorType error)
 {
-	MsnNexus *nexus;
 	MsnSession *session;
 
-	nexus = data;
-	g_return_if_fail(nexus != NULL);
-
-	nexus->gsc = NULL;
-
-	session = nexus->session;
+	session = soapconn->session;
 	g_return_if_fail(session != NULL);
 
-	msn_session_set_error(session, MSN_ERROR_AUTH, _("Unable to connect"));
+	soapconn->gsc = NULL;
+
+	msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication:Unable to connect"));
 	/* the above line will result in nexus being destroyed, so we don't want
 	 * to destroy it here, or we'd crash */
 }
 
-static void
-nexus_login_written_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	MsnNexus *nexus = data;
-	MsnSession *session;
-	int len;
-
-	session = nexus->session;
-	g_return_if_fail(session != NULL);
-
-	if (nexus->input_handler == 0)
-		/* TODO: Use purple_ssl_input_add()? */
-		nexus->input_handler = purple_input_add(nexus->gsc->fd,
-			PURPLE_INPUT_READ, nexus_login_written_cb, nexus);
-
-
-	len = msn_ssl_read(nexus);
-
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len < 0) {
-		purple_input_remove(nexus->input_handler);
-		nexus->input_handler = 0;
-		g_free(nexus->read_buf);
-		nexus->read_buf = NULL;
-		nexus->read_len = 0;
-		/* TODO: error handling */
-		return;
-	}
-
-	if (g_strstr_len(nexus->read_buf, nexus->read_len,
-			"\r\n\r\n") == NULL)
-		return;
-
-	purple_input_remove(nexus->input_handler);
-	nexus->input_handler = 0;
-
-	purple_ssl_close(nexus->gsc);
-	nexus->gsc = NULL;
-
-	purple_debug_misc("msn", "ssl buffer: {%s}\n", nexus->read_buf);
-
-	if (strstr(nexus->read_buf, "HTTP/1.1 302") != NULL)
-	{
-		/* Redirect. */
-		char *location, *c;
-
-		location = strstr(nexus->read_buf, "Location: ");
-		if (location == NULL)
-		{
-			g_free(nexus->read_buf);
-			nexus->read_buf = NULL;
-			nexus->read_len = 0;
-
-			return;
-		}
-		location = strchr(location, ' ') + 1;
-
-		if ((c = strchr(location, '\r')) != NULL)
-			*c = '\0';
-
-		/* Skip the http:// */
-		if ((c = strchr(location, '/')) != NULL)
-			location = c + 2;
-
-		if ((c = strchr(location, '/')) != NULL)
-		{
-			g_free(nexus->login_path);
-			nexus->login_path = g_strdup(c);
-
-			*c = '\0';
-		}
-
-		g_free(nexus->login_host);
-		nexus->login_host = g_strdup(location);
-
-		nexus->gsc = purple_ssl_connect(session->account,
-				nexus->login_host, PURPLE_SSL_DEFAULT_PORT,
-				login_connect_cb, login_error_cb, nexus);
-	}
-	else if (strstr(nexus->read_buf, "HTTP/1.1 401 Unauthorized") != NULL)
-	{
-		const char *error;
-
-		if ((error = strstr(nexus->read_buf, "WWW-Authenticate")) != NULL)
-		{
-			if ((error = strstr(error, "cbtxt=")) != NULL)
-			{
-				const char *c;
-				char *temp;
-
-				error += strlen("cbtxt=");
-
-				if ((c = strchr(error, '\n')) == NULL)
-					c = error + strlen(error);
-
-				temp = g_strndup(error, c - error);
-				error = purple_url_decode(temp);
-				g_free(temp);
-				if ((temp = strstr(error, " Do one of the following or try again:")) != NULL)
-					*temp = '\0';
-			}
-		}
-
-		msn_session_set_error(session, MSN_ERROR_AUTH, error);
-	}
-	else if (strstr(nexus->read_buf, "HTTP/1.1 503 Service Unavailable"))
-	{
-		msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL);
-	}
-	else if (strstr(nexus->read_buf, "HTTP/1.1 200 OK"))
-	{
-		char *base, *c;
-		char *login_params;
-
-#if 0
-		/* All your base are belong to us. */
-		base = buffer;
-
-		/* For great cookie! */
-		while ((base = strstr(base, "Set-Cookie: ")) != NULL)
-		{
-			base += strlen("Set-Cookie: ");
-
-			c = strchr(base, ';');
-
-			session->login_cookies =
-				g_list_append(session->login_cookies,
-							  g_strndup(base, c - base));
-		}
-#endif
-
-		base  = strstr(nexus->read_buf, "Authentication-Info: ");
-
-		g_return_if_fail(base != NULL);
-
-		base  = strstr(base, "from-PP='");
-		base += strlen("from-PP='");
-		c     = strchr(base, '\'');
-
-		login_params = g_strndup(base, c - base);
-
-		msn_got_login_params(session, login_params);
-
-		g_free(login_params);
-
-		msn_nexus_destroy(nexus);
-		session->nexus = NULL;
-		return;
-	}
-
-	g_free(nexus->read_buf);
-	nexus->read_buf = NULL;
-	nexus->read_len = 0;
-
-}
-
-/* this guards against missing hash entries */
-static char *
-nexus_challenge_data_lookup(GHashTable *challenge_data, const char *key)
-{
-	char *entry;
-
-	return (entry = (char *)g_hash_table_lookup(challenge_data, key)) ?
-		entry : "(null)";
-}
-
-void
-login_connect_cb(gpointer data, PurpleSslConnection *gsc,
-				 PurpleInputCondition cond)
+/*process the SOAP reply, get the Authentication Info*/
+static gboolean
+nexus_login_read_cb(MsnSoapConn *soapconn)
 {
 	MsnNexus *nexus;
 	MsnSession *session;
+
+	char *base, *c;
+	char *msn_twn_t,*msn_twn_p;
+	char *login_params;
+	char **elems, **cur, **tokens;
+	char * cert_str;
+
+	nexus = soapconn->parent;
+	g_return_val_if_fail(nexus != NULL, TRUE);
+	session = nexus->session;
+	g_return_val_if_fail(session != NULL, FALSE);
+
+	/*reply OK, we should process the SOAP body*/
+	purple_debug_info("MSN Nexus","TWN Server Windows Live ID Reply OK!\n");
+
+	//TODO: we should parse it using XML
+#ifdef NEXUS_LOGIN_TWN
+	base  = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_START_TOKEN);
+	base += strlen(TWN_START_TOKEN);
+	c     = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_END_TOKEN);
+#else
+	base  = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_LIVE_START_TOKEN);
+	base += strlen(TWN_LIVE_START_TOKEN);
+	c     = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_LIVE_END_TOKEN);
+#endif
+	login_params = g_strndup(base, c - base);
+
+	//		purple_debug_info("msn", "TWN Cert: {%s}\n", login_params);
+
+	/* Parse the challenge data. */
+	elems = g_strsplit(login_params, "&amp;", 0);
+
+	for (cur = elems; *cur != NULL; cur++){
+			tokens = g_strsplit(*cur, "=", 2);
+			g_hash_table_insert(nexus->challenge_data, tokens[0], tokens[1]);
+			/* Don't free each of the tokens, only the array. */
+			g_free(tokens);
+	}
+
+	g_strfreev(elems);
+
+	msn_twn_t = (char *)g_hash_table_lookup(nexus->challenge_data, "t");
+	msn_twn_p = (char *)g_hash_table_lookup(nexus->challenge_data, "p");
+
+	/*setup the t and p parameter for session*/
+	if (session->passport_info.t != NULL){
+			g_free(session->passport_info.t);
+	}
+	session->passport_info.t = g_strdup(msn_twn_t);
+
+	if (session->passport_info.p != NULL)
+			g_free(session->passport_info.p);
+	session->passport_info.p = g_strdup(msn_twn_p);
+
+	cert_str = g_strdup_printf("t=%s&p=%s",msn_twn_t,msn_twn_p);
+	msn_got_login_params(session, cert_str);
+
+	purple_debug_info("MSN Nexus","Close nexus connection!\n");
+	g_free(cert_str);
+	g_free(login_params);
+	msn_nexus_destroy(nexus);
+	session->nexus = NULL;
+
+	return FALSE;
+}
+
+static void
+nexus_login_written_cb(MsnSoapConn *soapconn)
+{
+	soapconn->read_cb = nexus_login_read_cb;
+//	msn_soap_read_cb(data,source,cond);
+}
+
+
+/*when connect, do the SOAP Style windows Live ID authentication */
+gboolean
+nexus_login_connect_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc)
+{
+	MsnNexus * nexus;
+	MsnSession *session;
+	char *ru,*lc,*id,*tw,*ct,*kpp,*kv,*ver,*rn,*tpf;
+	char *fs0,*fs;
 	char *username, *password;
 	char *request_str, *head, *tail;
-	char *buffer = NULL;
-	guint32 ctint;
+#ifdef NEXUS_LOGIN_TWN
+	char *challenge_str;
+#else
+	char *rst1_str,*rst2_str,*rst3_str;
+#endif
+	
+	purple_debug_info("MSN Nexus","Starting Windows Live ID authentication\n");
 
-	nexus = data;
-	g_return_if_fail(nexus != NULL);
+	g_return_val_if_fail(soapconn != NULL, FALSE);
+
+	nexus = soapconn->parent;
+	g_return_val_if_fail(nexus != NULL, TRUE);
 
-	session = nexus->session;
-	g_return_if_fail(session != NULL);
+	session = soapconn->session;
+	g_return_val_if_fail(session != NULL, FALSE);
+
+	msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING);
 
 	msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE);
 
-	username =
-		g_strdup(purple_url_encode(purple_account_get_username(session->account)));
+	/*prepare the Windows Live ID authentication token*/
+	username = g_strdup(purple_account_get_username(session->account));
+	password = g_strdup(purple_connection_get_password(session->account->gc));
 
-	password =
-		g_strdup(purple_url_encode(purple_connection_get_password(session->account->gc)));
+	lc =	(char *)g_hash_table_lookup(nexus->challenge_data, "lc");
+	id =	(char *)g_hash_table_lookup(nexus->challenge_data, "id");
+	tw =	(char *)g_hash_table_lookup(nexus->challenge_data, "tw");
+	fs0=	(char *)g_hash_table_lookup(nexus->challenge_data, "fs");
+	ru =	(char *)g_hash_table_lookup(nexus->challenge_data, "ru");
+	ct =	(char *)g_hash_table_lookup(nexus->challenge_data, "ct");
+	kpp=	(char *)g_hash_table_lookup(nexus->challenge_data, "kpp");
+	kv =	(char *)g_hash_table_lookup(nexus->challenge_data, "kv");
+	ver=	(char *)g_hash_table_lookup(nexus->challenge_data, "ver");
+	rn =	(char *)g_hash_table_lookup(nexus->challenge_data, "rn");
+	tpf=	(char *)g_hash_table_lookup(nexus->challenge_data, "tpf");
 
-	ctint = strtoul((char *)g_hash_table_lookup(nexus->challenge_data, "ct"), NULL, 10) + 200;
+	/*
+	 * add some fail-safe code to avoid windows Purple Crash bug #1540454
+	 * If any of these string is NULL, will return Authentication Fail!
+	 * for when windows g_strdup_printf() implementation get NULL point,It crashed!
+	 */
+	if(!(lc && id && tw && ru && ct && kpp && kv && ver && tpf)){
+		purple_debug_error("MSN Nexus","WLM Authenticate Key Error!\n");
+		msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication Failed"));
+		g_free(username);
+		g_free(password);
+		purple_ssl_close(gsc);
+		msn_nexus_destroy(nexus);
+		session->nexus = NULL;
+		return FALSE;
+	}
 
-	head = g_strdup_printf(
-		"GET %s HTTP/1.1\r\n"
-		"Authorization: Passport1.4 OrgVerb=GET,OrgURL=%s,sign-in=%s",
-		nexus->login_path,
-		(char *)g_hash_table_lookup(nexus->challenge_data, "ru"),
-		username);
+	/*
+	 * in old MSN NS server's "USR TWN S" return,didn't include fs string
+	 * so we use a default "1" for fs.
+	 */
+	if(fs0){
+		fs = g_strdup(fs0);
+	}else{
+		fs = g_strdup("1");
+	}
 
-	tail = g_strdup_printf(
-		"lc=%s,id=%s,tw=%s,fs=%s,ru=%s,ct=%" G_GUINT32_FORMAT ",kpp=%s,kv=%s,ver=%s,tpf=%s\r\n"
-		"User-Agent: MSMSGS\r\n"
-		"Host: %s\r\n"
-		"Connection: Keep-Alive\r\n"
-		"Cache-Control: no-cache\r\n",
-		nexus_challenge_data_lookup(nexus->challenge_data, "lc"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "id"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "tw"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "fs"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "ru"),
-		ctint,
-		nexus_challenge_data_lookup(nexus->challenge_data, "kpp"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "kv"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "ver"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "tpf"),
-		nexus->login_host);
+#ifdef NEXUS_LOGIN_TWN
+	challenge_str = g_strdup_printf(
+		"lc=%s&amp;id=%s&amp;tw=%s&amp;fs=%s&amp;ru=%s&amp;ct=%s&amp;kpp=%s&amp;kv=%s&amp;ver=%s&amp;rn=%s&amp;tpf=%s\r\n",
+		lc,id,tw,fs,ru,ct,kpp,kv,ver,rn,tpf
+		);
 
-	buffer = g_strdup_printf("%s,pwd=XXXXXXXX,%s\r\n", head, tail);
-	request_str = g_strdup_printf("%s,pwd=%s,%s\r\n", head, password, tail);
+	/*build the SOAP windows Live ID XML body */
+	tail = g_strdup_printf(TWN_ENVELOP_TEMPLATE,username,password,challenge_str	);
+	g_free(challenge_str);
+#else
+	rst1_str = g_strdup_printf(
+		"id=%s&amp;tw=%s&amp;fs=%s&amp;kpp=%s&amp;kv=%s&amp;ver=%s&amp;rn=%s",
+		id,tw,fs,kpp,kv,ver,rn
+		);
+	rst2_str = g_strdup_printf(
+		"fs=%s&amp;id=%s&amp;kv=%s&amp;rn=%s&amp;tw=%s&amp;ver=%s",
+		fs,id,kv,rn,tw,ver
+		);
+	rst3_str = g_strdup_printf("id=%s",id);
+	tail = g_strdup_printf(TWN_LIVE_ENVELOP_TEMPLATE,username,password,rst1_str,rst2_str,rst3_str);
+	g_free(rst1_str);
+	g_free(rst2_str);
+	g_free(rst3_str);
+#endif
+	g_free(fs);
 
-	purple_debug_misc("msn", "Sending: {%s}\n", buffer);
+	soapconn->login_path = g_strdup(TWN_POST_URL);
+	head = g_strdup_printf(
+					"POST %s HTTP/1.1\r\n"
+					"Accept: text/*\r\n"
+					"User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n"
+					"Host: %s\r\n"
+					"Content-Length: %" G_GSIZE_FORMAT "\r\n"
+					"Connection: Keep-Alive\r\n"
+					"Cache-Control: no-cache\r\n\r\n",
+					soapconn->login_path, soapconn->login_host, strlen(tail));
 
-	g_free(buffer);
+	request_str = g_strdup_printf("%s%s", head,tail);
+
+#ifdef MSN_SOAP_DEBUG	
+	purple_debug_misc("MSN Nexus", "TWN Sending:\n%s\n", request_str);
+#endif
 	g_free(head);
 	g_free(tail);
 	g_free(username);
 	g_free(password);
 
-	nexus->write_buf = request_str;
-	nexus->written_len = 0;
-
-	nexus->read_len = 0;
-
-	nexus->written_cb = nexus_login_written_cb;
+	/*prepare to send the SOAP request*/
+	msn_soap_write(soapconn, request_str, nexus_login_written_cb);
 
-	nexus->input_handler = purple_input_add(gsc->fd, PURPLE_INPUT_WRITE,
-		nexus_write_cb, nexus);
-
-	nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE);
-
-	return;
-
-
+	return TRUE;
 }
 
+#if 0 /* khc */
 static void
 nexus_connect_written_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
 	MsnNexus *nexus = data;
 	int len;
+
 	char *da_login;
 	char *base, *c;
 
@@ -408,6 +359,7 @@
 		nexus->input_handler = purple_input_add(nexus->gsc->fd,
 			PURPLE_INPUT_READ, nexus_connect_written_cb, nexus);
 
+
 	/* Get the PassportURLs line. */
 	len = msn_ssl_read(nexus);
 
@@ -470,10 +422,13 @@
 }
 
 
+#endif
+
 /**************************************************************************
  * Connect
  **************************************************************************/
 
+#if 0 /* khc */
 static void
 nexus_connect_cb(gpointer data, PurpleSslConnection *gsc,
 				 PurpleInputCondition cond)
@@ -502,10 +457,12 @@
 	nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE);
 }
 
+#endif
+
 void
 msn_nexus_connect(MsnNexus *nexus)
 {
-	nexus->gsc = purple_ssl_connect(nexus->session->account,
-			"nexus.passport.com", PURPLE_SSL_DEFAULT_PORT,
-			nexus_connect_cb, login_error_cb, nexus);
+	/*  Authenticate via Windows Live ID. */
+	msn_soap_init(nexus->soapconn, MSN_TWN_SERVER, 1, nexus_login_connect_cb, nexus_login_error_cb);
+	msn_soap_connect(nexus->soapconn);
 }