Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery

My Solution

by hawtin (Prior)
on Nov 02, 2004 at 08:36 UTC ( [id://404530] : note . print w/replies, xml ) Need Help??

in reply to Compressing and Encrypting files on Windows

The way I solved it

Thanks to all those of you who replied to my post. I thought it would be worthwhile to hint at how I solved the issue in case someone has a similar problem later. I mentioned in the post I am using various tools (like XML::SAX) that require file handles of some kind, so any solution has to involve producing IO::Handle like objects.

When reading data this is no problem, I can just read all my file into a scalar, use my encryption algorithm to decrypt then inflate and finally use the reulting vector to create an IO::Handle via IO::Scalar.

When writing data the issue is more complex, I have to pass something that the writing modules can treat as a file handle, but when they are done make sure that the post processing happens. I could have changed my code to add an extra "now save this file" step after every place where files are written, but I decided to create my own IO::Thing to do that work for me.

My first thought was to have an object that held a handle and some extra info, pass all calls through to the handle except close() and when the object is closed do the extra work. That doesn't work because handles are not objects (and life is too short to understand as much about them as I want to).

My second thought was to create a IO::Scalar subclass that can perform a special action when the close it called, that doesn't work either because IO::Scalar implements its objects as a localised GLOB.

My third thought was to badly mangle the definition of IO::Scalar to force the behaviour I wanted. This I did creating my own class called IO::ActionOnClose (the implementation is so horrible I won't troble you with it).

With this class my open function now looks like:

sub _open_file { my($file_name,$mode) = @_; $mode = "r" if(!defined($mode)); my $compress = 1; $compress = "" if($file_name =~ /\.xml$/i); if($mode eq "r") { my $fh = new IO::File($file_name); binmode $fh; my $f1 = ""; while(!$fh->eof()) { my $c = $fh->read($f1,1024*16,length($f1)); } $fh = undef; my $f2 = ""; if(defined($model_passphrase) && $model_passphrase ne "") { # Pad $f1 to the next 8 byte boundary if((length($f1) % 8) != 0) { $f1 .= "\x00" x (8 - (length($f1) % 8)); } my $cipher = new Crypt::Blowfish $model_passphrase; for(my $i=0;(8*$i)<length($f1);$i++) { $f2 .= $cipher->decrypt(substr($f1,8*$i,8)); } $f2 =~ s/\x00+$//s; } else { $f2 = $f1; } # Just in case the file is big save some memory $f1 = ""; my $f3 = ""; if($compress) { $f3 = uncompress($f2); } else { $f3 = $f2; } return new IO::Scalar \$f3; } else { # When writing I want to first get the output, then compress # then encrypt, then write to the file. # So I have to create a handle that does some magic # when it is closed my $buffer; return new IO::ActionOnClose(\$buffer, action => \&_send_file, args => [\$buffer,$file_name,$compress,$model +_passphrase]); } } } sub _send_file { my($f3_ref,$file_name,$compress,$model_passphrase) = @_; # This is the reverse of what _open_file does for # read (even the variable names are the same) my $f2; if($compress) { $f2 = compress(${$f3_ref}); } else { $f2 = ${$f3_ref}; } my $f1 = ""; if(defined($model_passphrase) && $model_passphrase ne "") { my $cipher = new Crypt::Blowfish $model_passphrase; # Pad $f1 to the next 8 byte boundary if((length($f2) % 8) != 0) { $f2 .= "\x00" x (8 - (length($f2) % 8)); } for(my $i=0;8*$i<length($f2);$i++) { $f1 .= $cipher->encrypt(substr($f2,8*$i,8)); } } else { $f1 = $f2; } my $fh = new IO::File($file_name,"w"); binmode $fh; $fh->syswrite($f1,length($f1)); $fh->close(); }

Once again thaks to those who contributed and I hope this helps someone else