Adjust to GNU coding standards
[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 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 #include "http.h"
29 #include "util.h"
30 #include "ksutil.h"
31 #include "curl-shim.h"
32
33 static CURLcode
34 handle_error(CURL *curl,CURLcode err,const char *str)
35 {
36   if(curl->errorbuffer)
37     {
38       /* Make sure you never exceed CURL_ERROR_SIZE, currently set to
39          256 in curl-shim.h */
40       switch(err)
41         {
42         case CURLE_OK:
43           strcpy(curl->errorbuffer,"okay");
44           break;
45
46         case CURLE_UNSUPPORTED_PROTOCOL:
47           strcpy(curl->errorbuffer,"unsupported protocol");
48           break;
49
50         case CURLE_COULDNT_CONNECT:
51           strcpy(curl->errorbuffer,"couldn't connect");
52           break;
53
54         case CURLE_WRITE_ERROR:
55           strcpy(curl->errorbuffer,"write error");
56           break;
57
58         case CURLE_HTTP_RETURNED_ERROR:
59           sprintf(curl->errorbuffer,"url returned error %u",curl->status);
60           break;
61
62         default:
63           strcpy(curl->errorbuffer,"generic error");
64           break;
65         }
66
67       if(str && (strlen(curl->errorbuffer)+2+strlen(str)+1)<=CURL_ERROR_SIZE)
68         {
69           strcat(curl->errorbuffer,": ");
70           strcat(curl->errorbuffer,str);
71         }
72     }
73
74   return err;
75 }
76
77 CURLcode
78 curl_global_init(long flags)
79 {
80   return CURLE_OK;
81 }
82
83 void
84 curl_global_cleanup(void) {}
85
86 CURL *
87 curl_easy_init(void)
88 {
89   CURL *handle;
90
91   handle=calloc(1,sizeof(CURL));
92   if(handle)
93     handle->errors=stderr;
94
95   return handle;
96 }
97
98 void
99 curl_easy_cleanup(CURL *curl)
100 {
101   free(curl);
102 }
103
104 CURLcode
105 curl_easy_setopt(CURL *curl,CURLoption option,...)
106 {
107   va_list ap;
108
109   va_start(ap,option);
110
111   switch(option)
112     {
113     case CURLOPT_URL:
114       curl->url=va_arg(ap,char *);
115       break;
116     case CURLOPT_USERPWD:
117       curl->auth=va_arg(ap,char *);
118       break;
119     case CURLOPT_WRITEFUNCTION:
120       curl->writer=va_arg(ap,write_func);
121       break;
122     case CURLOPT_FILE:
123       curl->file=va_arg(ap,void *);
124       break;
125     case CURLOPT_ERRORBUFFER:
126       curl->errorbuffer=va_arg(ap,char *);
127       break;
128     case CURLOPT_PROXY:
129       curl->proxy=va_arg(ap,char *);
130       break;
131     case CURLOPT_POST:
132       curl->flags.post=va_arg(ap,long)?1:0;
133       break;
134     case CURLOPT_POSTFIELDS:
135       curl->postfields=va_arg(ap,char *);
136       break;
137     case CURLOPT_SRVTAG_GPG_HACK:
138       curl->srvtag=va_arg(ap,char *);
139       break;
140     case CURLOPT_FAILONERROR:
141       curl->flags.failonerror=va_arg(ap,long)?1:0;
142       break;
143     case CURLOPT_VERBOSE:
144       curl->flags.verbose=va_arg(ap,long)?1:0;
145       break;
146     case CURLOPT_STDERR:
147       curl->errors=va_arg(ap,FILE *);
148       break;
149     case CURLOPT_HTTPHEADER:
150       curl->headers=va_arg(ap,struct curl_slist *);
151       break;
152     default:
153       /* We ignore the huge majority of curl options */
154       break;
155     }
156
157   return handle_error(curl,CURLE_OK,NULL);
158 }
159
160 CURLcode
161 curl_easy_perform(CURL *curl)
162 {
163   int rc;
164   CURLcode err=CURLE_OK;
165   const char *errstr=NULL;
166   char *proxy=NULL;
167   struct http_srv srv;
168
169   memset(&srv,0,sizeof(srv));
170
171   /* Emulate the libcurl proxy behavior.  If the calling program set a
172      proxy, use it.  If it didn't set a proxy or set it to NULL, check
173      for one in the environment.  If the calling program explicitly
174      set a null-string proxy the http code doesn't use a proxy at
175      all. */
176
177   if(curl->proxy)
178     proxy=curl->proxy;
179   else
180     proxy=getenv(HTTP_PROXY_ENV);
181
182   if(curl->srvtag)
183     srv.srvtag=curl->srvtag;
184
185   if(curl->flags.verbose)
186     {
187       fprintf(curl->errors,"* HTTP proxy is \"%s\"\n",proxy?proxy:"null");
188       fprintf(curl->errors,"* HTTP URL is \"%s\"\n",curl->url);
189       if(srv.srvtag)
190         fprintf(curl->errors,
191                 "* SRV tag is \"%s\": host and port may be overridden\n",
192                 srv.srvtag);
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,curl->auth,0,proxy,
202                    &srv,curl->headers?curl->headers->list:NULL);
203       if(rc==0)
204         {
205           char content_len[50];
206           unsigned int post_len=strlen(curl->postfields);
207
208           if(curl->flags.verbose && srv.used_server && srv.used_port)
209             fprintf (curl->errors, "* HTTP host:port post-SRV is \"%s:%hu\"\n",
210                      srv.used_server, srv.used_port);
211
212           iobuf_writestr(curl->hd.fp_write,
213                          "Content-Type: application/x-www-form-urlencoded\r\n");
214           sprintf(content_len,"Content-Length: %u\r\n",post_len);
215
216           iobuf_writestr(curl->hd.fp_write,content_len);
217
218           http_start_data(&curl->hd);
219           iobuf_write(curl->hd.fp_write,curl->postfields,post_len);
220           rc=http_wait_response(&curl->hd,&curl->status);
221           if(rc==0 && curl->flags.failonerror && curl->status>=300)
222             err=CURLE_HTTP_RETURNED_ERROR;
223         }
224     }
225   else
226     {
227       rc=http_open(&curl->hd,HTTP_REQ_GET,curl->url,curl->auth,0,proxy,
228                    &srv,curl->headers?curl->headers->list:NULL);
229       if(rc==0)
230         {
231           if(curl->flags.verbose && srv.used_server && srv.used_port)
232             fprintf (curl->errors, "* HTTP host:port post-SRV is \"%s:%hu\"\n",
233                      srv.used_server, srv.used_port);
234
235           rc=http_wait_response(&curl->hd,&curl->status);
236           if(rc==0)
237             {
238               if(curl->flags.failonerror && curl->status>=300)
239                 err=CURLE_HTTP_RETURNED_ERROR;
240               else
241                 {
242                   unsigned int maxlen=1024,buflen,len;
243                   byte *line=NULL;
244
245                   while((len=iobuf_read_line(curl->hd.fp_read,
246                                              &line,&buflen,&maxlen)))
247                     {
248                       size_t ret;
249
250                       maxlen=1024;
251
252                       ret=(curl->writer)(line,len,1,curl->file);
253                       if(ret!=len)
254                         {
255                           err=CURLE_WRITE_ERROR;
256                           break;
257                         }
258                     }
259
260                   xfree(line);
261                   http_close(&curl->hd);
262                 }
263             }
264           else
265             http_close(&curl->hd);
266         }
267     }
268
269   free (srv.used_server);
270
271   switch(rc)
272     {
273     case 0:
274       break;
275
276     case G10ERR_INVALID_URI:
277       err=CURLE_UNSUPPORTED_PROTOCOL;
278       break;
279
280     case G10ERR_NETWORK:
281       errstr=strerror(errno);
282       err=CURLE_COULDNT_CONNECT;
283       break;
284
285     default:
286       errstr=g10_errstr(rc);
287       err=CURLE_COULDNT_CONNECT;
288       break;
289     }
290       
291   return handle_error(curl,err,errstr);
292 }
293
294 /* This is not the same exact set that is allowed according to
295    RFC-2396, but it is what the real curl uses. */
296 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
297                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
298                         "0123456789"
299
300 char *
301 curl_easy_escape(CURL *curl,char *str,int length)
302 {
303   int len,max,idx,enc_idx=0;
304   char *enc;
305
306   if(length)
307     len=length;
308   else
309     len=strlen(str);
310
311   enc = xtrymalloc(len+1);
312   if(!enc)
313     return enc;
314
315   max=len;
316
317   for(idx=0;idx<len;idx++)
318     {
319       if(enc_idx+3>max)
320         {
321           char *tmp;
322
323           max+=100;
324
325           tmp=realloc(enc,max+1);
326           if(!tmp)
327             {
328               free(enc);
329               return NULL;
330             }
331
332           enc=tmp;
333         }
334
335       if(strchr(VALID_URI_CHARS,str[idx]))
336         enc[enc_idx++]=str[idx];
337       else
338         {
339           char numbuf[5];
340           sprintf(numbuf,"%%%02X",(unsigned char)str[idx]);
341           strcpy(&enc[enc_idx],numbuf);
342           enc_idx+=3;
343         }
344     }
345
346   enc[enc_idx]='\0';
347
348   return enc;
349 }
350
351 curl_version_info_data *
352 curl_version_info(int type)
353 {
354   static curl_version_info_data data;
355   static const char *protocols[]={"http",NULL};
356
357   data.protocols=protocols;
358
359   return &data;
360 }
361
362 struct curl_slist *
363 curl_slist_append(struct curl_slist *list,const char *string)
364 {
365   if(!list)
366     {
367       list=calloc(1,sizeof(*list));
368       if(!list)
369         return NULL;
370     }
371
372   add_to_strlist(&list->list,string);
373
374   return list;
375 }
376
377 void
378 curl_slist_free_all(struct curl_slist *list)
379 {
380   if(list)
381     {
382       free_strlist(list->list);
383       free(list);
384     }
385 }