LCOV - code coverage report
Current view: top level - src/plugins/sms - valent-sms-store.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 91.2 % 559 510
Test Date: 2024-04-16 07:28:14 Functions: 98.0 % 49 48
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 50.3 % 380 191

             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   [ +  +  +  - ]:         166 : 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                 :          21 : emit_change_main (gpointer data)
      72                 :             : {
      73                 :          21 :   ChangeEmission *emission = data;
      74                 :          42 :   g_autoptr (ValentSmsStore) store = NULL;
      75                 :             : 
      76         [ +  - ]:          21 :   g_assert (emission != NULL);
      77                 :             : 
      78                 :          21 :   g_rec_mutex_lock (&emission->mutex);
      79         [ +  - ]:          21 :   if ((store = g_weak_ref_get (&emission->store)) != NULL)
      80                 :             :     {
      81                 :          21 :       g_signal_emit (G_OBJECT (store),
      82                 :             :                      emission->signal_id, 0,
      83                 :             :                      emission->message);
      84                 :             :     }
      85                 :             : 
      86                 :          21 :   g_weak_ref_clear (&emission->store);
      87         [ +  - ]:          21 :   g_clear_object (&emission->message);
      88                 :          21 :   g_rec_mutex_unlock (&emission->mutex);
      89                 :          21 :   g_rec_mutex_clear (&emission->mutex);
      90                 :          21 :   g_clear_pointer (&emission, g_free);
      91                 :             : 
      92         [ +  - ]:          21 :   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                 :          39 : task_closure_free (gpointer data)
     114                 :             : {
     115                 :          78 :   g_autofree TaskClosure *closure = data;
     116                 :             : 
     117         [ +  - ]:          39 :   g_clear_object (&closure->task);
     118                 :          39 :   g_clear_pointer (&closure, g_free);
     119                 :          39 : }
     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                 :           9 :   g_autoptr (GAsyncQueue) tasks = data;
     141                 :           7 :   TaskClosure *closure = NULL;
     142                 :             : 
     143         [ +  - ]:          44 :   while ((closure = g_async_queue_pop (tasks)))
     144                 :             :     {
     145                 :          44 :       unsigned int mode = closure->task_mode;
     146                 :             : 
     147   [ -  +  +  -  :          44 :       if (G_IS_TASK (closure->task) && !g_task_get_completed (closure->task))
          -  +  -  -  -  
                      + ]
     148                 :             :         {
     149                 :          44 :           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   [ +  -  -  - ]:          39 :           if (mode == TASK_CRITICAL && g_task_had_error (closure->task))
     155                 :             :             mode = TASK_TERMINAL;
     156                 :             :         }
     157                 :             : 
     158                 :          39 :       g_clear_pointer (&closure, task_closure_free);
     159                 :             : 
     160         [ +  + ]:          39 :       if (mode == TASK_TERMINAL)
     161                 :             :         break;
     162                 :             :     }
     163                 :             : 
     164                 :             :   /* Cancel any queued tasks */
     165                 :           2 :   g_async_queue_lock (tasks);
     166                 :             : 
     167         [ -  + ]:           4 :   while ((closure = g_async_queue_try_pop_unlocked (tasks)) != NULL)
     168                 :           2 :     g_clear_pointer (&closure, task_closure_cancel);
     169                 :             : 
     170                 :           2 :   g_async_queue_unlock (tasks);
     171                 :             : 
     172         [ +  - ]:           2 :   return NULL;
     173                 :             : }
     174                 :             : 
     175                 :             : /*
     176                 :             :  * Step functions
     177                 :             :  */
     178                 :             : static inline ValentMessage *
     179                 :          34 : valent_sms_store_get_message_step (sqlite3_stmt  *stmt,
     180                 :             :                                    GError       **error)
     181                 :             : {
     182                 :          68 :   g_autoptr (GVariant) metadata = NULL;
     183                 :          34 :   const char *metadata_str;
     184                 :          34 :   int rc;
     185                 :             : 
     186         [ +  - ]:          34 :   g_assert (stmt != NULL);
     187   [ +  -  -  + ]:          34 :   g_assert (error == NULL || *error == NULL);
     188                 :             : 
     189         [ +  + ]:          34 :   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                 :          18 : valent_sms_store_set_message_step (sqlite3_stmt   *stmt,
     218                 :             :                                    ValentMessage  *message,
     219                 :             :                                    GError        **error)
     220                 :             : {
     221                 :          18 :   int rc;
     222                 :          18 :   ValentMessageBox box;
     223                 :          18 :   int64_t date;
     224                 :          18 :   int64_t id;
     225                 :          18 :   GVariant *metadata;
     226                 :          18 :   gboolean read;
     227                 :          18 :   const char *sender;
     228                 :          18 :   const char *text;
     229                 :          18 :   int64_t thread_id;
     230                 :          36 :   g_autofree char *metadata_str = NULL;
     231                 :             : 
     232                 :             :   /* Extract the message data */
     233                 :          18 :   box = valent_message_get_box (message);
     234                 :          18 :   date = valent_message_get_date (message);
     235                 :          18 :   id = valent_message_get_id (message);
     236                 :          18 :   metadata = valent_message_get_metadata (message);
     237                 :          18 :   read = valent_message_get_read (message);
     238                 :          18 :   sender = valent_message_get_sender (message);
     239                 :          18 :   text = valent_message_get_text (message);
     240                 :          18 :   thread_id = valent_message_get_thread_id (message);
     241                 :             : 
     242         [ +  - ]:          18 :   if (metadata != NULL)
     243                 :          18 :     metadata_str = g_variant_print (metadata, TRUE);
     244                 :             : 
     245                 :             :   /* Bind the message data */
     246                 :          18 :   sqlite3_bind_int (stmt, 1, box);
     247                 :          18 :   sqlite3_bind_int64 (stmt, 2, date);
     248                 :          18 :   sqlite3_bind_int64 (stmt, 3, id);
     249                 :          18 :   sqlite3_bind_text (stmt, 4, metadata_str, -1, NULL);
     250                 :          18 :   sqlite3_bind_int (stmt, 5, read);
     251                 :          18 :   sqlite3_bind_text (stmt, 6, sender, -1, NULL);
     252                 :          18 :   sqlite3_bind_text (stmt, 7, text, -1, NULL);
     253                 :          18 :   sqlite3_bind_int64 (stmt, 8, thread_id);
     254                 :             : 
     255                 :             :   /* Execute and auto-reset */
     256         [ +  + ]:          18 :   if ((rc = sqlite3_step (stmt)) != SQLITE_DONE)
     257                 :             :     {
     258                 :           1 :       g_set_error (error,
     259                 :             :                    G_IO_ERROR,
     260                 :             :                    G_IO_ERROR_FAILED,
     261                 :             :                    "%s: %s", G_STRFUNC, sqlite3_errstr (rc));
     262                 :           1 :       sqlite3_reset (stmt);
     263                 :           1 :       return FALSE;
     264                 :             :     }
     265                 :             : 
     266                 :          17 :   sqlite3_reset (stmt);
     267                 :          17 :   return TRUE;
     268                 :             : }
     269                 :             : 
     270                 :             : static gboolean
     271                 :          30 : valent_sms_store_return_error_if_closed (GTask          *task,
     272                 :             :                                          ValentSmsStore *self)
     273                 :             : {
     274   [ +  -  +  -  :          30 :   g_assert (G_IS_TASK (task));
             -  +  -  - ]
     275         [ -  + ]:          30 :   g_assert (VALENT_IS_SMS_STORE (self));
     276                 :             : 
     277         [ -  + ]:          30 :   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                 :          21 : update_hook (gpointer       user_data,
     295                 :             :              int            event,
     296                 :             :              char const    *database,
     297                 :             :              char const    *table,
     298                 :             :              sqlite3_int64  rowid)
     299                 :             : {
     300                 :          21 :   ValentSmsStore *self = VALENT_SMS_STORE (user_data);
     301                 :          21 :   sqlite3_stmt *stmt = self->stmts[STMT_GET_MESSAGE];
     302                 :          21 :   g_autoptr (ValentMessage) message = NULL;
     303   [ +  -  -  - ]:          21 :   g_autoptr (GError) error = NULL;
     304                 :             : 
     305         [ +  - ]:          21 :   g_assert (VALENT_IS_SMS_STORE (self));
     306         [ -  + ]:          21 :   g_assert (!VALENT_IS_MAIN_THREAD ());
     307                 :             : 
     308         [ +  - ]:          21 :   if G_UNLIKELY (g_strcmp0 (table, "message") != 0)
     309                 :             :     return;
     310                 :             : 
     311         [ +  + ]:          21 :   if (event != SQLITE_DELETE)
     312                 :             :     {
     313                 :          18 :       sqlite3_bind_int64 (stmt, 1, rowid);
     314                 :          18 :       message = valent_sms_store_get_message_step (stmt, &error);
     315                 :          18 :       sqlite3_reset (stmt);
     316                 :             :     }
     317                 :             : 
     318         [ +  - ]:          21 :   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         [ +  + ]:          21 :   if (message == NULL)
     326                 :             :     {
     327                 :           8 :       message = g_object_new (VALENT_TYPE_MESSAGE,
     328                 :             :                               "id", rowid,
     329                 :             :                               NULL);
     330                 :             :     }
     331                 :             : 
     332   [ +  +  +  - ]:          21 :   switch (event)
     333                 :             :     {
     334                 :          16 :     case SQLITE_INSERT:
     335                 :          16 :       valent_sms_store_message_added (self, message);
     336                 :          16 :       break;
     337                 :             : 
     338                 :           2 :     case SQLITE_UPDATE:
     339                 :           2 :       valent_sms_store_message_changed (self, message);
     340                 :           2 :       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         [ +  + ]:           3 :   if (rc != SQLITE_OK)
     461                 :             :     {
     462                 :           2 :       g_debug ("sqlite3_exec(): \"%s\": [%i] %s",
     463                 :             :                "PRAGMA optimize;", rc, sqlite3_errstr (rc));
     464                 :             :     }
     465                 :             : 
     466                 :             :   /* Close the connection */
     467         [ -  + ]:           3 :   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                 :           2 :   self->connection = NULL;
     478                 :           2 :   g_task_return_boolean (task, TRUE);
     479                 :             : }
     480                 :             : 
     481                 :             : static void
     482                 :           9 : add_messages_task (GTask        *task,
     483                 :             :                    gpointer      source_object,
     484                 :             :                    gpointer      task_data,
     485                 :             :                    GCancellable *cancellable)
     486                 :             : {
     487                 :           9 :   ValentSmsStore *self = VALENT_SMS_STORE (source_object);
     488                 :           9 :   GPtrArray *messages = task_data;
     489                 :           9 :   sqlite3_stmt *stmt = self->stmts[STMT_ADD_MESSAGE];
     490                 :           9 :   unsigned int n_messages = messages->len;
     491                 :           9 :   GError *error = NULL;
     492                 :             : 
     493         [ +  - ]:           9 :   if (g_task_return_error_if_cancelled (task))
     494                 :           1 :     return;
     495                 :             : 
     496         [ +  - ]:           9 :   if (valent_sms_store_return_error_if_closed (task, self))
     497                 :             :     return;
     498                 :             : 
     499         [ +  + ]:          26 :   for (unsigned int i = 0; i < n_messages; i++)
     500                 :             :     {
     501                 :          18 :       ValentMessage *message = g_ptr_array_index (messages, i);
     502                 :             : 
     503                 :             :       /* Iterate the results stopping on error to mark the point of failure */
     504         [ +  + ]:          18 :       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         [ +  + ]:           9 :   if (n_messages < messages->len)
     513                 :           1 :     g_ptr_array_remove_range (messages, n_messages, messages->len - n_messages);
     514                 :             : 
     515         [ +  + ]:           9 :   if (error != NULL)
     516                 :           1 :     return g_task_return_error (task, error);
     517                 :             : 
     518                 :           8 :   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                 :           5 : get_thread_date_task (GTask        *task,
     702                 :             :                       gpointer      source_object,
     703                 :             :                       gpointer      task_data,
     704                 :             :                       GCancellable *cancellable)
     705                 :             : {
     706                 :           5 :   ValentSmsStore *self = VALENT_SMS_STORE (source_object);
     707                 :           5 :   int64_t *thread_id = task_data;
     708                 :           5 :   sqlite3_stmt *stmt = self->stmts[STMT_GET_THREAD_DATE];
     709                 :           5 :   int64_t date = 0;
     710                 :           5 :   int rc;
     711                 :             : 
     712         [ +  - ]:           5 :   if (g_task_return_error_if_cancelled (task))
     713                 :             :     return;
     714                 :             : 
     715         [ +  - ]:           5 :   if (valent_sms_store_return_error_if_closed (task, self))
     716                 :             :     return;
     717                 :             : 
     718                 :           5 :   sqlite3_bind_int64 (stmt, 1, *thread_id);
     719                 :             : 
     720         [ +  + ]:           5 :   if ((rc = sqlite3_step (stmt)) == SQLITE_ROW)
     721                 :           3 :     date = sqlite3_column_int64 (stmt, 0);
     722                 :             : 
     723                 :           5 :   sqlite3_reset (stmt);
     724                 :             : 
     725         [ -  + ]:           5 :   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                 :           5 :   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                 :          37 : valent_sms_store_push (ValentSmsStore  *self,
     795                 :             :                        GTask           *task,
     796                 :             :                        GTaskThreadFunc  task_func)
     797                 :             : {
     798                 :          37 :   TaskClosure *closure = NULL;
     799                 :             : 
     800         [ -  + ]:          37 :   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                 :          37 :   closure = g_new0 (TaskClosure, 1);
     810                 :          37 :   closure->task = g_object_ref (task);
     811                 :          37 :   closure->task_func = task_func;
     812                 :          37 :   closure->task_mode = TASK_DEFAULT;
     813                 :          37 :   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                 :           8 : valent_sms_store_add_messages (ValentSmsStore      *store,
    1064                 :             :                                GPtrArray           *messages,
    1065                 :             :                                GCancellable        *cancellable,
    1066                 :             :                                GAsyncReadyCallback  callback,
    1067                 :             :                                gpointer             user_data)
    1068                 :             : {
    1069                 :          16 :   g_autoptr (GTask) task = NULL;
    1070                 :             : 
    1071         [ +  - ]:           8 :   g_return_if_fail (VALENT_IS_SMS_STORE (store));
    1072         [ -  + ]:           8 :   g_return_if_fail (messages != NULL);
    1073                 :             : 
    1074                 :           8 :   task = g_task_new (store, cancellable, callback, user_data);
    1075         [ +  - ]:           8 :   g_task_set_source_tag (task, valent_sms_store_add_message);
    1076                 :           8 :   g_task_set_task_data (task,
    1077                 :           8 :                         g_ptr_array_ref (messages),
    1078                 :             :                         (GDestroyNotify)g_ptr_array_unref);
    1079         [ +  - ]:           8 :   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                 :           5 : valent_sms_store_get_thread_date (ValentSmsStore *store,
    1380                 :             :                                   int64_t         thread_id)
    1381                 :             : {
    1382                 :          10 :   g_autoptr (GTask) task = NULL;
    1383         [ +  - ]:           5 :   g_autoptr (GError) error = NULL;
    1384                 :           5 :   int64_t date = 0;
    1385                 :             : 
    1386         [ +  - ]:           5 :   g_return_val_if_fail (VALENT_IS_SMS_STORE (store), 0);
    1387         [ -  + ]:           5 :   g_return_val_if_fail (thread_id >= 0, 0);
    1388                 :             : 
    1389                 :           5 :   task = g_task_new (store, NULL, NULL, NULL);
    1390         [ +  - ]:           5 :   g_task_set_source_tag (task, valent_sms_store_get_thread_date);
    1391                 :           5 :   g_task_set_task_data (task, &thread_id, NULL);
    1392                 :           5 :   valent_sms_store_push (store, task, get_thread_date_task);
    1393                 :             : 
    1394         [ +  + ]:         950 :   while (!g_task_get_completed (task))
    1395                 :         945 :     g_main_context_iteration (NULL, FALSE);
    1396                 :             : 
    1397                 :           5 :   date = g_task_propagate_int (task, &error);
    1398                 :             : 
    1399         [ -  + ]:           5 :   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                 :          16 : valent_sms_store_message_added (ValentSmsStore *store,
    1449                 :             :                                 ValentMessage  *message)
    1450                 :             : {
    1451                 :          16 :   ChangeEmission *emission;
    1452                 :             : 
    1453         [ +  - ]:          16 :   g_return_if_fail (VALENT_IS_SMS_STORE (store));
    1454         [ -  + ]:          16 :   g_return_if_fail (VALENT_IS_MESSAGE (message));
    1455                 :             : 
    1456         [ +  - ]:          16 :   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                 :          16 :   emission = g_new0 (ChangeEmission, 1);
    1463                 :          16 :   g_rec_mutex_init (&emission->mutex);
    1464                 :          16 :   g_rec_mutex_lock (&emission->mutex);
    1465                 :          16 :   g_weak_ref_init (&emission->store, store);
    1466                 :          16 :   emission->message = g_object_ref (message);
    1467                 :          16 :   emission->signal_id = signals [MESSAGE_ADDED];
    1468                 :          16 :   g_rec_mutex_unlock (&emission->mutex);
    1469                 :             : 
    1470                 :          16 :   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                 :           2 : valent_sms_store_message_changed (ValentSmsStore *store,
    1531                 :             :                                   ValentMessage  *message)
    1532                 :             : {
    1533                 :           2 :   ChangeEmission *emission;
    1534                 :             : 
    1535         [ +  - ]:           2 :   g_return_if_fail (VALENT_IS_SMS_STORE (store));
    1536         [ -  + ]:           2 :   g_return_if_fail (VALENT_IS_MESSAGE (message));
    1537                 :             : 
    1538         [ +  - ]:           2 :   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                 :           2 :   emission = g_new0 (ChangeEmission, 1);
    1545                 :           2 :   g_rec_mutex_init (&emission->mutex);
    1546                 :           2 :   g_rec_mutex_lock (&emission->mutex);
    1547                 :           2 :   g_weak_ref_init (&emission->store, store);
    1548                 :           2 :   emission->message = g_object_ref (message);
    1549                 :           2 :   emission->signal_id = signals [MESSAGE_CHANGED];
    1550                 :           2 :   g_rec_mutex_unlock (&emission->mutex);
    1551                 :             : 
    1552                 :           2 :   g_idle_add_full (G_PRIORITY_DEFAULT,
    1553                 :             :                    emit_change_main,
    1554                 :             :                    g_steal_pointer (&emission),
    1555                 :             :                    NULL);
    1556                 :             : }
    1557                 :             : 
        

Generated by: LCOV version 2.0-1