changeset 28656:a177a1cdfe4e

merged with im.pidgin.pidgin
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Thu, 24 Sep 2009 16:02:36 +0900
parents ce5593e0e1dc (current diff) c9d3bda6ef81 (diff)
children 4b3756ed1053
files libpurple/protocols/jabber/jabber.c libpurple/protocols/msn/msn.c libpurple/protocols/yahoo/libymsg.c libpurple/server.c libpurple/util.c pidgin/gtkconv.c pidgin/gtkprefs.c
diffstat 26 files changed, 391 insertions(+), 388 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Sep 14 19:43:32 2009 +0900
+++ b/ChangeLog	Thu Sep 24 16:02:36 2009 +0900
@@ -4,6 +4,10 @@
 	XMPP:
 	* Fix a crash when attempting to validate an invalid JID.
 
+	General:
+	* New 'plugins' sub-command to 'debug' command (i.e. '/debug plugins')
+	  to announce the list of loaded plugins (in both Finch and Pidgin).
+
 version 2.6.2 (09/05/2009):
 	libpurple:
 	* Fix --disable-avahi to actually disable it in configure, as opposed
--- a/ChangeLog.API	Mon Sep 14 19:43:32 2009 +0900
+++ b/ChangeLog.API	Thu Sep 24 16:02:36 2009 +0900
@@ -34,7 +34,6 @@
 		  and fwrite for saving a file locally. These allow a UI to stream a
 		  file through a socket without buffering the file on the local disk.
 		* Jabber plugin signals (see jabber-signals.dox)
-		* purple_account_get_name_for_display
 		* purple_account_remove_setting
 		* purple_buddy_destroy
 		* purple_buddy_get_protocol_data
--- a/doc/finch.1.in	Mon Sep 14 19:43:32 2009 +0900
+++ b/doc/finch.1.in	Thu Sep 24 16:02:36 2009 +0900
@@ -40,14 +40,14 @@
 The following options are provided by \fBfinch\fR using the standard GNU
 command line syntax:
 .TP
+.B \-c, \-\-config=\fIDIR\fB
+Use \fIDIR\fR as the directory for config files instead of \fI~/.purple\fR.
+.TP
 .B \-d, \-\-debug
 Print debugging messages to stderr and start with the \fBDebug\fR window. The
 messages shown in the \fBDebug\fR window are the same as the ones printed in
 stderr.
 .TP
-.B \-c, \-\-config=\fIDIR\fB
-Use \fIDIR\fR as the directory for config files instead of \fI~/.purple\fR.
-.TP
 .B \-h, \-\-help
 Print this help and exit.
 .TP
--- a/doc/pidgin.1.in	Mon Sep 14 19:43:32 2009 +0900
+++ b/doc/pidgin.1.in	Thu Sep 24 16:02:36 2009 +0900
@@ -49,6 +49,10 @@
 Print debugging messages to stdout.  These are the same debugging messages
 that are displayed in the \fBDebug Window\fR.
 .TP
+.B \-f, \-\-force-online
+Try to be online even if the network is reported (by Windows, or NetworkManager
+on Linux) to be unavailable.
+.TP
 .B \-h, \-\-help
 Print a summary of command line options and exit.
 .TP
--- a/finch/gntconv.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/finch/gntconv.c	Thu Sep 24 16:02:36 2009 +0900
@@ -29,6 +29,7 @@
 #include <internal.h>
 
 #include <cmds.h>
+#include <core.h>
 #include <idle.h>
 #include <prefs.h>
 #include <util.h>
@@ -1181,22 +1182,43 @@
                  const char *cmd, char **args, char **error, void *data)
 {
 	char *tmp, *markup;
-	PurpleCmdStatus status;
 
 	if (!g_ascii_strcasecmp(args[0], "version")) {
-		tmp = g_strdup_printf("me is using Finch v%s.", DISPLAY_VERSION);
-		markup = g_markup_escape_text(tmp, -1);
-
-		status = purple_cmd_do_command(conv, tmp, markup, error);
+		tmp = g_strdup_printf("Using Finch v%s with libpurple v%s.",
+				DISPLAY_VERSION, purple_core_get_version());
+	} else if (!g_ascii_strcasecmp(args[0], "plugins")) {
+		/* Show all the loaded plugins, including the protocol plugins and plugin loaders.
+		 * This is intentional, since third party prpls are often sources of bugs, and some
+		 * plugin loaders (e.g. mono) can also be buggy.
+		 */
+		GString *str = g_string_new("Loaded Plugins: ");
+		const GList *plugins = purple_plugins_get_loaded();
+		if (plugins) {
+			for (; plugins; plugins = plugins->next) {
+				str = g_string_append(str, purple_plugin_get_name(plugins->data));
+				if (plugins->next)
+					str = g_string_append(str, ", ");
+			}
+		} else {
+			str = g_string_append(str, "(none)");
+		}
 
-		g_free(tmp);
-		g_free(markup);
-		return status;
+		tmp = g_string_free(str, FALSE);
 	} else {
-		purple_conversation_write(conv, NULL, _("Supported debug options are:  version"),
+		purple_conversation_write(conv, NULL, _("Supported debug options are: plugins version"),
 		                        PURPLE_MESSAGE_NO_LOG|PURPLE_MESSAGE_ERROR, time(NULL));
-		return PURPLE_CMD_STATUS_OK;
+		return PURPLE_CMD_RET_OK;
 	}
+
+	markup = g_markup_escape_text(tmp, -1);
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
+		purple_conv_im_send(PURPLE_CONV_IM(conv), markup);
+	else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
+		purple_conv_chat_send(PURPLE_CONV_CHAT(conv), markup);
+
+	g_free(tmp);
+	g_free(markup);
+	return PURPLE_CMD_RET_OK;
 }
 
 /* Xerox */
@@ -1207,7 +1229,7 @@
 	FinchConv *ggconv = FINCH_GET_DATA(conv);
 	gnt_text_view_clear(GNT_TEXT_VIEW(ggconv->tv));
 	purple_conversation_clear_message_history(conv);
-	return PURPLE_CMD_STATUS_OK;
+	return PURPLE_CMD_RET_OK;
 }
 
 /* Xerox */
@@ -1247,7 +1269,7 @@
 	purple_conversation_write(conv, NULL, s->str, PURPLE_MESSAGE_NO_LOG, time(NULL));
 	g_string_free(s, TRUE);
 
-	return PURPLE_CMD_STATUS_OK;
+	return PURPLE_CMD_RET_OK;
 }
 
 static PurpleCmdRet
@@ -1255,7 +1277,7 @@
 {
 	void (*callback)(void) = data;
 	callback();
-	return PURPLE_CMD_STATUS_OK;
+	return PURPLE_CMD_RET_OK;
 }
 
 #if GLIB_CHECK_VERSION(2,6,0)
@@ -1278,26 +1300,26 @@
 	else {
 		if (error)
 			*error = g_strdup_printf(_("%s is not a valid message class. See '/help msgcolor' for valid message classes."), args[0]);
-		return PURPLE_CMD_STATUS_FAILED;
+		return PURPLE_CMD_RET_FAILED;
 	}
 
 	fg = gnt_colors_get_color(args[1]);
 	if (fg == -EINVAL) {
 		if (error)
 			*error = g_strdup_printf(_("%s is not a valid color. See '/help msgcolor' for valid colors."), args[1]);
-		return PURPLE_CMD_STATUS_FAILED;
+		return PURPLE_CMD_RET_FAILED;
 	}
 
 	bg = gnt_colors_get_color(args[2]);
 	if (bg == -EINVAL) {
 		if (error)
 			*error = g_strdup_printf(_("%s is not a valid color. See '/help msgcolor' for valid colors."), args[2]);
-		return PURPLE_CMD_STATUS_FAILED;
+		return PURPLE_CMD_RET_FAILED;
 	}
 
 	init_pair(*msgclass, fg, bg);
 
-	return PURPLE_CMD_STATUS_OK;
+	return PURPLE_CMD_RET_OK;
 }
 #endif
 
