changeset 25774:c623a1ff57de

propagate from branch 'im.pidgin.pidgin' (head 410f7adb8bbf7aef2373d653d847556cd17169dc) to branch 'im.pidgin.cpw.malu.xmpp.ibb_ft' (head 89b81687c8f0d105829d06cef9f778ed77438ce4)
author Marcus Lundblad <ml@update.uu.se>
date Mon, 16 Feb 2009 20:51:37 +0000
parents 463a2caa35e6 (diff) da4e18e318e7 (current diff)
children 7bae3a3ea4c1
files
diffstat 19 files changed, 263 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Thu Feb 12 21:48:48 2009 +0000
+++ b/COPYRIGHT	Mon Feb 16 20:51:37 2009 +0000
@@ -14,6 +14,7 @@
 Patrick Aussems
 Anibal Avelar
 Ali Albazaz
+Kosta Arvanitis
 Christopher Ayoup
 Alex Badea
 John Bailey
@@ -66,6 +67,7 @@
 Ludovico Cavedon
 Steve Cavilia
 Julien Cegarra
+Matěj Cepl
 Cerulean Studios, LLC
 Jonathan Champ
 Christophe Chapuis
@@ -204,6 +206,7 @@
 Intel Corporation
 Scott Jackson
 Hans Petter Jansson
+David Jedelsky
 Henry Jen
 Benjamin Kahn
 Anders Kaseorg
@@ -419,6 +422,7 @@
 Andreas Stührk
 Oleg Sukhodolsky
 Sun Microsystems
+Marcus Sundberg
 Mårten Svantesson (fursten)
 Amir Szekely (kichik)
 Robert T.
--- a/ChangeLog	Thu Feb 12 21:48:48 2009 +0000
+++ b/ChangeLog	Mon Feb 16 20:51:37 2009 +0000
@@ -7,6 +7,8 @@
 	  enable, check the "Use SSL" option from the Advanced tab when
 	  editing your AIM or ICQ account. (Paul Aurich)
 	* Fix a memory leak in SILC. (Luke Petre)
+	* Fix some string handling in the SIMPLE prpl, which fixes some buddy name
+	  handling and other issues. (Paul Aurich, Marcus Sundberg)
 
 	ICQ:
 	* Fix retrieval of status messages from users of ICQ 6.x, Miranda, and
@@ -15,6 +17,7 @@
 	  of buddy icons and available messages.
 	* Properly publish status messages for statuses other than Available.
 	  ICQ 6.x users can now see these status messages. (Daniel Ljungborg)
+	* Fix recipt of messages from the mobile client Slick. (David Jedelsky)
 
 	MSN:
 	* Fix transfer of buddy icons, custom smileys, and files from the
@@ -23,8 +26,41 @@
 	* Large (multi-part) messages are now correctly re-combined.
 	* Federated/Yahoo! buddies should now stop creating sync issues at
 	  every signin.  You may need to remove duplicates in the Address
-	  Book.  See the FAQ for more information.
+	  Book.  See the FAQ for more information.  Thanks to Jason Lingohr
+	  for lots of debugging and testing.
 	* Messages from Yahoo! buddies are no longer silently dropped.
+	* We now save and use the CacheKey for ABCH SOAP requests.
+	* Don't try to parse Personal Status Messages or Current Media if they
+	  don't exist.
+	* Convert from ISO-8859-1 encoding to UTF-8 when no charset is specified
+	  on incoming messages.  This should fix some issues with messages from
+	  older clients.
+	* Force sending the font "Segoe UI" if outgoing formatting doesn't specify
+	  a font already.
+	* Queue callbacks when token updates are in progress to prevent two token
+	  update attempts from trampling each other.
+	* Fixed a crash on Windows when removing a buddy's alias.
+	* Update the Address Book when buddies' friendly names change.  This
+	  prevents seeing an outdated alias or not seeing an alias at all for
+	  buddies who are offline when you sign in.
+	* Update tokens for FindMembership and ABFindAll SOAP requests.
+	* We no longer try to send empty messages.  This could happen when a
+	  message contained only formatting and that formatting was not supported
+	  on MSN.
+
+	XMPP:
+	* Resources using __HOSTNAME__ substitution will now grab only the short
+	  hostname instead of the FQDN on systems which put the FQDN in the
+	  hostname. (Matěj Cepl)
+	* No longer send a 'to' attribute on an outgoing stanza when we haven't
+	  received one.  This fixes a registration bug as described in ticket
+	  #6635.
+
+	Pidgin:
+	* Tooltip windows now appear below the mouse cursor. (Kosta Arvanitis)
+	* Tooltip windows now disappear on keypress events. (Kosta Arvanitis)
+	* Tooltip windows no longer linger when scrolling the buddy list. (Kosta
+	  Arvanitis)
 
 	Finch:
 	* Allow rebinding keys to change the focused widget (details in the
--- a/finch/libgnt/gntwm.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/finch/libgnt/gntwm.c	Mon Feb 16 20:51:37 2009 +0000
@@ -353,7 +353,8 @@
 				p->y = y;
 				g_hash_table_replace(wm->positions, g_strdup(title + 1), p);
 			} else {
-				gnt_warning("Invalid number of arguments (%d) for positioning a window.", l);
+				gnt_warning("Invalid number of arguments (%" G_GSIZE_FORMAT
+						") for positioning a window.", l);
 			}
 			g_strfreev(coords);
 		}
