res_geolocation: Add profile parameter suppress_empty_ca_elements

Added profile parameter "suppress_empty_ca_elements" that
will cause Civic Address elements that are empty to be
suppressed from the outgoing PIDF-LO document.

Fixed a possible SEGV if a sub-parameter value didn't have a
value.

ASTERISK-30177

Change-Id: I924ccc5aa2f45110a3155b22e53dfaf3ef2092dd
This commit is contained in:
George Joseph
2022-08-17 07:15:00 -06:00
committed by Friendly Automation
parent 08907bf6d8
commit 81ede203b6
9 changed files with 74 additions and 32 deletions

View File

@@ -170,8 +170,9 @@ location_source = sip1.myserver.net
Defines the object type. Defines the object type.
type = profile type = profile
-- profile_precedence (optional) ------------------------------------------ -- profile_precedence (optional) --------------------------------------
Sets how to reconcile incoming and configured profiles. Sets how to reconcile incoming and configured profiles.
profile_precedence = < prefer_incoming | prefer_config | discard_incoming profile_precedence = < prefer_incoming | prefer_config | discard_incoming
| discard_config > | discard_config >
@@ -202,7 +203,9 @@ profile_precedence = prefer_config
-- pidf_element (optional) -------------------------------------------- -- pidf_element (optional) --------------------------------------------
PIDF-LO element in which to place the location description. PIDF-LO element in which to place the location description.
pidf_element = < tuple | device | person > pidf_element = < tuple | device | person >
Default: device
If the format is civicAddress or GML, this sets the PIDF element into If the format is civicAddress or GML, this sets the PIDF element into
which the location information will be placed. which the location information will be placed.
@@ -217,10 +220,12 @@ Per [RFC5491], "device" is preferred and therefore the default.
Example: Example:
pidf_element = tuple pidf_element = tuple
-- allow_routing_use (optional) ------------------------------------- -- allow_routing_use (optional) ---------------------------------------
Sets whether the "Geolocation-Routing" header is added to outgoing Sets whether the "Geolocation-Routing" header is added to outgoing
requests. requests.
allow_routing_use = < yes | no > allow_routing_use = < yes | no >
Default: no
Set to "yes" to indicate that servers later in the path Set to "yes" to indicate that servers later in the path
can use the location information for routing purposes. Set to "no" can use the location information for routing purposes. Set to "no"
@@ -253,7 +258,7 @@ floor and room just for this profile
Example: Example:
location_info_refinement = floor=20, room=20a2 location_info_refinement = floor=20, room=20a2
-- location_variables ------------------------------------------------- -- location_variables (optional) --------------------------------------
If the referenced Location object uses any replacement variables, they If the referenced Location object uses any replacement variables, they
can be assigned here. There is no need to define variables that come can be assigned here. There is no need to define variables that come
@@ -261,6 +266,26 @@ from the channel using this profile. They get assigned automatically.
location_variables = myfloor=20, myroom=222 location_variables = myfloor=20, myroom=222
-- suppress_empty_ca_elements (optional) ------------------------------
Sets whether empty values for Civic Address elements should be
suppressed from the outgoing PIDF-LO document.
suppress_empty_ca_elements = < yes | no >
Default: no
Setting to "yes" allows you to define a location info template
with channel variables that may or may not exist.
For example, with:
location_info_refinement = FLR=${MyFlr}
suppress_empty_ca_elements = no ; the default
If the MyFlr channel variable weren't set, the outgoing PIDF-LO document
would have an empty <FLR/> element in it. If suppress_empty_ca_elements
were set to "yes", the FLR element would be dropped from the PIDF-LO
document altogether.
-- Profile Example ---------------------------------------------------- -- Profile Example ----------------------------------------------------
[myprofile] [myprofile]

View File

@@ -29,3 +29,7 @@ Added 4 built-in profiles:
"<discard_incoming>" "<discard_incoming>"
The profiles are empty except for having their precedence The profiles are empty except for having their precedence
set. set.
Added profile parameter "suppress_empty_ca_elements" that
will cause Civic Address elements that are empty to be
suppressed from the outgoing PIDF-LO document.

View File

