2 # $Revision: 1.104.2.1 $
4 # Copyright (c) 2000-2002 Ned Konz. All rights reserved. This program is free
5 # software; you can redistribute it and/or modify it under the same terms as
8 # ----------------------------------------------------------------------
10 # Note that the package Archive::Zip exists only for exporting and
11 # sharing constants. Everything else is in another package
13 # Creation of a new Archive::Zip object actually creates a new object
14 # of class Archive::Zip::Archive.
15 # ----------------------------------------------------------------------
25 use File
::Spec
0.8 ();
28 # use sigtrap qw(die normal-signals); # is this needed?
30 use vars
qw( @ISA @EXPORT_OK %EXPORT_TAGS $VERSION $ChunkSize $ErrorHandler );
32 # This is the size we'll try to read, write, and (de)compress.
33 # You could set it to something different if you had lots of memory
34 # and needed more speed.
37 $ErrorHandler = \&Carp::carp;
39 # BEGIN block is necessary here so that other modules can use the constants.
45 @ISA = qw( Exporter );
47 my @ConstantNames = qw( FA_MSDOS FA_UNIX GPBF_ENCRYPTED_MASK
48 GPBF_DEFLATING_COMPRESSION_MASK GPBF_HAS_DATA_DESCRIPTOR_MASK
49 COMPRESSION_STORED COMPRESSION_DEFLATED COMPRESSION_LEVEL_NONE
50 COMPRESSION_LEVEL_DEFAULT COMPRESSION_LEVEL_FASTEST
51 COMPRESSION_LEVEL_BEST_COMPRESSION IFA_TEXT_FILE_MASK IFA_TEXT_FILE
54 my @MiscConstantNames = qw( FA_AMIGA FA_VAX_VMS FA_VM_CMS FA_ATARI_ST
55 FA_OS2_HPFS FA_MACINTOSH FA_Z_SYSTEM FA_CPM FA_TOPS20
56 FA_WINDOWS_NTFS FA_QDOS FA_ACORN FA_VFAT FA_MVS FA_BEOS FA_TANDEM
57 FA_THEOS GPBF_IMPLODING_8K_SLIDING_DICTIONARY_MASK
58 GPBF_IMPLODING_3_SHANNON_FANO_TREES_MASK
59 GPBF_IS_COMPRESSED_PATCHED_DATA_MASK COMPRESSION_SHRUNK
60 DEFLATING_COMPRESSION_NORMAL DEFLATING_COMPRESSION_MAXIMUM
61 DEFLATING_COMPRESSION_FAST DEFLATING_COMPRESSION_SUPER_FAST
62 COMPRESSION_REDUCED_1 COMPRESSION_REDUCED_2 COMPRESSION_REDUCED_3
63 COMPRESSION_REDUCED_4 COMPRESSION_IMPLODED COMPRESSION_TOKENIZED
64 COMPRESSION_DEFLATED_ENHANCED
65 COMPRESSION_PKWARE_DATA_COMPRESSION_LIBRARY_IMPLODED );
67 my @ErrorCodeNames = qw( AZ_OK AZ_STREAM_END AZ_ERROR AZ_FORMAT_ERROR
70 my @PKZipConstantNames = qw( SIGNATURE_FORMAT SIGNATURE_LENGTH
71 LOCAL_FILE_HEADER_SIGNATURE LOCAL_FILE_HEADER_FORMAT
72 LOCAL_FILE_HEADER_LENGTH CENTRAL_DIRECTORY_FILE_HEADER_SIGNATURE
73 DATA_DESCRIPTOR_FORMAT DATA_DESCRIPTOR_LENGTH DATA_DESCRIPTOR_SIGNATURE
74 DATA_DESCRIPTOR_FORMAT_NO_SIG DATA_DESCRIPTOR_LENGTH_NO_SIG
75 CENTRAL_DIRECTORY_FILE_HEADER_FORMAT CENTRAL_DIRECTORY_FILE_HEADER_LENGTH
76 END_OF_CENTRAL_DIRECTORY_SIGNATURE END_OF_CENTRAL_DIRECTORY_SIGNATURE_STRING
77 END_OF_CENTRAL_DIRECTORY_FORMAT END_OF_CENTRAL_DIRECTORY_LENGTH );
79 my @UtilityMethodNames = qw( _error _printError _ioError _formatError
80 _subclassResponsibility _binmode _isSeekable _newFileHandle _readSignature
83 @EXPORT_OK = ('computeCRC32');
85 'CONSTANTS' => \
@ConstantNames,
86 'MISC_CONSTANTS' => \
@MiscConstantNames,
87 'ERROR_CODES' => \
@ErrorCodeNames,
89 # The following two sets are for internal use only
90 'PKZIP_CONSTANTS' => \
@PKZipConstantNames,
91 'UTILITY_METHODS' => \
@UtilityMethodNames
94 # Add all the constant names and error code names to @EXPORT_OK
95 Exporter
::export_ok_tags
(
96 'CONSTANTS', 'ERROR_CODES',
97 'PKZIP_CONSTANTS', 'UTILITY_METHODS',
102 # ------------------------- begin exportable error codes -------------------
104 use constant AZ_OK
=> 0;
105 use constant AZ_STREAM_END
=> 1;
106 use constant AZ_ERROR
=> 2;
107 use constant AZ_FORMAT_ERROR
=> 3;
108 use constant AZ_IO_ERROR
=> 4;
110 # ------------------------- end exportable error codes ---------------------
111 # ------------------------- begin exportable constants ---------------------
114 # Values of Archive::Zip::Member->fileAttributeFormat()
116 use constant FA_MSDOS
=> 0;
117 use constant FA_AMIGA
=> 1;
118 use constant FA_VAX_VMS
=> 2;
119 use constant FA_UNIX
=> 3;
120 use constant FA_VM_CMS
=> 4;
121 use constant FA_ATARI_ST
=> 5;
122 use constant FA_OS2_HPFS
=> 6;
123 use constant FA_MACINTOSH
=> 7;
124 use constant FA_Z_SYSTEM
=> 8;
125 use constant FA_CPM
=> 9;
126 use constant FA_TOPS20
=> 10;
127 use constant FA_WINDOWS_NTFS
=> 11;
128 use constant FA_QDOS
=> 12;
129 use constant FA_ACORN
=> 13;
130 use constant FA_VFAT
=> 14;
131 use constant FA_MVS
=> 15;
132 use constant FA_BEOS
=> 16;
133 use constant FA_TANDEM
=> 17;
134 use constant FA_THEOS
=> 18;
136 # general-purpose bit flag masks
137 # Found in Archive::Zip::Member->bitFlag()
139 use constant GPBF_ENCRYPTED_MASK
=> 1 << 0;
140 use constant GPBF_DEFLATING_COMPRESSION_MASK
=> 3 << 1;
141 use constant GPBF_HAS_DATA_DESCRIPTOR_MASK
=> 1 << 3;
143 # deflating compression types, if compressionMethod == COMPRESSION_DEFLATED
144 # ( Archive::Zip::Member->bitFlag() & GPBF_DEFLATING_COMPRESSION_MASK )
146 use constant DEFLATING_COMPRESSION_NORMAL
=> 0 << 1;
147 use constant DEFLATING_COMPRESSION_MAXIMUM
=> 1 << 1;
148 use constant DEFLATING_COMPRESSION_FAST
=> 2 << 1;
149 use constant DEFLATING_COMPRESSION_SUPER_FAST
=> 3 << 1;
153 # these two are the only ones supported in this module
154 use constant COMPRESSION_STORED
=> 0; # file is stored (no compression)
155 use constant COMPRESSION_DEFLATED
=> 8; # file is Deflated
157 use constant COMPRESSION_LEVEL_NONE
=> 0;
158 use constant COMPRESSION_LEVEL_DEFAULT
=> -1;
159 use constant COMPRESSION_LEVEL_FASTEST
=> 1;
160 use constant COMPRESSION_LEVEL_BEST_COMPRESSION
=> 9;
162 # internal file attribute bits
163 # Found in Archive::Zip::Member::internalFileAttributes()
165 use constant IFA_TEXT_FILE_MASK
=> 1;
166 use constant IFA_TEXT_FILE
=> 1; # file is apparently text
167 use constant IFA_BINARY_FILE
=> 0;
169 # PKZIP file format miscellaneous constants (for internal use only)
170 use constant SIGNATURE_FORMAT
=> "V";
171 use constant SIGNATURE_LENGTH
=> 4;
173 # these lengths are without the signature.
174 use constant LOCAL_FILE_HEADER_SIGNATURE
=> 0x04034b50;
175 use constant LOCAL_FILE_HEADER_FORMAT
=> "v3 V4 v2";
176 use constant LOCAL_FILE_HEADER_LENGTH
=> 26;
178 # PKZIP docs don't mention the signature, but Info-Zip writes it.
179 use constant DATA_DESCRIPTOR_SIGNATURE
=> 0x08074b50;
180 use constant DATA_DESCRIPTOR_FORMAT
=> "V3";
181 use constant DATA_DESCRIPTOR_LENGTH
=> 12;
183 # but the signature is apparently optional.
184 use constant DATA_DESCRIPTOR_FORMAT_NO_SIG
=> "V2";
185 use constant DATA_DESCRIPTOR_LENGTH_NO_SIG
=> 8;
187 use constant CENTRAL_DIRECTORY_FILE_HEADER_SIGNATURE
=> 0x02014b50;
188 use constant CENTRAL_DIRECTORY_FILE_HEADER_FORMAT
=> "C2 v3 V4 v5 V2";
189 use constant CENTRAL_DIRECTORY_FILE_HEADER_LENGTH
=> 42;
191 use constant END_OF_CENTRAL_DIRECTORY_SIGNATURE
=> 0x06054b50;
192 use constant END_OF_CENTRAL_DIRECTORY_SIGNATURE_STRING
=>
193 pack( "V", END_OF_CENTRAL_DIRECTORY_SIGNATURE
);
194 use constant END_OF_CENTRAL_DIRECTORY_FORMAT
=> "v4 V2 v";
195 use constant END_OF_CENTRAL_DIRECTORY_LENGTH
=> 18;
197 use constant GPBF_IMPLODING_8K_SLIDING_DICTIONARY_MASK
=> 1 << 1;
198 use constant GPBF_IMPLODING_3_SHANNON_FANO_TREES_MASK
=> 1 << 2;
199 use constant GPBF_IS_COMPRESSED_PATCHED_DATA_MASK
=> 1 << 5;
201 # the rest of these are not supported in this module
202 use constant COMPRESSION_SHRUNK
=> 1; # file is Shrunk
203 use constant COMPRESSION_REDUCED_1
=> 2; # file is Reduced CF=1
204 use constant COMPRESSION_REDUCED_2
=> 3; # file is Reduced CF=2
205 use constant COMPRESSION_REDUCED_3
=> 4; # file is Reduced CF=3
206 use constant COMPRESSION_REDUCED_4
=> 5; # file is Reduced CF=4
207 use constant COMPRESSION_IMPLODED
=> 6; # file is Imploded
208 use constant COMPRESSION_TOKENIZED
=> 7; # reserved for Tokenizing compr.
209 use constant COMPRESSION_DEFLATED_ENHANCED
=> 9; # reserved for enh. Deflating
210 use constant COMPRESSION_PKWARE_DATA_COMPRESSION_LIBRARY_IMPLODED
=> 10;
212 # ------------------------- end of exportable constants ---------------------
214 use constant ZIPARCHIVECLASS
=> 'Archive::Zip::Archive';
215 use constant ZIPMEMBERCLASS
=> 'Archive::Zip::Member';
217 sub new
# Archive::Zip
220 return $class->ZIPARCHIVECLASS->new(@_);
223 sub computeCRC32
# Archive::Zip
226 $data = shift if ref($data); # allow calling as an obj method
228 return Compress
::Zlib
::crc32
( $data, $crc );
231 # Report or change chunk size used for reading and writing.
232 # Also sets Zlib's default buffer size (eventually).
233 sub setChunkSize
# Archive::Zip
235 my $chunkSize = shift;
236 $chunkSize = shift if ref($chunkSize); # object method on zip?
237 my $oldChunkSize = $Archive::Zip
::ChunkSize
;
238 $Archive::Zip
::ChunkSize
= $chunkSize if ($chunkSize);
239 return $oldChunkSize;
242 sub chunkSize
# Archive::Zip
244 return $Archive::Zip
::ChunkSize
;
247 sub setErrorHandler
(&) # Archive::Zip
249 my $errorHandler = shift;
250 $errorHandler = \
&Carp
::carp
unless defined($errorHandler);
251 my $oldErrorHandler = $Archive::Zip
::ErrorHandler
;
252 $Archive::Zip
::ErrorHandler
= $errorHandler;
253 return $oldErrorHandler;
256 # ----------------------------------------------------------------------
257 # Private utility functions (not methods).
258 # ----------------------------------------------------------------------
260 sub _printError
# Archive::Zip
262 my $string = join ( ' ', @_, "\n" );
263 my $oldCarpLevel = $Carp::CarpLevel
;
264 $Carp::CarpLevel
+= 2;
265 &{$ErrorHandler} ($string);
266 $Carp::CarpLevel
= $oldCarpLevel;
269 # This is called on format errors.
270 sub _formatError
# Archive::Zip
272 shift if ref( $_[0] );
273 _printError
( 'format error:', @_ );
274 return AZ_FORMAT_ERROR
;
277 # This is called on IO errors.
278 sub _ioError
# Archive::Zip
280 shift if ref( $_[0] );
281 _printError
( 'IO error:', @_, ':', $! );
285 # This is called on generic errors.
286 sub _error
# Archive::Zip
288 shift if ref( $_[0] );
289 _printError
( 'error:', @_ );
293 # Called when a subclass should have implemented
294 # something but didn't
295 sub _subclassResponsibility
# Archive::Zip
297 Carp
::croak
("subclass Responsibility\n");
300 # Try to set the given file handle or object into binary mode.
301 sub _binmode
# Archive::Zip
304 return UNIVERSAL
::can
( $fh, 'binmode' ) ?
$fh->binmode() : binmode($fh);
307 # Attempt to guess whether file handle is seekable.
308 # Because of problems with Windoze, this only returns true when
309 # the file handle is a real file.
310 sub _isSeekable
# Archive::Zip
314 if ( UNIVERSAL
::isa
( $fh, 'IO::Scalar' ) )
318 elsif ( UNIVERSAL
::isa
( $fh, 'IO::String' ) )
322 elsif ( UNIVERSAL
::can
( $fh, 'stat' ) )
326 return UNIVERSAL
::can
( $fh, 'seek' );
329 # Return an opened IO::Handle
330 # my ( $status, fh ) = _newFileHandle( 'fileName', 'w' );
331 # Can take a filename, file handle, or ref to GLOB
332 # Or, if given something that is a ref but not an IO::Handle,
333 # passes back the same thing.
334 sub _newFileHandle
# Archive::Zip
342 if ( UNIVERSAL
::isa
( $fd, 'IO::Scalar' )
343 or UNIVERSAL
::isa
( $fd, 'IO::String' ) )
347 elsif ( UNIVERSAL
::isa
( $fd, 'IO::Handle' )
348 or UNIVERSAL
::isa
( $fd, 'GLOB' ) )
350 $handle = IO
::File
->new();
351 $status = $handle->fdopen( $fd, @_ );
360 $handle = IO
::File
->new();
361 $status = $handle->open( $fd, @_ );
364 return ( $status, $handle );
367 # Returns next signature from given file handle, leaves
368 # file handle positioned afterwards.
369 # In list context, returns ($status, $signature)
370 # ( $status, $signature) = _readSignature( $fh, $fileName );
372 sub _readSignature
# Archive::Zip
375 my $fileName = shift;
376 my $expectedSignature = shift; # optional
379 my $bytesRead = $fh->read( $signatureData, SIGNATURE_LENGTH
);
380 return _ioError
("reading header signature")
381 if $bytesRead != SIGNATURE_LENGTH
;
382 my $signature = unpack( SIGNATURE_FORMAT
, $signatureData );
385 # compare with expected signature, if any, or any known signature.
386 if ( ( defined($expectedSignature) && $signature != $expectedSignature )
387 || ( !defined($expectedSignature)
388 && $signature != CENTRAL_DIRECTORY_FILE_HEADER_SIGNATURE
389 && $signature != LOCAL_FILE_HEADER_SIGNATURE
390 && $signature != END_OF_CENTRAL_DIRECTORY_SIGNATURE
391 && $signature != DATA_DESCRIPTOR_SIGNATURE
) )
393 my $errmsg = sprintf( "bad signature: 0x%08x", $signature );
394 if ( _isSeekable
($fh) )
397 sprintf( " at offset %d", $fh->tell() - SIGNATURE_LENGTH
);
400 $status = _formatError
("$errmsg in file $fileName");
403 return ( $status, $signature );
406 # Utility method to make and open a temp file.
407 # Will create $temp_dir if it doesn't exist.
408 # Returns file handle and name:
410 # my ($fh, $name) = Archive::Zip::tempFile();
411 # my ($fh, $name) = Archive::Zip::tempFile('mytempdir');
414 sub tempFile
# Archive::Zip
417 my ( $fh, $filename ) = File
::Temp
::tempfile
(
419 UNLINK
=> 0, # we will delete it!
420 $dir ?
( DIR
=> $dir ) : ()
422 return ( undef, undef ) unless $fh;
423 my ( $status, $newfh ) = _newFileHandle
( $fh, 'w+' );
424 return ( $newfh, $filename );
427 # Return the normalized directory name as used in a zip file (path
428 # separators become slashes, etc.).
429 # Will translate internal slashes in path components (i.e. on Macs) to
430 # underscores. Discards volume names.
431 # When $forceDir is set, returns paths with trailing slashes (or arrays
432 # with trailing blank members).
434 # If third argument is a reference, returns volume information there.
439 # ./a/b ('a','b') a/b
440 # ./a/b/ ('a','b') a/b
442 # /a/b/ ('','a','b') /a/b
443 # c:\a\b\c.doc ('','a','b','c.doc') /a/b/c.doc # on Windoze
444 # "i/o maps:whatever" ('i_o maps', 'whatever') "i_o maps/whatever" # on Macs
445 sub _asZipDirName
# Archive::Zip
448 my $forceDir = shift;
449 my $volReturn = shift;
450 my ( $volume, $directories, $file ) =
451 File
::Spec
->splitpath( File
::Spec
->canonpath($name), $forceDir );
452 $$volReturn = $volume if ( ref($volReturn) );
453 my @dirs = map { $_ =~ s{/}{_}g; $_ } File
::Spec
->splitdir($directories);
454 if ( @dirs > 0 ) { pop (@dirs) unless $dirs[-1] } # remove empty component
455 push ( @dirs, $file || '' );
456 #return wantarray ? @dirs : join ( '/', @dirs );
457 return join ( '/', @dirs );
460 # Return an absolute local name for a zip name.
461 # Assume a directory if zip name has trailing slash.
462 # Takes an optional volume name in FS format (like 'a:').
464 sub _asLocalName
# Archive::Zip
466 my $name = shift; # zip format
468 $volume = '' unless defined($volume); # local FS format
470 my @paths = split ( /\//, $name );
471 my $filename = pop (@paths);
472 $filename = '' unless defined($filename);
473 my $localDirs = File
::Spec
->catdir(@paths);
474 my $localName = File
::Spec
->catpath( $volume, $localDirs, $filename );
475 $localName = File
::Spec
->rel2abs($localName) unless $volume;
479 # ----------------------------------------------------------------------
480 # class Archive::Zip::Archive (concrete)
481 # Generic ZIP archive.
482 # ----------------------------------------------------------------------
483 package Archive
::Zip
::Archive
;
492 @ISA = qw( Archive::Zip );
496 use Archive
::Zip
qw( :CONSTANTS :ERROR_CODES :PKZIP_CONSTANTS
500 # Note that this returns undef on read errors, else new zip object.
502 sub new
# Archive::Zip::Archive
507 'diskNumberWithStartOfCentralDirectory' => 0,
508 'numberOfCentralDirectoriesOnThisDisk' => 0, # shld be # of members
509 'numberOfCentralDirectories' => 0, # shld be # of members
510 'centralDirectorySize' => 0, # must re-compute on write
511 'centralDirectoryOffsetWRTStartingDiskNumber' => 0, # must re-compute
512 'writeEOCDOffset' => 0,
513 'writeCentralDirectoryOffset' => 0,
514 'zipfileComment' => '',
520 $self->{'members'} = [];
523 my $status = $self->read(@_);
524 return $status == AZ_OK ?
$self : undef;
529 sub members
# Archive::Zip::Archive
531 @
{ shift->{'members'} };
534 sub numberOfMembers
# Archive::Zip::Archive
536 scalar( shift->members() );
539 sub memberNames
# Archive::Zip::Archive
542 return map { $_->fileName() } $self->members();
545 # return ref to member with given name or undef
546 sub memberNamed
# Archive::Zip::Archive
548 my ( $self, $fileName ) = @_;
549 foreach my $member ( $self->members() )
551 return $member if $member->fileName() eq $fileName;
556 sub membersMatching
# Archive::Zip::Archive
558 my ( $self, $pattern ) = @_;
559 return grep { $_->fileName() =~ /$pattern/ } $self->members();
562 sub diskNumber
# Archive::Zip::Archive
564 shift->{'diskNumber'};
567 sub diskNumberWithStartOfCentralDirectory
# Archive::Zip::Archive
569 shift->{'diskNumberWithStartOfCentralDirectory'};
572 sub numberOfCentralDirectoriesOnThisDisk
# Archive::Zip::Archive
574 shift->{'numberOfCentralDirectoriesOnThisDisk'};
577 sub numberOfCentralDirectories
# Archive::Zip::Archive
579 shift->{'numberOfCentralDirectories'};
582 sub centralDirectorySize
# Archive::Zip::Archive
584 shift->{'centralDirectorySize'};
587 sub centralDirectoryOffsetWRTStartingDiskNumber
# Archive::Zip::Archive
589 shift->{'centralDirectoryOffsetWRTStartingDiskNumber'};
592 sub zipfileComment
# Archive::Zip::Archive
595 my $comment = $self->{'zipfileComment'};
598 $self->{'zipfileComment'} = pack( 'C0a*', shift () ); # avoid unicode
603 sub eocdOffset
# Archive::Zip::Archive
605 shift->{'eocdOffset'};
608 # Return the name of the file last read.
609 sub fileName
# Archive::Zip::Archive
614 sub removeMember
# Archive::Zip::Archive
616 my ( $self, $member ) = @_;
617 $member = $self->memberNamed($member) unless ref($member);
618 return undef unless $member;
619 my @newMembers = grep { $_ != $member } $self->members();
620 $self->{'members'} = \
@newMembers;
624 sub replaceMember
# Archive::Zip::Archive
626 my ( $self, $oldMember, $newMember ) = @_;
627 $oldMember = $self->memberNamed($oldMember) unless ref($oldMember);
628 return undef unless $oldMember;
629 return undef unless $newMember;
631 map { ( $_ == $oldMember ) ?
$newMember : $_ } $self->members();
632 $self->{'members'} = \
@newMembers;
636 sub extractMember
# Archive::Zip::Archive
640 $member = $self->memberNamed($member) unless ref($member);
641 return _error
('member not found') unless $member;
642 my $originalSize = $member->compressedSize();
643 my $name = shift; # local FS name if given
644 my ( $volumeName, $dirName, $fileName );
645 if ( defined($name) )
647 ( $volumeName, $dirName, $fileName ) = File
::Spec
->splitpath($name);
648 $dirName = File
::Spec
->catpath( $volumeName, $dirName, '' );
652 $name = $member->fileName();
653 ( $dirName = $name ) =~ s{[^/]*$}{};
654 $dirName = Archive
::Zip
::_asLocalName
($dirName);
655 $name = Archive
::Zip
::_asLocalName
($name);
657 if ( $dirName && !-d
$dirName )
660 return _ioError
("can't create dir $dirName") if ( !-d
$dirName );
662 my $rc = $member->extractToFileNamed( $name, @_ );
663 # TODO refactor this fix into extractToFileNamed()
664 $member->{'compressedSize'} = $originalSize;
668 sub extractMemberWithoutPaths
# Archive::Zip::Archive
672 $member = $self->memberNamed($member) unless ref($member);
673 return _error
('member not found') unless $member;
674 my $originalSize = $member->compressedSize();
675 return AZ_OK
if $member->isDirectory();
679 $name = $member->fileName();
680 $name =~ s{.*/}{}; # strip off directories, if any
681 $name = Archive
::Zip
::_asLocalName
($name);
683 my $rc = $member->extractToFileNamed( $name, @_ );
684 $member->{'compressedSize'} = $originalSize;
688 sub addMember
# Archive::Zip::Archive
690 my ( $self, $newMember ) = @_;
691 push ( @
{ $self->{'members'} }, $newMember ) if $newMember;
695 sub addFile
# Archive::Zip::Archive
698 my $fileName = shift;
700 my $newMember = $self->ZIPMEMBERCLASS->newFromFile( $fileName, $newName );
701 $self->addMember($newMember) if defined($newMember);
705 sub addString
# Archive::Zip::Archive
708 my $newMember = $self->ZIPMEMBERCLASS->newFromString(@_);
709 return $self->addMember($newMember);
712 sub addDirectory
# Archive::Zip::Archive
714 my ( $self, $name, $newName ) = @_;
715 my $newMember = $self->ZIPMEMBERCLASS->newDirectoryNamed( $name, $newName );
716 $self->addMember($newMember);
720 # add either a file or a directory.
722 sub addFileOrDirectory
724 my ( $self, $name, $newName ) = @_;
727 ( $newName =~ s{/$}{} ) if $newName;
728 return $self->addFile( $name, $newName );
732 ( $newName =~ s{[^/]$}{&/} ) if $newName;
733 return $self->addDirectory( $name, $newName );
737 return _error
("$name is neither a file nor a directory");
741 sub contents
# Archive::Zip::Archive
743 my ( $self, $member, $newContents ) = @_;
744 $member = $self->memberNamed($member) unless ref($member);
745 return undef unless $member;
746 return $member->contents($newContents);
749 sub writeToFileNamed
# Archive::Zip::Archive
752 my $fileName = shift; # local FS format
753 foreach my $member ( $self->members() )
755 if ( $member->_usesFileNamed($fileName) )
757 return _error
( "$fileName is needed by member "
758 . $member->fileName()
759 . "; consider using overwrite() or overwriteAs() instead." );
762 my ( $status, $fh ) = _newFileHandle
( $fileName, 'w' );
763 return _ioError
("Can't open $fileName for write") unless $status;
764 my $retval = $self->writeToFileHandle( $fh, 1 );
771 # It is possible to write data to the FH before calling this,
772 # perhaps to make a self-extracting archive.
773 sub writeToFileHandle
# Archive::Zip::Archive
777 return _error
('No filehandle given') unless $fh;
778 return _ioError
('filehandle not open') unless $fh->opened();
780 my $fhIsSeekable = @_ ?
shift: _isSeekable
($fh);
783 # Find out where the current position is.
784 my $offset = $fhIsSeekable ?
$fh->tell() : 0;
785 $offset = 0 if $offset < 0;
787 foreach my $member ( $self->members() )
789 my $retval = $member->_writeToFileHandle( $fh, $fhIsSeekable, $offset );
791 return $retval if $retval != AZ_OK
;
792 $offset += $member->_localHeaderSize() + $member->_writeOffset();
793 $offset += $member->hasDataDescriptor()
794 ? DATA_DESCRIPTOR_LENGTH
+ SIGNATURE_LENGTH
797 # changed this so it reflects the last successful position
798 $self->{'writeCentralDirectoryOffset'} = $offset;
800 return $self->writeCentralDirectory($fh);
803 # Write zip back to the original file,
804 # as safely as possible.
805 # Returns AZ_OK if successful.
806 sub overwrite
# Archive::Zip::Archive
809 return $self->overwriteAs( $self->{'fileName'} );
812 # Write zip to the specified file,
813 # as safely as possible.
814 # Returns AZ_OK if successful.
815 sub overwriteAs
# Archive::Zip::Archive
819 return _error
("no filename in overwriteAs()") unless defined($zipName);
821 my ( $fh, $tempName ) = Archive
::Zip
::tempFile
();
822 return _error
( "Can't open temp file", $! ) unless $fh;
824 ( my $backupName = $zipName ) =~ s{(\.[^.]*)?$}{.zbk};
826 my $status = $self->writeToFileHandle($fh);
830 if ( $status != AZ_OK
)
833 _printError
("Can't write to $tempName");
840 if ( -f
$zipName && !rename( $zipName, $backupName ) )
844 return _error
( "Can't rename $zipName as $backupName", $err );
847 # move the temp to the original name (possibly copying)
848 unless ( File
::Copy
::move
( $tempName, $zipName ) )
851 rename( $backupName, $zipName );
853 return _error
( "Can't move $tempName to $zipName", $err );
857 if ( -f
$backupName && !unlink($backupName) )
860 return _error
( "Can't unlink $backupName", $err );
866 # Used only during writing
867 sub _writeCentralDirectoryOffset
# Archive::Zip::Archive
869 shift->{'writeCentralDirectoryOffset'};
872 sub _writeEOCDOffset
# Archive::Zip::Archive
874 shift->{'writeEOCDOffset'};
877 # Expects to have _writeEOCDOffset() set
878 sub _writeEndOfCentralDirectory
# Archive::Zip::Archive
880 my ( $self, $fh ) = @_;
882 $fh->print(END_OF_CENTRAL_DIRECTORY_SIGNATURE_STRING
)
883 or return _ioError
('writing EOCD Signature');
884 my $zipfileCommentLength = length( $self->zipfileComment() );
887 END_OF_CENTRAL_DIRECTORY_FORMAT
,
889 0, # {'diskNumberWithStartOfCentralDirectory'},
890 $self->numberOfMembers(), # {'numberOfCentralDirectoriesOnThisDisk'},
891 $self->numberOfMembers(), # {'numberOfCentralDirectories'},
892 $self->_writeEOCDOffset() - $self->_writeCentralDirectoryOffset(),
893 $self->_writeCentralDirectoryOffset(),
894 $zipfileCommentLength
897 or return _ioError
('writing EOCD header');
898 if ($zipfileCommentLength)
900 $fh->print( $self->zipfileComment() )
901 or return _ioError
('writing zipfile comment');
906 # $offset can be specified to truncate a zip file.
907 sub writeCentralDirectory
# Archive::Zip::Archive
909 my ( $self, $fh, $offset ) = @_;
911 if ( defined($offset) )
913 $self->{'writeCentralDirectoryOffset'} = $offset;
914 $fh->seek( $offset, IO
::Seekable
::SEEK_SET
)
915 or return _ioError
('seeking to write central directory');
919 $offset = $self->_writeCentralDirectoryOffset();
922 foreach my $member ( $self->members() )
924 my $status = $member->_writeCentralDirectoryFileHeader($fh);
925 return $status if $status != AZ_OK
;
926 $offset += $member->_centralDirectoryHeaderSize();
927 $self->{'writeEOCDOffset'} = $offset;
929 return $self->_writeEndOfCentralDirectory($fh);
932 sub read # Archive::Zip::Archive
935 my $fileName = shift;
936 return _error
('No filename given') unless $fileName;
937 my ( $status, $fh ) = _newFileHandle
( $fileName, 'r' );
938 return _ioError
("opening $fileName for read") unless $status;
940 $status = $self->readFromFileHandle( $fh, $fileName );
941 return $status if $status != AZ_OK
;
944 $self->{'fileName'} = $fileName;
948 sub readFromFileHandle
# Archive::Zip::Archive
952 my $fileName = shift;
953 $fileName = $fh unless defined($fileName);
954 return _error
('No filehandle given') unless $fh;
955 return _ioError
('filehandle not open') unless $fh->opened();
958 $self->{'fileName'} = "$fh";
960 # TODO: how to support non-seekable zips?
961 return _error
('file not seekable')
962 unless _isSeekable
($fh);
964 $fh->seek( 0, 0 ); # rewind the file
966 my $status = $self->_findEndOfCentralDirectory($fh);
967 return $status if $status != AZ_OK
;
969 my $eocdPosition = $fh->tell();
971 $status = $self->_readEndOfCentralDirectory($fh);
972 return $status if $status != AZ_OK
;
974 $fh->seek( $eocdPosition - $self->centralDirectorySize(),
975 IO
::Seekable
::SEEK_SET
)
976 or return _ioError
("Can't seek $fileName");
978 # Try to detect garbage at beginning of archives
980 $self->{'eocdOffset'} = $eocdPosition - $self->centralDirectorySize() # here
981 - $self->centralDirectoryOffsetWRTStartingDiskNumber();
986 $self->ZIPMEMBERCLASS->_newFromZipFile( $fh, $fileName,
987 $self->eocdOffset() );
989 ( $status, $signature ) = _readSignature
( $fh, $fileName );
990 return $status if $status != AZ_OK
;
991 last if $signature == END_OF_CENTRAL_DIRECTORY_SIGNATURE
;
992 $status = $newMember->_readCentralDirectoryFileHeader();
993 return $status if $status != AZ_OK
;
994 $status = $newMember->endRead();
995 return $status if $status != AZ_OK
;
996 $newMember->_becomeDirectoryIfNecessary();
997 push ( @
{ $self->{'members'} }, $newMember );
1003 # Read EOCD, starting from position before signature.
1004 # Return AZ_OK on success.
1005 sub _readEndOfCentralDirectory
# Archive::Zip::Archive
1010 # Skip past signature
1011 $fh->seek( SIGNATURE_LENGTH
, IO
::Seekable
::SEEK_CUR
)
1012 or return _ioError
("Can't seek past EOCD signature");
1015 my $bytesRead = $fh->read( $header, END_OF_CENTRAL_DIRECTORY_LENGTH
);
1016 if ( $bytesRead != END_OF_CENTRAL_DIRECTORY_LENGTH
)
1018 return _ioError
("reading end of central directory");
1021 my $zipfileCommentLength;
1022 ( $self->{'diskNumber'},
1023 $self->{'diskNumberWithStartOfCentralDirectory'},
1024 $self->{'numberOfCentralDirectoriesOnThisDisk'},
1025 $self->{'numberOfCentralDirectories'},
1026 $self->{'centralDirectorySize'},
1027 $self->{'centralDirectoryOffsetWRTStartingDiskNumber'},
1028 $zipfileCommentLength )
1029 = unpack( END_OF_CENTRAL_DIRECTORY_FORMAT
, $header );
1031 if ($zipfileCommentLength)
1033 my $zipfileComment = '';
1034 $bytesRead = $fh->read( $zipfileComment, $zipfileCommentLength );
1035 if ( $bytesRead != $zipfileCommentLength )
1037 return _ioError
("reading zipfile comment");
1039 $self->{'zipfileComment'} = $zipfileComment;
1045 # Seek in my file to the end, then read backwards until we find the
1046 # signature of the central directory record. Leave the file positioned right
1047 # before the signature. Returns AZ_OK if success.
1048 sub _findEndOfCentralDirectory
# Archive::Zip::Archive
1053 $fh->seek( 0, IO
::Seekable
::SEEK_END
)
1054 or return _ioError
("seeking to end");
1056 my $fileLength = $fh->tell();
1057 if ( $fileLength < END_OF_CENTRAL_DIRECTORY_LENGTH
+ 4 )
1059 return _formatError
("file is too short");
1067 $seekOffset = $fileLength if ( $seekOffset > $fileLength );
1068 $fh->seek( -$seekOffset, IO
::Seekable
::SEEK_END
)
1069 or return _ioError
("seek failed");
1070 my $bytesRead = $fh->read( $data, $seekOffset );
1071 if ( $bytesRead != $seekOffset )
1073 return _ioError
("read failed");
1075 $pos = rindex( $data, END_OF_CENTRAL_DIRECTORY_SIGNATURE_STRING
);
1078 or $seekOffset == $fileLength
1079 or $seekOffset >= $Archive::Zip
::ChunkSize
);
1084 $fh->seek( $pos - $seekOffset, IO
::Seekable
::SEEK_CUR
)
1085 or return _ioError
("seeking to EOCD");
1090 return _formatError
("can't find EOCD signature");
1094 # Used to avoid taint problems when chdir'ing.
1095 # Not intended to increase security in any way; just intended to shut up the -T
1096 # complaints. If your Cwd module is giving you unreliable returns from cwd()
1097 # you have bigger problems than this.
1101 $dir =~ m/\A(.+)\z/s;
1105 sub addTree
# Archive::Zip::Archive
1108 my $root = shift or return _error
("root arg missing in call to addTree()");
1110 $dest = '' unless defined($dest);
1111 my $pred = shift || sub { -r
};
1113 my $startDir = _untaintDir
( cwd
() );
1115 return _error
( 'undef returned by _untaintDir on cwd ', cwd
() )
1118 # This avoids chdir'ing in Find, in a way compatible with older
1119 # versions of File::Find.
1121 local $main::_
= $File::Find
::name
;
1122 my $dir = _untaintDir
($File::Find
::dir
);
1124 push ( @files, $File::Find
::name
) if (&$pred);
1128 File
::Find
::find
( $wanted, $root );
1130 my $rootZipName = _asZipDirName
( $root, 1 ); # with trailing slash
1131 my $pattern = $rootZipName eq './' ?
'^' : "^\Q$rootZipName\E";
1133 $dest = _asZipDirName
( $dest, 1 ); # with trailing slash
1135 foreach my $fileName (@files)
1137 my $isDir = -d
$fileName;
1139 # normalize, remove leading ./
1140 my $archiveName = _asZipDirName
( $fileName, $isDir );
1141 if ( $archiveName eq $rootZipName ) { $archiveName = $dest }
1142 else { $archiveName =~ s{$pattern}{$dest} }
1143 next if $archiveName =~ m{^\.?/?$}; # skip current dir
1146 ?
$self->addDirectory( $fileName, $archiveName )
1147 : $self->addFile( $fileName, $archiveName );
1148 return _error
("add $fileName failed in addTree()") if !$member;
1153 sub addTreeMatching
# Archive::Zip::Archive
1157 or return _error
("root arg missing in call to addTreeMatching()");
1159 $dest = '' unless defined($dest);
1161 or return _error
("pattern missing in call to addTreeMatching()");
1164 $pred ?
sub { m{$pattern} && &$pred } : sub { m{$pattern} && -r
};
1165 return $self->addTree( $root, $dest, $matcher );
1168 # $zip->extractTree( $root, $dest [, $volume] );
1170 # $root and $dest are Unix-style.
1171 # $volume is in local FS format.
1173 sub extractTree
# Archive::Zip::Archive
1176 my $root = shift; # Zip format
1177 $root = '' unless defined($root);
1178 my $dest = shift; # Zip format
1179 $dest = './' unless defined($dest);
1180 my $volume = shift; # optional
1181 my $pattern = "^\Q$root";
1182 my @members = $self->membersMatching($pattern);
1184 foreach my $member (@members)
1186 my $fileName = $member->fileName(); # in Unix format
1187 $fileName =~ s{$pattern}{$dest}; # in Unix format
1188 # convert to platform format:
1189 $fileName = Archive
::Zip
::_asLocalName
( $fileName, $volume );
1190 my $status = $member->extractToFileNamed($fileName);
1191 return $status if $status != AZ_OK
;
1196 # $zip->updateMember( $memberOrName, $fileName );
1197 # Returns (possibly updated) member, if any; undef on errors.
1199 sub updateMember
# Archive::Zip::Archive
1202 my $oldMember = shift;
1203 my $fileName = shift;
1205 if ( !defined($fileName) )
1207 _error
("updateMember(): missing fileName argument");
1211 my @newStat = stat($fileName);
1214 _ioError
("Can't stat $fileName");
1222 if ( ref($oldMember) )
1224 $memberName = $oldMember->fileName();
1228 $oldMember = $self->memberNamed( $memberName = $oldMember )
1229 || $self->memberNamed( $memberName =
1230 _asZipDirName
( $oldMember, $isDir ) );
1233 unless ( defined($oldMember)
1234 && $oldMember->lastModTime() == $newStat[9]
1235 && $oldMember->isDirectory() == $isDir
1236 && ( $isDir || ( $oldMember->uncompressedSize() == $newStat[7] ) ) )
1239 # create the new member
1240 my $newMember = $isDir
1241 ?
$self->ZIPMEMBERCLASS->newDirectoryNamed( $fileName, $memberName )
1242 : $self->ZIPMEMBERCLASS->newFromFile( $fileName, $memberName );
1244 unless ( defined($newMember) )
1246 _error
("creation of member $fileName failed in updateMember()");
1250 # replace old member or append new one
1251 if ( defined($oldMember) )
1253 $self->replaceMember( $oldMember, $newMember );
1255 else { $self->addMember($newMember); }
1263 # $zip->updateTree( $root, [ $dest, [ $pred [, $mirror]]] );
1265 # This takes the same arguments as addTree, but first checks to see
1266 # whether the file or directory already exists in the zip file.
1268 # If the fourth argument $mirror is true, then delete all my members
1269 # if corresponding files weren't found.
1271 sub updateTree
# Archive::Zip::Archive
1275 or return _error
("root arg missing in call to updateTree()");
1277 $dest = '' unless defined($dest);
1278 $dest = _asZipDirName
( $dest, 1 );
1279 my $pred = shift || sub { -r
};
1282 my $rootZipName = _asZipDirName
( $root, 1 ); # with trailing slash
1283 my $pattern = $rootZipName eq './' ?
'^' : "^\Q$rootZipName\E";
1286 my $startDir = _untaintDir
( cwd
() );
1288 return _error
( 'undef returned by _untaintDir on cwd ', cwd
() )
1291 # This avoids chdir'ing in Find, in a way compatible with older
1292 # versions of File::Find.
1294 local $main::_
= $File::Find
::name
;
1295 my $dir = _untaintDir
($File::Find
::dir
);
1297 push ( @files, $File::Find
::name
) if (&$pred);
1301 File
::Find
::find
( $wanted, $root );
1303 # Now @files has all the files that I could potentially be adding to
1304 # the zip. Only add the ones that are necessary.
1305 # For each file (updated or not), add its member name to @done.
1307 foreach my $fileName (@files)
1309 my @newStat = stat($fileName);
1312 # normalize, remove leading ./
1313 my $memberName = _asZipDirName
( $fileName, $isDir );
1314 if ( $memberName eq $rootZipName ) { $memberName = $dest }
1315 else { $memberName =~ s{$pattern}{$dest} }
1316 next if $memberName =~ m{^\.?/?$}; # skip current dir
1318 $done{$memberName} = 1;
1319 my $changedMember = $self->updateMember( $memberName, $fileName );
1320 return _error
("updateTree failed to update $fileName")
1321 unless ref($changedMember);
1324 # @done now has the archive names corresponding to all the found files.
1325 # If we're mirroring, delete all those members that aren't in @done.
1328 foreach my $member ( $self->members() )
1330 $self->removeMember($member)
1331 unless $done{ $member->fileName() };
1338 # ----------------------------------------------------------------------
1339 # class Archive::Zip::Member
1340 # A generic member of an archive ( abstract )
1341 # ----------------------------------------------------------------------
1342 package Archive
::Zip
::Member
;
1343 use vars
qw( @ISA );
1344 @ISA = qw ( Archive::Zip );
1348 use Archive::Zip qw( :CONSTANTS :MISC_CONSTANTS :ERROR_CODES
1349 :PKZIP_CONSTANTS :UTILITY_METHODS );
1353 use Compress
::Zlib
qw( Z_OK Z_STREAM_END MAX_WBITS );
1357 use constant ZIPFILEMEMBERCLASS
=> 'Archive::Zip::ZipFileMember';
1358 use constant NEWFILEMEMBERCLASS
=> 'Archive::Zip::NewFileMember';
1359 use constant STRINGMEMBERCLASS
=> 'Archive::Zip::StringMember';
1360 use constant DIRECTORYMEMBERCLASS
=> 'Archive::Zip::DirectoryMember';
1362 # Unix perms for default creation of files/dirs.
1363 use constant DEFAULT_DIRECTORY_PERMISSIONS
=> 040755;
1364 use constant DEFAULT_FILE_PERMISSIONS
=> 0100666;
1365 use constant DIRECTORY_ATTRIB
=> 040000;
1366 use constant FILE_ATTRIB
=> 0100000;
1368 # Returns self if successful, else undef
1369 # Assumes that fh is positioned at beginning of central directory file header.
1370 # Leaves fh positioned immediately after file header or EOCD signature.
1371 sub _newFromZipFile
# Archive::Zip::Member
1374 my $self = $class->ZIPFILEMEMBERCLASS->_newFromZipFile(@_);
1378 sub newFromString
# Archive::Zip::Member
1381 my $self = $class->STRINGMEMBERCLASS->_newFromString(@_);
1385 sub newFromFile
# Archive::Zip::Member
1388 my $self = $class->NEWFILEMEMBERCLASS->_newFromFileNamed(@_);
1392 sub newDirectoryNamed
# Archive::Zip::Member
1395 my $self = $class->DIRECTORYMEMBERCLASS->_newNamed(@_);
1399 sub new
# Archive::Zip::Member
1403 'lastModFileDateTime' => 0,
1404 'fileAttributeFormat' => FA_UNIX
,
1405 'versionMadeBy' => 20,
1406 'versionNeededToExtract' => 20,
1408 'compressionMethod' => COMPRESSION_STORED
,
1409 'desiredCompressionMethod' => COMPRESSION_STORED
,
1410 'desiredCompressionLevel' => COMPRESSION_LEVEL_NONE
,
1411 'internalFileAttributes' => 0,
1412 'externalFileAttributes' => 0, # set later
1414 'cdExtraField' => '',
1415 'localExtraField' => '',
1416 'fileComment' => '',
1418 'compressedSize' => 0,
1419 'uncompressedSize' => 0,
1422 bless( $self, $class );
1423 $self->unixFileAttributes( $self->DEFAULT_FILE_PERMISSIONS );
1427 sub _becomeDirectoryIfNecessary
# Archive::Zip::Member
1430 $self->_become(DIRECTORYMEMBERCLASS
)
1431 if $self->isDirectory();
1435 # Morph into given class (do whatever cleanup I need to do)
1436 sub _become
# Archive::Zip::Member
1438 return bless( $_[0], $_[1] );
1441 sub versionMadeBy
# Archive::Zip::Member
1443 shift->{'versionMadeBy'};
1446 sub fileAttributeFormat
# Archive::Zip::Member
1449 ?
( $_[0]->{'fileAttributeFormat'} = $_[1] )
1450 : $_[0]->{'fileAttributeFormat'};
1453 sub versionNeededToExtract
# Archive::Zip::Member
1455 shift->{'versionNeededToExtract'};
1458 sub bitFlag
# Archive::Zip::Member
1463 sub compressionMethod
# Archive::Zip::Member
1465 shift->{'compressionMethod'};
1468 sub desiredCompressionMethod
# Archive::Zip::Member
1471 my $newDesiredCompressionMethod = shift;
1472 my $oldDesiredCompressionMethod = $self->{'desiredCompressionMethod'};
1473 if ( defined($newDesiredCompressionMethod) )
1475 $self->{'desiredCompressionMethod'} = $newDesiredCompressionMethod;
1476 if ( $newDesiredCompressionMethod == COMPRESSION_STORED
)
1478 $self->{'desiredCompressionLevel'} = 0;
1480 elsif ( $oldDesiredCompressionMethod == COMPRESSION_STORED
)
1482 $self->{'desiredCompressionLevel'} = COMPRESSION_LEVEL_DEFAULT
;
1485 return $oldDesiredCompressionMethod;
1488 sub desiredCompressionLevel
# Archive::Zip::Member
1491 my $newDesiredCompressionLevel = shift;
1492 my $oldDesiredCompressionLevel = $self->{'desiredCompressionLevel'};
1493 if ( defined($newDesiredCompressionLevel) )
1495 $self->{'desiredCompressionLevel'} = $newDesiredCompressionLevel;
1496 $self->{'desiredCompressionMethod'} =
1497 ( $newDesiredCompressionLevel
1498 ? COMPRESSION_DEFLATED
1499 : COMPRESSION_STORED
);
1501 return $oldDesiredCompressionLevel;
1504 sub fileName
# Archive::Zip::Member
1507 my $newName = shift;
1510 $newName =~ s{[\\/]+}{/}g; # deal with dos/windoze problems
1511 $self->{'fileName'} = $newName;
1513 return $self->{'fileName'};
1516 sub lastModFileDateTime
# Archive::Zip::Member
1518 my $modTime = shift->{'lastModFileDateTime'};
1519 $modTime =~ m/^(\d+)$/; # untaint
1523 sub lastModTime
# Archive::Zip::Member
1526 return _dosToUnixTime
( $self->lastModFileDateTime() );
1529 sub setLastModFileDateTimeFromUnix
# Archive::Zip::Member
1533 $self->{'lastModFileDateTime'} = _unixToDosTime
($time_t);
1536 # DOS date/time format
1537 # 0-4 (5) Second divided by 2
1538 # 5-10 (6) Minute (0-59)
1539 # 11-15 (5) Hour (0-23 on a 24-hour clock)
1540 # 16-20 (5) Day of the month (1-31)
1541 # 21-24 (4) Month (1 = January, 2 = February, etc.)
1542 # 25-31 (7) Year offset from 1980 (add 1980 to get actual year)
1544 # Convert DOS date/time format to unix time_t format
1545 # NOT AN OBJECT METHOD!
1546 sub _dosToUnixTime
# Archive::Zip::Member
1549 return time() unless defined($dt);
1551 my $year = ( ( $dt >> 25 ) & 0x7f ) + 80;
1552 my $mon = ( ( $dt >> 21 ) & 0x0f ) - 1;
1553 my $mday = ( ( $dt >> 16 ) & 0x1f );
1555 my $hour = ( ( $dt >> 11 ) & 0x1f );
1556 my $min = ( ( $dt >> 5 ) & 0x3f );
1557 my $sec = ( ( $dt << 1 ) & 0x3e );
1561 eval { Time
::Local
::timelocal
( $sec, $min, $hour, $mday, $mon, $year ); };
1562 return time() if ($@
);
1566 sub internalFileAttributes
# Archive::Zip::Member
1568 shift->{'internalFileAttributes'};
1571 sub externalFileAttributes
# Archive::Zip::Member
1573 shift->{'externalFileAttributes'};
1576 # Convert UNIX permissions into proper value for zip file
1578 sub _mapPermissionsFromUnix
# Archive::Zip::Member
1581 return $perms << 16;
1583 # TODO: map MS-DOS perms too (RHSA?)
1586 # Convert ZIP permissions into Unix ones
1588 # This was taken from Info-ZIP group's portable UnZip
1589 # zipfile-extraction program, version 5.50.
1590 # http://www.info-zip.org/pub/infozip/
1592 # See the mapattr() function in unix/unix.c
1593 # See the attribute format constants in unzpriv.h
1595 # XXX Note that there's one situation that isn't implemented
1596 # yet that depends on the "extra field."
1597 sub _mapPermissionsToUnix
# Archive::Zip::Member
1601 my $format = $self->{'fileAttributeFormat'};
1602 my $attribs = $self->{'externalFileAttributes'};
1606 if ( $format == FA_AMIGA
)
1608 $attribs = $attribs >> 17 & 7; # Amiga RWE bits
1609 $mode = $attribs << 6 | $attribs << 3 | $attribs;
1613 if ( $format == FA_THEOS
)
1615 $attribs &= 0xF1FFFFFF;
1616 if ( ( $attribs & 0xF0000000 ) != 0x40000000 )
1618 $attribs &= 0x01FFFFFF; # not a dir, mask all ftype bits
1622 $attribs &= 0x41FFFFFF; # leave directory bit as set
1626 if ( $format == FA_UNIX
1627 || $format == FA_VAX_VMS
1628 || $format == FA_ACORN
1629 || $format == FA_ATARI_ST
1630 || $format == FA_BEOS
1631 || $format == FA_QDOS
1632 || $format == FA_TANDEM
)
1634 $mode = $attribs >> 16;
1635 return $mode if $mode != 0 or not $self->localExtraField;
1637 # warn("local extra field is: ", $self->localExtraField, "\n");
1639 # XXX This condition is not implemented
1640 # I'm just including the comments from the info-zip section for now.
1642 # Some (non-Info-ZIP) implementations of Zip for Unix and
1643 # VMS (and probably others ??) leave 0 in the upper 16-bit
1644 # part of the external_file_attributes field. Instead, they
1645 # store file permission attributes in some extra field.
1646 # As a work-around, we search for the presence of one of
1647 # these extra fields and fall back to the MSDOS compatible
1648 # part of external_file_attributes if one of the known
1649 # e.f. types has been detected.
1650 # Later, we might implement extraction of the permission
1651 # bits from the VMS extra field. But for now, the work-around
1652 # should be sufficient to provide "readable" extracted files.
1653 # (For ASI Unix e.f., an experimental remap from the e.f.
1654 # mode value IS already provided!)
1657 # PKWARE's PKZip for Unix marks entries as FA_MSDOS, but stores the
1658 # Unix attributes in the upper 16 bits of the external attributes
1659 # field, just like Info-ZIP's Zip for Unix. We try to use that
1660 # value, after a check for consistency with the MSDOS attribute
1662 if ( $format == FA_MSDOS
)
1664 $mode = $attribs >> 16;
1667 # FA_MSDOS, FA_OS2_HPFS, FA_WINDOWS_NTFS, FA_MACINTOSH, FA_TOPS20
1668 $attribs = !( $attribs & 1 ) << 1 | ( $attribs & 0x10 ) >> 4;
1670 # keep previous $mode setting when its "owner"
1671 # part appears to be consistent with DOS attribute flags!
1672 return $mode if ( $mode & 0700 ) == ( 0400 | $attribs << 6 );
1673 $mode = 0444 | $attribs << 6 | $attribs << 3 | $attribs;
1677 sub unixFileAttributes
# Archive::Zip::Member
1680 my $oldPerms = $self->_mapPermissionsToUnix();
1684 if ( $self->isDirectory() )
1686 $perms &= ~FILE_ATTRIB
;
1687 $perms |= DIRECTORY_ATTRIB
;
1691 $perms &= ~DIRECTORY_ATTRIB
;
1692 $perms |= FILE_ATTRIB
;
1694 $self->{'externalFileAttributes'} = _mapPermissionsFromUnix
($perms);
1699 sub localExtraField
# Archive::Zip::Member
1702 ?
( $_[0]->{'localExtraField'} = $_[1] )
1703 : $_[0]->{'localExtraField'};
1706 sub cdExtraField
# Archive::Zip::Member
1708 ( $#_ > 0 ) ?
( $_[0]->{'cdExtraField'} = $_[1] ) : $_[0]->{'cdExtraField'};
1711 sub extraFields
# Archive::Zip::Member
1714 return $self->localExtraField() . $self->cdExtraField();
1717 sub fileComment
# Archive::Zip::Member
1720 ?
( $_[0]->{'fileComment'} = pack( 'C0a*', $_[1] ) )
1721 : $_[0]->{'fileComment'};
1724 sub hasDataDescriptor
# Archive::Zip::Member
1729 my $shouldHave = shift;
1732 $self->{'bitFlag'} |= GPBF_HAS_DATA_DESCRIPTOR_MASK
;
1736 $self->{'bitFlag'} &= ~GPBF_HAS_DATA_DESCRIPTOR_MASK
;
1739 return $self->{'bitFlag'} & GPBF_HAS_DATA_DESCRIPTOR_MASK
;
1742 sub crc32
# Archive::Zip::Member
1747 sub crc32String
# Archive::Zip::Member
1749 sprintf( "%08x", shift->{'crc32'} );
1752 sub compressedSize
# Archive::Zip::Member
1754 shift->{'compressedSize'};
1757 sub uncompressedSize
# Archive::Zip::Member
1759 shift->{'uncompressedSize'};
1762 sub isEncrypted
# Archive::Zip::Member
1764 shift->bitFlag() & GPBF_ENCRYPTED_MASK
;
1767 sub isTextFile
# Archive::Zip::Member
1770 my $bit = $self->internalFileAttributes() & IFA_TEXT_FILE_MASK
;
1774 $self->{'internalFileAttributes'} &= ~IFA_TEXT_FILE_MASK
;
1775 $self->{'internalFileAttributes'} |=
1776 ( $flag ? IFA_TEXT_FILE
: IFA_BINARY_FILE
);
1778 return $bit == IFA_TEXT_FILE
;
1781 sub isBinaryFile
# Archive::Zip::Member
1784 my $bit = $self->internalFileAttributes() & IFA_TEXT_FILE_MASK
;
1788 $self->{'internalFileAttributes'} &= ~IFA_TEXT_FILE_MASK
;
1789 $self->{'internalFileAttributes'} |=
1790 ( $flag ? IFA_BINARY_FILE
: IFA_TEXT_FILE
);
1792 return $bit == IFA_BINARY_FILE
;
1795 sub extractToFileNamed
# Archive::Zip::Member
1798 my $name = shift; # local FS name
1799 return _error
("encryption unsupported") if $self->isEncrypted();
1800 mkpath
( dirname
($name) ); # croaks on error
1801 my ( $status, $fh ) = _newFileHandle
( $name, 'w' );
1802 return _ioError
("Can't open file $name for write") unless $status;
1803 my $retval = $self->extractToFileHandle($fh);
1805 utime( $self->lastModTime(), $self->lastModTime(), $name );
1809 sub isDirectory
# Archive::Zip::Member
1814 sub externalFileName
# Archive::Zip::Member
1819 # The following are used when copying data
1820 sub _writeOffset
# Archive::Zip::Member
1822 shift->{'writeOffset'};
1825 sub _readOffset
# Archive::Zip::Member
1827 shift->{'readOffset'};
1830 sub writeLocalHeaderRelativeOffset
# Archive::Zip::Member
1832 shift->{'writeLocalHeaderRelativeOffset'};
1835 sub wasWritten
{ shift->{'wasWritten'} }
1837 sub _dataEnded
# Archive::Zip::Member
1839 shift->{'dataEnded'};
1842 sub _readDataRemaining
# Archive::Zip::Member
1844 shift->{'readDataRemaining'};
1847 sub _inflater
# Archive::Zip::Member
1849 shift->{'inflater'};
1852 sub _deflater
# Archive::Zip::Member
1854 shift->{'deflater'};
1857 # Return the total size of my local header
1858 sub _localHeaderSize
# Archive::Zip::Member
1861 return SIGNATURE_LENGTH
+ LOCAL_FILE_HEADER_LENGTH
+
1862 length( $self->fileName() ) + length( $self->localExtraField() );
1865 # Return the total size of my CD header
1866 sub _centralDirectoryHeaderSize
# Archive::Zip::Member
1869 return SIGNATURE_LENGTH
+ CENTRAL_DIRECTORY_FILE_HEADER_LENGTH
+
1870 length( $self->fileName() ) + length( $self->cdExtraField() ) +
1871 length( $self->fileComment() );
1874 # convert a unix time to DOS date/time
1875 # NOT AN OBJECT METHOD!
1876 sub _unixToDosTime
# Archive::Zip::Member
1879 my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime($time_t);
1881 $dt += ( $sec >> 1 );
1882 $dt += ( $min << 5 );
1883 $dt += ( $hour << 11 );
1884 $dt += ( $mday << 16 );
1885 $dt += ( ( $mon + 1 ) << 21 );
1886 $dt += ( ( $year - 80 ) << 25 );
1890 # Write my local header to a file handle.
1891 # Stores the offset to the start of the header in my
1892 # writeLocalHeaderRelativeOffset member.
1893 # Returns AZ_OK on success.
1894 sub _writeLocalFileHeader
# Archive::Zip::Member
1899 my $signatureData = pack( SIGNATURE_FORMAT
, LOCAL_FILE_HEADER_SIGNATURE
);
1900 $fh->print($signatureData)
1901 or return _ioError
("writing local header signature");
1904 LOCAL_FILE_HEADER_FORMAT
,
1905 $self->versionNeededToExtract(),
1907 $self->desiredCompressionMethod(),
1908 $self->lastModFileDateTime(),
1910 $self->compressedSize(), # may need to be re-written later
1911 $self->uncompressedSize(),
1912 length( $self->fileName() ),
1913 length( $self->localExtraField() )
1916 $fh->print($header) or return _ioError
("writing local header");
1917 if ( $self->fileName() )
1919 $fh->print( $self->fileName() )
1920 or return _ioError
("writing local header filename");
1922 if ( $self->localExtraField() )
1924 $fh->print( $self->localExtraField() )
1925 or return _ioError
("writing local extra field");
1931 sub _writeCentralDirectoryFileHeader
# Archive::Zip::Member
1937 pack( SIGNATURE_FORMAT
, CENTRAL_DIRECTORY_FILE_HEADER_SIGNATURE
);
1938 $fh->print($sigData)
1939 or return _ioError
("writing central directory header signature");
1941 my $fileNameLength = length( $self->fileName() );
1942 my $extraFieldLength = length( $self->cdExtraField() );
1943 my $fileCommentLength = length( $self->fileComment() );
1946 CENTRAL_DIRECTORY_FILE_HEADER_FORMAT
,
1947 $self->versionMadeBy(),
1948 $self->fileAttributeFormat(),
1949 $self->versionNeededToExtract(),
1951 $self->desiredCompressionMethod(),
1952 $self->lastModFileDateTime(),
1953 $self->crc32(), # these three fields should have been updated
1954 $self->_writeOffset(), # by writing the data stream out
1955 $self->uncompressedSize(), #
1959 0, # {'diskNumberStart'},
1960 $self->internalFileAttributes(),
1961 $self->externalFileAttributes(),
1962 $self->writeLocalHeaderRelativeOffset()
1966 or return _ioError
("writing central directory header");
1967 if ($fileNameLength)
1969 $fh->print( $self->fileName() )
1970 or return _ioError
("writing central directory header signature");
1972 if ($extraFieldLength)
1974 $fh->print( $self->cdExtraField() )
1975 or return _ioError
("writing central directory extra field");
1977 if ($fileCommentLength)
1979 $fh->print( $self->fileComment() )
1980 or return _ioError
("writing central directory file comment");
1986 # This writes a data descriptor to the given file handle.
1987 # Assumes that crc32, writeOffset, and uncompressedSize are
1988 # set correctly (they should be after a write).
1989 # Further, the local file header should have the
1990 # GPBF_HAS_DATA_DESCRIPTOR_MASK bit set.
1991 sub _writeDataDescriptor
# Archive::Zip::Member
1996 SIGNATURE_FORMAT
. DATA_DESCRIPTOR_FORMAT
,
1997 DATA_DESCRIPTOR_SIGNATURE
,
1999 $self->_writeOffset(), # compressed size
2000 $self->uncompressedSize()
2004 or return _ioError
("writing data descriptor");
2008 # Re-writes the local file header with new crc32 and compressedSize fields.
2009 # To be called after writing the data stream.
2010 # Assumes that filename and extraField sizes didn't change since last written.
2011 sub _refreshLocalFileHeader
# Archive::Zip::Member
2016 my $here = $fh->tell();
2017 $fh->seek( $self->writeLocalHeaderRelativeOffset() + SIGNATURE_LENGTH
,
2018 IO
::Seekable
::SEEK_SET
)
2019 or return _ioError
("seeking to rewrite local header");
2022 LOCAL_FILE_HEADER_FORMAT
,
2023 $self->versionNeededToExtract(),
2025 $self->desiredCompressionMethod(),
2026 $self->lastModFileDateTime(),
2028 $self->_writeOffset(), # compressed size
2029 $self->uncompressedSize(),
2030 length( $self->fileName() ),
2031 length( $self->localExtraField() )
2035 or return _ioError
("re-writing local header");
2036 $fh->seek( $here, IO
::Seekable
::SEEK_SET
)
2037 or return _ioError
("seeking after rewrite of local header");
2042 sub readChunk
# Archive::Zip::Member
2044 my ( $self, $chunkSize ) = @_;
2046 if ( $self->readIsDone() )
2050 return ( \
$dummy, AZ_STREAM_END
);
2053 $chunkSize = $Archive::Zip
::ChunkSize
if not defined($chunkSize);
2054 $chunkSize = $self->_readDataRemaining()
2055 if $chunkSize > $self->_readDataRemaining();
2059 my ( $bytesRead, $status ) = $self->_readRawChunk( \
$buffer, $chunkSize );
2060 return ( \
$buffer, $status ) unless $status == AZ_OK
;
2062 $self->{'readDataRemaining'} -= $bytesRead;
2063 $self->{'readOffset'} += $bytesRead;
2065 if ( $self->compressionMethod() == COMPRESSION_STORED
)
2067 $self->{'crc32'} = $self->computeCRC32( $buffer, $self->{'crc32'} );
2070 ( $outputRef, $status ) = &{ $self->{'chunkHandler'} } ( $self, \
$buffer );
2071 $self->{'writeOffset'} += length($$outputRef);
2074 if $self->readIsDone();
2076 return ( $outputRef, $status );
2079 # Read the next raw chunk of my data. Subclasses MUST implement.
2080 # my ( $bytesRead, $status) = $self->_readRawChunk( \$buffer, $chunkSize );
2081 sub _readRawChunk
# Archive::Zip::Member
2084 return $self->_subclassResponsibility();
2087 # A place holder to catch rewindData errors if someone ignores
2089 sub _noChunk
# Archive::Zip::Member
2092 return ( \
undef, _error
("trying to copy chunk when init failed") );
2095 # Basically a no-op so that I can have a consistent interface.
2096 # ( $outputRef, $status) = $self->_copyChunk( \$buffer );
2097 sub _copyChunk
# Archive::Zip::Member
2099 my ( $self, $dataRef ) = @_;
2100 return ( $dataRef, AZ_OK
);
2103 # ( $outputRef, $status) = $self->_deflateChunk( \$buffer );
2104 sub _deflateChunk
# Archive::Zip::Member
2106 my ( $self, $buffer ) = @_;
2107 my ( $out, $status ) = $self->_deflater()->deflate($buffer);
2109 if ( $self->_readDataRemaining() == 0 )
2112 ( $extraOutput, $status ) = $self->_deflater()->flush();
2113 $out .= $extraOutput;
2115 return ( \
$out, AZ_STREAM_END
);
2117 elsif ( $status == Z_OK
)
2119 return ( \
$out, AZ_OK
);
2124 my $retval = _error
( 'deflate error', $status );
2126 return ( \
$dummy, $retval );
2130 # ( $outputRef, $status) = $self->_inflateChunk( \$buffer );
2131 sub _inflateChunk
# Archive::Zip::Member
2133 my ( $self, $buffer ) = @_;
2134 my ( $out, $status ) = $self->_inflater()->inflate($buffer);
2136 $self->endRead() unless $status == Z_OK
;
2137 if ( $status == Z_OK
|| $status == Z_STREAM_END
)
2139 $retval = ( $status == Z_STREAM_END
) ? AZ_STREAM_END
: AZ_OK
;
2140 return ( \
$out, $retval );
2144 $retval = _error
( 'inflate error', $status );
2146 return ( \
$dummy, $retval );
2150 sub rewindData
# Archive::Zip::Member
2155 # set to trap init errors
2156 $self->{'chunkHandler'} = $self->can('_noChunk');
2158 # Work around WinZip bug with 0-length DEFLATED files
2159 $self->desiredCompressionMethod(COMPRESSION_STORED
)
2160 if $self->uncompressedSize() == 0;
2162 # assume that we're going to read the whole file, and compute the CRC anew.
2163 $self->{'crc32'} = 0
2164 if ( $self->compressionMethod() == COMPRESSION_STORED
);
2166 # These are the only combinations of methods we deal with right now.
2167 if ( $self->compressionMethod() == COMPRESSION_STORED
2168 and $self->desiredCompressionMethod() == COMPRESSION_DEFLATED
)
2170 ( $self->{'deflater'}, $status ) = Compress
::Zlib
::deflateInit
(
2171 '-Level' => $self->desiredCompressionLevel(),
2172 '-WindowBits' => -MAX_WBITS
(), # necessary magic
2173 '-Bufsize' => $Archive::Zip
::ChunkSize
,
2175 ); # pass additional options
2176 return _error
( 'deflateInit error:', $status )
2177 unless $status == Z_OK
;
2178 $self->{'chunkHandler'} = $self->can('_deflateChunk');
2180 elsif ( $self->compressionMethod() == COMPRESSION_DEFLATED
2181 and $self->desiredCompressionMethod() == COMPRESSION_STORED
)
2183 ( $self->{'inflater'}, $status ) = Compress
::Zlib
::inflateInit
(
2184 '-WindowBits' => -MAX_WBITS
(), # necessary magic
2185 '-Bufsize' => $Archive::Zip
::ChunkSize
,
2187 ); # pass additional options
2188 return _error
( 'inflateInit error:', $status )
2189 unless $status == Z_OK
;
2190 $self->{'chunkHandler'} = $self->can('_inflateChunk');
2192 elsif ( $self->compressionMethod() == $self->desiredCompressionMethod() )
2194 $self->{'chunkHandler'} = $self->can('_copyChunk');
2200 "Unsupported compression combination: read %d, write %d",
2201 $self->compressionMethod(),
2202 $self->desiredCompressionMethod()
2207 $self->{'readDataRemaining'} =
2208 ( $self->compressionMethod() == COMPRESSION_STORED
)
2209 ?
$self->uncompressedSize()
2210 : $self->compressedSize();
2211 $self->{'dataEnded'} = 0;
2212 $self->{'readOffset'} = 0;
2217 sub endRead
# Archive::Zip::Member
2220 delete $self->{'inflater'};
2221 delete $self->{'deflater'};
2222 $self->{'dataEnded'} = 1;
2223 $self->{'readDataRemaining'} = 0;
2227 sub readIsDone
# Archive::Zip::Member
2230 return ( $self->_dataEnded() or !$self->_readDataRemaining() );
2233 sub contents
# Archive::Zip::Member
2236 my $newContents = shift;
2238 if ( defined($newContents) )
2241 # change our type and call the subclass contents method.
2242 $self->_become(STRINGMEMBERCLASS
);
2243 return $self->contents( pack( 'C0a*', $newContents ) )
2244 ; # in case of Unicode
2248 my $oldCompression =
2249 $self->desiredCompressionMethod(COMPRESSION_STORED
);
2250 my $status = $self->rewindData(@_);
2251 if ( $status != AZ_OK
)
2257 while ( $status == AZ_OK
)
2260 ( $ref, $status ) = $self->readChunk( $self->_readDataRemaining() );
2262 # did we get it in one chunk?
2263 if ( length($$ref) == $self->uncompressedSize() )
2267 else { $retval .= $$ref }
2269 $self->desiredCompressionMethod($oldCompression);
2271 $status = AZ_OK
if $status == AZ_STREAM_END
;
2272 $retval = undef unless $status == AZ_OK
;
2273 return wantarray ?
( $retval, $status ) : $retval;
2277 sub extractToFileHandle
# Archive::Zip::Member
2280 return _error
("encryption unsupported") if $self->isEncrypted();
2283 my $oldCompression = $self->desiredCompressionMethod(COMPRESSION_STORED
);
2284 my $status = $self->rewindData(@_);
2285 $status = $self->_writeData($fh) if $status == AZ_OK
;
2286 $self->desiredCompressionMethod($oldCompression);
2291 # write local header and data stream to file handle
2292 sub _writeToFileHandle
# Archive::Zip::Member
2296 my $fhIsSeekable = shift;
2299 return _error
("no member name given for $self")
2300 unless $self->fileName();
2302 $self->{'writeLocalHeaderRelativeOffset'} = $offset;
2303 $self->{'wasWritten'} = 0;
2305 # Determine if I need to write a data descriptor
2306 # I need to do this if I can't refresh the header
2307 # and I don't know compressed size or crc32 fields.
2308 my $headerFieldsUnknown =
2309 ( ( $self->uncompressedSize() > 0 )
2310 and ( $self->compressionMethod() == COMPRESSION_STORED
2311 or $self->desiredCompressionMethod() == COMPRESSION_DEFLATED
) );
2313 my $shouldWriteDataDescriptor =
2314 ( $headerFieldsUnknown and not $fhIsSeekable );
2316 $self->hasDataDescriptor(1)
2317 if ($shouldWriteDataDescriptor);
2319 $self->{'writeOffset'} = 0;
2321 my $status = $self->rewindData();
2322 ( $status = $self->_writeLocalFileHeader($fh) )
2323 if $status == AZ_OK
;
2324 ( $status = $self->_writeData($fh) )
2325 if $status == AZ_OK
;
2326 if ( $status == AZ_OK
)
2328 $self->{'wasWritten'} = 1;
2329 if ( $self->hasDataDescriptor() )
2331 $status = $self->_writeDataDescriptor($fh);
2333 elsif ($headerFieldsUnknown)
2335 $status = $self->_refreshLocalFileHeader($fh);
2342 # Copy my (possibly compressed) data to given file handle.
2343 # Returns C<AZ_OK> on success
2344 sub _writeData
# Archive::Zip::Member
2347 my $writeFh = shift;
2349 return AZ_OK
if ( $self->uncompressedSize() == 0 );
2351 my $chunkSize = $Archive::Zip
::ChunkSize
;
2352 while ( $self->_readDataRemaining() > 0 )
2355 ( $outRef, $status ) = $self->readChunk($chunkSize);
2356 return $status if ( $status != AZ_OK
and $status != AZ_STREAM_END
);
2358 if ( length($$outRef) > 0 )
2360 $writeFh->print($$outRef)
2361 or return _ioError
("write error during copy");
2364 last if $status == AZ_STREAM_END
;
2366 $self->{'compressedSize'} = $self->_writeOffset();
2370 # Return true if I depend on the named file
2376 # ----------------------------------------------------------------------
2377 # class Archive::Zip::DirectoryMember
2378 # ----------------------------------------------------------------------
2380 package Archive
::Zip
::DirectoryMember
;
2383 use vars
qw( @ISA );
2384 @ISA = qw ( Archive::Zip::Member );
2385 BEGIN { use Archive::Zip qw( :ERROR_CODES :UTILITY_METHODS ) }
2387 sub _newNamed
# Archive::Zip::DirectoryMember
2390 my $fileName = shift; # FS name
2391 my $newName = shift; # Zip name
2392 $newName = _asZipDirName
($fileName) unless $newName;
2393 my $self = $class->new(@_);
2394 $self->{'externalFileName'} = $fileName;
2395 $self->fileName($newName);
2402 $self->unixFileAttributes( $stat[2] );
2403 $self->setLastModFileDateTimeFromUnix( $stat[9] );
2405 else # hmm.. trying to add a non-directory?
2407 _error
( $fileName, ' exists but is not a directory' );
2413 $self->unixFileAttributes( $self->DEFAULT_DIRECTORY_PERMISSIONS );
2414 $self->setLastModFileDateTimeFromUnix( time() );
2419 sub externalFileName
# Archive::Zip::DirectoryMember
2421 shift->{'externalFileName'};
2424 sub isDirectory
# Archive::Zip::DirectoryMember
2429 sub extractToFileNamed
# Archive::Zip::DirectoryMember
2432 my $name = shift; # local FS name
2433 my $attribs = $self->unixFileAttributes() & 07777;
2434 mkpath
( $name, 0, $attribs ); # croaks on error
2435 utime( $self->lastModTime(), $self->lastModTime(), $name );
2439 sub fileName
# Archive::Zip::DirectoryMember
2442 my $newName = shift;
2443 $newName =~ s{/?$}{/} if defined($newName);
2444 return $self->SUPER::fileName
($newName);
2447 # So people don't get too confused. This way it looks like the problem
2448 # is in their code...
2451 return wantarray ?
( undef, AZ_OK
) : undef;
2454 # ----------------------------------------------------------------------
2455 # class Archive::Zip::FileMember
2456 # Base class for classes that have file handles
2458 # ----------------------------------------------------------------------
2460 package Archive
::Zip
::FileMember
;
2461 use vars
qw( @ISA );
2462 @ISA = qw ( Archive::Zip::Member );
2463 BEGIN { use Archive::Zip qw( :UTILITY_METHODS ) }
2465 sub externalFileName
# Archive::Zip::FileMember
2467 shift->{'externalFileName'};
2470 # Return true if I depend on the named file
2471 sub _usesFileNamed
# Archive::Zip::FileMember
2474 my $fileName = shift;
2475 my $xfn = $self->externalFileName();
2476 return undef if ref($xfn);
2477 return $xfn eq $fileName;
2480 sub fh
# Archive::Zip::FileMember
2484 if !defined( $self->{'fh'} ) || !$self->{'fh'}->opened();
2485 return $self->{'fh'};
2488 # opens my file handle from my file name
2489 sub _openFile
# Archive::Zip::FileMember
2492 my ( $status, $fh ) = _newFileHandle
( $self->externalFileName(), 'r' );
2495 _ioError
( "Can't open", $self->externalFileName() );
2498 $self->{'fh'} = $fh;
2503 # Make sure I close my file handle
2504 sub endRead
# Archive::Zip::FileMember
2507 undef $self->{'fh'}; # _closeFile();
2508 return $self->SUPER::endRead
(@_);
2511 sub _become
# Archive::Zip::FileMember
2514 my $newClass = shift;
2515 return $self if ref($self) eq $newClass;
2516 delete( $self->{'externalFileName'} );
2517 delete( $self->{'fh'} );
2518 return $self->SUPER::_become
($newClass);
2521 # ----------------------------------------------------------------------
2522 # class Archive::Zip::NewFileMember
2523 # Used when adding a pre-existing file to an archive
2524 # ----------------------------------------------------------------------
2526 package Archive
::Zip
::NewFileMember
;
2527 use vars
qw( @ISA );
2528 @ISA = qw ( Archive::Zip::FileMember );
2530 BEGIN { use Archive::Zip qw( :CONSTANTS :ERROR_CODES :UTILITY_METHODS ) }
2532 # Given a file name, set up for eventual writing.
2533 sub _newFromFileNamed
# Archive::Zip::NewFileMember
2536 my $fileName = shift; # local FS format
2537 my $newName = shift;
2538 $newName = _asZipDirName
($fileName) unless defined($newName);
2539 return undef unless ( stat($fileName) && -r _
&& !-d _
);
2540 my $self = $class->new(@_);
2541 $self->fileName($newName);
2542 $self->{'externalFileName'} = $fileName;
2543 $self->{'compressionMethod'} = COMPRESSION_STORED
;
2545 $self->{'compressedSize'} = $self->{'uncompressedSize'} = $stat[7];
2546 $self->desiredCompressionMethod( ( $self->compressedSize() > 0 )
2547 ? COMPRESSION_DEFLATED
2548 : COMPRESSION_STORED
);
2549 $self->unixFileAttributes( $stat[2] );
2550 $self->setLastModFileDateTimeFromUnix( $stat[9] );
2551 $self->isTextFile( -T _
);
2555 sub rewindData
# Archive::Zip::NewFileMember
2559 my $status = $self->SUPER::rewindData
(@_);
2560 return $status unless $status == AZ_OK
;
2562 return AZ_IO_ERROR
unless $self->fh();
2563 $self->fh()->clearerr();
2564 $self->fh()->seek( 0, IO
::Seekable
::SEEK_SET
)
2565 or return _ioError
( "rewinding", $self->externalFileName() );
2569 # Return bytes read. Note that first parameter is a ref to a buffer.
2571 # my ( $bytesRead, $status) = $self->readRawChunk( \$data, $chunkSize );
2572 sub _readRawChunk
# Archive::Zip::NewFileMember
2574 my ( $self, $dataRef, $chunkSize ) = @_;
2575 return ( 0, AZ_OK
) unless $chunkSize;
2576 my $bytesRead = $self->fh()->read( $$dataRef, $chunkSize )
2577 or return ( 0, _ioError
("reading data") );
2578 return ( $bytesRead, AZ_OK
);
2581 # If I already exist, extraction is a no-op.
2582 sub extractToFileNamed
# Archive::Zip::NewFileMember
2585 my $name = shift; # local FS name
2586 if ( File
::Spec
->rel2abs($name) eq
2587 File
::Spec
->rel2abs( $self->externalFileName() ) and -r
$name )
2593 return $self->SUPER::extractToFileNamed
( $name, @_ );
2597 # ----------------------------------------------------------------------
2598 # class Archive::Zip::ZipFileMember
2599 # This represents a member in an existing zip file on disk.
2600 # ----------------------------------------------------------------------
2602 package Archive
::Zip
::ZipFileMember
;
2603 use vars
qw( @ISA );
2604 @ISA = qw ( Archive::Zip::FileMember );
2608 use Archive::Zip qw( :CONSTANTS :ERROR_CODES :PKZIP_CONSTANTS
2612 # Create a new Archive::Zip::ZipFileMember
2613 # given a filename and optional open file handle
2615 sub _newFromZipFile
# Archive::Zip::ZipFileMember
2619 my $externalFileName = shift;
2620 my $possibleEocdOffset = shift; # normally 0
2622 my $self = $class->new(
2624 'diskNumberStart' => 0,
2625 'localHeaderRelativeOffset' => 0,
2626 'dataOffset' => 0, # localHeaderRelativeOffset + header length
2629 $self->{'externalFileName'} = $externalFileName;
2630 $self->{'fh'} = $fh;
2631 $self->{'possibleEocdOffset'} = $possibleEocdOffset;
2635 sub isDirectory
# Archive::Zip::ZipFileMember
2638 return ( substr( $self->fileName(), -1, 1 ) eq '/'
2639 and $self->uncompressedSize() == 0 );
2642 # Seek to the beginning of the local header, just past the signature.
2643 # Verify that the local header signature is in fact correct.
2644 # Update the localHeaderRelativeOffset if necessary by adding the possibleEocdOffset.
2647 sub _seekToLocalHeader
# Archive::Zip::ZipFileMember
2650 my $where = shift; # optional
2651 my $previousWhere = shift; # optional
2653 $where = $self->localHeaderRelativeOffset() unless defined($where);
2655 # avoid loop on certain corrupt files (from Julian Field)
2656 return _formatError
("corrupt zip file")
2657 if defined($previousWhere) && $where == $previousWhere;
2662 $status = $self->fh()->seek( $where, IO
::Seekable
::SEEK_SET
);
2663 return _ioError
("seeking to local header") unless $status;
2665 ( $status, $signature ) =
2666 _readSignature
( $self->fh(), $self->externalFileName(),
2667 LOCAL_FILE_HEADER_SIGNATURE
);
2668 return $status if $status == AZ_IO_ERROR
;
2670 # retry with EOCD offset if any was given.
2671 if ( $status == AZ_FORMAT_ERROR
&& $self->{'possibleEocdOffset'} )
2674 $self->_seekToLocalHeader(
2675 $self->localHeaderRelativeOffset() + $self->{'possibleEocdOffset'},
2677 if ( $status == AZ_OK
)
2679 $self->{'localHeaderRelativeOffset'} +=
2680 $self->{'possibleEocdOffset'};
2681 $self->{'possibleEocdOffset'} = 0;
2688 # Because I'm going to delete the file handle, read the local file
2689 # header if the file handle is seekable. If it isn't, I assume that
2690 # I've already read the local header.
2691 # Return ( $status, $self )
2693 sub _become
# Archive::Zip::ZipFileMember
2696 my $newClass = shift;
2697 return $self if ref($self) eq $newClass;
2701 if ( _isSeekable
( $self->fh() ) )
2703 my $here = $self->fh()->tell();
2704 $status = $self->_seekToLocalHeader();
2705 $status = $self->_readLocalFileHeader() if $status == AZ_OK
;
2706 $self->fh()->seek( $here, IO
::Seekable
::SEEK_SET
);
2707 return $status unless $status == AZ_OK
;
2710 delete( $self->{'eocdCrc32'} );
2711 delete( $self->{'diskNumberStart'} );
2712 delete( $self->{'localHeaderRelativeOffset'} );
2713 delete( $self->{'dataOffset'} );
2715 return $self->SUPER::_become
($newClass);
2718 sub diskNumberStart
# Archive::Zip::ZipFileMember
2720 shift->{'diskNumberStart'};
2723 sub localHeaderRelativeOffset
# Archive::Zip::ZipFileMember
2725 shift->{'localHeaderRelativeOffset'};
2728 sub dataOffset
# Archive::Zip::ZipFileMember
2730 shift->{'dataOffset'};
2733 # Skip local file header, updating only extra field stuff.
2734 # Assumes that fh is positioned before signature.
2735 sub _skipLocalFileHeader
# Archive::Zip::ZipFileMember
2739 my $bytesRead = $self->fh()->read( $header, LOCAL_FILE_HEADER_LENGTH
);
2740 if ( $bytesRead != LOCAL_FILE_HEADER_LENGTH
)
2742 return _ioError
("reading local file header");
2745 my $extraFieldLength;
2747 ( undef, # $self->{'versionNeededToExtract'},
2749 undef, # $self->{'compressionMethod'},
2750 undef, # $self->{'lastModFileDateTime'},
2752 undef, # $compressedSize,
2753 undef, # $uncompressedSize,
2756 = unpack( LOCAL_FILE_HEADER_FORMAT
, $header );
2758 if ($fileNameLength)
2760 $self->fh()->seek( $fileNameLength, IO
::Seekable
::SEEK_CUR
)
2761 or return _ioError
("skipping local file name");
2764 if ($extraFieldLength)
2767 $self->fh()->read( $self->{'localExtraField'}, $extraFieldLength );
2768 if ( $bytesRead != $extraFieldLength )
2770 return _ioError
("reading local extra field");
2774 $self->{'dataOffset'} = $self->fh()->tell();
2776 if ( $bitFlag & GPBF_HAS_DATA_DESCRIPTOR_MASK
)
2779 # Read the crc32, compressedSize, and uncompressedSize from the
2780 # extended data descriptor, which directly follows the compressed data.
2782 # Skip over the compressed file data (assumes that EOCD compressedSize
2784 $self->fh()->seek( $self->{'compressedSize'}, IO
::Seekable
::SEEK_CUR
)
2785 or return _ioError
("seeking to extended local header");
2787 # these values should be set correctly from before.
2788 my $oldCrc32 = $self->{'eocdCrc32'};
2789 my $oldCompressedSize = $self->{'compressedSize'};
2790 my $oldUncompressedSize = $self->{'uncompressedSize'};
2792 my $status = $self->_readDataDescriptor();
2793 return $status unless $status == AZ_OK
;
2795 return _formatError
(
2796 "CRC or size mismatch while skipping data descriptor")
2797 if ( $oldCrc32 != $self->{'crc32'}
2798 || $oldUncompressedSize != $self->{'uncompressedSize'} );
2804 # Read from a local file header into myself. Returns AZ_OK if successful.
2805 # Assumes that fh is positioned after signature.
2806 # Note that crc32, compressedSize, and uncompressedSize will be 0 if
2807 # GPBF_HAS_DATA_DESCRIPTOR_MASK is set in the bitFlag.
2809 sub _readLocalFileHeader
# Archive::Zip::ZipFileMember
2813 my $bytesRead = $self->fh()->read( $header, LOCAL_FILE_HEADER_LENGTH
);
2814 if ( $bytesRead != LOCAL_FILE_HEADER_LENGTH
)
2816 return _ioError
("reading local file header");
2821 my $uncompressedSize;
2822 my $extraFieldLength;
2823 ( $self->{'versionNeededToExtract'}, $self->{'bitFlag'},
2824 $self->{'compressionMethod'}, $self->{'lastModFileDateTime'},
2825 $crc32, $compressedSize,
2826 $uncompressedSize, $fileNameLength,
2828 = unpack( LOCAL_FILE_HEADER_FORMAT
, $header );
2830 if ($fileNameLength)
2833 $bytesRead = $self->fh()->read( $fileName, $fileNameLength );
2834 if ( $bytesRead != $fileNameLength )
2836 return _ioError
("reading local file name");
2838 $self->fileName($fileName);
2841 if ($extraFieldLength)
2844 $self->fh()->read( $self->{'localExtraField'}, $extraFieldLength );
2845 if ( $bytesRead != $extraFieldLength )
2847 return _ioError
("reading local extra field");
2851 $self->{'dataOffset'} = $self->fh()->tell();
2853 if ( $self->hasDataDescriptor() )
2856 # Read the crc32, compressedSize, and uncompressedSize from the
2857 # extended data descriptor.
2858 # Skip over the compressed file data (assumes that EOCD compressedSize
2860 $self->fh()->seek( $self->{'compressedSize'}, IO
::Seekable
::SEEK_CUR
)
2861 or return _ioError
("seeking to extended local header");
2863 my $status = $self->_readDataDescriptor();
2864 return $status unless $status == AZ_OK
;
2868 return _formatError
(
2869 "CRC or size mismatch after reading data descriptor")
2870 if ( $self->{'crc32'} != $crc32
2871 || $self->{'uncompressedSize'} != $uncompressedSize );
2877 # This will read the data descriptor, which is after the end of compressed file
2878 # data in members that that have GPBF_HAS_DATA_DESCRIPTOR_MASK set in their
2880 # The only reliable way to find these is to rely on the EOCD compressedSize.
2881 # Assumes that file is positioned immediately after the compressed data.
2882 # Returns status; sets crc32, compressedSize, and uncompressedSize.
2883 sub _readDataDescriptor
2890 my $uncompressedSize;
2892 my $bytesRead = $self->fh()->read( $signatureData, SIGNATURE_LENGTH
);
2893 return _ioError
("reading header signature")
2894 if $bytesRead != SIGNATURE_LENGTH
;
2895 my $signature = unpack( SIGNATURE_FORMAT
, $signatureData );
2897 # unfortunately, the signature appears to be optional.
2898 if ( $signature == DATA_DESCRIPTOR_SIGNATURE
2899 && ( $signature != $self->{'crc32'} ) )
2901 $bytesRead = $self->fh()->read( $header, DATA_DESCRIPTOR_LENGTH
);
2902 return _ioError
("reading data descriptor")
2903 if $bytesRead != DATA_DESCRIPTOR_LENGTH
;
2905 ( $crc32, $compressedSize, $uncompressedSize ) =
2906 unpack( DATA_DESCRIPTOR_FORMAT
, $header );
2911 $self->fh()->read( $header, DATA_DESCRIPTOR_LENGTH_NO_SIG
);
2912 return _ioError
("reading data descriptor")
2913 if $bytesRead != DATA_DESCRIPTOR_LENGTH_NO_SIG
;
2915 $crc32 = $signature;
2916 ( $compressedSize, $uncompressedSize ) =
2917 unpack( DATA_DESCRIPTOR_FORMAT_NO_SIG
, $header );
2920 $self->{'eocdCrc32'} = $self->{'crc32'}
2921 unless defined( $self->{'eocdCrc32'} );
2922 $self->{'crc32'} = $crc32;
2923 $self->{'compressedSize'} = $compressedSize;
2924 $self->{'uncompressedSize'} = $uncompressedSize;
2929 # Read a Central Directory header. Return AZ_OK on success.
2930 # Assumes that fh is positioned right after the signature.
2932 sub _readCentralDirectoryFileHeader
# Archive::Zip::ZipFileMember
2935 my $fh = $self->fh();
2937 my $bytesRead = $fh->read( $header, CENTRAL_DIRECTORY_FILE_HEADER_LENGTH
);
2938 if ( $bytesRead != CENTRAL_DIRECTORY_FILE_HEADER_LENGTH
)
2940 return _ioError
("reading central dir header");
2942 my ( $fileNameLength, $extraFieldLength, $fileCommentLength );
2944 $self->{'versionMadeBy'}, $self->{'fileAttributeFormat'},
2945 $self->{'versionNeededToExtract'}, $self->{'bitFlag'},
2946 $self->{'compressionMethod'}, $self->{'lastModFileDateTime'},
2947 $self->{'crc32'}, $self->{'compressedSize'},
2948 $self->{'uncompressedSize'}, $fileNameLength,
2949 $extraFieldLength, $fileCommentLength,
2950 $self->{'diskNumberStart'}, $self->{'internalFileAttributes'},
2951 $self->{'externalFileAttributes'}, $self->{'localHeaderRelativeOffset'}
2953 = unpack( CENTRAL_DIRECTORY_FILE_HEADER_FORMAT
, $header );
2955 $self->{'eocdCrc32'} = $self->{'crc32'};
2957 if ($fileNameLength)
2959 $bytesRead = $fh->read( $self->{'fileName'}, $fileNameLength );
2960 if ( $bytesRead != $fileNameLength )
2962 _ioError
("reading central dir filename");
2965 if ($extraFieldLength)
2967 $bytesRead = $fh->read( $self->{'cdExtraField'}, $extraFieldLength );
2968 if ( $bytesRead != $extraFieldLength )
2970 return _ioError
("reading central dir extra field");
2973 if ($fileCommentLength)
2975 $bytesRead = $fh->read( $self->{'fileComment'}, $fileCommentLength );
2976 if ( $bytesRead != $fileCommentLength )
2978 return _ioError
("reading central dir file comment");
2982 # NK 10/21/04: added to avoid problems with manipulated headers
2983 if ( $self->{'uncompressedSize'} != $self->{'compressedSize'}
2984 and $self->{'compressionMethod'} == COMPRESSION_STORED
)
2986 $self->{'uncompressedSize'} = $self->{'compressedSize'};
2989 $self->desiredCompressionMethod( $self->compressionMethod() );
2994 sub rewindData
# Archive::Zip::ZipFileMember
2998 my $status = $self->SUPER::rewindData
(@_);
2999 return $status unless $status == AZ_OK
;
3001 return AZ_IO_ERROR
unless $self->fh();
3003 $self->fh()->clearerr();
3005 # Seek to local file header.
3006 # The only reason that I'm doing this this way is that the extraField
3007 # length seems to be different between the CD header and the LF header.
3008 $status = $self->_seekToLocalHeader();
3009 return $status unless $status == AZ_OK
;
3011 # skip local file header
3012 $status = $self->_skipLocalFileHeader();
3013 return $status unless $status == AZ_OK
;
3015 # Seek to beginning of file data
3016 $self->fh()->seek( $self->dataOffset(), IO
::Seekable
::SEEK_SET
)
3017 or return _ioError
("seeking to beginning of file data");
3022 # Return bytes read. Note that first parameter is a ref to a buffer.
3024 # my ( $bytesRead, $status) = $self->readRawChunk( \$data, $chunkSize );
3025 sub _readRawChunk
# Archive::Zip::ZipFileMember
3027 my ( $self, $dataRef, $chunkSize ) = @_;
3028 return ( 0, AZ_OK
) unless $chunkSize;
3029 my $bytesRead = $self->fh()->read( $$dataRef, $chunkSize )
3030 or return ( 0, _ioError
("reading data") );
3031 return ( $bytesRead, AZ_OK
);
3034 # ----------------------------------------------------------------------
3035 # class Archive::Zip::StringMember ( concrete )
3036 # A Zip member whose data lives in a string
3037 # ----------------------------------------------------------------------
3039 package Archive
::Zip
::StringMember
;
3040 use vars
qw( @ISA );
3041 @ISA = qw ( Archive::Zip::Member );
3043 BEGIN { use Archive::Zip qw( :CONSTANTS :ERROR_CODES ) }
3045 # Create a new string member. Default is COMPRESSION_STORED.
3046 # Can take a ref to a string as well.
3047 sub _newFromString
# Archive::Zip::StringMember
3052 my $self = $class->new(@_);
3053 $self->contents($string);
3054 $self->fileName($name) if defined($name);
3056 # Set the file date to now
3057 $self->setLastModFileDateTimeFromUnix( time() );
3058 $self->unixFileAttributes( $self->DEFAULT_FILE_PERMISSIONS );
3062 sub _become
# Archive::Zip::StringMember
3065 my $newClass = shift;
3066 return $self if ref($self) eq $newClass;
3067 delete( $self->{'contents'} );
3068 return $self->SUPER::_become
($newClass);
3071 # Get or set my contents. Note that we do not call the superclass
3072 # version of this, because it calls us.
3073 sub contents
# Archive::Zip::StringMember
3077 if ( defined($string) )
3079 $self->{'contents'} =
3080 pack( 'C0a*', ( ref($string) eq 'SCALAR' ) ?
$$string : $string );
3081 $self->{'uncompressedSize'} = $self->{'compressedSize'} =
3082 length( $self->{'contents'} );
3083 $self->{'compressionMethod'} = COMPRESSION_STORED
;
3085 return $self->{'contents'};
3088 # Return bytes read. Note that first parameter is a ref to a buffer.
3090 # my ( $bytesRead, $status) = $self->readRawChunk( \$data, $chunkSize );
3091 sub _readRawChunk
# Archive::Zip::StringMember
3093 my ( $self, $dataRef, $chunkSize ) = @_;
3094 $$dataRef = substr( $self->contents(), $self->_readOffset(), $chunkSize );
3095 return ( length($$dataRef), AZ_OK
);
3102 # vim: ts=4 sw=4 tw=80 wrap