* keygen.c (generate_keypair): Create an AUTHKEYTYPE entry for cards.
[gnupg.git] / g10 / g10.c
index 8dcf6b4..b7ad9d7 100644 (file)
--- a/g10/g10.c
+++ b/g10/g10.c
@@ -1,5 +1,5 @@
 /* g10.c - The GnuPG utility (main for gpg)
- * Copyright (C) 1998,1999,2000,2001,2002 Free Software Foundation, Inc.
+ * Copyright (C) 1998,1999,2000,2001,2002,2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <string.h>
 #include <ctype.h>
 #include <unistd.h>
+#include <assert.h>
 #ifdef HAVE_DOSISH_SYSTEM
-  #include <fcntl.h> /* for setmode() */
+#include <fcntl.h> /* for setmode() */
+#endif
+#ifdef HAVE_STAT
+#include <sys/stat.h> /* for stat() */
 #endif
 
 #define INCLUDED_BY_MAIN_MODULE 1
+#include "gpg.h"
 #include "packet.h"
 #include "iobuf.h"
 #include "memory.h"
@@ -44,7 +49,6 @@
 #include "ttyio.h"
 #include "i18n.h"
 #include "status.h"
-#include "g10defs.h"
 #include "keyserver-internal.h"
 #include "exec.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',
