g10: Fix card-edit/fetch to use keyserver_fetch.
[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, 2012,
5  *               2013 Free Software Foundation, Inc.
6  *
7  * This file is part of GnuPG.
8  *
9  * GnuPG is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * GnuPG is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, see <http://www.gnu.org/licenses/>.
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
30 #include "util.h"
31 #include "http.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_UNSUPPORTED_PROTOCOL:
49           strcpy(curl->errorbuffer,"unsupported protocol");
50           break;
51
52         case CURLE_COULDNT_CONNECT:
53           strcpy(curl->errorbuffer,"couldn't connect");
54           break;
55
56         case CURLE_WRITE_ERROR:
57           strcpy(curl->errorbuffer,"write error");
58           break;
59
60         case CURLE_HTTP_RETURNED_ERROR:
61           sprintf(curl->errorbuffer,"url returned error %u",curl->status);
62           break;
63
64         default:
65           strcpy(curl->errorbuffer,"generic error");
66           break;
67         }
68
69       if(str && (strlen(curl->errorbuffer)+2+strlen(str)+1)<=CURL_ERROR_SIZE)
70         {
71           strcat(curl->errorbuffer,": ");
72           strcat(curl->errorbuffer,str);
73         }
74     }
75
76   return err;
77 }
78
79 CURLcode
80 curl_global_init(long flags)
81 {
82   (void)flags;
83   return CURLE_OK;
84 }
85
86 void
87 curl_global_cleanup(void) {}
88
89 CURL *
90 curl_easy_init(void)
91 {
92   CURL *handle;
93
94 #ifdef HAVE_W32_SYSTEM
95   w32_init_sockets ();
96 #endif
97
98   handle=calloc(1,sizeof(CURL));
99   if(handle)
100     handle->errors=stderr;
101
102   return handle;
103 }
104
105 void
106 curl_easy_cleanup(CURL *curl)
107 {
108   if (curl)
109     {
110       http_close (curl->hd, 0);
111       free(curl);
112     }
113 }
114
115 CURLcode
116 curl_easy_setopt(CURL *curl,CURLoption option,...)
117 {
118   va_list ap;
119
120   va_start(ap,option);
121
122   switch(option)
123     {
124     case CURLOPT_URL:
125       curl->url=va_arg(ap,char *);
126       break;
127     case CURLOPT_USERPWD:
128       curl->auth=va_arg(ap,char *);
129       break;
130     case CURLOPT_WRITEFUNCTION:
131       curl->writer=va_arg(ap,write_func);
132       break;
133     case CURLOPT_FILE:
134       curl->file=va_arg(ap,void *);
135       break;
136     case CURLOPT_ERRORBUFFER:
137       curl->errorbuffer=va_arg(ap,char *);
138       break;
139     case CURLOPT_PROXY:
140       curl->proxy=va_arg(ap,char *);
141       break;
142     case CURLOPT_POST:
143       curl->flags.post=va_arg(ap,long)?1:0;
144       break;
145     case CURLOPT_POSTFIELDS:
146       curl->postfields=va_arg(ap,char *);
147       break;
148     case CURLOPT_SRVTAG_GPG_HACK:
149       curl->srvtag=va_arg(ap,char *);
150       break;
151     case CURLOPT_FAILONERROR:
152       curl->flags.failonerror=va_arg(ap,long)?1:0;
153       break;
154     case CURLOPT_VERBOSE:
155       curl->flags.verbose=va_arg(ap,long)?1:0;
156       break;
157     case CURLOPT_STDERR:
158       curl->errors=va_arg(ap,FILE *);
159       break;
160     case CURLOPT_HTTPHEADER:
161       curl->headers=va_arg(ap,struct curl_slist *);
162       break;
163     default:
164       /* We ignore the huge majority of curl options */
165       break;
166     }
167
168   va_end(ap);
169
170   return handle_error(curl,CURLE_OK,NULL);
171 }
172
173 CURLcode
174 curl_easy_perform(CURL *curl)
175 {
176   int rc;
177   CURLcode err=CURLE_OK;
178   const char *errstr=NULL;
179   char *proxy=NULL;
180   struct http_srv srv;
181
182   memset(&srv,0,sizeof(srv));
183
184   /* Emulate the libcurl proxy behavior.  If the calling program set a
185      proxy, use it.  If it didn't set a proxy or set it to NULL, check
186      for one in the environment.  If the calling program explicitly
187      set a null-string proxy the http code doesn't use a proxy at
188      all. */
189
190   if(curl->proxy)
191     proxy=curl->proxy;
192   else
193     proxy=getenv(HTTP_PROXY_ENV);
194
195   if(curl->srvtag)
196     srv.srvtag=curl->srvtag;
197
198   if(curl->flags.verbose)
199     {
200       fprintf(curl->errors,"* HTTP proxy is \"%s\"\n",proxy?proxy:"null");
201       fprintf(curl->errors,"* HTTP URL is \"%s\"\n",curl->url);
202       if(srv.srvtag)
203         fprintf(curl->errors,
204                 "* SRV tag is \"%s\": host and port may be overridden\n",
205                 srv.srvtag);
206       fprintf(curl->errors,"* HTTP auth is \"%s\"\n",
207               curl->auth?curl->auth:"null");
208       fprintf(curl->errors,"* HTTP method is %s\n",
209               curl->flags.post?"POST":"GET");
210     }
211
212   if(curl->flags.post)
213     {
214       rc = http_open (&curl->hd, HTTP_REQ_POST, curl->url, curl->auth,
215                       0, proxy, NULL, &srv,
216                       curl->headers?curl->headers->list:NULL);
217       if (!rc)
218         {
219           unsigned int post_len = strlen(curl->postfields);
220
221           if(curl->flags.verbose && srv.used_server && srv.used_port)
222             fprintf (curl->errors, "* HTTP host:port post-SRV is \"%s:%hu\"\n",
223                      srv.used_server, srv.used_port);
224
225           es_fprintf (http_get_write_ptr (curl->hd),
226                       "Content-Type: application/x-www-form-urlencoded\r\n"
227                       "Content-Length: %u\r\n", post_len);
228           http_start_data (curl->hd);
229           es_write (http_get_write_ptr (curl->hd),
230                     curl->postfields, post_len, NULL);
231
232           rc = http_wait_response (curl->hd);
233           curl->status = http_get_status_code (curl->hd);
234           if (!rc && curl->flags.failonerror && curl->status>=300)
235             err = CURLE_HTTP_RETURNED_ERROR;
236           http_close (curl->hd, 0);
237           curl->hd = NULL;
238         }
239     }
240   else
241     {
242       rc = http_open (&curl->hd, HTTP_REQ_GET, curl->url, curl->auth,
243                       0, proxy, NULL, &srv,
244                       curl->headers?curl->headers->list:NULL);
245       if (!rc)
246         {
247           if(curl->flags.verbose && srv.used_server && srv.used_port)
248             fprintf (curl->errors, "* HTTP host:port post-SRV is \"%s:%hu\"\n",
249                      srv.used_server, srv.used_port);
250
251           rc = http_wait_response (curl->hd);
252           curl->status = http_get_status_code (curl->hd);
253           if (!rc)
254             {
255               if (curl->flags.failonerror && curl->status>=300)
256                 err = CURLE_HTTP_RETURNED_ERROR;
257               else
258                 {
259                   size_t maxlen = 1024;
260                   size_t buflen;
261                   unsigned int len;
262                   char *line = NULL;
263
264                   while ((len = es_read_line (http_get_read_ptr (curl->hd),
265                                               &line, &buflen, &maxlen)))
266                     {
267                       size_t ret;
268
269                       maxlen=1024;
270
271                       ret=(curl->writer)(line,len,1,curl->file);
272                       if(ret!=len)
273                         {
274                           err=CURLE_WRITE_ERROR;
275                           break;
276                         }
277                     }
278
279                   es_free (line);
280                   http_close(curl->hd, 0);
281                   curl->hd = NULL;
282                 }
283             }
284           else
285             {
286               http_close (curl->hd, 0);
287               curl->hd = NULL;
288             }
289         }
290     }
291
292   xfree(srv.used_server);
293
294   switch(gpg_err_code (rc))
295     {
296     case 0:
297       break;
298
299     case GPG_ERR_INV_URI:
300       err=CURLE_UNSUPPORTED_PROTOCOL;
301       break;
302
303     default:
304       errstr=gpg_strerror (rc);
305       err=CURLE_COULDNT_CONNECT;
306       break;
307     }
308       
309   return handle_error(curl,err,errstr);
310 }
311
312 CURLcode
313 curl_easy_getinfo(CURL *curl, CURLINFO info, ... )
314 {
315   va_list ap;
316   long *var;
317
318   va_start(ap,info);
319
320   switch(info)
321     {
322     case CURLINFO_RESPONSE_CODE:
323       var=va_arg(ap,long *);
324       *var=curl->status;
325       break;
326     default:
327       break;
328     }
329
330   return handle_error(curl,CURLE_OK,NULL);
331 }
332
333 /* This is not the same exact set that is allowed according to
334    RFC-2396, but it is what the real curl uses. */
335 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
336                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
337                         "0123456789"
338
339 char *
340 curl_escape(char *str,int length)
341 {
342   int len,max,idx,enc_idx=0;
343   char *enc;
344
345   if(length)
346     len=length;
347   else
348     len=strlen(str);
349
350   enc=malloc(len+1);
351   if(!enc)
352     return enc;
353
354   max=len;
355
356   for(idx=0;idx<len;idx++)
357     {
358       if(enc_idx+3>max)
359         {
360           char *tmp;
361
362           max+=100;
363
364           tmp=realloc(enc,max+1);
365           if(!tmp)
366             {
367               free(enc);
368               return NULL;
369             }
370
371           enc=tmp;
372         }
373
374       if(strchr(VALID_URI_CHARS,str[idx]))
375         enc[enc_idx++]=str[idx];
376       else
377         {
378           char numbuf[5];
379           sprintf(numbuf,"%%%02X",str[idx]);
380           strcpy(&enc[enc_idx],numbuf);
381           enc_idx+=3;
382         }
383     }
384
385   enc[enc_idx]='\0';
386
387   return enc;
388 }
389
390 curl_version_info_data *
391 curl_version_info(int type)
392 {
393   static curl_version_info_data data;
394   static const char *protocols[]={"http",NULL};
395
396   (void)type;
397
398   data.protocols=protocols;
399
400   return &data;
401 }
402
403 struct curl_slist *
404 curl_slist_append(struct curl_slist *list,const char *string)
405 {
406   if(!list)
407     {
408       list=calloc(1,sizeof(*list));
409       if(!list)
410         return NULL;
411     }
412
413   add_to_strlist(&list->list,string);
414
415   return list;
416 }
417
418 void
419 curl_slist_free_all(struct curl_slist *list)
420 {
421   if(list)
422     {
423       free_strlist(list->list);
424       free(list);
425     }
426 }