changeset 797:c3ee2bfaeb02

Implement new list text naming scheme Also fix a bug where the normal list text would be sent when unsubscribing from the nomail version of the list Also fix some minor documentation bugs
author Ben Schmidt
date Sat, 10 Sep 2011 12:23:40 +1000
parents 9f503b69a55d
children 98a5de42afcc
files ChangeLog include/mlmmj-sub.h include/mlmmj-unsub.h include/mlmmj.h include/prepstdreply.h include/send_help.h man/mlmmj-sub.1 man/mlmmj-unsub.1 src/listcontrol.c src/mlmmj-bounce.c src/mlmmj-maintd.c src/mlmmj-process.c src/mlmmj-sub.c src/mlmmj-unsub.c src/prepstdreply.c src/send_digest.c src/send_help.c src/send_list.c
diffstat 18 files changed, 290 insertions(+), 130 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Mar 07 01:30:02 2011 +1100
+++ b/ChangeLog	Sat Sep 10 12:23:40 2011 +1000
@@ -1,3 +1,6 @@
+ o Fix bug where the normal listtext would be sent when unsubscribing from the
+   nomail version of the list
+ o New listtext naming scheme
  o Avoid trailing whitespace in MAIL FROM line (Lukas Fleischer)
  o Better end-of-line handling and error reporting in php-admin (Franky Van
    Liedekerke)
--- a/include/mlmmj-sub.h	Mon Mar 07 01:30:02 2011 +1100
+++ b/include/mlmmj-sub.h	Sat Sep 10 12:23:40 2011 +1000
@@ -24,18 +24,5 @@
 #ifndef MLMMJ_SUBSCRIBE_H
 #define MLMMJ_SUBSCRIBE_H
 
-#include <mlmmj.h>
-
-void moderate_sub(const char *listdir, const char *listaddr,
-		  const char *listdelim, const char *subaddr,
-		  const char *mlmmjsend, enum subtype typesub);
-void getaddrandtype(const char *listdir, const char *modstr,
-		char **addrptr, enum subtype *subtypeptr);
-void confirm_sub(const char *listdir, const char *listaddr,
-		 const char *listdelim, const char *subaddr,
-		 const char *mlmmjsend, enum subtype typesub);
-void generate_subconfirm(const char *listdir, const char *listadr,
-		const char *listdelim, const char *subaddr,
-		const char *mlmmjsend, enum subtype typesub);
 
 #endif /* MLMMJ_SUBSCRIBE_H */
--- a/include/mlmmj-unsub.h	Mon Mar 07 01:30:02 2011 +1100
+++ b/include/mlmmj-unsub.h	Sat Sep 10 12:23:40 2011 +1000
@@ -24,14 +24,5 @@
 #ifndef MLMMJ_UNSUBSCRIBE_H
 #define MLMMJ_UNSUBSCRIBE_H
 
-#include <sys/types.h>
-
-void confirm_unsub(const char *listdir, const char *listaddr,
-		   const char *listdelim, const char *subaddr,
-		   const char *mlmmj, enum subtype typesub);
-ssize_t unsubscribe(int subreadfd, int subwritefd, const char *address);
-void generate_unsubconfirm(const char *listdir, const char *listaddr,
-			   const char *listdelim, const char *subaddr,
-			   const char *mlmmjsend, enum subtype typesub);
 
 #endif /* MLMMJ_UNSUBSCRIBE_H */
--- a/include/mlmmj.h	Mon Mar 07 01:30:02 2011 +1100
+++ b/include/mlmmj.h	Sat Sep 10 12:23:40 2011 +1000
@@ -70,13 +70,27 @@
 };
 
 /* Has to go here, since it's used in many places */
+
 enum subtype {
 	SUB_NORMAL,
 	SUB_DIGEST,
 	SUB_NOMAIL,
-	SUB_FILE /* For single files (moderator, owner etc.) */
+	SUB_FILE, /* For single files (moderator, owner etc.) */
+	SUB_ALL /* For listing all kinds of subscribers */
 };
 
+char *subtype_strs[5]; /* count matches enum above; defined in mlmmj-sub.c */
+
+enum subreason {
+	SUB_REQUEST,
+	SUB_CONFIRM,
+	SUB_PERMIT,
+	SUB_ADMIN,
+	SUB_BOUNCING
+};
+
+char * subreason_strs[5]; /* count matches enum above; defined in mlmmj-sub.c */
+
 void print_version(const char *prg);
 
 #define MY_ASSERT(expression) if (!(expression)) { \
--- a/include/prepstdreply.h	Mon Mar 07 01:30:02 2011 +1100
+++ b/include/prepstdreply.h	Sat Sep 10 12:23:40 2011 +1000
@@ -29,8 +29,9 @@
 char *substitute_one(const char *line, const char *listaddr,
 		const char *listdelim, size_t datacount, char **data, const char *listdir);
 int open_listtext(const char *listdir, const char *filename);
-char *prepstdreply(const char *listdir, const char *filename, const char *from,
-		const char *to, const char *replyto, size_t tokencount,
-		char **data, const char *mailname);
+char *prepstdreply(const char *listdir, const char *purpose, const char *action,
+		const char *reason, const char *type, const char *compat,
+		const char *from, const char *to, const char *replyto,
+		size_t tokencount, char **data, const char *mailname);
 
 #endif /* PREPSTDREPLY_H */
--- a/include/send_help.h	Mon Mar 07 01:30:02 2011 +1100
+++ b/include/send_help.h	Sat Sep 10 12:23:40 2011 +1000
@@ -25,6 +25,7 @@
 #define SEND_HELP_H
 
 void send_help(const char *listdir, const char *emailaddr,
-	       const char *mlmmjsend, const char *name, const char *textfile);
+	       const char *mlmmjsend, const char *purpose, const char *action,
+	       const char *reason, const char *type, const char *compat);
 
 #endif
