‪TYPO3CMS  9.5
CoreUpdateService.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
27 
40 {
44  protected ‪$coreVersionService;
45 
49  protected ‪$messages;
50 
56  protected ‪$downloadTargetPath;
57 
63  protected ‪$symlinkToCoreFiles;
64 
71 
75  public function ‪__construct(CoreVersionService ‪$coreVersionService = null)
76  {
77  $this->coreVersionService = ‪$coreVersionService ?: GeneralUtility::makeInstance(CoreVersionService::class);
78  $this->‪setDownloadTargetPath(‪Environment::getVarPath() . '/transient/');
79  $this->symlinkToCoreFiles = $this->‪discoverCurrentCoreSymlink();
80  $this->downloadBaseUri = 'https://get.typo3.org';
81  $this->messages = new ‪FlashMessageQueue('install');
82  }
83 
89  public function ‪isCoreUpdateEnabled()
90  {
91  $coreUpdateDisabled = getenv('TYPO3_DISABLE_CORE_UPDATER') ?: (getenv('REDIRECT_TYPO3_DISABLE_CORE_UPDATER') ?: false);
92  return !‪Environment::isComposerMode() && !$coreUpdateDisabled;
93  }
94 
100  protected function ‪discoverCurrentCoreSymlink()
101  {
102  return ‪Environment::getPublicPath() . '/typo3_src';
103  }
104 
112  {
113  if (!is_dir(‪$downloadTargetPath)) {
114  GeneralUtility::mkdir_deep(‪$downloadTargetPath);
115  }
116  $this->downloadTargetPath = ‪$downloadTargetPath;
117  }
118 
124  public function ‪getMessages(): FlashMessageQueue
125  {
126  return ‪$this->messages;
127  }
128 
135  public function ‪updateVersionMatrix()
136  {
137  trigger_error(
138  'The method updateVersionMatrix() is deprecated since TYPO3 v9 and will be removed in TYPO3 v10.0, use the REST api directly (see https://get.typo3.org/v1/api/doc).',
139  E_USER_DEPRECATED
140  );
141  $success = true;
142  try {
143  $this->coreVersionService->getYoungestPatchRelease();
144  } catch (RemoteFetchException $e) {
145  $success = false;
146  $this->messages->enqueue(new FlashMessage(
147  'Current version specification could not be fetched from https://get.typo3.org.'
148  . ' This is probably a network issue, please fix it.',
149  'Version information could not be fetched from get.typo3.org',
151  ));
152  }
153  return $success;
154  }
155 
162  public function ‪checkPreConditions(CoreRelease $coreRelease)
163  {
164  $success = true;
165 
166  // Folder structure test: Update can be done only if folder structure returns no errors
167  $folderStructureFacade = GeneralUtility::makeInstance(DefaultFactory::class)->getStructure();
168  $folderStructureMessageQueue = $folderStructureFacade->getStatus();
169  $folderStructureErrors = $folderStructureMessageQueue->getAllMessages(‪FlashMessage::ERROR);
170  $folderStructureWarnings = $folderStructureMessageQueue->getAllMessages(‪FlashMessage::WARNING);
171  if (!empty($folderStructureErrors) || !empty($folderStructureWarnings) || !is_link(‪Environment::getPublicPath() . '/typo3_src')) {
172  $success = false;
173  $this->messages->enqueue(new FlashMessage(
174  'To perform an update, the folder structure of this TYPO3 CMS instance must'
175  . ' stick to the conventions, or the update process could lead to unexpected'
176  . ' results and may be hazardous to your system',
177  'Automatic TYPO3 CMS core update not possible: Folder structure has errors or warnings',
179  ));
180  }
181 
182  // No core update on windows
184  $success = false;
185  $this->messages->enqueue(new FlashMessage(
186  '',
187  'Automatic TYPO3 CMS core update not possible: Update not supported on Windows OS',
189  ));
190  }
191 
192  if ($success) {
193  // Explicit write check to document root
194  $file = ‪Environment::getPublicPath() . '/' . ‪StringUtility::getUniqueId('install-core-update-test-');
195  $result = @touch($file);
196  if (!$result) {
197  $success = false;
198  $this->messages->enqueue(new FlashMessage(
199  'Could not write a file in path "' . ‪Environment::getPublicPath() . '/"!',
200  'Automatic TYPO3 CMS core update not possible: No write access to document root',
202  ));
203  } else {
204  // Check symlink creation
205  $link = ‪Environment::getPublicPath() . '/' . ‪StringUtility::getUniqueId('install-core-update-test-');
206  @symlink($file, $link);
207  if (!is_link($link)) {
208  $success = false;
209  $this->messages->enqueue(new FlashMessage(
210  'Could not create a symbolic link in path "' . ‪Environment::getPublicPath() . '/"!',
211  'Automatic TYPO3 CMS core update not possible: No symlink creation possible',
213  ));
214  } else {
215  unlink($link);
216  }
217  unlink($file);
218  }
219 
220  if (!$this->‪checkCoreFilesAvailable($coreRelease->getVersion())) {
221  // Explicit write check to upper directory of current core location
222  $coreLocation = @realpath($this->symlinkToCoreFiles . '/../');
223  $file = $coreLocation . '/' . ‪StringUtility::getUniqueId('install-core-update-test-');
224  $result = @touch($file);
225  if (!$result) {
226  $success = false;
227  $this->messages->enqueue(new FlashMessage(
228  'New TYPO3 CMS core should be installed in "' . $coreLocation . '", but this directory is not writable!',
229  'Automatic TYPO3 CMS core update not possible: No write access to TYPO3 CMS core location',
231  ));
232  } else {
233  unlink($file);
234  }
235  }
236  }
237 
238  if ($success && !$this->coreVersionService->isInstalledVersionAReleasedVersion()) {
239  $success = false;
240  $this->messages->enqueue(new FlashMessage(
241  'Your current version is specified as ' . $this->coreVersionService->getInstalledVersion() . '.'
242  . ' This is a development version and can not be updated automatically. If this is a "git"'
243  . ' checkout, please update using git directly.',
244  'Automatic TYPO3 CMS core update not possible: You are running a development version of TYPO3',
246  ));
247  }
248 
249  return $success;
250  }
251 
258  public function ‪downloadVersion(CoreRelease $coreRelease)
259  {
260  $version = $coreRelease->getVersion();
261  $success = true;
262  if ($this->‪checkCoreFilesAvailable($version)) {
263  $this->messages->enqueue(new FlashMessage(
264  '',
265  'Skipped download of TYPO3 CMS core. A core source directory already exists in destination path. Using this instead.',
267  ));
268  } else {
269  $downloadUri = $this->downloadBaseUri . '/' . $version;
270  $fileLocation = $this->‪getDownloadTarGzTargetPath($version);
271 
272  if (@file_exists($fileLocation)) {
273  $success = false;
274  $this->messages->enqueue(new FlashMessage(
275  '',
276  'TYPO3 CMS core download exists in download location: ' . ‪PathUtility::stripPathSitePrefix($this->downloadTargetPath),
278  ));
279  } else {
280  $fileContent = GeneralUtility::getUrl($downloadUri);
281  if (!$fileContent) {
282  $success = false;
283  $this->messages->enqueue(new FlashMessage(
284  'Failed to download ' . $downloadUri,
285  'Download not successful',
287  ));
288  } else {
289  $fileStoreResult = file_put_contents($fileLocation, $fileContent);
290  if (!$fileStoreResult) {
291  $success = false;
292  $this->messages->enqueue(new FlashMessage(
293  '',
294  'Unable to store download content',
296  ));
297  } else {
298  $this->messages->enqueue(new FlashMessage(
299  '',
300  'TYPO3 CMS core download finished'
301  ));
302  }
303  }
304  }
305  }
306  return $success;
307  }
308 
315  public function ‪verifyFileChecksum(CoreRelease $coreRelease)
316  {
317  $version = $coreRelease->getVersion();
318  $success = true;
319  if ($this->‪checkCoreFilesAvailable($version)) {
320  $this->messages->enqueue(new FlashMessage(
321  '',
322  'Verifying existing TYPO3 CMS core checksum is not possible',
324  ));
325  } else {
326  $fileLocation = $this->‪getDownloadTarGzTargetPath($version);
327  $expectedChecksum = $coreRelease->getChecksum();
328  if (!file_exists($fileLocation)) {
329  $success = false;
330  $this->messages->enqueue(new FlashMessage(
331  '',
332  'Downloaded TYPO3 CMS core not found',
334  ));
335  } else {
336  $actualChecksum = sha1_file($fileLocation);
337  if ($actualChecksum !== $expectedChecksum) {
338  $success = false;
339  $this->messages->enqueue(new FlashMessage(
340  'The official TYPO3 CMS version system on https://get.typo3.org expects a sha1 checksum of '
341  . $expectedChecksum . ' from the content of the downloaded new TYPO3 CMS core version ' . $version . '.'
342  . ' The actual checksum is ' . $actualChecksum . '. The update is stopped. This may be a'
343  . ' failed download, an attack, or an issue with the typo3.org infrastructure.',
344  'New TYPO3 CMS core checksum mismatch',
346  ));
347  } else {
348  $this->messages->enqueue(new FlashMessage(
349  '',
350  'Checksum verified'
351  ));
352  }
353  }
354  }
355  return $success;
356  }
357 
364  public function ‪unpackVersion(CoreRelease $coreRelease)
365  {
366  $version = $coreRelease->getVersion();
367  $success = true;
368  if ($this->‪checkCoreFilesAvailable($version)) {
369  $this->messages->enqueue(new FlashMessage(
370  '',
371  'Unpacking TYPO3 CMS core files skipped',
373  ));
374  } else {
375  $fileLocation = $this->downloadTargetPath . $version . '.tar.gz';
376  if (!@is_file($fileLocation)) {
377  $success = false;
378  $this->messages->enqueue(new FlashMessage(
379  '',
380  'Downloaded TYPO3 CMS core not found',
382  ));
383  } elseif (@file_exists($this->downloadTargetPath . 'typo3_src-' . $version)) {
384  $success = false;
385  $this->messages->enqueue(new FlashMessage(
386  '',
387  'Unpacked TYPO3 CMS core exists in download location: ' . ‪PathUtility::stripPathSitePrefix($this->downloadTargetPath),
389  ));
390  } else {
391  $unpackCommand = 'tar xf ' . escapeshellarg($fileLocation) . ' -C ' . escapeshellarg($this->downloadTargetPath) . ' 2>&1';
392  exec($unpackCommand, ‪$output, $errorCode);
393  if ($errorCode) {
394  $success = false;
395  $this->messages->enqueue(new FlashMessage(
396  '',
397  'Unpacking TYPO3 CMS core not successful',
399  ));
400  } else {
401  $removePackedFileResult = unlink($fileLocation);
402  if (!$removePackedFileResult) {
403  $success = false;
404  $this->messages->enqueue(new FlashMessage(
405  '',
406  'Removing packed TYPO3 CMS core not successful',
408  ));
409  } else {
410  $this->messages->enqueue(new FlashMessage(
411  '',
412  'Unpacking TYPO3 CMS core successful'
413  ));
414  }
415  }
416  }
417  }
418  return $success;
419  }
420 
427  public function ‪moveVersion(CoreRelease $coreRelease)
428  {
429  $version = $coreRelease->getVersion();
430  $success = true;
431  if ($this->‪checkCoreFilesAvailable($version)) {
432  $this->messages->enqueue(new FlashMessage(
433  '',
434  'Moving TYPO3 CMS core files skipped',
436  ));
437  } else {
438  $downloadedCoreLocation = $this->downloadTargetPath . 'typo3_src-' . $version;
439  $newCoreLocation = @realpath($this->symlinkToCoreFiles . '/../') . '/typo3_src-' . $version;
440 
441  if (!@is_dir($downloadedCoreLocation)) {
442  $success = false;
443  $this->messages->enqueue(new FlashMessage(
444  '',
445  'Unpacked TYPO3 CMS core not found',
447  ));
448  } else {
449  $moveResult = rename($downloadedCoreLocation, $newCoreLocation);
450  if (!$moveResult) {
451  $success = false;
452  $this->messages->enqueue(new FlashMessage(
453  '',
454  'Moving TYPO3 CMS core to ' . $newCoreLocation . ' failed',
456  ));
457  } else {
458  $this->messages->enqueue(new FlashMessage(
459  '',
460  'Moved TYPO3 CMS core to final location'
461  ));
462  }
463  }
464  }
465  return $success;
466  }
467 
474  public function ‪activateVersion(CoreRelease $coreRelease)
475  {
476  $newCoreLocation = @realpath($this->symlinkToCoreFiles . '/../') . '/typo3_src-' . $coreRelease->getVersion();
477  $success = true;
478  if (!is_dir($newCoreLocation)) {
479  $success = false;
480  $this->messages->enqueue(new FlashMessage(
481  '',
482  'New TYPO3 CMS core not found',
484  ));
485  } elseif (!is_link($this->symlinkToCoreFiles)) {
486  $success = false;
487  $this->messages->enqueue(new FlashMessage(
488  '',
489  'TYPO3 CMS core source directory (typo3_src) is not a link',
491  ));
492  } else {
493  $isCurrentCoreSymlinkAbsolute = ‪PathUtility::isAbsolutePath(readlink($this->symlinkToCoreFiles));
494  $unlinkResult = unlink($this->symlinkToCoreFiles);
495  if (!$unlinkResult) {
496  $success = false;
497  $this->messages->enqueue(new FlashMessage(
498  '',
499  'Removing old symlink failed',
501  ));
502  } else {
503  if (!$isCurrentCoreSymlinkAbsolute) {
504  $newCoreLocation = $this->‪getRelativePath($newCoreLocation);
505  }
506  $symlinkResult = symlink($newCoreLocation, $this->symlinkToCoreFiles);
507  if ($symlinkResult) {
508  GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
509  } else {
510  $success = false;
511  $this->messages->enqueue(new FlashMessage(
512  '',
513  'Linking new TYPO3 CMS core failed',
515  ));
516  }
517  }
518  }
519  return $success;
520  }
521 
528  protected function ‪getDownloadTarGzTargetPath($version)
529  {
530  return $this->downloadTargetPath . $version . '.tar.gz';
531  }
532 
539  protected function ‪getRelativePath($absolutePath)
540  {
541  $sourcePath = explode(DIRECTORY_SEPARATOR, ‪Environment::getPublicPath());
542  $targetPath = explode(DIRECTORY_SEPARATOR, rtrim($absolutePath, DIRECTORY_SEPARATOR));
543  while (count($sourcePath) && count($targetPath) && $sourcePath[0] === $targetPath[0]) {
544  array_shift($sourcePath);
545  array_shift($targetPath);
546  }
547  return str_pad('', count($sourcePath) * 3, '..' . DIRECTORY_SEPARATOR) . implode(DIRECTORY_SEPARATOR, $targetPath);
548  }
549 
557  protected function ‪checkCoreFilesAvailable($version)
558  {
559  $newCoreLocation = @realpath($this->symlinkToCoreFiles . '/../') . '/typo3_src-' . $version;
560  return @is_dir($newCoreLocation);
561  }
562 }
‪TYPO3\CMS\Install\Service\CoreUpdateService\activateVersion
‪bool activateVersion(CoreRelease $coreRelease)
Definition: CoreUpdateService.php:469
‪TYPO3\CMS\Install\Service\CoreUpdateService\getRelativePath
‪string getRelativePath($absolutePath)
Definition: CoreUpdateService.php:534
‪TYPO3\CMS\Install\Service\CoreUpdateService\unpackVersion
‪bool unpackVersion(CoreRelease $coreRelease)
Definition: CoreUpdateService.php:359
‪TYPO3\CMS\Install\Service\CoreUpdateService\verifyFileChecksum
‪bool verifyFileChecksum(CoreRelease $coreRelease)
Definition: CoreUpdateService.php:310
‪TYPO3\CMS\Install\Service\CoreUpdateService\getMessages
‪FlashMessageQueue getMessages()
Definition: CoreUpdateService.php:119
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:23
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static string getPublicPath()
Definition: Environment.php:153
‪TYPO3\CMS\Core\Utility\PathUtility\stripPathSitePrefix
‪static string stripPathSitePrefix($path)
Definition: PathUtility.php:371
‪TYPO3\CMS\Install\Service\CoreUpdateService\$downloadBaseUri
‪string $downloadBaseUri
Definition: CoreUpdateService.php:65
‪TYPO3\CMS\Install\Service\CoreUpdateService\$symlinkToCoreFiles
‪string $symlinkToCoreFiles
Definition: CoreUpdateService.php:59
‪TYPO3\CMS\Core\Core\Environment\isWindows
‪static bool isWindows()
Definition: Environment.php:266
‪TYPO3\CMS\Install\Service\CoreUpdateService\downloadVersion
‪bool downloadVersion(CoreRelease $coreRelease)
Definition: CoreUpdateService.php:253
‪TYPO3\CMS\Install\FolderStructure\DefaultFactory
Definition: DefaultFactory.php:24
‪TYPO3\CMS\Install\Service\CoreUpdateService\checkCoreFilesAvailable
‪bool checkCoreFilesAvailable($version)
Definition: CoreUpdateService.php:552
‪TYPO3\CMS\Install\CoreVersion\CoreRelease
Definition: CoreRelease.php:21
‪TYPO3\CMS\Install\Service\CoreUpdateService\updateVersionMatrix
‪bool updateVersionMatrix()
Definition: CoreUpdateService.php:130
‪TYPO3\CMS\Core\Messaging\AbstractMessage\WARNING
‪const WARNING
Definition: AbstractMessage.php:28
‪TYPO3\CMS\Install\Service\CoreUpdateService\isCoreUpdateEnabled
‪bool isCoreUpdateEnabled()
Definition: CoreUpdateService.php:84
‪TYPO3\CMS\Install\Service\CoreUpdateService\discoverCurrentCoreSymlink
‪string discoverCurrentCoreSymlink()
Definition: CoreUpdateService.php:95
‪TYPO3\CMS\Install\Service\CoreUpdateService\$messages
‪FlashMessageQueue $messages
Definition: CoreUpdateService.php:47
‪TYPO3\CMS\Install\CoreVersion\CoreRelease\getChecksum
‪getChecksum()
Definition: CoreRelease.php:60
‪TYPO3\CMS\Install\Service\Exception\RemoteFetchException
Definition: RemoteFetchException.php:22
‪TYPO3\CMS\Core\Service\OpcodeCacheService
Definition: OpcodeCacheService.php:24
‪TYPO3\CMS\Core\Utility\PathUtility\isAbsolutePath
‪static bool isAbsolutePath($path)
Definition: PathUtility.php:222
‪TYPO3\CMS\Install\Service\CoreUpdateService\moveVersion
‪bool moveVersion(CoreRelease $coreRelease)
Definition: CoreUpdateService.php:422
‪$output
‪$output
Definition: annotationChecker.php:113
‪TYPO3\CMS\Install\Service\CoreUpdateService\getDownloadTarGzTargetPath
‪string getDownloadTarGzTargetPath($version)
Definition: CoreUpdateService.php:523
‪TYPO3\CMS\Install\Service\CoreUpdateService
Definition: CoreUpdateService.php:40
‪TYPO3\CMS\Core\Core\Environment\isComposerMode
‪static bool isComposerMode()
Definition: Environment.php:117
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:91
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:22
‪TYPO3\CMS\Install\Service\CoreUpdateService\$coreVersionService
‪TYPO3 CMS Install Service CoreVersionService $coreVersionService
Definition: CoreUpdateService.php:43
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:39
‪TYPO3\CMS\Core\Messaging\AbstractMessage\NOTICE
‪const NOTICE
Definition: AbstractMessage.php:25
‪TYPO3\CMS\Install\Service\CoreUpdateService\setDownloadTargetPath
‪setDownloadTargetPath($downloadTargetPath)
Definition: CoreUpdateService.php:106
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:21
‪TYPO3\CMS\Install\CoreVersion\CoreRelease\getVersion
‪getVersion()
Definition: CoreRelease.php:45
‪TYPO3\CMS\Core\Messaging\FlashMessageQueue
Definition: FlashMessageQueue.php:25
‪TYPO3\CMS\Install\Service
Definition: ClearCacheService.php:2
‪TYPO3\CMS\Install\Service\CoreUpdateService\checkPreConditions
‪bool checkPreConditions(CoreRelease $coreRelease)
Definition: CoreUpdateService.php:157
‪TYPO3\CMS\Install\Service\CoreUpdateService\$downloadTargetPath
‪string $downloadTargetPath
Definition: CoreUpdateService.php:53
‪TYPO3\CMS\Core\Messaging\AbstractMessage\ERROR
‪const ERROR
Definition: AbstractMessage.php:29
‪TYPO3\CMS\Install\Service\CoreUpdateService\__construct
‪__construct(CoreVersionService $coreVersionService=null)
Definition: CoreUpdateService.php:70
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static string getVarPath()
Definition: Environment.php:165