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 % 139 134
Test Date: 2024-12-21 23:29:11 Functions: 100.0 % 14 14
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 60.0 % 80 48

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

Generated by: LCOV version 2.0-1