From 43670e471fecefb9fbd150b91cc1f69ec4ce2083 Mon Sep 17 00:00:00 2001 From: Sean Bright Date: Fri, 25 Aug 2017 13:20:16 -0400 Subject: app_record: Resolve some absolute vs. relative filename bugs If the Record() application is called with a relative filename that includes directories, we were not properly creating the intermediate directories and Record() would fail. Secondarily, updated the documentation for RECORDED_FILE to mention that it does not include a filename extension. Finally, rewrote the '%d' functionality to be a bit more straight forward and less noisy. ASTERISK-16777 #close Reported by: klaus3000 Change-Id: Ibc2640cba3a8c7f17d97b02f76b7608b1e7ffde2 --- apps/app_record.c | 113 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 45 deletions(-) diff --git a/apps/app_record.c b/apps/app_record.c index 0b85ff8a6..8c3a577ef 100644 --- a/apps/app_record.c +++ b/apps/app_record.c @@ -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 @@ -102,7 +103,7 @@ If the user hangs up during a recording, all data will be lost and the application will terminate. - Will be set to the final filename of the recording. + Will be set to the final filename of the recording, without an extension. This is the final status of the command @@ -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); -- cgit v1.2.3