1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-service.c : Abstract class for an email service
3  *
4  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.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  * Authors: Bertrand Guiheneuf <bertrand@helixcode.com>
19  */
20 
21 #include "evolution-data-server-config.h"
22 
23 #include <ctype.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include <glib/gstdio.h>
29 #include <glib/gi18n-lib.h>
30 
31 #include "camel-async-closure.h"
32 #include "camel-debug.h"
33 #include "camel-enumtypes.h"
34 #include "camel-local-settings.h"
35 #include "camel-network-service.h"
36 #include "camel-network-settings.h"
37 #include "camel-operation.h"
38 #include "camel-session.h"
39 #include "camel-service.h"
40 
41 #define d(x)
42 #define w(x)
43 
44 #define DISPATCH_DATA_KEY "camel-service-dispatch-data"
45 
46 typedef struct _AsyncContext AsyncContext;
47 typedef struct _ConnectionOp ConnectionOp;
48 typedef struct _DispatchData DispatchData;
49 
50 struct _CamelServicePrivate {
51 	GWeakRef session;
52 
53 	GMutex property_lock;
54 	CamelSettings *settings;
55 	GProxyResolver *proxy_resolver;
56 
57 	CamelProvider *provider;
58 
59 	gchar *display_name;
60 	gchar *user_data_dir;
61 	gchar *user_cache_dir;
62 	gchar *uid;
63 	gchar *password;
64 
65 	GMutex connection_lock;
66 	ConnectionOp *connection_op;
67 	CamelServiceConnectionStatus status;
68 
69 	/* Queues of GTasks, by source object. */
70 	GHashTable *task_table;
71 	GMutex task_table_lock;
72 
73 	gboolean network_service_inited;
74 };
75 
76 struct _AsyncContext {
77 	gchar *auth_mechanism;
78 	gboolean clean;
79 };
80 
81 /* The GQueue is only modified while CamelService's
82  * connection_lock is held, so it does not need its
83  * own mutex. */
84 struct _ConnectionOp {
85 	volatile gint ref_count;
86 	GQueue pending;
87 	GMutex task_lock;
88 	GTask *task;
89 	GCancellable *cancellable;
90 };
91 
92 struct _DispatchData {
93 	GWeakRef service;
94 	gboolean return_on_cancel;
95 	GTaskThreadFunc task_func;
96 };
97 
98 enum {
99 	PROP_0,
100 	PROP_CONNECTION_STATUS,
101 	PROP_DISPLAY_NAME,
102 	PROP_PASSWORD,
103 	PROP_PROVIDER,
104 	PROP_PROXY_RESOLVER,
105 	PROP_SESSION,
106 	PROP_SETTINGS,
107 	PROP_UID
108 };
109 
110 /* Forward Declarations */
111 void		camel_network_service_init	(CamelNetworkService *service);
112 static void	camel_service_initable_init	(GInitableIface *iface);
113 static void	service_task_dispatch		(CamelService *service,
114 						 GTask *task);
115 
116 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
117 	CamelService, camel_service, CAMEL_TYPE_OBJECT,
118 	G_ADD_PRIVATE (CamelService)
119 	G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, camel_service_initable_init))
120 G_DEFINE_BOXED_TYPE (CamelServiceAuthType, camel_service_auth_type, camel_service_auth_type_copy, camel_service_auth_type_free);
121 
122 static void
async_context_free(AsyncContext * async_context)123 async_context_free (AsyncContext *async_context)
124 {
125 	g_free (async_context->auth_mechanism);
126 
127 	g_slice_free (AsyncContext, async_context);
128 }
129 
130 static ConnectionOp *
connection_op_new(GTask * task,GCancellable * cancellable)131 connection_op_new (GTask *task,
132                    GCancellable *cancellable)
133 {
134 	ConnectionOp *op;
135 
136 	op = g_slice_new0 (ConnectionOp);
137 	op->ref_count = 1;
138 	g_mutex_init (&op->task_lock);
139 	op->task = g_object_ref (task);
140 
141 	if (G_IS_CANCELLABLE (cancellable))
142 		op->cancellable = g_object_ref (cancellable);
143 
144 	return op;
145 }
146 
147 static ConnectionOp *
connection_op_ref(ConnectionOp * op)148 connection_op_ref (ConnectionOp *op)
149 {
150 	g_return_val_if_fail (op != NULL, NULL);
151 	g_return_val_if_fail (op->ref_count > 0, NULL);
152 
153 	g_atomic_int_inc (&op->ref_count);
154 
155 	return op;
156 }
157 
158 static void
connection_op_unref(ConnectionOp * op)159 connection_op_unref (ConnectionOp *op)
160 {
161 	g_return_if_fail (op != NULL);
162 	g_return_if_fail (op->ref_count > 0);
163 
164 	if (g_atomic_int_dec_and_test (&op->ref_count)) {
165 
166 		/* The pending queue should be empty. */
167 		g_warn_if_fail (g_queue_is_empty (&op->pending));
168 
169 		g_mutex_clear (&op->task_lock);
170 
171 		if (op->task != NULL)
172 			g_object_unref (op->task);
173 
174 		if (op->cancellable != NULL)
175 			g_object_unref (op->cancellable);
176 
177 		g_slice_free (ConnectionOp, op);
178 	}
179 }
180 
181 static void
connection_op_complete(ConnectionOp * op,const GError * error)182 connection_op_complete (ConnectionOp *op,
183                         const GError *error)
184 {
185 	g_mutex_lock (&op->task_lock);
186 
187 	if (op->task != NULL) {
188 		if (error != NULL) {
189 			g_task_return_error (op->task, g_error_copy (error));
190 		} else {
191 			g_task_return_boolean (op->task, TRUE);
192 		}
193 
194 		g_clear_object (&op->task);
195 	}
196 
197 	g_mutex_unlock (&op->task_lock);
198 }
199 
200 static void
connection_op_add_pending(ConnectionOp * op,GTask * task,GCancellable * cancellable)201 connection_op_add_pending (ConnectionOp *op,
202                            GTask *task,
203                            GCancellable *cancellable)
204 {
205 	ConnectionOp *pending_op;
206 
207 	g_return_if_fail (op != NULL);
208 
209 	pending_op = connection_op_new (task, cancellable);
210 
211 	g_queue_push_tail (&op->pending, pending_op);
212 }
213 
214 static void
connection_op_complete_pending(ConnectionOp * op,const GError * error)215 connection_op_complete_pending (ConnectionOp *op,
216                                 const GError *error)
217 {
218 	ConnectionOp *pending_op;
219 
220 	g_return_if_fail (op != NULL);
221 
222 	while (!g_queue_is_empty (&op->pending)) {
223 		pending_op = g_queue_pop_head (&op->pending);
224 		connection_op_complete (pending_op, error);
225 		connection_op_unref (pending_op);
226 	}
227 }
228 
229 static void
dispatch_data_free(DispatchData * dispatch_data)230 dispatch_data_free (DispatchData *dispatch_data)
231 {
232 	g_weak_ref_clear (&dispatch_data->service);
233 
234 	g_slice_free (DispatchData, dispatch_data);
235 }
236 
237 static void
task_queue_free(GQueue * task_queue)238 task_queue_free (GQueue *task_queue)
239 {
240 	g_queue_free_full (task_queue, g_object_unref);
241 }
242 
243 static void
service_task_table_push(CamelService * service,GTask * task)244 service_task_table_push (CamelService *service,
245                          GTask *task)
246 {
247 	GQueue *task_queue;
248 	gpointer source_object;
249 	gboolean queue_was_empty;
250 
251 	g_return_if_fail (CAMEL_IS_SERVICE (service));
252 	g_return_if_fail (G_IS_TASK (task));
253 
254 	source_object = g_task_get_source_object (task);
255 	if (source_object == NULL)
256 		source_object = service;
257 
258 	g_mutex_lock (&service->priv->task_table_lock);
259 
260 	task_queue = g_hash_table_lookup (
261 		service->priv->task_table, source_object);
262 
263 	/* Create on demand. */
264 	if (task_queue == NULL) {
265 		task_queue = g_queue_new ();
266 		g_hash_table_insert (
267 			service->priv->task_table,
268 			source_object, task_queue);
269 	}
270 
271 	queue_was_empty = g_queue_is_empty (task_queue);
272 	g_queue_push_tail (task_queue, g_object_ref (task));
273 
274 	g_mutex_unlock (&service->priv->task_table_lock);
275 
276 	if (queue_was_empty)
277 		service_task_dispatch (service, task);
278 }
279 
280 static void
service_task_table_done(CamelService * service,GTask * task)281 service_task_table_done (CamelService *service,
282                          GTask *task)
283 {
284 	GQueue *task_queue;
285 	gpointer source_object;
286 	GTask *next = NULL;
287 
288 	g_return_if_fail (CAMEL_IS_SERVICE (service));
289 	g_return_if_fail (G_IS_TASK (task));
290 
291 	source_object = g_task_get_source_object (task);
292 	if (source_object == NULL)
293 		source_object = service;
294 
295 	g_mutex_lock (&service->priv->task_table_lock);
296 
297 	task_queue = g_hash_table_lookup (
298 		service->priv->task_table, source_object);
299 
300 	if (task_queue != NULL) {
301 		if (g_queue_remove (task_queue, task))
302 			g_object_unref (task);
303 
304 		next = g_queue_peek_head (task_queue);
305 		if (next != NULL)
306 			g_object_ref (next);
307 	}
308 
309 	g_mutex_unlock (&service->priv->task_table_lock);
310 
311 	if (next != NULL) {
312 		service_task_dispatch (service, next);
313 		g_object_unref (next);
314 	}
315 }
316 
317 static void
service_task_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)318 service_task_thread (GTask *task,
319                      gpointer source_object,
320                      gpointer task_data,
321                      GCancellable *cancellable)
322 {
323 	CamelService *service;
324 	DispatchData *data;
325 
326 	data = g_object_get_data (G_OBJECT (task), DISPATCH_DATA_KEY);
327 	g_return_if_fail (data != NULL);
328 
329 	service = g_weak_ref_get (&data->service);
330 	g_return_if_fail (service != NULL);
331 
332 	data->task_func (task, source_object, task_data, cancellable);
333 
334 	service_task_table_done (service, task);
335 
336 	g_object_unref (service);
337 }
338 
339 static void
service_task_dispatch(CamelService * service,GTask * task)340 service_task_dispatch (CamelService *service,
341                        GTask *task)
342 {
343 	DispatchData *data;
344 
345 	data = g_object_get_data (G_OBJECT (task), DISPATCH_DATA_KEY);
346 	g_return_if_fail (data != NULL);
347 
348 	/* Restore the task's previous "return-on-cancel" flag.
349 	 * This returns FALSE if the task is already cancelled,
350 	 * in which case we skip calling g_task_run_in_thread()
351 	 * so the task doesn't complete twice. */
352 	if (g_task_set_return_on_cancel (task, data->return_on_cancel))
353 		g_task_run_in_thread (task, service_task_thread);
354 	else
355 		service_task_table_done (service, task);
356 }
357 
358 static gchar *
service_find_old_data_dir(CamelService * service)359 service_find_old_data_dir (CamelService *service)
360 {
361 	CamelProvider *provider;
362 	CamelSession *session;
363 	CamelURL *url;
364 	GString *path;
365 	gboolean allows_host;
366 	gboolean allows_user;
367 	gboolean needs_host;
368 	gboolean needs_path;
369 	gboolean needs_user;
370 	const gchar *base_dir;
371 	gchar *old_data_dir;
372 
373 	provider = camel_service_get_provider (service);
374 	url = camel_service_new_camel_url (service);
375 
376 	allows_host = CAMEL_PROVIDER_ALLOWS (provider, CAMEL_URL_PART_HOST);
377 	allows_user = CAMEL_PROVIDER_ALLOWS (provider, CAMEL_URL_PART_USER);
378 
379 	needs_host = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_HOST);
380 	needs_path = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_PATH);
381 	needs_user = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_USER);
382 
383 	/* This function reproduces the way service data directories used
384 	 * to be determined before we moved to just using the UID.  If the
385 	 * old data directory exists, try renaming it to the new form.
386 	 *
387 	 * A virtual class method was used to determine the directory path,
388 	 * but no known CamelProviders ever overrode the default algorithm
389 	 * below.  So this should work for everyone. */
390 
391 	path = g_string_new (provider->protocol);
392 
393 	if (allows_user) {
394 		g_string_append_c (path, '/');
395 		if (url->user != NULL)
396 			g_string_append (path, url->user);
397 		if (allows_host) {
398 			g_string_append_c (path, '@');
399 			if (url->host != NULL)
400 				g_string_append (path, url->host);
401 			if (url->port) {
402 				g_string_append_c (path, ':');
403 				g_string_append_printf (path, "%d", url->port);
404 			}
405 		} else if (!needs_user) {
406 			g_string_append_c (path, '@');
407 		}
408 
409 	} else if (allows_host) {
410 		g_string_append_c (path, '/');
411 		if (!needs_host)
412 			g_string_append_c (path, '@');
413 		if (url->host != NULL)
414 			g_string_append (path, url->host);
415 		if (url->port) {
416 			g_string_append_c (path, ':');
417 			g_string_append_printf (path, "%d", url->port);
418 		}
419 	}
420 
421 	if (needs_path && url->path) {
422 		if (*url->path != '/')
423 			g_string_append_c (path, '/');
424 		g_string_append (path, url->path);
425 	}
426 
427 	session = camel_service_ref_session (service);
428 	if (session) {
429 		base_dir = camel_session_get_user_data_dir (session);
430 		old_data_dir = g_build_filename (base_dir, path->str, NULL);
431 
432 		g_object_unref (session);
433 	} else {
434 		old_data_dir = NULL;
435 	}
436 
437 	g_string_free (path, TRUE);
438 
439 	if (old_data_dir && !g_file_test (old_data_dir, G_FILE_TEST_IS_DIR)) {
440 		g_free (old_data_dir);
441 		old_data_dir = NULL;
442 	}
443 
444 	camel_url_free (url);
445 
446 	return old_data_dir;
447 }
448 
449 static gboolean
service_notify_connection_status_cb(gpointer user_data)450 service_notify_connection_status_cb (gpointer user_data)
451 {
452 	CamelService *service = CAMEL_SERVICE (user_data);
453 
454 	g_object_notify (G_OBJECT (service), "connection-status");
455 
456 	return FALSE;
457 }
458 
459 static void
service_queue_notify_connection_status(CamelService * service)460 service_queue_notify_connection_status (CamelService *service)
461 {
462 	CamelSession *session;
463 
464 	session = camel_service_ref_session (service);
465 
466 	/* most-likely exitting the application */
467 	if (!session)
468 		return;
469 
470 	/* Prioritize ahead of GTK+ redraws. */
471 	camel_session_idle_add (
472 		session, G_PRIORITY_HIGH_IDLE,
473 		service_notify_connection_status_cb,
474 		g_object_ref (service),
475 		(GDestroyNotify) g_object_unref);
476 
477 	g_object_unref (session);
478 }
479 
480 static void
service_shared_connect_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)481 service_shared_connect_cb (GObject *source_object,
482                            GAsyncResult *result,
483                            gpointer user_data)
484 {
485 	CamelService *service;
486 	ConnectionOp *op = user_data;
487 	gboolean success;
488 	GError *local_error = NULL;
489 
490 	service = CAMEL_SERVICE (source_object);
491 	success = g_task_propagate_boolean (G_TASK (result), &local_error);
492 
493 	g_mutex_lock (&service->priv->connection_lock);
494 
495 	if (service->priv->connection_op == op) {
496 		connection_op_unref (service->priv->connection_op);
497 		service->priv->connection_op = NULL;
498 		if (success)
499 			service->priv->status = CAMEL_SERVICE_CONNECTED;
500 		else
501 			service->priv->status = CAMEL_SERVICE_DISCONNECTED;
502 		service_queue_notify_connection_status (service);
503 	}
504 
505 	connection_op_complete (op, local_error);
506 	connection_op_complete_pending (op, local_error);
507 
508 	g_mutex_unlock (&service->priv->connection_lock);
509 
510 	connection_op_unref (op);
511 	g_clear_error (&local_error);
512 }
513 
514 static void
service_shared_connect_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)515 service_shared_connect_thread (GTask *task,
516                                gpointer source_object,
517                                gpointer task_data,
518                                GCancellable *cancellable)
519 {
520 	CamelServiceClass *class;
521 	gboolean success;
522 	GError *local_error = NULL;
523 
524 	/* Note we call the class method directly here. */
525 
526 	class = CAMEL_SERVICE_GET_CLASS (source_object);
527 	g_return_if_fail (class->connect_sync != NULL);
528 
529 	success = class->connect_sync (
530 		CAMEL_SERVICE (source_object),
531 		cancellable, &local_error);
532 
533 	if (local_error != NULL) {
534 		g_task_return_error (task, local_error);
535 	} else {
536 		g_task_return_boolean (task, success);
537 	}
538 }
539 
540 static void
service_shared_connect(CamelService * service,gint io_priority,ConnectionOp * op)541 service_shared_connect (CamelService *service,
542                         gint io_priority,
543                         ConnectionOp *op)
544 {
545 	GTask *task;
546 
547 	task = g_task_new (
548 		service, op->cancellable,
549 		service_shared_connect_cb,
550 		connection_op_ref (op));
551 
552 	g_task_set_source_tag (task, service_shared_connect);
553 	g_task_set_priority (task, io_priority);
554 
555 	g_task_run_in_thread (task, service_shared_connect_thread);
556 
557 	g_object_unref (task);
558 }
559 
560 static void
service_shared_disconnect_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)561 service_shared_disconnect_cb (GObject *source_object,
562                               GAsyncResult *result,
563                               gpointer user_data)
564 {
565 	CamelService *service;
566 	ConnectionOp *op = user_data;
567 	gboolean success;
568 	GError *local_error = NULL;
569 
570 	service = CAMEL_SERVICE (source_object);
571 	success = g_task_propagate_boolean (G_TASK (result), &local_error);
572 
573 	g_mutex_lock (&service->priv->connection_lock);
574 
575 	if (service->priv->connection_op == op) {
576 		connection_op_unref (service->priv->connection_op);
577 		service->priv->connection_op = NULL;
578 		if (success || service->priv->status == CAMEL_SERVICE_CONNECTING)
579 			service->priv->status = CAMEL_SERVICE_DISCONNECTED;
580 		else
581 			service->priv->status = CAMEL_SERVICE_CONNECTED;
582 		service_queue_notify_connection_status (service);
583 	}
584 
585 	connection_op_complete (op, local_error);
586 	connection_op_complete_pending (op, local_error);
587 
588 	g_mutex_unlock (&service->priv->connection_lock);
589 
590 	connection_op_unref (op);
591 	g_clear_error (&local_error);
592 }
593 
594 static void
service_shared_disconnect_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)595 service_shared_disconnect_thread (GTask *task,
596                                   gpointer source_object,
597                                   gpointer task_data,
598                                   GCancellable *cancellable)
599 {
600 	CamelServiceClass *class;
601 	AsyncContext *async_context;
602 	gboolean success;
603 	GError *local_error = NULL;
604 
605 	/* Note we call the class method directly here. */
606 
607 	async_context = (AsyncContext *) task_data;
608 
609 	class = CAMEL_SERVICE_GET_CLASS (source_object);
610 	g_return_if_fail (class->disconnect_sync != NULL);
611 
612 	success = class->disconnect_sync (
613 		CAMEL_SERVICE (source_object),
614 		async_context->clean,
615 		cancellable, &local_error);
616 
617 	if (local_error != NULL) {
618 		g_task_return_error (task, local_error);
619 	} else {
620 		g_task_return_boolean (task, success);
621 	}
622 }
623 
624 static void
service_shared_disconnect(CamelService * service,gboolean clean,gint io_priority,ConnectionOp * op)625 service_shared_disconnect (CamelService *service,
626                            gboolean clean,
627                            gint io_priority,
628                            ConnectionOp *op)
629 {
630 	GTask *task;
631 	AsyncContext *async_context;
632 
633 	async_context = g_slice_new0 (AsyncContext);
634 	async_context->clean = clean;
635 
636 	task = g_task_new (
637 		service, op->cancellable,
638 		service_shared_disconnect_cb,
639 		connection_op_ref (op));
640 
641 	g_task_set_source_tag (task, service_shared_disconnect);
642 	g_task_set_priority (task, io_priority);
643 
644 	g_task_set_task_data (
645 		task, async_context,
646 		(GDestroyNotify) async_context_free);
647 
648 	g_task_run_in_thread (task, service_shared_disconnect_thread);
649 
650 	g_object_unref (task);
651 }
652 
653 static void
service_set_provider(CamelService * service,CamelProvider * provider)654 service_set_provider (CamelService *service,
655                       CamelProvider *provider)
656 {
657 	g_return_if_fail (provider != NULL);
658 	g_return_if_fail (service->priv->provider == NULL);
659 
660 	service->priv->provider = provider;
661 }
662 
663 static void
service_set_session(CamelService * service,CamelSession * session)664 service_set_session (CamelService *service,
665                      CamelSession *session)
666 {
667 	g_return_if_fail (CAMEL_IS_SESSION (session));
668 
669 	g_weak_ref_set (&service->priv->session, session);
670 }
671 
672 static void
service_set_uid(CamelService * service,const gchar * uid)673 service_set_uid (CamelService *service,
674                  const gchar *uid)
675 {
676 	g_return_if_fail (uid != NULL);
677 	g_return_if_fail (service->priv->uid == NULL);
678 
679 	service->priv->uid = g_strdup (uid);
680 }
681 
682 static void
service_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)683 service_set_property (GObject *object,
684                       guint property_id,
685                       const GValue *value,
686                       GParamSpec *pspec)
687 {
688 	switch (property_id) {
689 		case PROP_DISPLAY_NAME:
690 			camel_service_set_display_name (
691 				CAMEL_SERVICE (object),
692 				g_value_get_string (value));
693 			return;
694 
695 		case PROP_PASSWORD:
696 			camel_service_set_password (
697 				CAMEL_SERVICE (object),
698 				g_value_get_string (value));
699 			return;
700 
701 		case PROP_PROVIDER:
702 			service_set_provider (
703 				CAMEL_SERVICE (object),
704 				g_value_get_boxed (value));
705 			return;
706 
707 		case PROP_PROXY_RESOLVER:
708 			camel_service_set_proxy_resolver (
709 				CAMEL_SERVICE (object),
710 				g_value_get_object (value));
711 			return;
712 
713 		case PROP_SESSION:
714 			service_set_session (
715 				CAMEL_SERVICE (object),
716 				g_value_get_object (value));
717 			return;
718 
719 		case PROP_SETTINGS:
720 			camel_service_set_settings (
721 				CAMEL_SERVICE (object),
722 				g_value_get_object (value));
723 			return;
724 
725 		case PROP_UID:
726 			service_set_uid (
727 				CAMEL_SERVICE (object),
728 				g_value_get_string (value));
729 			return;
730 	}
731 
732 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
733 }
734 
735 static void
service_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)736 service_get_property (GObject *object,
737                       guint property_id,
738                       GValue *value,
739                       GParamSpec *pspec)
740 {
741 	switch (property_id) {
742 		case PROP_CONNECTION_STATUS:
743 			g_value_set_enum (
744 				value,
745 				camel_service_get_connection_status (
746 				CAMEL_SERVICE (object)));
747 			return;
748 
749 		case PROP_DISPLAY_NAME:
750 			g_value_take_string (
751 				value,
752 				camel_service_dup_display_name (
753 				CAMEL_SERVICE (object)));
754 			return;
755 
756 		case PROP_PASSWORD:
757 			g_value_take_string (
758 				value,
759 				camel_service_dup_password (
760 				CAMEL_SERVICE (object)));
761 			return;
762 
763 		case PROP_PROVIDER:
764 			g_value_set_boxed (
765 				value,
766 				camel_service_get_provider (
767 				CAMEL_SERVICE (object)));
768 			return;
769 
770 		case PROP_PROXY_RESOLVER:
771 			g_value_take_object (
772 				value,
773 				camel_service_ref_proxy_resolver (
774 				CAMEL_SERVICE (object)));
775 			return;
776 
777 		case PROP_SESSION:
778 			g_value_take_object (
779 				value,
780 				camel_service_ref_session (
781 				CAMEL_SERVICE (object)));
782 			return;
783 
784 		case PROP_SETTINGS:
785 			g_value_take_object (
786 				value,
787 				camel_service_ref_settings (
788 				CAMEL_SERVICE (object)));
789 			return;
790 
791 		case PROP_UID:
792 			g_value_set_string (
793 				value,
794 				camel_service_get_uid (
795 				CAMEL_SERVICE (object)));
796 			return;
797 	}
798 
799 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
800 }
801 
802 static void
service_dispose(GObject * object)803 service_dispose (GObject *object)
804 {
805 	CamelServicePrivate *priv;
806 
807 	priv = CAMEL_SERVICE (object)->priv;
808 
809 	if (priv->status == CAMEL_SERVICE_CONNECTED)
810 		CAMEL_SERVICE_GET_CLASS (object)->disconnect_sync (
811 			CAMEL_SERVICE (object), TRUE, NULL, NULL);
812 
813 	g_weak_ref_set (&priv->session, NULL);
814 
815 	g_clear_object (&priv->settings);
816 	g_clear_object (&priv->proxy_resolver);
817 
818 	g_hash_table_remove_all (priv->task_table);
819 
820 	/* Chain up to parent's dispose() method. */
821 	G_OBJECT_CLASS (camel_service_parent_class)->dispose (object);
822 }
823 
824 static void
service_finalize(GObject * object)825 service_finalize (GObject *object)
826 {
827 	CamelServicePrivate *priv;
828 
829 	priv = CAMEL_SERVICE (object)->priv;
830 
831 	g_mutex_clear (&priv->property_lock);
832 
833 	g_free (priv->display_name);
834 	g_free (priv->user_data_dir);
835 	g_free (priv->user_cache_dir);
836 	g_free (priv->uid);
837 	g_free (priv->password);
838 
839 	/* There should be no outstanding connection operations. */
840 	g_warn_if_fail (priv->connection_op == NULL);
841 	g_mutex_clear (&priv->connection_lock);
842 
843 	g_hash_table_destroy (priv->task_table);
844 	g_mutex_clear (&priv->task_table_lock);
845 
846 	g_weak_ref_clear (&priv->session);
847 
848 	/* Chain up to parent's finalize() method. */
849 	G_OBJECT_CLASS (camel_service_parent_class)->finalize (object);
850 }
851 
852 static void
service_constructed(GObject * object)853 service_constructed (GObject *object)
854 {
855 	CamelService *service;
856 	CamelSession *session;
857 	const gchar *base_dir;
858 	const gchar *uid;
859 
860 	/* Chain up to parent's constructed() method. */
861 	G_OBJECT_CLASS (camel_service_parent_class)->constructed (object);
862 
863 	service = CAMEL_SERVICE (object);
864 	session = camel_service_ref_session (service);
865 
866 	uid = camel_service_get_uid (service);
867 
868 	base_dir = camel_session_get_user_data_dir (session);
869 	service->priv->user_data_dir = g_build_filename (base_dir, uid, NULL);
870 
871 	base_dir = camel_session_get_user_cache_dir (session);
872 	service->priv->user_cache_dir = g_build_filename (base_dir, uid, NULL);
873 
874 	g_object_unref (session);
875 
876 	/* The CamelNetworkService interface needs initialization. */
877 	if (CAMEL_IS_NETWORK_SERVICE (service)) {
878 		camel_network_service_init (CAMEL_NETWORK_SERVICE (service));
879 		service->priv->network_service_inited = TRUE;
880 	}
881 }
882 
883 static gchar *
service_get_name(CamelService * service,gboolean brief)884 service_get_name (CamelService *service,
885                   gboolean brief)
886 {
887 	g_warning (
888 		"%s does not implement CamelServiceClass::get_name()",
889 		G_OBJECT_TYPE_NAME (service));
890 
891 	return g_strdup (G_OBJECT_TYPE_NAME (service));
892 }
893 
894 static gboolean
service_connect_sync(CamelService * service,GCancellable * cancellable,GError ** error)895 service_connect_sync (CamelService *service,
896                       GCancellable *cancellable,
897                       GError **error)
898 {
899 	return TRUE;
900 }
901 
902 static gboolean
service_disconnect_sync(CamelService * service,gboolean clean,GCancellable * cancellable,GError ** error)903 service_disconnect_sync (CamelService *service,
904                          gboolean clean,
905                          GCancellable *cancellable,
906                          GError **error)
907 {
908 	if (CAMEL_IS_NETWORK_SERVICE (service))
909 		camel_network_service_set_connectable (
910 			CAMEL_NETWORK_SERVICE (service), NULL);
911 
912 	return TRUE;
913 }
914 
915 static GList *
service_query_auth_types_sync(CamelService * service,GCancellable * cancellable,GError ** error)916 service_query_auth_types_sync (CamelService *service,
917                                GCancellable *cancellable,
918                                GError **error)
919 {
920 	return NULL;
921 }
922 
923 static gboolean
service_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)924 service_initable_init (GInitable *initable,
925                        GCancellable *cancellable,
926                        GError **error)
927 {
928 	/* Nothing to do here, but we may need add something in the future.
929 	 * For now this is a placeholder so subclasses can safely chain up. */
930 
931 	return TRUE;
932 }
933 
934 static void
camel_service_class_init(CamelServiceClass * class)935 camel_service_class_init (CamelServiceClass *class)
936 {
937 	GObjectClass *object_class;
938 
939 	object_class = G_OBJECT_CLASS (class);
940 	object_class->set_property = service_set_property;
941 	object_class->get_property = service_get_property;
942 	object_class->dispose = service_dispose;
943 	object_class->finalize = service_finalize;
944 	object_class->constructed = service_constructed;
945 
946 	class->settings_type = CAMEL_TYPE_SETTINGS;
947 	class->get_name = service_get_name;
948 	class->connect_sync = service_connect_sync;
949 	class->disconnect_sync = service_disconnect_sync;
950 	class->query_auth_types_sync = service_query_auth_types_sync;
951 
952 	g_object_class_install_property (
953 		object_class,
954 		PROP_CONNECTION_STATUS,
955 		g_param_spec_enum (
956 			"connection-status",
957 			"Connection Status",
958 			"The connection status for the service",
959 			CAMEL_TYPE_SERVICE_CONNECTION_STATUS,
960 			CAMEL_SERVICE_DISCONNECTED,
961 			G_PARAM_READABLE |
962 			G_PARAM_STATIC_STRINGS));
963 
964 	g_object_class_install_property (
965 		object_class,
966 		PROP_DISPLAY_NAME,
967 		g_param_spec_string (
968 			"display-name",
969 			"Display Name",
970 			"The display name for the service",
971 			NULL,
972 			G_PARAM_READWRITE |
973 			G_PARAM_CONSTRUCT |
974 			G_PARAM_EXPLICIT_NOTIFY |
975 			G_PARAM_STATIC_STRINGS));
976 
977 	g_object_class_install_property (
978 		object_class,
979 		PROP_PASSWORD,
980 		g_param_spec_string (
981 			"password",
982 			"Password",
983 			"The password for the service",
984 			NULL,
985 			G_PARAM_READWRITE |
986 			G_PARAM_CONSTRUCT |
987 			G_PARAM_EXPLICIT_NOTIFY |
988 			G_PARAM_STATIC_STRINGS));
989 
990 	g_object_class_install_property (
991 		object_class,
992 		PROP_PROVIDER,
993 		g_param_spec_boxed (
994 			"provider",
995 			"Provider",
996 			"The CamelProvider for the service",
997 			CAMEL_TYPE_PROVIDER,
998 			G_PARAM_READWRITE |
999 			G_PARAM_CONSTRUCT_ONLY |
1000 			G_PARAM_STATIC_STRINGS));
1001 
1002 	g_object_class_install_property (
1003 		object_class,
1004 		PROP_PROXY_RESOLVER,
1005 		g_param_spec_object (
1006 			"proxy-resolver",
1007 			"Proxy Resolver",
1008 			"The proxy resolver for the service",
1009 			G_TYPE_PROXY_RESOLVER,
1010 			G_PARAM_READWRITE |
1011 			G_PARAM_EXPLICIT_NOTIFY |
1012 			G_PARAM_STATIC_STRINGS));
1013 
1014 	g_object_class_install_property (
1015 		object_class,
1016 		PROP_SESSION,
1017 		g_param_spec_object (
1018 			"session",
1019 			"Session",
1020 			"A CamelSession instance",
1021 			CAMEL_TYPE_SESSION,
1022 			G_PARAM_READWRITE |
1023 			G_PARAM_CONSTRUCT_ONLY |
1024 			G_PARAM_STATIC_STRINGS));
1025 
1026 	g_object_class_install_property (
1027 		object_class,
1028 		PROP_SETTINGS,
1029 		g_param_spec_object (
1030 			"settings",
1031 			"Settings",
1032 			"A CamelSettings instance",
1033 			CAMEL_TYPE_SETTINGS,
1034 			G_PARAM_READWRITE |
1035 			G_PARAM_CONSTRUCT |
1036 			G_PARAM_EXPLICIT_NOTIFY |
1037 			G_PARAM_STATIC_STRINGS));
1038 
1039 	g_object_class_install_property (
1040 		object_class,
1041 		PROP_UID,
1042 		g_param_spec_string (
1043 			"uid",
1044 			"UID",
1045 			"The unique identity of the service",
1046 			NULL,
1047 			G_PARAM_READWRITE |
1048 			G_PARAM_CONSTRUCT_ONLY |
1049 			G_PARAM_STATIC_STRINGS));
1050 }
1051 
1052 static void
camel_service_initable_init(GInitableIface * iface)1053 camel_service_initable_init (GInitableIface *iface)
1054 {
1055 	iface->init = service_initable_init;
1056 }
1057 
1058 static void
camel_service_init(CamelService * service)1059 camel_service_init (CamelService *service)
1060 {
1061 	GHashTable *task_table;
1062 
1063 	task_table = g_hash_table_new_full (
1064 		(GHashFunc) g_direct_hash,
1065 		(GEqualFunc) g_direct_equal,
1066 		(GDestroyNotify) NULL,
1067 		(GDestroyNotify) task_queue_free);
1068 
1069 	service->priv = camel_service_get_instance_private (service);
1070 
1071 	g_mutex_init (&service->priv->property_lock);
1072 	g_mutex_init (&service->priv->connection_lock);
1073 	g_weak_ref_init (&service->priv->session, NULL);
1074 	service->priv->status = CAMEL_SERVICE_DISCONNECTED;
1075 
1076 	service->priv->proxy_resolver = g_proxy_resolver_get_default ();
1077 	if (service->priv->proxy_resolver != NULL)
1078 		g_object_ref (service->priv->proxy_resolver);
1079 
1080 	service->priv->task_table = task_table;
1081 	g_mutex_init (&service->priv->task_table_lock);
1082 }
1083 
1084 G_DEFINE_QUARK (camel-service-error-quark, camel_service_error)
1085 
1086 /**
1087  * camel_service_migrate_files:
1088  * @service: a #CamelService
1089  *
1090  * Performs any necessary file migrations for @service.  This should be
1091  * called after installing or configuring the @service's #CamelSettings,
1092  * since it requires building a URL string for @service.
1093  *
1094  * Since: 3.4
1095  **/
1096 void
camel_service_migrate_files(CamelService * service)1097 camel_service_migrate_files (CamelService *service)
1098 {
1099 	const gchar *new_data_dir;
1100 	gchar *old_data_dir;
1101 
1102 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1103 
1104 	new_data_dir = camel_service_get_user_data_dir (service);
1105 	old_data_dir = service_find_old_data_dir (service);
1106 
1107 	/* If the old data directory name exists, try renaming
1108 	 * it to the new data directory.  Failure is non-fatal. */
1109 	if (old_data_dir != NULL) {
1110 		if (g_rename (old_data_dir, new_data_dir) == -1) {
1111 			g_warning (
1112 				"%s: Failed to rename '%s' to '%s': %s",
1113 				G_STRFUNC, old_data_dir, new_data_dir, g_strerror (errno));
1114 		}
1115 		g_free (old_data_dir);
1116 	}
1117 }
1118 
1119 /**
1120  * camel_service_new_camel_url:
1121  * @service: a #CamelService
1122  *
1123  * Returns a new #CamelURL representing @service.
1124  * Free the returned #CamelURL with camel_url_free().
1125  *
1126  * Returns: a new #CamelURL
1127  *
1128  * Since: 3.2
1129  **/
1130 CamelURL *
camel_service_new_camel_url(CamelService * service)1131 camel_service_new_camel_url (CamelService *service)
1132 {
1133 	CamelURL *url;
1134 	CamelProvider *provider;
1135 	CamelSettings *settings;
1136 	gchar *host = NULL;
1137 	gchar *user = NULL;
1138 	gchar *path = NULL;
1139 	guint16 port = 0;
1140 
1141 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1142 
1143 	provider = camel_service_get_provider (service);
1144 	g_return_val_if_fail (provider != NULL, NULL);
1145 
1146 	settings = camel_service_ref_settings (service);
1147 
1148 	/* Allocate as camel_url_new_with_base() does. */
1149 	url = g_new0 (CamelURL, 1);
1150 
1151 	if (CAMEL_IS_NETWORK_SETTINGS (settings)) {
1152 		CamelNetworkSettings *network_settings;
1153 
1154 		network_settings = CAMEL_NETWORK_SETTINGS (settings);
1155 		host = camel_network_settings_dup_host (network_settings);
1156 		port = camel_network_settings_get_port (network_settings);
1157 		user = camel_network_settings_dup_user (network_settings);
1158 	}
1159 
1160 	if (CAMEL_IS_LOCAL_SETTINGS (settings)) {
1161 		CamelLocalSettings *local_settings;
1162 
1163 		local_settings = CAMEL_LOCAL_SETTINGS (settings);
1164 		path = camel_local_settings_dup_path (local_settings);
1165 	}
1166 
1167 	camel_url_set_protocol (url, provider->protocol);
1168 	camel_url_set_host (url, host);
1169 	camel_url_set_port (url, port);
1170 	camel_url_set_user (url, user);
1171 	camel_url_set_path (url, path);
1172 
1173 	g_free (host);
1174 	g_free (user);
1175 	g_free (path);
1176 
1177 	g_object_unref (settings);
1178 
1179 	return url;
1180 }
1181 
1182 /**
1183  * camel_service_get_connection_status:
1184  * @service: a #CamelService
1185  *
1186  * Returns the connection status for @service.
1187  *
1188  * Returns: the connection status
1189  *
1190  * Since: 3.2
1191  **/
1192 CamelServiceConnectionStatus
camel_service_get_connection_status(CamelService * service)1193 camel_service_get_connection_status (CamelService *service)
1194 {
1195 	g_return_val_if_fail (
1196 		CAMEL_IS_SERVICE (service),
1197 		CAMEL_SERVICE_DISCONNECTED);
1198 
1199 	return service->priv->status;
1200 }
1201 
1202 /**
1203  * camel_service_get_display_name:
1204  * @service: a #CamelService
1205  *
1206  * Returns the display name for @service, or %NULL if @service has not
1207  * been given a display name.  The display name is intended for use in
1208  * a user interface and should generally be given a user-defined name.
1209  *
1210  * Compare this with camel_service_get_name(), which returns a built-in
1211  * description of the type of service (IMAP, SMTP, etc.).
1212  *
1213  * Returns: the display name for @service, or %NULL
1214  *
1215  * Since: 3.2
1216  **/
1217 const gchar *
camel_service_get_display_name(CamelService * service)1218 camel_service_get_display_name (CamelService *service)
1219 {
1220 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1221 
1222 	return service->priv->display_name;
1223 }
1224 
1225 /**
1226  * camel_service_dup_display_name:
1227  * @service: a #CamelService
1228  *
1229  * Thread-safe variation of camel_service_get_display_name().
1230  * Use this function when accessing @service from multiple threads.
1231  *
1232  * The returned string should be freed with g_free() when no longer needed.
1233  *
1234  * Returns: a newly-allocated copy of #CamelService:display-name
1235  *
1236  * Since: 3.12
1237  **/
1238 gchar *
camel_service_dup_display_name(CamelService * service)1239 camel_service_dup_display_name (CamelService *service)
1240 {
1241 	const gchar *protected;
1242 	gchar *duplicate;
1243 
1244 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1245 
1246 	g_mutex_lock (&service->priv->property_lock);
1247 
1248 	protected = camel_service_get_display_name (service);
1249 	duplicate = g_strdup (protected);
1250 
1251 	g_mutex_unlock (&service->priv->property_lock);
1252 
1253 	return duplicate;
1254 }
1255 
1256 /**
1257  * camel_service_set_display_name:
1258  * @service: a #CamelService
1259  * @display_name: a valid UTF-8 string, or %NULL
1260  *
1261  * Assigns a UTF-8 display name to @service.  The display name is intended
1262  * for use in a user interface and should generally be given a user-defined
1263  * name.
1264  *
1265  * Compare this with camel_service_get_name(), which returns a built-in
1266  * description of the type of service (IMAP, SMTP, etc.).
1267  *
1268  * Since: 3.2
1269  **/
1270 void
camel_service_set_display_name(CamelService * service,const gchar * display_name)1271 camel_service_set_display_name (CamelService *service,
1272                                 const gchar *display_name)
1273 {
1274 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1275 
1276 	if (display_name != NULL)
1277 		g_return_if_fail (g_utf8_validate (display_name, -1, NULL));
1278 
1279 	g_mutex_lock (&service->priv->property_lock);
1280 
1281 	if (g_strcmp0 (service->priv->display_name, display_name) == 0) {
1282 		g_mutex_unlock (&service->priv->property_lock);
1283 		return;
1284 	}
1285 
1286 	g_free (service->priv->display_name);
1287 	service->priv->display_name = g_strdup (display_name);
1288 
1289 	g_mutex_unlock (&service->priv->property_lock);
1290 
1291 	g_object_notify (G_OBJECT (service), "display-name");
1292 }
1293 
1294 /**
1295  * camel_service_get_password:
1296  * @service: a #CamelService
1297  *
1298  * Returns the password for @service.  Some SASL mechanisms use this
1299  * when attempting to authenticate.
1300  *
1301  * Returns: the password for @service
1302  *
1303  * Since: 3.4
1304  **/
1305 const gchar *
camel_service_get_password(CamelService * service)1306 camel_service_get_password (CamelService *service)
1307 {
1308 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1309 
1310 	return service->priv->password;
1311 }
1312 
1313 /**
1314  * camel_service_dup_password:
1315  * @service: a #CamelService
1316  *
1317  * Thread-safe variation of camel_service_get_password().
1318  * Use this function when accessing @service from multiple threads.
1319  *
1320  * The returned string should be freed with g_free() when no longer needed.
1321  *
1322  * Returns: a newly-allocated copy of #CamelService:password
1323  *
1324  * Since: 3.12
1325  **/
1326 gchar *
camel_service_dup_password(CamelService * service)1327 camel_service_dup_password (CamelService *service)
1328 {
1329 	const gchar *protected;
1330 	gchar *duplicate;
1331 
1332 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1333 
1334 	g_mutex_lock (&service->priv->property_lock);
1335 
1336 	protected = camel_service_get_password (service);
1337 	duplicate = g_strdup (protected);
1338 
1339 	g_mutex_unlock (&service->priv->property_lock);
1340 
1341 	return duplicate;
1342 }
1343 
1344 /**
1345  * camel_service_set_password:
1346  * @service: a #CamelService
1347  * @password: the password for @service
1348  *
1349  * Sets the password for @service.  Use this function to cache the password
1350  * in memory after obtaining it through camel_session_get_password().  Some
1351  * SASL mechanisms use this when attempting to authenticate.
1352  *
1353  * Since: 3.4
1354  **/
1355 void
camel_service_set_password(CamelService * service,const gchar * password)1356 camel_service_set_password (CamelService *service,
1357                             const gchar *password)
1358 {
1359 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1360 
1361 	g_mutex_lock (&service->priv->property_lock);
1362 
1363 	if (g_strcmp0 (service->priv->password, password) == 0) {
1364 		g_mutex_unlock (&service->priv->property_lock);
1365 		return;
1366 	}
1367 
1368 	g_free (service->priv->password);
1369 	service->priv->password = g_strdup (password);
1370 
1371 	g_mutex_unlock (&service->priv->property_lock);
1372 
1373 	g_object_notify (G_OBJECT (service), "password");
1374 }
1375 
1376 /**
1377  * camel_service_get_user_data_dir:
1378  * @service: a #CamelService
1379  *
1380  * Returns the base directory under which to store user-specific data
1381  * for @service.  The directory is formed by appending the directory
1382  * returned by camel_session_get_user_data_dir() with the service's
1383  * #CamelService:uid value.
1384  *
1385  * Returns: the base directory for @service
1386  *
1387  * Since: 3.2
1388  **/
1389 const gchar *
camel_service_get_user_data_dir(CamelService * service)1390 camel_service_get_user_data_dir (CamelService *service)
1391 {
1392 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1393 
1394 	return service->priv->user_data_dir;
1395 }
1396 
1397 /**
1398  * camel_service_get_user_cache_dir:
1399  * @service: a #CamelService
1400  *
1401  * Returns the base directory under which to store cache data
1402  * for @service.  The directory is formed by appending the directory
1403  * returned by camel_session_get_user_cache_dir() with the service's
1404  * #CamelService:uid value.
1405  *
1406  * Returns: the base cache directory for @service
1407  *
1408  * Since: 3.4
1409  **/
1410 const gchar *
camel_service_get_user_cache_dir(CamelService * service)1411 camel_service_get_user_cache_dir (CamelService *service)
1412 {
1413 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1414 
1415 	return service->priv->user_cache_dir;
1416 }
1417 
1418 /**
1419  * camel_service_get_name:
1420  * @service: a #CamelService
1421  * @brief: whether or not to use a briefer form
1422  *
1423  * This gets the name of the service in a "friendly" (suitable for
1424  * humans) form. If @brief is %TRUE, this should be a brief description
1425  * such as for use in the folder tree. If @brief is %FALSE, it should
1426  * be a more complete and mostly unambiguous description.
1427  *
1428  * Returns: a description of the service which the caller must free
1429  **/
1430 gchar *
camel_service_get_name(CamelService * service,gboolean brief)1431 camel_service_get_name (CamelService *service,
1432                         gboolean brief)
1433 {
1434 	CamelServiceClass *class;
1435 
1436 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1437 
1438 	class = CAMEL_SERVICE_GET_CLASS (service);
1439 	g_return_val_if_fail (class != NULL, NULL);
1440 	g_return_val_if_fail (class->get_name != NULL, NULL);
1441 
1442 	return class->get_name (service, brief);
1443 }
1444 
1445 /**
1446  * camel_service_get_provider:
1447  * @service: a #CamelService
1448  *
1449  * Gets the #CamelProvider associated with the service.
1450  *
1451  * Returns: the #CamelProvider
1452  **/
1453 CamelProvider *
camel_service_get_provider(CamelService * service)1454 camel_service_get_provider (CamelService *service)
1455 {
1456 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1457 
1458 	return service->priv->provider;
1459 }
1460 
1461 /**
1462  * camel_service_ref_proxy_resolver:
1463  * @service: a #CamelService
1464  *
1465  * Returns the #GProxyResolver for @service.  If an application needs to
1466  * override this, it should do so prior to calling functions on @service
1467  * that may require a network connection.
1468  *
1469  * The returned #GProxyResolver is referenced for thread-safety and must
1470  * be unreferenced with g_object_unref() when finished with it.
1471  *
1472  * Returns: (transfer full): a #GProxyResolver, or %NULL
1473  *
1474  * Since: 3.12
1475  **/
1476 GProxyResolver *
camel_service_ref_proxy_resolver(CamelService * service)1477 camel_service_ref_proxy_resolver (CamelService *service)
1478 {
1479 	GProxyResolver *proxy_resolver = NULL;
1480 
1481 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1482 
1483 	g_mutex_lock (&service->priv->property_lock);
1484 
1485 	if (service->priv->proxy_resolver != NULL)
1486 		proxy_resolver = g_object_ref (service->priv->proxy_resolver);
1487 
1488 	g_mutex_unlock (&service->priv->property_lock);
1489 
1490 	return proxy_resolver;
1491 }
1492 
1493 /**
1494  * camel_service_set_proxy_resolver:
1495  * @service: a #CamelService
1496  * @proxy_resolver: a #GProxyResolver, or %NULL for the default
1497  *
1498  * Sets the #GProxyResolver for @service.  If an application needs to
1499  * override this, it should do so prior to calling functions on @service
1500  * that may require a network connection.
1501  *
1502  * Since: 3.12
1503  **/
1504 void
camel_service_set_proxy_resolver(CamelService * service,GProxyResolver * proxy_resolver)1505 camel_service_set_proxy_resolver (CamelService *service,
1506                                   GProxyResolver *proxy_resolver)
1507 {
1508 	gboolean notify = FALSE;
1509 
1510 	if (proxy_resolver == NULL)
1511 		proxy_resolver = g_proxy_resolver_get_default ();
1512 
1513 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1514 	g_return_if_fail (G_IS_PROXY_RESOLVER (proxy_resolver));
1515 
1516 	g_mutex_lock (&service->priv->property_lock);
1517 
1518 	/* Emitting a "notify" signal unnecessarily might have
1519 	 * unwanted side effects like cancelling a SoupMessage.
1520 	 * Only emit if we now have a different GProxyResolver. */
1521 
1522 	if (proxy_resolver != service->priv->proxy_resolver) {
1523 		g_clear_object (&service->priv->proxy_resolver);
1524 		service->priv->proxy_resolver = proxy_resolver;
1525 
1526 		if (proxy_resolver != NULL)
1527 			g_object_ref (proxy_resolver);
1528 
1529 		notify = TRUE;
1530 	}
1531 
1532 	g_mutex_unlock (&service->priv->property_lock);
1533 
1534 	if (notify)
1535 		g_object_notify (G_OBJECT (service), "proxy-resolver");
1536 }
1537 
1538 /**
1539  * camel_service_ref_session:
1540  * @service: a #CamelService
1541  *
1542  * Returns the #CamelSession associated with the service.
1543  *
1544  * The returned #CamelSession is referenced for thread-safety.  Unreference
1545  * the #CamelSession with g_object_unref() when finished with it.
1546  *
1547  * Returns: (transfer full) (type CamelSession): the #CamelSession
1548  *
1549  * Since: 3.8
1550  **/
1551 CamelSession *
camel_service_ref_session(CamelService * service)1552 camel_service_ref_session (CamelService *service)
1553 {
1554 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1555 
1556 	return g_weak_ref_get (&service->priv->session);
1557 }
1558 
1559 /**
1560  * camel_service_ref_settings:
1561  * @service: a #CamelService
1562  *
1563  * Returns the #CamelSettings instance associated with the service.
1564  *
1565  * The returned #CamelSettings is referenced for thread-safety and must
1566  * be unreferenced with g_object_unref() when finished with it.
1567  *
1568  * Returns: (transfer full): the #CamelSettings
1569  *
1570  * Since: 3.6
1571  **/
1572 CamelSettings *
camel_service_ref_settings(CamelService * service)1573 camel_service_ref_settings (CamelService *service)
1574 {
1575 	CamelSettings *settings;
1576 
1577 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1578 
1579 	/* Every service should have a settings object. */
1580 	g_return_val_if_fail (service->priv->settings != NULL, NULL);
1581 
1582 	g_mutex_lock (&service->priv->property_lock);
1583 
1584 	settings = g_object_ref (service->priv->settings);
1585 
1586 	g_mutex_unlock (&service->priv->property_lock);
1587 
1588 	return settings;
1589 }
1590 
1591 /**
1592  * camel_service_set_settings:
1593  * @service: a #CamelService
1594  * @settings: an instance derviced from #CamelSettings, or %NULL
1595  *
1596  * Associates a new #CamelSettings instance with the service.
1597  * The @settings instance must match the settings type defined in
1598  * #CamelServiceClass.  If @settings is %NULL, a new #CamelSettings
1599  * instance of the appropriate type is created with all properties
1600  * set to defaults.
1601  *
1602  * Since: 3.2
1603  **/
1604 void
camel_service_set_settings(CamelService * service,CamelSettings * settings)1605 camel_service_set_settings (CamelService *service,
1606                             CamelSettings *settings)
1607 {
1608 	CamelServiceClass *class;
1609 
1610 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1611 
1612 	class = CAMEL_SERVICE_GET_CLASS (service);
1613 	g_return_if_fail (class != NULL);
1614 
1615 	if (settings != NULL) {
1616 		g_return_if_fail (
1617 			g_type_is_a (
1618 				G_OBJECT_TYPE (settings),
1619 				class->settings_type));
1620 		g_object_ref (settings);
1621 
1622 	} else {
1623 		g_return_if_fail (
1624 			g_type_is_a (
1625 				class->settings_type,
1626 				CAMEL_TYPE_SETTINGS));
1627 		settings = g_object_new (class->settings_type, NULL);
1628 	}
1629 
1630 	g_mutex_lock (&service->priv->property_lock);
1631 
1632 	if (service->priv->settings != NULL)
1633 		g_object_unref (service->priv->settings);
1634 
1635 	service->priv->settings = settings;  /* takes ownership */
1636 
1637 	g_mutex_unlock (&service->priv->property_lock);
1638 
1639 	/* If the service is a CamelNetworkService, it needs to
1640 	 * replace its GSocketConnectable for the new settings. */
1641 	if (service->priv->network_service_inited)
1642 		camel_network_service_set_connectable (
1643 			CAMEL_NETWORK_SERVICE (service), NULL);
1644 
1645 	g_object_notify (G_OBJECT (service), "settings");
1646 }
1647 
1648 /**
1649  * camel_service_get_uid:
1650  * @service: a #CamelService
1651  *
1652  * Gets the unique identifier string associated with the service.
1653  *
1654  * Returns: the UID string
1655  *
1656  * Since: 3.2
1657  **/
1658 const gchar *
camel_service_get_uid(CamelService * service)1659 camel_service_get_uid (CamelService *service)
1660 {
1661 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1662 
1663 	return service->priv->uid;
1664 }
1665 
1666 /**
1667  * camel_service_queue_task:
1668  * @service: a #CamelService
1669  * @task: a #GTask
1670  * @task_func: (scope async): function to call when @task is dispatched
1671  *
1672  * Adds @task to a queue of waiting tasks with the same source object.
1673  * Queued tasks execute one at a time in the order they were added.  When
1674  * @task reaches the front of the queue, it will be dispatched by invoking
1675  * @task_func in a separate thread.  If @task is cancelled while queued,
1676  * it will complete immediately with an appropriate error.
1677  *
1678  * This is primarily intended for use by #CamelStore, #CamelTransport and
1679  * #CamelFolder to achieve ordered invocation of synchronous class methods.
1680  *
1681  * Since: 3.12
1682  **/
1683 void
camel_service_queue_task(CamelService * service,GTask * task,GTaskThreadFunc task_func)1684 camel_service_queue_task (CamelService *service,
1685                           GTask *task,
1686                           GTaskThreadFunc task_func)
1687 {
1688 	DispatchData *dispatch_data;
1689 	gboolean return_on_cancel;
1690 
1691 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1692 	g_return_if_fail (G_IS_TASK (task));
1693 	g_return_if_fail (task_func != NULL);
1694 
1695 	return_on_cancel = g_task_get_return_on_cancel (task);
1696 
1697 	dispatch_data = g_slice_new0 (DispatchData);
1698 	g_weak_ref_init (&dispatch_data->service, service);
1699 	dispatch_data->return_on_cancel = return_on_cancel;
1700 	dispatch_data->task_func = task_func;
1701 
1702 	/* Complete immediately if cancelled while queued. */
1703 	g_task_set_return_on_cancel (task, TRUE);
1704 
1705 	/* Stash this until it's time to dispatch the GTask. */
1706 	g_object_set_data_full (
1707 		G_OBJECT (task), DISPATCH_DATA_KEY,
1708 		dispatch_data, (GDestroyNotify) dispatch_data_free);
1709 
1710 	service_task_table_push (service, task);
1711 }
1712 
1713 /**
1714  * camel_service_connect_sync:
1715  * @service: a #CamelService
1716  * @cancellable: optional #GCancellable object, or %NULL
1717  * @error: return location for a #GError, or %NULL
1718  *
1719  * Connects @service to a remote server using the information in its
1720  * #CamelService:settings instance.
1721  *
1722  * If a connect operation is already in progress when this function is
1723  * called, its results will be reflected in this connect operation.
1724  *
1725  * Returns: %TRUE if the connection is made or %FALSE otherwise
1726  *
1727  * Since: 3.6
1728  **/
1729 gboolean
camel_service_connect_sync(CamelService * service,GCancellable * cancellable,GError ** error)1730 camel_service_connect_sync (CamelService *service,
1731                             GCancellable *cancellable,
1732                             GError **error)
1733 {
1734 	CamelAsyncClosure *closure;
1735 	GAsyncResult *result;
1736 	gboolean success;
1737 
1738 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1739 
1740 	closure = camel_async_closure_new ();
1741 
1742 	camel_service_connect (
1743 		service, G_PRIORITY_DEFAULT, cancellable,
1744 		camel_async_closure_callback, closure);
1745 
1746 	result = camel_async_closure_wait (closure);
1747 
1748 	success = camel_service_connect_finish (service, result, error);
1749 
1750 	camel_async_closure_free (closure);
1751 
1752 	return success;
1753 }
1754 
1755 /**
1756  * camel_service_connect:
1757  * @service: a #CamelService
1758  * @io_priority: the I/O priority of the request
1759  * @cancellable: optional #GCancellable object, or %NULL
1760  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1761  * @user_data: data to pass to the callback function
1762  *
1763  * Asynchronously connects @service to a remote server using the information
1764  * in its #CamelService:settings instance.
1765  *
1766  * If a connect operation is already in progress when this function is
1767  * called, its results will be reflected in this connect operation.
1768  *
1769  * If any disconnect operations are in progress when this function is
1770  * called, they will be cancelled.
1771  *
1772  * When the operation is finished, @callback will be called.  You can
1773  * then call camel_service_connect_finish() to get the result of the
1774  * operation.
1775  *
1776  * Since: 3.6
1777  **/
1778 void
camel_service_connect(CamelService * service,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1779 camel_service_connect (CamelService *service,
1780                        gint io_priority,
1781                        GCancellable *cancellable,
1782                        GAsyncReadyCallback callback,
1783                        gpointer user_data)
1784 {
1785 	GTask *task;
1786 	ConnectionOp *op;
1787 
1788 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1789 
1790 	cancellable = camel_operation_new_proxy (cancellable);
1791 
1792 	task = g_task_new (service, cancellable, callback, user_data);
1793 	g_task_set_source_tag (task, camel_service_connect);
1794 	g_task_set_priority (task, io_priority);
1795 
1796 	g_mutex_lock (&service->priv->connection_lock);
1797 
1798 	switch (service->priv->status) {
1799 
1800 		/* If a connect operation is already in progress,
1801 		 * queue this operation so it completes at the same
1802 		 * time the first connect operation completes. */
1803 		case CAMEL_SERVICE_CONNECTING:
1804 			connection_op_add_pending (
1805 				service->priv->connection_op,
1806 				task, cancellable);
1807 			break;
1808 
1809 		/* If we're already connected, just report success. */
1810 		case CAMEL_SERVICE_CONNECTED:
1811 			g_task_return_boolean (task, TRUE);
1812 			break;
1813 
1814 		/* If a disconnect operation is currently in progress,
1815 		 * cancel it and make room for the connect operation. */
1816 		case CAMEL_SERVICE_DISCONNECTING:
1817 			g_return_if_fail (
1818 				service->priv->connection_op != NULL);
1819 			g_cancellable_cancel (
1820 				service->priv->connection_op->cancellable);
1821 			connection_op_unref (service->priv->connection_op);
1822 			service->priv->connection_op = NULL;
1823 			/* fall through */
1824 
1825 		/* Start a new connect operation.  Subsequent connect
1826 		 * operations are queued until this operation completes
1827 		 * and will share this operation's result. */
1828 		case CAMEL_SERVICE_DISCONNECTED:
1829 			g_return_if_fail (
1830 				service->priv->connection_op == NULL);
1831 
1832 			op = connection_op_new (task, cancellable);
1833 			service->priv->connection_op = op;
1834 
1835 			service->priv->status = CAMEL_SERVICE_CONNECTING;
1836 			service_queue_notify_connection_status (service);
1837 
1838 			service_shared_connect (service, io_priority, op);
1839 			break;
1840 
1841 		default:
1842 			g_warn_if_reached ();
1843 	}
1844 
1845 	g_mutex_unlock (&service->priv->connection_lock);
1846 
1847 	g_object_unref (cancellable);
1848 	g_object_unref (task);
1849 }
1850 
1851 /**
1852  * camel_service_connect_finish:
1853  * @service: a #CamelService
1854  * @result: a #GAsyncResult
1855  * @error: return location for a #GError, or %NULL
1856  *
1857  * Finishes the operation started with camel_service_connect().
1858  *
1859  * Returns: %TRUE if the connection was made or %FALSE otherwise
1860  *
1861  * Since: 3.6
1862  **/
1863 gboolean
camel_service_connect_finish(CamelService * service,GAsyncResult * result,GError ** error)1864 camel_service_connect_finish (CamelService *service,
1865                               GAsyncResult *result,
1866                               GError **error)
1867 {
1868 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1869 	g_return_val_if_fail (g_task_is_valid (result, service), FALSE);
1870 
1871 	g_return_val_if_fail (
1872 		g_async_result_is_tagged (
1873 		result, camel_service_connect), FALSE);
1874 
1875 	return g_task_propagate_boolean (G_TASK (result), error);
1876 }
1877 
1878 /**
1879  * camel_service_disconnect_sync:
1880  * @service: a #CamelService
1881  * @clean: whether or not to try to disconnect cleanly
1882  * @cancellable: optional #GCancellable object, or %NULL
1883  * @error: return location for a #GError, or %NULL
1884  *
1885  * Disconnect from the service. If @clean is %FALSE, it should not
1886  * try to do any synchronizing or other cleanup of the connection.
1887  *
1888  * If a disconnect operation is already in progress when this function is
1889  * called, its results will be reflected in this disconnect operation.
1890  *
1891  * If any connect operations are in progress when this function is called,
1892  * they will be cancelled.
1893  *
1894  * Returns: %TRUE if the connection was severed or %FALSE otherwise
1895  *
1896  * Since: 3.6
1897  **/
1898 gboolean
camel_service_disconnect_sync(CamelService * service,gboolean clean,GCancellable * cancellable,GError ** error)1899 camel_service_disconnect_sync (CamelService *service,
1900                                gboolean clean,
1901                                GCancellable *cancellable,
1902                                GError **error)
1903 {
1904 	CamelAsyncClosure *closure;
1905 	GAsyncResult *result;
1906 	gboolean success;
1907 
1908 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1909 
1910 	closure = camel_async_closure_new ();
1911 
1912 	camel_service_disconnect (
1913 		service, clean, G_PRIORITY_DEFAULT, cancellable,
1914 		camel_async_closure_callback, closure);
1915 
1916 	result = camel_async_closure_wait (closure);
1917 
1918 	success = camel_service_disconnect_finish (service, result, error);
1919 
1920 	camel_async_closure_free (closure);
1921 
1922 	return success;
1923 }
1924 
1925 /**
1926  * camel_service_disconnect:
1927  * @service: a #CamelService
1928  * @clean: whether or not to try to disconnect cleanly
1929  * @io_priority: the I/O priority of the request
1930  * @cancellable: optional #GCancellable object, or %NULL
1931  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1932  * @user_data: data to pass to the callback function
1933  *
1934  * If a disconnect operation is already in progress when this function is
1935  * called, its results will be reflected in this disconnect operation.
1936  *
1937  * If any connect operations are in progress when this function is called,
1938  * they will be cancelled.
1939  *
1940  * When the operation is finished, @callback will be called.  You can
1941  * then call camel_service_disconnect_finish() to get the result of the
1942  * operation.
1943  *
1944  * Since: 3.6
1945  **/
1946 void
camel_service_disconnect(CamelService * service,gboolean clean,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1947 camel_service_disconnect (CamelService *service,
1948                           gboolean clean,
1949                           gint io_priority,
1950                           GCancellable *cancellable,
1951                           GAsyncReadyCallback callback,
1952                           gpointer user_data)
1953 {
1954 	GTask *task;
1955 	ConnectionOp *op;
1956 
1957 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1958 
1959 	cancellable = camel_operation_new_proxy (cancellable);
1960 
1961 	task = g_task_new (service, cancellable, callback, user_data);
1962 	g_task_set_source_tag (task, camel_service_disconnect);
1963 	g_task_set_priority (task, io_priority);
1964 
1965 	g_mutex_lock (&service->priv->connection_lock);
1966 
1967 	switch (service->priv->status) {
1968 
1969 		/* If a connect operation is currently in progress,
1970 		 * cancel it and make room for the disconnect operation. */
1971 		case CAMEL_SERVICE_CONNECTING:
1972 			g_return_if_fail (
1973 				service->priv->connection_op != NULL);
1974 			g_cancellable_cancel (
1975 				service->priv->connection_op->cancellable);
1976 			connection_op_unref (service->priv->connection_op);
1977 			service->priv->connection_op = NULL;
1978 			/* fall through */
1979 
1980 		/* Start a new disconnect operation.  Subsequent disconnect
1981 		 * operations are queued until this operation completes and
1982 		 * will share this operation's result. */
1983 		case CAMEL_SERVICE_CONNECTED:
1984 			g_return_if_fail (
1985 				service->priv->connection_op == NULL);
1986 
1987 			op = connection_op_new (task, cancellable);
1988 			service->priv->connection_op = op;
1989 
1990 			/* Do not change the status if CONNECTING, in case a
1991 			 * provider calls disconnect() during the connection
1992 			 * phase, which confuses the other logic here and
1993 			 * effectively makes the service's connection state
1994 			 * CONNECTED instead of DISCONNECTED at the end. */
1995 			if (service->priv->status != CAMEL_SERVICE_CONNECTING) {
1996 				service->priv->status = CAMEL_SERVICE_DISCONNECTING;
1997 				service_queue_notify_connection_status (service);
1998 			}
1999 
2000 			service_shared_disconnect (
2001 				service, clean, io_priority, op);
2002 			break;
2003 
2004 		/* If a disconnect operation is already in progress,
2005 		 * queue this operation so it completes at the same
2006 		 * time the first disconnect operation completes. */
2007 		case CAMEL_SERVICE_DISCONNECTING:
2008 			connection_op_add_pending (
2009 				service->priv->connection_op,
2010 				task, cancellable);
2011 			break;
2012 
2013 		/* If we're already disconnected, just report success. */
2014 		case CAMEL_SERVICE_DISCONNECTED:
2015 			g_task_return_boolean (task, TRUE);
2016 			break;
2017 
2018 		default:
2019 			g_warn_if_reached ();
2020 	}
2021 
2022 	g_mutex_unlock (&service->priv->connection_lock);
2023 
2024 	g_object_unref (cancellable);
2025 	g_object_unref (task);
2026 }
2027 
2028 /**
2029  * camel_service_disconnect_finish:
2030  * @service: a #CamelService
2031  * @result: a #GAsyncResult
2032  * @error: return location for a #GError, or %NULL
2033  *
2034  * Finishes the operation started with camel_service_disconnect().
2035  *
2036  * Returns: %TRUE if the connection was severed or %FALSE otherwise
2037  *
2038  * Since: 3.6
2039  **/
2040 gboolean
camel_service_disconnect_finish(CamelService * service,GAsyncResult * result,GError ** error)2041 camel_service_disconnect_finish (CamelService *service,
2042                                  GAsyncResult *result,
2043                                  GError **error)
2044 {
2045 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
2046 	g_return_val_if_fail (g_task_is_valid (result, service), FALSE);
2047 
2048 	g_return_val_if_fail (
2049 		g_async_result_is_tagged (
2050 		result, camel_service_disconnect), FALSE);
2051 
2052 	return g_task_propagate_boolean (G_TASK (result), error);
2053 }
2054 
2055 /**
2056  * camel_service_authenticate_sync:
2057  * @service: a #CamelService
2058  * @mechanism: (nullable): a SASL mechanism name, or %NULL
2059  * @cancellable: optional #GCancellable object, or %NULL
2060  * @error: return location for a #GError, or %NULL
2061  *
2062  * Attempts to authenticate @service using @mechanism and, if necessary,
2063  * @service's #CamelService:password property.  The function makes only
2064  * ONE attempt at authentication and does not loop.
2065  *
2066  * If the authentication attempt completed and the server accepted the
2067  * credentials, the function returns #CAMEL_AUTHENTICATION_ACCEPTED.
2068  *
2069  * If the authentication attempt completed but the server rejected the
2070  * credentials, the function returns #CAMEL_AUTHENTICATION_REJECTED.
2071  *
2072  * If the authentication attempt failed to complete due to a network
2073  * communication issue or some other mishap, the function sets @error
2074  * and returns #CAMEL_AUTHENTICATION_ERROR.
2075  *
2076  * Generally this function should only be called from a #CamelSession
2077  * subclass in order to implement its own authentication loop.
2078  *
2079  * Returns: the authentication result
2080  *
2081  * Since: 3.4
2082  **/
2083 CamelAuthenticationResult
camel_service_authenticate_sync(CamelService * service,const gchar * mechanism,GCancellable * cancellable,GError ** error)2084 camel_service_authenticate_sync (CamelService *service,
2085                                  const gchar *mechanism,
2086                                  GCancellable *cancellable,
2087                                  GError **error)
2088 {
2089 	CamelServiceClass *class;
2090 	CamelAuthenticationResult result;
2091 
2092 	g_return_val_if_fail (
2093 		CAMEL_IS_SERVICE (service),
2094 		CAMEL_AUTHENTICATION_ERROR);
2095 
2096 	class = CAMEL_SERVICE_GET_CLASS (service);
2097 	g_return_val_if_fail (class != NULL, CAMEL_AUTHENTICATION_ERROR);
2098 	g_return_val_if_fail (class->authenticate_sync != NULL, CAMEL_AUTHENTICATION_ERROR);
2099 
2100 	result = class->authenticate_sync (
2101 		service, mechanism, cancellable, error);
2102 	CAMEL_CHECK_GERROR (
2103 		service, authenticate_sync,
2104 		result != CAMEL_AUTHENTICATION_ERROR, error);
2105 
2106 	return result;
2107 }
2108 
2109 /* Helper for camel_service_authenticate() */
2110 static void
service_authenticate_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)2111 service_authenticate_thread (GTask *task,
2112                              gpointer source_object,
2113                              gpointer task_data,
2114                              GCancellable *cancellable)
2115 {
2116 	CamelAuthenticationResult auth_result;
2117 	AsyncContext *async_context;
2118 	GError *local_error = NULL;
2119 
2120 	async_context = (AsyncContext *) task_data;
2121 
2122 	auth_result = camel_service_authenticate_sync (
2123 		CAMEL_SERVICE (source_object),
2124 		async_context->auth_mechanism,
2125 		cancellable, &local_error);
2126 
2127 	if (local_error != NULL) {
2128 		g_task_return_error (task, local_error);
2129 	} else {
2130 		g_task_return_int (task, auth_result);
2131 	}
2132 }
2133 
2134 /**
2135  * camel_service_authenticate:
2136  * @service: a #CamelService
2137  * @mechanism: (nullable): a SASL mechanism name, or %NULL
2138  * @io_priority: the I/O priority of the request
2139  * @cancellable: optional #GCancellable object, or %NULL
2140  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2141  * @user_data: data to pass to the callback function
2142  *
2143  * Asynchronously attempts to authenticate @service using @mechanism and,
2144  * if necessary, @service's #CamelService:password property.  The function
2145  * makes only ONE attempt at authentication and does not loop.
2146  *
2147  * Generally this function should only be called from a #CamelSession
2148  * subclass in order to implement its own authentication loop.
2149  *
2150  * When the operation is finished, @callback will be called.  You can
2151  * then call camel_service_authenticate_finish() to get the result of
2152  * the operation.
2153  *
2154  * Since: 3.4
2155  **/
2156 void
camel_service_authenticate(CamelService * service,const gchar * mechanism,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2157 camel_service_authenticate (CamelService *service,
2158                             const gchar *mechanism,
2159                             gint io_priority,
2160                             GCancellable *cancellable,
2161                             GAsyncReadyCallback callback,
2162                             gpointer user_data)
2163 {
2164 	GTask *task;
2165 	AsyncContext *async_context;
2166 
2167 	g_return_if_fail (CAMEL_IS_SERVICE (service));
2168 
2169 	async_context = g_slice_new0 (AsyncContext);
2170 	async_context->auth_mechanism = g_strdup (mechanism);
2171 
2172 	task = g_task_new (service, cancellable, callback, user_data);
2173 	g_task_set_source_tag (task, camel_service_authenticate);
2174 	g_task_set_priority (task, io_priority);
2175 
2176 	g_task_set_task_data (
2177 		task, async_context,
2178 		(GDestroyNotify) async_context_free);
2179 
2180 	g_task_run_in_thread (task, service_authenticate_thread);
2181 
2182 	g_object_unref (task);
2183 }
2184 
2185 /**
2186  * camel_service_authenticate_finish:
2187  * @service: a #CamelService
2188  * @result: a #GAsyncResult
2189  * @error: return location for a #GError, or %NULL
2190  *
2191  * Finishes the operation started with camel_service_authenticate().
2192  *
2193  * If the authentication attempt completed and the server accepted the
2194  * credentials, the function returns #CAMEL_AUTHENTICATION_ACCEPTED.
2195  *
2196  * If the authentication attempt completed but the server rejected the
2197  * credentials, the function returns #CAMEL_AUTHENTICATION_REJECTED.
2198  *
2199  * If the authentication attempt failed to complete due to a network
2200  * communication issue or some other mishap, the function sets @error
2201  * and returns #CAMEL_AUTHENTICATION_ERROR.
2202  *
2203  * Returns: the authentication result
2204  *
2205  * Since: 3.4
2206  **/
2207 CamelAuthenticationResult
camel_service_authenticate_finish(CamelService * service,GAsyncResult * result,GError ** error)2208 camel_service_authenticate_finish (CamelService *service,
2209                                    GAsyncResult *result,
2210                                    GError **error)
2211 {
2212 	CamelAuthenticationResult auth_result;
2213 
2214 	g_return_val_if_fail (
2215 		CAMEL_IS_SERVICE (service),
2216 		CAMEL_AUTHENTICATION_ERROR);
2217 	g_return_val_if_fail (
2218 		g_task_is_valid (result, service),
2219 		CAMEL_AUTHENTICATION_ERROR);
2220 
2221 	g_return_val_if_fail (
2222 		g_async_result_is_tagged (
2223 		result, camel_service_authenticate),
2224 		CAMEL_AUTHENTICATION_ERROR);
2225 
2226 	/* XXX A little hackish, but best way to return enum values
2227 	 *     from GTask in GLib 2.36.  Recommended by Dan Winship. */
2228 
2229 	auth_result = g_task_propagate_int (G_TASK (result), error);
2230 
2231 	if (auth_result == (CamelAuthenticationResult) -1)
2232 		return CAMEL_AUTHENTICATION_ERROR;
2233 
2234 	return auth_result;
2235 }
2236 
2237 /**
2238  * camel_service_query_auth_types_sync:
2239  * @service: a #CamelService
2240  * @cancellable: optional #GCancellable object, or %NULL
2241  * @error: return location for a #GError, or %NULL
2242  *
2243  * Obtains a list of authentication types supported by @service.
2244  * Free the returned list with g_list_free().
2245  *
2246  * Returns: (element-type CamelServiceAuthType) (transfer container): a list of #CamelServiceAuthType structs
2247  **/
2248 GList *
camel_service_query_auth_types_sync(CamelService * service,GCancellable * cancellable,GError ** error)2249 camel_service_query_auth_types_sync (CamelService *service,
2250                                      GCancellable *cancellable,
2251                                      GError **error)
2252 {
2253 	CamelServiceClass *class;
2254 
2255 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
2256 
2257 	class = CAMEL_SERVICE_GET_CLASS (service);
2258 	g_return_val_if_fail (class != NULL, NULL);
2259 	g_return_val_if_fail (class->query_auth_types_sync != NULL, NULL);
2260 
2261 	return class->query_auth_types_sync (service, cancellable, error);
2262 }
2263 
2264 /* Helper for camel_service_query_auth_types() */
2265 static void
service_query_auth_types_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)2266 service_query_auth_types_thread (GTask *task,
2267                                  gpointer source_object,
2268                                  gpointer task_data,
2269                                  GCancellable *cancellable)
2270 {
2271 	GList *auth_types;
2272 	GError *local_error = NULL;
2273 
2274 	auth_types = camel_service_query_auth_types_sync (
2275 		CAMEL_SERVICE (source_object),
2276 		cancellable, &local_error);
2277 
2278 	if (local_error != NULL) {
2279 		g_warn_if_fail (auth_types == NULL);
2280 		g_task_return_error (task, local_error);
2281 	} else {
2282 		g_task_return_pointer (
2283 			task, auth_types,
2284 			(GDestroyNotify) g_list_free);
2285 	}
2286 }
2287 
2288 /**
2289  * camel_service_query_auth_types:
2290  * @service: a #CamelService
2291  * @io_priority: the I/O priority of the request
2292  * @cancellable: optional #GCancellable object, or %NULL
2293  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2294  * @user_data: data to pass to the callback function
2295  *
2296  * Asynchronously obtains a list of authentication types supported by
2297  * @service.
2298  *
2299  * When the operation is finished, @callback will be called.  You can
2300  * then call camel_service_query_auth_types_finish() to get the result
2301  * of the operation.
2302  *
2303  * Since: 3.2
2304  **/
2305 void
camel_service_query_auth_types(CamelService * service,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2306 camel_service_query_auth_types (CamelService *service,
2307                                 gint io_priority,
2308                                 GCancellable *cancellable,
2309                                 GAsyncReadyCallback callback,
2310                                 gpointer user_data)
2311 {
2312 	GTask *task;
2313 
2314 	g_return_if_fail (CAMEL_IS_SERVICE (service));
2315 
2316 	task = g_task_new (service, cancellable, callback, user_data);
2317 	g_task_set_source_tag (task, camel_service_query_auth_types);
2318 	g_task_set_priority (task, io_priority);
2319 
2320 	g_task_run_in_thread (task, service_query_auth_types_thread);
2321 
2322 	g_object_unref (task);
2323 }
2324 
2325 /**
2326  * camel_service_query_auth_types_finish:
2327  * @service: a #CamelService
2328  * @result: a #GAsyncResult
2329  * @error: return location for a #GError, or %NULL
2330  *
2331  * Finishes the operation started with camel_service_query_auth_types().
2332  * Free the returned list with g_list_free().
2333  *
2334  * Returns: (element-type CamelServiceAuthType) (transfer container): a list of #CamelServiceAuthType structs
2335  *
2336  * Since: 3.2
2337  **/
2338 GList *
camel_service_query_auth_types_finish(CamelService * service,GAsyncResult * result,GError ** error)2339 camel_service_query_auth_types_finish (CamelService *service,
2340                                        GAsyncResult *result,
2341                                        GError **error)
2342 {
2343 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
2344 	g_return_val_if_fail (g_task_is_valid (result, service), NULL);
2345 
2346 	g_return_val_if_fail (
2347 		g_async_result_is_tagged (
2348 		result, camel_service_query_auth_types), NULL);
2349 
2350 	return g_task_propagate_pointer (G_TASK (result), error);
2351 }
2352 
2353 /**
2354  * camel_service_auth_type_copy:
2355  * @service_auth_type: a #CamelServiceAuthType
2356  *
2357  * Copies the @service_auth_type struct.
2358  * Does nothing and returns the given object in reality, needed for the introspection.
2359  *
2360  * Returns: (transfer full): the copy of @service_auth_type
2361  *
2362  * Since: 3.24
2363  **/
2364 CamelServiceAuthType *
camel_service_auth_type_copy(const CamelServiceAuthType * service_auth_type)2365 camel_service_auth_type_copy (const CamelServiceAuthType *service_auth_type)
2366 {
2367 	/* This is needed for the introspection.
2368 	 * In the reality, each CamelSasl subclass has a static reference of it.
2369 	 */
2370 	return (CamelServiceAuthType *) service_auth_type;
2371 }
2372 
2373 /**
2374  * camel_service_auth_type_free:
2375  * @service_auth_type: a #CamelServiceAuthType
2376  *
2377  * Frees the @service_auth_type struct.
2378  * Does nothing in reality, needed for the introspection.
2379  *
2380  * Since: 3.24
2381  **/
2382 void
camel_service_auth_type_free(CamelServiceAuthType * service_auth_type)2383 camel_service_auth_type_free (CamelServiceAuthType *service_auth_type)
2384 {
2385 	/* This is needed for the introspection.
2386 	 * In the reality, each CamelSasl subclass has a static reference of it.
2387 	 */
2388 }
2389