--- a/man/mlmmj-sub.1	Mon Mar 07 01:30:02 2011 +1100
+++ b/man/mlmmj-sub.1	Sat Sep 10 12:23:40 2011 +1000
@@ -3,8 +3,8 @@
 mlmmj-sub \- subscribe address to a mailinglist run by mlmmj
 .SH SYNOPSIS
 .B mlmmj-sub
-\fI-L /path/to/list -a john@doe.org \fR[\fI-c\fR | \fI-C\fR] \fR[\fI-d\fR | \fI-n\fR]
-[\fI-f\fR] [\fI-h\fR] [\fI-U\fR] [\fI-V\fR]
+\fI-L /path/to/list\fR [\fI-a john@doe.org\fR | \fI-m str\fR]
+[\fI-c\fR | \fI-C\fR] [\fI-d\fR | \fI-n\fR] [\fI-f\fR] [\fI-h\fR] \fR[\fI-r\fR | \fI-R\fR] [\fI-s\fR] [\fI-U\fR] [\fI-V\fR]
 .HP
 \fB\-a\fR: Email address to subscribe
 .HP
@@ -24,6 +24,10 @@
 .HP
 \fB\-n\fR: Subscribe to nomail version of the list
 .HP
+\fB\-r\fR: Behave as if request arrived via email (internal use)
+.HP
+\fB\-R\fR: Behave as if confirmation arrived via email (internal use)
+.HP
 \fB\-s\fR: Don't send a mail to the subscriber if already subscribed
 .HP
 \fB\-U\fR: Don't switch to the user id of the listdir owner
--- a/man/mlmmj-unsub.1	Mon Mar 07 01:30:02 2011 +1100
+++ b/man/mlmmj-unsub.1	Sat Sep 10 12:23:40 2011 +1000
@@ -3,11 +3,13 @@
 mlmmj-unsub \- manual page for mlmmj-unsub
 .SH SYNOPSIS
 .B mlmmj-unsub
-\fI-L /path/to/list -a john@doe.org \fR[\fI-c\fR | \fI-C\fR] [\fI-h\fR]
-\fR[\fI-d\fR | \fI-n\fR] [\fI-V\fR]
+\fI-L /path/to/list -a john@doe.org\fR [\fI-b\fR] [\fI-c\fR | \fI-C\fR]
+[\fI-d\fR | \fI-n\fR] [\fI-h\fR] [\fI-r\fR | \fI-R\fR] [\fI-s\fR] [\fI-U\fR] [\fI-V\fR]
 .HP
 \fB\-a\fR: Email address to unsubscribe
 .HP
+\fB\-b\fR: Behave as if unsubscription is due to bouncing (internal use)
+.HP
 \fB\-c\fR: Send goodbye mail
 .HP
 \fB\-C\fR: Request mail confirmation
@@ -20,6 +22,10 @@
 .HP
 \fB\-n\fR: Unsubscribe from the nomail version of the list
 .HP
+\fB\-r\fR: Behave as if request arrived via email (internal use)
+.HP
+\fB\-R\fR: Behave as if confirmation arrived via email (internal use)
+.HP
 \fB\-s\fR: Don't send a mail to the address if not subscribed
 .HP
 \fB\-U\fR: Don't switch to the user id of the listdir owner
--- a/src/listcontrol.c	Mon Mar 07 01:30:02 2011 +1100
+++ b/src/listcontrol.c	Sat Sep 10 12:23:40 2011 +1000
@@ -212,7 +212,7 @@
 			log_error(LOG_ARGS, "A subcribe-digest request was"
 				" denied");
 			send_help(listdir, fromemails->emaillist[0],
-				mlmmjsend, "nodigest", "sub-deny-digest");
+				mlmmjsend, "deny", "sub", "disabled", "digest", "sub-deny-digest");
 			return -1;
 		}
 		log_oper(listdir, OPLOGFNAME, "mlmmj-sub: request for digest"
@@ -222,7 +222,7 @@
 				"-L", listdir,
 				"-a", fromemails->emaillist[0],
 				"-d",
-				subswitch, (char *)NULL);
+				"-r", subswitch, (char *)NULL);
 		log_error(LOG_ARGS, "execlp() of '%s' failed",
 					mlmmjsub);
 		exit(EXIT_FAILURE);
@@ -249,7 +249,7 @@
 			log_error(LOG_ARGS, "A subcribe-nomail request was"
 				" denied");
 			send_help(listdir, fromemails->emaillist[0],
-				mlmmjsend, "nonomail", "sub-deny-nomail");
+				mlmmjsend, "deny", "sub", "disabled", "nomail", "sub-deny-nomail");
 			return -1;
 		}
 		log_oper(listdir, OPLOGFNAME, "mlmmj-sub: request for nomail"
@@ -259,7 +259,7 @@
 				"-L", listdir,
 				"-a", fromemails->emaillist[0],
 				"-n",
-				subswitch, (char *)NULL);
+				"-r", subswitch, (char *)NULL);
 		log_error(LOG_ARGS, "execlp() of '%s' failed",
 					mlmmjsub);
 		exit(EXIT_FAILURE);
@@ -287,7 +287,7 @@
 		execlp(mlmmjsub, mlmmjsub,
 				"-L", listdir,
 				"-a", fromemails->emaillist[0],
-				subswitch, (char *)NULL);
+				"-r", subswitch, (char *)NULL);
 		log_error(LOG_ARGS, "execlp() of '%s' failed",
 					mlmmjsub);
 		exit(EXIT_FAILURE);
@@ -315,7 +315,7 @@
 				"-L", listdir,
 				"-a", tmpstr,
 				"-d",
-				"-c", (char *)NULL);
+				"-R", "-c", (char *)NULL);
 		log_error(LOG_ARGS, "execlp() of '%s' failed",
 				mlmmjsub);
 		exit(EXIT_FAILURE);
@@ -343,7 +343,7 @@
 				"-L", listdir,
 				"-a", tmpstr,
 				"-n",
