X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=application%2FApplicationUtils.php;h=bd1c7cf3f42a06a427d817821c5c9a058f549aba;hb=e6215a2ad97182efcf88ef532ec6bd65ae35fd19;hp=37deb4b3fd50540fe4d62a519cf83242c5262aad;hpb=da10377b3c263d96a46cf9101c202554343d2cd0;p=github%2Fshaarli%2FShaarli.git diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php index 37deb4b3..bd1c7cf3 100644 --- a/application/ApplicationUtils.php +++ b/application/ApplicationUtils.php @@ -1,12 +1,22 @@ '; @@ -15,9 +25,12 @@ class ApplicationUtils * * The code is read from the raw content of the version file on the Git server. * + * @param string $url URL to reach to get the latest version. + * @param int $timeout Timeout to check the URL (in seconds). + * * @return mixed the version code from the repository if available, else 'false' */ - public static function getLatestGitVersionCode($url, $timeout=2) + public static function getLatestGitVersionCode($url, $timeout = 2) { list($headers, $data) = get_http_response($url, $timeout); @@ -26,6 +39,30 @@ class ApplicationUtils return false; } + return $data; + } + + /** + * Retrieve the version from a remote URL or a file. + * + * @param string $remote URL or file to fetch. + * @param int $timeout For URLs fetching. + * + * @return bool|string The version or false if it couldn't be retrieved. + */ + public static function getVersion($remote, $timeout = 2) + { + if (startsWith($remote, 'http')) { + if (($data = static::getLatestGitVersionCode($remote, $timeout)) === false) { + return false; + } + } else { + if (!is_file($remote)) { + return false; + } + $data = file_get_contents($remote); + } + return str_replace( array(self::$VERSION_START_TAG, self::$VERSION_END_TAG, PHP_EOL), array('', '', ''), @@ -49,25 +86,24 @@ class ApplicationUtils * @param int $checkInterval the minimum interval between update checks (in seconds * @param bool $enableCheck whether to check for new versions * @param bool $isLoggedIn whether the user is logged in + * @param string $branch check update for the given branch * * @throws Exception an invalid branch has been set for update checks * * @return mixed the new version code if available and greater, else 'false' */ - public static function checkUpdate($currentVersion, - $updateFile, - $checkInterval, - $enableCheck, - $isLoggedIn, - $branch='stable') - { - if (! $isLoggedIn) { - // Do not check versions for visitors - return false; - } - - if (empty($enableCheck)) { - // Do not check if the user doesn't want to + public static function checkUpdate( + $currentVersion, + $updateFile, + $checkInterval, + $enableCheck, + $isLoggedIn, + $branch = 'stable' + ) { + // Do not check versions for visitors + // Do not check if the user doesn't want to + // Do not check with dev version + if (!$isLoggedIn || empty($enableCheck) || $currentVersion === 'dev') { return false; } @@ -81,7 +117,7 @@ class ApplicationUtils return false; } - if (! in_array($branch, self::$GIT_BRANCHES)) { + if (!in_array($branch, self::$GIT_BRANCHES)) { throw new Exception( 'Invalid branch selected for updates: "' . $branch . '"' ); @@ -89,11 +125,11 @@ class ApplicationUtils // Late Static Binding allows overriding within tests // See http://php.net/manual/en/language.oop5.late-static-bindings.php - $latestVersion = static::getLatestGitVersionCode( - self::$GIT_URL . '/' . $branch . '/' . self::$VERSION_FILE + $latestVersion = static::getVersion( + self::$GIT_RAW_URL . '/' . $branch . '/' . self::$VERSION_FILE ); - if (! $latestVersion) { + if (!$latestVersion) { // Only update the file's modification date file_put_contents($updateFile, $currentVersion); return false; @@ -115,78 +151,164 @@ class ApplicationUtils * @param string $minVersion minimum PHP required version * @param string $curVersion current PHP version (use PHP_VERSION) * + * @return bool true on success + * * @throws Exception the PHP version is not supported */ public static function checkPHPVersion($minVersion, $curVersion) { if (version_compare($curVersion, $minVersion) < 0) { - throw new Exception( + $msg = t( 'Your PHP version is obsolete!' - .' Shaarli requires at least PHP '.$minVersion.', and thus cannot run.' - .' Your PHP version has known security vulnerabilities and should be' - .' updated as soon as possible.' + . ' Shaarli requires at least PHP %s, and thus cannot run.' + . ' Your PHP version has known security vulnerabilities and should be' + . ' updated as soon as possible.' ); + throw new Exception(sprintf($msg, $minVersion)); } + return true; } /** * Checks Shaarli has the proper access permissions to its resources * + * @param ConfigManager $conf Configuration Manager instance. + * @param bool $minimalMode In minimal mode we only check permissions to be able to display a template. + * Currently we only need to be able to read the theme and write in raintpl cache. + * * @return array A list of the detected configuration issues */ - public static function checkResourcePermissions() + public static function checkResourcePermissions(ConfigManager $conf, bool $minimalMode = false): array { - $errors = array(); - $conf = ConfigManager::getInstance(); + $errors = []; + $rainTplDir = rtrim($conf->get('resource.raintpl_tpl'), '/'); // Check script and template directories are readable - foreach (array( - 'application', - 'inc', - 'plugins', - $conf->get('path.raintpl_tpl'), - ) as $path) { - if (! is_readable(realpath($path))) { - $errors[] = '"'.$path.'" directory is not readable'; + foreach ([ + 'application', + 'inc', + 'plugins', + $rainTplDir, + $rainTplDir . '/' . $conf->get('resource.theme'), + ] as $path) { + if (!is_readable(realpath($path))) { + $errors[] = '"' . $path . '" ' . t('directory is not readable'); } } - // Check cache and data directories are readable and writeable - foreach (array( - $conf->get('path.thumbnails_cache'), - $conf->get('path.data_dir'), - $conf->get('path.page_cache'), - $conf->get('path.raintpl_tmp'), - ) as $path) { - if (! is_readable(realpath($path))) { - $errors[] = '"'.$path.'" directory is not readable'; + // Check cache and data directories are readable and writable + if ($minimalMode) { + $folders = [ + $conf->get('resource.raintpl_tmp'), + ]; + } else { + $folders = [ + $conf->get('resource.thumbnails_cache'), + $conf->get('resource.data_dir'), + $conf->get('resource.page_cache'), + $conf->get('resource.raintpl_tmp'), + ]; + } + + foreach ($folders as $path) { + if (!is_readable(realpath($path))) { + $errors[] = '"' . $path . '" ' . t('directory is not readable'); } - if (! is_writable(realpath($path))) { - $errors[] = '"'.$path.'" directory is not writable'; + if (!is_writable(realpath($path))) { + $errors[] = '"' . $path . '" ' . t('directory is not writable'); } } - // Check configuration files are readable and writeable + if ($minimalMode) { + return $errors; + } + + // Check configuration files are readable and writable foreach (array( - $conf->getConfigFile(), - $conf->get('path.datastore'), - $conf->get('path.ban_file'), - $conf->get('path.log'), - $conf->get('path.update_check'), - ) as $path) { - if (! is_file(realpath($path))) { + $conf->getConfigFileExt(), + $conf->get('resource.datastore'), + $conf->get('resource.ban_file'), + $conf->get('resource.log'), + $conf->get('resource.update_check'), + ) as $path) { + if (!is_file(realpath($path))) { # the file may not exist yet continue; } - if (! is_readable(realpath($path))) { - $errors[] = '"'.$path.'" file is not readable'; + if (!is_readable(realpath($path))) { + $errors[] = '"' . $path . '" ' . t('file is not readable'); } - if (! is_writable(realpath($path))) { - $errors[] = '"'.$path.'" file is not writable'; + if (!is_writable(realpath($path))) { + $errors[] = '"' . $path . '" ' . t('file is not writable'); } } return $errors; } + + /** + * Returns a salted hash representing the current Shaarli version. + * + * Useful for assets browser cache. + * + * @param string $currentVersion of Shaarli + * @param string $salt User personal salt, also used for the authentication + * + * @return string version hash + */ + public static function getVersionHash($currentVersion, $salt) + { + return hash_hmac('sha256', $currentVersion, $salt); + } + + /** + * Get a list of PHP extensions used by Shaarli. + * + * @return array[] List of extension with following keys: + * - name: extension name + * - required: whether the extension is required to use Shaarli + * - desc: short description of extension usage in Shaarli + * - loaded: whether the extension is properly loaded or not + */ + public static function getPhpExtensionsRequirement(): array + { + $extensions = [ + ['name' => 'json', 'required' => true, 'desc' => t('Configuration parsing')], + ['name' => 'simplexml', 'required' => true, 'desc' => t('Slim Framework (routing, etc.)')], + ['name' => 'mbstring', 'required' => true, 'desc' => t('Multibyte (Unicode) string support')], + ['name' => 'gd', 'required' => false, 'desc' => t('Required to use thumbnails')], + ['name' => 'intl', 'required' => false, 'desc' => t('Localized text sorting (e.g. e->è->f)')], + ['name' => 'curl', 'required' => false, 'desc' => t('Better retrieval of bookmark metadata and thumbnail')], + ['name' => 'gettext', 'required' => false, 'desc' => t('Use the translation system in gettext mode')], + ['name' => 'ldap', 'required' => false, 'desc' => t('Login using LDAP server')], + ]; + + foreach ($extensions as &$extension) { + $extension['loaded'] = extension_loaded($extension['name']); + } + + return $extensions; + } + + /** + * Return the EOL date of given PHP version. If the version is unknown, + * we return today + 2 years. + * + * @param string $fullVersion PHP version, e.g. 7.4.7 + * + * @return string Date format: YYYY-MM-DD + */ + public static function getPhpEol(string $fullVersion): string + { + preg_match('/(\d+\.\d+)\.\d+/', $fullVersion, $matches); + + return [ + '7.1' => '2019-12-01', + '7.2' => '2020-11-30', + '7.3' => '2021-12-06', + '7.4' => '2022-11-28', + '8.0' => '2023-12-01', + ][$matches[1]] ?? (new \DateTime('+2 year'))->format('Y-m-d'); + } }