changeset 10225:ecf3ce2e2ab1

[gaim-migrate @ 11357] This is mostly a patch from Felipe Contreras that eliminates MSN switchboard errors and fixes MSN buddy icon syncronization, with some tweaks by me. Thank Felipe if it works, blame me if something broke. I also fixed a couple of text markup escaping things, fixed a glib warning that was bugging me, fix a rare SILC crash, and make gtkstatusselector.c compile (but do nothing) with gtk < 2.4 committer: Tailor Script <tailor@pidgin.im>
author Stu Tomlinson <stu@nosnilmot.com>
date Sun, 21 Nov 2004 17:48:09 +0000
parents ef7f9e69f03e
children 62ab1a4c3a2a
files src/ft.c src/gtkrequest.c src/gtkstatusselector.c src/protocols/msn/cmdproc.c src/protocols/msn/msg.c src/protocols/msn/msg.h src/protocols/msn/msn.c src/protocols/msn/notification.c src/protocols/msn/servconn.c src/protocols/msn/servconn.h src/protocols/msn/session.c src/protocols/msn/slp.c src/protocols/msn/slpcall.c src/protocols/msn/slpcall.h src/protocols/msn/slplink.c src/protocols/msn/slplink.h src/protocols/msn/slpmsg.c src/protocols/msn/switchboard.c src/protocols/msn/switchboard.h src/protocols/msn/table.h src/protocols/msn/transaction.c src/protocols/msn/transaction.h src/protocols/msn/userlist.c src/protocols/silc/silc.c
diffstat 24 files changed, 511 insertions(+), 125 deletions(-) [+]
line wrap: on
line diff
--- a/src/ft.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/ft.c	Sun Nov 21 17:48:09 2004 +0000
@@ -246,7 +246,7 @@
 static void
 gaim_xfer_ask_recv(GaimXfer *xfer)
 {
-	char *buf, *size_buf;
+	char *buf, *size_buf, *escaped;
 	size_t size;
 
 	/* If we have already accepted the request, ask the destination file
@@ -255,10 +255,12 @@
 		size = gaim_xfer_get_size(xfer);
 		size_buf = gaim_str_size_to_units(size);
 
+		escaped = g_markup_escape_text(gaim_xfer_get_filename(xfer), -1);
 		buf = g_strdup_printf(_("%s wants to send you %s (%s)"),
-				      xfer->who, gaim_xfer_get_filename(xfer),
+				      xfer->who, escaped,
 				      size_buf);
 		g_free(size_buf);
+		g_free(escaped);
 
 		if (xfer->message != NULL)
 			serv_got_im(gaim_account_get_connection(xfer->account),
@@ -904,14 +906,16 @@
 gaim_xfer_cancel_local(GaimXfer *xfer)
 {
 	GaimXferUiOps *ui_ops;
-	char *msg = NULL;
+	char *msg = NULL, *escaped;
 
 	g_return_if_fail(xfer != NULL);
 
 	gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_LOCAL);
 
+	escaped = g_markup_escape_text(gaim_xfer_get_filename(xfer), -1);
 	msg = g_strdup_printf(_("You canceled the transfer of %s"),
-						  gaim_xfer_get_filename(xfer));
+						  escaped);
+	g_free(escaped);
 	gaim_xfer_conversation_write(xfer, msg, FALSE);
 	g_free(msg);
 
@@ -953,15 +957,17 @@
 gaim_xfer_cancel_remote(GaimXfer *xfer)
 {
 	GaimXferUiOps *ui_ops;
-	gchar *msg;
+	gchar *msg, *escaped;
 
 	g_return_if_fail(xfer != NULL);
 
 	gaim_request_close_with_handle(xfer);
 	gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_REMOTE);
 
+	escaped = g_markup_escape_text(gaim_xfer_get_filename(xfer), -1);
 	msg = g_strdup_printf(_("%s canceled the transfer of %s"),
-						  xfer->who, gaim_xfer_get_filename(xfer));
+						  xfer->who, escaped);
+	g_free(escaped);
 	gaim_xfer_conversation_write(xfer, msg, TRUE);
 	gaim_xfer_error(gaim_xfer_get_type(xfer), xfer->who, msg);
 	g_free(msg);
--- a/src/gtkrequest.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/gtkrequest.c	Sun Nov 21 17:48:09 2004 +0000
@@ -1433,7 +1433,8 @@
 	filesel = gtk_file_selection_new(title ? title
 										   : (savedialog ? _("Save File...")
 														 : _("Open File...")));
-	gtk_file_selection_set_filename(GTK_FILE_SELECTION(filesel), filename);
+	if (filename != NULL)
+		gtk_file_selection_set_filename(GTK_FILE_SELECTION(filesel), filename);
 	g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(filesel)), "delete_event",
 							 G_CALLBACK(file_cancel_cb), data);
 	g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button),
--- a/src/gtkstatusselector.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/gtkstatusselector.c	Sun Nov 21 17:48:09 2004 +0000
@@ -38,14 +38,11 @@
 	GtkWidget *entry;
 	GtkWidget *frame;
 
-#if GTK_CHECK_VERSION(2,4,0)
 	GtkListStore *model;
-#endif
 
 	guint entry_timer;
 };
 
-#if GTK_CHECK_VERSION(2,4,0)
 enum
 {
 	COLUMN_STATUS_TYPE_ID,
@@ -53,15 +50,16 @@
 	COLUMN_NAME,
 	NUM_COLUMNS
 };
-#endif /* GTK >= 2.4.0 */
 
 static void gaim_gtk_status_selector_class_init(GaimGtkStatusSelectorClass *klass);
 static void gaim_gtk_status_selector_init(GaimGtkStatusSelector *selector);
 static void gaim_gtk_status_selector_finalize(GObject *obj);
 static void gaim_gtk_status_selector_destroy(GtkObject *obj);
+#if GTK_CHECK_VERSION (2,4,0)
 static void status_switched_cb(GtkWidget *combo, GaimGtkStatusSelector *selector);
 static gboolean key_press_cb(GtkWidget *entry, GdkEventKey *event, gpointer user_data);
 static void signed_on_off_cb(GaimConnection *gc, GaimGtkStatusSelector *selector);
+#endif
 static void rebuild_list(GaimGtkStatusSelector *selector);
 
 static GtkVBox *parent_class = NULL;
@@ -112,11 +110,11 @@
 static void
 gaim_gtk_status_selector_init(GaimGtkStatusSelector *selector)
 {
+#if GTK_CHECK_VERSION(2,4,0)
 	GtkWidget *combo;
 	GtkWidget *entry;
 	GtkWidget *toolbar;
 	GtkWidget *frame;
-#if GTK_CHECK_VERSION(2,4,0)
 	GtkCellRenderer *renderer;
 #endif
 
@@ -145,11 +143,9 @@
 
 	g_signal_connect(G_OBJECT(combo), "changed",
 					 G_CALLBACK(status_switched_cb), selector);
-#else /* GTK < 2.4.0 */
 
 	/* TODO */
 
-#endif /* GTK < 2.4.0 */
 
 	gtk_widget_show(combo);
 	gtk_box_pack_start(GTK_BOX(selector), combo, FALSE, FALSE, 0);
@@ -172,6 +168,7 @@
 						selector);
 
 	rebuild_list(selector);
+#endif /* GTK < 2.4.0 */
 }
 
 static void