@@ -1307,7 +1329,7 @@
 	FinchConv *fc = FINCH_GET_DATA(conv);
 	FinchConvChat *ch;
 	if (!fc)
-		return PURPLE_CMD_STATUS_FAILED;
+		return PURPLE_CMD_RET_FAILED;
 
 	ch = fc->u.chat;
 	gnt_widget_set_visible(ch->userlist,
@@ -1315,7 +1337,7 @@
 	gnt_box_readjust(GNT_BOX(fc->window));
 	gnt_box_give_focus_to_child(GNT_BOX(fc->window), fc->entry);
 	purple_prefs_set_bool(PREF_USERLIST, !(GNT_WIDGET_IS_FLAG_SET(ch->userlist, GNT_WIDGET_INVISIBLE)));
-	return PURPLE_CMD_STATUS_OK;
+	return PURPLE_CMD_RET_OK;
 }
 
 void finch_conversation_init()
--- a/libpurple/account.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/account.c	Thu Sep 24 16:02:36 2009 +0900
@@ -2019,42 +2019,6 @@
 	return account->gc;
 }
 
-const gchar *
-purple_account_get_name_for_display(const PurpleAccount *account)
-{
-	PurpleBuddy *self = NULL;
-	PurpleConnection *gc = NULL;
-	const gchar *name = NULL, *username = NULL, *displayname = NULL;
-
-	name = purple_account_get_alias(account);
-
-	if (name) {
-		return name;
-	}
-
-	username = purple_account_get_username(account);
-	self = purple_find_buddy((PurpleAccount *)account, username);
-
-	if (self) {
-		const gchar *calias= purple_buddy_get_contact_alias(self);
-
-		/* We don't want to return the buddy name if the buddy/contact
-		 * doesn't have an alias set. */
-		if (!purple_strequal(username, calias)) {
-			return calias;
-		}
-	}
-
-	gc = purple_account_get_connection(account);
-	displayname = purple_connection_get_display_name(gc);
-
-	if (displayname) {
-		return displayname;
-	}
-
-	return username;
-}
-
 gboolean
 purple_account_get_remember_password(const PurpleAccount *account)
 {
--- a/libpurple/account.h	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/account.h	Thu Sep 24 16:02:36 2009 +0900
@@ -630,20 +630,6 @@
 PurpleConnection *purple_account_get_connection(const PurpleAccount *account);
 
 /**
- * Returns a name for this account appropriate for display to the user. In
- * order of preference: the account's alias; the contact or buddy alias (if
- * the account exists on its own buddy list); the connection's display name;
- * the account's username.
- *
- * @param account The account.
- *
- * @return The name to display.
- *
- * @since 2.7.0
- */
-const gchar *purple_account_get_name_for_display(const PurpleAccount *account);
-
-/**
  * Returns whether or not this account should save its password.
  *
  * @param account The account.
--- a/libpurple/connection.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/connection.c	Thu Sep 24 16:02:36 2009 +0900
@@ -52,10 +52,7 @@
 send_keepalive(gpointer data)
 {
 	PurpleConnection *gc = data;
-	PurplePluginProtocolInfo *prpl_info = NULL;
-
-	if (gc == NULL)
-		return TRUE;
+	PurplePluginProtocolInfo *prpl_info;
 
 	/* Only send keep-alives if we haven't heard from the
 	 * server in a while.
@@ -63,12 +60,8 @@
 	if ((time(NULL) - gc->last_received) < KEEPALIVE_INTERVAL)
 		return TRUE;
 
-	if (gc->prpl == NULL)
-		return TRUE;
-
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
-
-	if (prpl_info && prpl_info->keepalive)
+	if (prpl_info->keepalive)
 		prpl_info->keepalive(gc);
 
 	return TRUE;
--- a/libpurple/dbus-server.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/dbus-server.c	Thu Sep 24 16:02:36 2009 +0900
@@ -126,8 +126,10 @@
 	gint id = GPOINTER_TO_INT(g_hash_table_lookup(map_node_id, node));
 	if ((id == 0) && (node != NULL))
 	{
-		purple_debug_warning("dbus",
-				"Need to register an object with the dbus subsystem. (If you are not a developer, please ignore this message.)\n");
+		if (purple_debug_is_verbose())
+			purple_debug_warning("dbus",
+				"Need to register an object with the dbus subsystem."
+				" (If you are not a developer, please ignore this message.)\n");
 		return 0;
 	}
 	return id;
@@ -795,7 +797,11 @@
 	dbus_message_iter_init_append(signal, &iter);
 
 	if (purple_dbus_message_append_purple_values(&iter, num_values, values, vargs))
-		purple_debug_warning("dbus", "The signal \"%s\" caused some dbus error. (If you are not a developer, please ignore this message.)\n", name);
+		if (purple_debug_is_verbose())
+			purple_debug_warning("dbus",
+				"The signal \"%s\" caused some dbus error."
+				" (If you are not a developer, please ignore this message.)\n",
+				name);
 
 	dbus_connection_send(purple_dbus_connection, signal, NULL);
 
--- a/libpurple/media.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/media.c	Thu Sep 24 16:02:36 2009 +0900
@@ -2153,7 +2153,7 @@
 			GstElement *lastElement = NULL;
 			while (!GST_IS_PIPELINE(element)) {
 				if (element == media->priv->confbin) {
-					purple_media_error(media, _("Conference error."));
+					purple_media_error(media, _("Conference error"));
 					purple_media_end(media, NULL, NULL);
 					break;
 				}
@@ -2167,9 +2167,9 @@
 
 					if (session->src == lastElement) {
 						if (session->type & PURPLE_MEDIA_AUDIO)
-							purple_media_error(media, _("Error with your microphone."));
+							purple_media_error(media, _("Error with your microphone"));
 						else
-							purple_media_error(media, _("Error with your webcam."));
+							purple_media_error(media, _("Error with your webcam"));
 						purple_media_end(media, NULL, NULL);
 						break;
 					}
--- a/libpurple/protocols/jabber/jabber.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/protocols/jabber/jabber.c	Thu Sep 24 16:02:36 2009 +0900
@@ -3468,15 +3468,16 @@
 
 	/* Jingle features! */
 	jabber_add_feature(JINGLE, 0);
-	jabber_add_feature(JINGLE_TRANSPORT_RAWUDP, 0);
 
 #ifdef USE_VV
 	jabber_add_feature("http://www.google.com/xmpp/protocol/session", jabber_audio_enabled);
 	jabber_add_feature("http://www.google.com/xmpp/protocol/voice/v1", jabber_audio_enabled);
 	jabber_add_feature("http://www.google.com/xmpp/protocol/video/v1", jabber_video_enabled);
 	jabber_add_feature("http://www.google.com/xmpp/protocol/camera/v1", jabber_video_enabled);
+	jabber_add_feature(JINGLE_APP_RTP, 0);
 	jabber_add_feature(JINGLE_APP_RTP_SUPPORT_AUDIO, jabber_audio_enabled);
 	jabber_add_feature(JINGLE_APP_RTP_SUPPORT_VIDEO, jabber_video_enabled);
+	jabber_add_feature(JINGLE_TRANSPORT_RAWUDP, 0);
 	jabber_add_feature(JINGLE_TRANSPORT_ICEUDP, 0);
 #endif
 
--- a/libpurple/protocols/jabber/jingle/jingle.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/protocols/jabber/jingle/jingle.c	Thu Sep 24 16:02:36 2009 +0900
@@ -115,7 +115,7 @@
 		const gchar *creator = xmlnode_get_attrib(content, "creator");
 		JingleContent *local_content = jingle_session_find_content(session, name, creator);
 
-		if (content != NULL) {
+		if (local_content != NULL) {
 			const gchar *senders = xmlnode_get_attrib(content, "senders");
 			gchar *local_senders = jingle_content_get_senders(local_content);
 			if (strcmp(senders, local_senders))
--- a/libpurple/protocols/msn/msn.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/protocols/msn/msn.c	Thu Sep 24 16:02:36 2009 +0900
@@ -1449,7 +1449,7 @@
 	{
 		PurpleBuddy * buddy = purple_find_buddy(session->account, who);
 		gchar *buf;
-		buf = g_strdup_printf(_("Unable to add the buddy %s because the username is invalid.  Usernames must be a valid email address."), who);
+		buf = g_strdup_printf(_("Unable to add the buddy %s because the username is invalid.  Usernames must be valid email addresses."), who);
 		if (!purple_conv_present_error(who, session->account, buf))
 			purple_notify_error(purple_account_get_connection(session->account), NULL, _("Unable to Add"), buf);
 		g_free(buf);
@@ -1539,7 +1539,7 @@
 
 	if (!purple_email_is_valid(bname)) {
 		gchar *buf;
-		buf = g_strdup_printf(_("Unable to add the buddy %s because the username is invalid.  Usernames must be a valid email address."), bname);
+		buf = g_strdup_printf(_("Unable to add the buddy %s because the username is invalid.  Usernames must be valid email addresses."), bname);
 		if (!purple_conv_present_error(bname, purple_connection_get_account(gc), buf))
 			purple_notify_error(gc, NULL, _("Unable to Add"), buf);
 		g_free(buf);
--- a/libpurple/protocols/msn/oim.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/protocols/msn/oim.c	Thu Sep 24 16:02:36 2009 +0900
@@ -172,8 +172,18 @@
 
 	if (faultcode) {
 		gchar *faultcode_str = xmlnode_get_data(faultcode);
+		gboolean need_token_update = FALSE;
 
-		if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) {
+		if (faultcode_str) {
+			if (g_str_equal(faultcode_str, "q0:BadContextToken") ||
+				g_str_equal(faultcode_str, "AuthenticationFailed"))
+				need_token_update = TRUE;
+			else if (g_str_equal(faultcode_str, "q0:AuthenticationFailed") &&
+				xmlnode_get_child(fault, "detail/RequiredAuthPolicy") != NULL)
+				need_token_update = TRUE;
+		}
+
+		if (need_token_update) {
 			purple_debug_warning("msn", "OIM Request Error, Updating token now.\n");
 			msn_nexus_update_token(data->oim->session->nexus,
 				data->send ? MSN_AUTH_LIVE_SECURE : MSN_AUTH_MESSENGER_WEB,
@@ -181,16 +191,8 @@
 			g_free(faultcode_str);
 			return;
 
-		} else if (faultcode_str && g_str_equal(faultcode_str, "q0:AuthenticationFailed")) {
-			if (xmlnode_get_child(fault, "detail/RequiredAuthPolicy") != NULL) {
-				purple_debug_warning("msn", "OIM Request Error, Updating token now.\n");
-				msn_nexus_update_token(data->oim->session->nexus,
-					data->send ? MSN_AUTH_LIVE_SECURE : MSN_AUTH_MESSENGER_WEB,
-					(GSourceFunc)msn_oim_request_helper, data);
-				g_free(faultcode_str);
-				return;
-			}
 		}
+
 		g_free(faultcode_str);
 	}
 
--- a/libpurple/protocols/oscar/family_feedbag.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/protocols/oscar/family_feedbag.c	Thu Sep 24 16:02:36 2009 +0900
@@ -135,13 +135,18 @@
 					}
 			} while (exists);
 		}
-	} else if (type == AIM_SSI_TYPE_ICONINFO) {
+	} else if (new->gid == 0x0000) {
+		/*
+		 * This is weird, but apparently items in the root group can't
+		 * have a buddy ID equal to any group ID.  You'll get error
+		 * 0x0003 when trying to add, which is "item already exists"
+		 */
 		if (new->bid == 0xFFFF) {
 			do {
 				new->bid += 0x0001;
 				exists = FALSE;
 				for (cur = *list; cur != NULL; cur = cur->next)
-					if ((cur->bid >= new->bid) || (cur->gid >= new->bid)) {
+					if (cur->bid == new->bid || cur->gid == new->bid) {
 						exists = TRUE;
 						break;
 					}
@@ -153,7 +158,7 @@
 				new->bid += 0x0001;
 				exists = FALSE;
 				for (cur = *list; cur != NULL; cur = cur->next)
-					if ((cur->bid == new->bid) && (cur->gid == new->gid)) {
+					if (cur->bid == new->bid && cur->gid == new->gid) {
 						exists = TRUE;
 						break;
 					}
@@ -687,10 +692,6 @@
 		cur = cur->next;
 	}
 
-	/* Check if the master group is empty */
-	if ((cur = aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000)) && (!cur->data))
-		aim_ssi_itemlist_del(&od->ssi.local, cur);
-
 	/* If we've made any changes then sync our list with the server's */
 	return aim_ssi_sync(od);
 }
--- a/libpurple/protocols/oscar/oscarcommon.h	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/protocols/oscar/oscarcommon.h	Thu Sep 24 16:02:36 2009 +0900
@@ -44,8 +44,8 @@
 #define OSCAR_DEFAULT_WEB_AWARE FALSE
 #define OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY FALSE
 #define OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS TRUE
-#define OSCAR_DEFAULT_USE_SSL FALSE
-#define OSCAR_DEFAULT_USE_CLIENTLOGIN FALSE
+#define OSCAR_DEFAULT_USE_SSL TRUE
+#define OSCAR_DEFAULT_USE_CLIENTLOGIN TRUE
 
 #ifdef _WIN32
 const char *oscar_get_locale_charset(void);
--- a/libpurple/protocols/yahoo/libymsg.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/protocols/yahoo/libymsg.c	Thu Sep 24 16:02:36 2009 +0900
@@ -4155,14 +4155,13 @@
 	struct yahoo_sms_carrier_cb_data *sms_cb_data = user_data;
 	PurpleConnection *gc = sms_cb_data->gc;
 	YahooData *yd = gc->proto_data;
-	char *mobile_no = NULL;
 	char *status = NULL;
 	char *carrier = NULL;
 	PurpleAccount *account = purple_connection_get_account(gc);
 	PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms_cb_data->who, account);
 
 	if (error_message != NULL) {
-		purple_conversation_write(conv, NULL, "Cant send SMS, Unable to obtain mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL));
+		purple_conversation_write(conv, NULL, _("Can't send SMS. Unable to obtain mobile carrier."), PURPLE_MESSAGE_SYSTEM, time(NULL));
 
 		g_free(sms_cb_data->who);
 		g_free(sms_cb_data->what);
@@ -4172,7 +4171,7 @@
 	else if (len > 0 && webdata && *webdata) {
 		xmlnode *validate_data_root = xmlnode_from_str(webdata, -1);
 		xmlnode *validate_data_child = xmlnode_get_child(validate_data_root, "mobile_no");
-		mobile_no = (char *)xmlnode_get_attrib(validate_data_child, "msisdn");
+		const char *mobile_no = xmlnode_get_attrib(validate_data_child, "msisdn");
 
 		validate_data_root = xmlnode_copy(validate_data_child);
 		validate_data_child = xmlnode_get_child(validate_data_root, "status");
@@ -4189,7 +4188,7 @@
 		}
 		else	{
 			g_hash_table_insert(yd->sms_carrier, g_strdup_printf("+%s", mobile_no), g_strdup("Unknown"));
-			purple_conversation_write(conv, NULL, "Cant send SMS, Unknown mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL));
+			purple_conversation_write(conv, NULL, _("Can't send SMS. Unknown mobile carrier."), PURPLE_MESSAGE_SYSTEM, time(NULL));
 		}
 
 		xmlnode_free(validate_data_child);
@@ -4197,7 +4196,6 @@
 		g_free(sms_cb_data->who);
 		g_free(sms_cb_data->what);
 		g_free(sms_cb_data);
-		g_free(mobile_no);
 		g_free(status);
 		g_free(carrier);
 	}
@@ -4254,7 +4252,7 @@
 	if (!url_data) {
 		PurpleAccount *account = purple_connection_get_account(gc);
 		PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms_cb_data->who, account);
-		purple_conversation_write(conv, NULL, "Cant send SMS, Unable to obtain mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL));
+		purple_conversation_write(conv, NULL, _("Can't send SMS. Unable to obtain mobile carrier."), PURPLE_MESSAGE_SYSTEM, time(NULL));
 		g_free(sms_cb_data->who);
 		g_free(sms_cb_data->what);
 		g_free(sms_cb_data);
@@ -4309,7 +4307,7 @@
 			sms_cb_data->who = g_strdup(who);
 			sms_cb_data->what = g_strdup(what);
 
-			purple_conversation_write(conv, NULL, "Getting mobile carrier to send the sms", PURPLE_MESSAGE_SYSTEM, time(NULL));
+			purple_conversation_write(conv, NULL, _("Getting mobile carrier to send the SMS."), PURPLE_MESSAGE_SYSTEM, time(NULL));
 
 			yahoo_get_sms_carrier(gc, sms_cb_data);
 
@@ -4318,7 +4316,7 @@
 			return ret;
 		}
 		else if( strcmp(carrier,"Unknown") == 0 ) {
-			purple_conversation_write(conv, NULL, "Cant send SMS, Unknown mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL));
+			purple_conversation_write(conv, NULL, _("Can't send SMS. Unknown mobile carrier."), PURPLE_MESSAGE_SYSTEM, time(NULL));
 
 			g_free(msg);
 			g_free(msg2);
--- a/libpurple/server.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/server.c	Thu Sep 24 16:02:36 2009 +0900
@@ -44,17 +44,16 @@
 unsigned int
 serv_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state)
 {
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
-	if(gc)
+	if (gc) {
 		prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(prpl_info && prpl_info->send_typing)
-		return prpl_info->send_typing(gc, name, state);
+		if (prpl_info->send_typing)
+			return prpl_info->send_typing(gc, name, state);
+	}
 
 	return 0;
 }
@@ -142,7 +141,7 @@
 
 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account);
 
-	if(prpl_info && prpl_info->send_im)
+	if (prpl_info->send_im)
 		val = prpl_info->send_im(gc, name, message, flags);
 
 	/*
@@ -167,43 +166,40 @@
 
 void serv_get_info(PurpleConnection *gc, const char *name)
 {
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
-	if(gc)
+	if (gc) {
 		prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(gc && prpl_info && prpl_info->get_info)
-		prpl_info->get_info(gc, name);
+		if (prpl_info->get_info)
+			prpl_info->get_info(gc, name);
+	}
 }
 
 void serv_set_info(PurpleConnection *gc, const char *info)
 {
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
-	PurpleAccount *account = NULL;;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
+	PurpleAccount *account;
 
-	if(gc)
+	if (gc) {
 		prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(prpl_info && prpl_info->set_info) {
-
-		account = purple_connection_get_account(gc);
+		if (prpl_info->set_info) {
+			account = purple_connection_get_account(gc);
 
-		if(purple_signal_emit_return_1(purple_accounts_get_handle(),
-									  "account-setting-info", account, info))
-			return;
+			if (purple_signal_emit_return_1(purple_accounts_get_handle(),
+					"account-setting-info", account, info))
+				return;
 
-		prpl_info->set_info(gc, info);
+			prpl_info->set_info(gc, info);
 
-		purple_signal_emit(purple_accounts_get_handle(),
-						 "account-set-info", account, info);
+			purple_signal_emit(purple_accounts_get_handle(),
+					"account-set-info", account, info);
+		}
 	}
 }
 
@@ -212,25 +208,27 @@
  */
 void serv_alias_buddy(PurpleBuddy *b)
 {
-	PurpleAccount *account = NULL;
-	PurpleConnection *gc = NULL;
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurpleAccount *account;
+	PurpleConnection *gc;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
-	if(b)
+	if (b) {
 		account = purple_buddy_get_account(b);
 
-	if(account)
-		gc = purple_account_get_connection(account);
+		if (account) {
+			gc = purple_account_get_connection(account);
 
-	if(gc)
-		prpl = purple_connection_get_prpl(gc);
+			if (gc) {
+				prpl = purple_connection_get_prpl(gc);
+				prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(prpl)
-		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
-	if(b && prpl_info && prpl_info->alias_buddy) {
-		prpl_info->alias_buddy(gc, purple_buddy_get_name(b), purple_buddy_get_local_buddy_alias(b));
+				if (prpl_info->alias_buddy)
+					prpl_info->alias_buddy(gc,
+							purple_buddy_get_name(b),
+							purple_buddy_get_local_buddy_alias(b));
+			}
+		}
 	}
 }
 
@@ -360,10 +358,10 @@
  */
 void serv_move_buddy(PurpleBuddy *b, PurpleGroup *og, PurpleGroup *ng)
 {
-	PurpleAccount *account = NULL;
-	PurpleConnection *gc = NULL;
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurpleAccount *account;
+	PurpleConnection *gc;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
 	g_return_if_fail(b != NULL);
 	g_return_if_fail(og != NULL);
@@ -372,131 +370,120 @@
 	account = purple_buddy_get_account(b);
 	gc = purple_account_get_connection(account);
 
-	if(gc)
+	if (gc) {
 		prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(gc && og && ng) {
-		if (prpl_info && prpl_info->group_buddy) {
+		if (prpl_info->group_buddy)
 			prpl_info->group_buddy(gc, purple_buddy_get_name(b),
-			                       purple_group_get_name(og),
-								   purple_group_get_name(ng));
-		}
+					purple_group_get_name(og),
+					purple_group_get_name(ng));
 	}
 }
 
 void serv_add_permit(PurpleConnection *gc, const char *name)
 {
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
-	if(gc)
+	if (gc) {
 		prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(prpl_info && prpl_info->add_permit)
-		prpl_info->add_permit(gc, name);
+		if (prpl_info->add_permit)
+			prpl_info->add_permit(gc, name);
+	}
 }
 
 void serv_add_deny(PurpleConnection *gc, const char *name)
 {
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
-	if(gc)
+	if (gc) {
 		prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(prpl_info && prpl_info->add_deny)
-		prpl_info->add_deny(gc, name);
+		if (prpl_info->add_deny)
+			prpl_info->add_deny(gc, name);
+	}
 }
 
 void serv_rem_permit(PurpleConnection *gc, const char *name)
 {
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
-	if(gc)
+	if (gc) {
 		prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(prpl_info && prpl_info->rem_permit)
-		prpl_info->rem_permit(gc, name);
+		if (prpl_info->rem_permit)
+			prpl_info->rem_permit(gc, name);
+	}
 }
 
 void serv_rem_deny(PurpleConnection *gc, const char *name)
 {
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
-	if(gc)
+	if (gc) {
 		prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(prpl_info && prpl_info->rem_deny)
-		prpl_info->rem_deny(gc, name);
+		if (prpl_info->rem_deny)
+			prpl_info->rem_deny(gc, name);
+	}
 }
 
 void serv_set_permit_deny(PurpleConnection *gc)
 {
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
-	if(gc)
+	if (gc) {
 		prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	/*
-	 * this is called when either you import a buddy list, and make lots
-	 * of changes that way, or when the user toggles the permit/deny mode
-	 * in the prefs. In either case you should probably be resetting and
-	 * resending the permit/deny info when you get this.
-	 */
-	if(prpl_info && prpl_info->set_permit_deny)
-		prpl_info->set_permit_deny(gc);
+		/*
+		 * this is called when either you import a buddy list, and make lots
+		 * of changes that way, or when the user toggles the permit/deny mode
+		 * in the prefs. In either case you should probably be resetting and
+		 * resending the permit/deny info when you get this.
+		 */
+		if (prpl_info->set_permit_deny)
+			prpl_info->set_permit_deny(gc);
+	}
 }
 
 void serv_join_chat(PurpleConnection *gc, GHashTable *data)
 {
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
-	if(gc)
+	if (gc) {
 		prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(prpl_info && prpl_info->join_chat)
-		prpl_info->join_chat(gc, data);
+		if (prpl_info->join_chat)
+			prpl_info->join_chat(gc, data);
+	}
 }
 
 
 void serv_reject_chat(PurpleConnection *gc, GHashTable *data)
 {
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
-	if(gc)
+	if (gc) {
 		prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(prpl_info && prpl_info->reject_chat)
-		prpl_info->reject_chat(gc, data);
+		if (prpl_info->reject_chat)
+			prpl_info->reject_chat(gc, data);
+	}
 }
 
 void serv_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
@@ -534,51 +521,44 @@
  * Then again, something might want to use this, from outside prpl-land
  * to leave a chat without destroying the conversation.
  */
-
 void serv_chat_leave(PurpleConnection *gc, int id)
 {
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
 	prpl = purple_connection_get_prpl(gc);
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(prpl)
-		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
-	if(prpl_info && prpl_info->chat_leave)
+	if (prpl_info->chat_leave)
 		prpl_info->chat_leave(gc, id);
 }
 
 void serv_chat_whisper(PurpleConnection *gc, int id, const char *who, const char *message)
 {
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
-	if(gc)
+	if (gc) {
 		prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(prpl_info && prpl_info->chat_whisper)
-		prpl_info->chat_whisper(gc, id, who, message);
+		if (prpl_info->chat_whisper)
+			prpl_info->chat_whisper(gc, id, who, message);
+	}
 }
 
 int serv_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
 {
-	int val = -EINVAL;
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
 	prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
-		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if(prpl_info && prpl_info->chat_send)
-		val = prpl_info->chat_send(gc, id, message, flags);
+	if (prpl_info->chat_send)
+		return prpl_info->chat_send(gc, id, message, flags);
 
-	return val;
+	return -EINVAL;
 }
 
 /*
@@ -991,18 +971,16 @@
 
 void serv_send_file(PurpleConnection *gc, const char *who, const char *file)
 {
-	PurplePlugin *prpl = NULL;
-	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurplePlugin *prpl;
+	PurplePluginProtocolInfo *prpl_info;
 
-	if(gc)
+	if (gc) {
 		prpl = purple_connection_get_prpl(gc);
-
-	if(prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-	if (prpl_info && prpl_info->send_file) {
-		if (!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, who)) {
+		if (prpl_info->send_file &&
+				(!prpl_info->can_receive_file
+						|| prpl_info->can_receive_file(gc, who)))
 			prpl_info->send_file(gc, who, file);
-		}
 	}
 }
--- a/libpurple/tests/test_jabber_jutil.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/tests/test_jabber_jutil.c	Thu Sep 24 16:02:36 2009 +0900
@@ -142,13 +142,20 @@
 
 	/* Cyrillic capital EF (U+0424) maps to lowercase EF (U+0444) */
 	assert_jid_parts("ф", "darkrain42.org", "Ф@darkrain42.org");
+
+#ifdef USE_IDN
 	/*
 	 * These character (U+A664 and U+A665) are not mapped to anything in
 	 * RFC3454 B.2. This first test *fails* when not using IDN because glib's
-	 * case-folding/utf8_strdown improperly lowercases the character.
+	 * case-folding/utf8_strdown improperly (for XMPP) lowercases the character.
+	 *
+	 * This is known, but not (very?) likely to actually cause a problem, so
+	 * this test is commented out when using glib's functions.
 	 */
 	assert_jid_parts("Ꙥ", "darkrain42.org", "Ꙥ@darkrain42.org");
 	assert_jid_parts("ꙥ", "darkrain42.org", "ꙥ@darkrain42.org");
+#endif
+
 	/* U+04E9 to U+04E9 */
 	assert_jid_parts("paul", "өarkrain42.org", "paul@Өarkrain42.org");
 }
--- a/libpurple/util.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/libpurple/util.c	Thu Sep 24 16:02:36 2009 +0900
@@ -3183,7 +3183,7 @@
 		{
 			PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 
-			if(prpl_info && prpl_info->normalize)
+			if (prpl_info->normalize)
 				ret = prpl_info->normalize(account, str);
 		}
 	}
--- a/pidgin/gtkconv.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/pidgin/gtkconv.c	Thu Sep 24 16:02:36 2009 +0900
@@ -369,23 +369,43 @@
                  const char *cmd, char **args, char **error, void *data)
 {
 	char *tmp, *markup;
-	PurpleCmdStatus status;
 
 	if (!g_ascii_strcasecmp(args[0], "version")) {
-		tmp = g_strdup_printf("me is using Pidgin v%s with libpurple v%s.",
+		tmp = g_strdup_printf("Using Pidgin v%s with libpurple v%s.",
 				DISPLAY_VERSION, purple_core_get_version());
-		markup = g_markup_escape_text(tmp, -1);
-
-		status = purple_cmd_do_command(conv, tmp, markup, error);
-
-		g_free(tmp);
-		g_free(markup);
-		return status;
+	} else if (!g_ascii_strcasecmp(args[0], "plugins")) {
+		/* Show all the loaded plugins, including the protocol plugins and plugin loaders.
+		 * This is intentional, since third party prpls are often sources of bugs, and some
+		 * plugin loaders (e.g. mono) can also be buggy.
+		 */
+		GString *str = g_string_new("Loaded Plugins: ");
+		const GList *plugins = purple_plugins_get_loaded();
+		if (plugins) {
+			for (; plugins; plugins = plugins->next) {
+				str = g_string_append(str, purple_plugin_get_name(plugins->data));
+				if (plugins->next)
+					str = g_string_append(str, ", ");
+			}
+		} else {
+			str = g_string_append(str, "(none)");
+		}
+
+		tmp = g_string_free(str, FALSE);
 	} else {
-		purple_conversation_write(conv, NULL, _("Supported debug options are:  version"),
+		purple_conversation_write(conv, NULL, _("Supported debug options are: plugins version"),
 		                        PURPLE_MESSAGE_NO_LOG|PURPLE_MESSAGE_ERROR, time(NULL));
-		return PURPLE_CMD_STATUS_OK;
-	}
+		return PURPLE_CMD_RET_OK;
+	}
+
+	markup = g_markup_escape_text(tmp, -1);
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
+		purple_conv_im_send(PURPLE_CONV_IM(conv), markup);
+	else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
+		purple_conv_chat_send(PURPLE_CONV_CHAT(conv), markup);
+
+	g_free(tmp);
+	g_free(markup);
+	return PURPLE_CMD_RET_OK;
 }
 
 static void clear_conversation_scrollback(PurpleConversation *conv)
@@ -405,7 +425,7 @@
                  const char *cmd, char **args, char **error, void *data)
 {
 	clear_conversation_scrollback(conv);
-	return PURPLE_CMD_STATUS_OK;
+	return PURPLE_CMD_RET_OK;
 }
 
 static PurpleCmdRet
@@ -413,7 +433,7 @@
                  const char *cmd, char **args, char **error, void *data)
 {
 	purple_conversation_foreach(clear_conversation_scrollback);
-	return PURPLE_CMD_STATUS_OK;
+	return PURPLE_CMD_RET_OK;
 }
 
 static PurpleCmdRet
@@ -452,7 +472,7 @@
 	purple_conversation_write(conv, NULL, s->str, PURPLE_MESSAGE_NO_LOG, time(NULL));
 	g_string_free(s, TRUE);
 
-	return PURPLE_CMD_STATUS_OK;
+	return PURPLE_CMD_RET_OK;
 }
 
 static void
@@ -3936,7 +3956,7 @@
 	gtk_size_group_add_widget(sg, image);
 
 	/* Make our menu item */
-	text = g_strdup_printf("%s (%s)", name, purple_account_get_name_for_display(account));
+	text = g_strdup_printf("%s (%s)", name, purple_account_get_username(account));
 	menuitem = gtk_radio_menu_item_new_with_label(*group, text);
 	g_free(text);
 	*group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
@@ -7405,7 +7425,8 @@
 			pidgin_setup_gtkspell(GTK_TEXT_VIEW(gtkconv->entry));
 		else {
 			spell = gtkspell_get_from_text_view(GTK_TEXT_VIEW(gtkconv->entry));
-			gtkspell_detach(spell);
+			if (spell)
+				gtkspell_detach(spell);
 		}
 	}
 #endif
