agent/
[gnupg.git] / keyserver / gpgkeys_curl.c
blob28ec6982265badebf7a741525a58511d914653f3
1 /* gpgkeys_curl.c - fetch a key via libcurl
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 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 3 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, see <http://www.gnu.org/licenses/>.
19 * In addition, as a special exception, the Free Software Foundation
20 * gives permission to link the code of the keyserver helper tools:
21 * gpgkeys_ldap, gpgkeys_curl and gpgkeys_hkp with the OpenSSL
22 * project's "OpenSSL" library (or with modified versions of it that
23 * use the same license as the "OpenSSL" library), and distribute the
24 * linked executables. You must obey the GNU General Public License
25 * in all respects for all of the code used other than "OpenSSL". If
26 * you modify this file, you may extend this exception to your version
27 * of the file, but you are not obligated to do so. If you do not
28 * wish to do so, delete this exception statement from your version.
31 #include <config.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #ifdef HAVE_GETOPT_H
38 #include <getopt.h>
39 #endif
40 #ifdef HAVE_LIBCURL
41 #include <curl/curl.h>
42 #else
43 #include "curl-shim.h"
44 #endif
45 #include "keyserver.h"
46 #include "ksutil.h"
48 extern char *optarg;
49 extern int optind;
51 static FILE *input,*output,*console;
52 static CURL *curl;
53 static struct ks_options *opt;
55 static int
56 get_key(char *getkey)
58 CURLcode res;
59 char errorbuffer[CURL_ERROR_SIZE];
60 char request[MAX_URL];
61 struct curl_writer_ctx ctx;
63 memset(&ctx,0,sizeof(ctx));
65 if(strncmp(getkey,"0x",2)==0)
66 getkey+=2;
68 fprintf(output,"KEY 0x%s BEGIN\n",getkey);
70 sprintf(request,"%s://%s%s%s%s",opt->scheme,opt->host,
71 opt->port?":":"",opt->port?opt->port:"",opt->path?opt->path:"/");
73 curl_easy_setopt(curl,CURLOPT_URL,request);
74 curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
75 ctx.stream=output;
76 curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
77 curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
79 res=curl_easy_perform(curl);
80 if(res!=CURLE_OK)
82 fprintf(console,"gpgkeys: %s fetch error %d: %s\n",opt->scheme,
83 res,errorbuffer);
84 fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
86 else
88 curl_writer_finalize(&ctx);
89 if(!ctx.flags.done)
91 fprintf(console,"gpgkeys: no key data found for %s\n",request);
92 fprintf(output,"\nKEY 0x%s FAILED %d\n",
93 getkey,KEYSERVER_KEY_NOT_FOUND);
95 else
96 fprintf(output,"\nKEY 0x%s END\n",getkey);
99 return curl_err_to_gpg_err(res);
102 static void
103 show_help (FILE *fp)
105 fprintf (fp,"-h, --help\thelp\n");
106 fprintf (fp,"-V\t\tmachine readable version\n");
107 fprintf (fp,"--version\thuman readable version\n");
108 fprintf (fp,"-o\t\toutput to this file\n");
112 main(int argc,char *argv[])
114 int arg,ret=KEYSERVER_INTERNAL_ERROR,i;
115 char line[MAX_LINE];
116 char *thekey=NULL;
117 long follow_redirects=5;
118 char *proxy=NULL;
119 curl_version_info_data *curldata;
120 struct curl_slist *headers=NULL;
122 console=stderr;
124 /* Kludge to implement standard GNU options. */
125 if (argc > 1 && !strcmp (argv[1], "--version"))
127 printf ("gpgkeys_curl (GnuPG) %s\n", VERSION);
128 printf ("Uses: %s\n", curl_version());
129 return 0;
131 else if (argc > 1 && !strcmp (argv[1], "--help"))
133 show_help (stdout);
134 return 0;
137 while((arg=getopt(argc,argv,"hVo:"))!=-1)
138 switch(arg)
140 default:
141 case 'h':
142 show_help (console);
143 return KEYSERVER_OK;
145 case 'V':
146 fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
147 return KEYSERVER_OK;
149 case 'o':
150 output=fopen(optarg,"wb");
151 if(output==NULL)
153 fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
154 optarg,strerror(errno));
155 return KEYSERVER_INTERNAL_ERROR;
158 break;
161 if(argc>optind)
163 input=fopen(argv[optind],"r");
164 if(input==NULL)
166 fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
167 argv[optind],strerror(errno));
168 return KEYSERVER_INTERNAL_ERROR;
172 if(input==NULL)
173 input=stdin;
175 if(output==NULL)
176 output=stdout;
178 opt=init_ks_options();
179 if(!opt)
180 return KEYSERVER_NO_MEMORY;
182 /* Get the command and info block */
184 while(fgets(line,MAX_LINE,input)!=NULL)
186 int err;
187 char option[MAX_OPTION+1];
189 if(line[0]=='\n')
190 break;
192 err=parse_ks_options(line,opt);
193 if(err>0)
195 ret=err;
196 goto fail;
198 else if(err==0)
199 continue;
201 if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
203 int no=0;
204 char *start=&option[0];
206 option[MAX_OPTION]='\0';
208 if(strncasecmp(option,"no-",3)==0)
210 no=1;
211 start=&option[3];
214 if(strncasecmp(start,"http-proxy",10)==0)
216 /* Safe to not check the return code of strdup() here.
217 If it fails, we simply won't use a proxy. */
218 if(no)
220 free(proxy);
221 proxy=strdup("");
223 else if(start[10]=='=')
225 if(strlen(&start[11])<MAX_PROXY)
227 free(proxy);
228 proxy=strdup(&start[11]);
232 else if(strncasecmp(start,"follow-redirects",16)==0)
234 if(no)
235 follow_redirects=0;
236 else if(start[16]=='=')
237 follow_redirects=atoi(&start[17]);
238 else if(start[16]=='\0')
239 follow_redirects=-1;
242 continue;
246 if(!opt->scheme)
248 fprintf(console,"gpgkeys: no scheme supplied!\n");
249 ret=KEYSERVER_SCHEME_NOT_FOUND;
250 goto fail;
253 if(!opt->host)
255 fprintf(console,"gpgkeys: no keyserver host provided\n");
256 goto fail;
259 if(opt->timeout && register_timeout()==-1)
261 fprintf(console,"gpgkeys: unable to register timeout handler\n");
262 return KEYSERVER_INTERNAL_ERROR;
265 curl_global_init(CURL_GLOBAL_DEFAULT);
267 curl=curl_easy_init();
268 if(!curl)
270 fprintf(console,"gpgkeys: unable to initialize curl\n");
271 ret=KEYSERVER_INTERNAL_ERROR;
272 goto fail;
275 /* Make sure we have the protocol the user is asking for so we can
276 print a nicer error message. */
277 curldata=curl_version_info(CURLVERSION_NOW);
278 for(i=0;curldata->protocols[i];i++)
279 if(strcasecmp(curldata->protocols[i],opt->scheme)==0)
280 break;
282 if(curldata->protocols[i]==NULL)
284 fprintf(console,"gpgkeys: protocol `%s' not supported\n",opt->scheme);
285 ret=KEYSERVER_SCHEME_NOT_FOUND;
286 goto fail;
289 if(follow_redirects)
291 curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L);
292 if(follow_redirects>0)
293 curl_easy_setopt(curl,CURLOPT_MAXREDIRS,follow_redirects);
296 if(opt->auth)
297 curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
299 if(opt->debug)
301 fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
302 curl_easy_setopt(curl,CURLOPT_STDERR,console);
303 curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
306 curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,(long)opt->flags.check_cert);
307 curl_easy_setopt(curl,CURLOPT_CAINFO,opt->ca_cert_file);
309 /* Avoid caches to get the most recent copy of the key. This is bug
310 #1061. In pre-curl versions of the code, we didn't do it. Then
311 we did do it (as a curl default) until curl changed the default.
312 Now we're doing it again, but in such a way that changing
313 defaults in the future won't impact us. We set both the Pragma
314 and Cache-Control versions of the header, so we're good with both
315 HTTP 1.0 and 1.1. */
316 headers=curl_slist_append(headers,"Pragma: no-cache");
317 if(headers)
318 headers=curl_slist_append(headers,"Cache-Control: no-cache");
320 if(!headers)
322 fprintf(console,"gpgkeys: out of memory when building HTTP headers\n");
323 ret=KEYSERVER_NO_MEMORY;
324 goto fail;
327 curl_easy_setopt(curl,CURLOPT_HTTPHEADER,headers);
329 if(proxy)
330 curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
332 /* If it's a GET or a SEARCH, the next thing to come in is the
333 keyids. If it's a SEND, then there are no keyids. */
335 if(opt->action==KS_GET)
337 /* Eat the rest of the file */
338 for(;;)
340 if(fgets(line,MAX_LINE,input)==NULL)
341 break;
342 else
344 if(line[0]=='\n' || line[0]=='\0')
345 break;
347 if(!thekey)
349 thekey=strdup(line);
350 if(!thekey)
352 fprintf(console,"gpgkeys: out of memory while "
353 "building key list\n");
354 ret=KEYSERVER_NO_MEMORY;
355 goto fail;
358 /* Trim the trailing \n */
359 thekey[strlen(line)-1]='\0';
364 else
366 fprintf(console,
367 "gpgkeys: this keyserver type only supports key retrieval\n");
368 goto fail;
371 if(!thekey)
373 fprintf(console,"gpgkeys: invalid keyserver instructions\n");
374 goto fail;
377 /* Send the response */
379 fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
380 fprintf(output,"PROGRAM %s\n\n",VERSION);
382 if(opt->verbose)
384 fprintf(console,"Scheme:\t\t%s\n",opt->scheme);
385 fprintf(console,"Host:\t\t%s\n",opt->host);
386 if(opt->port)
387 fprintf(console,"Port:\t\t%s\n",opt->port);
388 if(opt->path)
389 fprintf(console,"Path:\t\t%s\n",opt->path);
390 fprintf(console,"Command:\tGET\n");
393 set_timeout(opt->timeout);
395 ret=get_key(thekey);
397 fail:
399 free(thekey);
401 if(input!=stdin)
402 fclose(input);
404 if(output!=stdout)
405 fclose(output);
407 free_ks_options(opt);
409 curl_slist_free_all(headers);
411 if(curl)
412 curl_easy_cleanup(curl);
414 free(proxy);
416 curl_global_cleanup();
418 return ret;