@@ -206,6 +203,7 @@
 		GTK_OBJECT_CLASS(parent_class)->destroy(obj);
 }
 
+#if GTK_CHECK_VERSION(2,4,0)
 static void
 status_switched_cb(GtkWidget *combo, GaimGtkStatusSelector *selector)
 {
@@ -280,10 +278,12 @@
 			gtk_widget_hide(selector->priv->frame);
 	}
 }
+#endif
 
 static gboolean
 insert_text_timeout_cb(gpointer data)
 {
+#if GTK_CHECK_VERSION(2,4,0)
 	GaimGtkStatusSelector *selector = (GaimGtkStatusSelector *)data;
 	GtkTreeIter iter;
 	const char *status_type_id;
@@ -335,6 +335,7 @@
 			}
 		}
 	}
+#endif
 
 	return FALSE;
 }
--- a/src/protocols/msn/cmdproc.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/cmdproc.c	Sun Nov 21 17:48:09 2004 +0000
@@ -250,6 +250,10 @@
 	if (cmd->trId)
 		trans = msn_history_find(cmdproc->history, cmd->trId);
 
+	if (trans != NULL)
+		if (trans->timer)
+			gaim_timeout_remove(trans->timer);
+
 	if (g_ascii_isdigit(cmd->command[0]))
 	{
 		if (trans != NULL)
@@ -258,11 +262,17 @@
 			int error;
 
 			error = atoi(cmd->command);
-			if (cmdproc->cbs_table->errors != NULL)
+
+			if (trans->error_cb != NULL)
+				error_cb = trans->error_cb;
+
+			if (error_cb == NULL && cmdproc->cbs_table->errors != NULL)
 				error_cb = g_hash_table_lookup(cmdproc->cbs_table->errors, trans->command);
 
 			if (error_cb != NULL)
+			{
 				error_cb(cmdproc, trans, error);
+			}
 			else
 			{
 #if 1
--- a/src/protocols/msn/msg.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/msg.c	Sun Nov 21 17:48:09 2004 +0000
@@ -25,11 +25,12 @@
 #include "msg.h"
 
 MsnMessage *
-msn_message_new(void)
+msn_message_new(MsnMsgType type)
 {
 	MsnMessage *msg;
 
 	msg = g_new0(MsnMessage, 1);
+	msg->type = type;
 
 	msg->attr_table = g_hash_table_new_full(g_str_hash, g_str_equal,
 											g_free, g_free);
@@ -45,11 +46,11 @@
 	MsnMessage *msg;
 	char *message_cr;
 
-	msg = msn_message_new();
+	msg = msn_message_new(MSN_MSG_TEXT);
 	msn_message_set_attr(msg, "User-Agent", "Gaim/" VERSION);
 	msn_message_set_content_type(msg, "text/plain");
 	msn_message_set_charset(msg, "UTF-8");
-	msn_message_set_flag(msg, 'N');
+	msn_message_set_flag(msg, 'A');
 	msn_message_set_attr(msg, "X-MMS-IM-Format",
 						 "FN=MS%20Sans%20Serif; EF=; CO=0; PF=0");
 
@@ -65,7 +66,7 @@
 {
 	MsnMessage *msg;
 
-	msg = msn_message_new();
+	msg = msn_message_new(MSN_MSG_SLP);
 
 	msn_message_set_attr(msg, "User-Agent", NULL);
 
@@ -226,7 +227,7 @@
 
 	g_return_val_if_fail(cmd != NULL, NULL);
 
-	msg = msn_message_new();
+	msg = msn_message_new(MSN_MSG_UNKNOWN);
 
 	msg->remote_user = g_strdup(cmd->params[0]);
 	/* msg->size = atoi(cmd->params[2]); */
@@ -612,6 +613,20 @@
 	return table;
 }
 
+char *
+msn_message_to_string(MsnMessage *msg)
+{
+	size_t body_len;
+	const char *body;
+
+	g_return_val_if_fail(msg != NULL, NULL);
+	g_return_val_if_fail(msg->type == MSN_MSG_TEXT, NULL);
+
+	body = msn_message_get_bin_data(msg, &body_len);
+
+	return g_strndup(body, body_len);
+}
+
 void
 msn_message_show_readable(MsnMessage *msg, const char *info,
 						  gboolean text_body)
--- a/src/protocols/msn/msg.h	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/msg.h	Sun Nov 21 17:48:09 2004 +0000
@@ -32,6 +32,9 @@
 #include "command.h"
 #include "transaction.h"
 
+typedef void (*MsnCb)(void *data);
+
+/*
 typedef enum
 {
 	MSN_MSG_NORMAL,
@@ -39,6 +42,17 @@
 	MSN_MSG_SLP_DC
 
 } MsnMsgType;
+*/
+
+typedef enum
+{
+	MSN_MSG_UNKNOWN,
+	MSN_MSG_TEXT,
+	MSN_MSG_TYPING,
+	MSN_MSG_CAPS,
+	MSN_MSG_SLP
+
+} MsnMsgType;
 
 typedef struct
 {
@@ -88,7 +102,7 @@
 	MsnCommand *cmd;
 	MsnTransaction *trans;
 
-	MsnTransCb ack_cb;
+	MsnCb ack_cb;
 	void *ack_data;
 };
 
@@ -97,7 +111,7 @@
  *
  * @return A new message.
  */
-MsnMessage *msn_message_new(void);
+MsnMessage *msn_message_new(MsnMsgType type);
 
 /**
  * Creates a new, empty MSNSLP message.
@@ -299,5 +313,6 @@
 
 char *msn_message_gen_slp_body(MsnMessage *msg, size_t *ret_size);
 
+char *msn_message_to_string(MsnMessage *msg);
 
 #endif /* _MSN_MSG_H_ */
--- a/src/protocols/msn/msn.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/msn.c	Sun Nov 21 17:48:09 2004 +0000
@@ -192,7 +192,7 @@
 
 	payload = msn_page_gen_payload(page, &payload_len);
 
-	trans = msn_transaction_new("PGD", "%s 1 %d", who, payload_len);
+	trans = msn_transaction_new(cmdproc, "PGD", "%s 1 %d", who, payload_len);
 
 	msn_transaction_set_payload(trans, payload, payload_len);
 
@@ -798,7 +798,7 @@
 	if (!swboard->user_joined)
 		return 0;
 
-	msg = msn_message_new();
+	msg = msn_message_new(MSN_MSG_TYPING);
 	msn_message_set_content_type(msg, "text/x-msmsgscontrol");
 	msn_message_set_flag(msg, 'U');
 	msn_message_set_attr(msg, "TypingUser",
--- a/src/protocols/msn/notification.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/notification.c	Sun Nov 21 17:48:09 2004 +0000
@@ -286,7 +286,7 @@
 	for (i = 0; i < 16; i++)
 		g_snprintf(buf + (i*2), 3, "%02x", di[i]);
 
-	trans = msn_transaction_new("QRY", "%s 32", "PROD0038W!61ZTF9");
+	trans = msn_transaction_new(cmdproc, "QRY", "%s 32", "PROD0038W!61ZTF9");
 
 	msn_transaction_set_payload(trans, buf, 32);
 
@@ -981,7 +981,7 @@
 	if (session->passport_info.file == NULL)
 	{
 		MsnTransaction *trans;
-		trans = msn_transaction_new("URL", "%s", "INBOX");
+		trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX");
 		msn_transaction_queue_cmd(trans, msg->cmd);
 
 		msn_cmdproc_send_trans(cmdproc, trans);
@@ -1034,7 +1034,7 @@
 	if (session->passport_info.file == NULL)
 	{
 		MsnTransaction *trans;
-		trans = msn_transaction_new("URL", "%s", "INBOX");
+		trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX");
 		msn_transaction_queue_cmd(trans, msg->cmd);
 
 		msn_cmdproc_send_trans(cmdproc, trans);
--- a/src/protocols/msn/servconn.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/servconn.c	Sun Nov 21 17:48:09 2004 +0000
@@ -65,6 +65,10 @@
 	}
 	else
 	{
+		MsnSwitchBoard *swboard;
+		swboard = servconn->data;
+		swboard->error = MSN_SB_ERROR_CONNECTION;
+		/*
 		GaimAccount *account;
 		char *primary;
 
@@ -75,6 +79,7 @@
 		gaim_notify_error(gc, NULL, primary, tmp);
 
 		g_free(primary);
+		*/
 	}
 
 	g_free(tmp);
--- a/src/protocols/msn/servconn.h	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/servconn.h	Sun Nov 21 17:48:09 2004 +0000
@@ -70,7 +70,6 @@
 	size_t payload_len;
 
 	void (*connect_cb)(MsnServConn *);
-/*	void (*failed_io_cb)(MsnServConn *); */
 	void (*disconnect_cb)(MsnServConn *);
 	void (*data_free_cb)(void *data);
 	void *data;
@@ -86,8 +85,6 @@
 								 void (*connect_cb)(MsnServConn *));
 void msn_servconn_set_disconnect_cb(MsnServConn *servconn,
 									void (*disconnect_cb)(MsnServConn *));
-void msn_servconn_set_failed_io_cb(MsnServConn *servconn,
-								 void (*failed_io_cb)(MsnServConn *));
 size_t msn_servconn_write(MsnServConn *servconn, const char *buf, size_t size);
 
 #endif /* _MSN_SERVCONN_H_ */
--- a/src/protocols/msn/session.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/session.c	Sun Nov 21 17:48:09 2004 +0000
@@ -131,7 +131,6 @@
 }
 
 /* TODO: This must go away when conversation is redesigned */
-
 MsnSwitchBoard *
 msn_session_find_swboard(MsnSession *session, const char *username)
 {
@@ -185,9 +184,9 @@
 	if (swboard == NULL)
 	{
 		swboard = msn_switchboard_new(session);
+		swboard->im_user = g_strdup(username);
 		msn_switchboard_request(swboard);
 		msn_switchboard_request_add_user(swboard, username);
-		swboard->im_user = g_strdup(username);
 	}
 
 	return swboard;
--- a/src/protocols/msn/slp.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/slp.c	Sun Nov 21 17:48:09 2004 +0000
@@ -144,9 +144,11 @@
 void
 msn_xfer_end_cb(MsnSlpCall *slpcall)
 {
-	if (gaim_xfer_get_status(slpcall->xfer) != GAIM_XFER_STATUS_DONE)
+	if ((gaim_xfer_get_status(slpcall->xfer) != GAIM_XFER_STATUS_DONE) &&
+		(gaim_xfer_get_status(slpcall->xfer) != GAIM_XFER_STATUS_CANCEL_REMOTE) &&
+		(gaim_xfer_get_status(slpcall->xfer) != GAIM_XFER_STATUS_CANCEL_LOCAL))
 	{
-		gaim_xfer_cancel_remote(slpcall->xfer);
+		/* gaim_xfer_cancel_remote(slpcall->xfer); */
 	}
 }
 
@@ -334,13 +336,13 @@
 
 		gaim_base64_decode(context, &bin, &bin_len);
 		file_size = GUINT32_FROM_LE(*((gsize *)bin + 2));
-		
+
 		uni_name = (gunichar2 *)(bin + 20);
 		while(*uni_name != 0 && ((char *)uni_name - (bin + 20)) < MAX_FILE_NAME_LEN) {
 			*uni_name = GUINT16_FROM_LE(*uni_name);
 			uni_name++;
 		}
-		
+
 		file_name = g_utf16_to_utf8((const gunichar2 *)(bin + 20), -1,
 									NULL, NULL, NULL);
 
@@ -669,7 +671,7 @@
 				size_t offset =  c - status;
 				if (offset >= sizeof(temp))
 					offset = sizeof(temp) - 1;
-		      
+
 				strncpy(temp, status, offset);
 				temp[offset] = '\0';
 			}
@@ -760,7 +762,7 @@
 
 	slplink = msn_session_get_slplink(session, who);
 
-	msn_slplink_request_object(slplink, smile, got_emoticon, obj);
+	msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj);
 
 	g_strfreev(tokens);
 }
@@ -802,6 +804,10 @@
 {
 	MsnUser *user;
 
+	g_return_if_fail(userlist != NULL);
+
+	gaim_debug_info("msn", "Releasing buddy icon request\n");
+
 	while (userlist->buddy_icon_window > 0)
 	{
 		GQueue *queue;
@@ -820,6 +826,9 @@
 
 		msn_request_user_display(user);
 		userlist->buddy_icon_window--;
+
+		gaim_debug_info("msn", "buddy_icon_window=%d\n",
+						userlist->buddy_icon_window);
 	}
 }
 
@@ -830,6 +839,8 @@
 	MsnObject *obj;
 	GQueue *queue;
 
+	g_return_if_fail(user != NULL);
+
 	account = user->userlist->session->account;
 
 	obj = msn_user_get_object(user);
@@ -860,8 +871,14 @@
 		userlist = user->userlist;
 		queue = userlist->buddy_icon_requests;
 
+		gaim_debug_info("msn", "Queueing buddy icon request: %s\n",
+						user->passport);
+
 		g_queue_push_tail(queue, user);
 
+		gaim_debug_info("msn", "buddy_icon_window=%d\n",
+						userlist->buddy_icon_window);
+
 		if (userlist->buddy_icon_window > 0)
 			msn_release_buddy_icon_request(userlist);
 	}