--- a/pidgin/gtkprefs.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/pidgin/gtkprefs.c	Thu Sep 24 16:02:36 2009 +0900
@@ -459,13 +459,24 @@
 	return row_ref;
 }
 
+static gchar *
+get_theme_markup(const char *name, gboolean custom, const char *author,
+				 const char *description)
+{
+
+	return g_strdup_printf("<b>%s</b>%s%s%s%s\n<span foreground='dim grey'>%s</span>",
+						   name, custom ? " " : "", custom ? _("(Custom)") : "",
+						   author != NULL ? " - " : "", author != NULL ? author : "",
+						   description != NULL ? description : "");
+}
+
 /* Rebuild the markup for the sound theme selection for "(Custom)" themes */
 static void
 pref_sound_generate_markup(void)
 {
 	gboolean print_custom, customized;
-	const gchar *name, *author, *description, *current_theme;
-	gchar *markup;
+	const gchar *author, *description, *current_theme;
+	gchar *name, *markup;
 	PurpleSoundTheme *theme;
 	GtkTreeIter iter;
 
@@ -476,23 +487,24 @@
 		do {
 			gtk_tree_model_get(GTK_TREE_MODEL(prefs_sound_themes), &iter, 2, &name, -1);
 
-			print_custom = customized && g_str_equal(current_theme, name);
-
-			if (!name || *name == '\0')
-				markup = g_strdup_printf("<b>(Default)</b>%s%s - None\n<span foreground='dim grey'>The default Pidgin sound theme</span>",
-							 print_custom ? " " : "", print_custom ? "(Custom)" : "");
-			else {
+			print_custom = customized && name && g_str_equal(current_theme, name);
+
+			if (!name || *name == '\0') {
+				g_free(name);
+				name = g_strdup(_("(Default)"));
+				author = _("None");
+				description = _("The default Pidgin sound theme");
+			} else {
 				theme = PURPLE_SOUND_THEME(purple_theme_manager_find_theme(name, "sound"));
 				author = purple_theme_get_author(PURPLE_THEME(theme));
 				description = purple_theme_get_description(PURPLE_THEME(theme));
-
-				markup = g_strdup_printf("<b>%s</b>%s%s%s%s\n<span foreground='dim grey'>%s</span>",
-							 name, print_custom ? " " : "", print_custom ? "(Custom)" : "",
-							 author != NULL ? " - " : "", author != NULL ? author : "", description != NULL ? description : "");
 			}
 
+			markup = get_theme_markup(name, print_custom, author, description);
+
 			gtk_list_store_set(prefs_sound_themes, &iter, 1, markup, -1);
 
+			g_free(name);
 			g_free(markup);
 
 		} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(prefs_sound_themes), &iter));
