changeset 426:4b2488be8710

VERP support
author mmj
date Mon, 17 Jan 2005 05:43:48 +1100
parents cf0aadfa4c6c
children df8e1e2f0ff6
files ChangeLog TUNABLES VERSION include/getaddrsfromfd.h include/mail-functions.h include/mlmmj-send.h src/Makefile.am src/checkwait_smtpreply.c src/getaddrsfromfd.c src/mail-functions.c src/mlmmj-send.c
diffstat 11 files changed, 474 insertions(+), 127 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Jan 17 01:21:51 2005 +1100
+++ b/ChangeLog	Mon Jan 17 05:43:48 2005 +1100
@@ -1,3 +1,5 @@
+1.2.0-RC1
+ o Add VERP support. http://www.postfix.org/VERP_README.html for Postfix howto
 1.1.1-RC2
  o It's ok to log to a symbolic link to somewhere else
  o Add Message-Id: and Date: headers to mail from mlmmj
--- a/TUNABLES	Mon Jan 17 01:21:51 2005 +1100
+++ b/TUNABLES	Mon Jan 17 05:43:48 2005 +1100
@@ -109,3 +109,13 @@
    If this file exists, no mail confirmation is needed to subscribe to the
    list. This should in principle never ever be used, but there is times
    on local lists etc. where this is useful. HANDLE WITH CARE!
+
+ · verp				(normal)
+
+   Enable VERP support. Anything added in this variable will be appended the
+   MAIL FROM: line. If "postfix" is put in the file, it'll make postfix use
+   VERP by adding XVERP=-= to the MAIL FROM: line.
+
+ · maxverprecips		(normal)
+
+   How many recipients pr. mail delivered to the smtp server. Defaults to 100.
--- a/VERSION	Mon Jan 17 01:21:51 2005 +1100
+++ b/VERSION	Mon Jan 17 05:43:48 2005 +1100
@@ -1,1 +1,1 @@
-1.1.1-RC2
+1.2.0-RC1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/getaddrsfromfd.h	Mon Jan 17 05:43:48 2005 +1100
@@ -0,0 +1,32 @@
+/* Copyright (C) 2005 Mads Martin Joergensen <mmj at mmj.dk>
+ *
+ * $Id$
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef GETADDRSFROMFD_H
+#define GETADDRSFROMFD_H
+
+#include <sys/types.h>
+#include "mlmmj.h"
+
+off_t getaddrsfromfd(struct strlist *slist, int fd, int max);
+
+#endif /* GETADDRSFROMFD_H */
--- a/include/mail-functions.h	Mon Jan 17 01:21:51 2005 +1100
+++ b/include/mail-functions.h	Mon Jan 17 05:43:48 2005 +1100
@@ -29,7 +29,7 @@
 #include <stdio.h>
 
 int write_helo(int sockfd, const char *hostname);
-int write_mail_from(int sockfd, const char *from_addr);
+int write_mail_from(int sockfd, const char *from_addr, const char *extra);
 int write_rcpt_to(int sockfd, const char *rcpt_addr);
 int write_custom_line(int sockfd, const char *line);
 int write_mailbody_from_map(int sockfd, char *mailmap, size_t mailsize,
--- a/include/mlmmj-send.h	Mon Jan 17 01:21:51 2005 +1100
+++ b/include/mlmmj-send.h	Mon Jan 17 05:43:48 2005 +1100
@@ -29,12 +29,22 @@
 	      const char *listdir, const char *mlmmjbounce,
 	      const char *hdrs, size_t hdrslen, const char *body,
 	      size_t bodylen);
