changeset 27471:ef48fb87d8d2

merge of '834ee1803fbad0f4960a66592ac43641f23124c6' and 'a743f7b2086257b37f218802f4ae61803cf5ff01'
author Paul Aurich <paul@darkrain42.org>
date Sat, 11 Jul 2009 07:47:45 +0000
parents 2dff07ddcc83 (diff) e90fa379fcec (current diff)
children f1f901b3d6f2
files
diffstat 14 files changed, 290 insertions(+), 144 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Jul 11 07:47:07 2009 +0000
+++ b/ChangeLog	Sat Jul 11 07:47:45 2009 +0000
@@ -29,6 +29,7 @@
 	* Add support for receiving handwritten (ink) messages on MSN.
 	* Don't do IPv6 address lookups if the computer does not have an IPv6
 	  address configured.
+	* Add support for receiving audio clips on MSN.
 
 	AIM and ICQ:
 	* Preliminary support for a new authentication scheme called
--- a/configure.ac	Sat Jul 11 07:47:07 2009 +0000
+++ b/configure.ac	Sat Jul 11 07:47:45 2009 +0000
@@ -1731,8 +1731,8 @@
 	LIBS="$LIBS $GNUTLS_LIBS"
 	CPPFLAGS_save="$CPPFLAGS"
 	CPPFLAGS="$CPPFLAGS $GNUTLS_CFLAGS"
-	AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <gnutls/gnutls.h>],
-                                        [gnutls_session s; gnutls_priority_set_direct(s, NULL, NULL);])],
+	AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <gnutls/gnutls.h>]],
+                                        [[gnutls_session s; gnutls_priority_set_direct(s, NULL, NULL);]])],
 	               [AC_DEFINE([HAVE_GNUTLS_PRIORITY_FUNCS], 1,
                                   [Define if your gnutls has gnutls_priority_set_direct and friends])
 	                AC_MSG_RESULT(yes)],
@@ -2020,6 +2020,23 @@
 	AC_SUBST(NSS_CFLAGS)
 	AC_SUBST(NSS_LIBS)
 fi
+ 
+if test "x$enable_nss" = "xyes"; then
+	AC_MSG_CHECKING(for NSS_SetAlgorithmPolicy)
+	LIBS_save="$LIBS"
+	LIBS="$LIBS $NSS_LIBS"
+	CPPFLAGS_save="$CPPFLAGS"
+	CPPFLAGS="$CPPFLAGS $NSS_CFLAGS"
+	AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <nss.h>
+#include <secmod.h>]],
+                                        [[NSS_SetAlgorithmPolicy(SEC_OID_MD2, 0, 0);]])],
+	               [AC_DEFINE([NEED_NSS_WEAK_ALGORITHMS], 1,
+                                  [Define if your NSS needs weak algorithms activated with NSS_SetAlgorithmPolicy])
+	                AC_MSG_RESULT(yes)],
+	               [AC_MSG_RESULT(no)])
+	CPPFLAGS="$CPPFLAGS_save"
+        LIBS="$LIBS_save"
+fi
 
 AM_CONDITIONAL(USE_NSS, test "x$enable_nss" = "xyes")
 
--- a/libpurple/plugins/ssl/ssl-nss.c	Sat Jul 11 07:47:07 2009 +0000
+++ b/libpurple/plugins/ssl/ssl-nss.c	Sat Jul 11 07:47:45 2009 +0000
@@ -152,6 +152,12 @@
 	SSL_CipherPrefSetDefault(SSL_DHE_RSA_WITH_DES_CBC_SHA, 1);
 	SSL_CipherPrefSetDefault(SSL_DHE_DSS_WITH_DES_CBC_SHA, 1);
 
+#ifdef NEED_NSS_WEAK_ALGORITHMS
+	/* Enable some weaker algorithms for XMPP and MSN */
+	NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION, NSS_USE_ALG_IN_CERT_SIGNATURE, 0);
+	NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION, NSS_USE_ALG_IN_CERT_SIGNATURE, 0);
+#endif
+
 	_identity = PR_GetUniqueIdentity("Purple");
 	_nss_methods = PR_GetDefaultIOMethods();
 }