-				"-c", (char *)NULL);
+				"-R", "-c", (char *)NULL);
 		log_error(LOG_ARGS, "execlp() of '%s' failed",
 				mlmmjsub);
 		exit(EXIT_FAILURE);
@@ -371,7 +371,7 @@
 		execlp(mlmmjsub, mlmmjsub,
 				"-L", listdir,
 				"-a", tmpstr,
-				"-c", (char *)NULL);
+				"-R", "-c", (char *)NULL);
 		log_error(LOG_ARGS, "execlp() of '%s' failed",
 				mlmmjsub);
 		exit(EXIT_FAILURE);
@@ -400,7 +400,7 @@
 				"-L", listdir,
 				"-a", fromemails->emaillist[0],
 				"-d",
-				subswitch, (char *)NULL);
+				"-r", subswitch, (char *)NULL);
 		log_error(LOG_ARGS, "execlp() of '%s' failed",
 				mlmmjunsub);
 		exit(EXIT_FAILURE);
@@ -429,7 +429,7 @@
 				"-L", listdir,
 				"-a", fromemails->emaillist[0],
 				"-n",
-				subswitch, (char *)NULL);
+				"-r", subswitch, (char *)NULL);
 		log_error(LOG_ARGS, "execlp() of '%s' failed",
 				mlmmjunsub);
 		exit(EXIT_FAILURE);
@@ -457,7 +457,7 @@
 		execlp(mlmmjunsub, mlmmjunsub,
 				"-L", listdir,
 				"-a", fromemails->emaillist[0],
-				subswitch, (char *)NULL);
+				"-r", subswitch, (char *)NULL);
 		log_error(LOG_ARGS, "execlp() of '%s' failed",
 				mlmmjunsub);
 		exit(EXIT_FAILURE);
@@ -491,7 +491,7 @@
 				"-L", listdir,
 				"-a", tmpstr,
 				"-d",
-				"-c", (char *)NULL);
+				"-R", "-c", (char *)NULL);
 		log_error(LOG_ARGS, "execlp() of '%s' failed",
 				mlmmjunsub);
 		exit(EXIT_FAILURE);
@@ -525,7 +525,7 @@
 				"-L", listdir,
 				"-a", tmpstr,
 				"-n",
-				"-c", (char *)NULL);
+				"-R", "-c", (char *)NULL);
 		log_error(LOG_ARGS, "execlp() of '%s' failed",
 				mlmmjunsub);
 		exit(EXIT_FAILURE);
@@ -559,7 +559,7 @@
 		execlp(mlmmjunsub, mlmmjunsub,
 				"-L", listdir,
 				"-a", tmpstr,
-				"-c", (char *)NULL);
+				"-R", "-c", (char *)NULL);
 		log_error(LOG_ARGS, "execlp() of '%s' failed",
 				mlmmjunsub);
 		exit(EXIT_FAILURE);
@@ -664,7 +664,8 @@
 		}
 		log_oper(listdir, OPLOGFNAME, "%s requested help",
 				fromemails->emaillist[0]);
-		send_help(listdir, fromemails->emaillist[0], mlmmjsend, "help", "listhelp");
+		send_help(listdir, fromemails->emaillist[0], mlmmjsend,
+				"help", NULL, NULL, NULL, "listhelp");
 		break;
 
        /* listname+faq@domain.tld */
@@ -679,7 +680,8 @@
                }
                log_oper(listdir, OPLOGFNAME, "%s requested faq",
                                fromemails->emaillist[0]);
-               send_help(listdir, fromemails->emaillist[0], mlmmjsend, "faq", "listfaq");
+               send_help(listdir, fromemails->emaillist[0], mlmmjsend,
+               		"faq", NULL, NULL, NULL, "listfaq");
                break;
 
 	/* listname+get-INDEX@domain.tld */
--- a/src/mlmmj-bounce.c	Mon Mar 07 01:30:02 2011 +1100
+++ b/src/mlmmj-bounce.c	Sat Sep 10 12:23:40 2011 +1000
@@ -145,8 +145,9 @@
 	}
 
 	maildata[1] = indexstr;
-	queuefilename = prepstdreply(listdir, "bounce-probe", "$listowner$",
-					myaddr, NULL, 1, maildata, NULL);
+	queuefilename = prepstdreply(listdir,
+			"probe", NULL, NULL, NULL, "bounce-probe",
+			"$listowner$", myaddr, NULL, 1, maildata, NULL);
 	MY_ASSERT(queuefilename);
 	myfree(indexstr);
 
--- a/src/mlmmj-maintd.c	Mon Mar 07 01:30:02 2011 +1100
+++ b/src/mlmmj-maintd.c	Sat Sep 10 12:23:40 2011 +1000
@@ -754,7 +754,7 @@
 		} else {
 			execlp(mlmmjunsub, mlmmjunsub,
 					"-L", listdir,
-					"-a", address, (char *)NULL);
+					"-b", "-a", address, (char *)NULL);
 			log_error(LOG_ARGS, "Could not execlp %s",
 						mlmmjunsub);
 			return 1;
--- a/src/mlmmj-process.c	Mon Mar 07 01:30:02 2011 +1100
+++ b/src/mlmmj-process.c	Sat Sep 10 12:23:40 2011 +1000
@@ -71,9 +71,23 @@
 };
 
 
+enum modreason {
+	MODNONSUBPOSTS,
+	ACCESS,
+	MODERATED
+};
+
+
+static char *modreason_strs[] = {
+	"modnonsubposts",
+	"access",
+	"moderated"
+};
+
+
 void newmoderated(const char *listdir, const char *mailfilename,
 		  const char *mlmmjsend, const char *efromsender,
-		  size_t tokencount, char **data)
+		  size_t tokencount, char **data, enum modreason modreason)
 {
 	size_t i;
 	char *from, *listfqdn, *listname, *moderators = NULL;
@@ -142,9 +156,10 @@
 	myfree(listname);
 	myfree(listfqdn);
 
-	queuefilename = prepstdreply(listdir, "moderation", "$listowner$",
-				     to, replyto, tokencount, maildata,
-				     mailfilename);
+	queuefilename = prepstdreply(listdir,
+			"moderate", "post", modreason_strs[modreason], NULL,
+			"moderation", "$listowner$", to, replyto,
+			tokencount, maildata, mailfilename);
 
 	/* we might need to exec more than one mlmmj-send */
 	
@@ -184,8 +199,9 @@
 
 	/* send mail to poster that the list is moderated */
 
-	queuefilename = prepstdreply(listdir, "moderation-poster",
-				     "$listowner$", efromsender,
+	queuefilename = prepstdreply(listdir,
+			"wait", "post", modreason_strs[modreason], NULL,
+			"moderation-poster", "$listowner$", efromsender,
 				     NULL, tokencount-1, maildata+2, mailfilename);
 
 	execlp(mlmmjsend, mlmmjsend,
@@ -382,6 +398,7 @@
 int main(int argc, char **argv)
 {
 	int i, j, opt, noprocess = 0, moderated = 0;
+	enum modreason modreason;
 	int hdrfd, footfd, rawmailfd, donemailfd, omitfd;
 	int subonlypost = 0, addrtocc = 1, intocc = 0, modnonsubposts = 0;
 	int maxmailsize = 0;
@@ -715,6 +732,7 @@
 					"bounces-help@", listfqdn);
 			maildata[5] = maxmailsizestr;
 			queuefilename = prepstdreply(listdir,
+					"deny", "post", "maxmailsize", NULL,
 					"maxmailsize", "$listowner$",
 					fromemails.emaillist[0],
 					NULL, 1, maildata+4, donemailname);
@@ -830,9 +848,10 @@
 		listfqdn = genlistfqdn(listaddr);
 		fromaddr = concatstr(4, listname, listdelim, "bounces-help@",
 				     listfqdn);
-		queuefilename = prepstdreply(listdir, "notintocc",
-					"$listowner$", fromemails.emaillist[0],
-					     NULL, 2, maildata, donemailname);
+		queuefilename = prepstdreply(listdir,
+				"deny", "post", "notintocc", NULL, "notintocc",
+				"$listowner$", fromemails.emaillist[0], NULL,
+				2, maildata, donemailname);
 		MY_ASSERT(queuefilename)
 		myfree(listdelim);
 		myfree(listname);
@@ -869,6 +888,7 @@
 					"modnonsubposts");
 			if(modnonsubposts) {
 				moderated = 1;
+				modreason = MODNONSUBPOSTS;
 				goto startaccess;
 			}
 
@@ -890,9 +910,11 @@
 			listfqdn = genlistfqdn(listaddr);
 			fromaddr = concatstr(4, listname, listdelim,
 					"bounces-help@", listfqdn);
-			queuefilename = prepstdreply(listdir, "subonlypost",
-					"$listowner$", fromemails.emaillist[0],
-						     NULL, 2, maildata, donemailname);
+			queuefilename = prepstdreply(listdir,
+					"deny", "post", "subonlypost", NULL,
+					"subonlypost", "$listowner$",
+					fromemails.emaillist[0], NULL,
+					2, maildata, donemailname);
 			MY_ASSERT(queuefilename)
 			myfree(listaddr);
 			myfree(listdelim);
@@ -913,8 +935,12 @@
 
 startaccess:
 
-	if(!moderated)
-		moderated = statctrl(listdir, "moderated");
+	if(!moderated) {
+		if(statctrl(listdir, "moderated")) {
+			moderated = 1;
+			modreason = MODERATED;
+		}
+	}
 
 	noaccessdenymails = statctrl(listdir, "noaccessdenymails");
 
@@ -944,10 +970,11 @@
 			listfqdn = genlistfqdn(listaddr);
 			fromaddr = concatstr(4, listname, listdelim,
 					"bounces-help@", listfqdn);
-			queuefilename = prepstdreply(listdir, "access",
-							"$listowner$",
-							fromemails.emaillist[0],
-						     NULL, 2, maildata, donemailname);
+			queuefilename = prepstdreply(listdir,
+					"deny", "post", "access", NULL,
+					"access", "$listowner$",
+					fromemails.emaillist[0], NULL,
+					2, maildata, donemailname);
 			MY_ASSERT(queuefilename)
 			myfree(listaddr);
 			myfree(listdelim);
@@ -968,6 +995,7 @@
 			exit(EXIT_FAILURE);
 		} else if (accret == MODERATE) {
 			moderated = 1;
+			modreason = ACCESS;
 		} else if (accret == DISCARD) {
 	                discardname = concatstr(3, listdir,
                                 "/queue/discarded/", randomstr);
@@ -1024,7 +1052,8 @@
 			fsync(omitfd);
 			close(omitfd);
 		}
-		newmoderated(listdir, mqueuename, mlmmjsend, efrom, 2, maildata);
+		newmoderated(listdir, mqueuename,
+				mlmmjsend, efrom, 2, maildata, modreason);
 		return EXIT_SUCCESS;
 	}
 
--- a/src/mlmmj-sub.c	Mon Mar 07 01:30:02 2011 +1100
+++ b/src/mlmmj-sub.c	Sat Sep 10 12:23:40 2011 +1000
@@ -50,6 +50,22 @@
 #include "ctrlvalues.h"
 #include "chomp.h"
 
+char *subtype_strs[] = {
+	"normal",
+	"digest",
+	"nomail",
+	"file",
+	"all"
+};
+
+char * subreason_strs[] = {
+	"request",
+	"confirm",
+	"permit",
+	"admin",
+	"bouncing"
+};
+
 void moderate_sub(const char *listdir, const char *listaddr,
 		const char *listdelim, const char *subaddr,
 		const char *mlmmjsend, enum subtype typesub)