@@ -871,13 +888,17 @@
 got_user_display(MsnSlpCall *slpcall,
 				 const char *data, long long size)
 {
+	MsnUserList *userlist;
 	const char *info;
 	GaimAccount *account;
 	GSList *sl;
 
+	g_return_if_fail(slpcall != NULL);
+
 	info = slpcall->data_info;
 	gaim_debug_info("msn", "Got User Display: %s\n", info);
 
+	userlist = slpcall->slplink->session->userlist;
 	account = slpcall->slplink->session->account;
 
 	/* TODO: I think we need better buddy icon core functions. */
@@ -892,8 +913,35 @@
 		gaim_blist_node_set_string((GaimBlistNode*)buddy, "icon_checksum", info);
 	}
 
-	slpcall->slplink->session->userlist->buddy_icon_window++;
-	msn_release_buddy_icon_request(slpcall->slplink->session->userlist);
+#if 0
+	/* Free one window slot */
+	userlist->buddy_icon_window++;
+
+	gaim_debug_info("msn", "buddy_icon_window=%d\n",
+					userlist->buddy_icon_window);
+
+	msn_release_buddy_icon_request(userlist);
+#endif
+}
+
+void
+end_user_display(MsnSlpCall *slpcall)
+{
+	MsnUserList *userlist;
+
+	g_return_if_fail(slpcall != NULL);
+
+	gaim_debug_info("msn", "End User Display\n");
+
+	userlist = slpcall->slplink->session->userlist;
+
+	/* Free one window slot */
+	userlist->buddy_icon_window++;
+
+	gaim_debug_info("msn", "buddy_icon_window=%d\n",
+					userlist->buddy_icon_window);
+
+	msn_release_buddy_icon_request(userlist);
 }
 
 void