--- a/libpurple/protocols/msn/httpconn.c	Sat Jul 11 07:47:07 2009 +0000
+++ b/libpurple/protocols/msn/httpconn.c	Sat Jul 11 07:47:45 2009 +0000
@@ -161,10 +161,9 @@
 	memcpy(body, body_start, body_len);
 	body[body_len] = '\0';
 
-#ifdef MSN_DEBUG_HTTP
-	purple_debug_misc("msn", "Incoming HTTP buffer (header): {%s}\n",
-					header);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_misc("msn", "Incoming HTTP buffer (header): {%s}\n",
+		                  header);
 
 	/* Now we should be able to process the data. */
 	if ((s = purple_strcasestr(header, "X-MSN-Messenger: ")) != NULL)
--- a/libpurple/protocols/msn/msg.c	Sat Jul 11 07:47:07 2009 +0000
+++ b/libpurple/protocols/msn/msg.c	Sat Jul 11 07:47:45 2009 +0000
@@ -33,9 +33,8 @@
 	msg = g_new0(MsnMessage, 1);
 	msg->type = type;
 
-#ifdef MSN_DEBUG_MSG
-	purple_debug_info("msn", "message new (%p)(%d)\n", msg, type);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "message new (%p)(%d)\n", msg, type);
 
 	msg->attr_table = g_hash_table_new_full(g_str_hash, g_str_equal,
 											g_free, g_free);
@@ -57,9 +56,8 @@
 		return;
 	}
 
-#ifdef MSN_DEBUG_MSG
-	purple_debug_info("msn", "message destroy (%p)\n", msg);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "message destroy (%p)\n", msg);
 
 	g_free(msg->remote_user);
 	g_free(msg->body);
@@ -79,9 +77,8 @@
 
 	msg->ref_count++;
 
-#ifdef MSN_DEBUG_MSG
-	purple_debug_info("msn", "message ref (%p)[%" G_GSIZE_FORMAT "]\n", msg, msg->ref_count);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "message ref (%p)[%" G_GSIZE_FORMAT "]\n", msg, msg->ref_count);
 
 	return msg;
 }
@@ -94,9 +91,8 @@
 
 	msg->ref_count--;
 
-#ifdef MSN_DEBUG_MSG
-	purple_debug_info("msn", "message unref (%p)[%" G_GSIZE_FORMAT "]\n", msg, msg->ref_count);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "message unref (%p)[%" G_GSIZE_FORMAT "]\n", msg, msg->ref_count);
 
 	if (msg->ref_count == 0)
 	{
@@ -937,6 +933,91 @@
 	}
 }
 
+static void
+datacast_inform_user(MsnSwitchBoard *swboard, const char *who,
+                     const char *msg, const char *filename)
+{
+	char *username, *str;
+	PurpleAccount *account;
+	PurpleBuddy *b;
+
+	account = swboard->session->account;
+
+	if ((b = purple_find_buddy(account, who)) != NULL)
+		username = g_markup_escape_text(purple_buddy_get_alias(b), -1);
+	else
+		username = g_markup_escape_text(who, -1);
+	str = g_strdup_printf(msg, username, filename);
+	g_free(username);
+
+	if (swboard->conv == NULL) {
+		if (swboard->current_users > 1) 
+			swboard->conv = purple_find_chat(account->gc, swboard->chat_id);
+		else {
+			swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+									who, account);
+			if (swboard->conv == NULL)
+				swboard->conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, who);
+		}
+	}
+	swboard->flag |= MSN_SB_FLAG_IM;
+
+	purple_conversation_write(swboard->conv, NULL, str, PURPLE_MESSAGE_SYSTEM, time(NULL));
+	g_free(str);
+
+}
+
+/* TODO: Make these not be such duplicates of each other */
+static void 
+got_wink_cb(MsnSlpCall *slpcall, const guchar *data, gsize size)
+{
+	FILE *f;
+	char *path = NULL;
+	const char *who = slpcall->slplink->remote_user;
+	purple_debug_info("msn", "Received wink from %s\n", who);
+
+	if ((f = purple_mkstemp(&path, TRUE))) {
+		fwrite(data, size, 1, f);
+		fclose(f);
+		datacast_inform_user(slpcall->slplink->swboard,
+		                     who,
+		                     _("%s sent a wink. <a href='msn-wink://%s'>Click here to play it</a>"),
+		                     path);
+	} else {
+		purple_debug_error("msn", "Couldn\'t create temp file to store wink\n");
+		datacast_inform_user(slpcall->slplink->swboard,
+		                     who,
+		                     _("%s sent a wink, but it could not be saved"),
+		                     NULL);
+	} 
+	g_free(path);
+}
+
+static void 
+got_voiceclip_cb(MsnSlpCall *slpcall, const guchar *data, gsize size)
+{
+	FILE *f;
+	char *path = NULL;
+	const char *who = slpcall->slplink->remote_user;
+	purple_debug_info("msn", "Received voice clip from %s\n", who);
+
+	if ((f = purple_mkstemp(&path, TRUE))) {
+		fwrite(data, size, 1, f);
+		fclose(f);
+		datacast_inform_user(slpcall->slplink->swboard,
+		                     who,
+		                     _("%s sent a voice clip. <a href='audio://%s'>Click here to play it</a>"),
+		                     path);
+	} else {
+		purple_debug_error("msn", "Couldn\'t create temp file to store sound\n");
+		datacast_inform_user(slpcall->slplink->swboard,
+		                     who,
+		                     _("%s sent a voice clip, but it could not be saved"),
+		                     NULL);
+	} 
+	g_free(path);
+}
+
 void
 msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
