Index: configure
===================================================================
--- configure	(revision 3e5766022e8103765d62343956cf1aeba34b4d82)
+++ configure	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
@@ -26,4 +26,5 @@
 oscar=1
 yahoo=1
+twitter=1
 
 debug=0
@@ -66,4 +67,5 @@
 --oscar=0/1	Disable/enable Oscar part (ICQ, AIM)	$oscar
 --yahoo=0/1	Disable/enable Yahoo part		$yahoo
+--twitter=0/1 Disable/enable Twitter part		$twitter
 
 --debug=0/1	Disable/enable debugging		$debug
@@ -524,4 +526,12 @@
 fi
 
+if [ "$twitter" = 0 ]; then
+	echo '#undef WITH_TWITTER' >> config.h
+else
+	echo '#define WITH_TWITTER' >> config.h
+	protocols=$protocols'twitter '
+	protoobjs=$protoobjs'twitter_mod.o '
+fi
+
 if [ "$protocols" = "PROTOCOLS = " ]; then
 	echo "Warning: You haven't selected any communication protocol to compile!"
Index: protocols/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision 3e5766022e8103765d62343956cf1aeba34b4d82)
+++ protocols/nogaim.c	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
@@ -133,4 +133,5 @@
 	extern void byahoo_initmodule();
 	extern void jabber_initmodule();
+	extern void twitter_initmodule();
 
 #ifdef WITH_MSN
@@ -148,4 +149,8 @@
 #ifdef WITH_JABBER
 	jabber_initmodule();
+#endif
+
+#ifdef WITH_TWITTER
+	twitter_initmodule();
 #endif
 
