~ubuntu-branches/ubuntu/raring/folks/raring

« back to all changes in this revision

Viewing changes to backends/eds/lib/edsf-persona-store.vala

  • Committer: Package Import Robot
  • Author(s): Sjoerd Simons
  • Date: 2012-03-30 20:03:30 UTC
  • mfrom: (32.1.2 precise)
  • Revision ID: package-import@ubuntu.com-20120330200330-l61hwayt5vjz1zcf
Tags: 0.6.8-2
* d/p/0001-tpf-persona-use-tp_connection_get_account.patch
  + Added, fixes crash when accounts are disconnecting/connecting
* Target unstable

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
 
29
29
extern const string BACKEND_NAME;
30
30
 
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;
35
 
 
36
31
/**
37
32
 * A persona store.
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;
51
46
 
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
223
218
   *
224
219
   * @since 0.6.0
225
 
   * @deprecated 0.7.2 Use {@link Edsf.PersonaStore.with_source_registry}
226
 
   */
227
 
  [Deprecated (since = "0.7.2",
228
 
      replacement = "Edsf.PersonaStore.with_source_registry()")]
229
 
  public PersonaStore (E.SourceRegistry r, E.Source s)
230
 
    {
231
 
      string eds_uid = s.get_uid ();
232
 
      string eds_name = s.get_display_name ();
233
 
      Object (id: eds_uid,
234
 
              display_name: eds_name,
235
 
              source: s);
236
 
 
237
 
      this._source_registry = null; /* created in prepare() */
238
 
    }
239
 
 
240
 
  /**
241
 
   * Create a new PersonaStore.
242
 
   *
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
246
 
   * D-Bus round trips.
247
 
   *
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
250
 
   *
251
 
   * @since 0.7.2
252
 
   */
253
 
  public PersonaStore.with_source_registry (E.SourceRegistry r, E.Source s)
254
 
    {
255
 
      string eds_uid = s.get_uid ();
 
220
   */
 
221
  public PersonaStore (E.Source s)
 
222
    {
 
223
      string eds_uid = s.peek_uid ();
256
224
      Object (id: eds_uid,
257
225
              display_name: eds_uid,
258
226
              source: s);
259
 
 
260
 
      this._source_registry = r;
261
227
    }
262
228
 
263
229
  construct
266
232
      this._personas_ro = this._personas.read_only_view;
267
233
      this._query_str = "(contains \"x-evolution-any-field\" \"\")";
268
234
      this.source.changed.connect (this._source_changed_cb);
 
235
      this._notify_if_default ();
269
236
    }
270
237
 
271
238
  ~PersonaStore ()
289
256
 
290
257
          if (this._addressbook != null)
291
258
            {
 
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);
294
263
 
295
264
              this._addressbook = null;
296
265
            }
297
266
 
298
 
          if (this._source_registry != null)
 
267
          if (this._source_list != null)
299
268
            {
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;
305
272
            }
306
273
        }
307
274
      catch (GLib.Error e)
332
299
   *
333
300
   * See {@link Folks.PersonaStore.add_persona_from_details}.
334
301
   *
335
 
   * @throws Folks.PersonaStoreError.STORE_OFFLINE if the store hasn’t been
336
 
   * prepared
337
 
   * @throws Folks.PersonaStoreError.CREATE_FAILED if creating the persona in
338
 
   * the EDS store failed
339
 
   *
340
302
   * @since 0.6.0
341
303
   */
342
304
  public override async Folks.Persona? add_persona_from_details (
416
378
            {
417
379
              Set<PostalAddressFieldDetails> postal_fds =
418
380
                (Set<PostalAddressFieldDetails>) v.get_object ();
419
 
              yield this._set_contact_postal_addresses (contact,
420
 
                  postal_fds);
 
381
                yield this._set_contact_postal_addresses (contact,
 
382
                    postal_fds);
421
383
            }
422
384
          else if (k == Folks.PersonaStore.detail_key (
423
385
                PersonaDetail.STRUCTURED_NAME))
527
489
   * See {@link Folks.PersonaStore.remove_persona}.
528
490
   *
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
536
 
   * in the store
537
492
   *
538
493
   * @since 0.6.0
539
494
   */
630
585
   *
631
586
   * See {@link Folks.PersonaStore.prepare}.
632
587
   *
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
638
 
   *
639
588
   * @since 0.6.0
640
589
   */
641
590
  public override async void prepare () throws PersonaStoreError
642
591
    {
643
 
      Internal.profiling_start ("preparing Edsf.PersonaStore (ID: %s)",
644
 
          this.id);
645
 
 
646
592
      /* FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=652637 */
647
593
      lock (this._is_prepared)
648
594
        {
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)
662
 
                {
663
 
                  this._source_registry = yield create_source_registry ();
664
 
                }
 
607
              E.BookClient.get_sources (out this._source_list);
665
608
 
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);
672
613
 
673
614
              /* Connect to the address book. */
674
615
              this._addressbook = new E.BookClient (this.source);
675
616
 
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);
678
621
 
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);
682
625
 
683
 
              Internal.profiling_point ("opened address book in " +
684
 
                  "Edsf.PersonaStore (ID: %s)", this.id);
685
 
 
686
 
              this._notify_if_default ();
687
626
              this._update_trust_level ();
688
627
            }
689
628
          catch (GLib.Error e1)
771
710
              yield ((!) this._addressbook).get_backend_property (
772
711
                  "supported-fields", null, out supported_fields);
773
712
 
774
 
              Internal.profiling_point ("got supported fields in " +
775
 
                  "Edsf.PersonaStore (ID: %s)", this.id);
