2007-09-14 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / keyserver / curl-shim.c
blob085ed99c44c9bfcc0f486f32dd393d4cbae23dde
1 /* curl-shim.c - Implement a small subset of the curl API in terms of
2 * the iobuf HTTP API
4 * Copyright (C) 2005, 2006 Free Software Foundation, Inc.
6 * This file is part of GnuPG.
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.
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.
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/>.
22 #include <config.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <errno.h>
29 #include "http.h"
30 #include "util.h"
31 #include "ksutil.h"
32 #include "curl-shim.h"
34 static CURLcode
35 handle_error(CURL *curl,CURLcode err,const char *str)
37 if(curl->errorbuffer)
39 /* Make sure you never exceed CURL_ERROR_SIZE, currently set to
40 256 in curl-shim.h */
41 switch(err)
43 case CURLE_OK:
44 strcpy(curl->errorbuffer,"okay");
45 break;
47 case CURLE_UNSUPPORTED_PROTOCOL:
48 strcpy(curl->errorbuffer,"unsupported protocol");
49 break;
51 case CURLE_COULDNT_CONNECT:
52 strcpy(curl->errorbuffer,"couldn't connect");
53 break;
55 case CURLE_WRITE_ERROR:
56 strcpy(curl->errorbuffer,"write error");
57 break;
59 case CURLE_HTTP_RETURNED_ERROR:
60 sprintf(curl->errorbuffer,"url returned error %u",curl->status);
61 break;
63 default:
64 strcpy(curl->errorbuffer,"generic error");
65 break;
68 if(str && (strlen(curl->errorbuffer)+2+strlen(str)+1)<=CURL_ERROR_SIZE)
70 strcat(curl->errorbuffer,": ");
71 strcat(curl->errorbuffer,str);
75 return err;
78 CURLcode
79 curl_global_init(long flags)
81 return CURLE_OK;
84 void
85 curl_global_cleanup(void) {}
87 CURL *
88 curl_easy_init(void)
90 CURL *handle;
92 handle=calloc(1,sizeof(CURL));
93 if(handle)
94 handle->errors=stderr;
96 return handle;
99 void
100 curl_easy_cleanup(CURL *curl)
102 if (curl)
104 http_close (curl->hd, 0);
105 free(curl);
109 CURLcode
110 curl_easy_setopt(CURL *curl,CURLoption option,...)
112 va_list ap;
114 va_start(ap,option);
116 switch(option)
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;
156 return handle_error(curl,CURLE_OK,NULL);
159 CURLcode
160 curl_easy_perform(CURL *curl)
162 int rc;
163 CURLcode err=CURLE_OK;
164 const char *errstr=NULL;
165 char *proxy=NULL;
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. */
173 if(curl->proxy)
174 proxy=curl->proxy;
175 else
176 proxy=getenv(HTTP_PROXY_ENV);
178 if(curl->flags.verbose)
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");
188 if(curl->flags.post)
190 rc = http_open (&curl->hd, HTTP_REQ_POST, curl->url, curl->auth,
191 0, proxy, NULL);
192 if (!rc)
194 unsigned int post_len = strlen(curl->postfields);
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);
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;
211 else
213 rc = http_open (&curl->hd, HTTP_REQ_GET, curl->url, curl->auth,
214 0, proxy, NULL);
215 if (!rc)
217 rc = http_wait_response (curl->hd);
218 curl->status = http_get_status_code (curl->hd);
219 if (!rc)
221 if (curl->flags.failonerror && curl->status>=300)
222 err = CURLE_HTTP_RETURNED_ERROR;
223 else
225 size_t maxlen = 1024;
226 size_t buflen;
227 unsigned int len;
228 char *line = NULL;
230 while ((len = es_read_line (http_get_read_ptr (curl->hd),
231 &line, &buflen, &maxlen)))
233 size_t ret;
235 maxlen=1024;
237 ret=(curl->writer)(line,len,1,curl->file);
238 if(ret!=len)
240 err=CURLE_WRITE_ERROR;
241 break;
245 es_free (line);
246 http_close(curl->hd, 0);
247 curl->hd = NULL;
250 else
252 http_close (curl->hd, 0);
253 curl->hd = NULL;
258 switch(gpg_err_code (rc))
260 case 0:
261 break;
263 case GPG_ERR_INV_URI:
264 err=CURLE_UNSUPPORTED_PROTOCOL;
265 break;
267 default:
268 errstr=gpg_strerror (rc);
269 err=CURLE_COULDNT_CONNECT;
270 break;
273 return handle_error(curl,err,errstr);
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"
282 char *
283 curl_escape(char *str,int length)
285 int len,max,idx,enc_idx=0;
286 char *enc;
288 if(length)
289 len=length;
290 else
291 len=strlen(str);
293 enc=malloc(len+1);
294 if(!enc)
295 return enc;
297 max=len;
299 for(idx=0;idx<len;idx++)
301 if(enc_idx+3>max)
303 char *tmp;
305 max+=100;
307 tmp=realloc(enc,max+1);
308 if(!tmp)
310 free(enc);
311 return NULL;
314 enc=tmp;
317 if(strchr(VALID_URI_CHARS,str[idx]))
318 enc[enc_idx++]=str[idx];
319 else
321 char numbuf[5];
322 sprintf(numbuf,"%%%02X",str[idx]);
323 strcpy(&enc[enc_idx],numbuf);
324 enc_idx+=3;
328 enc[enc_idx]='\0';
330 return enc;
333 curl_version_info_data *
334 curl_version_info(int type)
336 static curl_version_info_data data;
337 static const char *protocols[]={"http",NULL};
339 data.protocols=protocols;
341 return &data;