adium: 9f8d02d7: pidgin-facebookchat at r620.

zacw at adiumx.com zacw at adiumx.com
Sun Jul 12 22:30:37 EDT 2009


-----------------------------------------------------------------
Revision: 9f8d02d7c2a00b146da385054661d2e467eb46f2
Ancestor: 534ffccfd244372a8f2569168b1bc15b8c9d9054
Author: zacw at adiumx.com
Date: 2009-07-13T02:28:54
Branch: im.pidgin.adium
URL: http://d.pidgin.im/viewmtn/revision/info/9f8d02d7c2a00b146da385054661d2e467eb46f2

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

ChangeLog: 

pidgin-facebookchat at r620.


-------------- next part --------------
============================================================
--- libpurple/protocols/facebook/fb_friendlist.c	75b0969a59d34b7145d1fe3c6e4121ab9f2d9ed3
+++ libpurple/protocols/facebook/fb_friendlist.c	75b0969a59d34b7145d1fe3c6e4121ab9f2d9ed3
@@ -0,0 +1,467 @@
+/*
+ * libfacebook
+ *
+ * libfacebook is the property of its developers.  See the COPYRIGHT file
+ * for more details.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "fb_friendlist.h"
+#include "fb_connection.h"
+#include "libfacebook.h"
+#include "fb_util.h"
+
+typedef struct _MoveRequest MoveRequest;
+struct _MoveRequest {
+	char *old_group;
+	char *new_group;
+	char *who;
+};
+
+/******************************************************************************/
+/* Friend list modification methods */
+/******************************************************************************/
+static void handle_move_request(FacebookAccount *fba, MoveRequest *request)
+{
+	const gchar *old_list_id;
+	const gchar *new_list_id;
+	gchar *postdata;
+	gboolean remove_flist, no_original_list;
+	const gchar *command;
+
+	purple_debug_info("facebook",
+		"handling movement of %s from %s to %s\n",
+		request->who, request->old_group, request->new_group);
+
+	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");
+
+	if (remove_flist) {
+		command = "&remove_fl=true";
+	} else if (no_original_list) {
+		command = "&add_fl=true";
+	} else {
+		command = "&move_fl=true";
+	}
+
+	postdata = g_strdup_printf(
+		"post_form_id=%s&drag_uid=%s&user=%" G_GINT64_FORMAT
+		"&new_flid=%s&old_flid=%s%s",
+		fba->post_form_id,
+		request->who,
+		fba->uid,
+		remove_flist ? "" : new_list_id,
+		no_original_list ? "" : old_list_id,
+		command);
+
+	fb_post_or_get(fba, FB_METHOD_POST, NULL,
+		"/ajax/chat/buddy_list_settings.php",
+		postdata, NULL, NULL, FALSE);
+
+	g_free(postdata);
+
+	g_free(request->who);
+	g_free(request->old_group);
+	g_free(request->new_group);
+	g_free(request);
+}
+
+static void create_list_cb(FacebookAccount *fba, gchar *data,
+	gsize data_len, gpointer userdata)
+{
+	// NOTE: this method can also be used for movements between
+	// friend lists if necessary.
+
+	JsonParser *parser;
+	JsonObject *objnode;
+	MoveRequest *request;
+
+	// Parse out old data.
+	parser = fb_get_parser(data, data_len);
+	if (parser == NULL) {
+		return;
+	}
+
+	objnode = fb_get_json_object(parser, NULL);
+	if (!objnode ||
+	    !json_object_has_member(objnode, "payload"))
+	{
+		g_object_unref(parser);
+		return;
+	}
+
+	objnode = json_node_get_object(json_object_get_member(
+			objnode, "payload"));
+	fb_process_friend_lists(fba, objnode);
+
+	g_object_unref(parser);
+
+	// Move Friend
+	request = (MoveRequest *) userdata;
+	if (request) {
+		handle_move_request(fba, request);
+	}
+}
+
+static void create_friend_list(FacebookAccount *fba, const gchar *new_group,
+	MoveRequest *request)
+{
+	gchar *postdata;
+	gchar *new_group_escaped;
+
+	purple_debug_info("facebook", "creating friend list %s\n", new_group);
+
+	new_group_escaped = fb_strdup_withhtml(new_group);
+
+	postdata = g_strdup_printf(
+		"post_form_id=%s&create=%s&user=%" G_GINT64_FORMAT,
+		fba->post_form_id,
+		new_group_escaped,
+		fba->uid);
+
+	fb_post_or_get(fba, FB_METHOD_POST, NULL,
+		"/ajax/chat/buddy_list_settings.php",
+		postdata, create_list_cb, request, FALSE);
+
+	g_free(postdata);
+	g_free(new_group_escaped);
+}
+
+void fb_group_buddy_move(PurpleConnection *pc, const char *who,
+	const char *old_group, const char *new_group)
+{
+	FacebookAccount *fba;
+	MoveRequest *request;
+	const gchar *new_list_id;
+	
+	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)) {
+		purple_debug_info("facebook", "groups are same, not moving\n");
+		return;
+	}
+
+	// Facebook doesn't support moving yourself because you can't be in a
+	// friend list.  Let buddy list be updated as appropriate.
+	if (atoll(who) == fba->uid) {
+		purple_debug_info("facebook",
+			"moving self, do not update server\n");
+		return;
+	}
+
+	request = g_new0(MoveRequest, 1);
+	request->old_group = g_strdup(old_group);
+	request->new_group = g_strdup(new_group);
+	request->who = g_strdup(who);
+
+	new_list_id = fb_get_list_id(fba, request->new_group);
+	if (new_list_id) {
+		handle_move_request(fba, request);
+	} else {
+		create_friend_list(fba, new_group, request);
+	}
+}
+
+void fb_buddy_remove(PurpleConnection *pc, PurpleBuddy *buddy,
+		PurpleGroup *group)
+{
+	// This method should only remove a buddy from a friend list.
+	// Nothing more.  It should not defriend a user ever.  See issue
+	// #185 for a good explaination of why this is a bad idea.
+	//
+	// Moreover, defriending is such a rare operation that we should
+	// never make it easy.  Facebook intentionally hides such a action
+	// behind multiple layers of links and dialogs.
+	//
+	// 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;
+
+	purple_debug_info("facebook", "handing removal of buddy %s\n",
+		buddy->name);
+       
+	fba = pc->proto_data;
+
+	fb_group_buddy_move(pc, buddy->name, purple_group_get_name(group),
+			DEFAULT_GROUP_NAME);
+}
+
+void fb_group_rename(PurpleConnection *pc, const char *old_name,
+	PurpleGroup *group, GList *moved_buddies)
+{
+	purple_debug_info("facebook",
+		"handling group rename of %s to %s\n",
+		old_name, purple_group_get_name(group));
+	
+	// We don't do anything here.  Facebook's AJAX API for renaming groups
+	// is horribly, horribly overcomplicated.  There is no simple rename
+	// call, instead you must also pass in all the current data about the
+	// friend list and port it over.  While it is possible to implement
+	// this, it is risky and could potentially destroy a friend list if
+	// the API changes.  That's a Bad Thing(tm).  Given the risk involved
+	// with this operation and how rare it is, it's not worth it.
+	// 
+	// The problem is compounded by the fact that renaming groups triggers
+	// all sorts of weird behaviors in Pidgin.  Renaming to a new name is
+	// simple.  Renaming to an existing group name (hence a merge)
+	// triggers completely different behavior with calls to group_buddy
+	// before the call to rename.  This completely defeats the purpose of
+	// having a rename function because group_buddy is called instead.
+	//
+	// Thus, the final decision is to use the buddy_move call.
+
+	// TODO: warn users that renaming has no effect here.
+}
+
+void fb_group_remove(PurpleConnection *pc, PurpleGroup *group)
+{
+	purple_debug_info("facebook", "got group removal of %s\n",
+		purple_group_get_name(group));
+
+	// We don't do anything here.  This is because a group rename also
+	// fires a group removal event.  This assumes that the new group is
+	// equivalent to the old group, but Facebook friend lists are much more
+	// than simple groups- they are privacy control lists too.  There is
+	// no easy way to port the settings between groups.  Better off not
+	// deleting, and the user can do the cleanup with their browser.
+}
+
+/******************************************************************************/
+/* Friend list fetch methods */
+/******************************************************************************/
+
+const gchar *fb_get_list_id(FacebookAccount *fba, const gchar *list_name)
+{
+	if (!strcmp(list_name, DEFAULT_GROUP_NAME)) {
+		return "-1";
+	}
+
+	return g_hash_table_lookup(fba->friend_lists_reverse, list_name);
+}
+
+gboolean fb_process_friend_lists(FacebookAccount *fba,
+		JsonObject *buddy_list)
+{
+	JsonObject *fl_obj;
+	GList *friend_list_ids, *cur;
+
+	purple_debug_info("facebook", "processing friend list data\n");
+
+	if (!json_object_has_member(buddy_list, "flData"))
+	{
+		purple_debug_info("facebook", "no friend list data\n");
+		return FALSE;
+	}
+
+
+	fl_obj = json_node_get_object(json_object_get_member(
+			buddy_list, "flData"));
+	friend_list_ids = json_object_get_members(fl_obj);
+	for (cur = friend_list_ids; cur != NULL; cur = cur->next)
+	{
+		const gchar *id;
+		const gchar *name;
+		JsonObject *data;
+
+		id = (gchar *) cur->data;
+		data = json_node_get_object(json_object_get_member(
+				fl_obj, id));
+		name = json_node_get_string(json_object_get_member(
+				data, "n"));
+		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")) {
+				id = "-1";
+			}
+			purple_debug_info("facebook",
+					"got friend list %s with id %s\n",
+					name, id);
+			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_list_free(friend_list_ids);
+
+	return TRUE;
+}
+
+static void destroy_buddy(gpointer key, gpointer value, gpointer data)
+{
+	PurpleBuddy *buddy;
+	gchar *group_name;
+	FacebookAccount *fba;
+
+	buddy = (PurpleBuddy *) value;
+	group_name = (gchar *) key;
+	fba = (FacebookAccount *) data;
+
+	purple_debug_info("facebook", "removing %s from group %s\n",
+			buddy->name, group_name);
+	if (atoll(buddy->name) == fba->uid) {
+		purple_debug_info("facebook", "not removing self from %s\n",
+			group_name);
+		return;
+	}
+
+	purple_blist_remove_buddy(buddy);
+}
+
+static PurpleBuddy *add_buddy(FacebookAccount *fba,
+	const gchar *friend_list_id, const gchar *uid, GHashTable *cur_groups)
+{
+	const gchar *group_name;
+	PurpleGroup *fb_group;
+	PurpleBuddy *buddy;
+
+	group_name = g_hash_table_lookup(fba->friend_lists, friend_list_id);
+	if (!group_name || !strcmp(group_name, "")) {
+		purple_debug_info("facebook",
+				"did not find name of list %s\n",
+				friend_list_id);
+		group_name = DEFAULT_GROUP_NAME;
+	}
+
+	// Initialize group as necessary.
+	fb_group = purple_find_group(group_name);
+	if (fb_group == NULL)
+	{
+		purple_debug_info("facebook", "adding friend list %s\n",
+				group_name);
+		fb_group = purple_group_new(group_name);
+		purple_blist_add_group(fb_group, NULL);
+	} 
+
+	buddy = (PurpleBuddy *)g_hash_table_lookup(cur_groups, 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);
+	}
+
+	return buddy;
+}
+
+
+GList *fb_get_buddies_friend_list (FacebookAccount *fba,
+		const gchar *uid, JsonArray *friend_list_ids)
+{
+	GSList *buddies;
+	GSList *cur;       
+	GHashTable *cur_groups;
+	int i;
+	GList *final_buddies, *cur_buddy;
+
+	final_buddies = NULL;
+	buddies = purple_find_buddies(fba->account, uid);
+
+	// If we're already in the buddy list, stop.  Ignore FB info because
+	// it will be incorrect.
+	if (atoll(uid) == fba->uid && buddies != NULL) {
+		purple_debug_info("facebook",
+			"already have buddies for self, not adding\n");
+		for (cur = buddies; cur != NULL; cur = cur->next)
+		{
+			final_buddies = g_list_append(
+				final_buddies, cur->data);
+		}
+		g_slist_free(buddies);
+		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);
+	for (cur = buddies; cur != NULL; cur = cur->next)
+	{
+		const gchar *group_name;
+
+		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_slist_free(buddies);
+
+	// Create/insert necessary buddies
+	if (friend_list_ids) {
+		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));
+
+			buddy = add_buddy(fba, friend_list_id, uid, cur_groups);
+
+			final_buddies = g_list_append(final_buddies, buddy);
+		}
+	} else {
+		// No friend list data, so we use the default group.
+		final_buddies = g_list_append(final_buddies,
+			add_buddy(fba, "-1", uid, cur_groups));
+	}
+
+	// Figure out which groups/buddies are not represented.
+	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)));
+	}
+
+	// Delete remaining buddies to maintain sync state with server.
+	g_hash_table_foreach(cur_groups, destroy_buddy, fba);
+
+	// Cleanup!
+	g_hash_table_destroy(cur_groups);
+
+	return final_buddies;
+}
+
+void fb_friendlist_init(FacebookAccount *fba)
+{
+	/* data structure mapping friend list id to name.  libpurple only
+	 * recognizes name, does not have group aliases */
+	fba->friend_lists = g_hash_table_new_full(g_str_hash, g_str_equal,
+			g_free, g_free);
+	/* structure mapping names to list id for speed. */
+	fba->friend_lists_reverse = g_hash_table_new_full(g_str_hash,
+			g_str_equal, g_free, g_free);
+}
+
+void fb_friendlist_destroy(FacebookAccount *fba)
+{
+	if (fba->friend_lists) {
+		g_hash_table_destroy(fba->friend_lists);
+	}
+	if (fba->friend_lists_reverse) {
+		g_hash_table_destroy(fba->friend_lists_reverse);
+	}
+}
============================================================
--- libpurple/protocols/facebook/fb_friendlist.h	4245e211978d4e907a4e22578faaee53bed74f7c
+++ libpurple/protocols/facebook/fb_friendlist.h	4245e211978d4e907a4e22578faaee53bed74f7c
@@ -0,0 +1,48 @@
+/*
+ * libfacebook
+ *
+ * libfacebook is the property of its developers.  See the COPYRIGHT file
+ * for more details.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FACEBOOK_FRIENDLIST_H
+#define FACEBOOK_FRIENDLIST_H
+
+#include "libfacebook.h"
+
+#include <json-glib/json-glib.h>
+
+#define DEFAULT_GROUP_NAME "Facebook"
+
+/* Friend list modification methods */
+void fb_group_buddy_move(PurpleConnection *pc, const char *who,
+	const char *old_group, const char *new_group);
+void fb_group_rename(PurpleConnection *pc, const char *old_name,
+	PurpleGroup *group, GList *moved_buddies);
+void fb_group_remove(PurpleConnection *pc, PurpleGroup *group);
+void fb_buddy_remove(PurpleConnection *pc, PurpleBuddy *buddy,
+	PurpleGroup *group);
+
+/* Friend list fetch methods */
+const gchar *fb_get_list_id(FacebookAccount *fba, const gchar *list_name);
+gboolean fb_process_friend_lists(FacebookAccount *fba, JsonObject *buddy_list);
+GList *fb_get_buddies_friend_list (FacebookAccount *fba,
+		const gchar *uid, JsonArray *friend_list_ids);
+
+void fb_friendlist_init(FacebookAccount *fba);
+void fb_friendlist_destroy(FacebookAccount *fba);
+
+#endif /* FACEBOOK_FRIENDLIST_H */
============================================================
--- libpurple/protocols/facebook/Makefile.am	8f244a845ebdbc8be60a3489aa8e2f9499c91549
+++ libpurple/protocols/facebook/Makefile.am	d0ee40a9e8032bf4f2e5329a45bcf4151ad30d19
@@ -12,6 +12,8 @@ FACEBOOK_SOURCES = \
 	fb_connection.c \
 	fb_conversation.h \
 	fb_conversation.c \
