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