@@ -917,7 +965,8 @@
 	if (g_ascii_strcasecmp(user->passport,
 						   gaim_account_get_username(account)))
 	{
-		msn_slplink_request_object(slplink, info, got_user_display, obj);
+		msn_slplink_request_object(slplink, info, got_user_display,
+								   end_user_display, obj);
 	}
 	else
 	{
@@ -943,7 +992,7 @@
 
 		/* TODO: I think we need better buddy icon core functions. */
 		gaim_buddy_icons_set_for_user(account, user->passport, (void *)data, len);
-		
+
 		sl = gaim_find_buddies(account, user->passport);
 
 		for (; sl != NULL; sl = sl->next)
@@ -952,7 +1001,12 @@
 			gaim_blist_node_set_string((GaimBlistNode*)buddy, "icon_checksum", info);
 		}
 
+		/* Free one window slot */
 		session->userlist->buddy_icon_window++;
+
+		gaim_debug_info("msn", "buddy_icon_window=%d\n",
+						session->userlist->buddy_icon_window);
+
 		msn_release_buddy_icon_request(session->userlist);
 	}
 }
--- a/src/protocols/msn/slpcall.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/slpcall.c	Sun Nov 21 17:48:09 2004 +0000
@@ -62,6 +62,8 @@
 	slplink->slp_calls =
 		g_list_append(slplink->slp_calls, slpcall);
 
+	slpcall->timer = gaim_timeout_add(MSN_SLPCALL_TIMEOUT, msn_slp_call_timeout, slpcall);
+
 	return slpcall;
 }
 
@@ -93,6 +95,9 @@
 
 	g_return_if_fail(slpcall != NULL);
 
+	if (slpcall->timer)
+		gaim_timeout_remove(slpcall->timer);
+
 	if (slpcall->id != NULL)
 		g_free(slpcall->id);
 
@@ -181,6 +186,16 @@
 	msn_slp_call_destroy(slpcall);
 }
 
+gboolean
+msn_slp_call_timeout(gpointer data)
+{
+	gaim_debug_info("msn", "slpcall timeout\n");
+
+	msn_slp_call_destroy(data);
+
+	return FALSE;
+}
+
 MsnSlpCall *
 msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
 {
@@ -201,7 +216,20 @@
 		slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id);
 
 		if (slpcall != NULL)
+		{
+			if (slpcall->timer)
+				gaim_timeout_remove(slpcall->timer);
+
 			slpcall->cb(slpcall, body, body_len);
+
+			/* TODO: Shall we send a BYE? I don't think so*/
+#if 0
+			send_bye(slpcall, "application/x-msnmsgr-sessionclosebody");
+			msn_slplink_unleash(slpcall->slplink);
+#endif
+
+			slpcall->wasted = TRUE;
+		}
 	}
 #if 0
 	else if (slpmsg->flags == 0x100)
@@ -213,5 +241,14 @@
 	}
 #endif
 
+	if (slpcall != NULL)
+	{
+		if (slpcall->timer)
+			gaim_timeout_remove(slpcall->timer);
+
+		slpcall->timer = gaim_timeout_add(MSN_SLPCALL_TIMEOUT,
+										  msn_slp_call_timeout, slpcall);
+	}
+
 	return slpcall;
 }
--- a/src/protocols/msn/slpcall.h	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/slpcall.h	Sun Nov 21 17:48:09 2004 +0000
@@ -28,10 +28,14 @@
 
 typedef void (*MsnSlpCb)(MsnSlpCall *slpcall,
 						 const char *data, long long size);
