adium: d49ed78a: pidgin-facebookchat at r685.

zacw at adiumx.com zacw at adiumx.com
Wed Nov 4 22:30:38 EST 2009


-----------------------------------------------------------------
Revision: d49ed78a4b14b42c77a5eca7b0ee204cf96fe91b
Ancestor: b278bb937b2e152fed6da73dc07ad5e9d2401f13
Author: zacw at adiumx.com
Date: 2009-11-05T03:29:01
Branch: im.pidgin.adium
URL: http://d.pidgin.im/viewmtn/revision/info/d49ed78a4b14b42c77a5eca7b0ee204cf96fe91b

Modified files:
        libpurple/protocols/facebook/Makefile.am
        libpurple/protocols/facebook/facebook.nsi
        libpurple/protocols/facebook/fb_blist.c
        libpurple/protocols/facebook/fb_connection.c
        libpurple/protocols/facebook/fb_conversation.c
        libpurple/protocols/facebook/fb_friendlist.c
        libpurple/protocols/facebook/fb_friendlist.h
        libpurple/protocols/facebook/fb_managefriends.c
        libpurple/protocols/facebook/fb_messages.c
        libpurple/protocols/facebook/fb_notifications.c
        libpurple/protocols/facebook/fb_util.c
        libpurple/protocols/facebook/fb_util.h
        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 r685.

-------------- next part --------------
============================================================
--- libpurple/protocols/facebook/Makefile.am	d0ee40a9e8032bf4f2e5329a45bcf4151ad30d19
+++ libpurple/protocols/facebook/Makefile.am	39a629e613b6031db6cc07318b3375b3079a131c
@@ -16,6 +16,8 @@ FACEBOOK_SOURCES = \
 	fb_friendlist.c \
 	fb_info.h \
 	fb_info.c \
+	fb_json.h \
+	fb_json.c \
 	fb_managefriends.h \
 	fb_managefriends.c \
 	fb_messages.h \
============================================================
--- libpurple/protocols/facebook/facebook.nsi	0ea82a12c41c433fc30f2431d1d6a0c9f53d1329
+++ libpurple/protocols/facebook/facebook.nsi	0a2bccf4de2f00e5527eac5cbc0facb8be28bf11
@@ -6,7 +6,7 @@ SetCompress off
 ; todo: SetBrandingImage
 ; HM NIS Edit Wizard helper defines
 !define PRODUCT_NAME "pidgin-facebookchat"
-!define PRODUCT_VERSION "1.60"
+!define PRODUCT_VERSION "1.62"
 !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}"
============================================================
--- libpurple/protocols/facebook/fb_blist.c	4bba8ded502f2a2210836c692c63af63ba0abcd0
+++ libpurple/protocols/facebook/fb_blist.c	a26776243a31152e02bde9a0405a45db552c5121
@@ -110,64 +110,6 @@ static GList *get_buddies(FacebookAccoun
 	return buddies;
 }
 
