File Coverage

blib/lib/Archive/Zip/Parser/Entry/Header.pm
Criterion Covered Total %
statement 89 113 78.7
branch 19 48 39.5
condition 3 21 14.2
subroutine 18 18 100.0
pod 0 13 0.0
total 129 213 60.5


line stmt bran cond sub pod time code
1             package Archive::Zip::Parser::Entry::Header;
2              
3 2     2   14 use warnings;
  2         6  
  2         62  
4 2     2   10 use strict;
  2         5  
  2         61  
5 2     2   11 use Data::ParseBinary;
  2         4  
  2         5003  
6              
7             sub get_signature {
8 2     2 0 4 my $self = shift;
9 2         44 return unpack( 'H*', pack( 'N', $self->{'_signature'} ) );
10             }
11              
12             sub get_version_needed {
13 4     4 0 56 my ( $self, $argref ) = @_;
14              
15 4         33 my $version_needed =
16             int( $self->{'_version_needed'} / 10 ) . '.'
17             . $self->{'_version_needed'} % 10;
18 4 100       18 if ( $argref->{'describe'} ) {
19 2         50 my %version_description_mapping = (
20             '1.0' => 'Default value',
21             '1.1' => 'File is a volume label',
22             '2.0' => join( ', ',
23             'File is a folder (directory)',
24             'File is compressed using Deflate compression',
25             'File is encrypted using traditional PKWARE encryption',
26             ),
27             '2.1' => 'File is compressed using Deflate64(tm)',
28             '2.5' => 'File is compressed using PKWARE DCL Implode ',
29             '2.7' => 'File is a patch data set ',
30             '4.5' => 'File uses ZIP64 format extensions',
31             '4.6' => 'File is compressed using BZIP2 compression*',
32             '5.0' => join( ', ',
33             'File is encrypted using DES',
34             'File is encrypted using 3DES',
35             'File is encrypted using original RC2 encryption',
36             'File is encrypted using RC4 encryption',
37             ),
38             '5.1' => join( ', ',
39             'File is encrypted using AES encryption',
40             'File is encrypted using corrected RC2 encryption',
41             ),
42             '5.2' => 'File is encrypted using corrected RC2-64 encryption',
43             '6.1' => 'File is encrypted using non-OAEP key wrapping',
44             '6.2' => 'Central directory encryption',
45             '6.3' => join( ', ',
46             'File is compressed using LZMA',
47             'File is compressed using PPMd',
48             'File is encrypted using Blowfish',
49             'File is encrypted using Twofish',
50             ),
51             );
52 2         28 return $version_description_mapping{$version_needed};
53             }
54              
55 2         13 return $version_needed;
56             }
57              
58             sub get_gp_bit {
59 4     4 0 15225 my ( $self, $argref ) = @_;
60              
61 4         8 my @bits;
62 4         13 for ( 0 .. 15 ) {
63 64         171 push @bits, $self->{'_gp_bit'}->{"_bit_$_"};
64             }
65              
66 4 100       15 if ( $argref->{'describe'} ) {
67 2         4 my @gp_bit_descriptions;
68              
69 2 50       11 if ( $bits[0] ) {
70 0         0 push @gp_bit_descriptions, 'File is encrypted';
71             }
72              
73 2 50       24 if ( $self->{'_compression_method'} == 6 ) {
    50          
    50          
74 0 0       0 if ( $bits[1] ) {
75 0         0 push @gp_bit_descriptions, '8K sliding dictionary';
76             }
77             else {
78 0         0 push @gp_bit_descriptions, '4K sliding dictionary';
79             }
80 0 0       0 if ( $bits[2] ) {
81 0         0 push @gp_bit_descriptions,
82             '3 Shannon-Fano trees were used to encode the sliding dictionary output';
83             }
84             else {
85 0         0 push @gp_bit_descriptions,
86             '2 Shannon-Fano trees were used to encode the sliding dictionary output';
87             }
88             }
89             elsif ( $self->{'_compression_method'} == 8 ) {
90 0 0       0 if ( $self->{'_compression_method'} == 9 ) {
91 0 0 0     0 if ( !$bits[2] && !$bits[1] ) {
    0 0        
    0 0        
    0 0        
92 0         0 push @gp_bit_descriptions,
93             'Normal (-en) compression option was used';
94             }
95             elsif ( !$bits[2] && $bits[1] ) {
96 0         0 push @gp_bit_descriptions,
97             'Maximum (-exx/-ex) compression option was used';
98             }
99             elsif ( $bits[2] && !$bits[1] ) {
100 0         0 push @gp_bit_descriptions,
101             'Fast (-ef) compression option was used';
102             }
103             elsif ( $bits[2] && $bits[1] ) {
104 0         0 push @gp_bit_descriptions,
105             'Super Fast (-es) compression option was used';
106             }
107             }
108              
109 0 0       0 if ( $bits[4] ) {
110 0         0 push @gp_bit_descriptions, 'Enhanced deflating';
111             }
112             }
113             elsif ( $self->{'_compression_method'} == 14 ) {
114 0 0       0 if ( $bits[1] ) {
115 0         0 push @gp_bit_descriptions,
116             'End-of-stream (EOS) marker is used to mark the end of the compressed data stream';
117             }
118             else {
119 0         0 push @gp_bit_descriptions,
120             'End-of-stream (EOS) marker is not present and the compressed data size must be known to extract';
121             }
122             }
123              
124 2 50       7 if ( $bits[3] ) {
125 0         0 push @gp_bit_descriptions,
126             'Data descriptor contains CRC-32, compressed size and uncompressed size';
127             }
128              
129 2 50 33     11 if ( $bits[5] && $self->{'_version_needed'} >= 27 ) {
130 0         0 push @gp_bit_descriptions, 'Compressed patched data';
131             }
132              
133 2 0 33     26 if ( $bits[6] && $self->{'_version_needed'} >= 50 && $bits[0] ) {
      33        
134 0         0 push @gp_bit_descriptions, 'Strong encryption';
135             }
136              
137 2 50       19 if ( $bits[11] ) {
138 0         0 push @gp_bit_descriptions,
139             'Filename and comment fields are encoded using UTF-8';
140             }
141 2 50       8 if ( $bits[12] ) {
142 0         0 push @gp_bit_descriptions, 'Enhanced compression';
143             }
144              
145 2 50       8 if ( $bits[13] ) {
146 0         0 push @gp_bit_descriptions,
147             'Selected data values in the Local Header are masked';
148             }
149              
150 2         13 return @gp_bit_descriptions;
151             }
152              
153 2         29 return @bits;
154             }
155              
156             sub get_compression_method {
157 4     4 0 11 my ( $self, $argref ) = @_;
158              
159 4 100       17 if ( $argref->{'describe'} ) {
160 2         44 my %compression_method_description_mapping = (
161             '0' => 'The file is stored (no compression)',
162             '1' => 'The file is Shrunk',
163             '2' => 'The file is Reduced with compression factor 1',
164             '3' => 'The file is Reduced with compression factor 2',
165             '4' => 'The file is Reduced with compression factor 3',
166             '5' => 'The file is Reduced with compression factor 4',
167             '6' => 'The file is Imploded',
168             '7' => 'Reserved for Tokenizing compression algorithm',
169             '8' => 'The file is Deflated',
170             '9' => 'Enhanced Deflating using Deflate64(tm)',
171             '10' => 'PKWARE Data Compression Library Imploding (old IBM TERSE)',
172             '11' => 'Reserved by PKWARE',
173             '12' => 'File is compressed using BZIP2 algorithm',
174             '13' => 'Reserved by PKWARE',
175             '14' => 'LZMA (EFS)',
176             '15' => 'Reserved by PKWARE',
177             '16' => 'Reserved by PKWARE',
178             '17' => 'Reserved by PKWARE',
179             '18' => 'File is compressed using IBM TERSE (new)',
180             '19' => 'IBM LZ77 z Architecture (PFS)',
181             '97' => 'WavPack compressed data',
182             '98' => 'PPMd version I, Rev 1',
183             );
184              
185 2         20 return $compression_method_description_mapping{ $self->{'_compression_method'} };
186             }
187              
188 2         12 return $self->{'_compression_method'};
189             }
190              
191             sub get_last_mod_time {
192 2     2 0 1416 my $self = shift;
193 2         15 my $last_mod_time = pack 'n', $self->{'_last_mod_time'};
194              
195 2         16 my $last_mod_time_struct = BitStruct(
196             'last_mod_time',
197             BitField( 'hour', 5 ),
198             BitField( 'minute', 6 ),
199             BitField( 'second', 5 ),
200             );
201 2         278 my $parsed_last_mod_time_struct
202             = $last_mod_time_struct->parse($last_mod_time);
203              
204 2         943 return %{$parsed_last_mod_time_struct};
  2         25  
205             }
206              
207             sub get_last_mod_date {
208 2     2 0 1132 my $self = shift;
209 2         8 my $last_mod_date = pack 'n', $self->{'_last_mod_date'};
210              
211 2         10 my $last_mod_date_struct = BitStruct(
212             'last_mod_date',
213             BitField( 'year', 7 ),
214             BitField( 'month', 4 ),
215             BitField( 'day', 5 ),
216             );
217 2         164 my $parsed_last_mod_date_struct
218             = $last_mod_date_struct->parse($last_mod_date);
219 2         637 $parsed_last_mod_date_struct->{'year'} += 1980;
220              
221 2         5 return %{$parsed_last_mod_date_struct};
  2         27  
222             }
223              
224             sub get_crc_32 {
225 2     2 0 7 my $self = shift;
226 2         23 return unpack( 'H*', pack( 'N', $self->{'_crc_32'} ) );
227             }
228              
229             sub get_compressed_size {
230 2     2 0 6 my $self = shift;
231 2         13 return $self->{'_compressed_size'};
232             }
233              
234             sub get_uncompressed_size {
235 2     2 0 5 my $self = shift;
236 2         13 return $self->{'_uncompressed_size'};
237             }
238              
239             sub get_file_name_length {
240 2     2 0 5 my $self = shift;
241 2         13 return $self->{'_file_name_length'};
242             }
243              
244             sub get_extra_field_length {
245 2     2 0 7 my $self = shift;
246 2         13 return $self->{'_extra_field_length'};
247             }
248              
249             sub get_file_name {
250 2     2 0 6 my $self = shift;
251 2         15 return $self->{'_file_name'};
252             }
253              
254             sub get_extra_field {
255 4     4 0 3926 my ( $self, $argref ) = @_;
256              
257             my $extra_field
258             = Struct(
259             '_extra_field',
260             RepeatUntil(
261             sub {
262 8     8   1053 $_->obj->{'_position'} == $self->{'_extra_field_length'};
263             },
264             Struct(
265             '_header',
266             ULInt16('_id'),
267             ULInt16('_size'),
268             Field(
269             '_data',
270             sub {
271 8     8   1638 $_->ctx->{'_size'}
272             }
273 4         37 ),
274             Anchor('_position'),
275             ),
276             ),
277             );
278 4         732 my $parsed_extra_field = $extra_field->parse( $self->{'_extra_field'} );
279              
280 4 100       272 if ( $argref->{'describe'} ) {
281 2         77 my %extra_field_description_mapping = (
282             '0001' => 'Zip64 extended information extra field',
283             '0007' => 'AV Info',
284             '0008' => 'Reserved for extended language encoding data (PFS)',
285             '0009' => 'OS/2',
286             '000a' => 'NTFS ',
287             '000c' => 'OpenVMS',
288             '000d' => 'UNIX',
289             '000e' => 'Reserved for file stream and fork descriptors',
290             '000f' => 'Patch Descriptor',
291             '0014' => 'PKCS#7 Store for X.509 Certificates',
292             '0015' => 'X.509 Certificate ID and Signature for individual file',
293             '0016' => 'X.509 Certificate ID for Central Directory',
294             '0017' => 'Strong Encryption Header',
295             '0018' => 'Record Management Controls',
296             '0019' => 'PKCS#7 Encryption Recipient Certificate List',
297             '0065' =>
298             'IBM S/390 (Z390), AS/400 (I400) attributes - uncompressed',
299             '0066' =>
300             'Reserved for IBM S/390 (Z390), AS/400 (I400) attributes - compressed',
301             '4690' => 'POSZIP 4690 (reserved) ',
302             '07c8' => 'Macintosh',
303             '2605' => 'ZipIt Macintosh',
304             '2705' => 'ZipIt Macintosh 1.3.5+',
305             '2805' => 'ZipIt Macintosh 1.3.5+',
306             '334d' => 'Info-ZIP Macintosh',
307             '4341' => 'Acorn/SparkFS ',
308             '4453' => 'Windows NT security descriptor (binary ACL)',
309             '4704' => 'VM/CMS',
310             '470f' => 'MVS',
311             '4b46' => 'FWKCS MD5',
312             '4c41' => 'OS/2 access control list (text ACL)',
313             '4d49' => 'Info-ZIP OpenVMS',
314             '4f4c' => 'Xceed original location extra field',
315             '5356' => 'AOS/VS (ACL)',
316             '5455' => 'extended timestamp',
317             '554e' => 'Xceed unicode extra field',
318             '5855' => 'Info-ZIP UNIX (original, also OS/2, NT, etc)',
319             '6375' => 'Info-ZIP Unicode Comment Extra Field',
320             '6542' => 'BeOS/BeBox',
321             '7075' => 'Info-ZIP Unicode Path Extra Field',
322             '756e' => 'ASi UNIX',
323             '7855' => 'Info-ZIP UNIX (new)',
324             'a220' => 'Microsoft Open Packaging Growth Hint',
325             'fd4a' => 'SMS/QDOS',
326             );
327              
328 2         4 my %extra_field_descriptions;
329             my @descriptions_to_be_serialised;
330 2         6 for ( @{ $parsed_extra_field->{'_header'} } ) {
  2         7  
331 4         31 my $id = unpack( 'H*', pack( 'n', $_->{'_id'} ) );
332 4         11 my $data = unpack( 'H*', $_->{'_data'} );
333 4         6 my @serialised_extra_field_description;
334              
335 4 100       16 if ( exists $extra_field_description_mapping{$id} ) {
336 2         5 my $description = $extra_field_description_mapping{$id};
337 2         6 $extra_field_descriptions{$description} = $data;
338 2         7 push @descriptions_to_be_serialised, $description;
339             }
340             else {
341 2         9 $extra_field_descriptions{$id} = $data;
342             }
343             }
344              
345 2         50 return %extra_field_descriptions;
346             }
347              
348 2         5 my %extra_field;
349 2         5 for ( @{ $parsed_extra_field->{'_header'} } ) {
  2         9  
350 4         22 my $id = unpack( 'H*', pack( 'n', $_->{'_id'} ) );
351 4         12 my $data = unpack( 'H*', $_->{'_data'} );
352 4         17 $extra_field{$id} = $data;
353             }
354 2         51 return %extra_field;
355             }
356              
357             1;