[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Implementation of zip packer. 19 * 20 * @package core_files 21 * @copyright 2008 Petr Skoda (http://skodak.org) 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 defined('MOODLE_INTERNAL') || die(); 26 27 require_once("$CFG->libdir/filestorage/file_packer.php"); 28 require_once("$CFG->libdir/filestorage/zip_archive.php"); 29 30 /** 31 * Utility class - handles all zipping and unzipping operations. 32 * 33 * @package core_files 34 * @category files 35 * @copyright 2008 Petr Skoda (http://skodak.org) 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class zip_packer extends file_packer { 39 40 /** 41 * Zip files and store the result in file storage. 42 * 43 * @param array $files array with full zip paths (including directory information) 44 * as keys (archivepath=>ospathname or archivepath/subdir=>stored_file or archivepath=>array('content_as_string')) 45 * @param int $contextid context ID 46 * @param string $component component 47 * @param string $filearea file area 48 * @param int $itemid item ID 49 * @param string $filepath file path 50 * @param string $filename file name 51 * @param int $userid user ID 52 * @param bool $ignoreinvalidfiles true means ignore missing or invalid files, false means abort on any error 53 * @param file_progress $progress Progress indicator callback or null if not required 54 * @return stored_file|bool false if error stored_file instance if ok 55 */ 56 public function archive_to_storage(array $files, $contextid, 57 $component, $filearea, $itemid, $filepath, $filename, 58 $userid = NULL, $ignoreinvalidfiles=true, file_progress $progress = null) { 59 global $CFG; 60 61 $fs = get_file_storage(); 62 63 check_dir_exists($CFG->tempdir.'/zip'); 64 $tmpfile = tempnam($CFG->tempdir.'/zip', 'zipstor'); 65 66 if ($result = $this->archive_to_pathname($files, $tmpfile, $ignoreinvalidfiles, $progress)) { 67 if ($file = $fs->get_file($contextid, $component, $filearea, $itemid, $filepath, $filename)) { 68 if (!$file->delete()) { 69 @unlink($tmpfile); 70 return false; 71 } 72 } 73 $file_record = new stdClass(); 74 $file_record->contextid = $contextid; 75 $file_record->component = $component; 76 $file_record->filearea = $filearea; 77 $file_record->itemid = $itemid; 78 $file_record->filepath = $filepath; 79 $file_record->filename = $filename; 80 $file_record->userid = $userid; 81 $file_record->mimetype = 'application/zip'; 82 83 $result = $fs->create_file_from_pathname($file_record, $tmpfile); 84 } 85 @unlink($tmpfile); 86 return $result; 87 } 88 89 /** 90 * Zip files and store the result in os file. 91 * 92 * @param array $files array with zip paths as keys (archivepath=>ospathname or archivepath=>stored_file or archivepath=>array('content_as_string')) 93 * @param string $archivefile path to target zip file 94 * @param bool $ignoreinvalidfiles true means ignore missing or invalid files, false means abort on any error 95 * @param file_progress $progress Progress indicator callback or null if not required 96 * @return bool true if file created, false if not 97 */ 98 public function archive_to_pathname(array $files, $archivefile, 99 $ignoreinvalidfiles=true, file_progress $progress = null) { 100 $ziparch = new zip_archive(); 101 if (!$ziparch->open($archivefile, file_archive::OVERWRITE)) { 102 return false; 103 } 104 105 $abort = false; 106 foreach ($files as $archivepath => $file) { 107 $archivepath = trim($archivepath, '/'); 108 109 // Record progress each time around this loop. 110 if ($progress) { 111 $progress->progress(); 112 } 113 114 if (is_null($file)) { 115 // Directories have null as content. 116 if (!$ziparch->add_directory($archivepath.'/')) { 117 debugging("Can not zip '$archivepath' directory", DEBUG_DEVELOPER); 118 if (!$ignoreinvalidfiles) { 119 $abort = true; 120 break; 121 } 122 } 123 124 } else if (is_string($file)) { 125 if (!$this->archive_pathname($ziparch, $archivepath, $file, $progress)) { 126 debugging("Can not zip '$archivepath' file", DEBUG_DEVELOPER); 127 if (!$ignoreinvalidfiles) { 128 $abort = true; 129 break; 130 } 131 } 132 133 } else if (is_array($file)) { 134 $content = reset($file); 135 if (!$ziparch->add_file_from_string($archivepath, $content)) { 136 debugging("Can not zip '$archivepath' file", DEBUG_DEVELOPER); 137 if (!$ignoreinvalidfiles) { 138 $abort = true; 139 break; 140 } 141 } 142 143 } else { 144 if (!$this->archive_stored($ziparch, $archivepath, $file, $progress)) { 145 debugging("Can not zip '$archivepath' file", DEBUG_DEVELOPER); 146 if (!$ignoreinvalidfiles) { 147 $abort = true; 148 break; 149 } 150 } 151 } 152 } 153 154 if (!$ziparch->close()) { 155 @unlink($archivefile); 156 return false; 157 } 158 159 if ($abort) { 160 @unlink($archivefile); 161 return false; 162 } 163 164 return true; 165 } 166 167 /** 168 * Perform archiving file from stored file. 169 * 170 * @param zip_archive $ziparch zip archive instance 171 * @param string $archivepath file path to archive 172 * @param stored_file $file stored_file object 173 * @param file_progress $progress Progress indicator callback or null if not required 174 * @return bool success 175 */ 176 private function archive_stored($ziparch, $archivepath, $file, file_progress $progress = null) { 177 $result = $file->archive_file($ziparch, $archivepath); 178 if (!$result) { 179 return false; 180 } 181 182 if (!$file->is_directory()) { 183 return true; 184 } 185 186 $baselength = strlen($file->get_filepath()); 187 $fs = get_file_storage(); 188 $files = $fs->get_directory_files($file->get_contextid(), $file->get_component(), $file->get_filearea(), $file->get_itemid(), 189 $file->get_filepath(), true, true); 190 foreach ($files as $file) { 191 // Record progress for each file. 192 if ($progress) { 193 $progress->progress(); 194 } 195 196 $path = $file->get_filepath(); 197 $path = substr($path, $baselength); 198 $path = $archivepath.'/'.$path; 199 if (!$file->is_directory()) { 200 $path = $path.$file->get_filename(); 201 } 202 // Ignore result here, partial zipping is ok for now. 203 $file->archive_file($ziparch, $path); 204 } 205 206 return true; 207 } 208 209 /** 210 * Perform archiving file from file path. 211 * 212 * @param zip_archive $ziparch zip archive instance 213 * @param string $archivepath file path to archive 214 * @param string $file path name of the file 215 * @param file_progress $progress Progress indicator callback or null if not required 216 * @return bool success 217 */ 218 private function archive_pathname($ziparch, $archivepath, $file, 219 file_progress $progress = null) { 220 // Record progress each time this function is called. 221 if ($progress) { 222 $progress->progress(); 223 } 224 225 if (!file_exists($file)) { 226 return false; 227 } 228 229 if (is_file($file)) { 230 if (!is_readable($file)) { 231 return false; 232 } 233 return $ziparch->add_file_from_pathname($archivepath, $file); 234 } 235 if (is_dir($file)) { 236 if ($archivepath !== '') { 237 $ziparch->add_directory($archivepath); 238 } 239 $files = new DirectoryIterator($file); 240 foreach ($files as $file) { 241 if ($file->isDot()) { 242 continue; 243 } 244 $newpath = $archivepath.'/'.$file->getFilename(); 245 $this->archive_pathname($ziparch, $newpath, $file->getPathname(), $progress); 246 } 247 unset($files); // Release file handles. 248 return true; 249 } 250 } 251 252 /** 253 * Unzip file to given file path (real OS filesystem), existing files are overwritten. 254 * 255 * @todo MDL-31048 localise messages 256 * @param string|stored_file $archivefile full pathname of zip file or stored_file instance 257 * @param string $pathname target directory 258 * @param array $onlyfiles only extract files present in the array. The path to files MUST NOT 259 * start with a /. Example: array('myfile.txt', 'directory/anotherfile.txt') 260 * @param file_progress $progress Progress indicator callback or null if not required 261 * @return bool|array list of processed files; false if error 262 */ 263 public function extract_to_pathname($archivefile, $pathname, 264 array $onlyfiles = null, file_progress $progress = null) { 265 global $CFG; 266 267 if (!is_string($archivefile)) { 268 return $archivefile->extract_to_pathname($this, $pathname, $progress); 269 } 270 271 $processed = array(); 272 273 $pathname = rtrim($pathname, '/'); 274 if (!is_readable($archivefile)) { 275 return false; 276 } 277 $ziparch = new zip_archive(); 278 if (!$ziparch->open($archivefile, file_archive::OPEN)) { 279 return false; 280 } 281 282 // Get the number of files (approx). 283 if ($progress) { 284 $approxmax = $ziparch->estimated_count(); 285 $done = 0; 286 } 287 288 foreach ($ziparch as $info) { 289 // Notify progress. 290 if ($progress) { 291 $progress->progress($done, $approxmax); 292 $done++; 293 } 294 295 $size = $info->size; 296 $name = $info->pathname; 297 298 if ($name === '' or array_key_exists($name, $processed)) { 299 // Probably filename collisions caused by filename cleaning/conversion. 300 continue; 301 } else if (is_array($onlyfiles) && !in_array($name, $onlyfiles)) { 302 // Skipping files which are not in the list. 303 continue; 304 } 305 306 if ($info->is_directory) { 307 $newdir = "$pathname/$name"; 308 // directory 309 if (is_file($newdir) and !unlink($newdir)) { 310 $processed[$name] = 'Can not create directory, file already exists'; // TODO: localise 311 continue; 312 } 313 if (is_dir($newdir)) { 314 //dir already there 315 $processed[$name] = true; 316 } else { 317 if (mkdir($newdir, $CFG->directorypermissions, true)) { 318 $processed[$name] = true; 319 } else { 320 $processed[$name] = 'Can not create directory'; // TODO: localise 321 } 322 } 323 continue; 324 } 325 326 $parts = explode('/', trim($name, '/')); 327 $filename = array_pop($parts); 328 $newdir = rtrim($pathname.'/'.implode('/', $parts), '/'); 329 330 if (!is_dir($newdir)) { 331 if (!mkdir($newdir, $CFG->directorypermissions, true)) { 332 $processed[$name] = 'Can not create directory'; // TODO: localise 333 continue; 334 } 335 } 336 337 $newfile = "$newdir/$filename"; 338 if (!$fp = fopen($newfile, 'wb')) { 339 $processed[$name] = 'Can not write target file'; // TODO: localise 340 continue; 341 } 342 if (!$fz = $ziparch->get_stream($info->index)) { 343 $processed[$name] = 'Can not read file from zip archive'; // TODO: localise 344 fclose($fp); 345 continue; 346 } 347 348 while (!feof($fz)) { 349 $content = fread($fz, 262143); 350 fwrite($fp, $content); 351 } 352 fclose($fz); 353 fclose($fp); 354 if (filesize($newfile) !== $size) { 355 $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise 356 // something went wrong :-( 357 @unlink($newfile); 358 continue; 359 } 360 $processed[$name] = true; 361 } 362 $ziparch->close(); 363 return $processed; 364 } 365 366 /** 367 * Unzip file to given file path (real OS filesystem), existing files are overwritten. 368 * 369 * @todo MDL-31048 localise messages 370 * @param string|stored_file $archivefile full pathname of zip file or stored_file instance 371 * @param int $contextid context ID 372 * @param string $component component 373 * @param string $filearea file area 374 * @param int $itemid item ID 375 * @param string $pathbase file path 376 * @param int $userid user ID 377 * @param file_progress $progress Progress indicator callback or null if not required 378 * @return array|bool list of processed files; false if error 379 */ 380 public function extract_to_storage($archivefile, $contextid, 381 $component, $filearea, $itemid, $pathbase, $userid = NULL, 382 file_progress $progress = null) { 383 global $CFG; 384 385 if (!is_string($archivefile)) { 386 return $archivefile->extract_to_storage($this, $contextid, $component, 387 $filearea, $itemid, $pathbase, $userid, $progress); 388 } 389 390 check_dir_exists($CFG->tempdir.'/zip'); 391 392 $pathbase = trim($pathbase, '/'); 393 $pathbase = ($pathbase === '') ? '/' : '/'.$pathbase.'/'; 394 $fs = get_file_storage(); 395 396 $processed = array(); 397 398 $ziparch = new zip_archive(); 399 if (!$ziparch->open($archivefile, file_archive::OPEN)) { 400 return false; 401 } 402 403 // Get the number of files (approx). 404 if ($progress) { 405 $approxmax = $ziparch->estimated_count(); 406 $done = 0; 407 } 408 409 foreach ($ziparch as $info) { 410 // Notify progress. 411 if ($progress) { 412 $progress->progress($done, $approxmax); 413 $done++; 414 } 415 416 $size = $info->size; 417 $name = $info->pathname; 418 419 if ($name === '' or array_key_exists($name, $processed)) { 420 //probably filename collisions caused by filename cleaning/conversion 421 continue; 422 } 423 424 if ($info->is_directory) { 425 $newfilepath = $pathbase.$name.'/'; 426 $fs->create_directory($contextid, $component, $filearea, $itemid, $newfilepath, $userid); 427 $processed[$name] = true; 428 continue; 429 } 430 431 $parts = explode('/', trim($name, '/')); 432 $filename = array_pop($parts); 433 $filepath = $pathbase; 434 if ($parts) { 435 $filepath .= implode('/', $parts).'/'; 436 } 437 438 if ($size < 2097151) { 439 // Small file. 440 if (!$fz = $ziparch->get_stream($info->index)) { 441 $processed[$name] = 'Can not read file from zip archive'; // TODO: localise 442 continue; 443 } 444 $content = ''; 445 while (!feof($fz)) { 446 $content .= fread($fz, 262143); 447 } 448 fclose($fz); 449 if (strlen($content) !== $size) { 450 $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise 451 // something went wrong :-( 452 unset($content); 453 continue; 454 } 455 456 if ($file = $fs->get_file($contextid, $component, $filearea, $itemid, $filepath, $filename)) { 457 if (!$file->delete()) { 458 $processed[$name] = 'Can not delete existing file'; // TODO: localise 459 continue; 460 } 461 } 462 $file_record = new stdClass(); 463 $file_record->contextid = $contextid; 464 $file_record->component = $component; 465 $file_record->filearea = $filearea; 466 $file_record->itemid = $itemid; 467 $file_record->filepath = $filepath; 468 $file_record->filename = $filename; 469 $file_record->userid = $userid; 470 if ($fs->create_file_from_string($file_record, $content)) { 471 $processed[$name] = true; 472 } else { 473 $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise 474 } 475 unset($content); 476 continue; 477 478 } else { 479 // large file, would not fit into memory :-( 480 $tmpfile = tempnam($CFG->tempdir.'/zip', 'unzip'); 481 if (!$fp = fopen($tmpfile, 'wb')) { 482 @unlink($tmpfile); 483 $processed[$name] = 'Can not write temp file'; // TODO: localise 484 continue; 485 } 486 if (!$fz = $ziparch->get_stream($info->index)) { 487 @unlink($tmpfile); 488 $processed[$name] = 'Can not read file from zip archive'; // TODO: localise 489 continue; 490 } 491 while (!feof($fz)) { 492 $content = fread($fz, 262143); 493 fwrite($fp, $content); 494 } 495 fclose($fz); 496 fclose($fp); 497 if (filesize($tmpfile) !== $size) { 498 $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise 499 // something went wrong :-( 500 @unlink($tmpfile); 501 continue; 502 } 503 504 if ($file = $fs->get_file($contextid, $component, $filearea, $itemid, $filepath, $filename)) { 505 if (!$file->delete()) { 506 @unlink($tmpfile); 507 $processed[$name] = 'Can not delete existing file'; // TODO: localise 508 continue; 509 } 510 } 511 $file_record = new stdClass(); 512 $file_record->contextid = $contextid; 513 $file_record->component = $component; 514 $file_record->filearea = $filearea; 515 $file_record->itemid = $itemid; 516 $file_record->filepath = $filepath; 517 $file_record->filename = $filename; 518 $file_record->userid = $userid; 519 if ($fs->create_file_from_pathname($file_record, $tmpfile)) { 520 $processed[$name] = true; 521 } else { 522 $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise 523 } 524 @unlink($tmpfile); 525 continue; 526 } 527 } 528 $ziparch->close(); 529 return $processed; 530 } 531 532 /** 533 * Returns array of info about all files in archive. 534 * 535 * @param string|file_archive $archivefile 536 * @return array of file infos 537 */ 538 public function list_files($archivefile) { 539 if (!is_string($archivefile)) { 540 return $archivefile->list_files(); 541 } 542 543 $ziparch = new zip_archive(); 544 if (!$ziparch->open($archivefile, file_archive::OPEN)) { 545 return false; 546 } 547 $list = $ziparch->list_files(); 548 $ziparch->close(); 549 return $list; 550 } 551 552 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |