Merged with gpg 1.4.3 code.
authorWerner Koch <wk@gnupg.org>
Wed, 19 Apr 2006 11:26:11 +0000 (11:26 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 19 Apr 2006 11:26:11 +0000 (11:26 +0000)
The gpg part does not yet build.

90 files changed:
ChangeLog
NEWS
common/ChangeLog
common/homedir.c
common/iobuf.h
common/util.h
configure.ac
g10/ChangeLog
g10/Makefile.am
g10/armor.c
g10/build-packet.c
g10/call-agent.c
g10/card-util.c
g10/cipher.c
g10/compress.c
g10/dearmor.c
g10/decrypt.c
g10/delkey.c
g10/encode.c
g10/encr-data.c
g10/exec.c
g10/exec.h
g10/export.c
g10/filter.h
g10/free-packet.c
g10/getkey.c
g10/global.h [deleted file]
g10/gpg.c
g10/gpg.h
g10/gpgv.c
g10/helptext.c
g10/import.c
g10/kbnode.c
g10/keydb.c
g10/keydb.h
g10/keyedit.c
g10/keygen.c
g10/keyid.c
g10/keylist.c
g10/keyring.c
g10/keyring.h
g10/keyserver-internal.h
g10/keyserver.c
g10/main.h
g10/mainproc.c
g10/mdfilter.c
g10/misc.c
g10/openfile.c
g10/options.h
g10/packet.h
g10/parse-packet.c
g10/passphrase.c
g10/photoid.c
g10/photoid.h
g10/pipemode.c [deleted file]
g10/pkclist.c
g10/plaintext.c
g10/progress.c
g10/pubkey-enc.c
g10/revoke.c
g10/seckey-cert.c
g10/seskey.c
g10/sig-check.c
g10/sign.c
g10/signal.c
g10/skclist.c
g10/status.c
g10/status.h
g10/tdbdump.c
g10/tdbio.c
g10/tdbio.h
g10/textfilter.c
g10/trustdb.c
g10/trustdb.h
g10/verify.c
include/ChangeLog
include/cipher.h
include/host2net.h
include/http.h
include/i18n.h
include/keyserver.h
include/memory.h
include/mpi.h
include/types.h
include/util.h
include/zlib-riscos.h
jnlib/ChangeLog
jnlib/libjnlib-config.h
jnlib/strlist.c
jnlib/strlist.h

index ea1ddf0..6e52288 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2006-04-18  Werner Koch  <wk@g10code.com>
+
+       * configure.ac (PK_UID_CACHE_SIZE): New.
+
+2006-04-07  Werner Koch  <wk@g10code.com>
+
+       * configure.ac: Use new method to include the SVN revison.  Now it
+       is the actual global revision number.
+
 2005-12-20  Werner Koch  <wk@g10code.com>
 
        Released 1.9.20.
diff --git a/NEWS b/NEWS
index a003b3f..6413242 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,12 +1,14 @@
 Noteworthy changes in version 1.9.21
 -------------------------------------------------
 
+ * [scdaemon] New command APDU.
+
  * [scdaemon] Support for keypads of some readers.  Tested only with
    SPR532. New option --disable-keypad.
 
- * Support for CardMan 4040 PCMCIA reader.
+ * [scdaemon] Support for CardMan 4040 PCMCIA reader.
 
- * Cards are not anymore reseted at the end of a connection. 
+ * [scdaemon] Cards are not anymore reseted at the end of a connection. 
 
  * [gpgsm] Kludge to allow use of Bundesnetzagentur issued
    certificates.
index 4a18400..54bce45 100644 (file)
@@ -1,3 +1,8 @@
+2006-04-18  Werner Koch  <wk@g10code.com>
+
+       * homedir.c (w32_shgetfolderpath): New.  Taken from gpg 1.4.3.
+       (default_homedir): Use it.
+
 2005-10-08  Marcus Brinkmann  <marcus@g10code.de>
 
        * signal.c (get_signal_name): Check value of HAVE_DECL_SYS_SIGLIST
index ab5b1d2..a118cba 100644 (file)
@@ -1,5 +1,5 @@
 /* homedir.c - Setup the home directory.
- *     Copyright (C) 2004 Free Software Foundation, Inc.
+ *     Copyright (C) 2004, 2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include "util.h"
 #include "sysutils.h"
 
+
+/* This is a helper function to load a Windows function from either of
+   one DLLs. */
+#ifdef HAVE_W32_SYSTEM
+static HRESULT
+w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
+{
+  static int initialized;
+  static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
+
+  if (!initialized)
+    {
+      static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
+      void *handle;
+      int i;
+
+      initialized = 1;
+
+      for (i=0, handle = NULL; !handle && dllnames[i]; i++)
+        {
+          handle = dlopen (dllnames[i], RTLD_LAZY);
+          if (handle)
+            {
+              func = dlsym (handle, "SHGetFolderPathA");
+              if (!func)
+                {
+                  dlclose (handle);
+                  handle = NULL;
+                }
+            }
+        }
+    }
+
+  if (func)
+    return func (a,b,c,d,e);
+  else
+    return -1;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
 /* Set up the default home directory.  The usual --homedir option
    should be parsed later. */
 const char *
@@ -56,15 +97,15 @@ default_homedir (void)
     {
       char path[MAX_PATH];
       
-      /* fixme: It might be better to use LOCAL_APPDATA because this
-         is defined as "non roaming" and thus more likely to be kept
+      /* It might be better to use LOCAL_APPDATA because this is
+         defined as "non roaming" and thus more likely to be kept
          locally.  For private keys this is desired.  However, given
          that many users copy private keys anyway forth and back,
-         using a system roaming serives might be better than to let
+         using a system roaming services might be better than to let
          them do it manually.  A security conscious user will anyway
          use the registry entry to have better control.  */
-      if (SHGetFolderPath(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, 
-                          NULL, 0, path) >= 0) 
+      if (w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, 
+                               NULL, 0, path) >= 0) 
         {
           char *tmp = xmalloc (strlen (path) + 6 +1);
           strcpy (stpcpy (tmp, path), "\\gnupg");
index b991717..def0a65 100644 (file)
@@ -36,6 +36,7 @@
 #define IOBUFCTRL_USER     16
 
 typedef struct iobuf_struct *iobuf_t;
+typedef struct iobuf_struct *IOBUF;  /* Compatibility with gpg 1.4. */
 
 /* fixme: we should hide most of this stuff */
 struct iobuf_struct
index 1ced59b..68f5222 100644 (file)
 #define xrealloc(a,b)    gcry_xrealloc ((a),(b))
 #define xstrdup(a)       gcry_xstrdup ((a))
 
+/* For compatibility with gpg 1.4 we also define these: */
+#define xmalloc_clear(a) gcry_xcalloc (1, (a))
+#define xmalloc_secure_clear(a) gcry_xcalloc_secure (1, (a))
+
 
 /* A type to hold the ISO time.  Note that this this is the same as
    the the KSBA type ksba_isotime_t. */
@@ -133,7 +137,7 @@ int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b);
 unsigned char *make_simple_sexp_from_hexstr (const char *line,
                                              size_t *nscanned);
 
-/*-- homedir. c --*/
+/*-- homedir.c --*/
 const char *default_homedir (void);
 
 
index 9b0f04c..53cbc38 100644 (file)
@@ -23,11 +23,16 @@ AC_PREREQ(2.52)
 min_automake_version="1.9.3"
 
 # Remember to change the version number immediately *after* a release.
-# Uncomment the my_iscvs macro for non-released code.
-m4_define(my_version, [1.9.21])
-m4_define(my_iscvs, yes)
-AC_INIT([gnupg], my_version[]m4_ifdef([my_iscvs], [-cvs[]m4_translit(
-                 [$Revision$],[Ra-z $:])]), [gnupg-devel@gnupg.org])
+# Set my_issvn to "yes" for non-released code.  Remember to run an
+# "svn up" and "autogen.sh" right before creating a distribution.
+m4_define([my_version], [1.9.21])
+m4_define([my_issvn], [yes])
+
+
+m4_define([svn_revision], m4_esyscmd([echo -n $((svn info 2>/dev/null \
+            || echo 'Revision: 0')|sed -n '/^Revision:/ {s/[^0-9]//gp;q}')]))
+AC_INIT([gnupg], my_version[]m4_if(my_issvn,[yes],[-svn[]svn_revision]),
+        [gnupg-devel@gnupg.org])
 # Set development_version to yes if the minor number is odd or you
 # feel that the default check for a development version is not
 # sufficient.
@@ -219,6 +224,34 @@ if test "$use_exec" = yes ; then
     AC_MSG_RESULT($enableval)
   fi
 
+
+dnl
+dnl Check for the key/uid cache size.  This can't be zero, but can be
+dnl pretty small on embedded systems.
+dnl
+AC_MSG_CHECKING([for the size of the key and uid cache])
+AC_ARG_ENABLE(key-cache,
+       AC_HELP_STRING([--enable-key-cache=SIZE],[Set key cache to SIZE (default 4096)]),,enableval=4096)
+
+if test "$enableval" = "no"; then
+   enableval=5
+elif test "$enableval" = "yes" || test "$enableval" = ""; then
+   enableval=4096
+fi
+
+changequote(,)dnl
+key_cache_size=`echo "$enableval" | sed 's/[A-Za-z]//g'`
+changequote([,])dnl
+
+if test "$enableval" != "$key_cache_size" || test "$key_cache_size" -lt 5; then
+   AC_MSG_ERROR([invalid key-cache size])
+fi
+
+AC_MSG_RESULT($key_cache_size)
+AC_DEFINE_UNQUOTED(PK_UID_CACHE_SIZE,$key_cache_size,[Size of the key and UID caches])
+
+
+
 dnl
 dnl Check whether we want to use Linux capabilities
 dnl
index 0ae73b5..6259bdc 100644 (file)
@@ -1,3 +1,33 @@
+2006-04-18  Werner Koch  <wk@g10code.com>
+
+       * tdbio.c (open_db, migrate_from_v2): Removed feature to migration
+       from old trustdb version 2.
+
+       * gpg.c, mainproc.c: Removed pipemode feature.
+
+       * status.c: Removed shared memory coprocess stuff
+
+       Merged with current gpg 1.4.3 code.
+       
+       * keygen.c, keyid.c, misc.c, openfile.c, verify.c, trustdb.c
+       * textfilter.c, tdbio.c, tdbdump.c, status.c, skclist.c, signal.c
+       * sign.c, sig-check.c, seskey.c, seckey-cert.c, revoke.c
+       * pubkey-enc.c, progress.c, plaintext.c, pkclist.c, photoid.c
+       * passphrase.c, parse-packet.c, mdfilter.c, mainproc.c
+       * keyserver.c, keyring.c, keylist.c, keyedit.c, keydb.c, kbnode.c
+       * import.c, getkey.c, gpgv.c, helptext.c, free-packet.c
+       * build-packet.c, cipher.c, compress.c, dearmor.c, decrypt.c
+       * delkey.c, encr-data.c, encode.c, exec.c, export.c
+       * gpg.c, armor.c: Updated from gnupg-1.4.3 and merged back gcry and
+       gnupg-1.9 related changes.
+       * trustdb.h, tdbio.h, status.h, photoid.h, packet.h, options.h
+       * main.h, keyserver-internal.h, keyring.h, keydb.h, filter.h
+       * exec.h: Ditto.
+       * global.h: Removed after merging constants with gpg.h.
+       * comment.c, pipemode.c: Removed.
+       * card-util.c: Updated from gnupg-1.4.3.
+       * compress-bz2.c: New.
+       
 2005-06-15  Werner Koch  <wk@g10code.com>
 
        * g10.c (print_hashline, add_group): Fixes for signed/unsigned
@@ -9007,7 +9037,8 @@ Thu Feb 12 22:24:42 1998  Werner Koch  (wk@frodo)
        * pubkey-enc.c (get_session_key): rewritten
 
 
- Copyright 1998,1999,2000,2001,2002,2003 Free Software Foundation, Inc.
+ Copyright 1998,1999,2000,2001,2002,2003,2004,2005,
+          2006 Free Software Foundation, Inc.
 
  This file is free software; as a special exception the author gives
  unlimited permission to copy and/or distribute it, with or without
index f371dab..1deacb9 100644 (file)
@@ -1,5 +1,5 @@
 # Copyright (C) 1998, 1999, 2000, 2001, 2002,
-#               2003  Free Software Foundation, Inc.
+#               2003, 2006  Free Software Foundation, Inc.
 #
 # This file is part of GnuPG.
 #
@@ -26,16 +26,17 @@ AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/common \
 
 include $(top_srcdir)/am/cmacros.am
 
-AM_CFLAGS = $(LIBGCRYPT_CFLAGS)
+AM_CFLAGS = $(LIBGCRYPT_CFLAGS) -Wno-pointer-sign
 
 needed_libs = ../gl/libgnu.a ../common/libcommon.a ../jnlib/libjnlib.a
 
 bin_PROGRAMS = gpg2 gpgv2
 
 common_source =  \
-             global.h gpg.h    \
+             gpg.h             \
              build-packet.c    \
              compress.c        \
+             compress-bz2.c    \
              filter.h          \
              free-packet.c     \
              getkey.c          \
@@ -55,7 +56,6 @@ common_source =  \
              keyid.c           \
              packet.h          \
              parse-packet.c    \
-             comment.c         \
              status.c          \
              status.h          \
              plaintext.c       \
@@ -63,7 +63,7 @@ common_source =  \
              keylist.c         \
              pkglue.c pkglue.h 
 
-gpg2_SOURCES  = g10.c          \
+gpg2_SOURCES  = gpg.c          \
              $(common_source)  \
              pkclist.c         \
              skclist.c         \
@@ -88,7 +88,6 @@ gpg2_SOURCES  = g10.c         \
              tdbio.h           \
              delkey.c          \
              keygen.c          \
-             pipemode.c        \
              helptext.c        \
              keyserver.c       \
              keyserver-internal.h \
index 121ec3a..a154c5c 100644 (file)
@@ -1,6 +1,6 @@
 /* armor.c - Armor flter
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
- *                                             Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ *               2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -16,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
@@ -30,7 +31,6 @@
 #include "gpg.h"
 #include "errors.h"
 #include "iobuf.h"
-#include "memory.h"
 #include "util.h"
 #include "filter.h"
 #include "packet.h"
 #include "status.h"
 #include "i18n.h"
 
-#ifdef HAVE_DOSISH_SYSTEM
-#define LF "\r\n"
-#else
-#define LF "\n"
-#endif
-
 #define MAX_LINELEN 20000
 
 #define CRCINIT 0xB704CE
@@ -120,7 +114,6 @@ static char *tail_strings[] = {
 };
 
 
-
 static void
 initialize(void)
 {
@@ -193,7 +186,7 @@ is_armored( const byte *buf )
  *        filter to do further processing.
  */
 int
-use_armor_filter( iobuf_t a )
+use_armor_filter( IOBUF a )
 {
     byte buf[1];
     int n;
@@ -292,17 +285,24 @@ is_armor_header( byte *line, unsigned len )
     save_p = p;
     p += 5;
 
-    /* Some mail programs on Windows seem to add spaces to the end of
-       the line.  This becomes strict if --openpgp is set. */
-
-    if(!RFC2440)
-      while(*p==' ')
+    /* Some Windows environments seem to add whitespace to the end of
+       the line, so we strip it here.  This becomes strict if
+       --rfc2440 is set since 2440 reads "The header lines, therefore,
+       MUST start at the beginning of a line, and MUST NOT have text
+       following them on the same line."  It is unclear whether "text"
+       refers to all text or just non-whitespace text. */
+
+    if(RFC2440)
+      {
+       if( *p == '\r' )
+         p++;
+       if( *p == '\n' )
+         p++;
+      }
+    else
+      while(*p==' ' || *p=='\r' || *p=='\n' || *p=='\t')
        p++;
 
-    if( *p == '\r' )
-       p++;
-    if( *p == '\n' )
-       p++;
     if( *p )
        return -1; /* garbage after dashes */
     save_c = *save_p; *save_p = 0;
@@ -334,21 +334,35 @@ parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len )
     int hashes=0;
     unsigned int len2;
 
-    len2 = length_sans_trailing_ws( line, len );
+    len2 = check_trailing_ws( line, len );
     if( !len2 ) {
         afx->buffer_pos = len2;  /* (it is not the fine way to do it here) */
        return 0; /* WS only: same as empty line */
     }
-    len = len2;
-    line[len2] = 0;
+
+    /*
+      This is fussy.  The spec says that a header line is delimited
+      with a colon-space pair.  This means that a line such as
+      "Comment: " (with nothing else) is actually legal as an empty
+      string comment.  However, email and cut-and-paste being what it
+      is, that trailing space may go away.  Therefore, we accept empty
+      headers delimited with only a colon.  --rfc2440, as always,
+      makes this strict and enforces the colon-space pair. -dms
+    */
 
     p = strchr( line, ':');
-    if( !p || !p[1] ) {
+    if( !p || (RFC2440 && p[1]!=' ')
+       || (!RFC2440 && p[1]!=' ' && p[1]!='\n' && p[1]!='\r'))
+      {
        log_error(_("invalid armor header: "));
        print_string( stderr, line, len, 0 );
        putc('\n', stderr);
        return -1;
-    }
+      }
+
+    /* Chop off the whitespace we detected before */
+    len=len2;
+    line[len2]='\0';
 
     if( opt.verbose ) {
        log_info(_("armor header: "));
@@ -373,7 +387,7 @@ parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len )
 
 /* figure out whether the data is armored or not */
 static int
-check_input( armor_filter_context_t *afx, iobuf_t a )
+check_input( armor_filter_context_t *afx, IOBUF a )
 {
     int rc = 0;
     int i;
@@ -415,7 +429,7 @@ check_input( armor_filter_context_t *afx, iobuf_t a )
            if( hdr_line == BEGIN_SIGNED_MSG_IDX ) {
                if( afx->in_cleartext ) {
                    log_error(_("nested clear text signatures\n"));
-                   rc = GPG_ERR_INV_ARMOR;
+                   rc = gpg_error (GPG_ERR_INV_ARMOR);
                }
                afx->in_cleartext = 1;
            }
@@ -431,9 +445,9 @@ check_input( armor_filter_context_t *afx, iobuf_t a )
        } while( !maxlen );
     }
 
-    /* parse the header lines */
+    /* Parse the header lines.  */
     while(len) {
-       /* read the next line (skip all truncated lines) */
+       /* Read the next line (skip all truncated lines). */
        do {
            maxlen = MAX_LINELEN;
            afx->buffer_len = iobuf_read_line( a, &afx->buffer,
@@ -444,8 +458,8 @@ check_input( armor_filter_context_t *afx, iobuf_t a )
 
        i = parse_header_line( afx, line, len );
        if( i <= 0 ) {
-           if( i )
-               rc = GPG_ERR_INV_ARMOR;
+           if (i && RFC2440)
+               rc = G10ERR_INVALID_ARMOR;
            break;
        }
     }
@@ -465,7 +479,8 @@ check_input( armor_filter_context_t *afx, iobuf_t a )
     return rc;
 }
 
-
+#define PARTIAL_CHUNK 512
+#define PARTIAL_POW   9
 
 /****************
  * Fake a literal data packet and wait for the next armor line
@@ -473,7 +488,7 @@ check_input( armor_filter_context_t *afx, iobuf_t a )
  *       not implemented/checked.
  */
 static int
-fake_packet( armor_filter_context_t *afx, iobuf_t a,
+fake_packet( armor_filter_context_t *afx, IOBUF a,
             size_t *retn, byte *buf, size_t size  )
 {
     int rc = 0;
@@ -481,19 +496,31 @@ fake_packet( armor_filter_context_t *afx, iobuf_t a,
     int lastline = 0;
     unsigned maxlen, n;
     byte *p;
+    byte tempbuf[PARTIAL_CHUNK];
+    size_t tempbuf_len=0;
 
-    len = 2;   /* reserve 2 bytes for the length header */
-    size -= 2; /* and 2 for the terminating header */
-    while( !rc && len < size ) {
+    while( !rc && size-len>=(PARTIAL_CHUNK+1)) {
        /* copy what we have in the line buffer */
        if( afx->faked == 1 )
            afx->faked++; /* skip the first (empty) line */
-       else {
-           while( len < size && afx->buffer_pos < afx->buffer_len )
-               buf[len++] = afx->buffer[afx->buffer_pos++];
-           if( len >= size )
+       else
+         {
+           /* It's full, so write this partial chunk */
+           if(tempbuf_len==PARTIAL_CHUNK)
+             {
+               buf[len++]=0xE0+PARTIAL_POW;
+               memcpy(&buf[len],tempbuf,PARTIAL_CHUNK);
+               len+=PARTIAL_CHUNK;
+               tempbuf_len=0;
                continue;
-       }
+             }
+
+           while( tempbuf_len < PARTIAL_CHUNK
+                  && afx->buffer_pos < afx->buffer_len )
+             tempbuf[tempbuf_len++] = afx->buffer[afx->buffer_pos++];
+           if( tempbuf_len==PARTIAL_CHUNK )
+             continue;
+         }
 
        /* read the next line */
        maxlen = MAX_LINELEN;
@@ -506,15 +533,64 @@ fake_packet( armor_filter_context_t *afx, iobuf_t a,
        }
        if( !maxlen )
            afx->truncated++;
-       if( !afx->not_dash_escaped ) {
-           int crlf;
-           p = afx->buffer;
-           n = afx->buffer_len;
-           crlf = n > 1 && p[n-2] == '\r' && p[n-1]=='\n';
+
+       p = afx->buffer;
+       n = afx->buffer_len;
+
+       /* Armor header or dash-escaped line? */
+       if(p[0]=='-')
+         {
+           /* 2440bis-10: When reversing dash-escaping, an
+              implementation MUST strip the string "- " if it occurs
+              at the beginning of a line, and SHOULD warn on "-" and
+              any character other than a space at the beginning of a
+              line.  */
+
+           if(p[1]==' ' && !afx->not_dash_escaped)
+             {
+               /* It's a dash-escaped line, so skip over the
+                  escape. */
+               afx->buffer_pos = 2;
+             }
+           else if(p[1]=='-' && p[2]=='-' && p[3]=='-' && p[4]=='-')
+             {
+               /* Five dashes in a row mean it's probably armor
+                  header. */
+               int type = is_armor_header( p, n );
+               if( afx->not_dash_escaped && type != BEGIN_SIGNATURE )
+                 ; /* this is okay */
+               else
+                 {
+                   if( type != BEGIN_SIGNATURE )
+                     {
+                       log_info(_("unexpected armor: "));
+                       print_string( stderr, p, n, 0 );
+                       putc('\n', stderr);
+                     }
+
+                   lastline = 1;
+                   rc = -1;
+                 }
+             }
+           else if(!afx->not_dash_escaped)
+             {
+               /* Bad dash-escaping. */
+               log_info(_("invalid dash escaped line: "));
+               print_string( stderr, p, n, 0 );
+               putc('\n', stderr);
+             }
+         }
+
+       /* Now handle the end-of-line canonicalization */
+       if( !afx->not_dash_escaped )
+         {
+           int crlf = n > 1 && p[n-2] == '\r' && p[n-1]=='\n';
 
            /* PGP2 does not treat a tab as white space character */
-           afx->buffer_len = trim_trailing_chars( p, n,
-                                        afx->pgp2mode ? " \r\n" : " \t\r\n");
+           afx->buffer_len=
+             trim_trailing_chars( &p[afx->buffer_pos], n-afx->buffer_pos,
+                                  afx->pgp2mode ? " \r\n" : " \t\r\n");
+           afx->buffer_len+=afx->buffer_pos;
            /* the buffer is always allocated with enough space to append
             * the removed [CR], LF and a Nul
             * The reason for this complicated procedure is to keep at least
@@ -526,48 +602,23 @@ fake_packet( armor_filter_context_t *afx, iobuf_t a,
             * faked packet could do the job).
             */
            if( crlf )
-               afx->buffer[afx->buffer_len++] = '\r';
+             afx->buffer[afx->buffer_len++] = '\r';
            afx->buffer[afx->buffer_len++] = '\n';
-           afx->buffer[afx->buffer_len] = 0;
-       }
-       p = afx->buffer;
-       n = afx->buffer_len;
-
-       if( n > 2 && *p == '-' ) {
-           /* check for dash escaped or armor header */
-           if( p[1] == ' ' && !afx->not_dash_escaped ) {
-               /* issue a warning if it is not regular encoded */
-               if( p[2] != '-' && !( n > 6 && !memcmp(p+2, "From ", 5))) {
-                   log_info(_("invalid dash escaped line: "));
-                   print_string( stderr, p, n, 0 );
-                   putc('\n', stderr);
-               }
-               afx->buffer_pos = 2; /* skip */
-           }
-           else if( n >= 15 &&  p[1] == '-' && p[2] == '-' && p[3] == '-' ) {
-               int type = is_armor_header( p, n );
-               if( afx->not_dash_escaped && type != BEGIN_SIGNATURE )
-                   ; /* this is okay */
-               else {
-                   if( type != BEGIN_SIGNATURE ) {
-                       log_info(_("unexpected armor:"));
-                       print_string( stderr, p, n, 0 );
-                       putc('\n', stderr);
-                   }
-                   lastline = 1;
-                   rc = -1;
-               }
-           }
-       }
+           afx->buffer[afx->buffer_len] = '\0';
+         }
     }
 
-    buf[0] = (len-2) >> 8;
-    buf[1] = (len-2);
     if( lastline ) { /* write last (ending) length header */
-       if( buf[0] || buf[1] ) { /* only if we have some text */
-           buf[len++] = 0;
-           buf[len++] = 0;
-       }
+        if(tempbuf_len<192)
+         buf[len++]=tempbuf_len;
+       else
+         {
+           buf[len++]=((tempbuf_len-192)/256) + 192;
+           buf[len++]=(tempbuf_len-192) % 256;
+         }
+       memcpy(&buf[len],tempbuf,tempbuf_len);
+       len+=tempbuf_len;
+
        rc = 0;
        afx->faked = 0;
        afx->in_cleartext = 0;
@@ -609,15 +660,15 @@ fake_packet( armor_filter_context_t *afx, iobuf_t a,
 static int
 invalid_crc(void)
 {
-    if ( opt.ignore_crc_error )
-        return 0;
-    log_inc_errorcount();
-    return GPG_ERR_INV_ARMOR;
+  if ( opt.ignore_crc_error )
+    return 0;
+  log_inc_errorcount();
+  return gpg_error (GPG_ERR_INV_ARMOR);
 }
 
 
 static int
-radix64_read( armor_filter_context_t *afx, iobuf_t a, size_t *retn,
+radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
              byte *buf, size_t size )
 {
     byte val;
@@ -676,7 +727,7 @@ radix64_read( armor_filter_context_t *afx, iobuf_t a, size_t *retn,
            break;
        }
        else if( (c = asctobin[(c2=c)]) == 255 ) {
-           log_error(_("invalid radix64 character %02x skipped\n"), c2);
+           log_error(_("invalid radix64 character %02X skipped\n"), c2);
            continue;
        }
        switch(idx) {
@@ -755,13 +806,17 @@ radix64_read( armor_filter_context_t *afx, iobuf_t a, size_t *retn,
            if( c == -1 ) {
                log_info(_("premature eof (in CRC)\n"));
                rc = invalid_crc();
-                           }
+           }
+           else if( idx == 0 ) {
+               /* No CRC at all is legal ("MAY") */
+               rc=0;
+           }
            else if( idx != 4 ) {
                log_info(_("malformed CRC\n"));
                rc = invalid_crc();
            }
            else if( mycrc != afx->crc ) {
-                log_info (_("CRC error; %06lx - %06lx\n"),
+                log_info (_("CRC error; %06lX - %06lX\n"),
                                    (ulong)afx->crc, (ulong)mycrc);
                 rc = invalid_crc();
            }
@@ -781,12 +836,12 @@ radix64_read( armor_filter_context_t *afx, iobuf_t a, size_t *retn,
                if( rc == -1 )
                    rc = 0;
                else if( rc == 2 ) {
-                   log_error(_("premature eof (in Trailer)\n"));
-                   rc = GPG_ERR_INV_ARMOR;
+                   log_error(_("premature eof (in trailer)\n"));
+                   rc = G10ERR_INVALID_ARMOR;
                }
                else {
                    log_error(_("error in trailer line\n"));
-                   rc = GPG_ERR_INV_ARMOR;
+                   rc = G10ERR_INVALID_ARMOR;
                }
 #endif
            }
@@ -805,7 +860,7 @@ radix64_read( armor_filter_context_t *afx, iobuf_t a, size_t *retn,
  */
 int
 armor_filter( void *opaque, int control,
-            iobuf_t a, byte *buf, size_t *ret_len)
+            IOBUF a, byte *buf, size_t *ret_len)
 {
     size_t size = *ret_len;
     armor_filter_context_t *afx = opaque;
@@ -843,9 +898,10 @@ armor_filter( void *opaque, int control,
        *ret_len = n;
     }
     else if( control == IOBUFCTRL_UNDERFLOW ) {
-        /* We need some space for the faked packet.  The minmum required
-         * size is ~18 + length of the session marker */
-       if( size < 50 ) 
+        /* We need some space for the faked packet.  The minmum
+         * required size is the PARTIAL_CHUNK size plus a byte for the
+         * length itself */
+       if( size < PARTIAL_CHUNK+1 ) 
            BUG(); /* supplied buffer too short */
 
        if( afx->faked )
@@ -882,7 +938,7 @@ armor_filter( void *opaque, int control,
                        afx->pgp2mode = 1;
                }
                n=0;
-                /* first a gpg control packet */
+                /* First a gpg control packet... */
                 buf[n++] = 0xff; /* new format, type 63, 1 length byte */
                 n++;   /* see below */
                 memcpy(buf+n, sesmark, sesmarklen ); n+= sesmarklen;
@@ -902,12 +958,16 @@ armor_filter( void *opaque, int control,
                     buf[n++] = DIGEST_ALGO_SHA512;
                 buf[1] = n - 2;
 
-               /* followed by a plaintext packet */
-               buf[n++] = 0xaf; /* old packet format, type 11, var length */
-               buf[n++] = 0;    /* set the length header */
-               buf[n++] = 6;
+               /* ...followed by an invented plaintext packet.
+                  Amusingly enough, this packet is not compliant with
+                  2440 as the initial partial length is less than 512
+                  bytes.  Of course, we'll accept it anyway ;) */
+
+               buf[n++] = 0xCB; /* new packet format, type 11 */
+               buf[n++] = 0xE1; /* 2^1 == 2 bytes */
                buf[n++] = 't';  /* canonical text mode */
                buf[n++] = 0;    /* namelength */
+               buf[n++] = 0xE2; /* 2^2 == 4 more bytes */
                memset(buf+n, 0, 4); /* timestamp */
                n += 4;
            }
@@ -926,35 +986,39 @@ armor_filter( void *opaque, int control,
     else if( control == IOBUFCTRL_FLUSH && !afx->cancel ) {
        if( !afx->status ) { /* write the header line */
            const char *s;
-            STRLIST comment = opt.comments;
+           STRLIST comment=opt.comments;
 
            if( afx->what >= DIM(head_strings) )
                log_bug("afx->what=%d", afx->what);
            iobuf_writestr(a, "-----");
            iobuf_writestr(a, head_strings[afx->what] );
-           iobuf_writestr(a, "-----" LF );
+           iobuf_writestr(a, "-----" );
+           iobuf_writestr(a,afx->eol);
            if( !opt.no_version )
+             {
                iobuf_writestr(a, "Version: GnuPG v"  VERSION " ("
-                                             PRINTABLE_OS_NAME ")" LF );
+                              PRINTABLE_OS_NAME ")" );
+               iobuf_writestr(a,afx->eol);
+             }
 
-           /* Write the comment string. */
-           for(s=comment? comment->d:NULL; comment;
-                comment=comment->next,s=comment->d)
+           /* write the comment strings */
+           for(s=comment->d;comment;comment=comment->next,s=comment->d)
              {
                iobuf_writestr(a, "Comment: " );
-               for ( ; *s; s++ )
-                  {
+               for( ; *s; s++ )
+                 {
                    if( *s == '\n' )
-                      iobuf_writestr(a, "\\n" );
+                     iobuf_writestr(a, "\\n" );
                    else if( *s == '\r' )
-                      iobuf_writestr(a, "\\r" );
+                     iobuf_writestr(a, "\\r" );
                    else if( *s == '\v' )
-                      iobuf_writestr(a, "\\v" );
+                     iobuf_writestr(a, "\\v" );
                    else
-                      iobuf_put(a, *s );
-                  }
-               iobuf_writestr(a, LF );
-              }
+                     iobuf_put(a, *s );
+                 }
+
+               iobuf_writestr(a,afx->eol);
+             }
 
            if ( afx->hdrlines ) {
                 for ( s = afx->hdrlines; *s; s++ ) {
@@ -965,7 +1029,8 @@ armor_filter( void *opaque, int control,
                     iobuf_put(a, *s );
                 }
             }
-           iobuf_writestr(a, LF );
+
+           iobuf_writestr(a,afx->eol);
            afx->status++;
            afx->idx = 0;
            afx->idx2 = 0;
@@ -994,10 +1059,11 @@ armor_filter( void *opaque, int control,
                iobuf_put(a, c);
                c = bintoasc[radbuf[2]&077];
                iobuf_put(a, c);
-               if( ++idx2 >= (64/4) ) { /* pgp doesn't like 72 here */
-                   iobuf_writestr(a, LF );
+               if( ++idx2 >= (64/4) )
+                 { /* pgp doesn't like 72 here */
+                   iobuf_writestr(a,afx->eol);
                    idx2=0;
-               }
+                 }
            }
        }
        for(i=0; i < idx; i++ )
@@ -1006,10 +1072,23 @@ armor_filter( void *opaque, int control,
        afx->idx2 = idx2;
        afx->crc  = crc;
     }
-    else if( control == IOBUFCTRL_INIT ) {
+    else if( control == IOBUFCTRL_INIT )
+      {
        if( !is_initialized )
-           initialize();
-    }
+         initialize();
+
+       /* Figure out what we're using for line endings if the caller
+          didn't specify. */
+       if(afx->eol[0]==0)
+         {
+#ifdef HAVE_DOSISH_SYSTEM
+           afx->eol[0]='\r';
+           afx->eol[1]='\n';
+#else
+           afx->eol[0]='\n';
+#endif
+         }
+      }
     else if( control == IOBUFCTRL_CANCEL ) {
        afx->cancel = 1;
     }
@@ -1038,14 +1117,15 @@ armor_filter( void *opaque, int control,
                    iobuf_put(a, c);
                    iobuf_put(a, '=');
                }
-               if( ++idx2 >= (64/4) ) { /* pgp doesn't like 72 here */
-                   iobuf_writestr(a, LF );
+               if( ++idx2 >= (64/4) )
+                 { /* pgp doesn't like 72 here */
+                   iobuf_writestr(a,afx->eol);
                    idx2=0;
-               }
+                 }
            }
            /* may need a linefeed */
            if( idx2 )
-               iobuf_writestr(a, LF );
+             iobuf_writestr(a,afx->eol);
            /* write the CRC */
            iobuf_put(a, '=');
            radbuf[0] = crc >>16;
@@ -1059,13 +1139,14 @@ armor_filter( void *opaque, int control,
            iobuf_put(a, c);
            c = bintoasc[radbuf[2]&077];
            iobuf_put(a, c);
-           iobuf_writestr(a, LF );
+           iobuf_writestr(a,afx->eol);
            /* and the the trailer */
            if( afx->what >= DIM(tail_strings) )
                log_bug("afx->what=%d", afx->what);
            iobuf_writestr(a, "-----");
            iobuf_writestr(a, tail_strings[afx->what] );
-           iobuf_writestr(a, "-----" LF );
+           iobuf_writestr(a, "-----" );
+           iobuf_writestr(a,afx->eol);
        }
        else if( !afx->any_data && !afx->inp_bypass ) {
            log_error(_("no valid OpenPGP data found.\n"));
@@ -1079,7 +1160,7 @@ armor_filter( void *opaque, int control,
        if( afx->qp_detected )
            log_error(_("quoted printable character in armor - "
                        "probably a buggy MTA has been used\n") );
-       xfree ( afx->buffer );
+       xfree( afx->buffer );
        afx->buffer = NULL;
     }
     else if( control == IOBUFCTRL_DESC )
@@ -1096,7 +1177,7 @@ make_radix64_string( const byte *data, size_t len )
 {
     char *buffer, *p;
 
-    buffer = p = xmalloc ( (len+2)/3*4 + 1 );
+    buffer = p = xmalloc( (len+2)/3*4 + 1 );
     for( ; len >= 3 ; len -= 3, data += 3 ) {
        *p++ = bintoasc[(data[0] >> 2) & 077];
        *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
@@ -1156,7 +1237,7 @@ unarmor_pump_new (void)
 
     if( !is_initialized )
         initialize();
-    x = xcalloc (1,sizeof *x);
+    x = xmalloc_clear (sizeof *x);
     return x;
 }
 
@@ -1253,7 +1334,7 @@ unarmor_pump (UnarmorPump x, int c)
         {
             int c2;
             if( (c = asctobin[(c2=c)]) == 255 ) {
-                log_error(_("invalid radix64 character %02x skipped\n"), c2);
+                log_error(_("invalid radix64 character %02X skipped\n"), c2);
                 break;
             }
         }
@@ -1290,7 +1371,7 @@ unarmor_pump (UnarmorPump x, int c)
         if( (c = asctobin[c]) == 255 ) {
             rval = -1; /* ready */
             if( x->crc != x->mycrc ) {
-                log_info (_("CRC error; %06lx - %06lx\n"),
+                log_info (_("CRC error; %06lX - %06lX\n"),
                           (ulong)x->crc, (ulong)x->mycrc);
                 if ( invalid_crc() )
                     rval = -3;
index d2c5384..cbf0374 100644 (file)
@@ -1,6 +1,6 @@
 /* build-packet.c - assemble packets and write them
- * Copyright (C) 1998, 1999, 2000, 2001, 2002,
- *               2003 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ *               2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -16,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <ctype.h>
 
 #include "gpg.h"
 #include "packet.h"
 #include "errors.h"
 #include "iobuf.h"
-#include "mpi.h"
 #include "util.h"
 #include "cipher.h"
-#include "memory.h"
+#include "i18n.h"
 #include "options.h"
 
-
-static int do_comment( iobuf_t out, int ctb, PKT_comment *rem );
-static int do_user_id( iobuf_t out, int ctb, PKT_user_id *uid );
-static int do_public_key( iobuf_t out, int ctb, PKT_public_key *pk );
-static int do_secret_key( iobuf_t out, int ctb, PKT_secret_key *pk );
-static int do_symkey_enc( iobuf_t out, int ctb, PKT_symkey_enc *enc );
-static int do_pubkey_enc( iobuf_t out, int ctb, PKT_pubkey_enc *enc );
+static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid );
+static int do_public_key( IOBUF out, int ctb, PKT_public_key *pk );
+static int do_secret_key( IOBUF out, int ctb, PKT_secret_key *pk );
+static int do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc );
+static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc );
 static u32 calc_plaintext( PKT_plaintext *pt );
-static int do_plaintext( iobuf_t out, int ctb, PKT_plaintext *pt );
-static int do_encrypted( iobuf_t out, int ctb, PKT_encrypted *ed );
-static int do_encrypted_mdc( iobuf_t out, int ctb, PKT_encrypted *ed );
-static int do_compressed( iobuf_t out, int ctb, PKT_compressed *cd );
-static int do_signature( iobuf_t out, int ctb, PKT_signature *sig );
-static int do_onepass_sig( iobuf_t out, int ctb, PKT_onepass_sig *ops );
+static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt );
+static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed );
+static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed );
+static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd );
+static int do_signature( IOBUF out, int ctb, PKT_signature *sig );
+static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops );
 
 static int calc_header_length( u32 len, int new_ctb );
-static int write_16(iobuf_t inp, u16 a);
-static int write_32(iobuf_t inp, u32 a);
-static int write_header( iobuf_t out, int ctb, u32 len );
-static int write_sign_packet_header( iobuf_t out, int ctb, u32 len );
-static int write_header2( iobuf_t out, int ctb, u32 len, int hdrlen, int blkmode );
-static int write_new_header( iobuf_t out, int ctb, u32 len, int hdrlen );
-static int write_version( iobuf_t out, int ctb );
+static int write_16(IOBUF inp, u16 a);
+static int write_32(IOBUF inp, u32 a);
+static int write_header( IOBUF out, int ctb, u32 len );
+static int write_sign_packet_header( IOBUF out, int ctb, u32 len );
+static int write_header2( IOBUF out, int ctb, u32 len, int hdrlen );
+static int write_new_header( IOBUF out, int ctb, u32 len, int hdrlen );
+static int write_version( IOBUF out, int ctb );
 
 /****************
  * Build a packet and write it to INP
@@ -66,7 +65,7 @@ static int write_version( iobuf_t out, int ctb );
  * Note: Caller must free the packet
  */
 int
-build_packet( iobuf_t out, PACKET *pkt )
+build_packet( IOBUF out, PACKET *pkt )
 {
     int new_ctb=0, rc=0, ctb;
     int pkttype;
@@ -75,30 +74,38 @@ build_packet( iobuf_t out, PACKET *pkt )
        log_debug("build_packet() type=%d\n", pkt->pkttype );
     assert( pkt->pkt.generic );
 
-    switch( (pkttype = pkt->pkttype) ) {
-      case PKT_OLD_COMMENT: pkttype = pkt->pkttype = PKT_COMMENT; break;
+    switch( (pkttype = pkt->pkttype) )
+      {
       case PKT_PLAINTEXT: new_ctb = pkt->pkt.plaintext->new_ctb; break;
       case PKT_ENCRYPTED:
       case PKT_ENCRYPTED_MDC: new_ctb = pkt->pkt.encrypted->new_ctb; break;
       case PKT_COMPRESSED:new_ctb = pkt->pkt.compressed->new_ctb; break;
       case PKT_USER_ID:
-           if( pkt->pkt.user_id->attrib_data )
-               pkttype = PKT_ATTRIBUTE;
-           break;
+       if( pkt->pkt.user_id->attrib_data )
+         pkttype = PKT_ATTRIBUTE;
+       break;
       default: break;
-    }
+      }
 
     if( new_ctb || pkttype > 15 ) /* new format */
        ctb = 0xc0 | (pkttype & 0x3f);
     else
        ctb = 0x80 | ((pkttype & 15)<<2);
-    switch( pkttype ) {
+    switch( pkttype )
+      {
       case PKT_ATTRIBUTE:
       case PKT_USER_ID:
        rc = do_user_id( out, ctb, pkt->pkt.user_id );
        break;
+      case PKT_OLD_COMMENT:
       case PKT_COMMENT:
-       rc = do_comment( out, ctb, pkt->pkt.comment );
+       /*
+         Ignore these.  Theoretically, this will never be called as
+         we have no way to output comment packets any longer, but
+         just in case there is some code path that would end up
+         outputting a comment that was written before comments were
+         dropped (in the public key?) this is a no-op.
+       */
        break;
       case PKT_PUBLIC_SUBKEY:
       case PKT_PUBLIC_KEY:
@@ -138,11 +145,32 @@ build_packet( iobuf_t out, PACKET *pkt )
       default:
        log_bug("invalid packet type in build_packet()\n");
        break;
-    }
+      }
 
     return rc;
 }
 
+
+/*
+ * Write the mpi A to OUT.
+ */
+static int
+mpi_write (iobuf_t out, gcry_mpi_t a)
+{
+  char buffer[(MAX_EXTERN_MPI_BITS+7)/8];
+  size_t nbytes;
+  int rc;
+
+  nbytes = (MAX_EXTERN_MPI_BITS+7)/8;
+  rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a );
+  if( !rc )
+    rc = iobuf_write( out, buffer, nbytes );
+  
+  return rc;
+}
+
+
+
 /****************
  * calculate the length of a packet described by PKT
  */
@@ -180,56 +208,42 @@ calc_packet_length( PACKET *pkt )
 }
 
 static void
-write_fake_data( iobuf_t out, gcry_mpi_t a )
+write_fake_data (IOBUF out, gcry_mpi_t a)
 {
-    if( a ) {
-       unsigned int n;
-       void *p;
-
-        assert( gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE));
-        p = gcry_mpi_get_opaque (a, &n);
-        iobuf_write (out, p, (n+7)/8);
-    }
-}
-
-
-static int
-do_comment (iobuf_t out, int ctb, PKT_comment *rem)
-{
-  int rc = 0;
-
-  if (opt.sk_comments)
+  if (a) 
     {
-      write_header(out, ctb, rem->len);
-      rc = iobuf_write( out, rem->data, rem->len );
+      unsigned int n;
+      void *p;
+      
+      p = gcry_mpi_get_opaque ( a, &n );
+      iobuf_write (out, p, (n+7)/8 );
     }
-  return rc;
 }
 
 static int
-do_user_id( iobuf_t out, int ctb, PKT_user_id *uid )
+do_user_id( IOBUF out, int ctb, PKT_user_id *uid )
 {
-  int rc;
+    int rc;
 
-  if (uid->attrib_data)
-    {
-      write_header (out, ctb, uid->attrib_len);
-      rc = iobuf_write (out, uid->attrib_data, uid->attrib_len );
-    }
-  else
-    {
-      write_header (out, ctb, uid->len);
-      rc = iobuf_write (out, uid->name, uid->len );
-    }
-  return rc;
+    if( uid->attrib_data )
+      {
+       write_header(out, ctb, uid->attrib_len);
+       rc = iobuf_write( out, uid->attrib_data, uid->attrib_len );
+      }
+    else
+      {
+        write_header2( out, ctb, uid->len, 2 );
+       rc = iobuf_write( out, uid->name, uid->len );
+      }
+    return 0;
 }
 
 static int
-do_public_key( iobuf_t out, int ctb, PKT_public_key *pk )
+do_public_key( IOBUF out, int ctb, PKT_public_key *pk )
 {
     int rc = 0;
     int n, i;
-    iobuf_t a = iobuf_temp();
+    IOBUF a = iobuf_temp();
 
     if( !pk->version )
        iobuf_put( a, 3 );
@@ -251,99 +265,20 @@ do_public_key( iobuf_t out, int ctb, PKT_public_key *pk )
     for(i=0; i < n; i++ )
        mpi_write(a, pk->pkey[i] );
 
-    write_header2(out, ctb, iobuf_get_temp_length(a), pk->hdrbytes, 1 );
-    rc = iobuf_write_temp (out, a);
+    write_header2(out, ctb, iobuf_get_temp_length(a), pk->hdrbytes);
+    rc = iobuf_write_temp( out, a );
 
     iobuf_close(a);
     return rc;
 }
 
 
-/****************
- * Make a hash value from the public key certificate
- */
-void
-hash_public_key( MD_HANDLE md, PKT_public_key *pk )
-{
-    PACKET pkt;
-    int rc = 0;
-    int ctb;
-    ulong pktlen;
-    int c;
-    iobuf_t a = iobuf_temp();
-#if 0
-    FILE *fp = fopen("dump.pk", "a");
-    int i=0;
-
-    fprintf(fp, "\nHashing PK (v%d):\n", pk->version);
-#endif
-
-    /* build the packet */
-    init_packet(&pkt);
-    pkt.pkttype = PKT_PUBLIC_KEY;
-    pkt.pkt.public_key = pk;
-    if( (rc = build_packet( a, &pkt )) )
-       log_fatal("build public_key for hashing failed: %s\n", gpg_strerror (rc));
-
-    if( !(pk->version == 3 && pk->pubkey_algo == 16) ) {
-       /* skip the constructed header but don't do this for our very old
-        * v3 ElG keys */
-       ctb = iobuf_get_noeof(a);
-       pktlen = 0;
-       if( (ctb & 0x40) ) {
-           c = iobuf_get_noeof(a);
-           if( c < 192 )
-               pktlen = c;
-           else if( c < 224 ) {
-               pktlen = (c - 192) * 256;
-               c = iobuf_get_noeof(a);
-               pktlen += c + 192;
-           }
-           else if( c == 255 ) {
-               pktlen  = iobuf_get_noeof(a) << 24;
-               pktlen |= iobuf_get_noeof(a) << 16;
-               pktlen |= iobuf_get_noeof(a) << 8;
-               pktlen |= iobuf_get_noeof(a);
-           }
-       }
-       else {
-           int lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
-           for( ; lenbytes; lenbytes-- ) {
-               pktlen <<= 8;
-               pktlen |= iobuf_get_noeof(a);
-           }
-       }
-       /* hash a header */
-       gcry_md_putc ( md, 0x99 );
-       pktlen &= 0xffff; /* can't handle longer packets */
-       gcry_md_putc ( md, pktlen >> 8 );
-       gcry_md_putc ( md, pktlen & 0xff );
-    }
-    /* hash the packet body */
-    while( (c=iobuf_get(a)) != -1 ) {
-#if 0
-       fprintf( fp," %02x", c );
-       if( (++i == 24) ) {
-           putc('\n', fp);
-           i=0;
-       }
-#endif
-       gcry_md_putc ( md, c );
-    }
-#if 0
-    putc('\n', fp);
-    fclose(fp);
-#endif
-    iobuf_cancel(a);
-}
-
-
 static int
-do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk )
+do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk )
 {
     int rc = 0;
     int i, nskey, npkey;
-    iobuf_t a = iobuf_temp(); /* build in a self-enlarging buffer */
+    IOBUF a = iobuf_temp(); /* build in a self-enlarging buffer */
 
     /* Write the version number - if none is specified, use 3 */
     if( !sk->version )
@@ -371,7 +306,7 @@ do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk )
 
     /* If we don't have any public parameters - which is the case if
        we don't know the algorithm used - the parameters are stored as
-       one blob in a faked (opaque) gcry_mpi_t */
+       one blob in a faked (opaque) MPI */
     if( !npkey ) {
        write_fake_data( a, sk->skey[0] );
        goto leave;
@@ -415,9 +350,9 @@ do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk )
            if( sk->protect.s2k.mode == 3 )
                iobuf_put(a, sk->protect.s2k.count ); 
 
-            /* For our special modes 1001 and 1002 we do not need an IV */
-           if( sk->protect.s2k.mode != 1001
-                && sk->protect.s2k.mode != 1002 )
+            /* For out special modes 1001, 1002 we do not need an IV */
+           if( sk->protect.s2k.mode != 1001 
+              && sk->protect.s2k.mode != 1002 )
                iobuf_write(a, sk->protect.iv, sk->protect.ivlen );
        }
     }
@@ -437,19 +372,21 @@ do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk )
     else if( sk->is_protected && sk->version >= 4 ) {
         /* The secret key is protected - write it out as it is */
        byte *p;
-       assert( gcry_mpi_get_flag( sk->skey[npkey], GCRYMPI_FLAG_OPAQUE ) );
-       p = gcry_mpi_get_opaque( sk->skey[npkey], &i );
-       iobuf_write(a, p, (i+7)/8 );
+       unsigned int ndatabits;
+
+       assert (gcry_mpi_get_flag (sk->skey[npkey], GCRYMPI_FLAG_OPAQUE));
+       p = gcry_mpi_get_opaque (sk->skey[npkey], &ndatabits );
+       iobuf_write (a, p, (ndatabits+7)/8 );
     }
     else if( sk->is_protected ) {
-        /* The secret key is protected the old v4 way. */
+        /* The secret key is protected te old v4 way. */
        for(   ; i < nskey; i++ ) {
             byte *p;
-            size_t n;
+            unsigned int ndatabits;
 
-            assertgcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE));
-            p = gcry_mpi_get_opaque( sk->skey[i], &n );
-            iobuf_write (a, p, (n+7)/8);
+            assert (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE));
+            p = gcry_mpi_get_opaque (sk->skey[i], &ndatabits);
+            iobuf_write (a, p, (ndatabits+7)/8);
         }
        write_16(a, sk->csum );
     }
@@ -463,19 +400,19 @@ do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk )
   leave:
     /* Build the header of the packet - which we must do after writing all
        the other stuff, so that we know the length of the packet */
-    write_header2(out, ctb, iobuf_get_temp_length(a), sk->hdrbytes, 1 );
+    write_header2(out, ctb, iobuf_get_temp_length(a), sk->hdrbytes);
     /* And finally write it out the real stream */
-    rc = iobuf_write_temp (out, a );
+    rc = iobuf_write_tempout, a );
 
     iobuf_close(a); /* close the remporary buffer */
     return rc;
 }
 
 static int
-do_symkey_enc( iobuf_t out, int ctb, PKT_symkey_enc *enc )
+do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc )
 {
     int rc = 0;
-    iobuf_t a = iobuf_temp();
+    IOBUF a = iobuf_temp();
 
     assert( enc->version == 4 );
     switch( enc->s2k.mode ) {
@@ -495,21 +432,19 @@ do_symkey_enc( iobuf_t out, int ctb, PKT_symkey_enc *enc )
        iobuf_write(a, enc->seskey, enc->seskeylen );
 
     write_header(out, ctb, iobuf_get_temp_length(a) );
-    rc = iobuf_write_temp (out, a);
+    rc = iobuf_write_temp( out, a );
 
     iobuf_close(a);
     return rc;
 }
 
 
-
-
 static int
-do_pubkey_enc( iobuf_t out, int ctb, PKT_pubkey_enc *enc )
+do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc )
 {
     int rc = 0;
     int n, i;
-    iobuf_t a = iobuf_temp();
+    IOBUF a = iobuf_temp();
 
     write_version( a, ctb );
     if( enc->throw_keyid ) {
@@ -528,55 +463,56 @@ do_pubkey_enc( iobuf_t out, int ctb, PKT_pubkey_enc *enc )
        mpi_write(a, enc->data[i] );
 
     write_header(out, ctb, iobuf_get_temp_length(a) );
-    rc = iobuf_write_temp (out, a);
+    rc = iobuf_write_temp( out, a );
 
     iobuf_close(a);
     return rc;
 }
 
 
-
-
 static u32
 calc_plaintext( PKT_plaintext *pt )
 {
-    return pt->len? (1 + 1 + pt->namelen + 4 + pt->len) : 0;
+  /* Truncate namelen to the maximum 255 characters.  Note this means
+     that a function that calls build_packet with an illegal literal
+     packet will get it back legalized. */
+
+  if(pt->namelen>255)
+    pt->namelen=255;
+
+  return pt->len? (1 + 1 + pt->namelen + 4 + pt->len) : 0;
 }
 
 static int
-do_plaintext( iobuf_t out, int ctb, PKT_plaintext *pt )
+do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt )
 {
     int i, rc = 0;
     u32 n;
     byte buf[1000]; /* this buffer has the plaintext! */
     int nbytes;
 
-    /* Truncate namelen to the maximum 255 characters.  This does mean
-       that a function that calls build_packet with an illegal literal
-       packet will get it back legalized. */
-    if(pt->namelen>255)
-      pt->namelen=255;
-
     write_header(out, ctb, calc_plaintext( pt ) );
     iobuf_put(out, pt->mode );
     iobuf_put(out, pt->namelen );
     for(i=0; i < pt->namelen; i++ )
        iobuf_put(out, pt->name[i] );
-    rc = write_32 (out, pt->timestamp);
+    rc = write_32(out, pt->timestamp );
+    if (rc) 
+      return rc;
 
     n = 0;
     while( (nbytes=iobuf_read(pt->buf, buf, 1000)) != -1 ) {
-        rc = iobuf_write(out, buf, nbytes);
-        if (rc)
-          break;
-       n += nbytes;
+      rc = iobuf_write (out, buf, nbytes);
+      if (rc)
+        break;
+      n += nbytes;
     }
     wipememory(buf,1000); /* burn the buffer */
-    if( !pt->len )
-       iobuf_set_block_mode(out, 0 ); /* write end marker */
-    else if( n != pt->len )
-       log_error("do_plaintext(): wrote %lu bytes but expected %lu bytes\n",
-                       (ulong)n, (ulong)pt->len );
+    if( (ctb&0x40) && !pt->len )
+      iobuf_set_partial_block_mode(out, 0 ); /* turn off partial */
+    if( pt->len && n != pt->len )
+      log_error("do_plaintext(): wrote %lu bytes but expected %lu bytes\n",
+               (ulong)n, (ulong)pt->len );
 
     return rc;
 }
@@ -584,7 +520,7 @@ do_plaintext( iobuf_t out, int ctb, PKT_plaintext *pt )
 
 
 static int
-do_encrypted( iobuf_t out, int ctb, PKT_encrypted *ed )
+do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed )
 {
     int rc = 0;
     u32 n;
@@ -598,7 +534,7 @@ do_encrypted( iobuf_t out, int ctb, PKT_encrypted *ed )
 }
 
 static int
-do_encrypted_mdc( iobuf_t out, int ctb, PKT_encrypted *ed )
+do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed )
 {
     int rc = 0;
     u32 n;
@@ -617,7 +553,7 @@ do_encrypted_mdc( iobuf_t out, int ctb, PKT_encrypted *ed )
 
 
 static int
-do_compressed( iobuf_t out, int ctb, PKT_compressed *cd )
+do_compressed( IOBUF out, int ctb, PKT_compressed *cd )
 {
     int rc = 0;
 
@@ -626,7 +562,7 @@ do_compressed( iobuf_t out, int ctb, PKT_compressed *cd )
        set, CTB is already formatted as new style and write_header2
        does create a partial length encoding using new the new
        style. */
-    write_header2(out, ctb, 0, 0, 0 );
+    write_header2(out, ctb, 0, 0);
     iobuf_put(out, cd->algorithm );
 
     /* This is all. The caller has to write the real data */
@@ -734,6 +670,7 @@ build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type,
       case SIGSUBPKT_NOTATION:
       case SIGSUBPKT_POLICY:
       case SIGSUBPKT_REV_KEY:
+      case SIGSUBPKT_SIGNATURE:
        /* we do allow multiple subpackets */
        break;
 
@@ -803,18 +740,20 @@ build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type,
     else
        nlen = 1; /* just a 1 byte length header */
 
-    switch( type ) {
+    switch( type )
+      {
        /* The issuer being unhashed is a historical oddity.  It
           should work equally as well hashed.  Of course, if even an
           unhashed issuer is tampered with, it makes it awfully hard
           to verify the sig... */
       case SIGSUBPKT_ISSUER:
+      case SIGSUBPKT_SIGNATURE:
         hashed = 0;
         break;
       default: 
         hashed = 1;
         break;
-    }
+      }
 
     if( critical )
        type |= SIGSUBPKT_FLAG_CRITICAL;
@@ -966,12 +905,179 @@ build_attribute_subpkt(PKT_user_id *uid,byte type,
   uid->attrib_len+=idx+headerlen+buflen;
 }
 
+struct notation *
+string_to_notation(const char *string,int is_utf8)
+{
+  const char *s;
+  int saw_at=0;
+  struct notation *notation;
+
+  notation=xmalloc_clear(sizeof(*notation));
+
+  if(*string=='-')
+    {
+      notation->flags.ignore=1;
+      string++;
+    }
+
+  if(*string=='!')
+    {
+      notation->flags.critical=1;
+      string++;
+    }
+
+  /* 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++;
+
+      /* -notationname is legal without an = sign */
+      if(!*s && notation->flags.ignore)
+       break;
+
+      if( !*s || !isascii (*s) || (!isgraph(*s) && !isspace(*s)) )
+       {
+         log_error(_("a notation name must have only printable characters"
+                     " or spaces, and end with an '='\n") );
+         goto fail;
+       }
+    }
+
+  notation->name=xmalloc((s-string)+1);
+  strncpy(notation->name,string,s-string);
+  notation->name[s-string]='\0';
+
+  if(!saw_at && !opt.expert)
+    {
+      log_error(_("a user notation name must contain the '@' character\n"));
+      goto fail;
+    }
+
+  if (saw_at > 1)
+    {
+      log_error(_("a notation name must not contain more than"
+                 " one '@' character\n"));
+      goto fail;
+    }
+
+  if(*s)
+    {
+      const char *i=s+1;
+      int highbit=0;
+
+      /* we only support printable text - therefore we enforce the use
+        of only printable characters (an empty value is valid) */
+      for(s++; *s ; s++ )
+       {
+         if ( !isascii (*s) )
+           highbit=1;
+         else if (iscntrl(*s))
+           {
+             log_error(_("a notation value must not use any"
+                         " control characters\n"));
+             goto fail;
+           }
+       }
+
+      if(!highbit || is_utf8)
+       notation->value=xstrdup(i);
+      else
+       notation->value=native_to_utf8(i);
+    }
+
+  return notation;
+
+ fail:
+  free_notation(notation);
+  return NULL;
+}
+
+struct notation *
+sig_to_notation(PKT_signature *sig)
+{
+  const byte *p;
+  size_t len;
+  int seq=0,crit;
+  struct notation *list=NULL;
+
+  while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,&len,&seq,&crit)))
+    {
+      int n1,n2;
+      struct notation *n=NULL;
+
+      if(len<8)
+       {
+         log_info(_("WARNING: invalid notation data found\n"));
+         continue;
+       }
+
+      n1=(p[4]<<8)|p[5];
+      n2=(p[6]<<8)|p[7];
+
+      if(8+n1+n2!=len)
+       {
+         log_info(_("WARNING: invalid notation data found\n"));
+         continue;
+       }
+
+      n=xmalloc_clear(sizeof(*n));
+      n->name=xmalloc(n1+1);
+
+      memcpy(n->name,&p[8],n1);
+      n->name[n1]='\0';
+
+      if(p[0]&0x80)
+       {
+         n->value=xmalloc(n2+1);
+         memcpy(n->value,&p[8+n1],n2);
+         n->value[n2]='\0';
+       }
+      else
+       {
+         n->bdat=xmalloc(n2);
+         n->blen=n2;
+         memcpy(n->bdat,&p[8+n1],n2);
+
+         n->value=xmalloc(2+strlen(_("not human readable"))+2+1);
+         strcpy(n->value,"[ ");
+         strcat(n->value,_("not human readable"));
+         strcat(n->value," ]");
+       }
+
+      n->flags.critical=crit;
+
+      n->next=list;
+      list=n;
+    }
+
+  return list;
+}
+
+void
+free_notation(struct notation *notation)
+{
+  while(notation)
+    {
+      struct notation *n=notation;
+
+      xfree(n->name);
+      xfree(n->value);
+      xfree(n->altvalue);
+      xfree(n->bdat);
+      notation=n->next;
+      xfree(n);
+    }
+}
+
 static int
