* g10.c (main): Use 3DES instead of CAST5 if we don't have CAST5 support.
[gnupg.git] / g10 / g10.c
index 6f7046e..3f1126e 100644 (file)
--- a/g10/g10.c
+++ b/g10/g10.c
 #include <string.h>
 #include <ctype.h>
 #include <unistd.h>
+#include <assert.h>
 #ifdef HAVE_DOSISH_SYSTEM
   #include <fcntl.h> /* for setmode() */
 #endif
+#ifdef HAVE_STAT
+#include <sys/stat.h> /* for stat() */
+#endif
 
 #define INCLUDED_BY_MAIN_MODULE 1
 #include "packet.h"
@@ -61,6 +65,7 @@ enum cmd_and_opt_values { aNull = 0,
     oOutput      = 'o',
     oQuiet       = 'q',
     oRecipient   = 'r',
+    oHiddenRecipient = 'R',
     aSign        = 's',
     oTextmodeShort= 't',
     oUser        = 'u',
@@ -136,6 +141,7 @@ enum cmd_and_opt_values { aNull = 0,
     oAnswerNo,
     oDefCertCheckLevel,
     oKeyring,
+    oPrimaryKeyring,
     oSecretKeyring,
     oShowKeyring,
     oDefaultKey,
@@ -169,6 +175,8 @@ enum cmd_and_opt_values { aNull = 0,
     oNoPGP6,
     oPGP7,
     oNoPGP7,
+    oPGP8,
+    oNoPGP8,
     oCipherAlgo,
     oDigestAlgo,
     oCertDigestAlgo,
@@ -186,6 +194,7 @@ enum cmd_and_opt_values { aNull = 0,
     oTrustDBName,
     oNoSecmemWarn,
     oNoPermissionWarn,
+    oNoMDCWarn,
     oNoArmor,
     oNoDefKeyring,
     oNoGreeting,
@@ -199,6 +208,8 @@ enum cmd_and_opt_values { aNull = 0,
     oCompressKeys,
     oCompressSigs,
     oAlwaysTrust,
+    oTrustModel,
+    oForceOwnertrust,
     oEmuChecksumBug,
     oRunAsShmCP,
     oSetFilename,
@@ -213,6 +224,7 @@ enum cmd_and_opt_values { aNull = 0,
     oComment,
     oDefaultComment,
     oThrowKeyid,
+    oNoThrowKeyid,
     oShowPhotos,
     oNoShowPhotos,
     oPhotoViewer,
@@ -237,10 +249,12 @@ enum cmd_and_opt_values { aNull = 0,
     oLockNever,
     oKeyServer,
     oKeyServerOptions,
+    oImportOptions,
     oExportOptions,
     oTempDir,
     oExecPath,
     oEncryptTo,
+    oHiddenEncryptTo,
     oNoEncryptTo,
     oLoggerFD,
 #ifdef __riscos__
@@ -262,8 +276,9 @@ enum cmd_and_opt_values { aNull = 0,
     oFastListMode,
     oListOnly,
     oIgnoreTimeConflict,
-    oIgnoreValidFrom,                         
-    oIgnoreCrcError,                          
+    oIgnoreValidFrom,
+    oIgnoreCrcError,
+    oIgnoreMDCError,
     oShowSessionKey,
     oOverrideSessionKey,
     oNoRandomSeedFile,
@@ -286,7 +301,6 @@ enum cmd_and_opt_values { aNull = 0,
     oPersonalCipherPreferences,
     oPersonalDigestPreferences,
     oPersonalCompressPreferences,
-    oEmu3DESS2KBug,  /* will be removed in 1.1 */
     oEmuMDEncodeBug,
     oDisplay,
     oTTYname,
@@ -294,6 +308,10 @@ enum cmd_and_opt_values { aNull = 0,
     oLCctype,
     oLCmessages,
     oGroup,
+    oStrict,
+    oNoStrict,
+    oMangleDosFilenames,
+    oNoMangleDosFilenames,
 aTest };
 
 
@@ -364,6 +382,7 @@ static ARGPARSE_OPTS opts[] = {
     { oArmor, "armor",     0, N_("create ascii armored output")},
     { oArmor, "armour",     0, "@" },
     { oRecipient, "recipient", 2, N_("|NAME|encrypt for NAME")},
+    { oHiddenRecipient, "hidden-recipient", 2, "@" },
     { oRecipient, "remote-user", 2, "@"},  /* old option name */
     { oDefRecipient, "default-recipient" ,2,
                                  N_("|NAME|use NAME as default recipient")},
@@ -373,6 +392,7 @@ static ARGPARSE_OPTS opts[] = {
     { oTempDir, "temp-directory", 2, "@" },
     { oExecPath, "exec-path", 2, "@" },
     { oEncryptTo, "encrypt-to", 2, "@" },
+    { oHiddenEncryptTo, "hidden-encrypt-to", 2, "@" },
     { oNoEncryptTo, "no-encrypt-to", 0, "@" },
     { oUser, "local-user",2, N_("use this user-id to sign or decrypt")},
     { oCompress, NULL,       1, N_("|N|set compress level N (0 disables)") },
@@ -397,7 +417,7 @@ static ARGPARSE_OPTS opts[] = {
     { oDisableMDC, "disable-mdc", 0, N_("never use a MDC for encryption") },
     { oNoDisableMDC, "no-disable-mdc", 0, "@" },
     { oDryRun, "dry-run",   0, N_("do not make any changes") },
-  /*{ oInteractive, "interactive", 0, N_("prompt before overwriting") }, */
+    { oInteractive, "interactive", 0, N_("prompt before overwriting") },
     { oUseAgent, "use-agent",0, N_("use the gpg-agent")},
     { oNoUseAgent, "no-use-agent",0, "@"},
     { oGpgAgentInfo, "gpg-agent-info",2, "@"},
@@ -405,11 +425,13 @@ static ARGPARSE_OPTS opts[] = {
     { oAnswerYes, "yes",       0, N_("assume yes on most questions")},
     { oAnswerNo,  "no",        0, N_("assume no on most questions")},
     { oKeyring, "keyring"   ,2, N_("add this keyring to the list of keyrings")},
+    { oPrimaryKeyring, "primary-keyring",2, "@" },
     { oSecretKeyring, "secret-keyring" ,2, N_("add this secret keyring to the list")},
     { oShowKeyring, "show-keyring", 0, N_("show which keyring a listed key is on")},
     { oDefaultKey, "default-key" ,2, N_("|NAME|use NAME as default secret key")},
     { oKeyServer, "keyserver",2, N_("|HOST|use this keyserver to lookup keys")},
     { oKeyServerOptions, "keyserver-options",2,"@"},
+    { oImportOptions, "import-options",2,"@"},
     { oExportOptions, "export-options",2,"@"},
     { oCharset, "charset"   , 2, N_("|NAME|set terminal charset to NAME") },
     { oOptions, "options"   , 2, N_("read options from file")},
@@ -440,6 +462,8 @@ static ARGPARSE_OPTS opts[] = {
     { oNoPGP6, "no-pgp6", 0, "@"},
     { oPGP7, "pgp7", 0, "@"},
     { oNoPGP7, "no-pgp7", 0, "@"},
+    { oPGP8, "pgp8", 0, "@"},
+    { oNoPGP8, "no-pgp8", 0, "@"},
     { oS2KMode, "s2k-mode",  1, N_("|N|use passphrase mode N")},
     { oS2KDigest, "s2k-digest-algo",2,
                N_("|NAME|use message digest algorithm NAME for passphrases")},
@@ -449,8 +473,9 @@ static ARGPARSE_OPTS opts[] = {
     { oCipherAlgo, "cipher-algo", 2 , N_("|NAME|use cipher algorithm NAME")},
     { oDigestAlgo, "digest-algo", 2 , N_("|NAME|use message digest algorithm NAME")},
     { oCertDigestAlgo, "cert-digest-algo", 2 , "@" },
-    { oCompressAlgo, "compress-algo", 1 , N_("|N|use compress algorithm N")},
+    { oCompressAlgo,"compress-algo",2,N_("|NAME|use compression algorithm NAME")},
     { oThrowKeyid, "throw-keyid", 0, N_("throw keyid field of encrypted packets")},
+    { oNoThrowKeyid, "no-throw-keyid", 0, "@" },
     { oShowPhotos,   "show-photos", 0, N_("Show Photo IDs")},
     { oNoShowPhotos, "no-show-photos", 0, N_("Don't show Photo IDs")},
     { oPhotoViewer,  "photo-viewer", 2, N_("Set command line to view Photo IDs")},
@@ -473,7 +498,8 @@ static ARGPARSE_OPTS opts[] = {
     { aExportOwnerTrust, "list-ownertrust",0 , "@"},  /* alias */
     { aPrintMDs, "print-mds" , 256, "@"}, /* old */
     { aListTrustDB, "list-trustdb",0 , "@"},
-    { aListTrustPath, "list-trust-path",0, "@"},
+    /* Not yet used */
+    /* { aListTrustPath, "list-trust-path",0, "@"}, */
     { aPipeMode,  "pipemode", 0, "@" },
     { oKOption, NULL,   0, "@"},
     { oPasswdFD, "passphrase-fd",1, "@" },
@@ -489,6 +515,7 @@ static ARGPARSE_OPTS opts[] = {
     { oTrustDBName, "trustdb-name", 2, "@" },
     { oNoSecmemWarn, "no-secmem-warning", 0, "@" }, /* used only by regression tests */
     { oNoPermissionWarn, "no-permission-warning", 0, "@" },
+    { oNoMDCWarn, "no-mdc-warning", 0, "@" },
     { oNoArmor, "no-armor",   0, "@"},
     { oNoArmor, "no-armour",   0, "@"},
     { oNoDefKeyring, "no-default-keyring", 0, "@" },
@@ -506,6 +533,8 @@ static ARGPARSE_OPTS opts[] = {
     { oCompressSigs, "compress-sigs",0, "@"},
     { oDefCertCheckLevel, "default-cert-check-level", 1, "@"},
     { oAlwaysTrust, "always-trust", 0, "@"},
+    { oTrustModel, "trust-model", 2, "@"},
+    { oForceOwnertrust, "force-ownertrust", 1, "@"},
     { oEmuChecksumBug, "emulate-checksum-bug", 0, "@"},
     { oRunAsShmCP, "run-as-shm-coprocess", 4, "@" },
     { oSetFilename, "set-filename", 2, "@" },
@@ -551,6 +580,7 @@ static ARGPARSE_OPTS opts[] = {
     { oIgnoreTimeConflict, "ignore-time-conflict", 0, "@" },
     { oIgnoreValidFrom,    "ignore-valid-from",    0, "@" },
     { oIgnoreCrcError, "ignore-crc-error", 0,"@" },
+    { oIgnoreMDCError, "ignore-mdc-error", 0,"@" },
     { oShowSessionKey, "show-session-key", 0, "@" },
     { oOverrideSessionKey, "override-session-key", 2, "@" },
     { oNoRandomSeedFile,  "no-random-seed-file", 0, "@" },
@@ -572,7 +602,6 @@ static ARGPARSE_OPTS opts[] = {
     { oPersonalCipherPreferences,  "personal-cipher-preferences", 2, "@"},
     { oPersonalDigestPreferences,  "personal-digest-preferences", 2, "@"},
     { oPersonalCompressPreferences,  "personal-compress-preferences", 2, "@"},
-    { oEmu3DESS2KBug,  "emulate-3des-s2k-bug", 0, "@"},
     { oEmuMDEncodeBug, "emulate-md-encode-bug", 0, "@"},
     { oDisplay,    "display",     2, "@" },
     { oTTYname,    "ttyname",     2, "@" },
@@ -580,6 +609,10 @@ static ARGPARSE_OPTS opts[] = {
     { oLCctype,    "lc-ctype",    2, "@" },
     { oLCmessages, "lc-messages", 2, "@" },
     { oGroup,      "group",       2, "@" },
+    { oStrict,     "strict",      0, "@" },
+    { oNoStrict,   "no-strict",   0, "@" },
+    { oMangleDosFilenames, "mangle-dos-filenames", 0, "@" },
+    { oNoMangleDosFilenames, "no-mangle-dos-filenames", 0, "@" },
 {0} };
 
 
@@ -593,7 +626,6 @@ static char *build_list( const char *text, char letter,
                         const char *(*mapf)(int), int (*chkf)(int) );
 static void set_cmd( enum cmd_and_opt_values *ret_cmd,
                        enum cmd_and_opt_values new_cmd );
-static void print_hex( byte *p, size_t n );
 static void print_mds( const char *fname, int algo );
 static void add_notation_data( const char *string, int which );
 static void add_policy_url( const char *string, int which );
@@ -771,6 +803,17 @@ set_debug(void)
 }
 
 
+/* We need the home directory also in some other directories, so make
+   sure that both variables are always in sync. */
+static void
+set_homedir (char *dir)
+{
+  if (!dir)
+    dir = "";
+  g10_opt_homedir = opt.homedir = dir;
+}
+
+
 static void
 set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd )
 {
@@ -807,16 +850,21 @@ static void add_group(char *string)
   STRLIST values=NULL;
 
   /* Break off the group name */
-  name=strsep(&string," ");
+  name=strsep(&string,"=");
   if(string==NULL)
     {
-      log_error(_("no values for group \"%s\"\n"),name);
+      log_error(_("no = sign found in group definition \"%s\"\n"),name);
       return;
     }
 
+  trim_trailing_ws(name,strlen(name));
+
   /* Break apart the values */
-  while((value=strsep(&string," ")) && *value!='\0')
-    add_to_strlist2(&values,value,utf8_strings);
+  while ((value= strsep(&string," \t")))
+    {
+      if (*value)
+        add_to_strlist2 (&values,value,utf8_strings);
+    }
 
   item=m_alloc(sizeof(struct groupitem));
   item->name=name;
@@ -826,6 +874,209 @@ static void add_group(char *string)
   opt.grouplist=item;
 }
 
+/* We need to check three things.
+
+   0) The homedir.  It must be x00, a directory, and owned by the
+   user.
+
+   1) The options file.  Okay unless it or its containing directory is
+   group or other writable or not owned by us.  disable exec in this
+   case.
+
+   2) Extensions.  Same as #2.
+
+   Returns true if the item is unsafe. */
+static int
+check_permissions(const char *path,int item)
+{
+#if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM)
+  static int homedir_cache=-1;
+  char *tmppath,*dir;
+  struct stat statbuf,dirbuf;
+  int homedir=0,ret=0,checkonly=0;
+  int perm=0,own=0,enc_dir_perm=0,enc_dir_own=0;
+
+  if(opt.no_perm_warn)
+    return 0;
+
+  assert(item==0 || item==1 || item==2);
+
+  /* extensions may attach a path */
+  if(item==2 && path[0]!=DIRSEP_C)
+    {
+      if(strchr(path,DIRSEP_C))
+       tmppath=make_filename(path,NULL);
+      else
+       tmppath=make_filename(GNUPG_LIBDIR,path,NULL);
+    }
+  else
+    tmppath=m_strdup(path);
+
+  /* If the item is located in the homedir, but isn't the homedir,
+     don't continue if we already checked the homedir itself.  This is
+     to avoid user confusion with an extra options file warning which
+     could be rectified if the homedir itself had proper
+     permissions. */
+  if(item!=0 && homedir_cache>-1
+     && ascii_strncasecmp(opt.homedir,tmppath,strlen(opt.homedir))==0)
+    {
+      ret=homedir_cache;
+      goto end;
+    }
+
+  /* It's okay if the file or directory doesn't exist */
+  if(stat(tmppath,&statbuf)!=0)
+    {
+      ret=0;
+      goto end;
+    }
+
+  /* Now check the enclosing directory.  Theoretically, we could walk
+     this test up to the root directory /, but for the sake of sanity,
+     I'm stopping at one level down. */
+  dir=make_dirname(tmppath);
+
+  if(stat(dir,&dirbuf)!=0 || !S_ISDIR(dirbuf.st_mode))
+    {
+      /* Weird error */
+      ret=1;
+      goto end;
+    }
+
+  m_free(dir);
+
+  /* Assume failure */
+  ret=1;
+
+  if(item==0)
+    {
+      /* The homedir must be x00, a directory, and owned by the user. */
+
+      if(S_ISDIR(statbuf.st_mode))
+       {
+         if(statbuf.st_uid==getuid())
+           {
+             if((statbuf.st_mode & (S_IRWXG|S_IRWXO))==0)
+               ret=0;
+             else
+               perm=1;
+           }
+         else
+           own=1;
+
+         homedir_cache=ret;
+       }
+    }
+  else if(item==1 || item==2)
+    {
+      /* The options or extension file.  Okay unless it or its
+        containing directory is group or other writable or not owned
+        by us or root. */
+
+      if(S_ISREG(statbuf.st_mode))
+       {
+         if(statbuf.st_uid==getuid() || statbuf.st_uid==0)
+           {
+             if((statbuf.st_mode & (S_IWGRP|S_IWOTH))==0)
+               {
+                 /* it's not writable, so make sure the enclosing
+                     directory is also not writable */
+                 if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0)
+                   {
+                     if((dirbuf.st_mode & (S_IWGRP|S_IWOTH))==0)
+                       ret=0;
+                     else
+                       enc_dir_perm=1;
+                   }
+                 else
+                   enc_dir_own=1;
+               }
+             else
+               {
+                 /* it's writable, so the enclosing directory had
+                     better not let people get to it. */
+                 if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0)
+                   {
+                     if((dirbuf.st_mode & (S_IRWXG|S_IRWXO))==0)
+                       ret=0;
+                     else
+                       perm=enc_dir_perm=1; /* unclear which one to fix! */
+                   }
+                 else
+                   enc_dir_own=1;
+               }
+           }
+         else
+           own=1;
+       }
+    }
+  else
+    BUG();
+
+  if(!checkonly)
+    {
+      if(own)
+       {
+         if(item==0)
+           log_info(_("WARNING: unsafe ownership on "
+                      "homedir \"%s\"\n"),tmppath);
+         else if(item==1)
+           log_info(_("WARNING: unsafe ownership on "
+                      "configuration file \"%s\"\n"),tmppath);
+         else
+           log_info(_("WARNING: unsafe ownership on "
+                      "extension \"%s\"\n"),tmppath);
+       }
+      if(perm)
+       {
+         if(item==0)
+           log_info(_("WARNING: unsafe permissions on "
+                      "homedir \"%s\"\n"),tmppath);
+         else if(item==1)
+           log_info(_("WARNING: unsafe permissions on "
+                      "configuration file \"%s\"\n"),tmppath);
+         else
+           log_info(_("WARNING: unsafe permissions on "
+                      "extension \"%s\"\n"),tmppath);
+       }
+      if(enc_dir_own)
+       {
+         if(item==0)
+           log_info(_("WARNING: unsafe enclosing directory ownership on "
+                      "homedir \"%s\"\n"),tmppath);
+         else if(item==1)
+           log_info(_("WARNING: unsafe enclosing directory ownership on "
+                      "configuration file \"%s\"\n"),tmppath);
+         else
+           log_info(_("WARNING: unsafe enclosing directory ownership on "
+                      "extension \"%s\"\n"),tmppath);
+       }
+      if(enc_dir_perm)
+       {
+         if(item==0)
+           log_info(_("WARNING: unsafe enclosing directory permissions on "
+                      "homedir \"%s\"\n"),tmppath);
+         else if(item==1)
+           log_info(_("WARNING: unsafe enclosing directory permissions on "
+                      "configuration file \"%s\"\n"),tmppath);
+         else
+           log_info(_("WARNING: unsafe enclosing directory permissions on "
+                      "extension \"%s\"\n"),tmppath);
+       }
+    }
+
+ end:
+  m_free(tmppath);
+
+  if(homedir)
+    homedir_cache=ret;
+
+  return ret;
+
+#endif /* HAVE_STAT && !HAVE_DOSISH_SYSTEM */
+
+  return 0;
+}
 
 int
 main( int argc, char **argv )
@@ -837,8 +1088,6 @@ main( int argc, char **argv )
     char **orig_argv;
     const char *fname;
     char *username;
-    STRLIST unsafe_files=NULL;
-    STRLIST extensions=NULL;
     int may_coredump;
     STRLIST sl, remusr= NULL, locusr=NULL;
     STRLIST nrings=NULL, sec_nrings=NULL;
@@ -857,6 +1106,7 @@ main( int argc, char **argv )
     const char *trustdb_name = NULL;
     char *def_cipher_string = NULL;
     char *def_digest_string = NULL;
+    char *def_compress_string = NULL;
     char *cert_digest_string = NULL;
     char *s2k_cipher_string = NULL;
     char *s2k_digest_string = NULL;
@@ -897,25 +1147,36 @@ main( int argc, char **argv )
     opt.def_compress_algo = -1;
     opt.s2k_mode = 3; /* iterated+salted */
     opt.s2k_digest_algo = DIGEST_ALGO_SHA1;
+#ifdef USE_CAST5
     opt.s2k_cipher_algo = CIPHER_ALGO_CAST5;
+#else
+    opt.s2k_cipher_algo = CIPHER_ALGO_3DES;
+#endif
     opt.completes_needed = 1;
     opt.marginals_needed = 3;
     opt.max_cert_depth = 5;
     opt.pgp2_workarounds = 1;
     opt.force_v3_sigs = 1;
     opt.escape_from = 1;
-    opt.export_options=EXPORT_DEFAULT;
-    opt.keyserver_options.export_options=EXPORT_DEFAULT;
+    opt.import_options=IMPORT_SK2PK;
+    opt.export_options=
+      EXPORT_INCLUDE_NON_RFC|EXPORT_INCLUDE_ATTRIBUTES;
+    opt.keyserver_options.import_options=IMPORT_REPAIR_HKP_SUBKEY_BUG;
+    opt.keyserver_options.export_options=
+      EXPORT_INCLUDE_NON_RFC|EXPORT_INCLUDE_ATTRIBUTES;
     opt.keyserver_options.include_subkeys=1;
-    opt.keyserver_options.include_attributes=1;
-#if defined (__MINGW32__) || defined (__CYGWIN32__)
-    opt.homedir = read_w32_registry_string( NULL, "Software\\GNU\\GnuPG", "HomeDir" );
+    opt.keyserver_options.include_revoked=1;
+    opt.trust_model=TM_OPENPGP;
+    opt.mangle_dos_filenames = 1;
+
+#if defined (__MINGW32__)
+    set_homedir ( read_w32_registry_string( NULL,
+                                    "Software\\GNU\\GnuPG", "HomeDir" ));
 #else
-    opt.homedir = getenv("GNUPGHOME");
+    set_homedir ( getenv("GNUPGHOME") );
 #endif
-    if( !opt.homedir || !*opt.homedir ) {
-       opt.homedir = GNUPG_HOMEDIR;
-    }
+    if( !*opt.homedir )
+       set_homedir ( GNUPG_HOMEDIR );
 
     /* check whether we have a config file on the commandline */
     orig_argc = argc;
@@ -935,7 +1196,19 @@ main( int argc, char **argv )
        else if( pargs.r_opt == oNoOptions )
            default_config = 0; /* --no-options */
        else if( pargs.r_opt == oHomedir )
-           opt.homedir = pargs.r.ret_str;
+           set_homedir ( pargs.r.ret_str );
+       else if( pargs.r_opt == oNoPermissionWarn )
+           opt.no_perm_warn=1;
+       else if (pargs.r_opt == oStrict )
+         {
+           opt.strict=1;
+           log_set_strict(1);
+         }
+       else if (pargs.r_opt == oNoStrict )
+         {
+           opt.strict=0;
+           log_set_strict(0);
+         }
       #ifdef USE_SHM_COPROCESSING
        else if( pargs.r_opt == oRunAsShmCP ) {
            /* does not make sense in a options file, we do it here,
@@ -958,7 +1231,7 @@ main( int argc, char **argv )
         for (d=buf,s=opt.homedir; *s; s++)
             *d++ = *s == '\\'? '/': *s;
         *d = 0;
-        opt.homedir = buf;
+        set_homedir (buf);
     }
 #endif
 #ifdef USE_SHM_COPROCESSING
@@ -971,21 +1244,37 @@ main( int argc, char **argv )
     maybe_setuid = 0;
     /* Okay, we are now working under our real uid */
 
-    if( default_config )
-       configname = make_filename(opt.homedir, "options", NULL );
+    set_native_charset (NULL); /* Try to auto set the character set */
 
+    if( default_config )
+      {
+       configname = make_filename(opt.homedir, "gpg" EXTSEP_S "conf", NULL );
+        if (!access (configname, R_OK))
+          { /* Print a warning when both config files are present. */
+            char *p = make_filename(opt.homedir, "options", NULL );
+            if (!access (p, R_OK))
+              log_info (_("NOTE: old default options file `%s' ignored\n"), p);
+            m_free (p);
+          }
+        else
+          { /* Keep on using the old default one. */
+            m_free (configname);
+            configname = make_filename(opt.homedir, "options", NULL );
+          }
+      }
     argc = orig_argc;
     argv = orig_argv;
     pargs.argc = &argc;
     pargs.argv = &argv;
     pargs.flags=  1;  /* do not remove the args */
+
+    /* By this point we have a homedir, and cannot change it. */
+    check_permissions(opt.homedir,0);
+
   next_pass:
     if( configname ) {
-
-      if(check_permissions(configname,0,1))
+      if(check_permissions(configname,1))
        {
-         add_to_strlist(&unsafe_files,configname);
-
          /* If any options file is unsafe, then disable any external
             programs for keyserver calls or photo IDs.  Since the
             external program to call is set in the options file, a
@@ -1093,7 +1382,7 @@ main( int argc, char **argv )
             opt.use_agent = 1;
 #else /* __riscos__ */
             opt.use_agent = 0;
-            not_implemented("use-agent");
+            riscos_not_implemented("use-agent");
 #endif /* __riscos__ */
             break;
           case oNoUseAgent: opt.use_agent = 0; break;
@@ -1101,6 +1390,10 @@ main( int argc, char **argv )
          case oAnswerYes: opt.answer_yes = 1; break;
          case oAnswerNo: opt.answer_no = 1; break;
          case oKeyring: append_to_strlist( &nrings, pargs.r.ret_str); break;
+         case oPrimaryKeyring:
+           sl=append_to_strlist( &nrings, pargs.r.ret_str);
+           sl->flags=2;
+           break;
          case oShowKeyring: opt.show_keyring = 1; break;
          case oDebug: opt.debug |= pargs.r.ret_ulong; break;
          case oDebugAll: opt.debug = ~0; break;
@@ -1109,7 +1402,7 @@ main( int argc, char **argv )
             break;
 #ifdef __riscos__
          case oStatusFile:
-            set_status_fd( iobuf_translate_file_handle ( fdopenfile (pargs.r.ret_str, 1), 1) );
+            set_status_fd( iobuf_translate_file_handle ( riscos_fdopenfile (pargs.r.ret_str, 1), 1) );
             break;
 #endif /* __riscos__ */
          case oAttributeFD:
@@ -1117,7 +1410,7 @@ main( int argc, char **argv )
             break;
 #ifdef __riscos__
          case oAttributeFile:
-            set_attrib_fd(iobuf_translate_file_handle ( fdopenfile (pargs.r.ret_str, 1), 1) );
+            set_attrib_fd(iobuf_translate_file_handle ( riscos_fdopenfile (pargs.r.ret_str, 1), 1) );
             break;
 #endif /* __riscos__ */
          case oLoggerFD:
@@ -1127,7 +1420,7 @@ main( int argc, char **argv )
 #ifdef __riscos__
          case oLoggerFile:
             log_set_logfile( NULL,
-                             iobuf_translate_file_handle ( fdopenfile (pargs.r.ret_str, 1), 1) );
+                             iobuf_translate_file_handle ( riscos_fdopenfile (pargs.r.ret_str, 1), 1) );
             break;
 #endif /* __riscos__ */
          case oWithFingerprint:
@@ -1178,29 +1471,58 @@ main( int argc, char **argv )
          case oWithColons: opt.with_colons=':'; break;
 
          case oSkipVerify: opt.skip_verify=1; break;
-         case oCompressAlgo: opt.def_compress_algo = pargs.r.ret_int; break;
          case oCompressKeys: opt.compress_keys = 1; break;
          case aListSecretKeys: set_cmd( &cmd, aListSecretKeys); break;
-         case oAlwaysTrust: opt.always_trust = 1; break;
+           /* There are many programs (like mutt) that call gpg with
+              --always-trust so keep this option around for a long
+              time. */
+         case oAlwaysTrust: opt.trust_model=TM_ALWAYS; break;
+         case oTrustModel:
+           if(ascii_strcasecmp(pargs.r.ret_str,"openpgp")==0)
+             opt.trust_model=TM_OPENPGP;
+           else if(ascii_strcasecmp(pargs.r.ret_str,"classic")==0)
+             opt.trust_model=TM_CLASSIC;
+           else if(ascii_strcasecmp(pargs.r.ret_str,"always")==0)
+             opt.trust_model=TM_ALWAYS;
+           else
+             log_error("unknown trust model \"%s\"\n",pargs.r.ret_str);
+           break;
+         case oForceOwnertrust:
+           log_info(_("NOTE: %s is not for normal use!\n"),
+                    "--force-ownertrust");
+           if(pargs.r.ret_int>=TRUST_UNDEFINED
+              && pargs.r.ret_int<=TRUST_ULTIMATE)
+             opt.force_ownertrust=pargs.r.ret_int;
+           else
+             log_error("invalid ownertrust %d\n",pargs.r.ret_int);
+           break;
          case oLoadExtension:
 #ifndef __riscos__
-           add_to_strlist(&extensions,pargs.r.ret_str);
-           register_cipher_extension(orig_argc? *orig_argv:NULL,
-                                     pargs.r.ret_str);
+#if defined(USE_DYNAMIC_LINKING) || defined(__MINGW32__)
+           if(check_permissions(pargs.r.ret_str,2))
+             log_info(_("cipher extension \"%s\" not loaded due to "
+                        "unsafe permissions\n"),pargs.r.ret_str);
+           else
+             register_cipher_extension(orig_argc? *orig_argv:NULL,
+                                       pargs.r.ret_str);
+#endif
 #else /* __riscos__ */
-            not_implemented("load-extension");
+            riscos_not_implemented("load-extension");
 #endif /* __riscos__ */
            break;
          case oRFC1991:
            opt.rfc1991 = 1;
            opt.rfc2440 = 0;
            opt.force_v4_certs = 0;
-           opt.sk_comments = 0;
+           opt.disable_mdc = 1;
            opt.escape_from = 1;
            break;
          case oOpenPGP:
+           /* TODO: When 2440bis becomes a RFC, these may need
+               changing. */
            opt.rfc1991 = 0;
            opt.rfc2440 = 1;
+           opt.disable_mdc = 1;
            opt.allow_non_selfsigned_uid = 1;
            opt.allow_freeform_uid = 1;
            opt.pgp2_workarounds = 0;
@@ -1212,10 +1534,10 @@ main( int argc, char **argv )
            opt.def_cipher_algo = 0;
            opt.def_digest_algo = 0;
            opt.cert_digest_algo = 0;
-           opt.def_compress_algo = 1;
+           opt.def_compress_algo = -1;
             opt.s2k_mode = 3; /* iterated+salted */
            opt.s2k_digest_algo = DIGEST_ALGO_SHA1;
-           opt.s2k_cipher_algo = CIPHER_ALGO_CAST5;
+           opt.s2k_cipher_algo = CIPHER_ALGO_3DES;
            break;
          case oPGP2: opt.pgp2 = 1; break;
          case oNoPGP2: opt.pgp2 = 0; break;
@@ -1223,19 +1545,19 @@ main( int argc, char **argv )
          case oNoPGP6: opt.pgp6 = 0; break;
          case oPGP7: opt.pgp7 = 1; break;
          case oNoPGP7: opt.pgp7 = 0; break;
-         case oEmuChecksumBug: opt.emulate_bugs |= EMUBUG_GPGCHKSUM; break;
-         case oEmu3DESS2KBug:  opt.emulate_bugs |= EMUBUG_3DESS2K; break;
+         case oPGP8: opt.pgp8 = 1; break;
+         case oNoPGP8: opt.pgp8 = 0; break;
          case oEmuMDEncodeBug: opt.emulate_bugs |= EMUBUG_MDENCODE; break;
          case oCompressSigs: opt.compress_sigs = 1; break;
          case oRunAsShmCP:
 #ifndef __riscos__
-         #ifndef USE_SHM_COPROCESSING
+ifndef USE_SHM_COPROCESSING
            /* not possible in the option file,
             * but we print the warning here anyway */
            log_error("shared memory coprocessing is not available\n");
-         #endif
+endif
 #else /* __riscos__ */
-            not_implemented("run-as-shm-coprocess");
+            riscos_not_implemented("run-as-shm-coprocess");
 #endif /* __riscos__ */
            break;
          case oSetFilename: opt.set_filename = pargs.r.ret_str; break;
@@ -1253,6 +1575,7 @@ main( int argc, char **argv )
          case oComment: opt.comment_string = pargs.r.ret_str; break;
          case oDefaultComment: opt.comment_string = NULL; break;
          case oThrowKeyid: opt.throw_keyid = 1; break;
+         case oNoThrowKeyid: opt.throw_keyid = 0; break;
          case oShowPhotos: opt.show_photos = 1; break;
          case oNoShowPhotos: opt.show_photos = 0; break;
          case oPhotoViewer: opt.photo_viewer = pargs.r.ret_str; break;
@@ -1273,10 +1596,19 @@ main( int argc, char **argv )
            sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
            sl->flags = 1;
            break;
+         case oHiddenEncryptTo: /* store the recipient in the second list */
+           sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
+           sl->flags = 1|2;
+           break;
          case oRecipient: /* store the recipient */
            add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
             any_explicit_recipient = 1;
            break;
+         case oHiddenRecipient: /* store the recipient with a flag */
+           sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
+           sl->flags = 2;
+            any_explicit_recipient = 1;
+           break;
          case oTextmodeShort: opt.textmode = 2; break;
          case oTextmode: opt.textmode=1;  break;
          case oExpert: opt.expert = 1; break;
@@ -1294,7 +1626,7 @@ main( int argc, char **argv )
             break;
 #ifdef __riscos__
          case oPasswdFile:
-            pwfd = iobuf_translate_file_handle ( fdopenfile (pargs.r.ret_str, 0), 0);
+            pwfd = iobuf_translate_file_handle ( riscos_fdopenfile (pargs.r.ret_str, 0), 0);
             break;
 #endif /* __riscos__ */
          case oCommandFD:
@@ -1302,15 +1634,40 @@ main( int argc, char **argv )
             break;
 #ifdef __riscos__
          case oCommandFile:
-            opt.command_fd = iobuf_translate_file_handle ( fdopenfile (pargs.r.ret_str, 0), 0);
+            opt.command_fd = iobuf_translate_file_handle ( riscos_fdopenfile (pargs.r.ret_str, 0), 0);
             break;
 #endif /* __riscos__ */
          case oCipherAlgo: def_cipher_string = m_strdup(pargs.r.ret_str); break;
          case oDigestAlgo: def_digest_string = m_strdup(pargs.r.ret_str); break;
+         case oCompressAlgo:
+           /* If it is all digits, stick a Z in front of it for
+              later.  This is for backwards compatibility with
+              versions that took the compress algorithm number. */
+           {
+             char *pt=pargs.r.ret_str;
+             while(*pt)
+               {
+                 if(!isdigit(*pt))
+                   break;
+
+                 pt++;
+               }
+
+             if(*pt=='\0')
+               {
+                 def_compress_string=m_alloc(strlen(pargs.r.ret_str)+2);
+                 strcpy(def_compress_string,"Z");
+                 strcat(def_compress_string,pargs.r.ret_str);
+               }
+             else
+               def_compress_string = m_strdup(pargs.r.ret_str);
+           }
+           break;
          case oCertDigestAlgo: cert_digest_string = m_strdup(pargs.r.ret_str); break;
          case oNoSecmemWarn: secmem_set_flags( secmem_get_flags() | 1 ); break;
          case oNoPermissionWarn: opt.no_perm_warn=1; break;
-         case oCharset:
+         case oNoMDCWarn: opt.no_mdc_warn=1; break;
+          case oCharset:
            if( set_native_charset( pargs.r.ret_str ) )
                log_error(_("%s is not a valid character set\n"),
                                                    pargs.r.ret_str);
@@ -1324,7 +1681,7 @@ main( int argc, char **argv )
 #ifndef __riscos__
            opt.lock_once = 0;
 #else /* __riscos__ */
-            not_implemented("lock-multiple");
+            riscos_not_implemented("lock-multiple");
 #endif /* __riscos__ */
             break;
          case oKeyServer:
@@ -1335,6 +1692,16 @@ main( int argc, char **argv )
          case oKeyServerOptions:
            parse_keyserver_options(pargs.r.ret_str);
            break;
+         case oImportOptions:
+           if(!parse_import_options(pargs.r.ret_str,&opt.import_options))
+             {
+               if(configname)
+                 log_error(_("%s:%d: invalid import options\n"),
+                           configname,configlineno);
+               else
+                 log_error(_("invalid import options\n"));
+             }
+           break;
          case oExportOptions:
            if(!parse_export_options(pargs.r.ret_str,&opt.export_options))
              {
@@ -1347,10 +1714,10 @@ main( int argc, char **argv )
            break;
          case oTempDir: opt.temp_dir=pargs.r.ret_str; break;
          case oExecPath:
-#ifndef FIXED_EXEC_PATH
-           if(set_exec_path(pargs.r.ret_str))
+           if(set_exec_path(pargs.r.ret_str,0))
              log_error(_("unable to set exec-path to %s\n"),pargs.r.ret_str);
-#endif
+           else
+             opt.exec_path_set=1;
            break;
          case oNotation:
            add_notation_data( pargs.r.ret_str, 0 );
@@ -1389,6 +1756,7 @@ main( int argc, char **argv )
          case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break;
          case oIgnoreValidFrom: opt.ignore_valid_from = 1; break;
          case oIgnoreCrcError: opt.ignore_crc_error = 1; break;
+         case oIgnoreMDCError: opt.ignore_mdc_error = 1; break;
          case oNoRandomSeedFile: use_random_seed = 0; break;
          case oAutoKeyRetrieve:
          case oNoAutoKeyRetrieve:
@@ -1433,6 +1801,12 @@ main( int argc, char **argv )
           case oLCctype: opt.lc_ctype = pargs.r.ret_str; break;
           case oLCmessages: opt.lc_messages = pargs.r.ret_str; break;
          case oGroup: add_group(pargs.r.ret_str); break;
+         case oStrict: opt.strict=1; log_set_strict(1); break;
+         case oNoStrict: opt.strict=0; log_set_strict(0); break;
+
+          case oMangleDosFilenames: opt.mangle_dos_filenames = 1; break;
+          case oNoMangleDosFilenames: opt.mangle_dos_filenames = 0; break;
+
          default : pargs.err = configfp? 1:2; break;
        }
     }
@@ -1462,27 +1836,8 @@ main( int argc, char **argv )
     }
   #endif
 
-    check_permissions(opt.homedir,0,0);
-
-    if(unsafe_files)
-      {
-       STRLIST tmp;
-
-       for(tmp=unsafe_files;tmp;tmp=tmp->next)
-         check_permissions(tmp->d,0,0);
-
-       free_strlist(unsafe_files);
-      }
-
-    if(extensions)
-      {
-       STRLIST tmp;
-
-       for(tmp=extensions;tmp;tmp=tmp->next)
-         check_permissions(tmp->d,1,0);
-
-       free_strlist(extensions);
-      }
+    if (opt.verbose > 2)
+        log_info ("using character set `%s'\n", get_native_charset ());
 
     if( may_coredump && !opt.quiet )
        log_info(_("WARNING: program may create a core file!\n"));
@@ -1514,12 +1869,11 @@ main( int argc, char **argv )
     secmem_set_flags( secmem_get_flags() & ~2 ); /* resume warnings */
 
     set_debug();
-    g10_opt_homedir = opt.homedir;
 
     /* Do these after the switch(), so they can override settings. */
-    if(opt.pgp2 && (opt.pgp6 || opt.pgp7))
+    if(opt.pgp2 && (opt.pgp6 || opt.pgp7 || opt.pgp8))
       log_error(_("%s not allowed with %s!\n"),
-               "--pgp2",opt.pgp6?"--pgp6":"--pgp7");
+               "--pgp2",opt.pgp6?"--pgp6":opt.pgp7?"--pgp7":"--pgp8");
     else
       {
        if(opt.pgp2)
@@ -1560,14 +1914,26 @@ main( int argc, char **argv )
                  }
                else if(cmd==aSym)
                  {
+                   /* This only sets IDEA for symmetric encryption
+                      since it is set via select_algo_from_prefs for
+                      pk encryption. */
                    m_free(def_cipher_string);
                    def_cipher_string = m_strdup("idea");
                  }
+
+               /* PGP2 can't handle the output from the textmode
+                  filter, so we disable it for anything that could
+                  create a literal packet (only encryption and
+                  symmetric encryption, since we disable signing
+                  above). */
+               if(!unusable)
+                 opt.textmode=0;
              }
 
            if(unusable)
              {
-               log_info(_("this message may not be usable by PGP 2.x\n"));
+               log_info(_("this message may not be usable by %s\n"),
+                        "PGP 2.x");
                opt.pgp2=0;
              }
            else
@@ -1588,17 +1954,29 @@ main( int argc, char **argv )
                opt.def_compress_algo = 1;
              }
          }
-
-       if(opt.pgp6 || opt.pgp7)
+       else if(opt.pgp6)
          {
+           opt.sk_comments=0;
+           opt.escape_from=1;
+           opt.force_v3_sigs=1;
+           opt.ask_sig_expire=0;
+           opt.def_compress_algo=1;
            opt.force_mdc=0;
            opt.disable_mdc=1;
+         }
+       else if(opt.pgp7)
+         {
            opt.sk_comments=0;
            opt.escape_from=1;
            opt.force_v3_sigs=1;
            opt.ask_sig_expire=0;
            opt.def_compress_algo=1;
          }
+       else if(opt.pgp8)
+         {
+           opt.escape_from=1;
+           opt.def_compress_algo=1;
+         }
       }
 
     /* must do this after dropping setuid, because string_to...
@@ -1618,6 +1996,12 @@ main( int argc, char **argv )
        if( check_digest_algo(opt.def_digest_algo) )
            log_error(_("selected digest algorithm is invalid\n"));
     }
+    if( def_compress_string ) {
+       opt.def_compress_algo = string_to_compress_algo(def_compress_string);
+       m_free(def_compress_string); def_compress_string = NULL;
+       if( check_compress_algo(opt.def_compress_algo) )
+           log_error(_("selected compression algorithm is invalid\n"));
+    }
     if( cert_digest_string ) {
        opt.cert_digest_algo = string_to_digest_algo(cert_digest_string);
        m_free(cert_digest_string); cert_digest_string = NULL;
@@ -1636,8 +2020,6 @@ main( int argc, char **argv )
        if( check_digest_algo(opt.s2k_digest_algo) )
            log_error(_("selected digest algorithm is invalid\n"));
     }
-    if( opt.def_compress_algo < -1 || opt.def_compress_algo > 2 )
-       log_error(_("compress algorithm must be in range %d..%d\n"), 0, 2);
     if( opt.completes_needed < 1 )
        log_error(_("completes-needed must be greater than 0\n"));
     if( opt.marginals_needed < 2 )
@@ -1684,7 +2066,6 @@ main( int argc, char **argv )
     /* set the random seed file */
     if( use_random_seed ) {
        char *p = make_filename(opt.homedir, "random_seed", NULL );
-       check_permissions(p,0,0);
        set_random_seed_file(p);
        m_free(p);
     }
@@ -1738,7 +2119,7 @@ main( int argc, char **argv )
        if( !nrings || default_keyring )  /* add default ring */
            keydb_add_resource ("pubring" EXTSEP_S "gpg", 0, 0);
        for(sl = nrings; sl; sl = sl->next )
-           keydb_add_resource ( sl->d, 0, 0 );
+           keydb_add_resource ( sl->d, sl->flags, 0 );
       }
     FREE_STRLIST(nrings);
     FREE_STRLIST(sec_nrings);
@@ -1758,13 +2139,6 @@ main( int argc, char **argv )
       case aEnArmor:
       case aFixTrustDB:
        break;
-      case aKMode:
-      case aListKeys:
-      case aListSecretKeys:
-      case aCheckKeys:
-       if( opt.with_colons ) /* need this to list the trust */
-           rc = setup_trustdb(1, trustdb_name );
-       break;
       case aExportOwnerTrust: rc = setup_trustdb( 0, trustdb_name ); break;
       case aListTrustDB: rc = setup_trustdb( argc? 1:0, trustdb_name ); break;
       default: rc = setup_trustdb(1, trustdb_name ); break;
@@ -2007,8 +2381,9 @@ main( int argc, char **argv )
        break;
 
       case aFastImport:
+        opt.import_options |= IMPORT_FAST_IMPORT;
       case aImport:
-       import_keys( argc? argv:NULL, argc, (cmd == aFastImport), NULL );
+       import_keys( argc? argv:NULL, argc, NULL, opt.import_options );
        break;
 
       case aExport:
@@ -2315,40 +2690,89 @@ g10_exit( int rc )
 }
 
 
-
-
+/* Pretty-print hex hashes.  This assumes at least an 80-character
+   display, but there are a few other similar assumptions in the
+   display code. */
 static void
-print_hex( byte *p, size_t n )
+print_hex( MD_HANDLE md, int algo, const char *fname )
 {
-    int i;
+  int i,n,count,indent=0;
+  const byte *p;
 
-    if( n == 20 ) {
-       for(i=0; i < n ; i++, i++, p += 2 ) {
-           if( i )
-               putchar(' ');
-           if( i == 10 )
-               putchar(' ');
-           printf("%02X%02X", *p, p[1] );
-       }
+  if(fname)
+    indent=printf("%s: ",fname);
+
+  if(indent>40)
+    {
+      printf("\n");
+      indent=0;
     }
-    else if( n == 24 ) {
-       for(i=0; i < n ; i += 4, p += 4 ) {
-           if( i )
-               putchar(' ');
-           if( i == 12 )
-               putchar(' ');
-           printf("%02X%02X%02X%02X", *p, p[1], p[2], p[3] );
+
+  if(algo==DIGEST_ALGO_RMD160)
+    indent+=printf("RMD160 = ");
+  else if(algo==DIGEST_ALGO_TIGER)
+    indent+=printf(" TIGER = ");
+  else if(algo>0)
+    indent+=printf("%6s = ",digest_algo_to_string(algo));
+  else
+    algo=abs(algo);
+
+  count=indent;
+
+  p = md_read( md, algo );
+  n = md_digest_length(algo);
+
+  count+=printf("%02X",*p++);
+
+  for(i=1;i<n;i++,p++)
+    {
+      if(n==16)
+       {
+         if(count+2>79)
+           {
+             printf("\n%*s",indent," ");
+             count=indent;
+           }
+         else
+           count+=printf(" ");
+
+         if(!(i%8))
+           count+=printf(" ");
        }
-    }
-    else {
-       for(i=0; i < n ; i++, p++ ) {
-           if( i )
-               putchar(' ');
-           if( i && !(i%8) )
-               putchar(' ');
-           printf("%02X", *p );
+      else if (n==20)
+       {
+         if(!(i%2))
+           {
+             if(count+4>79)
+               {
+                 printf("\n%*s",indent," ");
+                 count=indent;
+               }
+             else
+               count+=printf(" ");
+           }
+
+         if(!(i%10))
+           count+=printf(" ");
+       }
+      else
+       {
+         if(!(i%4))
+           {
+             if(count+8>79)
+               {
+                 printf("\n%*s",indent," ");
+                 count=indent;
+               }
+             else
+               count+=printf(" ");
+           }
        }
+
+      count+=printf("%02X",*p);
     }
+
+  printf("\n");
 }
 
 static void
@@ -2382,23 +2806,18 @@ print_mds( const char *fname, int algo )
     char buf[1024];
     size_t n;
     MD_HANDLE md;
-    char *pname;
 
     if( !fname ) {
        fp = stdin;
       #ifdef HAVE_DOSISH_SYSTEM
        setmode ( fileno(fp) , O_BINARY );
       #endif
-       pname = m_strdup("[stdin]: ");
     }
     else {
-       pname = m_alloc(strlen(fname)+3);
-       strcpy(stpcpy(pname,fname),": ");
        fp = fopen( fname, "rb" );
     }
     if( !fp ) {
-       log_error("%s%s\n", pname, strerror(errno) );
-       m_free(pname);
+       log_error("%s: %s\n", fname?fname:"[stdin]", strerror(errno) );
        return;
     }
 
@@ -2409,14 +2828,22 @@ print_mds( const char *fname, int algo )
        md_enable( md, DIGEST_ALGO_MD5 );
        md_enable( md, DIGEST_ALGO_SHA1 );
        md_enable( md, DIGEST_ALGO_RMD160 );
-       if( !check_digest_algo(DIGEST_ALGO_TIGER) )
-           md_enable( md, DIGEST_ALGO_TIGER );
+#ifdef USE_TIGER192
+       md_enable( md, DIGEST_ALGO_TIGER );
+#endif
+#ifdef USE_SHA256
+       md_enable( md, DIGEST_ALGO_SHA256 );
+#endif
+#ifdef USE_SHA512
+       md_enable( md, DIGEST_ALGO_SHA384 );
+       md_enable( md, DIGEST_ALGO_SHA512 );
+#endif
     }
 
     while( (n=fread( buf, 1, DIM(buf), fp )) )
        md_write( md, buf, n );
     if( ferror(fp) )
-       log_error("%s%s\n", pname, strerror(errno) );
+       log_error("%s: %s\n", fname?fname:"[stdin]", strerror(errno) );
     else {
        md_final(md);
         if ( opt.with_colons ) {
@@ -2426,29 +2853,36 @@ print_mds( const char *fname, int algo )
                 print_hashline( md, DIGEST_ALGO_MD5, fname );
                 print_hashline( md, DIGEST_ALGO_SHA1, fname );
                 print_hashline( md, DIGEST_ALGO_RMD160, fname );
-                if( !check_digest_algo(DIGEST_ALGO_TIGER) ) 
-                    print_hashline( md, DIGEST_ALGO_TIGER, fname );
+#ifdef USE_TIGER192
+               print_hashline( md, DIGEST_ALGO_TIGER, fname );
+#endif
+#ifdef USE_SHA256
+                print_hashline( md, DIGEST_ALGO_SHA256, fname );
+#endif
+#ifdef USE_SHA512
+               print_hashline( md, DIGEST_ALGO_SHA384, fname );
+               print_hashline( md, DIGEST_ALGO_SHA512, fname );
+#endif
             }
         }
         else {
-            if( algo ) {
-                if( fname )
-                    fputs( pname, stdout );
-                print_hex(md_read(md, algo), md_digest_length(algo) );
-            }
+            if( algo )
+              print_hex(md,-algo,fname);
             else {
-                printf(  "%s   MD5 = ", fname?pname:"" );
-                print_hex(md_read(md, DIGEST_ALGO_MD5), 16 );
-                printf("\n%s  SHA1 = ", fname?pname:""  );
-                print_hex(md_read(md, DIGEST_ALGO_SHA1), 20 );
-                printf("\n%sRMD160 = ", fname?pname:""  );
-                print_hex(md_read(md, DIGEST_ALGO_RMD160), 20 );
-                if( !check_digest_algo(DIGEST_ALGO_TIGER) ) {
-                    printf("\n%s TIGER = ", fname?pname:""  );
-                    print_hex(md_read(md, DIGEST_ALGO_TIGER), 24 );
-                }
+                print_hex( md, DIGEST_ALGO_MD5, fname );
+                print_hex( md, DIGEST_ALGO_SHA1, fname );
+                print_hex( md, DIGEST_ALGO_RMD160, fname );
+#ifdef USE_TIGER192
+               print_hex( md, DIGEST_ALGO_TIGER, fname );
+#endif
+#ifdef USE_SHA256
+                print_hex( md, DIGEST_ALGO_SHA256, fname );
+#endif
+#ifdef USE_SHA512
+               print_hex( md, DIGEST_ALGO_SHA384, fname );
+               print_hex( md, DIGEST_ALGO_SHA512, fname );
+#endif
             }
-            putchar('\n');
         }
     }
     md_close(md);
@@ -2470,6 +2904,7 @@ add_notation_data( const char *string, int which )
     STRLIST sl,*notation_data;
     int critical=0;
     int highbit=0;
+    int saw_at=0;
 
     if(which)
       notation_data=&opt.cert_notation_data;
@@ -2481,13 +2916,29 @@ add_notation_data( const char *string, int which )
        string++;
     }
 
-    for( s=string ; *s != '='; s++ ) {
-       if( !*s || (*s & 0x80) || (!isgraph(*s) && !isspace(*s)) ) {
+    /* If and when the IETF assigns some official name tags, we'll
+       have to add them here. */
+
+    for( s=string ; *s != '='; s++ )
+      {
+       if( *s=='@')
+         saw_at=1;
+
+       if( !*s || (*s & 0x80) || (!isgraph(*s) && !isspace(*s)) )
+         {
            log_error(_("a notation name must have only printable characters "
                        "or spaces, and end with an '='\n") );
            return;
-       }
-    }
+         }
+      }
+
+    if(!saw_at && !opt.expert)
+      {
+       log_error(
+               _("a user notation name must contain the '@' character\n"));
+       return;
+      }
+
     /* we only support printable text - therefore we enforce the use
      * of only printable characters (an empty value is valid) */
     for( s++; *s ; s++ ) {