gpg: Rename cipher.c to cipher-cfb.c
[gnupg.git] / g10 / trustdb.c
index 1fd2383..0a98c12 100644 (file)
@@ -1,11 +1,12 @@
 /* trustdb.c
- *     Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+ *               2008, 2012 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <errno.h>
-#include <ctype.h>
-#include <assert.h>
+
+#ifndef DISABLE_REGEX
 #include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
+#include <regex.h>
+#endif /* !DISABLE_REGEX */
 
-#include "errors.h"
-#include "iobuf.h"
+#include "gpg.h"
+#include "../common/status.h"
+#include "../common/iobuf.h"
 #include "keydb.h"
-#include <gcrypt.h>
-#include "util.h"
-#include "trustdb.h"
+#include "../common/util.h"
 #include "options.h"
 #include "packet.h"
 #include "main.h"
-#include "i18n.h"
+#include "../common/mbox-util.h"
+#include "../common/i18n.h"
 #include "tdbio.h"
-#include "ttyio.h"
-
-#if MAX_FINGERPRINT_LEN > 20
-  #error Must change structure of trustdb
-#endif
-
-struct keyid_list {
-    struct keyid_list *next;
-    u32 keyid[2];
-};
-
-struct local_id_item {
-    struct local_id_item *next;
-    ulong lid;
-    unsigned flag;
-};
-
-struct local_id_table {
-    struct local_id_table *next; /* only used to keep a list of unused tables */
-    struct local_id_item *items[16];
-};
-
-
-typedef struct local_id_table *LOCAL_ID_TABLE;
-
+#include "trustdb.h"
+#include "tofu.h"
 
-struct enum_cert_paths_ctx {
-   int init;
-   int idx;
-};
 
+typedef struct key_item **KeyHashTable; /* see new_key_hash_table() */
 
-struct recno_list_struct {
-    struct recno_list_struct *next;
-    ulong recno;
-    int type;
-};
-typedef struct recno_list_struct *RECNO_LIST;
-
-
-
-typedef struct trust_node *TN;
-struct trust_node {
-    TN   back;  /* parent */
-    TN   list;  /* list of other node (should all be of the same type)*/
-    TN   next;  /* used to build the list */
-    int   is_uid; /* set if this is an uid node */
-    ulong lid;  /* key or uid recordnumber */
-    union {
-       struct {
-           int ownertrust;
-           int validity;
-           /* helper */
-           int buckstop;
-       } k;
-       struct {
-           int marginal_count;
-           int fully_count;
-           int validity;
-       } u;
-    } n;
+/*
+ * Structure to keep track of keys, this is used as an array wherre
+ * the item right after the last one has a keyblock set to NULL.
+ * Maybe we can drop this thing and replace it by key_item
+ */
+struct key_array
+{
+  KBNODE keyblock;
 };
 
 
-static TN used_tns;
-static int alloced_tns;
-static int max_alloced_tns;
-
-static struct keyid_list *trusted_key_list;
-
-static LOCAL_ID_TABLE new_lid_table(void);
-static int ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag );
-static int qry_lid_table_flag( LOCAL_ID_TABLE tbl, ulong lid, unsigned *flag );
-
-
-static int propagate_validity( TN root, TN node,
-                              int (*add_fnc)(ulong), unsigned *retflgs );
-
-static void print_user_id( FILE *fp, const char *text, u32 *keyid );
-static int do_check( TRUSTREC *drec, unsigned *trustlevel,
-                    const char *nhash, int (*add_fnc)(ulong),
-                                               unsigned *retflgs);
-static int get_dir_record( PKT_public_key *pk, TRUSTREC *rec );
-static int do_update_trust_record( KBNODE keyblock, TRUSTREC *drec,
-                                  int sigs_only, int *modified );
-static int check_trust_record( TRUSTREC *drec, int sigs_only );
-static void mark_fresh_keys(void);
-
-/* a table used to keep track of ultimately trusted keys
- * which are the ones from our secrings and the trusted keys */
-static LOCAL_ID_TABLE ultikey_table;
-
+/* Control information for the trust DB.  */
+static struct
+{
+  int init;
+  int level;
+  char *dbname;
+  int no_trustdb;
+} trustdb_args;
 
-/* a table to keep track of newly importted keys.  This one is
- * create by the insert_trust_record function and from time to time
- * used to verify key signature which have been done with these new keys */
-static LOCAL_ID_TABLE fresh_imported_keys;
-static int fresh_imported_keys_count;
-#define FRESH_KEY_CHECK_THRESHOLD 200
+/* Some globals.  */
+static struct key_item *user_utk_list; /* temp. used to store --trusted-keys */
+static struct key_item *utk_list;      /* all ultimately trusted keys */
 
-/* list of unused lid items and tables */
-static LOCAL_ID_TABLE unused_lid_tables;
-static struct local_id_item *unused_lid_items;
+static int pending_check_trustdb;
 
-static struct {
-    int init;
-    int level;
-    char *dbname;
-} trustdb_args;
+static int validate_keys (ctrl_t ctrl, int interactive);
 
 \f
 /**********************************************
- ***********  record read write  **************
+ ************* some helpers *******************
  **********************************************/
 
-
-/****************
- * Read a record but die if it does not exist
- */
-static void
-read_record( ulong recno, TRUSTREC *rec, int rectype )
+static struct key_item *
+new_key_item (void)
 {
-    int rc = tdbio_read_record( recno, rec, rectype );
-    if( !rc )
-       return;
-    log_error(_("trust record %lu, req type %d: read failed: %s\n"),
-                                   recno, rectype,  gpg_errstr(rc) );
-    tdbio_invalid();
-}
-
+  struct key_item *k;
 
-/****************
- * Wirte a record but die on error
- */
-static void
-write_record( TRUSTREC *rec )
-{
-    int rc = tdbio_write_record( rec );
-    if( !rc )
-       return;
-    log_error(_("trust record %lu, type %d: write failed: %s\n"),
-                           rec->recnum, rec->rectype, gpg_errstr(rc) );
-    tdbio_invalid();
+  k = xmalloc_clear (sizeof *k);
+  return k;
 }
 
-/****************
- * Delete a record but die on error
- */
 static void
-delete_record( ulong recno )
+release_key_items (struct key_item *k)
 {
-    int rc = tdbio_delete_record( recno );
-    if( !rc )
-       return;
-    log_error(_("trust record %lu: delete failed: %s\n"),
-                                             recno, gpg_errstr(rc) );
-    tdbio_invalid();
-}
+  struct key_item *k2;
 
-/****************
- * sync the db
- */
-static void
-do_sync(void)
-{
-    int rc = tdbio_sync();
-    if( !rc )
-       return;
-    log_error(_("trustdb: sync failed: %s\n"), gpg_errstr(rc) );
-    gpg_exit(2);
+  for (; k; k = k2)
+    {
+      k2 = k->next;
+      xfree (k->trust_regexp);
+      xfree (k);
+    }
 }
 
+#define KEY_HASH_TABLE_SIZE 1024
 
-\f
-/**********************************************
- *****************  helpers  ******************
- **********************************************/
-
-
-static LOCAL_ID_TABLE
-new_lid_table(void)
+/*
+ * For fast keylook up we need a hash table.  Each byte of a KeyID
+ * should be distributed equally over the 256 possible values (except
+ * for v3 keyIDs but we consider them as not important here). So we
+ * can just use 10 bits to index a table of KEY_HASH_TABLE_SIZE key items.
+ * Possible optimization: Do not use key_items but other hash_table when the
+ * duplicates lists get too large.
+ */
+static KeyHashTable
+new_key_hash_table (void)
 {
-    LOCAL_ID_TABLE a;
+  struct key_item **tbl;
 
-    a = unused_lid_tables;
-    if( a ) {
-       unused_lid_tables = a->next;
-       memset( a, 0, sizeof *a );
-    }
-    else
-       a = gcry_xcalloc( 1, sizeof *a );
-    return a;
+  tbl = xmalloc_clear (KEY_HASH_TABLE_SIZE * sizeof *tbl);
+  return tbl;
 }
 
-#if 0
 static void
-release_lid_table( LOCAL_ID_TABLE tbl )
+release_key_hash_table (KeyHashTable tbl)
 {
-    struct local_id_item *a, *a2;
-    int i;
-
-    for(i=0; i < 16; i++ ) {
-       for(a=tbl->items[i]; a; a = a2 ) {
-           a2 = a->next;
-           a->next = unused_lid_items;
-           unused_lid_items = a;
-       }
-    }
-    tbl->next = unused_lid_tables;
-    unused_lid_tables = tbl;
-}
-#endif
-
+  int i;
 
-/****************
- * Remove all items from a LID table
- */
-static void
-clear_lid_table( LOCAL_ID_TABLE tbl )
-{
-    struct local_id_item *a, *a2;
-    int i;
-
-    for(i=0; i < 16; i++ ) {
-       for(a=tbl->items[i]; a; a = a2 ) {
-           a2 = a->next;
-           a->next = unused_lid_items;
-           unused_lid_items = a;
-       }
-       tbl->items[i] = NULL;
-    }
+  if (!tbl)
+    return;
+  for (i=0; i < KEY_HASH_TABLE_SIZE; i++)
+    release_key_items (tbl[i]);
+  xfree (tbl);
 }
 
-
-/****************
- * Add a new item to the table or return 1 if we already have this item
+/*
+ * Returns: True if the keyID is in the given hash table
  */
 static int
-ins_lid_table_item( LOCAL_ID_TABLE tbl, ulong lid, unsigned flag )
+test_key_hash_table (KeyHashTable tbl, u32 *kid)
 {
-    struct local_id_item *a;
-
-    for( a = tbl->items[lid & 0x0f]; a; a = a->next )
-       if( a->lid == lid )
-           return 1;
-    a = unused_lid_items;
-    if( a )
-       unused_lid_items = a->next;
-    else
-       a = gcry_xmalloc( sizeof *a );
-    a->lid = lid;
-    a->flag = flag;
-    a->next = tbl->items[lid & 0x0f];
-    tbl->items[lid & 0x0f] = a;
-    return 0;
-}
-
-static int
-qry_lid_table_flag( LOCAL_ID_TABLE tbl, ulong lid, unsigned *flag )
-{
-    struct local_id_item *a;
+  struct key_item *k;
 
-    for( a = tbl->items[lid & 0x0f]; a; a = a->next )
-       if( a->lid == lid ) {
-           if( flag )
-               *flag = a->flag;
-           return 0;
-       }
-    return -1;
+  for (k = tbl[(kid[1] % KEY_HASH_TABLE_SIZE)]; k; k = k->next)
+    if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
+      return 1;
+  return 0;
 }
 
-
-static TN
-new_tn(void)
+/*
+ * Add a new key to the hash table.  The key is identified by its key ID.
+ */
+static void
+add_key_hash_table (KeyHashTable tbl, u32 *kid)
 {
-    TN t;
-
-    if( used_tns ) {
-       t = used_tns;
-       used_tns = t->next;
-       memset( t, 0, sizeof *t );
-    }
-    else
-       t = gcry_xcalloc( 1, sizeof *t );
-    if( ++alloced_tns > max_alloced_tns )
-       max_alloced_tns = alloced_tns;
-    return t;
-}
+  int i = kid[1] % KEY_HASH_TABLE_SIZE;
+  struct key_item *k, *kk;
 
+  for (k = tbl[i]; k; k = k->next)
+    if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
+      return; /* already in table */
 
-static void
-release_tn( TN t )
-{
-    if( t ) {
-       t->next = used_tns;
-       used_tns = t;
-       alloced_tns--;
-    }
+  kk = new_key_item ();
+  kk->kid[0] = kid[0];
+  kk->kid[1] = kid[1];
+  kk->next = tbl[i];
+  tbl[i] = kk;
 }
 
-
+/*
+ * Release a key_array
+ */
 static void
-release_tn_tree( TN kr )
+release_key_array ( struct key_array *keys )
 {
-    TN kr2;
+    struct key_array *k;
 
-    for( ; kr; kr = kr2 ) {
-       release_tn_tree( kr->list );
-       kr2 = kr->next;
-       release_tn( kr );
+    if (keys) {
+        for (k=keys; k->keyblock; k++)
+            release_kbnode (k->keyblock);
+        xfree (keys);
     }
 }
 
+\f
+/*********************************************
+ **********  Initialization  *****************
+ *********************************************/
 
 
-\f
-/**********************************************
- ****** access by LID and other helpers *******
- **********************************************/
 
-/****************
- * Return the keyid from the primary key identified by LID.
+/*
+ * Used to register extra ultimately trusted keys - this has to be done
+ * before initializing the validation module.
+ * FIXME: Should be replaced by a function to add those keys to the trustdb.
  */
-int
-keyid_from_lid( ulong lid, u32 *keyid )
+void
+tdb_register_trusted_keyid (u32 *keyid)
 {
-    TRUSTREC rec;
-    int rc;
-
-    init_trustdb();
-    keyid[0] = keyid[1] = 0;
-    rc = tdbio_read_record( lid, &rec, 0 );
-    if( rc ) {
-       log_error(_("error reading dir record for LID %lu: %s\n"),
-                                                   lid, gpg_errstr(rc));
-       return GPGERR_TRUSTDB;
-    }
-    if( rec.rectype == RECTYPE_SDIR )
-       return 0;
-    if( rec.rectype != RECTYPE_DIR ) {
-       log_error(_("lid %lu: expected dir record, got type %d\n"),
-                                                   lid, rec.rectype );
-       return GPGERR_TRUSTDB;
-    }
-    if( !rec.r.dir.keylist ) {
-       log_error(_("no primary key for LID %lu\n"), lid );
-       return GPGERR_TRUSTDB;
-    }
-    rc = tdbio_read_record( rec.r.dir.keylist, &rec, RECTYPE_KEY );
-    if( rc ) {
-       log_error(_("error reading primary key for LID %lu: %s\n"),
-                                                   lid, gpg_errstr(rc));
-       return GPGERR_TRUSTDB;
-    }
-    keyid_from_fingerprint( rec.r.key.fingerprint, rec.r.key.fingerprint_len,
-                           keyid );
+  struct key_item *k;
 
-    return 0;
+  k = new_key_item ();
+  k->kid[0] = keyid[0];
+  k->kid[1] = keyid[1];
+  k->next = user_utk_list;
+  user_utk_list = k;
 }
 
-
-ulong
-lid_from_keyblock( KBNODE keyblock )
+void
+tdb_register_trusted_key( const char *string )
 {
-    KBNODE node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
-    PKT_public_key *pk;
-    if( !node )
-       BUG();
-    pk = node->pkt->pkt.public_key;
-    if( !pk->local_id ) {
-       TRUSTREC rec;
-       init_trustdb();
-
-       get_dir_record( pk, &rec );
+  gpg_error_t err;
+  KEYDB_SEARCH_DESC desc;
+
+  err = classify_user_id (string, &desc, 1);
+  if (err || desc.mode != KEYDB_SEARCH_MODE_LONG_KID )
+    {
+      log_error(_("'%s' is not a valid long keyID\n"), string );
+      return;
     }
-    return pk->local_id;
-}
 
+  register_trusted_keyid(desc.u.kid);
+}
 
+/*
+ * Helper to add a key to the global list of ultimately trusted keys.
+ * Returns: true = inserted, false = already in list.
+ */
 static int
-get_dir_record( PKT_public_key *pk, TRUSTREC *rec )
+add_utk (u32 *kid)
 {
-    int rc=0;
+  struct key_item *k;
 
-    if( pk->local_id ) {
-       read_record( pk->local_id, rec, RECTYPE_DIR );
-    }
-    else { /* no local_id: scan the trustdb */
-       if( (rc=tdbio_search_dir_bypk( pk, rec )) && rc != -1 )
-           log_error(_("get_dir_record: search_record failed: %s\n"),
-                                                           gpg_errstr(rc));
-    }
-    return rc;
+  if (tdb_keyid_is_utk (kid))
+    return 0;
+
+  k = new_key_item ();
+  k->kid[0] = kid[0];
+  k->kid[1] = kid[1];
+  k->ownertrust = TRUST_ULTIMATE;
+  k->next = utk_list;
+  utk_list = k;
+  if( opt.verbose > 1 )
+    log_info(_("key %s: accepted as trusted key\n"), keystr(kid));
+  return 1;
 }
 
-static ulong
-lid_from_keyid_no_sdir( u32 *keyid )
+
+/****************
+ * Verify that all our secret keys are usable and put them into the utk_list.
+ */
+static void
+verify_own_keys (ctrl_t ctrl)
 {
-    PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk );
-    TRUSTREC rec;
-    ulong lid = 0;
-    int rc;
-
-    rc = get_pubkey( pk, keyid );
-    if( !rc ) {
-       if( pk->local_id )
-           lid = pk->local_id;
-       else {
-           rc = tdbio_search_dir_bypk( pk, &rec );
-           if( !rc )
-               lid = rec.recnum;
-       }
-    }
-    free_public_key( pk );
-    return lid;
-}
+  TRUSTREC rec;
+  ulong recnum;
+  int rc;
+  struct key_item *k;
 
+  if (utk_list)
+    return;
 
-\f
-/***********************************************
- ************* Initialization  ****************
- ***********************************************/
+  /* scan the trustdb to find all ultimately trusted keys */
+  for (recnum=1; !tdbio_read_record (recnum, &rec, 0); recnum++ )
+    {
+      if ( rec.rectype == RECTYPE_TRUST
+           && (rec.r.trust.ownertrust & TRUST_MASK) == TRUST_ULTIMATE)
+        {
+            byte *fpr = rec.r.trust.fingerprint;
+            int fprlen;
+            u32 kid[2];
+
+            /* Problem: We do only use fingerprints in the trustdb but
+             * we need the keyID here to indetify the key; we can only
+             * use that ugly hack to distinguish between 16 and 20
+             * butes fpr - it does not work always so we better change
+             * the whole validation code to only work with
+             * fingerprints */
+            fprlen = (!fpr[16] && !fpr[17] && !fpr[18] && !fpr[19])? 16:20;
+            keyid_from_fingerprint (ctrl, fpr, fprlen, kid);
+            if (!add_utk (kid))
+             log_info(_("key %s occurs more than once in the trustdb\n"),
+                      keystr(kid));
+        }
+    }
 
-void
-register_trusted_key( const char *string )
-{
-    u32 keyid[2];
-    struct keyid_list *r;
+  /* Put any --trusted-key keys into the trustdb */
+  for (k = user_utk_list; k; k = k->next)
+    {
+      if ( add_utk (k->kid) )
+        { /* not yet in trustDB as ultimately trusted */
+          PKT_public_key pk;
+
+          memset (&pk, 0, sizeof pk);
+          rc = get_pubkey (ctrl, &pk, k->kid);
+          if (rc)
+           log_info(_("key %s: no public key for trusted key - skipped\n"),
+                    keystr(k->kid));
+          else
+           {
+             tdb_update_ownertrust
+                (ctrl, &pk, ((tdb_get_ownertrust (ctrl, &pk, 0) & ~TRUST_MASK)
+                             | TRUST_ULTIMATE ));
+             release_public_key_parts (&pk);
+           }
 
-    if( classify_user_id( string, keyid, NULL, NULL, NULL ) != 11 ) {
-        log_error(_("'%s' is not a valid long keyID\n"), string );
-        return;
+          log_info (_("key %s marked as ultimately trusted\n"),keystr(k->kid));
+        }
     }
 
-    for( r = trusted_key_list; r; r = r->next )
-        if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] )
-            return;
-    r = gcry_xmalloc( sizeof *r );
-    r->keyid[0] = keyid[0];
-    r->keyid[1] = keyid[1];
-    r->next = trusted_key_list;
-    trusted_key_list = r;
+  /* release the helper table table */
+  release_key_items (user_utk_list);
+  user_utk_list = NULL;
+  return;
 }
 
