summaryrefslogtreecommitdiffstats
path: root/roles/lists/files/usr/local
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2014-01-14 08:06:54 +0100
committerGuilhem Moulin <guilhem@fripost.org>2015-06-07 02:51:38 +0200
commitab83789bd70d294623e62e0b366b6b649cb5b0af (patch)
tree6d000972a52e04a0a9828843cb252a5dbe3828e9 /roles/lists/files/usr/local
parent97352d1452917fdcd81da0e209aed6e735c00961 (diff)
Mailing lists (using mlmmj).
Right now the list server cannot be hosted with a MX, due to bug 51: http://mlmmj.org/bugs/bug.php?id=51 Web archive can be compiled with MHonArc, but the web server configuration is not there yet.
Diffstat (limited to 'roles/lists/files/usr/local')
-rwxr-xr-xroles/lists/files/usr/local/bin/mhonarc-scan.sh114
-rwxr-xr-xroles/lists/files/usr/local/bin/mlmmj-newlist.sh139
2 files changed, 253 insertions, 0 deletions
diff --git a/roles/lists/files/usr/local/bin/mhonarc-scan.sh b/roles/lists/files/usr/local/bin/mhonarc-scan.sh
new file mode 100755
index 0000000..d0ea2af
--- /dev/null
+++ b/roles/lists/files/usr/local/bin/mhonarc-scan.sh
@@ -0,0 +1,114 @@
+#!/bin/sh
+
+# Convert a list archive into HTML.
+#
+# Copyright © 2014 Guilhem Moulin <guilhem@fripost.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -ue
+
+fail () {
+ echo Error: "$@" >&2
+ exit 1
+}
+
+[ $# -eq 1 ] || { echo "Usage: $0 listdir"; exit; }
+listdir="${1%/}"
+[ -d "$listdir" ] || fail "No such directory: $listdir"
+
+localpart="${listdir##*/}"
+domainpart="${listdir%/$localpart}"
+domainpart="${domainpart##*/}"
+
+# Determine the rotation period
+rotation=
+[ -s "$listdir/control/archiverotate" ] && read rotation <"$listdir/control/archiverotate"
+
+# Subdir format (/!\ shouldn't be empty, and shouldn't contain spaces!),
+# and archive date format.
+case "${rotation:-month}" in
+ '') subdirf='/' listpage='./'; archivef=;;
+ year) subdirf="%Y"; listpage='../'; archivef="for %Y";;
+ month) subdirf="%Y/%m"; listpage='../../'; archivef="for %B %Y";;
+ day) subdirf="%Y/%m/%d"; listpage='../../../'; archivef="for %a, %d %b %Y";;
+ *) fail "$rotation: unknown rotation period"
+esac
+
+# Look up for the send date in an email. Fall back to the creation date
+# if not found.
+printDate () {
+ local filename date
+ while read filename; do
+ if ! [ "$rotation" ]; then
+ # don't bother looking for a date
+ date=0
+ else
+ # stop as soon as the header is over
+ date=$(sed -nr '/^Date:\s*(\S.*)$/I {s//\1/p;q}; /^([^[:cntrl:][:space:]]+:|\s)/ !q' \
+ "$filename")
+ [ "$date" ] || date=@$(stat -c '%Y' "$filename")
+ fi
+ echo $(date -d "$date" +"%s $subdirf") "$filename"
+ done
+}
+
+# Process a (single) subdirectory
+process () {
+ local list="$1" subdir="$2" date="$3"
+ [ -s "$list" ] || return 0
+
+ [ -d "$listdir/webarchive/$subdir" ] || mkdir -p "$listdir/webarchive/$subdir"
+ # TODO: add a line to the index file
+ xargs -a"$list" mhonarc -definevar ListName="'$localpart'" \
+ -definevar ListPage="'${listpage}index.html'" \
+ -definevar DirDate="'$date'" \
+ -rcfile /etc/mhonarc.rc \
+ -add \
+ -quiet \
+ -outdir "$listdir/webarchive/$subdir" \
+ || exit 1
+ # empty the list
+ echo -n >"$list"
+}
+
+# Process all found emails
+processM () {
+ local cursubdir= date=
+ local timestamp subdir filename
+
+ while read timestamp subdir filename; do
+ if [ "$cursubdir" != "$subdir" ]; then
+ process "$list" "$cursubdir" "$date"
+ cursubdir="$subdir"
+ date="$(date -d "@$timestamp" +"$archivef")"
+ fi
+ echo "$filename" >>"$list"
+ done
+ process "$list" "$cursubdir" "$date"
+}
+
+# The span of emails we'll touch during the current instance
+now=$(date +'%s')
+list=$(mktemp) || exit 1
+trap 'rm -f "$list"' EXIT
+
+from=0
+if [ -s "$listdir/.webarchive.date" ]; then
+ read from <"$listdir/.webarchive.date"
+ from=$(( $from - 30 )) # remove 30s to fight race conditions
+fi
+
+find "$listdir/archive/" -type f -a -newermt @"$from" | printDate | sort -n -k1,1 | processM
+echo "$now" > "$listdir/.webarchive.date"
diff --git a/roles/lists/files/usr/local/bin/mlmmj-newlist.sh b/roles/lists/files/usr/local/bin/mlmmj-newlist.sh
new file mode 100755
index 0000000..a1fcc70
--- /dev/null
+++ b/roles/lists/files/usr/local/bin/mlmmj-newlist.sh
@@ -0,0 +1,139 @@
+#!/bin/sh
+
+# Add new lists (with a common options) to be managed by mlmmj.
+# Incoming e-mails need to be handed over (piped) to mlmmj-receive(1) by
+# the MTA, see http://mlmmj.org/docs/readme-postfix/ to configure the
+# MTA.
+#
+# Copyright © 2014 Guilhem Moulin <guilhem@fripost.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -ue
+
+fail () {
+ echo Error: "$@" >&2
+ exit 1
+}
+
+spool=/var/spool/mlmmj # mlmmj's how private directory
+lib=/var/lib/mlmmj # shared by mlmmj and (partially) by the web server
+webhost=https://lists.fripost.org
+umask 0022
+
+[ $# -ge 2 -a $# -le 3 ] || { echo "Usage: $0 list owner [language]"; exit; }
+list="$1"
+owner="$2"
+lang="${3:-en}"
+
+localpart="${list%@*}"
+domainpart="${list##*@}"
+[ "$localpart" = "$list" ] && [ "$domainpart" = "$list" ] \
+&& fail "$list is not fully-qualified"
+
+[ -d "$spool/$domainpart/$localpart" -o -d "$lib/$domainpart/$localpart" ] \
+&& fail "$list exists"
+
+ls -1 /usr/share/mlmmj/text.skel | grep -qFx "$lang" \
+|| fail "Available languages: $(echo $(ls /usr/share/mlmmj/text.skel))"
+
+case "$webhost" in
+ *"/$domainpart") listurl="$webhost/$localpart/";;
+ *) listurl="$webhost/$domainpart/$localpart/";;
+esac
+
+mkdir -p -m0700 "$spool/$domainpart/$localpart"
+mkdir -p -m0750 "$lib/$domainpart/$localpart"
+
+
+# The web server has read-only access to subscribers.
+for dir in subscribers.d digesters.d nomailsubs.d; do
+ mkdir -m0750 "$lib/$domainpart/$localpart/$dir"
+ ln -s "$lib/$domainpart/$localpart/$dir" \
+ "$spool/$domainpart/$localpart/$dir"
+done
+
+# The web server can update the list configuration.
+mkdir -m2770 "$lib/$domainpart/$localpart/control"
+ln -s "$lib/$domainpart/$localpart/control" \
+ "$spool/$domainpart/$localpart/control"
+
+# Internal directories.
+for dir in incoming queue queue/discarded \
+ subconf unsubconf bounce moderation requeue; do
+ mkdir -m0700 "$spool/$domainpart/$localpart/$dir"
+done
+
+# Link to templates.
+ln -s /usr/share/mlmmj/text.skel/$lang "$spool/$domainpart/$localpart/text"
+
+# Archives are private, but web archives are public.
+mkdir -m0700 "$lib/$domainpart/$localpart/archive"
+mkdir -m0750 "$lib/$domainpart/$localpart/webarchive"
+ln -s "$lib/$domainpart/$localpart/archive" \
+ "$spool/$domainpart/$localpart/archive"
+ln -s "$lib/$domainpart/$localpart/webarchive" \
+ "$spool/$domainpart/$localpart/webarchive"
+
+# Default configuration, non-writable from the web.
+echo "$list" > "$lib/$domainpart/$localpart/control/listaddress"
+echo "$owner" > "$lib/$domainpart/$localpart/control/owner"
+# XXX: these tunables are ignored, see http://mlmmj.org/bugs/bug.php?id=51
+#echo 127.0.0.1 > "$lib/$domainpart/$localpart/control/relayhost"
+#echo 16132 > "$lib/$domainpart/$localpart/control/smtpport"
+echo month > "$lib/$domainpart/$localpart/control/archiverotate"
+
+# RFC 2369
+cat > "$lib/$domainpart/$localpart/control/customheaders" <<- EOF
+ Errors-to: $localpart+owner@$domainpart
+ Precedence: list
+ List-Id: <$localpart.$domainpart>
+ List-URL: <$listurl>
+ List-Post: <mailto:$list>
+ List-Help: <mailto:$localpart+help@$domainpart>,
+ <$listurl/>
+ List-Subscribe: <$localpart+subscribe@$domainpart>,
+ <$listurl/>
+ List-Unsubscribe: <mailto:$localpart+unsubscribe@$domainpart>,
+ <$listurl/>
+ List-Owner: <mailto:$localpart+owner@$domainpart>
+ List-Archives: <$listurl/archives/>
+ Reply-To: $list
+ X-MailingList: $list
+ X-Loop: $list
+EOF
+cat > "$lib/$domainpart/$localpart/control/delheaders" <<- EOF
+ Return-Receipt-To:
+ Disposition-Notification-To:
+ X-Confirm-Reading-To:
+ X-Pmrqc:
+EOF
+
+# Some useful default, that the user is free to change via the web
+# interface.
+cat > "$lib/$domainpart/$localpart/control/footer" <<- EOF
+ _______________________________________________
+ $localpart mailing list
+ $localpart@$domainpart
+ $listurl
+EOF
+echo "[$localpart]" > "$lib/$domainpart/$localpart/control/prefix"
+touch "$lib/$domainpart/$localpart/control/subonlypost" \
+ "$lib/$domainpart/$localpart/control/subonlyget"
+
+for control in customheaders footer prefix subonlypost subonlyget; do
+ chmod 0664 "$lib/$domainpart/$localpart/control/$control"
+done
+
+# TODO: welcome mail