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