NAME
Mailmunge::Filter::Compat - base class for Mailmunge filtering that includes some backward-compatibility for migrating MIMEDefang filter code.
SYNOPSIS
Mailmunge::Filter::Compat
is a subclass of Mailmunge::Filter
. As such, all methods documented in Mailmunge::Filter are also available here.
Mailmunge::Filter::Compat
implements a filter_message
function that in turn calls filter_begin
, filter
, filter_multipart
, filter_end
and filter_wrapup
methods. These methods operate similarly to their counterparts in MIMEDefang filtering code and make it easier to migrate MIMEDefang filters to Mailmunge.
The return values from filter_begin
, filter
, filter_multipart
, filter_end
and filter_wrapup
are normally ignored. However, if any of these functions returns a Mailmunge::Response object, then it is interpreted the same way as a Mailmunge::Response
object returned by filter_message
.
If you derive your filter from Mailmunge::Filter::Compat
, you must not override filter_message
. Instead, override filter_begin
, filter
, filter_multipart
, filter_end
and filter_wrapup
as required.
Any functions that are callable from filter_message
as well as Mailmunge::Context methods available to filter_message
are available in filter_begin
, filter
, flter_multipart
and filter_end
.
OPERATION
The body filtering functions are called as follows:
filter_begin
is called once.Recursing through the MIME::Entity object
$ctx->mime_entity
,filter_multipart
is called for each multipart/* sub-part andfilter
is called for each non-multipart sub-part.filter_end
is called once. This is the last point at which you are allowed to modify the message body.filter_wrapup
is called once. Infilter_wrapup
, modifications to the message body are not allowed, but you are allowed to modify top-level headers. Typically, this is where you would do DKIM-signing.
Note that if any method rejects a message by calling action_bounce
, action_discard
or action_tempfail
, then filtering is short-circuited and remaining callbacks are not called.
ABSTRACT
package MyFilter;
use base qw(Mailmunge::Filter::Compat);
sub filter_begin {
my ($self, $ctx) = @_;
# ... etc
}
THINGS YOU SHOULD NEVER DO IN YOUR FILTER CODE
Because Mailmunge filters use STDIN and STDOUT to communicate with the multiplexor, you should never read from STDIN or write to STDOUT from filter code you write. It's OK to write to STDERR, however; if mailmunge-multiplexor
was invoked with the -l
option, then anything your filter prints to STDERR will be logged in the mail log.
METHODS
filter_message($ctx)
Overrides filter_message
from the base Mailmunge::Filter
class. Do not tamper with or override this method.
The $ctx fields available are documented in Mailmunge::Filter's filter_message
documentation; these same fields are available in filter_begin
, filter_multipart
, filter
, filter_end
and filter_wrapup
.
filter_begin($ctx)
Called once at the beginning of filtering. See "filter_message" in Mailmunge::Filter for the list of $ctx fields available.
filter_multipart($ctx, $part, $fname, $extension, $type)
filter_multipart is called once for each multipart/*
part in the message. $part
is the sub-part being filtered and is a MIME::Entity. $fname
is the best-guess at the filename associated with the part (if any); it is taken from the Content-Type.name or Content-Disposition.filename MIME fields. $ext
is the filename extension including the leading dot associated with $fname
, and $type
is the MIME type of the part.
filter($ctx, $part, $fname, $extension, $type)
filter is called once for each non-multipart part in the message. The arguments are the same as filter_multipart
.
filter_end($ctx)
filter_end is called once at the end of filtering. This is the last place you can modify the message (which you can do with action_add_entity
or action_add_part
).
action_accept($ctx [, $warning])
This method may only be called in filter
or filter_multipart
. It causes the part to remain in the message. If no method that removes or modifies a part is called, then action_accept
is implicitly the default.
If $warning
is supplied, then we call action_accept_with_warning
to add a warning message in a new text/plain
part appended to the message.
action_drop($ctx [, $warning])
This method may only be called in filter
or filter_multipart
. It causes the part (and if multipart, all sub-parts) to be silently removed from the message. However, if $warning
is supplied, then we call action_drop_with_warning
instead.
action_drop_with_warning($ctx, $warning)
This method may only be called in filter
or filter_multipart
. It causes the part (and if multipart, all sub-parts) to be removed from the message. Additionally, a warning message is added in a new text/plain
part that is appended to the message.
action_accept_with_warning($ctx, $warning)
This method may only be called in filter
or filter_multipart
. It causes a warning message to be added in a new text/plain
part that is appended to the message.
action_replace_with_warning($ctx, $warning)
This method may only be called in filter
or filter_multipart
. It causes the part to be removed from the message and replaced with a new text/plain
part containing the $warning
message.
action_add_entity($ctx, $entity [, $offset])
Causes a new MIME::Entity
to be added to the message at offset $offset
, which is the zero-based index in the top-level message at which to add the entity. If $offset
is not supplied, the part is added to the end of the message.
action_add_part($ctx, $type, $encoding, $data, $fname, $disposition, $offset)
Creates a new MIME::Entity
whose MIME type is $type
, Content-Encoding is $encoding
, Content-Disposition is $disposition
, Content-Disposition.filename is $fname
and contents are $data
. Then calls action_add_entity with the new part and supplied $offset
. This is really just a convenience function that builds the MIME::Entity for you.
take_stab_at_filename($entity)
Returns Mailmunge's best-guess at the filename associated with MIME::Entity $entity
. Note that the decoded filename is returned, so any MIME encoding is parsed and decoded.
CONVERTING FROM MIMEDefang
Conversion of a filter from MIMEDefang to Mailmunge can range from very mechanical to quite complicated, depending on the filter. This section is a MIMEDefang-to-Mailmunge conversion guide.
The Filter
A MIMEDefang filter is a fragment of a Perl program, whereas a Mailmunge filter is a complete Perl program.
To convert a MIMEDefang filter to Mailmunge, your Mailmunge filter should start something like this:
package MyFilter;
use strict;
use warnings;
use base qw(Mailmunge::Filter::Compat);
my $filter = MyFilter->new();
$filter->run();
Callbacks
Mailmunge callbacks are similar to MIMEDefang, but have different arguments. The Following table shows the correspondence.
MIMEDefang Mailmunge
========== =========
sub filter_initialize { sub initialize {
# ... my ($self) = @_;
} # ...
}
sub filter_cleanup { sub cleanup {
# ... my ($self) = @_;
} # ...
}
sub filter_relay { sub filter_relay {
my ($ip, $name, $port, my ($self, $ctx) = @_;
$my_ip, $my_port, $qid) = @_; # ...
# ... }
}
sub filter_helo { sub filter_helo {
my ($ip, $name, $helo, $port, my ($self, $ctx) = @_;
$my_ip, $my_port, $qid) = @_; # ...
# ... }
}
sub filter_sender { sub filter_sender {
my ($sender, $ip, $name, $helo) = @_; my ($self, $ctx) = @_;
# ... # ...
} }
sub filter_recipient { sub filter_recipient {
my ($recip, $sender, $ip, $name, my ($self, $ctx) = @_;
$first_recip, $helo, # ...
$mailer, $host, $addr) = @_; }
# ...
}
sub filter_begin { sub filter_begin {
my ($entity) = @_; my ($self, $ctx) = @_;
# ... # ... Entity is $ctx->mime_entity
} }
sub filter { sub filter {
my ($entity, $fname, my ($self, $ctx, $entity, $fname,
$extension, $mime_type) = @_; $extension, $mime_type) = @_;
# ... # ...
} }
sub filter_multipart { sub filter_multipart {
my ($entity, $fname, my ($self, $ctx, $entity, $fname,
$extension, $mime_type) = @_; $extension, $mime_type) = @_;
# ... # ...
} }
sub filter_end { sub filter_end {
my ($entity) = @_; my ($self, $ctx) = @_;
# ... # ... Entity is $ctx->mime_entity
} }
sub filter_wrapup { sub filter_wrapup {
my ($entity) = @_; my ($self, $ctx) = @_;
# ... # ... Entity is $ctx->mime_entity
} }
sub filter_map { sub filter_map {
my ($map, $key) = @_; my ($self, $map, $key) = @_;
# ... # ...
} }
sub filter_tick { sub tick {
my ($tick_no) = @_; my ($self, $tick_no) = @_;
# ... # ...
} }
sub filter_validate { ... } No equivalent in Mailmunge
sub defang_warning { ... } No equivalent in Mailmunge
Return Values
Many MIMEDefang functions return an array of elements. In Mailmunge, they instead return a Mailmunge::Response object.
MIMEDefang Mailmunge
========== =========
return ('CONTINUE', 'ok'); return Mailmunge::Response->CONTINUE();
return ('TEMPFAIL', 'Message', 421, '4.1.1'); return Mailmunge::Response->TEMPFAIL(message => 'Message', code => 421, dsn => '4.1.1');
return ('TEMPFAIL', 'Message', 571, '5.2.1'); return Mailmunge::Response->REJECT(message => 'Message', code => 571, dsn => '5.2.1');
return ('DISCARD', 'Message'); return Mailmunge::Response->DISCARD(message => 'Message');
return ('ACCEPT_AND_NO_MORE_FILTERING', 'ok'); return Mailmunge::Response->ACCEPT_AND_NO_MORE_FILTERING();
Global Variables
MIMEDefang filters make use of a plethora of global variables. Mailmunge does not use any global variables. The correspondences for the most important variables are shown below.
MIMEDefang Mailmunge
========== =========
$MessageID $ctx->message_id
$RealRelayAddr $ctx->connecting_ip
$RealRelayHostname $ctx->connecting_name
$CWD $ctx->cwd
@ESMTPArgs @{$ctx->esmtp_args}
@SenderESMTPArgs @{$ctx->esmtp_args}
$Helo $ctx->helo
$RelayAddr $ctx->hostip
$RelayHostname $ctx->hostname
$MIMEDefangID $ctx->mailmunge_id
$MessageID $ctx->message_id
$QueueID $ctx->qid
%RecipientESMTPArgs %{$ctx->recipient_esmtp_args}
@Recipients @{$ctx->recipients}
$Sender $ctx->sender
$Subject $ctx->subject
$SubjectCount $ctx->subject_count
$SuspiciousCharsInHeaders $ctx->suspicious_chars_in_headers
$SuspiciousCharsInBody $ctx->suspicious_chars_in_body
$WasResent $ctx->was_resent
VARIOUS BITS OF MIMEDEFANG FUNCTIONALITY
Mailmunge moves a lot of functionality out of the core filter into modules. Here is a rough correspondence between MIMEDefang and Mailmunge functionality. Note that in some cases, we recommend external CPAN modules that already have the required functionality; duplicating that effort within Mailmunge would not be efficient.
- DNSBL lookups
-
Instead of the various
relay_is_blacklisted
functions, use Net::DNSBL::Client. - Streaming
-
Instead of
stream_by_recipient
orstream_by_domain
, use Mailmunge::Action::Stream. - Virus-Scanning
-
Instead of all the
*_contains_virus_*
functions, use File::VirusScan. - Bogus MX Host Checks
-
Instead of
md_get_bogus_mx_hosts
, use Mailmunge::Test::GetMX. - Boilerplate
-
Instead of
append_text_boilerplate
orappend_html_boilerplate
, use Mailmunge::Action::Boilerplate. - SpamAssassin
-
Instead of the various
spam_assassin_*
functions, use Mailmunge::Test::SpamAssassin. - Rspamd
-
Instead of
rspamd_check
, use Mailmunge::Test::Rspamd. - SMTP Call-forwards
-
Instead of
md_check_against_smtp_server
, use Mailmunge::Test::SMTPForward.
AUTHOR
Dianne Skoll <dianne@skollsoft.com>
LICENSE
This code is licensed under the terms of the GNU General Public License, version 2.
Copyright © 2024 Skoll Software Consulting