changeset 359:ca217febcfbd

Rerwrite the way listtexts are handled and move Subject: out into the file in the process. Also bump VERSION to 1.1.0-RC1, it's 1.1.0 time :-)
author mmj
date Fri, 12 Nov 2004 00:10:54 +1100
parents ebec099d79a5
children 721b3563d92c
files ChangeLog TUNABLES UPGRADE VERSION contrib/web/perl-admin/htdocs/subscribers.cgi contrib/web/perl-admin/templates/index_row.html contrib/web/perl-admin/templates/subscribers.html contrib/web/perl-admin/templates/subscribers_row.html include/prepstdreply.h listtexts/listhelp listtexts/sub-ok listtexts/sub-ok-digest listtexts/sub-ok-nomail src/getlistaddr.c src/mlmmj-bounce.c src/mlmmj-process.c src/mlmmj-sub.c src/mlmmj-unsub.c src/prepstdreply.c src/send_help.c
diffstat 20 files changed, 420 insertions(+), 520 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Nov 11 00:37:42 2004 +1100
+++ b/ChangeLog	Fri Nov 12 00:10:54 2004 +1100
@@ -1,3 +1,6 @@
+1.1.0-RC1
+ o Rewrite the way listtexts are managed, and in the process move the Subject:
+   out into the listtext file making mlmmj completely translateable
  o Enhance perl webinterface - including group writable patch
  o Add option control/nosubconfirm which makes it possible to subscribe
    without confirmation by just sending the mail. USE WITH CARE!
--- a/TUNABLES	Thu Nov 11 00:37:42 2004 +1100
+++ b/TUNABLES	Fri Nov 12 00:10:54 2004 +1100
@@ -40,6 +40,11 @@
    The emailaddresses in this file (1 pr. line) will get mails to
    listname+owner@listdomain.tld
 
+ · customheaders		(list)
+
+   These headers are added to every mail coming through. This is the place you
+   want to add Reply-To: header in case you want such.
+
  · delheaders			(list)
 
    In this file is specified *ONE* headertoken to match pr. line. If the file
--- a/UPGRADE	Thu Nov 11 00:37:42 2004 +1100
+++ b/UPGRADE	Fri Nov 12 00:10:54 2004 +1100
@@ -1,3 +1,11 @@
+This applies to everyone using mlmmj < 1.1.0:
+---------------------------------------------
+
+ Don't forget to upgrade the listtexts. Listtext handling was
+ completely rewritten and the subject is now translateable as well.
+
+ That's why it's especially important to upgrade!
+
 This applies to everyone using mlmmj < 0.8.3:
 ---------------------------------------------
 
@@ -8,49 +16,3 @@
 
  Don't forget to add the emailaddress of the list owner
  in listdir/control/owner