+	fb_friendlist.h \
+	fb_friendlist.c \
 	fb_info.h \
 	fb_info.c \
 	fb_managefriends.h \
============================================================
--- libpurple/protocols/facebook/facebook.nsi	86d06564f9bcb28939ce31fea5cf6e899cb530a9
+++ libpurple/protocols/facebook/facebook.nsi	ae7c331b9422552cef92e0d67f9f096857745d2e
@@ -6,7 +6,7 @@ SetCompress off
 ; todo: SetBrandingImage
 ; HM NIS Edit Wizard helper defines
 !define PRODUCT_NAME "pidgin-facebookchat"
-!define PRODUCT_VERSION "1.53"
+!define PRODUCT_VERSION "1.54"
 !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	a756fad89ceaabc66d0f1f4798a348b416396471
+++ libpurple/protocols/facebook/fb_blist.c	4bba8ded502f2a2210836c692c63af63ba0abcd0
@@ -21,16 +21,19 @@
 #include "libfacebook.h"
 #include "fb_connection.h"
 #include "fb_blist.h"
+#include "fb_util.h"
+#include "fb_friendlist.h"
+#include "blist.h"
 
-#include <json-glib/json-glib.h>
-
-static void set_buddies_offline(PurpleBuddy *buddy, GHashTable *online_buddies_list)
+static void set_buddies_offline(PurpleBuddy *buddy,
+		GHashTable *online_buddies_list)
 {
 	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),
