With --enable-gpg the keyservers are now build and a first test using gpg2
[gnupg.git] / common / http.c
1 /* http.c  -  HTTP protocol handler
2  * Copyright (C) 1999, 2001, 2002, 2003, 2004,
3  *               2006 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20  * USA.
21  */
22
23 /* Simple HTTP client implementation.  We try to keep the code as
24    self-contained as possible.  There are some contraints however:
25
26   - stpcpy is required
27   - fixme: list other requirements.
28
29
30   - With HTTP_USE_ESTREAM defined, all I/O is done through estream.
31   - With HTTP_USE_GNUTLS support for https is provided (this also
32     requires estream).
33 */
34
35 #ifdef HAVE_CONFIG_H
36 # include <config.h>
37 #endif
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stdarg.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <errno.h>
44
45 #ifdef HAVE_W32_SYSTEM
46 # include <windows.h>
47 #else /*!HAVE_W32_SYSTEM*/
48 # include <unistd.h>
49 # include <sys/types.h>
50 # include <sys/socket.h>
51 # include <sys/time.h>
52 # include <time.h>
53 # include <netinet/in.h>
54 # include <arpa/inet.h>
55 # include <netdb.h>
56 #endif /*!HAVE_W32_SYSTEM*/
57
58 #ifdef HTTP_USE_GNUTLS
59 # include <gnutls/gnutls.h>
60 /* For non-understandable reasons GNUTLS dropped the _t suffix from
61    all types. yes, ISO-C might be read as this but there are still
62    other name space conflicts and using _t is actually a Good
63    Thing. */
64 typedef gnutls_session gnutls_session_t;
65 typedef gnutls_transport_ptr gnutls_transport_ptr_t;
66 #endif /*HTTP_USE_GNUTLS*/
67
68 #include "util.h"
69 #include "http.h"
70 #ifdef USE_DNS_SRV
71 #include "srv.h"
72 #else /*!USE_DNS_SRV*/
73 /* If we are not compiling with SRV record support we provide stub
74    data structures. */
75 #ifndef MAXDNAME
76 #define MAXDNAME 1025
77 #endif
78 struct srventry
79 {
80   unsigned short priority;
81   unsigned short weight;
82   unsigned short port;
83   int run_count;
84   char target[MAXDNAME];
85 };
86 #endif/*!USE_DNS_SRV*/
87
88
89 #ifdef HAVE_W32_SYSTEM
90 #define sock_close(a)  closesocket(a)
91 #else
92 #define sock_close(a)  close(a)
93 #endif
94
95 #ifndef EAGAIN
96 #define EAGAIN  EWOULDBLOCK
97 #endif
98
99 #define HTTP_PROXY_ENV           "http_proxy"
100 #define MAX_LINELEN 20000  /* Max. length of a HTTP header line. */
101 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz"   \
102                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"   \
103                         "01234567890@"                 \
104                         "!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
105
106 /* Define a prefix to map stream functions to the estream library. */
107 #ifdef HTTP_USE_ESTREAM
108 #define P_ES(a)  es_ ## a
109 #else
110 #define P_ES(a)  a
111 #endif
112 #ifndef HTTP_USE_GNUTLS
113 typedef void * gnutls_session_t;
114 #endif
115 #if defined(HTTP_USE_GNUTLS) && !defined(HTTP_USE_ESTREAM)
116 #error Use of GNUTLS also requires support for Estream
117 #endif
118
119 static gpg_error_t do_parse_uri (parsed_uri_t uri, int only_local_part);
120 static int remove_escapes (char *string);
121 static int insert_escapes (char *buffer, const char *string,
122                            const char *special);
123 static uri_tuple_t parse_tuple (char *string);
124 static gpg_error_t send_request (http_t hd,
125                                  const char *auth, const char *proxy);
126 static char *build_rel_path (parsed_uri_t uri);
127 static gpg_error_t parse_response (http_t hd);
128
129 static int connect_server (const char *server, unsigned short port,
130                            unsigned int flags, const char *srvtag);
131 static gpg_error_t write_server (int sock, const char *data, size_t length);
132
133 #ifdef HTTP_USE_ESTREAM
134 static ssize_t cookie_read (void *cookie, void *buffer, size_t size);
135 static ssize_t cookie_write (void *cookie, const void *buffer, size_t size);
136 static int cookie_close (void *cookie);
137
138 static es_cookie_io_functions_t cookie_functions =
139   {
140     cookie_read,
141     cookie_write,
142     NULL,
143     cookie_close
144   };
145
146 struct cookie_s 
147 {
148   int fd;  /* File descriptor or -1 if already closed. */
149   gnutls_session_t tls_session;  /* TLS session context or NULL if not used. */
150   int keep_socket; /* Flag to communicate with teh close handler. */
151 };
152 typedef struct cookie_s *cookie_t;
153
154 #endif /*HTTP_USE_ESTREAM*/
155
156 #ifdef HTTP_USE_GNUTLS
157 static gpg_error_t (*tls_callback) (http_t, gnutls_session_t, int);
158 #endif /*HTTP_USE_GNUTLS*/
159
160 /* Our handle context. */
161 struct http_context_s 
162 {
163   unsigned int status_code;
164   int sock;
165   int in_data;
166 #ifdef HTTP_USE_ESTREAM
167   estream_t fp_read;
168   estream_t fp_write;
169   void *write_cookie;
170 #else /*!HTTP_USE_ESTREAM*/
171   FILE *fp_read;
172   FILE *fp_write;
173 #endif /*!HTTP_USE_ESTREAM*/
174   void *tls_context;
175   int is_http_0_9;
176   parsed_uri_t uri;
177   http_req_t req_type;
178   char *buffer;          /* Line buffer. */
179   size_t buffer_size;
180   unsigned int flags;
181 };
182
183
184
185 \f
186 #ifdef HAVE_W32_SYSTEM
187 static void
188 deinit_sockets (void)
189 {
190   WSACleanup();
191 }
192
193 static void
194 init_sockets (void)
195 {
196   static int initialized;
197   static WSADATA wsdata;
198
199   if (initialized)
200     return;
201
202   if ( WSAStartup( 0x0101, &wsdata ) ) 
203     {
204       log_error ("error initializing socket library: ec=%d\n", 
205                  (int)WSAGetLastError () );
206       return;
207     }
208   if ( wsdata.wVersion < 0x0001 ) 
209     {
210       log_error ("socket library version is %x.%x - but 1.1 needed\n",
211                  LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion));
212       WSACleanup();
213       return;
214     }
215   atexit ( deinit_sockets );
216   initialized = 1;
217 }
218 #endif /*HAVE_W32_SYSTEM*/
219
220
221
222 /*
223  * Helper function to create an HTTP header with hex encoded data.  A
224  * new buffer is returned.  This buffer is the concatenation of the
225  * string PREFIX, the hex-encoded DATA of length LEN and the string
226  * SUFFIX.  On error NULL is returned and ERRNO set.
227  */
228 static char *
229 make_header_line (const char *prefix, const char *suffix,
230                    const void *data, size_t len )
231 {
232   static unsigned char bintoasc[] = 
233     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
234     "abcdefghijklmnopqrstuvwxyz"
235     "0123456789+/";
236   const unsigned int *s = data;
237   char *buffer, *p;
238
239   buffer = xtrymalloc (strlen (prefix) + (len+2)/3*4 + strlen (suffix) + 1);
240   if (!buffer)
241     return NULL;
242   p = stpcpy (buffer, prefix);
243   for ( ; len >= 3 ; len -= 3, s += 3 )
244     {
245       *p++ = bintoasc[(s[0] >> 2) & 077];
246       *p++ = bintoasc[(((s[0] <<4)&060)|((s[1] >> 4)&017))&077];
247       *p++ = bintoasc[(((s[1]<<2)&074)|((s[2]>>6)&03))&077];
248       *p++ = bintoasc[s[2]&077];
249     }
250   if ( len == 2 ) 
251     {
252       *p++ = bintoasc[(s[0] >> 2) & 077];
253       *p++ = bintoasc[(((s[0] <<4)&060)|((s[1] >> 4)&017))&077];
254       *p++ = bintoasc[((s[1]<<2)&074)];
255       *p++ = '=';
256     }
257   else if ( len == 1 )
258     {
259       *p++ = bintoasc[(s[0] >> 2) & 077];
260       *p++ = bintoasc[(s[0] <<4)&060];
261       *p++ = '=';
262       *p++ = '=';
263     }
264   strcpy (p, suffix);
265   return buffer;
266 }
267
268
269
270 \f
271 void
272 http_register_tls_callback ( gpg_error_t (*cb) (http_t, void *, int) )
273 {
274 #ifdef HTTP_USE_GNUTLS
275   tls_callback = (gpg_error_t (*) (http_t, gnutls_session_t, int))cb;
276 #endif  
277 }
278
279
280
281 /* Start a HTTP retrieval and return on success in R_HD a context
282    pointer for completing the the request and to wait for the
283    response. */
284 gpg_error_t
285 http_open (http_t *r_hd, http_req_t reqtype, const char *url, 
286            const char *auth, unsigned int flags, const char *proxy,
287            void *tls_context)
288 {
289   gpg_error_t err;
290   http_t hd;
291   
292   *r_hd = NULL;
293
294   if (!(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST))
295     return gpg_error (GPG_ERR_INV_ARG);
296
297   /* Create the handle. */
298   hd = xtrycalloc (1, sizeof *hd);
299   if (!hd)
300     return gpg_error_from_errno (errno);
301   hd->sock = -1;
302   hd->req_type = reqtype;
303   hd->flags = flags;
304   hd->tls_context = tls_context;
305
306   err = http_parse_uri (&hd->uri, url);
307   if (!err)
308     err = send_request (hd, auth, proxy);
309   
310   if (err)
311     {
312       if (!hd->fp_read && !hd->fp_write && hd->sock != -1)
313         sock_close (hd->sock);
314       if (hd->fp_read)
315         P_ES(fclose) (hd->fp_read);
316       if (hd->fp_write)
317         P_ES(fclose) (hd->fp_write);
318       http_release_parsed_uri (hd->uri);
319       xfree (hd);
320     }
321   else
322     *r_hd = hd;
323   return err;
324 }
325
326
327 void
328 http_start_data (http_t hd)
329 {
330   if (!hd->in_data)
331     {
332 #ifdef HTTP_USE_ESTREAM
333       es_fputs ("\r\n", hd->fp_write);
334       es_fflush (hd->fp_write);
335 #else
336       fflush (hd->fp_write);
337       write_server (hd->sock, "\r\n", 2);
338 #endif
339       hd->in_data = 1;
340     }
341   else
342     P_ES(fflush) (hd->fp_write);
343 }
344
345
346 gpg_error_t
347 http_wait_response (http_t hd)
348 {
349   gpg_error_t err;
350
351   /* Make sure that we are in the data. */
352   http_start_data (hd); 
353
354   /* We dup the socket, to cope with the fact that fclose closes the
355      underlying socket. In TLS mode we don't do that because we can't
356      close the socket gnutls is working on; instead we make sure that
357      the fclose won't close the socket in this case. */
358 #ifdef HTTP_USE_ESTREAM
359   if (hd->write_cookie)
360     {
361       /* The write cookie is only set in the TLS case. */
362       cookie_t cookie = hd->write_cookie;
363       cookie->keep_socket = 1;
364     }
365   else
366 #endif /*HTTP_USE_ESTREAM*/
367     {
368       hd->sock = dup (hd->sock);
369       if (hd->sock == -1)
370         return gpg_error_from_errno (errno);
371     }
372   P_ES(fclose) (hd->fp_write);
373   hd->fp_write = NULL;
374 #ifdef HTTP_USE_ESTREAM
375   hd->write_cookie = NULL;
376 #endif
377
378   if (!(hd->flags & HTTP_FLAG_NO_SHUTDOWN))
379     shutdown (hd->sock, 1);
380   hd->in_data = 0;
381
382 #ifdef HTTP_USE_ESTREAM
383   {
384     cookie_t cookie;
385
386     cookie = xtrycalloc (1, sizeof *cookie);
387     if (!cookie)
388       return gpg_error_from_errno (errno);
389     cookie->fd = hd->sock;
390     if (hd->uri->use_tls)
391       cookie->tls_session = hd->tls_context;
392
393     hd->fp_read = es_fopencookie (cookie, "r", cookie_functions);
394     if (!hd->fp_read)
395       {
396         xfree (cookie);
397         return gpg_error_from_errno (errno);
398       }
399   }
400 #else /*!HTTP_USE_ESTREAM*/
401   hd->fp_read = fdopen (hd->sock, "r");
402   if (!hd->fp_read)
403     return gpg_error_from_errno (errno);
404 #endif /*!HTTP_USE_ESTREAM*/
405
406   err = parse_response (hd);
407   return err;
408 }
409
410
411 /* Convenience function to send a request and wait for the response.
412    Closes the handle on error.  If PROXY is not NULL, this value will
413    be used as an HTTP proxy and any enabled $http_proxy gets
414    ignored. */
415 gpg_error_t
416 http_open_document (http_t *r_hd, const char *document, 
417                     const char *auth, unsigned int flags, const char *proxy,
418                     void *tls_context)
419 {
420   gpg_error_t err;
421
422   err = http_open (r_hd, HTTP_REQ_GET, document, auth, flags,
423                    proxy, tls_context);
424   if (err)
425     return err;
426
427   err = http_wait_response (*r_hd);
428   if (err)
429     http_close (*r_hd, 0);
430
431   return err;
432 }
433
434
435 void
436 http_close (http_t hd, int keep_read_stream)
437 {
438   if (!hd)
439     return;
440   if (!hd->fp_read && !hd->fp_write && hd->sock != -1)
441     sock_close (hd->sock);
442   if (hd->fp_read && !keep_read_stream)
443     P_ES(fclose) (hd->fp_read);
444   if (hd->fp_write)
445     P_ES(fclose) (hd->fp_write);
446   http_release_parsed_uri (hd->uri);
447   xfree (hd->buffer);
448   xfree (hd);
449 }
450
451
452 #ifdef HTTP_USE_ESTREAM
453 estream_t
454 http_get_read_ptr (http_t hd)
455 {
456   return hd?hd->fp_read:NULL;
457 }
458 estream_t
459 http_get_write_ptr (http_t hd)
460 {
461   return hd?hd->fp_write:NULL;
462 }
463 #else /*!HTTP_USE_ESTREAM*/
464 FILE *
465 http_get_read_ptr (http_t hd)
466 {
467   return hd?hd->fp_read:NULL;
468 }
469 FILE *
470 http_get_write_ptr (http_t hd)
471 {
472   return hd?hd->fp_write:NULL;
473 }
474 #endif /*!HTTP_USE_ESTREAM*/
475 unsigned int
476 http_get_status_code (http_t hd)
477 {
478   return hd?hd->status_code:0;
479 }
480
481
482 \f
483 /*
484  * Parse an URI and put the result into the newly allocated RET_URI.
485  * The caller must always use release_parsed_uri() to releases the
486  * resources (even on error).
487  */
488 gpg_error_t
489 http_parse_uri (parsed_uri_t * ret_uri, const char *uri)
490 {
491   *ret_uri = xcalloc (1, sizeof **ret_uri + strlen (uri));
492   strcpy ((*ret_uri)->buffer, uri);
493   return do_parse_uri (*ret_uri, 0);
494 }
495
496 void
497 http_release_parsed_uri (parsed_uri_t uri)
498 {
499   if (uri)
500     {
501       uri_tuple_t r, r2;
502
503       for (r = uri->query; r; r = r2)
504         {
505           r2 = r->next;
506           xfree (r);
507         }
508       xfree (uri);
509     }
510 }
511
512
513 static gpg_error_t
514 do_parse_uri (parsed_uri_t uri, int only_local_part)
515 {
516   uri_tuple_t *tail;
517   char *p, *p2, *p3, *pp;
518   int n;
519
520   p = uri->buffer;
521   n = strlen (uri->buffer);
522
523   /* Initialize all fields to an empty string or an empty list. */
524   uri->scheme = uri->host = uri->path = p + n;
525   uri->port = 0;
526   uri->params = uri->query = NULL;
527   uri->use_tls = 0;
528
529   /* A quick validity check. */
530   if (strspn (p, VALID_URI_CHARS) != n)
531     return gpg_error (GPG_ERR_BAD_URI); /* Invalid characters found. */
532
533   if (!only_local_part)
534     {
535       /* Find the scheme. */
536       if (!(p2 = strchr (p, ':')) || p2 == p)
537         return gpg_error (GPG_ERR_BAD_URI); /* No scheme. */
538       *p2++ = 0;
539       for (pp=p; *pp; pp++)
540        *pp = tolower (*(unsigned char*)pp);
541       uri->scheme = p;
542       if (!strcmp (uri->scheme, "http"))
543         uri->port = 80;
544 #ifdef HTTP_USE_GNUTLS
545       else if (!strcmp (uri->scheme, "https"))
546         {
547           uri->port = 443;
548           uri->use_tls = 1;
549         }
550 #endif
551       else if (!strcmp (uri->scheme, "hkp"))
552         uri->port = 11371;
553       else
554         return gpg_error (GPG_ERR_INV_URI); /* Unsupported scheme */
555
556       p = p2;
557
558       /* Find the hostname */
559       if (*p != '/')
560         return gpg_error (GPG_ERR_INV_URI); /* Does not start with a slash. */
561
562       p++;
563       if (*p == '/') /* There seems to be a hostname. */
564         { 
565           p++;
566           if ((p2 = strchr (p, '/')))
567             *p2++ = 0;
568
569           /* Check for username/password encoding */
570           if ((p3 = strchr (p, '@')))
571             {
572               uri->auth = p;
573               *p3++ = '\0';
574               p = p3;
575             }
576
577           for (pp=p; *pp; pp++)
578             *pp = tolower (*(unsigned char*)pp);
579           uri->host = p;
580           if ((p3 = strchr (p, ':')))
581             {
582               *p3++ = 0;
583               uri->port = atoi (p3);
584             }
585
586           uri->host = p;
587           if ((n = remove_escapes (uri->host)) < 0)
588             return gpg_error (GPG_ERR_BAD_URI);
589           if (n != strlen (p))
590             return gpg_error (GPG_ERR_BAD_URI); /* Hostname incudes a Nul. */
591           p = p2 ? p2 : NULL;
592         }
593     } /* End global URI part. */
594
595   /* Parse the pathname part */
596   if (!p || !*p)
597     return 0;  /* We don't have a path.  Okay. */
598
599   /* TODO: Here we have to check params. */
600
601   /* Do we have a query part? */
602   if ((p2 = strchr (p, '?')))
603     *p2++ = 0;
604
605   uri->path = p;
606   if ((n = remove_escapes (p)) < 0)
607     return gpg_error (GPG_ERR_BAD_URI);
608   if (n != strlen (p))
609     return gpg_error (GPG_ERR_BAD_URI); /* Path includes a Nul. */
610   p = p2 ? p2 : NULL;
611
612   if (!p || !*p)        
613     return 0; /* We don't have a query string.  Okay. */
614
615   /* Now parse the query string. */
616   tail = &uri->query;
617   for (;;)
618     {
619       uri_tuple_t elem;
620
621       if ((p2 = strchr (p, '&')))
622         *p2++ = 0;
623       if (!(elem = parse_tuple (p)))
624         return gpg_error (GPG_ERR_BAD_URI);
625       *tail = elem;
626       tail = &elem->next;
627
628       if (!p2)
629         break; /* Ready. */
630       p = p2;
631     }
632
633   return 0;
634 }
635
636
637 /*
638  * Remove all %xx escapes; this is done in-place.  Returns: New length
639  * of the string.
640  */
641 static int
642 remove_escapes (char *string)
643 {
644   int n = 0;
645   unsigned char *p, *s;
646
647   for (p = s = (unsigned char*)string; *s; s++)
648     {
649       if (*s == '%')
650         {
651           if (s[1] && s[2] && isxdigit (s[1]) && isxdigit (s[2]))
652             {
653               s++;
654               *p = *s >= '0' && *s <= '9' ? *s - '0' :
655                 *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10;
656               *p <<= 4;
657               s++;
658               *p |= *s >= '0' && *s <= '9' ? *s - '0' :
659                 *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10;
660               p++;
661               n++;
662             }
663           else
664             {
665               *p++ = *s++;
666               if (*s)
667                 *p++ = *s++;
668               if (*s)
669                 *p++ = *s++;
670               if (*s)
671                 *p = 0;
672               return -1; /* Bad URI. */
673             }
674         }
675       else
676         {
677           *p++ = *s;
678           n++;
679         }
680     }
681   *p = 0; /* Make sure to keep a string terminator. */
682   return n;
683 }
684
685
686 static int
687 insert_escapes (char *buffer, const char *string,
688                 const char *special)
689 {
690   const unsigned char *s = (const unsigned char*)string;
691   int n = 0;
692
693   for (; *s; s++)
694     {
695       if (strchr (VALID_URI_CHARS, *s) && !strchr (special, *s))
696         {
697           if (buffer)
698             *(unsigned char*)buffer++ = *s;
699           n++;
700         }
701       else
702         {
703           if (buffer)
704             {
705               sprintf (buffer, "%%%02X", *s);
706               buffer += 3;
707             }
708           n += 3;
709         }
710     }
711   return n;
712 }
713
714
715 /* Allocate a new string from STRING using standard HTTP escaping as
716    well as escaping of characters given in SPECIALS.  A common pattern
717    for SPECIALS is "%;?&=". However it depends on the needs, for
718    example "+" and "/: often needs to be escaped too.  Returns NULL on
719    failure and sets ERRNO. */
720 char *
721 http_escape_string (const char *string, const char *specials)
722 {
723   int n;
724   char *buf;
725
726   n = insert_escapes (NULL, string, specials);
727   buf = xtrymalloc (n+1);
728   if (buf)
729     {
730       insert_escapes (buf, string, specials);
731       buf[n] = 0;
732     }
733   return buf;
734 }
735
736
737
738 static uri_tuple_t
739 parse_tuple (char *string)
740 {
741   char *p = string;
742   char *p2;
743   int n;
744   uri_tuple_t tuple;
745
746   if ((p2 = strchr (p, '=')))
747     *p2++ = 0;
748   if ((n = remove_escapes (p)) < 0)
749     return NULL; /* Bad URI. */
750   if (n != strlen (p))
751     return NULL; /* Name with a Nul in it. */
752   tuple = xtrycalloc (1, sizeof *tuple);
753   if (!tuple)
754     return NULL; /* Out of core. */
755   tuple->name = p;
756   if (!p2) /* We have only the name, so we assume an empty value string. */
757     {
758       tuple->value = p + strlen (p);
759       tuple->valuelen = 0;
760       tuple->no_value = 1; /* Explicitly mark that we have seen no '='. */
761     }
762   else /* Name and value. */
763     {
764       if ((n = remove_escapes (p2)) < 0)
765         {
766           xfree (tuple);
767           return NULL; /* Bad URI. */
768         }
769       tuple->value = p2;
770       tuple->valuelen = n;
771     }
772   return tuple;
773 }
774
775
776 /*
777  * Send a HTTP request to the server
778  * Returns 0 if the request was successful
779  */
780 static gpg_error_t
781 send_request (http_t hd, const char *auth, const char *proxy)
782 {
783   gnutls_session_t tls_session;
784   gpg_error_t err;
785   const char *server;
786   char *request, *p;
787   unsigned short port;
788   const char *http_proxy = NULL;
789   char *proxy_authstr = NULL;
790   char *authstr = NULL;
791   int save_errno;
792
793   tls_session = hd->tls_context;
794   if (hd->uri->use_tls && !tls_session)
795     {
796       log_error ("TLS requested but no GNUTLS context provided\n");
797       return gpg_error (GPG_ERR_INTERNAL);
798     }
799
800   server = *hd->uri->host ? hd->uri->host : "localhost";
801   port = hd->uri->port ? hd->uri->port : 80;
802
803   if ( (proxy && *proxy)
804        || ( (hd->flags & HTTP_FLAG_TRY_PROXY)
805             && (http_proxy = getenv (HTTP_PROXY_ENV)) 
806             && *http_proxy ))
807     {
808       parsed_uri_t uri;
809
810       if (proxy)
811         http_proxy = proxy;
812
813       err = http_parse_uri (&uri, http_proxy);
814       if (err)
815         {
816           log_error ("invalid HTTP proxy (%s): %s\n",
817                      http_proxy, gpg_strerror (err));
818           http_release_parsed_uri (uri);
819           return gpg_error (GPG_ERR_CONFIGURATION);
820
821         }
822
823       if (uri->auth)
824         {
825           remove_escapes (uri->auth);
826           proxy_authstr = make_header_line ("Proxy-Authorization: Basic ",
827                                             "\r\n",
828                                             uri->auth, strlen(uri->auth));
829           if (!proxy_authstr)
830             {
831               err = gpg_error_from_errno (errno);
832               http_release_parsed_uri (uri);
833               return err;
834             }
835         }
836
837       hd->sock = connect_server (*uri->host ? uri->host : "localhost",
838                                  uri->port ? uri->port : 80,
839                                  hd->flags, hd->uri->scheme);
840       save_errno = errno;
841       http_release_parsed_uri (uri);
842     }
843   else
844     {
845       hd->sock = connect_server (server, port, hd->flags, hd->uri->scheme);
846       save_errno = errno;
847     }
848
849   if (hd->sock == -1)
850     {
851       xfree (proxy_authstr);
852       return gpg_error_from_errno (save_errno);
853     }
854
855 #ifdef HTTP_USE_GNUTLS
856   if (hd->uri->use_tls)
857     {
858       int rc;
859
860       gnutls_transport_set_ptr (tls_session, (gnutls_transport_ptr_t)hd->sock);
861       do
862         {
863           rc = gnutls_handshake (tls_session);
864         }
865       while (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN);
866       if (rc < 0)
867         {
868           log_info ("TLS handshake failed: %s\n", gnutls_strerror (rc));
869           xfree (proxy_authstr);
870           return gpg_error (GPG_ERR_NETWORK);
871         }
872
873       if (tls_callback)
874         {
875           err = tls_callback (hd, tls_session, 0);
876           if (err)
877             {
878               log_info ("TLS connection authentication failed: %s\n",
879                         gpg_strerror (err));
880               xfree (proxy_authstr);
881               return err;
882             }
883         }
884     }
885 #endif /*HTTP_USE_GNUTLS*/
886
887   if (auth || hd->uri->auth)
888     {
889       char *myauth;
890       
891       if (auth)
892         {
893           myauth = xtrystrdup (auth);
894           if (!myauth)
895             {
896               xfree (proxy_authstr);
897               return gpg_error_from_errno (errno);
898             }
899           remove_escapes (myauth);
900         }
901       else
902         {
903           remove_escapes (hd->uri->auth);
904           myauth = hd->uri->auth;
905         }
906
907       authstr = make_header_line ("Authorization: Basic %s", "\r\n",
908                                   myauth, strlen (myauth));
909       if (auth)
910         xfree (myauth);
911
912       if (!authstr)
913         {
914           xfree (proxy_authstr);
915           return gpg_error_from_errno (errno);
916         }
917     }
918   
919   p = build_rel_path (hd->uri);
920   if (!p)
921     return gpg_error_from_errno (errno);
922
923   request = xtrymalloc (2 * strlen (server) 
924                         + strlen (p)
925                         + (authstr?strlen(authstr):0)
926                         + (proxy_authstr?strlen(proxy_authstr):0)
927                         + 100);
928   if (!request)
929     {
930       err = gpg_error_from_errno (errno);
931       xfree (p);
932       xfree (authstr);
933       xfree (proxy_authstr);
934       return err;
935     }
936
937   if (http_proxy && *http_proxy)
938     {
939       sprintf (request, "%s http://%s:%hu%s%s HTTP/1.0\r\n%s%s",
940                hd->req_type == HTTP_REQ_GET ? "GET" :
941                hd->req_type == HTTP_REQ_HEAD ? "HEAD" :
942                hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS",
943                server, port, *p == '/' ? "" : "/", p,
944                authstr ? authstr : "",
945                proxy_authstr ? proxy_authstr : "");
946     }
947   else
948     {
949       char portstr[35];
950         
951       if (port == 80)
952         *portstr = 0;
953       else
954         sprintf (portstr, ":%u", port);
955
956       sprintf (request, "%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s",
957                hd->req_type == HTTP_REQ_GET ? "GET" :
958                hd->req_type == HTTP_REQ_HEAD ? "HEAD" :
959                hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS",
960                *p == '/' ? "" : "/", p, server, portstr,
961                authstr? authstr:"");
962     }
963   xfree (p);
964
965
966 #ifdef HTTP_USE_ESTREAM
967   /* First setup estream so that we can write even the first line
968      using estream.  This is also required for the sake of gnutls. */
969   {
970     cookie_t cookie;
971
972     cookie = xtrycalloc (1, sizeof *cookie);
973     if (!cookie)
974       {
975         err = gpg_error_from_errno (errno);
976         goto leave;
977       }
978     cookie->fd = hd->sock;
979     if (hd->uri->use_tls)
980       {
981         cookie->tls_session = tls_session;
982         hd->write_cookie = cookie;
983       }
984
985     hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
986     if (!hd->fp_write)
987       {
988         xfree (cookie);
989         err = gpg_error_from_errno (errno);
990       }
991     else if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write))
992       err = gpg_error_from_errno (errno);
993     else
994       err = 0;
995   }
996
997  leave:
998
999 #else /*!HTTP_USE_ESTREAM*/
1000   /* We send out the start of the request through our own send
1001      function and only then assign a stdio stream.  This allows for
1002      better error reporting that through standard stdio means. */
1003   err = write_server (hd->sock, request, strlen (request));
1004   if (!err)
1005     {
1006       hd->fp_write = fdopen (hd->sock, "w");
1007       if (!hd->fp_write)
1008         err = gpg_error_from_errno (errno);
1009     }
1010 #endif /*!HTTP_USE_ESTREAM*/
1011
1012   xfree (request);
1013   xfree (authstr);
1014   xfree (proxy_authstr);
1015
1016   return err;
1017 }
1018
1019
1020 /*
1021  * Build the relative path from the parsed URI.  Minimal
1022  * implementation.  May return NULL in case of memory failure; errno
1023  * is then set accordingly.
1024  */
1025 static char *
1026 build_rel_path (parsed_uri_t uri)
1027 {
1028   uri_tuple_t r;
1029   char *rel_path, *p;
1030   int n;
1031
1032   /* Count the needed space. */
1033   n = insert_escapes (NULL, uri->path, "%;?&");
1034   /* TODO: build params. */
1035   for (r = uri->query; r; r = r->next)
1036     {
1037       n++; /* '?'/'&' */
1038       n += insert_escapes (NULL, r->name, "%;?&=");
1039       if (!r->no_value)
1040         {
1041           n++; /* '=' */
1042           n += insert_escapes (NULL, r->value, "%;?&=");
1043         }
1044     }
1045   n++;
1046
1047   /* Now allocate and copy. */
1048   p = rel_path = xtrymalloc (n);
1049   if (!p)
1050     return NULL;
1051   n = insert_escapes (p, uri->path, "%;?&");
1052   p += n;
1053   /* TODO: add params. */
1054   for (r = uri->query; r; r = r->next)
1055     {
1056       *p++ = r == uri->query ? '?' : '&';
1057       n = insert_escapes (p, r->name, "%;?&=");
1058       p += n;
1059       if (!r->no_value)
1060         {
1061           *p++ = '=';
1062           /* TODO: Use valuelen. */
1063           n = insert_escapes (p, r->value, "%;?&=");
1064           p += n;
1065         }
1066     }
1067   *p = 0;
1068   return rel_path;
1069 }
1070
1071
1072
1073 /*
1074    Same as fgets() but if the buffer is too short a larger one will be
1075    allocated up to some limit *MAX_LENGTH.  A line is considered a
1076    byte stream ending in a LF.  Returns the length of the line. EOF is
1077    indicated by a line of length zero. The last LF may be missing due
1078    to an EOF.  If MAX_LENGTH is zero on return, the line has been
1079    truncated.  If the returned buffer is NULL, not enough memory was
1080    enable to increase it, the return value will also be 0 and some
1081    bytes might have been lost which should be no problem becuase
1082    out-of-memory is pretty fatal for most applications.
1083
1084    If a line has been truncated, the file pointer is internally moved
1085    forward to the end of the line.
1086
1087    Note: The returned buffer is allocated with enough extra space to
1088    append a CR,LF,Nul
1089  */
1090 static size_t
1091 my_read_line (
1092 #ifdef HTTP_USE_ESTREAM
1093               estream_t fp,
1094 #else
1095               FILE *fp,
1096 #endif
1097               char **addr_of_buffer,
1098               size_t *length_of_buffer, size_t *max_length)
1099 {
1100   int c;
1101   char *buffer = *addr_of_buffer;
1102   size_t length = *length_of_buffer;
1103   size_t nbytes = 0;
1104   size_t maxlen = *max_length;
1105   char *p;
1106
1107   if (!buffer) /* Must allocate a new buffer. */
1108     {           
1109       length = 256;
1110       buffer = xtrymalloc (length);
1111       *addr_of_buffer = buffer;
1112       if (!buffer)
1113         {
1114           *length_of_buffer = *max_length = 0;
1115           return 0;
1116         }
1117       *length_of_buffer = length;
1118     }
1119
1120   length -= 3; /* Reserve 3 bytes (cr,lf,eol). */
1121   p = buffer;
1122   while ((c = P_ES(getc) (fp)) != EOF)
1123     {
1124       if (nbytes == length) /* Increase the buffer. */
1125         {                       
1126           if (length > maxlen) /* Limit reached. */
1127             {
1128               /* Skip the rest of the line. */
1129               while (c != '\n' && (c = P_ES(getc) (fp)) != EOF)
1130                 ;
1131               *p++ = '\n'; /* Always append a LF (we reserved some space). */
1132               nbytes++;
1133               *max_length = 0; /* Indicate truncation */
1134               break; /*(the while loop)*/
1135             }
1136           length += 3; /* Adjust for the reserved bytes. */
1137           length += length < 1024 ? 256 : 1024;
1138           *addr_of_buffer = xtryrealloc (buffer, length);
1139           if (!*addr_of_buffer)
1140             {
1141               int save_errno = errno;
1142               xfree (buffer);
1143               *length_of_buffer = *max_length = 0;
1144               errno = save_errno;
1145               return 0;
1146             }
1147           buffer = *addr_of_buffer;
1148           *length_of_buffer = length;
1149           length -= 3; /* And re-adjust for the reservation. */
1150           p = buffer + nbytes;
1151         }
1152       *p++ = c;
1153       nbytes++;
1154       if (c == '\n')
1155         break;
1156     }
1157   *p = 0; /* Make sure the line is a string. */
1158
1159   return nbytes;
1160 }
1161
1162
1163 /*
1164  * Parse the response from a server.
1165  * Returns: Errorcode and sets some files in the handle
1166  */
1167 static gpg_error_t
1168 parse_response (http_t hd)
1169 {
1170   char *line, *p, *p2;
1171   size_t maxlen, len;
1172
1173   /* Wait for the status line. */
1174   do
1175     {
1176       maxlen = MAX_LINELEN;
1177       len = my_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen);
1178       line = hd->buffer;
1179       if (!line)
1180         return gpg_error_from_errno (errno); /* Out of core. */
1181       if (!maxlen)
1182         return gpg_error (GPG_ERR_TRUNCATED); /* Line has been truncated. */
1183       if (!len)
1184         return gpg_error (GPG_ERR_EOF);
1185       if ( (hd->flags & HTTP_FLAG_LOG_RESP) )
1186         log_info ("RESP: `%.*s'\n",
1187                   (int)strlen(line)-(*line&&line[1]?2:0),line);
1188     }
1189   while (!*line);
1190
1191   if ((p = strchr (line, '/')))
1192     *p++ = 0;
1193   if (!p || strcmp (line, "HTTP"))
1194     return 0; /* Assume http 0.9. */
1195
1196   if ((p2 = strpbrk (p, " \t")))
1197     {
1198       *p2++ = 0;
1199       p2 += strspn (p2, " \t");
1200     }
1201   if (!p2)
1202     return 0; /* Also assume http 0.9. */
1203   p = p2;
1204   /* TODO: Add HTTP version number check. */
1205   if ((p2 = strpbrk (p, " \t")))
1206     *p2++ = 0;
1207   if (!isdigit ((unsigned int)p[0]) || !isdigit ((unsigned int)p[1])
1208       || !isdigit ((unsigned int)p[2]) || p[3])
1209     {
1210       /* Malformed HTTP status code - assume http 0.9. */
1211       hd->is_http_0_9 = 1;
1212       hd->status_code = 200;
1213       return 0;
1214     }
1215   hd->status_code = atoi (p);
1216
1217   /* Skip all the header lines and wait for the empty line. */
1218   do
1219     {
1220       maxlen = MAX_LINELEN;
1221       len = my_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen);
1222       line = hd->buffer;
1223       if (!line)
1224         return gpg_error_from_errno (errno); /* Out of core. */
1225       /* Note, that we can silently ignore truncated lines. */
1226       if (!len)
1227         return gpg_error (GPG_ERR_EOF);
1228       /* Trim line endings of empty lines. */
1229       if ((*line == '\r' && line[1] == '\n') || *line == '\n')
1230         *line = 0;
1231       if ( (hd->flags & HTTP_FLAG_LOG_RESP) )
1232         log_info ("RESP: `%.*s'\n",
1233                   (int)strlen(line)-(*line&&line[1]?2:0),line);
1234     }
1235   while (len && *line);
1236
1237   return 0;
1238 }
1239
1240 #if 0
1241 static int
1242 start_server ()
1243 {
1244   struct sockaddr_in mya;
1245   struct sockaddr_in peer;
1246   int fd, client;
1247   fd_set rfds;
1248   int addrlen;
1249   int i;
1250
1251   if ((fd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
1252     {
1253       log_error ("socket() failed: %s\n", strerror (errno));
1254       return -1;
1255     }
1256   i = 1;
1257   if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (byte *) & i, sizeof (i)))
1258     log_info ("setsockopt(SO_REUSEADDR) failed: %s\n", strerror (errno));
1259
1260   mya.sin_family = AF_INET;
1261   memset (&mya.sin_addr, 0, sizeof (mya.sin_addr));
1262   mya.sin_port = htons (11371);
1263
1264   if (bind (fd, (struct sockaddr *) &mya, sizeof (mya)))
1265     {
1266       log_error ("bind to port 11371 failed: %s\n", strerror (errno));
1267       sock_close (fd);
1268       return -1;
1269     }
1270
1271   if (listen (fd, 5))
1272     {
1273       log_error ("listen failed: %s\n", strerror (errno));
1274       sock_close (fd);
1275       return -1;
1276     }
1277
1278   for (;;)
1279     {
1280       FD_ZERO (&rfds);
1281       FD_SET (fd, &rfds);
1282
1283       if (select (fd + 1, &rfds, NULL, NULL, NULL) <= 0)
1284         continue;               /* ignore any errors */
1285
1286       if (!FD_ISSET (fd, &rfds))
1287         continue;
1288
1289       addrlen = sizeof peer;
1290       client = accept (fd, (struct sockaddr *) &peer, &addrlen);
1291       if (client == -1)
1292         continue;               /* oops */
1293
1294       log_info ("connect from %s\n", inet_ntoa (peer.sin_addr));
1295
1296       fflush (stdout);
1297       fflush (stderr);
1298       if (!fork ())
1299         {
1300           int c;
1301           FILE *fp;
1302
1303           fp = fdopen (client, "r");
1304           while ((c = getc (fp)) != EOF)
1305             putchar (c);
1306           fclose (fp);
1307           exit (0);
1308         }
1309       sock_close (client);
1310     }
1311
1312
1313   return 0;
1314 }
1315 #endif
1316
1317 /* Actually connect to a server.  Returns the file descripto or -1 on
1318    error.  ERRNO is set on error. */
1319 static int
1320 connect_server (const char *server, unsigned short port,
1321                 unsigned int flags, const char *srvtag)
1322 {
1323   int sock = -1;
1324   int srvcount = 0;
1325   int hostfound = 0;
1326   int srv, connected;
1327   int last_errno = 0;
1328   struct srventry *serverlist = NULL;
1329
1330 #ifdef HAVE_W32_SYSTEM
1331   unsigned long inaddr;
1332
1333   init_sockets();
1334   /* Win32 gethostbyname doesn't handle IP addresses internally, so we
1335      try inet_addr first on that platform only. */
1336   inaddr = inet_addr(server);
1337   if ( inaddr != INADDR_NONE )
1338     {
1339       struct sockaddr_in addr;
1340       
1341       memset(&addr,0,sizeof(addr));
1342       
1343       sock = socket(AF_INET,SOCK_STREAM,0);
1344       if ( sock==INVALID_SOCKET )
1345         {
1346           log_error("error creating socket: ec=%d\n",(int)WSAGetLastError());
1347           return -1;
1348         }
1349
1350       addr.sin_family = AF_INET; 
1351       addr.sin_port = htons(port);
1352       memcpy (&addr.sin_addr,&inaddr,sizeof(inaddr));      
1353
1354       if (!connect (sock,(struct sockaddr *)&addr,sizeof(addr)) )
1355         return sock;
1356       sock_close(sock);
1357       return -1;
1358     }
1359 #endif /*HAVE_W32_SYSTEM*/
1360
1361 #ifdef USE_DNS_SRV
1362   /* Do the SRV thing */
1363   if ((flags & HTTP_FLAG_TRY_SRV) && srvtag)
1364     {
1365       /* We're using SRV, so append the tags. */
1366       if (1+strlen (srvtag) + 6 + strlen (server) + 1 <= MAXDNAME)
1367         {
1368           char srvname[MAXDNAME];
1369
1370           stpcpy (stpcpy (stpcpy (stpcpy (srvname,"_"), srvtag),
1371                            "._tcp."), server);
1372           srvcount = getsrv (srvname, &serverlist);
1373         }
1374     }
1375 #endif /*USE_DNS_SRV*/
1376
1377   if (!serverlist)
1378     {
1379       /* Either we're not using SRV, or the SRV lookup failed.  Make
1380          up a fake SRV record. */
1381       serverlist = xtrycalloc (1, sizeof *serverlist);
1382       if (!serverlist)
1383         return -1; /* Out of core.  */
1384       serverlist->port = port;
1385       strncpy (serverlist->target, server, MAXDNAME);
1386       serverlist->target[MAXDNAME-1] = '\0';
1387       srvcount = 1;
1388     }
1389
1390 #ifdef HAVE_GETADDRINFO
1391   connected = 0;
1392   for (srv=0; srv < srvcount && !connected; srv++)
1393     {
1394       struct addrinfo hints, *res, *ai;
1395       char portstr[35];
1396
1397       sprintf (portstr, "%hu", port);
1398       memset (&hints, 0, sizeof (hints));
1399       hints.ai_socktype = SOCK_STREAM;
1400       if (getaddrinfo (serverlist[srv].target, portstr, &hints, &res))
1401         continue; /* Not found - try next one. */
1402       hostfound = 1;
1403
1404       for (ai = res; ai && !connected; ai = ai->ai_next)
1405         {
1406           if (sock != -1)
1407             sock_close (sock);
1408           sock = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1409           if (sock == -1)
1410             {
1411               int save_errno = errno;
1412               log_error ("error creating socket: %s\n", strerror (errno));
1413               freeaddrinfo (res);
1414               xfree (serverlist);
1415               errno = save_errno;
1416               return -1;
1417             }
1418           
1419           if (connect (sock, ai->ai_addr, ai->ai_addrlen))
1420             last_errno = errno;
1421           else
1422             connected = 1;
1423         }
1424       freeaddrinfo (res);
1425     }
1426 #else /* !HAVE_GETADDRINFO */
1427   connected = 0;
1428   for (srv=0; srv < srvcount && !connected; srv++)
1429     {
1430       int i;
1431       struct hostent *host = NULL;
1432       struct sockaddr_in addr;
1433
1434       /* Note: This code is not thread-safe.  */
1435
1436       memset (&addr, 0, sizeof (addr));
1437       host = gethostbyname (serverlist[srv].target);
1438       if (!host)
1439         continue;
1440       hostfound = 1;
1441
1442       if (sock != -1)
1443         sock_close (sock);
1444       sock = socket (host->h_addrtype, SOCK_STREAM, 0);
1445       if (sock == -1)
1446         {
1447           log_error (_("error creating socket: %s\n"), strerror (errno));
1448           xfree (serverlist);
1449           return -1;
1450         }
1451       
1452       addr.sin_family = host->h_addrtype;
1453       if (addr.sin_family != AF_INET)
1454         {
1455           log_error ("unknown address family for `%s'\n",
1456                      serverlist[srv].target);
1457           xfree (serverlist);
1458           return -1;
1459         }
1460       addr.sin_port = htons (serverlist[srv].port);
1461       if (host->h_length != 4)
1462         {
1463           log_error ("illegal address length for `%s'\n",
1464                      serverlist[srv].target);
1465           xfree (serverlist);
1466           return -1;
1467         }
1468
1469       /* Try all A records until one responds. */
1470       for (i = 0; host->h_addr_list[i] && !connected; i++)
1471         {
1472           memcpy (&addr.sin_addr, host->h_addr_list[i], host->h_length);
1473           if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)))
1474             last_errno = errno;
1475           else
1476             {
1477               connected = 1;
1478               break;
1479             }
1480         }
1481     }
1482 #endif /* !HAVE_GETADDRINFO */
1483
1484   xfree (serverlist);
1485
1486   if (!connected)
1487     {
1488 #ifdef HAVE_W32_SYSTEM
1489       log_error ("can't connect to `%s': %s%sec=%d\n",
1490                    server,
1491                    hostfound? "":_("host not found"),
1492                    hostfound? "":" - ", (int)WSAGetLastError());
1493 #else
1494       log_error ("can't connect to `%s': %s\n",
1495                  server,
1496                  hostfound? strerror (last_errno):"host not found");
1497 #endif
1498       if (sock != -1)
1499         sock_close (sock);
1500       errno = last_errno;
1501       return -1;
1502     }
1503   return sock;
1504 }
1505
1506
1507 static gpg_error_t
1508 write_server (int sock, const char *data, size_t length)
1509 {
1510   int nleft;
1511
1512   nleft = length;
1513   while (nleft > 0)
1514     {
1515 #ifdef HAVE_W32_SYSTEM
1516       int nwritten;
1517       
1518       nwritten = send (sock, data, nleft, 0);
1519       if ( nwritten == SOCKET_ERROR ) 
1520         {
1521           log_info ("network write failed: ec=%d\n", (int)WSAGetLastError ());
1522           return G10ERR_NETWORK;
1523         }
1524 #else /*!HAVE_W32_SYSTEM*/
1525       int nwritten = write (sock, data, nleft);
1526       if (nwritten == -1)
1527         {
1528           if (errno == EINTR)
1529             continue;
1530           if (errno == EAGAIN)
1531             {
1532               struct timeval tv;
1533
1534               tv.tv_sec = 0;
1535               tv.tv_usec = 50000;
1536               select (0, NULL, NULL, NULL, &tv);
1537               continue;
1538             }
1539           log_info ("network write failed: %s\n", strerror (errno));
1540           return gpg_error_from_errno (errno);
1541         }
1542 #endif /*!HAVE_W32_SYSTEM*/
1543       nleft -= nwritten;
1544       data += nwritten;
1545     }
1546
1547   return 0;
1548 }
1549
1550
1551 \f
1552 #ifdef HTTP_USE_ESTREAM
1553 /* Read handler for estream.  */
1554 static ssize_t
1555 cookie_read (void *cookie, void *buffer, size_t size)
1556 {
1557   cookie_t c = cookie;
1558   int nread;
1559
1560 #ifdef HTTP_USE_GNUTLS
1561   if (c->tls_session)
1562     {
1563     again:
1564       nread = gnutls_record_recv (c->tls_session, buffer, size);
1565       if (nread < 0)
1566         {
1567           if (nread == GNUTLS_E_INTERRUPTED)
1568             goto again;
1569           if (nread == GNUTLS_E_AGAIN)
1570             {
1571               struct timeval tv;
1572               
1573               tv.tv_sec = 0;
1574               tv.tv_usec = 50000;
1575               select (0, NULL, NULL, NULL, &tv);
1576               goto again;
1577             }
1578           if (nread == GNUTLS_E_REHANDSHAKE)
1579             goto again; /* A client is allowed to just ignore this request. */
1580           log_info ("TLS network read failed: %s\n", gnutls_strerror (nread));
1581           errno = EIO;
1582           return -1;
1583         }
1584     }
1585   else
1586 #endif /*HTTP_USE_GNUTLS*/
1587     {
1588       do
1589         {
1590           nread = read (c->fd, buffer, size);
1591         }
1592       while (nread == -1 && errno == EINTR);
1593     }
1594
1595   return nread;
1596 }
1597
1598 /* Write handler for estream.  */
1599 static ssize_t
1600 cookie_write (void *cookie, const void *buffer, size_t size)
1601 {
1602   cookie_t c = cookie;
1603   int nwritten = 0;
1604
1605 #ifdef HTTP_USE_GNUTLS
1606   if (c->tls_session)
1607     {
1608       int nleft = size;
1609       while (nleft > 0)
1610         {
1611           nwritten = gnutls_record_send (c->tls_session, buffer, nleft); 
1612           if (nwritten <= 0)
1613             {
1614               if (nwritten == GNUTLS_E_INTERRUPTED)
1615                 continue;
1616               if (nwritten == GNUTLS_E_AGAIN)
1617                 {
1618                   struct timeval tv;
1619                   
1620                   tv.tv_sec = 0;
1621                   tv.tv_usec = 50000;
1622                   select (0, NULL, NULL, NULL, &tv);
1623                   continue;
1624                 }
1625               log_info ("TLS network write failed: %s\n",
1626                         gnutls_strerror (nwritten));
1627               errno = EIO;
1628               return -1;
1629             }
1630           nleft -= nwritten;
1631           buffer += nwritten;
1632         }
1633     }
1634   else
1635 #endif /*HTTP_USE_GNUTLS*/
1636     {
1637       if ( write_server (c->fd, buffer, size) )
1638         {
1639           errno = EIO;
1640           nwritten = -1;
1641         }
1642       else
1643         nwritten = size;
1644     }
1645
1646   return nwritten;
1647 }
1648
1649 /* Close handler for estream.  */
1650 static int
1651 cookie_close (void *cookie)
1652 {
1653   cookie_t c = cookie;
1654
1655   if (!c)
1656     return 0;
1657
1658  #ifdef HTTP_USE_GNUTLS
1659   if (c->tls_session && !c->keep_socket)
1660     {
1661       gnutls_bye (c->tls_session, GNUTLS_SHUT_RDWR);
1662     }
1663 #endif /*HTTP_USE_GNUTLS*/
1664   if (c->fd != -1 && !c->keep_socket)
1665     close (c->fd);
1666
1667   xfree (c);
1668   return 0;
1669 }
1670 #endif /*HTTP_USE_ESTREAM*/
1671
1672
1673
1674 \f
1675 /**** Test code ****/
1676 #ifdef TEST
1677
1678 static gpg_error_t
1679 verify_callback (http_t hd, void *tls_context, int reserved)
1680 {
1681   log_info ("verification of certificates skipped\n");
1682   return 0;
1683 }
1684
1685
1686
1687 /* static void */
1688 /* my_gnutls_log (int level, const char *text) */
1689 /* { */
1690 /*   fprintf (stderr, "gnutls:L%d: %s", level, text); */
1691 /* } */
1692
1693 int
1694 main (int argc, char **argv)
1695 {
1696   int rc;
1697   parsed_uri_t uri;
1698   uri_tuple_t r;
1699   http_t hd;
1700   int c;
1701   gnutls_session_t tls_session = NULL;
1702 #ifdef HTTP_USE_GNUTLS
1703   gnutls_certificate_credentials certcred;
1704   const int certprio[] = { GNUTLS_CRT_X509, 0 };
1705 #endif /*HTTP_USE_GNUTLS*/
1706
1707 #ifdef HTTP_USE_ESTREAM
1708   es_init ();
1709 #endif
1710   log_set_prefix ("http-test", 1 | 4);
1711   if (argc == 1)
1712     {
1713       /*start_server (); */
1714       return 0;
1715     }
1716
1717   if (argc != 2)
1718     {
1719       fprintf (stderr, "usage: http-test uri\n");
1720       return 1;
1721     }
1722   argc--;
1723   argv++;
1724
1725 #ifdef HTTP_USE_GNUTLS
1726   rc = gnutls_global_init ();
1727   if (rc)
1728     log_error ("gnutls_global_init failed: %s\n", gnutls_strerror (rc));
1729   rc = gnutls_certificate_allocate_credentials (&certcred);
1730   if (rc)
1731     log_error ("gnutls_certificate_allocate_credentials failed: %s\n",
1732                gnutls_strerror (rc));
1733 /*   rc = gnutls_certificate_set_x509_trust_file */
1734 /*     (certcred, "ca.pem", GNUTLS_X509_FMT_PEM); */
1735 /*   if (rc) */
1736 /*     log_error ("gnutls_certificate_set_x509_trust_file failed: %s\n", */
1737 /*                gnutls_strerror (rc)); */
1738   rc = gnutls_init (&tls_session, GNUTLS_CLIENT);
1739   if (rc)
1740     log_error ("gnutls_init failed: %s\n", gnutls_strerror (rc));
1741   rc = gnutls_set_default_priority (tls_session);
1742   if (rc)
1743     log_error ("gnutls_set_default_priority failed: %s\n",
1744                gnutls_strerror (rc));
1745   rc = gnutls_certificate_type_set_priority (tls_session, certprio);
1746   if (rc)
1747     log_error ("gnutls_certificate_type_set_priority failed: %s\n",
1748                gnutls_strerror (rc));
1749   rc = gnutls_credentials_set (tls_session, GNUTLS_CRD_CERTIFICATE, certcred);
1750   if (rc)
1751     log_error ("gnutls_credentials_set failed: %s\n", gnutls_strerror (rc));
1752 /*   gnutls_global_set_log_function (my_gnutls_log); */
1753 /*   gnutls_global_set_log_level (4); */
1754
1755   http_register_tls_callback (verify_callback);
1756 #endif /*HTTP_USE_GNUTLS*/
1757
1758   rc = http_parse_uri (&uri, *argv);
1759   if (rc)
1760     {
1761       log_error ("`%s': %s\n", *argv, gpg_strerror (rc));
1762       http_release_parsed_uri (uri);
1763       return 1;
1764     }
1765
1766   printf ("Scheme: %s\n", uri->scheme);
1767   printf ("Host  : %s\n", uri->host);
1768   printf ("Port  : %u\n", uri->port);
1769   printf ("Path  : %s\n", uri->path);
1770   for (r = uri->params; r; r = r->next)
1771     {
1772       printf ("Params: %s", r->name);
1773       if (!r->no_value)
1774         {
1775           printf ("=%s", r->value);
1776           if (strlen (r->value) != r->valuelen)
1777             printf (" [real length=%d]", (int) r->valuelen);
1778         }
1779       putchar ('\n');
1780     }
1781   for (r = uri->query; r; r = r->next)
1782     {
1783       printf ("Query : %s", r->name);
1784       if (!r->no_value)
1785         {
1786           printf ("=%s", r->value);
1787           if (strlen (r->value) != r->valuelen)
1788             printf (" [real length=%d]", (int) r->valuelen);
1789         }
1790       putchar ('\n');
1791     }
1792   http_release_parsed_uri (uri);
1793   uri = NULL;
1794
1795   rc = http_open_document (&hd, *argv, NULL, HTTP_FLAG_NO_SHUTDOWN,
1796                            NULL, tls_session);
1797   if (rc)
1798     {
1799       log_error ("can't get `%s': %s\n", *argv, gpg_strerror (rc));
1800       return 1;
1801     }
1802   log_info ("open_http_document succeeded; status=%u\n",
1803             http_get_status_code (hd));
1804   while ((c = P_ES(getc) (http_get_read_ptr (hd))) != EOF)
1805     putchar (c);
1806   http_close (hd, 0);
1807
1808 #ifdef HTTP_USE_GNUTLS
1809   gnutls_deinit (tls_session);
1810   gnutls_certificate_free_credentials (certcred);
1811   gnutls_global_deinit ();
1812 #endif /*HTTP_USE_GNUTLS*/
1813
1814   return 0;
1815 }
1816 #endif /*TEST*/
1817
1818
1819 /*
1820 Local Variables:
1821 compile-command: "gcc -I.. -I../gl -DTEST -DHAVE_CONFIG_H -Wall -O2 -g -o http-test http.c -L. -lcommon -L../jnlib -ljnlib -lgcrypt -lpth -lgnutls"
1822 End:
1823 */