changeset 24904:88b963749fea

merge of '2ec56f8fe4fa8959fcac15d674b339c112802a49' and 'd221fa27ec6a8f0ab246208650e5c26658127ed4'
author Mark Doliner <mark@kingant.net>
date Sun, 04 Jan 2009 20:10:55 +0000
parents 5a435e953bc6 (current diff) 115a2e9ea8b3 (diff)
children ae5e92bd6b08
files
diffstat 27 files changed, 384 insertions(+), 144 deletions(-) [+]
line wrap: on
line diff
--- a/AUTHORS	Mon Dec 29 03:49:33 2008 +0000
+++ b/AUTHORS	Sun Jan 04 20:10:55 2009 +0000
@@ -35,6 +35,7 @@
 
 Crazy Patch Writers:
 -------------------
+Paul Aurich
 Felipe 'shx' Contreras
 Marcus 'malu' Lundblad
 Dennis 'EvilDennisR' Ristuccia
--- a/COPYRIGHT	Mon Dec 29 03:49:33 2008 +0000
+++ b/COPYRIGHT	Sun Jan 04 20:10:55 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	Mon Dec 29 03:49:33 2008 +0000
+++ b/ChangeLog	Sun Jan 04 20:10:55 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/doc/pidgin.1.in	Mon Dec 29 03:49:33 2008 +0000
+++ b/doc/pidgin.1.in	Sun Jan 04 20:10:55 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	Mon Dec 29 03:49:33 2008 +0000
+++ b/finch/finch.c	Sun Jan 04 20:10:55 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	Mon Dec 29 03:49:33 2008 +0000
+++ b/finch/gntdebug.c	Sun Jan 04 20:10:55 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	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/core.c	Sun Jan 04 20:10:55 2009 +0000
@@ -137,7 +137,7 @@
 	 * subsystem right away too.
 	 */
 	purple_plugins_init();
-	
+
 	/* Initialize all static protocols. */
 	static_proto_init();
 
@@ -213,12 +213,12 @@
 	purple_accounts_uninit();
 	purple_savedstatuses_uninit();
 	purple_status_uninit();
-	purple_prefs_uninit();
 	purple_sound_uninit();
 	purple_xfers_uninit();
 	purple_proxy_uninit();
 	purple_dnsquery_uninit();
 	purple_imgstore_uninit();
+	purple_network_uninit();
 
 	purple_debug_info("main", "Unloading all plugins\n");
 	purple_plugins_destroy_all();
@@ -243,6 +243,7 @@
 #ifdef _WIN32
 	wpurple_cleanup();
 #endif
+	purple_prefs_uninit();
 
 	_core = NULL;
 }
--- a/libpurple/network.c	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/network.c	Sun Jan 04 20:10:55 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/prefs.c	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/prefs.c	Sun Jan 04 20:10:55 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 {
@@ -711,7 +714,8 @@
 
 	name = pref_full_name(pref);
 
-	purple_debug_info("prefs", "removing pref %s\n", name);
+	if (prefs_loaded)
+		purple_debug_info("prefs", "removing pref %s\n", name);
 
 	g_hash_table_remove(prefs_hash, name);
 	g_free(name);
@@ -1451,5 +1455,9 @@
 		sync_prefs();
 	}
 
-	purple_prefs_disconnect_by_handle(purple_prefs_get_handle());
+	prefs_loaded = FALSE;
+	purple_prefs_destroy();
+	g_hash_table_destroy(prefs_hash);
+	prefs_hash = NULL;
+
 }
--- a/libpurple/protocols/bonjour/mdns_win32.c	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Sun Jan 04 20:10:55 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	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sun Jan 04 20:10:55 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	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/protocols/jabber/message.c	Sun Jan 04 20:10:55 2009 +0000
@@ -290,7 +290,7 @@
 	PurpleBuddy *buddy;
 	PurpleAccount *account;
 	PurpleConversation *c;
-	char *username, *str;
+	char *username;
 
 	/* Delayed buzz MUST NOT be accepted */
 	if(jm->delayed)
@@ -314,7 +314,6 @@
 	purple_prpl_got_attention(jm->js->gc, username, 0);
 
 	g_free(username);
-	g_free(str);
 }
 
 /* used internally by the functions below */
--- a/libpurple/protocols/myspace/myspace.c	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Sun Jan 04 20:10:55 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	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/protocols/myspace/user.c	Sun Jan 04 20:10:55 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	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/protocols/myspace/user.h	Sun Jan 04 20:10:55 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/qq/ChangeLog	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Sun Jan 04 20:10:55 2009 +0000
@@ -1,3 +1,9 @@
+2008.12.28 - flos <lonicerae(at)gmail.com>
+	* Fixes #7908
+
+2008.12.28 - flos <lonicerae(at)gmail.com>
+	* References #7908
+
 2008.12.25 - flos <lonicerae(at)gmail.com>
 	* References #7821
 	* Updated authors
--- a/libpurple/protocols/qq/qq_network.c	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Sun Jan 04 20:10:55 2009 +0000
@@ -174,14 +174,16 @@
  *  Please conside tcp_pending and udp_pending */
 gboolean qq_connect_later(gpointer data)
 {
-	PurpleConnection *gc = (PurpleConnection *) data;
-	qq_data *qd;
-	char *server;
+	PurpleConnection *gc;
+	char *tmp_server;
 	int port;
 	gchar **segments;
+	qq_data *qd;
 
+	gc = (PurpleConnection *) data;
 	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE);
 	qd = (qq_data *) gc->proto_data;