+				purple_primitive_get_id_from_type(
+					PURPLE_STATUS_OFFLINE),
 				NULL);
 	}
 }
@@ -51,39 +54,280 @@ static void buddy_icon_cb(FacebookAccoun
 
 	buddy = purple_find_buddy(fba->account, buddyname);
 	g_free(buddyname);
-	if (buddy == NULL)
-		return;
 
+	g_return_if_fail(buddy != NULL);
+
 	fbuddy = buddy->proto_data;
 
+	g_return_if_fail(fbuddy != NULL);
+
 	buddy_icon_data = g_memdup(data, data_len);
 
 	purple_buddy_icons_set_for_user(fba->account, buddy->name,
 			buddy_icon_data, data_len, fbuddy->thumb_url);
 }
 
-static void got_buddy_list_cb(FacebookAccount *fba, gchar *data,
-		gsize data_len, gpointer userdata)
+/**
+ * Find buddy names 
+ */
+static GList *get_buddies(FacebookAccount *fba, const gchar *uid,
+		const gchar *name, JsonArray *friend_list_ids)
 {
-	GSList *buddies_list;
-	GHashTable *online_buddies_list = g_hash_table_new(g_str_hash, g_str_equal);
-	PurpleBuddy *buddy;
+	GList *buddies;
+	GList *cur;
+
+	buddies = fb_get_buddies_friend_list(fba, uid, friend_list_ids);
+
+	// Initialize proto data for each buddy.
+	for (cur = buddies; cur != NULL; cur = cur->next)
+	{
+		PurpleBuddy *buddy;
+
+		buddy = (PurpleBuddy *) cur->data;
+
+		/* Set the FacebookBuddy structure */
+		if (buddy->proto_data == NULL)
+		{
+			FacebookBuddy *fbuddy;
+			gchar *buddy_icon_url;
+
+			fbuddy = g_new0(FacebookBuddy, 1);
+			fbuddy->buddy = buddy;
+			fbuddy->fba = fba;
+			fbuddy->uid = atoll(uid);
+			fbuddy->name = g_strdup(name);
+
+			// load the old buddy icon url from the icon 'checksum'
+			buddy_icon_url = (char *)
+				purple_buddy_icons_get_checksum_for_user(buddy);
+			if (buddy_icon_url != NULL)
+				fbuddy->thumb_url = g_strdup(buddy_icon_url);
+
+			buddy->proto_data = fbuddy;
+		}
+	}
+
+	return buddies;
+}
+
+static gboolean process_buddy_status(FacebookAccount *fba, PurpleBuddy *buddy,
+	JsonObject *userInfo)
+{
 	FacebookBuddy *fbuddy;
-	gchar *uid;
-	gchar *name;
-	gchar *status_text;
-	gchar *status_time_text;
+	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)
+{
+	FacebookBuddy *fbuddy;
 	gchar *buddy_icon_url;
+
+	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"));
+	if (fbuddy->thumb_url == NULL ||
+	    !g_str_equal(fbuddy->thumb_url, buddy_icon_url))
+	{
+		g_free(fbuddy->thumb_url);
+		if (g_str_equal(buddy_icon_url,
+			"http://static.ak.fbcdn.net/pics/q_silhouette.gif"))
+		{
+			fbuddy->thumb_url = NULL;
+			/* User has no icon */
+			purple_buddy_icons_set_for_user(fba->account,
+				purple_buddy_get_name(buddy), NULL, 0, NULL);
+		}
+		else
+		{
+			gchar *search_tmp;
+
+			fbuddy->thumb_url = g_strdup(buddy_icon_url);
+
+			/* small icon at http://profile.ak.facebook.com/profile6/1845/74/q800753867_2878.jpg */
+			/* bigger icon at http://profile.ak.facebook.com/profile6/1845/74/n800753867_2878.jpg */
+			search_tmp = strstr(buddy_icon_url, "/q");
+			if (search_tmp)
+				*(search_tmp + 1) = 'n';
+				
+			/* Fetch their icon */
+			fb_post_or_get(fba, FB_METHOD_GET, "profile.ak.facebook.com",
+					buddy_icon_url, NULL,
+					buddy_icon_cb, g_strdup(purple_buddy_get_name(buddy)), FALSE);
+		}
+	}
+	g_free(buddy_icon_url);
+}
+
+static void process_buddies(FacebookAccount *fba, GHashTable *online_buddies_list,
+	JsonObject *nowAvailableList, gchar *uid, JsonObject *userInfo)
+{
+	const gchar *name;
 	gboolean idle;
-	guint32 error_number;
+	GList *buddies, *cur;
+	gboolean current_buddy_online;
 
-	gchar *search_tmp;
-	gchar *tmp;
+	JsonArray *friend_list_ids;
 
-	PurpleGroup *fb_group = NULL;
+	friend_list_ids = NULL;
+	name = json_node_get_string(json_object_get_member(userInfo, "name"));
 
-	gboolean current_buddy_online = FALSE;
+	/* look for "uid":{"i":_____} */
+	if (json_object_has_member(nowAvailableList, uid))
+	{
+		JsonObject *userBlistInfo;
+		userBlistInfo = json_node_get_object(
+			json_object_get_member(nowAvailableList, uid));
+		idle = json_node_get_boolean(
+			json_object_get_member(userBlistInfo, "i"));
+		if (json_object_has_member(userBlistInfo, "fl")) {
+			friend_list_ids = json_node_get_array(
+				json_object_get_member(userBlistInfo, "fl"));
+		}
 
+		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;
+	}
+
+	/* is this us? */
+	if (atoll(uid) == fba->uid)
+	{
+		purple_connection_set_display_name(fba->pc, name);
+
+		/* check that we don't want to show ourselves */
+		current_buddy_online = !purple_account_get_bool(
+			fba->account, "facebook_hide_self", TRUE);
+	}
+
+	buddies = get_buddies(fba, uid, name, friend_list_ids);
+	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);
+
+		if (current_buddy_online)
+		{
+			/* Add buddy to the list of online buddies */
+			g_hash_table_insert(online_buddies_list, buddy->name, buddy);
+
+			// Set buddy as online in buddy list.  We check for several
+			// 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)))
+			{
+				purple_prpl_got_user_status(fba->account, buddy->name,
+					purple_primitive_get_id_from_type(
+						idle ? PURPLE_STATUS_AWAY :
+					       	PURPLE_STATUS_AVAILABLE), NULL);
+			}
+		}
+	}
+
+	/* update the blist if we have no previous alias */
+	fb_blist_set_alias(fba, uid, name);
+}
+
+static void process_notifications(FacebookAccount *fba,
+		JsonObject *notifications)
+{
+	if (notifications != NULL &&
+	    purple_account_get_check_mail(fba->account))
+	{
+		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);
+			}
+		}
+	}
+}
+
+static void got_buddy_list_cb(FacebookAccount *fba, gchar *data,
+		gsize data_len, gpointer userdata)
+{
+	GSList *buddies_list;
+	GHashTable *online_buddies_list = g_hash_table_new(
+			g_str_hash, g_str_equal);
+	gchar *uid;
+
 	purple_debug_info("facebook", "parsing buddy list\n");
 
 	if (fba == NULL)
@@ -96,60 +340,50 @@ static void got_buddy_list_cb(FacebookAc
 				_("Could not retrieve buddy list"));
 		return;
 	}
-	
+
 	purple_debug_misc("facebook", "buddy list\n%s\n", data);
