0

I'm creating a class to stream a ZIP file from PHP.

The output is absolutely fine when opening in WinRAR, Windows compressed folder, BetterZip etc but when opening in the OSX Unarchiver all I get out is a .cpgz file and nothing else.

I've read the ZIP specification over and over again and cross-checked against my headers and everything looks fine. I did find another question on here which seemed to have the same issue but the solution was to change the "version needed to extract" field which I did to 0x0014 and I get an "operation not permitted" error when trying to unzip.

I've uploaded a sample zip file with a folder and a small image inside to here:

http://www.freefilehosting.net/test_97

Here is how the code works:

// File entry header
echo pack('V', 0x04034b50);
echo pack('v', 0x0014); // Version needed to extract
echo pack('v', $dir ? 0 : 0x0808); // General purpose bit flag for unknown CRC
echo pack('v', 0x00); // Compression method (store)
echo pack('V', $dts); // DOS timestamp
echo pack('V', 0x00); // CRC - empty
echo pack('V', 0x00); // Compressed data length
echo pack('V', 0x00); // Decompressed data length
echo pack('v', strlen($filename)); // Length of filename
echo pack('v', 0x00); // Length of additional data

<snip>Output raw file data</snip>

// File entry trailer
echo pack('V', 0x08074b50); // Magic
echo pack('V', $crc); // CRC generated using crc32b algo
echo pack('V', $data_size); // Compressed size
echo pack('V', $total_size); // Uncompressed size

// CDR record
echo pack('V', 0x02014b50); // Magic
echo pack('v', 0x0014); // Version created by
echo pack('v', 0x0014); // Version req'd to open
echo pack('v', 0x00); // General purpose bit
echo pack('v', 0x00); // Compression method (store)
echo pack('V', $dts); // DOS timestamp
echo pack('V', $crc); // CRC of data
echo pack('V', $data_size); // Compressed size
echo pack('V', $total_size); // Uncompressed size
echo pack('v', strlen($filename)); // Filename length
echo pack('v', 0x00); // Extra data length
echo pack('v', 0x00); // Comment length
echo pack('v', 0x00); // Start disk no.
echo pack('v', 0x00); // Internal file attributes
echo pack('V', $dir ? 16 : 32); // External file attributes
echo pack('V', $offset); // Relative offset

// End of file record
echo pack('V', 0x06054b50); // Magic
echo pack('v', 0x00); // Current disk number
echo pack('v', 0x00); // Disk containing CDR
echo pack('v', $count); // Number of entries in this CDR
echo pack('v', $count); // Total number of CDR entries
echo pack('V', $cdr_len); // Length of CDR
echo pack('V', $cdr_offset); // Offset of CDR
echo pack('v', 0x00); // Zip file comment length
JWood
  • 2,804
  • 2
  • 39
  • 64
  • Show some of your code. Also, what is the OS X Unzipper outputting? – Lukas Knuth Jun 28 '11 at 14:27
  • As I mentioned, the OSX unzipper outputs a .cpgz file or an error depending on the "version needed to extract" field. The code is very long, which particular parts would you like to see? – JWood Jun 28 '11 at 14:29
  • Are you calling `ZipStream->finish()`? – mikerobi Jun 28 '11 at 14:31
  • I'm not using ZipStream, I'm using good old PHP pack() to create the Zip file myself. I created the class from studying the ZIP specification. – JWood Jun 28 '11 at 14:32

2 Answers2

1

To answer my own question, the problem is that OSX does not like 2 things. 1) Appending the additional metadata (size, CRC etc) to the end of directory records as this is not strictly necessary (but within the spec). And 2) it does not support storage method 0x00 (store), files must be at the very least DEFLATED to be opened by the OSX unarchiver.

JWood
  • 2,804
  • 2
  • 39
  • 64
0

Interestingly, this isn't a server/code issue problem..

https://discussions.apple.com/thread/1446784?start=0&tstart=0

http://www.google.co.uk/#sclient=psy&hl=en&safe=off&biw=1436&bih=784&source=hp&q=cpgz&aq=f&aqi=g3g-s1g1&aql=&oq=&pbx=1&bav=on.2,or.r_gc.r_pw.&fp=c131ccfc5fe21e40

Loads of people are having the issue.

OSX wouldn't unzip it for me, citing error: operation not permitted. I had to get it to unzip manually using Terminal.

When you unzip using terminal, can you please show us the output?

unzip test.zip -d test

Shamil-Nunhucks-MacBook-Air:Downloads shamil$ unzip test.zip -d test Archive: test.zip creating: test/Test/ creating: test/Test/New folder/ extracting: test/Test/onebit_42.png

//Thread should not be here - move to superuser?

=============================

You need to use:

ob_start('ob_gzhandler')
bear
  • 11,364
  • 26
  • 77
  • 129
  • I saw those links and I'm certain there must be a way round it. The OSX unarchiver is obviously a bit more strict on it's interpretation of the directory records but I'm sure it's capable of handling a streamed ZIP file. – JWood Jun 28 '11 at 14:33
  • I've just updated my answer. Can you post the output of the command I've given you? – bear Jun 28 '11 at 14:35
  • Archive: Test.zip creating: test/Test/ creating: test/Test/New folder/ extracting: test/Test/onebit_42.png – JWood Jun 28 '11 at 14:36
  • Hmm, the ZIP file has the complete signature. – bear Jun 28 '11 at 14:37
  • Just to clarify. The "version needed to extract" field in that Zip file is set to 0x0014 which causes the "operation not permitted" error. When setting the field to (3 << 8) + 3 it gives outputs a .cpgx file. – JWood Jun 28 '11 at 14:39
  • Please read this: http://stackoverflow.com/questions/1436239/creating-and-serving-zipped-files-with-php – bear Jun 28 '11 at 14:42
  • Thanks but that question is unrelated to this issue. I am not using the zip command and ob_gzhandler will not have any effect on the final output in this instance. – JWood Jun 28 '11 at 14:54
  • In that case, can you please post code, as others have requested? – bear Jun 28 '11 at 15:59
  • Sure, which code would you like to see? The class is very large as I mentioned and probably wouldn't fit in the post. Which areas in particular are of interest? – JWood Jun 28 '11 at 16:02
  • I've updated the question to show the flow of output in code. – JWood Jun 28 '11 at 16:17