res/res_rtp_asterisk: Enable rxjitter calculation for video
[asterisk/asterisk.git] / apps / app_read.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
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 a variable
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \ingroup applications
26  */
27
28 /*** MODULEINFO
29         <support_level>core</support_level>
30  ***/
31
32 #include "asterisk.h"
33
34 #include "asterisk/file.h"
35 #include "asterisk/pbx.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/app.h"
38 #include "asterisk/module.h"
39 #include "asterisk/indications.h"
40
41 /*** DOCUMENTATION
42         <application name="Read" language="en_US">
43                 <synopsis>
44                         Read a variable.
45                 </synopsis>
46                 <syntax>
47                         <parameter name="variable" required="true">
48                                 <para>The input digits will be stored in the given <replaceable>variable</replaceable>
49                                 name.</para>
50                         </parameter>
51                         <parameter name="filenames" argsep="&amp;">
52                                 <argument name="filename" required="true">
53                                         <para>file(s) to play before reading digits or tone with option i</para>
54                                 </argument>
55                                 <argument name="filename2" multiple="true" />
56                         </parameter>
57                         <parameter name="maxdigits">
58                                 <para>Maximum acceptable number of digits. Stops reading after
59                                 <replaceable>maxdigits</replaceable> have been entered (without
60                                 requiring the user to press the <literal>#</literal> key).</para>
61                                 <para>Defaults to <literal>0</literal> - no limit - wait for the
62                                 user press the <literal>#</literal> key. Any value below
63                                 <literal>0</literal> means the same. Max accepted value is
64                                 <literal>255</literal>.</para>
65                         </parameter>
66                         <parameter name="options">
67                                 <optionlist>
68                                         <option name="s">
69                                                 <para>to return immediately if the line is not up.</para>
70                                         </option>
71                                         <option name="i">
72                                                 <para>to play  filename as an indication tone from your
73                                                 <filename>indications.conf</filename>.</para>
74                                         </option>
75                                         <option name="n">
76                                                 <para>to read digits even if the line is not up.</para>
77                                         </option>
78                                 </optionlist>
79                         </parameter>
80                         <parameter name="attempts">
81                                 <para>If greater than <literal>1</literal>, that many
82                                 <replaceable>attempts</replaceable> will be made in the
83                                 event no data is entered.</para>
84                         </parameter>
85                         <parameter name="timeout">
86                                 <para>The number of seconds to wait for a digit response. If greater
87                                 than <literal>0</literal>, that value will override the default timeout.
88                                 Can be floating point.</para>
89                         </parameter>
90                 </syntax>
91                 <description>
92                         <para>Reads a #-terminated string of digits a certain number of times from the
93                         user in to the given <replaceable>variable</replaceable>.</para>
94                         <para>This application sets the following channel variable upon completion:</para>
95                         <variablelist>
96                                 <variable name="READSTATUS">
97                                         <para>This is the status of the read operation.</para>
98                                         <value name="OK" />
99                                         <value name="ERROR" />
100                                         <value name="HANGUP" />
101                                         <value name="INTERRUPTED" />
102                                         <value name="SKIPPED" />
103                                         <value name="TIMEOUT" />
104                                 </variable>
105                         </variablelist>
106                 </description>
107                 <see-also>
108                         <ref type="application">SendDTMF</ref>
109                 </see-also>
110         </application>
111  ***/
112
113 enum read_option_flags {
114         OPT_SKIP = (1 << 0),
115         OPT_INDICATION = (1 << 1),
116         OPT_NOANSWER = (1 << 2),
117 };
118
119 AST_APP_OPTIONS(read_app_options, {
120         AST_APP_OPTION('s', OPT_SKIP),
121         AST_APP_OPTION('i', OPT_INDICATION),
122         AST_APP_OPTION('n', OPT_NOANSWER),
123 });
124
125 static char *app = "Read";
126
127 static int read_exec(struct ast_channel *chan, const char *data)
128 {
129         int res = 0;
130         char tmp[256] = "";
131         int maxdigits = 255;
132         int tries = 1, to = 0, x = 0;
133         double tosec;
134         char *argcopy = NULL;
135         struct ast_tone_zone_sound *ts = NULL;
136         struct ast_flags flags = {0};
137         const char *status = "ERROR";
138
139         AST_DECLARE_APP_ARGS(arglist,
140                 AST_APP_ARG(variable);
141                 AST_APP_ARG(filename);
142                 AST_APP_ARG(maxdigits);
143                 AST_APP_ARG(options);
144                 AST_APP_ARG(attempts);
145                 AST_APP_ARG(timeout);
146         );
147
148         pbx_builtin_setvar_helper(chan, "READSTATUS", status);
149         if (ast_strlen_zero(data)) {
150                 ast_log(LOG_WARNING, "Read requires an argument (variable)\n");
151                 return 0;
152         }
153
154         argcopy = ast_strdupa(data);
155
156         AST_STANDARD_APP_ARGS(arglist, argcopy);
157
158         if (!ast_strlen_zero(arglist.options)) {
159                 ast_app_parse_options(read_app_options, &flags, NULL, arglist.options);
160         }
161
162         if (!ast_strlen_zero(arglist.attempts)) {
163                 tries = atoi(arglist.attempts);
164                 if (tries <= 0)
165                         tries = 1;
166         }
167
168         if (!ast_strlen_zero(arglist.timeout)) {
169                 tosec = atof(arglist.timeout);
170                 if (tosec <= 0)
171                         to = 0;
172                 else
173                         to = tosec * 1000.0;
174         }
175
176         if (ast_strlen_zero(arglist.filename)) {
177                 arglist.filename = NULL;
178         }
179         if (!ast_strlen_zero(arglist.maxdigits)) {
180                 maxdigits = atoi(arglist.maxdigits);
181                 if ((maxdigits < 1) || (maxdigits > 255)) {
182                         maxdigits = 255;
183                 } else
184                         ast_verb(3, "Accepting a maximum of %d digits.\n", maxdigits);
185         }
186         if (ast_strlen_zero(arglist.variable)) {
187                 ast_log(LOG_WARNING, "Invalid! Usage: Read(variable[,filename][,maxdigits][,option][,attempts][,timeout])\n\n");
188                 return 0;
189         }
190         if (ast_test_flag(&flags, OPT_INDICATION)) {
191                 if (!ast_strlen_zero(arglist.filename)) {
192                         ts = ast_get_indication_tone(ast_channel_zone(chan), arglist.filename);
193                 }
194         }
195         if (ast_channel_state(chan) != AST_STATE_UP) {
196                 if (ast_test_flag(&flags, OPT_SKIP)) {
197                         /* At the user's option, skip if the line is not up */
198                         pbx_builtin_setvar_helper(chan, arglist.variable, "");
199                         pbx_builtin_setvar_helper(chan, "READSTATUS", "SKIPPED");
200                         return 0;
201                 } else if (!ast_test_flag(&flags, OPT_NOANSWER)) {
202                         /* Otherwise answer unless we're supposed to read while on-hook */
203                         res = ast_answer(chan);
204                 }
205         }
206         if (!res) {
207                 while (tries && !res) {
208                         ast_stopstream(chan);
209                         if (ts && ts->data[0]) {
210                                 if (!to)
211                                         to = ast_channel_pbx(chan) ? ast_channel_pbx(chan)->rtimeoutms : 6000;
212                                 res = ast_playtones_start(chan, 0, ts->data, 0);
213                                 for (x = 0; x < maxdigits; ) {
214                                         res = ast_waitfordigit(chan, to);
215                                         ast_playtones_stop(chan);
216                                         if (res < 1) {
217                                                 if (res == 0)
218                                                         status = "TIMEOUT";
219                                                 tmp[x]='\0';
220                                                 break;
221                                         }
222                                         tmp[x++] = res;
223                                         if (tmp[x-1] == '#') {
224                                                 tmp[x-1] = '\0';
225                                                 status = "OK";
226                                                 break;
227                                         }
228                                         if (x >= maxdigits) {
229                                                 status = "OK";
230                                         }
231                                 }
232                         } else {
233                                 res = ast_app_getdata(chan, arglist.filename, tmp, maxdigits, to);
234                                 if (res == AST_GETDATA_COMPLETE || res == AST_GETDATA_EMPTY_END_TERMINATED)
235                                         status = "OK";
236                                 else if (res == AST_GETDATA_TIMEOUT)
237                                         status = "TIMEOUT";
238                                 else if (res == AST_GETDATA_INTERRUPTED)
239                                         status = "INTERRUPTED";
240                         }
241                         if (res > -1) {
242                                 pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
243                                 if (!ast_strlen_zero(tmp)) {
244                                         ast_verb(3, "User entered '%s'\n", tmp);
245                                         tries = 0;
246                                 } else {
247                                         tries--;
248                                         if (tries)
249                                                 ast_verb(3, "User entered nothing, %d chance%s left\n", tries, (tries != 1) ? "s" : "");
250                                         else
251                                                 ast_verb(3, "User entered nothing.\n");
252                                 }
253                                 res = 0;
254                         } else {
255                                 pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
256                                 ast_verb(3, "User disconnected\n");
257                         }
258                 }
259         }
260
261         if (ts) {
262                 ts = ast_tone_zone_sound_unref(ts);
263         }
264
265         if (ast_check_hangup(chan))
266                 status = "HANGUP";
267         pbx_builtin_setvar_helper(chan, "READSTATUS", status);
268         return 0;
269 }
270
271 static int unload_module(void)
272 {
273         return ast_unregister_application(app);
274 }
275
276 static int load_module(void)
277 {
278         return ast_register_application_xml(app, read_exec);
279 }
280
281 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read Variable Application");