--- a/libpurple/protocols/jabber/jabber.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Mon Feb 16 20:51:37 2009 +0000
@@ -148,7 +148,8 @@
 }
 
 static char *jabber_prep_resource(char *input) {
-	char hostname[256]; /* current hostname */
+	char hostname[256], /* current hostname */
+		 *dot = NULL;
 
 	/* Empty resource == don't send any */
 	if (input == NULL || *input == '\0')
@@ -170,6 +171,12 @@
 	}
 	hostname[sizeof(hostname) - 1] = '\0';
 
+	/* We want only the short hostname, not the FQDN - this will prevent the
+	 * resource string from being unreasonably long on systems which stuff the
+	 * whole FQDN in the hostname */
+	if((dot = strchr(hostname, '.')))
+			dot = '\0';
+
 	return purple_strreplace(input, "__HOSTNAME__", hostname);
 }
 
@@ -802,10 +809,11 @@
 				js->user->node, js->user->domain);
 			if(account->registration_cb)
 				(account->registration_cb)(account, TRUE, account->registration_cb_user_data);
-		}
-		else
+		} else {
+			g_return_if_fail(to != NULL);
 			buf = g_strdup_printf(_("Registration to %s successful"),
 				to);
+		}
 		purple_notify_info(NULL, _("Registration Successful"),
 				_("Registration Successful"), buf);
 		g_free(buf);
@@ -832,7 +840,11 @@
 	const char *type = xmlnode_get_attrib(packet, "type");
 	char *buf;
 	char *to = data;
