LCOV - code coverage report
Current view: top level - src/plugins/contacts - valent-contacts-device.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 96.4 % 137 132
Test Date: 2024-12-02 22:49:30 Functions: 100.0 % 14 14
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 60.0 % 70 42

             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-contacts-device"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <inttypes.h>
       9                 :             : 
      10                 :             : #include <gio/gio.h>
      11                 :             : #include <libebook-contacts/libebook-contacts.h>
      12                 :             : #include <libtracker-sparql/tracker-sparql.h>
      13                 :             : #include <valent.h>
      14                 :             : 
      15                 :             : #include "valent-contacts-device.h"
      16                 :             : 
      17                 :             : struct _ValentContactsDevice
      18                 :             : {
      19                 :             :   ValentContactsAdapter    parent_instance;
      20                 :             : 
      21                 :             :   char                    *default_iri;
      22                 :             :   TrackerSparqlStatement  *get_timestamp_stmt;
      23                 :             :   GCancellable            *cancellable;
      24                 :             : };
      25                 :             : 
      26   [ +  +  +  - ]:          17 : G_DEFINE_FINAL_TYPE (ValentContactsDevice, valent_contacts_device, VALENT_TYPE_CONTACTS_ADAPTER)
      27                 :             : 
      28                 :             : static void
      29                 :           1 : execute_add_contacts_cb (TrackerSparqlConnection *connection,
      30                 :             :                          GAsyncResult            *result,
      31                 :             :                          gpointer                 user_data)
      32                 :             : {
      33                 :           2 :   g_autoptr (GError) error = NULL;
      34                 :             : 
      35   [ -  +  -  - ]:           1 :   if (!tracker_sparql_connection_update_resource_finish (connection, result, &error) &&
      36                 :           0 :       !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
      37                 :             :     {
      38                 :           0 :       g_debug ("%s(): %s", G_STRFUNC, error->message);
      39                 :             :     }
      40                 :           1 : }
      41                 :             : 
      42                 :             : /*
      43                 :             :  * ValentContactsAdapter
      44                 :             :  */
      45                 :             : static void
      46                 :           5 : valent_contacts_device_send_packet_cb (ValentDevice *device,
      47                 :             :                                        GAsyncResult *result,
      48                 :             :                                        gpointer      user_data)
      49                 :             : {
      50                 :          10 :   g_autoptr (GError) error = NULL;
      51                 :             : 
      52         [ +  + ]:           5 :   if (!valent_device_send_packet_finish (device, result, &error))
      53                 :             :     {
      54         [ -  + ]:           1 :       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
      55                 :           0 :         g_critical ("%s(): %s", G_STRFUNC, error->message);
      56         [ -  + ]:           1 :       else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_CONNECTED))
      57                 :           0 :         g_warning ("%s(): %s", G_STRFUNC, error->message);
      58         [ +  - ]:           1 :       else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
      59                 :           1 :         g_debug ("%s(): %s", G_STRFUNC, error->message);
      60                 :             :     }
      61                 :           5 : }
      62                 :             : 
      63                 :             : static void
      64                 :           1 : valent_contacts_device_handle_response_uids_timestamps (ValentContactsDevice *self,
      65                 :             :                                                         JsonNode             *packet)
      66                 :             : {
      67                 :           2 :   g_autoptr (JsonNode) request = NULL;
      68         [ +  - ]:           1 :   g_autoptr (JsonBuilder) builder = NULL;
      69                 :           1 :   JsonObjectIter iter;
      70                 :           1 :   const char *uid;
      71                 :           1 :   JsonNode *node;
      72                 :           1 :   unsigned int n_requested = 0;
      73                 :             : 
      74                 :           1 :   VALENT_ENTRY;
      75                 :             : 
      76         [ +  - ]:           1 :   g_assert (VALENT_IS_CONTACTS_DEVICE (self));
      77                 :             : 
      78                 :           1 :   valent_packet_init (&builder, "kdeconnect.contacts.request_vcards_by_uid");
      79                 :           1 :   json_builder_set_member_name (builder, "uids");
      80                 :           1 :   json_builder_begin_array (builder);
      81                 :             : 
      82                 :           1 :   json_object_iter_init (&iter, valent_packet_get_body (packet));
      83         [ +  + ]:           5 :   while (json_object_iter_next (&iter, &uid, &node))
      84                 :             :     {
      85                 :           4 :       int64_t timestamp = 0;
      86                 :             : 
      87                 :             :       // skip the "uids" array
      88         [ +  + ]:           4 :       if G_UNLIKELY (g_str_equal ("uids", uid))
      89                 :           1 :         continue;
      90                 :             : 
      91         [ +  - ]:           3 :       if G_LIKELY (json_node_get_value_type (node) == G_TYPE_INT64)
      92                 :           3 :         timestamp = json_node_get_int (node);
      93                 :             : 
      94                 :             :       // TODO
      95         [ -  + ]:           3 :       if (timestamp != 0)
      96                 :             :         {
      97                 :           3 :           json_builder_add_string_value (builder, uid);
      98                 :           3 :           n_requested++;
      99                 :             :         }
     100                 :             :     }
     101                 :             : 
     102                 :           1 :   json_builder_end_array (builder);
     103                 :           1 :   request = valent_packet_end (&builder);
     104                 :             : 
     105         [ +  - ]:           1 :   if (n_requested > 0)
     106                 :             :     {
     107                 :           1 :       ValentDevice *device = NULL;
     108                 :             : 
     109                 :           1 :       device = valent_extension_get_object (VALENT_EXTENSION (self));
     110                 :           1 :       valent_device_send_packet (device,
     111                 :             :                                  request,
     112                 :             :                                  NULL,
     113                 :             :                                  (GAsyncReadyCallback) valent_contacts_device_send_packet_cb,
     114                 :             :                                  NULL);
     115                 :             :     }
     116                 :             : 
     117         [ -  + ]:           1 :   VALENT_EXIT;
     118                 :             : }
     119                 :             : 
     120                 :             : static void
     121                 :           1 : valent_contacts_device_handle_response_vcards (ValentContactsDevice *self,
     122                 :             :                                                JsonNode             *packet)
     123                 :             : {
     124                 :           2 :   g_autoptr (TrackerSparqlConnection) connection = NULL;
     125         [ +  - ]:           1 :   g_autoptr (TrackerResource) list_resource = NULL;
     126                 :           1 :   ValentDevice *device;
     127                 :           1 :   const char *list_name;
     128                 :           1 :   JsonObjectIter iter;
     129                 :           1 :   const char *uid;
     130                 :           1 :   JsonNode *node;
     131                 :             : 
     132                 :           1 :   VALENT_ENTRY;
     133                 :             : 
     134         [ +  - ]:           1 :   g_assert (VALENT_IS_CONTACTS_DEVICE (self));
     135         [ -  + ]:           1 :   g_assert (VALENT_IS_PACKET (packet));
     136                 :             : 
     137                 :           1 :   device = valent_extension_get_object (VALENT_EXTENSION (self));
     138                 :           1 :   list_name = valent_device_get_name (device);
     139                 :             : 
     140                 :           1 :   list_resource = tracker_resource_new (self->default_iri);
     141                 :           1 :   tracker_resource_set_uri (list_resource, "rdf:type", "nco:ContactList");
     142                 :           1 :   tracker_resource_set_string (list_resource, "nie:title", list_name);
     143                 :             : 
     144                 :           1 :   json_object_iter_init (&iter, valent_packet_get_body (packet));
     145         [ +  + ]:           6 :   while (json_object_iter_next (&iter, &uid, &node))
     146                 :             :     {
     147                 :           4 :       TrackerResource *item_resource = NULL;
     148                 :           6 :       g_autoptr (EContact) contact = NULL;
     149                 :           4 :       const char *vcard;
     150                 :             : 
     151                 :             :       /* NOTE: This has the side-effect of ignoring `uids` array
     152                 :             :        */
     153         [ +  + ]:           4 :       if G_UNLIKELY (json_node_get_value_type (node) != G_TYPE_STRING)
     154                 :           1 :         continue;
     155                 :             : 
     156                 :           3 :       vcard = json_node_get_string (node);
     157                 :           3 :       contact = e_contact_new_from_vcard_with_uid (vcard, uid);
     158                 :           3 :       item_resource = valent_contact_resource_from_econtact (contact);
     159         [ +  - ]:           3 :       if (item_resource != NULL)
     160                 :             :         {
     161                 :           3 :           g_autofree char *item_urn = NULL;
     162                 :             : 
     163                 :           3 :           item_urn = tracker_sparql_escape_uri_printf ("%s:%s",
     164                 :             :                                                        self->default_iri,
     165                 :             :                                                        uid);
     166                 :           3 :           tracker_resource_set_identifier (item_resource, item_urn);
     167                 :           3 :           tracker_resource_add_take_relation (list_resource,
     168                 :             :                                               "nco:containsContact",
     169                 :           3 :                                               g_steal_pointer (&item_resource));
     170                 :             :         }
     171                 :             :     }
     172                 :             : 
     173                 :           1 :   g_object_get (self, "connection", &connection, NULL);
     174                 :           1 :   tracker_sparql_connection_update_resource_async (connection,
     175                 :             :                                                    VALENT_CONTACTS_GRAPH,
     176                 :             :                                                    list_resource,
     177                 :             :                                                    self->cancellable,
     178                 :             :                                                    (GAsyncReadyCallback) execute_add_contacts_cb,
     179                 :             :                                                    NULL);
     180                 :             : 
     181         [ +  - ]:           1 :   VALENT_EXIT;
     182                 :             : }
     183                 :             : 
     184                 :             : static void
     185                 :           4 : valent_contacts_device_request_all_uids_timestamps (ValentContactsDevice *self)
     186                 :             : {
     187                 :           4 :   ValentDevice *device = NULL;
     188                 :           8 :   g_autoptr (JsonNode) packet = NULL;
     189                 :             : 
     190         [ +  - ]:           4 :   g_assert (VALENT_IS_CONTACTS_DEVICE (self));
     191                 :             : 
     192                 :           4 :   device = valent_extension_get_object (VALENT_EXTENSION (self));
     193                 :           4 :   packet = valent_packet_new ("kdeconnect.contacts.request_all_uids_timestamps");
     194         [ +  - ]:           4 :   valent_device_send_packet (device,
     195                 :             :                              packet,
     196                 :             :                              NULL,
     197                 :             :                              (GAsyncReadyCallback) valent_contacts_device_send_packet_cb,
     198                 :             :                              NULL);
     199                 :           4 : }
     200                 :             : 
     201                 :             : static void
     202                 :          13 : on_device_state_changed (ValentDevice         *device,
     203                 :             :                          GParamSpec           *pspec,
     204                 :             :                          ValentContactsDevice *self)
     205                 :             : {
     206                 :          13 :   ValentDeviceState state = VALENT_DEVICE_STATE_NONE;
     207                 :          13 :   gboolean available;
     208                 :             : 
     209                 :          13 :   state = valent_device_get_state (device);
     210                 :          13 :   available = (state & VALENT_DEVICE_STATE_CONNECTED) != 0 &&
     211                 :             :               (state & VALENT_DEVICE_STATE_PAIRED) != 0;
     212                 :             : 
     213         [ +  + ]:          13 :   if (available)
     214                 :           4 :     valent_contacts_device_request_all_uids_timestamps (self);
     215                 :          13 : }
     216                 :             : 
     217                 :             : /*
     218                 :             :  * GObject
     219                 :             :  */
     220                 :             : static void
     221                 :           5 : valent_contacts_device_constructed (GObject *object)
     222                 :             : {
     223                 :           5 :   ValentContactsDevice *self = VALENT_CONTACTS_DEVICE (object);
     224                 :           5 :   ValentDevice *device = NULL;
     225                 :          10 :   g_autofree char *iri = NULL;
     226                 :             : 
     227                 :           5 :   G_OBJECT_CLASS (valent_contacts_device_parent_class)->constructed (object);
     228                 :             : 
     229                 :           5 :   device = valent_extension_get_object (VALENT_EXTENSION (self));
     230                 :           5 :   g_signal_connect_object (device,
     231                 :             :                            "notify::state",
     232                 :             :                            G_CALLBACK (on_device_state_changed),
     233                 :             :                            self,
     234                 :             :                            G_CONNECT_DEFAULT);
     235                 :             : 
     236                 :           5 :   iri = valent_object_dup_iri (VALENT_OBJECT (self));
     237                 :           5 :   self->default_iri = tracker_sparql_escape_uri_printf ("%s:default", iri);
     238                 :           5 : }
     239                 :             : 
     240                 :             : static void
     241                 :           5 : valent_contacts_device_finalize (GObject *object)
     242                 :             : {
     243                 :           5 :   ValentContactsDevice *self = VALENT_CONTACTS_DEVICE (object);
     244                 :             : 
     245         [ +  - ]:           5 :   g_clear_pointer (&self->default_iri, g_free);
     246         [ -  + ]:           5 :   g_clear_object (&self->get_timestamp_stmt);
     247                 :             : 
     248                 :           5 :   G_OBJECT_CLASS (valent_contacts_device_parent_class)->finalize (object);
     249                 :           5 : }
     250                 :             : 
     251                 :             : static void
     252                 :           2 : valent_contacts_device_class_init (ValentContactsDeviceClass *klass)
     253                 :             : {
     254                 :           2 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     255                 :             : 
     256                 :           2 :   object_class->constructed = valent_contacts_device_constructed;
     257                 :           2 :   object_class->finalize = valent_contacts_device_finalize;
     258                 :             : }
     259                 :             : 
     260                 :             : static void
     261                 :           5 : valent_contacts_device_init (ValentContactsDevice *self)
     262                 :             : {
     263                 :           5 : }
     264                 :             : 
     265                 :             : /*< private >
     266                 :             :  * valent_contacts_device_new:
     267                 :             :  * @device: a `ValentDevice`
     268                 :             :  *
     269                 :             :  * Create a new `ValentContactsDevice`.
     270                 :             :  *
     271                 :             :  * Returns: (transfer full): a new adapter
     272                 :             :  */
     273                 :             : ValentContactsAdapter *
     274                 :           5 : valent_contacts_device_new (ValentDevice *device)
     275                 :             : {
     276                 :          10 :   g_autoptr (ValentContext) context = NULL;
     277         [ +  - ]:           5 :   g_autofree char *iri = NULL;
     278                 :             : 
     279         [ +  - ]:           5 :   g_return_val_if_fail (VALENT_IS_DEVICE (device), NULL);
     280                 :             : 
     281                 :           5 :   context = valent_context_new (valent_device_get_context (device),
     282                 :             :                                 "plugin",
     283                 :             :                                 "contacts");
     284                 :           5 :   iri = tracker_sparql_escape_uri_printf ("urn:valent:contacts:%s",
     285                 :             :                                           valent_device_get_id (device));
     286                 :           5 :   return g_object_new (VALENT_TYPE_CONTACTS_DEVICE,
     287                 :             :                        "iri",     iri,
     288                 :             :                        "object",  device,
     289                 :             :                        "context", context,
     290                 :             :                        NULL);
     291                 :             : }
     292                 :             : 
     293                 :             : /*< private >
     294                 :             :  * valent_contacts_device_handle_packet:
     295                 :             :  * @adapter: a `ValentContactsAdapter`
     296                 :             :  * @type: a KDE Connect packet type
     297                 :             :  * @packet: a KDE Connect packet
     298                 :             :  *
     299                 :             :  * Handle an incoming `kdeconnect.contacts.*` packet.
     300                 :             :  */
     301                 :             : void
     302                 :           2 : valent_contacts_device_handle_packet (ValentContactsAdapter *adapter,
     303                 :             :                                       const char            *type,
     304                 :             :                                       JsonNode              *packet)
     305                 :             : {
     306                 :           2 :   ValentContactsDevice *self = VALENT_CONTACTS_DEVICE (adapter);
     307                 :             : 
     308         [ +  - ]:           2 :   g_assert (VALENT_IS_CONTACTS_DEVICE (adapter));
     309         [ -  + ]:           2 :   g_assert (type != NULL);
     310         [ -  + ]:           2 :   g_assert (VALENT_IS_PACKET (packet));
     311                 :             : 
     312                 :             :   /* A response to a request for a listing of contacts
     313                 :             :    */
     314         [ +  + ]:           2 :   if (g_str_equal (type, "kdeconnect.contacts.response_uids_timestamps"))
     315                 :           1 :     valent_contacts_device_handle_response_uids_timestamps (self, packet);
     316                 :             : 
     317                 :             :   /* A response to a request for vCards
     318                 :             :    */
     319         [ +  - ]:           1 :   else if (g_str_equal (type, "kdeconnect.contacts.response_vcards"))
     320                 :           1 :     valent_contacts_device_handle_response_vcards (self, packet);
     321                 :             : 
     322                 :             :   else
     323                 :           0 :     g_assert_not_reached ();
     324                 :           2 : }
     325                 :             : 
        

Generated by: LCOV version 2.0-1