[Asterisk-code-review] Geolocation: Base Asterisk Prereqs (asterisk[16])

George Joseph asteriskteam at digium.com
Tue Jun 28 07:04:09 CDT 2022


George Joseph has uploaded this change for review. ( https://gerrit.asterisk.org/c/asterisk/+/18691 )


Change subject: Geolocation: Base Asterisk Prereqs
......................................................................

Geolocation: Base Asterisk Prereqs

* Added ast_variable_list_from_quoted_string()
  Parse a quoted string into an ast_variable list.

* Added ast_str_substitute_variables_full2()
  Perform variable/function/expression substitution on an ast_str.

* Added ast_strsep_quoted()
  Like ast_strsep except you can specify a specific quote character.

* Added ast_xml_find_child_element()
  Find a direct child element by name.

* Added ast_xml_doc_dump_memory()
  Dump the specified document to a buffer

* ast_datastore_free() now checks for a NULL datastore
  before attempting to destroy it.

Change-Id: I5dcefed2f5f93a109e8b489e18d80d42e45244ec
---
M include/asterisk/config.h
M include/asterisk/pbx.h
M include/asterisk/strings.h
M include/asterisk/xml.h
M main/config.c
M main/datastore.c
M main/pbx_variables.c
M main/utils.c
M main/xml.c
M tests/test_config.c
10 files changed, 219 insertions(+), 20 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/91/18691/1

diff --git a/include/asterisk/config.h b/include/asterisk/config.h
index 3aef5f1..44ba4e4 100644
--- a/include/asterisk/config.h
+++ b/include/asterisk/config.h
@@ -1024,6 +1024,26 @@
 	const char *name_value_separator);
 
 /*!
+ * \brief Parse a string into an ast_variable list.  The reverse of ast_variable_list_join
+ *
+ * \param input                The name-value pair string to parse.
+ * \param item_separator       The string used to separate the list items.
+ *                             Only the first character in the string will be used.
+ *                             If NULL, "," will be used.
+ * \param name_value_separator The string used to separate each item's name and value.
+ *                             Only the first character in the string will be used.
+ *                             If NULL, "=" will be used.
+ * \param quote_str            The string used to quote values.
+ *                             Only the first character in the string will be used.
+ *                             If NULL, '"' will be used.
+ *
+ * \retval A pointer to a list of ast_variables.
+ * \retval NULL if there was an error or no variables could be parsed.
+ */
+struct ast_variable *ast_variable_list_from_quoted_string(const char *input, const char *item_separator,
+	const char *name_value_separator, const char *quote_str);
+
+/*!
  * \brief Update variable value within a config
  *
  * \param category Category element within the config
diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
index d531b44..6f2db63 100644
--- a/include/asterisk/pbx.h
+++ b/include/asterisk/pbx.h
@@ -1455,6 +1455,28 @@
  * \param used Number of bytes read from the template.  (May be NULL)
  */
 void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used);
+
+/*!
+ * \brief Perform variable/function/expression substitution on an ast_str
+ *
+ * \param buf      Result will be placed in this buffer.
+ * \param maxlen   -1 if the buffer should not grow, 0 if the buffer
+ *                 may grow to any size, and >0 if the buffer should
+ *                 grow only to that number of bytes.
+ * \param c        A channel from which to extract values, and to pass
+ *                 to any dialplan functions.
+ * \param headp    A channel variables list to also search for variables.
+ * \param templ    Variable template to expand.
+ * \param used     Number of bytes read from the template.  (May be NULL)
+ * \param use_both Normally, if a channel is specified, headp is ignored.
+ *                 If this parameter is set to 1 and both a channel and headp
+ *                 are specified, the channel will be searched for variables
+ *                 first and any not found will be searched for in headp.
+ */
+void ast_str_substitute_variables_full2(struct ast_str **buf, ssize_t maxlen,
+	struct ast_channel *c, struct varshead *headp, const char *templ,
+	size_t *used, int use_both);
+
 /*! @} */
 
 int ast_extension_patmatch(const char *pattern, const char *data);
diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h
index 93983c3..d2c3c82 100644
--- a/include/asterisk/strings.h
+++ b/include/asterisk/strings.h
@@ -309,6 +309,24 @@
 char *ast_strsep(char **s, const char sep, uint32_t flags);
 
 /*!
+ * \brief Like ast_strsep() except you can specify a specific quote character
+ *
+  \param s Pointer to address of the string to be processed.
+  Will be modified and can't be constant.
+  \param sep A single character delimiter.
+  \param quote The quote character
+  \param flags Controls post-processing of the result.
+  AST_STRSEP_TRIM trims all leading and trailing whitespace from the result.
+  AST_STRSEP_STRIP does a trim then strips the outermost quotes.  You may want
+  to trim again after the strip.  Just OR both the TRIM and STRIP flags.
+  AST_STRSEP_UNESCAPE unescapes '\' sequences.
+  AST_STRSEP_ALL does all of the above processing.
+  \return The next token or NULL if done or if there are more than 8 levels of
+  nested quotes.
+ */
+char *ast_strsep_quoted(char **s, const char sep, const char quote, uint32_t flags);
+
+/*!
   \brief Strip backslash for "escaped" semicolons,
 	the string to be stripped (will be modified).
   \return The stripped string.
diff --git a/include/asterisk/xml.h b/include/asterisk/xml.h
index 9d43560..c1b0797 100644
--- a/include/asterisk/xml.h
+++ b/include/asterisk/xml.h
@@ -189,6 +189,18 @@
 struct ast_xml_ns *ast_xml_find_namespace(struct ast_xml_doc *doc, struct ast_xml_node *node, const char *ns_name);
 
 /*!
+ * \brief Find a direct child element by name.
+ * \param parent_node This is the parent node to search.
+ * \param name Node name to find.
+ * \param attrname attribute name to match (if NULL it won't be matched).
+ * \param attrvalue attribute value to match (if NULL it won't be matched).
+ * \retval NULL if not found.
+ * \return The node on success.
+ */
+#define ast_xml_find_child_element(_parent_node, _name, _attrname, _attrvalue) \
+    ast_xml_find_element(ast_xml_node_get_children(_parent_node), _name, _attrname, _attrvalue)
+
+/*!
  * \brief Get the prefix of a namespace.
  * \param ns The namespace
  * \return The prefix of the namespace.
@@ -249,6 +261,17 @@
 int ast_xml_doc_dump_file(FILE *output, struct ast_xml_doc *doc);
 
 /*!
+ * \brief Dump the specified document to a buffer
+ *
+ * \param doc The XML doc to dump
+ * \param buffer A pointer to a char * to receive the address of the results
+ * \param length A pointer to an int to receive the length of the results
+ *
+ * \note The result buffer must be freed with ast_xml_free_text().
+ */
+void ast_xml_doc_dump_memory(struct ast_xml_doc *doc, char **buffer, int *length);
+
+/*!
  * \brief Free the XPath results
  * \param results The XPath results object to dispose of
  *
diff --git a/main/config.c b/main/config.c
index 122e7aa..0e27d76 100644
--- a/main/config.c
+++ b/main/config.c
@@ -722,11 +722,12 @@
 	return local_str;
 }
 
-struct ast_variable *ast_variable_list_from_string(const char *input, const char *item_separator,
-	const char *name_value_separator)
+struct ast_variable *ast_variable_list_from_quoted_string(const char *input, const char *item_separator,
+	const char *name_value_separator, const char *quote_str)
 {
 	char item_sep;
 	char nv_sep;
+	char quote;
 	struct ast_variable *new_list = NULL;
 	struct ast_variable *new_var = NULL;
 	char *item_string;
@@ -740,11 +741,22 @@
 
 	item_sep = ast_strlen_zero(item_separator) ? ',' : item_separator[0];
 	nv_sep = ast_strlen_zero(name_value_separator) ? '=' : name_value_separator[0];
+	quote = ast_strlen_zero(quote_str) ? '"' : quote_str[0];
 	item_string = ast_strip(ast_strdupa(input));
 
-	while ((item = ast_strsep(&item_string, item_sep, AST_STRSEP_ALL))) {
-		item_name = ast_strsep(&item, nv_sep, AST_STRSEP_ALL);
-		item_value = ast_strsep(&item, nv_sep, AST_STRSEP_ALL);
+	while ((item = ast_strsep_quoted(&item_string, item_sep, quote, AST_STRSEP_ALL))) {
+		item_name = ast_strsep_quoted(&item, nv_sep, quote, AST_STRSEP_ALL);
+		if (!item_name) {
+			ast_variables_destroy(new_list);
+			return NULL;
+		}
+
+		item_value = ast_strsep_quoted(&item, nv_sep, quote, AST_STRSEP_ALL);
+		if (!item_value) {
+			ast_variables_destroy(new_list);
+			return NULL;
+		}
+
 		new_var = ast_variable_new(item_name, item_value, "");
 		if (!new_var) {
 			ast_variables_destroy(new_list);
@@ -755,6 +767,12 @@
 	return new_list;
 }
 
+struct ast_variable *ast_variable_list_from_string(const char *input, const char *item_separator,
+	const char *name_value_separator)
+{
+	return ast_variable_list_from_quoted_string(input, item_separator, name_value_separator, NULL);
+}
+
 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
 {
 	const char *tmp;
diff --git a/main/datastore.c b/main/datastore.c
index f37ee6c..d5adfae 100644
--- a/main/datastore.c
+++ b/main/datastore.c
@@ -69,6 +69,10 @@
 {
 	int res = 0;
 
+	if (!datastore) {
+		return 0;
+	}
+
 	/* Using the destroy function (if present) destroy the data */
 	if (datastore->info->destroy != NULL && datastore->data != NULL) {
 		datastore->info->destroy(datastore->data);
diff --git a/main/pbx_variables.c b/main/pbx_variables.c
index 1f78a59..45b7ca0 100644
--- a/main/pbx_variables.c
+++ b/main/pbx_variables.c
@@ -394,7 +394,9 @@
 	return ret;
 }
 
