chiark / gitweb /
Shun time(), since on Linux it is not monotonic with gettimeofday().
[disorder] / lib / sendmail.c
index 95ad2a1..a01b9d3 100644 (file)
@@ -2,26 +2,28 @@
  * This file is part of DisOrder
  * Copyright (C) 2007-2008 Richard Kettlewell
  *
- * This program is free software; you can redistribute it and/or modify
+ * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
+/** @file lib/sendmail.c
+ * @brief Sending email
+ *
+ * Can send email either via a local MTA or by connecting to an SMTP TCP port.
+ * The former is preferred as there is a strong implication that the local MTA
+ * will queue for you than some random TCP SMTP server you found.
+ */
+#include "common.h"
 
-#include <config.h>
-#include "types.h"
-
-#include <stdio.h>
 #include <errno.h>
 #include <stdarg.h>
 #include <sys/socket.h>
@@ -39,6 +41,7 @@
 #include "hostname.h"
 #include "sendmail.h"
 #include "base64.h"
+#include "wstat.h"
 
 /** @brief Get a server response
  * @param tag Server name
@@ -123,7 +126,7 @@ static int sendmailfp(const char *tag, FILE *in, FILE *out,
   time_t now;
   char date[128];
 
-  time(&now);
+  xtime(&now);
   gmtime_r(&now, &ut);
   strftime(date, sizeof date, "%a, %d %b %Y %H:%M:%S +0000", &ut);
   gcry_create_nonce(idbuf, sizeof idbuf);
@@ -208,33 +211,55 @@ int sendmail(const char *sender,
   char *tag;
   int fdin, fdout, rc;
   FILE *in, *out;
+  pid_t pid = -1;
    
   static const struct addrinfo pref = {
-    0,
-    PF_INET,
-    SOCK_STREAM,
-    IPPROTO_TCP,
-    0,
-    0,
-    0,
-    0
+    .ai_flags = 0,
+    .ai_family = PF_INET,
+    .ai_socktype = SOCK_STREAM,
+    .ai_protocol = IPPROTO_TCP,
   };
 
   /* Find the SMTP server */
-  a.n = 2;
-  a.s = s;
-  s[0] = config->smtp_server;
-  s[1] = (char *)"smtp";
-  if(!(ai = get_address(&a, &pref, &tag)))
-    return -1;
-  fdin = xsocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
-  if(connect(fdin, ai->ai_addr, ai->ai_addrlen) < 0) {
-    error(errno, "error connecting to %s", tag);
-    xclose(fdin);
-    return -1;
+  if(config->sendmail && config->sendmail[0]) {
+    int inpipe[2], outpipe[2];
+    
+    /* If it's a path name, use -bs mode.  Exim, Postfix and Sendmail all claim
+     * to support this.  */
+    xpipe(inpipe);                      /* sendmail's stdin */
+    xpipe(outpipe);                     /* sendmail's stdout */
+    if(!(pid = xfork())) {
+      exitfn = _exit;
+      signal(SIGPIPE, SIG_DFL);
+      xdup2(inpipe[0], 0);
+      xclose(inpipe[1]);
+      xclose(outpipe[0]);
+      xdup2(outpipe[1], 1);
+      execlp(config->sendmail,
+             config->sendmail, "-bs", (char *)0);
+      fatal(errno, "executing %s", config->sendmail);
+    }
+    xclose(inpipe[0]);
+    xclose(outpipe[1]);
+    fdin = outpipe[0];                  /* read from sendmail's stdout */
+    fdout = inpipe[1];                  /* write to sendmail's stdin */
+    tag = config->sendmail;
+  } else {
+    a.n = 2;
+    a.s = s;
+    s[0] = config->smtp_server;
+    s[1] = (char *)"smtp";
+    if(!(ai = get_address(&a, &pref, &tag)))
+      return -1;
+    fdin = xsocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+    if(connect(fdin, ai->ai_addr, ai->ai_addrlen) < 0) {
+      error(errno, "error connecting to %s", tag);
+      xclose(fdin);
+      return -1;
+    }
+    if((fdout = dup(fdin)) < 0)
+      fatal(errno, "error calling dup2");
   }
-  if((fdout = dup(fdin)) < 0)
-    fatal(errno, "error calling dup2");
   if(!(in = fdopen(fdin, "rb")))
     fatal(errno, "error calling fdopen");
   if(!(out = fdopen(fdout, "wb")))
@@ -243,6 +268,19 @@ int sendmail(const char *sender,
                  encoding, content_type, body);
   fclose(in);
   fclose(out);
+  if(pid != -1) {
+    int w;
+
+    while(waitpid(pid, &w, 0) < 0 && errno == EINTR)
+      ;
+    if(w < 0)
+      fatal(errno, "error calling waitpid");
+    if(w)
+      info("warning: %s -bs: %s", config->sendmail, wstat(w));
+    /* Not fatal - we determine success/failure from the SMTP conversation.
+     * Some MTAs exit nonzero if you don't QUIT, which is just stupidly
+     * picky. */
+  }
   return rc;
 }