10d11ac7eSGreg Roach<?php 20d11ac7eSGreg Roach/** 30d11ac7eSGreg Roach * webtrees: online genealogy 4*8fcd0d32SGreg Roach * Copyright (C) 2019 webtrees development team 50d11ac7eSGreg Roach * This program is free software: you can redistribute it and/or modify 60d11ac7eSGreg Roach * it under the terms of the GNU General Public License as published by 70d11ac7eSGreg Roach * the Free Software Foundation, either version 3 of the License, or 80d11ac7eSGreg Roach * (at your option) any later version. 90d11ac7eSGreg Roach * This program is distributed in the hope that it will be useful, 100d11ac7eSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 110d11ac7eSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 120d11ac7eSGreg Roach * GNU General Public License for more details. 130d11ac7eSGreg Roach * You should have received a copy of the GNU General Public License 140d11ac7eSGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 150d11ac7eSGreg Roach */ 16e7f56f2aSGreg Roachdeclare(strict_types=1); 17e7f56f2aSGreg Roach 180d11ac7eSGreg Roachnamespace Fisharebest\Webtrees\Services; 190d11ac7eSGreg Roach 200d11ac7eSGreg Roachuse Fisharebest\Webtrees\Database; 210d11ac7eSGreg Roachuse Fisharebest\Webtrees\Site; 228d0ebef0SGreg Roachuse Fisharebest\Webtrees\Webtrees; 230d11ac7eSGreg Roachuse GuzzleHttp\Client; 240d11ac7eSGreg Roachuse GuzzleHttp\Exception\RequestException; 250d11ac7eSGreg Roachuse Symfony\Component\HttpFoundation\Response; 260d11ac7eSGreg Roach 270d11ac7eSGreg Roach/** 280d11ac7eSGreg Roach * Automatic upgrades. 290d11ac7eSGreg Roach */ 300d11ac7eSGreg Roachclass UpgradeService 310d11ac7eSGreg Roach{ 320d11ac7eSGreg Roach // Regular expression to match a version string such as "1.7.10" or "2.0.0-alpha.1" 330d11ac7eSGreg Roach const REGEX_VERSION = '\d+\.\d+\.\d+(-[a-z0-9.-]+)?'; 340d11ac7eSGreg Roach 350d11ac7eSGreg Roach // Only check the webtrees server infrequently. 360d11ac7eSGreg Roach const CHECK_FOR_UPDATE_INTERVAL = 24 * 60 * 60; 370d11ac7eSGreg Roach 380d11ac7eSGreg Roach // Fetch information about upgrades from here. 390d11ac7eSGreg Roach // Note: earlier versions of webtrees used svn.webtrees.net, so we must maintain both URLs. 400d11ac7eSGreg Roach const UPDATE_URL = 'https://dev.webtrees.net/build/latest-version.txt'; 410d11ac7eSGreg Roach 420d11ac7eSGreg Roach // If the update server doesn't respond after this time, give up. 430d11ac7eSGreg Roach const HTTP_TIMEOUT = 3.0; 440d11ac7eSGreg Roach 450d11ac7eSGreg Roach /** 460d11ac7eSGreg Roach * @return bool 470d11ac7eSGreg Roach */ 480d11ac7eSGreg Roach public function isUpgradeAvailable(): bool 490d11ac7eSGreg Roach { 500d11ac7eSGreg Roach // If the latest version is unavailable, we will have an empty sting which equates to version 0. 510d11ac7eSGreg Roach 528d0ebef0SGreg Roach return version_compare(Webtrees::VERSION, $this->fetchLatestVersion()) < 0; 530d11ac7eSGreg Roach } 540d11ac7eSGreg Roach 550d11ac7eSGreg Roach /** 560d11ac7eSGreg Roach * What is the latest version of webtrees. 570d11ac7eSGreg Roach * 580d11ac7eSGreg Roach * @return string 590d11ac7eSGreg Roach */ 600d11ac7eSGreg Roach public function latestVersion(): string 610d11ac7eSGreg Roach { 620d11ac7eSGreg Roach $latest_version = $this->fetchLatestVersion(); 630d11ac7eSGreg Roach 6465e02381SGreg Roach [$version] = explode('|', $latest_version); 650d11ac7eSGreg Roach 660d11ac7eSGreg Roach return $version; 670d11ac7eSGreg Roach } 680d11ac7eSGreg Roach 690d11ac7eSGreg Roach /** 700d11ac7eSGreg Roach * Where can we download the latest version of webtrees. 710d11ac7eSGreg Roach * 720d11ac7eSGreg Roach * @return string 730d11ac7eSGreg Roach */ 740d11ac7eSGreg Roach public function downloadUrl(): string 750d11ac7eSGreg Roach { 760d11ac7eSGreg Roach $latest_version = $this->fetchLatestVersion(); 770d11ac7eSGreg Roach 7865e02381SGreg Roach [, , $url] = explode('|', $latest_version . '||'); 790d11ac7eSGreg Roach 800d11ac7eSGreg Roach return $url; 810d11ac7eSGreg Roach } 820d11ac7eSGreg Roach 830d11ac7eSGreg Roach /** 840d11ac7eSGreg Roach * Check with the webtrees.net server for the latest version of webtrees. 850d11ac7eSGreg Roach * Fetching the remote file can be slow, so check infrequently, and cache the result. 860d11ac7eSGreg Roach * Pass the current versions of webtrees, PHP and MySQL, as the response 870d11ac7eSGreg Roach * may be different for each. The server logs are used to generate 880d11ac7eSGreg Roach * installation statistics which can be found at http://dev.webtrees.net/statistics.html 890d11ac7eSGreg Roach * 900d11ac7eSGreg Roach * @return string 910d11ac7eSGreg Roach */ 920d11ac7eSGreg Roach private function fetchLatestVersion(): string 930d11ac7eSGreg Roach { 940d11ac7eSGreg Roach $last_update_timestamp = (int) Site::getPreference('LATEST_WT_VERSION_TIMESTAMP'); 950d11ac7eSGreg Roach 960d11ac7eSGreg Roach if ($last_update_timestamp < WT_TIMESTAMP - self::CHECK_FOR_UPDATE_INTERVAL) { 970d11ac7eSGreg Roach try { 980d11ac7eSGreg Roach $client = new Client([ 990d11ac7eSGreg Roach 'timeout' => self::HTTP_TIMEOUT, 1000d11ac7eSGreg Roach ]); 1010d11ac7eSGreg Roach 1020d11ac7eSGreg Roach $response = $client->get(self::UPDATE_URL, [ 1030d11ac7eSGreg Roach 'query' => $this->serverParameters(), 1040d11ac7eSGreg Roach ]); 1050d11ac7eSGreg Roach 1060d11ac7eSGreg Roach if ($response->getStatusCode() === Response::HTTP_OK) { 1070d11ac7eSGreg Roach Site::setPreference('LATEST_WT_VERSION', $response->getBody()->getContents()); 108ebf05b8eSGreg Roach Site::setPreference('LATEST_WT_VERSION_TIMESTAMP', (string) WT_TIMESTAMP); 1090d11ac7eSGreg Roach } 1100d11ac7eSGreg Roach } catch (RequestException $ex) { 111c6a0ce5cSGreg Roach // Can't connect to the server? 112c6a0ce5cSGreg Roach // Use the existing information about latest versions. 1130d11ac7eSGreg Roach } 1140d11ac7eSGreg Roach } 1150d11ac7eSGreg Roach 1160d11ac7eSGreg Roach return Site::getPreference('LATEST_WT_VERSION'); 1170d11ac7eSGreg Roach } 1180d11ac7eSGreg Roach 1190d11ac7eSGreg Roach /** 1200d11ac7eSGreg Roach * The upgrade server needs to know a little about this server. 1210d11ac7eSGreg Roach */ 1228f53f488SRico Sonntag private function serverParameters(): array 1230d11ac7eSGreg Roach { 1240d11ac7eSGreg Roach $mysql_version = Database::prepare("SHOW VARIABLES LIKE 'version'")->fetchOneRow(); 1250d11ac7eSGreg Roach 1260d11ac7eSGreg Roach $operating_system = DIRECTORY_SEPARATOR === '/' ? 'u' : 'w'; 1270d11ac7eSGreg Roach 1280d11ac7eSGreg Roach return [ 1298d0ebef0SGreg Roach 'w' => Webtrees::VERSION, 1300d11ac7eSGreg Roach 'p' => PHP_VERSION, 1310d11ac7eSGreg Roach 'm' => $mysql_version->value, 1320d11ac7eSGreg Roach 'o' => $operating_system, 1330d11ac7eSGreg Roach ]; 1340d11ac7eSGreg Roach } 1350d11ac7eSGreg Roach} 136