+typedef void (*MsnSlpEndCb)(MsnSlpCall *slpcall);
 
 #include "slplink.h"
 #include "slpsession.h"
 
+/* The official client seems to timeout slp calls after 5 minutes */
+#define MSN_SLPCALL_TIMEOUT 300000
+
 typedef enum
 {
 	MSN_SLPCALL_ANY,
@@ -66,6 +70,8 @@
 	void (*end_cb)(MsnSlpCall *slpcall);
 	gboolean wasted;
 	gboolean started;
+
+	int timer;
 };
 
 MsnSlpCall *msn_slp_call_new(MsnSlpLink *slplink);
@@ -75,5 +81,6 @@
 void msn_slp_call_invite(MsnSlpCall *slpcall, const char *euf_guid,
 						 int app_id, const char *context);
 void msn_slp_call_close(MsnSlpCall *slpcall);
+gboolean msn_slp_call_timeout(gpointer data);
 
 #endif /* _MSN_SLPCALL_H_ */
--- a/src/protocols/msn/slplink.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/slplink.c	Sun Nov 21 17:48:09 2004 +0000
@@ -100,11 +100,12 @@
 MsnSlpLink *
 msn_session_find_slplink(MsnSession *session, const char *who)
 {
-	MsnSlpLink *slplink;
 	GList *l;
 
 	for (l = session->slplinks; l != NULL; l = l->next)
 	{
+		MsnSlpLink *slplink;
+
 		slplink = l->data;
 
 		if (!strcmp(slplink->remote_user, who))
@@ -192,7 +193,8 @@
 	{
 		MsnSwitchBoard *swboard;
 
-		swboard = msn_session_get_swboard(slplink->session, slplink->remote_user);
+		swboard = msn_session_get_swboard(slplink->session,
+										  slplink->remote_user);
 
 		if (swboard == NULL)
 			return;
@@ -209,13 +211,14 @@
 	}
 }
 
+/* We have received the message receive ack */
 static void
-t_ack(MsnCmdProc *cmdproc, MsnCommand *cmd)
+msg_ack(void *data)
 {
 	MsnSlpMessage *slpmsg;
 	long long real_size;
 
-	slpmsg = cmd->trans->data;
+	slpmsg = data;
 
 	real_size = (slpmsg->flags == 0x2) ? 0 : slpmsg->size;
 
@@ -283,10 +286,16 @@
 
 	msn_slplink_send_msg(slplink, msg);
 
-	if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030)
+	if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) && (slpmsg->slpcall != NULL))
 	{
-		if ((slpmsg->slpcall != NULL) &&
-			(slpmsg->slpcall->progress_cb != NULL))
+		if (slpmsg->slpcall->timer)
+		{
+			gaim_timeout_remove(slpmsg->slpcall->timer);
+			slpmsg->slpcall->timer = gaim_timeout_add(MSN_SLPCALL_TIMEOUT,
+													  msn_slp_call_timeout, slpmsg->slpcall);
+		}
+
+		if (slpmsg->slpcall->progress_cb != NULL)
 		{
 			slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size,
 										 len, slpmsg->offset);
@@ -338,7 +347,7 @@
 
 	msn_message_set_attr(msg, "P2P-Dest", slplink->remote_user);
 
-	msg->ack_cb = t_ack;
+	msg->ack_cb = msg_ack;
 	msg->ack_data = slpmsg;
 
 	msn_slplink_send_msgpart(slplink, slpmsg);
@@ -473,7 +482,7 @@
 				}
 			}
 		}
-		if (!slpmsg->fp)
+		if (!slpmsg->fp && slpmsg->size)
 		{
 			slpmsg->buffer = g_try_malloc(slpmsg->size);
 			if (slpmsg->buffer == NULL)
@@ -495,7 +504,7 @@
 			/* fseek(slpmsg->fp, offset, SEEK_SET); */
 			len = fwrite(data, 1, len, slpmsg->fp);
 		}
-		else
+		else if (slpmsg->size)
 		{
 			if ((offset + len) > slpmsg->size)
 			{
@@ -512,10 +521,16 @@
 		g_return_if_reached();
 	}
 
-	if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030)
+	if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) && (slpmsg->slpcall != NULL))
 	{
-		if ((slpmsg->slpcall != NULL) &&
-			(slpmsg->slpcall->progress_cb != NULL))
+		if (slpmsg->slpcall->timer)
+		{
+			gaim_timeout_remove(slpmsg->slpcall->timer);
+			slpmsg->slpcall->timer = gaim_timeout_add(MSN_SLPCALL_TIMEOUT,
+													  msn_slp_call_timeout, slpmsg->slpcall);
+		}
+
+		if (slpmsg->slpcall->progress_cb != NULL)
 		{
 			slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size,
 										 len, offset);
@@ -623,7 +638,7 @@
 	header.file_size = GUINT32_TO_LE(size);
 	header.unk2 = GUINT32_TO_LE(0);
 	header.unk3 = GUINT32_TO_LE(0);
-	
+
 	base = g_malloc(len + 1);
 	n = base;
 
@@ -638,7 +653,7 @@
 
 	memset(n, 0xFF, 4);
 	n += 4;
-	
+
 	g_free(uni);
 	return gaim_base64_encode(base, len);
 }
@@ -682,6 +697,7 @@
 msn_slplink_request_object(MsnSlpLink *slplink,
 						   const char *info,
 						   MsnSlpCb cb,
+						   MsnSlpEndCb end_cb,
 						   const MsnObject *obj)
 {
 	MsnSlpCall *slpcall;
@@ -700,6 +716,7 @@
 
 	slpcall->data_info = g_strdup(info);
 	slpcall->cb = cb;
+	slpcall->end_cb = end_cb;
 
 	msn_slp_call_invite(slpcall, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6", 1,
 						msnobj_base64);
--- a/src/protocols/msn/slplink.h	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/slplink.h	Sun Nov 21 17:48:09 2004 +0000
@@ -79,6 +79,7 @@
 void msn_slplink_request_object(MsnSlpLink *slplink,
 								 const char *info,
 								 MsnSlpCb cb,
+								 MsnSlpEndCb end_cb,
 								 const MsnObject *obj);
 
 MsnSlpCall *msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg);
--- a/src/protocols/msn/slpmsg.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/slpmsg.c	Sun Nov 21 17:48:09 2004 +0000
@@ -122,6 +122,7 @@
 		case 0x2:
 			info = "SLP ACK"; break;
 		case 0x20:
