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