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