58
58
E_TYPE_SOURCE_AUTHENTICATOR,
59
59
e_book_backend_google_source_authenticator_init))
67
61
struct _EBookBackendGooglePrivate {
72
EBookBackendCache *on_disk;
75
GHashTable *gdata_entries;
76
GTimeVal last_updated;
64
EBookBackendCache *cache;
80
66
/* Mapping from group ID to (human readable) group name */
81
67
GHashTable *groups_by_id;
127
cache_init (EBookBackend *backend,
109
cache_init (EBookBackend *backend)
130
111
EBookBackendGooglePrivate *priv;
131
112
const gchar *cache_dir;
133
115
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
135
117
cache_dir = e_book_backend_get_cache_dir (backend);
140
filename = g_build_filename (cache_dir, "cache.xml", NULL);
141
priv->cache_type = ON_DISK_CACHE;
142
priv->cache.on_disk = e_book_backend_cache_new (filename);
145
migrate_cache (priv->cache.on_disk);
147
priv->cache_type = IN_MEMORY_CACHE;
148
priv->cache.in_memory.contacts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
149
priv->cache.in_memory.gdata_entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
150
memset (&priv->cache.in_memory.last_updated, 0, sizeof (GTimeVal));
118
filename = g_build_filename (cache_dir, "cache.xml", NULL);
119
priv->cache = e_book_backend_cache_new (filename);
122
migrate_cache (priv->cache);
154
125
static EContact *
158
129
EBookBackendGooglePrivate *priv;
159
130
EContact *contact;
162
132
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
164
switch (priv->cache_type) {
166
contact = e_contact_new_from_gdata_entry (entry, priv->groups_by_id, priv->system_groups_by_entry_id);
167
e_contact_add_gdata_entry_xml (contact, entry);
168
e_book_backend_cache_add_contact (priv->cache.on_disk, contact);
169
e_contact_remove_gdata_entry_xml (contact);
171
case IN_MEMORY_CACHE:
172
contact = e_contact_new_from_gdata_entry (entry, priv->groups_by_id, priv->system_groups_by_entry_id);
173
uid = e_contact_get_const (contact, E_CONTACT_UID);
174
g_hash_table_insert (priv->cache.in_memory.contacts, g_strdup (uid), g_object_ref (contact));
175
g_hash_table_insert (priv->cache.in_memory.gdata_entries, g_strdup (uid), g_object_ref (entry));
134
contact = e_contact_new_from_gdata_entry (entry, priv->groups_by_id, priv->system_groups_by_entry_id);
135
e_contact_add_gdata_entry_xml (contact, entry);
136
e_book_backend_cache_add_contact (priv->cache, contact);
137
e_contact_remove_gdata_entry_xml (contact);
187
144
const gchar *uid)
189
146
EBookBackendGooglePrivate *priv;
190
gboolean success = TRUE;
192
148
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
194
switch (priv->cache_type) {
196
return e_book_backend_cache_remove_contact (priv->cache.on_disk, uid);
197
case IN_MEMORY_CACHE:
198
success = g_hash_table_remove (priv->cache.in_memory.contacts, uid);
199
return success && g_hash_table_remove (priv->cache.in_memory.gdata_entries, uid);
150
return e_book_backend_cache_remove_contact (priv->cache, uid);
214
159
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
216
switch (priv->cache_type) {
218
return e_book_backend_cache_check_contact (priv->cache.on_disk, uid);
219
case IN_MEMORY_CACHE:
220
return g_hash_table_lookup (priv->cache.in_memory.contacts, uid) ? TRUE : FALSE;
161
return e_book_backend_cache_check_contact (priv->cache, uid);
229
164
static EContact *
237
172
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
239
switch (priv->cache_type) {
241
contact = e_book_backend_cache_get_contact (priv->cache.on_disk, uid);
244
const gchar *entry_xml, *edit_uri = NULL;
246
entry_xml = e_contact_get_gdata_entry_xml (contact, &edit_uri);
247
*entry = GDATA_ENTRY (gdata_parsable_new_from_xml (GDATA_TYPE_CONTACTS_CONTACT, entry_xml, -1, NULL));
250
GDataLink *edit_link = gdata_link_new (edit_uri, GDATA_LINK_EDIT);
251
gdata_entry_add_link (*entry, edit_link);
252
g_object_unref (edit_link);
256
e_contact_remove_gdata_entry_xml (contact);
259
case IN_MEMORY_CACHE:
260
contact = g_hash_table_lookup (priv->cache.in_memory.contacts, uid);
174
contact = e_book_backend_cache_get_contact (priv->cache, uid);
262
*entry = g_hash_table_lookup (priv->cache.in_memory.gdata_entries, uid);
264
g_object_ref (*entry);
177
const gchar *entry_xml, *edit_uri = NULL;
179
entry_xml = e_contact_get_gdata_entry_xml (contact, &edit_uri);
180
*entry = GDATA_ENTRY (gdata_parsable_new_from_xml (GDATA_TYPE_CONTACTS_CONTACT, entry_xml, -1, NULL));
183
GDataLink *edit_link = gdata_link_new (edit_uri, GDATA_LINK_EDIT);
184
gdata_entry_add_link (*entry, edit_link);
185
g_object_unref (edit_link);
268
g_object_ref (contact);
189
e_contact_remove_gdata_entry_xml (contact);
280
_g_hash_table_to_list (GHashTable *ht)
286
g_hash_table_iter_init (&iter, ht);
287
while (g_hash_table_iter_next (&iter, &key, &value))
288
l = g_list_prepend (l, g_object_ref (G_OBJECT (value)));
290
l = g_list_reverse (l);
301
201
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
303
switch (priv->cache_type) {
305
contacts = e_book_backend_cache_get_contacts (priv->cache.on_disk, "(contains \"x-evolution-any-field\" \"\")");
306
for (iter = contacts; iter; iter = iter->next)
307
e_contact_remove_gdata_entry_xml (iter->data);
310
case IN_MEMORY_CACHE:
311
return _g_hash_table_to_list (priv->cache.in_memory.contacts);
203
contacts = e_book_backend_cache_get_contacts (priv->cache, "(contains \"x-evolution-any-field\" \"\")");
204
for (iter = contacts; iter; iter = iter->next)
205
e_contact_remove_gdata_entry_xml (iter->data);
325
215
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
327
if (priv->cache_type == ON_DISK_CACHE)
328
e_file_cache_freeze_changes (E_FILE_CACHE (priv->cache.on_disk));
217
e_file_cache_freeze_changes (E_FILE_CACHE (priv->cache));
336
225
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
338
if (priv->cache_type == ON_DISK_CACHE)
339
e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache.on_disk));
227
e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
347
235
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
349
switch (priv->cache_type) {
351
return e_book_backend_cache_get_time (priv->cache.on_disk);
352
case IN_MEMORY_CACHE:
353
if (priv->cache.in_memory.contacts)
354
return g_time_val_to_iso8601 (&priv->cache.in_memory.last_updated);
365
cache_get_last_update_tv (EBookBackend *backend,
368
EBookBackendGooglePrivate *priv;
372
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
374
switch (priv->cache_type) {
376
last_update = e_book_backend_cache_get_time (priv->cache.on_disk);
377
rv = last_update ? g_time_val_from_iso8601 (last_update, tv) : FALSE;
378
g_free (last_update);
380
case IN_MEMORY_CACHE:
381
memcpy (tv, &priv->cache.in_memory.last_updated, sizeof (GTimeVal));
382
return priv->cache.in_memory.contacts != NULL;
237
return e_book_backend_cache_get_time (priv->cache);
398
247
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
400
switch (priv->cache_type) {
402
_time = g_time_val_to_iso8601 (tv);
403
/* Work around a bug in EBookBackendCache */
404
e_file_cache_remove_object (E_FILE_CACHE (priv->cache.on_disk), "last_update_time");
405
e_book_backend_cache_set_time (priv->cache.on_disk, _time);
408
case IN_MEMORY_CACHE:
409
memcpy (&priv->cache.in_memory.last_updated, tv, sizeof (GTimeVal));
417
cache_needs_update (EBookBackend *backend,
418
guint *remaining_secs)
420
EBookBackendGooglePrivate *priv;
421
GTimeVal last, current;
425
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
428
*remaining_secs = G_MAXUINT;
430
/* We never want to update in offline mode */
431
if (!e_backend_get_online (E_BACKEND (backend)))
434
rv = cache_get_last_update_tv (backend, &last);
439
g_get_current_time (¤t);
440
if (last.tv_sec > current.tv_sec) {
441
g_warning ("last update is in the feature?");
443
/* Do an update so we can fix this */
446
diff = current.tv_sec - last.tv_sec;
448
if (diff >= priv->refresh_interval)
452
*remaining_secs = priv->refresh_interval - diff;
454
__debug__ ("No update needed. Next update needed in %d secs", priv->refresh_interval - diff);
249
_time = g_time_val_to_iso8601 (tv);
250
e_book_backend_cache_set_time (priv->cache, _time);
980
776
gdata_contacts_query_set_show_deleted (GDATA_CONTACTS_QUERY (query), TRUE);
779
g_object_ref (backend);
983
781
/* Run the query asynchronously */
984
782
cancellable = start_operation (backend, -2, NULL, _("Querying for updated groups…"));
985
783
gdata_contacts_service_query_groups_async (
1080
878
return create_group (E_BOOK_BACKEND (user_data), category_name, error);
1083
static gboolean cache_refresh_if_needed (EBookBackend *backend);
1086
on_refresh_timeout (EBookBackend *backend)
882
refresh_local_cache_cb (ESource *source,
1088
EBookBackendGooglePrivate *priv;
1090
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
1092
__debug__ (G_STRFUNC);
1094
priv->refresh_id = 0;
1095
if (priv->bookviews != NULL)
1096
cache_refresh_if_needed (backend);
885
EBookBackend *backend = user_data;
887
__debug__ ("Invoking cache refresh");
889
get_groups (backend);
890
get_new_contacts (backend);
1102
894
cache_refresh_if_needed (EBookBackend *backend)
1104
896
EBookBackendGooglePrivate *priv;
1105
guint remaining_secs;
1106
gboolean install_timeout;
1107
897
gboolean is_online;
1109
899
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
1115
905
if (!is_online || !backend_is_authorized (backend)) {
1116
906
__debug__ ("We are not connected to Google%s.", (!is_online) ? " (offline mode)" : "");
1120
install_timeout = (priv->bookviews != NULL && priv->refresh_interval > 0 && 0 == priv->refresh_id);
1122
if (cache_needs_update (backend, &remaining_secs)) {
1123
/* Update the cache asynchronously and schedule a new timeout */
1124
get_groups (backend);
1125
get_new_contacts (backend);
1126
remaining_secs = priv->refresh_interval;
1127
} else if (g_hash_table_size (priv->system_groups_by_id) == 0)
1128
get_groups (backend);
1130
if (install_timeout) {
1131
__debug__ ("Installing timeout with %d seconds", remaining_secs);
1132
priv->refresh_id = g_timeout_add_seconds (remaining_secs, (GSourceFunc) on_refresh_timeout, backend);
1139
cache_destroy (EBookBackend *backend)
1141
EBookBackendGooglePrivate *priv;
1143
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
1145
switch (priv->cache_type) {
1147
g_object_unref (priv->cache.on_disk);
1149
case IN_MEMORY_CACHE:
1150
g_hash_table_destroy (priv->cache.in_memory.contacts);
1151
g_hash_table_destroy (priv->cache.in_memory.gdata_entries);
1158
priv->cache_type = NO_CACHE;
910
if (!priv->refresh_id) {
911
/* Update the cache asynchronously */
912
refresh_local_cache_cb (NULL, backend);
914
priv->refresh_id = e_source_refresh_add_timeout (
915
e_backend_get_source (E_BACKEND (backend)),
917
refresh_local_cache_cb,
920
} else if (g_hash_table_size (priv->system_groups_by_id) == 0) {
921
get_groups (backend);
1184
950
EBookBackendGooglePrivate *priv;
1185
ESourceRegistry *registry;
1188
952
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
1189
source = e_backend_get_source (E_BACKEND (backend));
1190
registry = e_book_backend_get_registry (backend);
1192
954
/* Make sure we have the GDataService configured
1193
955
* before requesting authorization. */
1236
998
/* Otherwise it's up to us to obtain a login secret. */
1237
return e_source_registry_authenticate_sync (
1238
registry, source, E_SOURCE_AUTHENTICATOR (backend),
999
return e_backend_authenticate_sync (
1000
E_BACKEND (backend),
1001
E_SOURCE_AUTHENTICATOR (backend),
1239
1002
cancellable, error);
1436
1199
if (vcards->next != NULL) {
1437
1200
e_data_book_respond_create_contacts (book, opid,
1438
1201
EDB_ERROR_EX (NOT_SUPPORTED,
1439
_("The backend does not support bulk additions")),
1202
_("The backend does not support bulk additions")),
1557
1320
if (id_list->next != NULL) {
1558
1321
e_data_book_respond_remove_contacts (book, opid,
1559
1322
EDB_ERROR_EX (NOT_SUPPORTED,
1560
_("The backend does not support bulk removals")),
1323
_("The backend does not support bulk removals")),
1835
1598
if (vcards->next != NULL) {
1836
1599
e_data_book_respond_modify_contacts (book, opid,
1837
1600
EDB_ERROR_EX (NOT_SUPPORTED,
1838
_("The backend does not support bulk modifications")),
1601
_("The backend does not support bulk modifications")),
2025
1788
g_slist_free (filtered_uids);
2029
on_refresh_idle (EBookBackend *backend)
2031
EBookBackendGooglePrivate *priv;
2033
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
2036
cache_refresh_if_needed (backend);
2042
1792
e_book_backend_google_start_book_view (EBookBackend *backend,
2043
1793
EDataBookView *bookview)
2061
1811
/* Ensure that we're ready to support a view */
2062
1812
cache_refresh_if_needed (backend);
2064
/* Update the cache if necessary */
2065
if (cache_needs_update (backend, NULL)) {
2066
/* XXX We ought to be authorized by now, I would think.
2067
* Not sure when we wouldn't be or how to handle it. */
2068
if (!backend_is_authorized (backend)) {
2069
error = EDB_ERROR (AUTHENTICATION_REQUIRED);
2072
/* Update in an idle function, so that this call doesn't block */
2073
priv->idle_id = g_idle_add ((GSourceFunc) on_refresh_idle, backend);
2077
1814
/* Get the contacts */
2078
1815
cached_contacts = cache_get_contacts (backend);
2079
1816
__debug__ ("%d contacts found in cache", g_list_length (cached_contacts));
2106
1842
priv->bookviews = g_list_delete_link (priv->bookviews, view);
2107
1843
e_data_book_view_unref (bookview);
2110
/* If there are no book views left, we can stop doing certain things, like refreshes */
2111
if (!priv->bookviews && priv->refresh_id != 0) {
2112
g_source_remove (priv->refresh_id);
2113
priv->refresh_id = 0;
2118
e_book_backend_google_remove (EBookBackend *backend,
2121
GCancellable *cancellable)
2123
__debug__ (G_STRFUNC);
2124
e_data_book_respond_remove (book, opid, NULL);
2132
1852
gboolean only_if_exists)
2134
1854
EBookBackendGooglePrivate *priv;
2135
ESourceOffline *offline_extension;
2136
ESourceRefresh *refresh_extension;
2138
guint interval_in_minutes;
2140
const gchar *extension_name;
2141
1855
gboolean is_online;
2142
1856
GError *error = NULL;
2153
source = e_backend_get_source (E_BACKEND (backend));
2155
extension_name = E_SOURCE_EXTENSION_OFFLINE;
2156
offline_extension = e_source_get_extension (source, extension_name);
2158
extension_name = E_SOURCE_EXTENSION_REFRESH;
2159
refresh_extension = e_source_get_extension (source, extension_name);
2161
interval_in_minutes =
2162
e_source_refresh_get_enabled (refresh_extension) ?
2163
e_source_refresh_get_interval_minutes (refresh_extension) : 0;
2165
use_cache = e_source_offline_get_stay_synchronized (offline_extension);
2167
1867
/* Set up our object */
2168
1868
if (!priv->cancellables) {
2169
1869
priv->groups_by_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
2173
1873
priv->cancellables = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
2176
cache_init (backend, use_cache);
2177
priv->refresh_interval = interval_in_minutes * 60;
2179
/* Remove and re-add the timeout */
2180
if (priv->refresh_id != 0 && priv->refresh_interval > 0) {
2181
g_source_remove (priv->refresh_id);
2182
priv->refresh_id = g_timeout_add_seconds (priv->refresh_interval, (GSourceFunc) on_refresh_timeout, backend);
1876
cache_init (backend);
2185
1878
/* Set up ready to be interacted with */
2186
1879
is_online = e_backend_get_online (E_BACKEND (backend));
2198
1891
if (!is_online || backend_is_authorized (backend)) {
2200
1893
e_book_backend_notify_readonly (backend, FALSE);
1894
cache_refresh_if_needed (backend);
2201
1897
e_book_backend_notify_opened (backend, NULL /* Success */);
2439
2135
priv->bookviews = g_list_delete_link (priv->bookviews, priv->bookviews);
2442
if (priv->idle_id) {
2443
g_source_remove (priv->idle_id);
2138
if (priv->refresh_id) {
2139
e_source_refresh_remove_timeout (
2140
e_backend_get_source (E_BACKEND (object)),
2142
priv->refresh_id = 0;
2447
2145
if (priv->service)
2551
2249
backend_class->get_backend_property = e_book_backend_google_get_backend_property;
2552
2250
backend_class->start_book_view = e_book_backend_google_start_book_view;
2553
2251
backend_class->stop_book_view = e_book_backend_google_stop_book_view;
2554
backend_class->remove = e_book_backend_google_remove;
2555
2252
backend_class->create_contacts = e_book_backend_google_create_contacts;
2556
2253
backend_class->remove_contacts = e_book_backend_google_remove_contacts;
2557
2254
backend_class->modify_contacts = e_book_backend_google_modify_contacts;