+		case 0x1000030:
 			info = "SLP DATA"; break;
 		default:
 			info = "SLP UNKNOWN"; break;
--- a/src/protocols/msn/switchboard.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/switchboard.c	Sun Nov 21 17:48:09 2004 +0000
@@ -31,6 +31,8 @@
 
 static MsnTable *cbs_table;
 
+static void cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error);
+
 /**************************************************************************
  * Utility functions
  **************************************************************************/
@@ -39,7 +41,7 @@
 {
 	MsnMessage *msg;
 
-	msg = msn_message_new();
+	msg = msn_message_new(MSN_MSG_CAPS);
 	msn_message_set_content_type(msg, "text/x-clientcaps");
 	msn_message_set_flag(msg, 'U');
 	msn_message_set_bin_data(msg, MSN_CLIENTINFO, strlen(MSN_CLIENTINFO));
@@ -135,14 +137,41 @@
 	swboard->ready = TRUE;
 }
 
+GaimConversation *
+msn_switchboard_get_conv(MsnSwitchBoard *swboard)
+{
+	GaimAccount *account;
+
+	g_return_val_if_fail(swboard != NULL, NULL);
+
+	if (swboard->conv != NULL)
+		return swboard->conv;
+
+	account = swboard->session->account;
+
+	return gaim_find_conversation_with_account(swboard->im_user, account);
+}
+
+void
+msn_switchboard_report_user(MsnSwitchBoard *swboard, GaimMessageFlags flags, const char *msg)
+{
+	GaimConversation *conv;
+
+	g_return_if_fail(swboard != NULL);
+	g_return_if_fail(msg != NULL);
+
+	if ((conv = msn_switchboard_get_conv(swboard)) != NULL)
+	{
+		gaim_conversation_write(conv, NULL, msg, flags, time(NULL));
+	}
+}
+
 static void
 bye_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	GaimAccount *account;
 	MsnSwitchBoard *swboard;
 	const char *user;
 
-	account = cmdproc->session->account;
 	swboard = cmdproc->servconn->data;
 	user = cmd->params[0];
 
@@ -155,16 +184,8 @@
 	}
 	else
 	{
-		char *username;
-		GaimConversation *conv;
-		GaimBuddy *b;
 		char *str = NULL;
 
-		if ((b = gaim_find_buddy(account, user)) != NULL)
-			username = gaim_escape_html(gaim_buddy_get_alias(b));
-		else
-			username = gaim_escape_html(user);
-
 		if (cmd->param_count == 2 && atoi(cmd->params[1]) == 1)
 		{
 			if (gaim_prefs_get_bool("/plugins/prpl/msn/conv_timeout_notice"))
@@ -177,22 +198,28 @@
 		{
 			if (gaim_prefs_get_bool("/plugins/prpl/msn/conv_close_notice"))
 			{
+				char *username;
+				GaimAccount *account;
+				GaimBuddy *b;
+
+				account = cmdproc->session->account;
+
+				if ((b = gaim_find_buddy(account, user)) != NULL)
+					username = gaim_escape_html(gaim_buddy_get_alias(b));
+				else
+					username = gaim_escape_html(user);
+
 				str = g_strdup_printf(_("%s has closed the conversation "
 										"window."), username);
+
+				g_free(username);
 			}
 		}
 
-		if (str != NULL &&
-			(conv = gaim_find_conversation_with_account(user, account)) != NULL)
-		{
-			gaim_conversation_write(conv, NULL, str, GAIM_MESSAGE_SYSTEM,
-									time(NULL));
-
-			g_free(str);
-		}
+		if (str != NULL)
+			msn_switchboard_report_user(swboard, GAIM_MESSAGE_SYSTEM, str);
 
 		msn_switchboard_disconnect(swboard);
-		g_free(username);
 	}
 }
 
@@ -433,6 +460,71 @@
 #endif
 }
 
