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