changeset 27959:fd3869a9337b

propagate from branch 'im.pidgin.pidgin' (head 8c1fa13e01e278e3a2c91c7c595ff091b87f0c3c) to branch 'im.pidgin.pidgin.yaz' (head a4c69669e1b622aaf72d11d169f8f8503d0b00b3)
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Wed, 01 Jul 2009 04:12:32 +0000
parents 40a670f599ae (diff) 518b4a900f19 (current diff)
children f058edca3d66
files libpurple/protocols/yahoo/yahoo.c libpurple/util.c
diffstat 9 files changed, 279 insertions(+), 95 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Jun 29 06:24:37 2009 +0000
+++ b/ChangeLog	Wed Jul 01 04:12:32 2009 +0000
@@ -18,6 +18,8 @@
 	  from you on MSN.
 	* DNS servers are re-read when DNS queries fail in case the system has
 	  moved to a new network and the old servers are not accessible.
+	* DNS SRV records with equal priority are sorted with respect to their
+	  weight as specified in RFC 2782.  (Vijay Raghunathan)
 	* GnuTLS logging (disabled by default) can be controlled through the
 	  PURPLE_GNUTLS_DEBUG environment variable, which is an integer between
 	  0 and 9 (higher is more verbose). Higher values may reveal sensitive
@@ -75,6 +77,7 @@
 	  (Sulabh Mahajan)
 	* Addition of MSN buddies to Yahoo accounts by adding them as
 	  'msn/buddy@somedomain.com' is now supported.  (Sulabh Mahajan)
+	* Further fixes for buddy pictures, aliases etc.
 
 	Pidgin:
 	* Added -f command line option to tell Pidgin to ignore NetworkManager
--- a/libpurple/dnssrv.c	Mon Jun 29 06:24:37 2009 +0000
+++ b/libpurple/dnssrv.c	Wed Jul 01 04:12:32 2009 +0000
@@ -94,6 +94,17 @@
 	char query[256];
 } PurpleSrvInternalQuery;
 
+typedef struct _PurpleSrvResponseContainer {
+	PurpleSrvResponse *response;
+	int sum;
+} PurpleSrvResponseContainer;
+
+/**
+ * Sort by priority, then by weight.  Strictly numerically--no
+ * randomness.  Technically we only need to sort by pref and then
+ * make sure any records with weight 0 are at the beginning of
+ * their group, but it's just as easy to sort by weight.
+ */
 static gint
 responsecompare(gconstpointer ar, gconstpointer br)
 {
@@ -112,6 +123,129 @@
 	return 1;
 }
 
