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>
25 * Trust level for a {@link PersonaStore}'s {@link Persona}s for linking
30 public enum Folks.PersonaStoreTrust
33 * The {@link Persona}s aren't trusted at all, and cannot be linked.
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.
44 * Only the {@link Persona.uid} property is trusted for linking.
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}.
56 * Every property in {@link Persona.linkable_properties} is trusted.
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.
67 * Errors from {@link PersonaStore}s.
69 public errordomain Folks.PersonaStoreError
72 * An argument to the method was invalid.
77 * Creation of a {@link Persona} failed.
82 * Such an operation may not be performed on a {@link Persona} with
83 * {@link Persona.is_user} set to `true`.
90 * The {@link PersonaStore} was offline (ie, this is a temporary failure).
97 * The {@link PersonaStore} doesn't support write operations.
104 * The operation was denied due to not having sufficient permissions.
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.
120 * Such an operation may only be performed on a {@link Persona} with
121 * {@link Persona.is_user} set to `true`.
125 UNSUPPORTED_ON_NON_USER,
129 * Definition of the available fields to be looked up with
130 * {@link PersonaStore.detail_key}.
134 /* NOTE: Must be kept in sync with
135 * {@link Folks.PersonaStore._PERSONA_DETAIL}. */
136 public enum Folks.PersonaDetail
139 * Invalid field for use in error returns.
146 * Field for {@link AliasDetails.alias}.
153 * Field for {@link AvatarDetails.avatar}.
160 * Field for {@link BirthdayDetails.birthday}.
167 * Field for {@link EmailDetails.email_addresses}.
174 * Field for {@link NameDetails.full_name}.
181 * Field for {@link GenderDetails.gender}.
188 * Field for {@link ImDetails.im_addresses}.
195 * Field for {@link FavouriteDetails.is_favourite}.
202 * Field for {@link LocalIdDetails.local_ids}.
209 * Field for {@link NameDetails.nickname}.
216 * Field for {@link NoteDetails.notes}.
223 * Field for {@link PhoneDetails.phone_numbers}.
230 * Field for {@link PostalAddressDetails.postal_addresses}.
237 * Field for {@link RoleDetails.roles}.
244 * Field for {@link NameDetails.structured_name}.
251 * Field for {@link UrlDetails.urls}.
258 * Field for {@link WebServiceDetails.web_service_addresses}.
262 WEB_SERVICE_ADDRESSES,
265 * Field for {@link GroupDetails.groups}.
273 * A store for {@link Persona}s.
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
281 public abstract class Folks.PersonaStore : Object
285 debug ("Constructing PersonaStore ‘%s’ (%p)", this.id, this);
290 debug ("Destroying PersonaStore ‘%s’ (%p)", this.id, this);
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}.
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.
302 * NOTE: MUST be kept in sync with {@link Folks.PersonaDetail}.
306 private static const string _PERSONA_DETAIL[] = {
323 "web-service-addresses",
328 * Returns the key corresponding to @detail, for use in
329 * the details param of {@link PersonaStore.add_persona_from_details}.
331 * @param detail the {@link PersonaDetail} to lookup
332 * @return the corresponding property name, or `null` if `detail` is invalid
336 public static unowned string? detail_key (Folks.PersonaDetail detail)
338 if (detail == PersonaDetail.INVALID ||
339 detail >= PersonaStore._PERSONA_DETAIL.length)
344 return PersonaStore._PERSONA_DETAIL[detail];
348 * Emitted when one or more {@link Persona}s are added to or removed from
351 * This will not be emitted until after {@link PersonaStore.prepare} has been
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
362 public signal void personas_changed (Set<Persona> added,
363 Set<Persona> removed,
366 GroupDetails.ChangeReason reason);
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)
377 var _removed = removed;
379 if ((added == null || ((!) added).size == 0) &&
380 (removed == null || ((!) removed).size == 0))
382 /* Don't bother signalling if nothing's changed */
385 else if (added == null)
387 _added = new HashSet<Persona> ();
389 else if (removed == null)
391 _removed = new HashSet<Persona> ();
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);
400 * Emitted when the backing store for this PersonaStore has been removed.
402 * At this point, the PersonaStore and all its {@link Persona}s are invalid,
403 * so any client referencing it should unreference it.
405 * This will not be emitted until after {@link PersonaStore.prepare} has been
408 public abstract signal void removed ();
411 * The type of PersonaStore this is.
413 * This is the same for all PersonaStores provided by a given {@link Backend}.
415 * This is guaranteed to always be available; even before
416 * {@link PersonaStore.prepare} is called.
418 public abstract string type_id
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'. */
427 * The human-readable, service-specific name used to represent the
428 * PersonaStore to the user.
430 * For example: `foo@@xmpp.example.org`.
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.
436 * This is not guaranteed to be unique even within this PersonaStore's
441 public string display_name { get; construct; }
444 * The instance identifier for this PersonaStore.
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.
450 public string id { get; construct; }
453 * The {@link Persona}s exposed by this PersonaStore.
457 public abstract Map<string, Persona> personas { get; }
460 * Whether this {@link PersonaStore} can add {@link Persona}s.
464 public abstract MaybeBool can_add_personas { get; default = MaybeBool.UNSET; }
467 * Whether this {@link PersonaStore} can set the alias of {@link Persona}s.
471 [Deprecated (since = "0.6.3.1",
472 replacement = "PersonaStore.always_writeable_properties")]
473 public abstract MaybeBool can_alias_personas
476 default = MaybeBool.UNSET;
480 * Whether this {@link PersonaStore} can set the groups of {@link Persona}s.
484 [Deprecated (since = "0.6.3.1",
485 replacement = "PersonaStore.always_writeable_properties")]
486 public abstract MaybeBool can_group_personas
489 default = MaybeBool.UNSET;
493 * Whether this {@link PersonaStore} can remove {@link Persona}s.
497 public abstract MaybeBool can_remove_personas
500 default = MaybeBool.UNSET;
504 * Whether {@link PersonaStore.prepare} has successfully completed for this
509 public abstract bool is_prepared { get; default = false; }
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.
517 * It's guaranteed that this property's value will only ever change after
518 * {@link IndividualAggregator.is_prepared} has changed to `true`.
522 public abstract bool is_quiescent { get; default = false; }
525 * Whether the PersonaStore is writeable.
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.
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.
536 * PersonaStores must not set this property themselves; it will be set as
537 * appropriate by the {@link IndividualAggregator}.
541 [Deprecated (since = "0.6.3",
542 replacement = "PersonaStore.is_primary_store")]
543 public bool is_writeable { get; set; default = false; }
546 * The trust level of the PersonaStore for linking.
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.
552 * @see PersonaStoreTrust
555 public PersonaStoreTrust trust_level
557 get; set; default = PersonaStoreTrust.NONE;
561 * The names of the properties of the {@link Persona}s in this store which are
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
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
577 public abstract string[] always_writeable_properties { get; }
580 * Prepare the PersonaStore for use.
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.
588 * This is normally handled transparently by the {@link IndividualAggregator}.
590 * If this function throws an error, the PersonaStore will not be functional.
592 * This function is guaranteed to be idempotent (since version 0.3.0).
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
602 public abstract async void prepare () throws GLib.Error;
605 * Flush any pending changes to the PersonaStore's backing store.
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.
612 * This must not be called before {@link PersonaStore.prepare}.
616 public virtual async void flush ()
618 /* Default implementation doesn't have to do anything */
622 * Add a new {@link Persona} to the PersonaStore.
624 * The {@link Persona} will be created by the PersonaStore backend from the
625 * key-value pairs given in `details`.
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.
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
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.
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`.
646 * @param details a key-value map of details to use in creating the new
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}.
654 public abstract async Persona? add_persona_from_details (
655 HashTable<string, Value?> details) throws Folks.PersonaStoreError;
658 * Remove a {@link Persona} from the PersonaStore.
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}.
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.
671 * @param persona the {@link Persona} to remove
674 public abstract async void remove_persona (Persona persona)
675 throws Folks.PersonaStoreError;
678 * Whether this {@link PersonaStore} is the primary store which is
679 * to be used for linking {@link Persona}s and such.
683 public bool is_primary_store { get; internal set; default = false; }
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.
692 public bool is_user_set_default { get; internal set; default = false; }