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