libpurple/presence.c

branch
soc.2013.gobjectification
changeset 34773
b0ccef093327
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/presence.c	Sun Jul 14 20:07:33 2013 +0530
@@ -0,0 +1,582 @@
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+#include "presence.h"
+
+/**
+ * A list of statuses.
+ */
+struct _PurplePresence
+{
+	PurplePresenceContext context;
+
+	gboolean idle;
+	time_t idle_time;
+	time_t login_time;
+
+	GList *statuses;
+	GHashTable *status_table;
+
+	PurpleStatus *active_status;
+
+	union
+	{
+		PurpleAccount *account;
+
+		struct
+		{
+			PurpleConversation *conv;
+			char *user;
+
+		} chat;
+
+		struct
+		{
+			PurpleAccount *account;
+			char *name;
+			PurpleBuddy *buddy;
+
+		} buddy;
+
+	} u;
+};
+
+
+/**************************************************************************
+* PurplePresence API
+**************************************************************************/
+PurplePresence *
+purple_presence_new(PurplePresenceContext context)
+{
+	PurplePresence *presence;
+
+	g_return_val_if_fail(context != PURPLE_PRESENCE_CONTEXT_UNSET, NULL);
+
+	presence = g_new0(PurplePresence, 1);
+	PURPLE_DBUS_REGISTER_POINTER(presence, PurplePresence);
+
+	presence->context = context;
+
+	presence->status_table =
+		g_hash_table_new_full(g_str_hash, g_str_equal,
+							  g_free, NULL);
+
+	return presence;
+}
+
+PurplePresence *
+purple_presence_new_for_account(PurpleAccount *account)
+{
+	PurplePresence *presence = NULL;
+	g_return_val_if_fail(account != NULL, NULL);
+
+	presence = purple_presence_new(PURPLE_PRESENCE_CONTEXT_ACCOUNT);
+	presence->u.account = account;
+	presence->statuses = purple_prpl_get_statuses(account, presence);
+
+	return presence;
+}
+
+PurplePresence *
+purple_presence_new_for_conv(PurpleConversation *conv)
+{
+	PurplePresence *presence;
+
+	g_return_val_if_fail(conv != NULL, NULL);
+
+	presence = purple_presence_new(PURPLE_PRESENCE_CONTEXT_CONV);
+	presence->u.chat.conv = conv;
+	/* presence->statuses = purple_prpl_get_statuses(purple_conversation_get_account(conv), presence); ? */
+
+	return presence;
+}
+
+PurplePresence *
+purple_presence_new_for_buddy(PurpleBuddy *buddy)
+{
+	PurplePresence *presence;
+	PurpleAccount *account;
+
+	g_return_val_if_fail(buddy != NULL, NULL);
+	account = purple_buddy_get_account(buddy);
+
+	presence = purple_buddy_presence_new();
+
+	presence->u.buddy.name    = g_strdup(purple_buddy_get_name(buddy));
+	presence->u.buddy.account = account;
+	presence->statuses = purple_prpl_get_statuses(account, presence);
+
+	presence->u.buddy.buddy = buddy;
+
+	return presence;
+}
+
+void
+purple_presence_destroy(PurplePresence *presence)
+{
+	g_return_if_fail(presence != NULL);
+
+	if (purple_presence_get_context(presence) == PURPLE_PRESENCE_CONTEXT_BUDDY)
+	{
+		g_free(presence->u.buddy.name);
+	}
+	else if (purple_presence_get_context(presence) == PURPLE_PRESENCE_CONTEXT_CONV)
+	{
+		g_free(presence->u.chat.user);
+	}
+
+	g_list_foreach(presence->statuses, (GFunc)purple_status_destroy, NULL);
+	g_list_free(presence->statuses);
+
+	g_hash_table_destroy(presence->status_table);
+
+	PURPLE_DBUS_UNREGISTER_POINTER(presence);
+	g_free(presence);
+}
+
+void
+purple_presence_set_status_active(PurplePresence *presence, const char *status_id,
+		gboolean active)
+{
+	PurpleStatus *status;
+
+	g_return_if_fail(presence  != NULL);
+	g_return_if_fail(status_id != NULL);
+
+	status = purple_presence_get_status(presence, status_id);
+
+	g_return_if_fail(status != NULL);
+	/* TODO: Should we do the following? */
+	/* g_return_if_fail(active == status->active); */
+
+	if (purple_status_is_exclusive(status))
+	{
+		if (!active)
+		{
+			purple_debug_warning("status",
+					"Attempted to set a non-independent status "
+					"(%s) inactive. Only independent statuses "
+					"can be specifically marked inactive.",
+					status_id);
+			return;
+		}
+	}
+
+	purple_status_set_active(status, active);
+}
+
+void
+purple_presence_switch_status(PurplePresence *presence, const char *status_id)
+{
+	purple_presence_set_status_active(presence, status_id, TRUE);
+}
+
+static void
+update_buddy_idle(PurpleBuddy *buddy, PurplePresence *presence,
+		time_t current_time, gboolean old_idle, gboolean idle)
+{
+	PurpleBListUiOps *ops = purple_blist_get_ui_ops();
+	PurpleAccount *account = purple_buddy_get_account(buddy);
+
+	if (!old_idle && idle)
+	{
+		if (purple_prefs_get_bool("/purple/logging/log_system"))
+		{
+			PurpleLog *log = purple_account_get_log(account, FALSE);
+
+			if (log != NULL)
+			{
+				char *tmp, *tmp2;
+				tmp = g_strdup_printf(_("%s became idle"),
+				purple_buddy_get_alias(buddy));
+				tmp2 = g_markup_escape_text(tmp, -1);
+				g_free(tmp);
+
+				purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
+				purple_buddy_get_alias(buddy), current_time, tmp2);
+				g_free(tmp2);
+			}
+		}
+	}
+	else if (old_idle && !idle)
+	{
+		if (purple_prefs_get_bool("/purple/logging/log_system"))
+		{
+			PurpleLog *log = purple_account_get_log(account, FALSE);
+
+			if (log != NULL)
+			{
+				char *tmp, *tmp2;
+				tmp = g_strdup_printf(_("%s became unidle"),
+				purple_buddy_get_alias(buddy));
+				tmp2 = g_markup_escape_text(tmp, -1);
+				g_free(tmp);
+
+				purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
+				purple_buddy_get_alias(buddy), current_time, tmp2);
+				g_free(tmp2);
+			}
+		}
+	}
+
+	if (old_idle != idle)
+		purple_signal_emit(purple_blist_get_handle(), "buddy-idle-changed", buddy,
+		                 old_idle, idle);
+
+	purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
+
+	/* Should this be done here? It'd perhaps make more sense to
+	 * connect to buddy-[un]idle signals and update from there
+	 */
+
+	if (ops != NULL && ops->update != NULL)
+		ops->update(purple_blist_get_buddy_list(), (PurpleBListNode *)buddy);
+}
+
+void
+purple_presence_set_idle(PurplePresence *presence, gboolean idle, time_t idle_time)
+{
+	gboolean old_idle;
+	time_t current_time;
+
+	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);
+
+	current_time = time(NULL);
+
+	if (purple_presence_get_context(presence) == PURPLE_PRESENCE_CONTEXT_BUDDY)
+	{
+		update_buddy_idle(purple_presence_get_buddy(presence), presence, current_time,
+		                  old_idle, idle);
+	}
+	else if (purple_presence_get_context(presence) == PURPLE_PRESENCE_CONTEXT_ACCOUNT)
+	{
+		PurpleAccount *account;
+		PurpleConnection *gc = NULL;
+		PurplePlugin *prpl = NULL;
+		PurplePluginProtocolInfo *prpl_info = NULL;
+
+		account = purple_presence_get_account(presence);
+
+		if (purple_prefs_get_bool("/purple/logging/log_system"))
+		{
+			PurpleLog *log = purple_account_get_log(account, FALSE);
+
+			if (log != NULL)
+			{
+				char *msg, *tmp;
+
+				if (idle)
+					tmp = g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account));
+				else
+					tmp = g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account));
+
+				msg = g_markup_escape_text(tmp, -1);
+				g_free(tmp);
+				purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
+				                 purple_account_get_username(account),
+				                 (idle ? idle_time : current_time), msg);
+				g_free(msg);
+			}
+		}
+
+		gc = purple_account_get_connection(account);
+
+		if(gc)
+			prpl = purple_connection_get_prpl(gc);
+
+		if(PURPLE_CONNECTION_IS_CONNECTED(gc) && prpl != NULL)
+			prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+		if (prpl_info && prpl_info->set_idle)
+			prpl_info->set_idle(gc, (idle ? (current_time - idle_time) : 0));
+	}
+}
+
+void
+purple_presence_set_login_time(PurplePresence *presence, time_t login_time)
+{
+	g_return_if_fail(presence != NULL);
+
+	if (presence->login_time == login_time)
+		return;
+
+	presence->login_time = login_time;
+}
+
+PurplePresenceContext
+purple_presence_get_context(const PurplePresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, PURPLE_PRESENCE_CONTEXT_UNSET);
+
+	return presence->context;
+}
+
+PurpleAccount *
+purple_presence_get_account(const PurplePresence *presence)
+{
+	PurplePresenceContext context;
+
+	g_return_val_if_fail(presence != NULL, NULL);
+
+	context = purple_presence_get_context(presence);
+
+	g_return_val_if_fail(context == PURPLE_PRESENCE_CONTEXT_ACCOUNT ||
+			context == PURPLE_PRESENCE_CONTEXT_BUDDY, NULL);
+
+	return presence->u.account;
+}
+
+PurpleConversation *
+purple_presence_get_conversation(const PurplePresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, NULL);
+	g_return_val_if_fail(purple_presence_get_context(presence) ==
+			PURPLE_PRESENCE_CONTEXT_CONV, NULL);
+
+	return presence->u.chat.conv;
+}
+
+const char *
+purple_presence_get_chat_user(const PurplePresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, NULL);
+	g_return_val_if_fail(purple_presence_get_context(presence) ==
+			PURPLE_PRESENCE_CONTEXT_CONV, NULL);
+
+	return presence->u.chat.user;
+}
+
+PurpleBuddy *
+purple_presence_get_buddy(const PurplePresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, NULL);
+	g_return_val_if_fail(purple_presence_get_context(presence) ==
+			PURPLE_PRESENCE_CONTEXT_BUDDY, NULL);
+
+	return presence->u.buddy.buddy;
+}
+
+GList *
+purple_presence_get_statuses(const PurplePresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, NULL);
+
+	return presence->statuses;
+}
+
+PurpleStatus *
+purple_presence_get_status(const PurplePresence *presence, const char *status_id)
+{
+	PurpleStatus *status;
+	GList *l = NULL;
+
+	g_return_val_if_fail(presence  != NULL, NULL);
+	g_return_val_if_fail(status_id != NULL, NULL);
+
+	/* What's the purpose of this hash table? */
+	status = (PurpleStatus *)g_hash_table_lookup(presence->status_table,
+						   status_id);
+
+	if (status == NULL) {
+		for (l = purple_presence_get_statuses(presence);
+			 l != NULL && status == NULL; l = l->next)
+		{
+			PurpleStatus *temp_status = l->data;
+
+			if (purple_strequal(status_id, purple_status_get_id(temp_status)))
+				status = temp_status;
+		}
+
+		if (status != NULL)
+			g_hash_table_insert(presence->status_table,
+								g_strdup(purple_status_get_id(status)), status);
+	}
+
+	return status;
+}
+
+PurpleStatus *
+purple_presence_get_active_status(const PurplePresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, NULL);
+
+	return presence->active_status;
+}
+
+gboolean
+purple_presence_is_available(const PurplePresence *presence)
+{
+	PurpleStatus *status;
+
+	g_return_val_if_fail(presence != NULL, FALSE);
+
+	status = purple_presence_get_active_status(presence);
+
+	return ((status != NULL && purple_status_is_available(status)) &&
+			!purple_presence_is_idle(presence));
+}
+
+gboolean
+purple_presence_is_online(const PurplePresence *presence)
+{
+	PurpleStatus *status;
+
+	g_return_val_if_fail(presence != NULL, FALSE);
+
+	if ((status = purple_presence_get_active_status(presence)) == NULL)
+		return FALSE;
+
+	return purple_status_is_online(status);
+}
+
+gboolean
+purple_presence_is_status_active(const PurplePresence *presence,
+		const char *status_id)
+{
+	PurpleStatus *status;
+
+	g_return_val_if_fail(presence  != NULL, FALSE);
+	g_return_val_if_fail(status_id != NULL, FALSE);
+
+	status = purple_presence_get_status(presence, status_id);
+
+	return (status != NULL && purple_status_is_active(status));
+}
+
+gboolean
+purple_presence_is_status_primitive_active(const PurplePresence *presence,
+		PurpleStatusPrimitive primitive)
+{
+	GList *l;
+
+	g_return_val_if_fail(presence  != NULL,              FALSE);
+	g_return_val_if_fail(primitive != PURPLE_STATUS_UNSET, FALSE);
+
+	for (l = purple_presence_get_statuses(presence);
+	     l != NULL; l = l->next)
+	{
+		PurpleStatus *temp_status = l->data;
+		PurpleStatusType *type = purple_status_get_type(temp_status);
+
+		if (purple_status_type_get_primitive(type) == primitive &&
+		    purple_status_is_active(temp_status))
+			return TRUE;
+	}
+	return FALSE;
+}
+
+gboolean
+purple_presence_is_idle(const PurplePresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, FALSE);
+
+	return purple_presence_is_online(presence) && presence->idle;
+}
+
+time_t
+purple_presence_get_idle_time(const PurplePresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, 0);
+
+	return presence->idle_time;
+}
+
+time_t
+purple_presence_get_login_time(const PurplePresence *presence)
+{
+	g_return_val_if_fail(presence != NULL, 0);
+
+	return purple_presence_is_online(presence) ? presence->login_time : 0;
+}
+
+static int
+purple_presence_compute_score(const PurplePresence *presence)
+{
+	GList *l;
+	int score = 0;
+
+	for (l = purple_presence_get_statuses(presence); l != NULL; l = l->next) {
+		PurpleStatus *status = (PurpleStatus *)l->data;
+		PurpleStatusType *type = purple_status_get_type(status);
+
+		if (purple_status_is_active(status)) {
+			score += primitive_scores[purple_status_type_get_primitive(type)];
+			if (!purple_status_is_online(status)) {
+				PurpleBuddy *b = purple_presence_get_buddy(presence);
+				if (b && purple_account_supports_offline_message(purple_buddy_get_account(b), b))
+					score += primitive_scores[SCORE_OFFLINE_MESSAGE];
+			}
+		}
+	}
+	score += purple_account_get_int(purple_presence_get_account(presence), "score", 0);
+	if (purple_presence_is_idle(presence))
+		score += primitive_scores[SCORE_IDLE];
+	return score;
+}
+
+gint
+purple_presence_compare(const PurplePresence *presence1,
+		const PurplePresence *presence2)
+{
+	time_t idle_time_1, idle_time_2;
+	int score1 = 0, score2 = 0;
+
+	if (presence1 == presence2)
+		return 0;
+	else if (presence1 == NULL)
+		return 1;
+	else if (presence2 == NULL)
+		return -1;
+
+	if (purple_presence_is_online(presence1) &&
+			!purple_presence_is_online(presence2))
+		return -1;
+	else if (purple_presence_is_online(presence2) &&
+			!purple_presence_is_online(presence1))
+		return 1;
+
+	/* Compute the score of the first set of statuses. */
+	score1 = purple_presence_compute_score(presence1);
+
+	/* Compute the score of the second set of statuses. */
+	score2 = purple_presence_compute_score(presence2);
+
+	idle_time_1 = time(NULL) - purple_presence_get_idle_time(presence1);
+	idle_time_2 = time(NULL) - purple_presence_get_idle_time(presence2);
+
+	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];
+
+	if (score1 < score2)
+		return 1;
+	else if (score1 > score2)
+		return -1;
+
+	return 0;
+}

mercurial