@@ -514,7 +526,8 @@
 		if (image_full != NULL){
 			pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
 			g_free(image_full);
-		} else pixbuf = NULL;
+		} else
+			pixbuf = NULL;
 
 		gtk_list_store_append(prefs_sound_themes, &iter);
 		gtk_list_store_set(prefs_sound_themes, &iter, 0, pixbuf, 2, purple_theme_get_name(theme), -1);
@@ -527,20 +540,21 @@
 
 		if (PIDGIN_IS_BLIST_THEME(theme))
 			store = prefs_blist_themes;
-		else store = prefs_status_icon_themes;
+		else
+			store = prefs_status_icon_themes;
 
 		image_full = purple_theme_get_image_full(theme);
 		if (image_full != NULL){
 			pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
 			g_free(image_full);
-		} else pixbuf = NULL;
+		} else
+			pixbuf = NULL;
 
 		name = purple_theme_get_name(theme);
 		author = purple_theme_get_author(theme);
 		description = purple_theme_get_description(theme);
 
-		markup = g_strdup_printf("<b>%s</b>%s%s\n<span foreground='dim grey'>%s</span>", name, author != NULL ? " - " : "",
-					 author != NULL ? author : "", description != NULL ? description : "");
+		markup = get_theme_markup(name, FALSE, author, description);
 
 		gtk_list_store_append(store, &iter);
 		gtk_list_store_set(store, &iter, 0, pixbuf, 1, markup, 2, name, -1);
