rset(1) : Formulas

Mailing List

Running your own mail server is a challenge, and a mailing list even more so. There are some simple methods that can be employed using smptd(8) aliases.

OpenSMTPd

The dev alias delivers mail to three locations:

  1. rset-dev-YYYY.mbox for yearly archive
  2. local marc user used to build a HTML list archive
  3. Comma separated list of users
smtpd:
   ./rinstall hub/smtpd.conf /etc/mail/smtpd.conf
   ./rinstall members /etc/mail/members && rcctl restart smtpd
   ./rsub /etc/mail/aliases <<-CONF
   dev: /var/www/archive/rset-dev-$(date +%Y).mbox,marc,$(paste -sd ',' /etc/mail/members)
   CONF

The relay configuration needs to accomplish thee things:

  1. Add list headers
  2. Expand list alias to include all members
  3. Block non-members
# smtpd.conf
smtp max-message-size 1M

table aliases file:/etc/mail/aliases
table members file:/etc/mail/members

filter "add_list_headers" proc-exec "/usr/local/libexec/smtpd/add_list_headers.awk"

listen on any port 25

action "local" maildir "%{user.directory}/mail" alias <aliases>
action "outbound" relay helo scriptedconfiguration.org

match from any for rcpt-to "admin@scriptedconfiguration.org" action "local"
match !from mail-from <members> for domain "scriptedconfiguration.org" reject
match from any for domain "scriptedconfiguration.org" action "local"
match from local for local action "local"
match from local for any action "outbound"

Add List Headers

list_headers:
   mkdir -p /usr/local/libexec/smtpd
   ./rinstall hub/add_list_headers.awk /usr/local/libexec/smtpd

add_list_headers.awk was inspired by opensmtpd-filter-rewrite-from with some techiniques borrowed from d78.org.

#!/usr/bin/awk -f

BEGIN {
    FS = "|"
    OFS = FS
    _ = FS
    in_body[""] = 0
}

"config|ready" == $0 {
    print("register|filter|smtp-in|data-line")
    print("register|ready")
    fflush()
    next
}
"config" == $1 {
    next
}
"filter" == $1 {
    if (NF < 7) {
        print("invalid filter command: expected >6 fields!") > "/dev/stderr"
        exit 1
    }
    sid = $6
    token = $7
    line = substr($0, length($1$2$3$4$5$6$7) + 8)
}
"filter|smtp-in|data-line" == $1_$4_$5 {
    if (line == "") {  # end of headers
        in_body[sid] = 1
    }
    if (!in_body[sid] && match(toupper(line), /^FROM:[\t ]*/)) {
        print("filter-dataline", sid, token, "List-Id: <dev.scriptedconfiguration.org>")
        print("filter-dataline", sid, token, "List-Post: <mailto:dev@scriptedconfiguration.org>")
        print("filter-dataline", sid, token, "List-Owner: <mailto:ericshane@eradman.com> (Eric Radman)")
        print("filter-dataline", sid, token, "List-Unsubscribe: <mailto:admin@scriptedconfiguration.org?subject=unsubscribe>")
        print("filter-dataline", sid, token, "List-Help: <mailto:admin@scriptedconfiguration.org?subject=question>")
        print("filter-dataline", sid, token, "Precedence: list")
    }
    if (line == ".") {  # end of data
        delete in_body[sid]
    }
    print("filter-dataline", sid, token, line)
    fflush()
    next
}