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