Index: protocols/twitter/twitter.c
===================================================================
--- protocols/twitter/twitter.c	(revision 4804b2f2e7270148ab303bf31351a5e2aa829761)
+++ protocols/twitter/twitter.c	(revision 2322a9f38a2f6c9bf86eb62c2cd68fd3848b694f)
@@ -30,10 +30,10 @@
 
 #define twitter_msg( ic, fmt... ) \
-	do {                                                        \
-		struct twitter_data *td = ic->proto_data;           \
-		if( td->home_timeline_gc )                          \
-			imcb_chat_log( td->home_timeline_gc, fmt ); \
-		else                                                \
-			imcb_log( ic, fmt );                        \
+	do {                                            \
+		struct twitter_data *td = ic->proto_data;   \
+		if( td->timeline_gc )                       \
+			imcb_chat_log( td->timeline_gc, fmt );  \
+		else                                        \
+			imcb_log( ic, fmt );                    \
 	} while( 0 );
 
@@ -52,5 +52,5 @@
 
 	// Do stuff..
-	twitter_get_home_timeline(ic, -1);
+	twitter_get_timeline(ic, -1);
 
 	// If we are still logged in run this function again after timeout.
@@ -69,5 +69,6 @@
 	// 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);
+	td->main_loop_id =
+	    b_timeout_add(set_getint(&ic->acc->set, "fetch_interval") * 1000, twitter_main_loop, ic);
 }
 
@@ -77,4 +78,6 @@
 {
 	struct twitter_data *td = ic->proto_data;
+
+	td->flags &= ~TWITTER_DOING_TIMELINE;
 
 	if (set_getbool(&ic->acc->set, "oauth") && !td->oauth_info)
@@ -216,5 +219,4 @@
 		def_oauth = "true";
 	} else {		/* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */
-
 		def_url = IDENTICA_API_URL;
 		def_oauth = "false";
@@ -228,4 +230,9 @@
 	s = set_add(&acc->set, "commands", "true", set_eval_bool, acc);
 
+	s = set_add(&acc->set, "fetch_interval", "60", set_eval_int, acc);
+	s->flags |= ACC_SET_OFFLINE_ONLY;
+
+	s = set_add(&acc->set, "fetch_mentions", "true", set_eval_bool, acc);
+
 	s = set_add(&acc->set, "message_length", "140", set_eval_int, acc);
 
@@ -235,4 +242,6 @@
 	s = set_add(&acc->set, "show_ids", "false", set_eval_bool, acc);
 	s->flags |= ACC_SET_OFFLINE_ONLY;
+
+	s = set_add(&acc->set, "show_old_mentions", "true", set_eval_bool, acc);
 
 	s = set_add(&acc->set, "oauth", def_oauth, set_eval_bool, acc);
@@ -317,6 +326,6 @@
 	b_event_remove(td->main_loop_id);
 
-	if (td->home_timeline_gc)
-		imcb_chat_free(td->home_timeline_gc);
+	if (td->timeline_gc)
+		imcb_chat_free(td->timeline_gc);
 
 	if (td) {
@@ -404,11 +413,11 @@
 	struct twitter_data *td = c->ic->proto_data;
 
-	if (c != td->home_timeline_gc)
+	if (c != td->timeline_gc)
 		return;		/* WTF? */
 
 	/* If the user leaves the channel: Fine. Rejoin him/her once new
 	   tweets come in. */
-	imcb_chat_free(td->home_timeline_gc);
-	td->home_timeline_gc = NULL;
+	imcb_chat_free(td->timeline_gc);
+	td->timeline_gc = NULL;
 }
 
Index: protocols/twitter/twitter.h
===================================================================
--- protocols/twitter/twitter.h	(revision 4804b2f2e7270148ab303bf31351a5e2aa829761)
+++ protocols/twitter/twitter.h	(revision 2322a9f38a2f6c9bf86eb62c2cd68fd3848b694f)
@@ -36,4 +36,7 @@
 {
 	TWITTER_HAVE_FRIENDS = 1,
+	TWITTER_DOING_TIMELINE = 0x10000,
+	TWITTER_GOT_TIMELINE = 0x20000,
+	TWITTER_GOT_MENTIONS = 0x40000,
 } twitter_flags_t;
 
@@ -44,10 +47,15 @@
 	char* user;
 	struct oauth_info *oauth_info;
+
+	gpointer home_timeline_obj;
+	gpointer mentions_obj;
+
+	guint64 timeline_id;
+
 	GSList *follow_ids;
 	
-	guint64 home_timeline_id;
 	guint64 last_status_id; /* For undo */
 	gint main_loop_id;
-	struct groupchat *home_timeline_gc;
+	struct groupchat *timeline_gc;
 	gint http_fails;
 	twitter_flags_t flags;
Index: protocols/twitter/twitter_lib.c
===================================================================
--- protocols/twitter/twitter_lib.c	(revision 4804b2f2e7270148ab303bf31351a5e2aa829761)
+++ protocols/twitter/twitter_lib.c	(revision 2322a9f38a2f6c9bf86eb62c2cd68fd3848b694f)
@@ -78,4 +78,5 @@
 	if (txu == NULL)
 		return;
+
 	g_free(txu->name);
 	g_free(txu->screen_name);
@@ -83,5 +84,4 @@
 }
 
