changeset 25191:b874034d4df2

propagate from branch 'im.pidgin.pidgin' (head dd9cb9ed28311438c0cc3c2962f1c030fb282df3) to branch 'im.pidgin.cpw.malu.xmpp.attention' (head 334e92dfc949bbb0dfec42b9a3fcddd84bd57847)
author Marcus Lundblad <ml@update.uu.se>
date Thu, 01 Jan 2009 18:15:46 +0000
parents 302f13678ea3 (current diff) a33943427c90 (diff)
children a50545e37a7a
files libpurple/protocols/jabber/jabber.c
diffstat 13 files changed, 309 insertions(+), 108 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Dec 28 09:51:19 2008 +0000
+++ b/ChangeLog	Thu Jan 01 18:15:46 2009 +0000
@@ -8,9 +8,20 @@
 	  Witkowski)
 	* Fix a crash that occurred when retrieving certain Offline Messages
 	  on MSN.
+	* Extended purple-url-handler to handle "gtalk" URI's. (Paul Aurich)
+	* Fix the hang on exit in Network Location Awareness for Windows XP
+	  and Windows Vista. (Paul Aurich)
 
 	XMPP:
 	* Support for XEP-0191 blocking.  (Vijay Raghunathan)
+	* Don't put SASL PLAIN or IQ Auth passwords in debug logs. (Paul Aurich)
+
+	Pidgin:
+	* Fix a crash in the Add Account dialog when changing protocols under
+	  certain circumstances.
+
+	Finch:
+	* Redirect stderr outputs to the debug window.
 
 version 2.5.3 (12/20/2008):
 	libpurple:
--- a/finch/finch.c	Sun Dec 28 09:51:19 2008 +0000
+++ b/finch/finch.c	Thu Jan 01 18:15:46 2009 +0000
@@ -229,7 +229,6 @@
 	gboolean opt_nologin = FALSE;
 	gboolean opt_version = FALSE;
 	char *opt_config_dir_arg = NULL;
-	char *opt_session_arg = NULL;
 	gboolean debug_enabled = FALSE;
 
 	struct option long_options[] = {
@@ -237,7 +236,6 @@
 		{"debug",    no_argument,       NULL, 'd'},
 		{"help",     no_argument,       NULL, 'h'},
 		{"nologin",  no_argument,       NULL, 'n'},
-		{"session",  required_argument, NULL, 's'},
 		{"version",  no_argument,       NULL, 'v'},
 		{0, 0, 0, 0}
 	};
