]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-15919 unzipping support
authorskodak <skodak>
Sat, 2 Aug 2008 18:33:11 +0000 (18:33 +0000)
committerskodak <skodak>
Sat, 2 Aug 2008 18:33:11 +0000 (18:33 +0000)
lib/file/file_packer.php
lib/file/file_storage.php
lib/file/stored_file.php

index 2b826f2d5fbfca41095b5bf6ece01c0e7241c390..3263763ec018738fcb75e648a30614cdec531fb7 100644 (file)
@@ -1,5 +1,258 @@
 <?php  //$Id$
 
+/**
+ * Utility class - handles all zipping and unzipping operations.
+ */
 class file_packer {
 
+    public function zip_files_to_pathname($files, $zipfile) {
+        error('todo');
+    }
+
+    public function zip_files_to_storage($files, $contextid, $filearea, $itemid, $filepath, $filename) {
+        error('todo');
+    }
+
+    /**
+     * Unzip file to given file path (real OS filesystem), existing files are overwrited
+     * @param mixed $zipfile full pathname of zip file or stored_file instance
+     * @param string $path target directory
+     * @return mixed list of processed files; false if error
+     */
+    public function unzip_files_to_pathname($zipfile, $pathname) {
+        global $CFG;
+
+        if (!is_string($zipfile)) {
+            return $zipfile->unzip_files_to_pathname($pathname);
+        }
+
+        $processed = array();
+
+        $pathname = rtrim($pathname, '/');
+        if (!is_readable($zipfile)) {
+            return false;
+        }
+        $zip = new ZipArchive();
+        if (!$zip->open($zipfile, ZIPARCHIVE::FL_NOCASE)) {
+            return false;
+        }
+
+        for ($i=0; $i<$zip->numFiles; $i++) {
+            $index = $zip->statIndex($i);
+
+            $size = clean_param($index['size'], PARAM_INT);
+            $name = clean_param($index['name'], PARAM_PATH);
+            $name = ltrim($name, '/');
+
+            //TODO: add $name encoding conversions magic here
+
+            if ($name === '' or array_key_exists($name, $processed)) {
+                //probably filename collisions caused by filename cleaning/conversion
+                continue;
+            }
+
+            if ($size === 0 and $name[strlen($name)-1] === '/') {
+                $newdir = "$pathname/$name";
+                // directory
+                if (is_file($newdir) and !unlink($newdir)) {
+                    $processed[$name] = 'Can not create directory, file already exists'; // TODO: localise
+                    continue;
+                }
+                if (is_dir($newdir)) {
+                    //dir already there
+                    $processed[$name] = true;
+                } else {
+                    if (mkdir($newdir, $CFG->directorypermissions, true)) {
+                        $processed[$name] = true;
+                    } else {
+                        $processed[$name] = 'Can not create directory'; // TODO: localise
+                    }
+                }
+                continue;
+            }
+
+            $parts = explode('/', trim($name, '/'));
+            $filename = array_pop($parts);
+            $newdir = rtrim($pathname.'/'.implode('/', $parts), '/');
+
+            if (!is_dir($newdir)) {
+                if (!mkdir($newdir, $CFG->directorypermissions, true)) {
+                    $processed[$name] = 'Can not create directory'; // TODO: localise
+                    continue;
+                }
+            }
+
+            $newfile = "$newdir/$filename";
+            if (!$fp = fopen($newfile, 'wb')) {
+                $processed[$name] = 'Can not write target file'; // TODO: localise
+                continue;
+            }
+            if (!$fz = $zip->getStream($index['name'])) {
+                $processed[$name] = 'Can not read file from zip archive'; // TODO: localise
+                fclose($fp);
+                continue;
+            }
+
+            while (!feof($fz)) {
+                $content = fread($fz, 262143);
+                fwrite($fp, $content);
+            }
+            fclose($fz);
+            fclose($fp);
+            if (filesize($newfile) !== $size) {
+                $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise
+                // something went wrong :-(
+                @unlink($newfile);
+                continue;
+            }
+            $processed[$name] = true;
+        }
+        $zip->close();
+        return $processed;
+    }
+
+    /**
+     * Unzip file to given file path (real OS filesystem), existing files are overwrited
+     * @param mixed $zipfile full pathname of zip file or stored_file instance
+     * @param int $contextid
+     * @param string $filearea
+     * @param int $itemid
+     * @param string $filepath
+     * @return mixed list of processed files; false if error
+     */
+    public function unzip_files_to_storage($zipfile, $contextid, $filearea, $itemid, $pathbase, $userid=null) {
+        global $CFG;
+
+        if (!is_string($zipfile)) {
+            return $zipfile->unzip_files_to_pathname($contextid, $filearea, $itemid, $pathbase, $userid);
+        }
+
+        check_dir_exists($CFG->dataroot.'/temp/unzip', true, true);
+
+        $pathbase = trim($pathbase, '/');
+        $pathbase = ($pathbase === '') ? '/' : '/'.$pathbase.'/';
+        $fs = get_file_storage();
+
+        $processed = array();
+
+        $zip = new ZipArchive();
+        if (!$zip->open($zipfile, ZIPARCHIVE::FL_NOCASE)) {
+            return false;
+        }
+
+        for ($i=0; $i<$zip->numFiles; $i++) {
+            $index = $zip->statIndex($i);
+
+            $size = clean_param($index['size'], PARAM_INT);
+            $name = clean_param($index['name'], PARAM_PATH);
+            $name = ltrim($name, '/');
+
+            //TODO: add $name encoding conversions magic here
+
+            if ($name === '' or array_key_exists($name, $processed)) {
+                //probably filename collisions caused by filename cleaning/conversion
+                continue;
+            }
+
+            if ($size === 0 and $name[strlen($name)-1] === '/') {
+                $newfilepath = $pathbase.$name.'/';
+                $fs->create_directory($contextid, $filearea, $itemid, $newfilepath, $userid);
+                $processed[$name] = true;
+                continue;
+            }
+
+            $parts = explode('/', trim($name, '/'));
+            $filename = array_pop($parts);
+            $filepath = $pathbase;
+            if ($parts) {
+                $filepath .= implode('/', $parts).'/';
+            }
+
+            if ($size < 2097151) {
+                // small file
+                if (!$fz = $zip->getStream($index['name'])) {
+                    $processed[$name] = 'Can not read file from zip archive'; // TODO: localise
+                    continue;
+                }
+                $content = '';
+                while (!feof($fz)) {
+                    $content .= fread($fz, 262143);
+                }
+                fclose($fz);
+                if (strlen($content) !== $size) {
+                    $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise
+                    // something went wrong :-(
+                    unset($content);
+                    continue;
+                }
+
+                if ($file = $fs->get_file($contextid, $filearea, $itemid, $filepath, $filename)) {
+                    if (!$file->delete()) {
+                        $processed[$name] = 'Can not delete existing file'; // TODO: localise
+                        continue;
+                    }
+                }
+                $file_record = new object();
+                $file_record->contextid = $contextid;
+                $file_record->filearea  = $filearea;
+                $file_record->itemid    = $itemid;
+                $file_record->filepath  = $filepath;
+                $file_record->filename  = $filename;
+                $file_record->userid    = $userid;
+                if ($fs->create_file_from_string($file_record, $content)) {
+                    $processed[$name] = true;
+                } else {
+                    $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise
+                }
+                unset($content);
+                continue;
+
+            } else {
+                // large file, would not fit into memory :-(
+                $tmpfile = tempnam($CFG->dataroot.'/temp/unzip');
+                if (!$fp = fopen($tmpfile, 'wb')) {
+                    $processed[$name] = 'Can not write temp file'; // TODO: localise
+                    continue;
+                }
+                if (!$fz = $zip->getStream($index['name'])) {
+                    $processed[$name] = 'Can not read file from zip archive'; // TODO: localise
+                    continue;
+                }
+                while (!feof($fz)) {
+                    $content = fread($fz, 262143);
+                    fwrite($fp, $content);
+                }
+                fclose($fz);
+                fclose($fp);
+                if (strlen($tmpfile) !== $size) {
+                    $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise
+                    // something went wrong :-(
+                    @unlink($tmpfile);
+                    continue;
+                }
+
+                if ($file = $fs->get_file($contextid, $filearea, $itemid, $filepath, $filename)) {
+                    if (!$file->delete()) {
+                        $processed[$name] = 'Can not delete existing file'; // TODO: localise
+                        continue;
+                    }
+                }
+                $file_record = new object();
+                $file_record->contextid = $contextid;
+                $file_record->filearea  = $filearea;
+                $file_record->itemid    = $itemid;
+                $file_record->filepath  = $filepath;
+                $file_record->filename  = $filename;
+                $file_record->userid    = $userid;
+                if ($fs->create_file_from_pathname($file_record, $tmpfile)) {
+                    $processed[$name] = true;
+                } else {
+                    $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise
+                }
+                @unlink($tmpfile);
+                continue;
+            }
+        }
+        return $processed;
+    }
 }
