<p>N A has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/16076">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">app_mail: New SendMail application<br><br>Adds a mail application to Asterisk which invokes<br>the system mailer with configurable addressing info<br>and support for multiple attachments.<br><br>ASTERISK-29489<br><br>Change-Id: I82a7af95364382ac50edb4dd4660873e5f89d3dc<br>---<br>A apps/app_mail.c<br>A doc/CHANGES-staging/app_mail.txt<br>2 files changed, 478 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/76/16076/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_mail.c b/apps/app_mail.c</span><br><span>new file mode 100644</span><br><span>index 0000000..5f96fe7</span><br><span>--- /dev/null</span><br><span>+++ b/apps/app_mail.c</span><br><span>@@ -0,0 +1,471 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Naveen Albert</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Naveen Albert <asterisk@phreaknet.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Send email application</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \author Naveen Albert <asterisk@phreaknet.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \ingroup applications</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>extended</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/stat.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/logger.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/channel.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/pbx.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/app.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** DOCUMENTATION</span><br><span style="color: hsl(120, 100%, 40%);">+ <application name="SendMail" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Sends an email using the system mailer. The recipient and sender info</span><br><span style="color: hsl(120, 100%, 40%);">+ can be customized per invocation, and multiple attachments can be sent.</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="recipient" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Pipe-separated list of email addresses.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="subject" required="true" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="body" required="true" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="options" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+ <optionlist></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="A"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Pipe-separated list of attachments, using the full path. The</span><br><span style="color: hsl(120, 100%, 40%);">+ file name will be used as the name of the attachment.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="d"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Delete successfully attached files once the email is sent.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="f"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Email address from which the email should be sent.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="F"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Name from which the email should be sent.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="R"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Name of the recipient.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ </optionlist></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Sends an email using the system mailer.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Sets <variable>MAILSTATUS</variable> to one of the following values:</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <variablelist></span><br><span style="color: hsl(120, 100%, 40%);">+ <variable name="MAILSTATUS"></span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="SUCCESS"></span><br><span style="color: hsl(120, 100%, 40%);">+ Email was successfully sent. This does not necessarily mean delivery</span><br><span style="color: hsl(120, 100%, 40%);">+ will be successful or that the email will not bounce.</span><br><span style="color: hsl(120, 100%, 40%);">+ </value></span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="FAILURE"></span><br><span style="color: hsl(120, 100%, 40%);">+ Email was not successfully sent.</span><br><span style="color: hsl(120, 100%, 40%);">+ </value></span><br><span style="color: hsl(120, 100%, 40%);">+ </variable></span><br><span style="color: hsl(120, 100%, 40%);">+ </variablelist></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </application></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *app = "SendMail";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define VOICEMAIL_FILE_MODE 0666</span><br><span style="color: hsl(120, 100%, 40%);">+#define BASELINELEN 72</span><br><span style="color: hsl(120, 100%, 40%);">+#define BASEMAXINLINE 256</span><br><span style="color: hsl(120, 100%, 40%);">+#define SENDMAIL "/usr/sbin/sendmail -t"</span><br><span style="color: hsl(120, 100%, 40%);">+#define ENDL "\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int my_umask;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct baseio {</span><br><span style="color: hsl(120, 100%, 40%);">+ int iocp;</span><br><span style="color: hsl(120, 100%, 40%);">+ int iolen;</span><br><span style="color: hsl(120, 100%, 40%);">+ int linelength;</span><br><span style="color: hsl(120, 100%, 40%);">+ int ateof;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned char iobuf[BASEMAXINLINE];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief utility used by inchar(), for base_encode()</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int inbuf(struct baseio *bio, FILE *fi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int l;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (bio->ateof)</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) != BASEMAXINLINE) {</span><br><span style="color: hsl(120, 100%, 40%);">+ bio->ateof = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (l == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Assume EOF */</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ bio->iolen = l;</span><br><span style="color: hsl(120, 100%, 40%);">+ bio->iocp = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief utility used by base_encode()</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int inchar(struct baseio *bio, FILE *fi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (bio->iocp>=bio->iolen) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!inbuf(bio, fi))</span><br><span style="color: hsl(120, 100%, 40%);">+ return EOF;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return bio->iobuf[bio->iocp++];</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief utility used by base_encode()</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int ochar(struct baseio *bio, int c, FILE *so)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (bio->linelength >= BASELINELEN) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (fputs(ENDL, so) == EOF) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ bio->linelength = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (putc(((unsigned char) c), so) == EOF) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ bio->linelength++;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Performs a base 64 encode algorithm on the contents of a File</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * 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 ?</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return zero on success, -1 on error.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int base_encode(char *filename, FILE *so)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',</span><br><span style="color: hsl(120, 100%, 40%);">+ 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',</span><br><span style="color: hsl(120, 100%, 40%);">+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',</span><br><span style="color: hsl(120, 100%, 40%);">+ '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};</span><br><span style="color: hsl(120, 100%, 40%);">+ int i, hiteof = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ FILE *fi;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct baseio bio;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ memset(&bio, 0, sizeof(bio));</span><br><span style="color: hsl(120, 100%, 40%);">+ bio.iocp = BASEMAXINLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(fi = fopen(filename, "rb"))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while (!hiteof){</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned char igroup[3], ogroup[4];</span><br><span style="color: hsl(120, 100%, 40%);">+ int c, n;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ memset(igroup, 0, sizeof(igroup));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (n = 0; n < 3; n++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((c = inchar(&bio, fi)) == EOF) {</span><br><span style="color: hsl(120, 100%, 40%);">+ hiteof = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ igroup[n] = (unsigned char) c;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (n > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ogroup[0]= dtable[igroup[0] >> 2];</span><br><span style="color: hsl(120, 100%, 40%);">+ ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];</span><br><span style="color: hsl(120, 100%, 40%);">+ ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];</span><br><span style="color: hsl(120, 100%, 40%);">+ ogroup[3]= dtable[igroup[2] & 0x3F];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (n < 3) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ogroup[3] = '=';</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (n < 2)</span><br><span style="color: hsl(120, 100%, 40%);">+ ogroup[2] = '=';</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < 4; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+ ochar(&bio, ogroup[i], so);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ fclose(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (fputs(ENDL, so) == EOF) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* same as mkstemp, but return a FILE * */</span><br><span style="color: hsl(120, 100%, 40%);">+static FILE *vm_mkftemp(char *template)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ FILE *p = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ int pfd = mkstemp(template);</span><br><span style="color: hsl(120, 100%, 40%);">+ chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pfd > -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ p = fdopen(pfd, "w+");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!p) {</span><br><span style="color: hsl(120, 100%, 40%);">+ close(pfd);</span><br><span style="color: hsl(120, 100%, 40%);">+ pfd = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return p;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void make_email_file(FILE *p, char *subject, char *body, char *toaddress, char *toname, char *fromaddress, char *fromname, char *attachments, int delete)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_tm tm;</span><br><span style="color: hsl(120, 100%, 40%);">+ char date[256];</span><br><span style="color: hsl(120, 100%, 40%);">+ char host[MAXHOSTNAMELEN] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ char who[256];</span><br><span style="color: hsl(120, 100%, 40%);">+ char bound[256];</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *str2 = ast_str_create(16);</span><br><span style="color: hsl(120, 100%, 40%);">+ char filename[256];</span><br><span style="color: hsl(120, 100%, 40%);">+ char *emailsbuf, *email, *attachmentsbuf, *attachment;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval when = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!str2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(str2);</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ gethostname(host, sizeof(host) - 1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strchr(toaddress, '@')) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(who, toaddress, sizeof(who));</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(who, sizeof(who), "%s@%s", toaddress, host);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", ast_localtime(&when, &tm, NULL));</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, "Date: %s" ENDL, date);</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, "From: %s <%s>" ENDL, fromname, fromaddress);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ emailsbuf = ast_strdupa(toaddress);</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, "To:");</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((email = strsep(&emailsbuf, "|"))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ char *next = emailsbuf;</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, " \"%s\" <%s>%s" ENDL, toname, email, next ? "," : "");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(str2);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, "Subject: %s" ENDL, subject);</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, "Message-ID: <Asterisk-%u-%d@%s>" ENDL, (unsigned int) ast_random(), (int) getpid(), host);</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, "MIME-Version: 1.0" ENDL);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(attachments)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Something unique. */</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(bound, sizeof(bound), "----attachment_%d%u", (int) getpid(), (unsigned int) ast_random());</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, "--%s" ENDL, bound);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, "ISO-8859-1");</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, "%s", body);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(attachments)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ attachmentsbuf = ast_strdupa(attachments);</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((attachment = strsep(&attachmentsbuf, "|"))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(filename, sizeof(filename), "%s", basename(attachment));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_file_is_readable(attachment)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(AST_LOG_WARNING, "Failed to open file: %s\n", attachment);</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(5, "Creating attachment: %s\n", filename);</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, ENDL "--%s" ENDL, bound);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Eww. We want formats to tell us their own MIME type */</span><br><span style="color: hsl(120, 100%, 40%);">+ //char *mime_type = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ //fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, mime_type, format, filename);</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, "Content-Transfer-Encoding: base64" ENDL);</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, "Content-Description: File attachment." ENDL);</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);</span><br><span style="color: hsl(120, 100%, 40%);">+ base_encode(attachment, p);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (delete) {</span><br><span style="color: hsl(120, 100%, 40%);">+ unlink(attachment);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound); /* After the last attachment */</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum {</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_ATTACHMENTS = (1 << 0),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_FROM_ADDRESS = (1 << 1),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_FROM_NAME = (1 << 2),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_TO_NAME = (1 << 3),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_DELETE_ATTACHMENTS = (1 << 4),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum {</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_ARG_ATTACHMENTS,</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_ARG_FROM_ADDRESS,</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_ARG_FROM_NAME,</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_ARG_TO_NAME,</span><br><span style="color: hsl(120, 100%, 40%);">+ /* note: this entry _MUST_ be the last one in the enum */</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_ARG_ARRAY_SIZE,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_APP_OPTIONS(mail_exec_options, BEGIN_OPTIONS</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION_ARG('A', OPT_ATTACHMENTS, OPT_ARG_ATTACHMENTS),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('d', OPT_DELETE_ATTACHMENTS),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION_ARG('f', OPT_FROM_ADDRESS, OPT_ARG_FROM_ADDRESS),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION_ARG('F', OPT_FROM_NAME, OPT_ARG_FROM_NAME),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION_ARG('R', OPT_TO_NAME, OPT_ARG_TO_NAME),</span><br><span style="color: hsl(120, 100%, 40%);">+END_OPTIONS);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int mail_exec(struct ast_channel *chan, const char *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char *parse;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *toaddress, *subject, *body;</span><br><span style="color: hsl(120, 100%, 40%);">+ FILE *p = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ char tmp[80] = "/tmp/astmail-XXXXXX";</span><br><span style="color: hsl(120, 100%, 40%);">+ char tmp2[256];</span><br><span style="color: hsl(120, 100%, 40%);">+ char *mailcmd = SENDMAIL; /* Mail command */</span><br><span style="color: hsl(120, 100%, 40%);">+ char *fromaddress = "", *toname = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ char *fromname = "Asterisk PBX";</span><br><span style="color: hsl(120, 100%, 40%);">+ char *attachments = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ int delete = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(recipient);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(subject);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(body);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(options);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags64 opts = { 0, };</span><br><span style="color: hsl(120, 100%, 40%);">+ char *opt_args[OPT_ARG_ARRAY_SIZE];</span><br><span style="color: hsl(120, 100%, 40%);">+ parse = ast_strdupa(data);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(args, parse);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (args.argc < 3) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Incorrect number of arguments\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "MAILSTATUS", "FAILURE");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.recipient)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_NOTICE, "Mail requires a recipient\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "MAILSTATUS", "FAILURE");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.subject)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_NOTICE, "Mail requires a subject\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "MAILSTATUS", "FAILURE");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(args.options) &&</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_parse_options64(mail_exec_options, &opts, opt_args, args.options)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Invalid options: '%s'\n", args.options);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "MAILSTATUS", "FAILURE");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ toaddress = args.recipient;</span><br><span style="color: hsl(120, 100%, 40%);">+ subject = args.subject;</span><br><span style="color: hsl(120, 100%, 40%);">+ body = args.body;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag64(&opts, OPT_ATTACHMENTS)</span><br><span style="color: hsl(120, 100%, 40%);">+ && !ast_strlen_zero(opt_args[OPT_ARG_ATTACHMENTS])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ attachments = opt_args[OPT_ARG_ATTACHMENTS];</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag64(&opts, OPT_FROM_ADDRESS)</span><br><span style="color: hsl(120, 100%, 40%);">+ && !ast_strlen_zero(opt_args[OPT_ARG_FROM_ADDRESS])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ fromaddress = opt_args[OPT_ARG_FROM_ADDRESS];</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Must specify from address\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "MAILSTATUS", "FAILURE");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag64(&opts, OPT_FROM_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+ && !ast_strlen_zero(opt_args[OPT_ARG_FROM_NAME])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ fromname = opt_args[OPT_ARG_FROM_NAME];</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag64(&opts, OPT_TO_NAME)</span><br><span style="color: hsl(120, 100%, 40%);">+ && !ast_strlen_zero(opt_args[OPT_ARG_TO_NAME])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ toname = opt_args[OPT_ARG_TO_NAME];</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag64(&opts, OPT_DELETE_ATTACHMENTS)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ delete = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Make a temporary file instead of piping directly to sendmail, in case the mail</span><br><span style="color: hsl(120, 100%, 40%);">+ command hangs */</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((p = vm_mkftemp(tmp)) == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "MAILSTATUS", "FAILURE");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ make_email_file(p, subject, body, toaddress, toname, fromaddress, fromname, attachments, delete);</span><br><span style="color: hsl(120, 100%, 40%);">+ fclose(p);</span><br><span style="color: hsl(120, 100%, 40%);">+ //snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(tmp2, sizeof(tmp2), "( %s < %s ; cp %s /tmp/test.txt ) &", mailcmd, tmp, tmp);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_safe_system(tmp2);</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((res < 0) && (errno != ECHILD)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to execute '%s'\n", (char *)data);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "MAILSTATUS", "FAILURE");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (res == 127) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to execute '%s'\n", (char *)data);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "MAILSTATUS", "FAILURE");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "MAILSTATUS", "FAILURE");</span><br><span style="color: hsl(120, 100%, 40%);">+ else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Sent mail to %s with command '%s'\n", toaddress, mailcmd);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "MAILSTATUS", "SUCCESS");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autoservice_stop(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_unregister_application(app);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ my_umask = umask(0);</span><br><span style="color: hsl(120, 100%, 40%);">+ umask(my_umask);</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_register_application_xml(app, mail_exec);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Sends an email");</span><br><span>diff --git a/doc/CHANGES-staging/app_mail.txt b/doc/CHANGES-staging/app_mail.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..155a674</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/app_mail.txt</span><br><span>@@ -0,0 +1,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: app_mail</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Adds a mail application to Asterisk which</span><br><span style="color: hsl(120, 100%, 40%);">+invokes the system mailer with configurable</span><br><span style="color: hsl(120, 100%, 40%);">+addressing info and support for multiple</span><br><span style="color: hsl(120, 100%, 40%);">+attachments.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/16076">change 16076</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/16076"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I82a7af95364382ac50edb4dd4660873e5f89d3dc </div>
<div style="display:none"> Gerrit-Change-Number: 16076 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: N A <mail@interlinked.x10host.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>