@@ -579,16 +593,16 @@
 prefs_themes_refresh(void)
 {
 	GdkPixbuf *pixbuf = NULL;
-	gchar *filename;
+	gchar *tmp;
 	GtkTreeIter iter;
 
 	prefs_sound_themes_loading = TRUE;
 	/* refresh the list of themes in the manager */
 	purple_theme_manager_refresh();
 
-	filename = g_build_filename(DATADIR, "icons", "hicolor", "32x32", "apps", "pidgin.png", NULL);
-	pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
-	g_free(filename);
+	tmp = g_build_filename(DATADIR, "icons", "hicolor", "32x32", "apps", "pidgin.png", NULL);
+	pixbuf = gdk_pixbuf_new_from_file_at_scale(tmp, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
+	g_free(tmp);
 
 	/* sound themes */
 	gtk_list_store_clear(prefs_sound_themes);
@@ -598,16 +612,18 @@
 	/* blist themes */
 	gtk_list_store_clear(prefs_blist_themes);
 	gtk_list_store_append(prefs_blist_themes, &iter);
-	gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1,
-	                   "<b>(Default)</b> - None\n<span color='dim grey'>"
-	                   "The default Pidgin buddy list theme</span>", 2, "", -1);
+	tmp = get_theme_markup(_("(Default)"), FALSE, _("None"),
+		_("The default Pidgin buddy list theme"));
+	gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1, tmp, 2, "", -1);
+	g_free(tmp);
 
 	/* status icon themes */
 	gtk_list_store_clear(prefs_status_icon_themes);
 	gtk_list_store_append(prefs_status_icon_themes, &iter);
