dirmngr: New configure option --disable-libdns.
[gnupg.git] / sm / keydb.c
index 52f40f1..44dd9ca 100644 (file)
@@ -1,11 +1,12 @@
 /* keydb.c - key database dispatcher
- * Copyright (C) 2001 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2014 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 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,
@@ -14,8 +15,7 @@
  * 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 <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 
 #include "gpgsm.h"
 #include "../kbx/keybox.h"
-#include "keydb.h" 
+#include "keydb.h"
 #include "i18n.h"
 
-#define DIRSEP_C '/'
-
 static int active_handles;
 
 typedef enum {
@@ -49,15 +47,20 @@ struct resource_item {
     KEYBOX_HANDLE kr;
   } u;
   void *token;
-  int secret;
+  dotlock_t lockhandle;
 };
 
 static struct resource_item all_resources[MAX_KEYDB_RESOURCES];
 static int used_resources;
 
+/* Whether we have successfully registered any resource.  */
+static int any_registered;
+
+
 struct keydb_handle {
   int locked;
   int found;
+  int saved_found;
   int current;
   int is_ephemeral;
   int used; /* items in active */
@@ -69,29 +72,202 @@ static int lock_all (KEYDB_HANDLE hd);
 static void unlock_all (KEYDB_HANDLE hd);
 
 
+static void
+try_make_homedir (const char *fname)
+{
+  const char *defhome = standard_homedir ();
+
+  /* Create the directory only if the supplied directory name is the
+     same as the default one.  This way we avoid to create arbitrary
+     directories when a non-default home directory is used.  To cope
+     with HOME, we do compare only the suffix if we see that the
+     default homedir does start with a tilde.  */
+  if ( opt.dry_run || opt.no_homedir_creation )
+    return;
+
+  if (
+#ifdef HAVE_W32_SYSTEM
+      ( !compare_filenames (fname, defhome) )
+#else
+      ( *defhome == '~'
+        && (strlen(fname) >= strlen (defhome+1)
+            && !strcmp(fname+strlen(fname)-strlen(defhome+1), defhome+1 ) ))
+      || (*defhome != '~'  && !compare_filenames( fname, defhome ) )
+#endif
+      )
+    {
+      if (gnupg_mkdir (fname, "-rwx"))
+        log_info (_("can't create directory '%s': %s\n"),
+                  fname, strerror(errno) );
+      else if (!opt.quiet )
+        log_info (_("directory '%s' created\n"), fname);
+    }
+}
+
+
+/* Handle the creation of a keybox if it does not yet exist.  Take
+   into acount that other processes might have the keybox already
+   locked.  This lock check does not work if the directory itself is
+   not yet available.  If R_CREATED is not NULL it will be set to true
+   if the function created a new keybox.  */
+static gpg_error_t
+maybe_create_keybox (char *filename, int force, int *r_created)
+{
+  dotlock_t lockhd = NULL;
+  FILE *fp;
+  int rc;
+  mode_t oldmask;
+  char *last_slash_in_filename;
+  int save_slash;
+
+  if (r_created)
+    *r_created = 0;
+
+  /* A quick test whether the filename already exists. */
+  if (!access (filename, F_OK))
+    return !access (filename, R_OK)? 0 : gpg_error (GPG_ERR_EACCES);
+
+  /* If we don't want to create a new file at all, there is no need to
+     go any further - bail out right here.  */
+  if (!force)
+    return gpg_error (GPG_ERR_ENOENT);
+
+  /* First of all we try to create the home directory.  Note, that we
+     don't do any locking here because any sane application of gpg
+     would create the home directory by itself and not rely on gpg's
+     tricky auto-creation which is anyway only done for some home
+     directory name patterns. */
+  last_slash_in_filename = strrchr (filename, DIRSEP_C);
+#if HAVE_W32_SYSTEM
+  {
+    /* Windows may either have a slash or a backslash.  Take care of it.  */
+    char *p = strrchr (filename, '/');
+    if (!last_slash_in_filename || p > last_slash_in_filename)
+      last_slash_in_filename = p;
+  }
+#endif /*HAVE_W32_SYSTEM*/
+  if (!last_slash_in_filename)
+    return gpg_error (GPG_ERR_ENOENT);  /* No slash at all - should
+                                           not happen though.  */
+  save_slash = *last_slash_in_filename;
+  *last_slash_in_filename = 0;
+  if (access(filename, F_OK))
+    {
+      static int tried;
+
+      if (!tried)
+        {
+          tried = 1;
+          try_make_homedir (filename);
+        }
+      if (access (filename, F_OK))
+        {
+          rc = gpg_error_from_syserror ();
+          *last_slash_in_filename = save_slash;
+          goto leave;
+        }
+    }
+  *last_slash_in_filename = save_slash;
+
+  /* To avoid races with other instances of gpg trying to create or
+     update the keybox (it is removed during an update for a short
+     time), we do the next stuff in a locked state. */
+  lockhd = dotlock_create (filename, 0);
+  if (!lockhd)
+    {
+      /* A reason for this to fail is that the directory is not
+         writable. However, this whole locking stuff does not make
+         sense if this is the case. An empty non-writable directory
+         with no keyring is not really useful at all. */
+      if (opt.verbose)
+        log_info ("can't allocate lock for '%s'\n", filename );
+
+      if (!force)
+        return gpg_error (GPG_ERR_ENOENT);
+      else
+        return gpg_error (GPG_ERR_GENERAL);
+    }
+
+  if ( dotlock_take (lockhd, -1) )
+    {
+      /* This is something bad.  Probably a stale lockfile.  */
+      log_info ("can't lock '%s'\n", filename);
+      rc = gpg_error (GPG_ERR_GENERAL);
+      goto leave;
+    }
+
+  /* Now the real test while we are locked. */
+  if (!access(filename, F_OK))
+    {
+      rc = 0;  /* Okay, we may access the file now.  */
+      goto leave;
+    }
+
+  /* The file does not yet exist, create it now. */
+  oldmask = umask (077);
+  fp = fopen (filename, "w");
+  if (!fp)
+    {
+      rc = gpg_error_from_syserror ();
+      umask (oldmask);
+      log_error (_("error creating keybox '%s': %s\n"),
+                 filename, gpg_strerror (rc));
+      goto leave;
+    }
+  umask (oldmask);
+
+  /* Make sure that at least one record is in a new keybox file, so
+     that the detection magic for OpenPGP keyboxes works the next time
+     it is used.  */
+  rc = _keybox_write_header_blob (fp, 0);
+  if (rc)
+    {
+      fclose (fp);
+      log_error (_("error creating keybox '%s': %s\n"),
+                 filename, gpg_strerror (rc));
+      goto leave;
+    }
+
+  if (!opt.quiet)
+    log_info (_("keybox '%s' created\n"), filename);
+  if (r_created)
+    *r_created = 1;
+
+  fclose (fp);
+  rc = 0;
+
+ leave:
+  if (lockhd)
+    {
+      dotlock_release (lockhd);
+      dotlock_destroy (lockhd);
+    }
+  return rc;
+}
+
+
 /*
  * Register a resource (which currently may only be a keybox file).
- * The first keybox which is added by this function is
- * created if it does not exist.
- * Note: this function may be called before secure memory is
- * available.
+ * The first keybox which is added by this function is created if it
+ * does not exist.  If AUTO_CREATED is not NULL it will be set to true
+ * if the function has created a new keybox.
  */
-int
-keydb_add_resource (const char *url, int force, int secret)
+gpg_error_t
+keydb_add_resource (ctrl_t ctrl, const char *url, int force, int *auto_created)
 {
-  static int any_secret, any_public;
   const char *resname = url;
   char *filename = NULL;
-  int rc = 0; 
-  FILE *fp;
+  gpg_error_t err = 0;
   KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
-  const char *created_fname = NULL;
+
+  if (auto_created)
+    *auto_created = 0;
 
   /* Do we have an URL?
      gnupg-kbx:filename := this is a plain keybox
      filename := See what is is, but create as plain keybox.
   */
-  if (strlen (resname) > 10) 
+  if (strlen (resname) > 10)
     {
       if (!strncmp (resname, "gnupg-kbx:", 10) )
         {
@@ -101,8 +277,8 @@ keydb_add_resource (const char *url, int force, int secret)
 #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
       else if (strchr (resname, ':'))
         {
-          log_error ("invalid key resource URL `%s'\n", url );
-          rc = GNUPG_General_Error;
+          log_error ("invalid key resource URL '%s'\n", url );
+          err = gpg_error (GPG_ERR_GENERAL);
           goto leave;
        }
 #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
@@ -113,140 +289,125 @@ keydb_add_resource (const char *url, int force, int secret)
       if (strchr(resname, DIRSEP_C) )
         filename = make_filename (resname, NULL);
       else
-        filename = make_filename (opt.homedir, resname, NULL);
+        filename = make_filename (gnupg_homedir (), resname, NULL);
     }
   else
     filename = xstrdup (resname);
-  
+
   if (!force)
-    force = secret? !any_secret : !any_public;
-  
+    force = !any_registered;
+
   /* see whether we can determine the filetype */
   if (rt == KEYDB_RESOURCE_TYPE_NONE)
     {
-      FILE *fp2 = fopen( filename, "rb" );
-      
-      if (fp2) {
-        u32 magic;
-        
-        /* FIXME: check for the keybox magic */
-        if (fread( &magic, 4, 1, fp2) == 1 ) 
-          {
-            if (magic == 0x13579ace || magic == 0xce9a5713)
-              ; /* GDBM magic - no more support */
-            else
-              rt = KEYDB_RESOURCE_TYPE_KEYBOX;
-          }
-        else /* maybe empty: assume ring */
-          rt = KEYDB_RESOURCE_TYPE_KEYBOX;
-        fclose (fp2);
-      }
-      else /* no file yet: create ring */
+      FILE *fp = fopen( filename, "rb" );
+
+      if (fp)
+        {
+          u32 magic;
+
+          /* FIXME: check for the keybox magic */
+          if (fread (&magic, 4, 1, fp) == 1 )
+            {
+              if (magic == 0x13579ace || magic == 0xce9a5713)
+                ; /* GDBM magic - no more support */
+              else
+                rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+            }
+          else /* maybe empty: assume keybox */
+            rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+          fclose (fp);
+        }
+      else /* no file yet: create keybox */
         rt = KEYDB_RESOURCE_TYPE_KEYBOX;
     }
-    
+
   switch (rt)
     {
     case KEYDB_RESOURCE_TYPE_NONE:
-      log_error ("unknown type of key resource `%s'\n", url );
-      rc = GNUPG_General_Error;
+      log_error ("unknown type of key resource '%s'\n", url );
+      err = gpg_error (GPG_ERR_GENERAL);
       goto leave;
-      
+
     case KEYDB_RESOURCE_TYPE_KEYBOX:
-      fp = fopen (filename, "rb");
-      if (!fp && !force)
-        {
-          rc = GNUPG_File_Open_Error;
-          goto leave;
-        }
-      
-      if (!fp)
-        { /* no file */
-#if 0 /* no autocreate of the homedirectory yet */
+      err = maybe_create_keybox (filename, force, auto_created);
+      if (err)
+        goto leave;
+      /* Now register the file */
+      {
+        void *token;
+
+        err = keybox_register_file (filename, 0, &token);
+        if (gpg_err_code (err) == GPG_ERR_EEXIST)
+          ; /* Already registered - ignore.  */
+        else if (err)
+          ; /* Other error.  */
+        else if (used_resources >= MAX_KEYDB_RESOURCES)
+          err = gpg_error (GPG_ERR_RESOURCE_LIMIT);
+        else
           {
-            char *last_slash_in_filename;
-            
-            last_slash_in_filename = strrchr (filename, DIRSEP_C);
-            *last_slash_in_filename = 0;
-            if (access (filename, F_OK))
-              { /* on the first time we try to create the default
-                   homedir and in this case the process will be
-                   terminated, so that on the next invocation can
-                   read the options file in on startup */
-                try_make_homedir (filename);
-                rc = GNUPG_File_Open_Error;
-                *last_slash_in_filename = DIRSEP_C;
-                goto leave;
+            all_resources[used_resources].type = rt;
+            all_resources[used_resources].u.kr = NULL; /* Not used here */
+            all_resources[used_resources].token = token;
+
+            all_resources[used_resources].lockhandle
+              = dotlock_create (filename, 0);
+            if (!all_resources[used_resources].lockhandle)
+              log_fatal ( _("can't create lock for '%s'\n"), filename);
+
+            /* Do a compress run if needed and the file is not locked. */
+            if (!dotlock_take (all_resources[used_resources].lockhandle, 0))
+              {
+                KEYBOX_HANDLE kbxhd = keybox_new_x509 (token, 0);
+
+                if (kbxhd)
+                  {
+                    keybox_compress (kbxhd);
+                    keybox_release (kbxhd);
+                  }
+                dotlock_release (all_resources[used_resources].lockhandle);
               }
-            *last_slash_in_filename = DIRSEP_C;
+
+            used_resources++;
           }
-#endif
-          fp = fopen (filename, "w");
-          if (!fp)
-            {
-              log_error (_("error creating keybox `%s': %s\n"),
-                         filename, strerror(errno));
-              rc = GNUPG_File_Create_Error;
-              goto leave;
-           }
+      }
+      break;
 
-          if (!opt.quiet)
-            log_info (_("keybox `%s' created\n"), filename);
-          created_fname = filename;
-       }
-       fclose (fp);
-       fp = NULL;
-        /* now regsiter the file */
-        {
-          void *token = keybox_register_file (filename, secret);
-          if (!token)
-            ; /* already registered - ignore it */
-          else if (used_resources >= MAX_KEYDB_RESOURCES)
-            rc = GNUPG_Resource_Limit;
-          else 
-            {
-              all_resources[used_resources].type = rt;
-              all_resources[used_resources].u.kr = NULL; /* Not used here */
-              all_resources[used_resources].token = token;
-              all_resources[used_resources].secret = secret;
-              used_resources++;
-            }
-        }
-       break;
     default:
-      log_error ("resource type of `%s' not supported\n", url);
-      rc = GNUPG_Not_Supported;
+      log_error ("resource type of '%s' not supported\n", url);
+      err = gpg_error (GPG_ERR_NOT_SUPPORTED);
       goto leave;
     }
 
   /* fixme: check directory permissions and print a warning */
 
  leave:
-  if (rc)
-    log_error ("keyblock resource `%s': %s\n", filename, gnupg_strerror(rc));
-  else if (secret)
-    any_secret = 1;
+  if (err)
+    {
+      log_error ("keyblock resource '%s': %s\n", filename, gpg_strerror (err));
+      gpgsm_status_with_error (ctrl, STATUS_ERROR,
+                               "add_keyblock_resource", err);
+    }
   else
-    any_public = 1;
+    any_registered = 1;
   xfree (filename);
-  return rc;
+  return err;
 }
 
 
 KEYDB_HANDLE
-keydb_new (int secret)
+keydb_new (void)
 {
   KEYDB_HANDLE hd;
   int i, j;
-  
+
   hd = xcalloc (1, sizeof *hd);
   hd->found = -1;
-  
+  hd->saved_found = -1;
+
   assert (used_resources <= MAX_KEYDB_RESOURCES);
   for (i=j=0; i < used_resources; i++)
     {
-      if (!all_resources[i].secret != !secret)
-        continue;
       switch (all_resources[i].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
@@ -254,27 +415,28 @@ keydb_new (int secret)
         case KEYDB_RESOURCE_TYPE_KEYBOX:
           hd->active[j].type   = all_resources[i].type;
           hd->active[j].token  = all_resources[i].token;
-          hd->active[j].secret = all_resources[i].secret;
-          hd->active[j].u.kr = keybox_new (all_resources[i].token, secret);
-          if (!hd->active[j].u.kr) {
-            xfree (hd);
-            return NULL; /* fixme: release all previously allocated handles*/
-          }
+          hd->active[j].lockhandle = all_resources[i].lockhandle;
+          hd->active[j].u.kr = keybox_new_x509 (all_resources[i].token, 0);
+          if (!hd->active[j].u.kr)
+            {
+              xfree (hd);
+              return NULL; /* fixme: release all previously allocated handles*/
+            }
           j++;
           break;
         }
     }
   hd->used = j;
-  
+
   active_handles++;
   return hd;
 }
 
-void 
+void
 keydb_release (KEYDB_HANDLE hd)
 {
   int i;
-  
+
   if (!hd)
     return;
   assert (active_handles > 0);
@@ -283,7 +445,7 @@ keydb_release (KEYDB_HANDLE hd)
   unlock_all (hd);
   for (i=0; i < hd->used; i++)
     {
-      switch (hd->active[i].type) 
+      switch (hd->active[i].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE:
           break;
@@ -308,31 +470,31 @@ keydb_get_resource_name (KEYDB_HANDLE hd)
 {
   int idx;
   const char *s = NULL;
-  
-  if (!hd) 
+
+  if (!hd)
     return NULL;
 
-  if ( hd->found >= 0 && hd->found < hd->used) 
+  if ( hd->found >= 0 && hd->found < hd->used)
     idx = hd->found;
-  else if ( hd->current >= 0 && hd->current < hd->used) 
+  else if ( hd->current >= 0 && hd->current < hd->used)
     idx = hd->current;
   else
     idx = 0;
 
-  switch (hd->active[idx].type) 
+  switch (hd->active[idx].type)
     {
     case KEYDB_RESOURCE_TYPE_NONE:
-      s = NULL; 
+      s = NULL;
       break;
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       s = keybox_get_resource_name (hd->active[idx].u.kr);
       break;
     }
-  
+
   return s? s: "";
 }
 
-/* Switch the handle into ephemeral mode and return the orginal value. */
+/* Switch the handle into ephemeral mode and return the original value. */
 int
 keydb_set_ephemeral (KEYDB_HANDLE hd, int yes)
 {
@@ -346,7 +508,7 @@ keydb_set_ephemeral (KEYDB_HANDLE hd, int yes)
     {
       for (i=0; i < hd->used; i++)
         {
-          switch (hd->active[i].type) 
+          switch (hd->active[i].type)
             {
             case KEYDB_RESOURCE_TYPE_NONE:
               break;
@@ -356,42 +518,64 @@ keydb_set_ephemeral (KEYDB_HANDLE hd, int yes)
             }
         }
     }
-      
+
   i = hd->is_ephemeral;
   hd->is_ephemeral = yes;
   return i;
 }
 
 
+/* If the keyring has not yet been locked, lock it now.  This
+   operation is required before any update operation; it is optional
+   for an insert operation.  The lock is released with
+   keydb_released. */
+gpg_error_t
+keydb_lock (KEYDB_HANDLE hd)
+{
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_HANDLE);
+  if (hd->locked)
+    return 0; /* Already locked. */
+  return lock_all (hd);
+}
+
+
 \f
-static int 
+static int
 lock_all (KEYDB_HANDLE hd)
 {
   int i, rc = 0;
 
-  for (i=0; !rc && i < hd->used; i++) 
+  /* Fixme: This locking scheme may lead to deadlock if the resources
+     are not added in the same order by all processes.  We are
+     currently only allowing one resource so it is not a problem. */
+  for (i=0; i < hd->used; i++)
     {
-      switch (hd->active[i].type) 
+      switch (hd->active[i].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE:
           break;
         case KEYDB_RESOURCE_TYPE_KEYBOX:
-          /* FIXME  rc = keybox_lock (hd->active[i].u.kr, 1);*/
+          if (hd->active[i].lockhandle)
+            rc = dotlock_take (hd->active[i].lockhandle, -1);
           break;
         }
+      if (rc)
+        break;
     }
 
-    if (rc) 
+    if (rc)
       {
         /* revert the already set locks */
-        for (i--; i >= 0; i--) 
+        for (i--; i >= 0; i--)
           {
-            switch (hd->active[i].type) 
+            switch (hd->active[i].type)
               {
               case KEYDB_RESOURCE_TYPE_NONE:
                 break;
               case KEYDB_RESOURCE_TYPE_KEYBOX:
-                /* Fixme: keybox_lock (hd->active[i].u.kr, 0);*/
+                if (hd->active[i].lockhandle)
+                  dotlock_release (hd->active[i].lockhandle);
                 break;
               }
           }
@@ -399,225 +583,233 @@ lock_all (KEYDB_HANDLE hd)
     else
       hd->locked = 1;
 
-    return rc;
+    /* make_dotlock () does not yet guarantee that errno is set, thus
+       we can't rely on the error reason and will simply use
+       EACCES. */
+    return rc? gpg_error (GPG_ERR_EACCES) : 0;
 }
 
 static void
 unlock_all (KEYDB_HANDLE hd)
 {
   int i;
-  
+
   if (!hd->locked)
     return;
 
-  for (i=hd->used-1; i >= 0; i--) 
+  for (i=hd->used-1; i >= 0; i--)
     {
-      switch (hd->active[i].type) 
+      switch (hd->active[i].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE:
           break;
         case KEYDB_RESOURCE_TYPE_KEYBOX:
-          /* fixme: keybox_lock (hd->active[i].u.kr, 0);*/
+          if (hd->active[i].lockhandle)
+            dotlock_release (hd->active[i].lockhandle);
           break;
         }
     }
   hd->locked = 0;
 }
 
+
 \f
-#if 0
-/*
- * Return the last found keybox.  Caller must free it.
- * The returned keyblock has the kbode flag bit 0 set for the node with
- * the public key used to locate the keyblock or flag bit 1 set for 
- * the user ID node.
- */
-int
-keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
+/* Push the last found state if any.  */
+void
+keydb_push_found_state (KEYDB_HANDLE hd)
 {
-    int rc = 0;
-
-    if (!hd)
-        return G10ERR_INV_ARG;
-
-    if ( hd->found < 0 || hd->found >= hd->used) 
-        return -1; /* nothing found */
+  if (!hd)
+    return;
 
-    switch (hd->active[hd->found].type) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-        rc = G10ERR_GENERAL; /* oops */
-        break;
-      case KEYDB_RESOURCE_TYPE_KEYBOX:
-        rc = keybox_get_keyblock (hd->active[hd->found].u.kr, ret_kb);
-        break;
+  if (hd->found < 0 || hd->found >= hd->used)
+    {
+      hd->saved_found = -1;
+      return;
     }
 
-    return rc;
-}
-
-/* 
- * update the current keyblock with KB
- */
-int
-keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb)
-{
-    int rc = 0;
-
-    if (!hd)
-        return G10ERR_INV_ARG;
-
-    if ( hd->found < 0 || hd->found >= hd->used) 
-        return -1; /* nothing found */
-
-    if( opt.dry_run )
-       return 0;
-
-    rc = lock_all (hd);
-    if (rc)
-        return rc;
-
-    switch (hd->active[hd->found].type) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-        rc = G10ERR_GENERAL; /* oops */
-        break;
-      case KEYDB_RESOURCE_TYPE_KEYBOX:
-        rc = keybox_update_keyblock (hd->active[hd->found].u.kr, kb);
-        break;
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      keybox_push_found_state (hd->active[hd->found].u.kr);
+      break;
     }
 
-    unlock_all (hd);
-    return rc;
+  hd->saved_found = hd->found;
+  hd->found = -1;
 }
 
 
-/* 
- * Insert a new KB into one of the resources. 
- */
-int
-keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb)
+/* Pop the last found state.  */
+void
+keydb_pop_found_state (KEYDB_HANDLE hd)
 {
-    int rc = -1;
-    int idx;
-
-    if (!hd) 
-        return G10ERR_INV_ARG;
-
-    if( opt.dry_run )
-       return 0;
-
-    if ( hd->found >= 0 && hd->found < hd->used) 
-        idx = hd->found;
-    else if ( hd->current >= 0 && hd->current < hd->used) 
-        idx = hd->current;
-    else
-        return G10ERR_GENERAL;
+  if (!hd)
+    return;
 
-    rc = lock_all (hd);
-    if (rc)
-        return rc;
+  hd->found = hd->saved_found;
+  hd->saved_found = -1;
+  if (hd->found < 0 || hd->found >= hd->used)
+    return;
 
-    switch (hd->active[idx].type) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-        rc = G10ERR_GENERAL; /* oops */
-        break;
-      case KEYDB_RESOURCE_TYPE_KEYBOX:
-        rc = keybox_insert_keyblock (hd->active[idx].u.kr, kb);
-        break;
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      keybox_pop_found_state (hd->active[hd->found].u.kr);
+      break;
     }
-
-    unlock_all (hd);
-    return rc;
 }
 
-#endif /*disabled code*/
-
 
 \f
 /*
-  Return the last found keybox.  Caller must free it.  The returned
+  Return the last found object.  Caller must free it.  The returned
   keyblock has the kbode flag bit 0 set for the node with the public
   key used to locate the keyblock or flag bit 1 set for the user ID
   node.  */
 int
-keydb_get_cert (KEYDB_HANDLE hd, KsbaCert *r_cert)
+keydb_get_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert)
 {
   int rc = 0;
 
   if (!hd)
-    return GNUPG_Invalid_Value;
-  
-  if ( hd->found < 0 || hd->found >= hd->used) 
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if ( hd->found < 0 || hd->found >= hd->used)
     return -1; /* nothing found */
-  
-  switch (hd->active[hd->found].type) 
+
+  switch (hd->active[hd->found].type)
     {
     case KEYDB_RESOURCE_TYPE_NONE:
-      rc = GNUPG_General_Error; /* oops */
+      rc = gpg_error (GPG_ERR_GENERAL); /* oops */
       break;
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       rc = keybox_get_cert (hd->active[hd->found].u.kr, r_cert);
       break;
     }
-  
+
   return rc;
 }
 
-/* 
- * Insert a new Certificate into one of the resources. 
+/* Return a flag of the last found object. WHICH is the flag requested;
+   it should be one of the KEYBOX_FLAG_ values.  If the operation is
+   successful, the flag value will be stored at the address given by
+   VALUE.  Return 0 on success or an error code. */
+gpg_error_t
+keydb_get_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int *value)
+{
+  int err = 0;
+
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if ( hd->found < 0 || hd->found >= hd->used)
+    return gpg_error (GPG_ERR_NOTHING_FOUND);
+
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      err = gpg_error (GPG_ERR_GENERAL); /* oops */
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      err = keybox_get_flags (hd->active[hd->found].u.kr, which, idx, value);
+      break;
+    }
+
+  return err;
+}
+
+/* Set a flag of the last found object. WHICH is the flag to be set; it
+   should be one of the KEYBOX_FLAG_ values.  If the operation is
+   successful, the flag value will be stored in the keybox.  Note,
+   that some flag values can't be updated and thus may return an
+   error, some other flag values may be masked out before an update.
+   Returns 0 on success or an error code. */
+gpg_error_t
+keydb_set_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int value)
+{
+  int err = 0;
+
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if ( hd->found < 0 || hd->found >= hd->used)
+    return gpg_error (GPG_ERR_NOTHING_FOUND);
+
+  if (!hd->locked)
+    return gpg_error (GPG_ERR_NOT_LOCKED);
+
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      err = gpg_error (GPG_ERR_GENERAL); /* oops */
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      err = keybox_set_flags (hd->active[hd->found].u.kr, which, idx, value);
+      break;
+    }
+
+  return err;
+}
+
+/*
+ * Insert a new Certificate into one of the resources.
  */
 int
-keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert)
+keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert)
 {
   int rc = -1;
   int idx;
-  char digest[20];
-  
-  if (!hd) 
-    return GNUPG_Invalid_Value;
+  unsigned char digest[20];
+
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
 
   if (opt.dry_run)
     return 0;
-  
-  if ( hd->found >= 0 && hd->found < hd->used) 
+
+  if ( hd->found >= 0 && hd->found < hd->used)
     idx = hd->found;
-  else if ( hd->current >= 0 && hd->current < hd->used) 
+  else if ( hd->current >= 0 && hd->current < hd->used)
     idx = hd->current;
   else
-    return GNUPG_General_Error;
+    return gpg_error (GPG_ERR_GENERAL);
 
-  rc = lock_all (hd);
-  if (rc)
-    return rc;
+  if (!hd->locked)
+    return gpg_error (GPG_ERR_NOT_LOCKED);
 
   gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/
 
-  switch (hd->active[idx].type) 
+  switch (hd->active[idx].type)
     {
     case KEYDB_RESOURCE_TYPE_NONE:
-      rc = GNUPG_General_Error;
+      rc = gpg_error (GPG_ERR_GENERAL);
       break;
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       rc = keybox_insert_cert (hd->active[idx].u.kr, cert, digest);
       break;
     }
-  
+
   unlock_all (hd);
   return rc;
 }
 
 
 
