changeset 25484:463d8501e4e0

merge of '021b2e6a0a1a494338bfc1491b59acee849ec048' and '123a3369e1dd05078b2707f46de36749d4147153'
author Etan Reisner <pidgin@unreliablesource.net>
date Wed, 18 Feb 2009 03:27:04 +0000
parents c1f85543556d (current diff) e859785b49d8 (diff)
children 62de664662b1
files
diffstat 22 files changed, 388 insertions(+), 135 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Thu Feb 12 06:06:40 2009 +0000
+++ b/COPYRIGHT	Wed Feb 18 03:27:04 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 06:06:40 2009 +0000
+++ b/ChangeLog	Wed Feb 18 03:27:04 2009 +0000
@@ -7,6 +7,9 @@
 	  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)
+	* Implement support for resolving DNS via the SOCKS4 proxy (SOCKS4a).
 
 	ICQ:
 	* Fix retrieval of status messages from users of ICQ 6.x, Miranda, and
@@ -15,6 +18,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 +27,45 @@
 	* 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.
+	* Buddies on both the Allow and Block list are now automatically
+	  removed from the Allow list.  Users with this problem will now no
+	  longer receive an ADL 241 error.  The problematic buddy should now
+	  appear on the buddy list and can be removed or unblocked as desired.
+
+	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 06:06:40 2009 +0000
+++ b/finch/libgnt/gntwm.c	Wed Feb 18 03:27:04 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 06:06:40 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Wed Feb 18 03:27:04 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/jabber/parser.c	Thu Feb 12 06:06:40 2009 +0000
+++ b/libpurple/protocols/jabber/parser.c	Wed Feb 18 03:27:04 2009 +0000
@@ -86,15 +86,10 @@
 			}
 		}
 		for(i=0; i < nb_attributes * 5; i+=5) {
-			const char *prefix = (const char *)attributes[i + 1];
+			const char *attrib_ns = (const char *)attributes[i+2];
 			char *txt;
 			int attrib_len = attributes[i+4] - attributes[i+3];
 			char *attrib = g_malloc(attrib_len + 1);
-			char *attrib_ns = NULL;
-
-			if (attributes[i+2]) {
-				attrib_ns = g_strdup((char*)attributes[i+2]);
-			}
 
 			memcpy(attrib, attributes[i+3], attrib_len);
 			attrib[attrib_len] = '\0';
@@ -103,11 +98,7 @@
 			attrib = purple_unescape_html(txt);
 			g_free(txt);
 			xmlnode_set_attrib_with_namespace(node, (const char*) attributes[i], attrib_ns, attrib);
-			if (prefix && *prefix) {
-				node->prefix = g_strdup(prefix);
-			}
 			g_free(attrib);
-			g_free(attrib_ns);
 		}
 
 		js->current = node;
--- a/libpurple/protocols/msn/contact.c	Thu Feb 12 06:06:40 2009 +0000
+++ b/libpurple/protocols/msn/contact.c	Wed Feb 18 03:27:04 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 06:06:40 2009 +0000
+++ b/libpurple/protocols/msn/msg.c	Wed Feb 18 03:27:04 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 06:06:40 2009 +0000
+++ b/libpurple/protocols/msn/msnutils.c	Wed Feb 18 03:27:04 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 06:06:40 2009 +0000
+++ b/libpurple/protocols/msn/nexus.c	Wed Feb 18 03:27:04 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 06:06:40 2009 +0000
+++ b/libpurple/protocols/msn/nexus.h	Wed Feb 18 03:27:04 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 06:06:40 2009 +0000
+++ b/libpurple/protocols/msn/notification.c	Wed Feb 18 03:27:04 2009 +0000
@@ -623,6 +623,18 @@
 		if (user->passport && !strcmp(user->passport, "messenger@microsoft.com"))
 			continue;
 
+		if ((user->list_op & MSN_LIST_OP_MASK) == (MSN_LIST_AL_OP | MSN_LIST_BL_OP)) {
+			/* The server will complain if we send it a user on both the
+			   Allow and Block lists. So assume they're on the Block list
+			   and remove them from the Allow list in the membership lists to
+			   stop this from happening again. */
+			purple_debug_warning("msn",
+			                     "User %s is on both Allow and Block list,"
+			                     "removing from Allow list.\n",
+			                     user->passport);
+			msn_userlist_rem_buddy_from_list(session->userlist, user->passport, MSN_LIST_AL);
+		}
+
 		msn_add_contact_xml(session, adl_node, user->passport,
 			user->list_op & MSN_LIST_OP_MASK, user->networkid);
 
@@ -1619,19 +1631,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 06:06:40 2009 +0000
+++ b/libpurple/protocols/msn/session.c	Wed Feb 18 03:27:04 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 06:06:40 2009 +0000
+++ b/libpurple/protocols/msn/session.h	Wed Feb 18 03:27:04 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 06:06:40 2009 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Wed Feb 18 03:27:04 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 06:06:40 2009 +0000
+++ b/libpurple/protocols/simple/simple.c	Wed Feb 18 03:27:04 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/proxy.c	Thu Feb 12 06:06:40 2009 +0000
+++ b/libpurple/proxy.c	Wed Feb 18 03:27:04 2009 +0000
@@ -1177,56 +1177,47 @@
 }
 
 static void
