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-ebook-store"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <valent.h>
9 : :
10 : : #include "valent-ebook-store.h"
11 : : #include "valent-eds-utils.h"
12 : :
13 : : #define WAIT_FOR_CONNECTED_TIMEOUT 30
14 : :
15 : :
16 : : struct _ValentEBookStore
17 : : {
18 : : ValentContactStore parent_instance;
19 : :
20 : : EBookClient *client;
21 : : EBookClientView *view;
22 : : };
23 : :
24 : : static void g_async_initable_iface_init (GAsyncInitableIface *iface);
25 : :
26 : 0 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentEBookStore, valent_ebook_store, VALENT_TYPE_CONTACT_STORE,
27 : : G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, g_async_initable_iface_init))
28 : :
29 : :
30 : : /*
31 : : * ValentContactStore
32 : : */
33 : : static void
34 : 0 : valent_ebook_store_add_contacts_cb (GObject *object,
35 : : GAsyncResult *result,
36 : : gpointer user_data)
37 : : {
38 : 0 : EBookClient *client = E_BOOK_CLIENT (object);
39 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
40 : 0 : GError *error = NULL;
41 : :
42 : 0 : if (!e_book_client_add_contacts_finish (client, result, NULL, &error))
43 : 0 : return g_task_return_error (task, error);
44 : :
45 : 0 : g_task_return_boolean (task, TRUE);
46 : : }
47 : :
48 : : static void
49 : 0 : valent_ebook_store_add_contacts (ValentContactStore *store,
50 : : GSList *contacts,
51 : : GCancellable *cancellable,
52 : : GAsyncReadyCallback callback,
53 : : gpointer user_data)
54 : : {
55 : 0 : ValentEBookStore *self = VALENT_EBOOK_STORE (store);
56 : 0 : g_autoptr (GTask) task = NULL;
57 : :
58 : 0 : g_assert (VALENT_IS_CONTACT_STORE (store));
59 : 0 : g_assert (contacts != NULL);
60 : 0 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
61 : :
62 : 0 : task = g_task_new (store, cancellable, callback, user_data);
63 : 0 : g_task_set_source_tag (task, valent_ebook_store_add_contacts);
64 : :
65 : 0 : e_book_client_add_contacts (self->client,
66 : : contacts,
67 : : E_BOOK_OPERATION_FLAG_CONFLICT_USE_NEWER,
68 : : cancellable,
69 : : valent_ebook_store_add_contacts_cb,
70 : : g_steal_pointer (&task));
71 : 0 : }
72 : :
73 : : static void
74 : 0 : valent_ebook_store_remove_cb (GObject *object,
75 : : GAsyncResult *result,
76 : : gpointer user_data)
77 : : {
78 : 0 : EBookClient *client = E_BOOK_CLIENT (object);
79 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
80 : 0 : GError *error = NULL;
81 : :
82 : 0 : if (!e_book_client_remove_contacts_finish (client, result, &error))
83 : 0 : return g_task_return_error (task, error);
84 : :
85 : 0 : g_task_return_boolean (task, TRUE);
86 : : }
87 : :
88 : : static void
89 : 0 : valent_ebook_store_remove_contacts (ValentContactStore *store,
90 : : GSList *uids,
91 : : GCancellable *cancellable,
92 : : GAsyncReadyCallback callback,
93 : : gpointer user_data)
94 : : {
95 : 0 : ValentEBookStore *self = VALENT_EBOOK_STORE (store);
96 : 0 : g_autoptr (GTask) task = NULL;
97 : :
98 : 0 : g_assert (VALENT_IS_CONTACT_STORE (store));
99 : 0 : g_assert (uids != NULL);
100 : 0 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
101 : :
102 : 0 : task = g_task_new (store, cancellable, callback, user_data);
103 : 0 : g_task_set_source_tag (task, valent_ebook_store_remove_contacts);
104 : :
105 : 0 : e_book_client_remove_contacts (self->client,
106 : : uids,
107 : : E_BOOK_OPERATION_FLAG_NONE,
108 : : cancellable,
109 : : valent_ebook_store_remove_cb,
110 : : g_steal_pointer (&task));
111 : 0 : }
112 : :
113 : : static void
114 : 0 : valent_ebook_store_get_contact_cb (GObject *object,
115 : : GAsyncResult *result,
116 : : gpointer user_data)
117 : : {
118 : 0 : EBookClient *client = E_BOOK_CLIENT (object);
119 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
120 : 0 : g_autoptr (EContact) contact = NULL;
121 : 0 : GError *error = NULL;
122 : :
123 : 0 : if (!e_book_client_get_contact_finish (client, result, &contact, &error))
124 : 0 : return g_task_return_error (task, error);
125 : :
126 : 0 : g_task_return_pointer (task, g_steal_pointer (&contact), g_object_unref);
127 : : }
128 : :
129 : : static void
130 : 0 : valent_ebook_store_get_contact (ValentContactStore *store,
131 : : const char *uid,
132 : : GCancellable *cancellable,
133 : : GAsyncReadyCallback callback,
134 : : gpointer user_data)
135 : : {
136 : 0 : ValentEBookStore *self = VALENT_EBOOK_STORE (store);
137 : 0 : g_autoptr (GTask) task = NULL;
138 : :
139 : 0 : g_assert (VALENT_IS_CONTACT_STORE (store));
140 : 0 : g_assert (uid != NULL);
141 : 0 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
142 : :
143 : 0 : task = g_task_new (store, cancellable, callback, user_data);
144 : 0 : g_task_set_source_tag (task, valent_ebook_store_get_contact);
145 : :
146 : 0 : e_book_client_get_contact (self->client,
147 : : uid,
148 : : cancellable,
149 : : valent_ebook_store_get_contact_cb,
150 : : g_steal_pointer (&task));
151 : 0 : }
152 : :
153 : : static void
154 : 0 : valent_ebook_store_query_cb (GObject *object,
155 : : GAsyncResult *result,
156 : : gpointer user_data)
157 : : {
158 : 0 : EBookClient *client = E_BOOK_CLIENT (object);
159 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
160 : 0 : GSList *results = NULL;
161 : 0 : GError *error = NULL;
162 : :
163 : 0 : if (!e_book_client_get_contacts_finish (client, result, &results, &error))
164 : 0 : return g_task_return_error (task, error);
165 : :
166 : 0 : g_task_return_pointer (task, g_steal_pointer (&results), g_object_unref);
167 : : }
168 : :
169 : : static void
170 : 0 : valent_ebook_store_query (ValentContactStore *store,
171 : : const char *query,
172 : : GCancellable *cancellable,
173 : : GAsyncReadyCallback callback,
174 : : gpointer user_data)
175 : : {
176 : 0 : ValentEBookStore *self = VALENT_EBOOK_STORE (store);
177 : 0 : g_autoptr (GTask) task = NULL;
178 : :
179 : 0 : g_assert (VALENT_IS_CONTACT_STORE (store));
180 : 0 : g_assert (query != NULL);
181 : 0 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
182 : :
183 : 0 : task = g_task_new (store, cancellable, callback, user_data);
184 : 0 : g_task_set_source_tag (task, valent_ebook_store_query);
185 : 0 : g_task_set_task_data (task, g_strdup (query), g_free);
186 : :
187 : 0 : e_book_client_get_contacts (self->client,
188 : : query,
189 : : cancellable,
190 : : valent_ebook_store_query_cb,
191 : : g_steal_pointer (&task));
192 : 0 : }
193 : :
194 : : static void
195 : 0 : on_objects_added (EBookClientView *view,
196 : : GSList *contacts,
197 : : ValentContactStore *store)
198 : : {
199 : 0 : g_assert (E_IS_BOOK_CLIENT_VIEW (view));
200 : 0 : g_assert (VALENT_IS_CONTACT_STORE (store));
201 : :
202 : 0 : for (const GSList *iter = contacts; iter; iter = iter->next)
203 : 0 : valent_contact_store_contact_added (store, E_CONTACT (iter->data));
204 : 0 : }
205 : :
206 : : static void
207 : 0 : on_objects_removed (EBookClientView *view,
208 : : GSList *uids,
209 : : ValentContactStore *store)
210 : : {
211 : 0 : g_assert (E_IS_BOOK_CLIENT_VIEW (view));
212 : 0 : g_assert (VALENT_IS_CONTACT_STORE (store));
213 : :
214 : 0 : for (const GSList *iter = uids; iter; iter = iter->next)
215 : 0 : valent_contact_store_contact_removed (store, (const char *)iter->data);
216 : 0 : }
217 : :
218 : : static void
219 : 0 : e_client_wait_for_connected_cb (EClient *client,
220 : : GAsyncResult *result,
221 : : ValentContactStore *store)
222 : : {
223 : 0 : g_autoptr (GError) error = NULL;
224 : :
225 : 0 : if (!e_client_wait_for_connected_finish (client, result, &error) &&
226 : 0 : !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
227 : : {
228 : 0 : g_debug ("%s (%s): failed to connect backend: %s",
229 : : G_OBJECT_TYPE_NAME (store),
230 : : valent_contact_store_get_name (store),
231 : : error->message);
232 : : }
233 : 0 : }
234 : :
235 : : static void
236 : 0 : e_book_client_get_view_cb (EBookClient *client,
237 : : GAsyncResult *result,
238 : : ValentEBookStore *self)
239 : : {
240 : 0 : g_autoptr (EBookClientView) view = NULL;
241 : 0 : g_autoptr (GCancellable) destroy = NULL;
242 : 0 : g_autoptr (GError) error = NULL;
243 : :
244 : 0 : if (!e_book_client_get_view_finish (client, result, &view, &error))
245 : : {
246 : : /* If the operation was cancelled then the store has been destroyed */
247 : 0 : if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
248 : 0 : return;
249 : :
250 : 0 : g_warning ("%s (%s): failed to subscribe to changes: %s",
251 : : G_OBJECT_TYPE_NAME (self),
252 : : valent_contact_store_get_name (VALENT_CONTACT_STORE (self)),
253 : : error->message);
254 : : }
255 : : else
256 : : {
257 : 0 : self->view = g_steal_pointer (&view);
258 : 0 : g_signal_connect_object (self->view,
259 : : "objects-added",
260 : : G_CALLBACK (on_objects_added),
261 : : self, 0);
262 : 0 : g_signal_connect_object (self->view,
263 : : "objects-removed",
264 : : G_CALLBACK (on_objects_removed),
265 : : self, 0);
266 : : }
267 : :
268 : : /* Attempt to connect to the backend */
269 : 0 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
270 : 0 : e_client_wait_for_connected (E_CLIENT (self->client),
271 : : WAIT_FOR_CONNECTED_TIMEOUT,
272 : : destroy,
273 : : (GAsyncReadyCallback)e_client_wait_for_connected_cb,
274 : : self);
275 : : }
276 : :
277 : : /*
278 : : * GAsyncInitable
279 : : */
280 : : static void
281 : 0 : e_book_client_connect_cb (GObject *object,
282 : : GAsyncResult *result,
283 : : gpointer user_data)
284 : : {
285 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
286 : 0 : ValentEBookStore *self = g_task_get_source_object (user_data);
287 : 0 : g_autoptr (EClient) client = NULL;
288 : 0 : g_autoptr (GCancellable) destroy = NULL;
289 : 0 : g_autoptr (GError) error = NULL;
290 : :
291 : 0 : g_assert (VALENT_IS_EBOOK_STORE (self));
292 : :
293 : 0 : if ((client = e_book_client_connect_finish (result, &error)) == NULL)
294 : 0 : return g_task_return_error (task, g_steal_pointer (&error));
295 : :
296 : 0 : self->client = g_object_ref ((EBookClient *)client);
297 : 0 : g_task_return_boolean (task, TRUE);
298 : :
299 : : /* Initialization is finished; connect to the backend in the background */
300 : 0 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
301 : 0 : e_book_client_get_view (self->client,
302 : : "",
303 : : destroy,
304 : : (GAsyncReadyCallback)e_book_client_get_view_cb,
305 : : self);
306 : : }
307 : :
308 : : static void
309 : 0 : valent_ebook_store_init_async (GAsyncInitable *initable,
310 : : int io_priority,
311 : : GCancellable *cancellable,
312 : : GAsyncReadyCallback callback,
313 : : gpointer user_data)
314 : : {
315 : 0 : ValentContactStore *store = VALENT_CONTACT_STORE (initable);
316 : 0 : g_autoptr (GTask) task = NULL;
317 : 0 : g_autoptr (GCancellable) destroy = NULL;
318 : :
319 : 0 : g_assert (VALENT_IS_EBOOK_STORE (initable));
320 : 0 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
321 : :
322 : : /* Cancel initialization if the object is destroyed */
323 : 0 : destroy = valent_object_chain_cancellable (VALENT_OBJECT (initable),
324 : : cancellable);
325 : :
326 : 0 : task = g_task_new (initable, destroy, callback, user_data);
327 : 0 : g_task_set_priority (task, io_priority);
328 : 0 : g_task_set_source_tag (task, valent_ebook_store_init_async);
329 : :
330 : 0 : e_book_client_connect (valent_contact_store_get_source (store),
331 : : -1,
332 : : destroy,
333 : : (GAsyncReadyCallback)e_book_client_connect_cb,
334 : : g_steal_pointer (&task));
335 : 0 : }
336 : :
337 : : static void
338 : 0 : g_async_initable_iface_init (GAsyncInitableIface *iface)
339 : : {
340 : 0 : iface->init_async = valent_ebook_store_init_async;
341 : 0 : }
342 : :
343 : : /*
344 : : * ValentObject
345 : : */
346 : : static void
347 : 0 : valent_ebook_store_destroy (ValentObject *object)
348 : : {
349 : 0 : ValentEBookStore *self = VALENT_EBOOK_STORE (object);
350 : :
351 : 0 : if (self->view != NULL)
352 : : {
353 : 0 : g_signal_handlers_disconnect_by_data (self->view, self);
354 : 0 : g_clear_object (&self->view);
355 : : }
356 : :
357 : 0 : VALENT_OBJECT_CLASS (valent_ebook_store_parent_class)->destroy (object);
358 : 0 : }
359 : :
360 : : /*
361 : : * GObject
362 : : */
363 : : static void
364 : 0 : valent_ebook_store_finalize (GObject *object)
365 : : {
366 : 0 : ValentEBookStore *self = VALENT_EBOOK_STORE (object);
367 : :
368 : 0 : g_clear_object (&self->view);
369 : 0 : g_clear_object (&self->client);
370 : :
371 : 0 : G_OBJECT_CLASS (valent_ebook_store_parent_class)->finalize (object);
372 : 0 : }
373 : :
374 : : static void
375 : 0 : valent_ebook_store_class_init (ValentEBookStoreClass *klass)
376 : : {
377 : 0 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
378 : 0 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
379 : 0 : ValentContactStoreClass *store_class = VALENT_CONTACT_STORE_CLASS (klass);
380 : :
381 : 0 : object_class->finalize = valent_ebook_store_finalize;
382 : :
383 : 0 : vobject_class->destroy = valent_ebook_store_destroy;
384 : :
385 : 0 : store_class->add_contacts = valent_ebook_store_add_contacts;
386 : 0 : store_class->remove_contacts = valent_ebook_store_remove_contacts;
387 : 0 : store_class->get_contact = valent_ebook_store_get_contact;
388 : 0 : store_class->query = valent_ebook_store_query;
389 : : }
390 : :
391 : : static void
392 : 0 : valent_ebook_store_init (ValentEBookStore *self)
393 : : {
394 : 0 : }
395 : :
|