-
-This applies to everyone using mlmmj > 0.7.1:
----------------------------------------------
-
- Don't forget to upgrade the listtexts!
-
-This applies to everyone using mlmmj > 0.5.2:
----------------------------------------------
-
- Following directories have moved into the control/ directory since that
- is where they rightfully belong:
-
-  · listaddress
-  · moderators
-
- The following directory can be deleted:
-
-  · moderation/queue
-
- Start mlmmj-maintd
-
-This applies to everyone using mlmmj > 0.5.1:
----------------------------------------------
-
- Following directories have to be created:
-
-  · listdir/requeue/
-  · listdir/queue/discarded/
-
- The footer and customheaders file are now to be placed in the control/
- directory where they belong, so they should be moved there from listdir.
-
-This applies to everyone using mlmmj > 0.5.0:
----------------------------------------------
-
- Note that listdir/subcribers is no longer used for subscribers. Instead
- every file in listdir/subscribers.d/ is used.
-
- A quick way to convert:
-
- $ for i in `cat listdir/subscribers` ; do mlmmj-sub -a $i -L listdir ; done
-
- And then remove listdir/subscribers.
-
- A quick and dirty hack would be to simply move listdir/subscribers into
- subscribers.d/. This could lead to double subscriptions, so use with caution!
--- a/VERSION	Thu Nov 11 00:37:42 2004 +1100
+++ b/VERSION	Fri Nov 12 00:10:54 2004 +1100
@@ -1,1 +1,1 @@
-1.1-pre-11092004
+1.1.0-RC1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/web/perl-admin/htdocs/subscribers.cgi	Fri Nov 12 00:10:54 2004 +1100
@@ -0,0 +1,157 @@
+#!/usr/bin/perl -w
+
+# Copyright (C) 2004 Christian Laursen <christian@pil.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.
+
+use strict;
+use URI::Escape;
+use HTML::Entities;
+use CGI;
+use CGI::FastTemplate;
+use Digest::MD5;
+
+use vars qw($topdir $templatedir $list);
+
+if (exists $ENV{CONFIG_PATH}) {
+	require $ENV{CONFIG_PATH};
+} else {
+	require "../conf/config.pl";
+}
+
+my $mlmmjsub = "/usr/local/bin/mlmmj-sub";
+my $mlmmjunsub = "/usr/local/bin/mlmmj-unsub";
+
+my $tpl = new CGI::FastTemplate($templatedir);
+
+my $q = new CGI;
+$list = $q->param("list");
+my $subscribe = $q->param("subscribe");
+my $unsubscribe = $q->param("unsubscribe");
+
+die "no list specified" unless $list;
+die "non-existent list" unless -d("$topdir/$list");
+
+$tpl->define(main => "subscribers.html",
+			 row => "subscribers_row.html");
+
+my $action = '';
+
+my $subscribers;
+
+if (defined $subscribe) {
+	my $email = $q->param("email");
+	if ($email =~ /^[a-z0-9\.\-_\@]+$/i) {
+		system "$mlmmjsub -L $topdir/$list -a $email -U";
+		if (is_subscribed($email)) {
+			$action = "$email has been subscribed.";
+		} else {
+			$action = "$email was not subscribed.";
+		}
+	} else {
+		$action = '"'.encode_entities($email).'" is not a valid email address.';
+	}
+} elsif (defined $unsubscribe) {
+	my $maxid = $q->param("maxid");
+	for (my $i = 0; $i < $maxid; ++$i) {
+		my $email = $q->param("email$i");
+		if (defined $email) {
+			if ($email =~ /^[a-z0-9\.\-_\@]+$/i) {
+				system "$mlmmjunsub -L $topdir/$list -a $email";
+				if (!is_subscribed($email)) {
+					$action .= "$email has been unsubscribed.<br>\n";
+				} else {
+					$action .= "$email was not unsubscribed.<br>\n";
+				}
+			} else {
+				$action .= '"'.encode_entities($email).'" is not a valid email address.'."<br>\n";
+			}
+		}
+	}
+}
+
+$tpl->assign(ACTION => $action);
+
+$subscribers = get_subscribers();
+
+for (my $i = 0; $i < @$subscribers; ++$i) {
+	$tpl->assign(EMAIL => $subscribers->[$i],
+				 ID => $i);
+	$tpl->parse(ROWS => '.row');
+}
+if (@$subscribers == 0) {
+	$tpl->assign(ROWS => '');
+}
+
+$tpl->assign(LIST => encode_entities($list),
+			 MAXID => scalar(@$subscribers));
+
+print "Content-type: text/html\n\n";
+
+$tpl->parse(CONTENT => "main");
+$tpl->print;
+
+sub get_subscribers {
+	my @subscribers = ();
+
+	opendir (DIR, "$topdir/$list/subscribers.d") or die "Couldn't read dir $topdir/$list/subscribers.d: $!";
+	my @files = grep(/^.$/, readdir(DIR));
+	closedir DIR;
+	for my $file (@files) {
+		my $filename = "$topdir/$list/subscribers.d/$file";
+		if (-f $filename) {
+			open (FILE, $filename) or die "Couldn't open $filename for reading: $!";
+			while (<FILE>) {
+				chomp;
+				push @subscribers, $_;
+			}
+			close FILE;
+		}
+	}
+
+	@subscribers = sort @subscribers;
+
+	return \@subscribers;
+}
+
+sub is_subscribed {
+	my ($email) = @_;
+
+	opendir (DIR, "$topdir/$list/subscribers.d") or die "Couldn't read dir $topdir/$list/subscribers.d: $!";
+	my @files = grep(/^.$/, readdir(DIR));
+	closedir DIR;
+
+	for my $file (@files) {
+		my $filename = "$topdir/$list/subscribers.d/$file";
+		if (-f $filename) {
+			open (FILE, $filename) or die "Couldn't open $filename for reading: $!";
+			while (<FILE>) {
+				chomp;
+				if ($email eq $_) {
+					return 1;
+				}
+			}
+			close FILE;
+		}
+	}
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/web/perl-admin/templates/index_row.html	Fri Nov 12 00:10:54 2004 +1100
@@ -0,0 +1,1 @@
+<tr><td>$LIST</td><td><a href="edit.cgi?list=$ULIST">Configure</a></td><td><a href="subscribers.cgi?list=$ULIST">Subscribers</a></td></tr>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/web/perl-admin/templates/subscribers.html	Fri Nov 12 00:10:54 2004 +1100
@@ -0,0 +1,19 @@
+<html><head><title>mlmmj subscribers</title></head><body>
+<h1>mlmmj subscribers</h1>
+<p>
+<a href="index.cgi">Index</a> | <a href="subscribers.cgi?list=$LIST">Reload subscriber list</a>
+</p>
+<p>
+$ACTION
+</p>
+<form action="subscribers.cgi" method="post">
+<input type="hidden" name="list" value="$LIST">
+<input type="hidden" name="maxid" value="$MAXID">
+<p>Add subscriber: <input type="text" name="email"> <input type="submit" name="subscribe" value="Subscribe"></p>
+<table border="1">
+<tr><th>Email address</th><th>Unsubscribe</th></tr>
+$ROWS
+</table>
+<p><input type="submit" name="unsubscribe" value="Unsubscribe selected"></p>
+</form>
+</body></html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/web/perl-admin/templates/subscribers_row.html	Fri Nov 12 00:10:54 2004 +1100
@@ -0,0 +1,1 @@
+<tr><td>$EMAIL</td><td><input type="checkbox" name="email$ID" value="$EMAIL"></td></tr>
--- a/include/prepstdreply.h	Thu Nov 11 00:37:42 2004 +1100
+++ b/include/prepstdreply.h	Fri Nov 12 00:10:54 2004 +1100
@@ -24,8 +24,10 @@
 #ifndef PREPSTDREPLY_H
 #define PREPSTDREPLY_H
 
+char *substitute(const char *line, const char *listaddr, size_t datacount,
+		 char **data);
 char *prepstdreply(const char *listdir, const char *filename, const char *from,
-		   const char *to, const char *replyto, const char *subject,
-		   size_t tokencount, char **data);
+		   const char *to, const char *replyto, size_t tokencount,
+		   char **data);
 
 #endif /* PREPSTDREPLY_H */
--- a/listtexts/listhelp	Thu Nov 11 00:37:42 2004 +1100
+++ b/listtexts/listhelp	Fri Nov 12 00:10:54 2004 +1100
@@ -6,7 +6,7 @@
 
 To unsubscribe send a mail to:
 
-$unsubaddr$
+$listunsubaddr$
 
 To subscribe to the digest of this list send a mail to:
 
@@ -20,11 +20,11 @@
 subscriber, but will not get any mails to the list. Useful when it's
 necessary to post from several emailaddresses to a subscribers only list.
 
-For retrieval of message number 42 in the archive send a mail to
+For retrieval of message number N in the archive send a mail to
 
-$get42$
+$listgetN$
 
 To send a mail to the list owner, send a mail to:
 
-$owner$
+$listowner$
 
--- a/listtexts/sub-ok	Thu Nov 11 00:37:42 2004 +1100
+++ b/listtexts/sub-ok	Fri Nov 12 00:10:54 2004 +1100
@@ -6,7 +6,11 @@
 
 mailinglist.
 
