[asterisk-commits] tilghman: trunk r257560 - in /trunk: ./ include/asterisk/ main/ tests/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Apr 15 16:26:23 CDT 2010


Author: tilghman
Date: Thu Apr 15 16:26:19 2010
New Revision: 257560

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=257560
Log:
Merged revisions 257544 via svnmerge from 
https://origsvn.digium.com/svn/asterisk/branches/1.4

........
  r257544 | tilghman | 2010-04-15 16:23:24 -0500 (Thu, 15 Apr 2010) | 6 lines
  
  Allow application options with arguments to contain parentheses, through a variety of escaping techniques.
  
  Fixes SWP-1194 (ABE-2143).
  
  Review: https://reviewboard.asterisk.org/r/604/
........

Modified:
    trunk/   (props changed)
    trunk/include/asterisk/app.h
    trunk/main/app.c
    trunk/tests/test_app.c

Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-1.4-merged' - no diff available.

Modified: trunk/include/asterisk/app.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/app.h?view=diff&rev=257560&r1=257559&r2=257560
==============================================================================
--- trunk/include/asterisk/app.h (original)
+++ trunk/include/asterisk/app.h Thu Apr 15 16:26:19 2010
@@ -512,8 +512,7 @@
 
   	... do any argument parsing here ...
 
-	if (ast_parseoptions(my_app_options, &opts, opt_args, options)) {
-		ast_module_user_remove(u);
+	if (ast_app_parse_options(my_app_options, &opts, opt_args, options)) {
 		return -1;
 	}
   }

Modified: trunk/main/app.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/app.c?view=diff&rev=257560&r1=257559&r2=257560
==============================================================================
--- trunk/main/app.c (original)
+++ trunk/main/app.c Thu Apr 15 16:26:19 2010
@@ -1809,13 +1809,19 @@
 	return output;
 }
 
