* 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
* 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
/**************************************************************************
**************************************************************************/
/** Private data for a presence */
GHashTable *status_table;
PurpleStatus *active_status;
/* Presence property enums */
static GParamSpec *properties[PRES_PROP_LAST];
G_DEFINE_TYPE_WITH_PRIVATE(PurplePresence, purple_presence, G_TYPE_OBJECT)
/**************************************************************************
**************************************************************************/
* A presence for an account
struct _PurpleAccountPresence
/** Private data for an account presence */
} PurpleAccountPresencePrivate;
/* Account presence property enums */
static GParamSpec *ap_properties[ACPRES_PROP_LAST];
G_DEFINE_TYPE_WITH_PRIVATE(PurpleAccountPresence, purple_account_presence,
/**************************************************************************
**************************************************************************/
struct _PurpleBuddyPresence
/** Private data for a buddy presence */
} PurpleBuddyPresencePrivate;
/* Buddy presence property enums */
static GParamSpec *bp_properties[BUDPRES_PROP_LAST];
G_DEFINE_TYPE_WITH_PRIVATE(PurpleBuddyPresence, purple_buddy_presence,
/**************************************************************************
**************************************************************************/
purple_presence_set_status_active(PurplePresence *presence, const char *status_id,
g_return_if_fail(PURPLE_IS_PRESENCE(presence));
g_return_if_fail(status_id != NULL);
status = purple_presence_get_status(presence, status_id);
g_return_if_fail(PURPLE_IS_STATUS(status));
/* TODO: Should we do the following? */
/* g_return_if_fail(active == status->active); */
if (purple_status_is_exclusive(status))
purple_debug_warning("presence",
"Attempted to set a non-independent status "
"(%s) inactive. Only independent statuses "
"can be specifically marked inactive.",
purple_status_set_active(status, active);
purple_presence_switch_status(PurplePresence *presence, const char *status_id)
purple_presence_set_status_active(presence, status_id, TRUE);
purple_presence_set_idle(PurplePresence *presence, gboolean idle, time_t idle_time)
PurplePresencePrivate *priv = NULL;
PurplePresenceClass *klass = NULL;
g_return_if_fail(PURPLE_IS_PRESENCE(presence));
priv = purple_presence_get_instance_private(presence);
klass = PURPLE_PRESENCE_GET_CLASS(presence);
if (priv->idle == idle && priv->idle_time == idle_time)
priv->idle_time = (idle ? idle_time : 0);
obj = G_OBJECT(presence);
g_object_freeze_notify(obj);
g_object_notify_by_pspec(obj, properties[PRES_PROP_IDLE]);
g_object_notify_by_pspec(obj, properties[PRES_PROP_IDLE_TIME]);
g_object_thaw_notify(obj);
klass->update_idle(presence, old_idle);
purple_presence_set_login_time(PurplePresence *presence, time_t login_time)
PurplePresencePrivate *priv = NULL;
g_return_if_fail(PURPLE_IS_PRESENCE(presence));
priv = purple_presence_get_instance_private(presence);
if (priv->login_time == login_time)
priv->login_time = login_time;
g_object_notify_by_pspec(G_OBJECT(presence),
properties[PRES_PROP_LOGIN_TIME]);
purple_presence_get_statuses(PurplePresence *presence)
PurplePresencePrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), NULL);
priv = purple_presence_get_instance_private(presence);
purple_presence_get_status(PurplePresence *presence, const char *status_id)
PurplePresencePrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), NULL);
g_return_val_if_fail(status_id != NULL, NULL);
priv = purple_presence_get_instance_private(presence);
/* What's the purpose of this hash table? */
status = (PurpleStatus *)g_hash_table_lookup(priv->status_table,
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)))
g_hash_table_insert(priv->status_table,
g_strdup(purple_status_get_id(status)), status);
purple_presence_get_active_status(PurplePresence *presence)
PurplePresencePrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), NULL);
priv = purple_presence_get_instance_private(presence);
return priv->active_status;
purple_presence_is_available(PurplePresence *presence)
g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), FALSE);
status = purple_presence_get_active_status(presence);
return ((status != NULL && purple_status_is_available(status)) &&
!purple_presence_is_idle(presence));
purple_presence_is_online(PurplePresence *presence)
g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), FALSE);
if ((status = purple_presence_get_active_status(presence)) == NULL)
return purple_status_is_online(status);
purple_presence_is_status_active(PurplePresence *presence,
g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), 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));
purple_presence_is_status_primitive_active(PurplePresence *presence,
PurpleStatusPrimitive primitive)
g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), FALSE);
g_return_val_if_fail(primitive != PURPLE_STATUS_UNSET, FALSE);
for (l = purple_presence_get_statuses(presence);
PurpleStatus *temp_status = l->data;
PurpleStatusType *type = purple_status_get_status_type(temp_status);
if (purple_status_type_get_primitive(type) == primitive &&
purple_status_is_active(temp_status))
purple_presence_is_idle(PurplePresence *presence)
PurplePresencePrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), FALSE);
priv = purple_presence_get_instance_private(presence);
return purple_presence_is_online(presence) && priv->idle;
purple_presence_get_idle_time(PurplePresence *presence)
PurplePresencePrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), 0);
priv = purple_presence_get_instance_private(presence);
purple_presence_get_login_time(PurplePresence *presence)
PurplePresencePrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), 0);
priv = purple_presence_get_instance_private(presence);
return purple_presence_is_online(presence) ? priv->login_time : 0;
/**************************************************************************
* GObject code for PurplePresence
**************************************************************************/
/* Set method for GObject properties */
purple_presence_set_property(GObject *obj, guint param_id, const GValue *value,
PurplePresence *presence = PURPLE_PRESENCE(obj);
PurplePresencePrivate *priv = purple_presence_get_instance_private(presence);
purple_presence_set_idle(presence, g_value_get_boolean(value), 0);
case PRES_PROP_IDLE_TIME:
purple_presence_set_idle(presence, TRUE, g_value_get_int(value));
purple_presence_set_idle(presence, TRUE, g_value_get_int64(value));
#error Unknown size of time_t
case PRES_PROP_LOGIN_TIME:
purple_presence_set_login_time(presence, g_value_get_int(value));
purple_presence_set_login_time(presence, g_value_get_int64(value));
#error Unknown size of time_t
case PRES_PROP_ACTIVE_STATUS:
priv->active_status = g_value_get_object(value);
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
/* Get method for GObject properties */
purple_presence_get_property(GObject *obj, guint param_id, GValue *value,
PurplePresence *presence = PURPLE_PRESENCE(obj);
g_value_set_boolean(value, purple_presence_is_idle(presence));
case PRES_PROP_IDLE_TIME:
g_value_set_int(value, purple_presence_get_idle_time(presence));
g_value_set_int64(value, purple_presence_get_idle_time(presence));
#error Unknown size of time_t
case PRES_PROP_LOGIN_TIME:
g_value_set_int(value, purple_presence_get_login_time(presence));
g_value_set_int64(value, purple_presence_get_login_time(presence));
#error Unknown size of time_t
g_value_set_pointer(value, purple_presence_get_statuses(presence));
case PRES_PROP_ACTIVE_STATUS:
g_value_set_object(value, purple_presence_get_active_status(presence));
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
/* GObject initialization function */
purple_presence_init(PurplePresence *presence)
PurplePresencePrivate *priv = purple_presence_get_instance_private(presence);
priv->status_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
/* GObject dispose function */
purple_presence_dispose(GObject *object)
PurplePresencePrivate *priv =
purple_presence_get_instance_private(PURPLE_PRESENCE(object));
g_list_free_full(priv->statuses, g_object_unref);
G_OBJECT_CLASS(purple_presence_parent_class)->dispose(object);
/* GObject finalize function */
purple_presence_finalize(GObject *object)
PurplePresencePrivate *priv =
purple_presence_get_instance_private(PURPLE_PRESENCE(object));
g_hash_table_destroy(priv->status_table);
G_OBJECT_CLASS(purple_presence_parent_class)->finalize(object);
/* Class initializer function */
static void purple_presence_class_init(PurplePresenceClass *klass)
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
obj_class->dispose = purple_presence_dispose;
obj_class->finalize = purple_presence_finalize;
obj_class->get_property = purple_presence_get_property;
obj_class->set_property = purple_presence_set_property;
properties[PRES_PROP_IDLE] = g_param_spec_boolean("idle", "Idle",
"Whether the presence is in idle state.", FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
properties[PRES_PROP_IDLE_TIME] =
#error Unknown size of time_t
("idle-time", "Idle time",
"The idle time of the presence",
G_MININT64, G_MAXINT64, 0,
#error Unknown size of time_t
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
properties[PRES_PROP_LOGIN_TIME] =
#error Unknown size of time_t
("login-time", "Login time",
"The login time of the presence.",
G_MININT64, G_MAXINT64, 0,
#error Unknown size of time_t
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
properties[PRES_PROP_STATUSES] = g_param_spec_pointer("statuses",
"The list of statuses in the presence.",
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
properties[PRES_PROP_ACTIVE_STATUS] = g_param_spec_object("active-status",
"The active status for the presence.", PURPLE_TYPE_STATUS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(obj_class, PRES_PROP_LAST, properties);
/**************************************************************************
* PurpleAccountPresence API
**************************************************************************/
purple_account_presence_update_idle(PurplePresence *presence, gboolean old_idle)
PurpleConnection *gc = NULL;
PurpleProtocol *protocol = NULL;
gboolean idle = purple_presence_is_idle(presence);
time_t idle_time = purple_presence_get_idle_time(presence);
time_t current_time = time(NULL);
account = purple_account_presence_get_account(PURPLE_ACCOUNT_PRESENCE(presence));
if (purple_prefs_get_bool("/purple/logging/log_system"))
PurpleLog *log = purple_account_get_log(account, FALSE);
tmp = g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account));
dt = g_date_time_new_from_unix_local(idle_time);
tmp = g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account));
dt = g_date_time_new_now_utc();
msg = g_markup_escape_text(tmp, -1);
purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
purple_account_get_username(account),
gc = purple_account_get_connection(account);
if(PURPLE_CONNECTION_IS_CONNECTED(gc))
protocol = purple_connection_get_protocol(gc);
purple_protocol_server_iface_set_idle(protocol, gc, (idle ? (current_time - idle_time) : 0));
purple_account_presence_get_account(PurpleAccountPresence *presence)
PurpleAccountPresencePrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_ACCOUNT_PRESENCE(presence), NULL);
priv = purple_account_presence_get_instance_private(presence);
purple_buddy_presence_compute_score(PurpleBuddyPresence *buddy_presence)
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 += purple_account_get_int(purple_buddy_get_account(b), "score", 0);
if (purple_presence_is_idle(presence))
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)
else if (presence1 == NULL)
else if (presence2 == NULL)
if (purple_presence_is_online(presence1) &&
!purple_presence_is_online(presence2))
else if (purple_presence_is_online(presence2) &&
!purple_presence_is_online(presence1))
/* 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;
else if (score1 > score2)
/**************************************************************************
* GObject code for PurpleAccountPresence
**************************************************************************/
/* Set method for GObject properties */
purple_account_presence_set_property(GObject *obj, guint param_id, const GValue *value,
PurpleAccountPresence *account_presence = PURPLE_ACCOUNT_PRESENCE(obj);
PurpleAccountPresencePrivate *priv =
purple_account_presence_get_instance_private(account_presence);
case ACPRES_PROP_ACCOUNT:
priv->account = g_value_get_object(value);
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
/* Get method for GObject properties */
purple_account_presence_get_property(GObject *obj, guint param_id, GValue *value,
PurpleAccountPresence *account_presence = PURPLE_ACCOUNT_PRESENCE(obj);
case ACPRES_PROP_ACCOUNT:
g_value_set_object(value,
purple_account_presence_get_account(account_presence));
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
/* Called when done constructing */
purple_account_presence_constructed(GObject *object)
PurplePresence *presence = PURPLE_PRESENCE(object);
PurplePresencePrivate *parent_priv = purple_presence_get_instance_private(presence);
PurpleAccountPresencePrivate *account_priv =
purple_account_presence_get_instance_private(PURPLE_ACCOUNT_PRESENCE(presence));
G_OBJECT_CLASS(purple_account_presence_parent_class)->constructed(object);
parent_priv->statuses = purple_protocol_get_statuses(account_priv->account, presence);
/* Class initializer function */
static void purple_account_presence_class_init(PurpleAccountPresenceClass *klass)
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
PURPLE_PRESENCE_CLASS(klass)->update_idle = purple_account_presence_update_idle;
obj_class->constructed = purple_account_presence_constructed;
obj_class->get_property = purple_account_presence_get_property;
obj_class->set_property = purple_account_presence_set_property;
ap_properties[ACPRES_PROP_ACCOUNT] = g_param_spec_object("account",
"The account that this presence is of.", PURPLE_TYPE_ACCOUNT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
g_object_class_install_properties(obj_class, ACPRES_PROP_LAST,
purple_account_presence_init(PurpleAccountPresence *presence)
purple_account_presence_new(PurpleAccount *account)
g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
return g_object_new(PURPLE_TYPE_ACCOUNT_PRESENCE,
/**************************************************************************
* PurpleBuddyPresence API
**************************************************************************/
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 (purple_prefs_get_bool("/purple/logging/log_system"))
PurpleLog *log = purple_account_get_log(account, FALSE);
tmp = g_strdup_printf(_("%s became idle"),
purple_buddy_get_alias(buddy));
tmp2 = g_markup_escape_text(tmp, -1);
purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
purple_buddy_get_alias(buddy),
else if (old_idle && !idle)
if (purple_prefs_get_bool("/purple/logging/log_system"))
PurpleLog *log = purple_account_get_log(account, FALSE);
tmp = g_strdup_printf(_("%s became unidle"),
purple_buddy_get_alias(buddy));
tmp2 = g_markup_escape_text(tmp, -1);
purple_log_write(log, PURPLE_MESSAGE_SYSTEM,
purple_buddy_get_alias(buddy),
purple_signal_emit(purple_blist_get_handle(), "buddy-idle-changed", buddy,
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);
purple_buddy_presence_get_buddy(PurpleBuddyPresence *presence)
PurpleBuddyPresencePrivate *priv = NULL;
g_return_val_if_fail(PURPLE_IS_BUDDY_PRESENCE(presence), NULL);
priv = purple_buddy_presence_get_instance_private(presence);
/**************************************************************************
* GObject code for PurpleBuddyPresence
**************************************************************************/
/* Set method for GObject properties */
purple_buddy_presence_set_property(GObject *obj, guint param_id, const GValue *value,
PurpleBuddyPresence *buddy_presence = PURPLE_BUDDY_PRESENCE(obj);
PurpleBuddyPresencePrivate *priv =
purple_buddy_presence_get_instance_private(buddy_presence);
priv->buddy = g_value_get_object(value);
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
/* Get method for GObject properties */
purple_buddy_presence_get_property(GObject *obj, guint param_id, GValue *value,
PurpleBuddyPresence *buddy_presence = PURPLE_BUDDY_PRESENCE(obj);
g_value_set_object(value,
purple_buddy_presence_get_buddy(buddy_presence));
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
/* Called when done constructing */
purple_buddy_presence_constructed(GObject *object)
PurplePresence *presence = PURPLE_PRESENCE(object);
PurplePresencePrivate *parent_priv = purple_presence_get_instance_private(presence);
PurpleBuddyPresencePrivate *buddy_priv =
purple_buddy_presence_get_instance_private(PURPLE_BUDDY_PRESENCE(presence));
G_OBJECT_CLASS(purple_buddy_presence_parent_class)->constructed(object);
account = purple_buddy_get_account(buddy_priv->buddy);
parent_priv->statuses = purple_protocol_get_statuses(account, presence);
/* Class initializer function */
static void purple_buddy_presence_class_init(PurpleBuddyPresenceClass *klass)
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
PURPLE_PRESENCE_CLASS(klass)->update_idle = purple_buddy_presence_update_idle;
obj_class->constructed = purple_buddy_presence_constructed;
obj_class->get_property = purple_buddy_presence_get_property;
obj_class->set_property = purple_buddy_presence_set_property;
bp_properties[BUDPRES_PROP_BUDDY] = g_param_spec_object("buddy", "Buddy",
"The buddy that this presence is of.", PURPLE_TYPE_BUDDY,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
g_object_class_install_properties(obj_class, BUDPRES_PROP_LAST,
purple_buddy_presence_init(PurpleBuddyPresence *presence)
purple_buddy_presence_new(PurpleBuddy *buddy)
g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
return g_object_new(PURPLE_TYPE_BUDDY_PRESENCE,