Imported Upstream version 2.1.8 upstream/2.1.8
authorDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 9 Feb 2021 07:00:04 +0000 (16:00 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 9 Feb 2021 07:00:04 +0000 (16:00 +0900)
85 files changed:
NEWS
agent/agent.h
agent/command-ssh.c
agent/command.c
agent/cvt-openpgp.c
agent/findkey.c
agent/genkey.c
common/Makefile.am
common/argparse.c
common/iobuf.c
common/iobuf.h
common/status.h
common/t-iobuf.c [new file with mode: 0644]
common/utf8conv.c
configure.ac
dirmngr/server.c
doc/DETAILS
doc/HACKING
doc/gpg.texi
doc/help.de.txt
g10/Makefile.am
g10/call-agent.c
g10/cpr.c
g10/decrypt.c
g10/encrypt.c
g10/export.c
g10/getkey.c
g10/gpg.c
g10/import.c
g10/keydb.c
g10/keydb.h
g10/keyedit.c
g10/keygen.c
g10/keylist.c
g10/main.h
g10/options.h
g10/packet.h
g10/parse-packet.c
g10/passphrase.c
g10/revoke.c
g10/sign.c
g10/t-keydb-keyring.kbx [new file with mode: 0644]
g10/t-keydb.c [new file with mode: 0644]
g10/test-stubs.c [new file with mode: 0644]
g10/test.c [new file with mode: 0644]
g10/trustdb.c
g13/keyblob.h
kbx/Makefile.am
kbx/keybox-openpgp.c
kbx/keybox-search.c
kbx/keybox.h
po/POTFILES.in
po/ca.po
po/cs.po
po/da.po
po/de.po
po/el.po
po/eo.po
po/es.po
po/et.po
po/fi.po
po/fr.po
po/gl.po
po/hu.po
po/id.po
po/it.po
po/ja.po
po/nb.po
po/pl.po
po/pt.po
po/ro.po
po/ru.po
po/sk.po
po/sv.po
po/tr.po
po/uk.po
po/zh_CN.po
po/zh_TW.po
scd/app-openpgp.c
scd/command.c
sm/Makefile.am
sm/server.c
tests/openpgp/4gb-packet.asc [new file with mode: 0644]
tests/openpgp/4gb-packet.test [new file with mode: 0755]
tests/openpgp/Makefile.am

diff --git a/NEWS b/NEWS
index 8a2db79..e444f38 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,30 @@
+Noteworthy changes in version 2.1.8 (2015-09-10)
+------------------------------------------------
+
+ * gpg: Sending very large keys to the keyservers works again.
+
+ * gpg: Validity strings in key listings are now again translatable.
+
+ * gpg: Emit FAILURE status lines to help GPGME.
+
+ * gpg: Does not anymore link to Libksba to reduce dependencies.
+
+ * gpgsm: Export of secret keys via Assuan is now possible.
+
+ * agent: Raise the maximum passphrase length from 100 to 255 bytes.
+
+ * agent: Fix regression using EdDSA keys with ssh.
+
+ * Does not anymore use a build timestamp by default.
+
+ * The fallback encoding for broken locale settings changed
+   from Latin-1 to UTF-8.
+
+ * Many code cleanups and improved internal documentation.
+
+ * Various minor bug fixes.
+
+
 Noteworthy changes in version 2.1.7 (2015-08-11)
 ------------------------------------------------
 
index 958e3be..a1b3794 100644 (file)
 /* Maximum length of a digest.  */
 #define MAX_DIGEST_LEN 64
 
+/* The maximum length of a passphrase (in bytes).  Note: this is
+   further contrained by the Assuan line length (and any other text on
+   the same line).  However, the Assuan line length is 1k bytes so
+   this shouldn't be a problem in practice.  */
+#define MAX_PASSPHRASE_LEN 255
 
 
 /* A large struct name "opt" to keep global flags */
index 2a3037c..8868620 100644 (file)
@@ -1964,6 +1964,11 @@ ssh_key_to_blob (gcry_sexp_t sexp, int with_secret,
               err = gpg_error (GPG_ERR_INV_SEXP);
               goto out;
             }
+          if (*p_elems == 'q' && datalen)
+            { /* Remove the prefix 0x40.  */
+              data++;
+              datalen--;
+            }
           err = stream_write_string (stream, data, datalen);
           if (err)
             goto out;
@@ -3094,17 +3099,17 @@ ssh_identity_register (ctrl_t ctrl, ssh_key_type_spec_t *spec,
       goto out;
     }
 
-  pi = gcry_calloc_secure (2, sizeof (*pi) + 100 + 1);
+  pi = gcry_calloc_secure (2, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1);
   if (!pi)
     {
       err = gpg_error_from_syserror ();
       goto out;
     }
-  pi2 = pi + (sizeof *pi + 100 + 1);
-  pi->max_length = 100;
+  pi2 = pi + (sizeof *pi + MAX_PASSPHRASE_LEN + 1);
+  pi->max_length = MAX_PASSPHRASE_LEN + 1;
   pi->max_tries = 1;
   pi->with_repeat = 1;
-  pi2->max_length = 100;
+  pi2->max_length = MAX_PASSPHRASE_LEN + 1;
   pi2->max_tries = 1;
   pi2->check_cb = reenter_compare_cb;
   pi2->check_cb_arg = pi->pin;
index 62a4628..f09a2ff 100644 (file)
@@ -1541,6 +1541,9 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
             {
               char *response2;
 
+              if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK)
+                break;
+
               rc = agent_get_passphrase (ctrl, &response2, desc2, prompt,
                                          errtext, 0,
                                         cacheid, CACHE_MODE_USER);
index 8bf5873..6d22210 100644 (file)
@@ -918,10 +918,10 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
       struct pin_entry_info_s *pi;
       struct try_do_unprotect_arg_s pi_arg;
 
-      pi = xtrycalloc_secure (1, sizeof (*pi) + 100);
+      pi = xtrycalloc_secure (1, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1);
       if (!pi)
         return gpg_error_from_syserror ();
-      pi->max_length = 100;
+      pi->max_length = MAX_PASSPHRASE_LEN + 1;
       pi->min_digits = 0;  /* We want a real passphrase.  */
       pi->max_digits = 16;
       pi->max_tries = 3;
index e7cd79e..c49c37a 100644 (file)
@@ -450,10 +450,10 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
         }
     }
 
-  pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
+  pi = gcry_calloc_secure (1, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1);
   if (!pi)
     return gpg_error_from_syserror ();
-  pi->max_length = 100;
+  pi->max_length = MAX_PASSPHRASE_LEN + 1;
   pi->min_digits = 0;  /* we want a real passphrase */
   pi->max_digits = 16;
   pi->max_tries = 3;
index df17880..13858ca 100644 (file)
@@ -189,6 +189,9 @@ check_passphrase_constraints (ctrl_t ctrl, const char *pw,
   char *msg2 = NULL;
   char *msg3 = NULL;
 
+  if (ctrl && ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK)
+    return 0;
+
   if (!pw)
     pw = "";
 
@@ -371,13 +374,13 @@ agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
        return err;
     }
 
-  pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
-  pi2 = pi + (sizeof *pi + 100);
-  pi->max_length = 100;
+  pi = gcry_calloc_secure (2, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1);
+  pi2 = pi + (sizeof *pi + MAX_PASSPHRASE_LEN + 1);
+  pi->max_length = MAX_PASSPHRASE_LEN + 1;
   pi->max_tries = 3;
   pi->with_qualitybar = 1;
   pi->with_repeat = 1;
-  pi2->max_length = 100;
+  pi2->max_length = MAX_PASSPHRASE_LEN + 1;
   pi2->max_tries = 3;
   pi2->check_cb = reenter_compare_cb;
   pi2->check_cb_arg = pi->pin;
index c0df5ef..47facd4 100644 (file)
@@ -169,7 +169,7 @@ endif
 module_tests = t-stringhelp t-timestuff \
                t-convert t-percent t-gettime t-sysutils t-sexputil \
               t-session-env t-openpgp-oid t-ssh-utils \
-              t-mapstrings t-zb32 t-mbox-util
+              t-mapstrings t-zb32 t-mbox-util t-iobuf
 if !HAVE_W32CE_SYSTEM
 module_tests += t-exechelp
 endif
@@ -213,6 +213,7 @@ t_ssh_utils_LDADD = $(t_common_ldadd)
 t_mapstrings_LDADD = $(t_common_ldadd)
 t_zb32_LDADD = $(t_common_ldadd)
 t_mbox_util_LDADD = $(t_common_ldadd)
+t_iobuf_LDADD = $(t_common_ldadd)
 
 # System specific test
 if HAVE_W32_SYSTEM
index e9d98f0..53c20fc 100644 (file)
@@ -123,6 +123,13 @@ my_log_bug (const char *fmt, ...)
   abort ();
 }
 
+/* Return true if the native charset is utf-8.  */
+static int
+is_native_utf8 (void)
+{
+  return 1;
+}
+
 static char *
 my_trim_spaces (char *str)
 {
index 284408b..795ff11 100644 (file)
@@ -1,6 +1,7 @@
 /* iobuf.c  -  File Handling for OpenPGP.
  * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2006, 2007, 2008,
  *               2009, 2010, 2011  Free Software Foundation, Inc.
+ * Copyright (C) 2015  g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
@@ -132,19 +133,12 @@ typedef struct
 } sock_filter_ctx_t;
 #endif /*HAVE_W32_SYSTEM*/
 
-/* The first partial length header block must be of size 512
- * to make it easier (and efficienter) we use a min. block size of 512
- * for all chunks (but the last one) */
+/* The first partial length header block must be of size 512 to make
+ * it easier (and more efficient) we use a min. block size of 512 for
+ * all chunks (but the last one) */
 #define OP_MIN_PARTIAL_CHUNK     512
 #define OP_MIN_PARTIAL_CHUNK_2POW 9
 
-enum
-  {
-    BLOCK_FILTER_INPUT=1,
-    BLOCK_FILTER_OUTPUT=2,
-    BLOCK_FILTER_TEMP=3
-  };
-
 /* The context we use for the block filter (used to handle OpenPGP
    length information header).  */
 typedef struct
@@ -162,14 +156,26 @@ block_filter_ctx_t;
 
 
 /* Global flag to tell whether special file names are enabled.  See
-   gpg.c for an explanation of these file names.  FIXME: it does not
-   belong into the iobuf subsystem. */
+   gpg.c for an explanation of these file names.  FIXME: This does not
+   belong in the iobuf subsystem. */
 static int special_names_enabled;
 
 /* Local prototypes.  */
-static int underflow (iobuf_t a);
+static int underflow (iobuf_t a, int clear_pending_eof);
 static int translate_file_handle (int fd, int for_write);
 
+/* Sends any pending data to the filter's FILTER function.  Note: this
+   works on the filter and not on the whole pipeline.  That is,
+   iobuf_flush doesn't necessarily cause data to be written to any
+   underlying file; it just causes any data buffered at the filter A
+   to be sent to A's filter function.
+
+   If A is a IOBUF_OUTPUT_TEMP filter, then this also enlarges the
+   buffer by IOBUF_BUFFER_SIZE.
+
+   May only be called on an IOBUF_OUTPUT or IOBUF_OUTPUT_TEMP filters.  */
+static int filter_flush (iobuf_t a);
+
 
 \f
 /* This is a replacement for strcmp.  Under W32 it does not
@@ -395,7 +401,7 @@ fd_cache_close (const char *fname, gnupg_fd_t fp)
 }
 
 /*
- * Do an direct_open on FNAME but first try to reuse one from the fd_cache
+ * Do a direct_open on FNAME but first try to reuse one from the fd_cache
  */
 static gnupg_fd_t
 fd_cache_open (const char *fname, const char *mode)
@@ -434,30 +440,6 @@ fd_cache_open (const char *fname, const char *mode)
 }
 
 
-/****************
- * Read data from a file into buf which has an allocated length of *LEN.
- * return the number of read bytes in *LEN. OPAQUE is the FILE * of
- * the stream. A is not used.
- * control may be:
- * IOBUFCTRL_INIT: called just before the function is linked into the
- *                list of function. This can be used to prepare internal
- *                data structures of the function.
- * IOBUFCTRL_FREE: called just before the function is removed from the
- *                 list of functions and can be used to release internal
- *                 data structures or close a file etc.
- * IOBUFCTRL_UNDERFLOW: called by iobuf_underflow to fill the buffer
- *                 with new stuff. *RET_LEN is the available size of the
- *                 buffer, and should be set to the number of bytes
- *                 which were put into the buffer. The function
- *                 returns 0 to indicate success, -1 on EOF and
- *                 GPG_ERR_xxxxx for other errors.
- *
- * IOBUFCTRL_FLUSH: called by iobuf_flush() to write out the collected stuff.
- *                 *RET_LAN is the number of bytes in BUF.
- *
- * IOBUFCTRL_CANCEL: send to all filters on behalf of iobuf_cancel.  The
- *                 filter may take appropriate action on this message.
- */
 static int
 file_filter (void *opaque, int control, iobuf_t chain, byte * buf,
             size_t * ret_len)
@@ -1001,7 +983,7 @@ block_filter (void *opaque, int control, iobuf_t chain, byte * buffer,
        log_debug ("init block_filter %p\n", a);
       if (a->partial)
        a->count = 0;
-      else if (a->use == BLOCK_FILTER_INPUT)
+      else if (a->use == IOBUF_INPUT)
        a->count = a->size = 0;
       else
        a->count = a->size;     /* force first length bytes */
@@ -1015,7 +997,7 @@ block_filter (void *opaque, int control, iobuf_t chain, byte * buffer,
     }
   else if (control == IOBUFCTRL_FREE)
     {
-      if (a->use == BLOCK_FILTER_OUTPUT)
+      if (a->use == IOBUF_OUTPUT)
        {                       /* write the end markers */
          if (a->partial)
            {
@@ -1075,6 +1057,20 @@ block_filter (void *opaque, int control, iobuf_t chain, byte * buffer,
   return rc;
 }
 
+static const char *
+iobuf_desc (iobuf_t a)
+{
+  size_t dummy_len = 0;
+  const char *desc = "?";
+
+  if (! a || ! a->filter)
+    return desc;
+
+  a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL,
+            (byte *) & desc, &dummy_len);
+
+  return desc;
+}
 
 static void
 print_chain (iobuf_t a)
@@ -1083,15 +1079,9 @@ print_chain (iobuf_t a)
     return;
   for (; a; a = a->chain)
     {
-      size_t dummy_len = 0;
-      const char *desc = "[none]";
-
-      if (a->filter)
-       a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL,
-                  (byte *) & desc, &dummy_len);
 
       log_debug ("iobuf chain: %d.%d '%s' filter_eof=%d start=%d len=%d\n",
-                a->no, a->subno, desc?desc:"?", a->filter_eof,
+                a->no, a->subno, iobuf_desc (a), a->filter_eof,
                 (int) a->d.start, (int) a->d.len);
     }
 }
@@ -1103,23 +1093,19 @@ iobuf_print_chain (iobuf_t a)
   return 0;
 }
 
-/* Allocate a new io buffer, with no function assigned.
-
-   USE is the desired usage: BLOCK_FILTER_INPUT for input,
-   BLOCK_FILTER_OUTPUT for output, or BLOCK_FILTER_TEMP for a temp
-   buffer.
-
-   BUFSIZE is a suggested buffer size.
- */
 iobuf_t
 iobuf_alloc (int use, size_t bufsize)
 {
   iobuf_t a;
   static int number = 0;
 
-  assert (use == BLOCK_FILTER_INPUT
-         || use == BLOCK_FILTER_OUTPUT
-         || use == BLOCK_FILTER_TEMP);
+  assert (use == IOBUF_INPUT || use == IOBUF_INPUT_TEMP
+         || use == IOBUF_OUTPUT || use == IOBUF_OUTPUT_TEMP);
+  if (bufsize == 0)
+    {
+      log_bug ("iobuf_alloc() passed a bufsize of 0!\n");
+      bufsize = IOBUF_BUFFER_SIZE;
+    }
 
   a = xcalloc (1, sizeof *a);
   a->use = use;
@@ -1127,7 +1113,6 @@ iobuf_alloc (int use, size_t bufsize)
   a->d.size = bufsize;
   a->no = ++number;
   a->subno = 0;
-  a->opaque = NULL;
   a->real_fname = NULL;
   return a;
 }
@@ -1135,31 +1120,31 @@ iobuf_alloc (int use, size_t bufsize)
 int
 iobuf_close (iobuf_t a)
 {
-  iobuf_t a2;
+  iobuf_t a_chain;
   size_t dummy_len = 0;
   int rc = 0;
 
-  if (a && a->directfp)
+  for (; a; a = a_chain)
     {
-      fclose (a->directfp);
-      xfree (a->real_fname);
-      if (DBG_IOBUF)
-       log_debug ("iobuf_close -> %p\n", a->directfp);
-      return 0;
-    }
+      int rc2 = 0;
 
-  for (; a && !rc; a = a2)
-    {
-      a2 = a->chain;
-      if (a->use == BLOCK_FILTER_OUTPUT && (rc = iobuf_flush (a)))
-       log_error ("iobuf_flush failed on close: %s\n", gpg_strerror (rc));
+      a_chain = a->chain;
+
+      if (a->use == IOBUF_OUTPUT && (rc = filter_flush (a)))
+       log_error ("filter_flush failed on close: %s\n", gpg_strerror (rc));
 
       if (DBG_IOBUF)
-       log_debug ("iobuf-%d.%d: close '%s'\n", a->no, a->subno,
-                   a->desc?a->desc:"?");
-      if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_FREE,
-                                       a->chain, NULL, &dummy_len)))
+       log_debug ("iobuf-%d.%d: close '%s'\n",
+                  a->no, a->subno, iobuf_desc (a));
+
+      if (a->filter && (rc2 = a->filter (a->filter_ov, IOBUFCTRL_FREE,
+                                        a->chain, NULL, &dummy_len)))
        log_error ("IOBUFCTRL_FREE failed on close: %s\n", gpg_strerror (rc));
+      if (! rc && rc2)
+       /* Whoops!  An error occured.  Save it in RC if we haven't
+          already recorded an error.  */
+       rc = rc2;
+
       xfree (a->real_fname);
       if (a->d.buf)
        {
@@ -1181,7 +1166,7 @@ iobuf_cancel (iobuf_t a)
   char *remove_name = NULL;
 #endif
 
-  if (a && a->use == BLOCK_FILTER_OUTPUT)
+  if (a && a->use == IOBUF_OUTPUT)
     {
       s = iobuf_get_real_fname (a);
       if (s && *s)
@@ -1223,19 +1208,10 @@ iobuf_cancel (iobuf_t a)
 }
 
 
-/****************
- * create a temporary iobuf, which can be used to collect stuff
- * in an iobuf and later be written by iobuf_write_temp() to another
- * iobuf.
- */
 iobuf_t
-iobuf_temp ()
+iobuf_temp (void)
 {
-  iobuf_t a;
-
-  a = iobuf_alloc (3, IOBUF_BUFFER_SIZE);
-
-  return a;
+  return iobuf_alloc (IOBUF_OUTPUT_TEMP, IOBUF_BUFFER_SIZE);
 }
 
 iobuf_t
@@ -1244,7 +1220,8 @@ iobuf_temp_with_content (const char *buffer, size_t length)
   iobuf_t a;
   int i;
 
-  a = iobuf_alloc (3, length);
+  a = iobuf_alloc (IOBUF_INPUT_TEMP, length);
+  assert (length == a->d.size);
   /* memcpy (a->d.buf, buffer, length); */
   for (i=0; i < length; i++)
     a->d.buf[i] = buffer[i];
@@ -1280,8 +1257,6 @@ check_special_filename (const char *fname)
 }
 
 
-/* This fucntion returns true if FNAME indicates a PIPE (stdout or
-   stderr) or a special file name if those are enabled. */
 int
 iobuf_is_pipe_filename (const char *fname)
 {
@@ -1290,30 +1265,9 @@ iobuf_is_pipe_filename (const char *fname)
   return check_special_filename (fname) != -1;
 }
 
-
-/* Either open the file specified by the file descriptor FD or - if FD
-   is -1, the file with name FNAME.  As of now MODE is assumed to be
-   "rb" if FNAME is used.  In contrast to iobuf_fdopen the file
-   descriptor FD will not be closed during an iobuf_close.  */
-iobuf_t
-iobuf_open_fd_or_name (gnupg_fd_t fd, const char *fname, const char *mode)
-{
-  iobuf_t a;
-
-  if (fd == GNUPG_INVALID_FD)
-    a = iobuf_open (fname);
-  else
-    a = iobuf_fdopen_nc (FD2INT(fd), mode);
-  return a;
-}
-
-
-/****************
- * Create a head iobuf for reading from a file
- * returns: NULL if an error occures and sets errno
- */
-iobuf_t
-iobuf_open (const char *fname)
+static iobuf_t
+do_open (const char *fname, int special_filenames,
+        int use, const char *opentype, int mode700)
 {
   iobuf_t a;
   gnupg_fd_t fp;
@@ -1322,17 +1276,40 @@ iobuf_open (const char *fname)
   int print_only = 0;
   int fd;
 
-  if (!fname || (*fname == '-' && !fname[1]))
+  assert (use == IOBUF_INPUT || use == IOBUF_OUTPUT);
+
+  if (special_filenames
+      /* NULL or '-'.  */
+      && (!fname || (*fname == '-' && !fname[1])))
     {
-      fp = FD_FOR_STDIN;
-      fname = "[stdin]";
+      if (use == IOBUF_INPUT)
+       {
+         fp = FD_FOR_STDIN;
+         fname = "[stdin]";
+       }
+      else
+       {
+         fp = FD_FOR_STDOUT;
+         fname = "[stdout]";
+       }
       print_only = 1;
     }
-  else if ((fd = check_special_filename (fname)) != -1)
-    return iobuf_fdopen (translate_file_handle (fd, 0), "rb");
-  else if ((fp = fd_cache_open (fname, "rb")) == GNUPG_INVALID_FD)
+  else if (!fname)
     return NULL;
-  a = iobuf_alloc (1, IOBUF_BUFFER_SIZE);
+  else if (special_filenames && (fd = check_special_filename (fname)) != -1)
+    return iobuf_fdopen (translate_file_handle (fd, use == IOBUF_INPUT ? 0 : 1),
+                        opentype);
+  else
+    {
+      if (use == IOBUF_INPUT)
+       fp = fd_cache_open (fname, opentype);
+      else
+       fp = direct_open (fname, opentype, mode700);
+      if (fp == GNUPG_INVALID_FD)
+       return NULL;
+    }
+
+  a = iobuf_alloc (use, IOBUF_BUFFER_SIZE);
   fcx = xmalloc (sizeof *fcx + strlen (fname));
   fcx->fp = fp;
   fcx->print_only_name = print_only;
@@ -1341,15 +1318,32 @@ iobuf_open (const char *fname)
     a->real_fname = xstrdup (fname);
   a->filter = file_filter;
   a->filter_ov = fcx;
-  file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
   file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
   if (DBG_IOBUF)
-    log_debug ("iobuf-%d.%d: open '%s' fd=%d\n",
-              a->no, a->subno, fname, FD2INT (fcx->fp));
+    log_debug ("iobuf-%d.%d: open '%s' desc=%s fd=%d\n",
+              a->no, a->subno, fname, iobuf_desc (a), FD2INT (fcx->fp));
 
   return a;
 }
 
+iobuf_t
+iobuf_open (const char *fname)
+{
+  return do_open (fname, 1, IOBUF_INPUT, "rb", 0);
+}
+
+iobuf_t
+iobuf_create (const char *fname, int mode700)
+{
+  return do_open (fname, 1, IOBUF_OUTPUT, "wb", mode700);
+}
+
+iobuf_t
+iobuf_openrw (const char *fname)
+{
+  return do_open (fname, 0, IOBUF_OUTPUT, "r+b", 0);
+}
+
 
 static iobuf_t
 do_iobuf_fdopen (int fd, const char *mode, int keep_open)
@@ -1361,7 +1355,8 @@ do_iobuf_fdopen (int fd, const char *mode, int keep_open)
 
   fp = INT2FD (fd);
 
-  a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, IOBUF_BUFFER_SIZE);
+  a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT,
+                  IOBUF_BUFFER_SIZE);
   fcx = xmalloc (sizeof *fcx + 20);
   fcx->fp = fp;
   fcx->print_only_name = 1;
@@ -1369,7 +1364,6 @@ do_iobuf_fdopen (int fd, const char *mode, int keep_open)
   sprintf (fcx->fname, "[fd %d]", fd);
   a->filter = file_filter;
   a->filter_ov = fcx;
-  file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
   file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
   if (DBG_IOBUF)
     log_debug ("iobuf-%d.%d: fdopen%s '%s'\n",
@@ -1379,8 +1373,6 @@ do_iobuf_fdopen (int fd, const char *mode, int keep_open)
 }
 
 
-/* Create a head iobuf for reading or writing from/to a file Returns:
- * NULL and sets ERRNO if an error occured.  */
 iobuf_t
 iobuf_fdopen (int fd, const char *mode)
 {
@@ -1401,7 +1393,8 @@ iobuf_esopen (estream_t estream, const char *mode, int keep_open)
   file_es_filter_ctx_t *fcx;
   size_t len;
 
-  a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, IOBUF_BUFFER_SIZE);
+  a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT,
+                  IOBUF_BUFFER_SIZE);
   fcx = xtrymalloc (sizeof *fcx + 30);
   fcx->fp = estream;
   fcx->print_only_name = 1;
@@ -1409,7 +1402,6 @@ iobuf_esopen (estream_t estream, const char *mode, int keep_open)
   sprintf (fcx->fname, "[fd %p]", estream);
   a->filter = file_es_filter;
   a->filter_ov = fcx;
-  file_es_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
   file_es_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
   if (DBG_IOBUF)
     log_debug ("iobuf-%d.%d: esopen%s '%s'\n",
@@ -1426,14 +1418,14 @@ iobuf_sockopen (int fd, const char *mode)
   sock_filter_ctx_t *scx;
   size_t len;
 
-  a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, IOBUF_BUFFER_SIZE);
+  a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT,
+                  IOBUF_BUFFER_SIZE);
   scx = xmalloc (sizeof *scx + 25);
   scx->sock = fd;
   scx->print_only_name = 1;
   sprintf (scx->fname, "[sock %d]", fd);
   a->filter = sock_filter;
   a->filter_ov = scx;
-  sock_filter (scx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
   sock_filter (scx, IOBUFCTRL_INIT, NULL, NULL, &len);
   if (DBG_IOBUF)
     log_debug ("iobuf-%d.%d: sockopen '%s'\n", a->no, a->subno, scx->fname);
@@ -1444,78 +1436,6 @@ iobuf_sockopen (int fd, const char *mode)
   return a;
 }
 
-/****************
- * Create an iobuf for writing to a file; the file will be created.
- * With MODE700 set the file is created with that mode (Unix only).
- */
-iobuf_t
-iobuf_create (const char *fname, int mode700)
-{
-  iobuf_t a;
-  gnupg_fd_t fp;
-  file_filter_ctx_t *fcx;
-  size_t len;
-  int print_only = 0;
-  int fd;
-
-  if (!fname || (*fname == '-' && !fname[1]))
-    {
-      fp = FD_FOR_STDOUT;
-      fname = "[stdout]";
-      print_only = 1;
-    }
-  else if ((fd = check_special_filename (fname)) != -1)
-    return iobuf_fdopen (translate_file_handle (fd, 1), "wb");
-  else if ((fp = direct_open (fname, "wb", mode700)) == GNUPG_INVALID_FD)
-    return NULL;
-  a = iobuf_alloc (2, IOBUF_BUFFER_SIZE);
-  fcx = xmalloc (sizeof *fcx + strlen (fname));
-  fcx->fp = fp;
-  fcx->print_only_name = print_only;
-  strcpy (fcx->fname, fname);
-  if (!print_only)
-    a->real_fname = xstrdup (fname);
-  a->filter = file_filter;
-  a->filter_ov = fcx;
-  file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
-  file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
-  if (DBG_IOBUF)
-    log_debug ("iobuf-%d.%d: create '%s'\n", a->no, a->subno,
-               a->desc?a->desc:"?");
-
-  return a;
-}
-
-
-iobuf_t
-iobuf_openrw (const char *fname)
-{
-  iobuf_t a;
-  gnupg_fd_t fp;
-  file_filter_ctx_t *fcx;
-  size_t len;
-
-  if (!fname)
-    return NULL;
-  else if ((fp = direct_open (fname, "r+b", 0)) == GNUPG_INVALID_FD)
-    return NULL;
-  a = iobuf_alloc (2, IOBUF_BUFFER_SIZE);
-  fcx = xmalloc (sizeof *fcx + strlen (fname));
-  fcx->fp = fp;
-  strcpy (fcx->fname, fname);
-  a->real_fname = xstrdup (fname);
-  a->filter = file_filter;
-  a->filter_ov = fcx;
-  file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
-  file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
-  if (DBG_IOBUF)
-    log_debug ("iobuf-%d.%d: openrw '%s'\n", a->no, a->subno,
-               a->desc?a->desc:"?");
-
-  return a;
-}
-
-
 int
 iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval)
 {
@@ -1526,8 +1446,7 @@ iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval)
          anymore.  */
       if (DBG_IOBUF)
        log_debug ("iobuf-%d.%d: ioctl '%s' keep_open=%d\n",
-                  a ? a->no : -1, a ? a->subno : -1,
-                   a && a->desc ? a->desc : "?",
+                  a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a),
                   intval);
       for (; a; a = a->chain)
        if (!a->chain && a->filter == file_filter)
@@ -1561,8 +1480,7 @@ iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval)
     {
       if (DBG_IOBUF)
        log_debug ("iobuf-%d.%d: ioctl '%s' no_cache=%d\n",
-                  a ? a->no : -1, a ? a->subno : -1,
-                   a && a->desc? a->desc : "?",
+                  a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a),
                   intval);
       for (; a; a = a->chain)
        if (!a->chain && a->filter == file_filter)
