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