Fixing Zip to properly extract empty folders.
authorTim Düsterhus <timwolla@arcor.de>
Tue, 15 May 2012 19:51:59 +0000 (21:51 +0200)
committerTim Düsterhus <timwolla@arcor.de>
Tue, 15 May 2012 19:52:54 +0000 (21:52 +0200)
Additionally adding getContentList to IArchive and implementing it for Zip.
Furthermore the API of Tar and Zip now also matches in the data-arrays.

Fixes WoltLab/WCF#591
Closes WoltLab/WCF#592

wcfsetup/install/files/lib/system/io/IArchive.class.php
wcfsetup/install/files/lib/system/io/Tar.class.php
wcfsetup/install/files/lib/system/io/Zip.class.php

index 259db55537af23ee70e5e21b8f71b1376cb8d343..10b2f4583c1c1f434349baf386162179d9c24b73 100644 (file)
@@ -12,6 +12,13 @@ namespace wcf\system\io;
  * @category   Community Framework
  */
 interface IArchive {
+       /** 
+        * Returns the table of contents (TOC) list for this tar archive.
+        * 
+        * @return      array           list of content
+        */
+       public function getContentList();
+       
        /**
         * Returns an associative array with information
         * about a specific file in the archive.
index 99a8fa0f6095467a740febdf9a9caef3b83fef0b..7210002e9cd96718d3d1f9f14408ff9225f0b45a 100644 (file)
@@ -218,9 +218,10 @@ class Tar implements IArchive {
                }
                $header = $this->getFileInfo($index);
                
-               // can not extract a folder
-               if ($header['type'] != 'file') {
-                       return false;
+               FileUtil::makePath(dirname($destination));
+               if ($header['type'] === 'folder') {
+                       FileUtil::makePath($destination);
+                       return;
                }
                
                // seek to offset
index 59b9a4810ce23c6967eaaf65bcd57233350011ec..112f5ebe3cf47c2bae1f9f782e06fc419f123dcd 100644 (file)
@@ -36,6 +36,16 @@ class Zip extends File implements IArchive {
                return false;
        }
        
+       /**
+        * @see wcf\system\io\IArchive::getContentList()
+        */
+       public function getContentList() {
+               $this->jumpToCentralDirectory();
+               $centralDirectory = $this->readCentralDirectory();
+               
+               return $centralDirectory['files'];
+       }
+       
        /**
         * @see wcf\system\io\IArchive::getFileInfo()
         */
@@ -76,6 +86,7 @@ class Zip extends File implements IArchive {
                catch (SystemException $e) {
                        return false;
                }
+               if ($file['header']['type'] === 'folder') return false;
                
                return $file['content'];
        }
@@ -94,6 +105,11 @@ class Zip extends File implements IArchive {
                }
                
                FileUtil::makePath(dirname($destination));
+               if ($file['header']['type'] === 'folder') {
+                       FileUtil::makePath($destination);
+                       return;
+               }
+               
                $targetFile = new File($destination);
                $targetFile->write($file['content'], strlen($file['content']));
                $targetFile->close();
@@ -105,12 +121,12 @@ class Zip extends File implements IArchive {
                        @$targetFile->chmod(0755);
                }
                
-               if ($file['header']['x-timestamp']) {
-                       @$targetFile->touch($file['header']['x-timestamp']);
+               if ($file['header']['mtime']) {
+                       @$targetFile->touch($file['header']['mtime']);
                }
                
                // check filesize
-               if (filesize($destination) != $file['header']['uncompressedSize']) {
+               if (filesize($destination) != $file['header']['size']) {
                        throw new SystemException("Could not unzip file '".$file['header']['filename']."' to '".$destination."'. Maybe disk quota exceeded in folder '".dirname($destination)."'.");
                }
                
@@ -158,9 +174,11 @@ class Zip extends File implements IArchive {
                        $day = $data['mdate'] & 31 /* 5 bits */;
                        $month = ($data['mdate'] >> 5) & 15 /* 4 bits */;
                        $year = (($data['mdate'] >> 9) & 127 /* 7 bits */) + 1980;
-                       $data['x-timestamp'] = mktime($hour, $minute, $second, $month, $day, $year);
+                       $data['mtime'] = mktime($hour, $minute, $second, $month, $day, $year);
                        
-                       $data += unpack('Vcrc32/VcompressedSize/VuncompressedSize/vfilenameLength/vextraFieldLength/vfileCommentLength/vdiskNo/vinternalAttr/vexternalAttr', $this->read(26));
+                       $data += unpack('Vcrc32/VcompressedSize/Vsize/vfilenameLength/vextraFieldLength/vfileCommentLength/vdiskNo/vinternalAttr/vexternalAttr', $this->read(26));
+                       if ($data['compressedSize'] > 0) $data['type'] = 'file';
+                       else $data['type'] = 'folder';
                        $data['offset'] = $this->readAndUnpack(4, 'v');
                        $data['filename'] = $this->read($data['filenameLength']);
                        
@@ -267,7 +285,7 @@ class Zip extends File implements IArchive {
                $month = ($header['mdate'] >> 5) & 15 /* 4 bits */;
                $year = (($header['mdate'] >> 9) & 127 /* 7 bits */) + 1980;
                $header['x-timestamp'] = mktime($hour, $minute, $second, $month, $day, $year);
-               $header += unpack('Vcrc32/VcompressedSize/VuncompressedSize/vfilenameLength/vextraFieldLength', $this->read(16));
+               $header += unpack('Vcrc32/VcompressedSize/Vsize/vfilenameLength/vextraFieldLength', $this->read(16));
                
                // read filename
                $header['filename'] = $this->read($header['filenameLength']);
@@ -276,7 +294,12 @@ class Zip extends File implements IArchive {
                else $header['extraField'] = '';
                
                // read contents
-               $content = $this->read($header['compressedSize']);
+               $header['type'] = 'file';
+               if ($header['compressedSize'] > 0) $content = $this->read($header['compressedSize']);
+               else {
+                       $header['type'] = 'folder';
+                       $content = false;
+               }
                
                // uncompress file
                switch ($header['compression']) {