<p>George Joseph <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/18795">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Kevin Harwell: Looks good to me, but someone else must approve
  George Joseph: Looks good to me, approved; Approved for Submit

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">xml.c, config,c:  Add stylesheets and variable list string parsing<br><br>Added functions to open, close, and apply XML Stylesheets<br>to XML documents.  Although the presence of libxslt was already<br>being checked by configure, it was only happening if xmldoc was<br>enabled.  Now it's checked regardless.<br><br>Added ability to parse a string consisting of comma separated<br>name/value pairs into an ast_variable list.  The reverse of<br>ast_variable_list_join().<br><br>Change-Id: I1e1d149be22165a1fb8e88e2903a36bba1a6cf2e<br>---<br>M build_tools/menuselect-deps.in<br>M configure<br>M configure.ac<br>M include/asterisk/config.h<br>M include/asterisk/xml.h<br>M main/config.c<br>M main/xml.c<br>M tests/test_config.c<br>8 files changed, 364 insertions(+), 10 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/build_tools/menuselect-deps.in b/build_tools/menuselect-deps.in</span><br><span>index 161c67b..f66d7bd 100644</span><br><span>--- a/build_tools/menuselect-deps.in</span><br><span>+++ b/build_tools/menuselect-deps.in</span><br><span>@@ -32,6 +32,7 @@</span><br><span> LDAP=@PBX_LDAP@</span><br><span> LIBEDIT=@PBX_LIBEDIT@</span><br><span> LIBXML2=@PBX_LIBXML2@</span><br><span style="color: hsl(120, 100%, 40%);">+LIBXSLT=@PBX_LIBXSLT@</span><br><span> XMLSTARLET=@PBX_XMLSTARLET@</span><br><span> BASH=@PBX_BASH@</span><br><span> LUA=@PBX_LUA@</span><br><span>diff --git a/configure b/configure</span><br><span>index e227e22..1705820 100755</span><br><span>--- a/configure</span><br><span>+++ b/configure</span><br><span>@@ -15022,6 +15022,8 @@</span><br><span> </span><br><span> $as_echo "#define AST_XML_DOCS 1" >>confdefs.h</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+fi</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> </span><br><span> if test "x${PBX_LIBXSLT}" != "x1" -a "${USE_LIBXSLT}" != "no"; then</span><br><span>    pbxlibdir=""</span><br><span>@@ -15215,8 +15217,6 @@</span><br><span> </span><br><span> </span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-fi</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> # Check whether --enable-permanent-dlopen was given.</span><br><span> if test "${enable_permanent_dlopen+set}" = set; then :</span><br><span>   enableval=$enable_permanent_dlopen; case "${enableval}" in</span><br><span>@@ -23863,8 +23863,8 @@</span><br><span>    if test "x${PBX_NETSNMP}" != "x1" -a "${USE_NETSNMP}" != "no"; then</span><br><span> </span><br><span> pkg_failed=no</span><br><span style="color: hsl(0, 100%, 40%);">-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for netsnmp-agent" >&5</span><br><span style="color: hsl(0, 100%, 40%);">-$as_echo_n "checking for netsnmp-agent... " >&6; }</span><br><span style="color: hsl(120, 100%, 40%);">+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for NETSNMP" >&5</span><br><span style="color: hsl(120, 100%, 40%);">+$as_echo_n "checking for NETSNMP... " >&6; }</span><br><span> </span><br><span> if test -n "$NETSNMP_CFLAGS"; then</span><br><span>     pkg_cv_NETSNMP_CFLAGS="$NETSNMP_CFLAGS"</span><br><span>@@ -23904,7 +23904,7 @@</span><br><span> </span><br><span> </span><br><span> if test $pkg_failed = yes; then</span><br><span style="color: hsl(0, 100%, 40%);">-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5</span><br><span style="color: hsl(120, 100%, 40%);">+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5</span><br><span> $as_echo "no" >&6; }</span><br><span> </span><br><span> if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then</span><br><span>@@ -23925,7 +23925,7 @@</span><br><span> </span><br><span> </span><br><span> elif test $pkg_failed = untried; then</span><br><span style="color: hsl(0, 100%, 40%);">-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5</span><br><span style="color: hsl(120, 100%, 40%);">+           { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5</span><br><span> $as_echo "no" >&6; }</span><br><span> </span><br><span>             PBX_NETSNMP=0</span><br><span>diff --git a/configure.ac b/configure.ac</span><br><span>index 6071ef4..9fcea18 100644</span><br><span>--- a/configure.ac</span><br><span>+++ b/configure.ac</span><br><span>@@ -768,11 +768,11 @@</span><br><span> AC_SUBST([DISABLE_XMLDOC])</span><br><span> if test "${DISABLE_XMLDOC}" != "yes"; then</span><br><span>         AC_DEFINE([AST_XML_DOCS], 1, [Define to enable XML documentation.])</span><br><span style="color: hsl(0, 100%, 40%);">-        AST_EXT_LIB_CHECK([LIBXSLT], [xslt], [xsltLoadStylesheetPI], [libxslt/xsltInternals.h], [${LIBXML2_LIB}], [${LIBXML2_INCLUDE}])</span><br><span style="color: hsl(0, 100%, 40%);">-        AST_EXT_LIB_CHECK([LIBXSLT_CLEANUP], [xslt], [xsltCleanupGlobals], [libxslt/xsltInternals.h], [${LIBXML2_LIB}], [${LIBXML2_INCLUDE}])</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> fi</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+AST_EXT_LIB_CHECK([LIBXSLT], [xslt], [xsltLoadStylesheetPI], [libxslt/xsltInternals.h], [${LIBXML2_LIB}], [${LIBXML2_INCLUDE}])</span><br><span style="color: hsl(120, 100%, 40%);">+AST_EXT_LIB_CHECK([LIBXSLT_CLEANUP], [xslt], [xsltCleanupGlobals], [libxslt/xsltInternals.h], [${LIBXML2_LIB}], [${LIBXML2_INCLUDE}])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> AC_ARG_ENABLE([permanent-dlopen],</span><br><span>         [AS_HELP_STRING([--enable-permanent-dlopen],</span><br><span>                 [Enable when your libc has a permanent dlopen like musl])],</span><br><span>diff --git a/include/asterisk/config.h b/include/asterisk/config.h</span><br><span>index c66b10f..50b358e 100644</span><br><span>--- a/include/asterisk/config.h</span><br><span>+++ b/include/asterisk/config.h</span><br><span>@@ -1005,6 +1005,23 @@</span><br><span>        const char *name_value_separator, const char *quote_char, struct ast_str **str);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Parse a string into an ast_variable list.  The reverse of ast_variable_list_join</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param input                The name-value pair string to parse.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param item_separator       The string used to separate the list items.</span><br><span style="color: hsl(120, 100%, 40%);">+ *                             Only the first character in the string will be used.</span><br><span style="color: hsl(120, 100%, 40%);">+ *                             If NULL, "," will be used.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name_value_separator The string used to separate each item's name and value.</span><br><span style="color: hsl(120, 100%, 40%);">+ *                             Only the first character in the string will be used.</span><br><span style="color: hsl(120, 100%, 40%);">+ *                             If NULL, "=" will be used.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval A pointer to a list of ast_variables.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL if there was an error or no variables could be parsed.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_variable *ast_variable_list_from_string(const char *input, const char *item_separator,</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *name_value_separator);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \brief Update variable value within a config</span><br><span>  *</span><br><span>  * \param category Category element within the config</span><br><span>diff --git a/include/asterisk/xml.h b/include/asterisk/xml.h</span><br><span>index 73006e7..7b30ed8 100644</span><br><span>--- a/include/asterisk/xml.h</span><br><span>+++ b/include/asterisk/xml.h</span><br><span>@@ -21,9 +21,12 @@</span><br><span>  * \brief Asterisk XML abstraction layer</span><br><span>  */</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/vector.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct ast_xml_node;</span><br><span> struct ast_xml_doc;</span><br><span> struct ast_xml_xpath_results;</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_xslt_doc;</span><br><span> </span><br><span> /*!</span><br><span>  * \brief Initialize the XML library implementation.</span><br><span>@@ -184,6 +187,19 @@</span><br><span>  */</span><br><span> struct ast_xml_node *ast_xml_find_element(struct ast_xml_node *root_node, const char *name, const char *attrname, const char *attrvalue);</span><br><span> struct ast_xml_ns *ast_xml_find_namespace(struct ast_xml_doc *doc, struct ast_xml_node *node, const char *ns_name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Get the prefix of a namespace.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param ns The namespace</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return The prefix of the namespace.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_xml_get_ns_prefix(struct ast_xml_ns *ns);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Get the href of a namespace.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param ns The namespace</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return The href of the namespace.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span> const char *ast_xml_get_ns_href(struct ast_xml_ns *ns);</span><br><span> </span><br><span> /*!</span><br><span>@@ -260,6 +276,15 @@</span><br><span> struct ast_xml_node *ast_xml_xpath_get_first_result(struct ast_xml_xpath_results *results);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Return a specific result node of an XPath query</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param results The XPath results object to get the result from</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param n The index of the result to get</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return The nth result in the XPath object on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_xml_node *ast_xml_xpath_get_result(struct ast_xml_xpath_results *results, int n);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span>  * \brief Execute an XPath query on an XML document</span><br><span>  * \param doc The XML document to query</span><br><span>  * \param xpath_str The XPath query string to execute on the document</span><br><span>@@ -270,4 +295,80 @@</span><br><span>  */</span><br><span> struct ast_xml_xpath_results *ast_xml_query(struct ast_xml_doc *doc, const char *xpath_str);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Namespace definition</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_xml_namespace_def {</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *prefix;</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *href;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_VECTOR(ast_xml_namespace_def_vector, struct ast_xml_namespace_def);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Execute an XPath query on an XML document with namespaces</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param doc XML document to query</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param xpath_str The XPath query string to execute on the document</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param namespaces A vector of ast_xml_namespace structures (not pointers)</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return An object containing the results of the XPath query on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_xml_xpath_results *ast_xml_query_with_namespaces(struct ast_xml_doc *doc, const char *xpath_str,</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_xml_namespace_def_vector *namespaces);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef HAVE_LIBXSLT</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Open an XSLT document that resides in memory.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param buffer The address where the stylesheet is stored</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param size   The number of bytes in the stylesheet</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return The stylesheet document.  Must be closed with ast_xslt_close().</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_xslt_doc *ast_xslt_read_memory(char *buffer, size_t size);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Open an XSLT document.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param filename stylesheet path.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return The stylesheet document.  Must be closed with ast_xslt_close().</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_xslt_doc *ast_xslt_open(char *filename);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Close a stylesheet document and free its resources.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param xslt XSLT stylesheet to close</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_xslt_close(struct ast_xslt_doc *xslt);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Apply an XSLT stylesheet to an XML document</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param xslt    XSLT stylesheet to apply.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param xml     XML document the stylesheet will be applied to.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param params  An array of name value pairs to pass as parameters</span><br><span style="color: hsl(120, 100%, 40%);">+ *                The array must terminate with a NULL sentinel.</span><br><span style="color: hsl(120, 100%, 40%);">+ *                Example:  { "name1", "value1", "name2", "value2", NULL }</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return A pointer to the result document which must be freed with ast_xml_close()</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_xml_doc *ast_xslt_apply(struct ast_xslt_doc *xslt, struct ast_xml_doc *doc, const char **params);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Save the results of applying a stylesheet to a string</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param buffer[out]  A pointer to a char * to receive the address of the result string.</span><br><span style="color: hsl(120, 100%, 40%);">+ *                     The buffer must be freed with ast_xml_free_text().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param length[out]  A pointer to an int to receive the result string length.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param result       The result document from ast_xslt_apply.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param xslt         The stylesheet that was applied.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return 0 on success, any other value on failure.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_xslt_save_result_to_string(char **buffer, int *length, struct ast_xml_doc *result,</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_xslt_doc *xslt);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* HAVE_LIBXSLT */</span><br><span> #endif /* _ASTERISK_XML_H */</span><br><span>diff --git a/main/config.c b/main/config.c</span><br><span>index b74f612..da893b6 100644</span><br><span>--- a/main/config.c</span><br><span>+++ b/main/config.c</span><br><span>@@ -724,6 +724,39 @@</span><br><span>     return local_str;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_variable *ast_variable_list_from_string(const char *input, const char *item_separator,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *name_value_separator)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  char item_sep;</span><br><span style="color: hsl(120, 100%, 40%);">+        char nv_sep;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ast_variable *new_list = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_variable *new_var = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  char *item_string;</span><br><span style="color: hsl(120, 100%, 40%);">+    char *item;</span><br><span style="color: hsl(120, 100%, 40%);">+   char *item_name;</span><br><span style="color: hsl(120, 100%, 40%);">+      char *item_value;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_strlen_zero(input)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   item_sep = ast_strlen_zero(item_separator) ? ',' : item_separator[0];</span><br><span style="color: hsl(120, 100%, 40%);">+ nv_sep = ast_strlen_zero(name_value_separator) ? '=' : name_value_separator[0];</span><br><span style="color: hsl(120, 100%, 40%);">+       item_string = ast_strip(ast_strdupa(input));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        while ((item = ast_strsep(&item_string, item_sep, AST_STRSEP_ALL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+             item_name = ast_strsep(&item, nv_sep, AST_STRSEP_ALL);</span><br><span style="color: hsl(120, 100%, 40%);">+            item_value = ast_strsep(&item, nv_sep, AST_STRSEP_ALL);</span><br><span style="color: hsl(120, 100%, 40%);">+           new_var = ast_variable_new(item_name, item_value, "");</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!new_var) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       ast_variables_destroy(new_list);</span><br><span style="color: hsl(120, 100%, 40%);">+                      return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_variable_list_append(&new_list, new_var);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return new_list;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)</span><br><span> {</span><br><span>        const char *tmp;</span><br><span>diff --git a/main/xml.c b/main/xml.c</span><br><span>index 88c9edf..987f125 100644</span><br><span>--- a/main/xml.c</span><br><span>+++ b/main/xml.c</span><br><span>@@ -36,25 +36,33 @@</span><br><span> #include <libxml/tree.h></span><br><span> #include <libxml/xinclude.h></span><br><span> #include <libxml/xpath.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <libxml/xpathInternals.h></span><br><span> /* libxml2 ast_xml implementation. */</span><br><span> #ifdef HAVE_LIBXSLT</span><br><span>     #include <libxslt/xsltInternals.h></span><br><span>     #include <libxslt/transform.h></span><br><span style="color: hsl(120, 100%, 40%);">+  #include <libxslt/xsltutils.h></span><br><span> #endif /* HAVE_LIBXSLT */</span><br><span> </span><br><span> </span><br><span> int ast_xml_init(void)</span><br><span> {</span><br><span>     LIBXML_TEST_VERSION</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef HAVE_LIBXSLT</span><br><span style="color: hsl(120, 100%, 40%);">+      xsltInit();</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span>  return 0;</span><br><span> }</span><br><span> </span><br><span> int ast_xml_finish(void)</span><br><span> {</span><br><span>  xmlCleanupParser();</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef HAVE_LIBXSLT</span><br><span> #ifdef HAVE_LIBXSLT_CLEANUP</span><br><span>      xsltCleanupGlobals();</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span style="color: hsl(120, 100%, 40%);">+  xsltUninit();</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> #endif</span><br><span> </span><br><span>  return 0;</span><br><span>@@ -68,6 +76,8 @@</span><br><span>                return NULL;</span><br><span>         }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ xmlSubstituteEntitiesDefault(1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>   doc = xmlReadFile(filename, NULL, XML_PARSE_RECOVER);</span><br><span>        if (!doc) {</span><br><span>          return NULL;</span><br><span>@@ -309,6 +319,11 @@</span><br><span>  return (struct ast_xml_ns *) ns;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_xml_get_ns_prefix(struct ast_xml_ns *ns)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        return (const char *) ((xmlNsPtr) ns)->prefix;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> const char *ast_xml_get_ns_href(struct ast_xml_ns *ns)</span><br><span> {</span><br><span>        return (const char *) ((xmlNsPtr) ns)->href;</span><br><span>@@ -376,13 +391,24 @@</span><br><span>      return (struct ast_xml_node *) ((xmlXPathObjectPtr) results)->nodesetval->nodeTab[0];</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_xml_node *ast_xml_xpath_get_result(struct ast_xml_xpath_results *results, int i)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  return (struct ast_xml_node *) ((xmlXPathObjectPtr) results)->nodesetval->nodeTab[i];</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> void ast_xml_xpath_results_free(struct ast_xml_xpath_results *results)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!results) {</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span>    xmlXPathFreeObject((xmlXPathObjectPtr) results);</span><br><span> }</span><br><span> </span><br><span> int ast_xml_xpath_num_results(struct ast_xml_xpath_results *results)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!results) {</span><br><span style="color: hsl(120, 100%, 40%);">+               return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span>    return ((xmlXPathObjectPtr) results)->nodesetval->nodeNr;</span><br><span> }</span><br><span> </span><br><span>@@ -408,4 +434,149 @@</span><br><span>     return (struct ast_xml_xpath_results *) result;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_xml_xpath_results *ast_xml_query_with_namespaces(struct ast_xml_doc *doc, const char *xpath_str,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_xml_namespace_def_vector *namespaces)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   xmlXPathContextPtr context;</span><br><span style="color: hsl(120, 100%, 40%);">+   xmlXPathObjectPtr result;</span><br><span style="color: hsl(120, 100%, 40%);">+     int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!(context = xmlXPathNewContext((xmlDoc *) doc))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_log(LOG_ERROR, "Could not create XPath context!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = 0; i < AST_VECTOR_SIZE(namespaces); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                struct ast_xml_namespace_def ns = AST_VECTOR_GET(namespaces, i);</span><br><span style="color: hsl(120, 100%, 40%);">+              if (xmlXPathRegisterNs(context, (xmlChar *)ns.prefix,</span><br><span style="color: hsl(120, 100%, 40%);">+                 (xmlChar *)ns.href) != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   xmlXPathFreeContext(context);</span><br><span style="color: hsl(120, 100%, 40%);">+                 ast_log(LOG_ERROR, "Could not register namespace %s:%s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                          ns.prefix, ns.href);</span><br><span style="color: hsl(120, 100%, 40%);">+                  return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   result = xmlXPathEvalExpression((xmlChar *) xpath_str, context);</span><br><span style="color: hsl(120, 100%, 40%);">+      xmlXPathFreeContext(context);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!result) {</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_log(LOG_WARNING, "Error for query: %s\n", xpath_str);</span><br><span style="color: hsl(120, 100%, 40%);">+           return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          xmlXPathFreeObject(result);</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_debug(5, "No results for query: %s\n", xpath_str);</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     return (struct ast_xml_xpath_results *) result;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef HAVE_LIBXSLT</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_xslt_doc *ast_xslt_open(char *filename)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      xsltStylesheet *xslt;</span><br><span style="color: hsl(120, 100%, 40%);">+ xmlDoc *xml;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        xmlSubstituteEntitiesDefault(1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    xml = xmlReadFile(filename, NULL, XML_PARSE_RECOVER);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!xml) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (xmlXIncludeProcess(xml) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+         xmlFreeDoc(xml);</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     xmlXPathOrderDocElems(xml);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(xslt = xsltParseStylesheetDoc(xml))) {</span><br><span style="color: hsl(120, 100%, 40%);">+          xmlFreeDoc(xml);</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return (struct ast_xslt_doc *) xslt;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_xslt_doc *ast_xslt_read_memory(char *buffer, size_t size)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    xsltStylesheet *xslt;</span><br><span style="color: hsl(120, 100%, 40%);">+ xmlDoc *doc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!buffer) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   xmlSubstituteEntitiesDefault(1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!(doc = xmlParseMemory(buffer, (int) size))) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (xmlXIncludeProcess(doc) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+         xmlFreeDoc(doc);</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!(xslt = xsltParseStylesheetDoc(doc))) {</span><br><span style="color: hsl(120, 100%, 40%);">+          xmlFreeDoc(doc);</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return (struct ast_xslt_doc *) xslt;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_xslt_close(struct ast_xslt_doc *axslt)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!axslt) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   xsltFreeStylesheet((xsltStylesheet *) axslt);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_xml_doc *ast_xslt_apply(struct ast_xslt_doc *axslt, struct ast_xml_doc *axml, const char **params)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  xsltStylesheet *xslt = (xsltStylesheet *)axslt;</span><br><span style="color: hsl(120, 100%, 40%);">+       xmlDoc *xml = (xmlDoc *)axml;</span><br><span style="color: hsl(120, 100%, 40%);">+ xsltTransformContextPtr ctxt;</span><br><span style="color: hsl(120, 100%, 40%);">+ xmlNs *ns;</span><br><span style="color: hsl(120, 100%, 40%);">+    xmlDoc *res;</span><br><span style="color: hsl(120, 100%, 40%);">+  int options = XSLT_PARSE_OPTIONS;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /*</span><br><span style="color: hsl(120, 100%, 40%);">+     * Normally we could just call xsltApplyStylesheet() without creating</span><br><span style="color: hsl(120, 100%, 40%);">+  * our own transform context but we need to pass parameters to it</span><br><span style="color: hsl(120, 100%, 40%);">+      * that have namespace prefixes and that's not supported.  Instead</span><br><span style="color: hsl(120, 100%, 40%);">+         * we have to create a transform context, iterate over the namespace</span><br><span style="color: hsl(120, 100%, 40%);">+   * declarations in the stylesheet (not the incoming xml document),</span><br><span style="color: hsl(120, 100%, 40%);">+     * and add them to the transform context's xpath context.</span><br><span style="color: hsl(120, 100%, 40%);">+  *</span><br><span style="color: hsl(120, 100%, 40%);">+     * The alternative would be to pass the parameters with namespaces</span><br><span style="color: hsl(120, 100%, 40%);">+     * as text strings but that's not intuitive and results in much</span><br><span style="color: hsl(120, 100%, 40%);">+    * slower performance than adding the namespaces here.</span><br><span style="color: hsl(120, 100%, 40%);">+         */</span><br><span style="color: hsl(120, 100%, 40%);">+   ctxt = xsltNewTransformContext(xslt, xml);</span><br><span style="color: hsl(120, 100%, 40%);">+    xsltSetCtxtParseOptions(ctxt, options);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     for (ns = xslt->doc->children->nsDef; ns; ns = ns->next) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (xmlXPathRegisterNs(ctxt->xpathCtxt, ns->prefix, ns->href) != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        xmlXPathFreeContext(ctxt->xpathCtxt);</span><br><span style="color: hsl(120, 100%, 40%);">+                      xsltFreeTransformContext(ctxt);</span><br><span style="color: hsl(120, 100%, 40%);">+                       return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   res = xsltApplyStylesheetUser(xslt, xml, params, NULL, NULL, ctxt);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return (struct ast_xml_doc *)res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_xslt_save_result_to_string(char **buffer, int *length, struct ast_xml_doc *result,</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_xslt_doc *axslt)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        return xsltSaveResultToString((xmlChar **)buffer, length, (xmlDoc *)result, (xsltStylesheet *)axslt);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* defined(HAVE_LIBXSLT) */</span><br><span> #endif /* defined(HAVE_LIBXML2) */</span><br><span>diff --git a/tests/test_config.c b/tests/test_config.c</span><br><span>index 5b44b44..1a0ddaf 100644</span><br><span>--- a/tests/test_config.c</span><br><span>+++ b/tests/test_config.c</span><br><span>@@ -1943,6 +1943,35 @@</span><br><span> </span><br><span>   return AST_TEST_PASS;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(variable_list_from_string)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   RAII_VAR(struct ast_variable *, list, NULL, ast_variables_destroy);</span><br><span style="color: hsl(120, 100%, 40%);">+   RAII_VAR(struct ast_str *, str, NULL, ast_free);</span><br><span style="color: hsl(120, 100%, 40%);">+      char *parse_string;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = "variable_list_from_string";</span><br><span style="color: hsl(120, 100%, 40%);">+                info->category = "/main/config/";</span><br><span style="color: hsl(120, 100%, 40%);">+                info->summary = "Test parsing a string into a variable list";</span><br><span style="color: hsl(120, 100%, 40%);">+            info->description =  info->summary;</span><br><span style="color: hsl(120, 100%, 40%);">+             return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   parse_string = "abc = 'def', ghi = 'j,kl', mno='pq=r', stu = 'vwx=\"yz\", ABC = \"DEF\"'";</span><br><span style="color: hsl(120, 100%, 40%);">+      list = ast_variable_list_from_string(parse_string, ",", "=");</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_test_validate(test, list != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+        str = ast_variable_list_join(list, "|", "^", "@", NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_test_validate(test,</span><br><span style="color: hsl(120, 100%, 40%);">+               strcmp(ast_str_buffer(str), "abc^@def@|ghi^@j,kl@|mno^@pq=r@|stu^@vwx=\"yz\", ABC = \"DEF\"@") == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int unload_module(void)</span><br><span> {</span><br><span>    AST_TEST_UNREGISTER(config_save);</span><br><span>@@ -1956,6 +1985,7 @@</span><br><span>    AST_TEST_UNREGISTER(config_dialplan_function);</span><br><span>       AST_TEST_UNREGISTER(variable_lists_match);</span><br><span>   AST_TEST_UNREGISTER(variable_list_join_replace);</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_TEST_UNREGISTER(variable_list_from_string);</span><br><span>      return 0;</span><br><span> }</span><br><span> </span><br><span>@@ -1972,6 +2002,7 @@</span><br><span>   AST_TEST_REGISTER(config_dialplan_function);</span><br><span>         AST_TEST_REGISTER(variable_lists_match);</span><br><span>     AST_TEST_REGISTER(variable_list_join_replace);</span><br><span style="color: hsl(120, 100%, 40%);">+        AST_TEST_REGISTER(variable_list_from_string);</span><br><span>        return AST_MODULE_LOAD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span></span><br></pre><div style="white-space:pre-wrap"></div><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/18795">change 18795</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/18795"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: certified/18.9 </div>
<div style="display:none"> Gerrit-Change-Id: I1e1d149be22165a1fb8e88e2903a36bba1a6cf2e </div>
<div style="display:none"> Gerrit-Change-Number: 18795 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>