Disclaimer: This is an OLD, OLD blog post.

I shudder to think how much this PERL code looks like it was written by a C programmer...

If you still find it useful, great. If you'd like to mess with the code, great too. If you're really annoyed with fetchmail and it doesn't already have this feature built in, why not change the fetchmail code yourself and submit it to the maintainers? :)

Why don't I? Because I wanted to learn PERL...

I recently had some trouble with fetchmail and multidrop mailboxes. fetchmail handles mail well when a local address is found in the To: field of an email, but badly when it has to extract address information from the other headers of an email. In particular, people on the BCC list of an email (and who never appear in "official" mail headers) are likely to never receive emails addressed to them when handled via fetchmail with a multidrop mailbox.

I was also looking for an excuse to learn PERL (08/2004 update: wow, this page really is old...).

qmail has a nice solution where it inserts an Delivered-To: line into the mail headers. For virtual hosts, it prepends the domain name to the email account it was delivered to. My domain is uglybugger.org, so when the MX host for mail.uglybugger.org accepts mail for me, it dumps it into a single account. The headers it inserts look like this:

Return-Path: [email protected]
Delivered-To: uglybugger.org%[email protected]
Received: (cpmta 5066 invoked from network); 2 Apr 2001 21:59:52 -0700
Delivered-To: uglybugger.org%[email protected]
Received: (cpmta 5062 invoked from network); 2 Apr 2001 21:59:51 -0700

Read this from the bottom up. You'll see that the first Delivered-To: line reads uglybugger.org%[email protected] and the second Delivered-To: line reads uglybugger.org%[email protected].

fetchmail can be configured to read envelope addresses. These are the addresses (such as the Delivered-To: line) that mail servers include in mail headers to record which account the mail was delivered to. This is an important distinction from which address the mail was intended for. You can tell fetchmail to use these headers by specifying the following in your .fetchmailrc file:

envelope "Delivered-To:"
qvirtual "uglybugger.org%"

Obviously, please change the qvirtual line to read your domain :)

The problem here is that fetchmail will only read headers top-down, and it matches the first one it finds. This breaks the envelope/qvirtual delivery process completely as fetchmail is unable to ignore the first line it receives. So if the envelope/qvirtual settings do not solve your problem, read on.

What we need to do is specify an alternate delivery agent, so that we can handle our processing ourselves. You can do this by specifying an external Mail Delivery Agent for fetchmail. Use the mda keyword to point mail to a script that you can copy and paste from here:

-- /usr/sbin/fetchmail-inject -
#!/usr/bin/perl
# fetchmail-inject
# Andrew Harcourt, 1 May, 2001
#
# this script removes a nasty header that fetchmail can't handle
# from incoming mail and then passes the mail to sendmail to
# deliver locally

# write the mail to a temp file on disk
# as we do, parse it for the address
# lines
local($outputName, $fromAddress, $toAddress, $cmd);

$outputName = "/tmp/message.".$$;
open(OUTFILE,"> ".$outputName)
        || die "could not open $outputName!";

while (&ltSTDIN&gt) {
        if (/^Delivered-To: uglybugger\.org\%uglybugger\@uglybugger\.org/) {
                # just ignore this line - it's ugly
        }
        elsif (/^Delivered-To: uglybugger\.org\%(.*)\@uglybugger.org/) {
                $toAddress = $1."\@uglybugger.org";
        }
        elsif (/^From: .* /) {
                $fromAddress = $1;
        }
        elsif (/^From: /) {
                $fromAddress = $1;
        }
        elsif (/^From: .*/) {
                $fromAddress = $1
        }
        print OUTFILE $_;
}

close(OUTFILE);

# check that we have our addresses correct
if ($toAddress eq "") {
        $toAddress = "postmaster\@uglybugger.org";
}
if ($fromAddress eq "") {
        $fromAddress = "postmaster\@uglybugger.org";
}

# now call sendmail to deliver the message
$cmd = "/usr/sbin/sendmail -f $fromAddress $toAddress < $outputName";
system($cmd);
-

Good luck :)