@@ -146,7 +162,8 @@
 	maildata[3] = replyto;
 	maildata[5] = moderators;
 
-	queuefilename = prepstdreply(listdir, "submod-moderator",
+	queuefilename = prepstdreply(listdir,
+			"gatekeep", "sub", NULL, NULL, "submod-moderator",
 				"$listowner$", to, replyto, 3, maildata, NULL);
 	
 	myfree(maildata[1]);
@@ -188,8 +205,9 @@
 	/* send mail to requester that the list is submod'ed */
 
 	from = concatstr(4, listname, listdelim, "bounces-help@", listfqdn);
-	queuefilename = prepstdreply(listdir, "submod-requester", "$listowner$",
-					subaddr, NULL, 0, NULL, NULL);
+	queuefilename = prepstdreply(listdir,
+			"wait", "sub", NULL, NULL, "submod-requester",
+			"$listowner$", subaddr, NULL, 0, NULL, NULL);
 	
 	myfree(listname);
 	myfree(listfqdn);
@@ -256,7 +274,7 @@
 
 void confirm_sub(const char *listdir, const char *listaddr,
 		const char *listdelim, const char *subaddr,
-		const char *mlmmjsend, enum subtype typesub)
+		const char *mlmmjsend, enum subtype typesub, enum subreason reasonsub)
 {
 	char *queuefilename, *fromaddr, *listname, *listfqdn, *listtext;
 
@@ -281,8 +299,11 @@
 			break;
 	}
 
-	queuefilename = prepstdreply(listdir, listtext, "$helpaddr$",
-				     subaddr, NULL, 0, NULL, NULL);
+	queuefilename = prepstdreply(listdir,
+			"finish", "sub",
+			subreason_strs[reasonsub], subtype_strs[typesub],
+			listtext, "$helpaddr$", subaddr, NULL,
+			0, NULL, NULL);
 	MY_ASSERT(queuefilename);
 	myfree(listtext);
 
@@ -298,7 +319,7 @@
 
 void notify_sub(const char *listdir, const char *listaddr,
 		const char *listdelim, const char *subaddr,
-		const char *mlmmjsend, enum subtype typesub)
+		const char *mlmmjsend, enum subtype typesub, enum subreason reasonsub)
 {
 	char *maildata[2] = { "newsub", NULL };
 	char *listfqdn, *listname, *fromaddr, *tostr;
@@ -328,8 +349,11 @@
 			break;
 	}
 
-	queuefilename = prepstdreply(listdir, listtext, "$listowner$",
-				"$listowner$", NULL, 1, maildata, NULL);
+	queuefilename = prepstdreply(listdir,
+			"notify", "sub",
+			subreason_strs[reasonsub], subtype_strs[typesub],
+			listtext, "$listowner$", "$listowner$", NULL,
+			1, maildata, NULL);
 	MY_ASSERT(queuefilename)
 	myfree(listtext);
 	myfree(maildata[1]);