-do_signature( iobuf_t out, int ctb, PKT_signature *sig )
+do_signature( IOBUF out, int ctb, PKT_signature *sig )
 {
     int rc = 0;
     int n, i;
-    iobuf_t a = iobuf_temp();
+    IOBUF a = iobuf_temp();
 
     if( !sig->version )
        iobuf_put( a, 3 );
@@ -1013,7 +1119,7 @@ do_signature( iobuf_t out, int ctb, PKT_signature *sig )
        write_sign_packet_header(out, ctb, iobuf_get_temp_length(a) );
     else
        write_header(out, ctb, iobuf_get_temp_length(a) );
-    rc = iobuf_write_temp (out, a);
+    rc = iobuf_write_temp( out, a );
 
     iobuf_close(a);
     return rc;
@@ -1021,10 +1127,10 @@ do_signature( iobuf_t out, int ctb, PKT_signature *sig )
 
 
 static int
-do_onepass_sig( iobuf_t out, int ctb, PKT_onepass_sig *ops )
+do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops )
 {
     int rc = 0;
-    iobuf_t a = iobuf_temp();
+    IOBUF a = iobuf_temp();
 
     write_version( a, ctb );
     iobuf_put(a, ops->sig_class );
@@ -1035,7 +1141,7 @@ do_onepass_sig( iobuf_t out, int ctb, PKT_onepass_sig *ops )
     iobuf_put(a, ops->last );
 
     write_header(out, ctb, iobuf_get_temp_length(a) );
-    rc = iobuf_write_temp (out, a);
+    rc = iobuf_write_temp( out, a );
 
     iobuf_close(a);
     return rc;
@@ -1043,19 +1149,21 @@ do_onepass_sig( iobuf_t out, int ctb, PKT_onepass_sig *ops )
 
 
 static int
-write_16(iobuf_t out, u16 a)
+write_16(IOBUF out, u16 a)
 {
     iobuf_put(out, a>>8);
-    return iobuf_put(out,a);
+    if( iobuf_put(out,a) )
+       return -1;
+    return 0;
 }
 
 static int
-write_32(iobuf_t out, u32 a)
+write_32(IOBUF out, u32 a)
 {
     iobuf_put(out, a>> 24);
     iobuf_put(out, a>> 16);
     iobuf_put(out, a>> 8);
-    return iobuf_put (out, a);
+    return iobuf_put(out, a);
 }
 
 
@@ -1088,14 +1196,14 @@ calc_header_length( u32 len, int new_ctb )
  * Write the CTB and the packet length
  */
 static int
-write_header( iobuf_t out, int ctb, u32 len )
+write_header( IOBUF out, int ctb, u32 len )
 {
-    return write_header2( out, ctb, len, 0, 1 );
+    return write_header2( out, ctb, len, 0 );
 }
 
 
 static int
-write_sign_packet_header( iobuf_t out, int ctb, u32 len )
+write_sign_packet_header( IOBUF out, int ctb, u32 len )
 {
     /* work around a bug in the pgp read function for signature packets,
      * which are not correctly coded and silently assume at some
@@ -1106,57 +1214,66 @@ write_sign_packet_header( iobuf_t out, int ctb, u32 len )
 }
 
 /****************
- * if HDRLEN is > 0, try to build a header of this length.
- * we need this, so that we can hash packets without reading them again.
+ * If HDRLEN is > 0, try to build a header of this length.  We need
+ * this so that we can hash packets without reading them again.  If
+ * len is 0, write a partial or indeterminate length header, unless
+ * hdrlen is specified in which case write an actual zero length
+ * (using the specified hdrlen).
  */
 static int
-write_header2( iobuf_t out, int ctb, u32 len, int hdrlen, int blkmode )
+write_header2( IOBUF out, int ctb, u32 len, int hdrlen )
 {
-    if( ctb & 0x40 )
-       return write_new_header( out, ctb, len, hdrlen );
-
-    if( hdrlen ) {
-       if( !len )
-           ctb |= 3;
-       else if( hdrlen == 2 && len < 256 )
-           ;
-       else if( hdrlen == 3 && len < 65536 )
-           ctb |= 1;
-       else
-           ctb |= 2;
-    }
-    else {
-       if( !len )
-           ctb |= 3;
-       else if( len < 256 )
-           ;
-       else if( len < 65536 )
-           ctb |= 1;
-       else
-           ctb |= 2;
+  if( ctb & 0x40 )
+    return write_new_header( out, ctb, len, hdrlen );
+
+  if( hdrlen )
+    {
+      if( hdrlen == 2 && len < 256 )
+       ;
+      else if( hdrlen == 3 && len < 65536 )
+       ctb |= 1;
+      else
+       ctb |= 2;
     }
-    if( iobuf_put(out, ctb ) )
-       return -1;
-    if( !len ) {
-       if( blkmode )
-           iobuf_set_block_mode(out, 8196 );
+  else
+    {
+      if( !len )
+       ctb |= 3;
+      else if( len < 256 )
+       ;
+      else if( len < 65536 )
+       ctb |= 1;
+      else
+       ctb |= 2;
     }
-    else {
-       if( ctb & 2 ) {
-           iobuf_put(out, len >> 24 );
-           iobuf_put(out, len >> 16 );
-       }
-       if( ctb & 3 )
-           iobuf_put(out, len >> 8 );
-       if( iobuf_put(out, len ) )
+
+  if( iobuf_put(out, ctb ) )
+    return -1;
+
+  if( len || hdrlen )
+    {
+      if( ctb & 2 )
+       {
+         if(iobuf_put(out, len >> 24 ))
+           return -1;
+         if(iobuf_put(out, len >> 16 ))
            return -1;
+       }
+
+      if( ctb & 3 )
+       if(iobuf_put(out, len >> 8 ))
+         return -1;
+
+      if( iobuf_put(out, len ) )
+       return -1;
     }
-    return 0;
+
+  return 0;
 }
 
 
 static int
-write_new_header( iobuf_t out, int ctb, u32 len, int hdrlen )
+write_new_header( IOBUF out, int ctb, u32 len, int hdrlen )
 {
     if( hdrlen )
        log_bug("can't cope with hdrlen yet\n");
@@ -1195,7 +1312,7 @@ write_new_header( iobuf_t out, int ctb, u32 len, int hdrlen )
 }
 
 static int
-write_version( iobuf_t out, int ctb )
+write_version( IOBUF out, int ctb )
 {
     if( iobuf_put( out, 3 ) )
        return -1;
index 9c7f840..31c43cf 100644 (file)
@@ -18,8 +18,8 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  */
 
-#if 0  /* lety Emacs display a red warning */
-#error fixme: this shares a lof of code with the file in ../sm
+#if 0  /* let Emacs display a red warning */
+#error fixme: this shares a lot of code with the file in ../sm
 #endif
 
 #include <config.h>
index 1ff57ad..0c83654 100644 (file)
@@ -1,5 +1,5 @@
 /* card-util.c - Utility functions for the OpenPGP card.
- *     Copyright (C) 2003 Free Software Foundation, Inc.
+ *     Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +15,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
@@ -27,7 +28,7 @@
 
 #if GNUPG_MAJOR_VERSION != 1
 #include "gpg.h"
-#endif
+#endif /*GNUPG_MAJOR_VERSION != 1*/
 #include "util.h"
 #include "i18n.h"
 #include "ttyio.h"
 #include "main.h"
 #include "keyserver-internal.h"
 #if GNUPG_MAJOR_VERSION == 1
+#ifdef HAVE_LIBREADLINE
+#include <stdio.h>
+#include <readline/readline.h>
+#endif /*HAVE_LIBREADLINE*/
 #include "cardglue.h"
-#else
+#else /*GNUPG_MAJOR_VERSION!=1*/
 #include "call-agent.h"
-#endif
+#endif /*GNUPG_MAJOR_VERSION!=1*/
 
 #define CONTROL_D ('D' - 'A' + 1)
 
@@ -63,21 +68,25 @@ change_pin (int chvno, int allow_admin)
   log_info (_("OpenPGP card no. %s detected\n"),
               info.serialno? info.serialno : "[none]");
 
-  agent_release_card_info (&info);
+  agent_clear_pin_cache (info.serialno);
 
   if (opt.batch)
     {
-      log_error (_("sorry, can't do this in batch mode\n"));
+      agent_release_card_info (&info);
+      log_error (_("can't do this in batch mode\n"));
       return;
     }
 
   if(!allow_admin)
     {
-      rc = agent_scd_change_pin (1);
+      rc = agent_scd_change_pin (1, info.serialno);
       if (rc)
        tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
       else
-       tty_printf ("PIN changed.\n");
+        {
+          write_status (STATUS_SC_OP_SUCCESS);
+          tty_printf ("PIN changed.\n");
+        }
     }
   else
     for (;;)
@@ -99,33 +108,44 @@ change_pin (int chvno, int allow_admin)
        rc = 0;
        if (*answer == '1')
          {
-           rc = agent_scd_change_pin (1);
+           rc = agent_scd_change_pin (1, info.serialno);
            if (rc)
              tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
            else
-             tty_printf ("PIN changed.\n");
+              {
+                write_status (STATUS_SC_OP_SUCCESS);
+                tty_printf ("PIN changed.\n");
+              }
          }
        else if (*answer == '2')
          {
-           rc = agent_scd_change_pin (101);
+           rc = agent_scd_change_pin (101, info.serialno);
            if (rc)
              tty_printf ("Error unblocking the PIN: %s\n", gpg_strerror (rc));
            else
-             tty_printf ("PIN unblocked and new PIN set.\n");
-         }
+              {
+                write_status (STATUS_SC_OP_SUCCESS);
+                tty_printf ("PIN unblocked and new PIN set.\n");
+              }
+          }
        else if (*answer == '3')
          {
-           rc = agent_scd_change_pin (3);
+           rc = agent_scd_change_pin (3, info.serialno);
            if (rc)
              tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
            else
-             tty_printf ("PIN changed.\n");
+              {
+                write_status (STATUS_SC_OP_SUCCESS);
+                tty_printf ("PIN changed.\n");
+              }
          }
        else if (*answer == 'q' || *answer == 'Q')
          {
            break;
          }
       }
+
+  agent_release_card_info (&info);
 }
 
 static const char *
@@ -137,6 +157,8 @@ get_manufacturer (unsigned int no)
     case 0:
     case 0xffff: return "test card";
     case 0x0001: return "PPC Card Systems";
+    case 0x0002: return "Prism";
+    case 0x0003: return "OpenFortress";
     default: return "unknown";
     }
 }
@@ -270,6 +292,8 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
   PKT_public_key *pk = xcalloc (1, sizeof *pk);
   int rc;
   unsigned int uval;
+  const unsigned char *thefpr;
+  int i;
 
   if (serialno && serialnobuflen)
     *serialno = 0;
@@ -346,6 +370,17 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
                    info.chvretry[0], info.chvretry[1], info.chvretry[2]);
       fprintf (fp, "sigcount:%lu:::\n", info.sig_counter);
 