+/**
+ * Iterate over a list of PurpleSrvResponseContainer making the sum
+ * the running total of the sums.  Select a random integer in the range
+ * (1, sum+1), then find the first element greater than or equal to the
+ * number selected.  From RFC 2782.
+ *
+ * @param list The list of PurpleSrvResponseContainer.  This function
+ *        removes a node from this list and returns the new list.
+ * @param container_ptr The PurpleSrvResponseContainer that was chosen
+ *        will be returned here.
+ */
+static GList *select_random_response(GList *list,
+		PurpleSrvResponseContainer **container_ptr)
+{
+	GList *cur;
+	size_t runningtotal;
+	int r;
+
+	runningtotal = 0;
+	cur = list;
+
+	while (cur) {
+		PurpleSrvResponseContainer *container = cur->data;
+		runningtotal += container->response->weight;
+		container->sum = runningtotal;
+		cur = cur->next;
+	}
+
+	/*
+	 * If the running total is greater than 0, pick a number between
+	 * 1 and the runningtotal inclusive. (This is not precisely what
+	 * the RFC algorithm describes, but we wish to deal with integers
+	 * and avoid floats.  This is functionally equivalent.)
+	 * If running total is 0, then choose r = 0.
+	 */
+	r = runningtotal ? g_random_int_range(1, runningtotal + 1) : 0;
+	cur = list;
+	while (r > ((PurpleSrvResponseContainer *)cur->data)->sum) {
+		cur = cur->next;
+	}
+
+	/* Set the return parameter and remove cur from the list */
+	*container_ptr =  cur->data;
+	return g_list_delete_link(list, cur);
+}
+
+/**
+ * Reorder a GList of PurpleSrvResponses that have the same priority
+ * (aka "pref").
+ */
+static void srv_reorder(GList *list, int num)
+{
+	int i;
+	GList *cur;
+	GList *container_list = NULL;
+	PurpleSrvResponseContainer *container;
+
+	if (num < 2)
+		/* Nothing to sort */
+		return;
+
+	/* First build a list of container structs */
+	for (i = 0, cur = list; i < num; i++, cur = cur->next) {
+		container = g_new(PurpleSrvResponseContainer, 1);
+		container->response = cur->data;
+		container_list = g_list_prepend(container_list, container);
+	}
+	container_list = g_list_reverse(container_list);
+
+	/*
+	 * Re-order the list that was passed in as a parameter.  We leave
+	 * the list nodes in place, but replace their data pointers.
+	 */
+	cur = list;
+	while (container_list) {
+		container_list = select_random_response(container_list, &container);
+		cur->data = container->response;
+		g_free(container);
+		cur = cur->next;
+	}
+}
+
+/**
+ * Sorts a GList of PurpleSrvResponse's according to the
+ * algorithm described in RFC 2782.
+ *
+ * @param response GList of PurpleSrvResponse's
+ * @param The original list, resorted
+ */
+static GList *purple_srv_sort(GList *list)
+{
+	GList *cur, *start;
+	int count;
+	int pref;
+
+	if (!list || !list->next)
+		/* Nothing to sort */
+		return list;
+
+	list = g_list_sort(list, responsecompare);
+
+	start = cur = list;
+	count = 1;
+	while (cur) {
+		PurpleSrvResponse *next_response;
+		pref = ((PurpleSrvResponse *)cur->data)->pref;
+		next_response = cur->next ? cur->next->data : NULL;
+		if (!next_response || next_response->pref != pref) {
+			/*
+			 * The 'count' records starting at 'start' all have the same
+			 * priority.  Sort them by weight.
+			 */
+			srv_reorder(start, count);
+			start = cur->next;
+			count = 0;
+		}
+		count++;
+		cur = cur->next;
+	}
+
+	return list;
+}
+
 #ifndef _WIN32
 
 G_GNUC_NORETURN static void
@@ -191,7 +325,7 @@
 			srvres->port = port;
 			srvres->weight = weight;
 
-			ret = g_list_insert_sorted(ret, srvres, responsecompare);
+			ret = g_list_prepend(ret, srvres);
 		} else if (query.type == T_TXT) {
 			txtres = g_new0(PurpleTxtResponse, 1);
 			txtres->content = g_strndup((gchar*)(++cp), dlen-1);
@@ -204,6 +338,10 @@
 
 end:
 	size = g_list_length(ret);
+
+	if (query.type == T_SRV)
+		ret = purple_srv_sort(ret);
+
 	/* TODO: Check return value */
 	write(out, &(query.type), sizeof(query.type));
 	write(out, &size, sizeof(size));
@@ -397,11 +535,11 @@
 				srvres->port = srv_data->wPort;
 				srvres->weight = srv_data->wWeight;
 
-				lst = g_slist_insert_sorted(lst, srvres, responsecompare);
+				lst = g_slist_prepend(lst, srvres);
 			}
 
 			MyDnsRecordListFree(dr, DnsFreeRecordList);
-			query_data->results = lst;
+			query_data->results = purple_srv_sort(lst);
 		} else if (type == DNS_TYPE_TXT) {
 			PDNS_RECORD dr_tmp;
 			GSList *lst = NULL;
--- a/libpurple/dnssrv.h	Mon Jun 29 06:24:37 2009 +0000
+++ b/libpurple/dnssrv.h	Wed Jul 01 04:12:32 2009 +0000
@@ -41,6 +41,12 @@
 	int pref;
 };
 
