Clarified cURL and OpenLDAP license issues.
[gnupg.git] / keyserver / curl-shim.c
1 /* curl-shim.c - Implement a small subset of the curl API in terms of
2  * the iobuf HTTP API
3  *
4  * Copyright (C) 2005, 2006 Free Software Foundation, Inc.
5  *
6  * This file is part of GnuPG.
7  *
8  * GnuPG is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * GnuPG is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
21  * USA.
22  *
23  * In addition, as a special exception, the Free Software Foundation
24  * gives permission to link the code of the keyserver helper tools:
25  * gpgkeys_ldap, gpgkeys_curl and gpgkeys_hkp with the OpenSSL
26  * project's "OpenSSL" library (or with modified versions of it that
27  * use the same license as the "OpenSSL" library), and distribute the
28  * linked executables.  You must obey the GNU General Public License
29  * in all respects for all of the code used other than "OpenSSL".  If
30  * you modify this file, you may extend this exception to your version
31  * of the file, but you are not obligated to do so.  If you do not
32  * wish to do so, delete this exception statement from your version.
33  */
34
35 #include <config.h>
36 #include <stdarg.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stdio.h>
40 #include <errno.h>
41
42 #include "http.h"
43 #include "util.h"
44 #include "ksutil.h"
45 #include "curl-shim.h"
46
47 static CURLcode
48 handle_error(CURL *curl,CURLcode err,const char *str)
49 {
50   if(curl->errorbuffer)
51     {
52       /* Make sure you never exceed CURL_ERROR_SIZE, currently set to
53          256 in curl-shim.h */
54       switch(err)
55         {
56         case CURLE_OK:
57           strcpy(curl->errorbuffer,"okay");
58           break;
59
60         case CURLE_UNSUPPORTED_PROTOCOL:
61           strcpy(curl->errorbuffer,"unsupported protocol");
62           break;
63
64         case CURLE_COULDNT_CONNECT:
65           strcpy(curl->errorbuffer,"couldn't connect");
66           break;
67
68         case CURLE_WRITE_ERROR:
69           strcpy(curl->errorbuffer,"write error");
70           break;
71
72         case CURLE_HTTP_RETURNED_ERROR:
73           sprintf(curl->errorbuffer,"url returned error %u",curl->status);
74           break;
75
76         default:
77           strcpy(curl->errorbuffer,"generic error");
78           break;
79         }
80
81       if(str && (strlen(curl->errorbuffer)+2+strlen(str)+1)<=CURL_ERROR_SIZE)
82         {
83           strcat(curl->errorbuffer,": ");
84           strcat(curl->errorbuffer,str);
85         }
86     }
87
88   return err;
89 }
90
91 CURLcode
92 curl_global_init(long flags)
93 {
94   return CURLE_OK;
95 }
96
97 void
98 curl_global_cleanup(void) {}
99
100 CURL *
101 curl_easy_init(void)
102 {
103   CURL *handle;
104
105   handle=calloc(1,sizeof(CURL));
106   if(handle)
107     handle->errors=stderr;
108
109   return handle;
110 }
111
112 void
113 curl_easy_cleanup(CURL *curl)
114 {
115   if (curl)
116     {
117       http_close (curl->hd);
118       free(curl);
119     }
120 }
121
122 CURLcode
123 curl_easy_setopt(CURL *curl,CURLoption option,...)
124 {
125   va_list ap;
126
127   va_start(ap,option);
128
129   switch(option)
130     {
131     case CURLOPT_URL:
132       curl->url=va_arg(ap,char *);
133       break;
134     case CURLOPT_USERPWD:
135       curl->auth=va_arg(ap,char *);
136       break;
137     case CURLOPT_WRITEFUNCTION:
138       curl->writer=va_arg(ap,write_func);
139       break;
140     case CURLOPT_FILE:
141       curl->file=va_arg(ap,void *);
142       break;
143     case CURLOPT_ERRORBUFFER:
144       curl->errorbuffer=va_arg(ap,char *);
145       break;
146     case CURLOPT_PROXY:
147       curl->proxy=va_arg(ap,char *);
148       break;
149     case CURLOPT_POST:
150       curl->flags.post=va_arg(ap,unsigned int);
151       break;
152     case CURLOPT_POSTFIELDS:
153       curl->postfields=va_arg(ap,char *);
154       break;
155     case CURLOPT_FAILONERROR:
156       curl->flags.failonerror=va_arg(ap,unsigned int);
157       break;
158     case CURLOPT_VERBOSE:
159       curl->flags.verbose=va_arg(ap,unsigned int);
160       break;
161     case CURLOPT_STDERR:
162       curl->errors=va_arg(ap,FILE *);
163       break;
164     default:
165       /* We ignore the huge majority of curl options */
166       break;
167     }
168
169   return handle_error(curl,CURLE_OK,NULL);
170 }
171
172 CURLcode
173 curl_easy_perform(CURL *curl)
174 {
175   int rc;
176   CURLcode err=CURLE_OK;
177   const char *errstr=NULL;
178   char *proxy=NULL;
179
180   /* Emulate the libcurl proxy behavior.  If the calling program set a
181      proxy, use it.  If it didn't set a proxy or set it to NULL, check
182      for one in the environment.  If the calling program explicitly
183      set a null-string proxy the http code doesn't use a proxy at
184      all. */
185
186   if(curl->proxy)
187     proxy=curl->proxy;
188   else
189     proxy=getenv(HTTP_PROXY_ENV);
190
191   if(curl->flags.verbose)
192     fprintf(curl->errors,"* HTTP proxy is \"%s\"\n",proxy?proxy:"null");
193
194   if(curl->flags.post)
195     {
196       rc = http_open (&curl->hd, HTTP_REQ_POST, curl->url, curl->auth,
197                       0, proxy, NULL);
198       if (!rc)
199         {
200           unsigned int post_len = strlen(curl->postfields);
201
202           es_fprintf (http_get_write_ptr (curl->hd),
203                       "Content-Type: application/x-www-form-urlencoded\r\n"
204                       "Content-Length: %u\r\n", post_len);
205           http_start_data (curl->hd);
206           es_write (http_get_write_ptr (curl->hd),
207                     curl->postfields, post_len, NULL);
208
209           rc = http_wait_response (curl->hd);
210           curl->status = http_get_status_code (curl->hd);
211           if (!rc && curl->flags.failonerror && curl->status>=300)
212             err = CURLE_HTTP_RETURNED_ERROR;
213           http_close(curl->hd);
214           curl->hd = NULL;
215         }
216     }
217   else
218     {
219       rc = http_open (&curl->hd, HTTP_REQ_GET, curl->url, curl->auth,
220                       0, proxy, NULL);
221       if (!rc)
222         {
223           rc = http_wait_response (curl->hd);
224           curl->status = http_get_status_code (curl->hd);
225           if (!rc)
226             {
227               if (curl->flags.failonerror && curl->status>=300)
228                 err = CURLE_HTTP_RETURNED_ERROR;
229               else
230                 {
231                   unsigned int maxlen = 1024, buflen, len;
232                   unsigned char *line = NULL;
233
234                   while ((len = es_read_line (http_get_read_ptr (curl->hd),
235                                               &line, &buflen, &maxlen)))
236                     {
237                       size_t ret;
238
239                       maxlen=1024;
240
241                       ret=(curl->writer)(line,len,1,curl->file);
242                       if(ret!=len)
243                         {
244                           err=CURLE_WRITE_ERROR;
245                           break;
246                         }
247                     }
248
249                   es_free (line);
250                   http_close(curl->hd);
251                   curl->hd = NULL;
252                 }
253             }
254           else
255             {
256               http_close (curl->hd);
257               curl->hd = NULL;
258             }
259         }
260     }
261
262   switch(rc)
263     {
264     case 0:
265       break;
266
267     case G10ERR_INVALID_URI:
268       err=CURLE_UNSUPPORTED_PROTOCOL;
269       break;
270
271     case G10ERR_NETWORK:
272       errstr=strerror(errno);
273       err=CURLE_COULDNT_CONNECT;
274       break;
275
276     default:
277       errstr=g10_errstr(rc);
278       err=CURLE_COULDNT_CONNECT;
279       break;
280     }
281       
282   return handle_error(curl,err,errstr);
283 }
284
285 /* This is not the same exact set that is allowed according to
286    RFC-2396, but it is what the real curl uses. */
287 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
288                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
289                         "0123456789"
290
291 char *
292 curl_escape(char *str,int length)
293 {
294   int len,max,idx,enc_idx=0;
295   char *enc;
296
297   if(length)
298     len=length;
299   else
300     len=strlen(str);
301
302   enc=malloc(len+1);
303   if(!enc)
304     return enc;
305
306   max=len;
307
308   for(idx=0;idx<len;idx++)
309     {
310       if(enc_idx+3>max)
311         {
312           char *tmp;
313
314           max+=100;
315
316           tmp=realloc(enc,max+1);
317           if(!tmp)
318             {
319               free(enc);
320               return NULL;
321             }
322
323           enc=tmp;
324         }
325
326       if(strchr(VALID_URI_CHARS,str[idx]))
327         enc[enc_idx++]=str[idx];
328       else
329         {
330           char numbuf[5];
331           sprintf(numbuf,"%%%02X",str[idx]);
332           strcpy(&enc[enc_idx],numbuf);
333           enc_idx+=3;
334         }
335     }
336
337   enc[enc_idx]='\0';
338
339   return enc;
340 }
341
342 void
343 curl_free(char *ptr)
344 {
345   free(ptr);
346 }