-	
+
+	/* This function is never called for unregistering our XMPP account from
+	 * the server, so there should always be a 'to' address. */
+	g_return_if_fail(to != NULL);
+
 	if(!strcmp(type, "result")) {
 		buf = g_strdup_printf(_("Registration from %s successfully removed"),
 							  to);
@@ -867,7 +879,8 @@
 
 	iq = jabber_iq_new_query(cbdata->js, JABBER_IQ_SET, "jabber:iq:register");
 	query = xmlnode_get_child(iq->node, "query");
-	xmlnode_set_attrib(iq->node, "to", cbdata->who);
+	if (cbdata->who)
+		xmlnode_set_attrib(iq->node, "to", cbdata->who);
 
 	for(groups = purple_request_fields_get_groups(fields); groups;
 			groups = groups->next) {
@@ -883,7 +896,8 @@
 					jabber_iq_free(iq);
 					iq = jabber_iq_new_query(cbdata->js, JABBER_IQ_SET, "jabber:iq:register");
 					query = xmlnode_get_child(iq->node, "query");
-					xmlnode_set_attrib(iq->node,"to",cbdata->who);
+					if (cbdata->who)
+						xmlnode_set_attrib(iq->node,"to",cbdata->who);
 					xmlnode_new_child(query, "remove");
 					
 					jabber_iq_set_callback(iq, jabber_unregistration_result_cb, cbdata->who);
@@ -928,8 +942,7 @@
 			}
 			xmlnode_insert_data(y, value, -1);
 				if(cbdata->js->registration && !strcmp(id, "username")) {
-					if(cbdata->js->user->node)
-						g_free(cbdata->js->user->node);
+					g_free(cbdata->js->user->node);
 					cbdata->js->user->node = g_strdup(value);
 			}
 				if(cbdata->js->registration && !strcmp(id, "password"))
@@ -972,7 +985,8 @@
 
 	iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:register");
 	query = xmlnode_get_child(iq->node, "query");
-	xmlnode_set_attrib(iq->node,"to",to);
+	if (to)
+		xmlnode_set_attrib(iq->node,"to",to);
 
 	xmlnode_insert_child(query, result);
 
@@ -997,10 +1011,7 @@
 		return;
 
 	from = xmlnode_get_attrib(packet, "from");
-	if (!from)
-		from = js->serverFQDN;
-	g_return_if_fail(from != NULL);
-	
+
 	if(js->registration) {
 		/* get rid of the login thingy */
 		purple_connection_set_state(js->gc, PURPLE_CONNECTED);
@@ -1145,7 +1156,9 @@
 				purple_connection_get_account(js->gc), NULL, NULL,
 				cbdata);
 	else {
-		char *title = registered?g_strdup_printf(_("Change Account Registration at %s"), from)
+		char *title;
+		g_return_if_fail(from != NULL);
+		title = registered ? g_strdup_printf(_("Change Account Registration at %s"), from)
 								:g_strdup_printf(_("Register New Account at %s"), from);
 		purple_request_fields(js->gc, title,
 			  title, instructions, fields,
--- a/libpurple/protocols/msn/contact.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/libpurple/protocols/msn/contact.c	Mon Feb 16 20:51:37 2009 +0000
@@ -198,6 +198,8 @@
 	MsnCallbackState *state = data;
 	xmlnode *fault;
 	char *faultcode_str;
+	xmlnode *cachekey;
+	char *changed;
 
 	if (resp == NULL) {
 		purple_debug_error("msn",
@@ -206,12 +208,27 @@
 		return;
 	}
 
+ 	/* Update CacheKey if necessary */
+ 	cachekey = xmlnode_get_child(resp->xml, "Header/ServiceHeader/CacheKeyChanged");
+ 	if (cachekey != NULL) {
+ 		changed = xmlnode_get_data(cachekey);
+ 		if (changed && !strcmp(changed, "true")) {
+ 			cachekey = xmlnode_get_child(resp->xml, "Header/ServiceHeader/CacheKey");
+ 			g_free(state->session->abch_cachekey);
+ 			state->session->abch_cachekey = xmlnode_get_data(cachekey);
+ 			purple_debug_info("msn", "Updated CacheKey for %s to '%s'.\n",
+ 			                  purple_account_get_username(state->session->account),
+ 			                  state->session->abch_cachekey);
+ 		}
+ 		g_free(changed);
+ 	}
+
 	fault = xmlnode_get_child(resp->xml, "Body/Fault");
 
 	if (fault == NULL) {
 		/* No errors */
 		if (state->cb)
-			((MsnSoapCallback)state->cb)(req, resp, data);
+			state->cb(req, resp, data);
 		msn_callback_state_free(state);
 		return;
 	}
@@ -230,7 +247,7 @@
 	else
 	{
 		if (state->cb) {
-			((MsnSoapCallback)state->cb)(req, resp, data);
+			state->cb(req, resp, data);
 		} else {
 			/* We don't know how to respond to this faultcode, so log it */
 			char *str = xmlnode_to_str(fault, NULL);
@@ -247,6 +264,14 @@
 static gboolean
 msn_contact_request(MsnCallbackState *state)
 {
+	xmlnode *cachekey = xmlnode_get_child(state->body,
+	                                      "Header/ABApplicationHeader/CacheKey");
+	if (cachekey != NULL)
+		xmlnode_free(cachekey);
+	if (state->session->abch_cachekey != NULL) {
+		cachekey = xmlnode_new_child(xmlnode_get_child(state->body, "Header/ABApplicationHeader"), "CacheKey");
+		xmlnode_insert_data(cachekey, state->session->abch_cachekey, -1);
+	}
 	if (state->token == NULL)
 		state->token = xmlnode_get_child(state->body,
 			"Header/ABAuthHeader/TicketToken");
--- a/libpurple/protocols/msn/msg.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/libpurple/protocols/msn/msg.c	Mon Feb 16 20:51:37 2009 +0000
@@ -352,6 +352,14 @@
 			memcpy(msg->body, tmp, msg->body_len);
 			msg->body[msg->body_len] = '\0';
 		}
+		
+		if (msg->charset == NULL) {
+			char *body = g_convert(msg->body, msg->body_len, "UTF-8",
+			                       "ISO-8859-1", NULL, &msg->body_len, NULL);
+			g_free(msg->body);
+			msg->body = body;
+			msg->charset = g_strdup("UTF-8");
+		}
 	}
 
 	g_free(tmp_base);
--- a/libpurple/protocols/msn/msnutils.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/libpurple/protocols/msn/msnutils.c	Mon Feb 16 20:51:37 2009 +0000
@@ -446,7 +446,7 @@
 	}
 
 	if (fontface == NULL)
-		fontface = g_strdup("MS Sans Serif");
+		fontface = g_strdup("Segoe UI");
 
 	*attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
 								  encode_spaces(fontface),
--- a/libpurple/protocols/msn/nexus.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/libpurple/protocols/msn/nexus.c	Mon Feb 16 20:51:37 2009 +0000
@@ -74,6 +74,7 @@
 	for (i = 0; i < nexus->token_len; i++) {
 		g_hash_table_destroy(nexus->tokens[i].token);
 		g_free(nexus->tokens[i].secret);
+		g_slist_free(nexus->tokens[i].updates);
 	}
 
 	g_free(nexus->tokens);
@@ -235,6 +236,10 @@
 struct _MsnNexusUpdateData {
 	MsnNexus *nexus;
 	int id;
+};
+
+typedef struct _MsnNexusUpdateCallback MsnNexusUpdateCallback;
+struct _MsnNexusUpdateCallback {
 	GSourceFunc cb;
 	gpointer data;
 };
@@ -428,6 +433,7 @@
 	char *nonce;
 	gsize len;
 	char *key;
+	GSList *updates;
 
 #if 0
 	char *decrypted_pp;
@@ -489,8 +495,15 @@
 		g_free(decrypted_data);
 	}
 
-	if (ud->cb)
-		purple_timeout_add(0, ud->cb, ud->data);
+	updates = nexus->tokens[ud->id].updates;
+	nexus->tokens[ud->id].updates = NULL;
+	while (updates != NULL) {
+		MsnNexusUpdateCallback *update = updates->data;
+		if (update->cb)
+			purple_timeout_add(0, update->cb, update->data);
+		g_free(update);
+		updates = g_slist_delete_link(updates, updates);
+	}
 
 	g_free(ud);
 }
@@ -500,6 +513,7 @@
 {
 	MsnSession *session = nexus->session;
 	MsnNexusUpdateData *ud;
+	MsnNexusUpdateCallback *update;
 	PurpleCipherContext *sha1;
 	PurpleCipherContext *hmac;
 
@@ -526,16 +540,31 @@
 	char *request;
 	MsnSoapMessage *soap;
 
-	purple_debug_info("msn",
-	                  "Updating ticket for user '%s' on domain '%s'\n",
-	                  purple_account_get_username(session->account),
-	                  ticket_domains[id][SSO_VALID_TICKET_DOMAIN]);
+	update = g_new0(MsnNexusUpdateCallback, 1);
+	update->cb = cb;
+	update->data = data;
+
+	if (nexus->tokens[id].updates != NULL) {
+		/* Update already in progress. Just add to list and return. */
+		purple_debug_info("msn",
+		                  "Ticket update for user '%s' on domain '%s' in progress. Adding request to queue.\n",
+		                  purple_account_get_username(session->account),
+		                  ticket_domains[id][SSO_VALID_TICKET_DOMAIN]);
+		nexus->tokens[id].updates = g_slist_prepend(nexus->tokens[id].updates,
+		                                            update);
+		return;
+	} else {
+		purple_debug_info("msn",
+		                  "Updating ticket for user '%s' on domain '%s'\n",
+		                  purple_account_get_username(session->account),
+		                  ticket_domains[id][SSO_VALID_TICKET_DOMAIN]);
+		nexus->tokens[id].updates = g_slist_prepend(nexus->tokens[id].updates,
+		                                            update);
+	}
 
 	ud = g_new0(MsnNexusUpdateData, 1);
 	ud->nexus = nexus;
 	ud->id = id;
-	ud->cb = cb;
-	ud->data = data;
 
 	sha1 = purple_cipher_context_new_by_name("sha1", NULL);
 
--- a/libpurple/protocols/msn/nexus.h	Thu Feb 12 21:48:48 2009 +0000
+++ b/libpurple/protocols/msn/nexus.h	Mon Feb 16 20:51:37 2009 +0000
@@ -204,6 +204,7 @@
 	GHashTable *token;
 	char *secret;
 	time_t expiry;
+	GSList *updates;
 };
 
 typedef struct _MsnNexus MsnNexus;
--- a/libpurple/protocols/msn/notification.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/libpurple/protocols/msn/notification.c	Mon Feb 16 20:51:37 2009 +0000
@@ -1619,19 +1619,25 @@
 		return;
 	}
 
-	psm_str = msn_get_psm(cmd->payload,len);
-	msn_user_set_statusline(user, psm_str);
-	g_free(psm_str);
+	if (len != 0) {
+		psm_str = msn_get_psm(cmd->payload,len);
+		msn_user_set_statusline(user, psm_str);
+		g_free(psm_str);
 
-	str = msn_get_currentmedia(cmd->payload, len);
-	if (msn_parse_currentmedia(str, &media))
-		msn_user_set_currentmedia(user, &media);
-	else
+		str = msn_get_currentmedia(cmd->payload, len);
+		if (msn_parse_currentmedia(str, &media))
+			msn_user_set_currentmedia(user, &media);
+		else
+			msn_user_set_currentmedia(user, NULL);
+		g_free(media.title);
+		g_free(media.album);
+		g_free(media.artist);
+		g_free(str);
+
+	} else {
+		msn_user_set_statusline(user, NULL);
 		msn_user_set_currentmedia(user, NULL);
-	g_free(media.title);
-	g_free(media.album);
-	g_free(media.artist);
-	g_free(str);
+	}
 
 	msn_user_update(user);
 }
