See ChangeLog: Mon Sep 6 19:59:08 CEST 1999 Werner Koch
[gnupg.git] / g10 / pkclist.c
index 3e6e78d..fab2f13 100644 (file)
@@ -1,14 +1,14 @@
 /* pkclist.c
  *     Copyright (C) 1998 Free Software Foundation, Inc.
  *
- * This file is part of GNUPG.
+ * This file is part of GnuPG.
  *
- * GNUPG is free software; you can redistribute it and/or modify
+ * 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
  * (at your option) any later version.
  *
- * GNUPG is distributed in the hope that it will be useful,
+ * GnuPG is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
 #include "util.h"
 #include "trustdb.h"
 #include "ttyio.h"
+#include "status.h"
 #include "i18n.h"
 
+
+#define CONTROL_D ('D' - 'A' + 1)
+
+/* fixme: we have nearly the same code in keyedit.c */
+static void
+print_fpr( PKT_public_key *pk )
+{
+    byte array[MAX_FINGERPRINT_LEN], *p;
+    size_t i, n;
+
+    fingerprint_from_pk( pk, array, &n );
+    p = array;
+    /* Translators: this shoud fit into 24 bytes to that the fingerprint
+     * data is properly aligned with the user ID */
+    tty_printf(_("             Fingerprint:"));
+    if( n == 20 ) {
+       for(i=0; i < n ; i++, i++, p += 2 ) {
+           if( i == 10 )
+               tty_printf(" ");
+           tty_printf(" %02X%02X", *p, p[1] );
+       }
+    }
+    else {
+       for(i=0; i < n ; i++, p++ ) {
+           if( i && !(i%8) )
+               tty_printf(" ");
+           tty_printf(" %02X", *p );
+       }
+    }
+    tty_printf("\n");
+}
+
+static void
+fpr_info( PKT_public_key *pk )
+{
+    byte array[MAX_FINGERPRINT_LEN], *p;
+    size_t i, n;
+    FILE *fp = log_stream();
+
+    fingerprint_from_pk( pk, array, &n );
+    p = array;
+    log_info(_("Fingerprint:"));
+    if( n == 20 ) {
+       for(i=0; i < n ; i++, i++, p += 2 ) {
+           if( i == 10 )
+               putc(' ', fp);
+           fprintf(fp, " %02X%02X", *p, p[1] );
+       }
+    }
+    else {
+       for(i=0; i < n ; i++, p++ ) {
+           if( i && !(i%8) )
+               putc(' ', fp);
+           fprintf(fp, " %02X", *p );
+       }
+    }
+    putc('\n', fp );
+}
+
+
+
+static void
+show_paths( ulong lid, int only_first )
+{
+    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_string( p, n ),
+       m_free(p);
+       tty_printf("\"\n");
+       free_public_key( pk );
+    }
+    enum_cert_paths( &context, NULL, NULL, NULL ); /* release context */
+    tty_printf("\n");
+}
+
+
+
+
 /****************
  * Returns true if an ownertrust has changed.
  */
 static int