@@ -970,9 +1051,42 @@
 
 	} else if (!strcmp(id, "2")) {
 		/* Wink */
+		MsnSession *session;
+		MsnSlpLink *slplink;
+		MsnObject *obj;
+		const char *who;
+		const char *data;
+
+		session = cmdproc->session;
+
+		data = g_hash_table_lookup(body, "Data");
+		obj = msn_object_new_from_string(data);
+		who = msn_object_get_creator(obj);
+
+		slplink = msn_session_get_slplink(session, who);
+		msn_slplink_request_object(slplink, data, got_wink_cb, NULL, obj);
+
+		msn_object_destroy(obj);
+
 
 	} else if (!strcmp(id, "3")) {
 		/* Voiceclip */
+		MsnSession *session;
+		MsnSlpLink *slplink;
+		MsnObject *obj;
+		const char *who;
+		const char *data;
+
+		session = cmdproc->session;
+
+		data = g_hash_table_lookup(body, "Data");
+		obj = msn_object_new_from_string(data);
+		who = msn_object_get_creator(obj);
+
+		slplink = msn_session_get_slplink(session, who);
+		msn_slplink_request_object(slplink, data, got_voiceclip_cb, NULL, obj);
+
+		msn_object_destroy(obj);
 
 	} else if (!strcmp(id, "4")) {
 		/* Action */
--- a/libpurple/protocols/msn/msn.h	Sat Jul 11 07:47:07 2009 +0000
+++ b/libpurple/protocols/msn/msn.h	Sat Jul 11 07:47:45 2009 +0000
@@ -24,17 +24,6 @@
 #ifndef _MSN_H_
 #define _MSN_H_
 
-/* #define MSN_DEBUG_MSG 1 */
-/* #define MSN_DEBUG_SLPMSG 1 */
-/* #define MSN_DEBUG_HTTP 1 */
-
-/* #define MSN_DEBUG_SLP 1 */
-/* #define MSN_DEBUG_SLP_VERBOSE 1 */
-/* #define MSN_DEBUG_SLP_FILES 1 */
-
-/* #define MSN_DEBUG_NS 1 */
-/* #define MSN_DEBUG_SB 1 */
-
 #include "internal.h"
 
 #include "account.h"
@@ -138,7 +127,7 @@
 } MsnClientVerId;
 
 #define MSN_CLIENT_ID_VERSION      MSN_CLIENT_VER_7_0
