#!/usr/bin/perl # # Copyright (C) 2001-2023 Graeme Walker # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # === # # OpensslFileStore.pm # # A package that simplifies the management of temporary certificate files. # # Synopsis: # use OpensslFileStore ; # $fs = new OpensslFileStore( "certs" , ".pem" ) ; # $path = $fs->catfile( "bob.key" , "bob.crt" ) ; # $path = $fs->tmpfile( "bob.csr" ) ; # $path = $fs->infile( "alice.key" ) ; # $path = $fs->outfile( "alice.crt" ) ; # $path = $fs->logfile() ; # $fs->cleanup( 'cat' , 'tmp' , 'out' , 'log' ) ; # # The implementation can be customised with respect to logging, file paths # and file deletion, eg: # $OpensslFileStore::log_cat_fn = sub { my ($in,@out)=@_ ; print "creating [$in]\n" } ; # $OpensslFileStore::outpath_fn = sub { my ($dir,$fname)=@_ ; return "/tmp/$fname" } ; # $OpensslFileStore::inpath_fn = sub { my ($dir,$fname)=@_ ; return "/tmp/$fname" } ; # $OpensslFileStore::unlink_fn = sub { my ($path)=@_ ; unlink $path } ; # # Or perhaps: # use System ; # $OpensslFileStore::log_cat_fn = sub { System::log_("creating [$_[0]]\n") } ; # $OpensslFileStore::outpath_fn = sub { System::tempfile($_[1]) } ; # $OpensslFileStore::inpath_fn = sub { "certificates/$_[1]" } ; # $OpensslFileStore::unlink_fn = sub { System::unlink($_[0]) } ; # use strict ; use FileHandle ; use File::Basename ; use Carp ; package OpensslFileStore ; our $outpath_fn = sub { _joinpath( $_[0] , $_[1] ) } ; our $inpath_fn = sub { _joinpath( $_[0] , $_[1] ) } ; our $log_cat_fn = sub {} ; our $unlink_fn = sub { CORE::unlink($_[0]) } ; sub new { # Constructor. The directory parameter is only used when passed to # the inpath and outpath functions. The filename extension parameter # is used for 'catfile' output filenames (eg. ".pem"). my ( $classname , $dir , $ext ) = @_ ; $dir ||= "" ; $ext ||= "" ; return bless { m_dir => $dir , m_ext => $ext , m_cleanup => {} , } , $classname ; } sub catfile { # Concatenates the given files into a temporary file and the temporary file # is registered for cleanup with type 'cat'. my ( $this , @fnames ) = @_ ; my $dot_ext = $this->{m_ext} ; ( my $name = join("+",@fnames) ) =~ s/\./_/g ; my $path_out = $this->_outfilepath( "${name}${dot_ext}" ) ; my @paths_in = map { $this->_infilepath($_) } @fnames ; _log_cat( $path_out , @paths_in ) ; _cat( $path_out , @paths_in ) ; $this->_add_cleanup( 'cat' , $path_out ) ; return $path_out ; } sub tmpfile { # Returns the full path for a temporary file with the given filename key # and registers it for cleanup with type 'tmp'. my ( $this , $fname ) = @_ ; my $path_out = $this->_outfilepath( $fname ) ; $this->_add_cleanup( 'tmp' , $path_out ) ; return $path_out ; } sub outfile { # Returns the full path for an output file and optionally registers it # for cleanup with type 'out'. my ( $this , $fname , $keep ) = @_ ; my $path_out = $this->_outfilepath( $fname ) ; $this->_add_cleanup( 'out' , $path_out ) unless $keep ; return $path_out ; } sub infile { # Returns the full path for the given input filename. The file # should already exist. my ( $this , $fname ) = @_ ; my $path_in = $this->_infilepath( $fname ) ; die if !-f $path_in ; return $path_in ; } my $_generator = 0 ; sub logfile { # Returns the full path of a temporary log file and registers it for # cleanup with type 'log'. Consider customising via '$outpath_fn' # in order to improve uniqueness by incorporating '$$' etc. my ( $this ) = @_ ; my $fname = "$_generator.out" ; $_generator++ ; my $path_out = $this->_outfilepath( $fname ) ; $this->_add_cleanup( 'log' , $path_out ) ; return $path_out ; } sub cleanup { # Deletes files having the matching cleanup type. my ( $this , @types ) = @_ ; if( !defined($types[0]) ) { @types = keys %{$this->{m_cleanup}} ; } for my $type ( @types ) { for my $path ( @{$this->{m_cleanup}->{$type}} ) { _unlink( $path ) ; } } } sub _add_cleanup { my ( $this , $type , $path ) = @_ ; $this->{m_cleanup}->{$type} ||= [] ; push @{$this->{m_cleanup}->{$type}} , $path ; } sub _joinpath { return join( "/" , grep { m/./ } @_ ) ; } sub _cat { my ( $path_out , @paths_in ) = @_ ; my $fh_out = new FileHandle( "$path_out" , "w" ) or die ; for my $path_in ( @paths_in ) { my $fh_in = new FileHandle( $path_in , "r" ) or die ; while(<$fh_in>) { my $line = $_ ; print $fh_out $line or die ; } $fh_in->close() or die ; } } # -- sub _outfilepath { my ( $this , $fname ) = @_ ; return &$outpath_fn( $this->{m_dir} , $fname ) ; } sub _infilepath { my ( $this , $fname ) = @_ ; return &$inpath_fn( $this->{m_dir} , $fname ) ; } sub _log_cat { my ( $out , @in ) = @_ ; &$log_cat_fn( $out , @in ) ; } sub _unlink { my ( $path ) = @_ ; &$unlink_fn( $path ) ; } 1 ;