[network-manager-openvpn/th/ovpn-import-bgo761285: 1/12] properties: add args_parse_line() function to parse lines in openvpn configuration files
- From: Thomas Haller <thaller src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [network-manager-openvpn/th/ovpn-import-bgo761285: 1/12] properties: add args_parse_line() function to parse lines in openvpn configuration files
- Date: Fri, 29 Jan 2016 14:38:01 +0000 (UTC)
commit dab66ff2e611bbecbfe57f3fde203eccbbc4dad4
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]