+      for (i=0; i < 4; i++)
+        {
+          if (info.private_do[i])
+            {
+              fprintf (fp, "private_do:%d:", i+1);
+              print_string (fp, info.private_do[i],
+                            strlen (info.private_do[i]), ':');
+              fputs (":\n", fp);
+            }
+        }
+
       fputs ("cafpr:", fp);
       print_sha1_fpr_colon (fp, info.cafpr1valid? info.cafpr1:NULL);
       print_sha1_fpr_colon (fp, info.cafpr2valid? info.cafpr2:NULL);
@@ -356,7 +391,9 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
       print_sha1_fpr_colon (fp, info.fpr2valid? info.fpr2:NULL);
       print_sha1_fpr_colon (fp, info.fpr3valid? info.fpr3:NULL);
       putc ('\n', fp);
-
+      fprintf (fp, "fprtime:%lu:%lu:%lu:\n",
+               (unsigned long)info.fpr1time, (unsigned long)info.fpr2time,
+               (unsigned long)info.fpr3time);
     }
   else 
     {
@@ -377,6 +414,14 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
                    info.disp_sex == 2? _("female") : _("unspecified"));
       print_name (fp, "URL of public key : ", info.pubkey_url);
       print_name (fp, "Login data .......: ", info.login_data);
+      if (info.private_do[0])
+        print_name (fp, "Private DO 1 .....: ", info.private_do[0]);
+      if (info.private_do[1])
+        print_name (fp, "Private DO 2 .....: ", info.private_do[1]);
+      if (info.private_do[2])
+        print_name (fp, "Private DO 3 .....: ", info.private_do[2]);
+      if (info.private_do[3])
+        print_name (fp, "Private DO 4 .....: ", info.private_do[3]);
       if (info.cafpr1valid)
         {
           tty_fprintf (fp, "CA fingerprint %d .:", 1);
@@ -401,13 +446,48 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
       tty_fprintf (fp,    "Signature counter : %lu\n", info.sig_counter);
       tty_fprintf (fp, "Signature key ....:");
       print_sha1_fpr (fp, info.fpr1valid? info.fpr1:NULL);
+      if (info.fpr1valid && info.fpr1time)
+        tty_fprintf (fp, "      created ....: %s\n",
+                     isotimestamp (info.fpr1time));
       tty_fprintf (fp, "Encryption key....:");
       print_sha1_fpr (fp, info.fpr2valid? info.fpr2:NULL);
+      if (info.fpr2valid && info.fpr2time)
+        tty_fprintf (fp, "      created ....: %s\n",
+                     isotimestamp (info.fpr2time));
       tty_fprintf (fp, "Authentication key:");
       print_sha1_fpr (fp, info.fpr3valid? info.fpr3:NULL);
+      if (info.fpr3valid && info.fpr3time)
+        tty_fprintf (fp, "      created ....: %s\n",
+                     isotimestamp (info.fpr3time));
       tty_fprintf (fp, "General key info..: "); 
-      if (info.fpr1valid && !get_pubkey_byfprint (pk, info.fpr1, 20))
-        print_pubkey_info (fp, pk);
+
+      thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 : 
+                info.fpr3valid? info.fpr3 : NULL);
+      if ( thefpr && !get_pubkey_byfprint (pk, thefpr, 20))
+        {
+          KBNODE keyblock = NULL;
+
+          print_pubkey_info (fp, pk);
+
+          if ( !get_seckeyblock_byfprint (&keyblock, thefpr, 20) )
+            print_card_key_info (fp, keyblock);
+          else if ( !get_keyblock_byfprint (&keyblock, thefpr, 20) )
+            {
+              release_kbnode (keyblock);
+              keyblock = NULL;
+              
+              if (!auto_create_card_key_stub (info.serialno,
+                                              info.fpr1valid? info.fpr1:NULL,
+                                              info.fpr2valid? info.fpr2:NULL,
+                                              info.fpr3valid? info.fpr3:NULL))
+                {
+                  if ( !get_seckeyblock_byfprint (&keyblock, thefpr, 20) )
+                    print_card_key_info (fp, keyblock);
+                }
+            }
+
+          release_kbnode (keyblock);
+        }
       else
         tty_fprintf (fp, "[none]\n");
     }
@@ -483,8 +563,7 @@ change_name (void)
       return -1;
     }
 
-  log_debug ("setting Name to `%s'\n", isoname);
-  rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname) );
+  rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname), NULL );
   if (rc)
     log_error ("error setting Name: %s\n", gpg_strerror (rc));
 
@@ -513,13 +592,16 @@ change_url (void)
       return -1;
     }
 
-  rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url) );
+  rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url), NULL );
   if (rc)
     log_error ("error setting URL: %s\n", gpg_strerror (rc));
   xfree (url);
   return rc;
 }
 
+
+/* Fetch the key from the URL given on the card or try to get it from
+   the default keyserver.  */
 static int
 fetch_url(void)
 {
@@ -532,7 +614,7 @@ fetch_url(void)
   rc=agent_scd_getattr("PUBKEY-URL",&info);
   if(rc)
     log_error("error retrieving URL from card: %s\n",gpg_strerror(rc));
-  else if(info.pubkey_url)
+  else
     {
       struct keyserver_spec *spec=NULL;
 
@@ -540,9 +622,9 @@ fetch_url(void)
       if(rc)
        log_error("error retrieving key fingerprint from card: %s\n",
                  gpg_strerror(rc));
-      else
+      else if (info.pubkey_url && *info.pubkey_url)
        {
-         spec=parse_keyserver_uri(info.pubkey_url,0,NULL,0);
+         spec=parse_keyserver_uri(info.pubkey_url,1,NULL,0);
          if(spec && info.fpr1valid)
            {
              /* This is not perfectly right.  Currently, all card
@@ -556,9 +638,11 @@ fetch_url(void)
              free_keyserver_spec(spec);
            }
        }
+      else if (info.fpr1valid)
+       {
+          rc = keyserver_import_fprint (info.fpr1, 20, opt.keyserver);
+       }
     }
-  else
-    log_error("no URL set on card\n");
 
   return rc;
 #else
@@ -624,7 +708,7 @@ change_login (const char *args)
       return -1;
     }
 
-  rc = agent_scd_setattr ("LOGIN-DATA", data, n );
+  rc = agent_scd_setattr ("LOGIN-DATA", data, n, NULL );
   if (rc)
     log_error ("error setting login data: %s\n", gpg_strerror (rc));
   xfree (data);
@@ -632,6 +716,75 @@ change_login (const char *args)
 }
 
 static int
+change_private_do (const char *args, int nr)
+{
+  char do_name[] = "PRIVATE-DO-X";
+  char *data;
+  int n;
+  int rc; 
+
+  assert (nr >= 1 && nr <= 4);
+  do_name[11] = '0' + nr;
+
+  if (args && (args = strchr (args, '<')))  /* Read it from a file */
+    {
+      FILE *fp;
+
+      /* Fixme: Factor this duplicated code out. */
+      for (args++; spacep (args); args++)
+        ;
+      fp = fopen (args, "rb");
+#if GNUPG_MAJOR_VERSION == 1
+      if (fp && is_secured_file (fileno (fp)))
+        {
+          fclose (fp);
+          fp = NULL;
+          errno = EPERM;
+        }
+#endif
+      if (!fp)
+        {
+          tty_printf (_("can't open `%s': %s\n"), args, strerror (errno));
+          return -1;
+        }
+          
+      data = xmalloc (254);
+      n = fread (data, 1, 254, fp);
+      fclose (fp);
+      if (n < 0)
+        {
+          tty_printf (_("error reading `%s': %s\n"), args, strerror (errno));
+          xfree (data);
+          return -1;
+        }
+    }
+  else
+    {
+      data = cpr_get ("cardedit.change_private_do",
+                      _("Private DO data: "));
+      if (!data)
+        return -1;
+      trim_spaces (data);
+      cpr_kill_prompt ();
+      n = strlen (data);
+    }
+
+  if (n > 254 )
+    {
+      tty_printf (_("Error: Private DO too long "
+                    "(limit is %d characters).\n"), 254);    
+      xfree (data);
+      return -1;
+    }
+
+  rc = agent_scd_setattr (do_name, data, n, NULL );
+  if (rc)
+    log_error ("error setting private DO: %s\n", gpg_strerror (rc));
+  xfree (data);
+  return rc;
+}
+
+static int
 change_lang (void)
 {
   char *data, *p;
@@ -660,7 +813,7 @@ change_lang (void)
       return -1;
     }
 
-  rc = agent_scd_setattr ("DISP-LANG", data, strlen (data) );
+  rc = agent_scd_setattr ("DISP-LANG", data, strlen (data), NULL );
   if (rc)
     log_error ("error setting lang: %s\n", gpg_strerror (rc));
   xfree (data);
@@ -695,7 +848,7 @@ change_sex (void)
       return -1;
     }
      
-  rc = agent_scd_setattr ("DISP-SEX", str, 1 );
+  rc = agent_scd_setattr ("DISP-SEX", str, 1, NULL );
   if (rc)
     log_error ("error setting sex: %s\n", gpg_strerror (rc));
   xfree (data);
@@ -740,7 +893,7 @@ change_cafpr (int fprno)
 
   rc = agent_scd_setattr (fprno==1?"CA-FPR-1":
                           fprno==2?"CA-FPR-2":
-                          fprno==3?"CA-FPR-3":"x", fpr, 20 );
+                          fprno==3?"CA-FPR-3":"x", fpr, 20, NULL );
   if (rc)
     log_error ("error setting cafpr: %s\n", gpg_strerror (rc));
   return rc;
@@ -765,7 +918,7 @@ toggle_forcesig (void)
   newstate = !info.chv1_cached;
   agent_release_card_info (&info);
 
-  rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1);
+  rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1, NULL);
   if (rc)
     log_error ("error toggling signature PIN flag: %s\n", gpg_strerror (rc));
 }
@@ -803,12 +956,14 @@ check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1)
 {     
   int rc = 0;
 
+  agent_clear_pin_cache (info->serialno);
+
   *forced_chv1 = !info->chv1_cached;
   if (*forced_chv1)
     { /* Switch of the forced mode so that during key generation we
          don't get bothered with PIN queries for each
          self-signature. */
-      rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1);
+      rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1, info->serialno);
       if (rc)
         {
           log_error ("error clearing forced signature PIN flag: %s\n",
@@ -836,7 +991,7 @@ restore_forced_chv1 (int *forced_chv1)
 
   if (*forced_chv1)
     { /* Switch back to forced state. */
-      rc = agent_scd_setattr ("CHV-STATUS-1", "", 1);
+      rc = agent_scd_setattr ("CHV-STATUS-1", "", 1, NULL);
       if (rc)
         {
           log_error ("error setting forced signature PIN flag: %s\n",
@@ -900,7 +1055,7 @@ generate_card_keys (const char *serialno)
 
     want_backup=answer_is_yes_no_default(answer,1);
     cpr_kill_prompt();
-    m_free(answer);
+    xfree(answer);
   }
 #else
   want_backup = cpr_get_answer_is_yes 
@@ -949,7 +1104,7 @@ generate_card_keys (const char *serialno)
 }
 
 
-/* This fucntion is used by the key edit menu to generate an arbitrary
+/* This function is used by the key edit menu to generate an arbitrary
    subkey. */
 int
 card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
@@ -1007,9 +1162,10 @@ card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
 }
 
 
-/* Store the subkey at NODE into the smartcard and modify NODE to
-   carry the serrialno stuff instead of the actual secret key
-   parameters. */
+/* Store the key at NODE into the smartcard and modify NODE to
+   carry the serialno stuff instead of the actual secret key
+   parameters.  USE is the usage for that key; 0 means any
+   usage. */
 int 
 card_store_subkey (KBNODE node, int use)
 {
@@ -1140,49 +1296,101 @@ card_store_subkey (KBNODE node, int use)
 }
 
 
-/* Menu to edit all user changeable values on an OpenPGP card.  Only
-   Key creation is not handled here. */
-void
-card_edit (STRLIST commands)
-{
-  enum cmdids {
+\f
+/* Data used by the command parser.  This needs to be outside of the
+   function scope to allow readline based command completion.  */
+enum cmdids
+  {
     cmdNOP = 0,
-    cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG,
+    cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
-    cmdFORCESIG, cmdGENERATE, cmdPASSWD,
+    cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO,
     cmdINVCMD
   };
 
-  static struct {
-    const char *name;
-    enum cmdids id;
-    int admin_only;
-    const char *desc;
-  } cmds[] = {
-    { N_("quit")  , cmdQUIT  , 0, N_("quit this menu") },
-    { N_("q")     , cmdQUIT  , 0, NULL   },
-    { N_("admin") , cmdADMIN , 0, N_("show admin commands") },
-    { N_("help")  , cmdHELP  , 0, N_("show this help") },
-    {    "?"      , cmdHELP  , 0, NULL   },
-    { N_("list")  , cmdLIST  , 0, N_("list all available data") },
-    { N_("l")     , cmdLIST  , 0, NULL   },
-    { N_("debug") , cmdDEBUG , 0, NULL },
-    { N_("name")  , cmdNAME  , 1, N_("change card holder's name") },
-    { N_("url")   , cmdURL   , 1, N_("change URL to retrieve key") },
-    { N_("fetch") , cmdFETCH , 0,
-                               N_("fetch the key specified in the card URL") },
-    { N_("login") , cmdLOGIN , 1, N_("change the login name") },
-    { N_("lang")  , cmdLANG  , 1, N_("change the language preferences") },
-    { N_("sex")   , cmdSEX   , 1, N_("change card holder's sex") },
-    { N_("cafpr"),  cmdCAFPR,  1, N_("change a CA fingerprint") },
-    { N_("forcesig"),
-                  cmdFORCESIG, 1, N_("toggle the signature force PIN flag") },
-    { N_("generate"),
-                  cmdGENERATE, 1, N_("generate new keys") },
-    { N_("passwd"), cmdPASSWD, 0, N_("menu to change or unblock the PIN") },
+static struct
+{
+  const char *name;
+  enum cmdids id;
+  int admin_only;
+  const char *desc;
+} cmds[] =
+  {
+    { "quit"    , cmdQUIT  , 0, N_("quit this menu")},
+    { "q"       , cmdQUIT  , 0, NULL },
+    { "admin"   , cmdADMIN , 0, N_("show admin commands")},
+    { "help"    , cmdHELP  , 0, N_("show this help")},
+    { "?"       , cmdHELP  , 0, NULL },
+    { "list"    , cmdLIST  , 0, N_("list all available data")},
+    { "l"       , cmdLIST  , 0, NULL },
+    { "debug"   , cmdDEBUG , 0, NULL },
+    { "name"    , cmdNAME  , 1, N_("change card holder's name")},
+    { "url"     , cmdURL   , 1, N_("change URL to retrieve key")},
+    { "fetch"   , cmdFETCH , 0, N_("fetch the key specified in the card URL")},
+    { "login"   , cmdLOGIN , 1, N_("change the login name")},
+    { "lang"    , cmdLANG  , 1, N_("change the language preferences")},
+    { "sex"     , cmdSEX   , 1, N_("change card holder's sex")},
+    { "cafpr"   , cmdCAFPR , 1, N_("change a CA fingerprint")},
+    { "forcesig", cmdFORCESIG, 1, N_("toggle the signature force PIN flag")},
+    { "generate", cmdGENERATE, 1, N_("generate new keys")},
+    { "passwd"  , cmdPASSWD, 0, N_("menu to change or unblock the PIN")},
+    { "verify"  , cmdVERIFY, 0, N_("verify the PIN and list all data")},
+    /* Note, that we do not announce this command yet. */
+    { "privatedo", cmdPRIVATEDO, 0, NULL },
     { NULL, cmdINVCMD, 0, NULL } 
   };
+
+
+#if GNUPG_MAJOR_VERSION == 1 && defined (HAVE_LIBREADLINE)
+
+/* These two functions are used by readline for command completion. */
+
+static char *
+command_generator(const char *text,int state)
+{
+  static int list_index,len;
+  const char *name;
+
+  /* If this is a new word to complete, initialize now.  This includes
+     saving the length of TEXT for efficiency, and initializing the
+     index variable to 0. */
+  if(!state)
+    {
+      list_index=0;
+      len=strlen(text);
+    }
+
+  /* Return the next partial match */
+  while((name=cmds[list_index].name))
+    {
+      /* Only complete commands that have help text */
+      if(cmds[list_index++].desc && strncmp(name,text,len)==0)
+       return strdup(name);
+    }
+
+  return NULL;
+}
+
+static char **
+card_edit_completion(const char *text, int start, int end)
+{
+  /* If we are at the start of a line, we try and command-complete.
+     If not, just do nothing for now. */
+
+  if(start==0)
+    return rl_completion_matches(text,command_generator);
+
+  rl_attempted_completion_over=1;
+
+  return NULL;
+}
+#endif /* GNUPG_MAJOR_VERSION == 1 && HAVE_LIBREADLINE */
+
+/* Menu to edit all user changeable values on an OpenPGP card.  Only
+   Key creation is not handled here. */
+void
+card_edit (STRLIST commands)
+{
   enum cmdids cmd = cmdNOP;
   int have_commands = !!commands;
   int redisplay = 1;
@@ -1195,7 +1403,7 @@ card_edit (STRLIST commands)
     ;
   else if (opt.batch && !have_commands)
     {
-      log_error(_("can't do that in batchmode\n"));
+      log_error(_("can't do this in batch mode\n"));
       goto leave;
     }
 
@@ -1243,8 +1451,14 @@ card_edit (STRLIST commands)
 
            if (!have_commands)
               {
+#if GNUPG_MAJOR_VERSION == 1
+               tty_enable_completion (card_edit_completion);
+#endif
                answer = cpr_get_no_help("cardedit.prompt", _("Command> "));
                cpr_kill_prompt();
+#if GNUPG_MAJOR_VERSION == 1
+               tty_disable_completion ();
+#endif
            }
            trim_spaces(answer);
        }
@@ -1292,9 +1506,33 @@ card_edit (STRLIST commands)
           break;
 
        case cmdADMIN:
-         allow_admin=!allow_admin;
+          if ( !strcmp (arg_string, "on") )
+            allow_admin = 1;
+          else if ( !strcmp (arg_string, "off") )
+            allow_admin = 0;
+          else if ( !strcmp (arg_string, "verify") )
+            {
+              /* Force verification of the Admin Command.  However,
+                 this is only done if the retry counter is at initial
+                 state.  */
+              char *tmp = xmalloc (strlen (serialnobuf) + 6 + 1);
+              strcpy (stpcpy (tmp, serialnobuf), "[CHV3]");
+              allow_admin = !agent_scd_checkpin (tmp);
+              xfree (tmp);
+            }
+          else /* Toggle. */
+            allow_admin=!allow_admin;
+         if(allow_admin)
+           tty_printf(_("Admin commands are allowed\n"));
+         else
+           tty_printf(_("Admin commands are not allowed\n"));
          break;
 
+        case cmdVERIFY:
+          agent_scd_checkpin (serialnobuf);
+          redisplay = 1;
+          break;
+
         case cmdLIST:
           redisplay = 1;
           break;
@@ -1331,6 +1569,14 @@ card_edit (STRLIST commands)
             change_cafpr (arg_number);
           break;
 
+        case cmdPRIVATEDO:
+          if ( arg_number < 1 || arg_number > 4 )
+            tty_printf ("usage: privatedo N\n"
+                        "       1 <= N <= 4\n");
+          else
+            change_private_do (arg_string, arg_number);
+          break;
+
         case cmdFORCESIG:
           toggle_forcesig ();
           break;
index 3d51a87..ff10804 100644 (file)
@@ -1,5 +1,6 @@
 /* cipher.c - En-/De-ciphering filter
- * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003,
+ *               2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
@@ -28,7 +30,6 @@
 #include "gpg.h"
 #include "errors.h"
 #include "iobuf.h"
-#include "memory.h"
 #include "util.h"
 #include "filter.h"
 #include "packet.h"
 
 
 static void
-write_header( cipher_filter_context_t *cfx, iobuf_t a )
+write_header( cipher_filter_context_t *cfx, IOBUF a )
 {
+     gcry_error_t err;
     PACKET pkt;
     PKT_encrypted ed;
     byte temp[18];
     unsigned int blocksize;
     unsigned int nprefix;
-    gpg_error_t rc;
 
-    blocksize = gcry_cipher_get_algo_blklen ( cfx->dek->algo );
-    if( blocksize < 8 || blocksize > 16 )
+    blocksize = gcry_cipher_algo_blklen (cfx->dek->algo);
+    if ( blocksize < 8 || blocksize > 16 )
        log_fatal("unsupported blocksize %u\n", blocksize );
 
     memset( &ed, 0, sizeof ed );
@@ -60,9 +61,9 @@ write_header( cipher_filter_context_t *cfx, iobuf_t a )
     ed.new_ctb = !ed.len && !RFC1991;
     if( cfx->dek->use_mdc ) {
        ed.mdc_method = DIGEST_ALGO_SHA1;
-       gcry_md_open (&cfx->mdc_hash, GCRY_MD_SHA1, 0 );
+       gcry_md_open (&cfx->mdc_hash, DIGEST_ALGO_SHA1, 0);
        if ( DBG_HASHING )
-           gcry_md_start_debug ( cfx->mdc_hash, "creatmdc" );
+           gcry_md_start_debug (cfx->mdc_hash, "creatmdc");
     }
 
     {
@@ -78,28 +79,31 @@ write_header( cipher_filter_context_t *cfx, iobuf_t a )
     if( build_packet( a, &pkt ))
        log_bug("build_packet(ENCR_DATA) failed\n");
     nprefix = blocksize;
-    gcry_randomize ( temp, nprefix, GCRY_STRONG_RANDOM);
+    gcry_randomize (temp, nprefix, GCRY_STRONG_RANDOM );
     temp[nprefix] = temp[nprefix-2];
     temp[nprefix+1] = temp[nprefix-1];
     print_cipher_algo_note( cfx->dek->algo );
-    rc = gcry_cipher_open (&cfx->cipher_hd, cfx->dek->algo,
-                           GCRY_CIPHER_MODE_CFB,
-                           GCRY_CIPHER_SECURE
-                           | ((cfx->dek->use_mdc || cfx->dek->algo >= 100) ?
-                              0 : GCRY_CIPHER_ENABLE_SYNC));
+    err = gcry_cipher_open (&cfx->cipher_hd, 
+                            cfx->dek->algo,
+                            GCRY_CIPHER_MODE_CFB,
+                            (GCRY_CIPHER_SECURE
+                             | ((cfx->dek->use_mdc || cfx->dek->algo >= 100)?
+                                0 : GCRY_CIPHER_ENABLE_SYNC));
     if (rc) {
-       /* we should never get an error here cause we already checked, that
-        * the algorithm is available. */
+       /* We should never get an error here cause we already checked,
+        * that the algorithm is available.  */
        BUG();
     }
+
+
 /*   log_hexdump( "thekey", cfx->dek->key, cfx->dek->keylen );*/
     gcry_cipher_setkey( cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen );
     gcry_cipher_setiv( cfx->cipher_hd, NULL, 0 );
 /*  log_hexdump( "prefix", temp, nprefix+2 ); */
-    if( cfx->mdc_hash ) /* hash the "IV" */
-       gcry_md_writecfx->mdc_hash, temp, nprefix+2 );
-    gcry_cipher_encryptcfx->cipher_hd, temp, nprefix+2, NULL, 0);
-    gcry_cipher_sync( cfx->cipher_hd );
+    if (cfx->mdc_hash) /* Hash the "IV". */
+       gcry_md_write (cfx->mdc_hash, temp, nprefix+2 );
+    gcry_cipher_encrypt (cfx->cipher_hd, temp, nprefix+2, NULL, 0);
+    gcry_cipher_sync (cfx->cipher_hd);
     iobuf_write(a, temp, nprefix+2);
     cfx->header=1;
 }
@@ -111,7 +115,7 @@ write_header( cipher_filter_context_t *cfx, iobuf_t a )
  */
 int
 cipher_filter( void *opaque, int control,
-              iobuf_t a, byte *buf, size_t *ret_len)
+              IOBUF a, byte *buf, size_t *ret_len)
 {
     size_t size = *ret_len;
     cipher_filter_context_t *cfx = opaque;
@@ -125,32 +129,31 @@ cipher_filter( void *opaque, int control,
        if( !cfx->header ) {
            write_header( cfx, a );
        }
-       if( cfx->mdc_hash )
-           gcry_md_write( cfx->mdc_hash, buf, size );
-       gcry_cipher_encryptcfx->cipher_hd, buf, size, NULL, 0);
+       if (cfx->mdc_hash)
+           gcry_md_write (cfx->mdc_hash, buf, size);
+       gcry_cipher_encrypt (cfx->cipher_hd, buf, size, NULL, 0);
        rc = iobuf_write( a, buf, size );
     }
     else if( control == IOBUFCTRL_FREE ) {
        if( cfx->mdc_hash ) {
            byte *hash;
-           int hashlen = gcry_md_get_algo_dlen (gcry_md_get_algo (
-                                                            cfx->mdc_hash));
+           int hashlen = gcry_md_get_algo_dlen (gcry_md_get_algo
+                                                 (cfx->mdc_hash));
            byte temp[22];
 
            assert( hashlen == 20 );
            /* we must hash the prefix of the MDC packet here */
            temp[0] = 0xd3;
            temp[1] = 0x14;
-           gcry_md_putc ( cfx->mdc_hash, temp[0] );
-           gcry_md_putc ( cfx->mdc_hash, temp[1] );
+           gcry_md_putc (cfx->mdc_hash, temp[0]);
+           gcry_md_putc (cfx->mdc_hash, temp[1]);
 
-           gcry_md_final ( cfx->mdc_hash );
-           hash = gcry_md_read ( cfx->mdc_hash, 0 );
+           gcry_md_final (cfx->mdc_hash);
+           hash = gcry_md_read (cfx->mdc_hash, 0);
            memcpy(temp+2, hash, 20);
-           gcry_cipher_encrypt( cfx->cipher_hd, temp, 22, NULL, 0 );
-           gcry_md_close ( cfx->mdc_hash ); cfx->mdc_hash = NULL;
-           rc = iobuf_write( a, temp, 22 );
-            if (rc)
+           gcry_cipher_encrypt (cfx->cipher_hd, temp, 22, NULL, 0);
+           gcry_md_close (cfx->mdc_hash); cfx->mdc_hash = NULL;
+           if( iobuf_write( a, temp, 22 ) )
                log_error("writing MDC packet failed\n" );
        }
        gcry_cipher_close (cfx->cipher_hd);
@@ -160,6 +163,3 @@ cipher_filter( void *opaque, int control,
     }
     return rc;
 }
