Clarified cURL and OpenLDAP license issues.
[gnupg.git] / keyserver / gpgkeys_curl.c
1 /* gpgkeys_curl.c - fetch a key via libcurl
2  * Copyright (C) 2004, 2005, 2006 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  * 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.
31  */
32
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"
49
50 extern char *optarg;
51 extern int optind;
52
53 static FILE *input,*output,*console;
54 static CURL *curl;
55 static struct ks_options *opt;
56
57 static int
58 get_key(char *getkey)
59 {
60   CURLcode res;
61   char errorbuffer[CURL_ERROR_SIZE];
62   char request[MAX_URL];
63   struct curl_writer_ctx ctx;
64
65   memset(&ctx,0,sizeof(ctx));
66
67   if(strncmp(getkey,"0x",2)==0)
68     getkey+=2;
69
70   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
71
72   sprintf(request,"%s://%s%s%s%s",opt->scheme,opt->host,
73           opt->port?":":"",opt->port?opt->port:"",opt->path?opt->path:"/");
74
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);
80
81   res=curl_easy_perform(curl);
82   if(res!=CURLE_OK)
83     {
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));
87     }
88   else
89     {
90       curl_writer_finalize(&ctx);
91       if(!ctx.flags.done)
92         {
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);
96         }
97       else
98         fprintf(output,"\nKEY 0x%s END\n",getkey);
99     }
100
101   return curl_err_to_gpg_err(res);
102 }
103
104 static void 
105 show_help (FILE *fp)
106 {
107   fprintf (fp,"-h\thelp\n");
108   fprintf (fp,"-V\tversion\n");
109   fprintf (fp,"-o\toutput to this file\n");
110 }
111
112 int
113 main(int argc,char *argv[])
114 {
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;
120
121   console=stderr;
122
123   /* Kludge to implement standard GNU options.  */
124   if (argc > 1 && !strcmp (argv[1], "--version"))
125     {
126       fputs ("gpgkeys_curl (GnuPG) " VERSION"\n", stdout);
127       return 0;
128     }
129   else if (argc > 1 && !strcmp (argv[1], "--help"))
130     {
131       show_help (stdout);
132       return 0;
133     }
134
135   while((arg=getopt(argc,argv,"hVo:"))!=-1)
136     switch(arg)
137       {
138       default:
139       case 'h':
140         show_help (console);
141         return KEYSERVER_OK;
142
143       case 'V':
144         fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
145         return KEYSERVER_OK;
146
147       case 'o':
148         output=fopen(optarg,"wb");
149         if(output==NULL)
150           {
151             fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
152                     optarg,strerror(errno));
153             return KEYSERVER_INTERNAL_ERROR;
154           }
155
156         break;
157       }
158
159   if(argc>optind)
160     {
161       input=fopen(argv[optind],"r");
162       if(input==NULL)
163         {
164           fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
165                   argv[optind],strerror(errno));
166           return KEYSERVER_INTERNAL_ERROR;
167         }
168     }
169
170   if(input==NULL)
171     input=stdin;
172
173   if(output==NULL)
174     output=stdout;
175
176   opt=init_ks_options();
177   if(!opt)
178     return KEYSERVER_NO_MEMORY;
179
180   /* Get the command and info block */
181
182   while(fgets(line,MAX_LINE,input)!=NULL)
183     {
184       int err;
185       char option[MAX_OPTION+1];
186
187       if(line[0]=='\n')
188         break;
189
190       err=parse_ks_options(line,opt);
191       if(err>0)
192         {
193           ret=err;
194           goto fail;
195         }
196       else if(err==0)
197         continue;
198
199       if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
200         {
201           int no=0;
202           char *start=&option[0];
203
204           option[MAX_OPTION]='\0';
205
206           if(strncasecmp(option,"no-",3)==0)
207             {
208               no=1;
209               start=&option[3];
210             }
211
212           if(strncasecmp(start,"http-proxy",10)==0)
213             {
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)
217                 {
218                   free(proxy);
219                   proxy=strdup("");
220                 }
221               else if(start[10]=='=')
222                 {
223                   if(strlen(&start[11])<MAX_PROXY)
224                     {
225                       free(proxy);
226                       proxy=strdup(&start[11]);
227                     }
228                 }
229             }
230           else if(strncasecmp(start,"follow-redirects",16)==0)
231             {
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;
238             }
239
240           continue;
241         }
242     }
243
244   if(!opt->scheme)
245     {
246       fprintf(console,"gpgkeys: no scheme supplied!\n");
247       ret=KEYSERVER_SCHEME_NOT_FOUND;
248       goto fail;
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       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);
287     }
288
289   curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,opt->flags.check_cert);
290   curl_easy_setopt(curl,CURLOPT_CAINFO,opt->ca_cert_file);
291
292   if(proxy)
293     curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
294
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. */
297
298   if(opt->action==KS_GET)
299     {
300       /* Eat the rest of the file */
301       for(;;)
302         {
303           if(fgets(line,MAX_LINE,input)==NULL)
304             break;
305           else
306             {
307               if(line[0]=='\n' || line[0]=='\0')
308                 break;
309
310               if(!thekey)
311                 {
312                   thekey=strdup(line);
313                   if(!thekey)
314                     {
315                       fprintf(console,"gpgkeys: out of memory while "
316                               "building key list\n");
317                       ret=KEYSERVER_NO_MEMORY;
318                       goto fail;
319                     }
320
321                   /* Trim the trailing \n */
322                   thekey[strlen(line)-1]='\0';
323                 }
324             }
325         }
326     }
327   else
328     {
329       fprintf(console,
330               "gpgkeys: this keyserver type only supports key retrieval\n");
331       goto fail;
332     }
333
334   if(!thekey)
335     {
336       fprintf(console,"gpgkeys: invalid keyserver instructions\n");
337       goto fail;
338     }
339
340   /* Send the response */
341
342   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
343   fprintf(output,"PROGRAM %s\n\n",VERSION);
344
345   if(opt->verbose)
346     {
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");
354     }
355
356   set_timeout(opt->timeout);
357
358   ret=get_key(thekey);
359
360  fail:
361
362   free(thekey);
363
364   if(input!=stdin)
365     fclose(input);
366
367   if(output!=stdout)
368     fclose(output);
369
370   free_ks_options(opt);
371
372   if(curl)
373     curl_easy_cleanup(curl);
374
375   free(proxy);
376
377   curl_global_cleanup();
378
379   return ret;
380 }