2009-05-21 08:42:22 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
// This file is part of Moodle - http://moodle.org/
|
|
|
|
//
|
|
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of zip packer.
|
|
|
|
*
|
|
|
|
* @package moodlecore
|
|
|
|
* @subpackage file-packer
|
|
|
|
* @copyright 2008 Petr Skoda (http://skodak.org)
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
2008-07-31 22:15:30 +00:00
|
|
|
|
2008-08-08 10:22:59 +00:00
|
|
|
require_once("$CFG->libdir/packer/file_packer.php");
|
|
|
|
require_once("$CFG->libdir/packer/zip_archive.php");
|
|
|
|
|
2008-08-02 18:33:11 +00:00
|
|
|
/**
|
|
|
|
* Utility class - handles all zipping and unzipping operations.
|
|
|
|
*/
|
2008-08-08 10:22:59 +00:00
|
|
|
class zip_packer extends file_packer {
|
2008-07-31 22:15:30 +00:00
|
|
|
|
2008-08-03 21:37:50 +00:00
|
|
|
/**
|
|
|
|
* Zip files and store the result in file storage
|
2009-11-01 16:48:45 +00:00
|
|
|
* @param array $files array with full zip paths (including directory information) as keys (archivepath=>ospathname or archivepath/subdir=>stored_file)
|
2008-08-03 21:37:50 +00:00
|
|
|
* @param int $contextid
|
|
|
|
* @param string $filearea
|
|
|
|
* @param int $itemid
|
|
|
|
* @param string $filepath
|
|
|
|
* @param string $filename
|
|
|
|
* @return mixed false if error stored file instance if ok
|
|
|
|
*/
|
2008-08-08 10:22:59 +00:00
|
|
|
public function archive_to_storage($files, $contextid, $filearea, $itemid, $filepath, $filename, $userid=null) {
|
2008-08-03 21:37:50 +00:00
|
|
|
global $CFG;
|
|
|
|
|
|
|
|
$fs = get_file_storage();
|
|
|
|
|
|
|
|
check_dir_exists($CFG->dataroot.'/temp/zip', true, true);
|
|
|
|
$tmpfile = tempnam($CFG->dataroot.'/temp/zip', 'zipstor');
|
|
|
|
|
2008-08-08 10:22:59 +00:00
|
|
|
if ($result = $this->archive_to_pathname($files, $tmpfile)) {
|
2008-08-03 21:37:50 +00:00
|
|
|
if ($file = $fs->get_file($contextid, $filearea, $itemid, $filepath, $filename)) {
|
|
|
|
if (!$file->delete()) {
|
|
|
|
@unlink($tmpfile);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$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;
|
2008-08-12 15:52:58 +00:00
|
|
|
$file_record->mimetype = 'application/zip';
|
2008-08-08 10:22:59 +00:00
|
|
|
|
2008-08-03 21:37:50 +00:00
|
|
|
$result = $fs->create_file_from_pathname($file_record, $tmpfile);
|
|
|
|
}
|
|
|
|
@unlink($tmpfile);
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Zip files and store the result in os file
|
2009-11-01 16:48:45 +00:00
|
|
|
* @param array $files array with zip paths as keys (archivepath=>ospathname or archivepath=>stored_file)
|
2008-08-12 11:00:13 +00:00
|
|
|
* @param string $archivefile path to target zip file
|
2008-08-03 21:37:50 +00:00
|
|
|
* @return bool success
|
|
|
|
*/
|
2008-08-08 10:22:59 +00:00
|
|
|
public function archive_to_pathname($files, $archivefile) {
|
2008-08-04 13:25:27 +00:00
|
|
|
global $CFG;
|
|
|
|
|
2008-08-03 21:37:50 +00:00
|
|
|
if (!is_array($files)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$ziparch = new zip_archive();
|
2008-08-08 10:22:59 +00:00
|
|
|
if (!$ziparch->open($archivefile, file_archive::OVERWRITE)) {
|
2008-08-03 21:37:50 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($files as $archivepath => $file) {
|
|
|
|
$archivepath = trim($archivepath, '/');
|
|
|
|
|
|
|
|
if (is_null($file)) {
|
|
|
|
// empty directories have null as content
|
2008-08-08 10:22:59 +00:00
|
|
|
$ziparch->add_directory($archivepath.'/');
|
2008-08-03 21:37:50 +00:00
|
|
|
|
|
|
|
} else if (is_string($file)) {
|
2008-08-08 10:22:59 +00:00
|
|
|
$this->archive_pathname($ziparch, $archivepath, $file);
|
2008-08-03 21:37:50 +00:00
|
|
|
|
|
|
|
} else {
|
2008-08-08 10:22:59 +00:00
|
|
|
$this->archive_stored($ziparch, $archivepath, $file);
|
2008-08-03 21:37:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $ziparch->close();
|
2008-08-02 18:33:11 +00:00
|
|
|
}
|
|
|
|
|
2008-08-08 10:22:59 +00:00
|
|
|
private function archive_stored($ziparch, $archivepath, $file) {
|
|
|
|
$file->archive_file($ziparch, $archivepath);
|
2008-08-03 21:37:50 +00:00
|
|
|
|
|
|
|
if (!$file->is_directory()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$baselength = strlen($file->get_filepath());
|
|
|
|
$fs = get_file_storage();
|
|
|
|
$files = $fs->get_directory_files($file->get_contextid(), $file->get_filearea(), $file->get_itemid(),
|
|
|
|
$file->get_filepath(), true, true);
|
|
|
|
foreach ($files as $file) {
|
|
|
|
$path = $file->get_filepath();
|
|
|
|
$path = substr($path, $baselength);
|
|
|
|
$path = $archivepath.'/'.$path;
|
|
|
|
if (!$file->is_directory()) {
|
|
|
|
$path = $path.$file->get_filename();
|
|
|
|
}
|
2008-08-08 10:22:59 +00:00
|
|
|
$file->archive_file($ziparch, $path);
|
2008-08-03 21:37:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-08 10:22:59 +00:00
|
|
|
private function archive_pathname($ziparch, $archivepath, $file) {
|
2008-08-03 21:37:50 +00:00
|
|
|
if (!file_exists($file)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_file($file)) {
|
|
|
|
if (!is_readable($file)) {
|
|
|
|
return;
|
|
|
|
}
|
2008-08-08 10:22:59 +00:00
|
|
|
$ziparch->add_file_from_pathname($archivepath, $file);
|
2008-08-03 21:37:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (is_dir($file)) {
|
|
|
|
if ($archivepath !== '') {
|
2008-08-08 10:22:59 +00:00
|
|
|
$ziparch->add_directory($archivepath);
|
2008-08-03 21:37:50 +00:00
|
|
|
}
|
|
|
|
$files = new DirectoryIterator($file);
|
|
|
|
foreach ($files as $file) {
|
|
|
|
if ($file->isDot()) {
|
|
|
|
continue;
|
|
|
|
}
|
2008-10-17 06:38:25 +00:00
|
|
|
$newpath = $archivepath.'/'.$file->getFilename();
|
2008-08-08 10:22:59 +00:00
|
|
|
$this->archive_pathname($ziparch, $newpath, $file->getPathname());
|
2008-08-03 21:37:50 +00:00
|
|
|
}
|
|
|
|
unset($files); //release file handles
|
|
|
|
return;
|
|
|
|
}
|
2008-08-02 18:33:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unzip file to given file path (real OS filesystem), existing files are overwrited
|
2008-08-08 10:22:59 +00:00
|
|
|
* @param mixed $archivefile full pathname of zip file or stored_file instance
|
2008-08-03 21:37:50 +00:00
|
|
|
* @param string $pathname target directory
|
2008-08-02 18:33:11 +00:00
|
|
|
* @return mixed list of processed files; false if error
|
|
|
|
*/
|
2008-08-08 10:22:59 +00:00
|
|
|
public function extract_to_pathname($archivefile, $pathname) {
|
2008-08-02 18:33:11 +00:00
|
|
|
global $CFG;
|
|
|
|
|
2008-08-08 10:22:59 +00:00
|
|
|
if (!is_string($archivefile)) {
|
|
|
|
return $archivefile->extract_to_pathname($this, $pathname);
|
2008-08-02 18:33:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$processed = array();
|
|
|
|
|
|
|
|
$pathname = rtrim($pathname, '/');
|
2008-08-08 10:22:59 +00:00
|
|
|
if (!is_readable($archivefile)) {
|
2008-08-02 18:33:11 +00:00
|
|
|
return false;
|
|
|
|
}
|
2008-08-08 10:22:59 +00:00
|
|
|
$ziparch = new zip_archive();
|
|
|
|
if (!$ziparch->open($archivefile, file_archive::OPEN)) {
|
2008-08-02 18:33:11 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-08-08 10:22:59 +00:00
|
|
|
foreach ($ziparch as $info) {
|
|
|
|
$size = $info->size;
|
|
|
|
$name = $info->pathname;
|
2008-08-02 18:33:11 +00:00
|
|
|
|
|
|
|
if ($name === '' or array_key_exists($name, $processed)) {
|
|
|
|
//probably filename collisions caused by filename cleaning/conversion
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-08-08 10:22:59 +00:00
|
|
|
if ($info->is_directory) {
|
2008-08-02 18:33:11 +00:00
|
|
|
$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;
|
|
|
|
}
|
2008-08-08 10:22:59 +00:00
|
|
|
if (!$fz = $ziparch->get_stream($info->index)) {
|
2008-08-02 18:33:11 +00:00
|
|
|
$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;
|
|
|
|
}
|
2008-08-03 21:37:50 +00:00
|
|
|
$ziparch->close();
|
2008-08-02 18:33:11 +00:00
|
|
|
return $processed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unzip file to given file path (real OS filesystem), existing files are overwrited
|
2008-08-08 10:22:59 +00:00
|
|
|
* @param mixed $archivefile full pathname of zip file or stored_file instance
|
2008-08-02 18:33:11 +00:00
|
|
|
* @param int $contextid
|
|
|
|
* @param string $filearea
|
|
|
|
* @param int $itemid
|
|
|
|
* @param string $filepath
|
|
|
|
* @return mixed list of processed files; false if error
|
|
|
|
*/
|
2008-08-08 10:22:59 +00:00
|
|
|
public function extract_to_storage($archivefile, $contextid, $filearea, $itemid, $pathbase, $userid=null) {
|
2008-08-02 18:33:11 +00:00
|
|
|
global $CFG;
|
|
|
|
|
2008-08-08 10:22:59 +00:00
|
|
|
if (!is_string($archivefile)) {
|
|
|
|
return $archivefile->extract_to_pathname($this, $contextid, $filearea, $itemid, $pathbase, $userid);
|
2008-08-02 18:33:11 +00:00
|
|
|
}
|
|
|
|
|
2008-08-03 21:37:50 +00:00
|
|
|
check_dir_exists($CFG->dataroot.'/temp/zip', true, true);
|
2008-08-02 18:33:11 +00:00
|
|
|
|
|
|
|
$pathbase = trim($pathbase, '/');
|
|
|
|
$pathbase = ($pathbase === '') ? '/' : '/'.$pathbase.'/';
|
|
|
|
$fs = get_file_storage();
|
|
|
|
|
|
|
|
$processed = array();
|
|
|
|
|
2008-08-03 21:37:50 +00:00
|
|
|
$ziparch = new zip_archive();
|
2008-08-08 10:22:59 +00:00
|
|
|
if (!$ziparch->open($archivefile, file_archive::OPEN)) {
|
2008-08-02 18:33:11 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-08-08 10:22:59 +00:00
|
|
|
foreach ($ziparch as $info) {
|
|
|
|
$size = $info->size;
|
|
|
|
$name = $info->pathname;
|
2008-08-02 18:33:11 +00:00
|
|
|
|
|
|
|
if ($name === '' or array_key_exists($name, $processed)) {
|
|
|
|
//probably filename collisions caused by filename cleaning/conversion
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-08-08 10:22:59 +00:00
|
|
|
if ($info->is_directory) {
|
2008-08-02 18:33:11 +00:00
|
|
|
$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
|
2008-08-08 10:22:59 +00:00
|
|
|
if (!$fz = $ziparch->get_stream($info->index)) {
|
2008-08-02 18:33:11 +00:00
|
|
|
$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 :-(
|
2008-08-03 21:37:50 +00:00
|
|
|
$tmpfile = tempnam($CFG->dataroot.'/temp/zip', 'unzip');
|
2008-08-02 18:33:11 +00:00
|
|
|
if (!$fp = fopen($tmpfile, 'wb')) {
|
2008-08-02 18:49:23 +00:00
|
|
|
@unlink($tmpfile);
|
2008-08-02 18:33:11 +00:00
|
|
|
$processed[$name] = 'Can not write temp file'; // TODO: localise
|
|
|
|
continue;
|
|
|
|
}
|
2008-08-08 10:22:59 +00:00
|
|
|
if (!$fz = $ziparch->get_stream($info->index)) {
|
2008-08-02 18:49:23 +00:00
|
|
|
@unlink($tmpfile);
|
2008-08-02 18:33:11 +00:00
|
|
|
$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);
|
2008-08-02 18:42:38 +00:00
|
|
|
if (filesize($tmpfile) !== $size) {
|
2008-08-02 18:33:11 +00:00
|
|
|
$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()) {
|
2008-08-02 18:49:23 +00:00
|
|
|
@unlink($tmpfile);
|
2008-08-02 18:33:11 +00:00
|
|
|
$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;
|
|
|
|
}
|
|
|
|
}
|
2008-08-03 21:37:50 +00:00
|
|
|
$ziparch->close();
|
2008-08-02 18:33:11 +00:00
|
|
|
return $processed;
|
|
|
|
}
|
2008-09-03 21:16:54 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns array of info about all files in archive
|
|
|
|
* @return array of file infos
|
|
|
|
*/
|
|
|
|
public function list_files($archivefile) {
|
|
|
|
if (!is_string($archivefile)) {
|
|
|
|
return $archivefile->list_files();
|
|
|
|
}
|
|
|
|
|
|
|
|
$ziparch = new zip_archive();
|
|
|
|
if (!$ziparch->open($archivefile, file_archive::OPEN)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
$list = $ziparch->list_files();
|
|
|
|
$ziparch->close();
|
|
|
|
return $list;
|
|
|
|
}
|
|
|
|
|
2008-10-16 18:23:19 +00:00
|
|
|
}
|