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