adium.1-3: 38bca5dc: pidgin-facebookchat at r508 (1.51+).

zacw at adiumx.com zacw at adiumx.com
Sat Jun 20 20:20:22 EDT 2009


-----------------------------------------------------------------
Revision: 38bca5dc70ae9a5a234fcb2c1bbe32b29968c588
Ancestor: d86ba5dabb2878bdc997f97140693db5db92fb32
Author: zacw at adiumx.com
Date: 2009-06-21T00:19:12
Branch: im.pidgin.adium.1-3
URL: http://d.pidgin.im/viewmtn/revision/info/38bca5dc70ae9a5a234fcb2c1bbe32b29968c588

Modified files:
        libpurple/protocols/facebook/facebook.nsi
        libpurple/protocols/facebook/fb_blist.c
        libpurple/protocols/facebook/fb_connection.c
        libpurple/protocols/facebook/fb_messages.c
        libpurple/protocols/facebook/fb_messages.h
        libpurple/protocols/facebook/fb_notifications.c
        libpurple/protocols/facebook/libfacebook.c
        libpurple/protocols/facebook/libfacebook.h
        libpurple/protocols/facebook/pidgin-facebookchat.rc
        libpurple/protocols/facebook/rss.xml

ChangeLog: 

pidgin-facebookchat at r508 (1.51+).


-------------- next part --------------
============================================================
--- libpurple/protocols/facebook/facebook.nsi	3fcdc6328140c152a720ace476cd1a7922e7f1e4
+++ libpurple/protocols/facebook/facebook.nsi	67598b3e9ead8be15c1e600b6dd3de05c0dd682e
@@ -6,7 +6,7 @@ SetCompress off
 ; todo: SetBrandingImage
 ; HM NIS Edit Wizard helper defines
 !define PRODUCT_NAME "pidgin-facebookchat"
-!define PRODUCT_VERSION "1.50"
+!define PRODUCT_VERSION "1.51"
 !define PRODUCT_PUBLISHER "Eion Robb"
 !define PRODUCT_WEB_SITE "http://pidgin-facebookchat.googlecode.com/"
 !define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
@@ -75,7 +75,10 @@ Section "MainSection" SEC01
 	cancel:
 		Abort "Installation of pidgin-facebookchat aborted"
 	after_copy:
-		
+	
+	SetOutPath "$PidginDir"
+	File "libjson-glib-1.0.dll"
+	
 SectionEnd
 
 Function GetPidginInstPath
============================================================
--- libpurple/protocols/facebook/fb_blist.c	4a02f018ba3dac7cc7bc76cac9921751dacf09f3
+++ libpurple/protocols/facebook/fb_blist.c	5074f94eb56fc5bfe2337b0e991d3ef9d0f20361
@@ -22,10 +22,12 @@
 #include "fb_connection.h"
 #include "fb_blist.h"
 
