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 [ + + + - ]: 56 : 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 : 10 : valent_share_upload_update (ValentShareUpload *self)
50 : : {
51 : 20 : g_autoptr (JsonNode) packet = NULL;
52 [ + - ]: 10 : g_autoptr (JsonBuilder) builder = NULL;
53 : :
54 [ - + ]: 10 : g_assert (VALENT_IS_SHARE_UPLOAD (self));
55 : :
56 : 10 : valent_packet_init (&builder, "kdeconnect.share.request.update");
57 : 10 : json_builder_set_member_name (builder, "numberOfFiles");
58 : 10 : json_builder_add_int_value (builder, self->items->len);
59 : 10 : json_builder_set_member_name (builder, "totalPayloadSize");
60 : 10 : json_builder_add_int_value (builder, self->payload_size);
61 : 10 : packet = valent_packet_end (&builder);
62 : :
63 [ - + ]: 10 : valent_device_send_packet (self->device, packet, NULL, NULL, NULL);
64 : 10 : }
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 : : {
94 : 0 : g_task_return_error (task, g_steal_pointer (&error));
95 [ # # ]: 0 : return;
96 : : }
97 : :
98 [ + + ]: 10 : if (self->position < self->items->len)
99 : : {
100 : 6 : ValentTransfer *item = g_ptr_array_index (self->items, self->position++);
101 : :
102 : 6 : valent_share_upload_update_transfer (self, item);
103 : 6 : valent_transfer_execute (item,
104 : : g_task_get_cancellable (task),
105 : : valent_transfer_execute_cb,
106 : : g_object_ref (task));
107 : : }
108 [ - + ]: 4 : else if (self->processing_files)
109 : : {
110 [ - + ]: 10 : g_autoptr (GSource) source = NULL;
111 : :
112 : 0 : source = g_idle_source_new ();
113 [ # # ]: 0 : g_task_attach_source (task, source, valent_share_upload_idle);
114 : : }
115 : : else
116 : : {
117 : 4 : g_task_return_boolean (task, TRUE);
118 : : }
119 : : }
120 : :
121 : : static gboolean
122 : 5 : valent_share_upload_idle (gpointer data)
123 : : {
124 : 5 : ValentShareUpload *self = g_task_get_source_object (G_TASK (data));
125 : 5 : GTask *task = G_TASK (data);
126 : :
127 [ - + ]: 5 : if (g_task_return_error_if_cancelled (task))
128 : : return G_SOURCE_REMOVE;
129 : :
130 [ + + ]: 5 : if (self->position < self->items->len)
131 : : {
132 : 4 : ValentTransfer *item = g_ptr_array_index (self->items, self->position++);
133 : :
134 : 4 : valent_share_upload_update_transfer (self, item);
135 : 4 : valent_transfer_execute (item,
136 : : g_task_get_cancellable (task),
137 : : valent_transfer_execute_cb,
138 : : g_object_ref (task));
139 : :
140 : 4 : return G_SOURCE_REMOVE;
141 : : }
142 [ - + ]: 1 : else if (self->processing_files)
143 : : {
144 : : return G_SOURCE_CONTINUE;
145 : : }
146 : :
147 : 0 : g_task_return_boolean (task, TRUE);
148 : 0 : return G_SOURCE_REMOVE;
149 : : }
150 : :
151 : : static void
152 : 4 : valent_share_upload_execute (ValentTransfer *transfer,
153 : : GCancellable *cancellable,
154 : : GAsyncReadyCallback callback,
155 : : gpointer user_data)
156 : : {
157 : 4 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (transfer);
158 : 8 : g_autoptr (GTask) task = NULL;
159 [ + - ]: 4 : g_autoptr (GSource) source = NULL;
160 : :
161 [ - + ]: 4 : g_assert (VALENT_IS_SHARE_UPLOAD (self));
162 [ + - + - : 4 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
163 : :
164 : 4 : source = g_idle_source_new ();
165 : :
166 : 4 : task = g_task_new (self, cancellable, callback, user_data);
167 [ + - ]: 4 : g_task_set_source_tag (task, valent_share_upload_execute);
168 [ + - ]: 4 : g_task_attach_source (task, source, valent_share_upload_idle);
169 : 4 : }
170 : :
171 : : /*
172 : : * GListModel
173 : : */
174 : : static gpointer
175 : 4 : valent_share_upload_get_item (GListModel *model,
176 : : unsigned int position)
177 : : {
178 : 4 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (model);
179 : :
180 [ - + ]: 4 : g_assert (VALENT_SHARE_UPLOAD (self));
181 : :
182 [ + - ]: 4 : if G_UNLIKELY (position >= self->items->len)
183 : : return NULL;
184 : :
185 : 4 : return g_object_ref (g_ptr_array_index (self->items, position));
186 : : }
187 : :
188 : : static GType
189 : 1 : valent_share_upload_get_item_type (GListModel *model)
190 : : {
191 : 1 : return VALENT_TYPE_TRANSFER;
192 : : }
193 : :
194 : : static unsigned int
195 : 13 : valent_share_upload_get_n_items (GListModel *model)
196 : : {
197 : 13 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (model);
198 : :
199 [ - + ]: 13 : g_assert (VALENT_SHARE_UPLOAD (self));
200 : :
201 : 13 : return self->items->len;
202 : : }
203 : :
204 : : static void
205 : 2 : g_list_model_iface_init (GListModelInterface *iface)
206 : : {
207 : 2 : iface->get_item = valent_share_upload_get_item;
208 : 2 : iface->get_item_type = valent_share_upload_get_item_type;
209 : 2 : iface->get_n_items = valent_share_upload_get_n_items;
210 : 2 : }
211 : :
212 : : /*
213 : : * GObject
214 : : */
215 : : static void
216 : 4 : valent_share_upload_finalize (GObject *object)
217 : : {
218 : 4 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (object);
219 : :
220 : 4 : valent_object_lock (VALENT_OBJECT (self));
221 [ + - ]: 4 : g_clear_object (&self->device);
222 [ + - ]: 4 : g_clear_pointer (&self->items, g_ptr_array_unref);
223 : 4 : valent_object_unlock (VALENT_OBJECT (self));
224 : :
225 : 4 : G_OBJECT_CLASS (valent_share_upload_parent_class)->finalize (object);
226 : 4 : }
227 : :
228 : : static void
229 : 7 : valent_share_upload_get_property (GObject *object,
230 : : guint prop_id,
231 : : GValue *value,
232 : : GParamSpec *pspec)
233 : : {
234 : 7 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (object);
235 : :
236 [ + - ]: 7 : switch ((ValentShareUploadProperty)prop_id)
237 : : {
238 : : case PROP_DEVICE:
239 : 7 : valent_object_lock (VALENT_OBJECT (self));
240 : 7 : g_value_set_object (value, self->device);
241 : 7 : valent_object_unlock (VALENT_OBJECT (self));
242 : 7 : break;
243 : :
244 : 0 : default:
245 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
246 : : }
247 : 7 : }
248 : :
249 : : static void
250 : 4 : valent_share_upload_set_property (GObject *object,
251 : : guint prop_id,
252 : : const GValue *value,
253 : : GParamSpec *pspec)
254 : : {
255 : 4 : ValentShareUpload *self = VALENT_SHARE_UPLOAD (object);
256 : :
257 [ + - ]: 4 : switch ((ValentShareUploadProperty)prop_id)
258 : : {
259 : : case PROP_DEVICE:
260 : 4 : valent_object_lock (VALENT_OBJECT (self));
261 : 4 : self->device = g_value_dup_object (value);
262 : 4 : valent_object_unlock (VALENT_OBJECT (self));
263 : 4 : break;
264 : :
265 : 0 : default:
266 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
267 : : }
268 : 4 : }
269 : :
270 : : static void
271 : 2 : valent_share_upload_class_init (ValentShareUploadClass *klass)
272 : : {
273 : 2 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
274 : 2 : ValentTransferClass *transfer_class = VALENT_TRANSFER_CLASS (klass);
275 : :
276 : 2 : object_class->finalize = valent_share_upload_finalize;
277 : 2 : object_class->get_property = valent_share_upload_get_property;
278 : 2 : object_class->set_property = valent_share_upload_set_property;
279 : :
280 : 2 : transfer_class->execute = valent_share_upload_execute;
281 : :
282 : : /**
283 : : * ValentShareUpload:device:
284 : : *
285 : : * The [class@Valent.Device] this transfer is for.
286 : : */
287 : 4 : properties [PROP_DEVICE] =
288 : 2 : g_param_spec_object ("device", NULL, NULL,
289 : : VALENT_TYPE_DEVICE,
290 : : (G_PARAM_READWRITE |
291 : : G_PARAM_CONSTRUCT_ONLY |
292 : : G_PARAM_EXPLICIT_NOTIFY |
293 : : G_PARAM_STATIC_STRINGS));
294 : :
295 : 2 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
296 : 2 : }
297 : :
298 : : static void
299 : 4 : valent_share_upload_init (ValentShareUpload *self)
300 : : {
301 : 4 : self->items = g_ptr_array_new_with_free_func (g_object_unref);
302 : 4 : }
303 : :
304 : : /**
305 : : * valent_share_upload_new:
306 : : * @device: a `ValentDevice`
307 : : *
308 : : * Create a new `ValentShareUpload`.
309 : : *
310 : : * Returns: (transfer full): a new `ValentShareUpload`
311 : : */
312 : : ValentTransfer *
313 : 4 : valent_share_upload_new (ValentDevice *device)
314 : : {
315 [ - + ]: 4 : g_return_val_if_fail (VALENT_IS_DEVICE (device), NULL);
316 : :
317 : 4 : return g_object_new (VALENT_TYPE_SHARE_UPLOAD,
318 : : "device", device,
319 : : NULL);
320 : : }
321 : :
322 : : static void
323 : 10 : g_file_query_info_cb (GFile *file,
324 : : GAsyncResult *result,
325 : : gpointer user_data)
326 : : {
327 : 20 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
328 : 10 : ValentShareUpload *self = g_task_get_source_object (task);
329 [ - - + - ]: 10 : g_autoptr (GFileInfo) info = NULL;
330 [ - - ]: 10 : g_autoptr (ValentTransfer) transfer = NULL;
331 [ - - ]: 10 : g_autoptr (JsonNode) packet = NULL;
332 [ - - + - ]: 10 : g_autoptr (JsonBuilder) builder = NULL;
333 : 10 : const char *filename;
334 : 10 : goffset payload_size;
335 : 10 : unsigned int position;
336 [ - - - + ]: 10 : g_autoptr (GError) error = NULL;
337 : :
338 : 10 : info = g_file_query_info_finish (file, result, &error);
339 [ - + ]: 10 : if (info == NULL)
340 : : {
341 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
342 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
343 : :
344 : 0 : self->processing_files--;
345 [ # # ]: 0 : return;
346 : : }
347 : :
348 : 10 : filename = g_file_info_get_name (info);
349 : 10 : payload_size = g_file_info_get_size (info);
350 : 10 : position = self->items->len;
351 : :
352 : 10 : valent_packet_init (&builder, "kdeconnect.share.request");
353 : 10 : json_builder_set_member_name (builder, "filename");
354 : 10 : json_builder_add_string_value (builder, filename);
355 : 10 : json_builder_set_member_name (builder, "open");
356 : 10 : json_builder_add_boolean_value (builder, FALSE);
357 : 10 : packet = valent_packet_end (&builder);
358 : 10 : valent_packet_set_payload_size (packet, payload_size);
359 : :
360 : 10 : transfer = valent_device_transfer_new (self->device, packet, file);
361 : 10 : g_ptr_array_add (self->items, g_steal_pointer (&transfer));
362 : 10 : self->payload_size += payload_size;
363 : :
364 : 10 : self->processing_files--;
365 : 10 : valent_share_upload_update (self);
366 : 10 : g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
367 [ - + ]: 10 : g_task_return_boolean (task, TRUE);
368 : : }
369 : :
370 : : /**
371 : : * valent_share_upload_add_file:
372 : : * @group: a `ValentShareUpload`
373 : : * @file: a `GFile`
374 : : *
375 : : * Add @file to the transfer operation.
376 : : */
377 : : void
378 : 10 : valent_share_upload_add_file (ValentShareUpload *upload,
379 : : GFile *file)
380 : : {
381 : 20 : g_autoptr (GTask) task = NULL;
382 [ + - ]: 10 : g_autoptr (GCancellable) destroy = NULL;
383 [ + - ]: 10 : g_autoptr (GPtrArray) items = NULL;
384 : :
385 [ - + ]: 10 : g_return_if_fail (VALENT_IS_SHARE_UPLOAD (upload));
386 [ + - + - : 10 : g_return_if_fail (G_IS_FILE (file));
+ - + - ]
387 : :
388 : 10 : upload->processing_files++;
389 : :
390 : 10 : destroy = valent_object_ref_cancellable (VALENT_OBJECT (upload));
391 : 10 : task = g_task_new (upload, destroy, NULL, NULL);
392 [ + - ]: 10 : g_task_set_source_tag (task, valent_share_upload_add_file);
393 : :
394 [ + - ]: 10 : g_file_query_info_async (file,
395 : : G_FILE_ATTRIBUTE_STANDARD_NAME","
396 : : G_FILE_ATTRIBUTE_STANDARD_SIZE,
397 : : G_FILE_QUERY_INFO_NONE,
398 : : G_PRIORITY_DEFAULT,
399 : : destroy,
400 : : (GAsyncReadyCallback)g_file_query_info_cb,
401 : : g_object_ref (task));
402 : : }
403 : :
|