BioTorrents.de’s version of Gazelle
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

zip.class.php 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <?php
  2. #declare(strict_types=1);
  3. /*************************************************************************|
  4. |--------------- Zip class -----------------------------------------------|
  5. |*************************************************************************|
  6. This class provides a convenient way for us to generate and serve zip
  7. archives to our end users, both from physical files, cached
  8. or already parsed data (torrent files). It's all done on the fly, due to
  9. the high probability that a filesystem stored archive will never be
  10. downloaded twice.
  11. Utilizes gzcompress, based upon RFC 1950
  12. //------------- How it works --------------//
  13. Basic concept is construct archive, add files, and serve on the fly.
  14. //------------- How to use it --------------//
  15. * First, construct the archive:
  16. $Zip = new Zip('FileName');
  17. Adds the headers so that add_file can stream and we don't need to create a massive buffer.
  18. open_stream(); was integrated into the constructor to conform with Object-Oriented Standards.
  19. $Zip->unlimit();
  20. A simple shortcut function for raising the basic PHP limits, time and memory for larger archives.
  21. -----
  22. * Then, add files and begin streaming to the user to avoid memory buffering:
  23. $Zip->add_file(file_get_contents("data/file.txt"), "File.txt");
  24. Adds the contents of data/file.txt into File.txt in the archive root.
  25. $Zip->add_file($TorrentData, "Bookmarks/Artist - Album [2008].torrent");
  26. Adds the parsed torrent to the archive in the Bookmarks folder (created simply by placing it in the path).
  27. -----
  28. * Then, close the archive to the user:
  29. $Zip->close_stream();
  30. This collects everything put together thus far in the archive, and streams it to the user in the form of Test7.zip
  31. //------ Explanation of basic functions ------//
  32. add_file(Contents, Internal Path)
  33. Adds the contents to the archive, where it will be extracted to Internal Path.
  34. close_stream();
  35. Collect and stream to the user.
  36. //------------- Detailed example -------------//
  37. require('classes/zip.class.php');
  38. $Zip = new Zip('FileName');
  39. $Name = 'Ubuntu-8.10';
  40. $Zip->add_file($TorrentData, 'Torrents/'.Misc::file_string($Name).'.torrent');
  41. $Zip->add_file(file_get_contents('zip.php'), 'zip.php');
  42. $Zip->close_stream();
  43. //---------- Development reference -----------//
  44. http://www.pkware.com/documents/casestudies/APPNOTE.TXT - ZIP spec (this class)
  45. http://www.ietf.org/rfc/rfc1950.txt - ZLIB compression spec (gzcompress function)
  46. http://www.fileformat.info/tool/hexdump.htm - Useful for analyzing ZIP files
  47. |*************************************************************************/
  48. if (!extension_loaded('zlib')) {
  49. error('Zlib Extension not loaded.');
  50. }
  51. class Zip
  52. {
  53. public $ArchiveSize = 0; // Total size
  54. public $ArchiveFiles = 0; // Total files
  55. private $Structure = ''; // Structure saved to memory
  56. private $FileOffset = 0; // Offset to write data
  57. private $Data = ''; //An idea
  58. public function __construct($ArchiveName = 'Archive')
  59. {
  60. header("Content-type: application/octet-stream"); // Stream download
  61. header("Content-disposition: attachment; filename=\"$ArchiveName.zip\""); // Name the archive - Should not be urlencoded
  62. }
  63. public static function unlimit()
  64. {
  65. ob_end_clean();
  66. set_time_limit(3600); // Limit 1 hour
  67. ini_set('memory_limit', '1024M'); // Because the buffers can get extremely large
  68. }
  69. public function add_file($FileData, $ArchivePath, $TimeStamp = 0)
  70. {
  71. /* File header */
  72. $this->Data = "\x50\x4b\x03\x04"; // PK signature
  73. $this->Data .= "\x14\x00"; // Version requirements
  74. $this->Data .= "\x00\x08"; // Bit flag - 0x8 = UTF-8 file names
  75. $this->Data .= "\x08\x00"; // Compression
  76. $this->Data .= "\x00\x00\x00\x00";
  77. $DataLength = strlen($FileData); // Saved as variable to avoid wasting CPU calculating it multiple times
  78. $CRC32 = crc32($FileData); // Ditto
  79. $ZipData = gzcompress($FileData); // Ditto
  80. $ZipData = substr($ZipData, 2, (strlen($ZipData) - 6)); // Checksum resolution
  81. $ZipLength = strlen($ZipData); // Ditto
  82. $this->Data .= pack('V', $CRC32); // CRC-32
  83. $this->Data .= pack('V', $ZipLength); // Compressed file size
  84. $this->Data .= pack('V', $DataLength); // Uncompressed file size
  85. $this->Data .= pack('v', strlen($ArchivePath)); // Path name length
  86. $this->Data .="\x00\x00"; // Extra field length (0'd so we can ignore this)
  87. $this->Data .= $ArchivePath; // File name & Extra Field (length set to 0 so ignored)
  88. /* END file header */
  89. /* File data */
  90. $this->Data .= $ZipData; // File data
  91. /* END file data */
  92. /* Data descriptor
  93. Not needed (only needed when 3rd bitflag is set), causes problems with OS X archive utility
  94. $this->Data .= pack('V', $CRC32); // CRC-32
  95. $this->Data .= pack('V', $ZipLength); // Compressed file size
  96. $this->Data .= pack('V', $DataLength); // Uncompressed file size
  97. END data descriptor */
  98. $FileDataLength = strlen($this->Data);
  99. $this->ArchiveSize = $this->ArchiveSize + $FileDataLength; // All we really need is the size
  100. $CurrentOffset = $this->ArchiveSize; // Update offsets
  101. echo $this->Data; // Get this out to reduce our memory consumption
  102. /* Central Directory Structure */
  103. $CDS = "\x50\x4b\x01\x02"; // CDS signature
  104. $CDS .="\x14\x00"; // Constructor version
  105. $CDS .="\x14\x00"; // Version requirements
  106. $CDS .="\x00\x08"; // Bit flag - 0x8 = UTF-8 file names
  107. $CDS .="\x08\x00"; // Compression
  108. $CDS .="\x00\x00\x00\x00"; // Last modified
  109. $CDS .= pack('V', $CRC32); // CRC-32
  110. $CDS .= pack('V', $ZipLength); // Compressed file size
  111. $CDS .= pack('V', $DataLength); // Uncompressed file size
  112. $CDS .= pack('v', strlen($ArchivePath)); // Path name length
  113. $CDS .="\x00\x00"; // Extra field length (0'd so we can ignore this)
  114. $CDS .="\x00\x00"; // File comment length (no comment, 0'd)
  115. $CDS .="\x00\x00"; // Disk number start (0 seems valid)
  116. $CDS .="\x00\x00"; // Internal file attributes (again with the 0's)
  117. $CDS .="\x20\x00\x00\x00"; // External file attributes
  118. $CDS .= pack('V', $this->FileOffset); // Offsets
  119. $CDS .= $ArchivePath; // File name & Extra Field (length set to 0 so ignored)
  120. /* END central Directory Structure */
  121. $this->FileOffset = $CurrentOffset; // Update offsets
  122. $this->Structure .= $CDS; // Append to structure
  123. $this->ArchiveFiles++; // Increment file count
  124. }
  125. public function close_stream()
  126. {
  127. echo $this->Structure; // Structure Root
  128. echo "\x50\x4b\x05\x06"; // End of central directory signature
  129. echo "\x00\x00"; // This disk
  130. echo "\x00\x00"; // CDS start
  131. echo pack('v', $this->ArchiveFiles); // Handle the number of entries
  132. echo pack('v', $this->ArchiveFiles); // Ditto
  133. echo pack('V', strlen($this->Structure)); //Size
  134. echo pack('V', $this->ArchiveSize); // Offset
  135. echo "\x00\x00"; // No comment, close it off
  136. }
  137. }