Documentation TYPO3 par Ameos |
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) Vincent Blavet <vincent@phpconcept.net> 00006 * (c) 2005-2006 Karsten Dambekalns <karsten@typo3.org> 00007 * All rights reserved 00008 * 00009 * This library is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Lesser General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 2.1 of the License, or (at your option) any later version. 00013 * 00014 * This library is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 * Lesser General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU Lesser General Public 00020 * License along with this library; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 00022 * MA 02110-1301 USA 00023 * 00024 * This copyright notice MUST APPEAR in all copies of the script! 00025 ***************************************************************/ 00036 // Constants 00037 define( 'ARCHIVE_ZIP_READ_BLOCK_SIZE', 2048 ); 00038 00039 // File list separator 00040 define( 'ARCHIVE_ZIP_SEPARATOR', ',' ); 00041 00042 define( 'ARCHIVE_ZIP_TEMPORARY_DIR', '' ); 00043 00044 // Error codes 00045 define( 'ARCHIVE_ZIP_ERR_NO_ERROR', 0 ); 00046 define( 'ARCHIVE_ZIP_ERR_WRITE_OPEN_FAIL', -1 ); 00047 define( 'ARCHIVE_ZIP_ERR_READ_OPEN_FAIL', -2 ); 00048 define( 'ARCHIVE_ZIP_ERR_INVALID_PARAMETER', -3 ); 00049 define( 'ARCHIVE_ZIP_ERR_MISSING_FILE', -4 ); 00050 define( 'ARCHIVE_ZIP_ERR_FILENAME_TOO_LONG', -5 ); 00051 define( 'ARCHIVE_ZIP_ERR_INVALID_ZIP', -6 ); 00052 define( 'ARCHIVE_ZIP_ERR_BAD_EXTRACTED_FILE', -7 ); 00053 define( 'ARCHIVE_ZIP_ERR_DIR_CREATE_FAIL', -8 ); 00054 define( 'ARCHIVE_ZIP_ERR_BAD_EXTENSION', -9 ); 00055 define( 'ARCHIVE_ZIP_ERR_BAD_FORMAT', -10 ); 00056 define( 'ARCHIVE_ZIP_ERR_DELETE_FILE_FAIL', -11 ); 00057 define( 'ARCHIVE_ZIP_ERR_RENAME_FILE_FAIL', -12 ); 00058 define( 'ARCHIVE_ZIP_ERR_BAD_CHECKSUM', -13 ); 00059 define( 'ARCHIVE_ZIP_ERR_INVALID_ARCHIVE_ZIP', -14 ); 00060 define( 'ARCHIVE_ZIP_ERR_MISSING_OPTION_VALUE', -15 ); 00061 define( 'ARCHIVE_ZIP_ERR_INVALID_PARAM_VALUE', -16 ); 00062 00063 // Warning codes 00064 define( 'ARCHIVE_ZIP_WARN_NO_WARNING', 0 ); 00065 define( 'ARCHIVE_ZIP_WARN_FILE_EXIST', 1 ); 00066 00067 // Methods parameters 00068 define( 'ARCHIVE_ZIP_PARAM_PATH', 'path' ); 00069 define( 'ARCHIVE_ZIP_PARAM_ADD_PATH', 'add_path' ); 00070 define( 'ARCHIVE_ZIP_PARAM_REMOVE_PATH', 'remove_path' ); 00071 define( 'ARCHIVE_ZIP_PARAM_REMOVE_ALL_PATH', 'remove_all_path' ); 00072 define( 'ARCHIVE_ZIP_PARAM_SET_CHMOD', 'set_chmod' ); 00073 define( 'ARCHIVE_ZIP_PARAM_EXTRACT_AS_STRING', 'extract_as_string' ); 00074 define( 'ARCHIVE_ZIP_PARAM_NO_COMPRESSION', 'no_compression' ); 00075 00076 define( 'ARCHIVE_ZIP_PARAM_PRE_EXTRACT', 'callback_pre_extract' ); 00077 define( 'ARCHIVE_ZIP_PARAM_POST_EXTRACT', 'callback_post_extract' ); 00078 define( 'ARCHIVE_ZIP_PARAM_PRE_ADD', 'callback_pre_add' ); 00079 define( 'ARCHIVE_ZIP_PARAM_POST_ADD', 'callback_post_add' ); 00080 00081 00082 00089 class em_unzip { 00095 var $_zipname=''; 00096 00102 var $_zip_fd=0; 00103 00107 var $_error_code=1; 00108 00112 var $_error_string=''; 00113 00122 function em_unzip($p_zipname) { 00123 00124 // Check the zlib 00125 if (!extension_loaded('zlib')) { 00126 die("The extension 'zlib' couldn't be found.\n". 00127 "Please make sure your version of PHP was built ". 00128 "with 'zlib' support.\n"); 00129 } 00130 00131 // Set the attributes 00132 $this->_zipname = $p_zipname; 00133 $this->_zip_fd = 0; 00134 00135 return; 00136 } 00137 00138 00139 00159 function extract($p_params=0) { 00160 $this->_errorReset(); 00161 00162 // Check archive 00163 if (!$this->_checkFormat()) { 00164 return(0); 00165 } 00166 00167 // Set default values 00168 if ($p_params === 0) { 00169 $p_params = array(); 00170 } 00171 if ($this->_check_parameters($p_params, 00172 array ('extract_as_string' => false, 00173 'add_path' => '', 00174 'remove_path' => '', 00175 'remove_all_path' => false, 00176 'callback_pre_extract' => '', 00177 'callback_post_extract' => '', 00178 'set_chmod' => 0) ) != 1) { 00179 return 0; 00180 } 00181 00182 // Call the extracting fct 00183 $v_list = array(); 00184 if ($this->_extractByRule($v_list, $p_params) != 1) { 00185 unset($v_list); 00186 return(0); 00187 } 00188 00189 return $v_list; 00190 } 00191 00198 function errorCode() { 00199 return($this->_error_code); 00200 } 00201 00209 function errorName($p_with_code=false) { 00210 $v_const_list = get_defined_constants(); 00211 00212 // Extract error constants from all const. 00213 for (reset($v_const_list); 00214 list($v_key, $v_value) = each($v_const_list);) { 00215 if (substr($v_key, 0, strlen('ARCHIVE_ZIP_ERR_')) =='ARCHIVE_ZIP_ERR_') { 00216 $v_error_list[$v_key] = $v_value; 00217 } 00218 } 00219 00220 // Search the name form the code value 00221 $v_key=array_search($this->_error_code, $v_error_list, true); 00222 if ($v_key!=false) { 00223 $v_value = $v_key; 00224 } else { 00225 $v_value = 'NoName'; 00226 } 00227 00228 if ($p_with_code) { 00229 return($v_value.' ('.$this->_error_code.')'); 00230 } else { 00231 return($v_value); 00232 } 00233 } 00234 00245 function errorInfo($p_full=false) { 00246 if ($p_full) { 00247 return($this->errorName(true)." : ".$this->_error_string); 00248 } else { 00249 return($this->_error_string." [code ".$this->_error_code."]"); 00250 } 00251 } 00252 00253 00261 function _checkFormat($p_level=0) { 00262 $v_result = true; 00263 00264 // Reset the error handler 00265 $this->_errorReset(); 00266 00267 // Look if the file exits 00268 if (!is_file($this->_zipname)) { 00269 // Error log 00270 $this->_errorLog(ARCHIVE_ZIP_ERR_MISSING_FILE, 00271 "Missing archive file '".$this->_zipname."'"); 00272 return(false); 00273 } 00274 00275 // Check that the file is readeable 00276 if (!is_readable($this->_zipname)) { 00277 // Error log 00278 $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL, 00279 "Unable to read archive '".$this->_zipname."'"); 00280 return(false); 00281 } 00282 00283 // Check the magic code 00284 // TBC 00285 00286 // Check the central header 00287 // TBC 00288 00289 // Check each file header 00290 // TBC 00291 00292 // Return 00293 return $v_result; 00294 } 00295 00296 00303 function _openFd($p_mode) { 00304 $v_result=1; 00305 00306 // Look if already open 00307 if ($this->_zip_fd != 0) { 00308 $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL, 00309 'Zip file \''.$this->_zipname.'\' already open'); 00310 return em_unzip::errorCode(); 00311 } 00312 00313 // Open the zip file 00314 if (($this->_zip_fd = @fopen($this->_zipname, $p_mode)) == 0) { 00315 $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL, 00316 'Unable to open archive \''.$this->_zipname 00317 .'\' in '.$p_mode.' mode'); 00318 return em_unzip::errorCode(); 00319 } 00320 00321 // Return 00322 return $v_result; 00323 } 00324 00331 function _closeFd() { 00332 $v_result=1; 00333 00334 if ($this->_zip_fd != 0) 00335 @fclose($this->_zip_fd); 00336 $this->_zip_fd = 0; 00337 00338 // Return 00339 return $v_result; 00340 } 00341 00342 00343 00350 function _convertHeader2FileInfo($p_header, &$p_info) { 00351 $v_result=1; 00352 00353 // Get the interesting attributes 00354 $p_info['filename'] = $p_header['filename']; 00355 $p_info['stored_filename'] = $p_header['stored_filename']; 00356 $p_info['size'] = $p_header['size']; 00357 $p_info['compressed_size'] = $p_header['compressed_size']; 00358 $p_info['mtime'] = $p_header['mtime']; 00359 $p_info['comment'] = $p_header['comment']; 00360 $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010); 00361 $p_info['index'] = $p_header['index']; 00362 $p_info['status'] = $p_header['status']; 00363 00364 // Return 00365 return $v_result; 00366 } 00367 00368 00369 // Function : _extractByRule() 00370 // Description : 00371 // Extract a file or directory depending of rules (by index, by name, ...) 00372 // Parameters : 00373 // $p_file_list : An array where will be placed the properties of each 00374 // extracted file 00375 // $p_path : Path to add while writing the extracted files 00376 // $p_remove_path : Path to remove (from the file memorized path) while writing the 00377 // extracted files. If the path does not match the file path, 00378 // the file is extracted with its memorized path. 00379 // $p_remove_path does not apply to 'list' mode. 00380 // $p_path and $p_remove_path are commulative. 00381 // Return Values : 00382 // 1 on success,0 or less on error (see error code list) 00383 00390 function _extractByRule(&$p_file_list, &$p_params) 00391 { 00392 $v_result=1; 00393 00394 $p_path = $p_params['add_path']; 00395 $p_remove_path = $p_params['remove_path']; 00396 $p_remove_all_path = $p_params['remove_all_path']; 00397 00398 // Check the path 00399 if (($p_path == "") 00400 || ((substr($p_path, 0, 1) != "/") 00401 && (substr($p_path, 0, 3) != "../") && (substr($p_path,1,2)!=":/"))) 00402 $p_path = "./".$p_path; 00403 00404 // Reduce the path last (and duplicated) '/' 00405 if (($p_path != "./") && ($p_path != "/")) { 00406 // Look for the path end '/' 00407 while (substr($p_path, -1) == "/") { 00408 $p_path = substr($p_path, 0, strlen($p_path)-1); 00409 } 00410 } 00411 00412 // Open the zip file 00413 if (($v_result = $this->_openFd('rb')) != 1) 00414 { 00415 return $v_result; 00416 } 00417 00418 // Read the central directory informations 00419 $v_central_dir = array(); 00420 if (($v_result = $this->_readEndCentralDir($v_central_dir)) != 1) 00421 { 00422 // Close the zip file 00423 $this->_closeFd(); 00424 00425 return $v_result; 00426 } 00427 00428 // Start at beginning of Central Dir 00429 $v_pos_entry = $v_central_dir['offset']; 00430 00431 // Read each entry 00432 $j_start = 0; 00433 for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) { 00434 // Read next Central dir entry 00435 @rewind($this->_zip_fd); 00436 if (@fseek($this->_zip_fd, $v_pos_entry)) { 00437 $this->_closeFd(); 00438 00439 $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_ARCHIVE_ZIP, 00440 'Invalid archive size'); 00441 00442 return em_unzip::errorCode(); 00443 } 00444 00445 // Read the file header 00446 $v_header = array(); 00447 if (($v_result = $this->_readCentralFileHeader($v_header)) != 1) { 00448 $this->_closeFd(); 00449 00450 return $v_result; 00451 } 00452 00453 // Store the index 00454 $v_header['index'] = $i; 00455 00456 // Store the file position 00457 $v_pos_entry = ftell($this->_zip_fd); 00458 00459 00460 // Go to the file position 00461 @rewind($this->_zip_fd); 00462 if (@fseek($this->_zip_fd, $v_header['offset'])) 00463 { 00464 // Close the zip file 00465 $this->_closeFd(); 00466 00467 // Error log 00468 $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); 00469 00470 // Return 00471 return em_unzip::errorCode(); 00472 } 00473 00474 // Extracting the file 00475 if (($v_result = $this->_extractFile($v_header, $p_path, $p_remove_path, $p_remove_all_path, $p_params)) != 1) 00476 { 00477 // Close the zip file 00478 $this->_closeFd(); 00479 00480 return $v_result; 00481 } 00482 00483 // Get the only interesting attributes 00484 if (($v_result = $this->_convertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) 00485 { 00486 // Close the zip file 00487 $this->_closeFd(); 00488 00489 return $v_result; 00490 } 00491 } 00492 00493 // Close the zip file 00494 $this->_closeFd(); 00495 00496 // Return 00497 return $v_result; 00498 } 00499 00506 function _extractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_params) { 00507 $v_result=1; 00508 00509 // Read the file header 00510 $v_header = ''; 00511 if (($v_result = $this->_readFileHeader($v_header)) != 1) 00512 { 00513 // Return 00514 return $v_result; 00515 } 00516 00517 00518 // Check that the file header is coherent with $p_entry info 00519 // TBC 00520 00521 // Look for all path to remove 00522 if ($p_remove_all_path == true) { 00523 // Get the basename of the path 00524 $p_entry['filename'] = basename($p_entry['filename']); 00525 } 00526 00527 // Look for path to remove 00528 else if ($p_remove_path != "") 00529 { 00530 //if (strcmp($p_remove_path, $p_entry['filename'])==0) 00531 if ($this->_tool_PathInclusion($p_remove_path, $p_entry['filename']) == 2) 00532 { 00533 00534 // Change the file status 00535 $p_entry['status'] = "filtered"; 00536 00537 // Return 00538 return $v_result; 00539 } 00540 00541 $p_remove_path_size = strlen($p_remove_path); 00542 if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) 00543 { 00544 00545 // Remove the path 00546 $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size); 00547 00548 } 00549 } 00550 00551 // Add the path 00552 if ($p_path != '') 00553 { 00554 $p_entry['filename'] = $p_path."/".$p_entry['filename']; 00555 } 00556 00557 // Look for pre-extract callback 00558 if ( (isset($p_params[ARCHIVE_ZIP_PARAM_PRE_EXTRACT])) 00559 && ($p_params[ARCHIVE_ZIP_PARAM_PRE_EXTRACT] != '')) { 00560 00561 // Generate a local information 00562 $v_local_header = array(); 00563 $this->_convertHeader2FileInfo($p_entry, $v_local_header); 00564 00565 // Call the callback 00566 // Here I do not use call_user_func() because I need to send a reference to the 00567 // header. 00568 eval('$v_result = '.$p_params[ARCHIVE_ZIP_PARAM_PRE_EXTRACT].'(ARCHIVE_ZIP_PARAM_PRE_EXTRACT, $v_local_header);'); 00569 if ($v_result == 0) { 00570 // Change the file status 00571 $p_entry['status'] = "skipped"; 00572 $v_result = 1; 00573 } 00574 00575 // Update the informations 00576 // Only some fields can be modified 00577 $p_entry['filename'] = $v_local_header['filename']; 00578 } 00579 00580 // Trace 00581 00582 // Look if extraction should be done 00583 if ($p_entry['status'] == 'ok') { 00584 00585 // Look for specific actions while the file exist 00586 if (file_exists($p_entry['filename'])) { 00587 // Look if file is a directory 00588 if (is_dir($p_entry['filename'])) { 00589 // Change the file status 00590 $p_entry['status'] = "already_a_directory"; 00591 00592 // Return 00593 //return $v_result; 00594 } 00595 // Look if file is write protected 00596 else if (!is_writeable($p_entry['filename'])) { 00597 // Change the file status 00598 $p_entry['status'] = "write_protected"; 00599 00600 // Return 00601 //return $v_result; 00602 } 00603 00604 // Look if the extracted file is older 00605 else if (filemtime($p_entry['filename']) > $p_entry['mtime']) { 00606 // Change the file status 00607 $p_entry['status'] = "newer_exist"; 00608 00609 // Return 00610 //return $v_result; 00611 } 00612 } 00613 00614 // Check the directory availability and create it if necessary 00615 else { 00616 if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/')) 00617 $v_dir_to_check = $p_entry['filename']; 00618 else if (!strstr($p_entry['filename'], "/")) 00619 $v_dir_to_check = ""; 00620 else 00621 $v_dir_to_check = dirname($p_entry['filename']); 00622 00623 if (($v_result = $this->_dirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) { 00624 // Change the file status 00625 $p_entry['status'] = "path_creation_fail"; 00626 00627 // Return 00628 //return $v_result; 00629 $v_result = 1; 00630 } 00631 } 00632 } 00633 00634 // Look if extraction should be done 00635 if ($p_entry['status'] == 'ok') { 00636 // Do the extraction (if not a folder) 00637 if (!(($p_entry['external']&0x00000010)==0x00000010)) { 00638 // Look for not compressed file 00639 if ($p_entry['compressed_size'] == $p_entry['size']) { 00640 // Opening destination file 00641 if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { 00642 // Change the file status 00643 $p_entry['status'] = "write_error"; 00644 00645 // Return 00646 return $v_result; 00647 } 00648 00649 00650 // Read the file by ARCHIVE_ZIP_READ_BLOCK_SIZE octets blocks 00651 $v_size = $p_entry['compressed_size']; 00652 while ($v_size != 0) { 00653 $v_read_size = ($v_size < ARCHIVE_ZIP_READ_BLOCK_SIZE ? $v_size : ARCHIVE_ZIP_READ_BLOCK_SIZE); 00654 $v_buffer = fread($this->_zip_fd, $v_read_size); 00655 $v_binary_data = pack('a'.$v_read_size, $v_buffer); 00656 @fwrite($v_dest_file, $v_binary_data, $v_read_size); 00657 $v_size -= $v_read_size; 00658 } 00659 00660 // Closing the destination file 00661 fclose($v_dest_file); 00662 00663 // Change the file mtime 00664 touch($p_entry['filename'], $p_entry['mtime']); 00665 } else { 00666 // Trace 00667 00668 // Opening destination file 00669 if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { 00670 00671 // Change the file status 00672 $p_entry['status'] = "write_error"; 00673 00674 return $v_result; 00675 } 00676 00677 00678 // Read the compressed file in a buffer (one shot) 00679 $v_buffer = @fread($this->_zip_fd, $p_entry['compressed_size']); 00680 00681 // Decompress the file 00682 $v_file_content = gzinflate($v_buffer); 00683 unset($v_buffer); 00684 00685 // Write the uncompressed data 00686 @fwrite($v_dest_file, $v_file_content, $p_entry['size']); 00687 unset($v_file_content); 00688 00689 // Closing the destination file 00690 @fclose($v_dest_file); 00691 00692 // Change the file mtime 00693 @touch($p_entry['filename'], $p_entry['mtime']); 00694 } 00695 00696 // Look for chmod option 00697 if ( (isset($p_params[ARCHIVE_ZIP_PARAM_SET_CHMOD])) 00698 && ($p_params[ARCHIVE_ZIP_PARAM_SET_CHMOD] != 0)) { 00699 00700 // Change the mode of the file 00701 chmod($p_entry['filename'], $p_params[ARCHIVE_ZIP_PARAM_SET_CHMOD]); 00702 } 00703 00704 } 00705 } 00706 00707 // Look for post-extract callback 00708 if ( (isset($p_params[ARCHIVE_ZIP_PARAM_POST_EXTRACT])) 00709 && ($p_params[ARCHIVE_ZIP_PARAM_POST_EXTRACT] != '')) { 00710 00711 // Generate a local information 00712 $v_local_header = array(); 00713 $this->_convertHeader2FileInfo($p_entry, $v_local_header); 00714 00715 // Call the callback 00716 // Here I do not use call_user_func() because I need to send a reference to the 00717 // header. 00718 eval('$v_result = '.$p_params[ARCHIVE_ZIP_PARAM_POST_EXTRACT].'(ARCHIVE_ZIP_PARAM_POST_EXTRACT, $v_local_header);'); 00719 } 00720 00721 // Return 00722 return $v_result; 00723 } 00724 00731 function _readFileHeader(&$p_header) { 00732 $v_result=1; 00733 00734 // Read the 4 bytes signature 00735 $v_binary_data = @fread($this->_zip_fd, 4); 00736 $v_data = unpack('Vid', $v_binary_data); 00737 00738 // Check signature 00739 if ($v_data['id'] != 0x04034b50) { 00740 00741 // Error log 00742 $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); 00743 00744 // Return 00745 return em_unzip::errorCode(); 00746 } 00747 00748 // Read the first 42 bytes of the header 00749 $v_binary_data = fread($this->_zip_fd, 26); 00750 00751 // Look for invalid block size 00752 if (strlen($v_binary_data) != 26) { 00753 $p_header['filename'] = ""; 00754 $p_header['status'] = "invalid_header"; 00755 00756 // Error log 00757 $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); 00758 00759 // Return 00760 return em_unzip::errorCode(); 00761 } 00762 00763 // Extract the values 00764 $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data); 00765 00766 // Get filename 00767 $p_header['filename'] = fread($this->_zip_fd, $v_data['filename_len']); 00768 00769 // Get extra_fields 00770 if ($v_data['extra_len'] != 0) { 00771 $p_header['extra'] = fread($this->_zip_fd, $v_data['extra_len']); 00772 } 00773 else { 00774 $p_header['extra'] = ''; 00775 } 00776 00777 // Extract properties 00778 $p_header['compression'] = $v_data['compression']; 00779 $p_header['size'] = $v_data['size']; 00780 $p_header['compressed_size'] = $v_data['compressed_size']; 00781 $p_header['crc'] = $v_data['crc']; 00782 $p_header['flag'] = $v_data['flag']; 00783 00784 // Recuperate date in UNIX format 00785 $p_header['mdate'] = $v_data['mdate']; 00786 $p_header['mtime'] = $v_data['mtime']; 00787 if ($p_header['mdate'] && $p_header['mtime']) { 00788 // Extract time 00789 $v_hour = ($p_header['mtime'] & 0xF800) >> 11; 00790 $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; 00791 $v_seconde = ($p_header['mtime'] & 0x001F)*2; 00792 00793 // Extract date 00794 $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; 00795 $v_month = ($p_header['mdate'] & 0x01E0) >> 5; 00796 $v_day = $p_header['mdate'] & 0x001F; 00797 00798 // Get UNIX date format 00799 $p_header['mtime'] = mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); 00800 00801 } else { 00802 $p_header['mtime'] = time(); 00803 } 00804 00805 // Other informations 00806 00807 // TBC 00808 //for(reset($v_data); $key = key($v_data); next($v_data)) { 00809 //} 00810 00811 // Set the stored filename 00812 $p_header['stored_filename'] = $p_header['filename']; 00813 00814 // Set the status field 00815 $p_header['status'] = "ok"; 00816 00817 // Return 00818 return $v_result; 00819 } 00820 00827 function _readCentralFileHeader(&$p_header) { 00828 $v_result=1; 00829 00830 // Read the 4 bytes signature 00831 $v_binary_data = @fread($this->_zip_fd, 4); 00832 $v_data = unpack('Vid', $v_binary_data); 00833 00834 // Check signature 00835 if ($v_data['id'] != 0x02014b50) { 00836 00837 // Error log 00838 $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); 00839 00840 // Return 00841 return em_unzip::errorCode(); 00842 } 00843 00844 // Read the first 42 bytes of the header 00845 $v_binary_data = fread($this->_zip_fd, 42); 00846 00847 // Look for invalid block size 00848 if (strlen($v_binary_data) != 42) { 00849 $p_header['filename'] = ""; 00850 $p_header['status'] = "invalid_header"; 00851 00852 // Error log 00853 $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); 00854 00855 // Return 00856 return em_unzip::errorCode(); 00857 } 00858 00859 // Extract the values 00860 $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data); 00861 00862 // Get filename 00863 if ($p_header['filename_len'] != 0) 00864 $p_header['filename'] = fread($this->_zip_fd, $p_header['filename_len']); 00865 else 00866 $p_header['filename'] = ''; 00867 00868 // Get extra 00869 if ($p_header['extra_len'] != 0) 00870 $p_header['extra'] = fread($this->_zip_fd, $p_header['extra_len']); 00871 else 00872 $p_header['extra'] = ''; 00873 00874 // Get comment 00875 if ($p_header['comment_len'] != 0) 00876 $p_header['comment'] = fread($this->_zip_fd, $p_header['comment_len']); 00877 else 00878 $p_header['comment'] = ''; 00879 00880 // Extract properties 00881 00882 // Recuperate date in UNIX format 00883 if ($p_header['mdate'] && $p_header['mtime']) { 00884 // Extract time 00885 $v_hour = ($p_header['mtime'] & 0xF800) >> 11; 00886 $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; 00887 $v_seconde = ($p_header['mtime'] & 0x001F)*2; 00888 00889 // Extract date 00890 $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; 00891 $v_month = ($p_header['mdate'] & 0x01E0) >> 5; 00892 $v_day = $p_header['mdate'] & 0x001F; 00893 00894 // Get UNIX date format 00895 $p_header['mtime'] = mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); 00896 00897 } else { 00898 $p_header['mtime'] = time(); 00899 } 00900 00901 // Set the stored filename 00902 $p_header['stored_filename'] = $p_header['filename']; 00903 00904 // Set default status to ok 00905 $p_header['status'] = 'ok'; 00906 00907 // Look if it is a directory 00908 if (substr($p_header['filename'], -1) == '/') { 00909 $p_header['external'] = 0x41FF0010; 00910 } 00911 00912 00913 // Return 00914 return $v_result; 00915 } 00916 00923 function _readEndCentralDir(&$p_central_dir) { 00924 $v_result=1; 00925 00926 // Go to the end of the zip file 00927 $v_size = filesize($this->_zipname); 00928 @fseek($this->_zip_fd, $v_size); 00929 if (@ftell($this->_zip_fd) != $v_size) { 00930 $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, 00931 'Unable to go to the end of the archive \'' 00932 .$this->_zipname.'\''); 00933 return em_unzip::errorCode(); 00934 } 00935 00936 // First try : look if this is an archive with no commentaries 00937 // (most of the time) 00938 // in this case the end of central dir is at 22 bytes of the file end 00939 $v_found = 0; 00940 if ($v_size > 26) { 00941 @fseek($this->_zip_fd, $v_size-22); 00942 if (($v_pos = @ftell($this->_zip_fd)) != ($v_size-22)) { 00943 $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, 00944 'Unable to seek back to the middle of the archive \'' 00945 .$this->_zipname.'\''); 00946 return em_unzip::errorCode(); 00947 } 00948 00949 // Read for bytes 00950 $v_binary_data = @fread($this->_zip_fd, 4); 00951 $v_data = unpack('Vid', $v_binary_data); 00952 00953 // Check signature 00954 if ($v_data['id'] == 0x06054b50) { 00955 $v_found = 1; 00956 } 00957 00958 $v_pos = ftell($this->_zip_fd); 00959 } 00960 00961 // Go back to the maximum possible size of the Central Dir End Record 00962 if (!$v_found) { 00963 $v_maximum_size = 65557; // 0xFFFF + 22; 00964 if ($v_maximum_size > $v_size) 00965 $v_maximum_size = $v_size; 00966 @fseek($this->_zip_fd, $v_size-$v_maximum_size); 00967 if (@ftell($this->_zip_fd) != ($v_size-$v_maximum_size)) { 00968 $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, 00969 'Unable to seek back to the middle of the archive \'' 00970 .$this->_zipname.'\''); 00971 return em_unzip::errorCode(); 00972 } 00973 00974 // Read byte per byte in order to find the signature 00975 $v_pos = ftell($this->_zip_fd); 00976 $v_bytes = 0x00000000; 00977 while ($v_pos < $v_size) { 00978 // Read a byte 00979 $v_byte = @fread($this->_zip_fd, 1); 00980 00981 // Add the byte 00982 $v_bytes = ($v_bytes << 8) | Ord($v_byte); 00983 00984 // Compare the bytes 00985 if ($v_bytes == 0x504b0506) { 00986 $v_pos++; 00987 break; 00988 } 00989 00990 $v_pos++; 00991 } 00992 00993 // Look if not found end of central dir 00994 if ($v_pos == $v_size) { 00995 $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, 00996 "Unable to find End of Central Dir Record signature"); 00997 return em_unzip::errorCode(); 00998 } 00999 } 01000 01001 // Read the first 18 bytes of the header 01002 $v_binary_data = fread($this->_zip_fd, 18); 01003 01004 // Look for invalid block size 01005 if (strlen($v_binary_data) != 18) { 01006 $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, 01007 "Invalid End of Central Dir Record size : " 01008 .strlen($v_binary_data)); 01009 return em_unzip::errorCode(); 01010 } 01011 01012 // Extract the values 01013 $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data); 01014 01015 // Check the global size 01016 if (($v_pos + $v_data['comment_size'] + 18) != $v_size) { 01017 $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, 01018 "Fail to find the right signature"); 01019 return em_unzip::errorCode(); 01020 } 01021 01022 // Get comment 01023 if ($v_data['comment_size'] != 0) 01024 $p_central_dir['comment'] = fread($this->_zip_fd, $v_data['comment_size']); 01025 else 01026 $p_central_dir['comment'] = ''; 01027 01028 $p_central_dir['entries'] = $v_data['entries']; 01029 $p_central_dir['disk_entries'] = $v_data['disk_entries']; 01030 $p_central_dir['offset'] = $v_data['offset']; 01031 $p_central_dir['size'] = $v_data['size']; 01032 $p_central_dir['disk'] = $v_data['disk']; 01033 $p_central_dir['disk_start'] = $v_data['disk_start']; 01034 01035 // Return 01036 return $v_result; 01037 } 01038 01046 function _dirCheck($p_dir, $p_is_dir=false) { 01047 $v_result = 1; 01048 01049 // Remove the final '/' 01050 if (($p_is_dir) && (substr($p_dir, -1)=='/')) { 01051 $p_dir = substr($p_dir, 0, strlen($p_dir)-1); 01052 } 01053 01054 // Check the directory availability 01055 if ((is_dir($p_dir)) || ($p_dir == "")) { 01056 return 1; 01057 } 01058 01059 // Extract parent directory 01060 $p_parent_dir = dirname($p_dir); 01061 01062 // Just a check 01063 if ($p_parent_dir != $p_dir) { 01064 // Look for parent directory 01065 if ($p_parent_dir != "") { 01066 if (($v_result = $this->_dirCheck($p_parent_dir)) != 1) { 01067 return $v_result; 01068 } 01069 } 01070 } 01071 01072 // Create the directory 01073 if (!@mkdir($p_dir, 0777)) { 01074 $this->_errorLog(ARCHIVE_ZIP_ERR_DIR_CREATE_FAIL, 01075 "Unable to create directory '$p_dir'"); 01076 return em_unzip::errorCode(); 01077 } 01078 01079 // Return 01080 return $v_result; 01081 } 01082 01091 function _check_parameters(&$p_params, $p_default) { 01092 01093 // Check that param is an array 01094 if (!is_array($p_params)) { 01095 $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAMETER, 01096 'Unsupported parameter, waiting for an array'); 01097 return em_unzip::errorCode(); 01098 } 01099 01100 // Check that all the params are valid 01101 for (reset($p_params); list($v_key, $v_value) = each($p_params); ) { 01102 if (!isset($p_default[$v_key])) { 01103 $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAMETER, 01104 'Unsupported parameter with key \''.$v_key.'\''); 01105 01106 return em_unzip::errorCode(); 01107 } 01108 } 01109 01110 // Set the default values 01111 for (reset($p_default); list($v_key, $v_value) = each($p_default); ) { 01112 if (!isset($p_params[$v_key])) { 01113 $p_params[$v_key] = $p_default[$v_key]; 01114 } 01115 } 01116 01117 // Check specific parameters 01118 $v_callback_list = array ('callback_pre_add','callback_post_add', 01119 'callback_pre_extract','callback_post_extract'); 01120 for ($i=0; $i<sizeof($v_callback_list); $i++) { 01121 $v_key=$v_callback_list[$i]; 01122 if ( (isset($p_params[$v_key])) && ($p_params[$v_key] != '')) { 01123 if (!function_exists($p_params[$v_key])) { 01124 $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAM_VALUE, 01125 "Callback '".$p_params[$v_key] 01126 ."()' is not an existing function for " 01127 ."parameter '".$v_key."'"); 01128 return em_unzip::errorCode(); 01129 } 01130 } 01131 } 01132 01133 return(1); 01134 } 01135 01144 function _errorLog($p_error_code=0, $p_error_string='') { 01145 $this->_error_code = $p_error_code; 01146 $this->_error_string = $p_error_string; 01147 } 01148 01155 function _errorReset() { 01156 $this->_error_code = 1; 01157 $this->_error_string = ''; 01158 } 01159 01166 function _tool_PathReduction($p_dir) { 01167 $v_result = ""; 01168 01169 // Look for not empty path 01170 if ($p_dir != "") { 01171 // Explode path by directory names 01172 $v_list = explode("/", $p_dir); 01173 01174 // Study directories from last to first 01175 for ($i=sizeof($v_list)-1; $i>=0; $i--) { 01176 // Look for current path 01177 if ($v_list[$i] == ".") { 01178 // Ignore this directory 01179 // Should be the first $i=0, but no check is done 01180 } else if ($v_list[$i] == "..") { 01181 // Ignore it and ignore the $i-1 01182 $i--; 01183 } else if (($v_list[$i] == "") && ($i!=(sizeof($v_list)-1)) && ($i!=0)) { 01184 // Ignore only the double '//' in path, 01185 // but not the first and last '/' 01186 } else { 01187 $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:""); 01188 } 01189 } 01190 } 01191 01192 // Return 01193 return $v_result; 01194 } 01195 01202 function _tool_PathInclusion($p_dir, $p_path) { 01203 $v_result = 1; 01204 01205 // Explode dir and path by directory separator 01206 $v_list_dir = explode("/", $p_dir); 01207 $v_list_dir_size = sizeof($v_list_dir); 01208 $v_list_path = explode("/", $p_path); 01209 $v_list_path_size = sizeof($v_list_path); 01210 01211 // Study directories paths 01212 $i = 0; 01213 $j = 0; 01214 while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) { 01215 // Look for empty dir (path reduction) 01216 if ($v_list_dir[$i] == '') { 01217 $i++; 01218 continue; 01219 } 01220 if ($v_list_path[$j] == '') { 01221 $j++; 01222 continue; 01223 } 01224 01225 // Compare the items 01226 if ( ($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != '')) { 01227 $v_result = 0; 01228 } 01229 01230 // Next items 01231 $i++; 01232 $j++; 01233 } 01234 01235 // Look if everything seems to be the same 01236 if ($v_result) { 01237 // Skip all the empty items 01238 while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++; 01239 while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++; 01240 01241 if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) { 01242 // There are exactly the same 01243 $v_result = 2; 01244 } else if ($i < $v_list_dir_size) { 01245 // The path is shorter than the dir 01246 $v_result = 0; 01247 } 01248 } 01249 01250 // Return 01251 return $v_result; 01252 } 01253 01261 function _tool_CopyBlock($p_src, $p_dest, $p_size, $p_mode=0) { 01262 $v_result = 1; 01263 01264 if ($p_mode==0) { 01265 while ($p_size != 0) { 01266 $v_read_size = ($p_size < ARCHIVE_ZIP_READ_BLOCK_SIZE 01267 ? $p_size : ARCHIVE_ZIP_READ_BLOCK_SIZE); 01268 $v_buffer = @fread($p_src, $v_read_size); 01269 @fwrite($p_dest, $v_buffer, $v_read_size); 01270 $p_size -= $v_read_size; 01271 } 01272 } else if ($p_mode==1) { 01273 while ($p_size != 0) { 01274 $v_read_size = ($p_size < ARCHIVE_ZIP_READ_BLOCK_SIZE 01275 ? $p_size : ARCHIVE_ZIP_READ_BLOCK_SIZE); 01276 $v_buffer = @gzread($p_src, $v_read_size); 01277 @fwrite($p_dest, $v_buffer, $v_read_size); 01278 $p_size -= $v_read_size; 01279 } 01280 } else if ($p_mode==2) { 01281 while ($p_size != 0) { 01282 $v_read_size = ($p_size < ARCHIVE_ZIP_READ_BLOCK_SIZE 01283 ? $p_size : ARCHIVE_ZIP_READ_BLOCK_SIZE); 01284 $v_buffer = @fread($p_src, $v_read_size); 01285 @gzwrite($p_dest, $v_buffer, $v_read_size); 01286 $p_size -= $v_read_size; 01287 } 01288 } 01289 else if ($p_mode==3) { 01290 while ($p_size != 0) { 01291 $v_read_size = ($p_size < ARCHIVE_ZIP_READ_BLOCK_SIZE 01292 ? $p_size : ARCHIVE_ZIP_READ_BLOCK_SIZE); 01293 $v_buffer = @gzread($p_src, $v_read_size); 01294 @gzwrite($p_dest, $v_buffer, $v_read_size); 01295 $p_size -= $v_read_size; 01296 } 01297 } 01298 01299 // Return 01300 return $v_result; 01301 } 01302 01309 function _tool_Rename($p_src, $p_dest) { 01310 $v_result = 1; 01311 01312 // Try to rename the files 01313 if (!@rename($p_src, $p_dest)) { 01314 01315 // Try to copy & unlink the src 01316 if (!@copy($p_src, $p_dest)) { 01317 $v_result = 0; 01318 } else if (!@unlink($p_src)) { 01319 $v_result = 0; 01320 } 01321 } 01322 01323 // Return 01324 return $v_result; 01325 } 01326 01334 function _tool_TranslateWinPath($p_path, $p_remove_disk_letter=true) { 01335 if (stristr(php_uname(), 'windows')) { 01336 // Look for potential disk letter 01337 if ( ($p_remove_disk_letter) 01338 && (($v_position = strpos($p_path, ':')) != false)) { 01339 $p_path = substr($p_path, $v_position+1); 01340 } 01341 // Change potential windows directory separator 01342 if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { 01343 $p_path = strtr($p_path, '\\', '/'); 01344 } 01345 } 01346 return $p_path; 01347 } 01348 01349 } 01350 01351 ?>