See ChangeLog: Fri Nov 27 15:30:24 CET 1998 Werner Koch
[gnupg.git] / g10 / ringedit.c
index a281d8b..d12afa6 100644 (file)
@@ -35,8 +35,6 @@
  *
  *  - Delete a key block
  *
- * FIXME:  Keep track of all nodes, so that a change is propagated
- *        to all nodes. (or use shallow copies and ref-counting?)
  */
 
 
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <unistd.h> /* for truncate */
 #include <assert.h>
+#ifdef HAVE_LIBGDBM
+  #include <gdbm.h>
+#endif
 #include "util.h"
 #include "packet.h"
 #include "memory.h"
 #include "mpi.h"
 #include "iobuf.h"
 #include "keydb.h"
-#include <unistd.h> /* for truncate */
+#include "host2net.h"
+#include "options.h"
+#include "main.h"
+#include "i18n.h"
 
 
 struct resource_table_struct {
@@ -63,13 +68,16 @@ struct resource_table_struct {
     int secret; /* this is a secret keyring */
     char *fname;
     IOBUF iobuf;
+  #ifdef HAVE_LIBGDBM
+    GDBM_FILE dbf;
+  #endif
+    enum resource_type rt;
 };
 typedef struct resource_table_struct RESTBL;
 
 #define MAX_RESOURCES 10
 static RESTBL resource_table[MAX_RESOURCES];
 
-
 static int search( PACKET *pkt, KBPOS *kbpos, int secret );
 
 
@@ -79,6 +87,14 @@ static int keyring_read( KBPOS *kbpos, KBNODE *ret_root );
 static int keyring_enum( KBPOS *kbpos, KBNODE *ret_root, int skipsigs );
 static int keyring_copy( KBPOS *kbpos, int mode, KBNODE root );
 
+#ifdef HAVE_LIBGDBM
+static int do_gdbm_store( KBPOS *kbpos, KBNODE root, int update );
+static int do_gdbm_locate( GDBM_FILE dbf, KBPOS *kbpos,
+                                         const byte *fpr, int fprlen );
+static int do_gdbm_locate_by_keyid( GDBM_FILE dbf, KBPOS *kbpos, u32 *keyid );
+static int do_gdbm_read( KBPOS *kbpos, KBNODE *ret_root );
+static int do_gdbm_enum( KBPOS *kbpos, KBNODE *ret_root );
+#endif
 
 
 static RESTBL *
@@ -91,39 +107,224 @@ check_pos( KBPOS *kbpos )
     return resource_table + kbpos->resno;
 }
 
+#ifdef HAVE_LIBGDBM
+static void
+fatal_gdbm_error( const char *string )
+{
+    log_fatal("gdbm failed: %s\n", string);
+}
 
+#endif /* HAVE_LIBGDBM */
 
 /****************************************************************
  ****************** public functions ****************************
  ****************************************************************/
 
 /****************
+ * Get the name of the keyrings, start with a sequence number pointing to a 0.
+ */
+const char *
+enum_keyblock_resources( int *sequence, int secret )
+{
+    int i = *sequence;
+    const char *name = NULL;
+
+    for(; i < MAX_RESOURCES; i++ )
+       if( resource_table[i].used && !resource_table[i].secret == !secret ) {
+           if( resource_table[i].fname ) {
+               name = resource_table[i].fname;
+               break;
+           }
+       }
+    *sequence = ++i;
+    return name;
+}
+
+
+
+/****************
  * Register a resource (which currently may only be a keyring file).
+ * The first keyring which is added by this function is
+ * created if it does not exist.
+ * Note: this function may be called before secure memory is
+ * available.
  */
 int
