The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 19
CryptFile/CryptFile.xs 1529
CryptFile/lib/Filter/Crypto/CryptFile.pm 1013
INSTALL 11
LICENCE 11
META.yml 11
README 22
lib/Filter/Crypto.pm 44
script/crypt_file 2945
t/03_script.t 530
10 files changed (This is a version diff) 69135
@@ -1,11 +1,19 @@
 ________________________________________________________________________________
 
-                          Filter::Crypto, Version 2.03
+                          Filter::Crypto, Version 2.04
 ________________________________________________________________________________
 
 Revision history for Perl extension Filter::Crypto.
 _________________
 
+v2.04 19 Feb 2014
+
+    - Fixed crypt_file's "-c encrypted" and "-c decrypted" options when used in
+      conjunction with the "-e tempfile" option. These combinations of options
+      previously caused the input file to be replaced with a zero length file.
+      (Fixes [cpan #93152].)
+_________________
+
 v2.03 08 Jul 2013
 
     - Improved crypto library detection so that the library is now correctly
@@ -6,7 +6,7 @@
  *   C and XS portions of Filter::Crypto::CryptFile module.
  *
  * COPYRIGHT
- *   Copyright (C) 2004-2009, 2012 Steve Hay.  All rights reserved.
+ *   Copyright (C) 2004-2009, 2012, 2014 Steve Hay.  All rights reserved.
  *
  * LICENCE
  *   You may distribute under the terms of either the GNU General Public License
@@ -90,20 +90,21 @@ typedef enum {
 #endif
 
 static bool FilterCrypto_CryptFh(pTHX_ PerlIO *in_fh, PerlIO *out_fh,
-    FILTER_CRYPTO_MODE_EX crypt_mode_ex);
+    FILTER_CRYPTO_MODE_EX crypt_mode_ex, SV* num_bytes);
 static bool FilterCrypto_OutputData(pTHX_ SV *from_sv, bool encode_mode,
-    bool update_mode, PerlIO *to_fh, SV *to_sv);
+    bool update_mode, PerlIO *to_fh, SV *to_sv, SV* num_bytes);
 
 static const char *filter_crypto_use_text = "use Filter::Crypto::Decrypt;\n";
 
 /*
  * Function to encrypt or decrypt data from one filehandle to either another
  * filehandle or back to itself.
- * Returns a bool to indicate success or failure.
+ * Returns a bool to indicate success or failure and sets *num_bytes to the
+ * number of bytes written.
  */
 
 static bool FilterCrypto_CryptFh(pTHX_ PerlIO *in_fh, PerlIO *out_fh,
-    FILTER_CRYPTO_MODE_EX crypt_mode_ex)
+    FILTER_CRYPTO_MODE_EX crypt_mode_ex, SV* num_bytes)
 {
     bool encode_mode;
     bool update_mode = FALSE;
@@ -120,6 +121,9 @@ static bool FilterCrypto_CryptFh(pTHX_ PerlIO *in_fh, PerlIO *out_fh,
     unsigned char *in_text  = (unsigned char *)SvPVX(in_sv);
     const unsigned char *buf_text;
 
+    SvIOK_only_UV(num_bytes);
+    sv_setuv(num_bytes, 0);
+
     SvPOK_only(in_sv);
     SvPOK_only(in2_sv);
     SvPOK_only(out_sv);
@@ -251,6 +255,8 @@ static bool FilterCrypto_CryptFh(pTHX_ PerlIO *in_fh, PerlIO *out_fh,
 #endif
             }
 
+            sv_setuv(num_bytes, SvUV(num_bytes) + use_len);
+
             /* Remember that we have input data in in_text that still needs to
              * be encrypted and output. */
             have_in_text = TRUE;
@@ -315,7 +321,7 @@ static bool FilterCrypto_CryptFh(pTHX_ PerlIO *in_fh, PerlIO *out_fh,
             /* Write the output to the temporary output buffer or output
              * filehandle as appropriate. */
             if (!FilterCrypto_OutputData(aTHX_ out_sv, encode_mode, update_mode,
-                    out_fh, buf_sv))
+                    out_fh, buf_sv, num_bytes))
             {
                 FilterCrypto_CryptoFree(aTHX_ ctx);
                 ctx = NULL;
@@ -354,7 +360,7 @@ static bool FilterCrypto_CryptFh(pTHX_ PerlIO *in_fh, PerlIO *out_fh,
     /* Write the final block of output to the temporary output buffer or output
      * filehandle as appropriate. */
     if (!FilterCrypto_OutputData(aTHX_ out_sv, encode_mode, update_mode, out_fh,
-            buf_sv))
+            buf_sv, num_bytes))
     {
         FilterCrypto_CryptoFree(aTHX_ ctx);
         ctx = NULL;
@@ -399,11 +405,12 @@ static bool FilterCrypto_CryptFh(pTHX_ PerlIO *in_fh, PerlIO *out_fh,
  * Function to output data from a given SV to either a filehandle or to another
  * SV.  The output bytes can be optionally encoded as pairs of hexadecimal
  * digits.  Zeroes the length of the given SV after output.
- * Returns a bool to indicate success or failure.
+ * Returns a bool to indicate success or failure and adds the number of bytes
+ * written to a third given SV.
  */
 
 static bool FilterCrypto_OutputData(pTHX_ SV *from_sv, bool encode_mode,
-    bool update_mode, PerlIO *to_fh, SV *to_sv)
+    bool update_mode, PerlIO *to_fh, SV *to_sv, SV* num_bytes)
 {
     SV *from2_sv = sv_2mortal(newSV(BUFSIZ * 2));
     SvPOK_only(from2_sv);
@@ -425,6 +432,8 @@ static bool FilterCrypto_OutputData(pTHX_ SV *from_sv, bool encode_mode,
             "Appended %d bytes to output buffer", SvCUR(from2_sv)
         );
 #endif
+
+        sv_setuv(num_bytes, SvUV(num_bytes) + SvCUR(from2_sv));
     }
     else {
         /* Get the data and length to output. */
@@ -445,6 +454,8 @@ static bool FilterCrypto_OutputData(pTHX_ SV *from_sv, bool encode_mode,
             "Wrote %d bytes to output stream", from2_len
         );
 #endif
+
+        sv_setuv(num_bytes, SvUV(num_bytes) + from2_len);
     }
 
     FilterCrypto_SvSetCUR(from_sv, 0);
@@ -485,16 +496,18 @@ _debug_mode();
 # called with one in-out filehandle.
 
 void
-_crypt_fh(fh, crypt_mode_ex);
-    PROTOTYPE: $$
+_crypt_fh(fh, crypt_mode_ex, num_bytes);
+    PROTOTYPE: $$$
 
     INPUT:
         InOutStream fh;
         FILTER_CRYPTO_MODE_EX crypt_mode_ex
+        SV* num_bytes
 
     PPCODE:
     {
-        if (FilterCrypto_CryptFh(aTHX_ fh, (PerlIO *)NULL, crypt_mode_ex))
+        if (FilterCrypto_CryptFh(aTHX_ fh, (PerlIO *)NULL, crypt_mode_ex,
+                num_bytes))
             XSRETURN_YES;
         else
             XSRETURN_EMPTY;
@@ -504,17 +517,18 @@ _crypt_fh(fh, crypt_mode_ex);
 # called with one input filehandle and one output filehandle.
 
 void
-_crypt_fhs(in_fh, out_fh, crypt_mode_ex);
-    PROTOTYPE: $$$
+_crypt_fhs(in_fh, out_fh, crypt_mode_ex, num_bytes);
+    PROTOTYPE: $$$$
 
     INPUT:
         InputStream in_fh;
         OutputStream out_fh;
         FILTER_CRYPTO_MODE_EX crypt_mode_ex;
+        SV* num_bytes;
 
     PPCODE:
     {
-        if (FilterCrypto_CryptFh(aTHX_ in_fh, out_fh, crypt_mode_ex))
+        if (FilterCrypto_CryptFh(aTHX_ in_fh, out_fh, crypt_mode_ex, num_bytes))
             XSRETURN_YES;
         else
             XSRETURN_EMPTY;
@@ -7,7 +7,7 @@
 #   which they can be run via Filter::Crypto::Decrypt.
 #
 # COPYRIGHT
-#   Copyright (C) 2004-2009, 2012-2013 Steve Hay.  All rights reserved.
+#   Copyright (C) 2004-2009, 2012-2014 Steve Hay.  All rights reserved.
 #
 # LICENCE
 #   You may distribute under the terms of either the GNU General Public License
@@ -55,7 +55,7 @@ BEGIN {
         $ErrStr
     );
 
-    $VERSION = '2.03';
+    $VERSION = '2.04';
 
     XSLoader::load(__PACKAGE__, $VERSION);
 }
@@ -96,6 +96,7 @@ sub AUTOLOAD {
 
 sub crypt_file($;$$) {
     $ErrStr = '';
+    my $num_bytes = 0;
 
     if ( @_ == 1 or
         (@_ == 2 and (not defined $_[1] or $_[1] eq '' or
@@ -132,7 +133,7 @@ sub crypt_file($;$$) {
         my $crypt_mode = (@_ == 2 and defined $_[1] and $_[1] ne '')
                          ? $_[1] : CRYPT_MODE_AUTO();
 
-        unless (_crypt_fh($fh, $crypt_mode)) {
+        unless (_crypt_fh($fh, $crypt_mode, $num_bytes)) {
             local($!, $^E);
             $opened ? close $fh : $flocked ? flock $fh, LOCK_UN : 1;
             return;
@@ -231,7 +232,7 @@ sub crypt_file($;$$) {
             $crypt_mode = CRYPT_MODE_AUTO();
         }
 
-        unless (_crypt_fhs($in_fh, $out_fh, $crypt_mode)) {
+        unless (_crypt_fhs($in_fh, $out_fh, $crypt_mode, $num_bytes)) {
             local($!, $^E);
             $in_opened  ? close $in_fh
                         : $in_flocked  ? flock $in_fh,  LOCK_UN : 1;
@@ -261,7 +262,7 @@ sub crypt_file($;$$) {
         }
     }
 
-    return 1;
+    return $num_bytes ? $num_bytes : '0E0';
 }
 
 #===============================================================================
@@ -391,8 +392,10 @@ then the data is presumed to be in an encrypted state already so the mode will
 be set to C<CRYPT_MODE_DECRYPT>; otherwise the mode will be set to
 C<CRYPT_MODE_ENCRYPT>.
 
-Returns 1 on success, or a false value (namely, the undefined value in scalar
-context or the empty list in list context) and sets $ErrStr on failure.
+On success, returns the number of bytes written (which could be zero if the
+input was already in the requested state, in which case the special "zero but
+true" value will be returned); on failure returns the undefined value (in scalar
+context) or the empty list (in list context) and sets $ErrStr.
 
 =back
 
@@ -896,7 +899,7 @@ Steve Hay E<lt>shay@cpan.orgE<gt>
 
 =head1 COPYRIGHT
 
-Copyright (C) 2004-2009, 2012-2013 Steve Hay.  All rights reserved.
+Copyright (C) 2004-2009, 2012-2014 Steve Hay.  All rights reserved.
 
 =head1 LICENCE
 
@@ -906,11 +909,11 @@ License or the Artistic License, as specified in the F<LICENCE> file.
 
 =head1 VERSION
 
-Version 2.03
+Version 2.04
 
 =head1 DATE
 
-08 Jul 2013
+19 Feb 2014
 
 =head1 HISTORY
 
@@ -1,6 +1,6 @@
 ________________________________________________________________________________
 
-                          Filter::Crypto, Version 2.03
+                          Filter::Crypto, Version 2.04
 ________________________________________________________________________________
 
 PREREQUISITES
@@ -1,6 +1,6 @@
 ________________________________________________________________________________
 
-                          Filter::Crypto, Version 2.03
+                          Filter::Crypto, Version 2.04
 ________________________________________________________________________________
 
 This distribution is free software; you can redistribute it and/or modify it
@@ -54,4 +54,4 @@ requires:
   perl: 5.6.0
 resources:
   license: http://dev.perl.org/licenses/
-version: 2.03
+version: 2.04
@@ -1,6 +1,6 @@
 ________________________________________________________________________________
 
-                          Filter::Crypto, Version 2.03
+                          Filter::Crypto, Version 2.04
 ________________________________________________________________________________
 
 NAME
@@ -93,7 +93,7 @@ INSTALLATION
 
 COPYRIGHT
 
-    Copyright (C) 2004-2010, 2012-2013 Steve Hay.  All rights reserved.
+    Copyright (C) 2004-2010, 2012-2014 Steve Hay.  All rights reserved.
 
 LICENCE
 
@@ -29,7 +29,7 @@ use warnings;
 our($VERSION);
 
 BEGIN {
-    $VERSION = '2.03';
+    $VERSION = '2.04';
 }
 
 1;
@@ -270,7 +270,7 @@ Steve Hay E<lt>shay@cpan.orgE<gt>
 
 =head1 COPYRIGHT
 
-Copyright (C) 2004-2010, 2012-2013 Steve Hay.  All rights reserved.
+Copyright (C) 2004-2010, 2012-2014 Steve Hay.  All rights reserved.
 
 =head1 LICENCE
 
@@ -280,11 +280,11 @@ License or the Artistic License, as specified in the F<LICENCE> file.
 
 =head1 VERSION
 
-Version 2.03
+Version 2.04
 
 =head1 DATE
 
-08 Jul 2013
+19 Feb 2014
 
 =head1 HISTORY
 
@@ -8,7 +8,7 @@
 #   still runnable, format to hide the source code from casual prying eyes.
 #
 # COPYRIGHT
-#   Copyright (C) 2004-2006, 2012 Steve Hay.  All rights reserved.
+#   Copyright (C) 2004-2006, 2012, 2014 Steve Hay.  All rights reserved.
 #
 # LICENCE
 #   You may distribute under the terms of either the GNU General Public License
@@ -43,6 +43,7 @@ use constant WARNING_TYPE_SEVERE => 1;
 
 sub get_input_files($$$);
 sub resolve_file_expr($$);
+sub show_result();
 sub show_warning($$$@);
 sub exit_with_version();
 sub exit_with_help();
@@ -54,8 +55,8 @@ sub exit_with_error(@);
 # INITIALIZATION
 #===============================================================================
 
-our $VERSION = '1.03';
-our $YEAR    = '2004-2006, 2012';
+our $VERSION = '1.04';
+our $YEAR    = '2004-2006, 2012, 2014';
 
 #===============================================================================
 # MAIN PROGRAM
@@ -187,15 +188,7 @@ MAIN: {
 
         binmode STDIN;
         if (crypt_file(\*STDIN, $out_file, $crypt_mode)) {
-            unless ($silent) {
-                print STDERR "OK";
-
-                # There may be a message left in $ErrStr even after crypt_file()
-                # completes successfully, so output that too if there is.
-                print STDERR " ($ErrStr)" if $ErrStr ne '';
-
-                print STDERR "\n";
-            }
+            show_result() unless $silent;
         }
         else {
             show_warning($silent, WARNING_TYPE_SEVERE, '-',
@@ -273,7 +266,10 @@ MAIN: {
                         ($temp_fh, $temp_file) =
                             File::Temp::tempfile("$file.XXXXXXXX");
 
-                        unless (crypt_file($file, $temp_fh, $crypt_mode)) {
+                        my $num_bytes =
+                            crypt_file($file, $temp_fh, $crypt_mode);
+
+                        unless (defined $num_bytes) {
                             show_warning($silent, WARNING_TYPE_SEVERE, $file,
                                 "crypt_file() failed: %s", $ErrStr
                             );
@@ -289,6 +285,23 @@ MAIN: {
                             );
                         }
 
+                        # If no output was written because the file was already
+                        # in the requested state then just remove the temporary
+                        # file; there is nothing more to do.
+                        if ($num_bytes == 0 and
+                                ($crypt_mode == CRYPT_MODE_ENCRYPTED or
+                                $crypt_mode == CRYPT_MODE_DECRYPTED))
+                        {
+                            show_result() unless $silent;
+                            unless (unlink $temp_file) {
+                                show_warning($silent, WARNING_TYPE_NORMAL,
+                                    $file, "Can't delete temporary file " .
+                                    "'%s': %s", $temp_file, $!
+                                );
+                            }
+                            next;
+                        }
+
                         # Get the input file's permissions and set them on the
                         # temporary file, so that when it is renamed to the
                         # input file the new input file has the same permissions
@@ -356,15 +369,7 @@ MAIN: {
                 }
             }
 
-            unless ($silent) {
-                print STDERR "OK";
-
-                # There may be a message left in $ErrStr even after crypt_file()
-                # completes successfully, so output that too if there is.
-                print STDERR " ($ErrStr)" if $ErrStr ne '';
-
-                print STDERR "\n";
-            }
+            show_result() unless $silent;
         }
     }
 
@@ -474,13 +479,23 @@ sub resolve_file_expr($$) {
     return $new_file;
 }
 
+sub show_result() {
+    print STDERR "OK";
+
+    # There may be a message left in $ErrStr even after crypt_file() completes
+    # successfully, so output that too if there is.
+    print STDERR " ($ErrStr)" if $ErrStr ne '';
+
+    print STDERR "\n";
+}
+
 sub show_warning($$$@) {
     my($silent, $type, $file, $msg) = splice @_, 0, 4;
 
     $msg = sprintf $msg, @_ if @_;
 
     my $hdr = '';
-    $hdr = "$file: " if $silent;
+    $hdr = "$file: " unless $silent;
     if ($type == WARNING_TYPE_NORMAL) {
         $hdr .= "Warning: ";
     }
@@ -945,9 +960,10 @@ given.
 =item Can't delete temporary file '%s': %s
 
 (W) The specified temporary file could not be deleted during the clean up of
-temporary files just before exiting when a SIGINT has been caught.  The system
-error message corresponding to the standard C library C<errno> variable is also
-given.
+temporary files just before exiting when a SIGINT has been caught, or when
+removing an unneeded temporary file when an input file was found to already be
+in the requested state.  The system error message corresponding to the standard
+C library C<errno> variable is also given.
 
 =item Can't open list file '%s' for reading: %s
 
@@ -1098,7 +1114,7 @@ Steve Hay E<lt>shay@cpan.orgE<gt>
 
 =head1 COPYRIGHT
 
-Copyright (C) 2004-2006, 2012 Steve Hay.  All rights reserved.
+Copyright (C) 2004-2006, 2012, 2014 Steve Hay.  All rights reserved.
 
 =head1 LICENCE
 
@@ -1108,11 +1124,11 @@ License or the Artistic License, as specified in the F<LICENCE> file.
 
 =head1 VERSION
 
-Version 1.03
+Version 1.04
 
 =head1 DATE
 
-20 Mar 2012
+19 Feb 2014
 
 =head1 HISTORY
 
@@ -7,7 +7,7 @@
 #   Test script to check crypt_file script (and decryption filter).
 #
 # COPYRIGHT
-#   Copyright (C) 2004-2007, 2009 Steve Hay.  All rights reserved.
+#   Copyright (C) 2004-2007, 2009, 2014 Steve Hay.  All rights reserved.
 #
 # LICENCE
 #   You may distribute under the terms of either the GNU General Public License
@@ -49,7 +49,7 @@ BEGIN {
     $lib_dir = catfile($top_dir, 'blib', 'lib', 'Filter', 'Crypto');
 
     if (-f catfile($lib_dir, 'CryptFile.pm')) {
-        plan tests => 99;
+        plan tests => 105;
     }
     else {
         plan skip_all => 'CryptFile component not built';
@@ -557,7 +557,21 @@ MAIN: {
     }
 
     qx{$perl $crypt_file -i -c encrypted $iofile 2>$null};
-    is($?, 0, 'crypt_file ran OK with -c encrypted option');
+    is($?, 0, 'crypt_file ran OK with -c encrypted option (working in memory)');
+
+    open $fh, $iofile or die "Can't read file '$iofile': $!\n";
+    $contents = do { local $/; <$fh> };
+    close $fh;
+    like($contents, $qrhead, '... and left file encrypted');
+
+    SKIP: {
+        skip 'Decrypt component not built', 1 unless $have_decrypt;
+        chomp($line = qx{$perl $iofile});
+        is($line, $str, '... and encrypted file still runs OK');
+    }
+
+    qx{$perl $crypt_file -i -e tempfile -c encrypted $iofile 2>$null};
+    is($?, 0, 'crypt_file ran OK with -c encrypted option (using a tempfile)');
 
     open $fh, $iofile or die "Can't read file '$iofile': $!\n";
     $contents = do { local $/; <$fh> };
@@ -571,7 +585,7 @@ MAIN: {
     }
 
     qx{$perl $crypt_file -i -c decrypt $iofile 2>$null};
-    is($?, 0, 'crypt_file ran OK with -c decrypt');
+    is($?, 0, 'crypt_file ran OK with -c decrypt option');
 
     open $fh, $iofile or die "Can't read file '$iofile': $!\n";
     $contents = do { local $/; <$fh> };
@@ -582,7 +596,18 @@ MAIN: {
     is($line, $str, '... and decrypted file runs OK');
 
     qx{$perl $crypt_file -i -c decrypted $iofile 2>$null};
-    is($?, 0, 'crypt_file ran OK with -c decrypted');
+    is($?, 0, 'crypt_file ran OK with -c decrypted option (working in memory)');
+
+    open $fh, $iofile or die "Can't read file '$iofile': $!\n";
+    $contents = do { local $/; <$fh> };
+    close $fh;
+    is($contents, $prog, '... and left file decrypted');
+
+    chomp($line = qx{$perl $iofile});
+    is($line, $str, '... and decrypted file still runs OK');
+
+    qx{$perl $crypt_file -i -e tempfile -c decrypted $iofile 2>$null};
+    is($?, 0, 'crypt_file ran OK with -c decrypted option (using a tempfile)');
 
     open $fh, $iofile or die "Can't read file '$iofile': $!\n";
     $contents = do { local $/; <$fh> };