-	gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1,
-	                   "<b>(Default)</b> - None\n<span color='dim grey'>"
-	                   "The default Pidgin status icon theme</span>", 2, "", -1);
+	tmp = get_theme_markup(_("(Default)"), FALSE, _("None"),
+		_("The default Pidgin status icon theme"));
+	gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1, tmp, 2, "", -1);
+	g_free(tmp);
 	g_object_unref(G_OBJECT(pixbuf));
 
 	purple_theme_manager_for_each_theme(prefs_themes_sort);
@@ -676,6 +692,17 @@
 	return TRUE;
 }
 
+static void
+free_theme_info(struct theme_info *info)
+{
+	if (info != NULL) {
+		g_free(info->type);
+		g_free(info->extension);
+		g_free(info->original_name);
+		g_free(info);
+	}
+}
+
 /* installs a theme, info is freed by function */
 static void
 theme_install_theme(char *path, struct theme_info *info)
@@ -683,7 +710,8 @@
 #ifndef _WIN32
 	gchar *command;
 #endif
-	gchar *destdir, *tail, *type, *original_name;
+	gchar *destdir;
+	const char *tail;
 	GtkTreeRowReference *theme_rowref;
 	gboolean is_smiley_theme, is_archive;
 	PurpleTheme *theme = NULL;
@@ -691,30 +719,23 @@
 	if (info == NULL)
 		return;
 
-	original_name = info->original_name;
-	type = info->type;
-
 	/* check the extension */
-	tail = info->extension ? info->extension : g_strdup(strrchr(path, '.'));
+	tail = info->extension ? info->extension : strrchr(path, '.');
 
 	if (!tail) {
-		g_free(type);
-		g_free(original_name);
-		g_free(info);
+		free_theme_info(info);
 		return;
-	} else
-		g_free(info);
+	}
 
 	is_archive = !g_ascii_strcasecmp(tail, ".gz") || !g_ascii_strcasecmp(tail, ".tgz");
 
-	g_free(tail);
-
 	/* Just to be safe */
 	g_strchomp(path);
 
