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