-For help send a mail to:
+To unsubscribe send a mail to:
+
+$listunsubaddr$
+
+And for help send a mail to:
 
 $helpaddr$
 
--- a/listtexts/sub-ok-digest	Thu Nov 11 00:37:42 2004 +1100
+++ b/listtexts/sub-ok-digest	Fri Nov 12 00:10:54 2004 +1100
@@ -6,7 +6,11 @@
 
 mailinglist.
 
-For help send a mail to:
+To unsubscribe send a mail to:
+
+$digestunsubaddr$
+
+And for help send a mail to:
 
 $helpaddr$
 
--- a/listtexts/sub-ok-nomail	Thu Nov 11 00:37:42 2004 +1100
+++ b/listtexts/sub-ok-nomail	Fri Nov 12 00:10:54 2004 +1100
@@ -6,7 +6,11 @@
 
 mailinglist.
 
-For help send a mail to:
+To unsubscribe send a mail to:
+
+$nomailunsubaddr$
+
+And for help send a mail to:
 
 $helpaddr$
 
--- a/src/getlistaddr.c	Thu Nov 11 00:37:42 2004 +1100
+++ b/src/getlistaddr.c	Fri Nov 12 00:10:54 2004 +1100
@@ -49,8 +49,8 @@
 	tmpstr = mygetline(listnamefd);
 
 	if(tmpstr == NULL){
-		log_error(LOG_ARGS, "FATAL. Could not get listaddress in %s",
-					listdir);
+		log_error(LOG_ARGS, "FATAL. Could not get listaddress "
+				    "in %s/control/listaddress", listdir);
 		exit(EXIT_FAILURE);
 	}
 
--- a/src/mlmmj-bounce.c	Thu Nov 11 00:37:42 2004 +1100
+++ b/src/mlmmj-bounce.c	Fri Nov 12 00:10:54 2004 +1100
@@ -96,52 +96,44 @@
 
 void do_probe(const char *listdir, const char *mlmmjsend, const char *addr)
 {
-	char *myaddr, *from, *a, *fromstr, *subjectstr, *indexstr;
-	char *queuefilename, *listaddr, *listfqdn, *listname, *probefile;
-	char *maildata[] = { "*LSTADDR*", NULL, "*BOUNCENUMBERS*", NULL };
+	char *myaddr, *from, *a, *indexstr, *queuefilename, *listaddr;
+	char *listfqdn, *listname, *probefile;
+	char *maildata[] = { "$bouncenumbers$", NULL };
 	int fd;
 	time_t t;
 
 	myaddr = mystrdup(addr);
 
 	listaddr = getlistaddr(listdir);
-	chomp(listaddr);
-
 	listname = genlistname(listaddr);
 	listfqdn = genlistfqdn(listaddr);
 
 	from = concatstr(5, listname, "+bounces-", myaddr, "-probe@", listfqdn);
 
+	myfree(listaddr);
+	myfree(listfqdn);
+	myfree(listname);
+
 	a = strrchr(myaddr, '=');
 	if (!a) {
 		myfree(myaddr);
 		myfree(from);
 		log_error(LOG_ARGS, "do_probe(): malformed address");
 		exit(EXIT_FAILURE);
+
 	}
 	*a = '@';
 
-	fromstr = concatstr(3, listname, "+owner@", listfqdn);
-
-	subjectstr = concatstr(3, "Mails to you from ", listaddr,
-					" have been bouncing");
-
 	indexstr = fetchindexes(addr);
 	if(indexstr == NULL) {
 		log_error(LOG_ARGS, "Could not fetch bounceindexes");
 		exit(EXIT_FAILURE);
 	}
 
-	maildata[1] = listaddr;
-	maildata[3] = indexstr;
-	queuefilename = prepstdreply(listdir, "bounce-probe", fromstr,
-					myaddr, NULL, subjectstr, 2, maildata);
+	maildata[1] = indexstr;
+	queuefilename = prepstdreply(listdir, "bounce-probe", "$listowner$",
+					myaddr, NULL, 1, maildata);
 	MY_ASSERT(queuefilename);
-	myfree(fromstr);
-	myfree(subjectstr);
-	myfree(listaddr);
-	myfree(listfqdn);
-	myfree(listname);
 	myfree(indexstr);
 
 	probefile = concatstr(4, listdir, "/bounce/", addr, "-probe");
--- a/src/mlmmj-process.c	Thu Nov 11 00:37:42 2004 +1100
+++ b/src/mlmmj-process.c	Fri Nov 12 00:10:54 2004 +1100
@@ -343,9 +343,9 @@
 	char *randomstr = NULL, *mqueuename;
 	char *mlmmjsend, *mlmmjsub, *mlmmjunsub, *mlmmjbounce;
 	char *bindir, *subjectprefix, *discardname, *listaddr;
-	char *listfqdn, *listname, *fromaddr, *fromstr, *subject;
+	char *listfqdn, *listname, *fromaddr;
 	char *queuefilename, *recipdelim, *owner = NULL;
-	char *maildata[4];
+	char *maildata[2];
 	struct stat st;
 	uid_t uid;
 	struct email_container fromemails = { 0, NULL };
@@ -615,20 +615,13 @@
 		}
 		listname = genlistname(listaddr);
 		listfqdn = genlistfqdn(listaddr);
-		maildata[0] = "*LSTADDR*";
-		maildata[1] = listaddr;
 		fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
-		fromstr = concatstr(3, listname, "+owner@", listfqdn);
-		subject = concatstr(3, "Post to ", listaddr, " denied.");
-		queuefilename = prepstdreply(listdir, "notintocc", fromstr,
-					     fromemails.emaillist[0], NULL,
-					     subject, 1, maildata);
+		queuefilename = prepstdreply(listdir, "notintocc",
+					"$listowner$", fromemails.emaillist[0],
+					NULL, 0, NULL);
 		MY_ASSERT(queuefilename)
-		myfree(listaddr);
 		myfree(listname);
 		myfree(listfqdn);
