For chan_pjsip, this will be placed in the body of
outgoing INVITE messages in addition to any SDP.
-GML: [RFC4119] [RFC5491] [GeoShape]
+GML: [RFC4119] [RFC4479] [RFC5491] [GeoShape]
The location information will be placed in an XML document
conforming to the PIDF-LO standard.
For chan_pjsip, this will be placed in the body of
Example:
pidf_element = tuple
+-- pidf_element id(optional) ------------------------------------------
+Sets the value of the 'id' attribute for the top-level PIDF-LO element.
+You can reference channel variables in this parameter.
+
+pidf_element_id = <any valid attribute string>
+
+Example:
+pidf_element_id = ${CHANNEL(name)}
+
+-- device_id (optional) -----------------------------------------------
+Sets the contents of the <dm:deviceID> element in the top-level
+PIDF-LO element. RFC4479 defined this only as a 'URN' with the only
+examples being a mac address in the format 'mac:XXXXXXXXXXXX'.
+You can reference channel variables in this parameter.
+
+device_id = <urn>
+
+Example:
+device_id = mac:40000b0c0d12
+device_id = mac:${MAC_ADDRESS}
+
+
-- allow_routing_use (optional) ---------------------------------------
Sets whether the "Geolocation-Routing" header is added to outgoing
requests.
AST_STRING_FIELD(notes);
AST_STRING_FIELD(method);
AST_STRING_FIELD(location_source);
+ AST_STRING_FIELD(pidf_element_id);
+ AST_STRING_FIELD(device_id);
);
enum ast_geoloc_pidf_element pidf_element;
enum ast_geoloc_precedence precedence;
AST_STRING_FIELD(location_source);
AST_STRING_FIELD(method);
AST_STRING_FIELD(notes);
+ AST_STRING_FIELD(pidf_element_id);
+ AST_STRING_FIELD(device_id);
);
enum ast_geoloc_pidf_element pidf_element;
enum ast_geoloc_precedence precedence;
int ast_geoloc_civicaddr_is_code_valid(const char *code);
enum ast_geoloc_validate_result {
- AST_GEOLOC_VALIDATE_INVALID_VALUE = -1,
AST_GEOLOC_VALIDATE_SUCCESS = 0,
AST_GEOLOC_VALIDATE_MISSING_SHAPE,
AST_GEOLOC_VALIDATE_INVALID_SHAPE,
AST_GEOLOC_VALIDATE_INVALID_VARNAME,
AST_GEOLOC_VALIDATE_NOT_ENOUGH_VARNAMES,
AST_GEOLOC_VALIDATE_TOO_MANY_VARNAMES,
+ AST_GEOLOC_VALIDATE_INVALID_CRS,
+ AST_GEOLOC_VALIDATE_INVALID_CRS_FOR_SHAPE,
+ AST_GEOLOC_VALIDATE_INVALID_VALUE,
};
const char *ast_geoloc_validate_result_to_str(enum ast_geoloc_validate_result result);
* \return result code.
*/
enum ast_geoloc_validate_result ast_geoloc_civicaddr_validate_varlist(
- const struct ast_variable *varlist, const char **result);
+ const struct ast_variable *varlist, char **result);
/*!
* \brief Validate that the variables in the list represent a valid GML shape
*
* \return result code.
*/
-enum ast_geoloc_validate_result ast_geoloc_gml_validate_varlist(const struct ast_variable *varlist,
- const char **result);
+enum ast_geoloc_validate_result ast_geoloc_gml_validate_varlist(struct ast_variable *varlist,
+ char **result);
/*!
<xsl:template match="tuple">
<xsl:element name="tuple" namespace="urn:ietf:params:xml:ns:pidf">
+ <xsl:if test="@id">
+ <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
+ </xsl:if>
<xsl:element name="status" namespace="urn:ietf:params:xml:ns:pidf">
<gp:geopriv>
<xsl:apply-templates select="./location-info"/>
<xsl:value-of select="./timestamp"/>
</xsl:element>
</xsl:if>
+ <xsl:if test="./deviceID">
+ <dm:deviceID>
+ <xsl:value-of select="./deviceID"/>
+ </dm:deviceID>
+ </xsl:if>
</xsl:element>
</xsl:template>
</xsl:template>
<!-- usage-rules does have children so we add the "gp" namespace and copy in
- the children, also adding the "gp" namespace -->
+ the children, also adding the "gbp" namespace -->
<xsl:template match="usage-rules">
<gp:usage-rules>
<xsl:for-each select="*">
- <xsl:element name="gp:{local-name()}">
+ <xsl:element name="gbp:{local-name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
<xsl:element name="gs:{name()}">
<xsl:choose>
<xsl:when test="@uom = 'radians'">
- <xsl:attribute name="uom">urn:ogc:def:uom:EPSG::9102</xsl:attribute>
+ <xsl:attribute name="uom">urn:ogc:def:uom:EPSG::9101</xsl:attribute>
</xsl:when>
<xsl:otherwise>
- <xsl:attribute name="uom">urn:ogc:def:uom:EPSG::9101</xsl:attribute>
+ <xsl:attribute name="uom">urn:ogc:def:uom:EPSG::9102</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="."/>
}
enum ast_geoloc_validate_result ast_geoloc_civicaddr_validate_varlist(
- const struct ast_variable *varlist, const char **result)
+ const struct ast_variable *varlist, char **result)
{
const struct ast_variable *var = varlist;
for (; var; var = var->next) {
int valid = ast_geoloc_civicaddr_is_code_valid(var->name);
if (!valid) {
- *result = var->name;
+ *result = ast_strdup(var->name);
return AST_GEOLOC_VALIDATE_INVALID_VARNAME;
}
}
static const char *result_names[] = {
"Success",
- "Missing type",
+ "Missing shape type",
"Invalid shape type",
"Invalid variable name",
"Not enough variables",
"Too many variables",
- "Invalid variable value"
+ "Invalid CRS",
+ "Invalid CRS for shape",
+ "Invalid variable value",
};
const char *ast_geoloc_validate_result_to_str(enum ast_geoloc_validate_result result)
enum ast_geoloc_format format, struct ast_variable *location_info)
{
enum ast_geoloc_validate_result result;
- const char *failed;
+ char *failed;
const char *uri;
switch (format) {
if (result != AST_GEOLOC_VALIDATE_SUCCESS) {
ast_log(LOG_ERROR, "Location '%s' has invalid item '%s' in the location\n",
id, failed);
+ ast_free(failed);
return result;
}
break;
case AST_GEOLOC_FORMAT_GML:
result = ast_geoloc_gml_validate_varlist(location_info, &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, id);
+ ast_log(LOG_ERROR, "Location '%s' failed: %s\n", id, failed);
+ ast_free(failed);
return result;
}
-
break;
case AST_GEOLOC_FORMAT_URI:
uri = ast_variable_find_in_list(location_info, "URI");
"id: %-s\n"
"profile_precedence: %-s\n"
"pidf_element: %-s\n"
+ "pidf_element_id: %-s\n"
"location_reference: %-s\n"
"location_format: %-s\n"
"location_info: %-s\n"
"suppress_empty_elements: %-s\n"
"effective_location: %-s\n"
"usage_rules: %-s\n"
- "notes: %-s\n",
+ "notes: %-s\n"
+ "device_id: %-s\n",
eprofile->id,
precedence_names[eprofile->precedence],
pidf_element_names[eprofile->pidf_element],
+ S_OR(eprofile->pidf_element_id, "<none>"),
S_OR(eprofile->location_reference, "<none>"),
format_names[eprofile->format],
S_COR(loc_str, ast_str_buffer(loc_str), "<none>"),
S_COR(eprofile->suppress_empty_ca_elements, "yes", "no"),
S_COR(resolved_str, ast_str_buffer(resolved_str), "<none>"),
S_COR(usage_rules_str, ast_str_buffer(usage_rules_str), "<none>"),
- S_OR(eprofile->notes, "<none>")
+ S_OR(eprofile->notes, "<none>"),
+ S_OR(eprofile->device_id, "<none>")
);
ao2_ref(eprofile, -1);
ast_sorcery_object_field_register(geoloc_sorcery, "profile", "type", "", OPT_NOOP_T, 0, 0);
ast_sorcery_object_field_register_custom(geoloc_sorcery, "profile", "pidf_element",
pidf_element_names[AST_PIDF_ELEMENT_DEVICE], profile_pidf_element_handler, profile_pidf_element_to_str, NULL, 0, 0);
+ ast_sorcery_object_field_register(geoloc_sorcery, "profile", "pidf_element_id", "", OPT_STRINGFIELD_T,
+ 0, STRFLDSET(struct ast_geoloc_profile, pidf_element_id));
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", "profile_precedence", "discard_incoming",
0, STRFLDSET(struct ast_geoloc_profile, location_source));
ast_sorcery_object_field_register(geoloc_sorcery, "profile", "method", "", OPT_STRINGFIELD_T,
0, STRFLDSET(struct ast_geoloc_profile, method));
-
+ ast_sorcery_object_field_register(geoloc_sorcery, "profile", "device_id", "", OPT_STRINGFIELD_T,
+ 0, STRFLDSET(struct ast_geoloc_profile, device_id));
ast_sorcery_load(geoloc_sorcery);
} \
})
+#define RESOLVE_STRINGFIELD_FOR_READ(_param) \
+({ \
+ if (ast_test_flag(&opts, OPT_GEOLOC_RESOLVE)) { \
+ char *resolved = geoloc_eprofile_resolve_string( \
+ 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; \
+ } \
+ ast_str_append(buf, len, "%s", resolved); \
+ ast_free(resolved); \
+ } else { \
+ ast_str_append(buf, len, "%s", eprofile->_param); \
+ } \
+})
+
enum my_app_option_flags {
OPT_GEOLOC_RESOLVE = (1 << 0),
OPT_GEOLOC_APPEND = (1 << 1),
ast_str_append(buf, len, "%s", ast_geoloc_format_to_name(eprofile->format));
} else if (ast_strings_equal(args.field, "pidf_element")) {
ast_str_append(buf, len, "%s", ast_geoloc_pidf_element_to_name(eprofile->pidf_element));
+ } else if (ast_strings_equal(args.field, "pidf_element_id")) {
+ RESOLVE_STRINGFIELD_FOR_READ(pidf_element_id);
} else if (ast_strings_equal(args.field, "location_source")) {
ast_str_append(buf, len, "%s", eprofile->location_source);
} else if (ast_strings_equal(args.field, "notes")) {
RESOLVE_FOR_READ(usage_rules);
} else if (ast_strings_equal(args.field, "confidence")) {
varlist_to_str(eprofile->confidence, buf, len);
+ } else if (ast_strings_equal(args.field, "device_id")) {
+ RESOLVE_STRINGFIELD_FOR_READ(device_id);
} else {
ast_log(LOG_ERROR, "%s: Field '%s' is not valid\n", chan_name, args.field);
pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-3");
} \
})
+#define RESOLVE_STRINGFIELD_FOR_WRITE(_param, _value) \
+({ \
+if (ast_test_flag(&opts, OPT_GEOLOC_RESOLVE)) { \
+ char *resolved = geoloc_eprofile_resolve_string( \
+ _value, 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; \
+ } \
+ ast_string_field_set(eprofile, _param, resolved); \
+ ast_free(resolved); \
+} else { \
+ ast_string_field_set(eprofile, _param, _value); \
+} \
+})
+
static int geoloc_profile_write(struct ast_channel *chan, const char *cmd, char *data,
const char *value)
{
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, "pidf_element_id")) {
+ RESOLVE_STRINGFIELD_FOR_WRITE(pidf_element_id, 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")) {
RESOLVE_FOR_WRITE(usage_rules);
} else if (ast_strings_equal(args.field, "confidence")) {
TEST_VARLIST(chan_name, eprofile, confidence, value);
+ } else if (ast_strings_equal(args.field, "device_id")) {
+ RESOLVE_STRINGFIELD_FOR_WRITE(pidf_element_id, value);
} else {
ast_log(LOG_ERROR, "%s: Field '%s' is not valid\n", chan_name, args.field);
pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-3");
</see-also>
</configOption>
+ <configOption name="pidf_element_id" default="">
+ <since>
+ <version>20.18.0</version>
+ <version>22.8.0</version>
+ <version>23.2.0</version>
+ </since>
+ <synopsis>The id attribute value for the PIDF-LO element</synopsis>
+ </configOption>
+
+ <configOption name="device_id" default="">
+ <since>
+ <version>20.18.0</version>
+ <version>22.8.0</version>
+ <version>23.2.0</version>
+ </since>
+ <synopsis>The content of the deviceID element</synopsis>
+ </configOption>
+
<configOption name="location_reference" default="none">
<synopsis>Reference to a location object</synopsis>
</configOption>
<enum name="profile_precedence"/>
<enum name="format"/>
<enum name="pidf_element"/>
+ <enum name="pidf_element_id"/>
<enum name="location_source"/>
<enum name="notes"/>
<enum name="location_info"/>
<enum name="effective_location"/>
<enum name="usage_rules"/>
<enum name="confidence"/>
+ <enum name="device_id"/>
</enumlist>
<para>Additionally, the <literal>inheritable</literal> field may be
set to <literal>true</literal> or <literal>false</literal> to control
rc = ast_string_field_set(eprofile, location_reference, src->location_reference);
+ if (rc == 0) {
+ rc = ast_string_field_set(eprofile, pidf_element_id, src->pidf_element_id);
+ }
if (rc == 0) {
ast_string_field_set(eprofile, notes, src->notes);
}
if (rc == 0) {
rc = DUP_VARS(eprofile->confidence, src->confidence);
}
+ if (rc == 0) {
+ rc = ast_string_field_set(eprofile, device_id, src->device_id);
+ }
if (rc != 0) {
ao2_ref(eprofile, -1);
return NULL;
eprofile->suppress_empty_ca_elements = profile->suppress_empty_ca_elements;
eprofile->format = profile->format;
-
rc = ast_string_field_set(eprofile, location_reference, profile->location_reference);
+ if (rc == 0) {
+ rc = ast_string_field_set(eprofile, pidf_element_id, profile->pidf_element_id);
+ }
if (rc == 0) {
ast_string_field_set(eprofile, notes, profile->notes);
}
if (rc == 0) {
rc = DUP_VARS(eprofile->confidence, profile->confidence);
}
+ if (rc == 0) {
+ rc = ast_string_field_set(eprofile, device_id, profile->device_id);
+ }
if (rc != 0) {
ao2_unlock(profile);
ao2_ref(eprofile, -1);
return dest;
}
+char *geoloc_eprofile_resolve_string(const char *source,
+ struct ast_variable *variables, struct ast_channel *chan)
+{
+ struct varshead *vh = NULL;
+ struct ast_str *buf = ast_str_alloca(256);
+
+ if (!source || !chan) {
+ return NULL;
+ }
+
+ /*
+ * ast_str_substitute_variables does only minimal recursive resolution so we need to
+ * pre-resolve each variable in the "variables" list, then use that result to
+ * do the final pass on the "source" variable list.
+ */
+ if (variables) {
+ struct ast_variable *var = variables;
+ vh = ast_var_list_create();
+ if (!vh) {
+ return NULL;
+ }
+ for ( ; var; var = var->next) {
+ ast_str_substitute_variables_full2(&buf, 0, chan, vh, var->value, NULL, 1);
+ AST_VAR_LIST_INSERT_TAIL(vh, ast_var_assign(var->name, ast_str_buffer(buf)));
+ ast_str_reset(buf);
+ }
+ }
+
+ ast_str_substitute_variables_full2(&buf, 0, chan, vh, source, NULL, 1);
+ ast_var_list_destroy(vh);
+
+ return ast_strdup(ast_str_buffer(buf));
+}
const char *ast_geoloc_eprofile_to_uri(struct ast_geoloc_eprofile *eprofile,
struct ast_channel *chan, struct ast_str **buf, const char *ref_str)
struct ast_xml_node *usage_rules = NULL;
struct ast_xml_node *method = NULL;
struct ast_xml_node *note_well = NULL;
+ struct ast_xml_node *device_id = NULL;
/*
* Like nodes, names of nodes are just
* pointers into result_doc and don't need to be freed.
* so they DO need to be freed after use.
*/
const char *id = NULL;
+ const char *pidf_element_id = NULL;
const char *format_str = NULL;
const char *method_str = NULL;
const char *note_well_str = NULL;
+ const char *device_id_str = NULL;
SCOPE_ENTER(3, "%s\n", ref_str);
SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Can't find 'presence' root element\n",
ref_str);
}
+ id = ast_xml_get_attribute(presence, "entity");
pidf_element = ast_xml_node_get_children(presence);
if (!pidf_element) {
ref_str);
}
- id = ast_xml_get_attribute(pidf_element, "id");
- if (ast_strlen_zero(id)) {
- ast_xml_free_attr(id);
- id = ast_xml_get_attribute(presence, "entity");
- }
-
- if (ast_strlen_zero(id)) {
- SCOPE_EXIT_RTN_VALUE(NULL, "%s: Unable to find 'id' attribute\n", ref_str);
- }
-
- eprofile = ast_geoloc_eprofile_alloc(id);
+ eprofile = ast_geoloc_eprofile_alloc(S_OR(id, "unknown"));
ast_xml_free_attr(id);
if (!eprofile) {
SCOPE_EXIT_RTN_VALUE(NULL, "%s: Allocation failure\n", ref_str);
}
+ pidf_element_id = ast_xml_get_attribute(pidf_element, "id");
+ if (!ast_strlen_zero(pidf_element_id)) {
+ ast_string_field_set(eprofile, pidf_element_id, pidf_element_id);
+ }
+ ast_xml_free_attr(pidf_element_id);
+
location_info = ast_xml_find_child_element(pidf_element, "location-info", NULL, NULL);
if (!location_info) {
SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Can't find a location-info element\n",
ast_string_field_set(eprofile, notes, note_well_str);
ast_xml_free_text(note_well_str);
+ device_id = ast_xml_find_child_element(pidf_element, "deviceID", NULL, NULL);
+ device_id_str = ast_xml_get_text(device_id);
+ ast_string_field_set(eprofile, device_id, device_id_str);
+ ast_xml_free_text(device_id_str);
+
SCOPE_EXIT_RTN_VALUE(eprofile, "%s: Done.\n", ref_str);
}
struct ast_xml_node *method_node;
struct ast_xml_node *notes_node;
struct ast_xml_node *timestamp_node;
+ struct ast_xml_node *device_id_node;
struct timeval tv = ast_tvnow();
struct tm tm = { 0, };
char timestr[32] = { 0, };
SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create '%s' XML node\n",
ref_string, element_name);
}
+ if (!ast_strlen_zero(eprofile->pidf_element_id)) {
+ char *resolved_pidf_element_id = geoloc_eprofile_resolve_string(eprofile->pidf_element_id,
+ eprofile->location_variables, chan);
+ if (!resolved_pidf_element_id) {
+ SCOPE_EXIT_RTN_VALUE(NULL);
+ }
+ rc = ast_xml_set_attribute(pidf_node, "id", resolved_pidf_element_id);
+ ast_free(resolved_pidf_element_id);
+ if (rc != 0) {
+ SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create 'id' XML attribute\n", ref_string);
+ }
+ }
loc_node = ast_xml_new_child(pidf_node, "location-info");
if (!loc_node) {
}
ast_xml_set_text(timestamp_node, timestr);
+ if (!ast_strlen_zero(eprofile->device_id)) {
+ char *resolved_device_id = geoloc_eprofile_resolve_string(eprofile->device_id,
+ eprofile->location_variables, chan);
+ if (!resolved_device_id) {
+ SCOPE_EXIT_RTN_VALUE(NULL);
+ }
+ device_id_node = ast_xml_new_child(pidf_node, "deviceID");
+ if (!device_id_node) {
+ ast_free(resolved_device_id);
+ SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create 'deviceID' XML node\n",
+ ref_string);
+ }
+ ast_xml_set_text(device_id_node, resolved_device_id);
+ ast_free(resolved_device_id);
+ }
+
+
rtn_pidf_node = pidf_node;
pidf_node = NULL;
SCOPE_EXIT_RTN_VALUE(rtn_pidf_node, "%s: Done\n", ref_string);
SCOPE_EXIT_RTN_VALUE(ast_str_buffer(*buf), "%s: Done\n", ref_string);
}
-const char *ast_geoloc_eprofile_to_pidf(struct ast_geoloc_eprofile *eprofile,
+static struct ast_xml_doc *geoloc_eprofile_to_xmldoc(struct ast_geoloc_eprofile *eprofile,
struct ast_channel *chan, struct ast_str **buf, const char * ref_string)
{
RAII_VAR(struct ast_xml_doc *, intermediate, NULL, ast_xml_close);
- RAII_VAR(struct ast_xml_doc *, pidf_doc, NULL, ast_xml_close);
+ struct ast_xml_doc *pidf_doc = NULL;
struct ast_xml_node *root_node;
char *doc_str = NULL;
int doc_len;
ref_string);
}
+ SCOPE_EXIT_RTN_VALUE(pidf_doc, "%s: Done\n", ref_string);
+}
+
+const char *ast_geoloc_eprofile_to_pidf(struct ast_geoloc_eprofile *eprofile,
+ struct ast_channel *chan, struct ast_str **buf, const char * ref_string)
+{
+
+ RAII_VAR(struct ast_xml_doc *, pidf_doc, NULL, ast_xml_close);
+ char *doc_str = NULL;
+ int doc_len = 0;
+ int rc = 0;
+ SCOPE_ENTER(3, "%s\n", ref_string);
+
+ pidf_doc = geoloc_eprofile_to_xmldoc(eprofile, chan, buf, ref_string);
+ if (!pidf_doc) {
+ SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create final PIDF-LO doc from intermediate doc\n",
+ ref_string);
+ }
+
ast_xml_doc_dump_memory(pidf_doc, &doc_str, &doc_len);
if (doc_len == 0 || !doc_str) {
SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to dump final PIDF-LO doc to string\n",
ast_trace(5, "Final doc:\n%s\n", ast_str_buffer(*buf));
SCOPE_EXIT_RTN_VALUE(ast_str_buffer(*buf), "%s: Done\n", ref_string);
+
}
#ifdef TEST_FRAMEWORK
}
static enum ast_test_result_state validate_eprofile(struct ast_test *test,
+ struct ast_geoloc_eprofile *eprofile,
struct ast_xml_doc * pidf_xmldoc,
- const char *path,
const char *id,
+ const char *pidf_element_id,
enum ast_geoloc_pidf_element pidf_element,
enum ast_geoloc_format format,
const char *method,
const char *location,
- const char *usage
+ const char *usage,
+ const char *device_id
)
{
RAII_VAR(struct ast_str *, str, NULL, ast_free);
- RAII_VAR(struct ast_geoloc_eprofile *, eprofile, NULL, ao2_cleanup);
RAII_VAR(struct ast_xml_doc *, result_doc, NULL, ast_xml_close);
- if (!ast_strlen_zero(path)) {
- result_doc = ast_xslt_apply(pidf_to_eprofile_xslt, pidf_xmldoc, NULL);
- ast_test_validate(test, (result_doc && ast_xml_node_get_children((struct ast_xml_node *)result_doc)));
-
- eprofile = geoloc_eprofile_create_from_xslt_result(result_doc, "test_create_from_xslt");
- } else {
- eprofile = ast_geoloc_eprofile_create_from_pidf(pidf_xmldoc, NULL, "test_create_from_pidf");
- }
-
- ast_test_validate(test, eprofile != NULL);
- ast_test_status_update(test, "ID: '%s' pidf_element: '%s' format: '%s' method: '%s'\n", eprofile->id,
+ ast_test_status_update(test, "eprofile: ID: '%s' pidf_element: '%s' peid: %s format: '%s' method: '%s' device_id: '%s'\n",
+ eprofile->id,
ast_geoloc_pidf_element_to_name(eprofile->pidf_element),
+ eprofile->pidf_element_id,
ast_geoloc_format_to_name(eprofile->format),
- eprofile->method);
+ eprofile->method, eprofile->device_id);
+ ast_test_status_update(test, "xml: ID: '%s' pidf_element: '%s' peid: %s format: '%s' method: '%s' device_id: '%s'\n",
+ id,
+ ast_geoloc_pidf_element_to_name(pidf_element),
+ pidf_element_id,
+ ast_geoloc_format_to_name(format),
+ method, device_id);
ast_test_validate(test, ast_strings_equal(eprofile->id, id));
ast_test_validate(test, eprofile->pidf_element == pidf_element);
+ ast_test_validate(test, ast_strings_equal(eprofile->pidf_element_id, pidf_element_id));
ast_test_validate(test, eprofile->format == format);
ast_test_validate(test, ast_strings_equal(eprofile->method, method));
+ ast_test_validate(test, ast_strings_equal(eprofile->device_id, device_id));
str = ast_variable_list_join(eprofile->location_info, ",", "=", NULL, NULL);
ast_test_validate(test, str != NULL);
return AST_TEST_PASS;
}
+static char *normalize_string(char *in)
+{
+ char *out = ast_strip(in);
+ char *ptr = out;
+
+ while (*ptr != '\0') {
+ if (*ptr == '\n') {
+ char *next = ast_skip_blanks(ptr);
+ *ptr = ' ';
+ ptr++;
+ next = ast_strdup(next);
+ strcpy(ptr, next); /* Safe */
+ ast_free(next);
+ }
+ ptr++;
+ }
+ return out;
+}
+
+struct test_xpath_element {
+ const char *path;
+ int validate_content;
+};
+
+static enum ast_test_result_state validate_xml(struct ast_test *test,
+ struct ast_geoloc_eprofile *eprofile,
+ struct ast_xml_doc * pidf_xmldoc,
+ struct ast_xml_doc * eprofile_xmldoc
+ )
+{
+ enum ast_test_result_state res = AST_TEST_PASS;
+ struct ast_xml_namespace_def_vector ns;
+ struct ast_xml_namespace_def def = {"def", "urn:ietf:params:xml:ns:pidf"};
+ struct ast_xml_namespace_def dm = {"dm", "urn:ietf:params:xml:ns:pidf:data-model"};
+ struct ast_xml_namespace_def ca = {"ca", "urn:ietf:params:xml:ns:pidf:geopriv10:civicAddr"};
+ struct ast_xml_namespace_def gbp = {"gbp", "urn:ietf:params:xml:ns:pidf:geopriv10:basicPolicy"};
+ struct ast_xml_namespace_def gml = {"gml", "http://www.opengis.net/gml"};
+ struct ast_xml_namespace_def gp = {"gp", "urn:ietf:params:xml:ns:pidf:geopriv10"};
+ struct ast_xml_namespace_def con = {"con", "urn:ietf:params:xml:ns:geopriv:conf"};
+ struct ast_xml_namespace_def gs = {"gs", "http://www.opengis.net/pidflo/1.0"};
+
+ struct test_xpath_element elements[] = {
+ {"//def:tuple/@id", 1},
+ {"//gml:Point/@srsName", 1},
+ {"//gml:pos/text()", 1},
+ {"//con:confidence/text()", 1},
+ {"//con:confidence/@pdf", 1},
+ {"//gp:usage-rules", 0},
+ {"//gbp:retransmission-allowed/text()", 1},
+ {"//gbp:retention-expiry/text()", 1},
+ {"//gp:method/text()", 1},
+ {"//gp:note-well/text()", 1},
+ {"//dm:deviceID/text()", 1},
+ {"//def:timestamp", 0},
+ };
+ int i;
+
+ AST_VECTOR_INIT(&ns, 12);
+ AST_VECTOR_APPEND(&ns, def);
+ AST_VECTOR_APPEND(&ns, dm);
+ AST_VECTOR_APPEND(&ns, ca);
+ AST_VECTOR_APPEND(&ns, gbp);
+ AST_VECTOR_APPEND(&ns, gml);
+ AST_VECTOR_APPEND(&ns, gp);
+ AST_VECTOR_APPEND(&ns, con);
+ AST_VECTOR_APPEND(&ns, gs);
+
+
+ for (i = 0; i < ARRAY_LEN(elements); i++) {
+ struct ast_xml_xpath_results *aresults = ast_xml_query_with_namespaces(eprofile_xmldoc, elements[i].path, &ns);
+ struct ast_xml_xpath_results *bresults = ast_xml_query_with_namespaces(pidf_xmldoc, elements[i].path, &ns);
+ if (aresults && bresults) {
+ struct ast_xml_node *anode = ast_xml_xpath_get_first_result(aresults);
+ struct ast_xml_node *bnode = ast_xml_xpath_get_first_result(bresults);
+ if (elements[i].validate_content) {
+ char *atext = normalize_string(ast_strdupa(S_OR(ast_xml_get_text(anode), "")));
+ char *btext = normalize_string(ast_strdupa(S_OR(ast_xml_get_text(bnode), "")));
+ int pass = ast_strings_equal(atext, btext);
+ ast_test_status_update(test, "Element: %s eprofile: %s pidf: %s Result: %s\n",
+ elements[i].path, atext, btext, pass ? "pass" : "FAIL");
+ if (!pass) {
+ ast_log(LOG_ERROR, "Element: %s eprofile: %s pidf: %s Result: FAIL\n",
+ elements[i].path, atext, btext);
+ res = AST_TEST_FAIL;
+ }
+ } else {
+ int pass = !!anode && !!bnode;
+ ast_test_status_update(test, "Element: %s eprofile: %s pidf: %s Result: %s\n",
+ elements[i].path, anode ? "exists" : "doesn't exist", bnode ? "exists" : "doesn't exist",
+ pass ? "pass" : "FAIL");
+ if (!pass) {
+ ast_log(LOG_ERROR, "Element: %s eprofile: %s pidf: %s\n",
+ elements[i].path, anode ? "exists" : "doesn't exist", bnode ? "exists" : "doesn't exist");
+ }
+ }
+ } else {
+ if (!aresults) {
+ ast_log(LOG_ERROR, "No xpath eprofile result for %s\n", elements[i].path);
+ res = AST_TEST_FAIL;
+ }
+ if (!bresults) {
+ ast_log(LOG_ERROR, "No xpath pidf result for %s\n", elements[i].path);
+ res = AST_TEST_FAIL;
+ }
+ }
+ }
+
+ return res;
+}
+
AST_TEST_DEFINE(test_create_from_pidf)
{
RAII_VAR(struct ast_xml_doc *, pidf_xmldoc, NULL, ast_xml_close);
+ RAII_VAR(struct ast_xml_doc *, eprofile_xmldoc, NULL, ast_xml_close);
+ RAII_VAR(struct ast_geoloc_eprofile *, eprofile, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_str *, buf, NULL, ast_free);
+ RAII_VAR(struct ast_channel *, mock_channel, NULL, ast_hangup);
+
enum ast_test_result_state res = AST_TEST_PASS;
switch (cmd) {
pidf_xmldoc = ast_xml_read_memory((char *)_binary_res_geolocation_pidf_lo_test_xml_start, pidf_lo_test_xml_size);
ast_test_validate(test, pidf_xmldoc != NULL);
- res = validate_eprofile(test, pidf_xmldoc,
- NULL,
+ eprofile = ast_geoloc_eprofile_create_from_pidf(pidf_xmldoc, NULL, "test_create_from_pidf");
+ ast_test_validate(test, eprofile != NULL);
+
+
+ res = validate_eprofile(test, eprofile, pidf_xmldoc,
+ "pres:alice@asterisk.org",
"point-2d",
AST_PIDF_ELEMENT_TUPLE,
AST_GEOLOC_FORMAT_GML,
"Manual",
"shape=Point,crs=2d,pos=-34.410649 150.87651",
- "retransmission-allowed='no',retention-expiry='2010-11-14T20:00:00Z'"
+ "retransmission-allowed='no',retention-expiry='2010-11-14T20:00:00Z'",
+ "mac:112233445566"
);
- ast_test_validate(test, res == AST_TEST_PASS);
+ if (res != AST_TEST_PASS) {
+ return res;
+ }
- return res;
+ buf = ast_str_create(1024);
+ if (!buf) {
+ ast_log(LOG_ERROR, "Unable to allocate buf\n");
+ return AST_TEST_FAIL;
+ }
+
+ mock_channel = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, "TestChannel");
+ eprofile->effective_location = ast_variables_dup(eprofile->location_info);
+ eprofile_xmldoc = geoloc_eprofile_to_xmldoc(eprofile, mock_channel, &buf, "session_name");
+
+ return validate_xml(test, eprofile, pidf_xmldoc, eprofile_xmldoc);
}
static void load_tests(void) {
#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;
+ const char *name;
int min_required;
int max_allowed;
- int (*validator)(const char *value);
+ int (*validator)(const char *name, const char *value, const struct ast_variable *varlist,
+ char **result);
};
+#define MAX_SHAPE_ATTRIBUTES 9
struct geoloc_gml_shape_def {
const char *shape_type;
- struct geoloc_gml_attr required_attributes[8];
+ const char *crs;
+ struct geoloc_gml_attr required_attributes[MAX_SHAPE_ATTRIBUTES];
};
-static int pos_validator(const char *value)
+#define SET_RESULT(__result, ...) \
+({ \
+ if (__result) { \
+ __ast_asprintf(__FILE__, __LINE__, __PRETTY_FUNCTION__, result, __VA_ARGS__); \
+ } \
+})
+
+static int crs_validator(const char *name, const char *value, const struct ast_variable *varlist,
+ char **result)
{
- float lat;
- float lon;
- return (sscanf(value, "%f %f", &lat, &lon) == 2);
+ if (!ast_strings_equal(value, "2d") && !ast_strings_equal(value, "3d")) {
+ SET_RESULT(result, "Invalid crs '%s'. Must be either '2d' or '3d'", value);
+ return 0;
+ }
+ return 1;
}
-static int pos3d_validator(const char *value)
+static int pos_validator(const char *name, const char *value, const struct ast_variable *varlist,
+ char **result)
{
+ const char *crs = S_OR(ast_variable_find_in_list(varlist, "crs"), "2d");
float lat;
float lon;
float alt;
- return (sscanf(value, "%f %f %f", &lat, &lon, &alt) == 3);
+ int count;
+
+ count = sscanf(value, "%f %f %f", &lat, &lon, &alt);
+ if (ast_strings_equal(crs, "3d") && count != 3) {
+ SET_RESULT(result, "Invalid 3d position '%s'. Must be 3 floating point values.", value);
+ return 0;
+ }
+ if (ast_strings_equal(crs, "2d") && count != 2) {
+ SET_RESULT(result, "Invalid 2d position '%s'. Must be 2 floating point values.", value);
+ return 0;
+ }
+ return 1;
}
-static int float_validator(const char *value)
+static int float_validator(const char *name, const char *value, const struct ast_variable *varlist,
+ char **result)
{
float val;
- return (sscanf(value, "%f", &val) == 1);
+ if (sscanf(value, "%f", &val) != 1) {
+ SET_RESULT(result, "Invalid floating point value '%s' in '%s'.", value, name);
+ return 0;
+ }
+ return 1;
}
-static int uom_validator(const char *value)
+enum angle_parse_result {
+ ANGLE_PARSE_RESULT_SUCCESS = 0,
+ ANGLE_PARSE_ERROR_NO_ANGLE,
+ ANGLE_PARSE_ERROR_INVALID_ANGLE,
+ ANGLE_PARSE_ERROR_ANGLE_OUT_OF_RANGE,
+ ANGLE_PARSE_ERROR_INVALID_UOM,
+};
+
+static enum angle_parse_result angle_parser(const char *name, const char *value,
+ char **angle, char **uom, char **result)
{
- return (ast_strings_equal(value, "degrees") || ast_strings_equal(value, "radians"));
+ char *tmp_angle = NULL;
+ char *tmp_uom = NULL;
+ float f_angle;
+ char *junk;
+ char *work = ast_strdupa(value);
+
+ tmp_angle = ast_strsep(&work, ' ', AST_STRSEP_ALL);
+ if (ast_strlen_zero(tmp_angle)) {
+ SET_RESULT(result, "Empty angle in '%s'", name);
+ return ANGLE_PARSE_ERROR_NO_ANGLE;
+ }
+ f_angle = strtof(tmp_angle, &junk);
+ if (!ast_strlen_zero(junk)) {
+ SET_RESULT(result, "Invalid angle '%s' in '%s'", value, name);
+ return ANGLE_PARSE_ERROR_INVALID_ANGLE;
+ }
+
+ tmp_uom = ast_strsep(&work, ' ', AST_STRSEP_ALL);
+ if (ast_strlen_zero(tmp_uom)) {
+ tmp_uom = "degrees";
+ }
+
+ if (ast_begins_with(tmp_uom, "deg")) {
+ tmp_uom = "degrees";
+ } else if (ast_begins_with(tmp_uom, "rad")) {
+ tmp_uom = "radians";
+ } else {
+ SET_RESULT(result, "Invalid UOM '%s' in '%s'. Must be 'degrees' or 'radians'.", value, name);
+ return ANGLE_PARSE_ERROR_INVALID_UOM;
+ }
+
+ if (ast_strings_equal(tmp_uom, "degrees") && f_angle > 360.0) {
+ SET_RESULT(result, "Angle '%s' must be <= 360.0 for UOM '%s' in '%s'", tmp_angle, tmp_uom, name);
+ return ANGLE_PARSE_ERROR_ANGLE_OUT_OF_RANGE;
+ }
+
+ if (ast_strings_equal(tmp_uom, "radians") && f_angle > 100.0) {
+ SET_RESULT(result, "Angle '%s' must be <= 100.0 for UOM '%s' in '%s'", tmp_angle, tmp_uom, name);
+ return ANGLE_PARSE_ERROR_ANGLE_OUT_OF_RANGE;
+ }
+
+ if (angle) {
+ *angle = ast_strdup(tmp_angle);
+ }
+ if (uom) {
+ *uom = ast_strdup(tmp_uom);
+ }
+ return ANGLE_PARSE_RESULT_SUCCESS;
}
+static int angle_validator(const char *name, const char *value, const struct ast_variable *varlist,
+ char **result)
+{
+ enum angle_parse_result rc = angle_parser(name, value, NULL, NULL, result);
-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},
+ return rc == ANGLE_PARSE_RESULT_SUCCESS;
+}
+
+#define _SENTRY {NULL, -1, -1, NULL}
+
+#define CRS_OPT {"crs", 0, 1, crs_validator}
+#define CRS_REQ {"crs", 1, 1, crs_validator}
+
+static struct geoloc_gml_shape_def gml_shape_defs[] = {
+ { "Point", "any", { CRS_OPT, {"pos", 1, 1, pos_validator}, _SENTRY }},
+ { "Polygon", "any", { CRS_OPT, {"pos", 3, -1, pos_validator}, _SENTRY }},
+ { "Circle", "2d", { CRS_OPT, {"pos", 1, 1, pos_validator}, {"radius", 1, 1, float_validator}, _SENTRY }},
+ { "Ellipse", "2d", { CRS_OPT, {"pos", 1, 1, pos_validator}, {"semiMajorAxis", 1, 1, float_validator},
+ {"semiMinorAxis", 1, 1, float_validator}, {"orientation", 1, 1, angle_validator}, _SENTRY }},
+ { "ArcBand", "2d", { CRS_OPT, {"pos", 1, 1, pos_validator}, {"innerRadius", 1, 1, float_validator},
+ {"outerRadius", 1, 1, float_validator}, {"startAngle", 1, 1, angle_validator},
+ {"openingAngle", 1, 1, angle_validator},
+ _SENTRY }},
+ { "Sphere", "3d", { CRS_REQ, {"pos", 1, 1, pos_validator}, {"radius", 1, 1, float_validator}, _SENTRY }},
+ { "Ellipsoid", "3d", { CRS_REQ, {"pos", 1, 1, pos_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} }},
+ {"orientation", 1, 1, angle_validator}, _SENTRY }},
+ { "Prism", "3d", { CRS_REQ, {"pos", 3, -1, pos_validator}, {"height", 1, 1, float_validator}, _SENTRY }},
};
-enum ast_geoloc_validate_result ast_geoloc_gml_validate_varlist(const struct ast_variable *varlist,
- const char **result)
+static int find_shape_index(const char *shape)
{
- int def_index = -1;
- const struct ast_variable *var;
- int i;
- const char *shape_type = ast_variable_find_in_list(varlist, "shape");
+ int i = 0;
+ int shape_count = ARRAY_LEN(gml_shape_defs);
- if (!shape_type) {
- return AST_GEOLOC_VALIDATE_MISSING_SHAPE;
+ for (i = 0; i < shape_count; i++) {
+ if (ast_strings_equal(shape, gml_shape_defs[i].shape_type)) {
+ return i;
+ }
}
+ return -1;
+}
- 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;
+static int find_attribute_index(int shape_index, const char *name)
+{
+ int i = 0;
+
+ for (i = 0; i < MAX_SHAPE_ATTRIBUTES; i++) {
+ if (gml_shape_defs[shape_index].required_attributes[i].name == NULL) {
+ return -1;
+ }
+ if (ast_strings_equal(name, gml_shape_defs[shape_index].required_attributes[i].name)) {
+ return i;
}
}
- if (def_index < 0) {
- return AST_GEOLOC_VALIDATE_INVALID_SHAPE;
- }
+ return -1;
+}
+
+static enum ast_geoloc_validate_result validate_def_varlist(int shape_index, const struct ast_variable *varlist,
+ char **result)
+{
+ const struct ast_variable *var;
+ int i;
for (var = varlist; var; var = var->next) {
int vname_index = -1;
if (ast_strings_equal("shape", 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;
- }
- }
+
+ vname_index = find_attribute_index(shape_index, var->name);
if (vname_index < 0) {
- *result = var->name;
+ SET_RESULT(result, "Invalid variable name '%s'\n", var->name);
return AST_GEOLOC_VALIDATE_INVALID_VARNAME;
}
- if (!gml_shape_defs[def_index].required_attributes[vname_index].validator(var->value)) {
- *result = var->name;
+ if (!gml_shape_defs[shape_index].required_attributes[vname_index].validator(var->name, var->value,
+ varlist, result)) {
return AST_GEOLOC_VALIDATE_INVALID_VALUE;
}
}
- for (i = 0; i < ARRAY_LEN(gml_shape_defs[def_index].required_attributes); i++) {
+ for (i = 0; i < ARRAY_LEN(gml_shape_defs[shape_index].required_attributes); i++) {
int count = 0;
- if (gml_shape_defs[def_index].required_attributes[i].attribute == NULL) {
+ if (gml_shape_defs[shape_index].required_attributes[i].name == NULL) {
break;
}
for (var = varlist; var; var = var->next) {
- if (ast_strings_equal(gml_shape_defs[def_index].required_attributes[i].attribute, var->name)) {
+ if (ast_strings_equal(gml_shape_defs[shape_index].required_attributes[i].name, 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;
+ if (count < gml_shape_defs[shape_index].required_attributes[i].min_required) {
+ SET_RESULT(result, "Number of '%s' variables %d is < %d",
+ gml_shape_defs[shape_index].required_attributes[i].name,
+ count,
+ gml_shape_defs[shape_index].required_attributes[i].min_required);
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;
+ if (gml_shape_defs[shape_index].required_attributes[i].max_allowed > 0 &&
+ count > gml_shape_defs[shape_index].required_attributes[i].max_allowed) {
+ SET_RESULT(result, "Number of '%s' variables %d is > %d",
+ gml_shape_defs[shape_index].required_attributes[i].name,
+ count,
+ gml_shape_defs[shape_index].required_attributes[i].max_allowed);
return AST_GEOLOC_VALIDATE_TOO_MANY_VARNAMES;
}
}
+
return AST_GEOLOC_VALIDATE_SUCCESS;
}
+enum ast_geoloc_validate_result ast_geoloc_gml_validate_varlist(struct ast_variable *varlist,
+ char **result)
+{
+ const char *shape_type = ast_variable_find_in_list(varlist, "shape");
+ int shape_index = -1;
+ const char *crs = ast_variable_find_in_list(varlist, "crs");
+
+ if (!shape_type) {
+ SET_RESULT(result, "Missing 'shape'");
+ return AST_GEOLOC_VALIDATE_MISSING_SHAPE;
+ }
+
+ shape_index = find_shape_index(shape_type);
+ if (shape_index < 0) {
+ SET_RESULT(result, "Invalid shape '%s'", shape_type);
+ return AST_GEOLOC_VALIDATE_INVALID_SHAPE;
+ }
+
+ if (ast_strlen_zero(crs)) {
+ struct ast_variable *vcrs = NULL;
+ if (ast_strings_equal("any", gml_shape_defs[shape_index].crs)) {
+ crs = "2d";
+ } else {
+ crs = gml_shape_defs[shape_index].crs;
+ }
+
+ vcrs = ast_variable_new("crs", "2d", "");
+ if (vcrs) {
+ ast_variable_list_append(&varlist, vcrs);
+ }
+ }
+ if (!crs_validator("crs", crs, varlist, result)) {
+ return AST_GEOLOC_VALIDATE_INVALID_CRS;
+ }
+
+ if (!ast_strings_equal("any", gml_shape_defs[shape_index].crs)
+ && !ast_strings_equal(crs, gml_shape_defs[shape_index].crs)) {
+ SET_RESULT(result, "Invalid crs '%s' for shape '%s'", crs, shape_type);
+ return AST_GEOLOC_VALIDATE_INVALID_CRS_FOR_SHAPE;
+ }
+
+ return validate_def_varlist(shape_index, varlist, result);
+}
+
static char *handle_gml_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
int i;
return NULL;
}
- ast_cli(a->fd, "%-16s %-32s\n", "Shape", "Attributes name(min,max)");
- ast_cli(a->fd, "================ ===============================\n");
+ ast_cli(a->fd, "%-16s %-3s %-32s\n", "Shape", "CRS", "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);
+ ast_cli(a->fd, "%-16s %-3s", gml_shape_defs[i].shape_type, gml_shape_defs[i].crs);
for (j = 0; j < ARRAY_LEN(gml_shape_defs[i].required_attributes); j++) {
- if (gml_shape_defs[i].required_attributes[j].attribute == NULL) {
+ if (gml_shape_defs[i].required_attributes[j].name == 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,
+ ast_cli(a->fd, " %s(%d,%d)", gml_shape_defs[i].required_attributes[j].name,
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,
+ ast_cli(a->fd, " %s(%d,unl)", gml_shape_defs[i].required_attributes[j].name,
gml_shape_defs[i].required_attributes[j].min_required);
}
}
AST_CLI_DEFINE(handle_gml_show, "Show the GML Shape definitions"),
};
-struct ast_xml_node *geoloc_gml_list_to_xml(const struct ast_variable *resolved_location,
+struct ast_xml_node *geoloc_gml_list_to_xml(struct ast_variable *resolved_location,
const char *ref_string)
{
const char *shape;
- char *crs;
+ const char *crs;
struct ast_variable *var;
struct ast_xml_node *gml_node;
struct ast_xml_node *child_node;
+ enum ast_geoloc_validate_result res;
+ RAII_VAR(char *, result, NULL, ast_free);
int rc = 0;
SCOPE_ENTER(3, "%s", ref_string);
ref_string);
}
+ res = ast_geoloc_gml_validate_varlist(resolved_location, &result);
+ if (res != AST_GEOLOC_VALIDATE_SUCCESS) {
+ SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: %s\n",
+ ref_string, result);
+ }
+
shape = ast_variable_find_in_list(resolved_location, "shape");
if (ast_strlen_zero(shape)) {
SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: There's no 'shape' parameter\n",
ref_string);
}
- crs = (char *)ast_variable_find_in_list(resolved_location, "crs");
+
+ crs = ast_variable_find_in_list(resolved_location, "crs");
if (ast_strlen_zero(crs)) {
+ struct ast_variable *vcrs = ast_variable_new("crs", "2d", "");
+ if (vcrs) {
+ ast_variable_list_append(&resolved_location, vcrs);
+ }
crs = "2d";
}
}
for (var = (struct ast_variable *)resolved_location; var; var = var->next) {
- RAII_VAR(char *, value, NULL, ast_free);
- char *uom = NULL;
if (ast_strings_equal(var->name, "shape") || ast_strings_equal(var->name, "crs")) {
continue;
}
- value = ast_strdup(var->value);
-
- if (ast_strings_equal(var->name, "orientation") || ast_strings_equal(var->name, "startAngle")
- || ast_strings_equal(var->name, "openingAngle")) {
- char *a = NULL;
- char *junk = NULL;
- float angle;
- uom = value;
-
- /* 'a' should now be the angle and 'uom' should be the uom */
- a = strsep(&uom, " ");
- angle = strtof(a, &junk);
- /*
- * strtof sets junk to the first non-valid character so if it's
- * not empty after the conversion, there were unrecognized
- * characters in the angle. It'll point to the NULL terminator
- * if angle was completely converted.
- */
- if (!ast_strlen_zero(junk)) {
- ast_xml_free_node(gml_node);
- SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: The angle portion of parameter '%s' ('%s') is malformed\n",
- ref_string, var->name, var->value);
- }
-
- if (ast_strlen_zero(uom)) {
- uom = "degrees";
- }
-
- if (ast_begins_with(uom, "deg")) {
- if (angle > 360.0) {
- ast_xml_free_node(gml_node);
- SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Parameter '%s': '%s' is malformed. "
- "Degrees can't be > 360.0\n",
- ref_string, var->name, var->value);
- }
- } else if (ast_begins_with(uom, "rad")) {
- if(angle > 100.0) {
- ast_xml_free_node(gml_node);
- SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Parameter '%s': '%s' is malformed. "
- "Radians can't be > 100.0\n",
- ref_string, var->name, var->value);
- }
- } else {
- ast_xml_free_node(gml_node);
- SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Parameter '%s': '%s' is malformed. "
- "The unit of measure must be 'deg[rees]' or 'rad[ians]'\n",
- ref_string, var->name, var->value);
- }
- }
child_node = ast_xml_new_child(gml_node, var->name);
if (!child_node) {
ast_xml_free_node(gml_node);
SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create '%s' XML node\n", var->name, ref_string);
}
- if (!ast_strlen_zero(uom)) {
+
+ if (ast_strings_equal(var->name, "orientation") || ast_strings_equal(var->name, "startAngle")
+ || ast_strings_equal(var->name, "openingAngle")) {
+ RAII_VAR(char *, angle, NULL, ast_free);
+ RAII_VAR(char *, uom, NULL, ast_free);
+
+ enum angle_parse_result rc = angle_parser(var->name, var->value, &angle, &uom, &result);
+ if (rc != ANGLE_PARSE_RESULT_SUCCESS) {
+ ast_xml_free_node(gml_node);
+ SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: %s\n", ref_string, result);
+ }
rc = ast_xml_set_attribute(child_node, "uom", uom);
if (rc != 0) {
ast_xml_free_node(gml_node);
SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create 'uom' XML attribute\n", ref_string);
}
+ ast_xml_set_text(child_node, angle);
+ } else {
+ ast_xml_set_text(child_node, var->value);
}
- ast_xml_set_text(child_node, value);
}
SCOPE_EXIT_RTN_VALUE(gml_node, "%s: Done\n", ref_string);
int geoloc_civicaddr_unload(void);
int geoloc_civicaddr_reload(void);
-struct ast_xml_node *geoloc_gml_list_to_xml(const struct ast_variable *resolved_location,
+struct ast_xml_node *geoloc_gml_list_to_xml(struct ast_variable *resolved_location,
const char *ref_string);
int geoloc_gml_unload(void);
int geoloc_gml_load(void);
struct ast_variable *geoloc_eprofile_resolve_varlist(struct ast_variable *source,
struct ast_variable *variables, struct ast_channel *chan);
+char *geoloc_eprofile_resolve_string(const char *source,
+ struct ast_variable *variables, struct ast_channel *chan);
#endif /* GEOLOC_PRIVATE_H_ */
<?xml version="1.0" encoding="UTF-8"?>
<presence entity="pres:alice@asterisk.org"
- xmlns="urn:ietf:params:xml:ns:pidf"
- xmlns:ca="urn:ietf:params:xml:ns:pidf:geopriv10:civicAddr"
- xmlns:dm="urn:ietf:params:xml:ns:pidf:data-model"
- xmlns:gbp="urn:ietf:params:xml:ns:pidf:geopriv10:basicPolicy"
- xmlns:gml="http://www.opengis.net/gml"
- xmlns:gp="urn:ietf:params:xml:ns:pidf:geopriv10"
- xmlns:con="urn:ietf:params:xml:ns:geopriv:conf"
- xmlns:gs="http://www.opengis.net/pidflo/1.0">
- <tuple id="point-2d">
- <status>
- <gp:geopriv>
- <gp:location-info>
- <gml:Point srsName="urn:ogc:def:crs:EPSG::4326">
- <gml:pos>-34.410649 150.87651</gml:pos>
- </gml:Point>
- <con:confidence pdf="normal">66</con:confidence>
- </gp:location-info>
- <gp:usage-rules>
- <gbp:retransmission-allowed>no</gbp:retransmission-allowed>
- <gbp:retention-expiry>2010-11-14T20:00:00Z</gbp:retention-expiry>
- </gp:usage-rules>
- <gp:method>Manual</gp:method>
- <gp:note-well>
- this is a test
- of the emergency broadcast system
- </gp:note-well>
- </gp:geopriv>
- </status>
- <timestamp>2007-06-22T20:57:29Z</timestamp>
- </tuple>
+ xmlns="urn:ietf:params:xml:ns:pidf"
+ xmlns:ca="urn:ietf:params:xml:ns:pidf:geopriv10:civicAddr"
+ xmlns:dm="urn:ietf:params:xml:ns:pidf:data-model"
+ xmlns:gbp="urn:ietf:params:xml:ns:pidf:geopriv10:basicPolicy"
+ xmlns:gml="http://www.opengis.net/gml"
+ xmlns:gp="urn:ietf:params:xml:ns:pidf:geopriv10"
+ xmlns:con="urn:ietf:params:xml:ns:geopriv:conf"
+ xmlns:gs="http://www.opengis.net/pidflo/1.0">
+ <tuple id="point-2d">
+ <status>
+ <gp:geopriv>
+ <gp:location-info>
+ <gml:Point srsName="urn:ogc:def:crs:EPSG::4326">
+ <gml:pos>-34.410649 150.87651</gml:pos>
+ </gml:Point>
+ <con:confidence pdf="normal">66</con:confidence>
+ </gp:location-info>
+ <gp:usage-rules>
+ <gbp:retransmission-allowed>no</gbp:retransmission-allowed>
+ <gbp:retention-expiry>2010-11-14T20:00:00Z</gbp:retention-expiry>
+ </gp:usage-rules>
+ <gp:method>Manual</gp:method>
+ <gp:note-well>
+ this is a test
+ of the emergency broadcast system
+ </gp:note-well>
+ </gp:geopriv>
+ </status>
+ <timestamp>2007-06-22T20:57:29Z</timestamp>
+ <dm:deviceID>mac:112233445566</dm:deviceID>
+ </tuple>
</presence>
<xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
<xsl:call-template name="geopriv"/>
<xsl:apply-templates select="./def:timestamp"/>
+ <xsl:if test="./dm:deviceID">
+ <deviceID>
+ <xsl:value-of select="./dm:deviceID"/>
+ </deviceID>
+ </xsl:if>
</xsl:element>
</xsl:template>
<xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
<xsl:call-template name="geopriv"/>
<xsl:apply-templates select="./dm:timestamp"/>
- <!-- deviceID should only apply to devices -->
<xsl:if test="./dm:deviceID">
<deviceID>
<xsl:value-of select="./dm:deviceID"/>
<xsl:template name="angle">
<xsl:element name="{local-name(.)}">
<xsl:choose>
- <xsl:when test="@uom = 'urn:ogc:def:uom:EPSG::9102'">
+ <xsl:when test="@uom = 'urn:ogc:def:uom:EPSG::9101'">
<xsl:attribute name="uom">radians</xsl:attribute></xsl:when>
<xsl:otherwise>
<xsl:attribute name="uom">degrees</xsl:attribute></xsl:otherwise>