dirmngr: Add command to only load the swdb.
authorWerner Koch <wk@gnupg.org>
Wed, 16 Nov 2016 20:22:39 +0000 (21:22 +0100)
committerWerner Koch <wk@gnupg.org>
Wed, 16 Nov 2016 20:22:39 +0000 (21:22 +0100)
* dirmngr/loadswdb.c: New.
* dirmngr/Makefile.am (dirmngr_SOURCES): Add that file.
* dirmngr/server.c: Remove includes cpparray.h and exectool.h.
(cmd_loadswdb): New.
(parse_version_number,parse_version_string): Remove.
(my_mktmpdir, cmp_version): Remove.
(fetch_into_tmpdir): Remove.
(struct verify_swdb_parm_s): Remove.
(verify_swdb_status_cb): Remove.
(cmd_versioncheck): Remove.
(register_commands): Register LOADSWDB.  Remove VERSIONCHECK.
--

This change is required to to the new design where gpgconf does the
version check w/o network access and only dirmngr is responsible for
getting the swdb.

In the next installment the loading will be triggered as needed.

Signed-off-by: Werner Koch <wk@gnupg.org>
dirmngr/Makefile.am
dirmngr/dirmngr.h
dirmngr/loadswdb.c [new file with mode: 0644]
dirmngr/server.c

index bd70c8c..2a18a50 100644 (file)
@@ -56,6 +56,7 @@ noinst_HEADERS = dirmngr.h crlcache.h crlfetch.h misc.h
 
 dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c   \
        certcache.c certcache.h \
+       loadswdb.c \
        cdb.h cdblib.c misc.c dirmngr-err.h  \
        ocsp.c ocsp.h validate.c validate.h  \
        dns-stuff.c dns-stuff.h \
index 107059d..42b3b2b 100644 (file)
@@ -206,5 +206,8 @@ gpg_error_t dirmngr_status_help (ctrl_t ctrl, const char *text);
 gpg_error_t dirmngr_tick (ctrl_t ctrl);
 
 
+/*-- loadswdb.c --*/
+gpg_error_t dirmngr_load_swdb (ctrl_t ctrl, int force);
+
 
 #endif /*DIRMNGR_H*/
diff --git a/dirmngr/loadswdb.c b/dirmngr/loadswdb.c
new file mode 100644 (file)
index 0000000..57a7e04
--- /dev/null
@@ -0,0 +1,358 @@
+/* loadswdb.c - Load the swdb file from versions.gnupg.org
+ * Copyright (C) 2016 g10 Code GmbH
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dirmngr.h"
+#include "../common/ccparray.h"
+#include "../common/exectool.h"
+#include "misc.h"
+#include "ks-engine.h"
+
+
+/* Get the time from the current swdb file and store it at R_TIME.  If
+ * the file does not exist 0 is stored at R_TIME.  The function
+ * returns 0 on sucess or an error code.  */
+static gpg_error_t
+time_of_saved_swdb (const char *fname, time_t *r_time)
+{
+  gpg_error_t err;
+  estream_t fp = NULL;
+  char *line = NULL;
+  size_t length_of_line = 0;
+  size_t  maxlen;
+  ssize_t len;
+  char *fields[2];
+  time_t t = (time_t)(-1);
+
+  *r_time = 0;
+
+  fp = es_fopen (fname, "r");
+  err = fp? 0 : gpg_error_from_syserror ();
+  if (err)
+    {
+      if (gpg_err_code (err) == GPG_ERR_ENOENT)
+        err = 0; /* No file - assume time is the year of Unix.  */
+      goto leave;
+    }
+
+  /* Note that the parser uses the first occurance of a matching
+   * values and ignores possible duplicated values.  */
+  maxlen = 2048; /* Set limit.  */
+  while ((len = es_read_line (fp, &line, &length_of_line, &maxlen)) > 0)
+    {
+      if (!maxlen)
+        {
+          err = gpg_error (GPG_ERR_LINE_TOO_LONG);
+          goto leave;
+        }
+      /* Strip newline and carriage return, if present.  */
+      while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
+       line[--len] = '\0';
+
+      if (split_fields (line, fields, DIM (fields)) < DIM(fields))
+        continue; /* Skip empty lines and names w/o a value.  */
+      if (*fields[0] == '#')
+        continue; /* Skip comments.  */
+
+      /* Record the meta data.  */
+      if (!strcmp (fields[0], ".filedate"))
+        {
+          gnupg_isotime_t isot;
+          if (string2isotime (isot, fields[1])
+              && (t = isotime2epoch (isot)) != (time_t)(-1))
+            break;  /* Got the time - stop reading.  */
+        }
+    }
+  if (len < 0 || es_ferror (fp))
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  if (t == (time_t)(-1))
+    {
+      err = gpg_error (GPG_ERR_INV_TIME);
+      goto leave;
+    }
+
+  *r_time = t;
+
+ leave:
+  if (err)
+    log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err));
+  xfree (line);
+  es_fclose (fp);
+  return err;
+}
+
+
+
+/* Read a file from URL and return it as an estream memory buffer at
+ * R_FP.  */
+static gpg_error_t
+fetch_file (ctrl_t ctrl, const char *url, estream_t *r_fp)
+{
+  gpg_error_t err;
+  estream_t fp = NULL;
+  estream_t httpfp = NULL;
+  size_t nread, nwritten;
+  char buffer[1024];
+
+  if ((err = ks_http_fetch (ctrl, url, &httpfp)))
+    goto leave;
+
+  /* We now read the data from the web server into a memory buffer.
+   * To avoid excessive memory use in case of a ill behaving server we
+   * put a 64 k size limit on the buffer.  As of today the actual size
+   * of the swdb.lst file is 3k.  */
+  fp = es_fopenmem (64*1024, "rw");
+  if (!fp)
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
+      goto leave;
+    }
+
+  for (;;)
+    {
+      if (es_read (httpfp, buffer, sizeof buffer, &nread))
+        {
+          err = gpg_error_from_syserror ();
+          log_error ("error reading '%s': %s\n",
+                     es_fname_get (httpfp), gpg_strerror (err));
+          goto leave;
+        }
+
+      if (!nread)
+        break; /* Ready.  */
+      if (es_write (fp, buffer, nread, &nwritten))
+        {
+          err = gpg_error_from_syserror ();
+          log_error ("error writing '%s': %s\n",
+                     es_fname_get (fp), gpg_strerror (err));
+          goto leave;
+        }
+      else if (nread != nwritten)
+        {
+          err = gpg_error (GPG_ERR_EIO);
+          log_error ("error writing '%s': %s\n",
+                     es_fname_get (fp), "short write");
+          goto leave;
+        }
+    }
+
+  es_rewind (fp);
+  *r_fp = fp;
+  fp = NULL;
+
+ leave:
+  es_fclose (httpfp);
+  es_fclose (fp);
+  return err;
+}
+
+
+/* Communication object for verify_status_cb.  */
+struct verify_status_parm_s
+{
+  time_t sigtime;
+  int anyvalid;
+};
+
+static void
+verify_status_cb (void *opaque, const char *keyword, char *args)
+{
+  struct verify_status_parm_s *parm = opaque;
+
+  /* We care only about the first valid signature.  */
+  if (!strcmp (keyword, "VALIDSIG") && !parm->anyvalid)
+    {
+      char *fields[3];
+
+      parm->anyvalid = 1;
+      if (split_fields (args, fields, DIM (fields)) >= 3)
+        parm->sigtime = parse_timestamp (fields[2], NULL);
+    }
+}
+
+
+
+/* Load the swdb file into the current home directory.  Do this onlky
+ * when needed unless FORCE is set which will always get a new
+ * copy.  */
+gpg_error_t
+dirmngr_load_swdb (ctrl_t ctrl, int force)
+{
+  gpg_error_t err;
+  char *fname = NULL;      /* The swdb.lst file.  */
+  char *tmp_fname = NULL;  /* The temporary swdb.lst file.  */
+  char *keyfile_fname = NULL;
+  estream_t swdb = NULL;
+  estream_t swdb_sig = NULL;
+  ccparray_t ccp;
+  const char **argv = NULL;
+  struct verify_status_parm_s verify_status_parm = { (time_t)(-1), 0 };
+  estream_t outfp = NULL;
+  time_t now = gnupg_get_time ();
+  gnupg_isotime_t isotime;
+
+
+  fname = make_filename_try (gnupg_homedir (), "swdb.lst", NULL);
+  if (!fname)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+
+  /* Check whether there is a need to get an update.  */
+  if (!force)
+    {
+      time_t filetime;
+
+      err = time_of_saved_swdb (fname, &filetime);
+      if (err)
+        goto leave;
+      if (filetime >= now)
+        goto leave; /* Current or newer.  */
+      if (now - filetime < 3*86400)
+        goto leave; /* Not older than 3 days.  */
+    }
+
+  /* Create the filename of the file with the keys. */
+  keyfile_fname = make_filename_try (gnupg_datadir (), "distsigkey.gpg", NULL);
+  if (!keyfile_fname)
+    goto leave;
+
+  /* Fetch the swdb from the web.  */
+  err = fetch_file (ctrl, "https://versions.gnupg.org/swdb.lst", &swdb);
+  if (err)
+    goto leave;
+  err = fetch_file (ctrl, "https://versions.gnupg.org/swdb.lst.sig", &swdb_sig);
+  if (err)
+    goto leave;
+
+  /* Run gpgv.  */
+  ccparray_init (&ccp, 0);
+  ccparray_put (&ccp, "--enable-special-filenames");
+  ccparray_put (&ccp, "--status-fd=2");
+  ccparray_put (&ccp, "--keyring");
+  ccparray_put (&ccp, keyfile_fname);
+  ccparray_put (&ccp, "--");
+  ccparray_put (&ccp, "-&@INEXTRA@");
+  ccparray_put (&ccp, "-");
+  ccparray_put (&ccp, NULL);
+  argv = ccparray_get (&ccp, NULL);
+  if (!argv)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+
+  err = gnupg_exec_tool_stream (gnupg_module_name (GNUPG_MODULE_NAME_GPGV),
+                                argv, swdb, swdb_sig, NULL,
+                                verify_status_cb, &verify_status_parm);
+  if (!err && verify_status_parm.sigtime == (time_t)(-1))
+    err = gpg_error (verify_status_parm.anyvalid? GPG_ERR_BAD_SIGNATURE
+                     /**/                       : GPG_ERR_INV_TIME      );
+  if (err)
+    goto leave;
+
+  /* Create a file name for a temporary file in the home directory.
+   * We will later rename that file to the real name.  */
+  {
+    char *tmpstr;
+
+#ifdef HAVE_W32_SYSTEM
+    tmpstr = es_bsprintf ("tmp-%u-swdb", (unsigned int)getpid ());
+#else
+    tmpstr = es_bsprintf (".#%u.swdb", (unsigned int)getpid ());
+#endif
+    if (!tmpstr)
+      {
+        err = gpg_error_from_syserror ();
+        goto leave;
+      }
+    tmp_fname = make_filename_try (gnupg_homedir (), tmpstr, NULL);
+    xfree (tmpstr);
+    if (!tmp_fname)
+      {
+        err = gpg_error_from_syserror ();
+        goto leave;
+      }
+  }
+
+  outfp = es_fopen (tmp_fname, "w");
+  if (!outfp)
+    {
+      err = gpg_error_from_syserror ();
+      log_error (_("error creating '%s': %s\n"), tmp_fname, gpg_strerror (err));
+      goto leave;
+    }
+
+  epoch2isotime (isotime, verify_status_parm.sigtime);
+  es_fprintf (outfp, ".filedate %s\n", isotime);
+  epoch2isotime (isotime, now);
+  es_fprintf (outfp, ".verified %s\n", isotime);
+
+  if (es_fseek (swdb, 0, SEEK_SET))
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+
+  err = copy_stream (swdb, outfp);
+  if (err)
+    {
+      /* Well, it might also be a reading error, but that is pretty
+       * unlikely for a memory stream.  */
+      log_error (_("error writing '%s': %s\n"), tmp_fname, gpg_strerror (err));
+      goto leave;
+    }
+
+  if (es_fclose (outfp))
+    {
+      err = gpg_error_from_syserror ();
+      log_error (_("error writing '%s': %s\n"), tmp_fname, gpg_strerror (err));
+      goto leave;
+    }
+  outfp = NULL;
+
+  err = gnupg_rename_file (tmp_fname, fname, NULL);
+  if (err)
+    goto leave;
+  xfree (tmp_fname);
+  tmp_fname = NULL;
+
+
+ leave:
+  es_fclose (outfp);
+  if (tmp_fname)
+    gnupg_remove (tmp_fname);  /* This is a temporary file.  */
+  xfree (argv);
+  es_fclose (swdb_sig);
+  es_fclose (swdb);
+  xfree (keyfile_fname);
+  xfree (tmp_fname);
+  xfree (fname);
+  return err;
+}
index 2122d54..5c78d64 100644 (file)
@@ -54,8 +54,6 @@
 #include "mbox-util.h"
 #include "zb32.h"
 #include "server-help.h"
