I've had lots of success with mailman on <lists.zx2c4.com>. Here's my
config for it:
listen on eth0 hostname krantz.zx2c4.com
accept from any for domain lists.zx2c4.com virtual { "@lists.zx2c4.com" =>
mailman } deliver to mda "/var/lib/mailman/smtpd-deliver %{rcpt.user}"
Mailman is configured with the usual setup in /var/lib/mailman according to
their manual. smtpd-deliver is the clever part that makes the whole thing
really easy to work with:
krantz opensmtpd # cat /var/lib/mailman/smtpd-deliver.c
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
static const char *verbs[] = { "admin", "bounces", "confirm", "join",
"leave", "owner", "request", "subscribe", "unsubscribe" };
#define verbs_len (sizeof(verbs) / sizeof(const char *))
static const char *empty_verb = "post";
int main(int argc, char *argv[])
{
DIR *lists;
struct dirent *list;
int list_len, rcpt_len, i;
const char *verb;
if (argc != 2)
return 1;
rcpt_len = strlen(argv[1]);
lists = opendir("/var/lib/mailman/lists");
if (!lists) {
perror("opendir");
return 1;
}
while ((list = readdir(lists))) {
if (list->d_name[0] == '.')
continue;
list_len = strlen(list->d_name);
if (list_len > rcpt_len)
continue;
if (strncasecmp(argv[1], list->d_name, list_len))
continue;
verb = argv[1] + list_len;
if (!verb[0])
verb = empty_verb;
else if (verb[0] == '-') {
++verb;
for (i = 0; i < verbs_len; ++i) {
if (!strcmp(verbs[i], verb))
break;
}
if (i == verbs_len)
continue;
} else
continue;
execl("/var/lib/mailman/mail/mailman", "mailman", verb,
list->d_name, NULL);;
return 1;
}
closedir(lists);
return 1;
}
Or if you prefer, here's a version in python:
krantz opensmtpd # cat /var/lib/mailman/smtpd-deliver.py
#!/usr/bin/env python2
import sys
import os
lists = os.listdir("/var/lib/mailman/lists")
verbs = { "": "post", "admin": "admin", "bounces": "bounces", "confirm":
"confirm", "join": "join", "leave": "leave", "owner": "owner", "request":
"request", "subscribe": "subscribe", "unsubscribe": "unsubscribe" }
mail = sys.argv[1].lower()
for ml in lists:
if mail.startswith(ml):
verb = mail[len(ml) + 1:]
if verb not in verbs:
sys.exit(1)
verb = verbs[verb]
os.execl("/var/lib/mailman/mail/mailman",
"/var/lib/mailman/mail/mailman", verb, ml)
sys.exit(1)
sys.exit(1)
So far, mailman has been a joy to use and administer. Everything is super
super simple once it's rolling.