Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[platform/upstream/evolution-data-server.git] / camel / camel-net-utils.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  * Authors: Michael Zucchi <notzed@ximian.com>
4  *          Jeffrey Stedfast <fejj@ximian.com>
5  *          Chris Toshok <toshok@ximian.com>
6  *
7  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of version 2 of the GNU Lesser General Public
11  * License as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21  * USA
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <errno.h>
29 #include <stdio.h>
30
31 #include <glib/gi18n-lib.h>
32
33 #include "camel-msgport.h"
34 #include "camel-net-utils.h"
35 #ifdef G_OS_WIN32
36 #include <winsock2.h>
37 #include <ws2tcpip.h>
38 #ifdef HAVE_WSPIAPI_H
39 #include <wspiapi.h>
40 #endif
41 #endif
42 #include "camel-object.h"
43 #include "camel-operation.h"
44
45 #define d(x)
46
47 /* These are GNU extensions */
48 #ifndef NI_MAXHOST
49 #define NI_MAXHOST      1025
50 #endif
51 #ifndef NI_MAXSERV
52 #define NI_MAXSERV      32
53 #endif
54
55 #ifdef G_OS_WIN32
56
57 typedef gshort in_port_t;
58
59 #undef gai_strerror
60 #define gai_strerror my_gai_strerror
61
62 /* gai_strerror() is implemented as an inline function in Microsoft's
63  * SDK, but mingw lacks that. So implement here. The EAI_* errors can
64  * be handled with the normal FormatMessage() API,
65  * i.e. g_win32_error_message().
66  */
67
68 static const gchar *
69 gai_strerror (gint error_code)
70 {
71         gchar *msg = g_win32_error_message (error_code);
72         GQuark quark = g_quark_from_string (msg);
73         const gchar *retval = g_quark_to_string (quark);
74
75         g_free (msg);
76
77         return retval;
78 }
79
80 #endif
81
82 /* gethostbyname emulation code for emulating getaddrinfo code ...
83  *
84  * This should probably go away */
85
86 #ifdef NEED_ADDRINFO
87
88 #if !defined (HAVE_GETHOSTBYNAME_R) || !defined (HAVE_GETHOSTBYADDR_R)
89 G_LOCK_DEFINE_STATIC (gethost_mutex);
90 #endif
91
92 #define ALIGN(x) (((x) + (sizeof (gchar *) - 1)) & ~(sizeof (gchar *) - 1))
93
94 #define GETHOST_PROCESS(h, host, buf, buflen, herr) G_STMT_START {     \
95         gint num_aliases = 0, num_addrs = 0;                            \
96         gint req_length;                                                \
97         gchar *p;                                                       \
98         gint i;                                                         \
99                                                                        \
100         /* check to make sure we have enough room in our buffer */     \
101         req_length = 0;                                                \
102         if (h->h_aliases) {                                            \
103                 for (i = 0; h->h_aliases[i]; i++)                      \
104                         req_length += strlen (h->h_aliases[i]) + 1;    \
105                 num_aliases = i;                                       \
106         }                                                              \
107                                                                        \
108         if (h->h_addr_list) {                                          \
109                 for (i = 0; h->h_addr_list[i]; i++)                    \
110                         req_length += h->h_length;                     \
111                 num_addrs = i;                                         \
112         }                                                              \
113                                                                        \
114         req_length += sizeof (gchar *) * (num_aliases + 1);             \
115         req_length += sizeof (gchar *) * (num_addrs + 1);               \
116         req_length += strlen (h->h_name) + 1;                          \
117                                                                        \
118         if (buflen < req_length) {                                     \
119                 *herr = ERANGE;                                        \
120                 G_UNLOCK (gethost_mutex);                              \
121                 return ERANGE;                                         \
122         }                                                              \
123                                                                        \
124         /* we store the alias/addr pointers in the buffer */           \
125         /* their addresses here. */                                    \
126         p = buf;                                                       \
127         if (num_aliases) {                                             \
128                 host->h_aliases = (gchar **) p;                         \
129                 p += sizeof (gchar *) * (num_aliases + 1);              \
130         } else                                                         \
131                 host->h_aliases = NULL;                                \
132                                                                        \
133         if (num_addrs) {                                               \
134                 host->h_addr_list = (gchar **) p;                       \
135                 p += sizeof (gchar *) * (num_addrs + 1);                \
136         } else                                                         \
137                 host->h_addr_list = NULL;                              \
138                                                                        \
139         /* copy the host name into the buffer */                       \
140         host->h_name = p;                                              \
141         strcpy (p, h->h_name);                                         \
142         p += strlen (h->h_name) + 1;                                   \
143         host->h_addrtype = h->h_addrtype;                              \
144         host->h_length = h->h_length;                                  \
145                                                                        \
146         /* copy the aliases/addresses into the buffer */               \
147         /* and assign pointers into the hostent */                     \
148         *p = 0;                                                        \
149         if (num_aliases) {                                             \
150                 for (i = 0; i < num_aliases; i++) {                    \
151                         strcpy (p, h->h_aliases[i]);                   \
152                         host->h_aliases[i] = p;                        \
153                         p += strlen (h->h_aliases[i]);                 \
154                 }                                                      \
155                 host->h_aliases[num_aliases] = NULL;                   \
156         }                                                              \
157                                                                        \
158         if (num_addrs) {                                               \
159                 for (i = 0; i < num_addrs; i++) {                      \
160                         memcpy (p, h->h_addr_list[i], h->h_length);    \
161                         host->h_addr_list[i] = p;                      \
162                         p += h->h_length;                              \
163                 }                                                      \
164                 host->h_addr_list[num_addrs] = NULL;                   \
165         }                                                              \
166 } G_STMT_END
167
168 #ifdef ENABLE_IPv6
169 /* some helpful utils for IPv6 lookups */
170 #define IPv6_BUFLEN_MIN  (sizeof (gchar *) * 3)
171
172 static gint
173 ai_to_herr (gint error)
174 {
175         switch (error) {
176         case EAI_NONAME:
177         case EAI_FAIL:
178                 return HOST_NOT_FOUND;
179                 break;
180         case EAI_SERVICE:
181                 return NO_DATA;
182                 break;
183         case EAI_ADDRFAMILY:
184                 return NO_ADDRESS;
185                 break;
186         case EAI_NODATA:
187                 return NO_DATA;
188                 break;
189         case EAI_MEMORY:
190                 return ENOMEM;
191                 break;
192         case EAI_AGAIN:
193                 return TRY_AGAIN;
194                 break;
195         case EAI_SYSTEM:
196                 return errno;
197                 break;
198         default:
199                 return NO_RECOVERY;
200                 break;
201         }
202 }
203
204 #endif /* ENABLE_IPv6 */
205
206 static gint
207 camel_gethostbyname_r (const gchar *name,
208                        struct hostent *host,
209                        gchar *buf,
210                        gsize buflen,
211                        gint *herr)
212 {
213 #ifdef ENABLE_IPv6
214         struct addrinfo hints, *res;
215         gint retval, len;
216         gchar *addr;
217
218         memset (&hints, 0, sizeof (struct addrinfo));
219 #ifdef HAVE_AI_ADDRCONFIG
220         hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
221 #else
222         hints.ai_flags = AI_CANONNAME;
223 #endif
224         hints.ai_family = PF_UNSPEC;
225         hints.ai_socktype = SOCK_STREAM;
226         hints.ai_protocol = IPPROTO_TCP;
227
228         if ((retval = getaddrinfo (name, NULL, &hints, &res)) != 0) {
229                 *herr = ai_to_herr (retval);
230                 return -1;
231         }
232
233         len = ALIGN (strlen (res->ai_canonname) + 1);
234         if (buflen < IPv6_BUFLEN_MIN + len + res->ai_addrlen + sizeof (gchar *))
235                 return ERANGE;
236
237         /* h_name */
238         strcpy (buf, res->ai_canonname);
239         host->h_name = buf;
240         buf += len;
241
242         /* h_aliases */
243         ((gchar **) buf)[0] = NULL;
244         host->h_aliases = (gchar **) buf;
245         buf += sizeof (gchar *);
246
247         /* h_addrtype and h_length */
248         host->h_length = res->ai_addrlen;
249         if (res->ai_family == PF_INET6) {
250                 host->h_addrtype = AF_INET6;
251
252                 addr = (gchar *) &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
253         } else {
254                 host->h_addrtype = AF_INET;
255
256                 addr = (gchar *) &((struct sockaddr_in *) res->ai_addr)->sin_addr;
257         }
258
259         memcpy (buf, addr, host->h_length);
260         addr = buf;
261         buf += ALIGN (host->h_length);
262
263         /* h_addr_list */
264         ((gchar **) buf)[0] = addr;
265         ((gchar **) buf)[1] = NULL;
266         host->h_addr_list = (gchar **) buf;
267
268         freeaddrinfo (res);
269
270         return 0;
271 #else /* No support for IPv6 addresses */
272 #ifdef HAVE_GETHOSTBYNAME_R
273 #ifdef GETHOSTBYNAME_R_FIVE_ARGS
274         if (gethostbyname_r (name, host, buf, buflen, herr))
275                 return 0;
276         else
277                 return errno;
278 #else
279         struct hostent *hp;
280         gint retval;
281
282         retval = gethostbyname_r (name, host, buf, buflen, &hp, herr);
283         if (hp != NULL) {
284                 *herr = 0;
285         } else if (retval == 0) {
286                 /* glibc 2.3.2 workaround - it seems that
287                  * gethostbyname_r will sometimes return 0 on fail and
288                  * not set the hostent values (hence the crash in bug
289                  * #56337).  Hopefully we can depend on @hp being NULL
290                  * in this error case like we do with
291                  * gethostbyaddr_r().
292                  */
293                 retval = -1;
294         }
295
296         return retval;
297 #endif
298 #else /* No support for gethostbyname_r */
299         struct hostent *h;
300
301         G_LOCK (gethost_mutex);
302
303         h = gethostbyname (name);
304
305         if (!h) {
306                 *herr = h_errno;
307                 G_UNLOCK (gethost_mutex);
308                 return -1;
309         }
310
311         GETHOST_PROCESS (h, host, buf, buflen, herr);
312
313         G_UNLOCK (gethost_mutex);
314
315         return 0;
316 #endif /* HAVE_GETHOSTBYNAME_R */
317 #endif /* ENABLE_IPv6 */
318 }
319
320 static gint
321 camel_gethostbyaddr_r (const gchar *addr,
322                        gint addrlen,
323                        gint type,
324                        struct hostent *host,
325                        gchar *buf,
326                        gsize buflen,
327                        gint *herr)
328 {
329 #ifdef ENABLE_IPv6
330         gint retval, len;
331
332         if ((retval = getnameinfo (addr, addrlen, buf, buflen, NULL, 0, NI_NAMEREQD)) != 0) {
333                 *herr = ai_to_herr (retval);
334                 return -1;
335         }
336
337         len = ALIGN (strlen (buf) + 1);
338         if (buflen < IPv6_BUFLEN_MIN + len + addrlen + sizeof (gchar *))
339                 return ERANGE;
340
341         /* h_name */
342         host->h_name = buf;
343         buf += len;
344
345         /* h_aliases */
346         ((gchar **) buf)[0] = NULL;
347         host->h_aliases = (gchar **) buf;
348         buf += sizeof (gchar *);
349
350         /* h_addrtype and h_length */
351         host->h_length = addrlen;
352         host->h_addrtype = type;
353
354         memcpy (buf, addr, host->h_length);
355         addr = buf;
356         buf += ALIGN (host->h_length);
357
358         /* h_addr_list */
359         ((gchar **) buf)[0] = addr;
360         ((gchar **) buf)[1] = NULL;
361         host->h_addr_list = (gchar **) buf;
362
363         return 0;
364 #else /* No support for IPv6 addresses */
365 #ifdef HAVE_GETHOSTBYADDR_R
366 #ifdef GETHOSTBYADDR_R_SEVEN_ARGS
367         if (gethostbyaddr_r (addr, addrlen, type, host, buf, buflen, herr))
368                 return 0;
369         else
370                 return errno;
371 #else
372         struct hostent *hp;
373         gint retval;
374
375         retval = gethostbyaddr_r (addr, addrlen, type, host, buf, buflen, &hp, herr);
376         if (hp != NULL) {
377                 *herr = 0;
378                 retval = 0;
379         } else if (retval == 0) {
380                 /* glibc 2.3.2 workaround - it seems that
381                  * gethostbyaddr_r will sometimes return 0 on fail and
382                  * fill @host with garbage strings from /etc/hosts
383                  * (failure to parse the file? who knows). Luckily, it
384                  * seems that we can rely on @hp being NULL on
385                  * fail.
386                  */
387                 retval = -1;
388         }
389
390         return retval;
391 #endif
392 #else /* No support for gethostbyaddr_r */
393         struct hostent *h;
394
395         G_LOCK (gethost_mutex);
396
397         h = gethostbyaddr (addr, addrlen, type);
398
399         if (!h) {
400                 *herr = h_errno;
401                 G_UNLOCK (gethost_mutex);
402                 return -1;
403         }
404
405         GETHOST_PROCESS (h, host, buf, buflen, herr);
406
407         G_UNLOCK (gethost_mutex);
408
409         return 0;
410 #endif /* HAVE_GETHOSTBYADDR_R */
411 #endif /* ENABLE_IPv6 */
412 }
413 #endif /* NEED_ADDRINFO */
414
415 /* ********************************************************************** */
416 struct _addrinfo_msg {
417         CamelMsg msg;
418         guint cancelled : 1;
419
420         /* for host lookup */
421         const gchar *name;
422         const gchar *service;
423         gint result;
424         const struct addrinfo *hints;
425         struct addrinfo **res;
426
427         /* for host lookup emulation */
428 #ifdef NEED_ADDRINFO
429         struct hostent hostbuf;
430         gint hostbuflen;
431         gchar *hostbufmem;
432 #endif
433
434         /* for name lookup */
435         const struct sockaddr *addr;
436         socklen_t addrlen;
437         gchar *host;
438         gint hostlen;
439         gchar *serv;
440         gint servlen;
441         gint flags;
442 };
443
444 static void
445 cs_freeinfo (struct _addrinfo_msg *msg)
446 {
447         g_free (msg->host);
448         g_free (msg->serv);
449 #ifdef NEED_ADDRINFO
450         g_free (msg->hostbufmem);
451 #endif
452         g_free (msg);
453 }
454
455 /* returns -1 if we didn't wait for reply from thread */
456 static gint
457 cs_waitinfo (gpointer (worker)(gpointer),
458              struct _addrinfo_msg *msg,
459              const gchar *errmsg,
460              GCancellable *cancellable,
461              GError **error)
462 {
463         CamelMsgPort *reply_port;
464         GThread *thread;
465         gint cancel_fd, cancel = 0, fd;
466
467         cancel_fd = g_cancellable_get_fd (cancellable);
468         if (cancel_fd == -1) {
469                 worker (msg);
470                 return 0;
471         }
472
473         reply_port = msg->msg.reply_port = camel_msgport_new ();
474         fd = camel_msgport_fd (msg->msg.reply_port);
475         if ((thread = g_thread_new (NULL, worker, msg)) != NULL) {
476                 gint status;
477 #ifndef G_OS_WIN32
478                 GPollFD polls[2];
479
480                 polls[0].fd = fd;
481                 polls[0].events = G_IO_IN;
482                 polls[1].fd = cancel_fd;
483                 polls[1].events = G_IO_IN;
484
485                 d (printf ("waiting for name return/cancellation in main process\n"));
486                 do {
487                         polls[0].revents = 0;
488                         polls[1].revents = 0;
489                         status = g_poll (polls, 2, -1);
490                 } while (status == -1 && errno == EINTR);
491 #else
492                 fd_set read_set;
493
494                 FD_ZERO (&read_set);
495                 FD_SET (fd, &read_set);
496                 FD_SET (cancel_fd, &read_set);
497
498                 status = select (MAX (fd, cancel_fd) + 1, &read_set, NULL, NULL, NULL);
499 #endif
500
501                 if (status == -1 ||
502 #ifndef G_OS_WIN32
503                     (polls[1].revents & G_IO_IN)
504 #else
505                     FD_ISSET (cancel_fd, &read_set)
506 #endif
507                                                    ) {
508                         if (status == -1)
509                                 g_set_error (
510                                         error, G_IO_ERROR,
511                                         g_io_error_from_errno (errno),
512                                         "%s: %s", errmsg,
513 #ifndef G_OS_WIN32
514                                         g_strerror (errno)
515 #else
516                                         g_win32_error_message (WSAGetLastError ())
517 #endif
518                                         );
519                         else
520                                 g_set_error (
521                                         error, G_IO_ERROR,
522                                         G_IO_ERROR_CANCELLED,
523                                         _("Cancelled"));
524
525                         /* We cancel so if the thread impl is decent it causes immediate exit.
526                          * We check the reply port incase we had a reply in the mean time, which we free later */
527                         d (printf ("Canceling lookup thread and leaving it\n"));
528                         msg->cancelled = 1;
529                         g_thread_join (thread);
530                         cancel = 1;
531                 } else {
532                         struct _addrinfo_msg *reply;
533
534                         d (printf ("waiting for child to exit\n"));
535                         g_thread_join (thread);
536                         d (printf ("child done\n"));
537
538                         reply = (struct _addrinfo_msg *) camel_msgport_try_pop (reply_port);
539                         if (reply != msg)
540                                 g_warning ("%s: Received msg reply %p doesn't match msg %p", G_STRFUNC, reply, msg);
541                 }
542         }
543         camel_msgport_destroy (reply_port);
544
545         g_cancellable_release_fd (cancellable);
546
547         return cancel;
548 }
549
550 #ifdef NEED_ADDRINFO
551 static gpointer
552 cs_getaddrinfo (gpointer data)
553 {
554         struct _addrinfo_msg *msg = data;
555         gint herr;
556         struct hostent h;
557         struct addrinfo *res, *last = NULL;
558         struct sockaddr_in *sin;
559         in_port_t port = 0;
560         gint i;
561
562         /* This is a pretty simplistic emulation of getaddrinfo */
563
564         while ((msg->result = camel_gethostbyname_r (msg->name, &h, msg->hostbufmem, msg->hostbuflen, &herr)) == ERANGE) {
565                 if (msg->cancelled)
566                         break;
567                 msg->hostbuflen *= 2;
568                 msg->hostbufmem = g_realloc (msg->hostbufmem, msg->hostbuflen);
569         }
570
571         /* If we got cancelled, dont reply, just free it */
572         if (msg->cancelled)
573                 goto cancel;
574
575         /* FIXME: map error numbers across */
576         if (msg->result != 0)
577                 goto reply;
578
579         /* check hints matched */
580         if (msg->hints && msg->hints->ai_family && msg->hints->ai_family != h.h_addrtype) {
581                 msg->result = EAI_FAMILY;
582                 goto reply;
583         }
584
585         /* we only support ipv4 for this interface, even if it could supply ipv6 */
586         if (h.h_addrtype != AF_INET) {
587                 msg->result = EAI_FAMILY;
588                 goto reply;
589         }
590
591         /* check service mapping */
592         if (msg->service) {
593                 const gchar *p = msg->service;
594
595                 while (*p) {
596                         if (*p < '0' || *p > '9')
597                                 break;
598                         p++;
599                 }
600
601                 if (*p) {
602                         const gchar *socktype = NULL;
603                         struct servent *serv;
604
605                         if (msg->hints && msg->hints->ai_socktype) {
606                                 if (msg->hints->ai_socktype == SOCK_STREAM)
607                                         socktype = "tcp";
608                                 else if (msg->hints->ai_socktype == SOCK_DGRAM)
609                                         socktype = "udp";
610                         }
611
612                         serv = getservbyname (msg->service, socktype);
613                         if (serv == NULL) {
614                                 msg->result = EAI_NONAME;
615                                 goto reply;
616                         }
617                         port = serv->s_port;
618                 } else {
619                         port = htons (strtoul (msg->service, NULL, 10));
620                 }
621         }
622
623         for (i = 0; h.h_addr_list[i] && !msg->cancelled; i++) {
624                 res = g_malloc0 (sizeof (*res));
625                 if (msg->hints) {
626                         res->ai_flags = msg->hints->ai_flags;
627                         if (msg->hints->ai_flags & AI_CANONNAME)
628                                 res->ai_canonname = g_strdup (h.h_name);
629                         res->ai_socktype = msg->hints->ai_socktype;
630                         res->ai_protocol = msg->hints->ai_protocol;
631                 } else {
632                         res->ai_flags = 0;
633                         res->ai_socktype = SOCK_STREAM; /* fudge */
634                         res->ai_protocol = 0;   /* fudge */
635                 }
636                 res->ai_family = AF_INET;
637                 res->ai_addrlen = sizeof (*sin);
638                 res->ai_addr = g_malloc (sizeof (*sin));
639                 sin = (struct sockaddr_in *) res->ai_addr;
640                 sin->sin_family = AF_INET;
641                 sin->sin_port = port;
642                 memcpy (&sin->sin_addr, h.h_addr_list[i], sizeof (sin->sin_addr));
643
644                 if (last == NULL) {
645                         *msg->res = last = res;
646                 } else {
647                         last->ai_next = res;
648                         last = res;
649                 }
650         }
651 reply:
652         camel_msgport_reply ((CamelMsg *) msg);
653 cancel:
654         return NULL;
655 }
656 #else
657 static gpointer
658 cs_getaddrinfo (gpointer data)
659 {
660         struct _addrinfo_msg *info = data;
661
662         info->result = getaddrinfo (info->name, info->service, info->hints, info->res);
663
664         /* On Solaris, the service name 'http' or 'https' is not defined.
665          * Use the port as the service name directly. */
666         if (info->result && info->service) {
667                 if (strcmp (info->service, "http") == 0)
668                         info->result = getaddrinfo (info->name, "80", info->hints, info->res);
669                 else if (strcmp (info->service, "https") == 0)
670                         info->result = getaddrinfo (info->name, "443", info->hints, info->res);
671         }
672
673         if (!info->cancelled)
674                 camel_msgport_reply ((CamelMsg *) info);
675
676         return NULL;
677 }
678 #endif /* NEED_ADDRINFO */
679
680 /**
681  * camel_getaddrinfo:
682  *
683  * Since: 2.22
684  **/
685 struct addrinfo *
686 camel_getaddrinfo (const gchar *name,
687                    const gchar *service,
688                    const struct addrinfo *hints,
689                    GCancellable *cancellable,
690                    GError **error)
691 {
692         struct _addrinfo_msg *msg;
693         struct addrinfo *res = NULL;
694 #ifndef ENABLE_IPv6
695         struct addrinfo myhints;
696 #endif
697         g_return_val_if_fail (name != NULL, NULL);
698
699         if (g_cancellable_set_error_if_cancelled (cancellable, error))
700                 return NULL;
701
702         camel_operation_push_message (
703                 cancellable, _("Resolving: %s"), name);
704
705         /* force ipv4 addresses only */
706 #ifndef ENABLE_IPv6
707         if (hints == NULL)
708                 memset (&myhints, 0, sizeof (myhints));
709         else
710                 memcpy (&myhints, hints, sizeof (myhints));
711
712         myhints.ai_family = AF_INET;
713         hints = &myhints;
714 #endif
715
716         msg = g_malloc0 (sizeof (*msg));
717         msg->name = name;
718         msg->service = service;
719         msg->hints = hints;
720         msg->res = &res;
721 #ifdef NEED_ADDRINFO
722         msg->hostbuflen = 1024;
723         msg->hostbufmem = g_malloc (msg->hostbuflen);
724 #endif
725         if (cs_waitinfo (
726                 cs_getaddrinfo, msg, _("Host lookup failed"),
727                 cancellable, error) == 0) {
728
729                 if (msg->result != 0) {
730                         g_set_error (
731                                 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
732                                 _("Host lookup failed: %s: %s"),
733                                 name, gai_strerror (msg->result));
734                 }
735         } else
736                 res = NULL;
737
738         cs_freeinfo (msg);
739
740         camel_operation_pop_message (cancellable);
741
742         return res;
743 }
744
745 /**
746  * camel_freeaddrinfo:
747  *
748  * Since: 2.22
749  **/
750 void
751 camel_freeaddrinfo (struct addrinfo *host)
752 {
753 #ifdef NEED_ADDRINFO
754         while (host) {
755                 struct addrinfo *next = host->ai_next;
756
757                 g_free (host->ai_canonname);
758                 g_free (host->ai_addr);
759                 g_free (host);
760                 host = next;
761         }
762 #else
763         freeaddrinfo (host);
764 #endif
765 }
766
767 #ifdef NEED_ADDRINFO
768 static gpointer
769 cs_getnameinfo (gpointer data)
770 {
771         struct _addrinfo_msg *msg = data;
772         gint herr;
773         struct hostent h;
774         struct sockaddr_in *sin = (struct sockaddr_in *) msg->addr;
775
776         /* FIXME: error code */
777         if (msg->addr->sa_family != AF_INET) {
778                 msg->result = -1;
779                 return NULL;
780         }
781
782         /* FIXME: honour getnameinfo flags: do we care, not really */
783
784         while ((msg->result = camel_gethostbyaddr_r ((const gchar *) &sin->sin_addr, sizeof (sin->sin_addr), AF_INET, &h,
785                                                     msg->hostbufmem, msg->hostbuflen, &herr)) == ERANGE) {
786                 if (msg->cancelled)
787                         break;
788                 msg->hostbuflen *= 2;
789                 msg->hostbufmem = g_realloc (msg->hostbufmem, msg->hostbuflen);
790         }
791
792         if (msg->cancelled)
793                 goto cancel;
794
795         if (msg->host) {
796                 g_free (msg->host);
797                 if (msg->result == 0 && h.h_name && h.h_name[0]) {
798                         msg->host = g_strdup (h.h_name);
799                 } else {
800                         guchar *in = (guchar *) &sin->sin_addr;
801
802                         /* sin_addr is always network order which is big-endian */
803                         msg->host = g_strdup_printf ("%u.%u.%u.%u", in[0], in[1], in[2], in[3]);
804                 }
805         }
806
807         /* we never actually use this anyway */
808         if (msg->serv)
809                 sprintf (msg->serv, "%d", sin->sin_port);
810
811         if (!msg->cancelled)
812                 camel_msgport_reply ((CamelMsg *) msg);
813 cancel:
814         return NULL;
815 }
816 #else
817 static gpointer
818 cs_getnameinfo (gpointer data)
819 {
820         struct _addrinfo_msg *msg = data;
821
822         /* there doens't appear to be a return code which says host or serv buffers are too short, lengthen them */
823         msg->result = getnameinfo (msg->addr, msg->addrlen, msg->host, msg->hostlen, msg->serv, msg->servlen, msg->flags);
824
825         if (!msg->cancelled)
826                 camel_msgport_reply ((CamelMsg *) msg);
827
828         return NULL;
829 }
830 #endif
831
832 /**
833  * camel_getnameinfo:
834  *
835  * Since: 2.22
836  **/
837 gint
838 camel_getnameinfo (const struct sockaddr *sa,
839                    socklen_t salen,
840                    gchar **host,
841                    gchar **serv,
842                    gint flags,
843                    GCancellable *cancellable,
844                    GError **error)
845 {
846         struct _addrinfo_msg *msg;
847         gint result;
848
849         if (g_cancellable_set_error_if_cancelled (cancellable, error))
850                 return -1;
851
852         camel_operation_push_message (
853                 cancellable, _("Resolving address"));
854
855         msg = g_malloc0 (sizeof (*msg));
856         msg->addr = sa;
857         msg->addrlen = salen;
858         if (host) {
859                 msg->hostlen = NI_MAXHOST;
860                 msg->host = g_malloc (msg->hostlen);
861                 msg->host[0] = 0;
862         }
863         if (serv) {
864                 msg->servlen = NI_MAXSERV;
865                 msg->serv = g_malloc (msg->servlen);
866                 msg->serv[0] = 0;
867         }
868         msg->flags = flags;
869 #ifdef NEED_ADDRINFO
870         msg->hostbuflen = 1024;
871         msg->hostbufmem = g_malloc (msg->hostbuflen);
872 #endif
873         cs_waitinfo (
874                 cs_getnameinfo, msg, _("Name lookup failed"),
875                 cancellable, error);
876
877         if ((result = msg->result) != 0)
878                 g_set_error (
879                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
880                         _("Name lookup failed: %s"), gai_strerror (result));
881         else {
882                 if (host)
883                         *host = g_strdup(msg->host);
884                 if (serv)
885                         *serv = g_strdup(msg->serv);
886         }
887
888         cs_freeinfo (msg);
889
890         camel_operation_pop_message (cancellable);
891
892         return result;
893 }