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