|
|
|
@ -38,6 +38,7 @@
|
|
|
|
|
#include "asterisk/channel.h"
|
|
|
|
|
#include "asterisk/dsp.h" /* use dsp routines for silence detection */
|
|
|
|
|
#include "asterisk/format_cache.h"
|
|
|
|
|
#include "asterisk/paths.h"
|
|
|
|
|
|
|
|
|
|
/*** DOCUMENTATION
|
|
|
|
|
<application name="Record" language="en_US">
|
|
|
|
@ -102,7 +103,7 @@
|
|
|
|
|
If the user hangs up during a recording, all data will be lost and the application will terminate.</para>
|
|
|
|
|
<variablelist>
|
|
|
|
|
<variable name="RECORDED_FILE">
|
|
|
|
|
<para>Will be set to the final filename of the recording.</para>
|
|
|
|
|
<para>Will be set to the final filename of the recording, without an extension.</para>
|
|
|
|
|
</variable>
|
|
|
|
|
<variable name="RECORD_STATUS">
|
|
|
|
|
<para>This is the final status of the command</para>
|
|
|
|
@ -131,10 +132,9 @@ enum {
|
|
|
|
|
OPTION_STAR_TERMINATE = (1 << 4),
|
|
|
|
|
OPTION_IGNORE_TERMINATE = (1 << 5),
|
|
|
|
|
OPTION_KEEP = (1 << 6),
|
|
|
|
|
FLAG_HAS_PERCENT = (1 << 7),
|
|
|
|
|
OPTION_ANY_TERMINATE = (1 << 8),
|
|
|
|
|
OPTION_OPERATOR_EXIT = (1 << 9),
|
|
|
|
|
OPTION_NO_TRUNCATE = (1 << 10),
|
|
|
|
|
OPTION_ANY_TERMINATE = (1 << 7),
|
|
|
|
|
OPTION_OPERATOR_EXIT = (1 << 8),
|
|
|
|
|
OPTION_NO_TRUNCATE = (1 << 9),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AST_APP_OPTIONS(app_opts,{
|
|
|
|
@ -180,14 +180,47 @@ static int record_dtmf_response(struct ast_channel *chan, struct ast_flags *flag
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int create_destination_directory(const char *path)
|
|
|
|
|
{
|
|
|
|
|
int res;
|
|
|
|
|
char directory[PATH_MAX], *file_sep;
|
|
|
|
|
|
|
|
|
|
if (!(file_sep = strrchr(path, '/'))) {
|
|
|
|
|
/* No directory to create */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Overwrite temporarily */
|
|
|
|
|
*file_sep = '\0';
|
|
|
|
|
|
|
|
|
|
/* Absolute path? */
|
|
|
|
|
if (path[0] == '/') {
|
|
|
|
|
res = ast_mkdir(path, 0777);
|
|
|
|
|
*file_sep = '/';
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Relative path */
|
|
|
|
|
res = snprintf(directory, sizeof(directory), "%s/sounds/%s",
|
|
|
|
|
ast_config_AST_DATA_DIR, path);
|
|
|
|
|
|
|
|
|
|
*file_sep = '/';
|
|
|
|
|
|
|
|
|
|
if (res >= sizeof(directory)) {
|
|
|
|
|
/* We truncated, so we fail */
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ast_mkdir(directory, 0777);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int record_exec(struct ast_channel *chan, const char *data)
|
|
|
|
|
{
|
|
|
|
|
int res = 0;
|
|
|
|
|
int count = 0;
|
|
|
|
|
char *ext = NULL, *opts[0];
|
|
|
|
|
char *parse, *dir, *file;
|
|
|
|
|
char *parse;
|
|
|
|
|
int i = 0;
|
|
|
|
|
char tmp[256];
|
|
|
|
|
char tmp[PATH_MAX];
|
|
|
|
|
|
|
|
|
|
struct ast_filestream *s = NULL;
|
|
|
|
|
struct ast_frame *f = NULL;
|
|
|
|
@ -227,8 +260,6 @@ static int record_exec(struct ast_channel *chan, const char *data)
|
|
|
|
|
ast_app_parse_options(app_opts, &flags, opts, args.options);
|
|
|
|
|
|
|
|
|
|
if (!ast_strlen_zero(args.filename)) {
|
|
|
|
|
if (strstr(args.filename, "%d"))
|
|
|
|
|
ast_set_flag(&flags, FLAG_HAS_PERCENT);
|
|
|
|
|
ext = strrchr(args.filename, '.'); /* to support filename with a . in the filename, not format */
|
|
|
|
|
if (!ext)
|
|
|
|
|
ext = strchr(args.filename, ':');
|
|
|
|
@ -266,38 +297,31 @@ static int record_exec(struct ast_channel *chan, const char *data)
|
|
|
|
|
if (ast_test_flag(&flags, OPTION_IGNORE_TERMINATE))
|
|
|
|
|
terminator = '\0';
|
|
|
|
|
|
|
|
|
|
/* done parsing */
|
|
|
|
|
|
|
|
|
|
/* these are to allow the use of the %d in the config file for a wild card of sort to
|
|
|
|
|
create a new file with the inputed name scheme */
|
|
|
|
|
if (ast_test_flag(&flags, FLAG_HAS_PERCENT)) {
|
|
|
|
|
AST_DECLARE_APP_ARGS(fname,
|
|
|
|
|
AST_APP_ARG(piece)[100];
|
|
|
|
|
);
|
|
|
|
|
char *tmp2 = ast_strdupa(args.filename);
|
|
|
|
|
char countstring[15];
|
|
|
|
|
int idx;
|
|
|
|
|
/*
|
|
|
|
|
If a '%d' is specified as part of the filename, we replace that token with
|
|
|
|
|
sequentially incrementing numbers until we find a unique filename.
|
|
|
|
|
*/
|
|
|
|
|
if (strchr(args.filename, '%')) {
|
|
|
|
|
size_t src, dst, count = 0;
|
|
|
|
|
size_t src_len = strlen(args.filename);
|
|
|
|
|
size_t dst_len = sizeof(tmp) - 1;
|
|
|
|
|
|
|
|
|
|
/* Separate each piece out by the format specifier */
|
|
|
|
|
AST_NONSTANDARD_APP_ARGS(fname, tmp2, '%');
|
|
|
|
|
do {
|
|
|
|
|
int tmplen;
|
|
|
|
|
/* First piece has no leading percent, so it's copied verbatim */
|
|
|
|
|
ast_copy_string(tmp, fname.piece[0], sizeof(tmp));
|
|
|
|
|
tmplen = strlen(tmp);
|
|
|
|
|
for (idx = 1; idx < fname.argc; idx++) {
|
|
|
|
|
if (fname.piece[idx][0] == 'd') {
|
|
|
|
|
/* Substitute the count */
|
|
|
|
|
snprintf(countstring, sizeof(countstring), "%d", count);
|
|
|
|
|
ast_copy_string(tmp + tmplen, countstring, sizeof(tmp) - tmplen);
|
|
|
|
|
tmplen += strlen(countstring);
|
|
|
|
|
} else if (tmplen + 2 < sizeof(tmp)) {
|
|
|
|
|
/* Unknown format specifier - just copy it verbatim */
|
|
|
|
|
tmp[tmplen++] = '%';
|
|
|
|
|
tmp[tmplen++] = fname.piece[idx][0];
|
|
|
|
|
for (src = 0, dst = 0; src < src_len && dst < dst_len; src++) {
|
|
|
|
|
if (!strncmp(&args.filename[src], "%d", 2)) {
|
|
|
|
|
int s = snprintf(&tmp[dst], PATH_MAX - dst, "%zu", count);
|
|
|
|
|
if (s >= PATH_MAX - dst) {
|
|
|
|
|
/* We truncated, so we need to bail */
|
|
|
|
|
ast_log(LOG_WARNING, "Failed to create unique filename from template: %s\n", args.filename);
|
|
|
|
|
pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
dst += s;
|
|
|
|
|
src++;
|
|
|
|
|
} else {
|
|
|
|
|
tmp[dst] = args.filename[src];
|
|
|
|
|
tmp[++dst] = '\0';
|
|
|
|
|
}
|
|
|
|
|
/* Copy the remaining portion of the piece */
|
|
|
|
|
ast_copy_string(tmp + tmplen, &(fname.piece[idx][1]), sizeof(tmp) - tmplen);
|
|
|
|
|
}
|
|
|
|
|
count++;
|
|
|
|
|
} while (ast_fileexists(tmp, ext, ast_channel_language(chan)) > 0);
|
|
|
|
@ -305,7 +329,6 @@ static int record_exec(struct ast_channel *chan, const char *data)
|
|
|
|
|
ast_copy_string(tmp, args.filename, sizeof(tmp));
|
|
|
|
|
|
|
|
|
|
pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
|
|
|
|
|
/* end of routine mentioned */
|
|
|
|
|
|
|
|
|
|
if (ast_channel_state(chan) != AST_STATE_UP) {
|
|
|
|
|
if (ast_test_flag(&flags, OPTION_SKIP)) {
|
|
|
|
@ -354,11 +377,11 @@ static int record_exec(struct ast_channel *chan, const char *data)
|
|
|
|
|
ast_dsp_set_threshold(sildet, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create the directory if it does not exist. */
|
|
|
|
|
dir = ast_strdupa(tmp);
|
|
|
|
|
if ((file = strrchr(dir, '/')))
|
|
|
|
|
*file++ = '\0';
|
|
|
|
|
ast_mkdir (dir, 0777);
|
|
|
|
|
if (create_destination_directory(tmp)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Could not create directory for file %s\n", args.filename);
|
|
|
|
|
pbx_builtin_setvar_helper(chan, "RECORD_STATUS", "ERROR");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ioflags = ast_test_flag(&flags, OPTION_APPEND) ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
|
|
|
|
|
s = ast_writefile(tmp, ext, NULL, ioflags, 0, AST_FILE_MODE);
|
|
|
|
|