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