LCOV - code coverage report
Current view: top level - src/plugins/share - valent-share-plugin.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 75.7 % 588 445
Test Date: 2025-04-18 23:22:27 Functions: 85.3 % 34 29
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 44.0 % 364 160

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

Generated by: LCOV version 2.0-1