Post-release version bump
[platform/upstream/folks.git] / folks / persona.vala
1 /*
2  * Copyright (C) 2010 Collabora Ltd.
3  *
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.
8  *
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.
13  *
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/>.
16  *
17  * Authors:
18  *       Travis Reitter <travis.reitter@collabora.co.uk>
19  */
20
21 using GLib;
22
23 /**
24  * Errors which can be thrown when asynchronously setting a property of a
25  * {@link Persona} using a setter method defined on an interface such as
26  * {@link AliasDetails}.
27  *
28  * @since 0.6.2
29  */
30 public errordomain Folks.PropertyError
31 {
32   /**
33    * Property is not writeable for this particular object.
34    *
35    * @since 0.6.2
36    */
37   NOT_WRITEABLE,
38
39   /**
40    * Value was invalid for the property.
41    *
42    * @since 0.6.2
43    */
44   INVALID_VALUE,
45
46   /**
47    * Unknown error when setting the property.
48    *
49    * @since 0.6.2
50    */
51   UNKNOWN_ERROR
52 }
53
54 /**
55  * Represents a "shard" of a person from a single source (a single
56  * {@link Backend}), such as an XMPP contact from Telepathy or a vCard contact
57  * from evolution-data-server.
58  *
59  * All the personas belonging to one physical person are aggregated to form a
60  * single {@link Individual} representing that person.
61  */
62 public abstract class Folks.Persona : Object
63 {
64   private weak Individual? _individual;
65
66   /**
67    * The internal ID used to represent the Persona for linking.
68    *
69    * This is opaque, and shouldn't be parsed or considered meaningful by
70    * clients.
71    *
72    * The internal ID should be unique within a backend, but may not be unique
73    * across backends, so that links can be made between Personas with similar
74    * internal IDs.
75    */
76   /* For example: jabber:foo@xmpp.example.org or joe@example.org */
77   public string iid { get; construct; }
78
79   /**
80    * The universal ID used to represent the Persona outside its {@link Backend}.
81    *
82    * This is opaque, and should only be parsed by clients using
83    * {@link Persona.split_uid}.
84    *
85    * This is the canonical way to refer to any Persona. It is guaranteed to be
86    * unique within the Persona's {@link PersonaStore}.
87    *
88    * A Persona's UID is immutable over the life of the Persona in the backing
89    * store, so a given UID is guaranteed to refer to the same Persona each time
90    * libfolks is used, until the Persona is permanently removed from its backing
91    * store.
92    *
93    * @see Persona.build_uid
94    * @see Persona.split_uid
95    */
96   /* For example: telepathy:jabber:foo@xmpp.example.org or
97    * key-file:relationships.ini:joe@example.org
98    *
99    * It comprises three components, separated by colons:
100    * # {@link Backend.name}
101    * # {@link PersonaStore.id}
102    * # Persona identifier
103    * Each component is escaped by replacing all colons with double underscores
104    * before building the UID.*/
105   public string uid { get; construct; }
106
107   /**
108    * The human-readable, service-specific universal ID used to represent the
109    * Persona.
110    *
111    * For example: `foo@@xmpp.example.org`.
112    *
113    * This should be used whenever the user needs to be presented with a
114    * familiar, service-specific ID. For instance, in a prompt for the user to
115    * select a specific IM contact within an {@link Individual} to begin a chat
116    * with.
117    *
118    * This is not guaranteed to be unique outside of the Persona's
119    * {@link PersonaStore}.
120    *
121    * @since 0.1.13
122    */
123   public string display_id { get; construct; }
124
125   /**
126    * Whether the Persona is the user.
127    *
128    * Iff the Persona represents the user (the person who owns the account in
129    * the respective backend) this is `true`.
130    *
131    * @since 0.3.0
132    */
133   public bool is_user { get; construct; }
134
135   /**
136    * The {@link PersonaStore} which contains this Persona.
137    */
138   public weak PersonaStore store { get; construct; }
139
140   /**
141    * The {@link Individual} which contains this Persona.
142    *
143    * This may be `null`, but should only ever be so when the Persona has just
144    * been created, when its {@link PersonaStore} is being destroyed, or when
145    * it's moving between {@link Individual}s.
146    *
147    * @since 0.6.0
148    */
149   public weak Individual? individual
150     {
151       get
152         {
153           assert (this._individual == null ||
154               ((!) this._individual).personas.contains (this));
155
156           return this._individual;
157         }
158
159       internal set
160         {
161           assert (value == null || ((!) value).personas.contains (this));
162
163           this._individual = value;
164         }
165     }
166
167   /**
168    * The names of the properties of this Persona which are linkable.
169    *
170    * If a property name is in this list, and the Persona is from a
171    * {@link PersonaStore} whose trust level is {@link PersonaStoreTrust.FULL},
172    * the {@link IndividualAggregator} should be able to reliably use the value
173    * of the property from a given Persona instance to link the Persona with
174    * other Personas and form {@link Individual}s.
175    *
176    * Note that {@link Persona.uid} is always implicitly a member of this list,
177    * and doesn't need to be added explicitly.
178    *
179    * This list will have no effect if the Persona's {@link PersonaStore} trust
180    * level is not {@link PersonaStoreTrust.FULL}.
181    *
182    * This property value is guaranteed to be constant for a given persona,
183    * but may vary between personas in the same store.
184    *
185    * @since 0.1.13
186    */
187   public abstract string[] linkable_properties { get; }
188
189   /**
190    * The names of the properties of this Persona which are writeable.
191    *
192    * If a property name is in this list, setting the property should result in
193    * the updated value being stored in the backend's permanent storage (unless
194    * it gets rejected due to being invalid, or a different error occurs).
195    *
196    * It's intended that this property value will be constant for a given Persona
197    * subclass, but this isn't guaranteed; it's possible that Persona subclasses
198    * may vary the value of this property at run time.
199    *
200    * @since 0.6.0
201    */
202   public abstract string[] writeable_properties { get; }
203
204   /**
205    * Callback into the aggregator to manipulate a link mapping.
206    *
207    * This is a callback provided by the {@link IndividualAggregator} whenever
208    * a {@link Persona.linkable_property_to_links} method is called, which should
209    * be called by the `linkable_property_to_links` implementation for each
210    * linkable-property-to-individual mapping it wants to add or remove in the
211    * aggregator.
212    *
213    * @param link the mapping string to be added to the
214    * {@link IndividualAggregator}
215    * @since 0.1.13
216    */
217   public delegate void LinkablePropertyCallback (string link);
218
219   /* FIXME: This code should move to the ImDetails interface as a concrete
220    * method of the interface. However, that depends on bgo#624842 */
221   /**
222    * Produce one or more mapping strings for the given property's value.
223    *
224    * This is a virtual method, to be overridden by subclasses of {@link Persona}
225    * who have linkable properties. Each of their linkable properties should be
226    * handled by their implementation of this function, examining the current
227    * value of the property and calling `callback` with one or more mapping
228    * strings for the property's value. Each of these mapping strings will be
229    * added to the {@link IndividualAggregator}'s link map, related to the
230    * {@link Individual} instance which contains this {@link Persona}.
231    *
232    * @param prop_name the name of the linkable property to use, which must be
233    * listed in {@link Persona.linkable_properties}
234    * @param callback a callback to execute for each of the mapping strings
235    * generated by this property
236    * @see Persona.linkable_properties
237    * @since 0.1.13
238    */
239   public virtual void linkable_property_to_links (string prop_name,
240       LinkablePropertyCallback callback)
241     {
242       /* Backend-specific Persona subclasses should override this if they have
243        * any linkable properties */
244       assert_not_reached ();
245     }
246
247   private static string _escape_uid_component (string component)
248     {
249       /* Escape colons with backslashes */
250       string escaped = component.replace ("\\", "\\\\");
251       return escaped.replace (":", "\\:");
252     }
253
254   private static string _unescape_uid_component (string component)
255     {
256       /* Unescape colons and backslashes */
257       string unescaped = component.replace ("\\:", ":");
258       return unescaped.replace ("\\", "\\\\");
259     }
260
261   /**
262    * Build a UID from the given components.
263    *
264    * Each component is escaped before the UID is built.
265    *
266    * @param backend_name the {@link Backend.name}
267    * @param persona_store_id the {@link PersonaStore.id}
268    * @param persona_id the Persona identifier (backend-specific)
269    * @return a valid UID
270    * @see Persona.split_uid
271    * @since 0.1.13
272    */
273   public static string build_uid (string backend_name,
274       string persona_store_id, string persona_id)
275     {
276       return "%s:%s:%s".printf (Persona._escape_uid_component (backend_name),
277           Persona._escape_uid_component (persona_store_id),
278           Persona._escape_uid_component (persona_id));
279     }
280
281   /**
282    * Split a UID into its component parts.
283    *
284    * Each component is unescaped before being returned. The UID //must// be
285    * correctly formed.
286    *
287    * @param uid a valid UID
288    * @param backend_name the {@link Backend.name}
289    * @param persona_store_id the {@link PersonaStore.id}
290    * @param persona_id the Persona identifier (backend-specific)
291    * @see Persona.build_uid
292    * @since 0.1.13
293    */
294   public static void split_uid (string uid, out string backend_name,
295       out string persona_store_id, out string persona_id)
296     {
297       assert (uid.validate ());
298
299       size_t backend_name_length = 0, persona_store_id_length = 0;
300       var escaped = false;
301       for (unowned string i = uid; i.get_char () != '\0'; i = i.next_char ())
302         {
303           if (i.get_char () == '\\')
304             escaped = !escaped;
305           else if (escaped == false && i.get_char () == ':')
306             {
307               if (backend_name_length == 0)
308                 backend_name_length = ((char*) i) - ((char*) uid);
309               else
310                 persona_store_id_length =
311                   (((char*) i) - ((char*) uid)) - backend_name_length - 1;
312             }
313         }
314
315       assert (backend_name_length != 0 && persona_store_id_length != 0);
316
317       backend_name = Persona._unescape_uid_component (
318           uid.substring (0, (long) backend_name_length));
319       persona_store_id = Persona._unescape_uid_component (
320           ((string) ((char*) uid + backend_name_length + 1)).substring (0,
321               (long) persona_store_id_length));
322       persona_id = Persona._unescape_uid_component (
323           ((string) ((char*) uid + backend_name_length +
324               persona_store_id_length + 2)));
325     }
326 }