-#include "ccparray.h"
-#include "../common/exectool.h"
 
 /* To avoid DoS attacks we limit the size of a certificate to
    something reasonable.  The DoS was actually only an issue back when
@@ -2239,6 +2237,22 @@ cmd_ks_put (assuan_context_t ctx, char *line)
 }
 
 
+\f
+static const char hlp_loadswdb[] =
+  "LOADSWDB [--force]\n"
+  "\n"
+  "Load and verify the swdb.lst from the Net.";
+static gpg_error_t
+cmd_loadswdb (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+
+  err = dirmngr_load_swdb (ctrl, has_option (line, "--force"));
+
+  return leave_cmd (ctx, err);
+}
+
 
 \f
 static const char hlp_getinfo[] =
@@ -2343,388 +2357,6 @@ cmd_reloaddirmngr (assuan_context_t ctx, char *line)
 
 
 \f
-/* This function parses the first portion of the version number S and
- * stores it in *NUMBER.  On success, this function returns a pointer
- * into S starting with the first character, which is not part of the
- * initial number portion; on failure, NULL is returned.  */
-static const char*
-parse_version_number (const char *s, int *number)
-{
-  int val = 0;
-
-  if (*s == '0' && digitp (&s[1]))
-    return NULL;  /* Leading zeros are not allowed.  */
-  for (; digitp (s); s++)
-    {
-      val *= 10;
-      val += *s - '0';
-    }
-  *number = val;
-  return val < 0 ? NULL : s;
-}
-
-
-/* This function breaks up the complete string-representation of the
- * version number S, which is of the following struture: <major
- * number>.<minor number>[.<micro number>]<patch level>.  The major,
- * minor and micro number components will be stored in *MAJOR, *MINOR
- * and *MICRO.  If MICRO is not given 0 is used instead.
- *
- * On success, the last component, the patch level, will be returned;
- * on failure, NULL will be returned.  */
-static const char *
-parse_version_string (const char *s, int *major, int *minor, int *micro)
-{
-  s = parse_version_number (s, major);
-  if (!s || *s != '.')
-    return NULL;
-  s++;
-  s = parse_version_number (s, minor);
-  if (!s)
-    return NULL;
-  if (*s == '.')
-    {
-      s++;
-      s = parse_version_number (s, micro);
-      if (!s)
-        return NULL;
-    }
-  else
-    micro = 0;
-  return s;  /* Patchlevel.  */
-}
-
-
-/* Create temporary directory with mode 0700.  Returns a dynamically
- * allocated string with the filename of the directory.  */
-static char *
-my_mktmpdir (void)
-{
-  char *name, *p;
-
-  p = getenv ("TMPDIR");
-  if (!p || !*p)
-    p = "/tmp";
-  if (p[strlen (p) - 1] == '/')
-    name = strconcat (p, "gpg-XXXXXX", NULL);
-  else
-    name = strconcat (p, "/", "gpg-XXXXXX", NULL);
-  if (!name || !gnupg_mkdtemp (name))
-    {
-      int saveerr = errno;
-      log_error (_("can't create temporary directory '%s': %s\n"),
-                 name, strerror (saveerr));
-      gpg_err_set_errno (saveerr);
-      return NULL;
-    }
-
-  return name;
-}
-
-
-/* Sets result to -1 if version a is less than b, 0 if the versions are equal
- * and 1 otherwise. Patch levels are compared as strings.  */
-static gpg_error_t
-cmp_version (const char *a, const char *b, int *result)
-{
-  int a_major, b_major;
-  int a_minor, b_minor;
-  int a_micro, b_micro;
-  const char *a_patch, *b_patch;
-
-  if (!a || !b || !result)
-    return gpg_error (GPG_ERR_EINVAL);
-
-  a_patch = parse_version_string (a, &a_major, &a_minor, &a_micro);
-  b_patch = parse_version_string (b, &b_major, &b_minor, &b_micro);
-
-  if (!a_patch || !b_patch)
-    return gpg_error (GPG_ERR_EINVAL);
-
-  if (a_major == b_major)
-    {
-      if (a_minor == b_minor)
-        {
-          if (a_micro == b_micro)
-            *result = strcmp (a_patch, b_patch);
-          else
-            *result = a_micro - b_minor;
-        }
-      else
-        *result = a_minor - b_minor;
-    }
-  else
-    *result = a_major - b_major;
-
-  return 0;
-}
-
-
-static gpg_error_t
-fetch_into_tmpdir (ctrl_t ctrl, const char *url, estream_t *strm_out,
-                   char **path)
-{
-  gpg_error_t err;
-  char *filename = NULL;
-  char *dirname = NULL;
-  estream_t file = NULL;
-  estream_t strm = NULL;
-  size_t len, nwritten;
-  char buf[1024];
-
-  if (!strm_out || !path || !url)
-    {
-      err = gpg_error (GPG_ERR_INV_ARG);
-      goto leave;
-    }
-
-  dirname = my_mktmpdir ();
-  if (!dirname)
-    {
-      err = gpg_error_from_syserror ();
-      goto leave;
-    }
-
-  filename = strconcat (dirname, DIRSEP_S, "file", NULL);
-  if (!filename)
-    {
-      err = gpg_error_from_syserror ();
-      goto leave;
-    }
-
-  file = es_fopen (filename, "w+");
-  if (!file)
-    {
-      err = gpg_error_from_syserror ();
-      goto leave;
-    }
-
-  if ((err = ks_http_fetch (ctrl, url, &strm)))
-    goto leave;
-
-  for (;;)
-    {
-      if (es_read (strm, buf, sizeof buf, &len))
-        {
-          err = gpg_error_from_syserror ();
-          log_error ("error reading '%s': %s\n",
-                     es_fname_get (strm), gpg_strerror (err));
-          goto leave;
-        }
-
-      if (!len)
-        break;
-      if (es_write (file, buf, len, &nwritten))
-        {
-          err = gpg_error_from_syserror ();
-          log_error ("error writing '%s': %s\n", filename, gpg_strerror (err));
-          goto leave;
-        }
-      else if (len != nwritten)
-        {
-          err = gpg_error (GPG_ERR_EIO);
-          log_error ("error writing '%s': %s\n", filename, "short write");
-          goto leave;
-        }
-    }
-
-  es_rewind (file);
-  *strm_out = file;
-  file = NULL;
-
-  if (path)
-    {
-      *path = dirname;
-      dirname = NULL;
-    }
-
- leave:
-  es_fclose (file);
-  es_fclose (strm);
-  xfree (dirname);
-  xfree (filename);
-  return err;
-}
-
-
-struct verify_swdb_parm_s
-{
-  time_t sigtime;
-  int anyvalid;
-};
-
-static void
-verify_swdb_status_cb (void *opaque, const char *keyword, char *args)
-{
-  struct verify_swdb_parm_s *parm = opaque;
-
-  /* We care only about the first valid signature.  */
-  if (!strcmp (keyword, "VALIDSIG") && !parm->anyvalid)
-    {
-      char *fields[3];
-
-      parm->anyvalid = 1;
-      if (split_fields (args, fields, DIM (fields)) >= 3)
-        parm->sigtime = parse_timestamp (fields[2], NULL);
-    }
-}
-
-
-static const char hlp_versioncheck[] =
-  "VERSIONCHECK <name> <version>"
-  "\n"
-  "Checks the internet to find whenever a new program version is available."
-  "\n"
-  "<name> program name i.e. \"gnupg\"\n"
-  "<version> current version of the program i.e. \"2.0.2\"";
-static gpg_error_t
-cmd_versioncheck (assuan_context_t ctx, char *line)
-{
-  gpg_error_t err;
-
-  char *name;
-  char *version;
-  size_t name_len;
-  char *cmd_fields[2];
-
-  ctrl_t ctrl;
-  estream_t swdb = NULL;
-  estream_t swdb_sig = NULL;
-  char* swdb_dir = NULL;
-  char* swdb_sig_dir = NULL;
-  char* buf = NULL;
-  size_t len = 0;
-  ccparray_t ccp;
-  const char **argv = NULL;
-  char keyring_name[128];
-  char swdb_name[128];
-  char swdb_sig_name[128];
-
-  struct verify_swdb_parm_s verify_swdb_parm = { (time_t)(-1), 0 };
-
-
-  swdb_name[0] = 0;
-  swdb_sig_name[0] = 0;
-  ctrl = assuan_get_pointer (ctx);
-
-  if (split_fields (line, cmd_fields, 2) != 2)
-    {
-      err = set_error (GPG_ERR_ASS_PARAMETER,
-                       "No program name and/or version given");
-      goto out;
-    }
-
-  name = cmd_fields[0];
-  name_len = strlen (name);
-  version = cmd_fields[1];
-
-  if ((err = fetch_into_tmpdir (ctrl, "https://versions.gnupg.org/swdb.lst",
-                                &swdb, &swdb_dir)))
-    goto out;
-
-  snprintf (swdb_name, sizeof swdb_name, "%s%s%s", swdb_dir, DIRSEP_S, "file");
-
-  if ((err = fetch_into_tmpdir (ctrl, "https://versions.gnupg.org/swdb.lst.sig",
-                                &swdb_sig, &swdb_sig_dir)))
-    goto out;
-
-  snprintf (keyring_name, sizeof keyring_name, "%s%s%s", gnupg_datadir (),
-           DIRSEP_S, "distsigkey.gpg");
-  snprintf (swdb_sig_name, sizeof swdb_sig_name, "%s%s%s", swdb_sig_dir,
-           DIRSEP_S, "file");
-
-  ccparray_init (&ccp, 0);
-  ccparray_put (&ccp, "--status-fd=2");
-  ccparray_put (&ccp, "--keyring");
-  ccparray_put (&ccp, keyring_name);
-  ccparray_put (&ccp, "--");
-  ccparray_put (&ccp, swdb_sig_name);
-  ccparray_put (&ccp, "-");
-  ccparray_put (&ccp, NULL);
-  argv = ccparray_get (&ccp, NULL);
-  if (!argv)
-    {
-      err = gpg_error_from_syserror ();
-      goto out;
-    }
-
-  if ((err = gnupg_exec_tool_stream (gnupg_module_name (GNUPG_MODULE_NAME_GPGV),
-                                     argv, swdb, NULL, NULL,
-                                     verify_swdb_status_cb, &verify_swdb_parm)))
-    goto out;
-  if (verify_swdb_parm.sigtime == (time_t)(-1))
-    {
-      if (verify_swdb_parm.anyvalid)
-        err = gpg_error (GPG_ERR_BAD_SIGNATURE);
-      else
-        err = gpg_error (GPG_ERR_INV_TIME);
-      goto out;
-    }
-
-  {
-    gnupg_isotime_t tbuf;
-
-    epoch2isotime (tbuf, verify_swdb_parm.sigtime);
-    log_debug ("swdb created: %s\n", tbuf);
-  }
-
-  es_fseek (swdb, 0, SEEK_SET);
-
-  while (es_getline (&buf, &len, swdb) > 0)
-    {
-      if (len > name_len + 5 &&
-          strncmp (buf, name, name_len) == 0 &&
-          strncmp (buf + name_len, "_ver ", 5) == 0)
-        {
-          const char* this_ver_start = buf + name_len + 5;
-          char* this_ver_end = strchr (this_ver_start, '\n');
-          int cmp;
-
-          if (this_ver_end)
-            *this_ver_end = 0;
-
-          err = assuan_write_status (ctx, "LINE", buf);
-
-          err = cmp_version (this_ver_start, version, &cmp);
-          if (err > 0)
-            goto out;
-
-          if (cmp < 0)
-            err = assuan_send_data (ctx, "ROLLBACK", strlen ("ROLLBACK"));
-          else if (cmp == 0)
-            err = assuan_send_data (ctx, "CURRENT", strlen ("CURRENT"));
-          else
-            err = assuan_send_data (ctx, "UPDATE", strlen ("UPDATE"));
-
-          goto out;
-        }
-    }
-
-  err = assuan_send_data (ctx, "NOT_FOUND", strlen ("NOT_FOUND"));
-
- out:
-  es_fclose (swdb);
-  es_fclose (swdb_sig);
-  xfree (buf);
-
-  if (strlen (swdb_name) > 0)
-    remove (swdb_name);
-  if (swdb_dir)
-    rmdir (swdb_dir);
-  xfree (swdb_dir);
-
-  if (strlen (swdb_sig_name) > 0)
-    remove (swdb_sig_name);
-  if (swdb_sig_dir)
-    rmdir (swdb_sig_dir);
-  xfree (swdb_sig_dir);
-  xfree (argv);
-
-  return leave_cmd (ctx, err);
-}
-
-
-\f
 /* Tell the assuan library about our commands. */
 static int
 register_commands (assuan_context_t ctx)
@@ -2751,9 +2383,9 @@ register_commands (assuan_context_t ctx)
     { "KS_FETCH",   cmd_ks_fetch,   hlp_ks_fetch },
     { "KS_PUT",     cmd_ks_put,     hlp_ks_put },
     { "GETINFO",    cmd_getinfo,    hlp_getinfo },
+    { "LOADSWDB",   cmd_loadswdb,   hlp_loadswdb },
     { "KILLDIRMNGR",cmd_killdirmngr,hlp_killdirmngr },
     { "RELOADDIRMNGR",cmd_reloaddirmngr,hlp_reloaddirmngr },
-    { "VERSIONCHECK",cmd_versioncheck,hlp_versioncheck },
     { NULL, NULL }
   };
   int i, j, rc;