@@ -82,6 +82,7 @@ struct ast_geoloc_profile {
struct ast_variable *location_refinement; struct ast_variable *location_refinement;
struct ast_variable *location_variables; struct ast_variable *location_variables;
struct ast_variable *usage_rules; struct ast_variable *usage_rules;
int suppress_empty_ca_elements;
}; };
struct ast_geoloc_eprofile { struct ast_geoloc_eprofile {
@@ -102,6 +103,7 @@ struct ast_geoloc_eprofile {
struct ast_variable *effective_location; struct ast_variable *effective_location;
struct ast_variable *usage_rules; struct ast_variable *usage_rules;
struct ast_variable *confidence; struct ast_variable *confidence;
int suppress_empty_ca_elements;
}; };
/*! /*!

View File

@@ -13,6 +13,7 @@
<xsl:output method="xml" indent="yes"/> <xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/> <xsl:strip-space elements="*"/>
<xsl:param name="suppress_empty_ca_elements" select="false()"/>
<!-- REMINDER: The "match" and "select" xpaths refer to the input document, <!-- REMINDER: The "match" and "select" xpaths refer to the input document,
not the output document --> not the output document -->
@@ -80,9 +81,11 @@
each element, adding the "ca" namespace --> each element, adding the "ca" namespace -->
<xsl:template match="civicAddress/*"> <xsl:template match="civicAddress/*">
<xsl:element name="ca:{name()}"> <xsl:if test="not($suppress_empty_ca_elements) or boolean(node())">
<xsl:value-of select="."/> <xsl:element name="ca:{name()}">
</xsl:element> <xsl:value-of select="."/>
</xsl:element>
</xsl:if>
</xsl:template> </xsl:template>
<xsl:template match="location-info/civicAddress"> <xsl:template match="location-info/civicAddress">

View File

@@ -445,7 +445,6 @@ static char *geoloc_config_show_profiles(struct ast_cli_entry *e, int cmd, struc
iter = ao2_iterator_init(sorted_container, AO2_ITERATOR_UNLINK); iter = ao2_iterator_init(sorted_container, AO2_ITERATOR_UNLINK);
for (; (profile = ao2_iterator_next(&iter)); ) { for (; (profile = ao2_iterator_next(&iter)); ) {
char *action = NULL;
struct ast_str *loc_str = NULL; struct ast_str *loc_str = NULL;
struct ast_str *refinement_str = NULL; struct ast_str *refinement_str = NULL;
struct ast_str *variables_str = NULL; struct ast_str *variables_str = NULL;
@@ -463,24 +462,23 @@ static char *geoloc_config_show_profiles(struct ast_cli_entry *e, int cmd, struc
variables_str = ast_variable_list_join(eprofile->location_variables, ",", "=", "\"", NULL); variables_str = ast_variable_list_join(eprofile->location_variables, ",", "=", "\"", NULL);
usage_rules_str = ast_variable_list_join(eprofile->usage_rules, ",", "=", "\"", NULL); usage_rules_str = ast_variable_list_join(eprofile->usage_rules, ",", "=", "\"", NULL);
precedence_to_str(eprofile, NULL, &action);
ast_cli(a->fd, ast_cli(a->fd,
"id: %-s\n" "id: %-s\n"
"profile_disposition: %-s\n" "profile_precedence: %-s\n"
"pidf_element: %-s\n" "pidf_element: %-s\n"
"location_reference: %-s\n" "location_reference: %-s\n"
"Location_format: %-s\n" "Location_format: %-s\n"
"location_details: %-s\n" "location_details: %-s\n"
"location_method: %-s\n" "location_method: %-s\n"
"location_refinement: %-s\n" "location_refinement: %-s\n"
"location_variables: %-s\n" "location_variables: %-s\n"
"allow_routing_use: %-s\n" "allow_routing_use: %-s\n"
"effective_location: %-s\n" "suppress_empty_elements: %-s\n"
"usage_rules: %-s\n" "effective_location: %-s\n"
"notes: %-s\n", "usage_rules: %-s\n"
"notes: %-s\n",
eprofile->id, eprofile->id,
action, precedence_names[eprofile->precedence],
pidf_element_names[eprofile->pidf_element], pidf_element_names[eprofile->pidf_element],
S_OR(eprofile->location_reference, "<none>"), S_OR(eprofile->location_reference, "<none>"),
format_names[eprofile->format], format_names[eprofile->format],
@@ -488,14 +486,14 @@ static char *geoloc_config_show_profiles(struct ast_cli_entry *e, int cmd, struc
S_OR(eprofile->method, "<none>"), S_OR(eprofile->method, "<none>"),
S_COR(refinement_str, ast_str_buffer(refinement_str), "<none>"), S_COR(refinement_str, ast_str_buffer(refinement_str), "<none>"),
S_COR(variables_str, ast_str_buffer(variables_str), "<none>"), S_COR(variables_str, ast_str_buffer(variables_str), "<none>"),
S_COR(eprofile->precedence, "yes", "no"), S_COR(eprofile->allow_routing_use, "yes", "no"),
S_COR(eprofile->suppress_empty_ca_elements, "yes", "no"),
S_COR(resolved_str, ast_str_buffer(resolved_str), "<none>"), S_COR(resolved_str, ast_str_buffer(resolved_str), "<none>"),
S_COR(usage_rules_str, ast_str_buffer(usage_rules_str), "<none>"), S_COR(usage_rules_str, ast_str_buffer(usage_rules_str), "<none>"),
S_OR(eprofile->notes, "<none>") S_OR(eprofile->notes, "<none>")
); );
ao2_ref(eprofile, -1); ao2_ref(eprofile, -1);
ast_free(action);
ast_free(loc_str); ast_free(loc_str);
ast_free(refinement_str); ast_free(refinement_str);
ast_free(variables_str); ast_free(variables_str);
@@ -695,6 +693,8 @@ int geoloc_config_load(void)
0, STRFLDSET(struct ast_geoloc_profile, notes)); 0, STRFLDSET(struct ast_geoloc_profile, notes));
ast_sorcery_object_field_register(geoloc_sorcery, "profile", "allow_routing_use", ast_sorcery_object_field_register(geoloc_sorcery, "profile", "allow_routing_use",
"no", OPT_BOOL_T, 1, FLDSET(struct ast_geoloc_profile, allow_routing_use)); "no", OPT_BOOL_T, 1, FLDSET(struct ast_geoloc_profile, allow_routing_use));
ast_sorcery_object_field_register(geoloc_sorcery, "profile", "suppress_empty_ca_elements",
"no", OPT_BOOL_T, 1, FLDSET(struct ast_geoloc_profile, suppress_empty_ca_elements));
ast_sorcery_load(geoloc_sorcery); ast_sorcery_load(geoloc_sorcery);

View File

@@ -88,6 +88,8 @@ static int geoloc_profile_read(struct ast_channel *chan,
ast_str_append(buf, len, "%s", eprofile->method); ast_str_append(buf, len, "%s", eprofile->method);
} else if (ast_strings_equal(args.field, "allow_routing_use")) { } else if (ast_strings_equal(args.field, "allow_routing_use")) {
ast_str_append(buf, len, "%s", eprofile->allow_routing_use ? "yes" : "no"); ast_str_append(buf, len, "%s", eprofile->allow_routing_use ? "yes" : "no");
} else if (ast_strings_equal(args.field, "suppress_empty_ca_elements")) {
ast_str_append(buf, len, "%s", eprofile->suppress_empty_ca_elements ? "yes" : "no");
} else if (ast_strings_equal(args.field, "profile_precedence")) { } else if (ast_strings_equal(args.field, "profile_precedence")) {
ast_str_append(buf, len, "%s", ast_geoloc_precedence_to_name(eprofile->precedence)); ast_str_append(buf, len, "%s", ast_geoloc_precedence_to_name(eprofile->precedence));
} else if (ast_strings_equal(args.field, "format")) { } else if (ast_strings_equal(args.field, "format")) {
@@ -212,19 +214,16 @@ static int geoloc_profile_write(struct ast_channel *chan, const char *cmd, char
ast_string_field_set(eprofile, location_reference, value); ast_string_field_set(eprofile, location_reference, value);
} else if (ast_strings_equal(args.field, "method")) { } else if (ast_strings_equal(args.field, "method")) {
ast_string_field_set(eprofile, method, value); ast_string_field_set(eprofile, method, value);
} else if (ast_strings_equal(args.field, "allow_routing_use")) { } else if (ast_strings_equal(args.field, "allow_routing_use")) {
eprofile->allow_routing_use = ast_true(value); eprofile->allow_routing_use = ast_true(value);
} else if (ast_strings_equal(args.field, "suppress_empty_ca_elements")) {
eprofile->suppress_empty_ca_elements = ast_true(value);
} else if (ast_strings_equal(args.field, "profile_precedence")) { } else if (ast_strings_equal(args.field, "profile_precedence")) {
TEST_ENUM_VALUE(chan_name, eprofile, precedence, value); TEST_ENUM_VALUE(chan_name, eprofile, precedence, value);
} else if (ast_strings_equal(args.field, "format")) { } else if (ast_strings_equal(args.field, "format")) {
TEST_ENUM_VALUE(chan_name, eprofile, format, value); TEST_ENUM_VALUE(chan_name, eprofile, format, value);
} else if (ast_strings_equal(args.field, "pidf_element")) { } else if (ast_strings_equal(args.field, "pidf_element")) {
TEST_ENUM_VALUE(chan_name, eprofile, pidf_element, value); TEST_ENUM_VALUE(chan_name, eprofile, pidf_element, value);
} else if (ast_strings_equal(args.field, "location_info")) { } else if (ast_strings_equal(args.field, "location_info")) {
TEST_VARLIST(chan_name, eprofile, location_info, value); TEST_VARLIST(chan_name, eprofile, location_info, value);
} else if (ast_strings_equal(args.field, "location_source")) { } else if (ast_strings_equal(args.field, "location_source")) {

View File

@@ -175,6 +175,10 @@
<configOption name="allow_routing_use"> <configOption name="allow_routing_use">
<synopsis>Sets the value of the Geolocation-Routing header.</synopsis> <synopsis>Sets the value of the Geolocation-Routing header.</synopsis>
</configOption> </configOption>
<configOption name="suppress_empty_ca_elements">
<synopsis>Sets if empty Civic Address elements should be suppressed
from the PIDF-LO document.</synopsis>
</configOption>
<configOption name="profile_precedence" default="discard_incoming"> <configOption name="profile_precedence" default="discard_incoming">
<synopsis>Determine which profile on a channel should be used</synopsis> <synopsis>Determine which profile on a channel should be used</synopsis>

View File

@@ -176,6 +176,7 @@ struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_profile(struct ast_g
ao2_lock(profile); ao2_lock(profile);
eprofile->allow_routing_use = profile->allow_routing_use; eprofile->allow_routing_use = profile->allow_routing_use;
eprofile->pidf_element = profile->pidf_element; eprofile->pidf_element = profile->pidf_element;
eprofile->suppress_empty_ca_elements = profile->suppress_empty_ca_elements;
rc = ast_string_field_set(eprofile, location_reference, profile->location_reference); rc = ast_string_field_set(eprofile, location_reference, profile->location_reference);
if (rc == 0) { if (rc == 0) {
@@ -988,6 +989,7 @@ const char *ast_geoloc_eprofile_to_pidf(struct ast_geoloc_eprofile *eprofile,
struct ast_xml_node *temp_node = NULL; struct ast_xml_node *temp_node = NULL;
const char *entity = NULL; const char *entity = NULL;
int has_no_entity = 0; int has_no_entity = 0;
const char *params[] = { "suppress_empty_ca_elements", "false()", NULL };
SCOPE_ENTER(3, "%s\n", ref_string); SCOPE_ENTER(3, "%s\n", ref_string);
@@ -1038,7 +1040,10 @@ const char *ast_geoloc_eprofile_to_pidf(struct ast_geoloc_eprofile *eprofile,
doc_len = 0; doc_len = 0;
} }
pidf_doc = ast_xslt_apply(eprofile_to_pidf_xslt, intermediate, NULL); if (eprofile->suppress_empty_ca_elements) {
params[1] = "true()";
}
pidf_doc = ast_xslt_apply(eprofile_to_pidf_xslt, intermediate, params);
if (!pidf_doc) { if (!pidf_doc) {
SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create final PIDF-LO doc from intermediate doc\n", SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create final PIDF-LO doc from intermediate doc\n",
ref_string); ref_string);

View File

@@ -90,7 +90,7 @@ static int _stem ## _handler(const struct aco_option *opt, struct ast_variable *
while ((item = ast_strsep(&item_string, ',', AST_STRSEP_ALL))) { \ while ((item = ast_strsep(&item_string, ',', AST_STRSEP_ALL))) { \
item_name = ast_strsep(&item, '=', AST_STRSEP_ALL); \ item_name = ast_strsep(&item, '=', AST_STRSEP_ALL); \
item_value = ast_strsep(&item, '=', AST_STRSEP_ALL); \ item_value = ast_strsep(&item, '=', AST_STRSEP_ALL); \
new_var = ast_variable_new(item_name, item_value, ""); \ new_var = ast_variable_new(item_name, S_OR(item_value, ""), ""); \
if (!new_var) { \ if (!new_var) { \
rc = -1; \ rc = -1; \
break; \ break; \