gpg: default to AES-256.
[gnupg.git] / g10 / pkclist.c
index b11cda5..67d932e 100644 (file)
@@ -1,12 +1,12 @@
-/* pkclist.c
- * Copyright (C) 1998, 1999, 2000, 2001, 2002
- *               2003 Free Software Foundation, Inc.
+/* pkclist.c - create a list of public keys
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+ *               2008, 2009, 2010 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,
@@ -15,8 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 
+#include "gpg.h"
 #include "options.h"
 #include "packet.h"
-#include "errors.h"
+#include "../common/status.h"
 #include "keydb.h"
-#include "memory.h"
-#include "util.h"
+#include "../common/util.h"
 #include "main.h"
 #include "trustdb.h"
-#include "ttyio.h"
-#include "status.h"
+#include "../common/ttyio.h"
+#include "../common/status.h"
 #include "photoid.h"
-#include "i18n.h"
-
+#include "../common/i18n.h"
+#include "tofu.h"
 
 #define CONTROL_D ('D' - 'A' + 1)
 
+static void
+send_status_inv_recp (int reason, const char *name)
+{
+  char buf[40];
+
+  snprintf (buf, sizeof buf, "%d ", reason);
+  write_status_text_and_buffer (STATUS_INV_RECP, buf,
+                                name, strlen (name),
+                                -1);
+}
+
 
 /****************
  * Show the revocation reason as it is stored with the given signature
@@ -72,12 +81,11 @@ do_show_revocation_reason( PKT_signature *sig )
        else
            text = NULL;
 
-       log_info( _("reason for revocation: ") );
-       if( text )
-           fputs( text, log_stream() );
+       log_info ( _("reason for revocation: "));
+       if (text)
+          log_printf ("%s\n", text);
        else
-           fprintf( log_stream(), "code=%02x", *p );
-       putc( '\n', log_stream() );
+          log_printf ("code=%02x\n", *p );
        n--; p++;
        pp = NULL;
        do {
@@ -89,9 +97,9 @@ do_show_revocation_reason( PKT_signature *sig )
            if( n ) {
                pp = memchr( p, '\n', n );
                nn = pp? pp - p : n;
-               log_info( _("revocation comment: ") );
-               print_string( log_stream(), p, nn, 0 );
-               putc( '\n', log_stream() );
+               log_info ( _("revocation comment: ") );
+               es_write_sanitized (log_get_stream(), p, nn, NULL, NULL);
+               log_printf ("\n");
                p += nn; n -= nn;
            }
        } while( pp );
@@ -102,9 +110,9 @@ do_show_revocation_reason( PKT_signature *sig )
    subkeys, etc.)  Mode 1: use only the revocation on the main pk */
 
 void
-show_revocation_reason( PKT_public_key *pk, int mode )
+show_revocation_reason (ctrl_t ctrl, PKT_public_key *pk, int mode)
 {
-    /* Hmmm, this is not so easy becuase we have to duplicate the code
+    /* Hmmm, this is not so easy because we have to duplicate the code
      * used in the trustbd to calculate the keyflags.  We need to find
      * a clean way to check revocation certificates on keys and
      * signatures.  And there should be no duplicate code.  Because we
@@ -119,7 +127,7 @@ show_revocation_reason( PKT_public_key *pk, int mode )
 
     /* get the keyblock */
     fingerprint_from_pk( pk, fingerprint, &fingerlen );
-    rc = get_keyblock_byfprint( &keyblock, fingerprint, fingerlen );
+    rc = get_pubkey_byfprint (ctrl, NULL, &keyblock, fingerprint, fingerlen);
     if( rc ) { /* that should never happen */
        log_debug( "failed to get the keyblock\n");
        return;
@@ -152,107 +160,39 @@ show_revocation_reason( PKT_public_key *pk, int mode )
 
     /* We didn't find it, so check if the whole key is revoked */
     if(!node && !mode)
-      show_revocation_reason(pk,1);
+      show_revocation_reason (ctrl, pk, 1);
 
     release_kbnode( keyblock );
 }
 
 
-static void
-show_paths (const PKT_public_key *pk, int only_first )
-{
-    log_debug("not yet implemented\n");
-#if 0    
-    void *context = NULL;
-    unsigned otrust, validity;
-    int last_level, level;
-
-    last_level = 0;
-    while( (level=enum_cert_paths( &context, &lid, &otrust, &validity)) != -1){
-       char *p;
-       int c, rc;
-       size_t n;
-       u32 keyid[2];
-       PKT_public_key *pk ;
-
-       if( level < last_level && only_first )
-           break;
-       last_level = level;
-
-       rc = keyid_from_lid( lid, keyid );
-
-       if( rc ) {
-           log_error("ooops: can't get keyid for lid %lu\n", lid);
-           return;
-       }
-
-       pk = m_alloc_clear( sizeof *pk );
-       rc = get_pubkey( pk, keyid );
-       if( rc ) {
-           log_error("key %08lX: public key not found: %s\n",
-                                   (ulong)keyid[1], g10_errstr(rc) );
-           return;
-       }
-
-       tty_printf("%*s%4u%c/%08lX.%lu %s \"",
-                 level*2, "",
-                 nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ),
-                 (ulong)keyid[1], lid, datestr_from_pk( pk ) );
-
-       c = trust_letter(otrust);
-       if( c )
-           putchar( c );
-       else
-           printf( "%02x", otrust );
-       putchar('/');
-       c = trust_letter(validity);
-       if( c )
-           putchar( c );
-       else
-           printf( "%02x", validity );
-       putchar(' ');
-
-       p = get_user_id( keyid, &n );
-       tty_print_utf8_string( p, n ),
-       m_free(p);
-       tty_printf("\"\n");
-       free_public_key( pk );
-    }
-    enum_cert_paths( &context, NULL, NULL, NULL ); /* release context */
-#endif
-    tty_printf("\n");
-}
-
-
-
-
 /****************
  * mode: 0 = standard
  *       1 = Without key info and additional menu option 'm'
  *           this does also add an option to set the key to ultimately trusted.
- * Returns: 
+ * Returns:
  *      -2 = nothing changed - caller should show some additional info
  *      -1 = quit operation
  *       0 = nothing changed
  *       1 = new ownertrust now in new_trust
  */
+#ifndef NO_TRUST_MODELS
 static int
