‪TYPO3CMS  10.4
CoreUpdateService.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
27 
40 {
44  protected ‪$coreVersionService;
45 
49  protected ‪$messages;
50 
56  protected ‪$downloadTargetPath;
57 
63  protected ‪$symlinkToCoreFiles;
64 
70  protected ‪$downloadBaseUri;
71 
72  public function ‪__construct(CoreVersionService ‪$coreVersionService)
73  {
74  $this->coreVersionService = ‪$coreVersionService;
75  $this->‪setDownloadTargetPath(‪Environment::getVarPath() . '/transient/');
76  $this->symlinkToCoreFiles = $this->‪discoverCurrentCoreSymlink();
77  $this->downloadBaseUri = 'https://get.typo3.org';
78  $this->messages = new ‪FlashMessageQueue('install');
79  }
80 
86  public function ‪isCoreUpdateEnabled()
87  {
88  $coreUpdateDisabled = getenv('TYPO3_DISABLE_CORE_UPDATER') ?: (getenv('REDIRECT_TYPO3_DISABLE_CORE_UPDATER') ?: false);
89  return !‪Environment::isComposerMode() && !$coreUpdateDisabled;
90  }
91 
97  protected function ‪discoverCurrentCoreSymlink()
98  {
99  return ‪Environment::getPublicPath() . '/typo3_src';
100  }
101 
109  {
110  if (!is_dir(‪$downloadTargetPath)) {
112  }
113  $this->downloadTargetPath = ‪$downloadTargetPath;
114  }
115 
121  public function ‪getMessages(): ‪FlashMessageQueue
122  {
123  return ‪$this->messages;
124  }
125 
132  public function ‪checkPreConditions(‪CoreRelease $coreRelease)
133  {
134  $success = true;
135 
136  // Folder structure test: Update can be done only if folder structure returns no errors
137  $folderStructureFacade = GeneralUtility::makeInstance(DefaultFactory::class)->getStructure();
138  $folderStructureMessageQueue = $folderStructureFacade->getStatus();
139  $folderStructureErrors = $folderStructureMessageQueue->getAllMessages(‪FlashMessage::ERROR);
140  $folderStructureWarnings = $folderStructureMessageQueue->getAllMessages(‪FlashMessage::WARNING);
141  if (!empty($folderStructureErrors) || !empty($folderStructureWarnings) || !is_link(‪Environment::getPublicPath() . '/typo3_src')) {
142  $success = false;
143  $this->messages->enqueue(new ‪FlashMessage(
144  'To perform an update, the folder structure of this TYPO3 CMS instance must'
145  . ' stick to the conventions, or the update process could lead to unexpected results'
146  . ' and may be hazardous to your system. Please check your directory status in the'
147  . ' “Environment” module under “Directory Status”.',
148  'Automatic TYPO3 CMS core update not possible: Folder structure has errors or warnings',
150  ));
151  }
152 
153  // No core update on windows
155  $success = false;
156  $this->messages->enqueue(new ‪FlashMessage(
157  '',
158  'Automatic TYPO3 CMS core update not possible: Update not supported on Windows OS',
160  ));
161  }
162 
163  if ($success) {
164  // Explicit write check to document root
165  $file = ‪Environment::getPublicPath() . '/' . ‪StringUtility::getUniqueId('install-core-update-test-');
166  $result = @touch($file);
167  if (!$result) {
168  $success = false;
169  $this->messages->enqueue(new ‪FlashMessage(
170  'Could not write a file in path "' . ‪Environment::getPublicPath() . '/"!'
171  . ' Please check your directory status in the “Environment” module under “Directory Status”.',
172  'Automatic TYPO3 CMS core update not possible: No write access to document root',
174  ));
175  } else {
176  // Check symlink creation
177  $link = ‪Environment::getPublicPath() . '/' . ‪StringUtility::getUniqueId('install-core-update-test-');
178  @symlink($file, $link);
179  if (!is_link($link)) {
180  $success = false;
181  $this->messages->enqueue(new ‪FlashMessage(
182  'Could not create a symbolic link in path "' . ‪Environment::getPublicPath() . '/"!'
183  . ' Please check your directory status in the “Environment” module under “Directory Status”.',
184  'Automatic TYPO3 CMS core update not possible: No symlink creation possible',
186  ));
187  } else {
188  unlink($link);
189  }
190  unlink($file);
191  }
192 
193  if (!$this->‪checkCoreFilesAvailable($coreRelease->‪getVersion())) {
194  // Explicit write check to upper directory of current core location
195  $coreLocation = @realpath($this->symlinkToCoreFiles . '/../');
196  $file = $coreLocation . '/' . ‪StringUtility::getUniqueId('install-core-update-test-');
197  $result = @touch($file);
198  if (!$result) {
199  $success = false;
200  $this->messages->enqueue(new ‪FlashMessage(
201  'New TYPO3 CMS core should be installed in "' . $coreLocation . '", but this directory is not writable!'
202  . ' Please check your directory status in the “Environment” module under “Directory Status”.',
203  'Automatic TYPO3 CMS core update not possible: No write access to TYPO3 CMS core location',
205  ));
206  } else {
207  unlink($file);
208  }
209  }
210  }
211 
212  if ($success && !$this->coreVersionService->isInstalledVersionAReleasedVersion()) {
213  $success = false;
214  $this->messages->enqueue(new ‪FlashMessage(
215  'Your current version is specified as ' . $this->coreVersionService->getInstalledVersion() . '.'
216  . ' This is a development version and can not be updated automatically. If this is a "git"'
217  . ' checkout, please update using git directly.',
218  'Automatic TYPO3 CMS core update not possible: You are running a development version of TYPO3',
220  ));
221  }
222 
223  return $success;
224  }
225 
232  public function ‪downloadVersion(‪CoreRelease $coreRelease)
233  {
234  $version = $coreRelease->‪getVersion();
235  $success = true;
236  if ($this->‪checkCoreFilesAvailable($version)) {
237  $this->messages->enqueue(new ‪FlashMessage(
238  '',
239  'Skipped download of TYPO3 CMS core. A core source directory already exists in destination path. Using this instead.',
241  ));
242  } else {
243  $downloadUri = $this->downloadBaseUri . '/' . $version;
244  $fileLocation = $this->‪getDownloadTarGzTargetPath($version);
245 
246  if (@file_exists($fileLocation)) {
247  $success = false;
248  $this->messages->enqueue(new ‪FlashMessage(
249  '',
250  'TYPO3 CMS core download exists in download location: ' . ‪PathUtility::stripPathSitePrefix($this->downloadTargetPath),
252  ));
253  } else {
254  $fileContent = ‪GeneralUtility::getUrl($downloadUri);
255  if (!$fileContent) {
256  $success = false;
257  $this->messages->enqueue(new ‪FlashMessage(
258  'Failed to download ' . $downloadUri,
259  'Download not successful',
261  ));
262  } else {
263  $fileStoreResult = file_put_contents($fileLocation, $fileContent);
264  if (!$fileStoreResult) {
265  $success = false;
266  $this->messages->enqueue(new ‪FlashMessage(
267  '',
268  'Unable to store download content',
270  ));
271  } else {
272  $this->messages->enqueue(new ‪FlashMessage(
273  '',
274  'TYPO3 CMS core download finished'
275  ));
276  }
277  }
278  }
279  }
280  return $success;
281  }
282 
289  public function ‪verifyFileChecksum(‪CoreRelease $coreRelease)
290  {
291  $version = $coreRelease->‪getVersion();
292  $success = true;
293  if ($this->‪checkCoreFilesAvailable($version)) {
294  $this->messages->enqueue(new ‪FlashMessage(
295  '',
296  'Verifying existing TYPO3 CMS core checksum is not possible',
298  ));
299  } else {
300  $fileLocation = $this->‪getDownloadTarGzTargetPath($version);
301  $expectedChecksum = $coreRelease->‪getChecksum();
302  if (!file_exists($fileLocation)) {
303  $success = false;
304  $this->messages->enqueue(new ‪FlashMessage(
305  '',
306  'Downloaded TYPO3 CMS core not found',
308  ));
309  } else {
310  $actualChecksum = sha1_file($fileLocation);
311  if ($actualChecksum !== $expectedChecksum) {
312  $success = false;
313  $this->messages->enqueue(new ‪FlashMessage(
314  'The official TYPO3 CMS version system on https://get.typo3.org expects a sha1 checksum of '
315  . $expectedChecksum . ' from the content of the downloaded new TYPO3 CMS core version ' . $version . '.'
316  . ' The actual checksum is ' . $actualChecksum . '. The update is stopped. This may be a'
317  . ' failed download, an attack, or an issue with the typo3.org infrastructure.',
318  'New TYPO3 CMS core checksum mismatch',
320  ));
321  } else {
322  $this->messages->enqueue(new ‪FlashMessage(
323  '',
324  'Checksum verified'
325  ));
326  }
327  }
328  }
329  return $success;
330  }
331 
338  public function ‪unpackVersion(‪CoreRelease $coreRelease)
339  {
340  $version = $coreRelease->‪getVersion();
341  $success = true;
342  if ($this->‪checkCoreFilesAvailable($version)) {
343  $this->messages->enqueue(new ‪FlashMessage(
344  '',
345  'Unpacking TYPO3 CMS core files skipped',
347  ));
348  } else {
349  $fileLocation = $this->downloadTargetPath . $version . '.tar.gz';
350  if (!@is_file($fileLocation)) {
351  $success = false;
352  $this->messages->enqueue(new ‪FlashMessage(
353  '',
354  'Downloaded TYPO3 CMS core not found',
356  ));
357  } elseif (@file_exists($this->downloadTargetPath . 'typo3_src-' . $version)) {
358  $success = false;
359  $this->messages->enqueue(new ‪FlashMessage(
360  '',
361  'Unpacked TYPO3 CMS core exists in download location: ' . ‪PathUtility::stripPathSitePrefix($this->downloadTargetPath),
363  ));
364  } else {
365  $unpackCommand = 'tar xf ' . escapeshellarg($fileLocation) . ' -C ' . escapeshellarg($this->downloadTargetPath) . ' 2>&1';
366  exec($unpackCommand, ‪$output, $errorCode);
367  if ($errorCode) {
368  $success = false;
369  $this->messages->enqueue(new ‪FlashMessage(
370  '',
371  'Unpacking TYPO3 CMS core not successful',
373  ));
374  } else {
375  $removePackedFileResult = unlink($fileLocation);
376  if (!$removePackedFileResult) {
377  $success = false;
378  $this->messages->enqueue(new ‪FlashMessage(
379  '',
380  'Removing packed TYPO3 CMS core not successful',
382  ));
383  } else {
384  $this->messages->enqueue(new ‪FlashMessage(
385  '',
386  'Unpacking TYPO3 CMS core successful'
387  ));
388  }
389  }
390  }
391  }
392  return $success;
393  }
394 
401  public function ‪moveVersion(‪CoreRelease $coreRelease)
402  {
403  $version = $coreRelease->‪getVersion();
404  $success = true;
405  if ($this->‪checkCoreFilesAvailable($version)) {
406  $this->messages->enqueue(new ‪FlashMessage(
407  '',
408  'Moving TYPO3 CMS core files skipped',
410  ));
411  } else {
412  $downloadedCoreLocation = $this->downloadTargetPath . 'typo3_src-' . $version;
413  $newCoreLocation = @realpath($this->symlinkToCoreFiles . '/../') . '/typo3_src-' . $version;
414 
415  if (!@is_dir($downloadedCoreLocation)) {
416  $success = false;
417  $this->messages->enqueue(new ‪FlashMessage(
418  '',
419  'Unpacked TYPO3 CMS core not found',
421  ));
422  } else {
423  $moveResult = rename($downloadedCoreLocation, $newCoreLocation);
424  if (!$moveResult) {
425  $success = false;
426  $this->messages->enqueue(new ‪FlashMessage(
427  '',
428  'Moving TYPO3 CMS core to ' . $newCoreLocation . ' failed',
430  ));
431  } else {
432  $this->messages->enqueue(new ‪FlashMessage(
433  '',
434  'Moved TYPO3 CMS core to final location'
435  ));
436  }
437  }
438  }
439  return $success;
440  }
441 
448  public function ‪activateVersion(‪CoreRelease $coreRelease)
449  {
450  $newCoreLocation = @realpath($this->symlinkToCoreFiles . '/../') . '/typo3_src-' . $coreRelease->‪getVersion();
451  $success = true;
452  if (!is_dir($newCoreLocation)) {
453  $success = false;
454  $this->messages->enqueue(new ‪FlashMessage(
455  '',
456  'New TYPO3 CMS core not found',
458  ));
459  } elseif (!is_link($this->symlinkToCoreFiles)) {
460  $success = false;
461  $this->messages->enqueue(new ‪FlashMessage(
462  '',
463  'TYPO3 CMS core source directory (typo3_src) is not a link',
465  ));
466  } else {
467  $isCurrentCoreSymlinkAbsolute = ‪PathUtility::isAbsolutePath((string)readlink($this->symlinkToCoreFiles));
468  $unlinkResult = unlink($this->symlinkToCoreFiles);
469  if (!$unlinkResult) {
470  $success = false;
471  $this->messages->enqueue(new ‪FlashMessage(
472  '',
473  'Removing old symlink failed',
475  ));
476  } else {
477  if (!$isCurrentCoreSymlinkAbsolute) {
478  $newCoreLocation = $this->‪getRelativePath($newCoreLocation);
479  }
480  $symlinkResult = symlink($newCoreLocation, $this->symlinkToCoreFiles);
481  if ($symlinkResult) {
482  GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
483  } else {
484  $success = false;
485  $this->messages->enqueue(new ‪FlashMessage(
486  '',
487  'Linking new TYPO3 CMS core failed',
489  ));
490  }
491  }
492  }
493  return $success;
494  }
495 
502  protected function ‪getDownloadTarGzTargetPath($version)
503  {
504  return $this->downloadTargetPath . $version . '.tar.gz';
505  }
506 
513  protected function ‪getRelativePath($absolutePath)
514  {
515  $sourcePath = explode(DIRECTORY_SEPARATOR, ‪Environment::getPublicPath());
516  $targetPath = explode(DIRECTORY_SEPARATOR, rtrim($absolutePath, DIRECTORY_SEPARATOR));
517  while (count($sourcePath) && count($targetPath) && $sourcePath[0] === $targetPath[0]) {
518  array_shift($sourcePath);
519  array_shift($targetPath);
520  }
521  return str_pad('', count($sourcePath) * 3, '..' . DIRECTORY_SEPARATOR) . implode(DIRECTORY_SEPARATOR, $targetPath);
522  }
523 
531  protected function ‪checkCoreFilesAvailable($version)
532  {
533  $newCoreLocation = @realpath($this->symlinkToCoreFiles . '/../') . '/typo3_src-' . $version;
534  return @is_dir($newCoreLocation);
535  }
536 }
‪TYPO3\CMS\Install\Service\CoreUpdateService\activateVersion
‪bool activateVersion(CoreRelease $coreRelease)
Definition: CoreUpdateService.php:443
‪TYPO3\CMS\Install\Service\CoreUpdateService\getRelativePath
‪string getRelativePath($absolutePath)
Definition: CoreUpdateService.php:508
‪TYPO3\CMS\Install\Service\CoreUpdateService\unpackVersion
‪bool unpackVersion(CoreRelease $coreRelease)
Definition: CoreUpdateService.php:333
‪TYPO3\CMS\Install\Service\CoreUpdateService\verifyFileChecksum
‪bool verifyFileChecksum(CoreRelease $coreRelease)
Definition: CoreUpdateService.php:284
‪TYPO3\CMS\Install\Service\CoreUpdateService\getMessages
‪FlashMessageQueue getMessages()
Definition: CoreUpdateService.php:116
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:24
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static string getPublicPath()
Definition: Environment.php:180
‪TYPO3\CMS\Core\Utility\PathUtility\stripPathSitePrefix
‪static string stripPathSitePrefix($path)
Definition: PathUtility.php:372
‪TYPO3\CMS\Install\Service\CoreUpdateService\__construct
‪__construct(CoreVersionService $coreVersionService)
Definition: CoreUpdateService.php:67
‪TYPO3\CMS\Install\Service\CoreUpdateService\$downloadBaseUri
‪string $downloadBaseUri
Definition: CoreUpdateService.php:65
‪TYPO3\CMS\Core\Utility\GeneralUtility\getUrl
‪static mixed getUrl($url, $includeHeader=0, $requestHeaders=null, &$report=null)
Definition: GeneralUtility.php:1748
‪TYPO3\CMS\Install\Service\CoreUpdateService\$symlinkToCoreFiles
‪string $symlinkToCoreFiles
Definition: CoreUpdateService.php:59
‪TYPO3\CMS\Core\Core\Environment\isWindows
‪static bool isWindows()
Definition: Environment.php:292
‪TYPO3\CMS\Install\Service\CoreUpdateService\downloadVersion
‪bool downloadVersion(CoreRelease $coreRelease)
Definition: CoreUpdateService.php:227
‪TYPO3\CMS\Install\FolderStructure\DefaultFactory
Definition: DefaultFactory.php:25
‪TYPO3\CMS\Install\Service\CoreUpdateService\checkCoreFilesAvailable
‪bool checkCoreFilesAvailable($version)
Definition: CoreUpdateService.php:526
‪TYPO3\CMS\Install\CoreVersion\CoreRelease
Definition: CoreRelease.php:21
‪TYPO3\CMS\Core\Messaging\AbstractMessage\WARNING
‪const WARNING
Definition: AbstractMessage.php:30
‪TYPO3\CMS\Install\Service\CoreUpdateService\isCoreUpdateEnabled
‪bool isCoreUpdateEnabled()
Definition: CoreUpdateService.php:81
‪TYPO3\CMS\Install\Service\CoreUpdateService\discoverCurrentCoreSymlink
‪string discoverCurrentCoreSymlink()
Definition: CoreUpdateService.php:92
‪TYPO3\CMS\Install\Service\CoreUpdateService\$messages
‪FlashMessageQueue $messages
Definition: CoreUpdateService.php:47
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep($directory)
Definition: GeneralUtility.php:2022
‪TYPO3\CMS\Install\CoreVersion\CoreRelease\getChecksum
‪getChecksum()
Definition: CoreRelease.php:60
‪TYPO3\CMS\Core\Service\OpcodeCacheService
Definition: OpcodeCacheService.php:25
‪TYPO3\CMS\Core\Utility\PathUtility\isAbsolutePath
‪static bool isAbsolutePath($path)
Definition: PathUtility.php:223
‪TYPO3\CMS\Install\Service\CoreUpdateService\moveVersion
‪bool moveVersion(CoreRelease $coreRelease)
Definition: CoreUpdateService.php:396
‪$output
‪$output
Definition: annotationChecker.php:119
‪TYPO3\CMS\Install\Service\CoreUpdateService\getDownloadTarGzTargetPath
‪string getDownloadTarGzTargetPath($version)
Definition: CoreUpdateService.php:497
‪TYPO3\CMS\Install\Service\CoreUpdateService
Definition: CoreUpdateService.php:40
‪TYPO3\CMS\Core\Core\Environment\isComposerMode
‪static bool isComposerMode()
Definition: Environment.php:144
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:92
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:24
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:40
‪TYPO3\CMS\Core\Messaging\AbstractMessage\NOTICE
‪const NOTICE
Definition: AbstractMessage.php:27
‪TYPO3\CMS\Install\Service\CoreUpdateService\setDownloadTargetPath
‪setDownloadTargetPath($downloadTargetPath)
Definition: CoreUpdateService.php:103
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:22
‪TYPO3\CMS\Install\CoreVersion\CoreRelease\getVersion
‪getVersion()
Definition: CoreRelease.php:45
‪TYPO3\CMS\Core\Messaging\FlashMessageQueue
Definition: FlashMessageQueue.php:29
‪TYPO3\CMS\Install\Service
Definition: ClearCacheService.php:16
‪TYPO3\CMS\Install\Service\CoreUpdateService\checkPreConditions
‪bool checkPreConditions(CoreRelease $coreRelease)
Definition: CoreUpdateService.php:127
‪TYPO3\CMS\Install\Service\CoreUpdateService\$downloadTargetPath
‪string $downloadTargetPath
Definition: CoreUpdateService.php:53
‪TYPO3\CMS\Core\Messaging\AbstractMessage\ERROR
‪const ERROR
Definition: AbstractMessage.php:31
‪TYPO3\CMS\Install\Service\CoreUpdateService\$coreVersionService
‪CoreVersionService $coreVersionService
Definition: CoreUpdateService.php:43
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static string getVarPath()
Definition: Environment.php:192