4

Frequently, I use mutt's alias functionality, to send a message to larger groups of people (legitimate, not spam!). Normally, I put the alias into the Bcc: field, to cancel out spam harvesters, and for privacy reasons (not all recipient know each other).

By accident, I sometimes put the alias into the To: or Cc: field, where it is expanded, revealing all addresses do all recipients. This is very bad, since once sent, the information is leaked, and there's no way back.

  • How can I make mutt refuse to send a mail, if there is more than, say, 1 entry in the To: or 5 entries in the Cc: field?

  • Improvement: How can I make mutt send the mail anyways, if I add a user-defined header, e.g., X-limit-cc: 50?

Setting up a separate mailing list service is not what I'm asking for, this would rather be a separate solution to the problem.

2 Answers 2

4

AFAIK, Mutt itself cannot do such checks, but there's a workaround! The idea is to redefine the $sendmail variable to use a wrapper that will check the mail before sending it. For instance, in your .muttrc file:

set sendmail=$HOME/scripts/sendmail-mutt

This script can either terminate with an error (non-zero exit status) or terminate successfully with something like:

exec /usr/sbin/sendmail -oem -oi "$@"

in a shell script, or:

exec qw(/usr/sbin/sendmail -oem -oi), @ARGV;

in Perl.

2
  • Ok, I have done so. It's ugly, but working fine. Might have some bugs and glitches, though... I'll try to post the script below.
    – stefan
    Sep 13, 2014 at 17:04
  • Handling these test in the MUA may be was the OP was asking for, unfortunately, it's very easy to subvert. I can talk to sendmail directly. I can telnet to port 25 and talk to the MUA through a pipe. If I have a no telnet to the MTA but I have a compiler, I can easily write a program that talks to port 25. All this points to the MTA as being the proper place for filtering.
    – msw
    Sep 13, 2014 at 17:51
1
#!/bin/bash
set -u -e -C;
shopt -s nullglob;

# This script can be used as a `sendmail` replacement in `mutt`.  It
# performs several checks on the mail before sending ist, and it's
# easy to modify these checks, or to add more.  Currently, these are:
#
#     attachment — fail on non-multipart message if text contains
#         keywords referring to an attachment (English, German).
#
#     size — refuse to send excessively large mails.
#
#     fixme — refuse to send mail containing the word `FIXME`.
#
#     longline — refuse to send mails wit long lines, unless paths
#         (URLs) containing two slashes, or quoted.
#
#     limit-to, limit-cc — limit number of recipients in the according
#         header.  Better use Bcc field.
#
# Each of the tests can be disabled by puttig its name in an
# `X-checks` header field.  E.g., `X-checks: longline limit-cc` allows
# to send mails with long lines to unlimited number of recipients in
# the Cc field.
#
# To use this script, install it where it can be found, and configure
# mutt accordingly.  I have stored this script as `~/.mutt/sendmail`,
# and my `~/.mutt/muttrc` contains the line
#
#     set sendmail = "~/.mutt/sendmail"
#
# When sending a mail, it is stored as `mutt-sendmail.*` in a
# temporary location, from where it is accessible for checks.  On
# success, the mail is sent, otherwise this script fails.  The
# temporary file is deleted.



# ---- Configuration of this script ----------------------------------

# Mail is sent using `${sendmail} "$@" <"${wholemail}"`, see last line
# of the script.

sendmail=/usr/bin/msmtp;

# You may explicitly skip some tests by putting their name in a header
# field of your mail.  On checking, these names are printed to stderr.
# The name of the header is:

myhdr='X-checks';



# ---- The script ----------------------------------------------------

# print message to stderr, maybe fail
function err { echo "$@" >&2; exit 1; }
function warn { echo "$@" >&2; }

# store the mail in a temporary file
wholemail="$(mktemp -t mutt-sendmail.XXXXXXXXXX)" &&
    trap "rm -f '${wholemail}'" EXIT &&
    cat >| "${wholemail}" ||
    err 'cannot create temporary file';

# get the header, with indented lines joined
header="$(sed -rn ':a;/^$/q;$!N;s/\n\s+/ /;ta;P;D' "${wholemail}")";

# get values of one particular header, each occurence on one line
function getHeader { sed -rn "s/^${1}:\s+//p" <<< "$header"; }

# use this function to read body of message
function getBody { sed '1,/^$/d' "${wholemail}"; }

# get list of checks to be ignored for this particular mail
checks="$(getHeader "${myhdr}")";
function requested {
    if grep -q "${1}" <<< "${checks}"; then
        warn "${myhdr}: ${1} — skipping";
        return 1;
    else
        warn "no ${myhdr}: ${1}";
        return 0;
    fi;
}


# --- do all tests here ----------------------------------------------
#
# `$wholemail` contains the file name of the whole mail, as passed to
#   sendmail.
# `getBody` writes the body of the mail to stdout.
# `getHeader foo` writes one line for each value of a `foo:` header.
# `requested bar` fails if a `$myhdr` header contains `bar`.

# make a keyword search on the whole email, if it is not multipart.
requested 'attachment' &&
getHeader "Content-Type" | grep -vq multipart &&
grep -v '^>' "$wholemail" | grep -Eni 'attach|anhang|hängt|unten' &&
err "no multipart message, but hints to attachment.";

# reject emails greater than $maxsize bytes
maxsize='32768';
requested 'size' &&
test "$(stat -c%s "${wholemail}")" -gt "${maxsize}" &&
err "bigger then ${maxsize} bytes.";

# reject emails containing FIXME
requested 'fixme' &&
getBody | grep -n FIXME &&
err "FIXME keyword found.";

# reject emails with long lines
maxline=78;
requested 'longline' &&
test "$(getBody | grep -Ev '^>|/.*/' | wc -L)" -gt "$maxline" &&
err "lines longer than $maxline chars found.";

# count number of recipients
requested 'limit-to' &&
test "$(getHeader "To" | grep -o ',' | wc -l)" -gt 2 &&
err 'Too many recipients in the To field.';

requested 'limit-cc' &&
test "$(getHeader "Cc" | grep -o ',' | wc -l)" -gt 5 &&
err 'Too many recipients in the Cc field.';


# --- now send the mail ----------------------------------------------
#err 'debugging mode: not sending';

${sendmail} "$@" <"${wholemail}";

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .