remove another set of redundant #include "asterisk/options.h"
[asterisk/asterisk.git] / apps / app_readexten.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2007 Dave Chappell
5  *
6  * David Chappell <David.Chappell@trincoll.edu>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Trivial application to read an extension into a variable
22  *
23  * \author David Chappell <David.Chappell@trincoll.edu>
24  * 
25  * \ingroup applications
26  */
27  
28 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
32 #include "asterisk/lock.h"
33 #include "asterisk/file.h"
34 #include "asterisk/channel.h"
35 #include "asterisk/pbx.h"
36 #include "asterisk/app.h"
37 #include "asterisk/module.h"
38 #include "asterisk/utils.h"
39 #include "asterisk/indications.h"
40
41 enum {
42         OPT_SKIP = (1 << 0),
43         OPT_INDICATION = (1 << 1),
44         OPT_NOANSWER = (1 << 2),
45 } readexten_option_flags;
46
47 AST_APP_OPTIONS(readexten_app_options, {
48         AST_APP_OPTION('s', OPT_SKIP),
49         AST_APP_OPTION('i', OPT_INDICATION),
50         AST_APP_OPTION('n', OPT_NOANSWER),
51 });
52
53 static char *app = "ReadExten";
54
55 static char *synopsis = "Read an extension into a variable";
56
57 static char *descrip = 
58 "  ReadExten(<variable>[,[<filename>][,[<context>][,[<option>][,<timeout>]]]])\n\n"
59 "Reads a #-terminated string of digits from the user into the given variable.\n"
60 "  filename  file to play before reading digits or tone with option i\n"
61 "  context   context in which to match extensions\n"
62 "  option    options are:\n"
63 "              s - Return immediately if the channel is not answered,\n"
64 "              i - Play filename as an indication tone from your\n"
65 "                  indications.conf\n"
66 "              n - Read digits even if the channel is not answered.\n"
67 "  timeout   An integer number of seconds to wait for a digit response. If\n"
68 "            greater than 0, that value will override the default timeout.\n\n"
69 "ReadExten will set READEXTENSTATUS on exit with one of the following statuses:\n"
70 "  OK        A valid extension exists in ${variable}\n"
71 "  TIMEOUT   No extension was entered in the specified time\n"
72 "  INVALID   An invalid extension, ${INVALID_EXTEN}, was entered\n"
73 "  SKIP      Line was not up and the option 's' was specified\n"
74 "  ERROR     Invalid arguments were passed\n";
75
76
77 static int readexten_exec(struct ast_channel *chan, void *data)
78 {
79         int res = 0;
80         char exten[256] = "";
81         int maxdigits = sizeof(exten) - 1;
82         int timeout = 0, digit_timeout = 0, x = 0;
83         char *argcopy = NULL, *status = "";
84         struct ind_tone_zone_sound *ts = NULL;
85         struct ast_flags flags = {0};
86
87          AST_DECLARE_APP_ARGS(arglist,
88                 AST_APP_ARG(variable);
89                 AST_APP_ARG(filename);
90                 AST_APP_ARG(context);
91                 AST_APP_ARG(options);
92                 AST_APP_ARG(timeout);
93         );
94         
95         if (ast_strlen_zero(data)) {
96                 ast_log(LOG_WARNING, "ReadExten requires at least one argument\n");
97                 pbx_builtin_setvar_helper(chan, "READEXTENSTATUS", "ERROR");
98                 return 0;
99         }
100
101         argcopy = ast_strdupa(data);
102         AST_STANDARD_APP_ARGS(arglist, argcopy);
103
104         if (ast_strlen_zero(arglist.variable)) {
105                 ast_log(LOG_WARNING, "Invalid! Usage: ReadExten(variable[|filename][|context][|options][|timeout])\n\n");
106                 pbx_builtin_setvar_helper(chan, "READEXTENSTATUS", "ERROR");
107                 return 0;
108         }
109
110         if (ast_strlen_zero(arglist.filename))
111                 arglist.filename = NULL;
112
113         if (ast_strlen_zero(arglist.context))
114                 arglist.context = chan->context;
115
116         if (!ast_strlen_zero(arglist.options))
117                 ast_app_parse_options(readexten_app_options, &flags, NULL, arglist.options);
118
119         if (!ast_strlen_zero(arglist.timeout)) {
120                 timeout = atoi(arglist.timeout);
121                 if (timeout > 0)
122                         timeout *= 1000;
123         }
124
125         if (timeout <= 0)
126                 timeout = chan->pbx ? chan->pbx->rtimeout * 1000 : 10000;
127
128         if (digit_timeout <= 0)
129                 digit_timeout = chan->pbx ? chan->pbx->dtimeout * 1000 : 5000;
130
131         if (ast_test_flag(&flags, OPT_INDICATION) && !ast_strlen_zero(arglist.filename))
132                 ts = ast_get_indication_tone(chan->zone, arglist.filename);
133
134         do {
135                 if (chan->_state != AST_STATE_UP) {
136                         if (ast_test_flag(&flags, OPT_SKIP)) {
137                                 /* At the user's option, skip if the line is not up */
138                                 pbx_builtin_setvar_helper(chan, arglist.variable, "");
139                                 status = "SKIP";
140                                 break;
141                         } else if (!ast_test_flag(&flags, OPT_NOANSWER)) {
142                                 /* Otherwise answer unless we're supposed to read while on-hook */
143                                 res = ast_answer(chan);
144                         }
145                 }
146
147                 if (res < 0) {
148                         status = "HANGUP";
149                         break;
150                 }
151
152                 ast_playtones_stop(chan);
153                 ast_stopstream(chan);
154
155                 if (ts && ts->data[0])
156                         res = ast_playtones_start(chan, 0, ts->data, 0);
157                 else if (arglist.filename)
158                         res = ast_streamfile(chan, arglist.filename, chan->language);
159
160                 for (x = 0; x < maxdigits; x++) {
161                         ast_debug(3, "extension so far: '%s', timeout: %d\n", exten, timeout);
162                         res = ast_waitfordigit(chan, timeout);
163
164                         ast_playtones_stop(chan);
165                         ast_stopstream(chan);
166                         timeout = digit_timeout;
167
168                         if (res < 1) {          /* timeout expired or hangup */
169                                 if (ast_check_hangup(chan))
170                                         status = "HANGUP";
171                                 else
172                                         status = "TIMEOUT";
173                                 break;
174                         } else if (res == '#') {
175                                 break;
176                         }
177
178                         exten[x] = res;
179                         if (!ast_matchmore_extension(chan, arglist.context, exten, 1 /* priority */, chan->cid.cid_num)) {
180                                 break;
181                         }
182                 }
183
184                 if (!ast_strlen_zero(status))
185                         break;
186
187                 if (ast_exists_extension(chan, arglist.context, exten, 1, chan->cid.cid_num)) {
188                         ast_debug(3, "User entered valid extension '%s'\n", exten);
189                         pbx_builtin_setvar_helper(chan, arglist.variable, exten);
190                         status = "OK";
191                 } else {
192                         ast_debug(3, "User dialed invalid extension '%s' in context '%s' on %s\n", exten, arglist.context, chan->name);
193                         pbx_builtin_setvar_helper(chan, "INVALID_EXTEN", exten);
194                         status = "INVALID";
195                 }
196         } while (0);
197
198         pbx_builtin_setvar_helper(chan, "READEXTENSTATUS", status);
199
200         return status[0] == 'H' ? -1 : 0;
201 }
202
203 static int acf_isexten_exec(struct ast_channel *chan, const char *cmd, char *parse, char *buffer, size_t buflen)
204 {
205         int priority_int;
206         AST_DECLARE_APP_ARGS(args,
207                 AST_APP_ARG(context);
208                 AST_APP_ARG(extension);
209                 AST_APP_ARG(priority);
210         );
211
212         AST_STANDARD_APP_ARGS(args, parse);
213
214         if (ast_strlen_zero(args.context))
215                 args.context = chan->context;
216
217         if (ast_strlen_zero(args.extension)) {
218                 ast_log(LOG_WARNING, "Syntax: VALID_EXTEN([<context>],<extension>[,<priority>]) - missing argument <extension>!\n");
219                 return -1;
220         }
221
222         if (ast_strlen_zero(args.priority))
223                 priority_int = 1;
224         else
225                 priority_int = atoi(args.priority);
226
227         if (ast_exists_extension(chan, args.context, args.extension, priority_int, chan->cid.cid_num))
228             ast_copy_string(buffer, "1", buflen);
229         else
230             ast_copy_string(buffer, "0", buflen);
231
232         return 0;
233 }
234
235 static struct ast_custom_function acf_isexten = {
236         .name = "VALID_EXTEN",
237         .synopsis = "Determine whether an extension exists or not",
238         .syntax = "VALID_EXTEN([<context>],<extension>[,<priority>])",
239         .desc =
240 "Returns a true value if the indicated context, extension, and priority exist.\n"
241 "Context defaults to the current context, priority defaults to 1.\n",
242         .read = acf_isexten_exec,
243 };
244
245 static int unload_module(void)
246 {
247         int res = ast_unregister_application(app);
248         res |= ast_custom_function_unregister(&acf_isexten);
249
250         return res;     
251 }
252
253 static int load_module(void)
254 {
255         int res = ast_register_application(app, readexten_exec, synopsis, descrip);
256         res |= ast_custom_function_register(&acf_isexten);
257         return res;
258 }
259
260 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read and evaluate extension validity");