@@ -115,6 +120,7 @@ enum cmd_and_opt_values { aNull = 0,
     aListTrustDB,
     aListTrustPath,
     aExportOwnerTrust,
+    aListOwnerTrust,
     aImportOwnerTrust,
     aDeArmor,
     aEnArmor,
@@ -122,8 +128,10 @@ enum cmd_and_opt_values { aNull = 0,
     aPipeMode,
     aRebuildKeydbCaches,
     aRefreshKeys,
+    aChangePIN,
 
     oTextmode,
+    oNoTextmode,
     oExpert,
     oNoExpert,
     oAskSigExpire,
@@ -136,6 +144,7 @@ enum cmd_and_opt_values { aNull = 0,
     oAnswerNo,
     oDefCertCheckLevel,
     oKeyring,
+    oPrimaryKeyring,
     oSecretKeyring,
     oShowKeyring,
     oDefaultKey,
@@ -155,20 +164,19 @@ enum cmd_and_opt_values { aNull = 0,
 #endif /* __riscos__ */
     oSKComments,
     oNoSKComments,
-    oNoVersion,
     oEmitVersion,
+    oNoEmitVersion,
     oCompletesNeeded,
     oMarginalsNeeded,
     oMaxCertDepth,
     oLoadExtension,
+    oGnuPG,
     oRFC1991,
     oOpenPGP,
     oPGP2,
-    oNoPGP2,
     oPGP6,
-    oNoPGP6,
     oPGP7,
-    oNoPGP7,
+    oPGP8,
     oCipherAlgo,
     oDigestAlgo,
     oCertDigestAlgo,
@@ -200,6 +208,8 @@ enum cmd_and_opt_values { aNull = 0,
     oCompressKeys,
     oCompressSigs,
     oAlwaysTrust,
+    oTrustModel,
+    oForceOwnertrust,
     oEmuChecksumBug,
     oRunAsShmCP,
     oSetFilename,
@@ -214,6 +224,7 @@ enum cmd_and_opt_values { aNull = 0,
     oComment,
     oDefaultComment,
     oThrowKeyid,
+    oNoThrowKeyid,
     oShowPhotos,
     oNoShowPhotos,
     oPhotoViewer,
@@ -240,9 +251,12 @@ enum cmd_and_opt_values { aNull = 0,
     oKeyServerOptions,
     oImportOptions,
     oExportOptions,
+    oListOptions,
+    oVerifyOptions,
     oTempDir,
     oExecPath,
     oEncryptTo,
+    oHiddenEncryptTo,
     oNoEncryptTo,
     oLoggerFD,
 #ifdef __riscos__
@@ -289,14 +303,19 @@ enum cmd_and_opt_values { aNull = 0,
     oPersonalCipherPreferences,
     oPersonalDigestPreferences,
     oPersonalCompressPreferences,
-    oEmu3DESS2KBug,  /* will be removed in 1.1 */
     oEmuMDEncodeBug,
+    oAgentProgram,
     oDisplay,
     oTTYname,
     oTTYtype,
     oLCctype,
     oLCmessages,
     oGroup,
+    oStrict,
+    oNoStrict,
+    oMangleDosFilenames,
+    oNoMangleDosFilenames,
+    oEnableProgressFilter,                          
 aTest };
 
 
@@ -344,11 +363,13 @@ static ARGPARSE_OPTS opts[] = {
     { aExportSecretSub, "export-secret-subkeys" , 256, "@" },
     { aImport, "import",      256     , N_("import/merge keys")},
     { aFastImport, "fast-import",  256 , "@"},
+    { aChangePIN,  "change-pin", 256, N_("change a card's PIN")},
+
     { aListPackets, "list-packets",256,N_("list only the sequence of packets")},
     { aExportOwnerTrust,
              "export-ownertrust", 256, N_("export the ownertrust values")},
     { aImportOwnerTrust,
-             "import-ownertrust", 256 , N_("import ownertrust values")},
+             "import-ownertrust", 256, N_("import ownertrust values")},
     { aUpdateTrustDB,
              "update-trustdb",0 , N_("update the trust database")},
     { aCheckTrustDB,
@@ -367,6 +388,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")},
@@ -376,11 +398,13 @@ 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)") },
     { oTextmodeShort, NULL,   0, "@"},
     { oTextmode, "textmode",  0, N_("use canonical text mode")},
+    { oNoTextmode, "no-textmode",  0, "@"},
     { oExpert, "expert",   0, "@"},
     { oNoExpert, "no-expert",   0, "@"},
     { oAskSigExpire, "ask-sig-expire",   0, "@"},
@@ -400,7 +424,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, "@"},
@@ -408,6 +432,7 @@ 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")},
@@ -415,6 +440,7 @@ static ARGPARSE_OPTS opts[] = {
     { oKeyServerOptions, "keyserver-options",2,"@"},
     { oImportOptions, "import-options",2,"@"},
     { oExportOptions, "export-options",2,"@"},
+    { oListOptions, "list-options",2,"@"},
     { oCharset, "charset"   , 2, N_("|NAME|set terminal charset to NAME") },
     { oOptions, "options"   , 2, N_("read options from file")},
 
@@ -436,14 +462,17 @@ static ARGPARSE_OPTS opts[] = {
     { oMaxCertDepth,   "max-cert-depth", 1, "@" },
     { oTrustedKey, "trusted-key", 2, N_("|KEYID|ultimately trust this key")},
     { oLoadExtension, "load-extension" ,2, N_("|FILE|load extension module FILE")},
+    { oGnuPG, "gnupg",   0, "@"},
+    { oGnuPG, "no-pgp2", 0, "@"},
+    { oGnuPG, "no-pgp6", 0, "@"},
+    { oGnuPG, "no-pgp7", 0, "@"},
+    { oGnuPG, "no-pgp8", 0, "@"},
     { oRFC1991, "rfc1991",   0, N_("emulate the mode described in RFC1991")},
     { oOpenPGP, "openpgp", 0, N_("set all packet, cipher and digest options to OpenPGP behavior")},
     { oPGP2, "pgp2", 0, N_("set all packet, cipher and digest options to PGP 2.x behavior")},
-    { oNoPGP2, "no-pgp2", 0, "@"},
     { oPGP6, "pgp6", 0, "@"},
-    { oNoPGP6, "no-pgp6", 0, "@"},
     { oPGP7, "pgp7", 0, "@"},
-    { oNoPGP7, "no-pgp7", 0, "@"},
+    { oPGP8, "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")},
@@ -453,11 +482,12 @@ 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")},
-    { 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")},
+    { oNoThrowKeyid, "no-throw-keyid", 0, "@" },
+    { oShowPhotos,   "show-photos", 0, "@" },
+    { oNoShowPhotos, "no-show-photos", 0, "@" },
+    { oPhotoViewer,  "photo-viewer", 2, "@" },
     { oNotation,   "notation-data", 2, "@" },
     { oSigNotation,   "sig-notation", 2, "@" },
     { oCertNotation,  "cert-notation", 2, "@" },
@@ -474,10 +504,12 @@ static ARGPARSE_OPTS opts[] = {
     " --fingerprint [names]      show fingerprints\n"  ) },
 
   /* hidden options */
-    { aExportOwnerTrust, "list-ownertrust",0 , "@"},  /* alias */
+    { aListOwnerTrust, "list-ownertrust", 256, "@"}, /* deprecated */
+    { oCompressAlgo, "compression-algo", 1, "@"}, /* 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, "@" },
@@ -511,6 +543,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", 2, "@"},
     { oEmuChecksumBug, "emulate-checksum-bug", 0, "@"},
     { oRunAsShmCP, "run-as-shm-coprocess", 4, "@" },
     { oSetFilename, "set-filename", 2, "@" },
@@ -525,8 +559,9 @@ static ARGPARSE_OPTS opts[] = {
     { oNoShowNotation, "no-show-notation", 0, "@" },
     { oComment, "comment", 2, "@" },
     { oDefaultComment, "default-comment", 0, "@" },
-    { oNoVersion, "no-version", 0, "@"},
     { oEmitVersion, "emit-version", 0, "@"},
+    { oNoEmitVersion, "no-emit-version", 0, "@"},
+    { oNoEmitVersion, "no-version", 0, "@"}, /* alias */
     { oNotDashEscaped, "not-dash-escaped", 0, "@" },
     { oEscapeFrom, "escape-from-lines", 0, "@" },
     { oNoEscapeFrom, "no-escape-from-lines", 0, "@" },
@@ -578,14 +613,19 @@ 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, "@"},
+    { oAgentProgram, "agent-program", 2 , "@" },
     { oDisplay,    "display",     2, "@" },
     { oTTYname,    "ttyname",     2, "@" },
     { oTTYtype,    "ttytype",     2, "@" },
     { 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, "@" },
+    { oEnableProgressFilter, "enable-progress-filter", 0, "@" },
 {0} };
 
 
@@ -599,17 +639,24 @@ 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 );
+static void emergency_cleanup (void);
 
 #ifdef __riscos__
 RISCOS_GLOBAL_STATICS("GnuPG Heap")
 #endif /* __riscos__ */
 
-const char *
-strusage( int level )
+static int
+pk_test_algo (int algo)
+{
+  return openpgp_pk_test_algo (algo, 0);
+}
+
+
+static const char *
+my_strusage( int level )
 {
   static char *digests, *pubkeys, *ciphers, *zips;
     const char *p;
@@ -640,30 +687,30 @@ strusage( int level )
       case 33: p = _("\nSupported algorithms:\n"); break;
       case 34:
        if( !pubkeys )
-           pubkeys = build_list("Pubkey: ", 0, pubkey_algo_to_string,
-                                                       check_pubkey_algo );
+           pubkeys = build_list(_("Pubkey: "), 0, gcry_pk_algo_name,
+                                 pk_test_algo );
        p = pubkeys;
        break;
       case 35:
        if( !ciphers )
-           ciphers = build_list("Cipher: ", 'S', cipher_algo_to_string,
-                                                       check_cipher_algo );
+           ciphers = build_list(_("Cipher: "), 'S', gcry_cipher_algo_name,
+                                 openpgp_cipher_test_algo );
        p = ciphers;
        break;
       case 36:
        if( !digests )
-           digests = build_list("Hash: ", 'H', digest_algo_to_string,
-                                                       check_digest_algo );
+           digests = build_list(_("Hash: "), 'H', gcry_md_algo_name,
+                                       openpgp_md_test_algo );
        p = digests;
        break;
       case 37:
        if( !zips )
-           zips = build_list("Compress: ",'Z',compress_algo_to_string,
+           zips = build_list(_("Compression: "),'Z',compress_algo_to_string,
                                                        check_compress_algo);
        p = zips;
        break;
 
-      default: p = default_strusage(level);
+      default: p = NULL;
     }
     return p;
 }
@@ -679,12 +726,12 @@ build_list( const char *text, char letter,
     char *list, *p, *line=NULL;
 
     if( maybe_setuid )
-       secmem_init( 0 );    /* drop setuid */
+        gcry_control (GCRYCTL_INIT_SECMEM, 0, 0);  /* drop setuid */
 
     for(i=0; i <= 110; i++ )
        if( !chkf(i) && (s=mapf(i)) )
            n += strlen(s) + 7 + 2;
-    list = m_alloc( 21 + n ); *list = 0;
+    list = xmalloc ( 21 + n ); *list = 0;
     for(p=NULL, i=0; i <= 110; i++ ) {
        if( !chkf(i) && (s=mapf(i)) ) {
            if( !p ) {
@@ -697,7 +744,7 @@ build_list( const char *text, char letter,
            if(strlen(line)>60) {
              int spaces=strlen(text);
 
-             list=m_realloc(list,n+spaces+1);
+             list = xrealloc(list,n+spaces+1);
              /* realloc could move the block, so find the end again */
              p=list;
              while(*p)
@@ -727,15 +774,15 @@ build_list( const char *text, char letter,
 static void
 i18n_init(void)
 {
-  #ifdef USE_SIMPLE_GETTEXT
+#ifdef USE_SIMPLE_GETTEXT
     set_gettext_file( PACKAGE );
-  #else
-  #ifdef ENABLE_NLS
+#else
+#ifdef ENABLE_NLS
     setlocale( LC_ALL, "" );
-    bindtextdomain( PACKAGE, G10_LOCALEDIR );
+    bindtextdomain( PACKAGE, LOCALEDIR );
     textdomain( PACKAGE );
-  #endif
-  #endif
+#endif
+#endif
 }
 
 static void
@@ -748,32 +795,69 @@ wrong_args( const char *text)
 }
 
 
+static void
+log_set_strict (int yesno)
+{
+  /* FIXME-XXX*/
+}
+
 static char *
 make_username( const char *string )
 {
     char *p;
     if( utf8_strings )
-       p = m_strdup(string);
+       p = xstrdup (string);
     else
        p = native_to_utf8( string );
     return p;
 }
 
 
+/*
+ * same as add_to_strlist() but if is_utf8 is *not* set a conversion
+ * to UTF8 is done  
+ */
+static STRLIST
+add_to_strlist2 ( STRLIST *list, const char *string, int is_utf8)
+{
+  STRLIST sl;
+  
+  if (is_utf8)
+    sl = add_to_strlist( list, string );
+  else 
+    {
+      char *p = native_to_utf8( string );
+      sl = add_to_strlist( list, p );
+      xfree( p );
+    }
+  return sl;
+}
+
+
 static void
 set_debug(void)
 {
-    if( opt.debug & DBG_MEMORY_VALUE )
-       memory_debug_mode = 1;
-    if( opt.debug & DBG_MEMSTAT_VALUE )
-       memory_stat_debug_mode = 1;
-    if( opt.debug & DBG_MPI_VALUE )
-       mpi_debug_mode = 1;
-    if( opt.debug & DBG_CIPHER_VALUE )
-       g10c_debug_mode = 1;
-    if( opt.debug & DBG_IOBUF_VALUE )
-       iobuf_debug_mode = 1;
+  if (opt.debug & DBG_MEMORY_VALUE )
+    memory_debug_mode = 1;
+  if (opt.debug & DBG_MEMSTAT_VALUE )
+    memory_stat_debug_mode = 1;
+  if (opt.debug & DBG_MPI_VALUE)
+    gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2);
+  if (opt.debug & DBG_CIPHER_VALUE )
+    gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
+  if (opt.debug & DBG_IOBUF_VALUE )
+    iobuf_debug_mode = 1;
+}
 
+
+/* 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;
 }
 
 
@@ -813,18 +897,23 @@ 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=xmalloc (sizeof(struct groupitem));
   item->name=name;
   item->values=values;
   item->next=opt.grouplist;
@@ -832,19 +921,220 @@ 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=xstrdup (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;
+    }
+
+  xfree (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:
+  xfree (tmppath);
+
+  if(homedir)
+    homedir_cache=ret;
+
+  return ret;
+
+#endif /* HAVE_STAT && !HAVE_DOSISH_SYSTEM */
+
+  return 0;
+}
 
 int
 main( int argc, char **argv )
 {
     ARGPARSE_ARGS pargs;
-    IOBUF a;
+    iobuf_t a;
     int rc=0;
     int orig_argc;
     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;
@@ -863,6 +1153,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;
@@ -873,27 +1164,41 @@ main( int argc, char **argv )
     int pwfd = -1;
     int with_fpr = 0; /* make an option out of --fingerprint */
     int any_explicit_recipient = 0;
-  #ifdef USE_SHM_COPROCESSING
+#ifdef USE_SHM_COPROCESSING
     ulong requested_shm_size=0;
-  #endif
+#endif
 
-  #ifdef __riscos__
+#ifdef __riscos__
     riscos_global_defaults();
     opt.lock_once = 1;
-  #endif /* __riscos__ */
+#endif /* __riscos__ */
 
     trap_unaligned();
-    secmem_set_flags( secmem_get_flags() | 2 ); /* suspend warnings */
+    set_strusage (my_strusage);
+    gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+    /* We don't need any locking in libgcrypt unless we use any kind of
+       threading. */
+    gcry_control (GCRYCTL_DISABLE_INTERNAL_LOCKING);
     /* Please note that we may running SUID(ROOT), so be very CAREFUL
      * when adding any stuff between here and the call to
      * secmem_init()  somewhere after the option parsing
      */
-    log_set_name("gpg");
-    secure_random_alloc(); /* put random number into secure memory */
+    log_set_prefix ("gpg", 1);
+    /* check that the libraries are suitable.  Do it here because the
+       option parse may need services of the library */
+    if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
+      {
+        log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
+                   NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
+      }
+
+    gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
+
     may_coredump = disable_core_dumps();
-    init_signals();
-    create_dotlock(NULL); /* register locking cleanup */
+    gnupg_init_signals (0, emergency_cleanup);
+    create_dotlock (NULL); /* register locking cleanup */
     i18n_init();
+
     opt.command_fd = -1; /* no command fd */
     opt.compress = -1; /* defaults to standard compress level */
     /* note: if you change these lines, look at oOpenPGP */
@@ -903,28 +1208,39 @@ 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.import_options=0;
+    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.import_options=IMPORT_REPAIR_PKS_SUBKEY_BUG;
     opt.keyserver_options.export_options=
       EXPORT_INCLUDE_NON_RFC|EXPORT_INCLUDE_ATTRIBUTES;
     opt.keyserver_options.include_subkeys=1;
-#if defined (__MINGW32__) || defined (__CYGWIN32__)
-    opt.homedir = read_w32_registry_string( NULL, "Software\\GNU\\GnuPG", "HomeDir" );
+    opt.keyserver_options.include_revoked=1;
+    opt.keyserver_options.try_dns_srv=1;
+    opt.verify_options=VERIFY_SHOW_POLICY|VERIFY_SHOW_NOTATION;
+    opt.trust_model=TM_AUTO;
+    opt.mangle_dos_filenames = 1;
+    opt.use_agent = 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;
@@ -944,8 +1260,20 @@ 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;
-      #ifdef USE_SHM_COPROCESSING
+           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,
             * so that we are the able to drop setuid as soon as possible */
@@ -957,17 +1285,17 @@ main( int argc, char **argv )
             * initialized when init_shm_coprocessing() is called */
            set_status_fd( iobuf_translate_file_handle (pargs.r.ret_int, 1) );
        }
-      #endif
+#endif
     }
 
 #ifdef HAVE_DOSISH_SYSTEM
     if ( strchr (opt.homedir,'\\') ) {
-        char *d, *buf = m_alloc (strlen (opt.homedir)+1);
+        char *d, *buf = xmalloc (strlen (opt.homedir)+1);
         const char *s = opt.homedir;
         for (d=buf,s=opt.homedir; *s; s++)
             *d++ = *s == '\\'? '/': *s;
         *d = 0;
-        opt.homedir = buf;
+        set_homedir (buf);
     }
 #endif
 #ifdef USE_SHM_COPROCESSING
@@ -975,24 +1303,37 @@ main( int argc, char **argv )
        init_shm_coprocessing(requested_shm_size, 1 );
     }
 #endif
-    /* initialize the secure memory. */
-    secmem_init( 16384 );
+    /* Initialize the secure memory. */
+    gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
     maybe_setuid = 0;
     /* Okay, we are now working under our real uid */
 
+    /* malloc hooks go here ... */
+    assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
+
+    set_native_charset (NULL); /* Try to auto set the character set */
+
     if( default_config )
       {
-       configname = make_filename(opt.homedir, "gpg" EXTSEP_S "conf", NULL );
+       /* Try for a version specific config file first */
+       configname = make_filename(opt.homedir,
+                                  "gpg" EXTSEP_S "conf-" SAFE_VERSION, NULL );
+       if(access(configname,R_OK))
+         {
+           xfree (configname);
+           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);
+            xfree (p);
           }
         else
           { /* Keep on using the old default one. */
-            m_free (configname);
+            xfree (configname);
             configname = make_filename(opt.homedir, "options", NULL );
           }
       }
@@ -1001,13 +1342,14 @@ main( int argc, char **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
@@ -1030,7 +1372,7 @@ main( int argc, char **argv )
                                    configname, strerror(errno) );
                g10_exit(2);
            }
-           m_free(configname); configname = NULL;
+           xfree (configname); configname = NULL;
        }
        if( parse_debug && configname )
            log_info(_("reading options from `%s'\n"), configname );
@@ -1094,11 +1436,16 @@ main( int argc, char **argv )
          case aListTrustPath: set_cmd( &cmd, aListTrustPath); break;
          case aDeArmor: set_cmd( &cmd, aDeArmor); break;
          case aEnArmor: set_cmd( &cmd, aEnArmor); break;
+         case aListOwnerTrust:
+           deprecated_warning(configname,configlineno,
+                              "--list-ownertrust","--export-ownertrust","");
          case aExportOwnerTrust: set_cmd( &cmd, aExportOwnerTrust); break;
          case aImportOwnerTrust: set_cmd( &cmd, aImportOwnerTrust); break;
           case aPipeMode: set_cmd( &cmd, aPipeMode); break;
           case aRebuildKeydbCaches: set_cmd( &cmd, aRebuildKeydbCaches); break;
 
+          case aChangePIN: set_cmd (&cmd, aChangePIN); break;
+
          case oArmor: opt.armor = 1; opt.no_armor=0; break;
          case oOutput: opt.outfile = pargs.r.ret_str; break;
          case oQuiet: opt.quiet = 1; break;
@@ -1115,7 +1462,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;
@@ -1123,7 +1470,11 @@ 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 oShowKeyring: opt.show_keyring = 1; break;
+         case oPrimaryKeyring:
+           sl=append_to_strlist( &nrings, pargs.r.ret_str);
+           sl->flags=2;
+           break;
+         case oShowKeyring: opt.list_options|=LIST_SHOW_KEYRING; break;
          case oDebug: opt.debug |= pargs.r.ret_ulong; break;
          case oDebugAll: opt.debug = ~0; break;
          case oStatusFD:
@@ -1131,7 +1482,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:
@@ -1139,17 +1490,16 @@ 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:
-            log_set_logfile( NULL,
-                             iobuf_translate_file_handle (pargs.r.ret_int, 1) );
+            log_set_fd (iobuf_translate_file_handle (pargs.r.ret_int, 1));
             break;
 #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:
@@ -1160,8 +1510,8 @@ main( int argc, char **argv )
          case oOptions:
            /* config files may not be nested (silently ignore them) */
            if( !configfp ) {
-               m_free(configname);
-               configname = m_strdup(pargs.r.ret_str);
+               xfree (configname);
+               configname = xstrdup (pargs.r.ret_str);
                goto next_pass;
            }
            break;
@@ -1171,11 +1521,12 @@ main( int argc, char **argv )
          case oNoGreeting: nogreeting = 1; break;
          case oNoVerbose: g10_opt_verbose = 0;
                           opt.verbose = 0; opt.list_sigs=0; break;
-         case oQuickRandom: quick_random_gen(1); break;
+      /* disabled for now: 
+          case oQuickRandom: quick_random_gen(1); break; */
          case oSKComments: opt.sk_comments=1; break;
          case oNoSKComments: opt.sk_comments=0; break;
-         case oNoVersion: opt.no_version=1; break;
          case oEmitVersion: opt.no_version=0; break;
+         case oNoEmitVersion: opt.no_version=1; break;
          case oCompletesNeeded: opt.completes_needed = pargs.r.ret_int; break;
          case oMarginalsNeeded: opt.marginals_needed = pargs.r.ret_int; break;
          case oMaxCertDepth: opt.max_cert_depth = pargs.r.ret_int; break;
@@ -1186,11 +1537,11 @@ main( int argc, char **argv )
                        opt.def_recipient = make_username(pargs.r.ret_str);
                    break;
          case oDefRecipientSelf:
-                   m_free(opt.def_recipient); opt.def_recipient = NULL;
+                   xfree (opt.def_recipient); opt.def_recipient = NULL;
                    opt.def_recipient_self = 1;
                    break;
          case oNoDefRecipient:
-                   m_free(opt.def_recipient); opt.def_recipient = NULL;
+                   xfree (opt.def_recipient); opt.def_recipient = NULL;
                    opt.def_recipient_self = 0;
                    break;
          case oNoOptions: opt.no_homedir_creation = 1; break; /* no-options */
@@ -1200,29 +1551,59 @@ 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,"pgp")==0)
+             opt.trust_model=TM_PGP;
+           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 if(ascii_strcasecmp(pargs.r.ret_str,"auto")==0)
+             opt.trust_model=TM_AUTO;
+           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");
+           opt.force_ownertrust=string_to_trust_value(pargs.r.ret_str);
+           if(opt.force_ownertrust==-1)
+             {
+               log_error("invalid ownertrust \"%s\"\n",pargs.r.ret_str);
+               opt.force_ownertrust=0;
+             }
+           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.compliance = CO_RFC1991;
            opt.force_v4_certs = 0;
-           opt.sk_comments = 0;
+           opt.disable_mdc = 1;
            opt.escape_from = 1;
            break;
          case oOpenPGP:
-           opt.rfc1991 = 0;
-           opt.rfc2440 = 1;
+           /* TODO: When 2440bis becomes a RFC, these may need
+               changing. */
+           opt.compliance = CO_RFC2440;
+           opt.disable_mdc = 1;
            opt.allow_non_selfsigned_uid = 1;
            opt.allow_freeform_uid = 1;
            opt.pgp2_workarounds = 0;
@@ -1234,30 +1615,27 @@ 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;
-         case oPGP6: opt.pgp6 = 1; break;
-         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 oPGP2:  opt.compliance = CO_PGP2;  break;
+         case oPGP6:  opt.compliance = CO_PGP6;  break;
+         case oPGP7:  opt.compliance = CO_PGP7;  break;
+         case oPGP8:  opt.compliance = CO_PGP8;  break;
+         case oGnuPG: opt.compliance = CO_GNUPG; 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;
@@ -1269,14 +1647,27 @@ main( int argc, char **argv )
            break;
          case oSigPolicyURL: add_policy_url(pargs.r.ret_str,0); break;
          case oCertPolicyURL: add_policy_url(pargs.r.ret_str,1); break;
-          case oShowPolicyURL: opt.show_policy_url=1; break;
-         case oNoShowPolicyURL: opt.show_policy_url=0; break;
+          case oShowPolicyURL:
+           opt.list_options|=LIST_SHOW_POLICY;
+           opt.verify_options|=VERIFY_SHOW_POLICY;
+           break;
+         case oNoShowPolicyURL:
+           opt.list_options&=~LIST_SHOW_POLICY;
+           opt.verify_options&=~VERIFY_SHOW_POLICY;
+           break;
          case oUseEmbeddedFilename: opt.use_embedded_filename = 1; break;
          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 oShowPhotos: opt.show_photos = 1; break;
-         case oNoShowPhotos: opt.show_photos = 0; break;
+         case oNoThrowKeyid: opt.throw_keyid = 0; break;
+         case oShowPhotos: 
+           opt.list_options|=LIST_SHOW_PHOTOS;
+           opt.verify_options|=VERIFY_SHOW_PHOTOS;
+           break;
+         case oNoShowPhotos:
+           opt.list_options&=~LIST_SHOW_PHOTOS;
+           opt.verify_options&=~VERIFY_SHOW_PHOTOS;
+           break;
          case oPhotoViewer: opt.photo_viewer = pargs.r.ret_str; break;
          case oForceV3Sigs: opt.force_v3_sigs = 1; break;
          case oNoForceV3Sigs: opt.force_v3_sigs = 0; break;
@@ -1287,20 +1678,30 @@ main( int argc, char **argv )
          case oDisableMDC: opt.disable_mdc = 1; break;
          case oNoDisableMDC: opt.disable_mdc = 0; break;
          case oS2KMode:   opt.s2k_mode = pargs.r.ret_int; break;
-         case oS2KDigest: s2k_digest_string = m_strdup(pargs.r.ret_str); break;
-         case oS2KCipher: s2k_cipher_string = m_strdup(pargs.r.ret_str); break;
+         case oS2KDigest: s2k_digest_string = xstrdup (pargs.r.ret_str); break;
+         case oS2KCipher: s2k_cipher_string = xstrdup (pargs.r.ret_str); break;
           case oSimpleSKChecksum: opt.simple_sk_checksum = 1; break;
          case oNoEncryptTo: opt.no_encrypt_to = 1; break;
          case oEncryptTo: /* store the recipient in the second list */
            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 oNoTextmode: opt.textmode=0;  break;
          case oExpert: opt.expert = 1; break;
          case oNoExpert: opt.expert = 0; break;
          case oAskSigExpire: opt.ask_sig_expire = 1; break;
@@ -1316,7 +1717,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:
@@ -1324,16 +1725,43 @@ 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 oCertDigestAlgo: cert_digest_string = m_strdup(pargs.r.ret_str); break;
-         case oNoSecmemWarn: secmem_set_flags( secmem_get_flags() | 1 ); break;
+         case oCipherAlgo: def_cipher_string = xstrdup (pargs.r.ret_str); break;
+         case oDigestAlgo: def_digest_string = xstrdup (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=xmalloc (strlen(pargs.r.ret_str)+2);
+                 strcpy(def_compress_string,"Z");
+                 strcat(def_compress_string,pargs.r.ret_str);
+               }
+             else
+               def_compress_string = xstrdup (pargs.r.ret_str);
+           }
+           break;
+         case oCertDigestAlgo: cert_digest_string = xstrdup (pargs.r.ret_str); break;
+         case oNoSecmemWarn:
+#warning add secmem_get_flags
+/*              secmem_set_flags( secmem_get_flags() | 1 ); */
+            break;
          case oNoPermissionWarn: opt.no_perm_warn=1; break;
          case oNoMDCWarn: opt.no_mdc_warn=1; break;
-         case oCharset:
+          case oCharset:
            if( set_native_charset( pargs.r.ret_str ) )
                log_error(_("%s is not a valid character set\n"),
                                                    pargs.r.ret_str);
@@ -1347,11 +1775,11 @@ 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:
-           opt.keyserver_uri=m_strdup(pargs.r.ret_str);
+           opt.keyserver_uri=xstrdup (pargs.r.ret_str);
            if(parse_keyserver_uri(pargs.r.ret_str,configname,configlineno))
              log_error(_("could not parse keyserver URI\n"));
            break;
@@ -1378,6 +1806,49 @@ main( int argc, char **argv )
                  log_error(_("invalid export options\n"));
              }
            break;
+         case oListOptions:
+           {
+             struct parse_options lopts[]=
+               {
+                 {"show-photos",LIST_SHOW_PHOTOS},
+                 {"show-policy-url",LIST_SHOW_POLICY},
+                 {"show-notation",LIST_SHOW_NOTATION},
+                 {"show-keyring",LIST_SHOW_KEYRING},
+                 {"show-validity",LIST_SHOW_VALIDITY},
+                 {"show-long-keyid",LIST_SHOW_LONG_KEYID},
+                 {NULL,0}
+               };
+
+             if(!parse_options(pargs.r.ret_str,&opt.list_options,lopts))
+               {
+                 if(configname)
+                   log_error(_("%s:%d: invalid list options\n"),
+                             configname,configlineno);
+                 else
+                   log_error(_("invalid list options\n"));
+               }
+           }
+           break;
+         case oVerifyOptions:
+           {
+             struct parse_options vopts[]=
+               {
+                 {"show-photos",VERIFY_SHOW_PHOTOS},
+                 {"show-policy-url",VERIFY_SHOW_POLICY},
+                 {"show-notation",VERIFY_SHOW_NOTATION},
+                 {NULL,0}
+               };
+
+             if(!parse_options(pargs.r.ret_str,&opt.verify_options,vopts))
+               {
+                 if(configname)
+                   log_error(_("%s:%d: invalid verify options\n"),
+                             configname,configlineno);
+                 else
+                   log_error(_("invalid verify options\n"));
+               }
+           }
+           break;
          case oTempDir: opt.temp_dir=pargs.r.ret_str; break;
          case oExecPath:
            if(set_exec_path(pargs.r.ret_str,0))