-/* update the current keyblock with KB */
+/* Update the current keyblock with KB.  */
 int
-keydb_update_cert (KEYDB_HANDLE hd, KsbaCert cert)
+keydb_update_cert (KEYDB_HANDLE hd, ksba_cert_t cert)
 {
   int rc = 0;
-  char digest[20];
-  
+  unsigned char digest[20];
+
   if (!hd)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
 
-  if ( hd->found < 0 || hd->found >= hd->used) 
+  if ( hd->found < 0 || hd->found >= hd->used)
     return -1; /* nothing found */
 
   if (opt.dry_run)
@@ -629,10 +821,10 @@ keydb_update_cert (KEYDB_HANDLE hd, KsbaCert cert)
 
   gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/
 
-  switch (hd->active[hd->found].type) 
+  switch (hd->active[hd->found].type)
     {
     case KEYDB_RESOURCE_TYPE_NONE:
-      rc = GNUPG_General_Error; /* oops */
+      rc = gpg_error (GPG_ERR_GENERAL); /* oops */
       break;
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       rc = keybox_update_cert (hd->active[hd->found].u.kr, cert, digest);
@@ -644,38 +836,38 @@ keydb_update_cert (KEYDB_HANDLE hd, KsbaCert cert)
 }
 
 
-/* 
+/*
  * The current keyblock or cert will be deleted.
  */
 int
-keydb_delete (KEYDB_HANDLE hd)
+keydb_delete (KEYDB_HANDLE hd, int unlock)
 {
   int rc = -1;
-  
+
   if (!hd)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
 
-  if ( hd->found < 0 || hd->found >= hd->used) 
+  if ( hd->found < 0 || hd->found >= hd->used)
     return -1; /* nothing found */
 
   if( opt.dry_run )
     return 0;
 
-  rc = lock_all (hd);
-  if (rc)
-    return rc;
+  if (!hd->locked)
+    return gpg_error (GPG_ERR_NOT_LOCKED);
 
   switch (hd->active[hd->found].type)
     {
     case KEYDB_RESOURCE_TYPE_NONE:
-      rc = GNUPG_General_Error;
+      rc = gpg_error (GPG_ERR_GENERAL);
       break;
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       rc = keybox_delete (hd->active[hd->found].u.kr);
       break;
     }
 
-  unlock_all (hd);
+  if (unlock)
+    unlock_all (hd);
   return rc;
 }
 
@@ -684,23 +876,25 @@ keydb_delete (KEYDB_HANDLE hd)
 /*
  * Locate the default writable key resource, so that the next
  * operation (which is only relevant for inserts) will be done on this
- * resource.  
+ * resource.
  */
 int
 keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
 {
   int rc;
-  
+
+  (void)reserved;
+
   if (!hd)
-    return GNUPG_Invalid_Value;
-  
+    return gpg_error (GPG_ERR_INV_VALUE);
+
   rc = keydb_search_reset (hd); /* this does reset hd->current */
   if (rc)
     return rc;
-  
-  for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++) 
+
+  for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++)
     {
-      switch (hd->active[hd->current].type) 
+      switch (hd->active[hd->current].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE:
           BUG();
@@ -711,7 +905,7 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
           break;
         }
     }
-  
+
   return -1;
 }
 
@@ -722,11 +916,9 @@ void
 keydb_rebuild_caches (void)
 {
   int i;
-  
+
   for (i=0; i < used_resources; i++)
     {
-      if (all_resources[i].secret)
-        continue;
       switch (all_resources[i].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
@@ -743,23 +935,24 @@ keydb_rebuild_caches (void)
 
 
 
-/* 
+/*
  * Start the next search on this handle right at the beginning
  */
-int 
+gpg_error_t
 keydb_search_reset (KEYDB_HANDLE hd)
 {
-  int i, rc = 0;
-  
+  int i;
+  gpg_error_t rc = 0;
+
   if (!hd)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
 
-  hd->current = 0; 
+  hd->current = 0;
   hd->found = -1;
   /* and reset all resources */
-  for (i=0; !rc && i < hd->used; i++) 
+  for (i=0; !rc && i < hd->used; i++)
     {
-      switch (hd->active[i].type) 
+      switch (hd->active[i].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE:
           break;
@@ -768,487 +961,401 @@ keydb_search_reset (KEYDB_HANDLE hd)
           break;
         }
     }
-  return rc; /* fixme: we need to map error codes or share them with
-                all modules*/
+  return rc;
 }
 
-/* 
+/*
  * Search through all keydb resources, starting at the current position,
  * for a keyblock which contains one of the keys described in the DESC array.
  */
-int 
-keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc)
+int
+keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd,
+              KEYDB_SEARCH_DESC *desc, size_t ndesc)
 {
   int rc = -1;
-  
+  unsigned long skipped;
+
   if (!hd)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
 
-  while (rc == -1 && hd->current >= 0 && hd->current < hd->used) 
+  if (!any_registered)
     {
-      switch (hd->active[hd->current].type) 
+      gpgsm_status_with_error (ctrl, STATUS_ERROR, "keydb_search",
+                               gpg_error (GPG_ERR_KEYRING_OPEN));
+      return gpg_error (GPG_ERR_NOT_FOUND);
+    }
+
+  while (rc == -1 && hd->current >= 0 && hd->current < hd->used)
+    {
+      switch (hd->active[hd->current].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE:
           BUG(); /* we should never see it here */
           break;
         case KEYDB_RESOURCE_TYPE_KEYBOX:
-          rc = keybox_search (hd->active[hd->current].u.kr, desc, ndesc);
+          rc = keybox_search (hd->active[hd->current].u.kr, desc, ndesc,
+                              KEYBOX_BLOBTYPE_X509,
+                              NULL, &skipped);
           break;
         }
-      if (rc == -1) /* EOF -> switch to next resource */
-        hd->current++; 
+      if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
+        { /* EOF -> switch to next resource */
+          hd->current++;
+        }
       else if (!rc)
         hd->found = hd->current;
     }
-  
-  return rc; 
+
+  return rc;
 }
 
 
 int
-keydb_search_first (KEYDB_HANDLE hd)
+keydb_search_first (ctrl_t ctrl, KEYDB_HANDLE hd)
 {
   KEYDB_SEARCH_DESC desc;
-  
+
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_FIRST;
-  return keydb_search (hd, &desc, 1);
+  return keydb_search (ctrl, hd, &desc, 1);
 }
 
 int
-keydb_search_next (KEYDB_HANDLE hd)
+keydb_search_next (ctrl_t ctrl, KEYDB_HANDLE hd)
 {
   KEYDB_SEARCH_DESC desc;
-  
+
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_NEXT;
-  return keydb_search (hd, &desc, 1);
+  return keydb_search (ctrl, hd, &desc, 1);
 }
 
 int
-keydb_search_kid (KEYDB_HANDLE hd, u32 *kid)
+keydb_search_kid (ctrl_t ctrl, KEYDB_HANDLE hd, u32 *kid)
 {
   KEYDB_SEARCH_DESC desc;
-  
+
+  (void)kid;
+
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_LONG_KID;
-/*    desc.u.kid[0] = kid[0]; */
-/*    desc.u.kid[1] = kid[1]; */
-  return keydb_search (hd, &desc, 1);
+  desc.u.kid[0] = kid[0];
+  desc.u.kid[1] = kid[1];
+  return keydb_search (ctrl, hd, &desc, 1);
 }
 
 int
-keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr)
+keydb_search_fpr (ctrl_t ctrl, KEYDB_HANDLE hd, const byte *fpr)
 {
   KEYDB_SEARCH_DESC desc;
-  
+
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_FPR;
   memcpy (desc.u.fpr, fpr, 20);
-  return keydb_search (hd, &desc, 1);
+  return keydb_search (ctrl, hd, &desc, 1);
 }
 
 int
-keydb_search_issuer (KEYDB_HANDLE hd, const char *issuer)
+keydb_search_issuer (ctrl_t ctrl, KEYDB_HANDLE hd, const char *issuer)
 {
   KEYDB_SEARCH_DESC desc;
   int rc;
-  
+
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_ISSUER;
   desc.u.name = issuer;
-  rc = keydb_search (hd, &desc, 1);
+  rc = keydb_search (ctrl, hd, &desc, 1);
   return rc;
 }
 
 int
-keydb_search_issuer_sn (KEYDB_HANDLE hd,
-                        const char *issuer, KsbaConstSexp serial)
+keydb_search_issuer_sn (ctrl_t ctrl, KEYDB_HANDLE hd,
+                        const char *issuer, ksba_const_sexp_t serial)
 {
   KEYDB_SEARCH_DESC desc;
   int rc;
   const unsigned char *s;
-  
+
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_ISSUER_SN;
   s = serial;
   if (*s !='(')
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
   s++;
   for (desc.snlen = 0; digitp (s); s++)
     desc.snlen = 10*desc.snlen + atoi_1 (s);
   if (*s !=':')
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
   desc.sn = s+1;
   desc.u.name = issuer;
-  rc = keydb_search (hd, &desc, 1);
+  rc = keydb_search (ctrl, hd, &desc, 1);
   return rc;
 }
 
 int
-keydb_search_subject (KEYDB_HANDLE hd, const char *name)
+keydb_search_subject (ctrl_t ctrl, KEYDB_HANDLE hd, const char *name)
 {
   KEYDB_SEARCH_DESC desc;
   int rc;
-  
+
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_SUBJECT;
   desc.u.name = name;
-  rc = keydb_search (hd, &desc, 1);
+  rc = keydb_search (ctrl, hd, &desc, 1);
   return rc;
 }
 
 
-static int
-hextobyte (const unsigned char *s)
+\f
+/* Store the certificate in the key DB but make sure that it does not
+   already exists.  We do this simply by comparing the fingerprint.
+   If EXISTED is not NULL it will be set to true if the certificate
+   was already in the DB. */
+int
+keydb_store_cert (ctrl_t ctrl, ksba_cert_t cert, int ephemeral, int *existed)
 {
-  int c;
-
-  if( *s >= '0' && *s <= '9' )
-    c = 16 * (*s - '0');
-  else if ( *s >= 'A' && *s <= 'F' )
-    c = 16 * (10 + *s - 'A');
-  else if ( *s >= 'a' && *s <= 'f' )
-    c = 16 * (10 + *s - 'a');
-  else
-    return -1;
-  s++;
-  if ( *s >= '0' && *s <= '9' )
-    c += *s - '0';
-  else if ( *s >= 'A' && *s <= 'F' )
-    c += 10 + *s - 'A';
-  else if ( *s >= 'a' && *s <= 'f' )
-    c += 10 + *s - 'a';
-  else
-    return -1;
-  return c;
-}
+  KEYDB_HANDLE kh;
+  int rc;
+  unsigned char fpr[20];
 
+  if (existed)
+    *existed = 0;
 
-static int
-classify_user_id (const char *name, 
-                  KEYDB_SEARCH_DESC *desc,
-                  int *force_exact )
-{
-  const char *s;
-  int hexprefix = 0;
-  int hexlength;
-  int mode = 0;   
-    
-  /* clear the structure so that the mode field is set to zero unless
-   * we set it to the correct value right at the end of this function */
-  memset (desc, 0, sizeof *desc);
-  *force_exact = 0;
-  /* skip leading spaces.  Fixme: what about trailing white space? */
-  for(s = name; *s && spacep (s); s++ )
-    ;
-
-  switch (*s) 
+  if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL))
     {
-    case 0:  /* empty string is an error */
-      return 0;
-
-    case '.': /* an email address, compare from end */
-      mode = KEYDB_SEARCH_MODE_MAILEND;
-      s++;
-      desc->u.name = s;
-      break;
-
-    case '<': /* an email address */
-      mode = KEYDB_SEARCH_MODE_MAIL;
-      s++;
-      desc->u.name = s;
-      break;
-
-    case '@':  /* part of an email address */
-      mode = KEYDB_SEARCH_MODE_MAILSUB;
-      s++;
-      desc->u.name = s;
-      break;
-
-    case '=':  /* exact compare */
-      mode = KEYDB_SEARCH_MODE_EXACT;
-      s++;
-      desc->u.name = s;
-      break;
-
-    case '*':  /* case insensitive substring search */
-      mode = KEYDB_SEARCH_MODE_SUBSTR;
-      s++;
-      desc->u.name = s;
-      break;
+      log_error (_("failed to get the fingerprint\n"));
+      return gpg_error (GPG_ERR_GENERAL);
+    }
 
-    case '+':  /* compare individual words */
-      mode = KEYDB_SEARCH_MODE_WORDS;
-      s++;
-      desc->u.name = s;
-      break;
+  kh = keydb_new ();
+  if (!kh)
+    {
+      log_error (_("failed to allocate keyDB handle\n"));
+      return gpg_error (GPG_ERR_ENOMEM);;
+    }
 
-    case '/': /* subject's DN */
-      s++;
-      if (!*s || spacep (s))
-        return 0; /* no DN or prefixed with a space */
-      desc->u.name = s;
-      mode = KEYDB_SEARCH_MODE_SUBJECT;
-      break;
+  /* Set the ephemeral flag so that the search looks at all
+     records.  */
+  keydb_set_ephemeral (kh, 1);
 
-    case '#':
-      { 
-        const char *si;
-        
-        s++;
-        if ( *s == '/')
-          { /* "#/" indicates an issuer's DN */
-            s++;
-            if (!*s || spacep (s))
-              return 0; /* no DN or prefixed with a space */
-            desc->u.name = s;
-            mode = KEYDB_SEARCH_MODE_ISSUER;
-          }
-        else 
-          { /* serialnumber + optional issuer ID */
-            for (si=s; *si && *si != '/'; si++)
-              {
-                if (!strchr("01234567890abcdefABCDEF", *si))
-                  return 0; /* invalid digit in serial number*/
-              }
-            desc->sn = s;
-            desc->snlen = -1;
-            if (!*si)
-              mode = KEYDB_SEARCH_MODE_SN;
-            else
-              {
-                s = si+1;
-                if (!*s || spacep (s))
-                  return 0; /* no DN or prefixed with a space */
-                desc->u.name = s;
-                mode = KEYDB_SEARCH_MODE_ISSUER_SN;
-              }
-          }
-      }
-      break;
-
-    case ':': /*Unified fingerprint */
-      {  
-        const char *se, *si;
-        int i;
-        
-        se = strchr (++s,':');
-        if (!se)
-          return 0;
-        for (i=0,si=s; si < se; si++, i++ )
-          {
-            if (!strchr("01234567890abcdefABCDEF", *si))
-              return 0; /* invalid digit */
-          }
-        if (i != 32 && i != 40)
-          return 0; /* invalid length of fpr*/
-        for (i=0,si=s; si < se; i++, si +=2) 
-          desc->u.fpr[i] = hextobyte(si);
-        for (; i < 20; i++)
-          desc->u.fpr[i]= 0;
-        s = se + 1;
-        mode = KEYDB_SEARCH_MODE_FPR;
-      } 
-      break;
-           
-    default:
-      if (s[0] == '0' && s[1] == 'x')
-        {
-          hexprefix = 1;
-          s += 2;
-        }
+  rc = lock_all (kh);
+  if (rc)
+    return rc;
 
-      hexlength = strspn(s, "0123456789abcdefABCDEF");
-      if (hexlength >= 8 && s[hexlength] =='!')
-        {
-          *force_exact = 1;
-          hexlength++; /* just for the following check */
-        }
-      
-      /* check if a hexadecimal number is terminated by EOS or blank */
-      if (hexlength && s[hexlength] && !spacep (s+hexlength)) 
+  rc = keydb_search_fpr (ctrl, kh, fpr);
+  if (rc != -1)
+    {
+      keydb_release (kh);
+      if (!rc)
         {
-          if (hexprefix) /* a "0x" prefix without correct */
-            return 0;   /* termination is an error */
-          /* The first chars looked like a hex number, but really is
-             not */
-          hexlength = 0;  
-        }
-      
-      if (*force_exact)
-        hexlength--; /* remove the bang */
-
-      if (hexlength == 8
-          || (!hexprefix && hexlength == 9 && *s == '0'))
-        { /* short keyid */
-          unsigned long kid;
-          if (hexlength == 9)
-            s++;
-          kid = strtoul( s, NULL, 16 );
-          desc->u.kid[4] = kid >> 24; 
-          desc->u.kid[5] = kid >> 16; 
-          desc->u.kid[6] = kid >>  8; 
-          desc->u.kid[7] = kid; 
-          mode = KEYDB_SEARCH_MODE_SHORT_KID;
-        }
-      else if (hexlength == 16
-               || (!hexprefix && hexlength == 17 && *s == '0'))
-        { /* complete keyid */
-          unsigned long kid0, kid1;
-          char buf[9];
-          if (hexlength == 17)
-            s++;
-          mem2str(buf, s, 9 );
-          kid0 = strtoul (buf, NULL, 16);
-          kid1 = strtoul (s+8, NULL, 16);
-          desc->u.kid[0] = kid0 >> 24; 
-          desc->u.kid[1] = kid0 >> 16; 
-          desc->u.kid[2] = kid0 >>  8; 
-          desc->u.kid[3] = kid0; 
-          desc->u.kid[4] = kid1 >> 24; 
-          desc->u.kid[5] = kid1 >> 16; 
-          desc->u.kid[6] = kid1 >>  8; 
-          desc->u.kid[7] = kid1; 
-          mode = KEYDB_SEARCH_MODE_LONG_KID;
-        }
-      else if (hexlength == 32
-               || (!hexprefix && hexlength == 33 && *s == '0'))
-        { /* md5 fingerprint */
-          int i;
-          if (hexlength == 33)
-            s++;
-          memset(desc->u.fpr+16, 0, 4); 
-          for (i=0; i < 16; i++, s+=2) 
-            {
-              int c = hextobyte(s);
-              if (c == -1)
-                return 0;
-              desc->u.fpr[i] = c;
-            }
-          mode = KEYDB_SEARCH_MODE_FPR16;
-        }
-      else if (hexlength == 40
-               || (!hexprefix && hexlength == 41 && *s == '0'))
-        { /* sha1/rmd160 fingerprint */
-          int i;
-          if (hexlength == 41)
-            s++;
-          for (i=0; i < 20; i++, s+=2) 
+          if (existed)
+            *existed = 1;
+          if (!ephemeral)
             {
-              int c = hextobyte(s);
-              if (c == -1)
-                return 0;
-              desc->u.fpr[i] = c;
-            }
-          mode = KEYDB_SEARCH_MODE_FPR20;
-        }
-      else if (!hexprefix)
-        { 
-          /* The fingerprint in an X.509 listing is often delimited by
-             colons, so we try to single this case out. */
-          mode = 0;
-          hexlength = strspn (s, ":0123456789abcdefABCDEF");
-          if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength))) 
-            {
-              int i;
-
-              for (i=0; i < 20; i++, s += 3) 
+              /* Remove ephemeral flags from existing certificate to "store"
+                 it permanently. */
+              rc = keydb_set_cert_flags (ctrl, cert, 1, KEYBOX_FLAG_BLOB, 0,
+                                         KEYBOX_FLAG_BLOB_EPHEMERAL, 0);
+              if (rc)
                 {
-                  int c = hextobyte(s);
-                  if (c == -1 || (i < 19 && s[2] != ':'))
-                    break;
-                  desc->u.fpr[i] = c;
+                  log_error ("clearing ephemeral flag failed: %s\n",
+                             gpg_strerror (rc));
+                  return rc;
                 }
-              if (i == 20)
-                mode = KEYDB_SEARCH_MODE_FPR20;
             }
