PageRenderTime 38ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/administrator/components/com_admintools/engine/Util/ConfigurationCheck.php

https://bitbucket.org/saltwaterdev/offshorefinancial.com
PHP | 606 lines | 346 code | 98 blank | 162 comment | 40 complexity | 402c27084d34e9013b64c62ff8291605 MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0, 0BSD, MIT, Apache-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * Akeeba Engine
  4. * The modular PHP5 site backup engine
  5. *
  6. * @copyright Copyright (c)2006-2017 Nicholas K. Dionysopoulos / Akeeba Ltd
  7. * @license GNU GPL version 3 or, at your option, any later version
  8. * @package akeebaengine
  9. *
  10. */
  11. namespace Akeeba\Engine\Util;
  12. // Protection against direct access
  13. defined('AKEEBAENGINE') or die();
  14. use Akeeba\Engine\Factory;
  15. use Akeeba\Engine\Platform;
  16. /**
  17. * Quirk detection helper class
  18. */
  19. class ConfigurationCheck
  20. {
  21. /**
  22. * The configuration checks to perform
  23. *
  24. * @var array
  25. */
  26. protected $configurationChecks = array
  27. (
  28. array('code' => '001', 'severity' => 'critical', 'callback' => array(null, 'q001'), 'description' => 'COM_AKEEBA_CPANEL_WARNING_Q001'),
  29. array('code' => '003', 'severity' => 'critical', 'callback' => array(null, 'q003'), 'description' => 'COM_AKEEBA_CPANEL_WARNING_Q003'),
  30. array('code' => '004', 'severity' => 'critical', 'callback' => array(null, 'q004'), 'description' => 'COM_AKEEBA_CPANEL_WARNING_Q004'),
  31. array('code' => '101', 'severity' => 'high', 'callback' => array(null, 'q101'), 'description' => 'COM_AKEEBA_CPANEL_WARNING_Q101'),
  32. array('code' => '103', 'severity' => 'high', 'callback' => array(null, 'q103'), 'description' => 'COM_AKEEBA_CPANEL_WARNING_Q103'),
  33. array('code' => '104', 'severity' => 'high', 'callback' => array(null, 'q104'), 'description' => 'COM_AKEEBA_CPANEL_WARNING_Q104'),
  34. array('code' => '106', 'severity' => 'high', 'callback' => array(null, 'q106'), 'description' => 'COM_AKEEBA_CPANEL_WARNING_Q106'),
  35. array('code' => '201', 'severity' => 'medium', 'callback' => array(null, 'q201'), 'description' => 'COM_AKEEBA_CPANEL_WARNING_Q201'),
  36. array('code' => '202', 'severity' => 'medium', 'callback' => array(null, 'q202'), 'description' => 'COM_AKEEBA_CPANEL_WARNING_Q202'),
  37. array('code' => '204', 'severity' => 'medium', 'callback' => array(null, 'q204'), 'description' => 'COM_AKEEBA_CPANEL_WARNING_Q204'),
  38. array('code' => '203', 'severity' => 'low', 'callback' => array(null, 'q203'), 'description' => 'COM_AKEEBA_CPANEL_WARNING_Q203'),
  39. array('code' => '401', 'severity' => 'low', 'callback' => array(null, 'q401'), 'description' => 'COM_AKEEBA_CPANEL_WARNING_Q401'),
  40. );
  41. /**
  42. * The public constructor replaces the missing object reference in the configuration check callbacks
  43. */
  44. function __construct()
  45. {
  46. $temp = array();
  47. foreach ($this->configurationChecks as $check)
  48. {
  49. $check['callback'] = array($this, $check['callback'][1]);
  50. $temp[] = $check;
  51. }
  52. $this->configurationChecks = $temp;
  53. }
  54. /**
  55. * Returns the output & temporary folder writable status
  56. *
  57. * @return array A hash array with the writable status
  58. */
  59. public function getFolderStatus()
  60. {
  61. static $status = null;
  62. if (is_null($status))
  63. {
  64. $stock_dirs = Platform::getInstance()->get_stock_directories();
  65. // Get output writable status
  66. $registry = Factory::getConfiguration();
  67. $outdir = $registry->get('akeeba.basic.output_directory');
  68. foreach ($stock_dirs as $macro => $replacement)
  69. {
  70. $outdir = str_replace($macro, $replacement, $outdir);
  71. }
  72. $status['output'] = @is_writable($outdir);
  73. }
  74. return $status;
  75. }
  76. /**
  77. * Returns the overall status. It's true when both the temporary and output directories are writable and there are
  78. * no critical configuration check failures.
  79. *
  80. * @return boolean
  81. */
  82. public function getShortStatus()
  83. {
  84. // Base the status on directory writeable status
  85. $status = $this->getFolderStatus();
  86. $ret = $status['output'];
  87. // Scan for high severity configuration check errors
  88. $detailedStatus = $this->getDetailedStatus();
  89. if (!empty($detailedStatus))
  90. {
  91. foreach ($detailedStatus as $configCheck)
  92. {
  93. if ($configCheck['severity'] == 'critical')
  94. {
  95. $ret = false;
  96. }
  97. }
  98. }
  99. // Return status
  100. return $ret;
  101. }
  102. /**
  103. * Add a configuration check definition
  104. *
  105. * @param string $code The configuration check code (three digit number)
  106. * @param string $severity The severity (low, medium, high, critical)
  107. * @param string $description The description key for this configuration check
  108. * @param null $callback The callback used to determine the status of the configuration check
  109. *
  110. * @return void
  111. */
  112. public function addConfigurationCheckDefinition($code, $severity = 'low', $description = null, $callback = null)
  113. {
  114. if (!is_callable($callback))
  115. {
  116. $callback = array($this, 'q' . $code);
  117. }
  118. if (empty($description))
  119. {
  120. $description = 'COM_AKEEBA_CPANEL_WARNING_Q' . $code;
  121. }
  122. $newConfigurationCheck = array(
  123. 'code' => $code,
  124. 'severity' => $severity,
  125. 'description' => $description,
  126. 'callback' => $callback,
  127. );
  128. $this->configurationChecks[$code] = $newConfigurationCheck;
  129. }
  130. /**
  131. * Remove a configuration check definition
  132. *
  133. * @param string $code The code of the configuration check to remove
  134. *
  135. * @return void
  136. */
  137. public function removeConfigurationCheckDefinition($code)
  138. {
  139. if (isset($this->configurationChecks[$code]))
  140. {
  141. unset($this->configurationChecks[$code]);
  142. }
  143. }
  144. /**
  145. * Clear the configuration check definitions
  146. *
  147. * @return void
  148. */
  149. public function clearConfigurationCheckDefinitions()
  150. {
  151. $this->configurationChecks = array();
  152. }
  153. /**
  154. * Runs the configuration check scripts. These are potential problems related to server
  155. * configuration, out of Akeeba's control. They are intended to give the user a
  156. * chance to fix them before they cause the backup to fail.
  157. *
  158. * Numbering scheme:
  159. * Q0xx No-go errors
  160. * Q1xx Critical system configuration errors
  161. * Q2xx Medium and low system configuration warnings
  162. * Q3xx Critical software configuration errors
  163. * Q4xx Medium and low component configuration warnings
  164. *
  165. * @param boolean $low_priority Should I include low priority quirks?
  166. * @param string $help_url_template The sprintf template from creating a help URL from a config check code
  167. *
  168. * @return array
  169. */
  170. public function getDetailedStatus($low_priority = false, $help_url_template = 'https://www.akeebabackup.com/documentation/warnings/q%s.html')
  171. {
  172. static $detailedStatus = null;
  173. if (is_null($detailedStatus))
  174. {
  175. $detailedStatus = array();
  176. foreach ($this->configurationChecks as $quirkDef)
  177. {
  178. if (!$low_priority && ($quirkDef['severity'] == 'low'))
  179. {
  180. continue;
  181. }
  182. $this->checkConfiguration($detailedStatus, $quirkDef, $help_url_template);
  183. }
  184. }
  185. return $detailedStatus;
  186. }
  187. /**
  188. * Make a configuration check and adds it to the list if it raises a warning / error
  189. *
  190. * @param array $detailedStatus The configuration checks status array
  191. * @param array $quirkDef The configuration check definition
  192. * @param string $help_url_template The sprintf template from creating a help URL from a quirk code
  193. *
  194. * @return void
  195. */
  196. protected function checkConfiguration(&$detailedStatus, $quirkDef, $help_url_template)
  197. {
  198. if (call_user_func($quirkDef['callback']))
  199. {
  200. $description = Platform::getInstance()->translate($quirkDef['description']);
  201. $detailedStatus[(string)$quirkDef['code']] = array(
  202. 'code' => $quirkDef['code'],
  203. 'severity' => $quirkDef['severity'],
  204. 'description' => $description,
  205. 'help_url' => sprintf($help_url_template, $quirkDef['code']),
  206. );
  207. }
  208. }
  209. /**
  210. * Q001 - HIGH - Output directory unwriteable
  211. *
  212. * @return bool
  213. */
  214. private function q001()
  215. {
  216. $status = $this->getFolderStatus();
  217. return !$status['output'];
  218. }
  219. /**
  220. * Q003 - HIGH - Backup output or temporary set to site's root
  221. *
  222. * @return bool
  223. */
  224. private function q003()
  225. {
  226. $stock_dirs = Platform::getInstance()->get_stock_directories();
  227. $registry = Factory::getConfiguration();
  228. $outdir = $registry->get('akeeba.basic.output_directory');
  229. foreach ($stock_dirs as $macro => $replacement)
  230. {
  231. $outdir = str_replace($macro, $replacement, $outdir);
  232. }
  233. $outdir_real = @realpath($outdir);
  234. if (!empty($outdir_real))
  235. {
  236. $outdir = $outdir_real;
  237. }
  238. $siteroot = Platform::getInstance()->get_site_root();
  239. $siteroot_real = @realpath($siteroot);
  240. if (!empty($siteroot_real))
  241. {
  242. $siteroot = $siteroot_real;
  243. }
  244. return ($siteroot == $outdir);
  245. }
  246. /**
  247. * Q004 - HIGH - Free memory too low
  248. *
  249. * @return bool
  250. */
  251. private function q004()
  252. {
  253. // If we can't figure this out, don't report a problem. It doesn't
  254. // really matter, as the backup WILL crash eventually.
  255. if (!function_exists('ini_get'))
  256. {
  257. return false;
  258. }
  259. $memLimit = ini_get("memory_limit");
  260. $memLimit = $this->_return_bytes($memLimit);
  261. if ($memLimit <= 0)
  262. {
  263. return false;
  264. }
  265. // No limit?
  266. $availableRAM = $memLimit - memory_get_usage();
  267. // We need at least 12Mb of free memory
  268. return ($availableRAM <= (12 * 1024 * 1024));
  269. }
  270. /**
  271. * Q101 - HIGH - open_basedir on output directory
  272. *
  273. * @return bool
  274. */
  275. private function q101()
  276. {
  277. $stock_dirs = Platform::getInstance()->get_stock_directories();
  278. // Get output writable status
  279. $registry = Factory::getConfiguration();
  280. $outdir = $registry->get('akeeba.basic.output_directory');
  281. foreach ($stock_dirs as $macro => $replacement)
  282. {
  283. $outdir = str_replace($macro, $replacement, $outdir);
  284. }
  285. return $this->checkOpenBasedirs($outdir);
  286. }
  287. /**
  288. * Q103 - HIGH - Less than 10" of max_execution_time with PHP Safe Mode enabled
  289. *
  290. * @return bool
  291. */
  292. private function q103()
  293. {
  294. $exectime = ini_get('max_execution_time');
  295. $safemode = ini_get('safe_mode');
  296. if (!$safemode)
  297. {
  298. return false;
  299. }
  300. if (!is_numeric($exectime))
  301. {
  302. return false;
  303. }
  304. if ($exectime <= 0)
  305. {
  306. return false;
  307. }
  308. return $exectime < 10;
  309. }
  310. /**
  311. * Q104 - HIGH - Temp directory is the same as the site's root
  312. *
  313. * @return bool
  314. */
  315. private function q104()
  316. {
  317. $siteroot = Platform::getInstance()->get_site_root();
  318. $siteroot_real = @realpath($siteroot);
  319. if (!empty($siteroot_real))
  320. {
  321. $siteroot = $siteroot_real;
  322. }
  323. $stockDirs = Platform::getInstance()->get_stock_directories();
  324. $temp_directory = $stockDirs['[SITETMP]'];
  325. $temp_directory = @realpath($temp_directory);
  326. if (empty($temp_directory))
  327. {
  328. $temp_directory = $siteroot;
  329. }
  330. return ($siteroot == $temp_directory);
  331. }
  332. /**
  333. * Q106 - HIGH - Table name prefix contains uppercase characters
  334. *
  335. * @return bool
  336. */
  337. private function q106()
  338. {
  339. $filters = Factory::getFilters();
  340. $databases = $filters->getInclusions('db');
  341. foreach ($databases as $db)
  342. {
  343. if (!isset($db['prefix']))
  344. {
  345. continue;
  346. }
  347. if (preg_match('/[A-Z]/', $db['prefix']))
  348. {
  349. return true;
  350. }
  351. }
  352. return false;
  353. }
  354. /**
  355. * Q201 - MEDIUM - Outdated PHP version.
  356. *
  357. * We currently check for PHP lower than 5.5.
  358. *
  359. * @return bool
  360. */
  361. private function q201()
  362. {
  363. return version_compare(PHP_VERSION, '5.5.0', 'lt');
  364. }
  365. /**
  366. * Q202 - MED - CRC problems with hash extension not present
  367. *
  368. * @return bool
  369. */
  370. private function q202()
  371. {
  372. $registry = Factory::getConfiguration();
  373. $archiver = $registry->get('akeeba.advanced.archiver_engine');
  374. if ($archiver != 'zip')
  375. {
  376. return false;
  377. }
  378. return !function_exists('hash_file');
  379. }
  380. /**
  381. * Q203 - MED - Default output directory in use
  382. *
  383. * @return bool
  384. */
  385. private function q203()
  386. {
  387. $stock_dirs = Platform::getInstance()->get_stock_directories();
  388. $registry = Factory::getConfiguration();
  389. $outdir = $registry->get('akeeba.basic.output_directory');
  390. foreach ($stock_dirs as $macro => $replacement)
  391. {
  392. $outdir = str_replace($macro, $replacement, $outdir);
  393. }
  394. $default = $stock_dirs['[DEFAULT_OUTPUT]'];
  395. $outdir = Factory::getFilesystemTools()->TranslateWinPath($outdir);
  396. $default = Factory::getFilesystemTools()->TranslateWinPath($default);
  397. return $outdir == $default;
  398. }
  399. /**
  400. * Q204 - MED - Disabled functions may affect operation
  401. *
  402. * @return bool
  403. */
  404. private function q204()
  405. {
  406. $disabled = ini_get('disabled_functions');
  407. return (!empty($disabled));
  408. }
  409. /**
  410. * Q401 - LOW - ZIP format selected
  411. *
  412. * @return bool
  413. */
  414. private function q401()
  415. {
  416. $registry = Factory::getConfiguration();
  417. $archiver = $registry->get('akeeba.advanced.archiver_engine');
  418. return $archiver == 'zip';
  419. }
  420. /**
  421. * Checks if a path is restricted by open_basedirs
  422. *
  423. * @param string $check The path to check
  424. *
  425. * @return bool True if the path is restricted (which is bad)
  426. */
  427. public function checkOpenBasedirs($check)
  428. {
  429. static $paths;
  430. if (empty($paths))
  431. {
  432. $open_basedir = ini_get('open_basedir');
  433. if (empty($open_basedir))
  434. {
  435. return false;
  436. }
  437. $delimiter = strpos($open_basedir, ';') !== false ? ';' : ':';
  438. $paths_temp = explode($delimiter, $open_basedir);
  439. // Some open_basedirs are using environemtn variables
  440. $paths = array();
  441. foreach ($paths_temp as $path)
  442. {
  443. if (array_key_exists($path, $_ENV))
  444. {
  445. $paths[] = $_ENV[$path];
  446. }
  447. else
  448. {
  449. $paths[] = $path;
  450. }
  451. }
  452. }
  453. if (empty($paths))
  454. {
  455. return false; // no restrictions
  456. }
  457. else
  458. {
  459. $newcheck = @realpath($check); // Resolve symlinks, like PHP does
  460. if (!($newcheck === false))
  461. {
  462. $check = $newcheck;
  463. }
  464. $included = false;
  465. foreach ($paths as $path)
  466. {
  467. $newpath = @realpath($path);
  468. if (!($newpath === false))
  469. {
  470. $path = $newpath;
  471. }
  472. if (strlen($check) >= strlen($path))
  473. {
  474. // Only check if the path to check is longer than the inclusion path.
  475. // Otherwise, I guarantee it's not included!!
  476. // If the path to check begins with an inclusion path, it's permitted. Easy, huh?
  477. if (substr($check, 0, strlen($path)) == $path)
  478. {
  479. $included = true;
  480. }
  481. }
  482. }
  483. return !$included;
  484. }
  485. }
  486. private function _return_bytes($setting)
  487. {
  488. $val = trim($setting);
  489. $last = strtolower(substr($val, -1));
  490. $val = substr($val, 0, -1);
  491. if (is_numeric($last))
  492. {
  493. return $setting;
  494. }
  495. switch ($last)
  496. {
  497. case 't':
  498. $val *= 1024;
  499. case 'g':
  500. $val *= 1024;
  501. case 'm':
  502. $val *= 1024;
  503. case 'k':
  504. $val *= 1024;
  505. }
  506. return (int) $val;
  507. }
  508. }