/** * Imports a zip file into the Chamilo structure * @param string Zip file info as given by $_FILES['userFile'] * @return string Absolute path to the imsmanifest.xml file or empty string on error */ function import_package($zip_file_info, $current_dir = '') { if ($this->debug > 0) { error_log('In scorm::import_package(' . print_r($zip_file_info, true) . ',"' . $current_dir . '") method', 0); } $maxFilledSpace = DocumentManager::get_course_quota(); $zip_file_path = $zip_file_info['tmp_name']; $zip_file_name = $zip_file_info['name']; if ($this->debug > 1) { error_log('New LP - import_package() - zip file path = ' . $zip_file_path . ', zip file name = ' . $zip_file_name, 0); } // scorm dir web path starting from /courses $course_rel_dir = api_get_course_path() . '/scorm'; $course_sys_dir = api_get_path(SYS_COURSE_PATH) . $course_rel_dir; // Absolute system path for this course. if (!is_dir($course_sys_dir)) { mkdir($course_sys_dir, api_get_permissions_for_new_directories()); } $current_dir = api_replace_dangerous_char(trim($current_dir), 'strict'); // Current dir we are in, inside scorm/ if ($this->debug > 1) { error_log('New LP - import_package() - current_dir = ' . $current_dir, 0); } //$uploaded_filename = $_FILES['userFile']['name']; // Get name of the zip file without the extension. if ($this->debug > 1) { error_log('New LP - Received zip file name: ' . $zip_file_path, 0); } $file_info = pathinfo($zip_file_name); $filename = $file_info['basename']; $extension = $file_info['extension']; $file_base_name = str_replace('.' . $extension, '', $filename); // Filename without its extension. $this->zipname = $file_base_name; // Save for later in case we don't have a title. if ($this->debug > 1) { error_log("New LP - base file name is : " . $file_base_name, 0); } $new_dir = api_replace_dangerous_char(trim($file_base_name), 'strict'); $this->subdir = $new_dir; if ($this->debug > 1) { error_log("New LP - subdir is first set to : " . $this->subdir, 0); } $zipFile = new PclZip($zip_file_path); // Check the zip content (real size and file extension). $zipContentArray = $zipFile->listContent(); $package_type = ''; $at_root = false; $manifest = ''; $realFileSize = 0; $manifest_list = array(); // The following loop should be stopped as soon as we found the right imsmanifest.xml (how to recognize it?). foreach ($zipContentArray as $thisContent) { $file = $thisContent['filename']; //error_log('Looking at '.$thisContent['filename'], 0); if (preg_match('~.(php.*|phtml)$~i', $file)) { $this->set_error_msg("File {$file} contains a PHP script"); //return api_failure::set_failure('php_file_in_zip_file'); } elseif (stristr($thisContent['filename'], 'imsmanifest.xml')) { //error_log('Found imsmanifest at '.$thisContent['filename'], 0); if ($thisContent['filename'] == basename($thisContent['filename'])) { $at_root = true; } else { //$this->subdir .= '/'.dirname($thisContent['filename']); if ($this->debug > 2) { error_log("New LP - subdir is now " . $this->subdir, 0); } } $package_type = 'scorm'; $manifest_list[] = $thisContent['filename']; $manifest = $thisContent['filename']; //just the relative directory inside scorm/ } else { // Do nothing, if it has not been set as scorm somewhere else, it stays as '' default. } $realFileSize += $thisContent['size']; } // Now get the shortest path (basically, the imsmanifest that is the closest to the root). $shortest_path = $manifest_list[0]; $slash_count = substr_count($shortest_path, '/'); foreach ($manifest_list as $manifest_path) { $tmp_slash_count = substr_count($manifest_path, '/'); if ($tmp_slash_count < $slash_count) { $shortest_path = $manifest_path; $slash_count = $tmp_slash_count; } } $this->subdir .= '/' . dirname($shortest_path); // Do not concatenate because already done above. $manifest = $shortest_path; if ($this->debug > 1) { error_log('New LP - Package type is now ' . $package_type, 0); } // && defined('CHECK_FOR_SCORM') && CHECK_FOR_SCORM) if ($package_type == '') { if ($this->debug > 1) { error_log('New LP - Package type is empty', 0); } return api_failure::set_failure('not_scorm_content'); } // It happens on Linux that $new_dir sometimes doesn't start with '/' if ($new_dir[0] != '/') { $new_dir = '/' . $new_dir; } if ($new_dir[strlen($new_dir) - 1] == '/') { $new_dir = substr($new_dir, 0, -1); } $isDir = is_dir($course_sys_dir . $new_dir); if ($isDir == false) { mkdir($course_sys_dir . $new_dir, api_get_permissions_for_new_directories()); $isDir = is_dir($course_sys_dir . $new_dir); } /* Uncompressing phase */ /* We need to process each individual file in the zip archive to - add it to the database - parse & change relative html links - make sure the filenames are secure (filter funny characters or php extensions) */ if ($isDir) { if (!FileManager::enough_size($realFileSize, $course_sys_dir, $maxFilledSpace)) { if ($this->debug > 1) { error_log('New LP - Not enough space to store package', 0); } return api_failure::set_failure('not_enough_space'); } // PHP method - slower... if ($this->debug >= 1) { error_log('New LP - Changing dir to ' . $course_sys_dir . $new_dir, 0); } $saved_dir = getcwd(); chdir($course_sys_dir . $new_dir); $unzippingState = $zipFile->extract(); for ($j = 0; $j < count($unzippingState); $j++) { $state = $unzippingState[$j]; // TODO: Fix relative links in html files (?) $extension = strrchr($state['stored_filename'], '.'); if ($this->debug >= 1) { error_log('New LP - found extension ' . $extension . ' in ' . $state['stored_filename'], 0); } } if (!empty($new_dir)) { $new_dir = $new_dir . '/'; } // Rename files, for example with \\ in it. if ($this->debug >= 1) { error_log('New LP - try to open: ' . $course_sys_dir . $new_dir, 0); } if ($dir = @opendir($course_sys_dir . $new_dir)) { if ($this->debug >= 1) { error_log('New LP - Opened dir ' . $course_sys_dir . $new_dir, 0); } while ($file = readdir($dir)) { if ($file != '.' && $file != '..') { $filetype = 'file'; if (is_dir($course_sys_dir . $new_dir . $file)) { $filetype = 'folder'; } // TODO: RENAMING FILES CAN BE VERY DANGEROUS SCORM-WISE, avoid that as much as possible! //$safe_file = replace_dangerous_char($file, 'strict'); $find_str = array('\\', '.php', '.phtml'); $repl_str = array('/', '.txt', '.txt'); $safe_file = str_replace($find_str, $repl_str, $file); if ($this->debug >= 1) { error_log('Comparing: ' . $safe_file, 0); } if ($this->debug >= 1) { error_log('and: ' . $file, 0); } if ($safe_file != $file) { $mydir = dirname($course_sys_dir . $new_dir . $safe_file); if (!is_dir($mydir)) { $mysubdirs = split('/', $mydir); $mybasedir = '/'; foreach ($mysubdirs as $mysubdir) { if (!empty($mysubdir)) { $mybasedir = $mybasedir . $mysubdir . '/'; if (!is_dir($mybasedir)) { @mkdir($mybasedir, api_get_permissions_for_new_directories()); if ($this->debug >= 1) { error_log('New LP - Dir ' . $mybasedir . ' doesnt exist. Creating.', 0); } } } } } @rename($course_sys_dir . $new_dir . $file, $course_sys_dir . $new_dir . $safe_file); if ($this->debug >= 1) { error_log('New LP - Renaming ' . $course_sys_dir . $new_dir . $file . ' to ' . $course_sys_dir . $new_dir . $safe_file, 0); } } } } closedir($dir); chdir($saved_dir); api_chmod_R($course_sys_dir . $new_dir, api_get_permissions_for_new_directories()); if ($this->debug > 1) { error_log('New LP - changed back to init dir: ' . $course_sys_dir . $new_dir, 0); } } } else { return ''; } return $course_sys_dir . $new_dir . $manifest; }
function api_chmod_R($path, $filemode) { if (!is_dir($path)) { return chmod($path, $filemode); } $handler = opendir($path); while ($file = readdir($handler)) { if ($file != '.' && $file != '..') { $fullpath = "{$path}/{$file}"; if (!is_dir($fullpath)) { if (!chmod($fullpath, $filemode)) { return false; } } else { if (!api_chmod_R($fullpath, $filemode)) { return false; } } } } closedir($handler); return chmod($path, $filemode); }