-		myfree(fromstr);
-		myfree(subject);
 		unlink(donemailname);
 		myfree(donemailname);
 		execlp(mlmmjsend, mlmmjsend,
@@ -654,24 +647,17 @@
 		if(is_subbed(listdir, fromemails.emaillist[0]) != 0) {
 			listname = genlistname(listaddr);
 			listfqdn = genlistfqdn(listaddr);
-			maildata[0] = "*LSTADDR*";
-			maildata[1] = listaddr;
-			maildata[2] = "*POSTERADDR*";
-			maildata[3] = fromemails.emaillist[0];
+			maildata[0] = "$posteraddr$";
+			maildata[1] = fromemails.emaillist[0];
 			fromaddr = concatstr(3, listname, "+bounces-help@",
 					listfqdn);
-			fromstr = concatstr(3, listname, "+owner@", listfqdn);
-			subject = concatstr(3, "Post to ", listaddr,
-						" denied");
 			queuefilename = prepstdreply(listdir, "subonlypost",
-					fromstr, fromemails.emaillist[0], NULL,
-					     subject, 2, maildata);
+					"$listowner$", fromemails.emaillist[0],
+					NULL, 1, maildata);
 			MY_ASSERT(queuefilename)
 			myfree(listaddr);
 			myfree(listname);
 			myfree(listfqdn);
-			myfree(fromstr);
-			myfree(subject);
 			unlink(donemailname);
 			myfree(donemailname);
 			execlp(mlmmjsend, mlmmjsend,
@@ -698,24 +684,16 @@
 		if (do_access(access_rules, &allheaders) == DENY) {
 			listname = genlistname(listaddr);
 			listfqdn = genlistfqdn(listaddr);
-			maildata[0] = "*LSTADDR*";
-			maildata[1] = listaddr;
 			fromaddr = concatstr(3, listname, "+bounces-help@",
 					listfqdn);
-			fromstr = concatstr(3, listname, "+owner@", listfqdn);
-			subject = concatstr(3, "Post to ", listaddr,
-					" denied.");
 			queuefilename = prepstdreply(listdir, "access",
-							fromstr,
+							"$listowner$",
 							fromemails.emaillist[0],
-							NULL,
-							subject, 1, maildata);
+							NULL, 0, NULL);
 			MY_ASSERT(queuefilename)
 			myfree(listaddr);
 			myfree(listname);
 			myfree(listfqdn);
-			myfree(fromstr);
-			myfree(subject);
 			unlink(donemailname);
 			myfree(donemailname);
 			myfree(randomstr);
--- a/src/mlmmj-sub.c	Thu Nov 11 00:37:42 2004 +1100
+++ b/src/mlmmj-sub.c	Fri Nov 12 00:10:54 2004 +1100
@@ -50,98 +50,32 @@
 		const char *subaddr, const char *mlmmjsend,
 		enum subtype typesub)
 {
-	int subtextfd, queuefd;
-	char *buf, *subtextfilename, *randomstr, *queuefilename = NULL;
-	char *fromaddr, *listname, *listfqdn, *s1, *subject;
-
-	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) {
-		log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
-		myfree(subtextfilename);
-		exit(EXIT_FAILURE);
-	}
-	myfree(subtextfilename);
+	char *queuefilename, *fromaddr, *listname, *listfqdn, *listtext;
 
 	listname = genlistname(listaddr);
 	listfqdn = genlistfqdn(listaddr);
 
-	do {
-		randomstr = random_str();
-		myfree(queuefilename);
-		queuefilename = concatstr(3, listdir, "/queue/", randomstr);
-		myfree(randomstr);
-
-		queuefd = open(queuefilename, O_RDWR|O_CREAT|O_EXCL,
-					      S_IRUSR|S_IWUSR);
+	fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
 
-	} while ((queuefd < 0) && (errno == EEXIST));
-
-	if(queuefd < 0) {
-		log_error(LOG_ARGS, "Could not open '%s'", queuefilename);
-		myfree(queuefilename);
-		exit(EXIT_FAILURE);
-	}
-
-	fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
+	myfree(listname);
+	myfree(listfqdn);
 
 	switch(typesub) {
 		case SUB_NORMAL:
-			subject = mystrdup("to ");
+			listtext = mystrdup("/text/sub-ok");
 			break;
 		case SUB_DIGEST:
-			subject = mystrdup("to digest of ");
+			listtext = mystrdup("/text/sub-ok-digest");
 			break;
 		case SUB_NOMAIL:
-			subject = mystrdup("to nomail version of ");
+			listtext = mystrdup("/text/sub-ok-nomail");
 			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);
-	}
-	myfree(s1);
-
-	while((buf = mygetline(subtextfd))) {
-		if(strncmp(buf, "*LSTADDR*", 9) == 0) {
-			if(writen(queuefd, listaddr, strlen(listaddr)) < 0) {
-				log_error(LOG_ARGS, "Could not write welcome"
-						" mail");
-				exit(EXIT_FAILURE);
-			}
-		} else {
-			if(writen(queuefd, buf, strlen(buf)) < 0) {
-				log_error(LOG_ARGS, "Could not write welcome"
-						" mail");
-				exit(EXIT_FAILURE);
-			}
-		}
-		myfree(buf);
-	}
-
-	myfree(listname);
-	myfree(listfqdn);
-	close(subtextfd);
-	close(queuefd);
+	queuefilename = prepstdreply(listdir, listtext, "$helpaddr$",
+				     subaddr, NULL, 0, NULL);
+	MY_ASSERT(queuefilename);
+	myfree(listtext);
 
 	execlp(mlmmjsend, mlmmjsend,
 				"-l", "1",
@@ -156,54 +90,44 @@
 		const char *subaddr, const char *mlmmjsend,
 		enum subtype typesub)
 {
-	char *maildata[4] = { "*LSTADDR*", NULL, "*SUBADDR*", NULL };
-	char *listfqdn, *listname, *fromaddr, *fromstr, *subject;
+	char *maildata[2] = { "$newsub$", NULL };
+	char *listfqdn, *listname, *fromaddr, *tostr;
 	char *queuefilename = NULL, *listtext;
 
 	listname = genlistname(listaddr);
 	listfqdn = genlistfqdn(listaddr);
-	maildata[1] = mystrdup(listaddr);
-	maildata[3] = mystrdup(subaddr);
+
+	maildata[1] = mystrdup(subaddr);
+	
 	fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
-	fromstr = concatstr(3, listname, "+owner@", listfqdn);
+	tostr = concatstr(3, listname, "+owner@", listfqdn);
+	
+	myfree(listname);
+	myfree(listfqdn);
 
 	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);
 			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);
