changeset 25382:c0b42d6c2785

propagate from branch 'im.pidgin.pidgin' (head f018e11a7ea08e07f22667e6daca2ec7e64f9710) to branch 'im.pidgin.pidgin.next.minor' (head 685e1461486f2e5322bc2952f8e8bbbf4313dee9)
author Richard Laager <rlaager@wiktel.com>
date Fri, 02 Jan 2009 22:35:12 +0000
parents 43b721aa4b76 (current diff) b26e8a41a937 (diff)
children b5df6cd20d8b
files libpurple/core.c libpurple/network.c libpurple/protocols/jabber/jabber.c libpurple/protocols/myspace/myspace.c libpurple/protocols/myspace/user.c libpurple/protocols/yahoo/yahoo.c pidgin/gtkblist.c pidgin/gtkdialogs.c
diffstat 23 files changed, 345 insertions(+), 130 deletions(-) [+]
line wrap: on
line diff
--- a/AUTHORS	Fri Jan 02 22:28:34 2009 +0000
+++ b/AUTHORS	Fri Jan 02 22:35:12 2009 +0000
@@ -35,6 +35,7 @@
 
 Crazy Patch Writers:
 -------------------
+Paul Aurich
 Felipe 'shx' Contreras
 Marcus 'malu' Lundblad
 Dennis 'EvilDennisR' Ristuccia
--- a/COPYRIGHT	Fri Jan 02 22:28:34 2009 +0000
+++ b/COPYRIGHT	Fri Jan 02 22:35:12 2009 +0000
@@ -63,6 +63,7 @@
 Damien Carbery
 Michael Carlson
 Keegan Carruthers-Smith
+Ludovico Cavedon
 Steve Cavilia
 Julien Cegarra
 Cerulean Studios, LLC
@@ -228,6 +229,7 @@
 Steve Láposi
 Daniel Larsson
 Peter Lawler
+Vadim Lebedev
 Ho-seok Lee
 Jean-Yves Lefort
 Moses Lei
--- a/ChangeLog	Fri Jan 02 22:28:34 2009 +0000
+++ b/ChangeLog	Fri Jan 02 22:35:12 2009 +0000
@@ -6,9 +6,22 @@
 	  Rosinski)
 	* Don't ignore namespace information when parsing XMPP data. (Michal
 	  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/doc/pidgin.1.in	Fri Jan 02 22:28:34 2009 +0000
+++ b/doc/pidgin.1.in	Fri Jan 02 22:35:12 2009 +0000
@@ -641,6 +641,8 @@
 
 Our crazy patch writers include:
 .br
+  Paul Aurich
+.br
   Felipe 'shx' Contreras
 .br
   Marcus 'malu' Lundblad
--- a/finch/finch.c	Fri Jan 02 22:28:34 2009 +0000
+++ b/finch/finch.c	Fri Jan 02 22:35:12 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	Fri Jan 02 22:28:34 2009 +0000
+++ b/finch/gntdebug.c	Fri Jan 02 22:35:12 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	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/core.c	Fri Jan 02 22:35:12 2009 +0000
@@ -224,6 +224,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	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/network.c	Fri Jan 02 22:35:12 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 {
@@ -539,8 +544,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) (
@@ -553,23 +558,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. */
@@ -578,19 +607,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);
@@ -729,6 +779,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 */
@@ -797,6 +848,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/prefs.c	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/prefs.c	Fri Jan 02 22:35:12 2009 +0000
@@ -693,12 +693,15 @@
 	char *name;
 	GSList *l;
 
-	if(!pref || pref == &prefs)
+	if(!pref)
 		return;
 
 	while(pref->first_child)
 		remove_pref(pref->first_child);
 
+	if(pref == &prefs)
+		return;
+
 	if(pref->parent->first_child == pref) {
 		pref->parent->first_child = pref->sibling;
 	} else {
@@ -1452,4 +1455,8 @@
 	}
 
 	purple_prefs_disconnect_by_handle(purple_prefs_get_handle());
