cvs commit: qpsmtpd/t tempstuff.t

[email protected] (John Peacock) 22 Feb 2005 02:47:39 -0000
Newsgroups perl.cvs.qpsmtpd
Message-ID <[email protected]>
cvsuser     05/02/21 18:47:39

  Modified:    .        Changes README.plugins
               lib      Qpsmtpd.pm
               lib/Qpsmtpd Plugin.pm Transaction.pm
  Added:       t        tempstuff.t
  Log:
  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.
  
  Revision  Changes    Path
  1.82      +7 -0      qpsmtpd/Changes
  
  Index: Changes
  ===================================================================
  RCS file: /cvs/public/qpsmtpd/Changes,v
  retrieving revision 1.81
  retrieving revision 1.82
  diff -u -r1.81 -r1.82
  --- Changes	15 Feb 2005 21:42:52 -0000	1.81
  +++ Changes	22 Feb 2005 02:47:38 -0000	1.82
  @@ -1,6 +1,13 @@
   
   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
   
     Renamed config/ to config.sample/
  
  
  
  1.12      +29 -0     qpsmtpd/README.plugins
  
  Index: README.plugins
  ===================================================================
  RCS file: /cvs/public/qpsmtpd/README.plugins,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- README.plugins	9 Nov 2004 15:29:10 -0000	1.11
  +++ README.plugins	22 Feb 2005 02:47:38 -0000	1.12
  @@ -200,3 +200,32 @@
   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
   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
  
  
  
  1.47      +48 -0     qpsmtpd/lib/Qpsmtpd.pm
  
  Index: Qpsmtpd.pm
  ===================================================================
  RCS file: /cvs/public/qpsmtpd/lib/Qpsmtpd.pm,v
  retrieving revision 1.46
  retrieving revision 1.47
  diff -u -r1.46 -r1.47
  --- Qpsmtpd.pm	27 Nov 2004 06:46:21 -0000	1.46
  +++ Qpsmtpd.pm	22 Feb 2005 02:47:38 -0000	1.47
  @@ -255,4 +255,52 @@
     }
   }
   
  +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.15      +18 -0     qpsmtpd/lib/Qpsmtpd/Plugin.pm
  
  Index: Plugin.pm
  ===================================================================
  RCS file: /cvs/public/qpsmtpd/lib/Qpsmtpd/Plugin.pm,v
  retrieving revision 1.14
  retrieving revision 1.15
  diff -u -r1.14 -r1.15
  --- Plugin.pm	18 Nov 2004 19:47:10 -0000	1.14
  +++ Plugin.pm	22 Feb 2005 02:47:39 -0000	1.15
  @@ -53,6 +53,24 @@
     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:
   # usage:
   #  sub register {
  
  
  
  1.19      +20 -20    qpsmtpd/lib/Qpsmtpd/Transaction.pm
  
  Index: Transaction.pm
  ===================================================================
  RCS file: /cvs/public/qpsmtpd/lib/Qpsmtpd/Transaction.pm,v
  retrieving revision 1.18
  retrieving revision 1.19
  diff -u -r1.18 -r1.19
  --- Transaction.pm	23 Sep 2004 18:54:58 -0000	1.18
  +++ Transaction.pm	22 Feb 2005 02:47:39 -0000	1.19
  @@ -7,10 +7,6 @@
   
   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 start {
  @@ -71,22 +67,7 @@
     my $self = shift;
     my $data = shift;
     unless ($self->{_body_file}) {
  -     my $spool_dir = $self->config('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;
  -
  -     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->{_filename} = $self->temp_file();
       $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;
     }
  @@ -129,6 +110,25 @@
     if ($self->{_filename} and -e $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, ": $!");
  +    }
  +  }
   }
   
   
  
  
  
  1.1                  qpsmtpd/t/tempstuff.t
  
  Index: tempstuff.t
  ===================================================================
  #!/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);