\ No newline at end of file
index e1b293127fc065c6231d6d5768eb2290b94abe12..c00a157042f7c6502ab9273eaf368a197c9977bc 100644 (file)
@@ -23,7 +23,7 @@ class file_storage {
                 throw new file_exception('localfilecannotcreatefiledirs'); // permission trouble
             }
             // place warning file in file pool root
-            file_put_contents($this->filedir.'/warning.txt', 
+            file_put_contents($this->filedir.'/warning.txt',
                               'This directory contains the content of uploaded files and is controlled by Moodle code. Do not manually move, change or rename any of the files and subdirectories here.');
         }
     }
@@ -570,12 +570,12 @@ class file_storage {
     /**
      * Return path to file with given hash
      *
-     * DO NOT USE - should be protected, but protected is dumb in PHP
+     * NOTE: must not be public, files in pool must not be modified
      *
      * @param string $contenthash
      * @return string expected file location
      */
-    public function path_from_hash($contenthash) {
+    protected function path_from_hash($contenthash) {
         $l1 = $contenthash[0].$contenthash[1];
         $l2 = $contenthash[2].$contenthash[3];
         $l3 = $contenthash[4].$contenthash[5];
index c8371df55e09ea34ded4c8f250a52b6453c43d76..bc41d219f7f2d0e66f0b9ae1700e2445cdf8d13a 100644 (file)
@@ -37,11 +37,21 @@ class stored_file {
 
     /**
      * Protected - developers must not gain direct access to this function
+     * NOTE: do not make this public, we must not modify or delete the pool files directly! ;-)
+     * @return ful path to pool file with file content
      **/
     protected function get_content_file_location() {
-        // NOTE: do not make this public, we must not modify or delete the pool files directly! ;-)
-        $hashpath = $this->fs->path_from_hash($this->file_record->contenthash);
-        return $hashpath.'/'.$this->file_record->contenthash;
+        global $CFG;
+        if (isset($CFG->filedir)) {
+            $filedir = $CFG->filedir;
+        } else {
+            $filedir = $CFG->dataroot.'/filedir';
+        }
+        $contenthash = $this->file_record->contenthash;
+        $l1 = $contenthash[0].$contenthash[1];
+        $l2 = $contenthash[2].$contenthash[3];
+        $l3 = $contenthash[4].$contenthash[5];
+        return "$filedir/$l1/$l2/$l3/$contenthash";
     }
 
     /**
@@ -82,7 +92,7 @@ class stored_file {
 
     /**
      * Copy content of file to give npathname
-     * @param string $pathnema rela path to new file 
+     * @param string $pathnema rela path to new file
      * @return bool success
      */
     public function copy_content_to($pathname) {
@@ -93,6 +103,32 @@ class stored_file {
         return copy($path, $pathname);
     }
 
+    /**
+     * Unzip file to given file path (real OS filesystem), existing files are overwrited
+     * @param string $path target directory
+     * @return mixed list of processed files; false if error
+     */
+    public function unzip_files_to_pathname($path) {
+        $packer = get_file_packer();
+        $zipfile = $this->get_content_file_location();
+        return $packer->unzip_files_to_pathname($path, $path);
+    }
+
+    /**
+     * Unzip file to given file path (real OS filesystem), existing files are overwrited
+     * @param int $contextid
+     * @param string $filearea
+     * @param int $itemid
+     * @param string $pathbase
+     * @param int $userid
+     * @return mixed list of processed files; false if error
+     */
+    public function unzip_files_to_storage($contextid, $filearea, $itemid, $pathbase, $userid=null) {
+        $packer = get_file_packer();
+        $zipfile = $this->get_content_file_location();
+        return $packer->unzip_files_to_storage($zipfile, $contextid, $filearea, $itemid, $pathbase);
+    }
+
     public function get_contextid() {
         return $this->file_record->contextid;
     }