camel-service.c 13 KB
Newer Older
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
/*
5
 *
6
 * Author :
7
 *  Bertrand Guiheneuf <bertrand@helixcode.com>
Bertrand Guiheneuf's avatar
Bertrand Guiheneuf committed
8
 *
9
 * Copyright 1999, 2000 Helix Code, Inc. (http://www.helixcode.com)
10
 *
11 12
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
13 14 15 16 17 18 19 20 21 22 23 24 25
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */
26
#include <config.h>
27
#include "camel-service.h"
28
#include "camel-session.h"
29
#include "camel-exception.h"
30

31 32 33
#include <ctype.h>
#include <stdlib.h>

34 35
#include "camel-private.h"

36
static CamelObjectClass *parent_class = NULL;
37 38

/* Returns the class for a CamelService */
Peter Williams's avatar
Peter Williams committed
39
#define CSERV_CLASS(so) CAMEL_SERVICE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
40

41 42 43
static void construct (CamelService *service, CamelSession *session,
		       CamelProvider *provider, CamelURL *url,
		       CamelException *ex);
44
static gboolean service_connect(CamelService *service, CamelException *ex);
45 46
static gboolean service_disconnect(CamelService *service, gboolean clean,
				   CamelException *ex);
47
/*static gboolean is_connected (CamelService *service);*/
48
static GList *  query_auth_types (CamelService *service, CamelException *ex);
49
static char *   get_name (CamelService *service, gboolean brief);
50
static char *   get_path (CamelService *service);
51

52 53 54 55

static void
camel_service_class_init (CamelServiceClass *camel_service_class)
{
Peter Williams's avatar
Peter Williams committed
56
	parent_class = camel_type_get_global_classfuncs (CAMEL_OBJECT_TYPE);
57

58
	/* virtual method definition */
59
	camel_service_class->construct = construct;
60 61
	camel_service_class->connect = service_connect;
	camel_service_class->disconnect = service_disconnect;
62
	camel_service_class->query_auth_types = query_auth_types;
63
	camel_service_class->get_name = get_name;
64
	camel_service_class->get_path = get_path;
65 66
}

67 68 69 70 71 72 73 74 75 76 77
static void
camel_service_init (void *o, void *k)
{
	CamelService *service = o;

	service->priv = g_malloc0(sizeof(*service->priv));
#ifdef ENABLE_THREADS
	service->priv->connect_lock = e_mutex_new(E_MUTEX_REC);
#endif
}

78
static void
Peter Williams's avatar
Peter Williams committed
79
camel_service_finalize (CamelObject *object)
80 81 82
{
	CamelService *camel_service = CAMEL_SERVICE (object);

83 84 85 86 87
	if (camel_service->connected) {
		CamelException ex;

		/*g_warning ("camel_service_finalize: finalizing while still connected!");*/
		camel_exception_init (&ex);
88
		CSERV_CLASS (camel_service)->disconnect (camel_service, FALSE, &ex);
89 90 91 92 93 94 95
		if (camel_exception_is_set (&ex)) {
			g_warning ("camel_service_finalize: silent disconnect failure: %s",
				   camel_exception_get_description(&ex));
		}
		camel_exception_clear (&ex);
	}

96
	if (camel_service->url)
97
		camel_url_free (camel_service->url);
98
	if (camel_service->session)
Peter Williams's avatar
Peter Williams committed
99
		camel_object_unref (CAMEL_OBJECT (camel_service->session));
100 101 102 103 104

#ifdef ENABLE_THREADS
	e_mutex_destroy(camel_service->priv->connect_lock);
#endif
	g_free(camel_service->priv);
105
}
106

bertrand's avatar
bertrand committed
107

Peter Williams's avatar
Peter Williams committed
108 109 110 111 112 113 114