-s4_canwrite(gpointer data, gint source, PurpleInputCondition cond)
+s4_host_resolved(GSList *hosts, gpointer data, const char *error_message)
 {
-	unsigned char packet[9];
-	struct hostent *hp;
 	PurpleProxyConnectData *connect_data = data;
-	int error = ETIMEDOUT;
-	int ret;
-
-	purple_debug_info("socks4 proxy", "Connected.\n");
+	unsigned char packet[9];
+	struct sockaddr *addr;
 
-	if (connect_data->inpa > 0)
-	{
-		purple_input_remove(connect_data->inpa);
-		connect_data->inpa = 0;
-	}
+	connect_data->query_data = NULL;
 
-	ret = purple_input_get_error(connect_data->fd, &error);
-	if ((ret != 0) || (error != 0))
-	{
-		if (ret != 0)
-			error = errno;
-		purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
+	if (error_message != NULL) {
+		purple_proxy_connect_data_disconnect(connect_data, error_message);
 		return;
 	}
 
-	/*
-	 * The socks4 spec doesn't include support for doing host name
-	 * lookups by the proxy.  Some socks4 servers do this via
-	 * extensions to the protocol.  Since we don't know if a
-	 * server supports this, it would need to be implemented
-	 * with an option, or some detection mechanism - in the
-	 * meantime, stick with plain old SOCKS4.
-	 */
-	/* TODO: Use purple_dnsquery_a() */
-	hp = gethostbyname(connect_data->host);
-	if (hp == NULL) {
+	if (hosts == NULL) {
 		purple_proxy_connect_data_disconnect_formatted(connect_data,
 				_("Error resolving %s"), connect_data->host);
 		return;
 	}
 
-	packet[0] = 4;
-	packet[1] = 1;
+	/* Discard the length... */
+	hosts = g_slist_delete_link(hosts, hosts);
+	addr = hosts->data;
+	hosts = g_slist_delete_link(hosts, hosts);
+
+	packet[0] = 0x04;
+	packet[1] = 0x01;
 	packet[2] = connect_data->port >> 8;
 	packet[3] = connect_data->port & 0xff;
-	packet[4] = (unsigned char)(hp->h_addr_list[0])[0];
-	packet[5] = (unsigned char)(hp->h_addr_list[0])[1];
-	packet[6] = (unsigned char)(hp->h_addr_list[0])[2];
-	packet[7] = (unsigned char)(hp->h_addr_list[0])[3];
-	packet[8] = 0;
+	memcpy(packet + 4, &((struct sockaddr_in *)addr)->sin_addr.s_addr, 4);
+	packet[8] = 0x00;
+
+	g_free(addr);
+
+	/* We could try the other hosts, but hopefully that shouldn't be necessary */
+	while (hosts != NULL) {
+		/* Discard the length... */
+		hosts = g_slist_delete_link(hosts, hosts);
+		/* Free the address... */
+		g_free(hosts->data);
+		hosts = g_slist_delete_link(hosts, hosts);
+	}
 
 	connect_data->write_buffer = g_memdup(packet, sizeof(packet));
 	connect_data->write_buf_len = sizeof(packet);
@@ -1235,7 +1226,74 @@
 
 	connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
 
-	proxy_do_write(connect_data, connect_data->fd, cond);
+	proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
+}
+
+static void
+s4_canwrite(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleProxyConnectData *connect_data = data;
+	int error = ETIMEDOUT;
+	int ret;
+
+	purple_debug_info("socks4 proxy", "Connected.\n");
+
+	if (connect_data->inpa > 0) {
+		purple_input_remove(connect_data->inpa);
+		connect_data->inpa = 0;
+	}
+
+	ret = purple_input_get_error(connect_data->fd, &error);
+	if ((ret != 0) || (error != 0)) {
+		if (ret != 0)
+			error = errno;
+		purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
+		return;
+	}
+
+	/*
+	 * The socks4 spec doesn't include support for doing host name lookups by
+	 * the proxy.  Many socks4 servers do this via the "socks4a" extension to
+	 * the protocol.  There doesn't appear to be a way to detect if a server
+	 * supports this, so we require that the user set a global option.
+	 */
+	if (purple_prefs_get_bool("/purple/proxy/socks4_remotedns")) {
+		unsigned char packet[9];
+		int len;
+
+		purple_debug_info("socks4 proxy", "Attempting to use remote DNS.\n");
+
+		packet[0] = 0x04;
+		packet[1] = 0x01;
+		packet[2] = connect_data->port >> 8;
+		packet[3] = connect_data->port & 0xff;
+		packet[4] = 0x00;
+		packet[5] = 0x00;
+		packet[6] = 0x00;
+		packet[7] = 0x01;
+		packet[8] = 0x00;
+
+		len = sizeof(packet) + strlen(connect_data->host) + 1;
+
+		connect_data->write_buffer = g_malloc0(len);
+		memcpy(connect_data->write_buffer, packet, sizeof(packet));
+		memcpy(connect_data->write_buffer + sizeof(packet), connect_data->host, strlen(connect_data->host));
+		connect_data->write_buf_len = len;
+		connect_data->written_len = 0;
+		connect_data->read_cb = s4_canread;
+
+		connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
+
+		proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
+	} else {
+		connect_data->query_data = purple_dnsquery_a(connect_data->host,
+				connect_data->port, s4_host_resolved, connect_data);
+
+		if (connect_data->query_data == NULL) {
+			purple_debug_error("proxy", "dns query failed unexpectedly.\n");
+			purple_proxy_connect_data_destroy(connect_data);
+		}
+	}
 }
 
 static void
@@ -1396,7 +1454,7 @@
 s5_sendconnect(gpointer data, int source)
 {
 	PurpleProxyConnectData *connect_data = data;
-	int hlen = strlen(connect_data->host);
+	size_t hlen = strlen(connect_data->host);
 	connect_data->write_buf_len = 5 + hlen + 2;
 	connect_data->write_buffer = g_malloc(connect_data->write_buf_len);
 	connect_data->written_len = 0;
@@ -1479,7 +1537,7 @@
 	int i;
 	unsigned char Kxoripad[65];
 	unsigned char Kxoropad[65];
-	int pwlen;
+	size_t pwlen;
 
 	cipher = purple_ciphers_find_cipher("md5");
 	ctx = purple_cipher_context_new(cipher, NULL);
@@ -1697,7 +1755,7 @@
 			return;
 
 		msg_ret = s5_parse_chap_msg(connect_data);
-	
+
 		if (msg_ret < 0)
 			return;
 
@@ -1777,7 +1835,7 @@
 	}
 
 	if (connect_data->read_buffer[1] == 0x02) {
-		gsize i, j;
+		size_t i, j;
 		const char *u, *p;
 
 		u = purple_proxy_info_get_username(connect_data->gpi);
@@ -1810,7 +1868,7 @@
 
 		return;
 	} else if (connect_data->read_buffer[1] == 0x03) {
-		gsize userlen;
+		size_t userlen;
 		userlen = strlen(purple_proxy_info_get_username(connect_data->gpi));
 
 		connect_data->write_buf_len = 7 + userlen;
@@ -1957,7 +2015,7 @@
 
 static void try_connect(PurpleProxyConnectData *connect_data)
 {
-	size_t addrlen;
+	socklen_t addrlen;
 	struct sockaddr *addr;
 	char ipaddr[INET6_ADDRSTRLEN];
 
@@ -1969,7 +2027,7 @@
 	inet_ntop(addr->sa_family, &((struct sockaddr_in *)addr)->sin_addr,
 			ipaddr, sizeof(ipaddr));
 #else
-	memcpy(ipaddr,inet_ntoa(((struct sockaddr_in *)addr)->sin_addr),
+	memcpy(ipaddr, inet_ntoa(((struct sockaddr_in *)addr)->sin_addr),
 			sizeof(ipaddr));
 #endif
 	purple_debug_info("proxy", "Attempting connection to %s\n", ipaddr);
@@ -2293,6 +2351,7 @@
 	purple_prefs_add_int("/purple/proxy/port", 0);
 	purple_prefs_add_string("/purple/proxy/username", "");
 	purple_prefs_add_string("/purple/proxy/password", "");
+	purple_prefs_add_bool("/purple/proxy/socks4_remotedns", FALSE);
 
 	/* Setup callbacks for the preferences. */
 	handle = purple_proxy_get_handle();
--- a/libpurple/savedstatuses.c	Thu Feb 12 06:06:40 2009 +0000
+++ b/libpurple/savedstatuses.c	Wed Feb 18 03:27:04 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 06:06:40 2009 +0000
+++ b/pidgin/gtkblist.c	Wed Feb 18 03:27:04 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 06:06:40 2009 +0000
+++ b/pidgin/gtkconv.c	Wed Feb 18 03:27:04 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 06:06:40 2009 +0000
+++ b/pidgin/gtkdialogs.c	Wed Feb 18 03:27:04 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/gtkprefs.c	Thu Feb 12 06:06:40 2009 +0000
+++ b/pidgin/gtkprefs.c	Wed Feb 18 03:27:04 2009 +0000
@@ -1342,6 +1342,10 @@
 		purple_prefs_connect_callback(prefs, "/purple/proxy/type",
 					    proxy_changed_cb, prefs_proxy_frame);
 
+		/* This is a global option that affects SOCKS4 usage even with account-specific proxy settings */
+		pidgin_prefs_checkbox(_("Use remote DNS with SOCKS4 proxies"),
+							  "/purple/proxy/socks4_remotedns", prefs_proxy_frame);
+
 		table = gtk_table_new(4, 2, FALSE);
 		gtk_container_set_border_width(GTK_CONTAINER(table), 0);
 		gtk_table_set_col_spacings(GTK_TABLE(table), 5);
--- a/pidgin/pidgintooltip.c	Thu Feb 12 06:06:40 2009 +0000
+++ b/pidgin/pidgintooltip.c	Wed Feb 18 03:27:04 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;
 }