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