-
-
-
index 7dce179..030a4c1 100644 (file)
@@ -1,6 +1,6 @@
 /* compress.c - compress filter
  * Copyright (C) 1998, 1999, 2000, 2001, 2002,
- *               2003 Free Software Foundation, Inc.
+ *               2003, 2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
+/* Note that the code in compress-bz2.c is nearly identical to the
+   code here, so if you fix a bug here, look there to see if a
+   matching bug needs to be fixed.  I tried to have one set of
+   functions that could do ZIP, ZLIB, and BZIP2, but it became
+   dangerously unreadable with #ifdefs and if(algo) -dshaw */
+
 #include <config.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
 #include <errno.h>
 #include <zlib.h>
-#ifdef __riscos__
+#if defined(__riscos__) && defined(USE_ZLIBRISCOS)
 # include "zlib-riscos.h"
-#endif
+#endif 
 
 #include "gpg.h"
 #include "util.h"
-#include "memory.h"
 #include "packet.h"
 #include "filter.h"
 #include "main.h"
 #include "options.h"
 
+int compress_filter_bz2( void *opaque, int control,
+                        IOBUF a, byte *buf, size_t *ret_len);
+
 static void
 init_compress( compress_filter_context_t *zfx, z_stream *zs )
 {
     int rc;
     int level;
 
-#ifdef __riscos__
+#if defined(__riscos__) && defined(USE_ZLIBRISCOS)
     static int zlib_initialized = 0;
 
     if (!zlib_initialized)
         zlib_initialized = riscos_load_module("ZLib", zlib_path, 1);
 #endif
 
-    if( opt.compress >= 0 && opt.compress <= 9 )
-       level = opt.compress;
-    else if( opt.compress == -1 )
+    if( opt.compress_level >= 1 && opt.compress_level <= 9 )
+       level = opt.compress_level;
+    else if( opt.compress_level == -1 )
        level = Z_DEFAULT_COMPRESSION;
-    else if( opt.compress == 10 ) /* remove this ! */
-       level = 0;
     else {
        log_error("invalid compression level; using default level\n");
        level = Z_DEFAULT_COMPRESSION;
     }
 
-
     if( (rc = zfx->algo == 1? deflateInit2( zs, level, Z_DEFLATED,
                                            -13, 8, Z_DEFAULT_STRATEGY)
                            : deflateInit( zs, level )
@@ -75,13 +81,13 @@ init_compress( compress_filter_context_t *zfx, z_stream *zs )
     }
 
     zfx->outbufsize = 8192;
-    zfx->outbuf = xmalloc ( zfx->outbufsize );
+    zfx->outbuf = xmalloc( zfx->outbufsize );
 }
 
 static int
-do_compress( compress_filter_context_t *zfx, z_stream *zs, int flush, iobuf_t a )
+do_compress( compress_filter_context_t *zfx, z_stream *zs, int flush, IOBUF a )
 {
-    gpg_error_t rc;
+    int rc;
     int zrc;
     unsigned n;
 
@@ -111,12 +117,10 @@ do_compress( compress_filter_context_t *zfx, z_stream *zs, int flush, iobuf_t a
                (unsigned)zs->avail_in, (unsigned)zs->avail_out,
                                               (unsigned)n, zrc );
 
-       rc = iobuf_write (a, zfx->outbuf, n);
-        if (rc)
-          {
+       if( (rc=iobuf_write( a, zfx->outbuf, n )) ) {
            log_debug("deflate: iobuf_write failed\n");
            return rc;
-          }
+       }
     } while( zs->avail_in || (flush == Z_FINISH && zrc != Z_STREAM_END) );
     return 0;
 }
@@ -145,13 +149,13 @@ init_uncompress( compress_filter_context_t *zfx, z_stream *zs )
     }
 
     zfx->inbufsize = 2048;
-    zfx->inbuf = xmalloc ( zfx->inbufsize );
+    zfx->inbuf = xmalloc( zfx->inbufsize );
     zs->avail_in = 0;
 }
 
 static int
 do_uncompress( compress_filter_context_t *zfx, z_stream *zs,
-              iobuf_t a, size_t *ret_len )
+              IOBUF a, size_t *ret_len )
 {
     int zrc;
     int rc=0;
@@ -213,9 +217,9 @@ do_uncompress( compress_filter_context_t *zfx, z_stream *zs,
     return rc;
 }
 
-int
+static int
 compress_filter( void *opaque, int control,
-                iobuf_t a, byte *buf, size_t *ret_len)
+                IOBUF a, byte *buf, size_t *ret_len)
 {
     size_t size = *ret_len;
     compress_filter_context_t *zfx = opaque;
@@ -224,7 +228,7 @@ compress_filter( void *opaque, int control,
 
     if( control == IOBUFCTRL_UNDERFLOW ) {
        if( !zfx->status ) {
-           zs = zfx->opaque = xcalloc (1, sizeof *zs );
+           zs = zfx->opaque = xmalloc_clear( sizeof *zs );
            init_uncompress( zfx, zs );
            zfx->status = 1;
        }
@@ -242,10 +246,8 @@ compress_filter( void *opaque, int control,
        if( !zfx->status ) {
            PACKET pkt;
            PKT_compressed cd;
-
-           if( !zfx->algo )
-               zfx->algo = DEFAULT_COMPRESS_ALGO;
-           if( zfx->algo != 1 && zfx->algo != 2 )
+           if(zfx->algo != COMPRESS_ALGO_ZIP
+              && zfx->algo != COMPRESS_ALGO_ZLIB)
              BUG();
            memset( &cd, 0, sizeof cd );
            cd.len = 0;
@@ -255,7 +257,7 @@ compress_filter( void *opaque, int control,
            pkt.pkt.compressed = &cd;
            if( build_packet( a, &pkt ))
                log_bug("build_packet(PKT_COMPRESSED) failed\n");
-           zs = zfx->opaque = xcalloc (1, sizeof *zs );
+           zs = zfx->opaque = xmalloc_clear( sizeof *zs );
            init_compress( zfx, zs );
            zfx->status = 2;
        }
@@ -271,9 +273,9 @@ compress_filter( void *opaque, int control,
     else if( control == IOBUFCTRL_FREE ) {
        if( zfx->status == 1 ) {
            inflateEnd(zs);
-           xfree (zs);
+           xfree(zs);
            zfx->opaque = NULL;
-           xfree (zfx->outbuf); zfx->outbuf = NULL;
+           xfree(zfx->outbuf); zfx->outbuf = NULL;
        }
        else if( zfx->status == 2 ) {
 #ifndef __riscos__
@@ -284,9 +286,9 @@ compress_filter( void *opaque, int control,
            zs->avail_in = 0;
            do_compress( zfx, zs, Z_FINISH, a );
            deflateEnd(zs);
-           xfree (zs);
+           xfree(zs);
            zfx->opaque = NULL;
-           xfree (zfx->outbuf); zfx->outbuf = NULL;
+           xfree(zfx->outbuf); zfx->outbuf = NULL;
        }
         if (zfx->release)
           zfx->release (zfx);
@@ -308,17 +310,17 @@ release_context (compress_filter_context_t *ctx)
  */
 int
 handle_compressed( void *procctx, PKT_compressed *cd,
-                  int (*callback)(iobuf_t, void *), void *passthru )
+                  int (*callback)(IOBUF, void *), void *passthru )
 {
     compress_filter_context_t *cfx;
     int rc;
 
-    if( cd->algorithm < 1 || cd->algorithm > 2 )
-       return GPG_ERR_COMPR_ALGO;
-    cfx = xcalloc (1,sizeof *cfx);
-    cfx->algo = cd->algorithm;
+    if(check_compress_algo(cd->algorithm))
+      return G10ERR_COMPR_ALGO;
+    cfx = xmalloc_clear (sizeof *cfx);
     cfx->release = release_context;
-    iobuf_push_filter( cd->buf, compress_filter, cfx );
+    cfx->algo = cd->algorithm;
+    push_compress_filter(cd->buf,cfx,cd->algorithm);
     if( callback )
        rc = callback(cd->buf, passthru );
     else
@@ -327,3 +329,38 @@ handle_compressed( void *procctx, PKT_compressed *cd,
     return rc;
 }
 
+void
+push_compress_filter(IOBUF out,compress_filter_context_t *zfx,int algo)
+{
+  push_compress_filter2(out,zfx,algo,0);
+}
+
+void
+push_compress_filter2(IOBUF out,compress_filter_context_t *zfx,
+                     int algo,int rel)
+{
+  if(algo>=0)
+    zfx->algo=algo;
+  else
+    zfx->algo=DEFAULT_COMPRESS_ALGO;
+
+  switch(zfx->algo)
+    {
+    case COMPRESS_ALGO_NONE:
+      break;
+
+    case COMPRESS_ALGO_ZIP:
+    case COMPRESS_ALGO_ZLIB:
+      iobuf_push_filter2(out,compress_filter,zfx,rel);
+      break;
+
+#ifdef HAVE_BZIP2
+    case COMPRESS_ALGO_BZIP2:
+      iobuf_push_filter2(out,compress_filter_bz2,zfx,rel);
+      break;
+#endif
+
+    default:
+      BUG();
+    }
+}
index 4f9fa2d..dc9a22f 100644 (file)
@@ -1,5 +1,5 @@
 /* dearmor.c - Armor utility
- * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +15,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include "gpg.h"
 #include "errors.h"
 #include "iobuf.h"
-#include "memory.h"
 #include "util.h"
 #include "filter.h"
 #include "packet.h"
 #include "options.h"
 #include "main.h"
-
+#include "i18n.h"
 
 /****************
  * Take an armor file and write it out without armor
@@ -43,17 +43,24 @@ int
 dearmor_file( const char *fname )
 {
     armor_filter_context_t afx;
-    iobuf_t inp = NULL, out = NULL;
+    IOBUF inp = NULL, out = NULL;
     int rc = 0;
     int c;
 
     memset( &afx, 0, sizeof afx);
 
     /* prepare iobufs */
-    if( !(inp = iobuf_open(fname)) ) {
-        rc = gpg_error_from_errno (errno);
-       log_error("can't open %s: %s\n", fname? fname: "[stdin]",
+    inp = iobuf_open(fname);
+    if (inp && is_secured_file (iobuf_get_fd (inp)))
+      {
+        iobuf_close (inp);
+        inp = NULL;
+        errno = EPERM;
+      }
+    if (!inp) {
+       log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]",
                                        strerror(errno) );
+       rc = G10ERR_OPEN_FILE;
        goto leave;
     }
 
@@ -85,17 +92,24 @@ int
 enarmor_file( const char *fname )
 {
     armor_filter_context_t afx;
-    iobuf_t inp = NULL, out = NULL;
+    IOBUF inp = NULL, out = NULL;
     int rc = 0;
     int c;
 
     memset( &afx, 0, sizeof afx);
 
     /* prepare iobufs */
-    if( !(inp = iobuf_open(fname)) ) {
-        rc = gpg_error_from_errno (errno);
-       log_error("can't open %s: %s\n", fname? fname: "[stdin]",
-                                       strerror(errno) );
+    inp = iobuf_open(fname);
+    if (inp && is_secured_file (iobuf_get_fd (inp)))
+      {
+        iobuf_close (inp);
+        inp = NULL;
+        errno = EPERM;
+      }
+    if (!inp) {
+       log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]",
+                  strerror(errno) );
+       rc = G10ERR_OPEN_FILE;
        goto leave;
     }
 
index 98a270c..9a37283 100644 (file)
@@ -1,5 +1,6 @@
 /* decrypt.c - verify signed data
- * Copyright (C) 1998,1999,2000,2001,2002,2003 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
+ *               2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include <errno.h>
 #include <assert.h>
 
+#include "gpg.h"
 #include "options.h"
 #include "packet.h"
 #include "errors.h"
 #include "iobuf.h"
 #include "keydb.h"
-#include "memory.h"
 #include "util.h"
 #include "main.h"
 #include "status.h"
 int
 decrypt_message( const char *filename )
 {
-    iobuf_t fp;
+    IOBUF fp;
     armor_filter_context_t afx;
     progress_filter_context_t pfx;
     int rc;
     int no_out=0;
 
-    /* open the message file */
+    /* Open the message file.  */
     fp = iobuf_open(filename);
+    if (fp && is_secured_file (iobuf_get_fd (fp)))
+      {
+        iobuf_close (fp);
+        fp = NULL;
+        errno = EPERM;
+      }
     if( !fp ) {
         rc = gpg_error_from_errno (errno);
-       log_error(_("can't open `%s'\n"), print_fname_stdin(filename));
+       log_error (_("can't open `%s': %s\n"), print_fname_stdin(filename),
+                   gpg_strerror (rc));
        return rc;
     }
 
@@ -84,13 +93,14 @@ decrypt_message( const char *filename )
 }
 
 void
-decrypt_messages(int nfiles, char **files)
+decrypt_messages(int nfiles, char *files[])
 {
-  iobuf_t fp;
+  IOBUF fp;
   armor_filter_context_t afx;  
   progress_filter_context_t pfx;
   char *p, *output = NULL;
-  int rc = 0;
+  int rc=0,use_stdin=0;
+  unsigned int lno=0;
   
   if (opt.outfile)
     {
@@ -99,20 +109,61 @@ decrypt_messages(int nfiles, char **files)
         
     }
 
-  while (nfiles--)
+  if(!nfiles)
+    use_stdin=1;
+
+  for(;;)
     {
-      print_file_status(STATUS_FILE_START, *files, 3);      
-      output = make_outfile_name(*files);
+      char line[2048];
+      char *filename=NULL;
+
+      if(use_stdin)
+       {
+         if(fgets(line, DIM(line), stdin))
+           {
+             lno++;
+             if (!*line || line[strlen(line)-1] != '\n')
+               log_error("input line %u too long or missing LF\n", lno);
+             else
+               {
+                 line[strlen(line)-1] = '\0';
+                 filename=line;
+               }
+           }
+       }
+      else
+       {
+         if(nfiles)
+           {
+             filename=*files;
+             nfiles--;
+             files++;
+           }
+       }
+
+      if(filename==NULL)
+       break;
+
+      print_file_status(STATUS_FILE_START, filename, 3);      
+      output = make_outfile_name(filename);
       if (!output)
         goto next_file;
-      fp = iobuf_open(*files);
+      fp = iobuf_open(filename);
+      if (fp)
+        iobuf_ioctl (fp,3,1,NULL); /* disable fd caching */
+      if (fp && is_secured_file (iobuf_get_fd (fp)))
+        {
+          iobuf_close (fp);
+          fp = NULL;
+          errno = EPERM;
+        }
       if (!fp)
         {
-          log_error(_("can't open `%s'\n"), print_fname_stdin(*files));
+          log_error(_("can't open `%s'\n"), print_fname_stdin(filename));
           goto next_file;
         }
 
-      handle_progress (&pfx, fp, *files);
+      handle_progress (&pfx, fp, filename);
 
       if (!opt.no_armor)
         {
@@ -125,8 +176,8 @@ decrypt_messages(int nfiles, char **files)
       rc = proc_packets(NULL, fp);
       iobuf_close(fp);
       if (rc)
-        log_error("%s: decryption failed: %s\n", print_fname_stdin(*files),
-                  gpg_strerror (rc));
+        log_error("%s: decryption failed: %s\n", print_fname_stdin(filename),
+                  g10_errstr(rc));
       p = get_last_passphrase();
       set_next_passphrase(p);
       xfree (p);
@@ -134,9 +185,8 @@ decrypt_messages(int nfiles, char **files)
     next_file:
       /* Note that we emit file_done even after an error. */
       write_status( STATUS_FILE_DONE );
-      xfree (output);
-      files++;
+      xfree(output);
     }
+
   set_next_passphrase(NULL);  
 }
-
index 6263dec..bb81087 100644 (file)
@@ -1,5 +1,6 @@
 /* delkey.c - delete keys
- * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004,
+ *               2005, 2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include <assert.h>
 #include <ctype.h>
 
+#include "gpg.h"
 #include "options.h"
 #include "packet.h"
 #include "errors.h"
 #include "iobuf.h"
 #include "keydb.h"
-#include "memory.h"
 #include "util.h"
 #include "main.h"
 #include "trustdb.h"
@@ -47,7 +49,7 @@
  * key can't be deleted for that reason.
  */
 static int
-do_delete_key( const char *username, int secret, int *r_sec_avail )
+do_delete_key( const char *username, int secret, int force, int *r_sec_avail )
 {
     int rc = 0;
     KBNODE keyblock = NULL;
@@ -68,9 +70,9 @@ do_delete_key( const char *username, int secret, int *r_sec_avail )
     exactmatch = (desc.mode == KEYDB_SEARCH_MODE_FPR
                   || desc.mode == KEYDB_SEARCH_MODE_FPR16
                   || desc.mode == KEYDB_SEARCH_MODE_FPR20);
-    rc = desc.mode? keydb_search (hd, &desc, 1):GPG_ERR_INV_USER_ID;
+    rc = desc.mode? keydb_search (hd, &desc, 1):G10ERR_INV_USER_ID;
     if (rc) {
-       log_error (_("key `%s' not found: %s\n"), username, gpg_strerror (rc));
+       log_error (_("key \"%s\" not found: %s\n"), username, g10_errstr (rc));
        write_status_text( STATUS_DELETE_PROBLEM, "1" );
        goto leave;
     }
@@ -78,7 +80,7 @@ do_delete_key( const char *username, int secret, int *r_sec_avail )
     /* read the keyblock */
     rc = keydb_get_keyblock (hd, &keyblock );
     if (rc) {
-       log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) );
+       log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
        goto leave;
     }
 
@@ -86,29 +88,36 @@ do_delete_key( const char *username, int secret, int *r_sec_avail )
     node = find_kbnode( keyblock, secret? PKT_SECRET_KEY:PKT_PUBLIC_KEY );
     if( !node ) {
        log_error("Oops; key not found anymore!\n");
-       rc = GPG_ERR_GENERAL;
+       rc = G10ERR_GENERAL;
        goto leave;
     }
 
-    if( secret ) {
+    if( secret )
+      {
        sk = node->pkt->pkt.secret_key;
        keyid_from_sk( sk, keyid );
-    }
-    else {
+      }
+    else
+      {
+       /* public */
        pk = node->pkt->pkt.public_key;
        keyid_from_pk( pk, keyid );
-       rc = seckey_available( keyid );
-       if( !rc ) {
-            *r_sec_avail = 1;
-            rc = -1;
-            goto leave;
-       }
-       else if( rc != GPG_ERR_NO_SECKEY ) {
-           log_error("%s: get secret key: %s\n", username, gpg_strerror (rc) );
-       }
-       else
-           rc = 0;
-    }
+
+       if(!force)
+         {
+           rc = seckey_available( keyid );
+           if( !rc )
+             {
+               *r_sec_avail = 1;
+               rc = -1;
+               goto leave;
+             }
+           else if( rc != G10ERR_NO_SECKEY )
+             log_error("%s: get secret key: %s\n", username, g10_errstr(rc) );
+           else
+             rc = 0;
+         }
+      }
 
     if( rc )
        rc = 0;
@@ -116,26 +125,26 @@ do_delete_key( const char *username, int secret, int *r_sec_avail )
         okay++;
     else if( opt.batch && secret )
       {
-       log_error(_("can't do that in batchmode\n"));
+       log_error(_("can't do this in batch mode\n"));
         log_info (_("(unless you specify the key by fingerprint)\n"));
       }
     else if( opt.batch && opt.answer_yes )
        okay++;
     else if( opt.batch )
       {
-       log_error(_("can't do that in batchmode without \"--yes\"\n"));
+       log_error(_("can't do this in batch mode without \"--yes\"\n"));
         log_info (_("(unless you specify the key by fingerprint)\n"));
       }
     else {
         if( secret )
             print_seckey_info( sk );
         else
-            print_pubkey_info (NULL, pk );
+            print_pubkey_info(NULL, pk );
        tty_printf( "\n" );
 
        yes = cpr_get_answer_is_yes( secret? "delete_key.secret.okay"
                                           : "delete_key.okay",
-                             _("Delete this key from the keyring? "));
+                             _("Delete this key from the keyring? (y/N) "));
        if( !cpr_enabled() && secret && yes ) {
            /* I think it is not required to check a passphrase; if
             * the user is so stupid as to let others access his secret keyring
@@ -143,7 +152,7 @@ do_delete_key( const char *username, int secret, int *r_sec_avail )
             * basic texts about security.
             */
            yes = cpr_get_answer_is_yes("delete_key.secret.okay",
-                        _("This is a secret key! - really delete? "));
+                        _("This is a secret key! - really delete? (y/N) "));
        }
        if( yes )
            okay++;
@@ -153,7 +162,7 @@ do_delete_key( const char *username, int secret, int *r_sec_avail )
     if( okay ) {
        rc = keydb_delete_keyblock (hd);
        if (rc) {
-           log_error (_("deleting keyblock failed: %s\n"), gpg_strerror (rc) );
+           log_error (_("deleting keyblock failed: %s\n"), g10_errstr(rc) );
            goto leave;
        }
 
@@ -179,15 +188,18 @@ do_delete_key( const char *username, int secret, int *r_sec_avail )
 int
 delete_keys( STRLIST names, int secret, int allow_both )
 {
-    int rc, avail;
+    int rc, avail, force=(!allow_both && !secret && opt.expert);
+
+    /* Force allows us to delete a public key even if a secret key
+       exists. */
 
     for(;names;names=names->next) {
-       rc = do_delete_key (names->d, secret, &avail );
+       rc = do_delete_key (names->d, secret, force, &avail );
        if ( rc && avail ) { 
         if ( allow_both ) {
-          rc = do_delete_key (names->d, 1, &avail );
+          rc = do_delete_key (names->d, 1, 0, &avail );
           if ( !rc )
-            rc = do_delete_key (names->d, 0, &avail );
+            rc = do_delete_key (names->d, 0, 0, &avail );
         }
         else {
           log_error(_(
@@ -200,7 +212,7 @@ delete_keys( STRLIST names, int secret, int allow_both )
        }
 
        if(rc) {
-        log_error("%s: delete key failed: %s\n", names->d, gpg_strerror (rc) );
+        log_error("%s: delete key failed: %s\n", names->d, g10_errstr(rc) );
         return rc;
        }
     }
index 7794bdb..57f2272 100644 (file)
@@ -1,6 +1,6 @@
 /* encode.c - encode data
- * Copyright (C) 1998, 1999, 2000, 2001, 2002,
- *               2003 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ *               2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -16,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
@@ -32,7 +33,6 @@
 #include "errors.h"
 #include "iobuf.h"
 #include "keydb.h"
-#include "memory.h"
 #include "util.h"
 #include "main.h"
 #include "filter.h"
 #include "pkglue.h"
 
 
-static int encode_simple( const char *filename, int mode, int compat );
-static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out );
-
-
+static int encode_simple( const char *filename, int mode, int use_seskey );
+static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out );
 
 /****************
  * Encode FILENAME with only the symmetric cipher.  Take input from
@@ -54,17 +52,7 @@ static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out );
 int
 encode_symmetric( const char *filename )
 {
-    int compat = 1;
-
-#if 0    
-    /* We don't want to use it because older gnupg version can't
-       handle it and we can presume that a lot of scripts are running
-       with the expert mode set.  Some time in the future we might
-       want to allow for it. */
-    if ( opt.expert )
-        compat = 0; /* PGP knows how to handle this mode. */
-#endif
-    return encode_simple( filename, 1, compat );
+    return encode_simple( filename, 1, 0 );
 }
 
 /****************
@@ -74,69 +62,62 @@ encode_symmetric( const char *filename )
 int
 encode_store( const char *filename )
 {
-    return encode_simple( filename, 0, 1 );
+    return encode_simple( filename, 0, 0 );
 }
 
+
 static void
-encode_sesskey (DEK * dek, DEK ** ret_dek, byte * enckey)
+encode_seskey( DEK *dek, DEK **seskey, byte *enckey )
 {
-  CIPHER_HANDLE hd;
-  DEK * c;
-  byte buf[33];
+    gcry_cipher_hd_t hd;
+    byte buf[33];
 
-  assert (dek->keylen < 32);
-    
-  c = xcalloc (1, sizeof *c);
-  c->keylen = dek->keylen;
-  c->algo = dek->algo;
-  make_session_key (c);
-  /*log_hexdump ("thekey", c->key, c->keylen);*/
-
-  /* the encrypted session key is prefixed with a one-octet algorithm id */
-  buf[0] = c->algo;
-  memcpy (buf + 1, c->key, c->keylen);
+    assert ( dek->keylen <= 32 );
+    if(!*seskey)
+      {
+       *seskey=xmalloc_clear(sizeof(DEK));
+       (*seskey)->keylen=dek->keylen;
+       (*seskey)->algo=dek->algo;
+       make_session_key(*seskey);
+       /*log_hexdump( "thekey", c->key, c->keylen );*/
+      }
+
+    /* The encrypted session key is prefixed with a one-octet algorithm id.  */
+    buf[0] = (*seskey)->algo;
+    memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen );
     
-  /* due to the fact that we use only checked values, consider each
-     failure as fatal. */
-  if (gcry_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1))
-    BUG();
-  if (gcry_cipher_setkey (hd, dek->key, dek->keylen))
-    BUG();
-  gcry_cipher_setiv (hd, NULL, 0);
-  gcry_cipher_encrypt (hd, buf, c->keylen + 1, NULL, 0);
-  gcry_cipher_close (hd);
-
-  memcpy (enckey, buf, c->keylen + 1);
-  wipememory (buf, sizeof buf); /* burn key */
-  *ret_dek = c;
+    /* We only pass already checked values to the following fucntion,
+       thus we consider any failure as fatal.  */
+    if (gcry_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1))
+      BUG ();
+    if (gcry_cipher_setkey (hd, dek->key, dek->keylen))
+      BUG ();
+    gry_cipher_setiv (hd, NULL, 0);
+    gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0);
+    gcry_cipher_close (hd);
+
+    memcpy( enckey, buf, (*seskey)->keylen + 1 );
+    wipememory( buf, sizeof buf ); /* burn key */
 }
 
 /* We try very hard to use a MDC */
 static int
-use_mdc (PK_LIST pk_list,int algo)
+use_mdc(PK_LIST pk_list,int algo)
 {
-  byte cipher_algid[4] = {
-    CIPHER_ALGO_AES,
-    CIPHER_ALGO_AES192,
-    CIPHER_ALGO_AES256,
-    CIPHER_ALGO_TWOFISH
-  };
-  int i;
-
   /* RFC-1991 and 2440 don't have MDC */
   if(RFC1991 || RFC2440)
     return 0;
-  
+
   /* --force-mdc overrides --disable-mdc */
-  if (opt.force_mdc)
+  if(opt.force_mdc)
     return 1;
 
-  if (opt.disable_mdc)
+  if(opt.disable_mdc)
     return 0;
 
   /* Do the keys really support MDC? */
 
-  if (select_mdc_from_pklist (pk_list))
+  if(select_mdc_from_pklist(pk_list))
     return 1;
   
   /* The keys don't support MDC, so now we do a bit of a hack - if any
@@ -144,26 +125,40 @@ use_mdc (PK_LIST pk_list,int algo)
      can handle a MDC.  This is valid for PGP 7, which can handle MDCs
      though it will not generate them.  2440bis allows this, by the
      way. */
-  for (i=0; i < DIM (cipher_algid); i++)
-    {
-      if (select_algo_from_prefs (pk_list, PREFTYPE_SYM, cipher_algid[i],
-                                  NULL) == cipher_algid[i])
-        return 1;
-    }
+
+  if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
+                           CIPHER_ALGO_AES,NULL)==CIPHER_ALGO_AES)
+    return 1;
+
+  if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
+                           CIPHER_ALGO_AES192,NULL)==CIPHER_ALGO_AES192)
+    return 1;
+
+  if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
+                           CIPHER_ALGO_AES256,NULL)==CIPHER_ALGO_AES256)
+    return 1;
+
+  if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
+                           CIPHER_ALGO_TWOFISH,NULL)==CIPHER_ALGO_TWOFISH)
+    return 1;
 
   /* Last try.  Use MDC for the modern ciphers. */
+
   if (gcry_cipher_get_algo_blklen (algo) != 8)
     return 1;
 
   return 0; /* No MDC */
 }
 
+/* We don't want to use use_seskey yet because older gnupg versions
+   can't handle it, and there isn't really any point unless we're
+   making a message that can be decrypted by a public key or
+   passphrase. */
 static int
-encode_simple( const char *filename, int mode, int compat )
+encode_simple( const char *filename, int mode, int use_seskey )
 {
-    iobuf_t inp, out;
+    IOBUF inp, out;
     PACKET pkt;
-    DEK *dek = NULL;
     PKT_plaintext *pt = NULL;
     STRING2KEY *s2k = NULL;
     byte enckey[33];
@@ -175,7 +170,7 @@ encode_simple( const char *filename, int mode, int compat )
     compress_filter_context_t zfx;
     text_filter_context_t tfx;
     progress_filter_context_t pfx;
-    int do_compress = opt.compress && !RFC1991;
+    int do_compress = !RFC1991 && default_compress_algo();
 
     memset( &cfx, 0, sizeof cfx);
     memset( &afx, 0, sizeof afx);
@@ -184,10 +179,19 @@ encode_simple( const char *filename, int mode, int compat )
     init_packet(&pkt);
     
     /* prepare iobufs */
-    if( !(inp = iobuf_open(filename)) ) {
+    inp = iobuf_open(filename);
+    if (inp)
+      iobuf_ioctl (inp,3,1,NULL); /* disable fd caching */
+    if (inp && is_secured_file (iobuf_get_fd (inp)))
+      {
+        iobuf_close (inp);
+        inp = NULL;
+        errno = EPERM;
+      }
+    if( !inp ) {
         rc = gpg_error_from_errno (errno);
-       log_error(_("%s: can't open: %s\n"), filename? filename: "[stdin]",
-                                       strerror(errno) );
+       log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]"
+                  strerror(errno) );
        return rc;
     }
 
@@ -199,42 +203,49 @@ encode_simple( const char *filename, int mode, int compat )
     /* Due the the fact that we use don't use an IV to encrypt the
        session key we can't use the new mode with RFC1991 because
        it has no S2K salt. RFC1991 always uses simple S2K. */
-    if ( RFC1991 && !compat )
-        compat = 1;
+    if ( RFC1991 && use_seskey )
+        use_seskey = 0;
     
     cfx.dek = NULL;
     if( mode ) {
-       s2k = xcalloc (1, sizeof *s2k );
+       s2k = xmalloc_clear( sizeof *s2k );
        s2k->mode = RFC1991? 0:opt.s2k_mode;
-       s2k->hash_algo = opt.s2k_digest_algo;
+       s2k->hash_algo=S2K_DIGEST_ALGO;
        cfx.dek = passphrase_to_dek( NULL, 0,
                                     default_cipher_algo(), s2k, 2,
                                      NULL, NULL);
        if( !cfx.dek || !cfx.dek->keylen ) {
-            rc = gpg_error (GPG_ERR_INV_PASSPHRASE);
-           xfree (cfx.dek);
-           xfree (s2k);
+           rc = gpg_error (GPG_ERR_INV_PASSPHRASE);
+           xfree(cfx.dek);
+           xfree(s2k);
            iobuf_close(inp);
-           log_error(_("error creating passphrase: %s\n"), gpg_strerror (rc) );
+           log_error(_("error creating passphrase: %s\n"), gpg_strerror (rc));
            return rc;
        }
-        if (!compat && s2k->mode != 1 && s2k->mode != 3) {
-            compat = 1;
+        if (use_seskey && s2k->mode != 1 && s2k->mode != 3) {
+            use_seskey = 0;
             log_info (_("can't use a symmetric ESK packet "
                         "due to the S2K mode\n"));
         }
 
-        if ( !compat ) {            
-            seskeylen = gcry_cipher_get_algo_keylen (default_cipher_algo());
-            encode_sesskey( cfx.dek, &dek, enckey );
-            xfree (cfx.dek); cfx.dek = dek;
-        }
+        if ( use_seskey )
+         {
+           DEK *dek = NULL;
+
+            seskeylen = gcry_cipher_get_algo_keylen (default_cipher_algo ());
+            encode_seskey( cfx.dek, &dek, enckey );
+            xfree( cfx.dek ); cfx.dek = dek;
+         }
+
+       if(opt.verbose)
+         log_info(_("using cipher %s\n"),
+                  gcry_cipher_algo_name (cfx.dek->algo));
 
        cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo);
     }
 
-    if (opt.compress == -1 && cfx.dek && cfx.dek->use_mdc &&
-       is_file_compressed(filename, &rc))
+    if (do_compress && cfx.dek && cfx.dek->use_mdc
+       && is_file_compressed(filename, &rc))
       {
         if (opt.verbose)
           log_info(_("`%s' already compressed\n"), filename);
@@ -243,52 +254,43 @@ encode_simple( const char *filename, int mode, int compat )
 
     if( rc || (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) {
        iobuf_cancel(inp);
-       xfree (cfx.dek);
-       xfree (s2k);
+       xfree(cfx.dek);
+       xfree(s2k);
        return rc;
     }
 
     if( opt.armor )
        iobuf_push_filter( out, armor_filter, &afx );
-#ifdef ENABLE_COMMENT_PACKETS
-    else {
-       write_comment( out, "#created by GNUPG v" VERSION " ("
-                                           PRINTABLE_OS_NAME ")");
-       if( opt.comment_string )
-           write_comment( out, opt.comment_string );
-    }
-#endif
+
     if( s2k && !RFC1991 ) {
-       PKT_symkey_enc *enc = xcalloc (1, sizeof *enc + seskeylen + 1 );
+       PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 );
        enc->version = 4;
        enc->cipher_algo = cfx.dek->algo;
        enc->s2k = *s2k;
-        if ( !compat && seskeylen ) {
+        if ( use_seskey && seskeylen ) {
             enc->seskeylen = seskeylen + 1; /* algo id */
             memcpy( enc->seskey, enckey, seskeylen + 1 );
         }
        pkt.pkttype = PKT_SYMKEY_ENC;
        pkt.pkt.symkey_enc = enc;
        if( (rc = build_packet( out, &pkt )) )
-           log_error("build symkey packet failed: %s\n", gpg_strerror (rc) );
-       xfree (enc);
+           log_error("build symkey packet failed: %s\n", g10_errstr(rc) );
+       xfree(enc);
     }
 
     if (!opt.no_literal) {
        /* setup the inner packet */
        if( filename || opt.set_filename ) {
-           char *s = make_basename ( opt.set_filename ? opt.set_filename
-                                                     : filename
-                                      /* for riscos?
-                                         .iobuf_get_real_fname( inp ) */
-                                      );
-           pt = xmalloc ( sizeof *pt + strlen(s) - 1 );
+           char *s = make_basename( opt.set_filename ? opt.set_filename
+                                                     : filename,
+                                    iobuf_get_real_fname( inp ) );
+           pt = xmalloc( sizeof *pt + strlen(s) - 1 );
            pt->namelen = strlen(s);
            memcpy(pt->name, s, pt->namelen );
-           xfree (s);
+           xfree(s);
        }
        else { /* no filename */
-           pt = xmalloc ( sizeof *pt - 1 );
+           pt = xmalloc( sizeof *pt - 1 );
            pt->namelen = 0;
        }
     }
@@ -304,12 +306,14 @@ encode_simple( const char *filename, int mode, int compat )
        either partial length or fixed length with the new style
        messages. */
 
-    if (filename && *filename && !(*filename == '-' && !filename[1])
-        && !opt.textmode ) {
+    if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
+      {
         off_t tmpsize;
+        int overflow;
 
-       if ( !(tmpsize = iobuf_get_filelength(inp)) )
-          log_info(_("%s: WARNING: empty file\n"), filename );
+       if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
+             && !overflow )
+          log_info(_("WARNING: `%s' is an empty file\n"), filename );
         /* We can't encode the length of very large files because
            OpenPGP uses only 32 bit for file sizes.  So if the the
            size of a file is larger than 2^32 minus some bytes for
@@ -318,9 +322,9 @@ encode_simple( const char *filename, int mode, int compat )
           filesize = tmpsize;
         else
           filesize = 0;
-    }
+      }
     else
-       filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
+      filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
 
     if (!opt.no_literal) {
        pt->timestamp = make_timestamp();
@@ -347,14 +351,13 @@ encode_simple( const char *filename, int mode, int compat )
       {
         if (cfx.dek && cfx.dek->use_mdc)
           zfx.new_ctb = 1;
-       zfx.algo=default_compress_algo();
-       iobuf_push_filter( out, compress_filter, &zfx );
+       push_compress_filter(out,&zfx,default_compress_algo());
       }
 
     /* do the work */
     if (!opt.no_literal) {
        if( (rc = build_packet( out, &pkt )) )
-           log_error("build_packet failed: %s\n", gpg_strerror (rc) );
+           log_error("build_packet failed: %s\n", g10_errstr(rc) );
     }
     else {
        /* user requested not to create a literal packet,
@@ -362,8 +365,9 @@ encode_simple( const char *filename, int mode, int compat )
        byte copy_buffer[4096];
        int  bytes_copied;
        while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
-           if ( (rc=iobuf_write(out, copy_buffer, bytes_copied))) {
-               log_error("copying input to output failed: %s\n", gpg_strerror (rc) );
+           if (iobuf_write(out, copy_buffer, bytes_copied) == -1) {
+               rc = G10ERR_WRITE_FILE;
+               log_error("copying input to output failed: %s\n", g10_errstr(rc) );
                break;
            }
        wipememory(copy_buffer, 4096); /* burn buffer */
@@ -381,21 +385,70 @@ encode_simple( const char *filename, int mode, int compat )
     if (pt)
        pt->buf = NULL;
     free_packet(&pkt);
-    xfree (cfx.dek);
-    xfree (s2k);
+    xfree(cfx.dek);
+    xfree(s2k);
     return rc;
 }
 
+int
+setup_symkey(STRING2KEY **symkey_s2k,DEK **symkey_dek)
+{
+  *symkey_s2k=xmalloc_clear(sizeof(STRING2KEY));
+  (*symkey_s2k)->mode = opt.s2k_mode;
+  (*symkey_s2k)->hash_algo = S2K_DIGEST_ALGO;
+
+  *symkey_dek=passphrase_to_dek(NULL,0,opt.s2k_cipher_algo,
+                               *symkey_s2k,2,NULL,NULL);
+  if(!*symkey_dek || !(*symkey_dek)->keylen)
+    {
+      xfree(*symkey_dek);
+      xfree(*symkey_s2k);
+      return G10ERR_PASSPHRASE;
+    }
+
+  return 0;
+}
+
+static int
+write_symkey_enc(STRING2KEY *symkey_s2k,DEK *symkey_dek,DEK *dek,IOBUF out)
+{
+  int rc,seskeylen=cipher_get_keylen(dek->algo)/8;
+
+  PKT_symkey_enc *enc;
+  byte enckey[33];
+  PACKET pkt;
+
+  enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1);
+  encode_seskey(symkey_dek,&dek,enckey);
+
+  enc->version = 4;
+  enc->cipher_algo = opt.s2k_cipher_algo;
+  enc->s2k = *symkey_s2k;
+  enc->seskeylen = seskeylen + 1; /* algo id */
+  memcpy( enc->seskey, enckey, seskeylen + 1 );
+
+  pkt.pkttype = PKT_SYMKEY_ENC;
+  pkt.pkt.symkey_enc = enc;
+
+  if((rc=build_packet(out,&pkt)))
+    log_error("build symkey_enc packet failed: %s\n",g10_errstr(rc));
+
+  xfree(enc);
+  return rc;
+}
+
 /****************
  * Encrypt the file with the given userids (or ask if none
  * is supplied).
  */
 int
-encode_crypt( const char *filename, STRLIST remusr )
+encode_crypt( const char *filename, STRLIST remusr, int use_symkey )
 {
-    iobuf_t inp = NULL, out = NULL;
+    IOBUF inp = NULL, out = NULL;
     PACKET pkt;
     PKT_plaintext *pt = NULL;
+    DEK *symkey_dek = NULL;
+    STRING2KEY *symkey_s2k = NULL;
     int rc = 0, rc2 = 0;
     u32 filesize;
     cipher_filter_context_t cfx;
@@ -404,8 +457,7 @@ encode_crypt( const char *filename, STRLIST remusr )
     text_filter_context_t tfx;
     progress_filter_context_t pfx;
     PK_LIST pk_list,work_list;
-    int do_compress = opt.compress && !RFC1991;
-
+    int do_compress = opt.compress_algo && !RFC1991;
 
     memset( &cfx, 0, sizeof cfx);
     memset( &afx, 0, sizeof afx);
@@ -413,6 +465,10 @@ encode_crypt( const char *filename, STRLIST remusr )
     memset( &tfx, 0, sizeof tfx);
     init_packet(&pkt);
 
+    if(use_symkey
+       && (rc=setup_symkey(&symkey_s2k,&symkey_dek)))
+      return rc;
+
     if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC)) )
        return rc;
 
@@ -429,10 +485,20 @@ encode_crypt( const char *filename, STRLIST remusr )
     }
 
     /* prepare iobufs */
-    if( !(inp = iobuf_open(filename)) ) {
+    inp = iobuf_open(filename);
+    if (inp)
+      iobuf_ioctl (inp,3,1,NULL); /* disable fd caching */
+    if (inp && is_secured_file (iobuf_get_fd (inp)))
+      {
+        iobuf_close (inp);
+        inp = NULL;
+        errno = EPERM;
+      }
+    if( !inp ) {
         rc = gpg_error_from_errno (errno);
-       log_error(_("can't open %s: %s\n"), filename? filename: "[stdin]",
-                                       strerror(errno) );
+       log_error(_("can't open `%s': %s\n"),
+                  filename? filename: "[stdin]",
+                  gpg_strerror (rc) );
        goto leave;
     }
     else if( opt.verbose )
@@ -446,19 +512,11 @@ encode_crypt( const char *filename, STRLIST remusr )
     if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) )
        goto leave;
 
-
     if( opt.armor )
        iobuf_push_filter( out, armor_filter, &afx );
-#ifdef ENABLE_COMMENT_PACKETS
-    else {
-       write_comment( out, "#created by GNUPG v" VERSION " ("
-                                           PRINTABLE_OS_NAME ")");
-       if( opt.comment_string )
-           write_comment( out, opt.comment_string );
-    }
-#endif
+
     /* create a session key */
-    cfx.dek = xcalloc_secure (1, sizeof *cfx.dek);
+    cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek);
     if( !opt.def_cipher_algo ) { /* try to get it from the prefs */
        cfx.dek->algo = select_algo_from_prefs(pk_list,PREFTYPE_SYM,-1,NULL);
        /* The only way select_algo_from_prefs can fail here is when
@@ -482,8 +540,8 @@ encode_crypt( const char *filename, STRLIST remusr )
       if(!opt.expert &&
         select_algo_from_prefs(pk_list,PREFTYPE_SYM,
                                opt.def_cipher_algo,NULL)!=opt.def_cipher_algo)
-       log_info(_("forcing symmetric cipher %s (%d) "
-                  "violates recipient preferences\n"),
+       log_info(_("WARNING: forcing symmetric cipher %s (%d)"
+                  " violates recipient preferences\n"),
                 gcry_cipher_algo_name (opt.def_cipher_algo),
                 opt.def_cipher_algo);
 
@@ -497,8 +555,7 @@ encode_crypt( const char *filename, STRLIST remusr )
        not have a MDC to give some protection against chosen
        ciphertext attacks. */
 
-    if (opt.compress == -1 && cfx.dek->use_mdc &&
-       is_file_compressed(filename, &rc2) )
+    if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2) )
       {
         if (opt.verbose)
           log_info(_("`%s' already compressed\n"), filename);
@@ -518,40 +575,49 @@ encode_crypt( const char *filename, STRLIST remusr )
     if( rc  )
        goto leave;
 
+    /* We put the passphrase (if any) after any public keys as this
+       seems to be the most useful on the recipient side - there is no
+       point in prompting a user for a passphrase if they have the
+       secret key needed to decrypt. */
+    if(use_symkey && (rc=write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out)))
+      goto leave;
+
     if (!opt.no_literal) {
        /* setup the inner packet */
        if( filename || opt.set_filename ) {
            char *s = make_basename( opt.set_filename ? opt.set_filename
-                                                     : filename
-                                    /* ,iobuf_get_real_fname( inp )*/ );
-           pt = xmalloc ( sizeof *pt + strlen(s) - 1 );
+                                                     : filename,
+                                    iobuf_get_real_fname( inp ) );
+           pt = xmalloc( sizeof *pt + strlen(s) - 1 );
            pt->namelen = strlen(s);
            memcpy(pt->name, s, pt->namelen );
-           xfree (s);
+           xfree(s);
        }
        else { /* no filename */
-           pt = xmalloc ( sizeof *pt - 1 );
+           pt = xmalloc( sizeof *pt - 1 );
            pt->namelen = 0;
        }
     }
 
-    if (filename && *filename && !(*filename == '-' && !filename[1])
-        && !opt.textmode ) {
+    if (!iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
+      {
         off_t tmpsize;
+        int overflow;
 
-       if ( !(tmpsize = iobuf_get_filelength(inp)) )
-          log_info(_("%s: WARNING: empty file\n"), filename );
+       if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
+             && !overflow )
+          log_info(_("WARNING: `%s' is an empty file\n"), filename );
         /* We can't encode the length of very large files because
            OpenPGP uses only 32 bit for file sizes.  So if the the
            size of a file is larger than 2^32 minus some bytes for
            packet headers, we switch to partial length encoding. */
-        if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
+        if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
           filesize = tmpsize;
         else
           filesize = 0;
-    }
+      }
     else
-       filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
+      filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
 
     if (!opt.no_literal) {
        pt->timestamp = make_timestamp();
@@ -571,7 +637,7 @@ encode_crypt( const char *filename, STRLIST remusr )
 
     /* register the compress filter */
     if( do_compress ) {
-       int compr_algo = opt.def_compress_algo;
+       int compr_algo = opt.compress_algo;
 
        if(compr_algo==-1)
          {
@@ -584,8 +650,8 @@ encode_crypt( const char *filename, STRLIST remusr )
        else if(!opt.expert &&
                select_algo_from_prefs(pk_list,PREFTYPE_ZIP,
                                       compr_algo,NULL)!=compr_algo)
-         log_info(_("forcing compression algorithm %s (%d) "
-                    "violates recipient preferences\n"),
+         log_info(_("WARNING: forcing compression algorithm %s (%d)"
+                    " violates recipient preferences\n"),
                   compress_algo_to_string(compr_algo),compr_algo);
 
        /* algo 0 means no compression */
@@ -593,15 +659,14 @@ encode_crypt( const char *filename, STRLIST remusr )
          {
             if (cfx.dek && cfx.dek->use_mdc)
               zfx.new_ctb = 1;
-           zfx.algo = compr_algo;
-           iobuf_push_filter( out, compress_filter, &zfx );
+           push_compress_filter(out,&zfx,compr_algo);
          }
     }
 
     /* do the work */
     if (!opt.no_literal) {
        if( (rc = build_packet( out, &pkt )) )
-           log_error("build_packet failed: %s\n", gpg_strerror (rc) );
+           log_error("build_packet failed: %s\n", g10_errstr(rc) );
     }
     else {
        /* user requested not to create a literal packet, so we copy
@@ -609,9 +674,10 @@ encode_crypt( const char *filename, STRLIST remusr )
        byte copy_buffer[4096];
        int  bytes_copied;
        while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
-           if ((rc=iobuf_write(out, copy_buffer, bytes_copied))) {
+           if (iobuf_write(out, copy_buffer, bytes_copied) == -1) {
+               rc = G10ERR_WRITE_FILE;
                log_error("copying input to output failed: %s\n",
-                          gpg_strerror (rc) );
+                          g10_errstr(rc) );
                break;
            }
        wipememory(copy_buffer, 4096); /* burn buffer */
@@ -629,7 +695,9 @@ encode_crypt( const char *filename, STRLIST remusr )
     if( pt )
        pt->buf = NULL;
     free_packet(&pkt);
-    xfree (cfx.dek);
+    xfree(cfx.dek);
+    xfree(symkey_dek);
+    xfree(symkey_s2k);
     release_pk_list( pk_list );
     return rc;
 }
@@ -642,7 +710,7 @@ encode_crypt( const char *filename, STRLIST remusr )
  */
 int
 encrypt_filter( void *opaque, int control,
-              iobuf_t a, byte *buf, size_t *ret_len)
+              IOBUF a, byte *buf, size_t *ret_len)
 {
     size_t size = *ret_len;
     encrypt_filter_context_t *efx = opaque;
@@ -653,7 +721,7 @@ encrypt_filter( void *opaque, int control,
     }
     else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */
        if( !efx->header_okay ) {
-           efx->cfx.dek = xcalloc_secure (1, sizeof *efx->cfx.dek );
+           efx->cfx.dek = xmalloc_secure_clear( sizeof *efx->cfx.dek );
 
            if( !opt.def_cipher_algo  ) { /* try to get it from the prefs */
                efx->cfx.dek->algo =
@@ -688,6 +756,14 @@ encrypt_filter( void *opaque, int control,
            if( rc )
                return rc;
 
+           if(efx->symkey_s2k && efx->symkey_dek)
+             {
+               rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek,
+                                   efx->cfx.dek,a);
+               if(rc)
+                 return rc;
+             }
+
            iobuf_push_filter( a, cipher_filter, &efx->cfx );
 
            efx->header_okay = 1;
@@ -695,8 +771,11 @@ encrypt_filter( void *opaque, int control,
        rc = iobuf_write( a, buf, size );
 
     }
-    else if( control == IOBUFCTRL_FREE ) {
-    }
+    else if( control == IOBUFCTRL_FREE )
+      {
+       xfree(efx->symkey_dek);
+       xfree(efx->symkey_s2k);
+      }
     else if( control == IOBUFCTRL_DESC ) {
        *(char**)buf = "encrypt_filter";
     }
@@ -708,7 +787,7 @@ encrypt_filter( void *opaque, int control,
  * Write pubkey-enc packets from the list of PKs to OUT.
  */
 static int
-write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out )
+write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out )
 {
     PACKET pkt;
     PKT_public_key *pk;
@@ -716,12 +795,12 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out )
     int rc;
 
     for( ; pk_list; pk_list = pk_list->next ) {
-       gcry_mpi_t frame;
+       MPI frame;
 
        pk = pk_list->pk;
 
        print_pubkey_algo_note( pk->pubkey_algo );
-       enc = xcalloc (1, sizeof *enc );
+       enc = xmalloc_clear( sizeof *enc );
        enc->pubkey_algo = pk->pubkey_algo;
        keyid_from_pk( pk, enc->keyid );
        enc->throw_keyid = (opt.throw_keyid || (pk_list->flags&1));
@@ -742,23 +821,24 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out )
         * algorithm number PK->PUBKEY_ALGO and pass it to pubkey_encrypt
         * which returns the encrypted value in the array ENC->DATA.
         * This array has a size which depends on the used algorithm
-        * (e.g. 2 for ElGamal).  We don't need frame anymore because we
+        * (e.g. 2 for Elgamal).  We don't need frame anymore because we
         * have everything now in enc->data which is the passed to
         * build_packet()
         */
-       frame = encode_session_key( dek, pubkey_nbits( pk->pubkey_algo,
-                                                         pk->pkey ) );
-       rc = pk_encrypt( pk->pubkey_algo, enc->data, frame, pk->pkey );
-       gcry_mpi_release ( frame );
+       frame = encode_session_key (dek, pubkey_nbits (pk->pubkey_algo,
+                                                       pk->pkey) );
+       rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk->pkey);
+       gcry_mpi_release (frame);
        if( rc )
-           log_error("pubkey_encrypt failed: %s\n", gpg_strerror (rc) );
+           log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) );
        else {
            if( opt.verbose ) {
-               char *ustr = get_user_id_string_printable (enc->keyid);
+               char *ustr = get_user_id_string_native (enc->keyid);
                log_info(_("%s/%s encrypted for: \"%s\"\n"),
-                   gcry_pk_algo_name (enc->pubkey_algo),
-                   gcry_cipher_algo_name (dek->algo), ustr );
-               xfree (ustr);
+                         gcry_pk_algo_name (enc->pubkey_algo),
+                         gcry_cipher_algo_name (dek->algo),
+                         ustr );
+               xfree(ustr);
            }
            /* and write it */
            init_packet(&pkt);
@@ -766,7 +846,7 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out )
            pkt.pkt.pubkey_enc = enc;
            rc = build_packet( out, &pkt );
            if( rc )
-              log_error("build_packet(pubkey_enc) failed: %s\n", gpg_strerror (rc));
+              log_error("build_packet(pubkey_enc) failed: %s\n", g10_errstr(rc));
        }
        free_pubkey_enc(enc);
        if( rc )
@@ -800,9 +880,9 @@ encode_crypt_files(int nfiles, char **files, STRLIST remusr)
             }
           line[strlen(line)-1] = '\0';
           print_file_status(STATUS_FILE_START, line, 2);
-          if ( (rc = encode_crypt(line, remusr)) )
-            log_error("%s: encryption failed: %s\n",
-                      print_fname_stdin(line), gpg_strerror (rc) );
+          if ( (rc = encode_crypt(line, remusr, 0)) )
+            log_error("encryption of `%s' failed: %s\n",
+                      print_fname_stdin(line), g10_errstr(rc) );
           write_status( STATUS_FILE_DONE );
         }
     }
@@ -811,9 +891,9 @@ encode_crypt_files(int nfiles, char **files, STRLIST remusr)
       while (nfiles--)
         {
           print_file_status(STATUS_FILE_START, *files, 2);
-          if ( (rc = encode_crypt(*files, remusr)) )
-            log_error("%s: encryption failed: %s\n",
-                      print_fname_stdin(*files), gpg_strerror (rc) );
+          if ( (rc = encode_crypt(*files, remusr, 0)) )
+            log_error("encryption of `%s' failed: %s\n",
+                      print_fname_stdin(*files), g10_errstr(rc) );
           write_status( STATUS_FILE_DONE );
           files++;
         }
index 0744084..cf2e43d 100644 (file)
@@ -1,5 +1,6 @@
 /* encr-data.c -  process an encrypted data packet
- * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2005,
+ *               2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 
 #include "gpg.h"
 #include "util.h"
-#include "memory.h"
 #include "packet.h"
-#include "mpi.h"
 #include "cipher.h"
 #include "options.h"
 #include "i18n.h"
 
 
-static int mdc_decode_filter( void *opaque, int control, iobuf_t a,
+static int mdc_decode_filter( void *opaque, int control, IOBUF a,
                                              byte *buf, size_t *ret_len);
-static int decode_filter( void *opaque, int control, iobuf_t a,
+static int decode_filter( void *opaque, int control, IOBUF a,
                                        byte *buf, size_t *ret_len);
 
-typedef struct {
-    CIPHER_HANDLE cipher_hd;
-    MD_HANDLE mdc_hash;
-    char defer[20];
-    int  defer_filled;
-    int  eof_seen;
+typedef struct 
+{
+  gcry_cipher_hd_t cipher_hd;
+  gcry_md_hd_t mdc_hash;
+  char defer[20];
+  int  defer_filled;
+  int  eof_seen;
 } decode_filter_ctx_t;
 
 
@@ -70,7 +71,8 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
            log_info(_("encrypted with unknown algorithm %d\n"), dek->algo );
         dek->algo_info_printed = 1;
     }
-    if( (rc=openpgp_cipher_test_algo(dek->algo)) )
+    rc = openpgp_cipher_test_algo (dek->algo);
+    if (rc)
        goto leave;
     blocksize = gcry_cipher_get_algo_blklen (dek->algo);
     if( !blocksize || blocksize > 16 )
@@ -80,31 +82,39 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
        BUG();
 
     if( ed->mdc_method ) {
-       gcry_md_open (&dfx.mdc_hash, ed->mdc_method, 0 );
+        if (gcry_md_open (&dfx.mdc_hash, ed->mdc_method, 0 ))
+            BUG ();
        if ( DBG_HASHING )
            gcry_md_start_debug (dfx.mdc_hash, "checkmdc");
     }
+
     rc = gcry_cipher_open (&dfx.cipher_hd, dek->algo,
                            GCRY_CIPHER_MODE_CFB,
-                           GCRY_CIPHER_SECURE
-                           | ((ed->mdc_method || dek->algo >= 100)?
-                              0 : GCRY_CIPHER_ENABLE_SYNC) );
-     if (rc)
-       {
-         /* we should never get an error here cause we already
-          * checked, that the algorithm is available. What about a
-          * flag to let the function die in this case? */
-       BUG();
-       }
+                           (GCRY_CIPHER_SECURE
+                            | ((ed->mdc_method || dek->algo >= 100)?
+                               0 : GCRY_CIPHER_ENABLE_SYNC)));
+    if (rc)
+      {
+        /* We should never get an error here cause we already checked
+         * that the algorithm is available.  */
+        BUG();
+      }
+
+
     /* log_hexdump( "thekey", dek->key, dek->keylen );*/
     rc = gcry_cipher_setkey (dfx.cipher_hd, dek->key, dek->keylen);
-    if( gpg_err_code (rc) == GPG_ERR_WEAK_KEY )
-       log_info(_("WARNING: message was encrypted with "
-                   "a weak key in the symmetric cipher.\n"));
-    else if( rc ) {
-       log_error("key setup failed: %s\n", gpg_strerror (rc) );
+    if ( gpg_err_code (rc) == GPG_ERR_WEAK_KEY )
+      {
+       log_info(_("WARNING: message was encrypted with"
+                  " a weak key in the symmetric cipher.\n"));
+       rc=0;
+      }
+    else if( rc )
+      {
+       log_error("key setup failed: %s\n", g10_errstr(rc) );
        goto leave;
-    }
+      
+      }
     if (!ed->buf) {
         log_error(_("problem handling encrypted packet\n"));
         goto leave;
@@ -112,7 +122,7 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
 
     gcry_cipher_setiv (dfx.cipher_hd, NULL, 0);
 
-    if (ed->len) {
+    if( ed->len ) {
        for(i=0; i < (nprefix+2) && ed->len; i++, ed->len-- ) {
            if( (c=iobuf_get(ed->buf)) == -1 )
                break;
@@ -127,17 +137,20 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
            else
                temp[i] = c;
     }
-    gcry_cipher_decrypt( dfx.cipher_hd, temp, nprefix+2, NULL, 0);
-    gcry_cipher_sync( dfx.cipher_hd );
+
+    gcry_cipher_decrypt (dfx.cipher_hd, temp, nprefix+2, NULL, 0);
+    gcry_cipher_sync (dfx.cipher_hd);
     p = temp;
 /* log_hexdump( "prefix", temp, nprefix+2 ); */
-    if( p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1] ) {
+    if(dek->symmetric
+       && (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) )
+      {
        rc = GPG_ERR_BAD_KEY;
        goto leave;
-    }
+      }
 
     if( dfx.mdc_hash )
-       gcry_md_write( dfx.mdc_hash, temp, nprefix+2 );
+       gcry_md_write (dfx.mdc_hash, temp, nprefix+2);
 
     if( ed->mdc_method )
        iobuf_push_filter( ed->buf, mdc_decode_filter, &dfx );
@@ -152,18 +165,18 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
        int datalen = gcry_md_get_algo_dlen (ed->mdc_method);
 
        gcry_cipher_decrypt (dfx.cipher_hd, dfx.defer, 20, NULL, 0);
-       gcry_md_final ( dfx.mdc_hash );
-       ifdatalen != 20
-           || memcmp(gcry_md_read ( dfx.mdc_hash, 0 ), dfx.defer, datalen) )
-           rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
-       /*log_hexdump("MDC calculated:", gcry_md_read ( dfx.mdc_hash, 0), datalen);*/
+       gcry_md_final (dfx.mdc_hash);
+       if (datalen != 20
+           || memcmp (gcry_md_read( dfx.mdc_hash, 0 ), dfx.defer, datalen) )
+          rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
+       /*log_hexdump("MDC calculated:", md_read( dfx.mdc_hash, 0), datalen);*/
        /*log_hexdump("MDC message   :", dfx.defer, 20);*/
     }
     
 
   leave:
-    gcry_cipher_close(dfx.cipher_hd);
-    gcry_md_close ( dfx.mdc_hash );
+    gcry_cipher_close (dfx.cipher_hd);
+    gcry_md_close (dfx.mdc_hash);
     return rc;
 }
 
@@ -171,7 +184,7 @@ decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
 
 /* I think we should merge this with cipher_filter */
 static int
