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 : : GVolumeMonitor *monitor;
23 : : ValentSftpSession *session;
24 : : };
25 : :
26 [ + + + - ]: 72 : G_DEFINE_FINAL_TYPE (ValentSftpPlugin, valent_sftp_plugin, VALENT_TYPE_DEVICE_PLUGIN)
27 : :
28 : :
29 : : static char *
30 : 4 : get_device_host (ValentSftpPlugin *self)
31 : : {
32 : 4 : ValentDevice *device;
33 : 8 : g_autoptr (ValentChannel) channel = NULL;
34 [ + - ]: 4 : g_autofree char *host = NULL;
35 : 4 : GParamSpec *pspec = NULL;
36 : :
37 : : /* The plugin doesn't know ValentChannel derivations, so we have to check for
38 : : * a "host" property to ensure it's IP-based */
39 : 4 : device = valent_extension_get_object (VALENT_EXTENSION (self));
40 : 4 : channel = valent_device_ref_channel (device);
41 : :
42 [ + - ]: 4 : if G_LIKELY (channel != NULL)
43 : 4 : pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (channel), "host");
44 : :
45 [ + - ]: 4 : if G_LIKELY (pspec != NULL)
46 : 4 : g_object_get (channel, "host", &host, NULL);
47 : :
48 : 4 : return g_steal_pointer (&host);
49 : : }
50 : :
51 : : /**
52 : : * ValentSftpSession:
53 : : * @host: Host name or IP
54 : : * @port: Port
55 : : * @username: Username (deprecated)
56 : : * @password: Password (deprecated)
57 : : * @mount: A `GMount` for the session
58 : : *
59 : : * `ValentSftpSession` is a simple representation of a SFTP session.
60 : : */
61 : : typedef struct _ValentSftpSession
62 : : {
63 : : char *host;
64 : : uint16_t port;
65 : : char *username;
66 : : char *password;
67 : :
68 : : char *uri;
69 : : GMount *mount;
70 : : } ValentSftpSession;
71 : :
72 : :
73 : : static ValentSftpSession *
74 : 0 : sftp_session_new (ValentSftpPlugin *self,
75 : : JsonNode *packet)
76 : : {
77 : 0 : ValentSftpSession *session;
78 : 0 : g_autofree char *host = NULL;
79 : 0 : int64_t port;
80 : 0 : const char *password;
81 : 0 : const char *username;
82 : :
83 : : /* Ultimately, these are the only packet fields we really need */
84 [ # # ]: 0 : if (!valent_packet_get_int (packet, "port", &port) ||
85 [ # # ]: 0 : (port < 0 || port > G_MAXUINT16))
86 : : {
87 : 0 : g_debug ("%s(): expected \"port\" field holding a uint16",
88 : : G_STRFUNC);
89 : 0 : return NULL;
90 : : }
91 : :
92 [ # # ]: 0 : if ((host = get_device_host (self)) == NULL)
93 : : {
94 : 0 : g_warning ("%s(): failed to get host address",
95 : : G_STRFUNC);
96 : 0 : return NULL;
97 : : }
98 : :
99 : : // Create a session struct
100 : 0 : session = g_new0 (ValentSftpSession, 1);
101 : 0 : session->host = g_steal_pointer (&host);
102 : 0 : session->port = (uint16_t)port;
103 : :
104 [ # # ]: 0 : if (valent_packet_get_string (packet, "user", &username))
105 [ # # ]: 0 : session->username = g_strdup (username);
106 : :
107 [ # # ]: 0 : if (valent_packet_get_string (packet, "password", &password))
108 [ # # ]: 0 : session->password = g_strdup (password);
109 : :
110 : : // Gvfs
111 : 0 : session->uri = g_strdup_printf ("sftp://%s:%u/",
112 : : session->host,
113 : : session->port);
114 : :
115 : 0 : return session;
116 : : }
117 : :
118 : : static void
119 : 0 : sftp_session_free (gpointer data)
120 : : {
121 : 0 : ValentSftpSession *session = data;
122 : :
123 [ # # ]: 0 : g_clear_pointer (&session->host, g_free);
124 [ # # ]: 0 : g_clear_pointer (&session->username, g_free);
125 [ # # ]: 0 : g_clear_pointer (&session->password, g_free);
126 : :
127 [ # # ]: 0 : g_clear_object (&session->mount);
128 [ # # ]: 0 : g_clear_pointer (&session->uri, g_free);
129 : :
130 : 0 : g_free (session);
131 : 0 : }
132 : :
133 : : static void
134 : 0 : sftp_session_end_cb (GMount *mount,
135 : : GAsyncResult *result,
136 : : gpointer user_data)
137 : : {
138 : 0 : g_autoptr (GError) error = NULL;
139 : :
140 [ # # ]: 0 : if (!g_mount_unmount_with_operation_finish (mount, result, &error))
141 : 0 : g_debug ("Failed unmounting: %s", error->message);
142 : 0 : }
143 : :
144 : : static void
145 : 0 : sftp_session_end (gpointer data)
146 : : {
147 : 0 : ValentSftpSession *session = data;
148 : 0 : g_autoptr (GMount) mount = NULL;
149 : 0 : g_autoptr (GMountOperation) op = NULL;
150 : :
151 : 0 : mount = g_steal_pointer (&session->mount);
152 : 0 : sftp_session_free (session);
153 : :
154 [ # # ]: 0 : if (mount == NULL)
155 : : return;
156 : :
157 : 0 : op = g_mount_operation_new ();
158 [ # # ]: 0 : g_mount_unmount_with_operation (mount,
159 : : G_MOUNT_UNMOUNT_FORCE,
160 : : op,
161 : : NULL,
162 : : (GAsyncReadyCallback)sftp_session_end_cb,
163 : : NULL);
164 : : }
165 : :
166 : : static gboolean
167 : 4 : sftp_session_find (ValentSftpPlugin *self)
168 : : {
169 : 8 : g_autofree char *host = NULL;
170 : 4 : g_autofree char *host_pattern = NULL;
171 : 4 : g_autoptr (GRegex) regex = NULL;
172 [ + - ]: 4 : g_autolist (GMount) mounts = NULL;
173 : :
174 [ - + - - ]: 4 : if (self->session && self->session->mount)
175 : : return TRUE;
176 : :
177 [ + - ]: 4 : if ((host = get_device_host (self)) == NULL)
178 : : return FALSE;
179 : :
180 : : // TODO: is this reasonable?
181 : 4 : host_pattern = g_strdup_printf ("sftp://(%s):([22-65535])", host);
182 : 4 : regex = g_regex_new (host_pattern, G_REGEX_OPTIMIZE, 0, NULL);
183 : :
184 : : /* Search through each mount in the volume monitor... */
185 : 4 : mounts = g_volume_monitor_get_mounts (self->monitor);
186 : :
187 [ - + ]: 4 : for (const GList *iter = mounts; iter; iter = iter->next)
188 : : {
189 : 4 : g_autoptr (GFile) root = NULL;
190 [ # # # # ]: 0 : g_autofree char *uri = NULL;
191 : :
192 : 0 : root = g_mount_get_root (iter->data);
193 : 0 : uri = g_file_get_uri (root);
194 : :
195 : : /* The URI matches our mount */
196 [ # # ]: 0 : if (g_regex_match (regex, uri, 0, NULL))
197 : : {
198 [ # # ]: 0 : if (self->session == NULL)
199 : 0 : self->session = g_new0 (ValentSftpSession, 1);
200 : :
201 : 0 : g_set_object (&self->session->mount, iter->data);
202 : 0 : g_set_str (&self->session->uri, uri);
203 : :
204 : 0 : return TRUE;
205 : : }
206 : : }
207 : :
208 : : return FALSE;
209 : : }
210 : :
211 : :
212 : : /*
213 : : * GVolumeMonitor Callbacks
214 : : */
215 : : static void
216 : 0 : on_mount_added (GVolumeMonitor *volume_monitor,
217 : : GMount *mount,
218 : : ValentSftpPlugin *self)
219 : : {
220 : 0 : g_autoptr (GFile) root = NULL;
221 [ # # ]: 0 : g_autofree char *uri = NULL;
222 : :
223 [ # # ]: 0 : g_assert (VALENT_IS_SFTP_PLUGIN (self));
224 : :
225 [ # # ]: 0 : if (self->session == NULL)
226 : 0 : return;
227 : :
228 : 0 : root = g_mount_get_root (mount);
229 : 0 : uri = g_file_get_uri (root);
230 : :
231 [ # # ]: 0 : if (g_strcmp0 (self->session->uri, uri) == 0)
232 : 0 : g_set_object (&self->session->mount, mount);
233 : : }
234 : :
235 : : static void
236 : 0 : on_mount_removed (GVolumeMonitor *volume_monitor,
237 : : GMount *mount,
238 : : ValentSftpPlugin *self)
239 : : {
240 : 0 : g_autoptr (GFile) root = NULL;
241 [ # # ]: 0 : g_autofree char *uri = NULL;
242 : :
243 [ # # ]: 0 : g_assert (VALENT_IS_SFTP_PLUGIN (self));
244 : :
245 [ # # ]: 0 : if (self->session == NULL)
246 : 0 : return;
247 : :
248 : 0 : root = g_mount_get_root (mount);
249 : 0 : uri = g_file_get_uri (root);
250 : :
251 [ # # ]: 0 : if (g_strcmp0 (self->session->uri, uri) == 0)
252 [ # # ]: 0 : g_clear_pointer (&self->session, sftp_session_free);
253 : : }
254 : :
255 : :
256 : : /* GMountOperation Callbacks
257 : : *
258 : : * Rather than setting the password ahead of time, we set it upon request to
259 : : * avoid password authentication if possible.
260 : : *
261 : : * All host keys are accepted since we connect to known hosts as communicated
262 : : * over the TLS encrypted `ValentLanChannel`.
263 : : */
264 : : static void
265 : 0 : ask_password_cb (GMountOperation *op,
266 : : char *message,
267 : : char *default_user,
268 : : char *default_domain,
269 : : GAskPasswordFlags flags,
270 : : gpointer user_data)
271 : : {
272 : 0 : ValentSftpSession *session = user_data;
273 : :
274 [ # # ]: 0 : if (flags & G_ASK_PASSWORD_NEED_USERNAME)
275 : 0 : g_mount_operation_set_username (op, session->username);
276 : :
277 [ # # ]: 0 : if (flags & G_ASK_PASSWORD_NEED_PASSWORD)
278 : : {
279 : 0 : g_mount_operation_set_password (op, session->password);
280 : 0 : g_mount_operation_set_password_save (op, G_PASSWORD_SAVE_NEVER);
281 : : }
282 : :
283 : 0 : g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED);
284 : 0 : }
285 : :
286 : : static void
287 : 0 : ask_question_cb (GMountOperation *op,
288 : : char *message,
289 : : GStrv choices,
290 : : gpointer user_data)
291 : : {
292 : 0 : g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED);
293 : 0 : }
294 : :
295 : : /**
296 : : * remove_host_key:
297 : : * @host: An IP or hostname
298 : : *
299 : : * Remove all host keys associated with @host from the ssh-agent.
300 : : */
301 : : static void
302 : 0 : remove_host_key (const char *host)
303 : : {
304 [ # # ]: 0 : g_assert (host != NULL);
305 : :
306 [ # # ]: 0 : for (uint16_t port = 1739; port <= 1764; port++)
307 : : {
308 : 0 : g_autoptr (GSubprocess) proc = NULL;
309 [ # # ]: 0 : g_autofree char *match = NULL;
310 : :
311 : 0 : match = g_strdup_printf ("[%s]:%d", host, port);
312 : 0 : proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE |
313 : : G_SUBPROCESS_FLAGS_STDERR_MERGE,
314 : : NULL,
315 : : "ssh-keygen", "-R", match,
316 : : NULL);
317 : :
318 [ # # ]: 0 : if (proc != NULL)
319 : 0 : g_subprocess_wait_async (proc, NULL, NULL, NULL);
320 : : }
321 : 0 : }
322 : :
323 : : /*
324 : : * Packet Handlers
325 : : */
326 : : static void
327 : 0 : mount_cb (GFile *file,
328 : : GAsyncResult *result,
329 : : ValentSftpPlugin *self)
330 : : {
331 : 0 : g_autoptr (GError) error = NULL;
332 : :
333 [ # # ]: 0 : g_assert (VALENT_IS_SFTP_PLUGIN (self));
334 : :
335 : : /* On success we will acquire the mount from the volume monitor */
336 [ # # ]: 0 : if (g_file_mount_enclosing_volume_finish (file, result, &error))
337 : : return;
338 : :
339 : : /* On the off-chance this happens, we will just ensure we have the mount */
340 [ # # # # ]: 0 : if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED) &&
341 : 0 : sftp_session_find (self))
342 : : return;
343 : :
344 : : /* On failure, we're particularly interested in host key failures so that
345 : : * we can remove those from the ssh-agent. These are reported by GVfs as
346 : : * G_IO_ERROR_FAILED with a localized string, so we just assume. */
347 [ # # ]: 0 : if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED))
348 : : {
349 : 0 : g_warning ("%s(): Error mounting: %s", G_STRFUNC, error->message);
350 : :
351 [ # # # # ]: 0 : if (self->session && self->session->host)
352 : 0 : remove_host_key (self->session->host);
353 : : }
354 : :
355 [ # # # # ]: 0 : g_clear_pointer (&self->session, sftp_session_free);
356 : : }
357 : :
358 : : static void
359 : 0 : ssh_add_cb (GSubprocess *proc,
360 : : GAsyncResult *result,
361 : : ValentSftpPlugin *self)
362 : : {
363 : 0 : ValentSftpSession *session = self->session;
364 : 0 : g_autoptr (GError) error = NULL;
365 [ # # # # ]: 0 : g_autoptr (GFile) file = NULL;
366 [ # # # # ]: 0 : g_autoptr (GMountOperation) op = NULL;
367 : :
368 [ # # ]: 0 : g_assert (self->session != NULL);
369 : :
370 [ # # ]: 0 : if (!g_subprocess_wait_check_finish (proc, result, &error))
371 : : {
372 : 0 : g_warning ("%s(): Failed to add host key: %s", G_STRFUNC, error->message);
373 [ # # ]: 0 : g_clear_pointer (&self->session, sftp_session_free);
374 [ # # ]: 0 : return;
375 : : }
376 : :
377 : : /* Prepare the mount operation */
378 : 0 : op = g_mount_operation_new ();
379 : 0 : g_signal_connect (op, "ask-password", G_CALLBACK (ask_password_cb), session);
380 : 0 : g_signal_connect (op, "ask-question", G_CALLBACK (ask_question_cb), NULL);
381 : :
382 : : /* Start the mount operation */
383 : 0 : file = g_file_new_for_uri (session->uri);
384 [ # # ]: 0 : g_file_mount_enclosing_volume (file,
385 : : G_MOUNT_MOUNT_NONE,
386 : : op,
387 : : NULL,
388 : : (GAsyncReadyCallback)mount_cb,
389 : : self);
390 : : }
391 : :
392 : : static void
393 : 0 : sftp_session_begin (ValentSftpPlugin *self,
394 : : ValentSftpSession *session)
395 : : {
396 : 0 : g_autoptr (ValentContext) context = NULL;
397 [ # # # # ]: 0 : g_autoptr (GSubprocess) proc = NULL;
398 [ # # ]: 0 : g_autoptr (GError) error = NULL;
399 [ # # # # ]: 0 : g_autoptr (GFile) private_key = NULL;
400 : :
401 [ # # ]: 0 : g_assert (VALENT_IS_SFTP_PLUGIN (self));
402 : :
403 : : /* Get the root context and add the private key to the ssh-agent */
404 : 0 : context = valent_context_new (NULL, NULL, NULL);
405 : 0 : private_key = valent_context_get_config_file (context, "private.pem");
406 : 0 : proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE |
407 : : G_SUBPROCESS_FLAGS_STDERR_MERGE,
408 : : &error,
409 : : "ssh-add", g_file_peek_path (private_key),
410 : : NULL);
411 : :
412 [ # # ]: 0 : if (proc == NULL)
413 : : {
414 : 0 : g_warning ("%s(): Failed to add host key: %s", G_STRFUNC, error->message);
415 [ # # ]: 0 : g_clear_pointer (&self->session, sftp_session_free);
416 [ # # ]: 0 : return;
417 : : }
418 : :
419 [ # # ]: 0 : g_subprocess_wait_check_async (proc,
420 : : NULL,
421 : : (GAsyncReadyCallback)ssh_add_cb,
422 : : self);
423 : : }
424 : :
425 : :
426 : : /*
427 : : * Packet Handlers
428 : : */
429 : : static void
430 : 1 : handle_sftp_error (ValentSftpPlugin *self,
431 : : JsonNode *packet)
432 : : {
433 : 1 : ValentDevice *device;
434 : 2 : g_autoptr (GNotification) notification = NULL;
435 [ + - ]: 1 : g_autoptr (GIcon) error_icon = NULL;
436 [ + - ]: 1 : g_autofree char *error_title = NULL;
437 : 1 : const char *error_message;
438 : 1 : const char *device_name;
439 : 1 : JsonObject *body;
440 : :
441 [ + - ]: 1 : g_assert (VALENT_IS_SFTP_PLUGIN (self));
442 : :
443 : 1 : body = valent_packet_get_body (packet);
444 : :
445 : 1 : device = valent_extension_get_object (VALENT_EXTENSION (self));
446 : 1 : device_name = valent_device_get_name (device);
447 : :
448 : 1 : error_icon = g_themed_icon_new ("dialog-error-symbolic");
449 : 1 : error_title = g_strdup_printf ("%s: SFTP", device_name);
450 : 1 : error_message = json_object_get_string_member (body, "errorMessage");
451 : :
452 : : /* Send notification */
453 : 1 : notification = g_notification_new (error_title);
454 : 1 : g_notification_set_body (notification, error_message);
455 : 1 : g_notification_set_icon (notification, error_icon);
456 : 1 : g_notification_set_priority (notification, G_NOTIFICATION_PRIORITY_HIGH);
457 : 1 : valent_device_plugin_show_notification (VALENT_DEVICE_PLUGIN (self),
458 : : "sftp-error",
459 : : notification);
460 : 1 : }
461 : :
462 : : static void
463 : 0 : handle_sftp_mount (ValentSftpPlugin *self,
464 : : JsonNode *packet)
465 : : {
466 [ # # ]: 0 : g_assert (VALENT_IS_SFTP_PLUGIN (self));
467 : :
468 : : /* Check if we're already mounted or mounting */
469 [ # # ]: 0 : if (self->session != NULL)
470 : : return;
471 : :
472 : : /* Parse the connection data */
473 : 0 : self->session = sftp_session_new (self, packet);
474 : :
475 [ # # ]: 0 : if (self->session != NULL)
476 : 0 : sftp_session_begin (self, self->session);
477 : : }
478 : :
479 : : static void
480 : 1 : valent_sftp_plugin_handle_sftp (ValentSftpPlugin *self,
481 : : JsonNode *packet)
482 : : {
483 [ + - ]: 1 : g_assert (VALENT_IS_SFTP_PLUGIN (self));
484 : :
485 : : /* The request for mount information failed, most likely due to the remote
486 : : * device not being setup yet.
487 : : */
488 [ + - ]: 1 : if (valent_packet_check_field (packet, "errorMessage"))
489 : 1 : handle_sftp_error (self, packet);
490 : :
491 : : /* Otherwise we've been sent the information necessary to open an SSH/SFTP
492 : : * connection to the remote device.
493 : : */
494 : : else
495 : 0 : handle_sftp_mount (self, packet);
496 : 1 : }
497 : :
498 : : /*
499 : : * Packet Providers
500 : : */
501 : : static void
502 : 1 : valent_sftp_plugin_handle_request (ValentSftpPlugin *self,
503 : : JsonNode *packet)
504 : : {
505 : 1 : GSettings *settings;
506 : 1 : g_autoptr (JsonBuilder) builder = NULL;
507 [ - - - + ]: 1 : g_autoptr (JsonNode) response = NULL;
508 : :
509 [ + - ]: 1 : g_assert (VALENT_IS_SFTP_PLUGIN (self));
510 : :
511 [ - + ]: 1 : if (!valent_packet_check_field (packet, "startBrowsing"))
512 [ # # ]: 0 : return;
513 : :
514 : 1 : settings = valent_extension_get_settings (VALENT_EXTENSION (self));
515 : 1 : valent_packet_init (&builder, "kdeconnect.sftp");
516 : :
517 [ - + ]: 1 : if (g_settings_get_boolean (settings, "local-allow"))
518 : : {
519 : 0 : uint16_t local_port;
520 : :
521 : 0 : json_builder_set_member_name (builder, "user");
522 : 0 : json_builder_add_string_value (builder, g_get_user_name ());
523 : :
524 : 0 : local_port = g_settings_get_uint (settings, "local-port");
525 : 0 : json_builder_set_member_name (builder, "port");
526 : 0 : json_builder_add_int_value (builder, local_port);
527 : :
528 : 0 : json_builder_set_member_name (builder, "multiPaths");
529 : 0 : json_builder_begin_array (builder);
530 : 0 : json_builder_add_string_value (builder, g_get_home_dir ());
531 : 0 : json_builder_end_array (builder);
532 : :
533 : 0 : json_builder_set_member_name (builder, "pathNames");
534 : 0 : json_builder_begin_array (builder);
535 : 0 : json_builder_add_string_value (builder, _("Home"));
536 : 0 : json_builder_end_array (builder);
537 : : }
538 : : else
539 : : {
540 : 1 : json_builder_set_member_name (builder, "errorMessage");
541 : 1 : json_builder_add_string_value (builder, _("Permission denied"));
542 : : }
543 : :
544 : 1 : response = valent_packet_end (&builder);
545 [ + - ]: 1 : valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), response);
546 : : }
547 : :
548 : : static void
549 : 1 : valent_sftp_plugin_sftp_request (ValentSftpPlugin *self)
550 : : {
551 : 1 : g_autoptr (JsonBuilder) builder = NULL;
552 [ - - - + ]: 1 : g_autoptr (JsonNode) packet = NULL;
553 : :
554 [ + - ]: 1 : g_assert (VALENT_IS_SFTP_PLUGIN (self));
555 : :
556 [ - + ]: 1 : if (sftp_session_find (self))
557 [ # # ]: 0 : return;
558 : :
559 : 1 : valent_packet_init (&builder, "kdeconnect.sftp.request");
560 : 1 : json_builder_set_member_name (builder, "startBrowsing");
561 : 1 : json_builder_add_boolean_value (builder, TRUE);
562 : 1 : packet = valent_packet_end (&builder);
563 : :
564 [ + - ]: 1 : valent_device_plugin_queue_packet (VALENT_DEVICE_PLUGIN (self), packet);
565 : : }
566 : :
567 : : /*
568 : : * GActions
569 : : */
570 : : static void
571 : 1 : mount_action (GSimpleAction *action,
572 : : GVariant *parameter,
573 : : gpointer user_data)
574 : : {
575 : 1 : ValentSftpPlugin *self = VALENT_SFTP_PLUGIN (user_data);
576 : :
577 [ + - ]: 1 : g_assert (VALENT_IS_SFTP_PLUGIN (self));
578 : :
579 [ - + - - ]: 1 : if (self->session != NULL && self->session->uri != NULL)
580 : 0 : g_app_info_launch_default_for_uri_async (self->session->uri,
581 : : NULL, NULL, NULL, NULL);
582 : : else
583 : 1 : valent_sftp_plugin_sftp_request (self);
584 : 1 : }
585 : :
586 : : static const GActionEntry actions[] = {
587 : : {"browse", mount_action, NULL, NULL, NULL}
588 : : };
589 : :
590 : :
591 : : /*
592 : : * ValentDevicePlugin
593 : : */
594 : : static void
595 : 11 : valent_sftp_plugin_update_state (ValentDevicePlugin *plugin,
596 : : ValentDeviceState state)
597 : : {
598 : 11 : ValentSftpPlugin *self = VALENT_SFTP_PLUGIN (plugin);
599 : 11 : gboolean available;
600 : :
601 [ + - ]: 11 : g_assert (VALENT_IS_SFTP_PLUGIN (self));
602 : :
603 : 11 : available = (state & VALENT_DEVICE_STATE_CONNECTED) != 0 &&
604 : : (state & VALENT_DEVICE_STATE_PAIRED) != 0;
605 : :
606 : 11 : valent_extension_toggle_actions (VALENT_EXTENSION (plugin), available);
607 : :
608 : : /* GMounts */
609 [ + + ]: 11 : if (available)
610 : : {
611 : 3 : GSettings *settings;
612 : :
613 : 3 : sftp_session_find (self);
614 : :
615 : 3 : settings = valent_extension_get_settings (VALENT_EXTENSION (plugin));
616 : :
617 [ - + ]: 3 : if (g_settings_get_boolean (settings, "auto-mount"))
618 : 0 : valent_sftp_plugin_sftp_request (self);
619 : : }
620 : 11 : }
621 : :
622 : : static void
623 : 2 : valent_sftp_plugin_handle_packet (ValentDevicePlugin *plugin,
624 : : const char *type,
625 : : JsonNode *packet)
626 : : {
627 : 2 : ValentSftpPlugin *self = VALENT_SFTP_PLUGIN (plugin);
628 : :
629 [ + - ]: 2 : g_assert (VALENT_IS_SFTP_PLUGIN (self));
630 [ - + ]: 2 : g_assert (type != NULL);
631 [ - + ]: 2 : g_assert (VALENT_IS_PACKET (packet));
632 : :
633 [ + + ]: 2 : if (g_str_equal (type, "kdeconnect.sftp"))
634 : 1 : valent_sftp_plugin_handle_sftp (self, packet);
635 : :
636 [ + - ]: 1 : else if (g_str_equal (type, "kdeconnect.sftp.request"))
637 : 1 : valent_sftp_plugin_handle_request (self, packet);
638 : :
639 : : else
640 : 0 : g_warn_if_reached ();
641 : 2 : }
642 : :
643 : : /*
644 : : * ValentObject
645 : : */
646 : : static void
647 : 8 : valent_sftp_plugin_destroy (ValentObject *object)
648 : : {
649 : 8 : ValentSftpPlugin *self = VALENT_SFTP_PLUGIN (object);
650 : 8 : ValentDevicePlugin *plugin = VALENT_DEVICE_PLUGIN (object);
651 : :
652 : : /* Stop watching the volume monitor and unmount any current session */
653 [ + + ]: 8 : if (self->monitor != NULL)
654 : : {
655 : 4 : g_signal_handlers_disconnect_by_data (self->monitor, self);
656 [ + - ]: 4 : g_clear_object (&self->monitor);
657 : : }
658 [ - + ]: 8 : g_clear_pointer (&self->session, sftp_session_end);
659 : :
660 : 8 : valent_device_plugin_set_menu_item (plugin, "device.sftp.browse", NULL);
661 : :
662 : 8 : VALENT_OBJECT_CLASS (valent_sftp_plugin_parent_class)->destroy (object);
663 : 8 : }
664 : :
665 : : /*
666 : : * GObject
667 : : */
668 : : static void
669 : 4 : valent_sftp_plugin_constructed (GObject *object)
670 : : {
671 : 4 : ValentSftpPlugin *self = VALENT_SFTP_PLUGIN (object);
672 : 4 : ValentDevicePlugin *plugin = VALENT_DEVICE_PLUGIN (object);
673 : :
674 : 4 : g_action_map_add_action_entries (G_ACTION_MAP (plugin),
675 : : actions,
676 : : G_N_ELEMENTS (actions),
677 : : plugin);
678 : 4 : valent_device_plugin_set_menu_action (plugin,
679 : : "device.sftp.browse",
680 : 4 : _("Browse Files"),
681 : : "folder-remote-symbolic");
682 : :
683 : : /* Watch the volume monitor */
684 : 4 : self->monitor = g_volume_monitor_get ();
685 : 4 : g_signal_connect_object (self->monitor,
686 : : "mount-added",
687 : : G_CALLBACK (on_mount_added),
688 : : self, 0);
689 : 4 : g_signal_connect_object (self->monitor,
690 : : "mount-removed",
691 : : G_CALLBACK (on_mount_removed),
692 : : self, 0);
693 : :
694 : 4 : G_OBJECT_CLASS (valent_sftp_plugin_parent_class)->constructed (object);
695 : 4 : }
696 : :
697 : : static void
698 : 18 : valent_sftp_plugin_class_init (ValentSftpPluginClass *klass)
699 : : {
700 : 18 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
701 : 18 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
702 : 18 : ValentDevicePluginClass *plugin_class = VALENT_DEVICE_PLUGIN_CLASS (klass);
703 : :
704 : 18 : object_class->constructed = valent_sftp_plugin_constructed;
705 : :
706 : 18 : vobject_class->destroy = valent_sftp_plugin_destroy;
707 : :
708 : 18 : plugin_class->handle_packet = valent_sftp_plugin_handle_packet;
709 : 18 : plugin_class->update_state = valent_sftp_plugin_update_state;
710 : : }
711 : :
712 : : static void
713 : 4 : valent_sftp_plugin_init (ValentSftpPlugin *self)
714 : : {
715 : 4 : }
716 : :
|