PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/administrator/components/com_akeeba/akeeba/utils/quirks.php

https://github.com/CCI-Studios/Wee-Magazine
PHP | 444 lines | 255 code | 55 blank | 134 comment | 40 complexity | bc037ed054b9bec9271c33ac7d7ae56d MD5 | raw file
  1. <?php
  2. /**
  3. * Akeeba Engine
  4. * The modular PHP5 site backup engine
  5. * @copyright Copyright (c)2009-2012 Nicholas K. Dionysopoulos
  6. * @license GNU GPL version 3 or, at your option, any later version
  7. * @package akeebaengine
  8. *
  9. */
  10. // Protection against direct access
  11. defined('AKEEBAENGINE') or die();
  12. /**
  13. * Quirk detection helper class
  14. */
  15. class AEUtilQuirks
  16. {
  17. /**
  18. * Returns the output & temporary folder writability status
  19. * @return array A hash array with the writability status
  20. */
  21. public static function get_folder_status()
  22. {
  23. static $status = null;
  24. if(is_null($status))
  25. {
  26. $stock_dirs = AEPlatform::getInstance()->get_stock_directories();
  27. // Get output writable status
  28. $registry = AEFactory::getConfiguration();
  29. $outdir = $registry->get('akeeba.basic.output_directory');
  30. foreach( $stock_dirs as $macro => $replacement )
  31. {
  32. $outdir = str_replace($macro, $replacement, $outdir);
  33. }
  34. $status['output']= @is_writable($outdir);
  35. }
  36. return $status;
  37. }
  38. /**
  39. * Returns the overall status. It's true when both the temporary and
  40. * output directories are writable and no critical severity quirks have
  41. * been detected.
  42. * @return bool
  43. */
  44. public static function get_status()
  45. {
  46. // Base the status on directory writability
  47. $status = self::get_folder_status();
  48. $ret = $status['output'];
  49. // Scan for high severity quirks
  50. $quirks = self::get_quirks();
  51. if(!empty($quirks))
  52. {
  53. foreach($quirks as $quirk)
  54. {
  55. if($quirk['severity'] == 'critical') $ret = false;
  56. }
  57. }
  58. // Return status
  59. return $ret;
  60. }
  61. /**
  62. * Runs the "quirks" detection scripts. These are potential problems related to server
  63. * configuration, out of Akeeba's control. They are intended to give the user a
  64. * chance to fix them before they cause the backup to fail, eventually saving both
  65. * the user's and support personel's time.
  66. *
  67. * "Quirks" numbering scheme:
  68. * Q0xx No-go errors
  69. * Q1xx Critical system configuration errors
  70. * Q2xx Medium and low system configuration warnings
  71. * Q3xx Critical software configuration errors
  72. * Q4xx Medium and low component configuration warnings
  73. *
  74. * It populates and returns the $quirks array.
  75. *
  76. * @return array
  77. */
  78. public static function get_quirks($low_priority = false)
  79. {
  80. static $quirks = null;
  81. if(is_null($quirks))
  82. {
  83. $quirks = array();
  84. self::getQuirk($quirks, '001', 'critical');
  85. self::getQuirk($quirks, '003', 'critical');
  86. self::getQuirk($quirks, '004', 'critical');
  87. self::getQuirk($quirks, '101', 'high');
  88. self::getQuirk($quirks, '103', 'high');
  89. self::getQuirk($quirks, '104', 'high');
  90. self::getQuirk($quirks, '105', 'high');
  91. self::getQuirk($quirks, '201', 'high');
  92. self::getQuirk($quirks, '202', 'medium');
  93. self::getQuirk($quirks, '204', 'medium');
  94. if($low_priority)
  95. {
  96. self::getQuirk($quirks, '203', 'low');
  97. self::getQuirk($quirks, '401', 'low');
  98. }
  99. }
  100. return $quirks;
  101. }
  102. /**
  103. * Gets a "quirk" status and adds it to the list if it is active
  104. *
  105. * @param array $quirks The quirks array
  106. * @param string $code The Quirks code, without the Q
  107. * @param string $severity Severity: 'low','medium','high'
  108. */
  109. private static function getQuirk( &$quirks, $code, $severity )
  110. {
  111. $methodName = 'q'.$code;
  112. if(self::$methodName())
  113. {
  114. $description = AEPlatform::getInstance()->translate('Q'.$code);
  115. $quirks[(string)$code] = array(
  116. 'code' => $code,
  117. 'severity' => $severity,
  118. 'description' => $description,
  119. 'help_url' => 'https://www.akeebabackup.com/documentation/warnings/q'.$code.'.html'
  120. );
  121. }
  122. }
  123. /**
  124. * Q001 - HIGH - Output directory unwritable
  125. *
  126. * @return bool
  127. */
  128. private static function q001()
  129. {
  130. $status = self::get_folder_status();
  131. return !$status['output'];
  132. }
  133. /**
  134. * Q003 - HIGH - Backup output or temporary set to site's root
  135. *
  136. * @return bool
  137. */
  138. private static function q003()
  139. {
  140. $stock_dirs = AEPlatform::getInstance()->get_stock_directories();
  141. $registry = AEFactory::getConfiguration();
  142. $outdir = $registry->get('akeeba.basic.output_directory');
  143. foreach( $stock_dirs as $macro => $replacement )
  144. {
  145. $outdir = str_replace($macro, $replacement, $outdir);
  146. }
  147. $outdir_real = @realpath($outdir);
  148. if(!empty($outdir_real)) $outdir = $outdir_real;
  149. $siteroot = AEPlatform::getInstance()->get_site_root();
  150. $siteroot_real = @realpath($siteroot);
  151. if(!empty($siteroot_real)) $siteroot = $siteroot_real;
  152. return ($siteroot == $outdir);
  153. }
  154. /**
  155. * Q004 - HIGH - Free memory too low
  156. *
  157. * @return bool
  158. */
  159. private static function q004()
  160. {
  161. // If we can't figure this out, don't report a problem. It doesn't
  162. // really matter, as the backup WILL crash eventually.
  163. if(!function_exists('ini_get')) return false;
  164. $memLimit = ini_get("memory_limit");
  165. $memLimit = self::_return_bytes($memLimit);
  166. if($memLimit <= 0) return false; // No limit?
  167. $availableRAM = $memLimit - memory_get_usage();
  168. // We need at least 7Mb of free memory
  169. return ($availableRAM <= 7340032);
  170. }
  171. /**
  172. * Q101 - HIGH - open_basedir on output directory
  173. *
  174. * @return bool
  175. */
  176. private static function q101()
  177. {
  178. $stock_dirs = AEPlatform::getInstance()->get_stock_directories();
  179. // Get output writable status
  180. $registry = AEFactory::getConfiguration();
  181. $outdir = $registry->get('akeeba.basic.output_directory');
  182. foreach( $stock_dirs as $macro => $replacement )
  183. {
  184. $outdir = str_replace($macro, $replacement, $outdir);
  185. }
  186. return self::checkOpenBasedirs($outdir);
  187. }
  188. /**
  189. * Q103 - HIGH - Less than 10" of max_execution_time with PHP Safe Mode enabled
  190. *
  191. * @return bool
  192. */
  193. private static function q103()
  194. {
  195. $exectime = ini_get('max_execution_time');
  196. $safemode = ini_get('safe_mode');
  197. if(!$safemode) return false;
  198. if(!is_numeric($exectime)) return false;
  199. if($exectime <= 0) return false;
  200. return $exectime < 10;
  201. }
  202. /**
  203. * Q104 - HIGH - Temp directory is the same as the site's root
  204. *
  205. * @return bool
  206. */
  207. private static function q104()
  208. {
  209. $siteroot = AEPlatform::getInstance()->get_site_root();
  210. $siteroot_real = @realpath($siteroot);
  211. if(!empty($siteroot_real)) $siteroot = $siteroot_real;
  212. $temp_directory = JFactory::getConfig()->get('tmp_path', '/tmp');
  213. return ($siteroot == $temp_directory);
  214. }
  215. /**
  216. * Q104 - HIGH - Log directory is the same as the site's root
  217. *
  218. * @return bool
  219. */
  220. private static function q105()
  221. {
  222. $siteroot = AEPlatform::getInstance()->get_site_root();
  223. $siteroot_real = @realpath($siteroot);
  224. if(!empty($siteroot_real)) $siteroot = $siteroot_real;
  225. $log_directory = JFactory::getConfig()->get('log_path', '/var/log');
  226. return ($siteroot == $log_directory);
  227. }
  228. /**
  229. * Gets the system temporary directory's real path... or at least it tries hard to do so!
  230. * @return unknown_type
  231. */
  232. private static function sys_get_temp_dir() {
  233. // Try system environment variables
  234. if(function_exists('getenv'))
  235. {
  236. if( $temp=getenv('TMP') ) return $temp;
  237. if( $temp=getenv('TEMP') ) return $temp;
  238. if( $temp=getenv('TMPDIR') ) return $temp;
  239. }
  240. // Try sys_get_temp_dir()
  241. if(function_exists('sys_get_temp_dir'))
  242. {
  243. $temp = sys_get_temp_dir();
  244. if(!empty($temp))
  245. {
  246. $temp_real = @realpath($temp);
  247. if(!empty($temp_real)) $temp = $temp_real;
  248. return $temp;
  249. }
  250. }
  251. // Try creating a temp file
  252. $temp=@tempnam(__FILE__,'');
  253. if (file_exists($temp)) {
  254. unlink($temp);
  255. return dirname($temp);
  256. }
  257. // If all else fails...
  258. return '';
  259. }
  260. /**
  261. * Q201 - HIGH - PHP4 detected
  262. * Note: Q201 was originally a LOW severity quirk, but Akeeba Engine 3 no longer
  263. * works on PHP4.
  264. * @return bool
  265. */
  266. private static function q201()
  267. {
  268. return version_compare(PHP_VERSION,'5.0.0') < 0;
  269. }
  270. /**
  271. * Q202 - MED - CRC problems with hash extension not present
  272. *
  273. * @return bool
  274. */
  275. private static function q202()
  276. {
  277. $registry = AEFactory::getConfiguration();
  278. $archiver = $registry->get('akeeba.advanced.archiver_engine');
  279. if($archiver != 'zip') return false;
  280. return !function_exists('hash_file');
  281. }
  282. /**
  283. * Q203 - MED - Default output directory in use
  284. *
  285. * @return bool
  286. */
  287. private static function q203()
  288. {
  289. $stock_dirs = AEPlatform::getInstance()->get_stock_directories();
  290. $registry = AEFactory::getConfiguration();
  291. $outdir = $registry->get('akeeba.basic.output_directory');
  292. foreach( $stock_dirs as $macro => $replacement )
  293. {
  294. $outdir = str_replace($macro, $replacement, $outdir);
  295. }
  296. $default = $stock_dirs['[DEFAULT_OUTPUT]'];
  297. $outdir = AEUtilFilesystem::TranslateWinPath($outdir);
  298. $default = AEUtilFilesystem::TranslateWinPath($default);
  299. return $outdir == $default;
  300. }
  301. /**
  302. * Q204 - MED - Disabled functions may affect operation
  303. *
  304. * @return bool
  305. */
  306. private static function q204()
  307. {
  308. $disabled = ini_get('disabled_functions');
  309. return (!empty($disabled));
  310. }
  311. /**
  312. * Q401 - LOW - ZIP format selected
  313. *
  314. * @return bool
  315. */
  316. private static function q401()
  317. {
  318. $registry = AEFactory::getConfiguration();
  319. $archiver = $registry->get('akeeba.advanced.archiver_engine');
  320. return $archiver == 'zip';
  321. }
  322. /**
  323. * Checks if a path is restricted by open_basedirs
  324. *
  325. * @param string $check The path to check
  326. * @return bool True if the path is restricted (which is bad)
  327. */
  328. public static function checkOpenBasedirs($check)
  329. {
  330. static $paths;
  331. if(empty($paths))
  332. {
  333. $open_basedir = ini_get('open_basedir');
  334. if(empty($open_basedir)) return false;
  335. $delimiter = strpos($open_basedir, ';') !== false ? ';' : ':';
  336. $paths_temp = explode($delimiter, $open_basedir);
  337. // Some open_basedirs are using environemtn variables
  338. $paths = array();
  339. foreach($paths_temp as $path)
  340. {
  341. if(array_key_exists($path, $_ENV))
  342. {
  343. $paths[] = $_ENV[$path];
  344. }
  345. else
  346. {
  347. $paths[] = $path;
  348. }
  349. }
  350. }
  351. if(empty($paths))
  352. {
  353. return false; // no restrictions
  354. }
  355. else
  356. {
  357. $newcheck = @realpath($check); // Resolve symlinks, like PHP does
  358. if(!($newcheck === false)) $check = $newcheck;
  359. $included = false;
  360. foreach($paths as $path)
  361. {
  362. $newpath = @realpath($path);
  363. if(!($newpath === false)) $path = $newpath;
  364. if(strlen($check) >= strlen($path))
  365. {
  366. // Only check if the path to check is longer than the inclusion path.
  367. // Otherwise, I guarantee it's not included!!
  368. // If the path to check begins with an inclusion path, it's permitted. Easy, huh?
  369. if(substr($check,0,strlen($path)) == $path) $included = true;
  370. }
  371. }
  372. return !$included;
  373. }
  374. }
  375. private static function _return_bytes($val) {
  376. $val = trim($val);
  377. $last = strtolower($val{strlen($val)-1});
  378. switch($last) {
  379. // The 'G' modifier is available since PHP 5.1.0
  380. case 'g':
  381. $val *= 1024;
  382. case 'm':
  383. $val *= 1024;
  384. case 'k':
  385. $val *= 1024;
  386. }
  387. return $val;
  388. }
  389. }