@@ -1391,16 +1862,30 @@ main( int argc, char **argv )
            break;
          case oSigNotation: add_notation_data( pargs.r.ret_str, 0 ); break;
          case oCertNotation: add_notation_data( pargs.r.ret_str, 1 ); break;
-         case oShowNotation: opt.show_notation=1; break;
-         case oNoShowNotation: opt.show_notation=0; break;
+         case oShowNotation:
+           opt.list_options|=LIST_SHOW_NOTATION;
+           opt.verify_options|=VERIFY_SHOW_NOTATION;
+           break;
+         case oNoShowNotation:
+           opt.list_options&=~LIST_SHOW_NOTATION;
+           opt.verify_options&=~VERIFY_SHOW_NOTATION;
+           break;
          case oUtf8Strings: utf8_strings = 1; break;
          case oNoUtf8Strings: utf8_strings = 0; break;
          case oDisableCipherAlgo:
-               disable_cipher_algo( string_to_cipher_algo(pargs.r.ret_str) );
-               break;
+            {
+              int algo = gcry_cipher_map_name (pargs.r.ret_str);
+              gcry_cipher_ctl (NULL, GCRYCTL_DISABLE_ALGO,
+                               &algo, sizeof algo);
+            }
+            break;
          case oDisablePubkeyAlgo:
