r20056 - gnucash/trunk/src/libqof/qof - Bug #638543: Validate counter format strings before using them.
Christian Stimming
cstim at code.gnucash.org
Mon Jan 10 16:39:29 EST 2011
Author: cstim
Date: 2011-01-10 16:39:29 -0500 (Mon, 10 Jan 2011)
New Revision: 20056
Trac: http://svn.gnucash.org/trac/changeset/20056
Modified:
gnucash/trunk/src/libqof/qof/qofbook.c
gnucash/trunk/src/libqof/qof/qofbook.h
Log:
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.
Modified: gnucash/trunk/src/libqof/qof/qofbook.c
===================================================================
--- gnucash/trunk/src/libqof/qof/qofbook.c 2011-01-10 21:39:18 UTC (rev 20055)
+++ gnucash/trunk/src/libqof/qof/qofbook.c 2011-01-10 21:39:29 UTC (rev 20056)
@@ -495,6 +495,7 @@
KvpFrame *kvp;
gchar *format;
KvpValue *value;
+ gchar *error;
if (!book)
{
@@ -517,13 +518,26 @@
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 @@
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)
Modified: gnucash/trunk/src/libqof/qof/qofbook.h
===================================================================
--- gnucash/trunk/src/libqof/qof/qofbook.h 2011-01-10 21:39:18 UTC (rev 20055)
+++ gnucash/trunk/src/libqof/qof/qofbook.h 2011-01-10 21:39:29 UTC (rev 20056)
@@ -289,6 +289,12 @@
*/
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.
More information about the gnucash-changes
mailing list