2006-12-21 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / keyserver / gpgkeys_curl.c
blob192f9ba669c18cc4bd1d8c03c357ddd480c69b9b
1 /* gpgkeys_curl.c - fetch a key via libcurl
2 * Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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.
21 * In addition, as a special exception, the Free Software Foundation
22 * gives permission to link the code of the keyserver helper tools:
23 * gpgkeys_ldap, gpgkeys_curl and gpgkeys_hkp with the OpenSSL
24 * project's "OpenSSL" library (or with modified versions of it that
25 * use the same license as the "OpenSSL" library), and distribute the
26 * linked executables. You must obey the GNU General Public License
27 * in all respects for all of the code used other than "OpenSSL". If
28 * you modify this file, you may extend this exception to your version
29 * of the file, but you are not obligated to do so. If you do not
30 * wish to do so, delete this exception statement from your version.
33 #include <config.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #ifdef HAVE_GETOPT_H
40 #include <getopt.h>
41 #endif
42 #ifdef HAVE_LIBCURL
43 #include <curl/curl.h>
44 #else
45 #include "curl-shim.h"
46 #endif
47 #include "keyserver.h"
48 #include "ksutil.h"
50 extern char *optarg;
51 extern int optind;
53 static FILE *input,*output,*console;
54 static CURL *curl;
55 static struct ks_options *opt;
57 static int
58 get_key(char *getkey)
60 CURLcode res;
61 char errorbuffer[CURL_ERROR_SIZE];
62 char request[MAX_URL];
63 struct curl_writer_ctx ctx;
65 memset(&ctx,0,sizeof(ctx));
67 if(strncmp(getkey,"0x",2)==0)
68 getkey+=2;
70 fprintf(output,"KEY 0x%s BEGIN\n",getkey);
72 sprintf(request,"%s://%s%s%s%s",opt->scheme,opt->host,
73 opt->port?":":"",opt->port?opt->port:"",opt->path?opt->path:"/");
75 curl_easy_setopt(curl,CURLOPT_URL,request);
76 curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
77 ctx.stream=output;
78 curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
79 curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
81 res=curl_easy_perform(curl);
82 if(res!=CURLE_OK)
84 fprintf(console,"gpgkeys: %s fetch error %d: %s\n",opt->scheme,
85 res,errorbuffer);
86 fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
88 else
90 curl_writer_finalize(&ctx);
91 if(!ctx.flags.done)
93 fprintf(console,"gpgkeys: no key data found for %s\n",request);
94 fprintf(output,"\nKEY 0x%s FAILED %d\n",
95 getkey,KEYSERVER_KEY_NOT_FOUND);
97 else
98 fprintf(output,"\nKEY 0x%s END\n",getkey);
101 return curl_err_to_gpg_err(res);
104 static void
105 show_help (FILE *fp)
107 fprintf (fp,"-h\thelp\n");
108 fprintf (fp,"-V\tversion\n");
109 fprintf (fp,"-o\toutput to this file\n");
113 main(int argc,char *argv[])
115 int arg,ret=KEYSERVER_INTERNAL_ERROR;
116 char line[MAX_LINE];
117 char *thekey=NULL;
118 long follow_redirects=5;
119 char *proxy=NULL;
121 console=stderr;
123 /* Kludge to implement standard GNU options. */
124 if (argc > 1 && !strcmp (argv[1], "--version"))
126 fputs ("gpgkeys_curl (GnuPG) " VERSION"\n", stdout);
127 return 0;
129 else if (argc > 1 && !strcmp (argv[1], "--help"))
131 show_help (stdout);
132 return 0;
135 while((arg=getopt(argc,argv,"hVo:"))!=-1)
136 switch(arg)
138 default:
139 case 'h':
140 show_help (console);
141 return KEYSERVER_OK;
143 case 'V':
144 fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
145 return KEYSERVER_OK;
147 case 'o':
148 output=fopen(optarg,"wb");
149 if(output==NULL)
151 fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
152 optarg,strerror(errno));
153 return KEYSERVER_INTERNAL_ERROR;
156 break;
159 if(argc>optind)
161 input=fopen(argv[optind],"r");
162 if(input==NULL)
164 fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
165 argv[optind],strerror(errno));
166 return KEYSERVER_INTERNAL_ERROR;
170 if(input==NULL)
171 input=stdin;
173 if(output==NULL)
174 output=stdout;
176 opt=init_ks_options();
177 if(!opt)
178 return KEYSERVER_NO_MEMORY;
180 /* Get the command and info block */
182 while(fgets(line,MAX_LINE,input)!=NULL)
184 int err;
185 char option[MAX_OPTION+1];
187 if(line[0]=='\n')
188 break;
190 err=parse_ks_options(line,opt);
191 if(err>0)
193 ret=err;
194 goto fail;
196 else if(err==0)
197 continue;
199 if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
201 int no=0;
202 char *start=&option[0];
204 option[MAX_OPTION]='\0';
206 if(strncasecmp(option,"no-",3)==0)
208 no=1;
209 start=&option[3];
212 if(strncasecmp(start,"http-proxy",10)==0)
214 /* Safe to not check the return code of strdup() here.
215 If it fails, we simply won't use a proxy. */
216 if(no)
218 free(proxy);
219 proxy=strdup("");
221 else if(start[10]=='=')
223 if(strlen(&start[11])<MAX_PROXY)
225 free(proxy);
226 proxy=strdup(&start[11]);
230 else if(strncasecmp(start,"follow-redirects",16)==0)
232 if(no)
233 follow_redirects=0;
234 else if(start[16]=='=')
235 follow_redirects=atoi(&start[17]);
236 else if(start[16]=='\0')
237 follow_redirects=-1;
240 continue;
244 if(!opt->scheme)
246 fprintf(console,"gpgkeys: no scheme supplied!\n");
247 ret=KEYSERVER_SCHEME_NOT_FOUND;
248 goto fail;
251 if(!opt->host)
253 fprintf(console,"gpgkeys: no keyserver host provided\n");
254 goto fail;
257 if(opt->timeout && register_timeout()==-1)
259 fprintf(console,"gpgkeys: unable to register timeout handler\n");
260 return KEYSERVER_INTERNAL_ERROR;
263 curl_global_init(CURL_GLOBAL_DEFAULT);
264 curl=curl_easy_init();
265 if(!curl)
267 fprintf(console,"gpgkeys: unable to initialize curl\n");
268 ret=KEYSERVER_INTERNAL_ERROR;
269 goto fail;
272 if(follow_redirects)
274 curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1);
275 if(follow_redirects>0)
276 curl_easy_setopt(curl,CURLOPT_MAXREDIRS,follow_redirects);
279 if(opt->auth)
280 curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
282 if(opt->debug)
284 fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
285 curl_easy_setopt(curl,CURLOPT_STDERR,console);
286 curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
289 curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,opt->flags.check_cert);
290 curl_easy_setopt(curl,CURLOPT_CAINFO,opt->ca_cert_file);
292 if(proxy)
293 curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
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. */
298 if(opt->action==KS_GET)
300 /* Eat the rest of the file */
301 for(;;)
303 if(fgets(line,MAX_LINE,input)==NULL)
304 break;
305 else
307 if(line[0]=='\n' || line[0]=='\0')
308 break;
310 if(!thekey)
312 thekey=strdup(line);
313 if(!thekey)
315 fprintf(console,"gpgkeys: out of memory while "
316 "building key list\n");
317 ret=KEYSERVER_NO_MEMORY;
318 goto fail;
321 /* Trim the trailing \n */
322 thekey[strlen(line)-1]='\0';
327 else
329 fprintf(console,
330 "gpgkeys: this keyserver type only supports key retrieval\n");
331 goto fail;
334 if(!thekey)
336 fprintf(console,"gpgkeys: invalid keyserver instructions\n");
337 goto fail;
340 /* Send the response */
342 fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
343 fprintf(output,"PROGRAM %s\n\n",VERSION);
345 if(opt->verbose)
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");
356 set_timeout(opt->timeout);
358 ret=get_key(thekey);
360 fail:
362 free(thekey);
364 if(input!=stdin)
365 fclose(input);
367 if(output!=stdout)
368 fclose(output);
370 free_ks_options(opt);
372 if(curl)
373 curl_easy_cleanup(curl);
375 free(proxy);
377 curl_global_cleanup();
379 return ret;