@@ -346,7 +370,7 @@
 
 void generate_subconfirm(const char *listdir, const char *listaddr,
 			 const char *listdelim, const char *subaddr,
-			 const char *mlmmjsend, enum subtype typesub)
+			 const char *mlmmjsend, enum subtype typesub, enum subreason reasonsub)
 {
 	int subconffd;
 	char *confirmaddr, *listname, *listfqdn, *confirmfilename = NULL;
@@ -415,8 +439,11 @@
 	maildata[1] = mystrdup(subaddr);
 	maildata[3] = mystrdup(confirmaddr);
 
-	queuefilename = prepstdreply(listdir, listtext, "$helpaddr$", subaddr,
-				     confirmaddr, 2, maildata, NULL);
+	queuefilename = prepstdreply(listdir,
+			"confirm", "sub",
+			subreason_strs[reasonsub], subtype_strs[typesub],
+			listtext, "$helpaddr$", subaddr, confirmaddr,
+			2, maildata, NULL);
 
 	myfree(maildata[1]);
 	myfree(maildata[3]);
@@ -436,8 +463,8 @@
 
 static void print_help(const char *prg)
 {
-	printf("Usage: %s -L /path/to/list [-a john@doe.org | -m str]\n"
-	       "       [-c] [-C] [-f] [-h] [-L] [-d | -n] [-s] [-U] [-V]\n"
+	printf("Usage: %s -L /path/to/list {-a john@doe.org | -m str}\n"
+	       "       [-c | -C] [-f] [-h] [-L] [-d | -n] [-r | -R] [-s] [-U] [-V]\n"
 	       " -a: Email address to subscribe \n"
 	       " -c: Send welcome mail\n"
 	       " -C: Request mail confirmation\n"
@@ -447,7 +474,9 @@
 	       " -L: Full path to list directory\n"
 	       " -m: moderation string\n"
 	       " -n: Subscribe to no mail version of list\n", prg);
-	printf(" -s: Don't send a mail to subscriber if already subscribed\n"
+	printf(" -r: Behave as if request arrived via email (internal use)\n"
+	       " -R: Behave as if confirmation arrived via email (internal use)\n"
+	       " -s: Don't send a mail to subscriber if already subscribed\n"
 	       " -U: Don't switch to the user id of the listdir owner\n"
 	       " -V: Print version\n"
 	       "When no options are specified, subscription may be "
@@ -468,8 +497,9 @@
 	fromaddr = concatstr(4, listname, listdelim, "bounces-help@", listfqdn);
 	myfree(listdelim);
 
-	queuefilename = prepstdreply(listdir, "sub-subscribed", "$helpaddr$",
-				     subaddr, NULL, 0, NULL, NULL);
+	queuefilename = prepstdreply(listdir,
+			"deny", "sub", "subbed", NULL, "sub-subscribed",
+			"$helpaddr$", subaddr, NULL, 0, NULL, NULL);
 	MY_ASSERT(queuefilename);
 
 	myfree(listaddr);
@@ -502,6 +532,7 @@
 	pid_t pid, childpid;
 	uid_t uid;
 	enum subtype typesub = SUB_NORMAL;
+	enum subreason reasonsub = SUB_ADMIN;
 
 	CHECKFULLPATH(argv[0]);
 
@@ -511,7 +542,7 @@
 	mlmmjsend = concatstr(2, bindir, "/mlmmj-send");
 	myfree(bindir);
 
-	while ((opt = getopt(argc, argv, "hcCdfm:nsVUL:a:")) != -1) {
+	while ((opt = getopt(argc, argv, "hcCdfm:nsVUL:a:rR")) != -1) {
 		switch(opt) {
 		case 'a':
 			address = optarg;
@@ -540,6 +571,12 @@
 		case 'n':
 			nomail = 1;
 			break;
+		case 'r':
+			reasonsub = SUB_REQUEST;
+			break;
+		case 'R':
+			reasonsub = SUB_CONFIRM;
+			break;
 		case 's':
 			nogensubscribed = 1;
 			break;
@@ -564,8 +601,10 @@
 		exit(EXIT_FAILURE);
 	}
 
-	if(modstr)
+	if(modstr) {
 		getaddrandtype(listdir, modstr, &address, &typesub);
+		reasonsub = SUB_PERMIT;
+	}
 
 	if(strchr(address, '@') == NULL) {
 		log_error(LOG_ARGS, "No '@' sign in '%s', not subscribing",
@@ -590,6 +629,12 @@
 		exit(EXIT_FAILURE);
 	}
 
+	if(reasonsub == SUB_CONFIRM && subconfirm) {
+		fprintf(stderr, "Cannot specify both -C and -R\n");
+		fprintf(stderr, "%s -h for help\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
 	/* Make the address lowercase */
 	lowcaseaddr = mystrdup(address);
 	i = 0;
@@ -690,7 +735,7 @@
 			unlink(sublockname);
 			myfree(sublockname);
 			generate_subconfirm(listdir, listaddr, listdelim,
-					    address, mlmmjsend, typesub);
+					    address, mlmmjsend, typesub, reasonsub);
 		} else {
 			if(modstr == NULL)
 				submod = !force && statctrl(listdir, "submod");
@@ -734,7 +779,7 @@
 		if(childpid < 0) {
 			log_error(LOG_ARGS, "Could not fork; owner not notified");
 			confirm_sub(listdir, listaddr, listdelim, address,
-					mlmmjsend, typesub);
+					mlmmjsend, typesub, reasonsub);
 		}
 		
 		if(childpid > 0) {
@@ -746,7 +791,7 @@
 		/* child confirms subscription */
 		if(childpid == 0)
 			confirm_sub(listdir, listaddr, listdelim, address,
-					mlmmjsend, typesub);
+					mlmmjsend, typesub, reasonsub);
 	}
 
 	notifysub = statctrl(listdir, "notifysub");
@@ -754,7 +799,7 @@
 	/* Notify list owner about subscription */
 	if (notifysub)
 		notify_sub(listdir, listaddr, listdelim, address, mlmmjsend,
-				typesub);
+				typesub, reasonsub);
 
 	myfree(address);
 	myfree(listaddr);
--- a/src/mlmmj-unsub.c	Mon Mar 07 01:30:02 2011 +1100
+++ b/src/mlmmj-unsub.c	Sat Sep 10 12:23:40 2011 +1000
@@ -50,7 +50,8 @@
 
 void confirm_unsub(const char *listdir, const char *listaddr,
 		   const char *listdelim, const char *subaddr,
-		   const char *mlmmjsend, enum subtype typesub)
+		   const char *mlmmjsend,
+		   enum subtype typesub, enum subreason reasonsub)
 {
 	char *queuefilename, *fromaddr, *listname, *listfqdn, *listtext;
 
@@ -75,8 +76,10 @@
 			break;
 	}
 
-	queuefilename = prepstdreply(listdir, listtext, "$helpaddr$",
-				     subaddr, NULL, 0, NULL, NULL);
+	queuefilename = prepstdreply(listdir,
+			"finish", "unsub",
+			subreason_strs[reasonsub], subtype_strs[typesub],
+			listtext, "$helpaddr$", subaddr, NULL, 0, NULL, NULL);
 	MY_ASSERT(queuefilename);
 	myfree(listtext);
 
@@ -93,7 +96,8 @@
 
 void notify_unsub(const char *listdir, const char *listaddr,
 		  const char *listdelim, const char *subaddr,
-		  const char *mlmmjsend, enum subtype typesub)
+		  const char *mlmmjsend,
+		  enum subtype typesub, enum subreason reasonsub)
 {
         char *maildata[4] = { "oldsub", NULL };
         char *listfqdn, *listname, *fromaddr, *tostr;
@@ -123,8 +127,11 @@
 			break;
 	}
 	
-	queuefilename = prepstdreply(listdir, listtext, "$listowner$",
-				     "$listowner$", NULL, 1, maildata, NULL);
+	queuefilename = prepstdreply(listdir,
+			"notify", "unsub",
+			subreason_strs[reasonsub], subtype_strs[typesub],
+			listtext, "$listowner$", "$listowner$", NULL,
+			1, maildata, NULL);
 	MY_ASSERT(queuefilename);
 	myfree(listtext);
 	myfree(maildata[1]);
@@ -143,7 +150,8 @@
 
 void generate_unsubconfirm(const char *listdir, const char *listaddr,
 			   const char *listdelim, const char *subaddr,
-			   const char *mlmmjsend, enum subtype typesub)
+			   const char *mlmmjsend,
+			   enum subtype typesub, enum subreason reasonsub)
 {
 	char *confirmaddr, *listname, *listfqdn, *tmpstr;
 	char *queuefilename, *fromaddr;
@@ -212,8 +220,11 @@
 	maildata[1] = mystrdup(subaddr);
 	maildata[3] = mystrdup(confirmaddr);
 
-	queuefilename = prepstdreply(listdir, listtext, "$helpaddr$", subaddr,
-				     confirmaddr, 2, maildata, NULL);
+	queuefilename = prepstdreply(listdir,
+			"confirm", "unsub",
+			subreason_strs[reasonsub], subtype_strs[typesub],
+			listtext, "$helpaddr$", subaddr, confirmaddr,
+			2, maildata, NULL);
 
 	myfree(maildata[1]);
 	myfree(maildata[3]);
@@ -275,19 +286,22 @@
 static void print_help(const char *prg)
 {
 	printf("Usage: %s -L /path/to/list -a john@doe.org\n"
-	       "       [-c] [-C] [-h] [-L] [-d | -n] [-s] [-V]\n"
+	       "       [-b] [-c | -C] [-h] [-L] [-d | -n] [-r | -R] [-s] [-V]\n"
 	       " -a: Email address to unsubscribe \n"
+	       " -b: Behave as if unsubscription is due to bouncing (internal use)\n"
 	       " -c: Send goodbye mail\n"
 	       " -C: Request mail confirmation\n"
 	       " -d: Unsubscribe from digest of list\n"
 	       " -h: This help\n"
 	       " -L: Full path to list directory\n"
-	       " -n: Unsubscribe from no mail version of list\n"
+	       " -n: Unsubscribe from no mail version of list\n", prg);
+	printf(" -r: Behave as if request arrived via email (internal use)\n"
+	       " -R: Behave as if confirmation arrived via email (internal use)\n"
 	       " -s: Don't send a mail to the address if not subscribed\n"
 	       " -U: Don't switch to the user id of the listdir owner\n"
 	       " -V: Print version\n"
 	       "When no options are specified, unsubscription silently "
-	       "happens\n", prg);
+	       "happens\n");
 	exit(EXIT_SUCCESS);
 }
 
@@ -304,8 +318,10 @@
 	fromaddr = concatstr(4, listname, listdelim, "bounces-help@", listfqdn);
 	myfree(listdelim);
 
-	queuefilename = prepstdreply(listdir, "unsub-notsubscribed",
-				     "$helpaddr$", subaddr, NULL, 0, NULL, NULL);
+	queuefilename = prepstdreply(listdir,
+			"deny", "unsub", "unsubbed", NULL,
+			"unsub-notsubscribed", "$helpaddr$", subaddr, NULL,
+			0, NULL, NULL);
 	MY_ASSERT(queuefilename);
 
 	myfree(listaddr);
@@ -338,6 +354,7 @@
 	struct dirent *dp;
 	pid_t pid, childpid;
 	enum subtype typesub = SUB_NORMAL;
+	enum subreason reasonsub = SUB_ADMIN;
 	uid_t uid;
 	struct stat st;
 
@@ -349,7 +366,7 @@
 	mlmmjsend = concatstr(2, bindir, "/mlmmj-send");
 	myfree(bindir);
 
-	while ((opt = getopt(argc, argv, "hcCdnVUL:a:s")) != -1) {
+	while ((opt = getopt(argc, argv, "hcCdnVUL:a:sbrR")) != -1) {
 		switch(opt) {
 		case 'L':
 			listdir = optarg;
@@ -360,6 +377,9 @@
 		case 'a':
 			address = optarg;
 			break;
+		case 'b':
+			reasonsub = SUB_BOUNCING;
+			break;
 		case 'c':
 			confirmunsub = 1;
 			break;
@@ -372,6 +392,12 @@
 		case 'h':
 			print_help(argv[0]);
 			break;
+		case 'r':
+			reasonsub = SUB_REQUEST;
+			break;
+		case 'R':
+			reasonsub = SUB_CONFIRM;
+			break;
 		case 's':
 			nogennotsubscribed = 1;
 			break;
@@ -406,6 +432,18 @@
 		exit(EXIT_FAILURE);
 	}
 
+	if(reasonsub == SUB_CONFIRM && unsubconfirm) {
+		fprintf(stderr, "Cannot specify both -C and -R\n");
+		fprintf(stderr, "%s -h for help\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if(reasonsub == SUB_BOUNCING && unsubconfirm) {
+		fprintf(stderr, "Cannot specify both -C and -b\n");
+		fprintf(stderr, "%s -h for help\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
 	/* Make the address lowercase */
 	lowcaseaddr = mystrdup(address);
 	i = 0;
@@ -467,7 +505,7 @@
 	listdelim = getlistdelim(listdir);
 	if(unsubconfirm)
 		generate_unsubconfirm(listdir, listaddr, listdelim, address,
-				mlmmjsend, typesub);
+				mlmmjsend, typesub, reasonsub);
 
 	if((subddir = opendir(subddirname)) == NULL) {
 		log_error(LOG_ARGS, "Could not opendir(%s)",
@@ -603,7 +641,8 @@
 			if(childpid < 0) {
 				log_error(LOG_ARGS, "Could not fork");
 				confirm_unsub(listdir, listaddr, listdelim,
-						address, mlmmjsend, digest);
+						address, mlmmjsend,
+						typesub, reasonsub);
 			}
 
 			if(childpid > 0) {
@@ -615,7 +654,8 @@
 			/* child confirms subscription */
 			if(childpid == 0)
 				confirm_unsub(listdir, listaddr, listdelim,
-						address, mlmmjsend, digest);
+						address, mlmmjsend,
+						typesub, reasonsub);
 		}
         }
 
@@ -626,7 +666,7 @@
         /* Notify list owner about subscription */
         if (notifysub)
                 notify_unsub(listdir, listaddr, listdelim, address, mlmmjsend,
-				typesub);
+				typesub, reasonsub);
 
 	myfree(listaddr);
 	myfree(listdelim);
--- a/src/prepstdreply.c	Mon Mar 07 01:30:02 2011 +1100
+++ b/src/prepstdreply.c	Sat Sep 10 12:23:40 2011 +1000
@@ -225,26 +225,51 @@
 	if (fd >= 0)
 		return fd;
 
-	log_error(LOG_ARGS, "Could not open listtext '%s'", filename);
 	return -1;
 }
 
 
-char *prepstdreply(const char *listdir, const char *filename, const char *from,
-		   const char *to, const char *replyto, size_t tokencount,
-		   char **data, const char *mailname)
+char *prepstdreply(const char *listdir, const char *purpose, const char *action,
+		   const char *reason, const char *type, const char *compat,
+		   const char *from, const char *to, const char *replyto,
+		   size_t tokencount, char **data, const char *mailname)
 {
-	size_t i, len;
+	size_t filenamelen, i, len;
 	int infd, outfd, mailfd;
-	char *listaddr, *listdelim, *tmp, *retstr = NULL;
+	char *filename, *listaddr, *listdelim, *tmp, *retstr = NULL;
 	char *listfqdn, *line, *utfline, *utfsub, *utfsub2;
 	char *str = NULL;
 	char **moredata;
 	char *headers[10] = { NULL }; /* relies on NULL to flag end */
 
-	if ((infd = open_listtext(listdir, filename)) < 0) {
+	filename = concatstr(7,purpose,"-",action,"-",reason,"-",type);
+	filenamelen = strlen(filename);
+	do {
+		if ((infd = open_listtext(listdir, filename)) >= 0) break;
+		len = type ? strlen(type) : 0;
+		filename[filenamelen-len-1] = '\0';
+		if ((infd = open_listtext(listdir, filename)) >= 0) break;
+		filename[filenamelen-len-1] = '-';
+		filenamelen -= len + 1;
+		len = reason ? strlen(reason) : 0;
+		filename[filenamelen-len-1] = '\0';
+		if ((infd = open_listtext(listdir, filename)) >= 0) break;
+		filename[filenamelen-len-1] = '-';
+		filenamelen -= len + 1;
+		len = action ? strlen(action) : 0;
+		filename[filenamelen-len-1] = '\0';
+		if ((infd = open_listtext(listdir, filename)) >= 0) break;
+		filename[filenamelen-len-1] = '-';
+		filenamelen -= len + 1;
+		if ((infd = open_listtext(listdir, compat)) >= 0) {
+			myfree(filename);
+			filename = mystrdup(compat);
+			break;
+		}
+		log_error(LOG_ARGS, "Could not open listtext '%s'", filename);
+		myfree(filename);
 		return NULL;
-	}
+	} while (0);
 
 	listaddr = getlistaddr(listdir);
 	listdelim = getlistdelim(listdir);
@@ -267,6 +292,7 @@
 		myfree(listdelim);
 		myfree(listfqdn);
 		myfree(retstr);
+		myfree(filename);
 		return NULL;
 	}
 
@@ -501,5 +527,7 @@
 	}
 	myfree(moredata);
 
+	myfree(filename);
+
 	return retstr;
 }
--- a/src/send_digest.c	Mon Mar 07 01:30:02 2011 +1100
+++ b/src/send_digest.c	Sat Sep 10 12:23:40 2011 +1000
@@ -226,7 +226,7 @@
 	
 	txtfd = open_listtext(listdir, "digest");
 	if (txtfd < 0) {
-		log_error(LOG_ARGS, "Notice: Could not open std mail digest");
+		log_error(LOG_ARGS, "Could not open listtext 'digest'");
 	}
 
 	subst_data[0] = "digestfirst";
--- a/src/send_help.c	Mon Mar 07 01:30:02 2011 +1100
+++ b/src/send_help.c	Sat Sep 10 12:23:40 2011 +1000
@@ -43,7 +43,8 @@
 #include "memory.h"
 
 void send_help(const char *listdir, const char *emailaddr,
-	       const char *mlmmjsend, const char *name, const char *textfile)
+	       const char *mlmmjsend, const char *purpose, const char *action,
+	       const char *reason, const char *type, const char *compat)
 {
 	char *queuefilename, *listaddr, *listdelim, *listname, *listfqdn;
 	char *fromaddr;
@@ -56,10 +57,15 @@
 	fromaddr = concatstr(4, listname, listdelim, "bounces-help@", listfqdn);
 	myfree(listdelim);
 
-	queuefilename = prepstdreply(listdir, textfile, "$listowner$",
-					emailaddr, NULL, 0, NULL, NULL);
+	queuefilename = prepstdreply(listdir,
+			purpose, action, reason, type, compat,
+			"$listowner$", emailaddr, NULL, 0, NULL, NULL);
 	if(queuefilename == NULL) {
-		log_error(LOG_ARGS, "Could not prepare %s mail", name);
+		if (action == NULL) action = "";
+		if (reason == NULL) reason = "";
+		if (type == NULL) type = "";
+		log_error(LOG_ARGS, "Could not prepare %s-%s-%s-%s mail",
+				purpose, action, reason, type);
 		exit(EXIT_FAILURE);
 	}
 	
--- a/src/send_list.c	Mon Mar 07 01:30:02 2011 +1100
+++ b/src/send_list.c	Sat Sep 10 12:23:40 2011 +1000
@@ -98,8 +98,10 @@
 	fromaddr = concatstr(4, listname, listdelim, "bounces-help@", listfqdn);
 	myfree(listdelim);
 
-	queuefilename = prepstdreply(listdir, "listsubs", "$listowner$",
-					emailaddr, NULL, 0, NULL, NULL);
+	queuefilename = prepstdreply(listdir,
+			"list", NULL, NULL, subtype_strs[SUB_ALL],
+			"listsubs", "$listowner$", emailaddr, NULL,
+			0, NULL, NULL);
 	if(queuefilename == NULL) {
 		log_error(LOG_ARGS, "Could not prepare sub list mail");
 		exit(EXIT_FAILURE);