Importing Archive::Zip 1.16
[archive-zip.git] / lib / Archive / Zip.pm
blob11bde9a89ac3f8db901a2dc411aa2d4aab383771
1 #! perl -w
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
6 # Perl itself.
8 # ----------------------------------------------------------------------
9 # class Archive::Zip
10 # Note that the package Archive::Zip exists only for exporting and
11 # sharing constants. Everything else is in another package
12 # in this file.
13 # Creation of a new Archive::Zip object actually creates a new object
14 # of class Archive::Zip::Archive.
15 # ----------------------------------------------------------------------
17 package Archive::Zip;
18 require 5.003_96;
19 use strict;
21 use Carp();
22 use IO::File();
23 use IO::Seekable();
24 use Compress::Zlib();
25 use File::Spec 0.8 ();
26 use File::Temp();
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.
35 $ChunkSize = 32768;
37 $ErrorHandler = \&Carp::carp;
39 # BEGIN block is necessary here so that other modules can use the constants.
40 BEGIN
42 require Exporter;
44 $VERSION = "1.16";
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
52 IFA_BINARY_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
68 AZ_IO_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
81 _asZipDirName);
83 @EXPORT_OK = ('computeCRC32');
84 %EXPORT_TAGS = (
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',
98 'MISC_CONSTANTS'
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 ---------------------
113 # File types
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;
151 # compression method
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
219 my $class = shift;
220 return $class->ZIPARCHIVECLASS->new(@_);
223 sub computeCRC32 # Archive::Zip
225 my $data = shift;
226 $data = shift if ref($data); # allow calling as an obj method
227 my $crc = shift;
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:', @_, ':', $! );
282 return AZ_IO_ERROR;
285 # This is called on generic errors.
286 sub _error # Archive::Zip
288 shift if ref( $_[0] );
289 _printError( 'error:', @_ );
290 return AZ_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
303 my $fh = shift;
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
312 my $fh = shift;
314 if ( UNIVERSAL::isa( $fh, 'IO::Scalar' ) )
316 return 0;
318 elsif ( UNIVERSAL::isa( $fh, 'IO::String' ) )
320 return 1;
322 elsif ( UNIVERSAL::can( $fh, 'stat' ) )
324 return -f $fh;
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
336 my $fd = shift;
337 my $status = 1;
338 my $handle;
340 if ( ref($fd) )
342 if ( UNIVERSAL::isa( $fd, 'IO::Scalar' )
343 or UNIVERSAL::isa( $fd, 'IO::String' ) )
345 $handle = $fd;
347 elsif ( UNIVERSAL::isa( $fd, 'IO::Handle' )
348 or UNIVERSAL::isa( $fd, 'GLOB' ) )
350 $handle = IO::File->new();
351 $status = $handle->fdopen( $fd, @_ );
353 else
355 $handle = $fd;
358 else
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
374 my $fh = shift;
375 my $fileName = shift;
376 my $expectedSignature = shift; # optional
378 my $signatureData;
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 );
383 my $status = AZ_OK;
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) )
396 $errmsg .=
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
416 my $dir = shift;
417 my ( $fh, $filename ) = File::Temp::tempfile(
418 SUFFIX => '.zip',
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.
436 # input output
437 # . ('.') '.'
438 # ./a ('a') a
439 # ./a/b ('a','b') a/b
440 # ./a/b/ ('a','b') a/b
441 # 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
447 my $name = shift;
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
467 my $volume = shift;
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;
476 return $localName;
479 # ----------------------------------------------------------------------
480 # class Archive::Zip::Archive (concrete)
481 # Generic ZIP archive.
482 # ----------------------------------------------------------------------
483 package Archive::Zip::Archive;
484 use File::Path;
485 use File::Find();
486 use File::Spec();
487 use File::Copy();
488 use File::Basename;
489 use Cwd;
491 use vars qw( @ISA );
492 @ISA = qw( Archive::Zip );
494 BEGIN
496 use Archive::Zip qw( :CONSTANTS :ERROR_CODES :PKZIP_CONSTANTS
497 :UTILITY_METHODS );
500 # Note that this returns undef on read errors, else new zip object.
502 sub new # Archive::Zip::Archive
504 my $class = shift;
505 my $self = bless( {
506 'diskNumber' => 0,
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' => '',
515 'eocdOffset' => 0,
516 'fileName' => ''
518 $class
520 $self->{'members'} = [];
521 if (@_)
523 my $status = $self->read(@_);
524 return $status == AZ_OK ? $self : undef;
526 return $self;
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
541 my $self = shift;
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;
553 return undef;
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
594 my $self = shift;
595 my $comment = $self->{'zipfileComment'};
596 if (@_)
598 $self->{'zipfileComment'} = pack( 'C0a*', shift () ); # avoid unicode
600 return $comment;
603 sub eocdOffset # Archive::Zip::Archive
605 shift->{'eocdOffset'};
608 # Return the name of the file last read.
609 sub fileName # Archive::Zip::Archive
611 shift->{'fileName'};
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;
621 return $member;
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;
630 my @newMembers =
631 map { ( $_ == $oldMember ) ? $newMember : $_ } $self->members();
632 $self->{'members'} = \@newMembers;
633 return $oldMember;
636 sub extractMember # Archive::Zip::Archive
638 my $self = shift;
639 my $member = shift;
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, '' );
650 else
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 )
659 mkpath($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;
665 return $rc;
668 sub extractMemberWithoutPaths # Archive::Zip::Archive
670 my $self = shift;
671 my $member = shift;
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();
676 my $name = shift;
677 unless ($name)
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;
685 return $rc;
688 sub addMember # Archive::Zip::Archive
690 my ( $self, $newMember ) = @_;
691 push ( @{ $self->{'members'} }, $newMember ) if $newMember;
692 return $newMember;
695 sub addFile # Archive::Zip::Archive
697 my $self = shift;
698 my $fileName = shift;
699 my $newName = shift;
700 my $newMember = $self->ZIPMEMBERCLASS->newFromFile( $fileName, $newName );
701 $self->addMember($newMember) if defined($newMember);
702 return $newMember;
705 sub addString # Archive::Zip::Archive
707 my $self = shift;
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);
717 return $newMember;
720 # add either a file or a directory.
722 sub addFileOrDirectory
724 my ( $self, $name, $newName ) = @_;
725 if ( -f $name )
727 ( $newName =~ s{/$}{} ) if $newName;
728 return $self->addFile( $name, $newName );
730 elsif ( -d $name )
732 ( $newName =~ s{[^/]$}{&/} ) if $newName;
733 return $self->addDirectory( $name, $newName );
735 else
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
751 my $self = shift;
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 );
765 $fh->close();
766 $fh = undef;
768 return $retval;
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
775 my $self = shift;
776 my $fh = shift;
777 return _error('No filehandle given') unless $fh;
778 return _ioError('filehandle not open') unless $fh->opened();
780 my $fhIsSeekable = @_ ? shift: _isSeekable($fh);
781 _binmode($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 );
790 $member->endRead();
791 return $retval if $retval != AZ_OK;
792 $offset += $member->_localHeaderSize() + $member->_writeOffset();
793 $offset += $member->hasDataDescriptor()
794 ? DATA_DESCRIPTOR_LENGTH + SIGNATURE_LENGTH
795 : 0;
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
808 my $self = shift;
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
817 my $self = shift;
818 my $zipName = shift;
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);
827 $fh->close();
828 $fh = undef;
830 if ( $status != AZ_OK )
832 unlink($tempName);
833 _printError("Can't write to $tempName");
834 return $status;
837 my $err;
839 # rename the zip
840 if ( -f $zipName && !rename( $zipName, $backupName ) )
842 $err = $!;
843 unlink($tempName);
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 ) )
850 $err = $!;
851 rename( $backupName, $zipName );
852 unlink($tempName);
853 return _error( "Can't move $tempName to $zipName", $err );
856 # unlink the backup
857 if ( -f $backupName && !unlink($backupName) )
859 $err = $!;
860 return _error( "Can't unlink $backupName", $err );
863 return AZ_OK;
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() );
886 my $header = pack(
887 END_OF_CENTRAL_DIRECTORY_FORMAT,
888 0, # {'diskNumber'},
889 0, # {'diskNumberWithStartOfCentralDirectory'},
890 $self->numberOfMembers(), # {'numberOfCentralDirectoriesOnThisDisk'},
891 $self->numberOfMembers(), # {'numberOfCentralDirectories'},
892 $self->_writeEOCDOffset() - $self->_writeCentralDirectoryOffset(),
893 $self->_writeCentralDirectoryOffset(),
894 $zipfileCommentLength
896 $fh->print($header)
897 or return _ioError('writing EOCD header');
898 if ($zipfileCommentLength)
900 $fh->print( $self->zipfileComment() )
901 or return _ioError('writing zipfile comment');
903 return AZ_OK;
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');
917 else
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
934 my $self = shift;
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;
943 $fh->close();
944 $self->{'fileName'} = $fileName;
945 return AZ_OK;
948 sub readFromFileHandle # Archive::Zip::Archive
950 my $self = shift;
951 my $fh = shift;
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();
957 _binmode($fh);
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
979 # This should be 0
980 $self->{'eocdOffset'} = $eocdPosition - $self->centralDirectorySize() # here
981 - $self->centralDirectoryOffsetWRTStartingDiskNumber();
983 for ( ; ; )
985 my $newMember =
986 $self->ZIPMEMBERCLASS->_newFromZipFile( $fh, $fileName,
987 $self->eocdOffset() );
988 my $signature;
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 );
1000 return AZ_OK;
1003 # Read EOCD, starting from position before signature.
1004 # Return AZ_OK on success.
1005 sub _readEndOfCentralDirectory # Archive::Zip::Archive
1007 my $self = shift;
1008 my $fh = shift;
1010 # Skip past signature
1011 $fh->seek( SIGNATURE_LENGTH, IO::Seekable::SEEK_CUR )
1012 or return _ioError("Can't seek past EOCD signature");
1014 my $header = '';
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;
1042 return AZ_OK;
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
1050 my $self = shift;
1051 my $fh = shift;
1052 my $data = '';
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");
1062 my $seekOffset = 0;
1063 my $pos = -1;
1064 for ( ; ; )
1066 $seekOffset += 512;
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 );
1076 last
1077 if ( $pos >= 0
1078 or $seekOffset == $fileLength
1079 or $seekOffset >= $Archive::Zip::ChunkSize );
1082 if ( $pos >= 0 )
1084 $fh->seek( $pos - $seekOffset, IO::Seekable::SEEK_CUR )
1085 or return _ioError("seeking to EOCD");
1086 return AZ_OK;
1088 else
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.
1098 sub _untaintDir
1100 my $dir = shift;
1101 $dir =~ m/\A(.+)\z/s;
1102 return $1;
1105 sub addTree # Archive::Zip::Archive
1107 my $self = shift;
1108 my $root = shift or return _error("root arg missing in call to addTree()");
1109 my $dest = shift;
1110 $dest = '' unless defined($dest);
1111 my $pred = shift || sub { -r };
1112 my @files;
1113 my $startDir = _untaintDir( cwd() );
1115 return _error( 'undef returned by _untaintDir on cwd ', cwd() )
1116 unless $startDir;
1118 # This avoids chdir'ing in Find, in a way compatible with older
1119 # versions of File::Find.
1120 my $wanted = sub {
1121 local $main::_ = $File::Find::name;
1122 my $dir = _untaintDir($File::Find::dir);
1123 chdir($startDir);
1124 push ( @files, $File::Find::name ) if (&$pred);
1125 chdir($dir);
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
1144 my $member =
1145 $isDir
1146 ? $self->addDirectory( $fileName, $archiveName )
1147 : $self->addFile( $fileName, $archiveName );
1148 return _error("add $fileName failed in addTree()") if !$member;
1150 return AZ_OK;
1153 sub addTreeMatching # Archive::Zip::Archive
1155 my $self = shift;
1156 my $root = shift
1157 or return _error("root arg missing in call to addTreeMatching()");
1158 my $dest = shift;
1159 $dest = '' unless defined($dest);
1160 my $pattern = shift
1161 or return _error("pattern missing in call to addTreeMatching()");
1162 my $pred = shift;
1163 my $matcher =
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
1175 my $self = shift;
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;
1193 return AZ_OK;
1196 # $zip->updateMember( $memberOrName, $fileName );
1197 # Returns (possibly updated) member, if any; undef on errors.
1199 sub updateMember # Archive::Zip::Archive
1201 my $self = shift;
1202 my $oldMember = shift;
1203 my $fileName = shift;
1205 if ( !defined($fileName) )
1207 _error("updateMember(): missing fileName argument");
1208 return undef;
1211 my @newStat = stat($fileName);
1212 if ( !@newStat )
1214 _ioError("Can't stat $fileName");
1215 return undef;
1218 my $isDir = -d _;
1220 my $memberName;
1222 if ( ref($oldMember) )
1224 $memberName = $oldMember->fileName();
1226 else
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()");
1247 return undef;
1250 # replace old member or append new one
1251 if ( defined($oldMember) )
1253 $self->replaceMember( $oldMember, $newMember );
1255 else { $self->addMember($newMember); }
1257 return $newMember;
1260 return $oldMember;
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
1273 my $self = shift;
1274 my $root = shift
1275 or return _error("root arg missing in call to updateTree()");
1276 my $dest = shift;
1277 $dest = '' unless defined($dest);
1278 $dest = _asZipDirName( $dest, 1 );
1279 my $pred = shift || sub { -r };
1280 my $mirror = shift;
1282 my $rootZipName = _asZipDirName( $root, 1 ); # with trailing slash
1283 my $pattern = $rootZipName eq './' ? '^' : "^\Q$rootZipName\E";
1285 my @files;
1286 my $startDir = _untaintDir( cwd() );
1288 return _error( 'undef returned by _untaintDir on cwd ', cwd() )
1289 unless $startDir;
1291 # This avoids chdir'ing in Find, in a way compatible with older
1292 # versions of File::Find.
1293 my $wanted = sub {
1294 local $main::_ = $File::Find::name;
1295 my $dir = _untaintDir($File::Find::dir);
1296 chdir($startDir);
1297 push ( @files, $File::Find::name ) if (&$pred);
1298 chdir($dir);
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.
1306 my %done;
1307 foreach my $fileName (@files)
1309 my @newStat = stat($fileName);
1310 my $isDir = -d _;
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.
1326 if ($mirror)
1328 foreach my $member ( $self->members() )
1330 $self->removeMember($member)
1331 unless $done{ $member->fileName() };
1335 return AZ_OK;
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 );
1346 BEGIN
1348 use Archive::Zip qw( :CONSTANTS :MISC_CONSTANTS :ERROR_CODES
1349 :PKZIP_CONSTANTS :UTILITY_METHODS );
1352 use Time::Local();
1353 use Compress::Zlib qw( Z_OK Z_STREAM_END MAX_WBITS );
1354 use File::Path;
1355 use File::Basename;
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
1373 my $class = shift;
1374 my $self = $class->ZIPFILEMEMBERCLASS->_newFromZipFile(@_);
1375 return $self;
1378 sub newFromString # Archive::Zip::Member
1380 my $class = shift;
1381 my $self = $class->STRINGMEMBERCLASS->_newFromString(@_);
1382 return $self;
1385 sub newFromFile # Archive::Zip::Member
1387 my $class = shift;
1388 my $self = $class->NEWFILEMEMBERCLASS->_newFromFileNamed(@_);
1389 return $self;
1392 sub newDirectoryNamed # Archive::Zip::Member
1394 my $class = shift;
1395 my $self = $class->DIRECTORYMEMBERCLASS->_newNamed(@_);
1396 return $self;
1399 sub new # Archive::Zip::Member
1401 my $class = shift;
1402 my $self = {
1403 'lastModFileDateTime' => 0,
1404 'fileAttributeFormat' => FA_UNIX,
1405 'versionMadeBy' => 20,
1406 'versionNeededToExtract' => 20,
1407 'bitFlag' => 0,
1408 'compressionMethod' => COMPRESSION_STORED,
1409 'desiredCompressionMethod' => COMPRESSION_STORED,
1410 'desiredCompressionLevel' => COMPRESSION_LEVEL_NONE,
1411 'internalFileAttributes' => 0,
1412 'externalFileAttributes' => 0, # set later
1413 'fileName' => '',
1414 'cdExtraField' => '',
1415 'localExtraField' => '',
1416 'fileComment' => '',
1417 'crc32' => 0,
1418 'compressedSize' => 0,
1419 'uncompressedSize' => 0,
1422 bless( $self, $class );
1423 $self->unixFileAttributes( $self->DEFAULT_FILE_PERMISSIONS );
1424 return $self;
1427 sub _becomeDirectoryIfNecessary # Archive::Zip::Member
1429 my $self = shift;
1430 $self->_become(DIRECTORYMEMBERCLASS)
1431 if $self->isDirectory();
1432 return $self;
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
1448 ( $#_ > 0 )
1449 ? ( $_[0]->{'fileAttributeFormat'} = $_[1] )
1450 : $_[0]->{'fileAttributeFormat'};
1453 sub versionNeededToExtract # Archive::Zip::Member
1455 shift->{'versionNeededToExtract'};
1458 sub bitFlag # Archive::Zip::Member
1460 shift->{'bitFlag'};
1463 sub compressionMethod # Archive::Zip::Member
1465 shift->{'compressionMethod'};
1468 sub desiredCompressionMethod # Archive::Zip::Member
1470 my $self = shift;
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
1490 my $self = shift;
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
1506 my $self = shift;
1507 my $newName = shift;
1508 if ($newName)
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
1520 return $1;
1523 sub lastModTime # Archive::Zip::Member
1525 my $self = shift;
1526 return _dosToUnixTime( $self->lastModFileDateTime() );
1529 sub setLastModFileDateTimeFromUnix # Archive::Zip::Member
1531 my $self = shift;
1532 my $time_t = shift;
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
1548 my $dt = shift;
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 );
1559 # catch errors
1560 my $time_t =
1561 eval { Time::Local::timelocal( $sec, $min, $hour, $mday, $mon, $year ); };
1562 return time() if ($@);
1563 return $time_t;
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
1577 # NOT A METHOD!
1578 sub _mapPermissionsFromUnix # Archive::Zip::Member
1580 my $perms = shift;
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
1599 my $self = shift;
1601 my $format = $self->{'fileAttributeFormat'};
1602 my $attribs = $self->{'externalFileAttributes'};
1604 my $mode = 0;
1606 if ( $format == FA_AMIGA )
1608 $attribs = $attribs >> 17 & 7; # Amiga RWE bits
1609 $mode = $attribs << 6 | $attribs << 3 | $attribs;
1610 return $mode;
1613 if ( $format == FA_THEOS )
1615 $attribs &= 0xF1FFFFFF;
1616 if ( ( $attribs & 0xF0000000 ) != 0x40000000 )
1618 $attribs &= 0x01FFFFFF; # not a dir, mask all ftype bits
1620 else
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
1661 # bits (see below).
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;
1674 return $mode;
1677 sub unixFileAttributes # Archive::Zip::Member
1679 my $self = shift;
1680 my $oldPerms = $self->_mapPermissionsToUnix();
1681 if (@_)
1683 my $perms = shift;
1684 if ( $self->isDirectory() )
1686 $perms &= ~FILE_ATTRIB;
1687 $perms |= DIRECTORY_ATTRIB;
1689 else
1691 $perms &= ~DIRECTORY_ATTRIB;
1692 $perms |= FILE_ATTRIB;
1694 $self->{'externalFileAttributes'} = _mapPermissionsFromUnix($perms);
1696 return $oldPerms;
1699 sub localExtraField # Archive::Zip::Member
1701 ( $#_ > 0 )
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
1713 my $self = shift;
1714 return $self->localExtraField() . $self->cdExtraField();
1717 sub fileComment # Archive::Zip::Member
1719 ( $#_ > 0 )
1720 ? ( $_[0]->{'fileComment'} = pack( 'C0a*', $_[1] ) )
1721 : $_[0]->{'fileComment'};
1724 sub hasDataDescriptor # Archive::Zip::Member
1726 my $self = shift;
1727 if (@_)
1729 my $shouldHave = shift;
1730 if ($shouldHave)
1732 $self->{'bitFlag'} |= GPBF_HAS_DATA_DESCRIPTOR_MASK;
1734 else
1736 $self->{'bitFlag'} &= ~GPBF_HAS_DATA_DESCRIPTOR_MASK;
1739 return $self->{'bitFlag'} & GPBF_HAS_DATA_DESCRIPTOR_MASK;
1742 sub crc32 # Archive::Zip::Member
1744 shift->{'crc32'};
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
1769 my $self = shift;
1770 my $bit = $self->internalFileAttributes() & IFA_TEXT_FILE_MASK;
1771 if (@_)
1773 my $flag = shift;
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
1783 my $self = shift;
1784 my $bit = $self->internalFileAttributes() & IFA_TEXT_FILE_MASK;
1785 if (@_)
1787 my $flag = shift;
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
1797 my $self = shift;
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);
1804 $fh->close();
1805 utime( $self->lastModTime(), $self->lastModTime(), $name );
1806 return $retval;
1809 sub isDirectory # Archive::Zip::Member
1811 return 0;
1814 sub externalFileName # Archive::Zip::Member
1816 return undef;
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
1860 my $self = shift;
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
1868 my $self = shift;
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
1878 my $time_t = shift;
1879 my ( $sec, $min, $hour, $mday, $mon, $year ) = localtime($time_t);
1880 my $dt = 0;
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 );
1887 return $dt;
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
1896 my $self = shift;
1897 my $fh = shift;
1899 my $signatureData = pack( SIGNATURE_FORMAT, LOCAL_FILE_HEADER_SIGNATURE );
1900 $fh->print($signatureData)
1901 or return _ioError("writing local header signature");
1903 my $header = pack(
1904 LOCAL_FILE_HEADER_FORMAT,
1905 $self->versionNeededToExtract(),
1906 $self->bitFlag(),
1907 $self->desiredCompressionMethod(),
1908 $self->lastModFileDateTime(),
1909 $self->crc32(),
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");
1928 return AZ_OK;
1931 sub _writeCentralDirectoryFileHeader # Archive::Zip::Member
1933 my $self = shift;
1934 my $fh = shift;
1936 my $sigData =
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() );
1945 my $header = pack(
1946 CENTRAL_DIRECTORY_FILE_HEADER_FORMAT,
1947 $self->versionMadeBy(),
1948 $self->fileAttributeFormat(),
1949 $self->versionNeededToExtract(),
1950 $self->bitFlag(),
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(), #
1956 $fileNameLength,
1957 $extraFieldLength,
1958 $fileCommentLength,
1959 0, # {'diskNumberStart'},
1960 $self->internalFileAttributes(),
1961 $self->externalFileAttributes(),
1962 $self->writeLocalHeaderRelativeOffset()
1965 $fh->print($header)
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");
1983 return AZ_OK;
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
1993 my $self = shift;
1994 my $fh = shift;
1995 my $header = pack(
1996 SIGNATURE_FORMAT . DATA_DESCRIPTOR_FORMAT,
1997 DATA_DESCRIPTOR_SIGNATURE,
1998 $self->crc32(),
1999 $self->_writeOffset(), # compressed size
2000 $self->uncompressedSize()
2003 $fh->print($header)
2004 or return _ioError("writing data descriptor");
2005 return AZ_OK;
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
2013 my $self = shift;
2014 my $fh = shift;
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");
2021 my $header = pack(
2022 LOCAL_FILE_HEADER_FORMAT,
2023 $self->versionNeededToExtract(),
2024 $self->bitFlag(),
2025 $self->desiredCompressionMethod(),
2026 $self->lastModFileDateTime(),
2027 $self->crc32(),
2028 $self->_writeOffset(), # compressed size
2029 $self->uncompressedSize(),
2030 length( $self->fileName() ),
2031 length( $self->localExtraField() )
2034 $fh->print($header)
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");
2039 return AZ_OK;
2042 sub readChunk # Archive::Zip::Member
2044 my ( $self, $chunkSize ) = @_;
2046 if ( $self->readIsDone() )
2048 $self->endRead();
2049 my $dummy = '';
2050 return ( \$dummy, AZ_STREAM_END );
2053 $chunkSize = $Archive::Zip::ChunkSize if not defined($chunkSize);
2054 $chunkSize = $self->_readDataRemaining()
2055 if $chunkSize > $self->_readDataRemaining();
2057 my $buffer = '';
2058 my $outputRef;
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);
2073 $self->endRead()
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
2083 my $self = shift;
2084 return $self->_subclassResponsibility();
2087 # A place holder to catch rewindData errors if someone ignores
2088 # the error code.
2089 sub _noChunk # Archive::Zip::Member
2091 my $self = shift;
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 )
2111 my $extraOutput;
2112 ( $extraOutput, $status ) = $self->_deflater()->flush();
2113 $out .= $extraOutput;
2114 $self->endRead();
2115 return ( \$out, AZ_STREAM_END );
2117 elsif ( $status == Z_OK )
2119 return ( \$out, AZ_OK );
2121 else
2123 $self->endRead();
2124 my $retval = _error( 'deflate error', $status );
2125 my $dummy = '';
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);
2135 my $retval;
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 );
2142 else
2144 $retval = _error( 'inflate error', $status );
2145 my $dummy = '';
2146 return ( \$dummy, $retval );
2150 sub rewindData # Archive::Zip::Member
2152 my $self = shift;
2153 my $status;
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');
2196 else
2198 return _error(
2199 sprintf(
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;
2214 return AZ_OK;
2217 sub endRead # Archive::Zip::Member
2219 my $self = shift;
2220 delete $self->{'inflater'};
2221 delete $self->{'deflater'};
2222 $self->{'dataEnded'} = 1;
2223 $self->{'readDataRemaining'} = 0;
2224 return AZ_OK;
2227 sub readIsDone # Archive::Zip::Member
2229 my $self = shift;
2230 return ( $self->_dataEnded() or !$self->_readDataRemaining() );
2233 sub contents # Archive::Zip::Member
2235 my $self = shift;
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
2246 else
2248 my $oldCompression =
2249 $self->desiredCompressionMethod(COMPRESSION_STORED);
2250 my $status = $self->rewindData(@_);
2251 if ( $status != AZ_OK )
2253 $self->endRead();
2254 return $status;
2256 my $retval = '';
2257 while ( $status == AZ_OK )
2259 my $ref;
2260 ( $ref, $status ) = $self->readChunk( $self->_readDataRemaining() );
2262 # did we get it in one chunk?
2263 if ( length($$ref) == $self->uncompressedSize() )
2265 $retval = $$ref;
2267 else { $retval .= $$ref }
2269 $self->desiredCompressionMethod($oldCompression);
2270 $self->endRead();
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
2279 my $self = shift;
2280 return _error("encryption unsupported") if $self->isEncrypted();
2281 my $fh = shift;
2282 _binmode($fh);
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);
2287 $self->endRead();
2288 return $status;
2291 # write local header and data stream to file handle
2292 sub _writeToFileHandle # Archive::Zip::Member
2294 my $self = shift;
2295 my $fh = shift;
2296 my $fhIsSeekable = shift;
2297 my $offset = 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);
2339 return $status;
2342 # Copy my (possibly compressed) data to given file handle.
2343 # Returns C<AZ_OK> on success
2344 sub _writeData # Archive::Zip::Member
2346 my $self = shift;
2347 my $writeFh = shift;
2349 return AZ_OK if ( $self->uncompressedSize() == 0 );
2350 my $status;
2351 my $chunkSize = $Archive::Zip::ChunkSize;
2352 while ( $self->_readDataRemaining() > 0 )
2354 my $outRef;
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();
2367 return AZ_OK;
2370 # Return true if I depend on the named file
2371 sub _usesFileNamed
2373 return 0;
2376 # ----------------------------------------------------------------------
2377 # class Archive::Zip::DirectoryMember
2378 # ----------------------------------------------------------------------
2380 package Archive::Zip::DirectoryMember;
2381 use File::Path;
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
2389 my $class = shift;
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);
2396 if ( -e $fileName )
2399 if ( -d _ )
2401 my @stat = stat(_);
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' );
2408 return undef;
2411 else
2413 $self->unixFileAttributes( $self->DEFAULT_DIRECTORY_PERMISSIONS );
2414 $self->setLastModFileDateTimeFromUnix( time() );
2416 return $self;
2419 sub externalFileName # Archive::Zip::DirectoryMember
2421 shift->{'externalFileName'};
2424 sub isDirectory # Archive::Zip::DirectoryMember
2426 return 1;
2429 sub extractToFileNamed # Archive::Zip::DirectoryMember
2431 my $self = shift;
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 );
2436 return AZ_OK;
2439 sub fileName # Archive::Zip::DirectoryMember
2441 my $self = shift;
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...
2449 sub contents
2451 return wantarray ? ( undef, AZ_OK ) : undef;
2454 # ----------------------------------------------------------------------
2455 # class Archive::Zip::FileMember
2456 # Base class for classes that have file handles
2457 # to external files
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
2473 my $self = shift;
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
2482 my $self = shift;
2483 $self->_openFile()
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
2491 my $self = shift;
2492 my ( $status, $fh ) = _newFileHandle( $self->externalFileName(), 'r' );
2493 if ( !$status )
2495 _ioError( "Can't open", $self->externalFileName() );
2496 return undef;
2498 $self->{'fh'} = $fh;
2499 _binmode($fh);
2500 return $fh;
2503 # Make sure I close my file handle
2504 sub endRead # Archive::Zip::FileMember
2506 my $self = shift;
2507 undef $self->{'fh'}; # _closeFile();
2508 return $self->SUPER::endRead(@_);
2511 sub _become # Archive::Zip::FileMember
2513 my $self = shift;
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
2535 my $class = shift;
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;
2544 my @stat = stat(_);
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 _ );
2552 return $self;
2555 sub rewindData # Archive::Zip::NewFileMember
2557 my $self = shift;
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() );
2566 return AZ_OK;
2569 # Return bytes read. Note that first parameter is a ref to a buffer.
2570 # my $data;
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
2584 my $self = shift;
2585 my $name = shift; # local FS name
2586 if ( File::Spec->rel2abs($name) eq
2587 File::Spec->rel2abs( $self->externalFileName() ) and -r $name )
2589 return AZ_OK;
2591 else
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 );
2606 BEGIN
2608 use Archive::Zip qw( :CONSTANTS :ERROR_CODES :PKZIP_CONSTANTS
2609 :UTILITY_METHODS );
2612 # Create a new Archive::Zip::ZipFileMember
2613 # given a filename and optional open file handle
2615 sub _newFromZipFile # Archive::Zip::ZipFileMember
2617 my $class = shift;
2618 my $fh = shift;
2619 my $externalFileName = shift;
2620 my $possibleEocdOffset = shift; # normally 0
2622 my $self = $class->new(
2623 'crc32' => 0,
2624 'diskNumberStart' => 0,
2625 'localHeaderRelativeOffset' => 0,
2626 'dataOffset' => 0, # localHeaderRelativeOffset + header length
2629 $self->{'externalFileName'} = $externalFileName;
2630 $self->{'fh'} = $fh;
2631 $self->{'possibleEocdOffset'} = $possibleEocdOffset;
2632 return $self;
2635 sub isDirectory # Archive::Zip::ZipFileMember
2637 my $self = shift;
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.
2645 # Returns status.
2647 sub _seekToLocalHeader # Archive::Zip::ZipFileMember
2649 my $self = shift;
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;
2659 my $status;
2660 my $signature;
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'} )
2673 $status =
2674 $self->_seekToLocalHeader(
2675 $self->localHeaderRelativeOffset() + $self->{'possibleEocdOffset'},
2676 $where );
2677 if ( $status == AZ_OK )
2679 $self->{'localHeaderRelativeOffset'} +=
2680 $self->{'possibleEocdOffset'};
2681 $self->{'possibleEocdOffset'} = 0;
2685 return $status;
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
2695 my $self = shift;
2696 my $newClass = shift;
2697 return $self if ref($self) eq $newClass;
2699 my $status = AZ_OK;
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
2737 my $self = shift;
2738 my $header;
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");
2744 my $fileNameLength;
2745 my $extraFieldLength;
2746 my $bitFlag;
2747 ( undef, # $self->{'versionNeededToExtract'},
2748 $bitFlag,
2749 undef, # $self->{'compressionMethod'},
2750 undef, # $self->{'lastModFileDateTime'},
2751 undef, # $crc32,
2752 undef, # $compressedSize,
2753 undef, # $uncompressedSize,
2754 $fileNameLength,
2755 $extraFieldLength )
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)
2766 $bytesRead =
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
2783 # was correct)
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'} );
2801 return AZ_OK;
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
2811 my $self = shift;
2812 my $header;
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");
2818 my $fileNameLength;
2819 my $crc32;
2820 my $compressedSize;
2821 my $uncompressedSize;
2822 my $extraFieldLength;
2823 ( $self->{'versionNeededToExtract'}, $self->{'bitFlag'},
2824 $self->{'compressionMethod'}, $self->{'lastModFileDateTime'},
2825 $crc32, $compressedSize,
2826 $uncompressedSize, $fileNameLength,
2827 $extraFieldLength )
2828 = unpack( LOCAL_FILE_HEADER_FORMAT, $header );
2830 if ($fileNameLength)
2832 my $fileName;
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)
2843 $bytesRead =
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
2859 # was correct)
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;
2866 else
2868 return _formatError(
2869 "CRC or size mismatch after reading data descriptor")
2870 if ( $self->{'crc32'} != $crc32
2871 || $self->{'uncompressedSize'} != $uncompressedSize );
2874 return AZ_OK;
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
2879 # bitFlag.
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
2885 my $self = shift;
2886 my $signatureData;
2887 my $header;
2888 my $crc32;
2889 my $compressedSize;
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 );
2908 else
2910 $bytesRead =
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;
2926 return AZ_OK;
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
2934 my $self = shift;
2935 my $fh = $self->fh();
2936 my $header = '';
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() );
2991 return AZ_OK;
2994 sub rewindData # Archive::Zip::ZipFileMember
2996 my $self = shift;
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");
3019 return AZ_OK;
3022 # Return bytes read. Note that first parameter is a ref to a buffer.
3023 # my $data;
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
3049 my $class = shift;
3050 my $string = shift;
3051 my $name = shift;
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 );
3059 return $self;
3062 sub _become # Archive::Zip::StringMember
3064 my $self = shift;
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
3075 my $self = shift;
3076 my $string = shift;
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.
3089 # my $data;
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 );
3099 __END__
3102 # vim: ts=4 sw=4 tw=80 wrap