diff options
author | David M. Lee <dlee@digium.com> | 2013-07-03 17:58:45 +0000 |
---|---|---|
committer | David M. Lee <dlee@digium.com> | 2013-07-03 17:58:45 +0000 |
commit | a75fd32212c35b41143442bd757387fad636177a (patch) | |
tree | 461033acf36f4596d8fc9800a1195e12207b3ea2 /main | |
parent | c4adaf91067559dd5aa90577e181693abade0602 (diff) |
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
Diffstat (limited to 'main')
-rw-r--r-- | main/app.c | 27 | ||||
-rw-r--r-- | main/asterisk.c | 4 | ||||
-rw-r--r-- | main/channel.c | 9 | ||||
-rw-r--r-- | main/file.c | 3 | ||||
-rw-r--r-- | main/utils.c | 94 |
5 files changed, 131 insertions, 6 deletions
diff --git a/main/app.c b/main/app.c index a7a9029c9..031f6f28f 100644 --- a/main/app.c +++ b/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 */ diff --git a/main/asterisk.c b/main/asterisk.c index c1ce2c0f1..aa33f31e4 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -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) diff --git a/main/channel.c b/main/channel.c index 9d1ec69c2..3bc5c0a75 100644 --- a/main/channel.c +++ b/main/channel.c @@ -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); diff --git a/main/file.c b/main/file.c index 016afd197..cb495b310 100644 --- a/main/file.c +++ b/main/file.c @@ -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; diff --git a/main/utils.c b/main/utils.c index 208a4d326..04f612703 100644 --- a/main/utils.c +++ b/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); |