Asterisk - The Open Source Telephony Project GIT-master-3dae2cf
geoloc_dialplan.c
Go to the documentation of this file.
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/config.h"
21#include "asterisk/cli.h"
22#include "asterisk/module.h"
23#include "asterisk/channel.h"
24#include "asterisk/pbx.h"
25#include "asterisk/strings.h"
26#include "asterisk/utils.h"
27#include "asterisk/app.h"
28#include "geoloc_private.h"
29
30static void varlist_to_str(struct ast_variable *list, struct ast_str** buf, size_t len)
31{
32 struct ast_variable *var = list;
33
34 for (; var; var = var->next) {
35 ast_str_append(buf, len, "%s=\"%s\"%s", var->name, var->value, var->next ? "," : "");
36 }
37}
38
39#define RESOLVE_FOR_READ(_param) \
40({ \
41 if (ast_test_flag(&opts, OPT_GEOLOC_RESOLVE)) { \
42 struct ast_variable *resolved = geoloc_eprofile_resolve_varlist( \
43 eprofile->_param, eprofile->location_variables, chan); \
44 if (!resolved) { \
45 ast_log(LOG_ERROR, "%s: Unable to resolve " #_param "\n", chan_name); \
46 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-3"); \
47 return 0; \
48 } \
49 varlist_to_str(resolved, buf, len); \
50 ast_variables_destroy(resolved); \
51 } else { \
52 varlist_to_str(eprofile->_param, buf, len); \
53 } \
54})
55
59};
60
64});
65
66
67static int geoloc_profile_read(struct ast_channel *chan,
68 const char *cmd, char *data, struct ast_str **buf, ssize_t len)
69{
70 char *parsed_data = ast_strdupa(data);
71 const char *chan_name = ast_channel_name(chan);
72 struct ast_datastore *ds;
73 struct ast_geoloc_eprofile *orig_eprofile = NULL;
74 struct ast_geoloc_eprofile *eprofile = NULL;
75 struct ast_flags opts = { 0, };
76
78 AST_APP_ARG(field);
80 );
81
82 /* Check for zero arguments */
83 if (ast_strlen_zero(parsed_data)) {
84 ast_log(LOG_ERROR, "%s: Cannot call without arguments\n", chan_name);
85 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-1");
86 return 0;
87 }
88
89 AST_STANDARD_APP_ARGS(args, parsed_data);
90
91 if (ast_strlen_zero(args.field)) {
92 ast_log(LOG_ERROR, "%s: Cannot call without a field to query\n", chan_name);
93 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-1");
94 return 0;
95 }
96
97 if (!ast_strlen_zero(args.options)) {
98 if (ast_app_parse_options(action_options, &opts, NULL, args.options)) {
99 ast_log(LOG_ERROR, "%s: Invalid options: %s\n", chan_name, args.options);
100 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-1");
101 return 0;
102 }
103 }
104
105 ds = ast_geoloc_datastore_find(chan);
106 if (!ds) {
107 ast_log(LOG_NOTICE, "%s: There is no geoloc profile on this channel\n", chan_name);
108 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-2");
109 return 0;
110 }
111
112 orig_eprofile = ast_geoloc_datastore_get_eprofile(ds, 0);
113 if (!orig_eprofile) {
114 ast_log(LOG_NOTICE, "%s: There is no geoloc profile on this channel\n", chan_name);
115 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-2");
116 return 0;
117 }
118
119 eprofile = ast_geoloc_eprofile_dup(orig_eprofile);
120 ao2_ref(orig_eprofile, -1);
121 if (!eprofile) {
122 ast_log(LOG_ERROR, "%s: Unable to duplicate eprofile\n", chan_name);
123 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-2");
124 return 0;
125 }
126
127 if (!eprofile->effective_location) {
129 }
130
131 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "0");
132 if (ast_strings_equal(args.field, "inheritable")) {
133 ast_str_append(buf, len, "%s", ds->inheritance ? "true" : "false");
134 } else if (ast_strings_equal(args.field, "id")) {
135 ast_str_append(buf, len, "%s", eprofile->id);
136 } else if (ast_strings_equal(args.field, "location_reference")) {
137 ast_str_append(buf, len, "%s", eprofile->location_reference);
138 } else if (ast_strings_equal(args.field, "method")) {
139 ast_str_append(buf, len, "%s", eprofile->method);
140 } else if (ast_strings_equal(args.field, "allow_routing_use")) {
141 ast_str_append(buf, len, "%s", eprofile->allow_routing_use ? "yes" : "no");
142 } else if (ast_strings_equal(args.field, "suppress_empty_ca_elements")) {
143 ast_str_append(buf, len, "%s", eprofile->suppress_empty_ca_elements ? "yes" : "no");
144 } else if (ast_strings_equal(args.field, "profile_precedence")) {
145 ast_str_append(buf, len, "%s", ast_geoloc_precedence_to_name(eprofile->precedence));
146 } else if (ast_strings_equal(args.field, "format")) {
147 ast_str_append(buf, len, "%s", ast_geoloc_format_to_name(eprofile->format));
148 } else if (ast_strings_equal(args.field, "pidf_element")) {
149 ast_str_append(buf, len, "%s", ast_geoloc_pidf_element_to_name(eprofile->pidf_element));
150 } else if (ast_strings_equal(args.field, "location_source")) {
151 ast_str_append(buf, len, "%s", eprofile->location_source);
152 } else if (ast_strings_equal(args.field, "notes")) {
153 ast_str_append(buf, len, "%s", eprofile->notes);
154 } else if (ast_strings_equal(args.field, "location_info")) {
155 RESOLVE_FOR_READ(location_info);
156 } else if (ast_strings_equal(args.field, "location_info_refinement")) {
157 RESOLVE_FOR_READ(location_refinement);
158 } else if (ast_strings_equal(args.field, "location_variables")) {
159 RESOLVE_FOR_READ(location_variables);
160 } else if (ast_strings_equal(args.field, "effective_location")) {
161 RESOLVE_FOR_READ(effective_location);
162 } else if (ast_strings_equal(args.field, "usage_rules")) {
163 RESOLVE_FOR_READ(usage_rules);
164 } else if (ast_strings_equal(args.field, "confidence")) {
165 varlist_to_str(eprofile->confidence, buf, len);
166 } else {
167 ast_log(LOG_ERROR, "%s: Field '%s' is not valid\n", chan_name, args.field);
168 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-3");
169 }
170
171 ao2_ref(eprofile, -1);
172 return 0;
173}
174
175#define VAR_LIST_REPLACE(_old, _new) \
176 ast_variables_destroy(_old); \
177 _old = _new;
178
179#define TEST_ENUM_VALUE(_chan_name, _ep, _field, _value) \
180({ \
181 enum ast_geoloc_ ## _field v; \
182 v = ast_geoloc_ ## _field ## _str_to_enum(_value); \
183 if (v == AST_GEOLOC_INVALID_VALUE) { \
184 ast_log(LOG_ERROR, "%s: %s '%s' is invalid\n", _chan_name, #_field, value); \
185 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-3"); \
186 return 0; \
187 } \
188 _ep->_field = v; \
189})
190
191#define TEST_VARLIST(_chan_name, _ep, _field, _value) \
192({ \
193 struct ast_variable *_list; \
194 _list = ast_variable_list_from_quoted_string(_value, ",", "=", "\"" ); \
195 if (!_list) { \
196 ast_log(LOG_ERROR, "%s: %s '%s' is malformed or contains invalid values", _chan_name, #_field, _value); \
197 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-3"); \
198 return 0; \
199 } \
200 if (ast_test_flag(&opts, OPT_GEOLOC_APPEND)) { \
201 ast_variable_list_append(&_ep->_field, _list); \
202 } else {\
203 VAR_LIST_REPLACE(_ep->_field, _list); \
204 } \
205})
206
207
208#define RESOLVE_FOR_WRITE(_param) \
209({ \
210if (ast_test_flag(&opts, OPT_GEOLOC_RESOLVE)) { \
211 struct ast_variable *resolved = geoloc_eprofile_resolve_varlist( \
212 eprofile->_param, eprofile->location_variables, chan); \
213 if (!resolved) { \
214 ast_log(LOG_ERROR, "%s: Unable to resolve " #_param " %p %p\n", chan_name, eprofile->_param, eprofile->location_variables); \
215 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-3"); \
216 return 0; \
217 } \
218 VAR_LIST_REPLACE(eprofile->_param, resolved); \
219} \
220})
221
222static int geoloc_profile_write(struct ast_channel *chan, const char *cmd, char *data,
223 const char *value)
224{
225 char *parsed_data = ast_strdupa(data);
226 const char *chan_name = ast_channel_name(chan);
227 struct ast_datastore *ds; /* Reminder: datastores aren't ao2 objects */
228 RAII_VAR(struct ast_geoloc_eprofile *, eprofile, NULL, ao2_cleanup);
229 struct ast_flags opts = { 0, };
230
232 AST_APP_ARG(field);
234 );
235
236 /* Check for zero arguments */
237 if (ast_strlen_zero(parsed_data)) {
238 ast_log(LOG_ERROR, "%s: Cannot call without arguments\n", chan_name);
239 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-1");
240 return 0;
241 }
242
243 AST_STANDARD_APP_ARGS(args, parsed_data);
244
245 if (ast_strlen_zero(args.field)) {
246 ast_log(LOG_ERROR, "%s: Cannot call without a field to set\n", chan_name);
247 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-1");
248 return 0;
249 }
250
251 if (!ast_strlen_zero(args.options)) {
252 if (ast_app_parse_options(action_options, &opts, NULL, args.options)) {
253 ast_log(LOG_ERROR, "%s: Invalid options: %s\n", chan_name, args.options);
254 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-1");
255 return 0;
256 }
257 }
258
259 ast_debug(1, "%s: name: %s value: %s options: %s append: %s resolve: %s\n", chan_name,
260 args.field, value, args.options, ast_test_flag(&opts, OPT_GEOLOC_APPEND) ? "yes" : "no",
261 ast_test_flag(&opts, OPT_GEOLOC_RESOLVE) ? "yes" : "no");
262
263 ds = ast_geoloc_datastore_find(chan);
264 if (!ds) {
266 if (!ds) {
267 ast_log(LOG_WARNING, "%s: Unable to create geolocation datastore\n", chan_name);
268 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-2");
269 return 0;
270 }
272 }
273
274 eprofile = ast_geoloc_datastore_get_eprofile(ds, 0);
275 if (!eprofile) {
276 int rc;
277 eprofile = ast_geoloc_eprofile_alloc(chan_name);
278 if (!eprofile) {
279 ast_log(LOG_ERROR, "%s: Could not allocate eprofile\n", chan_name);
280 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-2");
281 return 0;
282 }
283 rc = ast_geoloc_datastore_add_eprofile(ds, eprofile);
284 if (rc <= 0) {
285 ast_log(LOG_ERROR, "%s: Could not add eprofile to datastore\n", chan_name);
286 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-2");
287 return 0;
288 }
289 }
290
291 if (ast_strings_equal(args.field, "inheritable")) {
293 } else if (ast_strings_equal(args.field, "id")) {
294 ast_string_field_set(eprofile, id, value);
295 } else if (ast_strings_equal(args.field, "location_reference")) {
297 ao2_cleanup(loc);
298 if (!loc) {
299 ast_log(LOG_ERROR, "%s: Location reference '%s' doesn't exist\n", chan_name, value);
300 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-3");
301 return 0;
302 }
303 ast_string_field_set(eprofile, location_reference, value);
304 } else if (ast_strings_equal(args.field, "method")) {
306 } else if (ast_strings_equal(args.field, "allow_routing_use")) {
307 eprofile->allow_routing_use = ast_true(value);
308 } else if (ast_strings_equal(args.field, "suppress_empty_ca_elements")) {
309 eprofile->suppress_empty_ca_elements = ast_true(value);
310 } else if (ast_strings_equal(args.field, "profile_precedence")) {
311 TEST_ENUM_VALUE(chan_name, eprofile, precedence, value);
312 } else if (ast_strings_equal(args.field, "format")) {
313 TEST_ENUM_VALUE(chan_name, eprofile, format, value);
314 } else if (ast_strings_equal(args.field, "pidf_element")) {
315 TEST_ENUM_VALUE(chan_name, eprofile, pidf_element, value);
316 } else if (ast_strings_equal(args.field, "location_source")) {
318 } else if (ast_strings_equal(args.field, "notes")) {
319 ast_string_field_set(eprofile, notes, value);
320 } else if (ast_strings_equal(args.field, "location_info")) {
321 TEST_VARLIST(chan_name, eprofile, location_info, value);
323 } else if (ast_strings_equal(args.field, "location_info_refinement")) {
324 TEST_VARLIST(chan_name, eprofile, location_refinement, value);
325 RESOLVE_FOR_WRITE(location_refinement);
326 } else if (ast_strings_equal(args.field, "location_variables")) {
327 TEST_VARLIST(chan_name, eprofile, location_variables, value);
328 RESOLVE_FOR_WRITE(location_variables);
329 } else if (ast_strings_equal(args.field, "effective_location")) {
330 TEST_VARLIST(chan_name, eprofile, effective_location, value);
331 RESOLVE_FOR_WRITE(effective_location);
332 } else if (ast_strings_equal(args.field, "usage_rules")) {
333 TEST_VARLIST(chan_name, eprofile, usage_rules, value);
334 RESOLVE_FOR_WRITE(usage_rules);
335 } else if (ast_strings_equal(args.field, "confidence")) {
336 TEST_VARLIST(chan_name, eprofile, confidence, value);
337 } else {
338 ast_log(LOG_ERROR, "%s: Field '%s' is not valid\n", chan_name, args.field);
339 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "-3");
340 return 0;
341 }
342
344
345 pbx_builtin_setvar_helper(chan, "GEOLOCPROFILESTATUS", "0");
346
347 return 0;
348}
349
351 .name = "GEOLOC_PROFILE",
352 .read2 = geoloc_profile_read,
353 .write = geoloc_profile_write,
354};
355
357{
359
361}
362
364{
365 int res = 0;
366
368
370}
371
373{
375}
376
#define var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2404
Standard Command Line Interface.
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static int geoloc_profile_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
static const struct ast_app_option action_options[128]
#define RESOLVE_FOR_WRITE(_param)
#define TEST_ENUM_VALUE(_chan_name, _ep, _field, _value)
int geoloc_dialplan_reload(void)
int geoloc_dialplan_unload(void)
#define RESOLVE_FOR_READ(_param)
int geoloc_dialplan_load(void)
static struct ast_custom_function geoloc_function
static void varlist_to_str(struct ast_variable *list, struct ast_str **buf, size_t len)
my_app_option_flags
@ OPT_GEOLOC_APPEND
@ OPT_GEOLOC_RESOLVE
#define TEST_VARLIST(_chan_name, _ep, _field, _value)
static int geoloc_profile_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
Configuration File Parser.
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_NOTICE
#define LOG_WARNING
Asterisk module definitions.
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Core PBX routines and definitions.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
static int notes
Definition: pval.c:66
int ast_geoloc_eprofile_refresh_location(struct ast_geoloc_eprofile *eprofile)
Refresh the effective profile with any changed info.
struct ast_geoloc_eprofile * ast_geoloc_eprofile_dup(struct ast_geoloc_eprofile *src)
Duplicate an effective profile.
struct ast_geoloc_eprofile * ast_geoloc_datastore_get_eprofile(struct ast_datastore *ds, int ix)
Retrieve a specific eprofile from a datastore by index.
struct ast_datastore * ast_geoloc_datastore_create(const char *id)
Create an empty geoloc datastore.
struct ast_geoloc_eprofile * ast_geoloc_eprofile_alloc(const char *name)
Geolocation Effective Profile Functions.
struct ast_geoloc_location * ast_geoloc_get_location(const char *id)
Retrieve a geolocation location object by id.
int ast_geoloc_datastore_set_inheritance(struct ast_datastore *ds, int inherit)
Sets the inheritance flag on the datastore.
int ast_geoloc_datastore_add_eprofile(struct ast_datastore *ds, struct ast_geoloc_eprofile *eprofile)
Add an eprofile to a datastore.
struct ast_datastore * ast_geoloc_datastore_find(struct ast_channel *chan)
Retrieves the geoloc datastore from a channel, if any.
const char * method
Definition: res_pjsip.c:1279
#define NULL
Definition: resample.c:96
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
String manipulation functions.
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
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition: strings.c:238
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store object.
Definition: datastore.h:64
unsigned int inheritance
Definition: datastore.h:69
Structure used to handle boolean flags.
Definition: utils.h:199
struct ast_variable * effective_location
const ast_string_field location_reference
enum ast_geoloc_format format
enum ast_geoloc_pidf_element pidf_element
const ast_string_field method
enum ast_geoloc_precedence precedence
const ast_string_field notes
const ast_string_field id
struct ast_variable * confidence
const ast_string_field location_source
enum ast_geoloc_format format
struct ast_variable * confidence
const ast_string_field location_source
struct ast_variable * location_info
Support for dynamic strings.
Definition: strings.h:623
Structure for variables, used for configurations and for channel variables.
int value
Definition: syslog.c:37
const char * args
static struct test_options options
Utility functions.
#define ast_test_flag(p, flag)
Definition: utils.h:63
#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