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