PageRenderTime 39ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/core/Filechecks.php

https://github.com/CodeYellowBV/piwik
PHP | 248 lines | 164 code | 34 blank | 50 comment | 37 complexity | 9470430ecf65473c866a3c6bcb7b6bb2 MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik;
  10. class Filechecks
  11. {
  12. /**
  13. * Check if this installation can be auto-updated.
  14. * For performance, we look for clues rather than an exhaustive test.
  15. *
  16. * @return bool
  17. */
  18. public static function canAutoUpdate()
  19. {
  20. if (!is_writable(PIWIK_INCLUDE_PATH . '/') ||
  21. !is_writable(PIWIK_DOCUMENT_ROOT . '/index.php') ||
  22. !is_writable(PIWIK_INCLUDE_PATH . '/core') ||
  23. !is_writable(PIWIK_USER_PATH . '/config/global.ini.php')
  24. ) {
  25. return false;
  26. }
  27. return true;
  28. }
  29. /**
  30. * Checks if directories are writable and create them if they do not exist.
  31. *
  32. * @param array $directoriesToCheck array of directories to check - if not given default Piwik directories that needs write permission are checked
  33. * @return array directory name => true|false (is writable)
  34. */
  35. public static function checkDirectoriesWritable($directoriesToCheck)
  36. {
  37. $resultCheck = array();
  38. foreach ($directoriesToCheck as $directoryToCheck) {
  39. if (!preg_match('/^' . preg_quote(PIWIK_USER_PATH, '/') . '/', $directoryToCheck)) {
  40. $directoryToCheck = PIWIK_USER_PATH . $directoryToCheck;
  41. }
  42. if(strpos($directoryToCheck, '/tmp/') !== false) {
  43. $directoryToCheck = SettingsPiwik::rewriteTmpPathWithInstanceId($directoryToCheck);
  44. }
  45. Filesystem::mkdir($directoryToCheck);
  46. $directory = Filesystem::realpath($directoryToCheck);
  47. $resultCheck[$directory] = false;
  48. if ($directory !== false // realpath() returns FALSE on failure
  49. && is_writable($directoryToCheck)
  50. ) {
  51. $resultCheck[$directory] = true;
  52. }
  53. }
  54. return $resultCheck;
  55. }
  56. /**
  57. * Checks that the directories Piwik needs write access are actually writable
  58. * Displays a nice error page if permissions are missing on some directories
  59. *
  60. * @param array $directoriesToCheck Array of directory names to check
  61. */
  62. public static function dieIfDirectoriesNotWritable($directoriesToCheck = null)
  63. {
  64. $resultCheck = self::checkDirectoriesWritable($directoriesToCheck);
  65. if (array_search(false, $resultCheck) === false) {
  66. return;
  67. }
  68. $directoryList = '';
  69. foreach ($resultCheck as $dir => $bool) {
  70. $realpath = Filesystem::realpath($dir);
  71. if (!empty($realpath) && $bool === false) {
  72. $directoryList .= self::getMakeWritableCommand($realpath);
  73. }
  74. }
  75. // Also give the chown since the chmod is only 755
  76. if (!SettingsServer::isWindows()) {
  77. $realpath = Filesystem::realpath(PIWIK_INCLUDE_PATH . '/');
  78. $directoryList = "<code>chown -R ". self::getUserAndGroup() ." " . $realpath . "</code><br />" . $directoryList;
  79. }
  80. if(function_exists('shell_exec')) {
  81. $currentUser = self::getUser();
  82. if(!empty($currentUser)) {
  83. $optionalUserInfo = " (running as user '" . $currentUser . "')";
  84. }
  85. }
  86. $directoryMessage = "<p><b>Piwik couldn't write to some directories $optionalUserInfo</b>.</p>";
  87. $directoryMessage .= "<p>Try to Execute the following commands on your server, to allow Write access on these directories"
  88. . ":</p>"
  89. . "<blockquote>$directoryList</blockquote>"
  90. . "<p>If this doesn't work, you can try to create the directories with your FTP software, and set the CHMOD to 0755 (or 0777 if 0755 is not enough). To do so with your FTP software, right click on the directories then click permissions.</p>"
  91. . "<p>After applying the modifications, you can <a href='index.php'>refresh the page</a>.</p>"
  92. . "<p>If you need more help, try <a href='?module=Proxy&action=redirect&url=http://piwik.org'>Piwik.org</a>.</p>";
  93. Piwik_ExitWithMessage($directoryMessage, false, true);
  94. }
  95. /**
  96. * Get file integrity information (in PIWIK_INCLUDE_PATH).
  97. *
  98. * @return array(bool, string, ...) Return code (true/false), followed by zero or more error messages
  99. */
  100. public static function getFileIntegrityInformation()
  101. {
  102. $messages = array();
  103. $messages[] = true;
  104. $manifest = PIWIK_INCLUDE_PATH . '/config/manifest.inc.php';
  105. if (file_exists($manifest)) {
  106. require_once $manifest;
  107. }
  108. if (!class_exists('Piwik\\Manifest')) {
  109. $messages[] = Piwik::translate('General_WarningFileIntegrityNoManifest')
  110. . ' '
  111. . Piwik::translate('General_WarningFileIntegrityNoManifestDeployingFromGit');
  112. return $messages;
  113. }
  114. $files = \Piwik\Manifest::$files;
  115. $hasMd5file = function_exists('md5_file');
  116. $hasMd5 = function_exists('md5');
  117. foreach ($files as $path => $props) {
  118. $file = PIWIK_INCLUDE_PATH . '/' . $path;
  119. if (!file_exists($file) || !is_readable($file)) {
  120. $messages[] = Piwik::translate('General_ExceptionMissingFile', $file);
  121. } else if (filesize($file) != $props[0]) {
  122. if (!$hasMd5 || in_array(substr($path, -4), array('.gif', '.ico', '.jpg', '.png', '.swf'))) {
  123. // files that contain binary data (e.g., images) must match the file size
  124. $messages[] = Piwik::translate('General_ExceptionFilesizeMismatch', array($file, $props[0], filesize($file)));
  125. } else {
  126. // convert end-of-line characters and re-test text files
  127. $content = @file_get_contents($file);
  128. $content = str_replace("\r\n", "\n", $content);
  129. if ((strlen($content) != $props[0])
  130. || (@md5($content) !== $props[1])
  131. ) {
  132. $messages[] = Piwik::translate('General_ExceptionFilesizeMismatch', array($file, $props[0], filesize($file)));
  133. }
  134. }
  135. } else if ($hasMd5file && (@md5_file($file) !== $props[1])) {
  136. $messages[] = Piwik::translate('General_ExceptionFileIntegrity', $file);
  137. }
  138. }
  139. if (count($messages) > 1) {
  140. $messages[0] = false;
  141. }
  142. if (!$hasMd5file) {
  143. $messages[] = Piwik::translate('General_WarningFileIntegrityNoMd5file');
  144. }
  145. return $messages;
  146. }
  147. /**
  148. * Returns the help message when the auto update can't run because of missing permissions
  149. *
  150. * @return string
  151. */
  152. public static function getAutoUpdateMakeWritableMessage()
  153. {
  154. $realpath = Filesystem::realpath(PIWIK_INCLUDE_PATH . '/');
  155. $message = '';
  156. $message .= "<code>chown -R ". self::getUserAndGroup() ." " . $realpath . "</code><br />";
  157. $message .= "<code>chmod -R 0755 " . $realpath . "</code><br />";
  158. $message .= 'After you execute these commands (or change permissions via your FTP software), refresh the page and you should be able to use the "Automatic Update" feature.';
  159. return $message;
  160. }
  161. /**
  162. * Returns friendly error message explaining how to fix permissions
  163. *
  164. * @param string $path to the directory missing permissions
  165. * @return string Error message
  166. */
  167. public static function getErrorMessageMissingPermissions($path)
  168. {
  169. $message = "Please check that the web server has enough permission to write to these files/directories:<br />";
  170. if (SettingsServer::isWindows()) {
  171. $message .= "On Windows, check that the folder is not read only and is writable.\n
  172. You can try to execute:<br />";
  173. } else {
  174. $message .= "For example, on a GNU/Linux server if your Apache httpd user is "
  175. . self::getUser()
  176. . ", you can try to execute:<br />\n"
  177. . "<code>chown -R ". self::getUserAndGroup() ." " . $path . "</code><br />";
  178. }
  179. $message .= self::getMakeWritableCommand($path);
  180. return $message;
  181. }
  182. private static function getUserAndGroup()
  183. {
  184. $user = self::getUser();
  185. if(!function_exists('shell_exec')) {
  186. return $user . ':' . $user;
  187. }
  188. $group = trim(shell_exec('groups '. $user .' | cut -f3 -d" "'));
  189. if(empty($group)) {
  190. $group = 'www-data';
  191. }
  192. return $user . ':' . $group;
  193. }
  194. private static function getUser()
  195. {
  196. if(!function_exists('shell_exec')) {
  197. return 'www-data';
  198. }
  199. return trim(shell_exec('whoami'));
  200. }
  201. /**
  202. * Returns the help text displayed to suggest which command to run to give writable access to a file or directory
  203. *
  204. * @param string $realpath
  205. * @return string
  206. */
  207. private static function getMakeWritableCommand($realpath)
  208. {
  209. if (SettingsServer::isWindows()) {
  210. return "<code>cacls $realpath /t /g " . get_current_user() . ":f</code><br />\n";
  211. }
  212. return "<code>chmod -R 0755 $realpath</code><br />";
  213. }
  214. }