See ChangeLog: Mon Sep 6 19:59:08 CEST 1999 Werner Koch
[gnupg.git] / g10 / pkclist.c
index 4f52eeb..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.
 
 #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.
  */
-int
-edit_ownertrust( ulong lid, int mode )
+static int
+do_edit_ownertrust( ulong lid, int mode, unsigned *new_trust, int defer_help )
 {
     char *p;
     int rc;
@@ -52,6 +173,9 @@ edit_ownertrust( ulong lid, int mode )
     u32 keyid[2];
     PKT_public_key *pk ;
     int changed=0;
+    int quit=0;
+    int show=0;
+    int did_help=defer_help;
 
     rc = keyid_from_lid( lid, keyid );
     if( rc ) {
@@ -67,17 +191,25 @@ edit_ownertrust( ulong lid, int mode )
        return 0;
     }
 
-    if( !mode ) {
-       tty_printf(_("No owner trust defined for %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\n");
-    }
-    tty_printf(_(
+
+    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"
@@ -86,20 +218,21 @@ edit_ownertrust( ulong lid, int mode )
 " 3 = I trust marginally\n"
 " 4 = I trust fully\n"
 " s = please show me more information\n") );
-    if( mode )
-       tty_printf(_(" m = back to the main menu\n"));
-    tty_printf("\n");
-
-    for(;;) {
-       /* a string with valid answers */
-       char *ans = _("sSmM");
-
-       if( strlen(ans) != 4 )
+           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(N_("edit_ownertrust.value"),_("Your decision? "));
+       p = cpr_get("edit_ownertrust.value",_("Your decision? "));
        trim_spaces(p);
        cpr_kill_prompt();
-       if( *p && p[1] )
+       if( !*p )
+           did_help = 0;
+       else if( *p && p[1] )
            ;
        else if( !p[1] && (*p >= '1' && *p <= '4') ) {
            unsigned trust;
@@ -110,66 +243,96 @@ edit_ownertrust( ulong lid, int mode )
              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(_("You will see a list of signators etc. here\n"));
+           tty_printf(_(
+               "Certificates leading to an ultimately trusted key:\n"));
+           show = 1;
+           break;
        }
        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(pk);
-    return changed;
+    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_key *pk )
+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( pk );
-    if( rc ) {
-       log_error("Ooops: not in trustdb\n");
-       return -1;
-    }
-
-    lid = pk->local_id;
-    while( !(rc=enum_trust_web( &context, &lid )) ) {
-       rc = get_ownertrust( lid, &trust );
-       if( rc )
-           log_fatal("Ooops: couldn't get owner trust for %lu\n", lid);
-       if( trust == TRUST_UNDEFINED || trust == TRUST_EXPIRED ||
-           trust == TRUST_UNKNOWN ) {
-           if( edit_ownertrust( lid, 0 ) )
-               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 owner trust 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;
 }
 
 /****************
@@ -180,13 +343,26 @@ static int
 do_we_trust( PKT_public_key *pk, int trustlevel )
 {
     int rc;
+    int did_add = 0;
 
+  retry:
     if( (trustlevel & TRUST_FLAG_REVOKED) ) {
-       log_info("key has been revoked!\n");
+       log_info(_("key %08lX: key has been revoked!\n"),
+                                       (ulong)keyid_from_pk( pk, NULL) );
        if( opt.batch )
            return 0;
 
-       if( !cpr_get_answer_is_yes(N_("revoked_key.override"),
+       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;
+
+       if( !cpr_get_answer_is_yes("revoked_key.override",
                                    _("Use this key anyway? ")) )
            return 0;
     }
@@ -194,70 +370,71 @@ do_we_trust( PKT_public_key *pk, int trustlevel )
 
     switch( (trustlevel & TRUST_MASK) ) {
       case TRUST_UNKNOWN: /* No pubkey in trustDB: Insert and check again */
-       rc = insert_trust_record( pk );
+       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( pk, &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();
+       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( pk );
-           if( !rc ) {
-               rc = check_trust( pk, &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( pk, 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.
@@ -265,20 +442,46 @@ do_we_trust( PKT_public_key *pk, int trustlevel )
 static int
 do_we_trust_pre( PKT_public_key *pk, int trustlevel )
 {
-    int rc = do_we_trust( pk, 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;
+
+       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");
 
-    if( !opt.batch && !rc ) {
        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") );
 
-       if( cpr_get_answer_is_yes(N_("untrusted_key.override"),
-                                  _("Use this key anyway? "))  )
+       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 the user about
+        *       his decision the next time he encrypts for this recipient?
+        */
     }
     else if( opt.always_trust && !rc ) {
-       log_info(_("WARNING: Using untrusted key!\n"));
+       if( !opt.quiet )
+           log_info(_("WARNING: Using untrusted key!\n"));
        rc = 1;
     }
     return rc;
@@ -295,9 +498,17 @@ check_signatures_trust( PKT_signature *sig )
 {
     PKT_public_key *pk = m_alloc_clear( sizeof *pk );
     int trustlevel;
-    int dont_try = 0;
+    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");
@@ -305,29 +516,33 @@ check_signatures_trust( PKT_signature *sig )
        goto leave;
     }
 
-  retry:
-    rc = check_trust( pk, &trustlevel );
+    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( pk );
+       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 );
+       rc = check_trust( pk, &trustlevel, NULL, NULL, NULL );
        if( rc )
            log_fatal("trust check after insert failed: %s\n",
                                                      g10_errstr(rc) );
@@ -337,21 +552,24 @@ check_signatures_trust( PKT_signature *sig )
 
       case TRUST_EXPIRED:
        log_info(_("Note: This key has expired!\n"));
+       fpr_info( pk );
        break;
 
       case TRUST_UNDEFINED:
-       if( dont_try || opt.batch || opt.answer_no ) {
+       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 {
-           rc = add_ownertrust( pk );
-           if( rc ) {
-               dont_try = 1;
+           int quit;
+           rc = add_ownertrust( pk, &quit, &trustlevel );
+           if( rc || quit ) {
+               did_add = 1;
                rc = 0;
            }
            goto retry;
@@ -373,6 +591,7 @@ check_signatures_trust( PKT_signature *sig )
        log_info(_(
         "         It is not certain that the signature belongs to the owner.\n"
                 ));
+       fpr_info( pk );
        break;
 
       case TRUST_FULLY:
@@ -405,53 +624,183 @@ release_pk_list( PK_LIST 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)
+{
+    PKT_secret_key *sk;
+    byte fpr[MAX_FINGERPRINT_LEN+1];
+    size_t n;
+    char *p;
+    int i;
+
+    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_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned usage )
+build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use )
 {
     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 )) ) {
+
+               /* 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( !remusr && !opt.batch ) { /* ask */
+    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 = cpr_get(N_("pklist.user_id.enter"),
-                             _("Enter the user ID: "));
-           trim_spaces(answer);
-           cpr_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( pk )
                free_public_key( pk );
            pk = m_alloc_clear( sizeof *pk );
-           pk->pubkey_usage = usage;
-           rc = get_pubkey_byname( pk, answer );
+           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(pk->pubkey_algo, usage)) ) {
-               int trustlevel;
-
-               rc = check_trust( pk, &trustlevel );
-               if( rc ) {
-                   log_error("error checking pk of '%s': %s\n",
-                                                     answer, g10_errstr(rc) );
-               }
-               else if( do_we_trust_pre( pk, trustlevel ) ) {
-                   PK_LIST r;
-
-                   r = m_alloc( sizeof *r );
-                   r->pk = pk; pk = NULL;
-                   r->next = pk_list;
-                   r->mark = 0;
-                   pk_list = r;
+           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 {
+                   int trustlevel;
+
+                   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( pk ) {
@@ -459,33 +808,74 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned usage )
            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 */
 
            pk = m_alloc_clear( sizeof *pk );
-           pk->pubkey_usage = usage;
-           if( (rc = get_pubkey_byname( pk, remusr->d )) ) {
+           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(pk->pubkey_algo, usage )) ) {
+           else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) {
                int trustlevel;
 
-               rc = check_trust( pk, &trustlevel );
+               rc = check_trust( pk, &trustlevel, NULL, NULL, NULL );
                if( rc ) {
                    free_public_key( pk ); pk = NULL;
                    log_error(_("%s: error checking key: %s\n"),
                                                      remusr->d, g10_errstr(rc) );
                }
+               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 */
-                   PK_LIST r;
 
-                   r = m_alloc( sizeof *r );
-                   r->pk = pk; pk = NULL;
-                   r->next = pk_list;
-                   r->mark = 0;
-                   pk_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 pk */
                    free_public_key( pk ); pk = NULL;
@@ -498,8 +888,7 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned usage )
        }
     }
 
-
-    if( !rc && !pk_list ) {
+    if( !rc && !any_recipients ) {
        log_error(_("no valid addressees\n"));
        rc = G10ERR_NO_USER_ID;
     }
@@ -512,6 +901,25 @@ build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned usage )
 }
 
 
+
+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.
  */
@@ -534,15 +942,22 @@ select_algo_from_prefs( PK_LIST pk_list, int preftype )
        u32 mask[8];
 
        memset( mask, 0, 8 * sizeof *mask );
-       if( !pkr->pk->local_id )
-           BUG(); /* if this occurs, we can use get_ownertrust to set it */
+       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 )
-           bits[0] = (1<<2); /* 3DES is implicitly there */
+           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 ) {
-           /*log_hexdump("raw: ", pref, npref );*/
+          #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);
@@ -555,14 +970,18 @@ select_algo_from_prefs( PK_LIST pk_list, int preftype )
            compr_hack = 1;
        }
 
-       /*log_debug("mask=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n",
+      #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]);*/
+            (ulong)mask[3], (ulong)mask[2], (ulong)mask[1], (ulong)mask[0]);
+      #endif
        for(i=0; i < 8; i++ )
            bits[i] &= mask[i];
-       /*log_debug("bits=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n",
+      #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]);*/
+            (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
@@ -575,10 +994,12 @@ select_algo_from_prefs( PK_LIST pk_list, int preftype )
     if( pref ) {
        for(j=0; j+1 < npref; j+=2 ) {
            if( pref[j] == preftype ) {
-               any = 1;
                if( (bits[pref[j+1]/32] & (1<<(pref[j+1]%32))) ) {
-                   i = pref[j+1];
-                   break;
+                   if( algo_available( preftype, pref[j+1] ) ) {
+                       any = 1;
+                       i = pref[j+1];
+                       break;
+                   }
                }
            }
        }
@@ -586,11 +1007,15 @@ select_algo_from_prefs( PK_LIST pk_list, int preftype )
     if( !pref || !any ) {
        for(j=0; j < 256; j++ )
            if( (bits[j/32] & (1<<(j%32))) ) {
-               i = j;
-               break;
+               if( algo_available( preftype, j ) ) {
+                   i = j;
+                   break;
+               }
            }
     }
-    /*log_debug("prefs of type %d: selected %d\n", preftype, 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