* gpgkeys_curl.c (main): If the http-proxy option is given without any
[gnupg.git] / keyserver / gpgkeys_curl.c
1 /* gpgkeys_curl.c - fetch a key via libcurl
2  * Copyright (C) 2004, 2005 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #ifdef HAVE_GETOPT_H
28 #include <getopt.h>
29 #endif
30 #ifdef FAKE_CURL
31 #include "curl-shim.h"
32 #else
33 #include <curl/curl.h>
34 #endif
35 #include "keyserver.h"
36 #include "ksutil.h"
37
38 extern char *optarg;
39 extern int optind;
40
41 static FILE *input,*output,*console;
42 static CURL *curl;
43 static struct ks_options *opt;
44
45 static int
46 get_key(char *getkey)
47 {
48   CURLcode res;
49   char errorbuffer[CURL_ERROR_SIZE];
50   char request[MAX_URL];
51
52   if(strncmp(getkey,"0x",2)==0)
53     getkey+=2;
54
55   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
56
57   sprintf(request,"%s://%s%s%s%s%s%s",opt->scheme,
58           opt->auth?opt->auth:"",
59           opt->auth?"@":"",opt->host,
60           opt->port?":":"",opt->port?opt->port:"",
61           opt->path?opt->path:"/");
62
63   curl_easy_setopt(curl,CURLOPT_URL,request);
64   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
65   curl_easy_setopt(curl,CURLOPT_FILE,output);
66   curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
67
68   res=curl_easy_perform(curl);
69   if(res!=0)
70     {
71       fprintf(console,"gpgkeys: %s fetch error %d: %s\n",opt->scheme,
72               res,errorbuffer);
73       fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
74     }
75   else
76     fprintf(output,"\nKEY 0x%s END\n",getkey);
77
78   return KEYSERVER_OK;
79 }
80
81 static void 
82 show_help (FILE *fp)
83 {
84   fprintf (fp,"-h\thelp\n");
85   fprintf (fp,"-V\tversion\n");
86   fprintf (fp,"-o\toutput to this file\n");
87 }
88
89 int
90 main(int argc,char *argv[])
91 {
92   int arg,ret=KEYSERVER_INTERNAL_ERROR;
93   char line[MAX_LINE];
94   char *thekey=NULL;
95   long follow_redirects=5;
96   char proxy[MAX_PROXY+1];
97
98   console=stderr;
99
100   /* Kludge to implement standard GNU options.  */
101   if (argc > 1 && !strcmp (argv[1], "--version"))
102     {
103       fputs ("gpgkeys_curl (GnuPG) " VERSION"\n", stdout);
104       return 0;
105     }
106   else if (argc > 1 && !strcmp (argv[1], "--help"))
107     {
108       show_help (stdout);
109       return 0;
110     }
111
112   while((arg=getopt(argc,argv,"hVo:"))!=-1)
113     switch(arg)
114       {
115       default:
116       case 'h':
117         show_help (console);
118         return KEYSERVER_OK;
119
120       case 'V':
121         fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
122         return KEYSERVER_OK;
123
124       case 'o':
125         output=fopen(optarg,"wb");
126         if(output==NULL)
127           {
128             fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
129                     optarg,strerror(errno));
130             return KEYSERVER_INTERNAL_ERROR;
131           }
132
133         break;
134       }
135
136   if(argc>optind)
137     {
138       input=fopen(argv[optind],"r");
139       if(input==NULL)
140         {
141           fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
142                   argv[optind],strerror(errno));
143           return KEYSERVER_INTERNAL_ERROR;
144         }
145     }
146
147   if(input==NULL)
148     input=stdin;
149
150   if(output==NULL)
151     output=stdout;
152
153   opt=init_ks_options();
154   if(!opt)
155     return KEYSERVER_NO_MEMORY;
156
157   /* Get the command and info block */
158
159   while(fgets(line,MAX_LINE,input)!=NULL)
160     {
161       int err;
162       char option[MAX_OPTION+1];
163
164       if(line[0]=='\n')
165         break;
166
167       err=parse_ks_options(line,opt);
168       if(err>0)
169         {
170           ret=err;
171           goto fail;
172         }
173       else if(err==0)
174         continue;
175
176       if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
177         {
178           int no=0;
179           char *start=&option[0];
180
181           option[MAX_OPTION]='\0';
182
183           if(strncasecmp(option,"no-",3)==0)
184             {
185               no=1;
186               start=&option[3];
187             }
188
189           if(strncasecmp(start,"http-proxy",10)==0)
190             {
191               if(no)
192                 proxy[0]='\0';
193               else if(start[10]=='=')
194                 {
195                   strncpy(proxy,&start[11],MAX_PROXY);
196                   proxy[MAX_PROXY]='\0';
197                 }
198               else if(start[10]=='\0')
199                 {
200                   char *http_proxy=getenv(HTTP_PROXY_ENV);
201                   if(http_proxy)
202                     {
203                       strncpy(proxy,http_proxy,MAX_PROXY);
204                       proxy[MAX_PROXY]='\0';
205                     }
206                 }
207             }
208           else if(strncasecmp(start,"follow-redirects",16)==0)
209             {
210               if(no)
211                 follow_redirects=0;
212               else if(start[16]=='=')
213                 follow_redirects=atoi(&start[17]);
214               else if(start[16]=='\0')
215                 follow_redirects=-1;
216             }
217
218           continue;
219         }
220     }
221
222   if(!opt->scheme)
223     {
224       fprintf(console,"gpgkeys: no scheme supplied!\n");
225       ret=KEYSERVER_SCHEME_NOT_FOUND;
226       goto fail;
227     }
228 #ifdef HTTP_VIA_LIBCURL
229   else if(strcasecmp(opt->scheme,"http")==0)
230     ;
231 #endif /* HTTP_VIA_LIBCURL */
232 #ifdef HTTPS_VIA_LIBCURL
233   else if(strcasecmp(opt->scheme,"https")==0)
234     ;
235 #endif /* HTTP_VIA_LIBCURL */
236 #ifdef FTP_VIA_LIBCURL
237   else if(strcasecmp(opt->scheme,"ftp")==0)
238     ;
239 #endif /* FTP_VIA_LIBCURL */
240 #ifdef FTPS_VIA_LIBCURL
241   else if(strcasecmp(opt->scheme,"ftps")==0)
242     ;
243 #endif /* FTPS_VIA_LIBCURL */
244   else
245     {
246       fprintf(console,"gpgkeys: scheme `%s' not supported\n",opt->scheme);
247       return KEYSERVER_SCHEME_NOT_FOUND;
248     }
249
250   if(!opt->host)
251     {
252       fprintf(console,"gpgkeys: no keyserver host provided\n");
253       goto fail;
254     }
255
256   if(opt->timeout && register_timeout()==-1)
257     {
258       fprintf(console,"gpgkeys: unable to register timeout handler\n");
259       return KEYSERVER_INTERNAL_ERROR;
260     }
261
262   curl_global_init(CURL_GLOBAL_DEFAULT);
263   curl=curl_easy_init();
264   if(!curl)
265     {
266       fprintf(console,"gpgkeys: unable to initialize curl\n");
267       ret=KEYSERVER_INTERNAL_ERROR;
268       goto fail;
269     }
270
271   if(follow_redirects)
272     {
273       curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1);
274       if(follow_redirects>0)
275         curl_easy_setopt(curl,CURLOPT_MAXREDIRS,follow_redirects);
276     }
277
278   if(opt->debug)
279     {
280       curl_easy_setopt(curl,CURLOPT_STDERR,console);
281       curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
282     }
283
284   curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,opt->flags.check_cert);
285   curl_easy_setopt(curl,CURLOPT_CAINFO,opt->ca_cert_file);
286
287   if(proxy[0])
288     curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
289
290   /* If it's a GET or a SEARCH, the next thing to come in is the
291      keyids.  If it's a SEND, then there are no keyids. */
292
293   if(opt->action==KS_GET)
294     {
295       /* Eat the rest of the file */
296       for(;;)
297         {
298           if(fgets(line,MAX_LINE,input)==NULL)
299             break;
300           else
301             {
302               if(line[0]=='\n' || line[0]=='\0')
303                 break;
304
305               if(!thekey)
306                 {
307                   thekey=strdup(line);
308                   if(!thekey)
309                     {
310                       fprintf(console,"gpgkeys: out of memory while "
311                               "building key list\n");
312                       ret=KEYSERVER_NO_MEMORY;
313                       goto fail;
314                     }
315
316                   /* Trim the trailing \n */
317                   thekey[strlen(line)-1]='\0';
318                 }
319             }
320         }
321     }
322   else
323     {
324       fprintf(console,
325               "gpgkeys: this keyserver type only supports key retrieval\n");
326       goto fail;
327     }
328
329   if(!thekey)
330     {
331       fprintf(console,"gpgkeys: invalid keyserver instructions\n");
332       goto fail;
333     }
334
335   /* Send the response */
336
337   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
338   fprintf(output,"PROGRAM %s\n\n",VERSION);
339
340   if(opt->verbose)
341     {
342       fprintf(console,"Scheme:\t\t%s\n",opt->scheme);
343       fprintf(console,"Host:\t\t%s\n",opt->host);
344       if(opt->port)
345         fprintf(console,"Port:\t\t%s\n",opt->port);
346       if(opt->path)
347         fprintf(console,"Path:\t\t%s\n",opt->path);
348       fprintf(console,"Command:\tGET\n");
349     }
350
351   set_timeout(opt->timeout);
352
353   ret=get_key(thekey);
354
355  fail:
356
357   free(thekey);
358
359   if(input!=stdin)
360     fclose(input);
361
362   if(output!=stdout)
363     fclose(output);
364
365   free_ks_options(opt);
366
367   if(curl)
368     curl_easy_cleanup(curl);
369
370   curl_global_cleanup();
371
372   return ret;
373 }