-add_keyblock_resource( const char *filename, int force, int secret )
+add_keyblock_resource( const char *url, int force, int secret )
 {
-    IOBUF iobuf;
+    static int any_secret, any_public;
+    const char *resname = url;
+    IOBUF iobuf = NULL;
     int i;
+    char *filename = NULL;
+    int rc = 0;
+    enum resource_type rt = rt_UNKNOWN;
+
+    /* Do we have an URL?
+     * gnupg-gdbm:filename  := this is a GDBM resource
+     * gnupg-ring:filename  := this is a plain keyring
+     * filename := See what is is, but create as plain keyring.
+     */
+    if( strlen( resname ) > 11 ) {
+       if( !strncmp( resname, "gnupg-ring:", 11 ) ) {
+           rt = rt_RING;
+           resname += 11;
+       }
+       else if( !strncmp( resname, "gnupg-gdbm:", 11 ) ) {
+           rt = rt_GDBM;
+           resname += 11;
+       }
+      #ifndef __MINGW32__
+       else if( strchr( resname, ':' ) ) {
+           log_error("%s: invalid URL\n", url );
+           rc = G10ERR_GENERAL;
+           goto leave;
+       }
+      #endif
+    }
+
+    if( *resname != '/' ) { /* do tilde expansion etc */
+       if( strchr(resname, '/') )
+           filename = make_filename(resname, NULL);
+       else
+           filename = make_filename(opt.homedir, resname, NULL);
+    }
+    else
+       filename = m_strdup( resname );
+
+    if( !force )
+       force = secret? !any_secret : !any_public;
 
     for(i=0; i < MAX_RESOURCES; i++ )
        if( !resource_table[i].used )
            break;
-    if( i == MAX_RESOURCES )
-       return G10ERR_RESOURCE_LIMIT;
+    if( i == MAX_RESOURCES ) {
+       rc = G10ERR_RESOURCE_LIMIT;
+       goto leave;
+    }
+
+    /* see whether we can determine the filetype */
+    if( rt == rt_UNKNOWN ) {
+       FILE *fp = fopen( filename, "rb" );
+
+       if( fp ) {
+           u32 magic;
+
+           if( fread( &magic, 4, 1, fp) == 1 ) {
+               if( magic == 0x13579ace )
+                   rt = rt_GDBM;
+               else if( magic == 0xce9a5713 )
+                   log_error("%s: endianess does not match\n", url );
+               else
+                   rt = rt_RING;
+           }
+           else /* maybe empty: assume ring */
+               rt = rt_RING;
+           fclose( fp );
+       }
+       else /* no file yet: create ring */
+           rt = rt_RING;
+    }
+
+    switch( rt ) {
+      case rt_UNKNOWN:
+       log_error("%s: unknown resource type\n", url );
+       rc = G10ERR_GENERAL;
+       goto leave;
+
+      case rt_RING:
+       iobuf = iobuf_fopen( filename, "rb" );
+       if( !iobuf && !force ) {
+           rc = G10ERR_OPEN_FILE;
+           goto leave;
+       }
+
+       if( !iobuf ) {
+           char *last_slash_in_filename;
+
+           last_slash_in_filename = strrchr(filename, '/');
+           *last_slash_in_filename = 0;
+
+           if( access(filename, F_OK) ) {
+               if( strlen(filename) >= 7
+                   && !strcmp(filename+strlen(filename)-7, "/.gnupg") ) {
+                 #if __MINGW32__
+                   if( mkdir(filename) )
+                 #else
+                   if( mkdir(filename, S_IRUSR|S_IWUSR|S_IXUSR) )
+                 #endif
+                   {
+                       log_error( _("%s: can't create directory: %s\n"),
+                                 filename, strerror(errno));
+                       rc = G10ERR_OPEN_FILE;
+                       goto leave;
+                   }
+                   else
+                       log_info( _("%s: directory created\n"), filename );
+                   copy_options_file( filename );
+               }
+               else
+               {
+                   rc = G10ERR_OPEN_FILE;
+                   goto leave;
+               }
+           }
+
+           *last_slash_in_filename = '/';
+
+           iobuf = iobuf_create( filename );
+           if( !iobuf ) {
+               log_error(_("%s: can't create keyring: %s\n"),
+                                           filename, strerror(errno));
+               rc = G10ERR_OPEN_FILE;
+               goto leave;
+           }
+           else
+               log_info(_("%s: keyring created\n"), filename );
+       }
+      #ifdef __MINGW32__
+       /* must close it again */
+       iobuf_close( iobuf );
+       iobuf = NULL;
+      #endif
+       break;
+
+    #ifdef HAVE_LIBGDBM
+      case rt_GDBM:
+       resource_table[i].dbf = gdbm_open( filename, 0,
+                                          force? GDBM_WRCREAT : GDBM_WRITER,
+                                          S_IRUSR | S_IWUSR |
+                                          S_IRGRP | S_IWGRP | S_IROTH,
+                                          fatal_gdbm_error );
+       if( !resource_table[i].dbf ) {
+           log_error("%s: can't open gdbm file: %s\n",
+                           filename, gdbm_strerror(gdbm_errno));
+           rc = G10ERR_OPEN_FILE;
+           goto leave;
+       }
+       break;
+    #endif
+
+      default:
+       log_error("%s: unsupported resource type\n", url );
+       rc = G10ERR_GENERAL;
+       goto leave;
+    }
 
-  #if __MINGW32__
-    iobuf = NULL;
-  #else
-    iobuf = iobuf_open( filename );
-    if( !iobuf && !force )
-       return G10ERR_OPEN_FILE;
-  #endif
     resource_table[i].used = 1;
     resource_table[i].secret = !!secret;
     resource_table[i].fname = m_strdup(filename);
     resource_table[i].iobuf = iobuf;
-    return 0;
+    resource_table[i].rt    = rt;
+  leave:
+    if( rc )
+       log_error("keyblock resource '%s': %s\n", filename, g10_errstr(rc) );
+    else if( secret )
+       any_secret = 1;
+    else
+       any_public = 1;
+    m_free( filename );
+    return rc;
 }
 
 /****************
@@ -156,6 +357,7 @@ get_keyblock_handle( const char *filename, int secret, KBPOS *kbpos )
            if( !filename || !strcmp( resource_table[i].fname, filename ) ) {
                memset( kbpos, 0, sizeof *kbpos );
                kbpos->resno = i;
+               kbpos->rt = resource_table[i].rt;
                return 0;
            }
        }
@@ -168,7 +370,7 @@ get_keyblock_handle( const char *filename, int secret, KBPOS *kbpos )
  * Search a keyblock which starts with the given packet and puts all
  * information into KBPOS, which can be used later to access this key block.
  * This function looks into all registered keyblock sources.
- * PACKET must be a packet with either a secret_cert or a public_cert
+ * PACKET must be a packet with either a secret_key or a public_key
  *
  * This function is intended to check whether a given certificate
  * is already in a keyring or to prepare it for editing.
@@ -182,10 +384,27 @@ search( PACKET *pkt, KBPOS *kbpos, int secret )
 
     for(i=0; i < MAX_RESOURCES; i++ ) {
        if( resource_table[i].used && !resource_table[i].secret == !secret ) {
-           /* note: here we have to add different search functions,
-            * depending on the type of the resource */
-           rc = keyring_search( pkt, kbpos, resource_table[i].iobuf,
-                                            resource_table[i].fname );
+           switch( resource_table[i].rt ) {
+             case rt_RING:
+               rc = keyring_search( pkt, kbpos, resource_table[i].iobuf,
+                                                resource_table[i].fname );
+               break;
+            #ifdef HAVE_LIBGDBM
+             case rt_GDBM: {
+                   PKT_public_key *req_pk = pkt->pkt.public_key;
+                   byte fpr[20];
+                   size_t fprlen;
+
+                   fingerprint_from_pk( req_pk, fpr, &fprlen );
+                   rc = do_gdbm_locate( resource_table[i].dbf,
+                                        kbpos, fpr, fprlen );
+               }
+               break;
+            #endif
+             default: BUG();
+           }
+
+           kbpos->rt = resource_table[i].rt;
            if( !rc ) {
                kbpos->resno = i;
                kbpos->fp = NULL;
@@ -210,20 +429,20 @@ int
 find_keyblock_byname( KBPOS *kbpos, const char *username )
 {
     PACKET pkt;
-    PKT_public_cert *pkc = m_alloc_clear( sizeof *pkc );
+    PKT_public_key *pk = m_alloc_clear( sizeof *pk );
     int rc;
 
-    rc = get_pubkey_byname( pkc, username );
+    rc = get_pubkey_byname( NULL, pk, username, NULL );
     if( rc ) {
-       free_public_cert(pkc);
+       free_public_key(pk);
        return rc;
     }
 
     init_packet( &pkt );
-    pkt.pkttype = PKT_PUBLIC_CERT;
-    pkt.pkt.public_cert = pkc;
+    pkt.pkttype = PKT_PUBLIC_KEY;
+    pkt.pkt.public_key = pk;
     rc = search( &pkt, kbpos, 0 );
-    free_public_cert(pkc);
+    free_public_key(pk);
     return rc;
 }
 
@@ -233,14 +452,14 @@ find_keyblock_byname( KBPOS *kbpos, const char *username )
  * of the keyblock.
  */
 int
-find_keyblock_bypkc( KBPOS *kbpos, PKT_public_cert *pkc )
+find_keyblock_bypk( KBPOS *kbpos, PKT_public_key *pk )
 {
     PACKET pkt;
     int rc;
 
     init_packet( &pkt );
-    pkt.pkttype = PKT_PUBLIC_CERT;
-    pkt.pkt.public_cert = pkc;
+    pkt.pkttype = PKT_PUBLIC_KEY;
+    pkt.pkt.public_key = pk;
     rc = search( &pkt, kbpos, 0 );
     return rc;
 }
@@ -254,24 +473,116 @@ int
 find_secret_keyblock_byname( KBPOS *kbpos, const char *username )
 {
     PACKET pkt;
-    PKT_secret_cert *skc = m_alloc_clear( sizeof *skc );
+    PKT_secret_key *sk = m_alloc_clear( sizeof *sk );
     int rc;
 
-    rc = get_seckey_byname( skc, username, 0 );
+    rc = get_seckey_byname( sk, username, 0 );
     if( rc ) {
-       free_secret_cert(skc);
+       free_secret_key(sk);
        return rc;
     }
 
     init_packet( &pkt );
-    pkt.pkttype = PKT_SECRET_CERT;
-    pkt.pkt.secret_cert = skc;
+    pkt.pkttype = PKT_SECRET_KEY;
+    pkt.pkt.secret_key = sk;
     rc = search( &pkt, kbpos, 1 );
-    free_secret_cert(skc);
+    free_secret_key(sk);
     return rc;
 }
 
 
+/****************
+ * Locate a keyblock in a database which is capable of direct access
+ * Put all information into KBPOS, which can be later be to access this
+ * key block.
+ * This function looks into all registered keyblock sources.
+ *
+ * Returns: 0 if found,
+ *         -1 if not found
+ *         G10ERR_UNSUPPORTED if no resource is able to handle this
+ *         or another errorcode.
+ */
+int
+locate_keyblock_by_fpr( KBPOS *kbpos, const byte *fpr, int fprlen, int secret )
+{
+    RESTBL *rentry;
+    int i, rc, any=0, last_rc=-1;
+
+
+    for(i=0, rentry = resource_table; i < MAX_RESOURCES; i++, rentry++ ) {
+       if( rentry->used && !rentry->secret == !secret ) {
+           kbpos->rt = rentry->rt;
+           switch( rentry->rt ) {
+            #ifdef HAVE_LIBGDBM
+             case rt_GDBM:
+               any = 1;
+               rc = do_gdbm_locate( rentry->dbf, kbpos, fpr, fprlen );
+               break;
+            #endif
+             default:
+               rc = G10ERR_UNSUPPORTED;
+               break;
+           }
+
+           if( !rc ) {
+               kbpos->resno = i;
+               kbpos->fp = NULL;
+               return 0;
+           }
+           else if( rc != -1 && rc != G10ERR_UNSUPPORTED ) {
+               log_error("error searching resource %d: %s\n",
+                                                 i, g10_errstr(rc));
+               last_rc = rc;
+           }
+       }
+    }
+
+    return (last_rc == -1 && !any)? G10ERR_UNSUPPORTED : last_rc;
+}
+
+
+int
+locate_keyblock_by_keyid( KBPOS *kbpos, u32 *keyid, int shortkid, int secret )
+{
+    RESTBL *rentry;
+    int i, rc, any=0, last_rc=-1;
+
+    if( shortkid )
+       return G10ERR_UNSUPPORTED;
+
+    for(i=0, rentry = resource_table; i < MAX_RESOURCES; i++, rentry++ ) {
+       if( rentry->used && !rentry->secret == !secret ) {
+           kbpos->rt = rentry->rt;
+           switch( rentry->rt ) {
+            #ifdef HAVE_LIBGDBM
+             case rt_GDBM:
+               any = 1;
+               rc = do_gdbm_locate_by_keyid( rentry->dbf, kbpos, keyid );
+               break;
+            #endif
+             default:
+               rc = G10ERR_UNSUPPORTED;
+               break;
+           }
+
+           if( !rc ) {
+               kbpos->resno = i;
+               kbpos->fp = NULL;
+               return 0;
+           }
+           else if( rc != -1 && rc != G10ERR_UNSUPPORTED ) {
+               log_error("error searching resource %d: %s\n",
+                                                 i, g10_errstr(rc));
+               last_rc = rc;
+           }
+       }
+    }
+
+    return (last_rc == -1 && !any)? G10ERR_UNSUPPORTED : last_rc;
+}
+
+
+
 
 /****************
  * Lock the keyblock; wait until it's available
@@ -305,7 +616,16 @@ read_keyblock( KBPOS *kbpos, KBNODE *ret_root )
 {
     if( !check_pos(kbpos) )
        return G10ERR_GENERAL;
-    return keyring_read( kbpos, ret_root );
+
+    switch( kbpos->rt ) {
+      case rt_RING:
+       return keyring_read( kbpos, ret_root );
+     #ifdef HAVE_LIBGDBM
+      case rt_GDBM:
+       return do_gdbm_read( kbpos, ret_root );
+     #endif
+      default: BUG();
+    }
 }
 
 
@@ -319,7 +639,7 @@ read_keyblock( KBPOS *kbpos, KBNODE *ret_root )
  *         all others are reserved!
  * Note that you do not need a search prior to this function,
  * only a handle is needed.
- * NOTE: It is not allowed to do an insert/update/delte with this
+ * NOTE: It is not allowed to do an insert/update/delete with this
  *      keyblock, if you want to do this, use search/read!
  */
 int
@@ -350,10 +670,22 @@ enum_keyblocks( int mode, KBPOS *kbpos, KBNODE *ret_root )
            return -1; /* no resources */
        kbpos->resno = i;
        rentry = check_pos( kbpos );
-       kbpos->fp = iobuf_open( rentry->fname );
-       if( !kbpos->fp ) {
-           log_error("can't open '%s'\n", rentry->fname );
-           return G10ERR_OPEN_FILE;
+       kbpos->rt = resource_table[i].rt;
+       switch( kbpos->rt ) {
+         case rt_RING:
+           kbpos->fp = iobuf_fopen( rentry->fname, "rb" );
+           if( !kbpos->fp ) {
+               log_error("can't open '%s'\n", rentry->fname );
+               return G10ERR_OPEN_FILE;
+           }
+           break;
+        #ifdef HAVE_LIBGDBM
+         case rt_GDBM:
+           /* FIXME: make sure that there is only one enum at a time */
+           kbpos->offset = 0;
+           break;
+        #endif
+         default: BUG();
        }
        kbpos->pkt = NULL;
     }
@@ -361,9 +693,20 @@ enum_keyblocks( int mode, KBPOS *kbpos, KBNODE *ret_root )
        int cont;
        do {
            cont = 0;
-           if( !kbpos->fp )
-               return G10ERR_GENERAL;
-           rc = keyring_enum( kbpos, ret_root, mode == 11 );
+           switch( kbpos->rt ) {
+             case rt_RING:
+               if( !kbpos->fp )
+                   return G10ERR_GENERAL;
+               rc = keyring_enum( kbpos, ret_root, mode == 11 );
+               break;
+            #ifdef HAVE_LIBGDBM
+             case rt_GDBM:
+               rc = do_gdbm_enum( kbpos, ret_root );
+               break;
+            #endif
+             default: BUG();
+           }
+
            if( rc == -1 ) {
                assert( !kbpos->pkt );
                rentry = check_pos( kbpos );
@@ -377,9 +720,21 @@ enum_keyblocks( int mode, KBPOS *kbpos, KBNODE *ret_root )
            }
        } while(cont);
     }
