changeset 343:6d1f589dee87

This is post crash commit of working copy mmj. Will test compile etc. in a minute. --- ChangeLog 14 Sep 2004 11:06:43 -0000 1.82 +++ ChangeLog 23 Sep 2004 14:25:13 -0000 @@ -1,6 +1,9 @@ - o Avoid message about changing uid in mlmmj-sub by only saying so when doing - so + o Add support for not archiving the list by touching listdir/control/noarchive + o Add 'nomail' version of lists. Subscribers to the nomail version are + subscribed, but does not get any mail + o Don't talk about changing uid in mlmmj-sub when we're not really doing it o Add sanity checks to disallow denial mails going to the list + o Add digest functionality o Implement -d option for mlmmj-maintd to be able to supply it with a directory containing several listdirs, where mlmmj-maintd then will run maintenance o Chown option and a fix for mlmmj-make-ml.sh. Thanks Ingo Lameter
author mmj
date Fri, 24 Sep 2004 00:29:31 +1000
parents 6b3e1584a0fd
children 410780151412
files ChangeLog TUNABLES VERSION include/mlmmj-sub.h include/mlmmj-unsub.h include/mlmmj.h include/subscriberfuncs.h listtexts/notifysub-nomail listtexts/notifyunsub-nomail listtexts/sub-confirm-nomail listtexts/sub-ok-nomail listtexts/unsub-confirm-nomail listtexts/unsub-ok-nomail man/mlmmj-sub.1 man/mlmmj-unsub.1 src/listcontrol.c src/mlmmj-make-ml.sh src/mlmmj-send.c src/mlmmj-sub.c src/mlmmj-unsub.c src/subscriberfuncs.c
diffstat 21 files changed, 522 insertions(+), 147 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Sep 14 21:06:44 2004 +1000
+++ b/ChangeLog	Fri Sep 24 00:29:31 2004 +1000
@@ -1,6 +1,9 @@
- o Avoid message about changing uid in mlmmj-sub by only saying so when doing
-   so
+ o Add support for not archiving the list by touching listdir/control/noarchive
+ o Add 'nomail' version of lists. Subscribers to the nomail version are
+   subscribed, but does not get any mail
+ o Don't talk about changing uid in mlmmj-sub when we're not really doing it
  o Add sanity checks to disallow denial mails going to the list
+ o Add digest functionality
  o Implement -d option for mlmmj-maintd to be able to supply it with a directory
    containing several listdirs, where mlmmj-maintd then will run maintenance
  o Chown option and a fix for mlmmj-make-ml.sh. Thanks Ingo Lameter
--- a/TUNABLES	Tue Sep 14 21:06:44 2004 +1000
+++ b/TUNABLES	Fri Sep 24 00:29:31 2004 +1000
@@ -93,3 +93,8 @@
 
    Here is specified for how long time in seconds an address can bounce before
    it's unsubscribed. Defaults to 432000 seconds, which is 5 days.
+
+ · noarchive			(boolean)
+
+   If this file exists, the mail won't be saved in the archive but simply
+   deleted.
--- a/VERSION	Tue Sep 14 21:06:44 2004 +1000
+++ b/VERSION	Fri Sep 24 00:29:31 2004 +1000
@@ -1,1 +1,1 @@
-1.0.1
+1.1-pre-09182004
--- a/include/mlmmj-sub.h	Tue Sep 14 21:06:44 2004 +1000
+++ b/include/mlmmj-sub.h	Fri Sep 24 00:29:31 2004 +1000
@@ -24,10 +24,13 @@
 #ifndef MLMMJ_SUBSCRIBE_H
 #define MLMMJ_SUBSCRIBE_H
 
+#include <mlmmj.h>
+
 void confirm_sub(const char *listdir, const char *listaddr,
-		 const char *subaddr, const char *mlmmjsend, int digest);
+		 const char *subaddr, const char *mlmmjsend,
+		 enum subtype typesub);
 void generate_subconfirm(const char *listdir, const char *listadr,
 		const char *subaddr, const char *mlmmjsend,
-		int digest);
+		enum subtype typesub);
 
 #endif /* MLMMJ_SUBSCRIBE_H */
--- a/include/mlmmj-unsub.h	Tue Sep 14 21:06:44 2004 +1000
+++ b/include/mlmmj-unsub.h	Fri Sep 24 00:29:31 2004 +1000
@@ -27,10 +27,11 @@
 #include <sys/types.h>
 
 void confirm_unsub(const char *listdir, const char *listaddr,
-		   const char *subaddr, const char *mlmmj, int digest);
+		   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 *subaddr, const char *mlmmjsend,
-			   int digest);
+			   enum subtype typesub);
 
 #endif /* MLMMJ_UNSUBSCRIBE_H */
--- a/include/mlmmj.h	Tue Sep 14 21:06:44 2004 +1000
+++ b/include/mlmmj.h	Fri Sep 24 00:29:31 2004 +1000
@@ -65,6 +65,13 @@
 	char **values;
 };
 
+/* Has to go here, since it's used in many places */
+enum subtype {
+	SUB_NORMAL,
+	SUB_DIGEST,
+	SUB_NOMAIL
+};
+
 void print_version(const char *prg);
 
 #define MY_ASSERT(expression) if (!(expression)) { \
--- a/include/subscriberfuncs.h	Tue Sep 14 21:06:44 2004 +1000
+++ b/include/subscriberfuncs.h	Fri Sep 24 00:29:31 2004 +1000
@@ -25,6 +25,7 @@
 #define SUBSCRIBERFUNC_H
 
 off_t find_subscriber(int fd, const char *address);
+int is_subbed_in(const char *subddirname, const char *address);
 int is_subbed(const char *listdir, const char *address);
 
 #endif /* SUBSCRIBERFUNC_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/listtexts/notifysub-nomail	Fri Sep 24 00:29:31 2004 +1000
