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, 2000 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
37 #include "e-util/e-msgport.h"
40 #include "e-util/e-host-utils.h"
42 #include "camel-service.h"
43 #include "camel-session.h"
44 #include "camel-exception.h"
45 #include "camel-operation.h"
46 #include "camel-private.h"
51 static CamelObjectClass *parent_class = NULL;
53 /* Returns the class for a CamelService */
54 #define CSERV_CLASS(so) CAMEL_SERVICE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
56 static void construct (CamelService *service, CamelSession *session,
57 CamelProvider *provider, CamelURL *url,
59 static gboolean service_connect(CamelService *service, CamelException *ex);
60 static gboolean service_disconnect(CamelService *service, gboolean clean,
62 static void cancel_connect (CamelService *service);
63 static GList *query_auth_types (CamelService *service, CamelException *ex);
64 static char *get_name (CamelService *service, gboolean brief);
65 static char *get_path (CamelService *service);
67 static int service_setv (CamelObject *object, CamelException *ex, CamelArgV *args);
68 static int service_getv (CamelObject *object, CamelException *ex, CamelArgGetV *args);
72 camel_service_class_init (CamelServiceClass *camel_service_class)
74 CamelObjectClass *object_class = CAMEL_OBJECT_CLASS (camel_service_class);
76 parent_class = camel_type_get_global_classfuncs (CAMEL_OBJECT_TYPE);
78 /* virtual method overloading */
79 object_class->setv = service_setv;
80 object_class->getv = service_getv;
82 /* virtual method definition */
83 camel_service_class->construct = construct;
84 camel_service_class->connect = service_connect;
85 camel_service_class->disconnect = service_disconnect;
86 camel_service_class->cancel_connect = cancel_connect;
87 camel_service_class->query_auth_types = query_auth_types;
88 camel_service_class->get_name = get_name;
89 camel_service_class->get_path = get_path;
93 camel_service_init (void *o, void *k)
95 CamelService *service = o;
97 service->priv = g_malloc0(sizeof(*service->priv));
99 service->priv->connect_lock = e_mutex_new(E_MUTEX_REC);
100 service->priv->connect_op_lock = e_mutex_new(E_MUTEX_SIMPLE);
105 camel_service_finalize (CamelObject *object)
107 CamelService *service = CAMEL_SERVICE (object);
109 if (service->status == CAMEL_SERVICE_CONNECTED) {
112 camel_exception_init (&ex);
113 CSERV_CLASS (service)->disconnect (service, TRUE, &ex);
114 if (camel_exception_is_set (&ex)) {
115 w(g_warning ("camel_service_finalize: silent disconnect failure: %s",
116 camel_exception_get_description (&ex)));
118 camel_exception_clear (&ex);
122 camel_url_free (service->url);
123 if (service->session)
124 camel_object_unref (CAMEL_OBJECT (service->session));
126 #ifdef ENABLE_THREADS
127 e_mutex_destroy (service->priv->connect_lock);
128 e_mutex_destroy (service->priv->connect_op_lock);
130 g_free (service->priv);
136 camel_service_get_type (void)
138 static CamelType type = CAMEL_INVALID_TYPE;
140 if (type == CAMEL_INVALID_TYPE) {
142 camel_type_register (CAMEL_OBJECT_TYPE,
144 sizeof (CamelService),
145 sizeof (CamelServiceClass),
146 (CamelObjectClassInitFunc) camel_service_class_init,
148 (CamelObjectInitFunc) camel_service_init,
149 camel_service_finalize );
157 service_setv (CamelObject *object, CamelException *ex, CamelArgV *args)
159 CamelService *service = (CamelService *) object;
160 CamelURL *url = service->url;
161 gboolean reconnect = FALSE;
165 for (i = 0; i < args->argc; i++) {
166 tag = args->argv[i].tag;
168 /* make sure this arg wasn't already handled */
169 if (tag & CAMEL_ARG_IGNORE)
172 /* make sure this is an arg we're supposed to handle */
173 if ((tag & CAMEL_ARG_TAG) <= CAMEL_SERVICE_ARG_FIRST ||
174 (tag & CAMEL_ARG_TAG) >= CAMEL_SERVICE_ARG_FIRST + 100)
177 if (tag == CAMEL_SERVICE_USERNAME) {
178 /* set the username */
179 if (strcmp (url->user, args->argv[i].ca_str) != 0) {
180 camel_url_set_user (url, args->argv[i].ca_str);
183 } else if (tag == CAMEL_SERVICE_AUTH) {
184 /* set the auth mechanism */
185 if (strcmp (url->authmech, args->argv[i].ca_str) != 0) {
186 camel_url_set_authmech (url, args->argv[i].ca_str);
189 } else if (tag == CAMEL_SERVICE_HOSTNAME) {
190 /* set the hostname */
191 if (strcmp (url->host, args->argv[i].ca_str) != 0) {
192 camel_url_set_host (url, args->argv[i].ca_str);
195 } else if (tag == CAMEL_SERVICE_PORT) {
197 if (url->port != args->argv[i].ca_int) {
198 camel_url_set_port (url, args->argv[i].ca_int);
201 } else if (tag == CAMEL_SERVICE_PATH) {
203 if (strcmp (url->path, args->argv[i].ca_str) != 0) {
204 camel_url_set_host (url, args->argv[i].ca_str);
212 /* let our parent know that we've handled this arg */
213 camel_argv_ignore (args, i);
216 /* FIXME: what if we are in the process of connecting? */
217 if (reconnect && service->status == CAMEL_SERVICE_CONNECTED) {
218 /* reconnect the service using the new URL */
219 if (camel_service_disconnect (service, TRUE, ex))
220 camel_service_connect (service, ex);
223 return CAMEL_OBJECT_CLASS (parent_class)->setv (object, ex, args);
227 service_getv (CamelObject *object, CamelException *ex, CamelArgGetV *args)
229 CamelService *service = (CamelService *) object;
230 CamelURL *url = service->url;
234 for (i = 0; i < args->argc; i++) {
235 tag = args->argv[i].tag;
237 /* make sure this is an arg we're supposed to handle */
238 if ((tag & CAMEL_ARG_TAG) <= CAMEL_SERVICE_ARG_FIRST ||
239 (tag & CAMEL_ARG_TAG) >= CAMEL_SERVICE_ARG_FIRST + 100)
243 case CAMEL_SERVICE_USERNAME:
244 /* get the username */
245 *args->argv[i].ca_str = url->user;
247 case CAMEL_SERVICE_AUTH:
248 /* get the auth mechanism */
249 *args->argv[i].ca_str = url->authmech;
251 case CAMEL_SERVICE_HOSTNAME:
252 /* get the hostname */
253 *args->argv[i].ca_str = url->host;
255 case CAMEL_SERVICE_PORT:
257 *args->argv[i].ca_int = url->port;
259 case CAMEL_SERVICE_PATH:
261 *args->argv[i].ca_str = url->path;
269 return CAMEL_OBJECT_CLASS (parent_class)->getv (object, ex, args);
273 construct (CamelService *service, CamelSession *session,
274 CamelProvider *provider, CamelURL *url, CamelException *ex)
278 if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_USER) &&
279 (url->user == NULL || url->user[0] == '\0')) {
280 url_string = camel_url_to_string (url, CAMEL_URL_HIDE_PASSWORD);
281 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
282 _("URL '%s' needs a username component"),
286 } else if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_HOST) &&
287 (url->host == NULL || url->host[0] == '\0')) {
288 url_string = camel_url_to_string (url, CAMEL_URL_HIDE_PASSWORD);
289 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
290 _("URL '%s' needs a host component"),
294 } else if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_PATH) &&
295 (url->path == NULL || url->path[0] == '\0')) {
296 url_string = camel_url_to_string (url, CAMEL_URL_HIDE_PASSWORD);
297 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
298 _("URL '%s' needs a path component"),
304 service->provider = provider;
306 service->session = session;
307 camel_object_ref (CAMEL_OBJECT (session));
309 service->status = CAMEL_SERVICE_DISCONNECTED;
313 * camel_service_construct:
314 * @service: the CamelService
315 * @session: the session for the service
316 * @provider: the service's provider
317 * @url: the default URL for the service (may be NULL)
318 * @ex: a CamelException
320 * Constructs a CamelService initialized with the given parameters.
323 camel_service_construct (CamelService *service, CamelSession *session,
324 CamelProvider *provider, CamelURL *url,
327 g_return_if_fail (CAMEL_IS_SERVICE (service));
328 g_return_if_fail (CAMEL_IS_SESSION (session));
330 CSERV_CLASS (service)->construct (service, session, provider, url, ex);
335 service_connect (CamelService *service, CamelException *ex)
337 /* Things like the CamelMboxStore can validly
338 * not define a connect function.
344 * camel_service_connect:
345 * @service: CamelService object
346 * @ex: a CamelException
348 * Connect to the service using the parameters it was initialized
351 * Return value: whether or not the connection succeeded
355 camel_service_connect (CamelService *service, CamelException *ex)
357 gboolean ret = FALSE;
358 gboolean unreg = FALSE;
360 g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
361 g_return_val_if_fail (service->session != NULL, FALSE);
362 g_return_val_if_fail (service->url != NULL, FALSE);
364 CAMEL_SERVICE_LOCK (service, connect_lock);
366 if (service->status == CAMEL_SERVICE_CONNECTED) {
367 CAMEL_SERVICE_UNLOCK (service, connect_lock);
371 /* Register a separate operation for connecting, so that
372 * the offline code can cancel it.
374 CAMEL_SERVICE_LOCK (service, connect_op_lock);
375 service->connect_op = camel_operation_registered ();
376 if (!service->connect_op) {
377 service->connect_op = camel_operation_new (NULL, NULL);
378 camel_operation_register (service->connect_op);
381 CAMEL_SERVICE_UNLOCK (service, connect_op_lock);
383 service->status = CAMEL_SERVICE_CONNECTING;
384 ret = CSERV_CLASS (service)->connect (service, ex);
385 service->status = ret ? CAMEL_SERVICE_CONNECTED : CAMEL_SERVICE_DISCONNECTED;
387 CAMEL_SERVICE_LOCK (service, connect_op_lock);
388 if (service->connect_op) {
390 camel_operation_unregister (service->connect_op);
392 camel_operation_unref (service->connect_op);
393 service->connect_op = NULL;
395 CAMEL_SERVICE_UNLOCK (service, connect_op_lock);
397 CAMEL_SERVICE_UNLOCK (service, connect_lock);
403 service_disconnect (CamelService *service, gboolean clean, CamelException *ex)
405 /*service->connect_level--;*/
407 /* We let people get away with not having a disconnect
408 * function -- CamelMboxStore, for example.
415 * camel_service_disconnect:
416 * @service: CamelService object
417 * @clean: whether or not to try to disconnect cleanly.
418 * @ex: a CamelException
420 * Disconnect from the service. If @clean is %FALSE, it should not
421 * try to do any synchronizing or other cleanup of the connection.
423 * Return value: whether or not the disconnection succeeded without
424 * errors. (Consult @ex if %FALSE.)
427 camel_service_disconnect (CamelService *service, gboolean clean,
433 CAMEL_SERVICE_LOCK (service, connect_lock);
435 if (service->status != CAMEL_SERVICE_DISCONNECTED
436 && service->status != CAMEL_SERVICE_DISCONNECTING) {
437 CAMEL_SERVICE_LOCK (service, connect_op_lock);
438 service->connect_op = camel_operation_registered ();
439 if (!service->connect_op) {
440 service->connect_op = camel_operation_new (NULL, NULL);
441 camel_operation_register (service->connect_op);
444 CAMEL_SERVICE_UNLOCK (service, connect_op_lock);
446 service->status = CAMEL_SERVICE_DISCONNECTING;
447 res = CSERV_CLASS (service)->disconnect (service, clean, ex);
448 service->status = CAMEL_SERVICE_DISCONNECTED;
450 CAMEL_SERVICE_LOCK (service, connect_op_lock);
452 camel_operation_unregister (service->connect_op);
454 camel_operation_unref (service->connect_op);
455 service->connect_op = NULL;
456 CAMEL_SERVICE_UNLOCK (service, connect_op_lock);
459 CAMEL_SERVICE_UNLOCK (service, connect_lock);
465 cancel_connect (CamelService *service)
467 camel_operation_cancel (service->connect_op);
471 * camel_service_cancel_connect:
472 * @service: a service
474 * If @service is currently attempting to connect to or disconnect
475 * from a server, this causes it to stop and fail. Otherwise it is a
479 camel_service_cancel_connect (CamelService *service)
481 CAMEL_SERVICE_LOCK (service, connect_op_lock);
482 if (service->connect_op)
483 CSERV_CLASS (service)->cancel_connect (service);
484 CAMEL_SERVICE_UNLOCK (service, connect_op_lock);
488 * camel_service_get_url:
489 * @service: a service
491 * Returns the URL representing a service. The returned URL must be
492 * freed when it is no longer needed. For security reasons, this
493 * routine does not return the password.
495 * Return value: the url name
498 camel_service_get_url (CamelService *service)
500 return camel_url_to_string (service->url, CAMEL_URL_HIDE_PASSWORD);
505 get_name (CamelService *service, gboolean brief)
507 w(g_warning ("CamelService::get_name not implemented for `%s'",
508 camel_type_to_name (CAMEL_OBJECT_GET_TYPE (service))));
509 return g_strdup ("???");
513 * camel_service_get_name:
514 * @service: the service
515 * @brief: whether or not to use a briefer form
517 * This gets the name of the service in a "friendly" (suitable for
518 * humans) form. If @brief is %TRUE, this should be a brief description
519 * such as for use in the folder tree. If @brief is %FALSE, it should
520 * be a more complete and mostly unambiguous description.
522 * Return value: the description, which the caller must free.
525 camel_service_get_name (CamelService *service, gboolean brief)
527 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
528 g_return_val_if_fail (service->url, NULL);
530 return CSERV_CLASS (service)->get_name (service, brief);
535 get_path (CamelService *service)
537 CamelProvider *prov = service->provider;
538 CamelURL *url = service->url;
542 /* A sort of ad-hoc default implementation that works for our
543 * current set of services.
546 gpath = g_string_new (service->provider->protocol);
547 if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_USER)) {
548 if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_HOST)) {
549 g_string_append_printf (gpath, "/%s@%s",
550 url->user ? url->user : "",
551 url->host ? url->host : "");
554 g_string_append_printf (gpath, ":%d", url->port);
556 g_string_append_printf (gpath, "/%s%s", url->user ? url->user : "",
557 CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_USER) ? "" : "@");
559 } else if (CAMEL_PROVIDER_ALLOWS (prov, CAMEL_URL_PART_HOST)) {
560 g_string_append_printf (gpath, "/%s%s",
561 CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_HOST) ? "" : "@",
562 url->host ? url->host : "");
565 g_string_append_printf (gpath, ":%d", url->port);
568 if (CAMEL_PROVIDER_NEEDS (prov, CAMEL_URL_PART_PATH))
569 g_string_append_printf (gpath, "%s%s", *url->path == '/' ? "" : "/", url->path);
572 g_string_free (gpath, FALSE);
578 * camel_service_get_path:
579 * @service: the service
581 * This gets a valid UNIX relative path describing the service, which
582 * is guaranteed to be different from the path returned for any
583 * different service. This path MUST start with the name of the
584 * provider, followed by a "/", but after that, it is up to the
587 * Return value: the path, which the caller must free.
590 camel_service_get_path (CamelService *service)
592 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
593 g_return_val_if_fail (service->url, NULL);
595 return CSERV_CLASS (service)->get_path (service);
600 * camel_service_get_session:
601 * @service: a service
603 * Returns the CamelSession associated with the service.
605 * Return value: the session
608 camel_service_get_session (CamelService *service)
610 return service->session;
614 * camel_service_get_provider:
615 * @service: a service
617 * Returns the CamelProvider associated with the service.
619 * Return value: the provider
622 camel_service_get_provider (CamelService *service)
624 return service->provider;
628 query_auth_types (CamelService *service, CamelException *ex)
634 * camel_service_query_auth_types:
635 * @service: a CamelService
636 * @ex: a CamelException
638 * This is used by the mail source wizard to get the list of
639 * authentication types supported by the protocol, and information
642 * Return value: a list of CamelServiceAuthType records. The caller
643 * must free the list with g_list_free() when it is done with it.
646 camel_service_query_auth_types (CamelService *service, CamelException *ex)
650 /* note that we get the connect lock here, which means the callee
651 must not call the connect functions itself */
652 CAMEL_SERVICE_LOCK (service, connect_lock);
653 ret = CSERV_CLASS (service)->query_auth_types (service, ex);
654 CAMEL_SERVICE_UNLOCK (service, connect_lock);
659 /* URL utility routines */
662 * camel_service_gethost:
663 * @service: a CamelService
664 * @ex: a CamelException
666 * This is a convenience function to do a gethostbyname on the host
667 * for the service's URL.
669 * Return value: a (statically-allocated) hostent.
672 camel_service_gethost (CamelService *service, CamelException *ex)
676 if (service->url->host)
677 hostname = service->url->host;
679 hostname = "localhost";
681 return camel_gethostbyname (hostname, ex);
685 #define STRUCT_OFFSET(type, field) ((gint) offsetof (type, field))
687 #define STRUCT_OFFSET(type, field) ((gint) ((gchar*) &((type *) 0)->field))
691 #ifdef ENABLE_THREADS
694 unsigned int cancelled:1;
700 struct hostent hostbuf;
706 get_hostbyname(void *data)
708 struct _lookup_msg *info = data;
710 while ((info->result = e_gethostbyname_r(info->name, &info->hostbuf, info->hostbufmem, info->hostbuflen, &info->herr)) == ERANGE) {
711 d(printf("gethostbyname fialed?\n"));
712 #ifdef ENABLE_THREADS
713 pthread_testcancel();
715 info->hostbuflen *= 2;
716 info->hostbufmem = g_realloc(info->hostbufmem, info->hostbuflen);
719 d(printf("gethostbyname ok?\n"));
721 #ifdef ENABLE_THREADS
722 /* If we got cancelled, dont reply, just free it */
723 if (info->cancelled) {
724 g_free(info->hostbufmem);
727 e_msgport_reply((EMsg *)info);
734 camel_gethostbyname (const char *name, CamelException *ex)
736 #ifdef ENABLE_THREADS
737 int fdmax, status, fd, cancel_fd;
739 struct _lookup_msg *msg;
741 g_return_val_if_fail(name != NULL, NULL);
743 if (camel_operation_cancel_check(NULL)) {
744 camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled"));
748 camel_operation_start_transient(NULL, _("Resolving: %s"), name);
750 msg = g_malloc0(sizeof(*msg));
751 msg->hostbuflen = 1024;
752 msg->hostbufmem = g_malloc(msg->hostbuflen);
756 #ifdef ENABLE_THREADS
757 cancel_fd = camel_operation_cancel_fd(NULL);
758 if (cancel_fd == -1) {
761 #ifdef ENABLE_THREADS
763 EMsgPort *reply_port;
767 reply_port = msg->msg.reply_port = e_msgport_new();
768 fd = e_msgport_fd(msg->msg.reply_port);
769 if (pthread_create(&id, NULL, get_hostbyname, msg) == 0) {
770 d(printf("waiting for name return/cancellation in main process\n"));
773 FD_SET(cancel_fd, &rdset);
775 fdmax = MAX(fd, cancel_fd) + 1;
776 status = select(fdmax, &rdset, NULL, 0, NULL);
777 } while (status == -1 && errno == EINTR);
779 if (status == -1 || FD_ISSET(cancel_fd, &rdset)) {
781 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Failure in name lookup: %s"), g_strerror(errno));
783 camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled"));
785 /* We cancel so if the thread impl is decent it causes immediate exit.
786 We detach so we dont need to wait for it to exit if it isn't.
787 We check the reply port incase we had a reply in the mean time, which we free later */
788 d(printf("Cancelling lookup thread and leaving it\n"));
792 msg = (struct _lookup_msg *)e_msgport_get(reply_port);
794 struct _lookup_msg *reply = (struct _lookup_msg *)e_msgport_get(reply_port);
796 g_assert(reply == msg);
797 d(printf("waiting for child to exit\n"));
798 pthread_join(id, NULL);
799 d(printf("child done\n"));
802 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Host lookup failed: cannot create thread: %s"), g_strerror(errno));
804 e_msgport_destroy(reply_port);
808 camel_operation_end(NULL);
810 if (!camel_exception_is_set(ex)) {
811 if (msg->result == 0)
812 return &msg->hostbuf;
814 if (msg->herr == HOST_NOT_FOUND || msg->herr == NO_DATA)
815 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
816 _("Host lookup failed: %s: host not found"), name);
818 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
819 _("Host lookup failed: %s: unknown reason"), name);
823 g_free(msg->hostbufmem);
831 get_hostbyaddr (void *data)
833 struct _lookup_msg *info = data;
835 while ((info->result = e_gethostbyaddr_r (info->name, info->len, info->type, &info->hostbuf,
836 info->hostbufmem, info->hostbuflen, &info->herr)) == ERANGE) {
837 d(printf ("gethostbyaddr fialed?\n"));
838 #ifdef ENABLE_THREADS
839 pthread_testcancel ();
841 info->hostbuflen *= 2;
842 info->hostbufmem = g_realloc (info->hostbufmem, info->hostbuflen);
845 d(printf ("gethostbyaddr ok?\n"));
847 #ifdef ENABLE_THREADS
848 if (info->cancelled) {
849 g_free(info->hostbufmem);
852 e_msgport_reply((EMsg *)info);
860 camel_gethostbyaddr (const char *addr, int len, int type, CamelException *ex)
862 #ifdef ENABLE_THREADS
863 int fdmax, status, fd, cancel_fd;
865 struct _lookup_msg *msg;
867 g_return_val_if_fail (addr != NULL, NULL);
869 if (camel_operation_cancel_check (NULL)) {
870 camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled"));
874 camel_operation_start_transient (NULL, _("Resolving address"));
876 msg = g_malloc0 (sizeof (struct _lookup_msg));
877 msg->hostbuflen = 1024;
878 msg->hostbufmem = g_malloc (msg->hostbuflen);
884 #ifdef ENABLE_THREADS
885 cancel_fd = camel_operation_cancel_fd (NULL);
886 if (cancel_fd == -1) {
888 get_hostbyaddr (msg);
889 #ifdef ENABLE_THREADS
891 EMsgPort *reply_port;
895 reply_port = msg->msg.reply_port = e_msgport_new ();
896 fd = e_msgport_fd (msg->msg.reply_port);
897 if (pthread_create (&id, NULL, get_hostbyaddr, msg) == 0) {
898 d(printf("waiting for name return/cancellation in main process\n"));
901 FD_SET(cancel_fd, &rdset);
903 fdmax = MAX(fd, cancel_fd) + 1;
904 status = select (fdmax, &rdset, NULL, 0, NULL);
905 } while (status == -1 && errno == EINTR);
907 if (status == -1 || FD_ISSET(cancel_fd, &rdset)) {
909 camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Failure in name lookup: %s"), g_strerror(errno));
911 camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled"));
913 /* We cancel so if the thread impl is decent it causes immediate exit.
914 We detach so we dont need to wait for it to exit if it isn't.
915 We check the reply port incase we had a reply in the mean time, which we free later */
916 d(printf("Cancelling lookup thread and leaving it\n"));
920 msg = (struct _lookup_msg *)e_msgport_get(reply_port);
922 struct _lookup_msg *reply = (struct _lookup_msg *)e_msgport_get(reply_port);
924 g_assert(reply == msg);
925 d(printf("waiting for child to exit\n"));
926 pthread_join(id, NULL);
927 d(printf("child done\n"));
931 e_msgport_destroy (reply_port);
935 camel_operation_end (NULL);
937 if (!camel_exception_is_set(ex)) {
938 if (msg->result == 0)
939 return &msg->hostbuf;
941 if (msg->herr == HOST_NOT_FOUND || msg->herr == NO_DATA)
942 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
943 _("Host lookup failed: host not found"));
945 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
946 _("Host lookup failed: unknown reason"));
950 g_free(msg->hostbufmem);
957 void camel_free_host(struct hostent *h)
959 struct _lookup_msg *msg;
961 g_return_if_fail(h != NULL);
963 /* yeah this looks ugly but it is safe. we passed out a reference to inside our structure, this maps it
964 to the base structure, so we can free everything right without having to keep track of it separately */
965 msg = (struct _lookup_msg *)(((char *)h) - STRUCT_OFFSET(struct _lookup_msg, hostbuf));
967 g_free(msg->hostbufmem);