You are here

function PclZip::privExtractFile in Quiz 6.5

Same name and namespace in other branches
  1. 6.6 includes/moodle/lib/pclzip/pclzip.lib.php \PclZip::privExtractFile()
1 call to PclZip::privExtractFile()
PclZip::privExtractByRule in includes/moodle/lib/pclzip/pclzip.lib.php

File

includes/moodle/lib/pclzip/pclzip.lib.php, line 3496

Class

PclZip

Code

function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) {

  //--(MAGIC-PclTrace)--//PclTraceFctStart(__FILE__, __LINE__, 'PclZip::privExtractFile', "path='$p_path', remove_path='$p_remove_path', remove_all_path='".($p_remove_all_path?'true':'false')."'");
  $v_result = 1;

  // ----- Read the file header
  if (($v_result = $this
    ->privReadFileHeader($v_header)) != 1) {

    // ----- Return

    //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
    return $v_result;
  }

  //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Found file '".$v_header['filename']."', size '".$v_header['size']."'");

  // ----- Check that the file header is coherent with $p_entry info
  if ($this
    ->privCheckFileHeaders($v_header, $p_entry) != 1) {

    // TBC
  }

  // ----- Look for all path to remove
  if ($p_remove_all_path == true) {

    // ----- Look for folder entry that not need to be extracted
    if (($p_entry['external'] & 0x10) == 0x10) {

      //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "The entry is a folder : need to be filtered");
      $p_entry['status'] = "filtered";

      //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
      return $v_result;
    }

    //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "All path is removed");

    // ----- Get the basename of the path
    $p_entry['filename'] = basename($p_entry['filename']);
  }
  else {
    if ($p_remove_path != "") {

      //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Look for some path to remove");
      if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2) {

        //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "The folder is the same as the removed path '".$p_entry['filename']."'");

        // ----- Change the file status
        $p_entry['status'] = "filtered";

        // ----- Return

        //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
        return $v_result;
      }
      $p_remove_path_size = strlen($p_remove_path);
      if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) {

        //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Found path '$p_remove_path' to remove in file '".$p_entry['filename']."'");

        // ----- Remove the path
        $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size);

        //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 3, "Resulting file is '".$p_entry['filename']."'");
      }
    }
  }

  // ----- Add the path
  if ($p_path != '') {
    $p_entry['filename'] = $p_path . "/" . $p_entry['filename'];
  }

  // ----- Check a base_dir_restriction
  if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) {

    //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Check the extract directory restriction");
    $v_inclusion = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION], $p_entry['filename']);
    if ($v_inclusion == 0) {

      //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "PCLZIP_OPT_EXTRACT_DIR_RESTRICTION is selected, file is outside restriction");
      PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION, "Filename is outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION");

      //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
      return PclZip::errorCode();
    }
  }

  // ----- Look for pre-extract callback
  if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) {

    //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A pre-callback '".$p_options[PCLZIP_CB_PRE_EXTRACT]."()') is defined for the extraction");

    // ----- Generate a local information
    $v_local_header = array();
    $this
      ->privConvertHeader2FileInfo($p_entry, $v_local_header);

    // ----- Call the callback
    // Here I do not use call_user_func() because I need to send a reference to the
    // header.
    eval('$v_result = ' . $p_options[PCLZIP_CB_PRE_EXTRACT] . '(PCLZIP_CB_PRE_EXTRACT, $v_local_header);');
    if ($v_result == 0) {

      // ----- Change the file status
      $p_entry['status'] = "skipped";
      $v_result = 1;
    }

    // ----- Look for abort result
    if ($v_result == 2) {

      //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "User callback abort the extraction");

      // ----- This status is internal and will be changed in 'skipped'
      $p_entry['status'] = "aborted";
      $v_result = PCLZIP_ERR_USER_ABORTED;
    }

    // ----- Update the informations
    // Only some fields can be modified
    $p_entry['filename'] = $v_local_header['filename'];

    //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "New filename is '".$p_entry['filename']."'");
  }

  //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting file (with path) '".$p_entry['filename']."', size '$v_header[size]'");

  // ----- Look if extraction should be done
  if ($p_entry['status'] == 'ok') {

    // ----- Look for specific actions while the file exist
    if (file_exists($p_entry['filename'])) {

      //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File '".$p_entry['filename']."' already exists");

      // ----- Look if file is a directory
      if (is_dir($p_entry['filename'])) {

        //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is a directory");

        // ----- Change the file status
        $p_entry['status'] = "already_a_directory";

        // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
        // For historical reason first PclZip implementation does not stop
        // when this kind of error occurs.
        if (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]) && $p_options[PCLZIP_OPT_STOP_ON_ERROR] === true) {

          //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "PCLZIP_OPT_STOP_ON_ERROR is selected, extraction will be stopped");
          PclZip::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY, "Filename '" . $p_entry['filename'] . "' is " . "already used by an existing directory");

          //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
          return PclZip::errorCode();
        }
      }
      else {
        if (!is_writeable($p_entry['filename'])) {

          //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is write protected");

          // ----- Change the file status
          $p_entry['status'] = "write_protected";

          // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
          // For historical reason first PclZip implementation does not stop
          // when this kind of error occurs.
          if (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]) && $p_options[PCLZIP_OPT_STOP_ON_ERROR] === true) {

            //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "PCLZIP_OPT_STOP_ON_ERROR is selected, extraction will be stopped");
            PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, "Filename '" . $p_entry['filename'] . "' exists " . "and is write protected");

            //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
            return PclZip::errorCode();
          }
        }
        else {
          if (filemtime($p_entry['filename']) > $p_entry['mtime']) {

            //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is newer (".date("l dS of F Y h:i:s A", filemtime($p_entry['filename'])).") than the extracted file (".date("l dS of F Y h:i:s A", $p_entry['mtime']).")");

            // ----- Change the file status
            if (isset($p_options[PCLZIP_OPT_REPLACE_NEWER]) && $p_options[PCLZIP_OPT_REPLACE_NEWER] === true) {

              //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "PCLZIP_OPT_REPLACE_NEWER is selected, file will be replaced");
            }
            else {

              //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File will not be replaced");
              $p_entry['status'] = "newer_exist";

              // ----- Look for PCLZIP_OPT_STOP_ON_ERROR
              // For historical reason first PclZip implementation does not stop
              // when this kind of error occurs.
              if (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR]) && $p_options[PCLZIP_OPT_STOP_ON_ERROR] === true) {

                //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "PCLZIP_OPT_STOP_ON_ERROR is selected, extraction will be stopped");
                PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, "Newer version of '" . $p_entry['filename'] . "' exists " . "and option PCLZIP_OPT_REPLACE_NEWER is not selected");

                //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, PclZip::errorCode(), PclZip::errorInfo());
                return PclZip::errorCode();
              }
            }
          }
          else {

            //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Existing file '".$p_entry['filename']."' is older than the extrated one - will be replaced by the extracted one (".date("l dS of F Y h:i:s A", filemtime($p_entry['filename'])).") than the extracted file (".date("l dS of F Y h:i:s A", $p_entry['mtime']).")");
          }
        }
      }
    }
    else {
      if (($p_entry['external'] & 0x10) == 0x10 || substr($p_entry['filename'], -1) == '/') {
        $v_dir_to_check = $p_entry['filename'];
      }
      else {
        if (!strstr($p_entry['filename'], "/")) {
          $v_dir_to_check = "";
        }
        else {
          $v_dir_to_check = dirname($p_entry['filename']);
        }
      }
      if (($v_result = $this
        ->privDirCheck($v_dir_to_check, ($p_entry['external'] & 0x10) == 0x10)) != 1) {

        //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Unable to create path for '".$p_entry['filename']."'");

        // ----- Change the file status
        $p_entry['status'] = "path_creation_fail";

        // ----- Return

        ////--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);

        //return $v_result;
        $v_result = 1;
      }
    }
  }

  // ----- Look if extraction should be done
  if ($p_entry['status'] == 'ok') {

    // ----- Do the extraction (if not a folder)
    if (!(($p_entry['external'] & 0x10) == 0x10)) {

      // ----- Look for not compressed file
      if ($p_entry['compression'] == 0) {

        //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting an un-compressed file");

        // ----- Opening destination file
        if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {

          //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Error while opening '".$p_entry['filename']."' in write binary mode");

          // ----- Change the file status
          $p_entry['status'] = "write_error";

          // ----- Return

          //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
          return $v_result;
        }

        //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Read '".$p_entry['size']."' bytes");

        // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks
        $v_size = $p_entry['compressed_size'];
        while ($v_size != 0) {
          $v_read_size = $v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE;

          //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Read $v_read_size bytes");
          $v_buffer = @fread($this->zip_fd, $v_read_size);

          /* Try to speed up the code
             $v_binary_data = pack('a'.$v_read_size, $v_buffer);
             @fwrite($v_dest_file, $v_binary_data, $v_read_size);
             */
          @fwrite($v_dest_file, $v_buffer, $v_read_size);
          $v_size -= $v_read_size;
        }

        // ----- Closing the destination file
        fclose($v_dest_file);

        // ----- Change the file mtime
        touch($p_entry['filename'], $p_entry['mtime']);
      }
      else {

        //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extracting a compressed file (Compression method ".$p_entry['compression'].")");

        // ----- TBC
        // Need to be finished
        if (($p_entry['flag'] & 1) == 1) {

          //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "File is encrypted");

          /*
          // ----- Read the encryption header
          //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Read 12 encryption header bytes");
          $v_encryption_header = @fread($this->zip_fd, 12);

          // ----- Read the encrypted & compressed file in a buffer
          //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Read '".($p_entry['compressed_size']-12)."' compressed & encrypted bytes");
          $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']-12);

          // ----- Decrypt the buffer
          $this->privDecrypt($v_encryption_header, $v_buffer,
          			                     $p_entry['compressed_size']-12, $p_entry['crc']);
          //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Buffer is '".$v_buffer."'");
          */
        }
        else {

          //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 5, "Read '".$p_entry['compressed_size']."' compressed bytes");

          // ----- Read the compressed file in a buffer (one shot)
          $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']);
        }

        // ----- Decompress the file
        $v_file_content = @gzinflate($v_buffer);
        unset($v_buffer);
        if ($v_file_content === FALSE) {

          //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Unable to inflate compressed file");

          // ----- Change the file status
          // TBC
          $p_entry['status'] = "error";

          //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
          return $v_result;
        }

        // ----- Opening destination file
        if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {

          //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Error while opening '".$p_entry['filename']."' in write binary mode");

          // ----- Change the file status
          $p_entry['status'] = "write_error";

          //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
          return $v_result;
        }

        // ----- Write the uncompressed data
        @fwrite($v_dest_file, $v_file_content, $p_entry['size']);
        unset($v_file_content);

        // ----- Closing the destination file
        @fclose($v_dest_file);

        // ----- Change the file mtime
        @touch($p_entry['filename'], $p_entry['mtime']);
      }

      // ----- Look for chmod option
      if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) {

        //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "chmod option activated '".$p_options[PCLZIP_OPT_SET_CHMOD]."'");

        // ----- Change the mode of the file
        @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]);
      }

      //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "Extraction done");
    }
  }

  // ----- Change abort status
  if ($p_entry['status'] == "aborted") {
    $p_entry['status'] = "skipped";
  }
  elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) {

    //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "A post-callback '".$p_options[PCLZIP_CB_POST_EXTRACT]."()') is defined for the extraction");

    // ----- Generate a local information
    $v_local_header = array();
    $this
      ->privConvertHeader2FileInfo($p_entry, $v_local_header);

    // ----- Call the callback
    // Here I do not use call_user_func() because I need to send a reference to the
    // header.
    eval('$v_result = ' . $p_options[PCLZIP_CB_POST_EXTRACT] . '(PCLZIP_CB_POST_EXTRACT, $v_local_header);');

    // ----- Look for abort result
    if ($v_result == 2) {

      //--(MAGIC-PclTrace)--//PclTraceFctMessage(__FILE__, __LINE__, 2, "User callback abort the extraction");
      $v_result = PCLZIP_ERR_USER_ABORTED;
    }
  }

  // ----- Return

  //--(MAGIC-PclTrace)--//PclTraceFctEnd(__FILE__, __LINE__, $v_result);
  return $v_result;
}