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