LCOV - code coverage report
Current view: top level - src/plugins/share - valent-share-plugin.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 77.6 % 581 451
Test Date: 2024-11-16 20:34:14 Functions: 85.3 % 34 29
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 45.1 % 364 164

             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-share-plugin"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <glib/gi18n.h>
       9                 :             : #include <gio/gio.h>
      10                 :             : #include <json-glib/json-glib.h>
      11                 :             : #include <valent.h>
      12                 :             : 
      13                 :             : #include "valent-share-plugin.h"
      14                 :             : #include "valent-share-download.h"
      15                 :             : #include "valent-share-upload.h"
      16                 :             : 
      17                 :             : 
      18                 :             : struct _ValentSharePlugin
      19                 :             : {
      20                 :             :   ValentDevicePlugin  parent_instance;
      21                 :             : 
      22                 :             :   GHashTable         *transfers;
      23                 :             :   ValentTransfer     *upload;
      24                 :             :   ValentTransfer     *download;
      25                 :             : };
      26                 :             : 
      27   [ +  +  +  - ]:         150 : G_DEFINE_FINAL_TYPE (ValentSharePlugin, valent_share_plugin, VALENT_TYPE_DEVICE_PLUGIN)
      28                 :             : 
      29                 :             : 
      30                 :             : static GFile *
      31                 :           7 : valent_share_plugin_create_download_file (ValentSharePlugin *self,
      32                 :             :                                           const char        *filename,
      33                 :             :                                           gboolean           unique)
      34                 :             : {
      35                 :           7 :   GSettings *settings;
      36                 :          14 :   g_autofree char *download_folder = NULL;
      37                 :             : 
      38         [ +  - ]:           7 :   g_return_val_if_fail (VALENT_IS_SHARE_PLUGIN (self), NULL);
      39         [ -  + ]:           7 :   g_return_val_if_fail (filename != NULL, NULL);
      40                 :             : 
      41                 :             :   /* Check for a configured download directory, returning a fallback if
      42                 :             :    * necessary, but don't save the fallback as though it were configured. */
      43                 :           7 :   settings = valent_extension_get_settings (VALENT_EXTENSION (self));
      44                 :           7 :   download_folder = g_settings_get_string (settings, "download-folder");
      45                 :             : 
      46   [ +  -  +  - ]:           7 :   if (download_folder == NULL || *download_folder == '\0')
      47                 :             :     {
      48                 :           7 :       const char *user_download = NULL;
      49                 :             : 
      50                 :           7 :       user_download = valent_get_user_directory (G_USER_DIRECTORY_DOWNLOAD);
      51                 :           7 :       g_set_str (&download_folder, user_download);
      52                 :             :     }
      53                 :             : 
      54         [ -  + ]:           7 :   if (g_mkdir_with_parents (download_folder, 0700) == -1)
      55                 :             :     {
      56                 :           0 :       int error = errno;
      57                 :             : 
      58                 :           0 :       g_critical ("%s(): creating \"%s\": %s",
      59                 :             :                   G_STRFUNC,
      60                 :             :                   download_folder,
      61                 :             :                   g_strerror (error));
      62                 :             :     }
      63                 :             : 
      64                 :           7 :   return valent_get_user_file (download_folder, filename, unique);
      65                 :             : }
      66                 :             : 
      67                 :             : /*
      68                 :             :  * File Downloads
      69                 :             :  */
      70                 :             : static void
      71                 :          10 : valent_share_download_file_notification (ValentSharePlugin *self,
      72                 :             :                                          ValentTransfer    *transfer)
      73                 :             : {
      74                 :          10 :   g_autoptr (ValentDevice) device = NULL;
      75   [ +  -  -  - ]:          10 :   g_autoptr (GNotification) notification = NULL;
      76   [ +  -  -  - ]:          10 :   g_autoptr (GIcon) icon = NULL;
      77                 :          10 :   const char *title = NULL;
      78   [ +  -  -  - ]:          10 :   g_autofree char *body = NULL;
      79                 :          10 :   g_autofree char *id = NULL;
      80                 :          10 :   unsigned int n_files = 0;
      81                 :          10 :   const char *device_name;
      82                 :          10 :   ValentTransferState state = VALENT_TRANSFER_STATE_PENDING;
      83                 :             : 
      84         [ +  - ]:          10 :   g_return_if_fail (VALENT_IS_TRANSFER (transfer));
      85         [ -  + ]:          10 :   g_return_if_fail (VALENT_IS_SHARE_DOWNLOAD (transfer));
      86                 :             : 
      87         [ +  - ]:          10 :   if ((n_files = g_list_model_get_n_items (G_LIST_MODEL (transfer))) == 0)
      88                 :             :     return;
      89                 :             : 
      90                 :          10 :   g_object_get (transfer,
      91                 :             :                 "device", &device,
      92                 :             :                 "id",     &id,
      93                 :             :                 "state",  &state,
      94                 :             :                 NULL);
      95                 :          10 :   device_name = valent_device_get_name (device);
      96                 :             : 
      97   [ +  +  -  - ]:          10 :   switch (state)
      98                 :             :     {
      99                 :           6 :     case VALENT_TRANSFER_STATE_PENDING:
     100                 :             :     case VALENT_TRANSFER_STATE_ACTIVE:
     101                 :           6 :       icon = g_themed_icon_new ("document-save-symbolic");
     102                 :           6 :       title = _("Transferring Files");
     103                 :           6 :       body = g_strdup_printf (ngettext ("Receiving one file from %1$s",
     104                 :             :                                         "Receiving %2$d files from %1$s",
     105                 :             :                                         n_files),
     106                 :             :                               device_name, n_files);
     107                 :           6 :       break;
     108                 :             : 
     109                 :           4 :     case VALENT_TRANSFER_STATE_COMPLETE:
     110                 :           4 :       icon = g_themed_icon_new ("document-save-symbolic");
     111                 :           4 :       title = _("Transfer Complete");
     112                 :           4 :       body = g_strdup_printf (ngettext ("Received one file from %1$s",
     113                 :             :                                         "Received %2$d files from %1$s",
     114                 :             :                                         n_files),
     115                 :             :                               device_name, n_files);
     116                 :           4 :       break;
     117                 :             : 
     118                 :           0 :     case VALENT_TRANSFER_STATE_FAILED:
     119                 :           0 :       icon = g_themed_icon_new ("dialog-warning-symbolic");
     120                 :           0 :       title = _("Transfer Failed");
     121                 :           0 :       body = g_strdup_printf (ngettext ("Receiving one file from %1$s",
     122                 :             :                                         "Receiving %2$d files from %1$s",
     123                 :             :                                         n_files),
     124                 :             :                               device_name, n_files);
     125                 :           0 :       break;
     126                 :             :     }
     127                 :             : 
     128                 :          10 :   notification = g_notification_new (title);
     129                 :          10 :   g_notification_set_body (notification, body);
     130                 :          10 :   g_notification_set_icon (notification, icon);
     131                 :             : 
     132         [ +  + ]:          10 :   if (state == VALENT_TRANSFER_STATE_ACTIVE)
     133                 :             :     {
     134                 :           6 :       valent_notification_add_device_button (notification,
     135                 :             :                                              device,
     136                 :           6 :                                              _("Cancel"),
     137                 :             :                                              "share.cancel",
     138                 :             :                                              g_variant_new_string (id));
     139                 :             :     }
     140         [ +  - ]:           4 :   else if (state == VALENT_TRANSFER_STATE_COMPLETE)
     141                 :             :     {
     142                 :          14 :       g_autoptr (ValentTransfer) item = NULL;
     143         [ +  - ]:           4 :       g_autoptr (GFile) file = NULL;
     144         [ +  - ]:           4 :       g_autoptr (GFile) dir = NULL;
     145         [ +  - ]:           4 :       g_autofree char *dirname = NULL;
     146                 :             : 
     147                 :           4 :       item = g_list_model_get_item (G_LIST_MODEL (transfer), 0);
     148                 :           4 :       file = valent_device_transfer_ref_file (VALENT_DEVICE_TRANSFER (item));
     149                 :           4 :       dir = g_file_get_parent (file);
     150                 :           4 :       dirname = g_file_get_uri (dir);
     151                 :             : 
     152                 :           4 :       valent_notification_add_device_button (notification,
     153                 :             :                                              device,
     154                 :           4 :                                              _("Open Folder"),
     155                 :             :                                              "share.view",
     156                 :             :                                              g_variant_new_string (dirname));
     157                 :             : 
     158         [ +  - ]:           4 :       if (n_files == 1)
     159                 :             :         {
     160                 :           4 :           g_autofree char *uri = NULL;
     161                 :             : 
     162                 :           4 :           uri = g_file_get_uri (file);
     163                 :           8 :           valent_notification_add_device_button (notification,
     164                 :             :                                                  device,
     165                 :           4 :                                                  _("Open File"),
     166                 :             :                                                  "share.view",
     167                 :             :                                                  g_variant_new_string (uri));
     168                 :             :         }
     169                 :             :     }
     170                 :             : 
     171                 :          10 :   valent_device_plugin_show_notification (VALENT_DEVICE_PLUGIN (self),
     172                 :             :                                           id,
     173                 :             :                                           notification);
     174                 :             : }
     175                 :             : 
     176                 :             : static void
     177                 :           4 : valent_share_download_file_cb (ValentTransfer *transfer,
     178                 :             :                                GAsyncResult   *result,
     179                 :             :                                gpointer        user_data)
     180                 :             : {
     181                 :           8 :   g_autoptr (ValentSharePlugin) self = VALENT_SHARE_PLUGIN (user_data);
     182                 :           4 :   g_autofree char *id = NULL;
     183                 :           4 :   g_autoptr (GError) error = NULL;
     184                 :             : 
     185         [ +  - ]:           4 :   g_assert (VALENT_IS_TRANSFER (transfer));
     186                 :             : 
     187                 :           4 :   id = valent_transfer_dup_id (transfer);
     188                 :             : 
     189   [ -  +  -  - ]:           4 :   if (valent_transfer_execute_finish (transfer, result, &error) ||
     190                 :           0 :       !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     191                 :           4 :     valent_share_download_file_notification (self, transfer);
     192                 :             :   else
     193                 :           0 :     valent_device_plugin_hide_notification (VALENT_DEVICE_PLUGIN (self), id);
     194                 :             : 
     195         [ +  + ]:           4 :   if (self->download == transfer)
     196         [ +  - ]:           3 :     g_clear_object (&self->download);
     197                 :             : 
     198         [ -  + ]:           4 :   g_hash_table_remove (self->transfers, id);
     199                 :           4 : }
     200                 :             : 
     201                 :             : /*
     202                 :             :  * File Download (Open)
     203                 :             :  */
     204                 :             : static void
     205                 :           1 : valent_share_download_open_notification (ValentSharePlugin *self,
     206                 :             :                                          ValentTransfer    *transfer)
     207                 :             : {
     208                 :           1 :   g_autoptr (ValentDevice) device = NULL;
     209   [ +  -  -  - ]:           1 :   g_autoptr (GFile) file = NULL;
     210   [ +  -  -  - ]:           1 :   g_autoptr (GNotification) notification = NULL;
     211   [ +  -  -  - ]:           1 :   g_autoptr (GIcon) icon = NULL;
     212                 :           1 :   const char *title = NULL;
     213   [ +  -  -  - ]:           1 :   g_autofree char *body = NULL;
     214                 :           1 :   g_autofree char *id = NULL;
     215                 :           1 :   g_autofree char *filename = NULL;
     216                 :           1 :   const char *device_name;
     217                 :           1 :   ValentTransferState state = VALENT_TRANSFER_STATE_PENDING;
     218                 :             : 
     219         [ +  - ]:           1 :   g_return_if_fail (VALENT_IS_TRANSFER (transfer));
     220         [ -  + ]:           1 :   g_return_if_fail (VALENT_IS_DEVICE_TRANSFER (transfer));
     221                 :             : 
     222                 :           1 :   g_object_get (transfer,
     223                 :             :                 "device", &device,
     224                 :             :                 "file",   &file,
     225                 :             :                 "id",     &id,
     226                 :             :                 "state",  &state,
     227                 :             :                 NULL);
     228                 :           1 :   device_name = valent_device_get_name (device);
     229                 :           1 :   filename = g_file_get_basename (file);
     230                 :             : 
     231   [ +  -  -  - ]:           1 :   switch (state)
     232                 :             :     {
     233                 :           1 :     case VALENT_TRANSFER_STATE_PENDING:
     234                 :             :     case VALENT_TRANSFER_STATE_ACTIVE:
     235                 :           1 :       icon = g_themed_icon_new ("document-save-symbolic");
     236                 :           1 :       title = _("Transferring File");
     237                 :           1 :       body = g_strdup_printf (_("Opening “%s” from “%s”"), filename, device_name);
     238                 :           1 :       break;
     239                 :             : 
     240                 :           0 :     case VALENT_TRANSFER_STATE_COMPLETE:
     241                 :           0 :       valent_device_plugin_hide_notification (VALENT_DEVICE_PLUGIN (self), id);
     242                 :           0 :       return;
     243                 :             : 
     244                 :           0 :     case VALENT_TRANSFER_STATE_FAILED:
     245                 :           0 :       icon = g_themed_icon_new ("dialog-warning-symbolic");
     246                 :           0 :       title = _("Transfer Failed");
     247                 :           0 :       body = g_strdup_printf (_("Opening “%s” from “%s”"), filename, device_name);
     248                 :           0 :       break;
     249                 :             :     }
     250                 :             : 
     251                 :           1 :   notification = g_notification_new (title);
     252                 :           1 :   g_notification_set_body (notification, body);
     253                 :           1 :   g_notification_set_icon (notification, icon);
     254                 :             : 
     255         [ +  - ]:           1 :   if (state == VALENT_TRANSFER_STATE_ACTIVE)
     256                 :             :     {
     257                 :           1 :       valent_notification_add_device_button (notification,
     258                 :             :                                              device,
     259                 :           1 :                                              _("Cancel"),
     260                 :             :                                              "share.cancel",
     261                 :             :                                              g_variant_new_string (id));
     262                 :             :     }
     263                 :             : 
     264                 :           1 :   valent_device_plugin_show_notification (VALENT_DEVICE_PLUGIN (self),
     265                 :             :                                           id,
     266                 :             :                                           notification);
     267                 :             : }
     268                 :             : 
     269                 :             : static void
     270                 :           1 : valent_share_download_open_cb (ValentTransfer *transfer,
     271                 :             :                                GAsyncResult   *result,
     272                 :             :                                gpointer        user_data)
     273                 :             : {
     274                 :           2 :   g_autoptr (ValentSharePlugin) self = VALENT_SHARE_PLUGIN (user_data);
     275                 :           1 :   g_autofree char *id = NULL;
     276                 :           1 :   g_autoptr (GError) error = NULL;
     277                 :             : 
     278         [ +  - ]:           1 :   g_assert (VALENT_IS_TRANSFER (transfer));
     279                 :             : 
     280                 :           1 :   id = valent_transfer_dup_id (transfer);
     281                 :             : 
     282         [ +  - ]:           1 :   if (valent_transfer_execute_finish (transfer, result, &error))
     283                 :             :     {
     284                 :           2 :       g_autoptr (GFile) file = NULL;
     285         [ +  - ]:           1 :       g_autofree char *uri = NULL;
     286                 :             : 
     287                 :           1 :       file = valent_device_transfer_ref_file (VALENT_DEVICE_TRANSFER (transfer));
     288                 :           1 :       uri = g_file_get_uri (file);
     289                 :             : 
     290                 :           1 :       g_app_info_launch_default_for_uri_async (uri, NULL, NULL, NULL, NULL);
     291                 :           1 :       valent_device_plugin_hide_notification (VALENT_DEVICE_PLUGIN (self), id);
     292                 :             :     }
     293         [ #  # ]:           0 :   else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     294                 :             :     {
     295                 :           0 :       valent_share_download_open_notification (self, transfer);
     296                 :             :     }
     297                 :             : 
     298         [ -  + ]:           1 :   g_hash_table_remove (self->transfers, id);
     299                 :           1 : }
     300                 :             : 
     301                 :             : /*
     302                 :             :  * File Upload (Open)
     303                 :             :  */
     304                 :             : static void
     305                 :           2 : valent_share_upload_open_notification (ValentSharePlugin *self,
     306                 :             :                                        ValentTransfer    *transfer)
     307                 :             : {
     308                 :           2 :   g_autoptr (ValentDevice) device = NULL;
     309   [ +  -  -  - ]:           2 :   g_autoptr (GFile) file = NULL;
     310   [ +  -  -  - ]:           2 :   g_autoptr (GNotification) notification = NULL;
     311   [ +  -  -  - ]:           2 :   g_autoptr (GIcon) icon = NULL;
     312                 :           2 :   const char *title = NULL;
     313   [ +  -  -  - ]:           2 :   g_autofree char *body = NULL;
     314                 :           2 :   g_autofree char *id = NULL;
     315                 :           2 :   g_autofree char *filename = NULL;
     316                 :           2 :   const char *device_name;
     317                 :           2 :   ValentTransferState state = VALENT_TRANSFER_STATE_PENDING;
     318                 :             : 
     319         [ +  - ]:           2 :   g_return_if_fail (VALENT_IS_TRANSFER (transfer));
     320         [ -  + ]:           2 :   g_return_if_fail (VALENT_IS_DEVICE_TRANSFER (transfer));
     321                 :             : 
     322                 :           2 :   g_object_get (transfer,
     323                 :             :                 "device", &device,
     324                 :             :                 "file",   &file,
     325                 :             :                 "id",     &id,
     326                 :             :                 "state",  &state,
     327                 :             :                 NULL);
     328                 :           2 :   device_name = valent_device_get_name (device);
     329                 :           2 :   filename = g_file_get_basename (file);
     330                 :             : 
     331   [ +  +  -  - ]:           2 :   switch (state)
     332                 :             :     {
     333                 :           1 :     case VALENT_TRANSFER_STATE_PENDING:
     334                 :             :     case VALENT_TRANSFER_STATE_ACTIVE:
     335                 :           1 :       icon = g_themed_icon_new ("document-send-symbolic");
     336                 :           1 :       title = _("Transferring File");
     337                 :           1 :       body = g_strdup_printf (_("Opening “%s” on “%s”"), filename, device_name);
     338                 :           1 :       break;
     339                 :             : 
     340                 :           1 :     case VALENT_TRANSFER_STATE_COMPLETE:
     341                 :           1 :       icon = g_themed_icon_new ("document-send-symbolic");
     342                 :           1 :       title = _("Transfer Complete");
     343                 :           1 :       body = g_strdup_printf (_("Opened “%s” on “%s”"), filename, device_name);
     344                 :           1 :       break;
     345                 :             : 
     346                 :           0 :     case VALENT_TRANSFER_STATE_FAILED:
     347                 :           0 :       icon = g_themed_icon_new ("dialog-warning-symbolic");
     348                 :           0 :       title = _("Transfer Failed");
     349                 :           0 :       body = g_strdup_printf (_("Opening “%s” on “%s”"), filename, device_name);
     350                 :           0 :       break;
     351                 :             :     }
     352                 :             : 
     353                 :           2 :   notification = g_notification_new (title);
     354                 :           2 :   g_notification_set_body (notification, body);
     355                 :           2 :   g_notification_set_icon (notification, icon);
     356                 :             : 
     357         [ +  + ]:           2 :   if (state == VALENT_TRANSFER_STATE_ACTIVE)
     358                 :             :     {
     359                 :           1 :       valent_notification_add_device_button (notification,
     360                 :             :                                              device,
     361                 :           1 :                                              _("Cancel"),
     362                 :             :                                              "share.cancel",
     363                 :             :                                              g_variant_new_string (id));
     364                 :             :     }
     365                 :             : 
     366                 :           2 :   valent_device_plugin_show_notification (VALENT_DEVICE_PLUGIN (self),
     367                 :             :                                           id,
     368                 :             :                                           notification);
     369                 :             : }
     370                 :             : 
     371                 :             : static void
     372                 :           1 : valent_share_upload_open_cb (ValentTransfer *transfer,
     373                 :             :                              GAsyncResult   *result,
     374                 :             :                              gpointer        user_data)
     375                 :             : {
     376                 :           2 :   g_autoptr (ValentSharePlugin) self = VALENT_SHARE_PLUGIN (user_data);
     377                 :           1 :   g_autofree char *id = NULL;
     378                 :           1 :   g_autoptr (GError) error = NULL;
     379                 :             : 
     380         [ +  - ]:           1 :   g_assert (VALENT_IS_DEVICE_TRANSFER (transfer));
     381         [ -  + ]:           1 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
     382                 :             : 
     383                 :           1 :   id = valent_transfer_dup_id (transfer);
     384                 :             : 
     385   [ -  +  -  - ]:           1 :   if (valent_transfer_execute_finish (transfer, result, &error) ||
     386                 :           0 :       !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     387                 :           1 :     valent_share_upload_open_notification (self, transfer);
     388                 :             :   else
     389                 :           0 :     valent_device_plugin_hide_notification (VALENT_DEVICE_PLUGIN (self), id);
     390                 :             : 
     391         [ -  + ]:           1 :   g_hash_table_remove (self->transfers, id);
     392                 :           1 : }
     393                 :             : 
     394                 :             : static void
     395                 :           1 : valent_share_plugin_open_file (ValentSharePlugin *self,
     396                 :             :                                GFile             *file)
     397                 :             : {
     398                 :           1 :   ValentDevice *device = NULL;
     399                 :           2 :   g_autoptr (ValentTransfer) transfer = NULL;
     400         [ +  - ]:           1 :   g_autofree char *filename = NULL;
     401                 :           1 :   g_autoptr (JsonBuilder) builder = NULL;
     402         [ -  + ]:           1 :   g_autoptr (JsonNode) packet = NULL;
     403                 :             : 
     404                 :           1 :   filename = g_file_get_basename (file);
     405                 :             : 
     406                 :           1 :   valent_packet_init (&builder, "kdeconnect.share.request");
     407                 :           1 :   json_builder_set_member_name (builder, "filename");
     408                 :           1 :   json_builder_add_string_value (builder, filename);
     409                 :           1 :   json_builder_set_member_name (builder, "open");
     410                 :           1 :   json_builder_add_boolean_value (builder, TRUE);
     411                 :           1 :   packet = valent_packet_end (&builder);
     412                 :             : 
     413                 :             :   /* File uploads that request to be opened are sent as discrete transfers
     414                 :             :    * because the remote client (i.e. kdeconnect-android) may download them
     415                 :             :    * discretely. Otherwise the remote client may get confused by the
     416                 :             :    * `numberOfFiles` field and consider a concurrent multi-file transfer as
     417                 :             :    * incomplete.
     418                 :             :    */
     419                 :           1 :   device = valent_extension_get_object (VALENT_EXTENSION (self));
     420                 :           1 :   transfer = valent_device_transfer_new (device, packet, file);
     421                 :           2 :   g_hash_table_insert (self->transfers,
     422                 :           1 :                        valent_transfer_dup_id (transfer),
     423                 :             :                        g_object_ref (transfer));
     424                 :             : 
     425                 :           1 :   valent_transfer_execute (transfer,
     426                 :             :                            NULL,
     427                 :             :                            (GAsyncReadyCallback)valent_share_upload_open_cb,
     428                 :             :                            g_object_ref (self));
     429         [ +  - ]:           1 :   valent_share_upload_open_notification (self, transfer);
     430                 :           1 : }
     431                 :             : 
     432                 :             : /*
     433                 :             :  * File Uploads
     434                 :             :  */
     435                 :             : static void
     436                 :           7 : valent_share_upload_file_notification (ValentSharePlugin *self,
     437                 :             :                                        ValentTransfer    *transfer)
     438                 :             : {
     439                 :           7 :   g_autoptr (ValentDevice) device = NULL;
     440   [ +  -  -  - ]:           7 :   g_autoptr (GNotification) notification = NULL;
     441   [ +  -  -  - ]:           7 :   g_autoptr (GIcon) icon = NULL;
     442                 :           7 :   const char *title = NULL;
     443   [ +  -  -  - ]:           7 :   g_autofree char *body = NULL;
     444                 :           7 :   g_autofree char *id = NULL;
     445                 :           7 :   unsigned int n_files = 0;
     446                 :           7 :   const char *device_name;
     447                 :           7 :   ValentTransferState state = VALENT_TRANSFER_STATE_PENDING;
     448                 :             : 
     449         [ +  - ]:           7 :   g_return_if_fail (VALENT_IS_TRANSFER (transfer));
     450         [ -  + ]:           7 :   g_return_if_fail (VALENT_IS_SHARE_UPLOAD (transfer));
     451                 :             : 
     452         [ +  - ]:           7 :   if ((n_files = g_list_model_get_n_items (G_LIST_MODEL (transfer))) == 0)
     453                 :             :     return;
     454                 :             : 
     455                 :           7 :   g_object_get (transfer,
     456                 :             :                 "device", &device,
     457                 :             :                 "id",     &id,
     458                 :             :                 "state",  &state,
     459                 :             :                 NULL);
     460                 :           7 :   device_name = valent_device_get_name (device);
     461                 :             : 
     462   [ +  +  -  - ]:           7 :   switch (state)
     463                 :             :     {
     464                 :           5 :     case VALENT_TRANSFER_STATE_PENDING:
     465                 :             :     case VALENT_TRANSFER_STATE_ACTIVE:
     466                 :           5 :       icon = g_themed_icon_new ("document-send-symbolic");
     467                 :           5 :       title = _("Transferring Files");
     468                 :           5 :       body = g_strdup_printf (ngettext ("Sending one file to %1$s",
     469                 :             :                                         "Sending %2$d files to %1$s",
     470                 :             :                                         n_files),
     471                 :             :                               device_name, n_files);
     472                 :           5 :       break;
     473                 :             : 
     474                 :           2 :     case VALENT_TRANSFER_STATE_COMPLETE:
     475                 :           2 :       icon = g_themed_icon_new ("document-send-symbolic");
     476                 :           2 :       title = _("Transfer Complete");
     477                 :           2 :       body = g_strdup_printf (ngettext ("Sent one file to %1$s",
     478                 :             :                                         "Sent %2$d files to %1$s",
     479                 :             :                                         n_files),
     480                 :             :                               device_name, n_files);
     481                 :           2 :       break;
     482                 :             : 
     483                 :           0 :     case VALENT_TRANSFER_STATE_FAILED:
     484                 :           0 :       icon = g_themed_icon_new ("dialog-warning-symbolic");
     485                 :           0 :       title = _("Transfer Failed");
     486                 :           0 :       body = g_strdup_printf (ngettext ("Sending one file to %1$s",
     487                 :             :                                         "Sending %2$d files to %1$s",
     488                 :             :                                         n_files),
     489                 :             :                               device_name, n_files);
     490                 :           0 :       break;
     491                 :             :     }
     492                 :             : 
     493                 :           7 :   notification = g_notification_new (title);
     494                 :           7 :   g_notification_set_body (notification, body);
     495                 :           7 :   g_notification_set_icon (notification, icon);
     496                 :             : 
     497         [ +  + ]:           7 :   if (state == VALENT_TRANSFER_STATE_ACTIVE)
     498                 :             :     {
     499                 :           5 :       valent_notification_add_device_button (notification,
     500                 :             :                                              device,
     501                 :           5 :                                              _("Cancel"),
     502                 :             :                                              "share.cancel",
     503                 :             :                                              g_variant_new_string (id));
     504                 :             :     }
     505                 :             : 
     506                 :           7 :   valent_device_plugin_show_notification (VALENT_DEVICE_PLUGIN (self),
     507                 :             :                                           id,
     508                 :             :                                           notification);
     509                 :             : }
     510                 :             : 
     511                 :             : static void
     512                 :           2 : valent_share_upload_file_cb (ValentTransfer *transfer,
     513                 :             :                              GAsyncResult   *result,
     514                 :             :                              gpointer        user_data)
     515                 :             : {
     516                 :           4 :   g_autoptr (ValentSharePlugin) self = VALENT_SHARE_PLUGIN (user_data);
     517                 :           2 :   g_autofree char *id = NULL;
     518                 :           2 :   g_autoptr (GError) error = NULL;
     519                 :             : 
     520         [ +  - ]:           2 :   g_assert (VALENT_IS_SHARE_UPLOAD (transfer));
     521         [ -  + ]:           2 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
     522                 :             : 
     523                 :           2 :   id = valent_transfer_dup_id (transfer);
     524                 :             : 
     525   [ -  +  -  - ]:           2 :   if (valent_transfer_execute_finish (transfer, result, &error) ||
     526                 :           0 :       !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     527                 :           2 :     valent_share_upload_file_notification (self, transfer);
     528                 :             :   else
     529                 :           0 :     valent_device_plugin_hide_notification (VALENT_DEVICE_PLUGIN (self), id);
     530                 :             : 
     531         [ +  - ]:           2 :   if (self->upload == transfer)
     532         [ +  - ]:           2 :     g_clear_object (&self->upload);
     533                 :             : 
     534         [ -  + ]:           2 :   g_hash_table_remove (self->transfers, id);
     535                 :           2 : }
     536                 :             : 
     537                 :             : static void
     538                 :           5 : valent_share_upload_files_added (ValentTransfer    *transfer,
     539                 :             :                                  unsigned int       position,
     540                 :             :                                  unsigned int       removed,
     541                 :             :                                  unsigned int       added,
     542                 :             :                                  ValentSharePlugin *self)
     543                 :             : {
     544         [ +  - ]:           5 :   g_assert (VALENT_IS_SHARE_UPLOAD (transfer));
     545         [ -  + ]:           5 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
     546                 :             : 
     547                 :             :   /* If no files were added, something went wrong */
     548         [ -  + ]:           5 :   g_return_if_fail (added > 0);
     549                 :             : 
     550                 :             :   /* Start the transfer, if necessary */
     551         [ +  + ]:           5 :   if (valent_transfer_get_state (transfer) == VALENT_TRANSFER_STATE_PENDING)
     552                 :             :     {
     553                 :           2 :       valent_transfer_execute (transfer,
     554                 :             :                                NULL,
     555                 :             :                                (GAsyncReadyCallback)valent_share_upload_file_cb,
     556                 :             :                                g_object_ref (self));
     557                 :             :     }
     558                 :             : 
     559                 :           5 :   valent_share_upload_file_notification (self, transfer);
     560                 :             : }
     561                 :             : 
     562                 :             : static void
     563                 :           5 : valent_share_plugin_upload_file (ValentSharePlugin *self,
     564                 :             :                                  GFile             *file)
     565                 :             : {
     566         [ +  - ]:           5 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
     567   [ +  -  +  -  :           5 :   g_assert (G_IS_FILE (file));
             +  -  -  + ]
     568                 :             : 
     569                 :             :   /* Create a new transfer, if necessary */
     570         [ +  + ]:           5 :   if (self->upload == NULL)
     571                 :             :     {
     572                 :           2 :       ValentDevice *device;
     573                 :             : 
     574                 :           2 :       device = valent_extension_get_object (VALENT_EXTENSION (self));
     575                 :             : 
     576                 :           2 :       self->upload = valent_share_upload_new (device);
     577                 :           2 :       g_signal_connect_object (self->upload,
     578                 :             :                                "items-changed",
     579                 :             :                                G_CALLBACK (valent_share_upload_files_added),
     580                 :             :                                self, 0);
     581                 :           2 :       g_hash_table_replace (self->transfers,
     582                 :           2 :                             valent_transfer_dup_id (self->upload),
     583                 :           2 :                             g_object_ref (self->upload));
     584                 :             :     }
     585                 :             : 
     586                 :           5 :   valent_share_upload_add_file (VALENT_SHARE_UPLOAD (self->upload), file);
     587                 :           5 : }
     588                 :             : 
     589                 :             : /*
     590                 :             :  * GActions
     591                 :             :  */
     592                 :             : /**
     593                 :             :  * ValentSharePlugin|share.cancel:
     594                 :             :  * @parameter: "s"
     595                 :             :  * @id: The transfer ID
     596                 :             :  *
     597                 :             :  * Each transfer is given a UUID for the purposes of cancelling it. Usually this
     598                 :             :  * action will only be activated from the transfer notification as sent by
     599                 :             :  * upload_operation() or the incoming file handler.
     600                 :             :  */
     601                 :             : static void
     602                 :           0 : share_cancel_action (GSimpleAction *action,
     603                 :             :                      GVariant      *parameter,
     604                 :             :                      gpointer       user_data)
     605                 :             : {
     606                 :           0 :   ValentSharePlugin *self = VALENT_SHARE_PLUGIN (user_data);
     607                 :           0 :   ValentTransfer *transfer;
     608                 :           0 :   const char *id;
     609                 :             : 
     610         [ #  # ]:           0 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
     611                 :             : 
     612                 :           0 :   id = g_variant_get_string (parameter, NULL);
     613                 :           0 :   transfer = g_hash_table_lookup (self->transfers, id);
     614                 :             : 
     615         [ #  # ]:           0 :   if (transfer != NULL)
     616                 :           0 :     valent_transfer_cancel (transfer);
     617                 :           0 : }
     618                 :             : 
     619                 :             : /**
     620                 :             :  * ValentSharePlugin|share.copy:
     621                 :             :  * @parameter: "s"
     622                 :             :  * @text: The text content
     623                 :             :  *
     624                 :             :  * This action allows copying shared text to the clipboard from a notification.
     625                 :             :  */
     626                 :             : static void
     627                 :           0 : share_copy_action (GSimpleAction *action,
     628                 :             :                    GVariant      *parameter,
     629                 :             :                    gpointer       user_data)
     630                 :             : {
     631                 :           0 :   const char *text;
     632                 :             : 
     633         [ #  # ]:           0 :   g_assert (VALENT_IS_SHARE_PLUGIN (user_data));
     634                 :             : 
     635                 :           0 :   text = g_variant_get_string (parameter, NULL);
     636                 :           0 :   valent_clipboard_write_text (valent_clipboard_get_default (),
     637                 :             :                                text,
     638                 :             :                                NULL,
     639                 :             :                                NULL,
     640                 :             :                                NULL);
     641                 :           0 : }
     642                 :             : 
     643                 :             : /**
     644                 :             :  * ValentSharePlugin|share.open:
     645                 :             :  * @parameter: "s"
     646                 :             :  * @uri: File URI to open
     647                 :             :  *
     648                 :             :  * This action is used to open a URI.
     649                 :             :  *
     650                 :             :  * By convention, the remote device will open the URI with the default handler
     651                 :             :  * for that type.
     652                 :             :  *
     653                 :             :  * If the URI scheme is `file://`, it will be converted to a file upload,
     654                 :             :  * requesting it be opened after transfer.
     655                 :             :  */
     656                 :             : static void
     657                 :           2 : share_open_action (GSimpleAction *action,
     658                 :             :                    GVariant      *parameter,
     659                 :             :                    gpointer       user_data)
     660                 :             : {
     661                 :           2 :   ValentSharePlugin *self = VALENT_SHARE_PLUGIN (user_data);
     662                 :           2 :   const char *uri_string = NULL;
     663                 :           2 :   g_autoptr (GUri) uri = NULL;
     664                 :           2 :   g_autoptr (GError) error = NULL;
     665                 :             : 
     666         [ +  - ]:           2 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
     667         [ -  + ]:           2 :   g_assert (g_variant_is_of_type (parameter, G_VARIANT_TYPE_STRING));
     668                 :             : 
     669                 :           2 :   uri_string = g_variant_get_string (parameter, NULL);
     670                 :             : 
     671         [ -  + ]:           2 :   if ((uri = g_uri_parse (uri_string, G_URI_FLAGS_NONE, &error)) == NULL)
     672                 :             :     {
     673                 :           0 :       g_warning ("%s(): %s", G_STRFUNC, error->message);
     674         [ #  # ]:           0 :       return;
     675                 :             :     }
     676                 :             : 
     677         [ +  - ]:           2 :   if (g_str_equal ("file", g_uri_get_scheme (uri)) ||
     678         [ +  + ]:           2 :       g_str_equal ("resource", g_uri_get_scheme (uri)))
     679                 :             :     {
     680         [ -  + ]:           2 :       g_autoptr (GFile) file = NULL;
     681                 :             : 
     682                 :           1 :       file = g_file_new_for_uri (uri_string);
     683         [ +  - ]:           1 :       valent_share_plugin_open_file (self, file);
     684                 :             :     }
     685                 :             :   else
     686                 :             :     {
     687                 :           1 :       g_autoptr (JsonBuilder) builder = NULL;
     688         [ -  + ]:           1 :       g_autoptr (JsonNode) packet = NULL;
     689                 :             : 
     690                 :           1 :       valent_packet_init (&builder, "kdeconnect.share.request");
     691                 :           1 :       json_builder_set_member_name (builder, "url");
     692                 :           1 :       json_builder_add_string_value (builder, uri_string);
     693                 :           1 :       packet = valent_packet_end (&builder);
     694                 :             : 
     695         [ +  - ]:           1 :       valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     696                 :             :     }
     697                 :             : }
     698                 :             : 
     699                 :             : static void
     700                 :           0 : share_save_action_cb (GFile        *file,
     701                 :             :                       GAsyncResult *result,
     702                 :             :                       gpointer      user_data)
     703                 :             : {
     704                 :           0 :   ValentSharePlugin *self = VALENT_SHARE_PLUGIN (user_data);
     705                 :           0 :   g_autoptr (GNotification) notification = NULL;
     706         [ #  # ]:           0 :   g_autoptr (GIcon) icon = NULL;
     707         [ #  # ]:           0 :   g_autoptr (GFile) parent = NULL;
     708                 :           0 :   ValentDevice *device = NULL;
     709         [ #  # ]:           0 :   g_autofree char *title = NULL;
     710                 :           0 :   g_autofree char *dir_uri = NULL;
     711                 :           0 :   g_autofree char *file_uri = NULL;
     712                 :           0 :   g_autofree char *basename = NULL;
     713                 :           0 :   const char *name = NULL;
     714                 :           0 :   g_autoptr (GError) error = NULL;
     715                 :             : 
     716         [ #  # ]:           0 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
     717                 :             : 
     718         [ #  # ]:           0 :   if (!g_file_replace_contents_finish (file, result, NULL, &error))
     719                 :             :     {
     720                 :           0 :       g_warning ("Saving \"%s\": %s", g_file_peek_path (file), error->message);
     721         [ #  # ]:           0 :       return;
     722                 :             :     }
     723                 :             : 
     724                 :           0 :   device = valent_extension_get_object (VALENT_EXTENSION (self));
     725                 :           0 :   name = valent_device_get_name (device);
     726                 :           0 :   parent = g_file_get_parent (file);
     727                 :           0 :   dir_uri = g_file_get_uri (parent);
     728                 :           0 :   file_uri = g_file_get_uri (file);
     729                 :           0 :   basename = g_file_get_basename (file);
     730                 :             : 
     731                 :           0 :   title = g_strdup_printf (_("Text from “%s” saved to “%s”"), name, basename);
     732                 :           0 :   icon = g_themed_icon_new ("document-save-symbolic");
     733                 :             : 
     734                 :           0 :   notification = g_notification_new (title);
     735                 :           0 :   g_notification_set_icon (notification, icon);
     736                 :           0 :   valent_notification_add_device_button (notification,
     737                 :             :                                          device,
     738                 :           0 :                                          _("Open Folder"),
     739                 :             :                                          "share.view",
     740                 :             :                                          g_variant_new_string (dir_uri));
     741                 :           0 :   valent_notification_add_device_button (notification,
     742                 :             :                                          device,
     743                 :           0 :                                          _("Open File"),
     744                 :             :                                          "share.view",
     745                 :             :                                          g_variant_new_string (file_uri));
     746         [ #  # ]:           0 :   valent_device_plugin_show_notification (VALENT_DEVICE_PLUGIN (self),
     747                 :             :                                           file_uri,
     748                 :             :                                           notification);
     749                 :             : }
     750                 :             : 
     751                 :             : /**
     752                 :             :  * ValentSharePlugin|share.save:
     753                 :             :  * @parameter: "s"
     754                 :             :  * @text: The text content
     755                 :             :  *
     756                 :             :  * This action allows saving shared text to file from a notification.
     757                 :             :  */
     758                 :             : static void
     759                 :           0 : share_save_action (GSimpleAction *action,
     760                 :             :                    GVariant      *parameter,
     761                 :             :                    gpointer       user_data)
     762                 :             : {
     763                 :           0 :   ValentSharePlugin *self = VALENT_SHARE_PLUGIN (user_data);
     764                 :           0 :   ValentDevice *device = NULL;
     765                 :           0 :   g_autoptr (GBytes) bytes = NULL;
     766         [ #  # ]:           0 :   g_autoptr (GDateTime) date = NULL;
     767         [ #  # ]:           0 :   g_autofree char *date_str = NULL;
     768                 :           0 :   g_autoptr (GFile) file = NULL;
     769         [ #  # ]:           0 :   g_autofree char *filename = NULL;
     770                 :           0 :   const char *name;
     771                 :           0 :   const char *text;
     772                 :             : 
     773         [ #  # ]:           0 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
     774                 :             : 
     775                 :           0 :   device = valent_extension_get_object (VALENT_EXTENSION (self));
     776                 :           0 :   name = valent_device_get_name (device);
     777                 :           0 :   text = g_variant_get_string (parameter, NULL);
     778                 :             : 
     779                 :           0 :   bytes = g_bytes_new (text, strlen (text));
     780                 :           0 :   date = g_date_time_new_now_local ();
     781                 :           0 :   date_str = g_date_time_format (date, "%F %T");
     782                 :             :   /* TRANSLATORS: this is a filename used for text shared by a device, where
     783                 :             :    * the first "%s" is the date and the second "%s" is the device name, e.g.
     784                 :             :    * "Text from 07-12-2024 10:00:46 PM (OnePlus 6)"
     785                 :             :    * */
     786                 :           0 :   filename = g_strdup_printf (_("Text from %s (%s).txt"), date_str, name);
     787                 :           0 :   file = valent_share_plugin_create_download_file (self, filename, TRUE);
     788                 :             : 
     789                 :           0 :   g_file_replace_contents_bytes_async (file,
     790                 :             :                                        bytes,
     791                 :             :                                        NULL,
     792                 :             :                                        FALSE,
     793                 :             :                                        G_FILE_CREATE_REPLACE_DESTINATION,
     794                 :             :                                        NULL,
     795                 :             :                                        (GAsyncReadyCallback)share_save_action_cb,
     796                 :             :                                        NULL);
     797                 :           0 : }
     798                 :             : 
     799                 :             : /**
     800                 :             :  * ValentSharePlugin|share.text:
     801                 :             :  * @parameter: "s"
     802                 :             :  * @text: text to share
     803                 :             :  *
     804                 :             :  * This action simply sends a chunk of text to the remote device. Ultimately,
     805                 :             :  * how the remote device handles the text is undefined. It may be copied to the
     806                 :             :  * clipboard, stored as a temporary file or just displayed.
     807                 :             :  */
     808                 :             : static void
     809                 :           1 : share_text_action (GSimpleAction *action,
     810                 :             :                    GVariant      *parameter,
     811                 :             :                    gpointer       user_data)
     812                 :             : {
     813                 :           1 :   ValentSharePlugin *self = VALENT_SHARE_PLUGIN (user_data);
     814                 :           1 :   const char *text;
     815                 :           2 :   g_autoptr (JsonBuilder) builder = NULL;
     816         [ -  + ]:           1 :   g_autoptr (JsonNode) packet = NULL;
     817                 :             : 
     818         [ +  - ]:           1 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
     819         [ -  + ]:           1 :   g_assert (g_variant_is_of_type (parameter, G_VARIANT_TYPE_STRING));
     820                 :             : 
     821                 :           1 :   text = g_variant_get_string (parameter, NULL);
     822                 :             : 
     823                 :           1 :   valent_packet_init (&builder, "kdeconnect.share.request");
     824                 :           1 :   json_builder_set_member_name (builder, "text");
     825                 :           1 :   json_builder_add_string_value (builder, text);
     826                 :           1 :   packet = valent_packet_end (&builder);
     827                 :             : 
     828         [ +  - ]:           1 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     829                 :           1 : }
     830                 :             : 
     831                 :             : /**
     832                 :             :  * ValentSharePlugin|share.uri:
     833                 :             :  * @parameter: "s"
     834                 :             :  * @uri: URI to share
     835                 :             :  *
     836                 :             :  * This action is used to share a URI.
     837                 :             :  *
     838                 :             :  * By convention, the remote device will open the URI with the default handler
     839                 :             :  * for that type.
     840                 :             :  *
     841                 :             :  * If the URI scheme is `file://`, it will be converted to a file upload.
     842                 :             :  */
     843                 :             : static void
     844                 :           9 : share_uri_action (GSimpleAction *action,
     845                 :             :                   GVariant      *parameter,
     846                 :             :                   gpointer       user_data)
     847                 :             : {
     848                 :           9 :   ValentSharePlugin *self = VALENT_SHARE_PLUGIN (user_data);
     849                 :           9 :   const char *uri_string = NULL;
     850                 :           9 :   g_autoptr (GUri) uri = NULL;
     851                 :           9 :   g_autoptr (GError) error = NULL;
     852                 :             : 
     853         [ +  - ]:           9 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
     854         [ -  + ]:           9 :   g_assert (g_variant_is_of_type (parameter, G_VARIANT_TYPE_STRING));
     855                 :             : 
     856                 :           9 :   uri_string = g_variant_get_string (parameter, NULL);
     857                 :             : 
     858         [ -  + ]:           9 :   if ((uri = g_uri_parse (uri_string, G_URI_FLAGS_NONE, &error)) == NULL)
     859                 :             :     {
     860                 :           0 :       g_warning ("%s(): %s", G_STRFUNC, error->message);
     861         [ #  # ]:           0 :       return;
     862                 :             :     }
     863                 :             : 
     864         [ +  - ]:           9 :   if (g_str_equal ("file", g_uri_get_scheme (uri)) ||
     865         [ +  + ]:           9 :       g_str_equal ("resource", g_uri_get_scheme (uri)))
     866                 :             :     {
     867         [ -  + ]:           9 :       g_autoptr (GFile) file = NULL;
     868                 :             : 
     869                 :           5 :       file = g_file_new_for_uri (uri_string);
     870         [ +  - ]:           5 :       valent_share_plugin_upload_file (self, file);
     871                 :             :     }
     872                 :             :   else
     873                 :             :     {
     874                 :           4 :       g_autoptr (JsonBuilder) builder = NULL;
     875         [ -  + ]:           4 :       g_autoptr (JsonNode) packet = NULL;
     876                 :             : 
     877                 :           4 :       valent_packet_init (&builder, "kdeconnect.share.request");
     878                 :           4 :       json_builder_set_member_name (builder, "url");
     879                 :           4 :       json_builder_add_string_value (builder, uri_string);
     880                 :           4 :       packet = valent_packet_end (&builder);
     881                 :             : 
     882         [ +  - ]:           4 :       valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     883                 :             :     }
     884                 :             : }
     885                 :             : 
     886                 :             : /**
     887                 :             :  * ValentSharePlugin|share.uris:
     888                 :             :  * @parameter: "as"
     889                 :             :  * @uris: a list of URIs
     890                 :             :  *
     891                 :             :  * This action is a convenience for sending multiple URIs, as with the
     892                 :             :  * `ValentSharePlugin|share.uri` `GAction`.
     893                 :             :  */
     894                 :             : static void
     895                 :           1 : share_uris_action (GSimpleAction *action,
     896                 :             :                    GVariant      *parameter,
     897                 :             :                    gpointer       user_data)
     898                 :             : {
     899                 :           1 :   GVariantIter iter;
     900                 :           1 :   GVariant *child;
     901                 :             : 
     902         [ +  - ]:           1 :   g_assert (VALENT_IS_SHARE_PLUGIN (user_data));
     903         [ -  + ]:           1 :   g_assert (g_variant_is_of_type (parameter, G_VARIANT_TYPE_STRING_ARRAY));
     904                 :             : 
     905                 :           1 :   g_variant_iter_init (&iter, parameter);
     906                 :             : 
     907         [ +  + ]:           9 :   while ((child = g_variant_iter_next_value (&iter)) != NULL)
     908                 :             :     {
     909                 :           7 :       share_uri_action (action, child, user_data);
     910                 :           8 :       g_clear_pointer (&child, g_variant_unref);
     911                 :             :     }
     912                 :           1 : }
     913                 :             : 
     914                 :             : /**
     915                 :             :  * ValentSharePlugin|share.view:
     916                 :             :  * @parameter: "s"
     917                 :             :  * @uri: File or directory URI to view
     918                 :             :  *
     919                 :             :  * This action opens a file or directory.
     920                 :             :  */
     921                 :             : static void
     922                 :           0 : share_view_action (GSimpleAction *action,
     923                 :             :                    GVariant      *parameter,
     924                 :             :                    gpointer       user_data)
     925                 :             : {
     926                 :           0 :   const char *uri;
     927                 :             : 
     928         [ #  # ]:           0 :   g_assert (VALENT_IS_SHARE_PLUGIN (user_data));
     929         [ #  # ]:           0 :   g_assert (g_variant_is_of_type (parameter, G_VARIANT_TYPE_STRING));
     930                 :             : 
     931                 :           0 :   uri = g_variant_get_string (parameter, NULL);
     932                 :           0 :   g_app_info_launch_default_for_uri_async (uri, NULL, NULL, NULL, NULL);
     933                 :           0 : }
     934                 :             : 
     935                 :             : static GActionEntry actions[] = {
     936                 :             :     {"cancel",  share_cancel_action,  "s",  NULL, NULL},
     937                 :             :     {"copy",    share_copy_action,    "s",  NULL, NULL},
     938                 :             :     {"open",    share_open_action,    "s",  NULL, NULL},
     939                 :             :     {"save",    share_save_action,    "s",  NULL, NULL},
     940                 :             :     {"text",    share_text_action,    "s",  NULL, NULL},
     941                 :             :     {"uri",     share_uri_action,     "s",  NULL, NULL},
     942                 :             :     {"uris",    share_uris_action,    "as", NULL, NULL},
     943                 :             :     {"view",    share_view_action,    "s",  NULL, NULL},
     944                 :             : };
     945                 :             : 
     946                 :             : /*
     947                 :             :  * Packet Handlers
     948                 :             :  */
     949                 :             : static void
     950                 :           7 : valent_share_plugin_handle_file (ValentSharePlugin *self,
     951                 :             :                                  JsonNode          *packet)
     952                 :             : {
     953                 :           7 :   g_autoptr (ValentTransfer) transfer = NULL;
     954                 :           7 :   ValentDevice *device;
     955                 :           7 :   const char *filename;
     956   [ +  -  +  - ]:           7 :   g_autoptr (GFile) file = NULL;
     957                 :           7 :   int64_t number_of_files = 0;
     958                 :             : 
     959         [ +  - ]:           7 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
     960         [ -  + ]:           7 :   g_assert (VALENT_IS_PACKET (packet));
     961                 :             : 
     962                 :             :   /* Common packet fields */
     963         [ -  + ]:           7 :   if (!valent_packet_has_payload (packet))
     964                 :             :     {
     965                 :           0 :       g_warning ("%s(): missing payload info", G_STRFUNC);
     966                 :           0 :       return;
     967                 :             :     }
     968                 :             : 
     969         [ -  + ]:           7 :   if (!valent_packet_get_string (packet, "filename", &filename))
     970                 :             :     {
     971                 :           0 :       g_debug ("%s(): expected \"filename\" field holding a string",
     972                 :             :                G_STRFUNC);
     973                 :           0 :       return;
     974                 :             :     }
     975                 :             : 
     976                 :             :   /* Newer implementations support sequential multi-file transfers */
     977         [ +  + ]:           7 :   if (!valent_packet_get_int (packet, "numberOfFiles", &number_of_files))
     978                 :             :     {
     979                 :           2 :       json_object_set_int_member (valent_packet_get_body (packet),
     980                 :             :                                   "numberOfFiles",
     981                 :             :                                   1);
     982                 :             :     }
     983                 :             : 
     984         [ +  + ]:           7 :   if (!valent_packet_get_int (packet, "totalPayloadSize", NULL))
     985                 :             :     {
     986                 :           2 :       json_object_set_int_member (valent_packet_get_body (packet),
     987                 :             :                                   "totalPayloadSize",
     988                 :             :                                   valent_packet_get_payload_size (packet));
     989                 :             :     }
     990                 :             : 
     991                 :           7 :   device = valent_extension_get_object (VALENT_EXTENSION (self));
     992                 :           7 :   file = valent_share_plugin_create_download_file (self, filename, TRUE);
     993                 :             : 
     994                 :             :   /* If the packet includes a request to open the file when the transfer
     995                 :             :    * completes, use a separate routine for success/failure. */
     996         [ +  + ]:           7 :   if (valent_packet_check_field (packet, "open"))
     997                 :             :     {
     998                 :           1 :       transfer = valent_device_transfer_new (device, packet, file);
     999                 :           2 :       g_hash_table_replace (self->transfers,
    1000                 :           1 :                             valent_transfer_dup_id (transfer),
    1001                 :             :                             g_object_ref (transfer));
    1002                 :             : 
    1003                 :           1 :       valent_transfer_execute (transfer,
    1004                 :             :                                NULL,
    1005                 :             :                                (GAsyncReadyCallback)valent_share_download_open_cb,
    1006                 :             :                                g_object_ref (self));
    1007                 :           1 :       valent_share_download_open_notification (self, transfer);
    1008                 :           1 :       return;
    1009                 :             :     }
    1010                 :             : 
    1011                 :             :   /* If the packet is missing the `numberOfFiles` field it is a legacy transfer
    1012                 :             :    * transfer; use a discrete transfer with standard success/failure handling. */
    1013         [ +  + ]:           6 :   if (!number_of_files)
    1014                 :             :     {
    1015                 :           1 :       transfer = valent_share_download_new (device);
    1016                 :           2 :       g_hash_table_replace (self->transfers,
    1017                 :           1 :                             valent_transfer_dup_id (transfer),
    1018                 :             :                             g_object_ref (transfer));
    1019                 :             : 
    1020                 :           1 :       valent_share_download_add_file (VALENT_SHARE_DOWNLOAD (transfer),
    1021                 :             :                                       file,
    1022                 :             :                                       packet);
    1023                 :             : 
    1024                 :           1 :       valent_transfer_execute (transfer,
    1025                 :             :                                NULL,
    1026                 :             :                                (GAsyncReadyCallback)valent_share_download_file_cb,
    1027                 :             :                                g_object_ref (self));
    1028                 :           1 :       valent_share_download_file_notification (self, transfer);
    1029                 :           1 :       return;
    1030                 :             :     }
    1031                 :             : 
    1032                 :             :   /* Otherwise the file will appended to a multi-file transfer */
    1033         [ +  + ]:           5 :   if (self->download != NULL)
    1034                 :             :     {
    1035                 :           1 :       transfer = g_object_ref (self->download);
    1036                 :             :     }
    1037                 :             :   else
    1038                 :             :     {
    1039                 :           4 :       transfer = valent_share_download_new (device);
    1040                 :           8 :       g_hash_table_replace (self->transfers,
    1041                 :           4 :                             valent_transfer_dup_id (transfer),
    1042                 :             :                             g_object_ref (transfer));
    1043                 :             :     }
    1044                 :             : 
    1045                 :           5 :   valent_share_download_add_file (VALENT_SHARE_DOWNLOAD (transfer),
    1046                 :             :                                   file,
    1047                 :             :                                   packet);
    1048                 :             : 
    1049         [ +  + ]:           5 :   if (self->download != transfer)
    1050                 :           4 :     g_set_object (&self->download, transfer);
    1051                 :             : 
    1052         [ +  + ]:           5 :   if (valent_transfer_get_state (transfer) == VALENT_TRANSFER_STATE_PENDING)
    1053                 :             :     {
    1054                 :           4 :       valent_transfer_execute (transfer,
    1055                 :             :                                NULL,
    1056                 :             :                                (GAsyncReadyCallback)valent_share_download_file_cb,
    1057                 :             :                                g_object_ref (self));
    1058                 :             :     }
    1059                 :             : 
    1060         [ +  - ]:           5 :   valent_share_download_file_notification (self, transfer);
    1061                 :             : }
    1062                 :             : 
    1063                 :             : static void
    1064                 :           1 : valent_share_plugin_handle_file_update (ValentSharePlugin *self,
    1065                 :             :                                         JsonNode          *packet)
    1066                 :             : {
    1067         [ +  - ]:           1 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
    1068         [ -  + ]:           1 :   g_assert (VALENT_IS_PACKET (packet));
    1069                 :             : 
    1070         [ -  + ]:           1 :   if (self->download == NULL)
    1071                 :             :     return;
    1072                 :             : 
    1073         [ #  # ]:           0 :   if (!valent_packet_check_field (packet, "numberOfFiles"))
    1074                 :             :     {
    1075                 :           0 :       g_debug ("%s(): expected \"numberOfFiles\" field holding an integer",
    1076                 :             :                G_STRFUNC);
    1077                 :           0 :       return;
    1078                 :             :     }
    1079                 :             : 
    1080         [ #  # ]:           0 :   if (!valent_packet_check_field (packet, "totalPayloadSize"))
    1081                 :             :     {
    1082                 :           0 :       g_debug ("%s(): expected \"totalPayloadSize\" field holding an integer",
    1083                 :             :                G_STRFUNC);
    1084                 :           0 :       return;
    1085                 :             :     }
    1086                 :             : 
    1087                 :           0 :   valent_share_download_update (VALENT_SHARE_DOWNLOAD (self->download), packet);
    1088                 :           0 :   valent_share_download_file_notification (self, self->download);
    1089                 :             : }
    1090                 :             : 
    1091                 :             : static void
    1092                 :           1 : valent_share_plugin_handle_text (ValentSharePlugin *self,
    1093                 :             :                                  const char        *text)
    1094                 :             : {
    1095                 :           1 :   ValentExtension *extension = VALENT_EXTENSION (self);
    1096                 :           1 :   ValentDevice *device = NULL;
    1097                 :           2 :   g_autoptr (GNotification) notification = NULL;
    1098         [ +  - ]:           1 :   g_autofree char *id = NULL;
    1099                 :           1 :   g_autofree char *title = NULL;
    1100                 :           1 :   const char *name = NULL;
    1101                 :             : 
    1102         [ +  - ]:           1 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
    1103         [ -  + ]:           1 :   g_assert (text != NULL);
    1104                 :             : 
    1105                 :           1 :   device = valent_extension_get_object (extension);
    1106                 :           1 :   name = valent_device_get_name (device);
    1107                 :           1 :   id = g_compute_checksum_for_string (G_CHECKSUM_MD5, text, -1);
    1108                 :           1 :   title = g_strdup_printf (_("Text from “%s”"), name);
    1109                 :             : 
    1110                 :           1 :   notification = g_notification_new (title);
    1111                 :           1 :   g_notification_set_body (notification, text);
    1112                 :           1 :   valent_notification_add_device_button (notification,
    1113                 :             :                                          device,
    1114                 :           1 :                                          _("Save"),
    1115                 :             :                                          "share.save",
    1116                 :             :                                          g_variant_new_string (text));
    1117                 :           1 :   valent_notification_add_device_button (notification,
    1118                 :             :                                          device,
    1119                 :           1 :                                          _("Copy"),
    1120                 :             :                                          "share.copy",
    1121                 :             :                                          g_variant_new_string (text));
    1122                 :             : 
    1123                 :           1 :   valent_device_plugin_show_notification (VALENT_DEVICE_PLUGIN (self),
    1124                 :             :                                           id,
    1125                 :             :                                           notification);
    1126                 :           1 : }
    1127                 :             : 
    1128                 :             : static void
    1129                 :           1 : valent_share_plugin_handle_url (ValentSharePlugin *self,
    1130                 :             :                                 const char        *url)
    1131                 :             : {
    1132         [ +  - ]:           1 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
    1133         [ -  + ]:           1 :   g_assert (url != NULL);
    1134                 :             : 
    1135                 :           1 :   g_app_info_launch_default_for_uri_async (url, NULL, NULL, NULL, NULL);
    1136                 :           1 : }
    1137                 :             : 
    1138                 :             : /*
    1139                 :             :  * ValentDevicePlugin
    1140                 :             :  */
    1141                 :             : static void
    1142                 :          37 : valent_share_plugin_update_state (ValentDevicePlugin *plugin,
    1143                 :             :                                   ValentDeviceState   state)
    1144                 :             : {
    1145                 :          37 :   ValentSharePlugin *self = VALENT_SHARE_PLUGIN (plugin);
    1146                 :          37 :   gboolean available;
    1147                 :             : 
    1148         [ +  - ]:          37 :   g_assert (VALENT_IS_SHARE_PLUGIN (plugin));
    1149                 :             : 
    1150                 :          37 :   available = (state & VALENT_DEVICE_STATE_CONNECTED) != 0 &&
    1151                 :             :               (state & VALENT_DEVICE_STATE_PAIRED) != 0;
    1152                 :             : 
    1153                 :             :   /* If the device has been unpaired it should be considered untrusted, so
    1154                 :             :    * cancel any ongoing transfers. */
    1155         [ +  + ]:          37 :   if ((state & VALENT_DEVICE_STATE_PAIRED) == 0)
    1156                 :             :     {
    1157                 :           3 :       GHashTableIter iter;
    1158                 :           3 :       ValentTransfer *transfer;
    1159                 :             : 
    1160                 :           3 :       g_hash_table_iter_init (&iter, self->transfers);
    1161                 :             : 
    1162         [ -  + ]:           3 :       while (g_hash_table_iter_next (&iter, NULL, (void **)&transfer))
    1163                 :             :         {
    1164                 :           0 :           valent_transfer_cancel (transfer);
    1165                 :           0 :           g_hash_table_iter_remove (&iter);
    1166                 :             :         }
    1167                 :             : 
    1168         [ -  + ]:           3 :       g_clear_object (&self->download);
    1169         [ -  + ]:           3 :       g_clear_object (&self->upload);
    1170                 :             :     }
    1171                 :             : 
    1172                 :          37 :   valent_extension_toggle_actions (VALENT_EXTENSION (plugin), available);
    1173                 :          37 : }
    1174                 :             : 
    1175                 :             : static void
    1176                 :          10 : valent_share_plugin_handle_packet (ValentDevicePlugin *plugin,
    1177                 :             :                                    const char         *type,
    1178                 :             :                                    JsonNode           *packet)
    1179                 :             : {
    1180                 :          10 :   ValentSharePlugin *self = VALENT_SHARE_PLUGIN (plugin);
    1181                 :          10 :   const char *text;
    1182                 :          10 :   const char *url;
    1183                 :             : 
    1184         [ +  - ]:          10 :   g_assert (VALENT_IS_SHARE_PLUGIN (self));
    1185         [ -  + ]:          10 :   g_assert (type != NULL);
    1186         [ -  + ]:          10 :   g_assert (VALENT_IS_PACKET (packet));
    1187                 :             : 
    1188         [ +  + ]:          10 :   if (g_str_equal (type, "kdeconnect.share.request"))
    1189                 :             :     {
    1190         [ +  + ]:           9 :       if (valent_packet_check_field (packet, "filename"))
    1191                 :           7 :         valent_share_plugin_handle_file (self, packet);
    1192                 :             : 
    1193         [ +  + ]:           2 :       else if (valent_packet_get_string (packet, "text", &text))
    1194                 :           1 :         valent_share_plugin_handle_text (self, text);
    1195                 :             : 
    1196         [ +  - ]:           1 :       else if (valent_packet_get_string (packet, "url", &url))
    1197                 :           1 :         valent_share_plugin_handle_url (self, url);
    1198                 :             : 
    1199                 :             :       else
    1200                 :           0 :         g_warning ("%s(): unsupported share request", G_STRFUNC);
    1201                 :             :     }
    1202         [ +  - ]:           1 :   else if (g_str_equal (type, "kdeconnect.share.request.update"))
    1203                 :             :     {
    1204                 :           1 :       valent_share_plugin_handle_file_update (self, packet);
    1205                 :             :     }
    1206                 :             :   else
    1207                 :           0 :     g_assert_not_reached ();
    1208                 :          10 : }
    1209                 :             : 
    1210                 :             : /*
    1211                 :             :  * ValentObject
    1212                 :             :  */
    1213                 :             : static void
    1214                 :          22 : valent_share_plugin_destroy (ValentObject *object)
    1215                 :             : {
    1216                 :          22 :   ValentSharePlugin *self = VALENT_SHARE_PLUGIN (object);
    1217                 :          22 :   GHashTableIter iter;
    1218                 :          22 :   ValentTransfer *transfer;
    1219                 :             : 
    1220                 :             :   /* Cancel active transfers */
    1221                 :          22 :   g_hash_table_iter_init (&iter, self->transfers);
    1222                 :             : 
    1223         [ -  + ]:          22 :   while (g_hash_table_iter_next (&iter, NULL, (void **)&transfer))
    1224                 :             :     {
    1225                 :           0 :       valent_transfer_cancel (transfer);
    1226                 :           0 :       g_hash_table_iter_remove (&iter);
    1227                 :             :     }
    1228                 :             : 
    1229         [ -  + ]:          22 :   g_clear_object (&self->download);
    1230         [ -  + ]:          22 :   g_clear_object (&self->upload);
    1231                 :             : 
    1232                 :          22 :   VALENT_OBJECT_CLASS (valent_share_plugin_parent_class)->destroy (object);
    1233                 :          22 : }
    1234                 :             : 
    1235                 :             : /*
    1236                 :             :  * GObject
    1237                 :             :  */
    1238                 :             : static void
    1239                 :          12 : valent_share_plugin_constructed (GObject *object)
    1240                 :             : {
    1241                 :          12 :   ValentDevicePlugin *plugin = VALENT_DEVICE_PLUGIN (object);
    1242                 :             : 
    1243                 :          12 :   g_action_map_add_action_entries (G_ACTION_MAP (plugin),
    1244                 :             :                                    actions,
    1245                 :             :                                    G_N_ELEMENTS (actions),
    1246                 :             :                                    plugin);
    1247                 :             : 
    1248                 :          12 :   G_OBJECT_CLASS (valent_share_plugin_parent_class)->constructed (object);
    1249                 :          12 : }
    1250                 :             : 
    1251                 :             : static void
    1252                 :          11 : valent_share_plugin_finalize (GObject *object)
    1253                 :             : {
    1254                 :          11 :   ValentSharePlugin *self = VALENT_SHARE_PLUGIN (object);
    1255                 :             : 
    1256         [ +  - ]:          11 :   g_clear_pointer (&self->transfers, g_hash_table_unref);
    1257                 :             : 
    1258                 :          11 :   G_OBJECT_CLASS (valent_share_plugin_parent_class)->finalize (object);
    1259                 :          11 : }
    1260                 :             : 
    1261                 :             : static void
    1262                 :          20 : valent_share_plugin_class_init (ValentSharePluginClass *klass)
    1263                 :             : {
    1264                 :          20 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
    1265                 :          20 :   ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
    1266                 :          20 :   ValentDevicePluginClass *plugin_class = VALENT_DEVICE_PLUGIN_CLASS (klass);
    1267                 :             : 
    1268                 :          20 :   object_class->constructed = valent_share_plugin_constructed;
    1269                 :          20 :   object_class->finalize = valent_share_plugin_finalize;
    1270                 :             : 
    1271                 :          20 :   vobject_class->destroy = valent_share_plugin_destroy;
    1272                 :             : 
    1273                 :          20 :   plugin_class->handle_packet = valent_share_plugin_handle_packet;
    1274                 :          20 :   plugin_class->update_state = valent_share_plugin_update_state;
    1275                 :             : }
    1276                 :             : 
    1277                 :             : static void
    1278                 :          12 : valent_share_plugin_init (ValentSharePlugin *self)
    1279                 :             : {
    1280                 :          12 :   self->transfers = g_hash_table_new_full (g_str_hash,
    1281                 :             :                                            g_str_equal,
    1282                 :             :                                            g_free,
    1283                 :             :                                            g_object_unref);
    1284                 :          12 : }
    1285                 :             : 
        

Generated by: LCOV version 2.0-1