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-messages-adapter"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <libvalent-core.h>
10 : : #include <libtracker-sparql/tracker-sparql.h>
11 : :
12 : : #include "valent-message.h"
13 : : #include "valent-message-attachment.h"
14 : : #include "valent-message-thread.h"
15 : :
16 : : #include "valent-messages.h"
17 : : #include "valent-messages-adapter.h"
18 : : #include "valent-messages-adapter-private.h"
19 : :
20 : : #define GET_THREAD_RQ "/ca/andyholmes/Valent/sparql/get-thread.rq"
21 : : #define GET_THREADS_RQ "/ca/andyholmes/Valent/sparql/get-threads.rq"
22 : : #define SEARCH_MESSAGES_RQ "/ca/andyholmes/Valent/sparql/search-messages.rq"
23 : :
24 : :
25 : : /**
26 : : * ValentMessagesAdapter:
27 : : *
28 : : * An abstract base class for messaging providers.
29 : : *
30 : : * `ValentMessagesAdapter` is a base class for plugins that provide an
31 : : * interface to manage messaging (i.e. SMS/MMS). This usually means loading
32 : : * message history into the SPARQL database and (optionally) sending outgoing
33 : : * messages.
34 : : *
35 : : * ## `.plugin` File
36 : : *
37 : : * Implementations may define the following extra fields in the `.plugin` file:
38 : : *
39 : : * - `X-MessagesAdapterPriority`
40 : : *
41 : : * An integer indicating the adapter priority. The implementation with the
42 : : * lowest value will be used as the primary adapter.
43 : : *
44 : : * Since: 1.0
45 : : */
46 : :
47 : : typedef struct
48 : : {
49 : : TrackerSparqlConnection *connection;
50 : : TrackerNotifier *notifier;
51 : : TrackerSparqlStatement *get_thread_stmt;
52 : : TrackerSparqlStatement *get_threads_stmt;
53 : : GRegex *iri_pattern;
54 : : GCancellable *cancellable;
55 : :
56 : : /* list model */
57 : : GPtrArray *items;
58 : : } ValentMessagesAdapterPrivate;
59 : :
60 : : static void g_list_model_iface_init (GListModelInterface *iface);
61 : :
62 : : static void valent_messages_adapter_load_thread (ValentMessagesAdapter *self,
63 : : const char *iri);
64 : :
65 [ + + + - ]: 363415 : G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ValentMessagesAdapter, valent_messages_adapter, VALENT_TYPE_EXTENSION,
66 : : G_ADD_PRIVATE (ValentMessagesAdapter)
67 : : G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init))
68 : :
69 : : typedef enum
70 : : {
71 : : PROP_CONNECTION = 1,
72 : : } ValentMessagesAdapterProperty;
73 : :
74 : : static GParamSpec *properties[PROP_CONNECTION + 1] = { NULL, };
75 : :
76 : : static void
77 : 9 : valent_messages_adapter_load_thread_cb (GObject *object,
78 : : GAsyncResult *result,
79 : : gpointer user_data)
80 : : {
81 : 9 : ValentMessagesAdapter *self = VALENT_MESSAGES_ADAPTER (object);
82 : 9 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
83 : 9 : g_autoptr (GListModel) list = NULL;
84 : 9 : unsigned int position;
85 : 9 : g_autoptr (GError) error = NULL;
86 : :
87 : 9 : list = g_task_propagate_pointer (G_TASK (result), &error);
88 [ - + ]: 9 : if (list == NULL)
89 : : {
90 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
91 : : {
92 : 0 : const char *urn = g_task_get_task_data (G_TASK (result));
93 : 0 : g_warning ("%s(): %s: %s", G_STRFUNC, urn, error->message);
94 : : }
95 : :
96 [ # # ]: 0 : return;
97 : : }
98 : :
99 : 9 : position = priv->items->len;
100 : 9 : g_ptr_array_add (priv->items, g_steal_pointer (&list));
101 [ - + ]: 9 : g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
102 : : }
103 : :
104 : : static inline gboolean
105 : 2 : valent_messages_adapter_equal_func (gconstpointer a,
106 : : gconstpointer b)
107 : : {
108 : 2 : const char *iri = valent_object_get_iri ((ValentObject *)a);
109 : :
110 : 2 : return g_utf8_collate (iri, (const char *)b) == 0;
111 : : }
112 : :
113 : : static void
114 : 1 : valent_messages_adapter_remove_thread (ValentMessagesAdapter *self,
115 : : const char *iri)
116 : : {
117 : 1 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
118 : 1 : unsigned int position = 0;
119 : :
120 [ - + ]: 1 : g_assert (VALENT_IS_MESSAGES_ADAPTER (self));
121 : :
122 [ - + ]: 1 : if (!g_ptr_array_find_with_equal_func (priv->items,
123 : : iri,
124 : : valent_messages_adapter_equal_func,
125 : : &position))
126 : : {
127 : 0 : g_warning ("Resource \"%s\" not found in \"%s\"",
128 : : iri,
129 : : G_OBJECT_TYPE_NAME (self));
130 : 0 : return;
131 : : }
132 : :
133 : 1 : g_ptr_array_remove_index (priv->items, position);
134 : 1 : g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
135 : : }
136 : :
137 : : static inline gboolean
138 : 42 : valent_messages_adapter_event_is_thread (ValentMessagesAdapter *self,
139 : : const char *iri)
140 : : {
141 : 42 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
142 : :
143 : 42 : return g_regex_match (priv->iri_pattern, iri, G_REGEX_MATCH_DEFAULT, NULL);
144 : : }
145 : :
146 : : static void
147 : 15 : on_notifier_event (TrackerNotifier *notifier,
148 : : const char *service,
149 : : const char *graph,
150 : : GPtrArray *events,
151 : : ValentMessagesAdapter *self)
152 : : {
153 [ - + ]: 15 : g_assert (VALENT_IS_MESSAGES_ADAPTER (self));
154 : :
155 [ + - ]: 15 : if (g_strcmp0 (VALENT_MESSAGES_GRAPH, graph) != 0)
156 : : return;
157 : :
158 [ + + ]: 57 : for (unsigned int i = 0; i < events->len; i++)
159 : : {
160 : 42 : TrackerNotifierEvent *event = g_ptr_array_index (events, i);
161 : 42 : const char *urn = tracker_notifier_event_get_urn (event);
162 : :
163 [ + + ]: 42 : if (!valent_messages_adapter_event_is_thread (self, urn))
164 : 28 : continue;
165 : :
166 [ + + - + ]: 14 : switch (tracker_notifier_event_get_event_type (event))
167 : : {
168 : 9 : case TRACKER_NOTIFIER_EVENT_CREATE:
169 : 9 : VALENT_NOTE ("CREATE: %s", urn);
170 : 9 : valent_messages_adapter_load_thread (self, urn);
171 : 9 : break;
172 : :
173 : 1 : case TRACKER_NOTIFIER_EVENT_DELETE:
174 : 1 : VALENT_NOTE ("DELETE: %s", urn);
175 : 1 : valent_messages_adapter_remove_thread (self, urn);
176 : 1 : break;
177 : :
178 : : case TRACKER_NOTIFIER_EVENT_UPDATE:
179 : : VALENT_NOTE ("UPDATE: %s", urn);
180 : : // valent_message_adapter_update_thread (self, urn);
181 : : break;
182 : :
183 : 0 : default:
184 : 42 : g_warn_if_reached ();
185 : : }
186 : : }
187 : : }
188 : :
189 : : static gboolean
190 : 8 : valent_messages_adapter_open (ValentMessagesAdapter *self,
191 : : GError **error)
192 : : {
193 : 8 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
194 : 8 : ValentContext *context = NULL;
195 : 16 : g_autoptr (GFile) file = NULL;
196 [ + - ]: 8 : g_autoptr (GFile) ontology = NULL;
197 : 8 : const char *iri = NULL;
198 [ + - ]: 8 : g_autofree char *iri_pattern = NULL;
199 : :
200 : 8 : context = valent_extension_get_context (VALENT_EXTENSION (self));
201 : 8 : file = valent_context_get_cache_file (context, "metadata");
202 : 8 : ontology = g_file_new_for_uri ("resource:///ca/andyholmes/Valent/ontologies/");
203 : :
204 : 16 : priv->connection =
205 : 8 : tracker_sparql_connection_new (TRACKER_SPARQL_CONNECTION_FLAGS_NONE,
206 : : file,
207 : : ontology,
208 : : NULL,
209 : : error);
210 : :
211 [ + - ]: 8 : if (priv->connection == NULL)
212 : : return FALSE;
213 : :
214 : 8 : iri = valent_object_get_iri (VALENT_OBJECT (self));
215 : 8 : iri_pattern = g_strdup_printf ("^%s:([^:]+)$", iri);
216 : 8 : priv->iri_pattern = g_regex_new (iri_pattern,
217 : : G_REGEX_OPTIMIZE,
218 : : G_REGEX_MATCH_DEFAULT,
219 : : NULL);
220 : :
221 : 8 : priv->notifier = tracker_sparql_connection_create_notifier (priv->connection);
222 : 8 : g_signal_connect_object (priv->notifier,
223 : : "events",
224 : : G_CALLBACK (on_notifier_event),
225 : : self,
226 : : G_CONNECT_DEFAULT);
227 : :
228 : 8 : return TRUE;
229 : : }
230 : :
231 : : /*
232 : : * GListModel
233 : : */
234 : : static gpointer
235 : 6 : valent_messages_adapter_get_item (GListModel *list,
236 : : unsigned int position)
237 : : {
238 : 6 : ValentMessagesAdapter *self = VALENT_MESSAGES_ADAPTER (list);
239 : 6 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
240 : 12 : g_autofree char *iri = NULL;
241 : 6 : g_autoptr (ValentMessage) latest_message = NULL;
242 [ + - ]: 6 : g_auto (GStrv) participants = NULL;
243 : :
244 [ - + ]: 6 : g_assert (VALENT_IS_MESSAGES_ADAPTER (self));
245 : :
246 [ + - ]: 6 : if G_UNLIKELY (position >= priv->items->len)
247 : : return NULL;
248 : :
249 : : // FIXME: a duplicate thread is returned to avoid accruing memory
250 : : // return g_object_ref (g_ptr_array_index (priv->items, position));
251 : 6 : g_object_get (g_ptr_array_index (priv->items, position),
252 : : "iri", &iri,
253 : : "latest-message", &latest_message,
254 : : "participants", &participants,
255 : : NULL);
256 : :
257 : 6 : return g_object_new (VALENT_TYPE_MESSAGE_THREAD,
258 : : "connection", priv->connection,
259 : : "notifier", priv->notifier,
260 : : "iri", iri,
261 : : "latest-message", latest_message,
262 : : "participants", participants,
263 : : NULL);
264 : : }
265 : :
266 : : static GType
267 : 1 : valent_messages_adapter_get_item_type (GListModel *list)
268 : : {
269 : 1 : return G_TYPE_LIST_MODEL;
270 : : }
271 : :
272 : : static unsigned int
273 : 181458 : valent_messages_adapter_get_n_items (GListModel *list)
274 : : {
275 : 181458 : ValentMessagesAdapter *self = VALENT_MESSAGES_ADAPTER (list);
276 : 181458 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
277 : :
278 [ - + ]: 181458 : g_assert (VALENT_IS_MESSAGES_ADAPTER (self));
279 : :
280 : 181458 : return priv->items->len;
281 : : }
282 : :
283 : : static void
284 : 53 : g_list_model_iface_init (GListModelInterface *iface)
285 : : {
286 : 53 : iface->get_item = valent_messages_adapter_get_item;
287 : 53 : iface->get_item_type = valent_messages_adapter_get_item_type;
288 : 53 : iface->get_n_items = valent_messages_adapter_get_n_items;
289 : 53 : }
290 : :
291 : : /*
292 : : * ValentMessagesAdapterPrivate
293 : : *
294 : : */
295 : : ValentMessage *
296 : 13 : valent_message_from_sparql_cursor (TrackerSparqlCursor *cursor,
297 : : ValentMessage *current)
298 : : {
299 : 13 : ValentMessage *ret = NULL;
300 : 13 : int64_t message_id;
301 : :
302 [ - + ]: 13 : g_assert (TRACKER_IS_SPARQL_CURSOR (cursor));
303 [ + + + - ]: 13 : g_assert (current == NULL || VALENT_IS_MESSAGE (current));
304 : :
305 : 13 : message_id = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_ID);
306 [ + + - + ]: 13 : if (current != NULL && valent_message_get_id (current) == message_id)
307 : : {
308 : 0 : ret = g_object_ref (current);
309 : : }
310 : : else
311 : : {
312 : 13 : const char *iri = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_IRI, NULL);
313 : 26 : g_autoptr (GListStore) attachments = NULL;
314 : 13 : ValentMessageBox box = VALENT_MESSAGE_BOX_ALL;
315 : 13 : int64_t date = 0;
316 [ + - ]: 13 : g_autoptr (GDateTime) datetime = NULL;
317 : 13 : gboolean read = FALSE;
318 : 13 : const char *recipients = NULL;
319 [ + - ]: 13 : g_auto (GStrv) recipientv = NULL;
320 : 13 : const char *sender = NULL;
321 : 13 : int64_t subscription_id = -1;
322 : 13 : const char *text = NULL;
323 : 13 : int64_t thread_id = -1;
324 : :
325 : 13 : attachments = g_list_store_new (VALENT_TYPE_MESSAGE_ATTACHMENT);
326 : 13 : box = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_BOX);
327 : :
328 : 13 : datetime = tracker_sparql_cursor_get_datetime (cursor, CURSOR_MESSAGE_DATE);
329 [ + - ]: 13 : if (datetime != NULL)
330 : 13 : date = g_date_time_to_unix_usec (datetime) / 1000;
331 : :
332 : 13 : read = tracker_sparql_cursor_get_boolean (cursor, CURSOR_MESSAGE_READ);
333 : :
334 : 13 : recipients = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_RECIPIENTS, NULL);
335 [ + + ]: 13 : if (recipients != NULL)
336 : 4 : recipientv = g_strsplit (recipients, ",", -1);
337 : :
338 [ + + ]: 13 : if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_SENDER))
339 : 2 : sender = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_SENDER, NULL);
340 : :
341 [ + - ]: 13 : if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_SUBSCRIPTION_ID))
342 : 13 : subscription_id = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_SUBSCRIPTION_ID);
343 : :
344 [ + + ]: 13 : if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_TEXT))
345 : 5 : text = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_TEXT, NULL);
346 : :
347 : 13 : thread_id = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_THREAD_ID);
348 : :
349 [ + + ]: 13 : ret = g_object_new (VALENT_TYPE_MESSAGE,
350 : : "iri", iri,
351 : : "box", box,
352 : : "date", date,
353 : : "id", message_id,
354 : : "read", read,
355 : : "recipients", recipientv,
356 : : "sender", sender,
357 : : "subscription-id", subscription_id,
358 : : "text", text,
359 : : "thread-id", thread_id,
360 : : "attachments", attachments,
361 : : NULL);
362 : : }
363 : :
364 : : /* Attachment
365 : : */
366 [ + + ]: 13 : if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_ATTACHMENT_IRI))
367 : : {
368 : 3 : const char *iri = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_ATTACHMENT_IRI, NULL);
369 : 3 : GListModel *attachments = valent_message_get_attachments (ret);
370 : 16 : g_autoptr (ValentMessageAttachment) attachment = NULL;
371 [ + - ]: 3 : g_autoptr (GIcon) preview = NULL;
372 [ + - ]: 3 : g_autoptr (GFile) file = NULL;
373 : :
374 [ - + ]: 3 : if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_ATTACHMENT_PREVIEW))
375 : : {
376 : 3 : const char *base64_data;
377 : :
378 : 3 : base64_data = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_ATTACHMENT_PREVIEW, NULL);
379 [ - + ]: 3 : if (base64_data != NULL)
380 : : {
381 : 3 : g_autoptr (GBytes) bytes = NULL;
382 : 3 : unsigned char *data;
383 : 3 : size_t len;
384 : :
385 : 3 : data = g_base64_decode (base64_data, &len);
386 : 3 : bytes = g_bytes_new_take (g_steal_pointer (&data), len);
387 [ + - ]: 3 : preview = g_bytes_icon_new (bytes);
388 : : }
389 : : }
390 : :
391 [ + + ]: 3 : if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_ATTACHMENT_FILE))
392 : : {
393 : 1 : const char *file_uri;
394 : :
395 : 1 : file_uri = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_ATTACHMENT_FILE, NULL);
396 [ - + ]: 1 : if (file_uri != NULL)
397 : 1 : file = g_file_new_for_uri (file_uri);
398 : : }
399 : :
400 : 3 : attachment = g_object_new (VALENT_TYPE_MESSAGE_ATTACHMENT,
401 : : "iri", iri,
402 : : "preview", preview,
403 : : "file", file,
404 : : NULL);
405 [ + + ]: 3 : g_list_store_append (G_LIST_STORE (attachments), attachment);
406 : : }
407 : :
408 : 13 : return g_steal_pointer (&ret);
409 : : }
410 : :
411 : : static ValentMessageThread *
412 : 9 : valent_message_thread_from_sparql_cursor (ValentMessagesAdapter *self,
413 : : TrackerSparqlCursor *cursor)
414 : : {
415 : 9 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
416 : 18 : g_autoptr (ValentMessage) message = NULL;
417 : 9 : const char *iri = NULL;
418 : 9 : const char *participants = NULL;
419 [ + - ]: 9 : g_auto (GStrv) participantv = NULL;
420 : :
421 [ - + ]: 9 : g_assert (TRACKER_IS_SPARQL_CURSOR (cursor));
422 : :
423 : : /* NOTE: typically there won't be a thread without a message, but this may be
424 : : * the case as an implementation detail.
425 : : */
426 : 9 : iri = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_IRI, NULL);
427 [ + - ]: 9 : if (iri != NULL)
428 : : {
429 : 18 : g_autoptr (GListStore) attachments = NULL;
430 : 9 : ValentMessageBox box = VALENT_MESSAGE_BOX_ALL;
431 : 9 : int64_t date = 0;
432 [ + - ]: 9 : g_autoptr (GDateTime) datetime = NULL;
433 : 9 : int64_t message_id;
434 : 9 : gboolean read = FALSE;
435 : 9 : const char *recipients = NULL;
436 [ + - ]: 9 : g_auto (GStrv) recipientv = NULL;
437 : 9 : const char *sender = NULL;
438 : 9 : int64_t subscription_id = -1;
439 : 9 : const char *text = NULL;
440 : 9 : int64_t thread_id = -1;
441 : :
442 : 9 : attachments = g_list_store_new (VALENT_TYPE_MESSAGE_ATTACHMENT);
443 : 9 : box = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_BOX);
444 : :
445 : 9 : datetime = tracker_sparql_cursor_get_datetime (cursor, CURSOR_MESSAGE_DATE);
446 [ + - ]: 9 : if (datetime != NULL)
447 : 9 : date = g_date_time_to_unix_usec (datetime) / 1000;
448 : :
449 : 9 : message_id = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_ID);
450 : 9 : read = tracker_sparql_cursor_get_boolean (cursor, CURSOR_MESSAGE_READ);
451 : :
452 : 9 : recipients = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_RECIPIENTS, NULL);
453 [ + + ]: 9 : if (recipients != NULL)
454 : 8 : recipientv = g_strsplit (recipients, ",", -1);
455 : :
456 [ + + ]: 9 : if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_SENDER))
457 : 6 : sender = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_SENDER, NULL);
458 : :
459 [ + - ]: 9 : if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_SUBSCRIPTION_ID))
460 : 9 : subscription_id = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_SUBSCRIPTION_ID);
461 : :
462 [ + - ]: 9 : if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_TEXT))
463 : 9 : text = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_TEXT, NULL);
464 : :
465 : 9 : thread_id = tracker_sparql_cursor_get_integer (cursor, CURSOR_MESSAGE_THREAD_ID);
466 : :
467 [ + + ]: 9 : message = g_object_new (VALENT_TYPE_MESSAGE,
468 : : "iri", iri,
469 : : "box", box,
470 : : "date", date,
471 : : "id", message_id,
472 : : "read", read,
473 : : "recipients", recipientv,
474 : : "sender", sender,
475 : : "subscription-id", subscription_id,
476 : : "text", text,
477 : : "thread-id", thread_id,
478 : : "attachments", attachments,
479 : : NULL);
480 : : }
481 : :
482 : : /* Thread
483 : : */
484 [ + - ]: 9 : if (tracker_sparql_cursor_is_bound (cursor, CURSOR_MESSAGE_THREAD_IRI))
485 : 9 : iri = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_THREAD_IRI, NULL);
486 : :
487 : 9 : participants = tracker_sparql_cursor_get_string (cursor, CURSOR_MESSAGE_THREAD_PARTICIPANTS, NULL);
488 [ + - ]: 9 : if (participants != NULL)
489 : 9 : participantv = g_strsplit (participants, ",", -1);
490 : :
491 [ + - ]: 9 : return g_object_new (VALENT_TYPE_MESSAGE_THREAD,
492 : : "connection", tracker_sparql_cursor_get_connection (cursor),
493 : : "notifier", priv->notifier,
494 : : "iri", iri,
495 : : "latest-message", message,
496 : : "participants", participantv,
497 : : NULL);
498 : : }
499 : :
500 : : static void
501 : 8 : cursor_get_threads_cb (TrackerSparqlCursor *cursor,
502 : : GAsyncResult *result,
503 : : gpointer user_data)
504 : : {
505 : 8 : g_autoptr (ValentMessagesAdapter) self = VALENT_MESSAGES_ADAPTER (g_steal_pointer (&user_data));
506 : 8 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
507 [ + - ]: 8 : g_autoptr (GError) error = NULL;
508 : :
509 [ - + ]: 8 : if (tracker_sparql_cursor_next_finish (cursor, result, &error))
510 : : {
511 : 0 : ValentMessageThread *thread = NULL;
512 : :
513 : 0 : thread = valent_message_thread_from_sparql_cursor (self, cursor);
514 [ # # ]: 0 : if (thread != NULL)
515 : : {
516 : 0 : unsigned int position;
517 : :
518 : 0 : position = priv->items->len;
519 : 0 : g_ptr_array_add (priv->items, g_steal_pointer (&thread));
520 : 0 : g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
521 : : }
522 : :
523 : 0 : tracker_sparql_cursor_next_async (cursor,
524 : : priv->cancellable,
525 : : (GAsyncReadyCallback) cursor_get_threads_cb,
526 : : g_object_ref (self));
527 : : }
528 : : else
529 : : {
530 [ - + - - ]: 8 : if (error != NULL && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
531 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
532 : :
533 : 8 : tracker_sparql_cursor_close (cursor);
534 : : }
535 : 8 : }
536 : :
537 : : static void
538 : 8 : execute_get_threads_cb (TrackerSparqlStatement *stmt,
539 : : GAsyncResult *result,
540 : : gpointer user_data)
541 : : {
542 : 16 : g_autoptr (ValentMessagesAdapter) self = VALENT_MESSAGES_ADAPTER (g_steal_pointer (&user_data));
543 [ - - + - ]: 8 : g_autoptr (TrackerSparqlCursor) cursor = NULL;
544 [ - - ]: 8 : g_autoptr (GError) error = NULL;
545 : :
546 : 8 : cursor = tracker_sparql_statement_execute_finish (stmt, result, &error);
547 [ - + ]: 8 : if (cursor == NULL)
548 : : {
549 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
550 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
551 : :
552 [ # # ]: 0 : return;
553 : : }
554 : :
555 [ - + ]: 8 : tracker_sparql_cursor_next_async (cursor,
556 : : g_task_get_cancellable (G_TASK (result)),
557 : : (GAsyncReadyCallback) cursor_get_threads_cb,
558 : : g_object_ref (self));
559 : : }
560 : :
561 : : static void
562 : 8 : valent_messages_adapter_load_threads (ValentMessagesAdapter *self)
563 : : {
564 : 8 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
565 : 8 : g_autoptr (GError) error = NULL;
566 : :
567 [ - + ]: 8 : g_assert (VALENT_IS_MESSAGES_ADAPTER (self));
568 [ + - ]: 8 : g_return_if_fail (TRACKER_IS_SPARQL_CONNECTION (priv->connection));
569 : :
570 [ + - ]: 8 : if (priv->cancellable != NULL)
571 : : return;
572 : :
573 : 8 : priv->cancellable = valent_object_ref_cancellable (VALENT_OBJECT (self));
574 [ + - ]: 8 : if (priv->get_threads_stmt == NULL)
575 : : {
576 : 8 : priv->get_threads_stmt =
577 : 8 : tracker_sparql_connection_load_statement_from_gresource (priv->connection,
578 : : GET_THREADS_RQ,
579 : : priv->cancellable,
580 : : &error);
581 : : }
582 : :
583 [ - + ]: 8 : if (priv->get_threads_stmt == NULL)
584 : : {
585 [ # # # # ]: 0 : if (error != NULL && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
586 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
587 : :
588 : 0 : return;
589 : : }
590 : :
591 [ - + ]: 8 : tracker_sparql_statement_execute_async (priv->get_threads_stmt,
592 : : priv->cancellable,
593 : : (GAsyncReadyCallback) execute_get_threads_cb,
594 : : g_object_ref (self));
595 : : }
596 : :
597 : : static void
598 : 9 : cursor_get_thread_cb (TrackerSparqlCursor *cursor,
599 : : GAsyncResult *result,
600 : : gpointer user_data)
601 : : {
602 : 9 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
603 : 9 : ValentMessagesAdapter *self = g_task_get_source_object (task);
604 [ + - ]: 9 : g_autoptr (ValentMessageThread) thread = NULL;
605 : 9 : GError *error = NULL;
606 : :
607 [ + - ]: 9 : if (tracker_sparql_cursor_next_finish (cursor, result, &error))
608 : 9 : thread = valent_message_thread_from_sparql_cursor (self, cursor);
609 : :
610 [ + - ]: 9 : if (thread != NULL)
611 : : {
612 : 9 : g_task_return_pointer (task, g_object_ref (thread), g_object_unref);
613 : : }
614 : : else
615 : : {
616 [ # # ]: 0 : if (error == NULL)
617 : : {
618 : 0 : g_set_error_literal (&error,
619 : : G_IO_ERROR,
620 : : G_IO_ERROR_NOT_FOUND,
621 : : "Failed to find thread");
622 : : }
623 : :
624 : 0 : g_task_return_error (task, g_steal_pointer (&error));
625 : : }
626 : :
627 [ + - ]: 9 : tracker_sparql_cursor_close (cursor);
628 : 9 : }
629 : :
630 : : static void
631 : 9 : execute_get_thread_cb (TrackerSparqlStatement *stmt,
632 : : GAsyncResult *result,
633 : : gpointer user_data)
634 : : {
635 : 18 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
636 : 9 : GCancellable *cancellable = g_task_get_cancellable (task);
637 [ - - + - ]: 9 : g_autoptr (TrackerSparqlCursor) cursor = NULL;
638 : 9 : GError *error = NULL;
639 : :
640 : 9 : cursor = tracker_sparql_statement_execute_finish (stmt, result, &error);
641 [ - + ]: 9 : if (cursor == NULL)
642 : : {
643 : 0 : g_task_return_error (task, g_steal_pointer (&error));
644 [ # # ]: 0 : return;
645 : : }
646 : :
647 : 9 : tracker_sparql_cursor_next_async (cursor,
648 : : cancellable,
649 : : (GAsyncReadyCallback) cursor_get_thread_cb,
650 : : g_object_ref (task));
651 : : }
652 : :
653 : : static void
654 : 9 : valent_messages_adapter_load_thread (ValentMessagesAdapter *self,
655 : : const char *iri)
656 : : {
657 : 9 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
658 : 9 : g_autoptr (GTask) task = NULL;
659 [ + - - - ]: 9 : g_autoptr (GCancellable) cancellable = NULL;
660 : 9 : GError *error = NULL;
661 : :
662 [ - + ]: 9 : g_assert (VALENT_IS_MESSAGES_ADAPTER (self));
663 [ + - ]: 9 : g_return_if_fail (TRACKER_IS_SPARQL_CONNECTION (priv->connection));
664 : :
665 : 9 : cancellable = valent_object_ref_cancellable (VALENT_OBJECT (self));
666 : 9 : task = g_task_new (self, cancellable, valent_messages_adapter_load_thread_cb, NULL);
667 [ + - ]: 9 : g_task_set_source_tag (task, valent_messages_adapter_load_thread);
668 : :
669 [ + + ]: 9 : if (priv->get_thread_stmt == NULL)
670 : : {
671 : 7 : priv->get_thread_stmt =
672 : 7 : tracker_sparql_connection_load_statement_from_gresource (priv->connection,
673 : : GET_THREAD_RQ,
674 : : cancellable,
675 : : &error);
676 : : }
677 : :
678 [ - + ]: 9 : if (priv->get_thread_stmt == NULL)
679 : : {
680 : 0 : g_task_return_error (task, g_steal_pointer (&error));
681 [ # # ]: 0 : return;
682 : : }
683 : :
684 : 9 : tracker_sparql_statement_bind_string (priv->get_thread_stmt, "iri", iri);
685 [ + - ]: 9 : tracker_sparql_statement_execute_async (priv->get_thread_stmt,
686 : : cancellable,
687 : : (GAsyncReadyCallback) execute_get_thread_cb,
688 : : g_object_ref (task));
689 : : }
690 : :
691 : : /*
692 : : * ValentMessagesAdapter
693 : : */
694 : : /* LCOV_EXCL_START */
695 : : static void
696 : : valent_messages_adapter_real_send_message (ValentMessagesAdapter *adapter,
697 : : ValentMessage *message,
698 : : GCancellable *cancellable,
699 : : GAsyncReadyCallback callback,
700 : : gpointer user_data)
701 : : {
702 : : g_assert (VALENT_IS_MESSAGES_ADAPTER (adapter));
703 : : g_assert (VALENT_IS_MESSAGE (message));
704 : : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
705 : :
706 : : g_task_report_new_error (adapter, callback, user_data,
707 : : valent_messages_adapter_real_send_message,
708 : : G_IO_ERROR,
709 : : G_IO_ERROR_NOT_SUPPORTED,
710 : : "%s does not implement send_message",
711 : : G_OBJECT_TYPE_NAME (adapter));
712 : : }
713 : :
714 : : static gboolean
715 : : valent_messages_adapter_real_send_message_finish (ValentMessagesAdapter *adapter,
716 : : GAsyncResult *result,
717 : : GError **error)
718 : : {
719 : : g_assert (VALENT_IS_MESSAGES_ADAPTER (adapter));
720 : : g_assert (g_task_is_valid (result, adapter));
721 : : g_assert (error == NULL || *error == NULL);
722 : :
723 : : return g_task_propagate_boolean (G_TASK (result), error);
724 : : }
725 : : /* LCOV_EXCL_STOP */
726 : :
727 : : /*
728 : : * ValentObject
729 : : */
730 : : static void
731 : 8 : valent_messages_adapter_destroy (ValentObject *object)
732 : : {
733 : 8 : ValentMessagesAdapter *self = VALENT_MESSAGES_ADAPTER (object);
734 : 8 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
735 : :
736 [ + + ]: 8 : g_clear_object (&priv->get_thread_stmt);
737 [ + + ]: 8 : g_clear_object (&priv->get_threads_stmt);
738 [ + + ]: 8 : g_clear_pointer (&priv->iri_pattern, g_regex_unref);
739 : :
740 [ + + ]: 8 : if (priv->notifier != NULL)
741 : : {
742 : 5 : g_signal_handlers_disconnect_by_func (priv->notifier, on_notifier_event, self);
743 [ + - ]: 5 : g_clear_object (&priv->notifier);
744 : : }
745 : :
746 [ + + ]: 8 : if (priv->connection != NULL)
747 : : {
748 : 5 : tracker_sparql_connection_close (priv->connection);
749 [ + - ]: 5 : g_clear_object (&priv->connection);
750 : : }
751 : :
752 : 8 : VALENT_OBJECT_CLASS (valent_messages_adapter_parent_class)->destroy (object);
753 : 8 : }
754 : :
755 : : /*
756 : : * GObject
757 : : */
758 : : static void
759 : 8 : valent_messages_adapter_constructed (GObject *object)
760 : : {
761 : 8 : ValentMessagesAdapter *self = VALENT_MESSAGES_ADAPTER (object);
762 : 8 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
763 : 16 : g_autoptr (GError) error = NULL;
764 : :
765 : 8 : G_OBJECT_CLASS (valent_messages_adapter_parent_class)->constructed (object);
766 : :
767 [ + - ]: 8 : if (priv->connection == NULL)
768 : : {
769 [ - + ]: 8 : if (!valent_messages_adapter_open (self, &error))
770 : 0 : g_critical ("%s(): %s", G_STRFUNC, error->message);
771 : : }
772 : :
773 [ - + ]: 8 : valent_messages_adapter_load_threads (self);
774 : 8 : }
775 : :
776 : : static void
777 : 5 : valent_messages_adapter_finalize (GObject *object)
778 : : {
779 : 5 : ValentMessagesAdapter *self = VALENT_MESSAGES_ADAPTER (object);
780 : 5 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
781 : :
782 [ + - ]: 5 : g_clear_object (&priv->cancellable);
783 [ + - ]: 5 : g_clear_pointer (&priv->items, g_ptr_array_unref);
784 : :
785 : 5 : G_OBJECT_CLASS (valent_messages_adapter_parent_class)->finalize (object);
786 : 5 : }
787 : :
788 : : static void
789 : 13 : valent_messages_adapter_get_property (GObject *object,
790 : : guint prop_id,
791 : : GValue *value,
792 : : GParamSpec *pspec)
793 : : {
794 : 13 : ValentMessagesAdapter *self = VALENT_MESSAGES_ADAPTER (object);
795 : 13 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
796 : :
797 [ + - ]: 13 : switch ((ValentMessagesAdapterProperty)prop_id)
798 : : {
799 : 13 : case PROP_CONNECTION:
800 : 13 : g_value_set_object (value, priv->connection);
801 : 13 : break;
802 : :
803 : 0 : default:
804 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
805 : : }
806 : 13 : }
807 : :
808 : : static void
809 : 8 : valent_messages_adapter_set_property (GObject *object,
810 : : guint prop_id,
811 : : const GValue *value,
812 : : GParamSpec *pspec)
813 : : {
814 : 8 : ValentMessagesAdapter *self = VALENT_MESSAGES_ADAPTER (object);
815 : 8 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
816 : :
817 [ + - ]: 8 : switch ((ValentMessagesAdapterProperty)prop_id)
818 : : {
819 : 8 : case PROP_CONNECTION:
820 [ - + ]: 8 : g_assert (priv->connection == NULL);
821 : 8 : priv->connection = g_value_dup_object (value);
822 : 8 : break;
823 : :
824 : 0 : default:
825 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
826 : : }
827 : 8 : }
828 : :
829 : : static void
830 : 53 : valent_messages_adapter_class_init (ValentMessagesAdapterClass *klass)
831 : : {
832 : 53 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
833 : 53 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
834 : :
835 : 53 : object_class->constructed = valent_messages_adapter_constructed;
836 : 53 : object_class->finalize = valent_messages_adapter_finalize;
837 : 53 : object_class->get_property = valent_messages_adapter_get_property;
838 : 53 : object_class->set_property = valent_messages_adapter_set_property;
839 : :
840 : 53 : vobject_class->destroy = valent_messages_adapter_destroy;
841 : :
842 : 53 : klass->send_message = valent_messages_adapter_real_send_message;
843 : 53 : klass->send_message_finish = valent_messages_adapter_real_send_message_finish;
844 : :
845 : : /**
846 : : * ValentMessagesAdapter:connection:
847 : : *
848 : : * The database connection.
849 : : */
850 : 106 : properties [PROP_CONNECTION] =
851 : 53 : g_param_spec_object ("connection", NULL, NULL,
852 : : TRACKER_TYPE_SPARQL_CONNECTION,
853 : : (G_PARAM_READWRITE |
854 : : G_PARAM_CONSTRUCT_ONLY |
855 : : G_PARAM_EXPLICIT_NOTIFY |
856 : : G_PARAM_STATIC_STRINGS));
857 : :
858 : 53 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
859 : 53 : }
860 : :
861 : : static void
862 : 8 : valent_messages_adapter_init (ValentMessagesAdapter *self)
863 : : {
864 : 8 : ValentMessagesAdapterPrivate *priv = valent_messages_adapter_get_instance_private (self);
865 : :
866 : 8 : priv->items = g_ptr_array_new_with_free_func (g_object_unref);
867 : 8 : }
868 : :
869 : : /**
870 : : * valent_messages_adapter_send_message: (virtual send_message)
871 : : * @adapter: a `ValentMessagesAdapter`
872 : : * @message: the message to send
873 : : * @cancellable: (nullable): a `GCancellable`
874 : : * @callback: (scope async): a `GAsyncReadyCallback`
875 : : * @user_data: user supplied data
876 : : *
877 : : * Send @message via @adapter.
878 : : *
879 : : * Call [method@Valent.MessagesAdapter.send_message_finish] to get the result.
880 : : *
881 : : * Since: 1.0
882 : : */
883 : : void
884 : 0 : valent_messages_adapter_send_message (ValentMessagesAdapter *adapter,
885 : : ValentMessage *message,
886 : : GCancellable *cancellable,
887 : : GAsyncReadyCallback callback,
888 : : gpointer user_data)
889 : : {
890 : 0 : VALENT_ENTRY;
891 : :
892 [ # # ]: 0 : g_return_if_fail (VALENT_IS_MESSAGES_ADAPTER (adapter));
893 [ # # ]: 0 : g_return_if_fail (VALENT_IS_MESSAGE (message));
894 [ # # # # : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
# # # # ]
895 : :
896 : 0 : VALENT_MESSAGES_ADAPTER_GET_CLASS (adapter)->send_message (adapter,
897 : : message,
898 : : cancellable,
899 : : callback,
900 : : user_data);
901 : :
902 : 0 : VALENT_EXIT;
903 : : }
904 : :
905 : : /**
906 : : * valent_messages_adapter_send_message_finish: (virtual send_message_finish)
907 : : * @adapter: a `ValentMessagesAdapter`
908 : : * @result: a `GAsyncResult`
909 : : * @error: (nullable): a `GError`
910 : : *
911 : : * Finish an operation started by [method@Valent.MessagesAdapter.send_message].
912 : : *
913 : : * Returns: %TRUE if successful, or %FALSE with @error set
914 : : *
915 : : * Since: 1.0
916 : : */
917 : : gboolean
918 : 0 : valent_messages_adapter_send_message_finish (ValentMessagesAdapter *adapter,
919 : : GAsyncResult *result,
920 : : GError **error)
921 : : {
922 : 0 : gboolean ret;
923 : :
924 : 0 : VALENT_ENTRY;
925 : :
926 [ # # ]: 0 : g_return_val_if_fail (VALENT_IS_MESSAGES_ADAPTER (adapter), FALSE);
927 [ # # ]: 0 : g_return_val_if_fail (g_task_is_valid (result, adapter), FALSE);
928 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
929 : :
930 : 0 : ret = VALENT_MESSAGES_ADAPTER_GET_CLASS (adapter)->send_message_finish (adapter,
931 : : result,
932 : : error);
933 : :
934 : 0 : VALENT_RETURN (ret);
935 : : }
936 : :
|