]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
app_voicemail: Refactor email generation functions
authorNaveen Albert <asterisk@phreaknet.org>
Mon, 1 Nov 2021 15:40:42 +0000 (15:40 +0000)
committerGeorge Joseph <gjoseph@digium.com>
Tue, 30 Nov 2021 15:40:14 +0000 (09:40 -0600)
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
apps/app_voicemail.c
include/asterisk/file.h
include/asterisk/utils.h
main/file.c
main/utils.c

index 8f1064e28d50a56dbc7e6b612c64bf566590500a..69370ef4c9ee243803869b61c67c337ebc440ba2 100644 (file)
 #include <dirent.h>
 #include <locale.h>
 
-
 #include "asterisk/paths.h"    /* use various paths */
 #include "asterisk/lock.h"
 #include "asterisk/file.h"
 #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);
index ee7752ddf8a0d75833f70a370b5ea1982348b5da..6f909d44e3ab853fb6d7c6f922fb9fe072226600 100644 (file)
@@ -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);
index 7e5442626d19e7870eb36ab958b0abe9b784bcdd..04fc5bbcc66518c728204ce62520501d068ae421 100644 (file)
@@ -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
index 08120bf2204c4ba0238fd086252f814b1aac8e5c..024b6c7a12c588ab24c17b336ddda6ed151ece52 100644 (file)
@@ -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)
index a16b6dd2b0178ea895922f6f01791ca50d9965ea..89fcd149277a31768f6ac4df967ac47224b6b45a 100644 (file)
@@ -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);
index f4a026d4207ce54ec22f1f5f9fa328451b803dd7..dc94c994d140748144d77627c9a4c46a4eea9568 100644 (file)
@@ -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};