-          if (!mode) /* default is substring search */
-            { 
-              *force_exact = 0;
-              desc->u.name = s;
-              mode = KEYDB_SEARCH_MODE_SUBSTR; 
-            }
-        }
-      else
-       { /* hex number with a prefix but a wrong length */
-          return 0;
+          return 0; /* okay */
         }
+      log_error (_("problem looking for existing certificate: %s\n"),
+                 gpg_strerror (rc));
+      return rc;
     }
-  
-  desc->mode = mode;
-  return mode;
-}
 
+  /* Reset the ephemeral flag if not requested.  */
+  if (!ephemeral)
+    keydb_set_ephemeral (kh, 0);
 
-int
-keydb_classify_name (const char *name, KEYDB_SEARCH_DESC *desc)
-{
-  int dummy;
-  KEYDB_SEARCH_DESC dummy_desc;
-
-  if (!desc)
-    desc = &dummy_desc;
+  rc = keydb_locate_writable (kh, 0);
+  if (rc)
+    {
+      log_error (_("error finding writable keyDB: %s\n"), gpg_strerror (rc));
+      keydb_release (kh);
+      return rc;
+    }
 
-  if (!classify_user_id (name, desc, &dummy))
-    return GNUPG_Invalid_Name;
+  rc = keydb_insert_cert (kh, cert);
+  if (rc)
+    {
+      log_error (_("error storing certificate: %s\n"), gpg_strerror (rc));
+      keydb_release (kh);
+      return rc;
+    }
+  keydb_release (kh);
   return 0;
 }
 
-\f
-/* Store the certificate in the key Db but make sure that it does not
-   already exists.  We do this simply by comparing the fingerprint */
-int
-keydb_store_cert (KsbaCert cert, int ephemeral)
+
+/* This is basically keydb_set_flags but it implements a complete
+   transaction by locating the certificate in the DB and updating the
+   flags. */
+gpg_error_t
+keydb_set_cert_flags (ctrl_t ctrl, ksba_cert_t cert, int ephemeral,
+                      int which, int idx,
+                      unsigned int mask, unsigned int value)
 {
   KEYDB_HANDLE kh;
-  int rc;
+  gpg_error_t err;
   unsigned char fpr[20];
+  unsigned int old_value;
 
   if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL))
     {
       log_error (_("failed to get the fingerprint\n"));
-      return GNUPG_General_Error;
+      return gpg_error (GPG_ERR_GENERAL);
     }
 
-  kh = keydb_new (0);
+  kh = keydb_new ();
   if (!kh)
     {
       log_error (_("failed to allocate keyDB handle\n"));
-      return GNUPG_Out_Of_Core;
+      return gpg_error (GPG_ERR_ENOMEM);;
     }
 
   if (ephemeral)
     keydb_set_ephemeral (kh, 1);
-  
-  rc = keydb_search_fpr (kh, fpr);
-  if (rc != -1)
+
+  err = keydb_lock (kh);
+  if (err)
     {
+      log_error (_("error locking keybox: %s\n"), gpg_strerror (err));
       keydb_release (kh);
-      if (!rc)
-        return 0; /* okay */
-      log_error (_("problem looking for existing certificate: %s\n"),
-                 gnupg_strerror (rc));
-      return rc;
+      return err;
     }
 
-  rc = keydb_locate_writable (kh, 0);
-  if (rc)
+  err = keydb_search_fpr (ctrl, kh, fpr);
+  if (err)
     {
-      log_error (_("error finding writable keyDB: %s\n"), gnupg_strerror (rc));
+      if (err == -1)
+        err = gpg_error (GPG_ERR_NOT_FOUND);
+      else
+        log_error (_("problem re-searching certificate: %s\n"),
+                   gpg_strerror (err));
       keydb_release (kh);
-      return rc;
+      return err;
     }
 
-  rc = keydb_insert_cert (kh, cert);
-  if (rc)
+  err = keydb_get_flags (kh, which, idx, &old_value);
+  if (err)
     {
-      log_error (_("error storing certificate: %s\n"), gnupg_strerror (rc));
+      log_error (_("error getting stored flags: %s\n"), gpg_strerror (err));
       keydb_release (kh);
-      return rc;
+      return err;
     }
-  keydb_release (kh);               
+
+  value = ((old_value & ~mask) | (value & mask));
+
+  if (value != old_value)
+    {
+      err = keydb_set_flags (kh, which, idx, value);
+      if (err)
+        {
+          log_error (_("error storing flags: %s\n"), gpg_strerror (err));
+          keydb_release (kh);
+          return err;
+        }
+    }
+
+  keydb_release (kh);
   return 0;
 }
 
 
+/* Reset all the certificate flags we have stored with the certificates
+   for performance reasons. */
+void
+keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names)
+{
+  gpg_error_t err;
+  KEYDB_HANDLE hd = NULL;
+  KEYDB_SEARCH_DESC *desc = NULL;
+  int ndesc;
+  strlist_t sl;
+  int rc=0;
+  unsigned int old_value, value;
+
+  (void)ctrl;
+
+  hd = keydb_new ();
+  if (!hd)
+    {
+      log_error ("keydb_new failed\n");
+      goto leave;
+    }
+
+  if (!names)
+    ndesc = 1;
+  else
+    {
+      for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++)
+        ;
+    }
+
+  desc = xtrycalloc (ndesc, sizeof *desc);
+  if (!ndesc)
+    {
+      log_error ("allocating memory failed: %s\n",
+                 gpg_strerror (out_of_core ()));
+      goto leave;
+    }
+
+  if (!names)
+    desc[0].mode = KEYDB_SEARCH_MODE_FIRST;
+  else
+    {
+      for (ndesc=0, sl=names; sl; sl = sl->next)
+        {
+          rc = classify_user_id (sl->d, desc+ndesc, 0);
+          if (rc)
+            log_error ("key '%s' not found: %s\n", sl->d, gpg_strerror (rc));
+          else
+            ndesc++;
+        }
+    }
+
+  err = keydb_lock (hd);
+  if (err)
+    {
+      log_error (_("error locking keybox: %s\n"), gpg_strerror (err));
+      goto leave;
+    }
+
+  while (!(rc = keydb_search (ctrl, hd, desc, ndesc)))
+    {
+      if (!names)
+        desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
+
+      err = keydb_get_flags (hd, KEYBOX_FLAG_VALIDITY, 0, &old_value);
+      if (err)
+        {
+          log_error (_("error getting stored flags: %s\n"),
+                     gpg_strerror (err));
+          goto leave;
+        }
+
+      value = (old_value & ~VALIDITY_REVOKED);
+      if (value != old_value)
+        {
+          err = keydb_set_flags (hd, KEYBOX_FLAG_VALIDITY, 0, value);
+          if (err)
+            {
+              log_error (_("error storing flags: %s\n"), gpg_strerror (err));
+              goto leave;
+            }
+        }
+    }
+  if (rc && rc != -1)
+    log_error ("keydb_search failed: %s\n", gpg_strerror (rc));
+
+ leave:
+  xfree (desc);
+  keydb_release (hd);
+}