Merge "contrib/sip_to_pjsip: handle setvar in conversion"
[asterisk/asterisk.git] / funcs / func_enum.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006
5  *
6  * Mark Spencer <markster@digium.com>
7  * Oleksiy Krivoshey <oleksiyk@gmail.com>
8  * Russell Bryant <russelb@clemson.edu>
9  * Brett Bryant <bbryant@digium.com>
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  * This program is free software, distributed under the terms of
18  * the GNU General Public License Version 2. See the LICENSE file
19  * at the top of the source tree.
20  */
21
22 /*! \file
23  *
24  * \brief ENUM Functions
25  *
26  * \author Mark Spencer <markster@digium.com>
27  * \author Oleksiy Krivoshey <oleksiyk@gmail.com>
28  * \author Russell Bryant <russelb@clemson.edu>
29  * \author Brett Bryant <bbryant@digium.com>
30  *
31  * \arg See also AstENUM
32  *
33  * \ingroup functions
34  */
35
36 /*** MODULEINFO
37         <support_level>core</support_level>
38  ***/
39
40 #include "asterisk.h"
41
42 #include "asterisk/module.h"
43 #include "asterisk/channel.h"
44 #include "asterisk/pbx.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/lock.h"
47 #include "asterisk/file.h"
48 #include "asterisk/enum.h"
49 #include "asterisk/app.h"
50
51 /*** DOCUMENTATION
52         <function name="ENUMQUERY" language="en_US">
53                 <synopsis>
54                         Initiate an ENUM query.
55                 </synopsis>
56                 <syntax>
57                         <parameter name="number" required="true" />
58                         <parameter name="method-type">
59                                 <para>If no <replaceable>method-type</replaceable> is given, the default will be
60                                 <literal>sip</literal>.</para>
61                         </parameter>
62                         <parameter name="zone-suffix">
63                                 <para>If no <replaceable>zone-suffix</replaceable> is given, the default will be
64                                 <literal>e164.arpa</literal></para>
65                         </parameter>
66                 </syntax>
67                 <description>
68                         <para>This will do a ENUM lookup of the given phone number.</para>
69                 </description>
70         </function>
71         <function name="ENUMRESULT" language="en_US">
72                 <synopsis>
73                         Retrieve results from a ENUMQUERY.
74                 </synopsis>
75                 <syntax>
76                         <parameter name="id" required="true">
77                                 <para>The identifier returned by the ENUMQUERY function.</para>
78                         </parameter>
79                         <parameter name="resultnum" required="true">
80                                 <para>The number of the result that you want to retrieve.</para>
81                                 <para>Results start at <literal>1</literal>. If this argument is specified
82                                 as <literal>getnum</literal>, then it will return the total number of results
83                                 that are available or -1 on error.</para>
84                         </parameter>
85                 </syntax>
86                 <description>
87                         <para>This function will retrieve results from a previous use
88                         of the ENUMQUERY function.</para>
89                 </description>
90         </function>
91         <function name="ENUMLOOKUP" language="en_US">
92                 <synopsis>
93                         General or specific querying of NAPTR records for ENUM or ENUM-like DNS pointers.
94                 </synopsis>
95                 <syntax>
96                         <parameter name="number" required="true" />
97                         <parameter name="method-type">
98                                 <para>If no <replaceable>method-type</replaceable> is given, the default will be
99                                 <literal>sip</literal>.</para>
100                         </parameter>
101                         <parameter name="options">
102                                 <optionlist>
103                                         <option name="c">
104                                                 <para>Returns an integer count of the number of NAPTRs of a certain RR type.</para>
105                                                 <para>Combination of <literal>c</literal> and Method-type of <literal>ALL</literal> will
106                                                 return a count of all NAPTRs for the record or -1 on error.</para>
107                                         </option>
108                                         <option name="u">
109                                                 <para>Returns the full URI and does not strip off the URI-scheme.</para>
110                                         </option>
111                                         <option name="s">
112                                                 <para>Triggers ISN specific rewriting.</para>
113                                         </option>
114                                         <option name="i">
115                                                 <para>Looks for branches into an Infrastructure ENUM tree.</para>
116                                         </option>
117                                         <option name="d">
118                                                 <para>for a direct DNS lookup without any flipping of digits.</para>
119                                         </option>
120                                 </optionlist>
121                         </parameter>
122                         <parameter name="record#">
123                                 <para>If no <replaceable>record#</replaceable> is given,
124                                 defaults to <literal>1</literal>.</para>
125                         </parameter>
126                         <parameter name="zone-suffix">
127                                 <para>If no <replaceable>zone-suffix</replaceable> is given, the default will be
128                                 <literal>e164.arpa</literal></para>
129                         </parameter>
130                 </syntax>
131                 <description>
132                         <para>For more information see <filename>doc/AST.pdf</filename>.</para>
133                 </description>
134         </function>
135         <function name="TXTCIDNAME" language="en_US">
136                 <synopsis>
137                         TXTCIDNAME looks up a caller name via DNS.
138                 </synopsis>
139                 <syntax>
140                         <parameter name="number" required="true" />
141                         <parameter name="zone-suffix">
142                                 <para>If no <replaceable>zone-suffix</replaceable> is given, the default will be
143                                 <literal>e164.arpa</literal></para>
144                         </parameter>
145                 </syntax>
146                 <description>
147                         <para>This function looks up the given phone number in DNS to retrieve
148                         the caller id name.  The result will either be blank or be the value
149                         found in the TXT record in DNS.</para>
150                 </description>
151         </function>
152  ***/
153
154 static char *synopsis = "Syntax: ENUMLOOKUP(number[,Method-type[,options[,record#[,zone-suffix]]]])\n";
155
156 static int function_enum(struct ast_channel *chan, const char *cmd, char *data,
157                          char *buf, size_t len)
158 {
159         AST_DECLARE_APP_ARGS(args,
160                 AST_APP_ARG(number);
161                 AST_APP_ARG(tech);
162                 AST_APP_ARG(options);
163                 AST_APP_ARG(record);
164                 AST_APP_ARG(zone);
165         );
166         char tech[80];
167         char dest[256] = "", tmp[2] = "", num[AST_MAX_EXTENSION] = "";
168         char *s, *p;
169         unsigned int record = 1;
170
171         buf[0] = '\0';
172
173         if (ast_strlen_zero(data)) {
174                 ast_log(LOG_WARNING, "%s", synopsis);
175                 return -1;
176         }
177
178         AST_STANDARD_APP_ARGS(args, data);
179
180         if (args.argc < 1) {
181                 ast_log(LOG_WARNING, "%s", synopsis);
182                 return -1;
183         }
184
185         if (args.tech && !ast_strlen_zero(args.tech)) {
186                 ast_copy_string(tech,args.tech, sizeof(tech));
187         } else {
188                 ast_copy_string(tech,"sip",sizeof(tech));
189         }
190
191         if (!args.zone) {
192                 args.zone = "e164.arpa";
193         }
194         if (!args.options) {
195                 args.options = "";
196         }
197         if (args.record) {
198                 record = atoi(args.record) ? atoi(args.record) : record;
199         }
200
201         /* strip any '-' signs from number */
202         for (s = p = args.number; *s; s++) {
203                 if (*s != '-') {
204                         snprintf(tmp, sizeof(tmp), "%c", *s);
205                         strncat(num, tmp, sizeof(num) - strlen(num) - 1);
206                 }
207
208         }
209         ast_get_enum(chan, num, dest, sizeof(dest), tech, sizeof(tech), args.zone, args.options, record, NULL);
210
211         p = strchr(dest, ':');
212         if (p && strcasecmp(tech, "ALL") && !strchr(args.options, 'u')) {
213                 ast_copy_string(buf, p + 1, len);
214         } else {
215                 ast_copy_string(buf, dest, len);
216         }
217         return 0;
218 }
219
220 static unsigned int enum_datastore_id;
221
222 struct enum_result_datastore {
223         struct enum_context *context;
224         unsigned int id;
225 };
226
227 static void erds_destroy(struct enum_result_datastore *data)
228 {
229         int k;
230
231         for (k = 0; k < data->context->naptr_rrs_count; k++) {
232                 ast_free(data->context->naptr_rrs[k].result);
233                 ast_free(data->context->naptr_rrs[k].tech);
234         }
235
236         ast_free(data->context->naptr_rrs);
237         ast_free(data->context);
238         ast_free(data);
239 }
240
241 static void erds_destroy_cb(void *data)
242 {
243         struct enum_result_datastore *erds = data;
244         erds_destroy(erds);
245 }
246
247 static const struct ast_datastore_info enum_result_datastore_info = {
248         .type = "ENUMQUERY",
249         .destroy = erds_destroy_cb,
250 };
251
252 static int enum_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
253 {
254         struct enum_result_datastore *erds;
255         struct ast_datastore *datastore;
256         char *parse, tech[128], dest[128];
257         int res = -1;
258
259         AST_DECLARE_APP_ARGS(args,
260                 AST_APP_ARG(number);
261                 AST_APP_ARG(tech);
262                 AST_APP_ARG(zone);
263         );
264
265         if (ast_strlen_zero(data)) {
266                 ast_log(LOG_WARNING, "ENUMQUERY requires at least a number as an argument...\n");
267                 goto finish;
268         }
269
270         parse = ast_strdupa(data);
271
272         AST_STANDARD_APP_ARGS(args, parse);
273
274         if (!chan) {
275                 ast_log(LOG_ERROR, "ENUMQUERY cannot be used without a channel!\n");
276                 goto finish;
277         }
278
279         if (!args.zone)
280                 args.zone = "e164.zone";
281
282         ast_copy_string(tech, args.tech ? args.tech : "sip", sizeof(tech));
283
284         if (!(erds = ast_calloc(1, sizeof(*erds))))
285                 goto finish;
286
287         if (!(erds->context = ast_calloc(1, sizeof(*erds->context)))) {
288                 ast_free(erds);
289                 goto finish;
290         }
291
292         erds->id = ast_atomic_fetchadd_int((int *) &enum_datastore_id, 1);
293
294         snprintf(buf, len, "%u", erds->id);
295
296         if (!(datastore = ast_datastore_alloc(&enum_result_datastore_info, buf))) {
297                 ast_free(erds->context);
298                 ast_free(erds);
299                 goto finish;
300         }
301
302         ast_get_enum(chan, args.number, dest, sizeof(dest), tech, sizeof(tech), args.zone, "", 1, &erds->context);
303
304         datastore->data = erds;
305
306         ast_channel_lock(chan);
307         ast_channel_datastore_add(chan, datastore);
308         ast_channel_unlock(chan);
309
310         res = 0;
311
312 finish:
313
314         return res;
315 }
316
317 static int enum_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
318 {
319         struct enum_result_datastore *erds;
320         struct ast_datastore *datastore;
321         char *parse, *p;
322         unsigned int num;
323         int res = -1, k;
324         AST_DECLARE_APP_ARGS(args,
325                 AST_APP_ARG(id);
326                 AST_APP_ARG(resultnum);
327         );
328
329         if (ast_strlen_zero(data)) {
330                 ast_log(LOG_WARNING, "ENUMRESULT requires two arguments (id and resultnum)\n");
331                 goto finish;
332         }
333
334         if (!chan) {
335                 ast_log(LOG_ERROR, "ENUMRESULT can not be used without a channel!\n");
336                 goto finish;
337         }
338
339         parse = ast_strdupa(data);
340
341         AST_STANDARD_APP_ARGS(args, parse);
342
343         if (ast_strlen_zero(args.id)) {
344                 ast_log(LOG_ERROR, "A result ID must be provided to ENUMRESULT\n");
345                 goto finish;
346         }
347
348         if (ast_strlen_zero(args.resultnum)) {
349                 ast_log(LOG_ERROR, "A result number must be given to ENUMRESULT!\n");
350                 goto finish;
351         }
352
353         ast_channel_lock(chan);
354         datastore = ast_channel_datastore_find(chan, &enum_result_datastore_info, args.id);
355         ast_channel_unlock(chan);
356         if (!datastore) {
357                 ast_log(LOG_WARNING, "No ENUM results found for query id!\n");
358                 goto finish;
359         }
360
361         erds = datastore->data;
362
363         if (!strcasecmp(args.resultnum, "getnum")) {
364                 snprintf(buf, len, "%d", erds->context->naptr_rrs_count);
365                 res = 0;
366                 goto finish;
367         }
368
369         if (sscanf(args.resultnum, "%30u", &num) != 1) {
370                 ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to ENUMRESULT!\n", args.resultnum);
371                 goto finish;
372         }
373
374         if (!num || num > erds->context->naptr_rrs_count) {
375                 ast_log(LOG_WARNING, "Result number %u is not valid for ENUM query results for ID %s!\n", num, args.id);
376                 goto finish;
377         }
378
379         for (k = 0; k < erds->context->naptr_rrs_count; k++) {
380                 if (num - 1 != erds->context->naptr_rrs[k].sort_pos)
381                         continue;
382
383                 p = strchr(erds->context->naptr_rrs[k].result, ':');
384
385                 if (p && strcasecmp(erds->context->naptr_rrs[k].tech, "ALL"))
386                         ast_copy_string(buf, p + 1, len);
387                 else
388                         ast_copy_string(buf, erds->context->naptr_rrs[k].result, len);
389
390                 break;
391         }
392
393         res = 0;
394
395 finish:
396
397         return res;
398 }
399
400 static struct ast_custom_function enum_query_function = {
401         .name = "ENUMQUERY",
402         .read = enum_query_read,
403 };
404
405 static struct ast_custom_function enum_result_function = {
406         .name = "ENUMRESULT",
407         .read = enum_result_read,
408 };
409
410 static struct ast_custom_function enum_function = {
411         .name = "ENUMLOOKUP",
412         .read = function_enum,
413 };
414
415 static int function_txtcidname(struct ast_channel *chan, const char *cmd,
416                                char *data, char *buf, size_t len)
417 {
418         AST_DECLARE_APP_ARGS(args,
419                 AST_APP_ARG(number);
420                 AST_APP_ARG(zone);
421         );
422
423         buf[0] = '\0';
424
425         if (ast_strlen_zero(data)) {
426                 ast_log(LOG_WARNING, "Syntax: TXTCIDNAME(number[,zone-suffix])\n");
427                 return -1;
428         }
429
430         AST_STANDARD_APP_ARGS(args, data);
431
432         if (args.argc < 1) {
433                 ast_log(LOG_WARNING, "Syntax: TXTCIDNAME(number[,zone-suffix])\n");
434                 return -1;
435         }
436
437         if (!args.zone) {
438                 args.zone = "e164.arpa";
439         }
440
441         ast_get_txt(chan, args.number, buf, len, args.zone);
442
443         return 0;
444 }
445
446 static struct ast_custom_function txtcidname_function = {
447         .name = "TXTCIDNAME",
448         .read = function_txtcidname,
449 };
450
451 static int unload_module(void)
452 {
453         int res = 0;
454
455         res |= ast_custom_function_unregister(&enum_result_function);
456         res |= ast_custom_function_unregister(&enum_query_function);
457         res |= ast_custom_function_unregister(&enum_function);
458         res |= ast_custom_function_unregister(&txtcidname_function);
459
460         return res;
461 }
462
463 static int load_module(void)
464 {
465         int res = 0;
466
467         res |= ast_custom_function_register(&enum_result_function);
468         res |= ast_custom_function_register(&enum_query_function);
469         res |= ast_custom_function_register(&enum_function);
470         res |= ast_custom_function_register(&txtcidname_function);
471
472         return res;
473 }
474
475 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ENUM related dialplan functions",
476         .support_level = AST_MODULE_SUPPORT_CORE,
477         .load = load_module,
478         .unload = unload_module,
479         .requires = "enum",
480 );