-	
-	JsonNode *root;
-	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");
-
-	/* if logged out, this comes up */
-	/* for (;;);{"error":1357001,"errorSummary":"Not Logged In",
-		"errorDescription":"You must be logged in to do that.",
-		"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"}]} */
-	if (json_object_has_member(objnode, "error"))
-	{
-		error_number = json_node_get_int(json_object_get_member(objnode, "error"));
-		if (error_number)
-		{
-			purple_connection_error_reason(fba->pc,
-					PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-					json_node_dup_string(json_object_get_member(objnode, "errorDescription")));
+	gchar *error = NULL;
+	JsonObject *objnode = fb_get_json_object(parser, &error);
+	if (error) {
+		purple_connection_error_reason(
+				fba->pc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				error);
 			g_object_unref(parser);
 			return;
-		}
 	}
-
+	
 	/* look for "userInfos":{ ... }, */
 	if (!json_object_has_member(objnode, "payload"))
 	{
 		g_object_unref(parser);
 		return;
 	}
-	objnode = json_node_get_object(json_object_get_member(objnode, "payload"));
+	objnode = json_node_get_object(json_object_get_member(
+			objnode, "payload"));
 	if (!json_object_has_member(objnode, "buddy_list"))
 	{
 		g_object_unref(parser);
 		return;
 	}
-	JsonObject *buddy_list = json_node_get_object(json_object_get_member(objnode, "buddy_list"));
+	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"));
-	
+
+	fb_process_friend_lists(fba, buddy_list);
+
+	// Iterate through the list of buddy infos sent to us.	
 	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"));
+	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;
@@ -160,200 +394,34 @@ static void got_buddy_list_cb(FacebookAc
 		uid = currentUserNode->data;
 
 		JsonObject *userInfo;
-		userInfo = json_node_get_object(json_object_get_member(userInfos, uid));
-		name = json_node_dup_string(json_object_get_member(userInfo, "name"));
-
-		/* update the blist if we have no previous alias */
-		fb_blist_set_alias(fba, uid, name);
-
-		/* look for "uid":{"i":_____} */
-		if (json_object_has_member(nowAvailableList, uid))
-		{
-			JsonObject *userBlistInfo;
-			userBlistInfo = json_node_get_object(json_object_get_member(nowAvailableList, uid));
-			idle = json_node_get_boolean(json_object_get_member(userBlistInfo, "i"));
-			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;
-		}
-
-		/* Set the buddy status text and time */
-		if (json_object_has_member(userInfo, "status"))
-		{
-			status_text = json_node_dup_string(json_object_get_member(userInfo, "status"));
-		} else {
-			status_text = NULL;
-		}
-
-		/* is this us? */
-		if (atoll(uid) == fba->uid)
-		{
-			purple_connection_set_display_name(fba->pc, name);
-
-			/* set our last known status so that we don't re-set it */
-			if (status_text && !fba->last_status_message)
-				fba->last_status_message = g_strdup(status_text);
-
-			/* check that we don't want to show ourselves */
-			if (purple_account_get_bool(fba->account, "facebook_hide_self", TRUE))
-			{
-				g_free(status_text);
-				g_free(name);
-				/* go on to the next buddy */
-				continue;
-			} else {
-				current_buddy_online = TRUE;
-			}
-		}
-
-		/* Is this a new buddy? */
-		buddy = purple_find_buddy(fba->account, uid);
-		if (buddy == NULL)
-		{
-			buddy = purple_buddy_new(fba->account, uid, NULL);
-			if (fb_group == NULL)
-			{
-				fb_group = purple_group_new("Facebook");
-				purple_blist_add_group(fb_group, NULL);
-			}
-			purple_blist_add_buddy(buddy, NULL, fb_group, NULL);
-		}
-		purple_presence_set_idle(purple_buddy_get_presence(buddy), idle, 0);
-
-		/* Set the FacebookBuddy structure */
-		if (buddy->proto_data == NULL)
-		{
-			fbuddy = g_new0(FacebookBuddy, 1);
-			fbuddy->buddy = buddy;
-			fbuddy->fba = fba;
-			fbuddy->uid = atoll(uid);
-			fbuddy->name = g_strdup(name);
-
-			/* load the old buddy icon url from the icon 'checksum' */
-			buddy_icon_url = (char *)purple_buddy_icons_get_checksum_for_user(buddy);
-			if (buddy_icon_url != NULL)
-				fbuddy->thumb_url = g_strdup(buddy_icon_url);
-
-			buddy->proto_data = fbuddy;
-		} else {
-			fbuddy = buddy->proto_data;
-		}
-
-		g_free(name);
-
-		if (status_text != NULL)
-		{
-			tmp = fb_strdup_withhtml(status_text);
-			g_free(status_text);
-			status_text = tmp;
-
-			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;
-			}
-			g_free(fbuddy->status_rel_time);
-			if (status_time_text != NULL)
-			{
-				fbuddy->status_rel_time = fb_strdup_withhtml(status_time_text);
-				g_free(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))
-			{
-				tmp = fbuddy->status;
-				fbuddy->status = status_text;
-				g_free(tmp);
-				if (current_buddy_online)
-					purple_prpl_got_user_status(fba->account, buddy->name, purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE), NULL);
-			} else {
-				g_free(status_text);
-			}
-		} else {
-			if (fbuddy->status != NULL)
-			{
-				g_free(fbuddy->status);
-				fbuddy->status = NULL;
-				if (current_buddy_online)
-				{
-					/* update the status in the contact list */
-					purple_prpl_got_user_status(fba->account, buddy->name, purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE), NULL);
-				}
-			}
-		}
-
-		/* Set the buddy icon (if it hasn't changed) */
-		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);
-			if (g_str_equal(buddy_icon_url, "http://static.ak.fbcdn.net/pics/q_silhouette.gif"))
-			{
-				fbuddy->thumb_url = NULL;
-				/* User has no icon */
-				purple_buddy_icons_set_for_user(fba->account,
-						purple_buddy_get_name(buddy), NULL, 0, NULL);
-			}
-			else
-			{
-				fbuddy->thumb_url = g_strdup(buddy_icon_url);
-
-				/* small icon at http://profile.ak.facebook.com/profile6/1845/74/q800753867_2878.jpg */
-				/* bigger icon at http://profile.ak.facebook.com/profile6/1845/74/n800753867_2878.jpg */
-				search_tmp = strstr(buddy_icon_url, "/q");
-				if (search_tmp)
-					*(search_tmp + 1) = 'n';
-				
-				/* Fetch their icon */
-				fb_post_or_get(fba, FB_METHOD_GET, "profile.ak.facebook.com",
-						buddy_icon_url + strlen("http://profile.ak.facebook.com"), NULL,
-						buddy_icon_cb, g_strdup(purple_buddy_get_name(buddy)), FALSE);
-			}
-		}
-		g_free(buddy_icon_url);
-
-		if (current_buddy_online)
-		{
-			/* Add buddy to the list of online buddies */
-			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);
-		}
+		userInfo = json_node_get_object(json_object_get_member(
+					userInfos, uid));
+		// Process the user, which generally consists of updating
+		// state info such as name, idle item, status message,etc.
+		process_buddies(fba, online_buddies_list, nowAvailableList,
+				uid, userInfo);
 	}
 	g_list_free(userIds);
 
+	// Set users offline.  We do this in a seperate function because FB
+	// only sends us a list of users who are online.  We find the users
+	// that are not in the union of of buddy list users + online, and
+	// mark them as offline.
 	buddies_list = purple_find_buddies(fba->account, NULL);
 	if (buddies_list != NULL)
 	{
-		g_slist_foreach(buddies_list, (GFunc)set_buddies_offline, online_buddies_list);
+		g_slist_foreach(
+			buddies_list,
+			(GFunc)set_buddies_offline, online_buddies_list);
 		g_slist_free(buddies_list);
 	}
 	g_hash_table_destroy(online_buddies_list);
 	
-	if (notifications != NULL && purple_account_get_check_mail(fba->account))
-	{
-		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);
-			}
-		}
-	}
-	
+	// The buddy list also contains notifications data.  Process and
+	// display is appropriate.
+	process_notifications(fba, json_node_get_object(
+		json_object_get_member(objnode, "notifications")));
+
 	g_object_unref(parser);
 }
 
@@ -426,3 +494,24 @@ void fb_blist_set_alias(FacebookAccount 
 	/* In case user removes an alias, we have the server as fallback */
 	serv_got_alias(fba->pc, id, name);
 }