+	queuefilename = prepstdreply(listdir, listtext, "$listowner$",
+				"$listowner", NULL, 1, maildata);
 	MY_ASSERT(queuefilename)
 	myfree(listtext);
-	myfree(listname);
-	myfree(listfqdn);
-	myfree(subject);
 	myfree(maildata[1]);
-	myfree(maildata[3]);
 	execlp(mlmmjsend, mlmmjsend,
 			"-l", "1",
-			"-T", fromstr,
+			"-T", tostr,
 			"-F", fromaddr,
 			"-m", queuefilename, NULL);
 
-	myfree(fromstr);
-
 	log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsend);
 	exit(EXIT_FAILURE);
 }
@@ -212,10 +136,11 @@
 			 const char *subaddr, const char *mlmmjsend,
 			 enum subtype typesub)
 {
-	int subconffd, subtextfd, queuefd;
+	int subconffd;
 	char *confirmaddr, *listname, *listfqdn, *confirmfilename = NULL;
-	char *subtextfilename, *queuefilename = NULL, *fromaddr;
-	char *buf, *s1, *randomstr = NULL, *tmpstr, *subject;
+	char *listtext, *queuefilename = NULL, *fromaddr;
+	char *randomstr = NULL, *tmpstr;
+	char *maildata[4] = { "$subaddr$", NULL, "$confaddr$", NULL };
 
 	listname = genlistname(listaddr);
 	listfqdn = genlistfqdn(listaddr);
@@ -255,18 +180,15 @@
 	
 	switch(typesub) {
 		case SUB_NORMAL:
-			subtextfilename = concatstr(2, listdir,
-					"/text/sub-confirm");
+			listtext = mystrdup("sub-confirm");
 			tmpstr = mystrdup("+confsub-");
 			break;
 		case SUB_DIGEST:
-			subtextfilename = concatstr(2, listdir,
-					"/text/sub-confirm-digest");
+			listtext = mystrdup("sub-confirm-digest");
 			tmpstr = mystrdup("+confsub-digest-");
 			break;
 		case SUB_NOMAIL:
-			subtextfilename = concatstr(2, listdir,
-					"/text/sub-confirm-nomail");
+			listtext = mystrdup("sub-confirm-nomail");
 			tmpstr = mystrdup("+confsub-nomail-");
 			break;
 	}
@@ -276,94 +198,22 @@
 	myfree(randomstr);
 	myfree(tmpstr);
 
-	if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
-		log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
-		myfree(subtextfilename);
-		exit(EXIT_FAILURE);
-	}
-
-	myfree(subtextfilename);
-
-        do {
-                randomstr = random_str();
-                myfree(queuefilename);
-                queuefilename = concatstr(3, listdir, "/queue/", randomstr);
-                myfree(randomstr);
-
-                queuefd = open(queuefilename, O_RDWR|O_CREAT|O_EXCL,
-					      S_IRUSR|S_IWUSR);
-
-        } while ((queuefd < 0) && (errno == EEXIST));
-
-	if(queuefd < 0) {
-		log_error(LOG_ARGS, "Could not open '%s'", queuefilename);
-		myfree(queuefilename);
-		exit(EXIT_FAILURE);
-	}
-
-	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;
-	}
+	maildata[1] = mystrdup(subaddr);
+	maildata[3] = mystrdup(confirmaddr);
 
-	s1 = concatstr(10, "From: ", listname, "+help@", listfqdn, "\nTo: ",
-			   subaddr, "\nSubject: Confirm subscribe ", subject,
-			   listaddr, "\n\n");
-	myfree(subject);
-
-	if(writen(queuefd, s1, strlen(s1)) < 0) {
-		log_error(LOG_ARGS, "Could not write subconffile");
-		exit(EXIT_FAILURE);
-	}
-
-	myfree(s1);
+	queuefilename = prepstdreply(listdir, listtext, "$helpaddr$", subaddr,
+				     confirmaddr, 2, maildata);
 
-	while((buf = mygetline(subtextfd))) {
-		if(strncmp(buf, "*LSTADDR*", 9) == 0) {
-			if(writen(queuefd, listaddr, strlen(listaddr)) < 0) {
-				log_error(LOG_ARGS,
-					"Could not write subconffile");
-				exit(EXIT_FAILURE);
-			}
-		} else if(strncmp(buf, "*SUBADDR*", 9) == 0) {
-			if(writen(queuefd, subaddr, strlen(subaddr)) < 0) {
-				log_error(LOG_ARGS,
-					"Could not write subconffile");
-				exit(EXIT_FAILURE);
-			}
-		} else if(strncmp(buf, "*CNFADDR*", 9) == 0) {
-			if(writen(queuefd, confirmaddr, strlen(confirmaddr))
-					< 0) {
-				log_error(LOG_ARGS,
-					"Could not write subconffile");
-				exit(EXIT_FAILURE);
-			}
-		} else {
-			if(writen(queuefd, buf, strlen(buf)) < 0) {
-				log_error(LOG_ARGS,
-					"Could not write subconffile");
-				exit(EXIT_FAILURE);
-			}
-		}
-	}
+	myfree(maildata[1]);
+	myfree(maildata[3]);
 
 	myfree(listname);
 	myfree(listfqdn);
-	close(subtextfd);
-	close(queuefd);
 
 	execlp(mlmmjsend, mlmmjsend,
 				"-l", "1",
 				"-T", subaddr,
 				"-F", fromaddr,
-				"-R", confirmaddr,
 				"-m", queuefilename, NULL);
 	log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsend);
 	exit(EXIT_FAILURE);
