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-upload"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <valent.h>
10 : :
11 : : #include "valent-share-upload.h"
12 : :
13 : :
14 : : /**
15 : : * ValentShareUpload:
16 : : *
17 : : * A class for multi-file uploads.
18 : : *
19 : : * `ValentShareUpload` is a class that supports multi-file uploads for
20 : : * `ValentSharePlugin`.
21 : : */
22 : :
23 : : struct _ValentShareUpload
24 : : {
25 : : ValentTransfer parent_instance;
26 : :
27 : : ValentDevice *device;
28 : : GPtrArray *items;
29 : :
30 : : unsigned int position;
31 : : unsigned int processing_files;
32 : : goffset payload_size;
33 : : };
34 : :
35 : : static void g_list_model_iface_init (GListModelInterface *iface);
36 : : static gboolean valent_share_upload_idle (gpointer data);
37 : :
38 [ + + + - ]: 64 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentShareUpload, valent_share_upload, VALENT_TYPE_TRANSFER,
39 : : G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init))
40 : :
41 : : typedef enum {
42 : : PROP_DEVICE = 1,
43 : : } ValentShareUploadProperty;
44 : :
45 : : static GParamSpec *properties[PROP_DEVICE + 1] = { NULL, };
46 : :
47 : :
48 : : static void
49 : 7 : valent_share_upload_update (ValentShareUpload *self)
50 : : {
51 : 14 : g_autoptr (JsonNode) packet = NULL;
52 [ + - ]: 7 : g_autoptr (JsonBuilder) builder = NULL;
53 : :
54 [ + - ]: 7 : g_assert (VALENT_IS_SHARE_UPLOAD (self));
55 : :
56 : 7 : valent_packet_init (&builder, "kdeconnect.share.request.update");
57 : 7 : json_builder_set_member_name (builder, "numberOfFiles");
58 : 7 : json_builder_add_int_value (builder, self->items->len);
59 : 7 : json_builder_set_member_name (builder, "totalPayloadSize");
60 : 7 : json_builder_add_int_value (builder, self->payload_size);
61 : 7 : packet = valent_packet_end (&builder);
62 : :
63 [ - + ]: 7 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
64 : 7 : }
65 : :
66 : : static inline void
67 : 10 : valent_share_upload_update_transfer (ValentShareUpload *self,
68 : : ValentTransfer *transfer)
69 : : {
70 : 20 : g_autoptr (JsonNode) packet = NULL;
71 : 10 : JsonObject *body;
72 : :
73 [ + - ]: 10 : g_assert (VALENT_IS_SHARE_UPLOAD (self));
74 [ - + ]: 10 : g_assert (VALENT_IS_TRANSFER (transfer));
75 : :
76 : 10 : packet = valent_device_transfer_ref_packet (VALENT_DEVICE_TRANSFER (transfer));
77 : 10 : body = valent_packet_get_body (packet);
78 : 10 : json_object_set_int_member (body, "numberOfFiles", self->items->len);
79 [ + - ]: 10 : json_object_set_int_member (body, "totalPayloadSize", self->payload_size);
80 : 10 : }
81 : :
82 : : static void
83 : 10 : valent_transfer_execute_cb (GObject *object,
84 : : GAsyncResult *result,
85 : : gpointer user_data)
86 : : {
87 : 10 : ValentTransfer *transfer = VALENT_TRANSFER (object);
88 : 10 : ValentShareUpload *self = g_task_get_source_object (G_TASK (user_data));
89 : 10 : g_autoptr (GTask) task = G_TASK (user_data);
90 [ - - + - ]: 10 : g_autoptr (GError) error = NULL;
91 : :
92 [ - + ]: 10 : if (!valent_transfer_execute_finish (transfer, result, &error))
93 [ # # ]: 0 : return g_task_return_error (task, g_steal_pointer (&error));
94 : :
95 [ + + ]: 10 : if (self->position < self->items->len)
96 : : {
97 : 6 : ValentTransfer *item = g_ptr_array_index (self->items, self->position++);
98 : :
99 : 6 : valent_share_upload_update_transfer (self, item);
100 : 6 : valent_transfer_execute (item,
101 : : g_task_get_cancellable (task),
102 : : valent_transfer_execute_cb,
103 : : g_object_ref (task));
104 : : }
105 [ - + ]: 4 : else if (self->processing_files)
106 : : {
107 [ - + ]: 10 : g_autoptr (GSource) source = NULL;
108 : :
109 : 0 : source = g_idle_source_new ();
110 [ # # ]: 0 : g_task_attach_source (task, source, valent_share_upload_idle);
111 : : }
112 : : else
113 : : {
114 : 4 : g_task_return_boolean (task, TRUE);
115 : : }
116 : : }
117 : :
118 : : static gboolean
119 : 183 : valent_share_upload_idle (gpointer data)
120 : : {
121 : 183 : ValentShareUpload *self = g_task_get_source_object (G_TASK (data));
122 : 183 : GTask *task = G_TASK (data);
123 : :
124 [ - + ]: 183 : if (g_task_return_error_if_cancelled (task))
125 : : return G_SOURCE_REMOVE;
126 : :
127 [ + + ]: 183 : if (self->position < self->items->len)
128 : : {
129 : 4 : ValentTransfer *item = g_ptr_array_index (self->items, self->position++);
130 : :
131 : 4 : valent_share_upload_update_transfer (self, item);
132 : 4 : valent_transfer_execute (item,
133 : : g_task_get_cancellable (task),
134 : : valent_transfer_execute_cb,
135 : : g_object_ref (task));
136 : :
137 : 4 : return G_SOURCE_REMOVE;
138 : : }
139 [ - + ]: 179 : else if (self->processing_files)
140 : : {
141 : : return G_SOURCE_CONTINUE;
142 : : }
143 : :
144 : 0 : g_task_return_boolean (task, TRUE);
145 : 0 : return G_SOURCE_REMOVE;
146 : : }
147 : :
148 : : static void
149 : 4 : valent_share_upload_execute (ValentTransfer *transfer,
150 : : GCancellable *cancellable,
151 : : GAsyncReadyCallback callback,
152 : : gpointer user_data)
153 : : {
154 : 4 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (transfer);
155 : 8 : g_autoptr (GTask) task = NULL;
156 [ + - ]: 4 : g_autoptr (GSource) source = NULL;
157 : :
158 [ + - ]: 4 : g_assert (VALENT_IS_SHARE_UPLOAD (self));
159 [ + - + - : 4 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
160 : :
161 : 4 : source = g_idle_source_new ();
162 : :
163 : 4 : task = g_task_new (self, cancellable, callback, user_data);
164 [ + - ]: 4 : g_task_set_source_tag (task, valent_share_upload_execute);
165 [ + - ]: 4 : g_task_attach_source (task, source, valent_share_upload_idle);
166 : 4 : }
167 : :
168 : : /*
169 : : * GListModel
170 : : */
171 : : static gpointer
172 : 1 : valent_share_upload_get_item (GListModel *model,
173 : : unsigned int position)
174 : : {
175 : 1 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (model);
176 : :
177 [ + - ]: 1 : g_assert (VALENT_SHARE_UPLOAD (self));
178 : :
179 [ + - ]: 1 : if G_UNLIKELY (position >= self->items->len)
180 : : return NULL;
181 : :
182 : 1 : return g_object_ref (g_ptr_array_index (self->items, position));
183 : : }
184 : :
185 : : static GType
186 : 1 : valent_share_upload_get_item_type (GListModel *model)
187 : : {
188 : 1 : return VALENT_TYPE_TRANSFER;
189 : : }
190 : :
191 : : static unsigned int
192 : 8 : valent_share_upload_get_n_items (GListModel *model)
193 : : {
194 : 8 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (model);
195 : :
196 [ + - ]: 8 : g_assert (VALENT_SHARE_UPLOAD (self));
197 : :
198 : 8 : return self->items->len;
199 : : }
200 : :
201 : : static void
202 : 2 : g_list_model_iface_init (GListModelInterface *iface)
203 : : {
204 : 2 : iface->get_item = valent_share_upload_get_item;
205 : 2 : iface->get_item_type = valent_share_upload_get_item_type;
206 : 2 : iface->get_n_items = valent_share_upload_get_n_items;
207 : 2 : }
208 : :
209 : : /*
210 : : * GObject
211 : : */
212 : : static void
213 : 4 : valent_share_upload_finalize (GObject *object)
214 : : {
215 : 4 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (object);
216 : :
217 : 4 : valent_object_lock (VALENT_OBJECT (self));
218 [ + - ]: 4 : g_clear_object (&self->device);
219 [ + - ]: 4 : g_clear_pointer (&self->items, g_ptr_array_unref);
220 : 4 : valent_object_unlock (VALENT_OBJECT (self));
221 : :
222 : 4 : G_OBJECT_CLASS (valent_share_upload_parent_class)->finalize (object);
223 : 4 : }
224 : :
225 : : static void
226 : 7 : valent_share_upload_get_property (GObject *object,
227 : : guint prop_id,
228 : : GValue *value,
229 : : GParamSpec *pspec)
230 : : {
231 : 7 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (object);
232 : :
233 [ + - ]: 7 : switch ((ValentShareUploadProperty)prop_id)
234 : : {
235 : : case PROP_DEVICE:
236 : 7 : valent_object_lock (VALENT_OBJECT (self));
237 : 7 : g_value_set_object (value, self->device);
238 : 7 : valent_object_unlock (VALENT_OBJECT (self));
239 : 7 : break;
240 : :
241 : 0 : default:
242 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
243 : : }
244 : 7 : }
245 : :
246 : : static void
247 : 4 : valent_share_upload_set_property (GObject *object,
248 : : guint prop_id,
249 : : const GValue *value,
250 : : GParamSpec *pspec)
251 : : {
252 : 4 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (object);
253 : :
254 [ + - ]: 4 : switch ((ValentShareUploadProperty)prop_id)
255 : : {
256 : : case PROP_DEVICE:
257 : 4 : valent_object_lock (VALENT_OBJECT (self));
258 : 4 : self->device = g_value_dup_object (value);
259 : 4 : valent_object_unlock (VALENT_OBJECT (self));
260 : 4 : break;
261 : :
262 : 0 : default:
263 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
264 : : }
265 : 4 : }
266 : :
267 : : static void
268 : 2 : valent_share_upload_class_init (ValentShareUploadClass *klass)
269 : : {
270 : 2 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
271 : 2 : ValentTransferClass *transfer_class = VALENT_TRANSFER_CLASS (klass);
272 : :
273 : 2 : object_class->finalize = valent_share_upload_finalize;
274 : 2 : object_class->get_property = valent_share_upload_get_property;
275 : 2 : object_class->set_property = valent_share_upload_set_property;
276 : :
277 : 2 : transfer_class->execute = valent_share_upload_execute;
278 : :
279 : : /**
280 : : * ValentShareUpload:device:
281 : : *
282 : : * The [class@Valent.Device] this transfer is for.
283 : : */
284 : 4 : properties [PROP_DEVICE] =
285 : 2 : g_param_spec_object ("device", NULL, NULL,
286 : : VALENT_TYPE_DEVICE,
287 : : (G_PARAM_READWRITE |
288 : : G_PARAM_CONSTRUCT_ONLY |
289 : : G_PARAM_EXPLICIT_NOTIFY |
290 : : G_PARAM_STATIC_STRINGS));
291 : :
292 : 2 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
293 : 2 : }
294 : :
295 : : static void
296 : 4 : valent_share_upload_init (ValentShareUpload *self)
297 : : {
298 : 4 : self->items = g_ptr_array_new_with_free_func (g_object_unref);
299 : 4 : }
300 : :
301 : : /**
302 : : * valent_share_upload_new:
303 : : * @device: a `ValentDevice`
304 : : *
305 : : * Create a new `ValentShareUpload`.
306 : : *
307 : : * Returns: (transfer full): a new `ValentShareUpload`
308 : : */
309 : : ValentTransfer *
310 : 4 : valent_share_upload_new (ValentDevice *device)
311 : : {
312 [ + - ]: 4 : g_return_val_if_fail (VALENT_IS_DEVICE (device), NULL);
313 : :
314 : 4 : return g_object_new (VALENT_TYPE_SHARE_UPLOAD,
315 : : "device", device,
316 : : NULL);
317 : : }
318 : :
319 : : static void
320 : 7 : valent_share_upload_add_files_cb (GObject *object,
321 : : GAsyncResult *result,
322 : : gpointer user_data)
323 : : {
324 : 7 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (object);
325 : 7 : g_autoptr (GPtrArray) items = NULL;
326 : 7 : unsigned int position, added;
327 : 7 : g_autoptr (GError) error = NULL;
328 : :
329 [ + - ]: 7 : g_assert (VALENT_IS_SHARE_UPLOAD (self));
330 [ - + ]: 7 : g_assert (g_task_is_valid (result, self));
331 : :
332 [ - + ]: 7 : if ((items = g_task_propagate_pointer (G_TASK (result), &error)) == NULL)
333 : : {
334 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
335 : 0 : g_warning ("%s: %s", G_OBJECT_TYPE_NAME (self), error->message);
336 : :
337 : 0 : self->processing_files--;
338 [ # # ]: 0 : return;
339 : : }
340 : :
341 : 7 : position = self->items->len;
342 : 7 : added = items->len;
343 : :
344 [ + + ]: 17 : for (unsigned int i = 0; i < items->len; i++)
345 : : {
346 : 10 : ValentDeviceTransfer *transfer = g_ptr_array_index (items, i);
347 : 10 : g_autoptr (JsonNode) packet = NULL;
348 : :
349 : 10 : packet = valent_device_transfer_ref_packet (transfer);
350 [ + - ]: 10 : self->payload_size += valent_packet_get_payload_size (packet);
351 : : }
352 : :
353 : 7 : g_ptr_array_extend_and_steal (self->items, g_steal_pointer (&items));
354 : 7 : self->processing_files--;
355 : :
356 : 7 : g_list_model_items_changed (G_LIST_MODEL (self), position, 0, added);
357 [ - + ]: 7 : valent_share_upload_update (self);
358 : : }
359 : :
360 : : static void
361 : 7 : valent_share_upload_add_files_task (GTask *task,
362 : : gpointer source_object,
363 : : gpointer task_data,
364 : : GCancellable *cancellable)
365 : : {
366 : 7 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (source_object);
367 : 7 : GPtrArray *files = task_data;
368 : 14 : g_autoptr (ValentDevice) device = NULL;
369 [ + - - - ]: 7 : g_autoptr (GPtrArray) items = NULL;
370 : :
371 [ + - ]: 7 : g_assert (VALENT_IS_SHARE_UPLOAD (self));
372 [ - + ]: 7 : g_assert (files != NULL);
373 : :
374 [ + - ]: 7 : if (g_task_return_error_if_cancelled (task))
375 : : return;
376 : :
377 : 7 : valent_object_lock (VALENT_OBJECT (self));
378 : 7 : device = g_object_ref (self->device);
379 : 7 : valent_object_unlock (VALENT_OBJECT (self));
380 : :
381 : 7 : items = g_ptr_array_new_with_free_func (g_object_unref);
382 : :
383 [ + + ]: 17 : for (unsigned int i = 0; i < files->len; i++)
384 : : {
385 : 10 : GFile *file = g_ptr_array_index (files, i);
386 [ - - ]: 10 : g_autoptr (ValentTransfer) transfer = NULL;
387 [ - - ]: 10 : g_autoptr (GFileInfo) info = NULL;
388 [ - - ]: 10 : g_autoptr (JsonNode) packet = NULL;
389 [ - - + - ]: 10 : g_autoptr (JsonBuilder) builder = NULL;
390 : 10 : const char *filename;
391 : 10 : goffset payload_size;
392 [ - - - + ]: 10 : g_autoptr (GError) error = NULL;
393 : :
394 : 10 : info = g_file_query_info (file,
395 : : G_FILE_ATTRIBUTE_STANDARD_NAME","
396 : : G_FILE_ATTRIBUTE_STANDARD_SIZE,
397 : : G_FILE_QUERY_INFO_NONE,
398 : : cancellable,
399 : : &error);
400 : :
401 [ - + ]: 10 : if (info == NULL)
402 [ # # ]: 0 : return g_task_return_error (task, g_steal_pointer (&error));
403 : :
404 : 10 : filename = g_file_info_get_name (info);
405 : 10 : payload_size = g_file_info_get_size (info);
406 : :
407 : 10 : valent_packet_init (&builder, "kdeconnect.share.request");
408 : 10 : json_builder_set_member_name (builder, "filename");
409 : 10 : json_builder_add_string_value (builder, filename);
410 : 10 : json_builder_set_member_name (builder, "open");
411 : 10 : json_builder_add_boolean_value (builder, FALSE);
412 : 10 : packet = valent_packet_end (&builder);
413 : :
414 : 10 : valent_packet_set_payload_size (packet, payload_size);
415 : :
416 : 10 : transfer = valent_device_transfer_new (device, packet, file);
417 [ - + ]: 10 : g_ptr_array_add (items, g_steal_pointer (&transfer));
418 : : }
419 : :
420 [ + - ]: 7 : g_task_return_pointer (task, g_steal_pointer (&items),
421 : : (GDestroyNotify)g_ptr_array_unref);
422 : : }
423 : :
424 : : /**
425 : : * valent_share_upload_add_file:
426 : : * @group: a `ValentShareUpload`
427 : : * @file: a `GFile`
428 : : *
429 : : * Add @file to the transfer operation.
430 : : */
431 : : void
432 : 6 : valent_share_upload_add_file (ValentShareUpload *upload,
433 : : GFile *file)
434 : : {
435 : 12 : g_autoptr (GTask) task = NULL;
436 [ + - ]: 6 : g_autoptr (GCancellable) destroy = NULL;
437 [ + - ]: 6 : g_autoptr (GPtrArray) items = NULL;
438 : :
439 [ + - ]: 6 : g_return_if_fail (VALENT_IS_SHARE_UPLOAD (upload));
440 [ + - + - : 6 : g_return_if_fail (G_IS_FILE (file));
+ - - + ]
441 : :
442 : 6 : upload->processing_files++;
443 : :
444 : 6 : items = g_ptr_array_new_full (1, g_object_unref);
445 : 6 : g_ptr_array_add (items, g_object_ref (file));
446 : :
447 : 6 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (upload));
448 : 6 : task = g_task_new (upload, destroy, valent_share_upload_add_files_cb, NULL);
449 [ + - ]: 6 : g_task_set_source_tag (task, valent_share_upload_add_file);
450 : 6 : g_task_set_task_data (task,
451 : : g_steal_pointer (&items),
452 : : (GDestroyNotify)g_ptr_array_unref);
453 [ + - ]: 6 : g_task_run_in_thread (task, valent_share_upload_add_files_task);
454 : : }
455 : :
456 : : /**
457 : : * valent_share_upload_add_files:
458 : : * @group: a `ValentShareUpload`
459 : : * @files: a `GListModel`
460 : : *
461 : : * Add @files to the transfer operation.
462 : : *
463 : : * The [property@Gio.ListModel:item-type] of @files must be [type@Gio.File].
464 : : *
465 : : * Call [method@Valent.ShareUpload.add_files_finish] to get the result.
466 : : */
467 : : void
468 : 1 : valent_share_upload_add_files (ValentShareUpload *upload,
469 : : GListModel *files)
470 : : {
471 : 2 : g_autoptr (GTask) task = NULL;
472 [ + - ]: 1 : g_autoptr (GCancellable) destroy = NULL;
473 [ + - ]: 1 : g_autoptr (GPtrArray) items = NULL;
474 : 1 : unsigned int n_files = 0;
475 : :
476 [ + - ]: 1 : g_return_if_fail (VALENT_IS_SHARE_UPLOAD (upload));
477 [ - + ]: 1 : g_return_if_fail (G_IS_LIST_MODEL (files));
478 [ - + ]: 1 : g_return_if_fail (g_list_model_get_item_type (files) == G_TYPE_FILE);
479 : :
480 : 1 : upload->processing_files++;
481 : :
482 : 1 : n_files = g_list_model_get_n_items (files);
483 : 1 : items = g_ptr_array_new_full (n_files, g_object_unref);
484 : :
485 [ + + ]: 5 : for (unsigned int i = 0; i < n_files; i++)
486 : 4 : g_ptr_array_add (items, g_list_model_get_item (files, i));
487 : :
488 : 1 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (upload));
489 : 1 : task = g_task_new (upload, destroy, valent_share_upload_add_files_cb, NULL);
490 [ + - ]: 1 : g_task_set_source_tag (task, valent_share_upload_add_files);
491 : 1 : g_task_set_task_data (task,
492 : : g_steal_pointer (&items),
493 : : (GDestroyNotify)g_ptr_array_unref);
494 [ + - ]: 1 : g_task_run_in_thread (task, valent_share_upload_add_files_task);
495 : : }
496 : :
|