changeset 9949:ced29c7b396c

[gaim-migrate @ 10845] (00:25:10) LSchiere: datallah: commit message? (00:25:40) datallah: LSchiere: nah.. nothing comes to me datallah undertook the heroic effort of merging in all the .rejs and fixing things up from the unclean merge of the status rewrite committer: Tailor Script <tailor@pidgin.im>
author Luke Schierer <lschiere@pidgin.im>
date Sat, 04 Sep 2004 04:27:05 +0000
parents b13013595c08
children ba085944efee
files src/Makefile.mingw src/blist.c src/blist.h src/gtkblist.c src/gtkdialogs.c src/gtkpounce.c src/gtkprefs.c src/idle.c src/prpl.c src/prpl.h src/server.c src/server.h src/status.c
diffstat 13 files changed, 1857 insertions(+), 451 deletions(-) [+]
line wrap: on
line diff
--- a/src/Makefile.mingw	Sat Sep 04 03:33:16 2004 +0000
+++ b/src/Makefile.mingw	Sat Sep 04 04:27:05 2004 +0000
@@ -201,7 +201,7 @@
 
 all: $(TARGET).exe $(TARGET).dll
 
-install:
+install: all
 	cp $(GAIM_SRC)/gaim.exe $(GAIM_SRC)/gaim.dll $(GAIM_INSTALL_DIR)
 
 $(IDLETRACK_TOP)/idletrack.dll:
--- a/src/blist.c	Sat Sep 04 03:33:16 2004 +0000
+++ b/src/blist.c	Sat Sep 04 04:27:05 2004 +0000
@@ -121,41 +121,48 @@
 	}
 }
 
-static void gaim_contact_compute_priority_buddy(GaimContact *contact)
+void gaim_contact_compute_priority_buddy(GaimContact *contact)
 {
 	GaimBlistNode *bnode;
-	int contact_score = INT_MAX;
+	GaimBuddy *new_priority = NULL;
 
 	g_return_if_fail(contact != NULL);
 
 	contact->priority = NULL;
-	for (bnode = ((GaimBlistNode*)contact)->child; bnode; bnode = bnode->next) {
+	for (bnode = ((GaimBlistNode*)contact)->child;
+			bnode != NULL;
+			bnode = bnode->next)
+	{
 		GaimBuddy *buddy;
-		int score = 0;
+		GaimPresence *presence;
 
 		if (!GAIM_BLIST_NODE_IS_BUDDY(bnode))
 			continue;
+
 		buddy = (GaimBuddy*)bnode;
+
 		if (!gaim_account_is_connected(buddy->account))
 			continue;
-
-		if (!GAIM_BUDDY_IS_ONLINE(buddy))
-			score += gaim_prefs_get_int("/core/contact/offline_score");
-		if (buddy->uc & UC_UNAVAILABLE)
-			score += gaim_prefs_get_int("/core/contact/away_score");
-		if (buddy->idle)
-			score += gaim_prefs_get_int("/core/contact/idle_score");
-
-		score += gaim_account_get_int(buddy->account, "score", 0);
-
-		if (score < contact_score) {
-			contact->priority = buddy;
-			contact_score = score;
-		}
-		if (gaim_prefs_get_bool("/core/contact/last_match"))
-			if (score == contact_score)
-				contact->priority = buddy;
+ 		if (new_priority == NULL)
+ 			new_priority = buddy;
+ 		else
+ 		{
+ 			int cmp;
+ 
+ 			presence = gaim_buddy_get_presence(buddy);
+ 
+ 			cmp = gaim_presence_compare(gaim_buddy_get_presence(new_priority),
+					gaim_buddy_get_presence(buddy));
+
+ 			if (cmp > 0 || (cmp == 0 &&
+						gaim_prefs_get_bool("/core/contact/last_match")))
+ 			{
+ 				new_priority = buddy;
+ 			}
+ 		}
 	}
+
+	contact->priority = new_priority;
 }
 
 static gboolean blist_save_callback(gpointer data)
@@ -236,6 +243,7 @@
 
 void gaim_blist_update_buddy_status(GaimBuddy *buddy, int status)
 {
+#if 0
 	GaimBlistUiOps *ops = gaimbuddylist->ui_ops;
 	int old_status;
 
@@ -256,6 +264,7 @@
 
 	if (ops && ops->update)
 		ops->update(gaimbuddylist, (GaimBlistNode*)buddy);
+#endif
 }
 
 static gboolean presence_update_timeout_cb(GaimBuddy *buddy)
@@ -340,6 +349,7 @@
 		ops->update(gaimbuddylist, (GaimBlistNode *)buddy);
 }
 
+#if 0
 void gaim_blist_update_buddy_idle(GaimBuddy *buddy, int idle)
 {
 	GaimBlistUiOps *ops = gaimbuddylist->ui_ops;
@@ -355,21 +365,7 @@
 	if (ops && ops->update)
 		ops->update(gaimbuddylist, (GaimBlistNode *)buddy);
 }
-
-void gaim_blist_update_buddy_evil(GaimBuddy *buddy, int warning)
-{
-	GaimBlistUiOps *ops = gaimbuddylist->ui_ops;
-
-	g_return_if_fail(buddy != NULL);
-
-	if (buddy->evil == warning)
-		return;
-
-	buddy->evil = warning;
-
-	if (ops && ops->update)
-		ops->update(gaimbuddylist, (GaimBlistNode *)buddy);
-}
+#endif
 
 void gaim_blist_update_buddy_icon(GaimBuddy *buddy)
 {
@@ -624,9 +620,11 @@
 	g_return_val_if_fail(screenname != NULL, FALSE);
 
 	buddy = g_new0(GaimBuddy, 1);
-	buddy->account = account;
-	buddy->name  = g_strdup(screenname);
-	buddy->alias = g_strdup(alias);
+	buddy->account  = account;
+	buddy->name     = g_strdup(screenname);
+	buddy->alias    = g_strdup(alias);
+	buddy->presence = gaim_presence_new_for_buddy(buddy);
+
 	gaim_blist_node_initialize_settings((GaimBlistNode *)buddy);
 	((GaimBlistNode *)buddy)->type = GAIM_BLIST_BUDDY_NODE;
 
@@ -1170,6 +1168,7 @@
 		for (node = gnode->child; node; node = node->next)
 			ops->update(gaimbuddylist, node);
 	}
+#endif
 }
 
 void gaim_blist_remove_contact(GaimContact *contact)
@@ -1624,6 +1623,13 @@
 	return (GaimContact*)((GaimBlistNode*)buddy)->parent;
 }
 
+GaimPresence *gaim_buddy_get_presence(const GaimBuddy *buddy)
+{
+	g_return_val_if_fail(buddy != NULL, NULL);
+ 	return buddy->presence;
+}
+
+
 GaimGroup *gaim_find_buddys_group(GaimBuddy *buddy)
 {
 	g_return_val_if_fail(buddy != NULL, NULL);
--- a/src/blist.h	Sat Sep 04 03:33:16 2004 +0000
+++ b/src/blist.h	Sat Sep 04 04:27:05 2004 +0000
@@ -112,14 +112,13 @@
 	char *server_alias;                     /**< The server-specified alias of the buddy.  (i.e. MSN "Friendly Names") */
 	GaimBuddyPresenceState present;         /**< This is 0 if the buddy appears offline, 1 if he appears online, and 2 if
 						    he has recently signed on */
-	int evil;                               /**< The warning level */
 	time_t signon;                          /**< The time the buddy signed on. */
-	int idle;                               /**< The time the buddy has been idle in minutes. */
 	int uc;                                 /**< This is a cryptic bitmask that makes sense only to the prpl.  This will get changed */
 	void *proto_data;                       /**< This allows the prpl to associate whatever data it wants with a buddy */
 	GaimBuddyIcon *icon;                    /**< The buddy icon. */
 	GaimAccount *account;           	/**< the account this buddy belongs to */
 	guint timer;				/**< The timer handle. */
+	GaimPresence *presence;
 };
 
 /**
@@ -192,6 +191,7 @@
 	void (*request_add_chat)(GaimAccount *account, GaimGroup *group, 
 							 const char *alias, const char *name);
 	void (*request_add_group)(void);
+	void (*status_changed)(GaimBuddy *buddy, GaimStatus *status);
 };
 
 
--- a/src/gtkblist.c	Sat Sep 04 03:33:16 2004 +0000
+++ b/src/gtkblist.c	Sat Sep 04 04:27:05 2004 +0000
@@ -102,7 +102,9 @@
 } GaimGtkJoinChatData;
 
 
-static GtkWidget *protomenu = NULL, *pluginmenu = NULL;
+static GtkWidget *protomenu = NULL;
+static GtkWidget *awaymenu = NULL;
+static GtkWidget *pluginmenu = NULL;
 
 GSList *gaim_gtk_blist_sort_methods = NULL;
 static struct gaim_gtk_blist_sort_method *current_sort_method = NULL;
@@ -2514,14 +2516,20 @@
 			}
 		}
 
+		presence = gaim_buddy_get_presence(b);
+
+		idle = gaim_presence_is_idle(presence);
+		idle_secs = gaim_presence_get_idle_time(presence);
+		warning_level = gaim_presence_get_warning_level(presence);
+
 		if (!statustext && !GAIM_BUDDY_IS_ONLINE(b))
 			statustext = g_strdup(_("\n<b>Status:</b> Offline"));
 
 		if (b->signon > 0)
 			loggedin = gaim_str_seconds_to_string(time(NULL) - b->signon);
 
-		if (b->idle > 0)
-			idletime = gaim_str_seconds_to_string(time(NULL) - b->idle);
+ 		if (idle && idle_secs > 0)
+ 			idletime = gaim_str_seconds_to_string(time(NULL) - idle_secs);
 
 		if(b->alias && b->alias[0])
 			aliastext = g_markup_escape_text(b->alias, -1);
@@ -2551,8 +2559,9 @@
 					aliastext ? _("\n<b>Alias:</b>") : "", aliastext ? aliastext : "",
 					nicktext ? _("\n<b>Nickname:</b>") : "", nicktext ? nicktext : "",
 					loggedin ? _("\n<b>Logged In:</b>") : "", loggedin ? loggedin : "",
-					idletime ? _("\n<b>Idle:</b>") : "", idletime ? idletime : "",
-					b->evil ? _("\n<b>Warned:</b>") : "", b->evil ? warning : "",
+					idle ? (idle_secs > 0 ? _("\n<b>Idle:</b>") : _("\n<b>Idle</b>")) : "",
+					idletime ? idletime : "",
+					warning_level ? _("\n<b>Warned:</b>") : "", warning_level ? warning : "",
 					statustext ? statustext : "",
 					!g_ascii_strcasecmp(b->name, "robflynn") ? _("\n<b>Description:</b> Spooky") :
 					!g_ascii_strcasecmp(b->name, "seanegn") ? _("\n<b>Status</b>: Awesome") :
--- a/src/gtkdialogs.c	Sat Sep 04 03:33:16 2004 +0000
+++ b/src/gtkdialogs.c	Sat Sep 04 04:27:05 2004 +0000
@@ -863,7 +863,6 @@
 
 	g_free(text);
 }
-#endif /* 0 */
 
 static void
 gaim_gtkdialogs_remove_chat_cb(GaimChat *chat)
