undo r700 (skip plugin)
git-svn-id: https://svn.perl.org/qpsmtpd/branches/0.3x@701 958fd67b-6ff1-0310-b445-bb7760255be9
This commit is contained in:
parent
39a9271213
commit
d54a960f9a
4
Changes
4
Changes
@ -1,9 +1,5 @@
|
|||||||
0.33 (to be)
|
0.33 (to be)
|
||||||
|
|
||||||
New "skip plugin API" + example plugin skip_plugins, see perldoc
|
|
||||||
Qpsmtpd::Plugins for more info. This can be used to disable (and re-
|
|
||||||
enable) loaded plugins for the current connection (Hanno Hecker)
|
|
||||||
|
|
||||||
Support "module" plugins ("My::Plugin" in the config/plugins file)
|
Support "module" plugins ("My::Plugin" in the config/plugins file)
|
||||||
|
|
||||||
Make the badmailfrom plugin support (optional) rejection messages after the
|
Make the badmailfrom plugin support (optional) rejection messages after the
|
||||||
|
@ -12,9 +12,6 @@
|
|||||||
# from one IP!
|
# from one IP!
|
||||||
hosts_allow
|
hosts_allow
|
||||||
|
|
||||||
# skip selected plugins for some hosts:
|
|
||||||
skip_plugins
|
|
||||||
|
|
||||||
# enable to accept MAIL FROM:/RCPT TO: addresses without surrounding <>
|
# enable to accept MAIL FROM:/RCPT TO: addresses without surrounding <>
|
||||||
dont_require_anglebrackets
|
dont_require_anglebrackets
|
||||||
|
|
||||||
|
@ -367,11 +367,6 @@ sub run_continuation {
|
|||||||
$@ and warn("FATAL LOGGING PLUGIN ERROR: ", $@) and next;
|
$@ and warn("FATAL LOGGING PLUGIN ERROR: ", $@) and next;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
my $skip = $self->connection->notes('_skip_plugins');
|
|
||||||
if (exists $skip->{$code->{name}} and $skip->{$code->{name}}) {
|
|
||||||
$self->log(LOGDEBUG, "skipping plugin ".$code->{name});
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
$self->varlog(LOGDEBUG, $hook, $code->{name});
|
$self->varlog(LOGDEBUG, $hook, $code->{name});
|
||||||
eval { (@r) = $code->{code}->($self, $self->transaction, @$args); };
|
eval { (@r) = $code->{code}->($self, $self->transaction, @$args); };
|
||||||
$@ and $self->log(LOGCRIT, "FATAL PLUGIN ERROR: ", $@) and next;
|
$@ and $self->log(LOGCRIT, "FATAL PLUGIN ERROR: ", $@) and next;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package Qpsmtpd::Plugin;
|
package Qpsmtpd::Plugin;
|
||||||
use Qpsmtpd::Constants;
|
use Qpsmtpd::Constants;
|
||||||
use strict;
|
use strict;
|
||||||
use vars qw(%symbols);
|
|
||||||
|
|
||||||
# more or less in the order they will fire
|
# more or less in the order they will fire
|
||||||
our @hooks = qw(
|
our @hooks = qw(
|
||||||
@ -117,8 +116,6 @@ sub isa_plugin {
|
|||||||
|
|
||||||
# don't reload plugins if they are already loaded
|
# don't reload plugins if they are already loaded
|
||||||
return if defined &{"${newPackage}::plugin_name"};
|
return if defined &{"${newPackage}::plugin_name"};
|
||||||
### someone test this please:
|
|
||||||
# return if $self->plugin_is_loaded($newPackage);
|
|
||||||
|
|
||||||
$self->compile($self->plugin_name . "_isa_$cleanParent",
|
$self->compile($self->plugin_name . "_isa_$cleanParent",
|
||||||
$newPackage,
|
$newPackage,
|
||||||
@ -186,211 +183,5 @@ sub _register_standard_hooks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
=head1 SKIP PLUGINS API
|
|
||||||
|
|
||||||
These functions allow to disable and re-enable loaded plugins. Loading
|
|
||||||
plugins after the initial loading phase is not possible. The earliest
|
|
||||||
place to disable a plugin is in C<hook_connect()>.
|
|
||||||
|
|
||||||
If you want to run a plugin just for some clients, load it like a usual
|
|
||||||
plugin and either hook it to the C<hook_connect()> (or any later hook)
|
|
||||||
and disable it there, use the C<skip_plugins> plugin or write your own
|
|
||||||
disabling plugin.
|
|
||||||
|
|
||||||
These modifications of disabling/re-enabling a plugin are valid for the
|
|
||||||
full connection, not transaction! For transaction based disabling of plugins,
|
|
||||||
use the C<reset_transaction> hook to reset the list of disabled plugins.
|
|
||||||
|
|
||||||
A small warning: the C<reset_transaction> hook is called at least three
|
|
||||||
times: after the client sent the C<(HE|EH)LO>, every time the client
|
|
||||||
issues a C<MAIL FROM:> and after the mail was queued (or rejected by a
|
|
||||||
C<data_post> hook). Don't forget it is also called after C<RSET> and
|
|
||||||
connection closing (e.g. after C<QUIT>).
|
|
||||||
|
|
||||||
=over 7
|
|
||||||
|
|
||||||
=item plugin_is_loaded( $plugin )
|
|
||||||
|
|
||||||
Returns true, if the given (escaped) plugin name is a loaded plugin
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
sub plugin_is_loaded {
|
|
||||||
my ($self, $plugin) = @_;
|
|
||||||
$plugin =~ s/^Qpsmtpd::Plugin:://; # for _loaded();
|
|
||||||
# each plugin has a sub called "plugin_name()", see compile() above...
|
|
||||||
# ... this restricts qpsmtpd a bit: No module named
|
|
||||||
# Qpsmtpd::Plugin(|::Something) must have a sub "plugin_name()", or
|
|
||||||
# it will be returned as a loaded plugin...
|
|
||||||
return defined &{"Qpsmtpd::Plugin::${plugin}::plugin_name"};
|
|
||||||
}
|
|
||||||
|
|
||||||
=item plugin_status( $plugin )
|
|
||||||
|
|
||||||
Shows the status of the given plugin. It returns undef if no plugin name
|
|
||||||
given or the plugin is not loaded, "0" if plugin is loaded, but disabled
|
|
||||||
and "1" if the plugin is loaded and active. The plugin name must be escaped
|
|
||||||
by B<escape_plugin()>.
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
sub plugin_status {
|
|
||||||
my ($self, $plugin) = @_;
|
|
||||||
return undef unless $plugin;
|
|
||||||
return undef unless $self->plugin_is_loaded($plugin);
|
|
||||||
my $skip = $self->qp->connection->notes('_skip_plugins') || {};
|
|
||||||
return 0 if (exists $skip->{$plugin} and $skip->{$plugin});
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
=item loaded_plugins( )
|
|
||||||
|
|
||||||
This returns a hash. Keys are (escaped, see below) plugin names of loaded
|
|
||||||
plugins. The value tells you if the plugin is currently active (1) or
|
|
||||||
disabled (0).
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
sub loaded_plugins {
|
|
||||||
my $self = shift;
|
|
||||||
# all plugins are in their own class "below" Qpsmtpd::Plugin,
|
|
||||||
# so we start searching the symbol table at this point
|
|
||||||
my %plugins = map {
|
|
||||||
s/^Qpsmtpd::Plugin:://;
|
|
||||||
($_, 1)
|
|
||||||
} $self->_loaded("Qpsmtpd::Plugin");
|
|
||||||
foreach ($self->disabled_plugins) {
|
|
||||||
$plugins{$_} = 0;
|
|
||||||
}
|
|
||||||
return %plugins;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _loaded {
|
|
||||||
my $self = shift;
|
|
||||||
my $base = shift;
|
|
||||||
my @loaded = ();
|
|
||||||
my (@sub, $symbol);
|
|
||||||
# let's see what's in this name space
|
|
||||||
no strict 'refs';
|
|
||||||
local (*symbols) = *{"${base}::"};
|
|
||||||
use strict 'refs';
|
|
||||||
foreach my $name (values %symbols) {
|
|
||||||
# $name is read only while walking the stash
|
|
||||||
|
|
||||||
# not a class name? ok, next
|
|
||||||
($symbol = $name) =~ s/^\*(.*)::$/$1/ || next;
|
|
||||||
next if $symbol eq "Qpsmtpd::Plugin";
|
|
||||||
|
|
||||||
# in qpsmtpd we have no way of loading a plugin with the same
|
|
||||||
# name as a sub directory inside the ./plugins dir, so we can safely
|
|
||||||
# use either the list of sub classes or the class itself we're
|
|
||||||
# looking at (unlike perl, e.g. Qpsmtpd.pm <-> Qpsmtpd/Plugin.pm).
|
|
||||||
@sub = $self->_loaded($symbol);
|
|
||||||
|
|
||||||
if (@sub) {
|
|
||||||
push @loaded, @sub;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# is this really a plugin?
|
|
||||||
next unless $self->plugin_is_loaded($symbol);
|
|
||||||
push @loaded, $symbol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return @loaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
=item escape_plugin( $plugin_name )
|
|
||||||
|
|
||||||
Turns a plugin filename into the way it is used inside qpsmtpd. This needs to
|
|
||||||
be done before you B<plugin_disable()> or B<plugin_enable()> a plugin. To
|
|
||||||
see if a plugin is loaded, use something like
|
|
||||||
|
|
||||||
my %loaded = $self->loaded_plugins;
|
|
||||||
my $wanted = $self->escape_plugin("virus/clamav");
|
|
||||||
if (exists $loaded{$wanted}) {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
... or shorter:
|
|
||||||
|
|
||||||
if ($self->plugin_is_loaded($self->escape_plugin("virus/clamav"))) {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
sub escape_plugin {
|
|
||||||
my $self = shift;
|
|
||||||
my $plugin_name = shift;
|
|
||||||
# "stolen" from Qpsmtpd.pm
|
|
||||||
# Escape everything into valid perl identifiers
|
|
||||||
$plugin_name =~ s/([^A-Za-z0-9_\/])/sprintf("_%2x",unpack("C",$1))/eg;
|
|
||||||
|
|
||||||
# second pass cares for slashes and words starting with a digit
|
|
||||||
$plugin_name =~ s{
|
|
||||||
(/+) # directory
|
|
||||||
(\d?) # package's first character
|
|
||||||
}[
|
|
||||||
"::" . (length $2 ? sprintf("_%2x",unpack("C",$2)) : "")
|
|
||||||
]egx;
|
|
||||||
return $plugin_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
=item disabled_plugins( )
|
|
||||||
|
|
||||||
This returns a list of all plugins which are disabled for the current
|
|
||||||
connection.
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
sub disabled_plugins {
|
|
||||||
my $self = shift;
|
|
||||||
my @skipped = ();
|
|
||||||
my $skip = $self->qp->connection->notes('_skip_plugins') || {};
|
|
||||||
foreach my $s (keys %{$skip}) {
|
|
||||||
push @skipped, $s if $skip->{$s};
|
|
||||||
}
|
|
||||||
return @skipped;
|
|
||||||
}
|
|
||||||
|
|
||||||
=item plugin_disable( $plugin )
|
|
||||||
|
|
||||||
B<plugin_disable()> disables a (loaded) plugin, it requires the plugin name
|
|
||||||
to be escaped by B<escape_plugin()>. It returns true, if the given plugin
|
|
||||||
name is a loaded plugin (and disables it of course).
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
sub plugin_disable {
|
|
||||||
my ($self, $plugin) = @_;
|
|
||||||
# do a basic check if the supplied plugin name is really a plugin
|
|
||||||
return 0 unless $self->plugin_is_loaded($plugin);
|
|
||||||
|
|
||||||
my $skip = $self->qp->connection->notes('_skip_plugins') || {};
|
|
||||||
$skip->{$plugin} = 1;
|
|
||||||
$self->qp->connection->notes('_skip_plugins', $skip);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
=item plugin_enable( $plugin )
|
|
||||||
|
|
||||||
B<plugin_enable()> re-enables a (loaded) plugin, it requires the plugin name
|
|
||||||
to be escaped by B<escape_plugin()>. It returns "0", if the given plugin
|
|
||||||
name is not a loaded plugin. Else it returns "1" after enabling.
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
sub plugin_enable {
|
|
||||||
my ($self, $plugin) = @_;
|
|
||||||
return 0 unless $self->plugin_is_loaded($plugin);
|
|
||||||
|
|
||||||
my $skip = $self->qp->connection->notes('_skip_plugins') || {};
|
|
||||||
$skip->{$plugin} = 0;
|
|
||||||
$self->qp->connection->notes('_skip_plugins', $skip);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
=back
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
|
|
||||||
=head1 NAME
|
|
||||||
|
|
||||||
skip_plugins - don't run selected plugins for some hosts
|
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
|
||||||
|
|
||||||
The B<skip_plugins> plugin allows you to skip selected plugins for some
|
|
||||||
clients. This is similar to some whitelist plugins, without the need to
|
|
||||||
modify any plugin.
|
|
||||||
|
|
||||||
This plugin should be run before any other plugins hooking to the
|
|
||||||
I<hook_connect>. The config allows to run all plugins for one host in a
|
|
||||||
subnet and skip some for all other hosts in this network.
|
|
||||||
|
|
||||||
=head1 CONFIG
|
|
||||||
|
|
||||||
The config file I<skip_plugins> contains lines with two or three items per
|
|
||||||
line. The first field is a network/mask pair (or just a single IP address).
|
|
||||||
An action is set in the second field: currently B<continue> or B<skip> are
|
|
||||||
valid actions.
|
|
||||||
|
|
||||||
If a host matches a B<continue> line, the parsing is stopped and all
|
|
||||||
plugins are run for this host. A B<skip> action tells qpsmtpd to skip
|
|
||||||
the plugins listed in the third field for this connection.
|
|
||||||
|
|
||||||
The plugin list in the third field must be separated by "," without any spaces.
|
|
||||||
|
|
||||||
=head1 EXAMPLE
|
|
||||||
|
|
||||||
10.7.7.2 continue
|
|
||||||
10.7.7.0/24 skip spamassassin,check_earlytalker
|
|
||||||
|
|
||||||
To disable a plugin for all clients except for one subnet:
|
|
||||||
|
|
||||||
10.1.0.0/16 continue
|
|
||||||
0.0.0.0/0 skip virus/clamdscan
|
|
||||||
|
|
||||||
=head1 NOTES
|
|
||||||
|
|
||||||
See perldoc Qpsmtpd::Plugin for more about disabling / re-enabling plugins
|
|
||||||
for the current connection.
|
|
||||||
|
|
||||||
=head1 BUGS
|
|
||||||
|
|
||||||
This plugin does not have IPv6 support.
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
use Socket;
|
|
||||||
|
|
||||||
sub hook_connect {
|
|
||||||
my ($self,$transaction) = @_;
|
|
||||||
|
|
||||||
my %skip = ();
|
|
||||||
#my %l = $self->loaded_plugins;
|
|
||||||
#foreach my $p (keys %l) {
|
|
||||||
# $self->log(LOGDEBUG, "LOADED: $p");
|
|
||||||
#}
|
|
||||||
my $remote = $self->qp->connection->remote_ip;
|
|
||||||
foreach ($self->qp->config("skip_plugins")) {
|
|
||||||
chomp;
|
|
||||||
s/^\s*//;
|
|
||||||
s/\s*$//;
|
|
||||||
my ($ipmask, $action, $plugins) = split /\s+/, $_, 3;
|
|
||||||
next unless defined $action;
|
|
||||||
$action = lc $action;
|
|
||||||
$plugins = "" unless defined $plugins;
|
|
||||||
|
|
||||||
my ($net,$mask) = split '/', $ipmask, 2;
|
|
||||||
if (!defined $mask) {
|
|
||||||
$mask = 32;
|
|
||||||
}
|
|
||||||
$mask = pack "B32", "1"x($mask)."0"x(32-$mask);
|
|
||||||
if (join(".", unpack("C4", inet_aton($remote) & $mask)) eq $net) {
|
|
||||||
if ($action eq 'skip') {
|
|
||||||
foreach my $plugin (split /,/, $plugins) {
|
|
||||||
$self->plugin_disable($self->escape_plugin($plugin))
|
|
||||||
or $self->log(LOGWARN, "tried to disable a plugin "
|
|
||||||
."which was not loaded: $plugin");
|
|
||||||
}
|
|
||||||
$self->log(LOGDEBUG, "skipping plugins "
|
|
||||||
.join(",", $self->disabled_plugins));
|
|
||||||
}
|
|
||||||
elsif ($action eq 'continue') {
|
|
||||||
$self->log(LOGDEBUG, "ok, doing nothing with the plugins");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$self->log(LOGWARN, "unknown action '$action' for $ipmask");
|
|
||||||
}
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (DECLINED);
|
|
||||||
}
|
|
||||||
|
|
||||||
# vim: sw=4 ts=4 expandtab syn=perl
|
|
Loading…
Reference in New Issue
Block a user