app.c: Allow ampersands in playback lists to be escaped.

Any function or application that accepts a `&`-separated list of
filenames can now include a literal `&` in a filename by wrapping the
entire filename in single quotes, e.g.:

```
exten = _X.,n,Playback('https://example.com/sound.cgi?a=b&c=d'&hello-world)
```

Fixes #172

UpgradeNote: Ampersands in URLs passed to the `Playback()`,
`Background()`, `SpeechBackground()`, `Read()`, `Authenticate()`, or
`Queue()` applications as filename arguments can now be escaped by
single quoting the filename. Additionally, this is also possible when
using the `CONFBRIDGE` dialplan function, or configuring various
features in `confbridge.conf` and `queues.conf`.
This commit is contained in:
Sean Bright 2023-11-07 15:03:53 -05:00 committed by asterisk-org-access-app[bot]
parent 31c44d0634
commit ca931c9436
9 changed files with 69 additions and 19 deletions

View File

@ -95,8 +95,17 @@ static const char app[] = "Authenticate";
maxdigits have been entered (without requiring the user to press the <literal>#</literal> key).
Defaults to 0 - no limit - wait for the user press the <literal>#</literal> key.</para>
</parameter>
<parameter name="prompt" required="false">
<para>Override the agent-pass prompt file.</para>
<parameter name="prompt" required="false" argsep="&amp;">
<para>Override the &quot;agent-pass&quot; sound file. Can be
an ampersand separated list of filenames. If the filename
is a relative filename (it does not begin with a slash), it
will be searched for in the Asterisk sounds directory. If the
filename is able to be parsed as a URL, Asterisk will
download the file and then begin playback on it. To include a
literal <literal>&amp;</literal> in the URL you can enclose
the URL in single quotes.</para>
<argument name="prompt" required="true" />
<argument name="prompt2" multiple="true" />
</parameter>
</syntax>
<description>

View File

@ -3035,7 +3035,7 @@ static int action_playback(struct ast_bridge_channel *bridge_channel, const char
char *file_copy = ast_strdupa(playback_file);
char *file = NULL;
while ((file = strsep(&file_copy, "&"))) {
while ((file = ast_strsep(&file_copy, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
return -1;
@ -3059,7 +3059,7 @@ static int action_playback_and_continue(struct confbridge_conference *conference
char *file_copy = ast_strdupa(playback_file);
char *file = NULL;
while ((file = strsep(&file_copy, "&"))) {
while ((file = ast_strsep(&file_copy, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
return -1;

View File

@ -48,6 +48,13 @@
</synopsis>
<syntax>
<parameter name="filenames" required="true" argsep="&amp;">
<para>Ampersand separated list of filenames. If the filename
is a relative filename (it does not begin with a slash), it
will be searched for in the Asterisk sounds directory. If the
filename is able to be parsed as a URL, Asterisk will
download the file and then begin playback on it. To include a
literal <literal>&amp;</literal> in the URL you can enclose
the URL in single quotes.</para>
<argument name="filename" required="true" />
<argument name="filename2" multiple="true" />
</parameter>
@ -492,7 +499,7 @@ static int playback_exec(struct ast_channel *chan, const char *data)
char *front;
ast_stopstream(chan);
while (!res && (front = strsep(&back, "&"))) {
while (!res && (front = ast_strsep(&back, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
if (option_say)
res = say_full(chan, front, "", ast_channel_language(chan), NULL, -1, -1);
else if (option_mix){

View File

@ -230,11 +230,18 @@
<para><replaceable>URL</replaceable> will be sent to the called party if the channel supports it.</para>
</parameter>
<parameter name="announceoverride" argsep="&amp;">
<argument name="filename" required="true">
<para>Announcement file(s) to play to agent before bridging call, overriding the announcement(s)
configured in <filename>queues.conf</filename>, if any.</para>
</argument>
<argument name="filename2" multiple="true" />
<para>Announcement file(s) to play to agent before bridging
call, overriding the announcement(s) configured in
<filename>queues.conf</filename>, if any.</para>
<para>Ampersand separated list of filenames. If the filename
is a relative filename (it does not begin with a slash), it
will be searched for in the Asterisk sounds directory. If the
filename is able to be parsed as a URL, Asterisk will
download the file and then begin playback on it. To include a
literal <literal>&amp;</literal> in the URL you can enclose
the URL in single quotes.</para>
<argument name="announceoverride" required="true" />
<argument name="announceoverride2" multiple="true" />
</parameter>
<parameter name="timeout">
<para>Will cause the queue to fail out after a specified number of
@ -7195,7 +7202,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
if (!res2 && announce) {
char *front;
char *announcefiles = ast_strdupa(announce);
while ((front = strsep(&announcefiles, "&"))) {
while ((front = ast_strsep(&announcefiles, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
if (play_file(peer, front) < 0) {
ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", front, ast_channel_name(peer));
}

View File

@ -49,9 +49,15 @@
name.</para>
</parameter>
<parameter name="filenames" argsep="&amp;">
<argument name="filename" required="true">
<para>file(s) to play before reading digits or tone with option i</para>
</argument>
<para>Ampersand separated list of filenames to play before
reading digits or tone with option <literal>i</literal>. If
the filename is a relative filename (it does not begin with a
slash), it will be searched for in the Asterisk sounds
directory. If the filename is able to be parsed as a URL,
Asterisk will download the file and then begin playback on
it. To include a literal <literal>&amp;</literal> in the URL
you can enclose the URL in single quotes.</para>
<argument name="filename" required="true" />
<argument name="filename2" multiple="true" />
</parameter>
<parameter name="maxdigits">

View File

@ -371,7 +371,8 @@ static void play_files_helper(struct ast_channel *chan, const char *prompts)
char *prompt, *rest = ast_strdupa(prompts);
ast_stopstream(chan);
while ((prompt = strsep(&rest, "&")) && !ast_stream_and_wait(chan, prompt, "")) {
while ((prompt = ast_strsep(&rest, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))
&& !ast_stream_and_wait(chan, prompt, "")) {
ast_stopstream(chan);
}
}

View File

@ -85,7 +85,17 @@
Play a sound file and wait for speech to be recognized.
</synopsis>
<syntax>
<parameter name="sound_file" required="true" />
<parameter name="sound_file" required="true" argsep="&amp;">
<para>Ampersand separated list of filenames. If the filename
is a relative filename (it does not begin with a slash), it
will be searched for in the Asterisk sounds directory. If the
filename is able to be parsed as a URL, Asterisk will
download the file and then begin playback on it. To include a
literal <literal>&amp;</literal> in the URL you can enclose
the URL in single quotes.</para>
<argument name="sound_file" required="true" />
<argument name="sound_file2" multiple="true" />
</parameter>
<parameter name="timeout">
<para>Timeout integer in seconds. Note the timeout will only start
once the sound file has stopped playing.</para>
@ -776,7 +786,10 @@ static int speech_background(struct ast_channel *chan, const char *data)
/* Okay it's streaming so go into a loop grabbing frames! */
while (done == 0) {
/* If the filename is null and stream is not running, start up a new sound file */
if (!quieted && (ast_channel_streamid(chan) == -1 && ast_channel_timingfunc(chan) == NULL) && (filename = strsep(&filename_tmp, "&"))) {
if (!quieted
&& ast_channel_streamid(chan) == -1
&& ast_channel_timingfunc(chan) == NULL
&& (filename = ast_strsep(&filename_tmp, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
/* Discard old stream information */
ast_stopstream(chan);
/* Start new stream */

View File

@ -205,7 +205,7 @@ enum ast_getdata_result ast_app_getdata_terminator(struct ast_channel *c, const
prompt = "";
filename = ast_strdupa(prompt);
while ((front = strsep(&filename, "&"))) {
while ((front = ast_strsep(&filename, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
if (!ast_strlen_zero(front)) {
res = ast_streamfile(c, front, ast_channel_language(c));
if (res)

View File

@ -77,6 +77,13 @@
</synopsis>
<syntax>
<parameter name="filenames" required="true" argsep="&amp;">
<para>Ampersand separated list of filenames. If the filename
is a relative filename (it does not begin with a slash), it
will be searched for in the Asterisk sounds directory. If the
filename is able to be parsed as a URL, Asterisk will
download the file and then begin playback on it. To include a
literal <literal>&amp;</literal> in the URL you can enclose
the URL in single quotes.</para>
<argument name="filename1" required="true" />
<argument name="filename2" multiple="true" />
</parameter>
@ -1172,7 +1179,7 @@ static int pbx_builtin_background(struct ast_channel *chan, const char *data)
ast_stopstream(chan); /* Stop anything playing */
/* Stream the list of files */
while (!res && (front = strsep(&back, "&")) ) {
while (!res && (front = ast_strsep(&back, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
if ( (res = ast_streamfile(chan, front, args.lang)) ) {
ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", ast_channel_name(chan), (char*)data);
res = 0;