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 [ + + + - ]: 740 : 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 : 33 : valent_transfer_finalize (GObject *object)
83 : : {
84 : 33 : ValentTransfer *self = VALENT_TRANSFER (object);
85 : 33 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (self);
86 : :
87 : 33 : valent_object_lock (VALENT_OBJECT (self));
88 [ + - ]: 33 : g_clear_object (&priv->cancellable);
89 : 33 : g_clear_error (&priv->error);
90 [ + + ]: 33 : g_clear_pointer (&priv->id, g_free);
91 : 33 : valent_object_unlock (VALENT_OBJECT (self));
92 : :
93 : 33 : G_OBJECT_CLASS (valent_transfer_parent_class)->finalize (object);
94 : 33 : }
95 : :
96 : : static void
97 : 42 : valent_transfer_get_property (GObject *object,
98 : : guint prop_id,
99 : : GValue *value,
100 : : GParamSpec *pspec)
101 : : {
102 : 42 : ValentTransfer *self = VALENT_TRANSFER (object);
103 : :
104 [ + - + - ]: 42 : switch ((ValentTransferProperty)prop_id)
105 : : {
106 : 21 : case PROP_ID:
107 : 21 : g_value_take_string (value, valent_transfer_dup_id (self));
108 : 21 : break;
109 : :
110 : 0 : case PROP_PROGRESS:
111 : 0 : g_value_set_double (value, valent_transfer_get_progress (self));
112 : 0 : break;
113 : :
114 : 21 : case PROP_STATE:
115 : 21 : g_value_set_enum (value, valent_transfer_get_state (self));
116 : 21 : break;
117 : :
118 : 0 : default:
119 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
120 : : }
121 : 42 : }
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 : 9 : 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 : g_object_notify_by_pspec (G_OBJECT (transfer), properties[PROP_PROGRESS]);
315 : : }
316 : 34 : valent_object_unlock (VALENT_OBJECT (transfer));
317 : : }
318 : :
319 : : /**
320 : : * valent_transfer_get_state: (get-property state)
321 : : * @transfer: a `ValentTransfer`
322 : : *
323 : : * Get the transfer state.
324 : : *
325 : : * Returns: a `ValentTransferState`
326 : : *
327 : : * Since: 1.0
328 : : */
329 : : ValentTransferState
330 : 31 : valent_transfer_get_state (ValentTransfer *transfer)
331 : : {
332 : 31 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (transfer);
333 : 31 : ValentTransferState ret = VALENT_TRANSFER_STATE_PENDING;
334 : :
335 [ - + ]: 31 : g_return_val_if_fail (VALENT_IS_TRANSFER (transfer), FALSE);
336 : :
337 : 31 : valent_object_lock (VALENT_OBJECT (transfer));
338 : 31 : ret = priv->state;
339 : 31 : valent_object_unlock (VALENT_OBJECT (transfer));
340 : :
341 : 31 : return ret;
342 : : }
343 : :
344 : : static void
345 : 34 : valent_transfer_execute_cb (GObject *object,
346 : : GAsyncResult *result,
347 : : gpointer user_data)
348 : : {
349 : 34 : ValentTransfer *self = VALENT_TRANSFER (object);
350 : 34 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (self);
351 : 68 : g_autoptr (GTask) task = G_TASK (user_data);
352 : :
353 : 34 : VALENT_ENTRY;
354 : :
355 [ - + ]: 34 : g_assert (VALENT_IS_TRANSFER (self));
356 [ + - ]: 34 : g_assert (g_task_is_valid (result, self));
357 [ + - + - : 34 : g_assert (G_IS_TASK (task));
- + - - ]
358 : :
359 : 34 : valent_transfer_set_progress (self, 1.0);
360 : :
361 : 34 : valent_object_lock (VALENT_OBJECT (self));
362 [ + - ]: 34 : if (g_task_propagate_boolean (G_TASK (result), &priv->error))
363 : : {
364 : 34 : priv->state = VALENT_TRANSFER_STATE_COMPLETE;
365 : 34 : valent_object_unlock (VALENT_OBJECT (self));
366 : :
367 : 34 : g_task_return_boolean (task, TRUE);
368 : : }
369 : : else
370 : : {
371 : 0 : priv->state = VALENT_TRANSFER_STATE_FAILED;
372 : 0 : valent_object_unlock (VALENT_OBJECT (self));
373 : :
374 : 0 : g_task_return_error (task, g_error_copy (priv->error));
375 : : }
376 : :
377 : 34 : g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATE]);
378 : :
379 : 34 : VALENT_EXIT;
380 : : }
381 : :
382 : : /**
383 : : * valent_transfer_execute: (virtual execute)
384 : : * @transfer: a `ValentTransfer`
385 : : * @cancellable: (nullable): a `GCancellable`
386 : : * @callback: (scope async): a `GAsyncReadyCallback`
387 : : * @user_data: user supplied data
388 : : *
389 : : * Start the transfer operation.
390 : : *
391 : : * Get the result with [method@Valent.Transfer.execute_finish].
392 : : *
393 : : * If the transfer operation has already started, this call will fail and
394 : : * [method@Valent.Transfer.execute_finish] will return %G_IO_ERROR_PENDING.
395 : : *
396 : : * Since: 1.0
397 : : */
398 : : void
399 : 35 : valent_transfer_execute (ValentTransfer *transfer,
400 : : GCancellable *cancellable,
401 : : GAsyncReadyCallback callback,
402 : : gpointer user_data)
403 : : {
404 : 35 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (transfer);
405 : 70 : g_autoptr (GTask) task = NULL;
406 : 70 : g_autoptr (GCancellable) destroy = NULL;
407 : :
408 : 35 : VALENT_ENTRY;
409 : :
410 [ - + ]: 35 : g_return_if_fail (VALENT_IS_TRANSFER (transfer));
411 [ + + + - : 35 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
412 : :
413 : 35 : valent_object_lock (VALENT_OBJECT (transfer));
414 [ - + ]: 35 : if (priv->state != VALENT_TRANSFER_STATE_PENDING)
415 : : {
416 : 0 : g_task_report_new_error (transfer, callback, user_data,
417 : : valent_transfer_execute,
418 : : G_IO_ERROR,
419 : : G_IO_ERROR_PENDING,
420 : : "%s is already in progress",
421 : 0 : G_OBJECT_TYPE_NAME (transfer));
422 : 0 : valent_object_unlock (VALENT_OBJECT (transfer));
423 : 0 : VALENT_EXIT;
424 : : }
425 : :
426 [ - + ]: 35 : g_clear_object (&priv->cancellable);
427 : 35 : priv->cancellable = g_cancellable_new ();
428 [ + + ]: 35 : if (cancellable != NULL)
429 : : {
430 : 20 : g_signal_connect_object (cancellable,
431 : : "cancelled",
432 : : G_CALLBACK (g_cancellable_cancel),
433 : : priv->cancellable,
434 : : G_CONNECT_SWAPPED);
435 : : }
436 : :
437 : 35 : task = g_task_new (transfer, destroy, callback, user_data);
438 [ + - ]: 35 : g_task_set_source_tag (task, valent_transfer_execute);
439 : :
440 : 35 : VALENT_TRANSFER_GET_CLASS (transfer)->execute (transfer,
441 : : priv->cancellable,
442 : : valent_transfer_execute_cb,
443 : : g_steal_pointer (&task));
444 : :
445 : 35 : priv->state = VALENT_TRANSFER_STATE_ACTIVE;
446 : 35 : valent_object_unlock (VALENT_OBJECT (transfer));
447 : :
448 : 35 : g_object_notify_by_pspec (G_OBJECT (transfer), properties[PROP_STATE]);
449 : :
450 : 35 : VALENT_EXIT;
451 : : }
452 : :
453 : : /**
454 : : * valent_transfer_execute_finish: (virtual execute_finish)
455 : : * @transfer: a `ValentTransfer`
456 : : * @result: a `GAsyncResult`
457 : : * @error: (nullable): a `GError`
458 : : *
459 : : * Finish an operation started by [method@Valent.Transfer.execute].
460 : : *
461 : : * Returns: %TRUE if successful, or %FALSE with @error set
462 : : *
463 : : * Since: 1.0
464 : : */
465 : : gboolean
466 : 34 : valent_transfer_execute_finish (ValentTransfer *transfer,
467 : : GAsyncResult *result,
468 : : GError **error)
469 : : {
470 : 34 : gboolean ret;
471 : :
472 : 34 : VALENT_ENTRY;
473 : :
474 [ - + ]: 34 : g_return_val_if_fail (VALENT_IS_TRANSFER (transfer), FALSE);
475 [ + - ]: 34 : g_return_val_if_fail (g_task_is_valid (result, transfer), FALSE);
476 [ + - + - ]: 34 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
477 : :
478 : 34 : ret = VALENT_TRANSFER_GET_CLASS (transfer)->execute_finish (transfer,
479 : : result,
480 : : error);
481 : :
482 : 34 : VALENT_RETURN (ret);
483 : : }
484 : :
485 : : /**
486 : : * valent_transfer_cancel:
487 : : * @transfer: a `ValentTransfer`
488 : : *
489 : : * Cancel the transfer operation.
490 : : *
491 : : * If this is called before [method@Valent.Transfer.execute] the transfer will
492 : : * fail unconditionally.
493 : : *
494 : : * Since: 1.0
495 : : */
496 : : void
497 : 0 : valent_transfer_cancel (ValentTransfer *transfer)
498 : : {
499 : 0 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (transfer);
500 : :
501 : 0 : VALENT_ENTRY;
502 : :
503 [ # # ]: 0 : g_return_if_fail (VALENT_IS_TRANSFER (transfer));
504 : :
505 : 0 : g_cancellable_cancel (priv->cancellable);
506 : :
507 : 0 : VALENT_EXIT;
508 : : }
509 : :
510 : : /**
511 : : * valent_transfer_check_status:
512 : : * @transfer: a `ValentTransfer`
513 : : * @error: (nullable): a `GError`
514 : : *
515 : : * Check the transfer status.
516 : : *
517 : : * Returns %TRUE if the transfer operation is in progress or completed
518 : : * successfully. Returns %FALSE with @error set if the transfer failed.
519 : : *
520 : : * Returns: %TRUE, or %FALSE with @error set
521 : : *
522 : : * Since: 1.0
523 : : */
524 : : gboolean
525 : 0 : valent_transfer_check_status (ValentTransfer *transfer,
526 : : GError **error)
527 : : {
528 : 0 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (transfer);
529 : 0 : gboolean ret = TRUE;
530 : :
531 : 0 : VALENT_ENTRY;
532 : :
533 [ # # ]: 0 : g_return_val_if_fail (VALENT_IS_TRANSFER (transfer), FALSE);
534 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
535 : :
536 : 0 : valent_object_lock (VALENT_OBJECT (transfer));
537 [ # # ]: 0 : if (priv->error != NULL)
538 : : {
539 [ # # ]: 0 : if (error != NULL)
540 : 0 : *error = g_error_copy (priv->error);
541 : : ret = FALSE;
542 : : }
543 : 0 : valent_object_unlock (VALENT_OBJECT (transfer));
544 : :
545 : 0 : VALENT_RETURN (ret);
546 : : }
547 : :
|