-static void set_buddies_offline(PurpleBuddy *buddy, GSList *online_buddies_list)
+#include <json-glib/json-glib.h>
+
+static void set_buddies_offline(PurpleBuddy *buddy, GHashTable *online_buddies_list)
 {
-	if (g_slist_find(online_buddies_list, buddy) == NULL &&
-			PURPLE_BUDDY_IS_ONLINE(buddy))
+	if (PURPLE_BUDDY_IS_ONLINE(buddy) &&
+		g_hash_table_lookup(online_buddies_list, buddy->name) == NULL)
 	{
 		purple_prpl_got_user_status(buddy->account, buddy->name,
 				purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE),
@@ -61,7 +63,7 @@ static void got_buddy_list_cb(FacebookAc
 		gsize data_len, gpointer userdata)
 {
 	GSList *buddies_list;
-	GSList *online_buddies_list = NULL;
+	GHashTable *online_buddies_list = g_hash_table_new(g_str_hash, g_str_equal);
 	PurpleBuddy *buddy;
 	FacebookBuddy *fbuddy;
 	gchar *uid;
@@ -72,17 +74,14 @@ static void got_buddy_list_cb(FacebookAc
 	gboolean idle;
 	guint32 error_number;
 
-	gchar *search_start;
 	gchar *search_tmp;
 	gchar *tmp;
-	gchar *largest_buddy_search_point = NULL;
 
 	PurpleGroup *fb_group = NULL;
 
 	gboolean current_buddy_online = FALSE;
 
 	purple_debug_info("facebook", "parsing buddy list\n");
-	purple_debug_misc("facebook", "buddy list\n%s\n", data);
 
 	if (fba == NULL)
 		return;
@@ -93,6 +92,29 @@ static void got_buddy_list_cb(FacebookAc
 				_("Could not retrieve buddy list"));
 		return;
 	}
+	
+	purple_debug_misc("facebook", "buddy list\n%s\n", data);
+	
+	tmp = g_strstr_len(data, data_len, "for (;;);");
+	if (tmp)
+	{
+		tmp += strlen("for (;;);");
+	}
+	
+	JsonParser *parser;
+	JsonNode *root;
+	
+	parser = json_parser_new();
+	if(!json_parser_load_from_data(parser, tmp, -1, NULL))
+	{
+		purple_connection_error_reason(fba->pc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Could not retrieve buddy list"));
+		return;	
+	}
+	root = json_parser_get_root(parser);
+	JsonObject *objnode;
+	objnode = json_node_get_object(root);
 
 	/* Check if the facebook group already exists (fixes #13) */
 	fb_group = purple_find_group("Facebook");
@@ -103,82 +125,79 @@ static void got_buddy_list_cb(FacebookAc
 		"payload":null,"bootload":[{"name":"js\/common.js.pkg.php",
 		"type":"js","src":"http:\/\/static.ak.fbcdn.net\/rsrc.php\/pkg\/59\
 		/98561\/js\/common.js.pkg.php"}]} */
-	tmp = g_strstr_len(data, data_len, "\"error\":");
-	if (tmp != NULL)
+	if (json_object_has_member(objnode, "error"))
 	{
-		tmp += 9;
-		tmp = g_strndup(tmp, strchr(tmp, ',')-tmp);
-		error_number = atoi(tmp);
-		g_free(tmp);
+		error_number = json_node_get_int(json_object_get_member(objnode, "error"));
 		if (error_number)
 		{
-			/* error :( */
-			tmp = g_strstr_len(data, data_len, "\"errorDescription\":");
-			tmp += 20;
-			tmp = g_strndup(tmp, strchr(tmp, '"')-tmp);
-			/* TODO: Use purple_connection_error_reason() */
-			purple_connection_error(fba->pc, tmp);
-			g_free(tmp);
+			purple_connection_error_reason(fba->pc,
+					PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+					json_node_dup_string(json_object_get_member(objnode, "errorDescription")));
+			g_object_unref(parser);
 			return;
 		}
 	}
 
 	/* look for "userInfos":{ ... }, */
-	search_start = strstr(data, "\"userInfos\":{");
-	if (search_start == NULL)
+	if (!json_object_has_member(objnode, "payload"))
+	{
+		g_object_unref(parser);
 		return;
-	search_start += 13;
-
-	while (*search_start != '}' && (search_start - data < data_len))
+	}
+	objnode = json_node_get_object(json_object_get_member(objnode, "payload"));
+	if (!json_object_has_member(objnode, "buddy_list"))
 	{
-		tmp = strchr(search_start, ':');
-		uid = g_strndup(search_start+1, tmp-search_start-2);
-		/* purple_debug_misc("facebook", "uid: %s\n", uid); */
+		g_object_unref(parser);
+		return;
+	}
+	JsonObject *buddy_list = json_node_get_object(json_object_get_member(objnode, "buddy_list"));
+	if (!json_object_has_member(buddy_list, "userInfos"))
+	{
+		g_object_unref(parser);
+		return;
+	}
+	JsonObject *notifications = json_node_get_object(json_object_get_member(objnode, "notifications"));
+	
+	JsonObject *userInfos;
+	JsonObject *nowAvailableList;
+	userInfos = json_node_get_object(json_object_get_member(buddy_list, "userInfos"));
+	nowAvailableList = json_node_get_object(json_object_get_member(buddy_list, "nowAvailableList"));
+	GList *userIds;
+	userIds = json_object_get_members(userInfos);
+	GList *currentUserNode;
+	for(	currentUserNode = userIds;
+		currentUserNode;
+		currentUserNode = g_list_next(currentUserNode))
+	{
+		uid = currentUserNode->data;
+		purple_debug_misc("facebook", "uid: %s\n", uid);
 
-		search_start += strlen(uid) + 2;
+		JsonObject *userInfo;
+		userInfo = json_node_get_object(json_object_get_member(userInfos, uid));
+		name = json_node_dup_string(json_object_get_member(userInfo, "name"));
+		purple_debug_misc("facebook", "name: %s\n", name);
 
-		search_tmp = strstr(search_start, "\"name\":") + 8;
-		if (search_tmp > largest_buddy_search_point)
-			largest_buddy_search_point = search_tmp;
-		search_tmp = g_strndup(search_tmp, strchr(search_tmp, '"')-search_tmp);
-		name = fb_convert_unicode(search_tmp);
-		g_free(search_tmp);
-		/* purple_debug_misc("facebook", "name: %s\n", name); */
-
 		/* try updating the alias, just in case it was removed locally */
 		serv_got_alias(fba->pc, uid, name);
 
 		/* look for "uid":{"i":_____} */
-		tmp = g_strdup_printf("\"%s\":{\"i\":", uid);
-		search_tmp = g_strstr_len(data, data_len, tmp);
-		if (search_tmp != NULL)
+		if (json_object_has_member(nowAvailableList, uid))
 		{
-			search_tmp += strlen(tmp);
-			if (search_tmp > largest_buddy_search_point)
-				largest_buddy_search_point = search_tmp;
-			search_tmp = g_strndup(search_tmp, strchr(search_tmp, '}')-search_tmp);
-			/* purple_debug_misc("facebook", "buddy idle: %s\n", search_tmp); */
-			buddy = purple_find_buddy(fba->account, uid);
-			idle = g_str_equal(search_tmp, "true");
-			g_free(search_tmp);
+			JsonObject *userBlistInfo;
+			userBlistInfo = json_node_get_object(json_object_get_member(nowAvailableList, uid));
+			idle = json_node_get_boolean(json_object_get_member(userBlistInfo, "i"));
+			purple_debug_misc("facebook", "buddy idle: %s\n", (idle?"true":"false"));
 			current_buddy_online = TRUE;
 		} else {
 			/* if we're here, the buddy's info has been sent, but they're not actually online */
 			current_buddy_online = FALSE;
 			idle = FALSE;
 		}
-		g_free(tmp);
 
 		/* Set the buddy status text and time */
-		search_tmp = strstr(search_start, "\"status\":");
-		if (search_tmp != NULL && *(search_tmp + 9) == '"')
+		if (json_object_has_member(userInfo, "status"))
 		{
-			search_tmp += 10;
-			if (search_tmp > largest_buddy_search_point)
-				largest_buddy_search_point = strstr(search_tmp, ",\"statusTime");
-			search_tmp = g_strndup(search_tmp, strstr(search_tmp, ",\"statusTime")-1-search_tmp);
-			status_text = fb_convert_unicode(search_tmp);
-			g_free(search_tmp);
+			status_text = json_node_dup_string(json_object_get_member(userInfo, "status"));
 		} else {
 			status_text = NULL;
 		}
@@ -197,12 +216,6 @@ static void got_buddy_list_cb(FacebookAc
 			{
 				g_free(status_text);
 				g_free(name);
-				g_free(uid);
-
-				/* Move pointer to the end of the buddy entry */
-				search_start = strchr(largest_buddy_search_point, '}') + 1;
-				while (*search_start == ',' && (search_start - data < data_len))
-					search_start++;
 				/* go on to the next buddy */
 				continue;
 			} else {
@@ -244,7 +257,6 @@ static void got_buddy_list_cb(FacebookAc
 			fbuddy = buddy->proto_data;
 		}
 
-		g_free(uid);
 		g_free(name);
 
 		if (status_text != NULL)
@@ -252,16 +264,10 @@ static void got_buddy_list_cb(FacebookAc
 			tmp = fb_strdup_withhtml(status_text);
 			g_free(status_text);
 			status_text = tmp;
-			/* purple_debug_misc("facebook", "status: %s\n", status_text); */
+			purple_debug_misc("facebook", "status: %s\n", status_text);
 
-			search_tmp = strstr(search_start, "\"statusTimeRel\":") + 17;
-			if (search_tmp > largest_buddy_search_point)
-				largest_buddy_search_point = strchr(search_tmp, '"');
-			search_tmp = g_strndup(search_tmp, strchr(search_tmp, '"')-search_tmp);
-			status_time_text = fb_convert_unicode(search_tmp);
-			g_free(search_tmp);
-
-			if (g_str_equal(status_time_text, "ull,"))
+			status_time_text = json_node_dup_string(json_object_get_member(userInfo, "statusTimeRel"));
+			if (strlen(status_time_text) == 0)
 			{
 				g_free(status_time_text);
 				status_time_text = NULL;
@@ -270,8 +276,7 @@ static void got_buddy_list_cb(FacebookAc
 			if (status_time_text != NULL)
 			{
 				fbuddy->status_rel_time = fb_strdup_withhtml(status_time_text);
-				g_free(status_time_text);
-				/* purple_debug_misc("facebook", "status time: %s\n", fbuddy->status_rel_time); */
+				purple_debug_misc("facebook", "status time: %s\n", fbuddy->status_rel_time);
 			} else {
 				fbuddy->status_rel_time = NULL;
 			}
@@ -301,10 +306,7 @@ static void got_buddy_list_cb(FacebookAc
 		}
 
 		/* Set the buddy icon (if it hasn't changed) */
-		search_tmp = strstr(search_start, "\"thumbSrc\":") + 12;
-		if (search_tmp > largest_buddy_search_point)
-			largest_buddy_search_point = search_tmp;
-		buddy_icon_url = g_strndup(search_tmp, strchr(search_tmp, '"')-search_tmp);
+		buddy_icon_url = json_node_dup_string(json_object_get_member(userInfo, "thumbSrc"));
 		if (fbuddy->thumb_url == NULL || !g_str_equal(fbuddy->thumb_url, buddy_icon_url))
 		{
 			g_free(fbuddy->thumb_url);
@@ -345,20 +347,14 @@ static void got_buddy_list_cb(FacebookAc
 		if (current_buddy_online)
 		{
 			/* Add buddy to the list of online buddies */
-			online_buddies_list = g_slist_append(online_buddies_list, buddy);
+			g_hash_table_insert(online_buddies_list, buddy->name, buddy);
 
 			/* Update the display of the buddy in the buddy list and make the user online */
 			if (!PURPLE_BUDDY_IS_ONLINE(buddy))
 				purple_prpl_got_user_status(fba->account, buddy->name, purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE), NULL);
 		}
-
-		/* Move pointer after any user configurable data */
-		search_start = search_tmp;
-		/* Move pointer to the end of the buddy entry */
-		search_start = strchr(largest_buddy_search_point, '}') + 1;
-		while (*search_start == ',' && (search_start - data < data_len))
-			search_start++;
 	}
+	g_list_free(userIds);
 
 	buddies_list = purple_find_buddies(fba->account, NULL);
 	if (buddies_list != NULL)
@@ -366,7 +362,25 @@ static void got_buddy_list_cb(FacebookAc
 		g_slist_foreach(buddies_list, (GFunc)set_buddies_offline, online_buddies_list);
 		g_slist_free(buddies_list);
 	}
-	g_slist_free(online_buddies_list);
+	g_hash_table_destroy(online_buddies_list);
+	
+	if (notifications != NULL)
+	{
+		JsonNode *inboxCount_node = json_object_get_member(notifications, "inboxCount");
+		if (inboxCount_node)
+		{
+			gint inbox_count = json_node_get_int(inboxCount_node);
+			if (inbox_count && inbox_count != fba->last_inbox_count)
+			{
+				fba->last_inbox_count = inbox_count;
+				gchar *url = g_strdup("http://www.facebook.com/inbox/");
+				purple_notify_emails(fba->pc, inbox_count, FALSE, NULL, NULL, (const char**) &(fba->account->username), (const char**) &(url), NULL, NULL);
+				g_free(url);
+			}
+		}
+	}
+	
+	g_object_unref(parser);
 }
 
 gboolean fb_get_buddy_list(gpointer data)
@@ -377,7 +391,7 @@ gboolean fb_get_buddy_list(gpointer data
 	fba = data;
 
 	postdata = g_strdup_printf(
-			"user=%d&popped_out=true&force_render=true&buddy_list=1",
+			"user=%d&popped_out=true&force_render=true&buddy_list=1&notifications=1",
 			fba->uid);
 	fb_post_or_get(fba, FB_METHOD_POST, NULL, "/ajax/presence/update.php",
 			postdata, got_buddy_list_cb, NULL, FALSE);
============================================================
--- libpurple/protocols/facebook/fb_connection.c	47f7c43df1f9af82cbaf76849d65aeb1671b7f4d
+++ libpurple/protocols/facebook/fb_connection.c	8668d97080d9afe72ca0519bb5f0ab641daa34c5
@@ -23,40 +23,80 @@ static void fb_attempt_connection(Facebo
 static void fb_attempt_connection(FacebookConnection *);
 
 #ifdef HAVE_ZLIB
-static guchar *fb_gunzip(const guchar *gzip_data, ssize_t *len_ptr)
+
+static gchar *fb_gunzip(const guchar *gzip_data, ssize_t *len_ptr)
 {
 	gsize gzip_data_len	= *len_ptr;
 	z_stream zstr;
 	int gzip_err = 0;
-	guchar *output_data;
+	gchar *data_buffer;
 	gulong gzip_len = G_MAXUINT16;
+	GString *output_string = NULL;
 
-	g_return_val_if_fail(zlib_inflate != NULL, NULL);
+	data_buffer = g_new0(gchar, gzip_len);
 
-	output_data = g_new0(guchar, gzip_len);
-
-	zstr.next_in = gzip_data;
-	zstr.avail_in = gzip_data_len;
+	zstr.next_in = NULL;
+	zstr.avail_in = 0;
 	zstr.zalloc = Z_NULL;
 	zstr.zfree = Z_NULL;
-	zstr.opaque = Z_NULL;
-	int flags = gzip_data[3];
-	int offset = 4;
-	/* if (flags & 0x04) offset += *tmp[] */
-	zstr.next_in += offset;
-	zstr.avail_in -= offset;
-	zlib_inflateInit2_(&zstr, -MAX_WBITS, ZLIB_VERSION, sizeof(z_stream));
-	zstr.next_out = output_data;
+	zstr.opaque = 0;
+	gzip_err = inflateInit2(&zstr, MAX_WBITS+32);
+	if (gzip_err != Z_OK)
+	{
+		g_free(data_buffer);
+		purple_debug_error("facebook", "no built-in gzip support in zlib\n");
+		return NULL;
+	}
+	
+	zstr.next_in = (Bytef *)gzip_data;
+	zstr.avail_in = gzip_data_len;
+	
+	zstr.next_out = (Bytef *)data_buffer;
 	zstr.avail_out = gzip_len;
-	gzip_err = zlib_inflate(&zstr, Z_FINISH);
-	zlib_inflateEnd(&zstr);
+	
+	gzip_err = inflate(&zstr, Z_SYNC_FLUSH);
 
-	purple_debug_info("facebook", "gzip len: %ld, len: %ld\n", gzip_len,
-			gzip_data_len);
-	purple_debug_info("facebook", "gzip flags: %d\n", flags);
-	purple_debug_info("facebook", "gzip error: %d\n", gzip_err);
+	if (gzip_err == Z_DATA_ERROR)
+	{
+		inflateEnd(&zstr);
+		inflateInit2(&zstr, -MAX_WBITS);
+		if (gzip_err != Z_OK)
+		{
+			g_free(data_buffer);
+			purple_debug_error("facebook", "Cannot decode gzip header\n");
+			return NULL;
+		}
+		zstr.next_in = (Bytef *)gzip_data;
+		zstr.avail_in = gzip_data_len;
+		zstr.next_out = (Bytef *)data_buffer;
+		zstr.avail_out = gzip_len;
+		gzip_err = inflate(&zstr, Z_SYNC_FLUSH);
+	}
+	output_string = g_string_new("");
+	while (gzip_err == Z_OK)
+	{
+		//append data to buffer
+		output_string = g_string_append_len(output_string, data_buffer, gzip_len - zstr.avail_out);
+		//reset buffer pointer
+		zstr.next_out = (Bytef *)data_buffer;
+		zstr.avail_out = gzip_len;
+		gzip_err = inflate(&zstr, Z_SYNC_FLUSH);
+	}
+	if (gzip_err == Z_STREAM_END)
+	{
+		output_string = g_string_append_len(output_string, data_buffer, gzip_len - zstr.avail_out);
+	} else {
+		purple_debug_error("facebook", "gzip inflate error\n");
+	}
+	inflateEnd(&zstr);
 
-	*len_ptr = gzip_len;
+	g_free(data_buffer);	
+
+	gchar *output_data = g_strdup(output_string->str);
+	*len_ptr = output_string->len;
+
+	g_string_free(output_string, TRUE);
+
 	return output_data;
 }
 #endif
@@ -152,13 +192,10 @@ static void fb_connection_process_data(F
 		if (strstr(fbconn->rx_buf, "Content-Encoding: gzip"))
 		{
 			/* we've received compressed gzip data, decompress */
-			if (zlib_inflate != NULL)
-			{
-				gchar *gunzipped;
-				gunzipped = fb_gunzip((const guchar *)tmp, &len);
-				g_free(tmp);
-				tmp = gunzipped;
-			}
+			gchar *gunzipped;
+			gunzipped = fb_gunzip((const guchar *)tmp, &len);
+			g_free(tmp);
+			tmp = gunzipped;
 		}
 #endif
 	}
@@ -456,6 +493,9 @@ void fb_post_or_get(FacebookAccount *fba
 
 	cookies = fb_cookies_to_string(fba);
 	user_agent = purple_account_get_string(fba->account, "user-agent", "Opera/9.50 (Windows NT 5.1; U; en-GB)");
+	
+	if (method & FB_METHOD_POST && !postdata)
+		postdata = "";
 
 	/* Build the request */
 	request = g_string_new(NULL);
@@ -476,8 +516,7 @@ void fb_post_or_get(FacebookAccount *fba
 	g_string_append_printf(request, "Accept: */*\r\n");
 	g_string_append_printf(request, "Cookie: isfbe=false;%s\r\n", cookies);
 #ifdef HAVE_ZLIB
-	if (zlib_inflate != NULL)
-		g_string_append_printf(request, "Accept-Encoding: gzip\r\n");
+	g_string_append_printf(request, "Accept-Encoding: gzip\r\n");
 #endif
 	if (is_proxy == TRUE)
 	{
@@ -485,7 +524,7 @@ void fb_post_or_get(FacebookAccount *fba
 			purple_proxy_info_get_password(proxy_info))
 		{
 			proxy_auth = g_strdup_printf("%s:%s", purple_proxy_info_get_username(proxy_info), purple_proxy_info_get_password(proxy_info));
-			proxy_auth_base64 = purple_base64_encode(proxy_auth, strlen(proxy_auth));
+			proxy_auth_base64 = purple_base64_encode((guchar *)proxy_auth, strlen(proxy_auth));
 			g_string_append_printf(request, "Proxy-Authorization: Basic %s\r\n", proxy_auth_base64);
 			g_free(proxy_auth_base64);
 			g_free(proxy_auth);
============================================================
--- libpurple/protocols/facebook/fb_messages.c	1d28c3f65c157319609995aea0eadb7ac6e9ab79
+++ libpurple/protocols/facebook/fb_messages.c	f5bc0e080df2dcc102667479e7940791008251bd
@@ -21,6 +21,8 @@
 #include "fb_messages.h"
 #include "fb_connection.h"
 
+#include <json-glib/json-glib.h>
+
 typedef struct _FacebookOutgoingMessage FacebookOutgoingMessage;
 
 struct _FacebookOutgoingMessage {
@@ -36,6 +38,7 @@ static gboolean fb_get_new_messages(Face
 static gboolean fb_send_im_fom(FacebookOutgoingMessage *msg);
 static gboolean fb_resend_im_fom(FacebookOutgoingMessage *msg);
 static gboolean fb_get_new_messages(FacebookAccount *fba);
+static gchar *fb_replace_styled_text(const gchar *text);
 
 static FacebookOutgoingMessage *fb_msg_create(FacebookAccount *fba)
 {
@@ -69,6 +72,7 @@ static void got_new_messages(FacebookAcc
 static void got_new_messages(FacebookAccount *fba, gchar *data,
 		gsize data_len, gpointer userdata)
 {
+	gchar *message;
 	gchar *message_text;
 	gchar *message_time;
 	gchar *from;
@@ -137,15 +141,14 @@ static void got_new_messages(FacebookAcc
 		purple_debug_error("facebook",
 				"got data back, but it's not even json\n");
 				
-		purple_timeout_add_seconds(1, (GSourceFunc)fb_get_new_messages, fba);
+		fb_get_new_messages(fba);
 		return;
 	}
 
-	/* refresh means that the session or post_form_id is invalid */
+	/* refresh means that the channel is invalid */
 	if (g_str_equal(data, "for (;;);{\"t\":\"refresh\"}"))
 	{
-		if (fba->post_form_id_refresh_timer == 0)
-			fba->post_form_id_refresh_timer = purple_timeout_add_seconds(1, (GSourceFunc)fb_get_post_form_id, fba);
+		fb_reconnect(fba);
 		return;
 	}
 
@@ -153,7 +156,7 @@ static void got_new_messages(FacebookAcc
 	if (g_str_equal(data, "for (;;);{\"t\":\"continue\"}"))
 	{
 		/* Continue looping, waiting for more messages */
-		purple_timeout_add_seconds(1, (GSourceFunc)fb_get_new_messages, fba);
+		fb_get_new_messages(fba);
 		return;
 	}
 
@@ -220,8 +223,6 @@ static void got_new_messages(FacebookAcc
 			if (from && to && g_str_equal(type, "msg"))
 			{
 				/* IM message */
-				if (fba->uid != atoi(from) || fba->uid == atoi(to))
-				{
 					tmp = strstr(start, "\"msgID\":");
 					tmp += 9;
 					tmp = g_strndup(tmp, strchr(tmp, '"') - tmp);
@@ -247,9 +248,10 @@ static void got_new_messages(FacebookAcc
 						tmp = strstr(start, "\"text\":\"");
 						tmp += 8;
 						tmp = g_strndup(tmp, strstr(tmp, "\",\"time\":") - tmp);
-						message_text = fb_convert_unicode(tmp);
+						message = fb_convert_unicode(tmp);
 						g_free(tmp);
-						tmp = fb_strdup_withhtml(message_text);
+						message_text = fb_strdup_withhtml(message);
+						tmp = fb_replace_styled_text(message_text);
 						g_free(message_text);
 						message_text = tmp;
 						purple_debug_info("facebook", "text: %s\n", message_text);
@@ -272,10 +274,13 @@ static void got_new_messages(FacebookAcc
 								g_free(tmp);
 							}
 						}
-
-						serv_got_im(pc, from, message_text, PURPLE_MESSAGE_RECV, atoi(message_time));
-
 						
+						if (fba->uid != atoi(from) || fba->uid == atoi(to))
+							serv_got_im(pc, from, message_text, PURPLE_MESSAGE_RECV, atoi(message_time));
+						else if (!g_hash_table_remove(fba->sent_messages_hash, message))
+							serv_got_im(pc, to, message_text, PURPLE_MESSAGE_SEND, atoi(message_time));
+						g_free(message);
+						
 						/*
 						 * Acknowledge receipt of the message by simulating
 						 * focusing the window.  Not sure what the window_id
@@ -283,7 +288,7 @@ static void got_new_messages(FacebookAcc
 						 * something internal to the Facebook javascript that
 						 * is used for maintaining UI state across page loads?
 						 */
-						if (!fba->is_idle)
+						if (!fba->is_idle && fba->uid != atoi(from))
 						{
 							gchar *postdata;
 	
@@ -299,7 +304,7 @@ static void got_new_messages(FacebookAcc
 						g_free(message_text);
 						g_free(message_time);
 					}
-				}
+				
 				start = strchr(start, '}')+1;
 			} else if (from && g_str_equal(type, "typ"))
 			{
@@ -398,10 +403,14 @@ static void fb_send_im_cb(FacebookAccoun
 	FacebookOutgoingMessage *msg = user_data;
 	gchar *error_summary = NULL;
 	gchar *tmp;
+	gboolean was_an_error = FALSE;
 
 	/* NULL data crashes on Windows */
 	if (data == NULL)
+	{
+		was_an_error = TRUE;
 		data = "(null)";
+	}
 	
 	purple_debug_misc("facebook", "sent im response: %s\n", data);
 	/* for (;;);{"error":1356003,"errorSummary":"Send destination not online",
@@ -443,11 +452,17 @@ static void fb_send_im_cb(FacebookAccoun
 							fba->account, msg->who);
 					purple_conversation_write(conv, NULL, error_summary,
 							PURPLE_MESSAGE_ERROR, msg->time);
+					was_an_error = TRUE;
 				}
 			}
 		}
 	}
 
+	if (was_an_error)
+	{
+		g_hash_table_remove(fba->sent_messages_hash, msg->message);
+	}
+
 	g_free(error_summary);
 	fb_msg_destroy(msg);
 }
@@ -480,8 +495,9 @@ int fb_send_im(PurpleConnection *pc, con
 int fb_send_im(PurpleConnection *pc, const gchar *who, const gchar *message, PurpleMessageFlags flags)
 {
 	FacebookOutgoingMessage *msg;
+	FacebookAccount *fba = pc->proto_data;
 
-	msg = fb_msg_create(pc->proto_data);
+	msg = fb_msg_create(fba);
 
 	/* convert html to plaintext, removing trailing spaces */
 	msg->message = purple_markup_strip_html(message);
@@ -496,16 +512,94 @@ int fb_send_im(PurpleConnection *pc, con
 	msg->time = time(NULL);
 	msg->retry_count = 0;
 
+	//save that we're sending the message
+	g_hash_table_insert(fba->sent_messages_hash, strdup(msg->message), NULL);
+
 	fb_send_im_fom(msg);
 
-	return strlen(message);
+	/* gchar *styled_message = fb_replace_styled_text(msg->message);
+	serv_got_im(pc, msg->who, styled_message, PURPLE_MESSAGE_SEND, msg->time);
+	g_free(styled_message);
+
+	return 0; */
+	return 1;
 }
 
+void got_reconnect_json(FacebookAccount *fba, gchar *data, gsize data_len, gpointer userdata)
+{
+	gchar *new_channel_number;
+	
+	JsonParser *parser;
+	JsonNode *root;
+
+	if (data == NULL)
+		data = "(null)";	
+
+	gchar *tmp = g_strstr_len(data, data_len, "for (;;);");
+	if (tmp)
+	{
+		tmp += strlen("for (;;);");
+	}
+	
+	parser = json_parser_new();
+	if(!json_parser_load_from_data(parser, tmp, -1, NULL))
+	{
+		purple_debug_error("facebook", "couldn't parse reconnect data\n");
+		purple_debug_info("facebook", "page content: %s\n", data);
+		purple_connection_error_reason(fba->pc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Chat service currently unavailable"));
+		g_object_unref(parser);
+		return;
+	}
+	root = json_parser_get_root(parser);
+	JsonObject *objnode;
+	objnode = json_node_get_object(root);
+
+	JsonObject *payload = json_node_get_object(json_object_get_member(objnode, "payload"));
+	
+	/* eg {"host":"channel01"} */
+	const gchar *new_channel_host = json_node_get_string(json_object_get_member(payload, "host"));
+
+	if (new_channel_host == NULL)
+	{
+		purple_debug_error("facebook", "couldn't find new channel number\n");
+		purple_debug_info("facebook", "page content: %s\n", data);
+		purple_connection_error_reason(fba->pc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Chat service currently unavailable"));
+		g_object_unref(parser);
+		return;
+	}
+	
+	new_channel_number = g_strdup(&new_channel_host[7]);
+	g_free(fba->channel_number);
+	fba->channel_number = new_channel_number;
+	
+	gint new_seq = json_node_get_int(json_object_get_member(payload, "seq"));
+	fba->message_fetch_sequence = new_seq;
+	
+	/*
+	 * Now that we have a channel number we can start looping and
+	 * waiting for messages
+	 */
+	fb_get_new_messages(fba);
+	g_object_unref(parser);
+}
+
+gboolean fb_reconnect(FacebookAccount *fba)
+{
+	gchar *url = g_strdup_printf("/ajax/presence/reconnect.php?post_form_id=%s", fba->post_form_id);
+	fb_post_or_get(fba, FB_METHOD_GET, NULL, url, NULL, got_reconnect_json, NULL, FALSE);
+	g_free(url);
+	
+	return FALSE;
+}
+
 static void got_form_id_page(FacebookAccount *fba, gchar *data, gsize data_len, gpointer userdata)
 {
 	const gchar *start_text = "id=\"post_form_id\" name=\"post_form_id\" value=\"";
 	gchar *post_form_id;
-	gchar *channel_number;
 	gchar *tmp = NULL;
 	
 	/* NULL data crashes on Windows */
@@ -529,46 +623,47 @@ static void got_form_id_page(FacebookAcc
 	g_free(fba->post_form_id);
 	fba->post_form_id = post_form_id;
 
-	/* search for channel server number. we might want to use
-         * /ajax/presence/reconnect.php in the future */
-	start_text = "\", \"channel";
-	tmp = g_strstr_len(data, data_len, start_text);
-	if (tmp == NULL)
-	{
-		/* Some proxies strip whitepsace */
-		start_text = "\",\"channel";
-		tmp = g_strstr_len(data, data_len, start_text);
-		if (tmp == NULL)
-		{
-			purple_debug_error("facebook", "couldn't find channel\n");
-			purple_debug_misc("facebook", "page content: %s\n", data);
-			purple_connection_error_reason(fba->pc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Chat service currently unavailable."));
-			return;
-		}
-	}
-	
-	tmp += strlen(start_text);
-	channel_number = g_strndup(tmp, strchr(tmp, '"') - tmp);
-	
-	g_free(fba->channel_number);
-	fba->channel_number = channel_number;
 
 	tmp = g_strdup_printf("visibility=true&post_form_id=%s", post_form_id);
 	fb_post_or_get(fba, FB_METHOD_POST, "apps.facebook.com", "/ajax/chat/settings.php", tmp, NULL, NULL, FALSE);
 	g_free(tmp);
-
-	/*
-	 * Now that we have a channel number we can start looping and
-	 * waiting for messages
-	 */
-	fb_get_new_messages(fba);
+	
+	/* Grab new channel number */
+	fb_reconnect(fba);
 }
 
 gboolean fb_get_post_form_id(FacebookAccount *fba)
 {
-	fba->post_form_id_refresh_timer = 0;
 	fb_post_or_get(fba, FB_METHOD_GET, NULL, "/presence/popout.php", NULL, got_form_id_page, NULL, FALSE);
 	return FALSE;
 }
+
+/* Converts *text* into <b>text</b>  and _text_ into <i>text</i> */
+static gchar *fb_replace_styled_text(const gchar *text)
+{
+	if (glib_check_version(2, 14, 0))
+	{
+		return g_strdup(text);
+	} else {
+		static GRegex *underline_regex = NULL;
+		static GRegex *bold_regex = NULL;
+		gchar *midway_string;
+		gchar *output_string;
+		
+		if (underline_regex == NULL)
+		{
+			underline_regex = g_regex_new("\\b_([^_\\*]+)_\\b", G_REGEX_OPTIMIZE, 0, NULL);
+		}
+		if (bold_regex == NULL)
+		{
+			bold_regex = g_regex_new("(\\s|^)\\*([^_\\*]+)\\*(?=$|\\s)", G_REGEX_OPTIMIZE, 0, NULL);
+		}
+		
+		midway_string = g_regex_replace(underline_regex, text, -1, 0, "<u>\\1</u>", 0, NULL);
+		output_string = g_regex_replace(bold_regex, midway_string, -1, 0, "\\1<b>\\2</b>", 0, NULL);
+		g_free(midway_string);
+		
+		return output_string;
+	}
+}
+
============================================================
--- libpurple/protocols/facebook/fb_messages.h	79be7f4f904b524f43c1c45564065564b4cc1e96
+++ libpurple/protocols/facebook/fb_messages.h	aaab76c0a6d161f4e6e522833e4aae633a952997
@@ -24,6 +24,7 @@ gboolean fb_get_post_form_id(FacebookAcc
 #include "libfacebook.h"
 
 gboolean fb_get_post_form_id(FacebookAccount *fba);
+gboolean fb_reconnect(FacebookAccount *fba);
 int fb_send_im(PurpleConnection *pc, const gchar *who, const gchar *message,
 		PurpleMessageFlags flags);
 
============================================================
--- libpurple/protocols/facebook/fb_notifications.c	bb4c47d05900af3060131bb8ba4028beadcb8300
+++ libpurple/protocols/facebook/fb_notifications.c	1066c720340c2b996497a69a99de13e422bf285b
@@ -143,33 +143,20 @@ static void find_feed_url_cb(FacebookAcc
 
 static void find_feed_url_cb(FacebookAccount *fba, gchar *data, gsize data_len, gpointer userdata)
 {
-	const gchar *search_string = "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Your &quot;Facebook Notifications Feed\" href=\"";
-	const gchar *search_string2 = "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Your &amp;quot;Facebook Notifications Feed\" href=\"";
-	const gchar *search_string3 = "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Your &amp;quot;Facebook Notifications&amp;quot; Feed\" href=\"";
+	const gchar *search_string = "/feeds/notifications.php";
 	gchar *feed_url;
 	gchar *stripped;
 
 	purple_debug_info("facebook", "find_feed_url_cb\n");
 
+	if (!data)
+		data = "(null)";
+
 	feed_url = g_strstr_len(data, data_len, search_string);
-	if (feed_url)
+	if (!feed_url)
 	{
-		feed_url += strlen(search_string);
-	} else {
-		feed_url = g_strstr_len(data, data_len, search_string2);
-		if (feed_url)
-		{
-			feed_url += strlen(search_string2);
-		} else {
-			feed_url = g_strstr_len(data, data_len, search_string3);
-			if (feed_url)
-			{
-				feed_url += strlen(search_string3);
-			} else {
-				purple_debug_error("facebook", "received data, but could not find url on page\n");
-				return;
-			}
-		}
+		purple_debug_error("facebook", "received data, but could not find url on page\n");
+		return;
 	}
 
 	feed_url = g_strndup(feed_url, strchr(feed_url, '"') - feed_url);
@@ -177,10 +164,8 @@ static void find_feed_url_cb(FacebookAcc
 	/* convert &amp; to & */
 	stripped = purple_unescape_html(feed_url);
 	g_free(feed_url);
-	/* strip the host and protocol off url */
-	feed_url = g_strdup(strstr(stripped, "/feeds"));
-	g_free(stripped);
-
+	feed_url = stripped;
+	
 	purple_debug_info("facebook", "parsed feed url %s\n", feed_url);
 
 	if (feed_url && *feed_url)
============================================================
--- libpurple/protocols/facebook/libfacebook.c	4fb06c08fe0cadc12262920cccb69c59cdd6edd8
+++ libpurple/protocols/facebook/libfacebook.c	343796cc30236c62ce8522c5fe370f43924f7194
@@ -262,7 +262,9 @@ static void fb_login(PurpleAccount *acco
 	FacebookAccount *fba;
 	guint16 i;
 	gchar *postdata, *encoded_username, *encoded_password, *encoded_charset_test;
-
+	const gchar* const *languages;
+	const gchar *locale;
+	
 	/* Create account and initialize state */
 	fba = g_new0(FacebookAccount, 1);
 	fba->account = account;
@@ -273,6 +275,8 @@ static void fb_login(PurpleAccount *acco
 			g_free, g_free);
 	fba->hostname_ip_cache = g_hash_table_new_full(g_str_hash, g_str_equal,
 			g_free, g_free);
+	fba->sent_messages_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+			g_free, NULL);
 
 	g_hash_table_replace(fba->cookie_table, g_strdup("test_cookie"),
 			g_strdup("1"));
@@ -298,16 +302,20 @@ static void fb_login(PurpleAccount *acco
 	encoded_password = g_strdup(purple_url_encode(
 			purple_account_get_password(fba->account)));
 	encoded_charset_test = g_strdup(purple_url_encode("EUR,?,EUR,?,?,?,?"));
+	languages = g_get_language_names();
+	locale = languages[0];
+	if (locale == NULL || g_str_equal(locale, "C"))
+		locale = "en_US";
 
 	postdata = g_strdup_printf(
-			"email=%s&pass=%s&persistent=1&login=Login&charset_test=%s",
-			encoded_username, encoded_password, encoded_charset_test);
+			"charset_test=%s&locale=%s&email=%s&pass=%s&pass_placeHolder=Password&persistent=1&login=Login&charset_test=%s",
+			encoded_charset_test, locale, encoded_username, encoded_password, encoded_charset_test);
 	g_free(encoded_username);
 	g_free(encoded_password);
 	g_free(encoded_charset_test);
 
 	fb_post_or_get(fba, FB_METHOD_POST | FB_METHOD_SSL, "login.facebook.com",
-			"/login.php", postdata, fb_login_cb, NULL, FALSE);
+			"/login.php?login_attempt=1", postdata, fb_login_cb, NULL, FALSE);
 	g_free(postdata);
 }
 
@@ -369,9 +377,6 @@ static void fb_close(PurpleConnection *p
 	if (fba->perpetual_messages_timer) {
 		purple_timeout_remove(fba->perpetual_messages_timer);
 	}
-	if (fba->post_form_id_refresh_timer) {
-		purple_timeout_remove(fba->post_form_id_refresh_timer);
-	}
 
 	purple_debug_info("facebook", "destroying %d incomplete connections\n",
 			g_slist_length(fba->conns));
@@ -548,38 +553,11 @@ static gboolean plugin_load(PurplePlugin
 
 static gboolean plugin_load(PurplePlugin *plugin)
 {
-#ifdef HAVE_ZLIB
-	/* try dynamically loading zlib functions */
-	if (zlib_library == NULL)
-		/* zlib_library = dlopen("zlib1.dll", RTLD_LAZY); */
-		zlib_library = dlopen("libz.dll", RTLD_LAZY);
-	if (zlib_library == NULL)
-		zlib_library = dlopen("libz.so", RTLD_LAZY);
-	if (zlib_library == NULL)
-		zlib_library = dlopen("libz.dylib", RTLD_LAZY);
-	if (zlib_inflate == NULL && zlib_library != NULL)
-	{
-		zlib_inflate = (int (*)()) dlsym(zlib_library, "inflate");
-		zlib_inflateInit2_ = (int (*) ()) dlsym(zlib_library, "inflateInit2_");
-		zlib_inflateEnd = (int (*) ()) dlsym(zlib_library, "inflateEnd");
-	}
-#endif
-
 	return TRUE;
 }
 
 static gboolean plugin_unload(PurplePlugin *plugin)
 {
-#ifdef HAVE_ZLIB
-	if (zlib_library != NULL)
-	{
-		dlclose(zlib_library);
-		zlib_library = NULL;
-		zlib_inflate = NULL;
-		zlib_inflateInit2_ = NULL;
-		zlib_inflateEnd = NULL;
-	}
-#endif
 	return TRUE;
 }
 
============================================================
--- libpurple/protocols/facebook/libfacebook.h	3cd12690865ac9977b9e55a244d43fe3684dfb86
+++ libpurple/protocols/facebook/libfacebook.h	30723f2d8d98def05c0e4811c6f95f4f5242364d
@@ -21,7 +21,7 @@
 #ifndef LIBFACEBOOK_H
 #define LIBFACEBOOK_H
 
-#define FACEBOOK_PLUGIN_VERSION "1.50"
+#define FACEBOOK_PLUGIN_VERSION "1.51"
 
 #include <glib.h>
 
@@ -67,12 +67,7 @@
 #include "version.h"
 
 #ifdef HAVE_ZLIB
-/* for dynamically loading gzip uncompression */
-#include <zlib.h>
-static void *zlib_library = NULL;
-static int (*zlib_inflate)(z_streamp, int) = NULL;
-static int (*zlib_inflateEnd)(z_streamp) = NULL;
-static int (*zlib_inflateInit2_)(z_streamp, int, char *, int) = NULL;
+#	include <zlib.h>
 #endif
 
 #define FB_LAST_MESSAGE_MAX 10
@@ -107,6 +102,8 @@ struct _FacebookAccount {
 	guint perpetual_messages_timer;
 	gchar *last_status_message;
 	gboolean is_idle;
+	GHashTable *sent_messages_hash;
+	gint last_inbox_count;
 };
 
 struct _FacebookBuddy {
============================================================
--- libpurple/protocols/facebook/pidgin-facebookchat.rc	18e43509de2ce09ee5a4fde52ac334a133eb2d02
+++ libpurple/protocols/facebook/pidgin-facebookchat.rc	c5f4af2b3b3bf3614532c11fbff8e7d901d8b8ca
@@ -1,7 +1,7 @@ 1 VERSIONINFO
 
 1 VERSIONINFO
-FILEVERSION 1,50,0,0
-PRODUCTVERSION 1,50,0,0
+FILEVERSION 1,51,0,0
+PRODUCTVERSION 1,51,0,0
 FILEOS 0x40004 // VOS_NT_WINDOWS32
 FILETYPE 0x2 // VFT_DLL
 {
@@ -12,8 +12,8 @@ BLOCK "StringFileInfo"
 		VALUE "CompanyName", "Eion Robb\0"
 		VALUE "FileDescription",  "Facebook Chat plugin for Pidgin\0"
 		VALUE "ProductName", "pidgin-facebookchat\0"
-		VALUE "FileVersion", "1.50\0"
-		VALUE "ProductVersion", "1.50\0"
+		VALUE "FileVersion", "1.51\0"
+		VALUE "ProductVersion", "1.51\0"
 		VALUE "InternalName", "pidgin-facebookchat\0"
 		VALUE "OriginalFilename", "pidgin-facebookchat.dll\0"
 		VALUE "Comments", "http://pidgin-facebookchat.googlecode.com/\0"
============================================================
--- libpurple/protocols/facebook/rss.xml	d62ddd39ac317b8409bc64d84a3c848dafeb23b7
+++ libpurple/protocols/facebook/rss.xml	63a62d482440ae47b29631cfb4f7bb866184134c
@@ -13,6 +13,31 @@
 			<width>48</width><height>48</height>
 		</image>
 		<item>
+			<title>Version 1.51</title>
+			<link>http://code.google.com/p/pidgin-facebookchat/issues/detail?id=24#c44</link>
+			<description><![CDATA[Version 1.51 of the pidgin-facebookchat plugin is available for download.<br/>
+			<br/>
+			Download (as usual) from:<br/>
+			<a href="http://code.google.com/p/pidgin-facebookchat/downloads/list">
+			http://code.google.com/p/pidgin-facebookchat/downloads/list</a><br/>
+			<br/>
+			A list of the changes to the plugin is at:<br/>
+			<a href="http://code.google.com/p/pidgin-facebookchat/wiki/Changelog">
+			http://code.google.com/p/pidgin-facebookchat/wiki/Changelog</a><br/>
+			<br/>
+			If you're a package maintainer, you'll want to look at my blog post:<br/>
+			<a href="http://eion.robbmob.com/blog/2009/06/12/pidgin-facebookchat-v1-51-important-changes/">
+			http://eion.robbmob.com/blog/2009/06/12/pidgin-facebookchat-v1-51-important-changes/</a><br/>
+			(Shameless plug)<br/>
+			<br/>
+			And of course, if you like this plugin feel free to leave a comment or donation at<br/>
+			<a href="http://code.google.com/p/pidgin-facebookchat/wiki/Donate">
+			http://code.google.com/p/pidgin-facebookchat/wiki/Donate</a>
+			]]></description>
+			<pubDate>Sat, 13 June 2009 19:24:19 +1200</pubDate>
+			<guid isPermaLink="true">http://code.google.com/p/pidgin-facebookchat/issues/detail?id=24#c44</guid>
+		</item>
+		<item>
 			<title>Version 1.50</title>
 			<link>http://code.google.com/p/pidgin-facebookchat/issues/detail?id=24#c42</link>
 			<description><![CDATA[So its been a while since the last version so I've bumped it up to v1.50.


More information about the Commits mailing list