[network-manager-openvpn/th/ovpn-import-bgo761285: 1/12] properties: add args_parse_line() function to parse lines in openvpn configuration files



commit 7b93a2bf63cc95a3a6f1f88fdae562f3b6a9604c
Author: Thomas Haller <thaller redhat com>
Date:   Thu Jan 28 12:48:55 2016 +0100

    properties: add args_parse_line() function to parse lines in openvpn configuration files
    
    openvpn configuration files allow quoting and escaping of arguments.
    Add a function to split a line according to openvpn's behavior.

 properties/import-export.c            |  147 +++++++++++++++++++++++++++++++++
 properties/import-export.h            |    5 +
 properties/tests/test-import-export.c |   91 ++++++++++++++++++++
 shared/nm-default.h                   |    2 +
 4 files changed, 245 insertions(+), 0 deletions(-)
---
diff --git a/properties/import-export.c b/properties/import-export.c
index 3af56a4..d1f8180 100644
--- a/properties/import-export.c
+++ b/properties/import-export.c
@@ -38,6 +38,7 @@
 #include "nm-openvpn.h"
 #include "nm-openvpn-service-defines.h"
 #include "utils.h"
+#include "nm-macros-internal.h"
 
 #define CA_BLOB_START_TAG "<ca>"
 #define CA_BLOB_END_TAG "</ca>"
@@ -118,6 +119,152 @@ args_is_option (const char *line, const char *tag)
        return FALSE;
 }
 
