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 1999-2003 Ximian, Inc. (www.ximian.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 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 General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
36 #include "e-util/e-msgport.h"
38 #include "e-util/e-host-utils.h"
40 #include "camel-service.h"
41 #include "camel-session.h"
42 #include "camel-exception.h"
43 #include "camel-operation.h"
44 #include "camel-private.h"
49 static CamelObjectClass *parent_class = NULL;
51 /* Returns the class for a CamelService */
52 #define CSERV_CLASS(so) CAMEL_SERVICE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
54 static void construct (CamelService *service, CamelSession *session,
55 CamelProvider *provider, CamelURL *url,
57 static gboolean service_connect(CamelService *service, CamelException *ex);
58 static gboolean service_disconnect(CamelService *service, gboolean clean,
60 static void cancel_connect (CamelService *service);
61 static GList *query_auth_types (CamelService *service, CamelException *ex);
62 static char *get_name (CamelService *service, gboolean brief);
63 static char *get_path (CamelService *service);
65 static int service_setv (CamelObject *object, CamelException *ex, CamelArgV *args);
66 static int service_getv (CamelObject *object, CamelException *ex, CamelArgGetV *args);
70 camel_service_class_init (CamelServiceClass *camel_service_class)
72 CamelObjectClass *object_class = CAMEL_OBJECT_CLASS (camel_service_class);
74 parent_class = camel_type_get_global_classfuncs (CAMEL_OBJECT_TYPE);
76 /* virtual method overloading */
77 object_class->setv = service_setv;
78 object_class->getv = service_getv;
80 /* virtual method definition */
81 camel_service_class->construct = construct;
82 camel_service_class->connect = service_connect;
83 camel_service_class->disconnect = service_disconnect;
84 camel_service_class->cancel_connect = cancel_connect;
85 camel_service_class->query_auth_types = query_auth_types;
86 camel_service_class->get_name = get_name;
87 camel_service_class->get_path = get_path;
91 camel_service_init (void *o, void *k)
93 CamelService *service = o;
95 service->priv = g_malloc0(sizeof(*service->priv));
96 service->priv->connect_lock = e_mutex_new(E_MUTEX_REC);
97 service->priv->connect_op_lock = e_mutex_new(E_MUTEX_SIMPLE);
101 camel_service_finalize (CamelObject *object)
103 CamelService *service = CAMEL_SERVICE (object);
105 if (service->status == CAMEL_SERVICE_CONNECTED) {
108 camel_exception_init (&ex);
109 CSERV_CLASS (service)->disconnect (service, TRUE, &ex);
110 if (camel_exception_is_set (&ex)) {
111 w(g_warning ("camel_service_finalize: silent disconnect failure: %s",
112 camel_exception_get_description (&ex)));
114 camel_exception_clear (&ex);
118 camel_url_free (service->url);
119 if (service->session)
120 camel_object_unref (CAMEL_OBJECT (service->session));
122 e_mutex_destroy (service->priv->connect_lock);
123 e_mutex_destroy (service->priv->connect_op_lock);
125 g_free (service->priv);
131 camel_service_get_type (void)
133 static CamelType type = CAMEL_INVALID_TYPE;
135 if (type == CAMEL_INVALID_TYPE) {
137 camel_type_register (CAMEL_OBJECT_TYPE,
139 sizeof (CamelService),
140 sizeof (CamelServiceClass),
141 (CamelObjectClassInitFunc) camel_service_class_init,
143 (CamelObjectInitFunc) camel_service_init,
144 camel_service_finalize );
152 service_setv (CamelObject *object, CamelException *ex, CamelArgV *args)
154 CamelService *service = (CamelService *) object;
155 CamelURL *url = service->url;
156 gboolean reconnect = FALSE;
160 for (i = 0; i < args->argc; i++) {
161 tag = args->argv[i].tag;
163 /* make sure this arg wasn't already handled */
164 if (tag & CAMEL_ARG_IGNORE)
167 /* make sure this is an arg we're supposed to handle */
168 if ((tag & CAMEL_ARG_TAG) <= CAMEL_SERVICE_ARG_FIRST ||
169 (tag & CAMEL_ARG_TAG) >= CAMEL_SERVICE_ARG_FIRST + 100)
172 if (tag == CAMEL_SERVICE_USERNAME) {
173 /* set the username */
174 if (strcmp (url->user, args->argv[i].ca_str) != 0) {
175 camel_url_set_user (url, args->argv[i].ca_str);
178 } else if (tag == CAMEL_SERVICE_AUTH) {
179 /* set the auth mechanism */
180 if (strcmp (url->authmech, args->argv[i].ca_str) != 0) {
181 camel_url_set_authmech (url, args->argv[i].ca_str);
184 } else if (tag == CAMEL_SERVICE_HOSTNAME) {
185 /* set the hostname */
186 if (strcmp (url->host, args->argv[i].ca_str) != 0) {
187 camel_url_set_host (url, args->argv[i].ca_str);
190 } else if (tag == CAMEL_SERVICE_PORT) {
192 if (url->port != args->argv[i].ca_int) {
193 camel_url_set_port (url, args->argv[i].ca_int);
196 } else if (tag == CAMEL_SERVICE_PATH) {
198 if (strcmp (url->path, args->argv[i].ca_str) != 0) {
199 camel_url_set_host (url, args->argv[i].ca_str);
207 /* let our parent know that we've handled this arg */
208 camel_argv_ignore (args, i);
211 /* FIXME: what if we are in the process of connecting? */
212 if (reconnect && service->status == CAMEL_SERVICE_CONNECTED) {
213 /* reconnect the service using the new URL */
214 if (camel_service_disconnect (service, TRUE, ex))
215 camel_service_connect (service, ex);
218 return CAMEL_OBJECT_CLASS (parent_class)->setv (object, ex, args);
222 service_getv (CamelObject *object, CamelException *ex, CamelArgGetV *args)
224 CamelService *service = (CamelService *) object;
225 CamelURL *url = service->url;
229 for (i = 0; i < args->argc; i++) {
230 tag = args->argv[i].tag;
232 /* make sure this is an arg we're supposed to handle */
233 if ((tag & CAMEL_ARG_TAG) <= CAMEL_SERVICE_ARG_FIRST ||
234 (tag & CAMEL_ARG_TAG) >= CAMEL_SERVICE_ARG_FIRST + 100)
238 case CAMEL_SERVICE_USERNAME:
239 /* get the username */
240 *args->argv[i].ca_str = url->user;
242 case CAMEL_SERVICE_AUTH:
243 /* get the auth mechanism */
244 *args->argv[i].ca_str = url->authmech;
246 case CAMEL_SERVICE_HOSTNAME:
247 /* get the hostname */
248 *args->argv[i].ca_str = url->host;
250 case CAMEL_SERVICE_PORT:
252 *args->argv[i].ca_int = url->port;
254 case CAMEL_SERVICE_PATH:
256 *args->argv[i].ca_str = url->path;
264 return CAMEL_OBJECT_CLASS (parent_class)->getv (object, ex, args);
268 construct (CamelService *service, CamelSession *session,
269 CamelProvider *provider, CamelURL *url, CamelException *ex)
273 if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_USER) &&
274 (url->user == NULL || url->user[0] == '\0')) {
275 url_string = camel_url_to_string (url, CAMEL_URL_HIDE_PASSWORD);
276 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
277 _("URL '%s' needs a username component"),
281 } else if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_HOST) &&
282 (url->host == NULL || url->host[0] == '\0')) {
283 url_string = camel_url_to_string (url, CAMEL_URL_HIDE_PASSWORD);
284 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
285 _("URL '%s' needs a host component"),
289 } else if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_PATH) &&
290 (url->path == NULL || url->path[0] == '\0')) {
291 url_string = camel_url_to_string (url, CAMEL_URL_HIDE_PASSWORD);
292 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
293 _("URL '%s' needs a path component"),
299 service->provider = provider;
301 service->session = session;
302 camel_object_ref (CAMEL_OBJECT (session));
304 service->status = CAMEL_SERVICE_DISCONNECTED;
308 * camel_service_construct:
309 * @service: the CamelService
310 * @session: the session for the service
311 * @provider: the service's provider
312 * @url: the default URL for the service (may be NULL)
313 * @ex: a CamelException
315 * Constructs a CamelService initialized with the given parameters.
318 camel_service_construct (CamelService *service, CamelSession *session,
319 CamelProvider *provider, CamelURL *url,
322 g_return_if_fail (CAMEL_IS_SERVICE (service));
323 g_return_if_fail (CAMEL_IS_SESSION (session));
325 CSERV_CLASS (service)->construct (service, session, provider, url, ex);
330 service_connect (CamelService *service, CamelException *ex)
332 /* Things like the CamelMboxStore can validly
333 * not define a connect function.
339 * camel_service_connect:
340 * @service: CamelService object
341 * @ex: a CamelException
343 * Connect to the service using the parameters it was initialized
346 * Return value: whether or not the connection succeeded
350 camel_service_connect (CamelService *service, CamelException *ex)
352 gboolean ret = FALSE;
353 gboolean unreg = FALSE;
355 g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
356 g_return_val_if_fail (service->session != NULL, FALSE);
357 g_return_val_if_fail (service->url != NULL, FALSE);
359 CAMEL_SERVICE_LOCK (service, connect_lock);
361 if (service->status == CAMEL_SERVICE_CONNECTED) {
362 CAMEL_SERVICE_UNLOCK (service, connect_lock);
366 /* Register a separate operation for connecting, so that
367 * the offline code can cancel it.
369 CAMEL_SERVICE_LOCK (service, connect_op_lock);
370 service->connect_op = camel_operation_registered ();
371 if (!service->connect_op) {
372 service->connect_op = camel_operation_new (NULL, NULL);
373 camel_operation_register (service->connect_op);
376 CAMEL_SERVICE_UNLOCK (service, connect_op_lock);
378 service->status = CAMEL_SERVICE_CONNECTING;
379 ret = CSERV_CLASS (service)->connect (service, ex);
380 service->status = ret ? CAMEL_SERVICE_CONNECTED : CAMEL_SERVICE_DISCONNECTED;
382 CAMEL_SERVICE_LOCK (service, connect_op_lock);
383 if (service->connect_op) {
385 camel_operation_unregister (service->connect_op);
387 camel_operation_unref (service->connect_op);
388 service->connect_op = NULL;
390 CAMEL_SERVICE_UNLOCK (service, connect_op_lock);
392 CAMEL_SERVICE_UNLOCK (service, connect_lock);
398 service_disconnect (CamelService *service, gboolean clean, CamelException *ex)
400 /*service->connect_level--;*/
402 /* We let people get away with not having a disconnect
403 * function -- CamelMboxStore, for example.
410 * camel_service_disconnect:
411 * @service: CamelService object
412 * @clean: whether or not to try to disconnect cleanly.
413 * @ex: a CamelException
415 * Disconnect from the service. If @clean is %FALSE, it should not
416 * try to do any synchronizing or other cleanup of the connection.
418 * Return value: whether or not the disconnection succeeded without
419 * errors. (Consult @ex if %FALSE.)
422 camel_service_disconnect (CamelService *service, gboolean clean,
428 CAMEL_SERVICE_LOCK (service, connect_lock);
430 if (service->status != CAMEL_SERVICE_DISCONNECTED
431 && service->status != CAMEL_SERVICE_DISCONNECTING) {
432 CAMEL_SERVICE_LOCK (service, connect_op_lock);
433 service->connect_op = camel_operation_registered ();
434 if (!service->connect_op) {
435 service->connect_op = camel_operation_new (NULL, NULL);
436 camel_operation_register (service->connect_op);
439 CAMEL_SERVICE_UNLOCK (service, connect_op_lock);
441 service->status = CAMEL_SERVICE_DISCONNECTING;
442 res = CSERV_CLASS (service)->disconnect (service, clean, ex);
443 service->status = CAMEL_SERVICE_DISCONNECTED;
445 CAMEL_SERVICE_LOCK (service, connect_op_lock);
447 camel_operation_unregister (service->connect_op);
449 camel_operation_unref (service->connect_op);
450 service->connect_op = NULL;
451 CAMEL_SERVICE_UNLOCK (service, connect_op_lock);
454 CAMEL_SERVICE_UNLOCK (service, connect_lock);
460 cancel_connect (CamelService *service)
462 camel_operation_cancel (service->connect_op);
466 * camel_service_cancel_connect:
467 * @service: a service
469 * If @service is currently attempting to connect to or disconnect
470 * from a server, this causes it to stop and fail. Otherwise it is a
474 camel_service_cancel_connect (CamelService *service)
476 CAMEL_SERVICE_LOCK (service, connect_op_lock);
477 if (service->connect_op)
478 CSERV_CLASS (service)->cancel_connect (service);
479 CAMEL_SERVICE_UNLOCK (service, connect_op_lock);
483 * camel_service_get_url:
484 * @service: a service
486 * Returns the URL representing a service. The returned URL must be
487 * freed when it is no longer needed. For security reasons, this
488 * routine does not return the password.
490 * Return value: the url name
493 camel_service_get_url (CamelService *service)
495 return camel_url_to_string (service->url, CAMEL_URL_HIDE_PASSWORD);
500 get_name (CamelService *service, gboolean brief)
502 w(g_warning ("CamelService::get_name not implemented for `%s'",
503 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (service))));
504 return g_strdup ("???");
508 * camel_service_get_name:
509 * @service: the service
510 * @brief: whether or not to use a briefer form
512 * This gets the name of the service in a "friendly" (suitable for
513 * humans) form. If @brief is %TRUE, this should be a brief description
514 * such as for use in the folder tree. If @brief is %FALSE, it should
515 * be a more complete and mostly unambiguous description.
517 * Return value: the description, which the caller must free.
520 camel_service_get_name (CamelService *service, gboolean brief)
522 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
523 g_return_val_if_fail (service->url, NULL);
525 return CSERV_CLASS (service)->get_name (service, brief);
530 get_path (CamelService *service)
532 CamelProvider *prov = service->provider;
533 CamelURL *url = service->url;
537 /* A sort of ad-hoc default implementation that works for our
538 * current set of services.
541 gpath = g_string_new (service->provider->protocol);
542 if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_USER)) {
543 if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_HOST)) {
544 g_string_append_printf (gpath, "/%s@%s",
545 url->user ? url->user : "",
546 url->host ? url->host : "");
549 g_string_append_printf (gpath, ":%d", url->port);
551 g_string_append_printf (gpath, "/%s%s", url->user ? url->user : "",
552 CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_USER) ? "" : "@");
554 } else if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_HOST)) {
555 g_string_append_printf (gpath, "/%s%s",
556 CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_HOST) ? "" : "@",
557 url->host ? url->host : "");
560 g_string_append_printf (gpath, ":%d", url->port);
563 if (CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_PATH))
564 g_string_append_printf (gpath, "%s%s", *url->path == '/' ? "" : "/", url->path);
567 g_string_free (gpath, FALSE);
573 * camel_service_get_path:
574 * @service: the service
576 * This gets a valid UNIX relative path describing the service, which
577 * is guaranteed to be different from the path returned for any
578 * different service. This path MUST start with the name of the
579 * provider, followed by a "/", but after that, it is up to the
582 * Return value: the path, which the caller must free.
585 camel_service_get_path (CamelService *service)
587 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
588 g_return_val_if_fail (service->url, NULL);
590 return CSERV_CLASS (service)->get_path (service);
595 * camel_service_get_session:
596 * @service: a service
598 * Returns the CamelSession associated with the service.
600 * Return value: the session
603 camel_service_get_session (CamelService *service)
605 return service->session;
609 * camel_service_get_provider:
610 * @service: a service
612 * Returns the CamelProvider associated with the service.
614 * Return value: the provider
617 camel_service_get_provider (CamelService *service)
619 return service->provider;
623 query_auth_types (CamelService *service, CamelException *ex)
629 * camel_service_query_auth_types:
630 * @service: a CamelService
631 * @ex: a CamelException
633 * This is used by the mail source wizard to get the list of
634 * authentication types supported by the protocol, and information
637 * Return value: a list of CamelServiceAuthType records. The caller
638 * must free the list with g_list_free() when it is done with it.
641 camel_service_query_auth_types (CamelService *service, CamelException *ex)
645 /* note that we get the connect lock here, which means the callee
646 must not call the connect functions itself */
647 CAMEL_SERVICE_LOCK (service, connect_lock);
648 ret = CSERV_CLASS (service)->query_auth_types (service, ex);
649 CAMEL_SERVICE_UNLOCK (service, connect_lock);
654 /* URL utility routines */
657 * camel_service_gethost:
658 * @service: a CamelService
659 * @ex: a CamelException
661 * This is a convenience function to do a gethostbyname on the host
662 * for the service's URL.
664 * Return value: a (statically-allocated) hostent.
667 camel_service_gethost (CamelService *service, CamelException *ex)
671 if (service->url->host)
672 hostname = service->url->host;
674 hostname = "localhost";
676 return camel_gethostbyname (hostname, ex);
680 #define STRUCT_OFFSET(type, field) ((gint) offsetof (type, field))
682 #define STRUCT_OFFSET(type, field) ((gint) ((gchar*) &((type *) 0)->field))
687 unsigned int cancelled:1;
693 struct hostent hostbuf;
699 get_hostbyname(void *data)
701 struct _lookup_msg *info = data;
703 while ((info->result = e_gethostbyname_r(info->name, &info->hostbuf, info->hostbufmem, info->hostbuflen, &info->herr)) == ERANGE) {
704 d(printf("gethostbyname fialed?\n"));
705 pthread_testcancel();
706 info->hostbuflen *= 2;
707 info->hostbufmem = g_realloc(info->hostbufmem, info->hostbuflen);
710 d(printf("gethostbyname ok?\n"));
712 /* If we got cancelled, dont reply, just free it */
713 if (info->cancelled) {
714 g_free(info->hostbufmem);
717 e_msgport_reply((EMsg *)info);
724 camel_gethostbyname (const char *name, CamelException *exout)
726 int fdmax, status, fd, cancel_fd;
727 struct _lookup_msg *msg;
730 g_return_val_if_fail(name != NULL, NULL);
732 if (camel_operation_cancel_check(NULL)) {
733 camel_exception_set (exout, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled"));
737 camel_exception_init(&ex);
738 camel_operation_start_transient(NULL, _("Resolving: %s"), name);
740 msg = g_malloc0(sizeof(*msg));
741 msg->hostbuflen = 1024;
742 msg->hostbufmem = g_malloc(msg->hostbuflen);
746 cancel_fd = camel_operation_cancel_fd(NULL);
747 if (cancel_fd == -1) {
750 EMsgPort *reply_port;
754 reply_port = msg->msg.reply_port = e_msgport_new();
755 fd = e_msgport_fd(msg->msg.reply_port);
756 if (pthread_create(&id, NULL, get_hostbyname, msg) == 0) {
757 d(printf("waiting for name return/cancellation in main process\n"));
760 FD_SET(cancel_fd, &rdset);
762 fdmax = MAX(fd, cancel_fd) + 1;
763 status = select(fdmax, &rdset, NULL, 0, NULL);
764 } while (status == -1 && errno == EINTR);
766 if (status == -1 || FD_ISSET(cancel_fd, &rdset)) {
768 camel_exception_setv(&ex, CAMEL_EXCEPTION_SYSTEM, _("Failure in name lookup: %s"), g_strerror(errno));
770 camel_exception_setv(&ex, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled"));
772 /* We cancel so if the thread impl is decent it causes immediate exit.
773 We detach so we dont need to wait for it to exit if it isn't.
774 We check the reply port incase we had a reply in the mean time, which we free later */
775 d(printf("Cancelling lookup thread and leaving it\n"));
779 msg = (struct _lookup_msg *)e_msgport_get(reply_port);
781 struct _lookup_msg *reply = (struct _lookup_msg *)e_msgport_get(reply_port);
783 g_assert(reply == msg);
784 d(printf("waiting for child to exit\n"));
785 pthread_join(id, NULL);
786 d(printf("child done\n"));
789 camel_exception_setv(&ex, CAMEL_EXCEPTION_SYSTEM, _("Host lookup failed: cannot create thread: %s"), g_strerror(errno));
791 e_msgport_destroy(reply_port);
794 camel_operation_end(NULL);
796 if (!camel_exception_is_set(&ex)) {
797 if (msg->result == 0)
798 return &msg->hostbuf;
800 if (msg->herr == HOST_NOT_FOUND || msg->herr == NO_DATA)
801 camel_exception_setv (&ex, CAMEL_EXCEPTION_SYSTEM,
802 _("Host lookup failed: %s: host not found"), name);
804 camel_exception_setv (&ex, CAMEL_EXCEPTION_SYSTEM,
805 _("Host lookup failed: %s: unknown reason"), name);
809 g_free(msg->hostbufmem);
813 camel_exception_xfer(exout, &ex);
819 get_hostbyaddr (void *data)
821 struct _lookup_msg *info = data;
823 while ((info->result = e_gethostbyaddr_r (info->name, info->len, info->type, &info->hostbuf,
824 info->hostbufmem, info->hostbuflen, &info->herr)) == ERANGE) {
825 d(printf ("gethostbyaddr fialed?\n"));
826 pthread_testcancel ();
827 info->hostbuflen *= 2;
828 info->hostbufmem = g_realloc (info->hostbufmem, info->hostbuflen);
831 d(printf ("gethostbyaddr ok?\n"));
833 if (info->cancelled) {
834 g_free(info->hostbufmem);
837 e_msgport_reply((EMsg *)info);
845 camel_gethostbyaddr (const char *addr, int len, int type, CamelException *exout)
847 int fdmax, status, fd, cancel_fd;
848 struct _lookup_msg *msg;
851 g_return_val_if_fail (addr != NULL, NULL);
853 if (camel_operation_cancel_check (NULL)) {
854 camel_exception_set (exout, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled"));
858 camel_exception_init(&ex);
859 camel_operation_start_transient (NULL, _("Resolving address"));
861 msg = g_malloc0 (sizeof (struct _lookup_msg));
862 msg->hostbuflen = 1024;
863 msg->hostbufmem = g_malloc (msg->hostbuflen);
869 cancel_fd = camel_operation_cancel_fd (NULL);
870 if (cancel_fd == -1) {
871 get_hostbyaddr (msg);
873 EMsgPort *reply_port;
877 reply_port = msg->msg.reply_port = e_msgport_new ();
878 fd = e_msgport_fd (msg->msg.reply_port);
879 if (pthread_create (&id, NULL, get_hostbyaddr, msg) == 0) {
880 d(printf("waiting for name return/cancellation in main process\n"));
883 FD_SET(cancel_fd, &rdset);
885 fdmax = MAX(fd, cancel_fd) + 1;
886 status = select (fdmax, &rdset, NULL, 0, NULL);
887 } while (status == -1 && errno == EINTR);
889 if (status == -1 || FD_ISSET(cancel_fd, &rdset)) {
891 camel_exception_setv(&ex, CAMEL_EXCEPTION_SYSTEM, _("Failure in name lookup: %s"), g_strerror(errno));
893 camel_exception_setv(&ex, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled"));
895 /* We cancel so if the thread impl is decent it causes immediate exit.
896 We detach so we dont need to wait for it to exit if it isn't.
897 We check the reply port incase we had a reply in the mean time, which we free later */
898 d(printf("Cancelling lookup thread and leaving it\n"));
902 msg = (struct _lookup_msg *)e_msgport_get(reply_port);
904 struct _lookup_msg *reply = (struct _lookup_msg *)e_msgport_get(reply_port);
906 g_assert(reply == msg);
907 d(printf("waiting for child to exit\n"));
908 pthread_join(id, NULL);
909 d(printf("child done\n"));
913 e_msgport_destroy (reply_port);
916 camel_operation_end (NULL);
918 if (!camel_exception_is_set(&ex)) {
919 if (msg->result == 0)
920 return &msg->hostbuf;
922 if (msg->herr == HOST_NOT_FOUND || msg->herr == NO_DATA)
923 camel_exception_setv (&ex, CAMEL_EXCEPTION_SYSTEM,
924 _("Host lookup failed: host not found"));
926 camel_exception_setv (&ex, CAMEL_EXCEPTION_SYSTEM,
927 _("Host lookup failed: unknown reason"));
931 g_free(msg->hostbufmem);
935 camel_exception_xfer(exout, &ex);
940 void camel_free_host(struct hostent *h)
942 struct _lookup_msg *msg;
944 g_return_if_fail(h != NULL);
946 /* yeah this looks ugly but it is safe. we passed out a reference to inside our structure, this maps it
947 to the base structure, so we can free everything right without having to keep track of it separately */
948 msg = (struct _lookup_msg *)(((char *)h) - STRUCT_OFFSET(struct _lookup_msg, hostbuf));
950 g_free(msg->hostbufmem);