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-transfer"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : :
10 : : #include "valent-core-enums.h"
11 : : #include "valent-debug.h"
12 : : #include "valent-macros.h"
13 : : #include "valent-object.h"
14 : : #include "valent-transfer.h"
15 : :
16 : :
17 : : /**
18 : : * ValentTransfer:
19 : : *
20 : : * An abstract base class for data transfers.
21 : : *
22 : : * `ValentTransfer` is a generic class for transfers.
23 : : *
24 : : * Since: 1.0
25 : : */
26 : :
27 : : typedef struct
28 : : {
29 : : GCancellable *cancellable;
30 : : GError *error;
31 : : char *id;
32 : : double progress;
33 : : ValentTransferState state;
34 : : } ValentTransferPrivate;
35 : :
36 [ + + + - ]: 732 : G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ValentTransfer, valent_transfer, VALENT_TYPE_OBJECT)
37 : :
38 : : typedef enum {
39 : : PROP_ID = 1,
40 : : PROP_PROGRESS,
41 : : PROP_STATE,
42 : : } ValentTransferProperty;
43 : :
44 : : static GParamSpec *properties[PROP_STATE + 1] = { NULL, };
45 : :
46 : :
47 : : /* LCOV_EXCL_START */
48 : : static void
49 : : valent_transfer_real_execute (ValentTransfer *transfer,
50 : : GCancellable *cancellable,
51 : : GAsyncReadyCallback callback,
52 : : gpointer user_data)
53 : : {
54 : : g_assert (VALENT_IS_TRANSFER (transfer));
55 : : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
56 : :
57 : : g_task_report_new_error (transfer, callback, user_data,
58 : : valent_transfer_real_execute,
59 : : G_IO_ERROR,
60 : : G_IO_ERROR_NOT_SUPPORTED,
61 : : "%s does not implement execute()",
62 : : G_OBJECT_TYPE_NAME (transfer));
63 : : }
64 : :
65 : : static gboolean
66 : : valent_transfer_real_execute_finish (ValentTransfer *transfer,
67 : : GAsyncResult *result,
68 : : GError **error)
69 : : {
70 : : g_assert (VALENT_IS_TRANSFER (transfer));
71 : : g_assert (g_task_is_valid (result, transfer));
72 : : g_assert (error == NULL || *error == NULL);
73 : :
74 : : return g_task_propagate_boolean (G_TASK (result), error);
75 : : }
76 : : /* LCOV_EXCL_STOP */
77 : :
78 : : /*
79 : : * GObject
80 : : */
81 : : static void
82 : 31 : valent_transfer_finalize (GObject *object)
83 : : {
84 : 31 : ValentTransfer *self = VALENT_TRANSFER (object);
85 : 31 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (self);
86 : :
87 : 31 : valent_object_lock (VALENT_OBJECT (self));
88 [ + - ]: 31 : g_clear_object (&priv->cancellable);
89 : 31 : g_clear_error (&priv->error);
90 [ + + ]: 31 : g_clear_pointer (&priv->id, g_free);
91 : 31 : valent_object_unlock (VALENT_OBJECT (self));
92 : :
93 : 31 : G_OBJECT_CLASS (valent_transfer_parent_class)->finalize (object);
94 : 31 : }
95 : :
96 : : static void
97 : 40 : valent_transfer_get_property (GObject *object,
98 : : guint prop_id,
99 : : GValue *value,
100 : : GParamSpec *pspec)
101 : : {
102 : 40 : ValentTransfer *self = VALENT_TRANSFER (object);
103 : :
104 [ + - + - ]: 40 : switch ((ValentTransferProperty)prop_id)
105 : : {
106 : 20 : case PROP_ID:
107 : 20 : g_value_take_string (value, valent_transfer_dup_id (self));
108 : 20 : break;
109 : :
110 : 0 : case PROP_PROGRESS:
111 : 0 : g_value_set_double (value, valent_transfer_get_progress (self));
112 : 0 : break;
113 : :
114 : 20 : case PROP_STATE:
115 : 20 : g_value_set_enum (value, valent_transfer_get_state (self));
116 : 20 : break;
117 : :
118 : 0 : default:
119 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
120 : : }
121 : 40 : }
122 : :
123 : : static void
124 : 35 : valent_transfer_set_property (GObject *object,
125 : : guint prop_id,
126 : : const GValue *value,
127 : : GParamSpec *pspec)
128 : : {
129 : 35 : ValentTransfer *self = VALENT_TRANSFER (object);
130 : 35 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (self);
131 : :
132 [ + - - ]: 35 : switch ((ValentTransferProperty)prop_id)
133 : : {
134 : : case PROP_ID:
135 : 35 : valent_object_lock (VALENT_OBJECT (self));
136 : 35 : priv->id = g_value_dup_string (value);
137 : 35 : valent_object_unlock (VALENT_OBJECT (self));
138 : 35 : break;
139 : :
140 : 0 : case PROP_PROGRESS:
141 : 0 : valent_transfer_set_progress (self, g_value_get_double (value));
142 : 0 : break;
143 : :
144 : 0 : case PROP_STATE:
145 : : default:
146 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
147 : : }
148 : 35 : }
149 : :
150 : : static void
151 : 8 : valent_transfer_class_init (ValentTransferClass *klass)
152 : : {
153 : 8 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
154 : :
155 : 8 : object_class->finalize = valent_transfer_finalize;
156 : 8 : object_class->get_property = valent_transfer_get_property;
157 : 8 : object_class->set_property = valent_transfer_set_property;
158 : :
159 : 8 : klass->execute = valent_transfer_real_execute;
160 : 8 : klass->execute_finish = valent_transfer_real_execute_finish;
161 : :
162 : : /**
163 : : * ValentTransfer:id: (getter ref_id)
164 : : *
165 : : * A unique identifier for the transfer.
166 : : *
167 : : * If not specified at construction, a random UUID will be generated on-demand
168 : : * with [func@GLib.uuid_string_random].
169 : : *
170 : : * This property is thread-safe. Emissions of [signal@GObject.Object::notify]
171 : : * are guaranteed to happen in the main thread.
172 : : *
173 : : * Since: 1.0
174 : : */
175 : 16 : properties [PROP_ID] =
176 : 8 : g_param_spec_string ("id", NULL, NULL,
177 : : NULL,
178 : : (G_PARAM_READWRITE |
179 : : G_PARAM_CONSTRUCT_ONLY |
180 : : G_PARAM_EXPLICIT_NOTIFY |
181 : : G_PARAM_STATIC_STRINGS));
182 : :
183 : : /**
184 : : * ValentTransfer:progress: (getter get_progress) (setter set_progress)
185 : : *
186 : : * The progress of the transfer.
187 : : *
188 : : * This value will change from `0.0` to `1.0` during the course of the
189 : : * operation. It is guaranteed to change to `1.0` when the transfer operation
190 : : * completes, but not guaranteed to change before that unless set by an
191 : : * implementation.
192 : : *
193 : : * This property is thread-safe. Emissions of [signal@GObject.Object::notify]
194 : : * are guaranteed to happen in the main thread.
195 : : *
196 : : * Since: 1.0
197 : : */
198 : 16 : properties [PROP_PROGRESS] =
199 : 8 : g_param_spec_double ("progress", NULL, NULL,
200 : : 0.0, 1.0,
201 : : 0.0,
202 : : (G_PARAM_READWRITE |
203 : : G_PARAM_EXPLICIT_NOTIFY |
204 : : G_PARAM_STATIC_STRINGS));
205 : :
206 : : /**
207 : : * ValentTransfer:state: (getter get_state)
208 : : *
209 : : * The [enum@Valent.TransferState] of the transfer.
210 : : *
211 : : * The value will change from %VALENT_TRANSFER_STATE_PENDING to
212 : : * %VALENT_TRANSFER_STATE_ACTIVE when [method@Valent.Transfer.execute] is
213 : : * called. When the operation completes it will change to either
214 : : * %VALENT_TRANSFER_STATE_COMPLETE or %VALENT_TRANSFER_STATE_FAILED.
215 : : *
216 : : * This property is thread-safe. Emissions of [signal@GObject.Object::notify]
217 : : * are guaranteed to happen in the main thread.
218 : : *
219 : : * Since: 1.0
220 : : */
221 : 16 : properties [PROP_STATE] =
222 : 8 : g_param_spec_enum ("state", NULL, NULL,
223 : : VALENT_TYPE_TRANSFER_STATE,
224 : : VALENT_TRANSFER_STATE_PENDING,
225 : : (G_PARAM_READABLE |
226 : : G_PARAM_EXPLICIT_NOTIFY |
227 : : G_PARAM_STATIC_STRINGS));
228 : :
229 : 8 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
230 : 8 : }
231 : :
232 : : static void
233 : 35 : valent_transfer_init (ValentTransfer *self)
234 : : {
235 : 35 : }
236 : :
237 : : /**
238 : : * valent_transfer_dup_id: (get-property id)
239 : : * @transfer: a `ValentTransfer`
240 : : *
241 : : * Get the transfer ID.
242 : : *
243 : : * Returns: (transfer full) (not nullable): a unique ID
244 : : *
245 : : * Since: 1.0
246 : : */
247 : : char *
248 : 38 : valent_transfer_dup_id (ValentTransfer *transfer)
249 : : {
250 : 38 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (transfer);
251 : 76 : g_autofree char *ret = NULL;
252 : :
253 [ + - ]: 38 : g_return_val_if_fail (VALENT_IS_TRANSFER (transfer), NULL);
254 : :
255 : 38 : valent_object_lock (VALENT_OBJECT (transfer));
256 [ + + ]: 38 : if (priv->id == NULL)
257 : 10 : priv->id = g_uuid_string_random ();
258 [ - + ]: 38 : ret = g_strdup (priv->id);
259 : 38 : valent_object_unlock (VALENT_OBJECT (transfer));
260 : :
261 : 38 : return g_steal_pointer (&ret);
262 : : }
263 : :
264 : : /**
265 : : * valent_transfer_get_progress: (get-property progress)
266 : : * @transfer: a `ValentTransfer`
267 : : *
268 : : * Get the transfer progress.
269 : : *
270 : : * Returns: a number from `0.0` to `1.0`
271 : : *
272 : : * Since: 1.0
273 : : */
274 : : double
275 : 0 : valent_transfer_get_progress (ValentTransfer *transfer)
276 : : {
277 : 0 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (transfer);
278 : 0 : double ret;
279 : :
280 [ # # ]: 0 : g_return_val_if_fail (VALENT_IS_TRANSFER (transfer), 0.0);
281 : :
282 : 0 : valent_object_lock (VALENT_OBJECT (transfer));
283 : 0 : ret = priv->progress;
284 : 0 : valent_object_unlock (VALENT_OBJECT (transfer));
285 : :
286 : 0 : return ret;
287 : : }
288 : :
289 : : /**
290 : : * valent_transfer_set_progress: (set-property progress)
291 : : * @transfer: a `ValentTransfer`
292 : : * @progress: a number from `0.0` to `1.0`
293 : : *
294 : : * Set the transfer progress.
295 : : *
296 : : * This method should only be called by implementations of
297 : : * [class@Valent.Transfer].
298 : : *
299 : : * Since: 1.0
300 : : */
301 : : void
302 : 34 : valent_transfer_set_progress (ValentTransfer *transfer,
303 : : double progress)
304 : : {
305 : 34 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (transfer);
306 : :
307 [ + - ]: 34 : g_return_if_fail (VALENT_IS_TRANSFER (transfer));
308 [ + - - + ]: 34 : g_return_if_fail (progress >= 0.0 && progress <= 1.0);
309 : :
310 : 34 : valent_object_lock (VALENT_OBJECT (transfer));
311 [ - + + - ]: 34 : if (!G_APPROX_VALUE (priv->progress, progress, 0.01))
312 : : {
313 : 34 : priv->progress = progress;
314 : 34 : valent_object_notify_by_pspec (VALENT_OBJECT (transfer),
315 : : properties [PROP_PROGRESS]);
316 : : }
317 : 34 : valent_object_unlock (VALENT_OBJECT (transfer));
318 : : }
319 : :
320 : : /**
321 : : * valent_transfer_get_state: (get-property state)
322 : : * @transfer: a `ValentTransfer`
323 : : *
324 : : * Get the transfer state.
325 : : *
326 : : * Returns: a `ValentTransferState`
327 : : *
328 : : * Since: 1.0
329 : : */
330 : : ValentTransferState
331 : 30 : valent_transfer_get_state (ValentTransfer *transfer)
332 : : {
333 : 30 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (transfer);
334 : 30 : ValentTransferState ret = VALENT_TRANSFER_STATE_PENDING;
335 : :
336 [ + - ]: 30 : g_return_val_if_fail (VALENT_IS_TRANSFER (transfer), FALSE);
337 : :
338 : 30 : valent_object_lock (VALENT_OBJECT (transfer));
339 : 30 : ret = priv->state;
340 : 30 : valent_object_unlock (VALENT_OBJECT (transfer));
341 : :
342 : 30 : return ret;
343 : : }
344 : :
345 : : static void
346 : 34 : valent_transfer_execute_cb (GObject *object,
347 : : GAsyncResult *result,
348 : : gpointer user_data)
349 : : {
350 : 34 : ValentTransfer *self = VALENT_TRANSFER (object);
351 : 34 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (self);
352 : 68 : g_autoptr (GTask) task = G_TASK (user_data);
353 : :
354 : 34 : VALENT_ENTRY;
355 : :
356 [ + - ]: 34 : g_assert (VALENT_IS_TRANSFER (self));
357 [ - + ]: 34 : g_assert (g_task_is_valid (result, self));
358 [ + - + - : 34 : g_assert (G_IS_TASK (task));
- + - - ]
359 : :
360 : 34 : valent_transfer_set_progress (self, 1.0);
361 : :
362 : 34 : valent_object_lock (VALENT_OBJECT (self));
363 [ + + ]: 34 : if (g_task_propagate_boolean (G_TASK (result), &priv->error))
364 : : {
365 : 32 : priv->state = VALENT_TRANSFER_STATE_COMPLETE;
366 : 32 : valent_object_unlock (VALENT_OBJECT (self));
367 : :
368 : 32 : g_task_return_boolean (task, TRUE);
369 : : }
370 : : else
371 : : {
372 : 2 : priv->state = VALENT_TRANSFER_STATE_FAILED;
373 : 2 : valent_object_unlock (VALENT_OBJECT (self));
374 : :
375 : 2 : g_task_return_error (task, g_error_copy (priv->error));
376 : : }
377 : :
378 : 34 : valent_object_notify_by_pspec (VALENT_OBJECT (self), properties [PROP_STATE]);
379 : :
380 : 34 : VALENT_EXIT;
381 : : }
382 : :
383 : : /**
384 : : * valent_transfer_execute: (virtual execute)
385 : : * @transfer: a `ValentTransfer`
386 : : * @cancellable: (nullable): a `GCancellable`
387 : : * @callback: (scope async): a `GAsyncReadyCallback`
388 : : * @user_data: user supplied data
389 : : *
390 : : * Start the transfer operation.
391 : : *
392 : : * Get the result with [method@Valent.Transfer.execute_finish].
393 : : *
394 : : * If the transfer operation has already started, this call will fail and
395 : : * [method@Valent.Transfer.execute_finish] will return %G_IO_ERROR_PENDING.
396 : : *
397 : : * Since: 1.0
398 : : */
399 : : void
400 : 35 : valent_transfer_execute (ValentTransfer *transfer,
401 : : GCancellable *cancellable,
402 : : GAsyncReadyCallback callback,
403 : : gpointer user_data)
404 : : {
405 : 35 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (transfer);
406 : 70 : g_autoptr (GTask) task = NULL;
407 : 70 : g_autoptr (GCancellable) destroy = NULL;
408 : :
409 : 35 : VALENT_ENTRY;
410 : :
411 [ + - ]: 35 : g_return_if_fail (VALENT_IS_TRANSFER (transfer));
412 [ + + + - : 35 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
413 : :
414 : 35 : valent_object_lock (VALENT_OBJECT (transfer));
415 [ - + ]: 35 : if (priv->state != VALENT_TRANSFER_STATE_PENDING)
416 : : {
417 : 0 : g_task_report_new_error (transfer, callback, user_data,
418 : : valent_transfer_execute,
419 : : G_IO_ERROR,
420 : : G_IO_ERROR_PENDING,
421 : : "%s is already in progress",
422 : 0 : G_OBJECT_TYPE_NAME (transfer));
423 : 0 : valent_object_unlock (VALENT_OBJECT (transfer));
424 : 0 : VALENT_EXIT;
425 : : }
426 : :
427 [ - + ]: 35 : g_clear_object (&priv->cancellable);
428 : 35 : priv->cancellable = g_cancellable_new ();
429 [ + + ]: 35 : if (cancellable != NULL)
430 : : {
431 : 19 : g_signal_connect_object (cancellable,
432 : : "cancelled",
433 : : G_CALLBACK (g_cancellable_cancel),
434 : : priv->cancellable,
435 : : G_CONNECT_SWAPPED);
436 : : }
437 : :
438 : 35 : task = g_task_new (transfer, destroy, callback, user_data);
439 [ + - ]: 35 : g_task_set_source_tag (task, valent_transfer_execute);
440 : :
441 : 35 : VALENT_TRANSFER_GET_CLASS (transfer)->execute (transfer,
442 : : priv->cancellable,
443 : : valent_transfer_execute_cb,
444 : : g_steal_pointer (&task));
445 : :
446 : 35 : priv->state = VALENT_TRANSFER_STATE_ACTIVE;
447 : 35 : valent_object_unlock (VALENT_OBJECT (transfer));
448 : :
449 : 35 : valent_object_notify_by_pspec (VALENT_OBJECT (transfer), properties [PROP_STATE]);
450 : :
451 : 35 : VALENT_EXIT;
452 : : }
453 : :
454 : : /**
455 : : * valent_transfer_execute_finish: (virtual execute_finish)
456 : : * @transfer: a `ValentTransfer`
457 : : * @result: a `GAsyncResult`
458 : : * @error: (nullable): a `GError`
459 : : *
460 : : * Finish an operation started by [method@Valent.Transfer.execute].
461 : : *
462 : : * Returns: %TRUE if successful, or %FALSE with @error set
463 : : *
464 : : * Since: 1.0
465 : : */
466 : : gboolean
467 : 34 : valent_transfer_execute_finish (ValentTransfer *transfer,
468 : : GAsyncResult *result,
469 : : GError **error)
470 : : {
471 : 34 : gboolean ret;
472 : :
473 : 34 : VALENT_ENTRY;
474 : :
475 [ + - ]: 34 : g_return_val_if_fail (VALENT_IS_TRANSFER (transfer), FALSE);
476 [ - + ]: 34 : g_return_val_if_fail (g_task_is_valid (result, transfer), FALSE);
477 [ + - - + ]: 34 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
478 : :
479 : 34 : ret = VALENT_TRANSFER_GET_CLASS (transfer)->execute_finish (transfer,
480 : : result,
481 : : error);
482 : :
483 : 34 : VALENT_RETURN (ret);
484 : : }
485 : :
486 : : /**
487 : : * valent_transfer_cancel:
488 : : * @transfer: a `ValentTransfer`
489 : : *
490 : : * Cancel the transfer operation.
491 : : *
492 : : * If this is called before [method@Valent.Transfer.execute] the transfer will
493 : : * fail unconditionally.
494 : : *
495 : : * Since: 1.0
496 : : */
497 : : void
498 : 0 : valent_transfer_cancel (ValentTransfer *transfer)
499 : : {
500 : 0 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (transfer);
501 : :
502 : 0 : VALENT_ENTRY;
503 : :
504 [ # # ]: 0 : g_return_if_fail (VALENT_IS_TRANSFER (transfer));
505 : :
506 : 0 : g_cancellable_cancel (priv->cancellable);
507 : :
508 : 0 : VALENT_EXIT;
509 : : }
510 : :
511 : : /**
512 : : * valent_transfer_check_status:
513 : : * @transfer: a `ValentTransfer`
514 : : * @error: (nullable): a `GError`
515 : : *
516 : : * Check the transfer status.
517 : : *
518 : : * Returns %TRUE if the transfer operation is in progress or completed
519 : : * successfully. Returns %FALSE with @error set if the transfer failed.
520 : : *
521 : : * Returns: %TRUE, or %FALSE with @error set
522 : : *
523 : : * Since: 1.0
524 : : */
525 : : gboolean
526 : 0 : valent_transfer_check_status (ValentTransfer *transfer,
527 : : GError **error)
528 : : {
529 : 0 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (transfer);
530 : 0 : gboolean ret = TRUE;
531 : :
532 : 0 : VALENT_ENTRY;
533 : :
534 [ # # ]: 0 : g_return_val_if_fail (VALENT_IS_TRANSFER (transfer), FALSE);
535 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
536 : :
537 : 0 : valent_object_lock (VALENT_OBJECT (transfer));
538 [ # # ]: 0 : if (priv->error != NULL)
539 : : {
540 [ # # ]: 0 : if (error != NULL)
541 : 0 : *error = g_error_copy (priv->error);
542 : : ret = FALSE;
543 : : }
544 : 0 : valent_object_unlock (VALENT_OBJECT (transfer));
545 : :
546 : 0 : VALENT_RETURN (ret);
547 : : }
548 : :
|