--- a/libpurple/protocols/msn/session.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/libpurple/protocols/msn/session.c	Mon Feb 16 20:51:37 2009 +0000
@@ -90,8 +90,10 @@
 	msn_userlist_destroy(session->userlist);
 
 	g_free(session->psm);
-
+	g_free(session->abch_cachekey);
+#if 0
 	g_free(session->blocked_text);
+#endif
 
 	g_free(session->passport_info.kv);
 	g_free(session->passport_info.sid);
--- a/libpurple/protocols/msn/session.h	Thu Feb 12 21:48:48 2009 +0000
+++ b/libpurple/protocols/msn/session.h	Mon Feb 16 20:51:37 2009 +0000
@@ -94,11 +94,11 @@
 	gboolean http_method;
 
 	MsnNotification *notification;
-	MsnNexus *nexus;
-	MsnOim		*oim;
-	MsnSync *sync;
-
-	MsnUserList *userlist;
+	MsnNexus        *nexus;
+	MsnOim          *oim;
+	MsnSync         *sync;
+	MsnUserList     *userlist;
+	char            *abch_cachekey;
 
 	int servconns_count; /**< The count of server connections. */
 	GList *switches; /**< The list of all the switchboards. */
@@ -107,7 +107,9 @@
 	/*psm info*/
 	char *psm;
 