-query_ownertrust( ulong lid )
+do_edit_ownertrust( ulong lid, int mode, unsigned *new_trust, int defer_help )
 {
     char *p;
     int rc;
     size_t n;
     u32 keyid[2];
-    PKT_public_cert *pkc ;
+    PKT_public_key *pk ;
     int changed=0;
+    int quit=0;
+    int show=0;
+    int did_help=defer_help;
 
-    rc = keyid_from_trustdb( lid, keyid );
+    rc = keyid_from_lid( lid, keyid );
     if( rc ) {
        log_error("ooops: can't get keyid for lid %lu\n", lid);
        return 0;
     }
 
-    pkc = m_alloc_clear( sizeof *pkc );
-    rc = get_pubkey( pkc, keyid );
+    pk = m_alloc_clear( sizeof *pk );
+    rc = get_pubkey( pk, keyid );
     if( rc ) {
-       log_error("keyid %08lX: pubkey not found: %s\n",
+       log_error("key %08lX: public key not found: %s\n",
                                (ulong)keyid[1], g10_errstr(rc) );
        return 0;
     }
 
-    tty_printf(_("No ownertrust defined for %lu:\n"
-              "%4u%c/%08lX %s \""), lid,
-             nbits_from_pkc( pkc ), pubkey_letter( pkc->pubkey_algo ),
-             (ulong)keyid[1], datestr_from_pkc( pkc ) );
-    p = get_user_id( keyid, &n );
-    tty_print_string( p, n ),
-    m_free(p);
-    tty_printf(_("\"\n\n"
+
+    for(;;) {
+       /* a string with valid answers */
+       char *ans = _("sSmMqQ");
+
+       if( !did_help ) {
+           if( !mode ) {
+               tty_printf(_("No trust value assigned to %lu:\n"
+                          "%4u%c/%08lX %s \""), lid,
+                         nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ),
+                         (ulong)keyid[1], datestr_from_pk( pk ) );
+               p = get_user_id( keyid, &n );
+               tty_print_string( p, n ),
+               m_free(p);
+               tty_printf("\"\n");
+               print_fpr( pk );
+               tty_printf("\n");
+           }
+           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"
@@ -77,20 +217,23 @@ query_ownertrust( ulong lid )
 " 2 = I do NOT trust\n"
 " 3 = I trust marginally\n"
 " 4 = I trust fully\n"
-" s = please show me more information\n\n") );
-
-    for(;;) {
-       p = tty_get(_("Your decision? "));
+" s = please show me more information\n") );
+           if( mode )
+               tty_printf(_(" m = back to the main menu\n"));
+           else
+               tty_printf(_(" q = quit\n"));
+           tty_printf("\n");
+           did_help = 1;
+       }
+       if( strlen(ans) != 6 )
+           BUG();
+       p = cpr_get("edit_ownertrust.value",_("Your decision? "));
        trim_spaces(p);
-       tty_kill_prompt();
-       if( *p && p[1] )
+       cpr_kill_prompt();
+       if( !*p )
+           did_help = 0;
+       else if( *p && p[1] )
            ;
-       else if( *p == '?' ) {
-           tty_printf(_(
-"It's up to you to assign a value here; this value will never be exported\n"
-"to any 3rd party.  We need it to implement the web-of-trust; it has nothing\n"
-"to do with the (implicitly created) web-of-certificates.\n"));
-       }
        else if( !p[1] && (*p >= '1' && *p <= '4') ) {
            unsigned trust;
            switch( *p ) {
@@ -100,297 +243,789 @@ query_ownertrust( ulong lid )
              case '4': trust = TRUST_FULLY    ; break;
              default: BUG();
            }
-           if( !update_ownertrust( lid, trust ) )
-               changed++;
+           *new_trust = trust;
+           changed = 1;
+           break;
+       }
+       else if( *p == ans[0] || *p == ans[1] ) {
+           tty_printf(_(
+               "Certificates leading to an ultimately trusted key:\n"));
+           show = 1;
            break;
        }
-       else if( *p == 's' || *p == 'S' ) {
-           tty_printf(_("You will see a list of signators etc. here\n"));
+       else if( mode && (*p == ans[2] || *p == ans[3] || *p == CONTROL_D ) ) {
+           break ; /* back to the menu */
+       }
+       else if( !mode && (*p == ans[4] || *p == ans[5] ) ) {
+           quit = 1;
+           break ; /* back to the menu */
        }
        m_free(p); p = NULL;
     }
     m_free(p);
-    m_free(pkc);
-    return changed;
+    m_free(pk);
+    return show? -2: quit? -1 : changed;
 }
 
 
+int
+edit_ownertrust( ulong lid, int mode )
+{
+    unsigned int trust;
+    int no_help = 0;
+
+    for(;;) {
+       switch( do_edit_ownertrust( lid, mode, &trust, no_help ) ) {
+         case -1:
+           return 0;
+         case -2:
+           show_paths( lid, 1  );
+           no_help = 1;
+           break;
+         case 1:
+           trust &= ~TRUST_FLAG_DISABLED;
+           trust |= get_ownertrust( lid ) & TRUST_FLAG_DISABLED;
+           if( !update_ownertrust( lid, trust ) )
+               return 1;
+           return 0;
+         default:
+           return 0;
+       }
+    }
+}
+
+static int
+add_ownertrust_cb( ulong lid )
+{
+    unsigned trust;
+    int rc = do_edit_ownertrust( lid, 0, &trust, 0 );
+
+    if( rc == 1 )
+       return trust & TRUST_MASK;
+    return rc > 0? 0 : rc;
+}
+
 /****************
  * Try to add some more owner trusts (interactive)
+ * This function presents all the signator in a certificate
+ * chain who have no ownertrust value assigned.
  * Returns: -1 if no ownertrust were added.
  */
 static int