@@ -256,7 +254,7 @@
 	opterr = 1;
 	while ((opt = getopt_long(argc, argv,
 #ifndef _WIN32
-				  "c:dhn::s:v",
+				  "c:dhn::v",
 #else
 				  "c:dhn::v",
 #endif
@@ -275,10 +273,6 @@
 		case 'n':	/* no autologin */
 			opt_nologin = TRUE;
 			break;
-		case 's':	/* use existing session ID */
-			g_free(opt_session_arg);
-			opt_session_arg = g_strdup(optarg);
-			break;
 		case 'v':	/* version */
 			opt_version = TRUE;
 			break;
--- a/finch/gntdebug.c	Sun Dec 28 09:51:19 2008 +0000
+++ b/finch/gntdebug.c	Thu Jan 01 18:15:46 2009 +0000
@@ -43,6 +43,48 @@
 
 #define PREF_ROOT "/finch/debug"
 
+static gboolean
+handle_fprintf_stderr_cb(GIOChannel *source, GIOCondition cond, gpointer null)
+{
+	gssize size;
+	char message[1024];
+
+	size = read(g_io_channel_unix_get_fd(source), message, sizeof(message) - 1);
+	if (size <= 0) {
+		/* Something bad probably happened elsewhere ... let's ignore */
+	} else {
+		message[size] = '\0';
+		g_log("stderr", G_LOG_LEVEL_WARNING, "%s", message);
+	}
+
+	return TRUE;
+}
+
+static void
+handle_fprintf_stderr(gboolean stop)
+{
+	GIOChannel *stderrch;
+	static int readhandle = -1;
+	int pipes[2];
+
+	if (stop) {
+		if (readhandle >= 0) {
+			g_source_remove(readhandle);
+			readhandle = -1;
+		}
+		return;
+	}
+	pipe(pipes);
+	dup2(pipes[1], STDERR_FILENO);
+
+	stderrch = g_io_channel_unix_new(pipes[0]);
+	g_io_channel_set_close_on_unref(stderrch, TRUE);
+	readhandle = g_io_add_watch_full(stderrch, G_PRIORITY_HIGH,
+			G_IO_IN | G_IO_ERR | G_IO_PRI,
+			handle_fprintf_stderr_cb, NULL, NULL);
+	g_io_channel_unref(stderrch);
+}
+
 static struct
 {
 	GntWidget *window;
@@ -143,10 +185,6 @@
 }
 
 static void
-suppress_error_messages(const char *message)
-{}
-
-static void
 toggle_pause(GntWidget *w, gpointer n)
 {
 	debug.paused = !debug.paused;
@@ -348,10 +386,11 @@
 #ifdef USE_GSTREAMER
 	REGISTER_G_LOG_HANDLER("GStreamer");
 #endif
+	REGISTER_G_LOG_HANDLER("stderr");
 
 	g_set_print_handler(print_stderr);   /* Redirect the debug messages to stderr */
 	if (!purple_debug_is_enabled())
-		g_set_printerr_handler(suppress_error_messages);
+		handle_fprintf_stderr(FALSE);
 
 	purple_prefs_add_none(PREF_ROOT);
 	purple_prefs_add_string(PREF_ROOT "/filter", "");
@@ -365,5 +404,6 @@
 
 void finch_debug_uninit()
 {
+	handle_fprintf_stderr(TRUE);
 }
 
--- a/libpurple/core.c	Sun Dec 28 09:51:19 2008 +0000
+++ b/libpurple/core.c	Thu Jan 01 18:15:46 2009 +0000
@@ -219,6 +219,7 @@
 	purple_proxy_uninit();
 	purple_dnsquery_uninit();
 	purple_imgstore_uninit();
+	purple_network_uninit();
 
 	purple_debug_info("main", "Unloading all plugins\n");
 	purple_plugins_destroy_all();
--- a/libpurple/network.c	Sun Dec 28 09:51:19 2008 +0000
+++ b/libpurple/network.c	Thu Jan 01 18:15:46 2009 +0000
@@ -71,6 +71,11 @@
 
 #elif defined _WIN32
 static int current_network_count;
+
+/* Mutex for the other global vars */
+static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+static gboolean network_initialized;
+static HANDLE network_change_handle;
 #endif
 
 struct _PurpleNetworkListenData {
@@ -535,8 +540,8 @@
 
 static gpointer wpurple_network_change_thread(gpointer data)
 {
-	HANDLE h;
 	WSAQUERYSET qs;
+	WSAEVENT *nla_event;
 	time_t last_trigger = time(NULL);
 
 	int (WSAAPI *MyWSANSPIoctl) (
@@ -549,23 +554,47 @@
 		return NULL;
 	}
 
+	if ((nla_event = WSACreateEvent()) == WSA_INVALID_EVENT) {
+		int errorid = WSAGetLastError();
+		gchar *msg = g_win32_error_message(errorid);
+		purple_debug_warning("network", "Couldn't create WSA event. "
+			"Message: %s (%d).\n", msg, errorid);
+		g_free(msg);
+		g_thread_exit(NULL);
+		return NULL;
+	}
+
 	while (TRUE) {
 		int retval;
 		DWORD retLen = 0;
+		WSACOMPLETION completion;
+		WSAOVERLAPPED overlapped;
+
+		g_static_mutex_lock(&mutex);
+		if (network_initialized == FALSE) {
+			/* purple_network_uninit has been called */
+			WSACloseEvent(nla_event);
+			g_static_mutex_unlock(&mutex);
+			g_thread_exit(NULL);
+			return NULL;
+		}
 
 		memset(&qs, 0, sizeof(WSAQUERYSET));
 		qs.dwSize = sizeof(WSAQUERYSET);
 		qs.dwNameSpace = NS_NLA;
-		if (WSALookupServiceBegin(&qs, 0, &h) == SOCKET_ERROR) {
+		if (WSALookupServiceBegin(&qs, 0, &network_change_handle) == SOCKET_ERROR) {
 			int errorid = WSAGetLastError();
 			gchar *msg = g_win32_error_message(errorid);
 			purple_debug_warning("network", "Couldn't retrieve NLA SP lookup handle. "
 				"NLA service is probably not running. Message: %s (%d).\n",
 				msg, errorid);
 			g_free(msg);
+			WSACloseEvent(nla_event);
+			g_static_mutex_unlock(&mutex);
 			g_thread_exit(NULL);
 			return NULL;
 		}
+		g_static_mutex_unlock(&mutex);
 
 		/* Make sure at least 30 seconds have elapsed since the last
 		 * notification so we don't peg the cpu if this keeps changing. */
@@ -574,19 +603,40 @@
 
 		last_trigger = time(NULL);
 
-		/* This will block until there is a network change */
-		if (MyWSANSPIoctl(h, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &retLen, NULL) == SOCKET_ERROR) {
+		memset(&completion, 0, sizeof(WSACOMPLETION));
+		completion.Type = NSP_NOTIFY_EVENT;
+		overlapped.hEvent = nla_event;
+		completion.Parameters.Event.lpOverlapped = &overlapped;
+
+		if (MyWSANSPIoctl(network_change_handle, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &retLen, &completion) == SOCKET_ERROR) {
 			int errorid = WSAGetLastError();
-			gchar *msg = g_win32_error_message(errorid);
-			purple_debug_warning("network", "Unable to wait for changes. Message: %s (%d).\n",
-				msg, errorid);
-			g_free(msg);
+			/* WSA_IO_PENDING indicates successful async notification will happen */
+			if (errorid != WSA_IO_PENDING) {
+				gchar *msg = g_win32_error_message(errorid);
+				purple_debug_warning("network", "Unable to wait for changes. Message: %s (%d).\n",
+					msg, errorid);
+				g_free(msg);
+			}
 		}
 
-		retval = WSALookupServiceEnd(h);
+		/* This will block until NLA notifies us */
+		retval = WaitForSingleObjectEx(nla_event, WSA_INFINITE, TRUE);
+
+		g_static_mutex_lock(&mutex);
+		if (network_initialized == FALSE) {
+			/* Time to die */
+			WSACloseEvent(nla_event);
+			g_static_mutex_unlock(&mutex);
+			g_thread_exit(NULL);
+			return NULL;
+		}
+
+		retval = WSALookupServiceEnd(network_change_handle);
+		network_change_handle = NULL;
+		WSAResetEvent(nla_event);
+		g_static_mutex_unlock(&mutex);
 
 		purple_timeout_add(0, wpurple_network_change_thread_cb, NULL);
-
 	}
 
 	g_thread_exit(NULL);
@@ -714,6 +764,7 @@
 	GError *err = NULL;
 	gint cnt = wpurple_get_connected_network_count();
 
+	network_initialized = TRUE;
 	if (cnt < 0) /* Assume there is a network */
 		current_network_count = 1;
 	/* Don't listen for network changes if we can't tell anyway */
@@ -782,6 +833,25 @@
 		dbus_g_connection_unref(nm_conn);
 #endif
 
+#ifdef _WIN32
+	g_static_mutex_lock(&mutex);
+	network_initialized = FALSE;
+	if (network_change_handle != NULL) {
+		int retval;
+		/* Trigger the NLA thread to stop waiting for network changes. Not
+		 * doing this can cause hangs on WSACleanup. */
+		purple_debug_warning("network", "Terminating the NLA thread\n");
+		if ((retval = WSALookupServiceEnd(network_change_handle)) == SOCKET_ERROR) {
+			int errorid = WSAGetLastError();
+			gchar *msg = g_win32_error_message(errorid);
+			purple_debug_warning("network", "Unable to kill NLA thread. Message: %s (%d).\n",
+				msg, errorid);
+			g_free(msg);
+		}
+	}
+	g_static_mutex_unlock(&mutex);
+
+#endif
 	purple_signal_unregister(purple_network_get_handle(),
 	                         "network-configuration-changed");
 }
--- a/libpurple/protocols/jabber/jabber.c	Sun Dec 28 09:51:19 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Thu Jan 01 18:15:46 2009 +0000
@@ -351,9 +351,33 @@
 {
 
 	/* because printing a tab to debug every minute gets old */
-	if(strcmp(data, "\t"))
-		purple_debug(PURPLE_DEBUG_MISC, "jabber", "Sending%s: %s\n",
-				js->gsc ? " (ssl)" : "", data);
+	if(strcmp(data, "\t")) {
+		char *text = NULL, *last_part = NULL, *tag_start = NULL;
+
+		/* Because debug logs with plaintext passwords make me sad */
+		if(js->state != JABBER_STREAM_CONNECTED &&
+				/* Either <auth> or <query><password>... */
+				(((tag_start = strstr(data, "<auth ")) &&
+					strstr(data, "xmlns='urn:ietf:params:xml:ns:xmpp-sasl'")) ||
+				((tag_start = strstr(data, "<query ")) &&
+					strstr(data, "xmlns='jabber:iq:auth'>") &&
+					(tag_start = strstr(tag_start, "<password>"))))) {
+			char *data_start, *tag_end = strchr(tag_start, '>');
+			text = g_strdup(data);
+
+			data_start = text + (tag_end - data) + 1;
+
+			last_part = strchr(data_start, '<');
+			*data_start = '\0';
+		}
+
+		purple_debug(PURPLE_DEBUG_MISC, "jabber", "Sending%s: %s%s%s\n",
+				js->gsc ? " (ssl)" : "", text ? text : data,
+				last_part ? "password removed" : "",
+				last_part ? last_part : "");
+
+		g_free(text);
+	}
 
 	/* If we've got a security layer, we need to encode the data,
 	 * splitting it on the maximum buffer length negotiated */
--- a/libpurple/protocols/myspace/myspace.c	Sun Dec 28 09:51:19 2008 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Thu Jan 01 18:15:46 2009 +0000
@@ -211,7 +211,6 @@
 			/* Don't have uid offhand - need to ask for it, and wait until hear back before sending. */
 			purple_debug_info("msim", ">>> msim_postprocess_outgoing: couldn't find username %s in blist\n",
 					username ? username : "(NULL)");
-			/* TODO: where is cloned message freed? Should be in _cb. */
 			msim_lookup_user(session, username, msim_postprocess_outgoing_cb, msim_msg_clone(msg));
 			return TRUE;       /* not sure of status yet - haven't sent! */
 		}
@@ -1923,8 +1922,7 @@
 
 	msim_process(session, msg);
 
-	/* TODO: Free copy cloned from  msim_preprocess_incoming(). */
-	/* msim_msg_free(msg); */
+	msim_msg_free(msg);
 	msim_msg_free(body);
 }
 
@@ -2731,9 +2729,15 @@
 	 * doesn't seem like it would be necessary, but the official client
 	 * does it)
 	 */
-	if (!msim_update_blocklist_for_buddy(session, buddy->name, FALSE, FALSE))
+	if (!msim_update_blocklist_for_buddy(session, buddy->name, FALSE, FALSE)) {
 		purple_notify_error(NULL, NULL,
 				_("Failed to remove buddy"), _("blocklist command failed"));
+		return;
+	}
+	if (buddy->proto_data) {
+		msim_user_free(buddy->proto_data);
+		buddy->proto_data = NULL;
+	}
 }
 
 /**
@@ -2823,6 +2827,13 @@
 	msim_update_blocklist_for_buddy(session, name, FALSE, FALSE);
 }
 
+static void
+msim_buddy_free(PurpleBuddy *buddy)
+{
+	msim_user_free(buddy->proto_data);
+	buddy->proto_data = NULL;
+}
+
 /**
  * Returns a string of a username in canonical form. Basically removes all the
  * spaces, lowercases the string, and looks up user IDs to usernames.
@@ -3039,7 +3050,7 @@
 	NULL,              /* alias_buddy */
 	NULL,              /* group_buddy */
 	NULL,              /* rename_group */
-	NULL,              /* buddy_free */
+	msim_buddy_free,   /* buddy_free */
 	NULL,              /* convo_closed */
 	msim_normalize,    /* normalize */
 	NULL,              /* set_buddy_icon */
--- a/libpurple/protocols/myspace/user.c	Sun Dec 28 09:51:19 2008 +0000
+++ b/libpurple/protocols/myspace/user.c	Thu Jan 01 18:15:46 2009 +0000
@@ -55,10 +55,9 @@
 	if (!buddy->proto_data) {
 		/* No MsimUser for this buddy; make one. */
 
-		/* TODO: where is this freed? */
 		user = g_new0(MsimUser, 1);
 		user->buddy = buddy;
-		user->id = purple_blist_node_get_int(&buddy->node, "UserID");
+		user->id = purple_blist_node_get_int((PurpleBlistNode*)buddy, "UserID");
 		buddy->proto_data = (gpointer)user;
 	}
 
@@ -67,6 +66,23 @@
 	return user;
 }
 
+void msim_user_free(MsimUser *user)
+{
+	if (!user)
+		return;
+
+	g_free(user->client_info);
+	g_free(user->gender);
+	g_free(user->location);
+	g_free(user->headline);
+	g_free(user->display_name);
+	g_free(user->username);
+	g_free(user->band_name);
+	g_free(user->song_name);
+	g_free(user->image_url);
+	g_free(user);
+}
+
 /**
  * Find and return an MsimUser * representing a user on the buddy list, or NULL.
  */
--- a/libpurple/protocols/myspace/user.h	Sun Dec 28 09:51:19 2008 +0000
+++ b/libpurple/protocols/myspace/user.h	Thu Jan 01 18:15:46 2009 +0000
@@ -47,6 +47,7 @@
 typedef void (*MSIM_USER_LOOKUP_CB)(MsimSession *session, const MsimMessage *userinfo, gpointer data);
 
 MsimUser *msim_get_user_from_buddy(PurpleBuddy *buddy);
+void msim_user_free(MsimUser *user);
 MsimUser *msim_find_user(MsimSession *session, const gchar *username);
 void msim_append_user_info(MsimSession *session, PurpleNotifyUserInfo *user_info, MsimUser *user, gboolean full);
 gboolean msim_store_user_info(MsimSession *session, const MsimMessage *msg, MsimUser *user);
--- a/libpurple/purple-url-handler	Sun Dec 28 09:51:19 2008 +0000
+++ b/libpurple/purple-url-handler	Thu Jan 01 18:15:46 2009 +0000
@@ -299,6 +299,31 @@
     else:
         goim(account, screenname)
 
+def gtalk(uri):
+    protocol = "prpl-jabber"
+    match = re.match(r"^gtalk:([^?]*)(\?(.*))", uri)
+    if not match:
+        print "Invalid gtalk URI: %s" % uri
+        return
+
+    command = urllib.unquote_plus(match.group(1))
+    paramstring = match.group(3)
+    params = {}
+    if paramstring:
+        for param in paramstring.split("&"):
+            key, value = extendlist(param.split("=", 1), 2, "")
+            params[key] = urllib.unquote_plus(value)
+    accountname = params.get("from_jid", "")
+    jid = params.get("jid", "")
+
+    account = findaccount(protocol, accountname)
+
+    if command.lower() == "chat":
+        goim(account, jid)
+    elif command.lower() == "call":
+        # XXX V&V prompt to establish call
+        goim(account, jid)
+
 def ymsgr(uri):
     protocol = "prpl-yahoo"
     match = re.match(r"^ymsgr:([^?]*)(\?([^&]*)(&(.*))?)", uri)
@@ -355,6 +380,8 @@
             sip(uri)
         elif type == "xmpp":
             xmpp(uri)
+        elif type == "gtalk":
+            gtalk(uri)
         elif type == "ymsgr":
             ymsgr(uri)
         else:
--- a/libpurple/value.c	Sun Dec 28 09:51:19 2008 +0000
+++ b/libpurple/value.c	Thu Jan 01 18:15:46 2009 +0000
@@ -320,6 +320,7 @@
 purple_value_set_string(PurpleValue *value, const char *data)
 {
 	g_return_if_fail(value != NULL);
+	g_return_if_fail(data == NULL || g_utf8_validate(data, -1, NULL));
 
 	g_free(value->data.string_data);
 	value->data.string_data = g_strdup(data);
--- a/pidgin/gtkaccount.c	Sun Dec 28 09:51:19 2008 +0000
+++ b/pidgin/gtkaccount.c	Thu Jan 01 18:15:46 2009 +0000
@@ -286,25 +286,27 @@
 screenname_nofocus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog *dialog)
 {
 	GdkColor color = {0, 34952, 35466, 34181};
-	GHashTable *table;
-	const char *label;
-
-	table = dialog->prpl_info->get_account_text_table(NULL);
-	label = g_hash_table_lookup(table, "login_label");
-
-	if (*gtk_entry_get_text(GTK_ENTRY(widget)) == '\0') {
-		/* We have to avoid hitting the screenname_changed_cb function 
-		 * because it enables buttons we don't want enabled yet ;)
-		 */
-		g_signal_handlers_block_by_func(widget, G_CALLBACK(screenname_changed_cb), dialog);
-		gtk_entry_set_text(GTK_ENTRY(widget), label);
-		/* Make sure we can hit it again */
-		g_signal_handlers_unblock_by_func(widget, G_CALLBACK(screenname_changed_cb), dialog);
-		gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &color);
+	GHashTable *table = NULL;
+	const char *label = NULL;
+
+	if(PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(dialog->prpl_info, get_account_text_table)) {
+		table = dialog->prpl_info->get_account_text_table(NULL);
+		label = g_hash_table_lookup(table, "login_label");
+
+		if (*gtk_entry_get_text(GTK_ENTRY(widget)) == '\0') {
+			/* We have to avoid hitting the screenname_changed_cb function 
+			 * because it enables buttons we don't want enabled yet ;)
+			 */
+			g_signal_handlers_block_by_func(widget, G_CALLBACK(screenname_changed_cb), dialog);
+			gtk_entry_set_text(GTK_ENTRY(widget), label);
+			/* Make sure we can hit it again */
+			g_signal_handlers_unblock_by_func(widget, G_CALLBACK(screenname_changed_cb), dialog);
+			gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &color);
+		}
+
+		g_hash_table_destroy(table);
 	}
 
-	g_hash_table_destroy(table);
-
 	return FALSE;
 }
 
--- a/pidgin/gtkblist.c	Sun Dec 28 09:51:19 2008 +0000
+++ b/pidgin/gtkblist.c	Thu Jan 01 18:15:46 2009 +0000
@@ -7655,6 +7655,7 @@
 	GtkAccelGroup *accel_group = NULL;
 	GList *l = NULL, *accounts = NULL;
 	gboolean disabled_accounts = FALSE;
+	gboolean enabled_accounts = FALSE;
 
 	if (accountmenu == NULL)
 		return;
@@ -7708,10 +7709,16 @@
 				G_CALLBACK(enable_account_cb), account);
 			gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
 			gtk_widget_show(menuitem);
+		} else {
+			enabled_accounts = TRUE;
 		}
 	}
 
