Index: protocols/account.c
===================================================================
--- protocols/account.c	(revision 9f03c472fef309878ff2f3bc720d51e6d03077f1)
+++ protocols/account.c	(revision 537d9b9f9eb0d3390c43f0ba861d3f28da57f14b)
@@ -27,8 +27,4 @@
 #include "bitlbee.h"
 #include "account.h"
-
-static const char* account_protocols_local[] = {
-	"gg", "whatsapp", NULL
-};
 
 static char *set_eval_nick_source(set_t *set, char *value);
@@ -461,14 +457,2 @@
 	return a->auto_reconnect_delay;
 }
-
-int protocol_account_islocal(const char* protocol)
-{
-	const char** p = account_protocols_local;
-
-	do {
-		if (strcmp(*p, protocol) == 0) {
-			return 1;
-		}
-	} while (*(++p));
-	return 0;
-}
Index: protocols/account.h
===================================================================
--- protocols/account.h	(revision 9f03c472fef309878ff2f3bc720d51e6d03077f1)
+++ protocols/account.h	(revision 537d9b9f9eb0d3390c43f0ba861d3f28da57f14b)
@@ -58,6 +58,4 @@
 int account_reconnect_delay(account_t *a);
 
-int protocol_account_islocal(const char* protocol);
-
 typedef enum {
 	ACC_SET_OFFLINE_ONLY = 0x02,    /* Allow changes only if the acct is offline. */
@@ -70,5 +68,5 @@
 	ACC_FLAG_STATUS_MESSAGE = 0x02, /* Supports status messages (without being away). */
 	ACC_FLAG_HANDLE_DOMAINS = 0x04, /* Contact handles need a domain portion. */
-	ACC_FLAG_LOCAL = 0x08,          /* Contact list is local. */
+	ACC_FLAG_LOCAL_CONTACTS = 0x08, /* Contact list is local. */
 	ACC_FLAG_LOCKED = 0x10,         /* Account is locked (cannot be deleted, certain settings can't changed) */
 } account_flag_t;
Index: protocols/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision 9f03c472fef309878ff2f3bc720d51e6d03077f1)
+++ protocols/nogaim.c	(revision 537d9b9f9eb0d3390c43f0ba861d3f28da57f14b)
@@ -204,4 +204,5 @@
 	extern void twitter_initmodule();
 	extern void purple_initmodule();
+	extern void rpc_initmodule();
 
 #ifdef WITH_MSN
@@ -227,4 +228,8 @@
 #ifdef WITH_PURPLE
 	purple_initmodule();
+#endif
+
+#ifdef WITH_RPC
+	rpc_initmodule();
 #endif
 
@@ -392,10 +397,12 @@
 	}
 
-	if (ic->acc->flags & ACC_FLAG_LOCAL) {
+	if ((ic->acc->flags & ACC_FLAG_LOCAL_CONTACTS) &&
+	    !(ic->flags & OPT_LOCAL_CONTACTS_SENT) &&
+	    ic->acc->prpl->add_buddy) {
 		GHashTableIter nicks;
-		gpointer k, v;
+		gpointer handle;
 		g_hash_table_iter_init(&nicks, ic->acc->nicks);
-		while (g_hash_table_iter_next(&nicks, &k, &v)) {
-			ic->acc->prpl->add_buddy(ic, (char *) k, NULL);
+		while (g_hash_table_iter_next(&nicks, &handle, NULL)) {
+			ic->acc->prpl->add_buddy(ic, (char *) handle, NULL);
 		}
 	}
@@ -492,4 +499,13 @@
 	query_del_by_conn((irc_t *) ic->bee->ui_data, ic);
 
+	/* Throw away groupchats owned by this account. Historically this was only
+	   ever done by IM modules which is a bug. But it gives them opportunity
+	   to clean up protocol-specific bits as well so keep it that way, just
+	   do another cleanup here as a fallback. Don't want to leave any dangling
+	   pointers! */
+	while (ic->groupchats) {
+		imcb_chat_free(ic->groupchats->data);
+	}
+
 	if (!a) {
 		/* Uhm... This is very sick. */
@@ -590,4 +606,33 @@
 }
 
+/* Returns the local contacts for an IM account (based on assigned nicks).
+   Linked list should be freed, the strings themselves not! So look at it
+   like a GSList<const char*> I guess? Empty list means NULL retval (as
+   always with GSList). */
+GSList *imcb_get_local_contacts(struct im_connection *ic)
+{
+ 	GHashTableIter nicks;
+	GSList *ret = NULL;
+	
+	if (!(ic->acc->flags & ACC_FLAG_LOCAL_CONTACTS)) {
+		/* Only allow protocols that indicate local contact list
+		   support to use this function. */
+		return ret;
+	}
+	
+	g_hash_table_iter_init(&nicks, ic->acc->nicks);
+	gpointer handle;
+	while (g_hash_table_iter_next(&nicks, &handle, NULL)) {
+		ret = g_slist_prepend(ret, (char *) handle);
+	}
+	
+	/* If the protocol asked for the list, assume we won't have to send it
+	   anymore in imcb_connected(). */
+	ic->flags |= OPT_LOCAL_CONTACTS_SENT;
+	
+	return ret;
+}
+
+
 struct imcb_ask_cb_data {
 	struct im_connection *ic;
@@ -647,5 +692,7 @@
 	struct imcb_ask_cb_data *cbd = data;
 
-	cbd->ic->acc->prpl->add_buddy(cbd->ic, cbd->handle, NULL);
+	if (cbd->ic->acc->prpl->add_buddy) {
+		cbd->ic->acc->prpl->add_buddy(cbd->ic, cbd->handle, NULL);
+	}
 
 	imcb_ask_cb_free(data);
Index: protocols/nogaim.h
===================================================================
--- protocols/nogaim.h	(revision 9f03c472fef309878ff2f3bc720d51e6d03077f1)
+++ protocols/nogaim.h	(revision 537d9b9f9eb0d3390c43f0ba861d3f28da57f14b)
@@ -57,5 +57,10 @@
 
 /* Sharing flags between all kinds of things. I just hope I won't hit any
-   limits before 32-bit machines become extinct. ;-) */
+   limits before 32-bit machines become extinct. ;-)
+   
+   Argh! This needs to be renamed to be more clear which field they're used
+   for. As said it's currently mixed which is nonsense. Some are for the
+   im_connection flags field, some for imcb_buddy_status(), some for typing
+   notifications, and who knows what else... */
 #define OPT_LOGGED_IN   0x00000001
 #define OPT_LOGGING_OUT 0x00000002
@@ -70,4 +75,5 @@
 #define OPT_PONGS       0x00010000 /* Service sends us keep-alives */
 #define OPT_PONGED      0x00020000 /* Received a keep-alive during last interval */
+#define OPT_LOCAL_CONTACTS_SENT 0x00040000 /* Protocol already requested local contact list, so don't send it after finishing login. */
 #define OPT_SELFMESSAGE 0x00080000 /* A message sent by self from another location */
 
@@ -371,4 +377,5 @@
 G_MODULE_EXPORT void imcb_buddy_nick_change(struct im_connection *ic, const char *handle, const char *nick);
 G_MODULE_EXPORT void imcb_buddy_action_response(bee_user_t *bu, const char *action, char * const args[], void *data);
+G_MODULE_EXPORT GSList *imcb_get_local_contacts(struct im_connection *ic);
 
 G_MODULE_EXPORT void imcb_buddy_typing(struct im_connection *ic, const char *handle, guint32 flags);
Index: protocols/purple/purple.c
===================================================================
--- protocols/purple/purple.c	(revision 9f03c472fef309878ff2f3bc720d51e6d03077f1)
+++ protocols/purple/purple.c	(revision 537d9b9f9eb0d3390c43f0ba861d3f28da57f14b)
@@ -298,4 +298,9 @@
 	}
 	purple_accounts_remove(pa);
+	
+	/* Last, some protocols want their contact lists locally. */
+	if (strcmp(acc->prpl->name, "whatsapp") == 0 || strcmp(acc->prpl->name, "gg") == 0) {
+		acc->flags |= ACC_FLAG_LOCAL_CONTACTS;
+	}
 }
 