--- a/src/mlmmj-unsub.c	Thu Nov 11 00:37:42 2004 +1100
+++ b/src/mlmmj-unsub.c	Fri Nov 12 00:10:54 2004 +1100
@@ -50,104 +50,39 @@
 		   const char *subaddr, const char *mlmmjsend,
 		   enum subtype typesub)
 {
-	int subtextfd, queuefd;
-	char *buf, *subtextfilename, *randomstr, *queuefilename = NULL;
-	char *fromaddr, *listname, *listfqdn, *s1, *subject;
-
-	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) {
-		log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
-		myfree(subtextfilename);
-		exit(EXIT_FAILURE);
-	}
-	myfree(subtextfilename);
+	char *queuefilename, *fromaddr, *listname, *listfqdn, *listtext;
 
 	listname = genlistname(listaddr);
 	listfqdn = genlistfqdn(listaddr);
 
-	do {
-		randomstr = random_str();
-		myfree(queuefilename);
-		queuefilename = concatstr(3, listdir, "/queue/", randomstr);
-		myfree(randomstr);
-
-		queuefd = open(queuefilename, O_RDWR|O_CREAT|O_EXCL,
-					      S_IRUSR|S_IWUSR);
+	fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
 
-	} while ((queuefd < 0) && (errno == EEXIST));
-
-	if(queuefd < 0) {
-		log_error(LOG_ARGS, "Could not open '%s'", queuefilename);
-		myfree(queuefilename);
-		exit(EXIT_FAILURE);
-	}
-
-	fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
+	myfree(listname);
+	myfree(listfqdn);
 
 	switch(typesub) {
 		case SUB_NORMAL:
-			subject = mystrdup("from ");
+			listtext = mystrdup("/text/unsub-ok");
 			break;
 		case SUB_DIGEST:
-			subject = mystrdup("from digest of ");
+			listtext = mystrdup("/text/unsub-ok-digest");
 			break;
 		case SUB_NOMAIL:
-			subject = mystrdup("from nomail version of ");
+			listtext = mystrdup("/text/unsub-ok-nomail");
 			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);
-	}
-
-	myfree(s1);
-
-	while((buf = mygetline(subtextfd))) {
-		if(strncmp(buf, "*LSTADDR*", 9) == 0) {
-			if(writen(queuefd, listaddr, strlen(listaddr)) < 0) {
-				log_error(LOG_ARGS,
-					"Could not write goodbye mail");
-				exit(EXIT_FAILURE);
-			}
-		} else {
-			if(writen(queuefd, buf, strlen(buf)) < 0) {
-				log_error(LOG_ARGS,
-					"Could not write goodbye mail");
-				exit(EXIT_FAILURE);
-			}
-		}
-	}
-
-	myfree(listname);
-	myfree(listfqdn);
-	close(subtextfd);
-	close(queuefd);
+	queuefilename = prepstdreply(listdir, listtext, "$helpaddr$",
+				     subaddr, NULL, 0, NULL);
+	MY_ASSERT(queuefilename);
+	myfree(listtext);
 
 	execlp(mlmmjsend, mlmmjsend,
 				"-l", "1",
 				"-T", subaddr,
 				"-F", fromaddr,
 				"-m", queuefilename, NULL);
+
 	log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsend);
 	exit(EXIT_FAILURE);
 }
@@ -156,55 +91,45 @@
 		  const char *subaddr, const char *mlmmjsend,
 		  enum subtype typesub)
 {
-        char *maildata[4] = { "*LSTADDR*", NULL, "*SUBADDR*", NULL };
-        char *listfqdn, *listname, *fromaddr, *fromstr, *subject;
+        char *maildata[4] = { "$oldsub$", NULL };
+        char *listfqdn, *listname, *fromaddr, *tostr;
         char *queuefilename = NULL, *listtext;
 
         listname = genlistname(listaddr);
         listfqdn = genlistfqdn(listaddr);
-        maildata[1] = mystrdup(listaddr);
-        maildata[3] = mystrdup(subaddr);
+
+        maildata[1] = mystrdup(subaddr);
+
         fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
-        fromstr = concatstr(3, listname, "+owner@", listfqdn);
+	tostr = concatstr(3, listname, "+owner@", listfqdn);
+
+	myfree(listname);
+	myfree(listfqdn);
 
 	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);
 			listtext = mystrdup("notifyunsub-digest");
 			break;
 		case SUB_NOMAIL:
-			subject = concatstr(2,
-					"Unsubscription from nomail of ",
-					listaddr);
 			listtext = mystrdup("notifyunsub-nomail");
 			break;
 	}
 
-	
-	queuefilename = prepstdreply(listdir, listtext, fromstr, fromstr,
-				     NULL, subject, 2, maildata);
+	queuefilename = prepstdreply(listdir, listtext, "$listowner$",
+				     "$listowner$", NULL, 1, maildata);
 	MY_ASSERT(queuefilename);
 	myfree(listtext);
-	myfree(listname);
-	myfree(listfqdn);
-	myfree(subject);
 	myfree(maildata[1]);
-	myfree(maildata[3]);
+
 	execlp(mlmmjsend, mlmmjsend,
 			"-l", "1",
-			"-T", fromstr,
+			"-T", tostr,
 			"-F", fromaddr,
 			"-m", queuefilename, NULL);
 
-	myfree(fromstr);
-
         log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsend);
         exit(EXIT_FAILURE);
 }
