46
46
sub newFromString {
48
my $self = $class->STRINGMEMBERCLASS->_newFromString(@_);
49
my ( $stringOrStringRef, $fileName );
50
if ( ref( $_[0] ) eq 'HASH' ) {
51
$stringOrStringRef = $_[0]->{string};
52
$fileName = $_[0]->{zipName};
55
( $stringOrStringRef, $fileName ) = @_;
58
my $self = $class->STRINGMEMBERCLASS->_newFromString( $stringOrStringRef,
54
my $self = $class->NEWFILEMEMBERCLASS->_newFromFileNamed(@_);
66
my ( $fileName, $zipName );
67
if ( ref( $_[0] ) eq 'HASH' ) {
68
$fileName = $_[0]->{fileName};
69
$zipName = $_[0]->{zipName};
72
( $fileName, $zipName ) = @_;
75
my $self = $class->NEWFILEMEMBERCLASS->_newFromFileNamed( $fileName,
58
80
sub newDirectoryNamed {
60
my $self = $class->DIRECTORYMEMBERCLASS->_newNamed(@_);
83
my ( $directoryName, $newName );
84
if ( ref( $_[0] ) eq 'HASH' ) {
85
$directoryName = $_[0]->{directoryName};
86
$newName = $_[0]->{zipName};
89
( $directoryName, $newName ) = @_;
92
my $self = $class->DIRECTORYMEMBERCLASS->_newNamed( $directoryName,
107
141
sub fileAttributeFormat {
109
? ( $_[0]->{'fileAttributeFormat'} = $_[1] )
110
: $_[0]->{'fileAttributeFormat'};
145
$self->{fileAttributeFormat} = ( ref( $_[0] ) eq 'HASH' )
146
? $_[0]->{format} : $_[0];
149
return $self->{fileAttributeFormat};
113
153
sub versionNeededToExtract {
160
# Set General Purpose Bit Flags according to the desiredCompressionLevel setting
161
if ( $self->desiredCompressionLevel == 1 || $self->desiredCompressionLevel == 2 ) {
162
$self->{'bitFlag'} = DEFLATING_COMPRESSION_FAST;
163
} elsif ( $self->desiredCompressionLevel == 3 || $self->desiredCompressionLevel == 4
164
|| $self->desiredCompressionLevel == 5 || $self->desiredCompressionLevel == 6
165
|| $self->desiredCompressionLevel == 7 ) {
166
$self->{'bitFlag'} = DEFLATING_COMPRESSION_NORMAL;
167
} elsif ( $self->desiredCompressionLevel == 8 || $self->desiredCompressionLevel == 9 ) {
168
$self->{'bitFlag'} = DEFLATING_COMPRESSION_MAXIMUM;
121
173
sub compressionMethod {
125
177
sub desiredCompressionMethod {
127
my $newDesiredCompressionMethod = shift;
179
my $newDesiredCompressionMethod =
180
( ref( $_[0] ) eq 'HASH' ) ? shift->{compressionMethod} : shift;
128
181
my $oldDesiredCompressionMethod = $self->{'desiredCompressionMethod'};
129
182
if ( defined($newDesiredCompressionMethod) ) {
130
183
$self->{'desiredCompressionMethod'} = $newDesiredCompressionMethod;
131
184
if ( $newDesiredCompressionMethod == COMPRESSION_STORED ) {
132
185
$self->{'desiredCompressionLevel'} = 0;
134
elsif ( $oldDesiredCompressionMethod == COMPRESSION_STORED ) {
186
$self->{'bitFlag'} &= ~GPBF_HAS_DATA_DESCRIPTOR_MASK;
188
} elsif ( $oldDesiredCompressionMethod == COMPRESSION_STORED ) {
135
189
$self->{'desiredCompressionLevel'} = COMPRESSION_LEVEL_DEFAULT;
141
195
sub desiredCompressionLevel {
143
my $newDesiredCompressionLevel = shift;
197
my $newDesiredCompressionLevel =
198
( ref( $_[0] ) eq 'HASH' ) ? shift->{compressionLevel} : shift;
144
199
my $oldDesiredCompressionLevel = $self->{'desiredCompressionLevel'};
145
200
if ( defined($newDesiredCompressionLevel) ) {
146
201
$self->{'desiredCompressionLevel'} = $newDesiredCompressionLevel;
191
246
# Convert UNIX permissions into proper value for zip file
247
# Usable as a function or a method
193
248
sub _mapPermissionsFromUnix {
197
# TODO: map MS-DOS perms too (RHSA?)
251
my $attribs = $mode << 16;
253
# Microsoft Windows Explorer needs this bit set for directories
254
if ( $mode & DIRECTORY_ATTRIB ) {
260
# TODO: map more MS-DOS perms
200
263
# Convert ZIP permissions into Unix ones
285
348
sub unixFileAttributes {
286
349
my $self = shift;
287
my $oldPerms = $self->_mapPermissionsToUnix();
290
if ( $self->isDirectory() ) {
350
my $oldPerms = $self->_mapPermissionsToUnix;
354
$perms = ( ref( $_[0] ) eq 'HASH' ) ? $_[0]->{attributes} : $_[0];
356
if ( $self->isDirectory ) {
291
357
$perms &= ~FILE_ATTRIB;
292
358
$perms |= DIRECTORY_ATTRIB;
295
360
$perms &= ~DIRECTORY_ATTRIB;
296
361
$perms |= FILE_ATTRIB;
298
$self->{'externalFileAttributes'} = _mapPermissionsFromUnix($perms);
363
$self->{externalFileAttributes} = $self->_mapPermissionsFromUnix($perms);
300
366
return $oldPerms;
303
369
sub localExtraField {
305
? ( $_[0]->{'localExtraField'} = $_[1] )
306
: $_[0]->{'localExtraField'};
373
$self->{localExtraField} = ( ref( $_[0] ) eq 'HASH' )
374
? $_[0]->{field} : $_[0];
377
return $self->{localExtraField};
309
381
sub cdExtraField {
310
( $#_ > 0 ) ? ( $_[0]->{'cdExtraField'} = $_[1] ) : $_[0]->{'cdExtraField'};
385
$self->{cdExtraField} = ( ref( $_[0] ) eq 'HASH' )
386
? $_[0]->{field} : $_[0];
389
return $self->{cdExtraField};
313
393
sub extraFields {
318
398
sub fileComment {
320
? ( $_[0]->{'fileComment'} = pack( 'C0a*', $_[1] ) )
321
: $_[0]->{'fileComment'};
402
$self->{fileComment} = ( ref( $_[0] ) eq 'HASH' )
403
? pack( 'C0a*', $_[0]->{comment} ) : pack( 'C0a*', $_[0] );
406
return $self->{fileComment};
324
410
sub hasDataDescriptor {
359
445
my $self = shift;
360
446
my $bit = $self->internalFileAttributes() & IFA_TEXT_FILE_MASK;
448
my $flag = ( ref( $_[0] ) eq 'HASH' ) ? shift->{flag} : shift;
363
449
$self->{'internalFileAttributes'} &= ~IFA_TEXT_FILE_MASK;
364
450
$self->{'internalFileAttributes'} |=
365
451
( $flag ? IFA_TEXT_FILE: IFA_BINARY_FILE );
382
468
sub extractToFileNamed {
383
469
my $self = shift;
384
my $name = shift; # local FS name
385
return _error("encryption unsupported") if $self->isEncrypted();
386
mkpath( dirname($name) ); # croaks on error
387
my ( $status, $fh ) = _newFileHandle( $name, 'w' );
388
return _ioError("Can't open file $name for write") unless $status;
472
my $name = ( ref( $_[0] ) eq 'HASH' ) ? $_[0]->{name} : $_[0];
473
$self->{'isSymbolicLink'} = 0;
475
# Check if the file / directory is a symbolic link or not
476
if ( $self->{'externalFileAttributes'} == 0xA1FF0000 ) {
477
$self->{'isSymbolicLink'} = 1;
478
$self->{'newName'} = $name;
479
my ( $status, $fh ) = _newFileHandle( $name, 'r' );
480
my $retval = $self->extractToFileHandle($fh);
483
#return _writeSymbolicLink($self, $name) if $self->isSymbolicLink();
484
return _error("encryption unsupported") if $self->isEncrypted();
485
mkpath( dirname($name) ); # croaks on error
486
my ( $status, $fh ) = _newFileHandle( $name, 'w' );
487
return _ioError("Can't open file $name for write") unless $status;
488
my $retval = $self->extractToFileHandle($fh);
490
chmod ($self->unixFileAttributes(), $name)
491
or return _error("Can't chmod() ${name}: $!");
492
utime( $self->lastModTime(), $self->lastModTime(), $name );
497
sub _writeSymbolicLink {
500
my $chunkSize = $Archive::Zip::ChunkSize;
501
#my ( $outRef, undef ) = $self->readChunk($chunkSize);
389
503
my $retval = $self->extractToFileHandle($fh);
391
utime( $self->lastModTime(), $self->lastModTime(), $name );
504
my ( $outRef, undef ) = $self->readChunk(100);
509
if ( $self->{'externalFileAttributes'} == 0xA1FF0000 ) {
510
$self->{'isSymbolicLink'} = 1;
395
517
sub isDirectory {
513
635
my $signatureData = pack( SIGNATURE_FORMAT, LOCAL_FILE_HEADER_SIGNATURE );
514
$fh->print($signatureData)
636
$self->_print($fh, $signatureData)
515
637
or return _ioError("writing local header signature");
517
639
my $header = pack(
527
649
length( $self->localExtraField() )
530
$fh->print($header) or return _ioError("writing local header");
531
if ( $self->fileName() ) {
532
$fh->print( $self->fileName() )
652
$self->_print($fh, $header) or return _ioError("writing local header");
654
# Check for a valid filename or a filename equal to a literal `0'
655
if ( $self->fileName() || $self->fileName eq '0' ) {
656
$self->_print($fh, $self->fileName() )
533
657
or return _ioError("writing local header filename");
535
659
if ( $self->localExtraField() ) {
536
$fh->print( $self->localExtraField() )
660
$self->_print($fh, $self->localExtraField() )
537
661
or return _ioError("writing local extra field");
548
672
pack( SIGNATURE_FORMAT, CENTRAL_DIRECTORY_FILE_HEADER_SIGNATURE );
673
$self->_print($fh, $sigData)
550
674
or return _ioError("writing central directory header signature");
552
676
my $fileNameLength = length( $self->fileName() );
573
697
$self->writeLocalHeaderRelativeOffset()
700
$self->_print($fh, $header)
577
701
or return _ioError("writing central directory header");
578
702
if ($fileNameLength) {
579
$fh->print( $self->fileName() )
703
$self->_print($fh, $self->fileName() )
580
704
or return _ioError("writing central directory header signature");
582
706
if ($extraFieldLength) {
583
$fh->print( $self->cdExtraField() )
707
$self->_print($fh, $self->cdExtraField() )
584
708
or return _ioError("writing central directory extra field");
586
710
if ($fileCommentLength) {
587
$fh->print( $self->fileComment() )
711
$self->_print($fh, $self->fileComment() )
588
712
or return _ioError("writing central directory file comment");
637
761
length( $self->localExtraField() )
764
$self->_print($fh, $header)
641
765
or return _ioError("re-writing local header");
642
766
$fh->seek( $here, IO::Seekable::SEEK_SET )
643
767
or return _ioError("seeking after rewrite of local header");
703
828
# ( $outputRef, $status) = $self->_deflateChunk( \$buffer );
704
829
sub _deflateChunk {
705
830
my ( $self, $buffer ) = @_;
706
my ( $out, $status ) = $self->_deflater()->deflate($buffer);
831
my ( $status ) = $self->_deflater()->deflate( $buffer, my $out );
708
833
if ( $self->_readDataRemaining() == 0 ) {
710
( $extraOutput, $status ) = $self->_deflater()->flush();
835
( $status ) = $self->_deflater()->flush($extraOutput);
711
836
$out .= $extraOutput;
712
837
$self->endRead();
713
838
return ( \$out, AZ_STREAM_END );
726
851
# ( $outputRef, $status) = $self->_inflateChunk( \$buffer );
727
852
sub _inflateChunk {
728
853
my ( $self, $buffer ) = @_;
729
my ( $out, $status ) = $self->_inflater()->inflate($buffer);
854
my ( $status ) = $self->_inflater()->inflate( $buffer, my $out );
731
856
$self->endRead() unless $status == Z_OK;
732
857
if ( $status == Z_OK || $status == Z_STREAM_END ) {
759
884
if ( $self->compressionMethod() == COMPRESSION_STORED
760
885
and $self->desiredCompressionMethod() == COMPRESSION_DEFLATED )
762
( $self->{'deflater'}, $status ) = Compress::Zlib::deflateInit(
887
( $self->{'deflater'}, $status ) = Compress::Raw::Zlib::Deflate->new(
763
888
'-Level' => $self->desiredCompressionLevel(),
764
889
'-WindowBits' => -MAX_WBITS(), # necessary magic
765
890
'-Bufsize' => $Archive::Zip::ChunkSize,
772
897
elsif ( $self->compressionMethod() == COMPRESSION_DEFLATED
773
898
and $self->desiredCompressionMethod() == COMPRESSION_STORED )
775
( $self->{'inflater'}, $status ) = Compress::Zlib::inflateInit(
900
( $self->{'inflater'}, $status ) = Compress::Raw::Zlib::Inflate->new(
776
901
'-WindowBits' => -MAX_WBITS(), # necessary magic
777
902
'-Bufsize' => $Archive::Zip::ChunkSize,
859
984
sub extractToFileHandle {
860
985
my $self = shift;
861
986
return _error("encryption unsupported") if $self->isEncrypted();
987
my $fh = ( ref( $_[0] ) eq 'HASH' ) ? shift->{fileHandle} : shift;
864
989
my $oldCompression = $self->desiredCompressionMethod(COMPRESSION_STORED);
865
990
my $status = $self->rewindData(@_);
877
1002
my $offset = shift;
879
1004
return _error("no member name given for $self")
880
unless $self->fileName();
1005
if $self->fileName() eq '';
882
1007
$self->{'writeLocalHeaderRelativeOffset'} = $offset;
883
1008
$self->{'wasWritten'} = 0;
923
1048
my $self = shift;
924
1049
my $writeFh = shift;
926
return AZ_OK if ( $self->uncompressedSize() == 0 );
928
my $chunkSize = $Archive::Zip::ChunkSize;
929
while ( $self->_readDataRemaining() > 0 ) {
931
( $outRef, $status ) = $self->readChunk($chunkSize);
932
return $status if ( $status != AZ_OK and $status != AZ_STREAM_END );
934
if ( length($$outRef) > 0 ) {
935
$writeFh->print($$outRef)
936
or return _ioError("write error during copy");
1051
# If symbolic link, just create one if the operating system is Linux, Unix, BSD or VMS
1052
# TODO: Add checks for other operating systems
1053
if ( $self->{'isSymbolicLink'} == 1 && $^O eq 'linux' ) {
1054
my $chunkSize = $Archive::Zip::ChunkSize;
1055
my ( $outRef, $status ) = $self->readChunk($chunkSize);
1056
symlink $$outRef, $self->{'newName'};
1058
return AZ_OK if ( $self->uncompressedSize() == 0 );
1060
my $chunkSize = $Archive::Zip::ChunkSize;
1061
while ( $self->_readDataRemaining() > 0 ) {
1063
( $outRef, $status ) = $self->readChunk($chunkSize);
1064
return $status if ( $status != AZ_OK and $status != AZ_STREAM_END );
1066
if ( length($$outRef) > 0 ) {
1067
$self->_print($writeFh, $$outRef)
1068
or return _ioError("write error during copy");
1071
last if $status == AZ_STREAM_END;
939
last if $status == AZ_STREAM_END;
1073
$self->{'compressedSize'} = $self->_writeOffset();
941
$self->{'compressedSize'} = $self->_writeOffset();