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 [ + + + - ]: 729 : G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ValentTransfer, valent_transfer, VALENT_TYPE_OBJECT)
36 : :
37 : :
38 : : enum {
39 : : PROP_0,
40 : : PROP_ID,
41 : : PROP_PROGRESS,
42 : : PROP_STATE,
43 : : N_PROPERTIES
44 : : };
45 : :
46 : : static GParamSpec *properties[N_PROPERTIES] = { NULL, };
47 : :
48 : :
49 : : /* LCOV_EXCL_START */
50 : : static void
51 : : valent_transfer_real_execute (ValentTransfer *transfer,
52 : : GCancellable *cancellable,
53 : : GAsyncReadyCallback callback,
54 : : gpointer user_data)
55 : : {
56 : : g_assert (VALENT_IS_TRANSFER (transfer));
57 : : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
58 : :
59 : : g_task_report_new_error (transfer, callback, user_data,
60 : : valent_transfer_real_execute,
61 : : G_IO_ERROR,
62 : : G_IO_ERROR_NOT_SUPPORTED,
63 : : "%s does not implement execute()",
64 : : G_OBJECT_TYPE_NAME (transfer));
65 : : }
66 : :
67 : : static gboolean
68 : : valent_transfer_real_execute_finish (ValentTransfer *transfer,
69 : : GAsyncResult *result,
70 : : GError **error)
71 : : {
72 : : g_assert (VALENT_IS_TRANSFER (transfer));
73 : : g_assert (g_task_is_valid (result, transfer));
74 : : g_assert (error == NULL || *error == NULL);
75 : :
76 : : return g_task_propagate_boolean (G_TASK (result), error);
77 : : }
78 : : /* LCOV_EXCL_STOP */
79 : :
80 : : /*
81 : : * GObject
82 : : */
83 : : static void
84 : 31 : valent_transfer_finalize (GObject *object)
85 : : {
86 : 31 : ValentTransfer *self = VALENT_TRANSFER (object);
87 : 31 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (self);
88 : :
89 : 31 : valent_object_lock (VALENT_OBJECT (self));
90 : 31 : g_clear_error (&priv->error);
91 [ + + ]: 31 : g_clear_pointer (&priv->id, g_free);
92 : 31 : valent_object_unlock (VALENT_OBJECT (self));
93 : :
94 : 31 : G_OBJECT_CLASS (valent_transfer_parent_class)->finalize (object);
95 : 31 : }
96 : :
97 : : static void
98 : 40 : valent_transfer_get_property (GObject *object,
99 : : guint prop_id,
100 : : GValue *value,
101 : : GParamSpec *pspec)
102 : : {
103 : 40 : ValentTransfer *self = VALENT_TRANSFER (object);
104 : :
105 [ + - + - ]: 40 : switch (prop_id)
106 : : {
107 : 20 : case PROP_ID:
108 : 20 : g_value_take_string (value, valent_transfer_dup_id (self));
109 : 20 : break;
110 : :
111 : 0 : case PROP_PROGRESS:
112 : 0 : g_value_set_double (value, valent_transfer_get_progress (self));
113 : 0 : break;
114 : :
115 : 20 : case PROP_STATE:
116 : 20 : g_value_set_enum (value, valent_transfer_get_state (self));
117 : 20 : break;
118 : :
119 : 0 : default:
120 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
121 : : }
122 : 40 : }
123 : :
124 : : static void
125 : 35 : valent_transfer_set_property (GObject *object,
126 : : guint prop_id,
127 : : const GValue *value,
128 : : GParamSpec *pspec)
129 : : {
130 : 35 : ValentTransfer *self = VALENT_TRANSFER (object);
131 : 35 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (self);
132 : :
133 [ + - - ]: 35 : switch (prop_id)
134 : : {
135 : : case PROP_ID:
136 : 35 : valent_object_lock (VALENT_OBJECT (self));
137 : 35 : priv->id = g_value_dup_string (value);
138 : 35 : valent_object_unlock (VALENT_OBJECT (self));
139 : 35 : break;
140 : :
141 : 0 : case PROP_PROGRESS:
142 : 0 : valent_transfer_set_progress (self, g_value_get_double (value));
143 : 0 : break;
144 : :
145 : 0 : 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, N_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 : 35 : VALENT_EXIT;
425 : : }
426 : :
427 : 35 : destroy = valent_object_chain_cancellable (VALENT_OBJECT (transfer),
428 : : cancellable);
429 : :
430 : 35 : task = g_task_new (transfer, destroy, callback, user_data);
431 [ + - ]: 35 : g_task_set_source_tag (task, valent_transfer_execute);
432 : :
433 : 35 : VALENT_TRANSFER_GET_CLASS (transfer)->execute (transfer,
434 : : destroy,
435 : : valent_transfer_execute_cb,
436 : : g_steal_pointer (&task));
437 : :
438 : 35 : priv->state = VALENT_TRANSFER_STATE_ACTIVE;
439 : 35 : valent_object_unlock (VALENT_OBJECT (transfer));
440 : :
441 : 35 : valent_object_notify_by_pspec (VALENT_OBJECT (transfer), properties [PROP_STATE]);
442 : :
443 [ + - ]: 35 : VALENT_EXIT;
444 : : }
445 : :
446 : : /**
447 : : * valent_transfer_execute_finish: (virtual execute_finish)
448 : : * @transfer: a `ValentTransfer`
449 : : * @result: a `GAsyncResult`
450 : : * @error: (nullable): a `GError`
451 : : *
452 : : * Finish an operation started by [method@Valent.Transfer.execute].
453 : : *
454 : : * Returns: %TRUE if successful, or %FALSE with @error set
455 : : *
456 : : * Since: 1.0
457 : : */
458 : : gboolean
459 : 34 : valent_transfer_execute_finish (ValentTransfer *transfer,
460 : : GAsyncResult *result,
461 : : GError **error)
462 : : {
463 : 34 : gboolean ret;
464 : :
465 : 34 : VALENT_ENTRY;
466 : :
467 [ + - ]: 34 : g_return_val_if_fail (VALENT_IS_TRANSFER (transfer), FALSE);
468 [ - + ]: 34 : g_return_val_if_fail (g_task_is_valid (result, transfer), FALSE);
469 [ + - - + ]: 34 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
470 : :
471 : 34 : ret = VALENT_TRANSFER_GET_CLASS (transfer)->execute_finish (transfer,
472 : : result,
473 : : error);
474 : :
475 : 34 : VALENT_RETURN (ret);
476 : : }
477 : :
478 : : /**
479 : : * valent_transfer_cancel:
480 : : * @transfer: a `ValentTransfer`
481 : : *
482 : : * Cancel the transfer operation.
483 : : *
484 : : * If this is called before [method@Valent.Transfer.execute] the transfer will
485 : : * fail unconditionally.
486 : : *
487 : : * Since: 1.0
488 : : */
489 : : void
490 : 0 : valent_transfer_cancel (ValentTransfer *transfer)
491 : : {
492 : 0 : g_autoptr (GCancellable) cancellable = NULL;
493 : :
494 : 0 : VALENT_ENTRY;
495 : :
496 [ # # ]: 0 : g_return_if_fail (VALENT_IS_TRANSFER (transfer));
497 : :
498 : 0 : cancellable = valent_object_ref_cancellable (VALENT_OBJECT (transfer));
499 : 0 : g_cancellable_cancel (cancellable);
500 : :
501 [ # # ]: 0 : VALENT_EXIT;
502 : : }
503 : :
504 : : /**
505 : : * valent_transfer_check_status:
506 : : * @transfer: a `ValentTransfer`
507 : : * @error: (nullable): a `GError`
508 : : *
509 : : * Check the transfer status.
510 : : *
511 : : * Returns %TRUE if the transfer operation is in progress or completed
512 : : * successfully. Returns %FALSE with @error set if the transfer failed.
513 : : *
514 : : * Returns: %TRUE, or %FALSE with @error set
515 : : *
516 : : * Since: 1.0
517 : : */
518 : : gboolean
519 : 0 : valent_transfer_check_status (ValentTransfer *transfer,
520 : : GError **error)
521 : : {
522 : 0 : ValentTransferPrivate *priv = valent_transfer_get_instance_private (transfer);
523 : 0 : gboolean ret = TRUE;
524 : :
525 : 0 : VALENT_ENTRY;
526 : :
527 [ # # ]: 0 : g_return_val_if_fail (VALENT_IS_TRANSFER (transfer), FALSE);
528 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
529 : :
530 : 0 : valent_object_lock (VALENT_OBJECT (transfer));
531 [ # # ]: 0 : if (priv->error != NULL)
532 : : {
533 [ # # ]: 0 : if (error != NULL)
534 : 0 : *error = g_error_copy (priv->error);
535 : : ret = FALSE;
536 : : }
537 : 0 : valent_object_unlock (VALENT_OBJECT (transfer));
538 : :
539 : 0 : VALENT_RETURN (ret);
540 : : }
541 : :
|