Index: protocols/rpc/Makefile
===================================================================
--- protocols/rpc/Makefile	(revision 537d9b9f9eb0d3390c43f0ba861d3f28da57f14b)
+++ protocols/rpc/Makefile	(revision 537d9b9f9eb0d3390c43f0ba861d3f28da57f14b)
@@ -0,0 +1,46 @@
+###########################
+## Makefile for BitlBee  ##
+##                       ##
+## Copyright 2002 Lintux ##
+###########################
+
+### DEFINITIONS
+
+-include ../../Makefile.settings
+ifdef _SRCDIR_
+_SRCDIR_ := $(_SRCDIR_)protocols/rpc/
+endif
+
+# [SH] Program variables
+objects = rpc.o
+
+LFLAGS += -r
+
+# [SH] Phony targets
+all: rpc_mod.o
+check: all
+lcov: check
+gcov: 
+	gcov *.c
+	
+.PHONY: all clean distclean
+
+clean:
+	rm -f *.o core
+
+distclean: clean
+	rm -rf .depend
+
+### MAIN PROGRAM
+
+$(objects): ../../Makefile.settings Makefile
+
+$(objects): %.o: $(_SRCDIR_)%.c
+	@echo '*' Compiling $<
+	@$(CC) -c $(CFLAGS) $(CFLAGS_BITLBEE) $< -o $@
+
+rpc_mod.o: $(objects)
+	@echo '*' Linking rpc_mod.o
+	@$(LD) $(LFLAGS) $(objects) -o rpc_mod.o
+	
+-include .depend/*.d
Index: protocols/rpc/rpc.c
===================================================================
--- protocols/rpc/rpc.c	(revision 537d9b9f9eb0d3390c43f0ba861d3f28da57f14b)
+++ protocols/rpc/rpc.c	(revision 537d9b9f9eb0d3390c43f0ba861d3f28da57f14b)
@@ -0,0 +1,1027 @@
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "bitlbee.h"
+#include "bee.h"
+#include "nogaim.h"
+#include "parson.h"
+
+#define JSON_O_FOREACH(o, k, v) \
+    const char *k; const JSON_Value *v; int __i; \
+    for (__i = 0; json_object_get_tuple(o, __i, &k, &v); __i++)
+
+static int next_rpc_id = 1;
+
+struct rpc_plugin {
+	/* Socket address of the RPC server. */
+	struct sockaddr *addr;
+	socklen_t addrlen;
+	/* Full copy of the "settings" section of the init message. This info
+	 * can only be applied later on, when an account is created (but well
+	 * before logging in). */
+	JSON_Value *settings;
+	/* Supported away states returned by the away_states() function. Since
+	 * RPC servers can't do return values, just get this info at init time.
+	 * This means the list of possible away states is static from init time
+	 * which hopefully won't be a problem. If NULL, the away_states function
+	 * will not be set on this protocol. */
+	GList *away_states;
+	/* Account flags. See account.h. Plugin lib should provide constants. */
+	int account_flags;
+};
+
+struct rpc_connection {
+	int fd;
+	char *buf;
+	int buflen;
+	GHashTable *groupchats;
+};
+
+struct rpc_groupchat {
+	int id;
+	struct groupchat *gc;
+};
+
+static JSON_Value *jsonrpc_error(int code, const char *msg) {
+	JSON_Value *ret = json_value_init_object();
+	json_object_set_null(json_object(ret), "result");
+	if (TRUE) {
+		/* Format from http://jsonrpc.org/historical/json-rpc-1-1-alt.html.
+		 * Not sure whether to use it. */
+		JSON_Value *error = json_value_init_object();
+		json_object_set_integer(json_object(error), "code", code);
+		json_object_set_string(json_object(error), "message", msg);
+		json_object_set_value(json_object(ret), "error", error);
+	} else {
+		json_object_set_string(json_object(ret), "error", msg);
+	}
+	
+	return ret;
+}
+
+// Might have liked to have this one in the library for optional values/etc.
+static void json_array_append_string_or_null(JSON_Array *array, const char *string) {
+	if (string)
+		json_array_append_string(array, string);
+	else
+		json_array_append_null(array);
+}
+
+static void json_object_set_string_or_null(JSON_Object *object, const char *key, const char *string) {
+	if (string)
+		json_object_set_string(object, key, string);
+	else
+		json_object_set_null(object, key);
+}
+
+static JSON_Value *rpc_out_new(const char *method, JSON_Array **params_) {
+	JSON_Value *rpc = json_value_init_object();
+	json_object_set_string(json_object(rpc), "method", method);
+	json_object_set_integer(json_object(rpc), "id", next_rpc_id++);
+
+	JSON_Value *params = json_value_init_array();
+	json_object_set_value(json_object(rpc), "params", params);
+
+	if (params_)
+		*params_ = json_array(params);
+
+	return rpc;
+}
+
+#define RPC_OUT_INIT(method) \
+	JSON_Array *params; \
+	JSON_Value *rpc = rpc_out_new(method, &params);
+
+/** Sends an RPC object. Takes ownership (i.e. frees it when done). */
+static gboolean rpc_send(struct im_connection *ic, JSON_Value *rpc) {
+	struct rpc_connection *rd = ic->proto_data;
+	char *buf = json_serialize_to_string(rpc);
+	int len = strlen(buf);
+	int st;
+
+	buf = g_realloc(buf, len + 3);
+	strcpy(buf + len, "\r\n");
+	len += 2;
+
+	st = write(rd->fd, buf, len);
+	g_free(buf);
+	json_value_free(rpc);
+
+	if (st != len) {
+		if (!(ic->flags & OPT_LOGGING_OUT))
+			imcb_log(ic, "Write error");
+		imc_logout(ic, TRUE);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static JSON_Value *rpc_ser_settings(set_t **set);
+
+static JSON_Value *rpc_ser_account(account_t *acc) {
+	JSON_Value *v = json_value_init_object();
+	JSON_Object *o = json_object(v);
+	json_object_set_string(o, "user", acc->user);
+	json_object_set_string(o, "pass", acc->pass);
+	if (acc->server)
+		json_object_set_string(o, "server", acc->server);
+	json_object_set_value(o, "settings", rpc_ser_settings(&acc->set));
+	return v;
+}
+
+static JSON_Value *rpc_ser_bee_user(bee_user_t *bu) {
+	JSON_Value *v = json_value_init_object();
+	JSON_Object *o = json_object(v);
+	json_object_set_string_or_null(o, "handle", bu->handle);
+	json_object_set_string_or_null(o, "fullname", bu->fullname);
+	json_object_set_string_or_null(o, "nick", bu->nick);
+	json_object_set_string_or_null(o, "group", bu->group ? bu->group->name : NULL);
+	json_object_set_integer(o, "flags", bu->flags);
+	json_object_set_string_or_null(o, "status", bu->status);
+	json_object_set_string_or_null(o, "status_msg", bu->status_msg);
+	json_object_set_integer(o, "login_time", bu->login_time);
+	json_object_set_integer(o, "idle_time", bu->idle_time);
+	return v;
+}
+
+static char *rpc_set_evaluator(set_t *set, char *value);
+
+static void rpc_init(account_t *acc) {
+	struct rpc_plugin *pd = acc->prpl->data;
+
+	JSON_O_FOREACH(json_object(pd->settings), name, value) {
+		JSON_Object *o = json_object(value);
+		char *defs = NULL;
+		JSON_Value *defv = json_object_get_value(o, "default");
+		if (json_type(defv) == JSONString)
+			defs = g_strdup(json_string(defv));
+		else if(json_type(defv) == JSONInteger)
+			defs = g_strdup_printf("%lld", (long long) json_integer(defv));
+		else if(json_type(defv) == JSONBoolean)
+			defs = g_strdup(json_boolean(defv) ? "true" : "false");
+		set_t *set = set_add(&acc->set, name, defs, NULL, acc);
+		g_free(defs);
+		set->flags |= json_object_get_integer(o, "flags");
+		set->eval = rpc_set_evaluator;
+		set->eval_data = o;
+		/* eval_list turns out to be a memory leak so don't implement it
+		 * for now.
+		 * Allowing a plugin to define its own evaluator is not really
+		 * possible without having BitlBee block on it responding which
+		 * I don't want to do.
+		 * Should a module want to override a user's setting, it can
+		 * use set_setstr(). */
+	}
+
+	acc->flags |= pd->account_flags;
+}
+
+set_eval rpc_type_set_eval(const set_t *set) {
+	JSON_Object *o = set->eval_data;
+	const char *type = json_object_get_string(o, "type");
+
+	set_eval type_eval = NULL;
+	if (type == NULL) {
+		/* Try to do something sane for settings that aren't ours. */
+		if (set->eval == set_eval_int || set->eval == set_eval_bool) {
+			type_eval = set->eval;
+		}
+	} else if (g_str_has_prefix(type, "int")) {
+		type_eval = set_eval_int;
+	} else if (g_str_has_prefix(type, "bool")) {
+		type_eval = set_eval_bool;
+	}
+
+	return type_eval;
+}
+
+static JSON_Value *set_make_json_value(set_eval type, const char *value) {
+	JSON_Value *ret;
+
+	if (value == NULL) {
+		ret = json_value_init_null();
+	} else if (type == set_eval_int) {
+		long long num = 0;
+		/* Evaluator already did validation so ignore retval. */
+		sscanf(value, "%lld", &num);
+		ret = json_value_init_integer(num);
+	} else if (type == set_eval_bool) {
+		ret = json_value_init_boolean(bool2int(value));
+	} else {
+		ret = json_value_init_string(value);
+	}
+	return ret;
+}
+
+static char *rpc_set_evaluator(set_t *set, char *value) {
+	set_eval type_eval = rpc_type_set_eval(set);
+	if (type_eval) {
+		char *new = type_eval(set, value);
+		if (new == SET_INVALID) {
+			return SET_INVALID;
+		}
+	}
+
+	account_t *acc = set->data;
+	if (acc->ic) {
+		/* But do send RPCs to the plugin for each changed setting so
+		 * it always has up-to-date values. */
+		RPC_OUT_INIT("set_set");
+		json_array_append_string(params, set->key);
+		json_array_append_value(params, set_make_json_value(rpc_type_set_eval(set), value));
+		rpc_send(acc->ic, rpc);
+	}
+
+	return value;
+}
+
+static JSON_Value *rpc_ser_settings(set_t **set) {
+	const set_t *s;
+	JSON_Value *ret = json_value_init_object();
+       
+	for (s = *set; s; s = s->next) {
+		JSON_Value *v = set_make_json_value(rpc_type_set_eval(s), set_value(s));
+		json_object_set_value(json_object(ret), s->key, v);
+	}
+
+	return ret;
+}
+
+static gboolean rpc_login_cb(gpointer data, gint fd, b_input_condition cond);
+static gboolean rpc_in_event(gpointer data, gint fd, b_input_condition cond);
+static JSON_Value *rpc_init_isup();
+
+static void rpc_login(account_t *acc) {
+	struct im_connection *ic = imcb_new(acc);
+	struct rpc_connection *rd = ic->proto_data = g_new0(struct rpc_connection, 1);
+	struct rpc_plugin *pd = acc->prpl->data;
+	imcb_log(ic, "Logging in via RPC server");
+	rd->fd = socket(pd->addr->sa_family, SOCK_STREAM, 0);
+	sock_make_nonblocking(rd->fd);
+	if (connect(rd->fd, pd->addr, pd->addrlen) == -1) {
+		closesocket(rd->fd);
+		imcb_error(ic, "RPC server unreachable");
+		imc_logout(ic, TRUE);
+		return;
+	}
+	ic->inpa = b_input_add(rd->fd, B_EV_IO_WRITE, rpc_login_cb, ic);
+	rd->groupchats = g_hash_table_new(g_int_hash, g_int_equal);
+}
+
+static gboolean rpc_login_cb(gpointer data, gint fd, b_input_condition cond) {
+	struct im_connection *ic = data;
+	struct rpc_connection *rd = ic->proto_data;
+
+	/* Need to repeat this since each IM connection means an actual new
+	 * RPC session. */
+	JSON_Value *init = rpc_init_isup();
+	if (!rpc_send(ic, init))
+		return FALSE;
+
+	RPC_OUT_INIT("login");
+	json_array_append_value(params, rpc_ser_account(ic->acc));
+	if (!rpc_send(ic, rpc))
+		return FALSE;
+
+	ic->inpa = b_input_add(rd->fd, B_EV_IO_READ, rpc_in_event, ic);
+
+	return FALSE;
+}
+
+static void rpc_keepalive(struct im_connection *ic) {
+	RPC_OUT_INIT("keepalive");
+	rpc_send(ic, rpc);
+}
+
+static void rpc_groupchat_free(struct groupchat *gc);
+
+static void rpc_logout(struct im_connection *ic) {
+	RPC_OUT_INIT("logout");
+	if (!rpc_send(ic, rpc))
+		return;
+
+	struct rpc_connection *rd = ic->proto_data;
+
+	GHashTableIter iter;
+	struct rpc_groupchat *rc;
+	g_hash_table_iter_init(&iter, rd->groupchats);
+	while (g_hash_table_iter_next(&iter, NULL, (gpointer*)&rc)) {
+		rpc_groupchat_free(rc->gc);
+	}
+
+	b_event_remove(ic->inpa);
+	closesocket(rd->fd);
+	g_free(rd->buf);
+	g_hash_table_destroy(rd->groupchats);
+	g_free(rd);
+}
+
+static int rpc_buddy_msg(struct im_connection *ic, char *to, char *message, int flags) {
+	RPC_OUT_INIT("buddy_msg");
+	json_array_append_string(params, to);
+	json_array_append_string(params, message);
+	json_array_append_integer(params, flags);
+	return rpc_send(ic, rpc);
+}
+
+static void rpc_set_away(struct im_connection *ic, char *state, char *message) {
+	RPC_OUT_INIT("set_away");
+	json_array_append_string_or_null(params, state);
+	json_array_append_string_or_null(params, message);
+	rpc_send(ic, rpc);
+}
+
+static int rpc_send_typing(struct im_connection *ic, char *who, int flags) {
+	RPC_OUT_INIT("send_typing");
+	json_array_append_string(params, who);
+	json_array_append_integer(params, flags);
+	return rpc_send(ic, rpc);
+}
+
+static void rpc_add_buddy(struct im_connection *ic, char *name, char *group) {
+	RPC_OUT_INIT("add_buddy");
+	json_array_append_string(params, name);
+	json_array_append_string_or_null(params, group);
+	rpc_send(ic, rpc);
+}
+
+static void rpc_remove_buddy(struct im_connection *ic, char *name, char *group) {
+	RPC_OUT_INIT("remove_buddy");
+	json_array_append_string(params, name);
+	json_array_append_string_or_null(params, group);
+	rpc_send(ic, rpc);
+}
+
+static void rpc_add_permit(struct im_connection *ic, char *who) {
+	RPC_OUT_INIT("add_permit");
+	json_array_append_string(params, who);
+	rpc_send(ic, rpc);
+}
+
+static void rpc_add_deny(struct im_connection *ic, char *who) {
+	RPC_OUT_INIT("add_deny");
+	json_array_append_string(params, who);
+	rpc_send(ic, rpc);
+}
+
+static void rpc_rem_permit(struct im_connection *ic, char *who) {
+	RPC_OUT_INIT("rem_permit");
+	json_array_append_string(params, who);
+	rpc_send(ic, rpc);
+}
+
+static void rpc_rem_deny(struct im_connection *ic, char *who) {
+	RPC_OUT_INIT("rem_deny");
+	json_array_append_string(params, who);
+	rpc_send(ic, rpc);
+}
+
+static void rpc_get_info(struct im_connection *ic, char *who) {
+	RPC_OUT_INIT("get_info");
+	json_array_append_string(params, who);
+	rpc_send(ic, rpc);
+}
+
+static void rpc_chat_invite(struct groupchat *gc, char *who, char *message) {
+	RPC_OUT_INIT("chat_invite");
+	struct rpc_groupchat *rc = gc->data;
+	json_array_append_integer(params, rc->id);
+	json_array_append_string(params, who);
+	json_array_append_string_or_null(params, message);
+	rpc_send(gc->ic, rpc);
+}
+
+static void rpc_chat_kick(struct groupchat *gc, char *who, const char *message) {
+	RPC_OUT_INIT("chat_kick");
+	struct rpc_groupchat *rc = gc->data;
+	json_array_append_integer(params, rc->id);
+	json_array_append_string(params, who);
+	json_array_append_string_or_null(params, message);
+	rpc_send(gc->ic, rpc);
+}
+
+static void rpc_chat_leave(struct groupchat *gc) {
+	RPC_OUT_INIT("chat_leave");
+	struct rpc_groupchat *rc = gc->data;
+	json_array_append_integer(params, rc->id);
+	rpc_send(gc->ic, rpc);
+
+	struct rpc_connection *rd = gc->ic->proto_data;
+	g_hash_table_remove(rd->groupchats, &rc->id);
+	rpc_groupchat_free(gc);
+}
+
+static void rpc_chat_msg(struct groupchat *gc, char *msg, int flags) {
+	RPC_OUT_INIT("chat_msg");	
+	struct rpc_groupchat *rc = gc->data;
+	json_array_append_integer(params, rc->id);
+	json_array_append_string(params, msg);
+	json_array_append_integer(params, flags);
+	rpc_send(gc->ic, rpc);
+}
+
+static struct rpc_groupchat *rpc_groupchat_new(struct im_connection *ic, const char *handle) {
+	struct rpc_connection *rd = ic->proto_data;
+	struct groupchat *gc = imcb_chat_new(ic, handle);
+	struct rpc_groupchat *rc = gc->data = g_new0(struct rpc_groupchat, 1);
+	rc->id = next_rpc_id++;
+	rc->gc = gc;
+	g_hash_table_insert(rd->groupchats, &rc->id, rc);
+	return rc;  // TODO: RETVAL HERE AND BELOW
+}
+
+static struct rpc_groupchat *rpc_groupchat_by_id(struct im_connection *ic, int id) {
+	struct rpc_connection *rd = ic->proto_data;
+	struct rpc_groupchat *rc = g_hash_table_lookup(rd->groupchats, &id);
+
+	return rc;
+}
+
+/* When calling this, also make sure the element gets removes from
+   rpc_connection->groupchat. This function doesn't do that because it gets
+   called from within an iterator. */
+static void rpc_groupchat_free(struct groupchat *gc) {
+	g_free(gc->data);
+	imcb_chat_free(gc);
+}
+
+/* Boilerplate for all incoming RPCs (where groupchat is identified using
+ * numeric ID). */
+#define SET_GROUPCHAT(rc) \
+	do { \
+		rc = rpc_groupchat_by_id(ic, json_array_get_integer(params, 0)); \
+		if (rc == NULL) \
+			return jsonrpc_error(ENOENT, "No groupchat with that id."); \
+	} while (0)
+
+static struct groupchat *rpc_chat_with(struct im_connection *ic, char *who) {
+	RPC_OUT_INIT("chat_with");
+	struct rpc_groupchat *rc = rpc_groupchat_new(ic, who);
+	json_array_append_integer(params, rc->id);
+	json_array_append_string(params, who);
+	rpc_send(ic, rpc);
+
+	return rc->gc; 
+}
+
+static struct groupchat *rpc_chat_join(struct im_connection *ic, const char *room, const char *nick,
+                                       const char *password, set_t **sets) {
+	RPC_OUT_INIT("chat_join");
+	struct rpc_groupchat *rc = rpc_groupchat_new(ic, room);
+	json_array_append_integer(params, rc->id);
+	json_array_append_string(params, room);
+	json_array_append_string_or_null(params, nick);
+	json_array_append_string_or_null(params, password);
+	json_array_append_value(params, rpc_ser_settings(sets));
+	rpc_send(ic, rpc);
+
+	return rc->gc;
+}
+
+static void rpc_chat_topic(struct groupchat *gc, char *topic) {
+	RPC_OUT_INIT("chat_topic");
+	struct rpc_groupchat *rc = gc->data;
+	json_array_append_integer(params, rc->id);
+	json_array_append_string(params, topic);
+	rpc_send(gc->ic, rpc);
+}
+
+static GList *rpc_away_states(struct im_connection *ic) {
+	struct rpc_plugin *pd = ic->acc->prpl->data;
+	return pd->away_states;
+}
+
+static JSON_Value *rpc_cmd_in(struct im_connection *ic, const char *cmd, JSON_Array *params);
+
+static gboolean rpc_in(struct im_connection *ic, JSON_Object *rpc) {
+	const char *cmd = json_object_get_string(rpc, "method");
+	JSON_Value *id = json_object_get_value(rpc, "id");
+	JSON_Value *error = json_object_get_value(rpc, "error");
+	JSON_Array *params = json_object_get_array(rpc, "params");
+
+	/* Removed checks for result/error/etc. as it's all too free-form and
+	 * at least for now this code is not going to care about retvals as
+	 * they come in late anyway. */
+	if (!id) {
+		imcb_log(ic, "Received invalid JSON-RPC object.");
+		imc_logout(ic, TRUE);
+		return FALSE;
+	}
+
+	if (cmd) {
+		JSON_Value *resp = rpc_cmd_in(ic, cmd, params);
+		if (!resp) {
+			resp = json_value_init_object();
+			json_object_set_boolean(json_object(resp), "result", TRUE);
+		}
+		json_object_set_value(json_object(resp), "id", json_value_deep_copy(id));
+		return rpc_send(ic, resp);
+	} else if (error && json_type(error) != JSONNull) {
+		char *error_str = json_serialize_to_string(error);
+		/* Maybe sanitise/truncate? Though really that should be done at
+		 * a different layer. */
+		imcb_error(ic, "RPC Error: %s", error_str);
+		g_free(error_str);
+	}
+
+	return TRUE;
+}
+
+static gboolean rpc_in_event(gpointer data, gint fd, b_input_condition cond) {
+	struct im_connection *ic = data;
+	struct rpc_connection *rd = ic->proto_data;
+	char buf[2048];
+	int st;
+
+	while ((st = read(rd->fd, buf, sizeof(buf))) > 0) {
+		rd->buf = g_realloc(rd->buf, rd->buflen + st + 1);
+		memcpy(rd->buf + rd->buflen, buf, st);
+		rd->buflen += st;
+	}
+
+	if (st == 0 || (st == -1 && !(sockerr_again() || errno == EAGAIN))) {
+		imcb_log(ic, "Lost RPC connection");
+		imc_logout(ic, TRUE);
+		return FALSE;
+	}
+	rd->buf[rd->buflen] = '\0';
+
+	JSON_Value *parsed;
+	const char *end;
+	while ((parsed = json_parse_first(rd->buf, &end))) {
+		st = rpc_in(ic, json_object(parsed));
+		json_value_free(parsed);
+
+		if (!st)
+			return FALSE;
+
+		if (end == rd->buf + rd->buflen) {
+			g_free(rd->buf);
+			rd->buf = NULL;
+		} else {
+			int newlen = rd->buf + rd->buflen - end;
+			char new[newlen];
+			memcpy(new, end, newlen);
+			rd->buf = g_realloc(rd->buf, newlen + 1);
+			memcpy(rd->buf, new, newlen);
+			rd->buflen = newlen;
+			rd->buf[rd->buflen] = '\0';
+		}
+	}
+
+	return TRUE;
+}
+
+static JSON_Value *rpc_imcb_log(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct im_connection*, const char*, ...) = func_;
+	func(ic, "%s", json_array_get_string(params, 0));
+	return NULL;
+}
+
+static JSON_Value *rpc_imcb_connected(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct im_connection*) = func_;
+	func(ic);
+	return NULL;
+}
+
+static JSON_Value *rpc_imc_logout(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct im_connection*, gboolean) = func_;
+	func(ic, json_array_get_boolean(params, 0));
+	return NULL;
+}
+
+static JSON_Value *rpc_imcb_add_buddy(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct im_connection*, const char*, const char*) = func_;
+	func(ic, json_array_get_string(params, 0), json_array_get_string(params, 1));
+	return NULL;
+}
+
+static JSON_Value *rpc_imcb_buddy_status(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct im_connection*, const char*, int, const char*, const char*) = func_;
+	func(ic, json_array_get_string(params, 0), json_array_get_integer(params, 1),
+	         json_array_get_string(params, 2), json_array_get_string(params, 3));
+	return NULL;
+}
+
+static JSON_Value *rpc_imcb_buddy_times(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct im_connection*, const char*, int, int) = func_;
+	func(ic, json_array_get_string(params, 0), json_array_get_integer(params, 1),
+	         json_array_get_integer(params, 2));
+	return NULL;
+}
+
+static JSON_Value *rpc_imcb_buddy_msg(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct im_connection*, const char*, const char*, int, int) = func_;
+	func(ic, json_array_get_string(params, 0), json_array_get_string(params, 1),
+	         json_array_get_integer(params, 2), json_array_get_integer(params, 3));
+	return NULL;
+}
+
+static JSON_Value *rpc_imcb_buddy_typing(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct im_connection*, const char*, int) = func_;
+	func(ic, (char*) json_array_get_string(params, 0), json_array_get_integer(params, 1));
+	return NULL;
+}
+
+static JSON_Value *rpc_imcb_get_local_contacts(struct im_connection *ic, void *func_, JSON_Array *params) {
+	JSON_Value *resp = json_value_init_object();
+	JSON_Value *arr = json_value_init_array();
+	GSList *contacts = imcb_get_local_contacts(ic);
+	GSList *c;
+	for (c = contacts; c; c = c->next) {
+		json_array_append_string(json_array(arr), (const char*) c->data);
+	}
+	g_slist_free(contacts);
+	json_object_set_value(json_object(resp), "result", arr);
+	return resp;
+}
+
+static JSON_Value *rpc_imcb_chat_new(struct im_connection *ic, void *func_, JSON_Array *params) {
+	struct rpc_groupchat *rc = rpc_groupchat_new(ic, json_array_get_string(params, 0));
+	JSON_Value *resp = json_value_init_object();
+	json_object_set_integer(json_object(resp), "result", rc->id);
+	return resp;
+}
+
+static JSON_Value *rpc_imcb_chat_name_hint(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct groupchat*, const char*) = func_;
+	struct rpc_groupchat *rc;
+	SET_GROUPCHAT(rc);
+	func(rc->gc, json_array_get_string(params, 1));
+	return NULL;
+}
+
+static JSON_Value *rpc_imcb_chat_msg(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct groupchat*, const char*, const char*, guint32, time_t) = func_;
+	struct rpc_groupchat *rc;
+	SET_GROUPCHAT(rc);
+	func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2),
+	     json_array_get_integer(params, 3), json_array_get_integer(params, 4));
+	return NULL;
+}
+
+static JSON_Value *rpc_imcb_chat_log(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct groupchat*, const char*, ...) = func_;
+	struct rpc_groupchat *rc;
+	SET_GROUPCHAT(rc);
+	func(rc->gc, "%s", json_array_get_string(params, 1));
+	return NULL;
+}
+
+static JSON_Value *rpc_imcb_chat_topic(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct groupchat*, const char*, const char*, time_t) = func_;
+	struct rpc_groupchat *rc;
+	SET_GROUPCHAT(rc);
+	func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2),
+	     json_array_get_integer(params, 3));
+	return NULL;
+}
+
+static JSON_Value *rpc_imcb_chat_remove_buddy(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct groupchat*, const char*, const char*) = func_;
+	struct rpc_groupchat *rc;
+	SET_GROUPCHAT(rc);
+	func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2));
+	return NULL;
+}
+
+static JSON_Value *rpc_imcb_chat_invite(struct im_connection *ic, void *func_, JSON_Array *params) {
+	void (*func)(struct groupchat*, const char*, const char*, const char*) = func_;
+	struct rpc_groupchat *rc;
+	SET_GROUPCHAT(rc);
+	func(rc->gc, json_array_get_string(params, 1), json_array_get_string(params, 2),
+	     json_array_get_string(params, 3));
+	return NULL;
+}
+
+static JSON_Value *rpc_set_getstr(struct im_connection *ic, void *func_, JSON_Array *params) {
+	char *rets = set_getstr(&ic->acc->set, json_array_get_string(params, 0));
+	JSON_Value *ret = json_value_init_object();
+	if (rets)
+		json_object_set_string(json_object(ret), "result", rets);
+	else
+		json_object_set_null(json_object(ret), "result");
+	return ret;
+}
+
+static JSON_Value *rpc_set_setstr(struct im_connection *ic, void *func_, JSON_Array *params) {
+	/* Sadly use of const is very poor in BitlBee. :-( */
+	char *newval = g_strdup(json_array_get_string(params, 1));
+	set_setstr(&ic->acc->set, json_array_get_string(params, 0), newval);
+	g_free(newval);
+	return rpc_set_getstr(ic, func_, params);
+}
+
+static JSON_Value *rpc_set_reset(struct im_connection *ic, void *func_, JSON_Array *params) {
+	set_reset(&ic->acc->set, json_array_get_string(params, 0));
+	return rpc_set_getstr(ic, func_, params);
+}
+
+static JSON_Value *rpc_bee_user_by_handle(struct im_connection *ic, void *func_, JSON_Array *params) {
+	bee_user_t *bu = bee_user_by_handle(ic->bee, ic, json_array_get_string(params, 0));
+	JSON_Value *ret = json_value_init_object();
+	if (bu)
+		json_object_set_value(json_object(ret), "result", rpc_ser_bee_user(bu));
+	else
+		json_object_set_value(json_object(ret), "error", jsonrpc_error(ENOENT, "Contact not found"));
+	return ret;
+}
+
+struct rpc_in_method {
+	char *name;
+	void *func;
+	JSON_Value* (* wfunc) (struct im_connection *ic, void *cmd, JSON_Array *params);
+	char args[8];
+};
+
+static const struct rpc_in_method methods[] = {
+	/* All these RPCs are equivalent of BitlBee C functions but with the
+	 * struct im_connection* removed as this is in the object context. */
+	{ "imcb_log", imcb_log, rpc_imcb_log, "s" },
+	{ "imcb_error", imcb_error, rpc_imcb_log, "s" },
+	{ "imcb_connected", imcb_connected, rpc_imcb_connected, "" },
+	{ "imc_logout", imc_logout, rpc_imc_logout, "b" },
+	{ "imcb_add_buddy", imcb_add_buddy, rpc_imcb_add_buddy, "ss" },
+	{ "imcb_remove_buddy", imcb_remove_buddy, rpc_imcb_add_buddy, "ss" },
+	{ "imcb_rename_buddy", imcb_rename_buddy, rpc_imcb_add_buddy, "ss" },
+	{ "imcb_buddy_nick_hint", imcb_buddy_nick_hint, rpc_imcb_add_buddy, "ss" },
+	{ "imcb_buddy_status", imcb_buddy_status, rpc_imcb_buddy_status, "siss" },
+	{ "imcb_buddy_status_msg", imcb_buddy_status_msg, rpc_imcb_add_buddy, "ss" },
+	{ "imcb_buddy_times", imcb_buddy_times, rpc_imcb_buddy_times, "sii" },
+	{ "imcb_buddy_msg", imcb_buddy_msg, rpc_imcb_buddy_msg, "ssii" },
+	{ "imcb_buddy_typing", imcb_buddy_typing, rpc_imcb_buddy_typing, "si" },
+	{ "imcb_get_local_contacts", NULL, rpc_imcb_get_local_contacts, "" },
+	{ "imcb_chat_new", NULL, rpc_imcb_chat_new, "s" },
+	
+	/* RPCs below are equivalent, but with the struct groupchat* replaced
+	 * with the numeric id of the chat. */
+	{ "imcb_chat_name_hint", imcb_chat_name_hint, rpc_imcb_chat_name_hint, "is" },
+	{ "imcb_chat_msg", imcb_chat_msg, rpc_imcb_chat_msg, "issii" },
+	{ "imcb_chat_log", imcb_chat_log, rpc_imcb_chat_log, "is" },
+	{ "imcb_chat_topic", imcb_chat_topic, rpc_imcb_chat_topic, "issi" },
+	{ "imcb_chat_add_buddy", imcb_chat_add_buddy, rpc_imcb_chat_name_hint, "is" },
+	{ "imcb_chat_remove_buddy", imcb_chat_remove_buddy, rpc_imcb_chat_remove_buddy, "iss" },
+	{ "imcb_chat_invite", imcb_chat_invite, rpc_imcb_chat_invite, "isss" },
+
+	/* These are not imcb* functions but should still be exported. */
+	/* Setting functions. Starting with just providing access to account
+	 * settings. See later whether access to chat/chan settings is necessary.
+	 * All of these will return the (new) value of given setting. */
+	{ "set_getstr", NULL, rpc_set_getstr, "s" },
+	{ "set_setstr", NULL, rpc_set_setstr, "ss" },
+	{ "set_reset", NULL, rpc_set_reset, "s" },
+	
+	{ "bee_user_by_handle", NULL, rpc_bee_user_by_handle, "s" },
+
+	{ NULL },
+};
+
+static JSON_Value *rpc_cmd_in(struct im_connection *ic, const char *cmd, JSON_Array *params) {
+	int i;
+
+	for (i = 0; methods[i].name; i++) {
+		if (strcmp(cmd, methods[i].name) == 0) {
+			if (json_array_get_count(params) != strlen(methods[i].args)) {
+				imcb_error(ic, "Invalid argument count to method %s: %d, wanted %zd", cmd, (int) json_array_get_count(params), strlen(methods[i].args));
+				return jsonrpc_error(E2BIG, "Invalid integer of arguments");
+			}
+			int j;
+			for (j = 0; methods[i].args[j]; j++) {
+				JSON_Value_Type type = json_value_get_type(json_array_get_value(params, j));
+				gboolean ok = FALSE;
+				switch (methods[i].args[j]) {
+				case 's':
+					ok = type == JSONString || type == JSONNull;
+					break;
+				case 'i':
+					ok = type == JSONInteger;
+					break;
+				case 'o':
+					ok = type == JSONObject;
+					break;
+				case 'a':
+					ok = type == JSONArray;
+					break;
+				case 'b':
+					ok = type == JSONBoolean;
+					break;
+				}
+				if (!ok) {
+					// This error sucks, but just get your types right!
+					imcb_error(ic, "Invalid argument type, %s parameter %d: %d not %c", cmd, j, type, methods[i].args[j]);
+					return jsonrpc_error(EINVAL, "Invalid argument type");
+				}
+			}
+			return methods[i].wfunc(ic, methods[i].func, params);
+		}
+	}
+	return jsonrpc_error(ENOSYS, "Function not implemented");
+}
+
+#define RPC_ADD_FUNC(func) \
+	ret->func = rpc_ ## func
+#define RPC_ADD_OPT_FUNC(func) \
+	if (g_hash_table_lookup(methods, #func)) \
+		RPC_ADD_FUNC(func)
+
+static JSON_Value *rpc_init_isup() {
+	int i;
+
+	RPC_OUT_INIT("init");
+	JSON_Value *d = json_value_init_object();
+	json_object_set_string(json_object(d), "version_str", BITLBEE_VERSION);
+	json_object_set_integer(json_object(d), "version", BITLBEE_VERSION_CODE);
+	
+	JSON_Value *ml = json_value_init_array();
+	for (i = 0; methods[i].name; i++) {
+		json_array_append_string(json_array(ml), methods[i].name);
+	}
+	json_object_set_value(json_object(d), "method_list", ml);
+	json_array_append_value(params, d);
+
+	return rpc;
+}
+
+gboolean rpc_initmodule_sock(struct sockaddr *address, socklen_t addrlen) {
+	int st, fd, i;
+
+	fd = socket(address->sa_family, SOCK_STREAM, 0);
+	if (fd == -1 || connect(fd, address, addrlen) == -1) {
+		log_message(LOGLVL_WARNING, "Failed to connect to RPC server: %s", strerror(errno));
+		if (fd != -1)
+			closesocket(fd);
+		return FALSE;
+	}
+
+	JSON_Value *rpc = rpc_init_isup();
+	char *s = json_serialize_to_string(rpc);
+	json_value_free(rpc);
+
+	int len = strlen(s);
+	s = g_realloc(s, len + 3);
+	strcpy(s + len, "\r\n");
+	len += 2;
+
+	if ((st = write(fd, s, len)) != len) {
+		log_message(LOGLVL_WARNING, "Error while writing to RPC server: %s", strerror(errno));
+		return FALSE;
+	}
+	g_free(s);
+
+	char *resp = NULL;
+	int buflen = 4096, resplen = 0;
+	JSON_Value *parsed;
+	do {
+		fd_set rfds;
+		struct timeval to;
+
+		FD_ZERO(&rfds);
+		FD_SET(fd, &rfds);
+		to.tv_sec = 1;
+		to.tv_usec = 0;
+		st = select(fd + 1, &rfds, NULL, NULL, &to);
+
+		if (st == 0) {
+			log_message(LOGLVL_WARNING, "Error while reading from RPC server: %s", strerror(errno));
+			closesocket(fd);
+			return FALSE;
+		}
+		
+		if (resplen >= buflen)
+			buflen *= 2;
+		resp = g_realloc(resp, buflen + 1);
+		st = read(fd, resp + resplen, buflen - resplen);
+		if (st == -1) {
+			if (sockerr_again())
+				continue;
+			log_message(LOGLVL_WARNING, "Error while reading from RPC server: %s", strerror(errno));
+			closesocket(fd);
+			return FALSE;
+		}
+		resplen += st;
+		resp[resplen] = '\0';
+	}
+	while (!(parsed = json_parse_string(resp)));
+	closesocket(fd);
+
+	JSON_Object *isup = json_object_get_object(json_object(parsed), "result");
+	if (isup == NULL) {
+		log_message(LOGLVL_WARNING, "Error while parsing RPC server response");
+		return FALSE;
+	}
+
+	struct prpl *ret = g_new0(struct prpl, 1);
+	struct rpc_plugin *proto_data = g_new0(struct rpc_plugin, 1);
+	proto_data->addr = g_memdup(address, addrlen);
+	proto_data->addrlen = addrlen;
+	ret->name = g_strdup(json_object_get_string(isup, "name"));
+	ret->data = proto_data;
+
+	proto_data->account_flags = json_object_get_integer(isup, "account_flags");
+
+	/* Keep a full copy of the settings list, we can only use it when we
+	 * have an account to work on. */
+	JSON_Value *settings = json_object_get_value(isup, "settings");
+	if (json_type(settings) == JSONObject)
+		proto_data->settings = json_value_deep_copy(settings);
+
+	JSON_Array *aways_a = json_object_get_array(isup, "away_state_list");
+	for (i = 0; i < json_array_get_count(aways_a); ++i) {
+		JSON_Value *state = json_array_get_value(aways_a, i);
+		if (json_type(state) == JSONString)
+			proto_data->away_states =
+				g_list_append(proto_data->away_states,
+					      g_strdup(json_string(state)));
+	}
+
+	JSON_Array *methods_a = json_object_get_array(isup, "method_list");
+	GHashTable *methods = g_hash_table_new(g_str_hash, g_str_equal);
+	for (i = 0; i < json_array_get_count(methods_a); i++) {
+		gpointer func = (void*) json_array_get_string(methods_a, i);
+		g_hash_table_replace(methods, func, func);
+	}
+
+	ret->init = rpc_init;
+	RPC_ADD_FUNC(login);
+	RPC_ADD_OPT_FUNC(keepalive);
+	RPC_ADD_FUNC(logout);
+	RPC_ADD_FUNC(buddy_msg);
+	RPC_ADD_OPT_FUNC(set_away);
+	RPC_ADD_OPT_FUNC(send_typing);
+	RPC_ADD_OPT_FUNC(add_buddy);
+	RPC_ADD_OPT_FUNC(remove_buddy);
+	RPC_ADD_OPT_FUNC(add_permit);
+	RPC_ADD_OPT_FUNC(add_deny);
+	RPC_ADD_OPT_FUNC(rem_permit);
+	RPC_ADD_OPT_FUNC(rem_deny);
+	RPC_ADD_OPT_FUNC(get_info);
+	RPC_ADD_OPT_FUNC(chat_invite);
+	RPC_ADD_OPT_FUNC(chat_kick);
+	RPC_ADD_OPT_FUNC(chat_leave);
+	RPC_ADD_OPT_FUNC(chat_msg);
+	RPC_ADD_OPT_FUNC(chat_with);
+	RPC_ADD_OPT_FUNC(chat_join);
+	RPC_ADD_OPT_FUNC(chat_topic);
+	if (proto_data->away_states)
+		ret->away_states = rpc_away_states;
+	
+	g_hash_table_destroy(methods);
+
+	// TODO: Property for a few standard nickcmp implementations.
+	ret->handle_cmp = g_ascii_strcasecmp;
+	
+	register_protocol(ret);
+
+	return TRUE;
+}
+
+/* YA RLY :-/ */
+#ifndef UNIX_PATH_MAX
+struct sockaddr_un sizecheck;
+#define UNIX_PATH_MAX sizeof(sizecheck.sun_path)
+#endif
+
+void rpc_initmodule() {
+	DIR *pdir = opendir(RPCPLUGINDIR);
+	struct dirent *de;
+
+	if (!pdir)
+		return;
+
+	while ((de = readdir(pdir))) {
+		if (de->d_type != DT_SOCK && de->d_type != DT_UNKNOWN)
+			continue;
+
+		char *fn = g_build_filename(RPCPLUGINDIR, de->d_name, NULL);
+		struct sockaddr_un su;
+
+		strncpy(su.sun_path, fn, UNIX_PATH_MAX);
+
+#if 0
+		struct stat fdata;
+		if (stat(fn, &fdata) == -1) {
+			log_message(LOGLVL_WARNING, "Could not stat %s: %s", fn, strerror(errno));
+			g_free(fn);
+			continue;
+		}
+		/* For now just skip anything that is not a Unix domain socket. */
+		if (!S_ISSOCK(fdata.st_mode))
+			continue;
+#endif
+
+		su.sun_path[UNIX_PATH_MAX-1] = '\0';
+		su.sun_family = AF_UNIX;
+		gboolean st = rpc_initmodule_sock((struct sockaddr*) &su, sizeof(su));
+		g_free(fn);
+		if (!st)
+			log_message(LOGLVL_WARNING, "Failed to register protocol %s", fn);
+		/* Idea: Also support textfiles containing a host:port tuple to
+		 * connect to. Not that remote RPC'ing would be a great idea,
+		 * but maybe some jsonrpc libs don't support Unix domain sockets. */
+	}
+	closedir(pdir);
+}
+
Index: protocols/twitter/twitter_lib.c
===================================================================
--- protocols/twitter/twitter_lib.c	(revision 9f03c472fef309878ff2f3bc720d51e6d03077f1)
+++ protocols/twitter/twitter_lib.c	(revision 537d9b9f9eb0d3390c43f0ba861d3f28da57f14b)
@@ -36,5 +36,5 @@
 #include "base64.h"
 #include "twitter_lib.h"
-#include "json_util.h"
+#include "parson.h"
 #include <ctype.h>
 #include <errno.h>
@@ -64,4 +64,8 @@
 	gboolean from_filter;
 };
+
+#define JSON_O_FOREACH(o, k, v) \
+    const char *k; const JSON_Value *v; int __i; \
+    for (__i = 0; json_object_get_tuple(o, __i, &k, &v); __i++)
 
 /**
@@ -166,5 +170,5 @@
 {
 	static char *ret = NULL;
-	json_value *root, *err;
+	JSON_Value *root, *err;
 
 	g_free(ret);
@@ -172,9 +176,10 @@
 
 	if (req->body_size > 0) {
-		root = json_parse(req->reply_body, req->body_size);
-		err = json_o_get(root, "errors");
-		if (err && err->type == json_array && (err = err->u.array.values[0]) &&
-		    err->type == json_object) {
-			const char *msg = json_o_str(err, "message");
+		root = json_parse_string(req->reply_body);
+		err = json_object_get_value(json_object(root), "errors");
+		if (err && json_type(err) == JSONArray &&
+		    (err = json_array_get_value(json_array(err), 0)) &&
+		    json_type(err) == JSONObject) {
+			const char *msg = json_object_get_string(json_object(err), "message");
 			if (msg) {
 				ret = g_strdup_printf("%s (%s)", req->status_string, msg);
@@ -189,10 +194,10 @@
 /* WATCH OUT: This function might or might not destroy your connection.
    Sub-optimal indeed, but just be careful when this returns NULL! */
-static json_value *twitter_parse_response(struct im_connection *ic, struct http_request *req)
+static JSON_Value *twitter_parse_response(struct im_connection *ic, struct http_request *req)
 {
 	gboolean logging_in = !(ic->flags & OPT_LOGGED_IN);
 	gboolean periodic;
 	struct twitter_data *td = ic->proto_data;
-	json_value *ret;
+	JSON_Value *ret;
 	char path[64] = "", *s;
 
@@ -232,5 +237,5 @@
 	}
 
-	if ((ret = json_parse(req->reply_body, req->body_size)) == NULL) {
+	if ((ret = json_parse_string(req->reply_body)) == NULL) {
 		imcb_error(ic, "Could not retrieve %s: %s",
 		           path, "XML parse error");
@@ -289,7 +294,7 @@
  * Fill a list of ids.
  */
-static gboolean twitter_xt_get_friends_id_list(json_value *node, struct twitter_xml_list *txl)
-{
-	json_value *c;
+static gboolean twitter_xt_get_friends_id_list(JSON_Value *node, struct twitter_xml_list *txl)
+{
+	JSON_Array *c;
 	int i;
 
@@ -297,21 +302,18 @@
 	txl->type = TXL_ID;
 
-	c = json_o_get(node, "ids");
-	if (!c || c->type != json_array) {
+	if (!(c = json_object_get_array(json_object(node), "ids"))) {
 		return FALSE;
 	}
 
-	for (i = 0; i < c->u.array.length; i++) {
-		if (c->u.array.values[i]->type != json_integer) {
-			continue;
-		}
+	for (i = 0; i < json_array_get_count(c); i++) {
+		jint id = json_array_get_integer(c, i);
 
 		txl->list = g_slist_prepend(txl->list,
-		                            g_strdup_printf("%" PRIu64, c->u.array.values[i]->u.integer));
-	}
-
-	c = json_o_get(node, "next_cursor");
-	if (c && c->type == json_integer) {
-		txl->next_cursor = c->u.integer;
+		                            g_strdup_printf("%lld", id));
+	}
+
+	JSON_Value *next = json_object_get_value(json_object(node), "next_cursor");
+	if (next && json_type(next) == JSONInteger) {
+		txl->next_cursor = json_integer(next);
 	} else {
 		txl->next_cursor = -1;
@@ -329,5 +331,5 @@
 {
 	struct im_connection *ic;
-	json_value *parsed;
+	JSON_Value *parsed;
 	struct twitter_xml_list *txl;
 	struct twitter_data *td;
@@ -373,5 +375,5 @@
 {
 	struct im_connection *ic = req->data;
-	json_value *parsed;
+	JSON_Value *parsed;
 	struct twitter_xml_list *txl;
 	struct twitter_data *td;
@@ -418,5 +420,5 @@
 {
 	struct im_connection *ic = req->data;
-	json_value *parsed;
+	JSON_Value *parsed;
 	struct twitter_xml_list *txl;
 	struct twitter_data *td;
@@ -444,13 +446,11 @@
 	// Process the retweet ids
 	txl->type = TXL_ID;
-	if (parsed->type == json_array) {
+	if (json_type(parsed) == JSONArray) {
+		JSON_Array *arr = json_array(parsed);
 		unsigned int i;
-		for (i = 0; i < parsed->u.array.length; i++) {
-			json_value *c = parsed->u.array.values[i];
-			if (c->type != json_integer) {
-				continue;
-			}
+		for (i = 0; i < json_array_get_count(arr); i++) {
+			jint id = json_array_get_integer(arr, i);
 			txl->list = g_slist_prepend(txl->list,
-			                            g_strdup_printf("%"PRIu64, c->u.integer));
+			                            g_strdup_printf("%lld", id));
 		}
 	}
@@ -463,5 +463,5 @@
 }
 
-static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl);
+static gboolean twitter_xt_get_users(JSON_Value *node, struct twitter_xml_list *txl);
 static void twitter_http_get_users_lookup(struct http_request *req);
 
@@ -504,5 +504,5 @@
 {
 	struct im_connection *ic = req->data;
-	json_value *parsed;
+	JSON_Value *parsed;
 	struct twitter_xml_list *txl;
 	GSList *l = NULL;
@@ -537,15 +537,15 @@
 }
 
-struct twitter_xml_user *twitter_xt_get_user(const json_value *node)
+struct twitter_xml_user *twitter_xt_get_user(const JSON_Object *node)
 {
 	struct twitter_xml_user *txu;
-	json_value *jv;
+	
+	if (!node)
+		return NULL;
 
 	txu = g_new0(struct twitter_xml_user, 1);
-	txu->name = g_strdup(json_o_str(node, "name"));
-	txu->screen_name = g_strdup(json_o_str(node, "screen_name"));
-
-	jv = json_o_get(node, "id");
-	txu->uid = jv->u.integer;
+	txu->name = g_strdup(json_object_get_string(node, "name"));
+	txu->screen_name = g_strdup(json_object_get_string(node, "screen_name"));
+	txu->uid = json_object_get_integer(node, "id");
 
 	return txu;
@@ -557,5 +557,5 @@
  *  - all <user>s from the <users> element.
  */
-static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl)
+static gboolean twitter_xt_get_users(JSON_Value *node, struct twitter_xml_list *txl)
 {
 	struct twitter_xml_user *txu;
@@ -565,12 +565,16 @@
 	txl->type = TXL_USER;
 
-	if (!node || node->type != json_array) {
+	if (json_type(node) != JSONArray) {
 		return FALSE;
 	}
-
+	
 	// The root <users> node should hold the list of users <user>
 	// Walk over the nodes children.
-	for (i = 0; i < node->u.array.length; i++) {
-		txu = twitter_xt_get_user(node->u.array.values[i]);
+	JSON_Array *arr = json_array(node);
+	for (i = 0; i < json_array_get_count(arr); i++) {
+		JSON_Object *o = json_array_get_object(arr, i);
+		if (!o)
+			continue;
+		txu = twitter_xt_get_user(o);
 		if (txu) {
 			txl->list = g_slist_prepend(txl->list, txu);
@@ -587,5 +591,5 @@
 #endif
 
-static void expand_entities(char **text, const json_value *node, const json_value *extended_node);
+static void expand_entities(char **text, const JSON_Object *node, const JSON_Object *extended_node);
 
 /**
@@ -597,12 +601,12 @@
  *  - the user in a twitter_xml_user struct.
  */
-static struct twitter_xml_status *twitter_xt_get_status(const json_value *node)
+static struct twitter_xml_status *twitter_xt_get_status(const JSON_Object *node)
 {
 	struct twitter_xml_status *txs = {0};
-	const json_value *rt = NULL;
-	const json_value *text_value = NULL;
-	const json_value *extended_node = NULL;
-
-	if (node->type != json_object) {
+	const JSON_Object *rt = NULL;
+	const JSON_Value *text_value = NULL;
+	const JSON_Object *extended_node = NULL;
+
+	if (!node) {
 		return FALSE;
 	}
@@ -610,14 +614,14 @@
 
 	JSON_O_FOREACH(node, k, v) {
-		if (strcmp("text", k) == 0 && v->type == json_string && text_value == NULL) {
+		if (strcmp("text", k) == 0 && json_type(v) == JSONString && text_value == NULL) {
 			text_value = v;
-		} else if (strcmp("full_text", k) == 0 && v->type == json_string) {
+		} else if (strcmp("full_text", k) == 0 && json_type(v) == JSONString) {
 			text_value = v;
-		} else if (strcmp("extended_tweet", k) == 0 && v->type == json_object) {
-			text_value = json_o_get(v, "full_text");
-			extended_node = v;
-		} else if (strcmp("retweeted_status", k) == 0 && v->type == json_object) {
-			rt = v;
-		} else if (strcmp("created_at", k) == 0 && v->type == json_string) {
+		} else if (strcmp("extended_tweet", k) == 0 && json_type(v) == JSONObject) {
+			text_value = json_object_get_value(json_object(v), "full_text");
+			extended_node = json_object(v);
+		} else if (strcmp("retweeted_status", k) == 0 && (rt = json_object(v))) {
+			// Handling below.
+		} else if (strcmp("created_at", k) == 0 && json_type(v) == JSONString) {
 			struct tm parsed;
 
@@ -625,13 +629,13 @@
 			   this field. :-( Also assumes the timezone used
 			   is UTC since C time handling functions suck. */
-			if (strptime(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) {
+			if (strptime(json_string(v), TWITTER_TIME_FORMAT, &parsed) != NULL) {
 				txs->created_at = mktime_utc(&parsed);
 			}
-		} else if (strcmp("user", k) == 0 && v->type == json_object) {
-			txs->user = twitter_xt_get_user(v);
-		} else if (strcmp("id", k) == 0 && v->type == json_integer) {
-			txs->rt_id = txs->id = v->u.integer;
-		} else if (strcmp("in_reply_to_status_id", k) == 0 && v->type == json_integer) {
-			txs->reply_to = v->u.integer;
+		} else if (strcmp("user", k) == 0 && json_type(v) == JSONObject) {
+			txs->user = twitter_xt_get_user(json_object(v));
+		} else if (strcmp("id", k) == 0 && json_type(v) == JSONInteger) {
+			txs->rt_id = txs->id = json_integer(v);
+		} else if (strcmp("in_reply_to_status_id", k) == 0 && json_type(v) == JSONInteger) {
+			txs->reply_to = json_integer(v);
 		}
 	}
@@ -646,6 +650,6 @@
 			txs_free(rtxs);
 		}
-	} else if (text_value && text_value->type == json_string) {
-		txs->text = g_memdup(text_value->u.string.ptr, text_value->u.string.length + 1);
+	} else if (text_value && json_type(text_value) == JSONString) {
+		txs->text = g_strdup(json_string(text_value));
 		strip_html(txs->text);
 		expand_entities(&txs->text, node, extended_node);
@@ -663,9 +667,9 @@
  * Function to fill a twitter_xml_status struct (DM variant).
  */
-static struct twitter_xml_status *twitter_xt_get_dm(const json_value *node)
+static struct twitter_xml_status *twitter_xt_get_dm(const JSON_Object *node)
 {
 	struct twitter_xml_status *txs;
 
-	if (node->type != json_object) {
+	if (!node) {
 		return FALSE;
 	}
@@ -673,8 +677,7 @@
 
 	JSON_O_FOREACH(node, k, v) {
-		if (strcmp("text", k) == 0 && v->type == json_string) {
-			txs->text = g_memdup(v->u.string.ptr, v->u.string.length + 1);
+		if (strcmp("text", k) == 0 && (txs->text = g_strdup(json_string(v)))) {
 			strip_html(txs->text);
-		} else if (strcmp("created_at", k) == 0 && v->type == json_string) {
+		} else if (strcmp("created_at", k) == 0 && json_type(v) == JSONString) {
 			struct tm parsed;
 
@@ -682,11 +685,11 @@
 			   this field. :-( Also assumes the timezone used
 			   is UTC since C time handling functions suck. */
-			if (strptime(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) {
+			if (strptime(json_string(v), TWITTER_TIME_FORMAT, &parsed) != NULL) {
 				txs->created_at = mktime_utc(&parsed);
 			}
-		} else if (strcmp("sender", k) == 0 && v->type == json_object) {
-			txs->user = twitter_xt_get_user(v);
-		} else if (strcmp("id", k) == 0 && v->type == json_integer) {
-			txs->id = v->u.integer;
+		} else if (strcmp("sender", k) == 0 && json_type(v) == JSONObject) {
+			txs->user = twitter_xt_get_user(json_object(v));
+		} else if (strcmp("id", k) == 0 && json_type(v) == JSONInteger) {
+			txs->id = json_integer(v);
 		}
 	}
@@ -702,12 +705,12 @@
 }
 
-static void expand_entities(char **text, const json_value *node, const json_value *extended_node)
-{
-	json_value *entities, *extended_entities, *quoted;
+static void expand_entities(char **text, const JSON_Object *node, const JSON_Object *extended_node)
+{
+	JSON_Object *entities, *extended_entities, *quoted;
 	char *quote_url = NULL, *quote_text = NULL;
 
-	if (!((entities = json_o_get(node, "entities")) && entities->type == json_object))
-		return;
-	if ((quoted = json_o_get(node, "quoted_status")) && quoted->type == json_object) {
+	if (!(entities = json_object_get_object(node, "entities")))
+		return;
+	if ((quoted = json_object_get_object(node, "quoted_status"))) {
 		/* New "retweets with comments" feature. Note that this info
 		 * seems to be included in the streaming API only! Grab the
@@ -723,6 +726,6 @@
 
 	if (extended_node) {
-		extended_entities = json_o_get(extended_node, "entities");
-		if (extended_entities && extended_entities->type == json_object) {
+		extended_entities = json_object_get_object(extended_node, "entities");
+		if (extended_entities) {
 			entities = extended_entities;
 		}
@@ -732,5 +735,5 @@
 		int i;
 
-		if (v->type != json_array) {
+		if (json_type(v) != JSONArray) {
 			continue;
 		}
@@ -739,14 +742,15 @@
 		}
 
-		for (i = 0; i < v->u.array.length; i++) {
+		for (i = 0; i < json_array_get_count(json_array(v)); i++) {
 			const char *format = "%s%s <%s>%s";
-
-			if (v->u.array.values[i]->type != json_object) {
+			JSON_Object *r = json_array_get_object(json_array(v), i);
+
+			if (!r) {
 				continue;
 			}
 
-			const char *kort = json_o_str(v->u.array.values[i], "url");
-			const char *disp = json_o_str(v->u.array.values[i], "display_url");
-			const char *full = json_o_str(v->u.array.values[i], "expanded_url");
+			const char *kort = json_object_get_string(r, "url");
+			const char *disp = json_object_get_string(r, "display_url");
+			const char *full = json_object_get_string(r, "expanded_url");
 			char *pos, *new;
 
@@ -777,5 +781,5 @@
  *  - the next_cursor.
  */
-static gboolean twitter_xt_get_status_list(struct im_connection *ic, const json_value *node,
+static gboolean twitter_xt_get_status_list(struct im_connection *ic, const JSON_Value *node,
                                            struct twitter_xml_list *txl)
 {
@@ -786,5 +790,5 @@
 	txl->type = TXL_STATUS;
 
-	if (node->type != json_array) {
+	if (json_type(node) != JSONArray) {
 		return FALSE;
 	}
@@ -792,6 +796,6 @@
 	// The root <statuses> node should hold the list of statuses <status>
 	// Walk over the nodes children.
-	for (i = 0; i < node->u.array.length; i++) {
-		txs = twitter_xt_get_status(node->u.array.values[i]);
+	for (i = 0; i < json_array_get_count(json_array(node)); i++) {
+		txs = twitter_xt_get_status(json_array_get_object(json_array(node), i));
 		if (!txs) {
 			continue;
@@ -978,5 +982,5 @@
 	
 	/* Check this is not a tweet that should be muted */
-	uid_str = g_strdup_printf("%" PRIu64, status->user->uid);
+	uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, status->user->uid);
 
 	if (g_slist_find_custom(td->mutes_ids, uid_str, (GCompareFunc)strcmp)) {
@@ -1013,5 +1017,5 @@
 }
 
-static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter);
+static gboolean twitter_stream_handle_object(struct im_connection *ic, JSON_Object *o, gboolean from_filter);
 
 static void twitter_http_stream(struct http_request *req)
@@ -1019,5 +1023,5 @@
 	struct im_connection *ic = req->data;
 	struct twitter_data *td;
-	json_value *parsed;
+	JSON_Value *parsed;
 	int len = 0;
 	char c, *nl;
@@ -1060,7 +1064,7 @@
 		req->reply_body[len] = '\0';
 
-		if ((parsed = json_parse(req->reply_body, req->body_size))) {
+		if ((parsed = json_parse_string(req->reply_body))) {
 			from_filter = (req == td->filter_stream);
-			twitter_stream_handle_object(ic, parsed, from_filter);
+			twitter_stream_handle_object(ic, json_object(parsed), from_filter);
 		}
 		json_value_free(parsed);
@@ -1076,12 +1080,12 @@
 }
 
-static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value *o);
+static gboolean twitter_stream_handle_event(struct im_connection *ic, JSON_Object *o);
 static gboolean twitter_stream_handle_status(struct im_connection *ic, struct twitter_xml_status *txs);
 
-static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter)
+static gboolean twitter_stream_handle_object(struct im_connection *ic, JSON_Object *o, gboolean from_filter)
 {
 	struct twitter_data *td = ic->proto_data;
 	struct twitter_xml_status *txs;
-	json_value *c;
+	JSON_Object *c;
 
 	if ((txs = twitter_xt_get_status(o))) {
@@ -1090,5 +1094,5 @@
 		txs_free(txs);
 		return ret;
-	} else if ((c = json_o_get(o, "direct_message")) &&
+	} else if ((c = json_object_get_object(o, "direct_message")) &&
 	           (txs = twitter_xt_get_dm(c))) {
 		if (g_strcasecmp(txs->user->screen_name, td->user) != 0) {
@@ -1098,12 +1102,12 @@
 		txs_free(txs);
 		return TRUE;
-	} else if ((c = json_o_get(o, "event")) && c->type == json_string) {
+	} else if (json_object_get_string(o, "event")) {
 		twitter_stream_handle_event(ic, o);
 		return TRUE;
-	} else if ((c = json_o_get(o, "disconnect")) && c->type == json_object) {
+	} else if ((c = json_object_get_object(o, "disconnect"))) {
 		/* HACK: Because we're inside an event handler, we can't just
 		   disconnect here. Instead, just change the HTTP status string
 		   into a Twitter status string. */
-		char *reason = json_o_strdup(c, "reason");
+		char *reason = g_strdup(json_object_get_string(c, "reason"));
 		if (reason) {
 			g_free(td->stream->status_string);
@@ -1144,15 +1148,14 @@
 }
 
-static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value *o)
+static gboolean twitter_stream_handle_event(struct im_connection *ic, JSON_Object *o)
 {
 	struct twitter_data *td = ic->proto_data;
-	json_value *source = json_o_get(o, "source");
-	json_value *target = json_o_get(o, "target");
-	const char *type = json_o_str(o, "event");
+	JSON_Object *source = json_object_get_object(o, "source");
+	JSON_Object *target = json_object_get_object(o, "target");
+	const char *type = json_object_get_string(o, "event");
 	struct twitter_xml_user *us = NULL;
 	struct twitter_xml_user *ut = NULL;
 
-	if (!type || !source || source->type != json_object
-	    || !target || target->type != json_object) {
+	if (!type || !source || !target) {
 		return FALSE;
 	}
@@ -1168,5 +1171,5 @@
 		char *uid_str;
 		ut = twitter_xt_get_user(target);
-		uid_str = g_strdup_printf("%" PRIu64, ut->uid);
+		uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, ut->uid);
 		if (!(found = g_slist_find_custom(td->mutes_ids, uid_str,
 		                                  (GCompareFunc)strcmp))) {
@@ -1175,5 +1178,5 @@
 		twitter_log(ic, "Muted user %s", ut->screen_name);
 		if (getenv("BITLBEE_DEBUG")) {
-			fprintf(stderr, "New mute: %s %"PRIu64"\n",
+			fprintf(stderr, "New mute: %s %"G_GUINT64_FORMAT"\n",
 			        ut->screen_name, ut->uid);
 		}
@@ -1182,5 +1185,5 @@
 		char *uid_str;
 		ut = twitter_xt_get_user(target);
-		uid_str = g_strdup_printf("%" PRIu64, ut->uid);
+		uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, ut->uid);
 		if ((found = g_slist_find_custom(td->mutes_ids, uid_str,
 		                                (GCompareFunc)strcmp))) {
@@ -1192,5 +1195,5 @@
 		twitter_log(ic, "Unmuted user %s", ut->screen_name);
 		if (getenv("BITLBEE_DEBUG")) {
-			fprintf(stderr, "New unmute: %s %"PRIu64"\n",
+			fprintf(stderr, "New unmute: %s %"G_GUINT64_FORMAT"\n",
 			        ut->screen_name, ut->uid);
 		}
@@ -1283,7 +1286,5 @@
 	struct twitter_filter *tf;
 	GList *users = NULL;
-	json_value *parsed;
-	json_value *id;
-	const char *name;
+	JSON_Value *parsed;
 	GString *fstr;
 	GSList *l;
@@ -1310,13 +1311,14 @@
 	}
 
-	if (parsed->type != json_array) {
+	if (json_type(parsed) != JSONArray) {
 		goto finish;
 	}
 
-	for (i = 0; i < parsed->u.array.length; i++) {
-		id = json_o_get(parsed->u.array.values[i], "id");
-		name = json_o_str(parsed->u.array.values[i], "screen_name");
-
-		if (!name || !id || id->type != json_integer) {
+	for (i = 0; i < json_array_get_count(json_array(parsed)); i++) {
+		JSON_Object *o = json_array_get_object(json_array(parsed), i);
+		jint id = json_object_get_integer(o, "id");
+		const char *name = json_object_get_string(o, "screen_name");
+
+		if (!name || !id) {
 			continue;
 		}
@@ -1326,5 +1328,5 @@
 
 			if (g_strcasecmp(tf->text, name) == 0) {
-				tf->uid = id->u.integer;
+				tf->uid = id;
 				users = g_list_delete_link(users, u);
 				break;
@@ -1571,5 +1573,5 @@
 	struct im_connection *ic = req->data;
 	struct twitter_data *td;
-	json_value *parsed;
+	JSON_Value *parsed;
 	struct twitter_xml_list *txl;
 
@@ -1611,5 +1613,5 @@
 	struct im_connection *ic = req->data;
 	struct twitter_data *td;
-	json_value *parsed;
+	JSON_Value *parsed;
 	struct twitter_xml_list *txl;
 
@@ -1652,5 +1654,6 @@
 	struct im_connection *ic = req->data;
 	struct twitter_data *td;
-	json_value *parsed, *id;
+	JSON_Value *parsed;
+	jint id;
 
 	// Check if the connection is still active.
@@ -1666,6 +1669,6 @@
 	}
 
-	if ((id = json_o_get(parsed, "id")) && id->type == json_integer) {
-		td->last_status_id = id->u.integer;
+	if ((id = json_object_get_integer(json_object(parsed), "id"))) {
+		td->last_status_id = id;
 	}
 
@@ -1788,5 +1791,6 @@
 {
 	struct im_connection *ic = req->data;
-	json_value *parsed, *id;
+	JSON_Value *parsed;
+	uint64_t id;
 	const char *name;
 
@@ -1800,14 +1804,9 @@
 	}
 
-	/* for the parson branch:
 	name = json_object_dotget_string(json_object(parsed), "user.screen_name");
 	id = json_object_get_integer(json_object(parsed), "id");
-	*/
-
-	name = json_o_str(json_o_get(parsed, "user"), "screen_name");
-	id = json_o_get(parsed, "id");
-
-	if (name && id && id->type == json_integer) {
-		twitter_log(ic, "https://twitter.com/%s/status/%" G_GUINT64_FORMAT, name, id->u.integer);
+
+	if (name && id) {
+		twitter_log(ic, "https://twitter.com/%s/status/%" G_GUINT64_FORMAT, name, id);
 	} else {
 		twitter_log(ic, "Error: could not fetch tweet url.");