+#if 0
 	char *blocked_text;
+#endif
 
 	struct
 	{
--- a/libpurple/protocols/oscar/family_icbm.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Mon Feb 16 20:51:37 2009 +0000
@@ -1566,9 +1566,10 @@
 
 static int incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, ByteStream *bs, guint8 *cookie)
 {
-	guint16 type, length;
+	guint16 type, length, magic1, msglen;
 	aim_rxcallback_t userfunc;
 	int ret = 0;
+	int rev = 0;
 	struct aim_incomingim_ch1_args args;
 	unsigned int endpos;
 
@@ -1603,10 +1604,30 @@
 			 *   - 0101 -- Unknown
 			 *   - Message
 			 *
+			 * Slick and possible others reverse 'Features' and 'Messages' section.
+			 * Thus, the TLV could have following layout:
+			 *   - 0101 -- Unknown (possibly magic for message section)
+			 *   - Message
+			 *   - 0501 -- Unknown (possibly magic for features section)
+			 *   - Features: Don't know how to interpret these
 			 */
 
-			byte_stream_get8(bs); /* 05 */
-			byte_stream_get8(bs); /* 01 */
+			magic1 = byte_stream_get16(bs); /* 0501 or 0101 */
+			if (magic1 == 0x101) /* Bad, message comes before attributes */
+			{
+				/* Jump to the features section */
+				msglen = byte_stream_get16(bs);
+				bs->offset += msglen;
+				rev = 1;
+
+				magic1 = byte_stream_get16(bs); /* 0501 */
+			}
+
+			if (magic1 != 0x501)
+			{
+				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
+				break;
+			}
 
 			args.featureslen = byte_stream_get16(bs);
 			if (args.featureslen > byte_stream_empty(bs))
@@ -1624,11 +1645,25 @@
 				args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES;
 			}
 
+			if (rev)
+			{
+				/* Fix buffer back to message */
+				bs->offset -= args.featureslen + 2 + 2 + msglen + 2 + 2;
+			}
+
+			magic1 = byte_stream_get16(bs); /* 01 01 */
+			if (magic1 != 0x101) /* Bad, message comes before attributes */
+			{
+				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
+				break;
+			}
+			msglen = byte_stream_get16(bs);
+
 			/*
 			 * The rest of the TLV contains one or more message
 			 * blocks...
 			 */