+/* Returns whether KID is on the list of ultimately trusted keys.  */
+int
+tdb_keyid_is_utk (u32 *kid)
+{
+  struct key_item *k;
+
+  for (k = utk_list; k; k = k->next)
+    if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
+      return 1;
 
+  return 0;
+}
+
+/* Return the list of ultimately trusted keys.  */
+struct key_item *
+tdb_utks (void)
+{
+  return utk_list;
+}
+\f
+/*********************************************
+ *********** TrustDB stuff *******************
+ *********************************************/
 
+/*
+ * Read a record but die if it does not exist
+ */
 static void
-add_ultimate_key( PKT_public_key *pk, u32 *keyid )
+read_record (ulong recno, TRUSTREC *rec, int rectype )
 {
-    int rc;
-
-    /* first make sure that the pubkey is in the trustdb */
-    rc = query_trust_record( pk );
-    if( rc == -1 && opt.dry_run )
-       return;
-    if( rc == -1 ) { /* put it into the trustdb */
-        rc = insert_trust_record_by_pk( pk );
-        if( rc ) {
-            log_error(_("key %08lX: can't put it into the trustdb\n"),
-                      (ulong)keyid[1] );
-            return;
-        }
+  int rc = tdbio_read_record (recno, rec, rectype);
+  if (rc)
+    {
+      log_error(_("trust record %lu, req type %d: read failed: %s\n"),
+                recno, rec->rectype, gpg_strerror (rc) );
+      tdbio_invalid();
     }
-    else if( rc ) {
-        log_error(_("key %08lX: query record failed\n"), (ulong)keyid[1] );
-        return;
+  if (rectype != rec->rectype)
+    {
+      log_error(_("trust record %lu is not of requested type %d\n"),
+                rec->recnum, rectype);
+      tdbio_invalid();
     }
-
-    if( DBG_TRUST )
-        log_debug("key %08lX.%lu: stored into ultikey_table\n",
-                  (ulong)keyid[1], pk->local_id );
-
-    if( ins_lid_table_item( ultikey_table, pk->local_id, 0 ) )
-        log_error(_("key %08lX: already in trusted key table\n"),
-                  (ulong)keyid[1]);
-    else if( opt.verbose > 1 )
-        log_info(_("key %08lX: accepted as trusted key.\n"),
-                 (ulong)keyid[1]);
-
 }
 
-/****************
- * Verify that all our public keys are in the trustdb.
+/*
+ * Write a record and die on error
  */
-static int
-verify_own_keys(void)
+static void
+write_record (ctrl_t ctrl, TRUSTREC *rec)
 {
-    int rc;
-    void *enum_context = NULL;
-    PKT_secret_key *sk = gcry_xcalloc( 1, sizeof *sk );
-    PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk );
-    u32 keyid[2];
-    struct keyid_list *kl;
-
-
-    /* put the trusted keys into the ultikey table */
-    for( kl = trusted_key_list; kl; kl = kl->next ) {
-        keyid[0] = kl->keyid[0];
-        keyid[1] = kl->keyid[1];
-        /* get the public key */
-        memset( pk, 0, sizeof *pk );
-        rc = get_pubkey( pk, keyid );
-        if( rc ) {
-            log_info(_("key %08lX: no public key for trusted key - skipped\n"),
-                                                            (ulong)keyid[1] );
-        }
-        else {
-            add_ultimate_key( pk, keyid );
-            release_public_key_parts( pk );
-        }
+  int rc = tdbio_write_record (ctrl, rec);
+  if (rc)
+    {
+      log_error(_("trust record %lu, type %d: write failed: %s\n"),
+                           rec->recnum, rec->rectype, gpg_strerror (rc) );
+      tdbio_invalid();
     }
+}
 
-    /* And now add all secret keys to the ultikey table */
-    while( !(rc=enum_secret_keys( &enum_context, sk, 0 ) ) ) {
-       int have_pk = 0;
-
-       keyid_from_sk( sk, keyid );
-
-       if( DBG_TRUST )
-           log_debug("key %08lX: checking secret key\n", (ulong)keyid[1] );
-
-       if( !opt.quiet && is_secret_key_protected( sk ) < 1 )
-           log_info(_("NOTE: secret key %08lX is NOT protected.\n"),
-                                                           (ulong)keyid[1] );
-
-        for( kl = trusted_key_list; kl; kl = kl->next ) {
-            if( kl->keyid[0] == keyid[0] && kl->keyid[1] == keyid[1] )
-                goto skip; /* already in trusted key table */
-        }
-
-       /* see whether we can access the public key of this secret key */
-       memset( pk, 0, sizeof *pk );
-       rc = get_pubkey( pk, keyid );
-       if( rc ) {
-           log_info(_("key %08lX: secret key without public key - skipped\n"),
-                                                           (ulong)keyid[1] );
-           goto skip;
-       }
-       have_pk=1;
-
-       if( cmp_public_secret_key( pk, sk ) ) {
-           log_info(_("key %08lX: secret and public key don't match\n"),
-                                                           (ulong)keyid[1] );
-           goto skip;
-       }
-
-       add_ultimate_key( pk, keyid );
+/*
+ * sync the TrustDb and die on error
+ */
+static void
+do_sync(void)
+{
+    int rc = tdbio_sync ();
+    if(rc)
+      {
+        log_error (_("trustdb: sync failed: %s\n"), gpg_strerror (rc) );
+        g10_exit(2);
+      }
+}
 
-      skip:
-       release_secret_key_parts( sk );
-       if( have_pk )
-           release_public_key_parts( pk );
-    }
-    if( rc != -1 )
-       log_error(_("enumerate secret keys failed: %s\n"), gpg_errstr(rc) );
-    else
-       rc = 0;
-
-    /* release the trusted keyid table */
-    {   struct keyid_list *kl2;
-        for( kl = trusted_key_list; kl; kl = kl2 ) {
-            kl2 = kl->next;
-            gcry_free( kl );
-        }
-        trusted_key_list = NULL;
+const char *
+trust_model_string (int model)
+{
+  switch (model)
+    {
+    case TM_CLASSIC:  return "classic";
+    case TM_PGP:      return "pgp";
+    case TM_EXTERNAL: return "external";
+    case TM_TOFU:     return "tofu";
+    case TM_TOFU_PGP: return "tofu+pgp";
+    case TM_ALWAYS:   return "always";
+    case TM_DIRECT:   return "direct";
+    default:          return "unknown";
     }
-
-    enum_secret_keys( &enum_context, NULL, 0 ); /* free context */
-    free_secret_key( sk );
-    free_public_key( pk );
-    return rc;
 }
 
-
-
-
 /****************
  * Perform some checks over the trustdb
  *  level 0: only open the db
@@ -629,2198 +414,1844 @@ setup_trustdb( int level, const char *dbname )
     if( trustdb_args.init )
        return 0;
     trustdb_args.level = level;
-    trustdb_args.dbname = dbname? gcry_xstrdup(dbname): NULL;
+    trustdb_args.dbname = dbname? xstrdup(dbname): NULL;
     return 0;
 }
 
 void
-init_trustdb()
+how_to_fix_the_trustdb ()
 {
-    int rc=0;
-    int level = trustdb_args.level;
-    const char* dbname = trustdb_args.dbname;
+  const char *name = trustdb_args.dbname;
 
-    if( trustdb_args.init )
-       return;
+  if (!name)
+    name = "trustdb.gpg";
 
-    trustdb_args.init = 1;
+  log_info (_("You may try to re-create the trustdb using the commands:\n"));
+  log_info ("  cd %s\n", default_homedir ());
+  log_info ("  %s --export-ownertrust > otrust.tmp\n", GPG_NAME);
+#ifdef HAVE_W32_SYSTEM
+  log_info ("  del %s\n", name);
+#else
+  log_info ("  rm %s\n", name);
+#endif
+  log_info ("  %s --import-ownertrust < otrust.tmp\n", GPG_NAME);
+  log_info (_("If that does not work, please consult the manual\n"));
+}
 
-    if( !ultikey_table )
-       ultikey_table = new_lid_table();
 
-    if( !level || level==1 ) {
-       rc = tdbio_set_dbname( dbname, !!level );
-       if( !rc ) {
-           if( !level )
-               return;
+/* Initialize the trustdb.  With NO_CREATE set a missing trustdb is
+ * not an error and the function won't terminate the process on error;
+ * in that case 0 is returned if there is a trustdb or an error code
+ * if no trustdb is available.  */
+gpg_error_t
+init_trustdb (ctrl_t ctrl, int no_create)
+{
+  int level = trustdb_args.level;
+  const char* dbname = trustdb_args.dbname;
 
-           /* verify that our own keys are in the trustDB
-            * or move them to the trustdb. */
-           rc = verify_own_keys();
+  if( trustdb_args.init )
+    return 0;
 
-           /* should we check whether there is no other ultimately trusted
-            * key in the database? */
+  trustdb_args.init = 1;
+
+  if(level==0 || level==1)
+    {
+      int rc = tdbio_set_dbname (ctrl, dbname, (!no_create && level),
+                                 &trustdb_args.no_trustdb);
+      if (no_create && trustdb_args.no_trustdb)
+        {
+          /* No trustdb found and the caller asked us not to create
+           * it.  Return an error and set the initialization state
+           * back so that we always test for an existing trustdb.  */
+          trustdb_args.init = 0;
+          return gpg_error (GPG_ERR_ENOENT);
+        }
+      if (rc)
+       log_fatal("can't init trustdb: %s\n", gpg_strerror (rc) );
+    }
+  else
+    BUG();
+
+  if(opt.trust_model==TM_AUTO)
+    {
+      /* Try and set the trust model off of whatever the trustdb says
+        it is. */
+      opt.trust_model=tdbio_read_model();
+
+      /* Sanity check this ;) */
+      if(opt.trust_model != TM_CLASSIC
+        && opt.trust_model != TM_PGP
+        && opt.trust_model != TM_TOFU_PGP
+        && opt.trust_model != TM_TOFU
+        && opt.trust_model != TM_EXTERNAL)
+       {
+         log_info(_("unable to use unknown trust model (%d) - "
+                    "assuming %s trust model\n"),opt.trust_model,"pgp");
+         opt.trust_model = TM_PGP;
        }
+
+      if(opt.verbose)
+       log_info(_("using %s trust model\n"),
+                 trust_model_string (opt.trust_model));
     }
-    else
-       BUG();
-    if( rc )
-       log_fatal("can't init trustdb: %s\n", gpg_errstr(rc) );
-}
 
+  if (opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC
+      || opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)
+    {
+      /* Verify the list of ultimately trusted keys and move the
+        --trusted-keys list there as well. */
+      if(level==1)
+       verify_own_keys (ctrl);
 
+      if(!tdbio_db_matches_options())
+       pending_check_trustdb=1;
+    }
 
-/****************
- * This function should be called in certain cases to sync the internal state
- * of the trustdb with the file image. Currently it is needed after
- * a sequence of insert_trust_record() calls.
- */
-void
-sync_trustdb()
-{
-    if( fresh_imported_keys && fresh_imported_keys_count )
-       mark_fresh_keys();
+  return 0;
 }
 
 
-\f
-/***********************************************
- ************* Print helpers   ****************
- ***********************************************/
-static void
-print_user_id( FILE *fp, const char *text, u32 *keyid )
+/* Check whether we have a trust database, initializing it if
+   necessary if the trust model is not 'always trust'.  Returns true
+   if we do have a usable trust database.  */
+int
+have_trustdb (ctrl_t ctrl)
 {
-    char *p;
-    size_t n;
-
-    p = get_user_id( keyid, &n );
-    if( fp ) {
-       fprintf( fp, "%s \"", text );
-       print_utf8_string( fp, p, n );
-       putc('\"', fp);
-       putc('\n', fp);
-    }
-    else {
-       tty_printf( "%s \"", text );
-       tty_print_utf8_string( p, n );
-       tty_printf( "\"\n" );
-    }
-    gcry_free(p);
+  return !init_trustdb (ctrl, opt.trust_model == TM_ALWAYS);
 }
 
 
-
 /****************
- * This function returns a letter for a trustvalue  Trust flags
- * are ignore.
+ * Recreate the WoT but do not ask for new ownertrusts.  Special
+ * feature: In batch mode and without a forced yes, this is only done
+ * when a check is due.  This can be used to run the check from a crontab
  */
-int
-trust_letter( unsigned value )
-{
-    switch( (value & TRUST_MASK) ) {
-      case TRUST_UNKNOWN:   return '-';
-      case TRUST_EXPIRED:   return 'e';
-      case TRUST_UNDEFINED: return 'q';
-      case TRUST_NEVER:     return 'n';
-      case TRUST_MARGINAL:  return 'm';
-      case TRUST_FULLY:     return 'f';
-      case TRUST_ULTIMATE:  return 'u';
-      default:             return  0 ;
-    }
-}
+void
+check_trustdb (ctrl_t ctrl)
+{
+  init_trustdb (ctrl, 0);
+  if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC
+      || opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_TOFU)
+    {
+      if (opt.batch && !opt.answer_yes)
+       {
+         ulong scheduled;
+
+         scheduled = tdbio_read_nextcheck ();
+         if (!scheduled)
+           {
+             log_info (_("no need for a trustdb check\n"));
+             return;
+           }
 
+         if (scheduled > make_timestamp ())
+           {
+             log_info (_("next trustdb check due at %s\n"),
+                       strtimestamp (scheduled));
+             return;
+           }
+       }
 
-#if 0
-static void
-print_path( int pathlen, TN ME .........., FILE *fp, ulong highlight )
-{
-    int rc, c, i;
-    u32 keyid[2];
-    char *p;
-    size_t n;
-
-    for( i = 0; i < pathlen; i++ )  {
-       if( highlight )
-           fputs(highlight == path[i].lid? "* ":"  ", fp );
-       rc = keyid_from_lid( path[i].lid, keyid );
-       if( rc )
-           fprintf(fp, "????????.%lu:", path[i].lid );
-       else
-           fprintf(fp,"%08lX.%lu:", (ulong)keyid[1], path[i].lid );
-       c = trust_letter(path[i].otrust);
-       if( c )
-           putc( c, fp );
-       else
-           fprintf( fp, "%02x", path[i].otrust );
-       putc('/', fp);
-       c = trust_letter(path[i].trust);
-       if( c )
-           putc( c, fp );
-       else
-           fprintf( fp, "%02x", path[i].trust );
-       putc(' ', fp);
-       p = get_user_id( keyid, &n );
-       putc(' ', fp);
-       putc('\"', fp);
-       print_utf8_string( fp, p, n > 40? 40:n );
-       putc('\"', fp);
-       gcry_free(p);
-       putc('\n', fp );
+      validate_keys (ctrl, 0);
     }
+  else
+    log_info (_("no need for a trustdb check with '%s' trust model\n"),
+             trust_model_string(opt.trust_model));
 }
-#endif
 
 
-static void
-print_default_uid( FILE *fp, ulong lid )
+/*
+ * Recreate the WoT.
+ */
+void
+update_trustdb (ctrl_t ctrl)
 {
-    u32 keyid[2];
-
-    if( !keyid_from_lid( lid, keyid ) )
-       print_user_id( fp, "", keyid );
+  init_trustdb (ctrl, 0);
+  if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC
+      || opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_TOFU)
+    validate_keys (ctrl, 1);
+  else
+    log_info (_("no need for a trustdb update with '%s' trust model\n"),
+             trust_model_string(opt.trust_model));
 }
 
-
-static void
-print_uid_from_keyblock( FILE *fp, KBNODE keyblock, ulong urecno )
+void
+tdb_revalidation_mark (ctrl_t ctrl)
 {
-    TRUSTREC urec;
-    KBNODE node;
-    byte uhash[20];
-
-    read_record( urecno, &urec, RECTYPE_UID );
-    for( node=keyblock; node; node = node->next ) {
-       if( node->pkt->pkttype == PKT_USER_ID ) {
-           PKT_user_id *uidpkt = node->pkt->pkt.user_id;
-
-           if( uidpkt->photo ) {
-               gcry_md_hash_buffer( GCRY_MD_RMD160,  uhash,
-                                    uidpkt->photo, uidpkt->photolen );
-           }
-           else {
-               gcry_md_hash_buffer( GCRY_MD_RMD160,  uhash,
-                                    uidpkt->name, uidpkt->len );
-           }
-           if( !memcmp( uhash, urec.r.uid.namehash, 20 ) ) {
-               print_string( fp,  uidpkt->name, uidpkt->len, ':' );
-               return;
-           }
-       }
-    }
+  init_trustdb (ctrl, 0);
+  if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS)
+    return;
 
-    fputs("[?]", fp );
+  /* We simply set the time for the next check to 1 (far back in 1970)
+     so that a --update-trustdb will be scheduled.  */
+  if (tdbio_write_nextcheck (ctrl, 1))
+    do_sync ();
+  pending_check_trustdb = 1;
 }
 
-
-
-static void
-dump_tn_tree( FILE *fp, int level, TN tree )
+int
+trustdb_pending_check(void)
 {
-    TN kr, ur;
-
-    for( kr=tree; kr; kr = kr->next ) {
-       if( fp ) {
-           fprintf( fp, "%*s", level*4, "" );
-           fprintf( fp, "K%lu(ot=%d,val=%d)  ", kr->lid,
-                                            kr->n.k.ownertrust,
-                                            kr->n.k.validity  );
-       }
-       else {
-           tty_printf("%*s", level*4, "" );
-           tty_printf("K%lu(ot=%d,val=%d)  ", kr->lid,
-                                            kr->n.k.ownertrust,
-                                            kr->n.k.validity  );
-       }
-       print_default_uid( fp, kr->lid );
-       for( ur=kr->list; ur; ur = ur->next ) {
-           if( fp ) {
-               fprintf(fp, "%*s  ", level*4, "" );
-               fprintf(fp, "U%lu(mc=%d,fc=%d,val=%d)\n", ur->lid,
-                                                    ur->n.u.marginal_count,
-                                                    ur->n.u.fully_count,
-                                                    ur->n.u.validity
-                                               );
-           }
-           else {
-               tty_printf("%*s  ", level*4, "" );
-               tty_printf("U%lu(mc=%d,fc=%d,val=%d)\n", ur->lid,
-                                                    ur->n.u.marginal_count,
-                                                    ur->n.u.fully_count,
-                                                    ur->n.u.validity
-                                               );
-           }
-           dump_tn_tree( fp, level+1, ur->list );
-       }
-    }
+  return pending_check_trustdb;
 }
 
-/****************
- * Special version of dump_tn_tree, which prints it colon delimited.
- * Format:
- *   level:keyid:type:recno:ot:val:mc:cc:name:
- * With TYPE = U for a user ID
- *            K for a key
- * The RECNO is either the one of the dir record or the one of the uid record.
- * OT is the the usual trust letter and only availabel on K lines.
- * VAL is the calcualted validity
- * MC is the marginal trust counter and only available on U lines
- * CC is the same for the complete count
- * NAME ist the username and only printed on U lines
- */
-static void
-dump_tn_tree_with_colons( int level, TN tree )
+/* If the trustdb is dirty, and we're interactive, update it.
+   Otherwise, check it unless no-auto-check-trustdb is set. */
+void
+tdb_check_or_update (ctrl_t ctrl)
 {
-    TN kr, ur;
-
-    for( kr=tree; kr; kr = kr->next ) {
-       KBNODE kb = NULL;
-       u32 kid[2];
-
-       keyid_from_lid( kr->lid, kid );
-       get_keyblock_bylid( &kb, kr->lid );
-
-       printf( "%d:%08lX%08lX:K:%lu:%c:%c::::\n",
-                       level, (ulong)kid[0], (ulong)kid[1], kr->lid,
-                       trust_letter( kr->n.k.ownertrust ),
-                       trust_letter( kr->n.k.validity ) );
-       for( ur=kr->list; ur; ur = ur->next ) {
-           printf( "%d:%08lX%08lX:U:%lu::%c:%d:%d:",
-                       level, (ulong)kid[0], (ulong)kid[1], ur->lid,
-                       trust_letter( kr->n.u.validity ),
-                       ur->n.u.marginal_count,
-                       ur->n.u.fully_count );
-           print_uid_from_keyblock( stdout, kb, ur->lid );
-           putchar(':');
-           putchar('\n');
-           dump_tn_tree_with_colons( level+1, ur->list );
-       }
-       release_kbnode( kb );
+  if (trustdb_pending_check ())
+    {
+      if (opt.interactive)
+       update_trustdb (ctrl);
+      else if (!opt.no_auto_check_trustdb)
+       check_trustdb (ctrl);
     }
 }
 
+void
+read_trust_options (ctrl_t ctrl,
+                    byte *trust_model, ulong *created, ulong *nextcheck,
+                   byte *marginals, byte *completes, byte *cert_depth,
+                   byte *min_cert_level)
+{
+  TRUSTREC opts;
+
+  init_trustdb (ctrl, 0);
+  if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS)
+    memset (&opts, 0, sizeof opts);
+  else
+    read_record (0, &opts, RECTYPE_VER);
+
+  if(trust_model)
+    *trust_model=opts.r.ver.trust_model;
+  if(created)
+    *created=opts.r.ver.created;
+  if(nextcheck)
+    *nextcheck=opts.r.ver.nextcheck;
+  if(marginals)
+    *marginals=opts.r.ver.marginals;
+  if(completes)
+    *completes=opts.r.ver.completes;
+  if(cert_depth)
+    *cert_depth=opts.r.ver.cert_depth;
+  if(min_cert_level)
+    *min_cert_level=opts.r.ver.min_cert_level;
+}
 
-\f
 /***********************************************
- ************* trustdb maintenance  ***********
+ ***********  Ownertrust et al. ****************
  ***********************************************/
 
-/****************
- * Create or update shadow dir record and return the LID of the record
- */
-static ulong
-create_shadow_dir( PKT_signature *sig )
+static int
+read_trust_record (ctrl_t ctrl, PKT_public_key *pk, TRUSTREC *rec)
 {
-    TRUSTREC sdir;
-    int rc;
-
-    /* first see whether we already have such a record */
-    rc = tdbio_search_sdir( sig->keyid, sig->pubkey_algo, &sdir );
-    if( rc && rc != -1 ) {
-       log_error("tdbio_search_sdir failed: %s\n", gpg_errstr(rc));
-       tdbio_invalid();
+  int rc;
+
+  init_trustdb (ctrl, 0);
+  rc = tdbio_search_trust_bypk (pk, rec);
+  if (rc)
+    {
+      if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
+        log_error ("trustdb: searching trust record failed: %s\n",
+                   gpg_strerror (rc));
+      return rc;
     }
-    if( rc == -1 ) { /* not found: create */
-       memset( &sdir, 0, sizeof sdir );
-       sdir.recnum = tdbio_new_recnum();
-       sdir.rectype= RECTYPE_SDIR;
-       sdir.r.sdir.lid = sdir.recnum;
-       sdir.r.sdir.keyid[0] = sig->keyid[0];
-       sdir.r.sdir.keyid[1] = sig->keyid[1];
-       sdir.r.sdir.pubkey_algo = sig->pubkey_algo;
-       write_record( &sdir );
+
+  if (rec->rectype != RECTYPE_TRUST)
+    {
+      log_error ("trustdb: record %lu is not a trust record\n",
+                 rec->recnum);
+      return GPG_ERR_TRUSTDB;
     }
-    return sdir.recnum;
+
+  return 0;
 }
 
 
-static ulong
-find_or_create_lid( PKT_signature *sig )
+/*
+ * Return the assigned ownertrust value for the given public key.  The
+ * key should be the primary key.  If NO_CREATE is set a missing
+ * trustdb will not be created.  This comes for example handy when we
+ * want to print status lines (DECRYPTION_KEY) which carry ownertrust
+ * values but we usually use --always-trust.
+ */
+unsigned int
+tdb_get_ownertrust (ctrl_t ctrl, PKT_public_key *pk, int no_create)
 {
-    ulong lid;
-
-    lid = lid_from_keyid_no_sdir( sig->keyid );
-    if( !lid )
-       lid = create_shadow_dir( sig );
-    return lid;
-}
+  TRUSTREC rec;
+  gpg_error_t err;
 
+  if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS)
+    return TRUST_UNKNOWN;
 
+  /* If the caller asked not to create a trustdb we call init_trustdb
+   * directly and allow it to fail with an error code for a
+   * non-existing trustdb.  */
+  if (no_create && init_trustdb (ctrl, 1))
+    return TRUST_UNKNOWN;
 
-/****************
- * Check the validity of a key and calculate the keyflags
- * keynode points to
- * a node with a [sub]key.  mainkid has the key ID of the primary key
- * keyblock is the complete keyblock which is needed for signature
- * checking.  LID and PK is only used in verbose mode.
- */
-static unsigned int
-check_keybinding( KBNODE keyblock, KBNODE keynode, u32 *mainkid,
-                 ulong lid, PKT_public_key *pk )
-{
-    KBNODE node;
-    int keybind_seen = 0;
-    int revoke_seen = 0;
-    unsigned int keyflags=0;
-    int is_main = (keynode->pkt->pkttype == PKT_PUBLIC_KEY);
-    int rc;
-
-    if( DBG_TRUST )
-       log_debug("check_keybinding: %08lX.%lu\n",
-                           (ulong)mainkid[1], lid );
-
-    if( is_main ) {
-       /* a primary key is always valid (user IDs are handled elsewhere)*/
-       keyflags = KEYF_CHECKED | KEYF_VALID;
+  err = read_trust_record (ctrl, pk, &rec);
+  if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+    return TRUST_UNKNOWN; /* no record yet */
+  if (err)
+    {
+      tdbio_invalid ();
+      return TRUST_UNKNOWN; /* actually never reached */
     }
 
-    for( node=keynode->next; node; node = node->next ) {
-       PKT_signature *sig;
+  return rec.r.trust.ownertrust;
+}
 
-       if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
-           break; /* ready */
-       if( node->pkt->pkttype != PKT_SIGNATURE )
-           continue; /* don't care about other packets */
 
-       sig = node->pkt->pkt.signature;
+unsigned int
+tdb_get_min_ownertrust (ctrl_t ctrl, PKT_public_key *pk, int no_create)
+{
+  TRUSTREC rec;
+  gpg_error_t err;
 
-       if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] )
-           continue; /* we only care about self-signatures */
+  if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS)
+    return TRUST_UNKNOWN;
 
-       if( sig->sig_class == 0x18 && !keybind_seen && !is_main ) {
-           /* check until we find a valid keybinding */
-           rc = check_key_signature( keyblock, node, NULL );
-           if( !rc ) {
-               if( opt.verbose )
-                   log_info(_("key %08lX.%lu: Good subkey binding\n"),
-                                    (ulong)keyid_from_pk(pk,NULL), lid );
-               keyflags |= KEYF_CHECKED | KEYF_VALID;
-           }
-           else {
-               log_info(_(
-                 "key %08lX.%lu: Invalid subkey binding: %s\n"),
-                   (ulong)keyid_from_pk(pk,NULL), lid, gpg_errstr(rc) );
-               keyflags |= KEYF_CHECKED;
-               keyflags &= ~KEYF_VALID;
-           }
-           keybind_seen = 1;
-       }
-       else if( sig->sig_class == 0x20 && !revoke_seen ) {
-           /* this is a key revocation certificate: check it */
-           rc = check_key_signature( keyblock, node, NULL );
-           if( !rc ) {
-               if( opt.verbose )
-                   log_info(_("key %08lX.%lu: Valid key revocation\n"),
-                                (ulong)keyid_from_pk(pk, NULL), lid );
-               keyflags |= KEYF_REVOKED;
-           }
-           else {
-               log_info(_(
-                 "key %08lX.%lu: Invalid key revocation: %s\n"),
-                 (ulong)keyid_from_pk(pk,NULL), lid, gpg_errstr(rc) );
-           }
-           revoke_seen = 1;
-       }
-       else if( sig->sig_class == 0x28 && !revoke_seen && !is_main ) {
-           /* this is a subkey revocation certificate: check it */
-           rc = check_key_signature( keyblock, node, NULL );
-           if( !rc ) {
-               if( opt.verbose )
-                   log_info(_(
-                       "key %08lX.%lu: Valid subkey revocation\n"),
-                        (ulong)keyid_from_pk(pk,NULL), lid );
-               keyflags |= KEYF_REVOKED;
-           }
-           else {
-               log_info(_(
-                 "key %08lX.%lu: Invalid subkey binding: %s\n"),
-                 (ulong)keyid_from_pk(pk,NULL), lid, gpg_errstr(rc) );
-           }
-           revoke_seen = 1;
-       }
-       /* Hmmm: should we handle direct key signatures here? */
-    }
-
-    return keyflags;
-}
+  /* If the caller asked not to create a trustdb we call init_trustdb
+   * directly and allow it to fail with an error code for a
+   * non-existing trustdb.  */
+  if (no_create && init_trustdb (ctrl, 1))
+    return TRUST_UNKNOWN;
 
-
-static ulong
-make_key_records( KBNODE keyblock, ulong lid, u32 *keyid, int *mainrev )
-{
-    TRUSTREC *krecs, **kend, *k, *k2;
-    KBNODE  node;
-    PKT_public_key *pk;
-    byte fpr[MAX_FINGERPRINT_LEN];
-    size_t fprlen;
-    ulong keyrecno;
-
-    *mainrev = 0;
-    krecs = NULL; kend = &krecs;
-    for( node=keyblock; node; node = node->next ) {
-       if( node->pkt->pkttype != PKT_PUBLIC_KEY
-           && node->pkt->pkttype != PKT_PUBLIC_SUBKEY )
-           continue;
-       pk = node->pkt->pkt.public_key;
-       fingerprint_from_pk( pk, fpr, &fprlen );
-
-       /* create the key record */
-       k = gcry_xcalloc( 1, sizeof *k );
-       k->rectype = RECTYPE_KEY;
-       k->r.key.lid = lid;
-       k->r.key.pubkey_algo = pk->pubkey_algo;
-       k->r.key.fingerprint_len = fprlen;
-       memcpy(k->r.key.fingerprint, fpr, fprlen );
-       k->recnum = tdbio_new_recnum();
-       *kend = k;
-       kend = &k->next;
-
-       k->r.key.keyflags = check_keybinding( keyblock, node, keyid, lid, pk );
-       if( (k->r.key.keyflags & KEYF_REVOKED)
-           && node->pkt->pkttype == PKT_PUBLIC_KEY )
-           *mainrev = 1;
+  err = read_trust_record (ctrl, pk, &rec);
+  if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+    return TRUST_UNKNOWN; /* no record yet */
+  if (err)
+    {
+      tdbio_invalid ();
+      return TRUST_UNKNOWN; /* actually never reached */
     }
 
-    keyrecno = krecs? krecs->recnum : 0;
-    /* write the keylist and release the memory */
-    for( k = krecs; k ; k = k2 ) {
-       if( k->next )
-           k->r.key.next = k->next->recnum;
-       write_record( k );
-       k2 = k->next;
-       gcry_free( k );
-    }
-    return keyrecno;
+  return rec.r.trust.min_ownertrust;
 }
 
 
-/****************
- * Check the validity of a user ID and calculate the uidflags
- * keynode points to  a node with a user ID.
- * mainkid has the key ID of the primary key, keyblock is the complete
- * keyblock which is needed for signature checking.
- * Returns: The uid flags and the self-signature which is considered to
- * be the most current.
+/*
+ * Set the trust value of the given public key to the new value.
+ * The key should be a primary one.
  */
-static unsigned int
-check_uidsigs( KBNODE keyblock, KBNODE keynode, u32 *mainkid, ulong lid,
-                                                 PKT_signature **bestsig )
+void
+tdb_update_ownertrust (ctrl_t ctrl, PKT_public_key *pk, unsigned int new_trust )
 {
-    KBNODE node;
-    unsigned int uidflags = 0;
-    PKT_signature *sig;
-    PKT_signature *selfsig = NULL; /* the latest valid self signature */
-    int rc;
-
-    if( DBG_TRUST ) {
-       PKT_user_id *uid;
-       log_debug("check_uidsigs: %08lX.%lu \"",
-                           (ulong)mainkid[1], lid );
-       assert(keynode->pkt->pkttype == PKT_USER_ID );
-       uid = keynode->pkt->pkt.user_id;
-       print_string( log_stream(), uid->name, uid->len, '\"' );
-       fputs("\"\n", log_stream());
-    }
+  TRUSTREC rec;
+  gpg_error_t err;
 
-    /* first we check only the selfsignatures */
-    for( node=keynode->next; node; node = node->next ) {
-       if( node->pkt->pkttype == PKT_USER_ID
-           || node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
-           break; /* ready */
-       if( node->pkt->pkttype != PKT_SIGNATURE )
-           continue; /* don't care about other packets */
-       sig = node->pkt->pkt.signature;
-       if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] )
-           continue; /* we only care about self-signatures for now */
-
-       if( (sig->sig_class&~3) == 0x10 ) { /* regular self signature */
-           rc = check_key_signature( keyblock, node, NULL );
-           if( !rc ) {
-               if( opt.verbose )
-                   log_info( "uid %08lX.%lu: %s\n",
-                      (ulong)mainkid[1], lid, _("Good self-signature") );
-               uidflags |= UIDF_CHECKED | UIDF_VALID;
-               if( !selfsig )
-                   selfsig = sig; /* use the first valid sig */
-               else if( sig->timestamp > selfsig->timestamp
-                        && sig->sig_class >= selfsig->sig_class )
-                   selfsig = sig; /* but this one is newer */
-           }
-           else {
-               log_info( "uid %08lX: %s: %s\n",
-                          (ulong)mainkid[1], _("Invalid self-signature"),
-                          gpg_errstr(rc) );
-               uidflags |= UIDF_CHECKED;
-           }
-       }
-    }
+  if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS)
+    return;
 
-    /* and now check for revocations - we must do this after the
-     * self signature check because a self-signature which is newer
-     * than a revocation makes the revocation invalid.
-     * RFC2440 is quiet about tis but I feel this is reasonable for
-     * non-primary-key revocations. */
-    for( node=keynode->next; node; node = node->next ) {
-       if( node->pkt->pkttype == PKT_USER_ID
-           || node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
-           break; /* ready */
-       if( node->pkt->pkttype != PKT_SIGNATURE )
-           continue; /* don't care about other packets */
-       sig = node->pkt->pkt.signature;
-       if( mainkid[0] != sig->keyid[0] || mainkid[1] != sig->keyid[1] )
-           continue; /* we only care about self-signatures for now */
-
-       if( sig->sig_class == 0x30 ) { /* cert revocation */
-           rc = check_key_signature( keyblock, node, NULL );
-           if( !rc && selfsig && selfsig->timestamp > sig->timestamp ) {
-               log_info( "uid %08lX.%lu: %s\n",
-                      (ulong)mainkid[1], lid,
-                      _("Valid user ID revocation skipped "
-                        "due to a newer self signature") );
-           }
-           else if( !rc ) {
-               if( opt.verbose )
-                   log_info( "uid %08lX.%lu: %s\n",
-                      (ulong)mainkid[1], lid, _("Valid user ID revocation") );
-               uidflags |= UIDF_CHECKED | UIDF_VALID | UIDF_REVOKED;
-           }
-           else {
-               log_info("uid %08lX: %s: %s\n",
-                           (ulong)mainkid[1], _("Invalid user ID revocation"),
-                                                   gpg_errstr(rc) );
-           }
-       }
+  err = read_trust_record (ctrl, pk, &rec);
+  if (!err)
+    {
+      if (DBG_TRUST)
+        log_debug ("update ownertrust from %u to %u\n",
+                   (unsigned int)rec.r.trust.ownertrust, new_trust );
+      if (rec.r.trust.ownertrust != new_trust)
+        {
+          rec.r.trust.ownertrust = new_trust;
+          write_record (ctrl, &rec);
+          tdb_revalidation_mark (ctrl);
+          do_sync ();
+        }
     }
+  else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+    { /* no record yet - create a new one */
+      size_t dummy;
 
-    *bestsig = selfsig;
-    return uidflags;
-}
-
+      if (DBG_TRUST)
+        log_debug ("insert ownertrust %u\n", new_trust );
 
-static unsigned int
-check_sig_record( KBNODE keyblock, KBNODE signode,
-                 ulong siglid, int sigidx, u32 *keyid, ulong lid,
-                 u32 *r_expiretime, int *mod_down, int *mod_up )
-{
-    PKT_signature *sig = signode->pkt->pkt.signature;
-    unsigned int sigflag = 0;
-    TRUSTREC tmp;
-    int revocation=0, expired=0, rc;
-
-    if( DBG_TRUST )
-       log_debug("check_sig_record: %08lX.%lu %lu[%d]\n",
-                           (ulong)keyid[1], lid, siglid, sigidx );
-    *r_expiretime = 0;
-    if( (sig->sig_class&~3) == 0x10 ) /* regular certification */
-       ;
-    else if( sig->sig_class == 0x30 ) /* cert revocation */
-       revocation = 1;
-    else
-       return SIGF_CHECKED | SIGF_IGNORED;
-
-    read_record( siglid, &tmp, 0 );
-    if( tmp.rectype == RECTYPE_DIR ) {
-       /* the public key is in the trustdb: check sig */
-       rc = check_key_signature2( keyblock, signode, NULL,
-                                            r_expiretime, &expired );
-       if( !rc ) { /* valid signature */
-           if( opt.verbose )
-               log_info("sig %08lX.%lu/%lu[%d]/%08lX: %s\n",
-                       (ulong)keyid[1], lid, siglid, sigidx,
-                                               (ulong)sig->keyid[1],
-                       revocation? _("Valid certificate revocation")
-                                 : _("Good certificate") );
-           sigflag |= SIGF_CHECKED | SIGF_VALID;
-           if( expired ) {
-               sigflag |= SIGF_EXPIRED;
-               /* We have to reset the expiretime, so that this signature
-                * does not get checked over and over due to the reached
-                * expiretime */
-               *r_expiretime = 0;
-           }
-           if( revocation ) {
-               sigflag |= SIGF_REVOKED;
-               *mod_down = 1;
-           }
-           else
-               *mod_up = 1;
-       }
-       else if( rc == GPGERR_NO_PUBKEY ) {
-           /* This may happen if the key is still in the trustdb
-            * but not available in the keystorage */
-           sigflag |= SIGF_NOPUBKEY;
-           *mod_down = 1;
-           if( revocation )
-               sigflag |= SIGF_REVOKED;
-       }
-       else {
-           log_info("sig %08lX.%lu/%lu[%d]/%08lX: %s: %s\n",
-                       (ulong)keyid[1], lid, siglid, sigidx,
-                                               (ulong)sig->keyid[1],
-                       revocation? _("Invalid certificate revocation")
-                                  : _("Invalid certificate"),
-                                           gpg_errstr(rc));
-           sigflag |= SIGF_CHECKED;
-           if( revocation ) {
-               sigflag |= SIGF_REVOKED;
-               *mod_down = 1;
-           }
-       }
-    }
-    else if( tmp.rectype == RECTYPE_SDIR ) {
-       /* better check that it is the right one */
-       if(    tmp.r.sdir.keyid[0] == sig->keyid[0]
-           && tmp.r.sdir.keyid[1] == sig->keyid[1]
-           && (!tmp.r.sdir.pubkey_algo
-                || tmp.r.sdir.pubkey_algo == sig->pubkey_algo ))
-           sigflag |= SIGF_NOPUBKEY;
-       else
-           log_error(_("sig record %lu[%d] points to wrong record.\n"),
-                        siglid, sigidx );
+      memset (&rec, 0, sizeof rec);
+      rec.recnum = tdbio_new_recnum (ctrl);
+      rec.rectype = RECTYPE_TRUST;
+      fingerprint_from_pk (pk, rec.r.trust.fingerprint, &dummy);
+      rec.r.trust.ownertrust = new_trust;
+      write_record (ctrl, &rec);
+      tdb_revalidation_mark (ctrl);
+      do_sync ();
     }
-    else {
-       log_error(_("sig record %lu[%d] points to wrong record.\n"),
-                   siglid, sigidx );
-       tdbio_invalid();
+  else
+    {
+      tdbio_invalid ();
     }
-
-    return sigflag;
 }
 
-/****************
- * Make the sig records for the given uid record
- * We don't set flags here or even check the signatures; this will
- * happen latter.
- */
-static ulong
-make_sig_records( KBNODE keyblock, KBNODE uidnode,
-                 ulong lid, u32 *mainkid, u32 *min_expire,
-                                       int *mod_down, int *mod_up  )
+static void
+update_min_ownertrust (ctrl_t ctrl, u32 *kid, unsigned int new_trust)
 {
-    TRUSTREC *srecs, **s_end, *s=NULL, *s2;
-    KBNODE  node;
-    PKT_signature *sig;
-    ulong sigrecno, siglid;
-    int i, sigidx = 0;
-    u32 expiretime;
-
-    srecs = NULL; s_end = &srecs;
-    for( node=uidnode->next; node; node = node->next ) {
-       if( node->pkt->pkttype == PKT_USER_ID
-           || node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
-           break; /* ready */
-       if( node->pkt->pkttype != PKT_SIGNATURE )
-           continue; /* don't care about other packets */
-       sig = node->pkt->pkt.signature;
-       if( mainkid[0] == sig->keyid[0] && mainkid[1] == sig->keyid[1] )
-           continue; /* we don't care about self-signatures here */
-
-       siglid = find_or_create_lid( sig );
-       /* smash dups */
-       /* FIXME: Here we have a problem:
-        *  We can't distinguish between a certification and a certification
-        *  revocation without looking at class of the signature - we have
-        *  to see how we can store the sigclass in the sigrecord..
-        *  Argg- I hope I can get rid of this ugly trustdb ASAP.
-        */
-       for( s2 = s; s2 ; s2 = s2->next ) {
-           for(i=0; i < sigidx; i++ ) {
-               if( s2->r.sig.sig[i].lid == siglid )
-                   goto leaveduptest;
-           }
-       }
-       for( s2 = srecs; s2 ; s2 = s2->next ) {
-           for(i=0; i < SIGS_PER_RECORD; i++ ) {
-               if( s2->r.sig.sig[i].lid == siglid )
-                   goto leaveduptest;
-           }
-       }
-      leaveduptest:
-       if( s2 ) {
-           log_info( "sig %08lX.%lu: %s\n", (ulong)mainkid[1], lid,
-                                   _("duplicated certificate - deleted") );
-           continue;
-       }
+  PKT_public_key *pk;
+  TRUSTREC rec;
+  gpg_error_t err;
 
-       /* create the sig record */
-       if( !sigidx ) {
-           s = gcry_xcalloc( 1, sizeof *s );
-           s->rectype = RECTYPE_SIG;
-           s->r.sig.lid = lid;
-       }
-       s->r.sig.sig[sigidx].lid = siglid;
-       s->r.sig.sig[sigidx].flag= check_sig_record( keyblock, node,
-                                                    siglid, sigidx,
-                                                    mainkid, lid, &expiretime,
-                                                    mod_down, mod_up );
-
-       sigidx++;
-       if( sigidx == SIGS_PER_RECORD ) {
-           s->recnum = tdbio_new_recnum();
-           *s_end = s;
-           s_end = &s->next;
-           sigidx = 0;
-       }
-       /* keep track of signers pk expire time */
-       if( expiretime && (!*min_expire || *min_expire > expiretime ) )
-           *min_expire = expiretime;
-    }
-    if( sigidx ) {
-       s->recnum = tdbio_new_recnum();
-       *s_end = s;
-       s_end = &s->next;
-    }
+  if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS)
+    return;
 
-    sigrecno = srecs? srecs->recnum : 0;
-    /* write the keylist and release the memory */
-    for( s = srecs; s ; s = s2 ) {
-       if( s->next )
-           s->r.sig.next = s->next->recnum;
-       write_record( s );
-       s2 = s->next;
-       gcry_free( s );
+  pk = xmalloc_clear (sizeof *pk);
+  err = get_pubkey (ctrl, pk, kid);
+  if (err)
+    {
+      log_error (_("public key %s not found: %s\n"),
+                 keystr (kid), gpg_strerror (err));
+      xfree (pk);
+      return;
+    }
+
+  err = read_trust_record (ctrl, pk, &rec);
+  if (!err)
+    {
+      if (DBG_TRUST)
+        log_debug ("key %08lX%08lX: update min_ownertrust from %u to %u\n",
+                   (ulong)kid[0],(ulong)kid[1],
+                  (unsigned int)rec.r.trust.min_ownertrust,
+                  new_trust );
+      if (rec.r.trust.min_ownertrust != new_trust)
+        {
+          rec.r.trust.min_ownertrust = new_trust;
+          write_record (ctrl, &rec);
+          tdb_revalidation_mark (ctrl);
+          do_sync ();
+        }
     }
-    return sigrecno;
-}
-
+  else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+    { /* no record yet - create a new one */
+      size_t dummy;
 
+      if (DBG_TRUST)
+        log_debug ("insert min_ownertrust %u\n", new_trust );
 
-/****************
- * Make a preference record (or a list of them) according to the supplied
- * signature.
- * Returns: The record number of the first pref record.
- */
-static ulong
-make_pref_record( PKT_signature *sig, ulong lid )
-{
-    static struct {
-       sigsubpkttype_t subpkttype;
-       int preftype;
-    } ptable[] = {
-       { SIGSUBPKT_PREF_SYM,   PREFTYPE_SYM    },
-       { SIGSUBPKT_PREF_HASH,  PREFTYPE_HASH   },
-       { SIGSUBPKT_PREF_COMPR, PREFTYPE_COMPR  },
-       { 0, 0 }
-    };
-    TRUSTREC *precs, **p_end, *p=NULL, *p2;
-    ulong precno;
-    int k, idx=0;
-    const byte *s;
-    size_t n;
-
-  #if (ITEMS_PER_PREF_RECORD % 2) != 0
-    #error ITEMS_PER_PREF_RECORD must have an even value
-  #endif
-
-    precs = NULL; p_end = &precs;
-    for(k=0; ptable[k].subpkttype; k++ ) {
-       s = parse_sig_subpkt2( sig, ptable[k].subpkttype, &n );
-       if( !s )
-           continue;
-       for( ; n; n--, s++ ) {
-           if( !idx ) {
-               p = gcry_xcalloc( 1, sizeof *p );
-               p->rectype = RECTYPE_PREF;
-               p->r.pref.lid = lid;
-           }
-           p->r.pref.data[idx++] = ptable[k].preftype;
-           p->r.pref.data[idx++] = *s;
-           if( idx >= ITEMS_PER_PREF_RECORD ) {
-               p->recnum = tdbio_new_recnum();
-               *p_end = p;
-               p_end = &p->next;
-               idx = 0;
-           }
-       }
+      memset (&rec, 0, sizeof rec);
+      rec.recnum = tdbio_new_recnum (ctrl);
+      rec.rectype = RECTYPE_TRUST;
+      fingerprint_from_pk (pk, rec.r.trust.fingerprint, &dummy);
+      rec.r.trust.min_ownertrust = new_trust;
+      write_record (ctrl, &rec);
+      tdb_revalidation_mark (ctrl);
+      do_sync ();
     }
-    if( idx ) {
-       p->recnum = tdbio_new_recnum();
-       *p_end = p;
-       p_end = &p->next;
+  else
+    {
+      tdbio_invalid ();
     }
 
-    precno = precs? precs->recnum : 0;
-    /* write the precs and release the memory */
-    for( p = precs; p ; p = p2 ) {
-       if( p->next )
-           p->r.pref.next = p->next->recnum;
-       write_record( p );
-       p2 = p->next;
-       gcry_free( p );
-    }
-    return precno;
+  free_public_key (pk);
 }
 
 
-static ulong
-make_uid_records( KBNODE keyblock, ulong lid, u32 *keyid, u32 *min_expire,
-                                                int *mod_down, int *mod_up )
-{
-    TRUSTREC *urecs, **uend, *u, *u2;
-    KBNODE  node;
-    PKT_user_id *uid;
-    byte uidhash[20];
-    ulong uidrecno;
-
-    urecs = NULL; uend = &urecs;
-    for( node=keyblock; node; node = node->next ) {
-       PKT_signature *bestsig;
-
-       if( node->pkt->pkttype != PKT_USER_ID )
-           continue;
-       uid = node->pkt->pkt.user_id;
-       if( uid->photo ) {
-           gcry_md_hash_buffer( GCRY_MD_RMD160,  uidhash,
-                                uid->photo, uid->photolen );
-       }
-       else {
-           gcry_md_hash_buffer( GCRY_MD_RMD160,  uidhash,
-                                uid->name, uid->len );
-       }
-
-       /* create the uid record */
-       u = gcry_xcalloc( 1, sizeof *u );
-       u->rectype = RECTYPE_UID;
-       u->r.uid.lid = lid;
-       memcpy(u->r.uid.namehash, uidhash, 20 );
-       u->recnum = tdbio_new_recnum();
-       *uend = u;
-       uend = &u->next;
-
-       u->r.uid.uidflags = check_uidsigs( keyblock, node, keyid,
-                                                    lid, &bestsig );
-       if( (u->r.uid.uidflags & UIDF_CHECKED)
-           && (u->r.uid.uidflags & UIDF_VALID) ) {
-           u->r.uid.prefrec = bestsig? make_pref_record( bestsig, lid ) : 0;
-       }
-
-       /* the next test is really bad because we should modify
-        * out modification timestamps only if we really have a change.
-        * But because we are deleting the uid records first it is somewhat
-        * difficult to track those changes.  fixme */
-       if(   !( u->r.uid.uidflags & UIDF_VALID )
-           || ( u->r.uid.uidflags & UIDF_REVOKED ) )
-           *mod_down=1;
-       else
-           *mod_up=1;
-
-       /* create the list of signatures */
-       u->r.uid.siglist = make_sig_records( keyblock, node,
-                                            lid, keyid, min_expire,
-                                            mod_down, mod_up );
-    }
-
-    uidrecno = urecs? urecs->recnum : 0;
-    /* write the uidlist and release the memory */
-    for( u = urecs; u ; u = u2 ) {
-       if( u->next )
-           u->r.uid.next = u->next->recnum;
-       write_record( u );
-       u2 = u->next;
-       gcry_free( u );
-    }
-    return uidrecno;
-}
-
-
-
-/****************
- * Update all the info from the public keyblock.
- * The key must already exist in the keydb.
+/*
+ * Clear the ownertrust and min_ownertrust values.
+ *
+ * Return: True if a change actually happened.
  */
 int
-update_trust_record( KBNODE keyblock, int recheck, int *modified )
+tdb_clear_ownertrusts (ctrl_t ctrl, PKT_public_key *pk)
 {
-    TRUSTREC drec;
-    int rc;
+  TRUSTREC rec;
+  gpg_error_t err;
 
-    /* NOTE: We don't need recheck anymore, but this might chnage again in
-     * the future */
-    if( opt.dry_run )
-       return 0;
-    if( modified )
-       *modified = 0;
-    init_trustdb();
-    rc = get_dir_record( find_kbnode( keyblock, PKT_PUBLIC_KEY )
-                                           ->pkt->pkt.public_key, &drec );
-    if( rc )
-       return rc;
-
-    rc = do_update_trust_record( keyblock, &drec, 0, modified );
-    return rc;
-}
+  init_trustdb (ctrl, 0);
 
-/****************
- * Same as update_trust_record, but this functions expects the dir record.
- * On exit the dir record will reflect any changes made.
- * With sigs_only set only foreign key signatures are checked.
- */
-static int
-do_update_trust_record( KBNODE keyblock, TRUSTREC *drec,
-                       int sigs_only, int *modified )
-{
-    PKT_public_key *primary_pk;
-    TRUSTREC krec, urec, prec, helprec;
-    int i, rc = 0;
-    u32 keyid[2]; /* keyid of primary key */
-    int mod_up = 0;
-    int mod_down = 0;
-    ulong recno, r2;
-    u32 expiretime;
-
-    primary_pk = find_kbnode( keyblock, PKT_PUBLIC_KEY )->pkt->pkt.public_key;
-    if( !primary_pk->local_id )
-       primary_pk->local_id = drec->recnum;
-
-    keyid_from_pk( primary_pk, keyid );
-    if( DBG_TRUST )
-       log_debug("do_update_trust_record: %08lX.%lu\n",
-                                       (ulong)keyid[1], drec->recnum );
-
-    rc = tdbio_begin_transaction();
-    if( rc )
-       return rc;
-
-    /* delete the old stuff FIXME: implementend sigs_only */
-    for( recno=drec->r.dir.keylist; recno; recno = krec.r.key.next ) {
-       read_record( recno, &krec, RECTYPE_KEY );
-       delete_record( recno );
-    }
-    drec->r.dir.keylist = 0;
-    for( recno=drec->r.dir.uidlist; recno; recno = urec.r.uid.next ) {
-       read_record( recno, &urec, RECTYPE_UID );
-       for(r2=urec.r.uid.prefrec ; r2; r2 = prec.r.pref.next ) {
-           read_record( r2, &prec, RECTYPE_PREF );
-           delete_record( r2 );
-       }
-       for(r2=urec.r.uid.siglist ; r2; r2 = helprec.r.sig.next ) {
-           read_record( r2, &helprec, RECTYPE_SIG );
-           delete_record( r2 );
+  if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS)
+    return 0;
+
+  err = read_trust_record (ctrl, pk, &rec);
+  if (!err)
+    {
+      if (DBG_TRUST)
+       {
+         log_debug ("clearing ownertrust (old value %u)\n",
+                    (unsigned int)rec.r.trust.ownertrust);
+         log_debug ("clearing min_ownertrust (old value %u)\n",
+                    (unsigned int)rec.r.trust.min_ownertrust);
        }
-       delete_record( recno );
+      if (rec.r.trust.ownertrust || rec.r.trust.min_ownertrust)
+        {
+          rec.r.trust.ownertrust = 0;
+          rec.r.trust.min_ownertrust = 0;
+          write_record (ctrl, &rec);
+          tdb_revalidation_mark (ctrl);
+          do_sync ();
+          return 1;
+        }
     }
-    drec->r.dir.uidlist = 0;
-
-
-    /* insert new stuff */
-    drec->r.dir.dirflags &= ~DIRF_REVOKED;
-    drec->r.dir.dirflags &= ~DIRF_NEWKEYS;
-    drec->r.dir.keylist = make_key_records( keyblock, drec->recnum, keyid, &i );
-    if( i ) /* primary key has been revoked */
-       drec->r.dir.dirflags |= DIRF_REVOKED;
-    expiretime = 0;
-    drec->r.dir.uidlist = make_uid_records( keyblock, drec->recnum, keyid,
-                                           &expiretime, &mod_down, &mod_up );
-    if( rc )
-       rc = tdbio_cancel_transaction();
-    else {
-       if( modified && tdbio_is_dirty() )
-           *modified = 1;
-       drec->r.dir.dirflags |= DIRF_CHECKED;
-       drec->r.dir.valcheck = 0;
-       drec->r.dir.checkat = expiretime;
-       write_record( drec );
-       tdbio_write_modify_stamp( mod_up, mod_down );
-       rc = tdbio_end_transaction();
+  else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND)
+    {
+      tdbio_invalid ();
     }
-    return rc;
+  return 0;
 }
 
+/*
+ * Note: Caller has to do a sync
+ */
+static void
+update_validity (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid,
+                 int depth, int validity)
+{
+  TRUSTREC trec, vrec;
+  gpg_error_t err;
+  ulong recno;
+
+  namehash_from_uid(uid);
+
+  err = read_trust_record (ctrl, pk, &trec);
+  if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND)
+    {
+      tdbio_invalid ();
+      return;
+    }
+  if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+    {
+      /* No record yet - create a new one. */
+      size_t dummy;
+
+      memset (&trec, 0, sizeof trec);
+      trec.recnum = tdbio_new_recnum (ctrl);
+      trec.rectype = RECTYPE_TRUST;
+      fingerprint_from_pk (pk, trec.r.trust.fingerprint, &dummy);
+      trec.r.trust.ownertrust = 0;
+      }
+
+  /* locate an existing one */
+  recno = trec.r.trust.validlist;
+  while (recno)
+    {
+      read_record (recno, &vrec, RECTYPE_VALID);
+      if ( !memcmp (vrec.r.valid.namehash, uid->namehash, 20) )
+        break;
+      recno = vrec.r.valid.next;
+    }
+
+  if (!recno) /* insert a new validity record */
+    {
+      memset (&vrec, 0, sizeof vrec);
+      vrec.recnum = tdbio_new_recnum (ctrl);
+      vrec.rectype = RECTYPE_VALID;
+      memcpy (vrec.r.valid.namehash, uid->namehash, 20);
+      vrec.r.valid.next = trec.r.trust.validlist;
+      trec.r.trust.validlist = vrec.recnum;
+    }
+  vrec.r.valid.validity = validity;
+  vrec.r.valid.full_count = uid->help_full_count;
+  vrec.r.valid.marginal_count = uid->help_marginal_count;
+  write_record (ctrl, &vrec);
+  trec.r.trust.depth = depth;
+  write_record (ctrl, &trec);
+}
 
 
-/****************
- * Insert a trust record into the TrustDB
- * This function assumes that the record does not yet exist.
- */
+/***********************************************
+ *********  Query trustdb values  **************
+ ***********************************************/
+
+/* Return true if key is disabled.  Note that this is usually used via
+   the pk_is_disabled macro.  */
 int
-insert_trust_record( KBNODE keyblock )
+tdb_cache_disabled_value (ctrl_t ctrl, PKT_public_key *pk)
 {
-    TRUSTREC dirrec;
-    TRUSTREC shadow;
-    KBNODE node;
-    int rc = 0;
-    PKT_public_key *pk;
+  gpg_error_t err;
+  TRUSTREC trec;
+  int disabled = 0;
 
+  if (pk->flags.disabled_valid)
+    return pk->flags.disabled;
 
-    if( opt.dry_run )
-       return 0;
+  init_trustdb (ctrl, 0);
 
-    init_trustdb();
+  if (trustdb_args.no_trustdb)
+    return 0;  /* No trustdb => not disabled.  */
 
-    pk = find_kbnode( keyblock, PKT_PUBLIC_KEY )->pkt->pkt.public_key;
-    if( pk->local_id ) {
-       log_debug("insert_trust_record with pk->local_id=%lu (2)\n",
-                                                       pk->local_id );
-       rc = update_trust_record( keyblock, 1, NULL );
-       return rc;
+  err = read_trust_record (ctrl, pk, &trec);
+  if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND)
+    {
+      tdbio_invalid ();
+      goto leave;
     }
-
-    /* We have to look for a shadow dir record which must be reused
-     * as the dir record. */
-    rc = tdbio_search_sdir( pk->keyid, pk->pubkey_algo, &shadow );
-    if( rc && rc != -1 ) {
-       log_error(_("tdbio_search_dir failed: %s\n"), gpg_errstr(rc));
-       tdbio_invalid();
-    }
-    memset( &dirrec, 0, sizeof dirrec );
-    dirrec.rectype = RECTYPE_DIR;
-    if( !rc ) /* we have a shadow dir record - convert to dir record */
-       dirrec.recnum = shadow.recnum;
-    else
-       dirrec.recnum = tdbio_new_recnum();
-    dirrec.r.dir.lid = dirrec.recnum;
-    write_record( &dirrec );
-
-    /* put the LID into the keyblock */
-    pk->local_id = dirrec.r.dir.lid;
-    for( node=keyblock; node; node = node->next ) {
-       if( node->pkt->pkttype == PKT_PUBLIC_KEY
-           || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
-           PKT_public_key *a_pk = node->pkt->pkt.public_key;
-           a_pk->local_id = dirrec.r.dir.lid;
-       }
-       else if( node->pkt->pkttype == PKT_SIGNATURE ) {
-           PKT_signature *a_sig = node->pkt->pkt.signature;
-           a_sig->local_id = dirrec.r.dir.lid;
-       }
+  if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+    {
+      /* No record found, so assume not disabled.  */
+      goto leave;
     }
 
+  if ((trec.r.trust.ownertrust & TRUST_FLAG_DISABLED))
+    disabled = 1;
 
-    /* mark tdb as modified upwards */
-    tdbio_write_modify_stamp( 1, 0 );
+  /* Cache it for later so we don't need to look at the trustdb every
+     time */
+  pk->flags.disabled = disabled;
+  pk->flags.disabled_valid = 1;
 
-    /* and put all the other stuff into the keydb */
-    rc = do_update_trust_record( keyblock, &dirrec, 0, NULL );
-
-    do_sync();
-
-    /* keep track of new keys */
-    if( !fresh_imported_keys )
-       fresh_imported_keys = new_lid_table();
-    ins_lid_table_item( fresh_imported_keys, pk->local_id, 0 );
-    if( ++fresh_imported_keys_count > FRESH_KEY_CHECK_THRESHOLD )
-       mark_fresh_keys();
-
-    return rc;
+ leave:
+  return disabled;
 }
 
 
-
-
-/****************
- * Insert a trust record indentified by a PK into the TrustDB
- */
-int
-insert_trust_record_by_pk( PKT_public_key *pk )
-{
-    KBNODE keyblock = NULL;
-    byte fingerprint[MAX_FINGERPRINT_LEN];
-    size_t fingerlen;
-    int rc;
-
-    /* get the keyblock */
-    fingerprint_from_pk( pk, fingerprint, &fingerlen );
-    rc = get_keyblock_byfprint( &keyblock, fingerprint, fingerlen );
-    if( rc ) { /* that should never happen */
-       log_debug( "insert_trust_record_by_pk: keyblock not found: %s\n",
-                                                         gpg_errstr(rc) );
-    }
-    else {
-       rc = insert_trust_record( keyblock );
-       if( !rc ) /* copy the LID into the PK */
-           pk->local_id = find_kbnode( keyblock, PKT_PUBLIC_KEY )
-                                           ->pkt->pkt.public_key->local_id;
+void
+tdb_check_trustdb_stale (ctrl_t ctrl)
+{
+  static int did_nextcheck=0;
+
+  init_trustdb (ctrl, 0);
+
+  if (trustdb_args.no_trustdb)
+    return;  /* No trustdb => can't be stale.  */
+
+  if (!did_nextcheck
+      && (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC
+          || opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_TOFU))
+    {
+      ulong scheduled;
+
+      did_nextcheck = 1;
+      scheduled = tdbio_read_nextcheck ();
+      if ((scheduled && scheduled <= make_timestamp ())
+         || pending_check_trustdb)
+        {
+          if (opt.no_auto_check_trustdb)
+            {
+              pending_check_trustdb = 1;
+              if (!opt.quiet)
+                log_info (_("please do a --check-trustdb\n"));
+            }
+          else
+            {
+              if (!opt.quiet)
+                log_info (_("checking the trustdb\n"));
+              validate_keys (ctrl, 0);
+            }
+        }
     }
-
-    release_kbnode( keyblock );
-    return rc;
 }
 
-
-/****************
- * Check one trust record.  This function is called for every
- * directory record which is to be checked.  The supplied
- * dir record is modified according to the performed actions.
- * Currently we only do an update_trust_record.
+/*
+ * Return the validity information for KB/PK (at least one of them
+ * must be non-NULL).  This is the core of get_validity.  If SIG is
+ * not NULL, then the trust is being evaluated in the context of the
+ * provided signature.  This is used by the TOFU code to record
+ * statistics.
  */
-static int
-check_trust_record( TRUSTREC *drec, int sigs_only )
-{
-    KBNODE keyblock;
-    int modified, rc;
-
-    rc = get_keyblock_bylid( &keyblock, drec->recnum );
-    if( rc ) {
-       log_debug( "check_trust_record %lu: keyblock not found: %s\n",
-                                             drec->recnum, gpg_errstr(rc) );
-       return rc;
+unsigned int
+tdb_get_validity_core (ctrl_t ctrl,
+                       kbnode_t kb,
+                       PKT_public_key *pk, PKT_user_id *uid,
+                       PKT_public_key *main_pk,
+                      PKT_signature *sig,
+                      int may_ask)
+{
+  TRUSTREC trec, vrec;
+  gpg_error_t err = 0;
+  ulong recno;
+#ifdef USE_TOFU
+  unsigned int tofu_validity = TRUST_UNKNOWN;
+  int free_kb = 0;
+#endif
+  unsigned int validity = TRUST_UNKNOWN;
+
+  if (kb && pk)
+    log_assert (keyid_cmp (pk_main_keyid (pk),
+                           pk_main_keyid (kb->pkt->pkt.public_key)) == 0);
+
+  if (! pk)
+    {
+      log_assert (kb);
+      pk = kb->pkt->pkt.public_key;
     }
 
-    rc = do_update_trust_record( keyblock, drec, sigs_only, &modified );
-    release_kbnode( keyblock );
+#ifndef USE_TOFU
+  (void)sig;
+  (void)may_ask;
+#endif
 
-    return rc;
-}
+  init_trustdb (ctrl, 0);
+
+  /* If we have no trustdb (which also means it has not been created)
+     and the trust-model is always, we don't know the validity -
+     return immediately.  If we won't do that the tdbio code would try
+     to open the trustdb and run into a fatal error.  */
+  if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS)
+    return TRUST_UNKNOWN;
+
+  check_trustdb_stale (ctrl);
+
+  if(opt.trust_model==TM_DIRECT)
+    {
+      /* Note that this happens BEFORE any user ID stuff is checked.
+        The direct trust model applies to keys as a whole. */
+      validity = tdb_get_ownertrust (ctrl, main_pk, 0);
+      goto leave;
+    }
+
+#ifdef USE_TOFU
+  if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)
+    {
+      kbnode_t n = NULL;
+      strlist_t user_id_list = NULL;
+      int done = 0;
+
+      /* If the caller didn't supply a user id then use all uids.  */
+      if (! uid)
+        {
+          if (! kb)
+            {
+              kb = get_pubkeyblock (ctrl, main_pk->keyid);
+              free_kb = 1;
+            }
+          n = kb;
+        }
 
+      if (DBG_TRUST && sig && sig->signers_uid)
+        log_debug ("TOFU: only considering user id: '%s'\n",
+                   sig->signers_uid);
+
+      while (!done && (uid || (n = find_next_kbnode (n, PKT_USER_ID))))
+       {
+         PKT_user_id *user_id;
+          int expired = 0;
+
+         if (uid)
+            {
+              user_id = uid;
+              /* If the caller specified a user id, then we only
+                 process the specified user id and are done after the
+                 first iteration.  */
+              done = 1;
+            }
+         else
+           user_id = n->pkt->pkt.user_id;
+
+          if (user_id->attrib_data)
+            /* Skip user attributes.  */
+            continue;
+
+          if (sig && sig->signers_uid)
+            /* Make sure the UID matches.  */
+            {
+              char *email = mailbox_from_userid (user_id->name);
+              if (!email || !*email || strcmp (sig->signers_uid, email) != 0)
+                {
+                  if (DBG_TRUST)
+                    log_debug ("TOFU: skipping user id '%s', which does"
+                               " not match the signer's email ('%s')\n",
+                               email, sig->signers_uid);
+                  xfree (email);
+                  continue;
+                }
+              xfree (email);
+            }
+
+          /* If the user id is revoked or expired, then skip it.  */
+          if (user_id->flags.revoked || user_id->flags.expired)
+            {
+              if (DBG_TRUST)
+                {
+                  char *s;
+                  if (user_id->flags.revoked && user_id->flags.expired)
+                    s = "revoked and expired";
+                  else if (user_id->flags.revoked)
+                    s = "revoked";
+                  else
+                    s = "expire";
+
+                  log_debug ("TOFU: Ignoring %s user id (%s)\n",
+                             s, user_id->name);
+                }
+
+              if (user_id->flags.revoked)
+                continue;
+
+              expired = 1;
+            }
+
+          add_to_strlist (&user_id_list, user_id->name);
+          user_id_list->flags = expired;
+        }
 
-/****************
- * Walk over the keyrings and create trustdb records for all keys
- * which are not currently in the trustdb.
- * It is intended to be used after a fast-import operation.
- */
-void
-update_trustdb()
-{
-    KBNODE keyblock = NULL;
-    KBPOS kbpos;
-    int rc;
-
-    if( opt.dry_run )
-       return;
-
-    init_trustdb();
-    rc = enum_keyblocks_begin( &kbpos, 0 );
-    if( !rc ) {
-       ulong count=0, err_count=0, new_count=0;
-
-       while( !(rc = enum_keyblocks_next( kbpos, 1, &keyblock )) ) {
-           /*int modified;*/
-           TRUSTREC drec;
-           PKT_public_key *pk = find_kbnode( keyblock, PKT_PUBLIC_KEY )
-                                       ->pkt->pkt.public_key;
-
-           rc = get_dir_record( pk, &drec );
-           if( rc == -1 ) { /* not in trustdb: insert */
-               rc = insert_trust_record( keyblock );
-               if( rc && !pk->local_id ) {
-                   log_error(_("lid ?: insert failed: %s\n"),
-                                                    gpg_errstr(rc) );
-                   err_count++;
-               }
-               else if( rc ) {
-                   log_error(_("lid %lu: insert failed: %s\n"),
-                                      pk->local_id, gpg_errstr(rc) );
-                   err_count++;
-               }
-               else {
-                   if( opt.verbose )
-                       log_info(_("lid %lu: inserted\n"), pk->local_id );
-                   new_count++;
+      /* Process the user ids in the order they appear in the key
+         block.  */
+      strlist_rev (&user_id_list);
+
+      /* It only makes sense to observe any signature before getting
+         the validity.  This is because if the current signature
+         results in a conflict, then we damn well want to take that
+         into account.  */
+      if (sig)
+        {
+          err = tofu_register_signature (ctrl, main_pk, user_id_list,
+                                         sig->digest, sig->digest_len,
+                                         sig->timestamp, "unknown");
+          if (err)
+            {
+              log_error ("TOFU: error registering signature: %s\n",
+                         gpg_strerror (err));
+
+              tofu_validity = TRUST_UNKNOWN;
+            }
+        }
+      if (! err)
+        tofu_validity = tofu_get_validity (ctrl, main_pk, user_id_list,
+                                           may_ask);
+
+      free_strlist (user_id_list);
+      if (free_kb)
+        release_kbnode (kb);
+    }
+#endif /*USE_TOFU*/
+
+  if (opt.trust_model == TM_TOFU_PGP
+      || opt.trust_model == TM_CLASSIC
+      || opt.trust_model == TM_PGP)
+    {
+      err = read_trust_record (ctrl, main_pk, &trec);
+      if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND)
+       {
+         tdbio_invalid ();
+         return 0;
+       }
+      if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+       {
+         /* No record found.  */
+         validity = TRUST_UNKNOWN;
+         goto leave;
+       }
+
+      /* Loop over all user IDs */
+      recno = trec.r.trust.validlist;
+      validity = 0;
+      while (recno)
+       {
+         read_record (recno, &vrec, RECTYPE_VALID);
+
+         if(uid)
+           {
+             /* If a user ID is given we return the validity for that
+                user ID ONLY.  If the namehash is not found, then
+                there is no validity at all (i.e. the user ID wasn't
+                signed). */
+             if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0)
+               {
+                 validity=(vrec.r.valid.validity & TRUST_MASK);
+                 break;
                }
            }
-           else if( rc ) {
-               log_error(_("error reading dir record: %s\n"), gpg_errstr(rc));
-               err_count++;
+         else
+           {
+             /* If no user ID is given, we take the maximum validity
+                over all user IDs */
+             if (validity < (vrec.r.valid.validity & TRUST_MASK))
+               validity = (vrec.r.valid.validity & TRUST_MASK);
            }
 
-           release_kbnode( keyblock ); keyblock = NULL;
-           if( !(++count % 100) )
-               log_info(_("%lu keys so far processed\n"), count);
+         recno = vrec.r.valid.next;
        }
-       log_info(_("%lu keys processed\n"), count);
-       if( err_count )
-           log_info(_("\t%lu keys with errors\n"), err_count);
-       if( new_count )
-           log_info(_("\t%lu keys inserted\n"), new_count);
-    }
-    if( rc && rc != -1 )
-       log_error(_("enumerate keyblocks failed: %s\n"), gpg_errstr(rc));
-
-    enum_keyblocks_end( kbpos ); 
-    release_kbnode( keyblock );
-}
-
-
 
-/****************
- * Do all required checks in the trustdb.  This function walks over all
- * records in the trustdb and does scheduled processing.
- */
-void
-check_trustdb( const char *username )
-{
-    TRUSTREC rec;
-    ulong recnum;
-    ulong count=0, upd_count=0, err_count=0, skip_count=0, sigonly_count=0;
-    ulong current_time = make_timestamp();
-
-    if( username )
-       log_info("given user IDs ignored in check_trustdb\n");
-
-    init_trustdb();
-
-    for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) {
-       int sigs_only;
-
-       if( rec.rectype != RECTYPE_DIR )
-           continue; /* we only want the dir records */
-
-       if( count && !(count % 100) && !opt.quiet )
-           log_info(_("%lu keys so far processed\n"), count);
-       count++;
-       sigs_only = 0;
-
-       if( !(rec.r.dir.dirflags & DIRF_CHECKED) )
-           ;
-       else if( !rec.r.dir.checkat || rec.r.dir.checkat > current_time ) {
-           if( !(rec.r.dir.dirflags & DIRF_NEWKEYS) ) {
-               skip_count++;
-               continue;  /* not scheduled for checking */
-           }
-           sigs_only = 1; /* new public keys - check them */
-           sigonly_count++;
+      if ((trec.r.trust.ownertrust & TRUST_FLAG_DISABLED))
+       {
+         validity |= TRUST_FLAG_DISABLED;
+         pk->flags.disabled = 1;
        }
+      else
+       pk->flags.disabled = 0;
+      pk->flags.disabled_valid = 1;
+    }
 
-       if( !rec.r.dir.keylist ) {
-           log_info(_("lid %lu: dir record w/o key - skipped\n"), recnum);
-           skip_count++;
-           continue;
-       }
+ leave:
+#ifdef USE_TOFU
+  validity = tofu_wot_trust_combine (tofu_validity, validity);
+#else /*!USE_TOFU*/
+  validity &= TRUST_MASK;
 
-       check_trust_record( &rec, sigs_only );
-    }
+  if (validity == TRUST_NEVER)
+    /* TRUST_NEVER trumps everything else.  */
+    validity |= TRUST_NEVER;
+  if (validity == TRUST_EXPIRED)
+    /* TRUST_EXPIRED trumps everything but TRUST_NEVER.  */
+    validity |= TRUST_EXPIRED;
+#endif /*!USE_TOFU*/
+
+  if (opt.trust_model != TM_TOFU
+      && pending_check_trustdb)
+    validity |= TRUST_FLAG_PENDING_CHECK;
 
-    log_info(_("%lu keys processed\n"), count);
-    if( sigonly_count )
-       log_info(_("\t%lu due to new pubkeys\n"), sigonly_count);
-    if( skip_count )
-       log_info(_("\t%lu keys skipped\n"), skip_count);
-    if( err_count )
-       log_info(_("\t%lu keys with errors\n"), err_count);
-    if( upd_count )
-       log_info(_("\t%lu keys updated\n"), upd_count);
+  return validity;
 }
 
 
-\f
-/***********************************************
- *********  Trust calculation  *****************
- ***********************************************/
-
-/****************
- * Find all certification paths of a given LID.
- * Limit the search to MAX_DEPTH.  stack is a helper variable which
- * should have been allocated with size max_depth, stack[0] should
- * be setup to the key we are investigating, so the minimal depth
- * we should ever see in this function is 1.
- * Returns: a new tree
- * certchain_set must be a valid set or point to NULL; this function
- * may modifiy it.
- *
- * Hmmm: add a fastscan mode which stops at valid validity nodes.
- */
-static TN
-build_cert_tree( ulong lid, int depth, int max_depth, TN helproot )
+static void
+get_validity_counts (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid)
 {
-    TRUSTREC dirrec;
-    TRUSTREC uidrec;
-    ulong uidrno;
-    TN keynode;
-
-    if( depth >= max_depth )
-       return NULL;
-
-    keynode = new_tn();
-    if( !helproot )
-       helproot = keynode;
-    keynode->lid = lid;
-    if( !qry_lid_table_flag( ultikey_table, lid, NULL ) ) {
-       /* this is an ultimately trusted key;
-        * which means that we have found the end of the chain:
-        * We do this here prior to reading the dir record
-        * because we don't really need the info from that record */
-       keynode->n.k.ownertrust = TRUST_ULTIMATE;
-       keynode->n.k.buckstop   = 1;
-       return keynode;
-    }
-    read_record( lid, &dirrec, 0 );
-    if( dirrec.rectype != RECTYPE_DIR ) {
-       if( dirrec.rectype != RECTYPE_SDIR )
-           log_debug("lid %lu, has rectype %d"
-                     " - skipped\n", lid, dirrec.rectype );
-       gcry_free(keynode);
-       return NULL;
-    }
+  TRUSTREC trec, vrec;
+  ulong recno;
 
-    if( dirrec.r.dir.checkat && dirrec.r.dir.checkat <= make_timestamp() ) {
-       check_trust_record( &dirrec, 0 );
-    }
-    else if( (dirrec.r.dir.dirflags & DIRF_NEWKEYS) ) {
-       check_trust_record( &dirrec, 1 );
-    }
+  if(pk==NULL || uid==NULL)
+    BUG();
 
-    keynode->n.k.ownertrust = dirrec.r.dir.ownertrust & TRUST_MASK;
-
-    /* loop over all user ids */
-    for( uidrno = dirrec.r.dir.uidlist; uidrno; uidrno = uidrec.r.uid.next ) {
-       TRUSTREC sigrec;
-       ulong sigrno;
-       TN uidnode = NULL;
-
-       read_record( uidrno, &uidrec, RECTYPE_UID );
-
-       if( !(uidrec.r.uid.uidflags & UIDF_CHECKED) )
-           continue; /* user id has not been checked */
-       if( !(uidrec.r.uid.uidflags & UIDF_VALID) )
-           continue; /* user id is not valid */
-       if( (uidrec.r.uid.uidflags & UIDF_REVOKED) )
-           continue; /* user id has been revoked */
-
-       /* loop over all signature records */
-       for(sigrno=uidrec.r.uid.siglist; sigrno; sigrno = sigrec.r.sig.next ) {
-           int i;
-           TN tn;
-
-           read_record( sigrno, &sigrec, RECTYPE_SIG );
-
-           for(i=0; i < SIGS_PER_RECORD; i++ ) {
-               if( !sigrec.r.sig.sig[i].lid )
-                   continue; /* skip deleted sigs */
-               if( !(sigrec.r.sig.sig[i].flag & SIGF_CHECKED) )
-                   continue; /* skip unchecked signatures */
-               if( !(sigrec.r.sig.sig[i].flag & SIGF_VALID) )
-                   continue; /* skip invalid signatures */
-               if( (sigrec.r.sig.sig[i].flag & SIGF_EXPIRED) )
-                   continue; /* skip expired signatures */
-               if( (sigrec.r.sig.sig[i].flag & SIGF_REVOKED) )
-                   continue; /* skip revoked signatures */
-               /* check for cycles */
-               for( tn=keynode; tn && tn->lid != sigrec.r.sig.sig[i].lid;
-                                                         tn = tn->back )
-                   ;
-               if( tn )
-                   continue; /* cycle found */
-
-               tn = build_cert_tree( sigrec.r.sig.sig[i].lid,
-                                     depth+1, max_depth, helproot );
-               if( !tn )
-                   continue; /* cert chain too deep or error */
-
-               if( !uidnode ) {
-                   uidnode = new_tn();
-                   uidnode->back = keynode;
-                   uidnode->lid = uidrno;
-                   uidnode->is_uid = 1;
-                   uidnode->next = keynode->list;
-                   keynode->list = uidnode;
-               }
+  namehash_from_uid(uid);
 
-               tn->back = uidnode;
-               tn->next = uidnode->list;
-               uidnode->list = tn;
-               if( tn->n.k.buckstop ) {
-                   /* ultimately trusted key found:
-                    * no need to check more signatures of this uid */
-                   sigrec.r.sig.next = 0;
-                   break;
-               }
-           }
-       } /* end loop over sig recs */
-    } /* end loop over user ids */
+  uid->help_marginal_count=uid->help_full_count=0;
 
-    if( !keynode->list ) {
-       release_tn_tree( keynode );
-       keynode = NULL;
-    }
+  init_trustdb (ctrl, 0);
 
-    return keynode;
-}
+  if(read_trust_record (ctrl, pk, &trec))
+    return;
 
+  /* loop over all user IDs */
+  recno = trec.r.trust.validlist;
+  while (recno)
+    {
+      read_record (recno, &vrec, RECTYPE_VALID);
+
+      if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0)
+       {
+         uid->help_marginal_count=vrec.r.valid.marginal_count;
+         uid->help_full_count=vrec.r.valid.full_count;
+         /*  es_printf("Fetched marginal %d, full %d\n",uid->help_marginal_count,uid->help_full_count); */
+         break;
+       }
 
-static void
-upd_one_ownertrust( ulong lid, unsigned new_trust, unsigned *retflgs )
-{
-    TRUSTREC rec;
-
-    read_record( lid, &rec, RECTYPE_DIR );
-    if( DBG_TRUST )
-       log_debug("upd_one_ownertrust of %lu from %u to %u\n",
-                          lid, (unsigned)rec.r.dir.ownertrust, new_trust );
-    if( retflgs ) {
-       if( (new_trust & TRUST_MASK) > (rec.r.dir.ownertrust & TRUST_MASK) )
-           *retflgs |= 16; /* modified up */
-       else
-           *retflgs |= 32; /* modified down */
+      recno = vrec.r.valid.next;
     }
-
-    /* we preserve the disabled state here */
-    if( (rec.r.dir.ownertrust & TRUST_FLAG_DISABLED) )
-       rec.r.dir.ownertrust = new_trust | TRUST_FLAG_DISABLED;
-    else
-       rec.r.dir.ownertrust = new_trust & ~TRUST_FLAG_DISABLED;
-    write_record( &rec );
 }
 
-/****************
- * Update the ownertrust in the complete tree.
- */
-static void
-propagate_ownertrust( TN kr, ulong lid, unsigned trust )
+void
+list_trust_path( const char *username )
 {
-    TN ur;
-
-    for( ; kr; kr = kr->next ) {
-       if( kr->lid == lid )
-           kr->n.k.ownertrust = trust;
-       for( ur=kr->list; ur; ur = ur->next )
-           propagate_ownertrust( ur->list, lid, trust );
-    }
+  (void)username;
 }
 
 /****************
- * Calculate the validity of all keys in the tree and especially
- * the one of the top key.  If add_fnc is not NULL, it is used to
- * ask for missing ownertrust values (but only if this will help
- * us to increase the validity.
- * add_fnc is expected to take the LID of the key under question
- * and return a ownertrust value or an error:  positive values
- * are assumed to be the new ownertrust value; a 0 does mean no change,
- * a -1 is a request to cancel this validation procedure, a -2 requests
- * a listing of the sub-tree using the tty functions.
+ * Enumerate all keys, which are needed to build all trust paths for
+ * the given key.  This function does not return the key itself or
+ * the ultimate key (the last point in cerificate chain).  Only
+ * certificate chains which ends up at an ultimately trusted key
+ * are listed. If ownertrust or validity is not NULL, the corresponding
+ * value for the returned LID is also returned in these variable(s).
  *
+ *  1) create a void pointer and initialize it to NULL
+ *  2) pass this void pointer by reference to this function.
+ *     Set lid to the key you want to enumerate and pass it by reference.
+ *  3) call this function as long as it does not return -1
+ *     to indicate EOF. LID does contain the next key used to build the web
+ *  4) Always call this function a last time with LID set to NULL,
+ *     so that it can free its context.
  *
- * Returns: 0 = okay
+ * Returns: -1 on EOF or the level of the returned LID
  */
-static int
-propagate_validity( TN root, TN node, int (*add_fnc)(ulong), unsigned *retflgs )
+int
+enum_cert_paths( void **context, ulong *lid,
+                unsigned *ownertrust, unsigned *validity )
 {
-    TN kr, ur;
-    int max_validity = 0;
-
-    assert( !node->is_uid );
-    if( node->n.k.ownertrust == TRUST_ULTIMATE ) {
-       /* this is one of our keys */
-       assert( !node->list ); /* it should be a leaf */
-       node->n.k.validity = TRUST_ULTIMATE;
-       if( retflgs )
-           *retflgs |= 1;  /* found a path to an ultimately trusted key */
-       return 0;
-    }
-
-    /* loop over all user ids */
-    for( ur=node->list; ur && max_validity <= TRUST_FULLY; ur = ur->next ) {
-       assert( ur->is_uid );
-       /* loop over all signators */
-       for(kr=ur->list; kr && max_validity <= TRUST_FULLY; kr = kr->next ) {
-           if( propagate_validity( root, kr, add_fnc, retflgs ) )
-               return -1; /* quit */
-           if( kr->n.k.validity == TRUST_ULTIMATE ) {
-               ur->n.u.fully_count = opt.completes_needed;
-           }
-           else if( kr->n.k.validity == TRUST_FULLY ) {
-               if( add_fnc && !kr->n.k.ownertrust ) {
-                   int rc;
-
-                   if( retflgs )
-                       *retflgs |= 2; /* found key with undefined ownertrust*/
-                   do {
-                       rc = add_fnc( kr->lid );
-                       switch( rc ) {
-                         case TRUST_NEVER:
-                         case TRUST_MARGINAL:
-                         case TRUST_FULLY:
-                           propagate_ownertrust( root, kr->lid, rc );
-                           upd_one_ownertrust( kr->lid, rc, retflgs );
-                           if( retflgs )
-                               *retflgs |= 4; /* changed */
-                           break;
-                         case -1:
-                           return -1; /* cancel */
-                         case -2:
-                           dump_tn_tree( NULL, 0, kr );
-                           tty_printf("\n");
-                           break;
-                         default:
-                           break;
-                       }
-                   } while( rc == -2 );
-               }
-               if( kr->n.k.ownertrust == TRUST_FULLY )
-                   ur->n.u.fully_count++;
-               else if( kr->n.k.ownertrust == TRUST_MARGINAL )
-                   ur->n.u.marginal_count++;
-           }
-
-           if( ur->n.u.fully_count >= opt.completes_needed
-               || ur->n.u.marginal_count >= opt.marginals_needed )
-               ur->n.u.validity = TRUST_FULLY;
-           else if( ur->n.u.fully_count || ur->n.u.marginal_count )
-               ur->n.u.validity = TRUST_MARGINAL;
-
-           if( ur->n.u.validity >= max_validity )
-               max_validity = ur->n.u.validity;
-       }
-    }
-
-    node->n.k.validity = max_validity;
-    return 0;
+  (void)context;
+  (void)lid;
+  (void)ownertrust;
+  (void)validity;
+  return -1;
 }
 
 
-
 /****************
- * Given the directory record of a key, check whether we can
- * find a path to an ultimately trusted key.  We do this by
- * checking all key signatures up to a some depth.
+ * Print the current path
  */
-static int
-verify_key( int max_depth, TRUSTREC *drec, const char *namehash,
-                           int (*add_fnc)(ulong), unsigned *retflgs )
+void
+enum_cert_paths_print (void **context, FILE *fp,
+                       int refresh, ulong selected_lid)
 {
-    TN tree;
-    int keytrust;
-    int pv_result;
-
-    tree = build_cert_tree( drec->r.dir.lid, 0, opt.max_cert_depth, NULL );
-    if( !tree )
-       return TRUST_UNDEFINED;
-    pv_result = propagate_validity( tree, tree, add_fnc, retflgs );
-    if( namehash && tree->n.k.validity != TRUST_ULTIMATE ) {
-       /* find the matching user id.
-        * We don't do this here if the key is ultimately trusted; in
-        * this case there will be no lids for the user IDs and frankly
-        * it does not make sense to compare by the name if we do
-        * have the secret key.
-        * fixme: the way we handle this is too inefficient */
-       TN ur;
-       TRUSTREC rec;
-
-       keytrust = 0;
-       for( ur=tree->list; ur; ur = ur->next ) {
-           read_record( ur->lid, &rec, RECTYPE_UID );
-           if( !memcmp( namehash, rec.r.uid.namehash, 20 ) ) {
-               keytrust = ur->n.u.validity;
-               break;
-           }
-       }
-    }
-    else
-       keytrust = tree->n.k.validity;
-
-    /* update the cached validity values */
-    if( !pv_result
-       && keytrust >= TRUST_UNDEFINED
-       && tdbio_db_matches_options()
-       && ( !drec->r.dir.valcheck || drec->r.dir.validity != keytrust ) ) {
-       TN ur;
-       TRUSTREC rec;
-
-       for( ur=tree->list; ur; ur = ur->next ) {
-           read_record( ur->lid, &rec, RECTYPE_UID );
-           if( rec.r.uid.validity != ur->n.u.validity ) {
-               rec.r.uid.validity = ur->n.u.validity;
-               write_record( &rec );
-           }
-       }
-
-       drec->r.dir.validity = tree->n.k.validity;
-       drec->r.dir.valcheck = make_timestamp();
-       write_record( drec );
-       do_sync();
-    }
-
-    release_tn_tree( tree );
-    return keytrust;
+  (void)context;
+  (void)fp;
+  (void)refresh;
+  (void)selected_lid;
 }
 
 
-/****************
- * we have the pubkey record and all needed informations are in the trustdb
- * but nothing more is known.
- */
+\f
+/****************************************
+ *********** NEW NEW NEW ****************
+ ****************************************/
+
 static int
-do_check( TRUSTREC *dr, unsigned *validity,
-         const char *namehash, int (*add_fnc)(ulong), unsigned *retflgs )
+ask_ownertrust (ctrl_t ctrl, u32 *kid, int minimum)
 {
-    if( !dr->r.dir.keylist ) {
-       log_error(_("Ooops, no keys\n"));
-       return GPGERR_TRUSTDB;
-    }
-    if( !dr->r.dir.uidlist ) {
-       log_error(_("Ooops, no user IDs\n"));
-       return GPGERR_TRUSTDB;
-    }
+  PKT_public_key *pk;
+  int rc;
+  int ot;
 
-    if( retflgs )
-       *retflgs &= ~(16|32);  /* reset the 2 special flags */
+  pk = xmalloc_clear (sizeof *pk);
+  rc = get_pubkey (ctrl, pk, kid);
+  if (rc)
+    {
+      log_error (_("public key %s not found: %s\n"),
+                 keystr(kid), gpg_strerror (rc) );
+      return TRUST_UNKNOWN;
+    }
 
-    if( (dr->r.dir.ownertrust & TRUST_FLAG_DISABLED) )
-       *validity = 0; /* no need to check further */
-    else if( namehash ) {
-       /* Fixme: use a cache */
-       *validity = verify_key( opt.max_cert_depth, dr, namehash,
-                                                       add_fnc, retflgs );
+  if(opt.force_ownertrust)
+    {
+      log_info("force trust for key %s to %s\n",
+              keystr(kid),trust_value_to_string(opt.force_ownertrust));
+      tdb_update_ownertrust (ctrl, pk, opt.force_ownertrust);
+      ot=opt.force_ownertrust;
     }
-    else if( !add_fnc
-       && tdbio_db_matches_options()
-           /* FIXME, TODO: This comparision is WRONG ! */
-       && dr->r.dir.valcheck
-           > tdbio_read_modify_stamp( (dr->r.dir.validity < TRUST_FULLY) )
-       && dr->r.dir.validity )
-       *validity = dr->r.dir.validity;
-    else
-       *validity = verify_key( opt.max_cert_depth, dr, NULL,
-                                                       add_fnc, retflgs );
-
-    if( !(*validity & TRUST_MASK) )
-       *validity = TRUST_UNDEFINED;
-
-    if( (dr->r.dir.ownertrust & TRUST_FLAG_DISABLED) )
-       *validity |= TRUST_FLAG_DISABLED;
-
-    if( dr->r.dir.dirflags & DIRF_REVOKED )
-       *validity |= TRUST_FLAG_REVOKED;
-
-    /* If we have changed some ownertrusts, set the trustdb timestamps
-     * and do a sync */
-    if( retflgs && (*retflgs & (16|32)) ) {
-       tdbio_write_modify_stamp( (*retflgs & 16), (*retflgs & 32) );
-       do_sync();
+  else
+    {
+      ot=edit_ownertrust (ctrl, pk, 0);
+      if(ot>0)
+       ot = tdb_get_ownertrust (ctrl, pk, 0);
+      else if(ot==0)
+       ot = minimum?minimum:TRUST_UNDEFINED;
+      else
+       ot = -1; /* quit */
     }
 
+  free_public_key( pk );
 
-    return 0;
+  return ot;
 }
 
 
-\f
-/***********************************************
- *********  Change trustdb values **************
- ***********************************************/
-
-int
-update_ownertrust( ulong lid, unsigned new_trust )
+static void
+mark_keyblock_seen (KeyHashTable tbl, KBNODE node)
 {
-    TRUSTREC rec;
-
-    init_trustdb();
-    read_record( lid, &rec, RECTYPE_DIR );
-    if( DBG_TRUST )
-       log_debug("update_ownertrust of %lu from %u to %u\n",
-                          lid, (unsigned)rec.r.dir.ownertrust, new_trust );
-    rec.r.dir.ownertrust = new_trust;
-    write_record( &rec );
-    do_sync();
-    return 0;
-}
+  for ( ;node; node = node->next )
+    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);
+        add_key_hash_table (tbl, aki);
+      }
+}
 
-int
-clear_trust_checked_flag( PKT_public_key *pk )
-{
-    TRUSTREC rec;
-    int rc;
 
-    if( opt.dry_run )
-       return 0;
+static void
+dump_key_array (int depth, struct key_array *keys)
+{
+  struct key_array *kar;
+
+  for (kar=keys; kar->keyblock; kar++)
+    {
+      KBNODE node = kar->keyblock;
+      u32 kid[2];
+
+      keyid_from_pk(node->pkt->pkt.public_key, kid);
+      es_printf ("%d:%08lX%08lX:K::%c::::\n",
+                 depth, (ulong)kid[0], (ulong)kid[1], '?');
+
+      for (; node; node = node->next)
+        {
+          if (node->pkt->pkttype == PKT_USER_ID)
+            {
+              int len = node->pkt->pkt.user_id->len;
+
+              if (len > 30)
+                len = 30;
+              es_printf ("%d:%08lX%08lX:U:::%c:::",
+                         depth, (ulong)kid[0], (ulong)kid[1],
+                         (node->flag & 4)? 'f':
+                         (node->flag & 2)? 'm':
+                         (node->flag & 1)? 'q':'-');
+              es_write_sanitized (es_stdout, node->pkt->pkt.user_id->name,
+                                  len, ":", NULL);
+              es_putc (':', es_stdout);
+              es_putc ('\n', es_stdout);
+            }
+        }
+    }
+}
 
-    init_trustdb();
-    rc = get_dir_record( pk, &rec );
-    if( rc )
-       return rc;
 
-    /* check whether they are already reset */
-    if( !(rec.r.dir.dirflags & DIRF_CHECKED) && !rec.r.dir.valcheck )
-       return 0;
+static void
+store_validation_status (ctrl_t ctrl, int depth,
+                         kbnode_t keyblock, KeyHashTable stored)
+{
+  KBNODE node;
+  int status;
+  int any = 0;
+
+  for (node=keyblock; node; node = node->next)
+    {
+      if (node->pkt->pkttype == PKT_USER_ID)
+        {
+          PKT_user_id *uid = node->pkt->pkt.user_id;
+          if (node->flag & 4)
+            status = TRUST_FULLY;
+          else if (node->flag & 2)
+            status = TRUST_MARGINAL;
+          else if (node->flag & 1)
+            status = TRUST_UNDEFINED;
+          else
+            status = 0;
+
+          if (status)
+            {
+              update_validity (ctrl, keyblock->pkt->pkt.public_key,
+                              uid, depth, status);
+
+             mark_keyblock_seen(stored,keyblock);
+
+              any = 1;
+            }
+        }
+    }
 
-    /* reset the flag */
-    rec.r.dir.dirflags &= ~DIRF_CHECKED;
-    rec.r.dir.valcheck = 0;
-    write_record( &rec );
-    do_sync();
-    return 0;
+  if (any)
+    do_sync ();
 }
 
 
+/* Returns a sanitized copy of the regexp (which might be "", but not
+   NULL). */
+#ifndef DISABLE_REGEX
+/* Operator charactors except '.' and backslash.
+   See regex(7) on BSD.  */
+#define REGEXP_OPERATOR_CHARS "^[$()|*+?{"
 
+static char *
+sanitize_regexp(const char *old)
+{
+  size_t start=0,len=strlen(old),idx=0;
+  int escaped=0,standard_bracket=0;
+  char *new=xmalloc((len*2)+1); /* enough to \-escape everything if we
+                                  have to */
 
-\f
-/***********************************************
- *********  Query trustdb values  **************
- ***********************************************/
+  /* There are basically two commonly-used regexps here.  GPG and most
+     versions of PGP use "<[^>]+[@.]example\.com>$" and PGP (9)
+     command line uses "example.com" (i.e. whatever the user specifies,
+     and we can't expect users know to use "\." instead of ".").  So
+     here are the rules: we're allowed to start with "<[^>]+[@.]" and
+     end with ">$" or start and end with nothing.  In between, the
+     only legal regex character is ".", and everything else gets
+     escaped.  Part of the gotcha here is that some regex packages
+     allow more than RFC-4880 requires.  For example, 4880 has no "{}"
+     operator, but GNU regex does.  Commenting removes these operators
+     from consideration.  A possible future enhancement is to use
+     commenting to effectively back off a given regex to the Henry
+     Spencer syntax in 4880. -dshaw */
 
+  /* Are we bracketed between "<[^>]+[@.]" and ">$" ? */
+  if(len>=12 && strncmp(old,"<[^>]+[@.]",10)==0
+     && old[len-2]=='>' && old[len-1]=='$')
+    {
+      strcpy(new,"<[^>]+[@.]");
+      idx=strlen(new);
+      standard_bracket=1;
+      start+=10;
+      len-=2;
+    }
 
-/****************
- * This function simply looks for the key in the trustdb
- * and makes sure that pk->local_id is set to the correct value.
- * Return: 0 = found
- *        -1 = not found
- *       other = error
- */
-int
-query_trust_record( PKT_public_key *pk )
-{
-    TRUSTREC rec;
-    init_trustdb();
-    return get_dir_record( pk, &rec );
-}
+  /* Walk the remaining characters and ensure that everything that is
+     left is not an operational regex character. */
+  for(;start<len;start++)
+    {
+      if(!escaped && old[start]=='\\')
+       escaped=1;
+      else if (!escaped && strchr (REGEXP_OPERATOR_CHARS, old[start]))
+       new[idx++]='\\';
+      else
+       escaped=0;
 
+      new[idx++]=old[start];
+    }
 
-/****************
- * Get the trustlevel for this PK.
- * Note: This does not ask any questions
- * Returns: 0 okay of an errorcode
- *
- * It operates this way:
- *  locate the pk in the trustdb
- *     found:
- *         Do we have a valid cache record for it?
- *             yes: return trustlevel from cache
- *             no:  make a cache record and all the other stuff
- *     not found:
- *         try to insert the pubkey into the trustdb and check again
- *
- * Problems: How do we get the complete keyblock to check that the
- *          cache record is actually valid?  Think we need a clever
- *          cache in getkey.c  to keep track of this stuff. Maybe it
- *          is not necessary to check this if we use a local pubring. Hmmmm.
- */
-int
-check_trust( PKT_public_key *pk, unsigned *r_trustlevel,
-            const byte *namehash, int (*add_fnc)(ulong), unsigned *retflgs )
-{
-    TRUSTREC rec;
-    unsigned trustlevel = TRUST_UNKNOWN;
-    int rc=0;
-    u32 cur_time;
-    u32 keyid[2];
+  new[idx]='\0';
 
+  /* Note that the (sub)string we look at might end with a bare "\".
+     If it does, leave it that way.  If the regexp actually ended with
+     ">$", then it was escaping the ">" and is fine.  If the regexp
+     actually ended with the bare "\", then it's an illegal regexp and
+     regcomp should kick it out. */
 
-    init_trustdb();
-    keyid_from_pk( pk, keyid );
+  if(standard_bracket)
+    strcat(new,">$");
 
-    /* get the pubkey record */
-    if( pk->local_id ) {
-       read_record( pk->local_id, &rec, RECTYPE_DIR );
-    }
-    else { /* no local_id: scan the trustdb */
-       if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 ) {
-           log_error(_("check_trust: search dir record failed: %s\n"),
-                                                           gpg_errstr(rc));
-           return rc;
-       }
-       else if( rc == -1 && opt.dry_run )
-           return GPGERR_GENERAL;
-       else if( rc == -1 ) { /* not found - insert */
-           rc = insert_trust_record_by_pk( pk );
-           if( rc ) {
-               log_error(_("key %08lX: insert trust record failed: %s\n"),
-                                         (ulong)keyid[1], gpg_errstr(rc));
-               goto leave;
-           }
-           log_info(_("key %08lX.%lu: inserted into trustdb\n"),
-                                         (ulong)keyid[1], pk->local_id );
-           /* and re-read the dir record */
-           read_record( pk->local_id, &rec, RECTYPE_DIR );
-       }
-    }
-    cur_time = make_timestamp();
-    if( pk->timestamp > cur_time ) {
-       log_info(_("key %08lX.%lu: created in future "
-                  "(time warp or clock problem)\n"),
-                                         (ulong)keyid[1], pk->local_id );
-       if( !opt.ignore_time_conflict )
-           return GPGERR_TIME_CONFLICT;
-    }
+  return new;
+}
+#endif /*!DISABLE_REGEX*/
 
-    if( !(rec.r.dir.dirflags & DIRF_CHECKED) )
-       check_trust_record( &rec, 0 );
-    else if( rec.r.dir.checkat && rec.r.dir.checkat <= cur_time )
-       check_trust_record( &rec, 0 );
-    else if( (rec.r.dir.dirflags & DIRF_NEWKEYS) )
-       check_trust_record( &rec, 1 );
-
-    if( pk->expiredate && pk->expiredate <= cur_time ) {
-       log_info(_("key %08lX.%lu: expired at %s\n"),
-                       (ulong)keyid[1], pk->local_id,
-                            asctimestamp( pk->expiredate) );
-       trustlevel = TRUST_EXPIRED;
-    }
-    else {
-       rc = do_check( &rec, &trustlevel, namehash, add_fnc, retflgs );
-       if( rc ) {
-           log_error(_("key %08lX.%lu: trust check failed: %s\n"),
-                           (ulong)keyid[1], pk->local_id, gpg_errstr(rc));
-           return rc;
-       }
-    }
+/* Used by validate_one_keyblock to confirm a regexp within a trust
+   signature.  Returns 1 for match, and 0 for no match or regex
+   error. */
+static int
+check_regexp(const char *expr,const char *string)
+{
+#ifdef DISABLE_REGEX
+  (void)expr;
+  (void)string;
+  /* When DISABLE_REGEX is defined, assume all regexps do not
+     match. */
+  return 0;
+#else
+  int ret;
+  char *regexp;
+
+  regexp=sanitize_regexp(expr);
+
+#ifdef __riscos__
+  ret=riscos_check_regexp(expr, string, DBG_TRUST);
+#else
+  {
+    regex_t pat;
+
+    ret=regcomp(&pat,regexp,REG_ICASE|REG_NOSUB|REG_EXTENDED);
+    if(ret==0)
+      {
+       ret=regexec(&pat,string,0,NULL,0);
+       regfree(&pat);
+      }
+    ret=(ret==0);
+  }
+#endif
 
-    /* is a subkey has been requested, we have to check its keyflags */
-    if( !rc ) {
-       TRUSTREC krec;
-       byte fpr[MAX_FINGERPRINT_LEN] = {0}; /* to avoid compiler warnings */
-       size_t fprlen = 0;
-       ulong recno;
-       int kcount=0;
-
-       for( recno = rec.r.dir.keylist; recno; recno = krec.r.key.next ) {
-           read_record( recno, &krec, RECTYPE_KEY );
-           if( ++kcount == 1 )
-               continue; /* skip the primary key */
-           if( kcount == 2 ) /* now we need the fingerprint */
-               fingerprint_from_pk( pk, fpr, &fprlen );
-
-           if( krec.r.key.fingerprint_len == fprlen
-               && !memcmp( krec.r.key.fingerprint, fpr, fprlen ) ) {
-               /* found the subkey */
-               if( (krec.r.key.keyflags & KEYF_REVOKED) )
-                   trustlevel |= TRUST_FLAG_SUB_REVOKED;
-               /* should we check for keybinding here??? */
-               /* Hmmm: Maybe this whole checking stuff should not go
-                * into the trustdb, but be done direct from the keyblock.
-                * Chnage this all when we add an abstarction layer around
-                * the way certificates are handled by different standards */
-               break;
-           }
-       }
-    }
+  if(DBG_TRUST)
+    log_debug("regexp '%s' ('%s') on '%s': %s\n",
+             regexp,expr,string,ret?"YES":"NO");
 
+  xfree(regexp);
 
-  leave:
-    if( DBG_TRUST )
-       log_debug("check_trust() returns trustlevel %04x.\n", trustlevel);
-    *r_trustlevel = trustlevel;
-    return 0;
+  return ret;
+#endif
 }
 
-
-/****************
- * scan the whole trustdb and mark all signature records whose keys
- * are freshly imported.
+/*
+ * Return true if the key is signed by one of the keys in the given
+ * key ID list.  User IDs with a valid signature are marked by node
+ * flags as follows:
+ *  flag bit 0: There is at least one signature
+ *           1: There is marginal confidence that this is a legitimate uid
+ *           2: There is full confidence that this is a legitimate uid.
+ *           8: Used for internal purposes.
+ *           9: Ditto (in mark_usable_uid_certs())
+ *          10: Ditto (ditto)
+ * This function assumes that all kbnode flags are cleared on entry.
  */
-static void
-mark_fresh_keys()
-{
-    TRUSTREC dirrec, rec;
-    ulong recnum, lid;
-    int i;
-
-    memset( &dirrec, 0, sizeof dirrec );
-
-    for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) {
-       if( rec.rectype != RECTYPE_SIG )
-           continue;
-       /* if we have already have the dir record, we can check it now */
-       if( dirrec.recnum == rec.r.sig.lid
-           && (dirrec.r.dir.dirflags & DIRF_NEWKEYS) )
-           continue; /* flag is already set */
-
-       for(i=0; i < SIGS_PER_RECORD; i++ ) {
-           if( !(lid=rec.r.sig.sig[i].lid) )
-               continue; /* skip deleted sigs */
-           if( !(rec.r.sig.sig[i].flag & SIGF_CHECKED) )
-               continue; /* skip checked signatures */
-           if( qry_lid_table_flag( fresh_imported_keys, lid, NULL ) )
-               continue; /* not in the list of new keys */
-           read_record( rec.r.sig.lid, &dirrec, RECTYPE_DIR );
-           if( !(dirrec.r.dir.dirflags & DIRF_NEWKEYS) ) {
-               dirrec.r.dir.dirflags |= DIRF_NEWKEYS;
-               write_record( &dirrec );
+static int
+validate_one_keyblock (ctrl_t ctrl, kbnode_t kb, struct key_item *klist,
+                       u32 curtime, u32 *next_expire)
+{
+  struct key_item *kr;
+  KBNODE node, uidnode=NULL;
+  PKT_user_id *uid=NULL;
+  PKT_public_key *pk = kb->pkt->pkt.public_key;
+  u32 main_kid[2];
+  int issigned=0, any_signed = 0;
+
+  keyid_from_pk(pk, main_kid);
+  for (node=kb; node; node = node->next)
+    {
+      /* A bit of discussion here: is it better for the web of trust
+        to be built among only self-signed uids?  On the one hand, a
+        self-signed uid is a statement that the key owner definitely
+        intended that uid to be there, but on the other hand, a
+        signed (but not self-signed) uid does carry trust, of a sort,
+        even if it is a statement being made by people other than the
+        key owner "through" the uids on the key owner's key.  I'm
+        going with the latter.  However, if the user ID was
+        explicitly revoked, or passively allowed to expire, that
+        should stop validity through the user ID until it is
+        resigned.  -dshaw */
+
+      if (node->pkt->pkttype == PKT_USER_ID
+         && !node->pkt->pkt.user_id->flags.revoked
+         && !node->pkt->pkt.user_id->flags.expired)
+        {
+          if (uidnode && issigned)
+            {
+              if (uid->help_full_count >= opt.completes_needed
+                  || uid->help_marginal_count >= opt.marginals_needed )
+                uidnode->flag |= 4;
+              else if (uid->help_full_count || uid->help_marginal_count)
+                uidnode->flag |= 2;
+              uidnode->flag |= 1;
+              any_signed = 1;
+            }
+          uidnode = node;
+         uid=uidnode->pkt->pkt.user_id;
+
+         /* If the selfsig is going to expire... */
+         if(uid->expiredate && uid->expiredate<*next_expire)
+           *next_expire = uid->expiredate;
+
+          issigned = 0;
+         get_validity_counts (ctrl, pk, uid);
+          mark_usable_uid_certs (ctrl, kb, uidnode, main_kid, klist,
+                                 curtime, next_expire);
+        }
+      else if (node->pkt->pkttype == PKT_SIGNATURE
+              && (node->flag & (1<<8)) && uid)
+        {
+         /* Note that we are only seeing unrevoked sigs here */
+          PKT_signature *sig = node->pkt->pkt.signature;
+
+          kr = is_in_klist (klist, sig);
+         /* If the trust_regexp does not match, it's as if the sig
+             did not exist.  This is safe for non-trust sigs as well
+             since we don't accept a regexp on the sig unless it's a
+             trust sig. */
+          if (kr && (!kr->trust_regexp
+                     || !(opt.trust_model == TM_PGP
+                          || opt.trust_model == TM_TOFU_PGP)
+                     || (uidnode
+                         && check_regexp(kr->trust_regexp,
+                                         uidnode->pkt->pkt.user_id->name))))
+            {
+             /* Are we part of a trust sig chain?  We always favor
+                 the latest trust sig, rather than the greater or
+                 lesser trust sig or value.  I could make a decent
+                 argument for any of these cases, but this seems to be
+                 what PGP does, and I'd like to be compatible. -dms */
+              if ((opt.trust_model == TM_PGP
+                   || opt.trust_model == TM_TOFU_PGP)
+                  && sig->trust_depth
+                  && pk->trust_timestamp <= sig->timestamp)
+               {
+                 unsigned char depth;
+
+                 /* If the depth on the signature is less than the
+                    chain currently has, then use the signature depth
+                    so we don't increase the depth beyond what the
+                    signer wanted.  If the depth on the signature is
+                    more than the chain currently has, then use the
+                    chain depth so we use as much of the signature
+                    depth as the chain will permit.  An ultimately
+                    trusted signature can restart the depth to
+                    whatever level it likes. */
+
+                 if (sig->trust_depth < kr->trust_depth
+                      || kr->ownertrust == TRUST_ULTIMATE)
+                   depth = sig->trust_depth;
+                 else
+                   depth = kr->trust_depth;
+
+                 if (depth)
+                   {
+                     if(DBG_TRUST)
+                       log_debug ("trust sig on %s, sig depth is %d,"
+                                   " kr depth is %d\n",
+                                   uidnode->pkt->pkt.user_id->name,
+                                   sig->trust_depth,
+                                   kr->trust_depth);
+
+                     /* If we got here, we know that:
+
+                        this is a trust sig.
+
+                        it's a newer trust sig than any previous trust
+                        sig on this key (not uid).
+
+                        it is legal in that it was either generated by an
+                        ultimate key, or a key that was part of a trust
+                        chain, and the depth does not violate the
+                        original trust sig.
+
+                        if there is a regexp attached, it matched
+                        successfully.
+                     */
+
+                     if (DBG_TRUST)
+                       log_debug ("replacing trust value %d with %d and "
+                                   "depth %d with %d\n",
+                                   pk->trust_value,sig->trust_value,
+                                   pk->trust_depth,depth);
+
+                     pk->trust_value = sig->trust_value;
+                     pk->trust_depth = depth-1;
+
+                     /* If the trust sig contains a regexp, record it
+                        on the pk for the next round. */
+                     if (sig->trust_regexp)
+                       pk->trust_regexp = sig->trust_regexp;
+                   }
+               }
+
+              if (kr->ownertrust == TRUST_ULTIMATE)
+                uid->help_full_count = opt.completes_needed;
+              else if (kr->ownertrust == TRUST_FULLY)
+                uid->help_full_count++;
+              else if (kr->ownertrust == TRUST_MARGINAL)
+                uid->help_marginal_count++;
+              issigned = 1;
            }
-           break;
-       }
+        }
     }
 
-    do_sync();
+  if (uidnode && issigned)
+    {
+      if (uid->help_full_count >= opt.completes_needed
+         || uid->help_marginal_count >= opt.marginals_needed )
+        uidnode->flag |= 4;
+      else if (uid->help_full_count || uid->help_marginal_count)
+        uidnode->flag |= 2;
+      uidnode->flag |= 1;
+      any_signed = 1;
+    }
 
-    clear_lid_table( fresh_imported_keys );
-    fresh_imported_keys_count = 0;
+  return any_signed;
 }
 
 
-
-int
-query_trust_info( PKT_public_key *pk, const byte *namehash )
+static int
+search_skipfnc (void *opaque, u32 *kid, int dummy_uid_no)
 {
-    unsigned trustlevel;
-    int c;
-
-    init_trustdb();
-    if( check_trust( pk, &trustlevel, namehash, NULL, NULL ) )
-       return '?';
-    if( trustlevel & TRUST_FLAG_DISABLED )
-       return 'd';
-    if( trustlevel & TRUST_FLAG_REVOKED )
-       return 'r';
-    c = trust_letter( (trustlevel & TRUST_MASK) );
-    if( !c )
-       c = '?';
-    return c;
+  (void)dummy_uid_no;
+  return test_key_hash_table ((KeyHashTable)opaque, kid);
 }
 
 
-
-/****************
- * Return the assigned ownertrust value for the given LID
+/*
+ * Scan all keys and return a key_array of all suitable keys from
+ * kllist.  The caller has to pass keydb handle so that we don't use
+ * to create our own.  Returns either a key_array or NULL in case of
+ * an error.  No results found are indicated by an empty array.
+ * Caller hast to release the returned array.
  */
-unsigned
-get_ownertrust( ulong lid )
-{
-    TRUSTREC rec;
-
-    init_trustdb();
-    read_record( lid, &rec, RECTYPE_DIR );
-    return rec.r.dir.ownertrust;
-}
+static struct key_array *
+validate_key_list (ctrl_t ctrl, KEYDB_HANDLE hd, KeyHashTable full_trust,
+                   struct key_item *klist, u32 curtime, u32 *next_expire)
+{
+  KBNODE keyblock = NULL;
+  struct key_array *keys = NULL;
+  size_t nkeys, maxkeys;
+  int rc;
+  KEYDB_SEARCH_DESC desc;
+
+  maxkeys = 1000;
+  keys = xmalloc ((maxkeys+1) * sizeof *keys);
+  nkeys = 0;
+
+  rc = keydb_search_reset (hd);
+  if (rc)
+    {
+      log_error ("keydb_search_reset failed: %s\n", gpg_strerror (rc));
+      xfree (keys);
+      return NULL;
+    }
+
+  memset (&desc, 0, sizeof desc);
+  desc.mode = KEYDB_SEARCH_MODE_FIRST;
+  desc.skipfnc = search_skipfnc;
+  desc.skipfncvalue = full_trust;
+  rc = keydb_search (hd, &desc, 1, NULL);
+  if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
+    {
+      keys[nkeys].keyblock = NULL;
+      return keys;
+    }
+  if (rc)
+    {
+      log_error ("keydb_search(first) failed: %s\n", gpg_strerror (rc));
+      goto die;
+    }
+
+  desc.mode = KEYDB_SEARCH_MODE_NEXT; /* change mode */
+  do
+    {
+      PKT_public_key *pk;
+
+      rc = keydb_get_keyblock (hd, &keyblock);
+      if (rc)
+        {
+          log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc));
+         goto die;
+        }
 
-int
-get_ownertrust_info( ulong lid )
-{
-    unsigned otrust;
-    int c;
-
-    init_trustdb();
-    otrust = get_ownertrust( lid );
-    c = trust_letter( (otrust & TRUST_MASK) );
-    if( !c )
-       c = '?';
-    return c;
-}
+      if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY)
+        {
+          log_debug ("ooops: invalid pkttype %d encountered\n",
+                     keyblock->pkt->pkttype);
+          dump_kbnode (keyblock);
+          release_kbnode(keyblock);
+          continue;
+        }
 
+      /* prepare the keyblock for further processing */
+      merge_keys_and_selfsig (ctrl, keyblock);
+      clear_kbnode_flags (keyblock);
+      pk = keyblock->pkt->pkt.public_key;
+      if (pk->has_expired || pk->flags.revoked)
+        {
+          /* it does not make sense to look further at those keys */
+          mark_keyblock_seen (full_trust, keyblock);
+        }
+      else if (validate_one_keyblock (ctrl, keyblock, klist,
+                                      curtime, next_expire))
+        {
+         KBNODE node;
 
+          if (pk->expiredate && pk->expiredate >= curtime
+              && pk->expiredate < *next_expire)
+            *next_expire = pk->expiredate;
 
-void
-list_trust_path( const char *username )
-{
-    int rc;
-    ulong lid;
-    TRUSTREC rec;
-    TN tree;
-    PKT_public_key *pk = gcry_xcalloc( 1, sizeof *pk );
-
-    init_trustdb();
-    if( (rc = get_pubkey_byname(NULL, pk, username, NULL )) )
-       log_error(_("user '%s' not found: %s\n"), username, gpg_errstr(rc) );
-    else if( (rc=tdbio_search_dir_bypk( pk, &rec )) && rc != -1 )
-       log_error(_("problem finding '%s' in trustdb: %s\n"),
-                                           username, gpg_errstr(rc));
-    else if( rc == -1 ) {
-       log_info(_("user '%s' not in trustdb - inserting\n"), username);
-       rc = insert_trust_record_by_pk( pk );
-       if( rc )
-           log_error(_("failed to put '%s' into trustdb: %s\n"),
-                                                   username, gpg_errstr(rc));
-       else {
-           assert( pk->local_id );
-       }
-    }
-    lid = pk->local_id;
-
-    tree = build_cert_tree( lid, 0, opt.max_cert_depth, NULL );
-    if( tree )
-       propagate_validity( tree, tree, NULL, NULL );
-    if( opt.with_colons )
-       dump_tn_tree_with_colons( 0, tree );
-    else
-       dump_tn_tree( stdout, 0, tree );
-    /*printf("(alloced tns=%d  max=%d)\n", alloced_tns, max_alloced_tns );*/
-    release_tn_tree( tree );
-    /*printf("Ownertrust=%c Validity=%c\n", get_ownertrust_info( lid ),
-                                         query_trust_info( pk, NULL ) ); */
-
-    free_public_key( pk );
+          if (nkeys == maxkeys) {
+            maxkeys += 1000;
+            keys = xrealloc (keys, (maxkeys+1) * sizeof *keys);
+          }
+          keys[nkeys++].keyblock = keyblock;
 
-}
+         /* Optimization - if all uids are fully trusted, then we
+            never need to consider this key as a candidate again. */
 
+         for (node=keyblock; node; node = node->next)
+           if (node->pkt->pkttype == PKT_USER_ID && !(node->flag & 4))
+             break;
 
+         if(node==NULL)
+           mark_keyblock_seen (full_trust, keyblock);
 
+          keyblock = NULL;
+        }
 
-/****************
- * Enumerate all keys, which are needed to build all trust paths for
- * the given key.  This function does not return the key itself or
- * the ultimate key (the last point in cerificate chain).  Only
- * certificate chains which ends up at an ultimately trusted key
- * are listed. If ownertrust or validity is not NULL, the corresponding
- * value for the returned LID is also returned in these variable(s).
- *
- *  1) create a void pointer and initialize it to NULL
- *  2) pass this void pointer by reference to this function.
- *     Set lid to the key you want to enumerate and pass it by reference.
- *  3) call this function as long as it does not return -1
- *     to indicate EOF. LID does contain the next key used to build the web
- *  4) Always call this function a last time with LID set to NULL,
- *     so that it can free its context.
- *
- * Returns: -1 on EOF or the level of the returned LID
- */
-int
-enum_cert_paths( void **context, ulong *lid,
-                unsigned *ownertrust, unsigned *validity )
-{
-    return -1;
-  #if 0
-    struct enum_cert_paths_ctx *ctx;
-    fixme: .....   tsl;
-
-    init_trustdb();
-    if( !lid ) {  /* release the context */
-       if( *context ) {
-           FIXME: ........tsl2;
-
-           ctx = *context;
-           for(tsl = ctx->tsl_head; tsl; tsl = tsl2 ) {
-               tsl2 = tsl->next;
-               gcry_free( tsl );
-           }
-           *context = NULL;
-       }
-       return -1;
+      release_kbnode (keyblock);
+      keyblock = NULL;
     }
+  while (!(rc = keydb_search (hd, &desc, 1, NULL)));
 
-    if( !*context ) {
-       FIXME .... *tmppath;
-       TRUSTREC rec;
-
-       if( !*lid )
-           return -1;
-
-       ctx = gcry_xcalloc( 1, sizeof *ctx );
-       *context = ctx;
-       /* collect the paths */
-      #if 0
-       read_record( *lid, &rec, RECTYPE_DIR );
-       tmppath = gcry_xcalloc( 1, (opt.max_cert_depth+1)* sizeof *tmppath );
-       tsl = NULL;
-       collect_paths( 0, opt.max_cert_depth, 1, &rec, tmppath, &tsl );
-       gcry_free( tmppath );
-       sort_tsl_list( &tsl );
-      #endif
-       /* setup the context */
-       ctx->tsl_head = tsl;
-       ctx->tsl = ctx->tsl_head;
-       ctx->idx = 0;
+  if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
+    {
+      log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc));
+      goto die;
     }
-    else
-       ctx = *context;
 
-    while( ctx->tsl && ctx->idx >= ctx->tsl->pathlen ) {
-       ctx->tsl = ctx->tsl->next;
-       ctx->idx = 0;
-    }
-    tsl = ctx->tsl;
-    if( !tsl )
-       return -1; /* eof */
-
-    if( ownertrust )
-       *ownertrust = tsl->path[ctx->idx].otrust;
-    if( validity )
-       *validity = tsl->path[ctx->idx].trust;
-    *lid = tsl->path[ctx->idx].lid;
-    ctx->idx++;
-    return ctx->idx-1;
-  #endif
-}
+  keys[nkeys].keyblock = NULL;
+  return keys;
 
+ die:
+  keys[nkeys].keyblock = NULL;
+  release_key_array (keys);
+  return NULL;
+}
 
-/****************
- * Print the current path
- */
-void
-enum_cert_paths_print( void **context, FILE *fp,
-                                      int refresh, ulong selected_lid )
-{
-    return;
-  #if 0
-    struct enum_cert_paths_ctx *ctx;
-    FIXME......... tsl;
-
-    if( !*context )
-       return;
-    init_trustdb();
-    ctx = *context;
-    if( !ctx->tsl )
-       return;
-    tsl = ctx->tsl;
-
-    if( !fp )
-       fp = stderr;
-
-    if( refresh ) { /* update the ownertrust and if possible the validity */
-       int i;
-       int match = tdbio_db_matches_options();
-
-       for( i = 0; i < tsl->pathlen; i++ )  {
-           TRUSTREC rec;
-
-           read_record( tsl->path[i].lid, &rec, RECTYPE_DIR );
-           tsl->path[i].otrust = rec.r.dir.ownertrust;
-           /* update validity only if we have it in the cache
-            * calculation is too time consuming */
-           if( match && rec.r.dir.valcheck && rec.r.dir.validity ) {
-               tsl->path[i].trust = rec.r.dir.validity;
-               if( rec.r.dir.dirflags & DIRF_REVOKED )
-                   tsl->path[i].trust = TRUST_FLAG_REVOKED;
+/* Caller must sync */
+static void
+reset_trust_records (ctrl_t ctrl)
+{
+  TRUSTREC rec;
+  ulong recnum;
+  int count = 0, nreset = 0;
+
+  for (recnum=1; !tdbio_read_record (recnum, &rec, 0); recnum++ )
+    {
+      if(rec.rectype==RECTYPE_TRUST)
+       {
+         count++;
+         if(rec.r.trust.min_ownertrust)
+           {
+             rec.r.trust.min_ownertrust=0;
+             write_record (ctrl, &rec);
            }
+
+       }
+      else if(rec.rectype==RECTYPE_VALID
+             && ((rec.r.valid.validity&TRUST_MASK)
+                 || rec.r.valid.marginal_count
+                 || rec.r.valid.full_count))
+       {
+         rec.r.valid.validity &= ~TRUST_MASK;
+         rec.r.valid.marginal_count=rec.r.valid.full_count=0;
+         nreset++;
+         write_record (ctrl, &rec);
        }
+
     }
 
-    print_path( tsl->pathlen, tsl->path, fp, selected_lid );
-  #endif
+  if (opt.verbose)
+    {
+      log_info (ngettext("%d key processed",
+                         "%d keys processed",
+                         count), count);
+      log_printf (ngettext(" (%d validity count cleared)\n",
+                           " (%d validity counts cleared)\n",
+                           nreset), nreset);
+    }
 }
 
-
 /*
- * Return an allocated buffer with the preference values for
- * the key with LID and the userid which is identified by the
- * HAMEHASH or the first one if namehash is NULL.  ret_n receives
- * the length of the allocated buffer. Structure of the buffer is
- * a repeated sequences of 2 bytes; where the first byte describes the
- * type of the preference and the second one the value.  The constants
- * PREFTYPE_xxxx should be used to reference a type.
+ * Run the key validation procedure.
+ *
+ * This works this way:
+ * Step 1: Find all ultimately trusted keys (UTK).
+ *         mark them all as seen and put them into klist.
+ * Step 2: loop max_cert_times
+ * Step 3:   if OWNERTRUST of any key in klist is undefined
+ *             ask user to assign ownertrust
+ * Step 4:   Loop over all keys in the keyDB which are not marked seen
+ * Step 5:     if key is revoked or expired
+ *                mark key as seen
+ *                continue loop at Step 4
+ * Step 6:     For each user ID of that key signed by a key in klist
+ *                Calculate validity by counting trusted signatures.
+ *                Set validity of user ID
+ * Step 7:     If any signed user ID was found
+ *                mark key as seen
+ *             End Loop
+ * Step 8:   Build a new klist from all fully trusted keys from step 6
+ *           End Loop
+ *         Ready
+ *
  */
-byte *
-get_pref_data( ulong lid, const byte *namehash, size_t *ret_n )
-{
-    TRUSTREC rec;
-    ulong recno;
-
-    init_trustdb();
-    read_record( lid, &rec, RECTYPE_DIR );
-    for( recno=rec.r.dir.uidlist; recno; recno = rec.r.uid.next ) {
-       read_record( recno, &rec, RECTYPE_UID );
-       if( rec.r.uid.prefrec
-           && ( !namehash || !memcmp(namehash, rec.r.uid.namehash, 20) ))  {
-           byte *buf;
-           /* found the correct one or the first one */
-           read_record( rec.r.uid.prefrec, &rec, RECTYPE_PREF );
-           if( rec.r.pref.next )
-               log_info(_("WARNING: can't yet handle long pref records\n"));
-           buf = gcry_xmalloc( ITEMS_PER_PREF_RECORD );
-           memcpy( buf, rec.r.pref.data, ITEMS_PER_PREF_RECORD );
-           *ret_n = ITEMS_PER_PREF_RECORD;
-           return buf;
-       }
-    }
-    return NULL;
-}
+static int
+validate_keys (ctrl_t ctrl, int interactive)
+{
+  int rc = 0;
+  int quit=0;
+  struct key_item *klist = NULL;
+  struct key_item *k;
+  struct key_array *keys = NULL;
+  struct key_array *kar;
+  KEYDB_HANDLE kdb = NULL;
+  KBNODE node;
+  int depth;
+  int ot_unknown, ot_undefined, ot_never, ot_marginal, ot_full, ot_ultimate;
+  KeyHashTable stored,used,full_trust;
+  u32 start_time, next_expire;
+
+  /* Make sure we have all sigs cached.  TODO: This is going to
+     require some architectural re-thinking, as it is agonizingly slow.
+     Perhaps combine this with reset_trust_records(), or only check
+     the caches on keys that are actually involved in the web of
+     trust. */
+  keydb_rebuild_caches (ctrl, 0);
+
+  kdb = keydb_new ();
+  if (!kdb)
+    return gpg_error_from_syserror ();
+
+  start_time = make_timestamp ();
+  next_expire = 0xffffffff; /* set next expire to the year 2106 */
+  stored = new_key_hash_table ();
+  used = new_key_hash_table ();
+  full_trust = new_key_hash_table ();
+
+  reset_trust_records (ctrl);
+
+  /* Fixme: Instead of always building a UTK list, we could just build it
+   * here when needed */
+  if (!utk_list)
+    {
+      if (!opt.quiet)
+        log_info (_("no ultimately trusted keys found\n"));
+      goto leave;
+    }
+
+  /* mark all UTKs as used and fully_trusted and set validity to
+     ultimate */
+  for (k=utk_list; k; k = k->next)
+    {
+      KBNODE keyblock;
+      PKT_public_key *pk;
+
+      keyblock = get_pubkeyblock (ctrl, k->kid);
+      if (!keyblock)
+        {
+          log_error (_("public key of ultimately"
+                       " trusted key %s not found\n"), keystr(k->kid));
+          continue;
+        }
+      mark_keyblock_seen (used, keyblock);
+      mark_keyblock_seen (stored, keyblock);
+      mark_keyblock_seen (full_trust, keyblock);
+      pk = keyblock->pkt->pkt.public_key;
+      for (node=keyblock; node; node = node->next)
+        {
+          if (node->pkt->pkttype == PKT_USER_ID)
+           update_validity (ctrl, pk, node->pkt->pkt.user_id,
+                             0, TRUST_ULTIMATE);
+        }
+      if ( pk->expiredate && pk->expiredate >= start_time
+           && pk->expiredate < next_expire)
+        next_expire = pk->expiredate;
+
+      release_kbnode (keyblock);
+      do_sync ();
+    }
+
+  if (opt.trust_model == TM_TOFU)
+    /* In the TOFU trust model, we only need to save the ultimately
+       trusted keys.  */
+    goto leave;
+
+  klist = utk_list;
+
+  if (!opt.quiet)
+    log_info ("marginals needed: %d  completes needed: %d  trust model: %s\n",
+              opt.marginals_needed, opt.completes_needed,
+              trust_model_string (opt.trust_model));
+
+  for (depth=0; depth < opt.max_cert_depth; depth++)
+    {
+      int valids=0,key_count;
+      /* See whether we should assign ownertrust values to the keys in
+         klist.  */
+      ot_unknown = ot_undefined = ot_never = 0;
+      ot_marginal = ot_full = ot_ultimate = 0;
+      for (k=klist; k; k = k->next)
+        {
+         int min=0;
+
+         /* 120 and 60 are as per RFC2440 */
+         if(k->trust_value>=120)
+           min=TRUST_FULLY;
+         else if(k->trust_value>=60)
+           min=TRUST_MARGINAL;
+
+         if(min!=k->min_ownertrust)
+           update_min_ownertrust (ctrl, k->kid,min);
+
+          if (interactive && k->ownertrust == TRUST_UNKNOWN)
+           {
+             k->ownertrust = ask_ownertrust (ctrl, k->kid,min);
+
+             if (k->ownertrust == (unsigned int)(-1))
+               {
+                 quit=1;
+                 goto leave;
+               }
+           }
 
+         /* This can happen during transition from an old trustdb
+            before trust sigs.  It can also happen if a user uses two
+            different versions of GnuPG or changes the --trust-model
+            setting. */
+         if(k->ownertrust<min)
+           {
+             if(DBG_TRUST)
+               log_debug("key %08lX%08lX:"
+                         " overriding ownertrust '%s' with '%s'\n",
+                         (ulong)k->kid[0],(ulong)k->kid[1],
+                         trust_value_to_string(k->ownertrust),
+                         trust_value_to_string(min));
+
+             k->ownertrust=min;
+           }
 
+         if (k->ownertrust == TRUST_UNKNOWN)
+            ot_unknown++;
+          else if (k->ownertrust == TRUST_UNDEFINED)
+            ot_undefined++;
+          else if (k->ownertrust == TRUST_NEVER)
+            ot_never++;
+          else if (k->ownertrust == TRUST_MARGINAL)
+            ot_marginal++;
+          else if (k->ownertrust == TRUST_FULLY)
+            ot_full++;
+          else if (k->ownertrust == TRUST_ULTIMATE)
+            ot_ultimate++;
+
+         valids++;
+        }
 
-/****************
- * Check whether the algorithm is in one of the pref records
- */
-int
-is_algo_in_prefs( ulong lid, int preftype, int algo )
-{
-    TRUSTREC rec;
-    ulong recno;
-    int i;
-    byte *pref;
-
-    init_trustdb();
-    read_record( lid, &rec, RECTYPE_DIR );
-    for( recno=rec.r.dir.uidlist; recno; recno = rec.r.uid.next ) {
-       read_record( recno, &rec, RECTYPE_UID );
-       if( rec.r.uid.prefrec ) {
-           read_record( rec.r.uid.prefrec, &rec, RECTYPE_PREF );
-           if( rec.r.pref.next )
-               log_info(_("WARNING: can't yet handle long pref records\n"));
-           pref = rec.r.pref.data;
-           for(i=0; i+1 < ITEMS_PER_PREF_RECORD; i+=2 ) {
-               if( pref[i] == preftype && pref[i+1] == algo )
-                   return 1;
+      /* Find all keys which are signed by a key in kdlist */
+      keys = validate_key_list (ctrl, kdb, full_trust, klist,
+                               start_time, &next_expire);
+      if (!keys)
+        {
+          log_error ("validate_key_list failed\n");
+          rc = GPG_ERR_GENERAL;
+          goto leave;
+        }
+
+      for (key_count=0, kar=keys; kar->keyblock; kar++, key_count++)
+        ;
+
+      /* Store the calculated valididation status somewhere */
+      if (opt.verbose > 1 && DBG_TRUST)
+        dump_key_array (depth, keys);
+
+      for (kar=keys; kar->keyblock; kar++)
+        store_validation_status (ctrl, depth, kar->keyblock, stored);
+
+      if (!opt.quiet)
+        log_info (_("depth: %d  valid: %3d  signed: %3d"
+                    "  trust: %d-, %dq, %dn, %dm, %df, %du\n"),
+                  depth, valids, key_count, ot_unknown, ot_undefined,
+                  ot_never, ot_marginal, ot_full, ot_ultimate );
+
+      /* Build a new kdlist from all fully valid keys in KEYS */
+      if (klist != utk_list)
+        release_key_items (klist);
+      klist = NULL;
+      for (kar=keys; kar->keyblock; kar++)
+        {
+          for (node=kar->keyblock; node; node = node->next)
+            {
+              if (node->pkt->pkttype == PKT_USER_ID && (node->flag & 4))
+                {
+                 u32 kid[2];
+
+                 /* have we used this key already? */
+                  keyid_from_pk (kar->keyblock->pkt->pkt.public_key, kid);
+                 if(test_key_hash_table(used,kid)==0)
+                   {
+                     /* Normally we add both the primary and subkey
+                        ids to the hash via mark_keyblock_seen, but
+                        since we aren't using this hash as a skipfnc,
+                        that doesn't matter here. */
+                     add_key_hash_table (used,kid);
+                     k = new_key_item ();
+                     k->kid[0]=kid[0];
+                     k->kid[1]=kid[1];
+                     k->ownertrust =
+                       (tdb_get_ownertrust
+                           (ctrl, kar->keyblock->pkt->pkt.public_key, 0)
+                         & TRUST_MASK);
+                     k->min_ownertrust = tdb_get_min_ownertrust
+                        (ctrl, kar->keyblock->pkt->pkt.public_key, 0);
+                     k->trust_depth=
+                       kar->keyblock->pkt->pkt.public_key->trust_depth;
+                     k->trust_value=
+                       kar->keyblock->pkt->pkt.public_key->trust_value;
+                     if(kar->keyblock->pkt->pkt.public_key->trust_regexp)
+                       k->trust_regexp=
+                         xstrdup(kar->keyblock->pkt->
+                                  pkt.public_key->trust_regexp);
+                     k->next = klist;
+                     klist = k;
+                     break;
+                   }
+               }
            }
        }
+      release_key_array (keys);
+      keys = NULL;
+      if (!klist)
+        break; /* no need to dive in deeper */
+    }
+
+ leave:
+  keydb_release (kdb);
+  release_key_array (keys);
+  if (klist != utk_list)
+    release_key_items (klist);
+  release_key_hash_table (full_trust);
+  release_key_hash_table (used);
+  release_key_hash_table (stored);
+  if (!rc && !quit) /* mark trustDB as checked */
+    {
+      int rc2;
+
+      if (next_expire == 0xffffffff || next_expire < start_time )
+        tdbio_write_nextcheck (ctrl, 0);
+      else
+        {
+          tdbio_write_nextcheck (ctrl, next_expire);
+          if (!opt.quiet)
+            log_info (_("next trustdb check due at %s\n"),
+                      strtimestamp (next_expire));
+        }
+
+      rc2 = tdbio_update_version_record (ctrl);
+      if (rc2)
+       {
+         log_error (_("unable to update trustdb version record: "
+                       "write failed: %s\n"), gpg_strerror (rc2));
+         tdbio_invalid ();
+       }
+
+      do_sync ();
+      pending_check_trustdb = 0;
     }
-    return 0;
-}
 
+  return rc;
+}