[ Index ] |
PHP Cross Reference of Unnamed Project |
[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 * @param bool $returnbool Whether to return a basic true/false indicating error state, or full per-file error 262 * details. 263 * @return bool|array list of processed files; false if error 264 */ 265 public function extract_to_pathname($archivefile, $pathname, 266 array $onlyfiles = null, file_progress $progress = null, $returnbool = false) { 267 global $CFG; 268 269 if (!is_string($archivefile)) { 270 return $archivefile->extract_to_pathname($this, $pathname, $progress); 271 } 272 273 $processed = array(); 274 $success = true; 275 276 $pathname = rtrim($pathname, '/'); 277 if (!is_readable($archivefile)) { 278 return false; 279 } 280 $ziparch = new zip_archive(); 281 if (!$ziparch->open($archivefile, file_archive::OPEN)) { 282 return false; 283 } 284 285 // Get the number of files (approx). 286 if ($progress) { 287 $approxmax = $ziparch->estimated_count(); 288 $done = 0; 289 } 290 291 foreach ($ziparch as $info) { 292 // Notify progress. 293 if ($progress) { 294 $progress->progress($done, $approxmax); 295 $done++; 296 } 297 298 $size = $info->size; 299 $name = $info->pathname; 300 301 if ($name === '' or array_key_exists($name, $processed)) { 302 // Probably filename collisions caused by filename cleaning/conversion. 303 continue; 304 } else if (is_array($onlyfiles) && !in_array($name, $onlyfiles)) { 305 // Skipping files which are not in the list. 306 continue; 307 } 308 309 if ($info->is_directory) { 310 $newdir = "$pathname/$name"; 311 // directory 312 if (is_file($newdir) and !unlink($newdir)) { 313 $processed[$name] = 'Can not create directory, file already exists'; // TODO: localise 314 $success = false; 315 continue; 316 } 317 if (is_dir($newdir)) { 318 //dir already there 319 $processed[$name] = true; 320 } else { 321 if (mkdir($newdir, $CFG->directorypermissions, true)) { 322 $processed[$name] = true; 323 } else { 324 $processed[$name] = 'Can not create directory'; // TODO: localise 325 $success = false; 326 } 327 } 328 continue; 329 } 330 331 $parts = explode('/', trim($name, '/')); 332 $filename = array_pop($parts); 333 $newdir = rtrim($pathname.'/'.implode('/', $parts), '/'); 334 335 if (!is_dir($newdir)) { 336 if (!mkdir($newdir, $CFG->directorypermissions, true)) { 337 $processed[$name] = 'Can not create directory'; // TODO: localise 338 $success = false; 339 continue; 340 } 341 } 342 343 $newfile = "$newdir/$filename"; 344 if (!$fp = fopen($newfile, 'wb')) { 345 $processed[$name] = 'Can not write target file'; // TODO: localise 346 $success = false; 347 continue; 348 } 349 if (!$fz = $ziparch->get_stream($info->index)) { 350 $processed[$name] = 'Can not read file from zip archive'; // TODO: localise 351 $success = false; 352 fclose($fp); 353 continue; 354 } 355 356 while (!feof($fz)) { 357 $content = fread($fz, 262143); 358 fwrite($fp, $content); 359 } 360 fclose($fz); 361 fclose($fp); 362 if (filesize($newfile) !== $size) { 363 $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise 364 $success = false; 365 // something went wrong :-( 366 @unlink($newfile); 367 continue; 368 } 369 $processed[$name] = true; 370 } 371 $ziparch->close(); 372 373 if ($returnbool) { 374 return $success; 375 } else { 376 return $processed; 377 } 378 } 379 380 /** 381 * Unzip file to given file path (real OS filesystem), existing files are overwritten. 382 * 383 * @todo MDL-31048 localise messages 384 * @param string|stored_file $archivefile full pathname of zip file or stored_file instance 385 * @param int $contextid context ID 386 * @param string $component component 387 * @param string $filearea file area 388 * @param int $itemid item ID 389 * @param string $pathbase file path 390 * @param int $userid user ID 391 * @param file_progress $progress Progress indicator callback or null if not required 392 * @return array|bool list of processed files; false if error 393 */ 394 public function extract_to_storage($archivefile, $contextid, 395 $component, $filearea, $itemid, $pathbase, $userid = NULL, 396 file_progress $progress = null) { 397 global $CFG; 398 399 if (!is_string($archivefile)) { 400 return $archivefile->extract_to_storage($this, $contextid, $component, 401 $filearea, $itemid, $pathbase, $userid, $progress); 402 } 403 404 check_dir_exists($CFG->tempdir.'/zip'); 405 406 $pathbase = trim($pathbase, '/'); 407 $pathbase = ($pathbase === '') ? '/' : '/'.$pathbase.'/'; 408 $fs = get_file_storage(); 409 410 $processed = array(); 411 412 $ziparch = new zip_archive(); 413 if (!$ziparch->open($archivefile, file_archive::OPEN)) { 414 return false; 415 } 416 417 // Get the number of files (approx). 418 if ($progress) { 419 $approxmax = $ziparch->estimated_count(); 420 $done = 0; 421 } 422 423 foreach ($ziparch as $info) { 424 // Notify progress. 425 if ($progress) { 426 $progress->progress($done, $approxmax); 427 $done++; 428 } 429 430 $size = $info->size; 431 $name = $info->pathname; 432 433 if ($name === '' or array_key_exists($name, $processed)) { 434 //probably filename collisions caused by filename cleaning/conversion 435 continue; 436 } 437 438 if ($info->is_directory) { 439 $newfilepath = $pathbase.$name.'/'; 440 $fs->create_directory($contextid, $component, $filearea, $itemid, $newfilepath, $userid); 441 $processed[$name] = true; 442 continue; 443 } 444 445 $parts = explode('/', trim($name, '/')); 446 $filename = array_pop($parts); 447 $filepath = $pathbase; 448 if ($parts) { 449 $filepath .= implode('/', $parts).'/'; 450 } 451 452 if ($size < 2097151) { 453 // Small file. 454 if (!$fz = $ziparch->get_stream($info->index)) { 455 $processed[$name] = 'Can not read file from zip archive'; // TODO: localise 456 continue; 457 } 458 $content = ''; 459 while (!feof($fz)) { 460 $content .= fread($fz, 262143); 461 } 462 fclose($fz); 463 if (strlen($content) !== $size) { 464 $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise 465 // something went wrong :-( 466 unset($content); 467 continue; 468 } 469 470 if ($file = $fs->get_file($contextid, $component, $filearea, $itemid, $filepath, $filename)) { 471 if (!$file->delete()) { 472 $processed[$name] = 'Can not delete existing file'; // TODO: localise 473 continue; 474 } 475 } 476 $file_record = new stdClass(); 477 $file_record->contextid = $contextid; 478 $file_record->component = $component; 479 $file_record->filearea = $filearea; 480 $file_record->itemid = $itemid; 481 $file_record->filepath = $filepath; 482 $file_record->filename = $filename; 483 $file_record->userid = $userid; 484 if ($fs->create_file_from_string($file_record, $content)) { 485 $processed[$name] = true; 486 } else { 487 $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise 488 } 489 unset($content); 490 continue; 491 492 } else { 493 // large file, would not fit into memory :-( 494 $tmpfile = tempnam($CFG->tempdir.'/zip', 'unzip'); 495 if (!$fp = fopen($tmpfile, 'wb')) { 496 @unlink($tmpfile); 497 $processed[$name] = 'Can not write temp file'; // TODO: localise 498 continue; 499 } 500 if (!$fz = $ziparch->get_stream($info->index)) { 501 @unlink($tmpfile); 502 $processed[$name] = 'Can not read file from zip archive'; // TODO: localise 503 continue; 504 } 505 while (!feof($fz)) { 506 $content = fread($fz, 262143); 507 fwrite($fp, $content); 508 } 509 fclose($fz); 510 fclose($fp); 511 if (filesize($tmpfile) !== $size) { 512 $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise 513 // something went wrong :-( 514 @unlink($tmpfile); 515 continue; 516 } 517 518 if ($file = $fs->get_file($contextid, $component, $filearea, $itemid, $filepath, $filename)) { 519 if (!$file->delete()) { 520 @unlink($tmpfile); 521 $processed[$name] = 'Can not delete existing file'; // TODO: localise 522 continue; 523 } 524 } 525 $file_record = new stdClass(); 526 $file_record->contextid = $contextid; 527 $file_record->component = $component; 528 $file_record->filearea = $filearea; 529 $file_record->itemid = $itemid; 530 $file_record->filepath = $filepath; 531 $file_record->filename = $filename; 532 $file_record->userid = $userid; 533 if ($fs->create_file_from_pathname($file_record, $tmpfile)) { 534 $processed[$name] = true; 535 } else { 536 $processed[$name] = 'Unknown error during zip extraction'; // TODO: localise 537 } 538 @unlink($tmpfile); 539 continue; 540 } 541 } 542 $ziparch->close(); 543 return $processed; 544 } 545 546 /** 547 * Returns array of info about all files in archive. 548 * 549 * @param string|file_archive $archivefile 550 * @return array of file infos 551 */ 552 public function list_files($archivefile) { 553 if (!is_string($archivefile)) { 554 return $archivefile->list_files(); 555 } 556 557 $ziparch = new zip_archive(); 558 if (!$ziparch->open($archivefile, file_archive::OPEN)) { 559 return false; 560 } 561 $list = $ziparch->list_files(); 562 $ziparch->close(); 563 return $list; 564 } 565 566 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |