(set_binary): New.
[gnupg.git] / sm / keydb.c
index a5f3f41..293e523 100644 (file)
@@ -1,5 +1,5 @@
 /* keydb.c - key database dispatcher
- * Copyright (C) 2001 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -33,8 +33,6 @@
 #include "keydb.h" 
 #include "i18n.h"
 
-#define DIRSEP_C '/'
-
 static int active_handles;
 
 typedef enum {
@@ -50,6 +48,7 @@ struct resource_item {
   } u;
   void *token;
   int secret;
+  DOTLOCK lockhandle;
 };
 
 static struct resource_item all_resources[MAX_KEYDB_RESOURCES];
@@ -59,6 +58,7 @@ struct keydb_handle {
   int locked;
   int found;
   int current;
+  int is_ephemeral;
   int used; /* items in active */
   struct resource_item active[MAX_KEYDB_RESOURCES];
 };
@@ -101,7 +101,7 @@ keydb_add_resource (const char *url, int force, int secret)
       else if (strchr (resname, ':'))
         {
           log_error ("invalid key resource URL `%s'\n", url );
-          rc = GNUPG_General_Error;
+          rc = gpg_error (GPG_ERR_GENERAL);
           goto leave;
        }
 #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
@@ -148,14 +148,14 @@ keydb_add_resource (const char *url, int force, int secret)
     {
     case KEYDB_RESOURCE_TYPE_NONE:
       log_error ("unknown type of key resource `%s'\n", url );
-      rc = GNUPG_General_Error;
+      rc = gpg_error (GPG_ERR_GENERAL);
       goto leave;
       
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       fp = fopen (filename, "rb");
       if (!fp && !force)
         {
-          rc = GNUPG_File_Open_Error;
+          rc = gpg_error (gpg_err_code_from_errno (errno));
           goto leave;
         }
       
@@ -173,7 +173,7 @@ keydb_add_resource (const char *url, int force, int secret)
                    terminated, so that on the next invocation can
                    read the options file in on startup */
                 try_make_homedir (filename);
-                rc = GNUPG_File_Open_Error;
+                rc = gpg_error (GPG_ERR_FILE_OPEN_ERROR);
                 *last_slash_in_filename = DIRSEP_C;
                 goto leave;
               }
@@ -183,9 +183,11 @@ keydb_add_resource (const char *url, int force, int secret)
           fp = fopen (filename, "w");
           if (!fp)
             {
+              rc = gpg_error (gpg_err_code_from_errno (errno));
               log_error (_("error creating keybox `%s': %s\n"),
                          filename, strerror(errno));
-              rc = GNUPG_File_Create_Error;
+              if (errno == ENOENT)
+                log_info (_("you may want to start the gpg-agent first\n"));
               goto leave;
            }
 
@@ -195,26 +197,48 @@ keydb_add_resource (const char *url, int force, int secret)
        }
        fclose (fp);
        fp = NULL;
-        /* now regsiter the file */
+        /* now register 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;
+            rc = gpg_error (GPG_ERR_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;
+
+              all_resources[used_resources].lockhandle
+                = create_dotlock (filename);
+              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 (!make_dotlock (all_resources[used_resources].lockhandle, 0))
+                {
+                  KEYBOX_HANDLE kbxhd = keybox_new (token, secret);
+                  
+                  if (kbxhd)
+                    {
+                      keybox_compress (kbxhd);
+                      keybox_release (kbxhd);
+                    }
+                  release_dotlock (all_resources[used_resources].lockhandle);
+                }
+                  
               used_resources++;
             }
         }
+
+
        break;
     default:
       log_error ("resource type of `%s' not supported\n", url);
-      rc = GNUPG_Not_Supported;
+      rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
       goto leave;
     }
 
@@ -222,7 +246,7 @@ keydb_add_resource (const char *url, int force, int secret)
 
  leave:
   if (rc)
-    log_error ("keyblock resource `%s': %s\n", filename, gnupg_strerror(rc));
+    log_error ("keyblock resource `%s': %s\n", filename, gpg_strerror(rc));
   else if (secret)
     any_secret = 1;
   else
@@ -254,11 +278,13 @@ keydb_new (int secret)
           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].lockhandle = all_resources[i].lockhandle;
           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*/
-          }
+          if (!hd->active[j].u.kr) 
+            {
+              xfree (hd);
+              return NULL; /* fixme: release all previously allocated handles*/
+            }
           j++;
           break;
         }
@@ -331,6 +357,51 @@ keydb_get_resource_name (KEYDB_HANDLE hd)
   return s? s: "";
 }
 
+/* Switch the handle into ephemeral mode and return the orginal value. */
+int
+keydb_set_ephemeral (KEYDB_HANDLE hd, int yes)
+{
+  int i;
+
+  if (!hd)
+    return 0;
+
+  yes = !!yes;
+  if (hd->is_ephemeral != yes)
+    {
+      for (i=0; i < hd->used; i++)
+        {
+          switch (hd->active[i].type) 
+            {
+            case KEYDB_RESOURCE_TYPE_NONE:
+              break;
+            case KEYDB_RESOURCE_TYPE_KEYBOX:
+              keybox_set_ephemeral (hd->active[i].u.kr, yes);
+              break;
+            }
+        }
+    }
+      
+  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 opeations; it is optionaly
+   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 
@@ -338,16 +409,22 @@ 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) 
         {
         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 = make_dotlock (hd->active[i].lockhandle, -1);
           break;
         }
+      if (rc)
+        break;
     }
 
     if (rc) 
@@ -360,7 +437,8 @@ lock_all (KEYDB_HANDLE hd)
               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)
+                  release_dotlock (hd->active[i].lockhandle);
                 break;
               }
           }
@@ -368,7 +446,10 @@ 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
@@ -386,7 +467,8 @@ unlock_all (KEYDB_HANDLE hd)
         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)
+            release_dotlock (hd->active[i].lockhandle);
           break;
         }
     }
@@ -441,9 +523,8 @@ keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb)
     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:
@@ -503,17 +584,17 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb)
 
 \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;
+    return gpg_error (GPG_ERR_INV_VALUE);
   
   if ( hd->found < 0 || hd->found >= hd->used) 
     return -1; /* nothing found */
@@ -521,7 +602,7 @@ keydb_get_cert (KEYDB_HANDLE hd, KsbaCert *r_cert)
   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);
@@ -531,18 +612,79 @@ keydb_get_cert (KEYDB_HANDLE hd, KsbaCert *r_cert)
   return rc;
 }
 
+/* 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;
+    return gpg_error (GPG_ERR_INV_VALUE);
 
   if (opt.dry_run)
     return 0;
@@ -552,7 +694,7 @@ keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert)
   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)
@@ -563,7 +705,7 @@ keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert)
   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);
@@ -578,13 +720,13 @@ keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert)
 
 /* 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];
   
   if (!hd)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
 
   if ( hd->found < 0 || hd->found >= hd->used) 
     return -1; /* nothing found */
@@ -601,7 +743,7 @@ keydb_update_cert (KEYDB_HANDLE hd, KsbaCert cert)
   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);
@@ -622,7 +764,7 @@ keydb_delete (KEYDB_HANDLE hd)
   int rc = -1;
   
   if (!hd)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
 
   if ( hd->found < 0 || hd->found >= hd->used) 
     return -1; /* nothing found */
@@ -630,14 +772,13 @@ keydb_delete (KEYDB_HANDLE hd)
   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);
@@ -661,7 +802,7 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
   int rc;
   
   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)
@@ -721,7 +862,7 @@ keydb_search_reset (KEYDB_HANDLE hd)
   int i, rc = 0;
   
   if (!hd)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
 
   hd->current = 0; 
   hd->found = -1;
@@ -751,7 +892,7 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc)
   int rc = -1;
   
   if (!hd)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
 
   while (rc == -1 && hd->current >= 0 && hd->current < hd->used) 
     {
@@ -832,14 +973,23 @@ keydb_search_issuer (KEYDB_HANDLE hd, const char *issuer)
 
 int
 keydb_search_issuer_sn (KEYDB_HANDLE hd,
-                        const char *issuer, const unsigned char *serial)
+                        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;
-  desc.sn = serial;
+  s = serial;
+  if (*s !='(')
+    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 gpg_error (GPG_ERR_INV_VALUE);
+  desc.sn = s+1;
   desc.u.name = issuer;
   rc = keydb_search (hd, &desc, 1);
   return rc;
@@ -859,4 +1009,522 @@ keydb_search_subject (KEYDB_HANDLE hd, const char *name)
 }
 
 
