PageRenderTime 1156ms CodeModel.GetById 35ms RepoModel.GetById 6ms app.codeStats 0ms

/administrator/components/com_akeeba/akeeba/platform/joomla25/platform.php

https://bitbucket.org/kraymitchell/apex
PHP | 683 lines | 481 code | 60 blank | 142 comment | 81 complexity | cceb581b8c8cb92a036c3a5252441f2d MD5 | raw file
Possible License(s): GPL-2.0, LGPL-3.0, BSD-3-Clause, LGPL-2.1, GPL-3.0
  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('Restricted access');
  12. if(!defined('DS')) {
  13. define('DS',DIRECTORY_SEPARATOR); // Still required by Joomla! :(
  14. }
  15. /**
  16. * Joomla! 2.5 platform class
  17. */
  18. class AEPlatformJoomla25 extends AEPlatformAbstract
  19. {
  20. /** @var int Platform class priority */
  21. public $priority = 53;
  22. public $platformName = 'joomla25';
  23. /**
  24. * Performs heuristics to determine if this platform object is the ideal
  25. * candidate for the environment Akeeba Engine is running in.
  26. *
  27. * @return bool
  28. */
  29. public function isThisPlatform()
  30. {
  31. // Make sure _JEXEC is defined
  32. if(!defined('_JEXEC')) return false;
  33. // We need JVERSION to be defined
  34. if(!defined('JVERSION')) return false;
  35. // Check if JFactory exists
  36. if(!class_exists('JFactory')) return false;
  37. // Check if JApplication exists
  38. if(!class_exists('JApplication')) return false;
  39. return version_compare(JVERSION, '2.5.0', 'ge');
  40. }
  41. /**
  42. * Registers Akeeba's class autoloader with Joomla!
  43. */
  44. public function register_autoloader()
  45. {
  46. // Try to register AEAutoloader with SPL, or fall back to making use of JLoader
  47. // Obviously, performance is better with SPL, but not all systems support it.
  48. if( function_exists('spl_autoload_register') )
  49. {
  50. // Joomla! is using its own autoloader function which has to be registered first...
  51. if(function_exists('__autoload')) spl_autoload_register('__autoload');
  52. // ...and then register ourselves.
  53. spl_autoload_register('AEAutoloader');
  54. }
  55. else
  56. {
  57. // Guys, it's 2011 at the time of this writing. If you have a host which
  58. // doesn't support SPL yet, SWITCH HOSTS!
  59. throw new Exception('Akeeba Backup REQUIRES the SPL extension to be loaded and activated',500);
  60. }
  61. }
  62. /**
  63. * Returns an associative array of stock platform directories
  64. * @return array
  65. */
  66. public function get_stock_directories()
  67. {
  68. static $stock_directories = array();
  69. if(empty($stock_directories))
  70. {
  71. $jreg = JFactory::getConfig();
  72. if(version_compare(JVERSION, '3.0', 'ge')) {
  73. $tmpdir = $jreg->get('tmp_path');
  74. } else {
  75. $tmpdir = $jreg->getValue('config.tmp_path');
  76. }
  77. $stock_directories['[SITEROOT]'] = $this->get_site_root();
  78. $stock_directories['[ROOTPARENT]'] = @realpath($this->get_site_root().'/..');
  79. $stock_directories['[SITETMP]'] = $tmpdir;
  80. $stock_directories['[DEFAULT_OUTPUT]'] = $this->get_site_root().'/administrator/components/com_akeeba/backup';
  81. }
  82. return $stock_directories;
  83. }
  84. /**
  85. * Returns the absolute path to the site's root
  86. * @return string
  87. */
  88. public function get_site_root()
  89. {
  90. static $root = null;
  91. if( empty($root) || is_null($root) )
  92. {
  93. $root = JPATH_ROOT;
  94. if(empty($root) || ($root == DIRECTORY_SEPARATOR) || ($root == '/'))
  95. {
  96. // Try to get the current root in a different way
  97. if(function_exists('getcwd')) {
  98. $root = getcwd();
  99. }
  100. $app = JFactory::getApplication();
  101. if( $app->isAdmin() )
  102. {
  103. if(empty($root)) {
  104. $root = '../';
  105. } else {
  106. $adminPos = strpos($root, 'administrator');
  107. if($adminPos !== false) {
  108. $root = substr($root, 0, $adminPos);
  109. } else {
  110. $root = '../';
  111. }
  112. // Degenerate case where $root = 'administrator'
  113. // without a leading slash before entering this
  114. // if-block
  115. if(empty($root)) $root = '../';
  116. }
  117. }
  118. else
  119. {
  120. if(empty($root) || ($root == DIRECTORY_SEPARATOR) || ($root == '/') ) {
  121. $root = './';
  122. }
  123. }
  124. }
  125. }
  126. return $root;
  127. }
  128. /**
  129. * Returns the absolute path to the installer images directory
  130. * @return string
  131. */
  132. public function get_installer_images_path()
  133. {
  134. return JPATH_ADMINISTRATOR.'/components/com_akeeba/assets/installers';
  135. }
  136. /**
  137. * Returns the active profile number
  138. * @return int
  139. */
  140. public function get_active_profile()
  141. {
  142. if( defined('AKEEBA_PROFILE') )
  143. {
  144. return AKEEBA_PROFILE;
  145. }
  146. else
  147. {
  148. $session = JFactory::getSession();
  149. return $session->get('profile', null, 'akeeba');
  150. }
  151. }
  152. /**
  153. * Returns the selected profile's name. If no ID is specified, the current
  154. * profile's name is returned.
  155. * @return string
  156. */
  157. public function get_profile_name($id = null)
  158. {
  159. if(empty($id)) $id = $this->get_active_profile();
  160. $id = (int)$id;
  161. $sql = 'SELECT `description` FROM `#__ak_profiles` WHERE `id` = '.$id;
  162. $db = AEFactory::getDatabase( $this->get_platform_database_options() );
  163. $db->setQuery($sql);
  164. return $db->loadResult();
  165. }
  166. /**
  167. * Returns the backup origin
  168. * @return string Backup origin: backend|frontend
  169. */
  170. public function get_backup_origin()
  171. {
  172. if(defined('AKEEBA_BACKUP_ORIGIN')) return AKEEBA_BACKUP_ORIGIN;
  173. if(JFactory::getApplication()->isAdmin()) {
  174. return 'backend';
  175. } else {
  176. return 'frontend';
  177. }
  178. }
  179. /**
  180. * Returns a MySQL-formatted timestamp out of the current date
  181. * @param string $date[optional] The timestamp to use. Omit to use current timestamp.
  182. * @return string
  183. */
  184. public function get_timestamp_database($date = 'now')
  185. {
  186. jimport('joomla.utilities.date');
  187. $jdate = new JDate($date);
  188. if(version_compare(JVERSION, '3.0', 'ge')) {
  189. return $jdate->toSql();
  190. } else {
  191. return $jdate->toMySQL();
  192. }
  193. }
  194. /**
  195. * Returns the current timestamp, taking into account any TZ information,
  196. * in the format specified by $format.
  197. * @param string $format Timestamp format string (standard PHP format string)
  198. * @return string
  199. */
  200. public function get_local_timestamp($format)
  201. {
  202. jimport('joomla.utilities.date');
  203. $jregistry = JFactory::getConfig();
  204. if(version_compare(JVERSION, '3.0', 'ge')) {
  205. $tzDefault = $jregistry->get('offset');
  206. } else {
  207. $tzDefault = $jregistry->getValue('config.offset');
  208. }
  209. $user = JFactory::getUser();
  210. $tz = $user->getParam('timezone', $tzDefault);
  211. $dateNow = new JDate('now',$tz);
  212. return $dateNow->format($format);
  213. }
  214. /**
  215. * Returns the current host name
  216. * @return string
  217. */
  218. public function get_host()
  219. {
  220. if(!array_key_exists('REQUEST_METHOD', $_SERVER)) {
  221. // Running under CLI
  222. require_once JPATH_ROOT.'/libraries/joomla/environment/uri.php';
  223. $url = AEPlatform::getInstance()->get_platform_configuration_option('siteurl','');
  224. $oURI = new JURI($url);
  225. } else {
  226. // Running under the web server
  227. $oURI = JURI::getInstance();
  228. }
  229. return $oURI->getHost();
  230. }
  231. public function get_site_name()
  232. {
  233. $jconfig = JFactory::getConfig();
  234. if(version_compare(JVERSION, '3.0', 'ge')) {
  235. return $jconfig->get('sitename','');
  236. } else {
  237. return $jconfig->getValue('config.sitename','');
  238. }
  239. }
  240. /**
  241. * Gets the best matching database driver class, according to CMS settings
  242. * @param bool $use_platform If set to false, it will forcibly try to assign one of the primitive type (AEDriverMySQL/AEDriverMySQLi) and NEVER tell you to use an AEPlatformDriver* class
  243. * @return string
  244. */
  245. public function get_default_database_driver( $use_platform = true )
  246. {
  247. $jconfig = JFactory::getConfig();
  248. if(version_compare(JVERSION, '3.0', 'ge')) {
  249. $driver = $jconfig->get('dbtype');
  250. } else {
  251. $driver = $jconfig->getValue('config.dbtype');
  252. }
  253. // Let's see what driver Joomla! uses...
  254. if( $use_platform )
  255. {
  256. $hasNookuContent = file_exists(JPATH_ROOT.'/plugins/system/nooku.php');
  257. switch($driver)
  258. {
  259. // MySQL or MySQLi drivers are known to be working; use their
  260. // Akeeba Engine extended version, AEDriverPlatformJoomla
  261. case 'mysql':
  262. if($hasNookuContent) {
  263. return 'AEDriverMysql';
  264. } else {
  265. return 'AEDriverPlatformJoomla';
  266. }
  267. break;
  268. case 'mysqli':
  269. if($hasNookuContent) {
  270. return 'AEDriverMysqli';
  271. } else {
  272. return 'AEDriverPlatformJoomla';
  273. }
  274. break;
  275. case 'sqlsrv':
  276. case 'sqlazure':
  277. return 'AEDriverPlatformJoomla';
  278. break;
  279. // Some custom driver. Uh oh!
  280. default:
  281. break;
  282. }
  283. }
  284. // Is this a subcase of mysqli or mysql drivers?
  285. if( strtolower(substr($driver, 0, 6)) == 'mysqli' )
  286. {
  287. return 'AEDriverMysqli';
  288. }
  289. elseif( strtolower(substr($driver, 0, 5)) == 'mysql' )
  290. {
  291. return 'AEDriverMysql';
  292. }
  293. elseif( strtolower(substr($driver, 0, 6)) == 'sqlsrv' )
  294. {
  295. return 'AEDriverSqlsrv';
  296. }
  297. elseif( strtolower(substr($driver, 0, 6)) == 'sqlazure' )
  298. {
  299. return 'AEDriverSqlazure';
  300. }
  301. // If we're still here, we have to guesstimate the correct driver. All bets are off.
  302. // And you'd better be using MySQL!!!
  303. if(function_exists('mysqli_connect'))
  304. {
  305. // MySQLi available. Let's use it.
  306. return 'AEDriverMysqli';
  307. }
  308. else
  309. {
  310. // MySQLi is not available; let's use standard MySQL.
  311. return 'AEDriverMysql';
  312. }
  313. }
  314. /**
  315. * Returns a set of options to connect to the default database of the current CMS
  316. * @return array
  317. */
  318. public function get_platform_database_options()
  319. {
  320. static $options;
  321. if(empty($options))
  322. {
  323. $conf = JFactory::getConfig();
  324. if(version_compare(JVERSION, '3.0', 'ge')) {
  325. $options = array(
  326. 'host' => $conf->get('host'),
  327. 'user' => $conf->get('user'),
  328. 'password' => $conf->get('password'),
  329. 'database' => $conf->get('db'),
  330. 'prefix' => $conf->get('dbprefix')
  331. );
  332. } else {
  333. $options = array(
  334. 'host' => $conf->getValue('config.host'),
  335. 'user' => $conf->getValue('config.user'),
  336. 'password' => $conf->getValue('config.password'),
  337. 'database' => $conf->getValue('config.db'),
  338. 'prefix' => $conf->getValue('config.dbprefix')
  339. );
  340. }
  341. }
  342. return $options;
  343. }
  344. /**
  345. * Provides a platform-specific translation function
  346. * @param string $key The translation key
  347. * @return string
  348. */
  349. public function translate($key)
  350. {
  351. return JText::_($key);
  352. }
  353. /**
  354. * Populates global constants holding the Akeeba version
  355. */
  356. public function load_version_defines()
  357. {
  358. if(file_exists(JPATH_COMPONENT_ADMINISTRATOR.'/version.php'))
  359. {
  360. require_once(JPATH_COMPONENT_ADMINISTRATOR.'/version.php');
  361. }
  362. if(!defined('AKEEBA_VERSION')) define("AKEEBA_VERSION", "svn");
  363. if(!defined('AKEEBA_PRO')) define('AKEEBA_PRO', false);
  364. if(!defined('AKEEBA_DATE')) {
  365. jimport('joomla.utilities.date');
  366. $date = new JDate();
  367. define( "AKEEBA_DATE", $date->format('Y-m-d') );
  368. }
  369. }
  370. /**
  371. * Returns the platform name and version
  372. * @param string $platform_name Name of the platform, e.g. Joomla!
  373. * @param string $version Full version of the platform
  374. */
  375. public function getPlatformVersion()
  376. {
  377. $v = new JVersion();
  378. return array(
  379. 'name' => 'Joomla!',
  380. 'version' => $v->getShortVersion()
  381. );
  382. }
  383. /**
  384. * Logs platform-specific directories with _AE_LOG_INFO log level
  385. */
  386. public function log_platform_special_directories()
  387. {
  388. AEUtilLogger::WriteLog(_AE_LOG_INFO, "JPATH_BASE :" . JPATH_BASE );
  389. AEUtilLogger::WriteLog(_AE_LOG_INFO, "JPATH_SITE :" . JPATH_SITE );
  390. AEUtilLogger::WriteLog(_AE_LOG_INFO, "JPATH_ROOT :" . JPATH_ROOT );
  391. AEUtilLogger::WriteLog(_AE_LOG_INFO, "JPATH_CACHE :" . JPATH_CACHE );
  392. AEUtilLogger::WriteLog(_AE_LOG_INFO, "Computed root :" . $this->get_site_root() );
  393. // Detect UNC paths and warn the user
  394. if(DIRECTORY_SEPARATOR == '\\') {
  395. if( (substr(JPATH_ROOT, 0, 2) == '\\\\') || (substr(JPATH_ROOT, 0, 2) == '//') ) {
  396. AEUtilLogger::WriteLog(_AE_LOG_WARNING, 'Your site\'s root is using a UNC path (e.g. \\SERVER\path\to\root). PHP has known bugs which may');
  397. AEUtilLogger::WriteLog(_AE_LOG_WARNING, 'prevent it from working properly on a site like this. Please take a look at');
  398. AEUtilLogger::WriteLog(_AE_LOG_WARNING, 'https://bugs.php.net/bug.php?id=40163 and https://bugs.php.net/bug.php?id=52376. As a result your');
  399. AEUtilLogger::WriteLog(_AE_LOG_WARNING, 'backup may fail.');
  400. }
  401. }
  402. }
  403. /**
  404. * Loads a platform-specific software configuration option
  405. * @param string $key
  406. * @param mixed $default
  407. * @return mixed
  408. */
  409. public function get_platform_configuration_option($key, $default)
  410. {
  411. // Get the component configuration option WITHOUT using the bloody ever-changing Joomla! API...
  412. return AEUtilComconfig::getValue($key, $default);
  413. }
  414. /**
  415. * Returns a list of emails to the Super Administrators
  416. * @return unknown_type
  417. */
  418. public function get_administrator_emails()
  419. {
  420. $db = JFactory::getDbo();
  421. $query = $db->getQuery(true);
  422. $query->select(array(
  423. $db->qn('u').'.'.$db->qn('name'),
  424. $db->qn('u').'.'.$db->qn('email'),
  425. ))
  426. ->from($db->qn('#__users').' AS '.$db->qn('u'))
  427. ->join(
  428. 'INNER', $db->qn('#__user_usergroup_map').' AS '.$db->qn('m').' ON ('.
  429. $db->qn('m').'.'.$db->qn('user_id').' = '.$db->qn('u').'.'.$db->qn('id').')'
  430. )
  431. ->where($db->qn('m').'.'.$db->qn('group_id').' = '.$db->q('8'));
  432. $db->setQuery($query);
  433. $superAdmins = $db->loadAssocList();
  434. $mails = array();
  435. if(!empty($superAdmins))
  436. {
  437. foreach($superAdmins as $admin)
  438. {
  439. $mails[] = $admin['email'];
  440. }
  441. }
  442. return $mails;
  443. }
  444. /**
  445. * Sends a very simple email using the platform's emailer facility
  446. * @param string $to
  447. * @param string $subject
  448. * @param string $body
  449. */
  450. public function send_email($to, $subject, $body, $attachFile = null)
  451. {
  452. AEUtilLogger::WriteLog(_AE_LOG_DEBUG,"-- Fetching mailer object" );
  453. $mailer = AEPlatform::getInstance()->getMailer();
  454. if(!is_object($mailer)) {
  455. AEUtilLogger::WriteLog(_AE_LOG_WARNING,"Could not send email to $to - Reason: Mailer object is not an object; please check your system settings");
  456. return false;
  457. }
  458. AEUtilLogger::WriteLog(_AE_LOG_DEBUG,"-- Creating email message");
  459. $recipient = array($to);
  460. $mailer->addRecipient($recipient);
  461. $mailer->setSubject($subject);
  462. $mailer->setBody($body);
  463. if(!empty($attachFile))
  464. {
  465. AEUtilLogger::WriteLog(_AE_LOG_WARNING, "-- Attaching $attachFile");
  466. if(!file_exists($attachFile) || !(is_file($attachFile) || is_link($attachFile))) {
  467. AEUtilLogger::WriteLog(_AE_LOG_WARNING, "The file does not exist, or it's not a file; no email sent");
  468. return false;
  469. }
  470. if(!is_readable($attachFile)) {
  471. AEUtilLogger::WriteLog(_AE_LOG_WARNING, "The file is not readable; no email sent");
  472. return false;
  473. }
  474. $filesize = @filesize($attachFile);
  475. if($filesize) {
  476. // Check that we have AT LEAST 2.5 times free RAM as the filesize (that's how much we'll need)
  477. if(!function_exists('ini_get')) {
  478. // Assume 8Mb of PHP memory limit (worst case scenario)
  479. $totalRAM = 8388608;
  480. } else {
  481. $totalRAM = ini_get('memory_limit');
  482. if(strstr($totalRAM, 'M')) {
  483. $totalRAM = (int)$totalRAM * 1048576;
  484. } elseif(strstr($totalRAM, 'K')) {
  485. $totalRAM = (int)$totalRAM * 1024;
  486. } elseif(strstr($totalRAM, 'G')) {
  487. $totalRAM = (int)$totalRAM * 1073741824;
  488. } else {
  489. $totalRAM = (int)$totalRAM;
  490. }
  491. if($totalRAM <= 0) {
  492. // No memory limit? Cool! Assume 1Gb of available RAM (which is absurdely abundant as of March 2011...)
  493. $totalRAM = 1086373952;
  494. }
  495. }
  496. if(!function_exists('memory_get_usage')) {
  497. $usedRAM = 8388608;
  498. } else {
  499. $usedRAM = memory_get_usage();
  500. }
  501. $availableRAM = $totalRAM - $usedRAM;
  502. if($availableRAM < 2.5*$filesize) {
  503. AEUtilLogger::WriteLog(_AE_LOG_WARNING, "The file is too big to be sent by email. Please use a smaller Part Size for Split Archives setting.");
  504. AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Memory limit $totalRAM bytes -- Used memory $usedRAM bytes -- File size $filesize -- Attachment requires approx. ".(2.5*$filesize)." bytes");
  505. return false;
  506. }
  507. } else {
  508. AEUtilLogger::WriteLog(_AE_LOG_WARNING, "Your server fails to report the file size of $attachFile. If the backup crashes, please use a smaller Part Size for Split Archives setting");
  509. }
  510. $mailer->addAttachment($attachFile);
  511. }
  512. AEUtilLogger::WriteLog(_AE_LOG_DEBUG,"-- Sending message");
  513. $result = $mailer->Send();
  514. if($result instanceof JException)
  515. {
  516. AEUtilLogger::WriteLog(_AE_LOG_WARNING,"Could not email $to:");
  517. AEUtilLogger::WriteLog(_AE_LOG_WARNING,$result->getMessage());
  518. $ret = $result->getMessage();
  519. unset($result);
  520. unset($mailer);
  521. return $ret;
  522. }
  523. else
  524. {
  525. AEUtilLogger::WriteLog(_AE_LOG_DEBUG,"-- Email sent");
  526. return true;
  527. }
  528. }
  529. /**
  530. * Deletes a file from the local server using direct file access or FTP
  531. * @param string $file
  532. * @return bool
  533. */
  534. public function unlink($file)
  535. {
  536. if(function_exists('jimport')) {
  537. jimport('joomla.filesystem.file');
  538. $result = JFile::delete($file);
  539. if(!$result) $result = @unlink($file);
  540. } else {
  541. $result = parent::unlink($file);
  542. }
  543. return $result;
  544. }
  545. /**
  546. * Moves a file around within the local server using direct file access or FTP
  547. * @param string $from
  548. * @param string $to
  549. * @return bool
  550. */
  551. public function move($from, $to)
  552. {
  553. if(function_exists('jimport')) {
  554. jimport('joomla.filesystem.file');
  555. $result = JFile::move($from, $to);
  556. // JFile failed. Let's try rename()
  557. if(!$result)
  558. {
  559. $result = @rename($from, $to);
  560. }
  561. // Rename failed, too. Let's try copy/delete
  562. if(!$result)
  563. {
  564. // Try copying with JFile. If it fails, use copy().
  565. $result = JFile::copy($from, $to);
  566. if(!$result) $result = @copy($from, $to);
  567. // If the copy succeeded, try deleting the original with JFile. If it fails, use unlink().
  568. if($result)
  569. {
  570. $result = $this->unlink($from);
  571. }
  572. }
  573. } else {
  574. $result = parent::move($from, $to);
  575. }
  576. return $result;
  577. }
  578. /**
  579. * Registers Akeeba Engine's core classes with JLoader
  580. * @param string $path_prefix The path prefix to look in
  581. */
  582. protected function register_akeeba_engine_classes($path_prefix)
  583. {
  584. global $Akeeba_Class_Map;
  585. jimport('joomla.filesystem.folder');
  586. foreach($Akeeba_Class_Map as $class_prefix => $path_suffix)
  587. {
  588. // Bail out if there is such directory, so as not to have Joomla! throw errors
  589. if(!@is_dir($path_prefix.'/'.$path_suffix)) continue;
  590. $file_list = JFolder::files( $path_prefix.'/'.$path_suffix, '.*\.php' );
  591. if(is_array($file_list) && !empty($file_list)) foreach($file_list as $file)
  592. {
  593. $class_suffix = ucfirst(basename($file, '.php'));
  594. JLoader::register($class_prefix.$class_suffix, $path_prefix.'/'.$path_suffix.'/'.$file );
  595. }
  596. }
  597. }
  598. /**
  599. * Joomla!-specific function to get an instance of the mailer class
  600. * @return JMail
  601. */
  602. public function &getMailer()
  603. {
  604. $mailer = JFactory::getMailer();
  605. if(!is_object($mailer)) {
  606. AEUtilLogger::WriteLog(_AE_LOG_WARNING,"Fetching Joomla!'s mailer was impossible; imminent crash!");
  607. } else {
  608. $emailMethod = $mailer->Mailer;
  609. AEUtilLogger::WriteLog(_AE_LOG_DEBUG,"-- Joomla!'s mailer is using $emailMethod mail method.");
  610. }
  611. return $mailer;
  612. }
  613. }