1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-service.c : Abstract class for an email service */
7 * Bertrand Guiheneuf <bertrand@helixcode.com>
9 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of version 2 of the GNU Lesser General Public
13 * License as published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
35 #include <glib/gi18n-lib.h>
37 #include "camel-debug.h"
38 #include "camel-operation.h"
39 #include "camel-service.h"
40 #include "camel-session.h"
45 #define CAMEL_SERVICE_GET_PRIVATE(obj) \
46 (G_TYPE_INSTANCE_GET_PRIVATE \
47 ((obj), CAMEL_TYPE_SERVICE, CamelServicePrivate))
49 struct _CamelServicePrivate {
50 GStaticRecMutex connect_lock; /* for locking connection operations */
51 GStaticMutex connect_op_lock; /* for locking the connection_op */
54 G_DEFINE_ABSTRACT_TYPE (CamelService, camel_service, CAMEL_TYPE_OBJECT)
57 service_finalize (GObject *object)
59 CamelService *service = CAMEL_SERVICE (object);
61 if (service->status == CAMEL_SERVICE_CONNECTED)
62 CAMEL_SERVICE_GET_CLASS (service)->disconnect (service, TRUE, NULL);
65 camel_url_free (service->url);
68 g_object_unref (service->session);
70 g_static_rec_mutex_free (&service->priv->connect_lock);
71 g_static_mutex_free (&service->priv->connect_op_lock);
73 /* Chain up to parent's finalize() method. */
74 G_OBJECT_CLASS (camel_service_parent_class)->finalize (object);
78 service_construct (CamelService *service,
79 CamelSession *session,
80 CamelProvider *provider,
84 gchar *err, *url_string;
86 if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_USER) &&
87 (url->user == NULL || url->user[0] == '\0')) {
88 err = _("URL '%s' needs a username component");
90 } else if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_HOST) &&
91 (url->host == NULL || url->host[0] == '\0')) {
92 err = _("URL '%s' needs a host component");
94 } else if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_PATH) &&
95 (url->path == NULL || url->path[0] == '\0')) {
96 err = _("URL '%s' needs a path component");
100 service->provider = provider;
101 service->url = camel_url_copy (url);
102 service->session = g_object_ref (session);
104 service->status = CAMEL_SERVICE_DISCONNECTED;
109 url_string = camel_url_to_string (url, CAMEL_URL_HIDE_PASSWORD);
111 error, CAMEL_SERVICE_ERROR,
112 CAMEL_SERVICE_ERROR_URL_INVALID,
120 service_connect (CamelService *service,
123 /* Things like the CamelMboxStore can validly
124 * not define a connect function. */
129 service_disconnect (CamelService *service,
133 /* We let people get away with not having a disconnect
134 * function -- CamelMboxStore, for example. */
139 service_cancel_connect (CamelService *service)
141 camel_operation_cancel (service->connect_op);
145 service_query_auth_types (CamelService *service,
152 service_get_name (CamelService *service,
156 "%s does not implement CamelServiceClass::get_name()",
157 G_OBJECT_TYPE_NAME (service));
159 return g_strdup (G_OBJECT_TYPE_NAME (service));
163 service_get_path (CamelService *service)
165 CamelProvider *prov = service->provider;
166 CamelURL *url = service->url;
170 /* A sort of ad-hoc default implementation that works for our
171 * current set of services.
174 gpath = g_string_new (service->provider->protocol);
175 if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_USER)) {
176 if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_HOST)) {
177 g_string_append_printf (gpath, "/%s@%s",
178 url->user ? url->user : "",
179 url->host ? url->host : "");
182 g_string_append_printf (gpath, ":%d", url->port);
184 g_string_append_printf (gpath, "/%s%s", url->user ? url->user : "",
185 CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_USER) ? "" : "@");
187 } else if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_HOST)) {
188 g_string_append_printf (gpath, "/%s%s",
189 CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_HOST) ? "" : "@",
190 url->host ? url->host : "");
193 g_string_append_printf (gpath, ":%d", url->port);
196 if (CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_PATH))
197 g_string_append_printf (gpath, "%s%s", *url->path == '/' ? "" : "/", url->path);
200 g_string_free (gpath, FALSE);
206 camel_service_class_init (CamelServiceClass *class)
208 GObjectClass *object_class;
210 g_type_class_add_private (class, sizeof (CamelServicePrivate));
212 object_class = G_OBJECT_CLASS (class);
213 object_class->finalize = service_finalize;
215 class->construct = service_construct;
216 class->connect = service_connect;
217 class->disconnect = service_disconnect;
218 class->cancel_connect = service_cancel_connect;
219 class->query_auth_types = service_query_auth_types;
220 class->get_name = service_get_name;
221 class->get_path = service_get_path;
225 camel_service_init (CamelService *service)
227 service->priv = CAMEL_SERVICE_GET_PRIVATE (service);
229 g_static_rec_mutex_init (&service->priv->connect_lock);
230 g_static_mutex_init (&service->priv->connect_op_lock);
234 camel_service_error_quark (void)
236 static GQuark quark = 0;
238 if (G_UNLIKELY (quark == 0)) {
239 const gchar *string = "camel-service-error-quark";
240 quark = g_quark_from_static_string (string);
247 * camel_service_construct:
248 * @service: a #CamelService object
249 * @session: the #CamelSession for @service
250 * @provider: the #CamelProvider associated with @service
251 * @url: the default URL for the service (may be %NULL)
252 * @error: return location for a #GError, or %NULL
254 * Constructs a #CamelService initialized with the given parameters.
256 * Returns: %TRUE on success, %FALSE on failure
259 camel_service_construct (CamelService *service,
260 CamelSession *session,
261 CamelProvider *provider,
265 CamelServiceClass *class;
268 g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
269 g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
271 class = CAMEL_SERVICE_GET_CLASS (service);
272 g_return_val_if_fail (class->construct != NULL, FALSE);
274 success = class->construct (service, session, provider, url, error);
275 CAMEL_CHECK_GERROR (service, construct, success, error);
281 * camel_service_connect:
282 * @service: a #CamelService object
283 * @error: return location for a #GError, or %NULL
285 * Connect to the service using the parameters it was initialized
288 * Returns: %TRUE if the connection is made or %FALSE otherwise
291 camel_service_connect (CamelService *service,
294 CamelServiceClass *class;
295 gboolean ret = FALSE;
296 gboolean unreg = FALSE;
297 CamelOperation *connect_op;
299 g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
300 g_return_val_if_fail (service->session != NULL, FALSE);
301 g_return_val_if_fail (service->url != NULL, FALSE);
303 class = CAMEL_SERVICE_GET_CLASS (service);
304 g_return_val_if_fail (class->connect != NULL, FALSE);
306 camel_service_lock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
308 if (service->status == CAMEL_SERVICE_CONNECTED) {
309 camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
313 /* Register a separate operation for connecting, so that
314 * the offline code can cancel it. */
315 camel_service_lock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
316 service->connect_op = camel_operation_registered ();
317 if (!service->connect_op) {
318 service->connect_op = camel_operation_new ();
319 camel_operation_register (service->connect_op);
322 connect_op = service->connect_op;
323 camel_service_unlock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
325 service->status = CAMEL_SERVICE_CONNECTING;
326 ret = class->connect (service, error);
327 CAMEL_CHECK_GERROR (service, connect, ret, error);
328 service->status = ret ? CAMEL_SERVICE_CONNECTED : CAMEL_SERVICE_DISCONNECTED;
330 camel_service_lock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
332 if (unreg && service->connect_op)
333 camel_operation_unregister ();
335 g_object_unref (connect_op);
336 service->connect_op = NULL;
338 camel_service_unlock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
340 camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
346 * camel_service_disconnect:
347 * @service: a #CamelService object
348 * @clean: whether or not to try to disconnect cleanly
349 * @error: return location for a #GError, or %NULL
351 * Disconnect from the service. If @clean is %FALSE, it should not
352 * try to do any synchronizing or other cleanup of the connection.
354 * Returns: %TRUE if the disconnect was successful or %FALSE otherwise
357 camel_service_disconnect (CamelService *service,
361 CamelServiceClass *class;
365 g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
367 class = CAMEL_SERVICE_GET_CLASS (service);
368 g_return_val_if_fail (class->disconnect != NULL, FALSE);
370 camel_service_lock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
372 if (service->status != CAMEL_SERVICE_DISCONNECTED
373 && service->status != CAMEL_SERVICE_DISCONNECTING) {
374 camel_service_lock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
375 service->connect_op = camel_operation_registered ();
376 if (!service->connect_op) {
377 service->connect_op = camel_operation_new ();
378 camel_operation_register (service->connect_op);
381 camel_service_unlock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
383 service->status = CAMEL_SERVICE_DISCONNECTING;
384 res = class->disconnect (service, clean, error);
385 CAMEL_CHECK_GERROR (service, disconnect, res, error);
386 service->status = CAMEL_SERVICE_DISCONNECTED;
388 camel_service_lock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
390 camel_operation_unregister ();
392 g_object_unref (service->connect_op);
393 service->connect_op = NULL;
394 camel_service_unlock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
397 camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
399 service->status = CAMEL_SERVICE_DISCONNECTED;
405 * camel_service_cancel_connect:
406 * @service: a #CamelService object
408 * If @service is currently attempting to connect to or disconnect
409 * from a server, this causes it to stop and fail. Otherwise it is a
413 camel_service_cancel_connect (CamelService *service)
415 CamelServiceClass *class;
417 g_return_if_fail (CAMEL_IS_SERVICE (service));
419 class = CAMEL_SERVICE_GET_CLASS (service);
420 g_return_if_fail (class->cancel_connect != NULL);
422 camel_service_lock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
423 if (service->connect_op)
424 class->cancel_connect (service);
425 camel_service_unlock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
429 * camel_service_get_url:
430 * @service: a #CamelService object
432 * Gets the URL representing @service. The returned URL must be
433 * freed when it is no longer needed. For security reasons, this
434 * routine does not return the password.
436 * Returns: the URL representing @service
439 camel_service_get_url (CamelService *service)
441 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
443 return camel_url_to_string (service->url, CAMEL_URL_HIDE_PASSWORD);
447 * camel_service_get_name:
448 * @service: a #CamelService object
449 * @brief: whether or not to use a briefer form
451 * This gets the name of the service in a "friendly" (suitable for
452 * humans) form. If @brief is %TRUE, this should be a brief description
453 * such as for use in the folder tree. If @brief is %FALSE, it should
454 * be a more complete and mostly unambiguous description.
456 * Returns: a description of the service which the caller must free
459 camel_service_get_name (CamelService *service,
462 CamelServiceClass *class;
464 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
465 g_return_val_if_fail (service->url, NULL);
467 class = CAMEL_SERVICE_GET_CLASS (service);
468 g_return_val_if_fail (class->get_name != NULL, NULL);
470 return class->get_name (service, brief);
474 * camel_service_get_path:
475 * @service: a #CamelService object
477 * This gets a valid UNIX relative path describing @service, which
478 * is guaranteed to be different from the path returned for any
479 * different service. This path MUST start with the name of the
480 * provider, followed by a "/", but after that, it is up to the
483 * Returns: the path, which the caller must free
486 camel_service_get_path (CamelService *service)
488 CamelServiceClass *class;
490 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
491 g_return_val_if_fail (service->url, NULL);
493 class = CAMEL_SERVICE_GET_CLASS (service);
494 g_return_val_if_fail (class->get_path != NULL, NULL);
496 return class->get_path (service);
500 * camel_service_get_session:
501 * @service: a #CamelService object
503 * Gets the #CamelSession associated with the service.
505 * Returns: the session
508 camel_service_get_session (CamelService *service)
510 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
512 return service->session;
516 * camel_service_get_provider:
517 * @service: a #CamelService object
519 * Gets the #CamelProvider associated with the service.
521 * Returns: the provider
524 camel_service_get_provider (CamelService *service)
526 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
528 return service->provider;
532 * camel_service_query_auth_types:
533 * @service: a #CamelService object
534 * @error: return location for a #GError, or %NULL
536 * This is used by the mail source wizard to get the list of
537 * authentication types supported by the protocol, and information
540 * Returns: a list of #CamelServiceAuthType records. The caller
541 * must free the list with #g_list_free when it is done with it.
544 camel_service_query_auth_types (CamelService *service,
547 CamelServiceClass *class;
550 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
552 class = CAMEL_SERVICE_GET_CLASS (service);
553 g_return_val_if_fail (class->query_auth_types != NULL, NULL);
555 /* Note that we get the connect lock here, which means the
556 * callee must not call the connect functions itself. */
557 camel_service_lock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
558 ret = class->query_auth_types (service, error);
559 camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
565 * camel_service_lock:
566 * @service: a #CamelService
567 * @lock: lock type to lock
569 * Locks #service's #lock. Unlock it with camel_service_unlock().
574 camel_service_lock (CamelService *service,
575 CamelServiceLock lock)
577 g_return_if_fail (CAMEL_IS_SERVICE (service));
580 case CAMEL_SERVICE_REC_CONNECT_LOCK:
581 g_static_rec_mutex_lock (&service->priv->connect_lock);
583 case CAMEL_SERVICE_CONNECT_OP_LOCK:
584 g_static_mutex_lock (&service->priv->connect_op_lock);
587 g_return_if_reached ();
592 * camel_service_unlock:
593 * @service: a #CamelService
594 * @lock: lock type to unlock
596 * Unlocks #service's #lock, previously locked with camel_service_lock().
601 camel_service_unlock (CamelService *service,
602 CamelServiceLock lock)
604 g_return_if_fail (CAMEL_IS_SERVICE (service));
607 case CAMEL_SERVICE_REC_CONNECT_LOCK:
608 g_static_rec_mutex_unlock (&service->priv->connect_lock);
610 case CAMEL_SERVICE_CONNECT_OP_LOCK:
611 g_static_mutex_unlock (&service->priv->connect_op_lock);
614 g_return_if_reached ();