From 70cdb0f9a88a5463327f58ee061480b1ef151467 Mon Sep 17 00:00:00 2001 From: Naveen Albert <asterisk@phreaknet.org> Date: Mon, 1 Nov 2021 15:40:42 +0000 Subject: [PATCH] app_voicemail: Refactor email generation functions Refactors generic functions used for email generation into utils.c so that they can be used by multiple modules, including app_voicemail and app_minivm, to avoid code duplication. ASTERISK-29715 #close Change-Id: I1de0ed3483623e9599711129edc817c45ad237ee --- apps/app_minivm.c | 142 +--------------------------------- apps/app_voicemail.c | 163 +-------------------------------------- include/asterisk/file.h | 9 +++ include/asterisk/utils.h | 20 +++++ main/file.c | 15 ++++ main/utils.c | 144 ++++++++++++++++++++++++++++++++++ 6 files changed, 193 insertions(+), 300 deletions(-) diff --git a/apps/app_minivm.c b/apps/app_minivm.c index 8f1064e28d..69370ef4c9 100644 --- a/apps/app_minivm.c +++ b/apps/app_minivm.c @@ -161,7 +161,6 @@ #include <dirent.h> #include <locale.h> - #include "asterisk/paths.h" /* use various paths */ #include "asterisk/lock.h" #include "asterisk/file.h" @@ -542,8 +541,6 @@ #define SENDMAIL "/usr/sbin/sendmail -t" #define SOUND_INTRO "vm-intro" -#define B64_BASEMAXINLINE 256 /*!< Buffer size for Base 64 attachment encoding */ -#define B64_BASELINELEN 72 /*!< Line length for Base 64 encoded messages */ #define EOL "\r\n" #define MAX_DATETIME_FORMAT 512 @@ -659,15 +656,6 @@ struct leave_vm_options { signed char record_gain; }; -/*! \brief Structure for base64 encoding */ -struct b64_baseio { - int iocp; - int iolen; - int linelength; - int ateof; - unsigned char iobuf[B64_BASEMAXINLINE]; -}; - /*! \brief Voicemail time zones */ struct minivm_zone { char name[80]; /*!< Name of this time zone */ @@ -853,134 +841,6 @@ static void message_destroy_list(void) AST_LIST_UNLOCK(&message_templates); } -/*!\internal - * \brief read buffer from file (base64 conversion) */ -static int b64_inbuf(struct b64_baseio *bio, FILE *fi) -{ - int l; - - if (bio->ateof) - return 0; - - if ((l = fread(bio->iobuf, 1, B64_BASEMAXINLINE, fi)) != B64_BASEMAXINLINE) { - bio->ateof = 1; - if (l == 0) { - /* Assume EOF */ - return 0; - } - } - - bio->iolen = l; - bio->iocp = 0; - - return 1; -} - -/*!\internal - * \brief read character from file to buffer (base64 conversion) */ -static int b64_inchar(struct b64_baseio *bio, FILE *fi) -{ - if (bio->iocp >= bio->iolen) { - if (!b64_inbuf(bio, fi)) - return EOF; - } - - return bio->iobuf[bio->iocp++]; -} - -/*!\internal - * \brief write buffer to file (base64 conversion) */ -static int b64_ochar(struct b64_baseio *bio, int c, FILE *so) -{ - if (bio->linelength >= B64_BASELINELEN) { - if (fputs(EOL,so) == EOF) - return -1; - - bio->linelength= 0; - } - - if (putc(((unsigned char) c), so) == EOF) - return -1; - - bio->linelength++; - - return 1; -} - -/*!\internal - * \brief Encode file to base64 encoding for email attachment (base64 conversion) */ -static int base_encode(char *filename, FILE *so) -{ - unsigned char dtable[B64_BASEMAXINLINE]; - int i,hiteof= 0; - FILE *fi; - struct b64_baseio bio; - - memset(&bio, 0, sizeof(bio)); - bio.iocp = B64_BASEMAXINLINE; - - if (!(fi = fopen(filename, "rb"))) { - ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno)); - return -1; - } - - for (i= 0; i<9; i++) { - dtable[i]= 'A'+i; - dtable[i+9]= 'J'+i; - dtable[26+i]= 'a'+i; - dtable[26+i+9]= 'j'+i; - } - for (i= 0; i < 8; i++) { - dtable[i+18]= 'S'+i; - dtable[26+i+18]= 's'+i; - } - for (i= 0; i < 10; i++) { - dtable[52+i]= '0'+i; - } - dtable[62]= '+'; - dtable[63]= '/'; - - while (!hiteof){ - unsigned char igroup[3], ogroup[4]; - int c,n; - - igroup[0]= igroup[1]= igroup[2]= 0; - - for (n= 0; n < 3; n++) { - if ((c = b64_inchar(&bio, fi)) == EOF) { - hiteof= 1; - break; - } - igroup[n]= (unsigned char)c; - } - - if (n> 0) { - ogroup[0]= dtable[igroup[0]>>2]; - ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)]; - ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)]; - ogroup[3]= dtable[igroup[2]&0x3F]; - - if (n<3) { - ogroup[3]= '='; - - if (n<2) - ogroup[2]= '='; - } - - for (i= 0;i<4;i++) - b64_ochar(&bio, ogroup[i], so); - } - } - - /* Put end of line - line feed */ - if (fputs(EOL, so) == EOF) - return 0; - - fclose(fi); - - return 1; -} - static int get_date(char *s, int len) { struct ast_tm tm; @@ -1481,7 +1341,7 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu fprintf(p, "Content-Description: Voicemail sound attachment.\n"); fprintf(p, "Content-Disposition: attachment; filename=\"voicemail%s.%s\"\n\n", counter ? counter : "", format); - base_encode(fname, p); + ast_base64_encode_file_path(fname, p, EOL); fprintf(p, "\n\n--%s--\n.\n", bound); } fclose(p); diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index ee7752ddf8..6f909d44e3 100644 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -529,7 +529,6 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate); /* Default mail command to mail voicemail. Change it with the * mailcmd= command in voicemail.conf */ #define SENDMAIL "/usr/sbin/sendmail -t" - #define INTRO "vm-intro" #define MAX_MAIL_BODY_CONTENT_SIZE 134217728L // 128 Mbyte @@ -539,8 +538,6 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate); #define MINPASSWORD 0 /*!< Default minimum mailbox password length */ -#define BASELINELEN 72 -#define BASEMAXINLINE 256 #ifdef IMAP_STORAGE #define ENDL "\r\n" #else @@ -744,14 +741,6 @@ and vm-Old are spelled plural, to make them sound more as folder name than an ad */ -struct baseio { - int iocp; - int iolen; - int linelength; - int ateof; - unsigned char iobuf[BASEMAXINLINE]; -}; - #define MAX_VM_MBOX_ID_LEN (AST_MAX_EXTENSION) #define MAX_VM_CONTEXT_LEN (AST_MAX_CONTEXT) /* MAX_VM_MAILBOX_LEN allows enough room for the '@' and NULL terminator */ @@ -1928,22 +1917,6 @@ static int make_file(char *dest, const int len, const char *dir, const int num) return snprintf(dest, len, "%s/msg%04d", dir, num); } -/* same as mkstemp, but return a FILE * */ -static FILE *vm_mkftemp(char *template) -{ - FILE *p = NULL; - int pfd = mkstemp(template); - chmod(template, VOICEMAIL_FILE_MODE & ~my_umask); - if (pfd > -1) { - p = fdopen(pfd, "w+"); - if (!p) { - close(pfd); - pfd = -1; - } - } - return p; -} - /*! \brief basically mkdir -p $dest/$context/$ext/$folder * \param dest String. base directory. * \param len Length of dest. @@ -2697,7 +2670,7 @@ static int imap_store_file(const char *dir, const char *mailboxuser, const char /* Make a temporary file instead of piping directly to sendmail, in case the mail command hangs. */ - if (!(p = vm_mkftemp(tmp))) { + if (!(p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask))) { ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn); if (tempcopy) { ast_free(vmu->email); @@ -4771,134 +4744,6 @@ static int vm_delete(char *file) return ast_filedelete(file, NULL); } -/*! - * \brief utility used by inchar(), for base_encode() - */ -static int inbuf(struct baseio *bio, FILE *fi) -{ - int l; - - if (bio->ateof) - return 0; - - if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) != BASEMAXINLINE) { - bio->ateof = 1; - if (l == 0) { - /* Assume EOF */ - return 0; - } - } - - bio->iolen = l; - bio->iocp = 0; - - return 1; -} - -/*! - * \brief utility used by base_encode() - */ -static int inchar(struct baseio *bio, FILE *fi) -{ - if (bio->iocp>=bio->iolen) { - if (!inbuf(bio, fi)) - return EOF; - } - - return bio->iobuf[bio->iocp++]; -} - -/*! - * \brief utility used by base_encode() - */ -static int ochar(struct baseio *bio, int c, FILE *so) -{ - if (bio->linelength >= BASELINELEN) { - if (fputs(ENDL, so) == EOF) { - return -1; - } - - bio->linelength = 0; - } - - if (putc(((unsigned char) c), so) == EOF) { - return -1; - } - - bio->linelength++; - - return 1; -} - -/*! - * \brief Performs a base 64 encode algorithm on the contents of a File - * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode. - * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename. - * - * TODO: shouldn't this (and the above 3 support functions) be put into some kind of external utility location, such as funcs/func_base64.c ? - * - * \return zero on success, -1 on error. - */ -static int base_encode(char *filename, FILE *so) -{ - static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', - 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', - '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; - int i, hiteof = 0; - FILE *fi; - struct baseio bio; - - memset(&bio, 0, sizeof(bio)); - bio.iocp = BASEMAXINLINE; - - if (!(fi = fopen(filename, "rb"))) { - ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno)); - return -1; - } - - while (!hiteof){ - unsigned char igroup[3], ogroup[4]; - int c, n; - - memset(igroup, 0, sizeof(igroup)); - - for (n = 0; n < 3; n++) { - if ((c = inchar(&bio, fi)) == EOF) { - hiteof = 1; - break; - } - - igroup[n] = (unsigned char) c; - } - - if (n > 0) { - ogroup[0]= dtable[igroup[0] >> 2]; - ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)]; - ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)]; - ogroup[3]= dtable[igroup[2] & 0x3F]; - - if (n < 3) { - ogroup[3] = '='; - - if (n < 2) - ogroup[2] = '='; - } - - for (i = 0; i < 4; i++) - ochar(&bio, ogroup[i], so); - } - } - - fclose(fi); - - if (fputs(ENDL, so) == EOF) { - return 0; - } - - return 1; -} - static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, const char *category, const char *flag) { char callerid[256]; @@ -5509,7 +5354,7 @@ static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename); else fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format); - base_encode(fname, p); + ast_base64_encode_file_path(fname, p, ENDL); if (last) fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound); @@ -5562,7 +5407,7 @@ static int sendmail(char *srcemail, ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %u\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH)); /* Make a temporary file instead of piping directly to sendmail, in case the mail command hangs */ - if ((p = vm_mkftemp(tmp)) == NULL) { + if ((p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask)) == NULL) { ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd); return -1; } else { @@ -5601,7 +5446,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname)); } - if ((p = vm_mkftemp(tmp)) == NULL) { + if ((p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask)) == NULL) { ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd); ast_free(str1); ast_free(str2); diff --git a/include/asterisk/file.h b/include/asterisk/file.h index 7e5442626d..04fc5bbcc6 100644 --- a/include/asterisk/file.h +++ b/include/asterisk/file.h @@ -137,6 +137,15 @@ int ast_filedelete(const char *filename, const char *fmt); */ int ast_filecopy(const char *oldname, const char *newname, const char *fmt); +/*! + * \brief same as mkstemp, but return a FILE + * \param template The template for the unique file name to generate. Modified in place to return the file name. + * \param mode The mode for file permissions + * + * \return FILE handle to the temporary file on success or NULL if creation failed + */ +FILE *ast_file_mkftemp(char *template, mode_t mode); + /*! * \brief Callback called for each file found when reading directories * \param dir_name the name of the directory diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h index 08120bf220..024b6c7a12 100644 --- a/include/asterisk/utils.h +++ b/include/asterisk/utils.h @@ -336,6 +336,26 @@ char *ast_base64url_decode_string(const char *src); */ char *ast_base64url_encode_string(const char *src); +/*! + * \brief Performs a base 64 encode algorithm on the contents of a File + * \param inputfile A FILE handle to the input file to be encoded. Must be readable. This handle is not automatically closed. + * \param outputfile A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename. + * \param endl The line ending to use (e.g. either "\n" or "\r\n") + * + * \return zero on success, -1 on error. + */ +int ast_base64_encode_file(FILE *inputfile, FILE *outputfile, const char *endl); + +/*! + * \brief Performs a base 64 encode algorithm on the contents of a File + * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode. + * \param outputfile A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename. + * \param endl The line ending to use (e.g. either "\n" or "\r\n") + * + * \return zero on success, -1 on error. + */ +int ast_base64_encode_file_path(const char *filename, FILE *outputfile, const char *endl); + #define AST_URI_ALPHANUM (1 << 0) #define AST_URI_MARK (1 << 1) #define AST_URI_UNRESERVED (AST_URI_ALPHANUM | AST_URI_MARK) diff --git a/main/file.c b/main/file.c index a16b6dd2b0..89fcd14927 100644 --- a/main/file.c +++ b/main/file.c @@ -184,6 +184,21 @@ int ast_format_def_unregister(const char *name) return res; } +FILE *ast_file_mkftemp(char *template, mode_t mode) +{ + FILE *p = NULL; + int pfd = mkstemp(template); + chmod(template, mode); + if (pfd > -1) { + p = fdopen(pfd, "w+"); + if (!p) { + close(pfd); + pfd = -1; + } + } + return p; +} + int ast_stopstream(struct ast_channel *tmp) { ast_channel_lock(tmp); diff --git a/main/utils.c b/main/utils.c index f4a026d420..dc94c994d1 100644 --- a/main/utils.c +++ b/main/utils.c @@ -570,6 +570,150 @@ static void base64_init(void) b2a_url[(int)'_'] = 63; } +#define BASELINELEN 72 /*!< Line length for Base 64 encoded messages */ +#define BASEMAXINLINE 256 /*!< Buffer size for Base 64 attachment encoding */ + +/*! \brief Structure used for base64 encoding */ +struct baseio { + int iocp; + int iolen; + int linelength; + int ateof; + unsigned char iobuf[BASEMAXINLINE]; +}; + +/*! + * \brief utility used by inchar(), for base_encode() + */ +static int inbuf(struct baseio *bio, FILE *fi) +{ + int l; + + if (bio->ateof) { + return 0; + } + + if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) != BASEMAXINLINE) { + bio->ateof = 1; + if (l == 0) { + /* Assume EOF */ + return 0; + } + } + + bio->iolen = l; + bio->iocp = 0; + + return 1; +} + +/*! + * \brief utility used by base_encode() + */ +static int inchar(struct baseio *bio, FILE *fi) +{ + if (bio->iocp >= bio->iolen) { + if (!inbuf(bio, fi)) { + return EOF; + } + } + + return bio->iobuf[bio->iocp++]; +} + +/*! + * \brief utility used by base_encode() + */ +static int ochar(struct baseio *bio, int c, FILE *so, const char *endl) +{ + if (bio->linelength >= BASELINELEN) { + if (fputs(endl, so) == EOF) { + return -1; + } + + bio->linelength = 0; + } + + if (putc(((unsigned char) c), so) == EOF) { + return -1; + } + + bio->linelength++; + + return 1; +} + +int ast_base64_encode_file(FILE *inputfile, FILE *outputfile, const char *endl) +{ + static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', + 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', + '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; + int i, hiteof = 0; + struct baseio bio; + + memset(&bio, 0, sizeof(bio)); + bio.iocp = BASEMAXINLINE; + + while (!hiteof){ + unsigned char igroup[3], ogroup[4]; + int c, n; + + memset(igroup, 0, sizeof(igroup)); + + for (n = 0; n < 3; n++) { + if ((c = inchar(&bio, inputfile)) == EOF) { + hiteof = 1; + break; + } + + igroup[n] = (unsigned char) c; + } + + if (n > 0) { + ogroup[0]= dtable[igroup[0] >> 2]; + ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)]; + ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)]; + ogroup[3]= dtable[igroup[2] & 0x3F]; + + if (n < 3) { + ogroup[3] = '='; + + if (n < 2) { + ogroup[2] = '='; + } + } + + for (i = 0; i < 4; i++) { + ochar(&bio, ogroup[i], outputfile, endl); + } + } + } + + if (fputs(endl, outputfile) == EOF) { + return 0; + } + + return 1; +} + +int ast_base64_encode_file_path(const char *filename, FILE *outputfile, const char *endl) +{ + FILE *fi; + int res; + + if (!(fi = fopen(filename, "rb"))) { + ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno)); + return -1; + } + + res = ast_base64_encode_file(fi, outputfile, endl); + + fclose(fi); + + return res; +} + const struct ast_flags ast_uri_http = {AST_URI_UNRESERVED}; const struct ast_flags ast_uri_http_legacy = {AST_URI_LEGACY_SPACE | AST_URI_UNRESERVED}; const struct ast_flags ast_uri_sip_user = {AST_URI_UNRESERVED | AST_URI_SIP_USER_UNRESERVED}; -- GitLab