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
This commit is contained in:
Naveen Albert 2021-11-01 15:40:42 +00:00 committed by Friendly Automation
parent ecffdab059
commit d374d63ef8
6 changed files with 193 additions and 300 deletions

View File

@ -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);

View 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);

View 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

View 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)

View 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);

View 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};