diff --git a/doc/CHANGES-staging/res_geolocation.txt b/doc/CHANGES-staging/res_geolocation.txt
index b543c6e29c..4d290ba94f 100644
--- a/doc/CHANGES-staging/res_geolocation.txt
+++ b/doc/CHANGES-staging/res_geolocation.txt
@@ -40,3 +40,10 @@ a profile object for simple scenarios where the location
information isn't common with any other profiles. This is
mutually exclusive with setting location_reference on the
profile.
+
+Added an 'a' option to the GEOLOC_PROFILE function to allow
+variable lists like location_info_refinement to be appended
+to instead of replacing the entire list.
+
+Added an 'r' option to the GEOLOC_PROFILE function to resolve all
+variables before a read operation and after a Set operation.
diff --git a/include/asterisk/res_geolocation.h b/include/asterisk/res_geolocation.h
index 378a6c736a..0a5a61d11d 100644
--- a/include/asterisk/res_geolocation.h
+++ b/include/asterisk/res_geolocation.h
@@ -317,6 +317,15 @@ struct ast_datastore *ast_geoloc_datastore_find(struct ast_channel *chan);
*/
struct ast_geoloc_eprofile *ast_geoloc_eprofile_alloc(const char *name);
+/*!
+ * \brief Duplicate an effective profile.
+ *
+ * \param src The eprofile to duplicate.
+ *
+ * \return The duplicated effective profile ao2 object.
+ */
+struct ast_geoloc_eprofile *ast_geoloc_eprofile_dup(struct ast_geoloc_eprofile *src);
+
/*!
* \brief Allocate a new effective profile from an existing profile.
*
diff --git a/res/res_geolocation/geoloc_dialplan.c b/res/res_geolocation/geoloc_dialplan.c
index 710daa65a2..1d1346a30d 100644
--- a/res/res_geolocation/geoloc_dialplan.c
+++ b/res/res_geolocation/geoloc_dialplan.c
@@ -25,7 +25,6 @@
#include "asterisk/strings.h"
#include "asterisk/utils.h"
#include "asterisk/app.h"
-#include "asterisk/res_geolocation.h"
#include "geoloc_private.h"
static void varlist_to_str(struct ast_variable *list, struct ast_str** buf, size_t len)
@@ -37,20 +36,52 @@ static void varlist_to_str(struct ast_variable *list, struct ast_str** buf, size
}
}
+#define RESOLVE_FOR_READ(_param) \
+({ \
+ if (ast_test_flag(&opts, OPT_GEOLOC_RESOLVE)) { \
+ struct ast_variable *resolved = geoloc_eprofile_resolve_varlist( \
+ eprofile->_param, eprofile->location_variables, chan); \
+ if (!resolved) { \
+ ast_log(LOG_ERROR, "%s: Unable to resolve " #_param "\n", chan_name); \
+ pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-3"); \
+ return 0; \
+ } \
+ varlist_to_str(resolved, buf, len); \
+ ast_variables_destroy(resolved); \
+ } else { \
+ varlist_to_str(eprofile->_param, buf, len); \
+ } \
+})
+
+enum my_app_option_flags {
+ OPT_GEOLOC_RESOLVE = (1 << 0),
+ OPT_GEOLOC_APPEND = (1 << 1),
+};
+
+AST_APP_OPTIONS(action_options, {
+ AST_APP_OPTION('r', OPT_GEOLOC_RESOLVE),
+ AST_APP_OPTION('a', OPT_GEOLOC_APPEND),
+});
+
+
static int geoloc_profile_read(struct ast_channel *chan,
const char *cmd, char *data, struct ast_str **buf, ssize_t len)
{
char *parsed_data = ast_strdupa(data);
+ const char *chan_name = ast_channel_name(chan);
struct ast_datastore *ds;
+ struct ast_geoloc_eprofile *orig_eprofile = NULL;
struct ast_geoloc_eprofile *eprofile = NULL;
+ struct ast_flags opts = { 0, };
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(field);
+ AST_APP_ARG(options);
);
/* Check for zero arguments */
if (ast_strlen_zero(parsed_data)) {
- ast_log(LOG_ERROR, "%s: Cannot call without arguments\n", cmd);
+ ast_log(LOG_ERROR, "%s: Cannot call without arguments\n", chan_name);
pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-1");
return 0;
}
@@ -58,25 +89,45 @@ static int geoloc_profile_read(struct ast_channel *chan,
AST_STANDARD_APP_ARGS(args, parsed_data);
if (ast_strlen_zero(args.field)) {
- ast_log(LOG_ERROR, "%s: Cannot call without a field to query\n", cmd);
+ ast_log(LOG_ERROR, "%s: Cannot call without a field to query\n", chan_name);
pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-1");
return 0;
}
+ if (!ast_strlen_zero(args.options)) {
+ if (ast_app_parse_options(action_options, &opts, NULL, args.options)) {
+ ast_log(LOG_ERROR, "%s: Invalid options: %s\n", chan_name, args.options);
+ pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-1");
+ return 0;
+ }
+ }
+
ds = ast_geoloc_datastore_find(chan);
if (!ds) {
- ast_log(LOG_NOTICE, "%s: There is no geoloc profile on this channel\n", cmd);
+ ast_log(LOG_NOTICE, "%s: There is no geoloc profile on this channel\n", chan_name);
pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-2");
return 0;
}
- eprofile = ast_geoloc_datastore_get_eprofile(ds, 0);
+ orig_eprofile = ast_geoloc_datastore_get_eprofile(ds, 0);
+ if (!orig_eprofile) {
+ ast_log(LOG_NOTICE, "%s: There is no geoloc profile on this channel\n", chan_name);
+ pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-2");
+ return 0;
+ }
+
+ eprofile = ast_geoloc_eprofile_dup(orig_eprofile);
+ ao2_ref(orig_eprofile, -1);
if (!eprofile) {
- ast_log(LOG_NOTICE, "%s: There is no geoloc profile on this channel\n", cmd);
+ ast_log(LOG_ERROR, "%s: Unable to duplicate eprofile\n", chan_name);
pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-2");
return 0;
}
+ if (!eprofile->effective_location) {
+ ast_geoloc_eprofile_refresh_location(eprofile);
+ }
+
pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "0");
if (ast_strings_equal(args.field, "inheritable")) {
ast_str_append(buf, len, "%s", ds->inheritance ? "true" : "false");
@@ -101,19 +152,19 @@ static int geoloc_profile_read(struct ast_channel *chan,
} else if (ast_strings_equal(args.field, "notes")) {
ast_str_append(buf, len, "%s", eprofile->notes);
} else if (ast_strings_equal(args.field, "location_info")) {
- varlist_to_str(eprofile->location_info, buf, len);
+ RESOLVE_FOR_READ(location_info);
} else if (ast_strings_equal(args.field, "location_info_refinement")) {
- varlist_to_str(eprofile->location_refinement, buf, len);
+ RESOLVE_FOR_READ(location_refinement);
} else if (ast_strings_equal(args.field, "location_variables")) {
- varlist_to_str(eprofile->location_variables, buf, len);
+ RESOLVE_FOR_READ(location_variables);
} else if (ast_strings_equal(args.field, "effective_location")) {
- varlist_to_str(eprofile->effective_location, buf, len);
+ RESOLVE_FOR_READ(effective_location);
} else if (ast_strings_equal(args.field, "usage_rules")) {
- varlist_to_str(eprofile->usage_rules, buf, len);
+ RESOLVE_FOR_READ(usage_rules);
} else if (ast_strings_equal(args.field, "confidence")) {
varlist_to_str(eprofile->confidence, buf, len);
} else {
- ast_log(LOG_ERROR, "%s: Field '%s' is not valid\n", cmd, args.field);
+ ast_log(LOG_ERROR, "%s: Field '%s' is not valid\n", chan_name, args.field);
pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-3");
}
@@ -121,6 +172,10 @@ static int geoloc_profile_read(struct ast_channel *chan,
return 0;
}
+#define VAR_LIST_REPLACE(_old, _new) \
+ ast_variables_destroy(_old); \
+ _old = _new;
+
#define TEST_ENUM_VALUE(_chan_name, _ep, _field, _value) \
({ \
enum ast_geoloc_ ## _field v; \
@@ -142,8 +197,26 @@ static int geoloc_profile_read(struct ast_channel *chan,
pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-3"); \
return 0; \
} \
- ast_variables_destroy(_ep->_field); \
- _ep->_field = _list; \
+ if (ast_test_flag(&opts, OPT_GEOLOC_APPEND)) { \
+ ast_variable_list_append(&_ep->_field, _list); \
+ } else {\
+ VAR_LIST_REPLACE(_ep->_field, _list); \
+ } \
+})
+
+
+#define RESOLVE_FOR_WRITE(_param) \
+({ \
+if (ast_test_flag(&opts, OPT_GEOLOC_RESOLVE)) { \
+ struct ast_variable *resolved = geoloc_eprofile_resolve_varlist( \
+ eprofile->_param, eprofile->location_variables, chan); \
+ if (!resolved) { \
+ ast_log(LOG_ERROR, "%s: Unable to resolve " #_param " %p %p\n", chan_name, eprofile->_param, eprofile->location_variables); \
+ pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-3"); \
+ return 0; \
+ } \
+ VAR_LIST_REPLACE(eprofile->_param, resolved); \
+} \
})
static int geoloc_profile_write(struct ast_channel *chan, const char *cmd, char *data,
@@ -153,9 +226,11 @@ static int geoloc_profile_write(struct ast_channel *chan, const char *cmd, char
const char *chan_name = ast_channel_name(chan);
struct ast_datastore *ds; /* Reminder: datastores aren't ao2 objects */
RAII_VAR(struct ast_geoloc_eprofile *, eprofile, NULL, ao2_cleanup);
+ struct ast_flags opts = { 0, };
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(field);
+ AST_APP_ARG(options);
);
/* Check for zero arguments */
@@ -173,6 +248,18 @@ static int geoloc_profile_write(struct ast_channel *chan, const char *cmd, char
return 0;
}
+ if (!ast_strlen_zero(args.options)) {
+ if (ast_app_parse_options(action_options, &opts, NULL, args.options)) {
+ ast_log(LOG_ERROR, "%s: Invalid options: %s\n", chan_name, args.options);
+ pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-1");
+ return 0;
+ }
+ }
+
+ ast_debug(1, "%s: name: %s value: %s options: %s append: %s resolve: %s\n", chan_name,
+ args.field, value, args.options, ast_test_flag(&opts, OPT_GEOLOC_APPEND) ? "yes" : "no",
+ ast_test_flag(&opts, OPT_GEOLOC_RESOLVE) ? "yes" : "no");
+
ds = ast_geoloc_datastore_find(chan);
if (!ds) {
ds = ast_geoloc_datastore_create(ast_channel_name(chan));
@@ -203,6 +290,8 @@ static int geoloc_profile_write(struct ast_channel *chan, const char *cmd, char
if (ast_strings_equal(args.field, "inheritable")) {
ast_geoloc_datastore_set_inheritance(ds, ast_true(value));
+ } else if (ast_strings_equal(args.field, "id")) {
+ ast_string_field_set(eprofile, id, value);
} else if (ast_strings_equal(args.field, "location_reference")) {
struct ast_geoloc_location *loc = ast_geoloc_get_location(value);
ao2_cleanup(loc);
@@ -224,18 +313,25 @@ static int geoloc_profile_write(struct ast_channel *chan, const char *cmd, char
TEST_ENUM_VALUE(chan_name, eprofile, format, value);
} else if (ast_strings_equal(args.field, "pidf_element")) {
TEST_ENUM_VALUE(chan_name, eprofile, pidf_element, value);
- } else if (ast_strings_equal(args.field, "location_info")) {
- TEST_VARLIST(chan_name, eprofile, location_info, value);
} else if (ast_strings_equal(args.field, "location_source")) {
ast_string_field_set(eprofile, location_source, value);
+ } else if (ast_strings_equal(args.field, "notes")) {
+ ast_string_field_set(eprofile, notes, value);
+ } else if (ast_strings_equal(args.field, "location_info")) {
+ TEST_VARLIST(chan_name, eprofile, location_info, value);
+ RESOLVE_FOR_WRITE(location_info);
} else if (ast_strings_equal(args.field, "location_info_refinement")) {
TEST_VARLIST(chan_name, eprofile, location_refinement, value);
+ RESOLVE_FOR_WRITE(location_refinement);
} else if (ast_strings_equal(args.field, "location_variables")) {
TEST_VARLIST(chan_name, eprofile, location_variables, value);
+ RESOLVE_FOR_WRITE(location_variables);
} else if (ast_strings_equal(args.field, "effective_location")) {
TEST_VARLIST(chan_name, eprofile, effective_location, value);
+ RESOLVE_FOR_WRITE(effective_location);
} else if (ast_strings_equal(args.field, "usage_rules")) {
TEST_VARLIST(chan_name, eprofile, usage_rules, value);
+ RESOLVE_FOR_WRITE(usage_rules);
} else if (ast_strings_equal(args.field, "confidence")) {
TEST_VARLIST(chan_name, eprofile, confidence, value);
} else {
@@ -245,6 +341,7 @@ static int geoloc_profile_write(struct ast_channel *chan, const char *cmd, char
}
ast_geoloc_eprofile_refresh_location(eprofile);
+
pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "0");
return 0;
diff --git a/res/res_geolocation/geoloc_doc.xml b/res/res_geolocation/geoloc_doc.xml
index 4f7cdc2339..b5713362f2 100644
--- a/res/res_geolocation/geoloc_doc.xml
+++ b/res/res_geolocation/geoloc_doc.xml
@@ -7,12 +7,14 @@
Location
- cffdffff
+ Parameters for defining a Location object
+
Must be of type 'location'.
-
+
+
Location specification type
@@ -42,7 +44,8 @@
-
+
+
Location information
The contents of this parameter are specific to the
@@ -68,7 +71,8 @@
-
+
+
Fully qualified host name
This parameter isn't required but if provided, RFC8787 says it MUST be a fully
@@ -77,7 +81,8 @@
Geolocation header.
-
+
+
Location determination method
This is a rarely used field in the specification that would
@@ -94,7 +99,8 @@
-
+
+
Level of confidence
This is a rarely used field in the specification that would
@@ -123,14 +129,16 @@
+
Profile
- cffdffff
+ Parameters for defining a Profile object
Must be of type 'profile'.
+
PIDF-LO element to place this profile in
@@ -148,21 +156,25 @@
[https://www.rfc-editor.org/rfc/rfc5491.html#section-3.4]
-
+
+
Reference to a location object
-
+
+
Reference to a location object
-
+
Reference to a location object
-
+
+
location specification type
xxxx
+
Notes to be added to the outgoing PIDF-LO document
@@ -171,11 +183,13 @@
outgoing PIDF-LO document. Its usage should be pre-negotiated with
any recipients.
+
-
+
Sets the value of the Geolocation-Routing header.
-
+
+
Sets if empty Civic Address elements should be suppressed
from the PIDF-LO document.
@@ -207,6 +221,7 @@
+
@@ -220,8 +235,8 @@
Get or Set a field in a geolocation profile
-
- The profile field to operate on. The following fields from the
+
+ The profile parameter to operate on. The following fields from the
Location and Profile objects are supported.
@@ -244,10 +259,38 @@
set to true or false to control
whether the profile will be passed to the outgoing channel.
+
+
+
+
+
+
+
+
+
- When used to set a field on a profile, if the profile doesn't already exist, a new
+ When used to set a parameter on a profile, if the profile doesn't already exist, a new
one will be created automatically.
@@ -258,8 +301,8 @@
Success
No or not enough parameters were supplied
There was an internal error finding or creating a profile
- There was an issue specific to the field specified
- (value not valid or field name not found)
+ There was an issue specific to the parameter specified
+ (value not valid or parameter name not found, etc.)
diff --git a/res/res_geolocation/geoloc_eprofile.c b/res/res_geolocation/geoloc_eprofile.c
index 1deb76e654..864dd23b24 100644
--- a/res/res_geolocation/geoloc_eprofile.c
+++ b/res/res_geolocation/geoloc_eprofile.c
@@ -156,6 +156,67 @@ int ast_geoloc_eprofile_refresh_location(struct ast_geoloc_eprofile *eprofile)
return 0;
}
+struct ast_geoloc_eprofile *ast_geoloc_eprofile_dup(struct ast_geoloc_eprofile *src)
+{
+ struct ast_geoloc_eprofile *eprofile;
+ const char *profile_id;
+ int rc = 0;
+
+ if (!src) {
+ return NULL;
+ }
+
+ profile_id = ast_strdupa(src->id);
+
+ eprofile = ast_geoloc_eprofile_alloc(profile_id);
+ if (!eprofile) {
+ return NULL;
+ }
+
+ eprofile->allow_routing_use = src->allow_routing_use;
+ eprofile->pidf_element = src->pidf_element;
+ eprofile->suppress_empty_ca_elements = src->suppress_empty_ca_elements;
+ eprofile->format = src->format;
+ eprofile->precedence = src->precedence;
+
+
+ rc = ast_string_field_set(eprofile, location_reference, src->location_reference);
+ if (rc == 0) {
+ ast_string_field_set(eprofile, notes, src->notes);
+ }
+ if (rc == 0) {
+ ast_string_field_set(eprofile, method, src->method);
+ }
+ if (rc == 0) {
+ ast_string_field_set(eprofile, location_source, src->location_source);
+ }
+ if (rc == 0) {
+ rc = DUP_VARS(eprofile->location_info, src->location_info);
+ }
+ if (rc == 0) {
+ rc = DUP_VARS(eprofile->effective_location, src->effective_location);
+ }
+ if (rc == 0) {
+ rc = DUP_VARS(eprofile->location_refinement, src->location_refinement);
+ }
+ if (rc == 0) {
+ rc = DUP_VARS(eprofile->location_variables, src->location_variables);
+ }
+ if (rc == 0) {
+ rc = DUP_VARS(eprofile->usage_rules, src->usage_rules);
+ }
+ if (rc == 0) {
+ rc = DUP_VARS(eprofile->confidence, src->confidence);
+ }
+ if (rc != 0) {
+ ao2_ref(eprofile, -1);
+ return NULL;
+ }
+
+
+ return eprofile;
+}
+
struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_profile(struct ast_geoloc_profile *profile)
{
struct ast_geoloc_eprofile *eprofile;
@@ -287,7 +348,7 @@ struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_uri(const char *uri,
return eprofile;
}
-static struct ast_variable *geoloc_eprofile_resolve_varlist(struct ast_variable *source,
+struct ast_variable *geoloc_eprofile_resolve_varlist(struct ast_variable *source,
struct ast_variable *variables, struct ast_channel *chan)
{
struct ast_variable *dest = NULL;
diff --git a/res/res_geolocation/geoloc_private.h b/res/res_geolocation/geoloc_private.h
index 910dbc5a3a..0bd0797cb7 100644
--- a/res/res_geolocation/geoloc_private.h
+++ b/res/res_geolocation/geoloc_private.h
@@ -155,4 +155,8 @@ int geoloc_eprofile_reload(void);
struct ast_sorcery *geoloc_get_sorcery(void);
+struct ast_variable *geoloc_eprofile_resolve_varlist(struct ast_variable *source,
+ struct ast_variable *variables, struct ast_channel *chan);
+
+
#endif /* GEOLOC_PRIVATE_H_ */
diff --git a/res/res_pjsip_geolocation.c b/res/res_pjsip_geolocation.c
index 0ca1e58980..d0e8d465d3 100644
--- a/res/res_pjsip_geolocation.c
+++ b/res/res_pjsip_geolocation.c
@@ -574,7 +574,10 @@ static void handle_outgoing_request(struct ast_sip_session *session, struct pjsi
session_name);
}
- ast_geoloc_eprofile_refresh_location(final_eprofile);
+ if (!final_eprofile->effective_location) {
+ ast_geoloc_eprofile_refresh_location(final_eprofile);
+ }
+
if (final_eprofile->format == AST_GEOLOC_FORMAT_URI) {
uri = ast_geoloc_eprofile_to_uri(final_eprofile, channel, &buf, session_name);
if (!uri) {