* 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 #define PURPLE_PRESENCE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_PRESENCE, PurplePresencePrivate)) /** @copydoc _PurplePresencePrivate */ typedef struct _PurplePresencePrivate PurplePresencePrivate; #define PURPLE_ACCOUNT_PRESENCE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_ACCOUNT_PRESENCE, PurpleAccountPresencePrivate)) /** @copydoc _PurpleAccountPresencePrivate */ typedef struct _PurpleAccountPresencePrivate PurpleAccountPresencePrivate; #define PURPLE_BUDDY_PRESENCE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_BUDDY_PRESENCE, PurpleBuddyPresencePrivate)) /** @copydoc _PurpleBuddyPresencePrivate */ typedef struct _PurpleBuddyPresencePrivate PurpleBuddyPresencePrivate; /** Private data for a presence */ struct _PurplePresencePrivate GHashTable *status_table; PurpleStatus *active_status; /* Presence property enums */ /** Private data for an account presence */ struct _PurpleAccountPresencePrivate /* Account presence property enums */ /** Private data for a buddy presence */ struct _PurpleBuddyPresencePrivate /* Buddy presence property enums */ static GObjectClass *parent_class; static PurplePresenceClass *presence_class; static GParamSpec *properties[PRES_PROP_LAST]; static GParamSpec *ap_properties[ACPRES_PROP_LAST]; static GParamSpec *bp_properties[BUDPRES_PROP_LAST]; /************************************************************************** **************************************************************************/ 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 = PURPLE_PRESENCE_GET_PRIVATE(presence); PurplePresenceClass *klass = PURPLE_PRESENCE_GET_CLASS(presence); g_return_if_fail(priv != NULL); 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 = PURPLE_PRESENCE_GET_PRIVATE(presence); g_return_if_fail(priv != NULL); 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(const PurplePresence *presence) PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence); g_return_val_if_fail(priv != NULL, NULL); purple_presence_get_status(const PurplePresence *presence, const char *status_id) PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence); g_return_val_if_fail(priv != 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(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(const PurplePresence *presence) PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence); g_return_val_if_fail(priv != NULL, NULL); return priv->active_status; purple_presence_is_available(const 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(const 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(const 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(const 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(const PurplePresence *presence) PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence); g_return_val_if_fail(priv != NULL, FALSE); return purple_presence_is_online(presence) && priv->idle; purple_presence_get_idle_time(const PurplePresence *presence) PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence); g_return_val_if_fail(priv != NULL, 0); purple_presence_get_login_time(const PurplePresence *presence) PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(presence); g_return_val_if_fail(priv != NULL, 0); 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_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(GTypeInstance *instance, gpointer klass) PURPLE_DBUS_REGISTER_POINTER(PURPLE_PRESENCE(instance), PurplePresence); PURPLE_PRESENCE_GET_PRIVATE(instance)->status_table = g_hash_table_new_full(g_str_hash, g_str_equal, /* GObject dispose function */ purple_presence_dispose(GObject *object) PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(object); g_list_foreach(priv->statuses, (GFunc)g_object_unref, NULL); g_list_free(priv->statuses); parent_class->dispose(object); /* GObject finalize function */ purple_presence_finalize(GObject *object) PurplePresencePrivate *priv = PURPLE_PRESENCE_GET_PRIVATE(object); g_hash_table_destroy(priv->status_table); PURPLE_DBUS_UNREGISTER_POINTER(object); parent_class->finalize(object); /* Class initializer function */ static void purple_presence_class_init(PurplePresenceClass *klass) GObjectClass *obj_class = G_OBJECT_CLASS(klass); parent_class = g_type_class_peek_parent(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; g_type_class_add_private(klass, sizeof(PurplePresencePrivate)); 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); purple_presence_get_type(void) static const GTypeInfo info = { sizeof(PurplePresenceClass), (GClassInitFunc)purple_presence_class_init, (GInstanceInitFunc)purple_presence_init, type = g_type_register_static(G_TYPE_OBJECT, "PurplePresence", &info, G_TYPE_FLAG_ABSTRACT); /************************************************************************** * PurpleAccountPresence API **************************************************************************/ purple_account_presence_update_idle(PurplePresence *presence, gboolean old_idle) PurpleConnection *gc = NULL; PurplePlugin *prpl = NULL; PurplePluginProtocolInfo *prpl_info = 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)); tmp = g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account)); msg = g_markup_escape_text(tmp, -1); purple_log_write(log, PURPLE_MESSAGE_SYSTEM, purple_account_get_username(account), (idle ? idle_time : current_time), msg); gc = purple_account_get_connection(account); 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)); purple_account_presence_get_account(const PurpleAccountPresence *presence) PurpleAccountPresencePrivate *priv = PURPLE_ACCOUNT_PRESENCE_GET_PRIVATE(presence); g_return_val_if_fail(priv != NULL, NULL); purple_buddy_presence_compute_score(const 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(const PurpleBuddyPresence *buddy_presence1, const 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_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); PurpleAccountPresencePrivate *priv = PURPLE_ACCOUNT_PRESENCE_GET_PRIVATE(presence); G_OBJECT_CLASS(presence_class)->constructed(object); PURPLE_PRESENCE_GET_PRIVATE(presence)->statuses = purple_prpl_get_statuses(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; presence_class = g_type_class_peek_parent(klass); 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; g_type_class_add_private(klass, sizeof(PurpleAccountPresencePrivate)); 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_get_type(void) static const GTypeInfo info = { sizeof(PurpleAccountPresenceClass), (GClassInitFunc)purple_account_presence_class_init, sizeof(PurpleAccountPresence), type = g_type_register_static(PURPLE_TYPE_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)); time_t current_time = time(NULL); PurpleBlistUiOps *ops = purple_blist_get_ui_ops(); 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), current_time, tmp2); 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), current_time, tmp2); 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 if (ops != NULL && ops->update != NULL) ops->update(purple_blist_get_buddy_list(), (PurpleBlistNode *)buddy); purple_buddy_presence_get_buddy(const PurpleBuddyPresence *presence) PurpleBuddyPresencePrivate *priv = PURPLE_BUDDY_PRESENCE_GET_PRIVATE(presence); g_return_val_if_fail(priv != NULL, NULL); /************************************************************************** * 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_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); PurpleBuddyPresencePrivate *priv = PURPLE_BUDDY_PRESENCE_GET_PRIVATE(presence); G_OBJECT_CLASS(presence_class)->constructed(object); account = purple_buddy_get_account(priv->buddy); PURPLE_PRESENCE_GET_PRIVATE(presence)->statuses = purple_prpl_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; presence_class = g_type_class_peek_parent(klass); 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; g_type_class_add_private(klass, sizeof(PurpleBuddyPresencePrivate)); 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_get_type(void) static const GTypeInfo info = { sizeof(PurpleBuddyPresenceClass), (GClassInitFunc)purple_buddy_presence_class_init, sizeof(PurpleBuddyPresence), type = g_type_register_static(PURPLE_TYPE_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,