-#define MSN_CLIENT_ID_CAPABILITIES (MSN_CLIENT_CAP_PACKET|MSN_CLIENT_CAP_INK_GIF)
+#define MSN_CLIENT_ID_CAPABILITIES (MSN_CLIENT_CAP_PACKET|MSN_CLIENT_CAP_INK_GIF|MSN_CLIENT_CAP_VOICEIM|MSN_CLIENT_CAP_WINKS)
 
 #define MSN_CLIENT_ID \
 	((MSN_CLIENT_ID_VERSION    << 24) | \
--- a/libpurple/protocols/msn/notification.c	Sat Jul 11 07:47:07 2009 +0000
+++ b/libpurple/protocols/msn/notification.c	Sat Jul 11 07:47:45 2009 +0000
@@ -310,9 +310,8 @@
 	msg = msn_message_new_from_cmd(cmdproc->session, cmd);
 
 	msn_message_parse_payload(msg, payload, len, MSG_LINE_DEM, MSG_BODY_DEM);
-#ifdef MSN_DEBUG_NS
-	msn_message_show_readable(msg, "Notification", TRUE);
-#endif
+	if (purple_debug_is_verbose())
+		msn_message_show_readable(msg, "Notification", TRUE);
 
 	msn_cmdproc_process_msg(cmdproc, msg);
 
@@ -376,9 +375,8 @@
 	msg = msn_message_new_from_cmd(cmdproc->session, cmd);
 
 	msn_message_parse_payload(msg, payload, len,MSG_LINE_DEM,MSG_BODY_DEM);
-#ifdef MSN_DEBUG_NS
-	msn_message_show_readable(msg, "Notification", TRUE);
-#endif
+	if (purple_debug_is_verbose())
+		msn_message_show_readable(msg, "Notification", TRUE);
 
 	gc = cmdproc->session->account->gc;
 	passport = msg->remote_user;
--- a/libpurple/protocols/msn/slp.c	Sat Jul 11 07:47:07 2009 +0000
+++ b/libpurple/protocols/msn/slp.c	Sat Jul 11 07:47:45 2009 +0000
@@ -34,8 +34,6 @@
 
 /* ms to delay between sending buddy icon requests to the server. */
 #define BUDDY_ICON_DELAY 20
-/*debug SLP*/
-#define MSN_DEBUG_UD
 
 static void send_ok(MsnSlpCall *slpcall, const char *branch,
 					const char *type, const char *content);
@@ -212,10 +210,8 @@
 								"MSNSLP/1.0 200 OK",
 								branch, type, content);
 
-#ifdef MSN_DEBUG_SLP
 	slpmsg->info = "SLP 200 OK";
 	slpmsg->text_body = TRUE;
-#endif
 
 	msn_slplink_queue_slpmsg(slplink, slpmsg);
 
@@ -236,10 +232,8 @@
 								"MSNSLP/1.0 603 Decline",
 								branch, type, content);
 
-#ifdef MSN_DEBUG_SLP
 	slpmsg->info = "SLP 603 Decline";
 	slpmsg->text_body = TRUE;
-#endif
 
 	msn_slplink_queue_slpmsg(slplink, slpmsg);
 }
@@ -309,18 +303,14 @@
 		slpmsg->slpcall = slpcall;
 		slpmsg->session_id = slpcall->session_id;
 		msn_slpmsg_set_body(slpmsg, NULL, 4);
-#ifdef MSN_DEBUG_SLP
 		slpmsg->info = "SLP DATA PREP";
-#endif
 		msn_slplink_queue_slpmsg(slplink, slpmsg);
 
 		/* DATA */
 		slpmsg = msn_slpmsg_new(slplink);
 		slpmsg->slpcall = slpcall;
 		slpmsg->flags = 0x20;
-#ifdef MSN_DEBUG_SLP
 		slpmsg->info = "SLP DATA";
-#endif
 		msn_slpmsg_set_image(slpmsg, img);
 		msn_slplink_queue_slpmsg(slplink, slpmsg);
 		purple_imgstore_unref(img);
@@ -448,10 +438,8 @@
 								"\r\n");
 	g_free(header);
 
-#ifdef MSN_DEBUG_SLP
 	slpmsg->info = "SLP BYE";
 	slpmsg->text_body = TRUE;
-#endif
 
 	msn_slplink_queue_slpmsg(slplink, slpmsg);
 }
@@ -619,10 +607,8 @@
 										"application/x-msnmsgr-transreqbody",
 										content);
 
-#ifdef MSN_DEBUG_SLP
 			slpmsg->info = "SLP INVITE";
 			slpmsg->text_body = TRUE;
-#endif
 			msn_slplink_send_slpmsg(slplink, slpmsg);
 
 			g_free(header);