-do_edit_ownertrust (PKT_public_key *pk, int mode,
+do_edit_ownertrust (ctrl_t ctrl, PKT_public_key *pk, int mode,
                     unsigned *new_trust, int defer_help )
 {
   char *p;
-  size_t n;
   u32 keyid[2];
   int changed=0;
   int quit=0;
   int show=0;
   int min_num;
   int did_help=defer_help;
-  unsigned int minimum=get_min_ownertrust(pk);
+  unsigned int minimum = tdb_get_min_ownertrust (ctrl, pk, 0);
 
   switch(minimum)
     {
-    default:              min_num=0; break;
+    default:
     case TRUST_UNDEFINED: min_num=1; break;
     case TRUST_NEVER:     min_num=2; break;
     case TRUST_MARGINAL:  min_num=3; break;
@@ -261,78 +201,103 @@ do_edit_ownertrust (PKT_public_key *pk, int mode,
 
   keyid_from_pk (pk, keyid);
   for(;;) {
-    /* a string with valid answers */
+    /* A string with valid answers.
+
+       TRANSLATORS: These are the allowed answers in lower and
+       uppercase.  Below you will find the matching strings which
+       should be translated accordingly and the letter changed to
+       match the one in the answer string.
+
+         i = please show me more information
+         m = back to the main menu
+         s = skip this key
+        q = quit
+    */
     const char *ans = _("iImMqQsS");
 
-    if( !did_help ) 
+    if( !did_help )
       {
-        if( !mode ) 
+        if( !mode )
           {
             KBNODE keyblock, un;
 
-            tty_printf(_("No trust value assigned to:\n"
-                         "%4u%c/%08lX %s \""),
-                       nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ),
-                       (ulong)keyid[1], datestr_from_pk( pk ) );
-            p = get_user_id( keyid, &n );
-            tty_print_utf8_string( p, n ),
-              m_free(p);
-            tty_printf("\"\n");
+            tty_printf (_("No trust value assigned to:\n"));
+            print_key_line (ctrl, NULL, pk, 0);
 
-            keyblock = get_pubkeyblock (keyid);
+           p = get_user_id_native (ctrl, keyid);
+           tty_printf (_("      \"%s\"\n"),p);
+           xfree (p);
+
+            keyblock = get_pubkeyblock (ctrl, keyid);
             if (!keyblock)
                 BUG ();
-            for (un=keyblock; un; un = un->next) {
+            for (un=keyblock; un; un = un->next)
+             {
                 if (un->pkt->pkttype != PKT_USER_ID )
-                    continue;
-                if (un->pkt->pkt.user_id->is_revoked )
-                    continue;
-                if (un->pkt->pkt.user_id->is_expired )
-                    continue;
+                 continue;
+                if (un->pkt->pkt.user_id->flags.revoked)
+                 continue;
+                if (un->pkt->pkt.user_id->flags.expired)
+                 continue;
                /* Only skip textual primaries */
-                if (un->pkt->pkt.user_id->is_primary &&
-                   !un->pkt->pkt.user_id->attrib_data )
-                   continue;
-                
+                if (un->pkt->pkt.user_id->flags.primary
+                   && !un->pkt->pkt.user_id->attrib_data )
+                 continue;
+
                if((opt.verify_options&VERIFY_SHOW_PHOTOS)
                   && un->pkt->pkt.user_id->attrib_data)
-                    show_photos(un->pkt->pkt.user_id->attribs,
-                                un->pkt->pkt.user_id->numattribs,pk,NULL);
-                
-               tty_printf ("      %s", _("                aka \""));
-                tty_print_utf8_string (un->pkt->pkt.user_id->name,
-                                       un->pkt->pkt.user_id->len );
-                tty_printf("\"\n");
-            }
-        
-            print_fingerprint (pk, NULL, 2);
+                 show_photos (ctrl,
+                               un->pkt->pkt.user_id->attribs,
+                               un->pkt->pkt.user_id->numattribs, pk,
+                               un->pkt->pkt.user_id);
+
+               p=utf8_to_native(un->pkt->pkt.user_id->name,
+                                un->pkt->pkt.user_id->len,0);
+
+               tty_printf(_("  aka \"%s\"\n"),p);
+             }
+
+            print_fingerprint (ctrl, NULL, pk, 2);
             tty_printf("\n");
+           release_kbnode (keyblock);
           }
-       /* This string also used in keyedit.c:sign_uids */
-        tty_printf (_(
-                     "Please decide how far you trust this user to correctly\n"
-                     "verify other users' keys (by looking at passports,\n"
-                     "checking fingerprints from different sources...)?\n\n"));
+
+       if(opt.trust_model==TM_DIRECT)
+         {
+           tty_printf(_("How much do you trust that this key actually "
+                        "belongs to the named user?\n"));
+           tty_printf("\n");
+         }
+       else
+         {
+           /* This string also used in keyedit.c:trustsig_prompt */
+           tty_printf(_("Please decide how far you trust this user to"
+                        " correctly verify other users' keys\n"
+                        "(by looking at passports, checking fingerprints from"
+                        " different sources, etc.)\n"));
+           tty_printf("\n");
+         }
+
        if(min_num<=1)
-         tty_printf (_(" %d = I don't know or won't say\n"), 1);
+         tty_printf (_("  %d = I don't know or won't say\n"), 1);
        if(min_num<=2)
-         tty_printf (_(" %d = I do NOT trust\n"), 2);
+         tty_printf (_("  %d = I do NOT trust\n"), 2);
        if(min_num<=3)
-         tty_printf (_(" %d = I trust marginally\n"), 3);
+         tty_printf (_("  %d = I trust marginally\n"), 3);
        if(min_num<=4)
-         tty_printf (_(" %d = I trust fully\n"), 4);
+         tty_printf (_("  %d = I trust fully\n"), 4);
         if (mode)
-          tty_printf (_(" %d = I trust ultimately\n"), 5);
+          tty_printf (_("  %d = I trust ultimately\n"), 5);
 #if 0
        /* not yet implemented */
-        tty_printf (_(" i = please show me more information\n") );
+        tty_printf ("  i = please show me more information\n");
 #endif
         if( mode )
-          tty_printf(_(" m = back to the main menu\n"));
+          tty_printf(_("  m = back to the main menu\n"));
         else
          {
-           tty_printf(_(" s = skip this key\n"));
-           tty_printf(_(" q = quit\n"));
+           tty_printf(_("  s = skip this key\n"));
+           tty_printf(_("  q = quit\n"));
          }
         tty_printf("\n");
        if(minimum)
@@ -349,7 +314,7 @@ do_edit_ownertrust (PKT_public_key *pk, int mode,
       did_help = 0;
     else if( *p && p[1] )
       ;
-    else if( !p[1] && ((*p >= '0'+min_num) && *p <= (mode?'5':'4')) ) 
+    else if( !p[1] && ((*p >= '0'+min_num) && *p <= (mode?'5':'4')) )
       {
         unsigned int trust;
         switch( *p )
@@ -364,7 +329,7 @@ do_edit_ownertrust (PKT_public_key *pk, int mode,
         if (trust == TRUST_ULTIMATE
             && !cpr_get_answer_is_yes ("edit_ownertrust.set_ultimate.okay",
                                        _("Do you really want to set this key"
-                                         " to ultimate trust? ")))
+                                         " to ultimate trust? (y/N) ")))
           ; /* no */
         else
           {
@@ -375,14 +340,14 @@ do_edit_ownertrust (PKT_public_key *pk, int mode,
       }
 #if 0
     /* not yet implemented */
-    else if( *p == ans[0] || *p == ans[1] ) 
+    else if( *p == ans[0] || *p == ans[1] )
       {
         tty_printf(_("Certificates leading to an ultimately trusted key:\n"));
         show = 1;
         break;
       }
 #endif
-    else if( mode && (*p == ans[2] || *p == ans[3] || *p == CONTROL_D ) ) 
+    else if( mode && (*p == ans[2] || *p == ans[3] || *p == CONTROL_D ) )
       {
         break ; /* back to the menu */
       }
@@ -395,43 +360,46 @@ do_edit_ownertrust (PKT_public_key *pk, int mode,
         quit = 1;
         break ; /* back to the menu */
       }
-    m_free(p); p = NULL;
+    xfree(p); p = NULL;
   }
-  m_free(p);
+  xfree(p);
   return show? -2: quit? -1 : changed;
 }
+#endif /*!NO_TRUST_MODELS*/
 
-/* 
+
+/*
  * Display a menu to change the ownertrust of the key PK (which should
- * be a primary key).  
+ * be a primary key).
  * For mode values see do_edit_ownertrust ()
  */
+#ifndef NO_TRUST_MODELS
 int
-edit_ownertrust (PKT_public_key *pk, int mode )
+edit_ownertrust (ctrl_t ctrl, PKT_public_key *pk, int mode )
 {
-  unsigned int trust;
+  unsigned int trust = 0;
   int no_help = 0;
 
   for(;;)
     {
-      switch ( do_edit_ownertrust (pk, mode, &trust, no_help ) )
+      switch ( do_edit_ownertrust (ctrl, pk, mode, &trust, no_help ) )
         {
         case -1: /* quit */
           return -1;
         case -2: /* show info */
-          show_paths(pk, 1);
           no_help = 1;
           break;
         case 1: /* trust value set */
           trust &= ~TRUST_FLAG_DISABLED;
-          trust |= get_ownertrust (pk) & TRUST_FLAG_DISABLED;
-          update_ownertrust (pk, trust );
+          trust |= get_ownertrust (ctrl, pk) & TRUST_FLAG_DISABLED;
+          update_ownertrust (ctrl, pk, trust );
           return 1;
         default:
           return 0;
         }
     }
 }
+#endif /*!NO_TRUST_MODELS*/
 
 
 /****************
@@ -439,170 +407,152 @@ edit_ownertrust (PKT_public_key *pk, int mode )
  * Returns: true if we trust.
  */
 static int
-do_we_trust( PKT_public_key *pk, unsigned int *trustlevel )
+do_we_trust( PKT_public_key *pk, unsigned int trustlevel )
 {
-    unsigned int trustmask = 0;
-
-    /* FIXME: get_pubkey_byname already checks the validity and won't
-     * return keys which are either expired or revoked - so these
-     * question here won't get triggered.  We have to find a solution
-     * for this.  It might make sense to have a function in getkey.c
-     * which does only the basic checks and returns even revoked and
-     * expired keys.  This fnction could then also returhn a list of
-     * keys if the speicified name is ambiguous
-     */
-    if( (*trustlevel & TRUST_FLAG_REVOKED) ) {
-       log_info(_("key %08lX: key has been revoked!\n"),
-                                       (ulong)keyid_from_pk( pk, NULL) );
-       show_revocation_reason( pk, 0 );
-       if( opt.batch )
-          return 0; /* no */
-
-       if( !cpr_get_answer_is_yes("revoked_key.override",
-                                   _("Use this key anyway? ")) )
-          return 0; /* no */
-       trustmask |= TRUST_FLAG_REVOKED;
+  /* We should not be able to get here with a revoked or expired
+     key */
+  if(trustlevel & TRUST_FLAG_REVOKED
+     || trustlevel & TRUST_FLAG_SUB_REVOKED
+     || (trustlevel & TRUST_MASK) == TRUST_EXPIRED)
+    BUG();
+
+  if( opt.trust_model==TM_ALWAYS )
+    {
+      if( opt.verbose )
+       log_info("No trust check due to '--trust-model always' option\n");
+      return 1;
     }
-    if( (*trustlevel & TRUST_FLAG_SUB_REVOKED) ) {
-       log_info(_("key %08lX: subkey has been revoked!\n"),
-                                       (ulong)keyid_from_pk( pk, NULL) );
-       show_revocation_reason( pk, 0 );
-       if( opt.batch )
-           return 0;
 
-       if( !cpr_get_answer_is_yes("revoked_key.override",
-                                   _("Use this key anyway? ")) )
-           return 0;
-       trustmask |= TRUST_FLAG_SUB_REVOKED;
-    }
-    *trustlevel &= ~trustmask;
+  switch(trustlevel & TRUST_MASK)
+    {
+    default:
+      log_error ("invalid trustlevel %u returned from validation layer\n",
+                trustlevel);
+      /* fall through */
+    case TRUST_UNKNOWN:
+    case TRUST_UNDEFINED:
+      log_info(_("%s: There is no assurance this key belongs"
+                " to the named user\n"),keystr_from_pk(pk));
+      return 0; /* no */
 
-    if( opt.trust_model==TM_ALWAYS ) {
-       if( opt.verbose )
-           log_info("No trust check due to --trust-model always option\n");
-       return 1;
-    }
+    case TRUST_MARGINAL:
+      log_info(_("%s: There is limited assurance this key belongs"
+                " to the named user\n"),keystr_from_pk(pk));
+      return 1; /* yes */
 
-    switch( (*trustlevel & TRUST_MASK) ) {
-      case TRUST_EXPIRED:
-       log_info(_("%08lX: key has expired\n"),
-                                   (ulong)keyid_from_pk( pk, NULL) );
-       return 0; /* no */
-
-      default:
-         log_error ("invalid trustlevel %u returned from validation layer\n",
-                    *trustlevel);
-         /* fall thru */
-      case TRUST_UNKNOWN: 
-      case TRUST_UNDEFINED:
-        log_info(_("%08lX: There is no assurance this key belongs "
-                  "to the named user\n"),(ulong)keyid_from_pk( pk, NULL) );
-       return 0; /* no */
-
-       /* No way to get here? */
-      case TRUST_NEVER:
-       log_info(_("%08lX: We do NOT trust this key\n"),
-                                       (ulong)keyid_from_pk( pk, NULL) );
-       return 0; /* no */
-
-      case TRUST_MARGINAL:
-       log_info(_("%08lX: There is limited assurance this key belongs "
-                  "to the named user\n"),(ulong)keyid_from_pk(pk,NULL));
-       return 1; /* yes */
-
-      case TRUST_FULLY:
-       if( opt.verbose )
-           log_info(_("This key probably belongs to the named user\n"));
-       return 1; /* yes */
-
-      case TRUST_ULTIMATE:
-       if( opt.verbose )
-           log_info(_("This key belongs to us\n"));
-       return 1; /* yes */
+    case TRUST_FULLY:
+      if( opt.verbose )
+       log_info(_("This key probably belongs to the named user\n"));
+      return 1; /* yes */
+
+    case TRUST_ULTIMATE:
+      if( opt.verbose )
+       log_info(_("This key belongs to us\n"));
+      return 1; /* yes */
+
+    case TRUST_NEVER:
+      /* This can be returned by TOFU, which can return negative
+         assertions.  */
+      log_info(_("%s: This key is bad!  It has been marked as untrusted!\n"),
+               keystr_from_pk(pk));
+      return 0; /* no */
     }
 
-    return 1; /* yes */
+  return 1; /*NOTREACHED*/
 }
 
 
-
 /****************
  * wrapper around do_we_trust, so we can ask whether to use the
  * key anyway.
  */
 static int
-do_we_trust_pre( PKT_public_key *pk, unsigned int trustlevel )
+do_we_trust_pre (ctrl_t ctrl, PKT_public_key *pk, unsigned int trustlevel )
 {
-    int rc;
+  int rc;
 
-    rc = do_we_trust( pk, &trustlevel );
+  rc = do_we_trust( pk, trustlevel );
 
-    if( (trustlevel & TRUST_FLAG_REVOKED) && !rc )
-       return 0;
-    if( (trustlevel & TRUST_FLAG_SUB_REVOKED) && !rc )
-       return 0;
+  if( !opt.batch && !rc )
+    {
+      print_pubkey_info (ctrl, NULL,pk);
+      print_fingerprint (ctrl, NULL, pk, 2);
+      tty_printf("\n");
 
-    if( !opt.batch && !rc ) {
-       u32 keyid[2];
-
-       keyid_from_pk( pk, keyid);
-       tty_printf( "%4u%c/%08lX %s \"",
-                 nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ),
-                 (ulong)keyid[1], datestr_from_pk( pk ) );
-       /* If the pk was chosen by a particular user ID, this is the
-          one to ask about. */
-       if(pk->user_id)
-         tty_print_utf8_string(pk->user_id->name,pk->user_id->len);
-       else
-         {
-           size_t n;
-           char *p = get_user_id( keyid, &n );
-           tty_print_utf8_string( p, n );
-           m_free(p);
-         }
-       tty_printf("\"\n");
-        print_fingerprint (pk, NULL, 2);
-       tty_printf("\n");
-
-       tty_printf(_(
-"It is NOT certain that the key belongs to the person named\n"
-"in the user ID.  If you *really* know what you are doing,\n"
-"you may answer the next question with yes\n\n"));
-
-       if( cpr_get_answer_is_yes("untrusted_key.override",
-                                 _("Use this key anyway? "))  )
-           rc = 1;
-
-       /* Hmmm: Should we set a flag to tell the user about
-        *       his decision the next time he encrypts for this recipient?
-        */
-    }
-    else if( opt.trust_model==TM_ALWAYS && !rc ) {
-       if( !opt.quiet )
-           log_info(_("WARNING: Using untrusted key!\n"));
+      if ((trustlevel & TRUST_MASK) == TRUST_NEVER)
+        tty_printf(
+          _("This key is bad!  It has been marked as untrusted!  If you\n"
+            "*really* know what you are doing, you may answer the next\n"
+            "question with yes.\n"));
+      else
+        tty_printf(
+          _("It is NOT certain that the key belongs to the person named\n"
+            "in the user ID.  If you *really* know what you are doing,\n"
+            "you may answer the next question with yes.\n"));
+
+      tty_printf("\n");
+
+
+      if (is_status_enabled ())
+        {
+          u32 kid[2];
+          char *hint_str;
+
+          keyid_from_pk (pk, kid);
+          hint_str = get_long_user_id_string (ctrl, kid);
+          write_status_text ( STATUS_USERID_HINT, hint_str );
+          xfree (hint_str);
+        }
+
+      if( cpr_get_answer_is_yes("untrusted_key.override",
+                               _("Use this key anyway? (y/N) "))  )
        rc = 1;
+
+      /* Hmmm: Should we set a flag to tell the user about
+       *        his decision the next time he encrypts for this recipient?
+       */
     }
-    return rc;
+
+  return rc;
 }
 
 
+/* Write a TRUST_foo status line inclduing the validation model.  */
+static void
+write_trust_status (int statuscode, int trustlevel)
+{
+#ifdef NO_TRUST_MODELS
+  write_status (statuscode);
+#else /* NO_TRUST_MODELS */
+  int tm;
+
+  /* For the combined tofu+pgp method, we return the trust model which
+   * was responsible for the trustlevel.  */
+  if (opt.trust_model == TM_TOFU_PGP)
+    tm = (trustlevel & TRUST_FLAG_TOFU_BASED)? TM_TOFU : TM_PGP;
+  else
+    tm = opt.trust_model;
+  write_status_strings (statuscode, "0 ", trust_model_string (tm), NULL);
+#endif /* NO_TRUST_MODELS */
+}
+
 
 /****************
  * Check whether we can trust this signature.
- * Returns: Error if we shall not trust this signatures.
+ * Returns an error code if we should not trust this signature.
  */
 int
-check_signatures_trust( PKT_signature *sig )
+check_signatures_trust (ctrl_t ctrl, PKT_signature *sig)
 {
-  PKT_public_key *pk = m_alloc_clear( sizeof *pk );
-  unsigned int trustlevel;
+  PKT_public_key *pk = xmalloc_clear( sizeof *pk );
+  unsigned int trustlevel = TRUST_UNKNOWN;
   int rc=0;
 
-  rc = get_pubkey( pk, sig->keyid );
-  if (rc) 
+  rc = get_pubkey (ctrl, pk, sig->keyid );
+  if (rc)
     { /* this should not happen */
       log_error("Ooops; the key vanished  - can't check the trust\n");
-      rc = G10ERR_NO_PUBKEY;
+      rc = GPG_ERR_NO_PUBKEY;
       goto leave;
     }
 
@@ -611,79 +561,141 @@ check_signatures_trust( PKT_signature *sig )
       if( !opt.quiet )
         log_info(_("WARNING: Using untrusted key!\n"));
       if (opt.with_fingerprint)
-        print_fingerprint (pk, NULL, 1);
+        print_fingerprint (ctrl, NULL, pk, 1);
       goto leave;
     }
 
-  trustlevel = get_validity (pk, NULL);
+  if(pk->flags.maybe_revoked && !pk->flags.revoked)
+    log_info(_("WARNING: this key might be revoked (revocation key"
+              " not present)\n"));
 
-  if ( (trustlevel & TRUST_FLAG_REVOKED) ) 
+  trustlevel = get_validity (ctrl, NULL, pk, NULL, sig, 1);
+
+  if ( (trustlevel & TRUST_FLAG_REVOKED) )
     {
       write_status( STATUS_KEYREVOKED );
-      log_info(_("WARNING: This key has been revoked by its owner!\n"));
-      log_info(_("         This could mean that the signature is forgery.\n"));
-      show_revocation_reason( pk, 0 );
+      if(pk->flags.revoked == 2)
+       log_info(_("WARNING: This key has been revoked by its"
+                  " designated revoker!\n"));
+      else
+       log_info(_("WARNING: This key has been revoked by its owner!\n"));
+      log_info(_("         This could mean that the signature is forged.\n"));
+      show_revocation_reason (ctrl, pk, 0);
     }
-  else if ((trustlevel & TRUST_FLAG_SUB_REVOKED) ) 
+  else if ((trustlevel & TRUST_FLAG_SUB_REVOKED) )
     {
       write_status( STATUS_KEYREVOKED );
       log_info(_("WARNING: This subkey has been revoked by its owner!\n"));
-      show_revocation_reason( pk, 0 );
+      show_revocation_reason (ctrl, pk, 0);
     }
-  
+
   if ((trustlevel & TRUST_FLAG_DISABLED))
     log_info (_("Note: This key has been disabled.\n"));
 
-  switch ( (trustlevel & TRUST_MASK) ) 
+  /* If we have PKA information adjust the trustlevel. */
+  if (sig->pka_info && sig->pka_info->valid)
+    {
+      unsigned char fpr[MAX_FINGERPRINT_LEN];
+      PKT_public_key *primary_pk;
+      size_t fprlen;
+      int okay;
+
+
+      primary_pk = xmalloc_clear (sizeof *primary_pk);
+      get_pubkey (ctrl, primary_pk, pk->main_keyid);
+      fingerprint_from_pk (primary_pk, fpr, &fprlen);
+      free_public_key (primary_pk);
+
+      if ( fprlen == 20 && !memcmp (sig->pka_info->fpr, fpr, 20) )
+        {
+          okay = 1;
+          write_status_text (STATUS_PKA_TRUST_GOOD, sig->pka_info->email);
+          log_info (_("Note: Verified signer's address is '%s'\n"),
+                    sig->pka_info->email);
+        }
+      else
+        {
+          okay = 0;
+          write_status_text (STATUS_PKA_TRUST_BAD, sig->pka_info->email);
+          log_info (_("Note: Signer's address '%s' "
+                      "does not match DNS entry\n"), sig->pka_info->email);
+        }
+
+      switch ( (trustlevel & TRUST_MASK) )
+        {
+        case TRUST_UNKNOWN:
+        case TRUST_UNDEFINED:
+        case TRUST_MARGINAL:
+          if (okay && opt.verify_options&VERIFY_PKA_TRUST_INCREASE)
+            {
+              trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_FULLY);
+              log_info (_("trustlevel adjusted to FULL"
+                          " due to valid PKA info\n"));
+            }
+          /* fall through */
+        case TRUST_FULLY:
+          if (!okay)
+            {
+              trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_NEVER);
+              log_info (_("trustlevel adjusted to NEVER"
+                          " due to bad PKA info\n"));
+            }
+          break;
+        }
+    }
+
+  /* Now let the user know what up with the trustlevel. */
+  switch ( (trustlevel & TRUST_MASK) )
     {
     case TRUST_EXPIRED:
       log_info(_("Note: This key has expired!\n"));
-      print_fingerprint (pk, NULL, 1);
+      print_fingerprint (ctrl, NULL, pk, 1);
       break;
-        
+
     default:
       log_error ("invalid trustlevel %u returned from validation layer\n",
                  trustlevel);
-      /* fall thru */
-    case TRUST_UNKNOWN: 
+      /* fall through */
+    case TRUST_UNKNOWN:
     case TRUST_UNDEFINED:
-      write_status( STATUS_TRUST_UNDEFINED );
+      write_trust_status (STATUS_TRUST_UNDEFINED, trustlevel);
       log_info(_("WARNING: This key is not certified with"
                  " a trusted signature!\n"));
       log_info(_("         There is no indication that the "
                  "signature belongs to the owner.\n" ));
-      print_fingerprint (pk, NULL, 1);
+      print_fingerprint (ctrl, NULL, pk, 1);
       break;
 
     case TRUST_NEVER:
-      /* currently we won't get that status */
-      write_status( STATUS_TRUST_NEVER );
+      /* This level can be returned by TOFU, which supports negative
+       * assertions.  */
+      write_trust_status (STATUS_TRUST_NEVER, trustlevel);
       log_info(_("WARNING: We do NOT trust this key!\n"));
       log_info(_("         The signature is probably a FORGERY.\n"));
       if (opt.with_fingerprint)
-        print_fingerprint (pk, NULL, 1);
-      rc = G10ERR_BAD_SIGN;
+        print_fingerprint (ctrl, NULL, pk, 1);
+      rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
       break;
 
     case TRUST_MARGINAL:
-      write_status( STATUS_TRUST_MARGINAL );
+      write_trust_status (STATUS_TRUST_MARGINAL, trustlevel);
       log_info(_("WARNING: This key is not certified with"
                  " sufficiently trusted signatures!\n"));
       log_info(_("         It is not certain that the"
                  " signature belongs to the owner.\n" ));
-      print_fingerprint (pk, NULL, 1);
+      print_fingerprint (ctrl, NULL, pk, 1);
       break;
 
     case TRUST_FULLY:
-      write_status( STATUS_TRUST_FULLY );
+      write_trust_status (STATUS_TRUST_FULLY, trustlevel);
       if (opt.with_fingerprint)
-        print_fingerprint (pk, NULL, 1);
+        print_fingerprint (ctrl, NULL, pk, 1);
       break;
 
     case TRUST_ULTIMATE:
-      write_status( STATUS_TRUST_ULTIMATE );
+      write_trust_status (STATUS_TRUST_ULTIMATE, trustlevel);
       if (opt.with_fingerprint)
-        print_fingerprint (pk, NULL, 1);
+        print_fingerprint (ctrl, NULL, pk, 1);
       break;
     }
 
@@ -694,14 +706,15 @@ check_signatures_trust( PKT_signature *sig )
 
 
 void
-release_pk_list( PK_LIST pk_list )
+release_pk_list (pk_list_t pk_list)
 {
-    PK_LIST pk_rover;
+  PK_LIST pk_rover;
 
-    for( ; pk_list; pk_list = pk_rover ) {
-       pk_rover = pk_list->next;
-       free_public_key( pk_list->pk );
-       m_free( pk_list );
+  for ( ; pk_list; pk_list = pk_rover)
+    {
+      pk_rover = pk_list->next;
+      free_public_key ( pk_list->pk );
+      xfree ( pk_list );
     }
 }
 
@@ -718,31 +731,31 @@ key_present_in_pk_list(PK_LIST pk_list, PKT_public_key *pk)
 
 
 /****************
- * Return a malloced string with a default reciepient if there is any
+ * Return a malloced string with a default recipient if there is any
  */
 static char *
-default_recipient(void)
+default_recipient(ctrl_t ctrl)
 {
-    PKT_secret_key *sk;
+    PKT_public_key *pk;
     byte fpr[MAX_FINGERPRINT_LEN+1];
     size_t n;
     char *p;
     int i;
 
     if( opt.def_recipient )
-       return m_strdup( opt.def_recipient );
+       return xstrdup( opt.def_recipient );
     if( !opt.def_recipient_self )
        return NULL;
-    sk = m_alloc_clear( sizeof *sk );
-    i = get_seckey_byname( sk, NULL, 0 );
+    pk = xmalloc_clear( sizeof *pk );
+    i = get_seckey_default (ctrl, pk);
     if( i ) {
-       free_secret_key( sk );
+       free_public_key( pk );
        return NULL;
     }
     n = MAX_FINGERPRINT_LEN;
-    fingerprint_from_sk( sk, fpr, &n );
-    free_secret_key( sk );
-    p = m_alloc( 2*n+3 );
+    fingerprint_from_pk( pk, fpr, &n );
+    free_public_key( pk );
+    p = xmalloc( 2*n+3 );
     *p++ = '0';
     *p++ = 'x';
     for(i=0; i < n; i++ )
@@ -752,7 +765,7 @@ default_recipient(void)
 }
 
 static int
-expand_id(const char *id,STRLIST *into,unsigned int flags)
+expand_id(const char *id,strlist_t *into,unsigned int flags)
 {
   struct groupitem *groups;
   int count=0;
@@ -762,7 +775,7 @@ expand_id(const char *id,STRLIST *into,unsigned int flags)
       /* need strcasecmp() here, as this should be localized */
       if(strcasecmp(groups->name,id)==0)
        {
-         STRLIST each,sl;
+         strlist_t each,sl;
 
          /* this maintains the current utf8-ness */
          for(each=groups->values;each;each=each->next)
@@ -780,14 +793,16 @@ expand_id(const char *id,STRLIST *into,unsigned int flags)
 }
 
 /* For simplicity, and to avoid potential loops, we only expand once -
  you can't make an alias that points to an alias. */
-static STRLIST
-expand_group(STRLIST input)
* you can't make an alias that points to an alias.  */
+static strlist_t
+expand_group (strlist_t input)
 {
-  STRLIST sl,output=NULL,rover;
+  strlist_t output = NULL;
+  strlist_t sl, rover;
 
-  for(rover=input;rover;rover=rover->next)
-    if(expand_id(rover->d,&output,rover->flags)==0)
+  for (rover = input; rover; rover = rover->next)
+    if (!(rover->flags & PK_LIST_FROM_FILE)
+        && !expand_id(rover->d,&output,rover->flags))
       {
        /* Didn't find any groups, so use the existing string */
        sl=add_to_strlist(&output,rover->d);
@@ -797,315 +812,541 @@ expand_group(STRLIST input)
   return output;
 }
 
+
+/* Helper for build_pk_list to find and check one key.  This helper is
+ * also used directly in server mode by the RECIPIENTS command.  On
+ * success the new key is added to PK_LIST_ADDR.  NAME is the user id
+ * of the key.  USE the requested usage and a set MARK_HIDDEN will
+ * mark the key in the updated list as a hidden recipient.  If
+ * FROM_FILE is true, NAME is not a user ID but the name of a file
+ * holding a key. */
+gpg_error_t
+find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use,
+                    int mark_hidden, int from_file, pk_list_t *pk_list_addr)
+{
+  int rc;
+  PKT_public_key *pk;
+
+  if (!name || !*name)
+    return gpg_error (GPG_ERR_INV_USER_ID);
+
+  pk = xtrycalloc (1, sizeof *pk);
+  if (!pk)
+    return gpg_error_from_syserror ();
+  pk->req_usage = use;
+
+  if (from_file)
+    rc = get_pubkey_fromfile (ctrl, pk, name);
+  else
+    rc = get_best_pubkey_byname (ctrl, NULL, pk, name, NULL, 0, 0);
+  if (rc)
+    {
+      int code;
+
+      /* Key not found or other error. */
+      log_error (_("%s: skipped: %s\n"), name, gpg_strerror (rc) );
+      switch (gpg_err_code (rc))
+        {
+        case GPG_ERR_NO_SECKEY:
+        case GPG_ERR_NO_PUBKEY:   code =  1; break;
+        case GPG_ERR_INV_USER_ID: code = 14; break;
+        default: code = 0; break;
+        }
+      send_status_inv_recp (code, name);
+      free_public_key (pk);
+      return rc;
+    }
+
+  rc = openpgp_pk_test_algo2 (pk->pubkey_algo, use);
+  if (rc)
+    {
+      /* Key found but not usable for us (e.g. sign-only key). */
+      send_status_inv_recp (3, name); /* Wrong key usage */
+      log_error (_("%s: skipped: %s\n"), name, gpg_strerror (rc) );
+      free_public_key (pk);
+      return rc;
+    }
+
+  /* Key found and usable.  Check validity. */
+  if (!from_file)
+    {
+      int trustlevel;
+
+      trustlevel = get_validity (ctrl, NULL, pk, pk->user_id, NULL, 1);
+      if ( (trustlevel & TRUST_FLAG_DISABLED) )
+        {
+          /* Key has been disabled. */
+          send_status_inv_recp (13, name);
+          log_info (_("%s: skipped: public key is disabled\n"), name);
+          free_public_key (pk);
+          return GPG_ERR_UNUSABLE_PUBKEY;
+        }
+
+      if ( !do_we_trust_pre (ctrl, pk, trustlevel) )
+        {
+          /* We don't trust this key.  */
+          send_status_inv_recp (10, name);
+          free_public_key (pk);
+          return GPG_ERR_UNUSABLE_PUBKEY;
+        }
+    }
+
+  /* Skip the actual key if the key is already present in the
+     list.  */
+  if (!key_present_in_pk_list (*pk_list_addr, pk))
+    {
+      if (!opt.quiet)
+        log_info (_("%s: skipped: public key already present\n"), name);
+      free_public_key (pk);
+    }
+  else
+    {
+      pk_list_t r;
+
+      r = xtrymalloc (sizeof *r);
+      if (!r)
+        {
+          rc = gpg_error_from_syserror ();
+          free_public_key (pk);
+          return rc;
+        }
+      r->pk = pk;
+      r->next = *pk_list_addr;
+      r->flags = mark_hidden? 1:0;
+      *pk_list_addr = r;
+    }
+
+  return 0;
+}
+
+
+
+/* This is the central function to collect the keys for recipients.
+ * It is thus used to prepare a public key encryption. encrypt-to
+ * keys, default keys and the keys for the actual recipients are all
+ * collected here.  When not in batch mode and no recipient has been
+ * passed on the commandline, the function will also ask for
+ * recipients.
+ *
+ * RCPTS is a string list with the recipients; NULL is an allowed
+ * value but not very useful.  Group expansion is done on these names;
+ * they may be in any of the user Id formats we can handle.  The flags
+ * bits for each string in the string list are used for:
+ *
+ * - PK_LIST_ENCRYPT_TO :: This is an encrypt-to recipient.
+ * - PK_LIST_HIDDEN     :: This is a hidden recipient.
+ * - PK_LIST_FROM_FILE  :: The argument is a file with a key.
+ *
+ * On success a list of keys is stored at the address RET_PK_LIST; the
+ * caller must free this list.  On error the value at this address is
+ * not changed.
+ */
 int
-build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
+build_pk_list (ctrl_t ctrl, strlist_t rcpts, PK_LIST *ret_pk_list)
 {
-    PK_LIST pk_list = NULL;
-    PKT_public_key *pk=NULL;
-    int rc=0;
-    int any_recipients=0;
-    STRLIST rov,remusr;
-    char *def_rec = NULL;
-
-    if(opt.grouplist)
-      remusr=expand_group(rcpts);
-    else
-      remusr=rcpts;
-
-    /* check whether there are any recipients in the list and build the
-     * list of the encrypt-to ones (we always trust them) */
-    for( rov = remusr; rov; rov = rov->next ) {
-       if( !(rov->flags & 1) )
-         {
-           any_recipients = 1;
+  PK_LIST pk_list = NULL;
+  PKT_public_key *pk=NULL;
+  int rc=0;
+  int any_recipients=0;
+  strlist_t rov,remusr;
+  char *def_rec = NULL;
+  char pkstrbuf[PUBKEY_STRING_SIZE];
+
+  /* Try to expand groups if any have been defined. */
+  if (opt.grouplist)
+    remusr = expand_group (rcpts);
+  else
+    remusr = rcpts;
 
-           if((rov->flags&2) && (PGP2 || PGP6 || PGP7 || PGP8))
-             {
-               log_info(_("you may not use %s while in %s mode\n"),
-                        "--hidden-recipient",
-                        compliance_option_string());
+  /* XXX: Change this function to use get_pubkeys instead of
+     get_pubkey_byname to detect ambiguous key specifications and warn
+     about duplicate keyblocks.  For ambiguous key specifications on
+     the command line or provided interactively, prompt the user to
+     select the best key.  If a key specification is ambiguous and we
+     are in batch mode, die.  */
 
-               compliance_failure();
-             }
-         }
-       else if( (use & PUBKEY_USAGE_ENC) && !opt.no_encrypt_to ) {
-           pk = m_alloc_clear( sizeof *pk );
-           pk->req_usage = use;
-           /* We can encrypt-to a disabled key */
-           if( (rc = get_pubkey_byname( pk, rov->d, NULL, NULL, 1 )) ) {
-               free_public_key( pk ); pk = NULL;
-               log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) );
-                write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
-                                              rov->d, strlen (rov->d), -1);
-               goto fail;
+  if (opt.encrypt_to_default_key)
+    {
+      static int warned;
+
+      const char *default_key = parse_def_secret_key (ctrl);
+      if (default_key)
+        {
+          PK_LIST r = xmalloc_clear (sizeof *r);
+
+          r->pk = xmalloc_clear (sizeof *r->pk);
+          r->pk->req_usage = PUBKEY_USAGE_ENC;
+
+          rc = get_pubkey_byname (ctrl, NULL, r->pk, default_key,
+                                   NULL, NULL, 0, 1);
+          if (rc)
+            {
+              xfree (r->pk);
+              xfree (r);
+
+              log_error (_("can't encrypt to '%s'\n"), default_key);
+              if (!opt.quiet)
+                log_info (_("(check argument of option '%s')\n"),
+                          "--default-key");
             }
-           else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) {
-               /* Skip the actual key if the key is already present
-                * in the list */
-               if (key_present_in_pk_list(pk_list, pk) == 0) {
-                   free_public_key(pk); pk = NULL;
-                   log_info(_("%s: skipped: public key already present\n"),
-                                                           rov->d);
-               }
-               else {
-                   PK_LIST r;
-                   r = m_alloc( sizeof *r );
-                   r->pk = pk; pk = NULL;
-                   r->next = pk_list;
-                   r->flags = (rov->flags&2)?1:0;
-                   pk_list = r;
-
-                   if(r->flags&1 && (PGP2 || PGP6 || PGP7 || PGP8))
-                     {
-                       log_info(_("you may not use %s while in %s mode\n"),
-                                "--hidden-encrypt-to",
-                                compliance_option_string());
-
-                       compliance_failure();
-                     }
-               }
-           }
-           else {
-               free_public_key( pk ); pk = NULL;
-               log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) );
-                write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
-                                              rov->d, strlen (rov->d), -1);
-               goto fail;
-           }
-       }
+          else
+            {
+              r->next = pk_list;
+              r->flags = 0;
+              pk_list = r;
+            }
+        }
+      else if (opt.def_secret_key)
+        {
+          if (! warned)
+            log_info (_("option '%s' given, but no valid default keys given\n"),
+                      "--encrypt-to-default-key");
+          warned = 1;
+        }
+      else
+        {
+          if (! warned)
+            log_info (_("option '%s' given, but option '%s' not given\n"),
+                      "--encrypt-to-default-key", "--default-key");
+          warned = 1;
+        }
     }
 
-    if( !any_recipients && !opt.batch ) { /* ask */
-       int have_def_rec;
-       char *answer=NULL;
-       STRLIST backlog=NULL;
-
-       def_rec = default_recipient();
-       have_def_rec = !!def_rec;
-       if( !have_def_rec )
-           tty_printf(_(
-               "You did not specify a user ID. (you may use \"-r\")\n"));
-       for(;;) {
-           rc = 0;
-           m_free(answer);
-           if( have_def_rec ) {
-               answer = def_rec;
-               def_rec = NULL;
-           }
-           else if(backlog) {
-             answer=pop_strlist(&backlog);
-           }
-           else {
-               answer = cpr_get_utf8("pklist.user_id.enter",
-                        _("\nEnter the user ID.  End with an empty line: "));
-               trim_spaces(answer);
-               cpr_kill_prompt();
-           }
-           if( !answer || !*answer ) {
-               m_free(answer);
-               break;
-           }
-           if(expand_id(answer,&backlog,0))
-             continue;
-           if( pk )
-               free_public_key( pk );
-           pk = m_alloc_clear( sizeof *pk );
-           pk->req_usage = use;
-           rc = get_pubkey_byname( pk, answer, NULL, NULL, 0 );
-           if( rc )
-               tty_printf(_("No such user ID.\n"));
-           else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) ) {
-               if( have_def_rec ) {
-                   if (key_present_in_pk_list(pk_list, pk) == 0) {
-                       free_public_key(pk); pk = NULL;
-                       log_info(_("skipped: public key "
-                                  "already set as default recipient\n") );
-                   }
-                   else {
-                       PK_LIST r = m_alloc( sizeof *r );
-                       r->pk = pk; pk = NULL;
-                       r->next = pk_list;
-                       r->flags = 0; /* no throwing default ids */
-                       pk_list = r;
-                   }
-                   any_recipients = 1;
-                   continue;
-               }
-               else {
-                   int trustlevel;
-                   
-                   trustlevel = get_validity (pk, pk->user_id);
-                   if( (trustlevel & TRUST_FLAG_DISABLED) ) {
-                       tty_printf(_("Public key is disabled.\n") );
-                   }
-                   else if( do_we_trust_pre( pk, trustlevel ) ) {
-                       /* Skip the actual key if the key is already present
-                        * in the list */
-                       if (key_present_in_pk_list(pk_list, pk) == 0) {
-                           free_public_key(pk); pk = NULL;
-                           log_info(_("skipped: public key already set\n") );
-                       }
-                       else {
-                           PK_LIST r;
-                           u32 keyid[2];
-
-                           keyid_from_pk( pk, keyid);
-                           tty_printf("Added %4u%c/%08lX %s \"",
-                                      nbits_from_pk( pk ),
-                                      pubkey_letter( pk->pubkey_algo ),
-                                      (ulong)keyid[1],
-                                      datestr_from_pk( pk ) );
-                           if(pk->user_id)
-                             tty_print_utf8_string(pk->user_id->name,
-                                                   pk->user_id->len);
-                           else
-                             {
-                               size_t n;
-                               char *p = get_user_id( keyid, &n );
-                               tty_print_utf8_string( p, n );
-                               m_free(p);
-                             }
-                           tty_printf("\"\n");
-
-                           r = m_alloc( sizeof *r );
-                           r->pk = pk; pk = NULL;
-                           r->next = pk_list;
-                           r->flags = 0; /* no throwing interactive ids */
-                           pk_list = r;
-                       }
-                       any_recipients = 1;
-                       continue;
-                   }
-               }
-           }
-           m_free(def_rec); def_rec = NULL;
-           have_def_rec = 0;
-       }
-       if( pk ) {
-           free_public_key( pk );
-           pk = NULL;
-       }
+  /* Check whether there are any recipients in the list and build the
+   * list of the encrypt-to ones (we always trust them). */
+  for ( rov = remusr; rov; rov = rov->next )
+    {
+      if ( !(rov->flags & PK_LIST_ENCRYPT_TO) )
+        {
+          /* This is a regular recipient; i.e. not an encrypt-to
+             one. */
+          any_recipients = 1;
+
+          /* Hidden recipients are not allowed while in PGP mode,
+             issue a warning and switch into GnuPG mode. */
+          if ((rov->flags & PK_LIST_HIDDEN) && (PGP6 || PGP7 || PGP8))
+            {
+              log_info(_("option '%s' may not be used in %s mode\n"),
+                       "--hidden-recipient",
+                       gnupg_compliance_option_string (opt.compliance));
+
+              compliance_failure();
+            }
+        }
+      else if (!opt.no_encrypt_to)
+        {
+          /* --encrypt-to has not been disabled.  Check this
+             encrypt-to key. */
+          pk = xmalloc_clear( sizeof *pk );
+          pk->req_usage = PUBKEY_USAGE_ENC;
+
+          /* We explicitly allow encrypt-to to an disabled key; thus
+             we pass 1 for the second last argument and 1 as the last
+             argument to disable AKL. */
+          if ( (rc = get_pubkey_byname (ctrl,
+                                        NULL, pk, rov->d, NULL, NULL, 1, 1)) )
+            {
+              free_public_key ( pk ); pk = NULL;
+              log_error (_("%s: skipped: %s\n"), rov->d, gpg_strerror (rc) );
+              send_status_inv_recp (0, rov->d);
+              goto fail;
+            }
+          else if ( !(rc=openpgp_pk_test_algo2 (pk->pubkey_algo,
+                                                PUBKEY_USAGE_ENC)) )
+            {
+              /* Skip the actual key if the key is already present
+               * in the list.  Add it to our list if not. */
+              if (key_present_in_pk_list(pk_list, pk) == 0)
+                {
+                  free_public_key (pk); pk = NULL;
+                  if (!opt.quiet)
+                    log_info (_("%s: skipped: public key already present\n"),
+                              rov->d);
+                }
+              else
+                {
+                  PK_LIST r;
+                  r = xmalloc( sizeof *r );
+                  r->pk = pk; pk = NULL;
+                  r->next = pk_list;
+                  r->flags = (rov->flags&PK_LIST_HIDDEN)?1:0;
+                  pk_list = r;
+
+                  /* Hidden encrypt-to recipients are not allowed while
+                     in PGP mode, issue a warning and switch into
+                     GnuPG mode. */
+                  if ((r->flags&PK_LIST_ENCRYPT_TO) && (PGP6 || PGP7 || PGP8))
+                    {
+                      log_info(_("option '%s' may not be used in %s mode\n"),
+                               "--hidden-encrypt-to",
+                               gnupg_compliance_option_string (opt.compliance));
+
+                      compliance_failure();
+                    }
+                }
+            }
+          else
+            {
+              /* The public key is not usable for encryption. */
+              free_public_key( pk ); pk = NULL;
+              log_error(_("%s: skipped: %s\n"), rov->d, gpg_strerror (rc) );
+              send_status_inv_recp (3, rov->d); /* Wrong key usage */
+              goto fail;
+            }
+        }
     }
-    else if( !any_recipients && (def_rec = default_recipient()) ) {
-       pk = m_alloc_clear( sizeof *pk );
-       pk->req_usage = use;
-       /* The default recipient may be disabled */
-       rc = get_pubkey_byname( pk, def_rec, NULL, NULL, 1 );
-       if( rc )
-           log_error(_("unknown default recipient `%s'\n"), def_rec );
-       else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) ) {
-         /* Mark any_recipients here since the default recipient
+
+  /* If we don't have any recipients yet and we are not in batch mode
+     drop into interactive selection mode. */
+  if ( !any_recipients && !opt.batch )
+    {
+      int have_def_rec;
+      char *answer = NULL;
+      strlist_t backlog = NULL;
+
+      if (pk_list)
+        any_recipients = 1;
+      def_rec = default_recipient(ctrl);
+      have_def_rec = !!def_rec;
+      if ( !have_def_rec )
+        tty_printf(_("You did not specify a user ID. (you may use \"-r\")\n"));
+
+      for (;;)
+        {
+          rc = 0;
+          xfree(answer);
+          if ( have_def_rec )
+            {
+              /* A default recipient is taken as the first entry. */
+              answer = def_rec;
+              def_rec = NULL;
+            }
+          else if (backlog)
+            {
+              /* This is part of our trick to expand and display groups. */
+              answer = strlist_pop (&backlog);
+            }
+          else
+            {
+              /* Show the list of already collected recipients and ask
+                 for more. */
+              PK_LIST iter;
+
+              tty_printf("\n");
+              tty_printf(_("Current recipients:\n"));
+              for (iter=pk_list;iter;iter=iter->next)
+                {
+                  u32 keyid[2];
+
+                  keyid_from_pk(iter->pk,keyid);
+                  tty_printf ("%s/%s %s \"",
+                              pubkey_string (iter->pk,
+                                             pkstrbuf, sizeof pkstrbuf),
+                              keystr(keyid),
+                              datestr_from_pk (iter->pk));
+
+                  if (iter->pk->user_id)
+                    tty_print_utf8_string(iter->pk->user_id->name,
+                                          iter->pk->user_id->len);
+                  else
+                    {
+                      size_t n;
+                      char *p = get_user_id (ctrl, keyid, &n );
+                      tty_print_utf8_string ( p, n );
+                      xfree(p);
+                    }
+                  tty_printf("\"\n");
+                }
+
+              answer = cpr_get_utf8("pklist.user_id.enter",
+                                    _("\nEnter the user ID.  "
+                                      "End with an empty line: "));
+              trim_spaces(answer);
+              cpr_kill_prompt();
+            }
+
+          if ( !answer || !*answer )
+            {
+              xfree(answer);
+              break;  /* No more recipients entered - get out of loop. */
+            }
+
+          /* Do group expand here too.  The trick here is to continue
+             the loop if any expansion occurred.  The code above will
+             then list all expanded keys. */
+          if (expand_id(answer,&backlog,0))
+            continue;
+
+          /* Get and check key for the current name. */
+          free_public_key (pk);
+          pk = xmalloc_clear( sizeof *pk );
+          pk->req_usage = PUBKEY_USAGE_ENC;
+          rc = get_pubkey_byname (ctrl, NULL, pk, answer, NULL, NULL, 0, 0 );
+          if (rc)
+            tty_printf(_("No such user ID.\n"));
+          else if ( !(rc=openpgp_pk_test_algo2 (pk->pubkey_algo,
+                                                PUBKEY_USAGE_ENC)) )
+            {
+              if ( have_def_rec )
+                {
+                  /* No validation for a default recipient. */
+                  if (!key_present_in_pk_list(pk_list, pk))
+                    {
+                      free_public_key (pk);
+                      pk = NULL;
+                      log_info (_("skipped: public key "
+                                  "already set as default recipient\n") );
+                    }
+                  else
+                    {
+                      PK_LIST r = xmalloc (sizeof *r);
+                      r->pk = pk; pk = NULL;
+                      r->next = pk_list;
+                      r->flags = 0; /* No throwing default ids. */
+                      pk_list = r;
+                    }
+                  any_recipients = 1;
+                  continue;
+                }
+              else
+                { /* Check validity of this key. */
+                  int trustlevel;
+
+                  trustlevel =
+                    get_validity (ctrl, NULL, pk, pk->user_id, NULL, 1);
+                  if ( (trustlevel & TRUST_FLAG_DISABLED) )
+                    {
+                      tty_printf (_("Public key is disabled.\n") );
+                    }
+                  else if ( do_we_trust_pre (ctrl, pk, trustlevel) )
+                    {
+                      /* Skip the actual key if the key is already
+                       * present in the list */
+                      if (!key_present_in_pk_list(pk_list, pk))
+                        {
+                          free_public_key (pk);
+                          pk = NULL;
+                          log_info(_("skipped: public key already set\n") );
+                        }
+                      else
+                        {
+                          PK_LIST r;
+                          r = xmalloc( sizeof *r );
+                          r->pk = pk; pk = NULL;
+                          r->next = pk_list;
+                          r->flags = 0; /* No throwing interactive ids. */
+                          pk_list = r;
+                        }
+                      any_recipients = 1;
+                      continue;
+                    }
+                }
+            }
+          xfree(def_rec); def_rec = NULL;
+          have_def_rec = 0;
+        }
+      if ( pk )
+        {
+          free_public_key( pk );
+          pk = NULL;
+        }
+    }
+  else if ( !any_recipients && (def_rec = default_recipient(ctrl)) )
+    {
+      /* We are in batch mode and have only a default recipient. */
+      pk = xmalloc_clear( sizeof *pk );
+      pk->req_usage = PUBKEY_USAGE_ENC;
+
+      /* The default recipient is allowed to be disabled; thus pass 1
+         as second last argument.  We also don't want an AKL. */
+      rc = get_pubkey_byname (ctrl, NULL, pk, def_rec, NULL, NULL, 1, 1);
+      if (rc)
+        log_error(_("unknown default recipient \"%s\"\n"), def_rec );
+      else if ( !(rc=openpgp_pk_test_algo2(pk->pubkey_algo,
+                                           PUBKEY_USAGE_ENC)) )
+        {
+          /* Mark any_recipients here since the default recipient
              would have been used if it wasn't already there.  It
              doesn't really matter if we got this key from the default
              recipient or an encrypt-to. */
-         any_recipients = 1;
-         if (key_present_in_pk_list(pk_list, pk) == 0)
-           log_info(_("skipped: public key already set as default recipient\n"));
-         else {
-           PK_LIST r = m_alloc( sizeof *r );
-           r->pk = pk; pk = NULL;
-           r->next = pk_list;
-           r->flags = 0; /* no throwing default ids */
-           pk_list = r;
-         }
-       }
-       if( pk ) {
-           free_public_key( pk );
-           pk = NULL;
-       }
-       m_free(def_rec); def_rec = NULL;
+          any_recipients = 1;
+          if (!key_present_in_pk_list(pk_list, pk))
+            log_info (_("skipped: public key already set "
+                        "as default recipient\n"));
+          else
+            {
+              PK_LIST r = xmalloc( sizeof *r );
+              r->pk = pk; pk = NULL;
+              r->next = pk_list;
+              r->flags = 0; /* No throwing default ids. */
+              pk_list = r;
+            }
+        }
+      if ( pk )
+        {
+          free_public_key( pk );
+          pk = NULL;
+        }
+      xfree(def_rec); def_rec = NULL;
     }