-int send_mail_many(int sockfd, const char *from, const char *replyto,
+int send_mail_many_fd(int sockfd, const char *from, const char *replyto,
 		   char *mailmap, size_t mailsize, int subfd,
 		   const char *listaddr, const char *archivefilename,
 		   const char *listdir, const char *mlmmjbounce,
 		   const char *hdrs, size_t hdrslen, const char *body,
 		   size_t bodylen);
+int send_mail_many_list(int sockfd, const char *from, const char *replyto,
+		   char *mailmap, size_t mailsize, struct strlist *addrs,
+		   const char *listaddr, const char *archivefilename,
+		   const char *listdir, const char *mlmmjbounce,
+		   const char *hdrs, size_t hdrslen, const char *body,
+		   size_t bodylen);
+int send_mail_verp(int sockfd, struct strlist *addrs, char *mailmap,
+		   size_t mailsize, const char *from, const char *listdir,
+		   const char *hdrs, size_t hdrslen, const char *body,
+		   size_t bodylen, const char *extra);
 int initsmtp(int *sockfd, const char *relayhost);
 int endsmtp(int *sockfd);
 
--- a/src/Makefile.am	Mon Jan 17 01:21:51 2005 +1100
+++ b/src/Makefile.am	Mon Jan 17 05:43:48 2005 +1100
@@ -16,7 +16,7 @@
                      incindexfile.c checkwait_smtpreply.c getlistaddr.c \
 		     mylocking.c init_sockfd.c strgen.c random-int.c \
 		     print-version.c log_error.c mygetline.c memory.c \
-		     statctrl.c ctrlvalue.c
+		     statctrl.c ctrlvalue.c getaddrsfromfd.c
 
 mlmmj_recieve_SOURCES = mlmmj-recieve.c writen.c random-int.c strgen.c \
 			print-version.c log_error.c dumpfd2fd.c memory.c \
--- a/src/checkwait_smtpreply.c	Mon Jan 17 01:21:51 2005 +1100
+++ b/src/checkwait_smtpreply.c	Mon Jan 17 05:43:48 2005 +1100
@@ -92,11 +92,11 @@
 		break;
 	case MLMMJ_QUIT:
 		if(smtpreply[0] != '2' || smtpreply[1] != '2')
-			return (char *)0xDEADBEEF;
+			return mystrdup(smtpreply);
 		break;
 	case MLMMJ_RSET:
 		if(smtpreply[0] != '2' || smtpreply[1] != '5')
-			return (char *)0xDEADBEEF;
+			return mystrdup(smtpreply);
 		break;
 	default:
 		break;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/getaddrsfromfd.c	Mon Jan 17 05:43:48 2005 +1100
@@ -0,0 +1,54 @@
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "mlmmj.h"
+#include "log_error.h"
+#include "memory.h"
+#include "strgen.h"
+#include "getaddrsfromfd.h"
+
+off_t getaddrsfromfd(struct strlist *slist, int fd, int max)
+{
+	off_t offset = lseek(fd, 0, SEEK_CUR);
+	char *start, *cur, *next;
+	struct stat st;
+	size_t len;
+
+	if(fstat(fd, &st) < 0) {
+		log_error(LOG_ARGS, "Could not fstat fd");
+		return -1;
+	}
+
+	start = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+	if(start == MAP_FAILED) {
+		log_error(LOG_ARGS, "Could not mmap fd");
+		return -1;
+	}
+	
+	for(next = cur = (start + offset); next < start + st.st_size; next++) {
+		if(*next == '\n' || next == start + st.st_size - 1) {
+			len = next - cur;
+			if(next == start + st.st_size - 1 && *next != '\n')
+				len++;
+			slist->strs[slist->count] = mymalloc(len + 1);
+			strncpy(slist->strs[slist->count], cur, len);
+			slist->strs[slist->count][len] = '\0';
+			slist->count++;
+			cur = next + 1;
+		} else {
+			continue;
+		}
+		if(slist->count >= max) {
+			offset = (off_t)(cur - start);
+			goto donegetting;
+		}
+	}
+	offset = st.st_size;
+donegetting:			
+	munmap(start, st.st_size);
+	lseek(fd, offset, SEEK_SET);
+	return st.st_size - offset;
+}
--- a/src/mail-functions.c	Mon Jan 17 01:21:51 2005 +1100
+++ b/src/mail-functions.c	Mon Jan 17 05:43:48 2005 +1100
@@ -63,19 +63,26 @@
 }
 /* "MAIL FROM: <>\r\n" has length 15 */
 #define EXTRA_FROM_LEN 16