@@ -839,9 +825,8 @@
 		purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
 		purple_conv_custom_smiley_close(conv, slpcall->data_info );
 	}
-#ifdef MSN_DEBUG_UD
-	purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
 }
 
 void
@@ -963,9 +948,8 @@
 
 	g_return_if_fail(userlist != NULL);
 
-#ifdef MSN_DEBUG_UD
-	purple_debug_info("msn", "Releasing buddy icon request\n");
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "Releasing buddy icon request\n");
 
 	if (userlist->buddy_icon_window > 0)
 	{
@@ -986,10 +970,10 @@
 		userlist->buddy_icon_window--;
 		request_user_display(user);
 
-#ifdef MSN_DEBUG_UD
-		purple_debug_info("msn", "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n",
-						userlist->buddy_icon_window);
-#endif
+		if (purple_debug_is_verbose())
+			purple_debug_info("msn",
+			                  "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n",
+			                  userlist->buddy_icon_window);
 	}
 }
 
@@ -1039,10 +1023,9 @@
 		userlist = user->userlist;
 		queue = userlist->buddy_icon_requests;
 
-#ifdef MSN_DEBUG_UD
-		purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n",
-						user->passport, userlist->buddy_icon_window);
-#endif
+		if (purple_debug_is_verbose())
+			purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n",
+			                  user->passport, userlist->buddy_icon_window);
 
 		g_queue_push_tail(queue, user);
 
@@ -1062,9 +1045,8 @@
 	g_return_if_fail(slpcall != NULL);
 
 	info = slpcall->data_info;
-#ifdef MSN_DEBUG_UD
-	purple_debug_info("msn", "Got User Display: %s\n", slpcall->slplink->remote_user);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "Got User Display: %s\n", slpcall->slplink->remote_user);
 
 	userlist = slpcall->slplink->session->userlist;
 	account = slpcall->slplink->session->account;
@@ -1090,9 +1072,8 @@
 
 	g_return_if_fail(session != NULL);
 
-#ifdef MSN_DEBUG_UD
-	purple_debug_info("msn", "End User Display\n");
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "End User Display\n");
 
 	userlist = session->userlist;
 
@@ -1150,9 +1131,8 @@
 		gconstpointer data = NULL;
 		size_t len = 0;
 
-#ifdef MSN_DEBUG_UD
-		purple_debug_info("msn", "Requesting our own user display\n");
-#endif
+		if (purple_debug_is_verbose())
+			purple_debug_info("msn", "Requesting our own user display\n");
 
 		my_obj = msn_user_get_object(session->user);
 
@@ -1168,10 +1148,9 @@
 		/* Free one window slot */
 		session->userlist->buddy_icon_window++;
 
-#ifdef MSN_DEBUG_UD
-		purple_debug_info("msn", "request_user_display(): buddy_icon_window++ yields =%d\n",
-						session->userlist->buddy_icon_window);
-#endif
+		if (purple_debug_is_verbose())
+			purple_debug_info("msn", "request_user_display(): buddy_icon_window++ yields =%d\n",
+			                  session->userlist->buddy_icon_window);
 
 		msn_release_buddy_icon_request(session->userlist);
 	}
--- a/libpurple/protocols/msn/slpcall.c	Sat Jul 11 07:47:07 2009 +0000
+++ b/libpurple/protocols/msn/slpcall.c	Sat Jul 11 07:47:45 2009 +0000
@@ -27,8 +27,6 @@
 
 #include "slp.h"
 
-/* #define MSN_DEBUG_SLPCALL */
-
 /**************************************************************************
  * Main
  **************************************************************************/
@@ -40,9 +38,8 @@
 
 	slpcall = data;
 
-#ifdef MSN_DEBUG_SLPCALL
-	purple_debug_info("msn", "slpcall_timeout: slpcall(%p)\n", slpcall);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "slpcall_timeout: slpcall(%p)\n", slpcall);
 
 	if (!slpcall->pending && !slpcall->progress)
 	{
@@ -64,9 +61,8 @@
 
 	slpcall = g_new0(MsnSlpCall, 1);
 
-#ifdef MSN_DEBUG_SLPCALL
-	purple_debug_info("msn", "slpcall_new: slpcall(%p)\n", slpcall);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "slpcall_new: slpcall(%p)\n", slpcall);
 
 	slpcall->slplink = slplink;
 