@@ -1621,10 +1539,7 @@ iobuf_push_filter2 (iobuf_t a,
   size_t dummy_len = 0;
   int rc = 0;
 
-  if (a->directfp)
-    BUG ();
-
-  if (a->use == BLOCK_FILTER_OUTPUT && (rc = iobuf_flush (a)))
+  if (a->use == IOBUF_OUTPUT && (rc = filter_flush (a)))
     return rc;
 
   if (a->subno >= MAX_NESTING_FILTER)
@@ -1633,11 +1548,51 @@ iobuf_push_filter2 (iobuf_t a,
       return GPG_ERR_BAD_DATA;
     }
 
-  /* make a copy of the current stream, so that
-   * A is the new stream and B the original one.
-   * The contents of the buffers are transferred to the
-   * new stream.
-   */
+  /* We want to create a new filter and put it in front of A.  A
+     simple implementation would do:
+
+       b = iobuf_alloc (...);
+       b->chain = a;
+       return a;
+
+     This is a bit problematic: A is the head of the pipeline and
+     there are potentially many pointers to it.  Requiring the caller
+     to update all of these pointers is a burden.
+
+     An alternative implementation would add a level of indirection.
+     For instance, we could use a pipeline object, which contains a
+     pointer to the first filter in the pipeline.  This is not what we
+     do either.
+
+     Instead, we allocate a new buffer (B) and copy the first filter's
+     state into that and use the initial buffer (A) for the new
+     filter.  One limitation of this approach is that it is not
+     practical to maintain a pointer to a specific filter's state.
+
+     Before:
+
+           A
+           |
+           v 0x100               0x200
+           +----------+          +----------+
+           | filter x |--------->| filter y |---->....
+           +----------+          +----------+
+
+     After:           B
+                      |
+                      v 0x300
+                      +----------+
+           A          | filter x |
+           |          +----------+
+           v 0x100    ^          v 0x200
+           +----------+          +----------+
+           | filter w |          | filter y |---->....
+           +----------+          +----------+
+
+     Note: filter x's address changed from 0x100 to 0x300, but A still
+     points to the head of the pipeline.
+  */
+
   b = xmalloc (sizeof *b);
   memcpy (b, a, sizeof *b);
   /* fixme: it is stupid to keep a copy of the name at every level
@@ -1649,31 +1604,50 @@ iobuf_push_filter2 (iobuf_t a,
   a->filter_ov = NULL;
   a->filter_ov_owner = 0;
   a->filter_eof = 0;
-  if (a->use == BLOCK_FILTER_TEMP)
-    /* make a write stream from a temp stream */
-    a->use = BLOCK_FILTER_OUTPUT;
+  if (a->use == IOBUF_OUTPUT_TEMP)
+    /* A TEMP filter buffers any data sent to it; it does not forward
+       any data down the pipeline.  If we add a new filter to the
+       pipeline, it shouldn't also buffer data.  It should send it
+       downstream to be buffered.  Thus, the correct type for a filter
+       added in front of an IOBUF_OUTPUT_TEMP filter is IOBUF_OUPUT, not
+       IOBUF_OUTPUT_TEMP.  */
+    {
+      a->use = IOBUF_OUTPUT;
 
-  if (a->use == BLOCK_FILTER_OUTPUT)
-    {                          /* allocate a fresh buffer for the
-                                   original stream */
-      b->d.buf = xmalloc (a->d.size);
-      b->d.len = 0;
-      b->d.start = 0;
+      /* When pipeline is written to, the temp buffer's size is
+        increased accordingly.  We don't need to allocate a 10 MB
+        buffer for a non-terminal filter.  Just use the default
+        size.  */
+      a->d.size = IOBUF_BUFFER_SIZE;
     }
-  else
-    {                          /* allocate a fresh buffer for the new
-                                   stream */
-      a->d.buf = xmalloc (a->d.size);
-      a->d.len = 0;
-      a->d.start = 0;
+  else if (a->use == IOBUF_INPUT_TEMP)
+    /* Same idea as above.  */
+    {
+      a->use = IOBUF_INPUT;
+      a->d.size = IOBUF_BUFFER_SIZE;
     }
+
+  /* The new filter (A) gets a new buffer.
+
+     If the pipeline is an output or temp pipeline, then giving the
+     buffer to the new filter means that data that was written before
+     the filter was pushed gets sent to the filter.  That's clearly
+     wrong.
+
+     If the pipeline is an input pipeline, then giving the buffer to
+     the new filter (A) means that data that has read from (B), but
+     not yet read from the pipeline won't be processed by the new
+     filter (A)!  That's certainly not what we want.  */
+  a->d.buf = xmalloc (a->d.size);
+  a->d.len = 0;
+  a->d.start = 0;
+
   /* disable nlimit for the new stream */
   a->ntotal = b->ntotal + b->nbytes;
   a->nlimit = a->nbytes = 0;
-  a->nofast &= ~1;
+  a->nofast = 0;
   /* make a link from the new stream to the original stream */
   a->chain = b;
-  a->opaque = b->opaque;
 
   /* setup the function on the new stream */
   a->filter = f;
@@ -1681,12 +1655,11 @@ iobuf_push_filter2 (iobuf_t a,
   a->filter_ov_owner = rel_ov;
 
   a->subno = b->subno + 1;
-  f (ov, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &dummy_len);
 
   if (DBG_IOBUF)
     {
-      log_debug ("iobuf-%d.%d: push '%s'\n", a->no, a->subno,
-                 a->desc?a->desc:"?");
+      log_debug ("iobuf-%d.%d: push '%s'\n",
+                a->no, a->subno, iobuf_desc (a));
       print_chain (a);
     }
 
@@ -1709,12 +1682,15 @@ pop_filter (iobuf_t a, int (*f) (void *opaque, int control,
   size_t dummy_len = 0;
   int rc = 0;
 
-  if (a->directfp)
-    BUG ();
-
   if (DBG_IOBUF)
-    log_debug ("iobuf-%d.%d: pop '%s'\n", a->no, a->subno,
-               a->desc?a->desc:"?");
+    log_debug ("iobuf-%d.%d: pop '%s'\n",
+              a->no, a->subno, iobuf_desc (a));
+  if (a->use == IOBUF_INPUT_TEMP || a->use == IOBUF_OUTPUT_TEMP)
+    {
+      /* This should be the last filter in the pipeline.  */
+      assert (! a->chain);
+      return 0;
+    }
   if (!a->filter)
     {                          /* this is simple */
       b = a->chain;
@@ -1732,9 +1708,9 @@ pop_filter (iobuf_t a, int (*f) (void *opaque, int control,
     log_bug ("pop_filter(): filter function not found\n");
 
   /* flush this stream if it is an output stream */
-  if (a->use == BLOCK_FILTER_OUTPUT && (rc = iobuf_flush (b)))
+  if (a->use == IOBUF_OUTPUT && (rc = filter_flush (b)))
     {
-      log_error ("iobuf_flush failed in pop_filter: %s\n", gpg_strerror (rc));
+      log_error ("filter_flush failed in pop_filter: %s\n", gpg_strerror (rc));
       return rc;
     }
   /* and tell the filter to free it self */
@@ -1785,23 +1761,51 @@ pop_filter (iobuf_t a, int (*f) (void *opaque, int control,
  * the first byte or -1 on EOF.
  */
 static int
-underflow (iobuf_t a)
+underflow (iobuf_t a, int clear_pending_eof)
 {
   size_t len;
   int rc;
 
-  assert (a->d.start == a->d.len);
-  if (a->use == BLOCK_FILTER_TEMP)
-    return -1;                 /* EOF because a temp buffer can't do an underflow */
+  if (DBG_IOBUF)
+    log_debug ("iobuf-%d.%d: underflow: buffer size: %d; still buffered: %d => space for %d bytes\n",
+              a->no, a->subno,
+              (int) a->d.size, (int) (a->d.len - a->d.start),
+              (int) (a->d.size - (a->d.len - a->d.start)));
+
+  if (a->use == IOBUF_INPUT_TEMP)
+    /* By definition, there isn't more data to read into the
+       buffer.  */
+    return -1;
+
+  assert (a->use == IOBUF_INPUT);
+
+  /* If there is still some buffered data, then move it to the start
+     of the buffer and try to fill the end of the buffer.  (This is
+     useful if we are called from iobuf_peek().)  */
+  assert (a->d.start <= a->d.len);
+  a->d.len -= a->d.start;
+  memmove (a->d.buf, &a->d.buf[a->d.start], a->d.len);
+  a->d.start = 0;
 
-  if (a->filter_eof)
+  if (a->d.len == 0 && a->filter_eof)
+    /* The last time we tried to read from this filter, we got an EOF.
+       We couldn't return the EOF, because there was buffered data.
+       Since there is no longer any buffered data, return the
+       error.  */
     {
+      if (DBG_IOBUF)
+       log_debug ("iobuf-%d.%d: underflow: eof (pending eof)\n",
+                  a->no, a->subno);
+      if (! clear_pending_eof)
+       return -1;
+
       if (a->chain)
+       /* A filter follows this one.  Free this filter.  */
        {
          iobuf_t b = a->chain;
          if (DBG_IOBUF)
-           log_debug ("iobuf-%d.%d: pop '%s' in underflow\n",
-                      a->no, a->subno, a->desc?a->desc:"?");
+           log_debug ("iobuf-%d.%d: filter popped (pending EOF returned)\n",
+                      a->no, a->subno);
          xfree (a->d.buf);
          xfree (a->real_fname);
          memcpy (a, b, sizeof *a);
@@ -1810,135 +1814,138 @@ underflow (iobuf_t a)
        }
       else
        a->filter_eof = 0;      /* for the top level filter */
-      if (DBG_IOBUF)
-       log_debug ("iobuf-%d.%d: underflow: eof (due to filter eof)\n",
-                  a->no, a->subno);
       return -1;               /* return one(!) EOF */
     }
-  if (a->error)
+
+  if (a->d.len == 0 && a->error)
+    /* The last time we tried to read from this filter, we got an
+       error.  We couldn't return the error, because there was
+       buffered data.  Since there is no longer any buffered data,
+       return the error.  */
     {
       if (DBG_IOBUF)
-       log_debug ("iobuf-%d.%d: error\n", a->no, a->subno);
+       log_debug ("iobuf-%d.%d: pending error (%s) returned\n",
+                  a->no, a->subno, gpg_strerror (a->error));
       return -1;
     }
 
-  if (a->directfp)
+  if (a->filter && ! a->filter_eof && ! a->error)
+    /* We have a filter function and the last time we tried to read we
+       didn't get an EOF or an error.  Try to fill the buffer.  */
     {
-      FILE *fp = a->directfp;
-
-      len = fread (a->d.buf, 1, a->d.size, fp);
-      if (len < a->d.size)
-       {
-         if (ferror (fp))
-           a->error = gpg_error_from_syserror ();
-       }
-      a->d.len = len;
-      a->d.start = 0;
-      return len ? a->d.buf[a->d.start++] : -1;
-    }
-
-
-  if (a->filter)
-    {
-      len = a->d.size;
+      /* Be careful to account for any buffered data.  */
+      len = a->d.size - a->d.len;
       if (DBG_IOBUF)
-       log_debug ("iobuf-%d.%d: underflow: req=%lu\n",
+       log_debug ("iobuf-%d.%d: underflow: A->FILTER (%lu bytes)\n",
                   a->no, a->subno, (ulong) len);
-      rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
-                     a->d.buf, &len);
+      if (len == 0)
+       /* There is no space for more data.  Don't bother calling
+          A->FILTER.  */
+       rc = 0;
+      else
+       rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
+                       &a->d.buf[a->d.len], &len);
+      a->d.len += len;
+
       if (DBG_IOBUF)
-       {
-         log_debug ("iobuf-%d.%d: underflow: got=%lu rc=%d\n",
-                    a->no, a->subno, (ulong) len, rc);
+       log_debug ("iobuf-%d.%d: A->FILTER() returned rc=%d (%s), read %lu bytes\n",
+                  a->no, a->subno,
+                  rc, rc == 0 ? "ok" : rc == -1 ? "EOF" : gpg_strerror (rc),
+                  (ulong) len);
 /*         if( a->no == 1 ) */
 /*                   log_hexdump ("     data:", a->d.buf, len); */
-       }
-      if (a->use == BLOCK_FILTER_INPUT && rc == -1)
-       {                       /* EOF: we can remove the filter */
+
+      if (rc == -1)
+       /* EOF.  */
+       {
          size_t dummy_len = 0;
 
-         /* and tell the filter to free itself */
+         /* Tell the filter to free itself */
          if ((rc = a->filter (a->filter_ov, IOBUFCTRL_FREE, a->chain,
                               NULL, &dummy_len)))
            log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc));
+
+         /* Free everything except for the internal buffer.  */
          if (a->filter_ov && a->filter_ov_owner)
-           {
-             xfree (a->filter_ov);
-             a->filter_ov = NULL;
-           }
-         a->filter = NULL;
-         a->desc = NULL;
+           xfree (a->filter_ov);
          a->filter_ov = NULL;
+         a->filter = NULL;
          a->filter_eof = 1;
-         if (!len && a->chain)
+
+         if (clear_pending_eof && a->d.len == 0 && a->chain)
+           /* We don't need to keep this filter around at all:
+
+                - we got an EOF
+                - we have no buffered data
+                - a filter follows this one.
+
+             Unlink this filter.  */
            {
              iobuf_t b = a->chain;
              if (DBG_IOBUF)
-               log_debug ("iobuf-%d.%d: pop in underflow (!len)\n",
+               log_debug ("iobuf-%d.%d: pop in underflow (nothing buffered, got EOF)\n",
                           a->no, a->subno);
              xfree (a->d.buf);
              xfree (a->real_fname);
              memcpy (a, b, sizeof *a);
              xfree (b);
+
              print_chain (a);
+
+             return -1;
            }
+         else if (a->d.len == 0)
+           /* We can't unlink this filter (it is the only one in the
+              pipeline), but we can immediately return EOF.  */
+           return -1;
        }
       else if (rc)
-       a->error = rc;
-
-      if (!len)
+       /* Record the error.  */
        {
-         if (DBG_IOBUF)
-           log_debug ("iobuf-%d.%d: underflow: eof\n", a->no, a->subno);
-         return -1;
+         a->error = rc;
+
+         if (a->d.len == 0)
+           /* There is no buffered data.  Immediately return EOF.  */
+           return -1;
        }
-      a->d.len = len;
-      a->d.start = 0;
-      return a->d.buf[a->d.start++];
-    }
-  else
-    {
-      if (DBG_IOBUF)
-       log_debug ("iobuf-%d.%d: underflow: eof (no filter)\n",
-                  a->no, a->subno);
-      return -1;               /* no filter; return EOF */
     }
+
+  assert (a->d.start <= a->d.len);
+  if (a->d.start < a->d.len)
+    return a->d.buf[a->d.start++];
+
+  /* EOF.  */
+  return -1;
 }
 
 
-int
-iobuf_flush (iobuf_t a)
+static int
+filter_flush (iobuf_t a)
 {
   size_t len;
   int rc;
 
-  if (a->directfp)
-    return 0;
-
-  if (a->use == BLOCK_FILTER_TEMP)
+  if (a->use == IOBUF_OUTPUT_TEMP)
     {                          /* increase the temp buffer */
-      unsigned char *newbuf;
       size_t newsize = a->d.size + IOBUF_BUFFER_SIZE;
 
       if (DBG_IOBUF)
        log_debug ("increasing temp iobuf from %lu to %lu\n",
                   (ulong) a->d.size, (ulong) newsize);
-      newbuf = xmalloc (newsize);
-      memcpy (newbuf, a->d.buf, a->d.len);
-      xfree (a->d.buf);
-      a->d.buf = newbuf;
+
+      a->d.buf = xrealloc (a->d.buf, newsize);
       a->d.size = newsize;
       return 0;
     }
-  else if (a->use != BLOCK_FILTER_OUTPUT)
+  else if (a->use != IOBUF_OUTPUT)
     log_bug ("flush on non-output iobuf\n");
   else if (!a->filter)
-    log_bug ("iobuf_flush: no filter\n");
+    log_bug ("filter_flush: no filter\n");
   len = a->d.len;
   rc = a->filter (a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len);
   if (!rc && len != a->d.len)
     {
-      log_info ("iobuf_flush did not write all!\n");
+      log_info ("filter_flush did not write all!\n");
       rc = GPG_ERR_INTERNAL;
     }
   else if (rc)
@@ -1949,14 +1956,19 @@ iobuf_flush (iobuf_t a)
 }
 
 
-/****************
- * Read a byte from the iobuf; returns -1 on EOF
- */
 int
 iobuf_readbyte (iobuf_t a)
 {
   int c;
 
+  if (a->use == IOBUF_OUTPUT || a->use == IOBUF_OUTPUT_TEMP)
+    {
+      log_bug ("iobuf_readbyte called on a non-INPUT pipeline!\n");
+      return -1;
+    }
+
+  assert (a->d.start <= a->d.len);
+
   if (a->nlimit && a->nbytes >= a->nlimit)
     return -1;                 /* forced EOF */
 
@@ -1964,9 +1976,14 @@ iobuf_readbyte (iobuf_t a)
     {
       c = a->d.buf[a->d.start++];
     }
-  else if ((c = underflow (a)) == -1)
+  else if ((c = underflow (a, 1)) == -1)
     return -1;                 /* EOF */
 
+  assert (a->d.start <= a->d.len);
+
+  /* Note: if underflow doesn't return EOF, then it returns the first
+     byte that was read and advances a->d.start appropriately.  */
+
   a->nbytes++;
   return c;
 }
@@ -1978,6 +1995,12 @@ iobuf_read (iobuf_t a, void *buffer, unsigned int buflen)
   unsigned char *buf = (unsigned char *)buffer;
   int c, n;
 
+  if (a->use == IOBUF_OUTPUT || a->use == IOBUF_OUTPUT_TEMP)
+    {
+      log_bug ("iobuf_read called on a non-INPUT pipeline!\n");
+      return -1;
+    }
+
   if (a->nlimit)
     {
       /* Handle special cases. */
@@ -1989,10 +2012,12 @@ iobuf_read (iobuf_t a, void *buffer, unsigned int buflen)
                return -1;      /* eof */
              break;
            }
-         else if (buf)
-           *buf = c;
+
          if (buf)
-           buf++;
+           {
+             *buf = c;
+             buf++;
+           }
        }
       return n;
     }
@@ -2001,6 +2026,7 @@ iobuf_read (iobuf_t a, void *buffer, unsigned int buflen)
   do
     {
       if (n < buflen && a->d.start < a->d.len)
+       /* Drain the buffer.  */
        {
          unsigned size = a->d.len - a->d.start;
          if (size > buflen - n)
@@ -2013,8 +2039,13 @@ iobuf_read (iobuf_t a, void *buffer, unsigned int buflen)
            buf += size;
        }
       if (n < buflen)
+       /* Draining the internal buffer didn't fill BUFFER.  Call
+          underflow to read more data into the filter's internal
+          buffer.  */
        {
-         if ((c = underflow (a)) == -1)
+         if ((c = underflow (a, 1)) == -1)
+           /* EOF.  If we managed to read something, don't return EOF
+              now.  */
            {
              a->nbytes += n;
              return n ? n : -1 /*EOF*/;
@@ -2031,29 +2062,42 @@ iobuf_read (iobuf_t a, void *buffer, unsigned int buflen)
 
 
 
-/****************
- * Have a look at the iobuf.
- * NOTE: This only works in special cases.
- */
 int
 iobuf_peek (iobuf_t a, byte * buf, unsigned buflen)
 {
   int n = 0;
 
-  if (a->filter_eof)
-    return -1;
+  assert (buflen > 0);
+  assert (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP);
+
+  if (buflen > a->d.size)
+    /* We can't peek more than we can buffer.  */
+    buflen = a->d.size;
 
-  if (!(a->d.start < a->d.len))
+  /* Try to fill the internal buffer with enough data to satisfy the
+     request.  */
+  while (buflen > a->d.len - a->d.start)
     {
-      if (underflow (a) == -1)
-       return -1;
-      /* And unget this character. */
+      if (underflow (a, 0) == -1)
+       /* EOF.  We can't read any more.  */
+       break;
+
+      /* Underflow consumes the first character (it's the return
+        value).  unget() it by resetting the "file position".  */
       assert (a->d.start == 1);
       a->d.start = 0;
     }
 
-  for (n = 0; n < buflen && (a->d.start + n) < a->d.len; n++, buf++)
-    *buf = a->d.buf[n];
+  n = a->d.len - a->d.start;
+  if (n > buflen)
+    n = buflen;
+
+  if (n == 0)
+    /* EOF.  */
+    return -1;
+
+  memcpy (buf, &a->d.buf[a->d.start], n);
+
   return n;
 }
 
@@ -2065,11 +2109,14 @@ iobuf_writebyte (iobuf_t a, unsigned int c)
 {
   int rc;
 
-  if (a->directfp)
-    BUG ();
+  if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP)
+    {
+      log_bug ("iobuf_writebyte called on an input pipeline!\n");
+      return -1;
+    }
 
   if (a->d.len == a->d.size)
-    if ((rc=iobuf_flush (a)))
+    if ((rc=filter_flush (a)))
       return rc;
 
   assert (a->d.len < a->d.size);
@@ -2084,8 +2131,11 @@ iobuf_write (iobuf_t a, const void *buffer, unsigned int buflen)
   const unsigned char *buf = (const unsigned char *)buffer;
   int rc;
 
-  if (a->directfp)
-    BUG ();
+  if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP)
+    {
+      log_bug ("iobuf_write called on an input pipeline!\n");
+      return -1;
+    }
 
   do
     {
@@ -2101,7 +2151,7 @@ iobuf_write (iobuf_t a, const void *buffer, unsigned int buflen)
        }
       if (buflen)
        {
-         rc = iobuf_flush (a);
+         rc = filter_flush (a);
           if (rc)
            return rc;
        }
@@ -2114,35 +2164,44 @@ iobuf_write (iobuf_t a, const void *buffer, unsigned int buflen)
 int
 iobuf_writestr (iobuf_t a, const char *buf)
 {
-  int rc;
+  if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP)
+    {
+      log_bug ("iobuf_writestr called on an input pipeline!\n");
+      return -1;
+    }
 
-  for (; *buf; buf++)
-    if ((rc=iobuf_writebyte (a, *buf)))
-      return rc;
-  return 0;
+  return iobuf_write (a, buf, strlen (buf));
 }
 
 
 
-/****************
- * copy the contents of TEMP to A.
- */
 int
-iobuf_write_temp (iobuf_t a, iobuf_t temp)
+iobuf_write_temp (iobuf_t dest, iobuf_t source)
 {
-  while (temp->chain)
-    pop_filter (temp, temp->filter, NULL);
-  return iobuf_write (a, temp->d.buf, temp->d.len);
+  assert (source->use == IOBUF_OUTPUT || source->use == IOBUF_OUTPUT_TEMP);
+  assert (dest->use == IOBUF_OUTPUT || dest->use == IOBUF_OUTPUT_TEMP);
+
+  iobuf_flush_temp (source);
+  return iobuf_write (dest, source->d.buf, source->d.len);
 }
 
-/****************
- * copy the contents of the temp io stream to BUFFER.
- */
 size_t
 iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen)
 {
-  size_t n = a->d.len;
+  size_t n;
 
+  while (1)
+    {
+      int rc = filter_flush (a);
+      if (rc)
+       log_bug ("Flushing iobuf %d.%d (%s) from iobuf_temp_to_buffer failed.  Ignoring.\n",
+                a->no, a->subno, iobuf_desc (a));
+      if (! a->chain)
+       break;
+      a = a->chain;
+    }
+
+  n = a->d.len;
   if (n > buflen)
     n = buflen;
   memcpy (buffer, a->d.buf, n);
@@ -2150,31 +2209,23 @@ iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen)
 }
 
 
-/****************
- * Call this function to terminate processing of the temp stream
- * without closing it. This removes all filters from the stream
- * makes sure that iobuf_get_temp_{buffer,length}() returns correct
- * values.
- */
 void
 iobuf_flush_temp (iobuf_t temp)
 {
+  if (temp->use == IOBUF_INPUT || temp->use == IOBUF_INPUT_TEMP)
+    log_bug ("iobuf_writestr called on an input pipeline!\n");
   while (temp->chain)
     pop_filter (temp, temp->filter, NULL);
 }
 
 
-/****************
- * Set a limit on how many bytes may be read from the input stream A.
- * Setting the limit to 0 disables this feature.
- */
 void
 iobuf_set_limit (iobuf_t a, off_t nlimit)
 {
   if (nlimit)
-    a->nofast |= 1;
+    a->nofast = 1;
   else
-    a->nofast &= ~1;
+    a->nofast = 0;
   a->nlimit = nlimit;
   a->ntotal += a->nbytes;
   a->nbytes = 0;
@@ -2182,9 +2233,6 @@ iobuf_set_limit (iobuf_t a, off_t nlimit)
 
 
 
-/* Return the length of an open file A.  IF OVERFLOW is not NULL it
-   will be set to true if the file is larger than what off_t can cope
-   with.  The function return 0 on error or on overflow condition.  */
 off_t
 iobuf_get_filelength (iobuf_t a, int *overflow)
 {
@@ -2193,103 +2241,89 @@ iobuf_get_filelength (iobuf_t a, int *overflow)
   if (overflow)
     *overflow = 0;
 
-  if ( a->directfp )
-    {
-      FILE *fp = a->directfp;
+  /* Hmmm: file_filter may have already been removed */
+  for ( ; a->chain; a = a->chain )
+    ;
 
-      if ( !fstat(fileno(fp), &st) )
-        return st.st_size;
-      log_error("fstat() failed: %s\n", strerror(errno) );
-      return 0;
-    }
+  if (a->filter != file_filter)
+    return 0;
 
-  /* Hmmm: file_filter may have already been removed */
-  for ( ; a; a = a->chain )
-    if ( !a->chain && a->filter == file_filter )
-      {
-        file_filter_ctx_t *b = a->filter_ov;
-        gnupg_fd_t fp = b->fp;
+  {
+    file_filter_ctx_t *b = a->filter_ov;
+    gnupg_fd_t fp = b->fp;
 
 #if defined(HAVE_W32_SYSTEM)
-        ulong size;
-        static int (* __stdcall get_file_size_ex) (void *handle,
-                                                   LARGE_INTEGER *r_size);
-        static int get_file_size_ex_initialized;
-
-        if (!get_file_size_ex_initialized)
-          {
-            void *handle;
-
-            handle = dlopen ("kernel32.dll", RTLD_LAZY);
-            if (handle)
-              {
-                get_file_size_ex = dlsym (handle, "GetFileSizeEx");
-                if (!get_file_size_ex)
-                  dlclose (handle);
-              }
-            get_file_size_ex_initialized = 1;
-          }
-
-        if (get_file_size_ex)
-          {
-            /* This is a newer system with GetFileSizeEx; we use this
-               then because it seem that GetFileSize won't return a
-               proper error in case a file is larger than 4GB. */
-            LARGE_INTEGER exsize;
-
-            if (get_file_size_ex (fp, &exsize))
-              {
-                if (!exsize.u.HighPart)
-                  return exsize.u.LowPart;
-                if (overflow)
-                  *overflow = 1;
-                return 0;
-              }
-          }
-        else
-          {
-            if ((size=GetFileSize (fp, NULL)) != 0xffffffff)
-              return size;
-          }
-        log_error ("GetFileSize for handle %p failed: %s\n",
-                   fp, w32_strerror (0));
+    ulong size;
+    static int (* __stdcall get_file_size_ex) (void *handle,
+                                              LARGE_INTEGER *r_size);
+    static int get_file_size_ex_initialized;
+
+    if (!get_file_size_ex_initialized)
+      {
+       void *handle;
+
+       handle = dlopen ("kernel32.dll", RTLD_LAZY);
+       if (handle)
+         {
+           get_file_size_ex = dlsym (handle, "GetFileSizeEx");
+           if (!get_file_size_ex)
+             dlclose (handle);
+         }
+       get_file_size_ex_initialized = 1;
+      }
+
+    if (get_file_size_ex)
+      {
+       /* This is a newer system with GetFileSizeEx; we use this
+          then because it seem that GetFileSize won't return a
+          proper error in case a file is larger than 4GB. */
+       LARGE_INTEGER exsize;
+
+       if (get_file_size_ex (fp, &exsize))
+         {
+           if (!exsize.u.HighPart)
+             return exsize.u.LowPart;
+           if (overflow)
+             *overflow = 1;
+           return 0;
+         }
+      }
+    else
+      {
+       if ((size=GetFileSize (fp, NULL)) != 0xffffffff)
+         return size;
+      }
+    log_error ("GetFileSize for handle %p failed: %s\n",
+              fp, w32_strerror (0));
 #else
-        if ( !fstat (FD2INT (fp), &st) )
-          return st.st_size;
-        log_error("fstat() failed: %s\n", strerror(errno) );
+    if ( !fstat (FD2INT (fp), &st) )
+      return st.st_size;
+    log_error("fstat() failed: %s\n", strerror(errno) );
 #endif
-        break/*the for loop*/;
-      }
+  }
 
-    return 0;
+  return 0;
 }
 
 
-/* Return the file descriptor of the underlying file or -1 if it is
-   not available.  */
 int
 iobuf_get_fd (iobuf_t a)
 {
-  if (a->directfp)
-    return fileno ( (FILE*)a->directfp );
+  for (; a->chain; a = a->chain)
+    ;
 
-  for ( ; a; a = a->chain )
-    if (!a->chain && a->filter == file_filter)
-      {
-        file_filter_ctx_t *b = a->filter_ov;
-        gnupg_fd_t fp = b->fp;
+  if (a->filter != file_filter)
+    return -1;
 
-        return FD2INT (fp);
-      }
+  {
+    file_filter_ctx_t *b = a->filter_ov;
+    gnupg_fd_t fp = b->fp;
 
-  return -1;
+    return FD2INT (fp);
+  }
 }
 
 
-
-/****************
- * Tell the file position, where the next read will take place
- */
 off_t
 iobuf_tell (iobuf_t a)
 {
@@ -2327,37 +2361,22 @@ fseeko (FILE * stream, off_t newpos, int whence)
 }
 #endif
 
-/****************
- * This is a very limited implementation. It simply discards all internal
- * buffering and removes all filters but the first one.
- */
 int
 iobuf_seek (iobuf_t a, off_t newpos)
 {
   file_filter_ctx_t *b = NULL;
 
-  if (a->directfp)
-    {
-      FILE *fp = a->directfp;
-      if (fseeko (fp, newpos, SEEK_SET))
-       {
-         log_error ("can't seek: %s\n", strerror (errno));
-         return -1;
-       }
-      clearerr (fp);
-    }
-  else if (a->use != BLOCK_FILTER_TEMP)
+  if (a->use == IOBUF_OUTPUT || a->use == IOBUF_INPUT)
     {
-      for (; a; a = a->chain)
-       {
-         if (!a->chain && a->filter == file_filter)
-           {
-             b = a->filter_ov;
-             break;
-           }
-       }
-      if (!a)
+      /* Find the last filter in the pipeline.  */
+      for (; a->chain; a = a->chain)
+       ;
+
+      if (a->filter != file_filter)
        return -1;
+
+      b = a->filter_ov;
+
 #ifdef HAVE_W32_SYSTEM
       if (SetFilePointer (b->fp, newpos, NULL, FILE_BEGIN) == 0xffffffff)
        {
@@ -2372,16 +2391,25 @@ iobuf_seek (iobuf_t a, off_t newpos)
          return -1;
        }
 #endif
+      /* Discard the buffer it is not a temp stream.  */
+      a->d.len = 0;
     }
-  /* Discard the buffer unless it is a temp stream.  */
-  if (a->use != BLOCK_FILTER_TEMP)
-    a->d.len = 0;
   a->d.start = 0;
   a->nbytes = 0;
   a->nlimit = 0;
-  a->nofast &= ~1;
+  a->nofast = 0;
   a->ntotal = newpos;
   a->error = 0;
+
+  /* It is impossible for A->CHAIN to be non-NULL.  If A is an INPUT
+     or OUTPUT buffer, then we find the last filter, which is defined
+     as A->CHAIN being NULL.  If A is a TEMP filter, then A must be
+     the only filter in the pipe: when iobuf_push_filter adds a filter
+     to the front of a pipeline, it sets the new filter to be an
+     OUTPUT filter if the pipeline is an OUTPUT or TEMP pipeline and
+     to be an INPUT filter if the pipeline is an INPUT pipeline.
+     Thus, only the last filter in a TEMP pipeline can be a */
+
   /* remove filters, but the last */
   if (a->chain)
     log_debug ("pop_filter called in iobuf_seek - please report\n");
@@ -2392,15 +2420,6 @@ iobuf_seek (iobuf_t a, off_t newpos)
 }
 
 
-
-
-
-
-/****************
- * Retrieve the real filename.  This is the filename actually used on
- * disk and not a made up one.  Returns NULL if no real filename is
- * available.
- */
 const char *
 iobuf_get_real_fname (iobuf_t a)
 {
@@ -2418,10 +2437,6 @@ iobuf_get_real_fname (iobuf_t a)
   return NULL;
 }
 
-
-/****************
- * Retrieve the filename.  This name should only be used in diagnostics.
- */
 const char *
 iobuf_get_fname (iobuf_t a)
 {
@@ -2434,7 +2449,6 @@ iobuf_get_fname (iobuf_t a)
   return NULL;
 }
 
-/* Same as iobuf_get_fname but never returns NULL.  */
 const char *
 iobuf_get_fname_nonnull (iobuf_t a)
 {
@@ -2454,13 +2468,20 @@ iobuf_set_partial_block_mode (iobuf_t a, size_t len)
 {
   block_filter_ctx_t *ctx = xcalloc (1, sizeof *ctx);
 
-  assert (a->use == BLOCK_FILTER_INPUT || a->use == BLOCK_FILTER_OUTPUT);
   ctx->use = a->use;
   if (!len)
     {
-      if (a->use == BLOCK_FILTER_INPUT)
+      if (a->use == IOBUF_INPUT)
        log_debug ("pop_filter called in set_partial_block_mode"
                   " - please report\n");
+      /* XXX: This pop_filter doesn't make sense.  Since we haven't
+        actually added the filter to the pipeline yet, why are we
+        popping anything?  Moreover, since we don't report an error,
+        the caller won't directly see an error.  I think that it
+        would be better to push the filter and set a->error to
+        GPG_ERR_BAD_DATA, but Werner thinks it's impossible for len
+        to be 0 (but he doesn't want to remove the check just in
+        case).  */
       pop_filter (a, block_filter, NULL);
     }
   else
@@ -2474,16 +2495,6 @@ iobuf_set_partial_block_mode (iobuf_t a, size_t len)
 
 
 
-/****************
- * Same as fgets() but if the buffer is too short a larger one will
- * be allocated up to some limit *max_length.
- * A line is considered a byte stream ending in a LF.
- * Returns the length of the line. EOF is indicated by a line of
- * length zero. The last LF may be missing due to an EOF.
- * is max_length is zero on return, the line has been truncated.
- *
- * Note: The buffer is allocated with enough space to append a CR,LF,EOL
- */
 unsigned int
 iobuf_read_line (iobuf_t a, byte ** addr_of_buffer,
                 unsigned *length_of_buffer, unsigned *max_length)
@@ -2495,45 +2506,66 @@ iobuf_read_line (iobuf_t a, byte ** addr_of_buffer,
   unsigned maxlen = *max_length;
   char *p;
 
-  if (!buffer)
-    {                          /* must allocate a new buffer */
-      length = 256;
-      buffer = xmalloc (length);
+  /* The code assumes that we have space for at least a newline and a
+     NUL character in the buffer.  This requires at least 2 bytes.  We
+     don't complicate the code by handling the stupid corner case, but
+     simply assert that it can't happen.  */
+  assert (length >= 2 || maxlen >= 2);
+
+  if (!buffer || length <= 1)
+    /* must allocate a new buffer */
+    {
+      length = 256 <= maxlen ? 256 : maxlen;
+      buffer = xrealloc (buffer, length);
       *addr_of_buffer = (unsigned char *)buffer;
       *length_of_buffer = length;
     }
 
-  length -= 3;                 /* reserve 3 bytes (cr,lf,eol) */
   p = buffer;
   while ((c = iobuf_get (a)) != -1)
     {
-      if (nbytes == length)
-       {                       /* increase the buffer */
-         if (length > maxlen)
-           {                   /* this is out limit */
-             /* skip the rest of the line */
+      *p++ = c;
+      nbytes++;
+      if (c == '\n')
+       break;
+
+      if (nbytes == length - 1)
+       /* We don't have enough space to add a \n and a \0.  Increase
+          the buffer size.  */
+       {
+         if (length == maxlen)
+           /* We reached the buffer's size limit!  */
+           {
+             /* Skip the rest of the line.  */
              while (c != '\n' && (c = iobuf_get (a)) != -1)
                ;
-             *p++ = '\n';      /* always append a LF (we have reserved space) */
-             nbytes++;
-             *max_length = 0;  /* indicate truncation */
+
+             /* p is pointing at the last byte in the buffer.  We
+                always terminate the line with "\n\0" so overwrite
+                the previous byte with a \n.  */
+             assert (p > buffer);
+             p[-1] = '\n';
+
+             /* Indicate truncation.  */
+             *max_length = 0;
              break;
            }
-         length += 3;          /* correct for the reserved byte */
+
          length += length < 1024 ? 256 : 1024;
+         if (length > maxlen)
+           length = maxlen;
+
          buffer = xrealloc (buffer, length);
          *addr_of_buffer = (unsigned char *)buffer;
          *length_of_buffer = length;
-         length -= 3;          /* and reserve again */
          p = buffer + nbytes;
        }
-      *p++ = c;
-      nbytes++;
-      if (c == '\n')
-       break;
     }
-  *p = 0;                      /* make sure the line is a string */
+  /* Add the terminating NUL.  */
+  *p = 0;
 
+  /* Return the number of characters written to the buffer including
+     the newline, but not including the terminating NUL.  */
   return nbytes;
 }
 
index 3889459..bce6c31 100644 (file)
 #ifndef GNUPG_COMMON_IOBUF_H
 #define GNUPG_COMMON_IOBUF_H
 
+/* An iobuf is basically a filter in a pipeline.
+
+   Consider the following command, which consists of three filters
+   that are chained together:
+
+     $ cat file | base64 --decode | gunzip
+
+   The first filter reads the file from the file system and sends that
+   data to the second filter.  The second filter decodes
+   base64-encoded data and sends the data to the third and last
+   filter.  The last filter decompresses the data and the result is
+   displayed on the terminal.  The iobuf system works in the same way
+   where each iobuf is a filter and the individual iobufs can be
+   chained together.
+
+   There are number of predefined filters.  iobuf_open(), for
+   instance, creates a filter that reads from a specified file.  And,
+   iobuf_temp_with_content() creates a filter that returns some
+   specified contents.  There are also filters for writing content.
+   iobuf_openrw opens a file for writing.  iobuf_temp creates a filter
+   that writes data to a fixed-sized buffer.
+
+   To chain filters together, you use the iobuf_push_filter()
+   function.  The filters are chained together using the chain field
+   in the iobuf_t.
+
+   A pipeline can only be used for reading (IOBUF_INPUT) or for
+   writing (IOBUF_OUTPUT / IOBUF_OUTPUT_TEMP).  When reading, data
+   flows from the last filter towards the first.  That is, the user
+   calls iobuf_read(), the module reads from the first filter, which
+   gets its input from the second filter, etc.  When writing, data
+   flows from the first filter towards the last.  In this case, when
+   the user calls iobuf_write(), the data is written to the first
+   filter, which writes the transformed data to the second filter,
+   etc.
+
+   An iobuf_t contains some state about the filter.  For instance, it
+   indicates if the filter has already returned EOF (filter_eof) and
+   the next filter in the pipeline, if any (chain).  It also contains
+   a function pointer, filter.  This is a generic function.  It is
+   called when input is needed or output is available.  In this case
+   it is passed a pointer to some filter-specific persistent state
+   (filter_ov), the actual operation, the next filter in the chain, if
+   any, and a buffer that either contains the contents to write, if
+   the pipeline is setup to write data, or is the place to store data,
+   if the pipeline is setup to read data.
+
+
+   Unlike a Unix pipeline, an IOBUF pipeline can return EOF multiple
+   times.  This is similar to the following:
+
+     { cat file1; cat file2; } | grep foo
+
+   However, instead of grep seeing a single stream, grep would see
+   each byte stream followed by an EOF marker.  (When a filter returns
+   EOF, the EOF is returned to the user exactly once and then the
+   filter is removed from the pipeline.)  */
+
+/* For estream_t.  */
+#include <gpg-error.h>
+
 #include "../common/types.h"
 #include "../common/sysutils.h"
 
 #define DBG_IOBUF   iobuf_debug_mode
 
 /* Filter control modes.  */
-#define IOBUFCTRL_INIT     1
-#define IOBUFCTRL_FREE     2
-#define IOBUFCTRL_UNDERFLOW 3
-#define IOBUFCTRL_FLUSH     4
-#define IOBUFCTRL_DESC     5
-#define IOBUFCTRL_CANCEL    6
-#define IOBUFCTRL_USER     16
+enum
+  {
+    IOBUFCTRL_INIT     = 1,
+    IOBUFCTRL_FREE     = 2,
+    IOBUFCTRL_UNDERFLOW = 3,
+    IOBUFCTRL_FLUSH     = 4,
+    IOBUFCTRL_DESC     = 5,
+    IOBUFCTRL_CANCEL    = 6,
+    IOBUFCTRL_USER     = 16
+  };
 
 
 /* Command codes for iobuf_ioctl.  */
@@ -55,6 +119,25 @@ typedef enum
     IOBUF_IOCTL_FSYNC            = 4  /* Uses ptrval.  */
   } iobuf_ioctl_t;
 
+enum iobuf_use
+  {
+    /* Pipeline is in input mode.  The data flows from the end to the
+       beginning.  That is, when reading from the pipeline, the first
+       filter gets its input from the second filter, etc.  */
+    IOBUF_INPUT,
+    /* Pipeline is in input mode.  The last filter in the pipeline is
+       a temporary buffer from which the data is "read".  */
+    IOBUF_INPUT_TEMP,
+    /* Pipeline is in output mode.  The data flows from the beginning
+       to the end.  That is, when writing to the pipeline, the user
+       writes to the first filter, which transforms the data and sends
+       it to the second filter, etc.  */
+    IOBUF_OUTPUT,
+    /* Pipeline is in output mode.  The last filter in the pipeline is
+       a temporary buffer that grows as necessary.  */
+    IOBUF_OUTPUT_TEMP
+  };
+
 
 typedef struct iobuf_struct *iobuf_t;
 typedef struct iobuf_struct *IOBUF;  /* Compatibility with gpg 1.4. */
@@ -62,35 +145,108 @@ typedef struct iobuf_struct *IOBUF;  /* Compatibility with gpg 1.4. */
 /* fixme: we should hide most of this stuff */
 struct iobuf_struct
 {
-  int use;                     /* 1 input , 2 output, 3 temp */
+  /* The type of filter.  Either IOBUF_INPUT, IOBUF_OUTPUT or
+     IOBUF_OUTPUT_TEMP.  */
+  enum iobuf_use use;
+
+  /* nlimit can be changed using iobuf_set_limit.  If non-zero, it is
+     the number of additional bytes that can be read from the filter
+     before EOF is forcefully returned.  */
   off_t nlimit;
-  off_t nbytes;                        /* Used together with nlimit. */
-  off_t ntotal;                        /* Total bytes read (position of stream). */
-  int nofast;                  /* Used by the iobuf_get (). */
-  void *directfp;
+  /* nbytes if the number of bytes that have been read (using
+     iobuf_get / iobuf_readbyte / iobuf_read) since the last call to
+     iobuf_set_limit.  */
+  off_t nbytes;
+
+  /* The number of bytes read prior to the last call to
+     iobuf_set_limit.  Thus, the total bytes read (i.e., the position
+     of stream) is ntotal + nbytes. */
+  off_t ntotal;
+
+  /* Whether we need to read from the filter one byte at a time or
+     whether we can do bulk reads.  We need to read one byte at a time
+     if a limit (set via iobuf_set_limit) is active.  */
+  int nofast;
+
+  /* A buffer for unread/unwritten data.
+
+     For an output pipeline (IOBUF_OUTPUT), this is the data that has
+     not yet been written to the filter.  Consider a simple pipeline
+     consisting of a single stage, which writes to a file.  When you
+     write to the pipeline (iobuf_writebyte or iobuf_write), the data
+     is first stored in this buffer.  Only when the buffer is full or
+     you call iobuf_flush() is FILTER actually called and the data
+     written to the file.
+
+     For an input pipeline (IOBUF_INPUT), this is the data that has
+     been read from this filter, but not yet been read from the
+     preceding filter (or the user, if this filter is the head of the
+     pipeline).  Again, consider a simple pipeline consisting of a
+     single stage.  This stage reads from a file.  If you read a
+     single byte (iobuf_get) and the buffer is empty, then FILTER is
+     called to fill the buffer.  In this case, a single byte is not
+     requested, but the whole buffer is filled (if possible).  */
   struct
   {
-    size_t size;               /* Allocated size */
-    size_t start;              /* Number of invalid bytes at the
-                                   begin of the buffer */
-    size_t len;                        /* Currently filled to this size */
+    /* Size of the buffer.  */
+    size_t size;
+    /* Number of bytes at the beginning of the buffer that have
+       already been consumed.  (In other words: the index of the first
+       byte that hasn't been consumed.)  This is only non-zero for
+       input filters.  */
+    size_t start;
+    /* The number of bytes in the buffer including any bytes that have
+       been consumed.  */
+    size_t len;
+    /* The buffer itself.  */
     byte *buf;
   } d;
 
+  /* When FILTER is called to read some data, it may read some data
+     and then return EOF.  We can't return the EOF immediately.
+     Instead, we note that we observed the EOF and when the buffer is
+     finally empty, we return the EOF.  */
   int filter_eof;
+  /* Like filter_eof, when FILTER is called to read some data, it may
+     read some data and then return an error.  We can't return the
+     error (in the form of an EOF) immediately.  Instead, we note that
+     we observed the error and when the buffer is finally empty, we
+     return the EOF.  */
   int error;
+
+  /* The callback function to read data from the filter, etc.  See
+     iobuf_filter_push for details.  */
   int (*filter) (void *opaque, int control,
                 iobuf_t chain, byte * buf, size_t * len);
-  void *filter_ov;             /* Value for opaque */
+  /* An opaque pointer that can be used for local filter state.  This
+     is passed as the first parameter to FILTER.  */
+  void *filter_ov;
+  /* Whether the iobuf code should free(filter_ov) when destroying the
+     filter.  */
   int filter_ov_owner;
+
+  /* When using iobuf_open, iobuf_create, iobuf_openrw to open a file,
+     the file's name is saved here.  This is used to delete the file
+     when an output pipeline (IOBUF_OUPUT) is canceled
+     (iobuf_cancel).  */
   char *real_fname;
-  iobuf_t chain;               /* Next iobuf used for i/o if any
-                                   (passed to filter) */
-  int no, subno;
-  const char *desc;
-  void *opaque;                        /* Can be used to hold any information
-                                   this value is copied to all
-                                   instances */
+
+  /* The next filter in the pipeline.  */
+  iobuf_t chain;
+
+  /* This field is for debugging.  Each time a filter is allocated
+     (via iobuf_alloc()), a monotonically increasing counter is
+     incremented and this field is set to the new value.  This field
+     should only be accessed via the iobuf_io macro.  */
+  int no;
+
+  /* The number of filters in the pipeline following (not including)
+     this one.  When you call iobuf_push_filter or iobuf_push_filter2,
+     this value is used to check the length of the pipeline if the
+     pipeline already contains 65 stages then these functions fail.
+     This amount of nesting typically indicates corrupted data or an
+     active denial of service attack.  */
+  int subno;
 };
 
 #ifndef EXTERN_UNLESS_MAIN_MODULE
@@ -102,88 +258,357 @@ struct iobuf_struct
 #endif
 EXTERN_UNLESS_MAIN_MODULE int iobuf_debug_mode;
 
+/* Whether iobuf_open, iobuf_create and iobuf_is_pipefilename
+   recognize special filenames.  Special filenames are of the form
+   "-&nnnn" where n is a positive integer.  The integer corresponds to
+   a file descriptor.  Note: these functions always recognize the
+   special filename '-', which corresponds to standard input.  */
 void iobuf_enable_special_filenames (int yes);
+
+/* Returns whether the specified filename corresponds to a pipe.  In
+   particular, this function checks if FNAME is "-" and, if special
+   filenames are enabled (see iobuf_enable_special_filenames), whether
+   FNAME is a special filename.  */
 int  iobuf_is_pipe_filename (const char *fname);
+
+/* Allocate a new filter.  This filter doesn't have a function
+   assigned to it.  Thus you need to manually set IOBUF->FILTER and
+   IOBUF->FILTER_OV, if required.  This function is intended to help
+   create a new primary source or primary sink, i.e., the last filter
+   in the pipeline.
+
+   USE is IOBUF_INPUT, IOBUF_INPUT_TEMP, IOBUF_OUTPUT or
+   IOBUF_OUTPUT_TEMP.
+
+   BUFSIZE is the desired internal buffer size (that is, the size of
+   the typical read / write request).  */
 iobuf_t iobuf_alloc (int use, size_t bufsize);
+
+/* Create an output filter that simply buffers data written to it.
+   This is useful for collecting data for later processing.  The
+   buffer can be written to in the usual way (iobuf_write, etc.).  The
+   data can later be extracted using iobuf_write_temp() or
+   iobuf_temp_to_buffer().  */
 iobuf_t iobuf_temp (void);
+
+/* Create an input filter that contains some data for reading.  */
 iobuf_t iobuf_temp_with_content (const char *buffer, size_t length);
-iobuf_t iobuf_open_fd_or_name (gnupg_fd_t fd, const char *fname,
-                               const char *mode);
+
+/* Create an input file filter that reads from a file.  If FNAME is
+   '-', reads from stdin.  If special filenames are enabled
+   (iobuf_enable_special_filenames), then interprets special
+   filenames.  */
 iobuf_t iobuf_open (const char *fname);
+
+/* Create an output file filter that writes to a file.  If FNAME is
+   NULL or '-', writes to stdout.  If special filenames are enabled
+   (iobuf_enable_special_filenames), then interprets special
+   filenames.  If FNAME is not NULL, '-' or a special filename, the
+   file is opened for writing.  If the file exists, it is truncated.
+   If MODE700 is TRUE, the file is created with mode 600.  Otherwise,
+   mode 666 is used.  */
+iobuf_t iobuf_create (const char *fname, int mode700);
+
+/* Create an output file filter that writes to a specified file.
+   Neither '-' nor special file names are recognized.  */
+iobuf_t iobuf_openrw (const char *fname);
+
+/* Create a file filter using an existing file descriptor.  If MODE
+   contains the letter 'w', creates an output filter.  Otherwise,
+   creates an input filter.  Note: MODE must reflect the file
+   descriptors actual mode!  When the filter is destroyed, the file
+   descriptor is closed.  */
 iobuf_t iobuf_fdopen (int fd, const char *mode);
+
+/* Like iobuf_fdopen, but doesn't close the file descriptor when the
+   filter is destroyed.  */
 iobuf_t iobuf_fdopen_nc (int fd, const char *mode);
+
+/* Create a filter using an existing estream.  If MODE contains the
+   letter 'w', creates an output filter.  Otherwise, creates an input
+   filter.  If KEEP_OPEN is TRUE, then the stream is not closed when
+   the filter is destroyed.  Otherwise, the stream is closed when the
+   filter is destroyed.  */
 iobuf_t iobuf_esopen (estream_t estream, const char *mode, int keep_open);
+
+/* Create a filter using an existing socket.  On Windows creates a
+   special socket filter.  On non-Windows systems simply, this simply
+   calls iobuf_fdopen.  */
 iobuf_t iobuf_sockopen (int fd, const char *mode);
-iobuf_t iobuf_create (const char *fname, int mode700);
-iobuf_t iobuf_append (const char *fname);
-iobuf_t iobuf_openrw (const char *fname);
+
+/* Set various options / perform different actions on a PIPELINE.  See
+   the IOBUF_IOCTL_* macros above.  */
 int iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval);
+
+/* Close a pipeline.  The filters in the pipeline are first flushed
+   using iobuf_flush, if they are output filters, and then
+   IOBUFCTRL_FREE is called on each filter.
+
+   If any filter returns a non-zero value in response to the
+   IOBUFCTRL_FREE, that first such non-zero value is returned.  Note:
+   processing is not aborted in this case.  If all filters are freed
+   successfully, 0 is returned.  */
 int iobuf_close (iobuf_t iobuf);
+
+/* Calls IOBUFCTRL_CANCEL on each filter in the pipeline.  Then calls
+   io_close() on the pipeline.  Finally, if the pipeline is an output
+   pipeline, deletes the file.  Returns the result of calling
+   iobuf_close on the pipeline.  */
 int iobuf_cancel (iobuf_t iobuf);
 
+/* Add a new filter to the front of a pipeline.  A is the head of the
+   pipeline.  F is the filter implementation.  OV is an opaque pointer
+   that is passed to F and is normally used to hold any internal
+   state, such as a file pointer.
+
+   Note: you may only maintain a reference to an iobuf_t as a
+   reference to the head of the pipeline.  That is, don't think about
+   setting a pointer in OV to point to the filter's iobuf_t.  This is
+   because when we add a new filter to a pipeline, we memcpy the state
+   in A into new buffer.  This has the advantage that there is no need
+   to update any references to the pipeline when a filter is added or
+   removed, but it also means that a filter's state moves around in
+   memory.
+
+   The behavior of the filter function is determined by the value of
+   the control parameter:
+
+     IOBUFCTRL_INIT: Called this value just before the filter is
+       linked into the pipeline. This can be used to initialize
+       internal data structures.
+
+     IOBUFCTRL_FREE: Called with this value just before the filter is
+       removed from the pipeline.  Normally used to release internal
+       data structures, close a file handle, etc.
+
+     IOBUFCTRL_UNDERFLOW: Called with this value to fill the passed
+       buffer with more data. *LEN is the size of the buffer.  Before
+       returning, it should be set to the number of bytes which were
+       written into the buffer.  The function must return 0 to
+       indicate success, -1 on EOF and a GPG_ERR_xxxxx code for any
+       error.
+
+       Note: this function may both return data and indicate an error
+       or EOF.  In this case, it simply writes the data to BUF, sets
+       *LEN and returns the appropriate return code.  The implication
+       is that if an error occurs and no data has yet been written, it
+       is essential that *LEN be set to 0!
+
+     IOBUFCTRL_FLUSH: Called with this value to write out any
+       collected data.  *LEN is the number of bytes in BUF that need
+       to be written out.  Returns 0 on success and a GPG_ERR_* code
+       otherwise.  *LEN must be set to the number of bytes that were
+       written out.
+
+     IOBUFCTRL_CANCEL: Called with this value when iobuf_cancel() is
+       called on the pipeline.
+
+     IOBUFCTRL_DESC: Called with this value to get a human-readable
+       description of the filter.  * (char **) BUF should set to the
+       NUL-terminated string.  Note: you need to keep track of this
+       value and, if necessary, free it when the filter function is
+       called with control set to IOBUFCTRL_FREE.
+  */
 int iobuf_push_filter (iobuf_t a, int (*f) (void *opaque, int control,
-                                         iobuf_t chain, byte * buf,
-                                         size_t * len), void *ov);
+                                           iobuf_t chain, byte * buf,
+                                           size_t * len), void *ov);
+/* This variant of iobuf_push_filter allows the called to indicate
+   that OV should be freed when this filter is freed.  That is, if
+   REL_OV is TRUE, then when the filter is popped or freed OV will be
+   freed after the filter function is called with control set to
+   IOBUFCTRL_FREE.  */
 int iobuf_push_filter2 (iobuf_t a,
                        int (*f) (void *opaque, int control, iobuf_t chain,
                                  byte * buf, size_t * len), void *ov,
                        int rel_ov);
-int iobuf_flush (iobuf_t a);
-void iobuf_clear_eof (iobuf_t a);
+
+/* Used for debugging.  Prints out the chain using log_debug if
+   IOBUF_DEBUG_MODE is not 0.  */
+int iobuf_print_chain (iobuf_t a);
+
+/* Indicate that some error occured on the specified filter.  */
 #define iobuf_set_error(a)    do { (a)->error = 1; } while(0)
+
+/* Return any pending error on filter A.  */
 #define iobuf_error(a)       ((a)->error)
 
+/* Limit the amount of additional data that may be read from the
+   filter.  That is, if you've already read 100 bytes from A and you
+   set the limit to 50, then you can read up to an additional 50 bytes
+   (i.e., a total of 150 bytes) before EOF is forcefully returned.
+   Setting NLIMIT to 0 removes any active limit.
+
+   Note: using iobuf_seek removes any currently enforced limit!  */
 void iobuf_set_limit (iobuf_t a, off_t nlimit);
 
+/* Returns the number of bytes that have been read from the pipeline.
+   Note: the result is undefined for IOBUF_OUTPUT and IOBUF_OUTPUT_TEMP
+   pipelines!  */
 off_t iobuf_tell (iobuf_t a);
+
+/* There are two cases:
+
+   - If A is an INPUT or OUTPUT pipeline, then the last filter in the
+     pipeline is found.  If that is not a file filter, -1 is returned.
+     Otherwise, an fseek(..., SEEK_SET) is performed on the file
+     descriptor.
+
+   - If A is a TEMP pipeline and the *first* (and thus only filter) is
+     a TEMP filter, then the "file position" is effectively unchanged.
+     That is, data is appended to the buffer and the seek does not
+     cause the size of the buffer to grow.
+
+   If no error occured, then any limit previous set by
+   iobuf_set_limit() is cleared.  Further, any error on the filter
+   (the file filter or the temp filter) is cleared.
+
+   Returns 0 on success and -1 if an error occurs.  */
 int iobuf_seek (iobuf_t a, off_t newpos);
 
+/* Read a single byte.  If a filter has no more data, returns -1 to
+   indicate the EOF.  Generally, you don't want to use this function,
+   but instead prefer the iobuf_get macro, which is faster if there is
+   data in the internal buffer.  */
 int iobuf_readbyte (iobuf_t a);
+
+/* Get a byte from the iobuf; must check for eof prior to this
+   function.  This function returns values in the range 0 .. 255 or -1
+   to indicate EOF.  iobuf_get_noeof() does not return -1 to indicate
+   EOF, but masks the returned value to be in the range 0 .. 255.  */
+#define iobuf_get(a)  \
+     ( ((a)->nofast || (a)->d.start >= (a)->d.len )?  \
+       iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) )
+#define iobuf_get_noeof(a)    (iobuf_get((a))&0xff)
+
+/* Fill BUF with up to BUFLEN bytes.  If a filter has no more data,
+   returns -1 to indicate the EOF.  Otherwise returns the number of
+   bytes read.  */
 int iobuf_read (iobuf_t a, void *buf, unsigned buflen);
-void iobuf_unread (iobuf_t a, const unsigned char *buf, unsigned int buflen);
+
+/* Read a line of input (including the '\n') from the pipeline.
+
+   The semantics are the same as for fgets(), but if the buffer is too
+   short a larger one will be allocated up to *MAX_LENGTH and the end
+   of the line except the trailing '\n' discarded.  (Thus,
+   *ADDR_OF_BUFFER must be allocated using malloc().)  If the buffer
+   is enlarged, then *LENGTH_OF_BUFFER will be updated to reflect the
+   new size.  If the line is truncated, then *MAX_LENGTH will be set
+   to 0.  If *ADDR_OF_BUFFER is NULL, a buffer is allocated using
+   malloc().
+
+   A line is considered a byte stream ending in a '\n'.  Returns the
+   number of characters written to the buffer (i.e., excluding any
+   discarded characters due to truncation).  Thus, use this instead of
+   strlen(buffer) to determine the length of the string as this is
+   unreliable if the input contains NUL characters.
+
+   EOF is indicated by a line of length zero.
+
+   The last LF may be missing due to an EOF.  */
 unsigned iobuf_read_line (iobuf_t a, byte ** addr_of_buffer,
                          unsigned *length_of_buffer, unsigned *max_length);
+
+/* Read up to BUFLEN bytes from pipeline A.  Note: this function can't
+   return more than the pipeline's internal buffer size.  The return
+   value is the number of bytes actually written to BUF.  If the
+   filter returns EOF, then this function returns -1.
+
+   This function does not clear any pending EOF.  That is, if the
+   pipeline consists of two filters and the first one returns EOF
+   during the peek, then the subsequent iobuf_read* will still return
+   EOF before returning the data from the second filter.  */
 int iobuf_peek (iobuf_t a, byte * buf, unsigned buflen);
+
+/* Write a byte to the pipeline.  Returns 0 on success and an error
+   code otherwise.  */
 int iobuf_writebyte (iobuf_t a, unsigned c);
+
+/* Alias for iobuf_writebyte.  */
+#define iobuf_put(a,c) iobuf_writebyte(a,c)
+
+/* Write a sequence of bytes to the pipeline.  Returns 0 on success
+   and an error code otherwise.  */
 int iobuf_write (iobuf_t a, const void *buf, unsigned buflen);
+
+/* Write a string (not including the NUL terminator) to the pipeline.
+   Returns 0 on success and an error code otherwise.  */
 int iobuf_writestr (iobuf_t a, const char *buf);
 
+/* Flushes the pipeline removing all filters but the sink (the last
+   filter) in the process.  */
 void iobuf_flush_temp (iobuf_t temp);
-int iobuf_write_temp (iobuf_t a, iobuf_t temp);
+
+/* Flushes the pipeline SOURCE removing all filters but the sink (the
+   last filter) in the process (i.e., it calls
+   iobuf_flush_temp(source)) and then writes the data to the pipeline
+   DEST.  Note: this doesn't free (iobuf_close()) SOURCE.  Both SOURCE
+   and DEST must be output pipelines.  */
+int iobuf_write_temp (iobuf_t dest, iobuf_t source);
+
+/* Flushes each filter in the pipeline (i.e., sends any buffered data
+   to the filter by calling IOBUFCTRL_FLUSH).  Then, copies up to the
+   first BUFLEN bytes from the last filter's internal buffer (which
+   will only be non-empty if it is a temp filter) to the buffer
+   BUFFER.  Returns the number of bytes actually copied.  */
 size_t iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen);
 
+/* Return the size of any underlying file.  This only works with
+   file_filter based pipelines.
+
+   On Win32, it is sometimes not possible to determine the size of
+   files larger than 4GB.  In this case, *OVERFLOW (if not NULL) is
+   set to 1.  Otherwise, *OVERFLOW is set to 0.  */
 off_t iobuf_get_filelength (iobuf_t a, int *overflow);
 #define IOBUF_FILELENGTH_LIMIT 0xffffffff
+
+/* Return the file descriptor designating the underlying file.  This
+   only works with file_filter based pipelines.  */
 int  iobuf_get_fd (iobuf_t a);
+
+/* Return the real filename, if available.  This only supports
+   pipelines that end in file filters.  Returns NULL if not
+   available.  */
 const char *iobuf_get_real_fname (iobuf_t a);
+
+/* Return the filename or a description thereof.  For instance, for
+   iobuf_open("-"), this will return "[stdin]".  This only supports
+   pipelines that end in file filters.  Returns NULL if not
+   available.  */
 const char *iobuf_get_fname (iobuf_t a);
+
+/* Like iobuf_getfname, but instead of returning NULL if no
+   description is available, return "[?]".  */
 const char *iobuf_get_fname_nonnull (iobuf_t a);
 
+/* Pushes a filter on the pipeline that interprets the datastream as
+   an OpenPGP data block whose length is encoded using partial body
+   length headers (see Section 4.2.2.4 of RFC 4880).  Concretely, it
+   just returns / writes the data and finishes the packet with an
+   EOF.  */
 void iobuf_set_partial_block_mode (iobuf_t a, size_t len);
 
-void iobuf_skip_rest (iobuf_t a, unsigned long n, int partial);
+/* If PARTIAL is set, then read from the pipeline until the first EOF
+   is returned.
 
+   If PARTIAL is 0, then read up to N bytes or until the first EOF is
+   returned.
 
-/* Get a byte from the iobuf; must check for eof prior to this
- * function.  This function returns values in the range 0 .. 255 or -1
- * to indicate EOF.  iobuf_get_noeof() does not return -1 to indicate
- * EOF, but masks the returned value to be in the range 0 .. 255.
- */
-#define iobuf_get(a)  \
-     ( ((a)->nofast || (a)->d.start >= (a)->d.len )?  \
-       iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) )
-#define iobuf_get_noeof(a)    (iobuf_get((a))&0xff)
-
-/* write a byte to the iobuf and return true on write error
- * This macro does only write the low order byte
- */
-#define iobuf_put(a,c) iobuf_writebyte(a,c)
+   Recall: a filter can return EOF.  In this case, it and all
+   preceding filters are popped from the pipeline and the next read is
+   from the following filter (which may or may not return EOF).  */
+void iobuf_skip_rest (iobuf_t a, unsigned long n, int partial);
 
 #define iobuf_where(a) "[don't know]"
+
+/* Each time a filter is allocated (via iobuf_alloc()), a
+   monotonically increasing counter is incremented and this field is
+   set to the new value.  This macro returns that number.  */
 #define iobuf_id(a)    ((a)->no)
 
 #define iobuf_get_temp_buffer(a) ( (a)->d.buf )
 #define iobuf_get_temp_length(a) ( (a)->d.len )
-#define iobuf_is_temp(a)        ( (a)->use == 3 )
+
+/* Whether the filter uses an in-memory buffer.  */
+#define iobuf_is_temp(a)        ( (a)->use == IOBUF_OUTPUT_TEMP )
 
 #endif /*GNUPG_COMMON_IOBUF_H*/
index 9219bf4..f1af587 100644 (file)
@@ -125,8 +125,11 @@ enum
     STATUS_PINENTRY_LAUNCHED,
 
     STATUS_ERROR,
-    STATUS_SUCCESS
-};
+    STATUS_SUCCESS,
+    STATUS_FAILURE,
+
+    STATUS_INQUIRE_MAXLEN
+  };
 
 
 const char *get_status_string (int code);
diff --git a/common/t-iobuf.c b/common/t-iobuf.c
new file mode 100644 (file)
index 0000000..99581b9
--- /dev/null
@@ -0,0 +1,378 @@
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "iobuf.h"
+
+/* Return every other byte.  In particular, reads two bytes, returns
+   the second one.  */
+static int
+every_other_filter (void *opaque, int control,
+                   iobuf_t chain, byte *buf, size_t *len)
+{
+  (void) opaque;
+
+  if (control == IOBUFCTRL_DESC)
+    {
+      *(char **) buf = "every_other_filter";
+    }
+  if (control == IOBUFCTRL_UNDERFLOW)
+    {
+      int c = iobuf_readbyte (chain);
+      int c2;
+      if (c == -1)
+       c2 = -1;
+      else
+       c2 = iobuf_readbyte (chain);
+
+      /* printf ("Discarding %d (%c); return %d (%c)\n", c, c, c2, c2); */
+
+      if (c2 == -1)
+       {
+         *len = 0;
+         return -1;
+       }
+
+      *buf = c2;
+      *len = 1;
+
+      return 0;
+    }
+
+  return 0;
+}
+
+static int
+double_filter (void *opaque, int control,
+              iobuf_t chain, byte *buf, size_t *len)
+{
+  (void) opaque;
+
+  if (control == IOBUFCTRL_DESC)
+    {
+      * (char **) buf = "double_filter";
+    }
+  if (control == IOBUFCTRL_FLUSH)
+    {
+      int i;
+
+      for (i = 0; i < *len; i ++)
+       {
+         int rc;
+
+         rc = iobuf_writebyte (chain, buf[i]);
+         if (rc)
+           return rc;
+         rc = iobuf_writebyte (chain, buf[i]);
+         if (rc)
+           return rc;
+       }
+    }
+
+  return 0;
+}
+
+struct content_filter_state
+{
+  int pos;
+  int len;
+  const char *buffer;
+};
+
+static struct content_filter_state *
+content_filter_new (const char *buffer)
+{
+  struct content_filter_state *state
+    = malloc (sizeof (struct content_filter_state));
+
+  state->pos = 0;
+  state->len = strlen (buffer);
+  state->buffer = buffer;
+
+  return state;
+}
+
+static int
+content_filter (void *opaque, int control,
+               iobuf_t chain, byte *buf, size_t *len)
+{
+  struct content_filter_state *state = opaque;
+
+  (void) chain;
+
+  if (control == IOBUFCTRL_UNDERFLOW)
+    {
+      int remaining = state->len - state->pos;
+      int toread = *len;
+      assert (toread > 0);
+
+      if (toread > remaining)
+       toread = remaining;
+
+      memcpy (buf, &state->buffer[state->pos], toread);
+
+      state->pos += toread;
+
+      *len = toread;
+
+      if (toread == 0)
+       return -1;
+      return 0;
+    }
+
+  return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+  (void) argc;
+  (void) argv;
+
+  /* A simple test to make sure filters work.  We use a static buffer
+     and then add a filter in front of it that returns every other
+     character.  */
+  {
+    char *content = "0123456789abcdefghijklm";
+    iobuf_t iobuf;
+    int c;
+    int n;
+    int rc;
+
+    iobuf = iobuf_temp_with_content (content, strlen (content));
+    rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
+    assert (rc == 0);
+
+    n = 0;
+    while ((c = iobuf_readbyte (iobuf)) != -1)
+      {
+       /* printf ("%d: %c\n", n + 1, (char) c); */
+       assert (content[2 * n + 1] == c);
+       n ++;
+      }
+    /* printf ("Got EOF after reading %d bytes (content: %d)\n", */
+    /*         n, strlen (content)); */
+    assert (n == strlen (content) / 2);
+
+    iobuf_close (iobuf);
+  }
+
+  /* A simple test to check buffering.  Make sure that when we add a
+     filter to a pipeline, any buffered data gets processed by the */
+  {
+    char *content = "0123456789abcdefghijklm";
+    iobuf_t iobuf;
+    int c;
+    int n;
+    int rc;
+    int i;
+
+    iobuf = iobuf_temp_with_content (content, strlen (content));
+
+    n = 0;
+    for (i = 0; i < 10; i ++)
+      {
+       c = iobuf_readbyte (iobuf);
+       assert (content[i] == c);
+       n ++;
+      }
+
+    rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
+    assert (rc == 0);
+
+    while ((c = iobuf_readbyte (iobuf)) != -1)
+      {
+       /* printf ("%d: %c\n", n + 1, (char) c); */
+       assert (content[2 * (n - 5) + 1] == c);
+       n ++;
+      }
+    assert (n == 10 + (strlen (content) - 10) / 2);
+  }
+
+
+  /* A simple test to check that iobuf_read_line works.  */
+  {
+    /* - 3 characters plus new line
+       - 4 characters plus new line
+       - 5 characters plus new line
+       - 5 characters, no new line
+     */
+    char *content = "abc\ndefg\nhijkl\nmnopq";
+    iobuf_t iobuf;
+    byte *buffer;
+    unsigned size;
+    unsigned max_len;
+    int n;
+
+    iobuf = iobuf_temp_with_content (content, strlen(content));
+
+    /* We read a line with 3 characters plus a newline.  If we
+       allocate a buffer that is 5 bytes long, then no reallocation
+       should be required.  */
+    size = 5;
+    buffer = malloc (size);
+    assert (buffer);
+    max_len = 100;
+    n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
+    assert (n == 4);
+    assert (strcmp (buffer, "abc\n") == 0);
+    assert (size == 5);
+    assert (max_len == 100);
+    free (buffer);
+
+    /* We now read a line with 4 characters plus a newline.  This
+       requires 6 bytes of storage.  We pass a buffer that is 5 bytes
+       large and we allow the buffer to be grown.  */
+    size = 5;
+    buffer = malloc (size);
+    max_len = 100;
+    n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
+    assert (n == 5);
+    assert (strcmp (buffer, "defg\n") == 0);
+    assert (size >= 6);
+    /* The string shouldn't have been truncated (max_len == 0).  */
+    assert (max_len == 100);
+    free (buffer);
+
+    /* We now read a line with 5 characters plus a newline.  This
+       requires 7 bytes of storage.  We pass a buffer that is 5 bytes
+       large and we don't allow the buffer to be grown.  */
+    size = 5;
+    buffer = malloc (size);
+    max_len = 5;
+    n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
+    assert (n == 4);
+    /* Note: the string should still have a trailing \n.  */
+    assert (strcmp (buffer, "hij\n") == 0);
+    assert (size == 5);
+    /* The string should have been truncated (max_len == 0).  */
+    assert (max_len == 0);
+    free (buffer);
+
+    /* We now read a line with 6 characters without a newline.  This
+       requires 7 bytes of storage.  We pass a NULL buffer and we
+       don't allow the buffer to be grown larger than 5 bytes.  */
+    size = 5;
+    buffer = NULL;
+    max_len = 5;
+    n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
+    assert (n == 4);
+    /* Note: the string should still have a trailing \n.  */
+    assert (strcmp (buffer, "mno\n") == 0);
+    assert (size == 5);
+    /* The string should have been truncated (max_len == 0).  */
+    assert (max_len == 0);
+    free (buffer);
+  }
+
+  {
+    /* - 10 characters, EOF
+       - 17 characters, EOF
+     */
+    char *content = "abcdefghijklmnopq";
+    char *content2 = "0123456789";
+    iobuf_t iobuf;
+    int rc;
+    int c;
+    int n;
+    int lastc = 0;
+
+    iobuf = iobuf_temp_with_content (content, strlen(content));
+    rc = iobuf_push_filter (iobuf,
+                           content_filter, content_filter_new (content2));
+    assert (rc == 0);
+
+    n = 0;
+    while (1)
+      {
+       c = iobuf_readbyte (iobuf);
+       if (c == -1 && lastc == -1)
+         {
+           /* printf("Two EOFs in a row.  Done.\n");  */
+           assert (n == 27);
+           break;
+         }
+
+       lastc = c;
+
+       if (c == -1)
+         {
+           /* printf("After %d bytes, got EOF.\n", n); */
+           assert (n == 10 || n == 27);
+         }
+       else
+         {
+           n ++;
+           /* printf ("%d: '%c' (%d)\n", n, c, c); */
+         }
+      }
+  }
+
+  /* Write some data to a temporary filter.  Push a new filter.  The
+     already written data should not be processed by the new
+     filter.  */
+  {
+    iobuf_t iobuf;
+    int rc;
+    char *content = "0123456789";
+    char *content2 = "abc";
+    char buffer[4096];
+    int n;
+
+    iobuf = iobuf_temp ();
+    assert (iobuf);
+
+    rc = iobuf_write (iobuf, content, strlen (content));
+    assert (rc == 0);
+
+    rc = iobuf_push_filter (iobuf, double_filter, NULL);
+    assert (rc == 0);
+
+    /* Include a NUL.  */
+    rc = iobuf_write (iobuf, content2, strlen (content2) + 1);
+    assert (rc == 0);
+
+    n = iobuf_temp_to_buffer (iobuf, buffer, sizeof (buffer));
+#if 0
+    printf ("Got %d bytes\n", n);
+    printf ("buffer: `");
+    fwrite (buffer, n, 1, stdout);
+    fputc ('\'', stdout);
+    fputc ('\n', stdout);
+#endif
+
+    assert (n == strlen (content) + 2 * (strlen (content2) + 1));
+    assert (strcmp (buffer, "0123456789aabbcc") == 0);
+  }
+
+  {
+    iobuf_t iobuf;
+    int rc;
+    char *content = "0123456789";
+    int n;
+    int c;
+    char buffer[strlen (content)];
+
+    iobuf = iobuf_temp_with_content (content, strlen (content));
+    assert (iobuf);
+
+    rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
+    assert (rc == 0);
+    rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
+    assert (rc == 0);
+
+    for (n = 0; (c = iobuf_get (iobuf)) != -1; n ++)
+      {
+       /* printf ("%d: `%c'\n", n, c);  */
+       buffer[n] = c;
+      }
+
+    assert (n == 2);
+    assert (buffer[0] == '3');
+    assert (buffer[1] == '7');
+  }
+
+  return 0;
+}
index 99cfc2d..a912b82 100644 (file)
@@ -127,12 +127,10 @@ handle_iconv_error (const char *to, const char *from, int use_fallback)
 
   if (use_fallback)
     {
-      /* To avoid further error messages we fallback to Latin-1 for the
-         native encoding.  This is justified as one can expect that on a
-         utf-8 enabled system nl_langinfo() will work and thus we won't
-         never get to here.  Thus Latin-1 seems to be a reasonable
-         default.  */
-      active_charset_name = "iso-8859-1";
+      /* To avoid further error messages we fallback to UTF-8 for the
+         native encoding.  Nowadays this seems to be the best bet in
+         case of errors from iconv or nl_langinfo.  */
+      active_charset_name = "utf-8";
       no_translation = 0;
       use_iconv = 0;
     }
index b38aa06..680d0b7 100644 (file)
@@ -28,7 +28,7 @@ min_automake_version="1.14"
 m4_define([mym4_package],[gnupg])
 m4_define([mym4_major], [2])
 m4_define([mym4_minor], [1])
-m4_define([mym4_micro], [7])
+m4_define([mym4_micro], [8])
 
 # To start a new development series, i.e a new major or minor number
 # you need to mark an arbitrary commit before the first beta release
@@ -1636,7 +1636,16 @@ BUILD_FILEVERSION=`echo "${BUILD_VERSION}" | tr . ,`
 AC_SUBST(BUILD_VERSION)
 AC_SUBST(BUILD_FILEVERSION)
 
-BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date`
+AC_ARG_ENABLE([build-timestamp],
+  AC_HELP_STRING([--enable-build-timestamp],
+                 [set an explicit build timestamp for reproducibility.
+                  (default is the current time in ISO-8601 format)]),
+     [if test "$enableval" = "yes"; then
+        BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date`
+      else
+        BUILD_TIMESTAMP="$enableval"
+      fi],
+     [BUILD_TIMESTAMP="<none>"])
 AC_SUBST(BUILD_TIMESTAMP)
 AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP",
                    [The time this package was configured for a build])
index 1b7e9e9..0f1d262 100644 (file)
 #include "mbox-util.h"
 
 /* To avoid DoS attacks we limit the size of a certificate to
-   something reasonable. */
-#define MAX_CERT_LENGTH (8*1024)
+   something reasonable.  The DoS was actually only an issue back when
+   Dirmngr was a system service and not a user service. */
+#define MAX_CERT_LENGTH (16*1024)
 
 /* The same goes for OpenPGP keyblocks, but here we need to allow for
    much longer blocks; a 200k keyblock is not too unusual for keys
-   with a lot of signatures (e.g. 0x5b0358a2).  */
-#define MAX_KEYBLOCK_LENGTH (512*1024)
+   with a lot of signatures (e.g. 0x5b0358a2).  9C31503C6D866396 even
+   has 770 KiB as of 2015-08-23.  To avoid adding a runtime option we
+   now use 20MiB which should really be enough.  Well, a key with
+   several pictures could be larger (the parser as a 18MiB limit for
+   attribute packets) but it won't be nice to the keyservers to send
+   them such large blobs.  */
+#define MAX_KEYBLOCK_LENGTH (20*1024*1024)
 
 
 #define PARM_ERROR(t) assuan_set_error (ctx, \
index eb889c9..811b105 100644 (file)
@@ -823,9 +823,17 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB:
     numerical error code and an underscore; e.g.: "151011327_EOF".
 
 *** SUCCESS [<location>]
-    Postive confirimation that an operation succeeded.  <location> is
-    optional but if given should not contain spaces.  Used only with a
-    few commands.
+    Postive confirmation that an operation succeeded.  It is used
+    similar to ISO-C's EXIT_SUCCESS.  <location> is optional but if
+    given should not contain spaces.  Used only with a few commands.
+
+*** FAILURE <location> <error_code>
+    This is the counterpart to SUCCESS and used to indicate a program
+    failure.  It is used similar to ISO-C's EXIT_FAILURE but allows to
+    convey more information, in particular an gpg-error error code.
+    That numerical error code may optionally have a suffix made of an
+    underscore and a string with an error symbol like "151011327_EOF".
+    A dash may be used instead of <location>.
 
 *** BADARMOR
     The ASCII armor is corrupted.  No arguments yet.
index c1cd348..5d72017 100644 (file)
@@ -32,11 +32,31 @@ TAB, will not exceed 80 columns.  If you want to add text which shall
 not be copied to the ChangeLog, separate it by a line consisting of
 two dashes at the begin of a line.
 
-Typo fixes and documentation updates don't need a ChangeLog Entry,
+The one-line summary usually starts with a keyword to identify the
+mainly affected subsystem.  If more than one keyword is required the
+are delimited by a comma (e.g. =scd,w32:=). Commonly found keywords
+are
+
+ - agent   :: The gpg-agent component
+ - ssh     :: The ssh-agent part of the agent
+ - common  :: Code in common
+ - iobuf   :: The IOBUF system in common
+ - gpg     :: The gpg or gpgv components
+ - gpgsm   :: The gpgsm component
+ - scd     :: The scdaemon component
+ - ccid    :: The CCID driver in scdaemon
+ - dirmngr :: The dirmngr component
+ - w32     :: Windows related code
+ - po      :: Translations
+ - build   :: Changes to the build system
+ - speedo  :: Speedo build system specific changes
+ - doc     :: Documentation changes
+
+Typo fixes and documentation updates don't need a ChangeLog entry;
 thus you would use a commit message like
 
 #+begin_example
-Fix type in a comment
+Fix typo in a comment
 
 --
 #+end_example
@@ -54,7 +74,6 @@ Note that such a comment will be removed if the git commit option
 =--cleanup=scissor= is used.
 
 
-
 ** License policy
 
   GnuPG is licensed under the GPLv3+ with some files under a mixed
@@ -104,6 +123,12 @@ Note that such a comment will be removed if the git commit option
   need.  If you really need to do it, use a separate commit for such a
   change.
 
+  - C99 syntax should not be used; stick to C90.
+  - Please do not use C++ =//= style comments.
+  - Try to fit lines into 80 columns.
+  - Ignore signed/unsigned pointer mismatches
+  - No arithmetic on void pointers; cast to char* first.
+
 ** Commit log keywords
 
   - GnuPG-bug-id :: Values are comma or space delimited bug numbers
@@ -156,7 +181,6 @@ Note that such a comment will be removed if the git commit option
    the git repositories.  In case of problems, don't hesitate to ask
    on the gnupg-devel mailing for help.
 
-
 * Debug hints
 
   See the manual for some hints.
index 86726b3..9d62afb 100644 (file)
@@ -567,7 +567,7 @@ may be used.
 
 @item --gen-key
 @opindex gen-key
-Generate a new key pair using teh current default parameters.  This is
+Generate a new key pair using the current default parameters.  This is
 the standard command to create a new key.
 
 @item --full-gen-key
@@ -1629,15 +1629,6 @@ modifications, you can use this option to disable the caching. It
 probably does not make sense to disable it because all kind of damage
 can be done if someone else has write access to your public keyring.
 
-@item --no-sig-create-check
-@opindex no-sig-create-check
-GnuPG normally verifies each signature right after creation to protect
-against bugs and hardware malfunctions which could leak out bits from
-the secret key. This extra verification needs some time (about 115%
-for DSA keys), and so this option can be used to disable it.
-However, due to the fact that the signature creation needs manual
-interaction, this performance penalty does not matter in most settings.
-
 @item --auto-check-trustdb
 @itemx --no-auto-check-trustdb
 @opindex auto-check-trustdb
index ea2a4e4..7b2fffe 100644 (file)
@@ -7,12 +7,12 @@
 # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation; either version 3 of the License, or
 # (at your option) any later version.
-# 
+#
 # GnuPG is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 # GNU General Public License for more details.
-# 
+#
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
 
@@ -41,9 +41,9 @@ um das "Netz des Vertrauens" aufzubauen.  Dieses hat nichts mit dem
 
 .gpg.edit_ownertrust.set_ultimate.okay
 Um das Web-of-Trust aufzubauen muß GnuPG wissen, welchen Schlüsseln
-uneingeschränkt vertraut wird. Das sind üblicherweise die Schlüssel
+ultimativ vertraut wird. Das sind üblicherweise die Schlüssel
 auf deren geheimen Schlüssel Sie Zugruff haben.
-Antworten Sie mit "yes" um diesen Schlüssel uneingeschränkt zu vertrauen
+Antworten Sie mit "yes" um diesen Schlüssel ultimativ zu vertrauen
 
 .
 
@@ -74,7 +74,7 @@ unterschrieben werden kann.
 .gpg.keygen.algo.rsa_se
 Normalerweise ist es nicht gut, denselben Schlüssel zum unterschreiben
 und verschlüsseln zu nutzen.  Dieses Verfahren sollte in speziellen
-Anwendungsgebiten benutzt werden.  Bitte lassen Sie sich zuerst von 
+Anwendungsgebiten benutzt werden.  Bitte lassen Sie sich zuerst von
 einem Sicherheistexperten beraten.
 .
 
@@ -138,7 +138,7 @@ sicherstellen, daß der Schlüssel demjenigen gehört, der in der User-ID genann
 ist. Für Dritte ist es hilfreich zu wissen, wie gut diese Zuordnung überprüft
 wurde.
 
-"0" zeigt, daß Sie keine bestimmte Aussage über die Sorgfalt der 
+"0" zeigt, daß Sie keine bestimmte Aussage über die Sorgfalt der
     Schlüsselzuordnung machen.
 
 "1" Sie glauben, daß der Schlüssel der benannten Person gehört,
@@ -224,7 +224,7 @@ Eigenbeglaubigungen werden um eine Sekunde vorgestellt.
 .
 
 .gpg.passphrase.enter
-Bitte geben Sie die Passphrase ein. Dies ist ein geheimer Satz 
+Bitte geben Sie die Passphrase ein. Dies ist ein geheimer Satz
 
 .
 
index ea4afc8..2fd52b3 100644 (file)
 ## Process this file with automake to produce Makefile.in
 
 EXTRA_DIST = options.skel distsigkey.gpg ChangeLog-2011 gpg-w32info.rc \
-            gpg.w32-manifest.in
+            gpg.w32-manifest.in test.c t-keydb-keyring.kbx
 
 AM_CPPFLAGS = -I$(top_srcdir)/common
 
 include $(top_srcdir)/am/cmacros.am
 
-# We need KSBA_CFLAGS because that is included by keybox.h.  See also
-# comments below for libksba.
-AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) \
+AM_CFLAGS = $(LIBGCRYPT_CFLAGS) \
             $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS)
 
 needed_libs = ../kbx/libkeybox.a $(libcommon)
