From ab83789bd70d294623e62e0b366b6b649cb5b0af Mon Sep 17 00:00:00 2001
From: Guilhem Moulin <guilhem@fripost.org>
Date: Tue, 14 Jan 2014 08:06:54 +0100
Subject: 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.
---
 roles/lists/files/usr/local/bin/mhonarc-scan.sh  | 114 +++++++++++++++++++
 roles/lists/files/usr/local/bin/mlmmj-newlist.sh | 139 +++++++++++++++++++++++
 2 files changed, 253 insertions(+)
 create mode 100755 roles/lists/files/usr/local/bin/mhonarc-scan.sh
 create mode 100755 roles/lists/files/usr/local/bin/mlmmj-newlist.sh

(limited to 'roles/lists/files/usr')

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
-- 
cgit v1.2.3