-void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used)
+void ast_str_substitute_variables_full2(struct ast_str **buf, ssize_t maxlen,
+	struct ast_channel *c, struct varshead *headp, const char *templ,
+	size_t *used, int use_both)
 {
 	/* Substitutes variables into buf, based on string templ */
 	const char *whereweare;
@@ -501,7 +503,8 @@
 
 			/* Store variable name expression to lookup. */
 			ast_str_set_substr(&substr1, 0, vars, len);
-			ast_debug(5, "Evaluating '%s' (from '%s' len %d)\n", ast_str_buffer(substr1), vars, len);
+			ast_debug(5, "Evaluating '%s' (from '%s' len %d)\n",
+				ast_str_buffer(substr1), vars, len);
 
 			/* Substitute if necessary */
 			if (needsub) {
@@ -511,7 +514,8 @@
 						continue;
 					}
 				}
-				ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), NULL);
+				ast_str_substitute_variables_full2(&substr2, 0, c, headp,
+					ast_str_buffer(substr1), NULL, use_both);
 				finalvars = ast_str_buffer(substr2);
 			} else {
 				finalvars = ast_str_buffer(substr1);
@@ -520,31 +524,48 @@
 			parse_variable_name(finalvars, &offset, &offset2, &isfunction);
 			if (isfunction) {
 				/* Evaluate function */
-				if (c || !headp) {
+				res = -1;
+				if (c) {
 					res = ast_func_read2(c, finalvars, &substr3, 0);
-				} else {
+					ast_debug(2, "Function %s result is '%s' from channel\n",
+						finalvars, res ? "" : ast_str_buffer(substr3));
+				}
+				if (!c || (c && res < 0 && use_both)) {
 					struct varshead old;
 					struct ast_channel *bogus;
 
 					bogus = ast_dummy_channel_alloc();
 					if (bogus) {
 						old = *ast_channel_varshead(bogus);
-						*ast_channel_varshead(bogus) = *headp;
+						if (headp) {
+							*ast_channel_varshead(bogus) = *headp;
+						}
 						res = ast_func_read2(bogus, finalvars, &substr3, 0);
 						/* Don't deallocate the varshead that was passed in */
-						*ast_channel_varshead(bogus) = old;
+						if (headp) {
+							*ast_channel_varshead(bogus) = old;
+						}
 						ast_channel_unref(bogus);
 					} else {
 						ast_log(LOG_ERROR, "Unable to allocate bogus channel for function value substitution.\n");
 						res = -1;
 					}
+					ast_debug(2, "Function %s result is '%s' from headp\n",
+						finalvars, res ? "" : ast_str_buffer(substr3));
 				}
-				ast_debug(2, "Function %s result is '%s'\n",
-					finalvars, res ? "" : ast_str_buffer(substr3));
 			} else {
-				/* Retrieve variable value */
-				ast_str_retrieve_variable(&substr3, 0, c, headp, finalvars);
-				res = 0;
+				const char *result;
+				if (c) {
+					result = ast_str_retrieve_variable(&substr3, 0, c, NULL, finalvars);
+					ast_debug(2, "Variable %s result is '%s' from channel\n",
+						finalvars, S_OR(result, ""));
+				}
+				if (!c || (c && !result && use_both)) {
+					result = ast_str_retrieve_variable(&substr3, 0, NULL, headp, finalvars);
+					ast_debug(2, "Variable %s result is '%s' from headp\n",
+						finalvars, S_OR(result, ""));
+				}
+				res = (result ? 0 : -1);
 			}
 			if (!res) {
 				ast_str_substring(substr3, offset, offset2);
@@ -596,7 +617,8 @@
 						continue;
 					}
 				}
-				ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), NULL);
+				ast_str_substitute_variables_full2(&substr2, 0, c, headp,
+					ast_str_buffer(substr1), NULL, use_both);
 				finalvars = ast_str_buffer(substr2);
 			} else {
 				finalvars = ast_str_buffer(substr1);
@@ -616,6 +638,12 @@
 	ast_free(substr3);
 }
 
