Allow reading of long gpgconf output lines.
authorWerner Koch <wk@gnupg.org>
Tue, 30 Apr 2013 16:09:13 +0000 (18:09 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 30 Apr 2013 16:09:13 +0000 (18:09 +0200)
* src/engine-gpgconf.c (gpgconf_read): Rewrite to allow for line
lengths up to 64k.

NEWS
src/engine-gpgconf.c

diff --git a/NEWS b/NEWS
index b55c113..693a2b7 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,11 @@
 Noteworthy changes in version 1.4.1 (unreleased)
 ------------------------------------------------
 
+ * Fix reading of gpg conf files with excessive use of the group
+   option.
+
+ * Fix building with the i686-w64-mingw32 toolchain.
+
 
 Noteworthy changes in version 1.4.0 (2013-02-26)
 ------------------------------------------------
index fec0fc3..3a1c1c1 100644 (file)
@@ -1,6 +1,7 @@
 /* engine-gpgconf.c - gpg-conf engine.
    Copyright (C) 2000 Werner Koch (dd9jn)
-   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008 g10 Code GmbH
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008,
+                 2013 g10 Code GmbH
 
    This file is part of GPGME.
 
@@ -191,7 +192,10 @@ gpgconf_config_release (gpgme_conf_comp_t conf)
     }
 }
 
-
+/* Read from gpgconf and pass line after line to the hook function.
+   We put a limit of 64 k on the maximum size for a line.  This should
+   allow for quite a long "group" line, which is usually the longest
+   line (mine is currently ~3k).  */
 static gpgme_error_t
 gpgconf_read (void *engine, char *arg1, char *arg2,
              gpgme_error_t (*cb) (void *hook, char *line),
@@ -199,9 +203,9 @@ gpgconf_read (void *engine, char *arg1, char *arg2,
 {
   struct engine_gpgconf *gpgconf = engine;
   gpgme_error_t err = 0;
-#define LINELENGTH 1024
-  char linebuf[LINELENGTH] = "";
-  int linelen = 0;
+  char *linebuf;
+  size_t linebufsize;
+  int linelen;
   char *argv[4] = { NULL /* file_name */, NULL, NULL, NULL };
   int rp[2];
   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
@@ -232,51 +236,80 @@ gpgconf_read (void *engine, char *arg1, char *arg2,
       return gpg_error_from_syserror ();
     }
 
-  do
+  linebufsize = 1024; /* Usually enough for conf lines.  */
+  linebuf = malloc (linebufsize);
+  if (!linebuf)
     {
-      nread = _gpgme_io_read (rp[0],
-                              linebuf + linelen, LINELENGTH - linelen - 1);
-      if (nread > 0)
-       {
-          char *line;
-          const char *lastmark = NULL;
-          size_t nused;
-
-         linelen += nread;
-         linebuf[linelen] = '\0';
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  linelen = 0;
 
-         for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
-           {
-              lastmark = mark;
-             if (mark > line && mark[-1] == '\r')
-               mark[-1] = '\0';
-              else
-                mark[0] = '\0';
-
-             /* Got a full line.  Due to the CR removal code (which
-                 occurs only on Windows) we might be one-off and thus
-                 would see empty lines.  Don't pass them to the
-                 callback. */
-             err = *line? (*cb) (hook, line) : 0;
-             if (err)
-               goto leave;
-           }
+  while ((nread = _gpgme_io_read (rp[0], linebuf + linelen,
+                                  linebufsize - linelen - 1)))
+    {
+      char *line;
+      const char *lastmark = NULL;
+      size_t nused;
+
+      if (nread < 0)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+
+      linelen += nread;
+      linebuf[linelen] = '\0';
+
+      for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
+        {
+          lastmark = mark;
+          if (mark > line && mark[-1] == '\r')
+            mark[-1] = '\0';
+          else
+            mark[0] = '\0';
+
+          /* Got a full line.  Due to the CR removal code (which
+             occurs only on Windows) we might be one-off and thus
+             would see empty lines.  Don't pass them to the
+             callback. */
+          err = *line? (*cb) (hook, line) : 0;
+          if (err)
+            goto leave;
+        }
+
+      nused = lastmark? (lastmark + 1 - linebuf) : 0;
+      memmove (linebuf, linebuf + nused, linelen - nused);
+      linelen -= nused;
+
+      if (!(linelen < linebufsize - 1))
+        {
+          char *newlinebuf;
+
+          if (linelen <  8 * 1024 - 1)
+            linebufsize = 8 * 1024;
+          else if (linelen < 64 * 1024 - 1)
+            linebufsize = 64 * 1024;
+          else
+            {
+              /* We reached our limit - give up.  */
+              err = gpg_error (GPG_ERR_LINE_TOO_LONG);
+              goto leave;
+            }
 
-          nused = lastmark? (lastmark + 1 - linebuf) : 0;
-          memmove (linebuf, linebuf + nused, linelen - nused);
-          linelen -= nused;
-       }
+          newlinebuf = realloc (linebuf, linebufsize);
+          if (!newlinebuf)
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
+          linebuf = newlinebuf;
+        }
     }
-  while (nread > 0 && linelen < LINELENGTH - 1);
-
-  if (!err && nread < 0)
-    err = gpg_error_from_syserror ();
-  if (!err && nread > 0)
-    err = gpg_error (GPG_ERR_LINE_TOO_LONG);
 
  leave:
+  free (linebuf);
   _gpgme_io_close (rp[0]);
-
   return err;
 }