From 17d9269f59a7fdb089e65547398108cea1fda93b Mon Sep 17 00:00:00 2001 From: skodak Date: Sat, 2 Aug 2008 18:33:11 +0000 Subject: [PATCH] MDL-15919 unzipping support --- lib/file/file_packer.php | 253 ++++++++++++++++++++++++++++++++++++++ lib/file/file_storage.php | 6 +- lib/file/stored_file.php | 44 ++++++- 3 files changed, 296 insertions(+), 7 deletions(-) diff --git a/lib/file/file_packer.php b/lib/file/file_packer.php index 2b826f2d5f..3263763ec0 100644 --- a/lib/file/file_packer.php +++ b/lib/file/file_packer.php @@ -1,5 +1,258 @@ 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 diff --git a/lib/file/file_storage.php b/lib/file/file_storage.php index e1b293127f..c00a157042 100644 --- a/lib/file/file_storage.php +++ b/lib/file/file_storage.php @@ -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]; diff --git a/lib/file/stored_file.php b/lib/file/stored_file.php index c8371df55e..bc41d219f7 100644 --- a/lib/file/stored_file.php +++ b/lib/file/stored_file.php @@ -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; } -- 2.39.5