+static void
+msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg)
+{
+	if (msg->type == MSN_MSG_TEXT)
+	{
+		MsnSwitchBoard *swboard;
+		char *body;
+		char *report;
+		char *str_reason;
+
+		swboard = cmdproc->servconn->data;
+
+		switch (swboard->error)
+		{
+			case MSN_SB_ERROR_OFFLINE:
+				str_reason = _("Message could not be sent, not allowed while invisible");
+				break;
+			case MSN_SB_ERROR_USER_OFFLINE:
+				str_reason = _("Message could not be sent because the user is offline");
+				break;
+			case MSN_SB_ERROR_CONNECTION:
+				str_reason = _("Message could not be sent because a connection error occured");
+				break;
+			default:
+				str_reason = _("Message could not be sent for an unkwnown reason");
+				break;
+		}
+
+		body = msn_message_to_string(msg);
+		report = g_strdup_printf(_("%s:\n%s"), str_reason, body);
+		gaim_debug_info("msn", "%s\n", report);
+		msn_switchboard_report_user(cmdproc->servconn->data, GAIM_MESSAGE_ERROR, report);
+		g_free(report);
+		g_free(body);
+	}
+}
+
+static void
+msg_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans)
+{
+	MsnMessage *msg;
+
+	msg = trans->data;
+	g_return_if_fail(msg != NULL);
+
+	gaim_debug_info("msn", "msg_timeout\n");
+	msg_error_helper(cmdproc, msg);
+}
+
+static void
+msg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
+{
+	msg_error_helper(cmdproc, trans->data);
+}
+
+static void
+msg_ack (MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	MsnMessage *msg;
+
+	msg = cmd->trans->data;
+
+	msg->ack_cb (msg->ack_data);
+}
+
 void
 msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
 {
@@ -450,11 +542,19 @@
 
 	/* msn_message_show_readable(msg, "SB SEND", FALSE); */
 
-	trans = msn_transaction_new("MSG", "%c %d", msn_message_get_flag(msg),
-								payload_len);
+	trans = msn_transaction_new(cmdproc, "MSG", "%c %d",
+								msn_message_get_flag(msg), payload_len);
+
+	/* Data for callbacks */
+	msn_transaction_set_data(trans, msg);
 
 	if (msg->ack_cb != NULL)
-		msn_transaction_add_cb(trans, "ACK", msg->ack_cb, msg->ack_data);
+	{
+		msn_transaction_add_cb(trans, "ACK", msg_ack);
+		msn_transaction_set_timeout_cb(trans, msg_timeout);
+	}
+	else if (msg->type == MSN_MSG_TEXT)
+		msn_transaction_set_timeout_cb(trans, msg_timeout);
 
 	trans->payload = payload;
 	trans->payload_len = payload_len;
@@ -563,7 +663,8 @@
 	msn_table_add_cmd(cbs_table, NULL, "ACK", NULL);
 #endif
 
-	msn_table_add_error(cbs_table, "MSG", NULL);
+	msn_table_add_error(cbs_table, "MSG", msg_error);
+	msn_table_add_error(cbs_table, "CAL", cal_error);
 
 	/* Register the message type callbacks. */
 	msn_table_add_msg_type(cbs_table, "text/plain",
@@ -637,6 +738,19 @@
 
 	swboard->destroying = TRUE;
 
+	/* Destroy the message queue */
+	while ((msg = g_queue_pop_head(swboard->im_queue)) != NULL)
+	{
+		if (swboard->error > 0)
+		{
+			/* The messages could not be sent due to an error */
+			msg_error_helper(swboard->servconn->cmdproc, msg);
+		}
+		msn_message_destroy(msg);
+	}
+
+	g_queue_free(swboard->im_queue);
+
 	if (swboard->im_user != NULL)
 		g_free(swboard->im_user);
 
@@ -655,36 +769,10 @@
 	if (swboard->servconn != NULL)
 		msn_servconn_destroy(swboard->servconn);
 
-	while ((msg = g_queue_pop_head(swboard->im_queue)) != NULL)
-		msn_message_destroy(msg);
-
-	g_queue_free(swboard->im_queue);
-
 	g_free(swboard);
 }
 
 #if 0
-void
-msn_switchboard_set_user(MsnSwitchBoard *swboard, const char *user)
-{
-	g_return_if_fail(swboard != NULL);
-
-	if (swboard->user != NULL)
-		g_free(swboard->user);
-
-	swboard->user = g_strdup(user);
-}
-
-const char *
-msn_switchboard_get_user(MsnSwitchBoard *swboard)
-{
-	g_return_val_if_fail(swboard != NULL, NULL);
-
-	return swboard->user;
-}
-#endif
-
-#if 0
 static void
 got_cal(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
@@ -699,6 +787,53 @@
 }
 #endif
 
+static void
+swboard_error_helper(MsnSwitchBoard *swboard, int reason, const char *passport)
+{
+	gaim_debug_info("msg", "Error: Unable to call the user %s\n", passport);
+
+	if (swboard->total_users == 0)
+	{
+		swboard->error = reason;
+		msn_switchboard_destroy(swboard);
+	}
+}
+
+static void
+cal_error_helper(MsnTransaction *trans, int reason)
+{
+	MsnSwitchBoard *swboard;
+	const char *passport;
+	char **params;
+
+	params = g_strsplit(trans->params, " ", 0);
+
+	passport = params[0];
+
+	swboard = trans->data;
+
+	swboard_error_helper(swboard, reason, passport);
+
+	g_strfreev(params);
+}
+
+static void
+cal_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans)
+{
+	cal_error_helper(trans, MSN_SB_ERROR_UNKNOWN);
+}
+
+static void
+cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
+{
+	int reason = MSN_SB_ERROR_UNKNOWN;
+
+	if (error == 217)
+		reason = MSN_SB_ERROR_USER_OFFLINE;
+
+	cal_error_helper(trans, reason);
+}
+
 void
 msn_switchboard_request_add_user(MsnSwitchBoard *swboard, const char *user)
 {
@@ -709,8 +844,11 @@
 
 	cmdproc = swboard->servconn->cmdproc;
 
-	trans = msn_transaction_new("CAL", "%s", user);
-	/* msn_transaction_add_cb(trans, "CAL", got_cal, NULL); */
+	trans = msn_transaction_new(cmdproc, "CAL", "%s", user);
+	/* msn_transaction_add_cb(trans, "CAL", got_cal); */
+
+	msn_transaction_set_data(trans, swboard);
+	msn_transaction_set_timeout_cb(trans, cal_timeout);
 
 	if (swboard->ready)
 		msn_cmdproc_send_trans(cmdproc, trans);
@@ -839,6 +977,20 @@
 	g_free(host);
 }
 
+static void
+xfr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
+{
+	MsnSwitchBoard *swboard;
+	int reason = MSN_SB_ERROR_UNKNOWN;
+
+	if (error == 913)
+		reason = MSN_SB_ERROR_OFFLINE;
+
+	swboard = trans->data;
+
+	swboard_error_helper(swboard, reason, swboard->im_user);
+}
+
 void
 msn_switchboard_request(MsnSwitchBoard *swboard)
 {
@@ -849,8 +1001,10 @@
 
 	cmdproc = swboard->session->notification->cmdproc;
 
-	trans = msn_transaction_new("XFR", "%s", "SB");
-	msn_transaction_add_cb(trans, "XFR", got_swboard, swboard);
+	trans = msn_transaction_new(cmdproc, "XFR", "%s", "SB");
+	msn_transaction_add_cb(trans, "XFR", got_swboard);
+	msn_transaction_set_data(trans, swboard);
+	msn_transaction_set_error_cb(trans, xfr_error);
 
 	msn_cmdproc_send_trans(cmdproc, trans);
 }
--- a/src/protocols/msn/switchboard.h	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/switchboard.h	Sun Nov 21 17:48:09 2004 +0000
@@ -33,6 +33,17 @@
 
 #include "servconn.h"
 
