Asterisk - The Open Source Telephony Project  21.4.1
geoloc_eprofile.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2022, Sangoma Technologies Corporation
5  *
6  * George Joseph <gjoseph@sangoma.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 #include "asterisk.h"
20 #include "asterisk/pbx.h"
21 #include "asterisk/strings.h"
22 #include "asterisk/xml.h"
23 #include "geoloc_private.h"
24 
25 extern const uint8_t _binary_res_geolocation_pidf_to_eprofile_xslt_start[];
26 extern const uint8_t _binary_res_geolocation_pidf_to_eprofile_xslt_end[];
27 static size_t pidf_to_eprofile_xslt_size;
28 
29 extern const uint8_t _binary_res_geolocation_pidf_lo_test_xml_start[];
30 extern const uint8_t _binary_res_geolocation_pidf_lo_test_xml_end[];
31 static size_t pidf_lo_test_xml_size;
32 
33 extern const uint8_t _binary_res_geolocation_eprofile_to_pidf_xslt_start[];
34 extern const uint8_t _binary_res_geolocation_eprofile_to_pidf_xslt_end[];
35 static size_t eprofile_to_pidf_xslt_size;
36 
37 static struct ast_xslt_doc *eprofile_to_pidf_xslt;
38 static struct ast_xslt_doc *pidf_to_eprofile_xslt;
39 
40 static struct ast_sorcery *geoloc_sorcery;
41 
42 #define DUP_VARS(_dest, _source) \
43 ({ \
44  int _rc = 0; \
45  if (_source) { \
46  struct ast_variable *_vars = ast_variables_dup(_source); \
47  if (!_vars) { \
48  _rc = -1; \
49  } else { \
50  _dest = _vars; \
51  } \
52  } \
53  (_rc); \
54 })
55 
56 static void geoloc_eprofile_destructor(void *obj)
57 {
58  struct ast_geoloc_eprofile *eprofile = obj;
59 
61  ast_variables_destroy(eprofile->location_info);
62  ast_variables_destroy(eprofile->location_refinement);
63  ast_variables_destroy(eprofile->location_variables);
64  ast_variables_destroy(eprofile->effective_location);
65  ast_variables_destroy(eprofile->usage_rules);
66  ast_variables_destroy(eprofile->confidence);
67 }
68 
69 struct ast_geoloc_eprofile *ast_geoloc_eprofile_alloc(const char *name)
70 {
71  struct ast_geoloc_eprofile *eprofile = ao2_alloc_options(sizeof(*eprofile),
72  geoloc_eprofile_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
73 
74  ast_string_field_init(eprofile, 256);
75  ast_string_field_set(eprofile, id, name); /* SAFE string fields handle NULL */
76 
77  return eprofile;
78 }
79 
80 int ast_geoloc_eprofile_refresh_location(struct ast_geoloc_eprofile *eprofile)
81 {
82  struct ast_geoloc_location *loc = NULL;
83  RAII_VAR(struct ast_variable *, temp_locinfo, NULL, ast_variables_destroy);
84  RAII_VAR(struct ast_variable *, temp_effloc, NULL, ast_variables_destroy);
85  RAII_VAR(struct ast_variable *, temp_confidence, NULL, ast_variables_destroy);
86  const char *method = NULL;
87  const char *location_source = NULL;
88  enum ast_geoloc_format format;
89  struct ast_variable *var;
90  int rc = 0;
91 
92  if (!eprofile) {
93  return -1;
94  }
95 
96  if (!ast_strlen_zero(eprofile->location_reference)) {
97  loc = ast_sorcery_retrieve_by_id(geoloc_sorcery, "location", eprofile->location_reference);
98  if (!loc) {
99  ast_log(LOG_ERROR, "Profile '%s' referenced location '%s' does not exist!", eprofile->id,
100  eprofile->location_reference);
101  return -1;
102  }
103 
104  format = loc->format;
105  method = loc->method;
106  location_source = loc->location_source;
107  rc = DUP_VARS(temp_locinfo, loc->location_info);
108  if (rc == 0) {
109  rc = DUP_VARS(temp_confidence, loc->confidence);
110  }
111  ao2_ref(loc, -1);
112  if (rc != 0) {
113  return -1;
114  }
115  } else {
116  format = eprofile->format;
117  method = eprofile->method;
118  location_source = eprofile->location_source;
119  rc = DUP_VARS(temp_locinfo, eprofile->location_info);
120  if (rc == 0) {
121  rc = DUP_VARS(temp_confidence, eprofile->confidence);
122  }
123  if (rc != 0) {
124  return -1;
125  }
126  }
127 
128  rc = DUP_VARS(temp_effloc, temp_locinfo);
129  if (rc != 0) {
130  return -1;
131  }
132 
133  if (eprofile->location_refinement) {
134  for (var = eprofile->location_refinement; var; var = var->next) {
135  struct ast_variable *newvar = ast_variable_new(var->name, var->value, "");
136  if (!newvar) {
137  return -1;
138  }
139  if (ast_variable_list_replace(&temp_effloc, newvar)) {
140  ast_variable_list_append(&temp_effloc, newvar);
141  }
142  }
143  }
144 
145  eprofile->format = format;
146  ast_string_field_set(eprofile, method, method);
147  ast_string_field_set(eprofile, location_source, location_source);
148 
149  ast_variables_destroy(eprofile->location_info);
150  eprofile->location_info = temp_locinfo;
151  temp_locinfo = NULL;
152  ast_variables_destroy(eprofile->effective_location);
153  eprofile->effective_location = temp_effloc;
154  temp_effloc = NULL;
155 
156  return 0;
157 }
158 
159 struct ast_geoloc_eprofile *ast_geoloc_eprofile_dup(struct ast_geoloc_eprofile *src)
160 {
161  struct ast_geoloc_eprofile *eprofile;
162  const char *profile_id;
163  int rc = 0;
164 
165  if (!src) {
166  return NULL;
167  }
168 
169  profile_id = ast_strdupa(src->id);
170 
171  eprofile = ast_geoloc_eprofile_alloc(profile_id);
172  if (!eprofile) {
173  return NULL;
174  }
175 
176  eprofile->allow_routing_use = src->allow_routing_use;
177  eprofile->pidf_element = src->pidf_element;
178  eprofile->suppress_empty_ca_elements = src->suppress_empty_ca_elements;
179  eprofile->format = src->format;
180  eprofile->precedence = src->precedence;
181 
182 
183  rc = ast_string_field_set(eprofile, location_reference, src->location_reference);
184  if (rc == 0) {
185  ast_string_field_set(eprofile, notes, src->notes);
186  }
187  if (rc == 0) {
188  ast_string_field_set(eprofile, method, src->method);
189  }
190  if (rc == 0) {
191  ast_string_field_set(eprofile, location_source, src->location_source);
192  }
193  if (rc == 0) {
194  rc = DUP_VARS(eprofile->location_info, src->location_info);
195  }
196  if (rc == 0) {
197  rc = DUP_VARS(eprofile->effective_location, src->effective_location);
198  }
199  if (rc == 0) {
200  rc = DUP_VARS(eprofile->location_refinement, src->location_refinement);
201  }
202  if (rc == 0) {
203  rc = DUP_VARS(eprofile->location_variables, src->location_variables);
204  }
205  if (rc == 0) {
206  rc = DUP_VARS(eprofile->usage_rules, src->usage_rules);
207  }
208  if (rc == 0) {
209  rc = DUP_VARS(eprofile->confidence, src->confidence);
210  }
211  if (rc != 0) {
212  ao2_ref(eprofile, -1);
213  return NULL;
214  }
215 
216 
217  return eprofile;
218 }
219 
220 struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_profile(struct ast_geoloc_profile *profile)
221 {
222  struct ast_geoloc_eprofile *eprofile;
223  const char *profile_id;
224  int rc = 0;
225 
226  if (!profile) {
227  return NULL;
228  }
229 
230  profile_id = ast_sorcery_object_get_id(profile);
231 
232  eprofile = ast_geoloc_eprofile_alloc(profile_id);
233  if (!eprofile) {
234  return NULL;
235  }
236 
237  ao2_lock(profile);
238  eprofile->allow_routing_use = profile->allow_routing_use;
239  eprofile->pidf_element = profile->pidf_element;
240  eprofile->suppress_empty_ca_elements = profile->suppress_empty_ca_elements;
241  eprofile->format = profile->format;
242 
243 
244  rc = ast_string_field_set(eprofile, location_reference, profile->location_reference);
245  if (rc == 0) {
246  ast_string_field_set(eprofile, notes, profile->notes);
247  }
248  if (rc == 0) {
249  ast_string_field_set(eprofile, method, profile->method);
250  }
251  if (rc == 0) {
252  ast_string_field_set(eprofile, location_source, profile->location_source);
253  }
254  if (rc == 0) {
255  rc = DUP_VARS(eprofile->location_info, profile->location_info);
256  }
257  if (rc == 0) {
258  rc = DUP_VARS(eprofile->location_refinement, profile->location_refinement);
259  }
260  if (rc == 0) {
261  rc = DUP_VARS(eprofile->location_variables, profile->location_variables);
262  }
263  if (rc == 0) {
264  rc = DUP_VARS(eprofile->usage_rules, profile->usage_rules);
265  }
266  if (rc == 0) {
267  rc = DUP_VARS(eprofile->confidence, profile->confidence);
268  }
269  if (rc != 0) {
270  ao2_unlock(profile);
271  ao2_ref(eprofile, -1);
272  return NULL;
273  }
274 
275  eprofile->precedence = profile->precedence;
276  ao2_unlock(profile);
277 
278  if (ast_geoloc_eprofile_refresh_location(eprofile) != 0) {
279  ao2_ref(eprofile, -1);
280  return NULL;
281  }
282 
283  return eprofile;
284 }
285 
286 static int set_loc_src(struct ast_geoloc_eprofile *eprofile, const char *uri, const char *ref_str)
287 {
288  char *local_uri = ast_strdupa(uri);
289  char *loc_src = NULL;
290 
291  loc_src = strchr(local_uri, ';');
292  if (loc_src) {
293  *loc_src = '\0';
294  loc_src++;
295  }
296 
297  if (!ast_strlen_zero(loc_src)) {
298  if (ast_begins_with(loc_src, "loc-src=")) {
299  struct ast_sockaddr loc_source_addr;
300  int rc = 0;
301  loc_src += 8;
302  rc = ast_sockaddr_parse(&loc_source_addr, loc_src, PARSE_PORT_FORBID);
303  if (rc == 1) {
304  ast_log(LOG_WARNING, "%s: URI '%s' has an invalid 'loc-src' parameter."
305  " RFC8787 states that IP addresses MUST be dropped.\n",
306  ref_str, uri);
307  return -1;
308  } else {
309  ast_string_field_set(eprofile, location_source, loc_src);
310  }
311  }
312  }
313  return 0;
314 }
315 
316 struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_uri(const char *uri,
317  const char *ref_str)
318 {
319  struct ast_geoloc_eprofile *eprofile = NULL;
320  char *ra = NULL;
321  char *local_uri;
322 
323  if (ast_strlen_zero(uri)) {
324  return NULL;
325  }
326  local_uri = ast_strdupa(uri);
327 
328  if (local_uri[0] == '<') {
329  local_uri++;
330  }
331  ra = strchr(local_uri, '>');
332  if (ra) {
333  *ra = '\0';
334  }
335 
336  ast_strip(local_uri);
337 
338  eprofile = ast_geoloc_eprofile_alloc(local_uri);
339  if (!eprofile) {
340  return NULL;
341  }
342 
343  set_loc_src(eprofile, uri, ref_str);
344 
345  eprofile->format = AST_GEOLOC_FORMAT_URI;
346  eprofile->location_info = ast_variable_new("URI", local_uri, "");
347 
348  return eprofile;
349 }
350 
351 struct ast_variable *geoloc_eprofile_resolve_varlist(struct ast_variable *source,
352  struct ast_variable *variables, struct ast_channel *chan)
353 {
354  struct ast_variable *dest = NULL;
355  struct ast_variable *var = NULL;
356  struct varshead *vh = NULL;
357  struct ast_str *buf = ast_str_alloca(256);
358 
359  if (!source || !chan) {
360  return NULL;
361  }
362 
363  /*
364  * ast_str_substitute_variables does only minimal recursive resolution so we need to
365  * pre-resolve each variable in the "variables" list, then use that result to
366  * do the final pass on the "source" variable list.
367  */
368  if (variables) {
369  var = variables;
370  vh = ast_var_list_create();
371  if (!vh) {
372  return NULL;
373  }
374  for ( ; var; var = var->next) {
375  ast_str_substitute_variables_full2(&buf, 0, chan, vh, var->value, NULL, 1);
376  AST_VAR_LIST_INSERT_TAIL(vh, ast_var_assign(var->name, ast_str_buffer(buf)));
377  ast_str_reset(buf);
378  }
379  }
380 
381  var = source;
382  for ( ; var; var = var->next) {
383  struct ast_variable *newvar = NULL;
384  ast_str_substitute_variables_full2(&buf, 0, chan, vh, var->value, NULL, 1);
385  newvar = ast_variable_new(var->name, ast_str_buffer(buf), "");
386  if (!newvar) {
387  ast_variables_destroy(dest);
388  ast_var_list_destroy(vh);
389  return NULL;
390  }
391  ast_variable_list_append(&dest, newvar);
392  ast_str_reset(buf);
393  }
394  ast_var_list_destroy(vh);
395 
396  return dest;
397 }
398 
399 
400 const char *ast_geoloc_eprofile_to_uri(struct ast_geoloc_eprofile *eprofile,
401  struct ast_channel *chan, struct ast_str **buf, const char *ref_str)
402 {
403  const char *uri = NULL;
404  struct ast_variable *resolved = NULL;
405  char *result;
406  int we_created_buf = 0;
407 
408  if (!eprofile || !buf || !chan) {
409  return NULL;
410  }
411 
412  if (eprofile->format != AST_GEOLOC_FORMAT_URI) {
413  ast_log(LOG_ERROR, "%s: '%s' is not a URI profile. It's '%s'\n",
414  ref_str, eprofile->id, ast_geoloc_format_to_name(eprofile->format));
415  return NULL;
416  }
417 
418  resolved = geoloc_eprofile_resolve_varlist(eprofile->effective_location,
419  eprofile->location_variables, chan);
420  if (!resolved) {
421  return NULL;
422  }
423 
424  uri = ast_variable_find_in_list(resolved, "URI");
425  result = uri ? ast_strdupa(uri) : NULL;
426  ast_variables_destroy(resolved);
427 
428  if (ast_strlen_zero(result)) {
429  ast_log(LOG_ERROR, "%s: '%s' is a URI profile but had no, or an empty, 'URI' entry in location_info\n",
430  ref_str, eprofile->id);
431  return NULL;
432  }
433 
434  if (!*buf) {
435  *buf = ast_str_create(256);
436  if (!*buf) {
437  return NULL;
438  }
439  we_created_buf = 1;
440  }
441 
442  if (ast_str_append(buf, 0, "%s", result) <= 0) {
443  if (we_created_buf) {
444  ast_free(*buf);
445  *buf = NULL;
446  return NULL;
447  }
448  }
449 
450  return ast_str_buffer(*buf);
451 }
452 
453 static struct ast_variable *var_list_from_node(struct ast_xml_node *node,
454  const char *ref_str)
455 {
456  struct ast_variable *list = NULL;
457  struct ast_xml_node *container;
458  struct ast_xml_node *child;
459  struct ast_variable *var;
460  SCOPE_ENTER(3, "%s\n", ref_str);
461 
462  container = ast_xml_node_get_children(node);
463  for (child = container; child; child = ast_xml_node_get_next(child)) {
464  const char *name = ast_xml_node_get_name(child);
465  const char *value = ast_xml_get_text(child);
466  const char *uom = ast_xml_get_attribute(child, "uom");
467 
468  if (uom) {
469  /* '20 radians\0' */
470  char newval[strlen(value) + 1 + strlen(uom) + 1];
471  sprintf(newval, "%s %s", value, uom);
472  var = ast_variable_new(name, newval, "");
473  } else {
474  var = ast_variable_new(name, value, "");
475  }
476 
477  ast_xml_free_text(value);
478  ast_xml_free_attr(uom);
479 
480  if (!var) {
481  ast_variables_destroy(list);
482  SCOPE_EXIT_RTN_VALUE(NULL, "%s: Allocation failure\n", ref_str);
483  }
484  ast_variable_list_append(&list, var);
485  }
486 
487  if (TRACE_ATLEAST(5)) {
488  struct ast_str *buf = NULL;
489  ast_variable_list_join(list, ", ", "=", "\"", &buf);
490  ast_trace(5, "%s: Result: %s\n", ref_str, ast_str_buffer(buf));
491  ast_free(buf);
492  }
493 
494  SCOPE_EXIT_RTN_VALUE(list, "%s: Done\n", ref_str);
495 }
496 
497 static struct ast_variable *var_list_from_loc_info(struct ast_xml_node *locinfo,
498  enum ast_geoloc_format format, const char *ref_str)
499 {
500  struct ast_variable *list = NULL;
501  struct ast_variable *locinfo_list = NULL;
502  struct ast_xml_node *container;
503  struct ast_variable *var = NULL;
504  const char *attr;
505  SCOPE_ENTER(3, "%s\n", ref_str);
506 
507  container = ast_xml_node_get_children(locinfo);
508  if (format == AST_GEOLOC_FORMAT_CIVIC_ADDRESS) {
509  attr = ast_xml_get_attribute(container, "lang");
510  if (attr) {
511  var = ast_variable_new("lang", attr, "");
512  ast_xml_free_attr(attr);
513  if (!var) {
514  SCOPE_EXIT_RTN_VALUE(NULL, "%s: Allocation failure\n", ref_str);
515  }
516  ast_variable_list_append(&list, var);
517  }
518  } else {
519  var = ast_variable_new("shape", ast_xml_node_get_name(container), "");
520  if (!var) {
521  SCOPE_EXIT_RTN_VALUE(NULL, "%s: Allocation failure\n", ref_str);
522  }
523  ast_variable_list_append(&list, var);
524 
525  attr = ast_xml_get_attribute(container, "srsName");
526  var = ast_variable_new("crs", attr, "");
527  ast_xml_free_attr(attr);
528  if (!var) {
529  ast_variables_destroy(list);
530  SCOPE_EXIT_RTN_VALUE(NULL, "%s: Allocation failure\n", ref_str);
531  }
532  ast_variable_list_append(&list, var);
533  }
534 
535  locinfo_list = var_list_from_node(container, ref_str);
536  if (locinfo_list == NULL) {
537  ast_log(LOG_WARNING, "%s: There were no elements in the location info\n", ref_str);
538  SCOPE_EXIT_RTN_VALUE(list, "%s: There were no elements in the location info\n", ref_str);
539  }
540  ast_variable_list_append(&list, locinfo_list);
541 
542  if (TRACE_ATLEAST(5)) {
543  struct ast_str *buf = NULL;
544  ast_variable_list_join(list, ", ", "=", "\"", &buf);
545  ast_trace(5, "%s: Result: %s\n", ref_str, ast_str_buffer(buf));
546  ast_free(buf);
547  }
548 
549  SCOPE_EXIT_RTN_VALUE(list, "%s: Done\n", ref_str);
550 }
551 
552 static struct ast_variable *var_list_from_confidence(struct ast_xml_node *confidence,
553  const char *ref_str)
554 {
555  struct ast_variable *list = NULL;
556  struct ast_variable *var;
557  const char *pdf;
558  const char *value;
559  SCOPE_ENTER(3, "%s\n", ref_str);
560 
561  if (!confidence) {
562  SCOPE_EXIT_RTN_VALUE(NULL, "%s: No confidence\n", ref_str);
563  }
564 
565  pdf = ast_xml_get_attribute(confidence, "pdf");
566  var = ast_variable_new("pdf", S_OR(pdf, "unknown"), "");
567  ast_xml_free_attr(pdf);
568  if (!var) {
569  SCOPE_EXIT_RTN_VALUE(NULL, "%s: Allocation failure\n", ref_str);
570  }
571  ast_variable_list_append(&list, var);
572 
573  value = ast_xml_get_text(confidence);
574  var = ast_variable_new("value", S_OR(value, "95"), "");
575  ast_xml_free_text(value);
576  if (!var) {
577  ast_variables_destroy(list);
578  SCOPE_EXIT_RTN_VALUE(NULL, "%s: Allocation failure\n", ref_str);
579  }
580  ast_variable_list_append(&list, var);
581 
582  if (TRACE_ATLEAST(5)) {
583  struct ast_str *buf = NULL;
584  ast_variable_list_join(list, ", ", "=", "\"", &buf);
585  ast_trace(5, "%s: Result: %s\n", ref_str, ast_str_buffer(buf));
586  ast_free(buf);
587  }
588 
589  SCOPE_EXIT_RTN_VALUE(list, "%s: Done\n", ref_str);
590 }
591 
592 static struct ast_geoloc_eprofile *geoloc_eprofile_create_from_xslt_result(
593  struct ast_xml_doc *result_doc, const char *ref_str)
594 {
595  struct ast_geoloc_eprofile *eprofile;
596  /*
597  * None of the ast_xml_nodes needs to be freed
598  * because they're just pointers into result_doc.
599  */
600  struct ast_xml_node *presence = NULL;
601  struct ast_xml_node *pidf_element = NULL;
602  struct ast_xml_node *location_info = NULL;
603  struct ast_xml_node *confidence = NULL;
604  struct ast_xml_node *usage_rules = NULL;
605  struct ast_xml_node *method = NULL;
606  struct ast_xml_node *note_well = NULL;
607  /*
608  * Like nodes, names of nodes are just
609  * pointers into result_doc and don't need to be freed.
610  */
611  const char *pidf_element_str;
612  /*
613  * Attributes and element text however are allocated on the fly
614  * so they DO need to be freed after use.
615  */
616  const char *id = NULL;
617  const char *format_str = NULL;
618  const char *method_str = NULL;
619  const char *note_well_str = NULL;
620 
621  SCOPE_ENTER(3, "%s\n", ref_str);
622 
623  if (!result_doc) {
624  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: result_doc was NULL", ref_str);
625  }
626 
627  if (TRACE_ATLEAST(5)) {
628  char *doc_str = NULL;
629  int doc_len = 0;
630 
631  ast_xml_doc_dump_memory(result_doc, &doc_str, &doc_len);
632  ast_trace(5, "xslt result doc len: %d\n%s\n", doc_len, doc_len ? doc_str : "<empty>");
633  ast_xml_free_text(doc_str);
634  }
635 
636  presence = ast_xml_get_root(result_doc);
637  if (!presence) {
638  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Can't find 'presence' root element\n",
639  ref_str);
640  }
641 
642  pidf_element = ast_xml_node_get_children(presence);
643  if (!pidf_element) {
644  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Can't find a device, tuple or person element\n",
645  ref_str);
646  }
647 
648  id = ast_xml_get_attribute(pidf_element, "id");
649  if (ast_strlen_zero(id)) {
650  ast_xml_free_attr(id);
651  id = ast_xml_get_attribute(presence, "entity");
652  }
653 
654  if (ast_strlen_zero(id)) {
655  SCOPE_EXIT_RTN_VALUE(NULL, "%s: Unable to find 'id' attribute\n", ref_str);
656  }
657 
658  eprofile = ast_geoloc_eprofile_alloc(id);
659  ast_xml_free_attr(id);
660  if (!eprofile) {
661  SCOPE_EXIT_RTN_VALUE(NULL, "%s: Allocation failure\n", ref_str);
662  }
663 
664  location_info = ast_xml_find_child_element(pidf_element, "location-info", NULL, NULL);
665  if (!location_info) {
666  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Can't find a location-info element\n",
667  ref_str);
668  }
669 
670  format_str = ast_xml_get_attribute(location_info, "format");
671  if (ast_strlen_zero(format_str)) {
672  SCOPE_EXIT_RTN_VALUE(NULL, "%s: Unable to find 'format' attribute\n", ref_str);
673  }
674 
675  eprofile->format = AST_GEOLOC_FORMAT_NONE;
676  if (strcasecmp(format_str, "gml") == 0) {
677  eprofile->format = AST_GEOLOC_FORMAT_GML;
678  } else if (strcasecmp(format_str, "civicAddress") == 0) {
679  eprofile->format = AST_GEOLOC_FORMAT_CIVIC_ADDRESS;
680  }
681 
682  if (eprofile->format == AST_GEOLOC_FORMAT_NONE) {
683  char *dup_format_str = ast_strdupa(format_str);
684  ast_xml_free_attr(format_str);
685  ao2_ref(eprofile, -1);
686  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unknown format '%s'\n", ref_str, dup_format_str);
687  }
688  ast_xml_free_attr(format_str);
689 
690  pidf_element_str = ast_xml_node_get_name(pidf_element);
691  eprofile->pidf_element = ast_geoloc_pidf_element_str_to_enum(pidf_element_str);
692 
693  eprofile->location_info = var_list_from_loc_info(location_info, eprofile->format, ref_str);
694  if (!eprofile->location_info) {
695  ao2_ref(eprofile, -1);
696  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR,
697  "%s: Unable to create location variables\n", ref_str);
698  }
699 
700  /*
701  * The function calls that follow are all NULL tolerant
702  * so no need for explicit error checking.
703  */
704  usage_rules = ast_xml_find_child_element(pidf_element, "usage-rules", NULL, NULL);
705  eprofile->usage_rules = var_list_from_node(usage_rules, ref_str);
706  confidence = ast_xml_find_child_element(location_info, "confidence", NULL, NULL);
707  eprofile->confidence = var_list_from_confidence(confidence, ref_str);
708 
709  method = ast_xml_find_child_element(pidf_element, "method", NULL, NULL);
710  method_str = ast_xml_get_text(method);
711  ast_string_field_set(eprofile, method, method_str);
712  ast_xml_free_text(method_str);
713 
714  note_well = ast_xml_find_child_element(pidf_element, "note-well", NULL, NULL);
715  note_well_str = ast_xml_get_text(note_well);
716  ast_string_field_set(eprofile, notes, note_well_str);
717  ast_xml_free_text(note_well_str);
718 
719  SCOPE_EXIT_RTN_VALUE(eprofile, "%s: Done.\n", ref_str);
720 }
721 
722 static int is_pidf_lo(struct ast_xml_doc *result_doc)
723 {
724  struct ast_xml_node *presence;
725  struct ast_xml_node *pidf_element;
726  struct ast_xml_node *location_info;
727  const char *pidf_element_name;
728 
729  if (!result_doc) {
730  return 0;
731  }
732  presence = ast_xml_get_root(result_doc);
733  if (!presence || !ast_strings_equal("presence", ast_xml_node_get_name(presence))) {
734  return 0;
735  }
736 
737  pidf_element = ast_xml_node_get_children(presence);
738  if (!pidf_element) {
739  return 0;
740  }
741  pidf_element_name = ast_xml_node_get_name(pidf_element);
742  if (!ast_strings_equal(pidf_element_name, "device") &&
743  !ast_strings_equal(pidf_element_name, "tuple") &&
744  !ast_strings_equal(pidf_element_name, "person")) {
745  return 0;
746  }
747 
748  location_info = ast_xml_find_child_element(pidf_element, "location-info", NULL, NULL);
749  if (!location_info) {
750  return 0;
751  }
752 
753  return 1;
754 }
755 
756 struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_pidf(
757  struct ast_xml_doc *pidf_xmldoc, const char *geoloc_uri, const char *ref_str)
758 {
759  struct ast_xml_doc *result_doc = NULL;
760  struct ast_geoloc_eprofile *eprofile = NULL;
761 
762  SCOPE_ENTER(3, "%s\n", ref_str);
763 
764  result_doc = ast_xslt_apply(pidf_to_eprofile_xslt, pidf_xmldoc, NULL);
765  if (!is_pidf_lo(result_doc)) {
766  SCOPE_EXIT_RTN_VALUE(NULL, "%s: Not a PIDF-LO. Skipping.\n", ref_str);
767  }
768 
769  /*
770  * The document returned from the stylesheet application looks like this...
771  * <presence id="presence-entity">
772  * <tuple id="element-id">
773  * <location-info format="gml">shape="Ellipsoid", crs="3d", ...</location-info>
774  * <usage-rules>retransmission-allowed="no", retention-expiry="2010-11-14T20:00:00Z"</usage-rules>
775  * <method>Hybrid_A-GPS</method>
776  * </tuple>
777  * </presence>
778  *
779  * Regardless of whether the pidf-element was tuple, device or person and whether
780  * the format is gml or civicAddress, the presence, pidf-element and location-info
781  * elements should be there.
782  *
783  * The confidence, usage-rules and note-well elements are optional.
784  */
785 
786  if (TRACE_ATLEAST(5)) {
787  char *doc_str = NULL;
788  int doc_len = 0;
789 
790  ast_xml_doc_dump_memory(result_doc, &doc_str, &doc_len);
791  ast_trace(5, "Intermediate doc len: %d\n%s\n", doc_len, doc_len ? doc_str : "<empty>");
792  ast_xml_free_text(doc_str);
793  doc_str = NULL;
794  doc_len = 0;
795  }
796 
797  eprofile = geoloc_eprofile_create_from_xslt_result(result_doc, ref_str);
798  ast_xml_close(result_doc);
799 
800  if (eprofile && geoloc_uri) {
801  set_loc_src(eprofile, geoloc_uri, ref_str);
802  }
803 
804  SCOPE_EXIT_RTN_VALUE(eprofile, "%s: Done.\n", ref_str);
805 }
806 
807 /*!
808  * \internal
809  * \brief Create an common, intermediate XML document to pass to the outgoing XSLT process
810  *
811  * \param element_name The name of the top-level XML element to create
812  * \param eprofile The eprofile
813  * \param chan The channel to resolve variables against
814  * \param ref_string A reference string for error messages
815  * \return An XML doc
816  *
817  * \note Given that the document is simple and static, it was easier to just
818  * create the elements in a string buffer and call ast_xml_read_memory()
819  * at the end instead of creating
820  */
821 static struct ast_xml_node *geoloc_eprofile_to_intermediate(const char *element_name, struct ast_geoloc_eprofile *eprofile,
822  struct ast_channel *chan, const char *ref_string)
823 {
824  struct ast_variable *resolved_location = NULL;
825  struct ast_variable *resolved_usage = NULL;
826  struct ast_variable *var = NULL;
827  RAII_VAR(struct ast_xml_node *, pidf_node, NULL, ast_xml_free_node);
828  struct ast_xml_node *rtn_pidf_node;
829  struct ast_xml_node *loc_node;
830  struct ast_xml_node *confidence_node;
831  struct ast_xml_node *info_node;
832  struct ast_xml_node *rules_node;
833  struct ast_xml_node *method_node;
834  struct ast_xml_node *notes_node;
835  struct ast_xml_node *timestamp_node;
836  struct timeval tv = ast_tvnow();
837  struct tm tm = { 0, };
838  char timestr[32] = { 0, };
839  int rc = 0;
840 
841  SCOPE_ENTER(3, "%s\n", ref_string);
842 
843  if (!eprofile || !chan) {
844  SCOPE_EXIT_RTN_VALUE(NULL, "%s: Either or both eprofile or chan were NULL\n", ref_string);
845  }
846 
847  pidf_node = ast_xml_new_node(element_name);
848  if (!pidf_node) {
849  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create '%s' XML node\n",
850  ref_string, element_name);
851  }
852 
853  loc_node = ast_xml_new_child(pidf_node, "location-info");
854  if (!loc_node) {
855  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create 'location-info' XML node\n",
856  ref_string);
857  }
858  rc = ast_xml_set_attribute(loc_node, "format", ast_geoloc_format_to_name(eprofile->format));
859  if (rc != 0) {
860  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to set 'format' XML attribute\n", ref_string);
861  }
862 
863  resolved_location = geoloc_eprofile_resolve_varlist(eprofile->effective_location,
864  eprofile->location_variables, chan);
865  if (eprofile->format == AST_GEOLOC_FORMAT_CIVIC_ADDRESS) {
866  info_node = geoloc_civicaddr_list_to_xml(resolved_location, ref_string);
867  } else {
868  info_node = geoloc_gml_list_to_xml(resolved_location, ref_string);
869  }
870  ast_variables_destroy(resolved_location);
871 
872  if (!info_node) {
873  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create XML from '%s' list\n",
874  ref_string, ast_geoloc_format_to_name(eprofile->format));
875  }
876  if (!ast_xml_add_child(loc_node, info_node)) {
877  ast_xml_free_node(info_node);
878  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable add '%s' node to XML document\n",
879  ref_string, ast_geoloc_format_to_name(eprofile->format));
880  }
881 
882  if (eprofile->confidence) {
883  const char *value = S_OR(ast_variable_find_in_list(eprofile->confidence, "value"), "95");
884  const char *pdf = S_OR(ast_variable_find_in_list(eprofile->confidence, "pdf"), "unknown");
885 
886  confidence_node = ast_xml_new_child(loc_node, "confidence");
887  if (!confidence_node) {
888  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create 'confidence' XML node\n",
889  ref_string);
890  }
891  rc = ast_xml_set_attribute(confidence_node, "pdf", pdf);
892  if (rc != 0) {
893  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to set 'pdf' attribute on 'confidence' element\n", ref_string);
894  }
895 
896  ast_xml_set_text(confidence_node, value);
897  }
898 
899  rules_node = ast_xml_new_child(pidf_node, "usage-rules");
900  if (!rules_node) {
901  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create 'usage-rules' XML node\n",
902  ref_string);
903  }
904  resolved_usage = geoloc_eprofile_resolve_varlist(eprofile->usage_rules,
905  eprofile->location_variables, chan);
906  for (var = resolved_usage; var; var = var->next) {
907  struct ast_xml_node *ur = ast_xml_new_child(rules_node, var->name);
908  ast_xml_set_text(ur, var->value);
909  }
910  ast_variables_destroy(resolved_usage);
911 
912  if (!ast_strlen_zero(eprofile->method)) {
913  method_node = ast_xml_new_child(pidf_node, "method");
914  if (!method_node) {
915  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create 'method' XML node\n",
916  ref_string);
917  }
918  ast_xml_set_text(method_node, eprofile->method);
919  };
920 
921  if (!ast_strlen_zero(eprofile->notes)) {
922  notes_node = ast_xml_new_child(pidf_node, "note-well");
923  if (!notes_node) {
924  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create 'note-well' XML node\n",
925  ref_string);
926  }
927  ast_xml_set_text(notes_node, eprofile->notes);
928  };
929 
930  gmtime_r(&tv.tv_sec, &tm);
931  strftime(timestr, sizeof(timestr), "%FT%TZ", &tm);
932  timestamp_node = ast_xml_new_child(pidf_node, "timestamp");
933  if (!timestamp_node) {
934  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create 'timestamp' XML node\n",
935  ref_string);
936  }
937  ast_xml_set_text(timestamp_node, timestr);
938 
939  rtn_pidf_node = pidf_node;
940  pidf_node = NULL;
941  SCOPE_EXIT_RTN_VALUE(rtn_pidf_node, "%s: Done\n", ref_string);
942 }
943 
944 #define CREATE_NODE_LIST(node) \
945  if (!node) { \
946  node = ast_xml_new_child(root_node, \
947  geoloc_pidf_element_to_name(eprofile->pidf_element)); \
948  if (!pidfs[eprofile->pidf_element]) { \
949  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create pidf '%s' XML node\n", \
950  ref_string, geoloc_pidf_element_to_name(eprofile->pidf_element)); \
951  } \
952  }
953 
954 const char *ast_geoloc_eprofiles_to_pidf(struct ast_datastore *ds,
955  struct ast_channel *chan, struct ast_str **buf, const char * ref_string)
956 {
957  RAII_VAR(struct ast_xml_doc *, intermediate, NULL, ast_xml_close);
958  RAII_VAR(struct ast_xml_doc *, pidf_doc, NULL, ast_xml_close);
959  struct ast_xml_node *root_node;
960  struct ast_xml_node *pidfs[AST_PIDF_ELEMENT_LAST] = {NULL, };
961  struct ast_geoloc_eprofile *eprofile;
962  int eprofile_count = 0;
963  int i;
964  char *doc_str = NULL;
965  int doc_len = 0;
966  int rc = 0;
967  SCOPE_ENTER(3, "%s\n", ref_string);
968 
969  if (!ds || !chan || !buf || !*buf || ast_strlen_zero(ref_string)) {
970  SCOPE_EXIT_RTN_VALUE(NULL, "%s: Either or both datastore or chan were NULL\n",
971  ref_string);
972  }
973 
974  intermediate = ast_xml_new();
975  if (!intermediate) {
976  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create XML document\n", ref_string);
977  }
978  root_node = ast_xml_new_node("presence");
979  if (!root_node) {
980  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create root XML node\n", ref_string);
981  }
982  ast_xml_set_root(intermediate, root_node);
983 
984  eprofile_count = ast_geoloc_datastore_size(ds);
985  for (i = 0; i < eprofile_count; i++) {
986  struct ast_xml_node *temp_node = NULL;
987  struct ast_xml_node *curr_loc = NULL;
988  struct ast_xml_node *new_loc = NULL;
989  struct ast_xml_node *new_loc_child = NULL;
990  struct ast_xml_node *new_loc_child_dup = NULL;
991  const char *entity = NULL;
992  int has_no_entity = 0;
993  eprofile = ast_geoloc_datastore_get_eprofile(ds, i);
994  if (eprofile->format == AST_GEOLOC_FORMAT_URI) {
995  continue;
996  }
997 
998  entity = ast_xml_get_attribute(root_node, "entity");
999  has_no_entity = ast_strlen_zero(entity);
1000  ast_xml_free_attr(entity);
1001  if (has_no_entity) {
1002  rc = ast_xml_set_attribute(root_node, "entity", eprofile->id);
1003  if (rc != 0) {
1004  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to set 'entity' XML attribute\n", ref_string);
1005  }
1006  }
1007 
1008  temp_node = geoloc_eprofile_to_intermediate(ast_geoloc_pidf_element_to_name(eprofile->pidf_element),
1009  eprofile, chan, ref_string);
1010  if (!temp_node) {
1011  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create temp_node\n", ref_string);
1012  }
1013 
1014  if (!pidfs[eprofile->pidf_element]) {
1015  pidfs[eprofile->pidf_element] = temp_node;
1016  ast_xml_add_child(root_node, temp_node);
1017  continue;
1018  }
1019 
1020  curr_loc = ast_xml_find_child_element(pidfs[eprofile->pidf_element], "location-info", NULL, NULL);
1021  new_loc = ast_xml_find_child_element(temp_node, "location-info", NULL, NULL);
1022  new_loc_child = ast_xml_node_get_children(new_loc);
1023  new_loc_child_dup = ast_xml_copy_node_list(new_loc_child);
1024  ast_xml_add_child_list(curr_loc, new_loc_child_dup);
1025 
1026  ast_xml_free_node(temp_node);
1027  }
1028 
1029  if (TRACE_ATLEAST(5)) {
1030  ast_xml_doc_dump_memory(intermediate, &doc_str, &doc_len);
1031  ast_trace(5, "Intermediate doc len: %d\n%s\n", doc_len, doc_len ? doc_str : "<empty>");
1032  ast_xml_free_text(doc_str);
1033  doc_str = NULL;
1034  doc_len = 0;
1035  }
1036 
1037  pidf_doc = ast_xslt_apply(eprofile_to_pidf_xslt, intermediate, NULL);
1038  if (!pidf_doc) {
1039  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create final PIDF-LO doc from intermediate docs\n",
1040  ref_string);
1041  }
1042 
1043  ast_xml_doc_dump_memory(pidf_doc, &doc_str, &doc_len);
1044  if (doc_len == 0 || !doc_str) {
1045  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to dump final PIDF-LO doc to string\n",
1046  ref_string);
1047  }
1048 
1049  rc = ast_str_set(buf, 0, "%s", doc_str);
1050  ast_xml_free_text(doc_str);
1051  if (rc <= 0) {
1052  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to extend buffer (%d)\n",
1053  ref_string, rc);
1054  }
1055 
1056  ast_trace(5, "Final doc:\n%s\n", ast_str_buffer(*buf));
1057 
1058  SCOPE_EXIT_RTN_VALUE(ast_str_buffer(*buf), "%s: Done\n", ref_string);
1059 }
1060 
1061 const char *ast_geoloc_eprofile_to_pidf(struct ast_geoloc_eprofile *eprofile,
1062  struct ast_channel *chan, struct ast_str **buf, const char * ref_string)
1063 {
1064  RAII_VAR(struct ast_xml_doc *, intermediate, NULL, ast_xml_close);
1065  RAII_VAR(struct ast_xml_doc *, pidf_doc, NULL, ast_xml_close);
1066  struct ast_xml_node *root_node;
1067  char *doc_str = NULL;
1068  int doc_len;
1069  int rc = 0;
1070  struct ast_xml_node *temp_node = NULL;
1071  const char *entity = NULL;
1072  int has_no_entity = 0;
1073  const char *params[] = { "suppress_empty_ca_elements", "false()", NULL };
1074 
1075  SCOPE_ENTER(3, "%s\n", ref_string);
1076 
1077  if (!eprofile || !chan || !buf || !*buf || ast_strlen_zero(ref_string)) {
1078  SCOPE_EXIT_RTN_VALUE(NULL, "%s: One of eprofile, chan or buf was NULL\n",
1079  ref_string);
1080  }
1081 
1082  if (eprofile->format == AST_GEOLOC_FORMAT_URI) {
1083  SCOPE_EXIT_RTN_VALUE(NULL, "%s: eprofile '%s' was a URI format\n",
1084  ref_string, eprofile->id);
1085  }
1086 
1087  intermediate = ast_xml_new();
1088  if (!intermediate) {
1089  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create XML document\n", ref_string);
1090  }
1091  root_node = ast_xml_new_node("presence");
1092  if (!root_node) {
1093  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create root XML node\n", ref_string);
1094  }
1095  ast_xml_set_root(intermediate, root_node);
1096 
1097  entity = ast_xml_get_attribute(root_node, "entity");
1098  has_no_entity = ast_strlen_zero(entity);
1099  ast_xml_free_attr(entity);
1100  if (has_no_entity) {
1101  rc = ast_xml_set_attribute(root_node, "entity", eprofile->id);
1102  if (rc != 0) {
1103  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to set 'entity' XML attribute\n", ref_string);
1104  }
1105  }
1106 
1107  temp_node = geoloc_eprofile_to_intermediate(
1108  ast_geoloc_pidf_element_to_name(eprofile->pidf_element), eprofile, chan, ref_string);
1109  if (!temp_node) {
1110  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create temp_node for eprofile '%s'\n",
1111  ref_string, eprofile->id);
1112  }
1113 
1114  ast_xml_add_child(root_node, temp_node);
1115 
1116  if (TRACE_ATLEAST(5)) {
1117  ast_xml_doc_dump_memory(intermediate, &doc_str, &doc_len);
1118  ast_trace(5, "Intermediate doc len: %d\n%s\n", doc_len, doc_len ? doc_str : "<empty>");
1119  ast_xml_free_text(doc_str);
1120  doc_str = NULL;
1121  doc_len = 0;
1122  }
1123 
1124  if (eprofile->suppress_empty_ca_elements) {
1125  params[1] = "true()";
1126  }
1127  pidf_doc = ast_xslt_apply(eprofile_to_pidf_xslt, intermediate, params);
1128  if (!pidf_doc) {
1129  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to create final PIDF-LO doc from intermediate doc\n",
1130  ref_string);
1131  }
1132 
1133  ast_xml_doc_dump_memory(pidf_doc, &doc_str, &doc_len);
1134  if (doc_len == 0 || !doc_str) {
1135  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to dump final PIDF-LO doc to string\n",
1136  ref_string);
1137  }
1138 
1139  rc = ast_str_set(buf, 0, "%s", doc_str);
1140  ast_xml_free_text(doc_str);
1141  if (rc <= 0) {
1142  SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Unable to extend buffer (%d)\n",
1143  ref_string, rc);
1144  }
1145 
1146  ast_trace(5, "Final doc:\n%s\n", ast_str_buffer(*buf));
1147 
1148  SCOPE_EXIT_RTN_VALUE(ast_str_buffer(*buf), "%s: Done\n", ref_string);
1149 }
1150 
1151 #ifdef TEST_FRAMEWORK
1152 static void load_tests(void);
1153 static void unload_tests(void);
1154 #else
1155 static void load_tests(void) {}
1156 static void unload_tests(void) {}
1157 #endif
1158 
1159 
1160 int geoloc_eprofile_unload(void)
1161 {
1162  unload_tests();
1163  if (pidf_to_eprofile_xslt) {
1164  ast_xslt_close(pidf_to_eprofile_xslt);
1165  }
1166 
1167  if (eprofile_to_pidf_xslt) {
1168  ast_xslt_close(eprofile_to_pidf_xslt);
1169  }
1170 
1171  if (geoloc_sorcery) {
1172  ast_sorcery_unref(geoloc_sorcery);
1173  }
1174 
1175  return AST_MODULE_LOAD_SUCCESS;
1176 }
1177 
1178 int geoloc_eprofile_load(void)
1179 {
1180  pidf_to_eprofile_xslt_size =
1181  (_binary_res_geolocation_pidf_to_eprofile_xslt_end - _binary_res_geolocation_pidf_to_eprofile_xslt_start);
1182 
1183  pidf_lo_test_xml_size =
1184  (_binary_res_geolocation_pidf_lo_test_xml_end - _binary_res_geolocation_pidf_lo_test_xml_start);
1185 
1186  pidf_to_eprofile_xslt = ast_xslt_read_memory(
1187  (char *)_binary_res_geolocation_pidf_to_eprofile_xslt_start, pidf_to_eprofile_xslt_size);
1188  if (!pidf_to_eprofile_xslt) {
1189  ast_log(LOG_ERROR, "Unable to read pidf_to_eprofile_xslt from memory\n");
1190  return AST_MODULE_LOAD_DECLINE;
1191  }
1192 
1193  eprofile_to_pidf_xslt_size =
1194  (_binary_res_geolocation_eprofile_to_pidf_xslt_end - _binary_res_geolocation_eprofile_to_pidf_xslt_start);
1195 
1196  eprofile_to_pidf_xslt = ast_xslt_read_memory(
1197  (char *)_binary_res_geolocation_eprofile_to_pidf_xslt_start, eprofile_to_pidf_xslt_size);
1198  if (!eprofile_to_pidf_xslt) {
1199  ast_log(LOG_ERROR, "Unable to read eprofile_to_pidf_xslt from memory\n");
1200 // geoloc_eprofile_unload();
1201  return AST_MODULE_LOAD_DECLINE;
1202  }
1203 
1204  geoloc_sorcery = geoloc_get_sorcery();
1205 
1206  load_tests();
1207 
1208  return AST_MODULE_LOAD_SUCCESS;
1209 }
1210 
1211 int geoloc_eprofile_reload(void)
1212 {
1213  return AST_MODULE_LOAD_SUCCESS;
1214 }
1215 
1216 
1217 #ifdef TEST_FRAMEWORK
1218 #include "asterisk/test.h"
1219 
1220 AST_TEST_DEFINE(test_create_from_uri)
1221 {
1222 
1223  RAII_VAR(struct ast_geoloc_eprofile *, eprofile, NULL, ao2_cleanup);
1224  const char *uri = NULL;
1225  int rc = AST_TEST_PASS;
1226 
1227  switch (cmd) {
1228  case TEST_INIT:
1229  info->name = "create_from_uri";
1230  info->category = "/geoloc/";
1231  info->summary = "Test create from uri";
1232  info->description = info->summary;
1233  return AST_TEST_NOT_RUN;
1234  case TEST_EXECUTE:
1235  break;
1236  }
1237 
1238  eprofile = ast_geoloc_eprofile_create_from_uri("http://some_uri&a=b", __func__);
1239  ast_test_validate(test, eprofile != NULL);
1240  ast_test_validate(test, eprofile->format == AST_GEOLOC_FORMAT_URI);
1241  ast_test_validate(test, eprofile->location_info != NULL);
1242  uri = ast_variable_find_in_list(eprofile->location_info, "URI");
1243  ast_test_validate(test, uri != NULL);
1244  ast_test_validate(test, strcmp(uri, "http://some_uri&a=b") == 0);
1245 
1246  return rc;
1247 }
1248 
1249 static enum ast_test_result_state validate_eprofile(struct ast_test *test,
1250  struct ast_xml_doc * pidf_xmldoc,
1251  const char *path,
1252  const char *id,
1253  enum ast_geoloc_pidf_element pidf_element,
1254  enum ast_geoloc_format format,
1255  const char *method,
1256  const char *location,
1257  const char *usage
1258  )
1259 {
1260  RAII_VAR(struct ast_str *, str, NULL, ast_free);
1261  RAII_VAR(struct ast_geoloc_eprofile *, eprofile, NULL, ao2_cleanup);
1262  RAII_VAR(struct ast_xml_doc *, result_doc, NULL, ast_xml_close);
1263 
1264  if (!ast_strlen_zero(path)) {
1265  result_doc = ast_xslt_apply(pidf_to_eprofile_xslt, pidf_xmldoc, NULL);
1266  ast_test_validate(test, (result_doc && ast_xml_node_get_children((struct ast_xml_node *)result_doc)));
1267 
1268  eprofile = geoloc_eprofile_create_from_xslt_result(result_doc, "test_create_from_xslt");
1269  } else {
1270  eprofile = ast_geoloc_eprofile_create_from_pidf(pidf_xmldoc, NULL, "test_create_from_pidf");
1271  }
1272 
1273  ast_test_validate(test, eprofile != NULL);
1274  ast_test_status_update(test, "ID: '%s' pidf_element: '%s' format: '%s' method: '%s'\n", eprofile->id,
1275  ast_geoloc_pidf_element_to_name(eprofile->pidf_element),
1276  ast_geoloc_format_to_name(eprofile->format),
1277  eprofile->method);
1278 
1279  ast_test_validate(test, ast_strings_equal(eprofile->id, id));
1280  ast_test_validate(test, eprofile->pidf_element == pidf_element);
1281  ast_test_validate(test, eprofile->format == format);
1282  ast_test_validate(test, ast_strings_equal(eprofile->method, method));
1283 
1284  str = ast_variable_list_join(eprofile->location_info, ",", "=", NULL, NULL);
1285  ast_test_validate(test, str != NULL);
1286  ast_test_status_update(test, "location_vars expected: %s\n", location);
1287  ast_test_status_update(test, "location_vars received: %s\n", ast_str_buffer(str));
1288  ast_test_validate(test, ast_strings_equal(ast_str_buffer(str), location));
1289  ast_free(str);
1290 
1291  str = ast_variable_list_join(eprofile->usage_rules, ",", "=", "'", NULL);
1292  ast_test_validate(test, str != NULL);
1293  ast_test_status_update(test, "usage_rules expected: %s\n", usage);
1294  ast_test_status_update(test, "usage_rules received: %s\n", ast_str_buffer(str));
1295  ast_test_validate(test, ast_strings_equal(ast_str_buffer(str), usage));
1296 
1297  return AST_TEST_PASS;
1298 }
1299 
1300 AST_TEST_DEFINE(test_create_from_pidf)
1301 {
1302 
1303  RAII_VAR(struct ast_xml_doc *, pidf_xmldoc, NULL, ast_xml_close);
1304  enum ast_test_result_state res = AST_TEST_PASS;
1305 
1306  switch (cmd) {
1307  case TEST_INIT:
1308  info->name = "create_from_pidf";
1309  info->category = "/geoloc/";
1310  info->summary = "Test create from pidf scenarios";
1311  info->description = info->summary;
1312  return AST_TEST_NOT_RUN;
1313  case TEST_EXECUTE:
1314  break;
1315  }
1316 
1317  pidf_xmldoc = ast_xml_read_memory((char *)_binary_res_geolocation_pidf_lo_test_xml_start, pidf_lo_test_xml_size);
1318  ast_test_validate(test, pidf_xmldoc != NULL);
1319 
1320  res = validate_eprofile(test, pidf_xmldoc,
1321  NULL,
1322  "point-2d",
1323  AST_PIDF_ELEMENT_TUPLE,
1324  AST_GEOLOC_FORMAT_GML,
1325  "Manual",
1326  "shape=Point,crs=2d,pos=-34.410649 150.87651",
1327  "retransmission-allowed='no',retention-expiry='2010-11-14T20:00:00Z'"
1328  );
1329  ast_test_validate(test, res == AST_TEST_PASS);
1330 
1331  return res;
1332 }
1333 
1334 static void load_tests(void) {
1335  AST_TEST_REGISTER(test_create_from_uri);
1336  AST_TEST_REGISTER(test_create_from_pidf);
1337 }
1338 static void unload_tests(void) {
1339  AST_TEST_UNREGISTER(test_create_from_uri);
1340  AST_TEST_UNREGISTER(test_create_from_pidf);
1341 }
1342 
1343 #endif
struct ast_variable * next
Main Channel structure associated with a channel.
Definition: test_heap.c:38
Asterisk main include file. File version handling, generic pbx functions.
String manipulation functions.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition: netsock2.c:230
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
struct ast_xml_doc * ast_xml_new(void)
Create a XML document.
Definition: xml.c:136
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.
Test Framework API.
Full structure for sorcery.
Definition: sorcery.c:230
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
struct ast_xml_node * ast_xml_add_child(struct ast_xml_node *parent, struct ast_xml_node *child)
Add a child node, to a specified parent node.
Definition: xml.c:168
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
Structure for a data store object.
Definition: datastore.h:64
struct ast_xml_node * ast_xml_get_root(struct ast_xml_doc *doc)
Get the document root node.
Definition: xml.c:230
Socket address structure.
Definition: netsock2.h:97
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
struct ast_xml_doc * ast_xslt_apply(struct ast_xslt_doc *xslt, struct ast_xml_doc *doc, const char **params)
Apply an XSLT stylesheet to an XML document.
Definition: xml.c:561
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition: sorcery.c:1853
#define ast_sorcery_unref(sorcery)
Decrease the reference count of a sorcery structure.
Definition: sorcery.h:1500
const char * ast_xml_get_attribute(struct ast_xml_node *node, const char *attrname)
Get a node attribute by name.
Definition: xml.c:267
void ast_xml_free_attr(const char *attribute)
Free an attribute returned by ast_xml_get_attribute()
Definition: xml.c:253
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
struct ast_xml_node * ast_xml_add_child_list(struct ast_xml_node *parent, struct ast_xml_node *child)
Add a list of child nodes, to a specified parent node.
Definition: xml.c:176
int ast_variable_list_replace(struct ast_variable **head, struct ast_variable *replacement)
Replace a variable in the given list with a new value.
Definition: main/config.c:667
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
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 ao2_container * container
Definition: res_fax.c:501
struct ast_xml_node * ast_xml_new_node(const char *name)
Create a XML node.
Definition: xml.c:144
Asterisk XML abstraction layer.
Core PBX routines and definitions.
#define ast_xml_find_child_element(_parent_node, _name, _attrname, _attrvalue)
Find a direct child element by name.
Definition: xml.h:201
void ast_xslt_close(struct ast_xslt_doc *xslt)
Close a stylesheet document and free its resources.
Definition: xml.c:552
Support for dynamic strings.
Definition: strings.h:623
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition: strings.c:238
void ast_xml_doc_dump_memory(struct ast_xml_doc *doc, char **buffer, int *length)
Dump the specified document to a buffer.
Definition: xml.c:385
void ast_xml_set_root(struct ast_xml_doc *doc, struct ast_xml_node *node)
Specify the root node of a XML document.
Definition: xml.c:221
void ast_xml_set_text(struct ast_xml_node *node, const char *content)
Set an element content string.
Definition: xml.c:362
struct ast_xml_node * ast_xml_copy_node_list(struct ast_xml_node *list)
Create a copy of a n ode list.
Definition: xml.c:184
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
void ast_xml_close(struct ast_xml_doc *doc)
Close an already open document and free the used structure.
Definition: xml.c:211
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:693
struct ast_xslt_doc * ast_xslt_read_memory(char *buffer, size_t size)
Open an XSLT document that resides in memory.
Definition: xml.c:525
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
struct ast_str * ast_variable_list_join(const struct ast_variable *head, const char *item_separator, const char *name_value_separator, const char *quote_char, struct ast_str **str)
Join an ast_variable list with specified separators and quoted values.
Definition: main/config.c:700
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
const char * ast_xml_get_text(struct ast_xml_node *node)
Get an element content string.
Definition: xml.c:353
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 AST_TEST_DEFINE(hdr)
Definition: test.h:126
struct ast_xml_doc * ast_xml_read_memory(char *buffer, size_t size)
Open an XML document that resides in memory.
Definition: xml.c:192
struct ast_xml_node * ast_xml_node_get_next(struct ast_xml_node *node)
Get the next node in the same level.
Definition: xml.c:400
#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
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
struct ast_xml_node * ast_xml_node_get_children(struct ast_xml_node *node)
Get the node's children.
Definition: xml.c:395
const char * ast_xml_node_get_name(struct ast_xml_node *node)
Get the name of a node.
Definition: xml.c:390
void ast_str_substitute_variables_full2(struct ast_str **buf, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used, int use_both)
Perform variable/function/expression substitution on an ast_str.
void ast_xml_free_text(const char *text)
Free a content element that was returned by ast_xml_get_text()
Definition: xml.c:260
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521