[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/filestorage/ -> zip_packer.php (source)

   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  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1