-	if ((is_smiley_theme = g_str_equal(type, "smiley")))
+	if ((is_smiley_theme = g_str_equal(info->type, "smiley")))
 		destdir = g_build_filename(purple_user_dir(), "smileys", NULL);
-	else destdir = g_build_filename(purple_user_dir(), "themes", "temp", NULL);
+	else
+		destdir = g_build_filename(purple_user_dir(), "themes", "temp", NULL);
 
 	/* We'll check this just to make sure. This also lets us do something different on
 	 * other platforms, if need be */
@@ -735,15 +756,14 @@
 			purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL);
 			g_free(command);
 			g_free(destdir);
-			g_free(type);
-			g_free(original_name);
+			free_theme_info(info);
 			return;
 		}
 #else
-		if(!winpidgin_gz_untar(path, destdir)) {
+		if (!winpidgin_gz_untar(path, destdir)) {
+			purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL);
 			g_free(destdir);
-			g_free(type);
-			g_free(original_name);
+			free_theme_info(info);
 			return;
 		}
 #endif
@@ -763,13 +783,13 @@
 		}
 
 	} else if (is_archive) {
-		theme = prefs_theme_find_theme(destdir, type);
+		theme = prefs_theme_find_theme(destdir, info->type);
 
 		if (PURPLE_IS_THEME(theme)) {
 			/* create the location for the theme */
 			gchar *theme_dest = g_build_filename(purple_user_dir(), "themes",
 						 purple_theme_get_name(theme),
-						 "purple", type, NULL);
+						 "purple", info->type, NULL);
 
 			if (!g_file_test(theme_dest, G_FILE_TEST_IS_DIR))
 				purple_build_dir(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR);
@@ -777,7 +797,7 @@
 			g_free(theme_dest);
 			theme_dest = g_build_filename(purple_user_dir(), "themes",
 						 purple_theme_get_name(theme),
-						 "purple", type, NULL);
+						 "purple", info->type, NULL);
 
 			/* move the entire directory to new location */
 			g_rename(purple_theme_get_dir(theme), theme_dest);
@@ -799,9 +819,9 @@
 
 		temp_path = g_build_filename(purple_user_dir(), "themes", "temp", "sub_folder", NULL);
 
-		if (original_name != NULL) {
+		if (info->original_name != NULL) {
 			/* name was changed from the original (probably a dnd) change it back before loading */
-			temp_file = g_build_filename(temp_path, original_name, NULL);
+			temp_file = g_build_filename(temp_path, info->original_name, NULL);
 
 		} else {
 			gchar *source_name = g_path_get_basename(path);
@@ -814,12 +834,12 @@
 
 		if (purple_theme_file_copy(path, temp_file)) {
 			/* find the theme, could be in subfolder */
-			theme = prefs_theme_find_theme(temp_path, type);
+			theme = prefs_theme_find_theme(temp_path, info->type);
 
 			if (PURPLE_IS_THEME(theme)) {
 				gchar *theme_dest = g_build_filename(purple_user_dir(), "themes",
 							 purple_theme_get_name(theme),
-							 "purple", type, NULL);
+							 "purple", info->type, NULL);
 
 				if(!g_file_test(theme_dest, G_FILE_TEST_IS_DIR))
 					purple_build_dir(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR);
@@ -842,9 +862,8 @@
 		g_free(temp_path);
 	}
 
-	g_free(type);
-	g_free(original_name);
 	g_free(destdir);
+	free_theme_info(info);
 }
 
 static void
@@ -855,8 +874,10 @@
 	gchar *path;
 	size_t wc;
 
-	if ((error_message != NULL) || (len == 0))
+	if ((error_message != NULL) || (len == 0)) {
+		free_theme_info(user_data);
 		return;
+	}
 
 	f = purple_mkstemp(&path, TRUE);
 	wc = fwrite(themedata, len, 1, f);
@@ -865,6 +886,7 @@
 		fclose(f);
 		g_unlink(path);
 		g_free(path);
+		free_theme_info(user_data);
 		return;
 	}
 	fclose(f);
@@ -900,6 +922,7 @@
 				purple_debug(PURPLE_DEBUG_ERROR, "theme dnd", "%s\n",
 						   (converr ? converr->message :
 							"g_filename_from_uri error"));
+				free_theme_info(info);
 				return;
 			}
 			theme_install_theme(tmp, info);
@@ -920,7 +943,8 @@
 
 			purple_util_fetch_url(tmp, TRUE, NULL, FALSE, theme_got_url, info);
 			g_free(tmp);
-		}
+		} else
+			free_theme_info(info);
 
 		gtk_drag_finish(dc, TRUE, FALSE, t);
 	}
@@ -930,7 +954,7 @@
 
 /* builds a theme combo box from a list store with colums: icon preview, markup, theme name */
 static GtkWidget *
-prefs_build_theme_combo_box(GtkListStore *store, const gchar *current_theme, gchar *type)
+prefs_build_theme_combo_box(GtkListStore *store, const char *current_theme, const char *type)
 {
 	GtkCellRenderer *cell_rend;
 	GtkWidget *combo_box;
@@ -959,7 +983,7 @@
 	gtk_drag_dest_set(combo_box, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, te,
 					sizeof(te) / sizeof(GtkTargetEntry) , GDK_ACTION_COPY | GDK_ACTION_MOVE);
 
-	g_signal_connect(G_OBJECT(combo_box), "drag_data_received", G_CALLBACK(theme_dnd_recv), type);
+	g_signal_connect(G_OBJECT(combo_box), "drag_data_received", G_CALLBACK(theme_dnd_recv), (gpointer) type);
 
 	return combo_box;
 }
@@ -980,7 +1004,7 @@
 		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/sound/theme", new_theme);
 
 		/* New theme removes all customization */
-		for(i=0; i <  PURPLE_NUM_SOUNDS; i++){
+		for(i = 0; i < PURPLE_NUM_SOUNDS; i++){
 			pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s",
 						pidgin_sound_get_event_option(i));
 			purple_prefs_set_path(pref, "");
@@ -1037,8 +1061,6 @@
 {
 	struct theme_info *info = g_new0(struct theme_info, 1);
 	info->type = g_strdup("smiley");
-	info->extension = NULL;
-	info->original_name = NULL;
 
 	theme_install_theme(theme_file_name, info);
 }
--- a/pidgin/gtksound.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/pidgin/gtksound.c	Thu Sep 24 16:02:36 2009 +0900
@@ -212,6 +212,8 @@
 		return;
 
 	if (flags & PURPLE_MESSAGE_NICK || purple_utf8_has_word(message, chat->nick))
+		/* This isn't quite right; if you have the PURPLE_SOUND_CHAT_NICK event disabled
+		 * and the PURPLE_SOUND_CHAT_SAY event enabled, you won't get a sound at all */
 		play_conv_event(conv, PURPLE_SOUND_CHAT_NICK);
 	else
 		play_conv_event(conv, event);
@@ -591,6 +593,7 @@
 				purple_debug_error("sound", "The file: (%s) %s\n from theme: %s, was not found or wasn't readable\n",
 							sounds[event].pref, filename, theme_name);
 				g_free(filename);
+				filename = NULL;
 			}
 		}
 
@@ -614,19 +617,16 @@
 pidgin_sound_is_customized(void)
 {
 	gint i;
-	gchar *path, *file;
+	gchar *path;
+	const char *file;
 
 	for (i = 0; i < PURPLE_NUM_SOUNDS; i++) {
 		path = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s", sounds[i].pref);
-		file = g_strdup(purple_prefs_get_path(path));
+		file = purple_prefs_get_path(path);
 		g_free(path);
 
-		if (file && file[0] != '\0'){
-			g_free(file);
+		if (file && file[0] != '\0')
 			return TRUE;
-		}
-
-		g_free(file);
 	}
 
 	return FALSE;
--- a/pidgin/plugins/win32/winprefs/winprefs.c	Mon Sep 14 19:43:32 2009 +0900
+++ b/pidgin/plugins/win32/winprefs/winprefs.c	Thu Sep 24 16:02:36 2009 +0900
@@ -101,10 +101,7 @@
 			blist_set_ontop(TRUE);
 	} else {
 		purple_debug_info(WINPREFS_PLUGIN_ID, "Blist Undocking...\n");
-		if(purple_prefs_get_int(PREF_BLIST_ON_TOP) == BLIST_TOP_ALWAYS)
-			blist_set_ontop(TRUE);
-		else
-			blist_set_ontop(FALSE);
+		blist_set_ontop(purple_prefs_get_int(PREF_BLIST_ON_TOP) == BLIST_TOP_ALWAYS);
 	}
 }
 
@@ -120,10 +117,7 @@
 			blist_ab = NULL;
 		}
 
-		if(purple_prefs_get_int(PREF_BLIST_ON_TOP) == BLIST_TOP_ALWAYS)
-			blist_set_ontop(TRUE);
-		else
-			blist_set_ontop(FALSE);
+		blist_set_ontop(purple_prefs_get_int(PREF_BLIST_ON_TOP) == BLIST_TOP_ALWAYS);
 	}
 }
 
@@ -210,6 +204,12 @@
 }
 
 static void
+winprefs_set_multiple_instances(GtkWidget *w) {
+	wpurple_write_reg_string(HKEY_CURRENT_USER, "Environment", "PIDGIN_MULTI_INST",
+			gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)) ? "1" : NULL);
+}
+
+static void
 winprefs_set_blist_dockable(const char *pref, PurplePrefType type,
 		gconstpointer value, gpointer user_data)
 {
@@ -270,34 +270,18 @@
 	GtkWidget *ret;
 	GtkWidget *vbox;
 	GtkWidget *button;
-	char *gtk_version = NULL;
 	char *run_key_val;
 	char *tmp;
 
 	ret = gtk_vbox_new(FALSE, 18);
 	gtk_container_set_border_width(GTK_CONTAINER(ret), 12);
 
-	gtk_version = g_strdup_printf("GTK+\t%u.%u.%u\nGlib\t%u.%u.%u",
-		gtk_major_version, gtk_minor_version, gtk_micro_version,
-		glib_major_version, glib_minor_version, glib_micro_version);
-
-	/* Display Installed GTK+ Runtime Version */
-	if(gtk_version) {
-		GtkWidget *label;
-		vbox = pidgin_make_frame(ret, _("GTK+ Runtime Version"));
-		label = gtk_label_new(gtk_version);
-		gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-		gtk_widget_show(label);
-		g_free(gtk_version);
-	}
-
 	/* Autostart */
 	vbox = pidgin_make_frame(ret, _("Startup"));
 	tmp = g_strdup_printf(_("_Start %s on Windows startup"), PIDGIN_NAME);
 	button = gtk_check_button_new_with_mnemonic(tmp);
 	g_free(tmp);
 	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
-
 	if ((run_key_val = wpurple_read_reg_string(HKEY_CURRENT_USER, RUNKEY, "Pidgin"))
 			|| (run_key_val = wpurple_read_reg_string(HKEY_LOCAL_MACHINE, RUNKEY, "Pidgin"))) {
 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
@@ -306,6 +290,15 @@
 	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(winprefs_set_autostart), NULL);
 	gtk_widget_show(button);
 
+	button = gtk_check_button_new_with_mnemonic(_("Allow multiple instances"));
+	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+	if ((run_key_val = wpurple_read_reg_string(HKEY_CURRENT_USER, "Environment", "PIDGIN_MULTI_INST"))) {
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+		g_free(run_key_val);
+	}
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(winprefs_set_multiple_instances), NULL);
+	gtk_widget_show(button);
+
 	/* Buddy List */
 	vbox = pidgin_make_frame(ret, _("Buddy List"));
 	pidgin_prefs_checkbox(_("_Dockable Buddy List"),
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Mon Sep 14 19:43:32 2009 +0900
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Thu Sep 24 16:02:36 2009 +0900
@@ -1345,13 +1345,14 @@
   StrCpy $LANGUAGE_SET "0"
   ClearErrors
   ${GetOptions} "$R3" "/L=" $R1
-  IfErrors +3
+  IfErrors +4
   StrCpy $LANGUAGE $R1
   StrCpy $LANGUAGE_SET "1"
   Goto skip_lang
 
   ; Select Language
     ; Display Language selection dialog
+    !define MUI_LANGDLL_ALWAYSSHOW
     !insertmacro MUI_LANGDLL_DISPLAY
     skip_lang:
 
--- a/po/de.po	Mon Sep 14 19:43:32 2009 +0900
+++ b/po/de.po	Thu Sep 24 16:02:36 2009 +0900
@@ -11,9 +11,9 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-09-11 17:57+0200\n"
-"PO-Revision-Date: 2009-09-11 17:57+0200\n"
-"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
+"POT-Creation-Date: 2009-09-16 11:43+0200\n"
+"PO-Revision-Date: 2009-09-16 11:37+0200\n"
+"Last-Translator: Bjoern Voigt <bjoern@cs.tu-berlin.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -267,7 +267,7 @@
 msgstr "Empfange..."
 
 msgid "Get Info"
-msgstr "Benutzer-Info"
+msgstr "Info abrufen"
 
 msgid "Add Buddy Pounce"
 msgstr "Buddy-Alarm hinzufügen"
@@ -644,8 +644,8 @@
 msgstr[0] "Liste von %d Benutzer:\n"
 msgstr[1] "Liste von %d Benutzern:\n"
 
-msgid "Supported debug options are:  version"
-msgstr "Unterstützte Debug-Optionen sind:  version"
+msgid "Supported debug options are: plugins version"
+msgstr "Unterstützte Debug-Optionen sind: plugins version"
 
 msgid "No such command (in this context)."
 msgstr "Kein solches Kommando (in diesem Kontext)."
@@ -2201,13 +2201,13 @@
 msgid "A non-recoverable Farsight2 error has occurred."
 msgstr "Ein nicht behebbarer Farsight2-Fehler ist aufgetreten."
 
-msgid "Conference error."
+msgid "Conference error"
 msgstr "Konferenz-Fehler"
 
-msgid "Error with your microphone."
+msgid "Error with your microphone"
 msgstr "Fehler mit Ihrem Mikrofon"
 
-msgid "Error with your webcam."
+msgid "Error with your webcam"
 msgstr "Fehler mit Ihrer Webcam"
 
 #, c-format
@@ -7104,7 +7104,7 @@
 msgstr "V_erbinden"
 
 msgid "You closed the connection."
-msgstr "Sie haben die Verbindung beendet"
+msgstr "Sie haben die Verbindung beendet."
 
 msgid "Get AIM Info"
 msgstr "AIM-Info"
@@ -11093,7 +11093,7 @@
 msgstr "/Unterhaltung/_Buddy-Alarm hinzufügen..."
 
 msgid "/Conversation/_Get Info"
-msgstr "/Unterhaltung/Ben_utzer-Info abrufen"
+msgstr "/Unterhaltung/_Info abrufen"
 
 msgid "/Conversation/In_vite..."
 msgstr "/Unterhaltung/_Einladen..."
@@ -13253,7 +13253,7 @@
 msgstr "_Reiter schließen"
 
 msgid "_Get Info"
-msgstr "_Benutzer-Info"
+msgstr "_Info abrufen"
 
 msgid "_Invite"
 msgstr "_Einladen"
@@ -14317,12 +14317,13 @@
 #. *< name
 #. *< version
 msgid "Configure your microphone and webcam."
-msgstr "Mikrofon und Webcam konfigurieren"
+msgstr "Mikrofon und Webcam konfigurieren."
 
 #. *< summary
 msgid "Configure microphone and webcam settings for voice/video calls."
 msgstr ""
-"Die Mikrofon- und Webcam-Einstellungen für Audio- und Video-Anrufe bearbeiten"
+"Die Mikrofon- und Webcam-Einstellungen für Audio- und Video-Anrufe "
+"bearbeiten."
 
 msgid "Opacity:"
 msgstr "Durchlässigkeit:"