Commit b3f93726 authored by Jigal van Hemert's avatar Jigal van Hemert Committed by Susanne Moog
Browse files

[TASK] Use PathUtility wrappers for basename/dirname

Basename and dirname are locale aware and can damage path and file names
if they are used with UTF-8 file/path names while a UTF-8 locale is not
loaded.
The PathUtility wrappers solve this.

Releases: master
Resolves: #74806
Change-Id: Ibe113a1632b4e61ea87ed9a7dc8600bf3535acab
Reviewed-on: https://review.typo3.org/47169


Reviewed-by: default avatarMathias Schreiber <mathias.schreiber@typo3.com>
Tested-by: default avatarMathias Schreiber <mathias.schreiber@typo3.com>
Tested-by: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: Susanne Moog's avatarSusanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog's avatarSusanne Moog <susanne.moog@typo3.org>
parent 5519649d
......@@ -30,7 +30,7 @@ class PathUtility
*/
public static function getRelativePathTo($targetPath)
{
return self::getRelativePath(dirname(Environment::getCurrentScript()), $targetPath);
return self::getRelativePath(self::dirname(Environment::getCurrentScript()), $targetPath);
}
/**
......@@ -52,7 +52,7 @@ class PathUtility
return $targetPath;
} else {
// Make an absolute path out of it
$targetPath = GeneralUtility::resolveBackPath(dirname(Environment::getCurrentScript()) . '/' . $targetPath);
$targetPath = GeneralUtility::resolveBackPath(self::dirname(Environment::getCurrentScript()) . '/' . $targetPath);
$targetPath = self::stripPathSitePrefix($targetPath);
if (!(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI)) {
$targetPath = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH') . $targetPath;
......@@ -262,6 +262,36 @@ class PathUtility
return $result;
}
/**
* Returns parent directory's path
* Early during bootstrap there is no TYPO3_CONF_VARS yet so the setting for the system locale
* is also unavailable. The path of the parent directory is determined with a regular expression
* to avoid issues with locales.
*
* @param string $path
*
* @return string
*/
public static function dirnameDuringBootstrap($path): string
{
return preg_replace('#(.*)(/|\\\\)([^\\\\/]+)$#', '$1', $path);
}
/**
* Returns filename part of a path
* Early during bootstrap there is no TYPO3_CONF_VARS yet so the setting for the system locale
* is also unavailable. The filename part is determined with a regular expression to avoid issues
* with locales.
*
* @param string $path
*
* @return string
*/
public static function basenameDuringBootstrap($path): string
{
return preg_replace('#([^\\\\/]+)$#', '$1', $path);
}
/*********************
*
* Cleaning methods
......
......@@ -16,6 +16,7 @@ namespace TYPO3\CMS\Core\Tests\Unit\Resource;
*/
use TYPO3\CMS\Core\Resource\ResourceCompressor;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* Testcase for the ResourceCompressor class
......@@ -395,7 +396,7 @@ class ResourceCompressorTest extends BaseTestCase
// we have to fix relative paths, if we aren't working on a file in our target directory
$relativeFilename = str_replace(PATH_site, '', $cssFile);
if (strpos($relativeFilename, $this->subject->_get('targetDirectory')) === false) {
$compressedCss = $this->subject->_call('cssFixRelativeUrlPaths', $compressedCss, dirname($relativeFilename) . '/');
$compressedCss = $this->subject->_call('cssFixRelativeUrlPaths', $compressedCss, PathUtility::dirname($relativeFilename) . '/');
}
$this->assertEquals(file_get_contents($expected), $compressedCss, 'Group of file CSS assets optimized correctly.');
}
......
......@@ -506,7 +506,7 @@ class FileHandlingUtility implements \TYPO3\CMS\Core\SingletonInterface
public function sendZipFileToBrowserAndDelete($fileName, $downloadName = '')
{
if ($downloadName === '') {
$downloadName = basename($fileName, '.zip');
$downloadName = PathUtility::basename($fileName, '.zip');
}
header('Content-Type: application/zip');
header('Content-Length: ' . filesize($fileName));
......
......@@ -52,6 +52,7 @@ use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\Http\UrlHandlerInterface;
......@@ -4160,7 +4161,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
} else {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
// This is a hack to work around ___FILE___ resolving symbolic links
$PATH_site_real = dirname(realpath(PATH_site . 'typo3')) . '/';
$PATH_site_real = PathUtility::dirname(realpath(PATH_site . 'typo3')) . '/';
$file = $trace[0]['file'];
if (strpos($file, $PATH_site_real) === 0) {
$file = str_replace($PATH_site_real, '', $file);
......
......@@ -21,6 +21,7 @@ use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\File\BasicFileUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
/**
......@@ -189,7 +190,7 @@ class GifBuilder extends GraphicalFunctions
case 'IMAGE':
$fileInfo = $this->getResource($conf['file'], $conf['file.']);
if ($fileInfo) {
$this->combinedFileNames[] = preg_replace('/\\.[[:alnum:]]+$/', '', basename($fileInfo[3]));
$this->combinedFileNames[] = preg_replace('/\\.[[:alnum:]]+$/', '', PathUtility::basename($fileInfo[3]));
if ($fileInfo['processedFile'] instanceof ProcessedFile) {
// Use processed file, if a FAL file has been processed by GIFBUILDER (e.g. scaled/cropped)
$this->setup[$theKey . '.']['file'] = $fileInfo['processedFile']->getForLocalProcessing(false);
......@@ -582,7 +583,7 @@ class GifBuilder extends GraphicalFunctions
}
if ((string)$conf['text'] != '') {
// Char range map thingie:
$fontBaseName = basename($conf['fontFile']);
$fontBaseName = PathUtility::basename($conf['fontFile']);
if (is_array($this->charRangeMap[$fontBaseName])) {
// Initialize splitRendering array:
if (!is_array($conf['splitRendering.'])) {
......
......@@ -25,6 +25,7 @@ use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
......@@ -978,7 +979,7 @@ class AbstractPlugin
}
if ($languageFilePath === '' && $this->scriptRelPath) {
$languageFilePath = 'EXT:' . $this->extKey . '/' . dirname($this->scriptRelPath) . '/locallang.xlf';
$languageFilePath = 'EXT:' . $this->extKey . '/' . PathUtility::dirname($this->scriptRelPath) . '/locallang.xlf';
}
if ($languageFilePath !== '') {
/** @var $languageFactory LocalizationFactory */
......
......@@ -39,6 +39,7 @@ use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\File\ExtendedFileUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\CMS\Impexp\Domain\Repository\PresetRepository;
use TYPO3\CMS\Impexp\Export;
......@@ -532,7 +533,7 @@ class ImportExportController extends BaseScriptClass
$mimeType = 'application/octet-stream';
header('Content-Type: ' . $mimeType);
header('Content-Length: ' . strlen($out));
header('Content-Disposition: attachment; filename=' . basename($dlFile));
header('Content-Disposition: attachment; filename=' . PathUtility::basename($dlFile));
echo $out;
die;
}
......
......@@ -1584,7 +1584,7 @@ class Import extends ImportExport
// Just for security, check again. Should actually not be necessary.
if (!$bypassMountCheck) {
try {
ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier(dirname($fileName));
ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier(PathUtility::dirname($fileName));
} catch (InsufficientFolderAccessPermissionsException $e) {
$this->error('ERROR: Filename "' . $fileName . '" was not allowed in destination path!');
return false;
......
......@@ -19,6 +19,7 @@ use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\CommandUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* External standard parsers for indexed_search
......@@ -521,7 +522,7 @@ class FileContentParser
unset($res);
$content = $this->pObj->convertHTMLToUtf8($content);
$contentArr = $this->pObj->splitHTMLContent($this->removeEndJunk($content));
$contentArr['title'] = basename($absFile);
$contentArr['title'] = PathUtility::basename($absFile);
$this->setLocaleForServerFileSystem(true);
}
break;
......@@ -534,7 +535,7 @@ class FileContentParser
unset($res);
$content = $this->pObj->convertHTMLToUtf8($content);
$contentArr = $this->pObj->splitHTMLContent($this->removeEndJunk($content));
$contentArr['title'] = basename($absFile);
$contentArr['title'] = PathUtility::basename($absFile);
$this->setLocaleForServerFileSystem(true);
}
break;
......@@ -571,7 +572,7 @@ class FileContentParser
$utf8_content = trim(strip_tags(str_replace('<', ' <', $content_xml)));
$contentArr = $this->pObj->splitRegularContent($utf8_content);
// Make sure the title doesn't expose the absolute path!
$contentArr['title'] = basename($absFile);
$contentArr['title'] = PathUtility::basename($absFile);
// Meta information
$cmd = $this->app['unzip'] . ' -p ' . escapeshellarg($absFile) . ' docProps/core.xml';
CommandUtility::exec($cmd, $res);
......@@ -607,7 +608,7 @@ class FileContentParser
unset($res);
$utf8_content = trim(strip_tags(str_replace('<', ' <', $content_xml)));
$contentArr = $this->pObj->splitRegularContent($utf8_content);
$contentArr['title'] = basename($absFile);
$contentArr['title'] = PathUtility::basename($absFile);
// Make sure the title doesn't expose the absolute path!
// Meta information
$metaContent = GeneralUtility::xml2tree($meta_xml);
......@@ -646,7 +647,7 @@ class FileContentParser
$contentCharset = 'utf-8';
$content = $this->pObj->convertHTMLToUtf8($content, $contentCharset);
$contentArr = $this->pObj->splitRegularContent($content);
$contentArr['title'] = basename($absFile);
$contentArr['title'] = PathUtility::basename($absFile);
// Make sure the title doesn't expose the absolute path!
$this->setLocaleForServerFileSystem(true);
break;
......@@ -666,7 +667,7 @@ class FileContentParser
// Converting content:
$fileContent = $this->pObj->convertHTMLToUtf8(strip_tags(str_replace('<', ' <', $fileContent)), $charset);
$contentArr = $this->pObj->splitRegularContent($fileContent);
$contentArr['title'] = basename($absFile);
$contentArr['title'] = PathUtility::basename($absFile);
// Make sure the title doesn't expose the absolute path!
$this->setLocaleForServerFileSystem(true);
break;
......@@ -686,7 +687,7 @@ class FileContentParser
$comment = '';
}
$contentArr = $this->pObj->splitRegularContent($comment);
$contentArr['title'] = basename($absFile);
$contentArr['title'] = PathUtility::basename($absFile);
// Make sure the title doesn't expose the absolute path!
$this->setLocaleForServerFileSystem(true);
break;
......@@ -696,7 +697,7 @@ class FileContentParser
// If no title (and why should there be...) then the file-name is set as title. This will raise the hits considerably if the search matches the document name.
if (is_array($contentArr) && !$contentArr['title']) {
// Substituting "_" for " " because many filenames may have this instead of a space char.
$contentArr['title'] = str_replace('_', ' ', basename($absFile));
$contentArr['title'] = str_replace('_', ' ', PathUtility::basename($absFile));
}
return $contentArr;
}
......
......@@ -20,6 +20,7 @@ use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
use TYPO3\CMS\IndexedSearch\Utility\IndexedSearchUtility;
......@@ -1134,7 +1135,7 @@ class Indexer
$cParts = $this->fileContentParts($ext, $absFile);
foreach ($cParts as $cPKey) {
$this->internal_log = [];
$this->log_push('Index: ' . str_replace('.', '_', basename($file)) . ($cPKey ? '#' . $cPKey : ''), '');
$this->log_push('Index: ' . str_replace('.', '_', PathUtility::basename($file)) . ($cPKey ? '#' . $cPKey : ''), '');
$Pstart = GeneralUtility::milliseconds();
$subinfo = ['key' => $cPKey];
// Setting page range. This is "0" (zero) when no division is made, otherwise a range like "1-3"
......@@ -1619,7 +1620,7 @@ class Indexer
'contentHash' => $content_md5h,
'data_filename' => $file,
'item_type' => $storeItemType,
'item_title' => trim($contentParts['title']) ?: basename($file),
'item_title' => trim($contentParts['title']) ?: PathUtility::basename($file),
'item_description' => $this->bodyDescription($contentParts),
'item_mtime' => $mtime,
'item_size' => $size,
......
......@@ -23,6 +23,7 @@ use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\ResourceStorage;
use TYPO3\CMS\Core\Resource\StorageRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* Upgrade wizard which goes through all files referenced in backend_layout.icon
......@@ -207,7 +208,7 @@ class BackendLayoutIconUpdateWizard extends AbstractUpdate
$fileUid = null;
$sourcePath = PATH_site . $this->sourcePath . $item;
$targetDirectory = PATH_site . $fileadminDirectory . $this->targetPath;
$targetPath = $targetDirectory . basename($item);
$targetPath = $targetDirectory . PathUtility::basenameDuringBootstrap($item);
// maybe the file was already moved, so check if the original file still exists
if (file_exists($sourcePath)) {
......
......@@ -24,6 +24,7 @@ use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\ResourceStorage;
use TYPO3\CMS\Core\Resource\StorageRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* Upgrade wizard which goes through all files referenced in fe_users::image
......@@ -253,7 +254,7 @@ class FrontendUserImageUpdateWizard extends AbstractUpdate
$fileUid = null;
$sourcePath = PATH_site . $this->sourcePath . $item;
$targetDirectory = PATH_site . $fileadminDirectory . $this->targetPath;
$targetPath = $targetDirectory . basename($item);
$targetPath = $targetDirectory . PathUtility::basename($item);
// maybe the file was already moved, so check if the original file still exists
if (file_exists($sourcePath)) {
......
......@@ -19,6 +19,7 @@ namespace TYPO3\CMS\Install\UpgradeAnalysis;
use TYPO3\CMS\Core\Registry;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* Provide information about documentation files
......@@ -237,7 +238,7 @@ class DocumentationFile
$documentationFiles = [];
if ($this->isRelevantDirectory($docDirectory, $version)) {
$documentationFiles[$version] = [];
$absolutePath = strtr(dirname($docDirectory), '\\', '/') . '/' . $version;
$absolutePath = strtr(PathUtility::dirname($docDirectory), '\\', '/') . '/' . $version;
$rstFiles = scandir($docDirectory);
foreach ($rstFiles as $file) {
$fileInfo = pathinfo($file);
......
......@@ -209,13 +209,13 @@ If you want to get more detailed information, use the --verbose option.')
$io->writeln('Keeping "' . $fileName . '" for record "' . $recReference . '"');
} else {
// Create unique name for file
$newName = $fileFunc->getUniqueName(basename($fileName), dirname($absoluteFileName));
$newName = $fileFunc->getUniqueName(PathUtility::basename($fileName), PathUtility::dirname($absoluteFileName));
$io->writeln('Copying "' . $fileName . '" to "' . PathUtility::stripPathSitePrefix($newName) . '" for record "' . $recReference . '"');
if (!$dryRun) {
GeneralUtility::upload_copy_move($absoluteFileName, $newName);
clearstatcache();
if (@is_file($newName)) {
$error = $referenceIndex->setReferenceValue($hash, basename($newName));
$error = $referenceIndex->setReferenceValue($hash, PathUtility::basename($newName));
if ($error) {
$io->error('ReferenceIndex::setReferenceValue() reported "' . $error . '"');
}
......
......@@ -24,6 +24,7 @@ use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\ReferenceIndex;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* Finds files within uploads/ which are not needed anymore
......@@ -203,7 +204,7 @@ If you want to get more detailed information, use the --verbose option.')
}
// If the file is a RTEmagic-image name and if so, we allow it
if (preg_match('/^RTEmagic[P|C]_/', basename($value))) {
if (preg_match('/^RTEmagic[P|C]_/', PathUtility::basenameDuringBootstrap($value))) {
continue;
}
......
......@@ -227,7 +227,7 @@ If you want to get more detailed information, use the --verbose option.')
// Traverse the files and put into a large table:
while ($rec = $result->fetch()) {
$file = $rec['ref_string'];
$filename = basename($file);
$filename = PathUtility::basenameDuringBootstrap($file);
if (strpos($filename, 'RTEmagicC_') === 0) {
// First time the file is referenced => build index
if (!is_array($allRteImagesInUse[$file])) {
......@@ -268,7 +268,7 @@ If you want to get more detailed information, use the --verbose option.')
// Traverse files
foreach ($files as $key => $value) {
// If the file is a RTEmagic-image name
if (preg_match('/^RTEmagic[P|C]_/', basename($value))) {
if (preg_match('/^RTEmagic[P|C]_/', PathUtility::basenameDuringBootstrap($value))) {
$filesFound[] = $value;
continue;
}
......@@ -323,19 +323,19 @@ If you want to get more detailed information, use the --verbose option.')
if ($c === 0) {
$io->writeln('Keeping file ' . $fileName . ' for record ' . $recordID);
} else {
$io->writeln('Copying file ' . basename($fileName) . ' for record ' . $recordID);
$io->writeln('Copying file ' . PathUtility::basenameDuringBootstrap($fileName) . ' for record ' . $recordID);
// Get directory prefix for file and set the original name
$dirPrefix = dirname($fileName) . '/';
$rteOrigName = basename($fileInfo['original']);
$dirPrefix = PathUtility::dirnameDuringBootstrap($fileName) . '/';
$rteOrigName = PathUtility::basenameDuringBootstrap($fileInfo['original']);
// If filename looks like an RTE file, and the directory is in "uploads/", then process as a RTE file!
if ($rteOrigName && strpos($dirPrefix, 'uploads/') === 0 && @is_dir(PATH_site . $dirPrefix)) {
// From the "original" RTE filename, produce a new "original" destination filename which is unused.
$origDestName = $fileProcObj->getUniqueName($rteOrigName, PATH_site . $dirPrefix);
// Create copy file name
$pI = pathinfo($fileName);
$copyDestName = dirname($origDestName) . '/RTEmagicC_' . substr(basename($origDestName), 10) . '.' . $pI['extension'];
$copyDestName = PathUtility::dirnameDuringBootstrap($origDestName) . '/RTEmagicC_' . substr(PathUtility::basenameDuringBootstrap($origDestName), 10) . '.' . $pI['extension'];
if (!@is_file($copyDestName) && !@is_file($origDestName) && $origDestName === GeneralUtility::getFileAbsFileName($origDestName) && $copyDestName === GeneralUtility::getFileAbsFileName($copyDestName)) {
$io->writeln('Copying file ' . basename($fileName) . ' for record ' . $recordID . ' to ' . basename($copyDestName));
$io->writeln('Copying file ' . PathUtility::basenameDuringBootstrap($fileName) . ' for record ' . $recordID . ' to ' . PathUtility::basenameDuringBootstrap($copyDestName));
if (!$dryRun) {
// Making copies
GeneralUtility::upload_copy_move(PATH_site . $fileInfo['original'], $origDestName);
......
......@@ -115,8 +115,8 @@ class AdditionalResourceService implements SingletonInterface
protected function resolvePath($resourcePath)
{
$absoluteFilePath = GeneralUtility::getFileAbsFileName($resourcePath);
$absolutePath = dirname($absoluteFilePath);
$fileName = basename($absoluteFilePath);
$absolutePath = PathUtility::dirname($absoluteFilePath);
$fileName = PathUtility::basename($absoluteFilePath);
return PathUtility::getRelativePathTo($absolutePath) . $fileName;
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment