changeset 27766:056fb36a5770

propagate from branch 'im.pidgin.pidgin' (head 7a8f58fc7611c332a05ed1c1b8417be24c9cf981) to branch 'im.pidgin.pidgin.yaz' (head 9937b0516237dfce73c86fd0407e37b0619e3037)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Wed, 20 Feb 2008 06:56:39 +0000
parents 42724bd41274 (diff) ed6de6a3604f (current diff)
children 76ff0ad87964
files libpurple/protocols/yahoo/yahoo.c libpurple/protocols/yahoo/yahoo_filexfer.c pidgin/gtkconv.c pidgin/gtkprefs.c
diffstat 28 files changed, 670 insertions(+), 168 deletions(-) [+]
line wrap: on
line diff
--- a/doc/account-signals.dox	Mon Feb 11 08:16:15 2008 +0000
+++ b/doc/account-signals.dox	Wed Feb 20 06:56:39 2008 +0000
@@ -148,7 +148,8 @@
 void (*account_error_changed)(PurpleAccount *account, const PurpleConnectionErrorInfo *old_error, const PurpleConnectionErrorInfo *current_error);
   @endsignalproto
   @signaldesc
-   Emitted when @a account's error changes.
+   Emitted when @a account's error changes.  You should not call
+   purple_account_clear_current_error() while this signal is being emitted.
   @param account   The account whose error has changed.
   @param old_error The account's previous error, or @c NULL if it had no
                    error.  After this signal is emitted, @a old_error is
--- a/finch/gntblist.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/finch/gntblist.c	Wed Feb 20 06:56:39 2008 +0000
@@ -601,10 +601,10 @@
 	field = purple_request_field_string_new("screenname", _("Screen Name"), username, FALSE);
 	purple_request_field_group_add_field(group, field);
 
-	field = purple_request_field_string_new("alias", _("Alias"), alias, FALSE);
+	field = purple_request_field_string_new("alias", _("Alias (optional)"), alias, FALSE);
 	purple_request_field_group_add_field(group, field);
 
-	field = purple_request_field_string_new("group", _("Group"), grp, FALSE);
+	field = purple_request_field_string_new("group", _("Add in group"), grp, FALSE);
 	purple_request_field_group_add_field(group, field);
 	purple_request_field_set_type_hint(field, "group");
 
@@ -1408,7 +1408,6 @@
 	char *primary;
 	const char *name, *sec = NULL;
 
-	/* XXX: could be a contact */
 	if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		PurpleContact *c = (PurpleContact*)node;
 		name = purple_contact_get_alias(c);
@@ -2349,15 +2348,6 @@
 	return ret;
 }
 
-static gboolean
-blist_clicked(GntTree *tree, GntMouseEvent event, int x, int y, gpointer ggblist)
-{
-	if (event == GNT_RIGHT_MOUSE_DOWN) {
-		draw_context_menu(ggblist);
-	}
-	return FALSE;
-}
-
 static void
 plugin_action(GntMenuItem *item, gpointer data)
 {
@@ -2940,7 +2930,6 @@
 	g_signal_connect(G_OBJECT(ggblist->tree), "key_pressed", G_CALLBACK(key_pressed), ggblist);
 	g_signal_connect(G_OBJECT(ggblist->tree), "context-menu", G_CALLBACK(context_menu), ggblist);
 	g_signal_connect(G_OBJECT(ggblist->tree), "collapse-toggled", G_CALLBACK(group_collapsed), NULL);
-	g_signal_connect_after(G_OBJECT(ggblist->tree), "clicked", G_CALLBACK(blist_clicked), ggblist);
 	g_signal_connect(G_OBJECT(ggblist->tree), "activate", G_CALLBACK(selection_activate), ggblist);
 	g_signal_connect_data(G_OBJECT(ggblist->tree), "gained-focus", G_CALLBACK(draw_tooltip),
 				ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
--- a/finch/gntconv.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/finch/gntconv.c	Wed Feb 20 06:56:39 2008 +0000
@@ -805,6 +805,10 @@
 
 	g_return_if_fail(ggconv != NULL);
 
+	if (flags & PURPLE_MESSAGE_SYSTEM) {
+		flags &= ~(PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV);
+	}
+
 	if (ggconv->active_conv != conv) {
 		if (flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV))
 			finch_conversation_set_active(conv);
@@ -837,7 +841,11 @@
 
 		if (purple_message_meify((char*)message, -1)) {
 			name = g_strdup_printf("*** %s", who);
-			msgflags = gnt_color_pair(color_message_action);
+			if (!(flags & PURPLE_MESSAGE_SEND) &&
+					(flags & PURPLE_MESSAGE_NICK))
+				msgflags = gnt_color_pair(color_message_highlight);
+			else
+				msgflags = gnt_color_pair(color_message_action);
 			me = TRUE;
 		} else {
 			name =  g_strdup_printf("%s", who);
--- a/finch/libgnt/gntbutton.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/finch/libgnt/gntbutton.c	Wed Feb 20 06:56:39 2008 +0000
@@ -79,7 +79,8 @@
 static gboolean
 gnt_button_key_pressed(GntWidget *widget, const char *key)
 {
-	if (strcmp(key, GNT_KEY_ENTER) == 0)
+	if (strcmp(key, GNT_KEY_ENTER) == 0 ||
+			strcmp(key, SAFE(cursor_down)) == 0)
 	{
 		gnt_widget_activate(widget);
 		return TRUE;
--- a/finch/libgnt/gntcombobox.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/finch/libgnt/gntcombobox.c	Wed Feb 20 06:56:39 2008 +0000
@@ -155,6 +155,7 @@
 			{
 				case '\r':
 				case '\t':
+				case '\n':
 					hide_popup(box, TRUE);
 					return TRUE;
 				case 27:
--- a/finch/libgnt/gntentry.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/finch/libgnt/gntentry.c	Wed Feb 20 06:56:39 2008 +0000
@@ -713,7 +713,7 @@
 		return FALSE;
 	}
 
-	if ((text[0] == '\r' || text[0] == ' ') && entry->ddown)
+	if ((text[0] == '\r' || text[0] == ' ' || text[0] == '\n') && entry->ddown)
 	{
 		char *text = g_strdup(gnt_tree_get_selection_data(GNT_TREE(entry->ddown)));
 		destroy_suggest(entry);
@@ -782,7 +782,7 @@
 		return TRUE;
 	}
 
-	if (text[0] == '\r') {
+	if (text[0] == '\r' || text[0] == '\n') {
 		gnt_widget_activate(widget);
 		return TRUE;
 	}
--- a/finch/libgnt/gntfilesel.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/finch/libgnt/gntfilesel.c	Wed Feb 20 06:56:39 2008 +0000
@@ -342,7 +342,7 @@
 static gboolean
 dir_key_pressed(GntTree *tree, const char *key, GntFileSel *sel)
 {
-	if (strcmp(key, "\r") == 0) {
+	if (strcmp(key, "\r") == 0 || strcmp(key, "\n") == 0) {
 		char *str = g_strdup(gnt_tree_get_selection_data(tree));
 		char *path, *dir;
 
@@ -376,7 +376,7 @@
 	struct stat st;
 	int glob_ret;
 #endif
-	if (strcmp(key, "\r"))
+	if (strcmp(key, "\r") && strcmp(key, "\n"))
 		return FALSE;
 
 	str = (char*)gnt_entry_get_text(GNT_ENTRY(sel->location));
--- a/finch/libgnt/gntmain.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/finch/libgnt/gntmain.c	Wed Feb 20 06:56:39 2008 +0000
@@ -221,7 +221,7 @@
 io_invoke(GIOChannel *source, GIOCondition cond, gpointer null)
 {
 	char keys[256];
-	int rd;
+	gssize rd;
 	char *k;
 	char *cvrt = NULL;
 
--- a/finch/libgnt/gntmenu.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/finch/libgnt/gntmenu.c	Wed Feb 20 06:56:39 2008 +0000
@@ -46,6 +46,7 @@
 static void (*org_map)(GntWidget *wid);
 static void (*org_size_request)(GntWidget *wid);
 static gboolean (*org_key_pressed)(GntWidget *w, const char *t);
+static gboolean (*org_clicked)(GntWidget *w, GntMouseEvent event, int x, int y);
 
 static void menuitem_activate(GntMenu *menu, GntMenuItem *item);
 
@@ -390,6 +391,16 @@
 		menu->parentmenu->submenu = NULL;
 }
 
+static gboolean
+gnt_menu_clicked(GntWidget *widget, GntMouseEvent event, int x, int y)
+{
+	if (!org_clicked || !org_clicked(widget, event, x, y) ||
+			!GNT_MENU(widget)->type == GNT_MENU_TOPLEVEL)
+			return FALSE;
+	gnt_widget_activate(widget);
+	return TRUE;
+}
+
 static void
 gnt_menu_class_init(GntMenuClass *klass)
 {
@@ -401,6 +412,7 @@
 	org_draw = wid_class->draw;
 	org_key_pressed = wid_class->key_pressed;
 	org_size_request = wid_class->size_request;
+	org_clicked = wid_class->clicked;
 
 	wid_class->destroy = gnt_menu_destroy;
 	wid_class->draw = gnt_menu_draw;
@@ -409,6 +421,7 @@
 	wid_class->key_pressed = gnt_menu_key_pressed;
 	wid_class->activate = gnt_menu_activate;
 	wid_class->hide = gnt_menu_hide;
+	wid_class->clicked = gnt_menu_clicked;
 
 	parent_class->toggled = gnt_menu_toggled;
 
--- a/finch/libgnt/gnttree.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/finch/libgnt/gnttree.c	Wed Feb 20 06:56:39 2008 +0000
@@ -798,7 +798,7 @@
 	GntTree *tree = GNT_TREE(widget);
 	GntTreeRow *old = tree->current;
 
-	if (text[0] == '\r') {
+	if (text[0] == '\r' || text[0] == '\n') {
 		end_search(tree);
 		gnt_widget_activate(widget);
 	} else if (tree->priv->search) {
--- a/finch/libgnt/gntwidget.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/finch/libgnt/gntwidget.c	Wed Feb 20 06:56:39 2008 +0000
@@ -407,6 +407,8 @@
 {
 	gboolean ret;
 	g_signal_emit(widget, signals[SIG_CLICKED], 0, event, x, y, &ret);
+	if (!ret && event == GNT_RIGHT_MOUSE_DOWN)
+		ret = gnt_bindable_perform_action_named(GNT_BINDABLE(widget), "context-menu", NULL);
 	return ret;
 }
 
--- a/libpurple/protocols/jabber/buddy.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Wed Feb 20 06:56:39 2008 +0000
@@ -392,6 +392,7 @@
  */
 void jabber_set_info(PurpleConnection *gc, const char *info)
 {
+	PurpleStoredImage *img;
 	JabberIq *iq;
 	JabberStream *js = gc->proto_data;
 	xmlnode *vc_node;
@@ -410,57 +411,58 @@
 	 */
 	vc_node = info ? xmlnode_from_str(info, -1) : NULL;
 
-	if(!vc_node) {
-		vc_node = xmlnode_new("vCard");
-		for(tag_attr = vcard_tag_attr_list; tag_attr->attr != NULL; ++tag_attr)
-			xmlnode_set_attrib(vc_node, tag_attr->attr, tag_attr->value);
+	if (vc_node && (!vc_node->name ||
+			g_ascii_strncasecmp(vc_node->name, "vCard", 5))) {
+		xmlnode_free(vc_node);
+		vc_node = NULL;
 	}
 
-	if (vc_node->name &&
-			!g_ascii_strncasecmp(vc_node->name, "vCard", 5)) {
-		PurpleStoredImage *img;
-
-		if ((img = purple_buddy_icons_find_account_icon(gc->account))) {
-			gconstpointer avatar_data;
-			gsize avatar_len;
-			xmlnode *photo, *binval, *type;
-			gchar *enc;
-			int i;
-			unsigned char hashval[20];
-			char *p, hash[41];
+	if ((img = purple_buddy_icons_find_account_icon(gc->account))) {
+		gconstpointer avatar_data;
+		gsize avatar_len;
+		xmlnode *photo, *binval, *type;
+		gchar *enc;
+		int i;
+		unsigned char hashval[20];
+		char *p, hash[41];
 
-			avatar_data = purple_imgstore_get_data(img);
-			avatar_len = purple_imgstore_get_size(img);
-			/* have to get rid of the old PHOTO if it exists */
-			if((photo = xmlnode_get_child(vc_node, "PHOTO"))) {
-				xmlnode_free(photo);
-			}
-			photo = xmlnode_new_child(vc_node, "PHOTO");
-			type = xmlnode_new_child(photo, "TYPE");
-			xmlnode_insert_data(type, "image/png", -1);
-			binval = xmlnode_new_child(photo, "BINVAL");
-			enc = purple_base64_encode(avatar_data, avatar_len);
-
-			purple_cipher_digest_region("sha1", avatar_data,
-									  avatar_len, sizeof(hashval),
-									  hashval, NULL);
-
-			purple_imgstore_unref(img);
-
-			p = hash;
-			for(i=0; i<20; i++, p+=2)
-				snprintf(p, 3, "%02x", hashval[i]);
-			js->avatar_hash = g_strdup(hash);
-
-			xmlnode_insert_data(binval, enc, -1);
-			g_free(enc);
+		if(!vc_node) {
+			vc_node = xmlnode_new("vCard");
+			for(tag_attr = vcard_tag_attr_list; tag_attr->attr != NULL; ++tag_attr)
+				xmlnode_set_attrib(vc_node, tag_attr->attr, tag_attr->value);
 		}
 
+		avatar_data = purple_imgstore_get_data(img);
+		avatar_len = purple_imgstore_get_size(img);
+		/* have to get rid of the old PHOTO if it exists */
+		if((photo = xmlnode_get_child(vc_node, "PHOTO"))) {
+			xmlnode_free(photo);
+		}
+		photo = xmlnode_new_child(vc_node, "PHOTO");
+		type = xmlnode_new_child(photo, "TYPE");
+		xmlnode_insert_data(type, "image/png", -1);
+		binval = xmlnode_new_child(photo, "BINVAL");
+		enc = purple_base64_encode(avatar_data, avatar_len);
+
+		purple_cipher_digest_region("sha1", avatar_data,
+								  avatar_len, sizeof(hashval),
+								  hashval, NULL);
+
+		purple_imgstore_unref(img);
+
+		p = hash;
+		for(i=0; i<20; i++, p+=2)
+			snprintf(p, 3, "%02x", hashval[i]);
+		js->avatar_hash = g_strdup(hash);
+
+		xmlnode_insert_data(binval, enc, -1);
+		g_free(enc);
+	}
+
+	if (vc_node != NULL) {
 		iq = jabber_iq_new(js, JABBER_IQ_SET);
 		xmlnode_insert_child(iq->node, vc_node);
 		jabber_iq_send(iq);
-	} else {
-		xmlnode_free(vc_node);
 	}
 }
 
--- a/libpurple/protocols/myspace/myspace.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Wed Feb 20 06:56:39 2008 +0000
@@ -73,7 +73,7 @@
 static gboolean msim_check_alive(gpointer data);
 #endif
 
-static gboolean msim_we_are_logged_on(MsimSession *session, MsimMessage *msg);
+static gboolean msim_is_username_set(MsimSession *session, MsimMessage *msg);
 
 static gboolean msim_process(MsimSession *session, MsimMessage *msg);
 
@@ -1546,14 +1546,14 @@
 }
 #endif
 
-/** Called when the session key arrives. */
+/** Called when the session key arrives to check whether the user
+ * has a username, and set one if desired. */
 static gboolean
-msim_we_are_logged_on(MsimSession *session, MsimMessage *msg)
+msim_is_username_set(MsimSession *session, MsimMessage *msg) 
 {
-	MsimMessage *body;
-
 	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 	g_return_val_if_fail(msg != NULL, FALSE);
+	g_return_val_if_fail(session->gc != NULL, FALSE);
 
 	session->sesskey = msim_msg_get_integer(msg, "sesskey");
 	purple_debug_info("msim", "SESSKEY=<%d>\n", session->sesskey);
@@ -1578,8 +1578,32 @@
 	 * address and not username. Will be freed in msim_session_destroy(). */
 	session->username = msim_msg_get_string(msg, "uniquenick");
 
-	/* Set display name to username (otherwise will show email address) */
-	purple_connection_set_display_name(session->gc, session->username);
+	/* If user lacks a username, help them get one. */
+	if (msim_msg_get_integer(msg, "uniquenick") == session->userid) {
+		purple_debug_info("msim_is_username_set", "no username is set\n");
+		purple_request_yes_no(session->gc,
+			_("MySpaceIM - No Username Set"),
+			_("You appear to have no MySpace username."),
+			_("Would you like to set one now? (Note: THIS CANNOT BE CHANGED!)"),
+			0,
+			session->account,
+			NULL,
+			NULL,
+			session->gc, 
+			G_CALLBACK(msim_set_username_cb), 
+			G_CALLBACK(msim_do_not_set_username_cb));
+		purple_debug_info("msim_is_username_set","'username not set' alert prompted\n");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+/** Called after username is set, if necessary and we're open for business. */
+gboolean msim_we_are_logged_on(MsimSession *session) 
+{
+	MsimMessage *body;
+
+	g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
 
 	/* The session is now set up, ready to be connected. This emits the
 	 * signedOn signal, so clients can now do anything with msimprpl, and
@@ -1587,20 +1611,8 @@
 	purple_connection_update_progress(session->gc, _("Connected"), 3, 4);
 	purple_connection_set_state(session->gc, PURPLE_CONNECTED);
 
-
-	/* Additional post-connect operations */
-
-
-	if (msim_msg_get_integer(msg, "uniquenick") == session->userid) {
-		purple_debug_info("msim_we_are_logged_on", "TODO: pick username\n");
-		/* No username is set. */
-		purple_notify_error(session->account, 
-				_("No username set"),
-				_("Please go to http://editprofile.myspace.com/index.cfm?fuseaction=profile.username and choose a username and try to login again."), NULL);
-		purple_connection_error_reason (session->gc,
-			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("No username set"));
-		return FALSE;
-	}
+	/* Set display name to username (otherwise will show email address) */
+	purple_connection_set_display_name(session->gc, session->username);
 
 	body = msim_msg_new(
 			"UserID", MSIM_TYPE_INTEGER, session->userid,
@@ -1687,7 +1699,14 @@
 	if (msim_msg_get_integer(msg, "lc") == 1) {
 		return msim_login_challenge(session, msg);
 	} else if (msim_msg_get_integer(msg, "lc") == 2) {
-		return msim_we_are_logged_on(session, msg);
+		/* return msim_we_are_logged_on(session, msg); */
+		if (msim_is_username_set(session, msg)) {
+			return msim_we_are_logged_on(session);
+		} else {
+			/* No username is set... We'll wait for the callbacks to do their work */
+			/* When they're all done, the last one will call msim_we_are_logged_on() and pick up where we left off */
+			return FALSE;
+		}
 	} else if (msim_msg_get(msg, "bm"))  {
 		return msim_incoming_bm(session, msg);
 	} else if (msim_msg_get(msg, "rid")) {
@@ -2797,7 +2816,14 @@
 	 * the documentation claims). */
 	group_name = msim_msg_get_string(contact_info, "GroupName");
 	if (group_name) {
-		group = purple_group_new(group_name);
+		group = purple_find_group(group_name);
+		if (!group) {
+			group = purple_group_new(group_name);
+			/* Add group to beginning. See #2752. */
+			purple_blist_add_group(group, NULL);
+
+		}
+
 		purple_debug_info("msim_add_contact_from_server_cb",
 				"adding to GroupName: %s\n", group_name);
 		g_free(group_name);
@@ -2813,9 +2839,6 @@
 		buddy = purple_buddy_new(session->account, username, NULL);
 	}
 
-	/* Add group to beginning. See #2752. */
-	purple_blist_add_group(group, NULL);
-
 	/* TODO: use 'Position' in contact_info to take into account where buddy is */
 	purple_blist_add_buddy(buddy, NULL, group, NULL /* insertion point */);
 
--- a/libpurple/protocols/myspace/myspace.h	Mon Feb 11 08:16:15 2008 +0000
+++ b/libpurple/protocols/myspace/myspace.h	Wed Feb 20 06:56:39 2008 +0000
@@ -45,6 +45,7 @@
 #include "cipher.h"     /* for SHA-1 */
 #include "util.h"       /* for base64 */
 #include "debug.h"      /* for purple_debug_info */
+#include "request.h"    /* For dialogs used in setting the username */
 #include "xmlnode.h"
 #include "core.h"
 
@@ -88,6 +89,9 @@
  * warn user that it may be too long. */
 #define MSIM_MAX_PASSWORD_LENGTH    10
 
+/* Maximum length of usernames, when setting. */
+#define MSIM_MAX_USERNAME_LENGTH    25
+
 /* Build version of MySpaceIM to report to servers (1.0.xxx.0) */
 #define MSIM_CLIENT_VERSION         697
 
@@ -108,6 +112,7 @@
 
 /* Time between keepalives (seconds) - if no data within this time, is dead. */
 #define MSIM_KEEPALIVE_INTERVAL     (3 * 60)
+/*#define MSIM_USE_KEEPALIVE*/
 
 /* Time to check if alive (milliseconds) */
 #define MSIM_KEEPALIVE_INTERVAL_CHECK   (30 * 1000)
@@ -221,6 +226,8 @@
 
 gboolean msim_send_bm(MsimSession *session, const gchar *who, const gchar *text, int type);
 
+gboolean msim_we_are_logged_on(MsimSession *session);
+
 
 void msim_unrecognized(MsimSession *session, MsimMessage *msg, gchar *note);
 guint msim_new_reply_callback(MsimSession *session, MSIM_USER_LOOKUP_CB cb, gpointer data);
--- a/libpurple/protocols/myspace/persist.h	Mon Feb 11 08:16:15 2008 +0000
+++ b/libpurple/protocols/myspace/persist.h	Wed Feb 20 06:56:39 2008 +0000
@@ -73,7 +73,8 @@
 /** Messages to Change/send information */
 MSIM_PERSIST_DSN_LID(MC_USER_PREFERENCES,          1, 10)
 MSIM_PERSIST_DSN_LID(MC_CONTACT_INFO,              0, 9)
-MSIM_PERSIST_DSN_LID(MC_IMPORT_ALL_FRIENDS,        14, 21)
+MSIM_PERSIST_DSN_LID(MC_SET_USERNAME,              9, 14)
+MSIM_PERSIST_DSN_LID(MC_IMPORT_ALL_FRIENDS,       14, 21)
 MSIM_PERSIST_DSN_LID(MC_INVITE,                   16, 25)
 
 /** Messages to Delete information */
--- a/libpurple/protocols/myspace/user.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/libpurple/protocols/myspace/user.c	Wed Feb 20 06:56:39 2008 +0000
@@ -24,6 +24,15 @@
 static void msim_downloaded_buddy_icon(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text,
 		gsize len, const gchar *error_message);
 
+/* Callbacks for setting the username bit */
+static void msim_check_username_availability_cb(PurpleConnection *gc, const char *value);
+static void msim_username_is_available_cb(MsimSession *session, MsimMessage *userinfo, gpointer data);
+static void msim_set_username_confirmed_cb(PurpleConnection *gc);
+static void msim_username_is_set_cb(MsimSession *session, MsimMessage *userinfo, gpointer data);
+static void msim_set_username(MsimSession *session, const gchar *username,
+		MSIM_USER_LOOKUP_CB cb, gpointer data);
+static char *msim_username_to_set;
+
 /** Format the "now playing" indicator, showing the artist and song.
  * @return Return a new string (must be g_free()'d), or NULL.
  */
@@ -487,6 +496,19 @@
 	return strspn(user, "0123456789") == strlen(user);
 }
 
+/** Return whether a given username is syntactically valid. 
+ * Note: does not actually check that the user exists. */
+gboolean
+msim_is_valid_username(const gchar *user)
+{
+	return !msim_is_userid(user) &&  /* Not all numeric */
+		strlen(user) <= MSIM_MAX_USERNAME_LENGTH
+		&& strspn(user, "0123456789"
+			"abcdefghijklmnopqrstuvwxyz"
+			"_"
+			"ABCDEFGHIJKLMNOPQRSTUVWXYZ") == strlen(user);
+}
+
 /**
  * Check if a string is an email address (contains an @).
  *
@@ -537,4 +559,280 @@
 			user->image_url);		/* checksum */
 }
 
+/*** 
+ * If they hit cancel or no at any point in the Setting Username process, we come here.              *
+ * Currently.. We're safe letting them get by without setting it.. Unless we hear otherwise..        *
+ * So for now, give them a menu.. If this becomes an issue with the Official client.. boot them here */
+void msim_do_not_set_username_cb(PurpleConnection *gc) {
+	purple_debug_info("msim", "Don't set username");
 
+	/* Protocol won't log in now without a username set.. Disconnect */
+	purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("No username set"));
+}
+ 
+/** They've decided to set a username! Yay! */
+void msim_set_username_cb(PurpleConnection *gc) {
+	g_return_if_fail(gc != NULL);
+	purple_debug_info("msim","Set username\n");
+	purple_request_input(gc, _("MySpaceIM - Please Set a Username"),
+			_("Please enter a username to check its availability:"),
+			NULL,
+			"", FALSE, FALSE, NULL,
+			_("OK"), G_CALLBACK(msim_check_username_availability_cb),
+			_("Cancel"), G_CALLBACK(msim_do_not_set_username_cb),
+			purple_connection_get_account(gc),
+			NULL,
+			NULL,
+			gc);
+}
+
+/** Once they've submitted their desired new username, 
+ * check if it is available here. */
+static void msim_check_username_availability_cb(PurpleConnection *gc, const char *username_to_check) 
+{
+	MsimMessage *user_msg;
+	MsimSession *session;
+
+	g_return_if_fail(gc != NULL);
+
+	session = (MsimSession *)gc->proto_data;
+
+	g_return_if_fail(MSIM_SESSION_VALID(session));
+
+	purple_debug_info("msim_check_username_availability_cb", "Checking username: %s\n", username_to_check);
+	
+	user_msg = msim_msg_new(
+			"user", MSIM_TYPE_STRING, g_strdup(username_to_check),
+			NULL);
+
+	/* 25 characters: letters, numbers, underscores */
+	/* TODO: VERIFY ABOVE */
+
+	/* \persist\1\sesskey\288500516\cmd\1\dsn\5\uid\204084363\lid\7\rid\367\body\UserName=Jaywalker\final\ */
+	/* Official client uses a standard lookup... So do we! */
+	msim_lookup_user(session, username_to_check, msim_username_is_available_cb, user_msg);
+}
+
+/** This is where we do a bit more than merely prompt the user. 
+ * Now we have some real data to tell us the state of their requested username 
+ * \persistr\\cmd\257\dsn\5\uid\204084363\lid\7\rid\367\body\UserName=TheAlbinoRhino1\final\ */
+static void msim_username_is_available_cb(MsimSession *session, MsimMessage *userinfo, gpointer data) 
+{
+	MsimMessage *msg;
+	gchar *username;
+	MsimMessage *body;
+	gint userid;
+
+	purple_debug_info("msim_username_is_available_cb", "Look up username callback made\n");
+	
+	msg = (MsimMessage *)data;
+	g_return_if_fail(MSIM_SESSION_VALID(session));
+	g_return_if_fail(msg != NULL);
+	
+	username = msim_msg_get_string(msg, "user");
+	body = msim_msg_get_dictionary(userinfo, "body");
+
+	if (!body) {
+		purple_debug_info("msim_username_is_available_cb", "No body for %s?!\n", username);
+		purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, 
+				"An error occured while trying to set the username.\n"
+				"Please try again, or visit http://editprofile.myspace.com/index.cfm?"
+				"fuseaction=profile.username to set your username.");
+		return;
+	}
+
+	userid = msim_msg_get_integer(body, "UserID");
+
+	purple_debug_info("msim_username_is_available_cb", "Returned username is %s and userid is %d\n", username, userid);
+	msim_msg_free(body);
+	msim_msg_free(msg);
+
+	/* The response for a free username will ONLY have the UserName in it.. 
+	 * thus making UserID return 0 when we msg_get_integer it */
+	if (userid == 0) {
+		/* This username is currently unused */
+		purple_debug_info("msim_username_is_available_cb", "Username available. Prompting to Confirm.\n");
+		msim_username_to_set = g_strdup(username);
+		g_free(username);
+		purple_request_yes_no(session->gc,
+			_("MySpaceIM - Username Available"),
+			_("This username is available. Would you like to set it?"),
+			_("ONCE SET, THIS CANNOT BE CHANGED!"),
+			0,
+			session->account,
+			NULL,
+			NULL,
+			session->gc, 
+			G_CALLBACK(msim_set_username_confirmed_cb), 
+			G_CALLBACK(msim_do_not_set_username_cb));
+	} else {
+		/* Looks like its in use or we have an invalid response */
+		purple_debug_info("msim_username_is_available_cb", "Username unavaiable. Prompting for new entry.\n");
+		purple_request_input(session->gc, _("MySpaceIM - Please Set a Username"),
+			_("This username is unavailable."),
+				_("Please try another username:"),
+				"", FALSE, FALSE, NULL,
+				_("OK"), G_CALLBACK(msim_check_username_availability_cb),
+				_("Cancel"), G_CALLBACK(msim_do_not_set_username_cb),
+				session->account,
+				NULL,
+				NULL,
+				session->gc);
+	}
+}
+
+/* They've confirmed that username that was available, Lets make the call to set it */
+static void msim_set_username_confirmed_cb(PurpleConnection *gc) 
+{
+	MsimMessage *user_msg;
+	MsimSession *session;
+
+	g_return_if_fail(gc != NULL);
+
+	session = (MsimSession *)gc->proto_data;
+
+	g_return_if_fail(MSIM_SESSION_VALID(session));
+
+
+	user_msg = msim_msg_new(
+			"user", MSIM_TYPE_STRING, g_strdup(msim_username_to_set),
+			NULL);
+
+	purple_debug_info("msim_set_username_confirmed_cb", "Setting username to %s\n", msim_username_to_set);
+	
+	/* Sets our username... keep your fingers crossed :) */
+	msim_set_username(session, msim_username_to_set, msim_username_is_set_cb, user_msg);
+	g_free(msim_username_to_set);
+}
+
+/**
+ * Asynchronously set new username, calling callback when receive result.
+ *
+ * @param session
+ * @param username The username we're setting for ourselves. Not freed.
+ * @param cb Callback, called with user information when available.
+ * @param data An arbitray data pointer passed to the callback.
+ */
+static void 
+msim_set_username(MsimSession *session, const gchar *username,
+		MSIM_USER_LOOKUP_CB cb, gpointer data)
+{
+	MsimMessage *body;
+	guint rid;
+
+	g_return_if_fail(MSIM_SESSION_VALID(session));
+	g_return_if_fail(username != NULL);
+	g_return_if_fail(cb != NULL);
+
+	purple_debug_info("msim", "msim_set_username: "
+			"Setting username %s\n", username);
+
+	msim_msg_dump("msim_set_username: data=%s\n", (MsimMessage *)data);
+
+	/* Setup callback. Response will be associated with request using 'rid'. */
+	rid = msim_new_reply_callback(session, cb, data);
+
+	/* TODO: I dont know if the ContactType is -/ALWAYS/- 1 */
+
+	body = msim_msg_new("UserName", MSIM_TYPE_STRING, g_strdup(username),NULL);
+/* \setinfo\\sesskey\469958979\info\Age=21.AvatarUrl=.BandName=.ContactType=1.DisplayName=Msim.Gender=M.ImageURL=http:/1/1x.myspace.com/1images/1no_pic.gif.LastLogin=128335268400000000.Location=US.ShowAvatar=False.SongName=.TotalFriends=1.UserName=msimprpl2\final\
+*/
+
+	/* Send request */
+	g_return_if_fail(msim_send(session,
+			 "setinfo", MSIM_TYPE_BOOLEAN, TRUE,
+			 "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
+			 "info", MSIM_TYPE_DICTIONARY, body,
+			 NULL));
+	body = msim_msg_new("UserName", MSIM_TYPE_STRING, g_strdup(username),NULL);
+	g_return_if_fail(msim_send(session,
+			 "persist", MSIM_TYPE_INTEGER, 1,
+			 "sesskey", MSIM_TYPE_INTEGER, session->sesskey,
+			 "cmd", MSIM_TYPE_INTEGER, MSIM_CMD_GET,
+			 "dsn", MSIM_TYPE_INTEGER, MG_MYSPACE_INFO_BY_STRING_DSN,
+			 "uid", MSIM_TYPE_INTEGER, session->userid,
+			 "lid", MSIM_TYPE_INTEGER, MG_MYSPACE_INFO_BY_STRING_LID,
+			 "rid", MSIM_TYPE_INTEGER, rid,
+			 "body", MSIM_TYPE_DICTIONARY, body,
+			 NULL));
+}
+
+/** Called after username is set. */
+static void msim_username_is_set_cb(MsimSession *session, MsimMessage *userinfo, gpointer data) 
+{
+	gchar *username, *errmsg;
+	MsimMessage *body;
+
+	guint rid;
+	gint cmd,dsn,uid,lid,code;
+	/* \persistr\\cmd\258\dsn\9\uid\204084363\lid\14\rid\369\body\UserName=TheAlbinoRhino1.Code=0\final\ */
+
+	purple_debug_info("msim","username_is_set made\n");
+	
+	g_return_if_fail(MSIM_SESSION_VALID(session));
+
+
+	msim_msg_dump("username_is_set message is: %s\n", userinfo);
+	cmd = msim_msg_get_integer(userinfo, "cmd");
+	dsn = msim_msg_get_integer(userinfo, "dsn");
+	uid = msim_msg_get_integer(userinfo, "uid");
+	lid = msim_msg_get_integer(userinfo, "lid");
+	body = msim_msg_get_dictionary(userinfo, "body");
+	errmsg = g_strdup("An error occured while trying to set the username.\n"
+			"Please try again, or visit http://editprofile.myspace.com/index.cfm?"
+			"fuseaction=profile.username to set your username.");
+	
+	if (!body) {
+		purple_debug_info("msim_username_is_set_cb", "No body");
+		/* Error: No body! */
+		purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, errmsg);
+	}
+	username = msim_msg_get_string(body, "UserName");
+	code = msim_msg_get_integer(body,"Code");
+
+	msim_msg_free(body);
+
+	purple_debug_info("msim_username_is_set_cb", 
+			"cmd = %d, dsn = %d, lid = %d, code = %d, username = %s\n", 
+			cmd, dsn, lid, code, username);
+
+	if (cmd == (MSIM_CMD_BIT_REPLY | MSIM_CMD_PUT) 
+			&& dsn == MC_SET_USERNAME_DSN 
+			&& lid == MC_SET_USERNAME_LID) {
+		purple_debug_info("msim_username_is_set_cb", "Proper cmd,dsn,lid for username_is_set!\n");
+		purple_debug_info("msim_username_is_set_cb", "Username Set with return code %d\n",code);
+		if (code == 0) {
+			/* Good! */
+			session->username = username;
+			msim_we_are_logged_on(session);
+		} else {
+			purple_debug_info("msim_username_is_set", "code is %d",code);
+			/* TODO: what to do here? */
+		}
+	} else if (cmd == (MSIM_CMD_BIT_REPLY | MSIM_CMD_GET) 
+			&& dsn == MG_MYSPACE_INFO_BY_STRING_DSN 
+			&& lid == MG_MYSPACE_INFO_BY_STRING_LID) {
+		/* Not quite done... ONE MORE STEP :) */
+		rid = msim_new_reply_callback(session, msim_username_is_set_cb, data);
+		body = msim_msg_new("UserName", MSIM_TYPE_STRING, g_strdup(username), NULL);
+		if (!msim_send(session, "persist", MSIM_TYPE_INTEGER, 1, 
+					"sesskey", MSIM_TYPE_INTEGER, session->sesskey,
+					"cmd", MSIM_TYPE_INTEGER, MSIM_CMD_PUT, 
+					"dsn", MSIM_TYPE_INTEGER, MC_SET_USERNAME_DSN, 
+					"uid", MSIM_TYPE_INTEGER, session->userid,
+					"lid", MSIM_TYPE_INTEGER, MC_SET_USERNAME_LID, 
+					"rid", MSIM_TYPE_INTEGER, rid, 
+					"body", MSIM_TYPE_DICTIONARY, body, 
+					NULL)) {
+			/* Error! */
+			/* Can't set... Disconnect */
+			purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, errmsg);
+		}
+			
+	} else {
+		/* Error! */
+		purple_debug_info("msim","username_is_set Error: Invalid cmd/dsn/lid combination");
+		purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, errmsg);
+	}
+	g_free(errmsg);
+}
--- a/libpurple/protocols/myspace/user.h	Mon Feb 11 08:16:15 2008 +0000
+++ b/libpurple/protocols/myspace/user.h	Wed Feb 20 06:56:39 2008 +0000
@@ -51,6 +51,9 @@
 gboolean msim_store_user_info(MsimSession *session, MsimMessage *msg, MsimUser *user);
 gboolean msim_is_userid(const gchar *user);
 gboolean msim_is_email(const gchar *user);
+gboolean msim_is_valid_username(const gchar *user);
 void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data);
+void msim_set_username_cb(PurpleConnection *gc);
+void msim_do_not_set_username_cb(PurpleConnection *gc);
 
 #endif /* !_MYSPACE_USER_H */
--- a/libpurple/protocols/yahoo/yahoo.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Wed Feb 20 06:56:39 2008 +0000
@@ -1477,13 +1477,24 @@
 	to_y64(result96, digest, 16);
 
 	pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP,	YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash(pack, "ssssss",
-					  0, name,
-					  6, result6,
-					  96, result96,
-					  1, name,
-					  244, YAHOO_CLIENT_VERSION_ID,
-					  135, YAHOO_CLIENT_VERSION);
+
+	if(yd->jp) {
+		yahoo_packet_hash(pack, "sssss",
+						  0, name,
+						  6, result6,
+						  96, result96,
+						  1, name,
+						  135, YAHOOJP_CLIENT_VERSION);
+	} else {
+		yahoo_packet_hash(pack, "ssssss",
+						  0, name,
+						  6, result6,
+						  96, result96,
+						  1, name,
+						  244, YAHOO_CLIENT_VERSION_ID,
+						  135, YAHOO_CLIENT_VERSION);
+	}
+
 	yahoo_packet_send_and_free(pack, yd);
 
 	g_free(hash_string_p);
@@ -1929,13 +1940,24 @@
 	}
 	purple_debug_info("yahoo", "yahoo status: %d\n", yd->current_status);
 	pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP,	yd->current_status, 0);
-	yahoo_packet_hash(pack, "ssssss",
-					  0, name,
-					  6, resp_6,
-					  96, resp_96,
-					  1, name,
-					  244, YAHOO_CLIENT_VERSION_ID,
-					  135, YAHOO_CLIENT_VERSION);
+
+	if(yd->jp) {
+		yahoo_packet_hash(pack, "sssss",
+						  0, name,
+						  6, resp_6,
+						  96, resp_96,
+						  1, name,
+						  135, YAHOOJP_CLIENT_VERSION);
+	} else {
+		yahoo_packet_hash(pack, "ssssss",
+						  0, name,
+						  6, resp_6,
+						  96, resp_96,
+						  1, name,
+						  244, YAHOO_CLIENT_VERSION_ID,
+						  135, YAHOO_CLIENT_VERSION);
+	}
+
 	if (yd->picture_checksum)
 		yahoo_packet_hash_int(pack, 192, yd->picture_checksum);
 
--- a/libpurple/protocols/yahoo/yahoo.h	Mon Feb 11 08:16:15 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo.h	Wed Feb 20 06:56:39 2008 +0000
@@ -46,7 +46,7 @@
 #define YAHOOJP_XFER_HOST "filetransfer.msg.yahoo.co.jp"
 #define YAHOOJP_WEBCAM_HOST "wc.yahoo.co.jp"
 /*not sure, must test:*/
-#define YAHOOJP_XFER_RELAY_HOST "relay.msg.yahoo.com" 
+#define YAHOOJP_XFER_RELAY_HOST "relay.msg.yahoo.co.jp" 
 #define YAHOOJP_XFER_RELAY_PORT 80
 #define YAHOOJP_ROOMLIST_URL "http://insider.msg.yahoo.co.jp/ycontent/"
 #define YAHOOJP_ROOMLIST_LOCALE "ja"
@@ -76,6 +76,15 @@
 
 #define YAHOO_CLIENT_VERSION_ID "2097087"
 #define YAHOO_CLIENT_VERSION "8.1.0.421"
+#define YAHOOJP_CLIENT_VERSION "6,0,0,1710"
+
+#if 0
+/* The following were observed with the Yahoo Japan client current as of January
+ * 2008, but appear not to work correctly for file transfer.  Here as reference */
+# define YAHOOJP_CLIENT_VERSION_ID "524223"
+# define YAHOOJP_CLIENT_VERSION "7,0,1,1"
+#endif
+
 
 /* Index into attention types list. */
 #define YAHOO_BUZZ 0
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.c	Wed Feb 20 06:56:39 2008 +0000
@@ -1038,18 +1038,19 @@
 void yahoo_send_file(PurpleConnection *gc, const char *who, const char *file)
 {
 	struct yahoo_xfer_data *xfer_data;
-	struct yahoo_data *yd;
+	struct yahoo_data *yd = gc->proto_data;
 	int ver = 0;
 	PurpleXfer *xfer = yahoo_new_xfer(gc, who);
 	YahooFriend *yf = yahoo_friend_find(gc, who);
 
-	/* To determine whether client uses ymsg 15 i.e. client is higher than YM 7 */
-	if(yf && yf->version_id > 500000)
-		ver=15; 
+	/* To determine if we should use yahoo p15 for transfer.  Check other user's
+	 * reported version, but if we're on Yahoo Japan, ignore it. */
+	if(yf && yf->version_id > 500000 && !yd->jp)
+		ver = 15; 
+
 	g_return_if_fail(xfer != NULL);
 
 	if(ver == 15) {
-		yd = gc->proto_data;
 		xfer_data = xfer->data;
 		xfer_data->status_15 = STARTED;
 		purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15);
@@ -1325,17 +1326,21 @@
 		if(!xfer)
 			return;
 		/*
-		*	In the file trans info packet tht we must reply with , we are supposed to mention the ip address...
+		*	In the file trans info packet that we must reply with, we are
+		*	supposed to mention the ip address...
 		*	purple connect does not give me a way of finding the ip address...
-		*	so, purple dnsquery is used... but retries, trying with next ip address etc. is not implemented..TODO
+		*	so, purple dnsquery is used... but retries, trying with next ip
+		*	address etc. is not implemented..TODO
 		*/
 		if (yd->jp)
 		{
-			purple_dnsquery_a(YAHOOJP_XFER_RELAY_HOST, YAHOOJP_XFER_RELAY_PORT, yahoo_xfer_dns_connected_15, xfer);
+			purple_dnsquery_a(YAHOOJP_XFER_RELAY_HOST, YAHOOJP_XFER_RELAY_PORT,
+							yahoo_xfer_dns_connected_15, xfer);
 		}
 		else
 		{
-			purple_dnsquery_a(YAHOO_XFER_RELAY_HOST, YAHOO_XFER_RELAY_PORT, yahoo_xfer_dns_connected_15, xfer);
+			purple_dnsquery_a(YAHOO_XFER_RELAY_HOST, YAHOO_XFER_RELAY_PORT,
+							yahoo_xfer_dns_connected_15, xfer);
 		}
 		return;
 	}
--- a/libpurple/proxy.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/libpurple/proxy.c	Wed Feb 20 06:56:39 2008 +0000
@@ -737,6 +737,7 @@
 				proxy_do_write(connect_data, connect_data->fd, cond);
 				return;
 			} else if((ntlm = g_strrstr((const char *)connect_data->read_buffer, "Proxy-Authenticate: NTLM"))) { /* Empty message */
+				gchar *ntlm_type1;
 				gchar request[2048];
 				gchar *domain = (gchar*) purple_proxy_info_get_username(connect_data->gpi);
 				gchar *username = NULL;
@@ -759,11 +760,13 @@
 						connect_data->host, connect_data->port);
 
 				g_return_if_fail(request_len < sizeof(request));
+				ntlm_type1 = purple_ntlm_gen_type1(hostname, domain);
 				request_len += g_snprintf(request + request_len,
 					sizeof(request) - request_len,
 					"Proxy-Authorization: NTLM %s\r\n"
 					"Proxy-Connection: Keep-Alive\r\n\r\n",
-					purple_ntlm_gen_type1(hostname, domain));
+					ntlm_type1);
+				g_free(ntlm_type1);
 				*username = '\\';
 
 				purple_input_remove(connect_data->inpa);
@@ -847,7 +850,7 @@
 
 	if (purple_proxy_info_get_username(connect_data->gpi) != NULL)
 	{
-		char *t1, *t2;
+		char *t1, *t2, *ntlm_type1;
 		char hostname[256];
 
 		ret = gethostname(hostname, sizeof(hostname));
@@ -864,11 +867,14 @@
 		t2 = purple_base64_encode((const guchar *)t1, strlen(t1));
 		g_free(t1);
 
+		ntlm_type1 = purple_ntlm_gen_type1(hostname, "");
+
 		g_string_append_printf(request,
 			"Proxy-Authorization: Basic %s\r\n"
 			"Proxy-Authorization: NTLM %s\r\n"
 			"Proxy-Connection: Keep-Alive\r\n",
-			t2, purple_ntlm_gen_type1(hostname, ""));
+			t2, ntlm_type1);
+		g_free(ntlm_type1);
 		g_free(t2);
 	}
 
--- a/pidgin/gtkaccount.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/pidgin/gtkaccount.c	Wed Feb 20 06:56:39 2008 +0000
@@ -2092,8 +2092,10 @@
 	/* Figure out which node was clicked */
 	if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(dialog->treeview), event->x, event->y, &path, &column, NULL, NULL))
 		return FALSE;
-	if (column == gtk_tree_view_get_column(treeview, 0))
+	if (column == gtk_tree_view_get_column(treeview, 0)) {
+		gtk_tree_path_free(path);
 		return FALSE;
+	}
 
 	gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path);
 	gtk_tree_path_free(path);
--- a/pidgin/gtkblist.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/pidgin/gtkblist.c	Wed Feb 20 06:56:39 2008 +0000
@@ -159,6 +159,7 @@
 static void pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node);
 static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded);
 static void pidgin_blist_expand_contact_cb(GtkWidget *w, PurpleBlistNode *node);