-    else {
-       any_recipients = 0;
-       for(; remusr; remusr = remusr->next ) {
-           if( (remusr->flags & 1) )
-               continue; /* encrypt-to keys are already handled */
-
-           pk = m_alloc_clear( sizeof *pk );
-           pk->req_usage = use;
-           if( (rc = get_pubkey_byname( pk, remusr->d, NULL, NULL, 0 )) ) {
-               free_public_key( pk ); pk = NULL;
-               log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) );
-                write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
-                                              remusr->d, strlen (remusr->d),
-                                              -1);
-               goto fail;
-           }
-           else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) {
-               int trustlevel;
-
-               trustlevel = get_validity (pk, pk->user_id);
-               if( (trustlevel & TRUST_FLAG_DISABLED) ) {
-                   free_public_key(pk); pk = NULL;
-                   log_info(_("%s: skipped: public key is disabled\n"),
-                                                                   remusr->d);
-                    write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
-                                                  remusr->d,
-                                                  strlen (remusr->d),
-                                                  -1);
-                   rc=G10ERR_UNU_PUBKEY;
-                   goto fail;
-               }
-               else if( do_we_trust_pre( pk, trustlevel ) ) {
-                   /* note: do_we_trust may have changed the trustlevel */
-
-                   /* We have at least one valid recipient. It doesn't matters
-                    * if this recipient is already present. */
-                   any_recipients = 1;
-
-                   /* Skip the actual key if the key is already present
-                    * in the list */
-                   if (key_present_in_pk_list(pk_list, pk) == 0) {
-                       free_public_key(pk); pk = NULL;
-                       log_info(_("%s: skipped: public key already present\n"),
-                                                                   remusr->d);
-                   }
-                   else {
-                       PK_LIST r;
-                       r = m_alloc( sizeof *r );
-                       r->pk = pk; pk = NULL;
-                       r->next = pk_list;
-                       r->flags = (remusr->flags&2)?1:0;
-                       pk_list = r;
-                   }
-               }
-               else { /* we don't trust this pk */
-                   free_public_key( pk ); pk = NULL;
-                    write_status_text_and_buffer (STATUS_INV_RECP, "10 ",
-                                                  remusr->d,
-                                                  strlen (remusr->d),
-                                                  -1);
-                   rc=G10ERR_UNU_PUBKEY;
-                   goto fail;
-               }
-           }
-           else {
-               free_public_key( pk ); pk = NULL;
-                write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
-                                              remusr->d,
-                                              strlen (remusr->d),
-                                              -1);
-               log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) );
-               goto fail;
-           }
-       }
+  else
+    {
+      /* General case: Check all keys. */
+      any_recipients = 0;
+      for (; remusr; remusr = remusr->next )
+        {
+          if ( (remusr->flags & PK_LIST_ENCRYPT_TO) )
+            continue; /* encrypt-to keys are already handled. */
+
+          rc = find_and_check_key (ctrl, remusr->d, PUBKEY_USAGE_ENC,
+                                   !!(remusr->flags&PK_LIST_HIDDEN),
+                                   !!(remusr->flags&PK_LIST_FROM_FILE),
+                                   &pk_list);
+          if (rc)
+            goto fail;
+          any_recipients = 1;
+        }
+    }
+
+  if ( !rc && !any_recipients )
+    {
+      log_error(_("no valid addressees\n"));
+      write_status_text (STATUS_NO_RECP, "0");
+      rc = GPG_ERR_NO_USER_ID;
     }
 