+	if (!enabled_accounts)
+		return;
+
 	pidgin_separator(accountmenu);
+	accel_group = gtk_menu_get_accel_group(GTK_MENU(accountmenu));
 
 	for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) {
 		char *buf = NULL;
@@ -7723,67 +7730,63 @@
 		PurplePlugin *plugin = NULL;
 
 		account = accounts->data;
-		accel_group = gtk_menu_get_accel_group(GTK_MENU(accountmenu));
-
-		if(purple_account_get_enabled(account, PIDGIN_UI)) {
-			buf = g_strconcat(purple_account_get_username(account), " (",
-					purple_account_get_protocol_name(account), ")", NULL);
-			menuitem = gtk_image_menu_item_new_with_label(buf);
-			accel_path_buf = g_strconcat(N_("<PurpleMain>/Accounts/"), buf, NULL);
-			g_free(buf);
-			pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
-			if (pixbuf != NULL)
-			{
-				if (!purple_account_is_connected(account))
-					gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf,
-							0.0, FALSE);
-				image = gtk_image_new_from_pixbuf(pixbuf);
-				g_object_unref(G_OBJECT(pixbuf));
-				gtk_widget_show(image);
-				gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
-			}
-			gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem);
+
+		if (!purple_account_get_enabled(account, PIDGIN_UI))
+			continue;
+
+		buf = g_strconcat(purple_account_get_username(account), " (",
+				purple_account_get_protocol_name(account), ")", NULL);
+		menuitem = gtk_image_menu_item_new_with_label(buf);
+		accel_path_buf = g_strconcat(N_("<PurpleMain>/Accounts/"), buf, NULL);
+		g_free(buf);
+		pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+		if (pixbuf != NULL) {
+			if (!purple_account_is_connected(account))
+				gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf,
+						0.0, FALSE);
+			image = gtk_image_new_from_pixbuf(pixbuf);
+			g_object_unref(G_OBJECT(pixbuf));
+			gtk_widget_show(image);
+			gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
+		}
+		gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem);
+		gtk_widget_show(menuitem);
+
+		submenu = gtk_menu_new();
+		gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
+		gtk_menu_set_accel_path(GTK_MENU(submenu), accel_path_buf);
+		g_free(accel_path_buf);
+		gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
+		gtk_widget_show(submenu);
+
+
+		menuitem = gtk_menu_item_new_with_mnemonic(_("_Edit Account"));
+		g_signal_connect(G_OBJECT(menuitem), "activate",
+				G_CALLBACK(modify_account_cb), account);
+		gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
+		gtk_widget_show(menuitem);
+
+		pidgin_separator(submenu);
+
+		gc = purple_account_get_connection(account);
+		plugin = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? gc->prpl : NULL;
+		if (plugin && PURPLE_PLUGIN_HAS_ACTIONS(plugin)) {
+			build_plugin_actions(submenu, plugin, gc);
+		} else {
+			menuitem = gtk_menu_item_new_with_label(_("No actions available"));
+			gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
+			gtk_widget_set_sensitive(menuitem, FALSE);
 			gtk_widget_show(menuitem);
-
-			submenu = gtk_menu_new();
-			gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
-			gtk_menu_set_accel_path(GTK_MENU(submenu), accel_path_buf);
-			g_free(accel_path_buf);
-			gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
-			gtk_widget_show(submenu);
-
-
-			menuitem = gtk_menu_item_new_with_mnemonic(_("_Edit Account"));
-			g_signal_connect(G_OBJECT(menuitem), "activate",
-					G_CALLBACK(modify_account_cb), account);
-			gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
-			gtk_widget_show(menuitem);
-
-			pidgin_separator(submenu);
-
-			gc = purple_account_get_connection(account);
-			plugin = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? gc->prpl : NULL;
-			if (plugin && PURPLE_PLUGIN_HAS_ACTIONS(plugin)) {
-				build_plugin_actions(submenu, plugin, gc);
-			} else {
-				menuitem = gtk_menu_item_new_with_label(_("No actions available"));
-				gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
-				gtk_widget_set_sensitive(menuitem, FALSE);
-				gtk_widget_show(menuitem);
-			}
-
-			pidgin_separator(submenu);
-
-			menuitem = gtk_menu_item_new_with_mnemonic(_("_Disable"));
-			g_signal_connect(G_OBJECT(menuitem), "activate",
-					G_CALLBACK(disable_account_cb), account);
-			gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
-			gtk_widget_show(menuitem);
-		} else {
-			disabled_accounts = TRUE;
 		}
-	}
-
+
+		pidgin_separator(submenu);
+
+		menuitem = gtk_menu_item_new_with_mnemonic(_("_Disable"));
+		g_signal_connect(G_OBJECT(menuitem), "activate",
+				G_CALLBACK(disable_account_cb), account);
+		gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
+		gtk_widget_show(menuitem);
+	}
 }
 
 static GList *plugin_submenus = NULL;