+static void set_urgent(void);
 
 typedef enum {
 	PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE    =  1 << 0,  /* Whether there's pending message in a conversation */
@@ -2952,6 +2953,9 @@
 	gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), x, y, &path, NULL, NULL, NULL);
 	gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &rect);
 
+	if (path)
+		gtk_tree_path_free(path);
+
 	/* Only autoexpand when in the middle of the cell to avoid annoying un-intended expands */
 	if (y < rect.y + (rect.height / 3) ||
 	    y > rect.y + (2 * (rect.height /3)))
@@ -2962,8 +2966,6 @@
 
 	gtkblist->tip_rect = rect;
 
-	if (path)
-		gtk_tree_path_free(path);
 	gtkblist->drag_timeout = g_timeout_add(delay, (GSourceFunc)pidgin_blist_expand_timeout, tv);
 
 	if (gtkblist->mouseover_contact) {
@@ -4408,6 +4410,7 @@
 /***********************************/
 
 #define OBJECT_DATA_KEY_ACCOUNT "account"
+#define DO_NOT_CLEAR_ERROR "do-not-clear-error"
 
 static gboolean
 find_account_widget(GObject *widget,
@@ -4442,8 +4445,7 @@
 	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
 	gtk_container_add(GTK_CONTAINER(priv->error_scrollbook), dialog);
 
-	if (!GTK_WIDGET_HAS_FOCUS(gtkblist->window))
-		pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE);
+	set_urgent();
 }
 
 static GtkWidget *