-               disable_pubkey_algo( string_to_pubkey_algo(pargs.r.ret_str) );
-               break;
+            {
+              int algo = gcry_pk_map_name (pargs.r.ret_str);
+              gcry_pk_ctl (GCRYCTL_DISABLE_ALGO,
+                           &algo, sizeof algo );
+            }
+            break;
           case oNoSigCache: opt.no_sig_cache = 1; break;
           case oNoSigCreateCheck: opt.no_sig_create_check = 1; break;
          case oAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid = 1; break;
@@ -1461,12 +1946,21 @@ main( int argc, char **argv )
           case oPersonalCompressPreferences:
            pers_compress_list=pargs.r.ret_str;
            break;
+          case oAgentProgram: opt.agent_program = pargs.r.ret_str;  break;
           case oDisplay: opt.display = pargs.r.ret_str; break;
           case oTTYname: opt.ttyname = pargs.r.ret_str; break;
           case oTTYtype: opt.ttytype = pargs.r.ret_str; break;
           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;
+
+          case oEnableProgressFilter: opt.enable_progress_filter = 1; break;
+
          default : pargs.err = configfp? 1:2; break;
        }
     }
@@ -1474,10 +1968,10 @@ main( int argc, char **argv )
     if( configfp ) {
        fclose( configfp );
        configfp = NULL;
-       m_free(configname); configname = NULL;
+       xfree (configname); configname = NULL;
        goto next_pass;
     }
-    m_free( configname ); configname = NULL;
+    xfree ( configname ); configname = NULL;
     if( log_get_errorcount(0) )
        g10_exit(2);
     if( nogreeting )
@@ -1488,35 +1982,19 @@ main( int argc, char **argv )
                        strusage(11), strusage(13), strusage(14) );
        fprintf(stderr, "%s\n", strusage(15) );
     }
-  #ifdef IS_DEVELOPMENT_VERSION
+#ifdef IS_DEVELOPMENT_VERSION
     if( !opt.batch ) {
        log_info("NOTE: THIS IS A DEVELOPMENT VERSION!\n");
        log_info("It is only intended for test purposes and should NOT be\n");
        log_info("used in a production environment or with production keys!\n");
     }
-  #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);
+#endif
 
-       free_strlist(unsafe_files);
-      }
+#warning locking does not work - disabled
+    disable_dotlock ();
 
-    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"));
@@ -1545,138 +2023,148 @@ main( int argc, char **argv )
     if( opt.batch )
        tty_batchmode( 1 );
 
+#warning fix that
+#if 0
     secmem_set_flags( secmem_get_flags() & ~2 ); /* resume warnings */
-
+#endif
     set_debug();
-    g10_opt_homedir = opt.homedir;
 
     /* Do these after the switch(), so they can override settings. */
-    if(opt.pgp2 && (opt.pgp6 || opt.pgp7))
-      log_error(_("%s not allowed with %s!\n"),
-               "--pgp2",opt.pgp6?"--pgp6":"--pgp7");
-    else
+    if(PGP2)
       {
-       if(opt.pgp2)
+       int unusable=0;
+
+       if(cmd==aSign && !detached_sig)
+         {
+           log_info(_("you can only make detached or clear signatures "
+                      "while in --pgp2 mode\n"));
+           unusable=1;
+         }
+       else if(cmd==aSignEncr || cmd==aSignSym)
+         {
+           log_info(_("you can't sign and encrypt at the "
+                      "same time while in --pgp2 mode\n"));
+           unusable=1;
+         }
+       else if(argc==0 && (cmd==aSign || cmd==aEncr || cmd==aSym))
          {
-           int unusable=0;
+           log_info(_("you must use files (and not a pipe) when "
+                      "working with --pgp2 enabled.\n"));
+           unusable=1;
+         }
+       else if(cmd==aEncr || cmd==aSym)
+         {
+           /* Everything else should work without IDEA (except using
+              a secret key encrypted with IDEA and setting an IDEA
+              preference, but those have their own error
+              messages). */
 
-           if(cmd==aSign && !detached_sig)
+           if(openpgp_cipher_test_algo (CIPHER_ALGO_IDEA))
              {
-               log_info(_("you can only make detached or clear signatures "
-                          "while in --pgp2 mode\n"));
+               log_info(_("encrypting a message in --pgp2 mode requires "
+                          "the IDEA cipher\n"));
+               idea_cipher_warn(1);
                unusable=1;
              }
-           else if(cmd==aSignEncr || cmd==aSignSym)
+           else if(cmd==aSym)
              {
-               log_info(_("you can't sign and encrypt at the "
-                          "same time while in --pgp2 mode\n"));
-               unusable=1;
-             }
-           else if(argc==0 && (cmd==aSign || cmd==aEncr || cmd==aSym))
-             {
-               log_info(_("you must use files (and not a pipe) when "
-                          "working with --pgp2 enabled.\n"));
-               unusable=1;
-             }
-           else if(cmd==aEncr || cmd==aSym)
-             {
-               /* Everything else should work without IDEA (except using
-                  a secret key encrypted with IDEA and setting an IDEA
-                  preference, but those have their own error
-                  messages). */
-
-               if(check_cipher_algo(CIPHER_ALGO_IDEA))
-                 {
-                   log_info(_("encrypting a message in --pgp2 mode requires "
-                              "the IDEA cipher\n"));
-                   idea_cipher_warn(1);
-                   unusable=1;
-                 }
-               else if(cmd==aSym)
-                 {
-                   m_free(def_cipher_string);
-                   def_cipher_string = m_strdup("idea");
-                 }
+               /* This only sets IDEA for symmetric encryption
+                  since it is set via select_algo_from_prefs for
+                  pk encryption. */
+               xfree (def_cipher_string);
+               def_cipher_string = xstrdup ("idea");
              }
 
-           if(unusable)
-             {
-               log_info(_("this message may not be usable by %s\n"),
-                        "PGP 2.x");
-               opt.pgp2=0;
-             }
-           else
-             {
-               opt.rfc1991 = 1;
-               opt.rfc2440 = 0;
-               opt.force_mdc = 0;
-               opt.disable_mdc = 1;
-               opt.force_v4_certs = 0;
-               opt.sk_comments = 0;
-               opt.escape_from = 1;
-               opt.force_v3_sigs = 1;
-               opt.pgp2_workarounds = 1;
-               opt.ask_sig_expire = 0;
-               opt.ask_cert_expire = 0;
-               m_free(def_digest_string);
-               def_digest_string = m_strdup("md5");
-               opt.def_compress_algo = 1;
-             }
+           /* 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(opt.pgp6 || opt.pgp7)
+       if(unusable)
+         compliance_failure();
+       else
          {
-           opt.sk_comments=0;
-           opt.escape_from=1;
-           opt.force_v3_sigs=1;
-           opt.ask_sig_expire=0;
-           opt.def_compress_algo=1;
-
-           if(opt.pgp6) /* pgp7 has MDC */
-             {
-               opt.force_mdc=0;
-               opt.disable_mdc=1;
-             }
+           opt.force_mdc = 0;
+           opt.disable_mdc = 1;
+           opt.force_v4_certs = 0;
+           opt.sk_comments = 0;
+           opt.escape_from = 1;
+           opt.force_v3_sigs = 1;
+           opt.pgp2_workarounds = 1;
+           opt.ask_sig_expire = 0;
+           opt.ask_cert_expire = 0;
+           xfree (def_digest_string);
+           def_digest_string = xstrdup ("md5");
+           opt.def_compress_algo = 1;
          }
       }
