X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=libempathy%2Fempathy-contact.c;h=6842cde09d8078f60afd055236836eabf5d551bb;hp=44d9dbeedd6bd1abb6187927dd36f13dd5bfeaf1;hb=34e8fc1344c5433be1a9d8f8614cdb84933a84fb;hpb=69587b5167623c9c73a06ab9c86695019c2146c6 diff --git a/libempathy/empathy-contact.c b/libempathy/empathy-contact.c index 44d9dbee..6842cde0 100644 --- a/libempathy/empathy-contact.c +++ b/libempathy/empathy-contact.c @@ -25,13 +25,22 @@ #include +#include +#include #include +#include +#include + +#if HAVE_GEOCLUE +#include +#endif + #include "empathy-contact.h" -#include "empathy-account-manager.h" #include "empathy-utils.h" #include "empathy-enum-types.h" #include "empathy-marshal.h" +#include "empathy-location.h" #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT #include "empathy-debug.h" @@ -39,12 +48,12 @@ #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContact) typedef struct { TpContact *tp_contact; - EmpathyAccount *account; + TpAccount *account; + FolksPersona *persona; gchar *id; - gchar *name; + gchar *alias; EmpathyAvatar *avatar; TpConnectionPresenceType presence; - gchar *presence_message; guint handle; EmpathyCapabilities capabilities; gboolean is_user; @@ -52,8 +61,12 @@ typedef struct { /* Location is composed of string keys and GValues. * Example: a "city" key would have "Helsinki" as string GValue, * a "latitude" would have 65.0 as double GValue. + * + * This is a super set of the location stored in TpContact as we can try add + * more fields by searching the address using geoclue. */ GHashTable *location; + GHashTable *groups; } EmpathyContactPriv; static void contact_finalize (GObject *object); @@ -62,6 +75,18 @@ static void contact_get_property (GObject *object, guint param_id, static void contact_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec); +#if HAVE_GEOCLUE +static void update_geocode (EmpathyContact *contact); +#endif + +static void empathy_contact_set_location (EmpathyContact *contact, + GHashTable *location); + +static void set_capabilities_from_tp_caps (EmpathyContact *self, + TpCapabilities *caps); + +static void contact_set_avatar_from_tp_contact (EmpathyContact *contact); + G_DEFINE_TYPE (EmpathyContact, empathy_contact, G_TYPE_OBJECT); enum @@ -69,8 +94,9 @@ enum PROP_0, PROP_TP_CONTACT, PROP_ACCOUNT, + PROP_PERSONA, PROP_ID, - PROP_NAME, + PROP_ALIAS, PROP_AVATAR, PROP_PRESENCE, PROP_PRESENCE_MESSAGE, @@ -87,6 +113,9 @@ enum { static guint signals[LAST_SIGNAL]; +/* TpContact* -> EmpathyContact*, both borrowed ref */ +static GHashTable *contacts_table = NULL; + static void tp_contact_notify_cb (TpContact *tp_contact, GParamSpec *param, @@ -96,7 +125,7 @@ tp_contact_notify_cb (TpContact *tp_contact, /* Forward property notifications */ if (!tp_strdiff (param->name, "alias")) - g_object_notify (contact, "name"); + g_object_notify (contact, "alias"); else if (!tp_strdiff (param->name, "presence-type")) { TpConnectionPresenceType presence; @@ -106,12 +135,36 @@ tp_contact_notify_cb (TpContact *tp_contact, priv->presence = presence; g_object_notify (contact, "presence"); } - else if (!tp_strdiff (param->name, "presence-message")) - g_object_notify (contact, "presence-message"); else if (!tp_strdiff (param->name, "identifier")) g_object_notify (contact, "id"); else if (!tp_strdiff (param->name, "handle")) g_object_notify (contact, "handle"); + else if (!tp_strdiff (param->name, "location")) + { + GHashTable *location; + + location = tp_contact_get_location (tp_contact); + /* This will start a geoclue search to find the address if needed */ + empathy_contact_set_location (EMPATHY_CONTACT (contact), location); + } + else if (!tp_strdiff (param->name, "capabilities")) + { + set_capabilities_from_tp_caps (EMPATHY_CONTACT (contact), + tp_contact_get_capabilities (tp_contact)); + } + else if (!tp_strdiff (param->name, "avatar-file")) + { + contact_set_avatar_from_tp_contact (EMPATHY_CONTACT (contact)); + } +} + +static void +folks_persona_notify_cb (FolksPersona *folks_persona, + GParamSpec *param, + GObject *contact) +{ + if (!tp_strdiff (param->name, "presence-message")) + g_object_notify (contact, "presence-message"); } static void @@ -121,6 +174,7 @@ contact_dispose (GObject *object) if (priv->tp_contact) { + g_hash_table_remove (contacts_table, priv->tp_contact); g_signal_handlers_disconnect_by_func (priv->tp_contact, tp_contact_notify_cb, object); g_object_unref (priv->tp_contact); @@ -131,6 +185,26 @@ contact_dispose (GObject *object) g_object_unref (priv->account); priv->account = NULL; + if (priv->persona) + { + g_signal_handlers_disconnect_by_func (priv->persona, + folks_persona_notify_cb, object); + g_object_unref (priv->persona); + } + priv->persona = NULL; + + if (priv->avatar != NULL) + { + empathy_avatar_unref (priv->avatar); + priv->avatar = NULL; + } + + if (priv->location != NULL) + { + g_hash_table_unref (priv->location); + priv->location = NULL; + } + G_OBJECT_CLASS (empathy_contact_parent_class)->dispose (object); } @@ -159,9 +233,17 @@ empathy_contact_class_init (EmpathyContactClass *class) g_param_spec_object ("account", "The account", "The account associated with the contact", - EMPATHY_TYPE_ACCOUNT, + TP_TYPE_ACCOUNT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_PERSONA, + g_param_spec_object ("persona", + "Persona", + "The FolksPersona associated with the contact", + FOLKS_TYPE_PERSONA, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_ID, g_param_spec_string ("id", @@ -171,10 +253,10 @@ empathy_contact_class_init (EmpathyContactClass *class) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, - PROP_NAME, - g_param_spec_string ("name", - "Contact Name", - "The name of the contact", + PROP_ALIAS, + g_param_spec_string ("alias", + "Contact alias", + "An alias for the contact", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); @@ -263,6 +345,7 @@ empathy_contact_init (EmpathyContact *contact) contact->priv = priv; priv->location = NULL; + priv->groups = NULL; } static void @@ -274,15 +357,10 @@ contact_finalize (GObject *object) DEBUG ("finalize: %p", object); - g_free (priv->name); + if (priv->groups != NULL) + g_hash_table_destroy (priv->groups); + g_free (priv->alias); g_free (priv->id); - g_free (priv->presence_message); - - if (priv->avatar) - empathy_avatar_unref (priv->avatar); - - if (priv->location != NULL) - g_hash_table_unref (priv->location); G_OBJECT_CLASS (empathy_contact_parent_class)->finalize (object); } @@ -292,6 +370,9 @@ set_tp_contact (EmpathyContact *contact, TpContact *tp_contact) { EmpathyContactPriv *priv = GET_PRIV (contact); + GHashTable *location; + TpHandle self_handle; + TpHandle handle; if (tp_contact == NULL) return; @@ -300,6 +381,23 @@ set_tp_contact (EmpathyContact *contact, priv->tp_contact = g_object_ref (tp_contact); priv->presence = empathy_contact_get_presence (contact); + location = tp_contact_get_location (tp_contact); + if (location != NULL) + empathy_contact_set_location (contact, location); + + set_capabilities_from_tp_caps (contact, + tp_contact_get_capabilities (tp_contact)); + + contact_set_avatar_from_tp_contact (contact); + + /* Set is-user property. Note that it could still be the handle is + * different from the connection's self handle, in the case the handle + * comes from a group interface. */ + self_handle = tp_connection_get_self_handle ( + tp_contact_get_connection (tp_contact)); + handle = tp_contact_get_handle (tp_contact); + empathy_contact_set_is_user (contact, self_handle == handle); + g_signal_connect (priv->tp_contact, "notify", G_CALLBACK (tp_contact_notify_cb), contact); } @@ -320,11 +418,14 @@ contact_get_property (GObject *object, case PROP_ACCOUNT: g_value_set_object (value, empathy_contact_get_account (contact)); break; + case PROP_PERSONA: + g_value_set_object (value, empathy_contact_get_persona (contact)); + break; case PROP_ID: g_value_set_string (value, empathy_contact_get_id (contact)); break; - case PROP_NAME: - g_value_set_string (value, empathy_contact_get_name (contact)); + case PROP_ALIAS: + g_value_set_string (value, empathy_contact_get_alias (contact)); break; case PROP_AVATAR: g_value_set_boxed (value, empathy_contact_get_avatar (contact)); @@ -368,11 +469,14 @@ contact_set_property (GObject *object, g_assert (priv->account == NULL); priv->account = g_value_dup_object (value); break; + case PROP_PERSONA: + empathy_contact_set_persona (contact, g_value_get_object (value)); + break; case PROP_ID: empathy_contact_set_id (contact, g_value_get_string (value)); break; - case PROP_NAME: - empathy_contact_set_name (contact, g_value_get_string (value)); + case PROP_ALIAS: + empathy_contact_set_alias (contact, g_value_get_string (value)); break; case PROP_AVATAR: empathy_contact_set_avatar (contact, g_value_get_boxed (value)); @@ -409,17 +513,43 @@ empathy_contact_new (TpContact *tp_contact) } EmpathyContact * -empathy_contact_new_for_log (EmpathyAccount *account, +empathy_contact_from_tpl_contact (TpAccount *account, + TplEntity *tpl_entity) +{ + EmpathyContact *retval; + gboolean is_user; + + g_return_val_if_fail (TPL_IS_ENTITY (tpl_entity), NULL); + + is_user = (TPL_ENTITY_SELF == tpl_entity_get_entity_type (tpl_entity)); + + retval = g_object_new (EMPATHY_TYPE_CONTACT, + "id", tpl_entity_get_alias (tpl_entity), + "alias", tpl_entity_get_identifier (tpl_entity), + "account", account, + "is-user", is_user, + NULL); + + if (!EMP_STR_EMPTY (tpl_entity_get_avatar_token (tpl_entity))) + empathy_contact_load_avatar_cache (retval, + tpl_entity_get_avatar_token (tpl_entity)); + + return retval; +} + +EmpathyContact * +empathy_contact_new_for_log (TpAccount *account, const gchar *id, - const gchar *name, + const gchar *alias, gboolean is_user) { g_return_val_if_fail (id != NULL, NULL); + g_assert (account != NULL); return g_object_new (EMPATHY_TYPE_CONTACT, "account", account, "id", id, - "name", name, + "alias", alias, "is-user", is_user, NULL); } @@ -471,51 +601,117 @@ empathy_contact_set_id (EmpathyContact *contact, priv->id = g_strdup (id); g_object_notify (G_OBJECT (contact), "id"); - if (EMP_STR_EMPTY (priv->name)) - g_object_notify (G_OBJECT (contact), "name"); + if (EMP_STR_EMPTY (priv->alias)) + g_object_notify (G_OBJECT (contact), "alias"); } g_object_unref (contact); } const gchar * -empathy_contact_get_name (EmpathyContact *contact) +empathy_contact_get_alias (EmpathyContact *contact) { EmpathyContactPriv *priv; + const gchar *alias; g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); priv = GET_PRIV (contact); if (priv->tp_contact != NULL) - return tp_contact_get_alias (priv->tp_contact); - - if (EMP_STR_EMPTY (priv->name)) - return empathy_contact_get_id (contact); - - return priv->name; + alias = tp_contact_get_alias (priv->tp_contact); + else + alias = priv->alias; + + if (!EMP_STR_EMPTY (alias)) + return alias; + else + return empathy_contact_get_id (contact); } void -empathy_contact_set_name (EmpathyContact *contact, - const gchar *name) +empathy_contact_set_alias (EmpathyContact *contact, + const gchar *alias) { EmpathyContactPriv *priv; + FolksPersona *persona; g_return_if_fail (EMPATHY_IS_CONTACT (contact)); priv = GET_PRIV (contact); g_object_ref (contact); - if (tp_strdiff (name, priv->name)) + + /* Set the alias on the persona if possible */ + persona = empathy_contact_get_persona (contact); + if (persona != NULL && FOLKS_IS_ALIAS (persona)) + { + DEBUG ("Setting alias for contact %s to %s", + empathy_contact_get_id (contact), alias); + + folks_alias_set_alias (FOLKS_ALIAS (persona), alias); + } + + if (tp_strdiff (alias, priv->alias)) { - g_free (priv->name); - priv->name = g_strdup (name); - g_object_notify (G_OBJECT (contact), "name"); + g_free (priv->alias); + priv->alias = g_strdup (alias); + g_object_notify (G_OBJECT (contact), "alias"); } + g_object_unref (contact); } +static void +groups_change_group_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + FolksGroups *groups = FOLKS_GROUPS (source); + GError *error = NULL; + + folks_groups_change_group_finish (groups, result, &error); + if (error != NULL) + { + g_warning ("failed to change group: %s", error->message); + g_clear_error (&error); + } +} + +void +empathy_contact_change_group (EmpathyContact *contact, const gchar *group, + gboolean is_member) +{ + EmpathyContactPriv *priv; + FolksPersona *persona; + + g_return_if_fail (EMPATHY_IS_CONTACT (contact)); + g_return_if_fail (group != NULL); + + priv = GET_PRIV (contact); + + /* Normally pass through the changes to the persona */ + persona = empathy_contact_get_persona (contact); + if (persona != NULL) + { + if (FOLKS_IS_GROUPS (persona)) + folks_groups_change_group (FOLKS_GROUPS (persona), group, is_member, + groups_change_group_cb, contact); + return; + } + + /* If the persona doesn't exist yet, we have to cache the changes until it + * does */ + if (priv->groups == NULL) + { + priv->groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + NULL); + } + + g_hash_table_insert (priv->groups, g_strdup (group), + GUINT_TO_POINTER (is_member)); +} + EmpathyAvatar * empathy_contact_get_avatar (EmpathyContact *contact) { @@ -553,7 +749,7 @@ empathy_contact_set_avatar (EmpathyContact *contact, g_object_notify (G_OBJECT (contact), "avatar"); } -EmpathyAccount * +TpAccount * empathy_contact_get_account (EmpathyContact *contact) { EmpathyContactPriv *priv; @@ -564,21 +760,105 @@ empathy_contact_get_account (EmpathyContact *contact) if (priv->account == NULL && priv->tp_contact != NULL) { - EmpathyAccountManager *manager; TpConnection *connection; /* FIXME: This assume the account manager already exists */ - manager = empathy_account_manager_dup_singleton (); connection = tp_contact_get_connection (priv->tp_contact); - priv->account = empathy_account_manager_get_account_for_connection ( - manager, connection); - g_object_ref (priv->account); - g_object_unref (manager); + priv->account = + g_object_ref (empathy_get_account_for_connection (connection)); } return priv->account; } +FolksPersona * +empathy_contact_get_persona (EmpathyContact *contact) +{ + EmpathyContactPriv *priv; + + g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL); + + priv = GET_PRIV (contact); + + if (priv->persona == NULL && priv->tp_contact != NULL) + { + /* FIXME: This is disgustingly slow */ + /* Query for the persona */ + EmpathyIndividualManager *manager; + GList *individuals, *l; + + manager = empathy_individual_manager_dup_singleton (); + individuals = empathy_individual_manager_get_members (manager); + + for (l = individuals; l != NULL; l = l->next) + { + GList *personas, *j; + FolksIndividual *individual = FOLKS_INDIVIDUAL (l->data); + + personas = folks_individual_get_personas (individual); + for (j = personas; j != NULL; j = j->next) + { + TpfPersona *persona = j->data; + + if (TPF_IS_PERSONA (persona)) + { + TpContact *tp_contact = tpf_persona_get_contact (persona); + + if (tp_contact == priv->tp_contact) + { + /* Found the right persona */ + priv->persona = g_object_ref (persona); + goto finished; + } + } + } + } + +finished: + g_list_free (individuals); + g_object_unref (manager); + } + + return priv->persona; +} + +void +empathy_contact_set_persona (EmpathyContact *contact, + FolksPersona *persona) +{ + EmpathyContactPriv *priv; + + g_return_if_fail (EMPATHY_IS_CONTACT (contact)); + g_return_if_fail (TPF_IS_PERSONA (persona)); + + priv = GET_PRIV (contact); + + if (persona == priv->persona) + return; + + if (priv->persona != NULL) + g_object_unref (priv->persona); + priv->persona = g_object_ref (persona); + + g_signal_connect (priv->persona, "notify", + G_CALLBACK (folks_persona_notify_cb), contact); + + g_object_notify (G_OBJECT (contact), "persona"); + + /* Set the persona's alias, since ours could've been set using + * empathy_contact_set_alias() before we had a persona; this happens when + * adding a contact. */ + empathy_contact_set_alias (contact, priv->alias); + + /* Set the persona's groups */ + if (priv->groups != NULL) + { + folks_groups_set_groups (FOLKS_GROUPS (persona), priv->groups); + g_hash_table_destroy (priv->groups); + priv->groups = NULL; + } +} + TpConnection * empathy_contact_get_connection (EmpathyContact *contact) { @@ -641,10 +921,10 @@ empathy_contact_get_presence_message (EmpathyContact *contact) priv = GET_PRIV (contact); - if (priv->tp_contact != NULL) - return tp_contact_get_presence_message (priv->tp_contact); + if (priv->persona != NULL) + return folks_presence_get_presence_message (FOLKS_PRESENCE (priv->persona)); - return priv->presence_message; + return NULL; } void @@ -655,13 +935,11 @@ empathy_contact_set_presence_message (EmpathyContact *contact, g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - if (!tp_strdiff (message, priv->presence_message)) - return; - - g_free (priv->presence_message); - priv->presence_message = g_strdup (message); - - g_object_notify (G_OBJECT (contact), "presence-message"); + if (priv->persona != NULL) + { + folks_presence_set_presence_message (FOLKS_PRESENCE (priv->persona), + message); + } } guint @@ -839,7 +1117,7 @@ empathy_contact_can_send_files (EmpathyContact *contact) } gboolean -empathy_contact_can_use_stream_tube (EmpathyContact *contact) +empathy_contact_can_use_rfb_stream_tube (EmpathyContact *contact) { EmpathyContactPriv *priv; @@ -847,14 +1125,14 @@ empathy_contact_can_use_stream_tube (EmpathyContact *contact) priv = GET_PRIV (contact); - return priv->capabilities & EMPATHY_CAPABILITIES_STREAM_TUBE; + return priv->capabilities & EMPATHY_CAPABILITIES_RFB_STREAM_TUBE; } static gchar * contact_get_avatar_filename (EmpathyContact *contact, const gchar *token) { - EmpathyAccount *account; + TpAccount *account; gchar *avatar_path; gchar *avatar_file; gchar *token_escaped; @@ -868,8 +1146,8 @@ contact_get_avatar_filename (EmpathyContact *contact, avatar_path = g_build_filename (g_get_user_cache_dir (), "telepathy", "avatars", - empathy_account_get_connection_manager (account), - empathy_account_get_protocol (account), + tp_account_get_connection_manager (account), + tp_account_get_protocol (account), NULL); g_mkdir_with_parents (avatar_path, 0700); @@ -881,44 +1159,6 @@ contact_get_avatar_filename (EmpathyContact *contact, return avatar_file; } -void -empathy_contact_load_avatar_data (EmpathyContact *contact, - const guchar *data, - const gsize len, - const gchar *format, - const gchar *token) -{ - EmpathyAvatar *avatar; - gchar *filename; - GError *error = NULL; - - g_return_if_fail (EMPATHY_IS_CONTACT (contact)); - g_return_if_fail (data != NULL); - g_return_if_fail (len > 0); - g_return_if_fail (format != NULL); - g_return_if_fail (!EMP_STR_EMPTY (token)); - - /* Load and set the avatar */ - filename = contact_get_avatar_filename (contact, token); - avatar = empathy_avatar_new (g_memdup (data, len), len, g_strdup (format), - g_strdup (token), filename); - empathy_contact_set_avatar (contact, avatar); - empathy_avatar_unref (avatar); - - /* Save to cache if not yet in it */ - if (filename && !g_file_test (filename, G_FILE_TEST_EXISTS)) - { - if (!empathy_avatar_save_to_file (avatar, filename, &error)) - { - DEBUG ("Failed to save avatar in cache: %s", - error ? error->message : "No error given"); - g_clear_error (&error); - } - else - DEBUG ("Avatar saved to %s", filename); - } -} - gboolean empathy_contact_load_avatar_cache (EmpathyContact *contact, const gchar *token) @@ -947,10 +1187,15 @@ empathy_contact_load_avatar_cache (EmpathyContact *contact, if (data) { DEBUG ("Avatar loaded from %s", filename); - avatar = empathy_avatar_new (data, len, NULL, g_strdup (token), filename); + avatar = empathy_avatar_new ((guchar *) data, len, NULL, g_strdup (token), + filename); empathy_contact_set_avatar (contact, avatar); empathy_avatar_unref (avatar); } + else + { + g_free (filename); + } return data != NULL; } @@ -1014,6 +1259,7 @@ empathy_avatar_unref (EmpathyAvatar *avatar) g_free (avatar->data); g_free (avatar->format); g_free (avatar->token); + g_free (avatar->filename); g_slice_free (EmpathyAvatar, avatar); } } @@ -1043,7 +1289,8 @@ empathy_avatar_save_to_file (EmpathyAvatar *self, const gchar *filename, GError **error) { - return g_file_set_contents (filename, self->data, self->len, error); + return g_file_set_contents (filename, (const gchar *) self->data, self->len, + error); } /** @@ -1085,9 +1332,9 @@ empathy_contact_get_location (EmpathyContact *contact) * Example: a "city" key would have "Helsinki" as string GValue, * a "latitude" would have 65.0 as double GValue. */ -void +static void empathy_contact_set_location (EmpathyContact *contact, - GHashTable *location) + GHashTable *location) { EmpathyContactPriv *priv; @@ -1100,6 +1347,9 @@ empathy_contact_set_location (EmpathyContact *contact, g_hash_table_unref (priv->location); priv->location = g_hash_table_ref (location); +#if HAVE_GEOCLUE + update_geocode (contact); +#endif g_object_notify (G_OBJECT (contact), "location"); } @@ -1139,3 +1389,310 @@ empathy_contact_equal (gconstpointer contact1, } return FALSE; } + +#if HAVE_GEOCLUE +#define GEOCODE_SERVICE "org.freedesktop.Geoclue.Providers.Yahoo" +#define GEOCODE_PATH "/org/freedesktop/Geoclue/Providers/Yahoo" + +/* This callback is called by geoclue when it found a position + * for the given address. A position is necessary for a contact + * to show up on the map + */ +static void +geocode_cb (GeoclueGeocode *geocode, + GeocluePositionFields fields, + double latitude, + double longitude, + double altitude, + GeoclueAccuracy *accuracy, + GError *error, + gpointer contact) +{ + EmpathyContactPriv *priv = GET_PRIV (contact); + GHashTable *new_location; + + if (priv->location == NULL) + goto out; + + if (error != NULL) + { + DEBUG ("Error geocoding location : %s", error->message); + goto out; + } + + /* No need to change location if we didn't find the position */ + if (!(fields & GEOCLUE_POSITION_FIELDS_LATITUDE)) + goto out; + + if (!(fields & GEOCLUE_POSITION_FIELDS_LONGITUDE)) + goto out; + + new_location = tp_asv_new ( + EMPATHY_LOCATION_LAT, G_TYPE_DOUBLE, latitude, + EMPATHY_LOCATION_LON, G_TYPE_DOUBLE, longitude, + NULL); + + DEBUG ("\t - Latitude: %f", latitude); + DEBUG ("\t - Longitude: %f", longitude); + + /* Copy remaning fields. LAT and LON were not defined so we won't overwrite + * the values we just set. */ + tp_g_hash_table_update (new_location, priv->location, + (GBoxedCopyFunc) g_strdup, (GBoxedCopyFunc) tp_g_value_slice_dup); + + /* Set the altitude only if it wasn't defined before */ + if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE && + g_hash_table_lookup (new_location, EMPATHY_LOCATION_ALT) == NULL) + { + tp_asv_set_double (new_location, g_strdup (EMPATHY_LOCATION_ALT), + altitude); + DEBUG ("\t - Altitude: %f", altitude); + } + + /* Don't change the accuracy as we used an address to get this position */ + g_hash_table_unref (priv->location); + priv->location = new_location; + g_object_notify (contact, "location"); +out: + g_object_unref (geocode); + g_object_unref (contact); +} + +static gchar * +get_dup_string (GHashTable *location, + gchar *key) +{ + GValue *value; + + value = g_hash_table_lookup (location, key); + if (value != NULL) + return g_value_dup_string (value); + + return NULL; +} + +static void +update_geocode (EmpathyContact *contact) +{ + static GeoclueGeocode *geocode; + gchar *str; + GHashTable *address; + GHashTable *location; + + location = empathy_contact_get_location (contact); + if (location == NULL) + return; + + /* No need to search for position if contact published it */ + if (g_hash_table_lookup (location, EMPATHY_LOCATION_LAT) != NULL || + g_hash_table_lookup (location, EMPATHY_LOCATION_LON) != NULL) + return; + + if (geocode == NULL) + { + geocode = geoclue_geocode_new (GEOCODE_SERVICE, GEOCODE_PATH); + g_object_add_weak_pointer (G_OBJECT (geocode), (gpointer *) &geocode); + } + else + { + g_object_ref (geocode); + } + + address = geoclue_address_details_new (); + + str = get_dup_string (location, EMPATHY_LOCATION_COUNTRY_CODE); + if (str != NULL) + { + g_hash_table_insert (address, + g_strdup (GEOCLUE_ADDRESS_KEY_COUNTRYCODE), str); + DEBUG ("\t - countrycode: %s", str); + } + + str = get_dup_string (location, EMPATHY_LOCATION_COUNTRY); + if (str != NULL) + { + g_hash_table_insert (address, + g_strdup (GEOCLUE_ADDRESS_KEY_COUNTRY), str); + DEBUG ("\t - country: %s", str); + } + + str = get_dup_string (location, EMPATHY_LOCATION_POSTAL_CODE); + if (str != NULL) + { + g_hash_table_insert (address, + g_strdup (GEOCLUE_ADDRESS_KEY_POSTALCODE), str); + DEBUG ("\t - postalcode: %s", str); + } + + str = get_dup_string (location, EMPATHY_LOCATION_REGION); + if (str != NULL) + { + g_hash_table_insert (address, + g_strdup (GEOCLUE_ADDRESS_KEY_REGION), str); + DEBUG ("\t - region: %s", str); + } + + str = get_dup_string (location, EMPATHY_LOCATION_LOCALITY); + if (str != NULL) + { + g_hash_table_insert (address, + g_strdup (GEOCLUE_ADDRESS_KEY_LOCALITY), str); + DEBUG ("\t - locality: %s", str); + } + + str = get_dup_string (location, EMPATHY_LOCATION_STREET); + if (str != NULL) + { + g_hash_table_insert (address, + g_strdup (GEOCLUE_ADDRESS_KEY_STREET), str); + DEBUG ("\t - street: %s", str); + } + + if (g_hash_table_size (address) > 0) + { + g_object_ref (contact); + + geoclue_geocode_address_to_position_async (geocode, address, + geocode_cb, contact); + } + + g_hash_table_unref (address); +} +#endif + +static EmpathyCapabilities +tp_caps_to_capabilities (TpCapabilities *caps) +{ + EmpathyCapabilities capabilities = 0; + guint i; + GPtrArray *classes; + + classes = tp_capabilities_get_channel_classes (caps); + + for (i = 0; i < classes->len; i++) + { + GValueArray *class_struct; + GHashTable *fixed_prop; + GStrv allowed_prop; + TpHandleType handle_type; + const gchar *chan_type; + + class_struct = g_ptr_array_index (classes, i); + tp_value_array_unpack (class_struct, 2, + &fixed_prop, + &allowed_prop); + + handle_type = tp_asv_get_uint32 (fixed_prop, + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL); + if (handle_type != TP_HANDLE_TYPE_CONTACT) + continue; + + chan_type = tp_asv_get_string (fixed_prop, + TP_PROP_CHANNEL_CHANNEL_TYPE); + + if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER)) + { + capabilities |= EMPATHY_CAPABILITIES_FT; + } + else if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE)) + { + const gchar *service; + + service = tp_asv_get_string (fixed_prop, + TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); + + if (!tp_strdiff (service, "rfb")) + capabilities |= EMPATHY_CAPABILITIES_RFB_STREAM_TUBE; + } + else if (!tp_strdiff (chan_type, + TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)) + { + guint j; + + for (j = 0; allowed_prop[j] != NULL; j++) + { + if (!tp_strdiff (allowed_prop[j], + TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_AUDIO)) + capabilities |= EMPATHY_CAPABILITIES_AUDIO; + else if (!tp_strdiff (allowed_prop[j], + TP_PROP_CHANNEL_TYPE_STREAMED_MEDIA_INITIAL_VIDEO)) + capabilities |= EMPATHY_CAPABILITIES_VIDEO; + } + } + } + + return capabilities; +} + +static void +set_capabilities_from_tp_caps (EmpathyContact *self, + TpCapabilities *caps) +{ + EmpathyCapabilities capabilities; + + if (caps == NULL) + return; + + capabilities = tp_caps_to_capabilities (caps); + empathy_contact_set_capabilities (self, capabilities); +} + +static void +contact_set_avatar_from_tp_contact (EmpathyContact *contact) +{ + EmpathyContactPriv *priv = GET_PRIV (contact); + const gchar *mime; + const gchar *token; + GFile *file; + + token = tp_contact_get_avatar_token (priv->tp_contact); + mime = tp_contact_get_avatar_mime_type (priv->tp_contact); + file = tp_contact_get_avatar_file (priv->tp_contact); + + if (file != NULL) + { + EmpathyAvatar *avatar; + gchar *data; + gsize len; + + g_file_load_contents (file, NULL, &data, &len, NULL, NULL); + avatar = empathy_avatar_new ((guchar *) data, len, g_strdup (mime), g_strdup (token), + g_file_get_path (file)); + empathy_contact_set_avatar (contact, avatar); + empathy_avatar_unref (avatar); + } + else + { + empathy_contact_set_avatar (contact, NULL); + } +} + +EmpathyContact * +empathy_contact_dup_from_tp_contact (TpContact *tp_contact) +{ + EmpathyContact *contact = NULL; + + g_return_val_if_fail (TP_IS_CONTACT (tp_contact), NULL); + + if (contacts_table == NULL) + contacts_table = g_hash_table_new (g_direct_hash, g_direct_equal); + else + contact = g_hash_table_lookup (contacts_table, tp_contact); + + if (contact == NULL) + { + contact = empathy_contact_new (tp_contact); + + /* The hash table does not keep any ref. + * contact keeps a ref to tp_contact, and is removed from the table in + * contact_dispose() */ + g_hash_table_insert (contacts_table, tp_contact, contact); + } + else + { + g_object_ref (contact); + } + + return contact; +} +