my work on trunk and the debian/ branch.
+dma (0.0.2009.02.11-1~2) unstable; urgency=low
+
+ * Add four new patches:
+ - lock the newly-created files to prevent double delivery attempts
+ if "dma -q" is run at just the wrong time;
+ - provide the proper bounce message on failed deliveries;
+ - add the FULLBOUNCE option;
+ - add the MAILNAME and MAILNAMEFILE options and use /etc/mailname
+ as the MAILNAMEFILE.
+
+ -- Peter Pentchev <roam@ringlet.net> Wed, 11 Mar 2009 14:48:15 +0200
+
dma (0.0.2009.02.11-1~1) unstable; urgency=low
* Initial release (Closes: #511410)
--- /dev/null
+Lock the temporary files after creating them to protect from a "dma -q"
+run at just the wrong time causing a double delivery attempt for
+the same message.
+
+--- a/dma.c
++++ b/dma.c
+@@ -225,6 +225,8 @@
+ fd = mkstemp(fn);
+ if (fd < 0)
+ return (-1);
++ if (flock(fd, LOCK_EX) == -1)
++ return (-1);
+ queue->mailfd = fd;
+ queue->tmpf = strdup(fn);
+ if (queue->tmpf == NULL) {
--- /dev/null
+Provide the proper bounce error message on failed deliveries.
+This may not be the best solution - the error message buffer has now
+turned dynamic, but the only alternative I see is to make it a static
+array in net.c... and I'm not quite sure if I want to do that just now.
+
+--- a/dma.c
++++ b/dma.c
+@@ -439,7 +439,7 @@
+ }
+
+ static void
+-bounce(struct qitem *it, const char *reason)
++bounce(struct qitem *it, char *reason)
+ {
+ struct queue bounceq;
+ struct qitem *bit;
+@@ -598,6 +598,7 @@
+ VERSION, hostname(),
+ it->addr,
+ reason);
++ free(reason);
+ if (error < 0)
+ goto fail;
+ if (fflush(bit->queuef) != 0)
+@@ -674,7 +675,7 @@
+ #endif
+
+ static int
+-deliver_local(struct qitem *it, const char **errmsg)
++deliver_local(struct qitem *it, char **errmsg)
+ {
+ char fn[PATH_MAX+1];
+ char line[1000];
+@@ -757,7 +758,7 @@
+ #ifdef HAVE_LIBLOCKFILE
+ mailunlock();
+ #endif
+- *errmsg = "corrupted queue file";
++ *errmsg = strdup("corrupted queue file");
+ error = -1;
+ goto chop;
+ }
+@@ -800,7 +801,7 @@
+ {
+ int error;
+ unsigned int backoff = MIN_RETRY;
+- const char *errmsg = "unknown bounce reason";
++ char *errmsg = strdup("unknown bounce reason");
+ struct timeval now;
+ struct stat st;
+
+@@ -831,12 +832,9 @@
+ }
+ if (gettimeofday(&now, NULL) == 0 &&
+ (now.tv_sec - st.st_mtime > MAX_TIMEOUT)) {
+- char *msg;
+-
+- if (asprintf(&msg,
++ asprintf(&errmsg,
+ "Could not deliver for the last %d seconds. Giving up.",
+- MAX_TIMEOUT) > 0)
+- errmsg = msg;
++ MAX_TIMEOUT);
+ goto bounce;
+ }
+ sleep(backoff);
+--- a/dma.h
++++ b/dma.h
+@@ -163,7 +163,7 @@
+ /* net.c */
+ extern int read_remote(int, int, char *);
+ extern ssize_t send_remote_command(int, const char*, ...);
+-extern int deliver_remote(struct qitem *, const char **);
++extern int deliver_remote(struct qitem *, char **);
+
+ /* base64.c */
+ extern int base64_encode(const void *, int, char **);
+--- a/net.c
++++ b/net.c
+@@ -314,7 +314,7 @@
+ }
+
+ int
+-deliver_remote(struct qitem *it, const char **errmsg)
++deliver_remote(struct qitem *it, char **errmsg)
+ {
+ struct authuser *a;
+ char *host, line[1000];
+@@ -323,11 +323,14 @@
+
+ host = strrchr(it->addr, '@');
+ /* Should not happen */
+- if (host == NULL)
++ if (host == NULL) {
++ asprintf(errmsg, "Internal error: badly formed address %s",
++ it->addr);
+ return(-1);
+- else
++ } else {
+ /* Step over the @ */
+ host++;
++ }
+
+ /* Smarthost support? */
+ if (config->smarthost != NULL && strlen(config->smarthost) > 0) {
+@@ -370,6 +373,8 @@
+ if (read_remote(fd, 0, NULL) != 2) {
+ syslog(LOG_ERR, "%s: remote delivery deferred: "
+ " EHLO failed: %s", it->queueid, neterr);
++ asprintf(errmsg, "%s did not like our EHLO:\n%s",
++ host, neterr);
+ return (-1);
+ }
+ }
+@@ -379,6 +384,8 @@
+ if (read_remote(fd, 0, NULL) != 2) {
+ syslog(LOG_ERR, "%s: remote delivery deferred: "
+ " EHLO failed: %s", it->queueid, neterr);
++ asprintf(errmsg, "%s did not like our EHLO:\n%s",
++ host, neterr);
+ return (-1);
+ }
+ }
+@@ -405,6 +412,7 @@
+ if (error < 0) {
+ syslog(LOG_ERR, "%s: remote delivery failed:"
+ " SMTP login failed: %m", it->queueid);
++ asprintf(errmsg, "SMTP login to %s failed", host);
+ return (-1);
+ }
+ /* SMTP login is not available, so try without */
+@@ -418,6 +426,8 @@
+ if (res == 5) { \
+ syslog(LOG_ERR, "%s: remote delivery failed: " \
+ c " failed: %s", it->queueid, neterr); \
++ asprintf(errmsg, "%s did not like our " c ":\n%s", \
++ host, neterr); \
+ return (-1); \
+ } else if (res != exp) { \
+ syslog(LOG_ERR, "%s: remote delivery deferred: " \
+@@ -447,7 +457,7 @@
+ if (linelen == 0 || line[linelen - 1] != '\n') {
+ syslog(LOG_CRIT, "%s: remote delivery failed:"
+ "corrupted queue file", it->queueid);
+- *errmsg = "corrupted queue file";
++ *errmsg = strdup("corrupted queue file");
+ error = -1;
+ goto out;
+ }
--- /dev/null
+Add the FULLBOUNCE config option to include the full message in the bounce.
+
+--- a/conf.c
++++ b/conf.c
+@@ -250,6 +250,8 @@
+ config->features |= DEFER;
+ else if (strcmp(word, "INSECURE") == 0)
+ config->features |= INSECURE;
++ else if (strcmp(word, "FULLBOUNCE") == 0)
++ config->features |= FULLBOUNCE;
+ }
+ }
+
+--- a/dma.8
++++ b/dma.8
+@@ -237,6 +237,11 @@
+ .Ar queueid
+ .Fl f
+ .Ar messagefile
++.It Ic FULLBOUNCE Xo
++(boolean, default=commented)
++.Xc
++Uncomment if you want the bounce message to include the complete original
++message, not just the headers.
+ .El
+ .Ss virtusertable
+ The
+--- a/dma.c
++++ b/dma.c
+@@ -585,7 +585,7 @@
+ \n\
+ %s\n\
+ \n\
+-Message headers follow.\n\
++%s\n\
+ \n\
+ ",
+ bounceq.id,
+@@ -597,7 +597,9 @@
+ rfc822date(),
+ VERSION, hostname(),
+ it->addr,
+- reason);
++ reason,
++ config->features & FULLBOUNCE? "Original message follows.":
++ "Message headers follow.");
+ free(reason);
+ if (error < 0)
+ goto fail;
+@@ -606,14 +608,20 @@
+
+ if (fseek(it->queuef, it->hdrlen, SEEK_SET) != 0)
+ goto fail;
+- while (!feof(it->queuef)) {
+- if (fgets(line, sizeof(line), it->queuef) == NULL)
+- break;
+- if (line[0] == '\n')
+- break;
+- if (write(bounceq.mailfd, line, strlen(line)) != strlen(line))
+- goto fail;
+- }
++ if (config->features & FULLBOUNCE)
++ while ((pos = fread(line, 1, sizeof(line), it->queuef)) > 0) {
++ if (write(bounceq.mailfd, line, pos) != pos)
++ goto fail;
++ }
++ else
++ while (!feof(it->queuef)) {
++ if (fgets(line, sizeof(line), it->queuef) == NULL)
++ break;
++ if (line[0] == '\n')
++ break;
++ if (write(bounceq.mailfd, line, strlen(line)) != strlen(line))
++ goto fail;
++ }
+ if (fsync(bounceq.mailfd) != 0)
+ goto fail;
+ if (linkspool(&bounceq) != 0)
+--- a/dma.h
++++ b/dma.h
+@@ -72,6 +72,7 @@
+ #define NOSSL 0x008 /* Do not use SSL */
+ #define DEFER 0x010 /* Defer mails */
+ #define INSECURE 0x020 /* Allow plain login w/o encryption */
++#define FULLBOUNCE 0x040 /* Bounce the full message */
+
+ #define CONF_PATH "/etc/dma/dma.conf" /* Default path to dma.conf */
+
+--- a/etc/dma.conf
++++ b/etc/dma.conf
+@@ -45,3 +45,7 @@
+ # default behavior of simply aborting the delivery, or specify the name or
+ # full path to a program that will process the double-bounce message.
+ DBOUNCEPROG dbounce-simple-safecat
++
++# Uncomment if you want the bounce message to include the complete original
++# message, not just the headers.
++#FULLBOUNCE
--- /dev/null
+Add the MAILNAME and MAILNAMEFILE config options.
+For Debian, use /etc/mailname for the MAILNAMEFILE.
+
+--- a/conf.c
++++ b/conf.c
+@@ -240,6 +240,14 @@
+ if (data != NULL)
+ config->dbounceprog = strdup(data);
+ }
++ else if (strcmp(word, "MAILNAME") == 0) {
++ if (data != NULL)
++ config->mailname = strdup(data);
++ }
++ else if (strcmp(word, "MAILNAMEFILE") == 0) {
++ if (data != NULL)
++ config->mailnamefile = strdup(data);
++ }
+ else if (strcmp(word, "VIRTUAL") == 0)
+ config->features |= VIRTUAL;
+ else if (strcmp(word, "STARTTLS") == 0)
+--- a/dma.8
++++ b/dma.8
+@@ -242,6 +242,20 @@
+ .Xc
+ Uncomment if you want the bounce message to include the complete original
+ message, not just the headers.
++.It Ic MAILNAME Xo
++(string, default=empty)
++.Xc
++The name to be used when introducing this host, if different from
++the result of
++.Xr hostname 1 .
++If specified, this option overrides
++.Sq MAILNAMEFILE .
++.It Ic MAILNAMEFILE Xo
++(string, default=empty)
++.Xc
++The name of the file to read the
++.Sq MAILNAME
++from.
+ .El
+ .Ss virtusertable
+ The
+--- a/dma.c
++++ b/dma.c
+@@ -82,10 +82,38 @@
+ hostname(void)
+ {
+ static char name[MAXHOSTNAMELEN+1];
+-
++ int initialized = 0;
++ FILE *fp;
++ size_t len;
++
++ if (initialized)
++ return (name);
++
++ if (config->mailname != NULL && config->mailname[0] != '\0') {
++ snprintf(name, sizeof(name), "%s", config->mailname);
++ initialized = 1;
++ return (name);
++ }
++ if (config->mailnamefile != NULL && config->mailnamefile[0] != '\0') {
++ fp = fopen(config->mailnamefile, "r");
++ if (fp != NULL) {
++ if (fgets(name, sizeof(name), fp) != NULL) {
++ len = strlen(name);
++ while (len > 0 &&
++ (name[len - 1] == '\r' ||
++ name[len - 1] == '\n'))
++ name[--len] = '\0';
++ if (name[0] != '\0') {
++ initialized = 1;
++ return (name);
++ }
++ }
++ fclose(fp);
++ }
++ }
+ if (gethostname(name, sizeof(name)) != 0)
+ strcpy(name, "(unknown hostname)");
+-
++ initialized = 1;
+ return name;
+ }
+
+--- a/dma.h
++++ b/dma.h
+@@ -122,6 +122,8 @@
+ SSL *ssl;
+ #endif /* HAVE_CRYPTO */
+ char *dbounceprog;
++ char *mailname;
++ char *mailnamefile;
+ };
+
+
+--- a/etc/dma.conf
++++ b/etc/dma.conf
+@@ -49,3 +49,11 @@
+ # Uncomment if you want the bounce message to include the complete original
+ # message, not just the headers.
+ #FULLBOUNCE
++
++# The name to be used when introducing this host, if different from
++# the result of "hostname". If specified, this overrides MAILNAMEFILE.
++#MAILNAME mail.example.net
++
++# The name of the file to read the MAILNAME from; if this file is not
++# present, the result of "hostname" will be used.
++MAILNAMEFILE /etc/mailname
11-double-bounce.patch
12-man-q-argument.patch
13-hardening.patch
+14-lock-new-files.patch
+15-bounce-message.patch
+16-bounce-full.patch
+17-mailname.patch