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