2 * Copyright (C) 2010 Collabora Ltd.
4 * This library is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 2.1 of the License, or
7 * (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
18 * Travis Reitter <travis.reitter@collabora.co.uk>
24 * Represents a "shard" of a person from a single source (a single
25 * {@link Backend}), such as an XMPP contact from Telepathy or a vCard contact
26 * from evolution-data-server.
28 * All the personas belonging to one physical person are aggregated to form a
29 * single {@link Individual} representing that person.
31 public abstract class Folks.Persona : Object
34 * The internal ID used to represent the Persona for linking.
36 * This is opaque, and shouldn't be parsed or considered meaningful by
39 * The internal ID should be unique within a backend, but may not be unique
40 * across backends, so that links can be made between Personas with similar
43 /* For example: jabber:foo@xmpp.example.org or joe@example.org */
44 public string iid { get; construct; }
47 * The universal ID used to represent the Persona outside its {@link Backend}.
49 * This is opaque, and should only be parsed by clients using
50 * {@link Persona.split_uid}.
52 * This is the canonical way to refer to any Persona. It is guaranteed to be
53 * unique within the Persona's {@link PersonaStore}.
55 * A Persona's UID is immutable over the life of the Persona in the backing
56 * store, so a given UID is guaranteed to refer to the same Persona each time
57 * libfolks is used, until the Persona is permanently removed from its backing
60 * @see Persona.build_uid
61 * @see Persona.split_uid
63 /* For example: telepathy:jabber:foo@xmpp.example.org or
64 * key-file:relationships.ini:joe@example.org
66 * It comprises three components, separated by colons:
67 * # {@link Backend.name}
68 * # {@link PersonaStore.id}
69 * # Persona identifier
70 * Each component is escaped by replacing all colons with double underscores
71 * before building the UID.*/
72 public string uid { get; construct; }
75 * The human-readable, service-specific universal ID used to represent the
78 * For example: `foo@@xmpp.example.org`.
80 * This should be used whenever the user needs to be presented with a
81 * familiar, service-specific ID. For instance, in a prompt for the user to
82 * select a specific IM contact within an {@link Individual} to begin a chat
85 * This is not guaranteed to be unique outside of the Persona's
86 * {@link PersonaStore}.
90 public string display_id { get; construct; }
93 * Whether the Persona is the user.
95 * Iff the Persona represents the user (the person who owns the account in
96 * the respective backend) this is `true`.
100 public bool is_user { get; construct; }
103 * The {@link PersonaStore} which contains this Persona.
105 public weak PersonaStore store { get; construct; }
108 * The names of the properties of this Persona which are linkable.
110 * If a property name is in this list, and the Persona is from a
111 * {@link PersonaStore} whose trust level is {@link PersonaStoreTrust.FULL},
112 * the {@link IndividualAggregator} should be able to reliably use the value
113 * of the property from a given Persona instance to link the Persona with
114 * other Personas and form {@link Individual}s.
116 * Note that {@link Persona.uid} is always implicitly a member of this list,
117 * and doesn't need to be added explicitly.
119 * This list will have no effect if the Persona's {@link PersonaStore} trust
120 * level is not {@link PersonaStoreTrust.FULL}.
124 public abstract string[] linkable_properties { get; }
127 * Callback into the aggregator to manipulate a link mapping.
129 * This is a callback provided by the {@link IndividualAggregator} whenever
130 * a {@link Persona.linkable_property_to_links} method is called, which should
131 * be called by the `linkable_property_to_links` implementation for each
132 * linkable-property-to-individual mapping it wants to add or remove in the
135 * @param link the mapping string to be added to the
136 * {@link IndividualAggregator}
139 public delegate void LinkablePropertyCallback (string link);
141 /* FIXME: This code should move to the ImDetails interface as a concrete
142 * method of the interface. However, that depends on bgo#624842 */
144 * Produce one or more mapping strings for the given property's value.
146 * This is a virtual method, to be overridden by subclasses of {@link Persona}
147 * who have linkable properties. Each of their linkable properties should be
148 * handled by their implementation of this function, examining the current
149 * value of the property and calling `callback` with one or more mapping
150 * strings for the property's value. Each of these mapping strings will be
151 * added to the {@link IndividualAggregator}'s link map, related to the
152 * {@link Individual} instance which contains this {@link Persona}.
154 * @param prop_name the name of the linkable property to use, which must be
155 * listed in {@link Persona.linkable_properties}
156 * @param callback a callback to execute for each of the mapping strings
157 * generated by this property
158 * @see Persona.linkable_properties
161 public virtual void linkable_property_to_links (string prop_name,
162 LinkablePropertyCallback callback)
164 /* Backend-specific Persona subclasses should override this if they have
165 * any linkable properties */
166 assert_not_reached ();
169 private static string _escape_uid_component (string component)
171 /* Escape colons with backslashes */
172 string escaped = component.replace ("\\", "\\\\");
173 return escaped.replace (":", "\\:");
176 private static string _unescape_uid_component (string component)
178 /* Unescape colons and backslashes */
179 string unescaped = component.replace ("\\:", ":");
180 return unescaped.replace ("\\", "\\\\");
184 * Build a UID from the given components.
186 * Each component is escaped before the UID is built.
188 * @param backend_name the {@link Backend.name}
189 * @param persona_store_id the {@link PersonaStore.id}
190 * @param persona_id the Persona identifier (backend-specific)
191 * @return a valid UID
192 * @see Persona.split_uid
195 public static string build_uid (string backend_name,
196 string persona_store_id, string persona_id)
198 return "%s:%s:%s".printf (Persona._escape_uid_component (backend_name),
199 Persona._escape_uid_component (persona_store_id),
200 Persona._escape_uid_component (persona_id));
204 * Split a UID into its component parts.
206 * Each component is unescaped before being returned. The UID //must// be
209 * @param uid a valid UID
210 * @param backend_name the {@link Backend.name}
211 * @param persona_store_id the {@link PersonaStore.id}
212 * @param persona_id the Persona identifier (backend-specific)
213 * @see Persona.build_uid
216 public static void split_uid (string uid, out string backend_name,
217 out string persona_store_id, out string persona_id)
219 assert (uid.validate ());
221 size_t backend_name_length = 0, persona_store_id_length = 0;
223 for (unowned string i = uid; i.get_char () != '\0'; i = i.next_char ())
225 if (i.get_char () == '\\')
227 else if (escaped == false && i.get_char () == ':')
229 if (backend_name_length == 0)
230 backend_name_length = ((char*) i) - ((char*) uid);
232 persona_store_id_length =
233 (((char*) i) - ((char*) uid)) - backend_name_length - 1;
237 assert (backend_name_length != 0 && persona_store_id_length != 0);
239 backend_name = Persona._unescape_uid_component (
240 uid.substring (0, (long) backend_name_length));
241 persona_store_id = Persona._unescape_uid_component (
242 ((string) ((char*) uid + backend_name_length + 1)).substring (0,
243 (long) persona_store_id_length));
244 persona_id = Persona._unescape_uid_component (
245 ((string) ((char*) uid + backend_name_length +
246 persona_store_id_length + 2)));