1<?php 2/** 3 * webtrees: online genealogy 4 * Copyright (C) 2018 webtrees development team 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16declare(strict_types=1); 17 18namespace Fisharebest\Webtrees\Services; 19 20use Fisharebest\Webtrees\Database; 21use Fisharebest\Webtrees\DebugBar; 22use Fisharebest\Webtrees\Site; 23use Fisharebest\Webtrees\Webtrees; 24use GuzzleHttp\Client; 25use GuzzleHttp\Exception\RequestException; 26use Symfony\Component\HttpFoundation\Response; 27 28/** 29 * Automatic upgrades. 30 */ 31class UpgradeService 32{ 33 // Regular expression to match a version string such as "1.7.10" or "2.0.0-alpha.1" 34 const REGEX_VERSION = '\d+\.\d+\.\d+(-[a-z0-9.-]+)?'; 35 36 // Only check the webtrees server infrequently. 37 const CHECK_FOR_UPDATE_INTERVAL = 24 * 60 * 60; 38 39 // Fetch information about upgrades from here. 40 // Note: earlier versions of webtrees used svn.webtrees.net, so we must maintain both URLs. 41 const UPDATE_URL = 'https://dev.webtrees.net/build/latest-version.txt'; 42 43 // If the update server doesn't respond after this time, give up. 44 const HTTP_TIMEOUT = 3.0; 45 46 /** 47 * @return bool 48 */ 49 public function isUpgradeAvailable(): bool 50 { 51 // If the latest version is unavailable, we will have an empty sting which equates to version 0. 52 53 return version_compare(Webtrees::VERSION, $this->fetchLatestVersion()) < 0; 54 } 55 56 /** 57 * What is the latest version of webtrees. 58 * 59 * @return string 60 */ 61 public function latestVersion(): string 62 { 63 $latest_version = $this->fetchLatestVersion(); 64 65 [$version] = explode('|', $latest_version); 66 67 return $version; 68 } 69 70 /** 71 * Where can we download the latest version of webtrees. 72 * 73 * @return string 74 */ 75 public function downloadUrl(): string 76 { 77 $latest_version = $this->fetchLatestVersion(); 78 79 [, , $url] = explode('|', $latest_version . '||'); 80 81 return $url; 82 } 83 84 /** 85 * Check with the webtrees.net server for the latest version of webtrees. 86 * Fetching the remote file can be slow, so check infrequently, and cache the result. 87 * Pass the current versions of webtrees, PHP and MySQL, as the response 88 * may be different for each. The server logs are used to generate 89 * installation statistics which can be found at http://dev.webtrees.net/statistics.html 90 * 91 * @return string 92 */ 93 private function fetchLatestVersion(): string 94 { 95 $last_update_timestamp = (int) Site::getPreference('LATEST_WT_VERSION_TIMESTAMP'); 96 97 if ($last_update_timestamp < WT_TIMESTAMP - self::CHECK_FOR_UPDATE_INTERVAL) { 98 try { 99 $client = new Client([ 100 'timeout' => self::HTTP_TIMEOUT, 101 ]); 102 103 $response = $client->get(self::UPDATE_URL, [ 104 'query' => $this->serverParameters(), 105 ]); 106 107 if ($response->getStatusCode() === Response::HTTP_OK) { 108 Site::setPreference('LATEST_WT_VERSION', $response->getBody()->getContents()); 109 Site::setPreference('LATEST_WT_VERSION_TIMESTAMP', (string) WT_TIMESTAMP); 110 } 111 } catch (RequestException $ex) { 112 DebugBar::addThrowable($ex); 113 } 114 } 115 116 return Site::getPreference('LATEST_WT_VERSION'); 117 } 118 119 /** 120 * The upgrade server needs to know a little about this server. 121 */ 122 private function serverParameters(): array 123 { 124 $mysql_version = Database::prepare("SHOW VARIABLES LIKE 'version'")->fetchOneRow(); 125 126 $operating_system = DIRECTORY_SEPARATOR === '/' ? 'u' : 'w'; 127 128 return [ 129 'w' => Webtrees::VERSION, 130 'p' => PHP_VERSION, 131 'm' => $mysql_version->value, 132 'o' => $operating_system, 133 ]; 134 } 135} 136