Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-contact-cache"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <libvalent-core.h>
9 : :
10 : : #include "valent-contact-cache-private.h"
11 : : #include "valent-contact-store.h"
12 : : #include "valent-eds.h"
13 : :
14 : :
15 : : /**
16 : : * ValentContactCache:
17 : : *
18 : : * An implementation of [class@Valent.ContactStore].
19 : : *
20 : : * #ValentContactCache is a simple implementation of [class@Valent.ContactStore]
21 : : * for local contact store, used as a fallback when Evolution Data Server is not
22 : : * available.
23 : : *
24 : : * Since: 1.0
25 : : */
26 : :
27 : : struct _ValentContactCache
28 : : {
29 : : ValentContactStore parent_instance;
30 : :
31 : : EBookCache *cache;
32 : : char *path;
33 : : };
34 : :
35 [ + + + - ]: 194 : G_DEFINE_FINAL_TYPE (ValentContactCache, valent_contact_cache, VALENT_TYPE_CONTACT_STORE)
36 : :
37 : :
38 : : enum {
39 : : PROP_0,
40 : : PROP_PATH,
41 : : N_PROPERTIES
42 : : };
43 : :
44 : : static GParamSpec *properties[N_PROPERTIES] = { NULL, };
45 : :
46 : : static inline void
47 : 14 : object_slist_free (gpointer slist)
48 : : {
49 : 14 : g_slist_free_full (slist, g_object_unref);
50 : 14 : }
51 : :
52 : : static inline void
53 : 1 : string_slist_free (gpointer slist)
54 : : {
55 : 1 : g_slist_free_full (slist, g_free);
56 : 1 : }
57 : :
58 : : /*
59 : : * ValentContactStore
60 : : */
61 : : static void
62 : 14 : valent_contact_cache_add_task (GTask *task,
63 : : gpointer source_object,
64 : : gpointer task_data,
65 : : GCancellable *cancellable)
66 : : {
67 : 14 : ValentContactCache *self = VALENT_CONTACT_CACHE (source_object);
68 : 14 : ValentContactStore *store = VALENT_CONTACT_STORE (source_object);
69 : 14 : GSList *contacts = task_data;
70 : 14 : GError *error = NULL;
71 : :
72 [ + - ]: 14 : if (g_task_return_error_if_cancelled (task))
73 : 0 : return;
74 : :
75 : 14 : valent_object_lock (VALENT_OBJECT (self));
76 : 14 : e_book_cache_put_contacts (self->cache,
77 : : contacts,
78 : : NULL,
79 : : NULL,
80 : : E_CACHE_IS_OFFLINE,
81 : : cancellable,
82 : : &error);
83 : 14 : valent_object_unlock (VALENT_OBJECT (self));
84 : :
85 [ - + ]: 14 : if (error != NULL)
86 : 0 : return g_task_return_error (task, error);
87 : :
88 [ + + ]: 35 : for (const GSList *iter = contacts; iter; iter = iter->next)
89 : 21 : valent_contact_store_contact_added (store, E_CONTACT (iter->data));
90 : :
91 : 14 : g_task_return_boolean (task, TRUE);
92 : : }
93 : :
94 : : static void
95 : 14 : valent_contact_cache_add_contacts (ValentContactStore *store,
96 : : GSList *contacts,
97 : : GCancellable *cancellable,
98 : : GAsyncReadyCallback callback,
99 : : gpointer user_data)
100 : : {
101 : 28 : g_autoptr (GTask) task = NULL;
102 : 14 : GSList *additions = NULL;
103 : :
104 [ + - ]: 14 : g_assert (VALENT_IS_CONTACT_STORE (store));
105 [ - + ]: 14 : g_assert (contacts != NULL);
106 [ + + + - : 14 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+ - - - ]
107 : :
108 [ + + ]: 35 : for (const GSList *iter = contacts; iter; iter = iter->next)
109 : 21 : additions = g_slist_append (additions, g_object_ref (iter->data));
110 : :
111 : 14 : task = g_task_new (store, cancellable, callback, user_data);
112 [ + - ]: 14 : g_task_set_source_tag (task, valent_contact_cache_add_contacts);
113 : 14 : g_task_set_task_data (task, additions, object_slist_free);
114 [ + - ]: 14 : g_task_run_in_thread (task, valent_contact_cache_add_task);
115 : 14 : }
116 : :
117 : : static void
118 : 1 : valent_contact_cache_remove_task (GTask *task,
119 : : gpointer source_object,
120 : : gpointer task_data,
121 : : GCancellable *cancellable)
122 : : {
123 : 1 : ValentContactCache *self = VALENT_CONTACT_CACHE (source_object);
124 : 1 : ValentContactStore *store = VALENT_CONTACT_STORE (source_object);
125 : 1 : GSList *uids = task_data;
126 : 1 : GError *error = NULL;
127 : :
128 [ + - ]: 1 : if (g_task_return_error_if_cancelled (task))
129 : 0 : return;
130 : :
131 : 1 : valent_object_lock (VALENT_OBJECT (self));
132 : 1 : e_book_cache_remove_contacts (self->cache,
133 : : uids,
134 : : 0,
135 : : E_CACHE_IS_OFFLINE,
136 : : cancellable,
137 : : &error);
138 : 1 : valent_object_unlock (VALENT_OBJECT (self));
139 : :
140 [ - + ]: 1 : if (error != NULL)
141 : 0 : return g_task_return_error (task, error);
142 : :
143 [ + + ]: 2 : for (const GSList *iter = uids; iter; iter = iter->next)
144 : 1 : valent_contact_store_contact_removed (store, (char *)iter->data);
145 : :
146 : 1 : g_task_return_boolean (task, TRUE);
147 : : }
148 : :
149 : : static void
150 : 1 : valent_contact_cache_remove_contacts (ValentContactStore *store,
151 : : GSList *uids,
152 : : GCancellable *cancellable,
153 : : GAsyncReadyCallback callback,
154 : : gpointer user_data)
155 : : {
156 : 2 : g_autoptr (GTask) task = NULL;
157 : 1 : GSList *removals = NULL;
158 : :
159 [ + - ]: 1 : g_assert (VALENT_IS_CONTACT_STORE (store));
160 [ - + ]: 1 : g_assert (uids != NULL);
161 [ - + - - : 1 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
162 : :
163 [ + + ]: 2 : for (const GSList *iter = uids; iter; iter = iter->next)
164 [ - + ]: 2 : removals = g_slist_append (removals, g_strdup (iter->data));
165 : :
166 : 1 : task = g_task_new (store, cancellable, callback, user_data);
167 [ + - ]: 1 : g_task_set_source_tag (task, valent_contact_cache_remove_contacts);
168 : 1 : g_task_set_task_data (task, removals, string_slist_free);
169 [ + - ]: 1 : g_task_run_in_thread (task, valent_contact_cache_remove_task);
170 : 1 : }
171 : :
172 : : static void
173 : 2 : valent_contact_cache_get_contact_task (GTask *task,
174 : : gpointer source_object,
175 : : gpointer task_data,
176 : : GCancellable *cancellable)
177 : : {
178 : 2 : ValentContactCache *self = VALENT_CONTACT_CACHE (source_object);
179 : 2 : const char *uid = task_data;
180 : 2 : EContact *contact = NULL;
181 : 2 : GError *error = NULL;
182 : :
183 [ + - ]: 2 : if (g_task_return_error_if_cancelled (task))
184 : 1 : return;
185 : :
186 : 2 : valent_object_lock (VALENT_OBJECT (self));
187 : 2 : e_book_cache_get_contact (self->cache,
188 : : uid,
189 : : FALSE,
190 : : &contact,
191 : : cancellable,
192 : : &error);
193 : 2 : valent_object_unlock (VALENT_OBJECT (self));
194 : :
195 [ + + ]: 2 : if (error != NULL)
196 : 1 : return g_task_return_error (task, error);
197 : :
198 : 1 : g_task_return_pointer (task, contact, g_object_unref);
199 : : }
200 : :
201 : : static void
202 : 2 : valent_contact_cache_get_contact (ValentContactStore *store,
203 : : const char *uid,
204 : : GCancellable *cancellable,
205 : : GAsyncReadyCallback callback,
206 : : gpointer user_data)
207 : : {
208 : 4 : g_autoptr (GTask) task = NULL;
209 : :
210 [ + - ]: 2 : g_assert (VALENT_IS_CONTACT_STORE (store));
211 [ - + ]: 2 : g_assert (uid != NULL);
212 [ - + - - : 2 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
213 : :
214 : 2 : task = g_task_new (store, cancellable, callback, user_data);
215 [ + - ]: 2 : g_task_set_source_tag (task, valent_contact_cache_get_contact);
216 [ - + ]: 4 : g_task_set_task_data (task, g_strdup (uid), g_free);
217 [ + - ]: 2 : g_task_run_in_thread (task, valent_contact_cache_get_contact_task);
218 : 2 : }
219 : :
220 : : static void
221 : 12 : valent_contact_cache_query_task (GTask *task,
222 : : gpointer source_object,
223 : : gpointer task_data,
224 : : GCancellable *cancellable)
225 : : {
226 : 12 : ValentContactCache *self = VALENT_CONTACT_CACHE (source_object);
227 : 12 : const char *query = task_data;
228 : 12 : GSList *results = NULL;
229 : 12 : GSList *contacts = NULL;
230 : 12 : GError *error = NULL;
231 : :
232 [ + - ]: 12 : if (g_task_return_error_if_cancelled (task))
233 : 0 : return;
234 : :
235 : 12 : valent_object_lock (VALENT_OBJECT (self));
236 : 12 : e_book_cache_search (self->cache,
237 : : query,
238 : : FALSE,
239 : : &results,
240 : : cancellable,
241 : : &error);
242 : 12 : valent_object_unlock (VALENT_OBJECT (self));
243 : :
244 [ - + ]: 12 : if (error != NULL)
245 : 0 : return g_task_return_error (task, error);
246 : :
247 [ + + ]: 28 : for (const GSList *iter = results; iter; iter = iter->next)
248 : : {
249 : 16 : EBookCacheSearchData *result = iter->data;
250 : 16 : EContact *contact;
251 : :
252 : 16 : contact = e_contact_new_from_vcard (result->vcard);
253 : 16 : contacts = g_slist_prepend (contacts, contact);
254 : : }
255 : 12 : g_slist_free_full (results, e_book_cache_search_data_free);
256 : :
257 : 12 : g_task_return_pointer (task, contacts, object_slist_free);
258 : : }
259 : :
260 : : static void
261 : 12 : valent_contact_cache_query (ValentContactStore *store,
262 : : const char *query,
263 : : GCancellable *cancellable,
264 : : GAsyncReadyCallback callback,
265 : : gpointer user_data)
266 : : {
267 : 24 : g_autoptr (GTask) task = NULL;
268 : :
269 [ + - ]: 12 : g_assert (VALENT_IS_CONTACT_STORE (store));
270 [ - + ]: 12 : g_assert (query != NULL);
271 [ + + + - : 12 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
272 : :
273 : 12 : task = g_task_new (store, cancellable, callback, user_data);
274 [ + - ]: 12 : g_task_set_source_tag (task, valent_contact_cache_query);
275 [ - + ]: 24 : g_task_set_task_data (task, g_strdup (query), g_free);
276 [ + - ]: 12 : g_task_run_in_thread (task, valent_contact_cache_query_task);
277 : 12 : }
278 : :
279 : : /*
280 : : * GObject
281 : : */
282 : : static void
283 : 17 : valent_contact_cache_constructed (GObject *object)
284 : : {
285 : 17 : ValentContactCache *self = VALENT_CONTACT_CACHE (object);
286 : 17 : ValentContactStore *store = VALENT_CONTACT_STORE (object);
287 : 17 : ESource *source;
288 : 34 : g_autoptr (GError) error = NULL;
289 : :
290 : 17 : source = valent_contact_store_get_source (store);
291 : :
292 : : /* This will usually be the path for the contacts plugin, since the device ID
293 : : * is used as the ESource UID. */
294 [ + - ]: 17 : if (self->path == NULL)
295 : : {
296 : 34 : g_autoptr (ValentContext) context = NULL;
297 [ + - ]: 17 : g_autoptr (GFile) contacts_db = NULL;
298 : :
299 : 17 : context = valent_context_new (NULL, "contacts", e_source_get_uid (source));
300 : 17 : contacts_db = valent_context_get_cache_file (context, "contacts.db");
301 [ + - ]: 17 : self->path = g_file_get_path (contacts_db);
302 : : }
303 : :
304 : 17 : valent_object_lock (VALENT_OBJECT (self));
305 : 17 : self->cache = e_book_cache_new (self->path, source, NULL, &error);
306 : 17 : valent_object_unlock (VALENT_OBJECT (self));
307 : :
308 [ - + ]: 17 : if (error != NULL)
309 : 0 : g_critical ("%s(): %s", G_STRFUNC, error->message);
310 : :
311 [ - + ]: 17 : G_OBJECT_CLASS (valent_contact_cache_parent_class)->constructed (object);
312 : 17 : }
313 : :
314 : : static void
315 : 8 : valent_contact_cache_finalize (GObject *object)
316 : : {
317 : 8 : ValentContactCache *self = VALENT_CONTACT_CACHE (object);
318 : :
319 : 8 : valent_object_lock (VALENT_OBJECT (self));
320 [ + - ]: 8 : g_clear_object (&self->cache);
321 [ + - ]: 8 : g_clear_pointer (&self->path, g_free);
322 : 8 : valent_object_unlock (VALENT_OBJECT (self));
323 : :
324 : 8 : G_OBJECT_CLASS (valent_contact_cache_parent_class)->finalize (object);
325 : 8 : }
326 : :
327 : : static void
328 : 1 : valent_contact_cache_get_property (GObject *object,
329 : : guint prop_id,
330 : : GValue *value,
331 : : GParamSpec *pspec)
332 : : {
333 : 1 : ValentContactCache *self = VALENT_CONTACT_CACHE (object);
334 : :
335 [ + - ]: 1 : switch (prop_id)
336 : : {
337 : 1 : case PROP_PATH:
338 : 1 : g_value_set_string (value, self->path);
339 : 1 : break;
340 : :
341 : 0 : default:
342 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
343 : : }
344 : 1 : }
345 : :
346 : : static void
347 : 17 : valent_contact_cache_set_property (GObject *object,
348 : : guint prop_id,
349 : : const GValue *value,
350 : : GParamSpec *pspec)
351 : : {
352 : 17 : ValentContactCache *self = VALENT_CONTACT_CACHE (object);
353 : :
354 [ + - ]: 17 : switch (prop_id)
355 : : {
356 : 17 : case PROP_PATH:
357 : 17 : self->path = g_value_dup_string (value);
358 : 17 : break;
359 : :
360 : 0 : default:
361 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
362 : : }
363 : 17 : }
364 : :
365 : : static void
366 : 7 : valent_contact_cache_class_init (ValentContactCacheClass *klass)
367 : : {
368 : 7 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
369 : 7 : ValentContactStoreClass *store_class = VALENT_CONTACT_STORE_CLASS (klass);
370 : :
371 : 7 : object_class->constructed = valent_contact_cache_constructed;
372 : 7 : object_class->finalize = valent_contact_cache_finalize;
373 : 7 : object_class->get_property = valent_contact_cache_get_property;
374 : 7 : object_class->set_property = valent_contact_cache_set_property;
375 : :
376 : 7 : store_class->add_contacts = valent_contact_cache_add_contacts;
377 : 7 : store_class->remove_contacts = valent_contact_cache_remove_contacts;
378 : 7 : store_class->query = valent_contact_cache_query;
379 : 7 : store_class->get_contact = valent_contact_cache_get_contact;
380 : :
381 : : /**
382 : : * ValentContactCache:path:
383 : : *
384 : : * The path to the database file.
385 : : *
386 : : * Since: 1.0
387 : : */
388 : 14 : properties [PROP_PATH] =
389 : 7 : g_param_spec_string ("path", NULL, NULL,
390 : : NULL,
391 : : (G_PARAM_READWRITE |
392 : : G_PARAM_CONSTRUCT_ONLY |
393 : : G_PARAM_EXPLICIT_NOTIFY |
394 : : G_PARAM_STATIC_STRINGS));
395 : :
396 : 7 : g_object_class_install_properties (object_class, N_PROPERTIES, properties);
397 : 7 : }
398 : :
399 : : static void
400 : 17 : valent_contact_cache_init (ValentContactCache *self)
401 : : {
402 : 17 : }
403 : :
|