/** * Unset all additional properties of test classes to help PHP * garbage collection. This reduces memory footprint with lots * of tests. * * If owerwriting tearDown() in test classes, please call * parent::tearDown() at the end. Unsetting of own properties * is not needed this way. * * @throws \RuntimeException * @return void */ protected function tearDown() { // Unset properties of test classes to safe memory $reflection = new \ReflectionObject($this); foreach ($reflection->getProperties() as $property) { $declaringClass = $property->getDeclaringClass()->getName(); if (!$property->isStatic() && $declaringClass !== \TYPO3\CMS\Core\Tests\UnitTestCase::class && $declaringClass !== \TYPO3\CMS\Core\Tests\BaseTestCase::class && strpos($property->getDeclaringClass()->getName(), 'PHPUnit_') !== 0) { $propertyName = $property->getName(); unset($this->{$propertyName}); } } unset($reflection); // Delete registered test files and directories foreach ($this->testFilesToDelete as $absoluteFileName) { $absoluteFileName = GeneralUtility::fixWindowsFilePath(PathUtility::getCanonicalPath($absoluteFileName)); if (!GeneralUtility::validPathStr($absoluteFileName)) { throw new \RuntimeException('tearDown() cleanup: Filename contains illegal characters', 1410633087); } if (!StringUtility::beginsWith($absoluteFileName, PATH_site . 'typo3temp/')) { throw new \RuntimeException('tearDown() cleanup: Files to delete must be within typo3temp/', 1410633412); } // file_exists returns false for links pointing to not existing targets, so handle links before next check. if (@is_link($absoluteFileName) || @is_file($absoluteFileName)) { unlink($absoluteFileName); } elseif (@is_dir($absoluteFileName)) { GeneralUtility::rmdir($absoluteFileName, true); } else { throw new \RuntimeException('tearDown() cleanup: File, link or directory does not exist', 1410633510); } } $this->testFilesToDelete = array(); }
/** * Check extension of given filename * * @param string $filename Filename like (upload.png) * @return bool If Extension is allowed */ public static function checkExtension($filename) { $extensionList = 'jpg,jpeg,png,gif,bmp'; $settings = self::getTypoScriptFrontendController()->tmpl->setup['plugin.']['tx_femanager.']['settings.']; if (!empty($settings['misc.']['uploadFileExtension'])) { $extensionList = $settings['misc.']['uploadFileExtension']; $extensionList = str_replace(' ', '', $extensionList); } $fileInfo = pathinfo($filename); return !empty($fileInfo['extension']) && GeneralUtility::inList($extensionList, strtolower($fileInfo['extension'])) && GeneralUtility::verifyFilenameAgainstDenyPattern($filename) && GeneralUtility::validPathStr($filename); }
/** * Validates the additional fields' values * * @param array $submittedData An array containing the data submitted by the add/edit task form * @param \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $schedulerModule Reference to the scheduler backend module * @return boolean TRUE if validation was ok (or selected class is not relevant), FALSE otherwise */ public function validateAdditionalFields(array &$submittedData, SchedulerModuleController $schedulerModule) { $validInput = true; $directoriesToClean = GeneralUtility::trimExplode(LF, $submittedData[$this->fieldPrefix . 'DirectoriesToClean'], true); foreach ($directoriesToClean as $path) { $path = trim($path, DIRECTORY_SEPARATOR); if (!(strlen($path) > 0 && file_exists(PATH_site . $path) && GeneralUtility::isAllowedAbsPath(PATH_site . $path) && GeneralUtility::validPathStr($path) && !GeneralUtility::inList($this->blackList, $path))) { $validInput = false; break; } } if (empty($submittedData[$this->fieldPrefix . 'DirectoriesToClean']) || $validInput === false) { $schedulerModule->addMessage($GLOBALS['LANG']->sL('LLL:EXT:minicleaner/locallang.xml:error.pathNotValid'), FlashMessage::ERROR); $validInput = false; } return $validInput; }
public function execute() { $directories = GeneralUtility::trimExplode(LF, $this->directoriesToClean, true); if (is_array($directories)) { foreach ($directories as $key => $directory) { $path = PATH_site . trim($directory, DIRECTORY_SEPARATOR); if ($path != PATH_site && file_exists($path) && GeneralUtility::isAllowedAbsPath($path) && GeneralUtility::validPathStr($path) && !GeneralUtility::inList($this->blackList, $path)) { $result = GeneralUtility::flushDirectory($path, true); if ($result === false) { GeneralUtility::devLog($GLOBALS['LANG']->sL('LLL:EXT:minicleaner/locallang.xml:error.couldNotFlushDirectory'), 'minicleaner', 3); return false; } } else { GeneralUtility::devLog($GLOBALS['LANG']->sL('LLL:EXT:minicleaner/locallang.xml:error.pathNotFound'), 'minicleaner', 3); return false; } } } return true; }
/** * Checks if $thePath is a path under one of the paths in $this->mounts * See comment in the header of this class. * * @param string $thePath MUST HAVE a trailing '/' in order to match correctly with the mounts * @return string The key to the first mount found, otherwise nothing is returned. * @see init() * @todo: deprecate this function, now done in the Storage object. But still in use by impexp and ExtendedFileUtility * @deprecated but still in use in the Core. Don't use in your extensions! */ public function checkPathAgainstMounts($thePath) { if ($thePath && GeneralUtility::validPathStr($thePath) && is_array($this->mounts)) { foreach ($this->mounts as $k => $val) { if (GeneralUtility::isFirstPartOfStr($thePath, $val['path'])) { return $k; } } } }
/** * Get file info of uploaded file * * @return array */ protected function getUploadedFileInfo() { $uploadData = $_FILES[$this->getNamespace()]; $fileData = []; if (is_array($uploadData) && count($uploadData) > 0) { $filename = str_replace(chr(0), '', $uploadData['name'][$this->fieldname]); $type = $uploadData['type'][$this->fieldname]; $tmpName = $uploadData['tmp_name'][$this->fieldname]; $error = $uploadData['error'][$this->fieldname]; $size = $uploadData['size'][$this->fieldname]; if ($filename !== null && $filename !== '' && GeneralUtility::validPathStr($filename)) { if ($this->settings['useEncryptedFilename']) { $filenameParts = GeneralUtility::trimExplode('.', $filename); $extension = array_pop($filenameParts); $filename = md5(mktime() . mt_rand() . $filename . $tmpName . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']) . '.' . $extension; } $fileData = array('filename' => $filename, 'type' => $type, 'tmp_name' => $tmpName, 'error' => $error, 'size' => $size); } } return $fileData; }
/** * Wrapper for \TYPO3\CMS\Core\Utility\GeneralUtility::validPathStr() * * @param string $theFile Filepath to evaluate * @return bool TRUE if no '/', '..' or '\' is in the $theFile * @see \TYPO3\CMS\Core\Utility\GeneralUtility::validPathStr() */ protected function isPathValid($theFile) { return GeneralUtility::validPathStr($theFile); }
/** * Cleans $theDir for slashes in the end of the string and returns the new path, if it exists on the server. * * @param string $theDir Absolute path to directory * @return string The directory path if it existed as was valid to access. * @access private * @see filelist() */ public function clean_directory($theDir) { // proceeds if no '//', '..' or '\' is in the $theFile if (GeneralUtility::validPathStr($theDir)) { // Removes all dots, slashes and spaces after a path... $theDir = preg_replace('/[\\/\\. ]*$/', '', $theDir); if (!GeneralUtility::isAbsPath($theDir) && @is_dir($theDir)) { return $theDir; } } return ''; }
/** * Test if a value is a valid and existing directory * * @param string value * @param array (optional) additional info, will be displayed as debug message, if a key "message" exists this will be appended to the error message * @throws Tx_PtExtbase_Exception_Assertion if assertion fails */ public static function isDir($val, array $info = array()) { self::isNotEmptyString($val, $info); $filePath = GeneralUtility::getFileAbsFileName($val, false); return self::test(GeneralUtility::validPathStr($filePath) && is_dir($filePath), true, $info); }
/** * Evaluate the environment for editing a staticFileEdit file. * Called for almost all fields being saved in the database. Is called without * an instance of \TYPO3\CMS\Core\Html\RteHtmlParser::evalWriteFile() * * @param array $pArr Parameters for the current field as found in types-config * @param array $currentRecord Current record we are editing. * @return mixed On success an array with various information is returned, otherwise a string with an error message */ public static function evalWriteFile($pArr, $currentRecord) { // Write file configuration: if (is_array($pArr)) { if ($GLOBALS['TYPO3_CONF_VARS']['BE']['staticFileEditPath'] && substr($GLOBALS['TYPO3_CONF_VARS']['BE']['staticFileEditPath'], -1) == '/' && @is_dir(PATH_site . $GLOBALS['TYPO3_CONF_VARS']['BE']['staticFileEditPath'])) { $SW_p = $pArr['parameters']; $SW_editFileField = trim($SW_p[0]); $SW_editFile = $currentRecord[$SW_editFileField]; if ($SW_editFileField && $SW_editFile && GeneralUtility::validPathStr($SW_editFile)) { $SW_relpath = $GLOBALS['TYPO3_CONF_VARS']['BE']['staticFileEditPath'] . $SW_editFile; $SW_editFile = PATH_site . $SW_relpath; if (@is_file($SW_editFile)) { return array('editFile' => $SW_editFile, 'relEditFile' => $SW_relpath, 'contentField' => trim($SW_p[1]), 'markerField' => trim($SW_p[2]), 'loadFromFileField' => trim($SW_p[3]), 'statusField' => trim($SW_p[4])); } else { return 'ERROR: Editfile \'' . $SW_relpath . '\' did not exist'; } } else { return 'ERROR: Edit file name could not be found or was bad.'; } } else { return 'ERROR: staticFileEditPath was not set, not set correctly or did not exist!'; } } }
/** * Wrapper for t3lib_div::validPathStr() * * @param string Filepath to evaluate * @return boolean TRUE, if no '//', '..' or '\' is in the $theFile * @see t3lib_div::validPathStr() * @todo Define visibility */ public function isPathValid($theFile) { // @todo: should go into the LocalDriver in a protected way (not important to the outside world) return \TYPO3\CMS\Core\Utility\GeneralUtility::validPathStr($theFile); }
/** * Adds a filemount to the users array of filemounts, $this->groupData['filemounts'][hash_key] = Array ('name'=>$name, 'path'=>$path, 'type'=>$type); * Is a part of the authentication proces of the user. * A final requirement for a path being mounted is that a) it MUST return TRUE on is_dir(), b) must contain either PATH_site+'fileadminDir' OR 'lockRootPath' - if lockRootPath is set - as first part of string! * Paths in the mounted information will always be absolute and have a trailing slash. * * @param string $title Will be the (root)name of the filemount in the folder tree * @param string $altTitle Will be the (root)name of the filemount IF $title is not TRUE (blank or zero) * @param string $path Is the path which should be mounted. Will accept backslash in paths on windows servers (will substituted with forward slash). The path should be 1) relative to TYPO3_CONF_VARS[BE][fileadminDir] if $webspace is set, otherwise absolute. * @param boolean $webspace If $webspace is set, the $path is relative to 'fileadminDir' in TYPO3_CONF_VARS, otherwise $path is absolute. 'fileadminDir' must be set to allow mounting of relative paths. * @param string $type Type of filemount; Can be blank (regular) or "user" / "group" (for user and group filemounts presumably). Probably sets the icon first and foremost. * @return boolean Returns "1" if the requested filemount was mounted, otherwise no return value. * @deprecated since TYPO3 6.0, will be removed in TYPO3 6.1, all data is stored in $this->fileStorages now, see initializeFileStorages() * @access private * @todo Define visibility */ public function addFileMount($title, $altTitle, $path, $webspace, $type) { \TYPO3\CMS\Core\Utility\GeneralUtility::logDeprecatedFunction(); // Return FALSE if fileadminDir is not set and we try to mount a relative path if ($webspace && !$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']) { return FALSE; } // Trimming and pre-processing $path = trim($path); // with WINDOWS convert backslash to slash!! if ($this->OS == 'WIN') { $path = str_replace('\\', '/', $path); } // If the path is TRUE and validates as a valid path string: if ($path && \TYPO3\CMS\Core\Utility\GeneralUtility::validPathStr($path)) { // normalize path: remove leading '/' and './', and trailing '/' and '/.' $path = trim($path); $path = preg_replace('#^\\.?/|/\\.?$#', '', $path); // There must be some chars in the path if ($path) { // Fileadmin dir, absolute $fdir = PATH_site . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']; if ($webspace) { $path = $fdir . $path; } else { // With WINDOWS no prepending!! if ($this->OS != 'WIN') { // With WINDOWS no prepending!! $path = '/' . $path; } } $path .= '/'; // We now have a path with slash after and slash before (if unix) if (@is_dir($path) && ($GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'] && \TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($path, $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath']) || \TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($path, $fdir))) { // Alternative title? $name = $title ? $title : $altTitle; // Adds the filemount. The same filemount with same name, type and path cannot be set up twice because of the hash string used as key. $this->groupData['filemounts'][md5($name . '|' . $path . '|' . $type)] = array('name' => $name, 'path' => $path, 'type' => $type); // Return TRUE - went well, success! return 1; } } } }
/** * Check extension of given filename * * @param string $filename Filename like (upload.png) * @return bool If Extension is allowed */ public static function checkExtension($filename) { $extensionList = 'jpg,jpeg,png,gif,bmp'; if (!empty($GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_femanager.']['settings.']['misc.']['uploadFileExtension'])) { $extensionList = $GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_femanager.']['settings.']['misc.']['uploadFileExtension']; $extensionList = str_replace(' ', '', $extensionList); } $fileInfo = pathinfo($filename); if (!empty($fileInfo['extension']) && GeneralUtility::inList($extensionList, strtolower($fileInfo['extension'])) && GeneralUtility::verifyFilenameAgainstDenyPattern($filename) && GeneralUtility::validPathStr($filename)) { return TRUE; } return FALSE; }
/** * Cleans $theDir for slashes in the end of the string and returns the new path, if it exists on the server. * * @param string $theDir Directory path to check * @return bool|string Returns the cleaned up directory name if OK, otherwise FALSE. */ protected function is_directory($theDir) { // @todo: should go into the LocalDriver in a protected way (not important to the outside world) if (GeneralUtility::validPathStr($theDir)) { $theDir = PathUtility::getCanonicalPath($theDir); if (@is_dir($theDir)) { return $theDir; } } return false; }
/** * Goes back in the path and checks in each directory if a folder named $this->recyclerFN (usually '_recycler_') is present. * If a folder in the tree happens to be a _recycler_-folder (which means that we're deleting something inside a _recycler_-folder) this is ignored * * @param string $theFile Takes a valid Path ($theFile) * @return string Returns the path (without trailing slash) of the closest recycle-folder if found. Else FALSE. * @todo To be put in Storage with a better concept * @todo Define visibility * @deprecated since TYPO3 6.0, use \TYPO3\CMS\Core\Resource\ResourceStorage method instead */ public function findRecycler($theFile) { GeneralUtility::logDeprecatedFunction(); if (GeneralUtility::validPathStr($theFile)) { $theFile = \TYPO3\CMS\Core\Utility\PathUtility::getCanonicalPath($theFile); $fI = GeneralUtility::split_fileref($theFile); $c = 0; // !!! Method has been put in the storage, can be saftely removed $rDir = $fI['path'] . $this->recyclerFN; while ($this->checkPathAgainstMounts($fI['path']) && $c < 20) { if (@is_dir($rDir) && $this->recyclerFN != $fI['file']) { return $rDir; } $theFile = $fI['path']; $theFile = \TYPO3\CMS\Core\Utility\PathUtility::getCanonicalPath($theFile); $fI = GeneralUtility::split_fileref($theFile); $c++; } } }
/** * Editing files in typo3conf directory (or elsewhere if enabled) * * @return void * @todo Define visibility */ public function typo3conf_edit() { // default: $EDIT_path = PATH_typo3conf; if ($this->allowFileEditOutsite_typo3conf_dir && $this->INSTALL['FILE']['EDIT_path']) { if (\TYPO3\CMS\Core\Utility\GeneralUtility::validPathStr($this->INSTALL['FILE']['EDIT_path']) && substr($this->INSTALL['FILE']['EDIT_path'], -1) == '/') { $tmp_path = PATH_site . $this->INSTALL['FILE']['EDIT_path']; if (is_dir($tmp_path)) { $EDIT_path = $tmp_path; } else { $this->errorMessages[] = ' \'' . $tmp_path . '\' was not directory '; } } else { $this->errorMessages[] = ' Bad directory name (must be like t3lib/ or media/script/) '; } } $headCode = 'Edit files in ' . basename($EDIT_path) . '/'; $messages = ''; if ($this->INSTALL['SAVE_FILE']) { $save_to_file = $this->INSTALL['FILE']['name']; if (@is_file($save_to_file)) { $save_to_file_md5 = md5($save_to_file); if (isset($this->INSTALL['FILE'][$save_to_file_md5]) && \TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($save_to_file, $EDIT_path . '') && substr($save_to_file, -1) != '~' && !strstr($save_to_file, '_bak')) { $this->INSTALL['typo3conf_files'] = $save_to_file; $save_fileContent = $this->INSTALL['FILE'][$save_to_file_md5]; if ($this->INSTALL['FILE']['win_to_unix_br']) { $save_fileContent = str_replace(CRLF, LF, $save_fileContent); } $backupFile = $this->getBackupFilename($save_to_file); if ($this->INSTALL['FILE']['backup']) { if (@is_file($backupFile)) { unlink($backupFile); } rename($save_to_file, $backupFile); $messages .= ' Backup written to <strong>' . $backupFile . '</strong> <br /> '; } \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile($save_to_file, $save_fileContent); $messages .= ' File saved: <strong>' . $save_to_file . '</strong> <br /> MD5-sum: ' . $this->INSTALL['FILE']['prevMD5'] . ' (prev) <br /> MD5-sum: ' . md5($save_fileContent) . ' (new) <br /> '; } } } // Filelist: // Get the template file $templateFile = @file_get_contents(PATH_site . $this->templateFilePath . 'Typo3ConfEdit.html'); // Get the template part from the file $template = \TYPO3\CMS\Core\Html\HtmlParser::getSubpart($templateFile, '###TEMPLATE###'); // Get the subpart for the files $filesSubpart = \TYPO3\CMS\Core\Html\HtmlParser::getSubpart($template, '###FILES###'); $files = array(); $typo3conf_files = \TYPO3\CMS\Core\Utility\GeneralUtility::getFilesInDir($EDIT_path, '', 1, 1); $fileFound = 0; foreach ($typo3conf_files as $k => $file) { // Delete temp_CACHED files if option is set if ($this->INSTALL['delTempCached'] && preg_match('|/temp_CACHED_[a-z0-9_]+\\.php|', $file)) { unlink($file); continue; } if ($this->INSTALL['typo3conf_files'] && !strcmp($this->INSTALL['typo3conf_files'], $file)) { $fileFound = 1; } // Define the markers content for the files subpart $filesMarkers = array('editUrl' => $this->action . '&TYPO3_INSTALL[typo3conf_files]=' . rawurlencode($file) . ($this->allowFileEditOutsite_typo3conf_dir ? '&TYPO3_INSTALL[FILE][EDIT_path]=' . rawurlencode($this->INSTALL['FILE']['EDIT_path']) : '') . '#confEditFileList', 'fileName' => basename($file), 'fileSize' => \TYPO3\CMS\Core\Utility\GeneralUtility::formatSize(filesize($file)), 'class' => $this->INSTALL['typo3conf_files'] && !strcmp($this->INSTALL['typo3conf_files'], $file) ? 'class="act"' : ''); // Fill the markers in the subpart $files[] = \TYPO3\CMS\Core\Html\HtmlParser::substituteMarkerArray($filesSubpart, $filesMarkers, '###|###', TRUE, FALSE); } if ($fileFound && @is_file($this->INSTALL['typo3conf_files'])) { $backupFile = $this->getBackupFilename($this->INSTALL['typo3conf_files']); $fileContent = \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl($this->INSTALL['typo3conf_files']); // Get the subpart to edit the files $fileEditTemplate = \TYPO3\CMS\Core\Html\HtmlParser::getSubpart($template, '###FILEEDIT###'); $allowFileEditOutsideTypo3ConfDirSubPart = ''; if (substr($this->INSTALL['typo3conf_files'], -1) != '~' && !strstr($this->INSTALL['typo3conf_files'], '_bak')) { // Get the subpart to show the save button $showSaveButtonSubPart = \TYPO3\CMS\Core\Html\HtmlParser::getSubpart($fileEditTemplate, '###SHOWSAVEBUTTON###'); } if ($this->allowFileEditOutsite_typo3conf_dir) { // Get the subpart to show if files are allowed outside the directory typo3conf $allowFileEditOutsideTypo3ConfDirSubPart = \TYPO3\CMS\Core\Html\HtmlParser::getSubpart($fileEditTemplate, '###ALLOWFILEEDITOUTSIDETYPO3CONFDIR###'); } // Substitute the subpart for the save button $fileEditContent = \TYPO3\CMS\Core\Html\HtmlParser::substituteSubpart($fileEditTemplate, '###SHOWSAVEBUTTON###', $showSaveButtonSubPart); // Substitute the subpart to show if files are allowed outside the directory typo3conf $fileEditContent = \TYPO3\CMS\Core\Html\HtmlParser::substituteSubpart($fileEditContent, '###ALLOWFILEEDITOUTSIDETYPO3CONFDIR###', $allowFileEditOutsideTypo3ConfDirSubPart); // Define the markers content for subpart to edit the files $fileEditMarkers = array('messages' => !empty($messages) ? '<p class="typo3-message message-warning">' . $messages . '</p>' : '', 'action' => $this->action . '#fileEditHeader', 'saveFile' => 'Save file', 'close' => 'Close', 'llEditing' => 'Editing file:', 'file' => $this->INSTALL['typo3conf_files'], 'md5Sum' => 'MD5-sum: ' . md5($fileContent), 'fileName' => $this->INSTALL['typo3conf_files'], 'fileEditPath' => $this->INSTALL['FILE']['EDIT_path'], 'filePreviousMd5' => md5($fileContent), 'fileMd5' => md5($this->INSTALL['typo3conf_files']), 'fileContent' => \TYPO3\CMS\Core\Utility\GeneralUtility::formatForTextarea($fileContent), 'winToUnixBrChecked' => TYPO3_OS == 'WIN' ? '' : 'checked="checked"', 'winToUnixBr' => 'Convert Windows linebreaks (13-10) to Unix (10)', 'backupChecked' => @is_file($backupFile) ? 'checked="checked"' : '', 'backup' => 'Make backup copy (rename to ' . basename($backupFile) . ')'); // Fill the markers in the subpart to edit the files $fileEditContent = \TYPO3\CMS\Core\Html\HtmlParser::substituteMarkerArray($fileEditContent, $fileEditMarkers, '###|###', TRUE, FALSE); } if ($this->allowFileEditOutsite_typo3conf_dir) { // Get the subpart to show if files are allowed outside the directory typo3conf $allowFileEditOutsideTypo3ConfDirSubPart = \TYPO3\CMS\Core\Html\HtmlParser::getSubpart($template, '###ALLOWFILEEDITOUTSIDETYPO3CONFDIR###'); // Define the markers content $allowFileEditOutsideTypo3ConfDirMarkers = array('action' => $this->action, 'pathSite' => PATH_site, 'editPath' => $this->INSTALL['FILE']['EDIT_path'], 'set' => 'Set'); // Fill the markers in the subpart $allowFileEditOutsideTypo3ConfDirSubPart = \TYPO3\CMS\Core\Html\HtmlParser::substituteMarkerArray($allowFileEditOutsideTypo3ConfDirSubPart, $allowFileEditOutsideTypo3ConfDirMarkers, '###|###', TRUE, FALSE); } // Substitute the subpart to edit the file $fileListContent = \TYPO3\CMS\Core\Html\HtmlParser::substituteSubpart($template, '###FILEEDIT###', $fileEditContent); // Substitute the subpart when files can be edited outside typo3conf directory $fileListContent = \TYPO3\CMS\Core\Html\HtmlParser::substituteSubpart($fileListContent, '###ALLOWFILEEDITOUTSIDETYPO3CONFDIR###', $allowFileEditOutsideTypo3ConfDirSubPart); // Substitute the subpart for the files $fileListContent = \TYPO3\CMS\Core\Html\HtmlParser::substituteSubpart($fileListContent, '###FILES###', implode(LF, $files)); // Define the markers content $fileListMarkers = array('editPath' => '(' . $EDIT_path . ')', 'deleteTempCachedUrl' => $this->action . '&TYPO3_INSTALL[delTempCached]=1', 'deleteTempCached' => 'Delete temp_CACHED* files'); // Fill the markers $fileListContent = \TYPO3\CMS\Core\Html\HtmlParser::substituteMarkerArray($fileListContent, $fileListMarkers, '###|###', TRUE, FALSE); // Add the content to the message array $this->message($headCode, 'Files in folder', $fileListContent); // Output the page $this->output($this->outputWrapper($this->printAll())); }
/** * * @param \EBT\ExtensionBuilder\Domain\Model\Extension $extension * @param string $backupDir * * @return void */ static function backupExtension(Model\Extension $extension, $backupDir) { if (empty($backupDir)) { throw new \Exception('Please define a backup directory in extension configuration!'); } elseif (!GeneralUtility::validPathStr($backupDir)) { throw new \Exception('Backup directory is not a valid path: ' . $backupDir); } elseif (GeneralUtility::isAbsPath($backupDir)) { if (!GeneralUtility::isAllowedAbsPath($backupDir)) { throw new \Exception('Backup directory is not an allowed absolute path: ' . $backupDir); } } else { $backupDir = PATH_site . $backupDir; } if (strrpos($backupDir, '/') < strlen($backupDir) - 1) { $backupDir .= '/'; } if (!is_dir($backupDir)) { throw new \Exception('Backup directory does not exist: ' . $backupDir); } elseif (!is_writable($backupDir)) { throw new \Exception('Backup directory is not writable: ' . $backupDir); } $backupDir .= $extension->getExtensionKey(); // create a subdirectory for this extension if (!is_dir($backupDir)) { GeneralUtility::mkdir($backupDir); } if (strrpos($backupDir, '/') < strlen($backupDir) - 1) { $backupDir .= '/'; } $backupDir .= date('Y-m-d-') . time(); if (!is_dir($backupDir)) { GeneralUtility::mkdir($backupDir); } $extensionDir = substr($extension->getExtensionDir(), 0, strlen($extension->getExtensionDir()) - 1); try { self::recurse_copy($extensionDir, $backupDir); } catch (\Exception $e) { throw new \Exception('Code generation aborted:' . $e->getMessage()); } self::log('Backup created in ' . $backupDir); }
/** * Wrapper for t3lib_div::validPathStr() * * @param string $theFile Filepath to evaluate * @return boolean TRUE if no '/', '..' or '\' is in the $theFile * @see t3lib_div::validPathStr() */ protected function isPathValid($theFile) { return \TYPO3\CMS\Core\Utility\GeneralUtility::validPathStr($theFile); }
/** * Makes sure the Path given as parameter is valid * * @param string $filePath The file path (including the file name!) * @return string * @throws \TYPO3\CMS\Core\Resource\Exception\InvalidPathException */ protected function canonicalizeAndCheckFilePath($filePath) { $filePath = PathUtility::getCanonicalPath($filePath); // filePath must be valid // Special case is required by vfsStream in Unit Test context if (!GeneralUtility::validPathStr($filePath)) { throw new \TYPO3\CMS\Core\Resource\Exception\InvalidPathException('File ' . $filePath . ' is not valid (".." and "//" is not allowed in path).', 1320286857); } return $filePath; }
/** * Init function, setting the input vars in the global space. * * @return void * @todo Define visibility */ public function init() { // Loading internal vars with the GET/POST parameters from outside: $this->file = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('file'); $parametersArray = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('parameters'); $this->frame = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('frame'); $this->md5 = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('md5'); // Check parameters // If no file-param or parameters are given, we must exit if (!$this->file || !isset($parametersArray) || !is_array($parametersArray)) { \TYPO3\CMS\Core\Utility\HttpUtility::setResponseCodeAndExit(\TYPO3\CMS\Core\Utility\HttpUtility::HTTP_STATUS_410); } $this->parametersEncoded = implode('', $parametersArray); // Chech md5-checksum: If this md5-value does not match the one submitted, then we fail... (this is a kind of security that somebody don't just hit the script with a lot of different parameters $md5_value = \TYPO3\CMS\Core\Utility\GeneralUtility::hmac(implode('|', array($this->file, $this->parametersEncoded))); if ($md5_value !== $this->md5) { \TYPO3\CMS\Core\Utility\HttpUtility::setResponseCodeAndExit(\TYPO3\CMS\Core\Utility\HttpUtility::HTTP_STATUS_410); } $parameters = unserialize(base64_decode($this->parametersEncoded)); foreach ($parameters as $parameterName => $parameterValue) { $this->{$parameterName} = $parameterValue; } // Check the file. If must be in a directory beneath the dir of this script... // $this->file remains unchanged, because of the code in stdgraphic, but we do check if the file exists within the current path $test_file = PATH_site . $this->file; if (!\TYPO3\CMS\Core\Utility\GeneralUtility::validPathStr($test_file)) { \TYPO3\CMS\Core\Utility\HttpUtility::setResponseCodeAndExit(\TYPO3\CMS\Core\Utility\HttpUtility::HTTP_STATUS_410); } if (!@is_file($test_file)) { \TYPO3\CMS\Core\Utility\HttpUtility::setResponseCodeAndExit(\TYPO3\CMS\Core\Utility\HttpUtility::HTTP_STATUS_404); } }
/** * Is file-extension allowed for uploading? * * @param string $filename Filename like (upload_03.txt) * @param string $fileExtensions allowed file extensions * @return bool */ public static function checkExtension($filename, $fileExtensions = '') { $fileInfo = pathinfo($filename); if (!empty($fileInfo['extension']) && !empty($fileExtensions) && GeneralUtility::inList($fileExtensions, $fileInfo['extension']) && GeneralUtility::verifyFilenameAgainstDenyPattern($filename) && GeneralUtility::validPathStr($filename)) { return true; } return false; }
/** * This function initializes all UTF-8 character data tables. * * PLEASE SEE: http://www.unicode.org/Public/UNIDATA/ * * @param string $mode Mode ("case", "ascii", ...) * @return int Returns FALSE on error, a TRUE value on success: 1 table already loaded, 2, cached version, 3 table parsed (and cached). * @access private */ public function initUnicodeData($mode = NULL) { // Cache files $cacheFileCase = GeneralUtility::getFileAbsFileName('typo3temp/cs/cscase_utf-8.tbl'); $cacheFileASCII = GeneralUtility::getFileAbsFileName('typo3temp/cs/csascii_utf-8.tbl'); // Only process if the tables are not yet loaded switch ($mode) { case 'case': if (is_array($this->caseFolding['utf-8'])) { return 1; } // Use cached version if possible if ($cacheFileCase && @is_file($cacheFileCase)) { $this->caseFolding['utf-8'] = unserialize(GeneralUtility::getUrl($cacheFileCase)); return 2; } break; case 'ascii': if (is_array($this->toASCII['utf-8'])) { return 1; } // Use cached version if possible if ($cacheFileASCII && @is_file($cacheFileASCII)) { $this->toASCII['utf-8'] = unserialize(GeneralUtility::getUrl($cacheFileASCII)); return 2; } break; } // Process main Unicode data file $unicodeDataFile = ExtensionManagementUtility::extPath('core') . 'Resources/Private/Charsets/unidata/UnicodeData.txt'; if (!(GeneralUtility::validPathStr($unicodeDataFile) && @is_file($unicodeDataFile))) { return FALSE; } $fh = fopen($unicodeDataFile, 'rb'); if (!$fh) { return FALSE; } // key = utf8 char (single codepoint), value = utf8 string (codepoint sequence) // Note: we use the UTF-8 characters here and not the Unicode numbers to avoid conversion roundtrip in utf8_strtolower/-upper) $this->caseFolding['utf-8'] = array(); $utf8CaseFolding =& $this->caseFolding['utf-8']; // a shorthand $utf8CaseFolding['toUpper'] = array(); $utf8CaseFolding['toLower'] = array(); $utf8CaseFolding['toTitle'] = array(); // Array of temp. decompositions $decomposition = array(); // Array of chars that are marks (eg. composing accents) $mark = array(); // Array of chars that are numbers (eg. digits) $number = array(); // Array of chars to be omitted (eg. Russian hard sign) $omit = array(); while (!feof($fh)) { $line = fgets($fh, 4096); // Has a lot of info list($char, $name, $cat, , , $decomp, , , $num, , , , $upper, $lower, $title, ) = explode(';', rtrim($line)); $ord = hexdec($char); if ($ord > 65535) { // Only process the BMP break; } $utf8_char = $this->UnumberToChar($ord); if ($upper) { $utf8CaseFolding['toUpper'][$utf8_char] = $this->UnumberToChar(hexdec($upper)); } if ($lower) { $utf8CaseFolding['toLower'][$utf8_char] = $this->UnumberToChar(hexdec($lower)); } // Store "title" only when different from "upper" (only a few) if ($title && $title !== $upper) { $utf8CaseFolding['toTitle'][$utf8_char] = $this->UnumberToChar(hexdec($title)); } switch ($cat[0]) { case 'M': // mark (accent, umlaut, ...) $mark['U+' . $char] = 1; break; case 'N': // numeric value if ($ord > 128 && $num !== '') { $number['U+' . $char] = $num; } } // Accented Latin letters without "official" decomposition $match = array(); if (preg_match('/^LATIN (SMALL|CAPITAL) LETTER ([A-Z]) WITH/', $name, $match) && !$decomp) { $c = ord($match[2]); if ($match[1] === 'SMALL') { $c += 32; } $decomposition['U+' . $char] = array(dechex($c)); continue; } $match = array(); if (preg_match('/(<.*>)? *(.+)/', $decomp, $match)) { switch ($match[1]) { case '<circle>': // add parenthesis as circle replacement, eg (1) $match[2] = '0028 ' . $match[2] . ' 0029'; break; case '<square>': // add square brackets as square replacement, eg [1] $match[2] = '005B ' . $match[2] . ' 005D'; break; case '<compat>': // ignore multi char decompositions that start with a space if (preg_match('/^0020 /', $match[2])) { continue 2; } break; case '<initial>': case '<medial>': case '<final>': case '<isolated>': case '<vertical>': continue 2; } $decomposition['U+' . $char] = explode(' ', $match[2]); } } fclose($fh); // Process additional Unicode data for casing (allow folded characters to expand into a sequence) $specialCasingFile = ExtensionManagementUtility::extPath('core') . 'Resources/Private/Charsets/unidata/SpecialCasing.txt'; if (GeneralUtility::validPathStr($specialCasingFile) && @is_file($specialCasingFile)) { $fh = fopen($specialCasingFile, 'rb'); if ($fh) { while (!feof($fh)) { $line = fgets($fh, 4096); if ($line[0] !== '#' && trim($line) !== '') { list($char, $lower, $title, $upper, $cond) = GeneralUtility::trimExplode(';', $line); if ($cond === '' || $cond[0] === '#') { $utf8_char = $this->UnumberToChar(hexdec($char)); if ($char !== $lower) { $arr = explode(' ', $lower); for ($i = 0; isset($arr[$i]); $i++) { $arr[$i] = $this->UnumberToChar(hexdec($arr[$i])); } $utf8CaseFolding['toLower'][$utf8_char] = implode('', $arr); } if ($char !== $title && $title !== $upper) { $arr = explode(' ', $title); for ($i = 0; isset($arr[$i]); $i++) { $arr[$i] = $this->UnumberToChar(hexdec($arr[$i])); } $utf8CaseFolding['toTitle'][$utf8_char] = implode('', $arr); } if ($char !== $upper) { $arr = explode(' ', $upper); for ($i = 0; isset($arr[$i]); $i++) { $arr[$i] = $this->UnumberToChar(hexdec($arr[$i])); } $utf8CaseFolding['toUpper'][$utf8_char] = implode('', $arr); } } } } fclose($fh); } } // Process custom decompositions $customTranslitFile = ExtensionManagementUtility::extPath('core') . 'Resources/Private/Charsets/unidata/Translit.txt'; if (GeneralUtility::validPathStr($customTranslitFile) && @is_file($customTranslitFile)) { $fh = fopen($customTranslitFile, 'rb'); if ($fh) { while (!feof($fh)) { $line = fgets($fh, 4096); if ($line[0] !== '#' && trim($line) !== '') { list($char, $translit) = GeneralUtility::trimExplode(';', $line); if (!$translit) { $omit['U+' . $char] = 1; } $decomposition['U+' . $char] = explode(' ', $translit); } } fclose($fh); } } // Decompose and remove marks; inspired by unac (Loic Dachary <*****@*****.**>) foreach ($decomposition as $from => $to) { $code_decomp = array(); while ($code_value = array_shift($to)) { // Do recursive decomposition if (isset($decomposition['U+' . $code_value])) { foreach (array_reverse($decomposition['U+' . $code_value]) as $cv) { array_unshift($to, $cv); } } elseif (!isset($mark['U+' . $code_value])) { // remove mark array_push($code_decomp, $code_value); } } if (!empty($code_decomp) || isset($omit[$from])) { $decomposition[$from] = $code_decomp; } else { unset($decomposition[$from]); } } // Create ascii only mapping $this->toASCII['utf-8'] = array(); $ascii =& $this->toASCII['utf-8']; foreach ($decomposition as $from => $to) { $code_decomp = array(); while ($code_value = array_shift($to)) { $ord = hexdec($code_value); if ($ord > 127) { continue 2; } else { // Skip decompositions containing non-ASCII chars array_push($code_decomp, chr($ord)); } } $ascii[$this->UnumberToChar(hexdec($from))] = join('', $code_decomp); } // Add numeric decompositions foreach ($number as $from => $to) { $utf8_char = $this->UnumberToChar(hexdec($from)); if (!isset($ascii[$utf8_char])) { $ascii[$utf8_char] = $to; } } if ($cacheFileCase) { GeneralUtility::writeFileToTypo3tempDir($cacheFileCase, serialize($utf8CaseFolding)); } if ($cacheFileASCII) { GeneralUtility::writeFileToTypo3tempDir($cacheFileASCII, serialize($ascii)); } return 3; }
/** * Garbage collection: deactivates aged out sessions and deletes no longer needed session data * * @param integer $garbageCollectionTriggerTimer : ... * @param integer $rollbackSafetyTimespan : ... * @return void */ function gc($garbageCollectionTriggerTimer, $rollbackSafetyTimespan) { $now = mktime(); if (TYPO3_DLOG) { GeneralUtility::devLog('Entering garbage collection routine at ' . strftime("%d.%m.%Y - %H:%M:%S", $now), 'rs_userimp', -1); } if ($garbageCollectionTriggerTimer == 0) { GeneralUtility::devLog('TriggerTimer disabled, exiting garbage collection', 'rs_userimp', 1); GeneralUtility::devLog('Leaving garbage collection routine at ' . strftime("%d.%m.%Y - %H:%M:%S", mktime()), 'rs_userimp', -1); return; } else { $garbageCollectionTriggerTimer = $garbageCollectionTriggerTimer * 24 * 60 * 60; //time in days } $rollbackData = $this->getRollbackDataSets(); if (!empty($rollbackData)) { foreach ($rollbackData as $session) { if (TYPO3_DLOG) { GeneralUtility::devLog('Session ' . $session['uid'] . ' created ' . strftime("%d.%m.%Y - %H:%M:%S", $session['crdate']), 'rs_userimp', -1); } if ($rollbackSafetyTimespan == 0) { if (TYPO3_DLOG) { GeneralUtility::devLog('Rollback safety timer disabled', 'rs_userimp', -1); } } else { $ageOutTime = $session['crdate'] + 60 * $rollbackSafetyTimespan; if (TYPO3_DLOG) { GeneralUtility::devLog('Age out timer for session ' . $session['uid'] . ' set to ' . strftime("%d.%m.%Y - %H:%M:%S", $ageOutTime), 'rs_userimp', -1); GeneralUtility::devLog('Delete timer for session ' . $session['uid'] . ' set to ' . strftime("%d.%m.%Y - %H:%M:%S", $garbageCollectionTriggerTimer + $session['crdate']), 'rs_userimp', -1); } } if ($ageOutTime < $now && $session['active']) { $GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_rsuserimp_sessions', 'uid=' . $session['uid'], array('active' => '0')); GeneralUtility::devLog('Inactivated aged out session ' . $session['uid'], 'rs_userimp', 1); } } } $result = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,crdate,dropfile', 'tx_rsuserimp_sessions', 'user_uid=' . $this->getBackendUserAuthentication()->user['uid'] . ' AND active=0', 'uid DESC', ''); if (!empty($result)) { foreach ($result as $session) { if ($session['crdate'] + $garbageCollectionTriggerTimer <= $now) { $GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_rsuserimp_sessions', 'uid=' . $session['uid'], array('deleted' => '1')); GeneralUtility::devLog('Deleted aged out session ' . $session['uid'], 'rs_userimp', 1); if ($session['dropfile'] && GeneralUtility::validPathStr($session['dropfile']) && GeneralUtility::isFirstPartOfStr($session['dropfile'], PATH_site . 'uploads/tx_rsuserimp/')) { // unlink (delete) associated drop file if (is_file($session['dropfile']) && @unlink($session['dropfile'])) { GeneralUtility::devLog('Deleted drop file ' . $session['dropfile'], 'rs_userimp', 1); } else { GeneralUtility::devLog('Unable to delete drop file ' . $session['dropfile'] . ' (file not found?).', 'rs_userimp', 3); } } } } } if (TYPO3_DLOG) { GeneralUtility::devLog('Leaving garbage collection routine at ' . strftime("%d.%m.%Y - %H:%M:%S", mktime()), 'rs_userimp', -1); } return; }
/** * Tests whether Unicode characters are recognized as valid file name characters. * * @test */ public function validPathStrWorksWithUnicodeFileNames() { $this->assertTrue(Utility\GeneralUtility::validPathStr('fileadmin/templates/Ссылка (fce).xml')); }
/** * For testing we must allow vfs:// as first part of file path * * @param string $theFile File path to evaluate * @return bool TRUE, $theFile is allowed path string, FALSE otherwise */ public static function validPathStr($theFile) { return self::isFirstPartOfStr($theFile, 'vfs://') || parent::validPathStr($theFile); }