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