-    else if( kbpos->fp ) {
-       iobuf_close( kbpos->fp );
-       kbpos->fp = NULL;
+    else {
+       switch( kbpos->rt ) {
+         case rt_RING:
+           if( kbpos->fp ) {
+               iobuf_close( kbpos->fp );
+               kbpos->fp = NULL;
+           }
+           break;
+         case rt_GDBM:
+           break;
+         default:
+           log_error("OOPS in close enum_keyblocks - ignored\n");
+           return rc;
+           break;
+       }
        /* release pending packet */
        free_packet( kbpos->pkt );
        m_free( kbpos->pkt );
@@ -402,7 +757,17 @@ insert_keyblock( KBPOS *kbpos, KBNODE root )
     if( !check_pos(kbpos) )
        return G10ERR_GENERAL;
 
-    rc = keyring_copy( kbpos, 1, root );
+    switch( kbpos->rt ) {
+      case rt_RING:
+       rc = keyring_copy( kbpos, 1, root );
+       break;
+     #ifdef HAVE_LIBGDBM
+      case rt_GDBM:
+       rc = do_gdbm_store( kbpos, root, 0 );
+       break;
+     #endif
+      default: BUG();
+    }
 
     return rc;
 }
@@ -421,7 +786,18 @@ delete_keyblock( KBPOS *kbpos )
     if( !check_pos(kbpos) )
        return G10ERR_GENERAL;
 
-    rc = keyring_copy( kbpos, 2, NULL );
+    switch( kbpos->rt ) {
+      case rt_RING:
+       rc = keyring_copy( kbpos, 2, NULL );
+       break;
+     #ifdef HAVE_LIBGDBM
+      case rt_GDBM:
+       log_debug("deleting gdbm keyblock is not yet implemented\n");
+       rc = 0;
+       break;
+     #endif
+      default: BUG();
+    }
 
     return rc;
 }
