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