CamelType
camel_service_get_type (void)
{
	static CamelType camel_service_type = CAMEL_INVALID_TYPE;

	if (camel_service_type == CAMEL_INVALID_TYPE) {
Jeffrey Stedfast's avatar
Jeffrey Stedfast committed
115 116 117 118 119 120 121 122
		camel_service_type =
			camel_type_register (CAMEL_OBJECT_TYPE, "CamelService",
					     sizeof (CamelService),
					     sizeof (CamelServiceClass),
					     (CamelObjectClassInitFunc) camel_service_class_init,
					     NULL,
					     (CamelObjectInitFunc) camel_service_init,
					     camel_service_finalize );
Peter Williams's avatar
Peter Williams committed
123
	}
Jeffrey Stedfast's avatar
Jeffrey Stedfast committed
124
	
Peter Williams's avatar
Peter Williams committed
125 126 127
	return camel_service_type;
}

128 129 130 131

static void
construct (CamelService *service, CamelSession *session,
	   CamelProvider *provider, CamelURL *url, CamelException *ex)
132 133 134
{
	char *url_string;

135
	if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_USER) &&
136 137
	    (url->user == NULL || url->user[0] == '\0')) {
		url_string = camel_url_to_string (url, FALSE);
138
		camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
Dan Winship's avatar
Dan Winship committed
139
				      _("URL '%s' needs a username component"),
140 141
				      url_string);
		g_free (url_string);
142
		return;
143
	} else if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_HOST) &&
144 145
		   (url->host == NULL || url->host[0] == '\0')) {
		url_string = camel_url_to_string (url, FALSE);
146
		camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
Dan Winship's avatar
Dan Winship committed
147
				      _("URL '%s' needs a host component"),
148 149
				      url_string);
		g_free (url_string);
150
		return;
151
	} else if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_PATH) &&
152 153
		   (url->path == NULL || url->path[0] == '\0')) {
		url_string = camel_url_to_string (url, FALSE);
154
		camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
Dan Winship's avatar
Dan Winship committed
155
				      _("URL '%s' needs a path component"),
156 157
				      url_string);
		g_free (url_string);
158
		return;
159 160
	}

161 162 163 164 165 166
	service->provider = provider;
	service->url = url;
	service->session = session;
	camel_object_ref (CAMEL_OBJECT (session));

	service->connected = FALSE;
167 168
}

169
/**
170 171
 * camel_service_construct:
 * @service: the CamelService
172
 * @session: the session for the service
173
 * @provider: the service's provider
174 175 176
 * @url: the default URL for the service (may be NULL)
 * @ex: a CamelException
 *
177
 * Constructs a CamelService initialized with the given parameters.
178
 **/
179 180 181 182
void
camel_service_construct (CamelService *service, CamelSession *session,
			 CamelProvider *provider, CamelURL *url,
			 CamelException *ex)
183
{
184 185
	g_return_if_fail (CAMEL_IS_SERVICE (service));
	g_return_if_fail (CAMEL_IS_SESSION (session));
186

187
	CSERV_CLASS (service)->construct (service, session, provider, url, ex);
188
}
bertrand's avatar
bertrand committed
189

190

191
static gboolean
192
service_connect (CamelService *service, CamelException *ex)
bertrand's avatar
bertrand committed
193
{
194 195 196 197
	/* Things like the CamelMboxStore can validly
	 * not define a connect function.
	 */
	 return TRUE;
bertrand's avatar
bertrand committed
198 199
}

bertrand's avatar
bertrand committed
200
/**
201
 * camel_service_connect:
202
 * @service: CamelService object
203
 * @ex: a CamelException
204 205 206
 *
 * Connect to the service using the parameters it was initialized
 * with.
207 208
 *
 * Return value: whether or not the connection succeeded
bertrand's avatar
bertrand committed
209
 **/
210 211 212 213

gboolean
camel_service_connect (CamelService *service, CamelException *ex)
{
214 215
	gboolean ret = FALSE;

216 217 218 219
	g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
	g_return_val_if_fail (service->session != NULL, FALSE);
	g_return_val_if_fail (service->url != NULL, FALSE);

220 221
	CAMEL_SERVICE_LOCK(service, connect_lock);

222 223 224 225 226
	if (service->connected) {
		/* But we're still connected, so no exception
		 * and return true.
		 */
		g_warning ("camel_service_connect: trying to connect to an already connected service");
227 228
		ret = TRUE;
	} else if (CSERV_CLASS (service)->connect (service, ex)) {
229
		service->connected = TRUE;
230
		ret = TRUE;
231 232
	}

233 234 235
	CAMEL_SERVICE_UNLOCK(service, connect_lock);

	return ret;
236
}
237