--- a/src/gtkpounce.c	Sat Sep 04 03:33:16 2004 +0000
+++ b/src/gtkpounce.c	Sat Sep 04 04:27:05 2004 +0000
@@ -758,8 +758,9 @@
 			else
 			{
 				gboolean default_set = FALSE;
+				GaimPresence *presence = gaim_buddy_get_presence(buddy);
 
-				if (buddy->idle)
+				if (gaim_presence_is_idle(presence))
 				{
 					gtk_toggle_button_set_active(
 						GTK_TOGGLE_BUTTON(dialog->idle_return), TRUE);
@@ -767,7 +768,7 @@
 					default_set = TRUE;
 				}
 
-				if (buddy->uc & UC_UNAVAILABLE)
+				if (!gaim_presence_is_available(presence))
 				{
 					gtk_toggle_button_set_active(
 						GTK_TOGGLE_BUTTON(dialog->away_return), TRUE);
--- a/src/gtkprefs.c	Sat Sep 04 03:33:16 2004 +0000
+++ b/src/gtkprefs.c	Sat Sep 04 04:27:05 2004 +0000
@@ -638,7 +638,7 @@
 						gpointer		userdata)
 {
 	gint ret = 0;
-	gchar *name1, *name2;
+	gchar *name1 = NULL, *name2 = NULL;
 
 	gtk_tree_model_get(model, a, 3, &name1, -1);
 	gtk_tree_model_get(model, b, 3, &name2, -1);
@@ -2284,7 +2284,9 @@
 		return;
 	gtk_tree_model_get_value (GTK_TREE_MODEL(ls), &iter, 1, &val);
 	amt = g_value_get_pointer (&val);
+/* XXX CORE/UI
 	create_away_mess(NULL, amt);
+*/
 }
 
 static gboolean away_message_click_cb(GtkWidget *tv, GdkEventButton *event, gpointer null)
@@ -2299,7 +2301,9 @@
 }
 
 void remove_away_message(GtkWidget *widget, GtkTreeView *tv) {
+/* XXX CORE/UI
 	struct away_message *am;
+*/
 	GtkTreeIter iter;
 	GtkTreeSelection *sel = gtk_tree_view_get_selection(tv);
 	GtkTreeModel *model = GTK_TREE_MODEL(prefs_away_store);
@@ -2389,17 +2393,24 @@
 	button = gtk_button_new_from_stock (GTK_STOCK_ADD);
 	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
 	gtk_size_group_add_widget(sg, button);
+
+/* XXX CORE/UI
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(create_away_mess), NULL);
+*/
 
 	button = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
 	gtk_size_group_add_widget(sg, button);
+
+/* XXX CORE/UI
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(remove_away_message), event_view);
+*/
 
 	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
 
-	button = gaim_pixbuf_button_from_stock(_("_Edit"), GAIM_STOCK_EDIT, GAIM_BUTTON_HORIZONTAL);
+	button = gaim_pixbuf_button_from_stock(_("_Edit"), GAIM_STOCK_EDIT,
+			GAIM_BUTTON_HORIZONTAL);
 	gtk_size_group_add_widget(sg, button);
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(away_edit_sel), event_view);
--- a/src/idle.c	Sat Sep 04 03:33:16 2004 +0000
+++ b/src/idle.c	Sat Sep 04 04:27:05 2004 +0000
@@ -89,16 +89,22 @@
 
 	if (gaim_prefs_get_bool("/core/away/away_when_idle") &&
 		(idle_time > (60 * gaim_prefs_get_int("/core/away/mins_before_away")))
-		&& (!gc->is_auto_away)) {
+		&& (!gc->is_auto_away))
+	{
+		GaimPresence *presence;
+
+		presence = gaim_account_get_presence(account);
 
-		if (!gc->away || (!*gc->away && (!gc->away_state ||
-						!strcmp(gc->away_state, GAIM_AWAY_CUSTOM)))) {
+		if (gaim_presence_is_available(presence))
+		{
+/* XXX CORE/UI
 			struct away_message *default_away = NULL;
+			GSList *l;
+*/
 			const char *default_name;
-			GSList *l;
 
 			default_name = gaim_prefs_get_string("/core/away/default_message");
-
+/* XXX CORE/UI
 			for(l = away_messages; l; l = l->next) {
 				if(!strcmp(default_name, ((struct away_message *)l->data)->name)) {
 					default_away = l->data;
--- a/src/prpl.c	Sat Sep 04 03:33:16 2004 +0000
+++ b/src/prpl.c	Sat Sep 04 04:27:05 2004 +0000
@@ -28,6 +28,322 @@
 #include "request.h"
 #include "util.h"
 
+/**************************************************************************/
+/** @name Protocol Plugin API  */
+/**************************************************************************/
+void
+gaim_prpl_got_account_idle(GaimAccount *account, gboolean idle,
+						   time_t idle_time)
+{
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(gaim_account_is_connected(account));
+
+	gaim_presence_set_idle(gaim_account_get_presence(account),
+						   idle, idle_time);
+}
+
+void
+gaim_prpl_got_account_login_time(GaimAccount *account, const char *name,
+		time_t login_time)
+{
+	GaimPresence *presence;
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(name != NULL && *name != '\0');
+	g_return_if_fail(gaim_account_is_connected(account));
+
+	if (login_time == 0)
+		login_time = time(NULL);
+
+	presence = gaim_account_get_presence(account);
+
+	/*
+	 * TODO: Set a presence's sign-on time. We don't support this yet.
+	 */
+	gaim_debug_warning("prpl",
+					   "Attempting to set an account's sign-on time, but we "
+					   "don't support this yet! FIX IT!\n");
+}
+
+static gboolean
+set_value_from_arg(GaimStatus *status, const char *id, va_list *args)
+{
+	GaimValue *value;
+
+	value = gaim_status_get_attr_value(status, id);
+
+	if (value == NULL)
+	{
+		gaim_debug_error("prpl",
+						 "Attempted to set an unknown attribute %s on "
+						 "status %s\n",
+						 id, gaim_status_get_id(status));
+		return FALSE;
+	}
+
+	switch (gaim_value_get_type(value))
+	{
+		case GAIM_TYPE_CHAR:
+			gaim_value_set_char(value, (char)va_arg(*args, int));
+			break;
+
+		case GAIM_TYPE_UCHAR:
+			gaim_value_set_uchar(value,
+								 (unsigned char)va_arg(*args, unsigned int));
+			break;
+
+		case GAIM_TYPE_BOOLEAN:
+			gaim_value_set_boolean(value, va_arg(*args, gboolean));
+			break;
+
+		case GAIM_TYPE_SHORT:
+			gaim_value_set_short(value, (short)va_arg(*args, int));
+			break;
+
+		case GAIM_TYPE_USHORT:
+			gaim_value_set_ushort(value,
+					(unsigned short)va_arg(*args, unsigned int));
+			break;
+
+		case GAIM_TYPE_INT:
+			gaim_value_set_int(value, va_arg(*args, int));
+			break;
+
+		case GAIM_TYPE_UINT:
+			gaim_value_set_uint(value, va_arg(*args, unsigned int));
+			break;
+
+		case GAIM_TYPE_LONG:
+			gaim_value_set_long(value, va_arg(*args, long));
+			break;
+
+		case GAIM_TYPE_ULONG:
+			gaim_value_set_ulong(value, va_arg(*args, unsigned long));
+			break;
+
+		case GAIM_TYPE_INT64:
+			gaim_value_set_int64(value, va_arg(*args, gint64));
+			break;
+
+		case GAIM_TYPE_UINT64:
+			gaim_value_set_uint64(value, va_arg(*args, guint64));
+			break;
+
+		case GAIM_TYPE_STRING:
+			gaim_value_set_string(value, va_arg(*args, char *));
+			break;
+
+		case GAIM_TYPE_OBJECT:
+			gaim_value_set_object(value, va_arg(*args, void *));
+			break;
+
+		case GAIM_TYPE_POINTER:
+			gaim_value_set_pointer(value, va_arg(*args, void *));
+			break;
+
+		case GAIM_TYPE_ENUM:
+			gaim_value_set_enum(value, va_arg(*args, int));
+			break;
+
+		case GAIM_TYPE_BOXED:
+			gaim_value_set_boxed(value, va_arg(*args, void *));
+			break;
+
+		default:
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+void
+gaim_prpl_got_account_status(GaimAccount *account, const char *status_id,
+		const char *attr_id, ...)
+{
+	GaimPresence *presence;
+	GaimStatus *status;
+
+	g_return_if_fail(account   != NULL);
+	g_return_if_fail(status_id != NULL);
+	g_return_if_fail(gaim_account_is_connected(account));
+
+	presence = gaim_account_get_presence(account);
+	status   = gaim_presence_get_status(presence, status_id);
+
+	g_return_if_fail(status != NULL);
+
+	if (attr_id != NULL)
+	{
+		va_list args;
+
+		va_start(args, attr_id);
+
+		while (attr_id != NULL)
+		{
+			set_value_from_arg(status, attr_id, &args);
+
+			attr_id = va_arg(args, char *);
+		}
+
+		va_end(args);
+	}
+
+	gaim_presence_set_status_active(presence, status_id, TRUE);
+}
+
+void
+gaim_prpl_got_account_warning_level(GaimAccount *account, const char *username,
+		unsigned int level)
+{
+	GaimPresence *presence;
+	unsigned int old_level;
+	char buf2[1024];
+
+	g_return_if_fail(account != NULL);
+
+	presence = gaim_account_get_presence(account);
+
+	gaim_signal_emit(gaim_accounts_get_handle(), "account-warned",
+					 account, username, level);
+
+	old_level = gaim_presence_get_warning_level(presence);
+	gaim_presence_set_warning_level(presence, level);
+
+	if (old_level >= level)
+		return;
+
+	g_snprintf(buf2, sizeof(buf2),
+			   _("%s has just been warned by %s.\n"
+				 "Your new warning level is %d%%"),
+			   gaim_account_get_username(account),
+			   (username == NULL ? _("an anonymous person") : username),
+			   level);
+
+	gaim_notify_info(NULL, NULL, buf2, NULL);
+}
+
+void
+gaim_prpl_got_user_idle(GaimAccount *account, const char *name,
+		gboolean idle, time_t idle_time)
+{
+	GaimBuddy *buddy;
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(name    != NULL);
+	g_return_if_fail(gaim_account_is_connected(account));
+
+	if ((buddy = gaim_find_buddy(account, name)) == NULL)
+		return;
+
+	gaim_presence_set_idle(gaim_buddy_get_presence(buddy), idle, idle_time);
+}
+
+void
+gaim_prpl_got_user_login_time(GaimAccount *account, const char *name,
+		time_t login_time)
+{
+	GaimBuddy *buddy;
+	GaimPresence *presence;
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(name    != NULL);
+
+	if ((buddy = gaim_find_buddy(account, name)) == NULL)
+		return;
+
+	if (login_time == 0)
+		login_time = time(NULL);
+
+	presence = gaim_buddy_get_presence(buddy);
+
+	/*
+	 * TODO: Set a presence's sign-on time. We don't support this yet.
+	 */
+	gaim_debug_warning("prpl",
+					   "Attempting to set a user's sign-on time, but we "
+					   "don't support this yet! FIX IT!\n");
+}
+
+void
+gaim_prpl_got_user_status(GaimAccount *account, const char *name,
+		const char *status_id, const char *attr_id, ...)
+{
+	GaimBuddy *buddy;
+	GaimPresence *presence;
+	GaimStatus *status;
+
+	g_return_if_fail(account   != NULL);
+	g_return_if_fail(name      != NULL);
+	g_return_if_fail(status_id != NULL);
+	g_return_if_fail(gaim_account_is_connected(account));
+
+	if ((buddy = gaim_find_buddy(account, name)) == NULL)
+		return;
+
+	presence = gaim_buddy_get_presence(buddy);
+	status   = gaim_presence_get_status(presence, status_id);
+
+	g_return_if_fail(status != NULL);
+
+	if (attr_id != NULL)
+	{
+		va_list args;
+
+		va_start(args, attr_id);
+
+		while (attr_id != NULL)
+		{
+			set_value_from_arg(status, attr_id, &args);
+
+			attr_id = va_arg(args, char *);
+		}
+
+		va_end(args);
+	}
+
+	gaim_presence_set_status_active(presence, status_id, TRUE);
+}
+
+void
+gaim_prpl_got_user_warning_level(GaimAccount *account, const char *name,
+		unsigned int level)
+{
+	GaimBuddy *buddy;
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(name    != NULL);
+
+	if ((buddy = gaim_find_buddy(account, name)) == NULL)
+		return;
+
+	gaim_presence_set_warning_level(gaim_buddy_get_presence(buddy), level);
+}
+
+void
+gaim_prpl_set_account_status(GaimAccount *account, GaimStatus *status)
+{
+	GaimPlugin *prpl;
+	GaimPluginProtocolInfo *prpl_info;
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(status  != NULL);
+
+	prpl = gaim_find_prpl(gaim_account_get_protocol_id(account));
+
+	if (prpl == NULL)
+		return;
+
+	prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl);
+
+	if (prpl_info->set_status != NULL)
+		prpl_info->set_status(account, status);
+}
+
+
+/**************************************************************************
+ * Protocol Plugin Subsystem API
+ **************************************************************************/
+
 GaimPlugin *
 gaim_find_prpl(const char *id)
 {
--- a/src/prpl.h	Sat Sep 04 03:33:16 2004 +0000
+++ b/src/prpl.h	Sat Sep 04 04:27:05 2004 +0000
@@ -220,7 +220,7 @@
 	 */
 	char *(*tooltip_text)(GaimBuddy *buddy);
 
-	GList *(*away_states)(GaimConnection *gc);
+	GList *(*status_types)(GaimConnection *gc);
 
 	GList *(*blist_node_menu)(GaimBlistNode *node);
 	GList *(*chat_info)(GaimConnection *);
@@ -235,7 +235,8 @@
 	void (*set_info)(GaimConnection *, const char *info);
 	int  (*send_typing)(GaimConnection *, const char *name, int typing);
 	void (*get_info)(GaimConnection *, const char *who);
-	void (*set_away)(GaimConnection *, const char *state, const char *message);
+	void (*set_status)(GaimAccount *account, GaimStatus *status);
+
 	void (*set_idle)(GaimConnection *, int idletime);
 	void (*change_passwd)(GaimConnection *, const char *old_pass,
 						  const char *new_pass);
--- a/src/server.c	Sat Sep 04 03:33:16 2004 +0000
+++ b/src/server.c	Sat Sep 04 04:27:05 2004 +0000
@@ -261,15 +261,20 @@
 int serv_send_im(GaimConnection *gc, const char *name, const char *message,
 				 GaimConvImFlags imflags)
 {
-	GaimConversation *c;
+	GaimConversation *conv;
+	GaimAccount *account;
+	GaimPresence *presence;
+	GaimPluginProtocolInfo *prpl_info = NULL;
 	int val = -EINVAL;
-	GaimPluginProtocolInfo *prpl_info = NULL;
 	const gchar *auto_reply_pref;
 
 	if (gc != NULL && gc->prpl != NULL)
 		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
 
-	c = gaim_find_conversation_with_account(name, gc->account);
+	account  = gaim_connection_get_account(gc);
+	presence = gaim_account_get_presence(account);
+
+	conv = gaim_find_conversation_with_account(name, gc->account);
 
 	if (prpl_info && prpl_info->send_im)
 		val = prpl_info->send_im(gc, name, message, imflags);
@@ -282,16 +287,17 @@
 	 * this only reset lar->sent if we're away AND idle?
 	 */
 	auto_reply_pref = gaim_prefs_get_string("/core/away/auto_reply");
-	if (gc->away &&	(gc->flags & GAIM_CONNECTION_AUTO_RESP) &&
-		strcmp(auto_reply_pref, "never")) {
+	if ((gc->flags & GAIM_CONNECTION_AUTO_RESP) &&
+			!gaim_presence_is_available(presence) &&
+			strcmp(auto_reply_pref, "never")) {
 
 		struct last_auto_response *lar;
 		lar = get_last_auto_response(gc, name);
 		lar->sent = time(NULL);
 	}
 
-	if (c && gaim_conv_im_get_type_again_timeout(GAIM_CONV_IM(c)))
-		gaim_conv_im_stop_type_again_timeout(GAIM_CONV_IM(c));
+	if (conv && gaim_conv_im_get_type_again_timeout(GAIM_CONV_IM(conv)))
+		gaim_conv_im_stop_type_again_timeout(GAIM_CONV_IM(conv));
 
 	return val;
 }
@@ -307,63 +313,6 @@
 		prpl_info->get_info(gc, name);
 }
 
-void serv_set_away(GaimConnection *gc, const char *state, const char *message)
-{
-	GaimPluginProtocolInfo *prpl_info = NULL;
-	GaimAccount *account;
-
-	if (gc->away_state == NULL && state == NULL &&
-		gc->away == NULL && message == NULL) {
-
-		return;
-	}
-
-	if ((gc->away_state != NULL && state != NULL &&
-		 !strcmp(gc->away_state, state) &&
-		 !strcmp(gc->away_state, GAIM_AWAY_CUSTOM)) &&
-		(gc->away != NULL && message != NULL && !strcmp(gc->away, message))) {
-
-		return;
-	}
-
-	if (gc != NULL && gc->prpl != NULL)
-		prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
-
-	account = gaim_connection_get_account(gc);
-
-	if (prpl_info && prpl_info->set_away) {
-		if (gc->away_state) {
-			g_free(gc->away_state);
-			gc->away_state = NULL;
-		}
-
-		prpl_info->set_away(gc, state, message);
-
-		if (gc->away && state) {
-			gc->away_state = g_strdup(state);
-		}
-
-		gaim_signal_emit(gaim_accounts_get_handle(), "account-away",
-						 account, state, message);
-	}
-
-	/* LOG system_log(log_away, gc, NULL, OPT_LOG_BUDDY_AWAY | OPT_LOG_MY_SIGNON); */
-	/* New away message... Clear out the record of sent autoresponses */
-	flush_last_auto_responses(gc);
-}
-
-void serv_set_away_all(const char *message)
-{
-	GList *c;
-	GaimConnection *g;
-
-	for (c = gaim_connections_get_all(); c != NULL; c = c->next) {
-		g = (GaimConnection *)c->data;
-
-		serv_set_away(g, GAIM_AWAY_CUSTOM, message);
-	}
-}
-
 void serv_set_info(GaimConnection *gc, const char *info)
 {
 	GaimPluginProtocolInfo *prpl_info = NULL;
@@ -447,7 +396,7 @@
 {
 	GaimPluginProtocolInfo *prpl_info = NULL;
 
-	if (buddy->idle > 0)
+	if (gaim_presence_is_idle(gaim_buddy_get_presence(buddy)))
 		remove_idle_buddy(buddy);
 
 	if (gc != NULL && gc->prpl != NULL)
@@ -472,7 +421,7 @@
 			GList *curb;
 			for (curb = buddies; curb != NULL; curb = curb->next) {
 				GaimBuddy *buddy = curb->data;
-				if (buddy->idle > 0)
+				if (gaim_presence_is_idle(gaim_buddy_get_presence(buddy)))
 					remove_idle_buddy(buddy);
 			}
 			prpl_info->remove_buddies(gc, buddies, groups);
@@ -908,14 +857,18 @@
 	 * are three or four different ways of handling it and different
 	 * things we have to do for each.
 	 */
-	if (gc->away) {
+	if (!gaim_presence_is_available(presence))
+	{
 		time_t t = time(NULL);
 		char *tmpmsg;
 		GaimBuddy *b = gaim_find_buddy(gc->account, name);
 		const char *alias = b ? gaim_buddy_get_alias(b) : name;
-		int row;
 		struct last_auto_response *lar;
 		const gchar *auto_reply_pref;
+		const char *away_msg;
+#if 0
+		int row;
+#endif
 
 		/*
 		 * Either we're going to queue it or not. Because of the way
@@ -1001,11 +954,15 @@
 		 *    is set
 		 */
 		auto_reply_pref = gaim_prefs_get_string("/core/away/auto_reply");
+		status = gaim_presence_get_active_status(presence);
+		away_msg = gaim_value_get_string(
+			gaim_status_get_attr_value(status, "message"));
+		
 		if (!(gc->flags & GAIM_CONNECTION_AUTO_RESP) ||
-			*gc->away == '\0' ||
-			!strcmp(auto_reply_pref, "never") ||
-			(!gc->is_idle &&
-			 !strcmp(auto_reply_pref, "awayidle"))) {
+				away_msg == NULL || *away_msg == '\0' ||
+				!strcmp(auto_reply_pref, "never") ||
+				(!gaim_presence_is_idle(presence) &&
+				!strcmp(auto_reply_pref, "awayidle"))) {
 
 			g_free(name);
 			g_free(message);
@@ -1128,7 +1085,8 @@
 
 		l_next = l->next;
 
-		if (!GAIM_BUDDY_IS_ONLINE(buddy) || buddy->idle <= 0)
+		if (!GAIM_BUDDY_IS_ONLINE(buddy) ||
+				gaim_presence_is_idle(gaim_buddy_get_presence(buddy)))
 		{
 			remove_idle_buddy(buddy);
 		}
@@ -1171,14 +1129,14 @@
  *             since the epoch.
  */
 void serv_got_update(GaimConnection *gc, const char *name, gboolean loggedin,
-					 int evil, time_t signon, time_t idle, int type)
+					 time_t signon)
 {
+	GaimPresence *presence;
 	GaimAccount *account;
 	GaimConversation *c;
 	GaimBuddy *b;
 	char *alias;
 	GSList *buddies;
-	int old_idle;
 	time_t current_time = time(NULL);
 	int signing_on = 0;
 	int signing_off = 0;
@@ -1221,6 +1179,7 @@
 									time(NULL));
 			g_free(tmp);
 		}
+#if 0
 		else if (awayqueue && find_queue_total_by_name(b->name)) {
 			struct queued_message *qm = g_new0(struct queued_message, 1);
 			g_snprintf(qm->name, sizeof(qm->name), "%s", b->name);
@@ -1230,6 +1189,7 @@
 			qm->flags = GAIM_MESSAGE_SYSTEM;
 			message_queue = g_slist_append(message_queue, qm);
 		}
+#endif
 		gaim_sound_play_event(GAIM_SOUND_BUDDY_ARRIVE);
 
 		if(gaim_prefs_get_bool("/core/logging/log_system") &&
@@ -1244,55 +1204,15 @@
 		}
 	}
 
-	if(gaim_prefs_get_bool("/core/logging/log_system") &&
-	   gaim_prefs_get_bool("/core/logging/log_away_state")) {
-		GaimAccount *account = gaim_connection_get_account(gc);
-		GaimLog *log = gaim_account_get_log(account);
-		char *tmp = NULL;
-
-		if((b->uc & UC_UNAVAILABLE) && !(type & UC_UNAVAILABLE))
-			tmp = g_strdup_printf(_("%s came back"), alias);
-		else if(!(b->uc & UC_UNAVAILABLE) && (type & UC_UNAVAILABLE))
-			tmp = g_strdup_printf(_("%s went away"), alias);
-
-		if(tmp){
-			gaim_log_write(log, GAIM_MESSAGE_SYSTEM, (alias ? alias : name),
-						   current_time, tmp);
-			g_free(tmp);
-		}
-	}
-
-	if (!old_idle && idle) {
-		if(gaim_prefs_get_bool("/core/logging/log_system") &&
-		   gaim_prefs_get_bool("/core/logging/log_idle_state")) {
-			GaimAccount *account = gaim_connection_get_account(gc);
-			GaimLog *log = gaim_account_get_log(account);
-			char *tmp = g_strdup_printf(_("%s became idle"), alias);
-
-			gaim_log_write(log, GAIM_MESSAGE_SYSTEM, (alias ? alias : name),
-						   current_time, tmp);
-			g_free(tmp);
-		}
-	} else if (old_idle && !idle) {
-		if(gaim_prefs_get_bool("/core/logging/log_system") &&
-		   gaim_prefs_get_bool("/core/logging/log_idle_state")) {
-			GaimAccount *account = gaim_connection_get_account(gc);
-			GaimLog *log = gaim_account_get_log(account);
-			char *tmp = g_strdup_printf(_("%s became unidle"), alias);
-
-			gaim_log_write(log, GAIM_MESSAGE_SYSTEM, (alias ? alias : name),
-						   current_time, tmp);
-			g_free(tmp);
-		}
-	}
-
 	if (signing_off) {
 		if (c != NULL) {
 			char *tmp = g_strdup_printf(_("%s logged out."), alias);
 			gaim_conversation_write(c, NULL, tmp,
 									GAIM_MESSAGE_SYSTEM, time(NULL));
 			g_free(tmp);
-		} else if (awayqueue && find_queue_total_by_name(b->name)) {
+		}
+#if 0
+		else if (awayqueue && find_queue_total_by_name(b->name)) {
 			struct queued_message *qm = g_new0(struct queued_message, 1);
 			g_snprintf(qm->name, sizeof(qm->name), "%s", b->name);
 			qm->message = g_strdup_printf(_("%s logged out."), alias);
@@ -1301,6 +1221,7 @@
 			qm->flags = GAIM_MESSAGE_SYSTEM;
 			message_queue = g_slist_append(message_queue, qm);
 		}
+#endif
 		serv_got_typing_stopped(gc, name); /* obviously not typing */
 		gaim_sound_play_event(GAIM_SOUND_BUDDY_LEAVE);
 
@@ -1318,23 +1239,8 @@
 
 	if (gc->login_time_official && gc->login_time && signon > 0)
 		signon += gc->login_time_official - gc->login_time;
+
 	gaim_blist_update_buddy_signon(b, signon);
-	gaim_blist_update_buddy_idle(b, idle);
-	gaim_blist_update_buddy_evil(b, evil);
-	gaim_blist_update_buddy_status(b, type);
-
-	if (!old_idle && idle)
-	{
-		gaim_signal_emit(gaim_blist_get_handle(), "buddy-idle", b);
-
-		add_idle_buddy(b);
-	}
-	else if (old_idle && !idle)
-	{
-		gaim_signal_emit(gaim_blist_get_handle(), "buddy-unidle", b);
-
-		remove_idle_buddy(b);
-	}
 
 	if (c != NULL)
 		gaim_conversation_update(c, GAIM_CONV_UPDATE_AWAY);
@@ -1344,40 +1250,10 @@
 	for (buddies = gaim_find_buddies(account, name); buddies; buddies = g_slist_remove(buddies, buddies->data)) {
 		b = buddies->data;
 		gaim_blist_update_buddy_presence(b, loggedin);
-		gaim_blist_update_buddy_idle(b, idle);
-		gaim_blist_update_buddy_evil(b, evil);
-		gaim_blist_update_buddy_status(b, type);
 	}
 	g_free(alias);
 }
 
-
-void serv_got_eviled(GaimConnection *gc, const char *name, int lev)
-{
-	char buf2[1024];
-	GaimAccount *account;
-
-	account = gaim_connection_get_account(gc);
-
-	gaim_signal_emit(gaim_accounts_get_handle(), "account-warned",
-					 account, name, lev);
-
-	if (gc->evil >= lev) {
-		gc->evil = lev;
-		return;
-	}
-
-	gc->evil = lev;
-
-	g_snprintf(buf2, sizeof(buf2),
-			   _("%s has just been warned by %s.\n"
-				 "Your new warning level is %d%%"),
-			   gaim_account_get_username(gaim_connection_get_account(gc)),
-			   ((name == NULL) ? _("an anonymous person") : name), lev);
-
-	gaim_notify_info(NULL, NULL, buf2, NULL);
-}
-
 void serv_got_typing(GaimConnection *gc, const char *name, int timeout,
 					 GaimTypingState state) {
 
--- a/src/server.h	Sat Sep 04 03:33:16 2004 +0000
+++ b/src/server.h	Sat Sep 04 04:27:05 2004 +0000
@@ -25,12 +25,6 @@
 #ifndef _GAIM_SERVER_H_
 #define _GAIM_SERVER_H_
 
-/*
- * Really user states are controlled by the PRPLs now. We just
- * use this for event_away
- */
-#define UC_UNAVAILABLE  1
-
 #include "account.h"
 #include "conversation.h"
 #include "prpl.h"
@@ -47,8 +41,6 @@
 void serv_get_dir(GaimConnection *, const char *);
 void serv_set_idle(GaimConnection *, int);
 void serv_set_info(GaimConnection *, const char *);
-void serv_set_away(GaimConnection *, const char *, const char *);
-void serv_set_away_all(const char *);
 int  serv_send_typing(GaimConnection *, const char *, int);
 void serv_change_passwd(GaimConnection *, const char *, const char *);
 void serv_add_buddy(GaimConnection *, GaimBuddy *);
@@ -78,7 +70,6 @@
 int  serv_chat_send(GaimConnection *, int, const char *);
 void serv_alias_buddy(GaimBuddy *);
 void serv_got_alias(GaimConnection *gc, const char *who, const char *alias);
-void serv_got_eviled(GaimConnection *gc, const char *name, int lev);
 void serv_got_typing(GaimConnection *gc, const char *name, int timeout,
 					 GaimTypingState state);
 void serv_set_buddyicon(GaimConnection *gc, const char *filename);
@@ -86,7 +77,7 @@
 void serv_got_im(GaimConnection *gc, const char *who, const char *msg,
 				 GaimConvImFlags imflags, time_t mtime);
 void serv_got_update(GaimConnection *gc, const char *name, gboolean loggedin,
-					 int evil, time_t signon, time_t idle, int type);
+					 time_t signon);
 void serv_finish_login(GaimConnection *gc);
 void serv_got_chat_invite(GaimConnection *gc, const char *name,
 						  const char *who, const char *message,
--- a/src/status.c	Sat Sep 04 03:33:16 2004 +0000
+++ b/src/status.c	Sat Sep 04 04:27:05 2004 +0000
@@ -22,290 +22,1480 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
+#include "internal.h"
 
+#include "blist.h"
+#include "debug.h"
+#include "prefs.h"
 #include "status.h"
-#include "internal.h"
-#include "debug.h"
 #include "util.h"
 
-/* XXX CORE/UI */
-#include "away.h"
-#include "gtkgaim.h"
+/**
+ * A type of status.
+ */
+struct _GaimStatusType
+{
+	GaimStatusPrimitive primitive;
 
+	char *id;
+	char *name;
+	char *primary_attr_id;
+
+	gboolean saveable;
+	gboolean user_settable;
+	gboolean independent;
+
+	GList *attrs;
+};
 
-/* for people like myself who are too lazy to add an away msg :) */
-/* I don't know who "myself" is in this context.  The exclamation point
- * makes it slightly less boring ;) */
-#define BORING_DEFAULT_AWAY_MSG _("Sorry, I ran out for a bit!")
+/**
+ * A status attribute.
+ */
+struct _GaimStatusAttr
+{
+	char *id;
+	char *name;
+	GaimValue *value_type;
+};
 
-/* XML File Saving */
+/**
+ * A list of statuses.
+ */
+struct _GaimPresence
+{
+	GaimPresenceContext context;
+
+	gboolean idle;
+	time_t idle_time;
+
+	unsigned int warning_level;
 
-/* All of this code is adapted from Nathan Walp's.  It's adapted all over the place
- * for accounts, the buddy list, pounces, preferences, and the likes.  It would be
- * neat if we could somehow make this more generic. */
-static gboolean status_loaded = FALSE;
-static guint    status_save_timer = 0;
+	GList *statuses;
+	GHashTable *status_table;
+
+	GaimStatus *active_status;
 
+	union
+	{
+		GaimAccount *account;
+
+		struct
+		{
+			GaimConversation *conv;
+			char *user;
+
+		} chat;
 
-typedef enum
+		struct
+		{
+			GaimAccount *account;
+			char *name;
+			size_t ref_count;
+			GList *buddies;
+
+		} buddy;
+
+	} u;
+};
+
+/**
+ * An active status.
+ */
+struct _GaimStatus
 {
-	TAG_NONE = 0,
-	TAG_STATUS,
-	TAG_STATE,
-	TAG_MESSAGE,
+	GaimStatusType *type;
+	GaimPresence *presence;
+
+	const char *title;
 
-} StatusParserTag;
+	gboolean active;
 
+	GHashTable *attr_values;
+};
 
 typedef struct
 {
-	StatusParserTag tag;
-	GString *buffer;
-	struct away_message *am;
+	GaimAccount *account;
+	char *name;
+
+} GaimStatusBuddyKey;
+
+
+#if 0
+static GList *stored_statuses = NULL;
+
+/*
+ * XXX This stuff should be removed in a few versions. It stores the
+ *     old v1 status stuff so we can write it later. We don't write out
+ *     the new status stuff, though. These should all die soon, as the
+ *     old status.xml was created before the new status system's design
+ *     was created.
+ *
+ *       -- ChipX86
+ */
+typedef struct
+{
+	char *name;
+	char *state;
+	char *message;
+
+} GaimStatusV1Info;
+
+static GList *v1_statuses = NULL;
+#endif
+
+
+static int primitive_scores[] =
+{
+	0,      /* unset                    */
+	-500,   /* offline                  */
+	0,      /* online                   */
+	100,    /* available                */
+	-75,    /* unavailable              */
+	-50,    /* hidden                   */
+	-100,   /* away                     */
+	-200    /* extended away            */
+	-10,    /* idle, special case.      */
+	-5      /* idle time, special case. */
+};
+
+static GHashTable *buddy_presences = NULL;
+
+#define SCORE_IDLE      5
+#define SCORE_IDLE_TIME 6
+
+/**************************************************************************
+ * GaimStatusType API
+ **************************************************************************/
+GaimStatusType *
+gaim_status_type_new_full(GaimStatusPrimitive primitive, const char *id,
+		const char *name, gboolean saveable,
+		gboolean user_settable, gboolean independent)
+{
+	GaimStatusType *status_type;
+
+	g_return_val_if_fail(primitive != GAIM_STATUS_UNSET, NULL);
+	g_return_val_if_fail(id        != NULL,              NULL);
+	g_return_val_if_fail(name      != NULL,              NULL);
+
+	status_type = g_new0(GaimStatusType, 1);
+
+	status_type->primitive     = primitive;
+	status_type->id            = g_strdup(id);
+	status_type->name          = g_strdup(name);
+	status_type->saveable      = saveable;
+	status_type->user_settable = user_settable;
+	status_type->independent   = independent;
+
+	return status_type;
+}
+
+GaimStatusType *
+gaim_status_type_new(GaimStatusPrimitive primitive, const char *id,
+		const char *name, gboolean user_settable)
+{
+	g_return_val_if_fail(primitive != GAIM_STATUS_UNSET, NULL);
+	g_return_val_if_fail(id        != NULL,              NULL);
+	g_return_val_if_fail(name      != NULL,              NULL);
+
+	return gaim_status_type_new_full(primitive, id, name, FALSE,
+			user_settable, FALSE);
+}
+
+GaimStatusType *
+gaim_status_type_new_with_attrs(GaimStatusPrimitive primitive,
+		const char *id, const char *name,
+		gboolean saveable, gboolean user_settable,
+		gboolean independent, const char *attr_id,
+		const char *attr_name, GaimValue *attr_value,
+		...)
+{
+	GaimStatusType *status_type;
+	va_list args;
+
+	g_return_val_if_fail(primitive  != GAIM_STATUS_UNSET, NULL);
+	g_return_val_if_fail(id         != NULL,              NULL);
+	g_return_val_if_fail(name       != NULL,              NULL);
+	g_return_val_if_fail(attr_name  != NULL,              NULL);
+	g_return_val_if_fail(attr_value != NULL,              NULL);
+
+	status_type = gaim_status_type_new_full(primitive, id, name, saveable,
+			user_settable, independent);
+
+	gaim_status_type_add_attr(status_type, attr_id, attr_name, attr_value);
+
+	va_start(args, attr_value);
+	gaim_status_type_add_attrs_vargs(status_type, args);
+	va_end(args);
+
+	return status_type;
+}
+
+void
+gaim_status_type_destroy(GaimStatusType *status_type)
+{
+	GList *l;
+
+	g_return_if_fail(status_type != NULL);
+
+	g_free(status_type->id);
+	g_free(status_type->name);
+
+	if (status_type->primary_attr_id != NULL)
+		g_free(status_type->primary_attr_id);
+
+	if (status_type->attrs != NULL)
+	{
+		for (l = status_type->attrs; l != NULL; l = l->next)
+			gaim_status_attr_destroy((GaimStatusAttr *)l->data);
+
+		g_list_free(status_type->attrs);
+	}
+
+	g_free(status_type);
+}
+
+void
+gaim_status_type_set_primary_attr(GaimStatusType *status_type, const char *id)
+{
+	g_return_if_fail(status_type != NULL);
+
+	if (status_type->primary_attr_id != NULL)
+		g_free(status_type->primary_attr_id);
+
+	status_type->primary_attr_id = (id == NULL ? NULL : g_strdup(id));
+}
+
+void
+gaim_status_type_add_attr(GaimStatusType *status_type, const char *id,\
+		const char *name, GaimValue *value)
+{
+	GaimStatusAttr *attr;
+
+	g_return_if_fail(status_type != NULL);
+	g_return_if_fail(id          != NULL);
+	g_return_if_fail(name        != NULL);
+	g_return_if_fail(value       != NULL);
+
+	attr = gaim_status_attr_new(id, name, value);
+
+	status_type->attrs = g_list_append(status_type->attrs, attr);
+}
+
+void
+gaim_status_type_add_attrs(GaimStatusType *status_type, const char *id,
+		const char *name, GaimValue *value, ...)
+{
+	va_list args;
+
+	g_return_if_fail(status_type != NULL);
+	g_return_if_fail(id          != NULL);
+	g_return_if_fail(name        != NULL);
+	g_return_if_fail(value       != NULL);
+
+	/* Add the first. */
+	gaim_status_type_add_attr(status_type, id, name, value);
+
+	va_start(args, value);
+	gaim_status_type_add_attrs_vargs(status_type, args);
+	va_end(args);
+}
+
+void
+gaim_status_type_add_attrs_vargs(GaimStatusType *status_type, va_list args)
+{
+	const char *id, *name;
+	GaimValue *value;
+
+	g_return_if_fail(status_type != NULL);
+
+	while ((id = va_arg(args, const char *)) != NULL)
+	{
+		name = va_arg(args, const char *);
+		g_return_if_fail(name != NULL);
+
+		value = va_arg(args, GaimValue *);
+		g_return_if_fail(value != NULL);
 
-} StatusParserData;
+		gaim_status_type_add_attr(status_type, id, name, value);
+	}
+}
+
+GaimStatusPrimitive
+gaim_status_type_get_primitive(const GaimStatusType *status_type)
+{
+	g_return_val_if_fail(status_type != NULL, GAIM_STATUS_UNSET);
+
+	return status_type->primitive;
+}
+
+const char *
+gaim_status_type_get_id(const GaimStatusType *status_type)
+{
+	g_return_val_if_fail(status_type != NULL, NULL);
+
+	return status_type->id;
+}
+
+const char *
+gaim_status_type_get_name(const GaimStatusType *status_type)
+{
+	g_return_val_if_fail(status_type != NULL, NULL);
+
+	return status_type->name;
+}
+
+gboolean
+gaim_status_type_is_saveable(const GaimStatusType *status_type)
+{
+	g_return_val_if_fail(status_type != NULL, FALSE);
+
+	return status_type->saveable;
+}
+
+gboolean
+gaim_status_type_is_user_settable(const GaimStatusType *status_type)
+{
+	g_return_val_if_fail(status_type != NULL, FALSE);
+
+	return status_type->user_settable;
+}
+
+gboolean
+gaim_status_type_is_independent(const GaimStatusType *status_type)
+{
+	g_return_val_if_fail(status_type != NULL, FALSE);
+
+	return status_type->independent;
+}
+
+gboolean
+gaim_status_type_is_available(const GaimStatusType *status_type)
+{
+	GaimStatusPrimitive primitive;
+
+	g_return_val_if_fail(status_type != NULL, FALSE);
+
+	primitive = gaim_status_type_get_primitive(status_type);
+
+	return (primitive == GAIM_STATUS_AVAILABLE ||
+			primitive == GAIM_STATUS_HIDDEN);
+}
+
+const char *
+gaim_status_type_get_primary_attr(const GaimStatusType *status_type)
+{
+	g_return_val_if_fail(status_type != NULL, NULL);
+
+	return status_type->primary_attr_id;
+}
+
+GaimStatusAttr *
+gaim_status_type_get_attr(const GaimStatusType *status_type, const char *id)
+{
+	GList *l;
+
+	g_return_val_if_fail(status_type != NULL, NULL);
+	g_return_val_if_fail(id          != NULL, NULL);
+
+	for (l = status_type->attrs; l != NULL; l = l->next)
+	{
+		GaimStatusAttr *attr = (GaimStatusAttr *)l->data;
+
+		if (!strcmp(gaim_status_attr_get_id(attr), id))
+			return attr;
+	}
+
+	return NULL;
+}
+
+const GList *
+gaim_status_type_get_attrs(const GaimStatusType *status_type)
+{
+	g_return_val_if_fail(status_type != NULL, NULL);
+
+	return status_type->attrs;
+}
+
+
+/**************************************************************************
+* GaimStatusAttr API
+**************************************************************************/
+GaimStatusAttr *
+gaim_status_attr_new(const char *id, const char *name, GaimValue *value_type)
+{
+	GaimStatusAttr *attr;
+
+	g_return_val_if_fail(id         != NULL, NULL);
+	g_return_val_if_fail(name       != NULL, NULL);
+	g_return_val_if_fail(value_type != NULL, NULL);
+
+	attr = g_new0(GaimStatusAttr, 1);
+
+	attr->id         = g_strdup(id);
+	attr->name       = g_strdup(name);
+	attr->value_type = value_type;
+
+	return attr;
+}
+
+void
+gaim_status_attr_destroy(GaimStatusAttr *attr)
+{
+	g_return_if_fail(attr != NULL);
+
+	g_free(attr->id);
+	g_free(attr->name);
+
+	gaim_value_destroy(attr->value_type);
+
+	g_free(attr);
+}
+
+const char *
+gaim_status_attr_get_id(const GaimStatusAttr *attr)
+{
+	g_return_val_if_fail(attr != NULL, NULL);
+
+	return attr->id;
+}
+
+const char *
+gaim_status_attr_get_name(const GaimStatusAttr *attr)
+{
+	g_return_val_if_fail(attr != NULL, NULL);
+
+	return attr->name;
+}
+
+GaimValue *
+gaim_status_attr_get_value_type(const GaimStatusAttr *attr)
+{
+	g_return_val_if_fail(attr != NULL, NULL);
+
+	return attr->value_type;
+}
+
+
+/**************************************************************************
+* GaimStatus API
+**************************************************************************/
+GaimStatus *
+gaim_status_new(GaimStatusType *status_type, GaimPresence *presence)
+{
+	GaimStatus *status;
+	const GList *l;
+
+	g_return_val_if_fail(status_type != NULL, NULL);
+	g_return_val_if_fail(presence    != NULL, NULL);
+
+	status = g_new0(GaimStatus, 1);
+
+	status->type     = status_type;
+	status->presence = presence;
+
+	status->attr_values =
+		g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+		(GDestroyNotify)gaim_value_destroy);
+
+	for (l = gaim_status_type_get_attrs(status_type); l != NULL; l = l->next)
+	{
+		GaimStatusAttr *attr = (GaimStatusAttr *)l->data;
+		GaimValue *value = gaim_status_attr_get_value_type(attr);
+		GaimValue *new_value = gaim_value_dup(value);
+
+		g_hash_table_insert(status->attr_values,
+		g_strdup(gaim_status_attr_get_id(attr)), new_value);
+	}
+
+	return status;
+}
+
+void
+gaim_status_destroy(GaimStatus *status)
+{
+	g_return_if_fail(status != NULL);
+
+	gaim_status_set_active(status, FALSE);
+
+	g_hash_table_destroy(status->attr_values);
+
+	g_free(status);
+}
 
 static void
-free_parser_data(gpointer user_data)
+notify_buddy_status_update(GaimBuddy *buddy, GaimPresence *presence,
+		GaimStatus *old_status, GaimStatus *new_status)
+{
+	GaimBlistUiOps *ops = gaim_blist_get_ui_ops();
+
+	if (gaim_prefs_get_bool("/core/logging/log_system") &&
+		gaim_prefs_get_bool("/core/logging/log_away_state"))
+	{
+		time_t current_time = time(NULL);
+		const char *buddy_alias = gaim_buddy_get_alias(buddy);
+		char *tmp = NULL;
+
+		if (!gaim_status_is_available(old_status) &&
+				gaim_status_is_available(new_status))
+		{
+			tmp = g_strdup_printf(_("%s came back"), buddy_alias);
+		}
+		else if (gaim_status_is_available(old_status) &&
+				!gaim_status_is_available(new_status))
+		{
+			tmp = g_strdup_printf(_("%s went away"), buddy_alias);
+		}
+
+		if (tmp != NULL)
+		{
+			GaimLog *log = gaim_account_get_log(buddy->account);
+
+			gaim_log_write(log, GAIM_MESSAGE_SYSTEM, buddy_alias,
+			current_time, tmp);
+			g_free(tmp);
+		}
+	}
+
+	if (ops != NULL && ops->status_changed != NULL)
+		ops->status_changed(buddy, new_status);
+}
+
+static void
+notify_status_update(GaimPresence *presence, GaimStatus *old_status,
+		GaimStatus *new_status)
 {
-	StatusParserData *data = user_data;
+	GaimPresenceContext context = gaim_presence_get_context(presence);
+
+	if (context == GAIM_PRESENCE_CONTEXT_ACCOUNT)
+	{
+		GaimAccountUiOps *ops = gaim_accounts_get_ui_ops();
+
+		if (ops != NULL && ops->status_changed != NULL)
+		{
+			ops->status_changed(gaim_presence_get_account(presence),
+					new_status);
+		}
+	}
+	else if (context == GAIM_PRESENCE_CONTEXT_CONV)
+	{
+/* TODO */
+#if 0
+		GaimConversationUiOps *ops;
+		GaimConversation *conv;
+
+		conv = gaim_status_get_conversation(new_status);
+#endif
+	}
+	else if (context == GAIM_PRESENCE_CONTEXT_BUDDY)
+	{
+		const GList *l;
+
+		for (l = gaim_presence_get_buddies(presence); l != NULL; l = l->next)
+		{
+			notify_buddy_status_update((GaimBuddy *)l->data, presence,
+					old_status, new_status);
+		}
+	}
+}
+
+void
+gaim_status_set_active(GaimStatus *status, gboolean active)
+{
+	GaimStatusType *status_type;
+	GaimPresence *presence;
+	GaimStatus *old_status;
+
+	g_return_if_fail(status != NULL);
+
+	if (status->active == active)
+		return;
+
+	status_type = gaim_status_get_type(status);
 
-	if (data->buffer != NULL)
-		g_string_free(data->buffer, TRUE);
+	if (!active && gaim_status_type_is_independent(status_type))
+	{
+		gaim_debug(GAIM_DEBUG_ERROR, "status",
+				"Cannot deactivate an exclusive status (%s).\n",
+				gaim_status_type_get_id(status_type));
+		return;
+	}
+
+	presence   = gaim_status_get_presence(status);
+	old_status = gaim_presence_get_active_status(presence);
+
+	if (!gaim_status_type_is_independent(status_type))
+	{
+		const GList *l;
+
+		for (l = gaim_presence_get_statuses(presence);
+				l != NULL;
+				l = l->next)
+		{
+			GaimStatus *temp_status = (GaimStatus *)l->data;
+			GaimStatusType *temp_type;
+
+			if (temp_status == status)
+				continue;
+
+			temp_type = gaim_status_get_type(temp_status);
+
+			if (gaim_status_type_is_independent(temp_type))
+				continue;
+
+			if (gaim_status_is_active(temp_status))
+			{
+				/*
+				 * Since we don't want an infinite loop, we have to set
+				 * the active variable ourself.
+				 */
+				temp_status->active = FALSE;
+
+				notify_status_update(presence, old_status, temp_status);
+
+				break;
+			}
+		}
+	}
+
+	status->active = active;
 
-	g_free(data);
+	notify_status_update(presence, old_status, status);
+}
+
+void
+gaim_status_set_attr_boolean(GaimStatus *status, const char *id,
+		gboolean value)
+{
+	GaimStatusType *status_type;
+	GaimStatusAttr *attr;
+	GaimValue *attr_value;
+
+	g_return_if_fail(status != NULL);
+	g_return_if_fail(id     != NULL);
+
+	status_type = gaim_status_get_type(status);
+
+	/* Make sure this attribute exists. */
+	attr = gaim_status_type_get_attr(status_type, id);
+	g_return_if_fail(attr != NULL);
+
+	attr_value = gaim_status_attr_get_value_type(attr);
+	g_return_if_fail(gaim_value_get_type(attr_value) == GAIM_TYPE_BOOLEAN);
+
+	gaim_value_set_boolean(attr_value, value);
+}
+
+void
+gaim_status_set_attr_int(GaimStatus *status, const char *id, int value)
+{
+	GaimStatusType *status_type;
+	GaimStatusAttr *attr;
+	GaimValue *attr_value;
+
+	g_return_if_fail(status != NULL);
+	g_return_if_fail(id     != NULL);
+
+	status_type = gaim_status_get_type(status);
+
+	/* Make sure this attribute exists. */
+	attr = gaim_status_type_get_attr(status_type, id);
+	g_return_if_fail(attr != NULL);
+
+	attr_value = gaim_status_attr_get_value_type(attr);
+	g_return_if_fail(gaim_value_get_type(attr_value) == GAIM_TYPE_INT);
+
+	gaim_value_set_int(attr_value, value);
 }
 
-static void gaim_status_write(FILE *fp, struct away_message *am)
+void
+gaim_status_set_attr_string(GaimStatus *status, const char *id,
+		const char *value)
+{
+	GaimStatusType *status_type;
+	GaimStatusAttr *attr;
+	GaimValue *attr_value;
+
+	g_return_if_fail(status != NULL);
+	g_return_if_fail(id     != NULL);
+
+	status_type = gaim_status_get_type(status);
+
+	/* Make sure this attribute exists. */
+	attr = gaim_status_type_get_attr(status_type, id);
+	g_return_if_fail(attr != NULL);
+
+	attr_value = gaim_status_attr_get_value_type(attr);
+	g_return_if_fail(gaim_value_get_type(attr_value) == GAIM_TYPE_STRING);
+
+	gaim_value_set_string(attr_value, value);
+}
+
+GaimStatusType *
+gaim_status_get_type(const GaimStatus *status)
+{
+	g_return_val_if_fail(status != NULL, NULL);
+
+	return status->type;
+}
+
+GaimPresence *
+gaim_status_get_presence(const GaimStatus *status)
 {
-	char *esc = NULL;
+	g_return_val_if_fail(status != NULL, NULL);
+
+	return status->presence;
+}
+
+const char *
+gaim_status_get_id(const GaimStatus *status)
+{
+	g_return_val_if_fail(status != NULL, NULL);
+
+	return gaim_status_type_get_id(gaim_status_get_type(status));
+}
+
+const char *
+gaim_status_get_name(const GaimStatus *status)
+{
+	g_return_val_if_fail(status != NULL, NULL);
+
+	return gaim_status_type_get_name(gaim_status_get_type(status));
+}
+
+gboolean
+gaim_status_is_independent(const GaimStatus *status)
+{
+	g_return_val_if_fail(status != NULL, FALSE);
+
+	return gaim_status_type_is_independent(gaim_status_get_type(status));
+}
+
+gboolean
+gaim_status_is_available(const GaimStatus *status)
+{
+	g_return_val_if_fail(status != NULL, FALSE);
+
+	return gaim_status_type_is_available(gaim_status_get_type(status));
+}
 
-	esc = g_markup_escape_text(am->name, -1);
-	fprintf(fp, "\t<status name=\"%s\">\n", esc);
-	g_free(esc);
+gboolean
+gaim_status_is_active(const GaimStatus *status)
+{
+	g_return_val_if_fail(status != NULL, FALSE);
+
+	return status->active;
+}
+
+GaimValue *
+gaim_status_get_attr_value(const GaimStatus *status, const char *id)
+{
+	GaimStatusType *status_type;
+	GaimStatusAttr *attr;
+
+	g_return_val_if_fail(status != NULL, NULL);
+	g_return_val_if_fail(id     != NULL, NULL);
+
+	status_type = gaim_status_get_type(status);
+
+	/* Make sure this attribute exists. */
+	attr = gaim_status_type_get_attr(status_type, id);
+	g_return_val_if_fail(attr != NULL, NULL);
+
+	return (GaimValue *)g_hash_table_lookup(status->attr_values, id);
+}
+
+gboolean
+gaim_status_get_attr_boolean(const GaimStatus *status, const char *id)
+{
+	const GaimValue *value;
+
+	g_return_val_if_fail(status != NULL, FALSE);
+	g_return_val_if_fail(id     != NULL, FALSE);
+
+	if ((value = gaim_status_get_attr_value(status, id)) == NULL)
+		return FALSE;
+
+	g_return_val_if_fail(gaim_value_get_type(value) == GAIM_TYPE_STRING, FALSE);
+
+	return gaim_value_get_boolean(value);
+}
+
+int
+gaim_status_get_attr_int(const GaimStatus *status, const char *id)
+{
+	const GaimValue *value;
+
+	g_return_val_if_fail(status != NULL, FALSE);
+	g_return_val_if_fail(id     != NULL, FALSE);
+
+	if ((value = gaim_status_get_attr_value(status, id)) == NULL)
+		return FALSE;
+
+	g_return_val_if_fail(gaim_value_get_type(value) == GAIM_TYPE_INT, 0);
+
+	return gaim_value_get_int(value);
+}
+
+const char *
+gaim_status_get_attr_string(const GaimStatus *status, const char *id)
+{
+	const GaimValue *value;
+
+	g_return_val_if_fail(status != NULL, FALSE);
+	g_return_val_if_fail(id     != NULL, FALSE);
+
+	if ((value = gaim_status_get_attr_value(status, id)) == NULL)
+		return FALSE;
+
+	g_return_val_if_fail(gaim_value_get_type(value) == GAIM_TYPE_STRING, NULL);
+
+	return gaim_value_get_string(value);
+}
+
+gint
+gaim_status_compare(const GaimStatus *status1, const GaimStatus *status2)
+{
+	GaimStatusType *type1, *type2;
+	int score1 = 0, score2 = 0;
 
-	fprintf(fp, "\t\t<state>away</state>\n");
+	if ((status1 == NULL && status2 == NULL) ||
+			(status1 == status2))
+	{
+		return 0;
+	}
+	else if (status1 == NULL)
+		return 1;
+	else if (status2 == NULL)
+		return -1;
+
+	type1 = gaim_status_get_type(status1);
+	type2 = gaim_status_get_type(status2);
+
+	if (gaim_status_is_active(status1))
+		score1 = primitive_scores[gaim_status_type_get_primitive(type1)];
+
+	if (gaim_status_is_active(status2))
+		score2 = primitive_scores[gaim_status_type_get_primitive(type2)];
+
+	if (score1 > score2)
+		return -1;
+	else if (score1 < score2)
+		return 1;
+
+	return 0;
+}
+
+
+/**************************************************************************
+* GaimPresence API
+**************************************************************************/
+GaimPresence *
+gaim_presence_new(GaimPresenceContext context)
+{
+	GaimPresence *presence;
+
+	g_return_val_if_fail(context != GAIM_PRESENCE_CONTEXT_UNSET, NULL);
+
+	presence = g_new0(GaimPresence, 1);
+
+	presence->context = context;
+
+	presence->status_table =
+	g_hash_table_new_full(g_str_hash, g_str_equal,
+	g_free, (GFreeFunc)gaim_status_destroy);
+
+	return presence;
+}
+
+GaimPresence *
+gaim_presence_new_for_account(GaimAccount *account)
+{
+	GaimPresence *presence;
+
+	g_return_val_if_fail(account != NULL, NULL);
+
+	presence = gaim_presence_new(GAIM_PRESENCE_CONTEXT_ACCOUNT);
+
+	presence->u.account = account;
+
+	return presence;
+}
+
+GaimPresence *
+gaim_presence_new_for_conv(GaimConversation *conv)
+{
+	GaimPresence *presence;
+
+	g_return_val_if_fail(conv != NULL, NULL);
+
+	presence = gaim_presence_new(GAIM_PRESENCE_CONTEXT_CONV);
+
+	presence->u.chat.conv = conv;
+
+	return presence;
+}
 
-	esc = g_markup_escape_text(am->message, -1);
-	fprintf(fp, "\t\t<message>%s</message>\n", esc);
-	g_free(esc);
+GaimPresence *
+gaim_presence_new_for_buddy(GaimBuddy *buddy)
+{
+	GaimPresence *presence;
+	GaimStatusBuddyKey *key;
+
+	g_return_val_if_fail(buddy != NULL, NULL);
+
+	key = g_new0(GaimStatusBuddyKey, 1);
+	key->account = buddy->account;
+	key->name    = g_strdup(buddy->name);
+
+	if ((presence = g_hash_table_lookup(buddy_presences, key)) == NULL)
+	{
+		presence = gaim_presence_new(GAIM_PRESENCE_CONTEXT_BUDDY);
+
+		presence->u.buddy.name    = g_strdup(buddy->name);
+		presence->u.buddy.account = buddy->account;
+
+		g_hash_table_insert(buddy_presences, key, presence);
+	}
+	else
+	{
+		g_free(key->name);
+		g_free(key);
+	}
+
+	presence->u.buddy.ref_count++;
+	presence->u.buddy.buddies = g_list_append(presence->u.buddy.buddies,
+			buddy);
+
+	return presence;
+}
+
+void
+gaim_presence_destroy(GaimPresence *presence)
+{
+	g_return_if_fail(presence != NULL);
 
-	fprintf(fp, "\t</status>\n");
+	if (gaim_presence_get_context(presence) == GAIM_PRESENCE_CONTEXT_BUDDY)
+	{
+		GaimStatusBuddyKey key;
+
+		presence->u.buddy.ref_count--;
+
+		g_return_if_fail(presence->u.buddy.ref_count == 0);
+
+		key.account = presence->u.buddy.account;
+		key.name    = presence->u.buddy.name;
+
+		g_hash_table_remove(buddy_presences, &key);
+
+		if (presence->u.buddy.name != NULL)
+			g_free(presence->u.buddy.name);
+	}
+	else if (gaim_presence_get_context(presence) == GAIM_PRESENCE_CONTEXT_CONV)
+	{
+		if (presence->u.chat.user != NULL)
+			g_free(presence->u.chat.user);
+	}
+
+	if (presence->statuses != NULL)
+		g_list_free(presence->statuses);
+
+	g_hash_table_destroy(presence->status_table);
+
+	g_free(presence);
+}
+
+void
+gaim_presence_remove_buddy(GaimPresence *presence, GaimBuddy *buddy)
+{
+	g_return_if_fail(presence != NULL);
+	g_return_if_fail(buddy    != NULL);
+	g_return_if_fail(gaim_presence_get_context(presence) ==
+			GAIM_PRESENCE_CONTEXT_BUDDY);
+
+	if (g_list_find(presence->u.buddy.buddies, buddy) != NULL)
+	{
+		presence->u.buddy.buddies = g_list_remove(presence->u.buddy.buddies,
+				buddy);
+		presence->u.buddy.ref_count--;
+	}
 }
 
-static gboolean
-status_save_cb(gpointer unused)
+void
+gaim_presence_add_status(GaimPresence *presence, GaimStatus *status)
+{
+	g_return_if_fail(presence != NULL);
+	g_return_if_fail(status   != NULL);
+
+	presence->statuses = g_list_append(presence->statuses, status);
+
+	g_hash_table_insert(presence->status_table,
+	g_strdup(gaim_status_get_id(status)), status);
+}
+
+void
+gaim_presence_add_presence(GaimPresence *presence, const GList *source_list)
+{
+	const GList *l;
+
+	g_return_if_fail(presence    != NULL);
+	g_return_if_fail(source_list != NULL);
+
+	for (l = source_list; l != NULL; l = l->next)
+		gaim_presence_add_status(presence, (GaimStatus *)l->data);
+}
+
+void
+gaim_presence_set_status_active(GaimPresence *presence, const char *status_id,
+		gboolean active)
+{
+	GaimStatus *status;
+
+	g_return_if_fail(presence  != NULL);
+	g_return_if_fail(status_id != NULL);
+
+	status = gaim_presence_get_status(presence, status_id);
+
+	g_return_if_fail(status != NULL);
+
+	if (!gaim_status_is_independent(status))
+	{
+		if (!active)
+		{
+			gaim_debug_warning("status",
+					"Attempted to set a non-independent status "
+					"(%s) inactive. Only independent statuses "
+					"can be specifically marked inactive.",
+					status_id);
+
+			return;
+		}
+
+		if (presence->active_status != NULL)
+			gaim_status_set_active(presence->active_status, FALSE);
+
+		presence->active_status = status;
+	}
+
+	gaim_status_set_active(status, active);
+}
+
+void
+gaim_presence_switch_status(GaimPresence *presence, const char *status_id)
+{
+	GaimStatus *status;
+
+	g_return_if_fail(presence  != NULL);
+	g_return_if_fail(status_id != NULL);
+
+	status = gaim_presence_get_status(presence, status_id);
+
+	g_return_if_fail(status != NULL);
+
+	if (gaim_status_is_independent(status))
+		return;
+
+	if (presence->active_status != NULL)
+		gaim_status_set_active(presence->active_status, FALSE);
+
+	gaim_status_set_active(status, TRUE);
+}
+
+static void
+update_buddy_idle(GaimBuddy *buddy, GaimPresence *presence,
+		time_t current_time, gboolean old_idle, gboolean idle)
+{
+	GaimBlistUiOps *ops = gaim_get_blist()->ui_ops;
+
+	if (!old_idle && idle)
+	{
+		gaim_signal_emit(gaim_blist_get_handle(), "buddy-idle", buddy);
+
+		if (gaim_prefs_get_bool("/core/logging/log_system") &&
+				gaim_prefs_get_bool("/core/logging/log_idle_state"))
+		{
+			GaimLog *log = gaim_account_get_log(buddy->account);
+			char *tmp = g_strdup_printf(_("%s became idle"),
+			gaim_buddy_get_alias(buddy));
+
+			gaim_log_write(log, GAIM_MESSAGE_SYSTEM,
+			gaim_buddy_get_alias(buddy), current_time, tmp);
+			g_free(tmp);
+		}
+	}
+	else if (old_idle && !idle)
+	{
+		gaim_signal_emit(gaim_blist_get_handle(), "buddy-unidle", buddy);
+
+		if (gaim_prefs_get_bool("/core/logging/log_system") &&
+				gaim_prefs_get_bool("/core/logging/log_idle_state"))
+		{
+			GaimLog *log = gaim_account_get_log(buddy->account);
+			char *tmp = g_strdup_printf(_("%s became unidle"),
+			gaim_buddy_get_alias(buddy));
+
+			gaim_log_write(log, GAIM_MESSAGE_SYSTEM,
+			gaim_buddy_get_alias(buddy), current_time, tmp);
+			g_free(tmp);
+		}
+	}
+
+	gaim_contact_compute_priority_buddy(gaim_buddy_get_contact(buddy));
+
+	if (ops != NULL && ops->update != NULL)
+		ops->update(gaim_get_blist(), (GaimBlistNode *)buddy);
+}
+
+void
+gaim_presence_set_idle(GaimPresence *presence, gboolean idle, time_t idle_time)
+{
+	gboolean old_idle;
+
+	g_return_if_fail(presence != NULL);
+
+	if (presence->idle == idle && presence->idle_time == idle_time)
+		return;
+
+	old_idle            = presence->idle;
+	presence->idle      = idle;
+	presence->idle_time = (idle ? idle_time : 0);
+
+	if (gaim_presence_get_context(presence) == GAIM_PRESENCE_CONTEXT_BUDDY)
+	{
+		const GList *l;
+		time_t current_time = time(NULL);
+
+		for (l = gaim_presence_get_buddies(presence); l != NULL; l = l->next)
+		{
+			update_buddy_idle((GaimBuddy *)l->data, presence, current_time,
+			old_idle, idle);
+		}
+	}
+}
+
+void
+gaim_presence_set_warning_level(GaimPresence *presence, unsigned int level)
 {
-	gaim_status_sync();
-	status_save_timer = 0;
+	g_return_if_fail(presence != NULL);
+	g_return_if_fail(level <= 100);
+
+	if (presence->warning_level == level)
+		return;
+
+	presence->warning_level = level;
+
+	if (gaim_presence_get_context(presence) == GAIM_PRESENCE_CONTEXT_BUDDY)
+	{
+		GaimBlistUiOps *ops = gaim_get_blist()->ui_ops;
+
+		if (ops != NULL && ops->update != NULL)
+		{
+			const GList *l;
+
+			for (l = gaim_presence_get_buddies(presence);
+					l != NULL;
+					l = l->next)
+			{
+				ops->update(gaim_get_blist(), (GaimBlistNode *)l->data);
+			}
+		}
+	}
+}
+
+GaimPresenceContext
+gaim_presence_get_context(const GaimPresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, GAIM_PRESENCE_CONTEXT_UNSET);
+
+	return presence->context;
+}
+
+GaimAccount *
+gaim_presence_get_account(const GaimPresence *presence)
+{
+	GaimPresenceContext context;
+
+	g_return_val_if_fail(presence != NULL, NULL);
+
+	context = gaim_presence_get_context(presence);
+
+	g_return_val_if_fail(context == GAIM_PRESENCE_CONTEXT_ACCOUNT ||
+			context == GAIM_PRESENCE_CONTEXT_BUDDY, NULL);
+
+	return presence->u.account;
+}
+
+GaimConversation *
+gaim_presence_get_conversation(const GaimPresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, NULL);
+	g_return_val_if_fail(gaim_presence_get_context(presence) ==
+			GAIM_PRESENCE_CONTEXT_CONV, NULL);
+
+	return presence->u.chat.conv;
+}
+
+const char *
+gaim_presence_get_chat_user(const GaimPresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, NULL);
+	g_return_val_if_fail(gaim_presence_get_context(presence) ==
+			GAIM_PRESENCE_CONTEXT_CONV, NULL);
+
+	return presence->u.chat.user;
+}
+
+const GList *
+gaim_presence_get_buddies(const GaimPresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, NULL);
+	g_return_val_if_fail(gaim_presence_get_context(presence) ==
+			GAIM_PRESENCE_CONTEXT_BUDDY, NULL);
+
+	return presence->u.buddy.buddies;
+}
+
+const GList *
+gaim_presence_get_statuses(const GaimPresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, NULL);
+
+	return presence->statuses;
+}
+
+GaimStatus *
+gaim_presence_get_status(const GaimPresence *presence, const char *status_id)
+{
+	GaimStatus *status;
+
+	g_return_val_if_fail(presence  != NULL, NULL);
+	g_return_val_if_fail(status_id != NULL, NULL);
+
+	status = (GaimStatus *)g_hash_table_lookup(presence->status_table,
+			status_id);
+
+	return status;
+}
+
+GaimStatus *
+gaim_presence_get_active_status(const GaimPresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, NULL);
+
+	return presence->active_status;
+}
+
+gboolean
+gaim_presence_is_available(const GaimPresence *presence)
+{
+	GaimStatus *status;
+
+	g_return_val_if_fail(presence != NULL, FALSE);
+
+	status = gaim_presence_get_active_status(presence);
+
+	return ((status != NULL && gaim_status_is_available(status)) &&
+			!gaim_presence_is_idle(presence));
+}
+
+gboolean
+gaim_presence_is_online(const GaimPresence *presence)
+{
+	GaimStatus *status;
+	GaimStatusPrimitive primitive;
+
+	g_return_val_if_fail(presence != NULL, FALSE);
+
+	if ((status = gaim_presence_get_active_status(presence)) == NULL)
+		return FALSE;
+
+	primitive = gaim_status_type_get_primitive(gaim_status_get_type(status));
+
+	return (primitive != GAIM_STATUS_UNSET &&
+			primitive != GAIM_STATUS_OFFLINE);
+}
+
+gboolean
+gaim_presence_is_status_active(const GaimPresence *presence,
+		const char *status_id)
+{
+	GaimStatus *status;
+
+	g_return_val_if_fail(presence  != NULL, FALSE);
+	g_return_val_if_fail(status_id != NULL, FALSE);
+
+	status = gaim_presence_get_status(presence, status_id);
+
+	return (status != NULL && gaim_status_is_active(status));
+}
+
+gboolean
+gaim_presence_is_status_primitive_active(const GaimPresence *presence,
+		GaimStatusPrimitive primitive)
+{
+	GaimStatus *status;
+	GaimStatusType *status_type;
+
+	g_return_val_if_fail(presence  != NULL,              FALSE);
+	g_return_val_if_fail(primitive != GAIM_STATUS_UNSET, FALSE);
+
+	status      = gaim_presence_get_active_status(presence);
+	status_type = gaim_status_get_type(status);
+
+	if (gaim_status_type_get_primitive(status_type) == primitive)
+		return TRUE;
 
 	return FALSE;
 }
 
