view src/protocols/msn/nexus.c @ 8808:bbd8cdaf0ad5

[gaim-migrate @ 9570] A massive patch by shx to reorganize MSN some more and add command processor support. This allows us to do cool things like produce more detailed error messages. For example, the Invalid Username dialog now shows the username of the invalid user. I modified the aforementioned dialog so it'll look a little nicer looking, and also mention the account this happened on. It also removes the user from your blist, as there's no point to keeping the user on there. committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Sun, 25 Apr 2004 22:02:06 +0000
parents d0ba2f7b40e7
children 577c76e1442d
line wrap: on
line source

#include "msn.h"
#include "nexus.h"
#include "notification.h"

/**************************************************************************
 * Util
 **************************************************************************/

static size_t
msn_ssl_read(GaimSslConnection *gsc, char **dest_buffer)
{
	size_t size = 0, s;
	char *buffer = NULL;
	char temp_buf[4096];

	while ((s = gaim_ssl_read(gsc, temp_buf, sizeof(temp_buf))) > 0)
	{
		buffer = g_realloc(buffer, size + s + 1);

		strncpy(buffer + size, temp_buf, s);

		buffer[size + s] = '\0';

		size += s;
	}

	*dest_buffer = buffer;

	return size;
}

/**************************************************************************
 * Login
 **************************************************************************/

static void
login_error_cb(GaimSslConnection *gsc, GaimSslErrorType error, void *data)
{
	MsnNexus *nexus;
	MsnSession *session;
	GaimAccount *account;
	GaimConnection *gc;

	nexus = data;
	g_return_if_fail(nexus != NULL);

	session = nexus->session;
	g_return_if_fail(session != NULL);

	account = session->account;
	g_return_if_fail(account != NULL);

	gc = gaim_account_get_connection(account);
	g_return_if_fail(gc != NULL);

	gaim_connection_error(gc, _("Unable to connect to server"));

	msn_nexus_destroy(nexus);
	session->nexus = NULL;
}

static void
login_connect_cb(gpointer data, GaimSslConnection *gsc,
				 GaimInputCondition cond)
{
	MsnNexus *nexus;
	MsnSession *session;
	GaimConnection *gc;
	char *username, *password;
	char *request_str;
	char *buffer = NULL;
	size_t s;

	nexus = data;
	g_return_if_fail(nexus != NULL);

	session = nexus->session;
	g_return_if_fail(session != NULL);

	gc = gaim_account_get_connection(session->account);
	g_return_if_fail(gc != NULL);

	username =
		g_strdup(gaim_url_encode(gaim_account_get_username(session->account)));

	password =
		g_strdup(gaim_url_encode(gaim_account_get_password(session->account)));

	request_str = g_strdup_printf(
		"GET %s HTTP/1.1\r\n"
		"Authorization: Passport1.4 OrgVerb=GET,OrgURL=%s,sign-in=%s,pwd=%s,"
		"lc=%s,id=%s,tw=%s,fs=%s,ru=%s,ct=%s,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"
		"\r\n",
		nexus->login_path,
		(char *)g_hash_table_lookup(nexus->challenge_data, "ru"),
		username, password,
		(char *)g_hash_table_lookup(nexus->challenge_data, "lc"),
		(char *)g_hash_table_lookup(nexus->challenge_data, "id"),
		(char *)g_hash_table_lookup(nexus->challenge_data, "tw"),
		(char *)g_hash_table_lookup(nexus->challenge_data, "fs"),
		(char *)g_hash_table_lookup(nexus->challenge_data, "ru"),
		(char *)g_hash_table_lookup(nexus->challenge_data, "ct"),
		(char *)g_hash_table_lookup(nexus->challenge_data, "kpp"),
		(char *)g_hash_table_lookup(nexus->challenge_data, "kv"),
		(char *)g_hash_table_lookup(nexus->challenge_data, "ver"),
		(char *)g_hash_table_lookup(nexus->challenge_data, "tpf"),
		nexus->login_host);

	gaim_debug(GAIM_DEBUG_MISC, "msn", "Sending: {%s}\n", request_str);

	g_free(username);
	g_free(password);

	if ((s = gaim_ssl_write(gsc, request_str, strlen(request_str))) <= 0)
	{
		g_free(request_str);
		gaim_connection_error(gc, _("Unable to write to MSN Nexus server."));

		return;
	}

	g_free(request_str);

	if ((s = msn_ssl_read(gsc, &buffer)) <= 0)
	{
		gaim_connection_error(gc, _("Unable to write to MSN Nexus server."));
		return;
	}

	gaim_ssl_close(gsc);

	gaim_debug(GAIM_DEBUG_MISC, "msn", "ssl buffer: {%s}", buffer);

	if (strstr(buffer, "HTTP/1.1 302") != NULL)
	{
		/* Redirect. */
		char *location, *c;

		location = strstr(buffer, "Location: ");
		if (location == NULL)
		{
			gaim_connection_error(gc,
				_("MSN Nexus server returned invalid redirect information."));

			g_free(buffer);

			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);

		gaim_ssl_connect(session->account, nexus->login_host,
						 GAIM_SSL_DEFAULT_PORT, login_connect_cb,
						 login_error_cb, nexus);
	}
	else if (strstr(buffer, "HTTP/1.1 401 Unauthorized") != NULL)
	{
		GaimConnection *gc;
		const char *error, *c;
		char *temp;

		if ((error = strstr(buffer, "WWW-Authenticate")) != NULL)
		{
			if ((error = strstr(error, "cbtxt=")) != NULL)
			{
				error += strlen("cbtxt=");

				if ((c = strchr(error, '\n')) == NULL)
					c = error + strlen(error);

				temp = g_strndup(error, c - error);
				error = gaim_url_decode(temp);
				g_free(temp);
			}
		}

		gc = gaim_account_get_connection(session->account);

		if (error == NULL)
		{
			gaim_connection_error(gc,
				_("Unknown error when attempting to authorize with "
				  "MSN login server."));
		}
		else
			gaim_connection_error(gc, error);
	}
	else if (strstr(buffer, "HTTP/1.1 200 OK"))
	{
		MsnCmdProc *cmdproc;
		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

		cmdproc = session->notification_conn->cmdproc;
		base  = strstr(buffer, "Authentication-Info: ");

		if (base == NULL)
		{
			gaim_debug(GAIM_DEBUG_ERROR, "msn",
					   "Authentication information was not found. This did "
					   "not just happen, but if it did, you're screwed. "
					   "Report this.\n");

			return;
		}

		base  = strstr(base, "from-PP='");
		base += strlen("from-PP='");
		c     = strchr(base, '\'');

		login_params = g_strndup(base, c - base);

		msn_cmdproc_send(cmdproc, "USR", "TWN S %s", login_params);

		g_free(login_params);

		msn_nexus_destroy(nexus);
		session->nexus = NULL;
	}

	g_free(buffer);
}

