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 <gio/gio.h>
9 : : #include <libebook/libebook.h>
10 : : #include <libebook-contacts/libebook-contacts.h>
11 : : #include <libedata-book/libedata-book.h>
12 : : #include <libedataserver/libedataserver.h>
13 : : #include <libtracker-sparql/tracker-sparql.h>
14 : : #include <valent.h>
15 : :
16 : : #include "valent-ebook-store.h"
17 : :
18 : : #define WAIT_FOR_CONNECTED_TIMEOUT 30
19 : :
20 : :
21 : : struct _ValentEBookStore
22 : : {
23 : : ValentObject parent_instance;
24 : :
25 : : TrackerSparqlConnection *connection;
26 : : ESource *source;
27 : : EBookClient *client;
28 : : EBookClientView *view;
29 : : };
30 : :
31 : : static void g_async_initable_iface_init (GAsyncInitableIface *iface);
32 : :
33 : 0 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentEBookStore, valent_ebook_store, VALENT_TYPE_OBJECT,
34 : : G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, g_async_initable_iface_init))
35 : :
36 : : typedef enum {
37 : : PROP_CONNECTION = 1,
38 : : PROP_SOURCE,
39 : : } ValentEBookStoreProperty;
40 : :
41 : : static GParamSpec *properties[PROP_SOURCE + 1] = { NULL, };
42 : :
43 : : static void
44 : 0 : execute_add_contacts_cb (TrackerSparqlConnection *connection,
45 : : GAsyncResult *result,
46 : : gpointer user_data)
47 : : {
48 : 0 : g_autoptr (GError) error = NULL;
49 : :
50 : 0 : if (!tracker_sparql_connection_update_resource_finish (connection, result, &error) &&
51 : 0 : !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
52 : : {
53 : 0 : g_debug ("%s(): %s", G_STRFUNC, error->message);
54 : : }
55 : 0 : }
56 : :
57 : : static void
58 : 0 : on_objects_added (EBookClientView *view,
59 : : GSList *contacts,
60 : : ValentEBookStore *self)
61 : : {
62 : 0 : g_autoptr (TrackerResource) list_resource = NULL;
63 : 0 : g_autoptr (GCancellable) destroy = NULL;
64 : 0 : g_autofree char *list_urn = NULL;
65 : 0 : const char *list_name = NULL;
66 : 0 : const char *list_uid = NULL;
67 : :
68 : 0 : g_assert (E_IS_BOOK_CLIENT_VIEW (view));
69 : 0 : g_assert (VALENT_IS_EBOOK_STORE (self));
70 : :
71 : 0 : list_name = e_source_get_display_name (self->source);
72 : 0 : list_uid = e_source_get_uid (self->source);
73 : 0 : list_urn = tracker_sparql_escape_uri_printf ("urn:valent:contacts:eds:%s",
74 : : list_uid);
75 : 0 : list_resource = tracker_resource_new (list_urn);
76 : 0 : tracker_resource_set_uri (list_resource, "rdf:type", "nco:ContactList");
77 : 0 : if (list_name != NULL)
78 : 0 : tracker_resource_set_string (list_resource, "nie:title", list_name);
79 : :
80 : 0 : for (const GSList *iter = contacts; iter; iter = iter->next)
81 : : {
82 : 0 : EContact *contact = E_CONTACT (iter->data);
83 : 0 : TrackerResource *item_resource = NULL;
84 : :
85 : 0 : item_resource = valent_contact_resource_from_econtact ((EContact *)iter->data);
86 : 0 : if (item_resource != NULL)
87 : : {
88 : 0 : const char *uid = e_contact_get_const (contact, E_CONTACT_UID);
89 : 0 : g_autofree char *item_urn = NULL;
90 : :
91 : 0 : item_urn = tracker_sparql_escape_uri_printf ("%s:%s", list_urn, uid);
92 : 0 : tracker_resource_set_identifier (item_resource, item_urn);
93 : 0 : tracker_resource_add_take_relation (list_resource,
94 : : "nco:containsContact",
95 : 0 : g_steal_pointer (&item_resource));
96 : : }
97 : : }
98 : :
99 : 0 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
100 : 0 : tracker_sparql_connection_update_resource_async (self->connection,
101 : : VALENT_CONTACTS_GRAPH,
102 : : list_resource,
103 : : destroy,
104 : : (GAsyncReadyCallback) execute_add_contacts_cb,
105 : : NULL);
106 : 0 : }
107 : :
108 : : static void
109 : 0 : execute_remove_contacts_cb (TrackerBatch *batch,
110 : : GAsyncResult *result,
111 : : gpointer user_data)
112 : : {
113 : 0 : g_autoptr (GError) error = NULL;
114 : :
115 : 0 : if (!tracker_batch_execute_finish (batch, result, &error) &&
116 : 0 : !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
117 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
118 : 0 : }
119 : :
120 : : static void
121 : 0 : on_objects_removed (EBookClientView *view,
122 : : GSList *uids,
123 : : ValentEBookStore *self)
124 : : {
125 : 0 : g_autoptr (TrackerBatch) batch = NULL;
126 : 0 : g_autoptr (GCancellable) destroy = NULL;
127 : 0 : const char *book_uid = NULL;
128 : :
129 : 0 : g_assert (E_IS_BOOK_CLIENT_VIEW (view));
130 : 0 : g_assert (VALENT_IS_EBOOK_STORE (self));
131 : :
132 : 0 : book_uid = e_source_get_uid (self->source);
133 : 0 : batch = tracker_sparql_connection_create_batch (self->connection);
134 : 0 : for (const GSList *iter = uids; iter; iter = iter->next)
135 : : {
136 : 0 : const char *contact_uid = (const char *)iter->data;
137 : 0 : g_autofree char *sparql = NULL;
138 : :
139 : 0 : sparql = g_strdup_printf ("DELETE DATA {"
140 : : " GRAPH <valent:contacts> {"
141 : : " <urn:valent:contacts:eds:%s:%s> a nco:Contact ;"
142 : : " }"
143 : : "}",
144 : : book_uid,
145 : : contact_uid);
146 : 0 : tracker_batch_add_sparql (batch, sparql);
147 : : }
148 : :
149 : 0 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
150 : 0 : tracker_batch_execute_async (batch,
151 : : destroy,
152 : : (GAsyncReadyCallback) execute_remove_contacts_cb,
153 : : NULL);
154 : 0 : }
155 : :
156 : : static void
157 : 0 : e_client_wait_for_connected_cb (EClient *client,
158 : : GAsyncResult *result,
159 : : gpointer user_data)
160 : : {
161 : 0 : g_autoptr (GError) error = NULL;
162 : :
163 : 0 : if (!e_client_wait_for_connected_finish (client, result, &error) &&
164 : 0 : !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
165 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
166 : 0 : }
167 : :
168 : : static void
169 : 0 : e_book_client_get_view_cb (EBookClient *client,
170 : : GAsyncResult *result,
171 : : ValentEBookStore *self)
172 : : {
173 : 0 : g_autoptr (EBookClientView) view = NULL;
174 : 0 : g_autoptr (GCancellable) destroy = NULL;
175 : 0 : g_autoptr (GError) error = NULL;
176 : :
177 : 0 : if (!e_book_client_get_view_finish (client, result, &view, &error))
178 : : {
179 : 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
180 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
181 : :
182 : 0 : return;
183 : : }
184 : :
185 : 0 : self->view = g_steal_pointer (&view);
186 : 0 : g_signal_connect_object (self->view,
187 : : "objects-added",
188 : : G_CALLBACK (on_objects_added),
189 : : self,
190 : : G_CONNECT_DEFAULT);
191 : 0 : g_signal_connect_object (self->view,
192 : : "objects-removed",
193 : : G_CALLBACK (on_objects_removed),
194 : : self,
195 : : G_CONNECT_DEFAULT);
196 : :
197 : 0 : e_book_client_view_start (self->view, &error);
198 : 0 : if (error != NULL)
199 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
200 : :
201 : 0 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
202 : 0 : e_client_wait_for_connected (E_CLIENT (self->client),
203 : : WAIT_FOR_CONNECTED_TIMEOUT,
204 : : destroy,
205 : : (GAsyncReadyCallback)e_client_wait_for_connected_cb,
206 : : self);
207 : : }
208 : :
209 : : /*
210 : : * GAsyncInitable
211 : : */
212 : : static void
213 : 0 : e_book_client_connect_cb (GObject *object,
214 : : GAsyncResult *result,
215 : : gpointer user_data)
216 : : {
217 : 0 : g_autoptr (GTask) task = G_TASK (user_data);
218 : 0 : ValentEBookStore *self = g_task_get_source_object (user_data);
219 : 0 : g_autoptr (EClient) client = NULL;
220 : 0 : g_autoptr (GCancellable) destroy = NULL;
221 : 0 : g_autoptr (GError) error = NULL;
222 : :
223 : 0 : g_assert (VALENT_IS_EBOOK_STORE (self));
224 : :
225 : 0 : client = e_book_client_connect_finish (result, &error);
226 : 0 : if (client == NULL)
227 : : {
228 : 0 : g_task_return_error (task, g_steal_pointer (&error));
229 : 0 : return;
230 : : }
231 : :
232 : 0 : self->client = g_object_ref ((EBookClient *)client);
233 : 0 : g_task_return_boolean (task, TRUE);
234 : :
235 : : /* Initialization is finished; connect to the backend in the background
236 : : */
237 : 0 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
238 : 0 : e_book_client_get_view (self->client,
239 : : "",
240 : : destroy,
241 : : (GAsyncReadyCallback)e_book_client_get_view_cb,
242 : : self);
243 : : }
244 : :
245 : : static void
246 : 0 : valent_ebook_store_init_async (GAsyncInitable *initable,
247 : : int io_priority,
248 : : GCancellable *cancellable,
249 : : GAsyncReadyCallback callback,
250 : : gpointer user_data)
251 : : {
252 : 0 : ValentEBookStore *self = VALENT_EBOOK_STORE (initable);
253 : 0 : g_autoptr (GTask) task = NULL;
254 : 0 : g_autoptr (GCancellable) destroy = NULL;
255 : :
256 : 0 : g_assert (VALENT_IS_EBOOK_STORE (initable));
257 : 0 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
258 : :
259 : 0 : destroy = valent_object_chain_cancellable (VALENT_OBJECT (initable),
260 : : cancellable);
261 : :
262 : 0 : task = g_task_new (initable, destroy, callback, user_data);
263 : 0 : g_task_set_priority (task, io_priority);
264 : 0 : g_task_set_source_tag (task, valent_ebook_store_init_async);
265 : :
266 : 0 : e_book_client_connect (self->source,
267 : : -1,
268 : : destroy,
269 : : (GAsyncReadyCallback)e_book_client_connect_cb,
270 : : g_steal_pointer (&task));
271 : 0 : }
272 : :
273 : : static void
274 : 0 : g_async_initable_iface_init (GAsyncInitableIface *iface)
275 : : {
276 : 0 : iface->init_async = valent_ebook_store_init_async;
277 : 0 : }
278 : :
279 : : /*
280 : : * ValentObject
281 : : */
282 : : static void
283 : 0 : valent_ebook_store_destroy (ValentObject *object)
284 : : {
285 : 0 : ValentEBookStore *self = VALENT_EBOOK_STORE (object);
286 : :
287 : 0 : if (self->view != NULL)
288 : : {
289 : 0 : g_signal_handlers_disconnect_by_func (self->view, on_objects_added, self);
290 : 0 : g_signal_handlers_disconnect_by_func (self->view, on_objects_removed, self);
291 : 0 : g_clear_object (&self->view);
292 : : }
293 : :
294 : 0 : VALENT_OBJECT_CLASS (valent_ebook_store_parent_class)->destroy (object);
295 : 0 : }
296 : :
297 : : /*
298 : : * GObject
299 : : */
300 : : static void
301 : 0 : valent_ebook_store_finalize (GObject *object)
302 : : {
303 : 0 : ValentEBookStore *self = VALENT_EBOOK_STORE (object);
304 : :
305 : 0 : g_clear_object (&self->connection);
306 : 0 : g_clear_object (&self->source);
307 : 0 : g_clear_object (&self->view);
308 : 0 : g_clear_object (&self->client);
309 : :
310 : 0 : G_OBJECT_CLASS (valent_ebook_store_parent_class)->finalize (object);
311 : 0 : }
312 : :
313 : : static void
314 : 0 : valent_ebook_store_get_property (GObject *object,
315 : : guint prop_id,
316 : : GValue *value,
317 : : GParamSpec *pspec)
318 : : {
319 : 0 : ValentEBookStore *self = VALENT_EBOOK_STORE (object);
320 : :
321 : 0 : switch ((ValentEBookStoreProperty)prop_id)
322 : : {
323 : 0 : case PROP_CONNECTION:
324 : 0 : g_value_set_object (value, self->connection);
325 : 0 : break;
326 : :
327 : 0 : case PROP_SOURCE:
328 : 0 : g_value_set_object (value, self->source);
329 : 0 : break;
330 : :
331 : 0 : default:
332 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
333 : : }
334 : 0 : }
335 : :
336 : : static void
337 : 0 : valent_ebook_store_set_property (GObject *object,
338 : : guint prop_id,
339 : : const GValue *value,
340 : : GParamSpec *pspec)
341 : : {
342 : 0 : ValentEBookStore *self = VALENT_EBOOK_STORE (object);
343 : :
344 : 0 : switch ((ValentEBookStoreProperty)prop_id)
345 : : {
346 : 0 : case PROP_CONNECTION:
347 : 0 : g_set_object (&self->connection, g_value_get_object (value));
348 : 0 : break;
349 : :
350 : 0 : case PROP_SOURCE:
351 : 0 : g_set_object (&self->source, g_value_get_object (value));
352 : 0 : break;
353 : :
354 : 0 : default:
355 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
356 : : }
357 : 0 : }
358 : :
359 : : static void
360 : 0 : valent_ebook_store_class_init (ValentEBookStoreClass *klass)
361 : : {
362 : 0 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
363 : 0 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
364 : :
365 : 0 : object_class->finalize = valent_ebook_store_finalize;
366 : 0 : object_class->get_property = valent_ebook_store_get_property;
367 : 0 : object_class->set_property = valent_ebook_store_set_property;
368 : :
369 : 0 : vobject_class->destroy = valent_ebook_store_destroy;
370 : :
371 : 0 : properties [PROP_CONNECTION] =
372 : 0 : g_param_spec_object ("connection", NULL, NULL,
373 : : TRACKER_TYPE_SPARQL_CONNECTION,
374 : : (G_PARAM_READWRITE |
375 : : G_PARAM_CONSTRUCT_ONLY |
376 : : G_PARAM_STATIC_STRINGS));
377 : :
378 : 0 : properties [PROP_SOURCE] =
379 : 0 : g_param_spec_object ("source", NULL, NULL,
380 : : E_TYPE_SOURCE,
381 : : (G_PARAM_READWRITE |
382 : : G_PARAM_CONSTRUCT_ONLY |
383 : : G_PARAM_STATIC_STRINGS));
384 : :
385 : 0 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
386 : 0 : }
387 : :
388 : : static void
389 : 0 : valent_ebook_store_init (ValentEBookStore *self)
390 : : {
391 : 0 : }
392 : :
|