1 /*
2  * e-client.c
3  *
4  * Copyright (C) 2011 Red Hat, Inc. (www.redhat.com)
5  *
6  * This library is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13  * for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library. If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 /* TODO The next time we have a good excuse to break libedataserver's API,
21  *      I'd like to purge all the deprecated cruft here and convert EClient
22  *      from a GObjectClass to a GTypeInterface, implemented by EBookClient
23  *      and ECalClient.  Then we could just bind the "online", "readonly"
24  *      and "capabilities" properties to equivalent GDBusProxy properties
25  *      and kill e-client-private.h.  Would simplify things.  --mbarnes
26  */
27 
28 /**
29  * SECTION: e-client
30  * @include: libedataserver/libedataserver.h
31  * @short_description: Base class for client handles
32  *
33  * This class provides some base functionality for clients
34  * such as #EBookClient and #ECalClient.
35  **/
36 
37 #include "evolution-data-server-config.h"
38 
39 #include <glib/gi18n-lib.h>
40 #include <gio/gio.h>
41 
42 #include <libedataserver/e-data-server-util.h>
43 
44 #include "e-flag.h"
45 
46 #include "e-client.h"
47 #include "e-client-private.h"
48 
49 typedef struct _AsyncContext AsyncContext;
50 
51 struct _EClientPrivate {
52 	GRecMutex prop_mutex;
53 
54 	ESource *source;
55 	gboolean online;
56 	gboolean readonly;
57 	GSList *capabilities;
58 	GMainContext *main_context;
59 	gchar *bus_name;
60 };
61 
62 struct _AsyncContext {
63 	gchar *capabilities;
64 	gchar *prop_name;
65 	gchar *prop_value;
66 };
67 
68 enum {
69 	PROP_0,
70 	PROP_CAPABILITIES,
71 	PROP_MAIN_CONTEXT,
72 	PROP_ONLINE,
73 	PROP_OPENED,
74 	PROP_READONLY,
75 	PROP_SOURCE
76 };
77 
78 enum {
79 	OPENED,
80 	BACKEND_ERROR,
81 	BACKEND_DIED,
82 	BACKEND_PROPERTY_CHANGED,
83 	LAST_SIGNAL
84 };
85 
86 static guint signals[LAST_SIGNAL];
87 
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(EClient,e_client,G_TYPE_OBJECT)88 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (EClient, e_client, G_TYPE_OBJECT)
89 
90 static void
91 async_context_free (AsyncContext *async_context)
92 {
93 	g_free (async_context->capabilities);
94 	g_free (async_context->prop_name);
95 	g_free (async_context->prop_value);
96 
97 	g_slice_free (AsyncContext, async_context);
98 }
99 
100 /*
101  * Well-known client backend properties, which are common for each #EClient:
102  * @CLIENT_BACKEND_PROPERTY_OPENED: Is set to "TRUE" or "FALSE" depending
103  *   whether the backend is fully opened.
104  * @CLIENT_BACKEND_PROPERTY_OPENING: Is set to "TRUE" or "FALSE" depending
105  *   whether the backend is processing its opening phase.
106  * @CLIENT_BACKEND_PROPERTY_ONLINE: Is set to "TRUE" or "FALSE" depending
107  *   on the backend's loaded state. See also e_client_is_online().
108  * @CLIENT_BACKEND_PROPERTY_READONLY: Is set to "TRUE" or "FALSE" depending
109  *   on the backend's readonly state. See also e_client_is_readonly().
110  * @CLIENT_BACKEND_PROPERTY_CACHE_DIR: Local folder with cached data used
111  *   by the backend.
112  * @CLIENT_BACKEND_PROPERTY_CAPABILITIES: Retrieves comma-separated list
113  *   of	capabilities supported by the backend. Preferred method of retreiving
114  *   and working with capabilities is e_client_get_capabilities() and
115  *   e_client_check_capability().
116  */
117 
118 G_DEFINE_QUARK (e-client-error-quark, e_client_error)
119 
120 /**
121  * e_client_error_to_string:
122  * @code: an #EClientError error code
123  *
124  * Get localized human readable description of the given error code.
125  *
126  * Returns: Localized human readable description of the given error code
127  *
128  * Since: 3.2
129  **/
130 const gchar *
e_client_error_to_string(EClientError code)131 e_client_error_to_string (EClientError code)
132 {
133 	switch (code) {
134 	case E_CLIENT_ERROR_INVALID_ARG:
135 		return _("Invalid argument");
136 	case E_CLIENT_ERROR_BUSY:
137 		return _("Backend is busy");
138 	case E_CLIENT_ERROR_SOURCE_NOT_LOADED:
139 		return _("Source not loaded");
140 	case E_CLIENT_ERROR_SOURCE_ALREADY_LOADED:
141 		return _("Source already loaded");
142 	case E_CLIENT_ERROR_AUTHENTICATION_FAILED:
143 		return _("Authentication failed");
144 	case E_CLIENT_ERROR_AUTHENTICATION_REQUIRED:
145 		return _("Authentication required");
146 	case E_CLIENT_ERROR_REPOSITORY_OFFLINE:
147 		return _("Repository offline");
148 	case E_CLIENT_ERROR_OFFLINE_UNAVAILABLE:
149 		/* Translators: This means that the EClient does not
150 		 * support offline mode, or it's not set to by a user,
151 		 * thus it is unavailable while user is not connected. */
152 		return _("Offline unavailable");
153 	case E_CLIENT_ERROR_PERMISSION_DENIED:
154 		return _("Permission denied");
155 	case E_CLIENT_ERROR_CANCELLED:
156 		return _("Cancelled");
157 	case E_CLIENT_ERROR_COULD_NOT_CANCEL:
158 		return _("Could not cancel");
159 	case E_CLIENT_ERROR_NOT_SUPPORTED:
160 		return _("Not supported");
161 	case E_CLIENT_ERROR_UNSUPPORTED_AUTHENTICATION_METHOD:
162 		return _("Unsupported authentication method");
163 	case E_CLIENT_ERROR_TLS_NOT_AVAILABLE:
164 		return _("TLS not available");
165 	case E_CLIENT_ERROR_SEARCH_SIZE_LIMIT_EXCEEDED:
166 		return _("Search size limit exceeded");
167 	case E_CLIENT_ERROR_SEARCH_TIME_LIMIT_EXCEEDED:
168 		return _("Search time limit exceeded");
169 	case E_CLIENT_ERROR_INVALID_QUERY:
170 		return _("Invalid query");
171 	case E_CLIENT_ERROR_QUERY_REFUSED:
172 		return _("Query refused");
173 	case E_CLIENT_ERROR_DBUS_ERROR:
174 		return _("D-Bus error");
175 	case E_CLIENT_ERROR_OTHER_ERROR:
176 		return _("Other error");
177 	case E_CLIENT_ERROR_NOT_OPENED:
178 		return _("Backend is not opened yet");
179 	case E_CLIENT_ERROR_OUT_OF_SYNC:
180 		return _("Object is out of sync");
181 	}
182 
183 	return _("Unknown error");
184 }
185 
186 /**
187  * e_client_error_create:
188  * @code: an #EClientError code to create
189  * @custom_msg: (nullable): custom message to use for the error; can be %NULL
190  *
191  * Returns: (transfer full): a new #GError containing an #E_CLIENT_ERROR of the given
192  *    @code. If the @custom_msg is NULL, then the error message is the one returned
193  *    from e_client_error_to_string() for the @code, otherwise the given message is used.
194  *    Returned pointer should be freed with g_error_free().
195  *
196  * Since: 3.2
197  **/
198 GError *
e_client_error_create(EClientError code,const gchar * custom_msg)199 e_client_error_create (EClientError code,
200                        const gchar *custom_msg)
201 {
202 	if (!custom_msg)
203 		custom_msg = e_client_error_to_string (code);
204 
205 	return g_error_new_literal (E_CLIENT_ERROR, code, custom_msg);
206 }
207 
208 /**
209  * e_client_error_create_fmt:
210  * @code: an #EClientError
211  * @format: (nullable): message format, or %NULL to use the default message for the @code
212  * @...: arguments for the format
213  *
214  * Similar as e_client_error_create(), only here, instead of custom_msg,
215  * is used a printf() format to create a custom message for the error.
216  *
217  * Returns: (transfer full): a newly allocated #GError, which should be
218  *   freed with g_error_free(), when no longer needed.
219  *   The #GError has set the custom message, or the default message for
220  *   @code, when @format is %NULL.
221  *
222  * Since: 3.34
223  **/
224 GError *
e_client_error_create_fmt(EClientError code,const gchar * format,...)225 e_client_error_create_fmt (EClientError code,
226 			   const gchar *format,
227 			   ...)
228 {
229 	GError *error;
230 	gchar *custom_msg;
231 	va_list ap;
232 
233 	if (!format)
234 		return e_client_error_create (code, NULL);
235 
236 	va_start (ap, format);
237 	custom_msg = g_strdup_vprintf (format, ap);
238 	va_end (ap);
239 
240 	error = e_client_error_create (code, custom_msg);
241 
242 	g_free (custom_msg);
243 
244 	return error;
245 }
246 
247 static void
client_set_source(EClient * client,ESource * source)248 client_set_source (EClient *client,
249                    ESource *source)
250 {
251 	g_return_if_fail (E_IS_SOURCE (source));
252 	g_return_if_fail (client->priv->source == NULL);
253 
254 	client->priv->source = g_object_ref (source);
255 }
256 
257 static void
client_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)258 client_set_property (GObject *object,
259                      guint property_id,
260                      const GValue *value,
261                      GParamSpec *pspec)
262 {
263 	switch (property_id) {
264 		case PROP_ONLINE:
265 			e_client_set_online (
266 				E_CLIENT (object),
267 				g_value_get_boolean (value));
268 			return;
269 
270 		case PROP_SOURCE:
271 			client_set_source (
272 				E_CLIENT (object),
273 				g_value_get_object (value));
274 			return;
275 	}
276 
277 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
278 }
279 
280 static void
client_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)281 client_get_property (GObject *object,
282                      guint property_id,
283                      GValue *value,
284                      GParamSpec *pspec)
285 {
286 	switch (property_id) {
287 		case PROP_CAPABILITIES:
288 			g_value_set_pointer (
289 				value,
290 				(gpointer) e_client_get_capabilities (
291 				E_CLIENT (object)));
292 			return;
293 
294 		case PROP_MAIN_CONTEXT:
295 			g_value_take_boxed (
296 				value,
297 				e_client_ref_main_context (
298 				E_CLIENT (object)));
299 			return;
300 
301 		case PROP_ONLINE:
302 			g_value_set_boolean (
303 				value,
304 				e_client_is_online (
305 				E_CLIENT (object)));
306 			return;
307 
308 		case PROP_OPENED:
309 			g_value_set_boolean (
310 				value,
311 				e_client_is_opened (
312 				E_CLIENT (object)));
313 			return;
314 
315 		case PROP_READONLY:
316 			g_value_set_boolean (
317 				value,
318 				e_client_is_readonly (
319 				E_CLIENT (object)));
320 			return;
321 
322 		case PROP_SOURCE:
323 			g_value_set_object (
324 				value,
325 				e_client_get_source (
326 				E_CLIENT (object)));
327 			return;
328 	}
329 
330 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
331 }
332 
333 static void
client_dispose(GObject * object)334 client_dispose (GObject *object)
335 {
336 	EClientPrivate *priv;
337 
338 	priv = E_CLIENT (object)->priv;
339 	g_clear_pointer (&priv->main_context, g_main_context_unref);
340 	g_clear_object (&priv->source);
341 
342 	g_free (priv->bus_name);
343 	priv->bus_name = NULL;
344 
345 	/* Chain up to parent's dispose() method. */
346 	G_OBJECT_CLASS (e_client_parent_class)->dispose (object);
347 }
348 
349 static void
client_finalize(GObject * object)350 client_finalize (GObject *object)
351 {
352 	EClientPrivate *priv;
353 
354 	priv = E_CLIENT (object)->priv;
355 
356 	g_slist_free_full (priv->capabilities, (GDestroyNotify) g_free);
357 
358 	g_rec_mutex_clear (&priv->prop_mutex);
359 
360 	/* Chain up to parent's finalize() method. */
361 	G_OBJECT_CLASS (e_client_parent_class)->finalize (object);
362 }
363 
364 static void
client_unwrap_dbus_error(EClient * client,GError * dbus_error,GError ** out_error)365 client_unwrap_dbus_error (EClient *client,
366                           GError *dbus_error,
367                           GError **out_error)
368 {
369 	/* This method is deprecated.  Make it a no-op. */
370 
371 	if (out_error != NULL)
372 		*out_error = dbus_error;
373 }
374 
375 /* Helper for client_retrieve_capabilities() */
376 static void
client_retrieve_capabilities_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)377 client_retrieve_capabilities_thread (GSimpleAsyncResult *simple,
378                                      GObject *source_object,
379                                      GCancellable *cancellable)
380 {
381 	AsyncContext *async_context;
382 	GError *error = NULL;
383 
384 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
385 
386 	e_client_retrieve_capabilities_sync (
387 		E_CLIENT (source_object),
388 		&async_context->capabilities,
389 		cancellable, &error);
390 
391 	if (error != NULL)
392 		g_simple_async_result_take_error (simple, error);
393 }
394 
395 static void
client_retrieve_capabilities(EClient * client,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)396 client_retrieve_capabilities (EClient *client,
397                               GCancellable *cancellable,
398                               GAsyncReadyCallback callback,
399                               gpointer user_data)
400 {
401 	GSimpleAsyncResult *simple;
402 	AsyncContext *async_context;
403 
404 	async_context = g_slice_new0 (AsyncContext);
405 
406 	simple = g_simple_async_result_new (
407 		G_OBJECT (client), callback,
408 		user_data, client_retrieve_capabilities);
409 
410 	g_simple_async_result_set_check_cancellable (simple, cancellable);
411 
412 	g_simple_async_result_set_op_res_gpointer (
413 		simple, async_context, (GDestroyNotify) async_context_free);
414 
415 	g_simple_async_result_run_in_thread (
416 		simple, client_retrieve_capabilities_thread,
417 		G_PRIORITY_DEFAULT, cancellable);
418 
419 	g_object_unref (simple);
420 }
421 
422 static gboolean
client_retrieve_capabilities_finish(EClient * client,GAsyncResult * result,gchar ** capabilities,GError ** error)423 client_retrieve_capabilities_finish (EClient *client,
424                                      GAsyncResult *result,
425                                      gchar **capabilities,
426                                      GError **error)
427 {
428 	GSimpleAsyncResult *simple;
429 	AsyncContext *async_context;
430 
431 	g_return_val_if_fail (
432 		g_simple_async_result_is_valid (
433 		result, G_OBJECT (client),
434 		client_retrieve_capabilities), FALSE);
435 
436 	simple = G_SIMPLE_ASYNC_RESULT (result);
437 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
438 
439 	if (g_simple_async_result_propagate_error (simple, error))
440 		return FALSE;
441 
442 	g_return_val_if_fail (async_context->capabilities != NULL, FALSE);
443 
444 	if (capabilities != NULL) {
445 		*capabilities = async_context->capabilities;
446 		async_context->capabilities = NULL;
447 	}
448 
449 	return TRUE;
450 }
451 
452 static gboolean
client_retrieve_capabilities_sync(EClient * client,gchar ** capabilities,GCancellable * cancellable,GError ** error)453 client_retrieve_capabilities_sync (EClient *client,
454                                    gchar **capabilities,
455                                    GCancellable *cancellable,
456                                    GError **error)
457 {
458 	return e_client_get_backend_property_sync (
459 		client, CLIENT_BACKEND_PROPERTY_CAPABILITIES,
460 		capabilities, cancellable, error);
461 }
462 
463 /* Helper for client_get_backend_property() */
464 static void
client_get_backend_property_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)465 client_get_backend_property_thread (GSimpleAsyncResult *simple,
466                                     GObject *source_object,
467                                     GCancellable *cancellable)
468 {
469 	AsyncContext *async_context;
470 	GError *error = NULL;
471 
472 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
473 
474 	e_client_get_backend_property_sync (
475 		E_CLIENT (source_object),
476 		async_context->prop_name,
477 		&async_context->prop_value,
478 		cancellable, &error);
479 
480 	if (error != NULL)
481 		g_simple_async_result_take_error (simple, error);
482 }
483 
484 static void
client_get_backend_property(EClient * client,const gchar * prop_name,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)485 client_get_backend_property (EClient *client,
486                              const gchar *prop_name,
487                              GCancellable *cancellable,
488                              GAsyncReadyCallback callback,
489                              gpointer user_data)
490 {
491 	GSimpleAsyncResult *simple;
492 	AsyncContext *async_context;
493 
494 	async_context = g_slice_new0 (AsyncContext);
495 	async_context->prop_name = g_strdup (prop_name);
496 
497 	simple = g_simple_async_result_new (
498 		G_OBJECT (client), callback,
499 		user_data, client_get_backend_property);
500 
501 	g_simple_async_result_set_check_cancellable (simple, cancellable);
502 
503 	g_simple_async_result_set_op_res_gpointer (
504 		simple, async_context, (GDestroyNotify) async_context_free);
505 
506 	g_simple_async_result_run_in_thread (
507 		simple, client_get_backend_property_thread,
508 		G_PRIORITY_DEFAULT, cancellable);
509 
510 	g_object_unref (simple);
511 }
512 
513 static gboolean
client_get_backend_property_finish(EClient * client,GAsyncResult * result,gchar ** prop_value,GError ** error)514 client_get_backend_property_finish (EClient *client,
515                                     GAsyncResult *result,
516                                     gchar **prop_value,
517                                     GError **error)
518 {
519 	GSimpleAsyncResult *simple;
520 	AsyncContext *async_context;
521 
522 	g_return_val_if_fail (
523 		g_simple_async_result_is_valid (
524 		result, G_OBJECT (client),
525 		client_get_backend_property), FALSE);
526 
527 	simple = G_SIMPLE_ASYNC_RESULT (result);
528 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
529 
530 	if (g_simple_async_result_propagate_error (simple, error))
531 		return FALSE;
532 
533 	g_return_val_if_fail (async_context->prop_value != NULL, FALSE);
534 
535 	if (prop_value != NULL) {
536 		*prop_value = async_context->prop_value;
537 		async_context->prop_value = NULL;
538 	}
539 
540 	return TRUE;
541 }
542 
543 /* Helper for client_set_backend_property() */
544 static void
client_set_backend_property_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)545 client_set_backend_property_thread (GSimpleAsyncResult *simple,
546                                     GObject *source_object,
547                                     GCancellable *cancellable)
548 {
549 	AsyncContext *async_context;
550 	GError *error = NULL;
551 
552 	async_context = g_simple_async_result_get_op_res_gpointer (simple);
553 
554 	e_client_set_backend_property_sync (
555 		E_CLIENT (source_object),
556 		async_context->prop_name,
557 		async_context->prop_value,
558 		cancellable, &error);
559 
560 	if (error != NULL)
561 		g_simple_async_result_take_error (simple, error);
562 }
563 
564 static void
client_set_backend_property(EClient * client,const gchar * prop_name,const gchar * prop_value,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)565 client_set_backend_property (EClient *client,
566                              const gchar *prop_name,
567                              const gchar *prop_value,
568                              GCancellable *cancellable,
569                              GAsyncReadyCallback callback,
570                              gpointer user_data)
571 {
572 	GSimpleAsyncResult *simple;
573 	AsyncContext *async_context;
574 
575 	async_context = g_slice_new0 (AsyncContext);
576 	async_context->prop_name = g_strdup (prop_name);
577 	async_context->prop_value = g_strdup (prop_value);
578 
579 	simple = g_simple_async_result_new (
580 		G_OBJECT (client), callback,
581 		user_data, client_set_backend_property);
582 
583 	g_simple_async_result_set_check_cancellable (simple, cancellable);
584 
585 	g_simple_async_result_set_op_res_gpointer (
586 		simple, async_context, (GDestroyNotify) async_context_free);
587 
588 	g_simple_async_result_run_in_thread (
589 		simple, client_set_backend_property_thread,
590 		G_PRIORITY_DEFAULT, cancellable);
591 
592 	g_object_unref (simple);
593 }
594 
595 static gboolean
client_set_backend_property_finish(EClient * client,GAsyncResult * result,GError ** error)596 client_set_backend_property_finish (EClient *client,
597                                     GAsyncResult *result,
598                                     GError **error)
599 {
600 	GSimpleAsyncResult *simple;
601 
602 	g_return_val_if_fail (
603 		g_simple_async_result_is_valid (
604 		result, G_OBJECT (client),
605 		client_set_backend_property), FALSE);
606 
607 	simple = G_SIMPLE_ASYNC_RESULT (result);
608 
609 	/* Assume success unless a GError is set. */
610 	return !g_simple_async_result_propagate_error (simple, error);
611 }
612 
613 /* Helper for client_open() */
614 static void
client_open_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)615 client_open_thread (GSimpleAsyncResult *simple,
616                     GObject *source_object,
617                     GCancellable *cancellable)
618 {
619 	GError *error = NULL;
620 
621 	e_client_open_sync (E_CLIENT (source_object), FALSE, cancellable, &error);
622 
623 	if (error != NULL)
624 		g_simple_async_result_take_error (simple, error);
625 }
626 
627 static void
client_open(EClient * client,gboolean only_if_exists,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)628 client_open (EClient *client,
629              gboolean only_if_exists,
630              GCancellable *cancellable,
631              GAsyncReadyCallback callback,
632              gpointer user_data)
633 {
634 	GSimpleAsyncResult *simple;
635 	AsyncContext *async_context;
636 
637 	async_context = g_slice_new0 (AsyncContext);
638 
639 	simple = g_simple_async_result_new (
640 		G_OBJECT (client), callback, user_data, client_open);
641 
642 	g_simple_async_result_set_check_cancellable (simple, cancellable);
643 
644 	g_simple_async_result_set_op_res_gpointer (
645 		simple, async_context, (GDestroyNotify) async_context_free);
646 
647 	g_simple_async_result_run_in_thread (
648 		simple, client_open_thread,
649 		G_PRIORITY_DEFAULT, cancellable);
650 
651 	g_object_unref (simple);
652 }
653 
654 static gboolean
client_open_finish(EClient * client,GAsyncResult * result,GError ** error)655 client_open_finish (EClient *client,
656                     GAsyncResult *result,
657                     GError **error)
658 {
659 	GSimpleAsyncResult *simple;
660 
661 	g_return_val_if_fail (
662 		g_simple_async_result_is_valid (
663 		result, G_OBJECT (client), client_open), FALSE);
664 
665 	simple = G_SIMPLE_ASYNC_RESULT (result);
666 
667 	/* Assume success unless a GError is set. */
668 	return !g_simple_async_result_propagate_error (simple, error);
669 }
670 
671 /* Helper for client_remove() */
672 static void
client_remove_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)673 client_remove_thread (GSimpleAsyncResult *simple,
674                       GObject *source_object,
675                       GCancellable *cancellable)
676 {
677 	GError *error = NULL;
678 
679 	e_client_remove_sync (
680 		E_CLIENT (source_object), cancellable, &error);
681 
682 	if (error != NULL)
683 		g_simple_async_result_take_error (simple, error);
684 }
685 
686 static void
client_remove(EClient * client,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)687 client_remove (EClient *client,
688                GCancellable *cancellable,
689                GAsyncReadyCallback callback,
690                gpointer user_data)
691 {
692 	GSimpleAsyncResult *simple;
693 
694 	simple = g_simple_async_result_new (
695 		G_OBJECT (client), callback, user_data, client_remove);
696 
697 	g_simple_async_result_set_check_cancellable (simple, cancellable);
698 
699 	g_simple_async_result_run_in_thread (
700 		simple, client_remove_thread,
701 		G_PRIORITY_DEFAULT, cancellable);
702 
703 	g_object_unref (simple);
704 }
705 
706 static gboolean
client_remove_finish(EClient * client,GAsyncResult * result,GError ** error)707 client_remove_finish (EClient *client,
708                       GAsyncResult *result,
709                       GError **error)
710 {
711 	GSimpleAsyncResult *simple;
712 
713 	g_return_val_if_fail (
714 		g_simple_async_result_is_valid (
715 		result, G_OBJECT (client), client_remove), FALSE);
716 
717 	simple = G_SIMPLE_ASYNC_RESULT (result);
718 
719 	/* Assume success unless a GError is set. */
720 	return !g_simple_async_result_propagate_error (simple, error);
721 }
722 
723 static gboolean
client_remove_sync(EClient * client,GCancellable * cancellable,GError ** error)724 client_remove_sync (EClient *client,
725                     GCancellable *cancellable,
726                     GError **error)
727 {
728 	ESource *source;
729 
730 	source = e_client_get_source (client);
731 
732 	return e_source_remove_sync (source, cancellable, error);
733 }
734 
735 /* Helper for client_refresh() */
736 static void
client_refresh_thread(GSimpleAsyncResult * simple,GObject * source_object,GCancellable * cancellable)737 client_refresh_thread (GSimpleAsyncResult *simple,
738                        GObject *source_object,
739                        GCancellable *cancellable)
740 {
741 	GError *error = NULL;
742 
743 	e_client_refresh_sync (
744 		E_CLIENT (source_object), cancellable, &error);
745 
746 	if (error != NULL)
747 		g_simple_async_result_take_error (simple, error);
748 }
749 
750 static void
client_refresh(EClient * client,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)751 client_refresh (EClient *client,
752                 GCancellable *cancellable,
753                 GAsyncReadyCallback callback,
754                 gpointer user_data)
755 {
756 	GSimpleAsyncResult *simple;
757 
758 	simple = g_simple_async_result_new (
759 		G_OBJECT (client), callback, user_data, client_refresh);
760 
761 	g_simple_async_result_set_check_cancellable (simple, cancellable);
762 
763 	g_simple_async_result_run_in_thread (
764 		simple, client_refresh_thread,
765 		G_PRIORITY_DEFAULT, cancellable);
766 
767 	g_object_unref (simple);
768 }
769 
770 static gboolean
client_refresh_finish(EClient * client,GAsyncResult * result,GError ** error)771 client_refresh_finish (EClient *client,
772                        GAsyncResult *result,
773                        GError **error)
774 {
775 	GSimpleAsyncResult *simple;
776 
777 	g_return_val_if_fail (
778 		g_simple_async_result_is_valid (
779 		result, G_OBJECT (client), client_refresh), FALSE);
780 
781 	simple = G_SIMPLE_ASYNC_RESULT (result);
782 
783 	/* Assume success unless a GError is set. */
784 	return !g_simple_async_result_propagate_error (simple, error);
785 }
786 
787 static void
e_client_class_init(EClientClass * class)788 e_client_class_init (EClientClass *class)
789 {
790 	GObjectClass *object_class;
791 
792 	object_class = G_OBJECT_CLASS (class);
793 	object_class->set_property = client_set_property;
794 	object_class->get_property = client_get_property;
795 	object_class->dispose = client_dispose;
796 	object_class->finalize = client_finalize;
797 
798 	class->unwrap_dbus_error = client_unwrap_dbus_error;
799 	class->retrieve_capabilities = client_retrieve_capabilities;
800 	class->retrieve_capabilities_finish = client_retrieve_capabilities_finish;
801 	class->retrieve_capabilities_sync = client_retrieve_capabilities_sync;
802 	class->get_backend_property = client_get_backend_property;
803 	class->get_backend_property_finish = client_get_backend_property_finish;
804 	class->set_backend_property = client_set_backend_property;
805 	class->set_backend_property_finish = client_set_backend_property_finish;
806 	class->open = client_open;
807 	class->open_finish = client_open_finish;
808 	class->remove = client_remove;
809 	class->remove_finish = client_remove_finish;
810 	class->remove_sync = client_remove_sync;
811 	class->refresh = client_refresh;
812 	class->refresh_finish = client_refresh_finish;
813 
814 	/**
815 	 * EClient:capabilities:
816 	 *
817 	 * The capabilities of this client
818 	 */
819 	g_object_class_install_property (
820 		object_class,
821 		PROP_CAPABILITIES,
822 		g_param_spec_pointer (
823 			"capabilities",
824 			"Capabilities",
825 			"The capabilities of this client",
826 			G_PARAM_READABLE |
827 			G_PARAM_EXPLICIT_NOTIFY |
828 			G_PARAM_STATIC_STRINGS));
829 
830 	/**
831 	 * EClient:main-context:
832 	 *
833 	 * The main loop context in which notifications for
834 	 * this client will be delivered.
835 	 */
836 	g_object_class_install_property (
837 		object_class,
838 		PROP_MAIN_CONTEXT,
839 		g_param_spec_boxed (
840 			"main-context",
841 			"Main Context",
842 			"The main loop context on "
843 			"which to attach event sources",
844 			G_TYPE_MAIN_CONTEXT,
845 			G_PARAM_READABLE |
846 			G_PARAM_STATIC_STRINGS));
847 
848 	/**
849 	 * EClient:online:
850 	 *
851 	 * Whether this client's backing data is online.
852 	 */
853 	g_object_class_install_property (
854 		object_class,
855 		PROP_ONLINE,
856 		g_param_spec_boolean (
857 			"online",
858 			"Online",
859 			"Whether this client is online",
860 			FALSE,
861 			G_PARAM_READWRITE |
862 			G_PARAM_EXPLICIT_NOTIFY |
863 			G_PARAM_STATIC_STRINGS));
864 
865 	/**
866 	 * EClient:opened:
867 	 *
868 	 * Whether this client is open and ready to use.
869 	 *
870 	 * Deprecated: 3.8: This property is no longer relevant and
871 	 * will always be %TRUE after successfully creating any concrete
872 	 * type of #EClient.
873 	 */
874 	g_object_class_install_property (
875 		object_class,
876 		PROP_OPENED,
877 		g_param_spec_boolean (
878 			"opened",
879 			"Opened",
880 			"Whether this client is open and ready to use",
881 			FALSE,
882 			G_PARAM_READABLE |
883 			G_PARAM_STATIC_STRINGS));
884 
885 	/**
886 	 * EClient:readonly:
887 	 *
888 	 * Whether this client's backing data is readonly.
889 	 */
890 	g_object_class_install_property (
891 		object_class,
892 		PROP_READONLY,
893 		g_param_spec_boolean (
894 			"readonly",
895 			"Read only",
896 			"Whether this client's backing data is readonly",
897 			FALSE,
898 			G_PARAM_READABLE |
899 			G_PARAM_STATIC_STRINGS));
900 
901 	/**
902 	 * EClient:source:
903 	 *
904 	 * The #ESource for which this client was created.
905 	 */
906 	g_object_class_install_property (
907 		object_class,
908 		PROP_SOURCE,
909 		g_param_spec_object (
910 			"source",
911 			"Source",
912 			"The ESource for which this client was created",
913 			E_TYPE_SOURCE,
914 			G_PARAM_READWRITE |
915 			G_PARAM_CONSTRUCT_ONLY |
916 			G_PARAM_STATIC_STRINGS));
917 
918 	/**
919 	 * EClient::opened-signal: (skip)
920 	 *
921 	 * Deprecated: 3.8: This signal is no longer emitted.
922 	 **/
923 	signals[OPENED] = g_signal_new (
924 		"opened",
925 		G_OBJECT_CLASS_TYPE (class),
926 		G_SIGNAL_RUN_LAST |
927 		G_SIGNAL_DEPRECATED,
928 		G_STRUCT_OFFSET (EClientClass, opened),
929 		NULL, NULL, NULL,
930 		G_TYPE_NONE, 1,
931 		G_TYPE_ERROR);
932 
933 	signals[BACKEND_ERROR] = g_signal_new (
934 		"backend-error",
935 		G_OBJECT_CLASS_TYPE (class),
936 		G_SIGNAL_RUN_FIRST,
937 		G_STRUCT_OFFSET (EClientClass, backend_error),
938 		NULL, NULL, NULL,
939 		G_TYPE_NONE, 1,
940 		G_TYPE_STRING);
941 
942 	signals[BACKEND_DIED] = g_signal_new (
943 		"backend-died",
944 		G_OBJECT_CLASS_TYPE (class),
945 		G_SIGNAL_RUN_LAST,
946 		G_STRUCT_OFFSET (EClientClass, backend_died),
947 		NULL, NULL, NULL,
948 		G_TYPE_NONE, 0);
949 
950 	signals[BACKEND_PROPERTY_CHANGED] = g_signal_new (
951 		"backend-property-changed",
952 		G_OBJECT_CLASS_TYPE (class),
953 		G_SIGNAL_RUN_LAST,
954 		G_STRUCT_OFFSET (EClientClass, backend_property_changed),
955 		NULL, NULL, NULL,
956 		G_TYPE_NONE, 2,
957 		G_TYPE_STRING,
958 		G_TYPE_STRING);
959 }
960 
961 static void
e_client_init(EClient * client)962 e_client_init (EClient *client)
963 {
964 	client->priv = e_client_get_instance_private (client);
965 
966 	client->priv->readonly = FALSE;
967 	client->priv->main_context = g_main_context_ref_thread_default ();
968 
969 	g_rec_mutex_init (&client->priv->prop_mutex);
970 }
971 
972 /**
973  * e_client_get_source:
974  * @client: an #EClient
975  *
976  * Get the #ESource that this client has assigned.
977  *
978  * Returns: (transfer none): The source.
979  *
980  * Since: 3.2
981  **/
982 ESource *
e_client_get_source(EClient * client)983 e_client_get_source (EClient *client)
984 {
985 	g_return_val_if_fail (E_IS_CLIENT (client), NULL);
986 
987 	return client->priv->source;
988 }
989 
990 static void
client_ensure_capabilities(EClient * client)991 client_ensure_capabilities (EClient *client)
992 {
993 	gchar *capabilities = NULL;
994 
995 	g_return_if_fail (E_IS_CLIENT (client));
996 
997 	if (client->priv->capabilities != NULL)
998 		return;
999 
1000 	/* Despite appearances this function does not actually block. */
1001 	e_client_get_backend_property_sync (
1002 		client, CLIENT_BACKEND_PROPERTY_CAPABILITIES,
1003 		&capabilities, NULL, NULL);
1004 	e_client_set_capabilities (client, capabilities);
1005 	g_free (capabilities);
1006 }
1007 
1008 /**
1009  * e_client_get_capabilities:
1010  * @client: an #EClient
1011  *
1012  * Get list of strings with capabilities advertised by a backend.
1013  * This list, together with inner strings, is owned by the @client.
1014  * To check for individual capabilities use e_client_check_capability().
1015  *
1016  * Returns: (element-type utf8) (transfer none): #GSList of const strings
1017  *          of capabilities
1018  *
1019  * Since: 3.2
1020  **/
1021 const GSList *
e_client_get_capabilities(EClient * client)1022 e_client_get_capabilities (EClient *client)
1023 {
1024 	g_return_val_if_fail (E_IS_CLIENT (client), NULL);
1025 
1026 	client_ensure_capabilities (client);
1027 
1028 	return client->priv->capabilities;
1029 }
1030 
1031 /**
1032  * e_client_ref_main_context:
1033  * @client: an #EClient
1034  *
1035  * Returns the #GMainContext on which event sources for @client are to
1036  * be attached.
1037  *
1038  * The returned #GMainContext is referenced for thread-safety and must be
1039  * unreferenced with g_main_context_unref() when finished with it.
1040  *
1041  * Returns: (transfer full): a #GMainContext
1042  *
1043  * Since: 3.8
1044  **/
1045 GMainContext *
e_client_ref_main_context(EClient * client)1046 e_client_ref_main_context (EClient *client)
1047 {
1048 	g_return_val_if_fail (E_IS_CLIENT (client), NULL);
1049 
1050 	return g_main_context_ref (client->priv->main_context);
1051 }
1052 
1053 /**
1054  * e_client_check_capability:
1055  * @client: an #EClient
1056  * @capability: a capability
1057  *
1058  * Check if backend supports particular capability.
1059  * To get all capabilities use e_client_get_capabilities().
1060  *
1061  * Returns: #GSList of const strings of capabilities
1062  *
1063  * Since: 3.2
1064  **/
1065 gboolean
e_client_check_capability(EClient * client,const gchar * capability)1066 e_client_check_capability (EClient *client,
1067                            const gchar *capability)
1068 {
1069 	GSList *iter;
1070 
1071 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1072 	g_return_val_if_fail (capability, FALSE);
1073 
1074 	g_rec_mutex_lock (&client->priv->prop_mutex);
1075 
1076 	client_ensure_capabilities (client);
1077 
1078 	for (iter = client->priv->capabilities; iter; iter = g_slist_next (iter)) {
1079 		const gchar *cap = iter->data;
1080 
1081 		if (cap && g_ascii_strcasecmp (cap, capability) == 0) {
1082 			g_rec_mutex_unlock (&client->priv->prop_mutex);
1083 			return TRUE;
1084 		}
1085 	}
1086 
1087 	g_rec_mutex_unlock (&client->priv->prop_mutex);
1088 
1089 	return FALSE;
1090 }
1091 
1092 /**
1093  * e_client_check_refresh_supported:
1094  * @client: A client.
1095  *
1096  * Checks whether a client supports explicit refreshing
1097  * (see e_client_refresh()).
1098  *
1099  * Returns: TRUE if the client supports refreshing, FALSE otherwise.
1100  *
1101  * Since: 3.2
1102  **/
1103 gboolean
e_client_check_refresh_supported(EClient * client)1104 e_client_check_refresh_supported (EClient *client)
1105 {
1106 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1107 
1108 	return e_client_check_capability (client, "refresh-supported");
1109 }
1110 
1111 /* capabilities - comma-separated list of capabilities; can be NULL to unset */
1112 void
e_client_set_capabilities(EClient * client,const gchar * capabilities)1113 e_client_set_capabilities (EClient *client,
1114                            const gchar *capabilities)
1115 {
1116 	g_return_if_fail (E_IS_CLIENT (client));
1117 
1118 	g_rec_mutex_lock (&client->priv->prop_mutex);
1119 
1120 	g_slist_foreach (client->priv->capabilities, (GFunc) g_free, NULL);
1121 	g_slist_free (client->priv->capabilities);
1122 	client->priv->capabilities = e_client_util_parse_comma_strings (capabilities);
1123 
1124 	g_rec_mutex_unlock (&client->priv->prop_mutex);
1125 
1126 	g_object_notify (G_OBJECT (client), "capabilities");
1127 }
1128 
1129 /**
1130  * e_client_is_readonly:
1131  * @client: an #EClient
1132  *
1133  * Check if this @client is read-only.
1134  *
1135  * Returns: %TRUE if this @client is read-only, otherwise %FALSE.
1136  *
1137  * Since: 3.2
1138  **/
1139 gboolean
e_client_is_readonly(EClient * client)1140 e_client_is_readonly (EClient *client)
1141 {
1142 	g_return_val_if_fail (E_IS_CLIENT (client), TRUE);
1143 
1144 	return client->priv->readonly;
1145 }
1146 
1147 void
e_client_set_readonly(EClient * client,gboolean readonly)1148 e_client_set_readonly (EClient *client,
1149                        gboolean readonly)
1150 {
1151 	g_return_if_fail (E_IS_CLIENT (client));
1152 
1153 	g_rec_mutex_lock (&client->priv->prop_mutex);
1154 	if (client->priv->readonly == readonly) {
1155 		g_rec_mutex_unlock (&client->priv->prop_mutex);
1156 		return;
1157 	}
1158 
1159 	client->priv->readonly = readonly;
1160 
1161 	g_rec_mutex_unlock (&client->priv->prop_mutex);
1162 
1163 	g_object_notify (G_OBJECT (client), "readonly");
1164 }
1165 
1166 /**
1167  * e_client_is_online:
1168  * @client: an #EClient
1169  *
1170  * Check if this @client is connected.
1171  *
1172  * Returns: %TRUE if this @client is connected, otherwise %FALSE.
1173  *
1174  * Since: 3.2
1175  **/
1176 gboolean
e_client_is_online(EClient * client)1177 e_client_is_online (EClient *client)
1178 {
1179 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1180 
1181 	return client->priv->online;
1182 }
1183 
1184 void
e_client_set_online(EClient * client,gboolean is_online)1185 e_client_set_online (EClient *client,
1186                      gboolean is_online)
1187 {
1188 	g_return_if_fail (E_IS_CLIENT (client));
1189 
1190 	/* newly connected/disconnected => make sure capabilities will be correct */
1191 	e_client_set_capabilities (client, NULL);
1192 
1193 	g_rec_mutex_lock (&client->priv->prop_mutex);
1194 	if (client->priv->online == is_online) {
1195 		g_rec_mutex_unlock (&client->priv->prop_mutex);
1196 		return;
1197 	}
1198 
1199 	client->priv->online = is_online;
1200 
1201 	g_rec_mutex_unlock (&client->priv->prop_mutex);
1202 
1203 	g_object_notify (G_OBJECT (client), "online");
1204 }
1205 
1206 /**
1207  * e_client_is_opened:
1208  * @client: an #EClient
1209  *
1210  * Check if this @client is fully opened. This includes
1211  * everything from e_client_open() call up to the authentication,
1212  * if required by a backend. Client cannot do any other operation
1213  * during the opening phase except of authenticate or cancel it.
1214  * Every other operation results in an %E_CLIENT_ERROR_BUSY error.
1215  *
1216  * Returns: always %TRUE
1217  *
1218  * Since: 3.2
1219  *
1220  * Deprecated: 3.8: Clients don't need to care if they're fully opened
1221  *                  anymore.  This function always returns %TRUE.
1222  **/
1223 gboolean
e_client_is_opened(EClient * client)1224 e_client_is_opened (EClient *client)
1225 {
1226 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1227 
1228 	return TRUE;
1229 }
1230 
1231 /**
1232  * e_client_cancel_all:
1233  * @client: an #EClient
1234  *
1235  * Cancels all pending operations started on @client.
1236  *
1237  * Since: 3.2
1238  *
1239  * Deprecated: 3.8: The function no longer does anything.
1240  **/
1241 void
e_client_cancel_all(EClient * client)1242 e_client_cancel_all (EClient *client)
1243 {
1244 	/* Do nothing. */
1245 }
1246 
1247 /**
1248  * e_client_retrieve_capabilities:
1249  * @client: an #EClient
1250  * @cancellable: a #GCancellable; can be %NULL
1251  * @callback: callback to call when a result is ready
1252  * @user_data: user data for the @callback
1253  *
1254  * Initiates retrieval of capabilities on the @client. This is usually
1255  * required only once, after the @client is opened. The returned value
1256  * is cached and any subsequent call of e_client_get_capabilities() and
1257  * e_client_check_capability() is using the cached value.
1258  * The call is finished by e_client_retrieve_capabilities_finish()
1259  * from the @callback.
1260  *
1261  * Since: 3.2
1262  *
1263  * Deprecated: 3.8: Use e_client_get_capabilities() instead.
1264  **/
1265 void
e_client_retrieve_capabilities(EClient * client,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1266 e_client_retrieve_capabilities (EClient *client,
1267                                 GCancellable *cancellable,
1268                                 GAsyncReadyCallback callback,
1269                                 gpointer user_data)
1270 {
1271 	EClientClass *class;
1272 
1273 	g_return_if_fail (E_IS_CLIENT (client));
1274 	g_return_if_fail (callback != NULL);
1275 
1276 	class = E_CLIENT_GET_CLASS (client);
1277 	g_return_if_fail (class != NULL);
1278 	g_return_if_fail (class->retrieve_capabilities != NULL);
1279 
1280 	class->retrieve_capabilities (client, cancellable, callback, user_data);
1281 }
1282 
1283 /**
1284  * e_client_retrieve_capabilities_finish:
1285  * @client: an #EClient
1286  * @result: a #GAsyncResult
1287  * @capabilities: (out): Comma-separated list of capabilities of the @client
1288  * @error: a #GError to set an error, if any
1289  *
1290  * Finishes previous call of e_client_retrieve_capabilities().
1291  * Returned value of @capabilities should be freed with g_free(),
1292  * when no longer needed.
1293  *
1294  * Returns: %TRUE if successful, %FALSE otherwise.
1295  *
1296  * Since: 3.2
1297  *
1298  * Deprecated: 3.8: Use e_client_get_capabilities() instead.
1299  **/
1300 gboolean
e_client_retrieve_capabilities_finish(EClient * client,GAsyncResult * result,gchar ** capabilities,GError ** error)1301 e_client_retrieve_capabilities_finish (EClient *client,
1302                                        GAsyncResult *result,
1303                                        gchar **capabilities,
1304                                        GError **error)
1305 {
1306 	EClientClass *class;
1307 	gboolean res;
1308 
1309 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1310 	g_return_val_if_fail (capabilities != NULL, FALSE);
1311 
1312 	class = E_CLIENT_GET_CLASS (client);
1313 	g_return_val_if_fail (class != NULL, FALSE);
1314 	g_return_val_if_fail (class->retrieve_capabilities_finish != NULL, FALSE);
1315 
1316 	*capabilities = NULL;
1317 	res = class->retrieve_capabilities_finish (
1318 		client, result, capabilities, error);
1319 
1320 	e_client_set_capabilities (client, res ? *capabilities : NULL);
1321 
1322 	return res;
1323 }
1324 
1325 /**
1326  * e_client_retrieve_capabilities_sync:
1327  * @client: an #EClient
1328  * @capabilities: (out): Comma-separated list of capabilities of the @client
1329  * @cancellable: a #GCancellable; can be %NULL
1330  * @error: a #GError to set an error, if any
1331  *
1332  * Initiates retrieval of capabilities on the @client. This is usually
1333  * required only once, after the @client is opened. The returned value
1334  * is cached and any subsequent call of e_client_get_capabilities() and
1335  * e_client_check_capability() is using the cached value. Returned value
1336  * of @capabilities should be freed with g_free(), when no longer needed.
1337  *
1338  * Returns: %TRUE if successful, %FALSE otherwise.
1339  *
1340  * Since: 3.2
1341  *
1342  * Deprecated: 3.8: Use e_client_get_capabilities() instead.
1343  **/
1344 gboolean
e_client_retrieve_capabilities_sync(EClient * client,gchar ** capabilities,GCancellable * cancellable,GError ** error)1345 e_client_retrieve_capabilities_sync (EClient *client,
1346                                      gchar **capabilities,
1347                                      GCancellable *cancellable,
1348                                      GError **error)
1349 {
1350 	EClientClass *class;
1351 	gboolean res = FALSE;
1352 
1353 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1354 	g_return_val_if_fail (capabilities != NULL, FALSE);
1355 
1356 	class = E_CLIENT_GET_CLASS (client);
1357 	g_return_val_if_fail (class != NULL, FALSE);
1358 	g_return_val_if_fail (class->retrieve_capabilities_sync != NULL, FALSE);
1359 
1360 	*capabilities = NULL;
1361 	res = class->retrieve_capabilities_sync (
1362 		client, capabilities, cancellable, error);
1363 
1364 	e_client_set_capabilities (client, res ? *capabilities : NULL);
1365 
1366 	return res;
1367 }
1368 
1369 /**
1370  * e_client_get_backend_property:
1371  * @client: an #EClient
1372  * @prop_name: property name, whose value to retrieve; cannot be %NULL
1373  * @cancellable: a #GCancellable; can be %NULL
1374  * @callback: callback to call when a result is ready
1375  * @user_data: user data for the @callback
1376  *
1377  * Queries @client's backend for a property of name @prop_name.
1378  * The call is finished by e_client_get_backend_property_finish()
1379  * from the @callback.
1380  *
1381  * Since: 3.2
1382  **/
1383 void
e_client_get_backend_property(EClient * client,const gchar * prop_name,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1384 e_client_get_backend_property (EClient *client,
1385                                const gchar *prop_name,
1386                                GCancellable *cancellable,
1387                                GAsyncReadyCallback callback,
1388                                gpointer user_data)
1389 {
1390 	EClientClass *class;
1391 
1392 	g_return_if_fail (callback != NULL);
1393 	g_return_if_fail (E_IS_CLIENT (client));
1394 	g_return_if_fail (prop_name != NULL);
1395 
1396 	class = E_CLIENT_GET_CLASS (client);
1397 	g_return_if_fail (class != NULL);
1398 	g_return_if_fail (class->get_backend_property != NULL);
1399 
1400 	class->get_backend_property (
1401 		client, prop_name, cancellable, callback, user_data);
1402 }
1403 
1404 /**
1405  * e_client_get_backend_property_finish:
1406  * @client: an #EClient
1407  * @result: a #GAsyncResult
1408  * @prop_value: (out): Retrieved backend property value; cannot be %NULL
1409  * @error: a #GError to set an error, if any
1410  *
1411  * Finishes previous call of e_client_get_backend_property().
1412  *
1413  * Returns: %TRUE if successful, %FALSE otherwise.
1414  *
1415  * Since: 3.2
1416  **/
1417 gboolean
e_client_get_backend_property_finish(EClient * client,GAsyncResult * result,gchar ** prop_value,GError ** error)1418 e_client_get_backend_property_finish (EClient *client,
1419                                       GAsyncResult *result,
1420                                       gchar **prop_value,
1421                                       GError **error)
1422 {
1423 	EClientClass *class;
1424 
1425 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1426 	g_return_val_if_fail (prop_value != NULL, FALSE);
1427 
1428 	class = E_CLIENT_GET_CLASS (client);
1429 	g_return_val_if_fail (class != NULL, FALSE);
1430 	g_return_val_if_fail (class->get_backend_property_finish != NULL, FALSE);
1431 
1432 	return class->get_backend_property_finish (
1433 		client, result, prop_value, error);
1434 }
1435 
1436 /**
1437  * e_client_get_backend_property_sync:
1438  * @client: an #EClient
1439  * @prop_name: property name, whose value to retrieve; cannot be %NULL
1440  * @prop_value: (out): Retrieved backend property value; cannot be %NULL
1441  * @cancellable: a #GCancellable; can be %NULL
1442  * @error: a #GError to set an error, if any
1443  *
1444  * Queries @client's backend for a property of name @prop_name.
1445  *
1446  * Returns: %TRUE if successful, %FALSE otherwise.
1447  *
1448  * Since: 3.2
1449  **/
1450 gboolean
e_client_get_backend_property_sync(EClient * client,const gchar * prop_name,gchar ** prop_value,GCancellable * cancellable,GError ** error)1451 e_client_get_backend_property_sync (EClient *client,
1452                                     const gchar *prop_name,
1453                                     gchar **prop_value,
1454                                     GCancellable *cancellable,
1455                                     GError **error)
1456 {
1457 	EClientClass *class;
1458 
1459 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1460 	g_return_val_if_fail (prop_name != NULL, FALSE);
1461 	g_return_val_if_fail (prop_value != NULL, FALSE);
1462 
1463 	class = E_CLIENT_GET_CLASS (client);
1464 	g_return_val_if_fail (class != NULL, FALSE);
1465 	g_return_val_if_fail (class->get_backend_property_sync != NULL, FALSE);
1466 
1467 	return class->get_backend_property_sync (
1468 		client, prop_name, prop_value, cancellable, error);
1469 }
1470 
1471 /**
1472  * e_client_set_backend_property:
1473  * @client: an #EClient
1474  * @prop_name: property name, whose value to change; cannot be %NULL
1475  * @prop_value: property value, to set; cannot be %NULL
1476  * @cancellable: a #GCancellable; can be %NULL
1477  * @callback: callback to call when a result is ready
1478  * @user_data: user data for the @callback
1479  *
1480  * Sets @client's backend property of name @prop_name
1481  * to value @prop_value. The call is finished
1482  * by e_client_set_backend_property_finish() from the @callback.
1483  *
1484  * Since: 3.2
1485  *
1486  * Deprecated: 3.8: Clients cannot set backend properties.  Any attempt
1487  *                  will fail with an %E_CLIENT_ERROR_NOT_SUPPORTED error.
1488  **/
1489 void
e_client_set_backend_property(EClient * client,const gchar * prop_name,const gchar * prop_value,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1490 e_client_set_backend_property (EClient *client,
1491                                const gchar *prop_name,
1492                                const gchar *prop_value,
1493                                GCancellable *cancellable,
1494                                GAsyncReadyCallback callback,
1495                                gpointer user_data)
1496 {
1497 	EClientClass *class;
1498 
1499 	g_return_if_fail (callback != NULL);
1500 	g_return_if_fail (E_IS_CLIENT (client));
1501 	g_return_if_fail (prop_name != NULL);
1502 	g_return_if_fail (prop_value != NULL);
1503 
1504 	class = E_CLIENT_GET_CLASS (client);
1505 	g_return_if_fail (class != NULL);
1506 	g_return_if_fail (class->set_backend_property != NULL);
1507 
1508 	class->set_backend_property (
1509 		client, prop_name, prop_value,
1510 		cancellable, callback, user_data);
1511 }
1512 
1513 /**
1514  * e_client_set_backend_property_finish:
1515  * @client: an #EClient
1516  * @result: a #GAsyncResult
1517  * @error: a #GError to set an error, if any
1518  *
1519  * Finishes previous call of e_client_set_backend_property().
1520  *
1521  * Returns: %TRUE if successful, %FALSE otherwise.
1522  *
1523  * Since: 3.2
1524  *
1525  * Deprecated: 3.8: Clients cannot set backend properties.  Any attempt
1526  *                  will fail with an %E_CLIENT_ERROR_NOT_SUPPORTED error.
1527  **/
1528 gboolean
e_client_set_backend_property_finish(EClient * client,GAsyncResult * result,GError ** error)1529 e_client_set_backend_property_finish (EClient *client,
1530                                       GAsyncResult *result,
1531                                       GError **error)
1532 {
1533 	EClientClass *class;
1534 
1535 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1536 
1537 	class = E_CLIENT_GET_CLASS (client);
1538 	g_return_val_if_fail (class != NULL, FALSE);
1539 	g_return_val_if_fail (class->set_backend_property_finish != NULL, FALSE);
1540 
1541 	return class->set_backend_property_finish (client, result, error);
1542 }
1543 
1544 /**
1545  * e_client_set_backend_property_sync:
1546  * @client: an #EClient
1547  * @prop_name: property name, whose value to change; cannot be %NULL
1548  * @prop_value: property value, to set; cannot be %NULL
1549  * @cancellable: a #GCancellable; can be %NULL
1550  * @error: a #GError to set an error, if any
1551  *
1552  * Sets @client's backend property of name @prop_name
1553  * to value @prop_value.
1554  *
1555  * Returns: %TRUE if successful, %FALSE otherwise.
1556  *
1557  * Since: 3.2
1558  *
1559  * Deprecated: 3.8: Clients cannot set backend properties.  Any attempt
1560  *                  will fail with an %E_CLIENT_ERROR_NOT_SUPPORTED error.
1561  **/
1562 gboolean
e_client_set_backend_property_sync(EClient * client,const gchar * prop_name,const gchar * prop_value,GCancellable * cancellable,GError ** error)1563 e_client_set_backend_property_sync (EClient *client,
1564                                     const gchar *prop_name,
1565                                     const gchar *prop_value,
1566                                     GCancellable *cancellable,
1567                                     GError **error)
1568 {
1569 	EClientClass *class;
1570 
1571 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1572 	g_return_val_if_fail (prop_name != NULL, FALSE);
1573 	g_return_val_if_fail (prop_value != NULL, FALSE);
1574 
1575 	class = E_CLIENT_GET_CLASS (client);
1576 	g_return_val_if_fail (class != NULL, FALSE);
1577 	g_return_val_if_fail (class->set_backend_property_sync != NULL, FALSE);
1578 
1579 	return class->set_backend_property_sync (
1580 		client, prop_name, prop_value, cancellable, error);
1581 }
1582 
1583 /**
1584  * e_client_open:
1585  * @client: an #EClient
1586  * @only_if_exists: this parameter is not used anymore
1587  * @cancellable: a #GCancellable; can be %NULL
1588  * @callback: callback to call when a result is ready
1589  * @user_data: user data for the @callback
1590  *
1591  * Opens the @client, making it ready for queries and other operations.
1592  * The call is finished by e_client_open_finish() from the @callback.
1593  *
1594  * Since: 3.2
1595  *
1596  * Deprecated: 3.8: Use e_book_client_connect() and
1597  *                  e_book_client_connect_finish() or
1598  *                  e_cal_client_connect() and
1599  *                  e_cal_client_connect_finish() instead.
1600  **/
1601 void
e_client_open(EClient * client,gboolean only_if_exists,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1602 e_client_open (EClient *client,
1603                gboolean only_if_exists,
1604                GCancellable *cancellable,
1605                GAsyncReadyCallback callback,
1606                gpointer user_data)
1607 {
1608 	EClientClass *class;
1609 
1610 	g_return_if_fail (callback != NULL);
1611 	g_return_if_fail (E_IS_CLIENT (client));
1612 
1613 	class = E_CLIENT_GET_CLASS (client);
1614 	g_return_if_fail (class != NULL);
1615 	g_return_if_fail (class->open != NULL);
1616 
1617 	class->open (client, only_if_exists, cancellable, callback, user_data);
1618 }
1619 
1620 /**
1621  * e_client_open_finish:
1622  * @client: an #EClient
1623  * @result: a #GAsyncResult
1624  * @error: a #GError to set an error, if any
1625  *
1626  * Finishes previous call of e_client_open().
1627  *
1628  * Returns: %TRUE if successful, %FALSE otherwise.
1629  *
1630  * Since: 3.2
1631  *
1632  * Deprecated: 3.8: Use e_book_client_connect() and
1633  *                  e_book_client_connect_finish() or
1634  *                  e_cal_client_connect() and
1635  *                  e_cal_client_connect_finish() instead.
1636  **/
1637 gboolean
e_client_open_finish(EClient * client,GAsyncResult * result,GError ** error)1638 e_client_open_finish (EClient *client,
1639                       GAsyncResult *result,
1640                       GError **error)
1641 {
1642 	EClientClass *class;
1643 
1644 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1645 
1646 	class = E_CLIENT_GET_CLASS (client);
1647 	g_return_val_if_fail (class != NULL, FALSE);
1648 	g_return_val_if_fail (class->open_finish != NULL, FALSE);
1649 
1650 	return class->open_finish (client, result, error);
1651 }
1652 
1653 /**
1654  * e_client_open_sync:
1655  * @client: an #EClient
1656  * @only_if_exists: this parameter is not used anymore
1657  * @cancellable: a #GCancellable; can be %NULL
1658  * @error: a #GError to set an error, if any
1659  *
1660  * Opens the @client, making it ready for queries and other operations.
1661  *
1662  * Returns: %TRUE if successful, %FALSE otherwise.
1663  *
1664  * Since: 3.2
1665  *
1666  * Deprecated: 3.8: Use e_book_client_connect_sync() or
1667  *                  e_cal_client_connect_sync() instead.
1668  **/
1669 gboolean
e_client_open_sync(EClient * client,gboolean only_if_exists,GCancellable * cancellable,GError ** error)1670 e_client_open_sync (EClient *client,
1671                     gboolean only_if_exists,
1672                     GCancellable *cancellable,
1673                     GError **error)
1674 {
1675 	EClientClass *class;
1676 
1677 	class = E_CLIENT_GET_CLASS (client);
1678 	g_return_val_if_fail (class != NULL, FALSE);
1679 	g_return_val_if_fail (class->open_sync != NULL, FALSE);
1680 
1681 	return class->open_sync (client, only_if_exists, cancellable, error);
1682 }
1683 
1684 /**
1685  * e_client_remove:
1686  * @client: an #EClient
1687  * @cancellable: a #GCancellable; can be %NULL
1688  * @callback: callback to call when a result is ready
1689  * @user_data: user data for the @callback
1690  *
1691  * Removes the backing data for this #EClient. For example, with the file
1692  * backend this deletes the database file. You cannot get it back!
1693  * The call is finished by e_client_remove_finish() from the @callback.
1694  *
1695  * Since: 3.2
1696  *
1697  * Deprecated: 3.6: Use e_source_remove() instead.
1698  **/
1699 void
e_client_remove(EClient * client,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1700 e_client_remove (EClient *client,
1701                  GCancellable *cancellable,
1702                  GAsyncReadyCallback callback,
1703                  gpointer user_data)
1704 {
1705 	EClientClass *class;
1706 
1707 	g_return_if_fail (E_IS_CLIENT (client));
1708 	g_return_if_fail (callback != NULL);
1709 
1710 	class = E_CLIENT_GET_CLASS (client);
1711 	g_return_if_fail (class != NULL);
1712 	g_return_if_fail (class->remove != NULL);
1713 
1714 	class->remove (client, cancellable, callback, user_data);
1715 }
1716 
1717 /**
1718  * e_client_remove_finish:
1719  * @client: an #EClient
1720  * @result: a #GAsyncResult
1721  * @error: a #GError to set an error, if any
1722  *
1723  * Finishes previous call of e_client_remove().
1724  *
1725  * Returns: %TRUE if successful, %FALSE otherwise.
1726  *
1727  * Since: 3.2
1728  *
1729  * Deprecated: 3.6: Use e_source_remove_finish() instead.
1730  **/
1731 gboolean
e_client_remove_finish(EClient * client,GAsyncResult * result,GError ** error)1732 e_client_remove_finish (EClient *client,
1733                         GAsyncResult *result,
1734                         GError **error)
1735 {
1736 	EClientClass *class;
1737 
1738 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1739 
1740 	class = E_CLIENT_GET_CLASS (client);
1741 	g_return_val_if_fail (class != NULL, FALSE);
1742 	g_return_val_if_fail (class->remove_finish != NULL, FALSE);
1743 
1744 	return class->remove_finish (client, result, error);
1745 }
1746 
1747 /**
1748  * e_client_remove_sync:
1749  * @client: an #EClient
1750  * @cancellable: a #GCancellable; can be %NULL
1751  * @error: a #GError to set an error, if any
1752  *
1753  * Removes the backing data for this #EClient. For example, with the file
1754  * backend this deletes the database file. You cannot get it back!
1755  *
1756  * Returns: %TRUE if successful, %FALSE otherwise.
1757  *
1758  * Since: 3.2
1759  *
1760  * Deprecated: 3.6: Use e_source_remove_sync() instead.
1761  **/
1762 gboolean
e_client_remove_sync(EClient * client,GCancellable * cancellable,GError ** error)1763 e_client_remove_sync (EClient *client,
1764                       GCancellable *cancellable,
1765                       GError **error)
1766 {
1767 	EClientClass *class;
1768 
1769 	class = E_CLIENT_GET_CLASS (client);
1770 	g_return_val_if_fail (class != NULL, FALSE);
1771 	g_return_val_if_fail (class->remove_sync != NULL, FALSE);
1772 
1773 	return class->remove_sync (client, cancellable, error);
1774 }
1775 
1776 /**
1777  * e_client_refresh:
1778  * @client: an #EClient
1779  * @cancellable: a #GCancellable; can be %NULL
1780  * @callback: callback to call when a result is ready
1781  * @user_data: user data for the @callback
1782  *
1783  * Initiates refresh on the @client. Finishing the method doesn't mean
1784  * that the refresh is done, backend only notifies whether it started
1785  * refreshing or not. Use e_client_check_refresh_supported() to check
1786  * whether the backend supports this method.
1787  * The call is finished by e_client_refresh_finish() from the @callback.
1788  *
1789  * Since: 3.2
1790  **/
1791 void
e_client_refresh(EClient * client,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1792 e_client_refresh (EClient *client,
1793                   GCancellable *cancellable,
1794                   GAsyncReadyCallback callback,
1795                   gpointer user_data)
1796 {
1797 	EClientClass *class;
1798 
1799 	g_return_if_fail (E_IS_CLIENT (client));
1800 	g_return_if_fail (callback != NULL);
1801 
1802 	class = E_CLIENT_GET_CLASS (client);
1803 	g_return_if_fail (class != NULL);
1804 	g_return_if_fail (class->refresh != NULL);
1805 
1806 	class->refresh (client, cancellable, callback, user_data);
1807 }
1808 
1809 /**
1810  * e_client_refresh_finish:
1811  * @client: an #EClient
1812  * @result: a #GAsyncResult
1813  * @error: a #GError to set an error, if any
1814  *
1815  * Finishes previous call of e_client_refresh().
1816  *
1817  * Returns: %TRUE if successful, %FALSE otherwise.
1818  *
1819  * Since: 3.2
1820  **/
1821 gboolean
e_client_refresh_finish(EClient * client,GAsyncResult * result,GError ** error)1822 e_client_refresh_finish (EClient *client,
1823                          GAsyncResult *result,
1824                          GError **error)
1825 {
1826 	EClientClass *class;
1827 
1828 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1829 
1830 	class = E_CLIENT_GET_CLASS (client);
1831 	g_return_val_if_fail (class != NULL, FALSE);
1832 	g_return_val_if_fail (class->refresh_finish != NULL, FALSE);
1833 
1834 	return class->refresh_finish (client, result, error);
1835 }
1836 
1837 /**
1838  * e_client_refresh_sync:
1839  * @client: an #EClient
1840  * @cancellable: a #GCancellable; can be %NULL
1841  * @error: a #GError to set an error, if any
1842  *
1843  * Initiates refresh on the @client. Finishing the method doesn't mean
1844  * that the refresh is done, backend only notifies whether it started
1845  * refreshing or not. Use e_client_check_refresh_supported() to check
1846  * whether the backend supports this method.
1847  *
1848  * Returns: %TRUE if successful, %FALSE otherwise.
1849  *
1850  * Since: 3.2
1851  **/
1852 gboolean
e_client_refresh_sync(EClient * client,GCancellable * cancellable,GError ** error)1853 e_client_refresh_sync (EClient *client,
1854                        GCancellable *cancellable,
1855                        GError **error)
1856 {
1857 	EClientClass *class;
1858 
1859 	class = E_CLIENT_GET_CLASS (client);
1860 	g_return_val_if_fail (class != NULL, FALSE);
1861 	g_return_val_if_fail (class->refresh_sync != NULL, FALSE);
1862 
1863 	return class->refresh_sync (client, cancellable, error);
1864 }
1865 
1866 static void
client_wait_for_connected_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)1867 client_wait_for_connected_thread (GTask *task,
1868 				  gpointer source_object,
1869 				  gpointer task_data,
1870 				  GCancellable *cancellable)
1871 {
1872 	guint32 timeout_seconds;
1873 	gboolean success;
1874 	GError *local_error = NULL;
1875 
1876 	timeout_seconds = GPOINTER_TO_UINT (task_data);
1877 	success = e_client_wait_for_connected_sync (E_CLIENT (source_object), timeout_seconds, cancellable, &local_error);
1878 
1879 	if (local_error != NULL) {
1880 		g_task_return_error (task, local_error);
1881 	} else {
1882 		g_task_return_boolean (task, success);
1883 	}
1884 }
1885 
1886 /**
1887  * e_client_wait_for_connected:
1888  * @client: an #EClient
1889  * @timeout_seconds: a timeout for the wait, in seconds
1890  * @cancellable: a #GCancellable; or %NULL
1891  * @callback: callback to call when a result is ready
1892  * @user_data: user data for the @callback
1893  *
1894  * Asynchronously waits until the @client is connected (according
1895  * to @ESource::connection-status property), but not longer than @timeout_seconds.
1896  *
1897  * The call is finished by e_client_wait_for_connected_finish() from
1898  * the @callback.
1899  *
1900  * Since: 3.16
1901  **/
1902 void
e_client_wait_for_connected(EClient * client,guint32 timeout_seconds,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1903 e_client_wait_for_connected (EClient *client,
1904 			     guint32 timeout_seconds,
1905 			     GCancellable *cancellable,
1906 			     GAsyncReadyCallback callback,
1907 			     gpointer user_data)
1908 {
1909 	GTask *task;
1910 
1911 	g_return_if_fail (E_IS_CLIENT (client));
1912 
1913 	task = g_task_new (client, cancellable, callback, user_data);
1914 	g_task_set_source_tag (task, e_client_wait_for_connected);
1915 	g_task_set_task_data (task, GUINT_TO_POINTER (timeout_seconds), NULL);
1916 
1917 	g_task_run_in_thread (task, client_wait_for_connected_thread);
1918 
1919 	g_object_unref (task);
1920 }
1921 
1922 /**
1923  * e_client_wait_for_connected_finish:
1924  * @client: an #EClient
1925  * @result: a #GAsyncResult
1926  * @error: a #GError to set an error, or %NULL
1927  *
1928  * Finishes previous call of e_client_wait_for_connected().
1929  *
1930  * Returns: %TRUE if successful, %FALSE otherwise.
1931  *
1932  * Since: 3.16
1933  **/
1934 gboolean
e_client_wait_for_connected_finish(EClient * client,GAsyncResult * result,GError ** error)1935 e_client_wait_for_connected_finish (EClient *client,
1936 				    GAsyncResult *result,
1937 				    GError **error)
1938 {
1939 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
1940 	g_return_val_if_fail (g_task_is_valid (result, client), FALSE);
1941 
1942 	g_return_val_if_fail (
1943 		g_async_result_is_tagged (
1944 		result, e_client_wait_for_connected), FALSE);
1945 
1946 	return g_task_propagate_boolean (G_TASK (result), error);
1947 }
1948 
1949 static void
client_wait_for_connected_cancelled_cb(GCancellable * cancellable,EFlag * flag)1950 client_wait_for_connected_cancelled_cb (GCancellable *cancellable,
1951 					EFlag *flag)
1952 {
1953 	g_return_if_fail (flag != NULL);
1954 
1955 	e_flag_set (flag);
1956 }
1957 
1958 static void
client_wait_for_connected_notify_cb(ESource * source,GParamSpec * param,EFlag * flag)1959 client_wait_for_connected_notify_cb (ESource *source,
1960 				     GParamSpec *param,
1961 				     EFlag *flag)
1962 {
1963 	g_return_if_fail (flag != NULL);
1964 
1965 	if (e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_CONNECTED)
1966 		e_flag_set (flag);
1967 }
1968 
1969 /**
1970  * e_client_wait_for_connected_sync:
1971  * @client: an #EClient
1972  * @timeout_seconds: a timeout for the wait, in seconds
1973  * @cancellable: a #GCancellable; or %NULL
1974  * @error: a #GError to set an error, or %NULL
1975  *
1976  * Synchronously waits until the @client is connected (according
1977  * to @ESource::connection-status property), but not longer than @timeout_seconds.
1978  *
1979  * Note: This also calls e_client_retrieve_properties_sync() on success, to have
1980  *   up-to-date property values on the client side, without a delay due
1981  *   to property change notifcations delivery through D-Bus.
1982  *
1983  * Returns: %TRUE if successful, %FALSE otherwise.
1984  *
1985  * Since: 3.16
1986  **/
1987 gboolean
e_client_wait_for_connected_sync(EClient * client,guint32 timeout_seconds,GCancellable * cancellable,GError ** error)1988 e_client_wait_for_connected_sync (EClient *client,
1989 				  guint32 timeout_seconds,
1990 				  GCancellable *cancellable,
1991 				  GError **error)
1992 {
1993 	ESource *source;
1994 	EFlag *flag;
1995 	gulong cancellable_handler_id = 0, notify_handler_id;
1996 	gint64 end_time;
1997 	gboolean success;
1998 
1999 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
2000 
2001 	end_time = g_get_monotonic_time () + timeout_seconds * G_TIME_SPAN_SECOND;
2002 	flag = e_flag_new ();
2003 
2004 	if (cancellable)
2005 		cancellable_handler_id = g_cancellable_connect (cancellable,
2006 			G_CALLBACK (client_wait_for_connected_cancelled_cb), flag, NULL);
2007 
2008 	source = e_client_get_source (client);
2009 
2010 	notify_handler_id = g_signal_connect (source, "notify::connection-status",
2011 		G_CALLBACK (client_wait_for_connected_notify_cb), flag);
2012 
2013 	while (success = e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_CONNECTED,
2014 	       !success && !g_cancellable_is_cancelled (cancellable)) {
2015 		e_flag_clear (flag);
2016 
2017 		if (timeout_seconds > 0) {
2018 			if (g_get_monotonic_time () > end_time)
2019 				break;
2020 
2021 			e_flag_wait_until (flag, end_time);
2022 		} else {
2023 			e_flag_wait (flag);
2024 		}
2025 	}
2026 
2027 	g_signal_handler_disconnect (source, notify_handler_id);
2028 
2029 	if (cancellable_handler_id > 0 && cancellable)
2030 		g_cancellable_disconnect (cancellable, cancellable_handler_id);
2031 
2032 	e_flag_free (flag);
2033 
2034 	success = e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_CONNECTED;
2035 
2036 	if (!success && !g_cancellable_set_error_if_cancelled (cancellable, error))
2037 		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT, _("Timeout was reached"));
2038 	else if (success)
2039 		success = e_client_retrieve_properties_sync (client, cancellable, error);
2040 
2041 	return success;
2042 }
2043 
2044 /**
2045  * e_client_retrieve_properties_sync:
2046  * @client: an #EClient
2047  * @cancellable: optional #GCancellable object, or %NULL
2048  * @error: return location for a #GError, or %NULL
2049  *
2050  * Retrieves @client properties to match server-side values, without waiting
2051  * for the D-Bus property change notifications delivery.
2052  *
2053  * If an error occurs, the function sets @error and returns %FALSE.
2054  *
2055  * Returns: %TRUE on success, %FALSE on error
2056  *
2057  * Since: 3.16
2058  **/
2059 gboolean
e_client_retrieve_properties_sync(EClient * client,GCancellable * cancellable,GError ** error)2060 e_client_retrieve_properties_sync (EClient *client,
2061 				   GCancellable *cancellable,
2062 				   GError **error)
2063 {
2064 	EClientClass *klass;
2065 
2066 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
2067 
2068 	klass = E_CLIENT_GET_CLASS (client);
2069 	g_return_val_if_fail (klass != NULL, FALSE);
2070 	g_return_val_if_fail (klass->retrieve_properties_sync != NULL, FALSE);
2071 
2072 	return klass->retrieve_properties_sync (client, cancellable, error);
2073 }
2074 
2075 static void
client_retrieve_properties_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)2076 client_retrieve_properties_thread (GTask *task,
2077 				   gpointer source_object,
2078 				   gpointer task_data,
2079 				   GCancellable *cancellable)
2080 {
2081 	gboolean success;
2082 	GError *local_error = NULL;
2083 
2084 	success = e_client_retrieve_properties_sync (E_CLIENT (source_object), cancellable, &local_error);
2085 
2086 	if (local_error != NULL) {
2087 		g_task_return_error (task, local_error);
2088 	} else {
2089 		g_task_return_boolean (task, success);
2090 	}
2091 }
2092 
2093 /**
2094  * e_client_retrieve_properties:
2095  * @client: an #EClient
2096  * @cancellable: optional #GCancellable object, or %NULL
2097  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2098  * @user_data: data to pass to the callback function
2099  *
2100  * Asynchronously retrieves @client properties to match server-side values,
2101  * without waiting for the D-Bus property change notifications delivery.
2102  *
2103  * When the operation is finished, @callback will be called. You can then
2104  * call e_client_retrieve_properties_finish() to get the result of the operation.
2105  *
2106  * Since: 3.16
2107  **/
2108 void
e_client_retrieve_properties(EClient * client,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2109 e_client_retrieve_properties (EClient *client,
2110 			      GCancellable *cancellable,
2111 			      GAsyncReadyCallback callback,
2112 			      gpointer user_data)
2113 {
2114 	GTask *task;
2115 
2116 	g_return_if_fail (E_IS_CLIENT (client));
2117 
2118 	task = g_task_new (client, cancellable, callback, user_data);
2119 	g_task_set_source_tag (task, e_client_retrieve_properties);
2120 
2121 	g_task_run_in_thread (task, client_retrieve_properties_thread);
2122 
2123 	g_object_unref (task);
2124 }
2125 
2126 /**
2127  * e_client_retrieve_properties_finish:
2128  * @client: an #EClient
2129  * @result: a #GAsyncResult
2130  * @error: return location for a #GError, or %NULL
2131  *
2132  * Finishes the operation started with e_client_retrieve_properties().
2133  *
2134  * If an error occurs, the function sets @error and returns %FALSE.
2135  *
2136  * Returns: %TRUE on success, %FALSE on error
2137  *
2138  * Since: 3.16
2139  **/
2140 gboolean
e_client_retrieve_properties_finish(EClient * client,GAsyncResult * result,GError ** error)2141 e_client_retrieve_properties_finish (EClient *client,
2142 				     GAsyncResult *result,
2143 				     GError **error)
2144 {
2145 	g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
2146 	g_return_val_if_fail (g_task_is_valid (result, client), FALSE);
2147 
2148 	g_return_val_if_fail (
2149 		g_async_result_is_tagged (
2150 		result, e_client_retrieve_properties), FALSE);
2151 
2152 	return g_task_propagate_boolean (G_TASK (result), error);
2153 }
2154 
2155 /**
2156  * e_client_util_slist_to_strv:
2157  * @strings: (element-type utf8): a #GSList of strings (const gchar *)
2158  *
2159  * Convert a list of strings into a %NULL-terminated array of strings.
2160  *
2161  * Returns: (transfer full): Newly allocated %NULL-terminated array of strings.
2162  * The returned pointer should be freed with g_strfreev().
2163  *
2164  * Note: Paired function for this is e_client_util_strv_to_slist().
2165  *
2166  * Since: 3.2
2167  *
2168  * Deprecated: 3.8: Use e_util_slist_to_strv() instead.
2169  **/
2170 gchar **
e_client_util_slist_to_strv(const GSList * strings)2171 e_client_util_slist_to_strv (const GSList *strings)
2172 {
2173 	return e_util_slist_to_strv (strings);
2174 }
2175 
2176 /**
2177  * e_client_util_strv_to_slist:
2178  * @strv: a %NULL-terminated array of strings (const gchar *)
2179  *
2180  * Convert a %NULL-terminated array of strings to a list of strings.
2181  *
2182  * Returns: (transfer full) (element-type utf8): Newly allocated #GSList of
2183  * newly allocated strings. The returned pointer should be freed with
2184  * e_client_util_free_string_slist().
2185  *
2186  * Note: Paired function for this is e_client_util_slist_to_strv().
2187  *
2188  * Since: 3.2
2189  *
2190  * Deprecated: 3.8: Use e_util_strv_to_slist() instead.
2191  **/
2192 GSList *
e_client_util_strv_to_slist(const gchar * const * strv)2193 e_client_util_strv_to_slist (const gchar * const *strv)
2194 {
2195 	return e_util_strv_to_slist (strv);
2196 }
2197 
2198 /**
2199  * e_client_util_copy_string_slist:
2200  * @copy_to: (element-type utf8) (nullable) (transfer full): Where to copy; may be %NULL
2201  * @strings: (element-type utf8): #GSList of strings to be copied
2202  *
2203  * Copies the #GSList of strings to the end of @copy_to.
2204  *
2205  * Returns: (transfer full) (element-type utf8): New head of @copy_to.
2206  * The returned pointer can be freed with e_client_util_free_string_slist().
2207  *
2208  * Since: 3.2
2209  *
2210  * Deprecated: 3.8: Use e_util_copy_string_slist() instead.
2211  **/
2212 GSList *
e_client_util_copy_string_slist(GSList * copy_to,const GSList * strings)2213 e_client_util_copy_string_slist (GSList *copy_to,
2214                                  const GSList *strings)
2215 {
2216 	return e_util_copy_string_slist (copy_to, strings);
2217 }
2218 
2219 /**
2220  * e_client_util_copy_object_slist:
2221  * @copy_to: (element-type GObject) (nullable) (transfer full): Where to copy; may be %NULL
2222  * @objects: (element-type GObject): #GSList of #GObject<!-- -->s to be copied
2223  *
2224  * Copies a #GSList of #GObject<!-- -->s to the end of @copy_to.
2225  *
2226  * Returns: (transfer full) (element-type GObject): New head of @copy_to.
2227  * The returned pointer can be freed with e_client_util_free_object_slist().
2228  *
2229  * Since: 3.2
2230  *
2231  * Deprecated: 3.8: Use e_util_copy_object_slist() instead.
2232  **/
2233 GSList *
e_client_util_copy_object_slist(GSList * copy_to,const GSList * objects)2234 e_client_util_copy_object_slist (GSList *copy_to,
2235                                  const GSList *objects)
2236 {
2237 	return e_util_copy_object_slist (copy_to, objects);
2238 }
2239 
2240 /**
2241  * e_client_util_free_string_slist:
2242  * @strings: (element-type utf8): a #GSList of strings (gchar *)
2243  *
2244  * Frees memory previously allocated by e_client_util_strv_to_slist().
2245  *
2246  * Since: 3.2
2247  *
2248  * Deprecated: 3.8: Use g_slist_free_full() instead.
2249  **/
2250 void
e_client_util_free_string_slist(GSList * strings)2251 e_client_util_free_string_slist (GSList *strings)
2252 {
2253 	e_util_free_string_slist (strings);
2254 }
2255 
2256 /**
2257  * e_client_util_free_object_slist:
2258  * @objects: (element-type GObject): a #GSList of #GObject<!-- -->s
2259  *
2260  * Calls g_object_unref() on each member of @objects and then frees @objects
2261  * itself.
2262  *
2263  * Since: 3.2
2264  *
2265  * Deprecated: 3.8: Use g_slist_free_full() instead.
2266  **/
2267 void
e_client_util_free_object_slist(GSList * objects)2268 e_client_util_free_object_slist (GSList *objects)
2269 {
2270 	e_util_free_object_slist (objects);
2271 }
2272 
2273 /**
2274  * e_client_util_parse_comma_strings:
2275  * @strings: string of comma-separated values
2276  *
2277  * Parses comma-separated list of values into #GSList.
2278  *
2279  * Returns: (transfer full) (element-type utf8): Newly allocated #GSList of
2280  * newly allocated strings corresponding to values parsed from @strings.
2281  * Free the returned pointer with e_client_util_free_string_slist().
2282  *
2283  * Since: 3.2
2284  **/
2285 GSList *
e_client_util_parse_comma_strings(const gchar * strings)2286 e_client_util_parse_comma_strings (const gchar *strings)
2287 {
2288 	GSList *strs_slist = NULL;
2289 	gchar **strs_strv = NULL;
2290 	gint ii;
2291 
2292 	if (!strings || !*strings)
2293 		return NULL;
2294 
2295 	strs_strv = g_strsplit (strings, ",", -1);
2296 	g_return_val_if_fail (strs_strv != NULL, NULL);
2297 
2298 	for (ii = 0; strs_strv && strs_strv[ii]; ii++) {
2299 		gchar *str = g_strstrip (strs_strv[ii]);
2300 
2301 		if (str && *str)
2302 			strs_slist = g_slist_prepend (strs_slist, g_strdup (str));
2303 	}
2304 
2305 	g_strfreev (strs_strv);
2306 
2307 	return g_slist_reverse (strs_slist);
2308 }
2309 
2310 /**
2311  * e_client_unwrap_dbus_error:
2312  * @client: an #EClient
2313  * @dbus_error: a #GError returned bu D-Bus
2314  * @out_error: a #GError variable where to store the result
2315  *
2316  * Unwraps D-Bus error to local error. @dbus_error is automatically freed.
2317  * @dbus_erorr and @out_error can point to the same variable.
2318  *
2319  * Since: 3.2
2320  *
2321  * Deprecated: 3.8: Use g_dbus_error_strip_remote_error() instead.
2322  **/
2323 void
e_client_unwrap_dbus_error(EClient * client,GError * dbus_error,GError ** out_error)2324 e_client_unwrap_dbus_error (EClient *client,
2325                             GError *dbus_error,
2326                             GError **out_error)
2327 {
2328 	EClientClass *class;
2329 
2330 	g_return_if_fail (E_IS_CLIENT (client));
2331 
2332 	class = E_CLIENT_GET_CLASS (client);
2333 	g_return_if_fail (class != NULL);
2334 	g_return_if_fail (class->unwrap_dbus_error != NULL);
2335 
2336 	if (!dbus_error || !out_error) {
2337 		if (dbus_error)
2338 			g_error_free (dbus_error);
2339 	} else {
2340 		class->unwrap_dbus_error (client, dbus_error, out_error);
2341 	}
2342 }
2343 
2344 /**
2345  * e_client_util_unwrap_dbus_error:
2346  * @dbus_error: DBus #GError to unwrap
2347  * @client_error: (out): Resulting #GError; can be %NULL
2348  * @known_errors: List of known errors against which try to match
2349  * @known_errors_count: How many items are stored in @known_errors
2350  * @known_errors_domain: Error domain for @known_errors
2351  * @fail_when_none_matched: Whether to fail when none of @known_errors matches
2352  *
2353  * The function takes a @dbus_error and tries to find a match in @known_errors
2354  * for it, if it is a G_IO_ERROR, G_IO_ERROR_DBUS_ERROR. If it is anything else
2355  * then the @dbus_error is moved to @client_error.
2356  *
2357  * The @fail_when_none_matched influences behaviour. If it's %TRUE, and none of
2358  * @known_errors matches, or this is not a G_IO_ERROR_DBUS_ERROR, then %FALSE
2359  * is returned and the @client_error is left without change. Otherwise, the
2360  * @fail_when_none_matched is %FALSE, the error is always processed and will
2361  * result in E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR if none of @known_error
2362  * matches.
2363  *
2364  * Returns: Whether was @dbus_error processed into @client_error.
2365  *
2366  * Note: The @dbus_error is automatically freed if returned %TRUE.
2367  *
2368  * Since: 3.2
2369  *
2370  * Deprecated: 3.8: This function is no longer used.
2371  **/
2372 gboolean
e_client_util_unwrap_dbus_error(GError * dbus_error,GError ** client_error,const EClientErrorsList * known_errors,guint known_errors_count,GQuark known_errors_domain,gboolean fail_when_none_matched)2373 e_client_util_unwrap_dbus_error (GError *dbus_error,
2374                                  GError **client_error,
2375                                  const EClientErrorsList *known_errors,
2376                                  guint known_errors_count,
2377                                  GQuark known_errors_domain,
2378                                  gboolean fail_when_none_matched)
2379 {
2380 	if (!client_error) {
2381 		if (dbus_error)
2382 			g_error_free (dbus_error);
2383 		return TRUE;
2384 	}
2385 
2386 	if (!dbus_error) {
2387 		*client_error = NULL;
2388 		return TRUE;
2389 	}
2390 
2391 	if (dbus_error->domain == known_errors_domain) {
2392 		*client_error = dbus_error;
2393 		return TRUE;
2394 	}
2395 
2396 	if (known_errors) {
2397 		if (g_error_matches (dbus_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR)) {
2398 			gchar *name;
2399 			gint ii;
2400 
2401 			name = g_dbus_error_get_remote_error (dbus_error);
2402 
2403 			for (ii = 0; ii < known_errors_count; ii++) {
2404 				if (g_ascii_strcasecmp (known_errors[ii].name, name) == 0) {
2405 					g_free (name);
2406 
2407 					g_dbus_error_strip_remote_error (dbus_error);
2408 					*client_error = g_error_new_literal (
2409 						known_errors_domain,
2410 						known_errors[ii].err_code,
2411 						dbus_error->message);
2412 					g_error_free (dbus_error);
2413 					return TRUE;
2414 				}
2415 			}
2416 
2417 			g_free (name);
2418 		}
2419 	}
2420 
2421 	if (fail_when_none_matched)
2422 		return FALSE;
2423 
2424 	if (g_error_matches (dbus_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR)) {
2425 		g_dbus_error_strip_remote_error (dbus_error);
2426 		*client_error = g_error_new_literal (
2427 			E_CLIENT_ERROR,
2428 			E_CLIENT_ERROR_OTHER_ERROR,
2429 			dbus_error->message);
2430 		g_error_free (dbus_error);
2431 	} else {
2432 		g_dbus_error_strip_remote_error (dbus_error);
2433 		*client_error = dbus_error;
2434 	}
2435 
2436 	return TRUE;
2437 }
2438 
2439 /**
2440  * e_client_dup_bus_name:
2441  * @client: an #EClient
2442  *
2443  * Returns a D-Bus bus name that will be used to connect the
2444  * client to the backend subprocess.
2445  *
2446  * Returns: a newly-allocated string representing a D-Bus bus
2447  *          name that will be used to connect the client to
2448  *          the backend subprocess. The string should be
2449  *          freed by the caller using g_free().
2450  *
2451  * Since: 3.16
2452  **/
2453 gchar *
e_client_dup_bus_name(EClient * client)2454 e_client_dup_bus_name (EClient *client)
2455 {
2456 	g_return_val_if_fail (E_IS_CLIENT (client), NULL);
2457 
2458 	return g_strdup (client->priv->bus_name);
2459 }
2460 
2461 /**
2462  * e_client_set_bus_name:
2463  * @client: an #EClient
2464  * @bus_name: a string representing a D-Bus bus name
2465  *
2466  * Sets a D-Bus bus name that will be used to connect the client
2467  * to the backend subprocess.
2468  *
2469  * Since: 3.16
2470  **/
2471 void
e_client_set_bus_name(EClient * client,const gchar * bus_name)2472 e_client_set_bus_name (EClient *client,
2473 		       const gchar *bus_name)
2474 {
2475 	g_return_if_fail (E_IS_CLIENT (client));
2476 	g_return_if_fail (client->priv->bus_name == NULL);
2477 	g_return_if_fail (bus_name != NULL && *bus_name != '\0');
2478 
2479 	client->priv->bus_name = g_strdup (bus_name);
2480 }
2481