ApplicationUtils.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <?php
  2. /**
  3. * Shaarli (application) utilities
  4. */
  5. class ApplicationUtils
  6. {
  7. private static $GIT_URL = 'https://raw.githubusercontent.com/shaarli/Shaarli';
  8. private static $GIT_BRANCHES = array('master', 'stable');
  9. private static $VERSION_FILE = 'shaarli_version.php';
  10. private static $VERSION_START_TAG = '<?php /* ';
  11. private static $VERSION_END_TAG = ' */ ?>';
  12. /**
  13. * Gets the latest version code from the Git repository
  14. *
  15. * The code is read from the raw content of the version file on the Git server.
  16. *
  17. * @return mixed the version code from the repository if available, else 'false'
  18. */
  19. public static function getLatestGitVersionCode($url, $timeout=2)
  20. {
  21. list($headers, $data) = get_http_response($url, $timeout);
  22. if (strpos($headers[0], '200 OK') === false) {
  23. error_log('Failed to retrieve ' . $url);
  24. return false;
  25. }
  26. return str_replace(
  27. array(self::$VERSION_START_TAG, self::$VERSION_END_TAG, PHP_EOL),
  28. array('', '', ''),
  29. $data
  30. );
  31. }
  32. /**
  33. * Checks if a new Shaarli version has been published on the Git repository
  34. *
  35. * Updates checks are run periodically, according to the following criteria:
  36. * - the update checks are enabled (install, global config);
  37. * - the user is logged in (or this is an open instance);
  38. * - the last check is older than a given interval;
  39. * - the check is non-blocking if the HTTPS connection to Git fails;
  40. * - in case of failure, the update file's modification date is updated,
  41. * to avoid intempestive connection attempts.
  42. *
  43. * @param string $currentVersion the current version code
  44. * @param string $updateFile the file where to store the latest version code
  45. * @param int $checkInterval the minimum interval between update checks (in seconds
  46. * @param bool $enableCheck whether to check for new versions
  47. * @param bool $isLoggedIn whether the user is logged in
  48. *
  49. * @throws Exception an invalid branch has been set for update checks
  50. *
  51. * @return mixed the new version code if available and greater, else 'false'
  52. */
  53. public static function checkUpdate($currentVersion,
  54. $updateFile,
  55. $checkInterval,
  56. $enableCheck,
  57. $isLoggedIn,
  58. $branch='stable')
  59. {
  60. if (! $isLoggedIn) {
  61. // Do not check versions for visitors
  62. return false;
  63. }
  64. if (empty($enableCheck)) {
  65. // Do not check if the user doesn't want to
  66. return false;
  67. }
  68. if (is_file($updateFile) && (filemtime($updateFile) > time() - $checkInterval)) {
  69. // Shaarli has checked for updates recently - skip HTTP query
  70. $latestKnownVersion = file_get_contents($updateFile);
  71. if (version_compare($latestKnownVersion, $currentVersion) == 1) {
  72. return $latestKnownVersion;
  73. }
  74. return false;
  75. }
  76. if (! in_array($branch, self::$GIT_BRANCHES)) {
  77. throw new Exception(
  78. 'Invalid branch selected for updates: "' . $branch . '"'
  79. );
  80. }
  81. // Late Static Binding allows overriding within tests
  82. // See http://php.net/manual/en/language.oop5.late-static-bindings.php
  83. $latestVersion = static::getLatestGitVersionCode(
  84. self::$GIT_URL . '/' . $branch . '/' . self::$VERSION_FILE
  85. );
  86. if (! $latestVersion) {
  87. // Only update the file's modification date
  88. file_put_contents($updateFile, $currentVersion);
  89. return false;
  90. }
  91. // Update the file's content and modification date
  92. file_put_contents($updateFile, $latestVersion);
  93. if (version_compare($latestVersion, $currentVersion) == 1) {
  94. return $latestVersion;
  95. }
  96. return false;
  97. }
  98. /**
  99. * Checks the PHP version to ensure Shaarli can run
  100. *
  101. * @param string $minVersion minimum PHP required version
  102. * @param string $curVersion current PHP version (use PHP_VERSION)
  103. *
  104. * @throws Exception the PHP version is not supported
  105. */
  106. public static function checkPHPVersion($minVersion, $curVersion)
  107. {
  108. if (version_compare($curVersion, $minVersion) < 0) {
  109. throw new Exception(
  110. 'Your PHP version is obsolete!'
  111. .' Shaarli requires at least PHP '.$minVersion.', and thus cannot run.'
  112. .' Your PHP version has known security vulnerabilities and should be'
  113. .' updated as soon as possible.'
  114. );
  115. }
  116. }
  117. /**
  118. * Checks Shaarli has the proper access permissions to its resources
  119. *
  120. * @param array $globalConfig The $GLOBALS['config'] array
  121. *
  122. * @return array A list of the detected configuration issues
  123. */
  124. public static function checkResourcePermissions($globalConfig)
  125. {
  126. $errors = array();
  127. // Check script and template directories are readable
  128. foreach (array(
  129. 'application',
  130. 'inc',
  131. 'plugins',
  132. $globalConfig['RAINTPL_TPL']
  133. ) as $path) {
  134. if (! is_readable(realpath($path))) {
  135. $errors[] = '"'.$path.'" directory is not readable';
  136. }
  137. }
  138. // Check cache and data directories are readable and writeable
  139. foreach (array(
  140. $globalConfig['CACHEDIR'],
  141. $globalConfig['DATADIR'],
  142. $globalConfig['PAGECACHE'],
  143. $globalConfig['RAINTPL_TMP']
  144. ) as $path) {
  145. if (! is_readable(realpath($path))) {
  146. $errors[] = '"'.$path.'" directory is not readable';
  147. }
  148. if (! is_writable(realpath($path))) {
  149. $errors[] = '"'.$path.'" directory is not writable';
  150. }
  151. }
  152. // Check configuration files are readable and writeable
  153. foreach (array(
  154. $globalConfig['CONFIG_FILE'],
  155. $globalConfig['DATASTORE'],
  156. $globalConfig['IPBANS_FILENAME'],
  157. $globalConfig['LOG_FILE'],
  158. $globalConfig['UPDATECHECK_FILENAME']
  159. ) as $path) {
  160. if (! is_file(realpath($path))) {
  161. # the file may not exist yet
  162. continue;
  163. }
  164. if (! is_readable(realpath($path))) {
  165. $errors[] = '"'.$path.'" file is not readable';
  166. }
  167. if (! is_writable(realpath($path))) {
  168. $errors[] = '"'.$path.'" file is not writable';
  169. }
  170. }
  171. return $errors;
  172. }
  173. }