@@ -214,10 +139,11 @@
 			   const char *subaddr, const char *mlmmjsend,
 			   enum subtype typesub)
 {
-	char *confirmaddr, *buf, *listname, *listfqdn, *tmpstr;
-	char *subtextfilename, *queuefilename = NULL, *fromaddr, *s1;
-	char *randomstr = NULL, *confirmfilename = NULL, *subject;
-	int subconffd, subtextfd, queuefd;
+	char *confirmaddr, *listname, *listfqdn, *tmpstr;
+	char *queuefilename, *fromaddr;
+	char *randomstr = NULL, *confirmfilename = NULL, *listtext;
+	char *maildata[4] = { "$subaddr$", NULL, "$confaddr$", NULL };
+	int subconffd;
 
 	listname = genlistname(listaddr);
 	listfqdn = genlistfqdn(listaddr);
@@ -257,18 +183,15 @@
 
 	switch(typesub) {
 		case SUB_NORMAL:
-			subtextfilename = concatstr(2, listdir,
-						"/text/unsub-confirm");
+			listtext = mystrdup("unsub-confirm");
 			tmpstr = mystrdup("+confunsub-");
 			break;
 		case SUB_DIGEST:
-			subtextfilename = concatstr(2, listdir,
-						"/text/unsub-confirm-digest");
+			listtext = mystrdup("unsub-confirm-digest");
 			tmpstr = mystrdup("+confunsub-digest-");
 			break;
 		case SUB_NOMAIL:
-			subtextfilename = concatstr(2, listdir,
-						"/text/unsub-confirm-nomail");
+			listtext = mystrdup("unsub-confirm-nomail");
 			tmpstr = mystrdup("+confunsub-nomail-");
 			break;
 	}
@@ -278,92 +201,22 @@
 	myfree(randomstr);
 	myfree(tmpstr);
 
-	if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
-		log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
-		myfree(subtextfilename);
-		exit(EXIT_FAILURE);
-	}
-	myfree(subtextfilename);
-
-	do {
-		randomstr = random_str();
-		myfree(queuefilename);
-		queuefilename = concatstr(3, listdir, "/queue/", randomstr);
-		myfree(randomstr);
-
-		queuefd = open(queuefilename, O_RDWR|O_CREAT|O_EXCL,
-					      S_IRUSR|S_IWUSR);
-
-	} while ((queuefd < 0) && (errno == EEXIST));
-
-	if(queuefd < 0) {
-		log_error(LOG_ARGS, "Could not open '%s'", queuefilename);
-		myfree(queuefilename);
-		exit(EXIT_FAILURE);
-	}
-
-	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;
-	}
+	maildata[1] = mystrdup(subaddr);
+	maildata[3] = mystrdup(confirmaddr);
 
-	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);
-	}
-
-	myfree(s1);
+	queuefilename = prepstdreply(listdir, listtext, "$helpaddr$", subaddr,
+				     confirmaddr, 2, maildata);
 
-	while((buf = mygetline(subtextfd))) {
-		if(strncmp(buf, "*LSTADDR*", 9) == 0) {
-			if(writen(queuefd, listaddr, strlen(listaddr)) < 0) {
-				log_error(LOG_ARGS,
-					"Could not write subconffile");
-				exit(EXIT_FAILURE);
-			}
-		} else if(strncmp(buf, "*SUBADDR*", 9) == 0) {
-			if(writen(queuefd, subaddr, strlen(subaddr)) < 0) {
-				log_error(LOG_ARGS,
-					"Could not write subconffile");
-				exit(EXIT_FAILURE);
-			}
-		} else if(strncmp(buf, "*CNFADDR*", 9) == 0) {
-			if(writen(queuefd, confirmaddr, strlen(confirmaddr))
-					< 0) {
-				log_error(LOG_ARGS,
-					"Could not write subconffile");
-				exit(EXIT_FAILURE);
-			}
-		} else {
-			if(writen(queuefd, buf, strlen(buf)) < 0) {
-				log_error(LOG_ARGS,
-					"Could not write subconffile");
-				exit(EXIT_FAILURE);
-			}
-		}
-	}
+	myfree(maildata[1]);
+	myfree(maildata[3]);
 
 	myfree(listname);
 	myfree(listfqdn);
-	close(subtextfd);
-	close(queuefd);
 
 	execlp(mlmmjsend, mlmmjsend,
 				"-l", "1",
 				"-T", subaddr,
 				"-F", fromaddr,
-				"-R", confirmaddr,
 				"-m", queuefilename, NULL);
 	log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsend);
 	exit(EXIT_FAILURE);
@@ -654,8 +507,6 @@
 		myfree(subreadname);
 		myfree(subwritename);
 
-		closedir(subddir);
-
 		if(confirmunsub) {
 			childpid = fork();
 
@@ -678,6 +529,7 @@
 		}
         }
 
+	closedir(subddir);
 	myfree(subdir);
 
         notifysub = statctrl(listdir, "notifysub");
--- a/src/prepstdreply.c	Thu Nov 11 00:37:42 2004 +1100
+++ b/src/prepstdreply.c	Fri Nov 12 00:10:54 2004 +1100
@@ -22,6 +22,7 @@
  */
 
 #include <stdlib.h>
+#include <stdio.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -36,14 +37,86 @@
 #include "mygetline.h"
 #include "wrappers.h"
 #include "memory.h"