+void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen,
+	struct ast_channel *chan, struct varshead *headp, const char *templ, size_t *used)
+{
+	ast_str_substitute_variables_full2(buf, maxlen, chan, headp, templ, used, 0);
+}
+
 void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
 {
 	ast_str_substitute_variables_full(buf, maxlen, chan, NULL, templ, NULL);
diff --git a/main/utils.c b/main/utils.c
index 29676fa..7d1d6bd 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -1859,6 +1859,67 @@
 	return st;
 }
 
+char *ast_strsep_quoted(char **iss, const char sep, const char quote, uint32_t flags)
+{
+	char *st = *iss;
+	char *is;
+	int inquote = 0;
+	int found = 0;
+	char stack[8];
+	const char qstr[] = { quote };
+
+	if (ast_strlen_zero(st)) {
+		return NULL;
+	}
+
+	memset(stack, 0, sizeof(stack));
+
+	for(is = st; *is; is++) {
+		if (*is == '\\') {
+			if (*++is != '\0') {
+				is++;
+			} else {
+				break;
+			}
+		}
+
+		if (*is == quote) {
+			if (*is == stack[inquote]) {
+				stack[inquote--] = '\0';
+			} else {
+				if (++inquote >= sizeof(stack)) {
+					return NULL;
+				}
+				stack[inquote] = *is;
+			}
+		}
+
+		if (*is == sep && !inquote) {
+			*is = '\0';
+			found = 1;
+			*iss = is + 1;
+			break;
+		}
+	}
+	if (!found) {
+		*iss = NULL;
+	}
+
+	if (flags & AST_STRSEP_STRIP) {
+		st = ast_strip_quoted(st, qstr, qstr);
+	}
+
+	if (flags & AST_STRSEP_TRIM) {
+		st = ast_strip(st);
+	}
+
+	if (flags & AST_STRSEP_UNESCAPE) {
+		ast_unescape_quoted(st);
+	}
+
+	return st;
+}
+
 char *ast_unescape_semicolon(char *s)
 {
 	char *e;
diff --git a/main/xml.c b/main/xml.c
index 987f125..d244e4e 100644
--- a/main/xml.c
+++ b/main/xml.c
@@ -361,6 +361,11 @@
 	return xmlDocDump(output, (xmlDocPtr)doc);
 }
 
+void ast_xml_doc_dump_memory(struct ast_xml_doc *doc, char **buffer, int *length)
+{
+	xmlDocDumpFormatMemory((xmlDocPtr)doc, (xmlChar **)buffer, length, 1);
+}
+
 const char *ast_xml_node_get_name(struct ast_xml_node *node)
 {
 	return (const char *) ((xmlNode *) node)->name;
diff --git a/tests/test_config.c b/tests/test_config.c
index 1a0ddaf..166879a 100644
--- a/tests/test_config.c
+++ b/tests/test_config.c
@@ -1952,7 +1952,7 @@
 
 	switch (cmd) {
 	case TEST_INIT:
-		info->name = "variable_list_from_string";
+		info->name = "variable_list_from_quoted_string";
 		info->category = "/main/config/";
 		info->summary = "Test parsing a string into a variable list";
 		info->description =	info->summary;
@@ -1962,7 +1962,7 @@
 	}
 
 	parse_string = "abc = 'def', ghi = 'j,kl', mno='pq=r', stu = 'vwx=\"yz\", ABC = \"DEF\"'";
-	list = ast_variable_list_from_string(parse_string, ",", "=");
+	list = ast_variable_list_from_quoted_string(parse_string, ",", "=", "'");
 	ast_test_validate(test, list != NULL);
 	str = ast_variable_list_join(list, "|", "^", "@", NULL);
 

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/18691
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: 16
Gerrit-Change-Id: I5dcefed2f5f93a109e8b489e18d80d42e45244ec
Gerrit-Change-Number: 18691
Gerrit-PatchSet: 1
Gerrit-Owner: George Joseph <gjoseph at digium.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20220628/3e30c61e/attachment-0001.html>


More information about the asterisk-code-review mailing list