Post-release version bump
[platform/upstream/folks.git] / folks / persona-store.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 using Gee;
23
24 /**
25  * Trust level for a {@link PersonaStore}'s {@link Persona}s for linking
26  * purposes.
27  *
28  * @since 0.1.13
29  */
30 public enum Folks.PersonaStoreTrust
31 {
32   /**
33    * The {@link Persona}s aren't trusted at all, and cannot be linked.
34    *
35    * This should be used for {@link PersonaStore}s where even the
36    * {@link Persona} UID could be maliciously edited to corrupt {@link Persona}
37    * links, or where the UID changes regularly.
38    *
39    * @since 0.1.13
40    */
41   NONE,
42
43   /**
44    * Only the {@link Persona.uid} property is trusted for linking.
45    *
46    * In practice, this means that {@link Persona}s from this
47    * {@link PersonaStore} will not contribute towards the linking process, but
48    * can be linked together by their UIDs using data from {@link Persona}s from
49    * a fully-trusted {@link PersonaStore}.
50    *
51    * @since 0.1.13
52    */
53   PARTIAL,
54
55   /**
56    * Every property in {@link Persona.linkable_properties} is trusted.
57    *
58    * This should only be used for user-controlled {@link PersonaStore}s, as if a
59    * remote store is compromised, malicious changes could be made to its data
60    * which corrupt the user's {@link Persona} links.
61    *
62    * @since 0.1.13
63    */
64   FULL
65 }
66 /**
67  * Errors from {@link PersonaStore}s.
68  */
69 public errordomain Folks.PersonaStoreError
70 {
71   /**
72    * An argument to the method was invalid.
73    */
74   INVALID_ARGUMENT,
75
76   /**
77    * Creation of a {@link Persona} failed.
78    */
79   CREATE_FAILED,
80
81   /**
82    * Such an operation may not be performed on a {@link Persona} with
83    * {@link Persona.is_user} set to `true`.
84    *
85    * @since 0.3.0
86    */
87   UNSUPPORTED_ON_USER,
88
89   /**
90    * The {@link PersonaStore} was offline (ie, this is a temporary failure).
91    *
92    * @since 0.3.0
93    */
94   STORE_OFFLINE,
95
96   /**
97    * The {@link PersonaStore} doesn't support write operations.
98    *
99    * @since 0.3.4
100    */
101   READ_ONLY,
102
103   /**
104    * The operation was denied due to not having sufficient permissions.
105    *
106    * @since 0.6.0
107    */
108   PERMISSION_DENIED,
109
110   /**
111    * Removal of a {@link Persona} failed. This is a generic error which is used
112    * if no other error code (such as, e.g.,
113    * {@link PersonaStoreError.PERMISSION_DENIED}) is applicable.
114    *
115    * @since 0.6.0
116    */
117   REMOVE_FAILED,
118
119   /**
120    * Such an operation may only be performed on a {@link Persona} with
121    * {@link Persona.is_user} set to `true`.
122    *
123    * @since 0.6.4
124    */
125   UNSUPPORTED_ON_NON_USER,
126 }
127
128 /**
129  * Definition of the available fields to be looked up with
130  * {@link PersonaStore.detail_key}.
131  *
132  * @since 0.5.0
133  */
134 /* NOTE: Must be kept in sync with
135  * {@link Folks.PersonaStore._PERSONA_DETAIL}. */
136 public enum Folks.PersonaDetail
137 {
138   /**
139    * Invalid field for use in error returns.
140    *
141    * @since 0.6.2
142    */
143   INVALID = -1,
144
145   /**
146    * Field for {@link AliasDetails.alias}.
147    *
148    * @since 0.5.0
149    */
150   ALIAS = 0,
151
152   /**
153    * Field for {@link AvatarDetails.avatar}.
154    *
155    * @since 0.5.0
156    */
157   AVATAR,
158
159   /**
160    * Field for {@link BirthdayDetails.birthday}.
161    *
162    * @since 0.5.0
163    */
164   BIRTHDAY,
165
166   /**
167    * Field for {@link EmailDetails.email_addresses}.
168    *
169    * @since 0.5.0
170    */
171   EMAIL_ADDRESSES,
172
173   /**
174    * Field for {@link NameDetails.full_name}.
175    *
176    * @since 0.5.0
177    */
178   FULL_NAME,
179
180   /**
181    * Field for {@link GenderDetails.gender}.
182    *
183    * @since 0.5.0
184    */
185   GENDER,
186
187   /**
188    * Field for {@link ImDetails.im_addresses}.
189    *
190    * @since 0.5.0
191    */
192   IM_ADDRESSES,
193
194   /**
195    * Field for {@link FavouriteDetails.is_favourite}.
196    *
197    * @since 0.5.0
198    */
199   IS_FAVOURITE,
200
201   /**
202    * Field for {@link LocalIdDetails.local_ids}.
203    *
204    * @since 0.5.0
205    */
206   LOCAL_IDS,
207
208   /**
209    * Field for {@link NameDetails.nickname}.
210    *
211    * @since 0.5.0
212    */
213   NICKNAME,
214
215   /**
216    * Field for {@link NoteDetails.notes}.
217    *
218    * @since 0.5.0
219    */
220   NOTES,
221
222   /**
223    * Field for {@link PhoneDetails.phone_numbers}.
224    *
225    * @since 0.5.0
226    */
227   PHONE_NUMBERS,
228
229   /**
230    * Field for {@link PostalAddressDetails.postal_addresses}.
231    *
232    * @since 0.5.0
233    */
234   POSTAL_ADDRESSES,
235
236   /**
237    * Field for {@link RoleDetails.roles}.
238    *
239    * @since 0.5.0
240    */
241   ROLES,
242
243   /**
244    * Field for {@link NameDetails.structured_name}.
245    *
246    * @since 0.5.0
247    */
248   STRUCTURED_NAME,
249
250   /**
251    * Field for {@link UrlDetails.urls}.
252    *
253    * @since 0.5.0
254    */
255   URLS,
256
257   /**
258    * Field for {@link WebServiceDetails.web_service_addresses}.
259    *
260    * @since 0.5.0
261    */
262   WEB_SERVICE_ADDRESSES,
263
264   /**
265    * Field for {@link GroupDetails.groups}.
266    *
267    * @since 0.6.2
268    */
269   GROUPS
270 }
271
272 /**
273  * A store for {@link Persona}s.
274  *
275  * After creating a PersonaStore instance, you must connect to the
276  * {@link PersonaStore.personas_changed} signal, //then// call
277  * {@link PersonaStore.prepare}, otherwise a race condition may occur between
278  * emission of {@link PersonaStore.personas_changed} and your code connecting to
279  * it.
280  */
281 public abstract class Folks.PersonaStore : Object
282 {
283   construct
284     {
285       debug ("Constructing PersonaStore â€˜%s’ (%p)", this.id, this);
286     }
287
288   ~PersonaStore ()
289     {
290       debug ("Destroying PersonaStore â€˜%s’ (%p)", this.id, this);
291     }
292
293   /**
294    * The following list of properties are the basic keys
295    * that each PersonaStore with write capabilities should
296    * support for {@link PersonaStore.add_persona_from_details}.
297    *
298    * Note that these aren't the only valid keys; backends are
299    * allowed to support keys beyond the ones defined here
300    * which might be specific to the backend in question.
301    *
302    * NOTE: MUST be kept in sync with {@link Folks.PersonaDetail}.
303    *
304    * @since 0.5.0
305    */
306   private static const string _PERSONA_DETAIL[] = {
307     "alias",
308     "avatar",
309     "birthday",
310     "email-addresses",
311     "full-name",
312     "gender",
313     "im-addresses",
314     "is-favourite",
315     "local-ids",
316     "nickname",
317     "notes",
318     "phone-numbers",
319     "postal-addresses",
320     "roles",
321     "structured-name",
322     "urls",
323     "web-service-addresses",
324     "groups"
325   };
326
327   /**
328    * Returns the key corresponding to @detail, for use in
329    * the details param of {@link PersonaStore.add_persona_from_details}.
330    *
331    * @param detail the {@link PersonaDetail} to lookup
332    * @return the corresponding property name, or `null` if `detail` is invalid
333    *
334    * @since 0.5.0
335    */
336   public static unowned string? detail_key (Folks.PersonaDetail detail)
337     {
338       if (detail == PersonaDetail.INVALID ||
339           detail >= PersonaStore._PERSONA_DETAIL.length)
340         {
341           return null;
342         }
343
344       return PersonaStore._PERSONA_DETAIL[detail];
345     }
346
347   /**
348    * Emitted when one or more {@link Persona}s are added to or removed from
349    * the store.
350    *
351    * This will not be emitted until after {@link PersonaStore.prepare} has been
352    * called.
353    *
354    * @param added a set of {@link Persona}s which have been removed
355    * @param removed a set of {@link Persona}s which have been removed
356    * @param message a string message from the backend, if any
357    * @param actor the {@link Persona} who made the change, if known
358    * @param reason the reason for the change
359    *
360    * @since 0.5.1
361    */
362   public signal void personas_changed (Set<Persona> added,
363       Set<Persona> removed,
364       string? message,
365       Persona? actor,
366       GroupDetails.ChangeReason reason);
367
368   /* Emit the personas-changed signal, turning null parameters into empty sets
369    * and only passing a read-only view to the signal handlers. */
370   protected void _emit_personas_changed (Set<Persona>? added,
371       Set<Persona>? removed,
372       string? message = null,
373       Persona? actor = null,
374       GroupDetails.ChangeReason reason = GroupDetails.ChangeReason.NONE)
375     {
376       var _added = added;
377       var _removed = removed;
378
379       if ((added == null || ((!) added).size == 0) &&
380           (removed == null || ((!) removed).size == 0))
381         {
382           /* Don't bother signalling if nothing's changed */
383           return;
384         }
385       else if (added == null)
386         {
387           _added = new HashSet<Persona> ();
388         }
389       else if (removed == null)
390         {
391           _removed = new HashSet<Persona> ();
392         }
393
394       // We've now guaranteed that both _added and _removed are non-null.
395       this.personas_changed (((!) _added).read_only_view,
396           ((!) _removed).read_only_view, message, actor, reason);
397     }
398
399   /**
400    * Emitted when the backing store for this PersonaStore has been removed.
401    *
402    * At this point, the PersonaStore and all its {@link Persona}s are invalid,
403    * so any client referencing it should unreference it.
404    *
405    * This will not be emitted until after {@link PersonaStore.prepare} has been
406    * called.
407    */
408   public abstract signal void removed ();
409
410   /**
411    * The type of PersonaStore this is.
412    *
413    * This is the same for all PersonaStores provided by a given {@link Backend}.
414    *
415    * This is guaranteed to always be available; even before
416    * {@link PersonaStore.prepare} is called.
417    */
418   public abstract string type_id
419     {
420       /* Note: the type_id must not contain colons because the primary writeable
421        * store is configured, either via GConf or the FOLKS_PRIMARY_STORE
422        * env variable, with a string of the form 'type_id:store_id'. */
423       get;
424     }
425
426   /**
427    * The human-readable, service-specific name used to represent the
428    * PersonaStore to the user.
429    *
430    * For example: `foo@@xmpp.example.org`.
431    *
432    * This should be used whenever the user needs to be presented with a
433    * familiar, service-specific name. For instance, in a prompt for the user to
434    * select a specific IM account from which to initiate a chat.
435    *
436    * This is not guaranteed to be unique even within this PersonaStore's
437    * {@link Backend}.
438    *
439    * @since 0.1.13
440    */
441   public string display_name { get; construct; }
442
443   /**
444    * The instance identifier for this PersonaStore.
445    *
446    * Since each {@link Backend} can provide multiple different PersonaStores
447    * for different accounts or servers (for example), they each need an ID
448    * which is unique within the backend.
449    */
450   public string id { get; construct; }
451
452   /**
453    * The {@link Persona}s exposed by this PersonaStore.
454    *
455    * @since 0.5.1
456    */
457   public abstract Map<string, Persona> personas { get; }
458
459   /**
460    * Whether this {@link PersonaStore} can add {@link Persona}s.
461    *
462    * @since 0.3.1
463    */
464   public abstract MaybeBool can_add_personas { get; default = MaybeBool.UNSET; }
465
466   /**
467    * Whether this {@link PersonaStore} can set the alias of {@link Persona}s.
468    *
469    * @since 0.3.1
470    */
471   [Deprecated (since = "0.6.3.1",
472       replacement = "PersonaStore.always_writeable_properties")]
473   public abstract MaybeBool can_alias_personas
474     {
475       get;
476       default = MaybeBool.UNSET;
477     }
478
479   /**
480    * Whether this {@link PersonaStore} can set the groups of {@link Persona}s.
481    *
482    * @since 0.3.1
483    */
484   [Deprecated (since = "0.6.3.1",
485       replacement = "PersonaStore.always_writeable_properties")]
486   public abstract MaybeBool can_group_personas
487     {
488       get;
489       default = MaybeBool.UNSET;
490     }
491
492   /**
493    * Whether this {@link PersonaStore} can remove {@link Persona}s.
494    *
495    * @since 0.3.1
496    */
497   public abstract MaybeBool can_remove_personas
498     {
499       get;
500       default = MaybeBool.UNSET;
501     }
502
503   /**
504    * Whether {@link PersonaStore.prepare} has successfully completed for this
505    * store.
506    *
507    * @since 0.3.0
508    */
509   public abstract bool is_prepared { get; default = false; }
510
511   /**
512    * Whether the store has reached a quiescent state. This will happen at some
513    * point after {@link PersonaStore.prepare} has successfully completed for the
514    * store. A store is in a quiescent state when all the {@link Persona}s that
515    * it originally knows about have been loaded.
516    *
517    * It's guaranteed that this property's value will only ever change after
518    * {@link IndividualAggregator.is_prepared} has changed to `true`.
519    *
520    * @since 0.6.2
521    */
522   public abstract bool is_quiescent { get; default = false; }
523
524    /**
525    * Whether the PersonaStore is writeable.
526    *
527    * Only if a PersonaStore is writeable will its {@link Persona}s be updated by
528    * changes to the {@link Individual}s containing them, and those changes then
529    * be written out to the relevant backing store.
530    *
531    * If this property is `false`, it doesn't mean that {@link Persona}s in this
532    * persona store aren't writeable at all. If their properties are updated
533    * through the {@link Persona}, rather than through the {@link Individual}
534    * containing that persona, changes may be propagated to the backing store.
535    *
536    * PersonaStores must not set this property themselves; it will be set as
537    * appropriate by the {@link IndividualAggregator}.
538    *
539    * @since 0.1.13
540    */
541   [Deprecated (since = "0.6.3",
542       replacement = "PersonaStore.is_primary_store")]
543   public bool is_writeable { get; set; default = false; }
544
545   /**
546    * The trust level of the PersonaStore for linking.
547    *
548    * Each {@link PersonaStore} is assigned a trust level by the
549    * IndividualAggregator, designating whether to trust the properties of its
550    * {@link Persona}s for linking to produce {@link Individual}s.
551    *
552    * @see PersonaStoreTrust
553    * @since 0.1.13
554    */
555   public PersonaStoreTrust trust_level
556     {
557       get; set; default = PersonaStoreTrust.NONE;
558     }
559
560   /**
561    * The names of the properties of the {@link Persona}s in this store which are
562    * always writeable.
563    *
564    * If a property name is in this list, setting the property on a persona
565    * should result in the updated value being stored in the backend's permanent
566    * storage (unless it gets rejected due to being invalid, or a different error
567    * occurs).
568    *
569    * This property value is guaranteed to be constant for a given persona store,
570    * but may vary between persona stores in the same backend. It's guaranteed
571    * that this will always be a subset of the value of
572    * {@link Persona.writeable_properties} for the personas in this persona
573    * store.
574    *
575    * @since 0.6.2
576    */
577   public abstract string[] always_writeable_properties { get; }
578
579   /**
580    * Prepare the PersonaStore for use.
581    *
582    * This connects the PersonaStore to whichever backend-specific services it
583    * requires to be able to provide {@link Persona}s. This should be called
584    * //after// connecting to the {@link PersonaStore.personas_changed} signal,
585    * or a race condition could occur, with the signal being emitted before your
586    * code has connected to it, and {@link Persona}s getting "lost" as a result.
587    *
588    * This is normally handled transparently by the {@link IndividualAggregator}.
589    *
590    * If this function throws an error, the PersonaStore will not be functional.
591    *
592    * This function is guaranteed to be idempotent (since version 0.3.0).
593    *
594    * Concurrent calls to this function from different threads will block until
595    * preparation has completed. However, concurrent calls to this function from
596    * a single thread might not, i.e. the first call will block but subsequent
597    * calls might return before the first one. (Though they will be safe in every
598    * other respect.)
599    *
600    * @since 0.1.11
601    */
602   public abstract async void prepare () throws GLib.Error;
603
604   /**
605    * Flush any pending changes to the PersonaStore's backing store.
606    *
607    * PersonaStores may (transparently) implement caching or I/O queueing which
608    * means that changes to their {@link Persona}s may not be immediately written
609    * to the PersonaStore's backing store. Calling this function will force all
610    * pending changes to be flushed to the backing store.
611    *
612    * This must not be called before {@link PersonaStore.prepare}.
613    *
614    * @since 0.1.17
615    */
616   public virtual async void flush ()
617     {
618       /* Default implementation doesn't have to do anything */
619     }
620
621   /**
622    * Add a new {@link Persona} to the PersonaStore.
623    *
624    * The {@link Persona} will be created by the PersonaStore backend from the
625    * key-value pairs given in `details`.
626    *
627    * All additions through this function will later be emitted through the
628    * personas-changed signal to be notified of the new {@link Persona}. The
629    * return value is purely for convenience, since it can be complicated to
630    * correlate the provided details with the final Persona.
631    *
632    * If the store is offline (or {@link PersonaStore.prepare()} hasn't yet been
633    * called successfully), this function will throw
634    * {@link PersonaStoreError.STORE_OFFLINE}. It's the responsibility of the
635    * caller to cache details and re-try this function if it wishes to make
636    * offline adds work.
637    *
638    * If the details are not recognised or are invalid,
639    * {@link PersonaStoreError.INVALID_ARGUMENT} will be thrown. A default set
640    * of possible details are defined by {@link Folks.PersonaDetail} but backends
641    * can either support a subset or superset of the suggested defaults.
642    *
643    * If a {@link Persona} with the given details already exists in the store, no
644    * error will be thrown and this function will return `null`.
645    *
646    * @param details a key-value map of details to use in creating the new
647    * {@link Persona}
648    *
649    * @return the new {@link Persona} or `null` if the corresponding Persona
650    * already existed. If non-`null`, the new {@link Persona} will also be
651    * amongst the {@link Persona}(s) in a future emission of
652    * {@link PersonaStore.personas_changed}.
653    */
654   public abstract async Persona? add_persona_from_details (
655       HashTable<string, Value?> details) throws Folks.PersonaStoreError;
656
657   /**
658    * Remove a {@link Persona} from the PersonaStore.
659    *
660    * It isn't guaranteed that the Persona will actually be removed by the time
661    * this asynchronous function finishes. The successful removal of the Persona
662    * will be signalled through emission of
663    * {@link PersonaStore.personas_changed}.
664    *
665    * If the store is offline (or {@link PersonaStore.prepare()} hasn't yet been
666    * called successfully), this function will throw
667    * {@link PersonaStoreError.STORE_OFFLINE}. It's the responsibility of the
668    * caller to cache details and re-try this function if it wishes to make
669    * offline removals work.
670    *
671    * @param persona the {@link Persona} to remove
672    * @since 0.1.11
673    */
674   public abstract async void remove_persona (Persona persona)
675       throws Folks.PersonaStoreError;
676
677   /**
678    * Whether this {@link PersonaStore} is the primary store which is
679    * to be used for linking {@link Persona}s and such.
680    *
681    * @since 0.6.3
682    */
683   public bool is_primary_store { get; internal set; default = false; }
684
685   /**
686    * Whether this {@link PersonaStore} has been marked as the default
687    * store (in its backend) by the user. I.e.: a PersonaStore for the e-d-s
688    * backend would set this to true if it represents the default address book.
689    *
690    * @since 0.6.3
691    */
692   public bool is_user_set_default { get; internal set; default = false; }
693 }