TYPO3 CMS  TYPO3_8-7
FileHandlingUtility.php
Go to the documentation of this file.
1 <?php
3 
13 
14 /*
15  * This file is part of the TYPO3 CMS project.
16  *
17  * It is free software; you can redistribute it and/or modify it under
18  * the terms of the GNU General Public License, either version 2
19  * of the License, or any later version.
20  *
21  * For the full copyright and license information, please read the
22  * LICENSE.txt file that was distributed with this source code.
23  *
24  * The TYPO3 project - inspiring people to share!
25  */
26 
30 class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface
31 {
32  use LoggerAwareTrait;
33 
37  protected $emConfUtility;
38 
42  protected $installUtility;
43 
47  protected $languageService;
48 
52  public function injectEmConfUtility(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility)
53  {
54  $this->emConfUtility = $emConfUtility;
55  }
56 
60  public function injectInstallUtility(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility)
61  {
62  $this->installUtility = $installUtility;
63  }
64 
68  public function injectLanguageService(\TYPO3\CMS\Lang\LanguageService $languageService)
69  {
70  $this->languageService = $languageService;
71  }
72 
76  public function initializeObject()
77  {
78  $this->languageService->includeLLFile('EXT:extensionmanager/Resources/Private/Language/locallang.xlf');
79  }
80 
88  public function unpackExtensionFromExtensionDataArray(array $extensionData, Extension $extension = null, $pathType = 'Local')
89  {
90  $extensionDir = $this->makeAndClearExtensionDir($extensionData['extKey'], $pathType);
91  $files = $this->extractFilesArrayFromExtensionData($extensionData);
92  $directories = $this->extractDirectoriesFromExtensionData($files);
93  $files = array_diff_key($files, array_flip($directories));
94  $this->createDirectoriesForExtensionFiles($directories, $extensionDir);
95  $this->writeExtensionFiles($files, $extensionDir);
96  $this->writeEmConfToFile($extensionData, $extensionDir, $extension);
97  $this->reloadPackageInformation($extensionData['extKey']);
98  }
99 
106  protected function extractDirectoriesFromExtensionData(array $files)
107  {
108  $directories = [];
109  foreach ($files as $filePath => $file) {
110  preg_match('/(.*)\\//', $filePath, $matches);
111  if (!empty($matches[0])) {
112  $directories[] = $matches[0];
113  }
114  }
115  return array_unique($directories);
116  }
117 
124  protected function extractFilesArrayFromExtensionData(array $extensionData)
125  {
126  return $extensionData['FILES'];
127  }
128 
136  protected function createDirectoriesForExtensionFiles(array $directories, $rootPath)
137  {
138  foreach ($directories as $directory) {
139  $this->createNestedDirectory($rootPath . $directory);
140  }
141  }
142 
149  protected function createNestedDirectory($directory)
150  {
151  try {
152  GeneralUtility::mkdir_deep($directory);
153  } catch (\RuntimeException $exception) {
154  throw new ExtensionManagerException(
155  sprintf($this->languageService->getLL('fileHandling.couldNotCreateDirectory'), $this->getRelativePath($directory)),
156  1337280416
157  );
158  }
159  }
160 
167  protected function writeExtensionFiles(array $files, $rootPath)
168  {
169  foreach ($files as $file) {
170  GeneralUtility::writeFile($rootPath . $file['name'], $file['content']);
171  }
172  }
173 
183  protected function makeAndClearExtensionDir($extensionKey, $pathType = 'Local')
184  {
185  $extDirPath = $this->getExtensionDir($extensionKey, $pathType);
186  if (is_dir($extDirPath)) {
187  $this->removeDirectory($extDirPath);
188  }
189  $this->addDirectory($extDirPath);
190 
191  return $extDirPath;
192  }
193 
202  public function getExtensionDir($extensionKey, $pathType = 'Local')
203  {
205  $path = $paths[$pathType];
206  if (!$path || !is_dir($path) || !$extensionKey) {
207  throw new ExtensionManagerException(
208  sprintf($this->languageService->getLL('fileHandling.installPathWasNoDirectory'), $this->getRelativePath($path)),
209  1337280417
210  );
211  }
212 
213  return $path . $extensionKey . '/';
214  }
215 
222  protected function addDirectory($extDirPath)
223  {
224  GeneralUtility::mkdir($extDirPath);
225  if (!is_dir($extDirPath)) {
226  throw new ExtensionManagerException(
227  sprintf($this->languageService->getLL('fileHandling.couldNotCreateDirectory'), $this->getRelativePath($extDirPath)),
228  1337280418
229  );
230  }
231  }
232 
238  public function ensureConfiguredDirectoriesExist(array $extension)
239  {
240  foreach ($this->getAbsolutePathsToConfiguredDirectories($extension) as $directory) {
241  if (!$this->directoryExists($directory)) {
242  $this->createNestedDirectory($directory);
243  }
244  }
245  }
246 
253  protected function directoryExists($directory)
254  {
255  return is_dir($directory);
256  }
257 
264  protected function getAbsolutePathsToConfiguredDirectories(array $extension)
265  {
266  $requestedDirectories = [];
267  $requestUploadFolder = isset($extension['uploadfolder']) ? (bool)$extension['uploadfolder'] : false;
268  if ($requestUploadFolder) {
269  $requestedDirectories[] = $this->getAbsolutePath($this->getPathToUploadFolder($extension));
270  }
271 
272  $requestCreateDirectories = empty($extension['createDirs']) ? false : (string)$extension['createDirs'];
273  if ($requestCreateDirectories) {
274  foreach (GeneralUtility::trimExplode(',', $extension['createDirs']) as $directoryToCreate) {
275  $requestedDirectories[] = $this->getAbsolutePath($directoryToCreate);
276  }
277  }
278 
279  return $requestedDirectories;
280  }
281 
288  protected function getPathToUploadFolder($extension)
289  {
290  return 'uploads/tx_' . str_replace('_', '', $extension['key']) . '/';
291  }
292 
299  public function removeDirectory($extDirPath)
300  {
301  $extDirPath = GeneralUtility::fixWindowsFilePath($extDirPath);
302  $extensionPathWithoutTrailingSlash = rtrim($extDirPath, '/');
303  if (is_link($extensionPathWithoutTrailingSlash) && TYPO3_OS !== 'WIN') {
304  $result = unlink($extensionPathWithoutTrailingSlash);
305  } else {
306  $result = GeneralUtility::rmdir($extDirPath, true);
307  }
308  if ($result === false) {
309  throw new ExtensionManagerException(
310  sprintf($this->languageService->getLL('fileHandling.couldNotRemoveDirectory'), $this->getRelativePath($extDirPath)),
311  1337280415
312  );
313  }
314  }
315 
324  protected function writeEmConfToFile(array $extensionData, $rootPath, Extension $extension = null)
325  {
326  $emConfFileData = [];
327  if (file_exists($rootPath . 'ext_emconf.php')) {
328  $emConfFileData = $this->emConfUtility->includeEmConf(
329  [
330  'key' => $extensionData['extKey'],
331  'siteRelPath' => PathUtility::stripPathSitePrefix($rootPath)
332  ]
333  );
334  }
335  $extensionData['EM_CONF'] = array_replace_recursive($emConfFileData, $extensionData['EM_CONF']);
336  $emConfContent = $this->emConfUtility->constructEmConf($extensionData, $extension);
337  GeneralUtility::writeFile($rootPath . 'ext_emconf.php', $emConfContent);
338  }
339 
346  public function isValidExtensionPath($path)
347  {
348  $allowedPaths = Extension::returnAllowedInstallPaths();
349  foreach ($allowedPaths as $allowedPath) {
350  if (GeneralUtility::isFirstPartOfStr($path, $allowedPath)) {
351  return true;
352  }
353  }
354  return false;
355  }
356 
364  protected function getAbsolutePath($relativePath)
365  {
366  $absolutePath = GeneralUtility::getFileAbsFileName(GeneralUtility::resolveBackPath(PATH_site . $relativePath));
367  if (empty($absolutePath)) {
368  throw new ExtensionManagerException('Illegal relative path given', 1350742864);
369  }
370  return $absolutePath;
371  }
372 
379  protected function getRelativePath($absolutePath)
380  {
381  return PathUtility::stripPathSitePrefix($absolutePath);
382  }
383 
390  public function getAbsoluteExtensionPath($extension)
391  {
392  $extension = $this->installUtility->enrichExtensionWithDetails($extension);
393  $absolutePath = $this->getAbsolutePath($extension['siteRelPath']);
394  return $absolutePath;
395  }
396 
403  public function getExtensionVersion($extension)
404  {
405  $extensionData = $this->installUtility->enrichExtensionWithDetails($extension);
406  $version = $extensionData['version'];
407  return $version;
408  }
409 
416  public function createZipFileFromExtension($extension)
417  {
418  $extensionPath = $this->getAbsoluteExtensionPath($extension);
419 
420  // Add trailing slash to the extension path, getAllFilesAndFoldersInPath explicitly requires that.
421  $extensionPath = PathUtility::sanitizeTrailingSeparator($extensionPath);
422 
423  $version = $this->getExtensionVersion($extension);
424  if (empty($version)) {
425  $version = '0.0.0';
426  }
427 
428  if (!@is_dir(PATH_site . 'typo3temp/var/ExtensionManager/')) {
429  GeneralUtility::mkdir(PATH_site . 'typo3temp/var/ExtensionManager/');
430  }
431  $fileName = $this->getAbsolutePath('typo3temp/var/ExtensionManager/' . $extension . '_' . $version . '_' . date('YmdHi', $GLOBALS['EXEC_TIME']) . '.zip');
432 
433  $zip = new \ZipArchive();
434  $zip->open($fileName, \ZipArchive::CREATE);
435 
436  $excludePattern = $GLOBALS['TYPO3_CONF_VARS']['EXT']['excludeForPackaging'];
437 
438  // Get all the files of the extension, but exclude the ones specified in the excludePattern
440  [], // No files pre-added
441  $extensionPath, // Start from here
442  '', // Do not filter files by extension
443  true, // Include subdirectories
444  PHP_INT_MAX, // Recursion level
445  $excludePattern // Files and directories to exclude.
446  );
447 
448  // Make paths relative to extension root directory.
449  $files = GeneralUtility::removePrefixPathFromList($files, $extensionPath);
450 
451  // Remove the one empty path that is the extension dir itself.
452  $files = array_filter($files);
453 
454  foreach ($files as $file) {
455  $fullPath = $extensionPath . $file;
456  // Distinguish between files and directories, as creation of the archive
457  // fails on Windows when trying to add a directory with "addFile".
458  if (is_dir($fullPath)) {
459  $zip->addEmptyDir($file);
460  } else {
461  $zip->addFile($fullPath, $file);
462  }
463  }
464 
465  $zip->close();
466  return $fileName;
467  }
468 
477  public function unzipExtensionFromFile($file, $fileName, $pathType = 'Local')
478  {
479  $extensionDir = $this->makeAndClearExtensionDir($fileName, $pathType);
480 
481  try {
482  $zipService = GeneralUtility::makeInstance(ZipService::class);
483  if ($zipService->verify($file)) {
484  $zipService->extract($file, $extensionDir);
485  }
486  } catch (ExtractException $e) {
487  $this->logger->error('Extracting the extension archive failed', ['exception' => $e]);
488  throw new ExtensionManagerException('Extracting the extension archive failed: ' . $e->getMessage(), 1565777179, $e);
489  }
490 
491  GeneralUtility::fixPermissions($extensionDir, true);
492  }
493 
500  public function sendZipFileToBrowserAndDelete($fileName, $downloadName = '')
501  {
502  if ($downloadName === '') {
503  $downloadName = basename($fileName, '.zip');
504  }
505  header('Content-Type: application/zip');
506  header('Content-Length: ' . filesize($fileName));
507  header('Content-Disposition: attachment; filename="' . $downloadName . '.zip"');
508  readfile($fileName);
509  unlink($fileName);
510  die;
511  }
512 
519  public function sendSqlDumpFileToBrowserAndDelete($fileName, $downloadName = '')
520  {
521  if ($downloadName === '') {
522  $downloadName = basename($fileName, '.sql');
523  } else {
524  $downloadName = basename($downloadName, '.sql');
525  }
526  header('Content-Type: text');
527  header('Content-Length: ' . filesize($fileName));
528  header('Content-Disposition: attachment; filename="' . $downloadName . '.sql"');
529  readfile($fileName);
530  unlink($fileName);
531  die;
532  }
533 
537  protected function reloadPackageInformation($extensionKey)
538  {
539  $this->installUtility->reloadPackageInformation($extensionKey);
540  }
541 }
unpackExtensionFromExtensionDataArray(array $extensionData, Extension $extension=null, $pathType='Local')
static mkdir_deep($directory, $deepDirectory='')
writeEmConfToFile(array $extensionData, $rootPath, Extension $extension=null)
injectLanguageService(\TYPO3\CMS\Lang\LanguageService $languageService)
static isFirstPartOfStr($str, $partStr)
injectEmConfUtility(\TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility)
static getFileAbsFileName($filename, $_=null, $_2=null)
unzipExtensionFromFile($file, $fileName, $pathType='Local')
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static makeInstance($className,... $constructorArguments)
static fixPermissions($path, $recursive=false)
static getAllFilesAndFoldersInPath(array $fileArr, $path, $extList='', $regDirs=false, $recursivityLevels=99, $excludePattern='')
static rmdir($path, $removeNonEmpty=false)
injectInstallUtility(\TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static removePrefixPathFromList(array $fileArr, $prefixToRemove)
static writeFile($file, $content, $changePermissions=false)
static sanitizeTrailingSeparator($path, $separator='/')