-int write_mail_from(int sockfd, const char *from_addr)
+int write_mail_from(int sockfd, const char *from_addr, const char *extra)
 {
-	size_t len = (size_t)(strlen(from_addr) + EXTRA_FROM_LEN);
+	size_t len = (size_t)(strlen(from_addr) + EXTRA_FROM_LEN +
+			strlen(extra) + 2);
 	char *mail_from;
 	size_t bytes_written;
 
-	if((mail_from = mymalloc(len)) == NULL)
-		return errno;
-	snprintf(mail_from, len, "MAIL FROM: <%s>\r\n", from_addr);
+	mail_from = mymalloc(len);
+
+	if(extra && extra[0] == ' ')
+		snprintf(mail_from, len, "MAIL FROM: <%s>%s\r\n", from_addr,
+				extra);
+	else
+		snprintf(mail_from, len, "MAIL FROM: <%s> %s\r\n", from_addr,
+				extra);
+
 	len = strlen(mail_from);
 
 #if 0
-	fprintf(stderr, "\nwrite_mail_from, mail_from = [%s]\n", mail_from);
+	fprintf(stderr, "%s", mail_from);
 #endif
 	bytes_written = writen(sockfd, mail_from, len);
 	if(bytes_written < 0) {
@@ -101,9 +108,8 @@
 
 	snprintf(rcpt_to, len, "RCPT TO: <%s>\r\n", rcpt_addr);
 	len = strlen(rcpt_to);
-
 #if 0
-	fprintf(stderr, "\nwrite_rcpt_to, rcpt_to = [%s]\n", rcpt_to);
+	log_error(LOG_ARGS, "%s", rcpt_to);
 #endif
 	bytes_written = writen(sockfd, rcpt_to, len);
 	if(bytes_written < 0) {
@@ -289,7 +295,7 @@
 	snprintf(replyto, len, "Reply-To: %s\r\n", replyaddr);
 	len = strlen(replyto);
 
-#ifdef MLMMJ_DEBUG
+#if 0
 	fprintf(stderr, "\nwrite_replyto, replyto = [%s]\n", replyto);
 #endif
 	bytes_written = writen(sockfd, replyto, len);
--- a/src/mlmmj-send.c	Mon Jan 17 01:21:51 2005 +1100
+++ b/src/mlmmj-send.c	Mon Jan 17 05:43:48 2005 +1100
@@ -42,8 +42,8 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
+#include "mlmmj.h"
 #include "mlmmj-send.h"
-#include "mlmmj.h"
 #include "mail-functions.h"
 #include "itoa.h"
 #include "incindexfile.h"
@@ -59,9 +59,11 @@
 #include "statctrl.h"
 #include "ctrlvalue.h"
 #include "mylocking.h"
+#include "getaddrsfromfd.h"
 
 static int addtohdr = 0;
 static int prepmailinmem = 0;
+static int maxverprecips = MAXVERPRECIPS;
 
 char *bounce_from_adr(const char *recipient, const char *listadr,
 		      const char *mailfilename)
@@ -159,8 +161,8 @@
 	
 	execlp(mlmmjbounce, mlmmjbounce,
 			"-L", listdir,
-			"-a", addr,
-			"-n", num, NULL);
+			"-a", num,
+			"-n", addr, NULL);
 
 	log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjbounce);
 
@@ -176,7 +178,7 @@
 	int retval = 0;
 	char *reply, *tohdr;
 	
-	retval = write_mail_from(sockfd, from);
+	retval = write_mail_from(sockfd, from, "");
 	if(retval) {
 		log_error(LOG_ARGS, "Could not write MAIL FROM\n");
 		return retval;
@@ -318,13 +320,15 @@
 int endsmtp(int *sockfd)
 {
 	int retval = 0;
-	char *reply;
+	char *reply = NULL;
 	
 	write_quit(*sockfd);
-
-	if((reply = checkwait_smtpreply(*sockfd, MLMMJ_QUIT)) != 0) {
+	reply = checkwait_smtpreply(*sockfd, MLMMJ_QUIT);
+	if(reply) {
+		printf("reply from quit: %s\n", reply);
 		log_error(LOG_ARGS, "Mailserver would not let us QUIT. "
-			  "We close the socket anyway though.");
+			  "We close the socket anyway though. "
+			  "Mailserver reply = [%s]", reply);
 		myfree(reply);
 		retval = MLMMJ_QUIT;
 	}
@@ -334,65 +338,150 @@
 	return retval;
 }
 
-int send_mail_many(int sockfd, const char *from, const char *replyto,
+int send_mail_verp(int sockfd, struct strlist *addrs, char *mailmap,
+		   size_t mailsize, const char *from, const char *listdir,
+		   const char *hdrs, size_t hdrslen, const char *body,
+		   size_t bodylen, const char *verpextra)
+{
+	int retval, i;
+	char *reply;
+
+	retval = write_mail_from(sockfd, from, verpextra);
+	if(retval) {
+		log_error(LOG_ARGS, "Could not write MAIL FROM\n");
+		return retval;
+	}
+	reply = checkwait_smtpreply(sockfd, MLMMJ_FROM);
+	if(reply) {
+		log_error(LOG_ARGS, "Error in MAIL FROM. Reply = [%s]",
+				reply);
+		myfree(reply);
+		write_rset(sockfd);
+		checkwait_smtpreply(sockfd, MLMMJ_RSET);
+		return MLMMJ_FROM;
+	}
+	for(i = 0; i < addrs->count; i++) {
+		retval = write_rcpt_to(sockfd, addrs->strs[i]);
+		if(retval) {
+			log_error(LOG_ARGS, "Could not write RCPT TO:\n");
+			return retval;
+		}
+
+		reply = checkwait_smtpreply(sockfd, MLMMJ_RCPTTO);
+		if(reply) {
+			log_error(LOG_ARGS, "Error in RCPT TO. Reply = [%s]",
+					reply);
+			myfree(reply);
+			return MLMMJ_RCPTTO;
+		}
+	}
+
+	retval = write_data(sockfd);
+	if(retval) {
+		log_error(LOG_ARGS, "Could not write DATA\b");
+		return retval;
+	}
+
+	reply = checkwait_smtpreply(sockfd, MLMMJ_DATA);
+	if(reply) {
+		log_error(LOG_ARGS, "Error with DATA. Reply = [%s]", reply);
+		myfree(reply);
+		write_rset(sockfd);
+		checkwait_smtpreply(sockfd, MLMMJ_RSET);
+		return MLMMJ_DATA;
+	}
+
+	if(prepmailinmem) {
+		retval = writen(sockfd, hdrs, hdrslen);
+		if(retval < 0) {
+			log_error(LOG_ARGS, "Could not write mailheaders.\n");
+			return retval;
+		}
+		retval = writen(sockfd, body, bodylen);
+		if(retval < 0) {
+			log_error(LOG_ARGS, "Could not write mailbody.\n");
+			return retval;
+		}
+	} else {
+		retval = write_mailbody_from_map(sockfd, mailmap, mailsize,
+						 NULL);
+		if(retval) {
+			log_error(LOG_ARGS, "Could not write mail\n");
+			return retval;
+		}
+	}
+
+	retval = write_dot(sockfd);
+	if(retval) {
+		log_error(LOG_ARGS, "Could not write <CR><LF>.<CR><LF>\n");
+		return retval;
+	}
+
+	reply = checkwait_smtpreply(sockfd, MLMMJ_DOT);
+	if(reply) {
+		log_error(LOG_ARGS, "Mailserver did not ack end of mail.\n"
+				"<CR><LF>.<CR><LF> was written, to no"
+				"avail. Reply = [%s]", reply);
+		myfree(reply);
+		write_rset(sockfd);
+		checkwait_smtpreply(sockfd, MLMMJ_RSET);
+		return MLMMJ_DOT;
+	}
+
+	return 0;
+}
+
+int send_mail_many_fd(int sockfd, const char *from, const char *replyto,
 		   char *mailmap, size_t mailsize, int subfd,
 		   const char *listaddr, const char *archivefilename,
 		   const char *listdir, const char *mlmmjbounce,
 		   const char *hdrs, size_t hdrslen, const char *body,
 		   size_t bodylen)
 {
-	int sendres = 0, addrfd;
-	char *bounceaddr, *addr, *index, *dirname, *addrfilename;
-	char *start, *cur, *next;
-	struct stat st;
-	size_t len;
+	int res, ret, i;
+	struct strlist stl;
 
-	if(fstat(subfd, &st) < 0) {
-		log_error(LOG_ARGS, "Could not stat subfd");
-		return -1;
+	do {
+		res = getaddrsfromfd(&stl, subfd, maxverprecips);
+		if(stl.count == maxverprecips) {
+			ret = send_mail_many_list(sockfd, from, replyto,
+					mailmap, mailsize, &stl, listaddr,
+					archivefilename, listdir, mlmmjbounce,
+					hdrs, hdrslen, body, bodylen);
+			for(i = 0; i < stl.count; i++)
+				myfree(stl.strs[i]);
+			if(ret < 0)
+				return ret;
+			stl.count = 0;
 	}
+	} while(res > 0);
 
-	start = mmap(0, st.st_size, PROT_READ, MAP_SHARED, subfd, 0);
-	if(start == MAP_FAILED) {
-		log_error(LOG_ARGS, "Could not mmap subfd");
-		return -1;
+	if(stl.count) {
+		ret = send_mail_many_list(sockfd, from, replyto, mailmap,
+				mailsize, &stl, listaddr, archivefilename,
+				listdir, mlmmjbounce, hdrs, hdrslen, body,
+				bodylen);
+		for(i = 0; i < stl.count; i++)
+			myfree(stl.strs[i]);
+		stl.count = 0;
+		return ret;
 	}
 
-	for(next = cur = start; next < start + st.st_size; next++) {
-		if(*next == '\n' || next == start + st.st_size - 1) {
-			len = next - cur;
-			if(next == start + st.st_size - 1 && *next != '\n')
-				len++;
-			addr = mymalloc(len + 1);
-			strncpy(addr, cur, len);
-			addr[len] = '\0';
-			cur = next + 1;
-		} else
-			continue;
+	return 0;
+}
 
-		if(from) {
-			sendres = send_mail(sockfd, from, addr, replyto,
-					    mailmap, mailsize, listdir, NULL,
-					    hdrs, hdrslen, body, bodylen);
-		} else {
-			bounceaddr = bounce_from_adr(addr, listaddr,
-						     archivefilename);
-			sendres = send_mail(sockfd, bounceaddr, addr, replyto,
-				  mailmap, mailsize, listdir, mlmmjbounce,
-				  hdrs, hdrslen, body, bodylen);
-			myfree(bounceaddr);
-		}
-		if(sendres && listaddr && archivefilename) {
-			/* we failed, so save the addresses and bail */
-			index = mybasename(archivefilename);	
+int requeuemail(const char *listdir, const char *index, struct strlist *addrs,
+		int addrcount)
+{
+	int addrfd, i;
+	char *dirname, *addrfilename, *addr;
+	
 			dirname = concatstr(3, listdir, "/requeue/", index);
-			myfree(index);
 			if(mkdir(dirname, 0750) < 0 && errno != EEXIST) {
 				log_error(LOG_ARGS, "Could not mkdir(%s) for "
 						    "requeueing. Mail cannot "
 						    "be requeued.", dirname);
 				myfree(dirname);
-				myfree(addr);
 				return -1;
 			}
 			addrfilename = concatstr(2, dirname, "/subscribers");
@@ -403,40 +492,64 @@
 				log_error(LOG_ARGS, "Could not open %s",
 						    addrfilename);
 				myfree(addrfilename);
-				myfree(addr);
 				return -1;
 			} else {
-				/* Dump the remaining addresses. We dump the
-				 * remaining before we write the failing
-				 * address to ensure the potential good ones
-				 * will be tried first when mlmmj-maintd
-				 * sends out mails that have been requeued. */
-				if(writen(addrfd, cur, start+st.st_size-cur)
-						< 0) {
-					log_error(LOG_ARGS, "Could not dump "
-							"remaining addresses "
-							"of subfile to "
-							"requeue address "
-							"file");
+		/* Dump the remaining addresses. We dump the remaining before
+		 * we write the failing address to ensure the potential good
+		 * ones will be tried first when mlmmj-maintd sends out mails
+		 * that have been requeued. addrcount was so far we were */
+		for(i = addrcount + 1; i < addrs->count; i++) {
+			addr = concatstr(2, addrs->strs[i], "\n");
+			if(writen(addrfd, addr, strlen(addr)) < 0) {
+				log_error(LOG_ARGS, "Could not add [%s] "
+						    "to requeue file", addr);
+				return -1;
 				}
-				/* Dirty hack to add newline. */
-				addr[len] = '\n';
-				if(writen(addrfd, addr, len+1) < 0) {
-					addr[len] = '\0';
-					log_error(LOG_ARGS, "Could not add "
-							"[%s] to requeue "
-							"address file", addr);
+			myfree(addr);
 				}
-				
+		addr = concatstr(2, addrs->strs[addrcount], "\n");
+		if(writen(addrfd, addr, strlen(addr)) < 0) {
+			log_error(LOG_ARGS, "Could not add [%s] to requeue "
+					"file", addr);
+			return -1;
 			}
-			
 			myfree(addr);
+	}
 			myfree(addrfilename);
 			close(addrfd);
 
-			return -1;
+	return 0;
 		}
-		myfree(addr);
+
+int send_mail_many_list(int sockfd, const char *from, const char *replyto,
+		   char *mailmap, size_t mailsize, struct strlist *addrs,
+		   const char *listaddr, const char *archivefilename,
+		   const char *listdir, const char *mlmmjbounce,
+		   const char *hdrs, size_t hdrslen, const char *body,
+		   size_t bodylen)
+{
+	int res = 0, i;
+	char *bounceaddr, *addr, *index;
+
+	for(i = 0; i < addrs->count; i++) {
+		addr = addrs->strs[i];
+		if(from) {
+			res = send_mail(sockfd, from, addr, replyto,
+					    mailmap, mailsize, listdir, NULL,
+					    hdrs, hdrslen, body, bodylen);
+		} else {
+			bounceaddr = bounce_from_adr(addr, listaddr,
+						     archivefilename);
+			res = send_mail(sockfd, bounceaddr, addr, replyto,
+				  mailmap, mailsize, listdir, mlmmjbounce,
+				  hdrs, hdrslen, body, bodylen);
+			myfree(bounceaddr);
+		}
+		if(res && listaddr && archivefilename) {
+			/* we failed, so save the addresses and bail */
+			index = mybasename(archivefilename);
+			return requeuemail(listdir, index, addrs, i);
+		}
 	}
 	return 0;
 }	
@@ -471,22 +584,24 @@
 int main(int argc, char **argv)
 {
 	size_t len = 0, hdrslen, bodylen;
-	int sockfd = 0, mailfd = 0, opt, mindex, subfd = 0, tmpfd;
-	int deletewhensent = 1, sendres, archive = 1, digest = 0;
-	int ctrlarchive;
+	int sockfd = 0, mailfd = 0, opt, mindex = 0, subfd = 0, tmpfd, i;
+	int deletewhensent = 1, sendres = 0, archive = 1, digest = 0;
+	int ctrlarchive, res;
 	char *listaddr = NULL, *mailfilename = NULL, *subfilename = NULL;
 	char *replyto = NULL, *bounceaddr = NULL, *to_addr = NULL;
 	char *relayhost = NULL, *archivefilename = NULL, *tmpstr;
 	char *listctrl = NULL, *subddirname = NULL, *listdir = NULL;
 	char *mlmmjbounce = NULL, *bindir, *mailmap, *probefile, *a;
-	char *body = NULL, *hdrs = NULL, *memmailsizestr = NULL;
-	char relay[16];
+	char *body = NULL, *hdrs = NULL, *memmailsizestr = NULL, *verp = NULL;
+	char relay[16], *listname, *listfqdn, *verpfrom, *maxverprecipsstr;
+	char strindex[32], *reply;
 	ssize_t memmailsize = 0;
 	DIR *subddir;
 	struct dirent *dp;
 	struct stat st;
 	struct hostent *relayent;
 	uid_t uid;
+	struct strlist stl;
 
 	CHECKFULLPATH(argv[0]);
 	
@@ -582,6 +697,18 @@
 		exit(EXIT_FAILURE);
 	}
 
+	maxverprecipsstr = ctrlvalue(listdir, "maxverprecips");
+	if(maxverprecipsstr) {
+		maxverprecips = atol(maxverprecipsstr);
+		log_error(LOG_ARGS, "maxverprecipsstr = [%s] maxverprecips = [%d]",
+				maxverprecipsstr, maxverprecips);
+		myfree(maxverprecipsstr);
+	}
+	if(maxverprecips <= 0)
+		maxverprecips = MAXVERPRECIPS;
+
+	verp = ctrlvalue(listdir, "verp");
+	chomp(verp);
 
 	switch(listctrl[0]) {
 		case '1':
@@ -705,6 +832,8 @@
 			 mindex);
 	}
 
+	itoa(mindex, strindex);
+
 	if(!relayhost) {
 		relayhost = ctrlvalue(listdir, "relayhost");
 		chomp(relayhost);
@@ -770,7 +899,7 @@
 		break;
 	case '2': /* Moderators */
 		initsmtp(&sockfd, relay);
-		if(send_mail_many(sockfd, bounceaddr, NULL, mailmap,
+		if(send_mail_many_fd(sockfd, bounceaddr, NULL, mailmap,
 				  st.st_size, subfd, NULL, NULL, listdir,
 				  NULL, hdrs, hdrslen, body, bodylen))
 			close(sockfd);
@@ -779,7 +908,7 @@
 		break;
 	case '3': /* resending earlier failed mails */
 		initsmtp(&sockfd, relay);
-		if(send_mail_many(sockfd, NULL, NULL, mailmap, st.st_size,
+		if(send_mail_many_fd(sockfd, NULL, NULL, mailmap, st.st_size,
 				subfd, listaddr, mailfilename, listdir,
 				mlmmjbounce, hdrs, hdrslen, body, bodylen))
 			close(sockfd);
@@ -789,9 +918,10 @@
 		break;
 	case '4': /* send mails to owner */
 		initsmtp(&sockfd, relay);
-		if(send_mail_many(sockfd, bounceaddr, NULL, mailmap, st.st_size,
-				subfd, listaddr, mailfilename, listdir,
-				mlmmjbounce, hdrs, hdrslen, body, bodylen))
+		if(send_mail_many_fd(sockfd, bounceaddr, NULL, mailmap,
+				st.st_size, subfd, listaddr, mailfilename,
+				listdir, mlmmjbounce, hdrs, hdrslen, body,
+				bodylen))
 			close(sockfd);
 		else
 			endsmtp(&sockfd);
@@ -834,6 +964,46 @@
 			exit(EXIT_FAILURE);
 		}
 
+		stl.strs = (char **)mymalloc(1 + maxverprecips * sizeof(char *));
+		stl.count = 0;
+		listname = genlistname(listaddr);	
+		listfqdn = genlistfqdn(listaddr);	
+		verpfrom = concatstr(5, listname, "+bounces-", strindex, "@",
+				listfqdn);
+		myfree(listname);
+		myfree(listfqdn);
+
+		if(verp && (strcmp(verp, "postfix") == 0)) {
+			myfree(verp);
+			verp = mystrdup("XVERP=-=");
+		}
+
+		if(addtohdr && verp) {
+			log_error(LOG_ARGS, "Cannot use VERP and add To: "
+					"header. Not sending with VERP.");
+			verp = NULL;
+		}
+
+		if(verp) {
+			initsmtp(&sockfd, relay);
+			if(write_mail_from(sockfd, verpfrom, verp)) {
+				log_error(LOG_ARGS,
+						"Could not write MAIL FROM\n");
+				verp = NULL;
+			} else {
+				reply = checkwait_smtpreply(sockfd, MLMMJ_FROM);
+				if(reply) {
+					log_error(LOG_ARGS,
+						"Mailserver did not "
+						"accept verp mail from. "
+						"Not sending with VERP.");
+					myfree(reply);
+					verp = NULL;
+				}
+			}
+			endsmtp(&sockfd);
+		}
+
 		while((dp = readdir(subddir)) != NULL) {
 			if(!strcmp(dp->d_name, "."))
 				continue;
@@ -846,13 +1016,76 @@
 				myfree(subfilename);
 				continue;
 			}
-			myfree(subfilename);
-
+			do {
+				res = getaddrsfromfd(&stl, subfd,
+						maxverprecips);
+				if(stl.count == maxverprecips) {
 			initsmtp(&sockfd, relay);
-			sendres = send_mail_many(sockfd, NULL, NULL, mailmap,
-					st.st_size, subfd, listaddr,
-					archivefilename, listdir, mlmmjbounce,
-					hdrs, hdrslen, body, bodylen);
+					if(verp) {
+						sendres = send_mail_verp(
+								sockfd, &stl,
+								mailmap,
+								st.st_size,
+								verpfrom,
+								listdir, hdrs,
+								hdrslen, body,
+								bodylen, verp);
+						if(sendres)
+							requeuemail(listdir,
+								strindex,
+								&stl, 0);
+					} else {
+						sendres = send_mail_many_list(
+								sockfd, NULL,
+								NULL, mailmap,
+								st.st_size,
+								&stl,
+								listaddr,
+								archivefilename,
+								listdir,
+								mlmmjbounce,
+								hdrs, hdrslen,
+								body, bodylen);
+					}
+					endsmtp(&sockfd);
+					for(i = 0; i < stl.count; i++)
+						myfree(stl.strs[i]);
+					stl.count = 0;
+				}
+			} while(res > 0);
+			if(stl.count) {
+				initsmtp(&sockfd, relay);
+				if(verp) {
+					sendres = send_mail_verp(sockfd,
+							&stl, mailmap,
+							st.st_size,
+							verpfrom,
+							listdir, hdrs, hdrslen,
+							body, bodylen, verp);
+					if(sendres)
+						requeuemail(listdir, strindex,
+								&stl, 0);
+				} else {
+					sendres = send_mail_many_list(sockfd,
+							NULL, NULL, mailmap,
+							st.st_size, &stl,
+							listaddr,
+							archivefilename,
+							listdir, mlmmjbounce,
+							hdrs, hdrslen, body,
+							bodylen);
+				}
+				endsmtp(&sockfd);
+				for(i = 0; i < stl.count; i++)
+					myfree(stl.strs[i]);
+				stl.count = 0;
+			}
+
+			myfree(verpfrom);
+			myfree(subfilename);
+			myfree(stl.strs);
+			close(subfd);
+
 			if (sendres) {
 				/* If send_mail_many() failed we close the
 				 * connection to the mail server in a brutal
@@ -862,7 +1095,6 @@
 			} else {
 				endsmtp(&sockfd);
 			}
-			close(subfd);
 		}
 		closedir(subddir);
 		myfree(subddirname);
@@ -875,6 +1107,7 @@
 	close(sockfd);
 	munmap(mailmap, st.st_size);
 	close(mailfd);
+	myfree(verp);
 
 	if(archive) {
 		if(!ctrlarchive) {