+	tmp_server = NULL;
 
 	if (qd->check_watcher > 0) {
 		purple_timeout_remove(qd->check_watcher);
@@ -191,9 +193,11 @@
 
 	if (qd->redirect_ip.s_addr != 0) {
 		/* redirect to new server */
-		server = g_strdup_printf("%s:%d", inet_ntoa(qd->redirect_ip), qd->redirect_port);
-		qd->servers = g_list_append(qd->servers, server);
-		qd->curr_server = server;
+		tmp_server = g_strdup_printf("%s:%d", inet_ntoa(qd->redirect_ip), qd->redirect_port);
+		qd->servers = g_list_append(qd->servers, tmp_server);
+
+		qd->curr_server = tmp_server;
+		tmp_server = NULL;
 
 		qd->redirect_ip.s_addr = 0;
 		qd->redirect_port = 0;
@@ -211,21 +215,30 @@
 	}
 
 	segments = g_strsplit_set(qd->curr_server, ":", 0);
-	server = g_strdup(segments[0]);
-	port = atoi(segments[1]);
-	if (port <= 0) {
-		purple_debug_info("QQ", "Port not define in %s\n", qd->curr_server);
+	tmp_server = g_strdup(segments[0]);
+	if (NULL != segments[1]) {
+		port = atoi(segments[1]);
+		if (port <= 0) {
+			purple_debug_info("QQ", "Port not define in %s, use default.\n", qd->curr_server);
+			port = QQ_DEFAULT_PORT;
+		}
+	} else {
+		purple_debug_info("QQ", "Error splitting server string: %s, setting port to default.\n", qd->curr_server);
 		port = QQ_DEFAULT_PORT;
 	}
+
 	g_strfreev(segments);
 
 	qd->connect_retry--;
-	if ( !connect_to_server(gc, server, port) ) {
+	if ( !connect_to_server(gc, tmp_server, port) ) {
 			purple_connection_error_reason(gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Unable to connect."));
 	}
 
+	g_free(tmp_server);
+	tmp_server = NULL;
+
 	qd->check_watcher = purple_timeout_add_seconds(QQ_CONNECT_CHECK, connect_check, gc);
 	return FALSE;	/* timeout callback stops */
 }
--- a/libpurple/protocols/simple/simple.c	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/protocols/simple/simple.c	Sun Jan 04 20:10:55 2009 +0000
@@ -1942,8 +1942,7 @@
 	if (userserver[1] == NULL || userserver[1][0] == '\0') {
 		purple_connection_error_reason(gc,
 			PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
-			_("Unable to connect to server. Please enter the "
-			  "address of the server you wish to connect to."));
+			_("SIP connect server not specified"));
 		return;
 	}
 
--- a/libpurple/protocols/yahoo/yahoo.c	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Sun Jan 04 20:10:55 2009 +0000
@@ -4167,8 +4167,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	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/proxy.c	Sun Jan 04 20:10:55 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	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/purple-url-handler	Sun Jan 04 20:10:55 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	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/value.c	Sun Jan 04 20:10:55 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	Mon Dec 29 03:49:33 2008 +0000
+++ b/libpurple/win32/libc_interface.c	Sun Jan 04 20:10:55 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	Mon Dec 29 03:49:33 2008 +0000
+++ b/pidgin/gtkaccount.c	Sun Jan 04 20:10:55 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	Mon Dec 29 03:49:33 2008 +0000
+++ b/pidgin/gtkblist.c	Sun Jan 04 20:10:55 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;
--- a/pidgin/gtkdialogs.c	Mon Dec 29 03:49:33 2008 +0000
+++ b/pidgin/gtkdialogs.c	Sun Jan 04 20:10:55 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},
--- a/po/de.po	Mon Dec 29 03:49:33 2008 +0000
+++ b/po/de.po	Sun Jan 04 20:10:55 2009 +0000
@@ -11,15 +11,15 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-12-18 23:08+0100\n"
-"PO-Revision-Date: 2008-12-18 20:11+0100\n"
-"Last-Translator: Bjoern Voigt <bjoern@cs.tu-berlin.de>\n"
-"Language-Team: Deutsch <de@li.org>\n"
+"POT-Creation-Date: 2009-01-04 12:29+0100\n"
+"PO-Revision-Date: 2009-01-04 12:29+0100\n"
+"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
+"Language-Team: German <de@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: KBabel 1.11.4\n"
+"X-Generator: Lokalize 0.2\n"
 
 #. Translators may want to transliterate the name.
 #. It is not to be translated.
@@ -4168,6 +4168,9 @@
 msgid "Re-initializing Stream"
 msgstr "Initialisiere Stream nochmal"
 
+msgid "Server doesn't support blocking"
+msgstr "Server unterstützt kein Blockieren"
+
 msgid "Not Authorized"
 msgstr "Nicht autorisiert"
 
@@ -9263,6 +9266,9 @@
 msgid "SIP usernames may not contain whitespaces or @ symbols"
 msgstr "SIP-Benutzernamen dürfen keine Leerzeichen oder @-Symbole enthalten"
 
+msgid "SIP connect server not specified"
+msgstr "SIP-Verbindungsserver nicht angegeben"
+
 #. *< type
 #. *< ui_requirement
 #. *< flags