dryRun) { include $phpBBRootPath . 'config.php'; /** @var String $dbhost */ /** @var String $dbport */ /** @var String $dbname */ /** @var String $dbuser */ /** @var String $dbpasswd */ $pdo = new PDO( dsn: "mysql:host=$dbhost;port=$dbport;charset=utf8mb4;dbname=$dbname", username: $dbuser, password: $dbpasswd ); /** @var String $table_prefix */ $sql = "SELECT config_value FROM {$table_prefix}config WHERE config_name = 'version'"; $statement = $pdo->prepare(query: $sql); $statement->execute(); $result = $statement->fetch(); $installedVersion = $result['config_value']; print("Installed version: phpBB $installedVersion" . PHP_EOL); // phpBB has major, minor and maintenance version scheme [$major, $minor, $patch] = explode(separator: '.', string: $installedVersion); if ((intval(value: $major) != SUPPORTED_RELEASE_MAJOR) || (intval(value: $minor) < SUPPORTED_RELEASE_MINOR)) { echo 'This script only supports phpBB ' . SUPPORTED_RELEASE_MAJOR . '.' . SUPPORTED_RELEASE_MINOR . ' and above branch.', PHP_EOL; exit(1); } } echo "Checking for the current version …" . PHP_EOL; $json = file_get_contents(filename: 'https://version.phpbb.com/phpbb/versions.json'); $versions = json_decode(json: $json, associative: true); $stableVersions = $versions['stable']; // Get the highest stable version $highestStableVersion = null; foreach ($stableVersions as $version => $details) { if ($highestStableVersion === null || version_compare(version1: $version, version2: $highestStableVersion, operator: '>')) { $highestStableVersion = $version; } } echo "Highest Stable Version: $highestStableVersion" . PHP_EOL; if (!$this->dryRun) { echo 'Installed Version: '. $this->installedVersion . PHP_EOL; } [$major, $minor, $patch] = explode(separator: '.', string: $stableVersions[$highestStableVersion]['current']); echo "Latest stable release: $major.$minor.$patch" . PHP_EOL; $availableUpdate = $stableVersions[$highestStableVersion]['current']; // check for existing update if (!file_exists(filename: 'dist')) { echo "'dist' folder is missing, create a new one …'"; mkdir(directory: 'dist'); } $currentFile = "phpBB-$availableUpdate.tar.bz2"; $phpBBTarget = "dist/$currentFile"; if (!file_exists(filename: $phpBBTarget)) { echo "Downloading $currentFile" . PHP_EOL; $filePath = "https://download.phpbb.com/pub/release/$major.$minor/$availableUpdate/$currentFile"; $phpBBtbz = file_get_contents(filename: $filePath); file_put_contents(filename: $phpBBTarget, data: $phpBBtbz); } else { echo $currentFile . ' already exists. Skipping download.' . PHP_EOL; } // TODO check SHA256? // check for available language files $useLangDeDu = false; if (file_exists(filename: $phpBBRootPath . 'language/de')) { $useLangDeDu = true; // https://downloads.phpbb.de/pakete/deutsch/3.3/3.3.10/phpBB_lang_de-3.3.10.tar.bz2 $languageFile = "phpBB_lang_de-$availableUpdate.tar.bz2"; echo "Language file: $languageFile."; $langDeDuTarget = "dist/$languageFile"; if (!file_exists(filename: $langDeDuTarget)) { echo " Downloading $languageFile" . PHP_EOL; $filePath = "https://downloads.phpbb.de/pakete/deutsch/$major.$minor/$availableUpdate/$languageFile"; // https://downloads.phpbb.de/pakete/deutsch/3.3/3.3.10/phpBB_lang_de-3.3.10.tar.bz2 // https://downloads.phpbb.de/pakete/deutsch/3.3/3.3.10/phpBB_lang_de-3.3.10.tar.bz2 echo "File path: $filePath"; if ($phpBBtbz = file_get_contents(filename: $filePath)) { file_put_contents(filename: $langDeDuTarget, data: $phpBBtbz); } else { echo "Language file $languageFile does not exist." . PHP_EOL; $useLangDeDu = false; } } else { echo 'Language file ' . $languageFile . ' already exists' . PHP_EOL; } } else { echo 'Language Deutsch "Du" ist not installed, skipping' . PHP_EOL; } $useLangDeSie = false; if (file_exists(filename: $phpBBRootPath . 'language/de_x_sie')) { $useLangDeSie = true; $languageFile = "phpBB_lang_de_x_sie-$availableUpdate.tar.bz2"; echo "Language file: $languageFile."; $langDeSieTarget = "dist/$languageFile"; if (!file_exists(filename: $langDeSieTarget)) { echo "Downloading language $languageFile" . PHP_EOL; $filePath = "https://downloads.phpbb.de/pakete/deutsch/$major.$minor/$availableUpdate/$languageFile"; if (file_exists(filename: $filePath)) { $phpBBtbz = file_get_contents(filename: $filePath); file_put_contents(filename: $langDeSieTarget, data: $phpBBtbz); } else { echo 'Language file ' . $languageFile . ' does not exist' . PHP_EOL; echo "Error fetching: $filePath" .PHP_EOL; $useLangDeSie = false; } } else { echo 'Language file ' . $languageFile . ' already exists' . PHP_EOL; } } else { echo 'Language Deutsch "Sie" ist not installed, skipping' . PHP_EOL; } die; if (!$this->confirm(message: 'Do you want to proceed with the update now?')) { exit(0); } if ($this->dryRun) { echo 'Dry run, exiting.' . PHP_EOL; exit(0); } // ok, start update $now = date(format: 'd.m.Y H:i'); $disableMsg = "Software-update at $now, the forum ist down due to maintenance. We'll be back soon."; $sql = "UPDATE {$table_prefix}config SET config_value = :disable_message WHERE config_name = 'board_disable_msg'"; $statement = $pdo->prepare(query: $sql); $statement->bindParam(param: 'disable_message', var: $disableMsg); if ($statement->execute()) { echo "Disable Message set …", PHP_EOL; } else { echo 'There was an error talking to the DB.' . PHP_EOL; echo 'Failed SQL-statement: ' . $sql . PHP_EOL; exit(1); } $sql = "UPDATE {$table_prefix}config SET config_value = '1' WHERE config_name = 'board_disable'"; $statement = $pdo->prepare(query: $sql); if ($statement->execute()) { echo "Board disabled …", PHP_EOL; } else { echo 'There was an error talking to the DB.' . PHP_EOL; echo 'Failed SQL-statement: ' . $sql . PHP_EOL; exit(1); } $extensionsFile = 'extensions.txt'; if (file_exists(filename: $extensionsFile)) { echo 'Extensions state already stored. Remove extensions.txt if you wish to recreate it.' . PHP_EOL; } else { // check for enabled extensions $sql = "SELECT ext_name FROM {$table_prefix}ext WHERE ext_active = '1'"; $statement = $pdo->prepare(query: $sql); if ($statement->execute()) { $result = $statement->fetchAll(); if (count(value: $result) > 0) { $extensions = json_encode(value: $result); // safe enabled extensions $oFile = fopen(filename: 'extensions.txt', mode: 'w'); fputs(stream: $oFile, data: $extensions); fclose(stream: $oFile); echo 'Stored extensions state'; // disable all extensions $sql = "UPDATE {$table_prefix}ext SET ext_active = '0' WHERE ext_active = '1'"; $statement = $pdo->prepare(query: $sql); if ($statement->execute()) { echo 'Disabled all extensions'; } } } } $stylesFile = 'styles.txt'; if (file_exists(filename: $stylesFile)) { echo 'Styles state already stored. Remove styles.txt if you wish to recreate it' . PHP_EOL; } else { // check for enabled style $sql = "SELECT style_name FROM {$table_prefix}styles WHERE style_active = '1'"; $statement = $pdo->prepare(query: $sql); if ($statement->execute()) { $result = $statement->fetchAll(); if (count(value: $result) > 0) { $styles = json_encode(value: $result); // safe enabled styles $oFile = fopen(filename: 'styles.txt', mode: 'w'); fputs(stream: $oFile, data: $styles); fclose(stream: $oFile); echo 'Stored styles state.' . PHP_EOL; // disable all styles except prosilver $sql = "UPDATE {$table_prefix}styles SET style_active = '0' WHERE NOT style_name = 'prosilver'"; $statement = $pdo->prepare(query: $sql); if ($statement->execute()) { echo 'Disabled all styles except prosilver.' . PHP_EOL; } } } } // update phpBB $data = new PharData(filename: $phpBBTarget); try { unset($data['phpBB3/config.php']); unset($data['phpBB3/.htaccess']); } catch (Exception $e) { echo 'error: ', $e; } // remove all old files $excludes = [ 'config.php', '.htaccess', '.htpasswd', 'images', 'files', 'ext', 'styles', 'store', 'updates', 'mobiquo']; // this will fuck up nearly all modified boards, leave the files alone by default. // It will also destroy any API-keys and whatever you might have in your document root. // deleteDirectory($phpbb_root_path, $excludes); try { $data->extractTo(directory: $phpBBRootPath); $fromDir = $phpBBRootPath . 'phpBB3/'; $toDir = $phpBBRootPath; $this->copyDirectory(source: $fromDir, target: $toDir); $this->deleteDirectory(dir: $fromDir); } catch (Exception $e) { print("Error while extracting $data: $e"); exit(1); } echo 'Moved the update in place.', PHP_EOL; $fileOwner = fileowner(filename: $phpBBRootPath); $fileGroup = filegroup(filename: $phpBBRootPath); $fileOwnerName = posix_getpwuid(user_id: $fileOwner)['name']; $fileGroupName = posix_getgrgid(group_id: $fileGroup)['name']; echo 'Check file owner', PHP_EOL; print("You might need to perform 'chown -R $fileOwnerName:$fileGroupName $phpBBRootPath'" . PHP_EOL); echo 'prepare config.yml.', PHP_EOL; $oFile = fopen(filename: $phpBBRootPath . '/update-config.yml', mode: 'w'); fputs(stream: $oFile, data: 'updater:' . PHP_EOL . " type: db_only" . PHP_EOL); fclose(stream: $oFile); $command = <<deleteDirectory(dir: $installDir); } // update langDeDu if ($useLangDeDu) { $data = new PharData(filename: $langDeDuTarget); try { $data->extractTo(directory: $phpBBRootPath, overwrite: true); } catch (Exception $e) { print("Error while extracting $langDeDuTarget: $e"); exit(1); } } // update langDeSie if ($useLangDeSie) { $data = new PharData(filename: $langDeSieTarget); try { $data->extractTo(directory: $phpBBRootPath, overwrite: true); } catch (Exception $e) { print("Error while extracting $langDeSieTarget: $e"); exit(1); } } $sql = "UPDATE {$table_prefix}config SET config_value = '0' WHERE config_name = 'board_disable'"; $statement = $pdo->prepare(query: $sql); $statement->execute(); echo "Board reenabled …", PHP_EOL; if (file_exists(filename: $extensionsFile)) { $iFile = fopen(filename: $extensionsFile, mode: 'r'); $extensions = json_decode(json: fgets(stream: $iFile), associative: true); echo 'Enable extensions: '; foreach ($extensions as $extension) { $ext = $extension['ext_name']; $sql = "UPDATE {$table_prefix}ext SET ext_active = '1' WHERE ext_name = '$ext'"; $statement = $pdo->prepare(query: $sql); $statement->execute(); echo '.'; } echo 'done.', PHP_EOL; } else { echo 'There are no saved extension information available.', PHP_EOL; } if (file_exists(filename: $stylesFile)) { $iFile = fopen(filename: $stylesFile, mode: 'r'); $styles = json_decode(json: fgets(stream: $iFile), associative: true); echo 'Enable styles: '; foreach ($styles as $style) { $style = $style['style_name']; $sql = "UPDATE {$table_prefix}styles SET style_active = '1' WHERE style_name = '$style'"; $statement = $pdo->prepare(query: $sql); $statement->execute(); echo '.'; } echo 'done.', PHP_EOL; } else { echo 'There are no saved extension information available.', PHP_EOL; } // clear cache $dataGlobalCache = $phpBBRootPath . '/cache/data_global.' . $phpEx; if (file_exists(filename: $dataGlobalCache)) { unlink(filename: $dataGlobalCache); echo "Cache cleared …"; } echo "Your board should now be up and running." . PHP_EOL; } /** * @param String $message * @param string[] $options * @param string $default * * @return bool */ function confirm(string $message = 'Are you sure? ', array $options = ['y', 'n'], string $default = 'n'): bool { // first $options means true, any other false echo $message, ' ('; $first = true; foreach ($options as $option) { // mark default if ($option == $default) { $option = strtoupper(string: $option); } if ($first) { echo $option; $first = false; } else { echo '/', $option; } } echo '): '; $handle = fopen(filename: "php://stdin", mode: 'r'); $line = trim(string: fgetc(stream: $handle)); fclose(stream: $handle); if ($line == '') { // enter $line = $default; } if ($line == $options[0]) { $result = true; } else { $result = false; } return $result; } /** * @param $dir * @param array $excludes * * @return false|void */ function deleteDirectory($dir, array $excludes = []) { if (!file_exists(filename: $dir)) { return false; } $dir = rtrim(string: $dir, characters: '/') . '/'; static $skip = false; $entries = glob(pattern: $dir . '{,.}[!.,!..]*', flags: GLOB_MARK | GLOB_BRACE); foreach ($entries as $entry) { if (!in_array(needle: basename(path: $entry), haystack: $excludes)) { if (is_dir(filename: $entry)) { $this->deleteDirectory(dir: $entry); } else { unlink(filename: $entry); } } else { $skip = true; } } if (!$skip) { rmdir(directory: $dir); } } /** * @param $source * @param $target * * @return bool|void */ function copyDirectory($source, $target) { if (!file_exists(filename: $source)) { die("missing source: $source"); } if (is_file(filename: $source)) { if (copy(from: $source, to: $target)) { return true; } else { return false; } } if (is_dir(filename: $source)) { if (!file_exists(filename: $target)) { mkdir(directory: $target); } $source = rtrim(string: $source, characters: '/') . '/'; $target = rtrim(string: $target, characters: '/') . '/'; $dir = new DirectoryIterator(directory: $source); foreach ($dir as $entry) { if (!$entry->isDot()) { $this->copyDirectory(source: "$source$entry", target: "$target$entry"); } } } // ignore links, not part of phpBB arch } public function printHelp(): void { echo "Usage: php update.php [options]", PHP_EOL; echo "Options:", PHP_EOL; echo "-h --help Print this help", PHP_EOL; echo "-d --dry-run Just check for downloadable files, don't connect to database.", PHP_EOL; } public function setDryRun(): void { $this->dryRun = true; } }