mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 18:55:19 +00:00 
			
		
		
		
	
		
			
	
	
		
			363 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			363 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * Copyright (C) 2022, Sangoma Technologies Corporation | ||
|  |  * | ||
|  |  * George Joseph <gjoseph@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, "shape"); | ||
|  | 
 | ||
|  | 	if (!shape_type) { | ||
|  | 		return AST_GEOLOC_VALIDATE_MISSING_SHAPE; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	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_SHAPE; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	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; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		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"), | ||
|  | }; | ||
|  | 
 | ||
|  | struct ast_xml_node *geoloc_gml_list_to_xml(const struct ast_variable *resolved_location, | ||
|  | 	const char *ref_string) | ||
|  | { | ||
|  | 	const char *shape; | ||
|  | 	char *crs; | ||
|  | 	struct ast_variable *var; | ||
|  | 	struct ast_xml_node *gml_node; | ||
|  | 	struct ast_xml_node *child_node; | ||
|  | 	int rc = 0; | ||
|  | 
 | ||
|  | 	SCOPE_ENTER(3, "%s", ref_string); | ||
|  | 
 | ||
|  | 	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"); | ||
|  | 	if (ast_strlen_zero(crs)) { | ||
|  | 		crs = "2d"; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	gml_node = ast_xml_new_node(shape); | ||
|  | 	if (!gml_node) { | ||
|  | 		SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create '%s' XML node\n", shape, ref_string); | ||
|  | 	} | ||
|  | 	rc = ast_xml_set_attribute(gml_node, "crs", crs); | ||
|  | 	if (rc != 0) { | ||
|  | 		ast_xml_free_node(gml_node); | ||
|  | 		SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create 'crs' XML attribute\n", ref_string); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	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)) { | ||
|  | 			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, value); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	SCOPE_EXIT_RTN_VALUE(gml_node, "%s: Done\n", ref_string); | ||
|  | } | ||
|  | 
 | ||
|  | 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; | ||
|  | } |