-    if( !rc && !any_recipients ) {
-       log_error(_("no valid addressees\n"));
-        write_status_text (STATUS_NO_RECP, "0");
-       rc = G10ERR_NO_USER_ID;
+#ifdef USE_TOFU
+  if (! rc && (opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_TOFU))
+    {
+      PK_LIST iter;
+      for (iter = pk_list; iter; iter = iter->next)
+        {
+          int rc2;
+
+          /* Note: we already resolved any conflict when looking up
+             the key.  Don't annoy the user again if she selected
+             accept once.  */
+          rc2 = tofu_register_encryption (ctrl, iter->pk, NULL, 0);
+          if (rc2)
+            log_info ("WARNING: Failed to register encryption to %s"
+                      " with TOFU engine\n",
+                      keystr (pk_main_keyid (iter->pk)));
+          else if (DBG_TRUST)
+            log_debug ("Registered encryption to %s with TOFU DB.\n",
+                      keystr (pk_main_keyid (iter->pk)));
+        }
     }
+#endif /*USE_TOFU*/
 
  fail:
 
-    if( rc )
-       release_pk_list( pk_list );
-    else
-       *ret_pk_list = pk_list;
-    if(opt.grouplist)
-      free_strlist(remusr);
-    return rc;
+  if ( rc )
+    release_pk_list( pk_list );
+  else
+    *ret_pk_list = pk_list;
+  if (opt.grouplist)
+    free_strlist(remusr);
+  return rc;
 }
 
 
