bapt/mlmmj-webview: Tiny web frontend for mlmmj - Codeberg.org
bapt
mlmmj-webview
Fork
You've already forked mlmmj-webview
Code
Issues
Pull requests
Projects
Releases
Wiki
Activity
Tiny web frontend for mlmmj
48
commits
branch
tags
332
KiB
74.9%
Tcl
13.7%
Shell
10.2%
Roff
0.7%
Makefile
0.4%
Find a file
2026-03-16 10:11:14 +01:00
assets
self host mailing lists
2021-04-06 11:57:03 +02:00
autosetup
infra: add boilerplate to configure and build mlmmj-webview
2021-03-23 10:53:28 +01:00
genindex
manpages: add manpages
2026-03-16 09:29:08 +01:00
mk
genindex: add a new binary
2025-07-22 09:08:09 +02:00
webview
CGI: use 303 instead of generating a temporary html and refresh
2026-03-16 10:11:02 +01:00
auto.def
Release 0.1.0
2026-03-16 10:11:14 +01:00
configure
infra: add boilerplate to configure and build mlmmj-webview
2021-03-23 10:53:28 +01:00
Makefile.in
genindex: add a new binary
2025-07-22 09:08:09 +02:00
README.md
README: make it up to date
2026-03-16 09:18:06 +01:00
README.md
MLMMJ Webview
It provides an index page for mlmmj mailing lists and handles subscribe/unsubscribe
requests via a web interface.
It is composed of two parts:
mlmmj-genindex
: a static HTML generator that produces the index and
per-list subscription pages from Mustache-like templates.
mlmmj-webview
: a CGI that handles the
/action
POST endpoint for
subscribe/unsubscribe requests. It validates the email format and verifies
that the email domain resolves via DNS before processing the request.
Building
./configure
make
mlmmj-genindex
Generates static HTML pages from templates and the mailing list directory
structure.
mlmmj-genindex -t /path/to/templates -L /path/to/lists -o /path/to/output
Options:
-t
: directory containing template files (
index.tpl
subscription.tpl
-L
: directory containing mlmmj mailing list directories
-o
: output directory for generated HTML files
The generator enters Capsicum sandbox mode after opening all required
directories.
Mailing list directory structure
Each mailing list is a subdirectory under the
-L
directory. The following
files are recognized inside each list directory:
desc
: required, contains a one-line description of the list
private
: if present, the list is hidden from the index
noarchive
: if present, the archive link is not shown
Templates
Templates use Mustache-like syntax with
{{variable}}
for HTML-escaped output
and
{{{variable}}}
for raw output.
The following data is available in templates:
index.tpl
{{#mls}}...{{/mls}}
: iterates over the mailing lists array
{{name}}
: list name (directory name)
{{desc}}
: list description (from the
desc
file)
{{#archives}}...{{/archives}}
: conditional block, rendered if archiving is enabled
subscription.tpl
{{name}}
: the mailing list name
Example index.tpl

html
head
><
title
Mailing Lists
title
>head
body
table
tbody
{{#mls}}
tr
td
><
href
"/subscription/{{name}}"
{{name}}
>td
td
{{desc}}
td
td
{{#archives}}
href
"/archives/{{name}}/"
archives
{{/archives}}
td
tr
{{/mls}}
tbody
table
body
html
Example subscription.tpl

html
head
><
title
Subscription for {{name}}
title
>head
body
h1
Subscription for {{name}}
h1
form
action
"/action"
method
"post"
label
for
"email"
Your email address:
label
input
type
"text"
name
"email"
id
"email"
placeholder
"user@example.net"
input
type
"hidden"
name
"ml"
value
"{{name}}"
input
type
"Submit"
name
"Subscribe"
value
"Subscribe"
input
type
"Submit"
name
"Unsubscribe"
value
"Unsubscribe"
form
body
html
mlmmj-webview (CGI)
Handles the
/action
POST endpoint. It expects the following form fields:
ml
: the mailing list name
email
: the email address (validated with kcgi's
kvalid_email
and DNS domain check)
or
Unsubscribe
: the action to perform
The CGI writes a temporary file in
/var/spool/mlmmj-webview/subscribe/
or
/var/spool/mlmmj-webview/unsubscribe/
containing the mailing list name and
email address. It always returns the same response regardless of success or
failure to prevent information leakage.
Configuration with nginx
Here is an example of using mlmmj-webview with nginx and fastcgi:
location /action {
gzip off;
include fastcgi_params;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SCRIPT_FILENAME /usr/local/bin/mlmmj-webview;
fastcgi_param PATH_INFO $uri;
fastcgi_param QUERY_STRING $args;
fastcgi_pass unix:/var/run/fcgiwrap/fcgiwrap.sock;
Processing subscriptions
The (un)subscription requests are written to temporary files in
/var/spool/mlmmj-webview/{subscribe,unsubscribe}
A script should run on a regular basis (e.g. via crontab) to process these
files. Here is an example:
#!/bin/sh
set
-eu
tmpdir
/usr/local/mlmmj/tmp/
mls
/usr/local/mlmmj/lists
tml
$(
mktemp -d
${
tmpdir
/subscribe.XXXXX
for
f in
$(
find /var/spool/mlmmj-webview/subscribe -type f
do
ml
$(
head -1
$f
email
$(
tail -1
$f
tr -d
'[:space:]'
testemail
$(
tail -1
$f
tr
'[:upper:]'
'[:lower:]'
tr -d
'[:space:]'
rm -f
$f
case
$testemail
in
*@domain.org
awk -v
mail
${
testemail
%@domain.org
'BEGIN { found=1 } $1 == mail { found=0; exit } END { exit found }'
/usr/local/mlmmj/virtual
&&
continue
;;
esac
if
-d
${
mls
${
ml
then
echo
$email
>>
${
tml
${
ml
fi
done
for
f in
$(
find
${
tml
-type f
do
for
m in
$(
sort -u
${
do
/usr/local/bin/mlmmj-sub -L
${
mls
${
##*/
-a
$m
-C
done
done
rm -rf
${
tml
tml
$(
mktemp -d
${
tmpdir
/unsubscribe.XXXXX
for
f in
$(
find /var/spool/mlmmj-webview/unsubscribe -type f
do
ml
$(
head -1
$f
email
$(
tail -1
$f
tr -d
'[:space:]'
testemail
$(
tail -1
$f
tr
'[:upper:]'
'[:lower:]'
tr -d
'[:space:]'
rm -f
$f
case
$testemail
in
*@domain.org
awk -v
mail
${
testemail
%@domain.org
'BEGIN { found=1 } $1 == mail { found=0; exit } END { exit found }'
/usr/local/mlmmj/virtual
&&
continue
;;
esac
if
-d
${
mls
${
ml
then
echo
$email
>>
${
tml
${
ml
fi
done
for
f in
$(
find
${
tml
-type f
do
for
m in
$(
sort -u
${
do
/usr/local/bin/mlmmj-unsub -L
${
mls
${
##*/
-a
$m
-C
done
done
rm -rf
${
tml
A crontab entry could be added in
/usr/local/etc/cron.d/mlmmj-webview
@120 mlmmj /usr/local/mlmmj/bin/mlsubscriptions.sh