-add_ownertrust( PKT_public_cert *pkc )
+add_ownertrust( PKT_public_key *pk, int *quit, unsigned *trustlevel )
 {
     int rc;
-    void *context = NULL;
-    ulong lid;
-    unsigned trust;
-    int any=0;
+    unsigned flags = 0;
 
+    *quit = 0;
+    *trustlevel = 0;
     tty_printf(
 _("Could not find a valid trust path to the key.  Let's see whether we\n"
   "can assign some missing owner trust values.\n\n"));
 
-    rc = query_trust_record( pkc );
-    if( rc ) {
-       log_error("Ooops: not in trustdb\n");
-       return -1;
-    }
-
-    lid = pkc->local_id;
-    while( !(rc=enum_trust_web( &context, &lid )) ) {
-       rc = get_ownertrust( lid, &trust );
-       if( rc )
-           log_fatal("Ooops: couldn't get ownertrust for %lu\n", lid);
-       if( trust == TRUST_UNDEFINED || trust == TRUST_EXPIRED ||
-           trust == TRUST_UNKNOWN ) {
-           if( query_ownertrust( lid ) )
-               any=1;
-       }
-    }
-    if( rc == -1 )
-       rc = 0;
-    enum_trust_web( &context, NULL ); /* close */
+    rc = check_trust( pk, trustlevel, NULL, add_ownertrust_cb, &flags );
 
-    if( !any )
-       tty_printf(_("No ownertrust values changed.\n\n") );
+    if( !(flags & 1) )
+       tty_printf(_("No path leading to one of our keys found.\n\n") );
+    else if( !(flags & 2) )
+       tty_printf(_("No certificates with undefined trust found.\n\n") );
+    else if( !(flags & 4) )
+       tty_printf(_("No trust values changed.\n\n") );
 
-    return rc? rc : any? 0:-1;
+    return (flags & 4)? 0:-1;
 }
 
 /****************
- * Check whether we can trust this pkc which has a trustlevel of TRUSTLEVEL
+ * Check whether we can trust this pk which has a trustlevel of TRUSTLEVEL
  * Returns: true if we trust.
  */
 static int