+	purple_prefs_destroy();
+	g_hash_table_destroy(prefs_hash);
+	prefs_hash = NULL;
+
 }
--- a/libpurple/protocols/bonjour/mdns_win32.c	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Fri Jan 02 22:35:12 2009 +0000
@@ -167,7 +167,11 @@
 	ResolveCallbackArgs *args = (ResolveCallbackArgs*) data;
 	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
 	gboolean delete_buddy = FALSE;
-	PurpleBuddy *pb;
+	PurpleBuddy *pb = NULL;
+
+	/* Make sure that the BonjourBuddy associated with this request is still around */
+	if (g_slist_find(pending_buddies, args->bb) == NULL)
+		goto cleanup;
 
 	if ((pb = purple_find_buddy(args->account, args->bb->name)))
 		if (pb->proto_data != args->bb)
@@ -209,6 +213,8 @@
 
 	}
 
+	cleanup:
+
 	/* free the hosts list*/
 	while (hosts != NULL) {
 		hosts = g_slist_remove(hosts, hosts->data);
--- a/libpurple/protocols/jabber/jabber.c	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Fri Jan 02 22:35:12 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/jabber/message.c	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/protocols/jabber/message.c	Fri Jan 02 22:35:12 2009 +0000
@@ -291,27 +291,28 @@
 	PurpleAccount *account;
 	PurpleConversation *c;
 	char *username, *str;
-	
+
 	/* Delayed buzz MUST NOT be accepted */
 	if(jm->delayed)
 		return;
-	
+
 	/* Reject buzz when it's not enabled */
 	if(!jm->js->allowBuzz)
 		return;
-	
+
 	account = purple_connection_get_account(jm->js->gc);
-	
-	if ((buddy = purple_find_buddy(account, jm->from)) != NULL)
-		username = g_markup_escape_text(purple_buddy_get_alias(buddy), -1);
-	else
+
+	if ((buddy = purple_find_buddy(account, jm->from)) == NULL)
 		return; /* Do not accept buzzes from unknown people */
 
-	c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, jm->from);
+	c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, jm->from, account);
+	if (c == NULL)
+		c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, jm->from);
 
-	str = g_strdup_printf(_("%s has buzzed you!"), username);
-	
-	purple_conversation_write(c, NULL, str, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL));
+	username = g_markup_escape_text(purple_buddy_get_alias(buddy), -1);
+	/* xmpp only has 1 attention type, so index is 0 */
+	purple_prpl_got_attention(jm->js->gc, username, 0);
+
 	g_free(username);
 	g_free(str);
 }
--- a/libpurple/protocols/myspace/myspace.c	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Fri Jan 02 22:35:12 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! */
 		}
@@ -1930,8 +1929,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);
 }
 
@@ -2747,6 +2745,9 @@
 	if (!msim_update_blocklist_for_buddy(session, name, FALSE, FALSE))
 		purple_notify_error(NULL, NULL,
 				_("Failed to remove buddy"), _("blocklist command failed"));
+		return;
+	}
+	msim_buddy_free(buddy);
 }
 
 /**
@@ -2766,7 +2767,6 @@
 			"sesskey", MSIM_TYPE_INTEGER, session->sesskey,
 			/* 'delprofileid' with uid will be inserted here. */
 			NULL);
-
 	if (!msim_postprocess_outgoing(session, msg, name, "delprofileid", NULL))
 		purple_debug_error("myspace", "delbuddy command failed\n");
 	msim_msg_free(msg);
@@ -2837,6 +2837,13 @@
 	msim_update_blocklist_for_buddy(session, name, FALSE, FALSE);
 }
 
