summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMalfurious <m@lfurio.us>2024-06-15 17:43:00 -0400
committerMalfurious <m@lfurio.us>2024-06-15 17:43:00 -0400
commit2e2e06f47683d484e4852c2978fd787660ad730e (patch)
treeb1f25e6b9fe681144c82b361527965a535a535d6
parent2ee344e9c7ab8182efde6a18e50d4b4e7d148e71 (diff)
parent57d0c93851d72789c2adda1ed1e8b671bcd3f884 (diff)
downloadmailnode-2e2e06f47683d484e4852c2978fd787660ad730e.tar.gz
mailnode-2e2e06f47683d484e4852c2978fd787660ad730e.zip
Merge branch 'mailing-lists'
* mailing-lists: mlmmj: Add maintenance service mlmmj: Don't mention FAQ address mlmmj: Use simpler message prologue mlmmj: Add user configuration directories mlmmj: Integration with postfix mlmmj: Setup mailing list package
-rw-r--r--Dockerfile9
-rw-r--r--docker-compose.yml11
-rwxr-xr-xpostfix/entrypoint.sh45
-rw-r--r--postfix/main.cf6
-rwxr-xr-xpostfix/make_list.sh13
-rw-r--r--postfix/master.cf2
-rw-r--r--userconfig/lists/.gitkeep0
-rw-r--r--userconfig/listtext/confirm52
-rw-r--r--userconfig/listtext/deny76
-rw-r--r--userconfig/listtext/deny-post70
-rw-r--r--userconfig/listtext/digest5
-rw-r--r--userconfig/listtext/faq4
-rw-r--r--userconfig/listtext/finish55
-rw-r--r--userconfig/listtext/finish-sub44
-rw-r--r--userconfig/listtext/gatekeep-sub19
-rw-r--r--userconfig/listtext/help85
-rw-r--r--userconfig/listtext/list25
-rw-r--r--userconfig/listtext/moderate-post50
-rw-r--r--userconfig/listtext/notify41
-rw-r--r--userconfig/listtext/probe11
-rw-r--r--userconfig/listtext/prologue1
-rw-r--r--userconfig/listtext/subrelease20
-rw-r--r--userconfig/listtext/wait-post38
-rw-r--r--userconfig/listtext/wait-sub7
24 files changed, 680 insertions, 9 deletions
diff --git a/Dockerfile b/Dockerfile
index ec181bd..55b4e01 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -18,14 +18,23 @@ RUN useradd \
--home-dir /var/spool/postfix \
postfix
+RUN useradd \
+ --uid 2002 --system \
+ --shell /usr/sbin/nologin \
+ --home-dir /var/spool/mlmmj \
+ --skel /dev/null --create-home \
+ mlmmj
+
# Install packages
RUN apt update \
&& apt full-upgrade --yes \
&& apt install --yes \
dovecot-core \
dovecot-imapd \
+ mlmmj \
postfix \
postfix-pcre \
+ sudo \
&& apt clean
# Install files
diff --git a/docker-compose.yml b/docker-compose.yml
index a636181..e4c8a42 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -17,6 +17,7 @@ services:
- "certs:/etc/certs"
- "mail:/var/mail"
- "postfix:/var/spool/postfix"
+ - "mlmmj:/var/spool/mlmmj"
ports:
- "25:25"
- "465:465"
@@ -45,12 +46,22 @@ services:
LETSENCRYPT_HOST: "YOUR-DOMAIN.example"
command: ["/usr/sbin/dovecot", "-F"]
+ mlmmj-maint:
+ image: "mailnode"
+ pull_policy: "never"
+
+ restart: "always"
+ volumes:
+ - "mlmmj:/var/spool/mlmmj"
+ command: ["bash", "-c", "/usr/bin/mlmmj-maintd -d /var/spool/mlmmj && sleep infinity"]
+
volumes:
certs:
external: true
name: "proxy-docker_certs"
mail:
postfix:
+ mlmmj:
networks:
nginx-proxy-network:
diff --git a/postfix/entrypoint.sh b/postfix/entrypoint.sh
index 46c06ab..afea241 100755
--- a/postfix/entrypoint.sh
+++ b/postfix/entrypoint.sh
@@ -1,18 +1,47 @@
#!/bin/sh
-# Generate virtual mappings from user password file
+users() {
+ sed 's/:.*$//g' /etc/userconfig/passwd
+}
+
+## Generate virtual mappings from user password file
# Incoming mail: "user@domain domain/user/" >vmailbox
-# (The trailing slash indicates the directory is a maildir.)
-sed 's/:.*$//g' </etc/userconfig/passwd \
- | awk -F '@' '{printf "%s %s/%s/\n", $0, $2, $1}' >/etc/postfix/vmailbox
+# Outgoing mail: "user@domain user@domain" >vaddress
+users | awk -F '@' '{printf "%s %s/%s/\n", $0, $2, $1}' >/etc/postfix/vmailbox
+users | awk '{printf "%s %s\n", $0, $0}' >/etc/postfix/vaddress
+
+## Synchronize mailing list configuration directories
+eul=$(ls /etc/userconfig/lists | sed '/ /d') # Ignore spaces and dotfiles
+vsl=$(ls -A /var/spool/mlmmj)
+added=$(printf '%s\n' "$vsl" "$vsl" "$eul" | sort | uniq -u)
+removed=$(printf '%s\n' "$eul" "$eul" "$vsl" | sort | uniq -u)
+
+for list in $added; do
+ sudo -u mlmmj /etc/postfix/make_list.sh "$list"
+done
+for list in $removed; do
+ listdir="/var/spool/mlmmj/$list"
+ rm -rf "$listdir"
+ printf 'Deleted list directory: %s\n' "$listdir"
+done
-# Outgoing mail: "user@domain user@domain" >vaddress
-sed 's/:.*$//g' </etc/userconfig/passwd \
- | awk '{printf "%s %s\n", $0, $0}' >/etc/postfix/vaddress
+## Generate virtual mappings from mailing list addresses
+# "address@domain list@localhost.mlmmj" >mlvirtual
+# "list@localhost.mlmmj mlmmj:list" >mltransport
+touch /etc/postfix/mlvirtual
+touch /etc/postfix/mltransport
+for list in $(ls -A /var/spool/mlmmj); do
+ address="/var/spool/mlmmj/$list/control/listaddress" # there could be multiple
+ sed "s/$/ $list@localhost.mlmmj/g" <$address >>/etc/postfix/mlvirtual
+ echo "$list@localhost.mlmmj mlmmj:$list" >>/etc/postfix/mltransport
+done
-# Generate Berkeley DB files
+## Generate Berkeley DB files
+postmap /etc/postfix/mltransport
+postmap /etc/postfix/mlvirtual
postmap /etc/postfix/vaddress
postmap /etc/postfix/vmailbox
postmap /etc/userconfig/aliases
+## Execute postfix daemons
exec /usr/sbin/postfix start-fg
diff --git a/postfix/main.cf b/postfix/main.cf
index 7ded588..7f701a8 100644
--- a/postfix/main.cf
+++ b/postfix/main.cf
@@ -53,4 +53,8 @@ virtual_uid_maps = static:2000
virtual_gid_maps = static:2000
virtual_mailbox_limit = 0
-virtual_alias_maps = hash:/etc/userconfig/aliases
+# Mlmmj mailing list parameters
+mlmmj_destination_recipient_limit = 1
+transport_maps = hash:/etc/postfix/mltransport
+
+virtual_alias_maps = hash:/etc/userconfig/aliases hash:/etc/postfix/mlvirtual
diff --git a/postfix/make_list.sh b/postfix/make_list.sh
new file mode 100755
index 0000000..45a198e
--- /dev/null
+++ b/postfix/make_list.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+mkdir -p "/var/spool/mlmmj/$1"
+cd "/var/spool/mlmmj/$1"
+
+mkdir -p incoming queue queue/discarded archive subconf unsubconf bounce \
+ moderation subscribers.d digesters.d requeue nomailsubs.d
+
+touch index
+ln -s "/etc/userconfig/lists/$1" control
+ln -s /etc/userconfig/listtext text
+
+printf 'Created list directory: %s\n' "$(pwd)"
diff --git a/postfix/master.cf b/postfix/master.cf
index 935dbec..3511856 100644
--- a/postfix/master.cf
+++ b/postfix/master.cf
@@ -65,6 +65,8 @@ postlog unix-dgram n - n - 1 postlogd
# agent. See the pipe(8) man page for information about ${recipient}
# and other message envelope options.
# ====================================================================
+mlmmj unix - n n - - pipe
+ flags=ORhu user=mlmmj argv=/usr/bin/mlmmj-receive -F -L /var/spool/mlmmj/${nexthop}
#
# maildrop. See the Postfix MAILDROP_README file for details.
# Also specify in main.cf: maildrop_destination_recipient_limit=1
diff --git a/userconfig/lists/.gitkeep b/userconfig/lists/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/userconfig/lists/.gitkeep
diff --git a/userconfig/listtext/confirm b/userconfig/listtext/confirm
new file mode 100644
index 0000000..ae8cbdd
--- /dev/null
+++ b/userconfig/listtext/confirm
@@ -0,0 +1,52 @@
+%ifaction sub%Subject: Confirm subscription to $list$@$domain$%endif%
+%ifaction unsub%Subject: Confirm unsubscribe from $list$@$domain$%endif%
+
+%text prologue%
+
+%ifaction sub%
+ %^%%wrap%
+ %ifreason admin%
+ An administrator
+ %endif%
+ %ifreason request%
+ Somebody (and we hope it was you)
+ %endif%
+ has requested that your email address <$subaddr$> be added
+ %iftype normal%
+ to the list. This means every time a post is sent to the list, you will
+ receive a copy of it.
+ %endif%
+ %iftype digest%
+ to the list, to receive digests. This means you will receive multiple
+ posts in a single mail message, at regular intervals, or when a lot of
+ posts have accumulated.
+ %endif%
+ %iftype nomail%
+ to the list, without mail delivery. This means you will not receive any
+ posts to the list, but you are considered a member. This means, for
+ instance, you are able to post to a list which only subscribers may post
+ to, while you follow the list using a web archive or another subscribed
+ email address.
+ %endif%
+%endif%
+
+%ifaction unsub%
+ %^%%wrap%
+ %ifreason admin%
+ An administrator
+ %endif%
+ %ifreason request%
+ Somebody (and we hope it was you)
+ %endif%
+ has requested that the email address <$subaddr$> be removed from the list.
+%endif%
+
+%wrap%To confirm you want to do this, please send a message to <$confaddr$>
+which can usually be done simply by replying to this message. The subject
+and the body of the message can be anything.
+
+After doing so, you should receive a reply informing you that the operation
+succeeded.
+
+If you do not want to do this, simply ignore this message.
+
diff --git a/userconfig/listtext/deny b/userconfig/listtext/deny
new file mode 100644
index 0000000..e02b1be
--- /dev/null
+++ b/userconfig/listtext/deny
@@ -0,0 +1,76 @@
+%ifaction sub%Subject: Unable to subscribe to $list$@$domain$%endif%
+%ifaction unsub%Subject: Unable to unsubscribe from $list$@$domain$%endif%
+%ifaction release reject%Subject: Unable to moderate $list$@$domain$%endif%
+%ifaction permit obstruct%Subject: Unable to gatekeep $list$@$domain$%endif%
+
+%text prologue%
+
+%ifaction sub%
+ %^%%wrap%You were unable to be subscribed to the list
+ %ifreason disabled%
+ because the
+ %iftype normal% normal %endif%
+ %iftype digest% digest %endif%
+ %iftype nomail% no-mail %endif%
+ version of the list is turned off.
+ %endif%
+ %ifreason closed%
+ because people are not allowed to subscribe to this list by email.
+ %endif%
+ %ifreason subbed%
+ because you are already subscribed.
+ %endif%
+ %ifreason expired%
+ because too much time passed without a gatekeeper permitting your entry.
+ %endif%
+ %ifreason obstruct%
+ because a gatekeeper obstructed your entry.
+ %endif%
+%endif%
+
+%ifaction unsub%
+ %^%%wrap%You were unable to be unsubscribed from the list
+ %ifreason unsubbed%
+ because you are not subscribed.
+
+ %^%%wrap%If you are receiving messages, perhaps a different email
+ address is subscribed. To find out which address you are subscribed
+ with, refer to the message welcoming you to the list, or look at the
+ envelope "Return-Path" header of a message you receive from the list.
+ %endif%
+%endif%
+
+%ifaction release reject%
+ %^%%wrap%
+ %ifaction release%
+ You were unable to release the specified post to the list
+ %endif%
+ %ifaction reject%
+ You were unable to reject the specified post
+ %endif%
+ %ifreason notfound%
+ because it could not be found. Perhaps another moderator already
+ released or rejected it, or it expired.
+ %endif%
+ %ifreason moderators%
+ because you are not a moderator for the list.
+ %endif%
+%endif%
+
+%ifaction permit obstruct%
+ %^%%wrap%
+ %ifaction permit%
+ You were unable to permit the specified subscription request
+ %endif%
+ %ifaction obstruct%
+ You were unable to obstruct the specified subscription request
+ %endif%
+ %ifreason notfound%
+ because it could not be found. Perhaps another gatekeeper already
+ permitted or obstructed it, or it expired.
+ %endif%
+ %ifreason gatekeepers%
+ because you are not a gatekeeper for the list.
+ %endif%
+%endif%
+
diff --git a/userconfig/listtext/deny-post b/userconfig/listtext/deny-post
new file mode 100644
index 0000000..9de308a
--- /dev/null
+++ b/userconfig/listtext/deny-post
@@ -0,0 +1,70 @@
+Subject: Post to $list$@$domain$ denied: $subject$
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="=_$random0$$random1$_="
+Content-Transfer-Encoding: 8bit
+
+--=_$random0$$random1$_=
+Content-Type: text/plain; charset=utf8
+Content-Transfer-Encoding: 8bit
+
+%text prologue%
+
+%wrap%The message from <$posteraddr$> with subject "$subject$" was unable to
+be delivered to the list
+%ifreason maxmailsize%
+ because it exceeded the maximum allowed message size of $maxmailsize$
+ bytes.
+%endif%
+%ifreason tocc%
+ because the list address was not found in either the To: or CC: header.
+%endif%
+%ifreason access%
+ because of an access rule set up by the list administrator.
+%endif%
+%ifreason expired%
+ because too much time passed without any moderator releasing it.
+%endif%
+%ifreason reject%
+ because a moderator rejected it.
+%endif%
+%ifreason subonlypost%
+ because you are not a list subscriber.
+
+ %ifncontrol closedlist%%ifncontrol closedlistsub%
+ %^%%text subrelease%
+ %endif%%endif%
+
+ %ifcontrol closedlist closedlistsub%
+ %^%%wrap%If you wish to become a subscriber, you will need to contact a
+ list administrator. You can email <$list+$owner@$domain$> to contact the
+ list owner.
+ %endif%
+
+ %^%%wrap%If you believe you are a subscriber, you are probably subscribed
+ with a different email address. To find out which address you are
+ subscribed with, refer to the message welcoming you to the list, or look
+ at the envelope "Return-Path" header of a message you receive from the
+ list.
+%endif%
+%ifreason modonlypost%
+ because you are not a list moderator.
+%endif%
+
+%ifreason maxmailsize%
+ %^%(The beginning of the denied message is below.)
+%else%
+ %^%(The denied message is below.)
+%endif%
+
+--=_$random0$$random1$_=
+Content-Type: message/rfc822
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="message.eml"
+
+%ifreason maxmailsize%
+ %^%%originalmail 300%
+%else%
+ %^%%originalmail%
+%endif%
+--=_$random0$$random1$_=--
diff --git a/userconfig/listtext/digest b/userconfig/listtext/digest
new file mode 100644
index 0000000..e9fa859
--- /dev/null
+++ b/userconfig/listtext/digest
@@ -0,0 +1,5 @@
+Subject: Digest of $list$@$domain$ issue $digestissue$ ($digestinterval$)
+
+Topics (messages $digestfirst$ through $digestlast$):
+- %digestthreads%
+
diff --git a/userconfig/listtext/faq b/userconfig/listtext/faq
new file mode 100644
index 0000000..28f22fe
--- /dev/null
+++ b/userconfig/listtext/faq
@@ -0,0 +1,4 @@
+Subject: Frequently asked questions of $list$@$domain$
+
+Sorry, no FAQ available yet.
+
diff --git a/userconfig/listtext/finish b/userconfig/listtext/finish
new file mode 100644
index 0000000..8560bae
--- /dev/null
+++ b/userconfig/listtext/finish
@@ -0,0 +1,55 @@
+%ifaction unsub%Subject: Goodbye from $list$@$domain$%endif%
+%ifaction release reject%Subject: Moderated $list$@$domain$: $subject$%endif%
+%ifaction permit obstruct%Subject: Guarded $list$@$domain$: $subaddr$%endif%
+%ifaction post%Subject: Posted to $list$@$domain$: $subject$%endif%
+
+%text prologue%
+
+%ifaction unsub%
+ %^%%wrap%
+ %ifreason request%
+ %endif%
+ %ifreason confirm%
+ Thank you for confirming your unsubscribe.
+ %endif%
+ %ifreason admin%
+ An administrator has removed you from the list.
+ %else%
+ You have now been removed from the list.
+ %endif%
+%endif%
+
+%ifaction release%
+ %^%%wrap%You have successfully released the message from <$posteraddr$>
+ with subject "$subject$" to the list.
+%endif%
+
+%ifaction reject%
+ %^%%wrap%You have successfully rejected the message from <$posteraddr$>
+ with subject "$subject$".
+%endif%
+
+%ifaction permit%
+ %^%%wrap%You have successfully permitted <$subaddr$> to join the list.
+%endif%
+
+%ifaction obstruct%
+ %^%%wrap%You have successfully obstructed <$subaddr$> from joining the
+ list.
+%endif%
+
+%ifaction post%
+ %^%%wrap%
+ %ifreason confirm%
+ Thank you for confirming
+ %endif%
+ %ifreason release%
+ A moderator has released
+ %endif%
+ %ifreason request%
+ Thank you for
+ %endif%
+ your post with subject "$subject$". It is now being distributed to the
+ list.
+%endif%
+
diff --git a/userconfig/listtext/finish-sub b/userconfig/listtext/finish-sub
new file mode 100644
index 0000000..64337a3
--- /dev/null
+++ b/userconfig/listtext/finish-sub
@@ -0,0 +1,44 @@
+Subject: Welcome to $list$@$domain$
+
+%text prologue%
+
+%wrap%
+%ifreason request%
+ Thank you for your request to join us.
+%endif%
+%ifreason confirm%
+ Thank you for confirming your subscription.
+%endif%
+%ifreason permit%
+ A gatekeeper has permitted you to join us.
+%endif%
+%ifreason switch%
+ Your subscription has been switched to the
+%else%
+ %ifreason admin%
+ An administrator has subscribed you to the
+ %else%
+ You have now been added to the
+ %endif%
+%endif%
+%iftype normal% normal %endif%
+%iftype digest% digest %endif%
+%iftype nomail% no-mail %endif%
+version of the list.
+
+%wrap%The email address you are subscribed with is <$subaddr$>.
+
+%ifcontrol closedlist%
+ %^%%wrap%If you ever wish to unsubscribe, you will need to contact a list
+ administrator. You can email <$list+$owner@$domain$> to contact the list
+ owner.
+%else%
+ %^%%wrap%If you ever wish to unsubscribe, send a message to
+ <$list+$unsubscribe@$domain$> using this email address. The subject and
+ the body of the message can be anything. You will then receive
+ confirmation or further instructions.
+%endif%
+
+%wrap%For other information and help about this list, send a message to
+<$list+$help@$domain$>.
+
diff --git a/userconfig/listtext/gatekeep-sub b/userconfig/listtext/gatekeep-sub
new file mode 100644
index 0000000..ba46088
--- /dev/null
+++ b/userconfig/listtext/gatekeep-sub
@@ -0,0 +1,19 @@
+Subject: Subscription request for $list$@$domain$: $subaddr$
+
+%text prologue%
+
+%wrap%There has been a request from <$subaddr$> to join the
+%iftype normal% normal %endif%
+%iftype digest% digest %endif%
+%iftype nomail% no-mail %endif%
+version of the list.
+
+%wrap%To permit this, please send a message to <$permitaddr$> which can
+usually be done simply by replying to this message.
+
+%wrap%If you do not want to do this, either send a message to
+<$obstructaddr$> or simply ignore this message.
+
+The following gatekeepers have received this mail:
+- %gatekeepers%
+
diff --git a/userconfig/listtext/help b/userconfig/listtext/help
new file mode 100644
index 0000000..bfb6fb1
--- /dev/null
+++ b/userconfig/listtext/help
@@ -0,0 +1,85 @@
+Subject: Information for $list$@$domain$
+
+%text prologue%
+
+Here is some information about the list.
+
+You can subscribe to the following versions:
+
+- %wrap%The normal version: Every time a post is sent to the list,
+subscribers receive a copy of it.
+%ifcontrol closedlist closedlistsub%
+ Subscribe by contacting a list administrator.
+%else%
+ Subscribe by emailing <$list+$subscribe@$domain$>.
+%endif%
+
+%ifncontrol nodigestsub%
+ %^%- %wrap%The digest version: Subscribers receive multiple posts in a
+ single mail message, at regular intervals, or when a lot of posts have
+ accumulated.
+ %ifcontrol closedlist closedlistsub%
+ Subscribe by contacting a list administrator.
+ %else%
+ Subscribe by emailing <$list+$subscribe-digest@$domain$>.
+ %endif%
+%endif%
+
+%ifncontrol nonomailsub%
+ %^%- %wrap%The no-mail version: Subscribers do not receive any posts to
+ the list. This means, though, they are able to post to a list which only
+ subscribers may post to, while they follow the list using a web archive or
+ another subscribed email address.
+ %ifcontrol closedlist closedlistsub%
+ Subscribe by contacting a list administrator.
+ %else%
+ Subscribe by emailing <$list+$subscribe-nomail@$domain$>.
+ %endif%
+%endif%
+
+%ifcontrol submod%
+ %^%%wrap%The list has gatekeepers who will review subscription requests
+ before permitting new members.
+%endif%
+
+%ifcontrol closedlist%
+ %^%%wrap%Unsubscribe by contacting a list administrator.
+%else%
+ %^%%wrap%Unsubscribe by emailing <$list+$unsubscribe@$domain$>.
+%endif%
+
+%wrap%Posts are made by emailing <$list$@$domain$>.
+
+%ifcontrol subonlypost%%ifncontrol modnonsubposts%
+ %^%%wrap%However, only subscribers may post to the list.
+%endif%%endif%
+
+%ifcontrol moderated%
+ %^%%wrap%The list has moderators who will review all posts before
+ releasing them to the list.
+%else%
+ %ifcontrol subonlypost%%ifcontrol modnonsubposts%
+ %^%%wrap%The list has moderators who will review posts from
+ non-subscribers before releasing them to the list.
+ %endif%%endif%
+%endif%
+
+%ifcontrol access%
+ %^%%wrap%The list also has access rules which may affect who can post and
+ which posts are moderated.
+%endif%
+
+%ifncontrol noget%%ifncontrol noarchive%
+ %^%%wrap%
+ %ifcontrol subonlyget%
+ Anyone
+ %else%
+ Subscribers
+ %endif%
+ can retrieve message number N from the list's archive by sending a message
+ to <$list+$get-N@$domain$> (change the N to the number of the desired
+ message).
+%endif%%endif%
+
+%wrap%To contact the list owner, send a message to <$list+$owner@$domain$>.
+
diff --git a/userconfig/listtext/list b/userconfig/listtext/list
new file mode 100644
index 0000000..52f3be8
--- /dev/null
+++ b/userconfig/listtext/list
@@ -0,0 +1,25 @@
+Subject: Subscribers to $list$@$domain$
+
+%text prologue%
+
+%wrap%Here is the list of subscribers
+%iftype all%
+ (to all versions of the list):
+%else%
+ to the
+ %iftype normal% normal %endif%
+ %iftype digest% digest %endif%
+ %iftype nomail% no-mail %endif%
+ version of the list:
+%endif%
+
+%iftype all normal%
+ %^%- %listsubs%
+%endif%
+%iftype all digest%
+ %^%- %digestsubs%
+%endif%
+%iftype all nomail%
+ %^%- %nomailsubs%
+%endif%
+
diff --git a/userconfig/listtext/moderate-post b/userconfig/listtext/moderate-post
new file mode 100644
index 0000000..18944c3
--- /dev/null
+++ b/userconfig/listtext/moderate-post
@@ -0,0 +1,50 @@
+Subject: Please moderate $list$@$domain$: $subject$
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="=_$random0$$random1$_="
+Content-Transfer-Encoding: 8bit
+
+--=_$random0$$random1$_=
+Content-Type: text/plain; charset=utf8
+Content-Transfer-Encoding: 8bit
+
+%text prologue%
+
+%wrap%A message from <$posteraddr$> with subject "$subject$" has been
+submitted for posting. You are being asked to moderate
+%ifreason modnonsubposts%
+ because the requester is not a subscriber.
+%endif%
+%ifreason moderated%
+ because this is a moderated list.
+%endif%
+%ifreason access%
+ because of an access rule.
+%endif%
+The message is below.
+
+%wrap%To release it to the list, please send a message to <$releaseaddr$>
+which can usually be done simply by replying to this message.
+
+%ifcontrol subonrelease%
+ %^%%wrap%If you wish, you can simultaneously release the post and
+ subscribe the poster by sending a message to one of the following
+ addresses:%nowrap%
+ %^%- %wrap%Normal version: <$listsubreleaseaddr$>%nowrap%
+ %^%- %wrap%Digest version: <$digestsubreleaseaddr$>%nowrap%
+ %^%- %wrap%No-mail version: <$nomailsubreleaseaddr$>%nowrap%
+%endif%
+
+%wrap%If you do not want to do any of this, either send a message to
+<$rejectaddr$> or simply ignore this message.
+
+The following moderators have received this mail:
+- %moderators%
+
+--=_$random0$$random1$_=
+Content-Type: message/rfc822
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="message.eml"
+
+%originalmail%
+--=_$random0$$random1$_=--
diff --git a/userconfig/listtext/notify b/userconfig/listtext/notify
new file mode 100644
index 0000000..6c2876c
--- /dev/null
+++ b/userconfig/listtext/notify
@@ -0,0 +1,41 @@
+%ifaction sub%Subject: Subscribed to $list$@$domain$: $subaddr$%endif%
+%ifaction unsub%Subject: Unsubscribed from $list$@$domain$: $subaddr$%endif%
+
+%text prologue%
+
+%ifaction sub%
+ %^%%wrap%The address <$subaddr$> has been subscribed to the
+ %iftype normal% normal %endif%
+ %iftype digest% digest %endif%
+ %iftype nomail% no-mail %endif%
+ version of the list
+ %ifreason request%
+ because a request to join was received.
+ %endif%
+ %ifreason confirm%
+ because a request to join was confirmed.
+ %endif%
+ %ifreason admin%
+ because an administrator commanded it.
+ %endif%
+ %ifreason permit%
+ because a gatekeeper permitted it.
+ %endif%
+%endif%
+
+%ifaction unsub%
+ %^%%wrap%The address <$subaddr$> has been unsubscribed from the list
+ %ifreason request%
+ because a request to unsubscribe was received.
+ %endif%
+ %ifreason confirm%
+ because a request to unsubscribe was confirmed.
+ %endif%
+ %ifreason admin%
+ because an administrator commanded it.
+ %endif%
+ %ifreason bouncing%
+ because it has been bouncing for too long.
+ %endif%
+%endif%
+
diff --git a/userconfig/listtext/probe b/userconfig/listtext/probe
new file mode 100644
index 0000000..397ebbe
--- /dev/null
+++ b/userconfig/listtext/probe
@@ -0,0 +1,11 @@
+Subject: Bouncing messages from $list$@$domain$
+
+%text prologue%
+
+Some messages to you could not be delivered. If you're seeing this
+message it means things are back to normal, and it's merely for your
+information.
+
+Here is the list of the bounced messages:
+- %bouncenumbers%
+
diff --git a/userconfig/listtext/prologue b/userconfig/listtext/prologue
new file mode 100644
index 0000000..0779947
--- /dev/null
+++ b/userconfig/listtext/prologue
@@ -0,0 +1 @@
+%wrap%This is an automated message from <$list$@$domain$>.
diff --git a/userconfig/listtext/subrelease b/userconfig/listtext/subrelease
new file mode 100644
index 0000000..7cdae70
--- /dev/null
+++ b/userconfig/listtext/subrelease
@@ -0,0 +1,20 @@
+%^%%wrap%However, this is an open list. If you wish, you can simultaneously
+subscribe and release your post by sending a message to
+<$listsubreleaseaddr$> which can usually be done simply by replying to this
+message. The subject and the body of the message can be anything.
+
+%ifncontrol nodigestsub%
+ %^%%wrap%Or you can simultaneously subscribe to the digest version of the
+ list and release your post by sending a message to
+ <$digestsubreleaseaddr$> and you will receive multiple posts in a single
+ message, at regular intervals, or when a lot of posts have accumulated.
+%endif%
+
+%ifncontrol nonomailsub%
+ %^%%wrap%Or you can simultaneously subscribe to the no-mail version of the
+ list and release your post by sending a message to
+ <$nomailsubreleaseaddr$> and you will not receive posts sent to the list.
+ This means you potentially will not see replies to your message, unless
+ you follow the list using a web archive or have another email address
+ subscribed to another version of the list.
+%endif%
diff --git a/userconfig/listtext/wait-post b/userconfig/listtext/wait-post
new file mode 100644
index 0000000..d30fb5d
--- /dev/null
+++ b/userconfig/listtext/wait-post
@@ -0,0 +1,38 @@
+Subject: Awaiting release to $list$@$domain$: $subject$
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="=_$random0$$random1$_="
+Content-Transfer-Encoding: 8bit
+
+--=_$random0$$random1$_=
+Content-Type: text/plain; charset=utf8
+Content-Transfer-Encoding: 8bit
+
+%text prologue%
+
+%wrap%The message from <$posteraddr$> with subject "$subject$" has been
+submitted to the list. However, the moderators are being asked to review it
+before releasing it to the list
+%ifreason moderated%
+ because this is a moderated list.
+%endif%
+%ifreason access%
+ because of an access rule.
+%endif%
+%ifreason modnonsubposts%
+ because you are not a subscriber.
+
+ %ifncontrol closedlist%%ifncontrol closedlistsub%
+ %^%%text subrelease%
+ %endif%%endif%
+%endif%
+
+(The message is below.)
+
+--=_$random0$$random1$_=
+Content-Type: message/rfc822
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="message.eml"
+
+%originalmail%
+--=_$random0$$random1$_=--
diff --git a/userconfig/listtext/wait-sub b/userconfig/listtext/wait-sub
new file mode 100644
index 0000000..84df549
--- /dev/null
+++ b/userconfig/listtext/wait-sub
@@ -0,0 +1,7 @@
+Subject: Awaiting permission to join $list$@$domain$
+
+%text prologue%
+
+%wrap%Your request to join the list has been received. However, the
+gatekeepers are being asked to review it before permitting you to join.
+