[Asterisk-code-review] res_geolocation: Initial commit (asterisk[development/16/geolocation])
George Joseph
asteriskteam at digium.com
Fri Mar 25 06:31:22 CDT 2022
George Joseph has submitted this change. ( https://gerrit.asterisk.org/c/asterisk/+/18068 )
Change subject: res_geolocation: Initial commit
......................................................................
res_geolocation: Initial commit
This is the first commit for res_geolocation. It is in no way
complete but what is here is functional.
Change-Id: Ieb6e3640f31a676da42d8c144ebbb31ad795d849
---
A include/asterisk/res_geolocation.h
M res/Makefile
A res/res_geolocation.c
A res/res_geolocation.exports.in
A res/res_geolocation/geoloc_channel.c
A res/res_geolocation/geoloc_civicaddr.c
A res/res_geolocation/geoloc_config.c
A res/res_geolocation/geoloc_dialplan.c
A res/res_geolocation/geoloc_doc.xml
A res/res_geolocation/geoloc_gml.c
A res/res_geolocation/geoloc_private.h
11 files changed, 1,767 insertions(+), 0 deletions(-)
Approvals:
Joshua Colp: Looks good to me, but someone else must approve
George Joseph: Looks good to me, approved; Approved for Submit
Friendly Automation: Verified
diff --git a/include/asterisk/res_geolocation.h b/include/asterisk/res_geolocation.h
new file mode 100644
index 0000000..6230cf2
--- /dev/null
+++ b/include/asterisk/res_geolocation.h
@@ -0,0 +1,148 @@
+ /*
+ * 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.
+ */
+
+#ifndef INCLUDE_ASTERISK_RES_GEOLOCATION_H_
+#define INCLUDE_ASTERISK_RES_GEOLOCATION_H_
+
+#include "asterisk/sorcery.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_format {
+ AST_GEOLOC_FORMAT_NONE = 0,
+ AST_GEOLOC_FORMAT_CIVIC_ADDRESS,
+ AST_GEOLOC_FORMAT_GML,
+ AST_GEOLOC_FORMAT_URI,
+};
+
+enum ast_geoloc_location_disposition {
+ AST_GEOLOC_LOC_DISP_DISCARD = 0,
+ AST_GEOLOC_LOC_DISP_APPEND,
+ AST_GEOLOC_LOC_DISP_PREPEND,
+ AST_GEOLOC_LOC_DISP_REPLACE,
+};
+
+struct ast_geoloc_location {
+ SORCERY_OBJECT(details);
+ enum ast_geoloc_format format;
+ struct ast_variable *location_vars;
+};
+
+struct ast_geoloc_profile {
+ SORCERY_OBJECT(details);
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(location_reference);
+ );
+ enum ast_geoloc_pidf_section pidf_section;
+ enum ast_geoloc_location_disposition location_disposition;
+ int geolocation_routing;
+ int send_location;
+ struct ast_variable *location_refinement;
+ struct ast_variable *location_variables;
+ struct ast_variable *usage_rules_vars;
+};
+
+struct ast_geoloc_effective_profile {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(id);
+ AST_STRING_FIELD(location_reference);
+ );
+ enum ast_geoloc_pidf_section pidf_section;
+ enum ast_geoloc_location_disposition location_disposition;
+ int geolocation_routing;
+ int send_location;
+ enum ast_geoloc_format format;
+ struct ast_variable *location_vars;
+ struct ast_variable *location_refinement;
+ struct ast_variable *location_variables;
+ struct ast_variable *effective_location;
+ struct ast_variable *usage_rules_vars;
+};
+
+
+/*!
+ * \brief Given an official civicAddress code, return its friendly name.
+ *
+ * \param code Pointer to the code to check
+ *
+ * \return Pointer to the friendly name or NULL if code wasn't found.
+ */
+const char *ast_geoloc_civicaddr_get_name_from_code(const char *code);
+
+/*!
+ * \brief Given a civicAddress friendly name, return its official code.
+ *
+ * \param name Pointer to the name to check
+ *
+ * \return Pointer to the official code or NULL if name wasn't found.
+ */
+const char *ast_geoloc_civicaddr_get_code_from_name(const char *name);
+
+/*!
+ * \brief Given an unknown location variable, return its official civicAddress code.
+ *
+ * \param variable Pointer to the name or code to check
+ *
+ * \return Pointer to the official code or NULL if variable wasn't a name or code.
+ */
+const char *ast_geoloc_civicaddr_resolve_variable(const char *variable);
+
+enum ast_geoloc_validate_result {
+ AST_GEOLOC_VALIDATE_SUCCESS = 0,
+ AST_GEOLOC_VALIDATE_MISSING_TYPE,
+ AST_GEOLOC_VALIDATE_INVALID_TYPE,
+ AST_GEOLOC_VALIDATE_INVALID_VARNAME,
+ AST_GEOLOC_VALIDATE_NOT_ENOUGH_VARNAMES,
+ AST_GEOLOC_VALIDATE_TOO_MANY_VARNAMES,
+ AST_GEOLOC_VALIDATE_INVALID_VALUE,
+};
+
+const char *ast_geoloc_validate_result_to_str(enum ast_geoloc_validate_result result);
+
+/*!
+ * \brief Validate that the names of the variables in the list are valid codes or synonyms
+ *
+ * \param varlist Variable list to check.
+ * \param result[OUT] Pointer to char * to receive failing item.
+ *
+ * \return result code.
+ */
+enum ast_geoloc_validate_result ast_geoloc_civicaddr_validate_varlist(
+ const struct ast_variable *varlist, const char **result);
+
+/*!
+ * \brief Validate that the variables in the list represent a valid GML shape
+ *
+ * \param varlist Variable list to check.
+ * \param result[OUT] Pointer to char * to receive failing item.
+ *
+ * \return result code.
+ */
+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);
+
+struct ast_geoloc_effective_profile *ast_geoloc_effective_profile_create(struct ast_geoloc_profile *profile);
+
+#endif /* INCLUDE_ASTERISK_RES_GEOLOCATION_H_ */
diff --git a/res/Makefile b/res/Makefile
index cd6e8ad..a6dd181 100644
--- a/res/Makefile
+++ b/res/Makefile
@@ -67,6 +67,7 @@
$(call MOD_ADD_C,res_ari_model,ari/ari_model_validators.c)
$(call MOD_ADD_C,res_stasis_recording,stasis_recording/stored.c)
$(call MOD_ADD_C,res_stir_shaken,$(wildcard res_stir_shaken/*.c))
+$(call MOD_ADD_C,res_geolocation,$(wildcard res_geolocation/*.c))
res_parking.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
snmp/agent.o: _ASTCFLAGS+=-fPIC
diff --git a/res/res_geolocation.c b/res/res_geolocation.c
new file mode 100644
index 0000000..09a9a4b
--- /dev/null
+++ b/res/res_geolocation.c
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+
+#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;
+
+ res = geoloc_civicaddr_reload();
+ if (res) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ res = geoloc_gml_reload();
+ if (res) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ res = geoloc_config_reload();
+ if (res) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ res = geoloc_dialplan_reload();
+ if (res) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ res = geoloc_channel_reload();
+ if (res) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res += geoloc_channel_reload();
+ res += geoloc_dialplan_unload();
+ res += geoloc_config_unload();
+ res += geoloc_gml_unload();
+ res += geoloc_civicaddr_unload();
+
+ return (res != 0);
+}
+
+static int load_module(void)
+{
+ 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_config_load();
+ if (res) {
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ res = geoloc_dialplan_load();
+ if (res) {
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ res = geoloc_channel_load();
+ if (res) {
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "res_geolocation Module for Asterisk",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND - 10,
+);
diff --git a/res/res_geolocation.exports.in b/res/res_geolocation.exports.in
new file mode 100644
index 0000000..da0a981
--- /dev/null
+++ b/res/res_geolocation.exports.in
@@ -0,0 +1,6 @@
+{
+ global:
+ LINKER_SYMBOL_PREFIXast_geo*;
+ local:
+ *;
+};
diff --git a/res/res_geolocation/geoloc_channel.c b/res/res_geolocation/geoloc_channel.c
new file mode 100644
index 0000000..719e214
--- /dev/null
+++ b/res/res_geolocation/geoloc_channel.c
@@ -0,0 +1,92 @@
+/*
+ * 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/datastore.h"
+#include "geoloc_private.h"
+
+
+struct ast_sorcery *geoloc_sorcery;
+
+static void geoloc_datastore_destructor(void *obj)
+{
+ struct ast_geoloc_effective_profile *eprofile = obj;
+ ao2_ref(eprofile, -1);
+}
+
+
+static const struct ast_datastore_info geoloc_datastore_info = {
+ .type = "geolocation",
+ .destroy = geoloc_datastore_destructor
+};
+
+
+struct ast_datastore *ast_geoloc_datastore_create(const char *profile_name)
+{
+ struct ast_datastore *ds = NULL;
+ struct ast_geoloc_effective_profile *eprofile = NULL;
+ struct ast_geoloc_profile *profile = NULL;
+
+ if (ast_strlen_zero(profile_name)) {
+ return NULL;
+ }
+
+ ds = ast_datastore_alloc(&geoloc_datastore_info, NULL);
+ if (!ds) {
+ ast_log(LOG_ERROR, "A datasatore couldn't be allocated for profile '%s'", profile_name);
+ return NULL;
+ }
+
+ profile = ast_sorcery_retrieve_by_id(geoloc_sorcery, "profile", profile_name);
+ if (!profile) {
+ ast_datastore_free(ds);
+ ast_log(LOG_ERROR, "A profile with the name '%s' was not found", profile_name);
+ return NULL;
+ }
+
+ eprofile = ast_geoloc_effective_profile_create(profile);
+ ao2_ref(profile, -1);
+ if (!eprofile) {
+ ast_datastore_free(ds);
+ ast_log(LOG_ERROR, "An effective profile with the name '%s' couldn't be allocated", profile_name);
+ return NULL;
+ }
+
+ ds->data = eprofile;
+
+ return ds;
+}
+
+int geoloc_channel_unload(void)
+{
+ if (geoloc_sorcery) {
+ ast_sorcery_unref(geoloc_sorcery);
+ }
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+int geoloc_channel_load(void)
+{
+ geoloc_sorcery = geoloc_get_sorcery();
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+int geoloc_channel_reload(void)
+{
+ return AST_MODULE_LOAD_SUCCESS;
+}
diff --git a/res/res_geolocation/geoloc_civicaddr.c b/res/res_geolocation/geoloc_civicaddr.c
new file mode 100644
index 0000000..6816ebd
--- /dev/null
+++ b/res/res_geolocation/geoloc_civicaddr.c
@@ -0,0 +1,173 @@
+/*
+ * 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/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/res_geolocation.h"
+#include "geoloc_private.h"
+
+struct addr_field_entry {
+ const char *code;
+ const char *name;
+};
+
+static struct addr_field_entry addr_code_name_entries[] = {
+ {"A1", "state_province"},
+ {"A2", "county_district"},
+ {"A3", "city"},
+ {"A4", "city_district"},
+ {"A5", "neighborhood"},
+ {"A6", "street_group"},
+ {"ADDCODE", "additional_code"},
+ {"BLD", "building"},
+ {"country", "country"},
+ {"FLR", "floor"},
+ {"HNO", "house_number"},
+ {"HNS", "house_number_suffix"},
+ {"LMK", "landmark"},
+ {"LOC", "additional_location"},
+ {"NAM", "location_name"},
+ {"PC", "postal_code"},
+ {"PCN", "postal_community"},
+ {"PLC", "place_type"},
+ {"POBOX", "po_box"},
+ {"POD", "trailing_street_suffix"},
+ {"POM", "road_post_modifier"},
+ {"PRD", "leading_road_direction"},
+ {"PRM", "road_pre_modifier"},
+ {"RD", "road"},
+ {"RD", "street"},
+ {"RDBR", "road_branch"},
+ {"RDSEC", "road_section"},
+ {"RDSUBBR", "road_sub_branch"},
+ {"ROOM", "room"},
+ {"SEAT", "seat"},
+ {"STS", "street_suffix"},
+ {"UNIT", "unit"},
+};
+
+static struct addr_field_entry addr_name_code_entries[ARRAY_LEN(addr_code_name_entries)];
+
+static int compare_civicaddr_codes(const void *_a, const void *_b)
+{
+ const struct addr_field_entry *a = _a;
+ const struct addr_field_entry *b = _b;
+
+ return strcmp(a->code, b->code);
+}
+
+static int compare_civicaddr_names(const void *_a, const void *_b)
+{
+ const struct addr_field_entry *a = _a;
+ const struct addr_field_entry *b = _b;
+
+ return strcmp(a->name, b->name);
+}
+
+const char *ast_geoloc_civicaddr_get_name_from_code(const char *code)
+{
+ struct addr_field_entry key = { .code = code };
+ struct addr_field_entry *entry = bsearch(&key, addr_code_name_entries, ARRAY_LEN(addr_code_name_entries),
+ sizeof(struct addr_field_entry), compare_civicaddr_codes);
+ return entry ? entry->name : NULL;
+}
+
+const char *ast_geoloc_civicaddr_get_code_from_name(const char *name)
+{
+ struct addr_field_entry key = { .name = name };
+ struct addr_field_entry *entry = bsearch(&key, addr_name_code_entries, ARRAY_LEN(addr_name_code_entries),
+ sizeof(struct addr_field_entry), compare_civicaddr_names);
+ return entry ? entry->code : NULL;
+}
+
+const char *ast_geoloc_civicaddr_resolve_variable(const char *variable)
+{
+ const char *result = ast_geoloc_civicaddr_get_name_from_code(variable);
+ if (result) {
+ return result;
+ }
+ return ast_geoloc_civicaddr_get_code_from_name(variable);
+}
+
+enum ast_geoloc_validate_result ast_geoloc_civicaddr_validate_varlist(
+ const struct ast_variable *varlist, const char **result)
+{
+ const struct ast_variable *var = varlist;
+ for (; var; var = var->next) {
+ const char *newname = ast_geoloc_civicaddr_resolve_variable(var->name);
+ if (!newname) {
+ *result = var->name;
+ return AST_GEOLOC_VALIDATE_INVALID_VARNAME;
+ }
+ }
+ return AST_GEOLOC_VALIDATE_SUCCESS;
+}
+
+static char *handle_civicaddr_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "geoloc show civicAddr_mappings";
+ e->usage =
+ "Usage: geoloc show civicAddr_mappings\n"
+ " Show the mappings between civicAddress official codes and synonyms.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, "%-16s %-32s\n", "Official Code", "Synonym");
+ ast_cli(a->fd, "================ ================================\n");
+
+ for (i = 0; i < ARRAY_LEN(addr_code_name_entries); i++) {
+ ast_cli(a->fd, "%-16s %-32s\n", addr_code_name_entries[i].code, addr_code_name_entries[i].name);
+ }
+ ast_cli(a->fd, "\n");
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry geoloc_civicaddr_cli[] = {
+ AST_CLI_DEFINE(handle_civicaddr_show, "Show the mappings between civicAddress official codes and synonyms"),
+};
+
+int geoloc_civicaddr_unload(void)
+{
+ ast_cli_unregister_multiple(geoloc_civicaddr_cli, ARRAY_LEN(geoloc_civicaddr_cli));
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+int geoloc_civicaddr_load(void)
+{
+ memcpy(addr_name_code_entries, addr_code_name_entries, sizeof(addr_code_name_entries));
+ qsort(addr_code_name_entries, ARRAY_LEN(addr_code_name_entries), sizeof(struct addr_field_entry), compare_civicaddr_codes);
+ qsort(addr_name_code_entries, ARRAY_LEN(addr_name_code_entries), sizeof(struct addr_field_entry), compare_civicaddr_names);
+
+ ast_cli_register_multiple(geoloc_civicaddr_cli, ARRAY_LEN(geoloc_civicaddr_cli));
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+int geoloc_civicaddr_reload(void)
+{
+ return AST_MODULE_LOAD_SUCCESS;
+}
diff --git a/res/res_geolocation/geoloc_config.c b/res/res_geolocation/geoloc_config.c
new file mode 100644
index 0000000..c2fa2d0
--- /dev/null
+++ b/res/res_geolocation/geoloc_config.c
@@ -0,0 +1,653 @@
+/*
+ * 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/module.h"
+#include "asterisk/cli.h"
+#include "geoloc_private.h"
+#include "asterisk/res_geolocation.h"
+
+static struct ast_sorcery *geoloc_sorcery;
+
+static const char *pidf_section_names[] = {
+ "<none>",
+ "tuple",
+ "device",
+ "person"
+};
+
+static const char *format_names[] = {
+ "<none>",
+ "civicAddress",
+ "GML",
+ "URI",
+};
+
+static const char * location_disposition_names[] = {
+ "discard",
+ "append",
+ "prepend",
+ "replace"
+};
+
+CONFIG_ENUM(location, format)
+CONFIG_VAR_LIST(location, location_vars)
+
+static void geoloc_location_destructor(void *obj) {
+ struct ast_geoloc_location *location = obj;
+
+ 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);
+}
+
+
+CONFIG_ENUM(profile, pidf_section)
+CONFIG_ENUM(profile, location_disposition)
+CONFIG_VAR_LIST(profile, location_refinement)
+CONFIG_VAR_LIST(profile, location_variables)
+CONFIG_VAR_LIST(profile, usage_rules_vars)
+
+
+static void geoloc_profile_destructor(void *obj) {
+ struct ast_geoloc_profile *profile = obj;
+
+ ast_string_field_free_memory(profile);
+ ast_variables_destroy(profile->location_refinement);
+ ast_variables_destroy(profile->location_variables);
+ ast_variables_destroy(profile->usage_rules_vars);
+}
+
+static void *geoloc_profile_alloc(const char *name)
+{
+ struct ast_geoloc_profile *profile = ast_sorcery_generic_alloc(sizeof(*profile), geoloc_profile_destructor);
+ if (profile) {
+ ast_string_field_init(profile, 128);
+ }
+
+ 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;
+ const char *location_id = ast_sorcery_object_get_id(location);
+ const char *failed;
+ const char *uri;
+ enum ast_geoloc_validate_result result;
+
+ switch (location->format) {
+ case AST_GEOLOC_FORMAT_NONE:
+ ast_log(LOG_ERROR, "Location '%s' must have a format\n", location_id);
+ return -1;
+ case AST_GEOLOC_FORMAT_CIVIC_ADDRESS:
+ result = ast_geoloc_civicaddr_validate_varlist(location->location_vars, &failed);
+ if (result != AST_GEOLOC_VALIDATE_SUCCESS) {
+ ast_log(LOG_ERROR, "Location '%s' has invalid item '%s' in the location\n",
+ location_id, failed);
+ return -1;
+ }
+ break;
+ case AST_GEOLOC_FORMAT_GML:
+ result = ast_geoloc_gml_validate_varlist(location->location_vars, &failed);
+ if (result != AST_GEOLOC_VALIDATE_SUCCESS) {
+ ast_log(LOG_ERROR, "%s for item '%s' in location '%s'\n",
+ ast_geoloc_validate_result_to_str(result), failed, location_id);
+ return -1;
+ }
+
+ break;
+ case AST_GEOLOC_FORMAT_URI:
+ uri = ast_variable_find_in_list(location->location_vars, "URI");
+ if (!uri) {
+ struct ast_str *str = ast_variable_list_join(location->location_vars, ",", "=", "\"", NULL);
+
+ ast_log(LOG_ERROR, "Geolocation location '%s' format is set to '%s' but no 'URI' was found in location parameter '%s'\n",
+ location_id, format_names[AST_GEOLOC_FORMAT_URI], ast_str_buffer(str));
+ ast_free(str);
+ return -1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int geoloc_profile_apply_handler(const struct ast_sorcery *sorcery, void *obj)
+{
+ struct ast_geoloc_profile *profile = obj;
+ struct ast_geoloc_location *location;
+ const char *profile_id = ast_sorcery_object_get_id(profile);
+ const char *failed;
+ enum ast_geoloc_validate_result result;
+
+ if (ast_strlen_zero(profile->location_reference)) {
+ if (profile->location_refinement ||
+ profile->location_variables) {
+ ast_log(LOG_ERROR, "Profile '%s' can't have location_refinement or location_variables without a location_reference",
+ profile_id);
+ return -1;
+ }
+ return 0;
+ }
+
+ location = ast_sorcery_retrieve_by_id(geoloc_sorcery, "location", profile->location_reference);
+ if (!location) {
+ ast_log(LOG_ERROR, "Profile '%s' has a location_reference '%s' that doesn't exist",
+ profile_id, profile->location_reference);
+ return -1;
+ }
+
+ if (profile->location_refinement) {
+ switch (location->format) {
+ case AST_GEOLOC_FORMAT_NONE:
+ break;
+ case AST_GEOLOC_FORMAT_CIVIC_ADDRESS:
+ result = ast_geoloc_civicaddr_validate_varlist(profile->location_refinement, &failed);
+ if (result != AST_GEOLOC_VALIDATE_SUCCESS) {
+ ast_log(LOG_ERROR, "Profile '%s' error: %s: for item '%s' in the location_refinement\n",
+ profile_id, ast_geoloc_validate_result_to_str(result), failed);
+ ao2_ref(location, -1);
+ return -1;
+ }
+ break;
+ case AST_GEOLOC_FORMAT_GML:
+ break;
+ case AST_GEOLOC_FORMAT_URI:
+ break;
+ }
+ }
+ ao2_ref(location, -1);
+
+ return 0;
+}
+
+struct ast_sorcery *geoloc_get_sorcery(void)
+{
+ ast_sorcery_ref(geoloc_sorcery);
+ return geoloc_sorcery;
+}
+
+static char *geoloc_config_list_locations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ao2_iterator iter;
+ struct ao2_container *sorted_container;
+ struct ao2_container *unsorted_container;
+ struct ast_geoloc_location *loc;
+ int using_regex = 0;
+ char *result = CLI_SUCCESS;
+ int ret = 0;
+ char *format_name;
+ int count = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "geoloc list locations";
+ e->usage = "Usage: geoloc list locations [ like <pattern> ]\n"
+ " List Geolocation Location Objects\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3 && a->argc != 5) {
+ return CLI_SHOWUSAGE;
+ }
+
+ if (a->argc == 5) {
+ if (strcasecmp(a->argv[3], "like")) {
+ return CLI_SHOWUSAGE;
+ }
+ using_regex = 1;
+ }
+
+ sorted_container = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
+ ast_sorcery_object_id_sort, NULL);
+ if (!sorted_container) {
+ ast_cli(a->fd, "Geolocation Location Objects: Unable to allocate temporary container\n");
+ return CLI_FAILURE;
+ }
+
+ /* Get a sorted snapshot of the scheduled tasks */
+ if (using_regex) {
+ unsorted_container = ast_sorcery_retrieve_by_regex(geoloc_sorcery, "location", a->argv[4]);
+ } else {
+ unsorted_container = ast_sorcery_retrieve_by_fields(geoloc_sorcery, "location",
+ AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+ }
+
+ ret = ao2_container_dup(sorted_container, unsorted_container, 0);
+ ao2_ref(unsorted_container, -1);
+ if (ret != 0) {
+ ao2_ref(sorted_container, -1);
+ ast_cli(a->fd, "Geolocation Location Objects: Unable to sort temporary container\n");
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd, "Geolocation Location Objects:\n\n");
+
+ ast_cli(a->fd,
+ "<Object ID...................................> <Format.....> <Details.............>\n"
+ "===================================================================================\n");
+
+ iter = ao2_iterator_init(sorted_container, AO2_ITERATOR_UNLINK);
+ for (; (loc = ao2_iterator_next(&iter)); ao2_ref(loc, -1)) {
+ struct ast_str *str;
+
+ ao2_lock(loc);
+ str = ast_variable_list_join(loc->location_vars, ",", "=", "\"", NULL);
+ if (!str) {
+ ao2_unlock(loc);
+ ao2_ref(loc, -1);
+ ast_cli(a->fd, "Geolocation Location Objects: Unable to allocate temp string for '%s'\n",
+ ast_sorcery_object_get_id(loc));
+ result = CLI_FAILURE;
+ break;
+ }
+
+ format_to_str(loc, NULL, &format_name);
+ ast_cli(a->fd, "%-46.46s %-13s %-s\n",
+ ast_sorcery_object_get_id(loc),
+ format_name,
+ ast_str_buffer(str));
+ ao2_unlock(loc);
+ ast_free(str);
+ ast_free(format_name);
+ count++;
+ }
+ ao2_iterator_destroy(&iter);
+ ao2_ref(sorted_container, -1);
+ ast_cli(a->fd, "\nTotal Location Objects: %d\n\n", count);
+
+ return result;
+}
+
+static char *geoloc_config_list_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ao2_iterator iter;
+ struct ao2_container *sorted_container;
+ struct ao2_container *unsorted_container;
+ struct ast_geoloc_profile *profile;
+ int using_regex = 0;
+ char *result = CLI_SUCCESS;
+ int ret = 0;
+ char *disposition;
+ int count = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "geoloc list profiles";
+ e->usage = "Usage: geoloc list profiles [ like <pattern> ]\n"
+ " List Geolocation Profile Objects\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3 && a->argc != 5) {
+ return CLI_SHOWUSAGE;
+ }
+
+ if (a->argc == 5) {
+ if (strcasecmp(a->argv[3], "like")) {
+ return CLI_SHOWUSAGE;
+ }
+ using_regex = 1;
+ }
+
+ sorted_container = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
+ ast_sorcery_object_id_sort, NULL);
+ if (!sorted_container) {
+ ast_cli(a->fd, "Geolocation Profile Objects: Unable to allocate temporary container\n");
+ return CLI_FAILURE;
+ }
+
+ /* Get a sorted snapshot of the scheduled tasks */
+ if (using_regex) {
+ unsorted_container = ast_sorcery_retrieve_by_regex(geoloc_sorcery, "profile", a->argv[4]);
+ } else {
+ unsorted_container = ast_sorcery_retrieve_by_fields(geoloc_sorcery, "profile",
+ AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+ }
+
+ ret = ao2_container_dup(sorted_container, unsorted_container, 0);
+ ao2_ref(unsorted_container, -1);
+ if (ret != 0) {
+ ao2_ref(sorted_container, -1);
+ ast_cli(a->fd, "Geolocation Profile Objects: Unable to sort temporary container\n");
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd, "Geolocation Profile Objects:\n\n");
+
+ ast_cli(a->fd,
+ "<Object ID...................................> <Disposition> <Send> <Location Reference> \n"
+ "=========================================================================================\n");
+
+ iter = ao2_iterator_init(sorted_container, AO2_ITERATOR_UNLINK);
+ for (; (profile = ao2_iterator_next(&iter)); ao2_ref(profile, -1)) {
+ ao2_lock(profile);
+
+ location_disposition_to_str(profile, NULL, &disposition);
+ ast_cli(a->fd, "%-46.46s %-13s %-6s %-s\n",
+ ast_sorcery_object_get_id(profile),
+ disposition,
+ profile->send_location ? "yes" : "no",
+ profile->location_reference);
+ ao2_unlock(profile);
+ ast_free(disposition);
+ count++;
+ }
+ ao2_iterator_destroy(&iter);
+ ao2_ref(sorted_container, -1);
+ ast_cli(a->fd, "\nTotal Profile Objects: %d\n\n", count);
+
+ return result;
+}
+
+static char *geoloc_config_show_profiles(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ao2_iterator iter;
+ struct ao2_container *sorted_container;
+ struct ao2_container *unsorted_container;
+ struct ast_geoloc_profile *profile;
+ int using_regex = 0;
+ char *result = CLI_SUCCESS;
+ int ret = 0;
+ int count = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "geoloc show profiles";
+ e->usage = "Usage: geoloc show profiles [ like <pattern> ]\n"
+ " List Geolocation Profile Objects\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3 && a->argc != 5) {
+ return CLI_SHOWUSAGE;
+ }
+
+ if (a->argc == 5) {
+ if (strcasecmp(a->argv[3], "like")) {
+ return CLI_SHOWUSAGE;
+ }
+ using_regex = 1;
+ }
+
+ sorted_container = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
+ ast_sorcery_object_id_sort, NULL);
+ if (!sorted_container) {
+ ast_cli(a->fd, "Geolocation Profile Objects: Unable to allocate temporary container\n");
+ return CLI_FAILURE;
+ }
+
+ /* Get a sorted snapshot of the scheduled tasks */
+ if (using_regex) {
+ unsorted_container = ast_sorcery_retrieve_by_regex(geoloc_sorcery, "profile", a->argv[4]);
+ } else {
+ unsorted_container = ast_sorcery_retrieve_by_fields(geoloc_sorcery, "profile",
+ AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
+ }
+
+ ret = ao2_container_dup(sorted_container, unsorted_container, 0);
+ ao2_ref(unsorted_container, -1);
+ if (ret != 0) {
+ ao2_ref(sorted_container, -1);
+ ast_cli(a->fd, "Geolocation Profile Objects: Unable to sort temporary container\n");
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd, "Geolocation Profile Objects:\n\n");
+
+ iter = ao2_iterator_init(sorted_container, AO2_ITERATOR_UNLINK);
+ for (; (profile = ao2_iterator_next(&iter)); ) {
+ char *disposition = NULL;
+ struct ast_str *loc_str = NULL;
+ 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);
+ ao2_ref(profile, -1);
+
+ if (!ast_strlen_zero(eprofile->location_reference)) {
+ loc_str = ast_variable_list_join(eprofile->location_vars, ",", "=", "\"", NULL);
+ resolved_str = ast_variable_list_join(eprofile->effective_location, ",", "=", "\"", NULL);
+ }
+
+ refinement_str = ast_variable_list_join(eprofile->location_refinement, ",", "=", "\"", NULL);
+ variables_str = ast_variable_list_join(eprofile->location_variables, ",", "=", "\"", NULL);
+
+ location_disposition_to_str(eprofile, NULL, &disposition);
+
+ ast_cli(a->fd,
+ "id: %-s\n"
+ "received_location_disposition: %-s\n"
+ "send_location: %-s\n"
+ "pidf_section: %-s\n"
+ "location_reference: %-s\n"
+ "Location_format: %-s\n"
+ "location_reference_details: %-s\n"
+ "location_refinement: %-s\n"
+ "location_variables: %-s\n"
+ "effective_location: %-s\n\n",
+ eprofile->id,
+ disposition,
+ eprofile->send_location ? "yes" : "no",
+ pidf_section_names[eprofile->pidf_section],
+ S_OR(eprofile->location_reference, "<none>"),
+ format_names[eprofile->format],
+ S_COR(loc_str, ast_str_buffer(loc_str), "<none>"),
+ S_COR(refinement_str, ast_str_buffer(refinement_str), "<none>"),
+ S_COR(variables_str, ast_str_buffer(variables_str), "<none>"),
+ S_COR(resolved_str, ast_str_buffer(resolved_str), "<none>"));
+ ao2_ref(eprofile, -1);
+
+ ast_free(disposition);
+ ast_free(loc_str);
+ ast_free(refinement_str);
+ ast_free(variables_str);
+ ast_free(resolved_str);
+ count++;
+ }
+ ao2_iterator_destroy(&iter);
+ ao2_ref(sorted_container, -1);
+ ast_cli(a->fd, "\nTotal Profile Objects: %d\n\n", count);
+
+ return result;
+}
+
+static char *geoloc_config_cli_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *result = CLI_SUCCESS;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "geoloc reload";
+ e->usage = "Usage: geoloc reload\n"
+ " Reload Geolocation Configuration\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2) {
+ return CLI_SHOWUSAGE;
+ }
+
+ geoloc_config_reload();
+ ast_cli(a->fd, "Geolocation Configuration reloaded.\n");
+
+ return result;
+}
+
+static struct ast_cli_entry geoloc_location_cli_commands[] = {
+ AST_CLI_DEFINE(geoloc_config_list_locations, "List Geolocation Location Objects"),
+ AST_CLI_DEFINE(geoloc_config_list_profiles, "List Geolocation Profile Objects"),
+ AST_CLI_DEFINE(geoloc_config_show_profiles, "Show Geolocation Profile Objects"),
+ AST_CLI_DEFINE(geoloc_config_cli_reload, "Reload Geolocation Configuration"),
+};
+
+int geoloc_config_reload(void)
+{
+ if (geoloc_sorcery) {
+ ast_sorcery_reload(geoloc_sorcery);
+ }
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+int geoloc_config_unload(void)
+{
+ ast_cli_unregister_multiple(geoloc_location_cli_commands, ARRAY_LEN(geoloc_location_cli_commands));
+
+ if (geoloc_sorcery) {
+ ast_sorcery_unref(geoloc_sorcery);
+ }
+ geoloc_sorcery = NULL;
+
+ return 0;
+}
+
+int geoloc_config_load(void)
+{
+ if (!(geoloc_sorcery = ast_sorcery_open())) {
+ ast_log(LOG_ERROR, "Failed to open geolocation sorcery\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ ast_sorcery_apply_default(geoloc_sorcery, "location", "config", "geolocation.conf,criteria=type=location");
+ if (ast_sorcery_object_register(geoloc_sorcery, "location", geoloc_location_alloc, NULL, geoloc_location_apply_handler)) {
+ ast_log(LOG_ERROR, "Failed to register geoloc location object with sorcery\n");
+ ast_sorcery_unref(geoloc_sorcery);
+ geoloc_sorcery = NULL;
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ ast_sorcery_object_field_register(geoloc_sorcery, "location", "type", "", OPT_NOOP_T, 0, 0);
+ ast_sorcery_object_field_register_custom(geoloc_sorcery, "location", "format", AST_GEOLOC_FORMAT_NONE,
+ format_handler, format_to_str, NULL, 0, 0);
+ ast_sorcery_object_field_register_custom(geoloc_sorcery, "location", "location", NULL,
+ location_vars_handler, location_vars_to_str, location_vars_dup, 0, 0);
+
+ ast_sorcery_apply_default(geoloc_sorcery, "profile", "config", "geolocation.conf,criteria=type=profile");
+ if (ast_sorcery_object_register(geoloc_sorcery, "profile", geoloc_profile_alloc, NULL, geoloc_profile_apply_handler)) {
+ ast_log(LOG_ERROR, "Failed to register geoloc profile object with sorcery\n");
+ ast_sorcery_unref(geoloc_sorcery);
+ geoloc_sorcery = NULL;
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ 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(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",
+ location_disposition_handler, location_disposition_to_str, NULL, 0, 0);
+ ast_sorcery_object_field_register(geoloc_sorcery, "profile", "send_location", "no",
+ OPT_BOOL_T, 1, FLDSET(struct ast_geoloc_profile, send_location));
+ ast_sorcery_object_field_register_custom(geoloc_sorcery, "profile", "usage_rules", NULL,
+ usage_rules_vars_handler, usage_rules_vars_to_str, usage_rules_vars_dup, 0, 0);
+ ast_sorcery_object_field_register_custom(geoloc_sorcery, "profile", "location_refinement", NULL,
+ location_refinement_handler, location_refinement_to_str, location_refinement_dup, 0, 0);
+ ast_sorcery_object_field_register_custom(geoloc_sorcery, "profile", "location_variables", NULL,
+ location_variables_handler, location_variables_to_str, location_variables_dup, 0, 0);
+
+ ast_sorcery_load(geoloc_sorcery);
+
+ ast_cli_register_multiple(geoloc_location_cli_commands, ARRAY_LEN(geoloc_location_cli_commands));
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
diff --git a/res/res_geolocation/geoloc_dialplan.c b/res/res_geolocation/geoloc_dialplan.c
new file mode 100644
index 0000000..776441c
--- /dev/null
+++ b/res/res_geolocation/geoloc_dialplan.c
@@ -0,0 +1,38 @@
+/*
+ * 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/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/res_geolocation.h"
+#include "geoloc_private.h"
+
+int geoloc_dialplan_unload(void)
+{
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+int geoloc_dialplan_load(void)
+{
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+int geoloc_dialplan_reload(void)
+{
+ return AST_MODULE_LOAD_SUCCESS;
+}
diff --git a/res/res_geolocation/geoloc_doc.xml b/res/res_geolocation/geoloc_doc.xml
new file mode 100644
index 0000000..36bfa3c
--- /dev/null
+++ b/res/res_geolocation/geoloc_doc.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE docs SYSTEM "appdocsxml.dtd">
+<docs>
+ <configInfo name="res_geolocation" language="en_US">
+ <synopsis>Core Geolocation Support</synopsis>
+ <configFile name="geolocation.conf">
+ <configObject name="location">
+ <synopsis>Location</synopsis>
+ <description>
+ <para>cffdffff</para>
+ </description>
+ <configOption name="type">
+ <synopsis>Must be of type 'location'.</synopsis>
+ </configOption>
+ <configOption name="format" default="">
+ <synopsis>Location specification type</synopsis>
+ <description>
+ <enumlist>
+ <enum name="civicAddress">
+ <para>
+ The
+ <literal>location</literal>
+ parameter must contain a comma separated list of IANA codes
+ describing this location.
+ </para>
+ </enum>
+ <enum name="GML">
+ <para>
+ The
+ <literal>location</literal> parameter must contain a comma
+ separated list valid GML elements describing this location.
+ </para>
+ </enum>
+ <enum name="URI">
+ <para>
+ The
+ <literal>location</literal> parameter must contain a single
+ URI parameter which contains an external URI describing this location.
+ </para>
+ </enum>
+ </enumlist>
+ </description>
+ </configOption>
+ <configOption name="location" default="">
+ <synopsis>Location Data</synopsis>
+ <description>
+ <para>The contents of this parameter are specific to the
+ specification type.</para>
+ </description>
+ </configOption>
+ </configObject>
+ <configObject name="profile">
+ <synopsis>Profile</synopsis>
+ <description>
+ <para>cffdffff</para>
+ </description>
+ <configOption name="type">
+ <synopsis>Must be of type 'profile'.</synopsis>
+ </configOption>
+ <configOption name="pidf_lo_section" default="device">
+ <synopsis>PIDF-LO element to place this profile in</synopsis>
+ <description>
+ <enumlist>
+ <enum name="tuple" />
+ <enum name="device" />
+ <enum name="person" />
+ </enumlist>
+ <para>
+ Based on RFC5491 (see below) the recommended and default element
+ is <literal>device</literal>.
+ </para>
+ </description>
+ <see-also>
+ <ref type="link">https://www.rfc-editor.org/rfc/rfc5491.html#section-3.4</ref>
+ </see-also>
+ </configOption>
+ <configOption name="location_reference" default="">
+ <synopsis>Reference to a location object</synopsis>
+ </configOption>
+ <configOption name="location_refinement" default="">
+ <synopsis>Reference to a location object</synopsis>
+ </configOption>
+ <configOption name="location_variables" default="">
+ <synopsis>Reference to a location object</synopsis>
+ </configOption>
+ <configOption name="usage_rules" default="yes">
+ <synopsis>location specification type</synopsis>
+ <description>
+ <para>xxxx</para>
+ </description>
+ </configOption>
+ <configOption name="received_location_disposition" default="no">
+ <synopsis>Determine whether the location information supplied to a
+ channel should be used</synopsis>
+ <description>
+ <enumlist>
+ <enum name="discard"/>
+ <enum name="append"/>
+ <enum name="prepend"/>
+ <enum name="replace"/>
+ </enumlist>
+ </description>
+ </configOption>
+ <configOption name="send_location" default="no">
+ <synopsis>Determine whether the channel will send location
+ information</synopsis>
+ <description>
+ <para>
+ For an incoming call leg, this determines if location information
+ will be forwarded to the dialplan. If <replaceable>accept_location</replaceable>
+ is set to <literal>yes</literal> and location information was actually
+ received from the client, it will be forwarded to the dialplan and any location
+ information in this profile will be ignored. If <replaceable>accept_location</replaceable>
+ is set to <literal>no</literal> or no location information was received
+ from the client, location information from this profile, if any, will be
+ forwarded to the dialplan. Dialplan functions can modify this information before
+ forwarding to the outgoing call leg.
+ </para>
+ <para>
+ For an outgoing call leg, this determines if location information
+ will be forwarded to the remote UAS. If
+ <replaceable>accept_location</replaceable>
+ is set to <literal>yes</literal> and location information was actually received from
+ the dialplan, it will be sent and any location information in this
+ profile will be ignored. If <replaceable>accept_location</replaceable>
+ is set to <literal>no</literal> or no location information was received from
+ the dialplan, location information from this profile, if any, will be sent.
+ </para>
+ </description>
+ <see-also>
+ <ref type="configOption">accept_location</ref>
+ </see-also>
+ </configOption>
+ </configObject>
+ </configFile>
+ </configInfo>
+</docs>
+
diff --git a/res/res_geolocation/geoloc_gml.c b/res/res_geolocation/geoloc_gml.c
new file mode 100644
index 0000000..04e363d
--- /dev/null
+++ b/res/res_geolocation/geoloc_gml.c
@@ -0,0 +1,255 @@
+/*
+ * 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/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/res_geolocation.h"
+#include "geoloc_private.h"
+
+
+#if 1 //not used yet.
+enum geoloc_shape_attrs {
+ GEOLOC_SHAPE_ATTR_POS = 0,
+ GEOLOC_SHAPE_ATTR_POS3D,
+ GEOLOC_SHAPE_ATTR_RADIUS,
+ GEOLOC_SHAPE_ATTR_SEMI_MAJOR_AXIS,
+ GEOLOC_SHAPE_ATTR_SEMI_MINOR_AXIS,
+ GEOLOC_SHAPE_ATTR_VERTICAL_AXIS,
+ GEOLOC_SHAPE_ATTR_HEIGHT,
+ GEOLOC_SHAPE_ATTR_ORIENTATION,
+ GEOLOC_SHAPE_ATTR_ORIENTATION_UOM,
+ GEOLOC_SHAPE_ATTR_INNER_RADIUS,
+ GEOLOC_SHAPE_ATTR_OUTER_RADIUS,
+ GEOLOC_SHAPE_ATTR_STARTING_ANGLE,
+ GEOLOC_SHAPE_ATTR_OPENING_ANGLE,
+ GEOLOC_SHAPE_ATTR_ANGLE_UOM,
+};
+
+struct geoloc_gml_attr_def {
+ enum geoloc_shape_attrs attr;
+ const char *name;
+ int (*validator)(const char *value);
+ int (*transformer)(struct ast_variable *value);
+};
+
+struct geoloc_gml_attr_def gml_attr_defs[] = {
+ { GEOLOC_SHAPE_ATTR_POS, "pos", NULL, NULL},
+ { GEOLOC_SHAPE_ATTR_POS3D,"pos3d", NULL, NULL},
+ { GEOLOC_SHAPE_ATTR_RADIUS,"radius", NULL, NULL},
+ { GEOLOC_SHAPE_ATTR_SEMI_MAJOR_AXIS,"semiMajorAxis", NULL, NULL},
+ { GEOLOC_SHAPE_ATTR_SEMI_MINOR_AXIS,"semiMinorAxis", NULL, NULL},
+ { GEOLOC_SHAPE_ATTR_VERTICAL_AXIS,"verticalAxis", NULL, NULL},
+ { GEOLOC_SHAPE_ATTR_HEIGHT,"height", NULL, NULL},
+ { GEOLOC_SHAPE_ATTR_ORIENTATION,"orientation", NULL, NULL},
+ { GEOLOC_SHAPE_ATTR_ORIENTATION_UOM,"orientation_uom", NULL, NULL},
+ { GEOLOC_SHAPE_ATTR_INNER_RADIUS,"innerRadius", NULL, NULL},
+ { GEOLOC_SHAPE_ATTR_OUTER_RADIUS,"outerRadius", NULL, NULL},
+ { GEOLOC_SHAPE_ATTR_STARTING_ANGLE,"startingAngle", NULL, NULL},
+ { GEOLOC_SHAPE_ATTR_OPENING_ANGLE,"openingAngle", NULL, NULL},
+ { GEOLOC_SHAPE_ATTR_ANGLE_UOM,"angle_uom", NULL, NULL},
+};
+#endif //not used yet.
+
+struct geoloc_gml_attr {
+ const char *attribute;
+ int min_required;
+ int max_allowed;
+ int (*validator)(const char *value);
+};
+
+struct geoloc_gml_shape_def {
+ const char *shape_type;
+ struct geoloc_gml_attr required_attributes[8];
+};
+
+static int pos_validator(const char *value)
+{
+ float lat;
+ float lon;
+ return (sscanf(value, "%f %f", &lat, &lon) == 2);
+}
+
+static int pos3d_validator(const char *value)
+{
+ float lat;
+ float lon;
+ float alt;
+ return (sscanf(value, "%f %f %f", &lat, &lon, &alt) == 3);
+}
+
+static int float_validator(const char *value)
+{
+ float val;
+ return (sscanf(value, "%f", &val) == 1);
+}
+
+static int uom_validator(const char *value)
+{
+ return (ast_strings_equal(value, "degrees") || ast_strings_equal(value, "radians"));
+}
+
+
+static struct geoloc_gml_shape_def gml_shape_defs[8] = {
+ { "Point", { {"pos", 1, 1, pos_validator}, {NULL, -1, -1} }},
+ { "Polygon", { {"pos", 3, -1, pos_validator}, {NULL, -1, -1} }},
+ { "Circle", { {"pos", 1, 1, pos_validator}, {"radius", 1, 1, float_validator},{NULL, -1, -1}}},
+ { "Ellipse", { {"pos", 1, 1, pos_validator}, {"semiMajorAxis", 1, 1, float_validator},
+ {"semiMinorAxis", 1, 1, float_validator}, {"orientation", 1, 1, float_validator},
+ {"orientation_uom", 1, 1, uom_validator}, {NULL, -1, -1} }},
+ { "ArcBand", { {"pos", 1, 1, pos_validator}, {"innerRadius", 1, 1, float_validator},
+ {"outerRadius", 1, 1, float_validator}, {"startAngle", 1, 1, float_validator},
+ {"startAngle_uom", 1, 1, uom_validator}, {"openingAngle", 1, 1, float_validator},
+ {"openingAngle_uom", 1, 1, uom_validator}, {NULL, -1, -1} }},
+ { "Sphere", { {"pos3d", 1, 1, pos3d_validator}, {"radius", 1, 1, float_validator}, {NULL, -1, -1} }},
+ { "Ellipse", { {"pos3d", 1, 1, pos3d_validator}, {"semiMajorAxis", 1, 1, float_validator},
+ {"semiMinorAxis", 1, 1, float_validator}, {"verticalAxis", 1, 1, float_validator},
+ {"orientation", 1, 1, float_validator}, {"orientation_uom", 1, 1, uom_validator}, {NULL, -1, -1} }},
+ { "Prism", { {"pos3d", 3, -1, pos_validator}, {"height", 1, 1, float_validator}, {NULL, -1, -1} }},
+};
+
+enum ast_geoloc_validate_result ast_geoloc_gml_validate_varlist(const struct ast_variable *varlist,
+ const char **result)
+{
+ int def_index = -1;
+ const struct ast_variable *var;
+ int i;
+ const char *shape_type = ast_variable_find_in_list(varlist, "type");
+
+ if (!shape_type) {
+ return AST_GEOLOC_VALIDATE_MISSING_TYPE;
+ }
+
+ for (i = 0; i < ARRAY_LEN(gml_shape_defs); i++) {
+ if (ast_strings_equal(gml_shape_defs[i].shape_type, shape_type)) {
+ def_index = i;
+ }
+ }
+ if (def_index < 0) {
+ return AST_GEOLOC_VALIDATE_INVALID_TYPE;
+ }
+
+ for (var = varlist; var; var = var->next) {
+ int vname_index = -1;
+ if (ast_strings_equal("type", var->name)) {
+ continue;
+ }
+ for (i = 0; i < ARRAY_LEN(gml_shape_defs[def_index].required_attributes); i++) {
+ if (gml_shape_defs[def_index].required_attributes[i].attribute == NULL) {
+ break;
+ }
+ if (ast_strings_equal(gml_shape_defs[def_index].required_attributes[i].attribute, var->name)) {
+ vname_index = i;
+ break;
+ }
+ }
+ if (vname_index < 0) {
+ *result = var->name;
+ return AST_GEOLOC_VALIDATE_INVALID_VARNAME;
+ }
+ if (!gml_shape_defs[def_index].required_attributes[vname_index].validator(var->value)) {
+ *result = var->name;
+ return AST_GEOLOC_VALIDATE_INVALID_VALUE;
+ }
+ }
+
+ for (i = 0; i < ARRAY_LEN(gml_shape_defs[def_index].required_attributes); i++) {
+ int count = 0;
+ if (gml_shape_defs[def_index].required_attributes[i].attribute == NULL) {
+ break;
+ }
+
+ for (var = varlist; var; var = var->next) {
+ if (ast_strings_equal(gml_shape_defs[def_index].required_attributes[i].attribute, var->name)) {
+ count++;
+ }
+ }
+ if (count < gml_shape_defs[def_index].required_attributes[i].min_required) {
+ *result = gml_shape_defs[def_index].required_attributes[i].attribute;
+ return AST_GEOLOC_VALIDATE_NOT_ENOUGH_VARNAMES;
+ }
+ if (gml_shape_defs[def_index].required_attributes[i].max_allowed > 0 &&
+ count > gml_shape_defs[def_index].required_attributes[i].max_allowed) {
+ *result = gml_shape_defs[def_index].required_attributes[i].attribute;
+ return AST_GEOLOC_VALIDATE_TOO_MANY_VARNAMES;
+ }
+ }
+ return AST_GEOLOC_VALIDATE_SUCCESS;
+}
+
+static char *handle_gml_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "geoloc show gml_shape_defs";
+ e->usage =
+ "Usage: geoloc show gml_shape_defs\n"
+ " Show the GML Shape definitions.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, "%-16s %-32s\n", "Shape", "Attributes name(min,max)");
+ ast_cli(a->fd, "================ ===============================\n");
+
+ for (i = 0; i < ARRAY_LEN(gml_shape_defs); i++) {
+ int j;
+ ast_cli(a->fd, "%-16s", gml_shape_defs[i].shape_type);
+ for (j = 0; j < ARRAY_LEN(gml_shape_defs[i].required_attributes); j++) {
+ if (gml_shape_defs[i].required_attributes[j].attribute == NULL) {
+ break;
+ }
+ if (gml_shape_defs[i].required_attributes[j].max_allowed >= 0) {
+ ast_cli(a->fd, " %s(%d,%d)", gml_shape_defs[i].required_attributes[j].attribute,
+ gml_shape_defs[i].required_attributes[j].min_required,
+ gml_shape_defs[i].required_attributes[j].max_allowed);
+ } else {
+ ast_cli(a->fd, " %s(%d,unl)", gml_shape_defs[i].required_attributes[j].attribute,
+ gml_shape_defs[i].required_attributes[j].min_required);
+ }
+ }
+ ast_cli(a->fd, "\n");
+ }
+ ast_cli(a->fd, "\n");
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry geoloc_gml_cli[] = {
+ AST_CLI_DEFINE(handle_gml_show, "Show the GML Shape definitions"),
+};
+
+int geoloc_gml_unload(void)
+{
+ ast_cli_unregister_multiple(geoloc_gml_cli, ARRAY_LEN(geoloc_gml_cli));
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+int geoloc_gml_load(void)
+{
+ ast_cli_register_multiple(geoloc_gml_cli, ARRAY_LEN(geoloc_gml_cli));
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+int geoloc_gml_reload(void)
+{
+ return AST_MODULE_LOAD_SUCCESS;
+}
diff --git a/res/res_geolocation/geoloc_private.h b/res/res_geolocation/geoloc_private.h
new file mode 100644
index 0000000..194d507
--- /dev/null
+++ b/res/res_geolocation/geoloc_private.h
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+#ifndef GEOLOC_PRIVATE_H_
+#define GEOLOC_PRIVATE_H_
+
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/sorcery.h"
+#include "asterisk/lock.h"
+#include "asterisk/res_geolocation.h"
+
+#define CONFIG_STR_TO_ENUM(_stem) \
+static int _stem ## _str_to_enum(const char *str) \
+{ \
+ int i; \
+ for (i = 0; i < ARRAY_LEN(_stem ## _names); i++) { \
+ if (ast_strings_equal(str, _stem ## _names[i])) { \
+ return i; \
+ } \
+ } \
+ return -1; \
+}
+
+#define CONFIG_ENUM_HANDLER(_object, _stem) \
+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); \
+ if (enumval == -1) { \
+ return -1; \
+ } \
+ _thisobject->_stem = enumval; \
+ return 0; \
+}
+
+#define CONFIG_ENUM_TO_STR(_object, _stem) \
+static int _stem ## _to_str(const void *obj, const intptr_t *args, char **buf) \
+{ \
+ const struct ast_geoloc_ ## _object *_thisobject = obj; \
+ if (!ARRAY_IN_BOUNDS(_thisobject->_stem, _stem ## _names)) { \
+ *buf = ast_strdup("none"); \
+ } else { \
+ *buf = ast_strdup(_stem ## _names[_thisobject->_stem]); \
+ } \
+ return 0; \
+}
+
+#define CONFIG_ENUM(_object, _stem) \
+CONFIG_STR_TO_ENUM(_stem) \
+CONFIG_ENUM_HANDLER(_object, _stem) \
+CONFIG_ENUM_TO_STR(_object, _stem)
+
+#define CONFIG_VAR_LIST_HANDLER(_object, _stem) \
+static int _stem ## _handler(const struct aco_option *opt, struct ast_variable *var, void *obj) \
+{ \
+ struct ast_geoloc_ ## _object *_thisobject = obj; \
+ struct ast_variable *new_var; \
+ char *item_string, *item, *item_name, *item_value; \
+ int rc = 0;\
+ if (ast_strlen_zero(var->value)) { return 0; } \
+ item_string = ast_strdupa(var->value); \
+ while ((item = ast_strsep(&item_string, ',', AST_STRSEP_ALL))) { \
+ item_name = ast_strsep(&item, '=', AST_STRSEP_ALL); \
+ item_value = ast_strsep(&item, '=', AST_STRSEP_ALL); \
+ new_var = ast_variable_new(item_name, item_value, ""); \
+ if (!new_var) { \
+ rc = -1; \
+ break; \
+ } \
+ ast_variable_list_append(&_thisobject->_stem, new_var); \
+ } \
+ return rc; \
+}
+
+#define CONFIG_VAR_LIST_DUP(_object, _stem) \
+static int _stem ## _dup(const void *obj, struct ast_variable **fields) \
+{ \
+ const struct ast_geoloc_ ## _object *_thisobject = obj; \
+ if (_thisobject->_stem) { \
+ *fields = ast_variables_dup(_thisobject->_stem); \
+ } \
+ return 0; \
+}
+
+#define CONFIG_VAR_LIST_TO_STR(_object, _stem) \
+static int _stem ## _to_str(const void *obj, const intptr_t *args, char **buf) \
+{ \
+ const struct ast_geoloc_ ## _object *_thisobject = obj; \
+ struct ast_str *str = ast_variable_list_join(_thisobject->_stem, ",", "=", "\"", NULL); \
+ *buf = ast_strdup(ast_str_buffer(str)); \
+ ast_free(str); \
+ return 0; \
+}
+
+#define CONFIG_VAR_LIST(_object, _stem) \
+CONFIG_VAR_LIST_HANDLER(_object, _stem) \
+CONFIG_VAR_LIST_DUP(_object, _stem) \
+CONFIG_VAR_LIST_TO_STR(_object, _stem)
+
+int geoloc_config_load(void);
+int geoloc_config_reload(void);
+int geoloc_config_unload(void);
+
+int geoloc_civicaddr_load(void);
+int geoloc_civicaddr_unload(void);
+int geoloc_civicaddr_reload(void);
+
+int geoloc_gml_unload(void);
+int geoloc_gml_load(void);
+int geoloc_gml_reload(void);
+
+int geoloc_dialplan_unload(void);
+int geoloc_dialplan_load(void);
+int geoloc_dialplan_reload(void);
+
+int geoloc_channel_unload(void);
+int geoloc_channel_load(void);
+int geoloc_channel_reload(void);
+
+struct ast_sorcery *geoloc_get_sorcery(void);
+
+#endif /* GEOLOC_PRIVATE_H_ */
--
To view, visit https://gerrit.asterisk.org/c/asterisk/+/18068
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: Ieb6e3640f31a676da42d8c144ebbb31ad795d849
Gerrit-Change-Number: 18068
Gerrit-PatchSet: 13
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-Reviewer: Michael Bradeen <mbradeen at sangoma.com>
Gerrit-CC: Benjamin Keith Ford <bford at digium.com>
Gerrit-CC: N A <mail at interlinked.x10host.com>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20220325/f92a8061/attachment-0001.html>
More information about the asterisk-code-review
mailing list