Index: protocols/twitter/Makefile
===================================================================
--- protocols/twitter/Makefile	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
+++ protocols/twitter/Makefile	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
@@ -0,0 +1,43 @@
+###########################
+## Makefile for BitlBee  ##
+##                       ##
+## Copyright 2002 Lintux ##
+###########################
+
+### DEFINITIONS
+
+-include ../../Makefile.settings
+
+# [SH] Program variables
+objects = twitter.o twitter_http.o twitter_lib.o
+
+CFLAGS += -Wall
+LFLAGS += -r
+
+# [SH] Phony targets
+all: twitter_mod.o
+check: all
+lcov: check
+gcov: 
+	gcov *.c
+	
+.PHONY: all clean distclean
+
+clean:
+	rm -f *.o core
+
+distclean: clean
+
+### MAIN PROGRAM
+
+$(objects): ../../Makefile.settings Makefile
+
+$(objects): %.o: %.c
+	@echo '*' Compiling $<
+	@$(CC) -c $(CFLAGS) $< -o $@
+
+twitter_mod.o: $(objects)
+	@echo '*' Linking twitter_mod.o
+	@$(LD) $(LFLAGS) $(objects) -o twitter_mod.o
+	
+
Index: protocols/twitter/twitter.c
===================================================================
--- protocols/twitter/twitter.c	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
+++ protocols/twitter/twitter.c	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
@@ -0,0 +1,236 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple module to facilitate twitter functionality.                       *
+*                                                                           *
+*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
+*                                                                           *
+*  This library is free software; you can redistribute it and/or            *
+*  modify it under the terms of the GNU Lesser General Public               *
+*  License as published by the Free Software Foundation, version            *
+*  2.1.                                                                     *
+*                                                                           *
+*  This library 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        *
+*  Lesser General Public License for more details.                          *
+*                                                                           *
+*  You should have received a copy of the GNU Lesser General Public License *
+*  along with this library; if not, write to the Free Software Foundation,  *
+*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
+*                                                                           *
+****************************************************************************/
+
+#include "nogaim.h"
+#include "twitter.h"
+#include "twitter_http.h"
+#include "twitter_lib.h"
+
+
+/**
+ * Main loop function
+ */
+gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond)
+{
+	struct im_connection *ic = data;
+	// Check if we are still logged in...
+	// We are logged in if the flag says so and the connection is still in the connections list.
+	if (!g_slist_find( twitter_connections, ic ) ||
+	    (ic->flags & OPT_LOGGED_IN) != OPT_LOGGED_IN) 
+		return 0;
+
+	// If the user uses multiple private message windows we need to get the 
+	// users buddies.
+	if (!set_getbool( &ic->acc->set, "use_groupchat" ))
+		twitter_get_statuses_friends(ic, -1);
+
+	// Do stuff..
+	twitter_get_home_timeline(ic, -1);
+
+	// If we are still logged in run this function again after timeout.
+	return (ic->flags & OPT_LOGGED_IN) == OPT_LOGGED_IN;
+}
+
+
+static void twitter_init( account_t *acc )
+{
+	set_t *s;
+	s = set_add( &acc->set, "use_groupchat", "false", set_eval_bool, acc );
+	s->flags |= ACC_SET_OFFLINE_ONLY;
+}
+
+/**
+ * Login method. Since the twitter API works with seperate HTTP request we 
+ * only save the user and pass to the twitter_data object.
+ */
+static void twitter_login( account_t *acc )
+{
+	struct im_connection *ic = imcb_new( acc );
+	struct twitter_data *td = g_new0( struct twitter_data, 1 );
+
+	twitter_connections = g_slist_append( twitter_connections, ic );
+
+	td->user = acc->user;
+	td->pass = acc->pass;
+	td->home_timeline_id = 0;
+
+	ic->proto_data = td;
+
+	imcb_log( ic, "Connecting to Twitter" );
+	imcb_connected(ic);
+
+	// Run this once. After this queue the main loop function.
+	twitter_main_loop(ic, -1, 0);
+
+	// Queue the main_loop
+	// Save the return value, so we can remove the timeout on logout.
+	td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic);
+}
+
+/**
+ * Logout method. Just free the twitter_data.
+ */
+static void twitter_logout( struct im_connection *ic )
+{
+	struct twitter_data *td = ic->proto_data;
+	
+	// Set the status to logged out.
+	ic->flags = 0;
+
+	// Remove the main_loop function from the function queue.
+	b_event_remove(td->main_loop_id);
+
+	imcb_chat_free(td->home_timeline_gc);
+
+	if( td )
+	{
+		g_free( td );
+	}
+
+	twitter_connections = g_slist_remove( twitter_connections, ic );
+}
+
+/**
+ *
+ */
+static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away )
+{
+	// Let's just update the status.
+//	if ( g_strcasecmp(who, ic->acc->user) == 0 )
+		twitter_post_status(ic, message);
+//	else
+//		twitter_direct_messages_new(ic, who, message);
+	return( 0 );
+}
+
+/**
+ *
+ */
+static GList *twitter_away_states( struct im_connection *ic )
+{
+	static GList *l = NULL;
+	return l;
+}
+
+static void twitter_set_away( struct im_connection *ic, char *state, char *message )
+{
+}
+
+static void twitter_set_my_name( struct im_connection *ic, char *info )
+{
+}
+
+static void twitter_get_info(struct im_connection *ic, char *who) 
+{
+}
+
+static void twitter_add_buddy( struct im_connection *ic, char *who, char *group )
+{
+}
+
+static void twitter_remove_buddy( struct im_connection *ic, char *who, char *group )
+{
+}
+
+static void twitter_chat_msg( struct groupchat *c, char *message, int flags )
+{
+	if( c && message )
+		twitter_post_status(c->ic, message);
+}
+
+static void twitter_chat_invite( struct groupchat *c, char *who, char *message )
+{
+}
+
+static void twitter_chat_leave( struct groupchat *c )
+{
+}
+
+static struct groupchat *twitter_chat_with( struct im_connection *ic, char *who )
+{
+	return NULL;
+}
+
+static void twitter_keepalive( struct im_connection *ic )
+{
+}
+
+static void twitter_add_permit( struct im_connection *ic, char *who )
+{
+}
+
+static void twitter_rem_permit( struct im_connection *ic, char *who )
+{
+}
+
+static void twitter_add_deny( struct im_connection *ic, char *who )
+{
+}
+
+static void twitter_rem_deny( struct im_connection *ic, char *who )
+{
+}
+
+static int twitter_send_typing( struct im_connection *ic, char *who, int typing )
+{
+	return( 1 );
+}
+
+//static char *twitter_set_display_name( set_t *set, char *value )
+//{
+//	return value;
+//}
+
+void twitter_initmodule()
+{
+	struct prpl *ret = g_new0(struct prpl, 1);
+	
+	ret->name = "twitter";
+	ret->login = twitter_login;
+	ret->init = twitter_init;
+	ret->logout = twitter_logout;
+	ret->buddy_msg = twitter_buddy_msg;
+	ret->away_states = twitter_away_states;
+	ret->set_away = twitter_set_away;
+	ret->get_info = twitter_get_info;
+	ret->set_my_name = twitter_set_my_name;
+	ret->add_buddy = twitter_add_buddy;
+	ret->remove_buddy = twitter_remove_buddy;
+	ret->chat_msg = twitter_chat_msg;
+	ret->chat_invite = twitter_chat_invite;
+	ret->chat_leave = twitter_chat_leave;
+	ret->chat_with = twitter_chat_with;
+	ret->keepalive = twitter_keepalive;
+	ret->add_permit = twitter_add_permit;
+	ret->rem_permit = twitter_rem_permit;
+	ret->add_deny = twitter_add_deny;
+	ret->rem_deny = twitter_rem_deny;
+	ret->send_typing = twitter_send_typing;
+	ret->handle_cmp = g_strcasecmp;
+
+	register_protocol(ret);
+
+	// Initialise the twitter_connections GSList.
+	twitter_connections = NULL;
+}
+
Index: protocols/twitter/twitter.h
===================================================================
--- protocols/twitter/twitter.h	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
+++ protocols/twitter/twitter.h	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
@@ -0,0 +1,51 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple module to facilitate twitter functionality.                       *
+*                                                                           *
+*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
+*                                                                           *
+*  This library is free software; you can redistribute it and/or            *
+*  modify it under the terms of the GNU Lesser General Public               *
+*  License as published by the Free Software Foundation, version            *
+*  2.1.                                                                     *
+*                                                                           *
+*  This library 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        *
+*  Lesser General Public License for more details.                          *
+*                                                                           *
+*  You should have received a copy of the GNU Lesser General Public License *
+*  along with this library; if not, write to the Free Software Foundation,  *
+*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
+*                                                                           *
+****************************************************************************/
+
+#include "nogaim.h"
+
+#ifndef _TWITTER_H
+#define _TWITTER_H
+
+#ifdef DEBUG_TWITTER
+#define debug( text... ) imcb_log( ic, text );
+#else
+#define debug( text... )
+#endif
+
+struct twitter_data
+{
+	char* user;
+	char* pass;
+	guint64 home_timeline_id;
+	gint main_loop_id;
+	struct groupchat *home_timeline_gc;
+};
+
+/**
+ * This has the same function as the msn_connections GSList. We use this to 
+ * make sure the connection is still alive in callbacks before we do anything
+ * else.
+ */
+GSList *twitter_connections;
+
+#endif //_TWITTER_H
Index: protocols/twitter/twitter_http.c
===================================================================
--- protocols/twitter/twitter_http.c	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
+++ protocols/twitter/twitter_http.c	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
@@ -0,0 +1,161 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple module to facilitate twitter functionality.                       *
+*                                                                           *
+*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
+*                                                                           *
+*  This library is free software; you can redistribute it and/or            *
+*  modify it under the terms of the GNU Lesser General Public               *
+*  License as published by the Free Software Foundation, version            *
+*  2.1.                                                                     *
+*                                                                           *
+*  This library 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        *
+*  Lesser General Public License for more details.                          *
+*                                                                           *
+*  You should have received a copy of the GNU Lesser General Public License *
+*  along with this library; if not, write to the Free Software Foundation,  *
+*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
+*                                                                           *
+****************************************************************************/
+
+/***************************************************************************\
+*                                                                           *
+*  Some funtions within this file have been copied from other files within  *
+*  BitlBee.                                                                 *
+*                                                                           *
+****************************************************************************/ 
+
+#include "twitter_http.h"
+#include "twitter.h"
+#include "bitlbee.h"
+#include "url.h"
+#include "misc.h"
+#include "base64.h"
+#include <ctype.h>
+#include <errno.h>
+
+
+char *twitter_url_append(char *url, char *key, char* value);
+
+/**
+ * Do a request.
+ * This is actually pretty generic function... Perhaps it should move to the lib/http_client.c
+ */
+void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, char* user, char* pass, char** arguments, int arguments_len)
+{
+	url_t *url = g_new0( url_t, 1 );
+	char *tmp;
+	char *request;
+	void *ret;
+	char *userpass = NULL;
+	char *userpass_base64;
+	char *url_arguments;
+
+	// Fill the url structure.
+	if( !url_set( url, url_string ) )
+	{
+		g_free( url );
+		return NULL;
+	}
+
+	if( url->proto != PROTO_HTTP && url->proto != PROTO_HTTPS )
+	{
+		g_free( url );
+		return NULL;
+	}
+
+	// Concatenate user and pass
+	if (user && pass) {
+		userpass = g_strdup_printf("%s:%s", user, pass);
+		userpass_base64 = base64_encode((unsigned char*)userpass, strlen(userpass));
+	} else {
+		userpass_base64 = NULL;
+	}
+
+	url_arguments = g_malloc(1);
+	url_arguments[0] = '\0';
+
+	// Construct the url arguments.
+	if (arguments_len != 0)
+	{
+		int i;
+		for (i=0; i<arguments_len; i+=2) 
+		{
+			tmp = twitter_url_append(url_arguments, arguments[i], arguments[i+1]);
+			g_free(url_arguments);
+			url_arguments = tmp;
+		}
+	}
+
+	// Do GET stuff...
+	if (!is_post)
+	{
+		// Find the char-pointer of the end of the string.
+		tmp = url->file + strlen(url->file);
+		tmp[0] = '?';
+		// append the url_arguments to the end of the url->file.
+		// TODO GM: Check the length?
+		g_stpcpy (tmp+1, url_arguments);
+	}
+
+
+	// Make the request.
+	request = g_strdup_printf(  "%s %s HTTP/1.0\r\n"
+	                            "Host: %s\r\n"
+	                            "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n",
+	                            is_post ? "POST" : "GET", url->file, url->host );
+
+	// If a pass and user are given we append them to the request.
+	if (userpass_base64)
+	{
+		tmp = g_strdup_printf("%sAuthorization: Basic %s\r\n", request, userpass_base64);
+		g_free(request);
+		request = tmp;
+	}
+
+	// Do POST stuff..
+	if (is_post)
+	{
+		// Append the Content-Type and url-encoded arguments.
+		tmp = g_strdup_printf("%sContent-Type: application/x-www-form-urlencoded\r\nContent-Length: %zd\r\n\r\n%s", 
+								request, strlen(url_arguments), url_arguments);
+		g_free(request);
+		request = tmp;
+	} else {
+		// Append an extra \r\n to end the request...
+		tmp = g_strdup_printf("%s\r\n", request);
+		g_free(request);
+		request = tmp;
+	}
+
+	ret = http_dorequest( url->host, url->port,	url->proto == PROTO_HTTPS, request, func, data );
+
+	g_free( url );
+	g_free( userpass );
+	g_free( userpass_base64 );
+	g_free( url_arguments );
+	g_free( request );
+	return ret;
+}
+
+char *twitter_url_append(char *url, char *key, char* value)
+{
+	char *key_encoded = g_strndup(key, 3 * strlen(key));
+	http_encode(key_encoded);
+	char *value_encoded = g_strndup(value, 3 * strlen(value));
+	http_encode(value_encoded);
+
+	char *retval;
+	if (strlen(url) != 0)
+		retval = g_strdup_printf("%s&%s=%s", url, key_encoded, value_encoded);
+	else
+		retval = g_strdup_printf("%s=%s", key_encoded, value_encoded);
+
+	g_free(key_encoded);
+	g_free(value_encoded);
+
+	return retval;
+}
Index: protocols/twitter/twitter_http.h
===================================================================
--- protocols/twitter/twitter_http.h	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
+++ protocols/twitter/twitter_http.h	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
@@ -0,0 +1,34 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple module to facilitate twitter functionality.                       *
+*                                                                           *
+*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
+*                                                                           *
+*  This library is free software; you can redistribute it and/or            *
+*  modify it under the terms of the GNU Lesser General Public               *
+*  License as published by the Free Software Foundation, version            *
+*  2.1.                                                                     *
+*                                                                           *
+*  This library 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        *
+*  Lesser General Public License for more details.                          *
+*                                                                           *
+*  You should have received a copy of the GNU Lesser General Public License *
+*  along with this library; if not, write to the Free Software Foundation,  *
+*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
+*                                                                           *
+****************************************************************************/
+
+#ifndef _TWITTER_HTTP_H
+#define _TWITTER_HTTP_H
+
+#include "nogaim.h"
+#include "http_client.h"
+
+void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, 
+					char* user, char* pass, char** arguments, int arguments_len);
+
+#endif //_TWITTER_HTTP_H
+
Index: protocols/twitter/twitter_lib.c
===================================================================
--- protocols/twitter/twitter_lib.c	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
+++ protocols/twitter/twitter_lib.c	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
@@ -0,0 +1,606 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple module to facilitate twitter functionality.                       *
+*                                                                           *
+*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
+*                                                                           *
+*  This library is free software; you can redistribute it and/or            *
+*  modify it under the terms of the GNU Lesser General Public               *
+*  License as published by the Free Software Foundation, version            *
+*  2.1.                                                                     *
+*                                                                           *
+*  This library 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        *
+*  Lesser General Public License for more details.                          *
+*                                                                           *
+*  You should have received a copy of the GNU Lesser General Public License *
+*  along with this library; if not, write to the Free Software Foundation,  *
+*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
+*                                                                           *
+****************************************************************************/
+
+#include "twitter_http.h"
+#include "twitter.h"
+#include "bitlbee.h"
+#include "url.h"
+#include "misc.h"
+#include "base64.h"
+#include "xmltree.h"
+#include "twitter_lib.h"
+#include <ctype.h>
+#include <errno.h>
+
+#define TXL_STATUS 1
+#define TXL_USER 2
+#define TXL_ID 3
+
+struct twitter_xml_list {
+	int type;
+	int next_cursor;
+	GSList *list;
+	gpointer data;
+};
+
+struct twitter_xml_user {
+	char *name;
+	char *screen_name;
+};
+
+struct twitter_xml_status {
+	char *created_at;
+	char *text;
+	struct twitter_xml_user *user;
+	guint64 id;
+};
+
+/**
+ * Frees a twitter_xml_user struct.
+ */
+static void txu_free(struct twitter_xml_user *txu)
+{
+	g_free(txu->name);
+	g_free(txu->screen_name);
+	g_free(txu);
+}
+
+
+/**
+ * Frees a twitter_xml_status struct.
+ */
+static void txs_free(struct twitter_xml_status *txs)
+{
+	g_free(txs->created_at);
+	g_free(txs->text);
+	txu_free(txs->user);
+	g_free(txs);
+}
+
+/**
+ * Free a twitter_xml_list struct.
+ * type is the type of list the struct holds.
+ */
+static void txl_free(struct twitter_xml_list *txl)
+{
+	GSList *l;
+	for ( l = txl->list; l ; l = g_slist_next(l) )
+		if (txl->type == TXL_STATUS)
+			txs_free((struct twitter_xml_status *)l->data);
+		else if (txl->type == TXL_ID)
+			g_free(l->data);
+	g_slist_free(txl->list);
+}
+
+/**
+ * Add a buddy if it is not allready added, set the status to logged in.
+ */
+static void twitter_add_buddy(struct im_connection *ic, char *name)
+{
+	struct twitter_data *td = ic->proto_data;
+
+	// Check if the buddy is allready in the buddy list.
+	if (!imcb_find_buddy( ic, name ))
+	{
+		// The buddy is not in the list, add the buddy and set the status to logged in.
+		imcb_add_buddy( ic, name, NULL );
+		if (set_getbool( &ic->acc->set, "use_groupchat" ))
+			imcb_chat_add_buddy( td->home_timeline_gc, name );
+		else
+			imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL );
+	}
+}
+
+static void twitter_http_get_friends_ids(struct http_request *req);
+
+/**
+ * Get the friends ids.
+ */
+void twitter_get_friends_ids(struct im_connection *ic, int next_cursor)
+{
+	struct twitter_data *td = ic->proto_data;
+
+	// Primitive, but hey! It works...	
+	char* args[2];
+	args[0] = "cursor";
+	args[1] = g_strdup_printf ("%d", next_cursor);
+	twitter_http(TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, td->user, td->pass, args, 2);
+
+	g_free(args[1]);
+}
+
+/**
+ * Function to help fill a list.
+ */
+static xt_status twitter_xt_next_cursor( struct xt_node *node, struct twitter_xml_list *txl )
+{
+	// Do something with the cursor.
+	txl->next_cursor = node->text != NULL ? atoi(node->text) : -1;
+
+	return XT_HANDLED;
+}
+
+/**
+ * Fill a list of ids.
+ */
+static xt_status twitter_xt_get_friends_id_list( struct xt_node *node, struct twitter_xml_list *txl )
+{
+	struct xt_node *child;
+	
+	// Set the list type.
+	txl->type = TXL_ID;
+
+	// The root <statuses> node should hold the list of statuses <status>
+	// Walk over the nodes children.
+	for( child = node->children ; child ; child = child->next )
+	{
+		if ( g_strcasecmp( "id", child->name ) == 0)
+		{
+			// Add the item to the list.
+			txl->list = g_slist_append (txl->list, g_memdup( child->text, child->text_len + 1 ));
+		}
+		else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
+		{
+			twitter_xt_next_cursor(child, txl);
+		}
+	}
+
+	return XT_HANDLED;
+}
+
+/**
+ * Callback for getting the friends ids.
+ */
+static void twitter_http_get_friends_ids(struct http_request *req)
+{
+	struct im_connection *ic;
+	struct xt_parser *parser;
+	struct twitter_xml_list *txl;
+
+	ic = req->data;
+
+	// Check if the connection is still active.
+	if( !g_slist_find( twitter_connections, ic ) )
+		return;
+
+	// Check if the HTTP request went well.
+	if (req->status_code != 200) {
+		// It didn't go well, output the error and return.
+		imcb_error(ic, "Could not retrieve friends. HTTP STATUS: %d", req->status_code);
+		return;
+	}
+
+	txl = g_new0(struct twitter_xml_list, 1);
+
+	// Parse the data.
+	parser = xt_new( NULL, txl );
+	xt_feed( parser, req->reply_body, req->body_size );
+	twitter_xt_get_friends_id_list(parser->root, txl);
+	xt_free( parser );
+
+	if (txl->next_cursor)
+		twitter_get_friends_ids(ic, txl->next_cursor);
+
+	txl_free(txl);
+	g_free(txl);
+}
+
+/**
+ * Function to fill a twitter_xml_user struct.
+ * It sets:
+ *  - the name and
+ *  - the screen_name.
+ */
+static xt_status twitter_xt_get_user( struct xt_node *node, struct twitter_xml_user *txu )
+{
+	struct xt_node *child;
+
+	// Walk over the nodes children.
+	for( child = node->children ; child ; child = child->next )
+	{
+		if ( g_strcasecmp( "name", child->name ) == 0)
+		{
+			txu->name = g_memdup( child->text, child->text_len + 1 );
+		}
+		else if (g_strcasecmp( "screen_name", child->name ) == 0)
+		{
+			txu->screen_name = g_memdup( child->text, child->text_len + 1 );
+		}
+	}
+	return XT_HANDLED;
+}
+
+/**
+ * Function to fill a twitter_xml_list struct.
+ * It sets:
+ *  - all <user>s from the <users> element.
+ */
+static xt_status twitter_xt_get_users( struct xt_node *node, struct twitter_xml_list *txl )
+{
+	struct twitter_xml_user *txu;
+	struct xt_node *child;
+
+	// Set the type of the list.
+	txl->type = TXL_USER;
+
+	// The root <users> node should hold the list of users <user>
+	// Walk over the nodes children.
+	for( child = node->children ; child ; child = child->next )
+	{
+		if ( g_strcasecmp( "user", child->name ) == 0)
+		{
+			txu = g_new0(struct twitter_xml_user, 1);
+			twitter_xt_get_user(child, txu);
+			// Put the item in the front of the list.
+			txl->list = g_slist_prepend (txl->list, txu);
+		}
+	}
+
+	return XT_HANDLED;
+}
+
+/**
+ * Function to fill a twitter_xml_list struct.
+ * It calls twitter_xt_get_users to get the <user>s from a <users> element.
+ * It sets:
+ *  - the next_cursor.
+ */
+static xt_status twitter_xt_get_user_list( struct xt_node *node, struct twitter_xml_list *txl )
+{
+	struct xt_node *child;
+
+	// Set the type of the list.
+	txl->type = TXL_USER;
+
+	// The root <user_list> node should hold a users <users> element
+	// Walk over the nodes children.
+	for( child = node->children ; child ; child = child->next )
+	{
+		if ( g_strcasecmp( "users", child->name ) == 0)
+		{
+			twitter_xt_get_users(child, txl);
+		}
+		else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
+		{
+			twitter_xt_next_cursor(child, txl);
+		}
+	}
+
+	return XT_HANDLED;
+}
+
+
+/**
+ * Function to fill a twitter_xml_status struct.
+ * It sets:
+ *  - the status text and
+ *  - the created_at timestamp and
+ *  - the status id and
+ *  - the user in a twitter_xml_user struct.
+ */
+static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml_status *txs )
+{
+	struct xt_node *child;
+
+	// Walk over the nodes children.
+	for( child = node->children ; child ; child = child->next )
+	{
+		if ( g_strcasecmp( "text", child->name ) == 0)
+		{
+			txs->text = g_memdup( child->text, child->text_len + 1 );
+		}
+		else if (g_strcasecmp( "created_at", child->name ) == 0)
+		{
+			txs->created_at = g_memdup( child->text, child->text_len + 1 );
+		}
+		else if (g_strcasecmp( "user", child->name ) == 0)
+		{
+			txs->user = g_new0(struct twitter_xml_user, 1);
+			twitter_xt_get_user( child, txs->user );
+		}
+		else if (g_strcasecmp( "id", child->name ) == 0)
+		{
+			txs->id = g_ascii_strtoull (child->text, NULL, 10);
+		}
+	}
+	return XT_HANDLED;
+}
+
+/**
+ * Function to fill a twitter_xml_list struct.
+ * It sets:
+ *  - all <status>es within the <status> element and
+ *  - the next_cursor.
+ */
+static xt_status twitter_xt_get_status_list( struct xt_node *node, struct twitter_xml_list *txl )
+{
+	struct twitter_xml_status *txs;
+	struct xt_node *child;
+
+	// Set the type of the list.
+	txl->type = TXL_STATUS;
+
+	// The root <statuses> node should hold the list of statuses <status>
+	// Walk over the nodes children.
+	for( child = node->children ; child ; child = child->next )
+	{
+		if ( g_strcasecmp( "status", child->name ) == 0)
+		{
+			txs = g_new0(struct twitter_xml_status, 1);
+			twitter_xt_get_status(child, txs);
+			// Put the item in the front of the list.
+			txl->list = g_slist_prepend (txl->list, txs);
+		}
+		else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
+		{
+			twitter_xt_next_cursor(child, txl);
+		}
+	}
+
+	return XT_HANDLED;
+}
+
+static void twitter_http_get_home_timeline(struct http_request *req);
+
+/**
+ * Get the timeline.
+ */
+void twitter_get_home_timeline(struct im_connection *ic, int next_cursor)
+{
+	struct twitter_data *td = ic->proto_data;
+
+	char* args[4];
+	args[0] = "cursor";
+	args[1] = g_strdup_printf ("%d", next_cursor);
+	if (td->home_timeline_id) {
+		args[2] = "since_id";
+		args[3] = g_strdup_printf ("%llu", (long long unsigned int) td->home_timeline_id);
+	}
+
+	twitter_http(TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, td->user, td->pass, args, td->home_timeline_id ? 4 : 2);
+
+	g_free(args[1]);
+	if (td->home_timeline_id) {
+		g_free(args[3]);
+	}
+}
+
+/**
+ * Function that is called to see the statuses in a groupchat window.
+ */
+static void twitter_groupchat(struct im_connection *ic, GSList *list)
+{
+	struct twitter_data *td = ic->proto_data;
+	GSList *l = NULL;
+	struct twitter_xml_status *status;
+	struct groupchat *gc;
+
+	// Create a new groupchat if it does not exsist.
+	if (!td->home_timeline_gc)
+	{   
+		td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" );
+		// Add the current user to the chat...
+		imcb_chat_add_buddy( gc, ic->acc->user );
+	}
+	else
+	{   
+		gc = td->home_timeline_gc;
+	}
+
+	for ( l = list; l ; l = g_slist_next(l) )
+	{
+		status = l->data;
+		twitter_add_buddy(ic, status->user->screen_name);
+		
+		// Say it!
+		if (g_strcasecmp(td->user, status->user->screen_name) == 0)
+			imcb_chat_log (gc, "Your Tweet: %s", status->text);
+		else
+			imcb_chat_msg (gc, status->user->screen_name, status->text, 0, 0 );
+		
+		// Update the home_timeline_id to hold the highest id, so that by the next request
+		// we won't pick up the updates allready in the list.
+		td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id;
+	}
+}
+
+/**
+ * Function that is called to see statuses as private messages.
+ */
+static void twitter_private_message_chat(struct im_connection *ic, GSList *list)
+{
+	struct twitter_data *td = ic->proto_data;
+	GSList *l = NULL;
+	struct twitter_xml_status *status;
+
+	for ( l = list; l ; l = g_slist_next(l) )
+	{
+		status = l->data;
+		imcb_buddy_msg( ic, status->user->screen_name, status->text, 0, 0 );
+		// Update the home_timeline_id to hold the highest id, so that by the next request
+		// we won't pick up the updates allready in the list.
+		td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id;
+	}
+}
+
+/**
+ * Callback for getting the home timeline.
+ */
+static void twitter_http_get_home_timeline(struct http_request *req)
+{
+	struct im_connection *ic = req->data;
+	struct xt_parser *parser;
+	struct twitter_xml_list *txl;
+
+	// Check if the connection is still active.
+	if( !g_slist_find( twitter_connections, ic ) )
+		return;
+
+	// Check if the HTTP request went well.
+	if (req->status_code != 200) {
+		// It didn't go well, output the error and return.
+		imcb_error(ic, "Could not retrieve " TWITTER_HOME_TIMELINE_URL ". HTTP STATUS: %d", req->status_code);
+		return;
+	}
+
+	txl = g_new0(struct twitter_xml_list, 1);
+	txl->list = NULL;
+
+	// Parse the data.
+	parser = xt_new( NULL, txl );
+	xt_feed( parser, req->reply_body, req->body_size );
+	// The root <statuses> node should hold the list of statuses <status>
+	twitter_xt_get_status_list(parser->root, txl);
+	xt_free( parser );
+
+	// See if the user wants to see the messages in a groupchat window or as private messages.
+	if (set_getbool( &ic->acc->set, "use_groupchat" ))
+		twitter_groupchat(ic, txl->list);
+	else
+		twitter_private_message_chat(ic, txl->list);
+
+	// Free the structure.	
+	txl_free(txl);
+	g_free(txl);
+}
+
+/**
+ * Callback for getting (twitter)friends...
+ *
+ * Be afraid, be very afraid! This function will potentially add hundreds of "friends". "Who has 
+ * hundreds of friends?" you wonder? You probably not, since you are reading the source of 
+ * BitlBee... Get a life and meet new people!
+ */
+static void twitter_http_get_statuses_friends(struct http_request *req)
+{
+	struct im_connection *ic = req->data;
+	struct xt_parser *parser;
+	struct twitter_xml_list *txl;
+	GSList *l = NULL;
+	struct twitter_xml_user *user;
+
+	// Check if the connection is still active.
+	if( !g_slist_find( twitter_connections, ic ) )
+		return;
+
+	// Check if the HTTP request went well.
+	if (req->status_code != 200) {
+		// It didn't go well, output the error and return.
+		imcb_error(ic, "Could not retrieve " TWITTER_SHOW_FRIENDS_URL " HTTP STATUS: %d", req->status_code);
+		return;
+	}
+
+	txl = g_new0(struct twitter_xml_list, 1);
+	txl->list = NULL;
+
+	// Parse the data.
+	parser = xt_new( NULL, txl );
+	xt_feed( parser, req->reply_body, req->body_size );
+
+	// Get the user list from the parsed xml feed.
+	twitter_xt_get_user_list(parser->root, txl);
+	xt_free( parser );
+
+	// Add the users as buddies.
+	for ( l = txl->list; l ; l = g_slist_next(l) )
+	{
+		user = l->data;
+		twitter_add_buddy(ic, user->screen_name);
+	}
+
+	// if the next_cursor is set to something bigger then 0 there are more friends to gather.
+	if (txl->next_cursor > 0)
+		twitter_get_statuses_friends(ic, txl->next_cursor);
+
+	// Free the structure.
+	txl_free(txl);
+	g_free(txl);
+}
+
+/**
+ * Get the friends.
+ */
+void twitter_get_statuses_friends(struct im_connection *ic, int next_cursor)
+{
+	struct twitter_data *td = ic->proto_data;
+
+	char* args[2];
+	args[0] = "cursor";
+	args[1] = g_strdup_printf ("%d", next_cursor);
+
+	twitter_http(TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, td->user, td->pass, args, 2);
+
+	g_free(args[1]);
+}
+
+/**
+ * Callback after sending a new update to twitter.
+ */
+static void twitter_http_post_status(struct http_request *req)
+{
+	struct im_connection *ic = req->data;
+
+	// Check if the connection is still active.
+	if( !g_slist_find( twitter_connections, ic ) )
+		return;
+
+	// Check if the HTTP request went well.
+	if (req->status_code != 200) {
+		// It didn't go well, output the error and return.
+		imcb_error(ic, "Could not post tweet... HTTP STATUS: %d", req->status_code);
+		return;
+	}
+}
+
+/**
+ * Function to POST a new status to twitter.
+ */ 
+void twitter_post_status(struct im_connection *ic, char* msg)
+{
+	struct twitter_data *td = ic->proto_data;
+
+	char* args[2];
+	args[0] = "status";
+	args[1] = msg;
+	twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 2);
+//	g_free(args[1]);
+}
+
+
+/**
+ * Function to POST a new message to twitter.
+ */
+void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg)
+{
+	struct twitter_data *td = ic->proto_data;
+
+	char* args[4];
+	args[0] = "screen_name";
+	args[1] = who;
+	args[2] = "text";
+	args[3] = msg;
+	// Use the same callback as for twitter_post_status, since it does basically the same.
+	twitter_http(TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 4);
+//	g_free(args[1]);
+//	g_free(args[3]);
+}
Index: protocols/twitter/twitter_lib.h
===================================================================
--- protocols/twitter/twitter_lib.h	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
+++ protocols/twitter/twitter_lib.h	(revision 123cac724c5907365e0c4bd939806cc240e764f0)
@@ -0,0 +1,86 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple module to facilitate twitter functionality.                       *
+*                                                                           *
+*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
+*                                                                           *
+*  This library is free software; you can redistribute it and/or            *
+*  modify it under the terms of the GNU Lesser General Public               *
+*  License as published by the Free Software Foundation, version            *
+*  2.1.                                                                     *
+*                                                                           *
+*  This library 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        *
+*  Lesser General Public License for more details.                          *
+*                                                                           *
+*  You should have received a copy of the GNU Lesser General Public License *
+*  along with this library; if not, write to the Free Software Foundation,  *
+*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
+*                                                                           *
+****************************************************************************/
+
+
+#ifndef _TWITTER_LIB_H
+#define _TWITTER_LIB_H
+
+#include "nogaim.h"
+#include "twitter_http.h"
+
+#define TWITTER_API_URL "http://twitter.com"
+
+/* Status URLs */
+#define TWITTER_STATUS_UPDATE_URL TWITTER_API_URL "/statuses/update.xml"
+#define TWITTER_STATUS_SHOW_URL TWITTER_API_URL "/statuses/show/"
+#define TWITTER_STATUS_DESTROY_URL TWITTER_API_URL "/statuses/destroy/"
+
+/* Timeline URLs */
+#define TWITTER_PUBLIC_TIMELINE_URL TWITTER_API_URL "/statuses/public_timeline.xml"
+#define TWITTER_FEATURED_USERS_URL TWITTER_API_URL "/statuses/featured.xml"
+#define TWITTER_FRIENDS_TIMELINE_URL TWITTER_API_URL "/statuses/friends_timeline.xml"
+#define TWITTER_HOME_TIMELINE_URL TWITTER_API_URL "/statuses/home_timeline.xml"
+#define TWITTER_MENTIONS_URL TWITTER_API_URL "/statuses/mentions.xml"
+#define TWITTER_USER_TIMELINE_URL TWITTER_API_URL "/statuses/user_timeline.xml"
+
+/* Users URLs */
+#define TWITTER_SHOW_USERS_URL TWITTER_API_URL "/users/show.xml"
+#define TWITTER_SHOW_FRIENDS_URL TWITTER_API_URL "/statuses/friends.xml"
+#define TWITTER_SHOW_FOLLOWERS_URL TWITTER_API_URL "/statuses/followers.xml"
+
+/* Direct messages URLs */
+#define TWITTER_DIRECT_MESSAGES_URL TWITTER_API_URL "/direct_messages.xml"
+#define TWITTER_DIRECT_MESSAGES_NEW_URL TWITTER_API_URL "/direct_messages/new.xml"
+#define TWITTER_DIRECT_MESSAGES_SENT_URL TWITTER_API_URL "/direct_messages/sent.xml"
+#define TWITTER_DIRECT_MESSAGES_DESTROY_URL TWITTER_API_URL "/direct_messages/destroy/"
+
+/* Friendships URLs */
+#define TWITTER_FRIENDSHIPS_CREATE_URL TWITTER_API_URL "/friendships/create.xml"
+#define TWITTER_FRIENDSHIPS_DESTROY_URL TWITTER_API_URL "/friendships/destroy.xml"
+#define TWITTER_FRIENDSHIPS_SHOW_URL TWITTER_API_URL "/friendships/show.xml"
+
+/* Social graphs URLs */
+#define TWITTER_FRIENDS_IDS_URL TWITTER_API_URL "/friends/ids.xml"
+#define TWITTER_FOLLOWERS_IDS_URL TWITTER_API_URL "/followers/ids.xml"
+
+/* Account URLs */
+#define TWITTER_ACCOUNT_RATE_LIMIT_URL TWITTER_API_URL "/account/rate_limit_status.xml"
+
+/* Favorites URLs */
+#define TWITTER_FAVORITES_GET_URL TWITTER_API_URL "/favorites.xml"
+#define TWITTER_FAVORITE_CREATE_URL TWITTER_API_URL "/favorites/create/"
+#define TWITTER_FAVORITE_DESTROY_URL TWITTER_API_URL "/favorites/destroy/"
+
+/* Block URLs */
+#define TWITTER_BLOCKS_CREATE_URL TWITTER_API_URL "/blocks/create/"
+#define TWITTER_BLOCKS_DESTROY_URL TWITTER_API_URL "/blocks/destroy/"
+
+void twitter_get_friends_ids(struct im_connection *ic, int next_cursor);
+void twitter_get_home_timeline(struct im_connection *ic, int next_cursor);
+void twitter_get_statuses_friends(struct im_connection *ic, int next_cursor);
+
+void twitter_post_status(struct im_connection *ic, char *msg);
+void twitter_direct_messages_new(struct im_connection *ic, char *who, char *message);
+
+#endif //_TWITTER_LIB_H
+
