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