Abstracted spool_dir creation and added temp_file() and temp_dir() subs to

make it easier for plugins to manage temporary workspace.  Also add POD and
tests for the new functions.  Still need to add tests to the temp_*() calls
from a plugin.


git-svn-id: https://svn.perl.org/qpsmtpd/trunk@369 958fd67b-6ff1-0310-b445-bb7760255be9
This commit is contained in:
John Peacock 2005-02-22 02:47:39 +00:00
parent d790bd519d
commit bb36c60b6a
6 changed files with 149 additions and 20 deletions

View File

@ -1,6 +1,13 @@
0.29 0.29
New temp_file() and temp_dir() methods; when used by plugins, they create
a filename or directory which will last only as long as the current
transaction. Also created a spool_dir() method which checks/creates the
spool_dir when the application starts up. All three methods are also
available in the base class where the temp_* objects are not automatically
limited to the transaction's lifetime. (John Peacock)
Added Gavin Carr's greylisting plugin Added Gavin Carr's greylisting plugin
Renamed config/ to config.sample/ Renamed config/ to config.sample/

View File

@ -200,3 +200,32 @@ With the $Include stuff you order using the filename of the plugin.d
file. So if you have a plugin called xyz but want it to come early on, file. So if you have a plugin called xyz but want it to come early on,
you call it's config file 00_xyz, but that file still refers to the you call it's config file 00_xyz, but that file still refers to the
plugin called xyz. plugin called xyz.
=head1 Temporary Files
The temporary file and directory functions can be used for plugin specific
workfiles and will automatically be deleted at the end of the current
transaction.
=over 4
=item temp_file()
Returns a unique name of a file located in the default spool directory, but
does not open that file (i.e. it is the name not a file handle).
=item temp_dir()
Returns the name of a unique directory located in the default spool
directory, after creating the directory with 0700 rights. If you need a
directory with different rights (say for an antivirus daemon), you will
need to use the base function $self->qp->temp_dir() which takes a single
parameter for the permissions requested (see L<mkdir> for details). A
directory created like this will B<not> be deleted when the transaction is
ended.
=item spool_dir()
Returns the configured system-wide spool directory.
=back

View File

@ -255,4 +255,52 @@ sub _register_hook {
} }
} }
sub spool_dir {
my $self = shift;
unless ( $self->{_spool_dir} ) { # first time through
my $spool_dir = $self->config('spool_dir')
|| Qpsmtpd::Utils::tildeexp('~/tmp/');
$spool_dir .= "/" unless ($spool_dir =~ m!/$!);
$spool_dir =~ /^(.+)$/ or die "spool_dir not configured properly";
$spool_dir = $1; # cleanse the taint
$self->{_spool_dir} = $spool_dir;
# Make sure the spool dir has appropriate rights
if (-e $spool_dir) {
my $mode = (stat($spool_dir))[2];
warn "Permissions on spool_dir $spool_dir are not 0700" if $mode & 07077;
}
# And finally, create it if it doesn't already exist
-d $spool_dir or mkdir($spool_dir, 0700)
or die "Could not create spool_dir $spool_dir: $!";
}
return $self->{_spool_dir};
}
# For unique filenames. We write to a local tmp dir so we don't need
# to make them unpredictable.
my $transaction_counter = 0;
sub temp_file {
my $self = shift;
my $filename = $self->spool_dir()
. join(":", time, $$, $transaction_counter++);
$filename =~ tr!A-Za-z0-9:/_-!!cd;
return $filename;
}
sub temp_dir {
my $self = shift;
my $mask = shift || 0700;
my $dirname = $self->temp_file();
-d $dirname or mkdir($dirname, $mask)
or die "Could not create temporary directory $dirname: $!";
return $dirname;
}
1; 1;

View File

@ -53,6 +53,24 @@ sub connection {
shift->qp->connection; shift->qp->connection;
} }
sub spool_dir {
shift->qp->spool_dir;
}
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;
}
# plugin inheritance: # plugin inheritance:
# usage: # usage:
# sub register { # sub register {

View File