-mdc_decode_filter( void *opaque, int control, iobuf_t a,
+mdc_decode_filter( void *opaque, int control, IOBUF a,
                                              byte *buf, size_t *ret_len)
 {
     decode_filter_ctx_t *dfx = opaque;
@@ -229,8 +242,8 @@ mdc_decode_filter( void *opaque, int control, iobuf_t a,
        }
 
        if( n ) {
-           gcry_cipher_decryptdfx->cipher_hd, buf, n, NULL, 0);
-           gcry_md_write( dfx->mdc_hash, buf, n );
+           gcry_cipher_decrypt (dfx->cipher_hd, buf, n, NULL, 0);
+           gcry_md_write (dfx->mdc_hash, buf, n);
        }
        else {
            assert( dfx->eof_seen );
@@ -245,7 +258,7 @@ mdc_decode_filter( void *opaque, int control, iobuf_t a,
 }
 
 static int
-decode_filter( void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len)
+decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len)
 {
     decode_filter_ctx_t *fc = opaque;
     size_t n, size = *ret_len;
@@ -256,7 +269,7 @@ decode_filter( void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len)
        n = iobuf_read( a, buf, size );
        if( n == -1 ) n = 0;
        if( n )
-           gcry_cipher_decryptfc->cipher_hd, buf, n, NULL, 0);
+           gcry_cipher_decrypt (fc->cipher_hd, buf, n, NULL, 0);
        else
            rc = -1; /* eof */
        *ret_len = n;
index b1fc2c7..839964b 100644 (file)
@@ -1,5 +1,5 @@
 /* exec.c - generic call-a-program code
- * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +15,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+
+#include "gpg.h"
 #include "options.h"
-#include "memory.h"
 #include "i18n.h"
 #include "iobuf.h"
 #include "util.h"
-#include "mkdtemp.h"
+#include "mkdtemp.h"  /* From gnulib. */
 #include "exec.h"
 
 #ifdef NO_EXEC
@@ -47,12 +49,12 @@ int exec_write(struct exec_info **info,const char *program,
               const char *args_in,const char *name,int writeonly,int binary)
 {
   log_error(_("no remote program execution supported\n"));
-  return GPG_ERR_GENERAL;
+  return G10ERR_GENERAL;
 }
 
-int exec_read(struct exec_info *info) { return GPG_ERR_GENERAL; }
-int exec_finish(struct exec_info *info) { return GPG_ERR_GENERAL; }
-int set_exec_path(const char *path,int method) { return GPG_ERR_GENERAL; }
+int exec_read(struct exec_info *info) { return G10ERR_GENERAL; }
+int exec_finish(struct exec_info *info) { return G10ERR_GENERAL; }
+int set_exec_path(const char *path) { return G10ERR_GENERAL; }
 
 #else /* ! NO_EXEC */
 
@@ -60,7 +62,7 @@ int set_exec_path(const char *path,int method) { return GPG_ERR_GENERAL; }
 /* This is a nicer system() for windows that waits for programs to
    return before returning control to the caller.  I hate helpful
    computers. */
-static int win_system(const char *command)
+static int w32_system(const char *command)
 {
   PROCESS_INFORMATION pi;
   STARTUPINFO si;
@@ -68,7 +70,7 @@ static int win_system(const char *command)
 
   /* We must use a copy of the command as CreateProcess modifies this
      argument. */
-  string=xstrdup (command);
+  string=xstrdup(command);
 
   memset(&pi,0,sizeof(pi));
   memset(&si,0,sizeof(si));
@@ -82,42 +84,30 @@ static int win_system(const char *command)
 
   CloseHandle(pi.hProcess);
   CloseHandle(pi.hThread);
-  xfree (string);
+  xfree(string);
 
   return 0;
 }
 #endif
 
-/* method==0 to replace current $PATH, and 1 to append to current
-   $PATH.  */
-int set_exec_path(const char *path,int method)
+/* Replaces current $PATH */
+int set_exec_path(const char *path)
 {
-  char *p,*curpath=NULL;
-  size_t curlen=0;
-
-  if(method==1 && (curpath=getenv("PATH")))
-    curlen=strlen(curpath)+1;
+  char *p;
 
-  p=xmalloc (5+curlen+strlen(path)+1);
+  p=xmalloc(5+strlen(path)+1);
   strcpy(p,"PATH=");
-
-  if(curpath)
-    {
-      strcat(p,curpath);
-      strcat(p,":");
-    }
-
   strcat(p,path);
 
   if(DBG_EXTPROG)
-    log_debug("set_exec_path method %d: %s\n",method,p);
+    log_debug("set_exec_path: %s\n",p);
 
   /* Notice that path is never freed.  That is intentional due to the
      way putenv() works.  This leaks a few bytes if we call
      set_exec_path multiple times. */
 
   if(putenv(p)!=0)
-    return GPG_ERR_GENERAL;
+    return G10ERR_GENERAL;
   else
     return 0;
 }
@@ -128,16 +118,16 @@ static int make_tempdir(struct exec_info *info)
   char *tmp=opt.temp_dir,*namein=info->name,*nameout;
 
   if(!namein)
-    namein=info->binary?"tempin" EXTSEP_S "bin":"tempin" EXTSEP_S "txt";
+    namein=info->flags.binary?"tempin" EXTSEP_S "bin":"tempin" EXTSEP_S "txt";
 
-  nameout=info->binary?"tempout" EXTSEP_S "bin":"tempout" EXTSEP_S "txt";
+  nameout=info->flags.binary?"tempout" EXTSEP_S "bin":"tempout" EXTSEP_S "txt";
 
   /* Make up the temp dir and files in case we need them */
 
   if(tmp==NULL)
     {
 #if defined (_WIN32)
-      tmp=xmalloc (256);
+      tmp=xmalloc(256);
       if(GetTempPath(256,tmp)==0)
        strcpy(tmp,"c:\\windows\\temp");
       else
@@ -169,12 +159,12 @@ static int make_tempdir(struct exec_info *info)
 #endif
     }
 
-  info->tempdir=xmalloc (strlen(tmp)+strlen(DIRSEP_S)+10+1);
+  info->tempdir=xmalloc(strlen(tmp)+strlen(DIRSEP_S)+10+1);
 
   sprintf(info->tempdir,"%s" DIRSEP_S "gpg-XXXXXX",tmp);
 
 #if defined (_WIN32)
-  xfree (tmp);
+  xfree(tmp);
 #endif
 
   if(mkdtemp(info->tempdir)==NULL)
@@ -182,21 +172,21 @@ static int make_tempdir(struct exec_info *info)
              info->tempdir,strerror(errno));
   else
     {
-      info->madedir=1;
+      info->flags.madedir=1;
 
-      info->tempfile_in=xmalloc (strlen(info->tempdir)+
+      info->tempfile_in=xmalloc(strlen(info->tempdir)+
                                strlen(DIRSEP_S)+strlen(namein)+1);
       sprintf(info->tempfile_in,"%s" DIRSEP_S "%s",info->tempdir,namein);
 
-      if(!info->writeonly)
+      if(!info->flags.writeonly)
        {
-         info->tempfile_out=xmalloc (strlen(info->tempdir)+
+         info->tempfile_out=xmalloc(strlen(info->tempdir)+
                                     strlen(DIRSEP_S)+strlen(nameout)+1);
          sprintf(info->tempfile_out,"%s" DIRSEP_S "%s",info->tempdir,nameout);
        }
     }
 
-  return info->madedir?0:GPG_ERR_GENERAL;
+  return info->flags.madedir?0:G10ERR_GENERAL;
 }
 
 /* Expands %i and %o in the args to the full temp files within the
@@ -206,14 +196,14 @@ static int expand_args(struct exec_info *info,const char *args_in)
   const char *ch=args_in;
   unsigned int size,len;
 
-  info->use_temp_files=0;
-  info->keep_temp_files=0;
+  info->flags.use_temp_files=0;
+  info->flags.keep_temp_files=0;
 
   if(DBG_EXTPROG)
     log_debug("expanding string \"%s\"\n",args_in);
 
   size=100;
-  info->command=xmalloc (size);
+  info->command=xmalloc(size);
   len=0;
   info->command[0]='\0';
 
@@ -228,31 +218,31 @@ static int expand_args(struct exec_info *info,const char *args_in)
          switch(*ch)
            {
            case 'O':
-             info->keep_temp_files=1;
+             info->flags.keep_temp_files=1;
              /* fall through */
 
            case 'o': /* out */
-             if(!info->madedir)
+             if(!info->flags.madedir)
                {
                  if(make_tempdir(info))
                    goto fail;
                }
              append=info->tempfile_out;
-             info->use_temp_files=1;
+             info->flags.use_temp_files=1;
              break;
 
            case 'I':
-             info->keep_temp_files=1;
+             info->flags.keep_temp_files=1;
              /* fall through */
 
            case 'i': /* in */
-             if(!info->madedir)
+             if(!info->flags.madedir)
                {
                  if(make_tempdir(info))
                    goto fail;
                }
              append=info->tempfile_in;
-             info->use_temp_files=1;
+             info->flags.use_temp_files=1;
              break;
 
            case '%':
@@ -293,17 +283,17 @@ static int expand_args(struct exec_info *info,const char *args_in)
     }
 
   if(DBG_EXTPROG)
-    log_debug("args expanded to \"%s\", use %d, keep %d\n",
-             info->command,info->use_temp_files,info->keep_temp_files);
+    log_debug("args expanded to \"%s\", use %u, keep %u\n",info->command,
+             info->flags.use_temp_files,info->flags.keep_temp_files);
 
   return 0;
 
  fail:
 
-  xfree (info->command);
+  xfree(info->command);
   info->command=NULL;
 
-  return GPG_ERR_GENERAL;
+  return G10ERR_GENERAL;
 }
 
 /* Either handles the tempfile creation, or the fork/exec.  If it
@@ -315,7 +305,7 @@ static int expand_args(struct exec_info *info,const char *args_in)
 int exec_write(struct exec_info **info,const char *program,
               const char *args_in,const char *name,int writeonly,int binary)
 {
-  int ret=GPG_ERR_GENERAL;
+  int ret=G10ERR_GENERAL;
 
   if(opt.exec_disable && !opt.no_perm_warn)
     {
@@ -335,22 +325,22 @@ int exec_write(struct exec_info **info,const char *program,
   if(program==NULL && args_in==NULL)
     BUG();
 
-  *info=xcalloc (1,sizeof(struct exec_info));
+  *info=xmalloc_clear(sizeof(struct exec_info));
 
   if(name)
-    (*info)->name=xstrdup (name);
-  (*info)->binary=binary;
-  (*info)->writeonly=writeonly;
+    (*info)->name=xstrdup(name);
+  (*info)->flags.binary=binary;
+  (*info)->flags.writeonly=writeonly;
 
   /* Expand the args, if any */
   if(args_in && expand_args(*info,args_in))
     goto fail;
 
 #ifdef EXEC_TEMPFILE_ONLY
-  if(!(*info)->use_temp_files)
+  if(!(*info)->flags.use_temp_files)
     {
-      log_error(_("this platform requires temp files when calling external "
-                 "programs\n"));
+      log_error(_("this platform requires temporary files when calling"
+                 " external programs\n"));
       goto fail;
     }
 