+#include "getlistaddr.h"
+
+char *substitute(const char *line, const char *listaddr, size_t datacount,
+		 char **data)
+{
+	char *fqdn, *listname, *d1, *d2, *token, *value, *retstr;
+	char *origline = mystrdup(line);
+	size_t len, i;
+	
+	d1 = strchr(origline, '$');
+	d2 = strchr(d1 + 1, '$');
+	
+	if(d1 && d2) {
+		len = d2 - d1;
+		token = mymalloc(len + 1);
+		snprintf(token, len, "%s", d1 + 1);
+	} else
+		return origline;
+
+	*d1 = '\0';
+
+	fqdn = genlistfqdn(listaddr);
+	listname = genlistname(listaddr);
+
+	if(strcmp(token, "listaddr") == 0) {
+		value = mystrdup(listaddr);
+		goto concatandreturn;
+	} else if(strcmp(token, "listowner") == 0) {
+		value = concatstr(3, listname, "+owner@", fqdn);
+		goto concatandreturn;
+	} else if(strcmp(token, "helpaddr") == 0) {
+		value = concatstr(3, listname, "+help@", fqdn);
+		goto concatandreturn;
+	} else if(strcmp(token, "listgetN") == 0) {
+		value = concatstr(3, listname, "+get-N@", fqdn);
+		goto concatandreturn;
+	} else if(strcmp(token, "listunsubaddr") == 0) {
+		value = concatstr(3, listname, "+unsubscribe@", fqdn);
+		goto concatandreturn;
+	} else if(strcmp(token, "digestunsubaddr") == 0) {
+		value = concatstr(3, listname, "+unsubscribe-digest@", fqdn);
+		goto concatandreturn;
+	} else if(strcmp(token, "nomailunsubaddr") == 0) {
+		value = concatstr(3, listname, "+unsubscribe-nomail@", fqdn);
+		goto concatandreturn;
+	} else if(strcmp(token, "listsubaddr") == 0) {
+		value = concatstr(3, listname, "+subscribe@", fqdn);
+		goto concatandreturn;
+	} else if(strcmp(token, "digestsubaddr") == 0) {
+		value = concatstr(3, listname, "+subscribe-digest@", fqdn);
+		goto concatandreturn;
+	} else if(strcmp(token, "nomailsubaddr") == 0) {
+		value = concatstr(3, listname, "+subscribe-nomail@", fqdn);
+		goto concatandreturn;
+	}
+	if(data) {
+		for(i = 0; i < datacount; i++) {
+			if(strcmp(token, data[i*2]) == 0) {
+				value = mystrdup(data[(i*2)+1]);
+			}
+		}
+	}
+concatandreturn:
+	retstr = concatstr(3, origline, value, d2 + 1);
+	myfree(origline);
+	myfree(value);
+	myfree(token);
+	myfree(fqdn);
+	myfree(listname);
+
+	return retstr;
+}
 
 char *prepstdreply(const char *listdir, const char *filename, const char *from,
-		   const char *to, const char *replyto, const char *subject,
-		   size_t tokencount, char **data)
+		   const char *to, const char *replyto, size_t tokencount,
+		   char **data)
 {
 	int infd, outfd;
-	size_t i;
-	char *str, *tmp, *retstr = NULL;
+	char *listaddr, *myfrom, *str, *tmp, *subject, *retstr = NULL;
+	char *myreplyto;
 
 	tmp = concatstr(3, listdir, "/text/", filename);
 	infd = open(tmp, O_RDONLY);
@@ -53,16 +126,25 @@
 		return NULL;
 	}
 
-	tmp = concatstr(6, "From: ", from,
-			"\nTo: ", to,
-			"\nSubject: ", subject);
-	if(replyto)
-		str = concatstr(3, tmp, "\nReply-To: ", replyto, "\n\n");
-	else
-		str = concatstr(2, tmp, "\n\n");
+	listaddr = getlistaddr(listdir);
+
+	tmp = mygetline(infd);
+	if(strncasecmp(tmp, "Subject:", 8) != 0) {
+		log_error(LOG_ARGS, "No Subject in listtexts. Using "
+				"standard subject");
+		subject = mystrdup("mlmmj administrativa");
+	} else
+		subject = substitute(tmp, listaddr, tokencount, data);
 		
 	myfree(tmp);
 
+	myfrom = substitute(from, listaddr, tokencount, data);
+	
+	if(replyto)
+		myreplyto = substitute(replyto, listaddr, tokencount, data);
+	else
+		myreplyto = NULL;
+		
 	do {
 		tmp = random_str();
 		myfree(retstr);
@@ -79,20 +161,20 @@
 		return NULL;
 	}
 
+	str = concatstr(4, myfrom, to, replyto, subject);
+
 	if(writen(outfd, str, strlen(str)) < 0) {
 		log_error(LOG_ARGS, "Could not write std mail");
 		myfree(str);
 		return NULL;
 	}
+
 	myfree(str);
 
 	while((str = mygetline(infd))) {
-		for(i = 0; i < tokencount; i++) {
-			if(strncmp(str, data[i*2], strlen(data[i*2])) == 0) {
-				myfree(str);
-				str = mystrdup(data[(i*2)+1]);
-			}
-		}
+		tmp = str;
+		str = substitute(tmp, listaddr, tokencount, data);
+		myfree(tmp);
 		if(writen(outfd, str, strlen(str)) < 0) {
 			myfree(str);
 			log_error(LOG_ARGS, "Could not write std mail");
--- a/src/send_help.c	Thu Nov 11 00:37:42 2004 +1100
+++ b/src/send_help.c	Fri Nov 12 00:10:54 2004 +1100
@@ -45,39 +45,23 @@
 	       const char *mlmmjsend)
 {
 	char *queuefilename, *listaddr, *listname, *listfqdn, *fromaddr;
-	char *fromstr, *subject;
-	char *maildata[] = { "*UNSUBADDR*", NULL, "*SUBADDR*", NULL,
-			     "*HLPADDR*", NULL };
 
         listaddr = getlistaddr(listdir);
-	chomp(listaddr);
-
 	listname = genlistname(listaddr);
 	listfqdn = genlistfqdn(listaddr);
 
 	fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
 
-        maildata[1] = concatstr(3, listname, "+unsubscribe@", listfqdn);
-	maildata[3] = concatstr(3, listname, "+subscribe@", listfqdn);
-	maildata[5] = concatstr(3, listname, "+help@", listfqdn);
-	fromstr = concatstr(3, listname, "+owner@", listfqdn);
-	subject = concatstr(2, "Help for ", listaddr);
-
-	queuefilename = prepstdreply(listdir, "listhelp", fromstr, emailaddr,
-				NULL, subject, 3, maildata);
+	queuefilename = prepstdreply(listdir, "listhelp", "$listowner$",
+					emailaddr, NULL, 0, NULL);
 	if(queuefilename == NULL) {
 		log_error(LOG_ARGS, "Could not prepare help mail");
 		exit(EXIT_FAILURE);
 	}
 	
-	myfree(fromstr);
 	myfree(listaddr);
 	myfree(listname);
 	myfree(listfqdn);
-	myfree(maildata[1]);
-	myfree(maildata[3]);
-	myfree(maildata[5]);
-	myfree(subject);
 
 	execlp(mlmmjsend, mlmmjsend,
 				"-l", "1",