@@ -1127,7 +1368,7 @@ build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use )
    preference list, so I'm including it. -dms */
 
 int
-algo_available( preftype_t preftype, int algo, void *hint )
+algo_available( preftype_t preftype, int algo, const union pref_hint *hint)
 {
   if( preftype == PREFTYPE_SYM )
     {
@@ -1135,22 +1376,36 @@ algo_available( preftype_t preftype, int algo, void *hint )
                  && algo != CIPHER_ALGO_3DES
                  && algo != CIPHER_ALGO_CAST5))
        return 0;
-      
-      if((PGP7 || PGP8) && (algo != CIPHER_ALGO_IDEA
-                           && algo != CIPHER_ALGO_3DES
-                           && algo != CIPHER_ALGO_CAST5
-                           && algo != CIPHER_ALGO_AES
-                           && algo != CIPHER_ALGO_AES192
-                           && algo != CIPHER_ALGO_AES256
-                           && algo != CIPHER_ALGO_TWOFISH))
+
+      if(PGP7 && (algo != CIPHER_ALGO_IDEA
+                 && algo != CIPHER_ALGO_3DES
+                 && algo != CIPHER_ALGO_CAST5
+                 && algo != CIPHER_ALGO_AES
+                 && algo != CIPHER_ALGO_AES192
+                 && algo != CIPHER_ALGO_AES256
+                 && algo != CIPHER_ALGO_TWOFISH))
        return 0;
 
