diff options
-rw-r--r-- | apps/app_minivm.c | 121 | ||||
-rw-r--r-- | apps/app_record.c | 113 | ||||
-rw-r--r-- | apps/app_voicemail.c | 94 |
3 files changed, 205 insertions, 123 deletions
diff --git a/apps/app_minivm.c b/apps/app_minivm.c index 9359f82f3..15449ad4e 100644 --- a/apps/app_minivm.c +++ b/apps/app_minivm.c @@ -1232,6 +1232,8 @@ static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const cha * \brief Send voicemail with audio file as an attachment */ static int sendmail(struct minivm_template *template, struct minivm_account *vmu, char *cidnum, char *cidname, const char *filename, char *format, int duration, int attach_user_voicemail, enum mvm_messagetype type, const char *counter) { + RAII_VAR(struct ast_str *, str1, ast_str_create(16), ast_free); + RAII_VAR(struct ast_str *, str2, ast_str_create(16), ast_free); FILE *p = NULL; int pfd; char email[256] = ""; @@ -1241,20 +1243,18 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu char fname[PATH_MAX]; char dur[PATH_MAX]; char tmp[80] = "/tmp/astmail-XXXXXX"; - char tmp2[PATH_MAX]; - char newtmp[PATH_MAX]; /* Only used with volgain */ + char mail_cmd_buffer[PATH_MAX]; + char sox_gain_tmpdir[PATH_MAX] = ""; /* Only used with volgain */ + char *file_to_delete = NULL, *dir_to_delete = NULL; struct timeval now; struct ast_tm tm; struct minivm_zone *the_zone = NULL; - struct ast_channel *ast; - char *finalfilename = ""; - struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16); + struct ast_channel *chan = NULL; char *fromaddress; char *fromemail; + int res; if (!str1 || !str2) { - ast_free(str1); - ast_free(str2); return -1; } @@ -1269,9 +1269,7 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu if (ast_strlen_zero(email)) { ast_log(LOG_WARNING, "No address to send message to.\n"); - ast_free(str1); - ast_free(str2); - return -1; + return -1; } ast_debug(3, "Sending mail to %s@%s - Using template %s\n", vmu->username, vmu->domain, template->name); @@ -1279,35 +1277,30 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu if (!strcmp(format, "wav49")) format = "WAV"; - /* If we have a gain option, process it now with sox */ if (type == MVM_MESSAGE_EMAIL && (vmu->volgain < -.001 || vmu->volgain > .001) ) { - char tmpcmd[PATH_MAX]; - int tmpfd; - - ast_copy_string(newtmp, "/tmp/XXXXXX", sizeof(newtmp)); - ast_debug(3, "newtmp: %s\n", newtmp); - tmpfd = mkstemp(newtmp); - if (tmpfd < 0) { - ast_log(LOG_WARNING, "Failed to create temporary file for volgain: %d\n", errno); - ast_free(str1); - ast_free(str2); + char sox_gain_cmd[PATH_MAX]; + + ast_copy_string(sox_gain_tmpdir, "/tmp/minivm-gain-XXXXXX", sizeof(sox_gain_tmpdir)); + ast_debug(3, "sox_gain_tmpdir: %s\n", sox_gain_tmpdir); + if (!mkdtemp(sox_gain_tmpdir)) { + ast_log(LOG_WARNING, "Failed to create temporary directory for volgain: %d\n", errno); return -1; } - snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, filename, format, newtmp, format); - ast_safe_system(tmpcmd); - close(tmpfd); - finalfilename = newtmp; + snprintf(fname, sizeof(fname), "%s/output.%s", sox_gain_tmpdir, format); + snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s", vmu->volgain, filename, format, fname); + ast_safe_system(sox_gain_cmd); ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", filename, format, vmu->volgain, vmu->username); + + /* Mark some things for deletion */ + file_to_delete = fname; + dir_to_delete = sox_gain_tmpdir; } else { - finalfilename = ast_strdupa(filename); + snprintf(fname, sizeof(fname), "%s.%s", filename, format); } - /* Create file name */ - snprintf(fname, sizeof(fname), "%s.%s", finalfilename, format); - if (template->attachment) - ast_debug(1, "Attaching file '%s', format '%s', uservm is '%d'\n", finalfilename, format, attach_user_voicemail); + ast_debug(1, "Attaching file '%s', format '%s', uservm is '%d'\n", fname, format, attach_user_voicemail); /* Make a temporary file instead of piping directly to sendmail, in case the mail command hangs */ @@ -1322,16 +1315,12 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu } if (!p) { ast_log(LOG_WARNING, "Unable to open temporary file '%s'\n", tmp); - ast_free(str1); - ast_free(str2); - return -1; + goto out; } /* Allocate channel used for chanvar substitution */ - ast = ast_dummy_channel_alloc(); - if (!ast) { - ast_free(str1); - ast_free(str2); - return -1; + chan = ast_dummy_channel_alloc(); + if (!chan) { + goto out; } snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60); @@ -1359,9 +1348,8 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu /* Set date format for voicemail mail */ ast_strftime(date, sizeof(date), template->dateformat, &tm); - /* Populate channel with channel variables for substitution */ - prep_email_sub_vars(ast, vmu, cidnum, cidname, dur, date, counter); + prep_email_sub_vars(chan, vmu, cidnum, cidname, dur, date, counter); /* Find email address to use */ /* If there's a server e-mail address in the account, use that, othterwise template */ @@ -1386,7 +1374,7 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu fprintf(p, "From: Asterisk PBX <%s>\n", who); } else { ast_debug(4, "Fromaddress template: %s\n", fromaddress); - ast_str_substitute_variables(&str1, 0, ast, fromaddress); + ast_str_substitute_variables(&str1, 0, chan, fromaddress); if (check_mime(ast_str_buffer(str1))) { int first_line = 1; char *ptr; @@ -1429,7 +1417,7 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu } if (!ast_strlen_zero(template->subject)) { - ast_str_substitute_variables(&str1, 0, ast, template->subject); + ast_str_substitute_variables(&str1, 0, chan, template->subject); if (check_mime(ast_str_buffer(str1))) { int first_line = 1; char *ptr; @@ -1462,7 +1450,7 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu fprintf(p, "--%s\n", bound); fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", template->charset); if (!ast_strlen_zero(template->body)) { - ast_str_substitute_variables(&str1, 0, ast, template->body); + ast_str_substitute_variables(&str1, 0, chan, template->body); ast_debug(3, "Message now: %s\n-----\n", ast_str_buffer(str1)); fprintf(p, "%s\n", ast_str_buffer(str1)); } else { @@ -1489,14 +1477,45 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu fprintf(p, "\n\n--%s--\n.\n", bound); } fclose(p); - snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", global_mailcmd, tmp, tmp); - ast_safe_system(tmp2); - ast_debug(1, "Sent message to %s with command '%s' - %s\n", vmu->email, global_mailcmd, template->attachment ? "(media attachment)" : ""); - ast_debug(3, "Actual command used: %s\n", tmp2); - ast = ast_channel_unref(ast); - ast_free(str1); - ast_free(str2); - return 0; + + chan = ast_channel_unref(chan); + + if (file_to_delete && dir_to_delete) { + /* We can't delete these files ourselves because the mail command will execute in + the background and we'll end up deleting them out from under it. */ + res = snprintf(mail_cmd_buffer, sizeof(mail_cmd_buffer), + "( %s < %s ; rm -f %s %s ; rmdir %s ) &", + global_mailcmd, tmp, tmp, file_to_delete, dir_to_delete); + } else { + res = snprintf(mail_cmd_buffer, sizeof(mail_cmd_buffer), + "( %s < %s ; rm -f %s ) &", + global_mailcmd, tmp, tmp); + } + + if (res < sizeof(mail_cmd_buffer)) { + file_to_delete = dir_to_delete = NULL; + } else { + ast_log(LOG_ERROR, "Could not send message, command line too long\n"); + res = -1; + goto out; + } + + ast_safe_system(mail_cmd_buffer); + ast_debug(1, "Sent message to %s with command '%s'%s\n", vmu->email, global_mailcmd, template->attachment ? " - (media attachment)" : ""); + ast_debug(3, "Actual command used: %s\n", mail_cmd_buffer); + + res = 0; + +out: + if (file_to_delete) { + unlink(file_to_delete); + } + + if (dir_to_delete) { + rmdir(dir_to_delete); + } + + return res; } /*!\internal 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 <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); diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index f1b8bd1e1..a9b8fe362 100644 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -5370,55 +5370,95 @@ plain_message: static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum) { - char tmpdir[256], newtmp[256]; - char fname[256]; - char tmpcmd[256]; - int tmpfd = -1; - int soxstatus = 0; + char fname[PATH_MAX] = ""; + char *file_to_delete = NULL, *dir_to_delete = NULL; + int res; /* Eww. We want formats to tell us their own MIME type */ - char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-"; + char *mime_type = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-"; + + /* This 'while' loop will only execute once. We use it so that we can 'break' */ + while (vmu->volgain < -.001 || vmu->volgain > .001) { + char tmpdir[PATH_MAX]; + char sox_gain_tmpdir[PATH_MAX]; - if (vmu->volgain < -.001 || vmu->volgain > .001) { create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp"); - snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir); - tmpfd = mkstemp(newtmp); - chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask); - ast_debug(3, "newtmp: %s\n", newtmp); - if (tmpfd > -1) { - snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format); - if ((soxstatus = ast_safe_system(tmpcmd)) == 0) { - attach = newtmp; - ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox); + + res = snprintf(sox_gain_tmpdir, sizeof(sox_gain_tmpdir), "%s/vm-gain-XXXXXX", tmpdir); + if (res >= sizeof(sox_gain_tmpdir)) { + ast_log(LOG_ERROR, "Failed to create temporary directory path %s: Out of buffer space\n", tmpdir); + break; + } + + if (mkdtemp(sox_gain_tmpdir)) { + int soxstatus = 0; + char sox_gain_cmd[PATH_MAX]; + + ast_debug(3, "sox_gain_tmpdir: %s\n", sox_gain_tmpdir); + + /* Save for later */ + dir_to_delete = sox_gain_tmpdir; + + res = snprintf(fname, sizeof(fname), "%s/output.%s", sox_gain_tmpdir, format); + if (res >= sizeof(fname)) { + ast_log(LOG_ERROR, "Failed to create filename buffer for %s/output.%s: Too long\n", sox_gain_tmpdir, format); + break; + } + + res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s", + vmu->volgain, attach, format, fname); + if (res >= sizeof(sox_gain_cmd)) { + ast_log(LOG_ERROR, "Failed to generate sox command, out of buffer space\n"); + break; + } + + soxstatus = ast_safe_system(sox_gain_cmd); + if (!soxstatus) { + /* Save for later */ + file_to_delete = fname; + ast_debug(3, "VOLGAIN: Stored at: %s - Level: %.4f - Mailbox: %s\n", fname, vmu->volgain, mailbox); } else { - ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format, - soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing"); + ast_log(LOG_WARNING, "Sox failed to re-encode %s: %s (have you installed support for all sox file formats?)\n", + fname, + soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing"); ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n"); } } + + break; + } + + if (!file_to_delete) { + res = snprintf(fname, sizeof(fname), "%s.%s", attach, format); + if (res >= sizeof(fname)) { + ast_log(LOG_ERROR, "Failed to create filename buffer for %s.%s: Too long\n", attach, format); + return -1; + } } + fprintf(p, "--%s" ENDL, bound); if (msgnum > -1) - fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename); + fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, mime_type, format, filename); else - fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format); + fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, mime_type, format, greeting_attachment, format); fprintf(p, "Content-Transfer-Encoding: base64" ENDL); fprintf(p, "Content-Description: Voicemail sound attachment." ENDL); if (msgnum > -1) fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename); else fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format); - snprintf(fname, sizeof(fname), "%s.%s", attach, format); base_encode(fname, p); if (last) fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound); - if (tmpfd > -1) { - if (soxstatus == 0) { - unlink(fname); - } - close(tmpfd); - unlink(newtmp); + + if (file_to_delete) { + unlink(file_to_delete); } + + if (dir_to_delete) { + rmdir(dir_to_delete); + } + return 0; } |