@@ -0,0 +1,10 @@
+Hi, this is the mlmmj program managing the mailinglist
+
+*LSTADDR*
+
+
+The following address has just subscribed to the nomail version of the
+mailinglist:
+
+*SUBADDR*
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/listtexts/notifyunsub-nomail	Fri Sep 24 00:29:31 2004 +1000
@@ -0,0 +1,10 @@
+Hi, this is the mlmmj program managing the mailinglist
+
+*LSTADDR*
+
+
+The following address has just unsubscribed from the nomail version of
+mailinglist:
+
+*SUBADDR*
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/listtexts/sub-confirm-nomail	Fri Sep 24 00:29:31 2004 +1000
@@ -0,0 +1,31 @@
+Hi, this is the mlmmj program managing the mailinglist
+
+*LSTADDR*
+
+
+To confirm you want the address
+
+*SUBADDR*
+
+
+added to the nomail version of this list, please send a reply to
+
+*CNFADDR*
+
+
+You are getting this mail because of two possible scenarios:
+
+1) You want to be subscribed to the list without getting any mail from it,
+   because you have other means of reading it, and have sent an email
+   specifically to subscribe
+
+2) You have tried to post to a list that only allows posting from sub-
+   scribers. In this case simply reply to this mail, and you will be
+   able to post to the list with this emailaddress in the future
+
+Your mailer probably automatically replies to this address, when you hit
+the reply button.
+
+This confirmation serves two purposes. It tests that mail can be sent to your
+address. Second, it makes sure someone else did not try and subscribe your
+emailaddress.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/listtexts/sub-ok-nomail	Fri Sep 24 00:29:31 2004 +1000
@@ -0,0 +1,6 @@
+WELCOME! You have been subscribed to the nomail version of the
+
+*LSTADDR*
+
+
+mailinglist.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/listtexts/unsub-confirm-nomail	Fri Sep 24 00:29:31 2004 +1000
@@ -0,0 +1,21 @@
+Hi, this is the mlmmj program managing the mailinglist
+
+*LSTADDR*
+
+
+To confirm you want the address
+
+*SUBADDR*
+
+
+removed from the nomail version of this list, please send a reply to
+
+*CNFADDR*
+
+
+Your mailer probably automatically replies to this address, when you hit
+the reply button.
+
+If you're not subscribed with this list, you will recieve no reply. You can
+see in the From header of a mail to the mailinglist which mail you're sub-
+scribed with.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/listtexts/unsub-ok-nomail	Fri Sep 24 00:29:31 2004 +1000
@@ -0,0 +1,6 @@
+GOODBYE! You have been removed from the nomail version of the
+
+*LSTADDR*
+
+
+mailinglist.
--- a/man/mlmmj-sub.1	Tue Sep 14 21:06:44 2004 +1000
+++ b/man/mlmmj-sub.1	Fri Sep 24 00:29:31 2004 +1000
@@ -3,19 +3,23 @@
 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] [\fI-h\fR]
-[\fI-U\fR] [\fI-V\fR]
+\fI-L /path/to/list -a john@doe.org \fR[\fI-c\fR | \fI-C\fR] \fR[\fI-d\fR | \fI-n\fR]
+[\fI-h\fR] [\fI-U\fR] [\fI-V\fR]
 .HP
 \fB\-a\fR: Email address to subscribe
 .HP
 \fB\-c\fR: Send welcome mail
 .HP
+\fB\-d\fR: Subscribe to digest version of the list
+.HP
 \fB\-C\fR: Request mail confirmation
 .HP
 \fB\-h\fR: This help
 .HP
 \fB\-L\fR: Full path to list directory
 .HP
+\fB\-n\fR: Subscribe to nomail version of the list
+.HP
 \fB\-U\fR: Don't switch to the user id of the listdir owner
 .HP
 \fB\-V\fR: Print version
@@ -28,6 +32,11 @@
 owning the list directory. This is done to make sure that new files created are
 having correct permissions.
 
+The nomail version of the list is a list version where people are subscribed
+like usual, but they won't recieve any postings to the list. This is useful for
+people who read the mailinglist through a news gateway, but want to be able to
+post to the list.
+
 When neither \fB\-c\fR nor \fB\-C\fR are specified, subscription silently
 happens.
 .SH "SEE ALSO"
--- a/man/mlmmj-unsub.1	Tue Sep 14 21:06:44 2004 +1000
+++ b/man/mlmmj-unsub.1	Fri Sep 24 00:29:31 2004 +1000
@@ -4,7 +4,7 @@
 .SH SYNOPSIS
 .B mlmmj-sub
 \fI-L /path/to/list -a john@doe.org \fR[\fI-c\fR | \fI-C\fR] [\fI-h\fR]
-[\fI-V\fR]
+\fR[\fI-d\fR | \fI-n\fR] [\fI-V\fR]
 .HP
 \fB\-a\fR: Email address to unsubscribe
 .HP
@@ -12,10 +12,14 @@
 .HP
 \fB\-C\fR: Request mail confirmation
 .HP
+\fB\-d\fR: Unsubscribe from the digest version of the list
+.HP
 \fB\-h\fR: This help
 .HP
 \fB\-L\fR: Full path to list directory
 .HP
+\fB\-n\fR: Unsubscribe from the nomail version of the list
+.HP
 \fB\-V\fR: Print version
 .SH DESCRIPTION
 This utility is used to unsubscribe people from the specified mailinglist. It
