LCOV - code coverage report
Current view: top level - src/plugins/sftp - valent-sftp-plugin.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 36.3 % 397 144
Test Date: 2025-03-21 05:08:07 Functions: 45.7 % 35 16
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 18.0 % 294 53

             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-sftp-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-sftp-plugin.h"
      14                 :             : 
      15                 :             : 
      16                 :             : typedef struct _ValentSftpSession ValentSftpSession;
      17                 :             : 
      18                 :             : struct _ValentSftpPlugin
      19                 :             : {
      20                 :             :   ValentDevicePlugin  parent_instance;
      21                 :             : 
      22                 :             :   GDBusConnection    *connection;
      23                 :             :   GVolumeMonitor     *monitor;
      24                 :             :   ValentSftpSession  *session;
      25                 :             :   unsigned int        privkey_ready : 1;
      26                 :             : };
      27                 :             : 
      28                 :             : static void   valent_sftp_plugin_mount_volume   (ValentSftpPlugin *self);
      29                 :             : static void   valent_sftp_plugin_unmount_volume (ValentSftpPlugin *self);
      30                 :             : static void   valent_sftp_plugin_update_menu    (ValentSftpPlugin *self);
      31                 :             : 
      32   [ +  +  +  - ]:          89 : G_DEFINE_FINAL_TYPE (ValentSftpPlugin, valent_sftp_plugin, VALENT_TYPE_DEVICE_PLUGIN)
      33                 :             : 
      34                 :             : 
      35                 :             : static char *
      36                 :          15 : get_device_host (ValentSftpPlugin *self)
      37                 :             : {
      38                 :          15 :   ValentDevice *device;
      39                 :          30 :   g_autoptr (ValentChannel) channel = NULL;
      40         [ +  + ]:          15 :   g_autofree char *host = NULL;
      41                 :          15 :   GParamSpec *pspec = NULL;
      42                 :             : 
      43                 :             :   /* The plugin doesn't know ValentChannel derivations, so we have to check for
      44                 :             :    * a "host" property to ensure it's IP-based */
      45                 :          15 :   device = valent_resource_get_source (VALENT_RESOURCE (self));
      46                 :          15 :   channel = valent_device_ref_channel (device);
      47                 :             : 
      48         [ +  + ]:          15 :   if G_LIKELY (channel != NULL)
      49                 :           4 :     pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (channel), "host");
      50                 :             : 
      51         [ +  - ]:           4 :   if G_LIKELY (pspec != NULL)
      52                 :           4 :     g_object_get (channel, "host", &host, NULL);
      53                 :             : 
      54                 :          15 :   return g_steal_pointer (&host);
      55                 :             : }
      56                 :             : 
      57                 :             : /**
      58                 :             :  * ValentSftpSession:
      59                 :             :  * @host: Host name or IP
      60                 :             :  * @port: Port
      61                 :             :  * @username: Username (deprecated)
      62                 :             :  * @password: Password (deprecated)
      63                 :             :  * @mount: A `GMount` for the session
      64                 :             :  *
      65                 :             :  * `ValentSftpSession` is a simple representation of a SFTP session.
      66                 :             :  */
      67                 :             : typedef struct _ValentSftpSession
      68                 :             : {
      69                 :             :   char       *host;
      70                 :             :   uint16_t    port;
      71                 :             :   char       *username;
      72                 :             :   char       *password;
      73                 :             :   GHashTable *paths;
      74                 :             : 
      75                 :             :   /* Gvfs state */
      76                 :             :   GMount     *mount;
      77                 :             :   char       *uri;
      78                 :             : } ValentSftpSession;
      79                 :             : 
      80                 :             : 
      81                 :             : static ValentSftpSession *
      82                 :           0 : sftp_session_new (ValentSftpPlugin *self,
      83                 :             :                   JsonNode         *packet)
      84                 :             : {
      85                 :           0 :   ValentSftpSession *session;
      86                 :           0 :   g_autofree char *host = NULL;
      87                 :           0 :   int64_t port;
      88                 :           0 :   const char *password;
      89                 :           0 :   const char *username;
      90                 :           0 :   JsonArray *multi_paths = NULL;
      91                 :           0 :   JsonArray *path_names = NULL;
      92                 :             : 
      93                 :             :   /* Ultimately, these are the only packet fields we really need */
      94         [ #  # ]:           0 :   if (!valent_packet_get_int (packet, "port", &port) ||
      95         [ #  # ]:           0 :       (port < 0 || port > G_MAXUINT16))
      96                 :             :     {
      97                 :           0 :       g_debug ("%s(): expected \"port\" field holding a uint16",
      98                 :             :                G_STRFUNC);
      99                 :           0 :       return NULL;
     100                 :             :     }
     101                 :             : 
     102         [ #  # ]:           0 :   if ((host = get_device_host (self)) == NULL)
     103                 :             :     {
     104                 :           0 :       g_warning ("%s(): failed to get host address",
     105                 :             :                  G_STRFUNC);
     106                 :           0 :       return NULL;
     107                 :             :     }
     108                 :             : 
     109                 :             :   // Create a session struct
     110                 :           0 :   session = g_new0 (ValentSftpSession, 1);
     111                 :           0 :   session->host = g_steal_pointer (&host);
     112                 :           0 :   session->port = (uint16_t)port;
     113                 :           0 :   session->paths = g_hash_table_new_full (g_str_hash, g_str_equal,
     114                 :             :                                           g_free, g_free);
     115                 :           0 :   session->uri = g_strdup_printf ("sftp://%s:%u/", session->host, session->port);
     116                 :             : 
     117         [ #  # ]:           0 :   if (valent_packet_get_string (packet, "user", &username))
     118         [ #  # ]:           0 :     session->username = g_strdup (username);
     119                 :             : 
     120         [ #  # ]:           0 :   if (valent_packet_get_string (packet, "password", &password))
     121         [ #  # ]:           0 :     session->password = g_strdup (password);
     122                 :             : 
     123   [ #  #  #  # ]:           0 :   if (valent_packet_get_array (packet, "multiPaths", &multi_paths) &&
     124                 :           0 :       valent_packet_get_array (packet, "pathNames", &path_names))
     125                 :             :     {
     126                 :           0 :       unsigned int n_paths = json_array_get_length (multi_paths);
     127                 :           0 :       unsigned int n_names = json_array_get_length (path_names);
     128                 :             : 
     129         [ #  # ]:           0 :       for (unsigned int i = 0; i < n_paths && i < n_names; i++)
     130                 :             :         {
     131                 :           0 :           const char *path = json_array_get_string_element (multi_paths, i);
     132                 :           0 :           const char *name = json_array_get_string_element (path_names, i);
     133                 :           0 :           g_autofree char *uri = NULL;
     134                 :             : 
     135                 :           0 :           uri = g_strdup_printf ("sftp://%s:%u%s",
     136                 :             :                                  session->host,
     137                 :             :                                  session->port,
     138                 :             :                                  path);
     139         [ #  # ]:           0 :           g_hash_table_replace (session->paths,
     140                 :             :                                 g_steal_pointer (&uri),
     141                 :           0 :                                 g_strdup (name));
     142                 :             :         }
     143                 :             :     }
     144                 :             : 
     145                 :             :   return session;
     146                 :             : }
     147                 :             : 
     148                 :             : static void
     149                 :           0 : sftp_session_free (gpointer data)
     150                 :             : {
     151                 :           0 :   ValentSftpSession *session = data;
     152                 :             : 
     153         [ #  # ]:           0 :   g_clear_pointer (&session->host, g_free);
     154         [ #  # ]:           0 :   g_clear_pointer (&session->username, g_free);
     155         [ #  # ]:           0 :   g_clear_pointer (&session->password, g_free);
     156         [ #  # ]:           0 :   g_clear_pointer (&session->paths, g_hash_table_unref);
     157                 :             : 
     158         [ #  # ]:           0 :   g_clear_object (&session->mount);
     159         [ #  # ]:           0 :   g_clear_pointer (&session->uri, g_free);
     160                 :             : 
     161                 :           0 :   g_free (session);
     162                 :           0 : }
     163                 :             : 
     164                 :             : static void
     165                 :           0 : sftp_session_end_cb (GMount       *mount,
     166                 :             :                      GAsyncResult *result,
     167                 :             :                      gpointer      user_data)
     168                 :             : {
     169                 :           0 :   g_autoptr (GError) error = NULL;
     170                 :             : 
     171         [ #  # ]:           0 :   if (!g_mount_unmount_with_operation_finish (mount, result, &error))
     172                 :           0 :     g_debug ("Failed unmounting: %s", error->message);
     173                 :           0 : }
     174                 :             : 
     175                 :             : static void
     176                 :           0 : sftp_session_end (gpointer data)
     177                 :             : {
     178                 :           0 :   ValentSftpSession *session = data;
     179                 :           0 :   g_autoptr (GMount) mount = NULL;
     180                 :           0 :   g_autoptr (GMountOperation) op = NULL;
     181                 :             : 
     182                 :           0 :   mount = g_steal_pointer (&session->mount);
     183                 :           0 :   sftp_session_free (session);
     184                 :             : 
     185         [ #  # ]:           0 :   if (mount == NULL)
     186                 :             :     return;
     187                 :             : 
     188                 :           0 :   op = g_mount_operation_new ();
     189         [ #  # ]:           0 :   g_mount_unmount_with_operation (mount,
     190                 :             :                                   G_MOUNT_UNMOUNT_FORCE,
     191                 :             :                                   op,
     192                 :             :                                   NULL,
     193                 :             :                                   (GAsyncReadyCallback)sftp_session_end_cb,
     194                 :             :                                   NULL);
     195                 :             : }
     196                 :             : 
     197                 :             : static gboolean
     198                 :          15 : sftp_session_find (ValentSftpPlugin *self)
     199                 :             : {
     200                 :          30 :   g_autofree char *host = NULL;
     201                 :          15 :   g_autofree char *host_pattern = NULL;
     202                 :          15 :   g_autoptr (GRegex) regex = NULL;
     203         [ +  + ]:          15 :   g_autolist (GMount) mounts = NULL;
     204                 :             : 
     205   [ -  +  -  - ]:          15 :   if (self->session && self->session->mount)
     206                 :             :     return TRUE;
     207                 :             : 
     208         [ +  + ]:          15 :   if ((host = get_device_host (self)) == NULL)
     209                 :             :     return FALSE;
     210                 :             : 
     211                 :             :   // TODO: is this reasonable?
     212                 :           4 :   host_pattern = g_strdup_printf ("sftp://(%s):([22-65535])", host);
     213                 :           4 :   regex = g_regex_new (host_pattern, G_REGEX_OPTIMIZE, 0, NULL);
     214                 :             : 
     215                 :             :   /* Search through each mount in the volume monitor... */
     216                 :           4 :   mounts = g_volume_monitor_get_mounts (self->monitor);
     217                 :             : 
     218         [ -  + ]:           4 :   for (const GList *iter = mounts; iter; iter = iter->next)
     219                 :             :     {
     220                 :          15 :       g_autoptr (GFile) root = NULL;
     221   [ #  #  #  # ]:           0 :       g_autofree char *uri = NULL;
     222                 :             : 
     223                 :           0 :       root = g_mount_get_root (iter->data);
     224                 :           0 :       uri = g_file_get_uri (root);
     225                 :             : 
     226                 :             :       /* The URI matches our mount */
     227         [ #  # ]:           0 :       if (g_regex_match (regex, uri, 0, NULL))
     228                 :             :         {
     229         [ #  # ]:           0 :           if (self->session == NULL)
     230                 :           0 :             self->session = g_new0 (ValentSftpSession, 1);
     231                 :             : 
     232                 :           0 :           g_set_object (&self->session->mount, iter->data);
     233                 :           0 :           g_set_str (&self->session->uri, uri);
     234                 :             : 
     235                 :           0 :           valent_sftp_plugin_update_menu (self);
     236                 :           0 :           return TRUE;
     237                 :             :         }
     238                 :             :     }
     239                 :             : 
     240                 :             :   return FALSE;
     241                 :             : }
     242                 :             : 
     243                 :             : 
     244                 :             : /*
     245                 :             :  * GVolumeMonitor Callbacks
     246                 :             :  */
     247                 :             : static void
     248                 :           0 : on_mount_added (GVolumeMonitor   *volume_monitor,
     249                 :             :                 GMount           *mount,
     250                 :             :                 ValentSftpPlugin *self)
     251                 :             : {
     252                 :           0 :   g_autoptr (GFile) root = NULL;
     253         [ #  # ]:           0 :   g_autofree char *uri = NULL;
     254                 :             : 
     255         [ #  # ]:           0 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     256                 :             : 
     257         [ #  # ]:           0 :   if (self->session == NULL)
     258                 :           0 :     return;
     259                 :             : 
     260                 :           0 :   root = g_mount_get_root (mount);
     261                 :           0 :   uri = g_file_get_uri (root);
     262                 :             : 
     263         [ #  # ]:           0 :   if (g_strcmp0 (self->session->uri, uri) == 0)
     264                 :             :     {
     265                 :           0 :       g_set_object (&self->session->mount, mount);
     266                 :           0 :       valent_sftp_plugin_update_menu (self);
     267                 :             :     }
     268                 :             : }
     269                 :             : 
     270                 :             : static void
     271                 :           0 : on_mount_removed (GVolumeMonitor   *volume_monitor,
     272                 :             :                   GMount           *mount,
     273                 :             :                   ValentSftpPlugin *self)
     274                 :             : {
     275                 :           0 :   g_autoptr (GFile) root = NULL;
     276         [ #  # ]:           0 :   g_autofree char *uri = NULL;
     277                 :             : 
     278         [ #  # ]:           0 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     279                 :             : 
     280         [ #  # ]:           0 :   if (self->session == NULL)
     281                 :           0 :     return;
     282                 :             : 
     283                 :           0 :   root = g_mount_get_root (mount);
     284                 :           0 :   uri = g_file_get_uri (root);
     285                 :             : 
     286         [ #  # ]:           0 :   if (g_strcmp0 (self->session->uri, uri) == 0)
     287                 :             :     {
     288         [ #  # ]:           0 :       g_clear_object (&self->session->mount);
     289                 :           0 :       valent_sftp_plugin_update_menu (self);
     290                 :             :     }
     291                 :             : }
     292                 :             : 
     293                 :             : /**
     294                 :             :  * remove_host_key:
     295                 :             :  * @host: An IP or hostname
     296                 :             :  *
     297                 :             :  * Remove all host keys associated with @host from the ssh-agent.
     298                 :             :  */
     299                 :             : static void
     300                 :           0 : remove_host_key (const char *host)
     301                 :             : {
     302         [ #  # ]:           0 :   g_assert (host != NULL);
     303                 :             : 
     304         [ #  # ]:           0 :   for (uint16_t port = 1739; port <= 1764; port++)
     305                 :             :     {
     306                 :           0 :       g_autoptr (GSubprocess) proc = NULL;
     307         [ #  # ]:           0 :       g_autofree char *match = NULL;
     308                 :             : 
     309                 :           0 :       match = g_strdup_printf ("[%s]:%d", host, port);
     310                 :           0 :       proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE |
     311                 :             :                                G_SUBPROCESS_FLAGS_STDERR_MERGE,
     312                 :             :                                NULL,
     313                 :             :                                "ssh-keygen", "-R", match,
     314                 :             :                                NULL);
     315                 :             : 
     316         [ #  # ]:           0 :       if (proc != NULL)
     317                 :           0 :         g_subprocess_wait_async (proc, NULL, NULL, NULL);
     318                 :             :     }
     319                 :           0 : }
     320                 :             : 
     321                 :             : /*
     322                 :             :  * GMountOperation
     323                 :             :  */
     324                 :             : static void
     325                 :           0 : ask_password_cb (GMountOperation   *operation,
     326                 :             :                  char              *message,
     327                 :             :                  char              *default_user,
     328                 :             :                  char              *default_domain,
     329                 :             :                  GAskPasswordFlags  flags,
     330                 :             :                  ValentSftpPlugin  *self)
     331                 :             : {
     332         [ #  # ]:           0 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     333         [ #  # ]:           0 :   g_return_if_fail (self->session != NULL);
     334                 :             : 
     335                 :             :   /* The username/password are only set in response to a request, to prefer
     336                 :             :    * public key authentication when possible.
     337                 :             :    */
     338         [ #  # ]:           0 :   if ((flags & G_ASK_PASSWORD_NEED_USERNAME) != 0)
     339                 :           0 :     g_mount_operation_set_username (operation, self->session->username);
     340                 :             : 
     341         [ #  # ]:           0 :   if ((flags & G_ASK_PASSWORD_NEED_PASSWORD) != 0)
     342                 :             :     {
     343                 :           0 :       g_mount_operation_set_password (operation, self->session->password);
     344                 :           0 :       g_mount_operation_set_password_save (operation, G_PASSWORD_SAVE_NEVER);
     345                 :             :     }
     346                 :             : 
     347                 :           0 :   g_mount_operation_reply (operation, G_MOUNT_OPERATION_HANDLED);
     348                 :             : }
     349                 :             : 
     350                 :             : static void
     351                 :           0 : ask_question_cb (GMountOperation *operation,
     352                 :             :                  char            *message,
     353                 :             :                  GStrv            choices,
     354                 :             :                  gpointer         user_data)
     355                 :             : {
     356                 :             :   /* Host keys are automatically accepted, since we use the host address of
     357                 :             :    * the authenticated device connection.
     358                 :             :    */
     359                 :           0 :   g_mount_operation_reply (operation, G_MOUNT_OPERATION_HANDLED);
     360                 :           0 : }
     361                 :             : 
     362                 :             : static void
     363                 :           0 : g_file_mount_enclosing_volume_cb (GFile        *file,
     364                 :             :                                   GAsyncResult *result,
     365                 :             :                                   gpointer      user_data)
     366                 :             : {
     367                 :           0 :   g_autoptr (ValentSftpPlugin) self = VALENT_SFTP_PLUGIN (g_steal_pointer (&user_data));
     368                 :           0 :   g_autoptr (GError) error = NULL;
     369                 :             : 
     370         [ #  # ]:           0 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     371         [ #  # ]:           0 :   g_return_if_fail (self->session != NULL);
     372                 :             : 
     373                 :             :   /* On success we will acquire the mount from the volume monitor */
     374         [ #  # ]:           0 :   if (g_file_mount_enclosing_volume_finish (file, result, &error))
     375                 :             :     return;
     376                 :             : 
     377                 :             :   /* On the off-chance this happens, we will just ensure we have the mount */
     378   [ #  #  #  # ]:           0 :   if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED) &&
     379                 :           0 :       sftp_session_find (self))
     380                 :             :     return;
     381                 :             : 
     382                 :             :   /* On failure, we're particularly interested in host key failures so that
     383                 :             :    * we can remove those from the ssh-agent. These are reported by GVfs as
     384                 :             :    * G_IO_ERROR_FAILED with a localized string, so we just assume. */
     385         [ #  # ]:           0 :   if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED))
     386                 :             :     {
     387                 :           0 :       g_warning ("%s(): Error mounting: %s", G_STRFUNC, error->message);
     388                 :             : 
     389   [ #  #  #  # ]:           0 :       if (self->session && self->session->host)
     390                 :           0 :         remove_host_key (self->session->host);
     391                 :             :     }
     392                 :             : 
     393   [ #  #  #  # ]:           0 :   g_clear_pointer (&self->session, sftp_session_free);
     394                 :             : }
     395                 :             : 
     396                 :             : static void
     397                 :           0 : valent_sftp_plugin_mount_volume (ValentSftpPlugin *self)
     398                 :             : {
     399                 :           0 :   g_autoptr (GFile) file = NULL;
     400         [ #  # ]:           0 :   g_autoptr (GMountOperation) operation = NULL;
     401         [ #  # ]:           0 :   g_autoptr (GCancellable) destroy = NULL;
     402                 :             : 
     403         [ #  # ]:           0 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     404         [ #  # ]:           0 :   g_return_if_fail (self->session != NULL);
     405                 :             : 
     406                 :           0 :   file = g_file_new_for_uri (self->session->uri);
     407                 :           0 :   operation = g_mount_operation_new ();
     408                 :           0 :   g_signal_connect_object (operation,
     409                 :             :                            "ask-password",
     410                 :             :                            G_CALLBACK (ask_password_cb),
     411                 :             :                            self,
     412                 :             :                            G_CONNECT_DEFAULT);
     413                 :           0 :   g_signal_connect_object (operation,
     414                 :             :                            "ask-question",
     415                 :             :                            G_CALLBACK (ask_question_cb),
     416                 :             :                            self,
     417                 :             :                            G_CONNECT_DEFAULT);
     418                 :             : 
     419                 :           0 :   destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
     420         [ #  # ]:           0 :   g_file_mount_enclosing_volume (file,
     421                 :             :                                  G_MOUNT_MOUNT_NONE,
     422                 :             :                                  operation,
     423                 :             :                                  destroy,
     424                 :             :                                  (GAsyncReadyCallback)g_file_mount_enclosing_volume_cb,
     425                 :             :                                  g_object_ref (self));
     426                 :             : 
     427                 :             : }
     428                 :             : 
     429                 :             : static void
     430                 :           0 : g_mount_unmount_with_operation_cb (GMount       *mount,
     431                 :             :                                    GAsyncResult *result,
     432                 :             :                                    gpointer      user_data)
     433                 :             : {
     434                 :           0 :   g_autoptr (GError) error = NULL;
     435                 :             : 
     436         [ #  # ]:           0 :   if (!g_mount_unmount_with_operation_finish (mount, result, &error))
     437                 :             :     {
     438         [ #  # ]:           0 :       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     439                 :           0 :         g_warning ("%s(): Error unmounting: %s", G_STRFUNC, error->message);
     440                 :             : 
     441         [ #  # ]:           0 :       return;
     442                 :             :     }
     443                 :             : }
     444                 :             : 
     445                 :             : static void
     446                 :           0 : valent_sftp_plugin_unmount_volume (ValentSftpPlugin *self)
     447                 :             : {
     448                 :           0 :   g_autoptr (GMountOperation) operation = NULL;
     449         [ #  # ]:           0 :   g_autoptr (GCancellable) destroy = NULL;
     450                 :             : 
     451         [ #  # ]:           0 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     452         [ #  # ]:           0 :   g_return_if_fail (self->session != NULL);
     453                 :             : 
     454         [ #  # ]:           0 :   if (self->session->mount == NULL)
     455                 :             :     return;
     456                 :             : 
     457                 :           0 :   operation = g_mount_operation_new ();
     458                 :           0 :   destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
     459         [ #  # ]:           0 :   g_mount_unmount_with_operation (self->session->mount,
     460                 :             :                                   G_MOUNT_UNMOUNT_NONE,
     461                 :             :                                   operation,
     462                 :             :                                   destroy,
     463                 :             :                                   (GAsyncReadyCallback)g_mount_unmount_with_operation_cb,
     464                 :             :                                   NULL);
     465                 :             : }
     466                 :             : 
     467                 :             : static void
     468                 :           0 : valent_sftp_plugin_register_privkey_cb (GSubprocess  *proc,
     469                 :             :                                         GAsyncResult *result,
     470                 :             :                                         gpointer      user_data)
     471                 :             : {
     472                 :           0 :   g_autoptr (ValentSftpPlugin) self = VALENT_SFTP_PLUGIN (g_steal_pointer (&user_data));
     473                 :           0 :   g_autoptr (GError) error = NULL;
     474                 :             : 
     475         [ #  # ]:           0 :   if (!g_subprocess_wait_check_finish (proc, result, &error))
     476                 :             :     {
     477         [ #  # ]:           0 :       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     478                 :             :         {
     479                 :           0 :           g_warning ("%s(): Failed to register private key: %s",
     480                 :             :                      G_STRFUNC,
     481                 :             :                      error->message);
     482                 :             :         }
     483                 :             : 
     484         [ #  # ]:           0 :       g_clear_pointer (&self->session, sftp_session_free);
     485         [ #  # ]:           0 :       return;
     486                 :             :     }
     487                 :             : 
     488                 :           0 :   self->privkey_ready = TRUE;
     489         [ #  # ]:           0 :   valent_sftp_plugin_mount_volume (self);
     490                 :             : }
     491                 :             : 
     492                 :             : static void
     493                 :           0 : valent_sftp_plugin_register_privkey (ValentSftpPlugin  *self)
     494                 :             : {
     495                 :           0 :   g_autoptr (ValentContext) context = NULL;
     496   [ #  #  #  # ]:           0 :   g_autoptr (GSubprocess) proc = NULL;
     497         [ #  # ]:           0 :   g_autoptr (GFile) private_key = NULL;
     498   [ #  #  #  # ]:           0 :   g_autoptr (GCancellable) destroy = NULL;
     499   [ #  #  #  # ]:           0 :   g_autoptr (GError) error = NULL;
     500                 :             : 
     501         [ #  # ]:           0 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     502         [ #  # ]:           0 :   g_return_if_fail (self->session != NULL);
     503                 :             : 
     504                 :             :   /* Get the root context and add the private key to the ssh-agent */
     505                 :           0 :   context = valent_context_new (NULL, NULL, NULL);
     506                 :           0 :   private_key = valent_context_get_config_file (context, "private.pem");
     507                 :           0 :   proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE |
     508                 :             :                            G_SUBPROCESS_FLAGS_STDERR_MERGE,
     509                 :             :                            &error,
     510                 :             :                            "ssh-add", g_file_peek_path (private_key),
     511                 :             :                            NULL);
     512                 :             : 
     513         [ #  # ]:           0 :   if (proc == NULL)
     514                 :             :     {
     515                 :           0 :       g_warning ("%s(): Failed to add host key: %s", G_STRFUNC, error->message);
     516         [ #  # ]:           0 :       g_clear_pointer (&self->session, sftp_session_free);
     517                 :           0 :       return;
     518                 :             :     }
     519                 :             : 
     520                 :           0 :   destroy = valent_object_ref_cancellable (VALENT_OBJECT (self));
     521         [ #  # ]:           0 :   g_subprocess_wait_check_async (proc,
     522                 :             :                                  destroy,
     523                 :             :                                  (GAsyncReadyCallback)valent_sftp_plugin_register_privkey_cb,
     524                 :             :                                  g_object_ref (self));
     525                 :             : }
     526                 :             : 
     527                 :             : /*
     528                 :             :  * Packet Handlers
     529                 :             :  */
     530                 :             : static void
     531                 :           1 : handle_sftp_error (ValentSftpPlugin *self,
     532                 :             :                    JsonNode         *packet)
     533                 :             : {
     534                 :           1 :   ValentDevice *device;
     535                 :           2 :   g_autoptr (GNotification) notification = NULL;
     536         [ +  - ]:           1 :   g_autoptr (GIcon) error_icon = NULL;
     537         [ +  - ]:           1 :   g_autofree char *error_title = NULL;
     538                 :           1 :   const char *error_message;
     539                 :           1 :   const char *device_name;
     540                 :           1 :   JsonObject *body;
     541                 :             : 
     542         [ +  - ]:           1 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     543                 :             : 
     544                 :           1 :   body = valent_packet_get_body (packet);
     545                 :             : 
     546                 :           1 :   device = valent_resource_get_source (VALENT_RESOURCE (self));
     547                 :           1 :   device_name = valent_device_get_name (device);
     548                 :             : 
     549                 :           1 :   error_icon = g_themed_icon_new ("dialog-error-symbolic");
     550                 :           1 :   error_title = g_strdup_printf ("%s: SFTP", device_name);
     551                 :           1 :   error_message = json_object_get_string_member (body, "errorMessage");
     552                 :             : 
     553                 :             :   /* Send notification */
     554                 :           1 :   notification = g_notification_new (error_title);
     555                 :           1 :   g_notification_set_body (notification, error_message);
     556                 :           1 :   g_notification_set_icon (notification, error_icon);
     557                 :           1 :   g_notification_set_priority (notification, G_NOTIFICATION_PRIORITY_HIGH);
     558                 :           1 :   valent_device_plugin_show_notification (VALENT_DEVICE_PLUGIN (self),
     559                 :             :                                           "sftp-error",
     560                 :             :                                           notification);
     561                 :           1 : }
     562                 :             : 
     563                 :             : static void
     564                 :           0 : handle_sftp_mount (ValentSftpPlugin *self,
     565                 :             :                    JsonNode         *packet)
     566                 :             : {
     567         [ #  # ]:           0 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     568                 :             : 
     569                 :             :   /* Check if we're already mounted or mounting */
     570         [ #  # ]:           0 :   if (self->session != NULL)
     571                 :             :     return;
     572                 :             : 
     573                 :             :   /* Parse the connection data */
     574                 :           0 :   self->session = sftp_session_new (self, packet);
     575         [ #  # ]:           0 :   if (self->session != NULL)
     576                 :             :     {
     577         [ #  # ]:           0 :       if (self->privkey_ready)
     578                 :           0 :         valent_sftp_plugin_mount_volume (self);
     579                 :             :       else
     580                 :           0 :         valent_sftp_plugin_register_privkey (self);
     581                 :             :     }
     582                 :             : }
     583                 :             : 
     584                 :             : static void
     585                 :           1 : valent_sftp_plugin_handle_sftp (ValentSftpPlugin *self,
     586                 :             :                                 JsonNode         *packet)
     587                 :             : {
     588         [ +  - ]:           1 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     589                 :             : 
     590                 :             :   /* The request for mount information failed, most likely due to the remote
     591                 :             :    * device not being setup yet.
     592                 :             :    */
     593         [ +  - ]:           1 :   if (valent_packet_check_field (packet, "errorMessage"))
     594                 :           1 :     handle_sftp_error (self, packet);
     595                 :             : 
     596                 :             :   /* Otherwise we've been sent the information necessary to open an SSH/SFTP
     597                 :             :    * connection to the remote device.
     598                 :             :    */
     599                 :             :   else
     600                 :           0 :     handle_sftp_mount (self, packet);
     601                 :           1 : }
     602                 :             : 
     603                 :             : /*
     604                 :             :  * Packet Providers
     605                 :             :  */
     606                 :             : static void
     607                 :           1 : valent_sftp_plugin_handle_request (ValentSftpPlugin *self,
     608                 :             :                                    JsonNode         *packet)
     609                 :             : {
     610                 :           1 :   GSettings *settings;
     611                 :           1 :   g_autoptr (JsonBuilder) builder = NULL;
     612   [ -  -  -  + ]:           1 :   g_autoptr (JsonNode) response = NULL;
     613                 :             : 
     614         [ +  - ]:           1 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     615                 :             : 
     616         [ -  + ]:           1 :   if (!valent_packet_check_field (packet, "startBrowsing"))
     617         [ #  # ]:           0 :     return;
     618                 :             : 
     619                 :           1 :   settings = valent_extension_get_settings (VALENT_EXTENSION (self));
     620                 :           1 :   valent_packet_init (&builder, "kdeconnect.sftp");
     621                 :             : 
     622         [ -  + ]:           1 :   if (g_settings_get_boolean (settings, "local-allow"))
     623                 :             :     {
     624                 :           0 :       uint16_t local_port;
     625                 :             : 
     626                 :           0 :       json_builder_set_member_name (builder, "user");
     627                 :           0 :       json_builder_add_string_value (builder, g_get_user_name ());
     628                 :             : 
     629                 :           0 :       local_port = g_settings_get_uint (settings, "local-port");
     630                 :           0 :       json_builder_set_member_name (builder, "port");
     631                 :           0 :       json_builder_add_int_value (builder, local_port);
     632                 :             : 
     633                 :           0 :       json_builder_set_member_name (builder, "multiPaths");
     634                 :           0 :       json_builder_begin_array (builder);
     635                 :           0 :       json_builder_add_string_value (builder, g_get_home_dir ());
     636                 :           0 :       json_builder_end_array (builder);
     637                 :             : 
     638                 :           0 :       json_builder_set_member_name (builder, "pathNames");
     639                 :           0 :       json_builder_begin_array (builder);
     640                 :           0 :       json_builder_add_string_value (builder, _("Home"));
     641                 :           0 :       json_builder_end_array (builder);
     642                 :             :     }
     643                 :             :   else
     644                 :             :     {
     645                 :           1 :       json_builder_set_member_name (builder, "errorMessage");
     646                 :           1 :       json_builder_add_string_value (builder, _("Permission denied"));
     647                 :             :     }
     648                 :             : 
     649                 :           1 :   response = valent_packet_end (&builder);
     650         [ +  - ]:           1 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), response);
     651                 :             : }
     652                 :             : 
     653                 :             : static void
     654                 :           1 : valent_sftp_plugin_sftp_request (ValentSftpPlugin *self)
     655                 :             : {
     656                 :           1 :   g_autoptr (JsonBuilder) builder = NULL;
     657   [ -  -  -  + ]:           1 :   g_autoptr (JsonNode) packet = NULL;
     658                 :             : 
     659         [ +  - ]:           1 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     660                 :             : 
     661         [ -  + ]:           1 :   if (sftp_session_find (self))
     662         [ #  # ]:           0 :     return;
     663                 :             : 
     664                 :           1 :   valent_packet_init (&builder, "kdeconnect.sftp.request");
     665                 :           1 :   json_builder_set_member_name (builder, "startBrowsing");
     666                 :           1 :   json_builder_add_boolean_value (builder, TRUE);
     667                 :           1 :   packet = valent_packet_end (&builder);
     668                 :             : 
     669         [ +  - ]:           1 :   valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
     670                 :             : }
     671                 :             : 
     672                 :             : /*
     673                 :             :  * GActions
     674                 :             :  */
     675                 :             : static void
     676                 :           0 : valent_sftp_plugin_open_uri (ValentSftpPlugin *self,
     677                 :             :                              const char       *uri)
     678                 :             : {
     679         [ #  # ]:           0 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     680         [ #  # ]:           0 :   g_assert (uri != NULL);
     681                 :             : 
     682         [ #  # ]:           0 :   if (self->connection != NULL)
     683                 :             :     {
     684                 :           0 :       g_dbus_connection_call (self->connection,
     685                 :             :                               "org.freedesktop.FileManager1",
     686                 :             :                               "/org/freedesktop/FileManager1",
     687                 :             :                               "org.freedesktop.FileManager1",
     688                 :             :                               "ShowFolders",
     689                 :             :                               g_variant_new_parsed ("([%s], '')", uri),
     690                 :             :                               NULL,
     691                 :             :                               G_DBUS_CALL_FLAGS_NONE,
     692                 :             :                               -1,
     693                 :             :                               NULL, NULL, NULL);
     694                 :             :     }
     695                 :             :   else
     696                 :             :     {
     697                 :           0 :       g_app_info_launch_default_for_uri_async (uri, NULL, NULL, NULL, NULL);
     698                 :             :     }
     699                 :           0 : }
     700                 :             : 
     701                 :             : static void
     702                 :           1 : mount_action (GSimpleAction *action,
     703                 :             :               GVariant      *parameter,
     704                 :             :               gpointer       user_data)
     705                 :             : {
     706                 :           1 :   ValentSftpPlugin *self = VALENT_SFTP_PLUGIN (user_data);
     707                 :             : 
     708         [ +  - ]:           1 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     709                 :             : 
     710         [ +  - ]:           1 :   if (self->session == NULL)
     711                 :           1 :     valent_sftp_plugin_sftp_request (self);
     712         [ #  # ]:           0 :   else if (self->session->mount == NULL)
     713                 :           0 :     valent_sftp_plugin_mount_volume (self);
     714                 :           1 : }
     715                 :             : 
     716                 :             : static void
     717                 :           0 : unmount_action (GSimpleAction *action,
     718                 :             :                 GVariant      *parameter,
     719                 :             :                 gpointer       user_data)
     720                 :             : {
     721                 :           0 :   ValentSftpPlugin *self = VALENT_SFTP_PLUGIN (user_data);
     722                 :             : 
     723         [ #  # ]:           0 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     724                 :             : 
     725   [ #  #  #  # ]:           0 :   if (self->session != NULL && self->session->mount != NULL)
     726                 :           0 :     valent_sftp_plugin_unmount_volume (self);
     727                 :           0 : }
     728                 :             : 
     729                 :             : static void
     730                 :           0 : open_uri_action (GSimpleAction *action,
     731                 :             :                  GVariant      *parameter,
     732                 :             :                  gpointer       user_data)
     733                 :             : {
     734                 :           0 :   ValentSftpPlugin *self = VALENT_SFTP_PLUGIN (user_data);
     735                 :           0 :   const char *uri = NULL;
     736                 :             : 
     737         [ #  # ]:           0 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     738                 :             : 
     739                 :           0 :   uri = g_variant_get_string (parameter, NULL);
     740                 :           0 :   valent_sftp_plugin_open_uri (self, uri);
     741                 :           0 : }
     742                 :             : 
     743                 :             : static const GActionEntry actions[] = {
     744                 :             :     {"browse",   mount_action,    NULL, NULL, NULL},
     745                 :             :     {"unmount",  unmount_action,  NULL, NULL, NULL},
     746                 :             :     {"open-uri", open_uri_action, "s",  NULL, NULL},
     747                 :             : };
     748                 :             : 
     749                 :             : /*
     750                 :             :  * ValentSftpPlugin
     751                 :             :  */
     752                 :             : static void
     753                 :          14 : valent_sftp_plugin_update_menu (ValentSftpPlugin *self)
     754                 :             : {
     755                 :          14 :   ValentDevicePlugin *plugin = VALENT_DEVICE_PLUGIN (self);
     756                 :             : 
     757         [ +  - ]:          14 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     758                 :             : 
     759         [ -  + ]:          14 :   if (sftp_session_find (self))
     760                 :             :     {
     761                 :           0 :       GHashTableIter iter;
     762                 :           0 :       const char *uri = NULL;
     763                 :           0 :       const char *name = NULL;
     764                 :           0 :       g_autoptr (GMenuItem) item = NULL;
     765         [ #  # ]:           0 :       g_autoptr (GIcon) icon = NULL;
     766         [ #  # ]:           0 :       g_autoptr (GMenu) submenu = NULL;
     767         [ #  # ]:           0 :       g_autoptr (GMenuItem) unmount_item = NULL;
     768         [ #  # ]:           0 :       g_autoptr (GIcon) unmount_icon = NULL;
     769                 :             : 
     770                 :           0 :       icon = g_themed_icon_new ("folder-remote-symbolic");
     771                 :           0 :       item = g_menu_item_new (_("Browse Folders"), "device.sftp.browse");
     772                 :           0 :       g_menu_item_set_icon (item, icon);
     773                 :           0 :       g_menu_item_set_attribute (item, "hidden-when", "s", "action-disabled");
     774                 :             : 
     775                 :           0 :       submenu = g_menu_new ();
     776                 :           0 :       g_menu_item_set_submenu (item, G_MENU_MODEL (submenu));
     777                 :             : 
     778                 :           0 :       g_hash_table_iter_init (&iter, self->session->paths);
     779         [ #  # ]:           0 :       while (g_hash_table_iter_next (&iter, (void **)&uri, (void **)&name))
     780                 :             :         {
     781                 :           0 :           g_autofree char *action_name = NULL;
     782                 :             : 
     783                 :           0 :           action_name = g_strdup_printf ("device.sftp.open-uri::%s", uri);
     784                 :           0 :           g_menu_append (submenu, name, action_name);
     785                 :             :         }
     786                 :             : 
     787                 :           0 :       unmount_icon = g_themed_icon_new ("media-eject-symbolic");
     788                 :           0 :       unmount_item = g_menu_item_new (_("Unmount"), "device.sftp.unmount");
     789                 :           0 :       g_menu_item_set_icon (unmount_item, unmount_icon);
     790                 :           0 :       g_menu_append_item (submenu, unmount_item);
     791                 :             : 
     792         [ #  # ]:           0 :       valent_device_plugin_set_menu_item (plugin, "device.sftp.browse", item);
     793                 :             :     }
     794                 :             :   else
     795                 :             :     {
     796                 :          14 :       valent_device_plugin_set_menu_action (plugin,
     797                 :             :                                             "device.sftp.browse",
     798                 :          14 :                                             _("Mount"),
     799                 :             :                                             "folder-remote-symbolic");
     800                 :             :     }
     801                 :          14 : }
     802                 :             : 
     803                 :             : /*
     804                 :             :  * ValentDevicePlugin
     805                 :             :  */
     806                 :             : static void
     807                 :          14 : valent_sftp_plugin_update_state (ValentDevicePlugin *plugin,
     808                 :             :                                  ValentDeviceState   state)
     809                 :             : {
     810                 :          14 :   ValentSftpPlugin *self = VALENT_SFTP_PLUGIN (plugin);
     811                 :          14 :   gboolean available;
     812                 :             : 
     813         [ +  - ]:          14 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     814                 :             : 
     815                 :          14 :   available = (state & VALENT_DEVICE_STATE_CONNECTED) != 0 &&
     816                 :             :               (state & VALENT_DEVICE_STATE_PAIRED) != 0;
     817                 :             : 
     818                 :          14 :   valent_extension_toggle_actions (VALENT_EXTENSION (plugin), available);
     819                 :             : 
     820                 :          14 :   valent_sftp_plugin_update_menu (self);
     821                 :             : 
     822         [ +  + ]:          14 :   if (available)
     823                 :             :     {
     824                 :           3 :       GSettings *settings;
     825                 :             : 
     826                 :           3 :       settings = valent_extension_get_settings (VALENT_EXTENSION (plugin));
     827         [ -  + ]:           3 :       if (g_settings_get_boolean (settings, "auto-mount"))
     828                 :           0 :         valent_sftp_plugin_sftp_request (self);
     829                 :             :     }
     830         [ +  + ]:          11 :   else if ((state & VALENT_DEVICE_STATE_PAIRED) == 0)
     831                 :             :     {
     832         [ -  + ]:           4 :       g_clear_pointer (&self->session, sftp_session_end);
     833                 :             :     }
     834         [ +  - ]:           7 :   else if ((state & VALENT_DEVICE_STATE_CONNECTED) == 0)
     835                 :             :     {
     836         [ -  + ]:           7 :       g_clear_pointer (&self->session, sftp_session_free);
     837                 :             :     }
     838                 :          14 : }
     839                 :             : 
     840                 :             : static void
     841                 :           2 : valent_sftp_plugin_handle_packet (ValentDevicePlugin *plugin,
     842                 :             :                                   const char         *type,
     843                 :             :                                   JsonNode           *packet)
     844                 :             : {
     845                 :           2 :   ValentSftpPlugin *self = VALENT_SFTP_PLUGIN (plugin);
     846                 :             : 
     847         [ +  - ]:           2 :   g_assert (VALENT_IS_SFTP_PLUGIN (self));
     848         [ -  + ]:           2 :   g_assert (type != NULL);
     849         [ -  + ]:           2 :   g_assert (VALENT_IS_PACKET (packet));
     850                 :             : 
     851         [ +  + ]:           2 :   if (g_str_equal (type, "kdeconnect.sftp"))
     852                 :           1 :     valent_sftp_plugin_handle_sftp (self, packet);
     853                 :             : 
     854         [ +  - ]:           1 :   else if (g_str_equal (type, "kdeconnect.sftp.request"))
     855                 :           1 :     valent_sftp_plugin_handle_request (self, packet);
     856                 :             : 
     857                 :             :   else
     858                 :           0 :     g_warn_if_reached ();
     859                 :           2 : }
     860                 :             : 
     861                 :             : /*
     862                 :             :  * ValentObject
     863                 :             :  */
     864                 :             : static void
     865                 :           8 : valent_sftp_plugin_destroy (ValentObject *object)
     866                 :             : {
     867                 :           8 :   ValentSftpPlugin *self = VALENT_SFTP_PLUGIN (object);
     868                 :           8 :   ValentDevicePlugin *plugin = VALENT_DEVICE_PLUGIN (object);
     869                 :             : 
     870                 :             :   /* Stop watching the volume monitor and unmount any current session */
     871         [ +  + ]:           8 :   if (self->monitor != NULL)
     872                 :             :     {
     873                 :           4 :       g_signal_handlers_disconnect_by_data (self->monitor, self);
     874         [ +  - ]:           4 :       g_clear_object (&self->monitor);
     875                 :             :     }
     876         [ -  + ]:           8 :   g_clear_pointer (&self->session, sftp_session_end);
     877         [ +  + ]:           8 :   g_clear_object (&self->connection);
     878                 :             : 
     879                 :           8 :   valent_device_plugin_set_menu_item (plugin, "device.sftp.browse", NULL);
     880                 :             : 
     881                 :           8 :   VALENT_OBJECT_CLASS (valent_sftp_plugin_parent_class)->destroy (object);
     882                 :           8 : }
     883                 :             : 
     884                 :             : /*
     885                 :             :  * GObject
     886                 :             :  */
     887                 :             : static void
     888                 :           4 : valent_sftp_plugin_constructed (GObject *object)
     889                 :             : {
     890                 :           4 :   ValentSftpPlugin *self = VALENT_SFTP_PLUGIN (object);
     891                 :           4 :   ValentDevicePlugin *plugin = VALENT_DEVICE_PLUGIN (object);
     892                 :             : 
     893                 :           4 :   G_OBJECT_CLASS (valent_sftp_plugin_parent_class)->constructed (object);
     894                 :             : 
     895                 :           4 :   g_action_map_add_action_entries (G_ACTION_MAP (plugin),
     896                 :             :                                    actions,
     897                 :             :                                    G_N_ELEMENTS (actions),
     898                 :             :                                    plugin);
     899                 :             : 
     900                 :           4 :   self->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
     901                 :           4 :   self->monitor = g_volume_monitor_get ();
     902                 :           4 :   g_signal_connect_object (self->monitor,
     903                 :             :                            "mount-added",
     904                 :             :                            G_CALLBACK (on_mount_added),
     905                 :             :                            self,
     906                 :             :                            G_CONNECT_DEFAULT);
     907                 :           4 :   g_signal_connect_object (self->monitor,
     908                 :             :                            "mount-removed",
     909                 :             :                            G_CALLBACK (on_mount_removed),
     910                 :             :                            self,
     911                 :             :                            G_CONNECT_DEFAULT);
     912                 :           4 : }
     913                 :             : 
     914                 :             : static void
     915                 :          18 : valent_sftp_plugin_class_init (ValentSftpPluginClass *klass)
     916                 :             : {
     917                 :          18 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     918                 :          18 :   ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
     919                 :          18 :   ValentDevicePluginClass *plugin_class = VALENT_DEVICE_PLUGIN_CLASS (klass);
     920                 :             : 
     921                 :          18 :   object_class->constructed = valent_sftp_plugin_constructed;
     922                 :             : 
     923                 :          18 :   vobject_class->destroy = valent_sftp_plugin_destroy;
     924                 :             : 
     925                 :          18 :   plugin_class->handle_packet = valent_sftp_plugin_handle_packet;
     926                 :          18 :   plugin_class->update_state = valent_sftp_plugin_update_state;
     927                 :             : }
     928                 :             : 
     929                 :             : static void
     930                 :           4 : valent_sftp_plugin_init (ValentSftpPlugin *self)
     931                 :             : {
     932                 :           4 : }
     933                 :             : 
        

Generated by: LCOV version 2.0-1