@@ -82,9 +78,8 @@
 {
 	GList *e;
 
-#ifdef MSN_DEBUG_SLPCALL
-	purple_debug_info("msn", "slpcall_destroy: slpcall(%p)\n", slpcall);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "slpcall_destroy: slpcall(%p)\n", slpcall);
 
 	g_return_if_fail(slpcall != NULL);
 
@@ -96,10 +91,9 @@
 		MsnSlpMessage *slpmsg = e->data;
 		e = e->next;
 
-#ifdef MSN_DEBUG_SLPCALL_VERBOSE
-		purple_debug_info("msn", "slpcall_destroy: trying slpmsg(%p)\n",
-						slpmsg);
-#endif
+		if (purple_debug_is_verbose())
+			purple_debug_info("msn", "slpcall_destroy: trying slpmsg(%p)\n",
+			                  slpmsg);
 
 		if (slpmsg->slpcall == slpcall)
 		{
@@ -173,10 +167,8 @@
 	slpmsg = msn_slpmsg_sip_new(slpcall, 0, header, slpcall->branch,
 								"application/x-msnmsgr-sessionreqbody", content);
 
-#ifdef MSN_DEBUG_SLP
 	slpmsg->info = "SLP INVITE";
 	slpmsg->text_body = TRUE;
-#endif
 
 	msn_slplink_send_slpmsg(slplink, slpmsg);
 
--- a/libpurple/protocols/msn/slpmsg.c	Sat Jul 11 07:47:07 2009 +0000
+++ b/libpurple/protocols/msn/slpmsg.c	Sat Jul 11 07:47:45 2009 +0000
@@ -36,9 +36,8 @@
 
 	slpmsg = g_new0(MsnSlpMessage, 1);
 
-#ifdef MSN_DEBUG_SLPMSG
-	purple_debug_info("msn", "slpmsg new (%p)\n", slpmsg);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "slpmsg new (%p)\n", slpmsg);
 
 	slpmsg->slplink = slplink;
 
@@ -56,9 +55,8 @@
 
 	g_return_if_fail(slpmsg != NULL);
 
-#ifdef MSN_DEBUG_SLPMSG
-	purple_debug_info("msn", "slpmsg destroy (%p)\n", slpmsg);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "slpmsg destroy (%p)\n", slpmsg);
 
 	slplink = slpmsg->slplink;
 
@@ -72,13 +70,6 @@
 	if (slpmsg->img == NULL)
 		g_free(slpmsg->buffer);
 
-#ifdef MSN_DEBUG_SLP
-	/*
-	if (slpmsg->info != NULL)
-		g_free(slpmsg->info);
-	*/
-#endif
-
 	for (cur = slpmsg->msgs; cur != NULL; cur = cur->next)
 	{
 		/* Something is pointing to this slpmsg, so we should remove that
@@ -87,10 +78,6 @@
 
 		MsnMessage *msg = cur->data;
 
-#ifdef MSN_DEBUG_SLPMSG
-		purple_debug_info("msn", "Unlink slpmsg callbacks.\n");
-#endif
-
 		msg->ack_cb = NULL;
 		msg->nak_cb = NULL;
 		msg->ack_data = NULL;
@@ -132,7 +119,6 @@
 	slpmsg->size = purple_imgstore_get_size(img);
 }
 
-#ifdef MSN_DEBUG_SLP
 void
 msn_slpmsg_show(MsnMessage *msg)
 {
@@ -161,7 +147,6 @@
 
 	msn_message_show_readable(msg, info, text);
 }
-#endif
 
 MsnSlpMessage *
 msn_slpmsg_sip_new(MsnSlpCall *slpcall, int cseq,
--- a/libpurple/protocols/msn/slpmsg.h	Sat Jul 11 07:47:07 2009 +0000
+++ b/libpurple/protocols/msn/slpmsg.h	Sat Jul 11 07:47:45 2009 +0000
@@ -68,10 +68,8 @@
 	MsnMessage *msg; /**< The temporary real message that will be sent. */
 #endif
 
-#ifdef MSN_DEBUG_SLP
-	char *info;
+	const char *info;
 	gboolean text_body;
-#endif
 };
 
 /**
@@ -100,8 +98,6 @@
 								   const char *content_type,
 								   const char *content);
 
-#ifdef MSN_DEBUG_SLP
 void msn_slpmsg_show(MsnMessage *msg);
-#endif
 
 #endif /* _MSN_SLPMSG_H_ */
