Bug #638543: Validate counter format strings before using them.

Patch by Matthijs Kooijman:

The validation function is a very simple "parser" that simply checks for
a single gint64 conversion specification, allowing all modifiers and
flags that printf(3) specifies (except for the * width and precision,
which need an extra argument).

The validation function returns an error message that is used to log a
warning and can be used by the GUI later on.

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@20056 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Christian Stimming 2011-01-10 21:39:29 +00:00
parent e1c6d28eac
commit 10b45bf554
2 changed files with 112 additions and 1 deletions

View File

@ -495,6 +495,7 @@ qof_book_get_counter_format(const QofBook *book, const char *counter_name)
KvpFrame *kvp;
gchar *format;
KvpValue *value;
gchar *error;
if (!book)
{
@ -517,13 +518,26 @@ qof_book_get_counter_format(const QofBook *book, const char *counter_name)
return NULL;
}
format = NULL;
/* Get the format string */
value = kvp_frame_get_slot_path (kvp, "counter_formats", counter_name, NULL);
if (value)
{
format = kvp_value_get_string (value);
error = qof_book_validate_counter_format(format);
if (error != NULL)
{
PWARN("Invalid counter format string. Format string: '%s' Counter: '%s' Erorr: '%s')", format, counter_name, error);
/* Invalid format string */
format = NULL;
g_free(error);
}
}
else
/* If no (valid) format string was found, use the default format
* string */
if (!format)
{
/* Use the default format */
format = "%.6" G_GINT64_FORMAT;
@ -531,6 +545,97 @@ qof_book_get_counter_format(const QofBook *book, const char *counter_name)
return format;
}
gchar *
qof_book_validate_counter_format(const gchar *p)
{
const gchar *conv_start, *tmp;
/* Validate a counter format. This is a very simple "parser" that
* simply checks for a single gint64 conversion specification,
* allowing all modifiers and flags that printf(3) specifies (except
* for the * width and precision, which need an extra argument). */
/* Skip a prefix of any character except % */
while (*p)
{
/* Skip two adjacent percent marks, which are literal percent
* marks */
if (p[0] == '%' && p[1] == '%')
{
p += 2;
continue;
}
/* Break on a single percent mark, which is the start of the
* conversion specification */
if (*p == '%')
break;
/* Skip all other characters */
p++;
}
if (!*p)
return g_strdup("Format string ended without any conversion specification");
/* Store the start of the conversion for error messages */
conv_start = p;
/* Skip the % */
p++;
/* Skip any number of flag characters */
while (*p && strchr("#0- +'I", *p)) p++;
/* Skip any number of field width digits */
while (*p && strchr("0123456789", *p)) p++;
/* A precision specifier always starts with a dot */
if (*p && *p == '.')
{
/* Skip the . */
p++;
/* Skip any number of precision digits */
while (*p && strchr("0123456789", *p)) p++;
}
if (!*p)
return g_strdup_printf("Format string ended during the conversion specification. Conversion seen so far: %s", conv_start);
/* See if the format string starts with the correct format
* specification. */
tmp = strstr(p, G_GINT64_FORMAT);
if (tmp == NULL)
{
return g_strdup_printf("Invalid length modifier and/or conversion specifier ('%.2s'), it should be: " G_GINT64_FORMAT, p);
} else if (tmp != p) {
return g_strdup_printf("Garbage before length modifier and/or conversion specifier: '%*s'", (int)(tmp - p), p);
}
/* Skip length modifier / conversion specifier */
p += strlen(G_GINT64_FORMAT);
/* Skip a suffix of any character except % */
while (*p)
{
/* Skip two adjacent percent marks, which are literal percent
* marks */
if (p[0] == '%' && p[1] == '%')
{
p += 2;
continue;
}
/* Break on a single percent mark, which is the start of the
* conversion specification */
if (*p == '%')
return g_strdup_printf("Format string contains unescaped %% signs (or multiple conversion specifications) at '%s'", p);
/* Skip all other characters */
p++;
}
/* If we end up here, the string was valid, so return no error
* message */
return NULL;
}
/* Determine whether this book uses trading accounts */
gboolean
qof_book_use_trading_accounts (const QofBook *book)

View File

@ -289,6 +289,12 @@ gint64 qof_book_get_counter (QofBook *book, const char *counter_name);
*/
gchar *qof_book_increment_and_format_counter (QofBook *book, const char *counter_name);
/** Validate a counter format string. Returns an error message if the
* format string was invalid, or NULL if it is ok. The caller should
* free the error message with g_free.
*/
gchar * qof_book_validate_counter_format(const gchar *format);
/** Get the format string to use for the named counter.
* The return value is NULL on error or the format string of the
* counter. The string should not be freed.