Advertisement
Guest User

zip.class.php

a guest
Jul 21st, 2011
236
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 10.95 KB | None | 0 0
  1. <?php
  2. class zipfile
  3. {
  4.     /*
  5.         zipfile class, for reading or writing .zip files
  6.         See http://www.gamingg.net for more of my work
  7.         Based on tutorial given by John Coggeshall at http://www.zend.com/zend/spotlight/creating-zip-files3.php
  8.         Copyright (C) Joshua Townsend and licensed under the GPL
  9.         Version 1.0
  10.     */
  11.     var $datasec = array(); // array to store compressed data
  12.     var $files = array(); // array of uncompressed files
  13.     var $dirs = array(); // array of directories that have been created already
  14.     var $ctrl_dir = array(); // central directory
  15.     var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00"; //end of Central directory record
  16.     var $old_offset = 0;
  17.     var $basedir = ".";
  18.  
  19.     function create_dir($name) // Adds a directory to the zip with the name $name
  20.     {
  21.         $name = str_replace("\\", "/", $name);
  22.  
  23.         $fr = "\x50\x4b\x03\x04";
  24.         $fr .= "\x0a\x00";  // version needed to extract
  25.         $fr .= "\x00\x00";  // general purpose bit flag
  26.         $fr .= "\x00\x00";  // compression method
  27.         $fr .= "\x00\x00\x00\x00"; // last mod time and date
  28.  
  29.         $fr .= pack("V",0); // crc32
  30.         $fr .= pack("V",0); //compressed filesize
  31.         $fr .= pack("V",0); //uncompressed filesize
  32.         $fr .= pack("v",strlen($name)); //length of pathname
  33.         $fr .= pack("v", 0); //extra field length
  34.         $fr .= $name;
  35.         // end of "local file header" segment
  36.  
  37.         // no "file data" segment for path
  38.  
  39.         // "data descriptor" segment (optional but necessary if archive is not served as file)
  40.         $fr .= pack("V",0); //crc32
  41.         $fr .= pack("V",0); //compressed filesize
  42.         $fr .= pack("V",0); //uncompressed filesize
  43.  
  44.         // add this entry to array
  45.         $this->datasec[] = $fr;
  46.  
  47.         $new_offset = strlen(implode("", $this->datasec));
  48.  
  49.         // ext. file attributes mirrors MS-DOS directory attr byte, detailed
  50.         // at http://support.microsoft.com/support/kb/articles/Q125/0/19.asp
  51.  
  52.         // now add to central record
  53.         $cdrec = "\x50\x4b\x01\x02";
  54.         $cdrec .="\x00\x00";    // version made by
  55.         $cdrec .="\x0a\x00";    // version needed to extract
  56.         $cdrec .="\x00\x00";    // general purpose bit flag
  57.         $cdrec .="\x00\x00";    // compression method
  58.         $cdrec .="\x00\x00\x00\x00"; // last mod time and date
  59.         $cdrec .= pack("V",0); // crc32
  60.         $cdrec .= pack("V",0); //compressed filesize
  61.         $cdrec .= pack("V",0); //uncompressed filesize
  62.         $cdrec .= pack("v", strlen($name) ); //length of filename
  63.         $cdrec .= pack("v", 0 ); //extra field length
  64.         $cdrec .= pack("v", 0 ); //file comment length
  65.         $cdrec .= pack("v", 0 ); //disk number start
  66.         $cdrec .= pack("v", 0 ); //internal file attributes
  67.         $cdrec .= pack("V", 16 ); //external file attributes  - 'directory' bit set
  68.  
  69.         $cdrec .= pack("V", $this->old_offset); //relative offset of local header
  70.         $this->old_offset = $new_offset;
  71.  
  72.         $cdrec .= $name;
  73.         // optional extra field, file comment goes here
  74.         // save to array
  75.         $this->ctrl_dir[] = $cdrec;
  76.         $this->dirs[] = $name;
  77.     }
  78.  
  79.  
  80.     function create_file($data, $name) // Adds a file to the path specified by $name with the contents $data
  81.     {
  82.         $name = str_replace("\\", "/", $name);
  83.  
  84.         $fr = "\x50\x4b\x03\x04";
  85.         $fr .= "\x14\x00";  // version needed to extract
  86.         $fr .= "\x00\x00";  // general purpose bit flag
  87.         $fr .= "\x08\x00";  // compression method
  88.         $fr .= "\x00\x00\x00\x00"; // last mod time and date
  89.  
  90.         $unc_len = strlen($data);
  91.         $crc = crc32($data);
  92.         $zdata = gzcompress($data);
  93.         $zdata = substr($zdata, 2, -4); // fix crc bug
  94.         $c_len = strlen($zdata);
  95.         $fr .= pack("V",$crc); // crc32
  96.         $fr .= pack("V",$c_len); //compressed filesize
  97.         $fr .= pack("V",$unc_len); //uncompressed filesize
  98.         $fr .= pack("v", strlen($name) ); //length of filename
  99.         $fr .= pack("v", 0 ); //extra field length
  100.         $fr .= $name;
  101.         // end of "local file header" segment
  102.  
  103.         // "file data" segment
  104.         $fr .= $zdata;
  105.  
  106.         // "data descriptor" segment (optional but necessary if archive is not served as file)
  107.         $fr .= pack("V",$crc); // crc32
  108.         $fr .= pack("V",$c_len); // compressed filesize
  109.         $fr .= pack("V",$unc_len); // uncompressed filesize
  110.  
  111.         // add this entry to array
  112.         $this->datasec[] = $fr;
  113.  
  114.         $new_offset = strlen(implode("", $this->datasec));
  115.  
  116.         // now add to central directory record
  117.         $cdrec = "\x50\x4b\x01\x02";
  118.         $cdrec .="\x00\x00";    // version made by
  119.         $cdrec .="\x14\x00";    // version needed to extract
  120.         $cdrec .="\x00\x00";    // general purpose bit flag
  121.         $cdrec .="\x08\x00";    // compression method
  122.         $cdrec .="\x00\x00\x00\x00"; // last mod time & date
  123.         $cdrec .= pack("V",$crc); // crc32
  124.         $cdrec .= pack("V",$c_len); //compressed filesize
  125.         $cdrec .= pack("V",$unc_len); //uncompressed filesize
  126.         $cdrec .= pack("v", strlen($name) ); //length of filename
  127.         $cdrec .= pack("v", 0 ); //extra field length
  128.         $cdrec .= pack("v", 0 ); //file comment length
  129.         $cdrec .= pack("v", 0 ); //disk number start
  130.         $cdrec .= pack("v", 0 ); //internal file attributes
  131.         $cdrec .= pack("V", 32 ); //external file attributes - 'archive' bit set
  132.  
  133.         $cdrec .= pack("V", $this->old_offset); //relative offset of local header
  134.         $this->old_offset = $new_offset;
  135.  
  136.         $cdrec .= $name;
  137.         // optional extra field, file comment goes here
  138.         // save to central directory
  139.         $this->ctrl_dir[] = $cdrec;
  140.     }
  141.  
  142.     function read_zip($name)
  143.     {
  144.         // Clear current file
  145.         $this->datasec = array();
  146.  
  147.         // File information
  148.         $this->name = $name;
  149.         $this->mtime = filemtime($name);
  150.         $this->size = filesize($name);
  151.  
  152.         // Read file
  153.         $fh = fopen($name, "rb");
  154.         $filedata = fread($fh, $this->size);
  155.         fclose($fh);
  156.  
  157.         // Break into sections
  158.         $filesecta = explode("\x50\x4b\x05\x06", $filedata);
  159.  
  160.         // ZIP Comment
  161.         $unpackeda = unpack('x16/v1length', $filesecta[1]);
  162.         $this->comment = substr($filesecta[1], 18, $unpackeda['length']);
  163.         $this->comment = str_replace(array("\r\n", "\r"), "\n", $this->comment); // CR + LF and CR -> LF
  164.  
  165.         // Cut entries from the central directory
  166.         $filesecta = explode("\x50\x4b\x01\x02", $filedata);
  167.         $filesecta = explode("\x50\x4b\x03\x04", $filesecta[0]);
  168.         array_shift($filesecta); // Removes empty entry/signature
  169.  
  170.         foreach($filesecta as $filedata)
  171.         {
  172.             // CRC:crc, FD:file date, FT: file time, CM: compression method, GPF: general purpose flag, VN: version needed, CS: compressed size, UCS: uncompressed size, FNL: filename length
  173.             $entrya = array();
  174.             $entrya['error'] = "";
  175.  
  176.             $unpackeda = unpack("v1version/v1general_purpose/v1compress_method/v1file_time/v1file_date/V1crc/V1size_compressed/V1size_uncompressed/v1filename_length", $filedata);
  177.  
  178.             // Check for encryption
  179.             $isencrypted = (($unpackeda['general_purpose'] & 0x0001) ? true : false);
  180.  
  181.             // Check for value block after compressed data
  182.             if($unpackeda['general_purpose'] & 0x0008)
  183.             {
  184.                 $unpackeda2 = unpack("V1crc/V1size_compressed/V1size_uncompressed", substr($filedata, -12));
  185.  
  186.                 $unpackeda['crc'] = $unpackeda2['crc'];
  187.                 $unpackeda['size_compressed'] = $unpackeda2['size_uncompressed'];
  188.                 $unpackeda['size_uncompressed'] = $unpackeda2['size_uncompressed'];
  189.  
  190.                 unset($unpackeda2);
  191.             }
  192.  
  193.             $entrya['name'] = substr($filedata, 26, $unpackeda['filename_length']);
  194.  
  195.             if(substr($entrya['name'], -1) == "/") // skip directories
  196.             {
  197.                 continue;
  198.             }
  199.  
  200.             $entrya['dir'] = dirname($entrya['name']);
  201.             $entrya['dir'] = ($entrya['dir'] == "." ? "" : $entrya['dir']);
  202.             $entrya['name'] = basename($entrya['name']);
  203.  
  204.  
  205.             $filedata = substr($filedata, 26 + $unpackeda['filename_length']);
  206.  
  207.             if(strlen($filedata) != $unpackeda['size_compressed'])
  208.             {
  209.                 $entrya['error'] = "Compressed size is not equal to the value given in header.";
  210.             }
  211.  
  212.             if($isencrypted)
  213.             {
  214.                 $entrya['error'] = "Encryption is not supported.";
  215.             }
  216.             else
  217.             {
  218.                 switch($unpackeda['compress_method'])
  219.                 {
  220.                     case 0: // Stored
  221.                         // Not compressed, continue
  222.                     break;
  223.                     case 8: // Deflated
  224.                         $filedata = gzinflate($filedata);
  225.                     break;
  226.                     case 12: // BZIP2
  227.                         if(!extension_loaded("bz2"))
  228.                         {
  229.                             @dl((strtolower(substr(PHP_OS, 0, 3)) == "win") ? "php_bz2.dll" : "bz2.so");
  230.                         }
  231.  
  232.                         if(extension_loaded("bz2"))
  233.                         {
  234.                             $filedata = bzdecompress($filedata);
  235.                         }
  236.                         else
  237.                         {
  238.                             $entrya['error'] = "Required BZIP2 Extension not available.";
  239.                         }
  240.                     break;
  241.                     default:
  242.                         $entrya['error'] = "Compression method ({$unpackeda['compress_method']}) not supported.";
  243.                 }
  244.  
  245.                 if(!$entrya['error'])
  246.                 {
  247.                     if($filedata === false)
  248.                     {
  249.                         $entrya['error'] = "Decompression failed.";
  250.                     }
  251.                     elseif(strlen($filedata) != $unpackeda['size_uncompressed'])
  252.                     {
  253.                         $entrya['error'] = "File size is not equal to the value given in header.";
  254.                     }
  255.                     elseif(crc32($filedata) != $unpackeda['crc'])
  256.                     {
  257.                         $entrya['error'] = "CRC32 checksum is not equal to the value given in header.";
  258.                     }
  259.                 }
  260.  
  261.                 $entrya['filemtime'] = mktime(($unpackeda['file_time']  & 0xf800) >> 11,($unpackeda['file_time']  & 0x07e0) >>  5, ($unpackeda['file_time']  & 0x001f) <<  1, ($unpackeda['file_date']  & 0x01e0) >>  5, ($unpackeda['file_date']  & 0x001f), (($unpackeda['file_date'] & 0xfe00) >>  9) + 1980);
  262.                 $entrya['data'] = $filedata;
  263.             }
  264.  
  265.             $this->files[] = $entrya;
  266.         }
  267.  
  268.         return $this->files;
  269.     }
  270.  
  271.     function add_file($file, $dir = ".", $file_blacklist = array(), $ext_blacklist = array())
  272.     {
  273.         $file = str_replace("\\", "/", $file);
  274.         $dir = str_replace("\\", "/", $dir);
  275.  
  276.         if(strpos($file, "/") !== false)
  277.         {
  278.             $dira = explode("/", "{$dir}/{$file}");
  279.             $file = array_shift($dira);
  280.             $dir = implode("/", $dira);
  281.             unset($dira);
  282.         }
  283.  
  284.         while(substr($dir, 0, 2) == "./")
  285.         {
  286.             $dir = substr($dir, 2);
  287.         }
  288.         while(substr($file, 0, 2) == "./")
  289.         {
  290.             $file = substr($file, 2);
  291.         }
  292.         if(!in_array($dir, $this->dirs))
  293.         {
  294.             if($dir == ".")
  295.             {
  296.                 $this->create_dir("./");
  297.             }
  298.             $this->dirs[] = $dir;
  299.         }
  300.         if(in_array($file, $file_blacklist))
  301.         {
  302.             return true;
  303.         }
  304.         foreach($ext_blacklist as $ext)
  305.         {
  306.             if(substr($file, -1 - strlen($ext)) == ".{$ext}")
  307.             {
  308.                 return true;
  309.             }
  310.         }
  311.  
  312.         $filepath = (($dir && $dir != ".") ? "{$dir}/" : "").$file;
  313.         if(is_dir("{$this->basedir}/{$filepath}"))
  314.         {
  315.             $dh = opendir("{$this->basedir}/{$filepath}");
  316.             while(($subfile = readdir($dh)) !== false)
  317.             {
  318.                 if($subfile != "." && $subfile != "..")
  319.                 {
  320.                     $this->add_file($subfile, $filepath, $file_blacklist, $ext_blacklist);
  321.                 }
  322.             }
  323.             closedir($dh);
  324.         }
  325.         else
  326.         {
  327.             $this->create_file(implode("", file("{$this->basedir}/{$filepath}")), $filepath);
  328.         }
  329.  
  330.         return true;
  331.     }
  332.  
  333.  
  334.     function zipped_file() // return zipped file contents
  335.     {
  336.         $data = implode("", $this->datasec);
  337.         $ctrldir = implode("", $this->ctrl_dir);
  338.  
  339.         return $data.
  340.                 $ctrldir.
  341.                 $this->eof_ctrl_dir.
  342.                 pack("v", sizeof($this->ctrl_dir)). // total number of entries "on this disk"
  343.                 pack("v", sizeof($this->ctrl_dir)). // total number of entries overall
  344.                 pack("V", strlen($ctrldir)). // size of central dir
  345.                 pack("V", strlen($data)). // offset to start of central dir
  346.                 "\x00\x00"; // .zip file comment length
  347.     }
  348. }
  349. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement