gpg: Fix honoring --cert-digest-algo when recreating a cert
[gnupg.git] / keyserver / gpgkeys_curl.c
index 1ec7a8a..28ec698 100644 (file)
@@ -1,11 +1,11 @@
 /* gpgkeys_curl.c - fetch a key via libcurl
- * Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the Free Software Foundation
+ * gives permission to link the code of the keyserver helper tools:
+ * gpgkeys_ldap, gpgkeys_curl and gpgkeys_hkp with the OpenSSL
+ * project's "OpenSSL" library (or with modified versions of it that
+ * use the same license as the "OpenSSL" library), and distribute the
+ * linked executables.  You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL".  If
+ * you modify this file, you may extend this exception to your version
+ * of the file, but you are not obligated to do so.  If you do not
+ * wish to do so, delete this exception statement from your version.
  */
 
 #include <config.h>
 #ifdef HAVE_GETOPT_H
 #include <getopt.h>
 #endif
-#ifdef FAKE_CURL
-#include "curl-shim.h"
-#else
+#ifdef HAVE_LIBCURL
 #include <curl/curl.h>
+#else
+#include "curl-shim.h"
 #endif
 #include "keyserver.h"
 #include "ksutil.h"
@@ -48,59 +58,74 @@ get_key(char *getkey)
   CURLcode res;
   char errorbuffer[CURL_ERROR_SIZE];
   char request[MAX_URL];
+  struct curl_writer_ctx ctx;
+
+  memset(&ctx,0,sizeof(ctx));
 
   if(strncmp(getkey,"0x",2)==0)
     getkey+=2;
 
   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
 
-  sprintf(request,"%s://%s%s%s%s%s%s",opt->scheme,
-         opt->auth?opt->auth:"",
-         opt->auth?"@":"",opt->host,
-         opt->port?":":"",opt->port?opt->port:"",
-         opt->path?opt->path:"/");
+  sprintf(request,"%s://%s%s%s%s",opt->scheme,opt->host,
+         opt->port?":":"",opt->port?opt->port:"",opt->path?opt->path:"/");
 
   curl_easy_setopt(curl,CURLOPT_URL,request);
   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
-  curl_easy_setopt(curl,CURLOPT_FILE,output);
+  ctx.stream=output;
+  curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
   curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
 
   res=curl_easy_perform(curl);
-  if(res!=0)
+  if(res!=CURLE_OK)
     {
       fprintf(console,"gpgkeys: %s fetch error %d: %s\n",opt->scheme,
              res,errorbuffer);
       fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
     }
   else
-    fprintf(output,"\nKEY 0x%s END\n",getkey);
+    {
+      curl_writer_finalize(&ctx);
+      if(!ctx.flags.done)
+       {
+         fprintf(console,"gpgkeys: no key data found for %s\n",request);
+         fprintf(output,"\nKEY 0x%s FAILED %d\n",
+                 getkey,KEYSERVER_KEY_NOT_FOUND);
+       }
+      else
+       fprintf(output,"\nKEY 0x%s END\n",getkey);
+    }
 
-  return KEYSERVER_OK;
+  return curl_err_to_gpg_err(res);
 }
 
 static void 
 show_help (FILE *fp)
 {
-  fprintf (fp,"-h\thelp\n");
-  fprintf (fp,"-V\tversion\n");
-  fprintf (fp,"-o\toutput to this file\n");
+  fprintf (fp,"-h, --help\thelp\n");
+  fprintf (fp,"-V\t\tmachine readable version\n");
+  fprintf (fp,"--version\thuman readable version\n");
+  fprintf (fp,"-o\t\toutput to this file\n");
 }
 
 int
 main(int argc,char *argv[])
 {
-  int arg,ret=KEYSERVER_INTERNAL_ERROR;
+  int arg,ret=KEYSERVER_INTERNAL_ERROR,i;
   char line[MAX_LINE];
   char *thekey=NULL;
   long follow_redirects=5;
-  char proxy[MAX_PROXY+1];
+  char *proxy=NULL;
+  curl_version_info_data *curldata;
+  struct curl_slist *headers=NULL;
 
   console=stderr;
 
   /* Kludge to implement standard GNU options.  */
   if (argc > 1 && !strcmp (argv[1], "--version"))
     {
-      fputs ("gpgkeys_curl (GnuPG) " VERSION"\n", stdout);
+      printf ("gpgkeys_curl (GnuPG) %s\n", VERSION);
+      printf ("Uses: %s\n", curl_version());
       return 0;
     }
   else if (argc > 1 && !strcmp (argv[1], "--help"))
@@ -188,20 +213,19 @@ main(int argc,char *argv[])
 
          if(strncasecmp(start,"http-proxy",10)==0)
            {
+             /* Safe to not check the return code of strdup() here.
+                If it fails, we simply won't use a proxy. */
              if(no)
-               proxy[0]='\0';
-             else if(start[10]=='=')
                {
-                 strncpy(proxy,&start[11],MAX_PROXY);
-                 proxy[MAX_PROXY]='\0';
+                 free(proxy);
+                 proxy=strdup("");
                }
-             else if(start[10]=='\0')
+             else if(start[10]=='=')
                {
-                 char *http_proxy=getenv(HTTP_PROXY_ENV);
-                 if(http_proxy)
+                 if(strlen(&start[11])<MAX_PROXY)
                    {
-                     strncpy(proxy,http_proxy,MAX_PROXY);
-                     proxy[MAX_PROXY]='\0';
+                     free(proxy);
+                     proxy=strdup(&start[11]);
                    }
                }
            }
@@ -225,27 +249,6 @@ main(int argc,char *argv[])
       ret=KEYSERVER_SCHEME_NOT_FOUND;
       goto fail;
     }
-#ifdef HTTP_VIA_LIBCURL
-  else if(strcasecmp(opt->scheme,"http")==0)
-    ;
-#endif /* HTTP_VIA_LIBCURL */
-#ifdef HTTPS_VIA_LIBCURL
-  else if(strcasecmp(opt->scheme,"https")==0)
-    ;
-#endif /* HTTP_VIA_LIBCURL */
-#ifdef FTP_VIA_LIBCURL
-  else if(strcasecmp(opt->scheme,"ftp")==0)
-    ;
-#endif /* FTP_VIA_LIBCURL */
-#ifdef FTPS_VIA_LIBCURL
-  else if(strcasecmp(opt->scheme,"ftps")==0)
-    ;
-#endif /* FTPS_VIA_LIBCURL */
-  else
-    {
-      fprintf(console,"gpgkeys: scheme `%s' not supported\n",opt->scheme);
-      return KEYSERVER_SCHEME_NOT_FOUND;
-    }
 
   if(!opt->host)
     {
@@ -260,6 +263,7 @@ main(int argc,char *argv[])
     }
 
   curl_global_init(CURL_GLOBAL_DEFAULT);
+
   curl=curl_easy_init();
   if(!curl)
     {
@@ -268,23 +272,61 @@ main(int argc,char *argv[])
       goto fail;
     }
 
+  /* Make sure we have the protocol the user is asking for so we can
+     print a nicer error message. */
+  curldata=curl_version_info(CURLVERSION_NOW);
+  for(i=0;curldata->protocols[i];i++)
+    if(strcasecmp(curldata->protocols[i],opt->scheme)==0)
+      break;
+
+  if(curldata->protocols[i]==NULL)
+    {
+      fprintf(console,"gpgkeys: protocol `%s' not supported\n",opt->scheme);
+      ret=KEYSERVER_SCHEME_NOT_FOUND;
+      goto fail;
+    }
+
   if(follow_redirects)
     {
-      curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1);
+      curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L);
       if(follow_redirects>0)
        curl_easy_setopt(curl,CURLOPT_MAXREDIRS,follow_redirects);
     }
 
+  if(opt->auth)
+    curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
+
   if(opt->debug)
     {
+      fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
       curl_easy_setopt(curl,CURLOPT_STDERR,console);
-      curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
+      curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
     }
 
-  curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,opt->flags.check_cert);
+  curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,(long)opt->flags.check_cert);
   curl_easy_setopt(curl,CURLOPT_CAINFO,opt->ca_cert_file);
 
-  if(proxy[0])
+  /* Avoid caches to get the most recent copy of the key.  This is bug
+     #1061.  In pre-curl versions of the code, we didn't do it.  Then
+     we did do it (as a curl default) until curl changed the default.
+     Now we're doing it again, but in such a way that changing
+     defaults in the future won't impact us.  We set both the Pragma
+     and Cache-Control versions of the header, so we're good with both
+     HTTP 1.0 and 1.1. */
+  headers=curl_slist_append(headers,"Pragma: no-cache");
+  if(headers)
+    headers=curl_slist_append(headers,"Cache-Control: no-cache");
+
+  if(!headers)
+    {
+      fprintf(console,"gpgkeys: out of memory when building HTTP headers\n");
+      ret=KEYSERVER_NO_MEMORY;
+      goto fail;
+    }
+
+  curl_easy_setopt(curl,CURLOPT_HTTPHEADER,headers);
+
+  if(proxy)
     curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
 
   /* If it's a GET or a SEARCH, the next thing to come in is the
@@ -364,9 +406,13 @@ main(int argc,char *argv[])
 
   free_ks_options(opt);
 
+  curl_slist_free_all(headers);
+
   if(curl)
     curl_easy_cleanup(curl);
 
+  free(proxy);
+
   curl_global_cleanup();
 
   return ret;