+static char
+_ch_step_1 (const char **str, gsize *len)
+{
+       char ch;
+       g_assert (str);
+       g_assert (len && *len > 0);
+
+       ch = (*str)[0];
+
+       (*str)++;
+       (*len)--;
+       return ch;
+}
+
+static void
+_ch_skip_over_leading_whitespace (const char **str, gsize *len)
+{
+       while (*len > 0 && g_ascii_isspace ((*str)[0]))
+               _ch_step_1 (str, len);
+}
+
+static gboolean
+args_parse_line (const char *line,
+                 gsize line_len,
+                 const char ***out_p,
+                 char **out_error)
+{
+       gs_unref_array GArray *index = NULL;
+       GString *str;
+       gsize i;
+       const char *line_start = line;
+       char **data;
+       char *pdata;
+
+       /* reimplement openvpn's parse_line(). */
+
+       g_return_val_if_fail (line, FALSE);
+       g_return_val_if_fail (out_p && !*out_p, FALSE);
+       g_return_val_if_fail (out_error && !*out_error, FALSE);
+
+       *out_p = NULL;
+
+       /* we expect no newline during the first line_len chars. */
+       for (i = 0; i < line_len; i++) {
+               if (NM_IN_SET (line[i], '\0', '\n'))
+                       g_return_val_if_reached (FALSE);
+       }
+
+       /* if the line ends with '\r', drop that right way (covers \r\n). */
+       if (line_len > 0 && line[line_len - 1] == '\r')
+               line_len--;
+
+       /* skip over leading space. */
+       _ch_skip_over_leading_whitespace (&line, &line_len);
+
+       if (line_len == 0)
+               return TRUE;
+
+       if (NM_IN_SET (line[0], ';', '#')) {
+               /* comment. Note that als openvpn allows for leading spaces
+                * *before* the comment starts */
+               return TRUE;
+       }
+
+       str = g_string_sized_new (line_len + 5);
+       index = g_array_new (FALSE, FALSE, sizeof (gsize));
+
+       while (line_len > 0) {
+               char quote, ch0;
+               gssize word_start = line - line_start;
+
+               g_array_append_val (index, str->len);
+
+               do {
+                       switch ((ch0 = _ch_step_1 (&line, &line_len))) {
+                       case '"':
+                       case '\'':
+                               quote = ch0;
+
+                               while (line_len > 0 && line[0] != quote) {
+                                       if (quote == '"' && line[0] == '\\') {
+                                               _ch_step_1 (&line, &line_len);
+                                               if (line_len <= 0)
+                                                       break;
+                                       }
+                                       g_string_append_c (str, _ch_step_1 (&line, &line_len));
+                               }
+
+                               if (line_len <= 0) {
+                                       *out_error = g_strdup_printf (_("unterminated %s at position %lld"),
+                                                                     quote == '"' ? _("double quote") : 
_("single quote"),
+                                                                     (long long) word_start);
+                                       g_string_free (str, TRUE);
+                                       return FALSE;
+                               }
+
+                               _ch_step_1 (&line, &line_len);
+                               break;
+                       case '\\':
+                               if (line_len <= 0) {
+                                       *out_error = g_strdup_printf (_("trailing escaping backslash at 
position %lld"),
+                                                                     (long long) word_start);
+                                       g_string_free (str, TRUE);
+                                       return FALSE;
+                               }
+                               g_string_append_c (str, _ch_step_1 (&line, &line_len));
+                               break;
+                       default:
+                               if (g_ascii_isspace (ch0))
+                                       goto word_completed;
+                               g_string_append_c (str, ch0);
+                               break;
+                       }
+               } while (line_len > 0);
+word_completed:
+
+               /* the current word is complete.*/
+               g_string_append_c (str, '\0');
+               _ch_skip_over_leading_whitespace (&line, &line_len);
+       }
+
+       /* pack the result in a strv array */
+       data = g_malloc ((sizeof (const char *) * (index->len + 1)) + str->len);
+
+       pdata = (char *) &data[index->len + 1];
+       memcpy (pdata, str->str, str->len);
+
+       for (i = 0; i < index->len; i++)
+               data[i] = &pdata[g_array_index (index, gsize, i)];
+       data[i] = NULL;
+
+       g_string_free (str, TRUE);
+       *out_p = (const char **) data;
+
+       return TRUE;
+}
+
+gboolean
+_nmovpn_test_args_parse_line (const char *line,
+                              gsize line_len,
+                              const char ***out_p,
+                              char **out_error)
+{
+       return args_parse_line (line, line_len, out_p, out_error);
+}
+
 static char *
 unquote (const char *line, char **leftover)
 {
diff --git a/properties/import-export.h b/properties/import-export.h
index 9a1e1cc..da46c97 100644
--- a/properties/import-export.h
+++ b/properties/import-export.h
@@ -30,6 +30,11 @@
 #include <NetworkManager.h>
 #endif
 
+gboolean _nmovpn_test_args_parse_line (const char *line,
+                                       gsize line_len,
+                                       const char ***out_p,
+                                       char **out_error);
+
 NMConnection *do_import (const char *path, const char *contents, gsize contents_len, GError **error);
 
 gboolean do_export (const char *path, NMConnection *connection, GError **error);
diff --git a/properties/tests/test-import-export.c b/properties/tests/test-import-export.c
index ac69d15..5e7d84e 100644
--- a/properties/tests/test-import-export.c
+++ b/properties/tests/test-import-export.c
@@ -29,6 +29,7 @@
 
 #include "nm-openvpn.h"
 #include "nm-openvpn-service-defines.h"
+#include "import-export.h"
 
 #include "nm-test-utils.h"
 
@@ -1350,6 +1351,94 @@ test_route_export (NMVpnEditorPlugin *plugin,
        g_free (path);
 }
 
+/*****************************************************************************/
+
+static void
+do_test_args_parse_impl (const char *line,
+                         gboolean expects_success,
+                         ...)
+{
+       va_list ap;
+       guint i;
+       const char *s;
+       const char *expected_str[100] = { NULL };
+       gboolean again = TRUE;
+       gs_free char *line_again = NULL;
+       gsize len;
+
+       va_start (ap, len);
+       i = 0;
+       do {
+               s = va_arg (ap, const char *);
+               g_assert (i < G_N_ELEMENTS (expected_str));
+               expected_str[i++] = s;
+       } while (s);
+       va_end (ap);
+
+       len = strlen (line);
+
+do_again:
+       {
+               gs_free const char **p = NULL;
+               gs_free char *line_error = NULL;
+
+               if (!_nmovpn_test_args_parse_line (line, len, &p, &line_error)) {
+                       g_assert (!expects_success);
+                       g_assert (line_error && line_error[0]);
+                       g_assert (!p);
+               } else {
+                       g_assert (expects_success);
+                       g_assert (!line_error);
+
+                       if (expected_str[0] == NULL) {
+                               g_assert (!p);
+                       } else {
+                               g_assert (p);
+                               for (i = 0; TRUE; i++) {
+                                       g_assert_cmpstr (p[i], ==, expected_str[i]);
+                                       if (expected_str[i] == NULL)
+                                               break;
+                                       if (i > 0)
+                                               g_assert (p[i] == &((p[i - 1])[strlen (p[i - 1]) + 1]));
+                               }
+                               g_assert (p[0] == (const char *) (&p[i + 1]));
+                       }
+               }
+       }
+
+       if (again) {
+               /* append some gibberish. Ensure it's ignored. */
+               line = line_again = g_strconcat (line, "X", NULL);
+               again = FALSE;
+               goto do_again;
+       }
+}
+#define do_test_args_parse_line(...) do_test_args_parse_impl (__VA_ARGS__, NULL)
+
+static void
+test_args_parse_line (void)
+{
+       do_test_args_parse_line ("", TRUE);
+       do_test_args_parse_line ("  ", TRUE);
+       do_test_args_parse_line (" \t", TRUE);
+       do_test_args_parse_line (" \r", TRUE);
+       do_test_args_parse_line ("a", TRUE, "a");
+       do_test_args_parse_line (" ba ", TRUE, "ba");
+       do_test_args_parse_line (" b  a ", TRUE, "b", "a");
+       do_test_args_parse_line (" b \\ \\a ", TRUE, "b", " a");
+       do_test_args_parse_line ("\\ b \\ \\a ", TRUE, " b", " a");
+       do_test_args_parse_line ("'\\ b \\ \\a '", TRUE, "\\ b \\ \\a ");
+       do_test_args_parse_line ("\"\\ b \\ \\a \"a'b'", TRUE, " b  a ab");
+       do_test_args_parse_line ("\"\\ b \\ \\a \"a\\ 'b'", TRUE, " b  a a b");
+       do_test_args_parse_line ("\"\\ b \\ \\a \"a\\ 'b'   sd\\ \t", TRUE, " b  a a b", "sd ");
+
+       do_test_args_parse_line ("\"adfdaf  adf  ", FALSE);
+       do_test_args_parse_line ("\"adfdaf  adf  \\\"", FALSE);
+       do_test_args_parse_line ("\"\\ b \\ \\a \"a\\ 'b'   sd\\", FALSE);
+}
+
+/*****************************************************************************/
+
 int main (int argc, char **argv)
 {
        GError *error = NULL;
@@ -1420,6 +1509,8 @@ int main (int argc, char **argv)
        test_route_import (plugin, "route-import", TEST_SRCDIR_CONF);
        test_route_export (plugin, "route-export", TEST_SRCDIR_CONF, TEST_BUILDDIR_CONF);
 
+       test_args_parse_line ();
+
        g_object_unref (plugin);
 
        basename = g_path_get_basename (argv[0]);
diff --git a/shared/nm-default.h b/shared/nm-default.h
index 968d8c6..6990de4 100644
--- a/shared/nm-default.h
+++ b/shared/nm-default.h
@@ -76,6 +76,7 @@
 #define OPENVPN_EDITOR_PLUGIN_ERROR_MISSING_PROPERTY    NM_SETTING_VPN_ERROR_MISSING_PROPERTY
 #define OPENVPN_EDITOR_PLUGIN_ERROR_FILE_NOT_OPENVPN    NM_SETTING_VPN_ERROR_UNKNOWN
 #define OPENVPN_EDITOR_PLUGIN_ERROR_FILE_NOT_READABLE   NM_SETTING_VPN_ERROR_UNKNOWN
+#define OPENVPN_EDITOR_PLUGIN_ERROR_FILE_INVALID        NM_SETTING_VPN_ERROR_UNKNOWN
 
 #else /* !NM_OPENVPN_OLD */
 
@@ -86,6 +87,7 @@
 #define OPENVPN_EDITOR_PLUGIN_ERROR_MISSING_PROPERTY    NM_CONNECTION_ERROR_MISSING_PROPERTY
 #define OPENVPN_EDITOR_PLUGIN_ERROR_FILE_NOT_OPENVPN    NM_CONNECTION_ERROR_FAILED
 #define OPENVPN_EDITOR_PLUGIN_ERROR_FILE_NOT_READABLE   NM_CONNECTION_ERROR_FAILED
+#define OPENVPN_EDITOR_PLUGIN_ERROR_FILE_INVALID        NM_CONNECTION_ERROR_FAILED
 
 #endif /* NM_OPENVPN_OLD */
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]