-static void
-schedule_status_save()
+gboolean
+gaim_presence_is_idle(const GaimPresence *presence)
 {
-	if (!status_save_timer)
-		status_save_timer = gaim_timeout_add(5000, status_save_cb, NULL);
+	g_return_val_if_fail(presence != NULL, FALSE);
+
+	return presence->idle;
 }
 
-static void
-start_element_handler(GMarkupParseContext *context,
-					  const gchar *element_name,
-					  const gchar **attribute_names,
-					  const gchar **attribute_values,
-					  gpointer user_data, GError **error)
+time_t
+gaim_presence_get_idle_time(const GaimPresence *presence)
 {
-	const char *value;
-	StatusParserData *data = user_data;
-	GHashTable *atts;
-	int i;
-
-	atts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-
-	for (i = 0; attribute_names[i] != NULL; i++) {
-		g_hash_table_insert(atts, g_strdup(attribute_names[i]),
-				    g_strdup(attribute_values[i]));
-	}
+	g_return_val_if_fail(presence != NULL, 0);
 
-	if (data->buffer != NULL) {
-		g_string_free(data->buffer, TRUE);
-		data->buffer = NULL;
-	}
+	return presence->idle_time;
+}
 
-	if (!strcmp(element_name, "status")) {
-		data->tag = TAG_STATUS;
-		if ((value = g_hash_table_lookup(atts, "name")) != NULL) {
-			data->am = g_new0(struct away_message, 1);
-			g_snprintf(data->am->name, sizeof(data->am->name), "%s", value);
-			away_messages = g_slist_append(away_messages, data->am);
-		}
-	} else if (!strcmp(element_name, "message")) {
-		data->tag = TAG_MESSAGE;
+unsigned int
+gaim_presence_get_warning_level(const GaimPresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, 0);
 
-	}
-
-	g_hash_table_destroy(atts);
+	return presence->warning_level;
 }
 