+    else if(PGP6)
+      {
+       opt.sk_comments=0;
+       opt.escape_from=1;
+       opt.force_v3_sigs=1;
+       opt.ask_sig_expire=0;
+       opt.force_mdc=0;
+       opt.disable_mdc=1;
+      }
+    else if(PGP7)
+      {
+       opt.sk_comments=0;
+       opt.escape_from=1;
+       opt.force_v3_sigs=1;
+       opt.ask_sig_expire=0;
+      }
+    else if(PGP8)
+      {
+       opt.escape_from=1;
+      }
 
     /* must do this after dropping setuid, because string_to...
      * may try to load an module */
     if( def_cipher_string ) {
-       opt.def_cipher_algo = string_to_cipher_algo(def_cipher_string);
+       opt.def_cipher_algo = gcry_cipher_map_name (def_cipher_string);
        if(opt.def_cipher_algo==0 &&
-          ascii_strcasecmp(def_cipher_string,"idea")==0)
+          (ascii_strcasecmp(def_cipher_string,"idea")==0
+           || ascii_strcasecmp(def_cipher_string,"s1")==0))
          idea_cipher_warn(1);
-       m_free(def_cipher_string); def_cipher_string = NULL;
-       if( check_cipher_algo(opt.def_cipher_algo) )
+       xfree (def_cipher_string); def_cipher_string = NULL;
+       if( openpgp_cipher_test_algo (opt.def_cipher_algo) )
            log_error(_("selected cipher algorithm is invalid\n"));
     }
     if( def_digest_string ) {
-       opt.def_digest_algo = string_to_digest_algo(def_digest_string);
-       m_free(def_digest_string); def_digest_string = NULL;
-       if( check_digest_algo(opt.def_digest_algo) )
+       opt.def_digest_algo = gcry_md_map_name (def_digest_string);
+       xfree (def_digest_string); def_digest_string = NULL;
+       if( openpgp_md_test_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);
+       xfree (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;
-       if( check_digest_algo(opt.cert_digest_algo) )
+       opt.cert_digest_algo = gcry_md_map_name (cert_digest_string);
+       xfree (cert_digest_string); cert_digest_string = NULL;
+       if( openpgp_md_test_algo(opt.cert_digest_algo) )
            log_error(_("selected certification digest algorithm is invalid\n"));
     }
     if( s2k_cipher_string ) {
-       opt.s2k_cipher_algo = string_to_cipher_algo(s2k_cipher_string);
-       m_free(s2k_cipher_string); s2k_cipher_string = NULL;
-       if( check_cipher_algo(opt.s2k_cipher_algo) )
+       opt.s2k_cipher_algo = gcry_cipher_map_name (s2k_cipher_string);
+       xfree (s2k_cipher_string); s2k_cipher_string = NULL;
+       if( openpgp_cipher_test_algo (opt.s2k_cipher_algo) )
            log_error(_("selected cipher algorithm is invalid\n"));
     }
     if( s2k_digest_string ) {
-       opt.s2k_digest_algo = string_to_digest_algo(s2k_digest_string);
-       m_free(s2k_digest_string); s2k_digest_string = NULL;
-       if( check_digest_algo(opt.s2k_digest_algo) )
+       opt.s2k_digest_algo = gcry_md_map_name (s2k_digest_string);
+       xfree (s2k_digest_string); s2k_digest_string = NULL;
+       if( openpgp_md_test_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 )
@@ -1720,12 +2208,74 @@ main( int argc, char **argv )
     if( log_get_errorcount(0) )
        g10_exit(2);
 
+    /* Check our chosen algorithms against the list of legal
+       algorithms. */
+
+    if(!GNUPG)
+      {
+       const char *badalg=NULL;
+       preftype_t badtype=PREFTYPE_NONE;
+
+       if (opt.def_cipher_algo
+            && !algo_available (PREFTYPE_SYM,opt.def_cipher_algo,NULL))
+         {
+           badalg = gcry_cipher_algo_name (opt.def_cipher_algo);
+           badtype = PREFTYPE_SYM;
+         }
+       else if (opt.def_digest_algo
+               && !algo_available (PREFTYPE_HASH,opt.def_digest_algo,NULL))
+         {
+           badalg = gcry_md_algo_name (opt.def_digest_algo);
+           badtype = PREFTYPE_HASH;
+         }
+       else if (opt.cert_digest_algo
+                && !algo_available (PREFTYPE_HASH,opt.cert_digest_algo,NULL))
+         {
+           badalg = gcry_md_algo_name (opt.cert_digest_algo);
+           badtype = PREFTYPE_HASH;
+         }
+       else if (opt.def_compress_algo!=-1
+                 && !algo_available (PREFTYPE_ZIP,opt.def_compress_algo,NULL))
+         {
+           badalg = compress_algo_to_string (opt.def_compress_algo);
+           badtype = PREFTYPE_ZIP;
+         }
+
+       if (badalg)
+         {
+           switch(badtype)
+             {
+             case PREFTYPE_SYM:
+               log_info(_("you may not use cipher algorithm \"%s\" "
+                          "while in %s mode\n"),
+                        badalg,compliance_option_string());
+               break;
+             case PREFTYPE_HASH:
+               log_info(_("you may not use digest algorithm \"%s\" "
+                          "while in %s mode\n"),
+                        badalg,compliance_option_string());
+               break;
+             case PREFTYPE_ZIP:
+               log_info(_("you may not use compression algorithm \"%s\" "
+                          "while in %s mode\n"),
+                        badalg,compliance_option_string());
+               break;
+             default:
+               BUG();
+             }
+
+           compliance_failure();
+         }
+      }
+
     /* set the random seed file */
     if( use_random_seed ) {
        char *p = make_filename(opt.homedir, "random_seed", NULL );
-       check_permissions(p,0,0);
+#warning No random seed file yet
+#if 0
        set_random_seed_file(p);
-       m_free(p);
+#endif
+       xfree (p);
     }
 
     if( !cmd && opt.fingerprint && !with_fpr ) {
@@ -1777,7 +2327,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);
@@ -1796,20 +2346,14 @@ main( int argc, char **argv )
       case aDeArmor:
       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 );
+      case aChangePIN:
        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;
     }
     if( rc )
