2006-09-06 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / keyserver / gpgkeys_curl.c
blob9e4b56729993c6d08994d449df0584cd48db0315
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.
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 HAVE_LIBCURL
32 #include <curl/curl.h>
33 #else
34 #include "curl-shim.h"
35 #endif
36 #include "keyserver.h"
37 #include "ksutil.h"
39 extern char *optarg;
40 extern int optind;
42 static FILE *input,*output,*console;
43 static CURL *curl;
44 static struct ks_options *opt;
46 static int
47 get_key(char *getkey)
49 CURLcode res;
50 char errorbuffer[CURL_ERROR_SIZE];
51 char request[MAX_URL];
52 struct curl_writer_ctx ctx;
54 memset(&ctx,0,sizeof(ctx));
56 if(strncmp(getkey,"0x",2)==0)
57 getkey+=2;
59 fprintf(output,"KEY 0x%s BEGIN\n",getkey);
61 sprintf(request,"%s://%s%s%s%s",opt->scheme,opt->host,
62 opt->port?":":"",opt->port?opt->port:"",opt->path?opt->path:"/");
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);
70 res=curl_easy_perform(curl);
71 if(res!=CURLE_OK)
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));
77 else
79 curl_writer_finalize(&ctx);
80 if(!ctx.flags.done)
82 fprintf(console,"gpgkeys: no key data found for %s\n",request);
83 fprintf(output,"\nKEY 0x%s FAILED %d\n",
84 getkey,KEYSERVER_KEY_NOT_FOUND);
86 else
87 fprintf(output,"\nKEY 0x%s END\n",getkey);
90 return curl_err_to_gpg_err(res);
93 static void
94 show_help (FILE *fp)
96 fprintf (fp,"-h\thelp\n");
97 fprintf (fp,"-V\tversion\n");
98 fprintf (fp,"-o\toutput to this file\n");
102 main(int argc,char *argv[])
104 int arg,ret=KEYSERVER_INTERNAL_ERROR;
105 char line[MAX_LINE];
106 char *thekey=NULL;
107 long follow_redirects=5;
108 char *proxy=NULL;
110 console=stderr;
112 /* Kludge to implement standard GNU options. */
113 if (argc > 1 && !strcmp (argv[1], "--version"))
115 fputs ("gpgkeys_curl (GnuPG) " VERSION"\n", stdout);
116 return 0;
118 else if (argc > 1 && !strcmp (argv[1], "--help"))
120 show_help (stdout);
121 return 0;
124 while((arg=getopt(argc,argv,"hVo:"))!=-1)
125 switch(arg)
127 default:
128 case 'h':
129 show_help (console);
130 return KEYSERVER_OK;
132 case 'V':
133 fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
134 return KEYSERVER_OK;
136 case 'o':
137 output=fopen(optarg,"wb");
138 if(output==NULL)
140 fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
141 optarg,strerror(errno));
142 return KEYSERVER_INTERNAL_ERROR;
145 break;
148 if(argc>optind)
150 input=fopen(argv[optind],"r");
151 if(input==NULL)
153 fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
154 argv[optind],strerror(errno));
155 return KEYSERVER_INTERNAL_ERROR;
159 if(input==NULL)
160 input=stdin;
162 if(output==NULL)
163 output=stdout;
165 opt=init_ks_options();
166 if(!opt)
167 return KEYSERVER_NO_MEMORY;
169 /* Get the command and info block */
171 while(fgets(line,MAX_LINE,input)!=NULL)
173 int err;
174 char option[MAX_OPTION+1];
176 if(line[0]=='\n')
177 break;
179 err=parse_ks_options(line,opt);
180 if(err>0)
182 ret=err;
183 goto fail;
185 else if(err==0)
186 continue;
188 if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
190 int no=0;
191 char *start=&option[0];
193 option[MAX_OPTION]='\0';
195 if(strncasecmp(option,"no-",3)==0)
197 no=1;
198 start=&option[3];
201 if(strncasecmp(start,"http-proxy",10)==0)
203 /* Safe to not check the return code of strdup() here.
204 If it fails, we simply won't use a proxy. */
205 if(no)
207 free(proxy);
208 proxy=strdup("");
210 else if(start[10]=='=')
212 if(strlen(&start[11])<MAX_PROXY)
214 free(proxy);
215 proxy=strdup(&start[11]);
219 else if(strncasecmp(start,"follow-redirects",16)==0)
221 if(no)
222 follow_redirects=0;
223 else if(start[16]=='=')
224 follow_redirects=atoi(&start[17]);
225 else if(start[16]=='\0')
226 follow_redirects=-1;
229 continue;
233 if(!opt->scheme)
235 fprintf(console,"gpgkeys: no scheme supplied!\n");
236 ret=KEYSERVER_SCHEME_NOT_FOUND;
237 goto fail;
240 if(!opt->host)
242 fprintf(console,"gpgkeys: no keyserver host provided\n");
243 goto fail;
246 if(opt->timeout && register_timeout()==-1)
248 fprintf(console,"gpgkeys: unable to register timeout handler\n");
249 return KEYSERVER_INTERNAL_ERROR;
252 curl_global_init(CURL_GLOBAL_DEFAULT);
253 curl=curl_easy_init();
254 if(!curl)
256 fprintf(console,"gpgkeys: unable to initialize curl\n");
257 ret=KEYSERVER_INTERNAL_ERROR;
258 goto fail;
261 if(follow_redirects)
263 curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1);
264 if(follow_redirects>0)
265 curl_easy_setopt(curl,CURLOPT_MAXREDIRS,follow_redirects);
268 if(opt->auth)
269 curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
271 if(opt->debug)
273 fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
274 curl_easy_setopt(curl,CURLOPT_STDERR,console);
275 curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
278 curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,opt->flags.check_cert);
279 curl_easy_setopt(curl,CURLOPT_CAINFO,opt->ca_cert_file);
281 if(proxy)
282 curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
284 /* If it's a GET or a SEARCH, the next thing to come in is the
285 keyids. If it's a SEND, then there are no keyids. */
287 if(opt->action==KS_GET)
289 /* Eat the rest of the file */
290 for(;;)
292 if(fgets(line,MAX_LINE,input)==NULL)
293 break;
294 else
296 if(line[0]=='\n' || line[0]=='\0')
297 break;
299 if(!thekey)
301 thekey=strdup(line);
302 if(!thekey)
304 fprintf(console,"gpgkeys: out of memory while "
305 "building key list\n");
306 ret=KEYSERVER_NO_MEMORY;
307 goto fail;
310 /* Trim the trailing \n */
311 thekey[strlen(line)-1]='\0';
316 else
318 fprintf(console,
319 "gpgkeys: this keyserver type only supports key retrieval\n");
320 goto fail;
323 if(!thekey)
325 fprintf(console,"gpgkeys: invalid keyserver instructions\n");
326 goto fail;
329 /* Send the response */
331 fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
332 fprintf(output,"PROGRAM %s\n\n",VERSION);
334 if(opt->verbose)
336 fprintf(console,"Scheme:\t\t%s\n",opt->scheme);
337 fprintf(console,"Host:\t\t%s\n",opt->host);
338 if(opt->port)
339 fprintf(console,"Port:\t\t%s\n",opt->port);
340 if(opt->path)
341 fprintf(console,"Path:\t\t%s\n",opt->path);
342 fprintf(console,"Command:\tGET\n");
345 set_timeout(opt->timeout);
347 ret=get_key(thekey);
349 fail:
351 free(thekey);
353 if(input!=stdin)
354 fclose(input);
356 if(output!=stdout)
357 fclose(output);
359 free_ks_options(opt);
361 if(curl)
362 curl_easy_cleanup(curl);
364 free(proxy);
366 curl_global_cleanup();
368 return ret;