Changed to GPLv3.
[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 3 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, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <config.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <errno.h>
28
29 #include "http.h"
30 #include "util.h"
31 #include "ksutil.h"
32 #include "curl-shim.h"
33
34 static CURLcode
35 handle_error(CURL *curl,CURLcode err,const char *str)
36 {
37   if(curl->errorbuffer)
38     {
39       /* Make sure you never exceed CURL_ERROR_SIZE, currently set to
40          256 in curl-shim.h */
41       switch(err)
42         {
43         case CURLE_OK:
44           strcpy(curl->errorbuffer,"okay");
45           break;
46
47         case CURLE_UNSUPPORTED_PROTOCOL:
48           strcpy(curl->errorbuffer,"unsupported protocol");
49           break;
50
51         case CURLE_COULDNT_CONNECT:
52           strcpy(curl->errorbuffer,"couldn't connect");
53           break;
54
55         case CURLE_WRITE_ERROR:
56           strcpy(curl->errorbuffer,"write error");
57           break;
58
59         case CURLE_HTTP_RETURNED_ERROR:
60           sprintf(curl->errorbuffer,"url returned error %u",curl->status);
61           break;
62
63         default:
64           strcpy(curl->errorbuffer,"generic error");
65           break;
66         }
67
68       if(str && (strlen(curl->errorbuffer)+2+strlen(str)+1)<=CURL_ERROR_SIZE)
69         {
70           strcat(curl->errorbuffer,": ");
71           strcat(curl->errorbuffer,str);
72         }
73     }
74
75   return err;
76 }
77
78 CURLcode
79 curl_global_init(long flags)
80 {
81   return CURLE_OK;
82 }
83
84 void
85 curl_global_cleanup(void) {}
86
87 CURL *
88 curl_easy_init(void)
89 {
90   CURL *handle;
91
92   handle=calloc(1,sizeof(CURL));
93   if(handle)
94     handle->errors=stderr;
95
96   return handle;
97 }
98
99 void
100 curl_easy_cleanup(CURL *curl)
101 {
102   if (curl)
103     {
104       http_close (curl->hd, 0);
105       free(curl);
106     }
107 }
108
109 CURLcode
110 curl_easy_setopt(CURL *curl,CURLoption option,...)
111 {
112   va_list ap;
113
114   va_start(ap,option);
115
116   switch(option)
117     {
118     case CURLOPT_URL:
119       curl->url=va_arg(ap,char *);
120       break;
121     case CURLOPT_USERPWD:
122       curl->auth=va_arg(ap,char *);
123       break;
124     case CURLOPT_WRITEFUNCTION:
125       curl->writer=va_arg(ap,write_func);
126       break;
127     case CURLOPT_FILE:
128       curl->file=va_arg(ap,void *);
129       break;
130     case CURLOPT_ERRORBUFFER:
131       curl->errorbuffer=va_arg(ap,char *);
132       break;
133     case CURLOPT_PROXY:
134       curl->proxy=va_arg(ap,char *);
135       break;
136     case CURLOPT_POST:
137       curl->flags.post=va_arg(ap,unsigned int);
138       break;
139     case CURLOPT_POSTFIELDS:
140       curl->postfields=va_arg(ap,char *);
141       break;
142     case CURLOPT_FAILONERROR:
143       curl->flags.failonerror=va_arg(ap,unsigned int);
144       break;
145     case CURLOPT_VERBOSE:
146       curl->flags.verbose=va_arg(ap,unsigned int);
147       break;
148     case CURLOPT_STDERR:
149       curl->errors=va_arg(ap,FILE *);
150       break;
151     default:
152       /* We ignore the huge majority of curl options */
153       break;
154     }
155
156   return handle_error(curl,CURLE_OK,NULL);
157 }
158
159 CURLcode
160 curl_easy_perform(CURL *curl)
161 {
162   int rc;
163   CURLcode err=CURLE_OK;
164   const char *errstr=NULL;
165   char *proxy=NULL;
166
167   /* Emulate the libcurl proxy behavior.  If the calling program set a
168      proxy, use it.  If it didn't set a proxy or set it to NULL, check
169      for one in the environment.  If the calling program explicitly
170      set a null-string proxy the http code doesn't use a proxy at
171      all. */
172
173   if(curl->proxy)
174     proxy=curl->proxy;
175   else
176     proxy=getenv(HTTP_PROXY_ENV);
177
178   if(curl->flags.verbose)
179     {
180       fprintf(curl->errors,"* HTTP proxy is \"%s\"\n",proxy?proxy:"null");
181       fprintf(curl->errors,"* HTTP URL is \"%s\"\n",curl->url);
182       fprintf(curl->errors,"* HTTP auth is \"%s\"\n",
183               curl->auth?curl->auth:"null");
184       fprintf(curl->errors,"* HTTP method is %s\n",
185               curl->flags.post?"POST":"GET");
186     }
187
188   if(curl->flags.post)
189     {
190       rc = http_open (&curl->hd, HTTP_REQ_POST, curl->url, curl->auth,
191                       0, proxy, NULL);
192       if (!rc)
193         {
194           unsigned int post_len = strlen(curl->postfields);
195
196           es_fprintf (http_get_write_ptr (curl->hd),
197                       "Content-Type: application/x-www-form-urlencoded\r\n"
198                       "Content-Length: %u\r\n", post_len);
199           http_start_data (curl->hd);
200           es_write (http_get_write_ptr (curl->hd),
201                     curl->postfields, post_len, NULL);
202
203           rc = http_wait_response (curl->hd);
204           curl->status = http_get_status_code (curl->hd);
205           if (!rc && curl->flags.failonerror && curl->status>=300)
206             err = CURLE_HTTP_RETURNED_ERROR;
207           http_close (curl->hd, 0);
208           curl->hd = NULL;
209         }
210     }
211   else
212     {
213       rc = http_open (&curl->hd, HTTP_REQ_GET, curl->url, curl->auth,
214                       0, proxy, NULL);
215       if (!rc)
216         {
217           rc = http_wait_response (curl->hd);
218           curl->status = http_get_status_code (curl->hd);
219           if (!rc)
220             {
221               if (curl->flags.failonerror && curl->status>=300)
222                 err = CURLE_HTTP_RETURNED_ERROR;
223               else
224                 {
225                   size_t maxlen = 1024;
226                   size_t buflen;
227                   unsigned int len;
228                   char *line = NULL;
229
230                   while ((len = es_read_line (http_get_read_ptr (curl->hd),
231                                               &line, &buflen, &maxlen)))
232                     {
233                       size_t ret;
234
235                       maxlen=1024;
236
237                       ret=(curl->writer)(line,len,1,curl->file);
238                       if(ret!=len)
239                         {
240                           err=CURLE_WRITE_ERROR;
241                           break;
242                         }
243                     }
244
245                   es_free (line);
246                   http_close(curl->hd, 0);
247                   curl->hd = NULL;
248                 }
249             }
250           else
251             {
252               http_close (curl->hd, 0);
253               curl->hd = NULL;
254             }
255         }
256     }
257
258   switch(gpg_err_code (rc))
259     {
260     case 0:
261       break;
262
263     case GPG_ERR_INV_URI:
264       err=CURLE_UNSUPPORTED_PROTOCOL;
265       break;
266
267     default:
268       errstr=gpg_strerror (rc);
269       err=CURLE_COULDNT_CONNECT;
270       break;
271     }
272       
273   return handle_error(curl,err,errstr);
274 }
275
276 /* This is not the same exact set that is allowed according to
277    RFC-2396, but it is what the real curl uses. */
278 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
279                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
280                         "0123456789"
281
282 char *
283 curl_escape(char *str,int length)
284 {
285   int len,max,idx,enc_idx=0;
286   char *enc;
287
288   if(length)
289     len=length;
290   else
291     len=strlen(str);
292
293   enc=malloc(len+1);
294   if(!enc)
295     return enc;
296
297   max=len;
298
299   for(idx=0;idx<len;idx++)
300     {
301       if(enc_idx+3>max)
302         {
303           char *tmp;
304
305           max+=100;
306
307           tmp=realloc(enc,max+1);
308           if(!tmp)
309             {
310               free(enc);
311               return NULL;
312             }
313
314           enc=tmp;
315         }
316
317       if(strchr(VALID_URI_CHARS,str[idx]))
318         enc[enc_idx++]=str[idx];
319       else
320         {
321           char numbuf[5];
322           sprintf(numbuf,"%%%02X",str[idx]);
323           strcpy(&enc[enc_idx],numbuf);
324           enc_idx+=3;
325         }
326     }
327
328   enc[enc_idx]='\0';
329
330   return enc;
331 }
332
333 curl_version_info_data *
334 curl_version_info(int type)
335 {
336   static curl_version_info_data data;
337   static const char *protocols[]={"http",NULL};
338
339   data.protocols=protocols;
340
341   return &data;
342 }