238
static gboolean
239
service_disconnect (CamelService *service, gboolean clean, CamelException *ex)
bertrand's avatar
bertrand committed
240
{
241 242 243 244 245
	/*service->connect_level--;*/

	/* We let people get away with not having a disconnect
	 * function -- CamelMboxStore, for example. 
	 */
246 247

	return TRUE;
bertrand's avatar
bertrand committed
248 249 250
}

/**
251
 * camel_service_disconnect:
252
 * @service: CamelService object
253
 * @clean: whether or not to try to disconnect cleanly.
254 255
 * @ex: a CamelException
 *
256 257
 * Disconnect from the service. If @clean is %FALSE, it should not
 * try to do any synchronizing or other cleanup of the connection.
258 259
 *
 * Return value: whether or not the disconnection succeeded without
260
 * errors. (Consult @ex if %FALSE.)
bertrand's avatar
bertrand committed
261
 **/
262
gboolean
263 264
camel_service_disconnect (CamelService *service, gboolean clean,
			  CamelException *ex)
265
{
266
	gboolean res = TRUE;
267

268 269 270 271 272 273 274 275
	CAMEL_SERVICE_LOCK(service, connect_lock);

	if (service->connected) {
		res = CSERV_CLASS (service)->disconnect (service, clean, ex);
		service->connected = FALSE;
	}

	CAMEL_SERVICE_UNLOCK(service, connect_lock);
276

277
	return res;
278
}
bertrand's avatar
bertrand committed
279

280
/**
281 282
 * camel_service_get_url:
 * @service: a service
283
 *
284
 * Returns the URL representing a service. The returned URL must be
285 286
 * freed when it is no longer needed. For security reasons, this
 * routine does not return the password.
287
 *
bertrand's avatar
bertrand committed
288 289
 * Return value: the url name
 **/
290
char *
291
camel_service_get_url (CamelService *service)
bertrand's avatar
bertrand committed
292
{
293
	return camel_url_to_string(service->url, FALSE);
bertrand's avatar
bertrand committed
294 295 296
}


297 298 299 300
static char *
get_name (CamelService *service, gboolean brief)
{
	g_warning ("CamelService::get_name not implemented for `%s'",
Peter Williams's avatar
Peter Williams committed
301
		   camel_type_to_name (CAMEL_OBJECT_GET_TYPE (service)));
302
	return g_strdup ("???");
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
}		

/**
 * camel_service_get_name:
 * @service: the service
 * @brief: whether or not to use a briefer form
 *
 * This gets the name of the service in a "friendly" (suitable for
 * humans) form. If @brief is %TRUE, this should be a brief description
 * such as for use in the folder tree. If @brief is %FALSE, it should
 * be a more complete and mostly unambiguous description.
 *
 * Return value: the description, which the caller must free.
 **/
char *
camel_service_get_name (CamelService *service, gboolean brief)
{
	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
	g_return_val_if_fail (service->url, NULL);

	return CSERV_CLASS (service)->get_name (service, brief);
}


327 328 329 330 331 332
static char *
get_path (CamelService *service)
{
	GString *gpath;
	char *path;
	CamelURL *url = service->url;
333
	CamelProvider *prov = service->provider;
334 335 336 337 338 339

	/* A sort of ad-hoc default implementation that works for our
	 * current set of services.
	 */

	gpath = g_string_new (service->provider->protocol);
340 341
	if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_USER)) {
		if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_HOST)) {
342 343 344 345 346
			g_string_sprintfa (gpath, "/%s@%s",
					   url->user ? url->user : "",
					   url->host ? url->host : "");
		} else {
			g_string_sprintfa (gpath, "/%s%s",
347 348
					   url->user ? url->user : "",
					   CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_USER) ? "" : "@");