--- a/libpurple/protocols/msn/switchboard.c	Sat Jul 11 07:47:07 2009 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Sat Jul 11 07:47:45 2009 +0000
@@ -60,6 +60,9 @@
 
 	session->switches = g_list_prepend(session->switches, swboard);
 
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "switchboard new: swboard(%p)\n", swboard);
+
 	return swboard;
 }
 
@@ -70,9 +73,8 @@
 	MsnMessage *msg;
 	GList *l;
 
-#ifdef MSN_DEBUG_SB
-	purple_debug_info("msn", "switchboard_destroy: swboard(%p)\n", swboard);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "switchboard destroy: swboard(%p)\n", swboard);
 
 	g_return_if_fail(swboard != NULL);
 
@@ -230,10 +232,9 @@
 	swboard->current_users++;
 	swboard->empty = FALSE;
 
-#ifdef MSN_DEBUG_CHAT
-	purple_debug_info("msn", "user=[%s], total=%d\n", user,
-					swboard->current_users);
-#endif
+	if (purple_debug_is_verbose())
+		purple_debug_info("msn", "user=[%s], total=%d\n",
+		                  user, swboard->current_users);
 
 	if (!(swboard->flag & MSN_SB_FLAG_IM) && (swboard->conv != NULL))
 	{
@@ -255,10 +256,6 @@
 		{
 			GList *l;
 
-#ifdef MSN_DEBUG_CHAT
-			purple_debug_info("msn", "[chat] Switching to chat.\n");
-#endif
-
 #if 0
 			/* this is bad - it causes msn_switchboard_close to be called on the
 			 * switchboard we're in the middle of using :( */
@@ -278,18 +275,10 @@
 
 				tmp_user = l->data;
 
-#ifdef MSN_DEBUG_CHAT
-				purple_debug_info("msn", "[chat] Adding [%s].\n", tmp_user);
-#endif
-
 				purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv),
 										tmp_user, NULL, PURPLE_CBFLAGS_NONE, TRUE);
 			}
 
-#ifdef MSN_DEBUG_CHAT
-			purple_debug_info("msn", "[chat] We add ourselves.\n");
-#endif
-
 			purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv),
 									purple_account_get_username(account),
 									NULL, PURPLE_CBFLAGS_NONE, TRUE);
@@ -589,10 +578,10 @@
 
 	payload = msn_message_gen_payload(msg, &payload_len);
 
-#ifdef MSN_DEBUG_SB
-	purple_debug_info("msn", "SB length:{%" G_GSIZE_FORMAT "}\n", payload_len);
-	msn_message_show_readable(msg, "SB SEND", FALSE);
-#endif
+	if (purple_debug_is_verbose()) {
+		purple_debug_info("msn", "SB length:{%" G_GSIZE_FORMAT "}\n", payload_len);
+		msn_message_show_readable(msg, "SB SEND", FALSE);
+	}
 
 	flag = msn_message_get_flag(msg);
 	trans = msn_transaction_new(cmdproc, "MSG", "%c %" G_GSIZE_FORMAT,
@@ -790,9 +779,8 @@
 
 	msn_message_parse_payload(msg, payload, len,
 					MSG_LINE_DEM,MSG_BODY_DEM);
-#ifdef MSN_DEBUG_SB
-	msn_message_show_readable(msg, "SB RECV", FALSE);
-#endif
+	if (purple_debug_is_verbose())
+		msn_message_show_readable(msg, "SB RECV", FALSE);
 
 	g_free (msg->remote_user);
 	msg->remote_user = g_strdup(cmd->params[0]);
--- a/pidgin/gtkconv.c	Sat Jul 11 07:47:07 2009 +0000
+++ b/pidgin/gtkconv.c	Sat Jul 11 07:47:45 2009 +0000
@@ -4939,6 +4939,7 @@
 
 	gtk_widget_set_name(gtkconv->imhtml, "pidgin_conv_imhtml");
 	gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE);
