gpg: Create default keyring with .kbx suffix.
[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, 2007, 2008, 2009 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 3 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, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <config.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <errno.h>
28
29 #include "util.h"
30 #include "http.h"
31 #include "ksutil.h"
32 #include "curl-shim.h"
33
34 static CURLcode
35 handle_error(CURL *curl,CURLcode err,const char *str)
36 {
37   if(curl->errorbuffer)
38     {
39       /* Make sure you never exceed CURL_ERROR_SIZE, currently set to
40          256 in curl-shim.h */
41       switch(err)
42         {
43         case CURLE_OK:
44           strcpy(curl->errorbuffer,"okay");
45           break;
46
47         case CURLE_UNSUPPORTED_PROTOCOL:
48           strcpy(curl->errorbuffer,"unsupported protocol");
49           break;
50
51         case CURLE_COULDNT_CONNECT:
52           strcpy(curl->errorbuffer,"couldn't connect");
53           break;
54
55         case CURLE_WRITE_ERROR:
56           strcpy(curl->errorbuffer,"write error");
57           break;
58
59         case CURLE_HTTP_RETURNED_ERROR:
60           sprintf(curl->errorbuffer,"url returned error %u",curl->status);
61           break;
62
63         default:
64           strcpy(curl->errorbuffer,"generic error");
65           break;
66         }
67
68       if(str && (strlen(curl->errorbuffer)+2+strlen(str)+1)<=CURL_ERROR_SIZE)
69         {
70           strcat(curl->errorbuffer,": ");
71           strcat(curl->errorbuffer,str);
72         }
73     }
74
75   return err;
76 }
77
78 CURLcode
79 curl_global_init(long flags)
80 {
81   (void)flags;
82   return CURLE_OK;
83 }
84
85 void
86 curl_global_cleanup(void) {}
87
88 CURL *
89 curl_easy_init(void)
90 {
91   CURL *handle;
92
93 #ifdef HAVE_W32_SYSTEM
94   w32_init_sockets ();
95 #endif
96
97   handle=calloc(1,sizeof(CURL));
98   if(handle)
99     handle->errors=stderr;
100
101   return handle;
102 }
103
104 void
105 curl_easy_cleanup(CURL *curl)
106 {
107   if (curl)
108     {
109       http_close (curl->hd, 0);
110       free(curl);
111     }
112 }
113
114 CURLcode
115 curl_easy_setopt(CURL *curl,CURLoption option,...)
116 {
117   va_list ap;
118
119   va_start(ap,option);
120
121   switch(option)
122     {
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;
165     }
166
167   return handle_error(curl,CURLE_OK,NULL);
168 }
169
170 CURLcode
171 curl_easy_perform(CURL *curl)
172 {
173   int rc;
174   CURLcode err=CURLE_OK;
175   const char *errstr=NULL;
176   char *proxy=NULL;
177
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. */
183
184   if(curl->proxy)
185     proxy=curl->proxy;
186   else
187     proxy=getenv(HTTP_PROXY_ENV);
188
189   if(curl->flags.verbose)
190     {
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");
197     }
198
199   if(curl->flags.post)
200     {
201       rc = http_open (&curl->hd, HTTP_REQ_POST, curl->url, NULL, curl->auth,
202                       0, proxy, NULL, curl->srvtag,
203                       curl->headers?curl->headers->list:NULL);
204       if (!rc)
205         {
206           unsigned int post_len = strlen(curl->postfields);
207
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);
214
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;
221         }
222     }
223   else
224     {
225       rc = http_open (&curl->hd, HTTP_REQ_GET, curl->url, NULL, curl->auth,
226                       0, proxy, NULL, curl->srvtag,
227                       curl->headers?curl->headers->list:NULL);
228       if (!rc)
229         {
230           rc = http_wait_response (curl->hd);
231           curl->status = http_get_status_code (curl->hd);
232           if (!rc)
233             {
234               if (curl->flags.failonerror && curl->status>=300)
235                 err = CURLE_HTTP_RETURNED_ERROR;
236               else
237                 {
238                   size_t maxlen = 1024;
239                   size_t buflen;
240                   unsigned int len;
241                   char *line = NULL;
242
243                   while ((len = es_read_line (http_get_read_ptr (curl->hd),
244                                               &line, &buflen, &maxlen)))
245                     {
246                       size_t ret;
247
248                       maxlen=1024;
249
250                       ret=(curl->writer)(line,len,1,curl->file);
251                       if(ret!=len)
252                         {
253                           err=CURLE_WRITE_ERROR;
254                           break;
255                         }
256                     }
257
258                   es_free (line);
259                   http_close(curl->hd, 0);
260                   curl->hd = NULL;
261                 }
262             }
263           else
264             {
265               http_close (curl->hd, 0);
266               curl->hd = NULL;
267             }
268         }
269     }
270
271   switch(gpg_err_code (rc))
272     {
273     case 0:
274       break;
275
276     case GPG_ERR_INV_URI:
277       err=CURLE_UNSUPPORTED_PROTOCOL;
278       break;
279
280     default:
281       errstr=gpg_strerror (rc);
282       err=CURLE_COULDNT_CONNECT;
283       break;
284     }
285
286   return handle_error(curl,err,errstr);
287 }
288
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"
294
295 char *
296 curl_escape(char *str,int length)
297 {
298   int len,max,idx,enc_idx=0;
299   char *enc;
300
301   if(length)
302     len=length;
303   else
304     len=strlen(str);
305
306   enc=malloc(len+1);
307   if(!enc)
308     return enc;
309
310   max=len;
311
312   for(idx=0;idx<len;idx++)
313     {
314       if(enc_idx+3>max)
315         {
316           char *tmp;
317
318           max+=100;
319
320           tmp=realloc(enc,max+1);
321           if(!tmp)
322             {
323               free(enc);
324               return NULL;
325             }
326
327           enc=tmp;
328         }
329
330       if(strchr(VALID_URI_CHARS,str[idx]))
331         enc[enc_idx++]=str[idx];
332       else
333         {
334           char numbuf[5];
335           sprintf(numbuf,"%%%02X",str[idx]);
336           strcpy(&enc[enc_idx],numbuf);
337           enc_idx+=3;
338         }
339     }
340
341   enc[enc_idx]='\0';
342
343   return enc;
344 }
345
346 curl_version_info_data *
347 curl_version_info(int type)
348 {
349   static curl_version_info_data data;
350   static const char *protocols[]={"http",NULL};
351
352   (void)type;
353
354   data.protocols=protocols;
355
356   return &data;
357 }
358
359 struct curl_slist *
360 curl_slist_append(struct curl_slist *list,const char *string)
361 {
362   if(!list)
363     {
364       list=calloc(1,sizeof(*list));
365       if(!list)
366         return NULL;
367     }
368
369   add_to_strlist(&list->list,string);
370
371   return list;
372 }
373
374 void
375 curl_slist_free_all(struct curl_slist *list)
376 {
377   if(list)
378     {
379       free_strlist(list->list);
380       free(list);
381     }
382 }