[Asterisk-code-review] res_geolocation: eprofile, parsing, tests and more (asterisk[development/16/geolocation])
George Joseph
asteriskteam at digium.com
Fri Mar 25 06:33:08 CDT 2022
George Joseph has submitted this change. ( https://gerrit.asterisk.org/c/asterisk/+/18127 )
Change subject: res_geolocation: eprofile, parsing, tests and more
......................................................................
res_geolocation: eprofile, parsing, tests and more
There's a lot of stuff in this commit so hold on to
your knickers.
* Added ability to embed xml and xslt documents into a module.
It's actually quite simnple and works for gcc and clang.
See Makefile.rules, res/Makefile and geoloc_eprofile for
more info.
* Fixed an issue with ast_variable_list_from_string where
quotes weren't being handled correctly. This required
the addition of ast_strsep_strict() to utils.c
* Renamed some things...
pidf_lo_section was renamed to just pidf_element.
The "effective_profile" API names were shortened to
"eprofile".
* Added a ast_xml_find_child_element() convenience macro.
* Added pidf_to_eprofile.xslt that condenses pidf-lo xml
documents to a simpler format that's common to both GML
and civicAddress formats. This cut quite a bit of custom
parsing code.
* Effective profiles can now be created directly from a pidf
xml document or a URI.
* Created unit tests for effective profile creation.
Change-Id: I50b66bd041b2a62ab329406f20dbaeef1fa68fc1
---
M Makefile.rules
M configs/samples/geolocation.conf.sample
M include/asterisk/config.h
M include/asterisk/res_geolocation.h
M include/asterisk/strings.h
M include/asterisk/xml.h
M main/config.c
M main/utils.c
M res/Makefile
M res/res_geolocation.c
M res/res_geolocation/geoloc_channel.c
A res/res_geolocation/geoloc_common.c
M res/res_geolocation/geoloc_config.c
M res/res_geolocation/geoloc_doc.xml
A res/res_geolocation/geoloc_eprofile.c
M res/res_geolocation/geoloc_private.h
A res/res_geolocation/pidf_lo_test.xml
A res/res_geolocation/pidf_to_eprofile.xslt
M tests/test_config.c
19 files changed, 1,241 insertions(+), 132 deletions(-)
Approvals:
George Joseph: Looks good to me, approved; Approved for Submit
diff --git a/Makefile.rules b/Makefile.rules
index 934e44a..e6b6589 100644
--- a/Makefile.rules
+++ b/Makefile.rules
@@ -204,4 +204,19 @@
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
$(CMD_PREFIX) $(CXX) -o $@ $(PTHREAD_CFLAGS) $(_ASTLDFLAGS) $^ $(CXX_LIBS) $(ASTLDFLAGS)
+# These CC commands just create an object file with the input file embedded in it.
+# It can be access from code as follows:
+# If your input file is named abc_def.xml...
+#
+# extern const uint8_t _binary_abc_def_xml_start[];
+# extern const uint8_t _binary_abc_def_xml_end[];
+# extern const size_t _binary_abc_def_xml_size;
+%.o: %.xml
+ $(ECHO_PREFIX) echo " [LD] $^ -> $@"
+ $(CMD_PREFIX) $(CC) -g -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
+
+%.o: %.xslt
+ $(ECHO_PREFIX) echo " [LD] $^ -> $@"
+ $(CMD_PREFIX) $(CC) -g -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
+
dist-clean:: clean
diff --git a/configs/samples/geolocation.conf.sample b/configs/samples/geolocation.conf.sample
index f8909ec..7cf0bb5 100644
--- a/configs/samples/geolocation.conf.sample
+++ b/configs/samples/geolocation.conf.sample
@@ -8,9 +8,9 @@
5. Reference
--;
-;-----------------------------------------------------------------------
+;--=====================================================================
Overview
------------------------------------------------------------------------;
+=====================================================================--;
;--
@@ -43,9 +43,9 @@
being sent to an emergency response center or even call failure for
which you are solely responsible.
-;-----------------------------------------------------------------------
+;--=====================================================================
Location Object Description
------------------------------------------------------------------------;
+=====================================================================--;
;-- type -------------------------------------------
Must be "location" to identify this configuration section as a
@@ -224,16 +224,16 @@
---------------------------------------------------;
-;-----------------------------------------------------------------------
+;--=====================================================================
Profile Object Descriptions
------------------------------------------------------------------------;
+=====================================================================--;
;-- type -------------------------------------------
Must be "profile" to identify this configuration section as a
Geolocation Profile object.
-;-- pidf_lo_section -------------------------------
+;-- pidf_element ----------------------------------
If the format is civicAddress or GML, this sets the PIDF element into
which the location information will be placed.
@@ -323,9 +323,9 @@
---------------------------------------------------;
-;---------------------------------------------------
+;--=====================================================================
Examples
----------------------------------------------------;
+=====================================================================--;
; Create a Location Object for a building
[building_1]
@@ -392,9 +392,9 @@
; Geolocation: <https://some.company.com?building=bldg1&number=5678>
-;-------------------------------
------------ REFERENCE ----------
--------------------------------;
+;--=====================================================================
+ REFERENCE
+=====================================================================--;
;--
[RFC4119] A Presence-based GEOPRIV Location Object Format
diff --git a/include/asterisk/config.h b/include/asterisk/config.h
index afac229..a05e126 100644
--- a/include/asterisk/config.h
+++ b/include/asterisk/config.h
@@ -1015,12 +1015,15 @@
* \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_string(const char *input, const char *item_separator,
- const char *name_value_separator);
+ const char *name_value_separator, const char *quote_str);
/*!
* \brief Update variable value within a config
diff --git a/include/asterisk/res_geolocation.h b/include/asterisk/res_geolocation.h
index 6230cf2..7fae4bf 100644
--- a/include/asterisk/res_geolocation.h
+++ b/include/asterisk/res_geolocation.h
@@ -20,12 +20,14 @@
#define INCLUDE_ASTERISK_RES_GEOLOCATION_H_
#include "asterisk/sorcery.h"
+#include "asterisk/config.h"
+#include "asterisk/xml.h"
-enum ast_geoloc_pidf_section {
- AST_PIDF_SECTION_NONE = 0,
- AST_PIDF_SECTION_TUPLE,
- AST_PIDF_SECTION_DEVICE,
- AST_PIDF_SECTION_PERSON
+enum ast_geoloc_pidf_element {
+ AST_PIDF_ELEMENT_NONE = 0,
+ AST_PIDF_ELEMENT_TUPLE,
+ AST_PIDF_ELEMENT_DEVICE,
+ AST_PIDF_ELEMENT_PERSON
};
enum ast_geoloc_format {
@@ -44,6 +46,9 @@
struct ast_geoloc_location {
SORCERY_OBJECT(details);
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(method);
+ );
enum ast_geoloc_format format;
struct ast_variable *location_vars;
};
@@ -53,7 +58,7 @@
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(location_reference);
);
- enum ast_geoloc_pidf_section pidf_section;
+ enum ast_geoloc_pidf_element pidf_element;
enum ast_geoloc_location_disposition location_disposition;
int geolocation_routing;
int send_location;
@@ -66,8 +71,9 @@
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(id);
AST_STRING_FIELD(location_reference);
+ AST_STRING_FIELD(method);
);
- enum ast_geoloc_pidf_section pidf_section;
+ enum ast_geoloc_pidf_element pidf_element;
enum ast_geoloc_location_disposition location_disposition;
int geolocation_routing;
int send_location;
@@ -141,8 +147,72 @@
enum ast_geoloc_validate_result ast_geoloc_gml_validate_varlist(const struct ast_variable *varlist,
const char **result);
-struct ast_datastore *ast_geoloc_datastore_create(const char *profile_name);
+/*!
+ * \brief Create a geoloc datastore from a profile name
+ *
+ * \param profile_name The name of the profile to use.
+ *
+ * \return The datastore.
+ */
+struct ast_datastore *ast_geoloc_datastore_create_from_profile_name(const char *profile_name);
-struct ast_geoloc_effective_profile *ast_geoloc_effective_profile_create(struct ast_geoloc_profile *profile);
+/*!
+ * \brief Create a geoloc datastore from an effective profile.
+ *
+ * \param eprofile The effective profile to use.
+ *
+ * \return The datastore.
+ */
+struct ast_datastore *ast_geoloc_datastore_create_from_eprofile(
+ struct ast_geoloc_effective_profile *eprofile);
+
+/*!
+ * \brief Allocate a new, empty effective profile.
+ *
+ * \param name The profile's name
+ *
+ * \return The effective profile ao2 object.
+ */
+struct ast_geoloc_effective_profile *ast_geoloc_eprofile_alloc(const char *name);
+
+/*!
+ * \brief Allocate a new effective profile from an existing profile.
+ *
+ * \param profile The profile to use.
+ *
+ * \return The effective profile ao2 object.
+ */
+struct ast_geoloc_effective_profile *ast_geoloc_eprofile_create_from_profile(struct ast_geoloc_profile *profile);
+
+/*!
+ * \brief Allocate a new effective profile from an XML PIDF-LO document
+ *
+ * \param pidf_xmldoc The ast_xml_doc to use.
+ * \param reference_string An identifying string to use in error messages.
+ *
+ * \return The effective profile ao2 object.
+ */
+struct ast_geoloc_effective_profile *ast_geoloc_eprofile_create_from_pidf(
+ struct ast_xml_doc *pidf_xmldoc, const char *reference_string);
+
+/*!
+ * \brief Allocate a new effective profile from a URI.
+ *
+ * \param uri The URI to use.
+ * \param reference_string An identifying string to use in error messages.
+ *
+ * \return The effective profile ao2 object.
+ */
+struct ast_geoloc_effective_profile *ast_geoloc_eprofile_create_from_uri(const char *uri,
+ const char *reference_string);
+
+/*!
+ * \brief Refresh the effective profile with any changed info.
+ *
+ * \param eprofile The eprofile to refresh.
+ *
+ * \return 0 on success, any other value on error.
+ */
+int ast_geoloc_eprofile_refresh_location(struct ast_geoloc_effective_profile *eprofile);
#endif /* INCLUDE_ASTERISK_RES_GEOLOCATION_H_ */
diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h
index ab46273..5c14f4a 100644
--- a/include/asterisk/strings.h
+++ b/include/asterisk/strings.h
@@ -308,6 +308,8 @@
*/
char *ast_strsep(char **s, const char sep, uint32_t flags);
+char *ast_strsep_strict(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).
diff --git a/include/asterisk/xml.h b/include/asterisk/xml.h
index 9d43560..5564051 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.
diff --git a/main/config.c b/main/config.c
index 122e7aa..07925bd 100644
--- a/main/config.c
+++ b/main/config.c
@@ -723,10 +723,11 @@
}
struct ast_variable *ast_variable_list_from_string(const char *input, const char *item_separator,
- const char *name_value_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,14 @@
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_strict(&item_string, item_sep, quote, AST_STRSEP_ALL))) {
+ item_name = ast_strsep_strict(&item, nv_sep, quote, AST_STRSEP_ALL);
+
+ item_value = ast_strsep_strict(&item, nv_sep, quote, AST_STRSEP_ALL);
+
new_var = ast_variable_new(item_name, item_value, "");
if (!new_var) {
ast_variables_destroy(new_list);
diff --git a/main/utils.c b/main/utils.c
index 29676fa..38c5b0c 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -1859,6 +1859,67 @@
return st;
}
+char *ast_strsep_strict(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/res/Makefile b/res/Makefile
index a6dd181..6abdd2d 100644
--- a/res/Makefile
+++ b/res/Makefile
@@ -69,9 +69,13 @@
$(call MOD_ADD_C,res_stir_shaken,$(wildcard res_stir_shaken/*.c))
$(call MOD_ADD_C,res_geolocation,$(wildcard res_geolocation/*.c))
+# These are the xml and xslt files to be embedded
+res_geolocation.so: res_geolocation/pidf_lo_test.o res_geolocation/pidf_to_eprofile.o
+
res_parking.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
snmp/agent.o: _ASTCFLAGS+=-fPIC
res_snmp.o: _ASTCFLAGS+=-fPIC
# Dependencies for res_ari_*.so are generated, so they're in this file
include ari.make
+
diff --git a/res/res_geolocation.c b/res/res_geolocation.c
index 09a9a4b..9108102 100644
--- a/res/res_geolocation.c
+++ b/res/res_geolocation.c
@@ -17,6 +17,8 @@
*/
/*** MODULEINFO
+ <depend>libxml2</depend>
+ <depend>libxslt</depend>
<support_level>core</support_level>
***/
@@ -24,21 +26,6 @@
#include "asterisk.h"
#include "res_geolocation/geoloc_private.h"
-static const char *result_names[] = {
- "Success",
- "Missing type",
- "Invalid shape type",
- "Invalid variable name",
- "Not enough variables",
- "Too many variables",
- "Invalid variable value"
-};
-
-const char *ast_geoloc_validate_result_to_str(enum ast_geoloc_validate_result result)
-{
- return result_names[result];
-}
-
static int reload_module(void)
{
int res = 0;
@@ -55,6 +42,10 @@
if (res) {
return AST_MODULE_LOAD_DECLINE;
}
+ res = geoloc_eprofile_reload();
+ if (res) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
res = geoloc_dialplan_reload();
if (res) {
return AST_MODULE_LOAD_DECLINE;
@@ -71,8 +62,9 @@
{
int res = 0;
- res += geoloc_channel_reload();
+ res += geoloc_channel_unload();
res += geoloc_dialplan_unload();
+ res += geoloc_eprofile_unload();
res += geoloc_config_unload();
res += geoloc_gml_unload();
res += geoloc_civicaddr_unload();
@@ -84,23 +76,29 @@
{
int res = 0;
- res = geoloc_gml_load();
- if (res) {
- return AST_MODULE_LOAD_DECLINE;
- }
-
res = geoloc_civicaddr_load();
if (res) {
unload_module();
return AST_MODULE_LOAD_DECLINE;
}
+ res = geoloc_gml_load();
+ if (res) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
res = geoloc_config_load();
if (res) {
unload_module();
return AST_MODULE_LOAD_DECLINE;
}
+ res = geoloc_eprofile_load();
+ if (res) {
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
res = geoloc_dialplan_load();
if (res) {
unload_module();
diff --git a/res/res_geolocation/geoloc_channel.c b/res/res_geolocation/geoloc_channel.c
index 719e214..aa8ad87 100644
--- a/res/res_geolocation/geoloc_channel.c
+++ b/res/res_geolocation/geoloc_channel.c
@@ -18,6 +18,7 @@
#include "asterisk.h"
#include "asterisk/datastore.h"
+#include "asterisk/res_geolocation.h"
#include "geoloc_private.h"
@@ -36,7 +37,7 @@
};
-struct ast_datastore *ast_geoloc_datastore_create(const char *profile_name)
+struct ast_datastore *ast_geoloc_datastore_create_from_profile_name(const char *profile_name)
{
struct ast_datastore *ds = NULL;
struct ast_geoloc_effective_profile *eprofile = NULL;
@@ -59,7 +60,7 @@
return NULL;
}
- eprofile = ast_geoloc_effective_profile_create(profile);
+ eprofile = ast_geoloc_eprofile_create_from_profile(profile);
ao2_ref(profile, -1);
if (!eprofile) {
ast_datastore_free(ds);
diff --git a/res/res_geolocation/geoloc_common.c b/res/res_geolocation/geoloc_common.c
new file mode 100644
index 0000000..bb24a31
--- /dev/null
+++ b/res/res_geolocation/geoloc_common.c
@@ -0,0 +1,36 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2022, Sangoma Technologies Corporation
+ *
+ * George Joseph <gjoseph at sangoma.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#include "asterisk.h"
+#include "geoloc_private.h"
+
+static const char *result_names[] = {
+ "Success",
+ "Missing type",
+ "Invalid shape type",
+ "Invalid variable name",
+ "Not enough variables",
+ "Too many variables",
+ "Invalid variable value"
+};
+
+const char *ast_geoloc_validate_result_to_str(enum ast_geoloc_validate_result result)
+{
+ return result_names[result];
+}
+
diff --git a/res/res_geolocation/geoloc_config.c b/res/res_geolocation/geoloc_config.c
index c2fa2d0..b707c89 100644
--- a/res/res_geolocation/geoloc_config.c
+++ b/res/res_geolocation/geoloc_config.c
@@ -24,7 +24,7 @@
static struct ast_sorcery *geoloc_sorcery;
-static const char *pidf_section_names[] = {
+static const char *pidf_element_names[] = {
"<none>",
"tuple",
"device",
@@ -51,16 +51,22 @@
static void geoloc_location_destructor(void *obj) {
struct ast_geoloc_location *location = obj;
+ ast_string_field_free_memory(location);
ast_variables_destroy(location->location_vars);
}
static void *geoloc_location_alloc(const char *name)
{
- return ast_sorcery_generic_alloc(sizeof(struct ast_geoloc_location), geoloc_location_destructor);
+ struct ast_geoloc_location *location = ast_sorcery_generic_alloc(sizeof(struct ast_geoloc_location), geoloc_location_destructor);
+ if (location) {
+ ast_string_field_init(location, 128);
+ }
+
+ return location;
}
-CONFIG_ENUM(profile, pidf_section)
+CONFIG_ENUM(profile, pidf_element)
CONFIG_ENUM(profile, location_disposition)
CONFIG_VAR_LIST(profile, location_refinement)
CONFIG_VAR_LIST(profile, location_variables)
@@ -86,77 +92,6 @@
return profile;
}
-static void geoloc_effective_profile_destructor(void *obj) {
- struct ast_geoloc_effective_profile *eprofile = obj;
-
- ast_string_field_free_memory(eprofile);
- ast_variables_destroy(eprofile->location_refinement);
- ast_variables_destroy(eprofile->location_variables);
- ast_variables_destroy(eprofile->effective_location);
- ast_variables_destroy(eprofile->usage_rules_vars);
-}
-
-static void *geoloc_effective_profile_alloc(const char *name)
-{
- struct ast_geoloc_effective_profile *eprofile = ao2_alloc_options(sizeof(*eprofile),
- geoloc_effective_profile_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
-
- ast_string_field_init(eprofile, 256);
- ast_string_field_set(eprofile, id, name);
-
- return eprofile;
-}
-
-struct ast_geoloc_effective_profile *ast_geoloc_effective_profile_create(struct ast_geoloc_profile *profile)
-{
- struct ast_geoloc_effective_profile *eprofile;
- const char *profile_id;
- struct ast_geoloc_location *loc = NULL;
-
- if (!profile) {
- return NULL;
- }
- profile_id = ast_sorcery_object_get_id(profile);
- eprofile = geoloc_effective_profile_alloc(profile_id);
- if (!eprofile) {
- return NULL;
- }
-
- ao2_lock(profile);
- ast_string_field_set(eprofile, location_reference, profile->location_reference);
- eprofile->geolocation_routing = profile->geolocation_routing;
- eprofile->pidf_section = profile->pidf_section;
- eprofile->location_refinement = profile->location_refinement ? ast_variables_dup(profile->location_refinement) : NULL;
- eprofile->location_variables = profile->location_variables ? ast_variables_dup(profile->location_variables) : NULL;
- eprofile->usage_rules_vars = profile->usage_rules_vars ? ast_variables_dup(profile->usage_rules_vars) : NULL;
- eprofile->location_disposition = profile->location_disposition;
- eprofile->send_location = profile->send_location;
- ao2_unlock(profile);
-
- if (!ast_strlen_zero(eprofile->location_reference)) {
- loc = ast_sorcery_retrieve_by_id(geoloc_sorcery, "location", eprofile->location_reference);
- if (loc) {
- struct ast_variable *var;
- eprofile->format = loc->format;
- eprofile->location_vars = ast_variables_dup(loc->location_vars);
- ao2_ref(loc, -1);
-
- eprofile->effective_location = ast_variables_dup(loc->location_vars);
- for (var = profile->location_refinement; var; var = var->next) {
- struct ast_variable *newvar = ast_variable_new(var->name, var->value, "");
- if (ast_variable_list_replace(&eprofile->effective_location, newvar)) {
- ast_variable_list_append(&eprofile->effective_location, newvar);
- }
- }
- } else {
- ast_log(LOG_ERROR, "Profile '%s' referenced location '%s' does not exist!", profile_id,
- eprofile->location_reference);
- }
- }
-
- return eprofile;
-}
-
static int geoloc_location_apply_handler(const struct ast_sorcery *sorcery, void *obj)
{
struct ast_geoloc_location *location = obj;
@@ -498,7 +433,7 @@
struct ast_str *refinement_str = NULL;
struct ast_str *variables_str = NULL;
struct ast_str *resolved_str = NULL;
- struct ast_geoloc_effective_profile *eprofile = ast_geoloc_effective_profile_create(profile);
+ struct ast_geoloc_effective_profile *eprofile = ast_geoloc_eprofile_create_from_profile(profile);
ao2_ref(profile, -1);
if (!ast_strlen_zero(eprofile->location_reference)) {
@@ -515,7 +450,7 @@
"id: %-s\n"
"received_location_disposition: %-s\n"
"send_location: %-s\n"
- "pidf_section: %-s\n"
+ "pidf_element: %-s\n"
"location_reference: %-s\n"
"Location_format: %-s\n"
"location_reference_details: %-s\n"
@@ -525,7 +460,7 @@
eprofile->id,
disposition,
eprofile->send_location ? "yes" : "no",
- pidf_section_names[eprofile->pidf_section],
+ pidf_element_names[eprofile->pidf_element],
S_OR(eprofile->location_reference, "<none>"),
format_names[eprofile->format],
S_COR(loc_str, ast_str_buffer(loc_str), "<none>"),
@@ -591,6 +526,9 @@
{
ast_cli_unregister_multiple(geoloc_location_cli_commands, ARRAY_LEN(geoloc_location_cli_commands));
+ ast_sorcery_object_unregister(geoloc_sorcery, "profile");
+ ast_sorcery_object_unregister(geoloc_sorcery, "location");
+
if (geoloc_sorcery) {
ast_sorcery_unref(geoloc_sorcery);
}
@@ -629,8 +567,8 @@
}
ast_sorcery_object_field_register(geoloc_sorcery, "profile", "type", "", OPT_NOOP_T, 0, 0);
- ast_sorcery_object_field_register_custom(geoloc_sorcery, "profile", "pidf_lo_section", AST_PIDF_SECTION_NONE,
- pidf_section_handler, pidf_section_to_str, NULL, 0, 0);
+ ast_sorcery_object_field_register_custom(geoloc_sorcery, "profile", "pidf_element", AST_PIDF_ELEMENT_NONE,
+ pidf_element_handler, pidf_element_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(geoloc_sorcery, "profile", "location_reference", "", OPT_STRINGFIELD_T,
0, STRFLDSET(struct ast_geoloc_profile, location_reference));
ast_sorcery_object_field_register_custom(geoloc_sorcery, "profile", "received_location_disposition", "discard",
diff --git a/res/res_geolocation/geoloc_doc.xml b/res/res_geolocation/geoloc_doc.xml
index 36bfa3c..8322585 100644
--- a/res/res_geolocation/geoloc_doc.xml
+++ b/res/res_geolocation/geoloc_doc.xml
@@ -57,7 +57,7 @@
<configOption name="type">
<synopsis>Must be of type 'profile'.</synopsis>
</configOption>
- <configOption name="pidf_lo_section" default="device">
+ <configOption name="pidf_element" default="device">
<synopsis>PIDF-LO element to place this profile in</synopsis>
<description>
<enumlist>
diff --git a/res/res_geolocation/geoloc_eprofile.c b/res/res_geolocation/geoloc_eprofile.c
new file mode 100644
index 0000000..b6641c7
--- /dev/null
+++ b/res/res_geolocation/geoloc_eprofile.c
@@ -0,0 +1,503 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2022, Sangoma Technologies Corporation
+ *
+ * George Joseph <gjoseph at sangoma.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#include "asterisk.h"
+#include "asterisk/xml.h"
+#include "geoloc_private.h"
+
+extern const uint8_t _binary_res_geolocation_pidf_to_eprofile_xslt_start[];
+extern const uint8_t _binary_res_geolocation_pidf_to_eprofile_xslt_end[];
+static size_t pidf_to_eprofile_xslt_size;
+
+extern const uint8_t _binary_res_geolocation_pidf_lo_test_xml_start[];
+extern const uint8_t _binary_res_geolocation_pidf_lo_test_xml_end[];
+static size_t pidf_lo_test_xml_size;
+
+static struct ast_xslt_doc *pidf_lo_xslt;
+
+static struct ast_sorcery *geoloc_sorcery;
+
+static void geoloc_effective_profile_destructor(void *obj) {
+ struct ast_geoloc_effective_profile *eprofile = obj;
+
+ ast_string_field_free_memory(eprofile);
+ ast_variables_destroy(eprofile->location_vars);
+ ast_variables_destroy(eprofile->location_refinement);
+ ast_variables_destroy(eprofile->location_variables);
+ ast_variables_destroy(eprofile->effective_location);
+ ast_variables_destroy(eprofile->usage_rules_vars);
+}
+
+struct ast_geoloc_effective_profile *ast_geoloc_eprofile_alloc(const char *name)
+{
+ struct ast_geoloc_effective_profile *eprofile = ao2_alloc_options(sizeof(*eprofile),
+ geoloc_effective_profile_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
+
+ ast_string_field_init(eprofile, 256);
+ ast_string_field_set(eprofile, id, name); /* SAFE string fields handle NULL */
+
+ return eprofile;
+}
+
+int ast_geoloc_eprofile_refresh_location(struct ast_geoloc_effective_profile *eprofile)
+{
+ struct ast_geoloc_location *loc = NULL;
+ struct ast_variable *var;
+
+ if (!ast_strlen_zero(eprofile->location_reference)) {
+ loc = ast_sorcery_retrieve_by_id(geoloc_sorcery, "location", eprofile->location_reference);
+ if (!loc) {
+ ast_log(LOG_ERROR, "Profile '%s' referenced location '%s' does not exist!", eprofile->id,
+ eprofile->location_reference);
+ return -1;
+ }
+
+ eprofile->format = loc->format;
+ ast_variables_destroy(eprofile->location_vars);
+ eprofile->location_vars = ast_variables_dup(loc->location_vars);
+ ao2_ref(loc, -1);
+ }
+
+ ast_variables_destroy(eprofile->effective_location);
+ eprofile->effective_location = ast_variables_dup(eprofile->location_vars);
+ if (eprofile->location_refinement) {
+ for (var = eprofile->location_refinement; var; var = var->next) {
+ struct ast_variable *newvar = ast_variable_new(var->name, var->value, "");
+ if (ast_variable_list_replace(&eprofile->effective_location, newvar)) {
+ ast_variable_list_append(&eprofile->effective_location, newvar);
+ }
+ }
+ }
+
+ return 0;
+}
+
+#define DUP_VARS(_dest, _source) \
+({ \
+ int _rc = 0; \
+ if (_source) { \
+ struct ast_variable *_vars = ast_variables_dup(_source); \
+ if (!_vars) { \
+ _rc = -1; \
+ } else { \
+ _dest = _vars; \
+ } \
+ } \
+ (_rc); \
+})
+
+struct ast_geoloc_effective_profile *ast_geoloc_eprofile_create_from_profile(struct ast_geoloc_profile *profile)
+{
+ struct ast_geoloc_effective_profile *eprofile;
+ const char *profile_id;
+ int rc = 0;
+
+ if (!profile) {
+ return NULL;
+ }
+
+ profile_id = ast_sorcery_object_get_id(profile);
+
+ eprofile = ast_geoloc_eprofile_alloc(profile_id);
+ if (!eprofile) {
+ return NULL;
+ }
+
+ ao2_lock(profile);
+ ast_string_field_set(eprofile, location_reference, profile->location_reference);
+ eprofile->geolocation_routing = profile->geolocation_routing;
+ eprofile->pidf_element = profile->pidf_element;
+
+ rc = DUP_VARS(eprofile->location_refinement, profile->location_refinement);
+ if (rc == 0) {
+ rc = DUP_VARS(eprofile->location_variables, profile->location_variables);
+ }
+ if (rc == 0) {
+ rc = DUP_VARS(eprofile->usage_rules_vars, profile->usage_rules_vars);
+ }
+ if (rc != 0) {
+ ao2_unlock(profile);
+ ao2_ref(eprofile, -1);
+ return NULL;
+ }
+
+ eprofile->location_disposition = profile->location_disposition;
+ eprofile->send_location = profile->send_location;
+ ao2_unlock(profile);
+
+ if (ast_geoloc_eprofile_refresh_location(eprofile) != 0) {
+ ao2_ref(eprofile, -1);
+ return NULL;
+ }
+
+ return eprofile;
+}
+
+struct ast_geoloc_effective_profile *ast_geoloc_eprofile_create_from_uri(const char *uri,
+ const char *reference_string)
+{
+ struct ast_geoloc_effective_profile *eprofile = NULL;
+ char *local_uri;
+
+ if (ast_strlen_zero(uri)) {
+ return NULL;
+ }
+ local_uri = ast_strdupa(uri);
+
+ if (local_uri[0] == '<') {
+ local_uri++;
+ }
+ if (ast_ends_with(local_uri, ">")) {
+ local_uri[strlen(local_uri)-1] = '\0';
+ }
+ ast_strip(local_uri);
+
+ eprofile = ast_geoloc_eprofile_alloc(local_uri);
+ if (!eprofile) {
+ return NULL;
+ }
+
+ eprofile->format = AST_GEOLOC_FORMAT_URI;
+ eprofile->location_vars = ast_variable_new("URI", local_uri, "");
+
+ return eprofile;
+}
+
+static struct ast_geoloc_effective_profile *geoloc_eprofile_create_from_xslt_result(
+ struct ast_xml_doc *result_doc,
+ const char *reference_string)
+{
+ struct ast_geoloc_effective_profile *eprofile;
+ struct ast_xml_node *presence = NULL;
+ struct ast_xml_node *pidf_element = NULL;
+ struct ast_xml_node *location_info = NULL;
+ struct ast_xml_node *usage_rules = NULL;
+ struct ast_xml_node *method = NULL;
+ const char *id;
+ const char *format_str;
+ const char *pidf_element_str;
+ const char *location_str;
+ const char *usage_str;
+ const char *method_str;
+ char *duped;
+
+ presence = ast_xml_get_root(result_doc);
+ pidf_element = ast_xml_node_get_children(presence);
+ location_info = ast_xml_find_child_element(pidf_element, "location-info", NULL, NULL);
+ usage_rules = ast_xml_find_child_element(pidf_element, "usage-rules", NULL, NULL);
+ method = ast_xml_find_child_element(pidf_element, "method", NULL, NULL);
+
+ id = S_OR(ast_xml_get_attribute(pidf_element, "id"), ast_xml_get_attribute(presence, "entity"));
+ eprofile = ast_geoloc_eprofile_alloc(id);
+ if (!eprofile) {
+ return NULL;
+ }
+
+ format_str = ast_xml_get_attribute(location_info, "format");
+ if (ast_strings_equal(format_str, "gml")) {
+ eprofile->format = AST_GEOLOC_FORMAT_GML;
+ } else if (ast_strings_equal(format_str, "civicAddress")) {
+ eprofile->format = AST_GEOLOC_FORMAT_CIVIC_ADDRESS;
+ } else {
+ ao2_ref(eprofile, -1);
+ ast_log(LOG_ERROR, "%s: Unknown format '%s'\n", reference_string, format_str);
+ return NULL;
+ }
+
+ pidf_element_str = ast_xml_get_attribute(pidf_element, "name");
+ eprofile->pidf_element = geoloc_pidf_element_str_to_enum(pidf_element_str);
+
+ location_str = ast_xml_get_text(location_info);
+ duped = ast_strdupa(location_str);
+ eprofile->location_vars = ast_variable_list_from_string(duped, ",", "=", "\"");
+ if (!eprofile->location_vars) {
+ ao2_ref(eprofile, -1);
+ ast_log(LOG_ERROR, "%s: Unable to create location variables from '%s'\n", reference_string, location_str);
+ return NULL;
+ }
+
+ usage_str = ast_xml_get_text(usage_rules);
+ duped = ast_strdupa(usage_str);
+ eprofile->usage_rules_vars = ast_variable_list_from_string(duped, ",", "=", "\"");
+
+ method_str = ast_xml_get_text(method);
+ ast_string_field_set(eprofile, method, method_str);
+
+ return eprofile;
+}
+
+struct ast_geoloc_effective_profile *ast_geoloc_eprofile_create_from_pidf(
+ struct ast_xml_doc *pidf_xmldoc, const char *reference_string)
+{
+ RAII_VAR(struct ast_xml_doc *, result_doc, NULL, ast_xml_close);
+ struct ast_geoloc_effective_profile *eprofile;
+
+ /*
+ * The namespace prefixes used here (dm, def, gp, etc) don't have to match
+ * the ones used in the received PIDF-LO document but they MUST match the
+ * ones in the embedded pidf_to_eprofile stylesheet.
+ *
+ * RFC5491 Rule 8 states that...
+ * Where a PIDF document contains more than one <geopriv>
+ * element, the priority of interpretation is given to the first
+ * <device> element in the document containing a location. If no
+ * <device> element containing a location is present in the document,
+ * then priority is given to the first <tuple> element containing a
+ * location. Locations contained in <person> tuples SHOULD only be
+ * used as a last resort.
+ *
+ * Reminder: xpath arrays are 1-based not 0-based.
+ */
+ const char *find_device[] = { "path", "/def:presence/dm:device[.//gp:location-info][1]", NULL};
+ const char *find_tuple[] = { "path", "/def:presence/def:tuple[.//gp:location-info][1]", NULL};
+ const char *find_person[] = { "path", "/def:presence/dm:person[.//gp:location-info][1]", NULL};
+
+ result_doc = ast_xslt_apply(pidf_lo_xslt, pidf_xmldoc, find_device);
+ if (!result_doc || !ast_xml_node_get_children((struct ast_xml_node *)result_doc)) {
+ ast_xml_close(result_doc);
+ result_doc = ast_xslt_apply(pidf_lo_xslt, pidf_xmldoc, find_tuple);
+ }
+ if (!result_doc || !ast_xml_node_get_children((struct ast_xml_node *)result_doc)) {
+ ast_xml_close(result_doc);
+ result_doc = ast_xslt_apply(pidf_lo_xslt, pidf_xmldoc, find_person);
+ }
+ if (!result_doc || !ast_xml_node_get_children((struct ast_xml_node *)result_doc)) {
+ return NULL;
+ }
+
+ /*
+ * The document returned from the stylesheet application looks like this...
+ * <presence id="presence-entity">
+ * <pidf-element name="tuple" id="element-id">
+ * <location-info format="gml">format="gml", type="Ellipsoid", crs="3d", ...</location-info>
+ * <usage-rules>retransmission-allowed="no", retention-expiry="2010-11-14T20:00:00Z"</usage-rules>
+ * <method>Hybrid_A-GPS</method>
+ * </pidf-element>
+ * </presence>
+ *
+ * Regardless of whether the pidf-element was tuple, device or person and whether
+ * the format is gml or civicAddress, the presence, pidf-element, location-info,
+ * usage-rules and method elements should be there although usage-rules and method
+ * may be empty.
+ *
+ * The contents of the location-info and usage-rules elements can be passed directly to
+ * ast_variable_list_from_string().
+ */
+
+ eprofile = geoloc_eprofile_create_from_xslt_result(result_doc, reference_string);
+
+ return eprofile;
+}
+
+#ifdef TEST_FRAMEWORK
+static void load_tests(void);
+static void unload_tests(void);
+#else
+static void load_tests(void) {}
+static void unload_tests(void) {}
+#endif
+
+
+int geoloc_eprofile_unload(void)
+{
+ unload_tests();
+ if (pidf_lo_xslt) {
+ ast_xslt_close(pidf_lo_xslt);
+ }
+
+ if (geoloc_sorcery) {
+ ast_sorcery_unref(geoloc_sorcery);
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+int geoloc_eprofile_load(void)
+{
+ geoloc_sorcery = geoloc_get_sorcery();
+
+ pidf_to_eprofile_xslt_size =
+ (_binary_res_geolocation_pidf_to_eprofile_xslt_end - _binary_res_geolocation_pidf_to_eprofile_xslt_start);
+
+ pidf_lo_test_xml_size =
+ (_binary_res_geolocation_pidf_lo_test_xml_end - _binary_res_geolocation_pidf_lo_test_xml_start);
+
+ pidf_lo_xslt = ast_xslt_read_memory(
+ (char *)_binary_res_geolocation_pidf_to_eprofile_xslt_start, pidf_to_eprofile_xslt_size);
+ if (!pidf_lo_xslt) {
+ ast_log(LOG_ERROR, "Unable to read pidf_lo_xslt from memory\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ load_tests();
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+int geoloc_eprofile_reload(void)
+{
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+
+#ifdef TEST_FRAMEWORK
+#include "asterisk/test.h"
+
+AST_TEST_DEFINE(test_create_from_uri)
+{
+
+ RAII_VAR(struct ast_geoloc_effective_profile *, eprofile, NULL, ao2_cleanup);
+ const char *uri = NULL;
+ int rc = AST_TEST_PASS;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "create_from_uri";
+ info->category = "/geoloc/";
+ info->summary = "Test create from uri";
+ info->description = info->summary;
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ eprofile = ast_geoloc_eprofile_create_from_uri("http://some_uri&a=b", __func__);
+ ast_test_validate(test, eprofile != NULL);
+ ast_test_validate(test, eprofile->format == AST_GEOLOC_FORMAT_URI);
+ ast_test_validate(test, eprofile->location_vars != NULL);
+ uri = ast_variable_find_in_list(eprofile->location_vars, "URI");
+ ast_test_validate(test, uri != NULL);
+ ast_test_validate(test, strcmp(uri, "http://some_uri&a=b") == 0);
+
+ return rc;
+}
+
+static enum ast_test_result_state validate_eprofile(struct ast_test *test,
+ struct ast_xml_doc * pidf_xmldoc,
+ const char *path,
+ const char *id,
+ enum ast_geoloc_pidf_element pidf_element,
+ enum ast_geoloc_format format,
+ const char *method,
+ const char *location,
+ const char *usage
+ )
+{
+ RAII_VAR(struct ast_str *, str, NULL, ast_free);
+ RAII_VAR(struct ast_geoloc_effective_profile *, eprofile, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_xml_doc *, result_doc, NULL, ast_xml_close);
+ const char *search[] = { "path", path, NULL };
+
+ if (!ast_strlen_zero(path)) {
+ result_doc = ast_xslt_apply(pidf_lo_xslt, pidf_xmldoc, (const char **)search);
+ ast_test_validate(test, (result_doc && ast_xml_node_get_children((struct ast_xml_node *)result_doc)));
+
+ eprofile = geoloc_eprofile_create_from_xslt_result(result_doc, "test_create_from_xslt");
+ } else {
+ eprofile = ast_geoloc_eprofile_create_from_pidf(pidf_xmldoc, "test_create_from_pidf");
+ }
+
+ ast_test_validate(test, eprofile != NULL);
+ ast_test_status_update(test, "ID: '%s' pidf_element: '%s' format: '%s' method: '%s'\n", eprofile->id,
+ geoloc_pidf_element_to_name(eprofile->pidf_element),
+ geoloc_format_to_name(eprofile->format),
+ eprofile->method);
+
+ ast_test_validate(test, ast_strings_equal(eprofile->id, id));
+ ast_test_validate(test, eprofile->pidf_element == pidf_element);
+ ast_test_validate(test, eprofile->format == format);
+ ast_test_validate(test, ast_strings_equal(eprofile->method, method));
+
+ str = ast_variable_list_join(eprofile->location_vars, ",", "=", NULL, NULL);
+ ast_test_validate(test, str != NULL);
+ ast_test_status_update(test, "location_vars: %s\n", ast_str_buffer(str));
+ ast_test_validate(test, ast_strings_equal(ast_str_buffer(str), location));
+ ast_free(str);
+
+ str = ast_variable_list_join(eprofile->usage_rules_vars, ",", "=", "'", NULL);
+ ast_test_validate(test, str != NULL);
+ ast_test_status_update(test, "usage_rules: %s\n", ast_str_buffer(str));
+ ast_test_validate(test, ast_strings_equal(ast_str_buffer(str), usage));
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_create_from_pidf)
+{
+
+ RAII_VAR(struct ast_xml_doc *, pidf_xmldoc, NULL, ast_xml_close);
+ enum ast_test_result_state res = AST_TEST_PASS;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "create_from_pidf";
+ info->category = "/geoloc/";
+ info->summary = "Test create from pidf scenarios";
+ info->description = info->summary;
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ pidf_xmldoc = ast_xml_read_memory((char *)_binary_res_geolocation_pidf_lo_test_xml_start, pidf_lo_test_xml_size);
+ ast_test_validate(test, pidf_xmldoc != NULL);
+
+ res = validate_eprofile(test, pidf_xmldoc,
+ NULL,
+ "arcband-2d",
+ AST_PIDF_ELEMENT_DEVICE,
+ AST_GEOLOC_FORMAT_GML,
+ "TA-NMR",
+ "format=gml,type=ArcBand,crs=2d,pos=-43.5723 153.21760,innerRadius=3594,"
+ "outerRadius=4148,startAngle=20,startAngle_uom=radians,openingAngle=20,"
+ "openingAngle_uom=radians",
+ "retransmission-allowed='yes',ruleset-preference='https:/www/more.com',"
+ "retention-expires='2007-06-22T20:57:29Z'"
+ );
+ ast_test_validate(test, res == AST_TEST_PASS);
+
+
+ res = validate_eprofile(test, pidf_xmldoc,
+ "/def:presence/dm:device[.//ca:civicAddress][1]",
+ "pres:alice at asterisk.org",
+ AST_PIDF_ELEMENT_DEVICE,
+ AST_GEOLOC_FORMAT_CIVIC_ADDRESS,
+ "GPS",
+ "format=civicAddress,country=AU,A1=NSW,A3=Wollongong,A4=North Wollongong,"
+ "RD=Flinders,STS=Street,RDBR=Campbell Street,LMK=Gilligan's Island,"
+ "LOC=Corner,NAM=Video Rental Store,PC=2500,ROOM=Westerns and Classics,"
+ "PLC=store,POBOX=Private Box 15",
+ "retransmission-allowed='yes',ruleset-preference='https:/www/more.com',"
+ "retention-expires='2007-06-22T20:57:29Z'"
+ );
+ ast_test_validate(test, res == AST_TEST_PASS);
+
+
+ return res;
+}
+
+static void load_tests(void) {
+ AST_TEST_REGISTER(test_create_from_uri);
+ AST_TEST_REGISTER(test_create_from_pidf);
+}
+static void unload_tests(void) {
+ AST_TEST_UNREGISTER(test_create_from_uri);
+ AST_TEST_UNREGISTER(test_create_from_pidf);
+}
+
+#endif
diff --git a/res/res_geolocation/geoloc_private.h b/res/res_geolocation/geoloc_private.h
index 194d507..c5ea981 100644
--- a/res/res_geolocation/geoloc_private.h
+++ b/res/res_geolocation/geoloc_private.h
@@ -25,8 +25,18 @@
#include "asterisk/lock.h"
#include "asterisk/res_geolocation.h"
+#define CONFIG_STR_TO_ENUM_DECL(_stem) int geoloc_ ## _stem ## _str_to_enum(const char *str);
+CONFIG_STR_TO_ENUM_DECL(pidf_element)
+CONFIG_STR_TO_ENUM_DECL(format);
+CONFIG_STR_TO_ENUM_DECL(location_disposition);
+#define GEOLOC_ENUM_TO_NAME_DECL(_stem) const char * geoloc_ ## _stem ## _to_name(int ix);
+GEOLOC_ENUM_TO_NAME_DECL(pidf_element)
+GEOLOC_ENUM_TO_NAME_DECL(format);
+GEOLOC_ENUM_TO_NAME_DECL(location_disposition);
+
+
#define CONFIG_STR_TO_ENUM(_stem) \
-static int _stem ## _str_to_enum(const char *str) \
+int geoloc_ ## _stem ## _str_to_enum(const char *str) \
{ \
int i; \
for (i = 0; i < ARRAY_LEN(_stem ## _names); i++) { \
@@ -41,7 +51,7 @@
static int _stem ## _handler(const struct aco_option *opt, struct ast_variable *var, void *obj) \
{ \
struct ast_geoloc_ ## _object *_thisobject = obj; \
- int enumval = _stem ## _str_to_enum(var->value); \
+ int enumval = geoloc_ ## _stem ## _str_to_enum(var->value); \
if (enumval == -1) { \
return -1; \
} \
@@ -49,6 +59,17 @@
return 0; \
}
+
+#define GEOLOC_ENUM_TO_NAME(_stem) \
+const char * geoloc_ ## _stem ## _to_name(int ix) \
+{ \
+ if (!ARRAY_IN_BOUNDS(ix, _stem ## _names)) { \
+ return "none"; \
+ } else { \
+ return _stem ## _names[ix]; \
+ } \
+}
+
#define CONFIG_ENUM_TO_STR(_object, _stem) \
static int _stem ## _to_str(const void *obj, const intptr_t *args, char **buf) \
{ \
@@ -63,6 +84,7 @@
#define CONFIG_ENUM(_object, _stem) \
CONFIG_STR_TO_ENUM(_stem) \
+GEOLOC_ENUM_TO_NAME(_stem) \
CONFIG_ENUM_HANDLER(_object, _stem) \
CONFIG_ENUM_TO_STR(_object, _stem)
@@ -133,6 +155,10 @@
int geoloc_channel_load(void);
int geoloc_channel_reload(void);
+int geoloc_eprofile_unload(void);
+int geoloc_eprofile_load(void);
+int geoloc_eprofile_reload(void);
+
struct ast_sorcery *geoloc_get_sorcery(void);
#endif /* GEOLOC_PRIVATE_H_ */
diff --git a/res/res_geolocation/pidf_lo_test.xml b/res/res_geolocation/pidf_lo_test.xml
new file mode 100644
index 0000000..3948063
--- /dev/null
+++ b/res/res_geolocation/pidf_lo_test.xml
@@ -0,0 +1,312 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<presence entity="pres:alice at asterisk.org"
+ xmlns="urn:ietf:params:xml:ns:pidf"
+ xmlns:ca="urn:ietf:params:xml:ns:pidf:geopriv10:civicAddr"
+ xmlns:dm="urn:ietf:params:xml:ns:pidf:data-model"
+ xmlns:gbp="urn:ietf:params:xml:ns:pidf:geopriv10:basicPolicy"
+ xmlns:gml="http://www.opengis.net/gml"
+ xmlns:gp="urn:ietf:params:xml:ns:pidf:geopriv10"
+ xmlns:gs="http://www.opengis.net/pidflo/1.0">
+ <tuple id="point-2d">
+ <status>
+ <gp:geopriv>
+ <gp:location-info>
+ <gml:Point srsName="urn:ogc:def:crs:EPSG::4326">
+ <gml:pos>-34.410649 150.87651</gml:pos>
+ </gml:Point>
+ </gp:location-info>
+ <gp:usage-rules>
+ <gbp:retransmission-allowed>no</gbp:retransmission-allowed>
+ <gbp:retention-expiry>2010-11-14T20:00:00Z</gbp:retention-expiry>
+ </gp:usage-rules>
+ <gp:method>Manual</gp:method>
+ </gp:geopriv>
+ </status>
+ <timestamp>2007-06-22T20:57:29Z</timestamp>
+ </tuple>
+ <dm:person id="point-3d">
+ <gp:geopriv>
+ <gp:location-info>
+ <gml:Point srsName="urn:ogc:def:crs:EPSG::4979">
+ <gml:pos>-34.410649 150.87651 1800</gml:pos>
+ </gml:Point>
+ </gp:location-info>
+ <gp:usage-rules>
+ <gbp:retransmission-allowed>no</gbp:retransmission-allowed>
+ <gbp:retention-expiry>2010-11-14T20:00:00Z</gbp:retention-expiry>
+ </gp:usage-rules>
+ <gp:method>802.11</gp:method>
+ </gp:geopriv>
+ <dm:timestamp>2007-06-24T12:28:04Z</dm:timestamp>
+ </dm:person>
+ <tuple id="circle-2d">
+ <status>
+ <gp:geopriv>
+ <gp:location-info>
+ <gs:Circle srsName="urn:ogc:def:crs:EPSG::4326">
+ <gml:pos>-34.410649 150.87651</gml:pos>
+ <gs:radius uom="urn:ogc:def:uom:EPSG::9001">30</gs:radius>
+ </gs:Circle>
+ </gp:location-info>
+ <gp:usage-rules>
+ <gbp:retransmission-allowed>no</gbp:retransmission-allowed>
+ <gbp:retention-expiry>2010-11-14T20:00:00Z</gbp:retention-expiry>
+ </gp:usage-rules>
+ <gp:method>802.11</gp:method>
+ </gp:geopriv>
+ </status>
+ <timestamp>2007-06-22T20:57:29Z</timestamp>
+ </tuple>
+ <tuple id="circle-3d">
+ <status>
+ <gp:geopriv>
+ <gp:location-info>
+ <gs:Circle srsName="urn:ogc:def:crs:EPSG::4979">
+ <gml:pos>-34.410649 150.87651 1800</gml:pos>
+ <gs:radius uom="urn:ogc:def:uom:EPSG::9001">30</gs:radius>
+ </gs:Circle>
+ </gp:location-info>
+ <gp:usage-rules>
+ <gbp:retransmission-allowed>no</gbp:retransmission-allowed>
+ <gbp:retention-expiry>2010-11-14T20:00:00Z</gbp:retention-expiry>
+ </gp:usage-rules>
+ <gp:method>802.11</gp:method>
+ </gp:geopriv>
+ </status>
+ <timestamp>2007-06-22T20:57:29Z</timestamp>
+ </tuple>
+ <dm:person id="polygon-2d">
+ <gp:geopriv>
+ <gp:location-info>
+ <gml:Polygon srsName="urn:ogc:def:crs:EPSG::4326">
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:pos>43.311 -73.422</gml:pos>
+ <gml:pos>43.111 -73.322</gml:pos>
+ <gml:pos>43.111 -73.222</gml:pos>
+ <gml:pos>43.311 -73.122</gml:pos>
+ <gml:pos>43.411 -73.222</gml:pos>
+ <gml:pos>43.411 -73.322</gml:pos>
+ <gml:pos>43.311 -73.422</gml:pos>
+ </gml:LinearRing>
+ </gml:exterior>
+ </gml:Polygon>
+ </gp:location-info>
+ <gp:usage-rules>
+ <gbp:retransmission-allowed>no</gbp:retransmission-allowed>
+ <gbp:retention-expiry>2010-11-14T20:00:00Z</gbp:retention-expiry>
+ </gp:usage-rules>
+ <gp:method>802.11</gp:method>
+ </gp:geopriv>
+ <dm:timestamp>2007-06-24T12:28:04Z</dm:timestamp>
+ </dm:person>
+ <dm:person id="polygon-3d">
+ <gp:geopriv>
+ <gp:location-info>
+ <gml:Polygon srsName="urn:ogc:def:crs:EPSG::4979">
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:pos>43.311 -73.422 1800</gml:pos>
+ <gml:pos>43.111 -73.322 1800</gml:pos>
+ <gml:pos>43.111 -73.222 1800</gml:pos>
+ <gml:pos>43.311 -73.122 1800</gml:pos>
+ <gml:pos>43.411 -73.222 1800</gml:pos>
+ <gml:pos>43.411 -73.322 1800</gml:pos>
+ <gml:pos>43.311 -73.422 1800</gml:pos>
+ </gml:LinearRing>
+ </gml:exterior>
+ </gml:Polygon>
+ </gp:location-info>
+ <gp:usage-rules>
+ <gbp:retransmission-allowed>no</gbp:retransmission-allowed>
+ <gbp:retention-expiry>2010-11-14T20:00:00Z</gbp:retention-expiry>
+ </gp:usage-rules>
+ <gp:method>802.11</gp:method>
+ </gp:geopriv>
+ <dm:timestamp>2007-06-24T12:28:04Z</dm:timestamp>
+ </dm:person>
+ <tuple id="polygon-poslist-2d">
+ <status>
+ <gp:geopriv>
+ <gp:location-info>
+ <gml:Polygon srsName="urn:ogc:def:crs:EPSG::4326">
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>
+ 43.311 -73.422 43.111 -73.322
+ 43.111 -73.222 43.311 -73.122
+ 43.411 -73.222 43.411 -73.322
+ 43.311 -73.422
+ </gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ </gml:Polygon>
+ </gp:location-info>
+ <gp:usage-rules>
+ <gbp:retransmission-allowed>no</gbp:retransmission-allowed>
+ <gbp:retention-expiry>2010-11-14T20:00:00Z</gbp:retention-expiry>
+ </gp:usage-rules>
+ <gp:method>Wiremap</gp:method>
+ </gp:geopriv>
+ </status>
+ <timestamp>2007-06-22T20:57:29Z</timestamp>
+ </tuple>
+ <tuple id="polygon-poslist-3d">
+ <status>
+ <gp:geopriv>
+ <gp:location-info>
+ <gml:Polygon srsName="urn:ogc:def:crs:EPSG::4979">
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>
+ 43.311 -73.422 1800 43.111 -73.322 1800
+ 43.111 -73.222 1800 43.311 -73.122 1800
+ 43.411 -73.222 1800 43.411 -73.322 1800
+ 43.311 -73.422 1800
+ </gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ </gml:Polygon>
+ </gp:location-info>
+ <gp:usage-rules>
+ <gbp:retransmission-allowed>no</gbp:retransmission-allowed>
+ <gbp:retention-expiry>2010-11-14T20:00:00Z</gbp:retention-expiry>
+ </gp:usage-rules>
+ <gp:method>Wiremap</gp:method>
+ </gp:geopriv>
+ </status>
+ <timestamp>2007-06-22T20:57:29Z</timestamp>
+ </tuple>
+ <tuple id="ellipse-2d">
+ <status>
+ <gp:geopriv>
+ <gp:location-info>
+ <gs:Ellipse srsName="urn:ogc:def:crs:EPSG::4326">
+ <gml:pos>42.5463 -73.2512</gml:pos>
+ <gs:semiMajorAxis uom="urn:ogc:def:uom:EPSG::9001">1275</gs:semiMajorAxis>
+ <gs:semiMinorAxis uom="urn:ogc:def:uom:EPSG::9001">670</gs:semiMinorAxis>
+ <gs:orientation uom="urn:ogc:def:uom:EPSG::9102">43.2</gs:orientation>
+ </gs:Ellipse>
+ </gp:location-info>
+ <gp:usage-rules/>
+ <gp:method>Device-Assisted_A-GPS</gp:method>
+ </gp:geopriv>
+ </status>
+ <timestamp>2007-06-22T20:57:29Z</timestamp>
+ </tuple>
+ <dm:device id="arcband-2d">
+ <gp:geopriv>
+ <gp:location-info>
+ <gs:ArcBand srsName="urn:ogc:def:crs:EPSG::4326">
+ <gml:pos>-43.5723 153.21760</gml:pos>
+ <gs:innerRadius uom="urn:ogc:def:uom:EPSG::9001">3594</gs:innerRadius>
+ <gs:outerRadius uom="urn:ogc:def:uom:EPSG::9001">4148</gs:outerRadius>
+ <gs:startAngle uom="urn:ogc:def:uom:EPSG::9102">20</gs:startAngle>
+ <gs:openingAngle uom="urn:ogc:def:uom:EPSG::9102">20</gs:openingAngle>
+ </gs:ArcBand>
+ </gp:location-info>
+ <gp:usage-rules>
+ <gp:retransmission-allowed>yes</gp:retransmission-allowed>
+ <gp:ruleset-preference>https:/www/more.com</gp:ruleset-preference>
+ <gp:retention-expires>2007-06-22T20:57:29Z</gp:retention-expires>
+ </gp:usage-rules>
+ <gp:method>TA-NMR</gp:method>
+ </gp:geopriv>
+ <dm:deviceID>mac:1234567890ab</dm:deviceID>
+ <dm:timestamp>2007-06-22T20:57:29Z</dm:timestamp>
+ </dm:device>
+ <tuple id="sphere">
+ <status>
+ <gp:geopriv>
+ <gp:location-info>
+ <gs:Sphere srsName="urn:ogc:def:crs:EPSG::4979">
+ <gml:pos>42.5463 -73.2512 26.3</gml:pos>
+ <gs:radius uom="urn:ogc:def:uom:EPSG::9001">850.24</gs:radius>
+ </gs:Sphere>
+ </gp:location-info>
+ <gp:usage-rules>
+ <gbp:retransmission-allowed>no</gbp:retransmission-allowed>
+ <gbp:retention-expiry>2010-11-14T20:00:00Z</gbp:retention-expiry>
+ </gp:usage-rules>
+ <gp:method>Device-Based_A-GPS</gp:method>
+ </gp:geopriv>
+ </status>
+ </tuple>
+ <tuple id="ellipsoid">
+ <status>
+ <gp:geopriv>
+ <gp:location-info>
+ <gs:Ellipsoid srsName="urn:ogc:def:crs:EPSG::4979">
+ <gml:pos>42.5463 -73.2512 26.3</gml:pos>
+ <gs:semiMajorAxis uom="urn:ogc:def:uom:EPSG::9001">7.7156</gs:semiMajorAxis>
+ <gs:semiMinorAxis uom="urn:ogc:def:uom:EPSG::9001">3.31</gs:semiMinorAxis>
+ <gs:verticalAxis uom="urn:ogc:def:uom:EPSG::9001">28.7</gs:verticalAxis>
+ <gs:orientation uom="urn:ogc:def:uom:EPSG::9102">90</gs:orientation>
+ </gs:Ellipsoid>
+ </gp:location-info>
+ <gp:usage-rules/>
+ <gp:method>Hybrid_A-GPS</gp:method>
+ </gp:geopriv>
+ </status>
+ <timestamp>2007-06-22T20:57:29Z</timestamp>
+ </tuple>
+ <tuple id="prism">
+ <status>
+ <gp:geopriv>
+ <gp:location-info>
+ <gs:Prism srsName="urn:ogc:def:crs:EPSG::4979">
+ <gs:base>
+ <gml:Polygon>
+ <gml:exterior>
+ <gml:LinearRing>
+ <gml:posList>
+ 42.556844 -73.248157 36.6 <!--A -->
+ 42.656844 -73.248157 36.6 <!--B -->
+ 42.656844 -73.348157 36.6 <!--C -->
+ 42.556844 -73.348157 36.6 <!--D -->
+ 42.556844 -73.248157 36.6 <!--A -->
+ </gml:posList>
+ </gml:LinearRing>
+ </gml:exterior>
+ </gml:Polygon>
+ </gs:base>
+ <gs:height uom="urn:ogc:def:uom:EPSG::9001">2.4</gs:height>
+ </gs:Prism>
+ </gp:location-info>
+ <gp:usage-rules/>
+ <gp:method>Wiremap</gp:method>
+ </gp:geopriv>
+ </status>
+ <timestamp>2007-06-22T20:57:29Z</timestamp>
+ </tuple>
+ <dm:device>
+ <gp:geopriv>
+ <gp:location-info>
+ <ca:civicAddress xml:lang="en-AU">
+ <ca:country>AU</ca:country>
+ <ca:A1>NSW</ca:A1>
+ <ca:A3>Wollongong</ca:A3>
+ <ca:A4>North Wollongong</ca:A4>
+ <ca:RD>Flinders</ca:RD>
+ <ca:STS>Street</ca:STS>
+ <ca:RDBR>Campbell Street</ca:RDBR>
+ <ca:LMK>Gilligan's Island</ca:LMK>
+ <ca:LOC>Corner</ca:LOC>
+ <ca:NAM> Video Rental Store </ca:NAM>
+ <ca:PC>2500</ca:PC>
+ <ca:ROOM> Westerns and Classics </ca:ROOM>
+ <ca:PLC>store</ca:PLC>
+ <ca:POBOX>Private Box 15</ca:POBOX>
+ </ca:civicAddress>
+ </gp:location-info>
+ <gp:usage-rules>
+ <gp:retransmission-allowed>yes</gp:retransmission-allowed>
+ <gp:ruleset-preference>https:/www/more.com</gp:ruleset-preference>
+ <gp:retention-expires>2007-06-22T20:57:29Z</gp:retention-expires>
+ </gp:usage-rules>
+ <gp:method>GPS</gp:method>
+ </gp:geopriv>
+ <dm:deviceID>mac:1234567890ab</dm:deviceID>
+ <dm:timestamp>2007-06-22T20:57:29Z</dm:timestamp>
+ </dm:device>
+</presence>
diff --git a/res/res_geolocation/pidf_to_eprofile.xslt b/res/res_geolocation/pidf_to_eprofile.xslt
new file mode 100644
index 0000000..409ba9e
--- /dev/null
+++ b/res/res_geolocation/pidf_to_eprofile.xslt
@@ -0,0 +1,124 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0"
+ xmlns:ca="urn:ietf:params:xml:ns:pidf:geopriv10:civicAddr"
+ xmlns:def="urn:ietf:params:xml:ns:pidf"
+ xmlns:dm="urn:ietf:params:xml:ns:pidf:data-model"
+ xmlns:fn="http://www.w3.org/2005/xpath-functions"
+ xmlns:gbp="urn:ietf:params:xml:ns:pidf:geopriv10:basicPolicy"
+ xmlns:gml="http://www.opengis.net/gml"
+ xmlns:gp="urn:ietf:params:xml:ns:pidf:geopriv10"
+ xmlns:gs="http://www.opengis.net/pidflo/1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <!--xsl:output method="text" /-->
+ <xsl:strip-space elements="*" />
+ <xsl:param name="path"/>
+
+ <xsl:template name="name-value">
+ <xsl:value-of select="local-name(.)" /><xsl:text>="</xsl:text>
+ <xsl:value-of select="normalize-space(.)" /><xsl:text>"</xsl:text>
+ <xsl:if test="following-sibling::*"><xsl:text>, </xsl:text></xsl:if>
+ </xsl:template>
+
+ <xsl:template name="length"><xsl:call-template name="name-value" /></xsl:template>
+
+ <xsl:template name="angle">
+ <xsl:value-of select="local-name(.)" /><xsl:text>="</xsl:text>
+ <xsl:value-of select="normalize-space(.)" /><xsl:text>"</xsl:text><xsl:text>, </xsl:text>
+ <xsl:value-of select="local-name(.)" /><xsl:text>_uom="</xsl:text>
+ <xsl:choose>
+ <xsl:when test="@uom = 'urn:ogc:def:uom:EPSG::9102'"><xsl:text>radians</xsl:text></xsl:when>
+ <xsl:otherwise><xsl:text>degrees</xsl:text></xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>"</xsl:text>
+ <xsl:if test="following-sibling::*"><xsl:text>, </xsl:text></xsl:if>
+ </xsl:template>
+ <xsl:template match="gs:orientation"><xsl:call-template name="angle" /></xsl:template>
+ <xsl:template match="gs:radius"><xsl:call-template name="length" /></xsl:template>
+ <xsl:template match="gs:height"><xsl:call-template name="length" /></xsl:template>
+ <xsl:template match="gs:semiMajorAxis"><xsl:call-template name="length" /></xsl:template>
+ <xsl:template match="gs:semiMinorAxis"><xsl:call-template name="length" /></xsl:template>
+ <xsl:template match="gs:verticalAxis"><xsl:call-template name="length" /></xsl:template>
+ <xsl:template match="gs:innerRadius"><xsl:call-template name="length" /></xsl:template>
+ <xsl:template match="gs:outerRadius"><xsl:call-template name="length" /></xsl:template>
+ <xsl:template match="gs:startAngle"><xsl:call-template name="angle" /></xsl:template>
+ <xsl:template match="gs:openingAngle"><xsl:call-template name="angle" /></xsl:template>
+ <xsl:template match="gml:pos"><xsl:call-template name="name-value" /></xsl:template>
+ <xsl:template match="gml:posList"><xsl:call-template name="name-value" /></xsl:template>
+
+ <xsl:template name="shape">
+ <xsl:text>format="gml", type="</xsl:text><xsl:value-of select="local-name(.)" /><xsl:text>", </xsl:text>
+ <xsl:text>crs="</xsl:text>
+ <xsl:choose>
+ <xsl:when test="@srsName = 'urn:ogc:def:crs:EPSG::4326'"><xsl:text>2d</xsl:text></xsl:when>
+ <xsl:when test="@srsName = 'urn:ogc:def:crs:EPSG::4979'"><xsl:text>3d</xsl:text></xsl:when>
+ <xsl:otherwise><xsl:text>unknown</xsl:text></xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>", </xsl:text>
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="ca:civicAddress/*"><xsl:call-template name="name-value" /> </xsl:template>
+ <xsl:template name="civicAddress"><xsl:text>format="civicAddress", </xsl:text>
+ <xsl:apply-templates />
+ </xsl:template>
+
+ <xsl:template match="gp:location-info/gml:*">
+ <xsl:element name="location-info">
+ <xsl:attribute name="format">gml</xsl:attribute>
+ <xsl:call-template name="shape" />
+ </xsl:element>
+ </xsl:template>
+
+ <xsl:template match="gp:location-info/gs:*">
+ <xsl:element name="location-info">
+ <xsl:attribute name="format">gml</xsl:attribute>
+ <xsl:call-template name="shape" />
+ </xsl:element>
+ </xsl:template>
+
+ <xsl:template match="gp:location-info/ca:*">
+ <xsl:element name="location-info">
+ <xsl:attribute name="format">civicAddress</xsl:attribute>
+ <xsl:call-template name="civicAddress" />
+ </xsl:element>
+ </xsl:template>
+
+ <xsl:template match="gp:usage-rules/*">
+ <xsl:call-template name="name-value" />
+ </xsl:template>
+
+ <xsl:template match="gp:usage-rules">
+ <xsl:element name="usage-rules">
+ <xsl:apply-templates />
+ </xsl:element>
+ </xsl:template>
+
+ <xsl:template match="gp:method">
+ <xsl:value-of select="normalize-space(.)" />
+ </xsl:template>
+
+ <xsl:template name="topnode">
+ <xsl:element name="pidf-element">
+ <xsl:attribute name="name"><xsl:value-of select="local-name(.)"/></xsl:attribute>
+ <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
+ <xsl:apply-templates select=".//gp:location-info"/>
+ <xsl:apply-templates select=".//gp:usage-rules"/>
+ <xsl:element name="method">
+ <xsl:apply-templates select=".//gp:method"/>
+ </xsl:element>
+ </xsl:element>
+ <xsl:text>
+</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="dm:device"><xsl:call-template name="topnode" /></xsl:template>
+ <xsl:template match="def:tuple"><xsl:call-template name="topnode" /></xsl:template>
+ <xsl:template match="dm:person"><xsl:call-template name="topnode" /></xsl:template>
+
+ <xsl:template match="/">
+ <xsl:element name="presence">
+ <xsl:attribute name="entity"><xsl:value-of select="/*/@entity"/></xsl:attribute>
+ <xsl:apply-templates select="$path"/>
+ </xsl:element>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/tests/test_config.c b/tests/test_config.c
index 1a0ddaf..08c1bb1 100644
--- a/tests/test_config.c
+++ b/tests/test_config.c
@@ -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_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/+/18127
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings
Gerrit-Project: asterisk
Gerrit-Branch: development/16/geolocation
Gerrit-Change-Id: I50b66bd041b2a62ab329406f20dbaeef1fa68fc1
Gerrit-Change-Number: 18127
Gerrit-PatchSet: 5
Gerrit-Owner: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Friendly Automation
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Joshua Colp <jcolp at sangoma.com>
Gerrit-Reviewer: Kevin Harwell <kharwell at digium.com>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20220325/e9b5d171/attachment-0001.html>
More information about the asterisk-code-review
mailing list