-			incomingim_ch1_parsemsgs(od, userinfo, bs->data + bs->offset /* XXX evil!!! */, length - 2 - 2 - args.featureslen, &args);
+			incomingim_ch1_parsemsgs(od, userinfo, bs->data + bs->offset - 2 - 2 /* XXX evil!!! */, msglen + 2 + 2, &args);
 
 		} else if (type == 0x0003) { /* Server Ack Requested */
 
--- a/libpurple/protocols/simple/simple.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/libpurple/protocols/simple/simple.c	Mon Feb 16 20:51:37 2009 +0000
@@ -196,7 +196,7 @@
 {
 	struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data;
 	struct simple_buddy *b;
-	if(strcmp("sip:", buddy->name)) {
+	if(strncmp(buddy->name, "sip:", 4)) {
 		gchar *buf = g_strdup_printf("sip:%s", buddy->name);
 		purple_blist_rename_buddy(buddy, buf);
 		g_free(buf);
@@ -834,10 +834,10 @@
 		"Event: presence\r\n",
 		expiration);
 
-	if(strstr(buddy->name, "sip:"))
+	if(strncmp(buddy->name, "sip:", 4))
+		to = g_strdup_printf("sip:%s", buddy->name);
+	else
 		to = g_strdup(buddy->name);
-	else
-		to = g_strdup_printf("sip:%s", buddy->name);
 
 	tmp = get_contact(sip);
 	contact = g_strdup_printf("%sContact: %s\r\n", tmp2, tmp);
@@ -881,7 +881,7 @@
 
 
 	tmp = sipmsg_find_header(msg, "Event");
-	if(tmp && !strcmp(tmp, "vnd-microsoft-roaming-contacts")){
+	if(tmp && !strncmp(tmp, "vnd-microsoft-roaming-contacts", 30)){
 
 		purple_debug_info("simple", "simple_add_lcs_contacts->%s-%d\n", msg->body, len);
 		/*Convert the contact from XML to Purple Buddies*/
@@ -1013,11 +1013,11 @@
 static void simple_send_message(struct simple_account_data *sip, const char *to, const char *msg, const char *type) {
 	gchar *hdr;
 	gchar *fullto;
-	if(strcmp("sip:", to)) {
+	if(strncmp(to, "sip:", 4))
 		fullto = g_strdup_printf("sip:%s", to);
-	} else {
+	else
 		fullto = g_strdup(to);
-	}
+
 	if(type) {
 		hdr = g_strdup_printf("Content-Type: %s\r\n", type);
 	} else {
@@ -1050,12 +1050,12 @@
 	purple_debug(PURPLE_DEBUG_MISC, "simple", "got message from %s: %s\n", from, msg->body);
 
 	contenttype = sipmsg_find_header(msg, "Content-Type");
-	if(!contenttype || !strcmp(contenttype, "text/plain") || !strcmp(contenttype, "text/html")) {
+	if(!contenttype || !strncmp(contenttype, "text/plain", 10) || !strncmp(contenttype, "text/html", 9)) {
 		serv_got_im(sip->gc, from, msg->body, 0, time(NULL));
 		send_sip_response(sip->gc, msg, 200, "OK", NULL);
 		found = TRUE;
 	}
-	else if(!strcmp(contenttype, "application/im-iscomposing+xml")) {
+	else if(!strncmp(contenttype, "application/im-iscomposing+xml", 30)) {
 		xmlnode *isc = xmlnode_from_str(msg->body, msg->bodylen);
 		xmlnode *state;
 		gchar *statedata;
--- a/libpurple/savedstatuses.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/libpurple/savedstatuses.c	Mon Feb 16 20:51:37 2009 +0000
@@ -148,11 +148,11 @@
 	/* Avoid using 0 because it's an invalid hash key */
 	status->creation_time = creation_time != 0 ? creation_time : 1;
 
-	while (g_hash_table_lookup(creation_times, &status->creation_time) != NULL)
+	while (g_hash_table_lookup(creation_times, (gconstpointer)status->creation_time) != NULL)
 		status->creation_time++;
 
 	g_hash_table_insert(creation_times,
-						&status->creation_time,
+						(gpointer)status->creation_time,
 						status);
 }
 
@@ -217,7 +217,7 @@
 				{
 					saved_statuses = g_list_remove(saved_statuses, saved_status);
 					creation_time = purple_savedstatus_get_creation_time(saved_status);
-					g_hash_table_remove(creation_times, &creation_time);
+					g_hash_table_remove(creation_times, (gconstpointer)creation_time);
 					free_saved_status(saved_status);
 				}
 			}
@@ -713,7 +713,7 @@
 
 	saved_statuses = g_list_remove(saved_statuses, status);
 	creation_time = purple_savedstatus_get_creation_time(status);
-	g_hash_table_remove(creation_times, &creation_time);
+	g_hash_table_remove(creation_times, (gconstpointer)creation_time);
 	free_saved_status(status);
 
 	schedule_save();
@@ -801,13 +801,13 @@
 PurpleSavedStatus *
 purple_savedstatus_get_default()
 {
-	int creation_time;
+	time_t creation_time;
 	PurpleSavedStatus *saved_status = NULL;
 
 	creation_time = purple_prefs_get_int("/purple/savedstatus/default");
 
 	if (creation_time != 0)
-		saved_status = g_hash_table_lookup(creation_times, &creation_time);
+		saved_status = g_hash_table_lookup(creation_times, (gconstpointer)creation_time);
 
 	if (saved_status == NULL)
 	{
@@ -828,13 +828,13 @@
 PurpleSavedStatus *
 purple_savedstatus_get_idleaway()
 {
-	int creation_time;
+	time_t creation_time;
 	PurpleSavedStatus *saved_status = NULL;
 
 	creation_time = purple_prefs_get_int("/purple/savedstatus/idleaway");
 
 	if (creation_time != 0)
-		saved_status = g_hash_table_lookup(creation_times, &creation_time);
+		saved_status = g_hash_table_lookup(creation_times, (gconstpointer)creation_time);
 
 	if (saved_status == NULL)
 	{
@@ -907,13 +907,13 @@
 PurpleSavedStatus *
 purple_savedstatus_get_startup()
 {
-	int creation_time;
+	time_t creation_time;
 	PurpleSavedStatus *saved_status = NULL;
 
 	creation_time = purple_prefs_get_int("/purple/savedstatus/startup");
 
 	if (creation_time != 0)
-		saved_status = g_hash_table_lookup(creation_times, &creation_time);
+		saved_status = g_hash_table_lookup(creation_times, (gconstpointer)creation_time);
 
 	if (saved_status == NULL)
 	{
@@ -1187,7 +1187,7 @@
 {
 	void *handle = purple_savedstatuses_get_handle();
 
-	creation_times = g_hash_table_new(g_int_hash, g_int_equal);
+	creation_times = g_hash_table_new(g_direct_hash, g_direct_equal);
 
 	/*
 	 * Using 0 as the creation_time is a special case.
--- a/pidgin/gtkblist.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/pidgin/gtkblist.c	Mon Feb 16 20:51:37 2009 +0000
@@ -66,7 +66,7 @@
 #include <gtk/gtk.h>
 #include <gdk/gdk.h>
 
-#define HEADLINE_CLOSE_SIZE 12
+#define HEADLINE_CLOSE_SIZE 11
 
 typedef struct
 {
@@ -4554,6 +4554,9 @@
 	if (!gtkblist)
 		return FALSE;
 
+	/* clear any tooltips */
+	pidgin_blist_tooltip_destroy();
+
 	widget = gtk_window_get_focus(GTK_WINDOW(gtkblist->window));
 
 	if (GTK_IS_IMHTML(widget) || GTK_IS_ENTRY(widget)) {
@@ -5387,7 +5390,8 @@
 			  NULL);
 	gtk_widget_set_name(gtkblist->headline_hbox, "gtk-tooltips");
 
-	gtkblist->headline_close = gtk_widget_render_icon(ebox, GTK_STOCK_CLOSE, HEADLINE_CLOSE_SIZE, NULL);
+	gtkblist->headline_close = gtk_widget_render_icon(ebox, GTK_STOCK_CLOSE,
+		gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC), NULL);
 	gtkblist->hand_cursor = gdk_cursor_new (GDK_HAND2);
 	gtkblist->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR);
 
--- a/pidgin/gtkconv.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/pidgin/gtkconv.c	Mon Feb 16 20:51:37 2009 +0000
@@ -1959,6 +1959,9 @@
 	win      = gtkconv->win;
 	curconv = gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook));
 
+	/* clear any tooltips */
+	pidgin_tooltip_destroy();
+
 	/* If CTRL was held down... */
 	if (event->state & GDK_CONTROL_MASK) {
 		switch (event->keyval) {
--- a/pidgin/gtkdialogs.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/pidgin/gtkdialogs.c	Mon Feb 16 20:51:37 2009 +0000
@@ -457,12 +457,17 @@
 		  "warranty for this program.<BR><BR>"), PIDGIN_NAME, PIDGIN_NAME, PIDGIN_NAME);
 
 	g_string_append(str, "<FONT SIZE=\"4\">URL:</FONT> <A HREF=\""
-					PURPLE_WEBSITE "\">" PURPLE_WEBSITE "</A><BR/><BR/>");
-	g_string_append(str, "<FONT SIZE=\"4\">FAQ:</FONT> <A HREF=\""
-			"http://developer.pidgin.im/wiki/FAQ\">"
-			"http://developer.pidgin.im/wiki/FAQ</A><BR/><BR/>");
-	g_string_append_printf(str, _("<FONT SIZE=\"4\">IRC:</FONT> "
-						   "#pidgin on irc.freenode.net<BR><BR>"));
+				PURPLE_WEBSITE "\">" PURPLE_WEBSITE "</A><BR/><BR/>");
+	g_string_append_printf(str, _("<FONT SIZE=\"4\">FAQ:</FONT> <A HREF=\""
+				"http://developer.pidgin.im/wiki/FAQ\">"
+				"http://developer.pidgin.im/wiki/FAQ</A><BR/><BR/>"));
+	g_string_append_printf(str, _("<FONT SIZE=\"4\">Help via e-mail:</FONT>"
+				" <A HREF=\"mailto:support@pidgin.im\">support@pidgin.im</A>"
+				"<BR/><BR/>"));
+	g_string_append_printf(str, _("<FONT SIZE=\"4\">IRC Channel:</FONT> "
+				"#pidgin on irc.freenode.net<BR><BR>"));
+	g_string_append_printf(str, _("<FONT SIZE=\"4\">XMPP MUC:</FONT> "
+				"devel@conference.pidgin.im<BR><BR>"));
 
 	/* Current Developers */
 	g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
--- a/pidgin/pidgintooltip.c	Thu Feb 12 21:48:48 2009 +0000
+++ b/pidgin/pidgintooltip.c	Mon Feb 16 20:51:37 2009 +0000
@@ -135,14 +135,14 @@
 setup_tooltip_window_position(gpointer data, int w, int h)
 {
 	int sig;
-	int scr_w, scr_h, x, y;
+	int scr_w, scr_h, x, y, dy;
 #if GTK_CHECK_VERSION(2,2,0)
 	int mon_num;
 	GdkScreen *screen = NULL;
 #endif
 	GdkRectangle mon_size;
 	GtkWidget *tipwindow = pidgin_tooltip.tipwindow;
-
+	
 #if GTK_CHECK_VERSION(2,2,0)
 	gdk_display_get_pointer(gdk_display_get_default(), &screen, &x, &y, NULL);
 	mon_num = gdk_screen_get_monitor_at_point(screen, x, y);
@@ -158,6 +158,12 @@
 	mon_size.y = 0;
 #endif
 
+#if GTK_CHECK_VERSION(2,4,0)
+	dy = gdk_display_get_default_cursor_size(gdk_display_get_default()) / 2;
+#else
+	dy = 0;
+#endif
+
 #if GTK_CHECK_VERSION(2,2,0)
 	if (w > mon_size.width)
 		w = mon_size.width - 10;
@@ -168,9 +174,9 @@
 	x -= ((w >> 1) + 4);
 
 	if ((y + h + 4) > scr_h)
-		y = y - h - 5;
+		y = y - h - dy - 5;
 	else
-		y = y + 6;
+		y = y + dy + 6;
 
 	if (y < mon_size.y)
 		y = mon_size.y;
@@ -353,6 +359,7 @@
 
 	g_signal_connect(G_OBJECT(tree), "motion-notify-event", G_CALLBACK(row_motion_cb), tdata);
 	g_signal_connect(G_OBJECT(tree), "leave-notify-event", G_CALLBACK(widget_leave_cb), NULL);
+	g_signal_connect(G_OBJECT(tree), "scroll-event", G_CALLBACK(widget_leave_cb), NULL);
 	g_signal_connect_swapped(G_OBJECT(tree), "destroy", G_CALLBACK(destroy_tooltip_data), tdata);
 	return TRUE;
 }
@@ -381,6 +388,7 @@
 
 	g_signal_connect(G_OBJECT(widget), "motion-notify-event", G_CALLBACK(widget_motion_cb), wdata);
 	g_signal_connect(G_OBJECT(widget), "leave-notify-event", G_CALLBACK(widget_leave_cb), NULL);
+	g_signal_connect(G_OBJECT(widget), "scroll-event", G_CALLBACK(widget_leave_cb), NULL);
 	g_signal_connect_swapped(G_OBJECT(widget), "destroy", G_CALLBACK(destroy_tooltip_data), wdata);
 	return TRUE;
 }