src/protocols/msn/notification.c

changeset 5309
3178acad1449
child 5312
7f9faad3e386
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/msn/notification.c	Tue May 06 02:06:56 2003 +0000
@@ -0,0 +1,1061 @@
+/**
+ * @file notification.c Notification server functions
+ *
+ * gaim
+ *
+ * Copyright (C) 2003 Christian Hammond <chipx86@gnupdate.org>
+ * 
+ * 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
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "msn.h"
+#include "notification.h"
+#include "away.h"
+#include "error.h"
+#include "utils.h"
+
+typedef struct
+{
+	struct gaim_connection *gc;
+	MsnUser *user;
+
+} MsnPermitAdd;
+
+static GHashTable *notification_commands  = NULL;
+static GHashTable *notification_msg_types = NULL;
+
+/**************************************************************************
+ * Callbacks
+ **************************************************************************/
+static void
+msn_accept_add_cb(MsnPermitAdd *pa)
+{
+	if (g_slist_find(connections, pa->gc) != NULL) {
+		MsnSession *session = pa->gc->proto_data;
+		char outparams[MSN_BUF_LEN];
+
+		g_snprintf(outparams, sizeof(outparams), "AL %s %s",
+				   msn_user_get_passport(pa->user),
+				   msn_url_encode(msn_user_get_name(pa->user)));
+
+		if (msn_servconn_send_command(session->notification_conn,
+									  "ADD", outparams) <= 0) {
+			hide_login_progress(pa->gc, _("Write error"));
+			signoff(pa->gc);
+			return;
+		}
+
+		gaim_privacy_permit_add(pa->gc->account,
+								msn_user_get_passport(pa->user));
+		build_allow_list();
+
+		show_got_added(pa->gc, NULL, msn_user_get_passport(pa->user),
+					   msn_user_get_name(pa->user), NULL);
+	}
+
+	msn_user_destroy(pa->user);
+	g_free(pa);
+}
+
+static void
+msn_cancel_add_cb(MsnPermitAdd *pa)
+{
+	if (g_slist_find(connections, pa->gc) != NULL) {
+		MsnSession *session = pa->gc->proto_data;
+		char outparams[MSN_BUF_LEN];
+
+		g_snprintf(outparams, sizeof(outparams), "BL %s %s",
+				   msn_user_get_passport(pa->user),
+				   msn_url_encode(msn_user_get_name(pa->user)));
+
+		if (msn_servconn_send_command(session->notification_conn,
+									  "ADD", outparams) <= 0) {
+			hide_login_progress(pa->gc, _("Write error"));
+			signoff(pa->gc);
+			return;
+		}
+
+		gaim_privacy_deny_add(pa->gc->account,
+							  msn_user_get_passport(pa->user));
+		build_block_list();
+	}
+
+	msn_user_destroy(pa->user);
+	g_free(pa);
+}
+
+/**************************************************************************
+ * Catch-all commands
+ **************************************************************************/
+static gboolean
+__blank_cmd(MsnServConn *servconn, const char *command, const char **params,
+			size_t param_count)
+{
+	return TRUE;
+}
+
+static gboolean
+__unknown_cmd(MsnServConn *servconn, const char *command, const char **params,
+			  size_t param_count)
+{
+	struct gaim_connection *gc = servconn->session->account->gc;
+
+	if (isdigit(*command)) {
+		hide_login_progress(gc, (char *)msn_error_get_text(atoi(command)));
+	}
+	else
+		hide_login_progress(gc, _("Unable to parse message."));
+
+	signoff(gc);
+
+	return FALSE;
+}
+
+
+/**************************************************************************
+ * Login
+ **************************************************************************/
+static gboolean
+__ver_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	struct gaim_connection *gc = servconn->session->account->gc;
+	size_t i;
+	gboolean msnp5_found = FALSE;
+
+	for (i = 1; i < param_count; i++) {
+		if (!strcmp(params[i], "MSNP5")) {
+			msnp5_found = TRUE;
+			break;
+		}
+	}
+
+	if (!msnp5_found) {
+		hide_login_progress(gc, _("Protocol not supported"));
+		signoff(gc);
+
+		return FALSE;
+	}
+
+	if (!msn_servconn_send_command(servconn, "INF", NULL)) {
+		hide_login_progress(gc, _("Unable to request INF"));
+		signoff(gc);
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+__inf_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	struct gaim_connection *gc = servconn->session->account->gc;
+	char outparams[MSN_BUF_LEN];
+
+	if (strcmp(params[1], "MD5")) {
+		hide_login_progress(gc, _("Unable to login using MD5"));
+		signoff(gc);
+
+		return FALSE;
+	}
+
+	g_snprintf(outparams, sizeof(outparams), "MD5 I %s", gc->username);
+
+	if (!msn_servconn_send_command(servconn, "USR", outparams)) {
+		hide_login_progress(gc, _("Unable to send USR"));
+		signoff(gc);
+
+		return FALSE;
+	}
+
+	set_login_progress(gc, 3, _("Requesting to send password"));
+
+	return TRUE;
+}
+
+static gboolean
+__usr_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	struct gaim_connection *gc = servconn->session->account->gc;
+	char outparams[MSN_BUF_LEN];
+
+	/* We're either getting the challenge or the OK. Let's find out. */
+	if (!g_ascii_strcasecmp(params[1], "OK")) {
+		/* OK */
+
+		if (!msn_servconn_send_command(servconn, "SYN", "0")) {
+			hide_login_progress(gc, _("Unable to write"));
+			signoff(gc);
+
+			return FALSE;
+		}
+	}
+	else {
+		/* Challenge */
+		const char *challenge = params[3];
+		char buf[MSN_BUF_LEN];
+		md5_state_t st;
+		md5_byte_t di[16];
+		int i;
+
+		g_snprintf(buf, sizeof(buf), "%s%s", challenge, gc->password);
+
+		md5_init(&st);
+		md5_append(&st, (const md5_byte_t *)buf, strlen(buf));
+		md5_finish(&st, di);
+
+		g_snprintf(outparams, sizeof(outparams), "MD5 S ");
+
+		for (i = 0; i < 16; i++) {
+			g_snprintf(buf, sizeof(buf), "%02x", di[i]);
+			strcat(outparams, buf);
+		}
+
+		if (!msn_servconn_send_command(servconn, "USR", outparams)) {
+			hide_login_progress(gc, _("Unable to send password"));
+			signoff(gc);
+
+			return FALSE;
+		}
+
+		set_login_progress(gc, 4, _("Password sent"));
+	}
+
+	return TRUE;
+}
+
+/**************************************************************************
+ * Log out
+ **************************************************************************/
+static gboolean
+__out_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	struct gaim_connection *gc = servconn->session->account->gc;
+
+	if (!g_ascii_strcasecmp(params[0], "OTH")) {
+		hide_login_progress(gc,
+							_("You have been disconnected. You have "
+							  "signed on from another location."));
+		signoff(gc);
+	}
+	else if (!g_ascii_strcasecmp(params[0], "SSD")) {
+		hide_login_progress(gc,
+							_("You have been disconnected. The MSN servers "
+							  "are going down temporarily."));
+		signoff(gc);
+	}
+
+	return FALSE;
+}
+
+/**************************************************************************
+ * Messages
+ **************************************************************************/
+static gboolean
+__msg_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	gaim_debug(GAIM_DEBUG_INFO, "msn", "Found message. Parsing.\n");
+
+	servconn->parsing_msg = TRUE;
+	servconn->msg_passport = g_strdup(params[0]);
+	servconn->msg_friendly = g_strdup(params[1]);
+	servconn->msg_len      = atoi(params[2]);
+
+	return TRUE;
+}
+
+/**************************************************************************
+ * Challenges
+ **************************************************************************/
+static gboolean
+__chl_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	struct gaim_connection *gc = servconn->session->account->gc;
+	char buf[MSN_BUF_LEN];
+	char buf2[3];
+	md5_state_t st;
+	md5_byte_t di[16];
+	int i;
+
+	md5_init(&st);
+	md5_append(&st, (const md5_byte_t *)params[1], strlen(params[1]));
+	md5_append(&st, (const md5_byte_t *)"Q1P7W2E4J9R8U3S5",
+			   strlen("Q1P7W2E4J9R8U3S5"));
+	md5_finish(&st, di);
+
+	g_snprintf(buf, sizeof(buf),
+			   "QRY %u msmsgs@msnmsgr.com 32\r\n",
+			   servconn->session->trId++);
+
+	for (i = 0; i < 16; i++) {
+		g_snprintf(buf2, sizeof(buf2), "%02x", di[i]);
+		strcat(buf, buf2);
+	}
+
+	if (msn_servconn_write(servconn, buf, strlen(buf)) <= 0) {
+		hide_login_progress(gc, _("Unable to write to server"));
+		signoff(gc);
+	}
+
+	return TRUE;
+}
+
+/**************************************************************************
+ * Buddy Lists
+ **************************************************************************/
+static gboolean
+__add_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	MsnSession *session = servconn->session;
+	struct gaim_connection *gc = session->account->gc;
+	MsnPermitAdd *pa;
+	GSList *sl;
+	const char *list, *passport;
+	char *friend;
+	char msg[MSN_BUF_LEN];
+
+	list = params[1];
+	passport = params[3];
+
+	friend = msn_url_decode(params[4]);
+
+	if (g_ascii_strcasecmp(list, "RL"))
+		return TRUE;
+
+	for (sl = gc->account->permit; sl != NULL; sl = sl->next) {
+		if (!gaim_utf8_strcasecmp(sl->data, passport))
+			return TRUE;
+	}
+
+	pa = g_new0(MsnPermitAdd, 1);
+	pa->user = msn_user_new(session, passport, friend);
+	pa->gc = gc;
+
+	g_snprintf(msg, sizeof(msg),
+			   _("The user %s (%s) wants to add %s to his or her buddy list."),
+			   passport, friend, gc->username);
+
+	do_ask_dialog(msg, NULL, pa,
+				  _("Authorize"), msn_accept_add_cb,
+				  _("Deny"), msn_cancel_add_cb,
+				  session->prpl->handle, FALSE);
+
+	return TRUE;
+}
+
+static gboolean
+__blp_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	struct gaim_connection *gc = servconn->session->account->gc;
+
+	if (!g_ascii_strcasecmp(params[2], "AL")) {
+		/*
+		 * If the current setting is AL, messages from users who
+		 * are not in BL will be delivered.
+		 *
+		 * In other words, deny some.
+		 */
+		gc->account->permdeny = DENY_SOME;
+	}
+	else {
+		/* If the current setting is BL, only messages from people
+		 * who are in the AL will be delivered.
+		 *
+		 * In other words, permit some.
+		 */
+		gc->account->permdeny = PERMIT_SOME;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+__fln_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	struct gaim_connection *gc = servconn->session->account->gc;
+
+	serv_got_update(gc, (char *)params[0], 0, 0, 0, 0, 0);
+
+	return TRUE;
+}
+
+static gboolean
+__iln_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	struct gaim_connection *gc = servconn->session->account->gc;
+	int status = 0;
+	const char *state, *passport, *friend;
+
+	state    = params[1];
+	passport = params[2];
+	friend   = msn_url_decode(params[3]);
+
+	serv_got_alias(gc, (char *)passport, (char *)friend);
+
+	if (!g_ascii_strcasecmp(state, "BSY"))
+		status |= UC_UNAVAILABLE | (MSN_BUSY << 1);
+	else if (!g_ascii_strcasecmp(state, "IDL"))
+		status |= UC_UNAVAILABLE | (MSN_IDLE << 1);
+	else if (!g_ascii_strcasecmp(state, "BRB"))
+		status |= UC_UNAVAILABLE | (MSN_BRB << 1);
+	else if (!g_ascii_strcasecmp(state, "AWY"))
+		status |= UC_UNAVAILABLE | (MSN_AWAY << 1);
+	else if (!g_ascii_strcasecmp(state, "PHN"))
+		status |= UC_UNAVAILABLE | (MSN_PHONE << 1);
+	else if (!g_ascii_strcasecmp(state, "LUN"))
+		status |= UC_UNAVAILABLE | (MSN_LUNCH << 1);
+
+	serv_got_update(gc, (char *)passport, 1, 0, 0, 0, status);
+
+	return TRUE;
+}
+
+static gboolean
+__lsg_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	MsnSession *session = servconn->session;
+	struct group *g;
+	const char *name;
+	int group_num, num_groups, group_id;
+
+	group_num  = atoi(params[2]);
+	num_groups = atoi(params[3]);
+	group_id   = atoi(params[4]);
+	name       = msn_url_decode(params[5]);
+
+	if (group_num == 1) {
+		session->groups = g_hash_table_new_full(g_int_hash, g_int_equal,
+												NULL, g_free);
+	}
+
+	g_hash_table_insert(session->groups, GINT_TO_POINTER(group_id),
+						g_strdup(name));
+
+	if ((g = gaim_find_group(name)) == NULL) {
+		g = gaim_group_new(name);
+		gaim_blist_add_group(g, NULL);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+__lst_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	MsnSession *session = servconn->session;
+	struct gaim_connection *gc = session->account->gc;
+	int user_num;
+	int num_users;
+	const char *type;
+	const char *passport;
+	const char *friend;
+
+	user_num  = atoi(params[3]);
+	num_users = atoi(params[4]);
+
+	if (user_num == 0 && num_users == 0)
+		return TRUE; /* There are no users on this list. */
+
+	type      = params[1];
+	passport  = params[5];
+	friend    = msn_url_decode(params[6]);
+
+	if (!g_ascii_strcasecmp(type, "FL") && user_num != 0) {
+		/* These are users on our contact list. */
+		MsnUser *user;
+
+		user = msn_user_new(session, passport, friend);
+
+		if (param_count == 8)
+			msn_user_set_group_id(user, atoi(params[7]));
+
+		session->lists.forward = g_slist_append(session->lists.forward, user);
+	}
+	else if (!g_ascii_strcasecmp(type, "AL") && user_num != 0) {
+		/* These are users who are allowed to see our status. */
+		if (g_slist_find_custom(gc->account->deny, passport,
+								(GCompareFunc)strcmp)) {
+
+			gaim_debug(GAIM_DEBUG_INFO, "msn",
+					   "Moving user from deny list to permit: %s (%s)\n",
+					   passport, friend);
+
+			gaim_privacy_deny_remove(gc->account, passport);
+		}
+
+		gaim_privacy_permit_add(gc->account, passport);
+	}
+	else if (!g_ascii_strcasecmp(type, "BL") && user_num != 0) {
+		/* These are users who are not allowed to see our status. */
+		gaim_privacy_deny_add(gc->account, passport);
+	}
+	else if (!g_ascii_strcasecmp(type, "RL")) {
+		/* These are users who have us on their contact list. */
+		if (user_num > 0) {
+			gboolean new_entry = TRUE;
+
+			if (g_slist_find_custom(gc->account->permit, passport,
+									(GCompareFunc)g_ascii_strcasecmp)) {
+				new_entry = FALSE;
+			}
+			
+			if (g_slist_find_custom(gc->account->deny, passport,
+									(GCompareFunc)g_ascii_strcasecmp)) {
+				new_entry = FALSE;
+			}
+
+			if (new_entry) {
+				MsnPermitAdd *pa;
+				char msg[MSN_BUF_LEN];
+
+				gaim_debug(GAIM_DEBUG_WARNING, "msn",
+						   "Unresolved MSN RL entry: %s\n", passport);
+
+				pa       = g_new0(MsnPermitAdd, 1);
+				pa->user = msn_user_new(session, passport, friend);
+				pa->gc   = gc;
+
+				g_snprintf(msg, sizeof(msg),
+						   _("The user %s (%s) wants to add you to their "
+							 "buddy list."),
+						   msn_user_get_passport(pa->user),
+						   msn_user_get_name(pa->user));
+
+				do_ask_dialog(msg, NULL, pa,
+							  _("Authorize"), msn_accept_add_cb,
+							  _("Deny"), msn_cancel_add_cb,
+							  session->prpl->handle, FALSE);
+			}
+		}
+
+		if (user_num != num_users)
+			return TRUE; /* This isn't the last one in the RL. */
+
+		if (!msn_servconn_send_command(servconn, "CHG", "NLN")) {
+			hide_login_progress(gc, _("Unable to write"));
+			signoff(gc);
+
+			return FALSE;
+		}
+
+		account_online(gc);
+		serv_finish_login(gc);
+
+		session->lists.allow = g_slist_copy(gc->account->permit);
+		session->lists.block = g_slist_copy(gc->account->deny);
+
+		while (session->lists.forward != NULL) {
+			MsnUser *user = session->lists.forward->data;
+			struct buddy *b;
+			
+			b = gaim_find_buddy(gc->account, msn_user_get_passport(user));
+
+			session->lists.forward = g_slist_remove(session->lists.forward,
+													user);
+
+			if (b == NULL) {
+				struct group *g = NULL;
+				const char *group_name = NULL;
+				int group_id;
+
+				group_id = msn_user_get_group_id(user);
+
+				if (group_id > -1) {
+					group_name = g_hash_table_lookup(session->groups,
+							GINT_TO_POINTER(group_id));
+				}
+
+				if (group_name == NULL) {
+					gaim_debug(GAIM_DEBUG_WARNING, "msn",
+							   "Group ID %d for user %s was not defined.\n",
+							   group_id, passport);
+				}
+				else if ((g = gaim_find_group(group_name)) == NULL) {
+					gaim_debug(GAIM_DEBUG_ERROR, "msn",
+							   "Group '%s' appears in server-side "
+							   "buddy list, but not here!",
+							   group_name);
+				}
+
+				if (g == NULL) {
+					if ((g = gaim_find_group(_("Buddies"))) == NULL) {
+						g = gaim_group_new(_("Buddies"));
+						gaim_blist_add_group(g, NULL);
+					}
+				}
+
+				b = gaim_buddy_new(gc->account,
+								   msn_user_get_passport(user), NULL);
+
+				gaim_blist_add_buddy(b, g, NULL);
+			}
+
+			serv_got_alias(gc, (char *)msn_user_get_passport(user),
+						   (char *)msn_user_get_name(user));
+
+			msn_user_destroy(user);
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
+__nln_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	MsnSession *session = servconn->session;
+	struct gaim_connection *gc = session->account->gc;
+	const char *state;
+	const char *passport;
+	const char *friend;
+	int status = 0;
+
+	state    = params[0];
+	passport = params[1];
+	friend   = msn_url_decode(params[2]);
+
+	serv_got_alias(gc, (char *)passport, (char *)friend);
+
+	if (!g_ascii_strcasecmp(state, "BSY"))
+		status |= UC_UNAVAILABLE | (MSN_BUSY << 1);
+	else if (!g_ascii_strcasecmp(state, "IDL"))
+		status |= UC_UNAVAILABLE | (MSN_IDLE << 1);
+	else if (!g_ascii_strcasecmp(state, "BRB"))
+		status |= UC_UNAVAILABLE | (MSN_BRB << 1);
+	else if (!g_ascii_strcasecmp(state, "AWY"))
+		status |= UC_UNAVAILABLE | (MSN_AWAY << 1);
+	else if (!g_ascii_strcasecmp(state, "PHN"))
+		status |= UC_UNAVAILABLE | (MSN_PHONE << 1);
+	else if (!g_ascii_strcasecmp(state, "LUN"))
+		status |= UC_UNAVAILABLE | (MSN_LUNCH << 1);
+
+	serv_got_update(gc, (char *)passport, 1, 0, 0, 0, status);
+
+	return TRUE;
+}
+
+static gboolean
+__rea_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	MsnSession *session = servconn->session;
+	struct gaim_connection *gc = session->account->gc;
+	char *friend;
+
+	friend = msn_url_decode(params[2]);
+
+	g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", friend);
+
+	return TRUE;
+}
+
+/**************************************************************************
+ * Misc commands
+ **************************************************************************/
+static gboolean
+__url_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	MsnSession *session = servconn->session;
+	struct gaim_connection *gc = session->account->gc;
+	const char *rru;
+	const char *url;
+	md5_state_t st;
+	md5_byte_t di[16];
+	FILE *fd;
+	char buf[2048];
+	char buf2[3];
+	char sendbuf[64];
+	int i;
+
+	rru = params[1];
+	url = params[2];
+
+	g_snprintf(buf, sizeof(buf), "%s%lu%s",
+			   session->passport_info.mspauth,
+			   time(NULL) - session->passport_info.sl, gc->password);
+
+	md5_init(&st);
+	md5_append(&st, (const md5_byte_t *)buf, strlen(buf));
+	md5_finish(&st, di);
+
+	memset(sendbuf, 0, sizeof(sendbuf));
+
+	for (i = 0; i < 16; i++) {
+		g_snprintf(buf2, sizeof(buf2), "%02x", di[i]);
+		strcat(sendbuf, buf2);
+	}
+
+	if (session->passport_info.file != NULL) {
+		unlink(session->passport_info.file);
+		g_free(session->passport_info.file);
+	}
+
+	if ((fd = gaim_mkstemp(&session->passport_info.file)) == NULL) {
+		gaim_debug(GAIM_DEBUG_ERROR, "msn",
+				   "Error opening temp passport file: %s\n",
+				   strerror(errno));
+	}
+	else {
+		fputs("<html>\n"
+			  "<head>\n"
+			  "<noscript>\n"
+			  "<meta http-equiv=\"Refresh\" content=\"0; "
+			        "url=http://www.hotmail.com\">\n"
+			  "</noscript>\n"
+			  "</head>\n\n",
+			  fd);
+
+		fprintf(fd, "<body onload=\"document.pform.submit(); \">\n");
+		fprintf(fd, "<form name=\"pform\" action=\"%s\" method=\"POST\">\n\n",
+				url);
+		fprintf(fd, "<input type=\"hidden\" name=\"mode\" value=\"ttl\">\n");
+		fprintf(fd, "<input type=\"hidden\" name=\"login\" value=\"%s\">\n",
+				gc->username);
+		fprintf(fd, "<input type=\"hidden\" name=\"username\" value=\"%s\">\n",
+				gc->username);
+		fprintf(fd, "<input type=\"hidden\" name=\"sid\" value=\"%s\">\n",
+				session->passport_info.sid);
+		fprintf(fd, "<input type=\"hidden\" name=\"kv\" value=\"%s\">\n",
+				session->passport_info.kv);
+		fprintf(fd, "<input type=\"hidden\" name=\"id\" value=\"2\">\n");
+		fprintf(fd, "<input type=\"hidden\" name=\"sl\" value=\"%ld\">\n",
+				time(NULL) - session->passport_info.sl);
+		fprintf(fd, "<input type=\"hidden\" name=\"rru\" value=\"%s\">\n",
+				rru);
+		fprintf(fd, "<input type=\"hidden\" name=\"auth\" value=\"%s\">\n",
+				session->passport_info.mspauth);
+		fprintf(fd, "<input type=\"hidden\" name=\"creds\" value=\"%s\">\n",
+				sendbuf); /* TODO Digest me (huh? -- ChipX86) */
+		fprintf(fd, "<input type=\"hidden\" name=\"svc\" value=\"mail\">\n");
+		fprintf(fd, "<input type=\"hiden\" name=\"js\" value=\"yes\">\n");
+		fprintf(fd, "</form></body>\n");
+		fprintf(fd, "</html>\n");
+
+		if (fclose(fd)) {
+			gaim_debug(GAIM_DEBUG_ERROR, "msn",
+					   "Error closing temp passport file: %s\n",
+					   strerror(errno));
+
+			unlink(session->passport_info.file);
+			g_free(session->passport_info.file);
+		}
+		else {
+			/*
+			 * Renaming file with .html extension, so that the
+			 * win32 open_url will work.
+			 */
+			char *tmp;
+
+			if ((tmp = g_strdup_printf("%s.html",
+					session->passport_info.file)) != NULL) {
+
+				if (rename(session->passport_info.file, tmp) == 0) {
+					g_free(session->passport_info.file);
+					session->passport_info.file = tmp;
+				}
+				else
+					g_free(tmp);
+			}
+		}
+	}
+
+	return TRUE;
+}
+/**************************************************************************
+ * Switchboards
+ **************************************************************************/
+static gboolean
+__rng_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	MsnSession *session = servconn->session;
+	MsnSwitchBoard *swboard;
+	MsnUser *user;
+	const char *session_id;
+	char *host, *c;
+	int port;
+
+	session_id = params[0];
+
+	host = g_strdup(params[1]);
+
+	if ((c = strchr(host, ':')) != NULL) {
+		*c = '\0';
+		port = atoi(c + 1);
+	}
+	else
+		port = 1863;
+
+	swboard = msn_switchboard_new(session);
+
+	user = msn_user_new(session, params[4], NULL);
+
+	msn_switchboard_set_invited(swboard, TRUE);
+	msn_switchboard_set_session_id(swboard, params[0]);
+	msn_switchboard_set_auth_key(swboard, params[3]);
+	msn_switchboard_set_user(swboard, user);
+
+	if (!msn_switchboard_connect(swboard, host, port)) {
+		gaim_debug(GAIM_DEBUG_ERROR, "msn",
+				   "Unable to connect to switchboard on %s, port %d\n",
+				   host, port);
+
+		g_free(host);
+
+		return FALSE;
+	}
+
+	g_free(host);
+
+	return TRUE;
+}
+
+static gboolean
+__xfr_cmd(MsnServConn *servconn, const char *command, const char **params,
+		  size_t param_count)
+{
+	MsnSession *session = servconn->session;
+	MsnSwitchBoard *swboard;
+	struct gaim_connection *gc = session->account->gc;
+	char *host;
+	char *c;
+	int port;
+
+	if (strcmp(params[1], "SB")) {
+		hide_login_progress(gc, _("Got invalid XFR"));
+		signoff(gc);
+		
+		return FALSE;
+	}
+
+	host = g_strdup(params[2]);
+
+	if ((c = strchr(host, ':')) != NULL) {
+		*c = '\0';
+		port = atoi(c + 1);
+	}
+	else
+		port = 1863;
+
+	swboard = msn_session_find_unused_switch(session);
+
+	if (swboard == NULL) {
+		gaim_debug(GAIM_DEBUG_ERROR, "msn",
+				   "Received an XFR SB request when there's no unused "
+				   "switchboards!\n");
+	}
+
+	msn_switchboard_set_auth_key(swboard, params[4]);
+
+	if (!msn_switchboard_connect(swboard, host, port)) {
+		gaim_debug(GAIM_DEBUG_ERROR, "msn",
+				   "Unable to connect to switchboard on %s, port %d\n",
+				   host, port);
+
+		g_free(host);
+
+		return FALSE;
+	}
+
+	g_free(host);
+
+	return TRUE;
+}
+
+/**************************************************************************
+ * Message Types
+ **************************************************************************/
+static gboolean
+__profile_msg(MsnServConn *servconn, const MsnMessage *msg)
+{
+	MsnSession *session = servconn->session;
+	const char *value;
+
+	if ((value = msn_message_get_attr(msg, "kv")) != NULL)
+		session->passport_info.kv = g_strdup(value);
+
+	if ((value = msn_message_get_attr(msg, "sid")) != NULL)
+		session->passport_info.sid = g_strdup(value);
+
+	if ((value = msn_message_get_attr(msg, "MSPAuth")) != NULL)
+		session->passport_info.mspauth = g_strdup(value);
+
+	return TRUE;
+}
+
+static gboolean
+__initial_email_msg(MsnServConn *servconn, const MsnMessage *msg)
+{
+	MsnSession *session = servconn->session;
+	struct gaim_connection *gc = session->account->gc;
+	GHashTable *table;
+	const char *unread;
+
+	table = msn_message_get_hashtable_from_body(msg);
+
+	unread = g_hash_table_lookup(table, "Inbox-Unread");
+
+	if (unread != NULL)
+		connection_has_mail(gc, atoi(unread), NULL, NULL,
+							session->passport_info.file);
+
+	g_hash_table_destroy(table);
+
+	return TRUE;
+}
+
+static gboolean
+__email_msg(MsnServConn *servconn, const MsnMessage *msg)
+{
+	MsnSession *session = servconn->session;
+	struct gaim_connection *gc = session->account->gc;
+	GHashTable *table;
+	const char *from, *subject;
+
+	table = msn_message_get_hashtable_from_body(msg);
+
+	from    = g_hash_table_lookup(table, "From");
+	subject = g_hash_table_lookup(table, "Subject");
+
+	if (from == NULL || subject == NULL) {
+		connection_has_mail(gc, 1, NULL, NULL, session->passport_info.file);
+	}
+	else {
+		connection_has_mail(gc, -1, from, subject,
+							session->passport_info.file);
+	}
+
+	g_hash_table_destroy(table);
+
+	return TRUE;
+}
+
+static gboolean
+__connect_cb(gpointer data, gint source, GaimInputCondition cond)
+{
+	MsnServConn *notification = data;
+	MsnSession *session = notification->session;
+	struct gaim_connection *gc = session->account->gc;
+
+	if (source == -1) {
+		hide_login_progress(session->account->gc, _("Unable to connect"));
+		signoff(session->account->gc);
+		return FALSE;
+	}
+
+	if (notification->fd != source)
+		notification->fd = source;
+
+	if (!msn_servconn_send_command(notification, "VER",
+								   "MSNP6 MSNP5 MSNP4 CVR0")) {
+		hide_login_progress(gc, _("Unable to write to server"));
+		signoff(gc);
+		return FALSE;
+	}
+
+	set_login_progress(session->account->gc, 2, _("Syncing with server"));
+
+	return TRUE;
+}
+
+static void
+__failed_read_cb(gpointer data, gint source, GaimInputCondition cond)
+{
+	MsnServConn *notification = data;
+	struct gaim_connection *gc;
+
+	gc = notification->session->account->gc;
+
+	hide_login_progress(gc, _("Error reading from server"));
+	signoff(gc);
+}
+
+MsnServConn *
+msn_notification_new(MsnSession *session, const char *server, int port)
+{
+	MsnServConn *notification;
+
+	notification = msn_servconn_new(session);
+
+	msn_servconn_set_server(notification, server, port);
+	msn_servconn_set_connect_cb(notification, __connect_cb);
+	msn_servconn_set_failed_read_cb(notification, __failed_read_cb);
+
+	if (notification_commands == NULL) {
+		/* Register the command callbacks. */
+		msn_servconn_register_command(notification, "ADD",       __add_cmd);
+		msn_servconn_register_command(notification, "BLP",       __blp_cmd);
+		msn_servconn_register_command(notification, "BPR",       __blank_cmd);
+		msn_servconn_register_command(notification, "CHG",       __blank_cmd);
+		msn_servconn_register_command(notification, "CHL",       __chl_cmd);
+		msn_servconn_register_command(notification, "FLN",       __fln_cmd);
+		msn_servconn_register_command(notification, "GTC",       __blank_cmd);
+		msn_servconn_register_command(notification, "ILN",       __iln_cmd);
+		msn_servconn_register_command(notification, "INF",       __inf_cmd);
+		msn_servconn_register_command(notification, "LSG",       __lsg_cmd);
+		msn_servconn_register_command(notification, "LST",       __lst_cmd);
+		msn_servconn_register_command(notification, "MSG",       __msg_cmd);
+		msn_servconn_register_command(notification, "NLN",       __nln_cmd);
+		msn_servconn_register_command(notification, "OUT",       __out_cmd);
+		msn_servconn_register_command(notification, "PRP",       __blank_cmd);
+		msn_servconn_register_command(notification, "QNG",       __blank_cmd);
+		msn_servconn_register_command(notification, "QRY",       __blank_cmd);
+		msn_servconn_register_command(notification, "REA",       __rea_cmd);
+		msn_servconn_register_command(notification, "REM",       __blank_cmd);
+		msn_servconn_register_command(notification, "RNG",       __rng_cmd);
+		msn_servconn_register_command(notification, "SYN",       __blank_cmd);
+		msn_servconn_register_command(notification, "URL",       __url_cmd);
+		msn_servconn_register_command(notification, "USR",       __usr_cmd);
+		msn_servconn_register_command(notification, "VER",       __ver_cmd);
+		msn_servconn_register_command(notification, "XFR",       __xfr_cmd);
+		msn_servconn_register_command(notification, "_unknown_", __unknown_cmd);
+
+		/* Register the message type callbacks. */
+		msn_servconn_register_msg_type(notification, "text/x-msmsgsprofile",
+									   __profile_msg);
+		msn_servconn_register_msg_type(notification,
+									   "text/x-msmsgsinitialemailnotification",
+									   __initial_email_msg);
+		msn_servconn_register_msg_type(notification,
+									   "text/x-msmsgsemailnotification",
+									   __email_msg);
+
+		/* Save these for future use. */
+		notification_commands  = notification->commands;
+		notification_msg_types = notification->msg_types;
+	}
+	else {
+		g_hash_table_destroy(notification->commands);
+		g_hash_table_destroy(notification->msg_types);
+
+		notification->commands  = notification_commands;
+		notification->msg_types = notification_msg_types;
+	}
+
+	return notification;
+}

mercurial