@ -7,10 +7,6 @@ use Qpsmtpd::Constants;
use IO::File qw(O_RDWR O_CREAT); use IO::File qw(O_RDWR O_CREAT);
# For unique filenames. We write to a local tmp dir so we don't need
# to make them unpredictable.
my $transaction_counter = 0;
sub new { start(@_) } sub new { start(@_) }
sub start { sub start {
@ -71,22 +67,7 @@ sub body_write {
my $self = shift; my $self = shift;
my $data = shift; my $data = shift;
unless ($self->{_body_file}) { unless ($self->{_body_file}) {
my $spool_dir = $self->config('spool_dir') ? $self->config('spool_dir') $self->{_filename} = $self->temp_file();
: Qpsmtpd::Utils::tildeexp('~/tmp/');
$spool_dir .= "/" unless ($spool_dir =~ m!/$!);
$spool_dir =~ /^(.+)$/ or die "spool_dir not configured properly";
$spool_dir = $1;
if (-e $spool_dir) {
my $mode = (stat($spool_dir))[2];
die "Permissions on spool_dir $spool_dir are not 0700" if $mode & 07077;
}
-d $spool_dir or mkdir($spool_dir, 0700) or die "Could not create spool_dir $spool_dir: $!";
$self->{_filename} = $spool_dir . join(":", time, $$, $transaction_counter++);
$self->{_filename} =~ tr!A-Za-z0-9:/_-!!cd;
$self->{_body_file} = IO::File->new($self->{_filename}, O_RDWR|O_CREAT, 0600) $self->{_body_file} = IO::File->new($self->{_filename}, O_RDWR|O_CREAT, 0600)
or die "Could not open file $self->{_filename} - $! "; # . $self->{_body_file}->error; or die "Could not open file $self->{_filename} - $! "; # . $self->{_body_file}->error;
} }
@ -129,6 +110,25 @@ sub DESTROY {
if ($self->{_filename} and -e $self->{_filename}) { if ($self->{_filename} and -e $self->{_filename}) {
unlink $self->{_filename} or $self->log(LOGERROR, "Could not unlink ", $self->{_filename}, ": $!"); unlink $self->{_filename} or $self->log(LOGERROR, "Could not unlink ", $self->{_filename}, ": $!");
} }
# These may not exist
if ( $self->{_temp_files} ) {
$self->log(LOGDEBUG, "Cleaning up temporary transaction files");
foreach my $file ( @{$self->{_temp_files}} ) {
next unless -e $file;
unlink $file or $self->log(LOGERROR,
"Could not unlink temporary file", $file, ": $!");
}
}
# Ditto
if ( $self->{_temp_dirs} ) {
eval {use File::Path};
$self->log(LOGDEBUG, "Cleaning up temporary directories");
foreach my $dir ( @{$self->{_temp_dirs}} ) {
rmtree($dir) or $self->log(LOGERROR,
"Could not unlink temporary dir", $dir, ": $!");
}
}
} }

27
t/tempstuff.t Normal file
View File

@ -0,0 +1,27 @@
#!/usr/bin/perl -w
use Test::More qw(no_plan);
use File::Path;
use strict;
use lib 't';
use_ok('Test::Qpsmtpd');
BEGIN { # need this to happen before anything else
my $cwd = `pwd`;
chomp($cwd);
open my $spooldir, '>', "./config.sample/spool_dir";
print $spooldir "$cwd/t/tmp";
close $spooldir;
}
ok(my ($smtpd, $conn) = Test::Qpsmtpd->new_conn(), "get new connection");
my ($spool_dir,$tempfile,$tempdir) = ( $smtpd->spool_dir,
$smtpd->temp_file(), $smtpd->temp_dir() );
ok( $spool_dir =~ m!t/tmp/$!, "Located the spool directory");
ok( $tempfile =~ /^$spool_dir/, "Temporary filename" );
ok( $tempdir =~ /^$spool_dir/, "Temporary directory" );
ok( -d $tempdir, "And that directory exists" );
unlink "./config.sample/spool_dir";
rmtree($spool_dir);