Skip to content

Commit 80ca35a

Browse files
committed
Add out-of-tree modules
1 parent 356742a commit 80ca35a

13 files changed

+5137
-0
lines changed

apps/app_dialtone.c

+386
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
1+
/*
2+
* Asterisk -- An open source telephony toolkit.
3+
*
4+
* Copyright (C) 2021, Naveen Albert
5+
*
6+
* Naveen Albert <[email protected]>
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 Customer dial pulse receiver (CDPR)
22+
*
23+
* \author Naveen Albert <[email protected]>
24+
*
25+
* \ingroup applications
26+
*/
27+
28+
/*** MODULEINFO
29+
<support_level>extended</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="DialTone" language="en_US">
43+
<synopsis>
44+
Reads a telephone number from a user, terminating dialing against a digit map.
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="context" required="true">
52+
<para>Context to use to as the digit map for this dial tone. The digit map
53+
context is a dialplan context with extensions (including pattern matches)
54+
that should return a non-zero value to conclude dialing on a match. Returning
55+
0 will continue dialing.</para>
56+
</parameter>
57+
<parameter name="filenames" argsep="&amp;">
58+
<argument name="filename" required="true">
59+
<para>file(s) to play before reading first digit or tone with option i</para>
60+
</argument>
61+
<argument name="filename2" multiple="true" />
62+
</parameter>
63+
<parameter name="filenames2" argsep="&amp;">
64+
<argument name="filename" required="true">
65+
<para>file(s) to play before reading subsequent digits or tone with option i</para>
66+
</argument>
67+
<argument name="filename2" multiple="true" />
68+
</parameter>
69+
<parameter name="maxdigits">
70+
<para>Maximum acceptable number of digits. Stops reading after
71+
<replaceable>maxdigits</replaceable> have been entered (without
72+
requiring the user to press the <literal>#</literal> key).</para>
73+
<para>Defaults to <literal>0</literal> - no limit - wait for the
74+
user press the <literal>#</literal> key. Any value below
75+
<literal>0</literal> means the same. Max accepted value is
76+
<literal>255</literal>. This overrides the digit map, and will terminate
77+
dialing even if there is no match in the digit map context.</para>
78+
</parameter>
79+
<parameter name="timeout">
80+
<para>The number of seconds to wait for a digit response. If greater
81+
than <literal>0</literal>, that value will override the default timeout.
82+
Can be floating point.</para>
83+
</parameter>
84+
<parameter name="leading">
85+
<para>Leading digits that should be used to initialize the number dialed.
86+
Useful for second dial tones or when additional digits need to be
87+
received and use the same digit map.</para>
88+
</parameter>
89+
<parameter name="options">
90+
<optionlist>
91+
<option name="i">
92+
<para>to play filename as an indication tone from your
93+
<filename>indications.conf</filename>.</para>
94+
</option>
95+
<option name="p">
96+
<para>Parse digit map by performing variable substitution.</para>
97+
</option>
98+
<option name="r">
99+
<para>Reevaluate the variable substitution of
100+
<replaceable>filename2</replaceable> each digit
101+
(for all subsequent digits).</para>
102+
</option>
103+
<option name="t">
104+
<para>Terminate dialing using the <literal>#</literal> key.
105+
By default, the <literal>#</literal> key is not treated specially
106+
and will not terminate input.</para>
107+
</option>
108+
</optionlist>
109+
</parameter>
110+
</syntax>
111+
<description>
112+
<para>Reads a telephone number from a user into the
113+
given <replaceable>variable</replaceable>. Dialing
114+
concludes once an extension match is found in <literal>context</literal>
115+
that returns a non-zero number.</para>
116+
<para>This application does not automatically answer the channel and should
117+
be preceded by <literal>Progress</literal> or <literal>Answer</literal>.</para>
118+
</description>
119+
<see-also>
120+
<ref type="application">DISA</ref>
121+
<ref type="application">Read</ref>
122+
<ref type="application">ReadExten</ref>
123+
</see-also>
124+
</application>
125+
***/
126+
127+
#define BUFFER_LEN 256
128+
129+
enum dialtone_option_flags {
130+
OPT_INDICATION = (1 << 0),
131+
OPT_REEVALUATE = (1 << 1),
132+
OPT_TERMINATE = (1 << 2),
133+
OPT_PARSE = (1 << 3),
134+
};
135+
136+
AST_APP_OPTIONS(dialtone_app_options, {
137+
AST_APP_OPTION('i', OPT_INDICATION),
138+
AST_APP_OPTION('p', OPT_PARSE),
139+
AST_APP_OPTION('r', OPT_REEVALUATE),
140+
AST_APP_OPTION('t', OPT_TERMINATE),
141+
});
142+
143+
static char *app = "DialTone";
144+
145+
static int get_extension_data(char *name, int namesize, struct ast_channel *c,
146+
const char *context, const char *exten, int priority)
147+
{
148+
struct ast_exten *e;
149+
struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
150+
ast_rdlock_contexts();
151+
e = pbx_find_extension(c, NULL, &q, context, exten, priority, NULL, "", E_MATCH);
152+
ast_unlock_contexts();
153+
if (e) {
154+
if (name) {
155+
const char *tmp = ast_get_extension_app_data(e);
156+
if (tmp) {
157+
ast_copy_string(name, tmp, namesize);
158+
}
159+
}
160+
return 0;
161+
}
162+
return -1;
163+
}
164+
165+
static int digit_map_match(struct ast_channel *chan, char *context, char *digits, int parse)
166+
{
167+
char tmpbuf[BUFFER_LEN];
168+
int pri = 1, res;
169+
170+
if (get_extension_data(tmpbuf, BUFFER_LEN, chan, context, digits, pri)) {
171+
ast_debug(1, "Digit map result: could not find extension %s in context %s\n", digits, context);
172+
return 0; /* if extension DOESN'T exist, then definitely not a match */
173+
}
174+
if (parse) {
175+
char buf[BUFFER_LEN];
176+
const char *realcontext, *realexten;
177+
int realpriority;
178+
179+
/* get_extension_data and core logic of func_evalexten should be made ast_ public APIs. This is ALL duplicated logic. */
180+
ast_channel_lock(chan);
181+
realcontext = ast_strdupa(ast_channel_context(chan));
182+
realexten = ast_strdupa(ast_channel_exten(chan));
183+
realpriority = ast_channel_priority(chan);
184+
/* Substitute variables now, using the location of the evaluated extension */
185+
/* strdupa required or we'll just overwrite what we read when we set these */
186+
ast_channel_context_set(chan, context);
187+
ast_channel_exten_set(chan, digits);
188+
ast_channel_priority_set(chan, pri);
189+
ast_channel_unlock(chan);
190+
pbx_substitute_variables_helper(chan, tmpbuf, buf, BUFFER_LEN);
191+
ast_channel_lock(chan);
192+
ast_channel_context_set(chan, realcontext);
193+
ast_channel_exten_set(chan, realexten);
194+
ast_channel_priority_set(chan, realpriority);
195+
ast_channel_unlock(chan);
196+
res = atoi(buf);
197+
ast_debug(1, "Substituted %s -> %s -> %d\n", tmpbuf, buf, res);
198+
} else {
199+
res = atoi(tmpbuf);
200+
}
201+
/* if extension does exist, it MAY be a match */
202+
ast_debug(1, "Digit map result: %d\n", res);
203+
return res;
204+
}
205+
206+
static int dialtone_exec(struct ast_channel *chan, const char *data)
207+
{
208+
int res = 0;
209+
char tmp[256] = "";
210+
int maxdigits = 255;
211+
int to = 0, x = 0;
212+
double tosec;
213+
char *argcopy = NULL;
214+
struct ast_tone_zone_sound *ts = NULL;
215+
struct ast_flags flags = {0};
216+
int done = 0;
217+
char *tmpdigit, *subsequentaudio = NULL;
218+
char *terminator = "";
219+
int parse = 0;
220+
221+
AST_DECLARE_APP_ARGS(arglist,
222+
AST_APP_ARG(variable);
223+
AST_APP_ARG(context);
224+
AST_APP_ARG(filename);
225+
AST_APP_ARG(filename2);
226+
AST_APP_ARG(maxdigits);
227+
AST_APP_ARG(timeout);
228+
AST_APP_ARG(leading);
229+
AST_APP_ARG(options);
230+
);
231+
232+
if (ast_strlen_zero(data)) {
233+
ast_log(LOG_WARNING, "DialTone requires an argument (variable)\n");
234+
return 0;
235+
}
236+
237+
argcopy = ast_strdupa(data);
238+
239+
AST_STANDARD_APP_ARGS(arglist, argcopy);
240+
241+
if (ast_strlen_zero(arglist.variable)) {
242+
ast_log(LOG_WARNING, "Invalid! Usage: DialTone(variable,context[,initialfilename][,subsequentfilename][,timeout][,options])\n\n");
243+
return 0;
244+
}
245+
if (!ast_strlen_zero(arglist.maxdigits)) {
246+
maxdigits = atoi(arglist.maxdigits);
247+
if ((maxdigits < 1) || (maxdigits > 255)) {
248+
maxdigits = 255;
249+
} else {
250+
ast_verb(5, "Accepting a maximum of %d digits.\n", maxdigits);
251+
}
252+
}
253+
if (!ast_strlen_zero(arglist.options)) {
254+
ast_app_parse_options(dialtone_app_options, &flags, NULL, arglist.options);
255+
}
256+
257+
if (!ast_strlen_zero(arglist.timeout)) {
258+
tosec = atof(arglist.timeout);
259+
if (tosec <= 0) {
260+
to = 0;
261+
} else {
262+
to = tosec * 1000.0;
263+
}
264+
}
265+
if (ast_strlen_zero(arglist.filename)) {
266+
arglist.filename = NULL;
267+
}
268+
if (!ast_strlen_zero(arglist.filename2)) {
269+
subsequentaudio = arglist.filename2;
270+
}
271+
if (!ast_strlen_zero(arglist.leading)) {
272+
ast_copy_string(tmp, arglist.leading, BUFFER_LEN);
273+
x = strlen(arglist.leading);
274+
}
275+
if (ast_test_flag(&flags, OPT_TERMINATE)) {
276+
terminator = "#";
277+
}
278+
if (ast_test_flag(&flags, OPT_PARSE)) {
279+
parse = 1;
280+
}
281+
if (ast_test_flag(&flags, OPT_REEVALUATE)) {
282+
const char *context = NULL, *exten = NULL;
283+
int ipri;
284+
ast_channel_lock(chan);
285+
context = ast_channel_context(chan);
286+
exten = ast_strdupa(ast_channel_exten(chan));
287+
ipri = ast_channel_priority(chan);
288+
ast_channel_unlock(chan);
289+
char tmpbuf[BUFFER_LEN];
290+
291+
/* this is capable of parsing the exten data properly */
292+
AST_DECLARE_APP_ARGS(extendata,
293+
AST_APP_ARG(variable);
294+
AST_APP_ARG(context);
295+
AST_APP_ARG(filename);
296+
AST_APP_ARG(filename2);
297+
AST_APP_ARG(maxdigits);
298+
AST_APP_ARG(timeout);
299+
AST_APP_ARG(leading);
300+
AST_APP_ARG(options);
301+
);
302+
303+
if (get_extension_data(tmpbuf, BUFFER_LEN, chan, context, exten, ipri)) {
304+
ast_log(LOG_WARNING, "Cannot reevaluate audio: %s\n", arglist.filename2);
305+
return -1;
306+
}
307+
308+
AST_STANDARD_APP_ARGS(extendata, tmpbuf);
309+
310+
subsequentaudio = ast_strdupa(extendata.filename2); /* this is what we wanted */
311+
}
312+
ast_stopstream(chan);
313+
if (x && digit_map_match(chan, arglist.context, tmp, parse)) { /* leading digits were passed in, so check the digit map before doing anything */
314+
done = 1;
315+
}
316+
if (!done) {
317+
do {
318+
if (ts) {
319+
ts = ast_tone_zone_sound_unref(ts); /* unref first ts, ref second ts (etc.) */
320+
}
321+
if (!ts && ast_test_flag(&flags, OPT_INDICATION)) {
322+
if ((!x && !ast_strlen_zero(arglist.filename)) || (x && !ast_strlen_zero(subsequentaudio))) {
323+
ts = ast_get_indication_tone(ast_channel_zone(chan), x ? subsequentaudio : arglist.filename);
324+
}
325+
}
326+
if (ts && ((!x && !ast_strlen_zero(arglist.filename)) || (x && !ast_strlen_zero(subsequentaudio)))) {
327+
res = ast_playtones_start(chan, 0, ts->data, 0);
328+
res = ast_waitfordigit(chan, to ? to : (ast_channel_pbx(chan) ? ast_channel_pbx(chan)->rtimeoutms : 6000));
329+
ast_playtones_stop(chan);
330+
if (res < 1) {
331+
tmp[x] = '\0';
332+
done = 1;
333+
}
334+
tmp[x++] = res;
335+
if (strchr(terminator, tmp[x-1])) {
336+
tmp[x-1] = '\0';
337+
done = 1;
338+
}
339+
} else {
340+
char buf[BUFFER_LEN];
341+
char *audio = subsequentaudio;
342+
if (ast_test_flag(&flags, OPT_REEVALUATE)) {
343+
pbx_substitute_variables_helper(chan, subsequentaudio, buf, BUFFER_LEN);
344+
audio = buf;
345+
}
346+
tmpdigit = tmp + x;
347+
/* default timeout is NO timeout for audio files. But 0 does not mean 0 timeout, so get really close (1ms) */
348+
res = ast_app_getdata_terminator(chan, x ? audio : arglist.filename, tmpdigit, 1, to ? to : 1, terminator);
349+
if (res != AST_GETDATA_COMPLETE) {
350+
done = 1;
351+
}
352+
x++;
353+
}
354+
if (tmp[x - 1]) {
355+
tmpdigit = tmp + x - 1;
356+
ast_verb(4, "User dialed digit '%s'\n", tmpdigit);
357+
}
358+
} while (!done && !digit_map_match(chan, arglist.context, tmp, parse) && x < maxdigits);
359+
}
360+
361+
if (!ast_strlen_zero(tmp)) {
362+
ast_verb(3, "User dialed '%s'\n", tmp);
363+
} else {
364+
ast_verb(3, "User dialed nothing.\n");
365+
}
366+
367+
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
368+
369+
if (ts) {
370+
ts = ast_tone_zone_sound_unref(ts);
371+
}
372+
373+
return 0;
374+
}
375+
376+
static int unload_module(void)
377+
{
378+
return ast_unregister_application(app);
379+
}
380+
381+
static int load_module(void)
382+
{
383+
return ast_register_application_xml(app, dialtone_exec);
384+
}
385+
386+
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Advanced dial tone application");

0 commit comments

Comments
 (0)