2002-07-03 15:10:44 +02:00
|
|
|
package Qpsmtpd;
|
|
|
|
use strict;
|
|
|
|
|
2002-10-17 09:42:22 +02:00
|
|
|
$Qpsmtpd::VERSION = "0.12";
|
2002-09-24 12:56:35 +02:00
|
|
|
sub TRACE_LEVEL { 6 }
|
2002-07-03 15:10:44 +02:00
|
|
|
|
2002-09-24 12:56:35 +02:00
|
|
|
use Sys::Hostname;
|
|
|
|
use Qpsmtpd::Constants;
|
2002-07-03 15:10:44 +02:00
|
|
|
|
2002-09-24 12:56:35 +02:00
|
|
|
sub version { $Qpsmtpd::VERSION };
|
2002-07-03 15:10:44 +02:00
|
|
|
|
2002-09-24 12:56:35 +02:00
|
|
|
sub log {
|
|
|
|
my ($self, $trace, @log) = @_;
|
|
|
|
warn join(" ", $$, @log), "\n"
|
|
|
|
if $trace <= TRACE_LEVEL;
|
2002-07-03 15:10:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# method to get the configuration. It just calls get_qmail_config by
|
|
|
|
# default, but it could be overwritten to look configuration up in a
|
|
|
|
# database or whatever.
|
|
|
|
#
|
|
|
|
sub config {
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
2002-10-17 09:39:54 +02:00
|
|
|
#warn "SELF->config($c) ", ref $self;
|
2002-09-24 12:56:35 +02:00
|
|
|
|
2002-07-03 15:10:44 +02:00
|
|
|
my %defaults = (
|
|
|
|
me => hostname,
|
|
|
|
timeout => 1200,
|
|
|
|
);
|
|
|
|
|
2002-07-06 04:09:01 +02:00
|
|
|
if (wantarray) {
|
|
|
|
my @config = $self->get_qmail_config($c);
|
|
|
|
@config = @{$defaults{$c}} if (!@config and $defaults{$c});
|
|
|
|
return @config;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return ($self->get_qmail_config($c) || $defaults{$c});
|
|
|
|
}
|
2002-07-03 15:10:44 +02:00
|
|
|
}
|
|
|
|
|
2002-07-08 04:30:11 +02:00
|
|
|
|
2002-07-03 15:10:44 +02:00
|
|
|
sub get_qmail_config {
|
|
|
|
my ($self, $config) = (shift, shift);
|
2002-09-10 15:45:25 +02:00
|
|
|
$self->log(8, "trying to get config for $config");
|
2002-07-03 15:10:44 +02:00
|
|
|
if ($self->{_config_cache}->{$config}) {
|
|
|
|
return wantarray ? @{$self->{_config_cache}->{$config}} : $self->{_config_cache}->{$config}->[0];
|
|
|
|
}
|
|
|
|
my $configdir = '/var/qmail/control';
|
|
|
|
my ($name) = ($0 =~ m!(.*?)/([^/]+)$!);
|
|
|
|
$configdir = "$name/config" if (-e "$name/config/$config");
|
|
|
|
open CF, "<$configdir/$config" or warn "$$ could not open configfile $config: $!", return;
|
|
|
|
my @config = <CF>;
|
|
|
|
chomp @config;
|
|
|
|
@config = grep { $_ and $_ !~ m/^\s*#/ and $_ =~ m/\S/} @config;
|
|
|
|
close CF;
|
2002-09-10 15:45:25 +02:00
|
|
|
$self->log(10, "returning get_config for $config ",Data::Dumper->Dump([\@config], [qw(config)]));
|
2002-07-03 15:10:44 +02:00
|
|
|
$self->{_config_cache}->{$config} = \@config;
|
|
|
|
return wantarray ? @config : $config[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-07-06 09:16:23 +02:00
|
|
|
|
|
|
|
sub load_plugins {
|
|
|
|
my $self = shift;
|
|
|
|
my @plugins = $self->config('plugins');
|
|
|
|
|
|
|
|
my ($name) = ($0 =~ m!(.*?)/([^/]+)$!);
|
|
|
|
my $dir = "$name/plugins";
|
|
|
|
$self->log(2, "loading plugins from $dir");
|
|
|
|
|
|
|
|
for my $plugin (@plugins) {
|
2002-09-24 12:56:35 +02:00
|
|
|
$self->log(7, "Loading $plugin");
|
2002-07-06 09:16:23 +02:00
|
|
|
my $plugin_name = $plugin;
|
|
|
|
# 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;
|
|
|
|
|
|
|
|
|
|
|
|
my $sub;
|
|
|
|
open F, "$dir/$plugin" or die "could not open $dir/$plugin: $!";
|
|
|
|
{
|
|
|
|
local $/ = undef;
|
|
|
|
$sub = <F>;
|
|
|
|
}
|
|
|
|
close F;
|
|
|
|
|
|
|
|
my $package = "Qpsmtpd::Plugin::$plugin_name";
|
|
|
|
|
|
|
|
my $line = "\n#line 1 $dir/$plugin\n";
|
|
|
|
|
|
|
|
my $eval = join(
|
|
|
|
"\n",
|
|
|
|
"package $package;",
|
2002-07-08 04:30:11 +02:00
|
|
|
'use Qpsmtpd::Constants;',
|
2002-07-06 09:16:23 +02:00
|
|
|
"require Qpsmtpd::Plugin;",
|
|
|
|
'use vars qw(@ISA);',
|
|
|
|
'@ISA = qw(Qpsmtpd::Plugin);',
|
2002-09-08 11:58:47 +02:00
|
|
|
"sub plugin_name { qq[$plugin_name] }",
|
2002-07-08 04:30:11 +02:00
|
|
|
$line,
|
2002-07-06 09:16:23 +02:00
|
|
|
$sub,
|
|
|
|
"\n", # last line comment without newline?
|
|
|
|
);
|
|
|
|
|
2002-07-15 14:16:10 +02:00
|
|
|
#warn "eval: $eval";
|
2002-07-06 09:16:23 +02:00
|
|
|
|
2002-07-08 04:30:11 +02:00
|
|
|
$eval =~ m/(.*)/s;
|
2002-07-06 09:16:23 +02:00
|
|
|
$eval = $1;
|
|
|
|
|
|
|
|
eval $eval;
|
|
|
|
die "eval $@" if $@;
|
|
|
|
|
2002-07-08 04:30:11 +02:00
|
|
|
my $plug = $package->new(qpsmtpd => $self);
|
|
|
|
$plug->register($self);
|
2002-07-06 09:16:23 +02:00
|
|
|
|
2002-07-08 04:30:11 +02:00
|
|
|
}
|
|
|
|
}
|
2002-07-06 09:16:23 +02:00
|
|
|
|
2002-07-08 04:30:11 +02:00
|
|
|
sub run_hooks {
|
|
|
|
my ($self, $hook) = (shift, shift);
|
|
|
|
if ($self->{_hooks}->{$hook}) {
|
|
|
|
my @r;
|
|
|
|
for my $code (@{$self->{_hooks}->{$hook}}) {
|
2002-10-17 09:39:54 +02:00
|
|
|
$self->log(5, "running plugin ", $code->{name});
|
2002-09-24 12:56:35 +02:00
|
|
|
eval { (@r) = &{$code->{code}}($self->transaction, @_); };
|
2002-09-08 11:58:47 +02:00
|
|
|
$@ and $self->log(0, "FATAL PLUGIN ERROR: ", $@) and next;
|
2002-10-17 09:39:54 +02:00
|
|
|
!defined $r[0]
|
|
|
|
and $self->log(1, "plugin ".$code->{name}
|
|
|
|
."running the $hook hook returned undef!")
|
|
|
|
and next;
|
2002-07-08 04:30:11 +02:00
|
|
|
last unless $r[0] == DECLINED;
|
|
|
|
}
|
|
|
|
return @r;
|
2002-07-06 09:16:23 +02:00
|
|
|
}
|
2002-07-08 04:30:11 +02:00
|
|
|
warn "Did not run any hooks ...";
|
|
|
|
return (0, '');
|
|
|
|
}
|
|
|
|
|
|
|
|
sub _register_hook {
|
|
|
|
my $self = shift;
|
|
|
|
my ($hook, $code) = @_;
|
2002-07-06 09:16:23 +02:00
|
|
|
|
2002-07-08 04:30:11 +02:00
|
|
|
#my $plugin = shift; # see comment in Plugin.pm:register_hook
|
|
|
|
|
|
|
|
$self->{_hooks} ||= {};
|
|
|
|
my $hooks = $self->{_hooks};
|
|
|
|
push @{$hooks->{$hook}}, $code;
|
2002-07-06 09:16:23 +02:00
|
|
|
}
|
|
|
|
|
2002-07-03 15:10:44 +02:00
|
|
|
1;
|