349
		}
350
	} else if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_HOST)) {
351
		g_string_sprintfa (gpath, "/%s%s",
352 353
				   CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_HOST) ? "" : "@",
				   url->host ? url->host : "");
354
	}
355
	if (CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_PATH)) {
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
		g_string_sprintfa (gpath, "%s%s",
				   *url->path == '/' ? "" : "/",
				   url->path);
	}

	path = gpath->str;
	g_string_free (gpath, FALSE);
	return path;
}		

/**
 * camel_service_get_path:
 * @service: the service
 *
 * This gets a valid UNIX relative path describing the service, which
 * is guaranteed to be different from the path returned for any
 * different service. This path MUST start with the name of the
 * provider, followed by a "/", but after that, it is up to the
 * provider.
 *
 * Return value: the path, which the caller must free.
 **/
char *
camel_service_get_path (CamelService *service)
{
	g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
	g_return_val_if_fail (service->url, NULL);

	return CSERV_CLASS (service)->get_path (service);
}


388
/**
389 390
 * camel_service_get_session:
 * @service: a service
391
 *
392
 * Returns the CamelSession associated with the service.
393 394 395 396 397 398 399 400
 *
 * Return value: the session
 **/
CamelSession *
camel_service_get_session (CamelService *service)
{
	return service->session;
}
401

402 403 404 405 406 407 408 409 410 411 412 413 414
/**
 * camel_service_get_provider:
 * @service: a service
 *
 * Returns the CamelProvider associated with the service.
 *
 * Return value: the provider
 **/
CamelProvider *
camel_service_get_provider (CamelService *service)
{
	return service->provider;
}
415

Jeffrey Stedfast's avatar
Jeffrey Stedfast committed
416
static GList *
417
query_auth_types (CamelService *service, CamelException *ex)
418 419 420 421 422
{
	return NULL;
}

/**
423
 * camel_service_query_auth_types:
424
 * @service: a CamelService
425
 * @ex: a CamelException
426 427 428 429 430 431
 *
 * This is used by the mail source wizard to get the list of
 * authentication types supported by the protocol, and information
 * about them.
 *
 * Return value: a list of CamelServiceAuthType records. The caller
432
 * must free the list with g_list_free() when it is done with it.
433 434
 **/
GList *
435
camel_service_query_auth_types (CamelService *service, CamelException *ex)
436
{
437 438 439 440 441
	GList *ret;

	/* note that we get the connect lock here, which means the callee
	   must not call the connect functions itself */
	CAMEL_SERVICE_LOCK(service, connect_lock);
442
	ret = CSERV_CLASS (service)->query_auth_types (service, ex);
443 444 445
	CAMEL_SERVICE_UNLOCK(service, connect_lock);

	return ret;
446 447
}

448 449 450
/* URL utility routines */

/**
451
 * camel_service_gethost:
452 453 454 455 456 457 458 459 460 461 462 463 464 465
 * @service: a CamelService
 * @ex: a CamelException
 *
 * This is a convenience function to do a gethostbyname on the host
 * for the service's URL.
 *
 * Return value: a (statically-allocated) hostent.
 **/
struct hostent *
camel_service_gethost (CamelService *service, CamelException *ex)
{
	struct hostent *h;
	char *hostname;

466 467
#warning "This needs to use gethostbyname_r()"

468 469 470 471 472 473 474 475 476 477
	if (service->url->host)
		hostname = service->url->host;
	else
		hostname = "localhost";
	h = gethostbyname (hostname);
	if (!h) {
		extern int h_errno;

		if (h_errno == HOST_NOT_FOUND || h_errno == NO_DATA) {
			camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
Dan Winship's avatar
Dan Winship committed
478
					      _("No such host %s."), hostname);
479 480
		} else {
			camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
Dan Winship's avatar
Dan Winship committed
481 482
					      _("Temporarily unable to look "
						"up hostname %s."), hostname);
483 484 485 486 487 488
		}
		return NULL;
	}

	return h;
}