File indexing completed on 2024-12-22 05:33:10
0001 <?php 0002 ///////////////////////////////////////////////////////////////// 0003 /// getID3() by James Heinrich <info@getid3.org> // 0004 // available at http://getid3.sourceforge.net // 0005 // or http://www.getid3.org // 0006 // also https://github.com/JamesHeinrich/getID3 // 0007 ///////////////////////////////////////////////////////////////// 0008 // See readme.txt for more details // 0009 ///////////////////////////////////////////////////////////////// 0010 // // 0011 // module.archive.zip.php // 0012 // module for analyzing pkZip files // 0013 // dependencies: NONE // 0014 // /// 0015 ///////////////////////////////////////////////////////////////// 0016 0017 0018 class getid3_zip extends getid3_handler 0019 { 0020 0021 public function Analyze() { 0022 $info = &$this->getid3->info; 0023 0024 $info['fileformat'] = 'zip'; 0025 $info['zip']['encoding'] = 'ISO-8859-1'; 0026 $info['zip']['files'] = array(); 0027 0028 $info['zip']['compressed_size'] = 0; 0029 $info['zip']['uncompressed_size'] = 0; 0030 $info['zip']['entries_count'] = 0; 0031 0032 if (!getid3_lib::intValueSupported($info['filesize'])) { 0033 $info['error'][] = 'File is larger than '.round(PHP_INT_MAX / 1073741824).'GB, not supported by PHP'; 0034 return false; 0035 } else { 0036 $EOCDsearchData = ''; 0037 $EOCDsearchCounter = 0; 0038 while ($EOCDsearchCounter++ < 512) { 0039 0040 $this->fseek(-128 * $EOCDsearchCounter, SEEK_END); 0041 $EOCDsearchData = $this->fread(128).$EOCDsearchData; 0042 0043 if (strstr($EOCDsearchData, 'PK'."\x05\x06")) { 0044 0045 $EOCDposition = strpos($EOCDsearchData, 'PK'."\x05\x06"); 0046 $this->fseek((-128 * $EOCDsearchCounter) + $EOCDposition, SEEK_END); 0047 $info['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory(); 0048 0049 $this->fseek($info['zip']['end_central_directory']['directory_offset']); 0050 $info['zip']['entries_count'] = 0; 0051 while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) { 0052 $info['zip']['central_directory'][] = $centraldirectoryentry; 0053 $info['zip']['entries_count']++; 0054 $info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size']; 0055 $info['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size']; 0056 0057 //if ($centraldirectoryentry['uncompressed_size'] > 0) { zero-byte files are valid 0058 if (!empty($centraldirectoryentry['filename'])) { 0059 $info['zip']['files'] = getid3_lib::array_merge_clobber($info['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size'])); 0060 } 0061 } 0062 0063 if ($info['zip']['entries_count'] == 0) { 0064 $info['error'][] = 'No Central Directory entries found (truncated file?)'; 0065 return false; 0066 } 0067 0068 if (!empty($info['zip']['end_central_directory']['comment'])) { 0069 $info['zip']['comments']['comment'][] = $info['zip']['end_central_directory']['comment']; 0070 } 0071 0072 if (isset($info['zip']['central_directory'][0]['compression_method'])) { 0073 $info['zip']['compression_method'] = $info['zip']['central_directory'][0]['compression_method']; 0074 } 0075 if (isset($info['zip']['central_directory'][0]['flags']['compression_speed'])) { 0076 $info['zip']['compression_speed'] = $info['zip']['central_directory'][0]['flags']['compression_speed']; 0077 } 0078 if (isset($info['zip']['compression_method']) && ($info['zip']['compression_method'] == 'store') && !isset($info['zip']['compression_speed'])) { 0079 $info['zip']['compression_speed'] = 'store'; 0080 } 0081 0082 // secondary check - we (should) already have all the info we NEED from the Central Directory above, but scanning each 0083 // Local File Header entry will 0084 foreach ($info['zip']['central_directory'] as $central_directory_entry) { 0085 $this->fseek($central_directory_entry['entry_offset']); 0086 if ($fileentry = $this->ZIPparseLocalFileHeader()) { 0087 $info['zip']['entries'][] = $fileentry; 0088 } else { 0089 $info['warning'][] = 'Error parsing Local File Header at offset '.$central_directory_entry['entry_offset']; 0090 } 0091 } 0092 0093 if (!empty($info['zip']['files']['[Content_Types].xml']) && 0094 !empty($info['zip']['files']['_rels']['.rels']) && 0095 !empty($info['zip']['files']['docProps']['app.xml']) && 0096 !empty($info['zip']['files']['docProps']['core.xml'])) { 0097 // http://technet.microsoft.com/en-us/library/cc179224.aspx 0098 $info['fileformat'] = 'zip.msoffice'; 0099 if (!empty($ThisFileInfo['zip']['files']['ppt'])) { 0100 $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'; 0101 } elseif (!empty($ThisFileInfo['zip']['files']['xl'])) { 0102 $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; 0103 } elseif (!empty($ThisFileInfo['zip']['files']['word'])) { 0104 $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; 0105 } 0106 } 0107 0108 return true; 0109 } 0110 } 0111 } 0112 0113 if (!$this->getZIPentriesFilepointer()) { 0114 unset($info['zip']); 0115 $info['fileformat'] = ''; 0116 $info['error'][] = 'Cannot find End Of Central Directory (truncated file?)'; 0117 return false; 0118 } 0119 0120 // central directory couldn't be found and/or parsed 0121 // scan through actual file data entries, recover as much as possible from probable trucated file 0122 if ($info['zip']['compressed_size'] > ($info['filesize'] - 46 - 22)) { 0123 $info['error'][] = 'Warning: Truncated file! - Total compressed file sizes ('.$info['zip']['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($info['filesize'] - 46 - 22).' bytes)'; 0124 } 0125 $info['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete'; 0126 foreach ($info['zip']['entries'] as $key => $valuearray) { 0127 $info['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size']; 0128 } 0129 return true; 0130 } 0131 0132 0133 public function getZIPHeaderFilepointerTopDown() { 0134 $info = &$this->getid3->info; 0135 0136 $info['fileformat'] = 'zip'; 0137 0138 $info['zip']['compressed_size'] = 0; 0139 $info['zip']['uncompressed_size'] = 0; 0140 $info['zip']['entries_count'] = 0; 0141 0142 rewind($this->getid3->fp); 0143 while ($fileentry = $this->ZIPparseLocalFileHeader()) { 0144 $info['zip']['entries'][] = $fileentry; 0145 $info['zip']['entries_count']++; 0146 } 0147 if ($info['zip']['entries_count'] == 0) { 0148 $info['error'][] = 'No Local File Header entries found'; 0149 return false; 0150 } 0151 0152 $info['zip']['entries_count'] = 0; 0153 while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($this->getid3->fp)) { 0154 $info['zip']['central_directory'][] = $centraldirectoryentry; 0155 $info['zip']['entries_count']++; 0156 $info['zip']['compressed_size'] += $centraldirectoryentry['compressed_size']; 0157 $info['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size']; 0158 } 0159 if ($info['zip']['entries_count'] == 0) { 0160 $info['error'][] = 'No Central Directory entries found (truncated file?)'; 0161 return false; 0162 } 0163 0164 if ($EOCD = $this->ZIPparseEndOfCentralDirectory()) { 0165 $info['zip']['end_central_directory'] = $EOCD; 0166 } else { 0167 $info['error'][] = 'No End Of Central Directory entry found (truncated file?)'; 0168 return false; 0169 } 0170 0171 if (!empty($info['zip']['end_central_directory']['comment'])) { 0172 $info['zip']['comments']['comment'][] = $info['zip']['end_central_directory']['comment']; 0173 } 0174 0175 return true; 0176 } 0177 0178 0179 public function getZIPentriesFilepointer() { 0180 $info = &$this->getid3->info; 0181 0182 $info['zip']['compressed_size'] = 0; 0183 $info['zip']['uncompressed_size'] = 0; 0184 $info['zip']['entries_count'] = 0; 0185 0186 rewind($this->getid3->fp); 0187 while ($fileentry = $this->ZIPparseLocalFileHeader()) { 0188 $info['zip']['entries'][] = $fileentry; 0189 $info['zip']['entries_count']++; 0190 $info['zip']['compressed_size'] += $fileentry['compressed_size']; 0191 $info['zip']['uncompressed_size'] += $fileentry['uncompressed_size']; 0192 } 0193 if ($info['zip']['entries_count'] == 0) { 0194 $info['error'][] = 'No Local File Header entries found'; 0195 return false; 0196 } 0197 0198 return true; 0199 } 0200 0201 0202 public function ZIPparseLocalFileHeader() { 0203 $LocalFileHeader['offset'] = $this->ftell(); 0204 0205 $ZIPlocalFileHeader = $this->fread(30); 0206 0207 $LocalFileHeader['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 0, 4)); 0208 if ($LocalFileHeader['raw']['signature'] != 0x04034B50) { // "PK\x03\x04" 0209 // invalid Local File Header Signature 0210 $this->fseek($LocalFileHeader['offset']); // seek back to where filepointer originally was so it can be handled properly 0211 return false; 0212 } 0213 $LocalFileHeader['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 4, 2)); 0214 $LocalFileHeader['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 6, 2)); 0215 $LocalFileHeader['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 8, 2)); 0216 $LocalFileHeader['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 10, 2)); 0217 $LocalFileHeader['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 12, 2)); 0218 $LocalFileHeader['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 14, 4)); 0219 $LocalFileHeader['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 18, 4)); 0220 $LocalFileHeader['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 22, 4)); 0221 $LocalFileHeader['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 26, 2)); 0222 $LocalFileHeader['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 28, 2)); 0223 0224 $LocalFileHeader['extract_version'] = sprintf('%1.1f', $LocalFileHeader['raw']['extract_version'] / 10); 0225 $LocalFileHeader['host_os'] = $this->ZIPversionOSLookup(($LocalFileHeader['raw']['extract_version'] & 0xFF00) >> 8); 0226 $LocalFileHeader['compression_method'] = $this->ZIPcompressionMethodLookup($LocalFileHeader['raw']['compression_method']); 0227 $LocalFileHeader['compressed_size'] = $LocalFileHeader['raw']['compressed_size']; 0228 $LocalFileHeader['uncompressed_size'] = $LocalFileHeader['raw']['uncompressed_size']; 0229 $LocalFileHeader['flags'] = $this->ZIPparseGeneralPurposeFlags($LocalFileHeader['raw']['general_flags'], $LocalFileHeader['raw']['compression_method']); 0230 $LocalFileHeader['last_modified_timestamp'] = $this->DOStime2UNIXtime($LocalFileHeader['raw']['last_mod_file_date'], $LocalFileHeader['raw']['last_mod_file_time']); 0231 0232 $FilenameExtrafieldLength = $LocalFileHeader['raw']['filename_length'] + $LocalFileHeader['raw']['extra_field_length']; 0233 if ($FilenameExtrafieldLength > 0) { 0234 $ZIPlocalFileHeader .= $this->fread($FilenameExtrafieldLength); 0235 0236 if ($LocalFileHeader['raw']['filename_length'] > 0) { 0237 $LocalFileHeader['filename'] = substr($ZIPlocalFileHeader, 30, $LocalFileHeader['raw']['filename_length']); 0238 } 0239 if ($LocalFileHeader['raw']['extra_field_length'] > 0) { 0240 $LocalFileHeader['raw']['extra_field_data'] = substr($ZIPlocalFileHeader, 30 + $LocalFileHeader['raw']['filename_length'], $LocalFileHeader['raw']['extra_field_length']); 0241 } 0242 } 0243 0244 if ($LocalFileHeader['compressed_size'] == 0) { 0245 // *Could* be a zero-byte file 0246 // But could also be a file written on the fly that didn't know compressed filesize beforehand. 0247 // Correct compressed filesize should be in the data_descriptor located after this file data, and also in Central Directory (at end of zip file) 0248 if (!empty($this->getid3->info['zip']['central_directory'])) { 0249 foreach ($this->getid3->info['zip']['central_directory'] as $central_directory_entry) { 0250 if ($central_directory_entry['entry_offset'] == $LocalFileHeader['offset']) { 0251 if ($central_directory_entry['compressed_size'] > 0) { 0252 // overwrite local zero value (but not ['raw']'compressed_size']) so that seeking for data_descriptor (and next file entry) works correctly 0253 $LocalFileHeader['compressed_size'] = $central_directory_entry['compressed_size']; 0254 } 0255 break; 0256 } 0257 } 0258 } 0259 0260 } 0261 $LocalFileHeader['data_offset'] = $this->ftell(); 0262 $this->fseek($LocalFileHeader['compressed_size'], SEEK_CUR); // this should (but may not) match value in $LocalFileHeader['raw']['compressed_size'] -- $LocalFileHeader['compressed_size'] could have been overwritten above with value from Central Directory 0263 0264 if ($LocalFileHeader['flags']['data_descriptor_used']) { 0265 $DataDescriptor = $this->fread(16); 0266 $LocalFileHeader['data_descriptor']['signature'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 0, 4)); 0267 if ($LocalFileHeader['data_descriptor']['signature'] != 0x08074B50) { // "PK\x07\x08" 0268 $this->getid3->warning[] = 'invalid Local File Header Data Descriptor Signature at offset '.($this->ftell() - 16).' - expecting 08 07 4B 50, found '.getid3_lib::PrintHexBytes($LocalFileHeader['data_descriptor']['signature']); 0269 $this->fseek($LocalFileHeader['offset']); // seek back to where filepointer originally was so it can be handled properly 0270 return false; 0271 } 0272 $LocalFileHeader['data_descriptor']['crc_32'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 4, 4)); 0273 $LocalFileHeader['data_descriptor']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 8, 4)); 0274 $LocalFileHeader['data_descriptor']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor, 12, 4)); 0275 if (!$LocalFileHeader['raw']['compressed_size'] && $LocalFileHeader['data_descriptor']['compressed_size']) { 0276 foreach ($this->getid3->info['zip']['central_directory'] as $central_directory_entry) { 0277 if ($central_directory_entry['entry_offset'] == $LocalFileHeader['offset']) { 0278 if ($LocalFileHeader['data_descriptor']['compressed_size'] == $central_directory_entry['compressed_size']) { 0279 // $LocalFileHeader['compressed_size'] already set from Central Directory 0280 } else { 0281 $this->getid3->info['warning'][] = 'conflicting compressed_size from data_descriptor ('.$LocalFileHeader['data_descriptor']['compressed_size'].') vs Central Directory ('.$central_directory_entry['compressed_size'].') for file at offset '.$LocalFileHeader['offset']; 0282 } 0283 0284 if ($LocalFileHeader['data_descriptor']['uncompressed_size'] == $central_directory_entry['uncompressed_size']) { 0285 $LocalFileHeader['uncompressed_size'] = $LocalFileHeader['data_descriptor']['uncompressed_size']; 0286 } else { 0287 $this->getid3->info['warning'][] = 'conflicting uncompressed_size from data_descriptor ('.$LocalFileHeader['data_descriptor']['uncompressed_size'].') vs Central Directory ('.$central_directory_entry['uncompressed_size'].') for file at offset '.$LocalFileHeader['offset']; 0288 } 0289 break; 0290 } 0291 } 0292 } 0293 } 0294 return $LocalFileHeader; 0295 } 0296 0297 0298 public function ZIPparseCentralDirectory() { 0299 $CentralDirectory['offset'] = $this->ftell(); 0300 0301 $ZIPcentralDirectory = $this->fread(46); 0302 0303 $CentralDirectory['raw']['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 0, 4)); 0304 if ($CentralDirectory['raw']['signature'] != 0x02014B50) { 0305 // invalid Central Directory Signature 0306 $this->fseek($CentralDirectory['offset']); // seek back to where filepointer originally was so it can be handled properly 0307 return false; 0308 } 0309 $CentralDirectory['raw']['create_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 4, 2)); 0310 $CentralDirectory['raw']['extract_version'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 6, 2)); 0311 $CentralDirectory['raw']['general_flags'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 8, 2)); 0312 $CentralDirectory['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 10, 2)); 0313 $CentralDirectory['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 12, 2)); 0314 $CentralDirectory['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 14, 2)); 0315 $CentralDirectory['raw']['crc_32'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 16, 4)); 0316 $CentralDirectory['raw']['compressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 20, 4)); 0317 $CentralDirectory['raw']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 24, 4)); 0318 $CentralDirectory['raw']['filename_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 28, 2)); 0319 $CentralDirectory['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 30, 2)); 0320 $CentralDirectory['raw']['file_comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 32, 2)); 0321 $CentralDirectory['raw']['disk_number_start'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 34, 2)); 0322 $CentralDirectory['raw']['internal_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 36, 2)); 0323 $CentralDirectory['raw']['external_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 38, 4)); 0324 $CentralDirectory['raw']['local_header_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 42, 4)); 0325 0326 $CentralDirectory['entry_offset'] = $CentralDirectory['raw']['local_header_offset']; 0327 $CentralDirectory['create_version'] = sprintf('%1.1f', $CentralDirectory['raw']['create_version'] / 10); 0328 $CentralDirectory['extract_version'] = sprintf('%1.1f', $CentralDirectory['raw']['extract_version'] / 10); 0329 $CentralDirectory['host_os'] = $this->ZIPversionOSLookup(($CentralDirectory['raw']['extract_version'] & 0xFF00) >> 8); 0330 $CentralDirectory['compression_method'] = $this->ZIPcompressionMethodLookup($CentralDirectory['raw']['compression_method']); 0331 $CentralDirectory['compressed_size'] = $CentralDirectory['raw']['compressed_size']; 0332 $CentralDirectory['uncompressed_size'] = $CentralDirectory['raw']['uncompressed_size']; 0333 $CentralDirectory['flags'] = $this->ZIPparseGeneralPurposeFlags($CentralDirectory['raw']['general_flags'], $CentralDirectory['raw']['compression_method']); 0334 $CentralDirectory['last_modified_timestamp'] = $this->DOStime2UNIXtime($CentralDirectory['raw']['last_mod_file_date'], $CentralDirectory['raw']['last_mod_file_time']); 0335 0336 $FilenameExtrafieldCommentLength = $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'] + $CentralDirectory['raw']['file_comment_length']; 0337 if ($FilenameExtrafieldCommentLength > 0) { 0338 $FilenameExtrafieldComment = $this->fread($FilenameExtrafieldCommentLength); 0339 0340 if ($CentralDirectory['raw']['filename_length'] > 0) { 0341 $CentralDirectory['filename'] = substr($FilenameExtrafieldComment, 0, $CentralDirectory['raw']['filename_length']); 0342 } 0343 if ($CentralDirectory['raw']['extra_field_length'] > 0) { 0344 $CentralDirectory['raw']['extra_field_data'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'], $CentralDirectory['raw']['extra_field_length']); 0345 } 0346 if ($CentralDirectory['raw']['file_comment_length'] > 0) { 0347 $CentralDirectory['file_comment'] = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'], $CentralDirectory['raw']['file_comment_length']); 0348 } 0349 } 0350 0351 return $CentralDirectory; 0352 } 0353 0354 public function ZIPparseEndOfCentralDirectory() { 0355 $EndOfCentralDirectory['offset'] = $this->ftell(); 0356 0357 $ZIPendOfCentralDirectory = $this->fread(22); 0358 0359 $EndOfCentralDirectory['signature'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 0, 4)); 0360 if ($EndOfCentralDirectory['signature'] != 0x06054B50) { 0361 // invalid End Of Central Directory Signature 0362 $this->fseek($EndOfCentralDirectory['offset']); // seek back to where filepointer originally was so it can be handled properly 0363 return false; 0364 } 0365 $EndOfCentralDirectory['disk_number_current'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2)); 0366 $EndOfCentralDirectory['disk_number_start_directory'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 6, 2)); 0367 $EndOfCentralDirectory['directory_entries_this_disk'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 8, 2)); 0368 $EndOfCentralDirectory['directory_entries_total'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 10, 2)); 0369 $EndOfCentralDirectory['directory_size'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 12, 4)); 0370 $EndOfCentralDirectory['directory_offset'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 16, 4)); 0371 $EndOfCentralDirectory['comment_length'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2)); 0372 0373 if ($EndOfCentralDirectory['comment_length'] > 0) { 0374 $EndOfCentralDirectory['comment'] = $this->fread($EndOfCentralDirectory['comment_length']); 0375 } 0376 0377 return $EndOfCentralDirectory; 0378 } 0379 0380 0381 public static function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) { 0382 // https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip-printable.html 0383 $ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001); 0384 // 0x0002 -- see below 0385 // 0x0004 -- see below 0386 $ParsedFlags['data_descriptor_used'] = (bool) ($flagbytes & 0x0008); 0387 $ParsedFlags['enhanced_deflation'] = (bool) ($flagbytes & 0x0010); 0388 $ParsedFlags['compressed_patched_data'] = (bool) ($flagbytes & 0x0020); 0389 $ParsedFlags['strong_encryption'] = (bool) ($flagbytes & 0x0040); 0390 // 0x0080 - unused 0391 // 0x0100 - unused 0392 // 0x0200 - unused 0393 // 0x0400 - unused 0394 $ParsedFlags['language_encoding'] = (bool) ($flagbytes & 0x0800); 0395 // 0x1000 - reserved 0396 $ParsedFlags['mask_header_values'] = (bool) ($flagbytes & 0x2000); 0397 // 0x4000 - reserved 0398 // 0x8000 - reserved 0399 0400 switch ($compressionmethod) { 0401 case 6: 0402 $ParsedFlags['dictionary_size'] = (($flagbytes & 0x0002) ? 8192 : 4096); 0403 $ParsedFlags['shannon_fano_trees'] = (($flagbytes & 0x0004) ? 3 : 2); 0404 break; 0405 0406 case 8: 0407 case 9: 0408 switch (($flagbytes & 0x0006) >> 1) { 0409 case 0: 0410 $ParsedFlags['compression_speed'] = 'normal'; 0411 break; 0412 case 1: 0413 $ParsedFlags['compression_speed'] = 'maximum'; 0414 break; 0415 case 2: 0416 $ParsedFlags['compression_speed'] = 'fast'; 0417 break; 0418 case 3: 0419 $ParsedFlags['compression_speed'] = 'superfast'; 0420 break; 0421 } 0422 break; 0423 } 0424 0425 return $ParsedFlags; 0426 } 0427 0428 0429 public static function ZIPversionOSLookup($index) { 0430 static $ZIPversionOSLookup = array( 0431 0 => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)', 0432 1 => 'Amiga', 0433 2 => 'OpenVMS', 0434 3 => 'Unix', 0435 4 => 'VM/CMS', 0436 5 => 'Atari ST', 0437 6 => 'OS/2 H.P.F.S.', 0438 7 => 'Macintosh', 0439 8 => 'Z-System', 0440 9 => 'CP/M', 0441 10 => 'Windows NTFS', 0442 11 => 'MVS', 0443 12 => 'VSE', 0444 13 => 'Acorn Risc', 0445 14 => 'VFAT', 0446 15 => 'Alternate MVS', 0447 16 => 'BeOS', 0448 17 => 'Tandem', 0449 18 => 'OS/400', 0450 19 => 'OS/X (Darwin)', 0451 ); 0452 0453 return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]'); 0454 } 0455 0456 public static function ZIPcompressionMethodLookup($index) { 0457 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/ZIP.html 0458 static $ZIPcompressionMethodLookup = array( 0459 0 => 'store', 0460 1 => 'shrink', 0461 2 => 'reduce-1', 0462 3 => 'reduce-2', 0463 4 => 'reduce-3', 0464 5 => 'reduce-4', 0465 6 => 'implode', 0466 7 => 'tokenize', 0467 8 => 'deflate', 0468 9 => 'deflate64', 0469 10 => 'Imploded (old IBM TERSE)', 0470 11 => 'RESERVED[11]', 0471 12 => 'BZIP2', 0472 13 => 'RESERVED[13]', 0473 14 => 'LZMA (EFS)', 0474 15 => 'RESERVED[15]', 0475 16 => 'RESERVED[16]', 0476 17 => 'RESERVED[17]', 0477 18 => 'IBM TERSE (new)', 0478 19 => 'IBM LZ77 z Architecture (PFS)', 0479 96 => 'JPEG recompressed', 0480 97 => 'WavPack compressed', 0481 98 => 'PPMd version I, Rev 1', 0482 ); 0483 0484 return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]'); 0485 } 0486 0487 public static function DOStime2UNIXtime($DOSdate, $DOStime) { 0488 // wFatDate 0489 // Specifies the MS-DOS date. The date is a packed 16-bit value with the following format: 0490 // Bits Contents 0491 // 0-4 Day of the month (1-31) 0492 // 5-8 Month (1 = January, 2 = February, and so on) 0493 // 9-15 Year offset from 1980 (add 1980 to get actual year) 0494 0495 $UNIXday = ($DOSdate & 0x001F); 0496 $UNIXmonth = (($DOSdate & 0x01E0) >> 5); 0497 $UNIXyear = (($DOSdate & 0xFE00) >> 9) + 1980; 0498 0499 // wFatTime 0500 // Specifies the MS-DOS time. The time is a packed 16-bit value with the following format: 0501 // Bits Contents 0502 // 0-4 Second divided by 2 0503 // 5-10 Minute (0-59) 0504 // 11-15 Hour (0-23 on a 24-hour clock) 0505 0506 $UNIXsecond = ($DOStime & 0x001F) * 2; 0507 $UNIXminute = (($DOStime & 0x07E0) >> 5); 0508 $UNIXhour = (($DOStime & 0xF800) >> 11); 0509 0510 return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear); 0511 } 0512 0513 }