/* * Copyright (C) 2010 Collabora Ltd. * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . * * Authors: * Travis Reitter */ using GLib; /** * Represents a "shard" of a person from a single source (a single * {@link Backend}), such as an XMPP contact from Telepathy or a vCard contact * from evolution-data-server. * * All the personas belonging to one physical person are aggregated to form a * single {@link Individual} representing that person. */ public abstract class Folks.Persona : Object { /** * The internal ID used to represent the Persona for linking. * * This is opaque, and shouldn't be parsed or considered meaningful by * clients. * * The internal ID should be unique within a backend, but may not be unique * across backends, so that links can be made between Personas with similar * internal IDs. */ /* For example: jabber:foo@xmpp.example.org or joe@example.org */ public string iid { get; construct; } /** * The universal ID used to represent the Persona outside its {@link Backend}. * * This is opaque, and should only be parsed by clients using * {@link Persona.split_uid}. * * This is the canonical way to refer to any Persona. It is guaranteed to be * unique within the Persona's {@link PersonaStore}. * * A Persona's UID is immutable over the life of the Persona in the backing * store, so a given UID is guaranteed to refer to the same Persona each time * libfolks is used, until the Persona is permanently removed from its backing * store. * * @see Persona.build_uid * @see Persona.split_uid */ /* For example: telepathy:jabber:foo@xmpp.example.org or * key-file:relationships.ini:joe@example.org * * It comprises three components, separated by colons: * # {@link Backend.name} * # {@link PersonaStore.id} * # Persona identifier * Each component is escaped by replacing all colons with double underscores * before building the UID.*/ public string uid { get; construct; } /** * The human-readable, service-specific universal ID used to represent the * Persona. * * For example: `foo@@xmpp.example.org`. * * This should be used whenever the user needs to be presented with a * familiar, service-specific ID. For instance, in a prompt for the user to * select a specific IM contact within an {@link Individual} to begin a chat * with. * * This is not guaranteed to be unique outside of the Persona's * {@link PersonaStore}. * * @since 0.1.13 */ public string display_id { get; construct; } /** * Whether the Persona is the user. * * Iff the Persona represents the user (the person who owns the account in * the respective backend) this is `true`. * * @since 0.3.0 */ public bool is_user { get; construct; } /** * The {@link PersonaStore} which contains this Persona. */ public weak PersonaStore store { get; construct; } /** * The names of the properties of this Persona which are linkable. * * If a property name is in this list, and the Persona is from a * {@link PersonaStore} whose trust level is {@link PersonaStoreTrust.FULL}, * the {@link IndividualAggregator} should be able to reliably use the value * of the property from a given Persona instance to link the Persona with * other Personas and form {@link Individual}s. * * Note that {@link Persona.uid} is always implicitly a member of this list, * and doesn't need to be added explicitly. * * This list will have no effect if the Persona's {@link PersonaStore} trust * level is not {@link PersonaStoreTrust.FULL}. * * @since 0.1.13 */ public abstract string[] linkable_properties { get; } /** * Callback into the aggregator to manipulate a link mapping. * * This is a callback provided by the {@link IndividualAggregator} whenever * a {@link Persona.linkable_property_to_links} method is called, which should * be called by the `linkable_property_to_links` implementation for each * linkable-property-to-individual mapping it wants to add or remove in the * aggregator. * * @param link the mapping string to be added to the * {@link IndividualAggregator} * @since 0.1.13 */ public delegate void LinkablePropertyCallback (string link); /* FIXME: This code should move to the ImDetails interface as a concrete * method of the interface. However, that depends on bgo#624842 */ /** * Produce one or more mapping strings for the given property's value. * * This is a virtual method, to be overridden by subclasses of {@link Persona} * who have linkable properties. Each of their linkable properties should be * handled by their implementation of this function, examining the current * value of the property and calling `callback` with one or more mapping * strings for the property's value. Each of these mapping strings will be * added to the {@link IndividualAggregator}'s link map, related to the * {@link Individual} instance which contains this {@link Persona}. * * @param prop_name the name of the linkable property to use, which must be * listed in {@link Persona.linkable_properties} * @param callback a callback to execute for each of the mapping strings * generated by this property * @see Persona.linkable_properties * @since 0.1.13 */ public virtual void linkable_property_to_links (string prop_name, LinkablePropertyCallback callback) { /* Backend-specific Persona subclasses should override this if they have * any linkable properties */ assert_not_reached (); } private static string _escape_uid_component (string component) { /* Escape colons with backslashes */ string escaped = component.replace ("\\", "\\\\"); return escaped.replace (":", "\\:"); } private static string _unescape_uid_component (string component) { /* Unescape colons and backslashes */ string unescaped = component.replace ("\\:", ":"); return unescaped.replace ("\\", "\\\\"); } /** * Build a UID from the given components. * * Each component is escaped before the UID is built. * * @param backend_name the {@link Backend.name} * @param persona_store_id the {@link PersonaStore.id} * @param persona_id the Persona identifier (backend-specific) * @return a valid UID * @see Persona.split_uid * @since 0.1.13 */ public static string build_uid (string backend_name, string persona_store_id, string persona_id) { return "%s:%s:%s".printf (Persona._escape_uid_component (backend_name), Persona._escape_uid_component (persona_store_id), Persona._escape_uid_component (persona_id)); } /** * Split a UID into its component parts. * * Each component is unescaped before being returned. The UID //must// be * correctly formed. * * @param uid a valid UID * @param backend_name the {@link Backend.name} * @param persona_store_id the {@link PersonaStore.id} * @param persona_id the Persona identifier (backend-specific) * @see Persona.build_uid * @since 0.1.13 */ public static void split_uid (string uid, out string backend_name, out string persona_store_id, out string persona_id) { assert (uid.validate ()); size_t backend_name_length = 0, persona_store_id_length = 0; var escaped = false; for (unowned string i = uid; i.get_char () != '\0'; i = i.next_char ()) { if (i.get_char () == '\\') escaped = !escaped; else if (escaped == false && i.get_char () == ':') { if (backend_name_length == 0) backend_name_length = ((char*) i) - ((char*) uid); else persona_store_id_length = (((char*) i) - ((char*) uid)) - backend_name_length - 1; } } assert (backend_name_length != 0 && persona_store_id_length != 0); backend_name = Persona._unescape_uid_component ( uid.substring (0, (long) backend_name_length)); persona_store_id = Persona._unescape_uid_component ( ((string) ((char*) uid + backend_name_length + 1)).substring (0, (long) persona_store_id_length)); persona_id = Persona._unescape_uid_component ( ((string) ((char*) uid + backend_name_length + persona_store_id_length + 2))); } }