-do_we_trust( PKT_public_cert *pkc, int trustlevel )
+do_we_trust( PKT_public_key *pk, int trustlevel )
 {
     int rc;
+    int did_add = 0;
 
+  retry:
     if( (trustlevel & TRUST_FLAG_REVOKED) ) {
-       char *answer;
-       int yes;
+       log_info(_("key %08lX: key has been revoked!\n"),
+                                       (ulong)keyid_from_pk( pk, NULL) );
+       if( opt.batch )
+           return 0;
 
-       log_info("key has beed revoked!\n");
+       if( !cpr_get_answer_is_yes("revoked_key.override",
+                                   _("Use this key anyway? ")) )
+           return 0;
+    }
+    else if( (trustlevel & TRUST_FLAG_SUB_REVOKED) ) {
+       log_info(_("key %08lX: subkey has been revoked!\n"),
+                                       (ulong)keyid_from_pk( pk, NULL) );
        if( opt.batch )
            return 0;
 
-       answer = tty_get("Use this key anyway? ");
-       tty_kill_prompt();
-       yes = answer_is_yes(answer);
-       m_free(answer);
-       if( !yes )
+       if( !cpr_get_answer_is_yes("revoked_key.override",
+                                   _("Use this key anyway? ")) )
            return 0;
     }
 
 
     switch( (trustlevel & TRUST_MASK) ) {
       case TRUST_UNKNOWN: /* No pubkey in trustDB: Insert and check again */
-       rc = insert_trust_record( pkc );
+       rc = insert_trust_record_by_pk( pk );
        if( rc ) {
            log_error("failed to insert it into the trustdb: %s\n",
                                                      g10_errstr(rc) );
            return 0; /* no */
        }
-       rc = check_trust( pkc, &trustlevel );
+       rc = check_trust( pk, &trustlevel, NULL, NULL, NULL );
        if( rc )
            log_fatal("trust check after insert failed: %s\n",
                                                      g10_errstr(rc) );
-       if( trustlevel == TRUST_UNKNOWN || trustlevel == TRUST_EXPIRED )
-           BUG();
-       return do_we_trust( pkc, trustlevel );
+       if( trustlevel == TRUST_UNKNOWN || trustlevel == TRUST_EXPIRED ) {
+           log_debug("do_we_trust: oops at %d\n", __LINE__ );
+           return 0;
+       }
+       return do_we_trust( pk, trustlevel );
 
       case TRUST_EXPIRED:
-       log_info("key has expired\n");
+       log_info(_("%08lX: key has expired\n"),
+                                   (ulong)keyid_from_pk( pk, NULL) );
        return 0; /* no */
 
       case TRUST_UNDEFINED:
        if( opt.batch || opt.answer_no )
-           log_info("no info to calculate a trust probability\n");
+           log_info(_("%08lX: no info to calculate a trust probability\n"),
+                                       (ulong)keyid_from_pk( pk, NULL) );
        else {
-           rc = add_ownertrust( pkc );
-           if( !rc ) {
-               rc = check_trust( pkc, &trustlevel );
-               if( rc )
-                   log_fatal("trust check after add_ownertrust failed: %s\n",
-                                                             g10_errstr(rc) );
-               /* fixme: this is recursive; we should unroll it */
-               return do_we_trust( pkc, trustlevel );
+           int quit;
+
+           rc = add_ownertrust( pk, &quit, &trustlevel );
+           if( !rc && !did_add && !quit ) {
+               did_add = 1;
+               goto retry;
            }
        }
        return 0;
 
       case TRUST_NEVER:
-       log_info("We do NOT trust this key\n");
+       log_info(_("%08lX: We do NOT trust this key\n"),
+                                       (ulong)keyid_from_pk( pk, NULL) );
        return 0; /* no */
 
       case TRUST_MARGINAL:
-       log_info("I'm not sure whether this key really belongs to the owner\n"
-                "but I proceed anyway\n");
+       log_info(
+       _("%08lX: It is not sure that this key really belongs to the owner\n"
+        "but it is accepted anyway\n"), (ulong)keyid_from_pk( pk, NULL) );
        return 1; /* yes */
 
       case TRUST_FULLY:
        if( opt.verbose )
-           log_info("This key probably belongs to the owner\n");
+           log_info(_("This key probably belongs to the owner\n"));
        return 1; /* yes */
 
       case TRUST_ULTIMATE:
        if( opt.verbose )
-           log_info("This key belongs to us (we have the secret key)\n");
+           log_info(_("This key belongs to us\n"));
        return 1; /* yes */
 
       default: BUG();
     }
 
-
-    /* Eventuell fragen falls der trustlevel nicht ausreichend ist */
-
-
     return 1; /* yes */
 }
 
 
+
 /****************
  * wrapper around do_we_trust, so we can ask whether to use the
  * key anyway.
  */
 static int
-do_we_trust_pre( PKT_public_cert *pkc, int trustlevel )
+do_we_trust_pre( PKT_public_key *pk, int trustlevel )
 {
-    int rc = do_we_trust( pkc, trustlevel );
+    int rc;
+
+    rc = do_we_trust( pk, trustlevel );
+
+    if( (trustlevel & TRUST_FLAG_REVOKED) && !rc )
+       return 0;
+    if( (trustlevel & TRUST_FLAG_SUB_REVOKED) && !rc )
+       return 0;
+    else if( !opt.batch && !rc ) {
+       char *p;
+       u32 keyid[2];
+       size_t n;
 
-    if( !opt.batch && !rc ) {
-       char *answer;
+       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 ) );
+       p = get_user_id( keyid, &n );
+       tty_print_string( p, n ),
+       m_free(p);
+       tty_printf("\"\n");
+       print_fpr( pk );
+       tty_printf("\n");
 
        tty_printf(_(
 "It is NOT certain that the key belongs to its owner.\n"
 "If you *really* know what you are doing, you may answer\n"
 "the next question with yes\n\n") );
 
-       answer = tty_get("Use this key anyway? ");
-       tty_kill_prompt();
-       if( answer_is_yes(answer) )
+       if( cpr_get_answer_is_yes("untrusted_key.override",
+                                 _("Use this key anyway? "))  )
            rc = 1;
-       m_free(answer);
+
+       /* Hmmm: Should we set a flag to tell the user the user about
+        *       his decision the next time he encrypts for this recipient?
+        */
+    }
+    else if( opt.always_trust && !rc ) {
+       if( !opt.quiet )
+           log_info(_("WARNING: Using untrusted key!\n"));
+       rc = 1;
+    }
+    return rc;
+}
+
+
+
+/****************
+ * Check whether we can trust this signature.
+ * Returns: Error if we shall not trust this signatures.
+ */
+int
+check_signatures_trust( PKT_signature *sig )
+{
+    PKT_public_key *pk = m_alloc_clear( sizeof *pk );
+    int trustlevel;
+    int did_add = 0;
+    int rc=0;
+
+
+    if( opt.always_trust ) {
+       if( !opt.quiet )
+           log_info(_("WARNING: Using untrusted key!\n"));
+       return 0;
+    }
+
+
+    rc = get_pubkey( 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;
+       goto leave;
+    }
+
+    rc = check_trust( pk, &trustlevel, NULL, NULL, NULL );
+    if( rc ) {
+       log_error("check trust failed: %s\n", g10_errstr(rc));
+       goto leave;
     }
+
+  retry:
+    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"));
+    }
+    else if( (trustlevel & TRUST_FLAG_SUB_REVOKED) ) {
+       write_status( STATUS_KEYREVOKED );
+       log_info(_("WARNING: This subkey has been revoked by its owner!\n"));
+    }
+
+
+    switch( (trustlevel & TRUST_MASK) ) {
+      case TRUST_UNKNOWN: /* No pubkey in trustDB: Insert and check again */
+       rc = insert_trust_record_by_pk( pk );
+       if( rc ) {
+           log_error("failed to insert it into the trustdb: %s\n",
+                                                     g10_errstr(rc) );
+           goto leave;
+       }
+       rc = check_trust( pk, &trustlevel, NULL, NULL, NULL );
+       if( rc )
+           log_fatal("trust check after insert failed: %s\n",
+                                                     g10_errstr(rc) );
+       if( trustlevel == TRUST_UNKNOWN || trustlevel == TRUST_EXPIRED )
+           BUG();
+       goto retry;
+
+      case TRUST_EXPIRED:
+       log_info(_("Note: This key has expired!\n"));
+       fpr_info( pk );
+       break;
+
+      case TRUST_UNDEFINED:
+       if( did_add || opt.batch || opt.answer_no ) {
+           write_status( STATUS_TRUST_UNDEFINED );
+           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" ));
+           fpr_info( pk );
+       }
+       else {
+           int quit;
+           rc = add_ownertrust( pk, &quit, &trustlevel );
+           if( rc || quit ) {
+               did_add = 1;
+               rc = 0;
+           }
+           goto retry;
+       }
+       break;
+
+      case TRUST_NEVER:
+       write_status( STATUS_TRUST_NEVER );
+       log_info(_("WARNING: We do NOT trust this key!\n"));
+       log_info(_("         The signature is probably a FORGERY.\n"));
+       rc = G10ERR_BAD_SIGN;
+       break;
+
+      case TRUST_MARGINAL:
+       write_status( STATUS_TRUST_MARGINAL );
+       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"
+                ));
+       fpr_info( pk );
+       break;
+
+      case TRUST_FULLY:
+       write_status( STATUS_TRUST_FULLY );
+       break;
+
+      case TRUST_ULTIMATE:
+       write_status( STATUS_TRUST_ULTIMATE );
+       break;
+
+      default: BUG();
+    }
+
+
+  leave:
+    free_public_key( pk );
     return rc;
 }
 
 
 void
