Allow reading of long gpgconf output lines.

* src/engine-gpgconf.c (gpgconf_read): Rewrite to allow for line
lengths up to 64k.
This commit is contained in:
Werner Koch 2013-04-30 18:09:13 +02:00
parent 0ff0aa3fc8
commit f623a6b94c
2 changed files with 82 additions and 44 deletions

5
NEWS
View File

@ -1,6 +1,11 @@
Noteworthy changes in version 1.4.1 (unreleased) 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) Noteworthy changes in version 1.4.0 (2013-02-26)
------------------------------------------------ ------------------------------------------------

View File

@ -1,6 +1,7 @@
/* engine-gpgconf.c - gpg-conf engine. /* engine-gpgconf.c - gpg-conf engine.
Copyright (C) 2000 Werner Koch (dd9jn) 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. 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 static gpgme_error_t
gpgconf_read (void *engine, char *arg1, char *arg2, gpgconf_read (void *engine, char *arg1, char *arg2,
gpgme_error_t (*cb) (void *hook, char *line), 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; struct engine_gpgconf *gpgconf = engine;
gpgme_error_t err = 0; gpgme_error_t err = 0;
#define LINELENGTH 1024 char *linebuf;
char linebuf[LINELENGTH] = ""; size_t linebufsize;
int linelen = 0; int linelen;
char *argv[4] = { NULL /* file_name */, NULL, NULL, NULL }; char *argv[4] = { NULL /* file_name */, NULL, NULL, NULL };
int rp[2]; int rp[2];
struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0}, 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 (); return gpg_error_from_syserror ();
} }
do linebufsize = 1024; /* Usually enough for conf lines. */
linebuf = malloc (linebufsize);
if (!linebuf)
{ {
nread = _gpgme_io_read (rp[0], err = gpg_error_from_syserror ();
linebuf + linelen, LINELENGTH - linelen - 1); goto leave;
if (nread > 0)
{
char *line;
const char *lastmark = NULL;
size_t nused;
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;
}
} }
while (nread > 0 && linelen < LINELENGTH - 1); linelen = 0;
if (!err && nread < 0) while ((nread = _gpgme_io_read (rp[0], linebuf + linelen,
err = gpg_error_from_syserror (); linebufsize - linelen - 1)))
if (!err && nread > 0) {
err = gpg_error (GPG_ERR_LINE_TOO_LONG); 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;
}
newlinebuf = realloc (linebuf, linebufsize);
if (!newlinebuf)
{
err = gpg_error_from_syserror ();
goto leave;
}
linebuf = newlinebuf;
}
}
leave: leave:
free (linebuf);
_gpgme_io_close (rp[0]); _gpgme_io_close (rp[0]);
return err; return err;
} }