--- /dev/null
+/*
+ * Sendmail Wrapper :)
+ *
+ * (c) 2006 Martin Mares <mj@ucw.cz>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/wait.h>
+
+#include "util.h"
+
+static char *local_user;
+static char *local_domain;
+static char *from_user;
+static char *from_domain;
+
+static time_t now;
+static struct tm *now_tm;
+
+static pid_t sm_pid;
+static FILE *sm_fh;
+
+static void
+start_sendmail(char **oargv, int oargc)
+{
+ char **argv = xmalloc(sizeof(char *) * (oargc+4));
+ int argc = 0;
+ argv[argc++] = "/usr/sbin/sendmail";
+
+ char from[strlen(from_user) + strlen(from_domain) + 16];
+ if (from_user)
+ {
+ sprintf(from, "%s+f-%04d%02d%02d@%s", from_user, now_tm->tm_year + 1900, now_tm->tm_mon + 1, now_tm->tm_mday, from_domain);
+ argv[argc++] = "-f";
+ argv[argc++] = from;
+ verb(1, "Using -f %s", from);
+ }
+
+ memcpy(argv+argc, oargv, sizeof(char *) * oargc);
+ argc += oargc;
+ argv[argc] = NULL;
+
+ // for (uns i=0; argv[i]; i++) verb(2, "Option: <%s>", argv[i]);
+
+ int pfd[2];
+ if (pipe(pfd) < 0)
+ die("pipe failed: %m");
+
+ sm_pid = fork();
+ if (sm_pid < 0)
+ die("fork failed: %m");
+ if (!sm_pid)
+ {
+ dup2(pfd[0], 0);
+ close(pfd[0]);
+ close(pfd[1]);
+ execvp(argv[0], argv);
+ die("Cannot exec %s: %m", argv[0]);
+ }
+ close(pfd[0]);
+ sm_fh = fdopen(pfd[1], "w");
+ if (!sm_fh)
+ die("fdopen() failed: %m");
+ verb(1, "Forked pid %d for %s", sm_pid, argv[0]);
+}
+
+static void
+close_sendmail(void)
+{
+ if (ferror(sm_fh) || fclose(sm_fh))
+ die("Error occured when writing to sendmail pipe");
+ for (;;)
+ {
+ int st;
+ pid_t p = wait(&st);
+ if (p < 0)
+ die("wait failed: %m");
+ if (p == sm_pid)
+ {
+ verb(1, "Subprocess %d finished with status 0x%x", p, st);
+ if (WIFEXITED(st))
+ {
+ if (WEXITSTATUS(st))
+ die("Sendmail process failed with exit code %d", WEXITSTATUS(st));
+ return;
+ }
+ if (WIFSIGNALED(st))
+ die("Sendmail process died on signal %d", WIFSIGNALED(st));
+ die("Sendmail process died with unknown status 0x%x", st);
+ }
+ }
+}
+
+static void
+process_header(void)
+{
+ char hdr[1024];
+
+ for (;;)
+ {
+ if (!fgets(hdr, sizeof(hdr), stdin))
+ die("Malformed mail headers: unexpected EOF");
+ char *nl = strchr(hdr, '\n');
+ if (!nl)
+ die("Malformed mail headers: line too long");
+ if (hdr[0] == '\r' || hdr[0] == '\n')
+ break;
+ if (strncasecmp(hdr, "Message-ID:", 11))
+ {
+ fputs(hdr, sm_fh);
+ *nl = 0;
+ }
+ }
+
+ if (local_user)
+ {
+ char hostname[64];
+ if (gethostname(hostname, sizeof(hostname)))
+ hostname[0] = 0;
+ else
+ hostname[sizeof(hostname)-1] = 0;
+ fprintf(sm_fh, "Message-ID: <%s+md-%04d%02d%02d.%02d%02d%02d.%d.%s@%s>\n",
+ local_user,
+ now_tm->tm_year + 1900, now_tm->tm_mon + 1, now_tm->tm_mday,
+ now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec,
+ getpid(), hostname,
+ local_domain);
+ }
+ fputs("\n", sm_fh);
+}
+
+static void
+process_body(void)
+{
+ char buf[1024];
+ int n;
+ while ((n = fread(buf, 1, sizeof(buf), stdin)) > 0)
+ fwrite(buf, 1, n, sm_fh);
+ if (n < 0)
+ die("Malformed mail body: %m");
+}
+
+static void NONRET
+usage(void)
+{
+ fprintf(stderr, "Usage: smail [<options>] -- <sendmail-options>\n\
+\n\
+Options:\n\
+-f<user>@<domain>\tGenerate envelope sender address for easy identification of bounces\n\
+-l<user>@<domain>\tAdd Message-ID for detection of messages looped back\n\
+-v\t\t\tIncrease verbosity\n\
+\n\
+MailDups " STR(VERSION) ", (c) " STR(YEAR) " Martin Mares <mj@ucw.cz>\n\
+It can be freely distributed and used according to the GNU GPL v2.\n\
+");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ while ((c = getopt(argc, argv, "f:l:v")) >= 0)
+ switch (c)
+ {
+ case 'f':
+ {
+ char *c = strchr(optarg, '@');
+ if (!c)
+ usage();
+ *c++ = 0;
+ from_user = optarg;
+ from_domain = c;
+ break;
+ }
+ case 'l':
+ {
+ char *c = strchr(optarg, '@');
+ if (!c)
+ usage();
+ *c++ = 0;
+ local_user = optarg;
+ local_domain = c;
+ break;
+ }
+ case 'v':
+ verbose++;
+ break;
+ default:
+ usage();
+ }
+
+ now = time(NULL);
+ if (!(now_tm = gmtime(&now)))
+ die("gmtime() failed: %m");
+
+ start_sendmail(argv+optind, argc-optind);
+ process_header();
+ process_body();
+ close_sendmail();
+ return 0;
+}