Asterisk - The Open Source Telephony Project  21.4.1
geoloc_gml.c
1 /*
2  * Copyright (C) 2022, Sangoma Technologies Corporation
3  *
4  * George Joseph <gjoseph@sangoma.com>
5  *
6  * See http://www.asterisk.org for more information about
7  * the Asterisk project. Please do not directly contact
8  * any of the maintainers of this project for assistance;
9  * the project provides a web site, mailing lists and IRC
10  * channels for your use.
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License Version 2. See the LICENSE file
14  * at the top of the source tree.
15  */
16 
17 #include "asterisk.h"
18 #include "asterisk/config.h"
19 #include "asterisk/cli.h"
20 #include "asterisk/res_geolocation.h"
21 #include "geoloc_private.h"
22 
23 
24 #if 1 //not used yet.
25 enum geoloc_shape_attrs {
26  GEOLOC_SHAPE_ATTR_POS = 0,
27  GEOLOC_SHAPE_ATTR_POS3D,
28  GEOLOC_SHAPE_ATTR_RADIUS,
29  GEOLOC_SHAPE_ATTR_SEMI_MAJOR_AXIS,
30  GEOLOC_SHAPE_ATTR_SEMI_MINOR_AXIS,
31  GEOLOC_SHAPE_ATTR_VERTICAL_AXIS,
32  GEOLOC_SHAPE_ATTR_HEIGHT,
33  GEOLOC_SHAPE_ATTR_ORIENTATION,
34  GEOLOC_SHAPE_ATTR_ORIENTATION_UOM,
35  GEOLOC_SHAPE_ATTR_INNER_RADIUS,
36  GEOLOC_SHAPE_ATTR_OUTER_RADIUS,
37  GEOLOC_SHAPE_ATTR_STARTING_ANGLE,
38  GEOLOC_SHAPE_ATTR_OPENING_ANGLE,
39  GEOLOC_SHAPE_ATTR_ANGLE_UOM,
40 };
41 
43  enum geoloc_shape_attrs attr;
44  const char *name;
45  int (*validator)(const char *value);
46  int (*transformer)(struct ast_variable *value);
47 };
48 
49 struct geoloc_gml_attr_def gml_attr_defs[] = {
50  { GEOLOC_SHAPE_ATTR_POS, "pos", NULL, NULL},
51  { GEOLOC_SHAPE_ATTR_POS3D,"pos3d", NULL, NULL},
52  { GEOLOC_SHAPE_ATTR_RADIUS,"radius", NULL, NULL},
53  { GEOLOC_SHAPE_ATTR_SEMI_MAJOR_AXIS,"semiMajorAxis", NULL, NULL},
54  { GEOLOC_SHAPE_ATTR_SEMI_MINOR_AXIS,"semiMinorAxis", NULL, NULL},
55  { GEOLOC_SHAPE_ATTR_VERTICAL_AXIS,"verticalAxis", NULL, NULL},
56  { GEOLOC_SHAPE_ATTR_HEIGHT,"height", NULL, NULL},
57  { GEOLOC_SHAPE_ATTR_ORIENTATION,"orientation", NULL, NULL},
58  { GEOLOC_SHAPE_ATTR_ORIENTATION_UOM,"orientation_uom", NULL, NULL},
59  { GEOLOC_SHAPE_ATTR_INNER_RADIUS,"innerRadius", NULL, NULL},
60  { GEOLOC_SHAPE_ATTR_OUTER_RADIUS,"outerRadius", NULL, NULL},
61  { GEOLOC_SHAPE_ATTR_STARTING_ANGLE,"startingAngle", NULL, NULL},
62  { GEOLOC_SHAPE_ATTR_OPENING_ANGLE,"openingAngle", NULL, NULL},
63  { GEOLOC_SHAPE_ATTR_ANGLE_UOM,"angle_uom", NULL, NULL},
64 };
65 #endif //not used yet.
66 
68  const char *attribute;
69  int min_required;
70  int max_allowed;
71  int (*validator)(const char *value);
72 };
73 
75  const char *shape_type;
76  struct geoloc_gml_attr required_attributes[8];
77 };
78 
79 static int pos_validator(const char *value)
80 {
81  float lat;
82  float lon;
83  return (sscanf(value, "%f %f", &lat, &lon) == 2);
84 }
85 
86 static int pos3d_validator(const char *value)
87 {
88  float lat;
89  float lon;
90  float alt;
91  return (sscanf(value, "%f %f %f", &lat, &lon, &alt) == 3);
92 }
93 
94 static int float_validator(const char *value)
95 {
96  float val;
97  return (sscanf(value, "%f", &val) == 1);
98 }
99 
100 static int uom_validator(const char *value)
101 {
102  return (ast_strings_equal(value, "degrees") || ast_strings_equal(value, "radians"));
103 }
104 
105 
106 static struct geoloc_gml_shape_def gml_shape_defs[8] = {
107  { "Point", { {"pos", 1, 1, pos_validator}, {NULL, -1, -1} }},
108  { "Polygon", { {"pos", 3, -1, pos_validator}, {NULL, -1, -1} }},
109  { "Circle", { {"pos", 1, 1, pos_validator}, {"radius", 1, 1, float_validator},{NULL, -1, -1}}},
110  { "Ellipse", { {"pos", 1, 1, pos_validator}, {"semiMajorAxis", 1, 1, float_validator},
111  {"semiMinorAxis", 1, 1, float_validator}, {"orientation", 1, 1, float_validator},
112  {"orientation_uom", 1, 1, uom_validator}, {NULL, -1, -1} }},
113  { "ArcBand", { {"pos", 1, 1, pos_validator}, {"innerRadius", 1, 1, float_validator},
114  {"outerRadius", 1, 1, float_validator}, {"startAngle", 1, 1, float_validator},
115  {"startAngle_uom", 1, 1, uom_validator}, {"openingAngle", 1, 1, float_validator},
116  {"openingAngle_uom", 1, 1, uom_validator}, {NULL, -1, -1} }},
117  { "Sphere", { {"pos3d", 1, 1, pos3d_validator}, {"radius", 1, 1, float_validator}, {NULL, -1, -1} }},
118  { "Ellipse", { {"pos3d", 1, 1, pos3d_validator}, {"semiMajorAxis", 1, 1, float_validator},
119  {"semiMinorAxis", 1, 1, float_validator}, {"verticalAxis", 1, 1, float_validator},
120  {"orientation", 1, 1, float_validator}, {"orientation_uom", 1, 1, uom_validator}, {NULL, -1, -1} }},
121  { "Prism", { {"pos3d", 3, -1, pos_validator}, {"height", 1, 1, float_validator}, {NULL, -1, -1} }},
122 };
123 
124 enum ast_geoloc_validate_result ast_geoloc_gml_validate_varlist(const struct ast_variable *varlist,
125  const char **result)
126 {
127  int def_index = -1;
128  const struct ast_variable *var;
129  int i;
130  const char *shape_type = ast_variable_find_in_list(varlist, "shape");
131 
132  if (!shape_type) {
133  return AST_GEOLOC_VALIDATE_MISSING_SHAPE;
134  }
135 
136  for (i = 0; i < ARRAY_LEN(gml_shape_defs); i++) {
137  if (ast_strings_equal(gml_shape_defs[i].shape_type, shape_type)) {
138  def_index = i;
139  }
140  }
141  if (def_index < 0) {
142  return AST_GEOLOC_VALIDATE_INVALID_SHAPE;
143  }
144 
145  for (var = varlist; var; var = var->next) {
146  int vname_index = -1;
147  if (ast_strings_equal("shape", var->name)) {
148  continue;
149  }
150  for (i = 0; i < ARRAY_LEN(gml_shape_defs[def_index].required_attributes); i++) {
151  if (gml_shape_defs[def_index].required_attributes[i].attribute == NULL) {
152  break;
153  }
154  if (ast_strings_equal(gml_shape_defs[def_index].required_attributes[i].attribute, var->name)) {
155  vname_index = i;
156  break;
157  }
158  }
159  if (vname_index < 0) {
160  *result = var->name;
161  return AST_GEOLOC_VALIDATE_INVALID_VARNAME;
162  }
163  if (!gml_shape_defs[def_index].required_attributes[vname_index].validator(var->value)) {
164  *result = var->name;
165  return AST_GEOLOC_VALIDATE_INVALID_VALUE;
166  }
167  }
168 
169  for (i = 0; i < ARRAY_LEN(gml_shape_defs[def_index].required_attributes); i++) {
170  int count = 0;
171  if (gml_shape_defs[def_index].required_attributes[i].attribute == NULL) {
172  break;
173  }
174 
175  for (var = varlist; var; var = var->next) {
176  if (ast_strings_equal(gml_shape_defs[def_index].required_attributes[i].attribute, var->name)) {
177  count++;
178  }
179  }
180  if (count < gml_shape_defs[def_index].required_attributes[i].min_required) {
181  *result = gml_shape_defs[def_index].required_attributes[i].attribute;
182  return AST_GEOLOC_VALIDATE_NOT_ENOUGH_VARNAMES;
183  }
184  if (gml_shape_defs[def_index].required_attributes[i].max_allowed > 0 &&
185  count > gml_shape_defs[def_index].required_attributes[i].max_allowed) {
186  *result = gml_shape_defs[def_index].required_attributes[i].attribute;
187  return AST_GEOLOC_VALIDATE_TOO_MANY_VARNAMES;
188  }
189  }
190  return AST_GEOLOC_VALIDATE_SUCCESS;
191 }
192 
193 static char *handle_gml_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
194 {
195  int i;
196 
197  switch (cmd) {
198  case CLI_INIT:
199  e->command = "geoloc show gml_shape_defs";
200  e->usage =
201  "Usage: geoloc show gml_shape_defs\n"
202  " Show the GML Shape definitions.\n";
203  return NULL;
204  case CLI_GENERATE:
205  return NULL;
206  }
207 
208  ast_cli(a->fd, "%-16s %-32s\n", "Shape", "Attributes name(min,max)");
209  ast_cli(a->fd, "================ ===============================\n");
210 
211  for (i = 0; i < ARRAY_LEN(gml_shape_defs); i++) {
212  int j;
213  ast_cli(a->fd, "%-16s", gml_shape_defs[i].shape_type);
214  for (j = 0; j < ARRAY_LEN(gml_shape_defs[i].required_attributes); j++) {
215  if (gml_shape_defs[i].required_attributes[j].attribute == NULL) {
216  break;
217  }
218  if (gml_shape_defs[i].required_attributes[j].max_allowed >= 0) {
219  ast_cli(a->fd, " %s(%d,%d)", gml_shape_defs[i].required_attributes[j].attribute,
220  gml_shape_defs[i].required_attributes[j].min_required,
221  gml_shape_defs[i].required_attributes[j].max_allowed);
222  } else {
223  ast_cli(a->fd, " %s(%d,unl)", gml_shape_defs[i].required_attributes[j].attribute,
224  gml_shape_defs[i].required_attributes[j].min_required);
225  }
226  }
227  ast_cli(a->fd, "\n");
228  }
229  ast_cli(a->fd, "\n");
230 
231  return CLI_SUCCESS;
232 }
233 
234 static struct ast_cli_entry geoloc_gml_cli[] = {
235  AST_CLI_DEFINE(handle_gml_show, "Show the GML Shape definitions"),
236 };
237 
238 struct ast_xml_node *geoloc_gml_list_to_xml(const struct ast_variable *resolved_location,
239  const char *ref_string)
240 {
241  const char *shape;
242  char *crs;
243  struct ast_variable *var;
244  struct ast_xml_node *gml_node;
245  struct ast_xml_node *child_node;
246  int rc = 0;
247 
248  SCOPE_ENTER(3, "%s", ref_string);
249 
250  if (!resolved_location) {
251  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: resolved_location was NULL\n",
252  ref_string);
253  }
254 
255  shape = ast_variable_find_in_list(resolved_location, "shape");
256  if (ast_strlen_zero(shape)) {
257  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: There's no 'shape' parameter\n",
258  ref_string);
259  }
260  crs = (char *)ast_variable_find_in_list(resolved_location, "crs");
261  if (ast_strlen_zero(crs)) {
262  crs = "2d";
263  }
264 
265  gml_node = ast_xml_new_node(shape);
266  if (!gml_node) {
267  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create '%s' XML node\n", shape, ref_string);
268  }
269  rc = ast_xml_set_attribute(gml_node, "crs", crs);
270  if (rc != 0) {
271  ast_xml_free_node(gml_node);
272  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create 'crs' XML attribute\n", ref_string);
273  }
274 
275  for (var = (struct ast_variable *)resolved_location; var; var = var->next) {
276  RAII_VAR(char *, value, NULL, ast_free);
277  char *uom = NULL;
278 
279  if (ast_strings_equal(var->name, "shape") || ast_strings_equal(var->name, "crs")) {
280  continue;
281  }
282  value = ast_strdup(var->value);
283 
284  if (ast_strings_equal(var->name, "orientation") || ast_strings_equal(var->name, "startAngle")
285  || ast_strings_equal(var->name, "openingAngle")) {
286  char *a = NULL;
287  char *junk = NULL;
288  float angle;
289  uom = value;
290 
291  /* 'a' should now be the angle and 'uom' should be the uom */
292  a = strsep(&uom, " ");
293  angle = strtof(a, &junk);
294  /*
295  * strtof sets junk to the first non-valid character so if it's
296  * not empty after the conversion, there were unrecognized
297  * characters in the angle. It'll point to the NULL terminator
298  * if angle was completely converted.
299  */
300  if (!ast_strlen_zero(junk)) {
301  ast_xml_free_node(gml_node);
302  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: The angle portion of parameter '%s' ('%s') is malformed\n",
303  ref_string, var->name, var->value);
304  }
305 
306  if (ast_strlen_zero(uom)) {
307  uom = "degrees";
308  }
309 
310  if (ast_begins_with(uom, "deg")) {
311  if (angle > 360.0) {
312  ast_xml_free_node(gml_node);
313  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Parameter '%s': '%s' is malformed. "
314  "Degrees can't be > 360.0\n",
315  ref_string, var->name, var->value);
316  }
317  } else if (ast_begins_with(uom, "rad")) {
318  if(angle > 100.0) {
319  ast_xml_free_node(gml_node);
320  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Parameter '%s': '%s' is malformed. "
321  "Radians can't be > 100.0\n",
322  ref_string, var->name, var->value);
323  }
324  } else {
325  ast_xml_free_node(gml_node);
326  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Parameter '%s': '%s' is malformed. "
327  "The unit of measure must be 'deg[rees]' or 'rad[ians]'\n",
328  ref_string, var->name, var->value);
329  }
330  }
331 
332  child_node = ast_xml_new_child(gml_node, var->name);
333  if (!child_node) {
334  ast_xml_free_node(gml_node);
335  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create '%s' XML node\n", var->name, ref_string);
336  }
337  if (!ast_strlen_zero(uom)) {
338  rc = ast_xml_set_attribute(child_node, "uom", uom);
339  if (rc != 0) {
340  ast_xml_free_node(gml_node);
341  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create 'uom' XML attribute\n", ref_string);
342  }
343  }
344  ast_xml_set_text(child_node, value);
345  }
346 
347  SCOPE_EXIT_RTN_VALUE(gml_node, "%s: Done\n", ref_string);
348 }
349 
350 int geoloc_gml_unload(void)
351 {
352  ast_cli_unregister_multiple(geoloc_gml_cli, ARRAY_LEN(geoloc_gml_cli));
353 
355 }
356 
357 int geoloc_gml_load(void)
358 {
359  ast_cli_register_multiple(geoloc_gml_cli, ARRAY_LEN(geoloc_gml_cli));
360 
362 }
363 
364 int geoloc_gml_reload(void)
365 {
367 }
struct ast_variable * next
Asterisk main include file. File version handling, generic pbx functions.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
descriptor for a cli entry.
Definition: cli.h:171
void ast_xml_free_node(struct ast_xml_node *node)
Free node.
Definition: xml.c:243
Structure for variables, used for configurations and for channel variables.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
struct ast_xml_node * ast_xml_new_child(struct ast_xml_node *parent, const char *child_name)
Add a child node inside a passed parent node.
Definition: xml.c:156
Configuration File Parser.
const char * ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
Gets the value of a variable from a variable list by name.
Definition: main/config.c:919
struct ast_xml_node * ast_xml_new_node(const char *name)
Create a XML node.
Definition: xml.c:144
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition: strings.c:238
void ast_xml_set_text(struct ast_xml_node *node, const char *content)
Set an element content string.
Definition: xml.c:362
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
int ast_xml_set_attribute(struct ast_xml_node *node, const char *name, const char *value)
Set an attribute to a node.
Definition: xml.c:284
Standard Command Line Interface.
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition: strings.h:97
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941