1 package Archive::Zip::ZipFileMember;
4 use vars qw( $VERSION @ISA );
8 @ISA = qw ( Archive::Zip::FileMember );
18 # Create a new Archive::Zip::ZipFileMember
19 # given a filename and optional open file handle
24 my $externalFileName = shift;
25 my $possibleEocdOffset = shift; # normally 0
27 my $self = $class->new(
29 'diskNumberStart' => 0,
30 'localHeaderRelativeOffset' => 0,
31 'dataOffset' => 0, # localHeaderRelativeOffset + header length
34 $self->{'externalFileName'} = $externalFileName;
36 $self->{'possibleEocdOffset'} = $possibleEocdOffset;
42 return ( substr( $self->fileName(), -1, 1 ) eq '/'
43 and $self->uncompressedSize() == 0 );
46 # Seek to the beginning of the local header, just past the signature.
47 # Verify that the local header signature is in fact correct.
48 # Update the localHeaderRelativeOffset if necessary by adding the possibleEocdOffset.
51 sub _seekToLocalHeader {
53 my $where = shift; # optional
54 my $previousWhere = shift; # optional
56 $where = $self->localHeaderRelativeOffset() unless defined($where);
58 # avoid loop on certain corrupt files (from Julian Field)
59 return _formatError("corrupt zip file")
60 if defined($previousWhere) && $where == $previousWhere;
65 $status = $self->fh()->seek( $where, IO::Seekable::SEEK_SET );
66 return _ioError("seeking to local header") unless $status;
68 ( $status, $signature ) =
69 _readSignature( $self->fh(), $self->externalFileName(),
70 LOCAL_FILE_HEADER_SIGNATURE );
71 return $status if $status == AZ_IO_ERROR;
73 # retry with EOCD offset if any was given.
74 if ( $status == AZ_FORMAT_ERROR && $self->{'possibleEocdOffset'} ) {
75 $status = $self->_seekToLocalHeader(
76 $self->localHeaderRelativeOffset() + $self->{'possibleEocdOffset'},
79 if ( $status == AZ_OK ) {
80 $self->{'localHeaderRelativeOffset'} +=
81 $self->{'possibleEocdOffset'};
82 $self->{'possibleEocdOffset'} = 0;
89 # Because I'm going to delete the file handle, read the local file
90 # header if the file handle is seekable. If it isn't, I assume that
91 # I've already read the local header.
92 # Return ( $status, $self )
97 return $self if ref($self) eq $newClass;
101 if ( _isSeekable( $self->fh() ) ) {
102 my $here = $self->fh()->tell();
103 $status = $self->_seekToLocalHeader();
104 $status = $self->_readLocalFileHeader() if $status == AZ_OK;
105 $self->fh()->seek( $here, IO::Seekable::SEEK_SET );
106 return $status unless $status == AZ_OK;
109 delete( $self->{'eocdCrc32'} );
110 delete( $self->{'diskNumberStart'} );
111 delete( $self->{'localHeaderRelativeOffset'} );
112 delete( $self->{'dataOffset'} );
114 return $self->SUPER::_become($newClass);
117 sub diskNumberStart {
118 shift->{'diskNumberStart'};
121 sub localHeaderRelativeOffset {
122 shift->{'localHeaderRelativeOffset'};
126 shift->{'dataOffset'};
129 # Skip local file header, updating only extra field stuff.
130 # Assumes that fh is positioned before signature.
131 sub _skipLocalFileHeader {
134 my $bytesRead = $self->fh()->read( $header, LOCAL_FILE_HEADER_LENGTH );
135 if ( $bytesRead != LOCAL_FILE_HEADER_LENGTH ) {
136 return _ioError("reading local file header");
139 my $extraFieldLength;
142 undef, # $self->{'versionNeededToExtract'},
144 undef, # $self->{'compressionMethod'},
145 undef, # $self->{'lastModFileDateTime'},
147 undef, # $compressedSize,
148 undef, # $uncompressedSize,
151 ) = unpack( LOCAL_FILE_HEADER_FORMAT, $header );
153 if ($fileNameLength) {
154 $self->fh()->seek( $fileNameLength, IO::Seekable::SEEK_CUR )
155 or return _ioError("skipping local file name");
158 if ($extraFieldLength) {
160 $self->fh()->read( $self->{'localExtraField'}, $extraFieldLength );
161 if ( $bytesRead != $extraFieldLength ) {
162 return _ioError("reading local extra field");
166 $self->{'dataOffset'} = $self->fh()->tell();
168 if ( $bitFlag & GPBF_HAS_DATA_DESCRIPTOR_MASK ) {
170 # Read the crc32, compressedSize, and uncompressedSize from the
171 # extended data descriptor, which directly follows the compressed data.
173 # Skip over the compressed file data (assumes that EOCD compressedSize
175 $self->fh()->seek( $self->{'compressedSize'}, IO::Seekable::SEEK_CUR )
176 or return _ioError("seeking to extended local header");
178 # these values should be set correctly from before.
179 my $oldCrc32 = $self->{'eocdCrc32'};
180 my $oldCompressedSize = $self->{'compressedSize'};
181 my $oldUncompressedSize = $self->{'uncompressedSize'};
183 my $status = $self->_readDataDescriptor();
184 return $status unless $status == AZ_OK;
187 "CRC or size mismatch while skipping data descriptor")
188 if ( $oldCrc32 != $self->{'crc32'}
189 || $oldUncompressedSize != $self->{'uncompressedSize'} );
195 # Read from a local file header into myself. Returns AZ_OK if successful.
196 # Assumes that fh is positioned after signature.
197 # Note that crc32, compressedSize, and uncompressedSize will be 0 if
198 # GPBF_HAS_DATA_DESCRIPTOR_MASK is set in the bitFlag.
200 sub _readLocalFileHeader {
203 my $bytesRead = $self->fh()->read( $header, LOCAL_FILE_HEADER_LENGTH );
204 if ( $bytesRead != LOCAL_FILE_HEADER_LENGTH ) {
205 return _ioError("reading local file header");
210 my $uncompressedSize;
211 my $extraFieldLength;
213 $self->{'versionNeededToExtract'}, $self->{'bitFlag'},
214 $self->{'compressionMethod'}, $self->{'lastModFileDateTime'},
215 $crc32, $compressedSize,
216 $uncompressedSize, $fileNameLength,
218 ) = unpack( LOCAL_FILE_HEADER_FORMAT, $header );
220 if ($fileNameLength) {
222 $bytesRead = $self->fh()->read( $fileName, $fileNameLength );
223 if ( $bytesRead != $fileNameLength ) {
224 return _ioError("reading local file name");
226 $self->fileName($fileName);
229 if ($extraFieldLength) {
231 $self->fh()->read( $self->{'localExtraField'}, $extraFieldLength );
232 if ( $bytesRead != $extraFieldLength ) {
233 return _ioError("reading local extra field");
237 $self->{'dataOffset'} = $self->fh()->tell();
239 if ( $self->hasDataDescriptor() ) {
241 # Read the crc32, compressedSize, and uncompressedSize from the
242 # extended data descriptor.
243 # Skip over the compressed file data (assumes that EOCD compressedSize
245 $self->fh()->seek( $self->{'compressedSize'}, IO::Seekable::SEEK_CUR )
246 or return _ioError("seeking to extended local header");
248 my $status = $self->_readDataDescriptor();
249 return $status unless $status == AZ_OK;
253 "CRC or size mismatch after reading data descriptor")
254 if ( $self->{'crc32'} != $crc32
255 || $self->{'uncompressedSize'} != $uncompressedSize );
261 # This will read the data descriptor, which is after the end of compressed file
262 # data in members that that have GPBF_HAS_DATA_DESCRIPTOR_MASK set in their
264 # The only reliable way to find these is to rely on the EOCD compressedSize.
265 # Assumes that file is positioned immediately after the compressed data.
266 # Returns status; sets crc32, compressedSize, and uncompressedSize.
267 sub _readDataDescriptor {
273 my $uncompressedSize;
275 my $bytesRead = $self->fh()->read( $signatureData, SIGNATURE_LENGTH );
276 return _ioError("reading header signature")
277 if $bytesRead != SIGNATURE_LENGTH;
278 my $signature = unpack( SIGNATURE_FORMAT, $signatureData );
280 # unfortunately, the signature appears to be optional.
281 if ( $signature == DATA_DESCRIPTOR_SIGNATURE
282 && ( $signature != $self->{'crc32'} ) )
284 $bytesRead = $self->fh()->read( $header, DATA_DESCRIPTOR_LENGTH );
285 return _ioError("reading data descriptor")
286 if $bytesRead != DATA_DESCRIPTOR_LENGTH;
288 ( $crc32, $compressedSize, $uncompressedSize ) =
289 unpack( DATA_DESCRIPTOR_FORMAT, $header );
293 $self->fh()->read( $header, DATA_DESCRIPTOR_LENGTH_NO_SIG );
294 return _ioError("reading data descriptor")
295 if $bytesRead != DATA_DESCRIPTOR_LENGTH_NO_SIG;
298 ( $compressedSize, $uncompressedSize ) =
299 unpack( DATA_DESCRIPTOR_FORMAT_NO_SIG, $header );
302 $self->{'eocdCrc32'} = $self->{'crc32'}
303 unless defined( $self->{'eocdCrc32'} );
304 $self->{'crc32'} = $crc32;
305 $self->{'compressedSize'} = $compressedSize;
306 $self->{'uncompressedSize'} = $uncompressedSize;
311 # Read a Central Directory header. Return AZ_OK on success.
312 # Assumes that fh is positioned right after the signature.
314 sub _readCentralDirectoryFileHeader {
316 my $fh = $self->fh();
318 my $bytesRead = $fh->read( $header, CENTRAL_DIRECTORY_FILE_HEADER_LENGTH );
319 if ( $bytesRead != CENTRAL_DIRECTORY_FILE_HEADER_LENGTH ) {
320 return _ioError("reading central dir header");
322 my ( $fileNameLength, $extraFieldLength, $fileCommentLength );
324 $self->{'versionMadeBy'},
325 $self->{'fileAttributeFormat'},
326 $self->{'versionNeededToExtract'},
328 $self->{'compressionMethod'},
329 $self->{'lastModFileDateTime'},
331 $self->{'compressedSize'},
332 $self->{'uncompressedSize'},
336 $self->{'diskNumberStart'},
337 $self->{'internalFileAttributes'},
338 $self->{'externalFileAttributes'},
339 $self->{'localHeaderRelativeOffset'}
340 ) = unpack( CENTRAL_DIRECTORY_FILE_HEADER_FORMAT, $header );
342 $self->{'eocdCrc32'} = $self->{'crc32'};
344 if ($fileNameLength) {
345 $bytesRead = $fh->read( $self->{'fileName'}, $fileNameLength );
346 if ( $bytesRead != $fileNameLength ) {
347 _ioError("reading central dir filename");
350 if ($extraFieldLength) {
351 $bytesRead = $fh->read( $self->{'cdExtraField'}, $extraFieldLength );
352 if ( $bytesRead != $extraFieldLength ) {
353 return _ioError("reading central dir extra field");
356 if ($fileCommentLength) {
357 $bytesRead = $fh->read( $self->{'fileComment'}, $fileCommentLength );
358 if ( $bytesRead != $fileCommentLength ) {
359 return _ioError("reading central dir file comment");
363 # NK 10/21/04: added to avoid problems with manipulated headers
364 if ( $self->{'uncompressedSize'} != $self->{'compressedSize'}
365 and $self->{'compressionMethod'} == COMPRESSION_STORED )
367 $self->{'uncompressedSize'} = $self->{'compressedSize'};
370 $self->desiredCompressionMethod( $self->compressionMethod() );
378 my $status = $self->SUPER::rewindData(@_);
379 return $status unless $status == AZ_OK;
381 return AZ_IO_ERROR unless $self->fh();
383 $self->fh()->clearerr();
385 # Seek to local file header.
386 # The only reason that I'm doing this this way is that the extraField
387 # length seems to be different between the CD header and the LF header.
388 $status = $self->_seekToLocalHeader();
389 return $status unless $status == AZ_OK;
391 # skip local file header
392 $status = $self->_skipLocalFileHeader();
393 return $status unless $status == AZ_OK;
395 # Seek to beginning of file data
396 $self->fh()->seek( $self->dataOffset(), IO::Seekable::SEEK_SET )
397 or return _ioError("seeking to beginning of file data");
402 # Return bytes read. Note that first parameter is a ref to a buffer.
404 # my ( $bytesRead, $status) = $self->readRawChunk( \$data, $chunkSize );
406 my ( $self, $dataRef, $chunkSize ) = @_;
407 return ( 0, AZ_OK ) unless $chunkSize;
408 my $bytesRead = $self->fh()->read( $$dataRef, $chunkSize )
409 or return ( 0, _ioError("reading data") );
410 return ( $bytesRead, AZ_OK );