+
+void fb_blist_init(FacebookAccount *fba)
+{
+	fb_friendlist_init(fba);
+
+	fb_get_buddy_list(fba);
+
+	/* periodically check for updates to your buddy list */
+	fba->buddy_list_timer = purple_timeout_add_seconds(60,
+			fb_get_buddy_list, fba);
+
+}
+
+void fb_blist_destroy(FacebookAccount *fba)
+{
+	if (fba->buddy_list_timer) {
+		purple_timeout_remove(fba->buddy_list_timer);
+	}
+
+	fb_friendlist_destroy(fba);
+}
============================================================
--- libpurple/protocols/facebook/fb_blist.h	282d636d6e7f6011bec4f39d5cc9483b1be8b199
+++ libpurple/protocols/facebook/fb_blist.h	e77ea2b71a92a022052a1083631d810b540385b4
@@ -29,4 +29,7 @@ void fb_blist_set_alias(FacebookAccount 
 void fb_blist_set_alias(FacebookAccount *fba, const char *id,
 		const char *name);
 
+void fb_blist_init(FacebookAccount *fba);
+void fb_blist_destroy(FacebookAccount *fba);
+
 #endif /* FACEBOOK_BLIST_H */
============================================================
--- libpurple/protocols/facebook/fb_conversation.c	259358150129a9c835a26193282d46f2dd284d18
+++ libpurple/protocols/facebook/fb_conversation.c	c7a209ce60af9d7660eeceecb1941275fe47bbe1
@@ -49,7 +49,7 @@ void fb_conversation_handle_message(Face
 	if (fba->uid != atoll(from) || fba->uid == atoll(to)) {
 		purple_debug_info("facebook",
 			"displaying received message %lld: %s\n",
-			message_time, message_text);
+			(long long int) message_time, message_text);
 		// TODO/FIXME: cheat here by changing formatting colors.
 		// Or add an option to just disable history on conv open.  TBD.
 		serv_got_im(fba->pc, from, message_text,
@@ -68,7 +68,7 @@ void fb_conversation_handle_message(Face
 	{
 		purple_debug_info("facebook",
 			"displaying sent message %lld: %s\n",
-			message_time, message_text);
+			(long long int) message_time, message_text);
 
 		serv_got_im(fba->pc, to, message_text,
 			log? 
@@ -96,7 +96,6 @@ static void fb_history_fetch_cb(Facebook
 	gsize data_len, gpointer userdata)
 {
 	JsonParser *parser;
-	JsonNode *root;
 	JsonObject *object, *payload;
 	JsonArray *history;
 	guint i;
@@ -114,10 +113,9 @@ static void fb_history_fetch_cb(Facebook
 	min_time = atoll((char *) userdata);
 	g_free(userdata);
 	purple_debug_info("facebook", "history fetch with min time of %lld\n",
-		       min_time);	
+		       (long long int) min_time);	
 
-	root = json_parser_get_root(parser);
-	object = json_node_get_object(root);
+	object = fb_get_json_object(parser, NULL);
 	payload = json_node_get_object(
 		json_object_get_member(object, "payload"));
 	history = json_node_get_array(
@@ -159,7 +157,7 @@ static void fb_history_fetch_cb(Facebook
 			if (message_time > min_time) {
 				purple_debug_info("facebook",
 					"displaying history message %lld\n",
-					message_time);
+					(long long int) message_time);
 				fb_conversation_handle_message(
 					fba, from, to, message_time, message,
 					min_time != 0);
@@ -188,7 +186,7 @@ void fb_history_fetch(FacebookAccount *f
 	gchar *url = g_strdup_printf("/ajax/chat/history.php?id=%s", who);
 	fb_post_or_get(
 		fba, FB_METHOD_GET, NULL, url, NULL, fb_history_fetch_cb,
-		g_strdup_printf("%lld", min_time), FALSE);
+		g_strdup_printf("%lld", (long long int) min_time), FALSE);
 	g_free(url);
 }
 
@@ -224,7 +222,10 @@ static void fb_conversation_created(Purp
 	purple_debug_info("facebook", "conversation created with %s\n",
 		conv->name);
 
-	fb_history_fetch(account->gc->proto_data, conv->name, TRUE);
+	if (purple_account_get_bool(account, "facebook_show_history", TRUE))
+	{
+		fb_history_fetch(account->gc->proto_data, conv->name, TRUE);
+	}
 }
 
 gboolean fb_conversation_is_fb(PurpleConversation *conv)
============================================================
--- libpurple/protocols/facebook/fb_info.c	3eb9ceaf6170bc0b2ab8f2ef73d9adec28363839
+++ libpurple/protocols/facebook/fb_info.c	b41f1bf46271eaa87317bdaa410b13abde797e7a
@@ -20,6 +20,7 @@
 
 #include "fb_connection.h"
 #include "fb_info.h"
+#include "fb_blist.h"
 
 /*
  * TODO: Do we really want to do this?  Maybe we could just set a
@@ -130,7 +131,7 @@ static void fb_get_info_cb(FacebookAccou
 		value_tmp2 = g_strndup(value_tmp, strstr(value_tmp, "</title>")-value_tmp);
 		value_tmp = g_strchomp(purple_markup_strip_html(value_tmp2));
 		purple_notify_user_info_add_pair(user_info, _("Name"), value_tmp);
-		serv_got_alias(fba->pc, uid, value_tmp);
+		fb_blist_set_alias(fba, uid, value_tmp);
 		g_free(value_tmp);
 		g_free(value_tmp2);
 	}
============================================================
--- libpurple/protocols/facebook/fb_managefriends.c	48d9104fae04b40e366b570f78e4b366e0d87a43
+++ libpurple/protocols/facebook/fb_managefriends.c	1d9f2f9e32feef9aa049321c952ab8cb205f1162
@@ -87,6 +87,9 @@ static void fb_check_friend_request_cb(F
 	FacebookBuddy *buddy;
 	gchar *search_start = data;
 
+	g_return_if_fail(data_len > 0);
+	g_return_if_fail(data != NULL);
+
 	/* loop through the data and look for confirm_friend_add_([0-9]*)" */
 	while ((search_start = strstr(search_start, uid_pre_text)))
 	{
@@ -169,10 +172,14 @@ void fb_add_buddy(PurpleConnection *pc, 
 
 	if (!purple_account_get_bool(
 				fba->account, "facebook_manage_friends", FALSE)) {
+		/*
+		 * We used to pop up dialogs here but if a user renamed a group,
+		 * this would spawn message for each person in the buddy list.  Bad!
 		purple_notify_info(fba->pc, _("Friend not added"),
 				_("Adding Facebook friends via Pidgin is disabled"),
 				_("Either add a friend via Facebook.com or edit your account preferences"));
-		// TODO: Message here
+		*/
+		purple_debug_warning("facebook", "attempted to add %s but was blocked\n", buddy->name);
 		return;
 	}
 
@@ -197,24 +204,3 @@ void fb_add_buddy(PurpleConnection *pc, 
 	g_free(url);
 }
 
-#if 0
-/* This code should never be reinstated in it's current form.  Period.  See
- * issue 185 for why */
-static void fb_remove_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group)
-{
-	gchar *postdata;
-	FacebookAccount *fba = pc->proto_data;
-
-	if (atoll(buddy->name) == fba->uid)
-	{
-		purple_account_set_bool(fba->account, "facebook_hide_self", TRUE);
-		return;
-	}
-
-	postdata = g_strdup_printf("uid=%s&post_form_id=%s", buddy->name, fba->post_form_id);
-
-	fb_post_or_get(fba, FB_METHOD_POST, NULL, "/ajax/removefriend.php", postdata, NULL, NULL, FALSE);
-
-	g_free(postdata);
-}
-#endif
============================================================
--- libpurple/protocols/facebook/fb_messages.c	55cba18f6f06fcf967d93af64fce1cbb06224521
+++ libpurple/protocols/facebook/fb_messages.c	09b6b82775e77886d5aff74654063c9f70e7e7fb
@@ -22,9 +22,8 @@
 #include "fb_connection.h"
 #include "fb_conversation.h"
 #include "fb_blist.h"
+#include "fb_util.h"
 
-#include <json-glib/json-glib.h>
-
 #include "conversation.h"
 
 typedef struct _FacebookOutgoingMessage FacebookOutgoingMessage;
@@ -207,18 +206,14 @@ static void got_new_messages(FacebookAcc
 		return;
 	}
 
-	JsonNode *root;
-	root = json_parser_get_root(parser);
+	JsonObject *objnode = fb_get_json_object(parser, NULL);
 
-	JsonObject *objnode;
-	objnode = json_node_get_object(root);
-
 	if (json_object_has_member(objnode, "t")) {
 		const gchar* command = json_node_get_string(json_object_get_member(objnode, "t"));
 		if (g_str_equal(command, "refresh")) {
-			int seq = json_node_get_int(json_object_get_member(objnode, "seq"));
-			if (seq) {
-				fba->message_fetch_sequence = seq;	
+			if (json_object_has_member(objnode, "seq")) {
+				fba->message_fetch_sequence = json_node_get_int(
+					json_object_get_member(objnode, "seq"));
 			}
 
 			/* grab history items for all open conversations */
@@ -297,7 +292,7 @@ static gboolean fb_get_new_messages(Face
 
 	fetch_server = g_strdup_printf("%d.channel%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", time(NULL), (fba->is_idle?"false":"true"), fba->uid, fba->message_fetch_sequence);
+	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);
 
 	fb_post_or_get(fba, FB_METHOD_GET, fetch_server, fetch_url, NULL, got_new_messages, fba->pc, TRUE);
 	fba->last_messages_download_time = now;
@@ -311,29 +306,12 @@ static void fb_send_im_cb(FacebookAccoun
 static void fb_send_im_cb(FacebookAccount *fba, gchar *data, gsize data_len, gpointer user_data)
 {
 	FacebookOutgoingMessage *msg = user_data;
-	gint error_number;
-	const gchar *error_summary;
 	JsonParser *parser;
-	JsonNode *root;
 	JsonObject *object;
 	PurpleConversation *conv;
+	gchar *error = NULL;
 
-	/* NULL data crashes on Windows */
-	if (data == NULL)
-	{
-		data = "(null)";
-	}
-	
 	purple_debug_misc("facebook", "sent im response: %s\n", data);
-	/* for (;;);{"error":1356003,"errorSummary":"Send destination not online",
-		"errorDescription":"This person is no longer online.","payload":null,
-		"bootload":[{"name":"js\/common.js.pkg.php","type":"js",
-		"src":"http:\/\/static.ak.fbcdn.net\/rsrc.php\/pkg\/59\/98936\
-		/js\/common.js.pkg.php"}]} */
-	/* for (;;);{"error":0,"errorSummary":"","errorDescription":"No error.",
-		"payload":[],"bootload":[{"name":"js\/common.js.pkg.php","type":"js",
-		"src":"http:\/\/static.ak.fbcdn.net\/rsrc.php\/pkg\/59\/98936\
-		/js\/common.js.pkg.php"}]} */
 	
 	parser = fb_get_parser(data, data_len);
 	if (!parser) {
@@ -341,15 +319,11 @@ static void fb_send_im_cb(FacebookAccoun
 		purple_debug_warning("facebook", "bad data while parsing sent IM\n");
 		return;
 	}
-	root = json_parser_get_root(parser);
-	object = json_node_get_object(root);
+	object = fb_get_json_object(parser, &error);
 	
-	error_number = json_node_get_int(json_object_get_member(object, "error"));
-	error_summary = json_node_get_string(json_object_get_member(object, "errorSummary"));
-	
-	if (error_number)
+	if (error)
 	{
-		purple_debug_error("facebook", "sent im error: %s\n", error_summary);
+		purple_debug_error("facebook", "sent im error: %s\n", error);
 		/* there was an error, either report it or retry */
 		if (msg->retry_count++ < FB_MAX_MSG_RETRY)
 		{
@@ -362,7 +336,7 @@ 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_summary,
+			purple_conversation_write(conv, NULL, error,
 					PURPLE_MESSAGE_ERROR, msg->time);
 		}
 		
@@ -381,7 +355,7 @@ static gboolean fb_send_im_fom(FacebookO
 	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,
-			msg->time,
+			(gulong) msg->time,
 			msg->fba->post_form_id ? msg->fba->post_form_id : "0");
 	g_free(encoded_message);
 
@@ -432,7 +406,7 @@ void got_reconnect_json(FacebookAccount 
 	gchar *new_channel_number;
 	
 	JsonParser *parser;
-	JsonNode *root;
+	JsonObject *objnode;
 
 	parser = fb_get_parser(data, data_len);
 
@@ -445,9 +419,7 @@ void got_reconnect_json(FacebookAccount 
 		return;
 	}
 
-	root = json_parser_get_root(parser);
-	JsonObject *objnode;
-	objnode = json_node_get_object(root);
+	objnode = fb_get_json_object(parser, NULL);
 
 	JsonObject *payload = json_node_get_object(json_object_get_member(objnode, "payload"));
 	
============================================================
--- libpurple/protocols/facebook/fb_util.c	23606c4fef64d4b023b7dca41ac62a8b499a4924
+++ libpurple/protocols/facebook/fb_util.c	8d78175db675e416230954b25937b58700eb4b26
@@ -24,9 +24,179 @@
  * UTILITY CODE                                                              *
  *****************************************************************************/
 
+gchar *fb_convert_unicode(const gchar *input)
+{
+	/* \u00e9t\u00e9 should be ?t? */
+
+	gunichar unicode_char;
+	gchar unicode_char_str[6];
+	gint unicode_char_len;
+	gchar *next_pos;
+	gchar *input_string;
+	gchar *output_string;
+
+	if (input == NULL)
+		return NULL;
+
+	next_pos = input_string = g_strdup(input);
+
+	/* purple_debug_info("facebook", "unicode convert: in: %s\n", input); */
+	while ((next_pos = strstr(next_pos, "\\u")))
+	{
+		/* grab the unicode */
+		sscanf(next_pos, "\\u%4x", &unicode_char);
+		/* turn it to a char* */
+		unicode_char_len = g_unichar_to_utf8(unicode_char, unicode_char_str);
+		/* shove it back into the string */
+		g_memmove(next_pos, unicode_char_str, unicode_char_len);
+		/* move all the data after the \u0000 along */
+		g_stpcpy(next_pos + unicode_char_len, next_pos + 6);
+	}
+
+	/* purple_debug_info("facebook", "unicode convert: out: %s\n", input); */
+	output_string = g_strcompress(input_string);
+	g_free(input_string);
+
+	return output_string;
+}
+
+/* Like purple_strdup_withhtml, but escapes htmlentities too */
+gchar *fb_strdup_withhtml(const gchar *src)
+{
+	gulong destsize, i, j;
+	gchar *dest;
+
+	g_return_val_if_fail(src != NULL, NULL);
+
+	/* New length is (length of src) + (number of \n's * 3) + (number of &'s * 5) +
+		(number of <'s * 4) + (number of >'s *4) + (number of "'s * 6) -
+		(number of \r's) + 1 */
+	destsize = 1;
+	for (i = 0; src[i] != '\0'; i++)
+	{
+		if (src[i] == '\n' || src[i] == '<' || src[i] == '>')
+			destsize += 4;
+		else if (src[i] == '&')
+			destsize += 5;
+		else if (src[i] == '"')
+			destsize += 6;
+		else if (src[i] != '\r')
+			destsize++;
+	}
+
+	dest = g_malloc(destsize);
+
+	/* Copy stuff, ignoring \r's, because they are dumb */
+	for (i = 0, j = 0; src[i] != '\0'; i++) {
+		if (src[i] == '\n') {
+			strcpy(&dest[j], "<BR>");
+			j += 4;
+		} else if (src[i] == '<') {
+			strcpy(&dest[j], "&lt;");
+			j += 4;
+		} else if (src[i] == '>') {
+			strcpy(&dest[j], "&gt;");
+			j += 4;
+		} else if (src[i] == '&') {
+			strcpy(&dest[j], "&amp;");
+			j += 5;
+		} else if (src[i] == '"') {
+			strcpy(&dest[j], "&quot;");
+			j += 6;
+		} else if (src[i] != '\r')
+			dest[j++] = src[i];
+	}
+
+	dest[destsize-1] = '\0';
+
+	return dest;
+}
+
+gint64 fb_time_kludge(gint initial_time)
+{
+	if (sizeof(gint) >= sizeof(gint64))
+		return initial_time;
+	
+	gint64 now_millis = (gint64) time(NULL);
+	now_millis *= 1000;
+	now_millis &= 0xFFFFFFFF00000000LL;
+	gint64 final_time = now_millis | initial_time;
+
+	return final_time;
+}
+
+JsonParser *fb_get_parser(const gchar *data, gsize data_len)
+{
+	JsonParser *parser;
+
+	if (data == NULL) {
+		return NULL;
+	}
+
+	data = g_strstr_len(data, data_len, "for (;;);");
+	if (!data) {
+		return NULL;
+	} else {
+		data += strlen("for (;;);");
+	}
+
+	parser = json_parser_new();
+	if (!json_parser_load_from_data(parser, data, -1, NULL)) {
+		g_object_unref(parser);
+		return NULL;
+	}
+
+	return parser;
+}
+
+JsonObject *fb_get_json_object(JsonParser *parser, char **error_message)
+{
+	JsonNode *root;
+	root = json_parser_get_root(parser);
+	JsonObject *objnode;
+	objnode = json_node_get_object(root);
+
+	/* Sample error messages */
+	/* for (;;);{"error":1357001,"errorSummary":"Not Logged In",
+		"errorDescription":"You must be logged in to do that.",
+		"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"}]} */
+	if (json_object_has_member(objnode, "error"))
+	{
+		guint32 error_number;
+		const char *summary;
+		const char *description;
+
+		error_number = json_node_get_int(
+			json_object_get_member(objnode, "error"));
+		summary = json_node_get_string(
+			json_object_get_member(objnode, "errorSummary"));
+		description = json_node_get_string(
+			json_object_get_member(objnode, "errorDescription"));
+
+		if (error_number)
+		{
+			purple_debug_error("facebook",
+				"got error from facebook of %s (%s)",
+				summary, description);
+			// Pass error message to calling function if they asked for it.
+			if (error_message) {
+				*error_message = g_strdup(description);
+			}
+		}
+	}
+
+	return objnode;
+}
+
 /* Converts *text* into <b>text</b>  and _text_ into <i>text</i> */
 gchar *fb_replace_styled_text(const gchar *text)
 {
+#ifdef __ARM_EABI__
+	return g_strdup(text);
+#else /*__ARM_EABI__*/
+#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 14
 	if (glib_check_version(2, 14, 0))
 	{
 		return g_strdup(text);
@@ -52,13 +222,27 @@ gchar *fb_replace_styled_text(const gcha
 		
 		dup_text = g_strdup(text);
 		midway_string = g_regex_replace(underline_regex, dup_text,
-			-1, 0, "<u>\\1</u>", 0, NULL);
+			strlen(dup_text), 0, "<u>\\1</u>", 0, NULL);
+		if (midway_string == NULL)
+		{
+			purple_debug_warning("facebook", "regex failed for underline\n");
+			return dup_text;
+		}
 		g_free(dup_text);
 		output_string = g_regex_replace(bold_regex, midway_string,
-			-1, 0, "\\1<b>\\2</b>", 0, NULL);
+			strlen(midway_string), 0, "\\1<b>\\2</b>", 0, NULL);
+		if (output_string == NULL)
+		{
+			purple_debug_warning("facebook", "regex failed for bold\n");
+			return midway_string;
+		}
 		g_free(midway_string);
 		
 		return output_string;
 	}
+#else /* GLIB check */
+	return g_strdup(text);
+#endif /* GLIB check */
+#endif /*__ARM_EABI__*/
 }
 
============================================================
--- libpurple/protocols/facebook/fb_util.h	33093b85583658b53d9519de66ab7da32ebd5076
+++ libpurple/protocols/facebook/fb_util.h	53baf5d5d5678ff04dc221e8d629b56d3f3a67f5
@@ -22,8 +22,15 @@
 #define FACEBOOK_UTIL_H
 
 #include "libfacebook.h"
+#include <json-glib/json-glib.h>
 
+JsonParser *fb_get_parser(const gchar *data, gsize data_len);
+JsonObject *fb_get_json_object(JsonParser *parser, char **error_message);
+
 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);
 
 #endif /* FACEBOOK_UTIL_H */
 
============================================================
--- libpurple/protocols/facebook/libfacebook.c	4a5dcafa34bf51425459fa4d44852b720bbc3661
+++ libpurple/protocols/facebook/libfacebook.c	bf869dde3e462fc590369c74f963d444c2d2cbe9
@@ -27,137 +27,9 @@
 #include "fb_messages.h"
 #include "fb_notifications.h"
 #include "fb_search.h"
+#include "fb_friendlist.h"
 
 /******************************************************************************/
-/* Utility functions */
-/******************************************************************************/
-
-gchar *fb_convert_unicode(const gchar *input)
-{
-	/* \u00e9t\u00e9 should be ?t? */
-
-	gunichar unicode_char;
-	gchar unicode_char_str[6];
-	gint unicode_char_len;
-	gchar *next_pos;
-	gchar *input_string;
-	gchar *output_string;
-
-	if (input == NULL)
-		return NULL;
-
-	next_pos = input_string = g_strdup(input);
-
-	/* purple_debug_info("facebook", "unicode convert: in: %s\n", input); */
-	while ((next_pos = strstr(next_pos, "\\u")))
-	{
-		/* grab the unicode */
-		sscanf(next_pos, "\\u%4x", &unicode_char);
-		/* turn it to a char* */
-		unicode_char_len = g_unichar_to_utf8(unicode_char, unicode_char_str);
-		/* shove it back into the string */
-		g_memmove(next_pos, unicode_char_str, unicode_char_len);
-		/* move all the data after the \u0000 along */
-		g_stpcpy(next_pos + unicode_char_len, next_pos + 6);
-	}
-
-	/* purple_debug_info("facebook", "unicode convert: out: %s\n", input); */
-	output_string = g_strcompress(input_string);
-	g_free(input_string);
-
-	return output_string;
-}
-
-/* Like purple_strdup_withhtml, but escapes htmlentities too */
-gchar *fb_strdup_withhtml(const gchar *src)
-{
-	gulong destsize, i, j;
-	gchar *dest;
-
-	g_return_val_if_fail(src != NULL, NULL);
-
-	/* New length is (length of src) + (number of \n's * 3) + (number of &'s * 5) +
-		(number of <'s * 4) + (number of >'s *4) + (number of "'s * 6) -
-		(number of \r's) + 1 */
-	destsize = 1;
-	for (i = 0; src[i] != '\0'; i++)
-	{
-		if (src[i] == '\n' || src[i] == '<' || src[i] == '>')
-			destsize += 4;
-		else if (src[i] == '&')
-			destsize += 5;
-		else if (src[i] == '"')
-			destsize += 6;
-		else if (src[i] != '\r')
-			destsize++;
-	}
-
-	dest = g_malloc(destsize);
-
-	/* Copy stuff, ignoring \r's, because they are dumb */
-	for (i = 0, j = 0; src[i] != '\0'; i++) {
-		if (src[i] == '\n') {
-			strcpy(&dest[j], "<BR>");
-			j += 4;
-		} else if (src[i] == '<') {
-			strcpy(&dest[j], "&lt;");
-			j += 4;
-		} else if (src[i] == '>') {
-			strcpy(&dest[j], "&gt;");
-			j += 4;
-		} else if (src[i] == '&') {
-			strcpy(&dest[j], "&amp;");
-			j += 5;
-		} else if (src[i] == '"') {
-			strcpy(&dest[j], "&quot;");
-			j += 6;
-		} else if (src[i] != '\r')
-			dest[j++] = src[i];
-	}
-
-	dest[destsize-1] = '\0';
-
-	return dest;
-}
-
-JsonParser *fb_get_parser(const gchar *data, gsize data_len)
-{
-	JsonParser *parser;
-
-	if (data == NULL) {
-		return NULL;
-	}
-
-	data = g_strstr_len(data, data_len, "for (;;);");
-	if (!data) {
-		return NULL;
-	} else {
-		data += strlen("for (;;);");
-	}
-
-	parser = json_parser_new();
-	if (!json_parser_load_from_data(parser, data, -1, NULL)) {
-		g_object_unref(parser);
-		return NULL;
-	}
-
-	return parser;
-}
-
-gint64 fb_time_kludge(gint initial_time)
-{
-	if (sizeof(gint) >= sizeof(gint64))
-		return initial_time;
-	
-	gint64 now_millis = (gint64) time(NULL);
-	now_millis *= 1000;
-	now_millis &= 0xFFFFFFFF00000000LL;
-	gint64 final_time = now_millis | initial_time;
-
-	return final_time;
-}
-
-/******************************************************************************/
 /* PRPL functions */
 /******************************************************************************/
 
@@ -269,17 +141,12 @@ static void fb_login_cb(FacebookAccount 
 
 	/* This will kick off our long-poll message retrieval loop */
 	fb_get_post_form_id(fba);
-	fb_get_buddy_list(fba);
 	fb_check_friend_requests(fba);
 
 	/* periodically check for people adding you to their facebook friend list */
 	fba->friend_request_timer = purple_timeout_add_seconds(60 * 5,
 			fb_check_friend_requests, fba);
 
-	/* periodically check for updates to your buddy list */
-	fba->buddy_list_timer = purple_timeout_add_seconds(60,
-			fb_get_buddy_list, fba);
-
 	/* periodically check for new notifications */
 	fba->notifications_timer = purple_timeout_add_seconds(60,
 			(GSourceFunc)fb_get_notifications_feed, fba);
@@ -298,6 +165,9 @@ static void fb_login_cb(FacebookAccount 
 	fba->perpetual_messages_timer = purple_timeout_add_seconds(15,
 			(GSourceFunc)fb_get_messages_failsafe, fba);
 
+	/* init blist subsystem */
+	fb_blist_init(fba);
+
 	/* init conversation subsystem */
 	fb_conversation_init(fba);
 }
@@ -376,6 +246,9 @@ static void fb_close(PurpleConnection *p
 
 	purple_debug_info("facebook", "unloading plugin\n");
 
+	/* destroy blist subsystem */
+	fb_blist_destroy(fba);
+
 	/* destroy conversation subsystem */
 	fb_conversation_destroy(fba);
 
@@ -410,9 +283,6 @@ static void fb_close(PurpleConnection *p
 			postdata, NULL, NULL, FALSE);
 	g_free(postdata);
 
-	if (fba->buddy_list_timer) {
-		purple_timeout_remove(fba->buddy_list_timer);
-	}
 	if (fba->friend_request_timer) {
 		purple_timeout_remove(fba->friend_request_timer);
 	}
@@ -518,7 +388,7 @@ static void fb_set_status_ok_cb(gpointer
 		postdata = g_strdup_printf("profile_id=%" G_GINT64_FORMAT "&clear=1&post_form_id=%s",
 				fba->uid, fba->post_form_id);
 
-	fb_post_or_get(fba, FB_METHOD_POST, NULL, "/updatestatus.php",
+	fb_post_or_get(fba, FB_METHOD_POST, NULL, "/ajax/updatestatus.php",
 			postdata, NULL, NULL, FALSE);
 
 	g_free(postdata);
@@ -567,6 +437,7 @@ static void fb_buddy_free(PurpleBuddy *b
 	}
 }
 
+#if PURPLE_MAJOR_VERSION >= 2 && PURPLE_MINOR_VERSION >= 5
 static GHashTable *fb_get_account_text_table(PurpleAccount *account)
 {
 	GHashTable *table;
@@ -577,6 +448,7 @@ static GHashTable *fb_get_account_text_t
 
 	return table;
 }
+#endif
 
 /******************************************************************************/
 /* Plugin functions */
@@ -615,6 +487,17 @@ static void fb_display_plugin_info(Purpl
 			_("Version"), FACEBOOK_PLUGIN_VERSION);
 }
 
+static void fb_refresh_blist(PurplePluginAction *action)
+{
+	PurpleConnection *pc;
+	FacebookAccount *fba;
+
+	pc = (PurpleConnection *) action->context;
+	fba = pc->proto_data;
+
+	fb_get_buddy_list(fba);
+}
+
 static GList *fb_actions(PurplePlugin *plugin, gpointer context)
 {
 	GList *m = NULL;
@@ -632,6 +515,11 @@ static GList *fb_actions(PurplePlugin *p
 			fb_search_users);
 	m = g_list_append(m, act);
 
+	// TODO: remove, this is for testing.  REMOVE.
+	act = purple_plugin_action_new(_("Refresh buddy list..."),
+			fb_refresh_blist);
+	m = g_list_append(m, act);
+
 	return m;
 }
 
@@ -646,8 +534,8 @@ static GList *fb_node_menu(PurpleBlistNo
 		buddy = (PurpleBuddy *)node;
 		
 		act = purple_menu_action_new(_("_Poke"),
-										PURPLE_CALLBACK(fb_blist_poke_buddy),
-										NULL, NULL);
+				PURPLE_CALLBACK(fb_blist_poke_buddy),
+				NULL, NULL);
 		m = g_list_append(m, act);
 	}
 	return m;
@@ -674,17 +562,35 @@ static void plugin_init(PurplePlugin *pl
 	PurplePluginProtocolInfo *prpl_info = info->extra_info;
 
 	/* Add options to the advanced screen in the account settings */
-	option = purple_account_option_bool_new(_("Hide myself in the Buddy List"), "facebook_hide_self", TRUE);
-	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
+	option = purple_account_option_bool_new(
+		_("Show history in new conversations"),
+		"facebook_show_history", TRUE);
+	prpl_info->protocol_options = g_list_append(
+		prpl_info->protocol_options, option);
 
-	option = purple_account_option_bool_new(_("Set Facebook status through Pidgin status"), "facebook_set_status_through_pidgin", FALSE);
-	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(
+		prpl_info->protocol_options, option);
 
-	option = purple_account_option_bool_new(_("Show Facebook notifications as e-mails in Pidgin"), "facebook_get_notifications", TRUE);
-	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
+	option = purple_account_option_bool_new(
+		_("Set Facebook status through Pidgin status"),
+		"facebook_set_status_through_pidgin", FALSE);
+	prpl_info->protocol_options = g_list_append(
+		prpl_info->protocol_options, option);
 
-	option = purple_account_option_bool_new(_("Edit Facebook friends from Pidgin"), "facebook_manage_friends", FALSE);
-	prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
+	option = purple_account_option_bool_new(
+		_("Show Facebook notifications as e-mails in Pidgin"),
+		"facebook_get_notifications", TRUE);
+	prpl_info->protocol_options = g_list_append(
+		prpl_info->protocol_options, option);
+
+	option = purple_account_option_bool_new(
+		_("Edit Facebook friends from Pidgin"),
+		"facebook_manage_friends", FALSE);
+	prpl_info->protocol_options = g_list_append(
+		prpl_info->protocol_options, option);
 }
 
 static PurplePluginProtocolInfo prpl_info = {
@@ -714,7 +620,7 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,                   /* change_passwd */
 	fb_add_buddy,           /* add_buddy */
 	NULL,                   /* add_buddies */
-	NULL,                   /* remove_buddy */
+	fb_buddy_remove,        /* remove_buddy */
 	NULL,                   /* remove_buddies */
 	NULL,                   /* add_permit */
 	NULL,                   /* add_deny */
@@ -733,13 +639,13 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,                   /* get_cb_info */
 	NULL,                   /* get_cb_away */
 	NULL,                   /* alias_buddy */
-	NULL,                   /* group_buddy */
-	NULL,                   /* rename_group */
+	fb_group_buddy_move,    /* group_buddy */
+	fb_group_rename,        /* rename_group */
 	fb_buddy_free,          /* buddy_free */
 	fb_conversation_closed, /* convo_closed */
 	purple_normalize_nocase,/* normalize */
 	NULL,                   /* set_buddy_icon */
-	NULL,                   /* remove_group */
+	fb_group_remove,        /* remove_group */
 	NULL,                   /* get_cb_real_name */
 	NULL,                   /* set_chat_topic */
 	NULL,                   /* find_blist_chat */
@@ -756,8 +662,12 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,                   /* unregister_user */
 	NULL,                   /* send_attention */
 	NULL,                   /* attention_types */
+#if PURPLE_MAJOR_VERSION >= 2 && PURPLE_MINOR_VERSION >= 5
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
 	fb_get_account_text_table, /* get_account_text_table */
+#else
+	(gpointer) sizeof(PurplePluginProtocolInfo)
+#endif
 };
 
 static PurplePluginInfo info = {
============================================================
--- libpurple/protocols/facebook/libfacebook.h	e5edab36948064137e8888d3aa9da077f34e2589
+++ libpurple/protocols/facebook/libfacebook.h	7d04fd4027fa7d06bc78b44177216e6d018567c7
@@ -21,7 +21,7 @@
 #ifndef LIBFACEBOOK_H
 #define LIBFACEBOOK_H
 
-#define FACEBOOK_PLUGIN_VERSION "1.53"
+#define FACEBOOK_PLUGIN_VERSION "1.54"
 #define FACEBOOK_PLUGIN_ID "prpl-bigbrownchunx-facebookim"
 
 #include <glib.h>
@@ -71,9 +71,11 @@
 #	include <zlib.h>
 #endif
 
-#define FB_MAX_MSG_RETRY 2
+#if GLIB_MAJOR_VERSION >= 2 && GLIB_MINOR_VERSION >= 12
+#	define atoll(a) g_ascii_strtoll(a, NULL, 0)
+#endif
 
-#include <json-glib/json-glib.h>
+#define FB_MAX_MSG_RETRY 2
 
 typedef struct _FacebookAccount FacebookAccount;
 typedef struct _FacebookBuddy FacebookBuddy;
@@ -88,11 +90,13 @@ struct _FacebookAccount {
 	GHashTable *cookie_table;
 	gchar *post_form_id;
 	gint64 uid;
-	guint buddy_list_timer;
+	guint buddy_list_timer; 		/* handled by fb_blist */
+	GHashTable *friend_lists;		/* handled by fb_friendlist */
+	GHashTable *friend_lists_reverse;	/* handled by fb_friendlist */
 	guint friend_request_timer;
 	gchar *channel_number;
 	guint message_fetch_sequence;
-	gint64 last_message_time;
+	gint64 last_message_time;		/* handled by fb_conversation */
 	GSList *resending_messages;
 	GHashTable *auth_buddies;
 	GHashTable *hostname_ip_cache;
@@ -116,10 +120,4 @@ struct _FacebookBuddy {
 	gchar *thumb_url;
 };
 
-/* TODO: move util functions into a utils file */
-gchar *fb_strdup_withhtml(const gchar *src);
-gchar *fb_convert_unicode(const gchar *input);
-JsonParser *fb_get_parser(const gchar *data, gsize data_len);
-gint64 fb_time_kludge(int initial_time);
-
 #endif /* LIBFACEBOOK_H */
============================================================
--- libpurple/protocols/facebook/pidgin-facebookchat.rc	1254a3c5068a1788a466179ff715a73c2a695c6b
+++ libpurple/protocols/facebook/pidgin-facebookchat.rc	6fa6c5397a3dfd95a4447ef1a17390ba0e8246e1
@@ -1,7 +1,7 @@ 1 VERSIONINFO
 
 1 VERSIONINFO
-FILEVERSION 1,53,0,0
-PRODUCTVERSION 1,53,0,0
+FILEVERSION 1,54,0,0
+PRODUCTVERSION 1,54,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.53\0"
-		VALUE "ProductVersion", "1.53\0"
+		VALUE "FileVersion", "1.54\0"
+		VALUE "ProductVersion", "1.54\0"
 		VALUE "InternalName", "pidgin-facebookchat\0"
 		VALUE "OriginalFilename", "libfacebook.dll\0"
 		VALUE "Comments", "http://pidgin-facebookchat.googlecode.com/\0"


More information about the Commits mailing list