-       log_error(_("failed to initialize the TrustDB: %s\n"), g10_errstr(rc));
+       log_error(_("failed to initialize the TrustDB: %s\n"), gpg_strerror (rc));
 
 
     switch (cmd) {
@@ -1831,22 +2375,23 @@ main( int argc, char **argv )
        if( argc > 1 )
            wrong_args(_("--store [filename]"));
        if( (rc = encode_store(fname)) )
-           log_error_f( print_fname_stdin(fname),
-                       "store failed: %s\n", g10_errstr(rc) );
+           log_error ("\b%s: store failed: %s\n",
+                       print_fname_stdin(fname), gpg_strerror (rc) );
        break;
       case aSym: /* encrypt the given file only with the symmetric cipher */
        if( argc > 1 )
            wrong_args(_("--symmetric [filename]"));
        if( (rc = encode_symmetric(fname)) )
-           log_error_f(print_fname_stdin(fname),
-                       "symmetric encryption failed: %s\n",g10_errstr(rc) );
+           log_error ("\b%s: symmetric encryption failed: %s\n",
+                       print_fname_stdin(fname), gpg_strerror (rc) );
        break;
 
       case aEncr: /* encrypt the given file */
        if( argc > 1 )
            wrong_args(_("--encrypt [filename]"));
        if( (rc = encode_crypt(fname,remusr)) )
-           log_error("%s: encryption failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) );
+           log_error("%s: encryption failed: %s\n",
+                      print_fname_stdin(fname), gpg_strerror (rc) );
        break;
 
       case aEncrFiles: /* encrypt the given files */
@@ -1863,12 +2408,12 @@ main( int argc, char **argv )
            if( argc > 1 )
                wrong_args(_("--sign [filename]"));
            if( argc ) {
-               sl = m_alloc_clear( sizeof *sl + strlen(fname));
+               sl = xcalloc (1, sizeof *sl + strlen(fname));
                strcpy(sl->d, fname);
            }
        }
        if( (rc = sign_file( sl, detached_sig, locusr, 0, NULL, NULL)) )
-           log_error("signing failed: %s\n", g10_errstr(rc) );
+           log_error("signing failed: %s\n", gpg_strerror (rc) );
        free_strlist(sl);
        break;
 
@@ -1876,13 +2421,13 @@ main( int argc, char **argv )
        if( argc > 1 )
            wrong_args(_("--sign --encrypt [filename]"));
        if( argc ) {
-           sl = m_alloc_clear( sizeof *sl + strlen(fname));
+           sl = xcalloc (1, sizeof *sl + strlen(fname));
            strcpy(sl->d, fname);
        }
        else
            sl = NULL;
        if( (rc = sign_file(sl, detached_sig, locusr, 1, remusr, NULL)) )
-           log_error("%s: sign+encrypt failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) );
+           log_error("%s: sign+encrypt failed: %s\n", print_fname_stdin(fname), gpg_strerror (rc) );
        free_strlist(sl);
        break;
 
@@ -1892,7 +2437,7 @@ main( int argc, char **argv )
        rc = sign_symencrypt_file (fname, locusr);
         if (rc)
            log_error("%s: sign+symmetric failed: %s\n",
-                      print_fname_stdin(fname), g10_errstr(rc) );
+                      print_fname_stdin(fname), gpg_strerror (rc) );
        break;
 
       case aClearsign: /* make a clearsig */
@@ -1900,24 +2445,24 @@ main( int argc, char **argv )
            wrong_args(_("--clearsign [filename]"));
        if( (rc = clearsign_file(fname, locusr, NULL)) )
            log_error("%s: clearsign failed: %s\n",
-                      print_fname_stdin(fname), g10_errstr(rc) );
+                      print_fname_stdin(fname), gpg_strerror (rc) );
        break;
 
       case aVerify:
        if( (rc = verify_signatures( argc, argv ) ))
-           log_error("verify signatures failed: %s\n", g10_errstr(rc) );
+           log_error("verify signatures failed: %s\n", gpg_strerror (rc) );
        break;
 
       case aVerifyFiles:
        if( (rc = verify_files( argc, argv ) ))
-           log_error("verify files failed: %s\n", g10_errstr(rc) );
+           log_error("verify files failed: %s\n", gpg_strerror (rc) );
        break;
 
       case aDecrypt:
        if( argc > 1 )
            wrong_args(_("--decrypt [filename]"));
        if( (rc = decrypt_message( fname ) ))
-           log_error("decrypt_message failed: %s\n", g10_errstr(rc) );
+           log_error("decrypt_message failed: %s\n", gpg_strerror (rc) );
        break;
 
       case aDecryptFiles:
@@ -1929,7 +2474,7 @@ main( int argc, char **argv )
            wrong_args(_("--sign-key user-id"));
        username = make_username( fname );
        keyedit_menu(fname, locusr, NULL, 1 );
-       m_free(username);
+       xfree (username);
        break;
 
       case aLSignKey:
@@ -1937,7 +2482,7 @@ main( int argc, char **argv )
            wrong_args(_("--lsign-key user-id"));
        username = make_username( fname );
        keyedit_menu(fname, locusr, NULL, 2 );
-       m_free(username);
+       xfree (username);
        break;
 
       case aNRSignKey:
@@ -1945,7 +2490,7 @@ main( int argc, char **argv )
            wrong_args(_("--nrsign-key user-id"));
        username = make_username( fname );
        keyedit_menu(fname, locusr, NULL, 3 );
-        m_free(username);
+        xfree (username);
         break;
 
       case aNRLSignKey:
@@ -1953,7 +2498,7 @@ main( int argc, char **argv )
            wrong_args(_("--nrlsign-key user-id"));
        username = make_username( fname );
        keyedit_menu(fname, locusr, NULL, 4 );
-        m_free(username);
+        xfree (username);
         break;
 
       case aEditKey: /* Edit a key signature */
@@ -1969,7 +2514,7 @@ main( int argc, char **argv )
        }
        else
            keyedit_menu(username, locusr, NULL, 0 );
-       m_free(username);
+       xfree (username);
        break;
 
       case aDeleteKeys:
@@ -2046,9 +2591,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, opt.import_options );
+       import_keys( argc? argv:NULL, argc, NULL, opt.import_options );
        break;
 
       case aExport:
@@ -2059,20 +2604,40 @@ main( int argc, char **argv )
        for( ; argc; argc--, argv++ )
            add_to_strlist2( &sl, *argv, utf8_strings );
        if( cmd == aSendKeys )
-           keyserver_export( sl );
+           rc=keyserver_export( sl );
        else if( cmd == aRecvKeys )
-           keyserver_import( sl );
+           rc=keyserver_import( sl );
        else
-           export_pubkeys( sl, opt.export_options );
+           rc=export_pubkeys( sl, opt.export_options );
+       if(rc)
+         {
+           if(cmd==aSendKeys)
+             log_error(_("keyserver send failed: %s\n"),gpg_strerror (rc));
+           else if(cmd==aRecvKeys)
+             log_error(_("keyserver receive failed: %s\n"),gpg_strerror (rc));
+           else
+             log_error(_("key export failed: %s\n"),gpg_strerror (rc));
+         }
        free_strlist(sl);
        break;
 
      case aSearchKeys:
        sl = NULL;
        for( ; argc; argc--, argv++ )
-         append_to_strlist2( &sl, *argv, utf8_strings );
+          {
+            if (utf8_strings)
+              sl = append_to_strlist ( &sl, *argv );
+            else
+              {
+                char *p = native_to_utf8 ( *argv );
+                sl = append_to_strlist( &sl, p );
+                xfree( p );
+              }
+          }
 
-       keyserver_search( sl );
+       rc=keyserver_search( sl );
+       if(rc)
+         log_error(_("keyserver search failed: %s\n"),gpg_strerror (rc));
        free_strlist(sl);
        break;
 
@@ -2080,7 +2645,9 @@ main( int argc, char **argv )
        sl = NULL;
        for( ; argc; argc--, argv++ )
            add_to_strlist2( &sl, *argv, utf8_strings );
-       keyserver_refresh(sl);
+       rc=keyserver_refresh(sl);
+       if(rc)
+         log_error(_("keyserver refresh failed: %s\n"),gpg_strerror (rc));
        free_strlist(sl);
        break;
 
@@ -2105,7 +2672,7 @@ main( int argc, char **argv )
            wrong_args("--gen-revoke user-id");
        username =  make_username(*argv);
        gen_revoke( username );
-       m_free( username );
+       xfree ( username );
        break;
 
       case aDesigRevoke:
@@ -2113,7 +2680,7 @@ main( int argc, char **argv )
            wrong_args("--desig-revoke user-id");
        username =  make_username(*argv);
        gen_desig_revoke( username );
-       m_free( username );
+       xfree ( username );
        break;
 
       case aDeArmor:
@@ -2121,7 +2688,7 @@ main( int argc, char **argv )
            wrong_args("--dearmor [file]");
        rc = dearmor_file( argc? *argv: NULL );
        if( rc )
-           log_error(_("dearmoring failed: %s\n"), g10_errstr(rc));
+           log_error(_("dearmoring failed: %s\n"), gpg_strerror (rc));
        break;
 
       case aEnArmor:
@@ -2129,11 +2696,12 @@ main( int argc, char **argv )
            wrong_args("--enarmor [file]");
        rc = enarmor_file( argc? *argv: NULL );
        if( rc )
-           log_error(_("enarmoring failed: %s\n"), g10_errstr(rc));
+           log_error(_("enarmoring failed: %s\n"), gpg_strerror (rc));
        break;
 
 
       case aPrimegen:
