Avoid catch-22 with README.main not being distributed but having the
[gnupg.git] / keyserver / curl-shim.c
blob500d9f5622fe4a2a4e4bd44964db83aadca3b56a
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, 2007, 2008, 2009 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 "util.h"
30 #include "http.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 (void)flags;
82 return CURLE_OK;
85 void
86 curl_global_cleanup(void) {}
88 CURL *
89 curl_easy_init(void)
91 CURL *handle;
93 #ifdef HAVE_W32_SYSTEM
94 w32_init_sockets ();
95 #endif
97 handle=calloc(1,sizeof(CURL));
98 if(handle)
99 handle->errors=stderr;
101 return handle;
104 void
105 curl_easy_cleanup(CURL *curl)
107 if (curl)
109 http_close (curl->hd, 0);
110 free(curl);
114 CURLcode
115 curl_easy_setopt(CURL *curl,CURLoption option,...)
117 va_list ap;
119 va_start(ap,option);
121 switch(option)
123 case CURLOPT_URL:
124 curl->url=va_arg(ap,char *);
125 break;
126 case CURLOPT_USERPWD:
127 curl->auth=va_arg(ap,char *);
128 break;
129 case CURLOPT_WRITEFUNCTION:
130 curl->writer=va_arg(ap,write_func);
131 break;
132 case CURLOPT_FILE:
133 curl->file=va_arg(ap,void *);
134 break;
135 case CURLOPT_ERRORBUFFER:
136 curl->errorbuffer=va_arg(ap,char *);
137 break;
138 case CURLOPT_PROXY:
139 curl->proxy=va_arg(ap,char *);
140 break;
141 case CURLOPT_POST:
142 curl->flags.post=va_arg(ap,long)?1:0;
143 break;
144 case CURLOPT_POSTFIELDS:
145 curl->postfields=va_arg(ap,char *);
146 break;
147 case CURLOPT_SRVTAG_GPG_HACK:
148 curl->srvtag=va_arg(ap,char *);
149 break;
150 case CURLOPT_FAILONERROR:
151 curl->flags.failonerror=va_arg(ap,long)?1:0;
152 break;
153 case CURLOPT_VERBOSE:
154 curl->flags.verbose=va_arg(ap,long)?1:0;
155 break;
156 case CURLOPT_STDERR:
157 curl->errors=va_arg(ap,FILE *);
158 break;
159 case CURLOPT_HTTPHEADER:
160 curl->headers=va_arg(ap,struct curl_slist *);
161 break;
162 default:
163 /* We ignore the huge majority of curl options */
164 break;
167 return handle_error(curl,CURLE_OK,NULL);
170 CURLcode
171 curl_easy_perform(CURL *curl)
173 int rc;
174 CURLcode err=CURLE_OK;
175 const char *errstr=NULL;
176 char *proxy=NULL;
178 /* Emulate the libcurl proxy behavior. If the calling program set a
179 proxy, use it. If it didn't set a proxy or set it to NULL, check
180 for one in the environment. If the calling program explicitly
181 set a null-string proxy the http code doesn't use a proxy at
182 all. */
184 if(curl->proxy)
185 proxy=curl->proxy;
186 else
187 proxy=getenv(HTTP_PROXY_ENV);
189 if(curl->flags.verbose)
191 fprintf(curl->errors,"* HTTP proxy is \"%s\"\n",proxy?proxy:"null");
192 fprintf(curl->errors,"* HTTP URL is \"%s\"\n",curl->url);
193 fprintf(curl->errors,"* HTTP auth is \"%s\"\n",
194 curl->auth?curl->auth:"null");
195 fprintf(curl->errors,"* HTTP method is %s\n",
196 curl->flags.post?"POST":"GET");
199 if(curl->flags.post)
201 rc = http_open (&curl->hd, HTTP_REQ_POST, curl->url, curl->auth,
202 0, proxy, NULL, curl->srvtag,
203 curl->headers?curl->headers->list:NULL);
204 if (!rc)
206 unsigned int post_len = strlen(curl->postfields);
208 es_fprintf (http_get_write_ptr (curl->hd),
209 "Content-Type: application/x-www-form-urlencoded\r\n"
210 "Content-Length: %u\r\n", post_len);
211 http_start_data (curl->hd);
212 es_write (http_get_write_ptr (curl->hd),
213 curl->postfields, post_len, NULL);
215 rc = http_wait_response (curl->hd);
216 curl->status = http_get_status_code (curl->hd);
217 if (!rc && curl->flags.failonerror && curl->status>=300)
218 err = CURLE_HTTP_RETURNED_ERROR;
219 http_close (curl->hd, 0);
220 curl->hd = NULL;
223 else
225 rc = http_open (&curl->hd, HTTP_REQ_GET, curl->url, curl->auth,
226 0, proxy, NULL, curl->srvtag,
227 curl->headers?curl->headers->list:NULL);
228 if (!rc)
230 rc = http_wait_response (curl->hd);
231 curl->status = http_get_status_code (curl->hd);
232 if (!rc)
234 if (curl->flags.failonerror && curl->status>=300)
235 err = CURLE_HTTP_RETURNED_ERROR;
236 else
238 size_t maxlen = 1024;
239 size_t buflen;
240 unsigned int len;
241 char *line = NULL;
243 while ((len = es_read_line (http_get_read_ptr (curl->hd),
244 &line, &buflen, &maxlen)))
246 size_t ret;
248 maxlen=1024;
250 ret=(curl->writer)(line,len,1,curl->file);
251 if(ret!=len)
253 err=CURLE_WRITE_ERROR;
254 break;
258 es_free (line);
259 http_close(curl->hd, 0);
260 curl->hd = NULL;
263 else
265 http_close (curl->hd, 0);
266 curl->hd = NULL;
271 switch(gpg_err_code (rc))
273 case 0:
274 break;
276 case GPG_ERR_INV_URI:
277 err=CURLE_UNSUPPORTED_PROTOCOL;
278 break;
280 default:
281 errstr=gpg_strerror (rc);
282 err=CURLE_COULDNT_CONNECT;
283 break;
286 return handle_error(curl,err,errstr);
289 /* This is not the same exact set that is allowed according to
290 RFC-2396, but it is what the real curl uses. */
291 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
292 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
293 "0123456789"
295 char *
296 curl_escape(char *str,int length)
298 int len,max,idx,enc_idx=0;
299 char *enc;
301 if(length)
302 len=length;
303 else
304 len=strlen(str);
306 enc=malloc(len+1);
307 if(!enc)
308 return enc;
310 max=len;
312 for(idx=0;idx<len;idx++)
314 if(enc_idx+3>max)
316 char *tmp;
318 max+=100;
320 tmp=realloc(enc,max+1);
321 if(!tmp)
323 free(enc);
324 return NULL;
327 enc=tmp;
330 if(strchr(VALID_URI_CHARS,str[idx]))
331 enc[enc_idx++]=str[idx];
332 else
334 char numbuf[5];
335 sprintf(numbuf,"%%%02X",str[idx]);
336 strcpy(&enc[enc_idx],numbuf);
337 enc_idx+=3;
341 enc[enc_idx]='\0';
343 return enc;
346 curl_version_info_data *
347 curl_version_info(int type)
349 static curl_version_info_data data;
350 static const char *protocols[]={"http",NULL};
352 (void)type;
354 data.protocols=protocols;
356 return &data;
359 struct curl_slist *
360 curl_slist_append(struct curl_slist *list,const char *string)
362 if(!list)
364 list=calloc(1,sizeof(*list));
365 if(!list)
366 return NULL;
369 add_to_strlist(&list->list,string);
371 return list;
374 void
375 curl_slist_free_all(struct curl_slist *list)
377 if(list)
379 free_strlist(list->list);
380 free(list);