Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright © 2018, 2019 Endless Mobile, Inc.
4 : : *
5 : : * This library is free software; you can redistribute it and/or
6 : : * modify it under the terms of the GNU Lesser General Public
7 : : * License as published by the Free Software Foundation; either
8 : : * version 2.1 of the License, or (at your option) any later version.
9 : : *
10 : : * This library is distributed in the hope that it will be useful,
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : : * Lesser General Public License for more details.
14 : : *
15 : : * You should have received a copy of the GNU Lesser General Public
16 : : * License along with this library; if not, write to the Free Software
17 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 : : *
19 : : * Authors:
20 : : * - Philip Withnall <withnall@endlessm.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include <glib/gi18n-lib.h>
26 : : #include <glib.h>
27 : : #include <glib-object.h>
28 : : #include <gio/gio.h>
29 : : #include <libmogwai-schedule/schedule-entry-interface.h>
30 : : #include <libmogwai-schedule-client/schedule-entry.h>
31 : : #include <string.h>
32 : :
33 : :
34 : : /* These errors do not need to be registered with
35 : : * g_dbus_error_register_error_domain() as they never go over the bus. */
36 [ + + ]: 4 : G_DEFINE_QUARK (MwscScheduleEntryError, mwsc_schedule_entry_error)
37 : :
38 : : static void mwsc_schedule_entry_initable_init (GInitableIface *iface);
39 : : static void mwsc_schedule_entry_async_initable_init (GAsyncInitableIface *iface);
40 : : static void mwsc_schedule_entry_constructed (GObject *object);
41 : : static void mwsc_schedule_entry_dispose (GObject *object);
42 : :
43 : : static void mwsc_schedule_entry_get_property (GObject *object,
44 : : guint property_id,
45 : : GValue *value,
46 : : GParamSpec *pspec);
47 : : static void mwsc_schedule_entry_set_property (GObject *object,
48 : : guint property_id,
49 : : const GValue *value,
50 : : GParamSpec *pspec);
51 : :
52 : : static gboolean mwsc_schedule_entry_init_failable (GInitable *initable,
53 : : GCancellable *cancellable,
54 : : GError **error);
55 : : static void mwsc_schedule_entry_init_async (GAsyncInitable *initable,
56 : : int io_priority,
57 : : GCancellable *cancellable,
58 : : GAsyncReadyCallback callback,
59 : : gpointer user_data);
60 : : static gboolean mwsc_schedule_entry_init_finish (GAsyncInitable *initable,
61 : : GAsyncResult *result,
62 : : GError **error);
63 : :
64 : : static void properties_changed_cb (GDBusProxy *proxy,
65 : : GVariant *changed_properties,
66 : : GStrv invalidated_properties,
67 : : gpointer user_data);
68 : : static void signal_cb (GDBusProxy *proxy,
69 : : const gchar *sender_name,
70 : : const gchar *signal_name,
71 : : GVariant *parameters,
72 : : gpointer user_data);
73 : : static void proxy_notify_name_owner_cb (GObject *obj,
74 : : GParamSpec *pspec,
75 : : gpointer user_data);
76 : :
77 : : /**
78 : : * MwscScheduleEntry:
79 : : *
80 : : * An entry in the scheduler representing a single download (either active or
81 : : * inactive). This stores the scheduling parameters for the download as provided
82 : : * by this app, and exposes the scheduler’s decision about whether to pause or
83 : : * enable the download at the moment (#MwscScheduleEntry:download-now); whether
84 : : * it is ‘active’.
85 : : *
86 : : * To create an #MwscScheduleEntry, call mwsc_scheduler_schedule_async() with
87 : : * some initial parameters for the download. Once created, the entry may be
88 : : * made active immediately by the scheduler.
89 : : *
90 : : * The parameters for the download can be updated throughout its lifetime (for
91 : : * example, if an initial estimation of the size of the download is updated).
92 : : * Set them on the #MwscScheduleEntry using the mwsc_schedule_entry_*() methods,
93 : : * then send the updates to the service using
94 : : * mwsc_schedule_send_properties_async().
95 : : *
96 : : * Any updates to the properties from the service will be signalled using
97 : : * #GObject::notify and will overwrite any non-uploaded local changes.
98 : : *
99 : : * The schedule entry is active if #MwscScheduleEntry:download-now is %TRUE. It
100 : : * may be %TRUE or %FALSE immediately after the schedule entry is created, and
101 : : * may change value several times over the lifetime of the entry. If it changes
102 : : * value from %TRUE to %FALSE, the app must pause the ongoing download until
103 : : * #MwscScheduleEntry:download-now becomes %TRUE again.
104 : : *
105 : : * Once the download is finished, or if it is cancelled or becomes irrelevant
106 : : * or obsolete, the schedule entry must be removed using
107 : : * mwsc_schedule_entry_remove_async(). This will not automatically happen if
108 : : * the #MwscScheduleEntry instance is finalised.
109 : : *
110 : : * The ID for a #MwscSchedulerEntry is globally unique and never re-used. It’s
111 : : * generated when the #MwscScheduleEntry is created.
112 : : *
113 : : * If the service goes away, or if the schedule entry is removed
114 : : * (mwsc_schedule_entry_remove_async()), #MwscScheduleEntry::invalidated will be
115 : : * emitted, and all future method calls on the object will return a
116 : : * %MWSC_SCHEDULE_ENTRY_ERROR_INVALIDATED error.
117 : : *
118 : : * Since: 0.1.0
119 : : */
120 : : struct _MwscScheduleEntry
121 : : {
122 : : GObject parent;
123 : :
124 : : GDBusProxy *proxy; /* (owned); NULL during initialisation */
125 : : GDBusConnection *connection; /* (owned) */
126 : : gchar *name; /* (owned); NULL if not running on a message bus */
127 : : gchar *object_path; /* (owned) */
128 : :
129 : : /* Exactly one of these will be set after initialisation completes (or
130 : : * fails). */
131 : : GError *init_error; /* nullable; owned */
132 : : gboolean init_success;
133 : : gboolean initialising;
134 : :
135 : : gboolean resumable;
136 : : guint32 priority;
137 : : };
138 : :
139 : : typedef enum
140 : : {
141 : : PROP_CONNECTION = 1,
142 : : PROP_NAME,
143 : : PROP_OBJECT_PATH,
144 : : PROP_PROXY,
145 : : PROP_ID,
146 : : PROP_DOWNLOAD_NOW,
147 : : PROP_RESUMABLE,
148 : : PROP_PRIORITY,
149 : : } MwscScheduleEntryProperty;
150 : :
151 [ + + + - : 19 : G_DEFINE_TYPE_WITH_CODE (MwscScheduleEntry, mwsc_schedule_entry, G_TYPE_OBJECT,
+ + ]
152 : : G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
153 : : mwsc_schedule_entry_initable_init)
154 : : G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
155 : : mwsc_schedule_entry_async_initable_init))
156 : :
157 : : static void
158 : 1 : mwsc_schedule_entry_class_init (MwscScheduleEntryClass *klass)
159 : : {
160 : 1 : GObjectClass *object_class = (GObjectClass *) klass;
161 : 1 : GParamSpec *props[PROP_PRIORITY + 1] = { NULL, };
162 : :
163 : 1 : object_class->constructed = mwsc_schedule_entry_constructed;
164 : 1 : object_class->dispose = mwsc_schedule_entry_dispose;
165 : 1 : object_class->get_property = mwsc_schedule_entry_get_property;
166 : 1 : object_class->set_property = mwsc_schedule_entry_set_property;
167 : :
168 : : /**
169 : : * MwscScheduleEntry:connection:
170 : : *
171 : : * D-Bus connection to proxy the object from.
172 : : *
173 : : * Since: 0.1.0
174 : : */
175 : 1 : props[PROP_CONNECTION] =
176 : 1 : g_param_spec_object ("connection", "Connection",
177 : : "D-Bus connection to proxy the object from.",
178 : : G_TYPE_DBUS_CONNECTION,
179 : : G_PARAM_READWRITE |
180 : : G_PARAM_CONSTRUCT_ONLY |
181 : : G_PARAM_STATIC_STRINGS);
182 : :
183 : : /**
184 : : * MwscScheduleEntry:name:
185 : : *
186 : : * Well-known or unique name of the peer to proxy the object from. This must
187 : : * be %NULL if and only if the #MwscScheduleEntry:connection is not a message
188 : : * bus connection.
189 : : *
190 : : * Since: 0.1.0
191 : : */
192 : 1 : props[PROP_NAME] =
193 : 1 : g_param_spec_string ("name", "Name",
194 : : "Well-known or unique name of the peer to proxy the "
195 : : "object from.",
196 : : NULL,
197 : : G_PARAM_READWRITE |
198 : : G_PARAM_CONSTRUCT_ONLY |
199 : : G_PARAM_STATIC_STRINGS);
200 : :
201 : : /**
202 : : * MwscScheduleEntry:object-path:
203 : : *
204 : : * Object path to proxy. The object must implement
205 : : * `com.endlessm.DownloadManager1.ScheduleEntry`.
206 : : *
207 : : * Since: 0.1.0
208 : : */
209 : 1 : props[PROP_OBJECT_PATH] =
210 : 1 : g_param_spec_string ("object-path", "Object Path",
211 : : "Object path to proxy.",
212 : : "/",
213 : : G_PARAM_READWRITE |
214 : : G_PARAM_CONSTRUCT_ONLY |
215 : : G_PARAM_STATIC_STRINGS);
216 : :
217 : : /**
218 : : * MwscScheduleEntry:proxy:
219 : : *
220 : : * D-Bus proxy to use when interacting with the object. If this is %NULL at
221 : : * construction time, one will be created. If provided, it **must** have
222 : : * cached copies of its properties already.
223 : : *
224 : : * Since: 0.1.0
225 : : */
226 : 1 : props[PROP_PROXY] =
227 : 1 : g_param_spec_object ("proxy", "Proxy",
228 : : "D-Bus proxy to use when interacting with the object.",
229 : : G_TYPE_DBUS_PROXY,
230 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
231 : : G_PARAM_STATIC_STRINGS);
232 : :
233 : : /**
234 : : * MwscScheduleEntry:id:
235 : : *
236 : : * The unique, persistent ID for this schedule entry. It’s generated by the
237 : : * scheduler when the entry is first created, and never changes. It has no
238 : : * defined format, other than being a non-empty UTF-8 string.
239 : : *
240 : : * Since: 0.1.0
241 : : */
242 : 1 : props[PROP_ID] =
243 : 1 : g_param_spec_string ("id", "ID",
244 : : "Unique, persistent ID for this schedule entry.",
245 : : NULL,
246 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
247 : :
248 : : /**
249 : : * MwscScheduleEntry:download-now:
250 : : *
251 : : * Whether the scheduler is currently permitting this download to use the
252 : : * network. If %TRUE, the download should be performed. If %FALSE, it should
253 : : * be paused. The state of this property may change several times during the
254 : : * lifetime of a download.
255 : : *
256 : : * Since: 0.1.0
257 : : */
258 : 1 : props[PROP_DOWNLOAD_NOW] =
259 : 1 : g_param_spec_boolean ("download-now", "Download Now",
260 : : "Whether the scheduler is currently permitting "
261 : : "this download to use the network.",
262 : : FALSE,
263 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
264 : :
265 : : /**
266 : : * MwscScheduleEntry:resumable:
267 : : *
268 : : * Whether pausing and resuming this download is supported by this application
269 : : * after it’s started. Some applications and servers can only restart
270 : : * downloads from the beginning after pausing them.
271 : : *
272 : : * Since: 0.1.0
273 : : */
274 : 1 : props[PROP_RESUMABLE] =
275 : 1 : g_param_spec_boolean ("resumable", "Resumable",
276 : : "Whether pausing and resuming this download is supported.",
277 : : FALSE,
278 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
279 : :
280 : : /**
281 : : * MwscScheduleEntry:priority:
282 : : *
283 : : * The priority of this download relative to others belonging to this
284 : : * application. Higher numbers mean the download is more important.
285 : : *
286 : : * Since: 0.1.0
287 : : */
288 : 1 : props[PROP_PRIORITY] =
289 : 1 : g_param_spec_uint ("priority", "Priority",
290 : : "The priority of this download relative to others.",
291 : : 0, G_MAXUINT32, 0,
292 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
293 : :
294 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
295 : :
296 : : /**
297 : : * MwscScheduleEntry::invalidated:
298 : : * @self: a #MwscScheduleEntry
299 : : * @error: error which caused the schedule entry to be invalidated: currently
300 : : * %G_DBUS_ERROR_DISCONNECTED or %MWSC_SCHEDULE_ENTRY_ERROR_INVALIDATED
301 : : *
302 : : * Emitted when the backing object underlying this #MwscScheduleEntry
303 : : * disappears, or it is otherwise disconnected (due to, for example,
304 : : * providing invalid data). The most common reasons for this signal to be
305 : : * emitted are if the underlying D-Bus object disappears, or if the schedule
306 : : * entry is removed by calling mwsc_schedule_entry_remove_async().
307 : : *
308 : : * After this signal is emitted, all method calls to #MwscScheduleEntry
309 : : * methods will return %MWSC_SCHEDULE_ENTRY_ERROR_INVALIDATED.
310 : : *
311 : : * Since: 0.1.0
312 : : */
313 : 1 : g_signal_new ("invalidated", G_TYPE_FROM_CLASS (klass),
314 : : G_SIGNAL_RUN_LAST,
315 : : 0, NULL, NULL, NULL,
316 : : G_TYPE_NONE, 1,
317 : : G_TYPE_ERROR);
318 : 1 : }
319 : :
320 : : static void
321 : 1 : mwsc_schedule_entry_initable_init (GInitableIface *iface)
322 : : {
323 : 1 : iface->init = mwsc_schedule_entry_init_failable;
324 : 1 : }
325 : :
326 : : static void
327 : 1 : mwsc_schedule_entry_async_initable_init (GAsyncInitableIface *iface)
328 : : {
329 : 1 : iface->init_async = mwsc_schedule_entry_init_async;
330 : 1 : iface->init_finish = mwsc_schedule_entry_init_finish;
331 : 1 : }
332 : :
333 : : static void
334 : 2 : mwsc_schedule_entry_init (MwscScheduleEntry *self)
335 : : {
336 : : /* Nothing to do here. */
337 : 2 : }
338 : :
339 : : static void
340 : 2 : mwsc_schedule_entry_constructed (GObject *object)
341 : : {
342 : 2 : MwscScheduleEntry *self = MWSC_SCHEDULE_ENTRY (object);
343 : :
344 : : /* Chain up to the parent class */
345 : 2 : G_OBJECT_CLASS (mwsc_schedule_entry_parent_class)->constructed (object);
346 : :
347 : : /* Ensure that :name is %NULL iff :connection is not a message bus
348 : : * connection. */
349 : 2 : gboolean is_message_bus = (g_dbus_connection_get_unique_name (self->connection) != NULL);
350 [ - + ]: 2 : g_assert (is_message_bus == (self->name != NULL));
351 : :
352 : : /* Check all our construct-only properties are set. */
353 [ + - + - : 2 : g_assert (self->proxy != NULL ||
+ - + - ]
354 : : (self->connection != NULL &&
355 : : self->name != NULL &&
356 : : self->object_path != NULL));
357 : 2 : }
358 : :
359 : : static void
360 : 2 : mwsc_schedule_entry_dispose (GObject *object)
361 : : {
362 : 2 : MwscScheduleEntry *self = MWSC_SCHEDULE_ENTRY (object);
363 : :
364 [ + - ]: 2 : if (self->proxy != NULL)
365 : : {
366 : : /* Disconnect from signals. */
367 : 2 : g_signal_handlers_disconnect_by_func (self->proxy, properties_changed_cb,
368 : : self);
369 : 2 : g_signal_handlers_disconnect_by_func (self->proxy, signal_cb, self);
370 : 2 : g_signal_handlers_disconnect_by_func (self->proxy,
371 : : proxy_notify_name_owner_cb, self);
372 : : }
373 : :
374 [ + - ]: 2 : g_clear_object (&self->proxy);
375 [ + - ]: 2 : g_clear_object (&self->connection);
376 [ + - ]: 2 : g_clear_pointer (&self->name, g_free);
377 [ + - ]: 2 : g_clear_pointer (&self->object_path, g_free);
378 : 2 : g_clear_error (&self->init_error);
379 : :
380 : : /* Chain up to the parent class */
381 : 2 : G_OBJECT_CLASS (mwsc_schedule_entry_parent_class)->dispose (object);
382 : 2 : }
383 : :
384 : : static void
385 : 0 : mwsc_schedule_entry_get_property (GObject *object,
386 : : guint property_id,
387 : : GValue *value,
388 : : GParamSpec *pspec)
389 : : {
390 : 0 : MwscScheduleEntry *self = MWSC_SCHEDULE_ENTRY (object);
391 : :
392 [ # # # # : 0 : switch ((MwscScheduleEntryProperty) property_id)
# # # #
# ]
393 : : {
394 : 0 : case PROP_CONNECTION:
395 : 0 : g_value_set_object (value, self->connection);
396 : 0 : break;
397 : 0 : case PROP_NAME:
398 : 0 : g_value_set_string (value, self->name);
399 : 0 : break;
400 : 0 : case PROP_OBJECT_PATH:
401 : 0 : g_value_set_string (value, self->object_path);
402 : 0 : break;
403 : 0 : case PROP_PROXY:
404 : 0 : g_value_set_object (value, self->proxy);
405 : 0 : break;
406 : 0 : case PROP_ID:
407 : 0 : g_value_set_string (value, mwsc_schedule_entry_get_id (self));
408 : 0 : break;
409 : 0 : case PROP_DOWNLOAD_NOW:
410 : : {
411 : 0 : GVariant *variant = g_dbus_proxy_get_cached_property (self->proxy, "DownloadNow");
412 : 0 : g_value_set_boolean (value, g_variant_get_boolean (variant));
413 : 0 : break;
414 : : }
415 : 0 : case PROP_RESUMABLE:
416 : 0 : g_value_set_boolean (value, self->resumable);
417 : 0 : break;
418 : 0 : case PROP_PRIORITY:
419 : 0 : g_value_set_uint (value, self->priority);
420 : 0 : break;
421 : 0 : default:
422 : 0 : g_assert_not_reached ();
423 : : }
424 : 0 : }
425 : :
426 : : static void
427 : 8 : mwsc_schedule_entry_set_property (GObject *object,
428 : : guint property_id,
429 : : const GValue *value,
430 : : GParamSpec *pspec)
431 : : {
432 : 8 : MwscScheduleEntry *self = MWSC_SCHEDULE_ENTRY (object);
433 : :
434 [ + + + + : 8 : switch ((MwscScheduleEntryProperty) property_id)
- - - -
- ]
435 : : {
436 : 2 : case PROP_CONNECTION:
437 : : /* Construct only. */
438 [ - + ]: 2 : g_assert (self->connection == NULL);
439 : 2 : self->connection = g_value_dup_object (value);
440 : 2 : break;
441 : 2 : case PROP_NAME:
442 : : /* Construct only. */
443 [ - + ]: 2 : g_assert (self->name == NULL);
444 [ + - - + ]: 2 : g_assert (g_value_get_string (value) == NULL ||
445 : : g_dbus_is_name (g_value_get_string (value)));
446 : 2 : self->name = g_value_dup_string (value);
447 : 2 : break;
448 : 2 : case PROP_OBJECT_PATH:
449 : : /* Construct only. */
450 [ - + ]: 2 : g_assert (self->object_path == NULL);
451 [ - + ]: 2 : g_assert (g_variant_is_object_path (g_value_get_string (value)));
452 : 2 : self->object_path = g_value_dup_string (value);
453 : 2 : break;
454 : 2 : case PROP_PROXY:
455 : : /* Construct only. */
456 [ - + ]: 2 : g_assert (self->proxy == NULL);
457 : 2 : self->proxy = g_value_dup_object (value);
458 : 2 : break;
459 : 0 : case PROP_ID:
460 : : /* Read only. */
461 : 0 : g_assert_not_reached ();
462 : : break;
463 : 0 : case PROP_DOWNLOAD_NOW:
464 : : /* Read only. */
465 : 0 : g_assert_not_reached ();
466 : : break;
467 : 0 : case PROP_RESUMABLE:
468 : 0 : mwsc_schedule_entry_set_resumable (self, g_value_get_boolean (value));
469 : 0 : break;
470 : 0 : case PROP_PRIORITY:
471 : 0 : mwsc_schedule_entry_set_priority (self, g_value_get_uint (value));
472 : 0 : break;
473 : 0 : default:
474 : 0 : g_assert_not_reached ();
475 : : }
476 : 8 : }
477 : :
478 : : /* Report an error with the proxied object's interactions; for example,
479 : : * providing an incorrectly-typed attribute or an invalid update signal. */
480 : : static void
481 : 0 : schedule_entry_invalidate (MwscScheduleEntry *self,
482 : : const GError *error)
483 : : {
484 [ # # ]: 0 : g_assert (self->proxy != NULL);
485 : :
486 : : /* Disconnect from signals. */
487 : 0 : g_signal_handlers_disconnect_by_func (self->proxy, properties_changed_cb,
488 : : self);
489 : 0 : g_signal_handlers_disconnect_by_func (self->proxy, signal_cb, self);
490 : 0 : g_signal_handlers_disconnect_by_func (self->proxy,
491 : : proxy_notify_name_owner_cb, self);
492 : :
493 : : /* Clear the proxy, which marks this #MwscScheduleEntry as invalidated. */
494 : 0 : g_debug ("Marking schedule entry ‘%s’ as invalidated due to error: %s",
495 : : mwsc_schedule_entry_get_id (self), error->message);
496 : :
497 [ # # ]: 0 : g_clear_object (&self->proxy);
498 : 0 : g_object_notify (G_OBJECT (self), "proxy");
499 : :
500 : 0 : g_signal_emit_by_name (self, "invalidated", error);
501 : 0 : }
502 : :
503 : : static gboolean
504 : 0 : check_invalidated_with_task (MwscScheduleEntry *self,
505 : : GTask *task)
506 : : {
507 : : /* Invalidated? */
508 [ # # ]: 0 : if (self->proxy == NULL)
509 : : {
510 : 0 : g_task_return_new_error (task, MWSC_SCHEDULE_ENTRY_ERROR,
511 : : MWSC_SCHEDULE_ENTRY_ERROR_INVALIDATED,
512 : 0 : _("Schedule entry ‘%s’ has been invalidated."),
513 : : mwsc_schedule_entry_get_id (self));
514 : 0 : return FALSE;
515 : : }
516 : :
517 : 0 : return TRUE;
518 : : }
519 : :
520 : : static gboolean
521 : 0 : check_invalidated_with_error (MwscScheduleEntry *self,
522 : : GError **error)
523 : : {
524 : : /* Invalidated? */
525 [ # # ]: 0 : if (self->proxy == NULL)
526 : : {
527 : 0 : g_set_error (error, MWSC_SCHEDULE_ENTRY_ERROR,
528 : : MWSC_SCHEDULE_ENTRY_ERROR_INVALIDATED,
529 : : _("Schedule entry ‘%s’ has been invalidated."),
530 : : mwsc_schedule_entry_get_id (self));
531 : 0 : return FALSE;
532 : : }
533 : :
534 : 0 : return TRUE;
535 : : }
536 : :
537 : : static void
538 : 0 : properties_changed_cb (GDBusProxy *proxy,
539 : : GVariant *changed_properties,
540 : : GStrv invalidated_properties,
541 : : gpointer user_data)
542 : : {
543 : 0 : MwscScheduleEntry *self = MWSC_SCHEDULE_ENTRY (user_data);
544 : :
545 : 0 : g_auto(GVariantDict) dict = G_VARIANT_DICT_INIT (changed_properties);
546 : :
547 : 0 : g_object_freeze_notify (G_OBJECT (self));
548 : :
549 : : /* Ignore unrecognised properties. */
550 : : gboolean download_now;
551 [ # # ]: 0 : if (g_variant_dict_lookup (&dict, "DownloadNow", "b", &download_now))
552 : 0 : g_object_notify (G_OBJECT (self), "download-now");
553 : :
554 : : gboolean resumable;
555 [ # # ]: 0 : if (g_variant_dict_lookup (&dict, "Resumable", "b", &resumable))
556 : 0 : mwsc_schedule_entry_set_resumable (self, resumable);
557 : :
558 : : guint32 priority;
559 [ # # ]: 0 : if (g_variant_dict_lookup (&dict, "Priority", "u", &priority))
560 : 0 : mwsc_schedule_entry_set_priority (self, priority);
561 : :
562 : 0 : g_object_thaw_notify (G_OBJECT (self));
563 : 0 : }
564 : :
565 : : static void
566 : 0 : signal_cb (GDBusProxy *proxy,
567 : : const gchar *sender_name,
568 : : const gchar *signal_name,
569 : : GVariant *parameters,
570 : : gpointer user_data)
571 : : {
572 : 0 : MwscScheduleEntry *self = MWSC_SCHEDULE_ENTRY (user_data);
573 : :
574 : : /* @sender_name is validated by the #GDBusProxy code so we can trust it. */
575 [ # # ]: 0 : if (g_strcmp0 (signal_name, "Removed") == 0)
576 : : {
577 : : /* Mark the schedule entry as invalidated. */
578 [ # # ]: 0 : g_assert (g_variant_n_children (parameters) == 0);
579 : :
580 : 0 : g_autoptr(GError) error = NULL;
581 : 0 : g_set_error_literal (&error, MWSC_SCHEDULE_ENTRY_ERROR,
582 : : MWSC_SCHEDULE_ENTRY_ERROR_INVALIDATED,
583 : : _("Schedule entry was explicitly removed."));
584 : 0 : schedule_entry_invalidate (self, error);
585 : : }
586 : 0 : }
587 : :
588 : : static void
589 : 0 : proxy_notify_name_owner_cb (GObject *obj,
590 : : GParamSpec *pspec,
591 : : gpointer user_data)
592 : : {
593 : 0 : MwscScheduleEntry *self = MWSC_SCHEDULE_ENTRY (user_data);
594 : :
595 : 0 : g_debug ("Name owner for proxy ‘%s’ has changed.", self->object_path);
596 : :
597 [ # # ]: 0 : if (g_dbus_proxy_get_name_owner (G_DBUS_PROXY (obj)) == NULL)
598 : : {
599 : 0 : g_autoptr(GError) error = NULL;
600 : :
601 : 0 : g_set_error_literal (&error, G_DBUS_ERROR, G_DBUS_ERROR_DISCONNECTED,
602 : : _("Schedule entry owner has disconnected."));
603 : 0 : schedule_entry_invalidate (self, error);
604 : : }
605 : 0 : }
606 : :
607 : : static gboolean
608 : 2 : set_up_proxy (MwscScheduleEntry *self,
609 : : GError **error)
610 : : {
611 [ - + ]: 2 : g_assert (self->proxy != NULL);
612 : :
613 : : /* Ensure the proxy has its interface info specified, so we can rely on GDBus
614 : : * to check return value types, etc. (See #GDBusProxy:g-interface-info.) */
615 [ - + ]: 2 : if (g_dbus_proxy_get_interface_info (self->proxy) == NULL)
616 : 0 : g_dbus_proxy_set_interface_info (self->proxy,
617 : : (GDBusInterfaceInfo *) &schedule_entry_interface);
618 : :
619 : : /* Subscribe to signals. */
620 : 2 : g_signal_connect (self->proxy, "g-properties-changed",
621 : : (GCallback) properties_changed_cb, self);
622 : 2 : g_signal_connect (self->proxy, "g-signal", (GCallback) signal_cb, self);
623 : 2 : g_signal_connect (self->proxy, "notify::g-name-owner",
624 : : (GCallback) proxy_notify_name_owner_cb, self);
625 : :
626 : : /* Validate that the entry actually exists. */
627 : 2 : g_autoptr(GError) local_error = NULL;
628 : 2 : g_autoptr(GVariant) download_now = NULL;
629 : :
630 : 4 : g_autofree gchar *name_owner = g_dbus_proxy_get_name_owner (self->proxy);
631 : :
632 [ + - ]: 2 : if (name_owner == NULL)
633 : : {
634 : 2 : g_set_error_literal (&local_error, MWSC_SCHEDULE_ENTRY_ERROR,
635 : : MWSC_SCHEDULE_ENTRY_ERROR_UNKNOWN_ENTRY,
636 : : _("Schedule entry does not exist on the bus."));
637 : 2 : goto done;
638 : : }
639 : :
640 : : /* Validate that the properties are cached (use DownloadNow as a proxy for the
641 : : * others). */
642 : 0 : download_now = g_dbus_proxy_get_cached_property (self->proxy, "DownloadNow");
643 : :
644 [ # # # # ]: 0 : g_return_val_if_fail (download_now == NULL ||
645 : : g_variant_is_of_type (download_now, G_VARIANT_TYPE_BOOLEAN),
646 : : FALSE);
647 : :
648 [ # # ]: 0 : if (download_now == NULL)
649 : : {
650 : 0 : g_set_error_literal (&local_error, MWSC_SCHEDULE_ENTRY_ERROR,
651 : : MWSC_SCHEDULE_ENTRY_ERROR_UNKNOWN_ENTRY,
652 : : _("Required DownloadNow property is missing. "
653 : : "Might not have permission to access the schedule entry."));
654 : 0 : goto done;
655 : : }
656 : :
657 : 0 : done:
658 [ + - ]: 2 : if (local_error != NULL)
659 : : {
660 : 2 : g_propagate_error (error, g_error_copy (local_error));
661 : 2 : g_propagate_error (&self->init_error, g_steal_pointer (&local_error));
662 : 2 : self->init_success = FALSE;
663 : : }
664 : : else
665 : : {
666 : 0 : self->init_success = TRUE;
667 : : }
668 : :
669 : 2 : return self->init_success;
670 : : }
671 : :
672 : : static void
673 : 1 : proxy_init_cb (GObject *obj,
674 : : GAsyncResult *result,
675 : : gpointer user_data)
676 : : {
677 [ + - ]: 2 : g_autoptr(GTask) task = G_TASK (user_data);
678 : 1 : MwscScheduleEntry *self = g_task_get_source_object (task);
679 [ + - ]: 1 : g_autoptr(GError) local_error = NULL;
680 : :
681 : : /* Get the proxy. */
682 [ - + ]: 1 : g_assert (self->proxy == NULL);
683 : 1 : self->proxy = g_dbus_proxy_new_finish (result, &local_error);
684 : :
685 [ - + ]: 1 : g_assert (self->initialising);
686 : 1 : self->initialising = FALSE;
687 : :
688 [ - + ]: 1 : if (local_error != NULL)
689 : : {
690 : 0 : g_propagate_error (&self->init_error, g_error_copy (local_error));
691 : 0 : self->init_success = FALSE;
692 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
693 : 0 : return;
694 : : }
695 : :
696 [ - + ]: 1 : if (set_up_proxy (self, &local_error))
697 : 0 : g_task_return_boolean (task, TRUE);
698 : : else
699 : 1 : g_task_return_error (task, g_steal_pointer (&local_error));
700 : : }
701 : :
702 : : static gboolean
703 : 1 : mwsc_schedule_entry_init_failable (GInitable *initable,
704 : : GCancellable *cancellable,
705 : : GError **error)
706 : : {
707 : 1 : MwscScheduleEntry *self = MWSC_SCHEDULE_ENTRY (initable);
708 : :
709 : : /* For the moment, this only supports the case where we’ve been constructed
710 : : * with a suitable proxy already. */
711 [ - + ]: 1 : if (self->init_error != NULL)
712 : : {
713 : 0 : g_propagate_error (error, g_error_copy (self->init_error));
714 : 0 : return FALSE;
715 : : }
716 [ - + ]: 1 : else if (self->init_success)
717 : : {
718 : 0 : return TRUE;
719 : : }
720 : : else
721 : : {
722 [ - + ]: 1 : g_assert (self->proxy == NULL);
723 : 2 : self->proxy = g_dbus_proxy_new_sync (self->connection,
724 : : G_DBUS_PROXY_FLAGS_NONE,
725 : : (GDBusInterfaceInfo *) &schedule_entry_interface,
726 : 1 : self->name, self->object_path,
727 : : "com.endlessm.DownloadManager1.ScheduleEntry",
728 : : cancellable, error);
729 [ - + ]: 1 : if (self->proxy == NULL)
730 : : {
731 : 0 : self->init_success = FALSE;
732 : 0 : return FALSE;
733 : : }
734 : :
735 : 1 : return set_up_proxy (self, error);
736 : : }
737 : : }
738 : :
739 : : static void
740 : 1 : mwsc_schedule_entry_init_async (GAsyncInitable *initable,
741 : : int io_priority,
742 : : GCancellable *cancellable,
743 : : GAsyncReadyCallback callback,
744 : : gpointer user_data)
745 : : {
746 : 1 : MwscScheduleEntry *self = MWSC_SCHEDULE_ENTRY (initable);
747 : :
748 : : /* We don’t support parallel initialisation. */
749 [ - + ]: 1 : g_assert (!self->initialising);
750 : :
751 : 2 : g_autoptr(GTask) task = g_task_new (initable, cancellable, callback, user_data);
752 [ + - ]: 1 : g_task_set_source_tag (task, mwsc_schedule_entry_init_async);
753 : :
754 [ - + ]: 1 : if (self->init_error != NULL)
755 : 0 : g_task_return_error (task, g_error_copy (self->init_error));
756 [ - + ]: 1 : else if (self->init_success)
757 : 0 : g_task_return_boolean (task, TRUE);
758 : : else
759 : : {
760 : 1 : self->initialising = TRUE;
761 : 1 : g_dbus_proxy_new (self->connection, G_DBUS_PROXY_FLAGS_NONE,
762 : : (GDBusInterfaceInfo *) &schedule_entry_interface,
763 : 1 : self->name, self->object_path,
764 : : "com.endlessm.DownloadManager1.ScheduleEntry",
765 : : cancellable, proxy_init_cb, g_steal_pointer (&task));
766 : : }
767 : 1 : }
768 : :
769 : : static gboolean
770 : 1 : mwsc_schedule_entry_init_finish (GAsyncInitable *initable,
771 : : GAsyncResult *result,
772 : : GError **error)
773 : : {
774 : 1 : return g_task_propagate_boolean (G_TASK (result), error);
775 : : }
776 : :
777 : : /**
778 : : * mwsc_schedule_entry_new_from_proxy:
779 : : * @proxy: a #GDBusProxy for a `com.endlessm.DownloadManager1.ScheduleEntry`
780 : : * object
781 : : * @error: return location for a #GError, or %NULL
782 : : *
783 : : * Create a #MwscScheduleEntry object to wrap the given existing @proxy. The
784 : : * @proxy must have cached all the `ScheduleEntry` properties already.
785 : : *
786 : : * If any of the properties are missing or invalid, an error is returned.
787 : : *
788 : : * Returns: (transfer full): a new #MwscScheduleEntry wrapping @proxy
789 : : * Since: 0.1.0
790 : : */
791 : : MwscScheduleEntry *
792 : 0 : mwsc_schedule_entry_new_from_proxy (GDBusProxy *proxy,
793 : : GError **error)
794 : : {
795 [ # # # # : 0 : g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
# # # # ]
796 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
797 : :
798 : 0 : return g_initable_new (MWSC_TYPE_SCHEDULE_ENTRY, NULL, error,
799 : : "connection", g_dbus_proxy_get_connection (proxy),
800 : : "name", g_dbus_proxy_get_name (proxy),
801 : : "object-path", g_dbus_proxy_get_object_path (proxy),
802 : : "proxy", proxy,
803 : : NULL);
804 : : }
805 : :
806 : : /**
807 : : * mwsc_schedule_entry_new_full:
808 : : * @connection: D-Bus connection to use
809 : : * @name: (nullable): well-known or unique name of the peer to proxy from, or
810 : : * %NULL if @connection is not a message bus connection
811 : : * @object_path: path of the object to proxy
812 : : * @cancellable: (nullable): a #GCancellable, or %NULL
813 : : * @error: return location for a #GError, or %NULL
814 : : *
815 : : * Synchronous version of mwsc_schedule_entry_new_full_async().
816 : : *
817 : : * Returns: (transfer full): initialised #MwscScheduleEntry, or %NULL on error
818 : : * Since: 0.2.0
819 : : */
820 : : MwscScheduleEntry *
821 : 1 : mwsc_schedule_entry_new_full (GDBusConnection *connection,
822 : : const gchar *name,
823 : : const gchar *object_path,
824 : : GCancellable *cancellable,
825 : : GError **error)
826 : : {
827 [ - + + - : 1 : g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+ - - + ]
828 [ + - - + ]: 1 : g_return_val_if_fail (name == NULL || g_dbus_is_name (name), NULL);
829 [ - + ]: 1 : g_return_val_if_fail ((g_dbus_connection_get_unique_name (connection) == NULL) ==
830 : : (name == NULL), NULL);
831 [ + - + - ]: 1 : g_return_val_if_fail (object_path != NULL &&
832 : : g_variant_is_object_path (object_path), NULL);
833 [ - + - - : 1 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
- - - - -
- ]
834 [ + - - + ]: 1 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
835 : :
836 : 1 : return g_initable_new (MWSC_TYPE_SCHEDULE_ENTRY, cancellable, error,
837 : : "connection", connection,
838 : : "name", name,
839 : : "object-path", object_path,
840 : : NULL);
841 : : }
842 : :
843 : : /**
844 : : * mwsc_schedule_entry_new_full_async:
845 : : * @connection: D-Bus connection to use
846 : : * @name: (nullable): well-known or unique name of the peer to proxy from, or
847 : : * %NULL if @connection is not a message bus connection
848 : : * @object_path: path of the object to proxy
849 : : * @cancellable: (nullable): a #GCancellable, or %NULL
850 : : * @callback: callback to invoke on completion
851 : : * @user_data: user data to pass to @callback
852 : : *
853 : : * Create a new #MwscScheduleEntry for the given @object_path at @name on
854 : : * @connection, and set up the proxy object. This is an asynchronous process
855 : : * which might fail; object instantiation must be finished (or the error
856 : : * returned) by calling mwsc_schedule_entry_new_full_finish().
857 : : *
858 : : * Since: 0.1.0
859 : : */
860 : : void
861 : 1 : mwsc_schedule_entry_new_full_async (GDBusConnection *connection,
862 : : const gchar *name,
863 : : const gchar *object_path,
864 : : GCancellable *cancellable,
865 : : GAsyncReadyCallback callback,
866 : : gpointer user_data)
867 : : {
868 [ - + + - : 1 : g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+ - - + ]
869 [ + - - + ]: 1 : g_return_if_fail (name == NULL || g_dbus_is_name (name));
870 [ - + ]: 1 : g_return_if_fail ((g_dbus_connection_get_unique_name (connection) == NULL) ==
871 : : (name == NULL));
872 [ + - + - ]: 1 : g_return_if_fail (object_path != NULL &&
873 : : g_variant_is_object_path (object_path));
874 [ - + - - : 1 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - -
- ]
875 : :
876 : 1 : g_async_initable_new_async (MWSC_TYPE_SCHEDULE_ENTRY, G_PRIORITY_DEFAULT,
877 : : cancellable, callback, user_data,
878 : : "connection", connection,
879 : : "name", name,
880 : : "object-path", object_path,
881 : : NULL);
882 : : }
883 : :
884 : : /**
885 : : * mwsc_schedule_entry_new_full_finish:
886 : : * @result: asynchronous operation result
887 : : * @error: return location for a #GError
888 : : *
889 : : * Finish initialising a #MwscScheduleEntry. See
890 : : * mwsc_schedule_entry_new_full_async().
891 : : *
892 : : * Returns: (transfer full): initialised #MwscScheduleEntry, or %NULL on error
893 : : * Since: 0.1.0
894 : : */
895 : : MwscScheduleEntry *
896 : 1 : mwsc_schedule_entry_new_full_finish (GAsyncResult *result,
897 : : GError **error)
898 : : {
899 [ - + + - : 1 : g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
- + - + ]
900 [ + - - + ]: 1 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
901 : :
902 : 2 : g_autoptr(GObject) source_object = g_async_result_get_source_object (result);
903 : 1 : return MWSC_SCHEDULE_ENTRY (g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
904 : : result, error));
905 : : }
906 : :
907 : : /**
908 : : * mwsc_schedule_entry_get_id:
909 : : * @self: a #MwscScheduleEntry
910 : : *
911 : : * Get the persistent identifier for this schedule entry. This is assigned when
912 : : * at construction time, uniquely and persistently, and is never %NULL or the
913 : : * empty string.
914 : : *
915 : : * Returns: identifier for the entry
916 : : * Since: 0.1.0
917 : : */
918 : : const gchar *
919 : 0 : mwsc_schedule_entry_get_id (MwscScheduleEntry *self)
920 : : {
921 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULE_ENTRY (self), NULL);
922 : :
923 : 0 : const gchar *expected_prefix = "/com/endlessm/DownloadManager1/ScheduleEntry/";
924 : : const gchar *id;
925 : :
926 [ # # ]: 0 : if (g_str_has_prefix (self->object_path, expected_prefix) &&
927 [ # # ]: 0 : *(self->object_path + strlen (expected_prefix)) != '\0')
928 : 0 : id = self->object_path + strlen (expected_prefix);
929 : : else
930 : 0 : id = self->object_path;
931 : :
932 [ # # # # ]: 0 : g_assert (id != NULL && *id != '\0');
933 : 0 : return id;
934 : : }
935 : :
936 : : /**
937 : : * mwsc_schedule_entry_get_download_now:
938 : : * @self: a #MwscScheduleEntry
939 : : *
940 : : * Get the value of #MwscScheduleEntry:download-now.
941 : : *
942 : : * Returns: %TRUE if the download is allowed to use the network at the moment,
943 : : * %FALSE otherwise
944 : : * Since: 0.1.0
945 : : */
946 : : gboolean
947 : 0 : mwsc_schedule_entry_get_download_now (MwscScheduleEntry *self)
948 : : {
949 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULE_ENTRY (self), FALSE);
950 : :
951 [ # # ]: 0 : if (self->proxy == NULL)
952 : 0 : return FALSE;
953 : :
954 : 0 : g_autoptr(GVariant) download_now_variant = NULL;
955 : 0 : download_now_variant = g_dbus_proxy_get_cached_property (self->proxy, "DownloadNow");
956 [ # # ]: 0 : g_assert (download_now_variant != NULL);
957 : :
958 : 0 : return g_variant_get_boolean (download_now_variant);
959 : : }
960 : :
961 : : /**
962 : : * mwsc_schedule_entry_get_priority:
963 : : * @self: a #MwscScheduleEntry
964 : : *
965 : : * Get the value of #MwscScheduleEntry:priority.
966 : : *
967 : : * Returns: the entry’s priority
968 : : * Since: 0.1.0
969 : : */
970 : : guint32
971 : 0 : mwsc_schedule_entry_get_priority (MwscScheduleEntry *self)
972 : : {
973 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULE_ENTRY (self), 0);
974 : :
975 : 0 : return self->priority;
976 : : }
977 : :
978 : : /**
979 : : * mwsc_schedule_entry_set_priority:
980 : : * @self: a #MwscScheduleEntry
981 : : * @priority: the entry’s priority
982 : : *
983 : : * Set the value of #MwscScheduleEntry:priority.
984 : : *
985 : : * Since: 0.1.0
986 : : */
987 : : void
988 : 0 : mwsc_schedule_entry_set_priority (MwscScheduleEntry *self,
989 : : guint32 priority)
990 : : {
991 [ # # ]: 0 : g_return_if_fail (MWSC_IS_SCHEDULE_ENTRY (self));
992 : :
993 [ # # ]: 0 : if (self->priority == priority)
994 : 0 : return;
995 : :
996 : 0 : self->priority = priority;
997 : 0 : g_object_notify (G_OBJECT (self), "priority");
998 : : }
999 : :
1000 : : /**
1001 : : * mwsc_schedule_entry_get_resumable:
1002 : : * @self: a #MwscScheduleEntry
1003 : : *
1004 : : * Get the value of #MwscScheduleEntry:resumable.
1005 : : *
1006 : : * Returns: %TRUE if the download is resumable, %FALSE otherwise
1007 : : * Since: 0.1.0
1008 : : */
1009 : : gboolean
1010 : 0 : mwsc_schedule_entry_get_resumable (MwscScheduleEntry *self)
1011 : : {
1012 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULE_ENTRY (self), FALSE);
1013 : :
1014 : 0 : return self->resumable;
1015 : : }
1016 : :
1017 : : /**
1018 : : * mwsc_schedule_entry_set_resumable:
1019 : : * @self: a #MwscScheduleEntry
1020 : : * @resumable: %TRUE if the download is resumable, %FALSE otherwise
1021 : : *
1022 : : * Set the value of #MwscScheduleEntry:resumable.
1023 : : *
1024 : : * Since: 0.1.0
1025 : : */
1026 : : void
1027 : 0 : mwsc_schedule_entry_set_resumable (MwscScheduleEntry *self,
1028 : : gboolean resumable)
1029 : : {
1030 [ # # ]: 0 : g_return_if_fail (MWSC_IS_SCHEDULE_ENTRY (self));
1031 : :
1032 : 0 : resumable = !!resumable;
1033 [ # # ]: 0 : if (self->resumable == resumable)
1034 : 0 : return;
1035 : :
1036 : 0 : self->resumable = resumable;
1037 : 0 : g_object_notify (G_OBJECT (self), "resumable");
1038 : : }
1039 : :
1040 : : static gboolean queue_send_properties_sync (GDBusProxy *proxy,
1041 : : const gchar *property_name,
1042 : : GVariant *property_value,
1043 : : GCancellable *cancellable);
1044 : :
1045 : : /**
1046 : : * mwsc_schedule_entry_send_properties:
1047 : : * @self: a #MwscScheduleEntry
1048 : : * @cancellable: (nullable): a #GCancellable, or %NULL
1049 : : * @error: return location for a #GError, or %NULL
1050 : : *
1051 : : * Synchronous version of mwsc_schedule_entry_send_properties_async().
1052 : : *
1053 : : * Returns: %TRUE on success, %FALSE otherwise
1054 : : * Since: 0.2.0
1055 : : */
1056 : : gboolean
1057 : 0 : mwsc_schedule_entry_send_properties (MwscScheduleEntry *self,
1058 : : GCancellable *cancellable,
1059 : : GError **error)
1060 : : {
1061 : : gboolean success;
1062 : :
1063 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULE_ENTRY (self), FALSE);
1064 [ # # # # : 0 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
# # # # #
# ]
1065 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1066 : :
1067 [ # # ]: 0 : if (!check_invalidated_with_error (self, error))
1068 : 0 : return FALSE;
1069 : :
1070 : 0 : success = (queue_send_properties_sync (self->proxy,
1071 : : "Priority", g_variant_new_uint32 (self->priority),
1072 [ # # # # ]: 0 : cancellable) &&
1073 : 0 : queue_send_properties_sync (self->proxy,
1074 : : "Resumable", g_variant_new_boolean (self->resumable),
1075 : : cancellable));
1076 : :
1077 [ # # ]: 0 : if (!success)
1078 : : {
1079 : 0 : g_set_error_literal (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
1080 : : _("Error sending updated properties to service."));
1081 : 0 : return FALSE;
1082 : : }
1083 : :
1084 : 0 : return TRUE;
1085 : : }
1086 : :
1087 : : /* If @property_value is floating, it will be consumed. */
1088 : : static gboolean
1089 : 0 : queue_send_properties_sync (GDBusProxy *proxy,
1090 : : const gchar *property_name,
1091 : : GVariant *property_value,
1092 : : GCancellable *cancellable)
1093 : : {
1094 : 0 : g_autoptr(GVariant) sunk_property_value = g_variant_ref_sink (property_value);
1095 : :
1096 : 0 : g_autoptr(GVariant) cached_property_value = NULL;
1097 : 0 : cached_property_value = g_dbus_proxy_get_cached_property (proxy, property_name);
1098 [ # # ]: 0 : g_assert (cached_property_value != NULL);
1099 : :
1100 [ # # ]: 0 : if (g_variant_equal (cached_property_value, sunk_property_value))
1101 : 0 : return TRUE;
1102 : :
1103 : 0 : g_autoptr(GVariant) return_value =
1104 : 0 : g_dbus_connection_call_sync (g_dbus_proxy_get_connection (proxy),
1105 : : g_dbus_proxy_get_name (proxy),
1106 : : g_dbus_proxy_get_object_path (proxy),
1107 : : "org.freedesktop.DBus.Properties",
1108 : : "Set",
1109 : : g_variant_new ("(ssv)",
1110 : : "com.endlessm.DownloadManager1.ScheduleEntry",
1111 : : property_name,
1112 : : sunk_property_value),
1113 : : NULL, /* no reply type */
1114 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
1115 : : -1, /* default timeout */
1116 : : cancellable,
1117 : : NULL);
1118 : 0 : return (return_value != NULL);
1119 : : }
1120 : :
1121 : : typedef struct
1122 : : {
1123 : : guint n_properties;
1124 : : gboolean error;
1125 : : } SendPropertiesData;
1126 : :
1127 : : static void
1128 : 0 : send_properties_data_free (SendPropertiesData *data)
1129 : : {
1130 : 0 : g_free (data);
1131 : 0 : }
1132 : :
1133 [ # # ]: 0 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (SendPropertiesData, send_properties_data_free)
1134 : :
1135 : : static void queue_send_properties (GDBusProxy *proxy,
1136 : : const gchar *property_name,
1137 : : GVariant *property_value,
1138 : : GCancellable *cancellable,
1139 : : GTask *task,
1140 : : SendPropertiesData *data);
1141 : : static void send_properties_cb (GObject *obj,
1142 : : GAsyncResult *result,
1143 : : gpointer user_data);
1144 : :
1145 : : /**
1146 : : * mwsc_schedule_entry_send_properties_async:
1147 : : * @self: a #MwscScheduleEntry
1148 : : * @cancellable: (nullable): a #GCancellable, or %NULL
1149 : : * @callback: callback to invoke on completion
1150 : : * @user_data: user data to pass to @callback
1151 : : *
1152 : : * Send all locally updated properties to the server. Local changes made with
1153 : : * the mwsc_schedule_entry_set_*() functions are not sent to the server until
1154 : : * this method is called, in order to allow updates to be batched.
1155 : : *
1156 : : * (Note, currently the D-Bus API does not allow this batching to be atomic.)
1157 : : *
1158 : : * If no properties have been changed compared to their values on the server,
1159 : : * this is a no-op and will schedule @callback immediately.
1160 : : *
1161 : : * Since: 0.1.0
1162 : : */
1163 : : void
1164 : 0 : mwsc_schedule_entry_send_properties_async (MwscScheduleEntry *self,
1165 : : GCancellable *cancellable,
1166 : : GAsyncReadyCallback callback,
1167 : : gpointer user_data)
1168 : : {
1169 [ # # ]: 0 : g_return_if_fail (MWSC_IS_SCHEDULE_ENTRY (self));
1170 [ # # # # : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
# # # # #
# ]
1171 : :
1172 [ # # ]: 0 : g_autoptr(GTask) task = g_task_new (self, cancellable, callback, user_data);
1173 [ # # ]: 0 : g_task_set_source_tag (task, mwsc_schedule_entry_send_properties_async);
1174 : :
1175 [ # # ]: 0 : if (!check_invalidated_with_task (self, task))
1176 : 0 : return;
1177 : :
1178 : 0 : g_autoptr(SendPropertiesData) data = g_new0 (SendPropertiesData, 1);
1179 : 0 : data->n_properties = 1; /* keep it at 1 for this function to avoid an early return */
1180 : 0 : data->error = FALSE;
1181 : 0 : g_task_set_task_data (task, data /* steal */, (GDestroyNotify) send_properties_data_free);
1182 : :
1183 : 0 : queue_send_properties (self->proxy,
1184 : : "Priority", g_variant_new_uint32 (self->priority),
1185 : : cancellable, task, data);
1186 : 0 : queue_send_properties (self->proxy,
1187 : : "Resumable", g_variant_new_boolean (self->resumable),
1188 : : cancellable, task, data);
1189 : :
1190 : : /* Handle a possible early return. */
1191 : 0 : send_properties_cb (NULL, NULL, g_steal_pointer (&task));
1192 : 0 : data = NULL; /* stolen above */
1193 : : }
1194 : :
1195 : : /* If @property_value is floating, it will be consumed. */
1196 : : static void
1197 : 0 : queue_send_properties (GDBusProxy *proxy,
1198 : : const gchar *property_name,
1199 : : GVariant *property_value,
1200 : : GCancellable *cancellable,
1201 : : GTask *task,
1202 : : SendPropertiesData *data)
1203 : : {
1204 [ # # ]: 0 : g_autoptr(GVariant) sunk_property_value = g_variant_ref_sink (property_value);
1205 : :
1206 [ # # ]: 0 : g_autoptr(GVariant) cached_property_value = NULL;
1207 : 0 : cached_property_value = g_dbus_proxy_get_cached_property (proxy, property_name);
1208 [ # # ]: 0 : g_assert (cached_property_value != NULL);
1209 : :
1210 [ # # ]: 0 : if (g_variant_equal (cached_property_value, sunk_property_value))
1211 : 0 : return;
1212 : :
1213 : 0 : data->n_properties++;
1214 : 0 : g_dbus_connection_call (g_dbus_proxy_get_connection (proxy),
1215 : : g_dbus_proxy_get_name (proxy),
1216 : : g_dbus_proxy_get_object_path (proxy),
1217 : : "org.freedesktop.DBus.Properties",
1218 : : "Set",
1219 : : g_variant_new ("(ssv)",
1220 : : "com.endlessm.DownloadManager1.ScheduleEntry",
1221 : : property_name,
1222 : : sunk_property_value),
1223 : : NULL, /* no reply type */
1224 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
1225 : : -1, /* default timeout */
1226 : : cancellable,
1227 : : send_properties_cb,
1228 : : g_object_ref (task));
1229 : : }
1230 : :
1231 : : static void
1232 : 0 : send_properties_cb (GObject *obj,
1233 : : GAsyncResult *result,
1234 : : gpointer user_data)
1235 : : {
1236 : 0 : g_autoptr(GTask) task = G_TASK (user_data);
1237 : 0 : SendPropertiesData *data = g_task_get_task_data (task);
1238 : 0 : g_autoptr(GError) error = NULL;
1239 : :
1240 [ # # ]: 0 : if (result != NULL)
1241 : : {
1242 : 0 : g_autoptr(GVariant) return_value = NULL;
1243 : 0 : return_value = g_dbus_connection_call_finish (G_DBUS_CONNECTION (obj),
1244 : : result, &error);
1245 : : }
1246 : :
1247 : 0 : data->n_properties--;
1248 [ # # ]: 0 : if (error != NULL)
1249 : 0 : data->error = TRUE;
1250 : :
1251 [ # # ]: 0 : if (data->n_properties == 0)
1252 : : {
1253 [ # # ]: 0 : if (data->error)
1254 : 0 : g_task_return_new_error (task, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
1255 : 0 : _("Error sending updated properties to service."));
1256 : : else
1257 : 0 : g_task_return_boolean (task, TRUE);
1258 : : }
1259 : 0 : }
1260 : :
1261 : : /**
1262 : : * mwsc_schedule_entry_send_properties_finish:
1263 : : * @self: a #MwscScheduleEntry
1264 : : * @result: asynchronous operation result
1265 : : * @error: return location for a #GError
1266 : : *
1267 : : * Finish sending updated proprties from a #MwscScheduleEntry to the server. See
1268 : : * mwsc_schedule_entry_send_properties_async().
1269 : : *
1270 : : * Returns: %TRUE on success, %FALSE otherwise
1271 : : * Since: 0.1.0
1272 : : */
1273 : : gboolean
1274 : 0 : mwsc_schedule_entry_send_properties_finish (MwscScheduleEntry *self,
1275 : : GAsyncResult *result,
1276 : : GError **error)
1277 : : {
1278 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULE_ENTRY (self), FALSE);
1279 [ # # ]: 0 : g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
1280 [ # # ]: 0 : g_return_val_if_fail (g_async_result_is_tagged (result,
1281 : : mwsc_schedule_entry_send_properties_async),
1282 : : FALSE);
1283 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1284 : :
1285 : 0 : return g_task_propagate_boolean (G_TASK (result), error);
1286 : : }
1287 : :
1288 : : /**
1289 : : * mwsc_schedule_entry_remove:
1290 : : * @self: a #MwscScheduleEntry
1291 : : * @cancellable: (nullable): a #GCancellable, or %NULL
1292 : : * @error: return location for a #GError, or %NULL
1293 : : *
1294 : : * Synchronous version of mwsc_schedule_entry_remove_async().
1295 : : *
1296 : : * Returns: %TRUE on success, %FALSE otherwise
1297 : : * Since: 0.2.0
1298 : : */
1299 : : gboolean
1300 : 0 : mwsc_schedule_entry_remove (MwscScheduleEntry *self,
1301 : : GCancellable *cancellable,
1302 : : GError **error)
1303 : : {
1304 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULE_ENTRY (self), FALSE);
1305 [ # # # # : 0 : g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
# # # # #
# ]
1306 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1307 : :
1308 [ # # ]: 0 : if (!check_invalidated_with_error (self, error))
1309 : 0 : return FALSE;
1310 : :
1311 : 0 : g_autoptr(GVariant) return_value = NULL;
1312 : 0 : return_value = g_dbus_proxy_call_sync (self->proxy,
1313 : : "Remove",
1314 : : NULL, /* no parameters */
1315 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
1316 : : -1, /* default timeout */
1317 : : cancellable,
1318 : : error);
1319 : :
1320 : 0 : return (return_value != NULL);
1321 : : }
1322 : :
1323 : : static void remove_cb (GObject *obj,
1324 : : GAsyncResult *result,
1325 : : gpointer user_data);
1326 : :
1327 : : /**
1328 : : * mwsc_schedule_entry_remove_async:
1329 : : * @self: a #MwscScheduleEntry
1330 : : * @cancellable: (nullable): a #GCancellable, or %NULL
1331 : : * @callback: callback to invoke on completion
1332 : : * @user_data: user data to pass to @callback
1333 : : *
1334 : : * Remove this schedule entry from the scheduler. Typically this will be because
1335 : : * the associated download has finished; but it could also be because the
1336 : : * download has been cancelled or has errored.
1337 : : *
1338 : : * This will result in the #MwscScheduleEntry::invalidated signal being emitted,
1339 : : * and the entry entering the invalidated state; all future calls to
1340 : : * asynchronous methods on it will return
1341 : : * %MWSC_SCHEDULE_ENTRY_ERROR_INVALIDATED.
1342 : : *
1343 : : * Since: 0.1.0
1344 : : */
1345 : : void
1346 : 0 : mwsc_schedule_entry_remove_async (MwscScheduleEntry *self,
1347 : : GCancellable *cancellable,
1348 : : GAsyncReadyCallback callback,
1349 : : gpointer user_data)
1350 : : {
1351 [ # # ]: 0 : g_return_if_fail (MWSC_IS_SCHEDULE_ENTRY (self));
1352 [ # # # # : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
# # # # #
# ]
1353 : :
1354 [ # # ]: 0 : g_autoptr(GTask) task = g_task_new (self, cancellable, callback, user_data);
1355 [ # # ]: 0 : g_task_set_source_tag (task, mwsc_schedule_entry_remove_async);
1356 : :
1357 [ # # ]: 0 : if (!check_invalidated_with_task (self, task))
1358 : 0 : return;
1359 : :
1360 : 0 : g_dbus_proxy_call (self->proxy,
1361 : : "Remove",
1362 : : NULL, /* no parameters */
1363 : : G_DBUS_CALL_FLAGS_NO_AUTO_START,
1364 : : -1, /* default timeout */
1365 : : cancellable,
1366 : : remove_cb,
1367 : : g_steal_pointer (&task));
1368 : : }
1369 : :
1370 : : static void
1371 : 0 : remove_cb (GObject *obj,
1372 : : GAsyncResult *result,
1373 : : gpointer user_data)
1374 : : {
1375 : 0 : GDBusProxy *proxy = G_DBUS_PROXY (obj);
1376 : 0 : g_autoptr(GTask) task = G_TASK (user_data);
1377 : 0 : g_autoptr(GError) error = NULL;
1378 : :
1379 : 0 : g_autoptr(GVariant) return_value = NULL;
1380 : 0 : return_value = g_dbus_proxy_call_finish (proxy, result, &error);
1381 : :
1382 [ # # ]: 0 : if (error != NULL)
1383 : 0 : g_task_return_error (task, g_steal_pointer (&error));
1384 : : else
1385 : 0 : g_task_return_boolean (task, TRUE);
1386 : 0 : }
1387 : :
1388 : : /**
1389 : : * mwsc_schedule_entry_remove_finish:
1390 : : * @self: a #MwscScheduleEntry
1391 : : * @result: asynchronous operation result
1392 : : * @error: return location for a #GError
1393 : : *
1394 : : * Finish removing a #MwscScheduleEntry. See mwsc_schedule_entry_remove_async().
1395 : : *
1396 : : * Returns: %TRUE on success, %FALSE otherwise
1397 : : * Since: 0.1.0
1398 : : */
1399 : : gboolean
1400 : 0 : mwsc_schedule_entry_remove_finish (MwscScheduleEntry *self,
1401 : : GAsyncResult *result,
1402 : : GError **error)
1403 : : {
1404 [ # # ]: 0 : g_return_val_if_fail (MWSC_IS_SCHEDULE_ENTRY (self), FALSE);
1405 [ # # ]: 0 : g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
1406 [ # # ]: 0 : g_return_val_if_fail (g_async_result_is_tagged (result,
1407 : : mwsc_schedule_entry_remove_async),
1408 : : FALSE);
1409 : :
1410 : 0 : return g_task_propagate_boolean (G_TASK (result), error);
1411 : : }
|