-
 /**
  * Frees a twitter_xml_status struct.
@@ -89,4 +89,7 @@
 static void txs_free(struct twitter_xml_status *txs)
 {
+	if (txs == NULL)
+		return;
+
 	g_free(txs->text);
 	txu_free(txs->user);
@@ -103,11 +106,15 @@
 	if (txl == NULL)
 		return;
-	for (l = txl->list; l; l = g_slist_next(l))
-		if (txl->type == TXL_STATUS)
+
+	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)
+		} else if (txl->type == TXL_ID) {
 			g_free(l->data);
-		else if (txl->type == TXL_USER)
+		} else if (txl->type == TXL_USER) {
 			txu_free(l->data);
+		}
+	}
+
 	g_slist_free(txl->list);
 	g_free(txl);
@@ -115,5 +122,22 @@
 
 /**
- * Add a buddy if it is not allready added, set the status to logged in.
+ * Compare status elements
+ */
+static gint twitter_compare_elements(gconstpointer a, gconstpointer b)
+{
+	struct twitter_xml_status *a_status = (struct twitter_xml_status *) a;
+	struct twitter_xml_status *b_status = (struct twitter_xml_status *) b;
+
+	if (a_status->created_at < b_status->created_at) {
+		return -1;
+	} else if (a_status->created_at > b_status->created_at) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+/**
+ * Add a buddy if it is not already added, set the status to logged in.
  */
 static void twitter_add_buddy(struct im_connection *ic, char *name, const char *fullname)
@@ -132,5 +156,5 @@
 			   exact Twitter username. */
 			imcb_buddy_nick_hint(ic, name, name);
-			imcb_chat_add_buddy(td->home_timeline_gc, name);
+			imcb_chat_add_buddy(td->timeline_gc, name);
 		} else if (g_strcasecmp(mode, "many") == 0)
 			imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL);
@@ -260,5 +284,5 @@
 
 	/* Create the room now that we "logged in". */
-	if (!td->home_timeline_gc && g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
+	if (!td->timeline_gc && g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
 		twitter_groupchat_init(ic);
 
@@ -522,30 +546,4 @@
 }
 
-static void twitter_http_get_home_timeline(struct http_request *req);
-
-/**
- * Get the timeline.
- */
-void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)
-{
-	struct twitter_data *td = ic->proto_data;
-
-	char *args[4];
-	args[0] = "cursor";
-	args[1] = g_strdup_printf("%lld", (long long) 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(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args,
-		     td->home_timeline_id ? 4 : 2);
-
-	g_free(args[1]);
-	if (td->home_timeline_id) {
-		g_free(args[3]);
-	}
-}
-
 static char *twitter_msg_add_id(struct im_connection *ic,
 				struct twitter_xml_status *txs, const char *prefix)
@@ -586,5 +584,5 @@
 	GSList *l;
 
-	td->home_timeline_gc = gc = imcb_chat_new(ic, "home/timeline");
+	td->timeline_gc = gc = imcb_chat_new(ic, "twitter/timeline");
 
 	name_hint = g_strdup_printf("%s_%s", td->prefix, ic->acc->user);
@@ -595,5 +593,5 @@
 		bee_user_t *bu = l->data;
 		if (bu->ic == ic)
-			imcb_chat_add_buddy(td->home_timeline_gc, bu->handle);
+			imcb_chat_add_buddy(td->timeline_gc, bu->handle);
 	}
 }
@@ -608,10 +606,11 @@
 	struct twitter_xml_status *status;
 	struct groupchat *gc;
+	guint64 last_id = 0;
 
 	// Create a new groupchat if it does not exsist.
-	if (!td->home_timeline_gc)
+	if (!td->timeline_gc)
 		twitter_groupchat_init(ic);
 
-	gc = td->home_timeline_gc;
+	gc = td->timeline_gc;
 	if (!gc->joined)
 		imcb_chat_add_buddy(gc, ic->acc->user);
@@ -621,24 +620,28 @@
 
 		status = l->data;