-int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
+static int parse_options(const struct ast_app_option *options, void *_flags, char **args, char *optstr, int flaglen)
 {
 	char *s, *arg;
 	int curarg, res = 0;
 	unsigned int argloc;
-
-	ast_clear_flag(flags, AST_FLAGS_ALL);
+	struct ast_flags *flags = _flags;
+	struct ast_flags64 *flags64 = _flags;
+
+	if (flaglen == 32) {
+		ast_clear_flag(flags, AST_FLAGS_ALL);
+	} else {
+		flags64->flags = 0;
+	}
 
 	if (!optstr) {
 		return 0;
@@ -1826,8 +1832,40 @@
 		curarg = *s++ & 0x7f;	/* the array (in app.h) has 128 entries */
 		argloc = options[curarg].arg_index;
 		if (*s == '(') {
+			int paren = 1, quote = 0;
+			int parsequotes = (s[1] == '"') ? 1 : 0;
+
 			/* Has argument */
 			arg = ++s;
+			for (; *s; s++) {
+				if (*s == '(' && !quote) {
+					paren++;
+				} else if (*s == ')' && !quote) {
+					/* Count parentheses, unless they're within quotes (or backslashed, below) */
+					paren--;
+				} else if (*s == '"' && parsequotes) {
+					/* Leave embedded quotes alone, unless they are the first character */
+					quote = quote ? 0 : 1;
+					ast_copy_string(s, s + 1, INT_MAX);
+					s--;
+				} else if (*s == '\\') {
+					if (!quote) {
+						/* If a backslash is found outside of quotes, remove it */
+						ast_copy_string(s, s + 1, INT_MAX);
+					} else if (quote && s[1] == '"') {
+						/* Backslash for a quote character within quotes, remove the backslash */
+						ast_copy_string(s, s + 1, INT_MAX);
+					} else {
+						/* Backslash within quotes, keep both characters */
+						s++;
+					}
+				}
+
+				if (paren == 0) {
+					break;
+				}
+			}
+			/* This will find the closing paren we found above, or none, if the string ended before we found one. */
 			if ((s = strchr(s, ')'))) {
 				if (argloc) {
 					args[argloc - 1] = arg;
@@ -1841,48 +1879,24 @@
 		} else if (argloc) {
 			args[argloc - 1] = "";
 		}
-		ast_set_flag(flags, options[curarg].flag);
+		if (flaglen == 32) {
+			ast_set_flag(flags, options[curarg].flag);
+		} else {
+			ast_set_flag64(flags64, options[curarg].flag);
+		}
 	}
 
 	return res;
 }
 
+int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
+{
+	return parse_options(options, flags, args, optstr, 32);
+}
+
 int ast_app_parse_options64(const struct ast_app_option *options, struct ast_flags64 *flags, char **args, char *optstr)
 {
-	char *s, *arg;
-	int curarg, res = 0;
-	unsigned int argloc;
-
-	flags->flags = 0;
-
-	if (!optstr) {
-		return 0;
-	}
-
-	s = optstr;
-	while (*s) {
-		curarg = *s++ & 0x7f;	/* the array (in app.h) has 128 entries */
-		ast_set_flag64(flags, options[curarg].flag);
-		argloc = options[curarg].arg_index;
-		if (*s == '(') {
-			/* Has argument */
-			arg = ++s;
-			if ((s = strchr(s, ')'))) {
-				if (argloc) {
-					args[argloc - 1] = arg;
-				}
-				*s++ = '\0';
-			} else {
-				ast_log(LOG_WARNING, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg, arg);
-				res = -1;
-				break;
-			}
-		} else if (argloc) {
-			args[argloc - 1] = NULL;
-		}
-	}
-
-	return res;
+	return parse_options(options, flags, args, optstr, 64);
 }
 
 void ast_app_options2str64(const struct ast_app_option *options, struct ast_flags64 *flags, char *buf, size_t len)

Modified: trunk/tests/test_app.c
URL: http://svnview.digium.com/svn/asterisk/trunk/tests/test_app.c?view=diff&rev=257560&r1=257559&r2=257560
==============================================================================
--- trunk/tests/test_app.c (original)
+++ trunk/tests/test_app.c Thu Apr 15 16:26:19 2010
@@ -39,6 +39,88 @@
 #include "asterisk/channel.h"
 
 #define BASE_GROUP "a group"
+
+AST_TEST_DEFINE(options_parsing)
+{
+	enum test_option_flags {
+		OPT_SIMPLE,
+		OPT_WITHQUOTES,
+		OPT_WITHBACKSLASH,
+	};
+	enum test_option_args {
+		OPT_ARG_SIMPLE,
+		OPT_ARG_WITHQUOTES,
+		OPT_ARG_WITHBACKSLASH,
+		OPT_ARG_ARRAY_SIZE,
+	};
+	AST_APP_OPTIONS(test_options, {
+		AST_APP_OPTION_ARG('a', OPT_SIMPLE,        OPT_ARG_SIMPLE),
+		AST_APP_OPTION_ARG('b', OPT_WITHQUOTES,    OPT_ARG_WITHQUOTES),
+		AST_APP_OPTION_ARG('c', OPT_WITHBACKSLASH, OPT_ARG_WITHBACKSLASH),
+	});
+	struct ast_flags opts = { 0, };
+	struct ast_flags64 opts64 = { 0, };
+	char *opt_args[OPT_ARG_ARRAY_SIZE];
+	struct {
+		const char *string;
+		const char *parse[3];
+	} options[] = {
+		{ "a(simple)b(\"quoted\")c(back\\slash)", { "simple", "quoted", "backslash", }, },
+		{ "b(\"((())))\")a(simple)c(back\\)slash)", { "simple", "((())))", "back)slash", }, },
+		{ "b(\"((\\\"\\)\\(\")a(simple)c(back\\\"\\)\\(\\\"slash)", { "simple", "((\"\\)\\(", "back\")(\"slash", }, },
+	};
+	int i, j, res = AST_TEST_PASS;
+	char buffer[256];
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "options_parsing";
+		info->category = "main/app/";
+		info->summary = "App options unit test";
+		info->description =
+			"This tests the options parsing code to ensure that it behaves as expected";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	for (i = 0; i < ARRAY_LEN(options); i++) {
+		ast_copy_string(buffer, options[i].string, sizeof(buffer));
+
+		if (ast_app_parse_options(test_options, &opts, opt_args, buffer)) {
+			ast_test_status_update(test, "ast_app_parse_options() of '%s' failed\n", options[i].string);
+			res = AST_TEST_FAIL;
+		} else {
+			/* Check arguments for success */
+			for (j = 0; j < 3; j++) {
+				if (strcmp(opt_args[j], options[i].parse[j])) {
+					ast_test_status_update(test, "Parse of option %c from '%s' produced '%s', "
+						"but it should have produced '%s'\n",
+						'a' + j, options[i].string, opt_args[j], options[i].parse[j]);
+					res = AST_TEST_FAIL;
+				}
+			}
+		}
+
+		ast_copy_string(buffer, options[i].string, sizeof(buffer));
+		if (ast_app_parse_options64(test_options, &opts64, opt_args, buffer)) {
+			ast_test_status_update(test, "ast_app_parse_options64() of '%s' failed\n", options[i].string);
+			res = AST_TEST_FAIL;
+		} else {
+			/* Check arguments for success */
+			for (j = 0; j < 3; j++) {
+				if (strcmp(opt_args[j], options[i].parse[j])) {
+					ast_test_status_update(test, "Parse of option %c from '%s' produced '%s', "
+						"but it should have produced '%s'\n",
+						'a' + j, options[i].string, opt_args[j], options[i].parse[j]);
+					res = AST_TEST_FAIL;
+				}
+			}
+		}
+	}
+
+	return res;
+}
 
 AST_TEST_DEFINE(app_group)
 {
@@ -147,13 +229,15 @@
 static int unload_module(void)
 {
 	AST_TEST_UNREGISTER(app_group);
+	AST_TEST_UNREGISTER(options_parsing);
 	return 0;
 }
 
 static int load_module(void)
 {
 	AST_TEST_REGISTER(app_group);
+	AST_TEST_REGISTER(options_parsing);
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "App unit test");
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "App unit tests");




More information about the asterisk-commits mailing list