mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-21 12:30:41 +00:00
ARI - channel recording support
This patch is the first step in adding recording support to the Asterisk REST Interface. Recordings are stored in /var/spool/recording. Since recordings may be destructive (overwriting existing files), the API rejects attempts to escape the recording directory (avoiding issues if someone attempts to record to ../../lib/sounds/greeting, for example). (closes issue ASTERISK-21594) (closes issue ASTERISK-21581) Review: https://reviewboard.asterisk.org/r/2612/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393550 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
27
main/app.c
27
main/app.c
@@ -1169,7 +1169,7 @@ static int global_maxsilence = 0;
|
||||
* \retval 't' Recording ended from the message exceeding the maximum duration, or via DTMF in prepend mode
|
||||
* \retval dtmfchar Recording ended via the return value's DTMF character for either cancel or accept.
|
||||
*/
|
||||
static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence, const char *path, int prepend, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound)
|
||||
static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence, const char *path, int prepend, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists)
|
||||
{
|
||||
int d = 0;
|
||||
char *fmts;
|
||||
@@ -1186,6 +1186,21 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
|
||||
struct ast_format rfmt;
|
||||
struct ast_silence_generator *silgen = NULL;
|
||||
char prependfile[PATH_MAX];
|
||||
int ioflags; /* IO flags for writing output file */
|
||||
|
||||
ioflags = O_CREAT|O_WRONLY;
|
||||
|
||||
switch (if_exists) {
|
||||
case AST_RECORD_IF_EXISTS_FAIL:
|
||||
ioflags |= O_EXCL;
|
||||
break;
|
||||
case AST_RECORD_IF_EXISTS_OVERWRITE:
|
||||
ioflags |= O_TRUNC;
|
||||
break;
|
||||
case AST_RECORD_IF_EXISTS_APPEND:
|
||||
ioflags |= O_APPEND;
|
||||
break;
|
||||
}
|
||||
|
||||
ast_format_clear(&rfmt);
|
||||
if (silencethreshold < 0) {
|
||||
@@ -1239,7 +1254,7 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
|
||||
|
||||
end = start = time(NULL); /* pre-initialize end to be same as start in case we never get into loop */
|
||||
for (x = 0; x < fmtcnt; x++) {
|
||||
others[x] = ast_writefile(prepend ? prependfile : recordfile, sfmt[x], comment, O_TRUNC, 0, AST_FILE_MODE);
|
||||
others[x] = ast_writefile(prepend ? prependfile : recordfile, sfmt[x], comment, ioflags, 0, AST_FILE_MODE);
|
||||
ast_verb(3, "x=%d, open writing: %s format: %s, %p\n", x, prepend ? prependfile : recordfile, sfmt[x], others[x]);
|
||||
|
||||
if (!others[x]) {
|
||||
@@ -1477,19 +1492,19 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
|
||||
static const char default_acceptdtmf[] = "#";
|
||||
static const char default_canceldtmf[] = "";
|
||||
|
||||
int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf)
|
||||
int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists)
|
||||
{
|
||||
return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, 0, silencethreshold, maxsilence, path, 0, S_OR(acceptdtmf, default_acceptdtmf), S_OR(canceldtmf, default_canceldtmf), 0);
|
||||
return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, 0, silencethreshold, maxsilence, path, 0, S_OR(acceptdtmf, default_acceptdtmf), S_OR(canceldtmf, default_canceldtmf), skip_confirmation_sound, if_exists);
|
||||
}
|
||||
|
||||
int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence, const char *path)
|
||||
{
|
||||
return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, 0, silencethreshold, maxsilence, path, 0, default_acceptdtmf, default_canceldtmf, 0);
|
||||
return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, 0, silencethreshold, maxsilence, path, 0, default_acceptdtmf, default_canceldtmf, 0, AST_RECORD_IF_EXISTS_OVERWRITE);
|
||||
}
|
||||
|
||||
int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence)
|
||||
{
|
||||
return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, beep, silencethreshold, maxsilence, NULL, 1, default_acceptdtmf, default_canceldtmf, 1);
|
||||
return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, beep, silencethreshold, maxsilence, NULL, 1, default_acceptdtmf, default_canceldtmf, 1, AST_RECORD_IF_EXISTS_OVERWRITE);
|
||||
}
|
||||
|
||||
/* Channel group core functions */
|
||||
|
@@ -373,6 +373,7 @@ struct _cfg_paths {
|
||||
char module_dir[PATH_MAX];
|
||||
char spool_dir[PATH_MAX];
|
||||
char monitor_dir[PATH_MAX];
|
||||
char recording_dir[PATH_MAX];
|
||||
char var_dir[PATH_MAX];
|
||||
char data_dir[PATH_MAX];
|
||||
char log_dir[PATH_MAX];
|
||||
@@ -397,6 +398,7 @@ const char *ast_config_AST_CONFIG_FILE = cfg_paths.config_file;
|
||||
const char *ast_config_AST_MODULE_DIR = cfg_paths.module_dir;
|
||||
const char *ast_config_AST_SPOOL_DIR = cfg_paths.spool_dir;
|
||||
const char *ast_config_AST_MONITOR_DIR = cfg_paths.monitor_dir;
|
||||
const char *ast_config_AST_RECORDING_DIR = cfg_paths.recording_dir;
|
||||
const char *ast_config_AST_VAR_DIR = cfg_paths.var_dir;
|
||||
const char *ast_config_AST_DATA_DIR = cfg_paths.data_dir;
|
||||
const char *ast_config_AST_LOG_DIR = cfg_paths.log_dir;
|
||||
@@ -3306,6 +3308,7 @@ static void ast_readconfig(void)
|
||||
ast_copy_string(cfg_paths.spool_dir, DEFAULT_SPOOL_DIR, sizeof(cfg_paths.spool_dir));
|
||||
ast_copy_string(cfg_paths.module_dir, DEFAULT_MODULE_DIR, sizeof(cfg_paths.module_dir));
|
||||
snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir), "%s/monitor", cfg_paths.spool_dir);
|
||||
snprintf(cfg_paths.recording_dir, sizeof(cfg_paths.recording_dir), "%s/recording", cfg_paths.spool_dir);
|
||||
ast_copy_string(cfg_paths.var_dir, DEFAULT_VAR_DIR, sizeof(cfg_paths.var_dir));
|
||||
ast_copy_string(cfg_paths.data_dir, DEFAULT_DATA_DIR, sizeof(cfg_paths.data_dir));
|
||||
ast_copy_string(cfg_paths.log_dir, DEFAULT_LOG_DIR, sizeof(cfg_paths.log_dir));
|
||||
@@ -3341,6 +3344,7 @@ static void ast_readconfig(void)
|
||||
} else if (!strcasecmp(v->name, "astspooldir")) {
|
||||
ast_copy_string(cfg_paths.spool_dir, v->value, sizeof(cfg_paths.spool_dir));
|
||||
snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir), "%s/monitor", v->value);
|
||||
snprintf(cfg_paths.recording_dir, sizeof(cfg_paths.recording_dir), "%s/recording", v->value);
|
||||
} else if (!strcasecmp(v->name, "astvarlibdir")) {
|
||||
ast_copy_string(cfg_paths.var_dir, v->value, sizeof(cfg_paths.var_dir));
|
||||
if (!found.dbdir)
|
||||
|
@@ -3029,6 +3029,15 @@ int ast_answer(struct ast_channel *chan)
|
||||
return __ast_answer(chan, 0);
|
||||
}
|
||||
|
||||
inline int ast_auto_answer(struct ast_channel *chan)
|
||||
{
|
||||
if (ast_channel_state(chan) == AST_STATE_UP) {
|
||||
/* Already answered */
|
||||
return 0;
|
||||
}
|
||||
return ast_answer(chan);
|
||||
}
|
||||
|
||||
int ast_channel_get_duration(struct ast_channel *chan)
|
||||
{
|
||||
ast_assert(NULL != chan);
|
||||
|
@@ -1020,6 +1020,9 @@ int ast_closestream(struct ast_filestream *f)
|
||||
* We close the stream in order to quit queuing frames now, because we might
|
||||
* change the writeformat, which could result in a subsequent write error, if
|
||||
* the format is different. */
|
||||
if (f == NULL) {
|
||||
return 0;
|
||||
}
|
||||
filestream_close(f);
|
||||
ao2_ref(f, -1);
|
||||
return 0;
|
||||
|
94
main/utils.c
94
main/utils.c
@@ -2105,6 +2105,100 @@ int ast_mkdir(const char *path, int mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int safe_mkdir(const char *base_path, char *path, int mode)
|
||||
{
|
||||
RAII_VAR(char *, absolute_path, NULL, free);
|
||||
|
||||
absolute_path = realpath(path, NULL);
|
||||
|
||||
if (absolute_path) {
|
||||
/* Path exists, but is it in the right place? */
|
||||
if (!ast_begins_with(absolute_path, base_path)) {
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
/* It is in the right place! */
|
||||
return 0;
|
||||
} else {
|
||||
/* Path doesn't exist. */
|
||||
|
||||
/* The slash terminating the subpath we're checking */
|
||||
char *path_term = strchr(path, '/');
|
||||
/* True indicates the parent path is within base_path */
|
||||
int parent_is_safe = 0;
|
||||
int res;
|
||||
|
||||
while (path_term) {
|
||||
RAII_VAR(char *, absolute_subpath, NULL, free);
|
||||
|
||||
/* Truncate the path one past the slash */
|
||||
char c = *(path_term + 1);
|
||||
*(path_term + 1) = '\0';
|
||||
absolute_subpath = realpath(path, NULL);
|
||||
|
||||
if (absolute_subpath) {
|
||||
/* Subpath exists, but is it safe? */
|
||||
parent_is_safe = ast_begins_with(
|
||||
absolute_subpath, base_path);
|
||||
} else if (parent_is_safe) {
|
||||
/* Subpath does not exist, but parent is safe
|
||||
* Create it */
|
||||
res = mkdir(path, mode);
|
||||
if (res != 0) {
|
||||
ast_assert(errno != EEXIST);
|
||||
return errno;
|
||||
}
|
||||
} else {
|
||||
/* Subpath did not exist, parent was not safe
|
||||
* Fail! */
|
||||
errno = EPERM;
|
||||
return errno;
|
||||
}
|
||||
/* Restore the path */
|
||||
*(path_term + 1) = c;
|
||||
/* Move on to the next slash */
|
||||
path_term = strchr(path_term + 1, '/');
|
||||
}
|
||||
|
||||
/* Now to build the final path, but only if it's safe */
|
||||
if (!parent_is_safe) {
|
||||
errno = EPERM;
|
||||
return errno;
|
||||
}
|
||||
|
||||
res = mkdir(path, mode);
|
||||
if (res != 0 && errno != EEXIST) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ast_safe_mkdir(const char *base_path, const char *path, int mode)
|
||||
{
|
||||
RAII_VAR(char *, absolute_base_path, NULL, free);
|
||||
RAII_VAR(char *, p, NULL, ast_free);
|
||||
|
||||
if (base_path == NULL || path == NULL) {
|
||||
errno = EFAULT;
|
||||
return errno;
|
||||
}
|
||||
|
||||
p = ast_strdup(path);
|
||||
if (p == NULL) {
|
||||
errno = ENOMEM;
|
||||
return errno;
|
||||
}
|
||||
|
||||
absolute_base_path = realpath(base_path, NULL);
|
||||
if (absolute_base_path == NULL) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
return safe_mkdir(absolute_base_path, p, mode);
|
||||
}
|
||||
|
||||
int ast_utils_init(void)
|
||||
{
|
||||
dev_urandom_fd = open("/dev/urandom", O_RDONLY);
|
||||
|
Reference in New Issue
Block a user