static void
nexus_connect_cb(gpointer data, GaimSslConnection *gsc,
				 GaimInputCondition cond)
{
	MsnNexus *nexus;
	MsnSession *session;
	char *request_str;
	char *da_login;
	char *base, *c;
	char *buffer = NULL;
	size_t s;

	nexus = data;
	g_return_if_fail(nexus != NULL);

	session = nexus->session;
	g_return_if_fail(session != NULL);

	request_str = g_strdup_printf("GET /rdr/pprdr.asp\r\n\r\n");

	if ((s = gaim_ssl_write(gsc, request_str, strlen(request_str))) <= 0)
	{
		g_free(request_str);
		return;
	}

	g_free(request_str);

	/* Get the PassportURLs line. */
	if ((s = msn_ssl_read(gsc, &buffer)) <= 0)
		return;

	base = strstr(buffer, "PassportURLs");

	if (base == NULL)
	{
		g_free(buffer);
		return;
	}

	if ((da_login = strstr(base, "DALogin=")) != NULL)
	{
		if ((da_login = strchr(da_login, '=')) != NULL)
			da_login++;

		if ((c = strchr(da_login, ',')) != NULL)
			*c = '\0';

		if ((c = strchr(da_login, '/')) != NULL)
		{
			nexus->login_path = g_strdup(c);

			*c = '\0';
		}

		nexus->login_host = g_strdup(da_login);
	}

	g_free(buffer);

	gaim_ssl_close(gsc);

	/* Now begin the connection to the login server. */
	gaim_ssl_connect(session->account, nexus->login_host,
					 GAIM_SSL_DEFAULT_PORT, login_connect_cb,
					 login_error_cb, nexus);
}

/**************************************************************************
 * Nexus
 **************************************************************************/

MsnNexus *
msn_nexus_new(MsnSession *session)
{
	MsnNexus *nexus;

	nexus = g_new0(MsnNexus, 1);
	nexus->session = session;
	nexus->challenge_data = g_hash_table_new_full(g_str_hash, g_str_equal,
												  g_free, g_free);

	return nexus;
}

void
msn_nexus_destroy(MsnNexus *nexus)
{
	if (nexus->login_host != NULL)
		g_free(nexus->login_host);

	if (nexus->login_path != NULL)
		g_free(nexus->login_path);

	if (nexus->challenge_data != NULL)
		g_hash_table_destroy(nexus->challenge_data);

	g_free(nexus);
}

void
msn_nexus_connect(MsnNexus *nexus)
{
	gaim_ssl_connect(nexus->session->account, "nexus.passport.com",
					 GAIM_SSL_DEFAULT_PORT, nexus_connect_cb,
					 login_error_cb, nexus);
}