-      return algo && !check_cipher_algo( algo );
+      /* PGP8 supports all the ciphers we do.. */
+
+      return algo && !openpgp_cipher_test_algo ( algo );
     }
   else if( preftype == PREFTYPE_HASH )
     {
-      if(hint && ((*(int *)hint) != md_digest_length(algo)))
-       return 0;
+      if (hint && hint->digest_length)
+       {
+         if (hint->digest_length!=20 || opt.flags.dsa2)
+           {
+             /* If --enable-dsa2 is set or the hash isn't 160 bits
+                (which implies DSA2), then we'll accept a hash that
+                is larger than we need.  Otherwise we won't accept
+                any hash that isn't exactly the right size. */
+             if (hint->digest_length > gcry_md_get_algo_dlen (algo))
+               return 0;
+           }
+         else if (hint->digest_length != gcry_md_get_algo_dlen (algo))
+           return 0;
+       }
 
       if((PGP6 || PGP7) && (algo != DIGEST_ALGO_MD5
                            && algo != DIGEST_ALGO_SHA1
@@ -1164,209 +1419,276 @@ algo_available( preftype_t preftype, int algo, void *hint )
                  && algo != DIGEST_ALGO_SHA256))
        return 0;
 
-      return algo && !check_digest_algo( algo );
+      return algo && !openpgp_md_test_algo (algo);
     }
   else if( preftype == PREFTYPE_ZIP )
     {
-      if((PGP6 || PGP7 || PGP8) && (algo != COMPRESS_ALGO_NONE
-                                   && algo != COMPRESS_ALGO_ZIP))
+      if((PGP6 || PGP7) && (algo != COMPRESS_ALGO_NONE
+                           && algo != COMPRESS_ALGO_ZIP))
        return 0;
 
+      /* PGP8 supports all the compression algos we do */
+
       return !check_compress_algo( algo );
     }
   else
     return 0;
 }
 
