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-download"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <valent.h>
10 : :
11 : : #include "valent-share-download.h"
12 : :
13 : : /* The maximum time in milliseconds to wait for the next expected transfer item,
14 : : * allowing for the gap between one file completing and the packet for the next.
15 : : *
16 : : * The current timeout matches kdeconnect-android which waits 1000ms before
17 : : * reporting an error, while kdeconnect-kde has no wait period. */
18 : : #define OPERATION_TIMEOUT_MS 1000
19 : :
20 : :
21 : : /**
22 : : * ValentShareDownload:
23 : : *
24 : : * A class for multi-file downloads.
25 : : *
26 : : * `ValentShareDownload` is a class that supports multi-file downloads for
27 : : * `ValentSharePlugin`.
28 : : */
29 : :
30 : : struct _ValentShareDownload
31 : : {
32 : : ValentTransfer parent_instance;
33 : :
34 : : ValentDevice *device;
35 : : GPtrArray *items;
36 : :
37 : : unsigned int position;
38 : : int64_t number_of_files;
39 : : goffset payload_size;
40 : : };
41 : :
42 : : static void g_list_model_iface_init (GListModelInterface *iface);
43 : : static gboolean valent_share_download_timeout (gpointer data);
44 : :
45 [ + + + - ]: 30 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentShareDownload, valent_share_download, VALENT_TYPE_TRANSFER,
46 : : G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init))
47 : :
48 : : typedef enum {
49 : : PROP_DEVICE = 1,
50 : : } ValentShareDownloadProperty;
51 : :
52 : : static GParamSpec *properties[PROP_DEVICE + 1] = { NULL, };
53 : :
54 : :
55 : : /*
56 : : * ValentTransfer
57 : : */
58 : : static void
59 : 6 : valent_transfer_execute_cb (GObject *object,
60 : : GAsyncResult *result,
61 : : gpointer user_data)
62 : : {
63 : 6 : ValentTransfer *transfer = VALENT_TRANSFER (object);
64 : 6 : ValentShareDownload *self = g_task_get_source_object (G_TASK (user_data));
65 : 6 : g_autoptr (GTask) task = G_TASK (user_data);
66 [ - - + - ]: 6 : g_autoptr (GError) error = NULL;
67 : :
68 [ - + ]: 6 : if (!valent_transfer_execute_finish (transfer, result, &error))
69 [ # # ]: 0 : return g_task_return_error (task, g_steal_pointer (&error));
70 : :
71 [ - + ]: 6 : if (self->position < self->items->len)
72 : : {
73 : 0 : ValentTransfer *item = g_ptr_array_index (self->items, self->position++);
74 : :
75 : 0 : valent_transfer_execute (item,
76 : : g_task_get_cancellable (task),
77 : : valent_transfer_execute_cb,
78 : : g_object_ref (task));
79 : : }
80 [ + + ]: 6 : else if (self->position < self->number_of_files)
81 : : {
82 [ - + ]: 6 : g_autoptr (GSource) source = NULL;
83 : :
84 : 2 : source = g_timeout_source_new (OPERATION_TIMEOUT_MS);
85 [ + - ]: 2 : g_task_attach_source (task, source, valent_share_download_timeout);
86 : : }
87 : : else
88 : : {
89 : 4 : g_task_return_boolean (task, TRUE);
90 : : }
91 : : }
92 : :
93 : : static gboolean
94 : 1 : valent_share_download_timeout (gpointer data)
95 : : {
96 : 1 : ValentShareDownload *self = g_task_get_source_object (G_TASK (data));
97 : 1 : GTask *task = G_TASK (data);
98 : :
99 [ + - ]: 1 : if (g_task_return_error_if_cancelled (task))
100 : : return G_SOURCE_REMOVE;
101 : :
102 [ + - ]: 1 : if (self->position < self->items->len)
103 : : {
104 : 1 : ValentTransfer *item = g_ptr_array_index (self->items, self->position++);
105 : :
106 : 1 : valent_transfer_execute (item,
107 : : g_task_get_cancellable (task),
108 : : valent_transfer_execute_cb,
109 : : g_object_ref (task));
110 : : }
111 [ # # ]: 0 : else if (self->position < self->number_of_files)
112 : : {
113 : 0 : g_task_return_new_error (task,
114 : : G_IO_ERROR,
115 : : G_IO_ERROR_PARTIAL_INPUT,
116 : : "Failed to receive %u of %u files",
117 : : (unsigned int)self->number_of_files - self->position,
118 : : (unsigned int)self->number_of_files);
119 : : }
120 : :
121 : : return G_SOURCE_REMOVE;
122 : : }
123 : :
124 : : static void
125 : 5 : valent_share_download_execute (ValentTransfer *transfer,
126 : : GCancellable *cancellable,
127 : : GAsyncReadyCallback callback,
128 : : gpointer user_data)
129 : : {
130 : 5 : ValentShareDownload *self = VALENT_SHARE_DOWNLOAD (transfer);
131 : 10 : g_autoptr (GTask) task = NULL;
132 : :
133 [ + - ]: 5 : g_assert (VALENT_IS_SHARE_DOWNLOAD (self));
134 [ + - + - : 5 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
135 : :
136 : 5 : task = g_task_new (self, cancellable, callback, user_data);
137 [ + - ]: 5 : g_task_set_source_tag (task, valent_share_download_execute);
138 : :
139 [ + - ]: 5 : if (self->position < self->items->len)
140 : : {
141 : 5 : ValentTransfer *item = g_ptr_array_index (self->items, self->position++);
142 : :
143 : 5 : valent_transfer_execute (item,
144 : : g_task_get_cancellable (task),
145 : : valent_transfer_execute_cb,
146 : : g_object_ref (task));
147 : : }
148 [ # # ]: 0 : else if (self->position < self->number_of_files)
149 : : {
150 [ + - ]: 5 : g_autoptr (GSource) source = NULL;
151 : :
152 : 0 : source = g_timeout_source_new (OPERATION_TIMEOUT_MS);
153 [ # # ]: 0 : g_task_attach_source (task, source, valent_share_download_timeout);
154 : : }
155 : : else
156 : : {
157 : 0 : g_task_return_boolean (task, TRUE);
158 : : }
159 : 5 : }
160 : :
161 : : /*
162 : : * GListModel
163 : : */
164 : : static gpointer
165 : 4 : valent_share_download_get_item (GListModel *model,
166 : : unsigned int position)
167 : : {
168 : 4 : ValentShareDownload *self = VALENT_SHARE_DOWNLOAD (model);
169 : :
170 [ + - ]: 4 : g_assert (VALENT_SHARE_DOWNLOAD (self));
171 : :
172 [ + - ]: 4 : if G_UNLIKELY (position >= self->items->len)
173 : : return NULL;
174 : :
175 : 4 : return g_object_ref (g_ptr_array_index (self->items, position));
176 : : }
177 : :
178 : : static GType
179 : 0 : valent_share_download_get_item_type (GListModel *model)
180 : : {
181 [ # # ]: 0 : g_assert (VALENT_SHARE_DOWNLOAD (model));
182 : :
183 : 0 : return VALENT_TYPE_TRANSFER;
184 : : }
185 : :
186 : : static unsigned int
187 : 10 : valent_share_download_get_n_items (GListModel *model)
188 : : {
189 : 10 : ValentShareDownload *self = VALENT_SHARE_DOWNLOAD (model);
190 : :
191 [ + - ]: 10 : g_assert (VALENT_SHARE_DOWNLOAD (self));
192 : :
193 : : /* FIXME: this indicates the number of total transfers, not the number of
194 : : * items currently available in the list model. */
195 : 10 : return self->number_of_files;
196 : : }
197 : :
198 : : static void
199 : 2 : g_list_model_iface_init (GListModelInterface *iface)
200 : : {
201 : 2 : iface->get_item = valent_share_download_get_item;
202 : 2 : iface->get_item_type = valent_share_download_get_item_type;
203 : 2 : iface->get_n_items = valent_share_download_get_n_items;
204 : 2 : }
205 : :
206 : : /*
207 : : * GObject
208 : : */
209 : : static void
210 : 4 : valent_share_download_finalize (GObject *object)
211 : : {
212 : 4 : ValentShareDownload *self = VALENT_SHARE_DOWNLOAD (object);
213 : :
214 [ + - ]: 4 : g_clear_object (&self->device);
215 [ + - ]: 4 : g_clear_pointer (&self->items, g_ptr_array_unref);
216 : :
217 : 4 : G_OBJECT_CLASS (valent_share_download_parent_class)->finalize (object);
218 : 4 : }
219 : :
220 : : static void
221 : 10 : valent_share_download_get_property (GObject *object,
222 : : guint prop_id,
223 : : GValue *value,
224 : : GParamSpec *pspec)
225 : : {
226 : 10 : ValentShareDownload *self = VALENT_SHARE_DOWNLOAD (object);
227 : :
228 [ + - ]: 10 : switch ((ValentShareDownloadProperty)prop_id)
229 : : {
230 : 10 : case PROP_DEVICE:
231 : 10 : g_value_set_object (value, self->device);
232 : 10 : break;
233 : :
234 : 0 : default:
235 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
236 : : }
237 : 10 : }
238 : :
239 : : static void
240 : 5 : valent_share_download_set_property (GObject *object,
241 : : guint prop_id,
242 : : const GValue *value,
243 : : GParamSpec *pspec)
244 : : {
245 : 5 : ValentShareDownload *self = VALENT_SHARE_DOWNLOAD (object);
246 : :
247 [ + - ]: 5 : switch ((ValentShareDownloadProperty)prop_id)
248 : : {
249 : 5 : case PROP_DEVICE:
250 : 5 : self->device = g_value_dup_object (value);
251 : 5 : break;
252 : :
253 : 0 : default:
254 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
255 : : }
256 : 5 : }
257 : :
258 : : static void
259 : 2 : valent_share_download_class_init (ValentShareDownloadClass *klass)
260 : : {
261 : 2 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
262 : 2 : ValentTransferClass *transfer_class = VALENT_TRANSFER_CLASS (klass);
263 : :
264 : 2 : object_class->finalize = valent_share_download_finalize;
265 : 2 : object_class->get_property = valent_share_download_get_property;
266 : 2 : object_class->set_property = valent_share_download_set_property;
267 : :
268 : 2 : transfer_class->execute = valent_share_download_execute;
269 : :
270 : : /**
271 : : * ValentShareDownload:device:
272 : : *
273 : : * The [class@Valent.Device] this transfer is for.
274 : : */
275 : 4 : properties [PROP_DEVICE] =
276 : 2 : g_param_spec_object ("device", NULL, NULL,
277 : : VALENT_TYPE_DEVICE,
278 : : (G_PARAM_READWRITE |
279 : : G_PARAM_CONSTRUCT_ONLY |
280 : : G_PARAM_EXPLICIT_NOTIFY |
281 : : G_PARAM_STATIC_STRINGS));
282 : :
283 : 2 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
284 : 2 : }
285 : :
286 : : static void
287 : 5 : valent_share_download_init (ValentShareDownload *self)
288 : : {
289 : 5 : self->items = g_ptr_array_new_with_free_func (g_object_unref);
290 : 5 : }
291 : :
292 : : /**
293 : : * valent_share_download_new:
294 : : * @device: a `ValentDevice`
295 : : *
296 : : * Create a new `ValentShareDownload`.
297 : : *
298 : : * Returns: (transfer full): a new `ValentShareDownload`
299 : : */
300 : : ValentTransfer *
301 : 5 : valent_share_download_new (ValentDevice *device)
302 : : {
303 [ + - ]: 5 : g_return_val_if_fail (VALENT_IS_DEVICE (device), NULL);
304 : :
305 : 5 : return g_object_new (VALENT_TYPE_SHARE_DOWNLOAD,
306 : : "device", device,
307 : : NULL);
308 : : }
309 : :
310 : : /**
311 : : * valent_share_download_add_file:
312 : : * @group: a `ValentShareDownload`
313 : : * @file: a `GFile`
314 : : * @packet: a KDE Connect packet
315 : : *
316 : : * Add @file to the transfer operation.
317 : : */
318 : : void
319 : 6 : valent_share_download_add_file (ValentShareDownload *download,
320 : : GFile *file,
321 : : JsonNode *packet)
322 : : {
323 : 6 : g_autoptr (ValentTransfer) item = NULL;
324 : 6 : unsigned int position, added;
325 : 6 : int64_t number_of_files;
326 : 6 : goffset total_payload_size;
327 : :
328 [ + - ]: 6 : g_return_if_fail (VALENT_IS_SHARE_DOWNLOAD (download));
329 [ + - + - : 6 : g_return_if_fail (G_IS_FILE (file));
+ - - + ]
330 [ - + ]: 6 : g_return_if_fail (VALENT_IS_PACKET (packet));
331 : :
332 [ - + ]: 6 : if (!valent_packet_get_int (packet, "numberOfFiles", &number_of_files))
333 : 0 : number_of_files = download->number_of_files + 1;
334 : :
335 [ - + ]: 6 : if (!valent_packet_get_int (packet, "totalPayloadSize", &total_payload_size))
336 : 0 : total_payload_size = download->payload_size + valent_packet_get_payload_size (packet);
337 : :
338 : 6 : position = download->items->len;
339 : 6 : added = number_of_files - download->number_of_files;
340 : :
341 : 6 : download->number_of_files = number_of_files;
342 : 6 : download->payload_size = total_payload_size;
343 : :
344 : 6 : item = valent_device_transfer_new (download->device, packet, file);
345 : 6 : g_ptr_array_add (download->items, g_steal_pointer (&item));
346 : :
347 : : /* FIXME: this indicates the number of total transfers, not the number of
348 : : * items currently available in the list model. */
349 : 6 : g_list_model_items_changed (G_LIST_MODEL (download), position, 0, added);
350 : : }
351 : :
352 : : /**
353 : : * valent_share_download_update:
354 : : * @download: a `ValentShareDownload`
355 : : * @packet: a KDE Connect packet
356 : : *
357 : : * Update the number of files and total payload size for @download.
358 : : */
359 : : void
360 : 0 : valent_share_download_update (ValentShareDownload *self,
361 : : JsonNode *packet)
362 : : {
363 [ # # ]: 0 : g_return_if_fail (VALENT_IS_SHARE_DOWNLOAD (self));
364 [ # # ]: 0 : g_return_if_fail (VALENT_IS_PACKET (packet));
365 : :
366 [ # # ]: 0 : if (!valent_packet_get_int (packet, "numberOfFiles", &self->number_of_files))
367 : : {
368 : 0 : g_debug ("%s(): expected \"numberOfFiles\" field holding an integer",
369 : : G_STRFUNC);
370 : 0 : return;
371 : : }
372 : :
373 [ # # ]: 0 : if (!valent_packet_get_int (packet, "totalPayloadSize", &self->payload_size))
374 : : {
375 : 0 : g_debug ("%s(): expected \"totalPayloadSize\" field holding an integer",
376 : : G_STRFUNC);
377 : 0 : return;
378 : : }
379 : : }
380 : :
|