@@ -358,7 +348,7 @@ int exec_write(struct exec_info **info,const char *program,
 
   /* If there are no args, or there are args, but no temp files, we
      can use fork/exec/pipe */
-  if(args_in==NULL || (*info)->use_temp_files==0)
+  if(args_in==NULL || (*info)->flags.use_temp_files==0)
     {
       int to[2],from[2];
 
@@ -392,7 +382,7 @@ int exec_write(struct exec_info **info,const char *program,
 
          /* If the program isn't going to respond back, they get to
              keep their stdout/stderr */
-         if(!(*info)->writeonly)
+         if(!(*info)->flags.writeonly)
            {
              /* implied close of STDERR */
              if(dup2(STDOUT_FILENO,STDERR_FILENO)==-1)
@@ -426,10 +416,12 @@ int exec_write(struct exec_info **info,const char *program,
 
          /* If we get this far the exec failed.  Clean up and return. */
 
-         log_error(_("unable to execute %s \"%s\": %s\n"),
-                   args_in==NULL?"program":"shell",
-                   args_in==NULL?program:shell,
-                   strerror(errno));
+         if(args_in==NULL)
+           log_error(_("unable to execute program `%s': %s\n"),
+                     program,strerror(errno));
+         else
+           log_error(_("unable to execute shell `%s': %s\n"),
+                     shell,strerror(errno));
 
          /* This mimics the POSIX sh behavior - 127 means "not found"
              from the shell. */
@@ -446,8 +438,8 @@ int exec_write(struct exec_info **info,const char *program,
       (*info)->tochild=fdopen(to[1],binary?"wb":"w");
       if((*info)->tochild==NULL)
        {
-          ret = gpg_error_from_errno (errno);
          close(to[1]);
+         ret=G10ERR_WRITE_FILE;
          goto fail;
        }
 
@@ -456,8 +448,8 @@ int exec_write(struct exec_info **info,const char *program,
       (*info)->fromchild=iobuf_fdopen(from[0],"r");
       if((*info)->fromchild==NULL)
        {
-          ret = gpg_error_from_errno (errno);
          close(from[0]);
+         ret=G10ERR_READ_FILE;
          goto fail;
        }
 
@@ -472,12 +464,18 @@ int exec_write(struct exec_info **info,const char *program,
     log_debug("using temp file `%s'\n",(*info)->tempfile_in);
 
   /* It's not fork/exec/pipe, so create a temp file */
-  (*info)->tochild=fopen((*info)->tempfile_in,binary?"wb":"w");
+  if( is_secured_filename ((*info)->tempfile_in) )
+    {
+      (*info)->tochild = NULL;
+      errno = EPERM;
+    }
+  else
+    (*info)->tochild=fopen((*info)->tempfile_in,binary?"wb":"w");
   if((*info)->tochild==NULL)
     {
-      ret = gpg_error_from_errno (errno);
       log_error(_("can't create `%s': %s\n"),
                (*info)->tempfile_in,strerror(errno));
+      ret=G10ERR_WRITE_FILE;
       goto fail;
     }
 
@@ -489,18 +487,18 @@ int exec_write(struct exec_info **info,const char *program,
 
 int exec_read(struct exec_info *info)
 {
-  int ret=GPG_ERR_GENERAL;
+  int ret=G10ERR_GENERAL;
 
   fclose(info->tochild);
   info->tochild=NULL;
 
-  if(info->use_temp_files)
+  if(info->flags.use_temp_files)
     {
       if(DBG_EXTPROG)
        log_debug("system() command is %s\n",info->command);
 
 #if defined (_WIN32)
-      info->progreturn=win_system(info->command);
+      info->progreturn=w32_system(info->command);
 #else
       info->progreturn=system(info->command);
 #endif
@@ -537,14 +535,21 @@ int exec_read(struct exec_info *info)
          goto fail;
        }
 
-      if(!info->writeonly)
+      if(!info->flags.writeonly)
        {
          info->fromchild=iobuf_open(info->tempfile_out);
+          if (info->fromchild
+              && is_secured_file (iobuf_get_fd (info->fromchild)))
+            {
+              iobuf_close (info->fromchild);
+              info->fromchild = NULL;
+              errno = EPERM;
+            }
          if(info->fromchild==NULL)
            {
-              ret = gpg_error_from_errno (errno);
              log_error(_("unable to read external program response: %s\n"),
                        strerror(errno));
+             ret=G10ERR_READ_FILE;
              goto fail;
            }
 
@@ -583,7 +588,7 @@ int exec_finish(struct exec_info *info)
     }
 #endif
 
-  if(info->madedir && !info->keep_temp_files)
+  if(info->flags.madedir && !info->flags.keep_temp_files)
     {
       if(info->tempfile_in)
        {
@@ -604,12 +609,12 @@ int exec_finish(struct exec_info *info)
                 info->tempdir,strerror(errno));
     }
 
-  xfree (info->command);
-  xfree (info->name);
-  xfree (info->tempdir);
-  xfree (info->tempfile_in);
-  xfree (info->tempfile_out);
-  xfree (info);
+  xfree(info->command);
+  xfree(info->name);
+  xfree(info->tempdir);
+  xfree(info->tempfile_in);
+  xfree(info->tempfile_out);
+  xfree(info);
 
   return ret;
 }
index eda4068..66d13c7 100644 (file)
@@ -1,5 +1,5 @@
 /* exec.h
- * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +15,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #ifndef _EXEC_H_
 
 #include <unistd.h>
 #include <stdio.h>
+
 #include "../common/iobuf.h"
 
 struct exec_info
 {
-  int progreturn,binary,writeonly,madedir,use_temp_files,keep_temp_files;
+  int progreturn;
+  struct
+  {
+    unsigned int binary:1;
+    unsigned int writeonly:1;
+    unsigned int madedir:1;
+    unsigned int use_temp_files:1;
+    unsigned int keep_temp_files:1;
+  } flags;
   pid_t child;
   FILE *tochild;
   iobuf_t fromchild;
@@ -38,6 +48,6 @@ int exec_write(struct exec_info **info,const char *program,
               const char *args_in,const char *name,int writeonly,int binary);
 int exec_read(struct exec_info *info);
 int exec_finish(struct exec_info *info);
-int set_exec_path(const char *path,int method);
+int set_exec_path(const char *path);
 
 #endif /* !_EXEC_H_ */
index 43d1b21..4950796 100644 (file)
@@ -1,6 +1,6 @@
 /* export.c
- * Copyright (C) 1998, 1999, 2000, 2001, 2002,
- *               200 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ *               2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -16,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include <errno.h>
 #include <assert.h>
 
+#include "gpg.h"
 #include "options.h"
 #include "packet.h"
 #include "errors.h"
 #include "keydb.h"
-#include "memory.h"
 #include "util.h"
 #include "main.h"
 #include "i18n.h"
+#include "trustdb.h"
+
+
+/* An object to keep track of subkeys. */
+struct subkey_list_s
+{
+  struct subkey_list_s *next;
+  u32 kid[2];
+};
+typedef struct subkey_list_s *subkey_list_t;
+
 
 static int do_export( STRLIST users, int secret, unsigned int options );
-static int do_export_stream( iobuf_t out, STRLIST users, int secret,
+static int do_export_stream( IOBUF out, STRLIST users, int secret,
                             KBNODE *keyblock_out, unsigned int options,
                             int *any );
 static int build_sexp (iobuf_t out, PACKET *pkt, int *indent);
 
+
 int
-parse_export_options(char *str,unsigned int *options)
+parse_export_options(char *str,unsigned int *options,int noisy)
 {
   struct parse_options export_opts[]=
     {
-      {"include-non-rfc",EXPORT_INCLUDE_NON_RFC},
-      {"include-local-sigs",EXPORT_INCLUDE_LOCAL_SIGS},
-      {"include-attributes",EXPORT_INCLUDE_ATTRIBUTES},
-      {"include-sensitive-revkeys",EXPORT_INCLUDE_SENSITIVE_REVKEYS},
-      {"sexp-format",EXPORT_SEXP_FORMAT},
-      {NULL,0}
+      {"export-local-sigs",EXPORT_LOCAL_SIGS,NULL,
+       N_("export signatures that are marked as local-only")},
+      {"export-attributes",EXPORT_ATTRIBUTES,NULL,
+       N_("export attribute user IDs (generally photo IDs)")},
+      {"export-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL,
+       N_("export revocation keys marked as \"sensitive\"")},
+      {"export-reset-subkey-passwd",EXPORT_RESET_SUBKEY_PASSWD,NULL,
+       N_("remove the passphrase from exported subkeys")},
+      {"export-clean",EXPORT_CLEAN,NULL,
+       N_("remove unusable parts from key during export")},
+      {"export-minimal",EXPORT_MINIMAL|EXPORT_CLEAN,NULL,
+       N_("remove as much as possible from key during export")},
+      {"export-sexp-format",EXPORT_SEXP_FORMAT, NULL,
+       N_("export keys in an S-expression based format")},
+      /* Aliases for backward compatibility */
+      {"include-local-sigs",EXPORT_LOCAL_SIGS,NULL,NULL},
+      {"include-attributes",EXPORT_ATTRIBUTES,NULL,NULL},
+      {"include-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL,NULL},
+      /* dummy */
+      {"export-unusable-sigs",0,NULL,NULL},
+      {"export-clean-sigs",0,NULL,NULL},
+      {"export-clean-uids",0,NULL,NULL},
+      {NULL,0,NULL,NULL}
       /* add tags for include revoked and disabled? */
     };
 
-  return parse_options(str,options,export_opts);
+  return parse_options(str,options,export_opts,noisy);
 }
 
+
 /****************
  * Export the public keys (to standard out or --output).
  * Depending on opt.armor the output is armored.
@@ -74,7 +105,7 @@ export_pubkeys( STRLIST users, unsigned int options )
  * been exported
  */
 int
-export_pubkeys_stream( iobuf_t out, STRLIST users,
+export_pubkeys_stream( IOBUF out, STRLIST users,
                       KBNODE *keyblock_out, unsigned int options )
 {
     int any, rc;
@@ -90,7 +121,7 @@ export_seckeys( STRLIST users )
 {
   /* Use only relevant options for the secret key. */
   unsigned int options = (opt.export_options & EXPORT_SEXP_FORMAT);
-  return do_export (users, 1, options);
+  return do_export( users, 1, options );
 }
 
 int
@@ -98,37 +129,38 @@ export_secsubkeys( STRLIST users )
 {
   /* Use only relevant options for the secret key. */
   unsigned int options = (opt.export_options & EXPORT_SEXP_FORMAT);
-  return do_export( users, 2, options);
+  return do_export( users, 2, options );
 }
 
 static int
-do_export (STRLIST users, int secret, unsigned int options)
+do_export( STRLIST users, int secret, unsigned int options )
 {
-  iobuf_t out = NULL;
+  IOBUF out = NULL;
   int any, rc;
   armor_filter_context_t afx;
   compress_filter_context_t zfx;
   
-  memset (&afx, 0, sizeof afx);
-  memset (&zfx, 0, sizeof zfx);
+  memset&afx, 0, sizeof afx);
+  memset&zfx, 0, sizeof zfx);
   
-  rc = open_outfile (NULL, 0, &out);
+  rc = open_outfile( NULL, 0, &out );
   if (rc)
     return rc;
 
   if (!(options & EXPORT_SEXP_FORMAT))
     {
-      if (opt.armor) 
+      if ( opt.armor )
         {
           afx.what = secret?5:1;
-          iobuf_push_filter( out, armor_filter, &afx );
+          iobuf_push_filter ( out, armor_filter, &afx );
         }
-      if (opt.compress_keys && opt.compress)
-        iobuf_push_filter( out, compress_filter, &zfx );
+      if ( opt.compress_keys )
+        push_compress_filter (out,&zfx,default_compress_algo());
     }
-  rc = do_export_stream (out, users, secret, NULL, options, &any );
 
-  if (rc || !any)
+  rc = do_export_stream ( out, users, secret, NULL, options, &any );
+
+  if ( rc || !any )
     iobuf_cancel (out);
   else
     iobuf_close (out);
@@ -136,11 +168,129 @@ do_export (STRLIST users, int secret, unsigned int options)
 }
 
 
+
+/* Release an entire subkey list. */
+static void
+release_subkey_list (subkey_list_t list)
+{
+  while (list)
+    {
+      subkey_list_t tmp = list->next;;
+      xfree (list);
+      list = tmp;
+    }
+}
+
+
+/* Returns true if NODE is a subkey and contained in LIST. */
+static int
+subkey_in_list_p (subkey_list_t list, KBNODE node)
+{
+  if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+      || node->pkt->pkttype == PKT_SECRET_SUBKEY )
+    {
+      u32 kid[2];
+
+      if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+        keyid_from_pk (node->pkt->pkt.public_key, kid);
+      else
+        keyid_from_sk (node->pkt->pkt.secret_key, kid);
+      
+      for (; list; list = list->next)
+        if (list->kid[0] == kid[0] && list->kid[1] == kid[1])
+          return 1;
+    }
+  return 0;
+}
+
+/* Allocate a new subkey list item from NODE. */
+static subkey_list_t
+new_subkey_list_item (KBNODE node)
+{
+  subkey_list_t list = xcalloc (1, sizeof *list);
+
+  if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+    keyid_from_pk (node->pkt->pkt.public_key, list->kid);
+  else if (node->pkt->pkttype == PKT_SECRET_SUBKEY)
+    keyid_from_sk (node->pkt->pkt.secret_key, list->kid);
+
+  return list;
+}
+
+
+/* Helper function to check whether the subkey at NODE actually
+   matches the description at DESC.  The function returns true if the
+   key under question has been specified by an exact specification
+   (keyID or fingerprint) and does match the one at NODE.  It is
+   assumed that the packet at NODE is either a public or secret
+   subkey. */
+static int
+exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, KBNODE node)
+{
+  u32 kid[2];
+  byte fpr[MAX_FINGERPRINT_LEN];
+  size_t fprlen;
+  int result = 0;
+
+  switch(desc->mode)
+    {
+    case KEYDB_SEARCH_MODE_SHORT_KID:
+    case KEYDB_SEARCH_MODE_LONG_KID:
+      if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+        keyid_from_pk (node->pkt->pkt.public_key, kid);
+      else
+        keyid_from_sk (node->pkt->pkt.secret_key, kid);
+      break;
+      
+    case KEYDB_SEARCH_MODE_FPR16:
+    case KEYDB_SEARCH_MODE_FPR20:
+    case KEYDB_SEARCH_MODE_FPR:
+      if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+        fingerprint_from_pk (node->pkt->pkt.public_key, fpr,&fprlen);
+      else
+        fingerprint_from_sk (node->pkt->pkt.secret_key, fpr,&fprlen);
+      break;
+      
+    default:
+      break;
+    }
+  
+  switch(desc->mode)
+    {
+    case KEYDB_SEARCH_MODE_SHORT_KID:
+      if (desc->u.kid[1] == kid[1])
+        result = 1;
+      break;
+
+    case KEYDB_SEARCH_MODE_LONG_KID:
+      if (desc->u.kid[0] == kid[0] && desc->u.kid[1] == kid[1])
+        result = 1;
+      break;
+
+    case KEYDB_SEARCH_MODE_FPR16:
+      if (!memcmp (desc->u.fpr, fpr, 16))
+        result = 1;
+      break;
+
+    case KEYDB_SEARCH_MODE_FPR20:
+    case KEYDB_SEARCH_MODE_FPR:
+      if (!memcmp (desc->u.fpr, fpr, 20))
+        result = 1;
+      break;
+
+    default:
+      break;
+    }
+
+  return result;
+}
+
+
 /* If keyblock_out is non-NULL, AND the exit code is zero, then it
    contains a pointer to the first keyblock found and exported.  No
    other keyblocks are exported.  The caller must free it. */
 static int
-do_export_stream( iobuf_t out, STRLIST users, int secret,
+do_export_stream( IOBUF out, STRLIST users, int secret,
                  KBNODE *keyblock_out, unsigned int options, int *any )
 {
     int rc = 0;
@@ -149,6 +299,7 @@ do_export_stream( iobuf_t out, STRLIST users, int secret,
     KBNODE kbctx, node;
     size_t ndesc, descindex;
     KEYDB_SEARCH_DESC *desc = NULL;
+    subkey_list_t subkey_list = NULL;  /* Track alreay processed subkeys. */
     KEYDB_HANDLE kdbhd;
     STRLIST sl;
     int indent = 0;
@@ -159,7 +310,7 @@ do_export_stream( iobuf_t out, STRLIST users, int secret,
 
     if (!users) {
         ndesc = 1;
-        desc = xcalloc (1, ndesc * sizeof *desc);
+        desc = xcalloc ( ndesc, sizeof *desc );
         desc[0].mode = KEYDB_SEARCH_MODE_FIRST;
     }
     else {
@@ -171,11 +322,11 @@ do_export_stream( iobuf_t out, STRLIST users, int secret,
            if (classify_user_id (sl->d, desc+ndesc))
                 ndesc++;
             else
-                log_error (_("key `%s' not found: %s\n"),
-                           sl->d, gpg_strerror (GPG_ERR_INV_USER_ID));
+                log_error (_("key \"%s\" not found: %s\n"),
+                           sl->d, g10_errstr (G10ERR_INV_USER_ID));
         }
 
-        /* it would be nice to see which of the given users did
+        /* It would be nice to see which of the given users did
            actually match one in the keyring.  To implement this we
            need to have a found flag for each entry in desc and to set
            this we must check all those entries after a match to mark
@@ -183,6 +334,14 @@ do_export_stream( iobuf_t out, STRLIST users, int secret,
            do this we need an extra flag to enable this feature so */
     }
 
+#ifdef ENABLE_SELINUX_HACKS
+    if (secret) {
+        log_error (_("exporting secret keys not allowed\n"));
+        rc = G10ERR_GENERAL;
+        goto leave;
+    }
+#endif
+
     while (!(rc = keydb_search2 (kdbhd, desc, ndesc, &descindex))) {
         int sha1_warned=0,skip_until_subkey=0;
        u32 sk_keyid[2];
@@ -190,49 +349,59 @@ do_export_stream( iobuf_t out, STRLIST users, int secret,
        if (!users) 
             desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
 
-        /* read the keyblock */
+        /* Read the keyblock. */
         rc = keydb_get_keyblock (kdbhd, &keyblock );
        if( rc ) {
-            log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) );
+            log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
            goto leave;
        }
 
-       /* do not export keys which are incompatible with rfc2440 */
-       if( !(options&EXPORT_INCLUDE_NON_RFC) &&
-           (node = find_kbnode( keyblock, PKT_PUBLIC_KEY )) ) {
-           PKT_public_key *pk = node->pkt->pkt.public_key;
-           if( pk->version == 3 && pk->pubkey_algo > 3 ) {
-               log_info(_("key %08lX: not a rfc2440 key - skipped\n"),
-                             (ulong)keyid_from_pk( pk, NULL) );
-               continue;
-           }
-       }
-
-       node=find_kbnode( keyblock, PKT_SECRET_KEY );
-       if(node)
+       if((node=find_kbnode(keyblock,PKT_SECRET_KEY)))
          {
            PKT_secret_key *sk=node->pkt->pkt.secret_key;
 
            keyid_from_sk(sk,sk_keyid);
 
-           /* we can't apply GNU mode 1001 on an unprotected key */
+           /* We can't apply GNU mode 1001 on an unprotected key. */
            if( secret == 2 && !sk->is_protected )
              {
-               log_info(_("key %08lX: not protected - skipped\n"),
-                        (ulong)sk_keyid[1]);
+               log_info(_("key %s: not protected - skipped\n"),
+                        keystr(sk_keyid));
                continue;
              }
 
-           /* no v3 keys with GNU mode 1001 */
+           /* No v3 keys with GNU mode 1001. */
            if( secret == 2 && sk->version == 3 )
              {
-               log_info(_("key %08lX: PGP 2.x style key - skipped\n"),
-                        (ulong)sk_keyid[1]);
+               log_info(_("key %s: PGP 2.x style key - skipped\n"),
+                        keystr(sk_keyid));
                continue;
              }
+
+            /* It does not make sense to export a key with a primary
+               key on card using a non-key stub.  We simply skip those
+               keys when used with --export-secret-subkeys. */
+            if (secret == 2 && sk->is_protected
+                && sk->protect.s2k.mode == 1002 ) 
+              {
+               log_info(_("key %s: key material on-card - skipped\n"),
+                        keystr(sk_keyid));
+               continue;
+              }
+         }
+       else
+         {
+           /* It's a public key export, so do the cleaning if
+              requested.  Note that both export-clean and
+              export-minimal only apply to UID sigs (0x10, 0x11,
+              0x12, and 0x13).  A designated revocation is never
+              stripped, even with export-minimal set. */
+
+           if(options&EXPORT_CLEAN)
+             clean_key(keyblock,opt.verbose,options&EXPORT_MINIMAL,NULL,NULL);
          }
 
-       /* and write it */
+       /* And write it. */
        for( kbctx=NULL; (node = walk_kbnode( keyblock, &kbctx, 0 )); ) {
            if( skip_until_subkey )
              {
@@ -243,104 +412,92 @@ do_export_stream( iobuf_t out, STRLIST users, int secret,
                  continue;
              }
 
-           /* don't export any comment packets but those in the
-            * secret keyring */
-           if( !secret && node->pkt->pkttype == PKT_COMMENT )
-               continue;
+           /* We used to use comment packets, but not any longer.  In
+              case we still have comments on a key, strip them here
+              before we call build_packet(). */
+           if( node->pkt->pkttype == PKT_COMMENT )
+             continue;
 
-            /* make sure that ring_trust packets never get exported */
+            /* Make sure that ring_trust packets never get exported. */
             if (node->pkt->pkttype == PKT_RING_TRUST)
               continue;
 
            /* If exact is set, then we only export what was requested
               (plus the primary key, if the user didn't specifically
-              request it) */
+              request it). */
            if(desc[descindex].exact
               && (node->pkt->pkttype==PKT_PUBLIC_SUBKEY
                   || node->pkt->pkttype==PKT_SECRET_SUBKEY))
              {
-               u32 kid[2];
-               byte fpr[MAX_FINGERPRINT_LEN];
-               size_t fprlen;
-
-               switch(desc[descindex].mode)
-                 {
-                 case KEYDB_SEARCH_MODE_SHORT_KID:
-                 case KEYDB_SEARCH_MODE_LONG_KID:
-                   if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY)
-                     keyid_from_pk(node->pkt->pkt.public_key,kid);
-                   else
-                     keyid_from_sk(node->pkt->pkt.secret_key,kid);
-                   break;
-
-                 case KEYDB_SEARCH_MODE_FPR16:
-                 case KEYDB_SEARCH_MODE_FPR20:
-                 case KEYDB_SEARCH_MODE_FPR:
-                   if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY)
-                     fingerprint_from_pk(node->pkt->pkt.public_key,
-                                         fpr,&fprlen);
-                   else
-                     fingerprint_from_sk(node->pkt->pkt.secret_key,
-                                         fpr,&fprlen);
-                   break;
-
-                 default:
-                   break;
-                 }
-
-               switch(desc[descindex].mode)
-                 {
-                 case KEYDB_SEARCH_MODE_SHORT_KID:
-                   if (desc[descindex].u.kid[1] != kid[1])
-                     skip_until_subkey=1;
-                   break;
-                 case KEYDB_SEARCH_MODE_LONG_KID:
-                   if (desc[descindex].u.kid[0] != kid[0]
-                       || desc[descindex].u.kid[1] != kid[1])
-                     skip_until_subkey=1;
-                   break;
-                 case KEYDB_SEARCH_MODE_FPR16:
-                   if (memcmp (desc[descindex].u.fpr, fpr, 16))
-                     skip_until_subkey=1;
-                   break;
-                 case KEYDB_SEARCH_MODE_FPR20:
-                 case KEYDB_SEARCH_MODE_FPR:
-                   if (memcmp (desc[descindex].u.fpr, fpr, 20))
-                     skip_until_subkey=1;
-                   break;
-                 default:
-                   break;
-                 }
+                if (!exact_subkey_match_p (desc+descindex, node))
+                  {
+                    /* Before skipping this subkey, check whether any
+                       other description wants an exact match on a
+                       subkey and include that subkey into the output
+                       too.  Need to add this subkey to a list so that
+                       it won't get processed a second time.
+                   
+                       So the first step here is to check that list and
+                       skip in any case if the key is in that list.
+
+                       We need this whole mess because the import
+                       function is not able to merge secret keys and
+                       thus it is useless to output them as two
+                       separate keys and have import merge them.  */
+                    if (subkey_in_list_p (subkey_list, node))  
+                      skip_until_subkey = 1; /* Already processed this one. */
+                    else
+                      {
+                        size_t j;
+
+                        for (j=0; j < ndesc; j++)
+                          if (j != descindex && desc[j].exact
+                              && exact_subkey_match_p (desc+j, node))
+                            break;
+                        if (!(j < ndesc))
+                          skip_until_subkey = 1; /* No other one matching. */ 
+                      }
+                  }
 
                if(skip_until_subkey)
                  continue;
+
+                /* Mark this one as processed. */
+                {
+                  subkey_list_t tmp = new_subkey_list_item (node);
+                  tmp->next = subkey_list;
+                  subkey_list = tmp;
+                }
              }
 
-           if( node->pkt->pkttype == PKT_SIGNATURE ) {
-             /* do not export packets which are marked as not exportable */
-             if( !(options&EXPORT_INCLUDE_LOCAL_SIGS) &&
-                 !node->pkt->pkt.signature->flags.exportable )
-               continue; /* not exportable */
-
-             /* Do not export packets with a "sensitive" revocation
-                 key unless the user wants us to.  Note that we do
-                 export these when issuing the actual revocation (see
-                 revoke.c). */
-             if( !(options&EXPORT_INCLUDE_SENSITIVE_REVKEYS) &&
-                 node->pkt->pkt.signature->revkey ) {
-               int i;
-
-               for(i=0;i<node->pkt->pkt.signature->numrevkeys;i++)
-                 if(node->pkt->pkt.signature->revkey[i]->class & 0x40)
-                   break;
-
-               if(i<node->pkt->pkt.signature->numrevkeys)
-                 continue;
+           if(node->pkt->pkttype==PKT_SIGNATURE)
+             {
+               /* do not export packets which are marked as not
+                  exportable */
+               if(!(options&EXPORT_LOCAL_SIGS)
+                  && !node->pkt->pkt.signature->flags.exportable)
+                 continue; /* not exportable */
+
+               /* Do not export packets with a "sensitive" revocation
+                  key unless the user wants us to.  Note that we do
+                  export these when issuing the actual revocation
+                  (see revoke.c). */
+               if(!(options&EXPORT_SENSITIVE_REVKEYS)
+                  && node->pkt->pkt.signature->revkey)
+                 {
+                   int i;
+
+                   for(i=0;i<node->pkt->pkt.signature->numrevkeys;i++)
+                     if(node->pkt->pkt.signature->revkey[i]->class & 0x40)
+                       break;
+
+                   if(i<node->pkt->pkt.signature->numrevkeys)
+                     continue;
+                 }
              }
-           }
 
            /* Don't export attribs? */
-           if( !(options&EXPORT_INCLUDE_ATTRIBUTES) &&
+           if( !(options&EXPORT_ATTRIBUTES) &&
                node->pkt->pkttype == PKT_USER_ID &&
                node->pkt->pkt.user_id->attrib_data ) {
              /* Skip until we get to something that is not an attrib
@@ -352,8 +509,9 @@ do_export_stream( iobuf_t out, STRLIST users, int secret,
              continue;
            }
 
-           if( secret == 2 && node->pkt->pkttype == PKT_SECRET_KEY ) {
-               /* we don't want to export the secret parts of the
+           if( secret == 2 && node->pkt->pkttype == PKT_SECRET_KEY )
+             {
+               /* We don't want to export the secret parts of the
                 * primary key, this is done by using GNU protection mode 1001
                 */
                int save_mode = node->pkt->pkt.secret_key->protect.s2k.mode;
@@ -363,42 +521,91 @@ do_export_stream( iobuf_t out, STRLIST users, int secret,
                 else
                   rc = build_packet (out, node->pkt);
                node->pkt->pkt.secret_key->protect.s2k.mode = save_mode;
-           }
-           else {
-             /* Warn the user if the secret key or any of the secret
-                 subkeys are protected with SHA1 and we have
-                 simple_sk_checksum set. */
-             if(!sha1_warned && opt.simple_sk_checksum &&
-                (node->pkt->pkttype==PKT_SECRET_KEY ||
-                 node->pkt->pkttype==PKT_SECRET_SUBKEY) &&
-                node->pkt->pkt.secret_key->protect.sha1chk)
-               {
-                 /* I hope this warning doesn't confuse people. */
-                 log_info(_("WARNING: secret key %08lX does not have a "
-                            "simple SK checksum\n"),(ulong)sk_keyid[1]);
-
-                 sha1_warned=1;
-               }
+             }
+           else if (secret == 2 && node->pkt->pkttype == PKT_SECRET_SUBKEY
+                     && (opt.export_options&EXPORT_RESET_SUBKEY_PASSWD))
+              {
+                /* If the subkey is protected reset the passphrase to
+                   export an unprotected subkey.  This feature is
+                   useful in cases of a subkey copied to an unattended
+                   machine where a passphrase is not required. */
+                PKT_secret_key *sk_save, *sk;
+
+                sk_save = node->pkt->pkt.secret_key;
+                sk = copy_secret_key (NULL, sk_save);
+                node->pkt->pkt.secret_key = sk;
+
+                log_info (_("about to export an unprotected subkey\n"));
+                switch (is_secret_key_protected (sk))
+                  {
+                  case -1:
+                    rc = G10ERR_PUBKEY_ALGO;
+                    break;
+                  case 0:
+                    break;
+                  default:
+                    if (sk->protect.s2k.mode == 1001)
+                      ; /* No secret parts. */
+                    else if( sk->protect.s2k.mode == 1002 ) 
+                      ; /* Card key stub. */
+                    else 
+                      {
+                        rc = check_secret_key( sk, 0 );
+                      }
+                    break;
+                  }
+                if (rc)
+                  {
+                    node->pkt->pkt.secret_key = sk_save;
+                    free_secret_key (sk);
+                    log_error (_("failed to unprotect the subkey: %s\n"),
+                               g10_errstr (rc));
+                    goto leave;
+                  }
+
+               rc = build_packet (out, node->pkt);
+
+                node->pkt->pkt.secret_key = sk_save;
+                free_secret_key (sk);
+              }
+           else
+             {
+               /* Warn the user if the secret key or any of the secret
+                  subkeys are protected with SHA1 and we have
+                  simple_sk_checksum set. */
+               if(!sha1_warned && opt.simple_sk_checksum &&
+                  (node->pkt->pkttype==PKT_SECRET_KEY ||
+                   node->pkt->pkttype==PKT_SECRET_SUBKEY) &&
+                  node->pkt->pkt.secret_key->protect.sha1chk)
+                 {
+                   /* I hope this warning doesn't confuse people. */
+                   log_info(_("WARNING: secret key %s does not have a "
+                              "simple SK checksum\n"),keystr(sk_keyid));
+
+                   sha1_warned=1;
+                 }
 
                 if ((options&EXPORT_SEXP_FORMAT))
                   rc = build_sexp (out, node->pkt, &indent);
                 else
                   rc = build_packet (out, node->pkt);
-           }
+             }
 
            if( rc ) {
                log_error("build_packet(%d) failed: %s\n",
-                           node->pkt->pkttype, gpg_strerror (rc) );
+                           node->pkt->pkttype, g10_errstr(rc) );
+               rc = G10ERR_WRITE_FILE;
                goto leave;
            }
        }
+
         if ((options&EXPORT_SEXP_FORMAT) && indent)
           {
             for (; indent; indent--)
               iobuf_put (out, ')');
             iobuf_put (out, '\n');
           }
-        
+
        ++*any;
        if(keyblock_out)
          {
@@ -416,7 +623,8 @@ do_export_stream( iobuf_t out, STRLIST users, int secret,
        rc = 0;
 
   leave:
-    xfree (desc);
+    release_subkey_list (subkey_list);
+    xfree(desc);
     keydb_release (kdbhd);
     if(rc || keyblock_out==NULL)
       release_kbnode( keyblock );
@@ -426,6 +634,7 @@ do_export_stream( iobuf_t out, STRLIST users, int secret,
 }
 
 
+
 static int
 write_sexp_line (iobuf_t out, int *indent, const char *text)
 {
@@ -524,8 +733,8 @@ build_sexp_seckey (iobuf_t out, PACKET *pkt, int *indent)
 }
 
 
-/* For some packet types we write them in a S-Exp like format.  This is
-   still EXPERIMENTAL and subject to change. */
+/* For some packet types we write them in a S-expression format.  This
+   is still EXPERIMENTAL and subject to change.  */
 static int 
 build_sexp (iobuf_t out, PACKET *pkt, int *indent)
 {
index 12c5ceb..3b4e739 100644 (file)
@@ -1,5 +1,6 @@
 /* filter.h
- * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003,
+ *               2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 #ifndef G10_FILTER_H
 #define G10_FILTER_H
 
 #include "types.h"
 #include "cipher.h"
-#include "iobuf.h"
 
 typedef struct {
-    MD_HANDLE md;      /* catch all */
-    MD_HANDLE md2;     /* if we want to calculate an alternate hash */
+    gcry_md_hd_t md;      /* catch all */
+    gcry_md_hd_t md2;     /* if we want to calculate an alternate hash */
     size_t maxbuf_size;
 } md_filter_context_t;
 
@@ -49,6 +50,10 @@ typedef struct {
     int truncated;         /* number of truncated lines */
     int qp_detected;
     int pgp2mode;
+    byte eol[3];            /* The end of line characters as a
+                              zero-terminated string.  Defaults
+                              (eol[0]=='\0') to whatever the local
+                              platform uses. */
 
     byte *buffer;          /* malloced buffer */
     unsigned buffer_size;   /* and size of this buffer */
@@ -87,9 +92,9 @@ typedef struct compress_filter_context_s compress_filter_context_t;
 typedef struct {
     DEK *dek;
     u32 datalen;
-    CIPHER_HANDLE cipher_hd;
+    gcry_cipher_hd_t cipher_hd;
     int header;
-    MD_HANDLE mdc_hash;
+    gcry_md_hd_t mdc_hash;
     byte enchash[20];
     int create_mdc; /* flag will be set by the cipher filter */
 } cipher_filter_context_t;
@@ -104,7 +109,7 @@ typedef struct {
     int truncated;         /* number of truncated lines */
     int not_dash_escaped;
     int escape_from;
-    MD_HANDLE md;
+    gcry_md_hd_t md;
     int pending_lf;
     int pending_esc;
 } text_filter_context_t;
@@ -121,35 +126,36 @@ typedef struct {
 /* encrypt_filter_context_t defined in main.h */
 
 /*-- mdfilter.c --*/
-int md_filter( void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len);
+int md_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len);
 void free_md_filter_context( md_filter_context_t *mfx );
 
 /*-- armor.c --*/
-int use_armor_filter( iobuf_t a );
+int use_armor_filter( IOBUF a );
 int armor_filter( void *opaque, int control,
-                 iobuf_t chain, byte *buf, size_t *ret_len);
+                 IOBUF chain, byte *buf, size_t *ret_len);
 UnarmorPump unarmor_pump_new (void);
 void        unarmor_pump_release (UnarmorPump x);
 int         unarmor_pump (UnarmorPump x, int c);
 
 /*-- compress.c --*/
-int compress_filter( void *opaque, int control,
-                    iobuf_t chain, byte *buf, size_t *ret_len);
+void push_compress_filter(IOBUF out,compress_filter_context_t *zfx,int algo);
+void push_compress_filter2(IOBUF out,compress_filter_context_t *zfx,
+                          int algo,int rel);
 
 /*-- cipher.c --*/
 int cipher_filter( void *opaque, int control,
-                  iobuf_t chain, byte *buf, size_t *ret_len);
+                  IOBUF chain, byte *buf, size_t *ret_len);
 
 /*-- textfilter.c --*/
 int text_filter( void *opaque, int control,
-                iobuf_t chain, byte *buf, size_t *ret_len);
-int copy_clearsig_text( iobuf_t out, iobuf_t inp, MD_HANDLE md,
-                         int escape_dash, int escape_from, int pgp2mode );
+                IOBUF chain, byte *buf, size_t *ret_len);
+int copy_clearsig_text (IOBUF out, IOBUF inp, gcry_md_hd_t md,
+                        int escape_dash, int escape_from, int pgp2mode);
 
 /*-- progress.c --*/
 int progress_filter (void *opaque, int control,
-                    iobuf_t a, byte *buf, size_t *ret_len);
+                    IOBUF a, byte *buf, size_t *ret_len);
 void handle_progress (progress_filter_context_t *pfx,
-                     iobuf_t inp, const char *name);
+                     IOBUF inp, const char *name);
 
 #endif /*G10_FILTER_H*/
index 7ced327..8aab063 100644 (file)
@@ -1,6 +1,6 @@
 /* free-packet.c - cleanup stuff for packets
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
- *                                             Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
+ *               2005  Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -16,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include <string.h>
 #include <assert.h>
 
+#include "gpg.h"
 #include "packet.h"
-#include "iobuf.h"
-#include "mpi.h"
+#include "../common/iobuf.h"
 #include "util.h"
 #include "cipher.h"
-#include "memory.h"
-#include "options.h"
+#include "options.h" 
+
 
 void
 free_symkey_enc( PKT_symkey_enc *enc )
 {
-    xfree (enc);
+    xfree(enc);
 }
 
 void
@@ -45,10 +46,10 @@ free_pubkey_enc( PKT_pubkey_enc *enc )
     int n, i;
     n = pubkey_get_nenc( enc->pubkey_algo );
     if( !n )
-       mpi_release (enc->data[0]);
+       mpi_release(enc->data[0]);
     for(i=0; i < n; i++ )
-       mpi_release ( enc->data[i] );
-    xfree (enc);
+       mpi_release( enc->data[i] );
+    xfree(enc);
 }
 
 void
@@ -58,14 +59,21 @@ free_seckey_enc( PKT_signature *sig )
 
   n = pubkey_get_nsig( sig->pubkey_algo );
   if( !n )
-    mpi_release (sig->data[0]);
+    mpi_release(sig->data[0]);
   for(i=0; i < n; i++ )
-    mpi_release ( sig->data[i] );
-  
-  xfree (sig->revkey);
-  xfree (sig->hashed);
-  xfree (sig->unhashed);
-  xfree (sig);
+    mpi_release( sig->data[i] );
+
+  xfree(sig->revkey);
+  xfree(sig->hashed);
+  xfree(sig->unhashed);
+
+  if (sig->pka_info)
+    {
+      xfree (sig->pka_info->uri);
+      xfree (sig->pka_info);
+    }
+
+  xfree(sig);
 }
 
 
@@ -75,9 +83,9 @@ release_public_key_parts( PKT_public_key *pk )
     int n, i;
     n = pubkey_get_npkey( pk->pubkey_algo );
     if( !n )
-       mpi_release (pk->pkey[0]);
+       mpi_release(pk->pkey[0]);
     for(i=0; i < n; i++ ) {
-       mpi_release ( pk->pkey[i] );
+       mpi_release( pk->pkey[i] );
        pk->pkey[i] = NULL;
     }
     if (pk->prefs) {
@@ -89,7 +97,7 @@ release_public_key_parts( PKT_public_key *pk )
         pk->user_id = NULL;
     }
     if (pk->revkey) {
-        xfree (pk->revkey);
+        xfree(pk->revkey);
        pk->revkey=NULL;
        pk->numrevkeys=0;
     }
@@ -100,7 +108,7 @@ void
 free_public_key( PKT_public_key *pk )
 {
     release_public_key_parts( pk );
-    xfree (pk);
+    xfree(pk);
 }
 
 
@@ -150,7 +158,7 @@ copy_public_key ( PKT_public_key *d, PKT_public_key *s)
     int n, i;
 
     if( !d )
-       d = xmalloc (sizeof *d);
+       d = xmalloc(sizeof *d);
     memcpy( d, s, sizeof *d );
     d->user_id = scopy_user_id (s->user_id);
     d->prefs = copy_prefs (s->prefs);
@@ -164,7 +172,7 @@ copy_public_key ( PKT_public_key *d, PKT_public_key *s)
     if( !s->revkey && s->numrevkeys )
         BUG();
     if( s->numrevkeys ) {
-        d->revkey = xmalloc (sizeof(struct revocation_key)*s->numrevkeys);
+        d->revkey = xmalloc(sizeof(struct revocation_key)*s->numrevkeys);
         memcpy(d->revkey,s->revkey,sizeof(struct revocation_key)*s->numrevkeys);
     }
     else
@@ -194,13 +202,28 @@ copy_public_parts_to_secret_key( PKT_public_key *pk, PKT_secret_key *sk )
     sk->keyid[1]    = pk->keyid[1];
 }
 
+
+static pka_info_t *
+cp_pka_info (const pka_info_t *s)
+{
+  pka_info_t *d = xmalloc (sizeof *s + strlen (s->email));
+  
+  d->valid = s->valid;
+  d->checked = s->checked;
+  d->uri = s->uri? xstrdup (s->uri):NULL;
+  memcpy (d->fpr, s->fpr, sizeof s->fpr);
+  strcpy (d->email, s->email);
+  return d;
+}
+
+
 PKT_signature *
 copy_signature( PKT_signature *d, PKT_signature *s )
 {
     int n, i;
 
     if( !d )
-       d = xmalloc (sizeof *d);
+       d = xmalloc(sizeof *d);
     memcpy( d, s, sizeof *d );
     n = pubkey_get_nsig( s->pubkey_algo );
     if( !n )
@@ -209,6 +232,7 @@ copy_signature( PKT_signature *d, PKT_signature *s )
        for(i=0; i < n; i++ )
            d->data[i] = mpi_copy( s->data[i] );
     }
+    d->pka_info = s->pka_info? cp_pka_info (s->pka_info) : NULL;
     d->hashed = cp_subpktarea (s->hashed);
     d->unhashed = cp_subpktarea (s->unhashed);
     if(s->numrevkeys)
@@ -241,9 +265,9 @@ release_secret_key_parts( PKT_secret_key *sk )
 
     n = pubkey_get_nskey( sk->pubkey_algo );
     if( !n )
-       mpi_release (sk->skey[0]);
+       mpi_release(sk->skey[0]);
     for(i=0; i < n; i++ ) {
-       mpi_release ( sk->skey[i] );
+       mpi_release( sk->skey[i] );
        sk->skey[i] = NULL;
     }
 }
@@ -252,7 +276,7 @@ void
 free_secret_key( PKT_secret_key *sk )
 {
     release_secret_key_parts( sk );
-    xfree (sk);
+    xfree(sk);
 }
 
 PKT_secret_key *
@@ -261,29 +285,32 @@ copy_secret_key( PKT_secret_key *d, PKT_secret_key *s )
     int n, i;
 
     if( !d )
-       d = xmalloc (sizeof *d);
+       d = xmalloc_secure(sizeof *d);
+    else
+        release_secret_key_parts (d);
     memcpy( d, s, sizeof *d );
     n = pubkey_get_nskey( s->pubkey_algo );
     if( !n )
-       d->skey[0] = mpi_copy(s->skey[0]);
+       d->skey[0] = mpi_copy(s->skey[0]);
     else {
        for(i=0; i < n; i++ )
-           d->skey[i] = mpi_copy( s->skey[i] );
+           d->skey[i] = mpi_copy( s->skey[i] );
     }
+
     return d;
 }
 
 void
 free_comment( PKT_comment *rem )
 {
-    xfree (rem);
+    xfree(rem);
 }
 
 void
 free_attributes(PKT_user_id *uid)
 {
-  xfree (uid->attribs);
-  xfree (uid->attrib_data);
+  xfree(uid->attribs);
+  xfree(uid->attrib_data);
 
   uid->attribs=NULL;
   uid->attrib_data=NULL;
@@ -312,14 +339,14 @@ free_compressed( PKT_compressed *zd )
        while( iobuf_read( zd->buf, NULL, 1<<30 ) != -1 )
            ;
     }
-    xfree (zd);
+    xfree(zd);
 }
 
 void
 free_encrypted( PKT_encrypted *ed )
 {
     if( ed->buf ) { /* have to skip some bytes */
-       if( iobuf_in_block_mode(ed->buf) ) {
+       if( ed->is_partial ) {
            while( iobuf_read( ed->buf, NULL, 1<<30 ) != -1 )
                ;
        }
@@ -333,7 +360,7 @@ free_encrypted( PKT_encrypted *ed )
           }
        }
     }
-    xfree (ed);
+    xfree(ed);
 }
 
 
@@ -341,7 +368,7 @@ void
 free_plaintext( PKT_plaintext *pt )
 {
     if( pt->buf ) { /* have to skip some bytes */
-       if( iobuf_in_block_mode(pt->buf) ) {
+       if( pt->is_partial ) {
            while( iobuf_read( pt->buf, NULL, 1<<30 ) != -1 )
                ;
        }
@@ -355,7 +382,7 @@ free_plaintext( PKT_plaintext *pt )
           }
        }
     }
-    xfree (pt);
+    xfree(pt);
 }
 
 /****************
@@ -405,7 +432,7 @@ free_packet( PACKET *pkt )
        free_plaintext( pkt->pkt.plaintext );
        break;
       default:
-       xfree ( pkt->pkt.generic );
+       xfree( pkt->pkt.generic );
        break;
     }
     pkt->pkt.generic = NULL;
index f51b8f2..acd992c 100644 (file)
@@ -1,6 +1,6 @@
 /* getkey.c -  Get a key from the database
- * Copyright (C) 1998, 1999, 2000, 2001, 2002,
- *               2003 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ *               2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -16,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include "gpg.h"
 #include "util.h"
 #include "packet.h"
-#include "memory.h"
 #include "iobuf.h"
 #include "keydb.h"
 #include "options.h"
 #include "main.h"
 #include "trustdb.h"
 #include "i18n.h"
+#include "keyserver-internal.h"
 
-#define MAX_PK_CACHE_ENTRIES   200
-#define MAX_UID_CACHE_ENTRIES  200
+#define MAX_PK_CACHE_ENTRIES   PK_UID_CACHE_SIZE
+#define MAX_UID_CACHE_ENTRIES  PK_UID_CACHE_SIZE
 
 #if MAX_PK_CACHE_ENTRIES < 2
 #error We need the cache for key creation
 #endif
 
-
 struct getkey_ctx_s {
     int exact;
     KBNODE keyblock;
@@ -154,7 +154,7 @@ cache_public_key( PKT_public_key *pk )
        return;
     }
     pk_cache_entries++;
-    ce = xmalloc ( sizeof *ce );
+    ce = xmalloc( sizeof *ce );
     ce->next = pk_cache;
     pk_cache = ce;
     ce->pk = copy_public_key( NULL, pk );
@@ -164,6 +164,21 @@ cache_public_key( PKT_public_key *pk )
 }
 
 
+/* Return a const utf-8 string with the text "[User ID not found]".
+   This fucntion is required so that we don't need to switch gettext's
+   encoding temporary. */
+static const char *
+user_id_not_found_utf8 (void)
+{
+  static char *text;
+
+  if (!text)
+    text = native_to_utf8 (_("[User ID not found]"));
+  return text;
+}
+
+
+
 /*
  * Return the user ID from the given keyblock.
  * We use the primary uid flag which has been set by the merge_selfsigs
@@ -184,9 +199,7 @@ get_primary_uid ( KBNODE keyblock, size_t *uidlen )
             return k->pkt->pkt.user_id->name;
         }
     } 
-    /* fixme: returning translatable constants instead of a user ID is 
-     * not good because they are probably not utf-8 encoded. */
-    s = _("[User id not found]");
+    s = user_id_not_found_utf8 ();
     *uidlen = strlen (s);
     return s;
 }
@@ -218,7 +231,7 @@ cache_user_id( KBNODE keyblock )
     for (k=keyblock; k; k = k->next ) {
         if ( k->pkt->pkttype == PKT_PUBLIC_KEY
              || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
-            keyid_list_t a = xcalloc (1, sizeof *a );
+            keyid_list_t a = xmalloc_clear ( sizeof *a );
             /* Hmmm: For a long list of keyids it might be an advantage
              * to append the keys */
             keyid_from_pk( k->pkt->pkt.public_key, a->keyid );
@@ -252,10 +265,10 @@ cache_user_id( KBNODE keyblock )
        r = user_id_db;
        user_id_db = r->next;
         release_keyid_list ( r->keyids );
-       xfree (r);
+       xfree(r);
        uid_cache_entries--;
     }
-    r = xmalloc ( sizeof *r + uidlen-1 );
+    r = xmalloc( sizeof *r + uidlen-1 );
     r->keyids = keyids;
     r->len = uidlen;
     memcpy(r->name, uid, r->len);
@@ -275,7 +288,7 @@ getkey_disable_caches()
        for( ce = pk_cache; ce; ce = ce2 ) {
            ce2 = ce->next;
            free_public_key( ce->pk );
-           xfree ( ce );
+           xfree( ce );
        }
        pk_cache_disabled=1;
        pk_cache_entries = 0;
@@ -322,20 +335,25 @@ get_pubkey( PKT_public_key *pk, u32 *keyid )
     int rc = 0;
 
 #if MAX_PK_CACHE_ENTRIES
-    {  /* Try to get it from the cache */
+    if(pk)
+      {
+       /* Try to get it from the cache.  We don't do this when pk is
+          NULL as it does not guarantee that the user IDs are
+          cached. */
        pk_cache_entry_t ce;
-       for( ce = pk_cache; ce; ce = ce->next ) {
-           if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] ) {
-               if( pk )
-                   copy_public_key( pk, ce->pk );
+       for( ce = pk_cache; ce; ce = ce->next )
+         {
+           if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] )
+             {
+               copy_public_key( pk, ce->pk );
                return 0;
-           }
-       }
-    }
+             }
+         }
+      }
 #endif
     /* more init stuff */
     if( !pk ) {
-       pk = xcalloc (1, sizeof *pk );
+       pk = xmalloc_clear( sizeof *pk );
        internal++;
     }
 
@@ -363,7 +381,7 @@ get_pubkey( PKT_public_key *pk, u32 *keyid )
     if( !rc )
        goto leave;
 
-    rc = GPG_ERR_NO_PUBKEY;
+    rc = G10ERR_NO_PUBKEY;
 
   leave:
     if( !rc )
@@ -376,13 +394,15 @@ get_pubkey( PKT_public_key *pk, u32 *keyid )
 
 /* Get a public key and store it into the allocated pk.  This function
    differs from get_pubkey() in that it does not do a check of the key
-   to avoid recursion.  It should be used only in very certain cases.  */
+   to avoid recursion.  It should be used only in very certain cases.
+   It will only retrieve primary keys. */
 int
 get_pubkey_fast (PKT_public_key *pk, u32 *keyid)
 {
   int rc = 0;
   KEYDB_HANDLE hd;
   KBNODE keyblock;
+  u32 pkid[2];
   
   assert (pk);
 #if MAX_PK_CACHE_ENTRIES
@@ -406,29 +426,34 @@ get_pubkey_fast (PKT_public_key *pk, u32 *keyid)
   if (rc == -1)
     {
       keydb_release (hd);
-      return GPG_ERR_NO_PUBKEY;
+      return G10ERR_NO_PUBKEY;
     }
   rc = keydb_get_keyblock (hd, &keyblock);
   keydb_release (hd);
   if (rc) 
     {
-      log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc));
-      return GPG_ERR_NO_PUBKEY;
+      log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc));
+      return G10ERR_NO_PUBKEY;
     }
-  
+
   assert ( keyblock->pkt->pkttype == PKT_PUBLIC_KEY
            ||  keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY );
-  copy_public_key (pk, keyblock->pkt->pkt.public_key );
+
+  keyid_from_pk(keyblock->pkt->pkt.public_key,pkid);
+  if(keyid[0]==pkid[0] && keyid[1]==pkid[1])
+    copy_public_key (pk, keyblock->pkt->pkt.public_key );
+  else
+    rc=G10ERR_NO_PUBKEY;
+
   release_kbnode (keyblock);
 
   /* Not caching key here since it won't have all of the fields
      properly set. */
 
-  return 0;
+  return rc;
 }
 
 
-
 KBNODE
 get_pubkeyblock( u32 *keyid )
 {
@@ -496,7 +521,7 @@ get_seckey( PKT_secret_key *sk, u32 *keyid )
  * check and does not tell us whether the secret key is valid.  It
  * merely tells other whether there is some secret key.
  * Returns: 0 := key is available
- * GPG_ERR_NO_SECKEY := not availabe
+ * G10ERR_NO_SECKEY := not availabe
  */
 int
 seckey_available( u32 *keyid )
@@ -506,7 +531,7 @@ seckey_available( u32 *keyid )
 
     rc = keydb_search_kid (hd, keyid);
     if ( rc == -1 )
-        rc = GPG_ERR_NO_SECKEY;
+        rc = G10ERR_NO_SECKEY;
     keydb_release (hd);
     return rc;
 }
@@ -579,11 +604,13 @@ classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc )
        case 0:    /* empty string is an error */
            return 0;
 
+#if 0
        case '.':  /* an email address, compare from end */
            mode = KEYDB_SEARCH_MODE_MAILEND;
            s++;
             desc->u.name = s;
            break;
+#endif
 
        case '<':  /* an email address */
            mode = KEYDB_SEARCH_MODE_MAIL;
@@ -608,11 +635,13 @@ classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc )
             desc->u.name = s;
            break;
 
+#if 0
        case '+':  /* compare individual words */
            mode = KEYDB_SEARCH_MODE_WORDS;
            s++;
             desc->u.name = s;
            break;
+#endif
 
        case '#':  /* local user id */
             return 0; /* This is now obsolete and van't not be used anymore*/
@@ -653,7 +682,7 @@ classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc )
             }
 
            /* check if a hexadecimal number is terminated by EOS or blank */