-static gboolean process_buddy_status(FacebookAccount *fba, PurpleBuddy *buddy,
-	JsonObject *userInfo)
-{
-	FacebookBuddy *fbuddy;
-	gboolean status_changed;
-
-	status_changed = FALSE;
-	fbuddy = buddy->proto_data;
-
-	if (json_object_has_member(userInfo, "status"))
-	{
-		gchar *status_text;
-		const gchar *status_time_text;
-
-		status_time_text = json_node_get_string(
-			json_object_get_member(userInfo, "statusTimeRel"));
-		status_text = fb_strdup_withhtml(json_node_get_string(
-				json_object_get_member(userInfo, "status")));
-
-		/* set our last known status so that we don't re-set it */
-		if (!fba->last_status_message &&
-		    atoll(buddy->name) == fba->uid) {
-			fba->last_status_message = g_strdup(status_text);
-		}
-
-		if (strlen(status_time_text) == 0) {
-			status_time_text = NULL;
-		}
-
-		g_free(fbuddy->status_rel_time);
-		if (status_time_text != NULL) {
-			fbuddy->status_rel_time = 
-				fb_strdup_withhtml(status_time_text);
-		} else {
-			fbuddy->status_rel_time = NULL;
-		}
-
-		/* if the buddy status has changed, update the contact list */
-		if (fbuddy->status == NULL ||
-			!g_str_equal(fbuddy->status, status_text))
-		{
-			g_free(fbuddy->status);
-			fbuddy->status = g_strdup(status_text);
-			status_changed = TRUE;
-		}
-
-		g_free(status_text);
-	} else {
-		if (fbuddy->status != NULL) {
-			g_free(fbuddy->status);
-			fbuddy->status = NULL;
-			status_changed = TRUE;
-		}
-	}
-
-	return status_changed;
-}
-
 static void process_buddy_icon(FacebookAccount *fba, PurpleBuddy *buddy,
 	JsonObject *userInfo)
 {
@@ -177,8 +119,8 @@ static void process_buddy_icon(FacebookA
 	fbuddy = buddy->proto_data;
 	
 	/* Set the buddy icon (if it hasn't changed) */
-	buddy_icon_url = json_node_dup_string(json_object_get_member(
-			userInfo, "thumbSrc"));
+	buddy_icon_url = g_strdup(json_node_get_string(json_object_get_member(
+			userInfo, "thumbSrc")));
 	if (fbuddy->thumb_url == NULL ||
 	    !g_str_equal(fbuddy->thumb_url, buddy_icon_url))
 	{
@@ -260,12 +202,10 @@ static void process_buddies(FacebookAcco
 	for (cur = buddies; cur != NULL; cur = cur->next)
 	{
 		PurpleBuddy *buddy;
-		gboolean status_changed;
 
 		buddy = (PurpleBuddy *)cur->data;
 
 		process_buddy_icon(fba, buddy, userInfo);
-		status_changed = process_buddy_status(fba, buddy, userInfo);
 
 		purple_presence_set_idle(purple_buddy_get_presence(buddy),
 				idle, 0);
@@ -279,7 +219,6 @@ static void process_buddies(FacebookAcco
 			// conditions before doing this, because if we set it always
 			// Pidgin has a bug where the logs go nuts with "x is online".
 			if (!PURPLE_BUDDY_IS_ONLINE(buddy) ||
-			    status_changed ||
 			    idle != purple_presence_is_idle(
 				purple_buddy_get_presence(buddy)))
 			{
@@ -320,6 +259,118 @@ static void process_notifications(Facebo
 	}
 }
 
+static void got_status_stream_cb(FacebookAccount *fba, gchar *data,
+		gsize data_len, gpointer userdata)
+{
+	gchar *error = NULL;
+	JsonParser *parser;
+	JsonObject *objnode;
+	gint new_latest;
+	const gchar *html;
+	gchar **messages;
+	gchar *message;
+	gint i;
+	gchar *uid_string;
+	gchar *message_string;
+	gsize uid_length;
+	FacebookBuddy *fbuddy;
+	PurpleBuddy *buddy;
+	
+	purple_debug_info("facebook", "parsing status message stream\n");
+	
+	if (fba == NULL)
+		return;
+
+	parser = fb_get_parser(data, data_len);
+	if (parser == NULL) {
+		return;
+	}
+	
+	//purple_debug_misc("facebook", "status message stream\n%s\n", data);
+	
+	objnode = fb_get_json_object(parser, &error);
+	
+	if (error || !json_object_has_member(objnode, "payload")) {
+		json_parser_free(parser);
+		return;
+	}
+	
+	objnode = json_node_get_object(json_object_get_member(
+			objnode, "payload"));
+	
+	new_latest = json_node_get_int(json_object_get_member(
+			objnode, "newestStoryTime"));
+	
+	if (!new_latest)
+	{
+		json_parser_free(parser);
+		return;
+	}
+	
+	fba->last_status_timestamp = new_latest;
+	
+	html = json_node_get_string(json_object_get_member(
+			objnode, "html"));
+	//purple_debug_misc("facebook", "html data\n%s\n", html);
+	
+	messages = g_strsplit(html, "/h3>", -1);
+	for(i = 0; messages[i]; i++)
+	{
+		message = messages[i];
+		uid_length = 0;
+		
+		//find uid:
+		//start with aid_ ...  "
+		uid_string = strstr(message, "aid_");
+		if (!uid_string)
+			continue;
+		uid_string += 4;
+		while (uid_string[uid_length] >= '0' &&
+				uid_string[uid_length] <= '9')
+		{
+			uid_length++;
+		}
+		uid_string = g_strndup(uid_string, uid_length);
+		
+		//find message:
+		//class="UIStory_Message"> ... </span>
+		message_string = strstr(message, "UIStory_Message");
+		if (!message_string)
+		{
+			g_free(uid_string);
+			continue;	
+		}
+		message_string = strchr(message_string, '>');
+		if (!message_string)
+		{
+			g_free(uid_string);
+			continue;
+		}
+		message_string += 1;
+		message_string = g_strndup(message_string, strstr(message_string, "</span>")-message_string);
+		
+		buddy = purple_find_buddy(fba->account, uid_string);
+		if (buddy && buddy->proto_data)
+		{
+			fbuddy = buddy->proto_data;
+			g_free(fbuddy->status);
+			
+			fbuddy->status = purple_markup_strip_html(message_string);
+			
+			purple_prpl_got_user_status(fba->account, buddy->name,
+				purple_primitive_get_id_from_type(
+					purple_presence_is_idle(purple_buddy_get_presence(buddy)) ? PURPLE_STATUS_AWAY :
+						PURPLE_STATUS_AVAILABLE), "message", fbuddy->status, NULL);
+		}
+		
+		g_free(uid_string);
+		g_free(message_string);
+	}
+	g_strfreev(messages);
+	
+	json_parser_free(parser);
+}
+
 static void got_buddy_list_cb(FacebookAccount *fba, gchar *data,
 		gsize data_len, gpointer userdata)
 {
@@ -335,9 +386,12 @@ static void got_buddy_list_cb(FacebookAc
 
 	JsonParser *parser = fb_get_parser(data, data_len);
 	if (parser == NULL) {
-		purple_connection_error_reason(fba->pc,
+		if (fba->bad_buddy_list_count++ == 3)
+		{
+			purple_connection_error_reason(fba->pc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Could not retrieve buddy list"));
+		}
 		return;
 	}
 
@@ -346,37 +400,48 @@ static void got_buddy_list_cb(FacebookAc
 	gchar *error = NULL;
 	JsonObject *objnode = fb_get_json_object(parser, &error);
 	if (error) {
-		purple_connection_error_reason(
+		if (fba->bad_buddy_list_count++ == 3)
+		{
+			purple_connection_error_reason(
 				fba->pc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				error);
-			g_object_unref(parser);
-			return;
+		}
+		json_parser_free(parser);
+		
+		return;
 	}
 	
 	/* look for "userInfos":{ ... }, */
 	if (!json_object_has_member(objnode, "payload"))
 	{
-		g_object_unref(parser);
+		json_parser_free(parser);
 		return;
 	}
 	objnode = json_node_get_object(json_object_get_member(
 			objnode, "payload"));
 	if (!json_object_has_member(objnode, "buddy_list"))
 	{
-		g_object_unref(parser);
+		json_parser_free(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);
+		json_parser_free(parser);
 		return;
 	}
-
-	fb_process_friend_lists(fba, buddy_list);
-
+	
+	//Reset invalid buddy list counter
+	fba->bad_buddy_list_count = 0;
+	
+	if (purple_account_get_bool(fba->account, "facebook_use_groups", TRUE))
+	{
+		//Only process if we have the setting
+		fb_process_friend_lists(fba, buddy_list);
+	}
+	
 	// Iterate through the list of buddy infos sent to us.	
 	JsonObject *userInfos;
 	JsonObject *nowAvailableList;
@@ -422,7 +487,7 @@ static void got_buddy_list_cb(FacebookAc
 	process_notifications(fba, json_node_get_object(
 		json_object_get_member(objnode, "notifications")));
 
-	g_object_unref(parser);
+	json_parser_free(parser);
 }
 
 gboolean fb_get_buddy_list(gpointer data)
@@ -433,11 +498,17 @@ gboolean fb_get_buddy_list(gpointer data
 	fba = data;
 
 	postdata = g_strdup_printf(
-			"user=%" G_GINT64_FORMAT "&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",
+			"user=%" G_GINT64_FORMAT "&popped_out=true&force_render=true&buddy_list=1&__a=1&post_form_id_source=AsyncRequest&post_form_id=%s&fb_dtsg=%s",
+			fba->uid, fba->post_form_id?fba->post_form_id:"(null)", fba->dtsg?fba->dtsg:"(null)");
+	fb_post_or_get(fba, FB_METHOD_POST, NULL, "/ajax/chat/buddy_list.php",
 			postdata, got_buddy_list_cb, NULL, FALSE);
 	g_free(postdata);
+	
+	postdata = g_strdup_printf("/ajax/intent.php?filter=app_2915120374&request_type=1&__a=1&newest=%d&ignore_self=true",
+			fba->last_status_timestamp);
+	fb_post_or_get(fba, FB_METHOD_GET, NULL, postdata,
+			NULL, got_status_stream_cb, NULL, FALSE);
+	g_free(postdata);
 
 	return TRUE;
 }
============================================================
--- libpurple/protocols/facebook/fb_connection.c	557b8518d7576ae548b710f016cbca9d3f76f606
+++ libpurple/protocols/facebook/fb_connection.c	33973e7de9483646796ea2e4c476ccc886ce0df6
@@ -23,6 +23,7 @@ static void fb_attempt_connection(Facebo
 static void fb_attempt_connection(FacebookConnection *);
 
 #ifdef HAVE_ZLIB
+#include <zlib.h>
 
 static gchar *fb_gunzip(const guchar *gzip_data, ssize_t *len_ptr)
 {
@@ -559,7 +560,7 @@ void fb_post_or_get(FacebookAccount *fba
 	 *       the TTL returned by the DNS server.  We should expire things
 	 *       from the cache after some amount of time.
 	 */
-	if (!is_proxy)
+	if (!is_proxy && !(method & FB_METHOD_SSL))
 	{
 		/* Don't do this for proxy connections, since proxies do the DNS lookup */
 		gchar *host_ip;
============================================================
--- libpurple/protocols/facebook/fb_conversation.c	c7a209ce60af9d7660eeceecb1941275fe47bbe1
+++ libpurple/protocols/facebook/fb_conversation.c	20f34f5ff69959ab8ca5cc9e0d3eab7a485cfedc
@@ -21,9 +21,8 @@
 #include "fb_conversation.h"
 #include "fb_connection.h"
 #include "fb_util.h"
+#include "fb_json.h"
 
-#include <json-glib/json-glib.h>
-
 #include "conversation.h"
 #include "signals.h"
 
@@ -141,9 +140,9 @@ static void fb_history_fetch_cb(Facebook
 			gchar *to;
 			JsonObject *text_obj;
 
-			from = g_strdup_printf("%d", json_node_get_int(
+			from = g_strdup_printf("%" G_GINT64_FORMAT, (gint64)json_node_get_int(
 				json_object_get_member(message_obj, "from")));
-			to = g_strdup_printf("%d", json_node_get_int(
+			to = g_strdup_printf("%" G_GINT64_FORMAT, (gint64)json_node_get_int(
 				json_object_get_member(message_obj, "to")));
 
 			text_obj = json_node_get_object(
@@ -168,7 +167,7 @@ static void fb_history_fetch_cb(Facebook
 		}
 	}
 	
-	g_object_unref(parser);
+	json_parser_free(parser);
 }
 
 void fb_history_fetch(FacebookAccount *fba, const char *who,
============================================================
--- libpurple/protocols/facebook/fb_friendlist.c	75b0969a59d34b7145d1fe3c6e4121ab9f2d9ed3
+++ libpurple/protocols/facebook/fb_friendlist.c	66a6aeac0aba11a6919cf7a0a09db77e79a80d56
@@ -48,8 +48,8 @@ static void handle_move_request(Facebook
 	old_list_id = fb_get_list_id(fba, request->old_group);
 	new_list_id = fb_get_list_id(fba, request->new_group);
 
-	remove_flist = !new_list_id || !strcmp(new_list_id, "-1");
-	no_original_list = !old_list_id || !strcmp(old_list_id, "-1");
+	remove_flist = !new_list_id || g_str_equal(new_list_id, "-1");
+	no_original_list = !old_list_id || g_str_equal(old_list_id, "-1");
 
 	if (remove_flist) {
 		command = "&remove_fl=true";
@@ -101,7 +101,7 @@ static void create_list_cb(FacebookAccou
 	if (!objnode ||
 	    !json_object_has_member(objnode, "payload"))
 	{
-		g_object_unref(parser);
+		json_parser_free(parser);
 		return;
 	}
 
@@ -109,7 +109,7 @@ static void create_list_cb(FacebookAccou
 			objnode, "payload"));
 	fb_process_friend_lists(fba, objnode);
 
-	g_object_unref(parser);
+	json_parser_free(parser);
 
 	// Move Friend
 	request = (MoveRequest *) userdata;
@@ -149,13 +149,19 @@ void fb_group_buddy_move(PurpleConnectio
 	MoveRequest *request;
 	const gchar *new_list_id;
 	
+	if (!purple_account_get_bool(pc->account, "facebook_use_groups", TRUE))
+	{
+		//Dont do anything if we're ignoring groups
+		return;
+	}
+	
 	fba = pc->proto_data;
 
 	purple_debug_info("facebook", "handling move of %s from %s to %s\n",
 		who, old_group, new_group);
 
 	// Don't do anything if groups are not actually changing.
-	if (!strcmp(old_group, new_group)) {
+	if (!purple_utf8_strcasecmp(old_group, new_group)) {
 		purple_debug_info("facebook", "groups are same, not moving\n");
 		return;
 	}
@@ -169,8 +175,8 @@ void fb_group_buddy_move(PurpleConnectio
 	}
 
 	request = g_new0(MoveRequest, 1);
-	request->old_group = g_strdup(old_group);
-	request->new_group = g_strdup(new_group);
+	request->old_group = g_utf8_strdown(old_group, -1);
+	request->new_group = g_utf8_strdown(new_group, -1);
 	request->who = g_strdup(who);
 
 	new_list_id = fb_get_list_id(fba, request->new_group);
@@ -195,6 +201,12 @@ void fb_buddy_remove(PurpleConnection *p
 	// If the plugin is ever to perform an actual defriending, it needs
 	// to provide a dialog and user prompt at the absolute bare minimum.
 	FacebookAccount *fba;
+	
+	if (!purple_account_get_bool(pc->account, "facebook_use_groups", TRUE))
+	{
+		//Dont do anything if we're ignoring groups
+		return;
+	}
 
 	purple_debug_info("facebook", "handing removal of buddy %s\n",
 		buddy->name);
@@ -208,6 +220,13 @@ void fb_group_rename(PurpleConnection *p
 void fb_group_rename(PurpleConnection *pc, const char *old_name,
 	PurpleGroup *group, GList *moved_buddies)
 {
+	
+	if (!purple_account_get_bool(pc->account, "facebook_use_groups", TRUE))
+	{
+		//Dont do anything if we're ignoring groups
+		return;
+	}
+	
 	purple_debug_info("facebook",
 		"handling group rename of %s to %s\n",
 		old_name, purple_group_get_name(group));
@@ -234,6 +253,12 @@ void fb_group_remove(PurpleConnection *p
 
 void fb_group_remove(PurpleConnection *pc, PurpleGroup *group)
 {
+	if (!purple_account_get_bool(pc->account, "facebook_use_groups", TRUE))
+	{
+		//Dont do anything if we're ignoring groups
+		return;
+	}
+	
 	purple_debug_info("facebook", "got group removal of %s\n",
 		purple_group_get_name(group));
 
@@ -251,11 +276,11 @@ const gchar *fb_get_list_id(FacebookAcco
 
 const gchar *fb_get_list_id(FacebookAccount *fba, const gchar *list_name)
 {
-	if (!strcmp(list_name, DEFAULT_GROUP_NAME)) {
+	if (!purple_utf8_strcasecmp(list_name, DEFAULT_GROUP_NAME)) {
 		return "-1";
 	}
 
-	return g_hash_table_lookup(fba->friend_lists_reverse, list_name);
+	return g_hash_table_lookup(fba->friend_lists_reverse, purple_normalize_nocase(NULL, list_name));
 }
 
 gboolean fb_process_friend_lists(FacebookAccount *fba,
@@ -278,7 +303,7 @@ gboolean fb_process_friend_lists(Faceboo
 	friend_list_ids = json_object_get_members(fl_obj);
 	for (cur = friend_list_ids; cur != NULL; cur = cur->next)
 	{
-		const gchar *id;
+		gchar *id;
 		const gchar *name;
 		JsonObject *data;
 
@@ -290,7 +315,7 @@ gboolean fb_process_friend_lists(Faceboo
 		if (name) {
 			// Either -1 isnt a valid JSON string or JSON-glib does
 			// this wrong.  I'm too tired to tell the difference.
-			if (!strcmp(id, "_1")) {
+			if (g_str_equal(id, "_1")) {
 				id = "-1";
 			}
 			purple_debug_info("facebook",
@@ -299,7 +324,7 @@ gboolean fb_process_friend_lists(Faceboo
 			g_hash_table_insert(fba->friend_lists,
 					g_strdup(id), g_strdup(name));
 			g_hash_table_insert(fba->friend_lists_reverse,
-					g_strdup(name), g_strdup(id));
+					g_utf8_strdown(name, -1), g_strdup(id));
 		}
 	}
 
@@ -336,8 +361,8 @@ static PurpleBuddy *add_buddy(FacebookAc
 	PurpleGroup *fb_group;
 	PurpleBuddy *buddy;
 
-	group_name = g_hash_table_lookup(fba->friend_lists, friend_list_id);
-	if (!group_name || !strcmp(group_name, "")) {
+	group_name = g_hash_table_lookup(fba->friend_lists, purple_normalize_nocase(NULL, friend_list_id));
+	if (!group_name || group_name[0] == '\0') {
 		purple_debug_info("facebook",
 				"did not find name of list %s\n",
 				friend_list_id);
@@ -354,13 +379,13 @@ static PurpleBuddy *add_buddy(FacebookAc
 		purple_blist_add_group(fb_group, NULL);
 	} 
 
-	buddy = (PurpleBuddy *)g_hash_table_lookup(cur_groups, group_name);
+	buddy = (PurpleBuddy *)g_hash_table_lookup(cur_groups, purple_normalize_nocase(NULL, group_name));
 	if (!buddy) {
 		purple_debug_info("facebook", "adding %s to %s\n",
 				uid, group_name);
 		buddy = purple_buddy_new(fba->account, uid, NULL);
 		purple_blist_add_buddy(buddy, NULL, fb_group, NULL);
-		g_hash_table_remove(cur_groups, group_name);
+		g_hash_table_remove(cur_groups, purple_normalize_nocase(NULL, group_name));
 	}
 
 	return buddy;
@@ -375,6 +400,8 @@ GList *fb_get_buddies_friend_list (Faceb
 	GHashTable *cur_groups;
 	int i;
 	GList *final_buddies, *cur_buddy;
+	PurpleGroup *fb_group;
+	PurpleBuddy *buddy;
 
 	final_buddies = NULL;
 	buddies = purple_find_buddies(fba->account, uid);
@@ -392,7 +419,33 @@ GList *fb_get_buddies_friend_list (Faceb
 		g_slist_free(buddies);
 		return final_buddies;
 	}
-
+	
+	//Do we want to ignore groups?
+	if (!purple_account_get_bool(fba->account, "facebook_use_groups", TRUE))
+	{
+		if (buddies != NULL) {
+			//Copy the slist into the list
+			for (cur = buddies; cur != NULL; cur = cur->next)
+			{
+				final_buddies = g_list_append(
+					final_buddies, cur->data);
+			}
+			g_slist_free(buddies);
+			return final_buddies;
+		} else {
+			buddy = purple_buddy_new(fba->account, uid, NULL);
+			fb_group = purple_find_group(DEFAULT_GROUP_NAME);
+			if (fb_group == NULL)
+			{
+				fb_group = purple_group_new(DEFAULT_GROUP_NAME);
+				purple_blist_add_group(fb_group, NULL);
+			}
+			purple_blist_add_buddy(buddy, NULL, fb_group, NULL);
+			final_buddies = g_list_append(final_buddies, buddy);
+			return final_buddies;
+		}
+	}
+	
 	// Determine what buddies exist and what groups they are in.
 	cur_groups = g_hash_table_new_full(g_str_hash, g_str_equal,
 		g_free, NULL);
@@ -403,7 +456,7 @@ GList *fb_get_buddies_friend_list (Faceb
 		group_name = purple_group_get_name(purple_buddy_get_group(
 					(PurpleBuddy *)cur->data));
 
-		g_hash_table_insert(cur_groups, g_strdup(group_name), cur->data);
+		g_hash_table_insert(cur_groups, g_utf8_strdown(group_name, -1), cur->data);
 	}
 	g_slist_free(buddies);
 
@@ -412,7 +465,6 @@ GList *fb_get_buddies_friend_list (Faceb
 		for (i = 0; i < json_array_get_length(friend_list_ids); i++)
 		{
 			const gchar *friend_list_id;
-			PurpleBuddy *buddy;
 
 			friend_list_id = json_node_get_string(
 				json_array_get_element(friend_list_ids, i));
@@ -431,9 +483,9 @@ GList *fb_get_buddies_friend_list (Faceb
 	for (cur_buddy = final_buddies; cur_buddy != NULL;
 	     cur_buddy = cur_buddy->next)
 	{
-		g_hash_table_remove(cur_groups, purple_group_get_name(
-			purple_buddy_get_group(
-				(PurpleBuddy *)cur_buddy->data)));
+		g_hash_table_remove(cur_groups, purple_normalize_nocase(NULL, 
+			purple_group_get_name(purple_buddy_get_group(
+				(PurpleBuddy *)cur_buddy->data))));
 	}
 
 	// Delete remaining buddies to maintain sync state with server.
============================================================
--- libpurple/protocols/facebook/fb_friendlist.h	4245e211978d4e907a4e22578faaee53bed74f7c
+++ libpurple/protocols/facebook/fb_friendlist.h	0c755491d5b060bfc1eb798339315627c75d4bb6
@@ -22,9 +22,8 @@
 #define FACEBOOK_FRIENDLIST_H
 
 #include "libfacebook.h"
+#include "fb_json.h"
 
-#include <json-glib/json-glib.h>
-
 #define DEFAULT_GROUP_NAME "Facebook"
 
 /* Friend list modification methods */
============================================================
--- libpurple/protocols/facebook/fb_managefriends.c	1d9f2f9e32feef9aa049321c952ab8cb205f1162
+++ libpurple/protocols/facebook/fb_managefriends.c	372e3781799c1dbca5671830f39567e0b1283b6d
@@ -76,7 +76,7 @@ static void fb_check_friend_request_cb(F
 static void fb_check_friend_request_cb(FacebookAccount *fba, gchar *data,
 		gsize data_len, gpointer user_data)
 {
-	const char *uid_pre_text = "class=\"confirm\" id=\"friend_add_";
+	const char *uid_pre_text = "class=\"confirm\" id=\"friend_connect_";
 	const char *name_pre_text = "<td class=\"info\"><a ";
 	const char *msg_pre_text = "<div class=\"personal_msg\"><span>";
 	gchar *uid;
============================================================
--- libpurple/protocols/facebook/fb_messages.c	09b6b82775e77886d5aff74654063c9f70e7e7fb
+++ libpurple/protocols/facebook/fb_messages.c	db485285cc61b901a8cfb8f38564c8d805dd7004
@@ -31,7 +31,7 @@ struct _FacebookOutgoingMessage {
 struct _FacebookOutgoingMessage {
 	FacebookAccount *fba;
 	gchar *who;
-	time_t time;
+	GTimeVal time;
 	gchar *message;
 	gint msg_id;
 	guint retry_count;
@@ -98,12 +98,13 @@ static void parse_new_messages(PurpleCon
 	for (i = 0; i < json_array_get_length(messages); i++) {
 		const gchar *type;
 		gchar *from, *to;
+		
 
 		JsonObject *object = json_node_get_object(json_array_get_element(messages, i));
 		type = json_node_get_string(json_object_get_member(object, "type"));
 
-		from = g_strdup_printf("%d", json_node_get_int(json_object_get_member(object, "from")));
-		to = g_strdup_printf("%d", json_node_get_int(json_object_get_member(object, "to")));
+		from = g_strdup_printf("%" G_GINT64_FORMAT, (gint64)json_node_get_int(json_object_get_member(object, "from")));
+		to = g_strdup_printf("%" G_GINT64_FORMAT, (gint64)json_node_get_int(json_object_get_member(object, "to")));
 		
 		/* Use the in-line buddy name if the buddy list hasn't been downloaded yet */
 		buddy = purple_find_buddy(pc->account, from);
@@ -232,7 +233,7 @@ static void got_new_messages(FacebookAcc
 
 			/* refresh means that the channel is invalid */
 			fb_reconnect(fba);
-			g_object_unref(parser);
+			json_parser_free(parser);
 			return;
 		} else if (g_str_equal(command, "continue")) {
 			/* continue means that the server wants us to remake the connection.
@@ -243,7 +244,7 @@ static void got_new_messages(FacebookAcc
 		}
 	}
 
-	g_object_unref(parser);
+	json_parser_free(parser);
 
 	/* Continue looping, waiting for more messages */
 	fb_get_new_messages(fba);
@@ -290,7 +291,7 @@ static gboolean fb_get_new_messages(Face
 
 	purple_debug_info("facebook", "getting new messages\n");
 
-	fetch_server = g_strdup_printf("%d.channel%s.facebook.com", 0, channel_number);
+	fetch_server = g_strdup_printf("%d.%s.facebook.com", 0, channel_number);
 	/* use the current time in the url to get past any transparent proxy caches */
 	fetch_url = g_strdup_printf("/x/%lu/%s/p_%" G_GINT64_FORMAT "=%d", (gulong)time(NULL), (fba->is_idle?"false":"true"), fba->uid, fba->message_fetch_sequence);
 
@@ -329,7 +330,7 @@ static void fb_send_im_cb(FacebookAccoun
 		{
 			msg->resend_timer = purple_timeout_add_seconds(1, (GSourceFunc)fb_resend_im_fom, msg);
 			fba->resending_messages = g_slist_prepend(fba->resending_messages, msg);
-			g_object_unref(parser);
+			json_parser_free(parser);
 			return;
 		}
 		else
@@ -337,13 +338,13 @@ static void fb_send_im_cb(FacebookAccoun
 			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
 					fba->account, msg->who);
 			purple_conversation_write(conv, NULL, error,
-					PURPLE_MESSAGE_ERROR, msg->time);
+					PURPLE_MESSAGE_ERROR, msg->time.tv_sec);
 		}
 		
 		g_hash_table_remove(fba->sent_messages_hash, msg->message);
 	}
 
-	g_object_unref(parser);
+	json_parser_free(parser);
 	fb_msg_destroy(msg);
 }
 
@@ -351,13 +352,16 @@ static gboolean fb_send_im_fom(FacebookO
 {
 	gchar *encoded_message;
 	gchar *postdata;
+	gchar *jstime;
+	
+	jstime = g_strdup_printf("%ld%ld", msg->time.tv_sec, (msg->time.tv_usec/1000));
 
 	encoded_message = g_strdup(purple_url_encode(msg->message));
-	postdata = g_strdup_printf("msg_text=%s&msg_id=%d&to=%s&client_time=%lu&post_form_id=%s",
-			encoded_message, msg->msg_id, msg->who,
-			(gulong) msg->time,
+	postdata = g_strdup_printf("msg_text=%s&msg_id=%d&to=%s&client_time=%s&post_form_id=%s",
+			encoded_message, msg->msg_id, msg->who, jstime,
 			msg->fba->post_form_id ? msg->fba->post_form_id : "0");
 	g_free(encoded_message);
+	g_free(jstime);
 
 	fb_post_or_get(msg->fba, FB_METHOD_POST, NULL, "/ajax/chat/send.php", postdata, fb_send_im_cb, msg, FALSE);
 	g_free(postdata);
@@ -389,7 +393,7 @@ int fb_send_im(PurpleConnection *pc, con
 
 	msg->msg_id = g_random_int();
 	msg->who = g_strdup(who);
-	msg->time = time(NULL);
+	g_get_current_time(&msg->time);
 	msg->retry_count = 0;
 
 	//save that we're sending the message
@@ -403,8 +407,6 @@ void got_reconnect_json(FacebookAccount 
 
 void got_reconnect_json(FacebookAccount *fba, gchar *data, gsize data_len, gpointer userdata)
 {
-	gchar *new_channel_number;
-	
 	JsonParser *parser;
 	JsonObject *objnode;
 
@@ -433,13 +435,12 @@ void got_reconnect_json(FacebookAccount 
 		purple_connection_error_reason(fba->pc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Error fetching channel; did you log in elsewhere?"));
-		g_object_unref(parser);
+		json_parser_free(parser);
 		return;
 	}
 	
-	new_channel_number = g_strdup(&new_channel_host[7]);
 	g_free(fba->channel_number);
-	fba->channel_number = new_channel_number;
+	fba->channel_number = g_strdup(new_channel_host);
 	
 	gint new_seq = json_node_get_int(json_object_get_member(payload, "seq"));
 	fba->message_fetch_sequence = new_seq;
@@ -449,12 +450,12 @@ void got_reconnect_json(FacebookAccount 
 	 * waiting for messages
 	 */
 	fb_get_new_messages(fba);
-	g_object_unref(parser);
+	json_parser_free(parser);
 }
 
 gboolean fb_reconnect(FacebookAccount *fba)
 {
-	gchar *url = g_strdup_printf("/ajax/presence/reconnect.php?reason=3&post_form_id=%s", fba->post_form_id);
+	gchar *url = g_strdup_printf("/ajax/presence/reconnect.php?reason=7&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);
 	
@@ -464,7 +465,11 @@ static void got_form_id_page(FacebookAcc
 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=\"";
+	const gchar *dtsg_start = "fb_dtsg:\"";
+	const gchar *channel_start = "js\", \"channel";
+	const gchar *channel_start2 = "js\",\"channel";
 	gchar *post_form_id;
+	gchar *channel = NULL;
 	gchar *tmp = NULL;
 	
 	/* NULL data crashes on Windows */
@@ -484,17 +489,45 @@ static void got_form_id_page(FacebookAcc
 	}
 	tmp += strlen(start_text);
 	post_form_id = g_strndup(tmp, strchr(tmp, '"') - tmp);
-
+	
 	g_free(fba->post_form_id);
 	fba->post_form_id = post_form_id;
+	
+	tmp = g_strstr_len(data, data_len, dtsg_start);
+	if (tmp != NULL)
+	{
+		tmp += strlen(dtsg_start);
+		g_free(fba->dtsg);
+		fba->dtsg = g_strndup(tmp, strchr(tmp, '"') - tmp);
+	}
 
+	tmp = g_strstr_len(data, data_len, channel_start);
+	if (tmp != NULL)
+	{
+		tmp += 6;
+	} else {
+		tmp = g_strstr_len(data, data_len, channel_start2);
+		if (tmp != NULL)
+			tmp += 5;
+	}
+	if (tmp != NULL)
+	{
+		channel = g_strndup(tmp, strchr(tmp, '"') - tmp);
+		g_free(fba->channel_number);
+		fba->channel_number = channel;
+	}
 
 	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);
 	
-	/* Grab new channel number */
-	fb_reconnect(fba);
+	if (channel == NULL)
+	{
+		/* Grab new channel number */
+		fb_reconnect(fba);
+	} else {
+		fb_get_new_messages(fba);
+	}
 }
 
 gboolean fb_get_post_form_id(FacebookAccount *fba)
============================================================
--- libpurple/protocols/facebook/fb_notifications.c	ca23fe8908d98aa815fb521ef82a9c2937652dae
+++ libpurple/protocols/facebook/fb_notifications.c	4dfffc8a8452bc56f856c94b2dec1e27d6287ca4
@@ -23,6 +23,7 @@ static void fb_got_notifications_cb(Face
 
 static void fb_got_notifications_cb(FacebookAccount *fba, gchar *url_text, gsize len, gpointer userdata)
 {
+	gchar *salvaged;
 	time_t last_fetch_time;
 	time_t time_of_message;
 	time_t newest_message = 0;
@@ -41,18 +42,26 @@ static void fb_got_notifications_cb(Face
 	last_fetch_time = purple_account_get_int(fba->account, "facebook_notifications_last_fetch", 0);
 	/* purple_debug_info("facebook", "last fetch time: %zu\n", last_fetch_time); */
 
-	xmlnode *rss_root = xmlnode_from_str(url_text, len);
+	salvaged = purple_utf8_salvage(url_text);
+	xmlnode *rss_root = xmlnode_from_str(salvaged, -1);
+	g_free(salvaged);
 	if (rss_root == NULL)
 	{
+		purple_debug_error("facebook", "Could not load RSS file\n");
 		return;
 	}
 	xmlnode *channel = xmlnode_get_child(rss_root, "channel");
 	if (channel == NULL)
 	{
+		purple_debug_warning("facebook", "Invalid RSS feed\n");
 		xmlnode_free(rss_root);
 		return;
 	}
 	xmlnode *item = xmlnode_get_child(channel, "item");
+	if (item == NULL)
+	{
+		purple_debug_info("facebook", "No new notifications\n");
+	}
 	for (; item != NULL; item = xmlnode_get_next_twin(item))
 	{
 		xmlnode *pubDate = xmlnode_get_child(item, "pubDate");
============================================================
--- libpurple/protocols/facebook/fb_util.c	094060765df4b0671d66097a7ad6bfc0e1e87650
+++ libpurple/protocols/facebook/fb_util.c	db25bbe26ed405a613ac050b45a0567df3ee5aa8
@@ -112,9 +112,46 @@ gchar *fb_strdup_withhtml(const gchar *s
 	return dest;
 }
 
-gint64 fb_time_kludge(gint initial_time)
+static gboolean is_json_64bit_safe()
 {
+	//Cache the result to try make this function quick
+	static gint result = -1;
+	gint64 largeint = G_MAXINT64;
+	
+	if (result == 1)
+		return TRUE;
+	if (result == 0)
+		return FALSE;
+	
 	if (sizeof(gint) >= sizeof(gint64))
+	{
+		result = 1;
+		return TRUE;
+	}
+
+#ifndef USE_JSONC
+	JsonNode *node;
+
+	node = json_node_new(JSON_NODE_VALUE);
+	json_node_set_int(node, largeint);
+	if(json_node_get_int(node) == largeint)
+	{
+		result = 1;
+	} else {
+		result = 0;
+	}
+	json_node_free(node);
+	return result ? TRUE : FALSE;
+	
+#endif /* !USE_JSONC */
+	
+	result = 0;
+	return FALSE;
+}
+
+gint64 fb_time_kludge(gint64 initial_time)
+{
+	if (is_json_64bit_safe())
 		return initial_time;
 	
 	gint64 now_millis = (gint64) time(NULL);
@@ -142,7 +179,7 @@ JsonParser *fb_get_parser(const gchar *d
 
 	parser = json_parser_new();
 	if (!json_parser_load_from_data(parser, data, -1, NULL)) {
-		g_object_unref(parser);
+		json_parser_free(parser);
 		return NULL;
 	}
 
============================================================
--- libpurple/protocols/facebook/fb_util.h	53baf5d5d5678ff04dc221e8d629b56d3f3a67f5
+++ libpurple/protocols/facebook/fb_util.h	97d1d52c6ee49b764654ac3ae448284bfa071c81
@@ -22,7 +22,7 @@
 #define FACEBOOK_UTIL_H
 
 #include "libfacebook.h"
-#include <json-glib/json-glib.h>
+#include "fb_json.h"
 
 JsonParser *fb_get_parser(const gchar *data, gsize data_len);
 JsonObject *fb_get_json_object(JsonParser *parser, char **error_message);
@@ -30,7 +30,7 @@ gchar *fb_convert_unicode(const gchar *i
 gchar *fb_replace_styled_text(const gchar *text);
 gchar *fb_strdup_withhtml(const gchar *src);
 gchar *fb_convert_unicode(const gchar *input);
-gint64 fb_time_kludge(int initial_time);
+gint64 fb_time_kludge(gint64 initial_time);
 
 #endif /* FACEBOOK_UTIL_H */
 
============================================================
--- libpurple/protocols/facebook/libfacebook.c	83c57c5d42dc61b5fafa9b38bd25fd3e2773b20c
+++ libpurple/protocols/facebook/libfacebook.c	2d32ec731cd3af31e053880370477b39222df7fa
@@ -32,6 +32,7 @@ static void fb_close(PurpleConnection *p
 static void fb_login_cb(FacebookAccount *fba, gchar *response, gsize len,
 		gpointer userdata);
 static void fb_close(PurpleConnection *pc);
+static void fb_buddy_free(PurpleBuddy *buddy);
 
 /******************************************************************************/
 /* PRPL functions */
@@ -68,12 +69,6 @@ static void fb_tooltip_text(PurpleBuddy 
 		purple_notify_user_info_add_pair(userinfo,
 				_("Status"), status);
 		g_free(status);
-		if (fbuddy->status_rel_time && *fbuddy->status_rel_time)
-		{
-			purple_notify_user_info_add_pair(userinfo,
-					_("Status changed"),
-					fbuddy->status_rel_time);
-		}
 	}
 }
 
@@ -85,17 +80,13 @@ static GList *fb_statuses(PurpleAccount 
 	/* Online people have a status message and also a date when it was set */
 	status = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
 		NULL, _("Online"), FALSE, TRUE, FALSE, "message",
-		_("Message"), purple_value_new(PURPLE_TYPE_STRING),
-		"message_date", _("Message changed"),
-		purple_value_new(PURPLE_TYPE_STRING), NULL);
+		_("Message"), purple_value_new(PURPLE_TYPE_STRING), NULL);
 	types = g_list_append(types, status);
 	
 	/* Cave into feature requests and allow people to set themselves to be idle */
 	status = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
 		NULL, _("Idle"), FALSE, TRUE, FALSE, "message",
-		_("Message"), purple_value_new(PURPLE_TYPE_STRING),
-		"message_date", _("Message changed"),
-		purple_value_new(PURPLE_TYPE_STRING), NULL);
+		_("Message"), purple_value_new(PURPLE_TYPE_STRING), NULL);
 	types = g_list_append(types, status);
 
 	/* Offline people dont have messages */
@@ -118,52 +109,117 @@ static gboolean fb_get_messages_failsafe
 	return TRUE;
 }
 
+void fb_login_captcha_cancel_cb(PurpleConnection *pc, PurpleRequestFields *fields)
+{
+	purple_connection_error_reason(pc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+		"Could not authenticate captcha.  Logging into the Facebook website may fix this.");
+}
+
 void fb_login_captcha_ok_cb(PurpleConnection *pc, PurpleRequestFields *fields)
 {
-	gint birthday_year, birthday_month, birthday_day;
+	const gchar *captcha_response;
 	gchar *postdata, *encoded_username, *encoded_password, *encoded_charset_test,
-			*encoded_auth_token, *encoded_persist_data;
-	const gchar* const *languages;
-	const gchar *locale;
+			*encoded_persist_data, *encoded_response, *encoded_extra_challenge,
+			*encoded_session;
 	FacebookAccount *fba = pc->proto_data;
 
-	birthday_year = purple_request_fields_get_integer(fields, "birthday_year");
-	birthday_month = purple_request_fields_get_integer(fields, "birthday_month");
-	birthday_day = purple_request_fields_get_integer(fields, "birthday_day");
-		
+	captcha_response = purple_request_fields_get_string(fields, "captcha_response");
+	
+	encoded_response = g_strdup(purple_url_encode(captcha_response));
 	encoded_username = g_strdup(purple_url_encode(
 			purple_account_get_username(fba->account)));
 	encoded_password = g_strdup(purple_url_encode(
 			purple_account_get_password(fba->account)));
-	encoded_auth_token = g_strdup(purple_url_encode(
-			fba->auth_token));
+	encoded_extra_challenge = g_strdup(purple_url_encode(
+			fba->extra_challenge));
 	encoded_persist_data = g_strdup(purple_url_encode(
 			fba->persist_data));
+	encoded_session = g_strdup(purple_url_encode(
+			fba->captcha_session));
 	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(
-			"charset_test=%s&locale=%s&email=%s&pass=%s&persistent=1&login=Login&charset_test=%s&version=1.0&return_session=0&t_auth_token=%s&answered_captcha=1&captcha_persist_data=%s&birthday_captcha_day=%d&birthday_captcha_month=%d&birthday_captcha_year=%d",
-			encoded_charset_test, locale, encoded_username, encoded_password, encoded_charset_test, encoded_auth_token, encoded_persist_data, birthday_day, birthday_month, birthday_year);
+	postdata = g_strdup_printf("charset_test=%s&"
+								"version=1.0&"
+								"return_session=0&"
+								"charset_test=%s&"
+								"answered_captcha=1&"
+								"captcha_persist_data=%s&"
+								"captcha_session=%s&"
+								"extra_challenge_params=%s&"
+								"captcha_response=%s&"
+								"email=%s&pass=%s&"
+								"persistent=1",
+								encoded_charset_test, encoded_charset_test,
+								encoded_persist_data, encoded_session,
+								encoded_extra_challenge, encoded_response,
+								encoded_username, encoded_password);
 	g_free(encoded_username);
 	g_free(encoded_password);
 	g_free(encoded_charset_test);
-	g_free(encoded_auth_token);
+	g_free(encoded_extra_challenge);
 	g_free(encoded_persist_data);
+	g_free(encoded_response);
+	g_free(encoded_session);
 
 	fb_post_or_get(fba, FB_METHOD_POST | FB_METHOD_SSL, "login.facebook.com",
-			"/login.php?login_attempt=1", postdata, fb_login_cb, NULL, FALSE);
+			"/login.php?login_attempt=1&_fb_noscript=1", postdata, fb_login_cb, NULL, FALSE);
 	g_free(postdata);
 	
-	g_free(fba->auth_token);
+	g_free(fba->extra_challenge);
 	g_free(fba->persist_data);
-	fba->auth_token = NULL;
+	g_free(fba->captcha_session);
+	fba->extra_challenge = NULL;
 	fba->persist_data = NULL;
+	fba->captcha_session = NULL;
 }
 
+static void fb_login_captcha_image_cb(FacebookAccount *fba, gchar *response, 
+		gsize len, gpointer userdata)
+{
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *group;
+	PurpleRequestField *field;
+	
+	fields = purple_request_fields_new();
+	group = purple_request_field_group_new(NULL);
+	purple_request_fields_add_group(fields, group);
+	
+	field = purple_request_field_image_new("captcha_image", "", response, len);
+	purple_request_field_group_add_field(group, field);
+	
+	field = purple_request_field_string_new("captcha_response", "", "", FALSE);
+	purple_request_field_group_add_field(group, field);
+	
+	purple_request_fields(fba->pc, 
+		_("Facebook Captcha"), _("Facebook Captcha"), 
+		_("Enter both words below, separated by a space"), 
+		fields, 
+		_("OK"), G_CALLBACK(fb_login_captcha_ok_cb), 
+		_("Logout"), G_CALLBACK(fb_login_captcha_cancel_cb), 
+		fba->account, NULL, NULL, fba->pc	 
+	);
+}
+
+static void fb_login_captcha_cb(FacebookAccount *fba, gchar *response, 
+		gsize len, gpointer userdata)
+{
+	const gchar *challenge_start = "challenge : '";
+	gchar *challenge;
+	gchar *image_url;
+	
+	challenge = g_strstr_len(response, len, challenge_start);
+	if (challenge)
+	{
+		challenge += strlen(challenge_start);
+		challenge = g_strndup(challenge, strchr(challenge, '\'') - challenge);
+		
+		image_url = g_strdup_printf("/image?c=%s", challenge);
+		
+		fb_post_or_get(fba, FB_METHOD_GET | FB_METHOD_SSL, "api-secure.recaptcha.net",
+			image_url, NULL, fb_login_captcha_image_cb, NULL, FALSE);
+	}
+}
+
 static void fb_login_cb(FacebookAccount *fba, gchar *response, gsize len,
 		gpointer userdata)
 {
@@ -171,9 +227,11 @@ static void fb_login_cb(FacebookAccount 
 	
 	if (len && g_strstr_len(response, len, "captcha"))
 	{
+		purple_debug_info("facebook", "captcha page: %s\n", response);
+
 		purple_connection_update_progress(fba->pc, _("Handling Captcha"), 2, 4);
 		
-		const gchar *persist_data_start = "<input type=\"hidden\" name=\"captcha_persist_data\" value=\"";
+		const gchar *persist_data_start = "<input type=\"hidden\" id=\"captcha_persist_data\" name=\"captcha_persist_data\" value=\"";
 		gchar *persist_data = g_strstr_len(response, len, persist_data_start);
 		if (persist_data)
 		{
@@ -181,38 +239,48 @@ static void fb_login_cb(FacebookAccount 
 			fba->persist_data = g_strndup(persist_data, strchr(persist_data, '"') - persist_data);
 		}
 		
-		const gchar *auth_token_start = "<input type=\"hidden\" name=\"t_auth_token\" value=\"";
-		gchar *auth_token = g_strstr_len(response, len, auth_token_start);
-		if (auth_token)
+		const gchar *session_start = "<input type=\"hidden\" id=\"captcha_session\" name=\"captcha_session\" value=\"";
+		gchar *session = g_strstr_len(response, len, session_start);
+		if (session)
 		{
-			auth_token += strlen(auth_token);
-			fba->auth_token = g_strndup(auth_token, strchr(auth_token, '"') - auth_token);
+			session += strlen(session_start);
+			fba->captcha_session = g_strndup(session, strchr(session, '"') - session);
 		}
 		
-		PurpleRequestFields *fields;
-		PurpleRequestFieldGroup *group;
-		PurpleRequestField *field;
+		gchar *captcha_url;
+		const gchar *extra_challenge_params = "<input type=\"hidden\" id=\"extra_challenge_params\" name=\"extra_challenge_params\" value=\"";
+		gchar *extra_challenge = g_strstr_len(response, len, extra_challenge_params);
+		if (extra_challenge)
+		{
+			extra_challenge += strlen(extra_challenge_params);
+			fba->extra_challenge = g_strndup(extra_challenge, strchr(extra_challenge, '"') - extra_challenge);
+			extra_challenge = purple_unescape_html(fba->extra_challenge);
+			g_free(fba->extra_challenge);
+			fba->extra_challenge = extra_challenge;
+		}
 		
-		fields = purple_request_fields_new();
-		group = purple_request_field_group_new(NULL);
-		purple_request_fields_add_group(fields, group);
+		if (!fba->extra_challenge || !fba->persist_data || !fba->captcha_session)
+		{
+			purple_debug_info("facebook", "captcha response: %s\n", response);
+			g_free(fba->extra_challenge);
+			g_free(fba->persist_data);
+			g_free(fba->captcha_session);
+			fba->extra_challenge = NULL;
+			fba->persist_data = NULL;
+			fba->captcha_session = NULL;
+			purple_connection_error_reason(fba->pc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+				"Could not authenticate captcha.  Logging into the Facebook website may fix this.");
+			return;
+		}
 		
-		field = purple_request_field_int_new("birthday_year", _("Year"), 0);
-		purple_request_field_group_add_field(group, field);
-		field = purple_request_field_int_new("birthday_month", _("Month"), 0);
-		purple_request_field_group_add_field(group, field);
-		field = purple_request_field_int_new("birthday_day", _("Day"), 0);
-		purple_request_field_group_add_field(group, field);
+		captcha_url = g_strdup_printf("/challenge?k=" FACEBOOK_CAPTCHA_SITE "&%s",
+				fba->extra_challenge?fba->extra_challenge:"");
 		
-		purple_request_fields(fba->pc, 
-			_("Facebook Captcha"), _("Facebook Captcha"), 
-			_("Facebook thinks you're not you.  To prove you are, please enter your date of birth"), 
-			fields, 
-			_("OK"), G_CALLBACK(fb_login_captcha_ok_cb), 
-			_("Logout"), G_CALLBACK(fb_close), 
-			fba->account, NULL, NULL, fba->pc	 
-		);
+		fb_post_or_get(fba, FB_METHOD_GET | FB_METHOD_SSL, "api-secure.recaptcha.net",
+			captcha_url, NULL, fb_login_captcha_cb, NULL, FALSE);
 		
+		g_free(captcha_url);
+		
 		return;
 	}
 
@@ -317,15 +385,17 @@ static void fb_login(PurpleAccount *acco
 	if (locale == NULL || g_str_equal(locale, "C"))
 		locale = "en_US";
 
+	g_hash_table_replace(fba->cookie_table, g_strdup("lsd"), g_strdup("abcde"));
+
 	postdata = g_strdup_printf(
-			"charset_test=%s&locale=%s&email=%s&pass=%s&pass_placeHolder=Password&persistent=1&login=Login&charset_test=%s",
+			"charset_test=%s&locale=%s&email=%s&pass=%s&pass_placeHolder=Password&persistent=1&login=Login&charset_test=%s&lsd=abcde",
 			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?login_attempt=1", postdata, fb_login_cb, NULL, FALSE);
+			"/login.php?login_attempt=1&_fb_noscript=1", postdata, fb_login_cb, NULL, FALSE);
 	g_free(postdata);
 }
 
@@ -333,6 +403,7 @@ static void fb_close(PurpleConnection *p
 {
 	FacebookAccount *fba;
 	gchar *postdata;
+	GSList *buddies;
 
 	purple_debug_info("facebook", "disconnecting account\n");
 
@@ -349,6 +420,13 @@ static void fb_close(PurpleConnection *p
 	/* destroy conversation subsystem */
 	fb_conversation_destroy(fba);
 
+	buddies = purple_find_buddies(fba->account, NULL);
+	while(buddies) {
+		PurpleBuddy *b = buddies->data;
+		fb_buddy_free(b);
+		buddies = g_slist_delete_link(buddies, buddies);
+	}
+
 	/* Tell Facebook that we've logged out. */
 	/*
 	 * TODO
@@ -415,9 +493,11 @@ static void fb_close(PurpleConnection *p
 	g_hash_table_destroy(fba->hostname_ip_cache);
 	g_hash_table_destroy(fba->auth_buddies);
 	g_free(fba->post_form_id);
+	g_free(fba->dtsg);
 	g_free(fba->channel_number);
 	g_free(fba->last_status_message);
-	g_free(fba->auth_token);
+	g_free(fba->extra_challenge);
+	g_free(fba->captcha_session);
 	g_free(fba->persist_data);
 	g_free(fba);
 }
@@ -530,7 +610,6 @@ static void fb_buddy_free(PurpleBuddy *b
 
 		g_free(fbuddy->name);
 		g_free(fbuddy->status);
-		g_free(fbuddy->status_rel_time);
 		g_free(fbuddy->thumb_url);
 		g_free(fbuddy);
 	}
@@ -667,7 +746,14 @@ static void plugin_init(PurplePlugin *pl
 	prpl_info->protocol_options = g_list_append(
 		prpl_info->protocol_options, option);
 
+	
 	option = purple_account_option_bool_new(
+		_("Use Facebook friend-lists as Pidgin groups"),
+		"facebook_use_groups", TRUE);
+	prpl_info->protocol_options = g_list_append(
+		prpl_info->protocol_options, option);
+	
+	option = purple_account_option_bool_new(
 		_("Hide myself in the Buddy List"),
 		"facebook_hide_self", TRUE);
 	prpl_info->protocol_options = g_list_append(
@@ -684,6 +770,12 @@ static void plugin_init(PurplePlugin *pl
 		"facebook_get_notifications", TRUE);
 	prpl_info->protocol_options = g_list_append(
 		prpl_info->protocol_options, option);
+	
+	option = purple_account_option_string_new(
+		_("Notifications RSS Feed URL"),
+		"notifications_feed_url", "");
+	prpl_info->protocol_options = g_list_append(
+		prpl_info->protocol_options, option);
 
 	option = purple_account_option_bool_new(
 		_("Edit Facebook friends from Pidgin"),
============================================================
--- libpurple/protocols/facebook/libfacebook.h	dbc2bfd413ba1089196a55882771e37b6da1bc35
+++ libpurple/protocols/facebook/libfacebook.h	b56c2baea512f6680c01f5ea2c85cbbab5f1f51d
@@ -21,8 +21,9 @@
 #ifndef LIBFACEBOOK_H
 #define LIBFACEBOOK_H
 
-#define FACEBOOK_PLUGIN_VERSION "1.60"
+#define FACEBOOK_PLUGIN_VERSION "1.62"
 #define FACEBOOK_PLUGIN_ID "prpl-bigbrownchunx-facebookim"
+#define FACEBOOK_CAPTCHA_SITE "6LezHAAAAAAAADqVjseQ3ctG3ocfQs2Elo1FTa_a"
 
 #include <glib.h>
 
@@ -67,10 +68,6 @@
 #include "sslconn.h"
 #include "version.h"
 
-#ifdef HAVE_ZLIB
-#	include <zlib.h>
-#endif
-
 #if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 12
 #	define atoll(a) g_ascii_strtoll(a, NULL, 0)
 #endif
@@ -108,8 +105,12 @@ struct _FacebookAccount {
 	gboolean is_idle;
 	GHashTable *sent_messages_hash;
 	gint last_inbox_count;
-	gchar *auth_token;
+	gchar *extra_challenge;
 	gchar *persist_data;
+	gchar *captcha_session;
+	gint last_status_timestamp;
+	guint bad_buddy_list_count;
+	gchar *dtsg;
 };
 
 struct _FacebookBuddy {
@@ -118,7 +119,6 @@ struct _FacebookBuddy {
 	gint64 uid;
 	gchar *name;
 	gchar *status;
-	gchar *status_rel_time;
 	gchar *thumb_url;
 };
 
============================================================
--- libpurple/protocols/facebook/pidgin-facebookchat.rc	78abadafda8864721a2d0249a7710578eaba4cc2
+++ libpurple/protocols/facebook/pidgin-facebookchat.rc	9d0c53b057522ae3de5007d9121259ce641eb8f6
@@ -1,7 +1,7 @@ 1 VERSIONINFO
 
 1 VERSIONINFO
-FILEVERSION 1,60,0,0
-PRODUCTVERSION 1,60,0,0
+FILEVERSION 1,62,0,0
+PRODUCTVERSION 1,62,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.60\0"
-		VALUE "ProductVersion", "1.60\0"
+		VALUE "FileVersion", "1.62\0"
+		VALUE "ProductVersion", "1.62\0"
 		VALUE "InternalName", "pidgin-facebookchat\0"
 		VALUE "OriginalFilename", "libfacebook.dll\0"
 		VALUE "Comments", "http://pidgin-facebookchat.googlecode.com/\0"
============================================================
--- libpurple/protocols/facebook/rss.xml	40100d174f1ed87fdc64698421454ee9d4991879
+++ libpurple/protocols/facebook/rss.xml	baa5c5a1ad452cb3c2f2b341993062adf99d7f29
@@ -13,6 +13,29 @@
 			<width>48</width><height>48</height>
 		</image>
 		<item>
+			<title>Version 1.62</title>
+			<link>http://code.google.com/p/pidgin-facebookchat/issues/detail?id=24#c50</link>
+			<description><![CDATA[Some of you fancy people might have noticed that the plugin hasn't been doing too well over the last few hours.<br/>
+			Fortunately, I've been hard at work to quickly push out a new version.<br/>
+			<br/>
+			Grab it from<br/>
+			<a href="http://code.google.com/p/pidgin-facebookchat/wiki/Downloads">http://code.google.com/p/pidgin-facebookchat/wiki/Downloads</a><br/>
+			while it's still hot]]></description>
+			<pubDate>Wed, 4 November 2009 18:49:10 +1300</pubDate>
+			<guid isPermaLink="true">http://code.google.com/p/pidgin-facebookchat/issues/detail?id=24#c50</guid>
+		</item>
+		<item>
+			<title>Version 1.61</title>
+			<link>http://code.google.com/p/pidgin-facebookchat/issues/detail?id=24#c49</link>
+			<description><![CDATA[Against all odds, I've just put out a new version 1.61 of the plugin.  It should fix up the problems with messages arriving from weird numbers, but it also now requires version 0.7.6 of json-glib to work.  It might or might not also have the option to disable friends lists, depending on who you ask :)<br/><br/>
+			Changelog:<br/>
+			<a href="http://code.google.com/p/pidgin-facebookchat/wiki/Changelog">http://code.google.com/p/pidgin-facebookchat/wiki/Changelog</a><br/>
+			Download:<br/>
+			<a href="http://code.google.com/p/pidgin-facebookchat/downloads/list">http://code.google.com/p/pidgin-facebookchat/downloads/list</a><br/>]]></description>
+			<pubDate>Tue, 8 September 2009 17:38:16 +1200</pubDate>
+			<guid isPermaLink="true">http://code.google.com/p/pidgin-facebookchat/issues/detail?id=24#c49</guid>
+		</item>
+		<item>
 			<title>Version 1.60</title>
 			<link>http://code.google.com/p/pidgin-facebookchat/issues/detail?id=24#c48</link>
 			<description><![CDATA[Hi all, sorry for the recent weirdness with the plugin.  v1.60 is out which addresses the major bit of these problems.<br/>


More information about the Commits mailing list