+static void
+msim_buddy_free(PurpleBuddy *buddy)
+{
+	msim_user_free(purple_buddy_get_protocol_data(buddy));
+	purple_buddy_set_protocol_data(buddy, 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.
@@ -3053,7 +3060,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	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/protocols/myspace/user.c	Fri Jan 02 22:35:12 2009 +0000
@@ -55,7 +55,6 @@
 	if (!(user = purple_buddy_get_protocol_data(buddy))) {
 		/* No MsimUser for this buddy; make one. */
 
-		/* TODO: where is this freed? */
 		user = g_new0(MsimUser, 1);
 		user->buddy = buddy;
 		purple_buddy_set_protocol_data(buddy, user);
@@ -64,6 +63,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	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/protocols/myspace/user.h	Fri Jan 02 22:35:12 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/protocols/yahoo/yahoo.c	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Fri Jan 02 22:35:12 2009 +0000
@@ -4180,8 +4180,7 @@
 	           "Trying to join %s \n", args[0]);
 
 	comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-	g_hash_table_replace(comp, g_strdup("room"),
-	g_strdup_printf("%s", g_ascii_strdown(args[0], strlen(args[0]))));
+	g_hash_table_replace(comp, g_strdup("room"), g_ascii_strdown(args[0], -1));
 	g_hash_table_replace(comp, g_strdup("type"), g_strdup("Chat"));
 
 	yahoo_c_join(gc, comp);
--- a/libpurple/proxy.c	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/proxy.c	Fri Jan 02 22:35:12 2009 +0000
@@ -621,7 +621,7 @@
 	if (!PURPLE_PROXY_CONNECT_DATA_IS_VALID(connect_data))
 		return;
 
-	purple_debug_info("proxy", "Connected to %s:%d.\n",
+	purple_debug_info("proxy", "Connecting to %s:%d.\n",
 					connect_data->host, connect_data->port);
 
 	/*
--- a/libpurple/purple-url-handler	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/purple-url-handler	Fri Jan 02 22:35:12 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	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/value.c	Fri Jan 02 22:35:12 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/libpurple/win32/libc_interface.c	Fri Jan 02 22:28:34 2009 +0000
+++ b/libpurple/win32/libc_interface.c	Fri Jan 02 22:35:12 2009 +0000
@@ -31,13 +31,7 @@
 #include "config.h"
 #include "debug.h"
 #include "libc_internal.h"
-#if GLIB_CHECK_VERSION(2,6,0)
-# include <glib/gstdio.h>
-#else
-#define g_remove remove
-#define g_rename rename
-#define g_stat stat
-#endif
+#include <glib/gstdio.h>
 
 #ifdef ENABLE_NLS
 #  include <locale.h>
@@ -58,6 +52,9 @@
 #  define dngettext(Domain, Singular, Plural, Number) ((Number == 1) ? ((const char *)Singular) : ((const char *)Plural))
 #endif
 
+#ifndef S_ISDIR
+# define S_ISDIR(m) (((m)&S_IFDIR)==S_IFDIR)
+#endif
 
 static char errbuf[1024];
 
--- a/pidgin/gtkaccount.c	Fri Jan 02 22:28:34 2009 +0000
+++ b/pidgin/gtkaccount.c	Fri Jan 02 22:35:12 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	Fri Jan 02 22:28:34 2009 +0000
+++ b/pidgin/gtkblist.c	Fri Jan 02 22:35:12 2009 +0000
@@ -7825,6 +7825,7 @@
 	GtkAccelGroup *accel_group = NULL;
 	GList *l = NULL, *accounts = NULL;
 	gboolean disabled_accounts = FALSE;
+	gboolean enabled_accounts = FALSE;
 
 	if (accountmenu == NULL)
 		return;
@@ -7878,10 +7879,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;
@@ -7893,67 +7900,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;
--- a/pidgin/gtkdialogs.c	Fri Jan 02 22:28:34 2009 +0000
+++ b/pidgin/gtkdialogs.c	Fri Jan 02 22:35:12 2009 +0000
@@ -100,6 +100,7 @@
 
 /* Order: Alphabetical by Last Name */
 static const struct developer patch_writers[] = {
+	{"Paul Aurich", NULL, NULL },
 	{"Marcus 'malu' Lundblad", NULL, NULL},
 	{"Dennis 'EvilDennisR' Ristuccia",	N_("Senior Contributor/QA"),	NULL},
 	{"Peter 'Fmoo' Ruibal",		NULL,	NULL},