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