2002-07-06 09:18:48 +02:00
|
|
|
package Qpsmtpd::Plugin;
|
2005-07-07 06:17:39 +02:00
|
|
|
use Qpsmtpd::Constants;
|
2002-07-06 09:18:48 +02:00
|
|
|
use strict;
|
|
|
|
|
2006-03-01 17:46:55 +01:00
|
|
|
# more or less in the order they will fire
|
2005-07-14 04:31:01 +02:00
|
|
|
our @hooks = qw(
|
2006-04-07 20:58:02 +02:00
|
|
|
logging config pre-connection connect ehlo_parse ehlo
|
|
|
|
helo_parse helo auth_parse auth auth-plain auth-login auth-cram-md5
|
|
|
|
rcpt_parse rcpt_pre rcpt mail_parse mail mail_pre
|
|
|
|
data data_post queue_pre queue queue_post
|
2006-03-01 17:46:55 +01:00
|
|
|
quit reset_transaction disconnect post-connection
|
2007-04-09 16:45:11 +02:00
|
|
|
unrecognized_command deny ok received_line
|
2004-03-05 13:46:24 +01:00
|
|
|
);
|
2005-07-14 04:31:01 +02:00
|
|
|
our %hooks = map { $_ => 1 } @hooks;
|
2004-03-05 13:46:24 +01:00
|
|
|
|
2002-07-06 09:18:48 +02:00
|
|
|
sub new {
|
|
|
|
my $proto = shift;
|
|
|
|
my $class = ref($proto) || $proto;
|
2004-08-31 03:58:57 +02:00
|
|
|
bless ({}, $class);
|
2002-07-06 09:18:48 +02:00
|
|
|
}
|
|
|
|
|
2006-06-28 22:05:04 +02:00
|
|
|
sub hook_name {
|
|
|
|
return shift->{_hook};
|
|
|
|
}
|
|
|
|
|
2002-07-08 04:30:11 +02:00
|
|
|
sub register_hook {
|
2004-06-11 22:01:17 +02:00
|
|
|
my ($plugin, $hook, $method, $unshift) = @_;
|
2005-07-07 06:17:39 +02:00
|
|
|
|
2004-03-05 13:46:24 +01:00
|
|
|
die $plugin->plugin_name . " : Invalid hook: $hook" unless $hooks{$hook};
|
|
|
|
|
2005-07-14 04:31:01 +02:00
|
|
|
$plugin->{_qp}->log(LOGDEBUG, $plugin->plugin_name, "hooking", $hook)
|
|
|
|
unless $hook =~ /logging/; # can't log during load_logging()
|
2005-07-07 06:17:39 +02:00
|
|
|
|
2002-07-08 04:30:11 +02:00
|
|
|
# I can't quite decide if it's better to parse this code ref or if
|
|
|
|
# we should pass the plugin object and method name ... hmn.
|
2006-06-28 22:05:04 +02:00
|
|
|
$plugin->qp->_register_hook
|
|
|
|
($hook,
|
|
|
|
{ code => sub { local $plugin->{_qp} = shift;
|
|
|
|
local $plugin->{_hook} = $hook;
|
|
|
|
$plugin->$method(@_)
|
|
|
|
},
|
|
|
|
name => $plugin->plugin_name,
|
|
|
|
},
|
|
|
|
$unshift,
|
|
|
|
);
|
2002-07-08 04:30:11 +02:00
|
|
|
}
|
2002-07-06 09:18:48 +02:00
|
|
|
|
2004-08-31 03:58:57 +02:00
|
|
|
sub _register {
|
|
|
|
my $self = shift;
|
|
|
|
my $qp = shift;
|
|
|
|
local $self->{_qp} = $qp;
|
2005-07-07 06:17:39 +02:00
|
|
|
$self->init($qp, @_) if $self->can('init');
|
|
|
|
$self->_register_standard_hooks($qp, @_);
|
|
|
|
$self->register($qp, @_) if $self->can('register');
|
2004-08-31 03:58:57 +02:00
|
|
|
}
|
|
|
|
|
2002-07-08 04:30:11 +02:00
|
|
|
sub qp {
|
|
|
|
shift->{_qp};
|
|
|
|
}
|
2002-07-06 09:18:48 +02:00
|
|
|
|
2002-07-08 04:30:11 +02:00
|
|
|
sub log {
|
2002-09-08 12:00:02 +02:00
|
|
|
my $self = shift;
|
2005-03-24 22:16:35 +01:00
|
|
|
$self->qp->varlog(shift, $self->hook_name, $self->plugin_name, @_)
|
|
|
|
unless defined $self->hook_name and $self->hook_name eq 'logging';
|
2002-07-06 09:18:48 +02:00
|
|
|
}
|
|
|
|
|
2002-07-08 04:30:11 +02:00
|
|
|
sub transaction {
|
|
|
|
# not sure if this will work in a non-forking or a threaded daemon
|
|
|
|
shift->qp->transaction;
|
|
|
|
}
|
2002-07-06 09:18:48 +02:00
|
|
|
|
2004-08-31 03:58:57 +02:00
|
|
|
sub connection {
|
|
|
|
shift->qp->connection;
|
|
|
|
}
|
|
|
|
|
2005-02-22 03:47:39 +01:00
|
|
|
sub spool_dir {
|
|
|
|
shift->qp->spool_dir;
|
|
|
|
}
|
|
|
|
|
2005-10-31 18:12:37 +01:00
|
|
|
sub auth_user {
|
|
|
|
shift->qp->auth_user;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub auth_mechanism {
|
|
|
|
shift->qp->auth_mechanism;
|
|
|
|
}
|
|
|
|
|
2005-02-22 03:47:39 +01:00
|
|
|
sub temp_file {
|
|
|
|
my $self = shift;
|
|
|
|
my $tempfile = $self->qp->temp_file;
|
|
|
|
push @{$self->qp->transaction->{_temp_files}}, $tempfile;
|
|
|
|
return $tempfile;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub temp_dir {
|
|
|
|
my $self = shift;
|
|
|
|
my $tempdir = $self->qp->temp_dir();
|
|
|
|
push @{$self->qp->transaction->{_temp_dirs}}, $tempdir;
|
|
|
|
return $tempdir;
|
|
|
|
}
|
|
|
|
|
2004-09-08 07:14:10 +02:00
|
|
|
# plugin inheritance:
|
|
|
|
# usage:
|
2005-07-07 06:17:39 +02:00
|
|
|
# sub init {
|
2004-09-08 07:14:10 +02:00
|
|
|
# my $self = shift;
|
|
|
|
# $self->isa_plugin("rhsbl");
|
|
|
|
# $self->SUPER::register(@_);
|
|
|
|
# }
|
|
|
|
sub isa_plugin {
|
|
|
|
my ($self, $parent) = @_;
|
|
|
|
my ($currentPackage) = caller;
|
2005-07-07 06:17:39 +02:00
|
|
|
|
|
|
|
my $cleanParent = $parent;
|
|
|
|
$cleanParent =~ s/\W/_/g;
|
|
|
|
my $newPackage = $currentPackage."::_isa_$cleanParent";
|
|
|
|
|
2005-07-14 13:05:11 +02:00
|
|
|
# don't reload plugins if they are already loaded
|
|
|
|
return if defined &{"${newPackage}::plugin_name"};
|
2004-09-08 07:14:10 +02:00
|
|
|
|
2007-08-31 07:26:04 +02:00
|
|
|
# find $parent in plugin_dirs
|
|
|
|
my $parent_dir;
|
|
|
|
for ($self->qp->plugin_dirs) {
|
|
|
|
if (-e "$_/$parent") {
|
|
|
|
$parent_dir = $_;
|
|
|
|
last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
die "cannot find plugin '$parent'" unless $parent_dir;
|
|
|
|
|
2005-07-07 06:17:39 +02:00
|
|
|
$self->compile($self->plugin_name . "_isa_$cleanParent",
|
2004-09-08 07:14:10 +02:00
|
|
|
$newPackage,
|
2007-08-31 07:26:04 +02:00
|
|
|
"$parent_dir/$parent");
|
2005-07-07 06:17:39 +02:00
|
|
|
warn "---- $newPackage\n";
|
2004-09-07 07:07:20 +02:00
|
|
|
no strict 'refs';
|
2004-09-08 07:14:10 +02:00
|
|
|
push @{"${currentPackage}::ISA"}, $newPackage;
|
2004-09-07 07:07:20 +02:00
|
|
|
}
|
|
|
|
|
2005-07-07 06:17:39 +02:00
|
|
|
# why isn't compile private? it's only called from Plugin and Qpsmtpd.
|
2004-11-18 20:47:10 +01:00
|
|
|
sub compile {
|
2007-05-18 00:02:32 +02:00
|
|
|
my ($class, $plugin, $package, $file, $test_mode, $orig_name) = @_;
|
2004-11-18 20:47:10 +01:00
|
|
|
|
|
|
|
my $sub;
|
|
|
|
open F, $file or die "could not open $file: $!";
|
|
|
|
{
|
|
|
|
local $/ = undef;
|
|
|
|
$sub = <F>;
|
|
|
|
}
|
|
|
|
close F;
|
|
|
|
|
2005-05-05 09:44:34 +02:00
|
|
|
my $line = "\n#line 0 $file\n";
|
2004-11-18 20:47:10 +01:00
|
|
|
|
|
|
|
if ($test_mode) {
|
2007-05-18 00:02:32 +02:00
|
|
|
if (open(F, "t/plugin_tests/$orig_name")) {
|
2004-11-18 20:47:10 +01:00
|
|
|
local $/ = undef;
|
2007-05-18 00:02:32 +02:00
|
|
|
$sub .= "#line 1 t/plugin_tests/$orig_name\n";
|
2004-11-18 20:47:10 +01:00
|
|
|
$sub .= <F>;
|
|
|
|
close F;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
my $eval = join(
|
|
|
|
"\n",
|
|
|
|
"package $package;",
|
|
|
|
'use Qpsmtpd::Constants;',
|
|
|
|
"require Qpsmtpd::Plugin;",
|
|
|
|
'use vars qw(@ISA);',
|
2005-05-25 18:36:14 +02:00
|
|
|
'use strict;',
|
2004-11-18 20:47:10 +01:00
|
|
|
'@ISA = qw(Qpsmtpd::Plugin);',
|
|
|
|
($test_mode ? 'use Test::More;' : ''),
|
|
|
|
"sub plugin_name { qq[$plugin] }",
|
|
|
|
$line,
|
|
|
|
$sub,
|
|
|
|
"\n", # last line comment without newline?
|
|
|
|
);
|
|
|
|
|
|
|
|
#warn "eval: $eval";
|
|
|
|
|
|
|
|
$eval =~ m/(.*)/s;
|
|
|
|
$eval = $1;
|
|
|
|
|
|
|
|
eval $eval;
|
|
|
|
die "eval $@" if $@;
|
|
|
|
}
|
|
|
|
|
2005-07-07 06:17:39 +02:00
|
|
|
sub _register_standard_hooks {
|
|
|
|
my ($plugin, $qp) = @_;
|
|
|
|
|
2005-07-14 04:31:01 +02:00
|
|
|
for my $hook (@hooks) {
|
2005-07-07 06:17:39 +02:00
|
|
|
my $hooksub = "hook_$hook";
|
|
|
|
$hooksub =~ s/\W/_/g;
|
|
|
|
$plugin->register_hook( $hook, $hooksub )
|
|
|
|
if ($plugin->can($hooksub));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-07-06 09:18:48 +02:00
|
|
|
1;
|