* camel-lock-helper.c (g_strerror): have our own so we don't need
to link with glib.
* providers/*/Makefile.am: Do not install ANY provider header
files. No providers are subclassable. No providers are directly
linkable.
* camel.pc.in: create package config file.
* tests/lib/folders.c (test_folder_message_ops): updated counts for
delete also marking unread.
* tests/lib/camel-test.c (camel_test_provider_init): new api for
initialising test system 'in-build'.
* camel-provider.c: remove the assertions, init if we need to,k
use pthread_once stuff to serialise it.
* tests/folder/test3.c (main): remove gtk stuff (???).
* tests/*: Fix all the makefiles. Made make-check work 'in-build'.
* tests/lib/folders.c (test_folder_counts): update for api changes.
(test_message_info): similar.
* providers/Makefile.am: removed groupwise from the build, this
can't go in here anymore, not in its current state.
* camel-net-utils.c (camel_gethostbyaddr_r)
(camel_gethostbyname_r): the old e_gethost* calls from
e-host-utils.c.
2004-11-15 Not Zed <NotZed@Ximian.com>
* providers/imap/camel-imap-utils.c (imap_path_to_physical):
copied from e-path.c.
(imap_path_find_folders): copied from e-path.c.
* camel.h: remove the provider stuff from the header.
* camel-provider.c: globalise provider_init variable, and asserton
it in all functions that rely on it.
* camel-service.c: removed getaddrinfo/etc.
* camel-net-utils.[ch]: separate out camel_getaddrinfo etc.
* Makefile.am: split camel into 2 libraries, libcamel and
libcamel-store.
* camel-multipart-signed.c (camel_multipart_signed_sign)
(camel_multipart_signed_verify, prepare_sign): remove old
deprecated api.
* camel-multipart-encrypted.c (camel_multipart_encrypted_encrypt)
(camel_multipart_encrypted_decrypt): remove old deprecated api.
svn path=/trunk/; revision=28046
782 lines
20 KiB
C
782 lines
20 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
|
*
|
|
* Authors: Michael Zucchi <notzed@ximian.com>
|
|
* Jeffrey Stedfast <fejj@ximian.com>
|
|
* Chris Toshok <toshok@ximian.com>
|
|
*
|
|
* Copyright (C) 2004 Ximian Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of version 2 of the GNU General Public
|
|
* License as published by the Free Software Foundation.
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <sys/poll.h>
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#include "camel-i18n.h"
|
|
#include "camel-operation.h"
|
|
#include "camel-exception.h"
|
|
#include "camel-net-utils.h"
|
|
|
|
#include "libedataserver/e-msgport.h"
|
|
|
|
#define d(x)
|
|
|
|
/* gethostbyname emulation code for emulating getaddrinfo code ...
|
|
|
|
This should probably go away */
|
|
|
|
#ifdef NEED_ADDRINFO
|
|
|
|
#if !defined (HAVE_GETHOSTBYNAME_R) || !defined (HAVE_GETHOSTBYADDR_R)
|
|
G_LOCK_DEFINE_STATIC (gethost_mutex);
|
|
#endif
|
|
|
|
#define ALIGN(x) (((x) + (sizeof (char *) - 1)) & ~(sizeof (char *) - 1))
|
|
|
|
#define GETHOST_PROCESS(h, host, buf, buflen, herr) G_STMT_START { \
|
|
int num_aliases = 0, num_addrs = 0; \
|
|
int req_length; \
|
|
char *p; \
|
|
int i; \
|
|
\
|
|
/* check to make sure we have enough room in our buffer */ \
|
|
req_length = 0; \
|
|
if (h->h_aliases) { \
|
|
for (i = 0; h->h_aliases[i]; i++) \
|
|
req_length += strlen (h->h_aliases[i]) + 1; \
|
|
num_aliases = i; \
|
|
} \
|
|
\
|
|
if (h->h_addr_list) { \
|
|
for (i = 0; h->h_addr_list[i]; i++) \
|
|
req_length += h->h_length; \
|
|
num_addrs = i; \
|
|
} \
|
|
\
|
|
req_length += sizeof (char *) * (num_aliases + 1); \
|
|
req_length += sizeof (char *) * (num_addrs + 1); \
|
|
req_length += strlen (h->h_name) + 1; \
|
|
\
|
|
if (buflen < req_length) { \
|
|
*herr = ERANGE; \
|
|
G_UNLOCK (gethost_mutex); \
|
|
return ERANGE; \
|
|
} \
|
|
\
|
|
/* we store the alias/addr pointers in the buffer */ \
|
|
/* their addresses here. */ \
|
|
p = buf; \
|
|
if (num_aliases) { \
|
|
host->h_aliases = (char **) p; \
|
|
p += sizeof (char *) * (num_aliases + 1); \
|
|
} else \
|
|
host->h_aliases = NULL; \
|
|
\
|
|
if (num_addrs) { \
|
|
host->h_addr_list = (char **) p; \
|
|
p += sizeof (char *) * (num_addrs + 1); \
|
|
} else \
|
|
host->h_addr_list = NULL; \
|
|
\
|
|
/* copy the host name into the buffer */ \
|
|
host->h_name = p; \
|
|
strcpy (p, h->h_name); \
|
|
p += strlen (h->h_name) + 1; \
|
|
host->h_addrtype = h->h_addrtype; \
|
|
host->h_length = h->h_length; \
|
|
\
|
|
/* copy the aliases/addresses into the buffer */ \
|
|
/* and assign pointers into the hostent */ \
|
|
*p = 0; \
|
|
if (num_aliases) { \
|
|
for (i = 0; i < num_aliases; i++) { \
|
|
strcpy (p, h->h_aliases[i]); \
|
|
host->h_aliases[i] = p; \
|
|
p += strlen (h->h_aliases[i]); \
|
|
} \
|
|
host->h_aliases[num_aliases] = NULL; \
|
|
} \
|
|
\
|
|
if (num_addrs) { \
|
|
for (i = 0; i < num_addrs; i++) { \
|
|
memcpy (p, h->h_addr_list[i], h->h_length); \
|
|
host->h_addr_list[i] = p; \
|
|
p += h->h_length; \
|
|
} \
|
|
host->h_addr_list[num_addrs] = NULL; \
|
|
} \
|
|
} G_STMT_END
|
|
|
|
|
|
#ifdef ENABLE_IPv6
|
|
/* some helpful utils for IPv6 lookups */
|
|
#define IPv6_BUFLEN_MIN (sizeof (char *) * 3)
|
|
|
|
static int
|
|
ai_to_herr (int error)
|
|
{
|
|
switch (error) {
|
|
case EAI_NONAME:
|
|
case EAI_FAIL:
|
|
return HOST_NOT_FOUND;
|
|
break;
|
|
case EAI_SERVICE:
|
|
return NO_DATA;
|
|
break;
|
|
case EAI_ADDRFAMILY:
|
|
return NO_ADDRESS;
|
|
break;
|
|
case EAI_NODATA:
|
|
return NO_DATA;
|
|
break;
|
|
case EAI_MEMORY:
|
|
return ENOMEM;
|
|
break;
|
|
case EAI_AGAIN:
|
|
return TRY_AGAIN;
|
|
break;
|
|
case EAI_SYSTEM:
|
|
return errno;
|
|
break;
|
|
default:
|
|
return NO_RECOVERY;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif /* ENABLE_IPv6 */
|
|
|
|
static int
|
|
camel_gethostbyname_r (const char *name, struct hostent *host,
|
|
char *buf, size_t buflen, int *herr)
|
|
{
|
|
#ifdef ENABLE_IPv6
|
|
struct addrinfo hints, *res;
|
|
int retval, len;
|
|
char *addr;
|
|
|
|
memset (&hints, 0, sizeof (struct addrinfo));
|
|
#ifdef HAVE_AI_ADDRCONFIG
|
|
hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
|
|
#else
|
|
hints.ai_flags = AI_CANONNAME;
|
|
#endif
|
|
hints.ai_family = PF_UNSPEC;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
|
|
if ((retval = getaddrinfo (name, NULL, &hints, &res)) != 0) {
|
|
*herr = ai_to_herr (retval);
|
|
return -1;
|
|
}
|
|
|
|
len = ALIGN (strlen (res->ai_canonname) + 1);
|
|
if (buflen < IPv6_BUFLEN_MIN + len + res->ai_addrlen + sizeof (char *))
|
|
return ERANGE;
|
|
|
|
/* h_name */
|
|
strcpy (buf, res->ai_canonname);
|
|
host->h_name = buf;
|
|
buf += len;
|
|
|
|
/* h_aliases */
|
|
((char **) buf)[0] = NULL;
|
|
host->h_aliases = (char **) buf;
|
|
buf += sizeof (char *);
|
|
|
|
/* h_addrtype and h_length */
|
|
host->h_length = res->ai_addrlen;
|
|
if (res->ai_family == PF_INET6) {
|
|
host->h_addrtype = AF_INET6;
|
|
|
|
addr = (char *) &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
|
|
} else {
|
|
host->h_addrtype = AF_INET;
|
|
|
|
addr = (char *) &((struct sockaddr_in *) res->ai_addr)->sin_addr;
|
|
}
|
|
|
|
memcpy (buf, addr, host->h_length);
|
|
addr = buf;
|
|
buf += ALIGN (host->h_length);
|
|
|
|
/* h_addr_list */
|
|
((char **) buf)[0] = addr;
|
|
((char **) buf)[1] = NULL;
|
|
host->h_addr_list = (char **) buf;
|
|
|
|
freeaddrinfo (res);
|
|
|
|
return 0;
|
|
#else /* No support for IPv6 addresses */
|
|
#ifdef HAVE_GETHOSTBYNAME_R
|
|
#ifdef GETHOSTBYNAME_R_FIVE_ARGS
|
|
if (gethostbyname_r (name, host, buf, buflen, herr))
|
|
return 0;
|
|
else
|
|
return errno;
|
|
#else
|
|
struct hostent *hp;
|
|
int retval;
|
|
|
|
retval = gethostbyname_r (name, host, buf, buflen, &hp, herr);
|
|
if (hp != NULL) {
|
|
*herr = 0;
|
|
} else if (retval == 0) {
|
|
/* glibc 2.3.2 workaround - it seems that
|
|
* gethostbyname_r will sometimes return 0 on fail and
|
|
* not set the hostent values (hence the crash in bug
|
|
* #56337). Hopefully we can depend on @hp being NULL
|
|
* in this error case like we do with
|
|
* gethostbyaddr_r().
|
|
*/
|
|
retval = -1;
|
|
}
|
|
|
|
return retval;
|
|
#endif
|
|
#else /* No support for gethostbyname_r */
|
|
struct hostent *h;
|
|
|
|
G_LOCK (gethost_mutex);
|
|
|
|
h = gethostbyname (name);
|
|
|
|
if (!h) {
|
|
*herr = h_errno;
|
|
G_UNLOCK (gethost_mutex);
|
|
return -1;
|
|
}
|
|
|
|
GETHOST_PROCESS (h, host, buf, buflen, herr);
|
|
|
|
G_UNLOCK (gethost_mutex);
|
|
|
|
return 0;
|
|
#endif /* HAVE_GETHOSTBYNAME_R */
|
|
#endif /* ENABLE_IPv6 */
|
|
}
|
|
|
|
static int
|
|
camel_gethostbyaddr_r (const char *addr, int addrlen, int type, struct hostent *host,
|
|
char *buf, size_t buflen, int *herr)
|
|
{
|
|
#ifdef ENABLE_IPv6
|
|
int retval, len;
|
|
|
|
if ((retval = getnameinfo (addr, addrlen, buf, buflen, NULL, 0, NI_NAMEREQD)) != 0) {
|
|
*herr = ai_to_herr (retval);
|
|
return -1;
|
|
}
|
|
|
|
len = ALIGN (strlen (buf) + 1);
|
|
if (buflen < IPv6_BUFLEN_MIN + len + addrlen + sizeof (char *))
|
|
return ERANGE;
|
|
|
|
/* h_name */
|
|
host->h_name = buf;
|
|
buf += len;
|
|
|
|
/* h_aliases */
|
|
((char **) buf)[0] = NULL;
|
|
host->h_aliases = (char **) buf;
|
|
buf += sizeof (char *);
|
|
|
|
/* h_addrtype and h_length */
|
|
host->h_length = addrlen;
|
|
host->h_addrtype = type;
|
|
|
|
memcpy (buf, addr, host->h_length);
|
|
addr = buf;
|
|
buf += ALIGN (host->h_length);
|
|
|
|
/* h_addr_list */
|
|
((char **) buf)[0] = addr;
|
|
((char **) buf)[1] = NULL;
|
|
host->h_addr_list = (char **) buf;
|
|
|
|
return 0;
|
|
#else /* No support for IPv6 addresses */
|
|
#ifdef HAVE_GETHOSTBYADDR_R
|
|
#ifdef GETHOSTBYADDR_R_SEVEN_ARGS
|
|
if (gethostbyaddr_r (addr, addrlen, type, host, buf, buflen, herr))
|
|
return 0;
|
|
else
|
|
return errno;
|
|
#else
|
|
struct hostent *hp;
|
|
int retval;
|
|
|
|
retval = gethostbyaddr_r (addr, addrlen, type, host, buf, buflen, &hp, herr);
|
|
if (hp != NULL) {
|
|
*herr = 0;
|
|
retval = 0;
|
|
} else if (retval == 0) {
|
|
/* glibc 2.3.2 workaround - it seems that
|
|
* gethostbyaddr_r will sometimes return 0 on fail and
|
|
* fill @host with garbage strings from /etc/hosts
|
|
* (failure to parse the file? who knows). Luckily, it
|
|
* seems that we can rely on @hp being NULL on
|
|
* fail.
|
|
*/
|
|
retval = -1;
|
|
}
|
|
|
|
return retval;
|
|
#endif
|
|
#else /* No support for gethostbyaddr_r */
|
|
struct hostent *h;
|
|
|
|
G_LOCK (gethost_mutex);
|
|
|
|
h = gethostbyaddr (addr, addrlen, type);
|
|
|
|
if (!h) {
|
|
*herr = h_errno;
|
|
G_UNLOCK (gethost_mutex);
|
|
return -1;
|
|
}
|
|
|
|
GETHOST_PROCESS (h, host, buf, buflen, herr);
|
|
|
|
G_UNLOCK (gethost_mutex);
|
|
|
|
return 0;
|
|
#endif /* HAVE_GETHOSTBYADDR_R */
|
|
#endif /* ENABLE_IPv6 */
|
|
}
|
|
#endif /* NEED_ADDRINFO */
|
|
|
|
/* ********************************************************************** */
|
|
struct _addrinfo_msg {
|
|
EMsg msg;
|
|
unsigned int cancelled:1;
|
|
|
|
/* for host lookup */
|
|
const char *name;
|
|
const char *service;
|
|
int result;
|
|
const struct addrinfo *hints;
|
|
struct addrinfo **res;
|
|
|
|
/* for host lookup emulation */
|
|
#ifdef NEED_ADDRINFO
|
|
struct hostent hostbuf;
|
|
int hostbuflen;
|
|
char *hostbufmem;
|
|
#endif
|
|
|
|
/* for name lookup */
|
|
const struct sockaddr *addr;
|
|
socklen_t addrlen;
|
|
char *host;
|
|
int hostlen;
|
|
char *serv;
|
|
int servlen;
|
|
int flags;
|
|
};
|
|
|
|
static void
|
|
cs_freeinfo(struct _addrinfo_msg *msg)
|
|
{
|
|
g_free(msg->host);
|
|
g_free(msg->serv);
|
|
#ifdef NEED_ADDRINFO
|
|
g_free(msg->hostbufmem);
|
|
#endif
|
|
g_free(msg);
|
|
}
|
|
|
|
/* returns -1 if cancelled */
|
|
static int
|
|
cs_waitinfo(void *(worker)(void *), struct _addrinfo_msg *msg, const char *error, CamelException *ex)
|
|
{
|
|
EMsgPort *reply_port;
|
|
pthread_t id;
|
|
int err, cancel_fd, cancel = 0, fd;
|
|
|
|
cancel_fd = camel_operation_cancel_fd(NULL);
|
|
if (cancel_fd == -1) {
|
|
worker(msg);
|
|
return 0;
|
|
}
|
|
|
|
reply_port = msg->msg.reply_port = e_msgport_new();
|
|
fd = e_msgport_fd(msg->msg.reply_port);
|
|
if ((err = pthread_create(&id, NULL, worker, msg)) == 0) {
|
|
struct pollfd polls[2];
|
|
int status;
|
|
|
|
polls[0].fd = fd;
|
|
polls[0].events = POLLIN;
|
|
polls[1].fd = cancel_fd;
|
|
polls[1].events = POLLIN;
|
|
|
|
d(printf("waiting for name return/cancellation in main process\n"));
|
|
do {
|
|
polls[0].revents = 0;
|
|
polls[1].revents = 0;
|
|
status = poll(polls, 2, -1);
|
|
} while (status == -1 && errno == EINTR);
|
|
|
|
if (status == -1 || (polls[1].revents & POLLIN)) {
|
|
if (status == -1)
|
|
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, "%s: %s", error, g_strerror(errno));
|
|
else
|
|
camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled"));
|
|
|
|
/* We cancel so if the thread impl is decent it causes immediate exit.
|
|
We detach so we dont need to wait for it to exit if it isn't.
|
|
We check the reply port incase we had a reply in the mean time, which we free later */
|
|
d(printf("Cancelling lookup thread and leaving it\n"));
|
|
msg->cancelled = 1;
|
|
pthread_detach(id);
|
|
pthread_cancel(id);
|
|
cancel = 1;
|
|
} else {
|
|
struct _addrinfo_msg *reply = (struct _addrinfo_msg *)e_msgport_get(reply_port);
|
|
|
|
g_assert(reply == msg);
|
|
d(printf("waiting for child to exit\n"));
|
|
pthread_join(id, NULL);
|
|
d(printf("child done\n"));
|
|
}
|
|
} else {
|
|
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, "%s: %s: %s", error, _("cannot create thread"), g_strerror(err));
|
|
}
|
|
e_msgport_destroy(reply_port);
|
|
|
|
return cancel;
|
|
}
|
|
|
|
#ifdef NEED_ADDRINFO
|
|
static void *
|
|
cs_getaddrinfo(void *data)
|
|
{
|
|
struct _addrinfo_msg *msg = data;
|
|
int herr;
|
|
struct hostent h;
|
|
struct addrinfo *res, *last = NULL;
|
|
struct sockaddr_in *sin;
|
|
in_port_t port = 0;
|
|
int i;
|
|
|
|
/* This is a pretty simplistic emulation of getaddrinfo */
|
|
|
|
while ((msg->result = camel_gethostbyname_r(msg->name, &h, msg->hostbufmem, msg->hostbuflen, &herr)) == ERANGE) {
|
|
pthread_testcancel();
|
|
msg->hostbuflen *= 2;
|
|
msg->hostbufmem = g_realloc(msg->hostbufmem, msg->hostbuflen);
|
|
}
|
|
|
|
/* If we got cancelled, dont reply, just free it */
|
|
if (msg->cancelled)
|
|
goto cancel;
|
|
|
|
/* FIXME: map error numbers across */
|
|
if (msg->result != 0)
|
|
goto reply;
|
|
|
|
/* check hints matched */
|
|
if (msg->hints && msg->hints->ai_family && msg->hints->ai_family != h.h_addrtype) {
|
|
msg->result = EAI_FAMILY;
|
|
goto reply;
|
|
}
|
|
|
|
/* we only support ipv4 for this interface, even if it could supply ipv6 */
|
|
if (h.h_addrtype != AF_INET) {
|
|
msg->result = EAI_FAMILY;
|
|
goto reply;
|
|
}
|
|
|
|
/* check service mapping */
|
|
if (msg->service) {
|
|
const char *p = msg->service;
|
|
|
|
while (*p) {
|
|
if (*p < '0' || *p > '9')
|
|
break;
|
|
p++;
|
|
}
|
|
|
|
if (*p) {
|
|
const char *socktype = NULL;
|
|
struct servent *serv;
|
|
|
|
if (msg->hints && msg->hints->ai_socktype) {
|
|
if (msg->hints->ai_socktype == SOCK_STREAM)
|
|
socktype = "tcp";
|
|
else if (msg->hints->ai_socktype == SOCK_DGRAM)
|
|
socktype = "udp";
|
|
}
|
|
|
|
serv = getservbyname(msg->service, socktype);
|
|
if (serv == NULL) {
|
|
msg->result = EAI_NONAME;
|
|
goto reply;
|
|
}
|
|
port = serv->s_port;
|
|
} else {
|
|
port = htons(strtoul(msg->service, NULL, 10));
|
|
}
|
|
}
|
|
|
|
for (i=0;h.h_addr_list[i];i++) {
|
|
res = g_malloc0(sizeof(*res));
|
|
if (msg->hints) {
|
|
res->ai_flags = msg->hints->ai_flags;
|
|
if (msg->hints->ai_flags & AI_CANONNAME)
|
|
res->ai_canonname = g_strdup(h.h_name);
|
|
res->ai_socktype = msg->hints->ai_socktype;
|
|
res->ai_protocol = msg->hints->ai_protocol;
|
|
} else {
|
|
res->ai_flags = 0;
|
|
res->ai_socktype = SOCK_STREAM; /* fudge */
|
|
res->ai_protocol = 0; /* fudge */
|
|
}
|
|
res->ai_family = AF_INET;
|
|
res->ai_addrlen = sizeof(*sin);
|
|
res->ai_addr = g_malloc(sizeof(*sin));
|
|
sin = (struct sockaddr_in *)res->ai_addr;
|
|
sin->sin_family = AF_INET;
|
|
sin->sin_port = port;
|
|
memcpy(&sin->sin_addr, h.h_addr_list[i], sizeof(sin->sin_addr));
|
|
|
|
if (last == NULL) {
|
|
*msg->res = last = res;
|
|
} else {
|
|
last->ai_next = res;
|
|
last = res;
|
|
}
|
|
}
|
|
reply:
|
|
e_msgport_reply((EMsg *)msg);
|
|
return NULL;
|
|
cancel:
|
|
cs_freeinfo(msg);
|
|
return NULL;
|
|
}
|
|
#else
|
|
static void *
|
|
cs_getaddrinfo(void *data)
|
|
{
|
|
struct _addrinfo_msg *info = data;
|
|
|
|
info->result = getaddrinfo(info->name, info->service, info->hints, info->res);
|
|
|
|
if (info->cancelled) {
|
|
g_free(info);
|
|
} else {
|
|
e_msgport_reply((EMsg *)info);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif /* NEED_ADDRINFO */
|
|
|
|
struct addrinfo *
|
|
camel_getaddrinfo(const char *name, const char *service, const struct addrinfo *hints, CamelException *ex)
|
|
{
|
|
struct _addrinfo_msg *msg;
|
|
struct addrinfo *res = NULL;
|
|
#ifndef ENABLE_IPv6
|
|
struct addrinfo myhints;
|
|
#endif
|
|
g_return_val_if_fail(name != NULL, NULL);
|
|
|
|
if (camel_operation_cancel_check(NULL)) {
|
|
camel_exception_set(ex, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled"));
|
|
return NULL;
|
|
}
|
|
|
|
camel_operation_start_transient(NULL, _("Resolving: %s"), name);
|
|
|
|
/* force ipv4 addresses only */
|
|
#ifndef ENABLE_IPv6
|
|
if (hints == NULL)
|
|
memset(&myhints, 0, sizeof(myhints));
|
|
else
|
|
memcpy (&myhints, hints, sizeof (myhints));
|
|
|
|
myhints.ai_family = AF_INET;
|
|
hints = &myhints;
|
|
#endif
|
|
|
|
msg = g_malloc0(sizeof(*msg));
|
|
msg->name = name;
|
|
msg->service = service;
|
|
msg->hints = hints;
|
|
msg->res = &res;
|
|
#ifdef NEED_ADDRINFO
|
|
msg->hostbuflen = 1024;
|
|
msg->hostbufmem = g_malloc(msg->hostbuflen);
|
|
#endif
|
|
if (cs_waitinfo(cs_getaddrinfo, msg, _("Host lookup failed"), ex) == 0) {
|
|
if (msg->result != 0)
|
|
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Host lookup failed: %s: %s"),
|
|
name, gai_strerror (msg->result));
|
|
|
|
cs_freeinfo(msg);
|
|
} else
|
|
res = NULL;
|
|
|
|
camel_operation_end(NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
void
|
|
camel_freeaddrinfo(struct addrinfo *host)
|
|
{
|
|
#ifdef NEED_ADDRINFO
|
|
while (host) {
|
|
struct addrinfo *next = host->ai_next;
|
|
|
|
g_free(host->ai_canonname);
|
|
g_free(host->ai_addr);
|
|
g_free(host);
|
|
host = next;
|
|
}
|
|
#else
|
|
freeaddrinfo(host);
|
|
#endif
|
|
}
|
|
|
|
#ifdef NEED_ADDRINFO
|
|
static void *
|
|
cs_getnameinfo(void *data)
|
|
{
|
|
struct _addrinfo_msg *msg = data;
|
|
int herr;
|
|
struct hostent h;
|
|
struct sockaddr_in *sin = (struct sockaddr_in *)msg->addr;
|
|
|
|
/* FIXME: error code */
|
|
if (msg->addr->sa_family != AF_INET) {
|
|
msg->result = -1;
|
|
return NULL;
|
|
}
|
|
|
|
/* FIXME: honour getnameinfo flags: do we care, not really */
|
|
|
|
while ((msg->result = camel_gethostbyaddr_r((const char *)&sin->sin_addr, sizeof(sin->sin_addr), AF_INET, &h,
|
|
msg->hostbufmem, msg->hostbuflen, &herr)) == ERANGE) {
|
|
pthread_testcancel ();
|
|
msg->hostbuflen *= 2;
|
|
msg->hostbufmem = g_realloc(msg->hostbufmem, msg->hostbuflen);
|
|
}
|
|
|
|
if (msg->cancelled)
|
|
goto cancel;
|
|
|
|
if (msg->host) {
|
|
g_free(msg->host);
|
|
if (msg->result == 0 && h.h_name && h.h_name[0]) {
|
|
msg->host = g_strdup(h.h_name);
|
|
} else {
|
|
unsigned char *in = (unsigned char *)&sin->sin_addr;
|
|
|
|
/* sin_addr is always network order which is big-endian */
|
|
msg->host = g_strdup_printf("%u.%u.%u.%u", in[0], in[1], in[2], in[3]);
|
|
}
|
|
}
|
|
|
|
/* we never actually use this anyway */
|
|
if (msg->serv)
|
|
sprintf(msg->serv, "%d", sin->sin_port);
|
|
|
|
e_msgport_reply((EMsg *)msg);
|
|
return NULL;
|
|
cancel:
|
|
cs_freeinfo(msg);
|
|
return NULL;
|
|
}
|
|
#else
|
|
static void *
|
|
cs_getnameinfo(void *data)
|
|
{
|
|
struct _addrinfo_msg *msg = data;
|
|
|
|
/* there doens't appear to be a return code which says host or serv buffers are too short, lengthen them */
|
|
msg->result = getnameinfo(msg->addr, msg->addrlen, msg->host, msg->hostlen, msg->serv, msg->servlen, msg->flags);
|
|
|
|
if (msg->cancelled)
|
|
cs_freeinfo(msg);
|
|
else
|
|
e_msgport_reply((EMsg *)msg);
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
camel_getnameinfo(const struct sockaddr *sa, socklen_t salen, char **host, char **serv, int flags, CamelException *ex)
|
|
{
|
|
struct _addrinfo_msg *msg;
|
|
int result;
|
|
|
|
if (camel_operation_cancel_check(NULL)) {
|
|
camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Cancelled"));
|
|
return -1;
|
|
}
|
|
|
|
camel_operation_start_transient(NULL, _("Resolving address"));
|
|
|
|
msg = g_malloc0(sizeof(*msg));
|
|
msg->addr = sa;
|
|
msg->addrlen = salen;
|
|
if (host) {
|
|
msg->hostlen = NI_MAXHOST;
|
|
msg->host = g_malloc(msg->hostlen);
|
|
msg->host[0] = 0;
|
|
}
|
|
if (serv) {
|
|
msg->servlen = NI_MAXSERV;
|
|
msg->serv = g_malloc(msg->servlen);
|
|
msg->serv[0] = 0;
|
|
}
|
|
msg->flags = flags;
|
|
#ifdef NEED_ADDRINFO
|
|
msg->hostbuflen = 1024;
|
|
msg->hostbufmem = g_malloc(msg->hostbuflen);
|
|
#endif
|
|
cs_waitinfo(cs_getnameinfo, msg, _("Name lookup failed"), ex);
|
|
|
|
if ((result = msg->result) != 0)
|
|
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Name lookup failed: %s"),
|
|
gai_strerror (result));
|
|
|
|
if (host)
|
|
*host = g_strdup(msg->host);
|
|
if (serv)
|
|
*serv = g_strdup(msg->serv);
|
|
|
|
g_free(msg->host);
|
|
g_free(msg->serv);
|
|
g_free(msg);
|
|
|
|
camel_operation_end(NULL);
|
|
|
|
return result;
|
|
}
|