-		if (status->user == NULL || status->text == NULL)
+		if (status->user == NULL || status->text == NULL || last_id == status->id)
 			continue;
 
-		twitter_add_buddy(ic, status->user->screen_name, status->user->name);
+		last_id = status->id;
 
 		strip_html(status->text);
+
 		msg = twitter_msg_add_id(ic, status, "");
 
 		// Say it!
-		if (g_strcasecmp(td->user, status->user->screen_name) == 0)
+		if (g_strcasecmp(td->user, status->user->screen_name) == 0) {
 			imcb_chat_log(gc, "You: %s", msg ? msg : status->text);
-		else
+		} else {
+			twitter_add_buddy(ic, status->user->screen_name, status->user->name);
+
 			imcb_chat_msg(gc, status->user->screen_name,
 				      msg ? msg : status->text, 0, status->created_at);
+		}
 
 		g_free(msg);
 
-		// Update the home_timeline_id to hold the highest id, so that by the next request
+		// Update the timeline_id to hold the highest id, so that by the next request
 		// we won't pick up the updates already in the list.
-		td->home_timeline_id = MAX(td->home_timeline_id, status->id);
+		td->timeline_id = MAX(td->timeline_id, status->id);
 	}
 }
@@ -654,4 +657,5 @@
 	char from[MAX_STRING];
 	gboolean mode_one;
+	guint64 last_id = 0;
 
 	mode_one = g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "one") == 0;
@@ -666,4 +670,8 @@
 
 		status = l->data;
+		if (status->user == NULL || status->text == NULL || last_id == status->id)
+			continue;
+
+		last_id = status->id;
 
 		strip_html(status->text);
@@ -680,10 +688,145 @@
 			       text ? text : status->text, 0, status->created_at);
 
-		// Update the home_timeline_id to hold the highest id, so that by the next request
+		// Update the timeline_id to hold the highest id, so that by the next request
 		// we won't pick up the updates already in the list.
-		td->home_timeline_id = MAX(td->home_timeline_id, status->id);
+		td->timeline_id = MAX(td->timeline_id, status->id);
 
 		g_free(text);
 		g_free(prefix);
+	}
+}
+
+static void twitter_http_get_home_timeline(struct http_request *req);
+static void twitter_http_get_mentions(struct http_request *req);
+
+/**
+ * Get the timeline with optionally mentions
+ */
+void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor)
+{
+	struct twitter_data *td = ic->proto_data;
+	gboolean include_mentions = set_getbool(&ic->acc->set, "fetch_mentions");
+
+	if (td->flags & TWITTER_DOING_TIMELINE) {
+		return;
+	}
+
+	td->flags |= TWITTER_DOING_TIMELINE;
+
+	twitter_get_home_timeline(ic, next_cursor);
+
+	if (include_mentions) {
+		twitter_get_mentions(ic, next_cursor);
+	}
+}
+
+/**
+ * Call this one after receiving timeline/mentions. Show to user once we have
+ * both.
+ */
+void twitter_flush_timeline(struct im_connection *ic)
+{
+	struct twitter_data *td = ic->proto_data;
+	gboolean include_mentions = set_getbool(&ic->acc->set, "fetch_mentions");
+	gboolean show_old_mentions = set_getbool(&ic->acc->set, "show_old_mentions");
+	struct twitter_xml_list *home_timeline = td->home_timeline_obj;
+	struct twitter_xml_list *mentions = td->mentions_obj;
+	GSList *output = NULL;
+	GSList *l;
+
+	if (!(td->flags & TWITTER_GOT_TIMELINE)) {
+		return;
+	}
+
+	if (include_mentions && !(td->flags & TWITTER_GOT_MENTIONS)) {
+		return;
+	}
+
+	if (home_timeline && home_timeline->list) {
+		for (l = home_timeline->list; l; l = g_slist_next(l)) {
+			output = g_slist_insert_sorted(output, l->data, twitter_compare_elements);
+		}
+	}
+
+	if (include_mentions && mentions && mentions->list) {
+		for (l = mentions->list; l; l = g_slist_next(l)) {
+			if (!show_old_mentions && output && twitter_compare_elements(l->data, output->data) < 0) {
+				continue;
+			}
+
+			output = g_slist_insert_sorted(output, l->data, twitter_compare_elements);
+		}
+	}
+
+	// See if the user wants to see the messages in a groupchat window or as private messages.
+	if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
+		twitter_groupchat(ic, output);
+	else
+		twitter_private_message_chat(ic, output);
+
+	g_slist_free(output);
+
+	if (home_timeline && home_timeline->list) {
+		txl_free(home_timeline);
+	}
+
+	if (mentions && mentions->list) {
+		txl_free(mentions);
+	}
+
+	td->flags &= ~(TWITTER_DOING_TIMELINE | TWITTER_GOT_TIMELINE | TWITTER_GOT_MENTIONS);
+}
+
+/**
+ * Get the timeline.
+ */
+void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)
+{
+	struct twitter_data *td = ic->proto_data;
+
+	td->home_timeline_obj = NULL;
+	td->flags &= ~TWITTER_GOT_TIMELINE;
+
+	char *args[4];
+	args[0] = "cursor";
+	args[1] = g_strdup_printf("%lld", (long long) next_cursor);
+	if (td->timeline_id) {
+		args[2] = "since_id";
+		args[3] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id);
+	}
+
+	twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args,
+		     td->timeline_id ? 4 : 2);
+
+	g_free(args[1]);
+	if (td->timeline_id) {
+		g_free(args[3]);
+	}
+}
+
+/**
+ * Get mentions.
+ */
+void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor)
+{
+	struct twitter_data *td = ic->proto_data;
+
+	td->mentions_obj = NULL;
+	td->flags &= ~TWITTER_GOT_MENTIONS;
+
+	char *args[4];
+	args[0] = "cursor";
+	args[1] = g_strdup_printf("%lld", (long long) next_cursor);
+	if (td->timeline_id) {
+		args[2] = "since_id";
+		args[3] = g_strdup_printf("%llu", (long long unsigned int) td->timeline_id);
+	}
+
+	twitter_http(ic, TWITTER_MENTIONS_URL, twitter_http_get_mentions, ic, 0, args,
+		     td->timeline_id ? 4 : 2);
+
+	g_free(args[1]);
+	if (td->timeline_id) {
+		g_free(args[3]);
 	}
 }
@@ -713,5 +856,5 @@
 		imcb_error(ic, "Authentication failure");
 		imc_logout(ic, FALSE);
-		return;
+		goto end;
 	} else {
 		// It didn't go well, output the error and return.
@@ -720,5 +863,5 @@
 				   TWITTER_HOME_TIMELINE_URL, twitter_parse_error(req));
 
-		return;
+		goto end;
 	}
 
@@ -733,13 +876,62 @@
 	xt_free(parser);
 
-	// See if the user wants to see the messages in a groupchat window or as private messages.
-	if (txl->list == NULL);
-	else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
-		twitter_groupchat(ic, txl->list);
-	else
-		twitter_private_message_chat(ic, txl->list);
-
-	// Free the structure.  
-	txl_free(txl);
+	td->home_timeline_obj = txl;
+
+      end:
+	td->flags |= TWITTER_GOT_TIMELINE;
+
+	twitter_flush_timeline(ic);
+}
+
+/**
+ * Callback for getting mentions.
+ */
+static void twitter_http_get_mentions(struct http_request *req)
+{
+	struct im_connection *ic = req->data;
+	struct twitter_data *td;
+	struct xt_parser *parser;
+	struct twitter_xml_list *txl;
+
+	// Check if the connection is still active.
+	if (!g_slist_find(twitter_connections, ic))
+		return;
+
+	td = ic->proto_data;
+
+	// Check if the HTTP request went well.
+	if (req->status_code == 200) {
+		td->http_fails = 0;
+		if (!(ic->flags & OPT_LOGGED_IN))
+			imcb_connected(ic);
+	} else if (req->status_code == 401) {
+		imcb_error(ic, "Authentication failure");
+		imc_logout(ic, FALSE);
+		goto end;
+	} else {
+		// It didn't go well, output the error and return.
+		if (++td->http_fails >= 5)
+			imcb_error(ic, "Could not retrieve " TWITTER_MENTIONS_URL ": %s",
+				   twitter_parse_error(req));
+
+		goto end;
+	}
+
+	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(ic, parser->root, txl);
+	xt_free(parser);
+
+	td->mentions_obj = txl;
+
+      end:
+	td->flags |= TWITTER_GOT_MENTIONS;
+
+	twitter_flush_timeline(ic);
 }
 
Index: protocols/twitter/twitter_lib.h
===================================================================
--- protocols/twitter/twitter_lib.h	(revision 4804b2f2e7270148ab303bf31351a5e2aa829761)
+++ protocols/twitter/twitter_lib.h	(revision 2322a9f38a2f6c9bf86eb62c2cd68fd3848b694f)
@@ -76,6 +76,8 @@
 #define TWITTER_BLOCKS_DESTROY_URL "/blocks/destroy/"
 
+void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor);
 void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor);
 void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor);
+void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor);
 void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor);
 