+/**
+ * @param resp An array of PurpleSrvResponse of size results.  The array
+ *        is sorted based on the order described in the DNS SRV RFC.
+ *        Users of this API should try each record in resp in order,
+ *        starting at the beginning.
+ */
 typedef void (*PurpleSrvCallback)(PurpleSrvResponse *resp, int results, gpointer data);
 
 /**
--- a/libpurple/protocols/yahoo/yahoo.c	Mon Jun 29 06:24:37 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Wed Jul 01 04:12:32 2009 +0000
@@ -1689,11 +1689,11 @@
 #else
 		while (split_data[++totalelements] != NULL);	
 #endif
-		if (totalelements >= 5) {
-			response_no = strtol(split_data[1], NULL, 10);
-			crumb = g_strdup(split_data[2] + strlen("crumb="));
-			yd->cookie_y = g_strdup(split_data[3] + strlen("Y="));
-			yd->cookie_t = g_strdup(split_data[4] + strlen("T="));
+		if (totalelements >= 4) {
+			response_no = strtol(split_data[0], NULL, 10);
+			crumb = g_strdup(split_data[1] + strlen("crumb="));
+			yd->cookie_y = g_strdup(split_data[2] + strlen("Y="));
+			yd->cookie_t = g_strdup(split_data[3] + strlen("T="));
 		}
 
 		g_strfreev(split_data);
@@ -1775,9 +1775,9 @@
 #else
 		while (split_data[++totalelements] != NULL);	
 #endif
-		if(totalelements >= 5) {
-			response_no = strtol(split_data[1], NULL, 10);
-			token = g_strdup(split_data[2] + strlen("ymsgr="));
+		if(totalelements >= 2) {
+			response_no = strtol(split_data[0], NULL, 10);
+			token = g_strdup(split_data[1] + strlen("ymsgr="));
 		}
 
 		g_strfreev(split_data);
@@ -2164,6 +2164,7 @@
 		}
 		else	/* we are already connected. set friend to YAHOO_P2PSTATUS_WE_ARE_CLIENT */
 			yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT);
+		g_free(who);
 		return;
 	}
 
@@ -2882,15 +2883,13 @@
 	case YAHOO_SERVICE_PICTURE:
 		yahoo_process_picture(gc, pkt);
 		break;
-	case YAHOO_SERVICE_PICTURE_UPDATE:
-		yahoo_process_picture_update(gc, pkt);
-		break;
 	case YAHOO_SERVICE_PICTURE_CHECKSUM:
 		yahoo_process_picture_checksum(gc, pkt);
 		break;
 	case YAHOO_SERVICE_PICTURE_UPLOAD:
 		yahoo_process_picture_upload(gc, pkt);
 		break;
+	case YAHOO_SERVICE_PICTURE_UPDATE:
 	case YAHOO_SERVICE_AVATAR_UPDATE:
 		yahoo_process_avatar_update(gc, pkt);
 		break;
@@ -4227,7 +4226,7 @@
 		}
 	}
 
-	msn = g_str_has_prefix(who, "msn/") || g_str_has_prefix(who, "MSN/");
+	msn = !g_strncasecmp(who, "msn/", 4);
 
 	if( strncmp(who, "+", 1) == 0 ) {
 		/* we have an sms to be sent */
@@ -4351,7 +4350,7 @@
 {
 	struct yahoo_data *yd = gc->proto_data;
 	struct yahoo_p2p_data *p2p_data;
-	gboolean msn = (g_str_has_prefix(who, "msn/") || g_str_has_prefix(who, "MSN/"));
+	gboolean msn = !g_strncasecmp(who, "msn/", 4);
 	struct yahoo_packet *pkt = NULL;
 
 	/* Don't do anything if sms is being typed */
@@ -4625,7 +4624,7 @@
 		return;
 
 	f = yahoo_friend_find(gc, bname);
-	msn = g_str_has_prefix(bname, "msn/") || g_str_has_prefix(bname, "MSN/");
+	msn = !g_strncasecmp(bname, "msn/", 4);
 
 	g = purple_buddy_get_group(buddy);
 	if (g)
--- a/libpurple/protocols/yahoo/yahoo_picture.c	Mon Jun 29 06:24:37 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_picture.c	Wed Jul 01 04:12:32 2009 +0000
@@ -153,45 +153,6 @@
 	}
 }
 