+	g_object_set_data(gtkconv->imhtml, "gtkconv", gtkconv);
 
 	gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(imhtml_sw),
 	                               &imhtml_sw_hscroll, NULL);
--- a/pidgin/gtkutils.c	Sat Jul 11 07:47:07 2009 +0000
+++ b/pidgin/gtkutils.c	Sat Jul 11 07:47:45 2009 +0000
@@ -54,6 +54,7 @@
 #include "prpl.h"
 #include "request.h"
 #include "signals.h"
+#include "sound.h"
 #include "util.h"
 
 #include "gtkaccount.h"
@@ -3707,6 +3708,83 @@
 	return TRUE;
 }
 
+#define AUDIOLINKSIZE  (sizeof("audio://") - 1)
+static gboolean
+audio_clicked_cb(GtkIMHtml *imhtml, GtkIMHtmlLink *link)
+{
+	const char *uri;
+	PidginConversation *conv = g_object_get_data(G_OBJECT(imhtml), "gtkconv");
+	if (!conv) /* no playback in debug window */
+		return TRUE;
+	uri = gtk_imhtml_link_get_url(link) + AUDIOLINKSIZE;
+	purple_sound_play_file(uri, NULL);
+	return TRUE;
+}
+
+static void
+savefile_write_cb(gpointer user_data, char *file)
+{
+	char *temp_file = user_data;
+	gchar *contents;
+	gsize length;
+	GError *error;
+
+	if (!g_file_get_contents(temp_file, &contents, &length, &error)) {
+		purple_debug_error("gtkutils", "Unable to read contents of %s: %s\n",
+		                   temp_file, error->message);
+		g_error_free(error);
+		return;
+	}
+
+	if (!g_file_set_contents(file, contents, length, &error)) {
+		purple_debug_error("gtkutils", "Unable to write contents to %s: %s\n",
+		                   file, error->message);
+		g_error_free(error);
+	}
+}
+
+static gboolean
+save_file_cb(GtkWidget *item, const char *url)
+{
+	PidginConversation *conv = g_object_get_data(G_OBJECT(item), "gtkconv");
+	if (!conv)
+		return TRUE;
+	purple_request_file(conv->active_conv, _("Save File"), NULL, TRUE,
+	                    G_CALLBACK(savefile_write_cb), NULL,
+	                    conv->active_conv->account, NULL, conv->active_conv,
+	                    (void *)url);
+	return TRUE;
+}
+
+static gboolean
+audio_context_menu(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu)
+{
+	GtkWidget *img, *item;
+	const char *url;
+	PidginConversation *conv = g_object_get_data(G_OBJECT(imhtml), "gtkconv");
+	if (!conv) /* No menu in debug window */
+		return TRUE;
+
+	url = gtk_imhtml_link_get_url(link);
+
+	/* Play Sound */
+	img = gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_MENU);
+	item = gtk_image_menu_item_new_with_mnemonic(_("_Play Sound"));
+	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
+	g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(gtk_imhtml_link_activate), link);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	/* Save File */
+	img = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU);
+	item = gtk_image_menu_item_new_with_mnemonic(_("_Save File"));
+	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
+	g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(save_file_cb), (gpointer)(url+AUDIOLINKSIZE));
+	g_object_set_data(G_OBJECT(item), "gtkconv", conv);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	return TRUE;
+}
+
 /* XXX: The following two functions are for demonstration purposes only! */
 static gboolean
 open_dialog(GtkIMHtml *imhtml, GtkIMHtmlLink *link)
@@ -3814,6 +3892,7 @@
 	gtk_imhtml_class_register_protocol("mailto:", url_clicked_cb, copy_email_address);
 
 	gtk_imhtml_class_register_protocol("file://", file_clicked_cb, file_context_menu);
+	gtk_imhtml_class_register_protocol("audio://", audio_clicked_cb, audio_context_menu);
 
 	/* Example custom URL handler. */
 	gtk_imhtml_class_register_protocol("open://", open_dialog, dummy);
@@ -3840,6 +3919,8 @@
 		gnome_url_handlers = NULL;
 		return;
 	}
+
+	gtk_imhtml_class_register_protocol("audio://", NULL, NULL);
 	gtk_imhtml_class_register_protocol("file://", NULL, NULL);
 
 	gtk_imhtml_class_register_protocol("http://", NULL, NULL);