| 5767 |
alewis |
1 |
package Archive::Zip::ZipFileMember;
|
|
|
2 |
|
|
|
3 |
use strict;
|
|
|
4 |
use vars qw( $VERSION @ISA );
|
|
|
5 |
|
|
|
6 |
BEGIN {
|
|
|
7 |
$VERSION = '1.57';
|
|
|
8 |
@ISA = qw ( Archive::Zip::FileMember );
|
|
|
9 |
}
|
|
|
10 |
|
|
|
11 |
use Archive::Zip qw(
|
|
|
12 |
:CONSTANTS
|
|
|
13 |
:ERROR_CODES
|
|
|
14 |
:PKZIP_CONSTANTS
|
|
|
15 |
:UTILITY_METHODS
|
|
|
16 |
);
|
|
|
17 |
|
|
|
18 |
# Create a new Archive::Zip::ZipFileMember
|
|
|
19 |
# given a filename and optional open file handle
|
|
|
20 |
#
|
|
|
21 |
sub _newFromZipFile {
|
|
|
22 |
my $class = shift;
|
|
|
23 |
my $fh = shift;
|
|
|
24 |
my $externalFileName = shift;
|
|
|
25 |
my $possibleEocdOffset = shift; # normally 0
|
|
|
26 |
|
|
|
27 |
my $self = $class->new(
|
|
|
28 |
'crc32' => 0,
|
|
|
29 |
'diskNumberStart' => 0,
|
|
|
30 |
'localHeaderRelativeOffset' => 0,
|
|
|
31 |
'dataOffset' => 0, # localHeaderRelativeOffset + header length
|
|
|
32 |
@_
|
|
|
33 |
);
|
|
|
34 |
$self->{'externalFileName'} = $externalFileName;
|
|
|
35 |
$self->{'fh'} = $fh;
|
|
|
36 |
$self->{'possibleEocdOffset'} = $possibleEocdOffset;
|
|
|
37 |
return $self;
|
|
|
38 |
}
|
|
|
39 |
|
|
|
40 |
sub isDirectory {
|
|
|
41 |
my $self = shift;
|
|
|
42 |
return (substr($self->fileName, -1, 1) eq '/'
|
|
|
43 |
and $self->uncompressedSize == 0);
|
|
|
44 |
}
|
|
|
45 |
|
|
|
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.
|
|
|
49 |
# Returns status.
|
|
|
50 |
|
|
|
51 |
sub _seekToLocalHeader {
|
|
|
52 |
my $self = shift;
|
|
|
53 |
my $where = shift; # optional
|
|
|
54 |
my $previousWhere = shift; # optional
|
|
|
55 |
|
|
|
56 |
$where = $self->localHeaderRelativeOffset() unless defined($where);
|
|
|
57 |
|
|
|
58 |
# avoid loop on certain corrupt files (from Julian Field)
|
|
|
59 |
return _formatError("corrupt zip file")
|
|
|
60 |
if defined($previousWhere) && $where == $previousWhere;
|
|
|
61 |
|
|
|
62 |
my $status;
|
|
|
63 |
my $signature;
|
|
|
64 |
|
|
|
65 |
$status = $self->fh()->seek($where, IO::Seekable::SEEK_SET);
|
|
|
66 |
return _ioError("seeking to local header") unless $status;
|
|
|
67 |
|
|
|
68 |
($status, $signature) =
|
|
|
69 |
_readSignature($self->fh(), $self->externalFileName(),
|
|
|
70 |
LOCAL_FILE_HEADER_SIGNATURE);
|
|
|
71 |
return $status if $status == AZ_IO_ERROR;
|
|
|
72 |
|
|
|
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'},
|
|
|
77 |
$where
|
|
|
78 |
);
|
|
|
79 |
if ($status == AZ_OK) {
|
|
|
80 |
$self->{'localHeaderRelativeOffset'} +=
|
|
|
81 |
$self->{'possibleEocdOffset'};
|
|
|
82 |
$self->{'possibleEocdOffset'} = 0;
|
|
|
83 |
}
|
|
|
84 |
}
|
|
|
85 |
|
|
|
86 |
return $status;
|
|
|
87 |
}
|
|
|
88 |
|
|
|
89 |
# Because I'm going to delete the file handle, read the local file
|
|
|
90 |
# header if the file handle is seekable. If it is not, I assume that
|
|
|
91 |
# I've already read the local header.
|
|
|
92 |
# Return ( $status, $self )
|
|
|
93 |
|
|
|
94 |
sub _become {
|
|
|
95 |
my $self = shift;
|
|
|
96 |
my $newClass = shift;
|
|
|
97 |
return $self if ref($self) eq $newClass;
|
|
|
98 |
|
|
|
99 |
my $status = AZ_OK;
|
|
|
100 |
|
|
|
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;
|
|
|
107 |
}
|
|
|
108 |
|
|
|
109 |
delete($self->{'eocdCrc32'});
|
|
|
110 |
delete($self->{'diskNumberStart'});
|
|
|
111 |
delete($self->{'localHeaderRelativeOffset'});
|
|
|
112 |
delete($self->{'dataOffset'});
|
|
|
113 |
|
|
|
114 |
return $self->SUPER::_become($newClass);
|
|
|
115 |
}
|
|
|
116 |
|
|
|
117 |
sub diskNumberStart {
|
|
|
118 |
shift->{'diskNumberStart'};
|
|
|
119 |
}
|
|
|
120 |
|
|
|
121 |
sub localHeaderRelativeOffset {
|
|
|
122 |
shift->{'localHeaderRelativeOffset'};
|
|
|
123 |
}
|
|
|
124 |
|
|
|
125 |
sub dataOffset {
|
|
|
126 |
shift->{'dataOffset'};
|
|
|
127 |
}
|
|
|
128 |
|
|
|
129 |
# Skip local file header, updating only extra field stuff.
|
|
|
130 |
# Assumes that fh is positioned before signature.
|
|
|
131 |
sub _skipLocalFileHeader {
|
|
|
132 |
my $self = shift;
|
|
|
133 |
my $header;
|
|
|
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");
|
|
|
137 |
}
|
|
|
138 |
my $fileNameLength;
|
|
|
139 |
my $extraFieldLength;
|
|
|
140 |
my $bitFlag;
|
|
|
141 |
(
|
|
|
142 |
undef, # $self->{'versionNeededToExtract'},
|
|
|
143 |
$bitFlag,
|
|
|
144 |
undef, # $self->{'compressionMethod'},
|
|
|
145 |
undef, # $self->{'lastModFileDateTime'},
|
|
|
146 |
undef, # $crc32,
|
|
|
147 |
undef, # $compressedSize,
|
|
|
148 |
undef, # $uncompressedSize,
|
|
|
149 |
$fileNameLength,
|
|
|
150 |
$extraFieldLength
|
|
|
151 |
) = unpack(LOCAL_FILE_HEADER_FORMAT, $header);
|
|
|
152 |
|
|
|
153 |
if ($fileNameLength) {
|
|
|
154 |
$self->fh()->seek($fileNameLength, IO::Seekable::SEEK_CUR)
|
|
|
155 |
or return _ioError("skipping local file name");
|
|
|
156 |
}
|
|
|
157 |
|
|
|
158 |
if ($extraFieldLength) {
|
|
|
159 |
$bytesRead =
|
|
|
160 |
$self->fh()->read($self->{'localExtraField'}, $extraFieldLength);
|
|
|
161 |
if ($bytesRead != $extraFieldLength) {
|
|
|
162 |
return _ioError("reading local extra field");
|
|
|
163 |
}
|
|
|
164 |
}
|
|
|
165 |
|
|
|
166 |
$self->{'dataOffset'} = $self->fh()->tell();
|
|
|
167 |
|
|
|
168 |
if ($bitFlag & GPBF_HAS_DATA_DESCRIPTOR_MASK) {
|
|
|
169 |
|
|
|
170 |
# Read the crc32, compressedSize, and uncompressedSize from the
|
|
|
171 |
# extended data descriptor, which directly follows the compressed data.
|
|
|
172 |
#
|
|
|
173 |
# Skip over the compressed file data (assumes that EOCD compressedSize
|
|
|
174 |
# was correct)
|
|
|
175 |
$self->fh()->seek($self->{'compressedSize'}, IO::Seekable::SEEK_CUR)
|
|
|
176 |
or return _ioError("seeking to extended local header");
|
|
|
177 |
|
|
|
178 |
# these values should be set correctly from before.
|
|
|
179 |
my $oldCrc32 = $self->{'eocdCrc32'};
|
|
|
180 |
my $oldCompressedSize = $self->{'compressedSize'};
|
|
|
181 |
my $oldUncompressedSize = $self->{'uncompressedSize'};
|
|
|
182 |
|
|
|
183 |
my $status = $self->_readDataDescriptor();
|
|
|
184 |
return $status unless $status == AZ_OK;
|
|
|
185 |
|
|
|
186 |
# The buffer withe encrypted data is prefixed with a new
|
|
|
187 |
# encrypted 12 byte header. The size only changes when
|
|
|
188 |
# the buffer is also compressed
|
|
|
189 |
$self->isEncrypted && $oldUncompressedSize > $self->{uncompressedSize}
|
|
|
190 |
and $oldUncompressedSize -= DATA_DESCRIPTOR_LENGTH;
|
|
|
191 |
|
|
|
192 |
return _formatError(
|
|
|
193 |
"CRC or size mismatch while skipping data descriptor")
|
|
|
194 |
if ( $oldCrc32 != $self->{'crc32'}
|
|
|
195 |
|| $oldUncompressedSize != $self->{'uncompressedSize'});
|
|
|
196 |
|
|
|
197 |
$self->{'crc32'} = 0
|
|
|
198 |
if $self->compressionMethod() == COMPRESSION_STORED ;
|
|
|
199 |
}
|
|
|
200 |
|
|
|
201 |
return AZ_OK;
|
|
|
202 |
}
|
|
|
203 |
|
|
|
204 |
# Read from a local file header into myself. Returns AZ_OK if successful.
|
|
|
205 |
# Assumes that fh is positioned after signature.
|
|
|
206 |
# Note that crc32, compressedSize, and uncompressedSize will be 0 if
|
|
|
207 |
# GPBF_HAS_DATA_DESCRIPTOR_MASK is set in the bitFlag.
|
|
|
208 |
|
|
|
209 |
sub _readLocalFileHeader {
|
|
|
210 |
my $self = shift;
|
|
|
211 |
my $header;
|
|
|
212 |
my $bytesRead = $self->fh()->read($header, LOCAL_FILE_HEADER_LENGTH);
|
|
|
213 |
if ($bytesRead != LOCAL_FILE_HEADER_LENGTH) {
|
|
|
214 |
return _ioError("reading local file header");
|
|
|
215 |
}
|
|
|
216 |
my $fileNameLength;
|
|
|
217 |
my $crc32;
|
|
|
218 |
my $compressedSize;
|
|
|
219 |
my $uncompressedSize;
|
|
|
220 |
my $extraFieldLength;
|
|
|
221 |
(
|
|
|
222 |
$self->{'versionNeededToExtract'}, $self->{'bitFlag'},
|
|
|
223 |
$self->{'compressionMethod'}, $self->{'lastModFileDateTime'},
|
|
|
224 |
$crc32, $compressedSize,
|
|
|
225 |
$uncompressedSize, $fileNameLength,
|
|
|
226 |
$extraFieldLength
|
|
|
227 |
) = unpack(LOCAL_FILE_HEADER_FORMAT, $header);
|
|
|
228 |
|
|
|
229 |
if ($fileNameLength) {
|
|
|
230 |
my $fileName;
|
|
|
231 |
$bytesRead = $self->fh()->read($fileName, $fileNameLength);
|
|
|
232 |
if ($bytesRead != $fileNameLength) {
|
|
|
233 |
return _ioError("reading local file name");
|
|
|
234 |
}
|
|
|
235 |
$self->fileName($fileName);
|
|
|
236 |
}
|
|
|
237 |
|
|
|
238 |
if ($extraFieldLength) {
|
|
|
239 |
$bytesRead =
|
|
|
240 |
$self->fh()->read($self->{'localExtraField'}, $extraFieldLength);
|
|
|
241 |
if ($bytesRead != $extraFieldLength) {
|
|
|
242 |
return _ioError("reading local extra field");
|
|
|
243 |
}
|
|
|
244 |
}
|
|
|
245 |
|
|
|
246 |
$self->{'dataOffset'} = $self->fh()->tell();
|
|
|
247 |
|
|
|
248 |
if ($self->hasDataDescriptor()) {
|
|
|
249 |
|
|
|
250 |
# Read the crc32, compressedSize, and uncompressedSize from the
|
|
|
251 |
# extended data descriptor.
|
|
|
252 |
# Skip over the compressed file data (assumes that EOCD compressedSize
|
|
|
253 |
# was correct)
|
|
|
254 |
$self->fh()->seek($self->{'compressedSize'}, IO::Seekable::SEEK_CUR)
|
|
|
255 |
or return _ioError("seeking to extended local header");
|
|
|
256 |
|
|
|
257 |
my $status = $self->_readDataDescriptor();
|
|
|
258 |
return $status unless $status == AZ_OK;
|
|
|
259 |
} else {
|
|
|
260 |
return _formatError(
|
|
|
261 |
"CRC or size mismatch after reading data descriptor")
|
|
|
262 |
if ( $self->{'crc32'} != $crc32
|
|
|
263 |
|| $self->{'uncompressedSize'} != $uncompressedSize);
|
|
|
264 |
}
|
|
|
265 |
|
|
|
266 |
return AZ_OK;
|
|
|
267 |
}
|
|
|
268 |
|
|
|
269 |
# This will read the data descriptor, which is after the end of compressed file
|
|
|
270 |
# data in members that have GPBF_HAS_DATA_DESCRIPTOR_MASK set in their bitFlag.
|
|
|
271 |
# The only reliable way to find these is to rely on the EOCD compressedSize.
|
|
|
272 |
# Assumes that file is positioned immediately after the compressed data.
|
|
|
273 |
# Returns status; sets crc32, compressedSize, and uncompressedSize.
|
|
|
274 |
sub _readDataDescriptor {
|
|
|
275 |
my $self = shift;
|
|
|
276 |
my $signatureData;
|
|
|
277 |
my $header;
|
|
|
278 |
my $crc32;
|
|
|
279 |
my $compressedSize;
|
|
|
280 |
my $uncompressedSize;
|
|
|
281 |
|
|
|
282 |
my $bytesRead = $self->fh()->read($signatureData, SIGNATURE_LENGTH);
|
|
|
283 |
return _ioError("reading header signature")
|
|
|
284 |
if $bytesRead != SIGNATURE_LENGTH;
|
|
|
285 |
my $signature = unpack(SIGNATURE_FORMAT, $signatureData);
|
|
|
286 |
|
|
|
287 |
# unfortunately, the signature appears to be optional.
|
|
|
288 |
if ($signature == DATA_DESCRIPTOR_SIGNATURE
|
|
|
289 |
&& ($signature != $self->{'crc32'})) {
|
|
|
290 |
$bytesRead = $self->fh()->read($header, DATA_DESCRIPTOR_LENGTH);
|
|
|
291 |
return _ioError("reading data descriptor")
|
|
|
292 |
if $bytesRead != DATA_DESCRIPTOR_LENGTH;
|
|
|
293 |
|
|
|
294 |
($crc32, $compressedSize, $uncompressedSize) =
|
|
|
295 |
unpack(DATA_DESCRIPTOR_FORMAT, $header);
|
|
|
296 |
} else {
|
|
|
297 |
$bytesRead = $self->fh()->read($header, DATA_DESCRIPTOR_LENGTH_NO_SIG);
|
|
|
298 |
return _ioError("reading data descriptor")
|
|
|
299 |
if $bytesRead != DATA_DESCRIPTOR_LENGTH_NO_SIG;
|
|
|
300 |
|
|
|
301 |
$crc32 = $signature;
|
|
|
302 |
($compressedSize, $uncompressedSize) =
|
|
|
303 |
unpack(DATA_DESCRIPTOR_FORMAT_NO_SIG, $header);
|
|
|
304 |
}
|
|
|
305 |
|
|
|
306 |
$self->{'eocdCrc32'} = $self->{'crc32'}
|
|
|
307 |
unless defined($self->{'eocdCrc32'});
|
|
|
308 |
$self->{'crc32'} = $crc32;
|
|
|
309 |
$self->{'compressedSize'} = $compressedSize;
|
|
|
310 |
$self->{'uncompressedSize'} = $uncompressedSize;
|
|
|
311 |
|
|
|
312 |
return AZ_OK;
|
|
|
313 |
}
|
|
|
314 |
|
|
|
315 |
# Read a Central Directory header. Return AZ_OK on success.
|
|
|
316 |
# Assumes that fh is positioned right after the signature.
|
|
|
317 |
|
|
|
318 |
sub _readCentralDirectoryFileHeader {
|
|
|
319 |
my $self = shift;
|
|
|
320 |
my $fh = $self->fh();
|
|
|
321 |
my $header = '';
|
|
|
322 |
my $bytesRead = $fh->read($header, CENTRAL_DIRECTORY_FILE_HEADER_LENGTH);
|
|
|
323 |
if ($bytesRead != CENTRAL_DIRECTORY_FILE_HEADER_LENGTH) {
|
|
|
324 |
return _ioError("reading central dir header");
|
|
|
325 |
}
|
|
|
326 |
my ($fileNameLength, $extraFieldLength, $fileCommentLength);
|
|
|
327 |
(
|
|
|
328 |
$self->{'versionMadeBy'},
|
|
|
329 |
$self->{'fileAttributeFormat'},
|
|
|
330 |
$self->{'versionNeededToExtract'},
|
|
|
331 |
$self->{'bitFlag'},
|
|
|
332 |
$self->{'compressionMethod'},
|
|
|
333 |
$self->{'lastModFileDateTime'},
|
|
|
334 |
$self->{'crc32'},
|
|
|
335 |
$self->{'compressedSize'},
|
|
|
336 |
$self->{'uncompressedSize'},
|
|
|
337 |
$fileNameLength,
|
|
|
338 |
$extraFieldLength,
|
|
|
339 |
$fileCommentLength,
|
|
|
340 |
$self->{'diskNumberStart'},
|
|
|
341 |
$self->{'internalFileAttributes'},
|
|
|
342 |
$self->{'externalFileAttributes'},
|
|
|
343 |
$self->{'localHeaderRelativeOffset'}
|
|
|
344 |
) = unpack(CENTRAL_DIRECTORY_FILE_HEADER_FORMAT, $header);
|
|
|
345 |
|
|
|
346 |
$self->{'eocdCrc32'} = $self->{'crc32'};
|
|
|
347 |
|
|
|
348 |
if ($fileNameLength) {
|
|
|
349 |
$bytesRead = $fh->read($self->{'fileName'}, $fileNameLength);
|
|
|
350 |
if ($bytesRead != $fileNameLength) {
|
|
|
351 |
_ioError("reading central dir filename");
|
|
|
352 |
}
|
|
|
353 |
}
|
|
|
354 |
if ($extraFieldLength) {
|
|
|
355 |
$bytesRead = $fh->read($self->{'cdExtraField'}, $extraFieldLength);
|
|
|
356 |
if ($bytesRead != $extraFieldLength) {
|
|
|
357 |
return _ioError("reading central dir extra field");
|
|
|
358 |
}
|
|
|
359 |
}
|
|
|
360 |
if ($fileCommentLength) {
|
|
|
361 |
$bytesRead = $fh->read($self->{'fileComment'}, $fileCommentLength);
|
|
|
362 |
if ($bytesRead != $fileCommentLength) {
|
|
|
363 |
return _ioError("reading central dir file comment");
|
|
|
364 |
}
|
|
|
365 |
}
|
|
|
366 |
|
|
|
367 |
# NK 10/21/04: added to avoid problems with manipulated headers
|
|
|
368 |
if ( $self->{'uncompressedSize'} != $self->{'compressedSize'}
|
|
|
369 |
and $self->{'compressionMethod'} == COMPRESSION_STORED) {
|
|
|
370 |
$self->{'uncompressedSize'} = $self->{'compressedSize'};
|
|
|
371 |
}
|
|
|
372 |
|
|
|
373 |
$self->desiredCompressionMethod($self->compressionMethod());
|
|
|
374 |
|
|
|
375 |
return AZ_OK;
|
|
|
376 |
}
|
|
|
377 |
|
|
|
378 |
sub rewindData {
|
|
|
379 |
my $self = shift;
|
|
|
380 |
|
|
|
381 |
my $status = $self->SUPER::rewindData(@_);
|
|
|
382 |
return $status unless $status == AZ_OK;
|
|
|
383 |
|
|
|
384 |
return AZ_IO_ERROR unless $self->fh();
|
|
|
385 |
|
|
|
386 |
$self->fh()->clearerr();
|
|
|
387 |
|
|
|
388 |
# Seek to local file header.
|
|
|
389 |
# The only reason that I'm doing this this way is that the extraField
|
|
|
390 |
# length seems to be different between the CD header and the LF header.
|
|
|
391 |
$status = $self->_seekToLocalHeader();
|
|
|
392 |
return $status unless $status == AZ_OK;
|
|
|
393 |
|
|
|
394 |
# skip local file header
|
|
|
395 |
$status = $self->_skipLocalFileHeader();
|
|
|
396 |
return $status unless $status == AZ_OK;
|
|
|
397 |
|
|
|
398 |
# Seek to beginning of file data
|
|
|
399 |
$self->fh()->seek($self->dataOffset(), IO::Seekable::SEEK_SET)
|
|
|
400 |
or return _ioError("seeking to beginning of file data");
|
|
|
401 |
|
|
|
402 |
return AZ_OK;
|
|
|
403 |
}
|
|
|
404 |
|
|
|
405 |
# Return bytes read. Note that first parameter is a ref to a buffer.
|
|
|
406 |
# my $data;
|
|
|
407 |
# my ( $bytesRead, $status) = $self->readRawChunk( \$data, $chunkSize );
|
|
|
408 |
sub _readRawChunk {
|
|
|
409 |
my ($self, $dataRef, $chunkSize) = @_;
|
|
|
410 |
return (0, AZ_OK) unless $chunkSize;
|
|
|
411 |
my $bytesRead = $self->fh()->read($$dataRef, $chunkSize)
|
|
|
412 |
or return (0, _ioError("reading data"));
|
|
|
413 |
return ($bytesRead, AZ_OK);
|
|
|
414 |
}
|
|
|
415 |
|
|
|
416 |
1;
|