@@ -140,24 +138,24 @@ gpgv2_SOURCES = gpgv.c           \
 #             ks-db.h \
 #             $(common_source)
 
-# FIXME: Libkeybox.a links to libksba thus we need to add libksba
-# here, even that it is not used by gpg.  A proper solution would
-# either to split up libkeybox.a or to use a separate keybox daemon.
 LDADD =  $(needed_libs) ../common/libgpgrl.a \
          $(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS)
 gpg2_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \
-             $(KSBA_LIBS) $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \
+             $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \
             $(LIBICONV) $(resource_objs) $(extra_sys_libs)
 gpg2_LDFLAGS = $(extra_bin_ldflags)
 gpgv2_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \
-              $(KSBA_LIBS) $(GPG_ERROR_LIBS) \
+              $(GPG_ERROR_LIBS) \
              $(LIBICONV) $(resource_objs) $(extra_sys_libs)
 gpgv2_LDFLAGS = $(extra_bin_ldflags)
 
 t_common_ldadd =
-module_tests = t-rmd160
+module_tests = t-rmd160 t-keydb
 t_rmd160_SOURCES = t-rmd160.c rmd160.c
 t_rmd160_LDADD = $(t_common_ldadd)
+t_keydb_SOURCES = t-keydb.c test-stubs.c $(common_source)
+t_keydb_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \
+             $(LIBICONV) $(t_common_ldadd)
 
 
 $(PROGRAMS): $(needed_libs) ../common/libgpgrl.a