-void yahoo_process_picture_update(PurpleConnection *gc, struct yahoo_packet *pkt)
-{
-	GSList *l = pkt->hash;
-	char *who = NULL;
-	int icon = 0;
-
-	while (l) {
-		struct yahoo_pair *pair = l->data;
-
-		switch (pair->key) {
-		case 4:
-			who = pair->value;
-			break;
-		case 5:
-			/* us */
-			break;
-		/* NOTE: currently the server seems to only send 213; 206 was used
-		 * in older versions. Check whether it's still needed. */
-		case 206:
-		case 213:
-			icon = strtol(pair->value, NULL, 10);
-			break;
-		}
-		l = l->next;
-	}
-
-	if (who) {
-		if (icon == 2)
-			yahoo_send_picture_request(gc, who);
-		else if ((icon == 0) || (icon == 1)) {
-			YahooFriend *f;
-			purple_buddy_icons_set_for_user(gc->account, who, NULL, 0, NULL);
-			if ((f = yahoo_friend_find(gc, who)))
-				yahoo_friend_set_buddy_icon_need_request(f, TRUE);
-			purple_debug_misc("yahoo", "Setting user %s's icon to NULL.\n", who);
-		}
-	}
-}
-
 void yahoo_process_picture_checksum(PurpleConnection *gc, struct yahoo_packet *pkt)
 {
 	GSList *l = pkt->hash;
@@ -279,7 +240,8 @@
 		case 5:
 			/* us */
 			break;
-		case 206:
+		case 206:   /* Older versions. Still needed? */
+		case 213:   /* Newer versions */
 			/*
 			 * 0 - No icon or avatar
 			 * 1 - Using an avatar
@@ -349,8 +311,8 @@
 	struct yahoo_data *yd = gc->proto_data;
 	struct yahoo_packet *pkt;
 
-	pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPDATE, YAHOO_STATUS_AVAILABLE, 0);
-	yahoo_packet_hash(pkt, "ssi", 1, purple_connection_get_display_name(gc), 5, who, 206, type);
+	pkt = yahoo_packet_new(YAHOO_SERVICE_AVATAR_UPDATE, YAHOO_STATUS_AVAILABLE, 0);
+	yahoo_packet_hash(pkt, "si", 3, who, 213, type);
 	yahoo_packet_send_and_free(pkt, yd);
 }
 
--- a/libpurple/protocols/yahoo/yahoo_picture.h	Mon Jun 29 06:24:37 2009 +0000
+++ b/libpurple/protocols/yahoo/yahoo_picture.h	Wed Jul 01 04:12:32 2009 +0000
@@ -31,7 +31,6 @@
 void yahoo_send_picture_update_to_user(PurpleConnection *gc, const char *who, int type);
 
 void yahoo_process_picture(PurpleConnection *gc, struct yahoo_packet *pkt);
-void yahoo_process_picture_update(PurpleConnection *gc, struct yahoo_packet *pkt);
 void yahoo_process_picture_checksum(PurpleConnection *gc, struct yahoo_packet *pkt);
 void yahoo_process_picture_upload(PurpleConnection *gc, struct yahoo_packet *pkt);
 
--- a/libpurple/util.c	Mon Jun 29 06:24:37 2009 +0000
+++ b/libpurple/util.c	Wed Jul 01 04:12:32 2009 +0000
@@ -68,6 +68,7 @@
 	unsigned long len;
 	unsigned long data_len;
 	gssize max_len;
+	gboolean chunked;
 };
 
 static char *custom_user_dir = NULL;
@@ -219,6 +220,9 @@
 gchar *
 purple_base64_encode(const guchar *data, gsize len)
 {
+#if GLIB_CHECK_VERSION(2,12,0)
+	return g_base64_encode(data, len);
+#else
 	char *out, *rv;
 
 	g_return_val_if_fail(data != NULL, NULL);
@@ -253,11 +257,21 @@
 	*out = '\0';
 
 	return rv;
+#endif /* GLIB < 2.12.0 */
 }
 
 guchar *
 purple_base64_decode(const char *str, gsize *ret_len)
 {
+#if GLIB_CHECK_VERSION(2,12,0)
+	/*
+	 * We want to allow ret_len to be NULL for backward compatibility,
+	 * but g_base64_decode() requires a valid length variable.  So if
+	 * ret_len is NULL then pass in a dummy variable.
+	 */
+	gsize unused;
+	return g_base64_decode(str, ret_len != NULL ? ret_len : &unused);
+#else
 	guchar *out = NULL;
 	char tmp = 0;
 	const char *c;
@@ -319,6 +333,7 @@
 		*ret_len = len;
 
 	return out;
+#endif /* GLIB < 2.12.0 */
 }
 
 /**************************************************************************
@@ -3757,41 +3772,43 @@
 	return TRUE;
 }
 
+static const char *
+find_header_content(const char *data, size_t data_len, const char *header, size_t header_len)
+{
+	const char *p = NULL;
+
+	if (header_len <= 0)
+		header_len = strlen(header);
+
+	/* Note: data is _not_ nul-terminated.  */
+	if (data_len > header_len) {
+		if (header[0] == '\n')
+			p = (g_strncasecmp(data, header + 1, header_len - 1) == 0) ? data : NULL;
+		if (!p)
+			p = purple_strcasestr(data, header);
+		if (p)
+			p += header_len;
+	}
+
+	/* If we can find the header at all, try to sscanf it.
+	 * Response headers should end with at least \r\n, so sscanf is safe,
+	 * if we make sure that there is indeed a \n in our header.
+	 */
+	if (p && g_strstr_len(p, data_len - (p - data), "\n")) {
+		return p;
+	}
+
+	return NULL;
+}
+
 static size_t
 parse_content_len(const char *data, size_t data_len)
 {
 	size_t content_len = 0;
 	const char *p = NULL;
 
-	/* This is still technically wrong, since headers are case-insensitive
-	 * [RFC 2616, section 4.2], though this ought to catch the normal case.
-	 * Note: data is _not_ nul-terminated.
-	 */
-	if(data_len > 16) {
-		p = (strncmp(data, "Content-Length: ", 16) == 0) ? data : NULL;
-		if(!p)
-			p = (strncmp(data, "CONTENT-LENGTH: ", 16) == 0)
-				? data : NULL;
-		if(!p) {
-			p = g_strstr_len(data, data_len, "\nContent-Length: ");
-			if (p)
-				p++;
-		}
-		if(!p) {
-			p = g_strstr_len(data, data_len, "\nCONTENT-LENGTH: ");
-			if (p)
-				p++;
-		}
-
-		if(p)
-			p += 16;
-	}
-
-	/* If we can find a Content-Length header at all, try to sscanf it.
-	 * Response headers should end with at least \r\n, so sscanf is safe,
-	 * if we make sure that there is indeed a \n in our header.
-	 */
-	if (p && g_strstr_len(p, data_len - (p - data), "\n")) {
+	p = find_header_content(data, data_len, "\nContent-Length: ", sizeof("\nContent-Length: ") - 1);
+	if (p) {
 		sscanf(p, "%" G_GSIZE_FORMAT, &content_len);
 		purple_debug_misc("util", "parsed %" G_GSIZE_FORMAT "\n", content_len);
 	}
@@ -3799,6 +3816,49 @@
 	return content_len;
 }
 
+static gboolean
+content_is_chunked(const char *data, size_t data_len)
+{
+	gboolean chunked = FALSE;
+	const char *p = find_header_content(data, data_len, "\nTransfer-Encoding: ", sizeof("\nTransfer-Encoding: ") - 1);
+	if (p && g_strncasecmp(p, "chunked", 7) == 0)
+		chunked = TRUE;
+
+	return chunked;
+}
+
+/* Process in-place */
+static void
+process_chunked_data(char *data, gssize *len)
+{
+	gssize sz;
+	gssize nlen = 0;
+	char *p = data;
+	char *s = data;
+
+	while (*s) {
+		if (sscanf(s, "%x\r\n", &sz) != 1) {
+			purple_debug_error("util", "Error processing chunked data. Expected data length, found: %s\n", s);
+			break;
+		}
+		if (sz == 0)
+			break;
+		s = strstr(s, "\r\n") + 2;
+		g_memmove(p, s, sz);
+		p += sz;
+		s += sz;
+		nlen += sz;
+		if (*s != '\r' && *(s + 1) != '\n') {
+			purple_debug_error("util", "Error processing chunked data. Expected \\r\\n, found: %s\n", s);
+			break;
+		}
+		s += 2;
+	}
+	*p = 0;
+
+	if (len)
+		*len = nlen;
+}
 
 static void
 url_fetch_recv_cb(gpointer url_data, gint source, PurpleInputCondition cond)
@@ -3859,6 +3919,7 @@
 
 				/* No redirect. See if we can find a content length. */
 				content_len = parse_content_len(gfud->webdata, header_len);
+				gfud->chunked = content_is_chunked(gfud->webdata, header_len);
 
 				if(content_len == 0) {
 					/* We'll stick with an initial 8192 */
@@ -3931,6 +3992,11 @@
 		gfud->webdata = g_realloc(gfud->webdata, gfud->len + 1);
 		gfud->webdata[gfud->len] = '\0';
 
+		if (!gfud->include_headers && gfud->chunked) {
+			/* Process only if we don't want the headers. */
+			process_chunked_data(gfud->webdata, &gfud->len);
+		}
+
 		gfud->callback(gfud, gfud->user_data, gfud->webdata, gfud->len, NULL);
 		purple_util_fetch_url_cancel(gfud);
 	}
--- a/pidgin/gtkblist-theme-loader.c	Mon Jun 29 06:24:37 2009 +0000
+++ b/pidgin/gtkblist-theme-loader.c	Wed Jul 01 04:12:32 2009 +0000
@@ -20,6 +20,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "internal.h"
+
 #include <stdlib.h>
 #include <string.h>
 
--- a/po/ca.po	Mon Jun 29 06:24:37 2009 +0000
+++ b/po/ca.po	Wed Jul 01 04:12:32 2009 +0000
@@ -33,8 +33,8 @@
 msgstr ""
 "Project-Id-Version: Pidgin\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-06-25 21:33+0200\n"
-"PO-Revision-Date: 2009-06-25 21:44+0200\n"
+"POT-Creation-Date: 2009-06-29 08:35+0200\n"
+"PO-Revision-Date: 2009-06-29 08:37+0200\n"
 "Last-Translator: Josep Puigdemont i Casamajó <josep.puigdemont@gmail.com>\n"
 "Language-Team: Catalan <tradgnome@softcatala.net>\n"
 "MIME-Version: 1.0\n"
@@ -9784,8 +9784,8 @@
 msgid "Start Doodling"
 msgstr "Comença a dibuixar"
 
-msgid "Activate which ID?"
-msgstr "Quin ID voleu activar?"
+msgid "Select the ID you want to activate"
+msgstr "Seleccioneu l'ID que vulgueu activar"
 
 msgid "Join whom in chat?"
 msgstr "A qui us voleu unir al xat?"
@@ -12631,8 +12631,14 @@
 msgid "Unknown.... Please report this!"
 msgstr "Esdeveniment d'avís desconegut, informeu-nos-en."
 
-msgid "Smiley theme failed to unpack."
-msgstr "No s'ha pogut desempaquetar el tema d'emoticones."
+msgid "Theme failed to unpack."
+msgstr "No s'ha pogut desempaquetar el tema."
+
+msgid "Theme failed to load."
+msgstr "No s'ha pogut carregar el tema."
+
+msgid "Theme failed to copy."
+msgstr "No s'ha pogut copiar el tema."
 
 msgid "Install Theme"
 msgstr "Instal·la el tema"
@@ -14345,6 +14351,9 @@
 msgid "This plugin is useful for debbuging XMPP servers or clients."
 msgstr "Aquest connector és útil per a depurar servidors i clients XMPP."
 
+#~ msgid "Activate which ID?"
+#~ msgstr "Quin ID voleu activar?"
+
 #~ msgid "Account locked: Too many failed login attempts"
 #~ msgstr "S'ha blocat el compte: s'ha intentat entrar massa vegades"