+
+ 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-sms-store"
+ 5 : :
+ 6 : : #include "config.h"
+ 7 : :
+ 8 : : #include <gio/gio.h>
+ 9 : : #include <valent.h>
+ 10 : : #include <sqlite3.h>
+ 11 : :
+ 12 : : #include "valent-message.h"
+ 13 : : #include "valent-message-thread.h"
+ 14 : : #include "valent-sms-store.h"
+ 15 : : #include "valent-sms-store-private.h"
+ 16 : :
+ 17 : : /* Ensure that sqlite3_int64 is the same size as int64_t */
+ 18 : : G_STATIC_ASSERT (sizeof (sqlite3_int64) == sizeof (int64_t));
+ 19 : :
+ 20 : : struct _ValentSmsStore
+ 21 : : {
+ 22 : : ValentContext parent_instance;
+ 23 : :
+ 24 : : GAsyncQueue *queue;
+ 25 : : sqlite3 *connection;
+ 26 : : char *path;
+ 27 : : sqlite3_stmt *stmts[9];
+ 28 : :
+ 29 : : GListStore *summary;
+ 30 : : };
+ 31 : :
+ 32 [ + + + - ]: 152 : G_DEFINE_FINAL_TYPE (ValentSmsStore, valent_sms_store, VALENT_TYPE_CONTEXT)
+ 33 : :
+ 34 : : enum {
+ 35 : : MESSAGE_ADDED,
+ 36 : : MESSAGE_CHANGED,
+ 37 : : MESSAGE_REMOVED,
+ 38 : : N_SIGNALS
+ 39 : : };
+ 40 : :
+ 41 : : static guint signals[N_SIGNALS] = { 0, };
+ 42 : :
+ 43 : : enum {
+ 44 : : STMT_ADD_MESSAGE,
+ 45 : : STMT_REMOVE_MESSAGE,
+ 46 : : STMT_REMOVE_THREAD,
+ 47 : : STMT_GET_MESSAGE,
+ 48 : : STMT_GET_THREAD,
+ 49 : : STMT_GET_THREAD_DATE,
+ 50 : : STMT_GET_THREAD_ITEMS,
+ 51 : : STMT_FIND_MESSAGES,
+ 52 : : STMT_GET_SUMMARY,
+ 53 : : N_STATEMENTS,
+ 54 : : };
+ 55 : :
+ 56 : : static char *statements[N_STATEMENTS] = { NULL, };
+ 57 : :
+ 58 : :
+ 59 : : /*
+ 60 : : * Signal Emission Helpers
+ 61 : : */
+ 62 : : typedef struct
+ 63 : : {
+ 64 : : GRecMutex mutex;
+ 65 : : GWeakRef store;
+ 66 : : ValentMessage *message;
+ 67 : : guint signal_id;
+ 68 : : } ChangeEmission;
+ 69 : :
+ 70 : : static gboolean
+ 71 : 17 : emit_change_main (gpointer data)
+ 72 : : {
+ 73 : 17 : ChangeEmission *emission = data;
+ 74 : 34 : g_autoptr (ValentSmsStore) store = NULL;
+ 75 : :
+ 76 [ + - ]: 17 : g_assert (emission != NULL);
+ 77 : :
+ 78 : 17 : g_rec_mutex_lock (&emission->mutex);
+ 79 [ + - ]: 17 : if ((store = g_weak_ref_get (&emission->store)) != NULL)
+ 80 : : {
+ 81 : 17 : g_signal_emit (G_OBJECT (store),
+ 82 : : emission->signal_id, 0,
+ 83 : : emission->message);
+ 84 : : }
+ 85 : :
+ 86 : 17 : g_weak_ref_clear (&emission->store);
+ 87 [ + - ]: 17 : g_clear_object (&emission->message);
+ 88 : 17 : g_rec_mutex_unlock (&emission->mutex);
+ 89 : 17 : g_rec_mutex_clear (&emission->mutex);
+ 90 : 17 : g_clear_pointer (&emission, g_free);
+ 91 : :
+ 92 [ + - ]: 17 : return G_SOURCE_REMOVE;
+ 93 : : }
+ 94 : :
+ 95 : :
+ 96 : : /*
+ 97 : : * sqlite Threading Helpers
+ 98 : : */
+ 99 : : enum {
+ 100 : : TASK_DEFAULT,
+ 101 : : TASK_CRITICAL,
+ 102 : : TASK_TERMINAL,
+ 103 : : };
+ 104 : :
+ 105 : : typedef struct
+ 106 : : {
+ 107 : : GTask *task;
+ 108 : : GTaskThreadFunc task_func;
+ 109 : : unsigned int task_mode;
+ 110 : : } TaskClosure;
+ 111 : :
+ 112 : : static void
+ 113 : 37 : task_closure_free (gpointer data)
+ 114 : : {
+ 115 : 74 : g_autofree TaskClosure *closure = data;
+ 116 : :
+ 117 [ + - ]: 37 : g_clear_object (&closure->task);
+ 118 : 37 : g_clear_pointer (&closure, g_free);
+ 119 : 37 : }
+ 120 : :
+ 121 : : static void
+ 122 : 0 : task_closure_cancel (gpointer data)
+ 123 : : {
+ 124 : 0 : g_autofree TaskClosure *closure = data;
+ 125 : :
+ 126 [ # # # # : 0 : if (G_IS_TASK (closure->task) && !g_task_get_completed (closure->task))
+ # # # # #
+ # ]
+ 127 : : {
+ 128 : 0 : g_task_return_new_error (closure->task,
+ 129 : : G_IO_ERROR,
+ 130 : : G_IO_ERROR_CANCELLED,
+ 131 : : "Operation cancelled");
+ 132 : : }
+ 133 : :
+ 134 : 0 : g_clear_pointer (&closure, task_closure_free);
+ 135 : 0 : }
+ 136 : :
+ 137 : : static gpointer
+ 138 : 7 : valent_sms_store_thread (gpointer data)
+ 139 : : {
+ 140 : 10 : g_autoptr (GAsyncQueue) tasks = data;
+ 141 : 7 : TaskClosure *closure = NULL;
+ 142 : :
+ 143 [ + - ]: 41 : while ((closure = g_async_queue_pop (tasks)))
+ 144 : : {
+ 145 : 41 : unsigned int mode = closure->task_mode;
+ 146 : :
+ 147 [ - + + - : 41 : if (G_IS_TASK (closure->task) && !g_task_get_completed (closure->task))
+ - + - - -
+ + ]
+ 148 : : {
+ 149 : 41 : closure->task_func (closure->task,
+ 150 : : g_task_get_source_object (closure->task),
+ 151 : : g_task_get_task_data (closure->task),
+ 152 : : g_task_get_cancellable (closure->task));
+ 153 : :
+ 154 [ + - - - ]: 37 : if (mode == TASK_CRITICAL && g_task_had_error (closure->task))
+ 155 : : mode = TASK_TERMINAL;
+ 156 : : }
+ 157 : :
+ 158 : 37 : g_clear_pointer (&closure, task_closure_free);
+ 159 : :
+ 160 [ + + ]: 37 : if (mode == TASK_TERMINAL)
+ 161 : : break;
+ 162 : : }
+ 163 : :
+ 164 : : /* Cancel any queued tasks */
+ 165 : 3 : g_async_queue_lock (tasks);
+ 166 : :
+ 167 [ - + ]: 6 : while ((closure = g_async_queue_try_pop_unlocked (tasks)) != NULL)
+ 168 : 3 : g_clear_pointer (&closure, task_closure_cancel);
+ 169 : :
+ 170 : 3 : g_async_queue_unlock (tasks);
+ 171 : :
+ 172 [ + - ]: 3 : return NULL;
+ 173 : : }
+ 174 : :
+ 175 : : /*
+ 176 : : * Step functions
+ 177 : : */
+ 178 : : static inline ValentMessage *
+ 179 : 30 : valent_sms_store_get_message_step (sqlite3_stmt *stmt,
+ 180 : : GError **error)
+ 181 : : {
+ 182 : 60 : g_autoptr (GVariant) metadata = NULL;
+ 183 : 30 : const char *metadata_str;
+ 184 : 30 : int rc;
+ 185 : :
+ 186 [ + - ]: 30 : g_assert (stmt != NULL);
+ 187 [ + - - + ]: 30 : g_assert (error == NULL || *error == NULL);
+ 188 : :
+ 189 [ + + ]: 30 : if ((rc = sqlite3_step (stmt)) == SQLITE_DONE)
+ 190 : : return NULL;
+ 191 : :
+ 192 [ - + ]: 25 : if (rc != SQLITE_ROW)
+ 193 : : {
+ 194 : 0 : g_set_error (error,
+ 195 : : G_IO_ERROR,
+ 196 : : G_IO_ERROR_FAILED,
+ 197 : : "%s: %s", G_STRFUNC, sqlite3_errstr (rc));
+ 198 : 0 : return NULL;
+ 199 : : }
+ 200 : :
+ 201 [ + - ]: 25 : if ((metadata_str = (const char *)sqlite3_column_text (stmt, 3)) != NULL)
+ 202 : 25 : metadata = g_variant_parse (NULL, metadata_str, NULL, NULL, NULL);
+ 203 : :
+ 204 : 25 : return g_object_new (VALENT_TYPE_MESSAGE,
+ 205 : : "box", sqlite3_column_int (stmt, 0),
+ 206 : : "date", sqlite3_column_int64 (stmt, 1),
+ 207 : : "id", sqlite3_column_int64 (stmt, 2),
+ 208 : : "metadata", metadata,
+ 209 : : "read", sqlite3_column_int (stmt, 4),
+ 210 : : "sender", sqlite3_column_text (stmt, 5),
+ 211 : : "text", sqlite3_column_text (stmt, 6),
+ 212 : : "thread_id", sqlite3_column_int64 (stmt, 7),
+ 213 : : NULL);
+ 214 : : }
+ 215 : :
+ 216 : : static inline gboolean
+ 217 : 15 : valent_sms_store_set_message_step (sqlite3_stmt *stmt,
+ 218 : : ValentMessage *message,
+ 219 : : GError **error)
+ 220 : : {
+ 221 : 15 : int rc;
+ 222 : 15 : ValentMessageBox box;
+ 223 : 15 : int64_t date;
+ 224 : 15 : int64_t id;
+ 225 : 15 : GVariant *metadata;
+ 226 : 15 : gboolean read;
+ 227 : 15 : const char *sender;
+ 228 : 15 : const char *text;
+ 229 : 15 : int64_t thread_id;
+ 230 : 30 : g_autofree char *metadata_str = NULL;
+ 231 : :
+ 232 : : /* Extract the message data */
+ 233 : 15 : box = valent_message_get_box (message);
+ 234 : 15 : date = valent_message_get_date (message);
+ 235 : 15 : id = valent_message_get_id (message);
+ 236 : 15 : metadata = valent_message_get_metadata (message);
+ 237 : 15 : read = valent_message_get_read (message);
+ 238 : 15 : sender = valent_message_get_sender (message);
+ 239 : 15 : text = valent_message_get_text (message);
+ 240 : 15 : thread_id = valent_message_get_thread_id (message);
+ 241 : :
+ 242 [ + - ]: 15 : if (metadata != NULL)
+ 243 : 15 : metadata_str = g_variant_print (metadata, TRUE);
+ 244 : :
+ 245 : : /* Bind the message data */
+ 246 : 15 : sqlite3_bind_int (stmt, 1, box);
+ 247 : 15 : sqlite3_bind_int64 (stmt, 2, date);
+ 248 : 15 : sqlite3_bind_int64 (stmt, 3, id);
+ 249 : 15 : sqlite3_bind_text (stmt, 4, metadata_str, -1, NULL);
+ 250 : 15 : sqlite3_bind_int (stmt, 5, read);
+ 251 : 15 : sqlite3_bind_text (stmt, 6, sender, -1, NULL);
+ 252 : 15 : sqlite3_bind_text (stmt, 7, text, -1, NULL);
+ 253 : 15 : sqlite3_bind_int64 (stmt, 8, thread_id);
+ 254 : :
+ 255 : : /* Execute and auto-reset */
+ 256 [ + + ]: 15 : if ((rc = sqlite3_step (stmt)) != SQLITE_DONE)
+ 257 : : {
+ 258 : 2 : g_set_error (error,
+ 259 : : G_IO_ERROR,
+ 260 : : G_IO_ERROR_FAILED,
+ 261 : : "%s: %s", G_STRFUNC, sqlite3_errstr (rc));
+ 262 : 2 : sqlite3_reset (stmt);
+ 263 : 2 : return FALSE;
+ 264 : : }
+ 265 : :
+ 266 : 13 : sqlite3_reset (stmt);
+ 267 : 13 : return TRUE;
+ 268 : : }
+ 269 : :
+ 270 : : static gboolean
+ 271 : 27 : valent_sms_store_return_error_if_closed (GTask *task,
+ 272 : : ValentSmsStore *self)
+ 273 : : {
+ 274 [ + - + - : 27 : g_assert (G_IS_TASK (task));
+ - + - - ]
+ 275 [ - + ]: 27 : g_assert (VALENT_IS_SMS_STORE (self));
+ 276 : :
+ 277 [ - + ]: 27 : if G_UNLIKELY (self->connection == NULL)
+ 278 : : {
+ 279 : 0 : g_task_return_new_error (task,
+ 280 : : G_IO_ERROR,
+ 281 : : G_IO_ERROR_CONNECTION_CLOSED,
+ 282 : : "Database connection closed");
+ 283 : 0 : return TRUE;
+ 284 : : }
+ 285 : :
+ 286 : : return FALSE;
+ 287 : : }
+ 288 : :
+ 289 : :
+ 290 : : /*
+ 291 : : * Database Hooks
+ 292 : : */
+ 293 : : static void
+ 294 : 17 : update_hook (gpointer user_data,
+ 295 : : int event,
+ 296 : : char const *database,
+ 297 : : char const *table,
+ 298 : : sqlite3_int64 rowid)
+ 299 : : {
+ 300 : 17 : ValentSmsStore *self = VALENT_SMS_STORE (user_data);
+ 301 : 17 : sqlite3_stmt *stmt = self->stmts[STMT_GET_MESSAGE];
+ 302 : 17 : g_autoptr (ValentMessage) message = NULL;
+ 303 [ + - - - ]: 17 : g_autoptr (GError) error = NULL;
+ 304 : :
+ 305 [ + - ]: 17 : g_assert (VALENT_IS_SMS_STORE (self));
+ 306 [ - + ]: 17 : g_assert (!VALENT_IS_MAIN_THREAD ());
+ 307 : :
+ 308 [ + - ]: 17 : if G_UNLIKELY (g_strcmp0 (table, "message") != 0)
+ 309 : : return;
+ 310 : :
+ 311 [ + + ]: 17 : if (event != SQLITE_DELETE)
+ 312 : : {
+ 313 : 14 : sqlite3_bind_int64 (stmt, 1, rowid);
+ 314 : 14 : message = valent_sms_store_get_message_step (stmt, &error);
+ 315 : 14 : sqlite3_reset (stmt);
+ 316 : : }
+ 317 : :
+ 318 [ + - ]: 17 : if G_UNLIKELY (error != NULL)
+ 319 : : {
+ 320 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
+ 321 : 0 : return;
+ 322 : : }
+ 323 : :
+ 324 : : /* Fallback to using a message skeleton */
+ 325 [ + + ]: 17 : if (message == NULL)
+ 326 : : {
+ 327 : 4 : message = g_object_new (VALENT_TYPE_MESSAGE,
+ 328 : : "id", rowid,
+ 329 : : NULL);
+ 330 : : }
+ 331 : :
+ 332 [ + + + - ]: 17 : switch (event)
+ 333 : : {
+ 334 : 13 : case SQLITE_INSERT:
+ 335 : 13 : valent_sms_store_message_added (self, message);
+ 336 : 13 : break;
+ 337 : :
+ 338 : 1 : case SQLITE_UPDATE:
+ 339 : 1 : valent_sms_store_message_changed (self, message);
+ 340 : 1 : break;
+ 341 : :
+ 342 : 3 : case SQLITE_DELETE:
+ 343 : 3 : valent_sms_store_message_removed (self, message);
+ 344 : 3 : break;
+ 345 : : }
+ 346 : : }
+ 347 : :
+ 348 : :
+ 349 : : /*
+ 350 : : * ValentSmsStore Tasks
+ 351 : : */
+ 352 : : static void
+ 353 : 7 : valent_sms_store_open_task (GTask *task,
+ 354 : : gpointer source_object,
+ 355 : : gpointer task_data,
+ 356 : : GCancellable *cancellable)
+ 357 : : {
+ 358 : 7 : ValentSmsStore *self = VALENT_SMS_STORE (source_object);
+ 359 : 7 : const char *path = task_data;
+ 360 : 7 : int rc;
+ 361 : :
+ 362 [ + - ]: 7 : if (g_task_return_error_if_cancelled (task))
+ 363 : : return;
+ 364 : :
+ 365 [ - + ]: 7 : if (self->connection != NULL)
+ 366 : 0 : return g_task_return_boolean (task, TRUE);
+ 367 : :
+ 368 : : /* Pass NOMUTEX since concurrency is managed by the GMutex*/
+ 369 : 7 : rc = sqlite3_open_v2 (path,
+ 370 : : &self->connection,
+ 371 : : (SQLITE_OPEN_READWRITE |
+ 372 : : SQLITE_OPEN_CREATE |
+ 373 : : SQLITE_OPEN_NOMUTEX),
+ 374 : : NULL);
+ 375 : :
+ 376 [ - + ]: 7 : if (rc != SQLITE_OK)
+ 377 : : {
+ 378 : 0 : g_task_return_new_error (task,
+ 379 : : G_IO_ERROR,
+ 380 : : G_IO_ERROR_FAILED,
+ 381 : : "sqlite3_open_v2(): \"%s\": [%i] %s",
+ 382 : : path, rc, sqlite3_errstr (rc));
+ 383 [ # # ]: 0 : g_clear_pointer (&self->connection, sqlite3_close);
+ 384 : 0 : return;
+ 385 : : }
+ 386 : :
+ 387 : : /* Prepare the tables */
+ 388 : 7 : rc = sqlite3_exec (self->connection,
+ 389 : : MESSAGE_TABLE_SQL,
+ 390 : : NULL,
+ 391 : : NULL,
+ 392 : : NULL);
+ 393 : :
+ 394 [ - + ]: 7 : if (rc != SQLITE_OK)
+ 395 : : {
+ 396 : 0 : g_task_return_new_error (task,
+ 397 : : G_IO_ERROR,
+ 398 : : G_IO_ERROR_FAILED,
+ 399 : : "sqlite3_prepare_v2(): [%i] \"message\" Table: %s",
+ 400 : : rc, sqlite3_errstr (rc));
+ 401 [ # # ]: 0 : g_clear_pointer (&self->connection, sqlite3_close);
+ 402 : 0 : return;
+ 403 : : }
+ 404 : :
+ 405 : : /* Prepare the statements */
+ 406 [ + + ]: 70 : for (unsigned int i = 0; i < N_STATEMENTS; i++)
+ 407 : : {
+ 408 : 63 : sqlite3_stmt *stmt = NULL;
+ 409 : 63 : const char *sql = statements[i];
+ 410 : :
+ 411 : 63 : rc = sqlite3_prepare_v2 (self->connection, sql, -1, &stmt, NULL);
+ 412 : :
+ 413 [ - + ]: 63 : if (rc != SQLITE_OK)
+ 414 : : {
+ 415 : 0 : g_task_return_new_error (task,
+ 416 : : G_IO_ERROR,
+ 417 : : G_IO_ERROR_FAILED,
+ 418 : : "sqlite3_prepare_v2(): \"%s\": [%i] %s",
+ 419 : : sql, rc, sqlite3_errstr (rc));
+ 420 [ # # ]: 0 : g_clear_pointer (&self->connection, sqlite3_close);
+ 421 : 0 : return;
+ 422 : : }
+ 423 : :
+ 424 : 63 : self->stmts[i] = g_steal_pointer (&stmt);
+ 425 : : }
+ 426 : :
+ 427 : : /* Connect the hooks */
+ 428 : 7 : sqlite3_update_hook (self->connection, update_hook, self);
+ 429 : :
+ 430 : 7 : g_task_return_boolean (task, TRUE);
+ 431 : : }
+ 432 : :
+ 433 : : static void
+ 434 : 7 : valent_sms_store_close_task (GTask *task,
+ 435 : : gpointer source_object,
+ 436 : : gpointer task_data,
+ 437 : : GCancellable *cancellable)
+ 438 : : {
+ 439 : 7 : ValentSmsStore *self = VALENT_SMS_STORE (source_object);
+ 440 : 7 : int rc;
+ 441 : :
+ 442 [ + - ]: 7 : if (g_task_return_error_if_cancelled (task))
+ 443 : : return;
+ 444 : :
+ 445 [ - + ]: 7 : if (self->connection == NULL)
+ 446 : 0 : return g_task_return_boolean (task, TRUE);
+ 447 : :
+ 448 : : /* Cleanup cached statements */
+ 449 [ + + ]: 70 : for (unsigned int i = 0; i < N_STATEMENTS; i++)
+ 450 [ + - ]: 63 : g_clear_pointer (&self->stmts[i], sqlite3_finalize);
+ 451 : :
+ 452 : : /* Optimize the database before closing.
+ 453 : : *
+ 454 : : * See:
+ 455 : : * https://www.sqlite.org/pragma.html#pragma_optimize
+ 456 : : * https://www.sqlite.org/queryplanner-ng.html#update_2017_a_better_fix
+ 457 : : */
+ 458 : 7 : rc = sqlite3_exec (self->connection, "PRAGMA optimize;", NULL, NULL, NULL);
+ 459 : :
+ 460 [ + + ]: 4 : if (rc != SQLITE_OK)
+ 461 : : {
+ 462 : 3 : g_debug ("sqlite3_exec(): \"%s\": [%i] %s",
+ 463 : : "PRAGMA optimize;", rc, sqlite3_errstr (rc));
+ 464 : : }
+ 465 : :
+ 466 : : /* Close the connection */
+ 467 [ - + ]: 4 : if ((rc = sqlite3_close (self->connection)) != SQLITE_OK)
+ 468 : : {
+ 469 : 0 : g_task_return_new_error (task,
+ 470 : : G_IO_ERROR,
+ 471 : : G_IO_ERROR_FAILED,
+ 472 : : "sqlite3_close(): [%i] %s",
+ 473 : : rc, sqlite3_errstr (rc));
+ 474 : 0 : return;
+ 475 : : }
+ 476 : :
+ 477 : 3 : self->connection = NULL;
+ 478 : 3 : g_task_return_boolean (task, TRUE);
+ 479 : : }
+ 480 : :
+ 481 : : static void
+ 482 : 7 : add_messages_task (GTask *task,
+ 483 : : gpointer source_object,
+ 484 : : gpointer task_data,
+ 485 : : GCancellable *cancellable)
+ 486 : : {
+ 487 : 7 : ValentSmsStore *self = VALENT_SMS_STORE (source_object);
+ 488 : 7 : GPtrArray *messages = task_data;
+ 489 : 7 : sqlite3_stmt *stmt = self->stmts[STMT_ADD_MESSAGE];
+ 490 : 7 : unsigned int n_messages = messages->len;
+ 491 : 7 : GError *error = NULL;
+ 492 : :
+ 493 [ + - ]: 7 : if (g_task_return_error_if_cancelled (task))
+ 494 : 2 : return;
+ 495 : :
+ 496 [ + - ]: 7 : if (valent_sms_store_return_error_if_closed (task, self))
+ 497 : : return;
+ 498 : :
+ 499 [ + + ]: 20 : for (unsigned int i = 0; i < n_messages; i++)
+ 500 : : {
+ 501 : 15 : ValentMessage *message = g_ptr_array_index (messages, i);
+ 502 : :
+ 503 : : /* Iterate the results stopping on error to mark the point of failure */
+ 504 [ + + ]: 15 : if (!valent_sms_store_set_message_step (stmt, message, &error))
+ 505 : : {
+ 506 : : n_messages = i;
+ 507 : : break;
+ 508 : : }
+ 509 : : }
+ 510 : :
+ 511 : : /* Truncate the input on failure, since we'll be emitting signals */
+ 512 [ + + ]: 7 : if (n_messages < messages->len)
+ 513 : 2 : g_ptr_array_remove_range (messages, n_messages, messages->len - n_messages);
+ 514 : :
+ 515 [ + + ]: 7 : if (error != NULL)
+ 516 : 2 : return g_task_return_error (task, error);
+ 517 : :
+ 518 : 5 : g_task_return_boolean (task, TRUE);
+ 519 : : }
+ 520 : :
+ 521 : : static void
+ 522 : 1 : remove_message_task (GTask *task,
+ 523 : : gpointer source_object,
+ 524 : : gpointer task_data,
+ 525 : : GCancellable *cancellable)
+ 526 : : {
+ 527 : 1 : ValentSmsStore *self = VALENT_SMS_STORE (source_object);
+ 528 : 1 : int64_t *message_id = task_data;
+ 529 : 1 : sqlite3_stmt *stmt = self->stmts[STMT_REMOVE_MESSAGE];
+ 530 : 1 : int rc;
+ 531 : :
+ 532 [ + - ]: 1 : if (g_task_return_error_if_cancelled (task))
+ 533 : : return;
+ 534 : :
+ 535 [ + - ]: 1 : if (valent_sms_store_return_error_if_closed (task, self))
+ 536 : : return;
+ 537 : :
+ 538 : 1 : sqlite3_bind_int64 (stmt, 1, *message_id);
+ 539 : 1 : rc = sqlite3_step (stmt);
+ 540 : 1 : sqlite3_reset (stmt);
+ 541 : :
+ 542 [ + - ]: 1 : if (rc == SQLITE_DONE || rc == SQLITE_OK)
+ 543 : 1 : return g_task_return_boolean (task, TRUE);
+ 544 : :
+ 545 : 0 : return g_task_return_new_error (task,
+ 546 : : G_IO_ERROR,
+ 547 : : G_IO_ERROR_FAILED,
+ 548 : : "%s: %s",
+ 549 : : G_STRFUNC, sqlite3_errstr (rc));
+ 550 : : }
+ 551 : :
+ 552 : : static void
+ 553 : 1 : remove_thread_task (GTask *task,
+ 554 : : gpointer source_object,
+ 555 : : gpointer task_data,
+ 556 : : GCancellable *cancellable)
+ 557 : : {
+ 558 : 1 : ValentSmsStore *self = VALENT_SMS_STORE (source_object);
+ 559 : 1 : int64_t *thread_id = task_data;
+ 560 : 1 : sqlite3_stmt *stmt = self->stmts[STMT_REMOVE_THREAD];
+ 561 : 1 : int rc;
+ 562 : :
+ 563 [ + - ]: 1 : if (g_task_return_error_if_cancelled (task))
+ 564 : : return;
+ 565 : :
+ 566 [ + - ]: 1 : if (valent_sms_store_return_error_if_closed (task, self))
+ 567 : : return;
+ 568 : :
+ 569 : 1 : sqlite3_bind_int64 (stmt, 1, *thread_id);
+ 570 : 1 : rc = sqlite3_step (stmt);
+ 571 : 1 : sqlite3_reset (stmt);
+ 572 : :
+ 573 [ + - ]: 1 : if (rc == SQLITE_DONE || rc == SQLITE_OK)
+ 574 : 1 : return g_task_return_boolean (task, TRUE);
+ 575 : :
+ 576 : 0 : return g_task_return_new_error (task,
+ 577 : : G_IO_ERROR,
+ 578 : : G_IO_ERROR_FAILED,
+ 579 : : "%s: %s",
+ 580 : : G_STRFUNC, sqlite3_errstr (rc));
+ 581 : : }
+ 582 : :
+ 583 : : static void
+ 584 : 1 : find_messages_task (GTask *task,
+ 585 : : gpointer source_object,
+ 586 : : gpointer task_data,
+ 587 : : GCancellable *cancellable)
+ 588 : : {
+ 589 : 1 : ValentSmsStore *self = VALENT_SMS_STORE (source_object);
+ 590 : 1 : const char *query = task_data;
+ 591 : 1 : sqlite3_stmt *stmt = self->stmts[STMT_FIND_MESSAGES];
+ 592 : 1 : g_autoptr (GPtrArray) messages = NULL;
+ 593 [ - - ]: 1 : g_autofree char *query_param = NULL;
+ 594 : 1 : ValentMessage *message;
+ 595 : 1 : GError *error = NULL;
+ 596 : :
+ 597 [ + - ]: 1 : if (g_task_return_error_if_cancelled (task))
+ 598 : : return;
+ 599 : :
+ 600 [ + - ]: 1 : if (valent_sms_store_return_error_if_closed (task, self))
+ 601 : : return;
+ 602 : :
+ 603 : : // NOTE: escaped percent signs (%%) are query wildcards (%)
+ 604 : 1 : query_param = g_strdup_printf ("%%%s%%", query);
+ 605 : 1 : sqlite3_bind_text (stmt, 1, query_param, -1, NULL);
+ 606 : :
+ 607 : : /* Collect the results */
+ 608 : 1 : messages = g_ptr_array_new_with_free_func (g_object_unref);
+ 609 : :
+ 610 [ + + ]: 3 : while ((message = valent_sms_store_get_message_step (stmt, &error)))
+ 611 : 2 : g_ptr_array_add (messages, message);
+ 612 : 1 : sqlite3_reset (stmt);
+ 613 : :
+ 614 [ - + ]: 1 : if (error != NULL)
+ 615 : 0 : return g_task_return_error (task, error);
+ 616 : :
+ 617 : 1 : g_task_return_pointer (task,
+ 618 : : g_steal_pointer (&messages),
+ 619 : : (GDestroyNotify)g_ptr_array_unref);
+ 620 : : }
+ 621 : :
+ 622 : : static void
+ 623 : 6 : get_message_task (GTask *task,
+ 624 : : gpointer source_object,
+ 625 : : gpointer task_data,
+ 626 : : GCancellable *cancellable)
+ 627 : : {
+ 628 : 6 : ValentSmsStore *self = VALENT_SMS_STORE (source_object);
+ 629 : 6 : int64_t *message_id = task_data;
+ 630 : 6 : sqlite3_stmt *stmt = self->stmts[STMT_GET_MESSAGE];
+ 631 : 6 : g_autoptr (ValentMessage) message = NULL;
+ 632 : 6 : GError *error = NULL;
+ 633 : :
+ 634 [ + - ]: 6 : if (g_task_return_error_if_cancelled (task))
+ 635 : : return;
+ 636 : :
+ 637 [ + - ]: 6 : if (valent_sms_store_return_error_if_closed (task, self))
+ 638 : : return;
+ 639 : :
+ 640 : 6 : sqlite3_bind_int64 (stmt, 1, *message_id);
+ 641 : 6 : message = valent_sms_store_get_message_step (stmt, &error);
+ 642 : 6 : sqlite3_reset (stmt);
+ 643 : :
+ 644 [ - + ]: 6 : if (error != NULL)
+ 645 : 0 : return g_task_return_error (task, error);
+ 646 : :
+ 647 : 6 : g_task_return_pointer (task, g_steal_pointer (&message), g_object_unref);
+ 648 : : }
+ 649 : :
+ 650 : : static void
+ 651 : 3 : get_summary_cb (ValentSmsStore *self,
+ 652 : : GAsyncResult *result,
+ 653 : : gpointer user_data)
+ 654 : : {
+ 655 : 3 : g_autoptr (GPtrArray) messages = NULL;
+ 656 : 3 : g_autoptr (GError) error = NULL;
+ 657 : :
+ 658 [ - + ]: 3 : if ((messages = g_task_propagate_pointer (G_TASK (result), &error)) == NULL)
+ 659 : : {
+ 660 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
+ 661 [ # # ]: 0 : return;
+ 662 : : }
+ 663 : :
+ 664 [ - + ]: 3 : g_list_store_splice (self->summary, 0, 0, messages->pdata, messages->len);
+ 665 : : }
+ 666 : :
+ 667 : : static void
+ 668 : 3 : get_summary_task (GTask *task,
+ 669 : : gpointer source_object,
+ 670 : : gpointer task_data,
+ 671 : : GCancellable *cancellable)
+ 672 : : {
+ 673 : 3 : ValentSmsStore *self = VALENT_SMS_STORE (source_object);
+ 674 : 3 : sqlite3_stmt *stmt = self->stmts[STMT_GET_SUMMARY];
+ 675 : 3 : g_autoptr (GPtrArray) messages = NULL;
+ 676 : 3 : ValentMessage *message;
+ 677 : 3 : GError *error = NULL;
+ 678 : :
+ 679 [ + - ]: 3 : if (g_task_return_error_if_cancelled (task))
+ 680 : : return;
+ 681 : :
+ 682 [ + - ]: 3 : if (valent_sms_store_return_error_if_closed (task, self))
+ 683 : : return;
+ 684 : :
+ 685 : : /* Collect the results */
+ 686 : 3 : messages = g_ptr_array_new_with_free_func (g_object_unref);
+ 687 : :
+ 688 [ + + ]: 7 : while ((message = valent_sms_store_get_message_step (stmt, &error)))
+ 689 : 4 : g_ptr_array_add (messages, message);
+ 690 : 3 : sqlite3_reset (stmt);
+ 691 : :
+ 692 [ - + ]: 3 : if (error != NULL)
+ 693 : 0 : return g_task_return_error (task, error);
+ 694 : :
+ 695 : 3 : g_task_return_pointer (task,
+ 696 : : g_steal_pointer (&messages),
+ 697 : : (GDestroyNotify)g_ptr_array_unref);
+ 698 : : }
+ 699 : :
+ 700 : : static void
+ 701 : 4 : get_thread_date_task (GTask *task,
+ 702 : : gpointer source_object,
+ 703 : : gpointer task_data,
+ 704 : : GCancellable *cancellable)
+ 705 : : {
+ 706 : 4 : ValentSmsStore *self = VALENT_SMS_STORE (source_object);
+ 707 : 4 : int64_t *thread_id = task_data;
+ 708 : 4 : sqlite3_stmt *stmt = self->stmts[STMT_GET_THREAD_DATE];
+ 709 : 4 : int64_t date = 0;
+ 710 : 4 : int rc;
+ 711 : :
+ 712 [ + - ]: 4 : if (g_task_return_error_if_cancelled (task))
+ 713 : : return;
+ 714 : :
+ 715 [ + - ]: 4 : if (valent_sms_store_return_error_if_closed (task, self))
+ 716 : : return;
+ 717 : :
+ 718 : 4 : sqlite3_bind_int64 (stmt, 1, *thread_id);
+ 719 : :
+ 720 [ + + ]: 4 : if ((rc = sqlite3_step (stmt)) == SQLITE_ROW)
+ 721 : 2 : date = sqlite3_column_int64 (stmt, 0);
+ 722 : :
+ 723 : 4 : sqlite3_reset (stmt);
+ 724 : :
+ 725 [ - + ]: 4 : if (rc != SQLITE_DONE && rc != SQLITE_ROW)
+ 726 : : {
+ 727 : 0 : g_task_return_new_error (task,
+ 728 : : G_IO_ERROR,
+ 729 : : G_IO_ERROR_FAILED,
+ 730 : : "%s: %s",
+ 731 : : G_STRFUNC, sqlite3_errstr (rc));
+ 732 : 0 : return;
+ 733 : : }
+ 734 : :
+ 735 : 4 : g_task_return_int (task, date);
+ 736 : : }
+ 737 : :
+ 738 : : static void
+ 739 : 4 : get_thread_items_task (GTask *task,
+ 740 : : gpointer source_object,
+ 741 : : gpointer task_data,
+ 742 : : GCancellable *cancellable)
+ 743 : : {
+ 744 : 4 : ValentSmsStore *self = VALENT_SMS_STORE (source_object);
+ 745 : 4 : int64_t *thread_id = task_data;
+ 746 : 4 : sqlite3_stmt *stmt = self->stmts[STMT_GET_THREAD_ITEMS];
+ 747 : 4 : g_autoptr (GPtrArray) messages = NULL;
+ 748 : 4 : int rc;
+ 749 : :
+ 750 [ + - ]: 4 : if (g_task_return_error_if_cancelled (task))
+ 751 : : return;
+ 752 : :
+ 753 [ + - ]: 4 : if (valent_sms_store_return_error_if_closed (task, self))
+ 754 : : return;
+ 755 : :
+ 756 : 4 : messages = g_ptr_array_new_with_free_func (g_object_unref);
+ 757 : 4 : sqlite3_bind_int64 (stmt, 1, *thread_id);
+ 758 : :
+ 759 [ + + ]: 12 : while ((rc = sqlite3_step (stmt)) == SQLITE_ROW)
+ 760 : : {
+ 761 : 8 : ValentMessage *message;
+ 762 : :
+ 763 : 8 : message = g_object_new (VALENT_TYPE_MESSAGE,
+ 764 : : "date", sqlite3_column_int64 (stmt, 0),
+ 765 : : "id", sqlite3_column_int64 (stmt, 1),
+ 766 : : "sender", sqlite3_column_text (stmt, 2),
+ 767 : : "thread-id", *thread_id,
+ 768 : : NULL);
+ 769 : 8 : g_ptr_array_add (messages, message);
+ 770 : : }
+ 771 : :
+ 772 : 4 : sqlite3_reset (stmt);
+ 773 : :
+ 774 [ - + ]: 4 : if (rc != SQLITE_DONE && rc != SQLITE_ROW)
+ 775 : : {
+ 776 : 0 : g_task_return_new_error (task,
+ 777 : : G_IO_ERROR,
+ 778 : : G_IO_ERROR_FAILED,
+ 779 : : "%s: %s",
+ 780 : : G_STRFUNC, sqlite3_errstr (rc));
+ 781 [ # # ]: 0 : return;
+ 782 : : }
+ 783 : :
+ 784 : 4 : g_task_return_pointer (task,
+ 785 : : g_steal_pointer (&messages),
+ 786 : : (GDestroyNotify)g_ptr_array_unref);
+ 787 : : }
+ 788 : :
+ 789 : :
+ 790 : : /*
+ 791 : : * Private
+ 792 : : */
+ 793 : : static inline void
+ 794 : 34 : valent_sms_store_push (ValentSmsStore *self,
+ 795 : : GTask *task,
+ 796 : : GTaskThreadFunc task_func)
+ 797 : : {
+ 798 : 34 : TaskClosure *closure = NULL;
+ 799 : :
+ 800 [ - + ]: 34 : if G_UNLIKELY (self->queue == NULL)
+ 801 : : {
+ 802 : 0 : g_task_return_new_error (task,
+ 803 : : G_IO_ERROR,
+ 804 : : G_IO_ERROR_CLOSED,
+ 805 : : "Store is closed");
+ 806 : 0 : return;
+ 807 : : }
+ 808 : :
+ 809 : 34 : closure = g_new0 (TaskClosure, 1);
+ 810 : 34 : closure->task = g_object_ref (task);
+ 811 : 34 : closure->task_func = task_func;
+ 812 : 34 : closure->task_mode = TASK_DEFAULT;
+ 813 : 34 : g_async_queue_push (self->queue, closure);
+ 814 : : }
+ 815 : :
+ 816 : : static void
+ 817 : 7 : valent_sms_store_open (ValentSmsStore *self)
+ 818 : : {
+ 819 : 14 : g_autoptr (GThread) thread = NULL;
+ 820 [ + - ]: 7 : g_autoptr (GError) error = NULL;
+ 821 [ - + ]: 7 : g_autoptr (GTask) task = NULL;
+ 822 [ + - ]: 7 : g_autoptr (GFile) file = NULL;
+ 823 : :
+ 824 : 7 : file = valent_context_get_cache_file (VALENT_CONTEXT (self), "sms.db");
+ 825 : 7 : self->path = g_file_get_path (file);
+ 826 : :
+ 827 : 7 : task = g_task_new (self, NULL, NULL, NULL);
+ 828 [ + - ]: 7 : g_task_set_source_tag (task, valent_sms_store_open);
+ 829 [ - + ]: 14 : g_task_set_task_data (task, g_strdup (self->path), g_free);
+ 830 : 7 : valent_sms_store_push (self, task, valent_sms_store_open_task);
+ 831 : :
+ 832 : : /* Spawn the worker thread, passing in a reference to the queue */
+ 833 : 7 : thread = g_thread_try_new ("valent-task-queue",
+ 834 : : valent_sms_store_thread,
+ 835 : 7 : g_async_queue_ref (self->queue),
+ 836 : : &error);
+ 837 : :
+ 838 : : /* On failure drop the reference passed to the thread, then clear the last
+ 839 : : * reference so the open task is cancelled and new tasks are rejected */
+ 840 [ - + ]: 7 : if (error != NULL)
+ 841 : : {
+ 842 : 0 : g_critical ("%s: Failed to spawn worker thread: %s",
+ 843 : : G_OBJECT_TYPE_NAME (self),
+ 844 : : error->message);
+ 845 : 0 : g_async_queue_unref (self->queue);
+ 846 [ - - + - ]: 7 : g_clear_pointer (&self->queue, g_async_queue_unref);
+ 847 : : }
+ 848 : 7 : }
+ 849 : :
+ 850 : : static void
+ 851 : 7 : valent_sms_store_close (ValentSmsStore *self)
+ 852 : : {
+ 853 : 14 : g_autoptr (GTask) task = NULL;
+ 854 : 7 : TaskClosure *closure = NULL;
+ 855 : :
+ 856 : 7 : task = g_task_new (self, NULL, NULL, NULL);
+ 857 [ + - ]: 7 : g_task_set_source_tag (task, valent_sms_store_close);
+ 858 : :
+ 859 : 7 : closure = g_new0 (TaskClosure, 1);
+ 860 : 7 : closure->task = g_object_ref (task);
+ 861 : 7 : closure->task_func = valent_sms_store_close_task;
+ 862 : 7 : closure->task_mode = TASK_TERMINAL;
+ 863 [ + - ]: 7 : g_async_queue_push (self->queue, closure);
+ 864 : 7 : }
+ 865 : :
+ 866 : : /*
+ 867 : : * ValentObject
+ 868 : : */
+ 869 : : static void
+ 870 : 9 : valent_sms_store_destroy (ValentObject *object)
+ 871 : : {
+ 872 : 9 : ValentSmsStore *self = VALENT_SMS_STORE (object);
+ 873 : :
+ 874 : : /* We will drop our reference to queue once we queue the closing task, then
+ 875 : : * the task itself will end up holding the last reference. */
+ 876 [ + + ]: 9 : if (self->queue != NULL)
+ 877 : : {
+ 878 : 7 : valent_sms_store_close (self);
+ 879 [ + - ]: 7 : g_clear_pointer (&self->queue, g_async_queue_unref);
+ 880 : : }
+ 881 : :
+ 882 : 9 : VALENT_OBJECT_CLASS (valent_sms_store_parent_class)->destroy (object);
+ 883 : 9 : }
+ 884 : :
+ 885 : : /*
+ 886 : : * GObject
+ 887 : : */
+ 888 : : static void
+ 889 : 7 : valent_sms_store_constructed (GObject *object)
+ 890 : : {
+ 891 : 7 : ValentSmsStore *self = VALENT_SMS_STORE (object);
+ 892 : :
+ 893 : : /* Chain-up before queueing the open task to ensure the path is prepared */
+ 894 : 7 : G_OBJECT_CLASS (valent_sms_store_parent_class)->constructed (object);
+ 895 : :
+ 896 : 7 : valent_sms_store_open (self);
+ 897 : 7 : }
+ 898 : :
+ 899 : : static void
+ 900 : 2 : valent_sms_store_finalize (GObject *object)
+ 901 : : {
+ 902 : 2 : ValentSmsStore *self = VALENT_SMS_STORE (object);
+ 903 : :
+ 904 [ - + ]: 2 : g_clear_pointer (&self->queue, g_async_queue_unref);
+ 905 [ + - ]: 2 : g_clear_pointer (&self->path, g_free);
+ 906 : 2 : g_clear_weak_pointer (&self->summary);
+ 907 : :
+ 908 : 2 : G_OBJECT_CLASS (valent_sms_store_parent_class)->finalize (object);
+ 909 : 2 : }
+ 910 : :
+ 911 : : static void
+ 912 : 5 : valent_sms_store_class_init (ValentSmsStoreClass *klass)
+ 913 : : {
+ 914 : 5 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ 915 : 5 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
+ 916 : :
+ 917 : 5 : object_class->constructed = valent_sms_store_constructed;
+ 918 : 5 : object_class->finalize = valent_sms_store_finalize;
+ 919 : :
+ 920 : 5 : vobject_class->destroy = valent_sms_store_destroy;
+ 921 : :
+ 922 : : /**
+ 923 : : * ValentSmsStore::message-added:
+ 924 : : * @store: a `ValentSmsStore`
+ 925 : : * @message: a `ValentMessage`
+ 926 : : *
+ 927 : : * ValentSmsStore::message-added is emitted when a new message is added to
+ 928 : : * @store.
+ 929 : : */
+ 930 : 10 : signals [MESSAGE_ADDED] =
+ 931 : 5 : g_signal_new ("message-added",
+ 932 : : VALENT_TYPE_SMS_STORE,
+ 933 : : G_SIGNAL_RUN_LAST,
+ 934 : : 0,
+ 935 : : NULL, NULL,
+ 936 : : g_cclosure_marshal_VOID__OBJECT,
+ 937 : : G_TYPE_NONE, 1, VALENT_TYPE_MESSAGE);
+ 938 : 5 : g_signal_set_va_marshaller (signals [MESSAGE_ADDED],
+ 939 : : G_TYPE_FROM_CLASS (klass),
+ 940 : : g_cclosure_marshal_VOID__OBJECTv);
+ 941 : :
+ 942 : : /**
+ 943 : : * ValentSmsStore::message-changed:
+ 944 : : * @store: a `ValentSmsStore`
+ 945 : : * @message: a `ValentMessage`
+ 946 : : *
+ 947 : : * ValentSmsStore::message-changed is emitted when a message is updated in
+ 948 : : * @store.
+ 949 : : */
+ 950 : 10 : signals [MESSAGE_CHANGED] =
+ 951 : 5 : g_signal_new ("message-changed",
+ 952 : : VALENT_TYPE_SMS_STORE,
+ 953 : : G_SIGNAL_RUN_LAST,
+ 954 : : 0,
+ 955 : : NULL, NULL,
+ 956 : : g_cclosure_marshal_VOID__OBJECT,
+ 957 : : G_TYPE_NONE, 1, VALENT_TYPE_MESSAGE);
+ 958 : 5 : g_signal_set_va_marshaller (signals [MESSAGE_CHANGED],
+ 959 : : G_TYPE_FROM_CLASS (klass),
+ 960 : : g_cclosure_marshal_VOID__OBJECTv);
+ 961 : :
+ 962 : : /**
+ 963 : : * ValentSmsStore::message-removed:
+ 964 : : * @store: a `ValentSmsStore`
+ 965 : : * @message: a `ValentMessage`
+ 966 : : *
+ 967 : : * ValentSmsStore::message-removed is emitted when a message is removed from
+ 968 : : * @store.
+ 969 : : */
+ 970 : 10 : signals [MESSAGE_REMOVED] =
+ 971 : 5 : g_signal_new ("message-removed",
+ 972 : : VALENT_TYPE_SMS_STORE,
+ 973 : : G_SIGNAL_RUN_FIRST,
+ 974 : : 0,
+ 975 : : NULL, NULL,
+ 976 : : g_cclosure_marshal_VOID__OBJECT,
+ 977 : : G_TYPE_NONE, 1, VALENT_TYPE_MESSAGE);
+ 978 : 5 : g_signal_set_va_marshaller (signals [MESSAGE_REMOVED],
+ 979 : : G_TYPE_FROM_CLASS (klass),
+ 980 : : g_cclosure_marshal_VOID__OBJECTv);
+ 981 : :
+ 982 : : /* SQL Statements */
+ 983 : 5 : statements[STMT_ADD_MESSAGE] = ADD_MESSAGE_SQL;
+ 984 : 5 : statements[STMT_REMOVE_MESSAGE] = REMOVE_MESSAGE_SQL;
+ 985 : 5 : statements[STMT_REMOVE_THREAD] = REMOVE_THREAD_SQL;
+ 986 : 5 : statements[STMT_GET_MESSAGE] = GET_MESSAGE_SQL;
+ 987 : 5 : statements[STMT_GET_THREAD] = GET_THREAD_SQL;
+ 988 : 5 : statements[STMT_GET_THREAD_DATE] = GET_THREAD_DATE_SQL;
+ 989 : 5 : statements[STMT_GET_THREAD_ITEMS] = GET_THREAD_ITEMS_SQL;
+ 990 : 5 : statements[STMT_FIND_MESSAGES] = FIND_MESSAGES_SQL;
+ 991 : 5 : statements[STMT_GET_SUMMARY] = GET_SUMMARY_SQL;
+ 992 : 5 : }
+ 993 : :
+ 994 : : static void
+ 995 : 7 : valent_sms_store_init (ValentSmsStore *self)
+ 996 : : {
+ 997 : 7 : self->queue = g_async_queue_new_full (task_closure_cancel);
+ 998 : 7 : }
+ 999 : :
+ 1000 : : /**
+ 1001 : : * valent_sms_store_new:
+ 1002 : : * @parent: a `ValentContext`
+ 1003 : : *
+ 1004 : : * Create a new `ValentSmsStore`.
+ 1005 : : *
+ 1006 : : * Returns: (transfer full): a new sms store
+ 1007 : : */
+ 1008 : : ValentSmsStore *
+ 1009 : 4 : valent_sms_store_new (ValentContext *parent)
+ 1010 : : {
+ 1011 : 4 : return g_object_new (VALENT_TYPE_SMS_STORE,
+ 1012 : : "domain", "plugin",
+ 1013 : : "id", "sms",
+ 1014 : : "parent", parent,
+ 1015 : : NULL);
+ 1016 : : }
+ 1017 : :
+ 1018 : : /**
+ 1019 : : * valent_sms_store_add_message:
+ 1020 : : * @store: a `ValentSmsStore`
+ 1021 : : * @message: a `ValentMessage`
+ 1022 : : * @cancellable: (nullable): a `GCancellable`
+ 1023 : : * @callback: (scope async): a `GAsyncReadyCallback`
+ 1024 : : * @user_data: (closure): user supplied data
+ 1025 : : *
+ 1026 : : * Add @message to @store.
+ 1027 : : */
+ 1028 : : void
+ 1029 : 1 : valent_sms_store_add_message (ValentSmsStore *store,
+ 1030 : : ValentMessage *message,
+ 1031 : : GCancellable *cancellable,
+ 1032 : : GAsyncReadyCallback callback,
+ 1033 : : gpointer user_data)
+ 1034 : : {
+ 1035 : 2 : g_autoptr (GTask) task = NULL;
+ 1036 [ + - ]: 1 : g_autoptr (GPtrArray) messages = NULL;
+ 1037 : :
+ 1038 [ + - ]: 1 : g_return_if_fail (VALENT_IS_SMS_STORE (store));
+ 1039 [ - + ]: 1 : g_return_if_fail (VALENT_IS_MESSAGE (message));
+ 1040 : :
+ 1041 : 1 : messages = g_ptr_array_new_with_free_func (g_object_unref);
+ 1042 : 1 : g_ptr_array_add (messages, g_object_ref (message));
+ 1043 : :
+ 1044 : 1 : task = g_task_new (store, cancellable, callback, user_data);
+ 1045 [ + - ]: 1 : g_task_set_source_tag (task, valent_sms_store_add_message);
+ 1046 : 1 : g_task_set_task_data (task,
+ 1047 : : g_steal_pointer (&messages),
+ 1048 : : (GDestroyNotify)g_ptr_array_unref);
+ 1049 [ + - ]: 1 : valent_sms_store_push (store, task, add_messages_task);
+ 1050 : : }
+ 1051 : :
+ 1052 : : /**
+ 1053 : : * valent_sms_store_add_messages:
+ 1054 : : * @store: a `ValentSmsStore`
+ 1055 : : * @messages: (element-type Valent.Message): a `ValentMessage`
+ 1056 : : * @cancellable: (nullable): a `GCancellable`
+ 1057 : : * @callback: (scope async): a `GAsyncReadyCallback`
+ 1058 : : * @user_data: (closure): user supplied data
+ 1059 : : *
+ 1060 : : * Add @messages to @store.
+ 1061 : : */
+ 1062 : : void
+ 1063 : 6 : valent_sms_store_add_messages (ValentSmsStore *store,
+ 1064 : : GPtrArray *messages,
+ 1065 : : GCancellable *cancellable,
+ 1066 : : GAsyncReadyCallback callback,
+ 1067 : : gpointer user_data)
+ 1068 : : {
+ 1069 : 12 : g_autoptr (GTask) task = NULL;
+ 1070 : :
+ 1071 [ + - ]: 6 : g_return_if_fail (VALENT_IS_SMS_STORE (store));
+ 1072 [ - + ]: 6 : g_return_if_fail (messages != NULL);
+ 1073 : :
+ 1074 : 6 : task = g_task_new (store, cancellable, callback, user_data);
+ 1075 [ + - ]: 6 : g_task_set_source_tag (task, valent_sms_store_add_message);
+ 1076 : 6 : g_task_set_task_data (task,
+ 1077 : 6 : g_ptr_array_ref (messages),
+ 1078 : : (GDestroyNotify)g_ptr_array_unref);
+ 1079 [ + - ]: 6 : valent_sms_store_push (store, task, add_messages_task);
+ 1080 : : }
+ 1081 : :
+ 1082 : : /**
+ 1083 : : * valent_sms_store_add_messages_finish:
+ 1084 : : * @store: a `ValentSmsStore`
+ 1085 : : * @result: a `GAsyncResult`
+ 1086 : : * @error: (nullable): a `GError`
+ 1087 : : *
+ 1088 : : * Finish an operation started by valent_sms_store_add_messages().
+ 1089 : : *
+ 1090 : : * Returns: %TRUE, or %FALSE with @error set
+ 1091 : : */
+ 1092 : : gboolean
+ 1093 : 5 : valent_sms_store_add_messages_finish (ValentSmsStore *store,
+ 1094 : : GAsyncResult *result,
+ 1095 : : GError **error)
+ 1096 : : {
+ 1097 [ + - ]: 5 : g_return_val_if_fail (VALENT_IS_SMS_STORE (store), FALSE);
+ 1098 [ - + ]: 5 : g_return_val_if_fail (g_task_is_valid (result, store), FALSE);
+ 1099 [ + - - + ]: 5 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ 1100 : :
+ 1101 : 5 : return g_task_propagate_boolean (G_TASK (result), error);
+ 1102 : : }
+ 1103 : :
+ 1104 : : /**
+ 1105 : : * valent_sms_store_remove_message:
+ 1106 : : * @store: a `ValentSmsStore`
+ 1107 : : * @message_id: a message ID
+ 1108 : : * @cancellable: (nullable): a `GCancellable`
+ 1109 : : * @callback: (scope async): a `GAsyncReadyCallback`
+ 1110 : : * @user_data: (closure): user supplied data
+ 1111 : : *
+ 1112 : : * Remove the message with @message_id from @thread_id.
+ 1113 : : */
+ 1114 : : void
+ 1115 : 1 : valent_sms_store_remove_message (ValentSmsStore *store,
+ 1116 : : int64_t message_id,
+ 1117 : : GCancellable *cancellable,
+ 1118 : : GAsyncReadyCallback callback,
+ 1119 : : gpointer user_data)
+ 1120 : : {
+ 1121 : 0 : g_autoptr (GTask) task = NULL;
+ 1122 : 1 : int64_t *task_data;
+ 1123 : :
+ 1124 [ + - ]: 1 : g_return_if_fail (VALENT_IS_SMS_STORE (store));
+ 1125 : :
+ 1126 : 1 : task_data = g_new0 (int64_t, 1);
+ 1127 : 1 : *task_data = message_id;
+ 1128 : :
+ 1129 : 1 : task = g_task_new (store, cancellable, callback, user_data);
+ 1130 : 1 : g_task_set_task_data (task, task_data, g_free);
+ 1131 [ + - ]: 1 : g_task_set_source_tag (task, valent_sms_store_remove_message);
+ 1132 [ + - ]: 1 : valent_sms_store_push (store, task, remove_message_task);
+ 1133 : : }
+ 1134 : :
+ 1135 : : /**
+ 1136 : : * valent_sms_store_remove_message_finish:
+ 1137 : : * @store: a `ValentSmsStore`
+ 1138 : : * @result: a `GAsyncResult`
+ 1139 : : * @error: (nullable): a `GError`
+ 1140 : : *
+ 1141 : : * Finish an operation started by valent_sms_store_remove_message().
+ 1142 : : *
+ 1143 : : * Returns: %TRUE, or %FALSE with @error set
+ 1144 : : */
+ 1145 : : gboolean
+ 1146 : 1 : valent_sms_store_remove_message_finish (ValentSmsStore *store,
+ 1147 : : GAsyncResult *result,
+ 1148 : : GError **error)
+ 1149 : : {
+ 1150 [ + - ]: 1 : g_return_val_if_fail (VALENT_IS_SMS_STORE (store), FALSE);
+ 1151 [ - + ]: 1 : g_return_val_if_fail (g_task_is_valid (result, store), FALSE);
+ 1152 [ + - - + ]: 1 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ 1153 : :
+ 1154 : 1 : return g_task_propagate_boolean (G_TASK (result), error);
+ 1155 : : }
+ 1156 : :
+ 1157 : : /**
+ 1158 : : * valent_sms_store_remove_thread:
+ 1159 : : * @store: a `ValentSmsStore`
+ 1160 : : * @thread_id: a thread ID
+ 1161 : : * @cancellable: (nullable): a `GCancellable`
+ 1162 : : * @callback: (scope async): a `GAsyncReadyCallback`
+ 1163 : : * @user_data: (closure): user supplied data
+ 1164 : : *
+ 1165 : : * Remove @thread_id and all it's messages from @store.
+ 1166 : : */
+ 1167 : : void
+ 1168 : 1 : valent_sms_store_remove_thread (ValentSmsStore *store,
+ 1169 : : int64_t thread_id,
+ 1170 : : GCancellable *cancellable,
+ 1171 : : GAsyncReadyCallback callback,
+ 1172 : : gpointer user_data)
+ 1173 : : {
+ 1174 : 2 : g_autoptr (GTask) task = NULL;
+ 1175 : 1 : int64_t *task_data;
+ 1176 : :
+ 1177 [ + - ]: 1 : g_return_if_fail (VALENT_IS_SMS_STORE (store));
+ 1178 [ - + ]: 1 : g_return_if_fail (thread_id >= 0);
+ 1179 : :
+ 1180 : 1 : task_data = g_new0 (int64_t, 1);
+ 1181 : 1 : *task_data = thread_id;
+ 1182 : :
+ 1183 : 1 : task = g_task_new (store, cancellable, callback, user_data);
+ 1184 : 1 : g_task_set_task_data (task, task_data, g_free);
+ 1185 [ + - ]: 1 : g_task_set_source_tag (task, valent_sms_store_remove_thread);
+ 1186 [ + - ]: 1 : valent_sms_store_push (store, task, remove_thread_task);
+ 1187 : : }
+ 1188 : :
+ 1189 : : /**
+ 1190 : : * valent_sms_store_remove_thread_finish:
+ 1191 : : * @store: a `ValentSmsStore`
+ 1192 : : * @result: a `GAsyncResult`
+ 1193 : : * @error: (nullable): a `GError`
+ 1194 : : *
+ 1195 : : * Finish an operation started by valent_sms_store_remove_thread().
+ 1196 : : *
+ 1197 : : * Returns: %TRUE, or %FALSE with @error set
+ 1198 : : */
+ 1199 : : gboolean
+ 1200 : 1 : valent_sms_store_remove_thread_finish (ValentSmsStore *store,
+ 1201 : : GAsyncResult *result,
+ 1202 : : GError **error)
+ 1203 : : {
+ 1204 [ + - ]: 1 : g_return_val_if_fail (VALENT_IS_SMS_STORE (store), FALSE);
+ 1205 [ - + ]: 1 : g_return_val_if_fail (g_task_is_valid (result, store), FALSE);
+ 1206 [ + - - + ]: 1 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ 1207 : :
+ 1208 : 1 : return g_task_propagate_boolean (G_TASK (result), error);
+ 1209 : : }
+ 1210 : :
+ 1211 : : /**
+ 1212 : : * valent_sms_store_find_messages:
+ 1213 : : * @store: a `ValentSmsStore`
+ 1214 : : * @query: a string to search for
+ 1215 : : * @cancellable: (nullable): a `GCancellable`
+ 1216 : : * @callback: (scope async): a `GAsyncReadyCallback`
+ 1217 : : * @user_data: (closure): user supplied data
+ 1218 : : *
+ 1219 : : * Search through all the messages in @store and return the most recent message
+ 1220 : : * from each thread containing @query.
+ 1221 : : *
+ 1222 : : * Call valent_sms_store_find_messages_finish() to get the result.
+ 1223 : : */
+ 1224 : : void
+ 1225 : 1 : valent_sms_store_find_messages (ValentSmsStore *store,
+ 1226 : : const char *query,
+ 1227 : : GCancellable *cancellable,
+ 1228 : : GAsyncReadyCallback callback,
+ 1229 : : gpointer user_data)
+ 1230 : : {
+ 1231 : 2 : g_autoptr (GTask) task = NULL;
+ 1232 : :
+ 1233 [ + - ]: 1 : g_return_if_fail (VALENT_IS_SMS_STORE (store));
+ 1234 [ - + ]: 1 : g_return_if_fail (query != NULL);
+ 1235 [ - + - - : 1 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+ - - - - ]
+ 1236 : :
+ 1237 : 1 : task = g_task_new (store, cancellable, callback, user_data);
+ 1238 [ + - ]: 1 : g_task_set_source_tag (task, valent_sms_store_find_messages);
+ 1239 [ - + ]: 2 : g_task_set_task_data (task, g_strdup (query), g_free);
+ 1240 [ + - ]: 1 : valent_sms_store_push (store, task, find_messages_task);
+ 1241 : : }
+ 1242 : :
+ 1243 : : /**
+ 1244 : : * valent_sms_store_find_messages_finish:
+ 1245 : : * @store: a `ValentSmsStore`
+ 1246 : : * @result: a `GAsyncResult`
+ 1247 : : * @error: (nullable): a `GError`
+ 1248 : : *
+ 1249 : : * Finish an operation started by valent_sms_store_find_messages().
+ 1250 : : *
+ 1251 : : * Returns: (transfer container) (element-type Valent.Message): an `GPtrArray`
+ 1252 : : */
+ 1253 : : GPtrArray *
+ 1254 : 1 : valent_sms_store_find_messages_finish (ValentSmsStore *store,
+ 1255 : : GAsyncResult *result,
+ 1256 : : GError **error)
+ 1257 : : {
+ 1258 [ + - ]: 1 : g_return_val_if_fail (VALENT_IS_SMS_STORE (store), NULL);
+ 1259 [ - + ]: 1 : g_return_val_if_fail (g_task_is_valid (result, store), NULL);
+ 1260 [ + - - + ]: 1 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+ 1261 : :
+ 1262 : 1 : return g_task_propagate_pointer (G_TASK (result), error);
+ 1263 : : }
+ 1264 : :
+ 1265 : : /**
+ 1266 : : * valent_sms_store_get_message:
+ 1267 : : * @store: a `ValentSmsStore`
+ 1268 : : * @message_id: a message ID
+ 1269 : : * @cancellable: (nullable): a `GCancellable`
+ 1270 : : * @callback: (scope async): a `GAsyncReadyCallback`
+ 1271 : : * @user_data: (closure): user supplied data
+ 1272 : : *
+ 1273 : : * Get the `ValentMessage` with @message_id or %NULL if not found.
+ 1274 : : *
+ 1275 : : * Returns: (transfer none) (nullable): a `ValentMessage`
+ 1276 : : */
+ 1277 : : void
+ 1278 : 6 : valent_sms_store_get_message (ValentSmsStore *store,
+ 1279 : : int64_t message_id,
+ 1280 : : GCancellable *cancellable,
+ 1281 : : GAsyncReadyCallback callback,
+ 1282 : : gpointer user_data)
+ 1283 : : {
+ 1284 : 12 : g_autoptr (GTask) task = NULL;
+ 1285 : 6 : int64_t *task_data;
+ 1286 : :
+ 1287 [ + - ]: 6 : g_return_if_fail (VALENT_IS_SMS_STORE (store));
+ 1288 [ + + + - : 6 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+ - + - - ]
+ 1289 : :
+ 1290 : 6 : task_data = g_new (int64_t, 1);
+ 1291 : 6 : *task_data = message_id;
+ 1292 : :
+ 1293 : 6 : task = g_task_new (store, cancellable, callback, user_data);
+ 1294 : 6 : g_task_set_task_data (task, task_data, g_free);
+ 1295 [ + - ]: 6 : g_task_set_source_tag (task, valent_sms_store_get_message);
+ 1296 [ + - ]: 6 : valent_sms_store_push (store, task, get_message_task);
+ 1297 : : }
+ 1298 : :
+ 1299 : : /**
+ 1300 : : * valent_sms_store_get_message_finish:
+ 1301 : : * @store: a `ValentSmsStore`
+ 1302 : : * @result: a `GAsyncResult`
+ 1303 : : * @error: (nullable): a `GError`
+ 1304 : : *
+ 1305 : : * Finish an operation started by valent_sms_store_get_message().
+ 1306 : : *
+ 1307 : : * Returns: (transfer full) (nullable): a `ValentMessage`
+ 1308 : : */
+ 1309 : : ValentMessage *
+ 1310 : 6 : valent_sms_store_get_message_finish (ValentSmsStore *store,
+ 1311 : : GAsyncResult *result,
+ 1312 : : GError **error)
+ 1313 : : {
+ 1314 [ + - ]: 6 : g_return_val_if_fail (VALENT_IS_SMS_STORE (store), NULL);
+ 1315 [ - + ]: 6 : g_return_val_if_fail (g_task_is_valid (result, store), FALSE);
+ 1316 [ + - - + ]: 6 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+ 1317 : :
+ 1318 : 6 : return g_task_propagate_pointer (G_TASK (result), error);
+ 1319 : : }
+ 1320 : :
+ 1321 : : /**
+ 1322 : : * valent_sms_store_get_summary:
+ 1323 : : * @store: a `ValentSmsStore`
+ 1324 : : *
+ 1325 : : * Get the latest message of each thread as a `GListModel`.
+ 1326 : : *
+ 1327 : : * Returns: (transfer full) (nullable): a `GListModel`
+ 1328 : : */
+ 1329 : : GListModel *
+ 1330 : 3 : valent_sms_store_get_summary (ValentSmsStore *store)
+ 1331 : : {
+ 1332 : 6 : g_autoptr (GTask) task = NULL;
+ 1333 : :
+ 1334 [ + - ]: 3 : g_return_val_if_fail (VALENT_IS_SMS_STORE (store), NULL);
+ 1335 : :
+ 1336 [ - + ]: 3 : if (store->summary != NULL)
+ 1337 : 0 : return g_object_ref (G_LIST_MODEL (store->summary));
+ 1338 : :
+ 1339 : 3 : store->summary = g_list_store_new (VALENT_TYPE_MESSAGE);
+ 1340 : 3 : g_object_add_weak_pointer (G_OBJECT (store->summary),
+ 1341 : 3 : (gpointer)&store->summary);
+ 1342 : :
+ 1343 : 3 : task = g_task_new (store, NULL, (GAsyncReadyCallback)get_summary_cb, NULL);
+ 1344 [ + - ]: 3 : g_task_set_source_tag (task, valent_sms_store_get_summary);
+ 1345 : 3 : valent_sms_store_push (store, task, get_summary_task);
+ 1346 : :
+ 1347 [ - + ]: 3 : return G_LIST_MODEL (store->summary);
+ 1348 : : }
+ 1349 : :
+ 1350 : : /**
+ 1351 : : * valent_sms_store_get_thread:
+ 1352 : : * @store: a `ValentSmsStore`
+ 1353 : : * @thread_id: a message id
+ 1354 : : *
+ 1355 : : * Get the thread with @thread_id as a `GListModel`.
+ 1356 : : *
+ 1357 : : * Returns: (transfer full): a `GListModel`
+ 1358 : : */
+ 1359 : : GListModel *
+ 1360 : 4 : valent_sms_store_get_thread (ValentSmsStore *store,
+ 1361 : : int64_t thread_id)
+ 1362 : : {
+ 1363 [ + - ]: 4 : g_return_val_if_fail (VALENT_IS_SMS_STORE (store), 0);
+ 1364 [ - + ]: 4 : g_return_val_if_fail (thread_id > 0, 0);
+ 1365 : :
+ 1366 : 4 : return valent_message_thread_new (store, thread_id);
+ 1367 : : }
+ 1368 : :
+ 1369 : : /**
+ 1370 : : * valent_sms_store_get_thread_date:
+ 1371 : : * @store: a `ValentSmsStore`
+ 1372 : : * @thread_id: a thread ID
+ 1373 : : *
+ 1374 : : * Get the date of the last message in @thread_id.
+ 1375 : : *
+ 1376 : : * Returns: a UNIX epoch timestamp.
+ 1377 : : */
+ 1378 : : int64_t
+ 1379 : 4 : valent_sms_store_get_thread_date (ValentSmsStore *store,
+ 1380 : : int64_t thread_id)
+ 1381 : : {
+ 1382 : 8 : g_autoptr (GTask) task = NULL;
+ 1383 [ + - ]: 4 : g_autoptr (GError) error = NULL;
+ 1384 : 4 : int64_t date = 0;
+ 1385 : :
+ 1386 [ + - ]: 4 : g_return_val_if_fail (VALENT_IS_SMS_STORE (store), 0);
+ 1387 [ - + ]: 4 : g_return_val_if_fail (thread_id >= 0, 0);
+ 1388 : :
+ 1389 : 4 : task = g_task_new (store, NULL, NULL, NULL);
+ 1390 [ + - ]: 4 : g_task_set_source_tag (task, valent_sms_store_get_thread_date);
+ 1391 : 4 : g_task_set_task_data (task, &thread_id, NULL);
+ 1392 : 4 : valent_sms_store_push (store, task, get_thread_date_task);
+ 1393 : :
+ 1394 [ + + ]: 637 : while (!g_task_get_completed (task))
+ 1395 : 633 : g_main_context_iteration (NULL, FALSE);
+ 1396 : :
+ 1397 : 4 : date = g_task_propagate_int (task, &error);
+ 1398 : :
+ 1399 [ - + ]: 4 : if (error != NULL)
+ 1400 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
+ 1401 : :
+ 1402 : : return date;
+ 1403 : : }
+ 1404 : :
+ 1405 : : /**
+ 1406 : : * valent_sms_store_get_thread_items:
+ 1407 : : * @store: a `ValentSmsStore`
+ 1408 : : * @thread_id: a thread ID
+ 1409 : : *
+ 1410 : : * Get the `ValentMessage` in @thread_id at @position, when sorted by date in
+ 1411 : : * ascending order.
+ 1412 : : */
+ 1413 : : void
+ 1414 : 4 : valent_sms_store_get_thread_items (ValentSmsStore *store,
+ 1415 : : int64_t thread_id,
+ 1416 : : GCancellable *cancellable,
+ 1417 : : GAsyncReadyCallback callback,
+ 1418 : : gpointer user_data)
+ 1419 : : {
+ 1420 : 8 : g_autoptr (GTask) task = NULL;
+ 1421 : 4 : int64_t *task_data;
+ 1422 : :
+ 1423 [ + - ]: 4 : g_return_if_fail (VALENT_IS_SMS_STORE (store));
+ 1424 [ - + ]: 4 : g_return_if_fail (thread_id >= 0);
+ 1425 : :
+ 1426 : 4 : task_data = g_new0 (int64_t, 1);
+ 1427 : 4 : *task_data = thread_id;
+ 1428 : :
+ 1429 : 4 : task = g_task_new (store, cancellable, callback, user_data);
+ 1430 [ + - ]: 4 : g_task_set_source_tag (task, valent_sms_store_get_thread_items);
+ 1431 : 4 : g_task_set_task_data (task, task_data, g_free);
+ 1432 [ + - ]: 4 : valent_sms_store_push (store, task, get_thread_items_task);
+ 1433 : : }
+ 1434 : :
+ 1435 : : /**
+ 1436 : : * valent_sms_store_message_added:
+ 1437 : : * @store: a `ValentSmsStore`
+ 1438 : : * @message: a `ValentMessage`
+ 1439 : : *
+ 1440 : : * Emits the `ValentSmsStore`::message-added signal on @store.
+ 1441 : : *
+ 1442 : : * This function should only be called by classes implementing
+ 1443 : : * `ValentSmsStore`. It has to be called after the internal representation
+ 1444 : : * of @store has been updated, because handlers connected to this signal
+ 1445 : : * might query the new state of the provider.
+ 1446 : : */
+ 1447 : : void
+ 1448 : 13 : valent_sms_store_message_added (ValentSmsStore *store,
+ 1449 : : ValentMessage *message)
+ 1450 : : {
+ 1451 : 13 : ChangeEmission *emission;
+ 1452 : :
+ 1453 [ + - ]: 13 : g_return_if_fail (VALENT_IS_SMS_STORE (store));
+ 1454 [ - + ]: 13 : g_return_if_fail (VALENT_IS_MESSAGE (message));
+ 1455 : :
+ 1456 [ + - ]: 13 : if G_LIKELY (VALENT_IS_MAIN_THREAD ())
+ 1457 : : {
+ 1458 : 0 : g_signal_emit (G_OBJECT (store), signals [MESSAGE_ADDED], 0, message);
+ 1459 : 0 : return;
+ 1460 : : }
+ 1461 : :
+ 1462 : 13 : emission = g_new0 (ChangeEmission, 1);
+ 1463 : 13 : g_rec_mutex_init (&emission->mutex);
+ 1464 : 13 : g_rec_mutex_lock (&emission->mutex);
+ 1465 : 13 : g_weak_ref_init (&emission->store, store);
+ 1466 : 13 : emission->message = g_object_ref (message);
+ 1467 : 13 : emission->signal_id = signals [MESSAGE_ADDED];
+ 1468 : 13 : g_rec_mutex_unlock (&emission->mutex);
+ 1469 : :
+ 1470 : 13 : g_idle_add_full (G_PRIORITY_DEFAULT,
+ 1471 : : emit_change_main,
+ 1472 : : g_steal_pointer (&emission),
+ 1473 : : NULL);
+ 1474 : : }
+ 1475 : :
+ 1476 : : /**
+ 1477 : : * valent_sms_store_message_removed:
+ 1478 : : * @store: a `ValentSmsStore`
+ 1479 : : * @message: a `ValentMessage`
+ 1480 : : *
+ 1481 : : * Emits the `ValentSmsStore`::message-removed signal on @store.
+ 1482 : : *
+ 1483 : : * This function should only be called by classes implementing
+ 1484 : : * `ValentSmsStore`. It has to be called after the internal representation
+ 1485 : : * of @store has been updated, because handlers connected to this signal
+ 1486 : : * might query the new state of the provider.
+ 1487 : : */
+ 1488 : : void
+ 1489 : 3 : valent_sms_store_message_removed (ValentSmsStore *store,
+ 1490 : : ValentMessage *message)
+ 1491 : : {
+ 1492 : 3 : ChangeEmission *emission;
+ 1493 : :
+ 1494 [ + - ]: 3 : g_return_if_fail (VALENT_IS_SMS_STORE (store));
+ 1495 [ - + ]: 3 : g_return_if_fail (VALENT_IS_MESSAGE (message));
+ 1496 : :
+ 1497 [ + - ]: 3 : if G_LIKELY (VALENT_IS_MAIN_THREAD ())
+ 1498 : : {
+ 1499 : 0 : g_signal_emit (G_OBJECT (store), signals [MESSAGE_REMOVED], 0, message);
+ 1500 : 0 : return;
+ 1501 : : }
+ 1502 : :
+ 1503 : 3 : emission = g_new0 (ChangeEmission, 1);
+ 1504 : 3 : g_rec_mutex_init (&emission->mutex);
+ 1505 : 3 : g_rec_mutex_lock (&emission->mutex);
+ 1506 : 3 : g_weak_ref_init (&emission->store, store);
+ 1507 : 3 : emission->message = g_object_ref (message);
+ 1508 : 3 : emission->signal_id = signals [MESSAGE_REMOVED];
+ 1509 : 3 : g_rec_mutex_unlock (&emission->mutex);
+ 1510 : :
+ 1511 : 3 : g_idle_add_full (G_PRIORITY_DEFAULT,
+ 1512 : : emit_change_main,
+ 1513 : : g_steal_pointer (&emission),
+ 1514 : : NULL);
+ 1515 : : }
+ 1516 : :
+ 1517 : : /**
+ 1518 : : * valent_sms_store_message_changed:
+ 1519 : : * @store: a `ValentSmsStore`
+ 1520 : : * @message: a `ValentMessage`
+ 1521 : : *
+ 1522 : : * Emits the `ValentSmsStore`::message-changed signal on @store.
+ 1523 : : *
+ 1524 : : * This function should only be called by classes implementing
+ 1525 : : * `ValentSmsStore`. It has to be called after the internal representation
+ 1526 : : * of @store has been updated, because handlers connected to this signal
+ 1527 : : * might query the new state of the provider.
+ 1528 : : */
+ 1529 : : void
+ 1530 : 1 : valent_sms_store_message_changed (ValentSmsStore *store,
+ 1531 : : ValentMessage *message)
+ 1532 : : {
+ 1533 : 1 : ChangeEmission *emission;
+ 1534 : :
+ 1535 [ + - ]: 1 : g_return_if_fail (VALENT_IS_SMS_STORE (store));
+ 1536 [ - + ]: 1 : g_return_if_fail (VALENT_IS_MESSAGE (message));
+ 1537 : :
+ 1538 [ + - ]: 1 : if G_LIKELY (VALENT_IS_MAIN_THREAD ())
+ 1539 : : {
+ 1540 : 0 : g_signal_emit (G_OBJECT (store), signals [MESSAGE_CHANGED], 0, message);
+ 1541 : 0 : return;
+ 1542 : : }
+ 1543 : :
+ 1544 : 1 : emission = g_new0 (ChangeEmission, 1);
+ 1545 : 1 : g_rec_mutex_init (&emission->mutex);
+ 1546 : 1 : g_rec_mutex_lock (&emission->mutex);
+ 1547 : 1 : g_weak_ref_init (&emission->store, store);
+ 1548 : 1 : emission->message = g_object_ref (message);
+ 1549 : 1 : emission->signal_id = signals [MESSAGE_CHANGED];
+ 1550 : 1 : g_rec_mutex_unlock (&emission->mutex);
+ 1551 : :
+ 1552 : 1 : g_idle_add_full (G_PRIORITY_DEFAULT,
+ 1553 : : emit_change_main,
+ 1554 : : g_steal_pointer (&emission),
+ 1555 : : NULL);
+ 1556 : : }
+ 1557 : :
+
+ |
+
+