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