Mailmunge::Filter::Compat − base class for Mailmunge filtering that includes some backward−compatibility for migrating MIMEDefang filter code.
"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".
The body filtering functions are called as follows:
|
1. |
"filter_begin" is called once. |
|||
|
2. |
Recursing through the MIME::Entity object "$ctx−>mime_entity", "filter_multipart" is called for each multipart/* sub−part and "filter" is called for each non−multipart sub−part. |
|||
|
3. |
"filter_end" is called once. This is the last point at which you are allowed to modify the message body. |
|||
|
4. |
"filter_wrapup" is called once. In "filter_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.
package
MyFilter;
use base qw(Mailmunge::Filter::Compat);
sub filter_begin {
my ($self, $ctx) = @_;
# ... etc
}
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.
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".
Called once at the beginning of filtering. See "filter_message" in Mailmunge::Filter for the list of $ctx fields available.
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 is called once for each non−multipart part in the message. The arguments are the same as "filter_multipart".
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").
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.
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.
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.
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.
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.
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.
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.
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.
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.
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();
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
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();
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
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 <https://metacpan.org/pod/Net::DNSBL::Client>.
Streaming
Instead of "stream_by_recipient" or "stream_by_domain", use Mailmunge::Action::Stream.
Virus−Scanning
Instead of all the "*_contains_virus_*" functions, use File::VirusScan <https://metacpan.org/pod/File::VirusScan>.
Bogus MX Host Checks
Instead of "md_get_bogus_mx_hosts", use Mailmunge::Test::GetMX.
Boilerplate
Instead of "append_text_boilerplate" or "append_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.
Dianne Skoll <[email protected]>
This code is licensed under the terms of the GNU General Public License, version 2.