setError(Text::get('systemUpdateItemsError')); return false; } if ($phpVersion = self::higherPHPVersionRequired()) { $Messenger->setError(Text::get('systemUpdatePhpVersionError') . " $phpVersion+."); return false; } if (!self::permissionsGranted($items)) { $Messenger->setError(Text::get('systemUpdatePermissionError')); return false; } self::log('Version to be updated: ' . AM_VERSION); Debug::log('Version to be updated: ' . AM_VERSION); self::log('Updating items: ' . implode(', ', $items)); $archive = self::getArchive(); if (!$archive) { $Messenger->setError(Text::get('systemUpdateDownloadError')); return false; } if (!self::backupCurrent($items)) { $Messenger->setError(Text::get('systemUpdateFailedError')); return false; } if (!self::unpack($archive, $items)) { $Messenger->setError(Text::get('systemUpdateFailedError')); return false; } if (file_exists(Cache::FILE_SITE_MTIME)) { unlink(Cache::FILE_SITE_MTIME); } $version = ''; $versionFile = AM_BASE_DIR . '/automad/version.php'; if (is_readable($versionFile)) { $version = self::extractVersion(file_get_contents($versionFile)); } self::log('Successfully updated Automad to version ' . $version); Debug::log('Successfully updated Automad to version ' . $version); $Messenger->setData(array('current' => $version, 'state' => 'success')); $Messenger->setSuccess(Text::get('systemUpdateSuccess')); return true; } /** * Test if the server supports all required functions. * * @return bool True on success, false on error */ public static function supported(): bool { return (function_exists('curl_version') && class_exists('ZipArchive')); } /** * Move currently installed items to /cache/update/backup. * * @param array $items * @param string $str * @return bool True on success, false on error */ private static function backupCurrent(array $items): bool { $backup = AM_BASE_DIR . AM_UPDATE_TEMP . '/backup/' . self::$timestamp; FileSystem::makeDir($backup); foreach ($items as $item) { $itemPath = AM_BASE_DIR . $item; $backupPath = $backup . $item; // Only try to backup in case item exists. if (file_exists($itemPath)) { if (is_writable($itemPath) && is_writable(dirname($itemPath))) { FileSystem::makeDir(dirname($backupPath)); $success = rename($itemPath, $backupPath); self::log('Backing up ' . Str::stripStart($itemPath, AM_BASE_DIR) . ' to ' . Str::stripStart($backupPath, AM_BASE_DIR)); } else { $success = false; } if (!$success) { return false; } } } return true; } /** * Extract version number form content of version.php. * * @param string $str * @return string The version number */ private static function extractVersion(string $str): string { if (preg_match('/\d[^\'"]+/', $str, $matches)) { return $matches[0]; } return ''; } /** * Download zip-archive to be installed. * * @return string|null Path to the downloaded archive or null on error */ private static function getArchive(): ?string { $downloadUrl = AM_UPDATE_REPO_DOWNLOAD_URL . '/' . AM_UPDATE_BRANCH . '.zip'; $archive = AM_BASE_DIR . AM_UPDATE_TEMP . '/download/' . self::$timestamp . '.zip'; FileSystem::makeDir(dirname($archive)); if (!Fetch::download($downloadUrl, $archive)) { $archive = null; self::log('Download failed!'); } return $archive; } /** * Check if the server's PHP version matches the minimum requirements in the remote composer.json file. * * @return string a version number in case PHP is outdated or an empty string */ private static function higherPHPVersionRequired(): string { $requiredVersion = ''; $composerFileUrl = AM_UPDATE_REPO_RAW_URL . '/' . AM_UPDATE_BRANCH . '/composer.json'; $composerJson = Fetch::get($composerFileUrl); if (!$composerJson) { return $requiredVersion; } try { $data = json_decode($composerJson); $composerRequiredVersion = preg_replace('/[^\d\.]*/', '', $data->require->php); self::log("The required PHP version is $composerRequiredVersion"); Debug::log("The required PHP version is $composerRequiredVersion"); if (version_compare(PHP_VERSION, $composerRequiredVersion, '<')) { $requiredVersion = $composerRequiredVersion; self::log("The server's PHP version in outdated!"); } } catch (\Exception $e) { self::log($e->getMessage()); } return $requiredVersion; } /** * Log events to the update log file. * * @param string $data * @return string The path to the log file */ private static function log(string $data): string { $file = AM_BASE_DIR . AM_UPDATE_TEMP . '/' . self::$timestamp . '.log'; FileSystem::makeDir(dirname($file)); file_put_contents($file, $data . "\r\n", FILE_APPEND); return $file; } /** * Test if permissions for all items to be updated are granted. * * @param array $items * @return bool True on success, false on error */ private static function permissionsGranted(array $items): bool { foreach ($items as $item) { $item = AM_BASE_DIR . $item; $temp = $item . '.' . crc32($item); if (!@rename($item, $temp)) { return false; } rename($temp, $item); } return true; } /** * Preload required classes before removing old installation. */ private static function preloadClasses(): void { Text::getObject(); } /** * Unpack all item matching AM_UPDATE_ITEM. * * @param string $archive * @param array $items * @return bool True on success, false on error */ private static function unpack(string $archive, array $items): bool { $success = true; $zip = new \ZipArchive(); $itemsMatchRegex = '/^[\w\-]+(' . addcslashes(implode('|', $items), '/') . ')/'; if ($zip->open($archive)) { // Iterate over zip entries and unpack item in case // the name matches on of the update items. for ($i = 0; $i < $zip->numFiles; $i++) { $name = $zip->getNameIndex($i); if (preg_match($itemsMatchRegex, $name)) { $filename = AM_BASE_DIR . preg_replace('/^([\w\-]+)/', '', $name); if (FileSystem::write($filename, $zip->getFromName($name)) !== false) { self::log('Extracted ' . Str::stripStart($filename, AM_BASE_DIR)); } else { self::log('Error extracting ' . Str::stripStart($filename, AM_BASE_DIR)); $success = false; } } } $zip->close(); } else { $success = false; } unlink($archive); return $success; } } __halt_compiler();----SIGNATURE:----Nl67xg7FT0L9tfZA8pFDIvaWwefXcel0oY/Vr2c8VXYy+aLnTXaHGv2XdA0hj8StZVQ7XdkjdDXvuwFKILt/U1vsugETjKCj828AZm6B1tqlyK3XCU6HqjoljjY3l4b4h6ovDt4X+l/n+07ectHsx9+4hrgkDDlwVV9NDuFz7IZ9CaNw0dKx+y3nm3g9CX6qFUHRMEmn8emMzEGrTYNF4rcvM2a6b4slP1EI+kVJF8ny5pCzVw1XP8hiD8rzYprpHFxGMwk9tMJaE0uFjkw/+P9NZXSWSoksZV0vbd8iP+y6PWRwE+nlQnfomzfmB/HDc/+wYRieCkwBdIWXmRSmwuvCEpo0tBNMt2AcSvLzz2uF7Q9TYZ/g72JggOKjrwTLUoFioIrFOHgWSQ1XEyEuoXXBemQgeSE2XeXVP0ktpfQ3zk8zxRtyLmorw9LxkyGGkKxbs0zZFi5kJS4dIgF4qXzLyVxwAXhnLCZ2oJMLqc8G8TRDKO8831WbNJ7FB7R8Sune1mg22RXqxvGsdxlDLWzj/QEHn9+OR1SrlNdvUr1LKUImERzvEmswrdovwMAcqfRwO48ZWQned0X3qWI2EMPS1y1ne+G0/ulA5C/NlKF9HD/LlezoxIb3xwxUFWvcXxT85x8BSMzekwfNwwKElpwFdqYi4LQ4u7pS+8L7eGQ=----ATTACHMENT:----MTUzMDI0Nzg1ODM1OTEwMCA3NDAxMTIyMDQ0MTA3MzczIDQ5MzMzNTg3ODY3OTM0ODY=