-
-
 /****************
  * Return -1 if we could not find an algorithm.
  */
 int
-select_algo_from_prefs(PK_LIST pk_list, int preftype, int request, void *hint)
+select_algo_from_prefs(PK_LIST pk_list, int preftype,
+                      int request, const union pref_hint *hint)
 {
-    PK_LIST pkr;
-    u32 bits[8];
-    const prefitem_t *prefs;
-    int i, j;
-    int compr_hack=0;
-    int any;
-
-    if( !pk_list )
-       return -1;
-
-    memset( bits, ~0, 8 * sizeof *bits );
-    for( pkr = pk_list; pkr; pkr = pkr->next ) {
-       u32 mask[8];
-
-       memset( mask, 0, 8 * sizeof *mask );
-       if( preftype == PREFTYPE_SYM ) {
-         if( PGP2 &&
-             pkr->pk->version < 4 &&
-             pkr->pk->selfsigversion < 4 )
-           mask[0] |= (1<<1); /* IDEA is implicitly there for v3 keys
-                                 with v3 selfsigs (rfc2440:12.1) if
-                                 --pgp2 mode is on.  This doesn't
-                                 mean it's actually available, of
-                                 course. */
-         else
-           mask[0] |= (1<<2); /* 3DES is implicitly there for everyone else */
-       }
-       else if( preftype == PREFTYPE_HASH ) {
+  PK_LIST pkr;
+  u32 bits[8];
+  const prefitem_t *prefs;
+  int result=-1,i;
+  u16 scores[256];
+
+  if( !pk_list )
+    return -1;
+
+  memset(bits,0xFF,sizeof(bits));
+  memset(scores,0,sizeof(scores));
+
+  for( pkr = pk_list; pkr; pkr = pkr->next )
+    {
+      u32 mask[8];
+      int rank=1,implicit=-1;
+
+      memset(mask,0,sizeof(mask));
+
+      switch(preftype)
+       {
+       case PREFTYPE_SYM:
+         /* IDEA is implicitly there for v3 keys with v3 selfsigs if
+            --pgp2 mode is on.  This was a 2440 thing that was
+            dropped from 4880 but is still relevant to GPG's 1991
+            support.  All this doesn't mean IDEA is actually
+            available, of course. */
+          implicit=CIPHER_ALGO_3DES;
+
+         break;
+
+       case PREFTYPE_HASH:
          /* While I am including this code for completeness, note
             that currently --pgp2 mode locks the hash at MD5, so this
-            function will never even be called.  Even if the hash
-            wasn't locked at MD5, we don't support sign+encrypt in
-            --pgp2 mode, and that's the only time PREFTYPE_HASH is
-            used anyway. -dms */
-         if( PGP2 &&
-             pkr->pk->version < 4 &&
-             pkr->pk->selfsigversion < 4 )
-           mask[0] |= (1<<1); /* MD5 is there for v3 keys with v3
-                                 selfsigs when --pgp2 is on. */
-         else
-           mask[0] |= (1<<2); /* SHA1 is there for everyone else */
+            code will never even be called.  Even if the hash wasn't
+            locked at MD5, we don't support sign+encrypt in --pgp2
+            mode, and that's the only time PREFTYPE_HASH is used
+            anyway. -dms */
+
+          implicit=DIGEST_ALGO_SHA1;
+
+         break;
+
+       case PREFTYPE_ZIP:
+         /* Uncompressed is always an option. */
+         implicit=COMPRESS_ALGO_NONE;
        }
-       else if( preftype == PREFTYPE_ZIP )
-         mask[0] |= (1<<0); /* Uncompressed is implicit */
 
-        if (pkr->pk->user_id) /* selected by user ID */
-            prefs = pkr->pk->user_id->prefs;
-        else
-            prefs = pkr->pk->prefs;
-
-       any = 0;
-       if( prefs ) {
-           for (i=0; prefs[i].type; i++ ) {
-               if( prefs[i].type == preftype ) {
-                   mask[prefs[i].value/32] |= 1 << (prefs[i].value%32);
-                   any = 1;
+      if (pkr->pk->user_id) /* selected by user ID */
+       prefs = pkr->pk->user_id->prefs;
+      else
+       prefs = pkr->pk->prefs;
+
+      if( prefs )
+       {
+         for (i=0; prefs[i].type; i++ )
+           {
+             if( prefs[i].type == preftype )
+               {
+                 /* Make sure all scores don't add up past 0xFFFF
+                    (and roll around) */
+                 if(rank+scores[prefs[i].value]<=0xFFFF)
+                   scores[prefs[i].value]+=rank;
+                 else
+                   scores[prefs[i].value]=0xFFFF;
+
+                 mask[prefs[i].value/32] |= 1<<(prefs[i].value%32);
+
+                 rank++;
+
+                 /* We saw the implicit algorithm, so we don't need
+                    tack it on the end ourselves. */
+                 if(implicit==prefs[i].value)
+                   implicit=-1;
                }
            }
        }
 
-       if( (!prefs || !any) && preftype == PREFTYPE_ZIP ) {
-           mask[0] |= 3; /* asume no_compression and old pgp */
-           compr_hack = 1;
+      if(rank==1 && preftype==PREFTYPE_ZIP)
+       {
+         /* If the compression preferences are not present, they are
+            assumed to be ZIP, Uncompressed (RFC4880:13.3.1) */
+         scores[1]=1; /* ZIP is first choice */
+         scores[0]=2; /* Uncompressed is second choice */
+         mask[0]|=3;
        }
 
-#if 0
-       log_debug("pref mask=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n",
-              (ulong)mask[7], (ulong)mask[6], (ulong)mask[5], (ulong)mask[4],
-            (ulong)mask[3], (ulong)mask[2], (ulong)mask[1], (ulong)mask[0]);
-#endif
-       for(i=0; i < 8; i++ )
-           bits[i] &= mask[i];
-#if 0
-       log_debug("pref bits=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n",
-              (ulong)bits[7], (ulong)bits[6], (ulong)bits[5], (ulong)bits[4],
-            (ulong)bits[3], (ulong)bits[2], (ulong)bits[1], (ulong)bits[0]);
-#endif
-    }
-    /* usable algorithms are now in bits
-     * We now use the last key from pk_list to select
-     * the algorithm we want to use. there are no
-     * preferences for the last key, we select the one
-     * corresponding to first set bit.
-     */
-    i = -1;
-    any = 0;
-
-    /* Can we use the requested algorithm? */
-    if(request>-1 && (bits[request/32] & (1<<(request%32))) &&
-       algo_available(preftype,request,hint))
-      return request;
-
-    /* If we have personal prefs set, use them instead of the last key */
-    if(preftype==PREFTYPE_SYM && opt.personal_cipher_prefs)
-      prefs=opt.personal_cipher_prefs;
-    else if(preftype==PREFTYPE_HASH && opt.personal_digest_prefs)
-      prefs=opt.personal_digest_prefs;
-    else if(preftype==PREFTYPE_ZIP && opt.personal_compress_prefs)
-      prefs=opt.personal_compress_prefs;
-
-    if( prefs ) {
-       for(j=0; prefs[j].type; j++ ) {
-           if( prefs[j].type == preftype ) {
-                if( (bits[prefs[j].value/32] & (1<<(prefs[j].value%32))) ) {
-                   if( algo_available( preftype, prefs[j].value, hint ) ) {
-                       any = 1;
-                       i = prefs[j].value;
-                       break;
-                   }
-               }
-           }
+      /* If the key didn't have the implicit algorithm listed
+        explicitly, add it here at the tail of the list. */
+      if(implicit>-1)
+       {
+         scores[implicit]+=rank;
+         mask[implicit/32] |= 1<<(implicit%32);
        }
-    }
-    if( !prefs || !any ) {
-       for(j=0; j < 256; j++ )
-           if( (bits[j/32] & (1<<(j%32))) ) {
-               if( algo_available( preftype, j, hint ) ) {
-                   i = j;
-                   break;
-               }
-           }
+
+      for(i=0;i<8;i++)
+       bits[i]&=mask[i];
     }
 
-#if 0
-    log_debug("prefs of type %d: selected %d\n", preftype, i );
-#endif
-    if( compr_hack && !i ) {
-       /* selected no compression, but we should check whether
-        * algorithm 1 is also available (the ordering is not relevant
-        * in this case). */
-       if( bits[0] & (1<<1) )
-           i = 1; /* yep; we can use compression algo 1 */
+  /* We've now scored all of the algorithms, and the usable ones have
+     bits set.  Let's pick the winner. */
+
+  /* The caller passed us a request.  Can we use it? */
+  if(request>-1 && (bits[request/32] & (1<<(request%32))) &&
+     algo_available(preftype,request,hint))
+    result=request;
+
+  if(result==-1)
+    {
+      /* If we have personal prefs set, use them. */
+      prefs=NULL;
+      if(preftype==PREFTYPE_SYM && opt.personal_cipher_prefs)
+       prefs=opt.personal_cipher_prefs;
+      else if(preftype==PREFTYPE_HASH && opt.personal_digest_prefs)
+       prefs=opt.personal_digest_prefs;
+      else if(preftype==PREFTYPE_ZIP && opt.personal_compress_prefs)
+       prefs=opt.personal_compress_prefs;
+
+      if( prefs )
+       for(i=0; prefs[i].type; i++ )
+         {
+           if(bits[prefs[i].value/32] & (1<<(prefs[i].value%32))
+              && algo_available( preftype, prefs[i].value, hint))
+             {
+               result = prefs[i].value;
+               break;
+             }
+         }
     }
 
-    /* "If you are building an authentication system, the recipient
-       may specify a preferred signing algorithm. However, the signer
-       would be foolish to use a weak algorithm simply because the
-       recipient requests it." RFC2440:13.  If we settle on MD5, and
-       SHA1 is also available, use SHA1 instead.  Of course, if the
-       user intentionally chose MD5 (by putting it in their personal
-       prefs), then we should do what they say. */
+  if(result==-1)
+    {
+      unsigned int best=-1;
 
-    if(preftype==PREFTYPE_HASH &&
-       i==DIGEST_ALGO_MD5 && (bits[0] & (1<<DIGEST_ALGO_SHA1)))
-      {
-       i=DIGEST_ALGO_SHA1;
+      /* At this point, we have not selected an algorithm due to a
+        special request or via personal prefs.  Pick the highest
+        ranked algorithm (i.e. the one with the lowest score). */
 
-       if(opt.personal_digest_prefs)
-         for(j=0; prefs[j].type; j++ )
-           if(opt.personal_digest_prefs[j].type==PREFTYPE_HASH &&
-              opt.personal_digest_prefs[j].value==DIGEST_ALGO_MD5)
+      if(preftype==PREFTYPE_HASH && scores[DIGEST_ALGO_MD5])
+       {
+         /* "If you are building an authentication system, the recipient
+            may specify a preferred signing algorithm. However, the
+            signer would be foolish to use a weak algorithm simply
+            because the recipient requests it." (RFC4880:14).  If any
+            other hash algorithm is available, pretend that MD5 isn't.
+            Note that if the user intentionally chose MD5 by putting it
+            in their personal prefs, then we do what the user said (as we
+            never reach this code). */
+
+         for(i=DIGEST_ALGO_MD5+1;i<256;i++)
+           if(scores[i])
              {
-               i=DIGEST_ALGO_MD5;
+               scores[DIGEST_ALGO_MD5]=0;
                break;
              }
-      }
+       }
+
+      for(i=0;i<256;i++)
+       {
+         /* Note the '<' here.  This means in case of a tie, we will
+            favor the lower algorithm number.  We have a choice
+            between the lower number (probably an older algorithm
+            with more time in use), or the higher number (probably a
+            newer algorithm with less time in use).  Older is
+            probably safer here, even though the newer algorithms
+            tend to be "stronger". */
+         if(scores[i] && scores[i]<best
+            && (bits[i/32] & (1<<(i%32)))
+            && algo_available(preftype,i,hint))
+           {
+             best=scores[i];
+             result=i;
+           }
+       }
+    }
 
-    return i;
+  return result;
 }
 
 /*
- * Select the MDC flag from the pk_list.  We can only use MDC if all recipients
- * support this feature 
+ * Select the MDC flag from the pk_list.  We can only use MDC if all
+ * recipients support this feature.
  */
 int
 select_mdc_from_pklist (PK_LIST pk_list)
 {
-    PK_LIST pkr;
+  PK_LIST pkr;
 
-    if( !pk_list )
-       return 0;
+  if ( !pk_list )
+    return 0;
 
-    for (pkr = pk_list; pkr; pkr = pkr->next) {
-        int mdc;
+  for (pkr = pk_list; pkr; pkr = pkr->next)
+    {
+      int mdc;
+
+      if (pkr->pk->user_id) /* selected by user ID */
+        mdc = pkr->pk->user_id->flags.mdc;
+      else
+        mdc = pkr->pk->flags.mdc;
+      if (!mdc)
+        return 0;  /* At least one recipient does not support it. */
+    }
+  return 1; /* Can be used. */
+}
 
-        if (pkr->pk->user_id) /* selected by user ID */
-            mdc = pkr->pk->user_id->mdc_feature;
-        else
-            mdc = pkr->pk->mdc_feature;
-        if (!mdc)
-            return 0; /* at least one recipient does not support it */
+
+/* Print a warning for all keys in PK_LIST missing the MDC feature. */
+void
+warn_missing_mdc_from_pklist (PK_LIST pk_list)
+{
+  PK_LIST pkr;
+
+  for (pkr = pk_list; pkr; pkr = pkr->next)
+    {
+      int mdc;
+
+      if (pkr->pk->user_id) /* selected by user ID */
+        mdc = pkr->pk->user_id->flags.mdc;
+      else
+        mdc = pkr->pk->flags.mdc;
+      if (!mdc)
+        log_info (_("Note: key %s has no %s feature\n"),
+                  keystr_from_pk (pkr->pk), "MDC");
+    }
+}
+
+void
+warn_missing_aes_from_pklist (PK_LIST pk_list)
+{
+  PK_LIST pkr;
+
+  for (pkr = pk_list; pkr; pkr = pkr->next)
+    {
+      const prefitem_t *prefs;
+      int i;
+      int gotit = 0;
+
+      prefs = pkr->pk->user_id? pkr->pk->user_id->prefs : pkr->pk->prefs;
+      if (prefs)
+        {
+          for (i=0; !gotit && prefs[i].type; i++ )
+            if (prefs[i].type == PREFTYPE_SYM
+                && prefs[i].value == CIPHER_ALGO_AES)
+              gotit++;
+       }
+      if (!gotit)
+        log_info (_("Note: key %s has no preference for %s\n"),
+                  keystr_from_pk (pkr->pk), "AES");
     }
-    return 1; /* can be used */
 }