-           if (hexlength && s[hexlength] && !spacep (s+hexlength)) {
+           if (hexlength && s[hexlength] && !spacep(s+hexlength)) {
                if (hexprefix)      /* a "0x" prefix without correct */
                    return 0;       /* termination is an error */
                else                /* The first chars looked like */
@@ -728,39 +757,60 @@ classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc )
 
 
 static int
-skip_disabled(void *dummy,u32 *keyid)
+skip_unusable(void *dummy,u32 *keyid,PKT_user_id *uid)
 {
-  int rc,disabled=0;
-  PKT_public_key *pk=xcalloc (1,sizeof(PKT_public_key));
+  int unusable=0;
+  KBNODE keyblock;
 
-  rc = get_pubkey(pk, keyid);
-  if(rc)
+  keyblock=get_pubkeyblock(keyid);
+  if(!keyblock)
     {
-      log_error("error checking disabled status of %08lX: %s\n",
-               (ulong)keyid[1],gpg_strerror (rc));
+      log_error("error checking usability status of %s\n",keystr(keyid));
       goto leave;
     }
-  disabled=pk_is_disabled(pk);
+
+  /* Is the user ID in question revoked/expired? */
+  if(uid)
+    {
+      KBNODE node;
+
+      for(node=keyblock;node;node=node->next)
+       {
+         if(node->pkt->pkttype==PKT_USER_ID)
+           {
+             if(cmp_user_ids(uid,node->pkt->pkt.user_id)==0
+                && (node->pkt->pkt.user_id->is_revoked
+                    || node->pkt->pkt.user_id->is_expired))
+               {
+                 unusable=1;
+                 break;
+               }
+           }
+       }
+    }
+
+  if(!unusable)
+    unusable=pk_is_disabled(keyblock->pkt->pkt.public_key);
 
  leave:
-  free_public_key(pk);
-  return disabled;
+  release_kbnode(keyblock);
+  return unusable;
 }
 
 /****************
  * Try to get the pubkey by the userid. This function looks for the
- * first pubkey certificate which has the given name in a user_id.
- * if pk/sk has the pubkey algo set, the function will only return
- * a pubkey with that algo.
- * The caller should provide storage for either the pk or the sk.
- * If ret_kb is not NULL the function will return the keyblock there.
+ * first pubkey certificate which has the given name in a user_id.  if
+ * pk/sk has the pubkey algo set, the function will only return a
+ * pubkey with that algo.  If namelist is NULL, the first key is
+ * returned.  The caller should provide storage for either the pk or
+ * the sk.  If ret_kb is not NULL the function will return the
+ * keyblock there.
  */
 
 static int
 key_byname( GETKEY_CTX *retctx, STRLIST namelist,
            PKT_public_key *pk, PKT_secret_key *sk,
-           int secmode, int include_disabled,
+           int secmode, int include_unusable,
             KBNODE *ret_kb, KEYDB_HANDLE *ret_kdbhd )
 {
     int rc = 0;
@@ -777,29 +827,43 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist,
     if (ret_kdbhd)
         *ret_kdbhd = NULL;
 
-    /* build the search context */
-    for(n=0, r=namelist; r; r = r->next )
-       n++;
-    ctx = xcalloc (1,sizeof *ctx + (n-1)*sizeof ctx->items );
-    ctx->nitems = n;
+    if(!namelist)
+      {
+       ctx = xmalloc_clear (sizeof *ctx);
+       ctx->nitems = 1;
+       ctx->items[0].mode=KEYDB_SEARCH_MODE_FIRST;
+       if(!include_unusable)
+         ctx->items[0].skipfnc=skip_unusable;
+      }
+    else
+      {
+       /* build the search context */
+       for(n=0, r=namelist; r; r = r->next )
+         n++;
 
-    for(n=0, r=namelist; r; r = r->next, n++ ) {
-       classify_user_id (r->d, &ctx->items[n]);
+       ctx = xmalloc_clear (sizeof *ctx + (n-1)*sizeof ctx->items );
+       ctx->nitems = n;
+
+       for(n=0, r=namelist; r; r = r->next, n++ )
+         {
+           classify_user_id (r->d, &ctx->items[n]);
         
-        if (ctx->items[n].exact)
-            ctx->exact = 1;
-        if (!ctx->items[n].mode) {
-           xfree (ctx);
-           return GPG_ERR_INV_USER_ID;
-       }
-       if(!include_disabled
-          && ctx->items[n].mode!=KEYDB_SEARCH_MODE_SHORT_KID
-          && ctx->items[n].mode!=KEYDB_SEARCH_MODE_LONG_KID
-          && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR16
-          && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR20
-          && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR)
-         ctx->items[n].skipfnc=skip_disabled;
-    }
+           if (ctx->items[n].exact)
+             ctx->exact = 1;
+           if (!ctx->items[n].mode)
+             {
+               xfree (ctx);
+               return G10ERR_INV_USER_ID;
+             }
+           if(!include_unusable
+              && ctx->items[n].mode!=KEYDB_SEARCH_MODE_SHORT_KID
+              && ctx->items[n].mode!=KEYDB_SEARCH_MODE_LONG_KID
+              && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR16
+              && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR20
+              && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR)
+             ctx->items[n].skipfnc=skip_unusable;
+         }
+      }
 
     ctx->kr_handle = keydb_new (secmode);
     if ( !ret_kb ) 
@@ -841,24 +905,141 @@ key_byname( GETKEY_CTX *retctx, STRLIST namelist,
     return rc;
 }
 
-/*
- * Find a public key from NAME and returh the keyblock or the key.
- * If ret_kdb is not NULL, the KEYDB handle used to locate this keyblock is
- * returned and the caller is responsible for closing it.
- */
+
+
+/* Find a public key from NAME and return the keyblock or the key.  If
+   ret_kdb is not NULL, the KEYDB handle used to locate this keyblock
+   is returned and the caller is responsible for closing it.  If a key
+   was not found and NAME is a valid RFC822 mailbox and PKA retrieval
+   has been enabled, we try to import the pkea via the PKA
+   mechanism. */
 int
 get_pubkey_byname (PKT_public_key *pk,
                   const char *name, KBNODE *ret_keyblock,
-                   KEYDB_HANDLE *ret_kdbhd, int include_disabled ) 
+                   KEYDB_HANDLE *ret_kdbhd, int include_unusable )
 {
-    int rc;
-    STRLIST namelist = NULL;
+  int rc;
+  STRLIST namelist = NULL;
 
-    add_to_strlist( &namelist, name );
-    rc = key_byname( NULL, namelist, pk, NULL, 0,
-                    include_disabled, ret_keyblock, ret_kdbhd);
-    free_strlist( namelist );
-    return rc;
+  add_to_strlist( &namelist, name );
+
+  rc = key_byname( NULL, namelist, pk, NULL, 0,
+                   include_unusable, ret_keyblock, ret_kdbhd);
+
+  /* If the requested name resembles a valid mailbox and automatic
+     retrieval has been enabled, we try to import the key. */
+
+  if (rc == G10ERR_NO_PUBKEY && is_valid_mailbox(name))
+    {
+      struct akl *akl;
+
+      for(akl=opt.auto_key_locate;akl;akl=akl->next)
+       {
+         unsigned char *fpr;
+         size_t fpr_len;
+
+         switch(akl->type)
+           {
+           case AKL_CERT:
+             glo_ctrl.in_auto_key_retrieve++;
+             rc=keyserver_import_cert(name,&fpr,&fpr_len);
+             glo_ctrl.in_auto_key_retrieve--;
+
+             if(rc==0)
+               log_info(_("automatically retrieved `%s' via %s\n"),
+                        name,"DNS CERT");
+             break;
+
+           case AKL_PKA:
+             glo_ctrl.in_auto_key_retrieve++;
+             rc=keyserver_import_pka(name,&fpr,&fpr_len);
+             glo_ctrl.in_auto_key_retrieve--;
+
+             if(rc==0)
+               log_info(_("automatically retrieved `%s' via %s\n"),
+                        name,"PKA");
+             break;
+
+           case AKL_LDAP:
+             glo_ctrl.in_auto_key_retrieve++;
+             rc=keyserver_import_ldap(name,&fpr,&fpr_len);
+             glo_ctrl.in_auto_key_retrieve--;
+
+             if(rc==0)
+               log_info(_("automatically retrieved `%s' via %s\n"),
+                        name,"LDAP");
+             break;
+
+           case AKL_KEYSERVER:
+             /* Strictly speaking, we don't need to only use a valid
+                mailbox for the getname search, but it helps cut down
+                on the problem of searching for something like "john"
+                and getting a whole lot of keys back. */
+             if(opt.keyserver)
+               {
+                 glo_ctrl.in_auto_key_retrieve++;
+                 rc=keyserver_import_name(name,&fpr,&fpr_len,opt.keyserver);
+                 glo_ctrl.in_auto_key_retrieve--;
+
+                 if(rc==0)
+                   log_info(_("automatically retrieved `%s' via %s\n"),
+                            name,opt.keyserver->uri);
+               }
+             break;
+
+           case AKL_SPEC:
+             {
+               struct keyserver_spec *keyserver;
+
+               keyserver=keyserver_match(akl->spec);
+               glo_ctrl.in_auto_key_retrieve++;
+               rc=keyserver_import_name(name,&fpr,&fpr_len,keyserver);
+               glo_ctrl.in_auto_key_retrieve--;
+
+               if(rc==0)
+                 log_info(_("automatically retrieved `%s' via %s\n"),
+                          name,akl->spec->uri);
+             }
+             break;
+           }
+
+         /* Use the fingerprint of the key that we actually fetched.
+            This helps prevent problems where the key that we fetched
+            doesn't have the same name that we used to fetch it.  In
+            the case of CERT and PKA, this is an actual security
+            requirement as the URL might point to a key put in by an
+            attacker.  By forcing the use of the fingerprint, we
+            won't use the attacker's key here. */
+         if(rc==0 && fpr)
+           {
+             int i;
+             char fpr_string[MAX_FINGERPRINT_LEN*2+1];
+
+             assert(fpr_len<=MAX_FINGERPRINT_LEN);
+
+             free_strlist(namelist);
+             namelist=NULL;
+
+             for(i=0;i<fpr_len;i++)
+               sprintf(fpr_string+2*i,"%02X",fpr[i]);
+
+             if(opt.verbose)
+               log_info("auto-key-locate found fingerprint %s\n",fpr_string);
+
+             add_to_strlist( &namelist, fpr_string );
+
+             xfree(fpr);
+           }
+
+         rc = key_byname( NULL, namelist, pk, NULL, 0,
+                          include_unusable, ret_keyblock, ret_kdbhd);
+         if(rc!=G10ERR_NO_PUBKEY)
+           break;
+       }
+    }
+
+  free_strlist( namelist );
+  return rc;
 }
 
 int
@@ -880,7 +1061,6 @@ get_pubkey_next( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock )
     return rc;
 }
 
-
 void
 get_pubkey_end( GETKEY_CTX ctx )
 {
@@ -888,17 +1068,15 @@ get_pubkey_end( GETKEY_CTX ctx )
         memset (&ctx->kbpos, 0, sizeof ctx->kbpos);
         keydb_release (ctx->kr_handle);
        if( !ctx->not_allocated )
-           xfree ( ctx );
+           xfree( ctx );
     }
 }
 
 
-
-
 /****************
  * Search for a key with the given fingerprint.
  * FIXME:
- * We should replace this with the _byname function.  This can be done
+ * We should replace this with the _byname function.  Thiscsan be done
  * by creating a userID conforming to the unified fingerprint style. 
  */
 int
@@ -926,7 +1104,7 @@ get_pubkey_byfprint( PKT_public_key *pk,
        get_pubkey_end( &ctx );
     }
     else
-       rc = GPG_ERR_GENERAL; /* Oops */
+       rc = G10ERR_GENERAL; /* Oops */
     return rc;
 }
 
@@ -956,14 +1134,14 @@ get_pubkey_byfprint_fast (PKT_public_key *pk,
   if (rc == -1)
     {
       keydb_release (hd);
-      return GPG_ERR_NO_PUBKEY;
+      return G10ERR_NO_PUBKEY;
     }
   rc = keydb_get_keyblock (hd, &keyblock);
   keydb_release (hd);
   if (rc) 
     {
-      log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc));
-      return GPG_ERR_NO_PUBKEY;
+      log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc));
+      return G10ERR_NO_PUBKEY;
     }
   
   assert ( keyblock->pkt->pkttype == PKT_PUBLIC_KEY
@@ -1002,7 +1180,7 @@ get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint,
        get_pubkey_end( &ctx );
     }
     else
-       rc = GPG_ERR_GENERAL; /* Oops */
+       rc = G10ERR_GENERAL; /* Oops */
 
     return rc;
 }
@@ -1014,44 +1192,31 @@ get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint,
  */
 static int
 get_seckey_byname2( GETKEY_CTX *retctx,
-                   PKT_secret_key *sk, const char *name, int unprotect,
-                   KBNODE *retblock )
+                   PKT_secret_key *sk, const char *name, int unprotect,
+                   KBNODE *retblock )
 {
-    STRLIST namelist = NULL;
-    int rc;
+  STRLIST namelist = NULL;
+  int rc,include_unusable=1;
 
-    if( !name && opt.def_secret_key && *opt.def_secret_key ) {
-       add_to_strlist( &namelist, opt.def_secret_key );
-       rc = key_byname( retctx, namelist, NULL, sk, 1, 1, retblock, NULL );
-    }
-    else if( !name ) { /* use the first one as default key */
-       struct getkey_ctx_s ctx;
-        KBNODE kb = NULL;
+  /* If we have no name, try to use the default secret key.  If we
+     have no default, we'll use the first usable one. */
 
-        assert (!retctx ); /* do we need this at all */
-        assert (!retblock);
-       memset( &ctx, 0, sizeof ctx );
-       ctx.not_allocated = 1;
-        ctx.kr_handle = keydb_new (1);
-       ctx.nitems = 1;
-       ctx.items[0].mode = KEYDB_SEARCH_MODE_FIRST;
-       rc = lookup( &ctx, &kb, 1 );
-        if (!rc && sk )
-            sk_from_block ( &ctx, sk, kb );
-        release_kbnode ( kb );
-       get_seckey_end( &ctx );
-    }
-    else {
-       add_to_strlist( &namelist, name );
-       rc = key_byname( retctx, namelist, NULL, sk, 1, 1, retblock, NULL );
-    }
+  if( !name && opt.def_secret_key && *opt.def_secret_key )
+    add_to_strlist( &namelist, opt.def_secret_key );
+  else if(name)
+    add_to_strlist( &namelist, name );
+  else
+    include_unusable=0;
 
-    free_strlist( namelist );
+  rc = key_byname( retctx, namelist, NULL, sk, 1, include_unusable,
+                  retblock, NULL );
 
-    if( !rc && unprotect )
-       rc = check_secret_key( sk, 0 );
+  free_strlist( namelist );
 
-    return rc;
+  if( !rc && unprotect )
+    rc = check_secret_key( sk, 0 );
+
+  return rc;
 }
 
 int 
@@ -1117,13 +1282,41 @@ get_seckey_byfprint( PKT_secret_key *sk,
         if (!rc && sk )
             sk_from_block ( &ctx, sk, kb );
         release_kbnode ( kb );
-       get_pubkey_end( &ctx );
+       get_seckey_end( &ctx );
     }
     else
-       rc = GPG_ERR_GENERAL; /* Oops */
+       rc = G10ERR_GENERAL; /* Oops */
     return rc;
 }
 
+
+/* Search for a secret key with the given fingerprint and return the
+   complete keyblock which may have more than only this key. */
+int
+get_seckeyblock_byfprint (KBNODE *ret_keyblock, const byte *fprint,
+                          size_t fprint_len )
+{
+  int rc;
+  struct getkey_ctx_s ctx;
+  
+  if (fprint_len != 20 && fprint_len == 16)
+    return G10ERR_GENERAL; /* Oops */
+    
+  memset (&ctx, 0, sizeof ctx);
+  ctx.not_allocated = 1;
+  ctx.kr_handle = keydb_new (1);
+  ctx.nitems = 1;
+  ctx.items[0].mode = (fprint_len==16
+                       ? KEYDB_SEARCH_MODE_FPR16
+                       : KEYDB_SEARCH_MODE_FPR20);
+  memcpy (ctx.items[0].u.fpr, fprint, fprint_len);
+  rc = lookup (&ctx, ret_keyblock, 1);
+  get_seckey_end (&ctx);
+  
+  return rc;
+}
+
+
 \f
 /************************************************
  ************* Merging stuff ********************
@@ -1220,6 +1413,59 @@ merge_keys_and_selfsig( KBNODE keyblock )
     }
 }
 
+static int
+parse_key_usage(PKT_signature *sig)
+{
+  int key_usage=0;
+  const byte *p;
+  size_t n;
+  byte flags;
+
+  p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_KEY_FLAGS,&n);
+  if(p && n)
+    {
+      /* first octet of the keyflags */
+      flags=*p;
+
+      if(flags & 1)
+       {
+         key_usage |= PUBKEY_USAGE_CERT;
+         flags&=~1;
+       }
+
+      if(flags & 2)
+       {
+         key_usage |= PUBKEY_USAGE_SIG;
+         flags&=~2;
+       }
+
+      /* We do not distinguish between encrypting communications and
+        encrypting storage. */
+      if(flags & (0x04|0x08))
+       {
+         key_usage |= PUBKEY_USAGE_ENC;
+         flags&=~(0x04|0x08);
+       }
+
+      if(flags & 0x20)
+       {
+         key_usage |= PUBKEY_USAGE_AUTH;
+         flags&=~0x20;
+       }
+
+      if(flags)
+       key_usage |= PUBKEY_USAGE_UNKNOWN;
+    }
+
+  /* We set PUBKEY_USAGE_UNKNOWN to indicate that this key has a
+     capability that we do not handle.  This serves to distinguish
+     between a zero key usage which we handle as the default
+     capabilities for that algorithm, and a usage that we do not
+     handle. */
+
+  return key_usage;
+}
+
 /*
  * Apply information from SIGNODE (which is the valid self-signature
  * associated with that UID) to the UIDNODE:
@@ -1238,32 +1484,28 @@ fixup_uidnode ( KBNODE uidnode, KBNODE signode, u32 keycreated )
     const byte *p, *sym, *hash, *zip;
     size_t n, nsym, nhash, nzip;
 
+    sig->flags.chosen_selfsig = 1; /* we chose this one */
     uid->created = 0; /* not created == invalid */
     if ( IS_UID_REV ( sig ) ) {
         uid->is_revoked = 1;
         return; /* has been revoked */
     }
 
+    uid->expiredate = sig->expiredate;
+
+    if(sig->flags.expired)
+      {
+       uid->is_expired = 1;
+       return; /* has expired */
+      }
+
     uid->created = sig->timestamp; /* this one is okay */
     uid->selfsigversion = sig->version;
     /* If we got this far, it's not expired :) */
     uid->is_expired = 0;
-    uid->expiredate = sig->expiredate;
 
     /* store the key flags in the helper variable for later processing */
-    uid->help_key_usage = 0;
-    p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n );
-    if ( p && n ) {
-        /* first octet of the keyflags */   
-        if ( (*p & 0x03) )
-            uid->help_key_usage |= PUBKEY_USAGE_SIG;
-        if ( (*p & 0x0c) )    
-            uid->help_key_usage |= PUBKEY_USAGE_ENC;
-        /* Note: we do not set the CERT flag here because it can be assumed
-         * that thre is no real policy to set it. */
-        if ( (*p & 0x20) )    
-            uid->help_key_usage |= PUBKEY_USAGE_AUTH;
-    }
+    uid->help_key_usage=parse_key_usage(sig);
 
     /* ditto or the key expiration */
     uid->help_key_expire = 0;
@@ -1318,20 +1560,29 @@ fixup_uidnode ( KBNODE uidnode, KBNODE signode, u32 keycreated )
     }
 
     /* see whether we have the MDC feature */
-    uid->mdc_feature = 0;
+    uid->flags.mdc = 0;
     p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n);
     if (p && n && (p[0] & 0x01))
-        uid->mdc_feature = 1;
+        uid->flags.mdc = 1;
 
     /* and the keyserver modify flag */
-    uid->ks_modify = 1;
+    uid->flags.ks_modify = 1;
     p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n);
     if (p && n && (p[0] & 0x80))
-        uid->ks_modify = 0;
+        uid->flags.ks_modify = 0;
+}
+
+static void
+sig_to_revoke_info(PKT_signature *sig,struct revoke_info *rinfo)
+{
+  rinfo->date = sig->timestamp;
+  rinfo->algo = sig->pubkey_algo;
+  rinfo->keyid[0] = sig->keyid[0];
+  rinfo->keyid[1] = sig->keyid[1];
 }
 
 static void
-merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
+merge_selfsigs_main(KBNODE keyblock, int *r_revoked, struct revoke_info *rinfo)
 {
     PKT_public_key *pk = NULL;
     KBNODE k;
@@ -1346,6 +1597,8 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
     byte sigversion = 0;
 
     *r_revoked = 0;
+    memset(rinfo,0,sizeof(*rinfo));
+
     if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY )
         BUG ();
     pk = keyblock->pkt->pkt.public_key;
@@ -1368,7 +1621,7 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
      */
 
     /* In case this key was already merged */
-    xfree (pk->revkey);
+    xfree(pk->revkey);
     pk->revkey=NULL;
     pk->numrevkeys=0;
 
@@ -1391,6 +1644,7 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
                      * that key.
                      */ 
                     *r_revoked = 1;
+                   sig_to_revoke_info(sig,rinfo);
                 }
                 else if ( IS_KEY_SIG (sig) ) {
                  /* Add any revocation keys onto the pk.  This is
@@ -1459,42 +1713,34 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
                               pk->numrevkeys*sizeof(struct revocation_key));
       }
 
-    if ( signode ) {
+    if ( signode )
+      {
         /* some information from a direct key signature take precedence
          * over the same information given in UID sigs.
          */
         PKT_signature *sig = signode->pkt->pkt.signature;
         const byte *p;
-        size_t n;
-        
-        p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n );
-        if ( p && n ) {
-            /* first octet of the keyflags */   
-            if ( (*p & 0x03) )
-                key_usage |= PUBKEY_USAGE_SIG;
-            if ( (*p & 0x0c) )    
-                key_usage |= PUBKEY_USAGE_ENC;
-            if ( (*p & 0x20) )    
-                key_usage |= PUBKEY_USAGE_AUTH;
-        }
+
+       key_usage=parse_key_usage(sig);
 
        p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL);
-       if ( p ) {
-         key_expire = keytimestamp + buffer_to_u32(p);
-         key_expire_seen = 1;
-        }
+       if ( p )
+         {
+           key_expire = keytimestamp + buffer_to_u32(p);
+           key_expire_seen = 1;
+         }
 
         /* mark that key as valid: one direct key signature should 
          * render a key as valid */
         pk->is_valid = 1;
-    }
+      }
 
     /* pass 1.5: look for key revocation signatures that were not made
        by the key (i.e. did a revocation key issue a revocation for
        us?).  Only bother to do this if there is a revocation key in
-       the first place. */
+       the first place and we're not revoked already. */
 
-    if(pk->revkey)
+    if(!*r_revoked && pk->revkey)
       for(k=keyblock; k && k->pkt->pkttype != PKT_USER_ID; k = k->next )
        {
          if ( k->pkt->pkttype == PKT_SIGNATURE )
@@ -1504,15 +1750,26 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
              if(IS_KEY_REV(sig) &&
                 (sig->keyid[0]!=kid[0] || sig->keyid[1]!=kid[1]))
                { 
-                 /* Failure here means the sig did not verify, is was
-                    not issued by a revocation key, or a revocation
-                    key loop was broken. */
+                 int rc=check_revocation_keys(pk,sig);
+                 if(rc==0)
+                   {
+                     *r_revoked=2;
+                     sig_to_revoke_info(sig,rinfo);
+                     /* don't continue checking since we can't be any
+                        more revoked than this */
+                     break;
+                   }
+                 else if(rc==G10ERR_NO_PUBKEY)
+                   pk->maybe_revoked=1;
 
-                 if(check_revocation_keys(pk,sig)==0)
-                   *r_revoked=1;
+                 /* A failure here means the sig did not verify, was
+                    not issued by a revocation key, or a revocation
+                    key loop was broken.  If a revocation key isn't
+                    findable, however, the key might be revoked and
+                    we don't know it. */
 
-                 /* In the future handle subkey and cert revocations?
-                     PGP doesn't, but it's in 2440. */
+                 /* TODO: In the future handle subkey and cert
+                     revocations?  PGP doesn't, but it's in 2440. */
                }
            }
        }
@@ -1537,7 +1794,8 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
                 if ( check_key_signature( keyblock, k, NULL ) )
                     ; /* signature did not verify */
                 else if ( (IS_UID_SIG (sig) || IS_UID_REV (sig))
-                          && sig->timestamp >= sigdate ) {
+                          && sig->timestamp >= sigdate )
+                 {
                     /* Note: we allow to invalidate cert revocations
                      * by a newer signature.  An attacker can't use this
                      * because a key should be revoced with a key revocation.
@@ -1546,20 +1804,13 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
                      * the same email address may become valid again (hired,
                      * fired, hired again).
                      */
-                   if(sig->flags.expired) {
-                     /* Expired uids don't get to be primary unless
-                         they are the only uid there is. */
-                     uidnode->pkt->pkt.user_id->is_primary=0;
-                     uidnode->pkt->pkt.user_id->is_expired=1;
-                     uidnode->pkt->pkt.user_id->expiredate=sig->expiredate;
-                   }
-                    else {
-                        sigdate = sig->timestamp;
-                        signode = k;
-                       if( sig->version > sigversion )
-                         sigversion = sig->version;
-                    }
-                }
+
+                   sigdate = sig->timestamp;
+                   signode = k;
+                   signode->pkt->pkt.signature->flags.chosen_selfsig=0;
+                   if( sig->version > sigversion )
+                     sigversion = sig->version;
+                 }
             }
         }
     }
@@ -1573,10 +1824,8 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
     if(!pk->is_valid && opt.allow_non_selfsigned_uid)
       {
        if(opt.verbose)
-         log_info(_("Invalid key %08lX made valid by "
-                    "--allow-non-selfsigned-uid\n"),
-                  (ulong)keyid_from_pk(pk,NULL));
-
+         log_info(_("Invalid key %s made valid by"
+                    " --allow-non-selfsigned-uid\n"),keystr_from_pk(pk));
        pk->is_valid = 1;
       }
 
@@ -1598,7 +1847,7 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
                  {
                    PKT_public_key *ultimate_pk;
 
-                   ultimate_pk=xcalloc (1,sizeof(*ultimate_pk));
+                   ultimate_pk=xmalloc_clear(sizeof(*ultimate_pk));
 
                     /* We don't want to use the full get_pubkey to
                        avoid infinite recursion in certain cases.
@@ -1608,7 +1857,7 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
                        ultimate trust flag.  */
                    if(get_pubkey_fast(ultimate_pk,sig->keyid)==0
                       && check_key_signature2(keyblock,k,ultimate_pk,
-                                              NULL, NULL, NULL, NULL)==0
+                                              NULL,NULL,NULL,NULL)==0
                       && get_ownertrust(ultimate_pk)==TRUST_ULTIMATE)
                      {
                        free_public_key(ultimate_pk);
@@ -1659,7 +1908,9 @@ merge_selfsigs_main( KBNODE keyblock, int *r_revoked )
         if ( x ) /* mask it down to the actual allowed usage */
             key_usage &= x; 
     }
-    pk->pubkey_usage = key_usage;
+
+    /* Whatever happens, it's a primary key, so it can certify. */
+    pk->pubkey_usage = key_usage|PUBKEY_USAGE_CERT;
 
     if ( !key_expire_seen ) {
         /* find the latest valid user ID with a key expiration set 
@@ -1799,7 +2050,6 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode )
     u32 keytimestamp = 0;
     u32 key_expire = 0;
     const byte *p;
-    size_t n;
 
     if ( subnode->pkt->pkttype != PKT_PUBLIC_SUBKEY )
         BUG ();
@@ -1834,47 +2084,47 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode )
                      problem is in the distribution.  Plus, PGP (7)
                      does this the same way.  */
                     subpk->is_revoked = 1;
+                   sig_to_revoke_info(sig,&subpk->revoked);
                     /* although we could stop now, we continue to 
                      * figure out other information like the old expiration
                      * time */
                 }
-                else if ( IS_SUBKEY_SIG (sig) && sig->timestamp >= sigdate ) {
+                else if ( IS_SUBKEY_SIG (sig) && sig->timestamp >= sigdate )
+                 {
                    if(sig->flags.expired)
-                        ; /* signature has expired - ignore it */
-                    else {
+                     ; /* signature has expired - ignore it */
+                    else
+                     {
                         sigdate = sig->timestamp;
                         signode = k;
-                    }
-                }
+                       signode->pkt->pkt.signature->flags.chosen_selfsig=0;
+                     }
+                 }
             }
         }
     }
 
-    if ( !signode ) {
-        return;  /* no valid key binding */
-    }
+    /* no valid key binding */
+    if ( !signode )
+      return;
 
-    subpk->is_valid = 1;
     sig = signode->pkt->pkt.signature;
-        
-    p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n );
-    if ( p && n ) {
-        /* first octet of the keyflags */   
-        if ( (*p & 0x03) )
-            key_usage |= PUBKEY_USAGE_SIG;
-        if ( (*p & 0x0c) )    
-            key_usage |= PUBKEY_USAGE_ENC;
-        if ( (*p & 0x20) )    
-            key_usage |= PUBKEY_USAGE_AUTH;
-    }
-    if ( !key_usage ) { /* no key flags at all: get it from the algo */
+    sig->flags.chosen_selfsig=1; /* so we know which selfsig we chose later */
+
+    key_usage=parse_key_usage(sig);
+    if ( !key_usage )
+      {
+       /* no key flags at all: get it from the algo */
         key_usage = openpgp_pk_algo_usage ( subpk->pubkey_algo );
-    }
-    else { /* check that the usage matches the usage as given by the algo */
+      }
+    else
+      {
+       /* check that the usage matches the usage as given by the algo */
         int x = openpgp_pk_algo_usage ( subpk->pubkey_algo );
         if ( x ) /* mask it down to the actual allowed usage */
-            key_usage &= x; 
-    }
+         key_usage &= x; 
+      }
+
     subpk->pubkey_usage = key_usage;
     
     p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL);
@@ -1884,8 +2134,56 @@ merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode )
         key_expire = 0;
     subpk->has_expired = key_expire >= curtime? 0 : key_expire;
     subpk->expiredate = key_expire;
-}
 
+    /* algo doesn't exist */
+    if(openpgp_pk_test_algo(subpk->pubkey_algo))
+      return;
+
+    subpk->is_valid = 1;
+
+    /* Find the first 0x19 embedded signature on our self-sig. */
+    if(subpk->backsig==0)
+      {
+       int seq=0;
+       size_t n;
+
+       /* We do this while() since there may be other embedded
+          signatures in the future.  We only want 0x19 here. */
+       while((p=enum_sig_subpkt(sig->hashed,
+                                SIGSUBPKT_SIGNATURE,&n,&seq,NULL)))
+         if(n>3 && ((p[0]==3 && p[2]==0x19) || (p[0]==4 && p[1]==0x19)))
+           break;
+
+       if(p==NULL)
+         {
+           seq=0;
+           /* It is safe to have this in the unhashed area since the
+              0x19 is located on the selfsig for convenience, not
+              security. */
+           while((p=enum_sig_subpkt(sig->unhashed,SIGSUBPKT_SIGNATURE,
+                                    &n,&seq,NULL)))
+             if(n>3 && ((p[0]==3 && p[2]==0x19) || (p[0]==4 && p[1]==0x19)))
+               break;
+         }
+
+       if(p)
+         {
+           PKT_signature *backsig=xmalloc_clear(sizeof(PKT_signature));
+           IOBUF backsig_buf=iobuf_temp_with_content(p,n);
+
+           if(parse_signature(backsig_buf,PKT_SIGNATURE,n,backsig)==0)
+             {
+               if(check_backsig(mainpk,subpk,backsig)==0)
+                 subpk->backsig=2;
+               else
+                 subpk->backsig=1;
+