@@ -438,41 +814,173 @@ update_keyblock( KBPOS *kbpos, KBNODE root )
     if( !check_pos(kbpos) )
        return G10ERR_GENERAL;
 
-    rc = keyring_copy( kbpos, 3, root );
+    switch( kbpos->rt ) {
+      case rt_RING:
+       rc = keyring_copy( kbpos, 3, root );
+       break;
+     #ifdef HAVE_LIBGDBM
+      case rt_GDBM:
+       rc = do_gdbm_store( kbpos, root, 1 );
+       break;
+     #endif
+      default: BUG();
+    }
 
     return rc;
 }
 
 
+\f
+/****************************************************************
+ ********** Implemenation of a user ID database    **************
+ ****************************************************************/
+#if 0
+/****************
+ * Layout of the user ID db
+ *
+ * This user ID DB provides fast lookup of user ID, but the user ids are
+ * not in any specific order.
+ *
+ * A string "GnuPG user db", a \n.
+ * user ids of one key, delimited by \t,
+ * a # or ^ followed by a 20 byte fingerprint, followed by an \n 
+ * The literal characters =, \n, \t, #, ^ must be replaced by a equal sign
+ * and their hex value.
+ *
+ * (We use Boyer/Moore pattern matching)
+ */
+
+/****************
+ * This compiles pattern to the distance table, the table will be allocate
+ * here and must be freed by using free().
+ * Returns: Ptr to new allocated Table
+ *         Caller must free the table.
+ */
+
+static size_t *
+compile_bm_table( const byte *pattern, size_t len )
+{
+    ushort *dist;
+    int i;
+
+    dist = m_alloc_clear( 256 * sizeof *dist );
+    for(i=0; i < 256; i++ )
+       dist[i] = len;
+    for(i=0; i < len-1; i++ )
+       dTbl[p[i]] = len-i-1;
+    return dist;
+}
+
+
+
+
+/****************
+ * Search BUF of BUFLEN for pattern P of length PATLEN.
+ * dist is the Boyer/Moore distance table of 256 Elements,
+ * case insensitive search is done if IGNCASE is true (In this case
+ * the distance table has to compiled from uppercase chacaters and
+ * PAT must also be uppercase.
+ * Returns: Prt to maching string in BUF, or NULL if not found.
+ */
+
+static const *
+do_bm_search( const byte *buf, size_t buflen,
+             const byte *pat, size_t patlen, size_t *dist, int igncase )
+{
+    int i, j, k;
+
+    if( igncase ) {
+       int c, c1;
+
+       for( i = --patlen; i < buflen; i += dist[c1] )
+           for( j=patlen, k=i, c1=c=toupper(buf[k]); c == pat[j];
+                                         j--, k--, c=toupper(buf[k]) ) {
+               if( !j )
+                   return buf+k;
+           }
+    }
+    else {
+       for( i = --patlen; i < buflen; i += dist[buf[i]] )
+           for( j=patlen, k=i; buf[k] == pat[j]; j--, k-- ) {
+               if( !j )
+                   return buf+k;
+           }
+    }
+    return NULL;
+}
+
+
+typedef struct {
+    size_t dist[256];
+} *SCAN_USER_HANDLE;
+
+static SCAN_USER_HANDLE
+scan_user_file_open( const byte *name )
+{
+    SCAN_USER_HANDLE hd;
+    size_t *dist;
+    int i;
+
+    hd = m_alloc_clear( sizeof *hd );
+    dist = hd->dist;
+    /* compile the distance table */
+    for(i=0; i < 256; i++ )
+       dist[i] = len;
+    for(i=0; i < len-1; i++ )
+       dTbl[p[i]] = len-i-1;
+    /* setup other things */
+
+    return hd;
+}
+
+static int
+scan_user_file_close( SCAN_USER_HANDLE hd )
+{
+    m_free( hd );
+}
+
+static int
+scan_user_file_read( SCAN_USER_HANDLE hd, byte *fpr )
+{
+    char record[1000];
+
+    /* read a record */
+
+
+}
+#endif
+
+
+\f
 /****************************************************************
  ********** Functions which operates on regular keyrings ********
  ****************************************************************/
 
 static int