index 0df572a..6345784 100644 (file)
@@ -177,11 +177,15 @@ default_inq_cb (void *opaque, const char *line)
       else
         {
           char *pw;
+          char buf[32];
 
           if (parm->keyinfo.keyid)
             emit_status_need_passphrase (parm->keyinfo.keyid,
                                          parm->keyinfo.mainkeyid,
                                          parm->keyinfo.pubkey_algo);
+
+          snprintf (buf, sizeof (buf), "%u", 100);
+          write_status_text (STATUS_INQUIRE_MAXLEN, buf);
           pw = cpr_get_hidden ("passphrase.enter", _("Enter passphrase: "));
           cpr_kill_prompt ();
           if (*pw == CONTROL_D && !pw[1])
@@ -314,9 +318,12 @@ start_agent (ctrl_t ctrl, int for_card)
                                NULL, NULL, NULL, NULL, NULL, NULL);
               xfree (tmp);
               if (rc)
-                log_error ("setting pinentry mode '%s' failed: %s\n",
-                           str_pinentry_mode (opt.pinentry_mode),
-                           gpg_strerror (rc));
+                {
+                  log_error ("setting pinentry mode '%s' failed: %s\n",
+                             str_pinentry_mode (opt.pinentry_mode),
+                             gpg_strerror (rc));
+                  write_status_error ("set_pinentry_mode", rc);
+                }
             }
 
           check_hijacking (agent_ctx);
index 9fc9e09..9d8fec9 100644 (file)
--- a/g10/cpr.c
+++ b/g10/cpr.c
@@ -183,7 +183,7 @@ write_status_text (int no, const char *text)
   write_status_strings (no, text, NULL);
 }
 
-/* Wrte an ERROR status line using a full gpg-error error value.  */
+/* Write an ERROR status line using a full gpg-error error value.  */
 void
 write_status_error (const char *where, gpg_error_t err)
 {
@@ -211,6 +211,20 @@ write_status_errcode (const char *where, int errcode)
 }
 
 
+/* Write a FAILURE status line.  */
+void
+write_status_failure (const char *where, gpg_error_t err)
+{
+  if (!statusfp || !status_currently_allowed (STATUS_FAILURE))
+    return;  /* Not enabled or allowed. */
+
+  es_fprintf (statusfp, "[GNUPG:] %s %s %u\n",
+              get_status_string (STATUS_FAILURE), where, err);
+  if (es_fflush (statusfp) && opt.exit_on_status_write_error)
+    g10_exit (0);
+}
+
+
 /*
  * Write a status line with a buffer using %XX escapes.  If WRAP is >
  * 0 wrap the line after this length.  If STRING is not NULL it will
index fe6fd63..068b64a 100644 (file)
@@ -120,7 +120,7 @@ decrypt_message_fd (ctrl_t ctrl, int input_fd, int output_fd)
   pfx = new_progress_context ();
 
   /* Open the message file.  */
-  fp = iobuf_open_fd_or_name (input_fd, NULL, "rb");
+  fp = iobuf_fdopen_nc (FD2INT(input_fd), "rb");
   if (fp && is_secured_file (iobuf_get_fd (fp)))
     {
       iobuf_close (fp);
index d5835d4..e2e1c05 100644 (file)
@@ -510,14 +510,17 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
   /* Prepare iobufs. */
 #ifdef HAVE_W32_SYSTEM
   if (filefd == -1)
-    inp = iobuf_open_fd_or_name (GNUPG_INVALID_FD, filename, "rb");
+    inp = iobuf_open (filename);
   else
     {
       inp = NULL;
       gpg_err_set_errno (ENOSYS);
     }
 #else
-  inp = iobuf_open_fd_or_name (filefd, filename, "rb");
+  if (filefd == GNUPG_INVALID_FD)
+    inp = iobuf_open (filename);
+  else
+    inp = iobuf_fdopen_nc (FD2INT(filefd), "rb");
 #endif
   if (inp)
     iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL);
index 5050128..62802d3 100644 (file)
@@ -1011,7 +1011,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
                   int i;
 
                   for (i=0;i<node->pkt->pkt.signature->numrevkeys;i++)
-                    if ( (node->pkt->pkt.signature->revkey[i]->class & 0x40))
+                    if ( (node->pkt->pkt.signature->revkey[i].class & 0x40))
                       break;
 
                   if (i < node->pkt->pkt.signature->numrevkeys)
index 3a60161..028b049 100644 (file)
@@ -55,7 +55,6 @@ struct getkey_ctx_s
   KBNODE found_key;     /* Pointer into some keyblock. */
   strlist_t extra_list;         /* Will be freed when releasing the context.  */
   int req_usage;
-  int req_algo;
   KEYDB_HANDLE kr_handle;
   int not_allocated;
   int nitems;
@@ -392,7 +391,6 @@ get_pubkey (PKT_public_key * pk, u32 * keyid)
     ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID;
     ctx.items[0].u.kid[0] = keyid[0];
     ctx.items[0].u.kid[1] = keyid[1];
-    ctx.req_algo = pk->req_algo;
     ctx.req_usage = pk->req_usage;
     rc = lookup (&ctx, &kb, 0);
     if (!rc)
@@ -524,7 +522,6 @@ get_seckey (PKT_public_key *pk, u32 *keyid)
   ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID;
   ctx.items[0].u.kid[0] = keyid[0];
   ctx.items[0].u.kid[1] = keyid[1];
-  ctx.req_algo = pk->req_algo;
   ctx.req_usage = pk->req_usage;
   err = lookup (&ctx, &keyblock, 1);
   if (!err)
@@ -661,7 +658,6 @@ key_byname (GETKEY_CTX *retctx, strlist_t namelist,
 
   if (pk)
     {
-      ctx->req_algo = pk->req_algo;
       ctx->req_usage = pk->req_usage;
     }
 
@@ -720,7 +716,7 @@ get_pubkey_byname (ctrl_t ctrl, GETKEY_CTX * retctx, PKT_public_key * pk,
 
      ANYLOCALFIRST is set if the search order has the local method
      before any other or if "local" is used first by default.  This
-     makes sure that if a RETCTX is used it gets only set if a local
+     makes sure that if a RETCTX is used it is only set if a local
      search has precedence over the other search methods and only then
      a followup call to get_pubkey_next shall succeed.  */
   if (!no_akl)
@@ -1606,7 +1602,7 @@ merge_selfsigs_main (KBNODE keyblock, int *r_revoked,
 
                      for (i = 0; i < sig->numrevkeys; i++)
                        memcpy (&pk->revkey[pk->numrevkeys++],
-                               sig->revkey[i],
+                               &sig->revkey[i],
                                sizeof (struct revocation_key));
                    }
 
@@ -2606,7 +2602,7 @@ lookup (getkey_ctx_t ctx, kbnode_t *ret_keyblock, int want_secret)
         goto skip; /* No secret key available.  */
 
       /* Warning: node flag bits 0 and 1 should be preserved by
-       * merge_selfsigs.  For secret keys, premerge did tranfer the
+       * merge_selfsigs.  For secret keys, premerge transferred the
        * keys to the keyblock.  */
       merge_selfsigs (ctx->keyblock);
       if (finish_lookup (ctx))
@@ -2621,10 +2617,11 @@ lookup (getkey_ctx_t ctx, kbnode_t *ret_keyblock, int want_secret)
       /* Release resources and continue search. */
       release_kbnode (ctx->keyblock);
       ctx->keyblock = NULL;
-      /* We need to disable the caching so that for an exact key search we
-         won't get the result back from the cache and thus end up in an
-         endless loop.  Disabling this here is sufficient because although
-         the result may have been cached, if won't be used then.  */
+      /* We need to disable the caching so that for an exact key
+         search we won't get the result back from the cache and thus
+         end up in an endless loop.  Disabling the cache here at this
+         point is sufficient because even a cached result won't be
+         used after a call to keydb_disable_caching.  */
       keydb_disable_caching (ctx->kr_handle);
     }
 
index 10d8c20..9454b53 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -337,7 +337,6 @@ enum cmd_and_opt_values
     oFixedListMode,
     oLegacyListMode,
     oNoSigCache,
-    oNoSigCreateCheck,
     oAutoCheckTrustDB,
     oNoAutoCheckTrustDB,
     oPreservePermissions,
@@ -727,7 +726,6 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_s_n (oAutoKeyRetrieve, "auto-key-retrieve", "@"),
   ARGPARSE_s_n (oNoAutoKeyRetrieve, "no-auto-key-retrieve", "@"),
   ARGPARSE_s_n (oNoSigCache,         "no-sig-cache", "@"),
-  ARGPARSE_s_n (oNoSigCreateCheck,   "no-sig-create-check", "@"),
   ARGPARSE_s_n (oMergeOnly,      "merge-only", "@" ),
   ARGPARSE_s_n (oAllowSecretKeyImport, "allow-secret-key-import", "@"),
   ARGPARSE_s_n (oTryAllSecrets,  "try-all-secrets", "@"),
@@ -2990,7 +2988,6 @@ main (int argc, char **argv)
             }
             break;
           case oNoSigCache: opt.no_sig_cache = 1; break;
-          case oNoSigCreateCheck: opt.no_sig_create_check = 1; break;
          case oAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid = 1; break;
          case oNoAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid=0; break;
          case oAllowFreeformUID: opt.allow_freeform_uid = 1; break;
@@ -3661,15 +3658,21 @@ main (int argc, char **argv)
        if( argc > 1 )
            wrong_args(_("--store [filename]"));
        if( (rc = encrypt_store(fname)) )
+          {
+            write_status_failure ("store", rc);
            log_error ("storing '%s' failed: %s\n",
                        print_fname_stdin(fname),gpg_strerror (rc) );
+          }
        break;
       case aSym: /* encrypt the given file only with the symmetric cipher */
        if( argc > 1 )
            wrong_args(_("--symmetric [filename]"));
        if( (rc = encrypt_symmetric(fname)) )
+          {
+            write_status_failure ("symencrypt", rc);
             log_error (_("symmetric encryption of '%s' failed: %s\n"),
                         print_fname_stdin(fname),gpg_strerror (rc) );
+          }
        break;
 
       case aEncr: /* encrypt the given file */
@@ -3680,8 +3683,11 @@ main (int argc, char **argv)
            if( argc > 1 )
              wrong_args(_("--encrypt [filename]"));
            if( (rc = encrypt_crypt (ctrl, -1, fname, remusr, 0, NULL, -1)) )
-             log_error("%s: encryption failed: %s\n",
-                       print_fname_stdin(fname), gpg_strerror (rc) );
+              {
+                write_status_failure ("encrypt", rc);
+                log_error("%s: encryption failed: %s\n",
+                          print_fname_stdin(fname), gpg_strerror (rc) );
+              }
          }
        break;
 
@@ -3701,8 +3707,11 @@ main (int argc, char **argv)
        else
          {
            if( (rc = encrypt_crypt (ctrl, -1, fname, remusr, 1, NULL, -1)) )
-             log_error("%s: encryption failed: %s\n",
-                       print_fname_stdin(fname), gpg_strerror (rc) );
+              {
+                write_status_failure ("encrypt", rc);
+                log_error ("%s: encryption failed: %s\n",
+                           print_fname_stdin(fname), gpg_strerror (rc) );
+              }
          }
        break;
 
@@ -3720,8 +3729,11 @@ main (int argc, char **argv)
                strcpy(sl->d, fname);
            }
        }
-       if( (rc = sign_file (ctrl, sl, detached_sig, locusr, 0, NULL, NULL)) )
-           log_error("signing failed: %s\n", gpg_strerror (rc) );
+       if ((rc = sign_file (ctrl, sl, detached_sig, locusr, 0, NULL, NULL)))
+          {
+            write_status_failure ("sign", rc);
+           log_error ("signing failed: %s\n", gpg_strerror (rc) );
+          }
        free_strlist(sl);
        break;
 
@@ -3735,8 +3747,11 @@ main (int argc, char **argv)
        else
            sl = NULL;
        if ((rc = sign_file (ctrl, sl, detached_sig, locusr, 1, remusr, NULL)))
+          {
+            write_status_failure ("sign-encrypt", rc);
            log_error("%s: sign+encrypt failed: %s\n",
                      print_fname_stdin(fname), gpg_strerror (rc) );
+          }
        free_strlist(sl);
        break;
 
@@ -3760,8 +3775,11 @@ main (int argc, char **argv)
              sl = NULL;
            if ((rc = sign_file (ctrl, sl, detached_sig, locusr,
                                  2, remusr, NULL)))
-             log_error("%s: symmetric+sign+encrypt failed: %s\n",
-                       print_fname_stdin(fname), gpg_strerror (rc) );
+              {
+                write_status_failure ("sign-encrypt", rc);
+                log_error("%s: symmetric+sign+encrypt failed: %s\n",
+                          print_fname_stdin(fname), gpg_strerror (rc) );
+              }
            free_strlist(sl);
          }
        break;
@@ -3771,19 +3789,26 @@ main (int argc, char **argv)
            wrong_args(_("--sign --symmetric [filename]"));
        rc = sign_symencrypt_file (fname, locusr);
         if (rc)
+          {
+            write_status_failure ("sign-symencrypt", rc);
            log_error("%s: sign+symmetric failed: %s\n",
                       print_fname_stdin(fname), gpg_strerror (rc) );
+          }
        break;
 
       case aClearsign: /* make a clearsig */
        if( argc > 1 )
            wrong_args(_("--clearsign [filename]"));
        if( (rc = clearsign_file(fname, locusr, NULL)) )
+          {
+            write_status_failure ("sign", rc);
            log_error("%s: clearsign failed: %s\n",
                       print_fname_stdin(fname), gpg_strerror (rc) );
+          }
        break;
 
       case aVerify:
+        rc = 0;
        if (multifile)
          {
            if ((rc = verify_files (ctrl, argc, argv)))
@@ -3794,6 +3819,8 @@ main (int argc, char **argv)
            if ((rc = verify_signatures (ctrl, argc, argv)))
              log_error("verify signatures failed: %s\n", gpg_strerror (rc) );
          }
+        if (rc)
+          write_status_failure ("verify", rc);
        break;
 
       case aDecrypt:
@@ -3804,7 +3831,10 @@ main (int argc, char **argv)
            if( argc > 1 )
              wrong_args(_("--decrypt [filename]"));
            if( (rc = decrypt_message (ctrl, fname) ))
-             log_error("decrypt_message failed: %s\n", gpg_strerror (rc) );
+              {
+                write_status_failure ("decrypt", rc);
+                log_error("decrypt_message failed: %s\n", gpg_strerror (rc) );
+              }
          }
        break;
 
@@ -3930,9 +3960,18 @@ main (int argc, char **argv)
            generate_keypair (ctrl, 0, argc? *argv : NULL, NULL, 0);
        }
        else {
-           if( argc )
-               wrong_args("--gen-key");
-           generate_keypair (ctrl, 0, NULL, NULL, 0);
+            if (opt.command_fd != -1 && argc)
+              {
+                if( argc > 1 )
+                  wrong_args("--gen-key [parameterfile]");
+
+                opt.batch = 1;
+                generate_keypair (ctrl, 0, argc? *argv : NULL, NULL, 0);
+              }
+            else if (argc)
+              wrong_args ("--gen-key");
+            else
+              generate_keypair (ctrl, 0, NULL, NULL, 0);
        }
        break;
 
@@ -3989,11 +4028,21 @@ main (int argc, char **argv)
        if(rc)
          {
            if(cmd==aSendKeys)
-             log_error(_("keyserver send failed: %s\n"),gpg_strerror (rc));
+              {
+                write_status_failure ("send-keys", rc);
+                log_error(_("keyserver send failed: %s\n"),gpg_strerror (rc));
+              }
            else if(cmd==aRecvKeys)
-             log_error(_("keyserver receive failed: %s\n"),gpg_strerror (rc));
+              {
+                write_status_failure ("recv-keys", rc);
+                log_error (_("keyserver receive failed: %s\n"),
+                           gpg_strerror (rc));
+              }
            else
-             log_error(_("key export failed: %s\n"),gpg_strerror (rc));
+              {
+                write_status_failure ("export", rc);
+                log_error (_("key export failed: %s\n"), gpg_strerror (rc));
+              }
          }
        free_strlist(sl);
        break;
@@ -4004,7 +4053,10 @@ main (int argc, char **argv)
          append_to_strlist2 (&sl, *argv, utf8_strings);
        rc = keyserver_search (ctrl, sl);
        if (rc)
-         log_error (_("keyserver search failed: %s\n"), gpg_strerror (rc));
+          {
+            write_status_failure ("search-keys", rc);
+            log_error (_("keyserver search failed: %s\n"), gpg_strerror (rc));
+          }
        free_strlist (sl);
        break;
 
@@ -4014,7 +4066,10 @@ main (int argc, char **argv)
            append_to_strlist2( &sl, *argv, utf8_strings );
        rc = keyserver_refresh (ctrl, sl);
        if(rc)
-         log_error(_("keyserver refresh failed: %s\n"),gpg_strerror (rc));
+          {
+            write_status_failure ("refresh-keys", rc);
+            log_error (_("keyserver refresh failed: %s\n"),gpg_strerror (rc));
+          }
        free_strlist(sl);
        break;
 
@@ -4024,7 +4079,10 @@ main (int argc, char **argv)
            append_to_strlist2( &sl, *argv, utf8_strings );
        rc = keyserver_fetch (ctrl, sl);
        if(rc)
-         log_error("key fetch failed: %s\n",gpg_strerror (rc));
+          {
+            write_status_failure ("fetch-keys", rc);
+            log_error ("key fetch failed: %s\n",gpg_strerror (rc));
+          }
        free_strlist(sl);
        break;
 
@@ -4065,7 +4123,10 @@ main (int argc, char **argv)
            wrong_args("--dearmor [file]");
        rc = dearmor_file( argc? *argv: NULL );
        if( rc )
-           log_error(_("dearmoring failed: %s\n"), gpg_strerror (rc));
+          {
+            write_status_failure ("dearmor", rc);
+            log_error (_("dearmoring failed: %s\n"), gpg_strerror (rc));
+          }
        break;
 
       case aEnArmor:
@@ -4073,7 +4134,10 @@ main (int argc, char **argv)
            wrong_args("--enarmor [file]");
        rc = enarmor_file( argc? *argv: NULL );
        if( rc )
-           log_error(_("enarmoring failed: %s\n"), gpg_strerror (rc));
+          {
+            write_status_failure ("enarmor", rc);
+           log_error (_("enarmoring failed: %s\n"), gpg_strerror (rc));
+          }
        break;
 
 
@@ -4265,7 +4329,7 @@ main (int argc, char **argv)
         else if (argc == 1)
             change_pin (atoi (*argv),1);
         else
-        wrong_args ("--change-pin [no]");
+            wrong_args ("--change-pin [no]");
         break;
 #endif /* ENABLE_CARD_SUPPORT*/
 
@@ -4319,7 +4383,11 @@ main (int argc, char **argv)
            }
            rc = proc_packets (ctrl, NULL, a );
            if( rc )
-               log_error("processing message failed: %s\n", gpg_strerror (rc));
+              {
+                write_status_failure ("-", rc);
+                log_error ("processing message failed: %s\n",
+                           gpg_strerror (rc));
+              }
            iobuf_close(a);
        }
        break;
index e92769d..048b136 100644 (file)
@@ -1073,7 +1073,7 @@ import_one (ctrl_t ctrl,
     {
       KEYDB_HANDLE hd = keydb_new ();
 
-      rc = keydb_locate_writable (hd, NULL);
+      rc = keydb_locate_writable (hd);
       if (rc)
         {
           log_error (_("no writable keyring found: %s\n"), gpg_strerror (rc));
@@ -2397,7 +2397,7 @@ revocation_present (ctrl_t ctrl, kbnode_t keyblock)
            {
              u32 keyid[2];
 
-             keyid_from_fingerprint(sig->revkey[idx]->fpr,
+             keyid_from_fingerprint(sig->revkey[idx].fpr,
                                     MAX_FINGERPRINT_LEN,keyid);
 
              for(inode=keyblock->next;inode;inode=inode->next)
@@ -2416,7 +2416,7 @@ revocation_present (ctrl_t ctrl, kbnode_t keyblock)
                          itself? */
                      int rc;
 
-                     rc=get_pubkey_byfprint_fast (NULL,sig->revkey[idx]->fpr,
+                     rc=get_pubkey_byfprint_fast (NULL,sig->revkey[idx].fpr,
                                                    MAX_FINGERPRINT_LEN);
                      if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY
                           || gpg_err_code (rc) == GPG_ERR_UNUSABLE_PUBKEY)
@@ -2432,13 +2432,13 @@ revocation_present (ctrl_t ctrl, kbnode_t keyblock)
                                         " fetching revocation key %s\n"),
                                       tempkeystr,keystr(keyid));
                              keyserver_import_fprint (ctrl,
-                                                       sig->revkey[idx]->fpr,
+                                                       sig->revkey[idx].fpr,
                                                        MAX_FINGERPRINT_LEN,
                                                        opt.keyserver);
 
                              /* Do we have it now? */
                              rc=get_pubkey_byfprint_fast (NULL,
-                                                    sig->revkey[idx]->fpr,
+                                                    sig->revkey[idx].fpr,
                                                     MAX_FINGERPRINT_LEN);
                            }
 
index b31c6a6..da18bc0 100644 (file)
@@ -62,36 +62,6 @@ static struct resource_item all_resources[MAX_KEYDB_RESOURCES];
 static int used_resources;
 static void *primary_keyring=NULL;
 
-struct keydb_handle
-{
-  int locked;
-  int found;
-  int saved_found;
-  unsigned long skipped_long_blobs;
-  int no_caching;
-  int current;
-  int used;   /* Number of items in ACTIVE. */
-  struct resource_item active[MAX_KEYDB_RESOURCES];
-};
-
-
-/* This object is used to keep a list of keyids in a linked list.  */
-typedef struct kid_list_s
-{
-  struct kid_list_s *next;
-  u32 kid[2];
-  int state;  /* True if found.  */
-} *kid_list_t;
-
-/* To avoid looking up a key by keyid where we know that it does not
-   yet exist, we keep a table of keyids with search results.  This
-   improves the --list-sigs and --check-sigs commands substantively.
-   To avoid extra complexity we clear the entire table on any insert
-   or update operation.  The array is indexed by the LSB of the keyid.
-   KID_FOUND_TABLE_COUNT gives the number of keys in the table.  */
-static kid_list_t kid_found_table[256];
-static unsigned int kid_found_table_count;
-
 
 /* This is a simple cache used to return the last result of a
    successful fingerprint search.  This works only for keybox resources
@@ -104,115 +74,188 @@ enum keyblock_cache_states {
   KEYBLOCK_CACHE_FILLED
 };
 
-struct {
+struct keyblock_cache {
   enum keyblock_cache_states state;
   byte fpr[MAX_FINGERPRINT_LEN];
   iobuf_t iobuf; /* Image of the keyblock.  */
   u32 *sigstatus;
   int pk_no;
   int uid_no;
-} keyblock_cache;
+};
+
+
+struct keydb_handle
+{
+  /* When we locked all of the resources in ACTIVE (using keyring_lock
+     / keybox_lock, as appropriate).  */
+  int locked;
+
+  /* The index into ACTIVE of the resources in which the last search
+     result was found.  Initially -1.  */
+  int found;
+
+  /* Initially -1 (invalid).  This is used to save a search result and
+     later restore it as the selected result.  */
+  int saved_found;
+
+  /* The number of skipped long blobs since the last search
+     (keydb_search_reset).  */
+  unsigned long skipped_long_blobs;
+
+  /* If set, this disables the use of the keyblock cache.  */
+  int no_caching;
+
+  /* Whether the next search will be from the beginning of the
+     database (and thus consider all records).  */
+  int is_reset;
+
+  /* The "file position."  In our case, this is index of the current
+     resource in ACTIVE.  */
+  int current;
+
+  /* The number of resources in ACTIVE.  */
+  int used;
+
+  /* Cache of the last found and parsed key block (only used for
+     keyboxes, not keyrings).  */
+  struct keyblock_cache keyblock_cache;
+
+  /* Copy of ALL_RESOURCES when keydb_new is called.  */
+  struct resource_item active[MAX_KEYDB_RESOURCES];
+};
+
+/* Looking up keys is expensive.  To hide the cost, we cache whether
+   keys exist in the key database.  Then, if we know a key does not
+   exist, we don't have to spend time looking it up.  This
+   particularly helps the --list-sigs and --check-sigs commands.
+
+   The cache stores the results in a hash using separate chaining.
+   Concretely: we use the LSB of the keyid to index the hash table and
+   each bucket consists of a linked list of entries.  An entry
+   consists of the 64-bit key id.  If a key id is not in the cache,
+   then we don't know whether it is in the DB or not.
+
+   To simplify the cache consistency protocol, we simply flush the
+   whole cache whenever a key is inserted or updated.  */
+
+#define KID_NOT_FOUND_CACHE_BUCKETS 256
+static struct kid_not_found_cache_bucket *
+  kid_not_found_cache[KID_NOT_FOUND_CACHE_BUCKETS];
+
+/* The total number of entries in the hash table.  */
+static unsigned int kid_not_found_cache_count;
+
+struct kid_not_found_cache_bucket
+{
+  struct kid_not_found_cache_bucket *next;
+  u32 kid[2];
+};
 
 
 static int lock_all (KEYDB_HANDLE hd);
 static void unlock_all (KEYDB_HANDLE hd);
 
 
-/* Checkwhether the keyid KID is in the table of found or not found
-   keyids.
+/* Check whether the keyid KID is in key id is definately not in the
+   database.
 
    Returns:
-     0 - Keyid not in table
-     1 - Keyid in table because not found in a previous search
-     2 - Keyid in table because found in a previous search
- */
+
+     0 - Indeterminate: the key id is not in the cache; we don't know
+         whether the key is in the database or not.  If you want a
+         definitive answer, you'll need to perform a lookup.
+
+     1 - There is definitely no key with this key id in the database.
+         We searched for a key with this key id previously, but we
+         didn't find it in the database.  */
 static int
 kid_not_found_p (u32 *kid)
 {
-  kid_list_t k;
+  struct kid_not_found_cache_bucket *k;
 
-  for (k = kid_found_table[kid[0] % 256]; k; k = k->next)
+  for (k = kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS]; k; k = k->next)
     if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
       {
         if (DBG_CACHE)
-          log_debug ("keydb: kid_not_found_p (%08lx%08lx) => %s\n",
-                     (ulong)kid[0], (ulong)kid[1],
-                     k->state? "false (found)": "true");
-        return k->state? 2 : 1;
+          log_debug ("keydb: kid_not_found_p (%08lx%08lx) => not in DB\n",
+                     (ulong)kid[0], (ulong)kid[1]);
+        return 1;
       }
 
   if (DBG_CACHE)
-    log_debug ("keydb: kid_not_found_p (%08lx%08lx) => false\n",
+    log_debug ("keydb: kid_not_found_p (%08lx%08lx) => indeterminate\n",
                (ulong)kid[0], (ulong)kid[1]);
   return 0;
 }
 
 
-/* Put the keyid KID into the table of keyids with their find states of
-   previous searches.  Note that there is no check whether the keyid
-   is already in the table, thus kid_not_found_p() should be used prior.  */
+/* Insert the keyid KID into the kid_not_found_cache.  FOUND is whether
+   the key is in the key database or not.
+
+   Note this function does not check whether the key id is already in
+   the cache.  As such, kid_not_found_p() should be called first.  */
 static void
-kid_not_found_insert (u32 *kid, int found)
+kid_not_found_insert (u32 *kid)
 {
-  kid_list_t k;
+  struct kid_not_found_cache_bucket *k;
 
   if (DBG_CACHE)
-    log_debug ("keydb: kid_not_found_insert (%08lx%08lx, %d)\n",
-               (ulong)kid[0], (ulong)kid[1], found);
+    log_debug ("keydb: kid_not_found_insert (%08lx%08lx)\n",
+               (ulong)kid[0], (ulong)kid[1]);
   k = xmalloc (sizeof *k);
   k->kid[0] = kid[0];
   k->kid[1] = kid[1];
-  k->state = found;
-  k->next = kid_found_table[kid[0]%256];
-  kid_found_table[kid[0]%256] = k;
-  kid_found_table_count++;
+  k->next = kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS];
+  kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS] = k;
+  kid_not_found_cache_count++;
 }
 
 
-/* Flush the entire table of keyids whche were not found in previous
-   searches.  */
+/* Flush the kid not found cache.  */
 static void
 kid_not_found_flush (void)
 {
-  kid_list_t k, knext;
+  struct kid_not_found_cache_bucket *k, *knext;
   int i;
 
   if (DBG_CACHE)
     log_debug ("keydb: kid_not_found_flush\n");
 
-  if (!kid_found_table_count)
+  if (!kid_not_found_cache_count)
     return;
 
-  for (i=0; i < DIM(kid_found_table); i++)
+  for (i=0; i < DIM(kid_not_found_cache); i++)
     {
-      for (k = kid_found_table[i]; k; k = knext)
+      for (k = kid_not_found_cache[i]; k; k = knext)
         {
           knext = k->next;
           xfree (k);
         }
-      kid_found_table[i] = NULL;
+      kid_not_found_cache[i] = NULL;
     }
-  kid_found_table_count = 0;
+  kid_not_found_cache_count = 0;
 }
 
 
 static void
-keyblock_cache_clear (void)
+keyblock_cache_clear (struct keydb_handle *hd)
 {
-  keyblock_cache.state = KEYBLOCK_CACHE_EMPTY;
-  xfree (keyblock_cache.sigstatus);
-  keyblock_cache.sigstatus = NULL;
-  iobuf_close (keyblock_cache.iobuf);
-  keyblock_cache.iobuf = NULL;
+  hd->keyblock_cache.state = KEYBLOCK_CACHE_EMPTY;
+  xfree (hd->keyblock_cache.sigstatus);
+  hd->keyblock_cache.sigstatus = NULL;
+  iobuf_close (hd->keyblock_cache.iobuf);
+  hd->keyblock_cache.iobuf = NULL;
 }
 
 
 /* Handle the creation of a keyring or a keybox if it does not yet
    exist.  Take into account that other processes might have the
    keyring/keybox already locked.  This lock check does not work if
-   the directory itself is not yet available.  If is IS_BOX is true
-   the filename is expected to be a keybox.  If FORCE_CREATE is true
-   the keyring or keybox shall be created.  */
+   the directory itself is not yet available.  If IS_BOX is true the
+   filename is expected to refer to a keybox.  If FORCE_CREATE is true
+   the keyring or keybox will be created.
+
+   Return 0 if it is okay to access the specified file.  */
 static int
 maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
 {
@@ -375,10 +418,15 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
 }
 
 
-/* Helper for keydb_add_resource.  Opens FILENAME to figures out the
-   resource type.  Returns the resource type and a flag at R_NOTFOUND
-   indicating whether FILENAME could be opened at all.  If the openpgp
-   flag is set in a keybox header, R_OPENPGP will be set to true.  */
+/* Helper for keydb_add_resource.  Opens FILENAME to figure out the
+   resource type.
+
+   Returns the specified file's likely type.  If the file does not
+   exist, returns KEYDB_RESOURCE_TYPE_NONE and sets *R_FOUND to 0.
+   Otherwise, tries to figure out the file's type.  This is either
+   KEYDB_RESOURCE_TYPE_KEYBOX, KEYDB_RESOURCE_TYPE_KEYRING or
+   KEYDB_RESOURCE_TYPE_KEYNONE.  If the file is a keybox and it has
+   the OpenPGP flag set, then R_OPENPGP is also set.  */
 static KeydbResourceType
 rt_from_file (const char *filename, int *r_found, int *r_openpgp)
 {
@@ -419,17 +467,14 @@ rt_from_file (const char *filename, int *r_found, int *r_openpgp)
 }
 
 