--- a/src/listcontrol.c	Tue Sep 14 21:06:44 2004 +1000
+++ b/src/listcontrol.c	Fri Sep 24 00:29:31 2004 +1000
@@ -44,12 +44,16 @@
 
 enum ctrl_e {
 	CTRL_SUBSCRIBE_DIGEST,
+	CTRL_SUBSCRIBE_NOMAIL,
 	CTRL_SUBSCRIBE,
 	CTRL_CONFSUB_DIGEST,
+	CTRL_CONFSUB_NOMAIL,
 	CTRL_CONFSUB,
 	CTRL_UNSUBSCRIBE_DIGEST,
+	CTRL_UNSUBSCRIBE_NOMAIL,
 	CTRL_UNSUBSCRIBE,
 	CTRL_CONFUNSUB_DIGEST,
+	CTRL_CONFUNSUB_NOMAIL,
 	CTRL_CONFUNSUB,
 	CTRL_BOUNCES,
 	CTRL_MODERATE,
@@ -70,12 +74,16 @@
  * first to match correctly. */
 static struct ctrl_command ctrl_commands[] = {
 	{ "subscribe-digest",   0 },
+	{ "subscribe-nomail",   0 },
 	{ "subscribe",          0 },
 	{ "confsub-digest",     1 },
+	{ "confsub-nomail",     1 },
 	{ "confsub",            1 },
 	{ "unsubscribe-digest", 0 },
+	{ "unsubscribe-nomail", 0 },
 	{ "unsubscribe",        0 },
 	{ "confunsub-digest",   1 },
+	{ "confunsub-nomail",   1 },
 	{ "confunsub",          1 },
 	{ "bounces",            1 },
 	{ "moderate",           1 },
@@ -167,6 +175,24 @@
 		exit(EXIT_FAILURE);
 		break;
 
+	/* listname+subscribe-nomail@domain.tld */
+	case CTRL_SUBSCRIBE_NOMAIL:
+		unlink(mailname);
+		if (closedlist)
+			exit(EXIT_SUCCESS);
+		if (!strchr(fromemails->emaillist[0], '@'))
+			/* Not a valid From: address, silently ignore */
+			exit(EXIT_SUCCESS);
+		execlp(mlmmjsub, mlmmjsub,
+				"-L", listdir,
+				"-a", fromemails->emaillist[0],
+				"-n",
+				"-C", NULL);
+		log_error(LOG_ARGS, "execlp() of '%s' failed",
+					mlmmjsub);
+		exit(EXIT_FAILURE);
+		break;
+
 	/* listname+subscribe@domain.tld */
 	case CTRL_SUBSCRIBE:
 		unlink(mailname);
@@ -209,6 +235,31 @@
 		exit(EXIT_FAILURE);
 		break;
 
+	/* listname+subconf-nomail-COOKIE@domain.tld */
+	case CTRL_CONFSUB_NOMAIL:
+		unlink(mailname);
+		if (closedlist)
+			exit(EXIT_SUCCESS);
+		conffilename = concatstr(3, listdir, "/subconf/", param);
+		myfree(param);
+		if((tmpfd = open(conffilename, O_RDONLY)) < 0) {
+			/* invalid COOKIE, silently ignore */
+			exit(EXIT_SUCCESS);
+		}
+		tmpstr = mygetline(tmpfd);
+		chomp(tmpstr);
+		close(tmpfd);
+		unlink(conffilename);
+		execlp(mlmmjsub, mlmmjsub,
+				"-L", listdir,
+				"-a", tmpstr,
+				"-n",
+				"-c", NULL);
+		log_error(LOG_ARGS, "execlp() of '%s' failed",
+				mlmmjsub);
+		exit(EXIT_FAILURE);
+		break;
+
 	/* listname+subconf-COOKIE@domain.tld */
 	case CTRL_CONFSUB:
 		unlink(mailname);
@@ -251,6 +302,24 @@
 		exit(EXIT_FAILURE);
 		break;
 
+	/* listname+unsubscribe-nomail@domain.tld */
+	case CTRL_UNSUBSCRIBE_NOMAIL:
+		unlink(mailname);
+		if (closedlist)
+			exit(EXIT_SUCCESS);
+		if (!strchr(fromemails->emaillist[0], '@'))
+			/* Not a valid From: address, silently ignore */
+			exit(EXIT_SUCCESS);
+		execlp(mlmmjunsub, mlmmjunsub,
+				"-L", listdir,
+				"-a", fromemails->emaillist[0],
+				"-n",
+				"-C", NULL);
+		log_error(LOG_ARGS, "execlp() of '%s' failed",
+				mlmmjunsub);
+		exit(EXIT_FAILURE);
+		break;
+
 	/* listname+unsubscribe@domain.tld */
 	case CTRL_UNSUBSCRIBE:
 		unlink(mailname);
@@ -293,6 +362,31 @@
 		exit(EXIT_FAILURE);
 		break;
 
+	/* listname+unsubconf-nomail-COOKIE@domain.tld */
+	case CTRL_CONFUNSUB_NOMAIL:
+		unlink(mailname);
+		if (closedlist)
+			exit(EXIT_SUCCESS);
+		conffilename = concatstr(3, listdir, "/unsubconf/", param);
+		myfree(param);
+		if((tmpfd = open(conffilename, O_RDONLY)) < 0) {
+			/* invalid COOKIE, silently ignore */
+			exit(EXIT_SUCCESS);
+		}
+		tmpstr = mygetline(tmpfd);
+		close(tmpfd);
+		chomp(tmpstr);
+		unlink(conffilename);
+		execlp(mlmmjunsub, mlmmjunsub,
+				"-L", listdir,
+				"-a", tmpstr,
+				"-n",
+				"-c", NULL);
+		log_error(LOG_ARGS, "execlp() of '%s' failed",
+				mlmmjunsub);
+		exit(EXIT_FAILURE);
+		break;
+
 	/* listname+unsubconf-COOKIE@domain.tld */
 	case CTRL_CONFUNSUB:
 		unlink(mailname);
--- a/src/mlmmj-make-ml.sh	Tue Sep 14 21:06:44 2004 +1000
+++ b/src/mlmmj-make-ml.sh	Fri Sep 24 00:29:31 2004 +1000
@@ -67,7 +67,8 @@
 mkdir -p $LISTDIR
 
 for DIR in incoming queue queue/discarded archive text subconf unsubconf \
-	   bounce control moderation subscribers.d digesters.d requeue
+	   bounce control moderation subscribers.d digesters.d requeue \
+	   nomailsubs.d
 do
 	mkdir "$LISTDIR"/"$DIR"
 done
--- a/src/mlmmj-send.c	Tue Sep 14 21:06:44 2004 +1000
+++ b/src/mlmmj-send.c	Fri Sep 24 00:29:31 2004 +1000
@@ -472,6 +472,7 @@
 	size_t len = 0, hdrslen, bodylen;
 	int sockfd = 0, mailfd = 0, opt, mindex, subfd, tmpfd;
 	int deletewhensent = 1, sendres, archive = 1, digest = 0;
+	int ctrlarchive;
 	char *listaddr, *mailfilename = NULL, *subfilename = NULL;
 	char *replyto = NULL, *bounceaddr = NULL, *to_addr = NULL;
 	char *relayhost = NULL, *archivefilename = NULL, *tmpstr;
@@ -611,6 +612,7 @@
 
 	addtohdr = statctrl(listdir, "addtohdr");
 	memmailsizestr = ctrlvalue(listdir, "memorymailsize");
+	ctrlarchive = statctrl(listdir, "noarchive");
 	if(memmailsizestr) {
 		memmailsize = strtol(memmailsizestr, NULL, 10);
 		myfree(memmailsizestr);
@@ -865,7 +867,10 @@
 	close(mailfd);
 	
 	if(archive) {
+		if(!ctrlarchive)
 		rename(mailfilename, archivefilename);
+		else
+			unlink(mailfilename);
 		myfree(archivefilename);
 	} else if(deletewhensent)
 		unlink(mailfilename);
--- a/src/mlmmj-sub.c	Tue Sep 14 21:06:44 2004 +1000
+++ b/src/mlmmj-sub.c	Fri Sep 24 00:29:31 2004 +1000
@@ -48,16 +48,25 @@
 
 void confirm_sub(const char *listdir, const char *listaddr,
 		const char *subaddr, const char *mlmmjsend,
-		int digest)
+		enum subtype typesub)
 {
 	int subtextfd, queuefd;
 	char *buf, *subtextfilename, *randomstr, *queuefilename = NULL;
-	char *fromaddr, *listname, *listfqdn, *s1;
+	char *fromaddr, *listname, *listfqdn, *s1, *subject;
 
-	if (!digest) {
-		subtextfilename = concatstr(2, listdir, "/text/sub-ok");
-	} else {
-		subtextfilename = concatstr(2, listdir, "/text/sub-ok-digest");
+	switch(typesub) {
+		case SUB_NORMAL:
+			subtextfilename = concatstr(2, listdir,
+					"/text/sub-ok");
+			break;
+		case SUB_DIGEST:
+			subtextfilename = concatstr(2, listdir,
+					"/text/sub-ok-digest");
+			break;
+		case SUB_NOMAIL:
+			subtextfilename = concatstr(2, listdir,
+					"/text/sub-ok-nomail");
+			break;
 	}
 
 	if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
@@ -89,16 +98,23 @@
 
 	fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
 
-	if (!digest) {
-		s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
-				"\nTo: ", subaddr, "\nSubject: Welcome to ",
-				listaddr, "\n\n");
-	} else {
-		s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
-				"\nTo: ", subaddr, "\nSubject: Welcome to "
-				"digest of ", listaddr, "\n\n");
+	switch(typesub) {
+		case SUB_NORMAL:
+			subject = mystrdup("to ");
+			break;
+		case SUB_DIGEST:
+			subject = mystrdup("to digest of ");
+			break;
+		case SUB_NOMAIL:
+			subject = mystrdup("to nomail version of ");
+			break;
 	}
 
+	s1 = concatstr(10, "From: ", listname, "+help@", listfqdn, "\nTo: ",
+			   subaddr, "\nSubject: Welcome ", subject, listaddr,
+			   "\n\n");
+	myfree(subject);
+
 	if(writen(queuefd, s1, strlen(s1)) < 0) {
 		log_error(LOG_ARGS, "Could not write welcome mail");
 		exit(EXIT_FAILURE);
@@ -138,11 +154,11 @@
 
 void notify_sub(const char *listdir, const char *listaddr,
 		const char *subaddr, const char *mlmmjsend,
-		int digest)
+		enum subtype typesub)
 {
 	char *maildata[4] = { "*LSTADDR*", NULL, "*SUBADDR*", NULL };
 	char *listfqdn, *listname, *fromaddr, *fromstr, *subject;
-	char *queuefilename = NULL;
+	char *queuefilename = NULL, *listtext;
 
 	listname = genlistname(listaddr);
 	listfqdn = genlistfqdn(listaddr);
@@ -150,19 +166,31 @@
 	maildata[3] = mystrdup(subaddr);
 	fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
 	fromstr = concatstr(3, listname, "+owner@", listfqdn);
-	if (!digest) {
-		subject = concatstr(2, "New subscription to ", listaddr);
-		queuefilename = prepstdreply(listdir, "notifysub", fromstr,
-						fromstr, NULL, subject, 2,
-						maildata);
-	} else {
-		subject = concatstr(2, "New subscription to digest of ",
+
+	switch(typesub) {
+		case SUB_NORMAL:
+			subject  = concatstr(2,
+					"New subscription to ", listaddr);
+			listtext = mystrdup("notifysub");
+			break;
+		case SUB_DIGEST:
+			subject = concatstr(2,
+					"New subscription to digest of ",
 					listaddr);
-		queuefilename = prepstdreply(listdir, "notifysub-digest",
-						fromstr, fromstr, NULL,
-						subject, 2, maildata);
+			listtext = mystrdup("notifysub-digest");
+			break;
+		case SUB_NOMAIL:
+			subject = concatstr(2,
+					"New subscription to nomail of ",
+					listaddr);
+			listtext = mystrdup("notifysub-nomail");
+			break;
 	}
+
+	queuefilename = prepstdreply(listdir, listtext, fromstr, fromstr,
+				     NULL, subject, 2, maildata);
 	MY_ASSERT(queuefilename)
+	myfree(listtext);
 	myfree(listname);
 	myfree(listfqdn);
 	myfree(subject);
@@ -182,12 +210,12 @@
 
 void generate_subconfirm(const char *listdir, const char *listaddr,
 			 const char *subaddr, const char *mlmmjsend,
-			 int digest)
+			 enum subtype typesub)
 {
 	int subconffd, subtextfd, queuefd;
 	char *confirmaddr, *listname, *listfqdn, *confirmfilename = NULL;
 	char *subtextfilename, *queuefilename = NULL, *fromaddr;
-	char *buf, *s1, *randomstr = NULL;
+	char *buf, *s1, *randomstr = NULL, *tmpstr, *subject;
 
 	listname = genlistname(listaddr);
 	listfqdn = genlistfqdn(listaddr);
@@ -225,17 +253,28 @@
 	fromaddr = concatstr(5, listname, "+bounces-confsub-", randomstr,
 				"@", listfqdn);
 
-	if (!digest) {
-		subtextfilename = concatstr(2, listdir, "/text/sub-confirm");
-		confirmaddr = concatstr(5, listname, "+confsub-",
-					randomstr, "@", listfqdn);
-	} else {
-		subtextfilename = concatstr(2, listdir, "/text/sub-confirm-digest");
-		confirmaddr = concatstr(5, listname, "+confsub-digest-",
-					randomstr, "@", listfqdn);
+	switch(typesub) {
+		case SUB_NORMAL:
+			subtextfilename = concatstr(2, listdir,
+					"/text/sub-confirm");
+			tmpstr = mystrdup("+confsub-");
+			break;
+		case SUB_DIGEST:
+			subtextfilename = concatstr(2, listdir,
+					"/text/sub-confirm-digest");
+			tmpstr = mystrdup("+confsub-digest-");
+			break;
+		case SUB_NOMAIL:
+			subtextfilename = concatstr(2, listdir,
+					"/text/sub-confirm-nomail");
+			tmpstr = mystrdup("+confsub-nomail-");
+			break;
 	}
 
+	confirmaddr = concatstr(5, listname, tmpstr, randomstr, "@", listfqdn);
+
 	myfree(randomstr);
+	myfree(tmpstr);
 
 	if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
 		log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
@@ -262,15 +301,22 @@
 		exit(EXIT_FAILURE);
 	}
 
-	if (!digest) {
-		s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
-			"\nTo: ", subaddr, "\nSubject: Confirm subscribe to ",
+	switch(typesub) {
+		case SUB_NORMAL:
+			subject = mystrdup("to ");
+			break;
+		case SUB_DIGEST:
+			subject = mystrdup("to digest of ");
+			break;
+		case SUB_NOMAIL:
+			subject = mystrdup("to nomail version of ");
+			break;
+	}
+
+	s1 = concatstr(10, "From: ", listname, "+help@", listfqdn, "\nTo: ",
+			   subaddr, "\nSubject: Confirm subscribe ", subject,
 			listaddr, "\n\n");
-	} else {
-		s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
-			"\nTo: ", subaddr, "\nSubject: Confirm subscribe to "
-			"digest of ", listaddr, "\n\n");
-	}
+	myfree(subject);
 
 	if(writen(queuefd, s1, strlen(s1)) < 0) {
 		log_error(LOG_ARGS, "Could not write subconffile");
@@ -326,13 +372,14 @@
 static void print_help(const char *prg)
 {
 	printf("Usage: %s -L /path/to/list -a john@doe.org "
-	       "[-c] [-C] [-h]\n       [-L] [-U] [-V]\n"
+	       "[-c] [-C] [-h]\n       [-L] [-d | -n] [-U] [-V]\n"
 	       " -a: Email address to subscribe \n"
 	       " -c: Send welcome mail\n"
 	       " -C: Request mail confirmation\n"
 	       " -d: Subscribe to digest of list\n"
 	       " -h: This help\n"
 	       " -L: Full path to list directory\n"
+	       " -n: Subscribe to no mail version of list\n"
 	       " -U: Don't switch to the user id of the listdir owner\n"
 	       " -V: Print version\n"
 	       "When no options are specified, subscription silently "
@@ -343,14 +390,15 @@
 int main(int argc, char **argv)
 {
 	char *listaddr, *listdir = NULL, *address = NULL, *subfilename = NULL;
-	char *mlmmjsend, *bindir, chstr[2];
+	char *mlmmjsend, *bindir, chstr[2], *subdir;
 	int subconfirm = 0, confirmsub = 0, opt, subfilefd, lock, notifysub;
-	int changeuid = 1, status, digest = 0;
+	int changeuid = 1, status, digest = 0, nomail = 0;
 	size_t len;
 	off_t suboff;
 	struct stat st;
 	pid_t pid, childpid;
 	uid_t uid;
+	enum subtype typesub = SUB_NORMAL;
 
 	CHECKFULLPATH(argv[0]);
 
@@ -360,7 +408,7 @@
 	mlmmjsend = concatstr(2, bindir, "/mlmmj-send");
 	myfree(bindir);
 
-	while ((opt = getopt(argc, argv, "hcCdVL:a:")) != -1) {
+	while ((opt = getopt(argc, argv, "hcCdnVL:a:")) != -1) {
 		switch(opt) {
 		case 'a':
 			address = optarg;
@@ -380,6 +428,9 @@
 		case 'L':
 			listdir = optarg;
 			break;
+		case 'n':
+			nomail = 1;
+			break;
 		case 'U':
 			changeuid = 0;
 			break;
@@ -388,12 +439,24 @@
 			exit(0);
 		}
 	}
+	
 	if(listdir == 0 || address == 0) {
 		fprintf(stderr, "You have to specify -L and -a\n");
 		fprintf(stderr, "%s -h for help\n", argv[0]);
 		exit(EXIT_FAILURE);
 	}
 
+	if(digest && nomail) {
+		fprintf(stderr, "Specify either -d or -n, not both\n");
+		fprintf(stderr, "%s -h for help\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if(digest)
+		typesub = SUB_DIGEST;
+	if(nomail)
+		typesub = SUB_NOMAIL;
+
 	if(confirmsub && subconfirm) {
 		fprintf(stderr, "Cannot specify both -C and -c\n");
 		fprintf(stderr, "%s -h for help\n", argv[0]);
@@ -422,12 +485,22 @@
 
 	chstr[0] = address[0];
 	chstr[1] = '\0';
-	if (!digest) {
-		subfilename = concatstr(3, listdir, "/subscribers.d/", chstr);
-	} else {
-		subfilename = concatstr(3, listdir, "/digesters.d/", chstr);
+	
+	switch(typesub) {
+		case SUB_NORMAL:
+			subdir = mystrdup("/subscribers.d/");
+			break;
+		case SUB_DIGEST:
+			subdir = mystrdup("/digesters.d/");
+			break;
+		case SUB_NOMAIL:
+			subdir = mystrdup("/nomailsubs.d/");
+			break;
 	}
 
+	subfilename = concatstr(3, listdir, subdir, chstr);
+	myfree(subdir);
+
 	subfilefd = open(subfilename, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
 	if(subfilefd == -1) {
 		log_error(LOG_ARGS, "Could not open '%s'", subfilename);
@@ -444,7 +517,7 @@
 	if(suboff == -1) {
 		if(subconfirm)
 			generate_subconfirm(listdir, listaddr, address,
-					    mlmmjsend, digest);
+					    mlmmjsend, typesub);
 		else {
 			lseek(subfilefd, 0L, SEEK_END);
 			len = strlen(address);
@@ -466,7 +539,7 @@
 		if(childpid < 0) {
 			log_error(LOG_ARGS, "Could not fork");
 			confirm_sub(listdir, listaddr, address, mlmmjsend,
-					digest);
+					typesub);
 		}
 		
 		if(childpid > 0) {
@@ -478,14 +551,14 @@
 		/* child confirms subscription */
 		if(childpid == 0)
 			confirm_sub(listdir, listaddr, address, mlmmjsend,
-					digest);
+					typesub);
 	}
 
 	notifysub = statctrl(listdir, "notifysub");
 
 	/* Notify list owner about subscription */
 	if (notifysub)
-		notify_sub(listdir, listaddr, address, mlmmjsend, digest);
+		notify_sub(listdir, listaddr, address, mlmmjsend, typesub);
 
 	myfree(listaddr);
 
--- a/src/mlmmj-unsub.c	Tue Sep 14 21:06:44 2004 +1000
+++ b/src/mlmmj-unsub.c	Fri Sep 24 00:29:31 2004 +1000
@@ -47,17 +47,26 @@
 #include "prepstdreply.h"
 
 void confirm_unsub(const char *listdir, const char *listaddr,
-		   const char *subaddr, const char *mlmmjsend, int digest)
+		   const char *subaddr, const char *mlmmjsend,
+		   enum subtype typesub)
 {
 	int subtextfd, queuefd;
 	char *buf, *subtextfilename, *randomstr, *queuefilename = NULL;
-	char *fromaddr, *listname, *listfqdn, *s1;
+	char *fromaddr, *listname, *listfqdn, *s1, *subject;
 
-	if (!digest) {
-		subtextfilename = concatstr(2, listdir, "/text/unsub-ok");
-	} else {
+	switch(typesub) {
+		case SUB_NORMAL:
+			subtextfilename = concatstr(2, listdir,
+					"/text/sub-ok");
+			break;
+		case SUB_DIGEST:
 		subtextfilename = concatstr(2, listdir,
-					    "/text/unsub-ok-digest");
+					"/text/sub-ok-digest");
+			break;
+		case SUB_NOMAIL:
+			subtextfilename = concatstr(2, listdir,
+					"/text/sub-ok-nomail");
+			break;
 	}
 
 	if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
@@ -89,16 +98,23 @@
 
 	fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
 
-	if (!digest) {
-		s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
-			"\nTo: ", subaddr, "\nSubject: Goodbye from ",
-			listaddr, "\n\n");
-	} else {
-		s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
-			"\nTo: ", subaddr, "\nSubject: Goodbye from "
-			"digest of ", listaddr, "\n\n");
+	switch(typesub) {
+		case SUB_NORMAL:
+			subject = mystrdup("from ");
+			break;
+		case SUB_DIGEST:
+			subject = mystrdup("from digest of ");
+			break;
+		case SUB_NOMAIL:
+			subject = mystrdup("from nomail version of ");
+			break;
 	}
 
+	s1 = concatstr(10, "From: ", listname, "+help@", listfqdn, "\nTo: ",
+			   subaddr, "\nSubject: Goodbye ", subject, listaddr,
+			   "\n\n");
+	myfree(subject);
+
 	if(writen(queuefd, s1, strlen(s1)) < 0) {
 		log_error(LOG_ARGS, "Could not write subconffile");
 		exit(EXIT_FAILURE);
@@ -137,11 +153,12 @@
 }
 
 void notify_unsub(const char *listdir, const char *listaddr,
-		  const char *subaddr, const char *mlmmjsend, int digest)
+		  const char *subaddr, const char *mlmmjsend,
+		  enum subtype typesub)
 {
         char *maildata[4] = { "*LSTADDR*", NULL, "*SUBADDR*", NULL };
         char *listfqdn, *listname, *fromaddr, *fromstr, *subject;
-        char *queuefilename = NULL;
+        char *queuefilename = NULL, *listtext;
 
         listname = genlistname(listaddr);
         listfqdn = genlistfqdn(listaddr);
@@ -149,18 +166,32 @@
         maildata[3] = mystrdup(subaddr);
         fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
         fromstr = concatstr(3, listname, "+owner@", listfqdn);
-	if (!digest) {
-        	subject = concatstr(2, "Unsubscription from ", listaddr);
-        	queuefilename = prepstdreply(listdir, "notifyunsub", fromstr,
-                                     fromstr, NULL, subject, 2, maildata);
-	} else {
-        	subject = concatstr(2, "Unsubscription from digest of ",
+
+	switch(typesub) {
+		case SUB_NORMAL:
+			subject = concatstr(2,
+					"Unsubscription from ", listaddr);
+			listtext = mystrdup("notifyunsub");
+			break;
+		case SUB_DIGEST:
+			subject = concatstr(2,
+					"Unsubscription from digest of ",
 					listaddr);
-        	queuefilename = prepstdreply(listdir, "notifyunsub-digest",
-                                     fromstr, fromstr, NULL, subject, 2,
-                                     maildata);
+			listtext = mystrdup("notifyunsub-digest");
+			break;
+		case SUB_NOMAIL:
+			subject = concatstr(2,
+					"Unsubscription from nomail of ",
+					listaddr);
+			listtext = mystrdup("notifyunsub-nomail");
+			break;
 	}
-        MY_ASSERT(queuefilename)
+
+	
+	queuefilename = prepstdreply(listdir, listtext, fromstr, fromstr,
+				     NULL, subject, 2, maildata);
+	MY_ASSERT(queuefilename);
+	myfree(listtext);
         myfree(listname);
         myfree(listfqdn);
         myfree(subject);
@@ -181,11 +212,11 @@
 
 void generate_unsubconfirm(const char *listdir, const char *listaddr,
 			   const char *subaddr, const char *mlmmjsend,
-			   int digest)
+			   enum subtype typesub)
 {
-	char *confirmaddr, *buf, *listname, *listfqdn;
+	char *confirmaddr, *buf, *listname, *listfqdn, *tmpstr;
 	char *subtextfilename, *queuefilename = NULL, *fromaddr, *s1;
-	char *randomstr = NULL, *confirmfilename = NULL;
+	char *randomstr = NULL, *confirmfilename = NULL, *subject;
 	int subconffd, subtextfd, queuefd;
 
 	listname = genlistname(listaddr);
@@ -224,18 +255,28 @@
 	fromaddr = concatstr(5, listname, "+bounces-confunsub-", randomstr,
 				"@", listfqdn);
 
-	if (!digest) {
-		subtextfilename = concatstr(2, listdir, "/text/unsub-confirm");
-		confirmaddr = concatstr(5, listname, "+confunsub-", randomstr,
-					"@", listfqdn);
-	} else {
+	switch(typesub) {
+		case SUB_NORMAL:
+			subtextfilename = concatstr(2, listdir,
+						"/text/unsub-confirm");
+			tmpstr = mystrdup("+confunsub-");
+			break;
+		case SUB_DIGEST:
 		subtextfilename = concatstr(2, listdir,
 					"/text/unsub-confirm-digest");
-		confirmaddr = concatstr(5, listname, "+confunsub-digest-",
-					randomstr, "@", listfqdn);
+			tmpstr = mystrdup("+confunsub-digest-");
+			break;
+		case SUB_NOMAIL:
+			subtextfilename = concatstr(2, listdir,
+						"/text/unsub-confirm-nomail");
+			tmpstr = mystrdup("+confunsub-nomail-");
+			break;
 	}
 
+	confirmaddr = concatstr(5, listname, tmpstr, randomstr, "@", listfqdn);
+
 	myfree(randomstr);
+	myfree(tmpstr);
 
 	if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
 		log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
@@ -261,17 +302,22 @@
 		exit(EXIT_FAILURE);
 	}
 
-	if (!digest) {
-		s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
-				"\nTo: ", subaddr, "\nSubject: Confirm "
-				"unsubscribe from ", listaddr, "\n\n");
-	} else {
-		s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
-				"\nTo: ", subaddr, "\nSubject: Confirm "
-				"unsubscribe from digest of ", listaddr,
-				"\n\n");
+	switch(typesub) {
+		case SUB_NORMAL:
+			subject = mystrdup("from ");
+			break;
+		case SUB_DIGEST:
+			subject = mystrdup("from digest of ");
+			break;
+		case SUB_NOMAIL:
+			subject = mystrdup("from nomail version of ");
+			break;
 	}
 
+	s1 = concatstr(10, "From: ", listname, "+help@", listfqdn, "\nTo: ",
+			   subaddr, "\nSubject: Confirm unsubscribe ", subject,
+			   listaddr, "\n\n");
+
 	if(writen(queuefd, s1, strlen(s1)) < 0) {
 		log_error(LOG_ARGS, "Could not write subconffile");
 		exit(EXIT_FAILURE);
@@ -362,13 +408,14 @@
 static void print_help(const char *prg)
 {
 	printf("Usage: %s -L /path/to/list -a john@doe.org "
-	       "[-c] [-C] [-h] [-L] [-V]\n"
+	       "[-c] [-C] [-h] [-L] [-d | -n] [-V]\n"
 	       " -a: Email address to unsubscribe \n"
 	       " -c: Send goodbye mail\n"
 	       " -C: Request mail confirmation\n"
 	       " -d: Subscribe to digest of list\n"
 	       " -h: This help\n"
 	       " -L: Full path to list directory\n"
+	       " -n: Subscribe to no mail version of list\n"
 	       " -V: Print version\n"
 	       "When no options are specified, unsubscription silently "
 	       "happens\n", prg);
@@ -377,15 +424,16 @@
 
 int main(int argc, char **argv)
 {
-	int subread, subwrite, rlock, wlock, opt, unsubres, status;
+	int subread, subwrite, rlock, wlock, opt, unsubres, status, nomail = 0;
 	int confirmunsub = 0, unsubconfirm = 0, notifysub = 0, digest = 0;
 	char *listaddr, *listdir = NULL, *address = NULL, *subreadname = NULL;
-	char *subwritename, *mlmmjsend, *bindir;
+	char *subwritename, *mlmmjsend, *bindir, *subdir;
 	char *subddirname;
 	off_t suboff;
 	DIR *subddir;
 	struct dirent *dp;
 	pid_t pid, childpid;
+	enum subtype typesub = SUB_NORMAL;
 
 	CHECKFULLPATH(argv[0]);
 	
@@ -395,11 +443,14 @@
 	mlmmjsend = concatstr(2, bindir, "/mlmmj-send");
 	myfree(bindir);
 
-	while ((opt = getopt(argc, argv, "hcCdVL:a:")) != -1) {
+	while ((opt = getopt(argc, argv, "hcCdnVL:a:")) != -1) {
 		switch(opt) {
 		case 'L':
 			listdir = optarg;
 			break;
+		case 'n':
+			nomail = 1;
+			break;
 		case 'a':
 			address = optarg;
 			break;
@@ -426,6 +477,17 @@
 		exit(EXIT_FAILURE);
 	}
 
+	if(digest && nomail) {
+		fprintf(stderr, "Specify either -d or -n, not both\n");
+		fprintf(stderr, "%s -h for help\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if(digest)
+		typesub = SUB_DIGEST;
+	if(nomail)
+		typesub = SUB_NOMAIL;
+
 	if(confirmunsub && unsubconfirm) {
 		fprintf(stderr, "Cannot specify both -C and -c\n");
 		fprintf(stderr, "%s -h for help\n", argv[0]);
@@ -435,34 +497,50 @@
 	/* get the list address */
 	listaddr = getlistaddr(listdir);
 
+	switch(typesub) {
+		case SUB_NORMAL:
+			subdir = mystrdup("/subscribers.d/");
+			break;
+		case SUB_DIGEST:
+			subdir = mystrdup("/digesters.d/");
+			break;
+		case SUB_NOMAIL:
+			subdir = mystrdup("/nomailsubs.d/");
+			break;
+	}
+		
+	subddirname = concatstr(2, listdir, subdir);
+
+	if(is_subbed_in(subddirname, address)) {
+		/* Address is not subscribed, so exit silently */
+		myfree(subddirname);
+		myfree(subdir);
+		myfree(listaddr);
+		exit(EXIT_SUCCESS);
+	}
+
 	if(unsubconfirm)
 		generate_unsubconfirm(listdir, listaddr, address, mlmmjsend,
-				digest);
+				typesub);
 
-	if (!digest) {
-		subddirname = concatstr(2, listdir, "/subscribers.d/");
-	} else {
-		subddirname = concatstr(2, listdir, "/digesters.d/");
-	}
 	if((subddir = opendir(subddirname)) == NULL) {
 		log_error(LOG_ARGS, "Could not opendir(%s)",
 				    subddirname);
 		myfree(subddirname);
+		myfree(subdir);
+		myfree(listaddr);
 		exit(EXIT_FAILURE);
 	}
+
 	myfree(subddirname);
+
 	while((dp = readdir(subddir)) != NULL) {
 		if(!strcmp(dp->d_name, "."))
 			continue;
 		if(!strcmp(dp->d_name, ".."))
 			continue;
-		if (!digest) {
-			subreadname = concatstr(3, listdir, "/subscribers.d/",
-				dp->d_name);
-		} else {
-			subreadname = concatstr(3, listdir, "/digesters.d/",
-				dp->d_name);
-		}
+		
+		subreadname = concatstr(3, listdir, subdir, dp->d_name);
 
 		subread = open(subreadname, O_RDWR);
 		if(subread == -1) {
@@ -574,12 +652,13 @@
 		}
         }
 
+	myfree(subdir);
 
         notifysub = statctrl(listdir, "notifysub");
 
         /* Notify list owner about subscription */
         if (notifysub)
-                notify_unsub(listdir, listaddr, address, mlmmjsend, digest);
+                notify_unsub(listdir, listaddr, address, mlmmjsend, typesub);
 
 	myfree(listaddr);
 
--- a/src/subscriberfuncs.c	Tue Sep 14 21:06:44 2004 +1000
+++ b/src/subscriberfuncs.c	Fri Sep 24 00:29:31 2004 +1000
@@ -87,7 +87,7 @@
 	return (off_t)-1;
 }
 
-static int is_subbed_in(const char *subddirname, const char *address)
+int is_subbed_in(const char *subddirname, const char *address)
 {
 	int retval = 1, subread;
 	char *subreadname;
@@ -148,5 +148,11 @@
 	if (retval == 0)
 		return 0;
 
+	subddirname = concatstr(2, listdir, "/nomailsubs.d/");
+	retval = is_subbed_in(subddirname, address);
+	myfree(subddirname);
+	if (retval == 0)
+		return 0;
+
 	return 1;
 }