+typedef enum
+{
+	MSN_SB_ERROR_NONE,
+	MSN_SB_ERROR_CAL, /* The user could not join (answer the call) */
+	MSN_SB_ERROR_OFFLINE, /* The account is offline */
+	MSN_SB_ERROR_USER_OFFLINE, /* The user to call is offline */
+	MSN_SB_ERROR_CONNECTION, /* There was a connection error */
+	MSN_SB_ERROR_UNKNOWN
+
+} MsnSBErrorType;
+
 struct _MsnSwitchBoard
 {
 	MsnSession *session;
@@ -60,6 +71,8 @@
 
 	gboolean user_joined;
 	GQueue *im_queue;
+
+	int error;
 };
 
 /**
--- a/src/protocols/msn/table.h	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/table.h	Sun Nov 21 17:48:09 2004 +0000
@@ -30,8 +30,6 @@
 #include "transaction.h"
 #include "msg.h"
 
-typedef void (*MsnErrorCb)(MsnCmdProc *cmdproc, MsnTransaction *trans,
-						   int error);
 typedef void (*MsnMsgCb)(MsnCmdProc *cmdproc, MsnMessage *msg);
 
 struct _MsnTable
--- a/src/protocols/msn/transaction.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/transaction.c	Sun Nov 21 17:48:09 2004 +0000
@@ -25,7 +25,8 @@
 #include "transaction.h"
 
 MsnTransaction *
-msn_transaction_new(const char *command, const char *format, ...)
+msn_transaction_new(MsnCmdProc *cmdproc, const char *command,
+					const char *format, ...)
 {
 	MsnTransaction *trans;
 	va_list arg;
@@ -34,6 +35,7 @@
 
 	trans = g_new0(MsnTransaction, 1);
 
+	trans->cmdproc = cmdproc;
 	trans->command = g_strdup(command);
 
 	if (format != NULL)
@@ -73,6 +75,9 @@
 	}
 #endif
 
+	if (trans->timer)
+		gaim_timeout_remove(trans->timer);
+
 	g_free(trans);
 }
 
@@ -156,7 +161,7 @@
 
 void
 msn_transaction_add_cb(MsnTransaction *trans, char *answer,
-					   MsnTransCb cb, void *data)
+					   MsnTransCb cb)
 {
 	g_return_if_fail(trans  != NULL);
 	g_return_if_fail(answer != NULL);
@@ -165,6 +170,38 @@
 		trans->callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
 
 	g_hash_table_insert(trans->callbacks, answer, cb);
+}
 
-	trans->data = data;
+static gboolean
+transaction_timeout(gpointer data)
+{
+	MsnTransaction *trans;
+
+	trans = data;
+	g_return_val_if_fail(trans != NULL, FALSE);
+
+	gaim_debug_info("msn", "%s %d %s\n", trans->command, trans->trId, trans->params);
+
+	if (trans->timeout_cb != NULL)
+		trans->timeout_cb(trans->cmdproc, trans);
+
+	return FALSE;
 }
+
+void
+msn_transaction_set_timeout_cb(MsnTransaction *trans, MsnTimeoutCb cb)
+{
+	if (trans->timer)
+	{
+		gaim_debug_error("msn", "This shouldn't be happening\n");
+		gaim_timeout_remove(trans->timer);
+	}
+	trans->timeout_cb = cb;
+	trans->timer = gaim_timeout_add(60000, transaction_timeout, trans);
+}
+
+void
+msn_transaction_set_error_cb(MsnTransaction *trans, MsnErrorCb cb)
+{
+	trans->error_cb = cb;
+}
--- a/src/protocols/msn/transaction.h	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/transaction.h	Sun Nov 21 17:48:09 2004 +0000
@@ -30,28 +30,38 @@
 #include "cmdproc.h"
 
 typedef void (*MsnTransCb)(MsnCmdProc *cmdproc, MsnCommand *cmd);
+typedef void (*MsnTimeoutCb)(MsnCmdProc *cmdproc, MsnTransaction *trans);
+typedef void (*MsnErrorCb)(MsnCmdProc *cmdproc, MsnTransaction *trans,
+						   int error);
 
 /**
- * A transaction. A command that will initiate the transaction.
+ * A transaction. A sending command that will initiate the transaction.
  */
 struct _MsnTransaction
 {
+	MsnCmdProc *cmdproc;
 	unsigned int trId;
 
 	char *command;
 	char *params;
 
+	int timer;
+
+	void *data; /* The data to be used on the different callbacks */
 	GHashTable *callbacks;
-	void *data;
+	MsnErrorCb error_cb;
+	MsnTimeoutCb timeout_cb;
 
 	char *payload;
 	size_t payload_len;
 
 	GQueue *queue;
-	MsnCommand *pendent_cmd;
+	MsnCommand *pendent_cmd; /* The command that is waiting for the result of
+								this transaction. */
 };
 
-MsnTransaction *msn_transaction_new(const char *command,
+MsnTransaction *msn_transaction_new(MsnCmdProc *cmdproc,
+									const char *command,
 									const char *format, ...);
 void msn_transaction_destroy(MsnTransaction *trans);
 
@@ -62,6 +72,8 @@
 								 const char *payload, int payload_len);
 void msn_transaction_set_data(MsnTransaction *trans, void *data);
 void msn_transaction_add_cb(MsnTransaction *trans, char *answer,
-							MsnTransCb cb, void *data);
+							MsnTransCb cb);
+void msn_transaction_set_error_cb(MsnTransaction *trans, MsnErrorCb cb);
+void msn_transaction_set_timeout_cb(MsnTransaction *trans, MsnTimeoutCb cb);
 
 #endif /* _MSN_TRANSACTION_H */
--- a/src/protocols/msn/userlist.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/msn/userlist.c	Sun Nov 21 17:48:09 2004 +0000
@@ -73,7 +73,7 @@
 got_new_entry(GaimConnection *gc, const char *passport, const char *friendly)
 {
 	MsnPermitAdd *pa;
-	char *msg;
+	char *msg, *escaped;
 
 	pa      = g_new0(MsnPermitAdd, 1);
 	pa->who = g_strdup(passport);
@@ -81,11 +81,13 @@
 
 	if (friendly != NULL)
 	{
+		escaped = g_markup_escape_text(friendly, -1);
 		msg = g_strdup_printf(
 				   _("The user %s (%s) wants to add %s to his or her "
 					 "buddy list."),
-				   passport, friendly,
+				   passport, escaped,
 				   gaim_account_get_username(gc->account));
+		g_free(escaped);
 	}
 	else
 	{
@@ -173,7 +175,7 @@
 	if (old_group_name)
 		data->old_group_name = g_strdup(old_group_name);
 
-	trans = msn_transaction_new("ADG", "%s %d",
+	trans = msn_transaction_new(cmdproc, "ADG", "%s %d",
 								gaim_url_encode(new_group_name),
 								0);
 
--- a/src/protocols/silc/silc.c	Sun Nov 21 06:16:23 2004 +0000
+++ b/src/protocols/silc/silc.c	Sun Nov 21 17:48:09 2004 +0000
@@ -65,12 +65,16 @@
 silcgaim_set_status(GaimAccount *account, GaimStatus *status)
 {
 	GaimConnection *gc = gaim_account_get_connection(account);
-	SilcGaim sg = gc->proto_data;
+	SilcGaim sg;
 	SilcUInt32 mode;
 	SilcBuffer idp;
 	unsigned char mb[4];
 	const char *state;
 
+	g_return_if_fail(gc != NULL);
+
+	sg = gc->proto_data;
+
 	if ((status == NULL) || (sg->conn == NULL))
 		return;