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