136436a10647ba49110452c04aa0249d90a1cfff
[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,
5  *               2012 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   return handle_error(curl,CURLE_OK,NULL);
169 }
170
171 CURLcode
172 curl_easy_perform(CURL *curl)
173 {
174   int rc;
175   CURLcode err=CURLE_OK;
176   const char *errstr=NULL;
177   char *proxy=NULL;
178   struct http_srv srv;
179
180   memset(&srv,0,sizeof(srv));
181
182   /* Emulate the libcurl proxy behavior.  If the calling program set a
183      proxy, use it.  If it didn't set a proxy or set it to NULL, check
184      for one in the environment.  If the calling program explicitly
185      set a null-string proxy the http code doesn't use a proxy at
186      all. */
187
188   if(curl->proxy)
189     proxy=curl->proxy;
190   else
191     proxy=getenv(HTTP_PROXY_ENV);
192
193   if(curl->srvtag)
194     srv.srvtag=curl->srvtag;
195
196   if(curl->flags.verbose)
197     {
198       fprintf(curl->errors,"* HTTP proxy is \"%s\"\n",proxy?proxy:"null");
199       fprintf(curl->errors,"* HTTP URL is \"%s\"\n",curl->url);
200       if(srv.srvtag)
201         fprintf(curl->errors,
202                 "* SRV tag is \"%s\": host and port may be overridden\n",
203                 srv.srvtag);
204       fprintf(curl->errors,"* HTTP auth is \"%s\"\n",
205               curl->auth?curl->auth:"null");
206       fprintf(curl->errors,"* HTTP method is %s\n",
207               curl->flags.post?"POST":"GET");
208     }
209
210   if(curl->flags.post)
211     {
212       rc = http_open (&curl->hd, HTTP_REQ_POST, curl->url, curl->auth,
213                       0, proxy, NULL, &srv,
214                       curl->headers?curl->headers->list:NULL);
215       if (!rc)
216         {
217           unsigned int post_len = strlen(curl->postfields);
218
219           if(curl->flags.verbose && srv.used_server && srv.used_port)
220             fprintf (curl->errors, "* HTTP host:port post-SRV is \"%s:%hu\"\n",
221                      srv.used_server, srv.used_port);
222
223           es_fprintf (http_get_write_ptr (curl->hd),
224                       "Content-Type: application/x-www-form-urlencoded\r\n"
225                       "Content-Length: %u\r\n", post_len);
226           http_start_data (curl->hd);
227           es_write (http_get_write_ptr (curl->hd),
228                     curl->postfields, post_len, NULL);
229
230           rc = http_wait_response (curl->hd);
231           curl->status = http_get_status_code (curl->hd);
232           if (!rc && curl->flags.failonerror && curl->status>=300)
233             err = CURLE_HTTP_RETURNED_ERROR;
234           http_close (curl->hd, 0);
235           curl->hd = NULL;
236         }
237     }
238   else
239     {
240       rc = http_open (&curl->hd, HTTP_REQ_GET, curl->url, curl->auth,
241                       0, proxy, NULL, &srv,
242                       curl->headers?curl->headers->list:NULL);
243       if (!rc)
244         {
245           if(curl->flags.verbose && srv.used_server && srv.used_port)
246             fprintf (curl->errors, "* HTTP host:port post-SRV is \"%s:%hu\"\n",
247                      srv.used_server, srv.used_port);
248
249           rc = http_wait_response (curl->hd);
250           curl->status = http_get_status_code (curl->hd);
251           if (!rc)
252             {
253               if (curl->flags.failonerror && curl->status>=300)
254                 err = CURLE_HTTP_RETURNED_ERROR;
255               else
256                 {
257                   size_t maxlen = 1024;
258                   size_t buflen;
259                   unsigned int len;
260                   char *line = NULL;
261
262                   while ((len = es_read_line (http_get_read_ptr (curl->hd),
263                                               &line, &buflen, &maxlen)))
264                     {
265                       size_t ret;
266
267                       maxlen=1024;
268
269                       ret=(curl->writer)(line,len,1,curl->file);
270                       if(ret!=len)
271                         {
272                           err=CURLE_WRITE_ERROR;
273                           break;
274                         }
275                     }
276
277                   es_free (line);
278                   http_close(curl->hd, 0);
279                   curl->hd = NULL;
280                 }
281             }
282           else
283             {
284               http_close (curl->hd, 0);
285               curl->hd = NULL;
286             }
287         }
288     }
289
290   xfree(srv.used_server);
291
292   switch(gpg_err_code (rc))
293     {
294     case 0:
295       break;
296
297     case GPG_ERR_INV_URI:
298       err=CURLE_UNSUPPORTED_PROTOCOL;
299       break;
300
301     default:
302       errstr=gpg_strerror (rc);
303       err=CURLE_COULDNT_CONNECT;
304       break;
305     }
306       
307   return handle_error(curl,err,errstr);
308 }
309
310 /* This is not the same exact set that is allowed according to
311    RFC-2396, but it is what the real curl uses. */
312 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
313                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
314                         "0123456789"
315
316 char *
317 curl_escape(char *str,int length)
318 {
319   int len,max,idx,enc_idx=0;
320   char *enc;
321
322   if(length)
323     len=length;
324   else
325     len=strlen(str);
326
327   enc=malloc(len+1);
328   if(!enc)
329     return enc;
330
331   max=len;
332
333   for(idx=0;idx<len;idx++)
334     {
335       if(enc_idx+3>max)
336         {
337           char *tmp;
338
339           max+=100;
340
341           tmp=realloc(enc,max+1);
342           if(!tmp)
343             {
344               free(enc);
345               return NULL;
346             }
347
348           enc=tmp;
349         }
350
351       if(strchr(VALID_URI_CHARS,str[idx]))
352         enc[enc_idx++]=str[idx];
353       else
354         {
355           char numbuf[5];
356           sprintf(numbuf,"%%%02X",str[idx]);
357           strcpy(&enc[enc_idx],numbuf);
358           enc_idx+=3;
359         }
360     }
361
362   enc[enc_idx]='\0';
363
364   return enc;
365 }
366
367 curl_version_info_data *
368 curl_version_info(int type)
369 {
370   static curl_version_info_data data;
371   static const char *protocols[]={"http",NULL};
372
373   (void)type;
374
375   data.protocols=protocols;
376
377   return &data;
378 }
379
380 struct curl_slist *
381 curl_slist_append(struct curl_slist *list,const char *string)
382 {
383   if(!list)
384     {
385       list=calloc(1,sizeof(*list));
386       if(!list)
387         return NULL;
388     }
389
390   add_to_strlist(&list->list,string);
391
392   return list;
393 }
394
395 void
396 curl_slist_free_all(struct curl_slist *list)
397 {
398   if(list)
399     {
400       free_strlist(list->list);
401       free(list);
402     }
403 }