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