-/*
- * Register a resource (keyring or aeybox).  The first keyring or
- * keybox which is added by this function is created if it does not
- * exist.  FLAGS are a combination of the KEYDB_RESOURCE_FLAG_
- * constants as defined in keydb.h.
- */
 gpg_error_t
 keydb_add_resource (const char *url, unsigned int flags)
 {
+  /* Whether we have successfully registered a resource.  */
   static int any_registered;
+  /* The file named by the URL (i.e., without the prototype).  */
   const char *resname = url;
+
   char *filename = NULL;
   int create;
   int read_only = !!(flags&KEYDB_RESOURCE_FLAG_READONLY);
@@ -442,11 +487,6 @@ keydb_add_resource (const char *url, unsigned int flags)
   /* Create the resource if it is the first registered one.  */
   create = (!read_only && !any_registered);
 
-  /* Do we have an URL?
-   *   gnupg-ring:filename  := this is a plain keyring.
-   *   gnupg-kbx:filename   := this is a keybox file.
-   *   filename := See what is is, but create as plain keyring.
-   */
   if (strlen (resname) > 11 && !strncmp( resname, "gnupg-ring:", 11) )
     {
       rt = KEYDB_RESOURCE_TYPE_KEYRING;
@@ -646,8 +686,9 @@ keydb_add_resource (const char *url, unsigned int flags)
 void
 keydb_dump_stats (void)
 {
-  if (kid_found_table_count)
-    log_info ("keydb: kid_not_found_table: total: %u\n", kid_found_table_count);
+  if (kid_not_found_cache_count)
+    log_info ("keydb: kid_not_found_cache: total: %u\n",
+             kid_not_found_cache_count);
 }
 
 
@@ -656,6 +697,7 @@ keydb_new (void)
 {
   KEYDB_HANDLE hd;
   int i, j;
+  int die = 0;
 
   if (DBG_CLOCK)
     log_clock ("keydb_new");
@@ -663,9 +705,10 @@ keydb_new (void)
   hd = xmalloc_clear (sizeof *hd);
   hd->found = -1;
   hd->saved_found = -1;
+  hd->is_reset = 1;
 
   assert (used_resources <= MAX_KEYDB_RESOURCES);
-  for (i=j=0; i < used_resources; i++)
+  for (i=j=0; ! die && i < used_resources; i++)
     {
       switch (all_resources[i].type)
         {
@@ -675,10 +718,8 @@ keydb_new (void)
           hd->active[j].type   = all_resources[i].type;
           hd->active[j].token  = all_resources[i].token;
           hd->active[j].u.kr = keyring_new (all_resources[i].token);
-          if (!hd->active[j].u.kr) {
-            xfree (hd);
-            return NULL; /* fixme: release all previously allocated handles*/
-          }
+          if (!hd->active[j].u.kr)
+           die = 1;
           j++;
           break;
         case KEYDB_RESOURCE_TYPE_KEYBOX:
@@ -686,10 +727,7 @@ keydb_new (void)
           hd->active[j].token  = all_resources[i].token;
           hd->active[j].u.kb   = keybox_new_openpgp (all_resources[i].token, 0);
           if (!hd->active[j].u.kb)
-            {
-              xfree (hd);
-              return NULL; /* fixme: release all previously allocated handles*/
-            }
+           die = 1;
           j++;
           break;
         }
@@ -697,6 +735,13 @@ keydb_new (void)
   hd->used = j;
 
   active_handles++;
+
+  if (die)
+    {
+      keydb_release (hd);
+      hd = NULL;
+    }
+
   return hd;
 }
 
@@ -731,9 +776,6 @@ keydb_release (KEYDB_HANDLE hd)
 }
 
 
-/* Set a flag on handle to not use cached results.  This is required
-   for updating a keyring and for key listins.  Fixme: Using a new
-   parameter for keydb_new might be a better solution.  */
 void
 keydb_disable_caching (KEYDB_HANDLE hd)
 {
@@ -742,14 +784,6 @@ keydb_disable_caching (KEYDB_HANDLE hd)
 }
 
 
-/*
- * Return the name of the current resource.  This is function first
- * looks for the last found found, then for the current search
- * position, and last returns the first available resource.  The
- * returned string is only valid as long as the handle exists.  This
- * function does only return NULL if no handle is specified, in all
- * other error cases an empty string is returned.
- */
 const char *
 keydb_get_resource_name (KEYDB_HANDLE hd)
 {
@@ -863,7 +897,6 @@ unlock_all (KEYDB_HANDLE hd)
 
 
 \f
-/* Push the last found state if any.  */
 void
 keydb_push_found_state (KEYDB_HANDLE hd)
 {
@@ -893,7 +926,6 @@ keydb_push_found_state (KEYDB_HANDLE hd)
 }
 
 
-/* Pop the last found state.  */
 void
 keydb_pop_found_state (KEYDB_HANDLE hd)
 {
@@ -1084,12 +1116,6 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no,
 }
 
 
-/*
- * Return the last found keyring.  Caller must free it.
- * The returned keyblock has the kbode flag bit 0 set for the node with
- * the public key used to locate the keyblock or flag bit 1 set for
- * the user ID node.
- */
 gpg_error_t
 keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
 {
@@ -1103,20 +1129,28 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
   if (DBG_CLOCK)
     log_clock ("keydb_get_keybock enter");
 
-  if (keyblock_cache.state == KEYBLOCK_CACHE_FILLED)
+  if (hd->keyblock_cache.state == KEYBLOCK_CACHE_FILLED)
     {
-      iobuf_seek (keyblock_cache.iobuf, 0);
-      err = parse_keyblock_image (keyblock_cache.iobuf,
-                                  keyblock_cache.pk_no,
-                                  keyblock_cache.uid_no,
-                                  keyblock_cache.sigstatus,
-                                  ret_kb);
+      err = iobuf_seek (hd->keyblock_cache.iobuf, 0);
       if (err)
-        keyblock_cache_clear ();
-      if (DBG_CLOCK)
-        log_clock (err? "keydb_get_keyblock leave (cached, failed)"
-                      : "keydb_get_keyblock leave (cached)");
-      return err;
+       {
+         log_error ("keydb_get_keyblock: failed to rewind iobuf for cache\n");
+         keyblock_cache_clear (hd);
+       }
+      else
+       {
+         err = parse_keyblock_image (hd->keyblock_cache.iobuf,
+                                     hd->keyblock_cache.pk_no,
+                                     hd->keyblock_cache.uid_no,
+                                     hd->keyblock_cache.sigstatus,
+                                     ret_kb);
+         if (err)
+           keyblock_cache_clear (hd);
+         if (DBG_CLOCK)
+           log_clock (err? "keydb_get_keyblock leave (cached, failed)"
+                      : "keydb_get_keyblock leave (cached)");
+         return err;
+       }
     }
 
   if (hd->found < 0 || hd->found >= hd->used)
@@ -1142,13 +1176,13 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
           {
             err = parse_keyblock_image (iobuf, pk_no, uid_no, sigstatus,
                                         ret_kb);
-            if (!err && keyblock_cache.state == KEYBLOCK_CACHE_PREPARED)
+            if (!err && hd->keyblock_cache.state == KEYBLOCK_CACHE_PREPARED)
               {
-                keyblock_cache.state     = KEYBLOCK_CACHE_FILLED;
-                keyblock_cache.sigstatus = sigstatus;
-                keyblock_cache.iobuf     = iobuf;
-                keyblock_cache.pk_no     = pk_no;
-                keyblock_cache.uid_no    = uid_no;
+                hd->keyblock_cache.state     = KEYBLOCK_CACHE_FILLED;
+                hd->keyblock_cache.sigstatus = sigstatus;
+                hd->keyblock_cache.iobuf     = iobuf;
+                hd->keyblock_cache.pk_no     = pk_no;
+                hd->keyblock_cache.uid_no    = uid_no;
               }
             else
               {
@@ -1160,8 +1194,8 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
       break;
     }
 
-  if (keyblock_cache.state != KEYBLOCK_CACHE_FILLED)
-    keyblock_cache_clear ();
+  if (hd->keyblock_cache.state != KEYBLOCK_CACHE_FILLED)
+    keyblock_cache_clear (hd);
 
   if (DBG_CLOCK)
     log_clock (err? "keydb_get_keyblock leave (failed)"
@@ -1260,9 +1294,6 @@ build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf, u32 **r_sigstatus)
 }
 
 
-/*
- * Update the current keyblock with the keyblock KB
- */
 gpg_error_t
 keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
 {
@@ -1272,7 +1303,7 @@ keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
     return gpg_error (GPG_ERR_INV_ARG);
 
   kid_not_found_flush ();
-  keyblock_cache_clear ();
+  keyblock_cache_clear (hd);
 
   if (hd->found < 0 || hd->found >= hd->used)
     return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
@@ -1313,9 +1344,6 @@ keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
 }
 
 
-/*
- * Insert a new KB into one of the resources.
- */
 gpg_error_t
 keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
 {
@@ -1326,7 +1354,7 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
     return gpg_error (GPG_ERR_INV_ARG);
 
   kid_not_found_flush ();
-  keyblock_cache_clear ();
+  keyblock_cache_clear (hd);
 
   if (opt.dry_run)
     return 0;
@@ -1377,9 +1405,6 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
 }
 
 
-/*
- * Delete the current keyblock.
- */
 gpg_error_t
 keydb_delete_keyblock (KEYDB_HANDLE hd)
 {
@@ -1389,7 +1414,7 @@ keydb_delete_keyblock (KEYDB_HANDLE hd)
     return gpg_error (GPG_ERR_INV_ARG);
 
   kid_not_found_flush ();
-  keyblock_cache_clear ();
+  keyblock_cache_clear (hd);
 
   if (hd->found < 0 || hd->found >= hd->used)
     return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
@@ -1420,18 +1445,11 @@ keydb_delete_keyblock (KEYDB_HANDLE hd)
 
 
 \f
-/*
- * Locate the default writable key resource, so that the next
- * operation (which is only relevant for inserts) will be done on this
- * resource.
- */
 gpg_error_t
-keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
+keydb_locate_writable (KEYDB_HANDLE hd)
 {
   gpg_error_t rc;
 
-  (void)reserved;
-
   if (!hd)
     return GPG_ERR_INV_ARG;
 
@@ -1479,16 +1497,11 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
   return gpg_error (GPG_ERR_NOT_FOUND);
 }
 
-/*
- * Rebuild the caches of all key resources.
- */
 void
 keydb_rebuild_caches (int noisy)
 {
   int i, rc;
 
-  keyblock_cache_clear ();
-
   for (i=0; i < used_resources; i++)
     {
       if (!keyring_is_writable (all_resources[i].token))
@@ -1511,7 +1524,6 @@ keydb_rebuild_caches (int noisy)
 }
 
 
-/* Return the number of skipped blocks since the last search reset.  */
 unsigned long
 keydb_get_skipped_counter (KEYDB_HANDLE hd)
 {
@@ -1519,9 +1531,6 @@ keydb_get_skipped_counter (KEYDB_HANDLE hd)
 }
 
 
-/*
- * Start the next search on this handle right at the beginning
- */
 gpg_error_t
 keydb_search_reset (KEYDB_HANDLE hd)
 {
@@ -1531,7 +1540,7 @@ keydb_search_reset (KEYDB_HANDLE hd)
   if (!hd)
     return gpg_error (GPG_ERR_INV_ARG);
 
-  keyblock_cache_clear ();
+  keyblock_cache_clear (hd);
 
   if (DBG_CLOCK)
     log_clock ("keydb_search_reset");
@@ -1557,6 +1566,7 @@ keydb_search_reset (KEYDB_HANDLE hd)
           break;
         }
     }
+  hd->is_reset = 1;
   return rc;
 }
 
@@ -1608,18 +1618,14 @@ dump_search_desc (KEYDB_HANDLE hd, const char *text,
 }
 
 
-/*
- * Search through all keydb resources, starting at the current
- * position, for a keyblock which contains one of the keys described
- * in the DESC array.  Returns GPG_ERR_NOT_FOUND if no matching
- * keyring was found.
- */
 gpg_error_t
 keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
               size_t ndesc, size_t *descindex)
 {
   gpg_error_t rc;
-  int once_found = 0;
+  int was_reset = hd->is_reset;
+  /* If an entry is already in the cache, then don't add it again.  */
+  int already_in_cache = 0;
 
   if (descindex)
     *descindex = 0; /* Make sure it is always set on return.  */
@@ -1634,14 +1640,8 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
     dump_search_desc (hd, "keydb_search", desc, ndesc);
 
 
-  /* Note that we track the found state in the table to cope with the
-     case that a initial search found the key and the next search
-     (without a reset) did not found the key.  Without keeping the
-     found state we would falsely claim that the key has not been
-     found.  Actually this is quite common because we need to check
-     for ambgious keyids.  */
   if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID
-      && (once_found = kid_not_found_p (desc[0].u.kid)) == 1 )
+      && (already_in_cache = kid_not_found_p (desc[0].u.kid)) == 1 )
     {
       if (DBG_CLOCK)
         log_clock ("keydb_search leave (not found, cached)");
@@ -1655,8 +1655,8 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
       && ndesc == 1
       && (desc[0].mode == KEYDB_SEARCH_MODE_FPR20
           || desc[0].mode == KEYDB_SEARCH_MODE_FPR)
-      && keyblock_cache.state  == KEYBLOCK_CACHE_FILLED
-      && !memcmp (keyblock_cache.fpr, desc[0].u.fpr, 20))
+      && hd->keyblock_cache.state  == KEYBLOCK_CACHE_FILLED
+      && !memcmp (hd->keyblock_cache.fpr, desc[0].u.fpr, 20))
     {
       /* (DESCINDEX is already set).  */
       if (DBG_CLOCK)
@@ -1691,27 +1691,26 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
       else if (!rc)
         hd->found = hd->current;
     }
+  hd->is_reset = 0;
 
   rc = ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
         ? gpg_error (GPG_ERR_NOT_FOUND)
         : rc);
 
-  keyblock_cache_clear ();
+  keyblock_cache_clear (hd);
   if (!hd->no_caching
       && !rc
       && ndesc == 1 && (desc[0].mode == KEYDB_SEARCH_MODE_FPR20
                         || desc[0].mode == KEYDB_SEARCH_MODE_FPR))
     {
-      keyblock_cache.state = KEYBLOCK_CACHE_PREPARED;
-      memcpy (keyblock_cache.fpr, desc[0].u.fpr, 20);
+      hd->keyblock_cache.state = KEYBLOCK_CACHE_PREPARED;
+      memcpy (hd->keyblock_cache.fpr, desc[0].u.fpr, 20);
     }
 
-  if ((!rc || gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
-      && ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID
-      && !once_found)
-    {
-      kid_not_found_insert (desc[0].u.kid, !rc);
-    }
+  if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND
+      && ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID && was_reset
+      && !already_in_cache)
+    kid_not_found_insert (desc[0].u.kid);
 
   if (DBG_CLOCK)
     log_clock (rc? "keydb_search leave (not found)"
@@ -1720,14 +1719,16 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
 }
 
 
-/* Note that in contrast to using keydb_search in search first mode,
-   this function skips legacy keys.  */
 gpg_error_t
 keydb_search_first (KEYDB_HANDLE hd)
 {
   gpg_error_t err;
   KEYDB_SEARCH_DESC desc;
 
+  err = keydb_search_reset (hd);
+  if (err)
+    return err;
+
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_FIRST;
   err = keydb_search (hd, &desc, 1, NULL);
@@ -1737,8 +1738,6 @@ keydb_search_first (KEYDB_HANDLE hd)
 }
 
 
-/* Note that in contrast to using keydb_search in search next mode,
-   this fucntion skips legacy keys.  */
 gpg_error_t
 keydb_search_next (KEYDB_HANDLE hd)
 {
index b64438c..a943ded 100644 (file)
@@ -1,6 +1,7 @@
 /* keydb.h - Key database
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
  *               2006, 2010 Free Software Foundation, Inc.
+ * Copyright (C) 2015 g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
@@ -132,28 +133,212 @@ union pref_hint
 #define KEYDB_RESOURCE_FLAG_READONLY 8  /* Open in read only mode.  */
 #define KEYDB_RESOURCE_FLAG_GPGVDEF 16  /* Default file for gpgv.  */
 
+/* Register a resource (keyring or keybox).  The first keyring or
+   keybox that is added using this function is created if it does not
+   already exist and the KEYDB_RESOURCE_FLAG_READONLY is not set.
+
+   FLAGS are a combination of the KEYDB_RESOURCE_FLAG_* constants.
+
+   URL must have the following form:
+
+     gnupg-ring:filename  = plain keyring
+     gnupg-kbx:filename   = keybox file
+     filename             = check file's type (create as a plain keyring)
+
+   Note: on systems with drive letters (Windows) invalid URLs (i.e.,
+   those with an unrecognized part before the ':' such as "c:\...")
+   will silently be treated as bare filenames.  On other systems, such
+   URLs will cause this function to return GPG_ERR_GENERAL.
+
+   If KEYDB_RESOURCE_FLAG_DEFAULT is set, the resource is a keyring
+   and the file ends in ".gpg", then this function also checks if a
+   file with the same name, but the extension ".kbx" exists, is a
+   keybox and the OpenPGP flag is set.  If so, this function opens
+   that resource instead.
+
+   If the file is not found, KEYDB_RESOURCE_FLAG_GPGVDEF is set and
+   the URL ends in ".kbx", then this function will try opening the
+   same URL, but with the extension ".gpg".  If that file is a keybox
+   with the OpenPGP flag set or it is a keyring, then we use that
+   instead.
+
+   If the file is not found, KEYDB_RESOURCE_FLAG_DEFAULT is set, the
+   file should be created and the file's extension is ".gpg" then we
+   replace the extension with ".kbx".
+
+
+   If the KEYDB_RESOURCE_FLAG_PRIMARY is set and the resource is a
+   keyring (not a keybox), then this resource is considered the
+   primary resource.  This is used by keydb_locate_writable().  If
+   another primary keyring is set, then that keyring is considered the
+   primary.
+
+   If KEYDB_RESOURCE_FLAG_READONLY is set and the resource is a
+   keyring (not a keybox), then the keyring is marked as read only and
+   operations just as keyring_insert_keyblock will return
+   GPG_ERR_ACCESS.  */
 gpg_error_t keydb_add_resource (const char *url, unsigned int flags);
-void        keydb_dump_stats (void);
 
+/* Dump some statistics to the log.  */
+void keydb_dump_stats (void);
+
+/* Create a new database handle.  A database handle is similar to a
+   file handle: it contains a local file position.  This is used when
+   searching: subsequent searches resume where the previous search
+   left off.  To rewind the position, use keydb_search_reset().  */
 KEYDB_HANDLE keydb_new (void);
+
+/* Free all resources owned by the database handle.  */
 void keydb_release (KEYDB_HANDLE hd);
+
+/* Set a flag on the handle to suppress use of cached results.  This
+   is required for updating a keyring and for key listings.  Fixme:
+   Using a new parameter for keydb_new might be a better solution.  */
 void keydb_disable_caching (KEYDB_HANDLE hd);
+
+/* Save the last found state and invalidate the current selection
+   (i.e., the entry selected by keydb_search() is invalidated and
+   something like keydb_get_keyblock() will return an error).  This
+   does not change the file position.  This makes it possible to do
+   something like:
+
+     keydb_search (hd, ...);  // Result 1.
+     keydb_push_found_state (hd);
+       keydb_search_reset (hd);
+       keydb_search (hd, ...);  // Result 2.
+     keydb_pop_found_state (hd);
+     keydb_get_keyblock (hd, ...);  // -> Result 1.
+
+   Note: it is only possible to save a single save state at a time.
+   In other words, the the save stack only has room for a single
+   instance of the state.  */
 void keydb_push_found_state (KEYDB_HANDLE hd);
+
+/* Restore the previous save state.  If the saved state is invalid,
+   this is equivalent to */
 void keydb_pop_found_state (KEYDB_HANDLE hd);
+
+/* Return the file name of the resource in which the current search
+   result was found or, if there is no search result, the filename of
+   the current resource (i.e., the resource that the file position
+   points to).  Note: the filename is not necessarily the URL used to
+   open it!
+
+   This function only returns NULL if no handle is specified, in all
+   other error cases an empty string is returned.  */
 const char *keydb_get_resource_name (KEYDB_HANDLE hd);
+
+/* Return the keyblock last found by keydb_search() in *RET_KB.
+
+   On success, the function returns 0 and the caller must free *RET_KB
+   using release_kbnode().  Otherwise, the function returns an error
+   code.
+
+   The returned keyblock has the kbnode flag bit 0 set for the node
+   with the public key used to locate the keyblock or flag bit 1 set
+   for the user ID node.  */
 gpg_error_t keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb);
+
+/* Replace the currently selected keyblock (i.e., the last result
+   returned by keydb_search) with the key block in KB.
+
+   This doesn't do anything if --dry-run was specified.
+
+   Returns 0 on success.  Otherwise, it returns an error code.  */
 gpg_error_t keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb);
+
+/* Insert a keyblock into one of the underlying keyrings or keyboxes.
+
+   Be default, the keyring / keybox from which the last search result
+   came is used.  If there was no previous search result (or
+   keydb_search_reset was called), then the keyring / keybox where the
+   next search would start is used (i.e., the current file position).
+
+   Note: this doesn't do anything if --dry-run was specified.
+
+   Returns 0 on success.  Otherwise, it returns an error code.  */
 gpg_error_t keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb);
+
+/* Delete the currently selected keyblock.  If you haven't done a
+   search yet on this database handle (or called keydb_search_reset),
+   then this will return an error.
+
+   Returns 0 on success or an error code, if an error occurs.  */
 gpg_error_t keydb_delete_keyblock (KEYDB_HANDLE hd);
-gpg_error_t keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved);
+
+/* A database may consists of multiple keyrings / key boxes.  This
+   sets the "file position" to the start of the first keyring / key
+   box that is writable (i.e., doesn't have the read-only flag set).
+
+   This first tries the primary keyring (the last keyring (not
+   keybox!) added using keydb_add_resource() and with
+   KEYDB_RESOURCE_FLAG_PRIMARY set).  If that is not writable, then it
+   tries the keyrings / keyboxes in the order in which they were
+   added.  */
+gpg_error_t keydb_locate_writable (KEYDB_HANDLE hd);
+
+/* Rebuild the on-disk caches of all key resources.  */
 void keydb_rebuild_caches (int noisy);
+
+/* Return the number of skipped blocks (because they were to large to
+   read from a keybox) since the last search reset.  */
 unsigned long keydb_get_skipped_counter (KEYDB_HANDLE hd);
+
+/* Clears the current search result and resets the handle's position
+   so that the next search starts at the beginning of the database
+   (the start of the first resource).
+
+   Returns 0 on success and an error code if an error occured.
+   (Currently, this function always returns 0 if HD is valid.)  */
 gpg_error_t keydb_search_reset (KEYDB_HANDLE hd);
+
+/* Search the database for keys matching the search description.
+
+   DESC is an array of search terms with NDESC entries.  The search
+   terms are or'd together.  That is, the next entry in the DB that
+   matches any of the descriptions will be returned.
+
+   Note: this function resumes searching where the last search left
+   off (i.e., at the current file position).  If you want to search
+   from the start of the database, then you need to first call
+   keydb_search_reset().
+
+   If no key matches the search description, returns
+   GPG_ERR_NOT_FOUND.  If there was a match, returns 0.  If an error
+   occured, returns an error code.
+
+   The returned key is considered to be selected and the raw data can,
+   for instance, be returned by calling keydb_get_keyblock().  */
 gpg_error_t keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
                           size_t ndesc, size_t *descindex);
+
+/* Return the first non-legacy key in the database.
+
+   If you want the very first key in the database, you can directly
+   call keydb_search with the search description
+   KEYDB_SEARCH_MODE_FIRST.  */
 gpg_error_t keydb_search_first (KEYDB_HANDLE hd);
+
+/* Return the next key (not the next matching key!).
+
+   Unlike calling keydb_search with KEYDB_SEARCH_MODE_NEXT, this
+   function silently skips legacy keys.  */
 gpg_error_t keydb_search_next (KEYDB_HANDLE hd);
+
+/* This is a convenience function for searching for keys with a long
+   key id.
+
+   Note: this function resumes searching where the last search left
+   off.  If you want to search the whole database, then you need to
+   first call keydb_search_reset().  */
 gpg_error_t keydb_search_kid (KEYDB_HANDLE hd, u32 *kid);
+
+/* This is a convenience function for searching for keys with a long
+   (20 byte) fingerprint.  This function ignores legacy keys.
+
+   Note: this function resumes searching where the last search left
+   off.  If you want to search the whole database, then you need to
+   first call keydb_search_reset().  */
 gpg_error_t keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr);
 
 
@@ -235,6 +420,10 @@ int get_pubkey_byfprint_fast (PKT_public_key *pk,
 int get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint,
                                                 size_t fprint_len );
 
+/* Return whether a secret key is available for the public key with
+   key id KEYID.  Note: this is just a fast check and does not tell us
+   whether the secret key is valid; this check merely indicates
+   whether there is some secret key with the specified key id.  */
 int have_secret_key_with_kid (u32 *keyid);
 
 gpg_error_t get_seckey_byname (PKT_public_key *pk, const char *name);
index d8dba2d..0fc8c36 100644 (file)
@@ -1041,6 +1041,7 @@ sign_uids (ctrl_t ctrl, estream_t fp,
                                          NULL);
              if (rc)
                {
+                  write_status_error ("keysig", rc);
                  log_error (_("signing failed: %s\n"), gpg_strerror (rc));
                  goto leave;
                }
@@ -3497,6 +3498,7 @@ menu_adduid (kbnode_t pub_keyblock, int photo, const char *photo_name,
                             keygen_add_std_prefs, pk, NULL);
   if (err)
     {
+      write_status_error ("keysig", err);
       log_error ("signing failed: %s\n", gpg_strerror (err));
       free_user_id (uid);
       return 0;
@@ -3881,6 +3883,7 @@ menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive)
                           keygen_add_revkey, &revkey, NULL);
   if (rc)
     {
+      write_status_error ("keysig", rc);
       log_error ("signing failed: %s\n", gpg_strerror (rc));
       goto fail;
     }
@@ -5172,6 +5175,7 @@ reloop:                   /* (must use this, because we are modifing the list) */
       free_public_key (signerkey);
       if (rc)
        {
+          write_status_error ("keysig", rc);
          log_error (_("signing failed: %s\n"), gpg_strerror (rc));
          release_revocation_reason_info (reason);
          return changed;
@@ -5263,6 +5267,7 @@ menu_revuid (KBNODE pub_keyblock)
                                     sign_mk_attrib, &attrib, NULL);
            if (rc)
              {
+                write_status_error ("keysig", rc);
                log_error (_("signing failed: %s\n"), gpg_strerror (rc));
                goto leave;
              }
@@ -5327,6 +5332,7 @@ menu_revkey (KBNODE pub_keyblock)
                           revocation_reason_build_cb, reason, NULL);
   if (rc)
     {
+      write_status_error ("keysig", rc);
       log_error (_("signing failed: %s\n"), gpg_strerror (rc));
       goto scram;
     }