+#if 0 /*FIXME-XXX*/
        {   int mode = argc < 2 ? 0 : atoi(*argv);
 
            if( mode == 1 && argc == 2 ) {
@@ -2145,7 +2713,7 @@ main( int argc, char **argv )
                                             atoi(argv[2]), NULL,NULL ), 1);
            }
            else if( mode == 3 && argc == 3 ) {
-               MPI *factors;
+               gcry_mpi_t *factors;
                mpi_print( stdout, generate_elg_prime(
                                             1, atoi(argv[1]),
                                             atoi(argv[2]), NULL,&factors ), 1);
@@ -2153,7 +2721,7 @@ main( int argc, char **argv )
                mpi_print( stdout, factors[0], 1 ); /* print q */
            }
            else if( mode == 4 && argc == 3 ) {
-               MPI g = mpi_alloc(1);
+               gcry_mpi_t g = mpi_alloc(1);
                mpi_print( stdout, generate_elg_prime(
                                                 0, atoi(argv[1]),
                                                 atoi(argv[2]), g, NULL ), 1);
@@ -2165,6 +2733,7 @@ main( int argc, char **argv )
                wrong_args("--gen-prime mode bits [qbits] ");
            putchar('\n');
        }
+#endif
        break;
 
       case aGenRandom:
@@ -2184,14 +2753,14 @@ main( int argc, char **argv )
                    other tools */
                size_t n = !endless && count < 99? count : 99;
 
-               p = get_random_bits( n*8, level, 0);
-             #ifdef HAVE_DOSISH_SYSTEM
+               p = gcry_random_bytes (n, level);
+#ifdef HAVE_DOSISH_SYSTEM
                setmode ( fileno(stdout), O_BINARY );
-             #endif
+#endif
                 if (opt.armor) {
                     char *tmp = make_radix64_string (p, n);
                     fputs (tmp, stdout);
-                    m_free (tmp);
+                    xfree (tmp);
                     if (n%3 == 1)
                       putchar ('=');
                     if (n%3)
@@ -2199,7 +2768,7 @@ main( int argc, char **argv )
                 } else {
                     fwrite( p, n, 1, stdout );
                 }
-               m_free(p);
+               xfree (p);
                if( !endless )
                    count -= n;
            }
@@ -2213,7 +2782,7 @@ main( int argc, char **argv )
            wrong_args("--print-md algo [files]");
        {
            int all_algos = (**argv=='*' && !(*argv)[1]);
-           int algo = all_algos? 0 : string_to_digest_algo(*argv);
+           int algo = all_algos? 0 : gcry_md_map_name (*argv);
 
            if( !algo && !all_algos )
                log_error(_("invalid hash algorithm `%s'\n"), *argv );
@@ -2270,7 +2839,7 @@ main( int argc, char **argv )
        for( ; argc; argc--, argv++ ) {
            username = make_username( *argv );
            list_trust_path( username );
-           m_free(username);
+           xfree (username);
        }
        break;
 
@@ -2298,6 +2867,15 @@ main( int argc, char **argv )
         keydb_rebuild_caches ();
         break;
 
+    case aChangePIN:
+      if (!argc)
+        change_pin (0);
+      else if (argc == 1)
+        change_pin ( atoi (*argv));
+      else
+        wrong_args ("--change-pin [no]");
+      break;
+
       case aListPackets:
        opt.list_packets=2;
       default:
@@ -2324,7 +2902,7 @@ main( int argc, char **argv )
            }
            rc = proc_packets(NULL, a );
            if( rc )
-               log_error("processing message failed: %s\n", g10_errstr(rc) );
+               log_error("processing message failed: %s\n", gpg_strerror (rc) );
            iobuf_close(a);
        }
        break;
@@ -2337,58 +2915,114 @@ main( int argc, char **argv )
     return 8; /*NEVER REACHED*/
 }
 
+/* Note: This function is used by signal handlers!. */
+static void
+emergency_cleanup (void)
+{
+  gcry_control (GCRYCTL_TERM_SECMEM );
+}
+
 
 void
 g10_exit( int rc )
 {
-    update_random_seed_file();
-    if( opt.debug & DBG_MEMSTAT_VALUE ) {
-       m_print_stats("on exit");
-       random_dump_stats();
-    }
-    if( opt.debug )
-       secmem_dump_stats();
-    secmem_term();
+  /* FIXME-XX update_random_seed_file(); */
+/*      if( opt.debug & DBG_MEMSTAT_VALUE ) { */
+/*          m_print_stats("on exit"); */
+/*     random_dump_stats(); */
+/*      } */
+/*      if( opt.debug ) */
+/*     secmem_dump_stats(); */
+    gcry_control (GCRYCTL_TERM_SECMEM );
     rc = rc? rc : log_get_errorcount(0)? 2 :
                        g10_errors_seen? 1 : 0;
     exit(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 = ", gcry_md_algo_name (algo));
+  else
+    algo=abs(algo);
+
+  count=indent;
+
+  p = gcry_md_read (md, algo);
+  n = gcry_md_get_algo_dlen (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
@@ -2407,8 +3041,8 @@ print_hashline( MD_HANDLE md, int algo, const char *fname )
     }
     putchar(':');
     printf("%d:", algo );
-    p = md_read( md, algo );
-    n = md_digest_length(algo);
+    p = gcry_md_read (md, algo );
+    n = gcry_md_get_algo_dlen (algo);
     for(i=0; i < n ; i++, p++ ) 
         printf("%02X", *p );
     putchar(':');
@@ -2422,76 +3056,86 @@ 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
+#ifdef HAVE_DOSISH_SYSTEM
        setmode ( fileno(fp) , O_BINARY );
-      #endif
-       pname = m_strdup("[stdin]: ");
+#endif
     }
     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;
     }
 
-    md = md_open( 0, 0 );
+    gcry_md_open (&md, 0, 0 );
     if( algo )
-       md_enable( md, algo );
+       gcry_md_enable ( md, algo );
     else {
-       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 );
+       gcry_md_enable (md, GCRY_MD_MD5 );
+       gcry_md_enable (md, GCRY_MD_SHA1 );
+       gcry_md_enable (md, GCRY_MD_RMD160 );
+#ifdef USE_TIGER192
+       gcry_md_enable (md, GCRY_MD_TIGER );
+#endif
+#ifdef USE_SHA256
+       gcry_md_enable (md, GCRY_MD_SHA256 );
+#endif
+#ifdef USE_SHA512
+       gcry_md_enable (md, GCRY_MD_SHA384 );
+       gcry_md_enable (md, GCRY_MD_SHA512 );
+#endif
     }
 
     while( (n=fread( buf, 1, DIM(buf), fp )) )
-       md_write( md, buf, n );
+       gcry_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);
+       gcry_md_final (md);
         if ( opt.with_colons ) {
             if ( algo ) 
                 print_hashline( md, algo, fname );
             else {
-                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 );
+                print_hashline( md, GCRY_MD_MD5, fname );
+                print_hashline( md, GCRY_MD_SHA1, fname );
+                print_hashline( md, GCRY_MD_RMD160, fname );
+#ifdef USE_TIGER192
+               print_hashline( md, GCRY_MD_TIGER, fname );
+#endif
+#ifdef USE_SHA256
+                print_hashline( md, GCRY_MD_SHA256, fname );
+#endif
+#ifdef USE_SHA512
+               print_hashline( md, GCRY_MD_SHA384, fname );
+               print_hashline( md, GCRY_MD_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, GCRY_MD_MD5, fname );
+                print_hex( md, GCRY_MD_SHA1, fname );
+                print_hex( md, GCRY_MD_RMD160, fname );
+#ifdef USE_TIGER192
+               print_hex( md, GCRY_MD_TIGER, fname );
+#endif
+#ifdef USE_SHA256
+                print_hex( md, GCRY_MD_SHA256, fname );
+#endif
+#ifdef USE_SHA512
+               print_hex( md, GCRY_MD_SHA384, fname );
+               print_hex( md, GCRY_MD_SHA512, fname );
+#endif
             }
-            putchar('\n');
         }
     }
-    md_close(md);
+    gcry_md_close (md);
 
     if( fp != stdin )
        fclose(fp);
@@ -2510,6 +3154,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;
@@ -2521,13 +3166,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++ ) {