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