@@ -5388,6 +5394,7 @@ menu_revsubkey (KBNODE pub_keyblock)
                                    NULL);
          if (rc)
            {
+              write_status_error ("keysig", rc);
              log_error (_("signing failed: %s\n"), gpg_strerror (rc));
              release_revocation_reason_info (reason);
              return changed;
index f03c148..dd37559 100644 (file)
@@ -4146,7 +4146,7 @@ do_generate_keypair (struct para_data_s *para,
     {
       KEYDB_HANDLE pub_hd = keydb_new ();
 
-      err = keydb_locate_writable (pub_hd, NULL);
+      err = keydb_locate_writable (pub_hd);
       if (err)
         log_error (_("no writable public keyring found: %s\n"),
                    gpg_strerror (err));
index 0383931..cc92d1a 100644 (file)
@@ -626,6 +626,9 @@ locate_one (ctrl_t ctrl, strlist_t names)
        {
          if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY)
            log_error ("error reading key: %s\n", gpg_strerror (rc));
+          else if (opt.verbose)
+            log_info (_("key \"%s\" not found: %s\n"),
+                      sl->d, gpg_strerror (rc));
        }
       else
        {
index 06c497d..42d5ce1 100644 (file)
@@ -180,6 +180,7 @@ int  is_status_enabled ( void );
 void write_status ( int no );
 void write_status_error (const char *where, gpg_error_t err);
 void write_status_errcode (const char *where, int errcode);
+void write_status_failure (const char *where, gpg_error_t err);
 void write_status_text ( int no, const char *text );
 void write_status_strings (int no, const char *text,
                            ...) GPGRT_ATTR_SENTINEL(0);
index 465c5b2..fd2f4a2 100644 (file)
@@ -191,7 +191,6 @@ struct
   int try_all_secrets;
   int no_expensive_trust_checks;
   int no_sig_cache;
-  int no_sig_create_check;
   int no_auto_check_trustdb;
   int preserve_permissions;
   int no_homedir_creation;
index 523178b..1906ec5 100644 (file)
@@ -1,6 +1,7 @@
 /* packet.h - OpenPGP packet definitions
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
  *               2007 Free Software Foundation, Inc.
+ * Copyright (C) 2015 g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
@@ -166,7 +167,7 @@ typedef struct
   byte    trust_depth;
   byte    trust_value;
   const byte *trust_regexp;
-  struct revocation_key **revkey;
+  struct revocation_key *revkey;
   int numrevkeys;
   pka_info_t *pka_info;      /* Malloced PKA data or NULL if not
                                 available.  See also flags.pka_tried. */
@@ -272,7 +273,6 @@ typedef struct
   byte    pubkey_algo;    /* algorithm used for public key scheme */
   byte    pubkey_usage;   /* for now only used to pass it to getkey() */
   byte    req_usage;      /* hack to pass a request to getkey() */
-  byte    req_algo;       /* Ditto */
   u32     has_expired;    /* set to the expiration date if expired */
   u32     main_keyid[2];  /* keyid of the primary key */
   u32     keyid[2];        /* calculated by keyid_from_pk() */
@@ -373,7 +373,7 @@ struct packet_struct {
        PKT_pubkey_enc  *pubkey_enc;    /* PKT_PUBKEY_ENC */
        PKT_onepass_sig *onepass_sig;   /* PKT_ONEPASS_SIG */
        PKT_signature   *signature;     /* PKT_SIGNATURE */
-       PKT_public_key  *public_key;    /* PKT_PUBLIC_[SUB)KEY */
+       PKT_public_key  *public_key;    /* PKT_PUBLIC_[SUB]KEY */
        PKT_public_key  *secret_key;    /* PKT_SECRET_[SUB]KEY */
        PKT_comment     *comment;       /* PKT_COMMENT */
        PKT_user_id     *user_id;       /* PKT_USER_ID */
@@ -417,9 +417,19 @@ int proc_encryption_packets (ctrl_t ctrl, void *ctx, iobuf_t a);
 int list_packets( iobuf_t a );
 
 /*-- parse-packet.c --*/
+
+/* Sets the packet list mode to MODE (i.e., whether we are dumping a
+   packet or not).  Returns the current mode.  This allows for
+   temporarily suspending dumping by doing the following:
+
+     int saved_mode = set_packet_list_mode (0);
+     ...
+     set_packet_list_mode (saved_mode);
+*/
 int set_packet_list_mode( int mode );
 
 #if DEBUG_PARSE_PACKET
+/* There are debug functions and should not be used directly.  */
 int dbg_search_packet( iobuf_t inp, PACKET *pkt, off_t *retpos, int with_uid,
                        const char* file, int lineno  );
 int dbg_parse_packet( iobuf_t inp, PACKET *ret_pkt,
@@ -441,28 +451,147 @@ int dbg_skip_some_packets( iobuf_t inp, unsigned n,
 #define skip_some_packets( a,b ) \
              dbg_skip_some_packets((a),(b), __FILE__, __LINE__ )
 #else
+/* Return the next valid OpenPGP packet in *PKT.  (This function will
+   skip any packets whose type is 0.)
+
+   Returns 0 on success, -1 if EOF is reached, and an error code
+   otherwise.  In the case of an error, the packet in *PKT may be
+   partially constructed.  As such, even if there is an error, it is
+   necessary to free *PKT to avoid a resource leak.  To detect what
+   has been allocated, clear *PKT before calling this function.  */
+int parse_packet( iobuf_t inp, PACKET *pkt);
+
+/* Return the first OpenPGP packet in *PKT that contains a key (either
+   a public subkey, a public key, a secret subkey or a secret key) or,
+   if WITH_UID is set, a user id.
+
+   Saves the position in the pipeline of the start of the returned
+   packet (according to iobuf_tell) in RETPOS, if it is not NULL.
+
+   The return semantics are the same as parse_packet.  */
 int search_packet( iobuf_t inp, PACKET *pkt, off_t *retpos, int with_uid );
-int parse_packet( iobuf_t inp, PACKET *ret_pkt);
+
+/* Copy all packets (except invalid packets, i.e., those with a type
+   of 0) from INP to OUT until either an error occurs or EOF is
+   reached.
+
+   Returns -1 when end of file is reached or an error code, if an
+   error occured.  (Note: this function never returns 0, because it
+   effectively keeps going until it gets an EOF.)  */
 int copy_all_packets( iobuf_t inp, iobuf_t out );
+
+/* Like copy_all_packets, but stops at the first packet that starts at
+   or after STOPOFF (as indicated by iobuf_tell).
+
+   Example: if STOPOFF is 100, the first packet in INP goes from 0 to
+   110 and the next packet starts at offset 111, then the packet
+   starting at offset 0 will be completely processed (even though it
+   extends beyond STOPOFF) and the packet starting at offset 111 will
+   not be processed at all.  */
 int copy_some_packets( iobuf_t inp, iobuf_t out, off_t stopoff );
+
+/* Skips the next N packets from INP.
+
+   If parsing a packet returns an error code, then the function stops
+   immediately and returns the error code.  Note: in the case of an
+   error, this function does not indicate how many packets were
+   successfully processed.  */
 int skip_some_packets( iobuf_t inp, unsigned n );
 #endif
 
+/* Parse a signature packet and store it in *SIG.
+
+   The signature packet is read from INP.  The OpenPGP header (the tag
+   and the packet's length) have already been read; the next byte read
+   from INP should be the first byte of the packet's contents.  The
+   packet's type (as extract from the tag) must be passed as PKTTYPE
+   and the packet's length must be passed as PKTLEN.  This is used as
+   the upper bound on the amount of data read from INP.  If the packet
+   is shorter than PKTLEN, the data at the end will be silently
+   skipped.  If an error occurs, an error code will be returned.  -1
+   means the EOF was encountered.  0 means parsing was successful.  */
 int parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen,
                     PKT_signature *sig );
+
+/* Given a subpacket area (typically either PKT_signature.hashed or
+   PKT_signature.unhashed), either:
+
+     - test whether there are any subpackets with the critical bit set
+       that we don't understand,
+
+     - list the subpackets, or,
+
+     - find a subpacket with a specific type.
+
+   REQTYPE indicates the type of operation.
+
+   If REQTYPE is SIGSUBPKT_TEST_CRITICAL, then this function checks
+   whether there are any subpackets that have the critical bit and
+   which GnuPG cannot handle.  If GnuPG understands all subpackets
+   whose critical bit is set, then this function returns simply
+   returns SUBPKTS.  If there is a subpacket whose critical bit is set
+   and which GnuPG does not understand, then this function returns
+   NULL and, if START is not NULL, sets *START to the 1-based index of
+   the subpacket that violates the constraint.
+
+   If REQTYPE is SIGSUBPKT_LIST_HASHED or SIGSUBPKT_LIST_UNHASHED, the
+   packets are dumped.  Note: if REQTYPE is SIGSUBPKT_LIST_HASHED,
+   this function does not check whether the hash is correct; this is
+   merely an indication of the section that the subpackets came from.
+
+   If REQTYPE is anything else, then this function interprets the
+   values as a subpacket type and looks for the first subpacket with
+   that type.  If such a packet is found, *CRITICAL (if not NULL) is
+   set if the critical bit was set, *RET_N is set to the offset of the
+   subpacket's content within the SUBPKTS buffer, *START is set to the
+   1-based index of the subpacket within the buffer, and returns
+   &SUBPKTS[*RET_N].
+
+   *START is the number of initial subpackets to not consider.  Thus,
+   if *START is 2, then the first 2 subpackets are ignored.  */
 const byte *enum_sig_subpkt ( const subpktarea_t *subpkts,
                               sigsubpkttype_t reqtype,
                               size_t *ret_n, int *start, int *critical );
+
+/* Shorthand for:
+
+     enum_sig_subpkt (buffer, reqtype, ret_n, NULL, NULL); */
 const byte *parse_sig_subpkt ( const subpktarea_t *buffer,
                                sigsubpkttype_t reqtype,
                                size_t *ret_n );
+
+/* This calls parse_sig_subpkt first on the hashed signature area in
+   SIG and then, if that returns NULL, calls parse_sig_subpkt on the
+   unhashed subpacket area in SIG.  */
 const byte *parse_sig_subpkt2 ( PKT_signature *sig,
-                                sigsubpkttype_t reqtype,
-                                size_t *ret_n );
+                                sigsubpkttype_t reqtype);
+
+/* Returns whether the N byte large buffer BUFFER is sufficient to
+   hold a subpacket of type TYPE.  Note: the buffer refers to the
+   contents of the subpacket (not the header) and it must already be
+   initialized: for some subpackets, it checks some internal
+   constraints.
+
+   Returns 0 if the size is acceptable.  Returns -2 if the buffer is
+   definately too short.  To check for an error, check whether the
+   return value is less than 0.  */
 int parse_one_sig_subpkt( const byte *buffer, size_t n, int type );
+
+/* Looks for revocation key subpackets (see RFC 4880 5.2.3.15) in the
+   hashed area of the signature packet.  Any that are found are added
+   to SIG->REVKEY and SIG->NUMREVKEYS is updated appropriately.  */
 void parse_revkeys(PKT_signature *sig);
+
+/* Extract the attributes from the buffer at UID->ATTRIB_DATA and
+   update UID->ATTRIBS and UID->NUMATTRIBS accordingly.  */
 int parse_attribute_subpkts(PKT_user_id *uid);
+
+/* Set the UID->NAME field according to the attributes.  MAX_NAMELEN
+   must be at least 71.  */
 void make_attribute_uidname(PKT_user_id *uid, size_t max_namelen);
+
+/* Allocate and initialize a new GPG control packet.  DATA is the data
+   to save in the packet.  */
 PACKET *create_gpg_control ( ctrlpkttype_t type,
                              const byte *data,
                              size_t datalen );
index 478612a..4e236cb 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
  *               2007, 2009, 2010 Free Software Foundation, Inc.
  * Copyright (C) 2014 Werner Koch
+ * Copyright (C) 2015 g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
@@ -87,6 +88,7 @@ static int parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen,
 static int parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen,
                              PACKET * packet, int partial);
 
+/* Read a 16-bit value in MSB order (big endian) from an iobuf.  */
 static unsigned short
 read_16 (IOBUF inp)
 {
@@ -97,6 +99,7 @@ read_16 (IOBUF inp)
 }
 
 
+/* Read a 32-bit value in MSB order (big endian) from an iobuf.  */
 static unsigned long
 read_32 (IOBUF inp)
 {
@@ -109,11 +112,18 @@ read_32 (IOBUF inp)
 }
 
 
-/* Read an external representation of an mpi and return the MPI.  The
- * external format is a 16 bit unsigned value stored in network byte
- * order, giving the number of bits for the following integer. The
- * integer is stored with MSB first (left padded with zero bits to align
- * on a byte boundary).  */
+/* Read an external representation of an MPI and return the MPI.  The
+   external format is a 16-bit unsigned value stored in network byte
+   order giving the number of bits for the following integer.  The
+   integer is stored MSB first and is left padded with zero bits to
+   align on a byte boundary.
+
+   The caller must set *RET_NREAD to the maximum number of bytes to
+   read from the pipeline INP.  This function sets *RET_NREAD to be
+   the number of bytes actually read from the pipeline.
+
+   If SECURE is true, the integer is stored in secure memory
+   (allocated using gcry_xmalloc_secure).  */
 static gcry_mpi_t
 mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
 {
@@ -150,10 +160,15 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
   p[1] = c2;
   for (i = 0; i < nbytes; i++)
     {
-      p[i + 2] = iobuf_get (inp) & 0xff;
       if (nread == nmax)
-        goto overflow;
-      nread++;
+       goto overflow;
+
+      c = iobuf_get (inp);
+      if (c == -1)
+       goto leave;
+
+      p[i + 2] = c;
+      nread ++;
     }
 
   if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread))
@@ -213,6 +228,8 @@ set_packet_list_mode (int mode)
 }
 
 
+/* If OPT.VERBOSE is set, print a warning that the algorithm ALGO is
+   not suitable for signing and encryption.  */
 static void
 unknown_pubkey_warning (int algo)
 {
@@ -245,12 +262,6 @@ unknown_pubkey_warning (int algo)
 }
 
 
-/* Parse a packet and return it in packet structure.
- * Returns: 0 := valid packet in pkt
- *        -1 := no more packets
- *        >0 := error
- * Note: The function may return an error and a partly valid packet;
- * caller must free this packet.   */
 #ifdef DEBUG_PARSE_PACKET
 int
 dbg_parse_packet (IOBUF inp, PACKET *pkt, const char *dbg_f, int dbg_l)
@@ -261,7 +272,7 @@ dbg_parse_packet (IOBUF inp, PACKET *pkt, const char *dbg_f, int dbg_l)
     {
       rc = parse (inp, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #else /*!DEBUG_PARSE_PACKET*/
@@ -274,7 +285,7 @@ parse_packet (IOBUF inp, PACKET * pkt)
     {
       rc = parse (inp, pkt, 0, NULL, &skip, NULL, 0);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #endif /*!DEBUG_PARSE_PACKET*/
@@ -297,7 +308,7 @@ dbg_search_packet (IOBUF inp, PACKET * pkt, off_t * retpos, int with_uid,
        parse (inp, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0, "search",
               dbg_f, dbg_l);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #else /*!DEBUG_PARSE_PACKET*/
@@ -310,7 +321,7 @@ search_packet (IOBUF inp, PACKET * pkt, off_t * retpos, int with_uid)
     {
       rc = parse (inp, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #endif /*!DEBUG_PARSE_PACKET*/
@@ -325,6 +336,10 @@ dbg_copy_all_packets (IOBUF inp, IOBUF out, const char *dbg_f, int dbg_l)
 {
   PACKET pkt;
   int skip, rc = 0;
+
+  if (! out)
+    log_bug ("copy_all_packets: OUT may not be NULL.\n");
+
   do
     {
       init_packet (&pkt);
@@ -340,6 +355,10 @@ copy_all_packets (IOBUF inp, IOBUF out)
 {
   PACKET pkt;
   int skip, rc = 0;
+
+  if (! out)
+    log_bug ("copy_all_packets: OUT may not be NULL.\n");
+
   do
     {
       init_packet (&pkt);
@@ -424,12 +443,33 @@ skip_some_packets (IOBUF inp, unsigned n)
 #endif /*!DEBUG_PARSE_PACKET*/
 
 
-/*
- * Parse packet.  Stores 1 at SKIP 1 if the packet should be skipped;
- * this is the case if either ONLYKEYPKTS is set and the parsed packet
- * isn't a key packet or the packet-type is 0, indicating deleted
- * stuff.  If OUT is not NULL, a special copymode is used.
- */
+/* Parse a packet and save it in *PKT.
+
+   If OUT is not NULL and the packet is valid (its type is not 0),
+   then the header, the initial length field and the packet's contents
+   are written to OUT.  In this case, the packet is not saved in *PKT.
+
+   ONLYKEYPKTS is a simple packet filter.  If ONLYKEYPKTS is set to 1,
+   then only public subkey packets, public key packets, private subkey
+   packets and private key packets are parsed.  The rest are skipped
+   (i.e., the header and the contents are read from the pipeline and
+   discarded).  If ONLYKEYPKTS is set to 2, then in addition to the
+   above 4 types of packets, user id packets are also accepted.
+
+   DO_SKIP is a more coarse grained filter.  Unless ONLYKEYPKTS is set
+   to 2 and the packet is a user id packet, all packets are skipped.
+
+   Finally, if a packet is invalid (it's type is 0), it is skipped.
+
+   If a packet is skipped and SKIP is not NULL, then *SKIP is set to
+   1.
+
+   Note: ONLYKEYPKTS and DO_SKIP are only respected if OUT is NULL,
+   i.e., the packets are not simply being copied.
+
+   If RETPOS is not NULL, then the position of INP (as returned by
+   iobuf_tell) is saved there before any data is read from INP.
+  */
 static int
 parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
        int *skip, IOBUF out, int do_skip
@@ -457,6 +497,8 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
   else
     pos = 0; /* (silence compiler warning) */
 
+  /* The first byte of a packet is the so-called tag.  The highest bit
+     must be set.  */
   if ((ctb = iobuf_get (inp)) == -1)
     {
       rc = -1;
@@ -464,17 +506,31 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
     }
   hdrlen = 0;
   hdr[hdrlen++] = ctb;
+
   if (!(ctb & 0x80))
     {
       log_error ("%s: invalid packet (ctb=%02x)\n", iobuf_where (inp), ctb);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
+
+  /* Immediately following the header is the length.  There are two
+     formats: the old format and the new format.  If bit 6 (where the
+     least significant bit is bit 0) is set in the tag, then we are
+     dealing with a new format packet.  Otherwise, it is an old format
+     packet.  */
   pktlen = 0;
   new_ctb = !!(ctb & 0x40);
   if (new_ctb)
     {
+      /* Get the packet's type.  This is encoded in the 6 least
+        significant bits of the tag.  */
       pkttype = ctb & 0x3f;
+
+      /* Extract the packet's length.  New format packets have 4 ways
+        to encode the packet length.  The value of the first byte
+        determines the encoding and partially determines the length.
+        See section 4.2.2 of RFC 4880 for details.  */
       if ((c = iobuf_get (inp)) == -1)
        {
          log_error ("%s: 1st length byte missing\n", iobuf_where (inp));
@@ -501,16 +557,21 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
         }
       else if (c == 255)
         {
-          pktlen = (unsigned long)(hdr[hdrlen++] = iobuf_get_noeof (inp)) << 24;
-          pktlen |= (hdr[hdrlen++] = iobuf_get_noeof (inp)) << 16;
-          pktlen |= (hdr[hdrlen++] = iobuf_get_noeof (inp)) << 8;
-          if ((c = iobuf_get (inp)) == -1)
+         int i;
+         char value[4];
+
+         for (i = 0; i < 4; i ++)
             {
-              log_error ("%s: 4 byte length invalid\n", iobuf_where (inp));
-              rc = gpg_error (GPG_ERR_INV_PACKET);
-              goto leave;
+              if ((c = iobuf_get (inp)) == -1)
+                {
+                  log_error ("%s: 4 byte length invalid\n", iobuf_where (inp));
+                  rc = gpg_error (GPG_ERR_INV_PACKET);
+                  goto leave;
+                }
+              value[i] = hdr[hdrlen++] = c;
             }
-          pktlen |= (hdr[hdrlen++] = c);
+
+         pktlen = buf32_to_ulong (value);
         }
       else /* Partial body length.  */
         {
@@ -526,7 +587,7 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
               break;
 
             default:
-              log_error ("%s: partial length for invalid"
+              log_error ("%s: partial length invalid for"
                          " packet type %d\n", iobuf_where (inp), pkttype);
               rc = gpg_error (GPG_ERR_INV_PACKET);
               goto leave;
@@ -535,8 +596,13 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
 
     }
   else
+    /* This is an old format packet.  */
     {
+      /* Extract the packet's type.  This is encoded in bits 2-5.  */
       pkttype = (ctb >> 2) & 0xf;
+
+      /* The type of length encoding is encoded in bits 0-1 of the
+        tag.  */
       lenbytes = ((ctb & 3) == 3) ? 0 : (1 << (ctb & 3));
       if (!lenbytes)
        {
@@ -558,12 +624,26 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
          for (; lenbytes; lenbytes--)
            {
              pktlen <<= 8;
-             pktlen |= hdr[hdrlen++] = iobuf_get_noeof (inp);
+             c = iobuf_get (inp);
+             if (c == -1)
+               {
+                 log_error ("%s: length invalid\n", iobuf_where (inp));
+                 rc = gpg_error (GPG_ERR_INV_PACKET);
+                 goto leave;
+               }
+             pktlen |= hdr[hdrlen++] = c;
            }
        }
     }
 
-  if (pktlen == (unsigned long) (-1))
+  /* Sometimes the decompressing layer enters an error state in which
+     it simply outputs 0xff for every byte read.  If we have a stream
+     of 0xff bytes, then it will be detected as a new format packet
+     with type 63 and a 4-byte encoded length that is 4G-1.  Since
+     packets with type 63 are private and we use them as a control
+     packet, which won't be 4 GB, we reject such packets as
+     invalid.  */
+  if (pkttype == 63 && pktlen == 0xFFFFFFFF)
     {
       /* With some probability this is caused by a problem in the
        * the uncompressing layer - in some error cases it just loops
@@ -574,6 +654,17 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
 
   if (out && pkttype)
     {
+      /* This type of copying won't work if the packet uses a partial
+        body length.  (In other words, this only works if HDR is
+        actually the length.)  Currently, no callers require this
+        functionality so we just log this as an error.  */
+      if (partial)
+       {
+         log_error ("parse: Can't copy partial packet.  Aborting.\n");
+         rc = gpg_error (GPG_ERR_INV_PACKET);
+         goto leave;
+       }
+
       rc = iobuf_write (out, hdr, hdrlen);
       if (!rc)
        rc = copy_packet (inp, out, pkttype, pktlen, partial);
@@ -581,9 +672,13 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
     }
 
   if (with_uid && pkttype == PKT_USER_ID)
+    /* If ONLYKEYPKTS is set to 2, then we never skip user id packets,
+       even if DO_SKIP is set.  */
     ;
   else if (do_skip
+          /* type==0 is not allowed.  This is an invalid packet.  */
           || !pkttype
+          /* When ONLYKEYPKTS is set, we don't skip keys.  */
           || (onlykeypkts && pkttype != PKT_PUBLIC_SUBKEY
               && pkttype != PKT_PUBLIC_KEY
               && pkttype != PKT_SECRET_SUBKEY && pkttype != PKT_SECRET_KEY))
@@ -673,12 +768,13 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
       rc = parse_marker (inp, pkttype, pktlen);
       break;
     default:
+      /* Unknown packet.  Skip it.  */
       skip_packet (inp, pkttype, pktlen, partial);
       break;
     }
 
  leave:
-  /* FIXME: Do we leak in case of an error?  */
+  /* FIXME: We leak in case of an error (see the xmalloc's above).  */
   if (!rc && iobuf_error (inp))
     rc = GPG_ERR_INV_KEYRING;
 
@@ -707,6 +803,20 @@ dump_hex_line (int c, int *i)
 }
 
 
+/* Copy the contents of a packet from the pipeline IN to the pipeline
+   OUT.
+
+   The header and length have already been read from INP and the
+   decoded values are given as PKGTYPE and PKTLEN.
+
+   If the packet is a partial body length packet (RFC 4880, Section
+   4.2.2.4), then iobuf_set_partial_block_mode should already have
+   been called on INP and PARTIAL should be set.
+
+   If PARTIAL is set or PKTLEN is 0 and PKTTYPE is PKT_COMPRESSED,
+   copy until the first EOF is encountered on INP.
+
+   Returns 0 on success and an error code if an error occurs.  */
 static int
 copy_packet (IOBUF inp, IOBUF out, int pkttype,
             unsigned long pktlen, int partial)
@@ -717,7 +827,7 @@ copy_packet (IOBUF inp, IOBUF out, int pkttype,
 
   if (partial)
     {
-      while ((n = iobuf_read (inp, buf, 100)) != -1)
+      while ((n = iobuf_read (inp, buf, sizeof (buf))) != -1)
        if ((rc = iobuf_write (out, buf, n)))
          return rc;            /* write error */
     }
@@ -725,7 +835,7 @@ copy_packet (IOBUF inp, IOBUF out, int pkttype,
     {
       log_debug ("copy_packet: compressed!\n");
       /* compressed packet, copy till EOF */
-      while ((n = iobuf_read (inp, buf, 100)) != -1)
+      while ((n = iobuf_read (inp, buf, sizeof (buf))) != -1)
        if ((rc = iobuf_write (out, buf, n)))
          return rc;            /* write error */
     }
@@ -733,7 +843,7 @@ copy_packet (IOBUF inp, IOBUF out, int pkttype,
     {
       for (; pktlen; pktlen -= n)
        {
-         n = pktlen > 100 ? 100 : pktlen;
+         n = pktlen > sizeof (buf) ? sizeof (buf) : pktlen;
          n = iobuf_read (inp, buf, n);
          if (n == -1)
            return gpg_error (GPG_ERR_EOF);
@@ -745,6 +855,9 @@ copy_packet (IOBUF inp, IOBUF out, int pkttype,
 }
 
 
+/* Skip an unknown packet.  PKTTYPE is the packet's type, PKTLEN is
+   the length of the packet's content and PARTIAL is whether partial
+   body length encoding in used (in this case PKTLEN is ignored).  */
 static void
 skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial)
 {
@@ -779,8 +892,9 @@ skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial)
 
 
 /* Read PKTLEN bytes form INP and return them in a newly allocated
-   buffer.  In case of an error NULL is returned and a error messages
-   printed.  */
+   buffer.  In case of an error (including reading fewer than PKTLEN
+   bytes from INP before EOF is returned), NULL is returned and an
+   error message is logged.  */
 static void *
 read_rest (IOBUF inp, size_t pktlen)
 {
@@ -1111,6 +1225,12 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
 }
 
 
+/* Dump a subpacket to LISTFP.  BUFFER contains the subpacket in
+   question and points to the type field in the subpacket header (not
+   the start of the header).  TYPE is the subpacket's type with the
+   critical bit cleared.  CRITICAL is the value of the CRITICAL bit.
+   BUFLEN is the length of the buffer and LENGTH is the length of the
+   subpacket according to the subpacket's header.  */
 static void
 dump_sig_subpkt (int hashed, int type, int critical,
                 const byte * buffer, size_t buflen, size_t length)
@@ -1547,7 +1667,11 @@ enum_sig_subpkt (const subpktarea_t * pktbuf, sigsubpkttype_t reqtype,
       buflen -= n;
     }
   if (reqtype == SIGSUBPKT_TEST_CRITICAL)
-    return buffer;  /* Used as True to indicate that there is no. */
+    /* Returning NULL means we found a subpacket with the critical bit
+       set that we dn't grok.  We've iterated over all the subpackets
+       and haven't found such a packet so we need to return a non-NULL
+       value.  */
+    return buffer;
 
   /* Critical bit we don't understand. */
   if (start)
@@ -1572,14 +1696,13 @@ parse_sig_subpkt (const subpktarea_t * buffer, sigsubpkttype_t reqtype,
 
 
 const byte *
-parse_sig_subpkt2 (PKT_signature * sig, sigsubpkttype_t reqtype,
-                  size_t * ret_n)
+parse_sig_subpkt2 (PKT_signature * sig, sigsubpkttype_t reqtype)
 {
   const byte *p;
 
-  p = parse_sig_subpkt (sig->hashed, reqtype, ret_n);
+  p = parse_sig_subpkt (sig->hashed, reqtype, NULL);
   if (!p)
-    p = parse_sig_subpkt (sig->unhashed, reqtype, ret_n);
+    p = parse_sig_subpkt (sig->unhashed, reqtype, NULL);
   return p;
 }
 
@@ -1588,25 +1711,31 @@ parse_sig_subpkt2 (PKT_signature * sig, sigsubpkttype_t reqtype,
 void
 parse_revkeys (PKT_signature * sig)
 {
-  struct revocation_key *revkey;
+  const byte *revkey;
   int seq = 0;
   size_t len;
 
   if (sig->sig_class != 0x1F)
     return;
 
-  while ((revkey =
-         (struct revocation_key *) enum_sig_subpkt (sig->hashed,
-                                                    SIGSUBPKT_REV_KEY,
-                                                    &len, &seq, NULL)))
+  while ((revkey = enum_sig_subpkt (sig->hashed, SIGSUBPKT_REV_KEY,
+                                   &len, &seq, NULL)))
     {
-      if (len == sizeof (struct revocation_key)
-          && (revkey->class & 0x80))  /* 0x80 bit must be set.  */
+      if (/* The only valid length is 22 bytes.  See RFC 4880
+            5.2.3.15.  */
+         len == 22
+         /* 0x80 bit must be set on the class.  */
+          && (revkey[0] & 0x80))
        {
          sig->revkey = xrealloc (sig->revkey,
-                                 sizeof (struct revocation_key *) *
+                                 sizeof (struct revocation_key) *
                                  (sig->numrevkeys + 1));
-         sig->revkey[sig->numrevkeys] = revkey;
+
+         /* Copy the individual fields.  */
+         sig->revkey[sig->numrevkeys].class = revkey[0];
+         sig->revkey[sig->numrevkeys].algid = revkey[1];
+         memcpy (sig->revkey[sig->numrevkeys].fpr, &revkey[2], 20);
+
          sig->numrevkeys++;
        }
     }
@@ -1646,13 +1775,19 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
 
   if (!is_v4)
     {
+      if (pktlen == 0)
+       goto underflow;
       md5_len = iobuf_get_noeof (inp);
       pktlen--;
     }
+  if (pktlen == 0)
+    goto underflow;
   sig->sig_class = iobuf_get_noeof (inp);
   pktlen--;
   if (!is_v4)
     {
+      if (pktlen < 12)
+       goto underflow;
       sig->timestamp = read_32 (inp);
       pktlen -= 4;
       sig->keyid[0] = read_32 (inp);
@@ -1660,6 +1795,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
       sig->keyid[1] = read_32 (inp);
       pktlen -= 4;
     }
+  if (pktlen < 2)
+    goto underflow;
   sig->pubkey_algo = iobuf_get_noeof (inp);
   pktlen--;
   sig->digest_algo = iobuf_get_noeof (inp);
@@ -1668,8 +1805,12 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
   sig->flags.revocable = 1;
   if (is_v4) /* Read subpackets.  */
     {
+      if (pktlen < 2)
+       goto underflow;
       n = read_16 (inp);
       pktlen -= 2;  /* Length of hashed data. */
+      if (pktlen < n)
+       goto underflow;
       if (n > 10000)
        {
          log_error ("signature packet: hashed data too long\n");
@@ -1694,8 +1835,12 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
            }
          pktlen -= n;
        }
+      if (pktlen < 2)
+       goto underflow;
       n = read_16 (inp);
       pktlen -= 2;  /* Length of unhashed data.  */
+      if (pktlen < n)
+       goto underflow;
       if (n > 10000)
        {
          log_error ("signature packet: unhashed data too long\n");
@@ -1722,15 +1867,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
        }
     }
 
-  if (pktlen < 5)  /* Sanity check.  */
-    {
-      log_error ("packet(%d) too short\n", pkttype);
-      if (list_mode)
-        es_fputs (":signature packet: [too short]\n", listfp);
-      rc = GPG_ERR_INV_PACKET;
-      goto leave;
-    }
-
+  if (pktlen < 2)
+    goto underflow;
   sig->digest_start[0] = iobuf_get_noeof (inp);
   pktlen--;
   sig->digest_start[1] = iobuf_get_noeof (inp);
@@ -1754,7 +1892,7 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
               && opt.verbose)
        log_info ("signature packet without timestamp\n");
 
-      p = parse_sig_subpkt2 (sig, SIGSUBPKT_ISSUER, NULL);
+      p = parse_sig_subpkt2 (sig, SIGSUBPKT_ISSUER);
       if (p)
        {
          sig->keyid[0] = buf32_to_u32 (p);
@@ -1808,7 +1946,7 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
          unhashed area.  In theory, anyway, we should never see this
          packet off of a local keyring. */
 
-      p = parse_sig_subpkt2 (sig, SIGSUBPKT_EXPORTABLE, NULL);
+      p = parse_sig_subpkt2 (sig, SIGSUBPKT_EXPORTABLE);
       if (p && *p == 0)
        sig->flags.exportable = 0;
 
@@ -1877,6 +2015,15 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
  leave:
   iobuf_skip_rest (inp, pktlen, 0);
   return rc;
+
+ underflow:
+  log_error ("packet(%d) too short\n", pkttype);
+  if (list_mode)
+    es_fputs (":signature packet: [too short]\n", listfp);
+
+  iobuf_skip_rest (inp, pktlen, 0);
+
+  return GPG_ERR_INV_PACKET;
 }
 
 
index 08984ef..5eb2562 100644 (file)
@@ -533,6 +533,14 @@ passphrase_to_dek_ext (u32 *keyid, int pubkey_algo,
          s2k_cacheid = s2k_cacheidbuf;
        }
 
+      if (opt.pinentry_mode == PINENTRY_MODE_LOOPBACK)
+        {
+          char buf[32];
+
+          snprintf (buf, sizeof (buf), "%u", 100);
+          write_status_text (STATUS_INQUIRE_MAXLEN, buf);
+        }
+
       /* Divert to the gpg-agent. */
       pw = passphrase_get (keyid, mode == 2, s2k_cacheid,
                            (mode == 2 || mode == 4)? opt.passphrase_repeat : 0,
index 6e82187..eb3a989 100644 (file)
@@ -383,11 +383,11 @@ gen_desig_revoke( const char *uname, strlist_t locusr )
                    for(j=0;j<signode->pkt->pkt.signature->numrevkeys;j++)
                      {
                        if(pk->revkey[i].class==
-                          signode->pkt->pkt.signature->revkey[j]->class &&
+                          signode->pkt->pkt.signature->revkey[j].class &&
                           pk->revkey[i].algid==
-                          signode->pkt->pkt.signature->revkey[j]->algid &&
+                          signode->pkt->pkt.signature->revkey[j].algid &&
                           memcmp(pk->revkey[i].fpr,
-                                 signode->pkt->pkt.signature->revkey[j]->fpr,
+                                 signode->pkt->pkt.signature->revkey[j].fpr,
                                  MAX_FINGERPRINT_LEN)==0)
                          {
                            revkey=signode->pkt->pkt.signature;
index afc117e..7a8d697 100644 (file)
@@ -294,8 +294,13 @@ do_sign (PKT_public_key *pksk, PKT_signature *sig,
 
   /* Check that the signature verification worked and nothing is
    * fooling us e.g. by a bug in the signature create code or by
-   * deliberately introduced faults.  */
-  if (!err && !opt.no_sig_create_check)
+   * deliberately introduced faults.  Because Libgcrypt 1.7 does this
+   * for RSA internally there is no need to do it here again.  */
+  if (!err
+#if GCRYPT_VERSION_NUMBER >= 0x010700 /* Libgcrypt >= 1.7 */
+        && !is_RSA (pksk->pubkey_algo)
+#endif /* Libgcrypt >= 1.7 */
+      )
     {
       PKT_public_key *pk = xmalloc_clear (sizeof *pk);
 
diff --git a/g10/t-keydb-keyring.kbx b/g10/t-keydb-keyring.kbx
new file mode 100644 (file)
index 0000000..a1d3af0
Binary files /dev/null and b/g10/t-keydb-keyring.kbx differ
diff --git a/g10/t-keydb.c b/g10/t-keydb.c
new file mode 100644 (file)
index 0000000..17a7611
--- /dev/null
@@ -0,0 +1,93 @@
+/* t-keydb.c - Tests for keydb.c.
+ * Copyright (C) 2015 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "test.c"
+
+#include "keydb.h"
+
+static void
+do_test (int argc, char *argv[])
+{
+  int rc;
+  KEYDB_HANDLE hd1, hd2;
+  KEYDB_SEARCH_DESC desc1, desc2;
+  KBNODE kb1, kb2;
+  char *uid1;
+  char *uid2;
+  char *fname;
+
+  (void) argc;
+  (void) argv;
+
+  fname = prepend_srcdir ("t-keydb-keyring.kbx");
+  rc = keydb_add_resource (fname, 0);
+  test_free (fname);
+  if (rc)
+    ABORT ("Failed to open keyring.");
+
+  hd1 = keydb_new ();
+  hd2 = keydb_new ();
+
+  rc = classify_user_id ("2689 5E25 E844 6D44 A26D  8FAF 2F79 98F3 DBFC 6AD9",
+                        &desc1, 0);
+  if (rc)
+    ABORT ("Failed to convert fingerprint for DBFC6AD9");
+
+  rc = keydb_search (hd1, &desc1, 1, NULL);
+  if (rc)
+    ABORT ("Failed to lookup key associated with DBFC6AD9");
+
+
+  classify_user_id ("8061 5870 F5BA D690 3336  86D0 F2AD 85AC 1E42 B367",
+                   &desc2, 0);
+  if (rc)
+    ABORT ("Failed to convert fingerprint for 1E42B367");
+
+  rc = keydb_search (hd2, &desc2, 1, NULL);
+  if (rc)
+    ABORT ("Failed to lookup key associated with 1E42B367");
+
+  rc = keydb_get_keyblock (hd2, &kb2);
+  if (rc)
+    ABORT ("Failed to get keyblock for 1E42B367");
+
+  rc = keydb_get_keyblock (hd1, &kb1);
+  if (rc)
+    ABORT ("Failed to get keyblock for DBFC6AD9");
+
+  while (kb1 && kb1->pkt->pkttype != PKT_USER_ID)
+    kb1 = kb1->next;
+  if (! kb1)
+    ABORT ("DBFC6AD9 has no user id packet");
+  uid1 = kb1->pkt->pkt.user_id->name;
+
+  while (kb2 && kb2->pkt->pkttype != PKT_USER_ID)
+    kb2 = kb2->next;
+  if (! kb2)
+    ABORT ("1E42B367 has no user id packet");
+  uid2 = kb2->pkt->pkt.user_id->name;
+
+  if (verbose)
+    {
+      printf ("user id for DBFC6AD9: %s\n", uid1);
+      printf ("user id for 1E42B367: %s\n", uid2);
+    }
+
+  TEST_P ("cache consistency", strcmp (uid1, uid2) != 0);
+}
diff --git a/g10/test-stubs.c b/g10/test-stubs.c
new file mode 100644 (file)
index 0000000..c6f6d68
--- /dev/null
@@ -0,0 +1,413 @@
+/* test-stubs.c - The GnuPG signature verify utility
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005, 2006,
+ *               2008, 2009, 2012 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#define INCLUDED_BY_MAIN_MODULE 1
+#include "gpg.h"
+#include "util.h"
+#include "packet.h"
+#include "iobuf.h"
+#include "main.h"
+#include "options.h"
+#include "keydb.h"
+#include "trustdb.h"
+#include "filter.h"
+#include "ttyio.h"
+#include "i18n.h"
+#include "sysutils.h"
+#include "status.h"
+#include "call-agent.h"
+
+int g10_errors_seen;
+
+
+void
+g10_exit( int rc )
+{
+  rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0;
+  exit(rc );
+}
+
+
+/* Stub:
+ * We have to override the trustcheck from pkclist.c becuase
+ * this utility assumes that all keys in the keyring are trustworthy
+ */
+int
+check_signatures_trust( PKT_signature *sig )
+{
+  (void)sig;
+  return 0;
+}
+
+void
+read_trust_options(byte *trust_model, ulong *created, ulong *nextcheck,
+                  byte *marginals, byte *completes, byte *cert_depth,
+                  byte *min_cert_level)
+{
+  (void)trust_model;
+  (void)created;
+  (void)nextcheck;
+  (void)marginals;
+  (void)completes;
+  (void)cert_depth;
+  (void)min_cert_level;
+}
+
+/* Stub:
+ * We don't have the trustdb , so we have to provide some stub functions
+ * instead
+ */
+
+int
+cache_disabled_value(PKT_public_key *pk)
+{
+  (void)pk;
+  return 0;
+}
+
+void
+check_trustdb_stale(void)
+{
+}
+
+int
+get_validity_info (PKT_public_key *pk, PKT_user_id *uid)
+{
+  (void)pk;
+  (void)uid;
+  return '?';
+}
+
+unsigned int
+get_validity (PKT_public_key *pk, PKT_user_id *uid)
+{
+  (void)pk;
+  (void)uid;
+  return 0;
+}
+
+const char *
+trust_value_to_string (unsigned int value)
+{
+  (void)value;
+  return "err";
+}
+
+const char *
+uid_trust_string_fixed (PKT_public_key *key, PKT_user_id *uid)
+{
+  (void)key;
+  (void)uid;
+  return "err";
+}
+
+int
+get_ownertrust_info (PKT_public_key *pk)
+{
+  (void)pk;
+  return '?';
+}
+
+unsigned int
+get_ownertrust (PKT_public_key *pk)
+{
+  (void)pk;
+  return TRUST_UNKNOWN;
+}
+
+
+/* Stubs:
+ * Because we only work with trusted keys, it does not make sense to
+ * get them from a keyserver
+ */
+
+struct keyserver_spec *
+keyserver_match (struct keyserver_spec *spec)
+{
+  (void)spec;
+  return NULL;
+}
+
+int
+keyserver_import_keyid (u32 *keyid, void *dummy)
+{
+  (void)keyid;
+  (void)dummy;
+  return -1;
+}
+
+int
+keyserver_import_cert (const char *name)
+{
+  (void)name;
+  return -1;
+}
+
+int
+keyserver_import_pka (const char *name,unsigned char *fpr)
+{
+  (void)name;
+  (void)fpr;
+  return -1;
+}
+
+int
+keyserver_import_name (const char *name,struct keyserver_spec *spec)
+{
+  (void)name;
+  (void)spec;
+  return -1;
+}
+
+int
+keyserver_import_ldap (const char *name)
+{
+  (void)name;
+  return -1;
+}
+
+/* Stub:
+ * No encryption here but mainproc links to these functions.
+ */
+gpg_error_t
+get_session_key (PKT_pubkey_enc *k, DEK *dek)
+{
+  (void)k;
+  (void)dek;
+  return GPG_ERR_GENERAL;
+}
+
+/* Stub: */
+gpg_error_t
+get_override_session_key (DEK *dek, const char *string)
+{
+  (void)dek;
+  (void)string;
+  return GPG_ERR_GENERAL;
+}
+
+/* Stub: */
+int
+decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
+{
+  (void)ctrl;
+  (void)procctx;
+  (void)ed;
+  (void)dek;
+  return GPG_ERR_GENERAL;
+}
+
+
+/* Stub:
+ * No interactive commands, so we don't need the helptexts
+ */
+void
+display_online_help (const char *keyword)
+{
+  (void)keyword;
+}
+
+/* Stub:
+ * We don't use secret keys, but getkey.c links to this
+ */
+int
+check_secret_key (PKT_public_key *pk, int n)
+{
+  (void)pk;
+  (void)n;
+  return GPG_ERR_GENERAL;
+}
+
+/* Stub:
+ * No secret key, so no passphrase needed
+ */
+DEK *
+passphrase_to_dek (u32 *keyid, int pubkey_algo,
+                   int cipher_algo, STRING2KEY *s2k, int mode,
+                   const char *tmp, int *canceled)
+{
+  (void)keyid;
+  (void)pubkey_algo;
+  (void)cipher_algo;
+  (void)s2k;
+  (void)mode;
+  (void)tmp;
+
+  if (canceled)
+    *canceled = 0;
+  return NULL;
+}
+
+void
+passphrase_clear_cache (u32 *keyid, const char *cacheid, int algo)
+{
+  (void)keyid;
+  (void)cacheid;
+  (void)algo;
+}
+
+struct keyserver_spec *
+parse_preferred_keyserver(PKT_signature *sig)
+{
+  (void)sig;
+  return NULL;
+}
+
+struct keyserver_spec *
+parse_keyserver_uri (const char *uri, int require_scheme,
+                     const char *configname, unsigned int configlineno)
+{
+  (void)uri;
+  (void)require_scheme;
+  (void)configname;
+  (void)configlineno;
+  return NULL;
+}
+
+void
+free_keyserver_spec (struct keyserver_spec *keyserver)
+{
+  (void)keyserver;
+}
+
+/* Stubs to avoid linking to photoid.c */
+void
+show_photos (const struct user_attribute *attrs, int count, PKT_public_key *pk)
+{
+  (void)attrs;
+  (void)count;
+  (void)pk;
+}
+
+int
+parse_image_header (const struct user_attribute *attr, byte *type, u32 *len)
+{
+  (void)attr;
+  (void)type;
+  (void)len;
+  return 0;
+}
+
+char *
+image_type_to_string (byte type, int string)
+{
+  (void)type;
+  (void)string;
+  return NULL;
+}
+
+#ifdef ENABLE_CARD_SUPPORT
+int
+agent_scd_getattr (const char *name, struct agent_card_info_s *info)
+{
+  (void)name;
+  (void)info;
+  return 0;
+}
+#endif /* ENABLE_CARD_SUPPORT */
+
+/* We do not do any locking, so use these stubs here */
+void
+dotlock_disable (void)
+{
+}
+
+dotlock_t
+dotlock_create (const char *file_to_lock, unsigned int flags)
+{
+  (void)file_to_lock;
+  (void)flags;
+  return NULL;
+}
+
+void
+dotlock_destroy (dotlock_t h)
+{
+  (void)h;
+}
+
+int
+dotlock_take (dotlock_t h, long timeout)
+{
+  (void)h;
+  (void)timeout;
+  return 0;
+}
+
+int
+dotlock_release (dotlock_t h)
+{
+  (void)h;
+  return 0;
+}
+
+void
+dotlock_remove_lockfiles (void)
+{
+}
+
+gpg_error_t
+agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk)
+{
+  (void)ctrl;
+  (void)pk;
+  return gpg_error (GPG_ERR_NO_SECKEY);
+}
+
+gpg_error_t
+agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock)
+{
+  (void)ctrl;
+  (void)keyblock;
+  return gpg_error (GPG_ERR_NO_SECKEY);
+}
+
+gpg_error_t
+agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno)
+{
+  (void)ctrl;
+  (void)hexkeygrip;
+  *r_serialno = NULL;
+  return gpg_error (GPG_ERR_NO_SECKEY);
+}
+
+gpg_error_t
+gpg_dirmngr_get_pka (ctrl_t ctrl, const char *userid,
+                     unsigned char **r_fpr, size_t *r_fprlen,
+                     char **r_url)
+{
+  (void)ctrl;
+  (void)userid;
+  if (r_fpr)
+    *r_fpr = NULL;
+  if (r_fprlen)
+    *r_fprlen = 0;
+  if (r_url)
+    *r_url = NULL;
+  return gpg_error (GPG_ERR_NOT_FOUND);
+}
diff --git a/g10/test.c b/g10/test.c
new file mode 100644 (file)
index 0000000..59a015c
--- /dev/null
@@ -0,0 +1,186 @@
+/* test.c - Infrastructure for unit tests.
+ * Copyright (C) 2015 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gpg.h"
+
+/* A unit test consists of one or more tests.  Tests can be broken
+   into groups and each group can consist of one or more tests.  */
+
+/* The number of test groups.  */
+static int test_groups;
+/* The current test group.  */
+static char *test_group;
+
+/* Whether there was already a failure in the current test group.  */
+static int current_test_group_failed;
+/* The number of test groups with a failure.  */
+static int test_groups_failed;
+
+/* The total number of tests.  */
+static int tests;
+/* The total number of tests that failed.  */
+static int tests_failed;
+
+/* Flag to request verbose diagnostics.  This is set if the envvar
+   "verbose" exists and is not the empty string.  */
+static int verbose;
+
+#define TEST_GROUP(description)             \
+  do {                              \
+    test_group = (description);             \
+    test_groups ++;                 \
+    current_test_group_failed = 0;   \
+  } while (0)
+
+#define STRINGIFY2(x) #x
+#define STRINGIFY(x) STRINGIFY2(x)
+
+/* Execute a test.  */
+#define TEST(description, test, expected)      \
+  do {                                         \
+    int test_result;                           \
+    int expected_result;                       \
+                                               \
+    tests ++;                                  \
+                                               \
+    printf ("%d. Checking %s...",              \
+           tests, (description) ?: "");        \
+    fflush (stdout);                           \
+                                               \
+    test_result = (test);                      \
+    expected_result = (expected);              \
+                                               \
+    if (test_result == expected_result)                \
+      {                                                \
+        printf (" ok.\n");                     \
+      }                                                \
+    else                                       \
+      {                                                \
+       printf (" failed.\n");                  \
+       printf ("  %s == %s failed.\n",         \
+               STRINGIFY(test),                \
+               STRINGIFY(expected));           \
+       tests_failed ++;                        \
+       if (! current_test_group_failed)        \
+         {                                     \
+           current_test_group_failed = 1;      \
+           test_groups_failed ++;              \
+         }                                     \
+      }                                                \
+  } while (0)
+
+/* Test that a condition evaluates to true.  */
+#define TEST_P(description, test)              \
+  TEST(description, !!(test), 1)
+
+/* Like CHECK, but if the test fails, abort the program.  */
+#define ASSERT(description, test, expected)            \
+  do {                                                 \
+    int tests_failed_pre = tests_failed;               \
+    CHECK(description, test, expected);                        \
+    if (tests_failed_pre != tests_failed)              \
+      exit_tests (1);                                  \
+  } while (0)
+
+/* Call this if something went wrong.  */
+#define ABORT(message)                         \
+  do {                                         \
+    printf ("aborting...");                    \
+    if (message)                               \
+      printf (" %s\n", (message));             \
+                                               \
+    exit_tests (1);                            \
+  } while (0)
+
+/* You need to fill this function in.  */
+static void do_test (int argc, char *argv[]);
+
+
+/* Print stats and call the real exit.  If FORCE is set use
+   EXIT_FAILURE even if no test has failed.  */
+static void
+exit_tests (int force)
+{
+  if (tests_failed == 0)
+    {
+      printf ("All %d tests passed.\n", tests);
+      exit (!!force);
+    }
+  else
+    {
+      printf ("%d of %d tests failed",
+             tests_failed, tests);
+      if (test_groups > 1)
+       printf (" (%d of %d groups)",
+               test_groups_failed, test_groups);
+      printf ("\n");
+      exit (1);
+    }
+}
+
+
+/* Prepend FNAME with the srcdir environment variable's value and
+   return a malloced filename.  Caller must release the returned
+   string using test_free.  */
+char *
+prepend_srcdir (const char *fname)
+{
+  static const char *srcdir;
+  char *result;
+
+  if (!srcdir && !(srcdir = getenv ("srcdir")))
+    srcdir = ".";
+
+  result = malloc (strlen (srcdir) + 1 + strlen (fname) + 1);
+  strcpy (result, srcdir);
+  strcat (result, "/");
+  strcat (result, fname);
+  return result;
+}
+
+
+void
+test_free (void *a)
+{
+  if (a)
+    free (a);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  const char *s;
+
+  (void) test_group;
+
+  s = getenv ("verbose");
+  if (s && *s)
+    verbose = 1;
+
+  do_test (argc, argv);
+  exit_tests (0);
+
+  return !!tests_failed;
+}
index 1826e98..ba4ba5f 100644 (file)
@@ -99,20 +99,22 @@ release_key_items (struct key_item *k)
     }
 }
 
+#define KEY_HASH_TABLE_SIZE 1024
+
 /*
- * For fast keylook up we need a hash table.  Each byte of a KeyIDs
+ * For fast keylook up we need a hash table.  Each byte of a KeyID
  * should be distributed equally over the 256 possible values (except
  * for v3 keyIDs but we consider them as not important here). So we
- * can just use 10 bits to index a table of 1024 key items.
- * Possible optimization: Don not use key_items but other hash_table when the
- * duplicates lists gets too large.
+ * can just use 10 bits to index a table of KEY_HASH_TABLE_SIZE key items.
+ * Possible optimization: Do not use key_items but other hash_table when the
+ * duplicates lists get too large.
  */
 static KeyHashTable
 new_key_hash_table (void)
 {
   struct key_item **tbl;
 
-  tbl = xmalloc_clear (1024 * sizeof *tbl);
+  tbl = xmalloc_clear (KEY_HASH_TABLE_SIZE * sizeof *tbl);
   return tbl;
 }
 
@@ -123,7 +125,7 @@ release_key_hash_table (KeyHashTable tbl)
 
   if (!tbl)
     return;
-  for (i=0; i < 1024; i++)
+  for (i=0; i < KEY_HASH_TABLE_SIZE; i++)
     release_key_items (tbl[i]);
   xfree (tbl);
 }
@@ -136,7 +138,7 @@ test_key_hash_table (KeyHashTable tbl, u32 *kid)
 {
   struct key_item *k;
 
-  for (k = tbl[(kid[1] & 0x03ff)]; k; k = k->next)
+  for (k = tbl[(kid[1] % KEY_HASH_TABLE_SIZE)]; k; k = k->next)
     if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
       return 1;
   return 0;
@@ -148,17 +150,18 @@ test_key_hash_table (KeyHashTable tbl, u32 *kid)
 static void
 add_key_hash_table (KeyHashTable tbl, u32 *kid)
 {
+  int i = kid[1] % KEY_HASH_TABLE_SIZE;
   struct key_item *k, *kk;
 
-  for (k = tbl[(kid[1] & 0x03ff)]; k; k = k->next)
+  for (k = tbl[i]; k; k = k->next)
     if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
       return; /* already in table */
 
   kk = new_key_item ();
   kk->kid[0] = kid[0];
   kk->kid[1] = kid[1];
-  kk->next = tbl[(kid[1] & 0x03ff)];
-  tbl[(kid[1] & 0x03ff)] = kk;
+  kk->next = tbl[i];
+  tbl[i] = kk;
 }
 
 /*
index 99d239f..5c3e74e 100644 (file)
 /* A EncFS based backend.  This requires a whole directory which
    includes the encrypted files.  Metadata is not encrypted.  */
 
+#define CONTTYPE_DM_CRYPT   2
+/* A DM-Crypt based backend.  */
+
 
 #define CONTTYPE_TRUECRYPT  21571
 /* A Truecrypt (www.truecrypt.org) based container.  Due to the design
    of truecrypt this requires a second datafile because it is not
-   possible to to prepend a truecrypt container with our keyblob.  */
-
+   possible to prepend a truecrypt container with our keyblob.  */
 
 
 
index 5df2bba..95138e0 100644 (file)
@@ -26,7 +26,7 @@ include $(top_srcdir)/am/cmacros.am
 
 AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS)
 
-noinst_LIBRARIES = libkeybox.a
+noinst_LIBRARIES = libkeybox.a libkeybox509.a
 bin_PROGRAMS = kbxutil
 
 if HAVE_W32CE_SYSTEM
@@ -48,11 +48,17 @@ common_sources = \
 
 
 libkeybox_a_SOURCES = $(common_sources)
+libkeybox509_a_SOURCES = $(common_sources)
+
+libkeybox_a_CFLAGS = $(AM_CFLAGS)
+libkeybox509_a_CFLAGS = $(AM_CFLAGS) -DKEYBOX_WITH_X509=1
+
 
 # We need W32SOCKLIBS because the init subsystem code in libcommon
 # requires it - although we don't actually need it.  It is easier
 # to do it this way.
 kbxutil_SOURCES = kbxutil.c $(common_sources)
+kbxutil_CFLAGS = $(AM_CFLAGS) -DKEYBOX_WITH_X509=1
 kbxutil_LDADD   = ../common/libcommon.a \
                   $(KSBA_LIBS) $(LIBGCRYPT_LIBS) $(extra_libs) \
                   $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV) $(W32SOCKLIBS)
index 2cac242..a5f602b 100644 (file)
@@ -139,7 +139,14 @@ next_packet (unsigned char const **bufptr, size_t *buflen,
       return gpg_error (GPG_ERR_UNEXPECTED);
     }
 
-  if (pktlen == (unsigned long)(-1))
+  if (pkttype == 63 && pktlen == 0xFFFFFFFF)
+    /* Sometimes the decompressing layer enters an error state in
+       which it simply outputs 0xff for every byte read.  If we have a
+       stream of 0xff bytes, then it will be detected as a new format
+       packet with type 63 and a 4-byte encoded length that is 4G-1.
+       Since packets with type 63 are private and we use them as a
+       control packet, which won't be 4 GB, we reject such packets as
+       invalid.  */
     return gpg_error (GPG_ERR_INV_PACKET);
 
   if (pktlen > len)
index 1433591..05b6859 100644 (file)
@@ -611,6 +611,9 @@ has_keygrip (KEYBOXBLOB blob, const unsigned char *grip)
 #ifdef KEYBOX_WITH_X509
   if (blob_get_type (blob) == KEYBOX_BLOBTYPE_X509)
     return blob_x509_has_grip (blob, grip);
+#else
+  (void)blob;
+  (void)grip;
 #endif
   return 0;
 }
index 386fff1..8c31141 100644 (file)
@@ -29,9 +29,6 @@ extern "C" {
 #include "../common/iobuf.h"
 #include "keybox-search-desc.h"
 
-#define KEYBOX_WITH_X509 1
-
-
 #ifdef KEYBOX_WITH_X509
 # include <ksba.h>
 #endif
index 6050b1b..09c6ec4 100644 (file)
@@ -72,6 +72,7 @@ g10/tdbdump.c
 g10/tdbio.c
 g10/textfilter.c
 g10/trustdb.c
+g10/trust.c
 g10/verify.c
 
 kbx/kbxutil.c
index 1d0417a..65c392f 100644 (file)
--- a/po/ca.po
+++ b/po/ca.po
@@ -5753,6 +5753,57 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "registre de confiança %lu, tipus %d: no s'ha pogut escriure: %s\n"
 
+msgid "undefined"
+msgstr ""
+
+#, fuzzy
+msgid "never"
+msgstr "mai       "
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+msgid "[ revoked]"
+msgstr "[revocada]"
+
+#, fuzzy
+msgid "[ expired]"
+msgstr "[caducada]"
+
+#, fuzzy
+msgid "[ unknown]"
+msgstr "desconeguda"
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8837,22 +8888,6 @@ msgstr ""
 #~ "només podeu signar en clar amb claus d'estil PGP 2.x en el mode --pgp2\n"
 
 #, fuzzy
-#~ msgid "[ revoked]"
-#~ msgstr "[revocada]"
-
-#, fuzzy
-#~ msgid "[ expired]"
-#~ msgstr "[caducada]"
-
-#, fuzzy
-#~ msgid "[ unknown]"
-#~ msgstr "desconeguda"
-
-#, fuzzy
-#~ msgid "never"
-#~ msgstr "mai       "
-
-#, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Forma d'ús: gpg [opcions] [fitxers] (-h per a veure l'ajuda)"
 
index ff197ba..7c878e9 100644 (file)
--- a/po/cs.po
+++ b/po/cs.po
@@ -5337,6 +5337,57 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "nelze aktualizovat záznam v databázi důvěry: chyba při zápisu: %s\n"
 
+msgid "undefined"
+msgstr "nedefinována"
+
+msgid "never"
+msgstr "žádná"
+
+msgid "marginal"
+msgstr "částečná"
+
+msgid "full"
+msgstr "plná"
+
+msgid "ultimate"
+msgstr "absolutní"
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+#, fuzzy
+#| msgid "10 translator see trustdb.c:uid_trust_string_fixed"
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+"14 fixní délka na kterou se překládá see trustdb.c:uid_trust_string_fixed"
+
+msgid "[ revoked]"
+msgstr "[ revokován  ]"
+
+# TODO: use context to distinguish gender
+msgid "[ expired]"
+msgstr "[ prošlý(á)  ]"
+
+msgid "[ unknown]"
+msgstr "[  neznámá   ]"
+
+msgid "[  undef ]"
+msgstr "[nedefinovaná]"
+
+msgid "[marginal]"
+msgstr "[  částečná  ]"
+
+msgid "[  full  ]"
+msgstr "[    plná    ]"
+
+msgid "[ultimate]"
+msgstr "[  absolutní ]"
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8641,47 +8692,6 @@ msgstr ""
 #~ "v módu --pgp2 můžete vytvářet jen čitelné podpisy s klíči formátu PGP-2."
 #~ "x\n"
 
-#~ msgid "10 translator see trustdb.c:uid_trust_string_fixed"
-#~ msgstr ""
-#~ "14 fixní délka na kterou se překládá see trustdb.c:uid_trust_string_fixed"
-
-#~ msgid "[ revoked]"
-#~ msgstr "[ revokován  ]"
-
-# TODO: use context to distinguish gender
-#~ msgid "[ expired]"
-#~ msgstr "[ prošlý(á)  ]"
-
-#~ msgid "[ unknown]"
-#~ msgstr "[  neznámá   ]"
-
-#~ msgid "[  undef ]"
-#~ msgstr "[nedefinovaná]"
-
-#~ msgid "[marginal]"
-#~ msgstr "[  částečná  ]"
-
-#~ msgid "[  full  ]"
-#~ msgstr "[    plná    ]"
-
-#~ msgid "[ultimate]"
-#~ msgstr "[  absolutní ]"
-
-#~ msgid "undefined"
-#~ msgstr "nedefinována"
-
-#~ msgid "never"
-#~ msgstr "žádná"
-
-#~ msgid "marginal"
-#~ msgstr "částečná"
-
-#~ msgid "full"
-#~ msgstr "plná"
-
-#~ msgid "ultimate"
-#~ msgstr "absolutní"
-
 #~ msgid " - probably dead - removing lock"
 #~ msgstr " – asi mrtvý – odstraňuji zámek"
 
index 01700f0..742c0ed 100644 (file)
--- a/po/da.po
+++ b/po/da.po
@@ -5587,6 +5587,55 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "kan ikke opdatere trustdb-versionspost: skrivning mislykkedes: %s\n"
 
+msgid "undefined"
+msgstr "ej defineret"
+
+msgid "never"
+msgstr "aldrig"
+
+msgid "marginal"
+msgstr "marginal"
+
+msgid "full"
+msgstr "fuld"
+
+msgid "ultimate"
+msgstr "ultimativ"
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+#, fuzzy
+#| msgid "10 translator see trustdb.c:uid_trust_string_fixed"
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr "10 oversætter se trustdb.c:uid_trust_string_fixed"
+
+msgid "[ revoked]"
+msgstr "[   tilb.]"
+
+msgid "[ expired]"
+msgstr "[ udløbet]"
+
+msgid "[ unknown]"
+msgstr "[  ukendt]"
+
+msgid "[  undef ]"
+msgstr "[  ej def]"
+
+msgid "[marginal]"
+msgstr "[marginal]"
+
+msgid "[  full  ]"
+msgstr "[  fuld  ]"
+
+msgid "[ultimate]"
+msgstr "[ ultim. ]"
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8813,45 +8862,6 @@ msgstr ""
 #~ "du kan kun clearsign med nøgler i PGP 2.x-stil mens du er i tilstanden --"
 #~ "pgp2\n"
 
-#~ msgid "10 translator see trustdb.c:uid_trust_string_fixed"
-#~ msgstr "10 oversætter se trustdb.c:uid_trust_string_fixed"
-
-#~ msgid "[ revoked]"
-#~ msgstr "[   tilb.]"
-
-#~ msgid "[ expired]"
-#~ msgstr "[ udløbet]"
-
-#~ msgid "[ unknown]"
-#~ msgstr "[  ukendt]"
-
-#~ msgid "[  undef ]"
-#~ msgstr "[  ej def]"
-
-#~ msgid "[marginal]"
-#~ msgstr "[marginal]"
-
-#~ msgid "[  full  ]"
-#~ msgstr "[  fuld  ]"
-
-#~ msgid "[ultimate]"
-#~ msgstr "[ ultim. ]"
-
-#~ msgid "undefined"
-#~ msgstr "ej defineret"
-
-#~ msgid "never"
-#~ msgstr "aldrig"
-
-#~ msgid "marginal"
-#~ msgstr "marginal"
-
-#~ msgid "full"
-#~ msgstr "fuld"
-
-#~ msgid "ultimate"
-#~ msgstr "ultimativ"
-
 #~ msgid " - probably dead - removing lock"
 #~ msgstr " - sikkert død - fjerner lås"
 
index 368fce9..db82908 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -9,7 +9,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gnupg-2.1.0\n"
 "Report-Msgid-Bugs-To: translations@gnupg.org\n"
-"PO-Revision-Date: 2015-08-11 13:52+0200\n"
+"PO-Revision-Date: 2015-08-20 16:27+0200\n"
 "Last-Translator: Werner Koch <wk@gnupg.org>\n"
 "Language-Team: German <de@li.org>\n"
 "Language: de\n"
@@ -4704,7 +4704,7 @@ msgid "Your decision? "
 msgstr "Ihre Auswahl? "
 
 msgid "Do you really want to set this key to ultimate trust? (y/N) "
-msgstr "Wollen Sie diesem Schlüssel wirklich uneingeschränkt vertrauen? (j/N) "
+msgstr "Wollen Sie diesem Schlüssel wirklich ultimativ vertrauen? (j/N) "
 
 msgid "Certificates leading to an ultimately trusted key:\n"
 msgstr "Zertifikate führen zu einem letztlich vertrauenswürdigen Schlüssel:\n"
@@ -5331,7 +5331,7 @@ msgstr ""
 
 #, c-format
 msgid "key %s marked as ultimately trusted\n"
-msgstr "Schlüssel %s ist als uneingeschränkt vertrauenswürdig gekennzeichnet\n"
+msgstr "Schlüssel %s ist als ultimativ vertrauenswürdig gekennzeichnet\n"
 
 #, c-format
 msgid "trust record %lu, req type %d: read failed: %s\n"
@@ -5390,12 +5390,11 @@ msgid "%d keys processed (%d validity counts cleared)\n"
 msgstr "%d Schlüssel verarbeitet (%d Validity Zähler gelöscht)\n"
 
 msgid "no ultimately trusted keys found\n"
-msgstr "keine uneingeschränkt vertrauenswürdigen Schlüssel gefunden\n"
+msgstr "keine ultimativ vertrauenswürdigen Schlüssel gefunden\n"
 
 #, c-format
 msgid "public key of ultimately trusted key %s not found\n"
-msgstr ""
-"öff. Schlüssel des uneingeschränkt vertrautem Schlüssel %s nicht gefunden\n"
+msgstr "öff. Schlüssel des ultimativ vertrauten Schlüssel %s nicht gefunden\n"
 
 #, c-format
 msgid "%d marginal(s) needed, %d complete(s) needed, %s trust model\n"
@@ -5414,6 +5413,53 @@ msgstr ""
 "\"Trust-DB\"-Versions-Satz kann nicht geändert werden: Schreiben "
 "fehlgeschlagen: %s\n"
 
+msgid "undefined"
+msgstr "unbestimmt"
+
+msgid "never"
+msgstr "niemals"
+
+msgid "marginal"
+msgstr "marginal"
+
+msgid "full"
+msgstr "vollständig"
+
+msgid "ultimate"
+msgstr "ultimativ"
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr "13"
+
+msgid "[ revoked]"
+msgstr "[ widerrufen]"
+
+msgid "[ expired]"
+msgstr "[  verfallen]"
+
+msgid "[ unknown]"
+msgstr "[  unbekannt]"
+
+msgid "[  undef ]"
+msgstr "[undefiniert]"
+
+msgid "[marginal]"
+msgstr "[   marginal]"
+
+msgid "[  full  ]"
+msgstr "[vollständig]"
+
+msgid "[ultimate]"
+msgstr "[  ultimativ]"
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8222,45 +8268,6 @@ msgstr ""
 #~ msgid "deleting secret key not implemented\n"
 #~ msgstr "Löschen des geheimen Schlüssel ist nicht implementiert\n"
 
-#~ msgid "10 translator see trustdb.c:uid_trust_string_fixed"
-#~ msgstr "10"
-
-#~ msgid "[ revoked]"
-#~ msgstr "[widerrufen]"
-
-#~ msgid "[ expired]"
-#~ msgstr "[verfall.]"
-
-#~ msgid "[ unknown]"
-#~ msgstr "[  unbek.]"
-
-#~ msgid "[  undef ]"
-#~ msgstr "[  undef.]"
-
-#~ msgid "[marginal]"
-#~ msgstr "[marginal]"
-
-#~ msgid "[  full  ]"
-#~ msgstr "[ vollst.]"
-
-#~ msgid "[ultimate]"
-#~ msgstr "[ uneing.]"
-
-#~ msgid "undefined"
-#~ msgstr "unbestimmt"
-
-#~ msgid "never"
-#~ msgstr "niemals"
-
-#~ msgid "marginal"
-#~ msgstr "marginal"
-
-#~ msgid "full"
-#~ msgstr "vollständig"
-
-#~ msgid "ultimate"
-#~ msgstr "uneingeschränkt"
-
 #~ msgid "usage: gpgconf [options] "
 #~ msgstr "Aufruf: gpgconf [Optionen] "
 
@@ -8645,10 +8652,9 @@ msgstr ""
 #~ "ultimately trusted\n"
 #~ msgstr ""
 #~ "Um das Web-of-Trust aufzubauen muß GnuPG wissen, welchen Schlüsseln\n"
-#~ "uneingeschränkt vertraut wird. Das sind üblicherweise die Schlüssel\n"
+#~ "ultimativ vertraut wird. Das sind üblicherweise die Schlüssel\n"
 #~ "auf deren geheimen Schlüssel Sie Zugruff haben.\n"
-#~ "Antworten Sie mit \"yes\" um diesen Schlüssel uneingeschränkt zu "
-#~ "vertrauen\n"
+#~ "Antworten Sie mit \"yes\" um diesen Schlüssel ultimativ zu vertrauen\n"
 
 #~ msgid "If you want to use this untrusted key anyway, answer \"yes\"."
 #~ msgstr ""
index 9cbcf5f..fe4605c 100644 (file)
--- a/po/el.po
+++ b/po/el.po
@@ -5617,6 +5617,57 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "åããñáöÞ trust %lu, ôýðïò %d: write áðÝôõ÷å: %s\n"
 
+msgid "undefined"
+msgstr ""
+
+#, fuzzy
+msgid "never"
+msgstr "ðïôÝ     "
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+msgid "[ revoked]"
+msgstr "[áíáêëçìÝíï]"
+
+#, fuzzy
+msgid "[ expired]"
+msgstr "[ëçãìÝíï]"
+
+#, fuzzy
+msgid "[ unknown]"
+msgstr "Üãíùóôï"
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8654,22 +8705,6 @@ msgstr ""
 #~ "pgp2\n"
 
 #, fuzzy
-#~ msgid "[ revoked]"
-#~ msgstr "[áíáêëçìÝíï]"
-
-#, fuzzy
-#~ msgid "[ expired]"
-#~ msgstr "[ëçãìÝíï]"
-
-#, fuzzy
-#~ msgid "[ unknown]"
-#~ msgstr "Üãíùóôï"
-
-#, fuzzy
-#~ msgid "never"
-#~ msgstr "ðïôÝ     "
-
-#, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "×ñÞóç: gpg [åðéëïãÝò] [áñ÷åßá] (-h ãéá âïÞèåéá)"
 
index 062b2f0..5a90921 100644 (file)
--- a/po/eo.po
+++ b/po/eo.po
@@ -5569,6 +5569,56 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "fido-datenaro %lu, speco %d: skribo malsukcesis: %s\n"
 
+msgid "undefined"
+msgstr ""
+
+msgid "never"
+msgstr ""
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+msgid "[ revoked]"
+msgstr "rev"
+
+#, fuzzy
+msgid "[ expired]"
+msgstr "eksval"
+
+#, fuzzy
+msgid "[ unknown]"
+msgstr "nekonata versio"
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8572,18 +8622,6 @@ msgstr ""
 #~ "eblas klartekste subskribi nur per PGP-2.x-stilaj þlosiloj kun --pgp2\n"
 
 #, fuzzy
-#~ msgid "[ revoked]"
-#~ msgstr "rev"
-
-#, fuzzy
-#~ msgid "[ expired]"
-#~ msgstr "eksval"
-
-#, fuzzy
-#~ msgid "[ unknown]"
-#~ msgstr "nekonata versio"
-
-#, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Uzado: gpg [opcioj] [dosieroj] (-h por helpo)"
 
index 40ebb72..062394b 100644 (file)
--- a/po/es.po
+++ b/po/es.po
@@ -5590,6 +5590,55 @@ msgstr ""
 "no se puede actualizar el registro de la versión de la base de datos\n"
 "de confianza: fallo de escritura: %s\n"
 
+msgid "undefined"
+msgstr "no definido"
+
+msgid "never"
+msgstr "nunca"
+
+msgid "marginal"
+msgstr "dudosa"
+
+msgid "full"
+msgstr "total"
+
+msgid "ultimate"
+msgstr "absoluta"
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+#, fuzzy
+#| msgid "10 translator see trustdb.c:uid_trust_string_fixed"
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr "13 no apto para supersticiosos"
+
+msgid "[ revoked]"
+msgstr "[  revocada ]"
+
+msgid "[ expired]"
+msgstr "[  caducada ]"
+
+msgid "[ unknown]"
+msgstr "[desconocida]"
+
+msgid "[  undef ]"
+msgstr "[no definida]"
+
+msgid "[marginal]"
+msgstr "[   dudosa  ]"
+
+msgid "[  full  ]"
+msgstr "[   total   ]"
+
+msgid "[ultimate]"
+msgstr "[  absoluta ]"
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8873,45 +8922,6 @@ msgstr ""
 #~ msgstr ""
 #~ "sólo puede firmar en claro con claves PGP 2.x estando en modo --pgp2\n"
 
-#~ msgid "10 translator see trustdb.c:uid_trust_string_fixed"
-#~ msgstr "13 no apto para supersticiosos"
-
-#~ msgid "[ revoked]"
-#~ msgstr "[  revocada ]"
-
-#~ msgid "[ expired]"
-#~ msgstr "[  caducada ]"
-
-#~ msgid "[ unknown]"
-#~ msgstr "[desconocida]"
-
-#~ msgid "[  undef ]"
-#~ msgstr "[no definida]"
-
-#~ msgid "[marginal]"
-#~ msgstr "[   dudosa  ]"
-
-#~ msgid "[  full  ]"
-#~ msgstr "[   total   ]"
-
-#~ msgid "[ultimate]"
-#~ msgstr "[  absoluta ]"
-
-#~ msgid "undefined"
-#~ msgstr "no definido"
-
-#~ msgid "never"
-#~ msgstr "nunca"
-
-#~ msgid "marginal"
-#~ msgstr "dudosa"
-
-#~ msgid "full"
-#~ msgstr "total"
-
-#~ msgid "ultimate"
-#~ msgstr "absoluta"
-
 #~ msgid " - probably dead - removing lock"
 #~ msgstr " - probablemente muerto - suprimiendo el bloqueo"
 
index 5ec88c9..e78e98a 100644 (file)
--- a/po/et.po
+++ b/po/et.po
@@ -5546,6 +5546,57 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "usalduse kirje %lu, tüüp %d: kirjutamine ebaõnnestus: %s\n"
 
+msgid "undefined"
+msgstr ""
+
+#, fuzzy
+msgid "never"
+msgstr "mitte kunagi"
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+msgid "[ revoked]"
+msgstr "[tühistatud] "
+
+#, fuzzy
+msgid "[ expired]"
+msgstr "[aegunud] "
+
+#, fuzzy
+msgid "[ unknown]"
+msgstr "tundmatu"
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8564,22 +8615,6 @@ msgstr ""
 #~ "moodis\n"
 
 #, fuzzy
-#~ msgid "[ revoked]"
-#~ msgstr "[tühistatud] "
-
-#, fuzzy
-#~ msgid "[ expired]"
-#~ msgstr "[aegunud] "
-
-#, fuzzy
-#~ msgid "[ unknown]"
-#~ msgstr "tundmatu"
-
-#, fuzzy
-#~ msgid "never"
-#~ msgstr "mitte kunagi"
-
-#, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Kasuta: gpg [võtmed] [failid] (-h näitab abiinfot)"
 
index 14f4f16..a921e0b 100644 (file)
--- a/po/fi.po
+++ b/po/fi.po
@@ -5601,6 +5601,57 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "luottamustietue %lu, tyyppi %d: kirjoittaminen epäonnistui: %s\n"
 
+msgid "undefined"
+msgstr ""
+
+#, fuzzy
+msgid "never"
+msgstr "ei koskaan"
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+msgid "[ revoked]"
+msgstr "[mitätöity] "
+
+#, fuzzy
+msgid "[ expired]"
+msgstr "[vanhentunut] "
+
+#, fuzzy
+msgid "[ unknown]"
+msgstr "tuntematon "
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8632,22 +8683,6 @@ msgstr ""
 #~ "vain --pgp2-tilassa\n"
 
 #, fuzzy
-#~ msgid "[ revoked]"
-#~ msgstr "[mitätöity] "
-
-#, fuzzy
-#~ msgid "[ expired]"
-#~ msgstr "[vanhentunut] "
-
-#, fuzzy
-#~ msgid "[ unknown]"
-#~ msgstr "tuntematon "
-
-#, fuzzy
-#~ msgid "never"
-#~ msgstr "ei koskaan"
-
-#, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Käyttö: gpg [valitsimet] [tiedostot] (-h näyttää ohjeen)"
 
index aed3bb7..67aa7db 100644 (file)
--- a/po/fr.po
+++ b/po/fr.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gnupg 2.1\n"
 "Report-Msgid-Bugs-To: translations@gnupg.org\n"
-"PO-Revision-Date: 2014-11-01 19:04-0400\n"
+"PO-Revision-Date: 2015-09-10 16:22+0200\n"
 "Last-Translator: David Prévot <david@tilapin.org>\n"
 "Language-Team: French <traduc@traduc.org>\n"
 "Language: fr\n"
@@ -5464,6 +5464,53 @@ msgstr ""
 "impossible de mettre à jour l'enregistrement de version de la\n"
 "base de confiance : échec d'écriture : %s\n"
 
+msgid "undefined"
+msgstr "indéfinie"
+
+msgid "never"
+msgstr "jamais"
+
+msgid "marginal"
+msgstr "marginale"
+
+msgid "full"
+msgstr "totale"
+
+msgid "ultimate"
+msgstr "ultime"
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr "11 le traducteur a bien lu ce qu'il fallait :)"
+
+msgid "[ revoked]"
+msgstr "[ révoquée]"
+
+msgid "[ expired]"
+msgstr "[ expirée ]"
+
+msgid "[ unknown]"
+msgstr "[ inconnue]"
+
+msgid "[  undef ]"
+msgstr "[indéfinie]"
+
+msgid "[marginal]"
+msgstr "[marginale]"
+
+msgid "[  full  ]"
+msgstr "[  totale ]"
+
+msgid "[ultimate]"
+msgstr "[  ultime ]"
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8371,45 +8418,6 @@ msgstr ""
 #~ msgid "deleting secret key not implemented\n"
 #~ msgstr "la suppression de clef secrète n'est pas implémentée\n"
 
-#~ msgid "10 translator see trustdb.c:uid_trust_string_fixed"
-#~ msgstr "11 le traducteur a bien lu ce qu'il fallait :)"
-
-#~ msgid "[ revoked]"
-#~ msgstr "[ révoquée]"
-
-#~ msgid "[ expired]"
-#~ msgstr "[ expirée ]"
-
-#~ msgid "[ unknown]"
-#~ msgstr "[ inconnue]"
-
-#~ msgid "[  undef ]"
-#~ msgstr "[indéfinie]"
-
-#~ msgid "[marginal]"
-#~ msgstr "[marginale]"
-
-#~ msgid "[  full  ]"
-#~ msgstr "[  totale ]"
-
-#~ msgid "[ultimate]"
-#~ msgstr "[  ultime ]"
-
-#~ msgid "undefined"
-#~ msgstr "indéfinie"
-
-#~ msgid "never"
-#~ msgstr "jamais"
-
-#~ msgid "marginal"
-#~ msgstr "marginale"
-
-#~ msgid "full"
-#~ msgstr "totale"
-
-#~ msgid "ultimate"
-#~ msgstr "ultime"
-
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Utilisation : scdaemon [options] (-h pour l'aide)"
 
index ee50059..028fb92 100644 (file)
--- a/po/gl.po
+++ b/po/gl.po
@@ -5606,6 +5606,57 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "rexistro de confianza %lu, tipo %d: fallou a escritura: %s\n"
 
+msgid "undefined"
+msgstr ""
+
+#, fuzzy
+msgid "never"
+msgstr "nunca     "
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+msgid "[ revoked]"
+msgstr "[revocada] "
+
+#, fuzzy
+msgid "[ expired]"
+msgstr "[caducada ]"
+
+#, fuzzy
+msgid "[ unknown]"
+msgstr "descoñecido"
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8656,22 +8707,6 @@ msgstr ""
 #~ msgstr "só pode asinar en claro con chaves estilo PGP 2.x no modo --pgp2\n"
 
 #, fuzzy
-#~ msgid "[ revoked]"
-#~ msgstr "[revocada] "
-
-#, fuzzy
-#~ msgid "[ expired]"
-#~ msgstr "[caducada ]"
-
-#, fuzzy
-#~ msgid "[ unknown]"
-#~ msgstr "descoñecido"
-
-#, fuzzy
-#~ msgid "never"
-#~ msgstr "nunca     "
-
-#, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Uso: gpg [opcións] [ficheiros] (-h para ve-la axuda)"
 
index df78cee..5bd41aa 100644 (file)
--- a/po/hu.po
+++ b/po/hu.po
@@ -5577,6 +5577,57 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "%lu bizalmi rekord, %d típus: írás sikertelen: %s.\n"
 
+msgid "undefined"
+msgstr ""
+
+#, fuzzy
+msgid "never"
+msgstr "soha      "
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+msgid "[ revoked]"
+msgstr "[visszavont] "
+
+#, fuzzy
+msgid "[ expired]"
+msgstr "[lejárt]     "
+
+#, fuzzy
+msgid "[ unknown]"
+msgstr "Ismeretlen módú"
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8606,22 +8657,6 @@ msgstr ""
 #~ "alá!\n"
 
 #, fuzzy
-#~ msgid "[ revoked]"
-#~ msgstr "[visszavont] "
-
-#, fuzzy
-#~ msgid "[ expired]"
-#~ msgstr "[lejárt]     "
-
-#, fuzzy
-#~ msgid "[ unknown]"
-#~ msgstr "Ismeretlen módú"
-
-#, fuzzy
-#~ msgid "never"
-#~ msgstr "soha      "
-
-#, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Használat: gpg [opciók] [fájlok] (-h a súgóhoz)"
 
index 8cffa15..2338293 100644 (file)
--- a/po/id.po
+++ b/po/id.po
@@ -5566,6 +5566,57 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "trust record %lu, tipe %d: gagal menulis: %s\n"
 
+msgid "undefined"
+msgstr ""
+
+#, fuzzy
+msgid "never"
+msgstr "tidak pernah..."
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+msgid "[ revoked]"
+msgstr "[revoked] "
+
+#, fuzzy
+msgid "[ expired]"
+msgstr "[expired] "
+
+#, fuzzy
+msgid "[ unknown]"
+msgstr "tidak dikenal"
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8598,22 +8649,6 @@ msgstr ""
 #~ "pgp2\n"
 
 #, fuzzy
-#~ msgid "[ revoked]"
-#~ msgstr "[revoked] "
-
-#, fuzzy
-#~ msgid "[ expired]"
-#~ msgstr "[expired] "
-
-#, fuzzy
-#~ msgid "[ unknown]"
-#~ msgstr "tidak dikenal"
-
-#, fuzzy
-#~ msgid "never"
-#~ msgstr "tidak pernah..."
-
-#, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Pemakaian: gpg [pilihan] [file] (-h untuk bantuan)"
 
index 0486c6c..83ffc90 100644 (file)
--- a/po/it.po
+++ b/po/it.po
@@ -5605,6 +5605,57 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "trust record %lu, req type %d: write fallita: %s\n"
 
+msgid "undefined"
+msgstr ""
+
+#, fuzzy
+msgid "never"
+msgstr "mai       "
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+msgid "[ revoked]"
+msgstr "[revocata]"
+
+#, fuzzy
+msgid "[ expired]"
+msgstr "[scaduta]"
+
+#, fuzzy
+msgid "[ unknown]"
+msgstr "sconosciuto"
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8642,22 +8693,6 @@ msgstr ""
 #~ "x\n"
 
 #, fuzzy
-#~ msgid "[ revoked]"
-#~ msgstr "[revocata]"
-
-#, fuzzy
-#~ msgid "[ expired]"
-#~ msgstr "[scaduta]"
-
-#, fuzzy
-#~ msgid "[ unknown]"
-#~ msgstr "sconosciuto"
-
-#, fuzzy
-#~ msgid "never"
-#~ msgstr "mai       "
-
-#, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Uso: gpg [opzioni] [files] (-h per l'aiuto)"
 
index 51a9d74..e04c92c 100644 (file)
--- a/po/ja.po
+++ b/po/ja.po
@@ -5205,6 +5205,61 @@ msgstr ""
 "信用データベースのバージョン・レコードが更新できません: 書き込みに失敗しまし"
 "た: %s\n"
 
+msgid "undefined"
+msgstr ""
+
+#, fuzzy
+#| msgid "never     "
+msgid "never"
+msgstr "無期限    "
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+#| msgid "revoked"
+msgid "[ revoked]"
+msgstr "失効"
+
+#, fuzzy
+#| msgid "expired"
+msgid "[ expired]"
+msgstr "期限切れ"
+
+#, fuzzy
+#| msgid "unknown"
+msgid "[ unknown]"
+msgstr "不明の"
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
index c50c8d3..712d04e 100644 (file)
--- a/po/nb.po
+++ b/po/nb.po
@@ -5431,6 +5431,53 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr ""
 
+msgid "undefined"
+msgstr ""
+
+msgid "never"
+msgstr ""
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+msgid "[ revoked]"
+msgstr "[ opphevet]"
+
+msgid "[ expired]"
+msgstr "[ utgått]"
+
+msgid "[ unknown]"
+msgstr "[ ukjent]"
+
+msgid "[  undef ]"
+msgstr "[ udef ]"
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8338,18 +8385,6 @@ msgstr ""
 #~ msgid "Invalid passphrase; please try again"
 #~ msgstr "Ugyldig passfrase; vennligst prøv igjen"
 
-#~ msgid "[ revoked]"
-#~ msgstr "[ opphevet]"
-
-#~ msgid "[ expired]"
-#~ msgstr "[ utgått]"
-
-#~ msgid "[ unknown]"
-#~ msgstr "[ ukjent]"
-
-#~ msgid "[  undef ]"
-#~ msgstr "[ udef ]"
-
 #, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Bruksmåte: gpg [valg] [filer] (-h for hjelp)"
index 4f5d4a8..5ed9386 100644 (file)
--- a/po/pl.po
+++ b/po/pl.po
@@ -5611,6 +5611,55 @@ msgstr ""
 "nie mo¿na uaktualniæ rekordu wersji bazy zaufania: zapis nie powiód³ siê: "
 "%s\n"
 
+msgid "undefined"
+msgstr "nieokre¶lone"
+
+msgid "never"
+msgstr "nigdy"
+
+msgid "marginal"
+msgstr "marginalne"
+
+msgid "full"
+msgstr "pe³ne"
+
+msgid "ultimate"
+msgstr "absolutne"
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+#, fuzzy
+#| msgid "10 translator see trustdb.c:uid_trust_string_fixed"
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr "17"
+
+msgid "[ revoked]"
+msgstr "[  uniewa¿niony ]"
+
+msgid "[ expired]"
+msgstr "[przeterminowany]"
+
+msgid "[ unknown]"
+msgstr "[    nieznane   ]"
+
+msgid "[  undef ]"
+msgstr "[  nieokre¶lone ]"
+
+msgid "[marginal]"
+msgstr "[   marginalne  ]"
+
+msgid "[  full  ]"
+msgstr "[      pe³ne    ]"
+
+msgid "[ultimate]"
+msgstr "[    absolutne   ]"
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8841,45 +8890,6 @@ msgstr ""
 #~ msgstr ""
 #~ "w trybie --pgp2 mo¿na podpisywaæ tylko za pomoc± kluczy z wersji 2.x\n"
 
-#~ msgid "10 translator see trustdb.c:uid_trust_string_fixed"
-#~ msgstr "17"
-
-#~ msgid "[ revoked]"
-#~ msgstr "[  uniewa¿niony ]"
-
-#~ msgid "[ expired]"
-#~ msgstr "[przeterminowany]"
-
-#~ msgid "[ unknown]"
-#~ msgstr "[    nieznane   ]"
-
-#~ msgid "[  undef ]"
-#~ msgstr "[  nieokre¶lone ]"
-
-#~ msgid "[marginal]"
-#~ msgstr "[   marginalne  ]"
-
-#~ msgid "[  full  ]"
-#~ msgstr "[      pe³ne    ]"
-
-#~ msgid "[ultimate]"
-#~ msgstr "[    absolutne   ]"
-
-#~ msgid "undefined"
-#~ msgstr "nieokre¶lone"
-
-#~ msgid "never"
-#~ msgstr "nigdy"
-
-#~ msgid "marginal"
-#~ msgstr "marginalne"
-
-#~ msgid "full"
-#~ msgstr "pe³ne"
-
-#~ msgid "ultimate"
-#~ msgstr "absolutne"
-
 #~ msgid " - probably dead - removing lock"
 #~ msgstr " - prawdopodobnie martwy - usuwanie blokady"
 
index b9c0753..eefaea9 100644 (file)
--- a/po/pt.po
+++ b/po/pt.po
@@ -5581,6 +5581,56 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "registo de confiança %lu, tipo %d: escrita falhou: %s\n"
 
+msgid "undefined"
+msgstr ""
+
+msgid "never"
+msgstr ""
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+msgid "[ revoked]"
+msgstr "revkey"
+
+#, fuzzy
+msgid "[ expired]"
+msgstr "expire"
+
+#, fuzzy
+msgid "[ unknown]"
+msgstr "versão desconhecida"
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8596,18 +8646,6 @@ msgstr ""
 #~ msgstr "só pode assinar à vista com chaves do tipo PGP 2.x no modo --pgp2\n"
 
 #, fuzzy
-#~ msgid "[ revoked]"
-#~ msgstr "revkey"
-
-#, fuzzy
-#~ msgid "[ expired]"
-#~ msgstr "expire"
-
-#, fuzzy
-#~ msgid "[ unknown]"
-#~ msgstr "versão desconhecida"
-
-#, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Uso: gpg [opções] [ficheiros] (-h para ajuda)"
 
index 3cd7f33..554f463 100644 (file)
--- a/po/ro.po
+++ b/po/ro.po
@@ -5574,6 +5574,55 @@ msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr ""
 "nu pot actualiza înregistrare versiunii trustdb: scrierea a eºuat: %s\n"
 
+msgid "undefined"
+msgstr "nedefinitã"
+
+msgid "never"
+msgstr "niciodatã"
+
+msgid "marginal"
+msgstr "marginal"
+
+msgid "full"
+msgstr "deplinã"
+
+msgid "ultimate"
+msgstr "supremã"
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+#, fuzzy
+#| msgid "10 translator see trustdb.c:uid_trust_string_fixed"
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr "10 traducãtor vezi trustdb.c:uid_trust_string_fixed"
+
+msgid "[ revoked]"
+msgstr "[revocatã]"
+
+msgid "[ expired]"
+msgstr "[expiratã] "
+
+msgid "[ unknown]"
+msgstr "[necunoscutã]"
+
+msgid "[  undef ]"
+msgstr "[  nedef ]"
+
+msgid "[marginal]"
+msgstr "[marginal]"
+
+msgid "[  full  ]"
+msgstr "[ deplinã]"
+
+msgid "[ultimate]"
+msgstr "[ supremã]"
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8636,45 +8685,6 @@ msgstr ""
 #~ "you can only clearsign with PGP 2.x style keys while in --pgp2 mode\n"
 #~ msgstr "puteþi semna-în-clar cu chei stil PGP 2.x în modul --pgp2\n"
 
-#~ msgid "10 translator see trustdb.c:uid_trust_string_fixed"
-#~ msgstr "10 traducãtor vezi trustdb.c:uid_trust_string_fixed"
-
-#~ msgid "[ revoked]"
-#~ msgstr "[revocatã]"
-
-#~ msgid "[ expired]"
-#~ msgstr "[expiratã] "
-
-#~ msgid "[ unknown]"
-#~ msgstr "[necunoscutã]"
-
-#~ msgid "[  undef ]"
-#~ msgstr "[  nedef ]"
-
-#~ msgid "[marginal]"
-#~ msgstr "[marginal]"
-
-#~ msgid "[  full  ]"
-#~ msgstr "[ deplinã]"
-
-#~ msgid "[ultimate]"
-#~ msgstr "[ supremã]"
-
-#~ msgid "undefined"
-#~ msgstr "nedefinitã"
-
-#~ msgid "never"
-#~ msgstr "niciodatã"
-
-#~ msgid "marginal"
-#~ msgstr "marginal"
-
-#~ msgid "full"
-#~ msgstr "deplinã"
-
-#~ msgid "ultimate"
-#~ msgstr "supremã"
-
 #, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Folosire: gpg [opþiuni] [fiºiere] (-h pentru ajutor)"
index 661299a..f79effc 100644 (file)
--- a/po/ru.po
+++ b/po/ru.po
@@ -11,7 +11,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: GnuPG 2.1.0\n"
 "Report-Msgid-Bugs-To: translations@gnupg.org\n"
-"PO-Revision-Date: 2015-06-25 17:17+0000\n"
+"PO-Revision-Date: 2015-08-12 17:17+0000\n"
 "Last-Translator: Ineiev <ineiev@gnu.org>\n"
 "Language-Team: Russian <gnupg-ru@gnupg.org>\n"
 "Language: ru\n"
@@ -88,7 +88,7 @@ msgstr ""
 "этого сеанса"
 
 msgid "PIN:"
-msgstr ""
+msgstr "PIN:"
 
 msgid "Passphrase:"
 msgstr "Фраза-пароль:"
@@ -656,12 +656,11 @@ msgstr "Сменить фразу-пароль"
 msgid "I'll change it later"
 msgstr "Сменю позже"
 
-#, fuzzy, c-format
-#| msgid "Do you really want to delete the selected keys? (y/N) "
+#, c-format
 msgid ""
 "Do you really want to delete the key identified by keygrip%%0A  %s%%0A  %%C"
 "%%0A?"
-msgstr "Ð\92Ñ\8b Ð´ÐµÐ¹Ñ\81Ñ\82виÑ\82елÑ\8cно Ñ\85оÑ\82иÑ\82е Ñ\83далиÑ\82Ñ\8c Ð²Ñ\8bбÑ\80аннÑ\8bе ÐºÐ»Ñ\8eÑ\87и? (y/N) "
+msgstr "Ð\92Ñ\8b Ð´ÐµÐ¹Ñ\81Ñ\82виÑ\82елÑ\8cно Ñ\85оÑ\82иÑ\82е Ñ\83далиÑ\82Ñ\8c ÐºÐ»Ñ\8eÑ\87 Ñ\81 ÐºÐ¾Ð´Ð¾Ð¼%%0A  %s%%0A  %%C%%0A?"
 
 msgid "Delete key"
 msgstr "Удалить ключ"
@@ -2932,10 +2931,8 @@ msgstr "сохранить и выйти"
 msgid "show key fingerprint"
 msgstr "показать отпечаток ключа"
 
-#, fuzzy
-#| msgid "Enter the keygrip: "
 msgid "show the keygrip"
-msgstr "Ð\92ведиÑ\82е ÐºÐ¾Ð´ ÐºÐ»Ñ\8eÑ\87а:"
+msgstr "показаÑ\82Ñ\8c ÐºÐ¾Ð´ ÐºÐ»Ñ\8eÑ\87а"
 
 msgid "list key and user IDs"
 msgstr "вывести список ключей и ID пользователя"
@@ -3977,15 +3974,12 @@ msgstr "Критическое примечание к подписи: "
 msgid "Signature notation: "
 msgstr "Примечание к подписи: "
 
-#, fuzzy
-#| msgid "1 bad signature\n"
 msgid "1 good signature\n"
-msgstr "1 плохая подпись\n"
+msgstr "1 хорошая подпись\n"
 
-#, fuzzy, c-format
-#| msgid "%d bad signatures\n"
+#, c-format
 msgid "%d good signatures\n"
-msgstr "%d плохих подписей\n"
+msgstr "%d хороших подписей\n"
 
 #, c-format
 msgid "Warning: %lu key(s) skipped due to their large size\n"
@@ -5277,6 +5271,61 @@ msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr ""
 "невозможно обновить запись о версии таблицы доверия: ошибка записи: %s\n"
 
+msgid "undefined"
+msgstr ""
+
+#, fuzzy
+#| msgid "never     "
+msgid "never"
+msgstr "никогда   "
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+#| msgid "revoked"
+msgid "[ revoked]"
+msgstr "отозван"
+
+#, fuzzy
+#| msgid "expired"
+msgid "[ expired]"
+msgstr "просрочен"
+
+#, fuzzy
+#| msgid "unknown"
+msgid "[ unknown]"
+msgstr "неизвестно"
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
index 562aeb7..b38aab0 100644 (file)
--- a/po/sk.po
+++ b/po/sk.po
@@ -5591,6 +5591,57 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "záznam dôvery %lu, typ %d: zápis zlyhal: %s\n"
 
+msgid "undefined"
+msgstr ""
+
+#, fuzzy
+msgid "never"
+msgstr "nikdy     "
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+msgid "[ revoked]"
+msgstr "[revokované]"
+
+#, fuzzy
+msgid "[ expired]"
+msgstr "[expirované]"
+
+#, fuzzy
+msgid "[ unknown]"
+msgstr "neznáme"
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8618,22 +8669,6 @@ msgstr ""
 #~ "PGP-2.x\n"
 
 #, fuzzy
-#~ msgid "[ revoked]"
-#~ msgstr "[revokované]"
-
-#, fuzzy
-#~ msgid "[ expired]"
-#~ msgstr "[expirované]"
-
-#, fuzzy
-#~ msgid "[ unknown]"
-#~ msgstr "neznáme"
-
-#, fuzzy
-#~ msgid "never"
-#~ msgstr "nikdy     "
-
-#, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Pou¾itie: gpg [mo¾nosti] [súbory] (-h pre pomoc)"
 
index fb4ca7e..fd08e46 100644 (file)
--- a/po/sv.po
+++ b/po/sv.po
@@ -5691,6 +5691,64 @@ msgstr ""
 "kunde inte uppdatera versionspost i tillitsdatabasen: skrivning "
 "misslyckades: %s\n"
 
+msgid "undefined"
+msgstr "odefinierad"
+
+msgid "never"
+msgstr "aldrig"
+
+msgid "marginal"
+msgstr "marginell"
+
+msgid "full"
+msgstr "fullständig"
+
+msgid "ultimate"
+msgstr "förbehållslös"
+
+# Denna måste testas.
+# /* NOTE TO TRANSLATOR: these strings are similar to those in
+#    trust_value_to_string(), but are a fixed length.  This is needed to
+#    make attractive information listings where columns line up
+#    properly.  The value "10" should be the length of the strings you
+#    choose to translate to.  This is the length in printable columns.
+#    It gets passed to atoi() so everything after the number is
+#    essentially a comment and need not be translated.  Either key and
+#    uid are both NULL, or neither are NULL. */
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+#, fuzzy
+#| msgid "10 translator see trustdb.c:uid_trust_string_fixed"
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr "15"
+
+msgid "[ revoked]"
+msgstr "[   spärrad   ]"
+
+msgid "[ expired]"
+msgstr "[   utgånget  ]"
+
+msgid "[ unknown]"
+msgstr "[    okänt    ]"
+
+msgid "[  undef ]"
+msgstr "[ odefinierad ]"
+
+msgid "[marginal]"
+msgstr "[  marginell  ]"
+
+msgid "[  full  ]"
+msgstr "[ fullständig ]"
+
+msgid "[ultimate]"
+msgstr "[förbehållslös]"
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8954,54 +9012,6 @@ msgstr ""
 #~ "du kan bara göra klartextsignaturer med en PGP 2.x-nyckel\n"
 #~ "när du är i --pgp2-läge\n"
 
-# Denna måste testas.
-# /* NOTE TO TRANSLATOR: these strings are similar to those in
-#    trust_value_to_string(), but are a fixed length.  This is needed to
-#    make attractive information listings where columns line up
-#    properly.  The value "10" should be the length of the strings you
-#    choose to translate to.  This is the length in printable columns.
-#    It gets passed to atoi() so everything after the number is
-#    essentially a comment and need not be translated.  Either key and
-#    uid are both NULL, or neither are NULL. */
-#~ msgid "10 translator see trustdb.c:uid_trust_string_fixed"
-#~ msgstr "15"
-
-#~ msgid "[ revoked]"
-#~ msgstr "[   spärrad   ]"
-
-#~ msgid "[ expired]"
-#~ msgstr "[   utgånget  ]"
-
-#~ msgid "[ unknown]"
-#~ msgstr "[    okänt    ]"
-
-#~ msgid "[  undef ]"
-#~ msgstr "[ odefinierad ]"
-
-#~ msgid "[marginal]"
-#~ msgstr "[  marginell  ]"
-
-#~ msgid "[  full  ]"
-#~ msgstr "[ fullständig ]"
-
-#~ msgid "[ultimate]"
-#~ msgstr "[förbehållslös]"
-
-#~ msgid "undefined"
-#~ msgstr "odefinierad"
-
-#~ msgid "never"
-#~ msgstr "aldrig"
-
-#~ msgid "marginal"
-#~ msgstr "marginell"
-
-#~ msgid "full"
-#~ msgstr "fullständig"
-
-#~ msgid "ultimate"
-#~ msgstr "förbehållslös"
-
 #~ msgid " - probably dead - removing lock"
 #~ msgstr " - antagligen död - tar bort lås"
 
index 3ab774a..26b2c3a 100644 (file)
--- a/po/tr.po
+++ b/po/tr.po
@@ -5619,6 +5619,55 @@ msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr ""
 "güvence veritabanının sürüm kaydı güncellenemedi: yazma başarısız: %s\n"
 
+msgid "undefined"
+msgstr "tanımsız"
+
+msgid "never"
+msgstr "asla    "
+
+msgid "marginal"
+msgstr "şöyle böyle"
+
+msgid "full"
+msgstr "tamamen"
+
+msgid "ultimate"
+msgstr "son derece"
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+#, fuzzy
+#| msgid "10 translator see trustdb.c:uid_trust_string_fixed"
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr "20 translator seen trustdb.c:uid_trust_string_fixed"
+
+msgid "[ revoked]"
+msgstr "[yürürlükten kalktı]"
+
+msgid "[ expired]"
+msgstr "[  süresi  doldu  ]"
+
+msgid "[ unknown]"
+msgstr "[    bilinmeyen   ]"
+
+msgid "[  undef ]"
+msgstr "[     tanımsız    ]"
+
+msgid "[marginal]"
+msgstr "[   şöyle böyle   ]"
+
+msgid "[  full  ]"
+msgstr "[     tamamen     ]"
+
+msgid "[ultimate]"
+msgstr "[   son  derece   ]"
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8872,45 +8921,6 @@ msgstr ""
 #~ "--pgp2 kipinde sadece PGP 2.x tarzı anahtarlarla açık imzalama "
 #~ "yapabilirsiniz\n"
 
-#~ msgid "10 translator see trustdb.c:uid_trust_string_fixed"
-#~ msgstr "20 translator seen trustdb.c:uid_trust_string_fixed"
-
-#~ msgid "[ revoked]"
-#~ msgstr "[yürürlükten kalktı]"
-
-#~ msgid "[ expired]"
-#~ msgstr "[  süresi  doldu  ]"
-
-#~ msgid "[ unknown]"
-#~ msgstr "[    bilinmeyen   ]"
-
-#~ msgid "[  undef ]"
-#~ msgstr "[     tanımsız    ]"
-
-#~ msgid "[marginal]"
-#~ msgstr "[   şöyle böyle   ]"
-
-#~ msgid "[  full  ]"
-#~ msgstr "[     tamamen     ]"
-
-#~ msgid "[ultimate]"
-#~ msgstr "[   son  derece   ]"
-
-#~ msgid "undefined"
-#~ msgstr "tanımsız"
-
-#~ msgid "never"
-#~ msgstr "asla    "
-
-#~ msgid "marginal"
-#~ msgstr "şöyle böyle"
-
-#~ msgid "full"
-#~ msgstr "tamamen"
-
-#~ msgid "ultimate"
-#~ msgstr "son derece"
-
 #~ msgid " - probably dead - removing lock"
 #~ msgstr " - muhtemelen ölü - kilit siliniyor"
 
index b523414..02e5437 100644 (file)
--- a/po/uk.po
+++ b/po/uk.po
@@ -5383,6 +5383,55 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "не вдалося оновити запис версії trustdb: помилка запису: %s\n"
 
+msgid "undefined"
+msgstr "не визначено"
+
+msgid "never"
+msgstr "ніколи"
+
+msgid "marginal"
+msgstr "неповна"
+
+msgid "full"
+msgstr "повна"
+
+msgid "ultimate"
+msgstr "безмежна"
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+#, fuzzy
+#| msgid "10 translator see trustdb.c:uid_trust_string_fixed"
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr "10 translator see trustdb.c:uid_trust_string_fixed"
+
+msgid "[ revoked]"
+msgstr "[відклик.]"
+
+msgid "[ expired]"
+msgstr "[застаріл]"
+
+msgid "[ unknown]"
+msgstr "[невідома]"
+
+msgid "[  undef ]"
+msgstr "[не визн.]"
+
+msgid "[marginal]"
+msgstr "[неповна ]"
+
+msgid "[  full  ]"
+msgstr "[ повна  ]"
+
+msgid "[ultimate]"
+msgstr "[безмежна]"
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8221,45 +8270,6 @@ msgstr ""
 #~ msgid "the IDEA cipher plugin is not present\n"
 #~ msgstr "не виявлено додатка шифрування IDEA\n"
 
-#~ msgid "10 translator see trustdb.c:uid_trust_string_fixed"
-#~ msgstr "10 translator see trustdb.c:uid_trust_string_fixed"
-
-#~ msgid "[ revoked]"
-#~ msgstr "[відклик.]"
-
-#~ msgid "[ expired]"
-#~ msgstr "[застаріл]"
-
-#~ msgid "[ unknown]"
-#~ msgstr "[невідома]"
-
-#~ msgid "[  undef ]"
-#~ msgstr "[не визн.]"
-
-#~ msgid "[marginal]"
-#~ msgstr "[неповна ]"
-
-#~ msgid "[  full  ]"
-#~ msgstr "[ повна  ]"
-
-#~ msgid "[ultimate]"
-#~ msgstr "[безмежна]"
-
-#~ msgid "undefined"
-#~ msgstr "не визначено"
-
-#~ msgid "never"
-#~ msgstr "ніколи"
-
-#~ msgid "marginal"
-#~ msgstr "неповна"
-
-#~ msgid "full"
-#~ msgstr "повна"
-
-#~ msgid "ultimate"
-#~ msgstr "безмежна"
-
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "Використання: scdaemon [параметри] (-h — довідка)"
 
index ed873ba..9d32b3b 100644 (file)
@@ -5431,6 +5431,55 @@ msgstr ""
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "无法更新信任度数据库版本记录:写入失败:%s\n"
 
+msgid "undefined"
+msgstr "未定义"
+
+msgid "never"
+msgstr "从不"
+
+msgid "marginal"
+msgstr "勉强"
+
+msgid "full"
+msgstr "完全"
+
+msgid "ultimate"
+msgstr "绝对"
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+#, fuzzy
+#| msgid "10 translator see trustdb.c:uid_trust_string_fixed"
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr "10 translator see trustdb.c:uid_trust_string_fixed"
+
+msgid "[ revoked]"
+msgstr "[已吊销]"
+
+msgid "[ expired]"
+msgstr "[已过期]"
+
+msgid "[ unknown]"
+msgstr "[ 未知 ]"
+
+msgid "[  undef ]"
+msgstr "[未定义]"
+
+msgid "[marginal]"
+msgstr "[ 勉强 ]"
+
+msgid "[  full  ]"
+msgstr "[ 完全 ]"
+
+msgid "[ultimate]"
+msgstr "[ 绝对 ]"
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
@@ -8461,45 +8510,6 @@ msgstr ""
 #~ "you can only clearsign with PGP 2.x style keys while in --pgp2 mode\n"
 #~ msgstr "您在 --pgp2 模式下只能够使用 PGP 2.x 样式的密钥来做明文签名\n"
 
-#~ msgid "10 translator see trustdb.c:uid_trust_string_fixed"
-#~ msgstr "10 translator see trustdb.c:uid_trust_string_fixed"
-
-#~ msgid "[ revoked]"
-#~ msgstr "[已吊销]"
-
-#~ msgid "[ expired]"
-#~ msgstr "[已过期]"
-
-#~ msgid "[ unknown]"
-#~ msgstr "[ 未知 ]"
-
-#~ msgid "[  undef ]"
-#~ msgstr "[未定义]"
-
-#~ msgid "[marginal]"
-#~ msgstr "[ 勉强 ]"
-
-#~ msgid "[  full  ]"
-#~ msgstr "[ 完全 ]"
-
-#~ msgid "[ultimate]"
-#~ msgstr "[ 绝对 ]"
-
-#~ msgid "undefined"
-#~ msgstr "未定义"
-
-#~ msgid "never"
-#~ msgstr "从不"
-
-#~ msgid "marginal"
-#~ msgstr "勉强"
-
-#~ msgid "full"
-#~ msgstr "完全"
-
-#~ msgid "ultimate"
-#~ msgstr "绝对"
-
 #, fuzzy
 #~ msgid "Usage: scdaemon [options] (-h for help)"
 #~ msgstr "用法: gpg [选项] [文件] (用 -h 求助)"
index ac86d58..ffda8fd 100644 (file)
@@ -5193,6 +5193,61 @@ msgstr "深度: %d  有效: %3d  已簽署: %3d  信任: %d-, %dq, %dn, %dm, %df
 msgid "unable to update trustdb version record: write failed: %s\n"
 msgstr "無法更新信任資料庫版本記錄: 寫入失敗: %s\n"
 
+msgid "undefined"
+msgstr ""
+
+#, fuzzy
+#| msgid "never     "
+msgid "never"
+msgstr "永遠不過期"
+
+msgid "marginal"
+msgstr ""
+
+msgid "full"
+msgstr ""
+
+msgid "ultimate"
+msgstr ""
+
+#. TRANSLATORS: these strings are similar to those in
+#. trust_value_to_string(), but are a fixed length.  This is needed to
+#. make attractive information listings where columns line up
+#. properly.  The value "10" should be the length of the strings you
+#. choose to translate to.  This is the length in printable columns.
+#. It gets passed to atoi() so everything after the number is
+#. essentially a comment and need not be translated.  Either key and
+#. uid are both NULL, or neither are NULL.
+msgid "10 translator see trust.c:uid_trust_string_fixed"
+msgstr ""
+
+#, fuzzy
+#| msgid "revoked"
+msgid "[ revoked]"
+msgstr "已撤銷"
+
+#, fuzzy
+#| msgid "expired"
+msgid "[ expired]"
+msgstr "已過期"
+
+#, fuzzy
+#| msgid "unknown"
+msgid "[ unknown]"
+msgstr "未知"
+
+msgid "[  undef ]"
+msgstr ""
+
+msgid "[marginal]"
+msgstr ""
+
+msgid "[  full  ]"
+msgstr ""
+
+msgid "[ultimate]"
+msgstr ""
+
 msgid ""
 "the signature could not be verified.\n"
 "Please remember that the signature file (.sig or .asc)\n"
index 461c710..8f7c8b0 100644 (file)
@@ -1224,7 +1224,7 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
    the APP handle.  On error that field gets cleared.  If we already
    know about the public key we will just return.  Note that this does
    not mean a key is available; this is soley indicated by the
-   presence of the app->app_local->pk[KEYNO-1].key field.
+   presence of the app->app_local->pk[KEYNO].key field.
 
    Note that GnuPG 1.x does not need this and it would be too time
    consuming to send it just for the fun of it. However, given that we
@@ -1246,9 +1246,8 @@ get_public_key (app_t app, int keyno)
   gcry_sexp_t s_pkey;
   size_t len;
 
-  if (keyno < 1 || keyno > 3)
+  if (keyno < 0 || keyno > 2)
     return gpg_error (GPG_ERR_INV_ID);
-  keyno--;
 
   /* Already cached? */
   if (app->app_local->pk[keyno].read_done)
@@ -1475,11 +1474,12 @@ get_public_key (app_t app, int keyno)
 
 
 
-/* Send the KEYPAIRINFO back. KEYNO needs to be in the range [1,3].
+/* Send the KEYPAIRINFO back. KEY needs to be in the range [1,3].
    This is used by the LEARN command. */
 static gpg_error_t
-send_keypair_info (app_t app, ctrl_t ctrl, int keyno)
+send_keypair_info (app_t app, ctrl_t ctrl, int key)
 {
+  int keyno = key - 1;
   gpg_error_t err = 0;
   /* Note that GnuPG 1.x does not need this and it would be too time
      consuming to send it just for the fun of it. */
@@ -1492,19 +1492,19 @@ send_keypair_info (app_t app, ctrl_t ctrl, int keyno)
   if (err)
     goto leave;
 
-  assert (keyno >= 1 && keyno <= 3);
-  if (!app->app_local->pk[keyno-1].key)
+  assert (keyno >= 0 && keyno <= 2);
+  if (!app->app_local->pk[keyno].key)
     goto leave; /* No such key - ignore. */
 
-  err = keygrip_from_canon_sexp (app->app_local->pk[keyno-1].key,
-                                 app->app_local->pk[keyno-1].keylen,
+  err = keygrip_from_canon_sexp (app->app_local->pk[keyno].key,
+                                 app->app_local->pk[keyno].keylen,
                                  grip);
   if (err)
     goto leave;
 
   bin2hex (grip, 20, gripstr);
 
-  sprintf (idbuf, "OPENPGP.%d", keyno);
+  sprintf (idbuf, "OPENPGP.%d", keyno+1);
   send_status_info (ctrl, "KEYPAIRINFO",
                     gripstr, 40,
                     idbuf, strlen (idbuf),
@@ -1567,11 +1567,11 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
   unsigned char *buf;
 
   if (!strcmp (keyid, "OPENPGP.1"))
-    keyno = 1;
+    keyno = 0;
   else if (!strcmp (keyid, "OPENPGP.2"))
-    keyno = 2;
+    keyno = 1;
   else if (!strcmp (keyid, "OPENPGP.3"))
-    keyno = 3;
+    keyno = 2;
   else
     return gpg_error (GPG_ERR_INV_ID);
 
@@ -1579,10 +1579,10 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
   if (err)
     return err;
 
-  buf = app->app_local->pk[keyno-1].key;
+  buf = app->app_local->pk[keyno].key;
   if (!buf)
     return gpg_error (GPG_ERR_NO_PUBKEY);
-  *pklen = app->app_local->pk[keyno-1].keylen;;
+  *pklen = app->app_local->pk[keyno].keylen;;
   *pk = xtrymalloc (*pklen);
   if (!*pk)
     {
@@ -2729,10 +2729,47 @@ change_keyattr (app_t app, int keyno, const unsigned char *buf, size_t buflen,
 }
 
 
+static gpg_error_t
+change_rsa_keyattr (app_t app, int keyno, unsigned int nbits,
+                    gpg_error_t (*pincb)(void*, const char *, char **),
+                    void *pincb_arg)
+{
+  gpg_error_t err = 0;
+  unsigned char *buf;
+  size_t buflen;
+  void *relptr;
+
+  /* Read the current attributes into a buffer.  */
+  relptr = get_one_do (app, 0xC1+keyno, &buf, &buflen, NULL);
+  if (!relptr)
+    err = gpg_error (GPG_ERR_CARD);
+  else if (buflen < 6 || buf[0] != PUBKEY_ALGO_RSA)
+    {
+      /* Attriutes too short or not an RSA key.  */
+      xfree (relptr);
+      err = gpg_error (GPG_ERR_CARD);
+    }
+  else
+    {
+      /* We only change n_bits and don't touch anything else.  Before we
+         do so, we round up NBITS to a sensible way in the same way as
+         gpg's key generation does it.  This may help to sort out problems
+         with a few bits too short keys.  */
+      nbits = ((nbits + 31) / 32) * 32;
+      buf[1] = (nbits >> 8);
+      buf[2] = nbits;
+      err = change_keyattr (app, keyno, buf, buflen, pincb, pincb_arg);
+      xfree (relptr);
+    }
+
+  return err;
+}
+
+
 /* Helper to process an setattr command for name KEY-ATTR.
    In (VALUE,VALUELEN), it expects following string:
-        RSA: "--force <keyno> <algo> rsa<nbits>"
-        ECC: "--force <keyno> <algo> <curvename>"
+        RSA: "--force <key> <algo> rsa<nbits>"
+        ECC: "--force <key> <algo> <curvename>"
   */
 static gpg_error_t
 change_keyattr_from_string (app_t app,
@@ -2742,7 +2779,7 @@ change_keyattr_from_string (app_t app,
 {
   gpg_error_t err = 0;
   char *string;
-  int keyno, algo;
+  int key, keyno, algo;
   int n = 0;
 
   /* VALUE is expected to be a string but not guaranteed to be
@@ -2756,14 +2793,15 @@ change_keyattr_from_string (app_t app,
   /* Because this function deletes the key we require the string
      "--force" in the data to make clear that something serious might
      happen.  */
-  sscanf (string, " --force %d %d %n", &keyno, &algo, &n);
+  sscanf (string, " --force %d %d %n", &key, &algo, &n);
   if (n < 13)
     {
       err = gpg_error (GPG_ERR_INV_DATA);
       goto leave;
     }
 
-  if (keyno < 1 || keyno > 3)
+  keyno = key - 1;
+  if (keyno < 0 || keyno > 2)
     err = gpg_error (GPG_ERR_INV_ID);
   else if (algo == PUBKEY_ALGO_RSA)
     {
@@ -2778,64 +2816,35 @@ change_keyattr_from_string (app_t app,
       else if (nbits > 4096)
         err = gpg_error (GPG_ERR_TOO_LARGE);
       else
-        {
-          unsigned char *buf;
-          size_t buflen;
-          void *relptr;
-
-          /* Read the current attributes into a buffer.  */
-          relptr = get_one_do (app, 0xC1+keyno, &buf, &buflen, NULL);
-          if (!relptr)
-            {
-              err = gpg_error (GPG_ERR_CARD);
-              goto leave;
-            }
-          if (buflen < 6 || buf[0] != PUBKEY_ALGO_RSA)
-            {
-              /* Attriutes too short or not an RSA key.  */
-              xfree (relptr);
-              err = gpg_error (GPG_ERR_CARD);
-              goto leave;
-            }
-
-          /* We only change n_bits and don't touch anything else.  Before we
-             do so, we round up NBITS to a sensible way in the same way as
-             gpg's key generation does it.  This may help to sort out problems
-             with a few bits too short keys.  */
-          nbits = ((nbits + 31) / 32) * 32;
-          buf[1] = (nbits >> 8);
-          buf[2] = nbits;
-          err = change_keyattr (app, keyno-1, buf, buflen, pincb, pincb_arg);
-          xfree (relptr);
-        }
+        err = change_rsa_keyattr (app, keyno, nbits, pincb, pincb_arg);
     }
   else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA
            || algo == PUBKEY_ALGO_EDDSA)
     {
       const char *oidstr;
+      gcry_mpi_t oid;
+      const unsigned char *oidbuf;
+      size_t oid_len;
 
       oidstr = openpgp_curve_to_oid (string+n, NULL);
       if (!oidstr)
-        err = gpg_error (GPG_ERR_INV_DATA);
-      else
         {
-          gcry_mpi_t m;
-
-          err = openpgp_oid_from_str (oidstr, &m);
-          if (!err)
-            {
-              unsigned int len;
-              const unsigned char *buf = gcry_mpi_get_opaque (m, &len);
-
-              /* We have enough room at STRING.  */
-              len = buf[0];
-              string[0] = algo;
-              memcpy (string+1, buf+1, len++);
-              err = change_keyattr (app, keyno-1, string, len,
-                                    pincb, pincb_arg);
-              gcry_mpi_release (m);
-            }
+          err = gpg_error (GPG_ERR_INV_DATA);
+          goto leave;
         }
+
+      err = openpgp_oid_from_str (oidstr, &oid);
+      if (err)
+        goto leave;
+
+      oidbuf = gcry_mpi_get_opaque (oid, &n);
+      oid_len = (n+7)/8;
+
+      /* We have enough room at STRING.  */
+      string[0] = algo;
+      memcpy (string+1, oidbuf+1, oid_len-1);
+      err = change_keyattr (app, keyno, string, oid_len, pincb, pincb_arg);
+      gcry_mpi_release (oid);
     }
   else
     err = gpg_error (GPG_ERR_PUBKEY_ALGO);
@@ -2970,6 +2979,14 @@ rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
   if (opt.verbose)
     log_info ("RSA modulus size is %u bits (%u bytes)\n",
               nbits, (unsigned int)rsa_n_len);
+  if (nbits && nbits != maxbits
+      && app->app_local->extcap.algo_attr_change)
+    {
+      /* Try to switch the key to a new length.  */
+      err = change_rsa_keyattr (app, keyno, nbits, pincb, pincb_arg);
+      if (!err)
+        maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
+    }
   if (nbits != maxbits)
     {
       log_error (_("RSA modulus missing or not of size %d bits\n"),
@@ -3167,6 +3184,11 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
   const char *oidstr = NULL;
   int flag_djb_tweak = 0;
   int algo;
+  gcry_mpi_t oid;
+  const unsigned char *oidbuf = NULL;
+  unsigned int n;
+  size_t oid_len;
+  unsigned char fprbuf[20];
 
   /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)):
      curve = "NIST P-256" */
@@ -3305,13 +3327,38 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
   else
     algo = PUBKEY_ALGO_ECDSA;
 
+  err = openpgp_oid_from_str (oidstr, &oid);
+  if (err)
+    goto leave;
+  oidbuf = gcry_mpi_get_opaque (oid, &n);
+  oid_len = (n+7)/8;
+  if (!oidbuf)
+    {
+      err = gpg_error_from_syserror ();
+      gcry_mpi_release (oid);
+      goto leave;
+    }
+
   if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_ECC
       || app->app_local->keyattr[keyno].ecc.oid != oidstr
       || app->app_local->keyattr[keyno].ecc.flags != flag_djb_tweak)
     {
-      log_error ("key attribute on card doesn't match\n");
-      err = gpg_error (GPG_ERR_INV_VALUE);
-      goto leave;
+      if (app->app_local->extcap.algo_attr_change)
+        {
+          unsigned char keyattr[oid_len];
+
+          keyattr[0] = algo;
+          memcpy (keyattr+1, oidbuf+1, oid_len-1);
+          err = change_keyattr (app, keyno, keyattr, oid_len, pincb, pincb_arg);
+          if (err)
+            goto leave;
+        }
+      else
+        {
+          log_error ("key attribute on card doesn't match\n");
+          err = gpg_error (GPG_ERR_INV_VALUE);
+          goto leave;
+        }
     }
 
   if (opt.verbose)
@@ -3364,33 +3411,13 @@ ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
       log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
       goto leave;
     }
-  else
-    {
-      gcry_mpi_t oid;
-      const unsigned char *oidbuf;
-      unsigned int n;
-      size_t oid_len;
-      unsigned char fprbuf[20];
 
-      err = openpgp_oid_from_str (oidstr, &oid);
-      if (err)
-        goto leave;
-
-      oidbuf = gcry_mpi_get_opaque (oid, &n);
-      oid_len = (n+7)/8;
-      if (!oidbuf)
-        {
-          err = gpg_error_from_syserror ();
-          gcry_mpi_release (oid);
-          goto leave;
-        }
-      err = store_fpr (app, keyno, created_at, fprbuf, algo,
-                       oidbuf, oid_len, ecc_q, ecc_q_len,
-                       "\x03\x01\x08\x07", (size_t)4);
-      gcry_mpi_release (oid);
-    }
+  err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
+                   ecc_q, ecc_q_len, "\x03\x01\x08\x07", (size_t)4);
 
  leave:
+  if (oidbuf)
+    gcry_mpi_release (oid);
   return err;
 }
 
@@ -3486,16 +3513,15 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
   unsigned char *buffer = NULL;
   size_t buflen, keydatalen, mlen, elen;
   time_t created_at;
-  int keyno = atoi (keynostr);
+  int keyno = atoi (keynostr) - 1;
   int force = (flags & 1);
   time_t start_at;
   int exmode;
   int le_value;
   unsigned int keybits;
 
-  if (keyno < 1 || keyno > 3)
+  if (keyno < 0 || keyno > 2)
     return gpg_error (GPG_ERR_INV_ID);
-  keyno--;
 
   /* We flush the cache to increase the traffic before a key
      generation.  This _might_ help a card to gather more entropy. */
@@ -3645,7 +3671,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
   size_t buflen, n;
   int rc, i;
 
-  assert (keyno >= 1 && keyno <= 3);
+  assert (keyno >= 0 && keyno <= 2);
 
   rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0, 0);
   if (rc)
@@ -3660,7 +3686,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
       log_error (_("error reading fingerprint DO\n"));
       return gpg_error (GPG_ERR_GENERAL);
     }
-  fpr += (keyno-1)*20;
+  fpr += keyno*20;
   for (i=0; i < 20; i++)
     if (sha1fpr[i] != fpr[i])
       {
@@ -3679,7 +3705,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
    gpg has not been updated.  If there is no fingerprint we assume
    that this is okay. */
 static gpg_error_t
-check_against_given_fingerprint (app_t app, const char *fpr, int keyno)
+check_against_given_fingerprint (app_t app, const char *fpr, int key)
 {
   unsigned char tmp[20];
   const char *s;
@@ -3696,7 +3722,7 @@ check_against_given_fingerprint (app_t app, const char *fpr, int keyno)
 
   for (s=fpr, n=0; n < 20; s += 2, n++)
         tmp[n] = xtoi_2 (s);
-  return compare_fingerprint (app, keyno, tmp);
+  return compare_fingerprint (app, key-1, tmp);
 }
 
 
index 1cc580a..41a150b 100644 (file)
@@ -59,7 +59,7 @@
 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
 
 
-/* Macro to flag a removed card.  ENODEV is also tested to catch teh
+/* Macro to flag a removed card.  ENODEV is also tested to catch the
    case of a removed reader.  */
 #define TEST_CARD_REMOVAL(c,r)                              \
        do {                                                 \
index dda3eb8..43e3598 100644 (file)
@@ -23,7 +23,7 @@ bin_PROGRAMS = gpgsm
 
 AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS)
 
-AM_CPPFLAGS = -I$(top_srcdir)/common
+AM_CPPFLAGS = -I$(top_srcdir)/common -DKEYBOX_WITH_X509=1
 include $(top_srcdir)/am/cmacros.am
 
 if HAVE_W32_SYSTEM
@@ -57,7 +57,7 @@ gpgsm_SOURCES = \
        qualified.c
 
 
-common_libs = ../kbx/libkeybox.a $(libcommon)
+common_libs = ../kbx/libkeybox509.a $(libcommon)
 
 gpgsm_LDADD = $(common_libs) ../common/libgpgrl.a \
               $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \
index 571b079..cdf4a6e 100644 (file)
@@ -681,13 +681,15 @@ cmd_import (assuan_context_t ctx, char *line)
 
 
 static const char hlp_export[] =
-  "EXPORT [--data [--armor|--base64]] [--] <pattern>\n"
+  "EXPORT [--data [--armor|--base64]] [--secret [--(raw|pkcs12)] [--] <pattern>\n"
   "\n"
   "Export the certificates selected by PATTERN.  With --data the output\n"
   "is returned using Assuan D lines; the default is to use the sink given\n"
   "by the last \"OUTPUT\" command.  The options --armor or --base64 encode \n"
   "the output using the PEM respective a plain base-64 format; the default\n"
-  "is a binary format which is only suitable for a single certificate.";
+  "is a binary format which is only suitable for a single certificate.\n"
+  "With --secret the secret key is exported using the PKCS#8 format,\n"
+  "with --raw using PKCS#1, and with --pkcs12 as full PKCS#12 container.";
 static gpg_error_t
 cmd_export (assuan_context_t ctx, char *line)
 {
@@ -695,15 +697,23 @@ cmd_export (assuan_context_t ctx, char *line)
   char *p;
   strlist_t list, sl;
   int use_data;
+  int opt_secret;
+  int opt_raw = 0;
+  int opt_pkcs12 = 0;
 
   use_data = has_option (line, "--data");
-
   if (use_data)
     {
       /* We need to override any possible setting done by an OUTPUT command. */
       ctrl->create_pem = has_option (line, "--armor");
       ctrl->create_base64 = has_option (line, "--base64");
     }
+  opt_secret = has_option (line, "--secret");
+  if (opt_secret)
+    {
+      opt_raw = has_option (line, "--raw");
+      opt_pkcs12 = has_option (line, "--pkcs12");
+    }
 
   line = skip_options (line);
 
@@ -730,6 +740,14 @@ cmd_export (assuan_context_t ctx, char *line)
         }
     }
 
+  if (opt_secret)
+    {
+      if (!list || !*list->d)
+        return set_error (GPG_ERR_NO_DATA, "No key given");
+      if (list->next)
+        return set_error (GPG_ERR_TOO_MANY, "Only one key allowed");
+  }
+
   if (use_data)
     {
       estream_t stream;
@@ -741,7 +759,11 @@ cmd_export (assuan_context_t ctx, char *line)
           return set_error (GPG_ERR_ASS_GENERAL,
                             "error setting up a data stream");
         }
-      gpgsm_export (ctrl, list, stream);
+      if (opt_secret)
+        gpgsm_p12_export (ctrl, list->d, stream,
+                          opt_raw? 2 : opt_pkcs12 ? 0 : 1);
+      else
+        gpgsm_export (ctrl, list, stream);
       es_fclose (stream);
     }
   else
@@ -761,7 +783,11 @@ cmd_export (assuan_context_t ctx, char *line)
           return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
         }
 
-      gpgsm_export (ctrl, list, out_fp);
+      if (opt_secret)
+        gpgsm_p12_export (ctrl, list->d, out_fp,
+                          opt_raw? 2 : opt_pkcs12 ? 0 : 1);
+      else
+        gpgsm_export (ctrl, list, out_fp);
       es_fclose (out_fp);
     }
 
diff --git a/tests/openpgp/4gb-packet.asc b/tests/openpgp/4gb-packet.asc
new file mode 100644 (file)
index 0000000..7e5d6f3
Binary files /dev/null and b/tests/openpgp/4gb-packet.asc differ
diff --git a/tests/openpgp/4gb-packet.test b/tests/openpgp/4gb-packet.test
new file mode 100755 (executable)
index 0000000..57b8fc7
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+. $srcdir/defs.inc || exit 3
+
+# GnuPG through 2.1.7 would incorrect mark packets whose size is
+# 2^32-1 as invalid and exit with status code 2.
+i=$srcdir/4gb-packet.asc
+
+if ! $GPG --list-packets $i >/dev/null
+then
+  echo Failed to parse 4GB packet.
+  exit 1
+else
+  echo Can parse 4GB packets.
+  exit 0
+fi
index dae8c11..4fdb0a6 100644 (file)
@@ -38,7 +38,7 @@ TESTS = version.test mds.test \
        armdetachm.test detachm.test genkey1024.test \
        conventional.test conventional-mdc.test \
        multisig.test verify.test armor.test \
-       import.test ecc.test finish.test
+       import.test ecc.test 4gb-packet.test finish.test
 
 
 TEST_FILES = pubring.asc secring.asc plain-1o.asc plain-2o.asc plain-3o.asc \
@@ -46,7 +46,7 @@ TEST_FILES = pubring.asc secring.asc plain-1o.asc plain-2o.asc plain-3o.asc \
             pubring.pkr.asc secring.skr.asc secdemo.asc pubdemo.asc \
              gpg.conf.tmpl gpg-agent.conf.tmpl \
             bug537-test.data.asc bug894-test.asc \
-            bug1223-good.asc bug1223-bogus.asc
+            bug1223-good.asc bug1223-bogus.asc 4gb-packet.asc
 
 data_files = data-500 data-9000 data-32000 data-80000 plain-large