29
29
extern const string BACKEND_NAME;
31
/* The following function is needed in order to use the async SourceRegistry
32
* constructor. FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=659886 */
33
[CCode (cname = "e_source_registry_new", cheader_filename = "libedataserver/libedataserver.h", finish_function = "e_source_registry_new_finish")]
34
public extern static async E.SourceRegistry create_source_registry (GLib.Cancellable? cancellable = null) throws GLib.Error;
38
33
* It will create {@link Persona}s for each contacts on the main addressbook.
46
41
private bool _is_quiescent = false;
47
42
private E.BookClient? _addressbook = null; /* null before prepare() */
48
43
private E.BookClientView? _ebookview = null; /* null before prepare() */
49
private E.SourceRegistry? _source_registry = null; /* null before prepare() */
44
private E.SourceList? _source_list = null; /* null before prepare() */
50
45
private string _query_str;
52
47
/* The timeout after which we consider a property change to have failed if we
222
217
* @param s the e-d-s source being represented by the persona store
225
* @deprecated 0.7.2 Use {@link Edsf.PersonaStore.with_source_registry}
227
[Deprecated (since = "0.7.2",
228
replacement = "Edsf.PersonaStore.with_source_registry()")]
229
public PersonaStore (E.SourceRegistry r, E.Source s)
231
string eds_uid = s.get_uid ();
232
string eds_name = s.get_display_name ();
234
display_name: eds_name,
237
this._source_registry = null; /* created in prepare() */
241
* Create a new PersonaStore.
243
* Create a new persona store to store the {@link Persona}s for the contacts
244
* in `s`. Passing a re-used source registry to the constructor (compared to
245
* the old {@link Edsf.PersonaStore} constructor) saves a lot of time and
248
* @param r the EDS source registry giving access to all EDS sources
249
* @param s the EDS source being represented by the persona store
253
public PersonaStore.with_source_registry (E.SourceRegistry r, E.Source s)
255
string eds_uid = s.get_uid ();
221
public PersonaStore (E.Source s)
223
string eds_uid = s.peek_uid ();
256
224
Object (id: eds_uid,
257
225
display_name: eds_uid,
260
this._source_registry = r;
290
257
if (this._addressbook != null)
259
((!) this._addressbook).authenticate.disconnect (
260
this._address_book_authenticate_cb);
292
261
((!) this._addressbook).notify["readonly"].disconnect (
293
262
this._address_book_notify_read_only_cb);
295
264
this._addressbook = null;
298
if (this._source_registry != null)
267
if (this._source_list != null)
300
((!) this._source_registry).source_removed.disconnect (
301
this._source_registry_changed_cb);
302
((!) this._source_registry).source_disabled.disconnect (
303
this._source_registry_changed_cb);
304
this._source_registry = null;
269
((!) this._source_list).changed.disconnect (
270
this._source_list_changed_cb);
271
this._source_list = null;
307
274
catch (GLib.Error e)
333
300
* See {@link Folks.PersonaStore.add_persona_from_details}.
335
* @throws Folks.PersonaStoreError.STORE_OFFLINE if the store hasn’t been
337
* @throws Folks.PersonaStoreError.CREATE_FAILED if creating the persona in
338
* the EDS store failed
342
304
public override async Folks.Persona? add_persona_from_details (
417
379
Set<PostalAddressFieldDetails> postal_fds =
418
380
(Set<PostalAddressFieldDetails>) v.get_object ();
419
yield this._set_contact_postal_addresses (contact,
381
yield this._set_contact_postal_addresses (contact,
422
384
else if (k == Folks.PersonaStore.detail_key (
423
385
PersonaDetail.STRUCTURED_NAME))
527
489
* See {@link Folks.PersonaStore.remove_persona}.
529
491
* @param persona the persona that should be removed
530
* @throws Folks.PersonaStoreError.STORE_OFFLINE if the store hasn’t been
531
* prepared or has gone offline
532
* @throws Folks.PersonaStoreError.PERMISSION_DENIED if the store denied
533
* permission to delete the contact
534
* @throws Folks.PersonaStoreError.READ_ONLY if the store is read only
535
* @throws Folks.PersonaStoreError.REMOVE_FAILED if any other errors happened
631
586
* See {@link Folks.PersonaStore.prepare}.
633
* @throws Folks.PersonaStoreError.STORE_OFFLINE if the EDS store is offline
634
* @throws Folks.PersonaStoreError.PERMISSION_DENIED if permission was denied
635
* to open the EDS store
636
* @throws Folks.PersonaStoreError.INVALID_ARGUMENT if any other error
637
* occurred in the EDS store
641
590
public override async void prepare () throws PersonaStoreError
643
Internal.profiling_start ("preparing Edsf.PersonaStore (ID: %s)",
646
592
/* FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=652637 */
647
593
lock (this._is_prepared)
658
604
/* Listen for removal signals for the address book. There's no
659
605
* need to check if we still exist in the list, as
660
606
* addressbook.open() will fail if we don't. */
661
if (this._source_registry == null)
663
this._source_registry = yield create_source_registry ();
607
E.BookClient.get_sources (out this._source_list);
666
/* We know _source_registry != null because otherwise
667
* create_source_registry() would've thrown an error. */
668
((!) this._source_registry).source_removed.connect (
669
this._source_registry_changed_cb);
670
((!) this._source_registry).source_disabled.connect (
671
this._source_registry_changed_cb);
609
/* We know _source_list != null because otherwise
610
* E.BookClient.get_sources() would've thrown an error. */
611
((!) this._source_list).changed.connect (
612
this._source_list_changed_cb);
673
614
/* Connect to the address book. */
674
615
this._addressbook = new E.BookClient (this.source);
676
617
((!) this._addressbook).notify["readonly"].connect (
677
618
this._address_book_notify_read_only_cb);
619
((!) this._addressbook).authenticate.connect (
620
this._address_book_authenticate_cb);
679
622
yield this._open_address_book ();
680
623
debug ("Successfully finished opening address book %p for " +
681
624
"persona store ‘%s’ (%p).", this._addressbook, this.id, this);
683
Internal.profiling_point ("opened address book in " +
684
"Edsf.PersonaStore (ID: %s)", this.id);
686
this._notify_if_default ();
687
626
this._update_trust_level ();
689
628
catch (GLib.Error e1)
771
710
yield ((!) this._addressbook).get_backend_property (
772
711
"supported-fields", null, out supported_fields);
774
Internal.profiling_point ("got supported fields in " +
775
"Edsf.PersonaStore (ID: %s)", this.id);
777
713
var prop_set = new HashSet<string> ();
779
715
/* We get a comma-separated list of fields back. */
838
774
yield ((!) this._addressbook).get_backend_property (
839
775
"capabilities", null, out capabilities);
841
Internal.profiling_point ("got capabilities in " +
842
"Edsf.PersonaStore (ID: %s)", this.id);
844
777
if (capabilities != null)
846
779
string[] caps = ((!) capabilities).split (",");
868
801
got_view = yield ((!) this._addressbook).get_view (
869
802
this._query_str, null, out this._ebookview);
871
Internal.profiling_point ("opened book view in " +
872
"Edsf.PersonaStore (ID: %s)", this.id);
874
804
if (got_view == false)
876
806
throw new PersonaStoreError.INVALID_ARGUMENT (
978
908
this.notify_property ("is-quiescent");
982
Internal.profiling_end ("preparing Edsf.PersonaStore");
913
private bool _address_book_authenticate_cb (Client address_book,
916
/* FIXME: Add authentication support. That's:
917
* https://bugzilla.gnome.org/show_bug.cgi?id=653339
919
* For the moment, we just reject the authentication request, rather than
920
* leave it hanging. */
985
924
/* Temporaries for _open_address_book(). See the complaint below. */
1223
1162
/* Irrelevant */
1224
1163
case ContactField.UID: /* identifier */
1225
1164
case ContactField.REV: /* revision date */
1226
case ContactField.BOOK_UID: /* parent identifier */
1165
case ContactField.BOOK_URI: /* parent identifier */
1227
1166
case ContactField.NAME_OR_ORG: /* FULL_NAME or ORG; both handled */
1228
1167
return PersonaDetail.INVALID;
1229
1168
/* Unsupported */
1324
1263
/* Remove the callbacks. */
1327
persona.disconnect (signal_id);
1330
if (timeout_id != 0)
1332
GLib.Source.remove (timeout_id);
1264
persona.disconnect (signal_id);
1265
GLib.Source.remove (timeout_id);
2146
2078
warning ("Error in address book view query: %s", err.message);
2149
Internal.profiling_point ("initial query complete in " +
2150
"Edsf.PersonaStore (ID: %s)", this.id);
2152
2081
/* The initial query is complete, so signal that we've reached
2153
2082
* quiescence (even if there was an error). */
2154
2083
if (this._is_quiescent == false)
2239
2168
error_in.message);
2242
private bool _backend_name_matches (string backend_name)
2244
if (this.source.has_extension (SOURCE_EXTENSION_ADDRESS_BOOK))
2246
unowned E.SourceAddressBook extension = (E.SourceAddressBook)
2247
this.source.get_extension (SOURCE_EXTENSION_ADDRESS_BOOK);
2249
return (extension.get_backend_name () == backend_name);
2255
2171
/* Try and work out whether this address book is Google Contacts. If so, we
2256
2172
* can enable things like setting favourite status based on Android groups. */
2257
2173
internal bool _is_google_contacts_address_book ()
2259
/* Should only ever be called from property getters/setters. */
2260
assert (this._source_registry != null);
2175
unowned SourceGroup? group = (SourceGroup?) this.source.peek_group ();
2178
var base_uri = ((!) group).peek_base_uri ();
2179
/* base_uri should be google:// for Google Contacts address books */
2180
if (base_uri.has_prefix ("google:"))
2262
/* backend name should be ‘google’ for Google Contacts address books */
2263
return this._backend_name_matches ("google");
2266
private bool _is_in_source_registry ()
2189
private bool _is_in_source_list ()
2268
2191
/* Should only ever be called from a callback from the source list itself,
2269
2192
* so we can assert that the source list is non-null. */
2270
assert (this._source_registry != null);
2272
E.Source? needle = ((!) this._source_registry).ref_source (this.id);
2273
if (needle != null && needle.has_extension (SOURCE_EXTENSION_ADDRESS_BOOK))
2193
assert (this._source_list != null);
2195
unowned GLib.SList<weak E.SourceGroup> groups =
2196
((!) this._source_list).peek_groups ();
2198
foreach (var g in groups)
2275
/* We've found ourself. */
2200
foreach (var s in g.peek_sources ())
2202
if (s.peek_uid () == this.id)
2204
/* We've found ourself. */
2282
2213
/* Detect removal of the address book. We can't do this in Eds.Backend because
2283
2214
* it has no way to tell the PersonaStore that it's been removed without
2284
2215
* uglifying the store's public API. */
2285
private void _source_registry_changed_cb (E.Source list)
2216
private void _source_list_changed_cb (E.SourceList list)
2287
2218
/* If we can't find our source, this persona store's address book has
2288
2219
* been removed. */
2289
if (this._is_in_source_registry () == false)
2220
if (this._is_in_source_list () == false)
2291
2222
/* Marshal the personas from a Collection to a Set. */
2292
2223
var removed_personas = new HashSet<Persona> ();
2315
2246
/* We may be called before prepare() has finished (and it may then fail),
2316
2247
* but _addressbook should always be non-null when we're called. */
2317
assert (this._source_registry != null);
2318
2248
assert (this._addressbook != null);
2320
/* backend_name should be ‘ldap’ for LDAP based address books */
2321
if (this._backend_name_matches ("ldap"))
2250
unowned SourceGroup? group = (SourceGroup?) this.source.peek_group ();
2323
this.trust_level = PersonaStoreTrust.PARTIAL;
2253
var base_uri = ((!) group).peek_base_uri ();
2254
/* base_uri should be ldap:// for LDAP based address books */
2255
if (base_uri.has_prefix ("ldap"))
2257
this.trust_level = PersonaStoreTrust.PARTIAL;
2327
2262
if (((!) this._addressbook).readonly)
2340
2275
bool is_default = false;
2342
/* By peeking at the default source instead of checking the value of
2343
* the "default" property, we include EDS's fallback logic for the
2344
* "system" address book */
2345
if (this._source_registry != null)
2347
var default_source = this._source_registry.ref_default_address_book ();
2279
/* By peeking at the default source instead of checking the value of
2280
* the "default" property, we include EDS's fallback logic for the
2281
* "system" address book */
2282
E.SourceList sources;
2283
E.BookClient.get_sources (out sources);
2284
var default_source = sources.peek_default_source ();
2348
2285
if (default_source != null &&
2349
this.source.get_uid () == ((!) default_source).get_uid ())
2286
this.source.peek_uid () == ((!) default_source).peek_uid ())
2351
2288
is_default = true;
2291
catch (GLib.Error e)
2293
warning ("Failed to get the set of ESources while looking for a " +
2294
"default address book: %s", e);
2354
if (is_default != this.is_user_set_default)
2356
this.is_user_set_default = is_default;
2297
if (is_default != this.is_user_set_default)
2299
this.is_user_set_default = is_default;