* gpgkeys_ldap.c (main): Fix three wrong calls to fail_all(). Noted
[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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #ifdef HAVE_GETOPT_H
28 #include <getopt.h>
29 #endif
30 #ifdef FAKE_CURL
31 #include "curl-shim.h"
32 #else
33 #include <curl/curl.h>
34 #endif
35 #include "keyserver.h"
36 #include "ksutil.h"
37
38 extern char *optarg;
39 extern int optind;
40
41 static char proxy[MAX_PROXY+1];
42 static FILE *input,*output,*console;
43 static CURL *curl;
44 static struct ks_options *opt;
45
46 static int
47 curl_err_to_gpg_err(CURLcode error)
48 {
49   switch(error)
50     {
51     case CURLE_FTP_COULDNT_RETR_FILE: return KEYSERVER_KEY_NOT_FOUND;
52     default: return KEYSERVER_INTERNAL_ERROR;
53     }
54 }
55
56 static size_t
57 writer(const void *ptr,size_t size,size_t nmemb,void *stream)
58 {
59   const char *buf=ptr;
60   size_t i;
61   static int markeridx=0,begun=0,done=0;
62   static const char *marker=BEGIN;
63
64   /* scan the incoming data for our marker */
65   for(i=0;!done && i<(size*nmemb);i++)
66     {
67       if(buf[i]==marker[markeridx])
68         {
69           markeridx++;
70           if(marker[markeridx]=='\0')
71             {
72               if(begun)
73                 done=1;
74               else
75                 {
76                   /* We've found the BEGIN marker, so now we're looking
77                      for the END marker. */
78                   begun=1;
79                   marker=END;
80                   markeridx=0;
81                   fprintf(output,BEGIN);
82                   continue;
83                 }
84             }
85         }
86       else
87         markeridx=0;
88
89       if(begun)
90         {
91           /* Canonicalize CRLF to just LF by stripping CRs.  This
92              actually makes sense, since on Unix-like machines LF is
93              correct, and on win32-like machines, our output buffer is
94              opened in textmode and will re-canonicalize line endings
95              back to CRLF.  Since we only need to handle armored keys,
96              we don't have to worry about odd cases like CRCRCR and
97              the like. */
98
99           if(buf[i]!='\r')
100             fputc(buf[i],output);
101         }
102     }
103
104   return size*nmemb;
105 }
106
107 static int
108 get_key(char *getkey)
109 {
110   CURLcode res;
111   char errorbuffer[CURL_ERROR_SIZE];
112   char request[MAX_URL];
113
114   if(strncmp(getkey,"0x",2)==0)
115     getkey+=2;
116
117   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
118
119   sprintf(request,"%s://%s%s%s%s%s%s",opt->scheme,
120           opt->auth?opt->auth:"",
121           opt->auth?"@":"",opt->host,
122           opt->port?":":"",opt->port?opt->port:"",
123           opt->path?opt->path:"/");
124
125   curl_easy_setopt(curl,CURLOPT_URL,request);
126   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,writer);
127   curl_easy_setopt(curl,CURLOPT_FILE,output);
128   curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
129
130   res=curl_easy_perform(curl);
131   if(res!=0)
132     {
133       fprintf(console,"gpgkeys: %s fetch error %d: %s\n",opt->scheme,
134               res,errorbuffer);
135       fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
136     }
137   else
138     fprintf(output,"\nKEY 0x%s END\n",getkey);
139
140   return KEYSERVER_OK;
141 }
142
143 static void 
144 show_help (FILE *fp)
145 {
146   fprintf (fp,"-h\thelp\n");
147   fprintf (fp,"-V\tversion\n");
148   fprintf (fp,"-o\toutput to this file\n");
149 }
150
151 int
152 main(int argc,char *argv[])
153 {
154   int arg,ret=KEYSERVER_INTERNAL_ERROR;
155   char line[MAX_LINE];
156   char *thekey=NULL;
157   long follow_redirects=5;
158
159   console=stderr;
160
161   /* Kludge to implement standard GNU options.  */
162   if (argc > 1 && !strcmp (argv[1], "--version"))
163     {
164       fputs ("gpgkeys_curl (GnuPG) " VERSION"\n", stdout);
165       return 0;
166     }
167   else if (argc > 1 && !strcmp (argv[1], "--help"))
168     {
169       show_help (stdout);
170       return 0;
171     }
172
173   while((arg=getopt(argc,argv,"hVo:"))!=-1)
174     switch(arg)
175       {
176       default:
177       case 'h':
178         show_help (console);
179         return KEYSERVER_OK;
180
181       case 'V':
182         fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
183         return KEYSERVER_OK;
184
185       case 'o':
186         output=fopen(optarg,"wb");
187         if(output==NULL)
188           {
189             fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
190                     optarg,strerror(errno));
191             return KEYSERVER_INTERNAL_ERROR;
192           }
193
194         break;
195       }
196
197   if(argc>optind)
198     {
199       input=fopen(argv[optind],"r");
200       if(input==NULL)
201         {
202           fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
203                   argv[optind],strerror(errno));
204           return KEYSERVER_INTERNAL_ERROR;
205         }
206     }
207
208   if(input==NULL)
209     input=stdin;
210
211   if(output==NULL)
212     output=stdout;
213
214   opt=init_ks_options();
215   if(!opt)
216     return KEYSERVER_NO_MEMORY;
217
218   /* Get the command and info block */
219
220   while(fgets(line,MAX_LINE,input)!=NULL)
221     {
222       int err;
223       char option[MAX_OPTION+1];
224
225       if(line[0]=='\n')
226         break;
227
228       err=parse_ks_options(line,opt);
229       if(err>0)
230         {
231           ret=err;
232           goto fail;
233         }
234       else if(err==0)
235         continue;
236
237       if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
238         {
239           int no=0;
240           char *start=&option[0];
241
242           option[MAX_OPTION]='\0';
243
244           if(strncasecmp(option,"no-",3)==0)
245             {
246               no=1;
247               start=&option[3];
248             }
249
250           if(strncasecmp(start,"http-proxy",10)==0)
251             {
252               if(no)
253                 proxy[0]='\0';
254               else if(start[10]=='=')
255                 {
256                   strncpy(proxy,&start[11],MAX_PROXY);
257                   proxy[MAX_PROXY]='\0';
258                 }
259             }
260           else if(strncasecmp(start,"follow-redirects",16)==0)
261             {
262               if(no)
263                 follow_redirects=0;
264               else if(start[16]=='=')
265                 follow_redirects=atoi(&start[17]);
266               else if(start[16]=='\0')
267                 follow_redirects=-1;
268             }
269
270           continue;
271         }
272     }
273
274   if(!opt->scheme)
275     {
276       fprintf(console,"gpgkeys: no scheme supplied!\n");
277       ret=KEYSERVER_SCHEME_NOT_FOUND;
278       goto fail;
279     }
280 #ifdef HTTP_VIA_LIBCURL
281   else if(strcasecmp(opt->scheme,"http")==0)
282     ;
283 #endif /* HTTP_VIA_LIBCURL */
284 #ifdef HTTPS_VIA_LIBCURL
285   else if(strcasecmp(opt->scheme,"https")==0)
286     ;
287 #endif /* HTTP_VIA_LIBCURL */
288 #ifdef FTP_VIA_LIBCURL
289   else if(strcasecmp(opt->scheme,"ftp")==0)
290     ;
291 #endif /* FTP_VIA_LIBCURL */
292 #ifdef FTPS_VIA_LIBCURL
293   else if(strcasecmp(opt->scheme,"ftps")==0)
294     ;
295 #endif /* FTPS_VIA_LIBCURL */
296   else
297     {
298       fprintf(console,"gpgkeys: scheme `%s' not supported\n",opt->scheme);
299       return KEYSERVER_SCHEME_NOT_FOUND;
300     }
301
302   if(!opt->host)
303     {
304       fprintf(console,"gpgkeys: no keyserver host provided\n");
305       goto fail;
306     }
307
308   if(opt->timeout && register_timeout()==-1)
309     {
310       fprintf(console,"gpgkeys: unable to register timeout handler\n");
311       return KEYSERVER_INTERNAL_ERROR;
312     }
313
314   curl_global_init(CURL_GLOBAL_DEFAULT);
315   curl=curl_easy_init();
316   if(!curl)
317     {
318       fprintf(console,"gpgkeys: unable to initialize curl\n");
319       ret=KEYSERVER_INTERNAL_ERROR;
320       goto fail;
321     }
322
323   if(follow_redirects)
324     {
325       curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1);
326       if(follow_redirects>0)
327         curl_easy_setopt(curl,CURLOPT_MAXREDIRS,follow_redirects);
328     }
329
330   if(opt->debug)
331     {
332       curl_easy_setopt(curl,CURLOPT_STDERR,console);
333       curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
334     }
335
336   curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,opt->flags.check_cert);
337   curl_easy_setopt(curl,CURLOPT_CAINFO,opt->ca_cert_file);
338
339   if(proxy[0])
340     curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
341
342   /* If it's a GET or a SEARCH, the next thing to come in is the
343      keyids.  If it's a SEND, then there are no keyids. */
344
345   if(opt->action==KS_GET)
346     {
347       /* Eat the rest of the file */
348       for(;;)
349         {
350           if(fgets(line,MAX_LINE,input)==NULL)
351             break;
352           else
353             {
354               if(line[0]=='\n' || line[0]=='\0')
355                 break;
356
357               if(!thekey)
358                 {
359                   thekey=strdup(line);
360                   if(!thekey)
361                     {
362                       fprintf(console,"gpgkeys: out of memory while "
363                               "building key list\n");
364                       ret=KEYSERVER_NO_MEMORY;
365                       goto fail;
366                     }
367
368                   /* Trim the trailing \n */
369                   thekey[strlen(line)-1]='\0';
370                 }
371             }
372         }
373     }
374   else
375     {
376       fprintf(console,
377               "gpgkeys: this keyserver type only supports key retrieval\n");
378       goto fail;
379     }
380
381   if(!thekey)
382     {
383       fprintf(console,"gpgkeys: invalid keyserver instructions\n");
384       goto fail;
385     }
386
387   /* Send the response */
388
389   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
390   fprintf(output,"PROGRAM %s\n\n",VERSION);
391
392   if(opt->verbose)
393     {
394       fprintf(console,"Scheme:\t\t%s\n",opt->scheme);
395       fprintf(console,"Host:\t\t%s\n",opt->host);
396       if(opt->port)
397         fprintf(console,"Port:\t\t%s\n",opt->port);
398       if(opt->path)
399         fprintf(console,"Path:\t\t%s\n",opt->path);
400       fprintf(console,"Command:\tGET\n");
401     }
402
403   set_timeout(opt->timeout);
404
405   ret=get_key(thekey);
406
407  fail:
408
409   free(thekey);
410
411   if(input!=stdin)
412     fclose(input);
413
414   if(output!=stdout)
415     fclose(output);
416
417   free_ks_options(opt);
418
419   if(curl)
420     curl_easy_cleanup(curl);
421
422   curl_global_cleanup();
423
424   return ret;
425 }