PageRenderTime 52ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/Installation/SystemCheck.php

https://github.com/CodeYellowBV/piwik
PHP | 341 lines | 243 code | 49 blank | 49 comment | 39 complexity | b65f6e3f3e74f9e7182a76aa816bc930 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\Plugins\Installation;
  10. use Piwik\CliMulti;
  11. use Piwik\CliMulti\Process;
  12. use Piwik\Common;
  13. use Piwik\Config;
  14. use Piwik\Db;
  15. use Piwik\Db\Adapter;
  16. use Piwik\DbHelper;
  17. use Piwik\Filechecks;
  18. use Piwik\Filesystem;
  19. use Piwik\Http;
  20. use Piwik\Piwik;
  21. use Piwik\Plugins\UserCountry\LocationProvider;
  22. use Piwik\SettingsServer;
  23. class SystemCheck
  24. {
  25. /**
  26. * Get system information
  27. */
  28. public static function getSystemInformation()
  29. {
  30. global $piwik_minimumPHPVersion;
  31. $minimumMemoryLimit = Config::getInstance()->General['minimum_memory_limit'];
  32. $infos = array();
  33. $directoriesToCheck = array(
  34. '/tmp/',
  35. '/tmp/assets/',
  36. '/tmp/cache/',
  37. '/tmp/climulti/',
  38. '/tmp/latest/',
  39. '/tmp/logs/',
  40. '/tmp/sessions/',
  41. '/tmp/tcpdf/',
  42. '/tmp/templates_c/',
  43. );
  44. if (!DbHelper::isInstalled()) {
  45. // at install, need /config to be writable (so we can create config.ini.php)
  46. $directoriesToCheck[] = '/config/';
  47. }
  48. $infos['directories'] = Filechecks::checkDirectoriesWritable($directoriesToCheck);
  49. $infos['can_auto_update'] = Filechecks::canAutoUpdate();
  50. self::initServerFilesForSecurity();
  51. $infos['phpVersion_minimum'] = $piwik_minimumPHPVersion;
  52. $infos['phpVersion'] = PHP_VERSION;
  53. $infos['phpVersion_ok'] = self::isPhpVersionValid($infos['phpVersion']);
  54. // critical errors
  55. $extensions = @get_loaded_extensions();
  56. $needed_extensions = array(
  57. 'zlib',
  58. 'SPL',
  59. 'iconv',
  60. 'json',
  61. 'mbstring',
  62. );
  63. // HHVM provides the required subset of Reflection but lists Reflections as missing
  64. if (!defined('HHVM_VERSION')) {
  65. $needed_extensions[] = 'Reflection';
  66. }
  67. $infos['needed_extensions'] = $needed_extensions;
  68. $infos['missing_extensions'] = array();
  69. foreach ($needed_extensions as $needed_extension) {
  70. if (!in_array($needed_extension, $extensions)) {
  71. $infos['missing_extensions'][] = $needed_extension;
  72. }
  73. }
  74. // Special case for mbstring
  75. if (!function_exists('mb_get_info')
  76. || ((int)ini_get('mbstring.func_overload')) != 0) {
  77. $infos['missing_extensions'][] = 'mbstring';
  78. }
  79. $infos['pdo_ok'] = false;
  80. if (in_array('PDO', $extensions)) {
  81. $infos['pdo_ok'] = true;
  82. }
  83. $infos['adapters'] = Adapter::getAdapters();
  84. $needed_functions = array(
  85. 'debug_backtrace',
  86. 'create_function',
  87. 'eval',
  88. 'gzcompress',
  89. 'gzuncompress',
  90. 'pack',
  91. );
  92. $infos['needed_functions'] = $needed_functions;
  93. $infos['missing_functions'] = array();
  94. foreach ($needed_functions as $needed_function) {
  95. if (!self::functionExists($needed_function)) {
  96. $infos['missing_functions'][] = $needed_function;
  97. }
  98. }
  99. // warnings
  100. $desired_extensions = array(
  101. 'json',
  102. 'libxml',
  103. 'dom',
  104. 'SimpleXML',
  105. );
  106. $infos['desired_extensions'] = $desired_extensions;
  107. $infos['missing_desired_extensions'] = array();
  108. foreach ($desired_extensions as $desired_extension) {
  109. if (!in_array($desired_extension, $extensions)) {
  110. $infos['missing_desired_extensions'][] = $desired_extension;
  111. }
  112. }
  113. $desired_functions = array(
  114. 'set_time_limit',
  115. 'mail',
  116. 'parse_ini_file',
  117. 'glob',
  118. );
  119. $infos['missing_desired_functions'] = array();
  120. foreach ($desired_functions as $desired_function) {
  121. if (!self::functionExists($desired_function)) {
  122. $infos['missing_desired_functions'][] = $desired_function;
  123. }
  124. }
  125. $sessionAutoStarted = (int)ini_get('session.auto_start');
  126. if($sessionAutoStarted) {
  127. $infos['missing_desired_functions'][] = 'session.auto_start';
  128. }
  129. $desired_settings = array(
  130. 'session.auto_start',
  131. );
  132. $infos['desired_functions'] = array_merge($desired_functions, $desired_settings);
  133. $infos['openurl'] = Http::getTransportMethod();
  134. $infos['gd_ok'] = SettingsServer::isGdExtensionEnabled();
  135. $serverSoftware = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';
  136. $infos['serverVersion'] = addslashes($serverSoftware);
  137. $infos['serverOs'] = @php_uname();
  138. $infos['serverTime'] = date('H:i:s');
  139. $infos['memoryMinimum'] = $minimumMemoryLimit;
  140. $infos['memory_ok'] = true;
  141. $infos['memoryCurrent'] = '';
  142. $raised = SettingsServer::raiseMemoryLimitIfNecessary();
  143. if (($memoryValue = SettingsServer::getMemoryLimitValue()) > 0) {
  144. $infos['memoryCurrent'] = $memoryValue . 'M';
  145. $infos['memory_ok'] = $memoryValue >= $minimumMemoryLimit;
  146. }
  147. $infos['isWindows'] = SettingsServer::isWindows();
  148. $integrityInfo = Filechecks::getFileIntegrityInformation();
  149. $infos['integrity'] = $integrityInfo[0];
  150. $infos['integrityErrorMessages'] = array();
  151. if (isset($integrityInfo[1])) {
  152. if ($infos['integrity'] == false) {
  153. $infos['integrityErrorMessages'][] = Piwik::translate('General_FileIntegrityWarningExplanation');
  154. }
  155. $infos['integrityErrorMessages'] = array_merge($infos['integrityErrorMessages'], array_slice($integrityInfo, 1));
  156. }
  157. $infos['timezone'] = SettingsServer::isTimezoneSupportEnabled();
  158. $process = new CliMulti();
  159. $infos['cli_process_ok'] = $process->supportsAsync();
  160. $infos['tracker_status'] = Common::getRequestVar('trackerStatus', 0, 'int');
  161. // check if filesystem is NFS, if it is file based sessions won't work properly
  162. $infos['is_nfs'] = Filesystem::checkIfFileSystemIsNFS();
  163. $infos = self::enrichSystemChecks($infos);
  164. return $infos;
  165. }
  166. /**
  167. * @param $infos
  168. * @return mixed
  169. */
  170. public static function enrichSystemChecks($infos)
  171. {
  172. // determine whether there are any errors/warnings from the checks done above
  173. $infos['has_errors'] = false;
  174. $infos['has_warnings'] = false;
  175. if (in_array(0, $infos['directories']) // if a directory is not writable
  176. || !$infos['phpVersion_ok']
  177. || !empty($infos['missing_extensions'])
  178. || empty($infos['adapters'])
  179. || !empty($infos['missing_functions'])
  180. ) {
  181. $infos['has_errors'] = true;
  182. }
  183. if ( !empty($infos['missing_desired_extensions'])
  184. || !$infos['gd_ok']
  185. || !$infos['memory_ok']
  186. || !empty($infos['integrityErrorMessages'])
  187. || !$infos['timezone'] // if timezone support isn't available
  188. || $infos['tracker_status'] != 0
  189. || $infos['is_nfs']
  190. ) {
  191. $infos['has_warnings'] = true;
  192. }
  193. return $infos;
  194. }
  195. /**
  196. * Test if function exists. Also handles case where function is disabled via Suhosin.
  197. *
  198. * @param string $functionName Function name
  199. * @return bool True if function exists (not disabled); False otherwise.
  200. */
  201. public static function functionExists($functionName)
  202. {
  203. // eval() is a language construct
  204. if ($functionName == 'eval') {
  205. // does not check suhosin.executor.eval.whitelist (or blacklist)
  206. if (extension_loaded('suhosin')) {
  207. return @ini_get("suhosin.executor.disable_eval") != "1";
  208. }
  209. return true;
  210. }
  211. $exists = function_exists($functionName);
  212. if (extension_loaded('suhosin')) {
  213. $blacklist = @ini_get("suhosin.executor.func.blacklist");
  214. if (!empty($blacklist)) {
  215. $blacklistFunctions = array_map('strtolower', array_map('trim', explode(',', $blacklist)));
  216. return $exists && !in_array($functionName, $blacklistFunctions);
  217. }
  218. }
  219. return $exists;
  220. }
  221. /**
  222. * Performs extra system checks for the 'System Check' admin page. These
  223. * checks are not performed during Installation.
  224. *
  225. * The following checks are performed:
  226. * - Check for whether LOAD DATA INFILE can be used. The result of the check
  227. * is stored in $result['load_data_infile_available']. The error message is
  228. * stored in $result['load_data_infile_error'].
  229. *
  230. * - Check whether geo location is setup correctly
  231. *
  232. * @return array
  233. */
  234. public static function performAdminPageOnlySystemCheck()
  235. {
  236. $result = array();
  237. self::checkLoadDataInfile($result);
  238. self::checkGeolocation($result);
  239. return $result;
  240. }
  241. private static function checkGeolocation(&$result)
  242. {
  243. $currentProviderId = LocationProvider::getCurrentProviderId();
  244. $allProviders = LocationProvider::getAllProviderInfo();
  245. $isRecommendedProvider = in_array($currentProviderId, array( LocationProvider\GeoIp\Php::ID, $currentProviderId == LocationProvider\GeoIp\Pecl::ID));
  246. $isProviderInstalled = ($allProviders[$currentProviderId]['status'] == LocationProvider::INSTALLED);
  247. $result['geolocation_using_non_recommended'] = $result['geolocation_ok'] = false;
  248. if ($isRecommendedProvider && $isProviderInstalled) {
  249. $result['geolocation_ok'] = true;
  250. } elseif ($isProviderInstalled) {
  251. $result['geolocation_using_non_recommended'] = true;
  252. }
  253. }
  254. private static function checkLoadDataInfile(&$result)
  255. {
  256. // check if LOAD DATA INFILE works
  257. $optionTable = Common::prefixTable('option');
  258. $testOptionNames = array('test_system_check1', 'test_system_check2');
  259. $result['load_data_infile_available'] = false;
  260. try {
  261. $result['load_data_infile_available'] = \Piwik\Db\BatchInsert::tableInsertBatch(
  262. $optionTable,
  263. array('option_name', 'option_value'),
  264. array(
  265. array($testOptionNames[0], '1'),
  266. array($testOptionNames[1], '2'),
  267. ),
  268. $throwException = true
  269. );
  270. } catch (\Exception $ex) {
  271. $result['load_data_infile_error'] = str_replace("\n", "<br/>", $ex->getMessage());
  272. }
  273. // delete the temporary rows that were created
  274. Db::exec("DELETE FROM `$optionTable` WHERE option_name IN ('" . implode("','", $testOptionNames) . "')");
  275. }
  276. protected static function initServerFilesForSecurity()
  277. {
  278. ServerFilesGenerator::createWebConfigFiles();
  279. ServerFilesGenerator::createHtAccessFiles();
  280. ServerFilesGenerator::createWebRootFiles();
  281. }
  282. /**
  283. * @param $piwik_minimumPHPVersion
  284. * @param $infos
  285. * @return bool
  286. */
  287. public static function isPhpVersionValid($phpVersion)
  288. {
  289. global $piwik_minimumPHPVersion;
  290. return version_compare($piwik_minimumPHPVersion, $phpVersion) === -1;
  291. }
  292. }