[Asterisk-code-review] app_voicemail: Refactor email generation functions (asterisk[18])

George Joseph asteriskteam at digium.com
Tue Nov 30 09:40:17 CST 2021


George Joseph has submitted this change. ( https://gerrit.asterisk.org/c/asterisk/+/17564 )

Change subject: app_voicemail: Refactor email generation functions
......................................................................

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
---
M apps/app_minivm.c
M apps/app_voicemail.c
M include/asterisk/file.h
M include/asterisk/utils.h
M main/file.c
M main/utils.c
6 files changed, 193 insertions(+), 300 deletions(-)

Approvals:
  Kevin Harwell: Looks good to me, but someone else must approve
  George Joseph: Looks good to me, approved; Approved for Submit



diff --git a/apps/app_minivm.c b/apps/app_minivm.c
index 8f1064e..69370ef 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 @@
 	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 @@
 	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 @@
 		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 ee7752d..6f909d4 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -529,7 +529,6 @@
 /* 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 @@
 
 #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 @@
 
 */
 
-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 @@
 	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 @@
 
 	/* 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 @@
 	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 @@
 		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 @@
 	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 @@
 		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 7e54426..04fc5bb 100644
--- a/include/asterisk/file.h
+++ b/include/asterisk/file.h
@@ -138,6 +138,15 @@
 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
  * \param filename the name of the file
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index 08120bf..024b6c7 100644
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -336,6 +336,26 @@
  */
 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 a16b6dd..89fcd14 100644
--- a/main/file.c
+++ b/main/file.c
@@ -184,6 +184,21 @@
 	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 f4a026d..dc94c99 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -570,6 +570,150 @@
 	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};

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/17564
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: 18
Gerrit-Change-Id: I1de0ed3483623e9599711129edc817c45ad237ee
Gerrit-Change-Number: 17564
Gerrit-PatchSet: 2
Gerrit-Owner: N A <mail at interlinked.x10host.com>
Gerrit-Reviewer: Friendly Automation
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Kevin Harwell <kharwell at digium.com>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20211130/9badf702/attachment-0001.html>


More information about the asterisk-code-review mailing list