-static void
-end_element_handler(GMarkupParseContext *context, const gchar *element_name,
-					gpointer user_data,  GError **error)
+gint
+gaim_presence_compare(const GaimPresence *presence1,
+		const GaimPresence *presence2)
 {
-	StatusParserData *data = user_data;
-	gchar *buffer;
+	gboolean idle1, idle2;
+	size_t idle_time_1, idle_time_2;
+	int score1 = 0, score2 = 0;
+	const GList *l;
 
-	if (data->buffer == NULL)
-		return;
+	if ((presence1 == NULL && presence2 == NULL) || (presence1 == presence2))
+		return 0;
+	else if (presence1 == NULL)
+		return -1;
+	else if (presence2 == NULL)
+		return 1;
 
-	buffer = g_string_free(data->buffer, FALSE);
-	data->buffer = NULL;
+	/* Compute the score of the first set of statuses. */
+	for (l = gaim_presence_get_statuses(presence1); l != NULL; l = l->next)
+	{
+		GaimStatus *status = (GaimStatus *)l->data;
+		GaimStatusType *type = gaim_status_get_type(status);
 
-	if (data->tag == TAG_MESSAGE) {
-		if (*buffer != '\0')
-			g_snprintf(data->am->message, sizeof(data->am->message), "%s", buffer);
+		if (gaim_status_is_active(status))
+			score1 += primitive_scores[gaim_status_type_get_primitive(type)];
 	}
 
-	data->tag = TAG_NONE;
-
-	g_free(buffer);
-}
-
-static void
-text_handler(GMarkupParseContext *context, const gchar *text,
-			 gsize text_len, gpointer user_data, GError **error)
-{
-	StatusParserData *data = user_data;
-
-	if (data->buffer == NULL)
-		data->buffer = g_string_new_len(text, text_len);
-	else
-		g_string_append_len(data->buffer, text, text_len);
-}
+	/* Compute the score of the second set of statuses. */
+	for (l = gaim_presence_get_statuses(presence1); l != NULL; l = l->next)
+	{
+		GaimStatus *status = (GaimStatus *)l->data;
+		GaimStatusType *type = gaim_status_get_type(status);
 
-static GMarkupParser status_parser =
-{
-	start_element_handler,
-	end_element_handler,
-	text_handler,
-	NULL,
-	NULL
-};
-
-void gaim_status_sync()
-{
-	FILE *fp;
-	const char *user_dir = gaim_user_dir();
-	char *filename, *filename_real;
-
-	if (!status_loaded) {
-		gaim_debug(GAIM_DEBUG_WARNING, "status", "Writing status to disk.\n");
-		schedule_status_save();
-		return;
+		if (gaim_status_is_active(status))
+			score2 += primitive_scores[gaim_status_type_get_primitive(type)];
 	}
 
-	if (user_dir == NULL)
-		return;
+	idle1 = gaim_presence_is_idle(presence1);
+	idle2 = gaim_presence_is_idle(presence2);
 
-	gaim_debug(GAIM_DEBUG_INFO, "status", "Saving statuses to disk\n");
-
-	fp = fopen(user_dir, "r");
+	if (idle1)
+		score1 += primitive_scores[SCORE_IDLE];
 
-	if (fp == NULL)
-		mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR);
-	else
-		fclose(fp);
-
-	filename = g_build_filename(user_dir, "status.xml.save", NULL);
+	if (idle2)
+		score2 += primitive_scores[SCORE_IDLE];
 
-	if ((fp = fopen(filename, "w")) != NULL) {
-		GSList *l;
+	idle_time_1 = gaim_presence_get_idle_time(presence1);
+	idle_time_2 = gaim_presence_get_idle_time(presence2);
 
-		fprintf(fp, "<?xml version='1.0' encoding='UTF-8' ?>\n\n");
-		fprintf(fp, "<statuses>\n");
-
-		for (l = away_messages; l != NULL; l = l->next)
-			gaim_status_write(fp, l->data);
+	if (idle_time_1 > idle_time_2)
+		score1 += primitive_scores[SCORE_IDLE_TIME];
+	else if (idle_time_1 < idle_time_2)
+		score2 += primitive_scores[SCORE_IDLE_TIME];
 
-		fprintf(fp, "</statuses>\n");
+	if (score1 < score2)
+		return 1;
+	else if (score1 > score2)
+		return -1;
+
+	return 0;
+}
+
 
-		fclose(fp);
-		chmod(filename, S_IRUSR | S_IWUSR);
-	}
-	else {
-		gaim_debug(GAIM_DEBUG_ERROR, "status", "Unable to write %s\n",
-				   filename);
-		g_free(filename);
-		return;
-	}
+/**************************************************************************
+* Status subsystem
+**************************************************************************/
+static void
+score_pref_changed_cb(const char *name, GaimPrefType type, gpointer value,
+		gpointer data)
+{
+	int index = GPOINTER_TO_INT(data);
 
-	filename_real = g_build_filename(user_dir, "status.xml", NULL);
-
-	if (rename(filename, filename_real) < 0) {
-		gaim_debug(GAIM_DEBUG_ERROR, "status", "Error renaming %s to %s\n",
-				   filename, filename_real);
-	}
-
-	g_free(filename);
-	g_free(filename_real);
-
+	primitive_scores[index] = GPOINTER_TO_INT(value);
 }
 
-void gaim_status_load()
+void
+gaim_statuses_init(void)
 {
-	gchar *filename = g_build_filename(gaim_user_dir(), "status.xml", NULL);
-	gchar *contents = NULL;
-	gsize length;
-	GMarkupParseContext *context;
-	GError *error = NULL;
-	StatusParserData *parser_data;
-
-	if (filename == NULL) {
-		status_loaded = TRUE;
-		return;
-	}
+	gaim_prefs_add_none("/core/status");
+	gaim_prefs_add_none("/core/status/scores");
 
-	if (!g_file_get_contents(filename, &contents, &length, &error)) {
-		gaim_debug(GAIM_DEBUG_ERROR, "status",
-				   "Error reading statuses: %s\n", error->message);
-		g_error_free(error);
-		g_free(filename);
-		status_loaded = TRUE;
-		if (!away_messages) {
-			struct away_message *a = g_new0(struct away_message, 1);
-			g_snprintf(a->name, sizeof(a->name), _("Slightly less boring default"));
-			g_snprintf(a->message, sizeof(a->message), "%s", _(BORING_DEFAULT_AWAY_MSG));
-			away_messages = g_slist_append(away_messages, a);
-		}
-		return;
-	}
+	gaim_prefs_add_int("/core/status/scores/offline",
+			primitive_scores[GAIM_STATUS_OFFLINE]);
+	gaim_prefs_add_int("/core/status/scores/available",
+			primitive_scores[GAIM_STATUS_AVAILABLE]);
+	gaim_prefs_add_int("/core/status/scores/hidden",
+			primitive_scores[GAIM_STATUS_HIDDEN]);
+	gaim_prefs_add_int("/core/status/scores/away",
+			primitive_scores[GAIM_STATUS_AWAY]);
+	gaim_prefs_add_int("/core/status/scores/extended_away",
+			primitive_scores[GAIM_STATUS_EXTENDED_AWAY]);
+	gaim_prefs_add_int("/core/status/scores/idle",
+			primitive_scores[SCORE_IDLE]);
 
-	parser_data = g_new0(StatusParserData, 1);
-
-	context = g_markup_parse_context_new(&status_parser, 0,
-					     parser_data, free_parser_data);
-
-	if (!g_markup_parse_context_parse(context, contents, length, NULL)) {
-		g_markup_parse_context_free(context);
-		g_free(contents);
-		g_free(filename);
-		status_loaded = TRUE;
-		return;
-	}
+	gaim_prefs_connect_callback("/core/status/scores/offline",
+			score_pref_changed_cb,
+			GINT_TO_POINTER(GAIM_STATUS_OFFLINE));
+	gaim_prefs_connect_callback("/core/status/scores/available",
+			score_pref_changed_cb,
+			GINT_TO_POINTER(GAIM_STATUS_AVAILABLE));
+	gaim_prefs_connect_callback("/core/status/scores/hidden",
+			score_pref_changed_cb,
+			GINT_TO_POINTER(GAIM_STATUS_HIDDEN));
+	gaim_prefs_connect_callback("/core/status/scores/away",
+			score_pref_changed_cb,
+			GINT_TO_POINTER(GAIM_STATUS_AWAY));
+	gaim_prefs_connect_callback("/core/status/scores/extended_away",
+			score_pref_changed_cb,
+			GINT_TO_POINTER(GAIM_STATUS_EXTENDED_AWAY));
+	gaim_prefs_connect_callback("/core/status/scores/idle",
+			score_pref_changed_cb,
+			GINT_TO_POINTER(SCORE_IDLE));
+}
 
-	if (!g_markup_parse_context_end_parse(context, NULL)) {
-		gaim_debug(GAIM_DEBUG_ERROR, "status", "Error parsing %s\n",
-				   filename);
-		g_markup_parse_context_free(context);
-		g_free(contents);
-		g_free(filename);
-		status_loaded = TRUE;
-		return;
-	}
+void
+gaim_statuses_uninit(void)
+{
+}
 
-	g_markup_parse_context_free(context);
-	g_free(contents);
-	g_free(filename);
-	status_loaded = TRUE;
-	return;
+void
+gaim_statuses_sync(void)
+{
 }
+
+void
+gaim_statuses_load(void)
+{
+}