-cmp_seckey( PKT_secret_cert *req_skc, PKT_secret_cert *skc )
+cmp_seckey( PKT_secret_key *req_sk, PKT_secret_key *sk )
 {
     int n,i;
 
-    assert( req_skc->pubkey_algo == skc->pubkey_algo );
+    assert( req_sk->pubkey_algo == sk->pubkey_algo );
 
-    n = pubkey_get_nskey( req_skc->pubkey_algo );
+    n = pubkey_get_nskey( req_sk->pubkey_algo );
     for(i=0; i < n; i++ ) {
-       if( mpi_cmp( req_skc->skey[i], skc->skey[i] ) )
+       if( mpi_cmp( req_sk->skey[i], sk->skey[i] ) )
            return -1;
     }
     return 0;
 }
 
 static int
-cmp_pubkey( PKT_public_cert *req_pkc, PKT_public_cert *pkc )
+cmp_pubkey( PKT_public_key *req_pk, PKT_public_key *pk )
 {
     int n, i;
 
-    assert( req_pkc->pubkey_algo == pkc->pubkey_algo );
+    assert( req_pk->pubkey_algo == pk->pubkey_algo );
 
-    n = pubkey_get_npkey( req_pkc->pubkey_algo );
+    n = pubkey_get_npkey( req_pk->pubkey_algo );
     for(i=0; i < n; i++ ) {
-       if( mpi_cmp( req_pkc->pkey[i], pkc->pkey[i] )  )
+       if( mpi_cmp( req_pk->pkey[i], pk->pkey[i] )  )
            return -1;
     }
     return 0;
@@ -489,11 +997,12 @@ keyring_search( PACKET *req, KBPOS *kbpos, IOBUF iobuf, const char *fname )
     int save_mode;
     ulong offset;
     int pkttype = req->pkttype;
-    PKT_public_cert *req_pkc = req->pkt.public_cert;
-    PKT_secret_cert *req_skc = req->pkt.secret_cert;
+    PKT_public_key *req_pk = req->pkt.public_key;
+    PKT_secret_key *req_sk = req->pkt.secret_key;
 
     init_packet(&pkt);
     save_mode = set_packet_list_mode(0);
+    kbpos->rt = rt_RING;
 
   #if __MINGW32__
     assert(!iobuf);
@@ -512,22 +1021,20 @@ keyring_search( PACKET *req, KBPOS *kbpos, IOBUF iobuf, const char *fname )
   #endif
 
     while( !(rc=search_packet(iobuf, &pkt, pkttype, &offset)) ) {
-       if( pkt.pkttype == PKT_SECRET_CERT ) {
-           PKT_secret_cert *skc = pkt.pkt.secret_cert;
+       if( pkt.pkttype == PKT_SECRET_KEY ) {
+           PKT_secret_key *sk = pkt.pkt.secret_key;
 
-           if(   req_skc->timestamp == skc->timestamp
-              && req_skc->valid_days == skc->valid_days
-              && req_skc->pubkey_algo == skc->pubkey_algo
-              && !cmp_seckey( req_skc, skc) )
+           if(   req_sk->timestamp == sk->timestamp
+              && req_sk->pubkey_algo == sk->pubkey_algo
+              && !cmp_seckey( req_sk, sk) )
                break; /* found */
        }
-       else if( pkt.pkttype == PKT_PUBLIC_CERT ) {
-           PKT_public_cert *pkc = pkt.pkt.public_cert;
+       else if( pkt.pkttype == PKT_PUBLIC_KEY ) {
+           PKT_public_key *pk = pkt.pkt.public_key;
 
-           if(   req_pkc->timestamp == pkc->timestamp
-              && req_pkc->valid_days == pkc->valid_days
-              && req_pkc->pubkey_algo == pkc->pubkey_algo
-              && !cmp_pubkey( req_pkc, pkc ) )
+           if(   req_pk->timestamp == pk->timestamp
+              && req_pk->pubkey_algo == pk->pubkey_algo
+              && !cmp_pubkey( req_pk, pk ) )
                break; /* found */
        }
        else
@@ -560,7 +1067,7 @@ keyring_read( KBPOS *kbpos, KBNODE *ret_root )
     if( !(rentry=check_pos(kbpos)) )
        return G10ERR_GENERAL;
 
-    a = iobuf_open( rentry->fname );
+    a = iobuf_fopen( rentry->fname, "rb" );
     if( !a ) {
        log_error("can't open '%s'\n", rentry->fname );
        return G10ERR_OPEN_FILE;
@@ -582,7 +1089,6 @@ keyring_read( KBPOS *kbpos, KBNODE *ret_root )
                rc = G10ERR_INV_KEYRING;
                goto ready;
            }
-               log_info("read_keyblock: read error: %s\n", g10_errstr(rc) );
            kbpos->count++;
            free_packet( pkt );
            init_packet( pkt );
@@ -590,8 +1096,8 @@ keyring_read( KBPOS *kbpos, KBNODE *ret_root )
        }
        /* make a linked list of all packets */
        switch( pkt->pkttype ) {
-         case PKT_PUBLIC_CERT:
-         case PKT_SECRET_CERT:
+         case PKT_PUBLIC_KEY:
+         case PKT_SECRET_KEY:
            if( in_cert )
                goto ready;
            in_cert = 1;
@@ -652,8 +1158,8 @@ keyring_enum( KBPOS *kbpos, KBNODE *ret_root, int skipsigs )
        }
        /* make a linked list of all packets */
        switch( pkt->pkttype ) {
-         case PKT_PUBLIC_CERT:
-         case PKT_SECRET_CERT:
+         case PKT_PUBLIC_KEY:
+         case PKT_SECRET_KEY:
            if( root ) { /* store this packet */
                kbpos->pkt = pkt;
                pkt = NULL;
@@ -668,9 +1174,9 @@ keyring_enum( KBPOS *kbpos, KBNODE *ret_root, int skipsigs )
            /* skip pakets at the beginning of a keyring, until we find
             * a start packet; issue a warning if it is not a comment */
            if( !root && pkt->pkttype != PKT_COMMENT
-                     && pkt->pkttype != PKT_OLD_COMMENT )
-               log_info("keyring_enum: skipped packet of type %d\n",
-                           pkt->pkttype );
+                     && pkt->pkttype != PKT_OLD_COMMENT ) {
+               break;
+           }
            if( !root || (skipsigs && ( pkt->pkttype == PKT_SIGNATURE
                                      ||pkt->pkttype == PKT_COMMENT
                                      ||pkt->pkttype == PKT_OLD_COMMENT )) ) {
@@ -693,11 +1199,11 @@ keyring_enum( KBPOS *kbpos, KBNODE *ret_root, int skipsigs )
        *ret_root = root;
     free_packet( pkt );
     m_free( pkt );
+
     return rc;
 }
 
 
-
 /****************
  * Perform insert/delete/update operation.
  * mode 1 = insert
@@ -719,16 +1225,18 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root )
        BUG(); /* not allowed with such a handle */
 
     /* open the source file */
-    fp = iobuf_open( rentry->fname );
+    fp = iobuf_fopen( rentry->fname, "rb" );
     if( mode == 1 && !fp && errno == ENOENT ) { /* no file yet */
        KBNODE kbctx, node;
 
        /* insert: create a new file */
        newfp = iobuf_create( rentry->fname );
        if( !newfp ) {
-           log_error("%s: can't create: %s\n", rentry->fname, strerror(errno));
+           log_error(_("%s: can't create: %s\n"), rentry->fname, strerror(errno));
            return G10ERR_OPEN_FILE;
        }
+       else
+           log_info(_("%s: keyring created\n"), rentry->fname );
 
        kbctx=NULL;
        while( (node = walk_kbnode( root, &kbctx, 0 )) ) {
@@ -757,10 +1265,33 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root )
     }
 
     /* create the new file */
+  #ifdef __MINGW32__
+    /* Here is another Windoze bug?:
+     * you cant rename("pubring.gpg.tmp", "pubring.gpg");
+     * but     rename("pubring.gpg.tmp", "pubring.aaa");
+     * works.  So we replace .gpg by .bak or .tmp
+     */
+    if( strlen(rentry->fname) > 4
+       && !strcmp(rentry->fname+strlen(rentry->fname)-4, ".gpg") ) {
+       bakfname = m_alloc( strlen( rentry->fname ) + 1 );
+       strcpy(bakfname,rentry->fname);
+       strcpy(bakfname+strlen(rentry->fname)-4, ".bak");
+       tmpfname = m_alloc( strlen( rentry->fname ) + 1 );
+       strcpy(tmpfname,rentry->fname);
+       strcpy(tmpfname+strlen(rentry->fname)-4, ".tmp");
+    }
+    else { /* file does not end with gpg; hmmm */
+       bakfname = m_alloc( strlen( rentry->fname ) + 5 );
+       strcpy(stpcpy(bakfname,rentry->fname),".bak");
+       tmpfname = m_alloc( strlen( rentry->fname ) + 5 );
+       strcpy(stpcpy(tmpfname,rentry->fname),".tmp");
+    }
+  #else
     bakfname = m_alloc( strlen( rentry->fname ) + 2 );
     strcpy(stpcpy(bakfname,rentry->fname),"~");
     tmpfname = m_alloc( strlen( rentry->fname ) + 5 );
     strcpy(stpcpy(tmpfname,rentry->fname),".tmp");
+  #endif
     newfp = iobuf_create( tmpfname );
     if( !newfp ) {
        log_error("%s: can't create: %s\n", tmpfname, strerror(errno) );
@@ -846,6 +1377,7 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root )
        goto leave;
     }
     /* if the new file is a secring, restrict the permissions */
+  #ifndef __MINGW32__
     if( rentry->secret ) {
        if( chmod( tmpfname, S_IRUSR | S_IWUSR ) ) {
            log_error("%s: chmod failed: %s\n",
@@ -854,23 +1386,34 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root )
            goto leave;
        }
     }
-    /* rename and make backup file */
-  #if __MINGW32__
-    remove( bakfname );
   #endif
-    if( rename( rentry->fname, bakfname ) ) {
-       log_error("%s: rename to %s failed: %s\n",
-                               rentry->fname, bakfname, strerror(errno) );
-       rc = G10ERR_RENAME_FILE;
-       goto leave;
+
+    /* rename and make backup file */
+    if( !rentry->secret ) {  /* but not for secret keyrings */
+      #ifdef __MINGW32__
+       remove( bakfname );
+      #endif
+       if( rename( rentry->fname, bakfname ) ) {
+           log_error("%s: rename to %s failed: %s\n",
+                                   rentry->fname, bakfname, strerror(errno) );
+           rc = G10ERR_RENAME_FILE;
+           goto leave;
+       }
     }
-  #if __MINGW32__
+  #ifdef __MINGW32__
     remove( rentry->fname );
   #endif
     if( rename( tmpfname, rentry->fname ) ) {
        log_error("%s: rename to %s failed: %s\n",
                            tmpfname, rentry->fname,strerror(errno) );
        rc = G10ERR_RENAME_FILE;
+       if( rentry->secret ) {
+           log_info(_(
+               "WARNING: 2 files with confidential information exists.\n"));
+           log_info(_("%s is the unchanged one\n"), rentry->fname );
+           log_info(_("%s is the new one\n"), tmpfname );
+           log_info(_("Please fix this possible security flaw\n"));
+       }
        goto leave;
     }
 
@@ -880,8 +1423,294 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root )
     return rc;
 }
 
-
+\f
+#ifdef HAVE_LIBGDBM
 /****************************************************************
- ********** Functions which operates on databases ***************
+ ********** Functions which operates on GDM files ***************
  ****************************************************************/
 
+#if MAX_FINGERPRINT_LEN > 20
+  #error A GDBM keyring assumes that fingerprints are less than 21
+#endif
+
+/****************
+ * Insert the keyblock into the GDBM database
+ */
+
+static int
+do_gdbm_store( KBPOS *kbpos, KBNODE root, int update )
+{
+    RESTBL *rentry;
+    PKT_public_key *pk;
+    KBNODE kbctx, node;
+    IOBUF fp = NULL;
+    byte fpr[20];
+    byte contbuf[21];
+    byte keybuf[21];
+    size_t fprlen;
+    datum key, content;
+    int i, rc;
+
+    if( !(rentry = check_pos( kbpos )) )
+       return G10ERR_GENERAL;
+
+    /* construct the fingerprint which is used as the primary key */
+    node = find_kbnode( root, PKT_PUBLIC_KEY );
+    if( !node )
+       log_bug("a gdbm database can't store secret keys\n");
+    pk = node->pkt->pkt.public_key;
+
+    fingerprint_from_pk( pk, fpr, &fprlen );
+    for(i=fprlen; i < DIM(fpr); i++ )
+       fpr[i] = 0;
+
+    /* build the keyblock */
+    kbctx=NULL;
+    fp = iobuf_temp();
+    iobuf_put( fp, 1 ); /* data is a keyblock */
+    while( (node = walk_kbnode( root, &kbctx, 0 )) ) {
+       if( (rc = build_packet( fp, node->pkt )) ) {
+           log_error("build_packet(%d) failed: %s\n",
+                       node->pkt->pkttype, g10_errstr(rc) );
+           rc = G10ERR_WRITE_FILE;
+           goto leave;
+       }
+    }
+    /* store data and key */
+    *keybuf = 1;   /* key is a padded fingerprint */
+    memcpy(keybuf+1, fpr, 20 );
+    key.dptr  = keybuf;
+    key.dsize = 21;
+    content.dptr  = iobuf_get_temp_buffer( fp );
+    content.dsize = iobuf_get_temp_length( fp );
+    rc = gdbm_store( rentry->dbf, key, content,
+                                 update? GDBM_REPLACE : GDBM_INSERT );
+    if( rc ) {
+       log_error("%s: gdbm_store failed: %s\n", rentry->fname,
+                           rc == 1 ? "already stored"
+                                   : gdbm_strerror(gdbm_errno) );
+       rc = G10ERR_WRITE_FILE;
+       goto leave;
+    }
+    /* now store all keyids */
+    *contbuf = 2;  /* data is a list of fingerprints */
+    memcpy(contbuf+1, fpr, 20 );
+    content.dptr = contbuf;
+    content.dsize= 21;
+    kbctx=NULL;
+    while( (node = walk_kbnode( root, &kbctx, 0 )) ) {
+       if(    node->pkt->pkttype == PKT_PUBLIC_KEY
+           || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+           u32 aki[2];
+
+           keyid_from_pk( node->pkt->pkt.public_key, aki );
+           *keybuf = 2; /* key is a 8 byte keyid */
+           u32tobuf( keybuf+1  , aki[0] );
+           u32tobuf( keybuf+5, aki[1] );
+           key.dptr = keybuf;
+           key.dsize= 9;
+           /* fixme: must be more clever when a insert failed:
+            *        build a list of fingerprints in this case */
+           rc = gdbm_store( rentry->dbf, key, content,
+                                         update? GDBM_REPLACE : GDBM_INSERT );
+           if( rc ) {
+               log_info("%s: gdbm_store keyid failed: %s\n", rentry->fname,
+                                   rc == 1 ? "already stored"
+                                           : gdbm_strerror(gdbm_errno) );
+               rc = 0;
+           }
+       }
+    }
+
+  leave:
+    iobuf_close(fp); /* don't need a cancel because it is a temp iobuf */
+    return rc;
+}
+
+
+
+/****************
+ * search one keybox, return 0 if found, -1 if not found or an errorcode.
+ */
+static int
+do_gdbm_locate( GDBM_FILE dbf, KBPOS *kbpos, const byte *fpr, int fprlen )
+{
+    byte *keybuf = kbpos->keybuf;
+    datum key;
+    int i;
+
+    *keybuf = 1;
+    for(i=0; i < fprlen; i++ )
+       keybuf[i+1] = fpr[i];
+    for(; i < 20; i++ )
+       keybuf[i+1] = 0;
+
+    /* fetch the data */
+    key.dptr  = keybuf;
+    key.dsize = 21;
+    if( !gdbm_exists( dbf, key ) )
+       return -1; /* not found */
+    return 0;
+}
+
+/****************
+ * locate by keyid.
+ * FIXME: we must have a way to enumerate thru the list opf fingerprints
+ */
+static int
+do_gdbm_locate_by_keyid( GDBM_FILE dbf, KBPOS *kbpos, u32 *keyid )
+{
+    byte keybuf[9];
+    datum key, content;
+    int rc;
+
+    /* construct the fingerprint which is used as the primary key */
+    *keybuf = 2;
+    u32tobuf( keybuf+1, keyid[0] );
+    u32tobuf( keybuf+5, keyid[1] );
+
+    /* fetch the data */
+    key.dptr  = keybuf;
+    key.dsize = 9;
+    content = gdbm_fetch( dbf, key );
+    if( !content.dptr )
+       return -1;
+
+    if( content.dsize < 2 ) {
+       log_error("gdbm_fetch did not return enough data\n" );
+       free( content.dptr ); /* can't use m_free() here */
+       return G10ERR_INV_KEYRING;
+    }
+    if( *content.dptr != 2 ) {
+       log_error("gdbm_fetch returned unexpected type %d\n",
+                   *(byte*)content.dptr );
+       free( content.dptr ); /* can't use m_free() here */
+       return G10ERR_INV_KEYRING;
+    }
+    if( content.dsize < 21 ) {
+       log_error("gdbm_fetch did not return a complete fingerprint\n" );
+       free( content.dptr ); /* can't use m_free() here */
+       return G10ERR_INV_KEYRING;
+    }
+    if( content.dsize > 21 )
+       log_info("gdbm_fetch: WARNING: more than one fingerprint\n" );
+
+    rc = do_gdbm_locate( dbf, kbpos, content.dptr+1, 20 );
+    free( content.dptr ); /* can't use m_free() here */
+    return rc;
+}
+
+
+
+static int
+do_gdbm_read( KBPOS *kbpos, KBNODE *ret_root )
+{
+    PACKET *pkt;
+    int rc;
+    RESTBL *rentry;
+    KBNODE root = NULL;
+    IOBUF a;
+    datum key, content;
+
+    if( !(rentry=check_pos(kbpos)) )
+       return G10ERR_GENERAL;
+
+    key.dptr  = kbpos->keybuf;
+    key.dsize = 21;
+    content = gdbm_fetch( rentry->dbf, key );
+    if( !content.dptr ) {
+       log_error("gdbm_fetch failed: %s\n", gdbm_strerror(gdbm_errno) );
+       return G10ERR_INV_KEYRING;
+    }
+    if( content.dsize < 2 ) {
+       log_error("gdbm_fetch did not return enough data\n" );
+       free( content.dptr ); /* can't use m_free() here */
+       return G10ERR_INV_KEYRING;
+    }
+    if( *content.dptr != 1 ) {
+       log_error("gdbm_fetch returned unexpected type %d\n",
+                   *(byte*)content.dptr );
+       free( content.dptr ); /* can't use m_free() here */
+       return G10ERR_INV_KEYRING;
+    }
+
+    a = iobuf_temp_with_content( content.dptr+1, content.dsize-1 );
+    free( content.dptr ); /* can't use m_free() here */
+
+    pkt = m_alloc( sizeof *pkt );
+    init_packet(pkt);
+    kbpos->count=0;
+    while( (rc=parse_packet(a, pkt)) != -1 ) {
+       if( rc ) {  /* ignore errors */
+           if( rc != G10ERR_UNKNOWN_PACKET ) {
+               log_error("read_keyblock: read error: %s\n", g10_errstr(rc) );
+               rc = G10ERR_INV_KEYRING;
+               break;
+           }
+           kbpos->count++;
+           free_packet( pkt );
+           init_packet( pkt );
+           continue;
+       }
+       /* make a linked list of all packets */
+       kbpos->count++;
+       if( !root )
+           root = new_kbnode( pkt );
+       else
+           add_kbnode( root, new_kbnode( pkt ) );
+       pkt = m_alloc( sizeof *pkt );
+       init_packet(pkt);
+    }
+    if( rc == -1 && root )
+       rc = 0;
+    if( rc )
+       release_kbnode( root );
+    else
+       *ret_root = root;
+    free_packet( pkt );
+    m_free( pkt );
+    iobuf_close(a);
+    return rc;
+}
+
+
+/****************
+ * Enum over keyblok data
+ */
+static int
+do_gdbm_enum( KBPOS *kbpos, KBNODE *ret_root )
+{
+    RESTBL *rentry;
+    datum key, helpkey;
+
+    if( !(rentry=check_pos(kbpos)) )
+       return G10ERR_GENERAL;
+
+    if( !kbpos->offset ) {
+       kbpos->offset = 1;
+       key = gdbm_firstkey( rentry->dbf );
+    }
+    else {
+       helpkey.dptr = kbpos->keybuf;
+       helpkey.dsize= 21;
+       key = gdbm_nextkey( rentry->dbf, helpkey );
+    }
+    while( key.dptr && (!key.dsize || *key.dptr != 1) ) {
+       helpkey = key;
+       key = gdbm_nextkey( rentry->dbf, helpkey );
+       free( helpkey.dptr ); /* free and not m_free() ! */
+    }
+    if( !key.dptr )
+       return -1; /* eof */
+
+    if( key.dsize < 21 ) {
+       free( key.dptr ); /* free and not m_free() ! */
+       log_error("do_gdm_enum: key is too short\n" );
+       return G10ERR_INV_KEYRING;
+    }
+    memcpy( kbpos->keybuf, key.dptr, 21 );
+    free( key.dptr ); /* free and not m_free() ! */
+    return do_gdbm_read( kbpos, ret_root );
+}
+
+#endif /*HAVE_LIBGDBM*/