@@ -4470,6 +4472,11 @@
 {
 	GtkWidget *widget = find_child_widget_by_account(container, account);
 	if(widget) {
+		/* Since we are destroying the widget in response to a change in
+		 * error, we should not clear the error.
+		 */
+		g_object_set_data(G_OBJECT(widget), DO_NOT_CLEAR_ERROR,
+			GINT_TO_POINTER(TRUE));
 		gtk_widget_destroy(widget);
 	}
 }
@@ -4495,7 +4502,12 @@
                          PurpleAccount *account)
 {
 	g_hash_table_remove(gtkblist->connection_errors, account);
-	purple_account_clear_current_error(account);
+	/* If the error dialog is being destroyed in response to the
+	 * account-error-changed signal, we don't want to clear the current
+	 * error.
+	 */
+	if (g_object_get_data(G_OBJECT(dialog), DO_NOT_CLEAR_ERROR) == NULL)
+		purple_account_clear_current_error(account);
 }
 
 #define SSL_FAQ_URI "http://d.pidgin.im/wiki/FAQssl"
@@ -4571,6 +4583,19 @@
 }
 
 
+static void
+update_generic_error_message(PurpleAccount *account,
+                             const char *description)
+{
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	GtkWidget *mini_dialog = find_child_widget_by_account(
+		GTK_CONTAINER(priv->error_scrollbook), account);
+	pidgin_mini_dialog_set_description(PIDGIN_MINI_DIALOG(mini_dialog),
+		description);
+	set_urgent();
+}
+
+
 /* Notifications about accounts which were disconnected with
  * PURPLE_CONNECTION_ERROR_NAME_IN_USE
  */
@@ -4724,8 +4749,7 @@
 
 	update_signed_on_elsewhere_minidialog_title();
 
-	if (!GTK_WIDGET_HAS_FOCUS(gtkblist->window))
-		pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE);
+	set_urgent();
 }
 
 static void
@@ -4742,6 +4766,20 @@
 }
 
 
+static void
+update_signed_on_elsewhere_tooltip(PurpleAccount *account,
+                                   const char *description)
+{
+#if GTK_CHECK_VERSION(2,12,0)
+	PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
+	GtkContainer *c = GTK_CONTAINER(priv->signed_on_elsewhere->contents);
+	GtkWidget *label = find_child_widget_by_account(c, account);
+	gtk_widget_set_tooltip_text(label, description);
+	set_urgent();
+#endif
+}
+
+
 /* Call appropriate error notification code based on error types */
 static void
 update_account_error_state(PurpleAccount *account,
@@ -4749,35 +4787,60 @@
                            const PurpleConnectionErrorInfo *new,
                            PidginBuddyList *gtkblist)
 {
+	gboolean descriptions_differ;
+	const char *desc;
+
+	if (old == NULL && new == NULL)
+		return;
+
 	/* For backwards compatibility: */
 	if (new)
 		pidgin_blist_update_account_error_state(account, new->description);
 	else
 		pidgin_blist_update_account_error_state(account, NULL);
 
-	pidgin_blist_select_notebook_page(gtkblist);
-	/* Don't bother updating the error if it hasn't changed.  This stops
-	 * URGENT being repeatedly set for network errors whenever they try to
-	 * reconnect.
-	 */
-	if ((old == new) ||
-	    (old != NULL && new != NULL && old->type == new->type
-	     && g_str_equal(old->description, new->description))
-	   )
-		return;
-
-	if (old) {
+	if (new != NULL)
+		pidgin_blist_select_notebook_page(gtkblist);
+
+	if (old != NULL && new == NULL) {
 		if(old->type == PURPLE_CONNECTION_ERROR_NAME_IN_USE)
 			remove_from_signed_on_elsewhere(account);
 		else
 			remove_generic_error_dialog(account);
-	}
-
-	if (new) {
+		return;
+	}
+
+	if (old == NULL && new != NULL) {
 		if(new->type == PURPLE_CONNECTION_ERROR_NAME_IN_USE)
 			add_to_signed_on_elsewhere(account);
 		else
 			add_generic_error_dialog(account, new);
+		return;
+	}
+
+	/* else, new and old are both non-NULL */
+
+	descriptions_differ = strcmp(old->description, new->description);
+	desc = new->description;
+
+	switch (new->type) {
+	case PURPLE_CONNECTION_ERROR_NAME_IN_USE:
+		if (old->type == PURPLE_CONNECTION_ERROR_NAME_IN_USE
+		    && descriptions_differ) {
+			update_signed_on_elsewhere_tooltip(account, desc);
+		} else {
+			remove_generic_error_dialog(account);
+			add_to_signed_on_elsewhere(account);
+		}
+		break;
+	default:
+		if (old->type == PURPLE_CONNECTION_ERROR_NAME_IN_USE) {
+			remove_from_signed_on_elsewhere(account);
+			add_generic_error_dialog(account, new);
+		} else if (descriptions_differ) {
+			update_generic_error_message(account, desc);
+		}
+		break;
 	}
 }
 
@@ -6740,8 +6803,7 @@
 void pidgin_blist_add_alert(GtkWidget *widget)
 {
 	gtk_container_add(GTK_CONTAINER(gtkblist->scrollbook), widget);
-	if (!GTK_WIDGET_HAS_FOCUS(gtkblist->window))
-		pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE);
+	set_urgent();
 }
 
 void
@@ -6759,14 +6821,21 @@
 	gtkblist->headline_data = user_data;
 	gtkblist->headline_destroy = destroy;
 	if (text != NULL || pixbuf != NULL) {
-		if (!GTK_WIDGET_HAS_FOCUS(gtkblist->window))
-			pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE);
+		set_urgent();
 		gtk_widget_show_all(gtkblist->headline_hbox);
 	} else {
 		gtk_widget_hide(gtkblist->headline_hbox);
 	}
 }
 
+
+static void
+set_urgent(void)
+{
+	if (!GTK_WIDGET_HAS_FOCUS(gtkblist->window))
+		pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE);
+}
+
 static PurpleBlistUiOps blist_ui_ops =
 {
 	pidgin_blist_new_list,
--- a/pidgin/gtkconv.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/pidgin/gtkconv.c	Wed Feb 20 06:56:39 2008 +0000
@@ -242,7 +242,10 @@
 	switch (purple_conversation_get_type(conv)) {
 		case PURPLE_CONV_TYPE_IM:
 		{
-			hide_conv(gtkconv, TRUE);
+			if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately"))
+				close_this_sucker(gtkconv);
+			else
+				hide_conv(gtkconv, TRUE);
 			break;
 		}
 		case PURPLE_CONV_TYPE_CHAT:
@@ -7780,6 +7783,7 @@
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", TRUE);
 
 	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "never");
+	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", TRUE);
 
 #ifdef _WIN32
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/win32/minimize_new_convs", FALSE);
--- a/pidgin/gtklog.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/pidgin/gtklog.c	Wed Feb 20 06:56:39 2008 +0000
@@ -263,11 +263,10 @@
 				gtk_tree_store_remove(treestore, iter);
 			}
 		}
-		gtk_tree_path_free(path);
 #else
 		gtk_tree_store_remove(treestore, iter);
+#endif
 		gtk_tree_path_free(path);
-#endif
 	}
 
 	delete_log_cleanup_cb(data);
@@ -363,6 +362,7 @@
 		gtk_tree_model_get_iter(GTK_TREE_MODEL(lv->treestore), iter, path);
 		val.g_type = 0;
 		gtk_tree_model_get_value(GTK_TREE_MODEL(lv->treestore), iter, 1, &val);
+		gtk_tree_path_free(path);
 
 		log = g_value_get_pointer(&val);
 
--- a/pidgin/gtkprefs.c	Mon Feb 11 08:16:15 2008 +0000
+++ b/pidgin/gtkprefs.c	Wed Feb 20 06:56:39 2008 +0000
@@ -946,6 +946,8 @@
 
 	pidgin_prefs_checkbox(_("Show _formatting on incoming messages"),
 				PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", vbox);
+	pidgin_prefs_checkbox(_("Close IMs immediately when the tab is closed"),
+				PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", vbox);
 
 	iconpref1 = pidgin_prefs_checkbox(_("Show _detailed information"),
 			PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", vbox);
@@ -2198,7 +2200,6 @@
 	purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_group_count");
 	purple_prefs_remove(PIDGIN_PREFS_ROOT "/blist/show_warning_level");
 	purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/button_type");
-	purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/close_immediately");
 	purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/ctrl_enter_sends");
 	purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/enter_sends");
 	purple_prefs_remove(PIDGIN_PREFS_ROOT "/conversations/escape_closes");
Binary file pidgin/pixmaps/toolbar/16/get-attention.png has changed
--- a/po/de.po	Mon Feb 11 08:16:15 2008 +0000
+++ b/po/de.po	Wed Feb 20 06:56:39 2008 +0000
@@ -11,8 +11,8 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-02-08 16:08+0100\n"
-"PO-Revision-Date: 2008-02-08 16:08+0100\n"
+"POT-Creation-Date: 2008-02-19 20:29+0100\n"
+"PO-Revision-Date: 2008-02-19 20:28+0100\n"
 "Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
@@ -196,11 +196,12 @@
 msgid "Screen Name"
 msgstr "Benutzername"
 
-msgid "Alias"
-msgstr "Alias"
-
-msgid "Group"
-msgstr "Gruppe"
+msgid "Alias (optional)"
+msgstr "Alias (optional)"
+
+#, fuzzy
+msgid "Add in group"
+msgstr "Gruppe hinzufügen"
 
 msgid "Account"
 msgstr "Konto"
@@ -218,6 +219,12 @@
 msgid "Name"
 msgstr "Name"
 
+msgid "Alias"
+msgstr "Alias"
+
+msgid "Group"
+msgstr "Gruppe"
+
 msgid "Auto-join"
 msgstr "Automatisch beitreten"
 
@@ -5346,23 +5353,22 @@
 msgid "MySpace"
 msgstr "MySpace"
 
+msgid "MySpaceIM - No Username Set"
+msgstr "MySpaceIM - Kein Benutzername gesetzt"
+
+msgid "You appear to have no MySpace username."
+msgstr "Sie scheinen keinen MySpace-Benutzernamen zu haben."
+
+msgid "Would you like to set one now? (Note: THIS CANNOT BE CHANGED!)"
+msgstr ""
+"Möchten Sie jetzt einen setzen? (Bemerkung: DIES KANN NICHT GEÄNDERT WERDEN!)"
+
 #. The session is now set up, ready to be connected. This emits the
 #. * signedOn signal, so clients can now do anything with msimprpl, and
 #. * we're ready for it (session key, userid, username all setup).
 msgid "Connected"
 msgstr "Verbunden"
 
-msgid "No username set"
-msgstr "Kein Benutzername gesetzt"
-
-msgid ""
-"Please go to http://editprofile.myspace.com/index.cfm?fuseaction=profile."
-"username and choose a username and try to login again."
-msgstr ""
-"Bitte besuchen Sie http://editprofile.myspace.com/index.cfm?"
-"fuseaction=profile.username und wählen sie einen Benutzernamen und versuchen "
-"Sie sich erneut anzumelden."
-
 #, c-format
 msgid "Protocol error, code %d: %s"
 msgstr "Protokollfehler, Code %d: %s"
@@ -5492,6 +5498,32 @@
 msgid "Client Version"
 msgstr "Client-Version"
 
+#. Protocol won't log in now without a username set.. Disconnect
+msgid "No username set"
+msgstr "Kein Benutzername gesetzt"
+
+msgid "MySpaceIM - Please Set a Username"
+msgstr "MySpaceIM - Bitte setzen Sie einen Benutzernamen"
+
+msgid "Please enter a username to check its availability:"
+msgstr ""
+"Bitte geben Sie einen Benutzernamen ein um seine Verfügbarkeit zu überprüfen:"
+
+msgid "MySpaceIM - Username Available"
+msgstr "MySpaceIM - Benutzername verfügbar"
+
+msgid "This username is available. Would you like to set it?"
+msgstr "Dieser Benutzername ist verfügbar. Möchten Sie ihn setzen?"
+
+msgid "ONCE SET, THIS CANNOT BE CHANGED!"
+msgstr "EINMAL GESETZT, KANN DIES NICHT GEÄNDERT WERDEN!"
+
+msgid "This username is unavailable."
+msgstr "Dieser Benutzername ist nicht verfügbar."
+
+msgid "Please try another username:"
+msgstr "Bitte versuchen Sie einen anderen Benutzernamen:"
+
 #. TODO: icons for each zap
 #. Lots of comments for translators:
 #. Zap means "to strike suddenly and forcefully as if with a
@@ -11838,6 +11870,9 @@
 msgid "Show _formatting on incoming messages"
 msgstr "Zeige _Formatierung bei ankommenden Nachrichten"
 
+msgid "Close IMs immediately when the tab is closed"
+msgstr "IMs automatisch schließen, wenn der Reiter geschlossen wird"
+
 msgid "Show _detailed information"
 msgstr "_Detaillierte Informationen anzeigen"