776
 
 
777
713
              var prop_set = new HashSet<string> ();
778
714
 
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);
840
776
 
841
 
              Internal.profiling_point ("got capabilities in " +
842
 
                  "Edsf.PersonaStore (ID: %s)", this.id);
843
 
 
844
777
              if (capabilities != null)
845
778
                {
846
779
                  string[] caps = ((!) capabilities).split (",");
868
801
              got_view = yield ((!) this._addressbook).get_view (
869
802
                  this._query_str, null, out this._ebookview);
870
803
 
871
 
              Internal.profiling_point ("opened book view in " +
872
 
                  "Edsf.PersonaStore (ID: %s)", this.id);
873
 
 
874
804
              if (got_view == false)
875
805
                {
876
806
                  throw new PersonaStoreError.INVALID_ARGUMENT (
978
908
              this.notify_property ("is-quiescent");
979
909
            }
980
910
        }
 
911
    }
981
912
 
982
 
      Internal.profiling_end ("preparing Edsf.PersonaStore");
 
913
  private bool _address_book_authenticate_cb (Client address_book,
 
914
      void *credentials)
 
915
    {
 
916
      /* FIXME: Add authentication support. That's:
 
917
       * https://bugzilla.gnome.org/show_bug.cgi?id=653339
 
918
       *
 
919
       * For the moment, we just reject the authentication request, rather than
 
920
       * leave it hanging. */
 
921
      return false;
983
922
    }
984
923
 
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 */
1322
1261
      finally
1323
1262
        {
1324
1263
          /* Remove the callbacks. */
1325
 
          if (signal_id != 0)
1326
 
            {
1327
 
              persona.disconnect (signal_id);
1328
 
            }
1329
 
 
1330
 
          if (timeout_id != 0)
1331
 
            {
1332
 
              GLib.Source.remove (timeout_id);
1333
 
            }
 
1264
          persona.disconnect (signal_id);
 
1265
          GLib.Source.remove (timeout_id);
1334
1266
        }
1335
1267
    }
1336
1268
 
2146
2078
          warning ("Error in address book view query: %s", err.message);
2147
2079
        }
2148
2080
 
2149
 
      Internal.profiling_point ("initial query complete in " +
2150
 
          "Edsf.PersonaStore (ID: %s)", this.id);
2151
 
 
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);
2240
2169
    }
2241
2170
 
2242
 
  private bool _backend_name_matches (string backend_name)
2243
 
    {
2244
 
      if (this.source.has_extension (SOURCE_EXTENSION_ADDRESS_BOOK))
2245
 
        {
2246
 
          unowned E.SourceAddressBook extension = (E.SourceAddressBook)
2247
 
            this.source.get_extension (SOURCE_EXTENSION_ADDRESS_BOOK);
2248
 
 
2249
 
          return (extension.get_backend_name () == backend_name);
2250
 
        }
2251
 
 
2252
 
      return false;
2253
 
    }
2254
 
 
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 ()
2258
2174
    {
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 ();
 
2176
      if (group != null)
 
2177
        {
 
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:"))
 
2181
            {
 
2182
              return true;
 
2183
            }
 
2184
        }
2261
2185
 
2262
 
      /* backend name should be ‘google’ for Google Contacts address books */
2263
 
      return this._backend_name_matches ("google");
 
2186
      return false;
2264
2187
    }
2265
2188
 
2266
 
  private bool _is_in_source_registry ()
 
2189
  private bool _is_in_source_list ()
2267
2190
    {
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);
2271
 
 
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);
 
2194
 
 
2195
      unowned GLib.SList<weak E.SourceGroup> groups =
 
2196
          ((!) this._source_list).peek_groups ();
 
2197
 
 
2198
      foreach (var g in groups)
2274
2199
        {
2275
 
          /* We've found ourself. */
2276
 
          return true;
 
2200
          foreach (var s in g.peek_sources ())
 
2201
            {
 
2202
              if (s.peek_uid () == this.id)
 
2203
                {
 
2204
                  /* We've found ourself. */
 
2205
                  return true;
 
2206
                }
 
2207
            }
2277
2208
        }
2278
2209
 
2279
2210
      return false;
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)
2286
2217
    {
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)
2290
2221
        {
2291
2222
          /* Marshal the personas from a Collection to a Set. */
2292
2223
          var removed_personas = new HashSet<Persona> ();
2314
2245
    {
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);
2319
2249
 
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 ();
 
2251
      if (group != null)
2322
2252
        {
2323
 
          this.trust_level = PersonaStoreTrust.PARTIAL;
2324
 
          return;
 
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"))
 
2256
            {
 
2257
              this.trust_level = PersonaStoreTrust.PARTIAL;
 
2258
              return;
 
2259
            }
2325
2260
        }
2326
2261
 
2327
2262
      if (((!) this._addressbook).readonly)
2339
2274
    {
2340
2275
      bool is_default = false;
2341
2276
 
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)
 
2277
      try
2346
2278
        {
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 ())
2350
2287
            {
2351
2288
              is_default = true;
2352
2289
            }
 
2290
        }
 
2291
      catch (GLib.Error e)
 
2292
        {
 
2293
          warning ("Failed to get the set of ESources while looking for a " +
 
2294
              "default address book: %s", e);
 
2295
        }
2353
2296
 
2354
 
          if (is_default != this.is_user_set_default)
2355
 
            {
2356
 
              this.is_user_set_default = is_default;
2357
 
            }
 
2297
      if (is_default != this.is_user_set_default)
 
2298
        {
 
2299
          this.is_user_set_default = is_default;
2358
2300
        }
2359
2301
    }
2360
2302
}