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-device-transfer"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <math.h>
9 : :
10 : : #include <libvalent-core.h>
11 : :
12 : : #include "valent-channel.h"
13 : : #include "valent-device.h"
14 : : #include "valent-device-transfer.h"
15 : : #include "valent-packet.h"
16 : :
17 : :
18 : : /**
19 : : * ValentDeviceTransfer:
20 : : *
21 : : * A class for device file transfers.
22 : : *
23 : : * `ValentDeviceTransfer` is an implementation of [class@Valent.Transfer] for the
24 : : * common case of transferring a file between devices.
25 : : *
26 : : * The direction of the transfer is automatically detected from the content of
27 : : * [property@Valent.DeviceTransfer:packet]. If the KDE Connect packet holds
28 : : * payload information the transfer is assumed to be a download, otherwise it is
29 : : * assumed to be an upload.
30 : : *
31 : : * Since: 1.0
32 : : */
33 : :
34 : : struct _ValentDeviceTransfer
35 : : {
36 : : ValentObject parent_instance;
37 : :
38 : : ValentDevice *device;
39 : : GFile *file;
40 : : JsonNode *packet;
41 : : };
42 : :
43 [ + + + - ]: 230 : G_DEFINE_FINAL_TYPE (ValentDeviceTransfer, valent_device_transfer, VALENT_TYPE_TRANSFER)
44 : :
45 : :
46 : : typedef enum {
47 : : PROP_DEVICE = 1,
48 : : PROP_FILE,
49 : : PROP_PACKET,
50 : : } ValentDeviceTransferProperty;
51 : :
52 : : static GParamSpec *properties[PROP_PACKET + 1] = { NULL, };
53 : :
54 : : static ValentChannel *
55 : 0 : _valent_device_next_channel (ValentDevice *device,
56 : : ValentChannel *channel)
57 : : {
58 : 0 : GListModel *channels = NULL;
59 : 0 : unsigned int n_items = 0;
60 : :
61 : 0 : channels = valent_device_get_channels (device);
62 : 0 : n_items = g_list_model_get_n_items (channels);
63 [ # # ]: 0 : for (unsigned int i = 0; i < n_items; i++)
64 : : {
65 : 0 : g_autoptr (ValentChannel) next = NULL;
66 : :
67 : 0 : next = g_list_model_get_item (channels, i);
68 [ # # ]: 0 : if (next != channel)
69 : : return g_steal_pointer (&next);
70 : : }
71 : :
72 : : return NULL;
73 : : }
74 : :
75 : : static void
76 : 12 : valent_device_transfer_update_packet (JsonNode *packet,
77 : : GFileInfo *info)
78 : : {
79 : 12 : JsonObject *body;
80 : 12 : uint64_t btime_s, mtime_s;
81 : 12 : uint32_t btime_us, mtime_us;
82 : 12 : int64_t creation_time, last_modified;
83 : :
84 [ - + ]: 12 : g_assert (VALENT_IS_PACKET (packet));
85 : :
86 : 12 : btime_s = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CREATED);
87 : 12 : btime_us = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CREATED_USEC);
88 : 12 : creation_time = (int64_t)((btime_s * 1000) + floor (btime_us / 1000));
89 : :
90 : 12 : mtime_s = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
91 : 12 : mtime_us = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
92 : 12 : last_modified = (int64_t)((mtime_s * 1000) + floor (mtime_us / 1000));
93 : :
94 : 12 : body = valent_packet_get_body (packet);
95 : 12 : json_object_set_int_member (body, "creationTime", creation_time);
96 : 12 : json_object_set_int_member (body, "lastModified", last_modified);
97 : 12 : valent_packet_set_payload_size (packet, g_file_info_get_size (info));
98 : 12 : }
99 : :
100 : : /*
101 : : * ValentDeviceTransfer
102 : : */
103 : : typedef struct
104 : : {
105 : : GIOStream *connection;
106 : : GInputStream *source;
107 : : GOutputStream *target;
108 : : gboolean is_download;
109 : : } TransferOperation;
110 : :
111 : : static void
112 : 21 : transfer_operation_free (gpointer user_data)
113 : : {
114 : 21 : TransferOperation *op = (TransferOperation *)user_data;
115 : :
116 [ + - ]: 21 : g_clear_object (&op->connection);
117 [ + - ]: 21 : g_clear_object (&op->source);
118 [ + - ]: 21 : g_clear_object (&op->target);
119 : 21 : g_free (op);
120 : 21 : }
121 : :
122 : : static void
123 : 21 : g_output_stream_splice_cb (GOutputStream *target,
124 : : GAsyncResult *result,
125 : : gpointer user_data)
126 : : {
127 : 42 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
128 : 21 : ValentDeviceTransfer *self = g_task_get_source_object (task);
129 : 21 : TransferOperation *op = g_task_get_task_data (task);
130 : 21 : gssize transferred;
131 : 21 : int64_t last_modified = 0;
132 : 21 : int64_t creation_time = 0;
133 : 21 : goffset payload_size;
134 : 21 : GError *error = NULL;
135 : :
136 : 21 : transferred = g_output_stream_splice_finish (target, result, &error);
137 [ - + ]: 21 : if (error != NULL)
138 : : {
139 : 0 : g_task_return_error (task, g_steal_pointer (&error));
140 : 0 : return;
141 : : }
142 : :
143 : 21 : payload_size = valent_packet_get_payload_size (self->packet);
144 : 21 : if G_UNLIKELY (payload_size > G_MAXSSIZE)
145 : : {
146 : : g_debug ("%s(): Payload size greater than %"G_GSSIZE_FORMAT";"
147 : : "unable to confirm transfer completion",
148 : : G_STRFUNC, G_MAXSSIZE);
149 : : }
150 [ - + ]: 21 : else if (transferred < payload_size)
151 : : {
152 : 0 : g_debug ("%s(): Transfer incomplete (%"G_GSSIZE_FORMAT"/%"G_GOFFSET_FORMAT" bytes)",
153 : : G_STRFUNC, transferred, payload_size);
154 : :
155 : 0 : g_task_return_new_error (task,
156 : : G_IO_ERROR,
157 : : G_IO_ERROR_PARTIAL_INPUT,
158 : : "Transfer incomplete");
159 : 0 : return;
160 : : }
161 : :
162 : : /* Attempt to set file attributes for downloaded files.
163 : : */
164 [ + + ]: 21 : if (op->is_download)
165 : : {
166 : : /* NOTE: this is not supported by the Linux kernel...
167 : : */
168 [ + + ]: 9 : if (valent_packet_get_int (self->packet, "creationTime", &creation_time))
169 : : {
170 : 1 : gboolean success;
171 : 1 : g_autoptr (GError) warn = NULL;
172 : :
173 : 2 : success = g_file_set_attribute_uint64 (self->file,
174 : : G_FILE_ATTRIBUTE_TIME_CREATED,
175 : 1 : (uint64_t)floor (creation_time / 1000),
176 : : G_FILE_QUERY_INFO_NONE,
177 : : NULL,
178 : : &warn);
179 : :
180 [ - + ]: 1 : if (success)
181 : : {
182 : 0 : g_file_set_attribute_uint32 (self->file,
183 : : G_FILE_ATTRIBUTE_TIME_CREATED_USEC,
184 : 0 : (uint32_t)((creation_time % 1000) * 1000),
185 : : G_FILE_QUERY_INFO_NONE,
186 : : NULL,
187 : : &warn);
188 : : }
189 : :
190 [ + - ]: 1 : if (warn != NULL)
191 : 1 : g_debug ("%s: %s", G_OBJECT_TYPE_NAME (self), warn->message);
192 : : }
193 : :
194 [ + + ]: 9 : if (valent_packet_get_int (self->packet, "lastModified", &last_modified))
195 : : {
196 : 1 : gboolean success;
197 : 1 : g_autoptr (GError) warn = NULL;
198 : :
199 : 2 : success = g_file_set_attribute_uint64 (self->file,
200 : : G_FILE_ATTRIBUTE_TIME_MODIFIED,
201 : 1 : (uint64_t)floor (last_modified / 1000),
202 : : G_FILE_QUERY_INFO_NONE,
203 : : NULL,
204 : : &warn);
205 : :
206 [ + - ]: 1 : if (success)
207 : : {
208 : 1 : g_file_set_attribute_uint32 (self->file,
209 : : G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
210 : 1 : (uint32_t)((last_modified % 1000) * 1000),
211 : : G_FILE_QUERY_INFO_NONE,
212 : : NULL,
213 : : &warn);
214 : : }
215 : :
216 [ - + ]: 1 : if (warn != NULL)
217 : 0 : g_debug ("%s: %s", G_OBJECT_TYPE_NAME (self), warn->message);
218 : : }
219 : : }
220 : :
221 [ + - ]: 21 : g_task_return_boolean (task, TRUE);
222 : : }
223 : :
224 : : static void
225 : 46 : valent_device_transfer_execute_splice (GTask *task)
226 : : {
227 : 46 : TransferOperation *op = g_task_get_task_data (task);
228 : :
229 [ + + + + ]: 46 : if (op->source != NULL && op->target != NULL)
230 : : {
231 : 46 : g_output_stream_splice_async (op->target,
232 : : op->source,
233 : : (G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
234 : : G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET),
235 : 23 : g_task_get_priority (task),
236 : : g_task_get_cancellable (task),
237 : : (GAsyncReadyCallback)g_output_stream_splice_cb,
238 : : g_object_ref (task));
239 : : }
240 : 46 : }
241 : :
242 : : static void
243 : 11 : valent_channel_download_cb (ValentChannel *channel,
244 : : GAsyncResult *result,
245 : : gpointer user_data)
246 : : {
247 : 22 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
248 : 11 : ValentDeviceTransfer *self = g_task_get_source_object (task);
249 : 11 : TransferOperation *op = g_task_get_task_data (task);
250 : 11 : GError *error = NULL;
251 : :
252 : 11 : op->connection = valent_channel_download_finish (channel, result, &error);
253 [ - + ]: 11 : if (op->connection == NULL)
254 : : {
255 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
256 : : {
257 : 0 : g_autoptr (ValentChannel) next = NULL;
258 : :
259 : 0 : next = _valent_device_next_channel (self->device, channel);
260 [ # # ]: 0 : if (next != NULL)
261 : : {
262 : 0 : valent_channel_download_async (next,
263 : : self->packet,
264 : : g_task_get_cancellable (task),
265 : : (GAsyncReadyCallback)valent_channel_download_cb,
266 : : g_object_ref (task));
267 : 0 : return;
268 : : }
269 : : }
270 : :
271 : 0 : g_task_return_error (task, g_steal_pointer (&error));
272 : 0 : return;
273 : : }
274 : :
275 : 11 : op->source = g_object_ref (g_io_stream_get_input_stream (op->connection));
276 [ + - ]: 11 : valent_device_transfer_execute_splice (task);
277 : : }
278 : :
279 : : static void
280 : 11 : g_file_replace_cb (GFile *file,
281 : : GAsyncResult *result,
282 : : gpointer user_data)
283 : : {
284 : 22 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
285 : 11 : TransferOperation *op = g_task_get_task_data (task);
286 [ - - + - ]: 11 : g_autoptr (GFileOutputStream) stream = NULL;
287 : 11 : GError *error = NULL;
288 : :
289 : 11 : stream = g_file_replace_finish (file, result, &error);
290 [ - + ]: 11 : if (stream == NULL)
291 : : {
292 : 0 : g_task_return_error (task, g_steal_pointer (&error));
293 [ # # ]: 0 : return;
294 : : }
295 : :
296 : 11 : op->target = G_OUTPUT_STREAM (g_steal_pointer (&stream));
297 [ + - ]: 11 : valent_device_transfer_execute_splice (task);
298 : : }
299 : :
300 : : static void
301 : 12 : valent_channel_upload_cb (ValentChannel *channel,
302 : : GAsyncResult *result,
303 : : gpointer user_data)
304 : : {
305 : 24 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
306 : 12 : ValentDeviceTransfer *self = g_task_get_source_object (task);
307 : 12 : TransferOperation *op = g_task_get_task_data (task);
308 : 12 : GError *error = NULL;
309 : :
310 : 12 : op->connection = valent_channel_upload_finish (channel, result, &error);
311 [ - + ]: 12 : if (op->connection == NULL)
312 : : {
313 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
314 : : {
315 : 0 : g_autoptr (ValentChannel) next = NULL;
316 : :
317 : 0 : next = _valent_device_next_channel (self->device, channel);
318 [ # # ]: 0 : if (next != NULL)
319 : : {
320 : 0 : valent_channel_upload_async (next,
321 : : self->packet,
322 : : g_task_get_cancellable (task),
323 : : (GAsyncReadyCallback)valent_channel_upload_cb,
324 : : g_object_ref (task));
325 : 0 : return;
326 : : }
327 : : }
328 : :
329 : 0 : g_task_return_error (task, g_steal_pointer (&error));
330 : 0 : return;
331 : : }
332 : :
333 : 12 : op->target = g_object_ref (g_io_stream_get_output_stream (op->connection));
334 [ + - ]: 12 : valent_device_transfer_execute_splice (task);
335 : : }
336 : :
337 : : static void
338 : 12 : g_file_read_cb (GFile *file,
339 : : GAsyncResult *result,
340 : : gpointer user_data)
341 : : {
342 : 24 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
343 : 12 : TransferOperation *op = g_task_get_task_data (task);
344 [ - - + - ]: 12 : g_autoptr (GFileInputStream) stream = NULL;
345 : 12 : GError *error = NULL;
346 : :
347 : 12 : stream = g_file_read_finish (file, result, &error);
348 [ - + ]: 12 : if (stream == NULL)
349 : : {
350 : 0 : g_task_return_error (task, g_steal_pointer (&error));
351 [ # # ]: 0 : return;
352 : : }
353 : :
354 : 12 : op->source = G_INPUT_STREAM (g_steal_pointer (&stream));
355 [ + - ]: 12 : valent_device_transfer_execute_splice (task);
356 : : }
357 : :
358 : : static void
359 : 12 : g_file_query_info_cb (GFile *file,
360 : : GAsyncResult *result,
361 : : gpointer user_data)
362 : : {
363 : 24 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
364 : 12 : ValentDeviceTransfer *self = g_task_get_source_object (task);
365 [ + - - - ]: 12 : g_autoptr (GFileInfo) info = NULL;
366 [ - - ]: 12 : g_autoptr (ValentChannel) channel = NULL;
367 : 12 : GError *error = NULL;
368 : :
369 : 12 : info = g_file_query_info_finish (file, result, &error);
370 [ - + ]: 12 : if (info == NULL)
371 : : {
372 : 0 : g_task_return_error (task, g_steal_pointer (&error));
373 : 0 : return;
374 : : }
375 : :
376 : 12 : channel = g_list_model_get_item (valent_device_get_channels (self->device), 0);
377 [ - + ]: 12 : if (channel == NULL)
378 : : {
379 : 0 : g_task_return_new_error (task,
380 : : G_IO_ERROR,
381 : : G_IO_ERROR_NOT_CONNECTED,
382 : : "Device is disconnected");
383 : 0 : return;
384 : : }
385 : :
386 : 12 : valent_device_transfer_update_packet (self->packet, info);
387 : 12 : valent_channel_upload_async (channel,
388 : : self->packet,
389 : : g_task_get_cancellable (task),
390 : : (GAsyncReadyCallback)valent_channel_upload_cb,
391 : : g_object_ref (task));
392 : 12 : g_file_read_async (file,
393 : : G_PRIORITY_DEFAULT,
394 : : g_task_get_cancellable (task),
395 : : (GAsyncReadyCallback)g_file_read_cb,
396 : : g_object_ref (task));
397 : : }
398 : :
399 : : static void
400 : 23 : valent_device_transfer_execute (ValentTransfer *transfer,
401 : : GCancellable *cancellable,
402 : : GAsyncReadyCallback callback,
403 : : gpointer user_data)
404 : : {
405 : 23 : ValentDeviceTransfer *self = VALENT_DEVICE_TRANSFER (transfer);
406 : 46 : g_autoptr (GTask) task = NULL;
407 : 23 : TransferOperation *op = NULL;
408 : :
409 : 23 : VALENT_ENTRY;
410 : :
411 [ - + ]: 23 : g_assert (VALENT_IS_DEVICE_TRANSFER (transfer));
412 [ + - + - : 23 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
413 : :
414 : : /* Determine now if this is a download or an upload. This should be reliable,
415 : : * given that the channel service must set the `payloadTransferInfo` field in
416 : : * its [vfunc@Valent.Channel.upload] implementation.
417 : : */
418 : 23 : op = g_new0 (TransferOperation, 1);
419 : 23 : op->is_download = valent_packet_has_payload (self->packet);
420 : :
421 : 23 : task = g_task_new (transfer, cancellable, callback, user_data);
422 [ + - ]: 23 : g_task_set_source_tag (task, valent_device_transfer_execute);
423 : 23 : g_task_set_task_data (task, op, transfer_operation_free);
424 : :
425 : : /* If this is a download prepare the connection and file stream in parallel,
426 : : * otherwise start by reading the file metadata for the upload.
427 : : */
428 [ + + ]: 23 : if (op->is_download)
429 : : {
430 : 11 : g_autoptr (ValentChannel) channel = NULL;
431 : :
432 : 11 : channel = g_list_model_get_item (valent_device_get_channels (self->device), 0);
433 [ - + ]: 11 : if (channel == NULL)
434 : : {
435 : 0 : g_task_return_new_error (task,
436 : : G_IO_ERROR,
437 : : G_IO_ERROR_NOT_CONNECTED,
438 : : "Device is disconnected");
439 : 0 : return;
440 : : }
441 : :
442 : 22 : g_file_replace_async (self->file,
443 : : NULL,
444 : : FALSE,
445 : : G_FILE_CREATE_REPLACE_DESTINATION,
446 : 11 : g_task_get_priority (task),
447 : : g_task_get_cancellable (task),
448 : : (GAsyncReadyCallback)g_file_replace_cb,
449 : : g_object_ref (task));
450 : 11 : valent_channel_download_async (channel,
451 : : self->packet,
452 : : g_task_get_cancellable (task),
453 : : (GAsyncReadyCallback)valent_channel_download_cb,
454 : : g_object_ref (task));
455 : : }
456 : : else
457 : : {
458 : 24 : g_file_query_info_async (self->file,
459 : : G_FILE_ATTRIBUTE_TIME_CREATED","
460 : : G_FILE_ATTRIBUTE_TIME_CREATED_USEC","
461 : : G_FILE_ATTRIBUTE_TIME_MODIFIED","
462 : : G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC","
463 : : G_FILE_ATTRIBUTE_STANDARD_SIZE,
464 : : G_FILE_QUERY_INFO_NONE,
465 : 12 : g_task_get_priority (task),
466 : : g_task_get_cancellable (task),
467 : : (GAsyncReadyCallback)g_file_query_info_cb,
468 : : g_object_ref (task));
469 : : }
470 : :
471 [ + - ]: 23 : VALENT_EXIT;
472 : : }
473 : :
474 : : /*
475 : : * GObject
476 : : */
477 : : static void
478 : 18 : valent_device_transfer_finalize (GObject *object)
479 : : {
480 : 18 : ValentDeviceTransfer *self = VALENT_DEVICE_TRANSFER (object);
481 : :
482 : 18 : valent_object_lock (VALENT_OBJECT (self));
483 [ + - ]: 18 : g_clear_object (&self->device);
484 [ + - ]: 18 : g_clear_object (&self->file);
485 [ + - ]: 18 : g_clear_pointer (&self->packet, json_node_unref);
486 : 18 : valent_object_unlock (VALENT_OBJECT (self));
487 : :
488 : 18 : G_OBJECT_CLASS (valent_device_transfer_parent_class)->finalize (object);
489 : 18 : }
490 : :
491 : : static void
492 : 11 : valent_device_transfer_get_property (GObject *object,
493 : : guint prop_id,
494 : : GValue *value,
495 : : GParamSpec *pspec)
496 : : {
497 : 11 : ValentDeviceTransfer *self = VALENT_DEVICE_TRANSFER (object);
498 : :
499 [ + + + - ]: 11 : switch ((ValentDeviceTransferProperty)prop_id)
500 : : {
501 : 4 : case PROP_DEVICE:
502 : 4 : g_value_take_object (value, valent_device_transfer_ref_device (self));
503 : 4 : break;
504 : :
505 : 5 : case PROP_FILE:
506 : 5 : g_value_take_object (value, valent_device_transfer_ref_file (self));
507 : 5 : break;
508 : :
509 : 2 : case PROP_PACKET:
510 : 2 : g_value_take_boxed (value, valent_device_transfer_ref_packet (self));
511 : 2 : break;
512 : :
513 : 0 : default:
514 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
515 : : }
516 : 11 : }
517 : :
518 : : static void
519 : 69 : valent_device_transfer_set_property (GObject *object,
520 : : guint prop_id,
521 : : const GValue *value,
522 : : GParamSpec *pspec)
523 : : {
524 : 69 : ValentDeviceTransfer *self = VALENT_DEVICE_TRANSFER (object);
525 : :
526 [ + + + - ]: 69 : switch ((ValentDeviceTransferProperty)prop_id)
527 : : {
528 : : case PROP_DEVICE:
529 : 23 : valent_object_lock (VALENT_OBJECT (self));
530 : 23 : self->device = g_value_dup_object (value);
531 : 23 : valent_object_unlock (VALENT_OBJECT (self));
532 : 23 : break;
533 : :
534 : : case PROP_FILE:
535 : 23 : valent_object_lock (VALENT_OBJECT (self));
536 : 23 : self->file = g_value_dup_object (value);
537 : 23 : valent_object_unlock (VALENT_OBJECT (self));
538 : 23 : break;
539 : :
540 : : case PROP_PACKET:
541 : 23 : valent_object_lock (VALENT_OBJECT (self));
542 : 23 : self->packet = g_value_dup_boxed (value);
543 : 23 : valent_object_unlock (VALENT_OBJECT (self));
544 : 23 : break;
545 : :
546 : 0 : default:
547 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
548 : : }
549 : 69 : }
550 : :
551 : : static void
552 : 8 : valent_device_transfer_class_init (ValentDeviceTransferClass *klass)
553 : : {
554 : 8 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
555 : 8 : ValentTransferClass *transfer_class = VALENT_TRANSFER_CLASS (klass);
556 : :
557 : 8 : object_class->finalize = valent_device_transfer_finalize;
558 : 8 : object_class->get_property = valent_device_transfer_get_property;
559 : 8 : object_class->set_property = valent_device_transfer_set_property;
560 : :
561 : 8 : transfer_class->execute = valent_device_transfer_execute;
562 : :
563 : : /**
564 : : * ValentDeviceTransfer:device: (getter ref_device)
565 : : *
566 : : * The [class@Valent.Device] this transfer is for.
567 : : *
568 : : * Since: 1.0
569 : : */
570 : 16 : properties [PROP_DEVICE] =
571 : 8 : g_param_spec_object ("device", NULL, NULL,
572 : : VALENT_TYPE_DEVICE,
573 : : (G_PARAM_READWRITE |
574 : : G_PARAM_CONSTRUCT_ONLY |
575 : : G_PARAM_EXPLICIT_NOTIFY |
576 : : G_PARAM_STATIC_STRINGS));
577 : :
578 : : /**
579 : : * ValentDeviceTransfer:file: (getter ref_file)
580 : : *
581 : : * The local [iface@Gio.File].
582 : : *
583 : : * If this a download, then this is the destination. If it's upload, this is
584 : : * the source.
585 : : *
586 : : * Since: 1.0
587 : : */
588 : 16 : properties [PROP_FILE] =
589 : 8 : g_param_spec_object ("file", NULL, NULL,
590 : : G_TYPE_FILE,
591 : : (G_PARAM_READWRITE |
592 : : G_PARAM_CONSTRUCT_ONLY |
593 : : G_PARAM_EXPLICIT_NOTIFY |
594 : : G_PARAM_STATIC_STRINGS));
595 : :
596 : : /**
597 : : * ValentDeviceTransfer:packet: (getter ref_packet)
598 : : *
599 : : * The KDE Connect packet describing the payload.
600 : : *
601 : : * Since: 1.0
602 : : */
603 : 16 : properties [PROP_PACKET] =
604 : 8 : g_param_spec_boxed ("packet", NULL, NULL,
605 : : JSON_TYPE_NODE,
606 : : (G_PARAM_READWRITE |
607 : : G_PARAM_CONSTRUCT_ONLY |
608 : : G_PARAM_EXPLICIT_NOTIFY |
609 : : G_PARAM_STATIC_STRINGS));
610 : :
611 : 8 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
612 : 8 : }
613 : :
614 : : static void
615 : 23 : valent_device_transfer_init (ValentDeviceTransfer *self)
616 : : {
617 : 23 : }
618 : :
619 : : /**
620 : : * valent_device_transfer_new:
621 : : * @device: a `ValentDevice`
622 : : * @packet: a KDE Connect packet
623 : : * @file: a `GFile`
624 : : *
625 : : * A convenience for creating a simple file transfer.
626 : : *
627 : : * Returns: (transfer full) (nullable): a `ValentDeviceTransfer`
628 : : *
629 : : * Since: 1.0
630 : : */
631 : : ValentTransfer *
632 : 23 : valent_device_transfer_new (ValentDevice *device,
633 : : JsonNode *packet,
634 : : GFile *file)
635 : : {
636 [ - + ]: 23 : g_return_val_if_fail (VALENT_IS_DEVICE (device), NULL);
637 [ + - ]: 23 : g_return_val_if_fail (VALENT_IS_PACKET (packet), NULL);
638 [ + - + - : 23 : g_return_val_if_fail (G_IS_FILE (file), NULL);
+ - + - ]
639 : :
640 : 23 : return g_object_new (VALENT_TYPE_DEVICE_TRANSFER,
641 : : "device", device,
642 : : "packet", packet,
643 : : "file", file,
644 : : NULL);
645 : : }
646 : :
647 : : /**
648 : : * valent_device_transfer_ref_device: (get-property device)
649 : : * @transfer: a `ValentDeviceTransfer`
650 : : *
651 : : * Get the [class@Valent.Device].
652 : : *
653 : : * Returns: (transfer full) (nullable): a `ValentDevice`
654 : : *
655 : : * Since: 1.0
656 : : */
657 : : ValentDevice *
658 : 4 : valent_device_transfer_ref_device (ValentDeviceTransfer *transfer)
659 : : {
660 : 8 : g_autoptr (ValentDevice) ret = NULL;
661 : :
662 [ - + ]: 4 : g_return_val_if_fail (VALENT_IS_DEVICE_TRANSFER (transfer), NULL);
663 : :
664 : 4 : valent_object_lock (VALENT_OBJECT (transfer));
665 [ + - ]: 4 : if (transfer->device != NULL)
666 : 4 : ret = g_object_ref (transfer->device);
667 : 4 : valent_object_unlock (VALENT_OBJECT (transfer));
668 : :
669 : 4 : return g_steal_pointer (&ret);
670 : : }
671 : :
672 : : /**
673 : : * valent_device_transfer_ref_file: (get-property file)
674 : : * @transfer: a `ValentDeviceTransfer`
675 : : *
676 : : * Get the local [iface@Gio.File].
677 : : *
678 : : * Returns: (transfer full) (nullable): a `GFile`
679 : : *
680 : : * Since: 1.0
681 : : */
682 : : GFile *
683 : 10 : valent_device_transfer_ref_file (ValentDeviceTransfer *transfer)
684 : : {
685 : 20 : g_autoptr (GFile) ret = NULL;
686 : :
687 [ - + ]: 10 : g_return_val_if_fail (VALENT_IS_DEVICE_TRANSFER (transfer), NULL);
688 : :
689 : 10 : valent_object_lock (VALENT_OBJECT (transfer));
690 [ + - ]: 10 : if (transfer->file != NULL)
691 : 10 : ret = g_object_ref (transfer->file);
692 : 10 : valent_object_unlock (VALENT_OBJECT (transfer));
693 : :
694 : 10 : return g_steal_pointer (&ret);
695 : : }
696 : :
697 : : /**
698 : : * valent_device_transfer_ref_packet: (get-property packet)
699 : : * @transfer: a `ValentDeviceTransfer`
700 : : *
701 : : * Get the KDE Connect packet.
702 : : *
703 : : * Returns: (transfer full) (nullable): a KDE Connect packet
704 : : *
705 : : * Since: 1.0
706 : : */
707 : : JsonNode *
708 : 22 : valent_device_transfer_ref_packet (ValentDeviceTransfer *transfer)
709 : : {
710 : 44 : g_autoptr (JsonNode) ret = NULL;
711 : :
712 [ - + ]: 22 : g_return_val_if_fail (VALENT_IS_DEVICE_TRANSFER (transfer), NULL);
713 : :
714 : 22 : valent_object_lock (VALENT_OBJECT (transfer));
715 [ + - ]: 22 : if (transfer->packet != NULL)
716 : 22 : ret = json_node_ref (transfer->packet);
717 : 22 : valent_object_unlock (VALENT_OBJECT (transfer));
718 : :
719 : 22 : return g_steal_pointer (&ret);
720 : : }
721 : :
|