+static int
+hextobyte (const unsigned char *s)
+{
+  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;
+}
+
+
+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) 
+    {
+    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;
+
+    case '+':  /* compare individual words */
+      mode = KEYDB_SEARCH_MODE_WORDS;
+      s++;
+      desc->u.name = s;
+      break;
+
+    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;
+
+    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;
+        }
+
+      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)) 
+        {
+          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) 
+            {
+              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) 
+                {
+                  int c = hextobyte(s);
+                  if (c == -1 || (i < 19 && s[2] != ':'))
+                    break;
+                  desc->u.fpr[i] = c;
+                }
+              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;
+        }
+    }
+  
+  desc->mode = mode;
+  return mode;
+}
+
+
+int
+keydb_classify_name (const char *name, KEYDB_SEARCH_DESC *desc)
+{
+  int dummy;
+  KEYDB_SEARCH_DESC dummy_desc;
+
+  if (!desc)
+    desc = &dummy_desc;
+
+  if (!classify_user_id (name, desc, &dummy))
+    return gpg_error (GPG_ERR_INV_NAME);
+  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.
+   If EXISTED is not NULL it will be set to true if the certificate
+   was already in the DB. */
+int
+keydb_store_cert (ksba_cert_t cert, int ephemeral, int *existed)
+{
+  KEYDB_HANDLE kh;
+  int rc;
+  unsigned char fpr[20];
+
+  if (existed)
+    *existed = 0;
+
+  if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL))
+    {
+      log_error (_("failed to get the fingerprint\n"));
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+
+  kh = keydb_new (0);
+  if (!kh)
+    {
+      log_error (_("failed to allocate keyDB handle\n"));
+      return gpg_error (GPG_ERR_ENOMEM);;
+    }
+
+  if (ephemeral)
+    keydb_set_ephemeral (kh, 1);
+  
+  rc = keydb_search_fpr (kh, fpr);
+  if (rc != -1)
+    {
+      keydb_release (kh);
+      if (!rc)
+        {
+          if (existed)
+            *existed = 1;
+          return 0; /* okay */
+        }
+      log_error (_("problem looking for existing certificate: %s\n"),
+                 gpg_strerror (rc));
+      return rc;
+    }
+
+  rc = keydb_locate_writable (kh, 0);
+  if (rc)
+    {
+      log_error (_("error finding writable keyDB: %s\n"), gpg_strerror (rc));
+      keydb_release (kh);
+      return rc;
+    }
+
+  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;
+}
+
+
+/* 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 (ksba_cert_t cert, int which, int idx, unsigned int value)
+{
+  KEYDB_HANDLE kh;
+  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 gpg_error (GPG_ERR_GENERAL);
+    }
+
+  kh = keydb_new (0);
+  if (!kh)
+    {
+      log_error (_("failed to allocate keyDB handle\n"));
+      return gpg_error (GPG_ERR_ENOMEM);;
+    }
+
+  err = keydb_lock (kh);
+  if (err)
+    {
+      log_error (_("error locking keybox: %s\n"), gpg_strerror (err));
+      keydb_release (kh);
+      return err;
+    }
+
+  err = keydb_search_fpr (kh, fpr);
+  if (err)
+    {
+      log_error (_("problem re-searching certificate: %s\n"),
+                 gpg_strerror (err));
+      keydb_release (kh);
+      return err;
+    }
+
+  err = keydb_get_flags (kh, which, idx, &old_value);
+  if (err)
+    {
+      log_error (_("error getting stored flags: %s\n"), gpg_strerror (err));
+      keydb_release (kh);
+      return err;
+    }
+  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 names)
+{
+  gpg_error_t err;
+  KEYDB_HANDLE hd = NULL;
+  KEYDB_SEARCH_DESC *desc = NULL;
+  int ndesc;
+  STRLIST sl;
+  int rc=0;
+  unsigned int old_value, value;
+  
+  hd = keydb_new (0);
+  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 (errno)));
+      goto leave;
+    }
+
+  if (!names)
+    desc[0].mode = KEYDB_SEARCH_MODE_FIRST;
+  else 
+    {
+      for (ndesc=0, sl=names; sl; sl = sl->next) 
+        {
+          rc = keydb_classify_name (sl->d, desc+ndesc);
+          if (rc)
+            {
+              log_error ("key `%s' not found: %s\n",
+                         sl->d, gpg_strerror (rc));
+              rc = 0;
+            }
+          else
+            ndesc++;
+        }
+    }
+
+  err = keydb_lock (hd);
+  if (err)
+    {
+      log_error (_("error locking keybox: %s\n"), gpg_strerror (err));
+      goto leave;
+    }
+
+  while (!(rc = keydb_search (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);
+}
+