libpurple/purplebuddypresence.c

Thu, 03 Dec 2020 22:28:40 -0600

author
Gary Kramlich <grim@reaperworld.com>
date
Thu, 03 Dec 2020 22:28:40 -0600
changeset 40636
d9a2e635775d
parent 40537
fbf29150c28c
child 41137
3c1574216aed
permissions
-rw-r--r--

Fix a bug where accounts were losing their active status.

Testing Done:
Compiled and ran locally, verified that an accounts active status is actually saved in `accounts.xml` now.

Reviewed at https://reviews.imfreedom.org/r/262/

/*
 * 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, see <https://www.gnu.org/licenses/>.
 */

#include <glib/gi18n-lib.h>

#include "purplebuddypresence.h"

#include "purpleprivate.h"

struct _PurpleBuddyPresence {
	PurplePresence parent;

	PurpleBuddy *buddy;

	GList *statuses;
};

enum {
	PROP_0,
	PROP_BUDDY,
	N_PROPERTIES
};
static GParamSpec *properties[N_PROPERTIES];

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void
purple_buddy_presence_set_buddy(PurpleBuddyPresence *presence,
                                PurpleBuddy *buddy)
{
	if(g_set_object(&presence->buddy, buddy)) {
		g_object_notify_by_pspec(G_OBJECT(presence), properties[PROP_BUDDY]);
	}
}

static int
purple_buddy_presence_compute_score(PurpleBuddyPresence *buddy_presence)
{
	GList *l;
	int score = 0;
	PurplePresence *presence = PURPLE_PRESENCE(buddy_presence);
	PurpleBuddy *b = purple_buddy_presence_get_buddy(buddy_presence);
	int *primitive_scores = _purple_statuses_get_primitive_scores();
	int offline_score = purple_prefs_get_int("/purple/status/scores/offline_msg");
	int idle_score = purple_prefs_get_int("/purple/status/scores/idle");

	for (l = purple_presence_get_statuses(presence); l != NULL; l = l->next) {
		PurpleStatus *status = (PurpleStatus *)l->data;
		PurpleStatusType *type = purple_status_get_status_type(status);

		if (purple_status_is_active(status)) {
			score += primitive_scores[purple_status_type_get_primitive(type)];
			if (!purple_status_is_online(status)) {
				if (b && purple_account_supports_offline_message(purple_buddy_get_account(b), b))
					score += offline_score;
			}
		}
	}
	score += purple_account_get_int(purple_buddy_get_account(b), "score", 0);
	if (purple_presence_is_idle(presence))
		score += idle_score;
	return score;
}

gint
purple_buddy_presence_compare(PurpleBuddyPresence *buddy_presence1,
		PurpleBuddyPresence *buddy_presence2)
{
	PurplePresence *presence1 = PURPLE_PRESENCE(buddy_presence1);
	PurplePresence *presence2 = PURPLE_PRESENCE(buddy_presence2);
	time_t idle_time_1, idle_time_2;
	int score1 = 0, score2 = 0;
	int idle_time_score = purple_prefs_get_int("/purple/status/scores/idle_time");

	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_buddy_presence_compute_score(buddy_presence1);

	/* Compute the score of the second set of statuses. */
	score2 = purple_buddy_presence_compute_score(buddy_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 += idle_time_score;
	else if (idle_time_1 < idle_time_2)
		score2 += idle_time_score;

	if (score1 < score2)
		return 1;
	else if (score1 > score2)
		return -1;

	return 0;
}

/******************************************************************************
 * PurplePresence Implementation
 *****************************************************************************/
static void
purple_buddy_presence_update_idle(PurplePresence *presence, gboolean old_idle)
{
	PurpleBuddy *buddy = purple_buddy_presence_get_buddy(PURPLE_BUDDY_PRESENCE(presence));
	GDateTime *current_time = g_date_time_new_now_utc();
	PurpleAccount *account = purple_buddy_get_account(buddy);
	gboolean idle = purple_presence_is_idle(presence);

	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
	 */

	purple_blist_update_node(purple_blist_get_default(),
	                         PURPLE_BLIST_NODE(buddy));

	g_date_time_unref(current_time);
}

static GList *
purple_buddy_presence_get_statuses(PurplePresence *presence) {
	PurpleBuddyPresence *buddy_presence = NULL;

	buddy_presence = PURPLE_BUDDY_PRESENCE(presence);

	/* We cache purple_protocol_get_statuses because it creates all new
	 * statuses which loses at least the active attribute, which breaks all
	 * sorts of things.
	 */
	if(buddy_presence->statuses == NULL) {
		PurpleAccount *account = NULL;

		account = purple_buddy_get_account(buddy_presence->buddy);

		buddy_presence->statuses = purple_protocol_get_statuses(account,
		                                                        presence);
	}

	return buddy_presence->statuses;
}

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
G_DEFINE_TYPE(PurpleBuddyPresence, purple_buddy_presence, PURPLE_TYPE_PRESENCE)

static void
purple_buddy_presence_set_property(GObject *obj, guint param_id,
                                   const GValue *value, GParamSpec *pspec)
{
	PurpleBuddyPresence *presence = PURPLE_BUDDY_PRESENCE(obj);

	switch(param_id) {
		case PROP_BUDDY:
			purple_buddy_presence_set_buddy(presence,
			                                g_value_get_object(value));
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
			break;
	}
}

static void
purple_buddy_presence_get_property(GObject *obj, guint param_id, GValue *value,
                                   GParamSpec *pspec)
{
	PurpleBuddyPresence *presence = PURPLE_BUDDY_PRESENCE(obj);

	switch(param_id) {
		case PROP_BUDDY:
			g_value_set_object(value,
			                   purple_buddy_presence_get_buddy(presence));
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
			break;
	}
}

static void
purple_buddy_presence_finalize(GObject *obj) {
	PurpleBuddyPresence *presence = PURPLE_BUDDY_PRESENCE(obj);

	g_clear_object(&presence->buddy);
	g_list_free_full(presence->statuses, g_object_unref);

	G_OBJECT_CLASS(purple_buddy_presence_parent_class)->finalize(obj);
}

static void
purple_buddy_presence_init(PurpleBuddyPresence *presence) {
}

static void
purple_buddy_presence_class_init(PurpleBuddyPresenceClass *klass) {
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
	PurplePresenceClass *presence_class = PURPLE_PRESENCE_CLASS(klass);

	obj_class->get_property = purple_buddy_presence_get_property;
	obj_class->set_property = purple_buddy_presence_set_property;
	obj_class->finalize = purple_buddy_presence_finalize;

	presence_class->update_idle = purple_buddy_presence_update_idle;
	presence_class->get_statuses = purple_buddy_presence_get_statuses;

	properties[PROP_BUDDY] = g_param_spec_object(
		"buddy", "Buddy",
		"The buddy for this presence.",
		PURPLE_TYPE_BUDDY,
		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);

	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
}

/******************************************************************************
 * Public API
 *****************************************************************************/
PurpleBuddyPresence *
purple_buddy_presence_new(PurpleBuddy *buddy) {
	g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);

	return g_object_new(
		PURPLE_TYPE_BUDDY_PRESENCE,
		"buddy", buddy,
		NULL);
}

PurpleBuddy *
purple_buddy_presence_get_buddy(PurpleBuddyPresence *presence) {
	g_return_val_if_fail(PURPLE_IS_BUDDY_PRESENCE(presence), NULL);

	return presence->buddy;
}

mercurial