-release_pkc_list( PKC_LIST pkc_list )
+release_pk_list( PK_LIST pk_list )
+{
+    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 );
+    }
+}
+
+
+static int
+key_present_in_pk_list(PK_LIST pk_list, PKT_public_key *pk)
+{
+    for( ; pk_list; pk_list = pk_list->next)
+       if (cmp_public_keys(pk_list->pk, pk) == 0)
+           return 0;
+
+    return -1;
+}
+
+
+/****************
+ * Return a malloced string with a default reciepient if there is any
+ */
+static char *
+default_recipient(void)
 {
-    PKC_LIST pkc_rover;
+    PKT_secret_key *sk;
+    byte fpr[MAX_FINGERPRINT_LEN+1];
+    size_t n;
+    char *p;
+    int i;
 
-    for( ; pkc_list; pkc_list = pkc_rover ) {
-       pkc_rover = pkc_list->next;
-       free_public_cert( pkc_list->pkc );
-       m_free( pkc_list );
+    if( opt.def_recipient )
+       return m_strdup( opt.def_recipient );
+    if( !opt.def_recipient_self )
+       return NULL;
+    sk = m_alloc_clear( sizeof *sk );
+    i = get_seckey_byname( sk, NULL, 0 );
+    if( i ) {
+       free_secret_key( sk );
+       return NULL;
     }
+    n = MAX_FINGERPRINT_LEN;
+    fingerprint_from_sk( sk, fpr, &n );
+    free_secret_key( sk );
+    p = m_alloc( 2*n+3 );
+    *p++ = '0';
+    *p++ = 'x';
+    for(i=0; i < n; i++ )
+       sprintf( p+2*i, "%02X", fpr[i] );
+    p -= 2;
+    return p;
 }
 
+
 int
-build_pkc_list( STRLIST remusr, PKC_LIST *ret_pkc_list, unsigned usage )
+build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use )
 {
-    PKC_LIST pkc_list = NULL;
-    PKT_public_cert *pkc=NULL;
+    PK_LIST pk_list = NULL;
+    PKT_public_key *pk=NULL;
     int rc=0;
+    int any_recipients=0;
+    STRLIST rov;
+    char *def_rec = 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 & 1) )
+           any_recipients = 1;
+       else if( (use & PUBKEY_USAGE_ENC) && !opt.no_encrypt_to ) {
+           pk = m_alloc_clear( sizeof *pk );
+           pk->pubkey_usage = use;
+           if( (rc = get_pubkey_byname( NULL, pk, rov->d, NULL )) ) {
+               free_public_key( pk ); pk = NULL;
+               log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) );
+           }
+           else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) {
 
-    if( !remusr && !opt.batch ) { /* ask */
+               /* 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->mark = 0;
+                   pk_list = r;
+               }
+           }
+           else {
+               free_public_key( pk ); pk = NULL;
+               log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) );
+           }
+       }
+    }
+
+    if( !any_recipients && !opt.batch ) { /* ask */
        char *answer=NULL;
+       int have_def_rec;
 
-       tty_printf(_(
+       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\n"));
        for(;;) {
            rc = 0;
            m_free(answer);
-           answer = tty_get(_("Enter the user ID: "));
-           trim_spaces(answer);
-           tty_kill_prompt();
+           if( have_def_rec ) {
+               answer = def_rec;
+               def_rec = NULL;
+           }
+           else {
+               answer = cpr_get_utf8("pklist.user_id.enter",
+                                      _("Enter the user ID: "));
+               trim_spaces(answer);
+               cpr_kill_prompt();
+           }
            if( !*answer )
                break;
-           if( pkc )
-               free_public_cert( pkc );
-           pkc = m_alloc_clear( sizeof *pkc );
-           rc = get_pubkey_byname( pkc, answer );
+           if( pk )
+               free_public_key( pk );
+           pk = m_alloc_clear( sizeof *pk );
+           pk->pubkey_usage = use;
+           rc = get_pubkey_byname( NULL, pk, answer, NULL );
            if( rc )
-               tty_printf("No such user ID.\n");
-           else if( !(rc=check_pubkey_algo2(pkc->pubkey_algo, usage)) ) {
-               int trustlevel;
-
-               rc = check_trust( pkc, &trustlevel );
-               if( rc ) {
-                   log_error("error checking pkc of '%s': %s\n",
-                                                     answer, g10_errstr(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->mark = 0;
+                       pk_list = r;
+                   }
+                   any_recipients = 1;
+                   break;
                }
-               else if( do_we_trust_pre( pkc, trustlevel ) ) {
-                   PKC_LIST r;
+               else {
+                   int trustlevel;
 
-                   r = m_alloc( sizeof *r );
-                   r->pkc = pkc; pkc = NULL;
-                   r->next = pkc_list;
-                   r->mark = 0;
-                   pkc_list = r;
-                   break;
+                   rc = check_trust( pk, &trustlevel, NULL, NULL, NULL );
+                   if( rc ) {
+                       log_error("error checking pk of `%s': %s\n",
+                                                    answer, g10_errstr(rc) );
+                   }
+                   else 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 with --encrypt-to\n") );
+                       }
+                       else {
+                           PK_LIST r;
+
+                           r = m_alloc( sizeof *r );
+                           r->pk = pk; pk = NULL;
+                           r->next = pk_list;
+                           r->mark = 0;
+                           pk_list = r;
+                       }
+                       any_recipients = 1;
+                       break;
+                   }
                }
            }
+           m_free(def_rec); def_rec = NULL;
+           have_def_rec = 0;
        }
        m_free(answer);
-       if( pkc ) {
-           free_public_cert( pkc );
-           pkc = NULL;
+       if( pk ) {
+           free_public_key( pk );
+           pk = NULL;
        }
     }
+    else if( !any_recipients && (def_rec = default_recipient()) ) {
+       pk = m_alloc_clear( sizeof *pk );
+       pk->pubkey_usage = use;
+       rc = get_pubkey_byname( NULL, pk, def_rec, NULL );
+       if( rc )
+           log_error(_("unknown default recipient `%s'\n"), def_rec );
+       else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) ) {
+           PK_LIST r = m_alloc( sizeof *r );
+           r->pk = pk; pk = NULL;
+           r->next = pk_list;
+           r->mark = 0;
+           pk_list = r;
+           any_recipients = 1;
+       }
+       if( pk ) {
+           free_public_key( pk );
+           pk = NULL;
+       }
+       m_free(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 */
 
-           pkc = m_alloc_clear( sizeof *pkc );
-           if( (rc = get_pubkey_byname( pkc, remusr->d )) ) {
-               free_public_cert( pkc ); pkc = NULL;
-               log_error("skipped '%s': %s\n", remusr->d, g10_errstr(rc) );
+           pk = m_alloc_clear( sizeof *pk );
+           pk->pubkey_usage = use;
+           if( (rc = get_pubkey_byname( NULL, pk, remusr->d, NULL )) ) {
+               free_public_key( pk ); pk = NULL;
+               log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) );
            }
-           else if( !(rc=check_pubkey_algo2(pkc->pubkey_algo, usage )) ) {
+           else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) {
                int trustlevel;
 
-               rc = check_trust( pkc, &trustlevel );
+               rc = check_trust( pk, &trustlevel, NULL, NULL, NULL );
                if( rc ) {
-                   free_public_cert( pkc ); pkc = NULL;
-                   log_error("error checking pkc of '%s': %s\n",
+                   free_public_key( pk ); pk = NULL;
+                   log_error(_("%s: error checking key: %s\n"),
                                                      remusr->d, g10_errstr(rc) );
                }
-               else if( do_we_trust_pre( pkc, trustlevel ) ) {
+               else if( (trustlevel & TRUST_FLAG_DISABLED) ) {
+                   free_public_key(pk); pk = NULL;
+                   log_info(_("%s: skipped: public key is disabled\n"),
+                                                                   remusr->d);
+               }
+               else if( do_we_trust_pre( pk, trustlevel ) ) {
                    /* note: do_we_trust may have changed the trustlevel */
-                   PKC_LIST r;
 
-                   r = m_alloc( sizeof *r );
-                   r->pkc = pkc; pkc = NULL;
-                   r->next = pkc_list;
-                   r->mark = 0;
-                   pkc_list = r;
+                   /* 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->mark = 0;
+                       pk_list = r;
+                   }
                }
-               else { /* we don't trust this pkc */
-                   free_public_cert( pkc ); pkc = NULL;
+               else { /* we don't trust this pk */
+                   free_public_key( pk ); pk = NULL;
                }
            }
            else {
-               free_public_cert( pkc ); pkc = NULL;
-               log_error("skipped '%s': %s\n", remusr->d, g10_errstr(rc) );
+               free_public_key( pk ); pk = NULL;
+               log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) );
            }
        }
     }
 
-
-    if( !rc && !pkc_list ) {
-       log_error("no valid addressees\n");
+    if( !rc && !any_recipients ) {
+       log_error(_("no valid addressees\n"));
        rc = G10ERR_NO_USER_ID;
     }
 
     if( rc )
-       release_pkc_list( pkc_list );
+       release_pk_list( pk_list );
     else
-       *ret_pkc_list = pkc_list;
+       *ret_pk_list = pk_list;
     return rc;
 }
 
 
+
+static int
+algo_available( int preftype, int algo )
+{
+    if( preftype == PREFTYPE_SYM ) {
+       if( algo == CIPHER_ALGO_TWOFISH )
+           return 0;  /* we don't want to generate Twofish messages for now*/
+       return algo && !check_cipher_algo( algo );
+    }
+    else if( preftype == PREFTYPE_HASH ) {
+       return algo && !check_digest_algo( algo );
+    }
+    else if( preftype == PREFTYPE_COMPR ) {
+       return !algo || algo == 1 || algo == 2;
+    }
+    else
+       return 0;
+}
+
+/****************
+ * Return -1 if we could not find an algorithm.
+ */
+int
+select_algo_from_prefs( PK_LIST pk_list, int preftype )
+{
+    PK_LIST pkr;
+    u32 bits[8];
+    byte *pref = NULL;
+    size_t npref;
+    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( !pkr->pk->local_id ) { /* try to set the local id */
+           query_trust_info( pkr->pk, NULL );
+           if( !pkr->pk->local_id ) {
+               log_debug("select_algo_from_prefs: can't get LID\n");
+               continue;
+           }
+       }
+       if( preftype == PREFTYPE_SYM )
+           mask[0] |= (1<<2); /* 3DES is implicitly there */
+       m_free(pref);
+       pref = get_pref_data( pkr->pk->local_id, pkr->pk->namehash, &npref);
+       any = 0;
+       if( pref ) {
+          #if 0
+           log_hexdump("raw: ", pref, npref );
+          #endif
+           for(i=0; i+1 < npref; i+=2 ) {
+               if( pref[i] == preftype ) {
+                   mask[pref[i+1]/32] |= 1 << (pref[i+1]%32);
+                   any = 1;
+               }
+           }
+       }
+       if( (!pref || !any) && preftype == PREFTYPE_COMPR ) {
+           mask[0] |= 3; /* asume no_compression and old pgp */
+           compr_hack = 1;
+       }
+
+      #if 0
+       log_debug("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("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;
+    if( pref ) {
+       for(j=0; j+1 < npref; j+=2 ) {
+           if( pref[j] == preftype ) {
+               if( (bits[pref[j+1]/32] & (1<<(pref[j+1]%32))) ) {
+                   if( algo_available( preftype, pref[j+1] ) ) {
+                       any = 1;
+                       i = pref[j+1];
+                       break;
+                   }
+               }
+           }
+       }
+    }
+    if( !pref || !any ) {
+       for(j=0; j < 256; j++ )
+           if( (bits[j/32] & (1<<(j%32))) ) {
+               if( algo_available( preftype, j ) ) {
+                   i = j;
+                   break;
+               }
+           }
+    }
+  #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 */
+    }
+
+    m_free(pref);
+    return i;
+}
+
+