PageRenderTime 40ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/administrator/components/com_akeeba/models/cpanels.php

https://bitbucket.org/kraymitchell/apex
PHP | 603 lines | 431 code | 66 blank | 106 comment | 77 complexity | e497ff9322ee1ee3a515102a080f992d 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. * @package AkeebaBackup
  4. * @copyright Copyright (c)2009-2012 Nicholas K. Dionysopoulos
  5. * @license GNU General Public License version 3, or later
  6. * @since 1.3
  7. */
  8. // Protect from unauthorized access
  9. defined('_JEXEC') or die();
  10. /**
  11. * The Control Panel model
  12. *
  13. */
  14. class AkeebaModelCpanels extends FOFModel
  15. {
  16. /**
  17. * Get an array of icon definitions for the Control Panel
  18. *
  19. * @return array
  20. */
  21. public function getIconDefinitions()
  22. {
  23. AEPlatform::getInstance()->load_version_defines();
  24. $core = $this->loadIconDefinitions(JPATH_COMPONENT_ADMINISTRATOR.'/views');
  25. if(AKEEBA_PRO) {
  26. $pro = $this->loadIconDefinitions(JPATH_COMPONENT_ADMINISTRATOR.'/plugins/views');
  27. } else {
  28. $pro = array();
  29. }
  30. $ret = array_merge_recursive($core, $pro);
  31. return $ret;
  32. }
  33. private function loadIconDefinitions($path)
  34. {
  35. $ret = array();
  36. if(!@file_exists($path.'/views.ini')) return $ret;
  37. $ini_data = AEUtilINI::parse_ini_file($path.'/views.ini', true);
  38. if(!empty($ini_data))
  39. {
  40. foreach($ini_data as $view => $def)
  41. {
  42. $task = array_key_exists('task',$def) ? $def['task'] : null;
  43. $ret[$def['group']][] = $this->_makeIconDefinition($def['icon'], JText::_($def['label']), $view, $task);
  44. }
  45. }
  46. return $ret;
  47. }
  48. /**
  49. * Returns a list of available backup profiles, to be consumed by JHTML in order to build
  50. * a drop-down
  51. *
  52. * @return array
  53. */
  54. public function getProfilesList()
  55. {
  56. $db = $this->getDbo();
  57. $query = $db->getQuery(true)
  58. ->select(array(
  59. $db->qn('id'),
  60. $db->qn('description')
  61. ))->from($db->qn('#__ak_profiles'))
  62. ->order($db->qn('id')." ASC");
  63. $db->setQuery($query);
  64. $rawList = $db->loadAssocList();
  65. $options = array();
  66. if(!is_array($rawList)) return $options;
  67. foreach($rawList as $row)
  68. {
  69. $options[] = JHTML::_('select.option', $row['id'], $row['description']);
  70. }
  71. return $options;
  72. }
  73. /**
  74. * Returns the active Profile ID
  75. *
  76. * @return int The active profile ID
  77. */
  78. public function getProfileID()
  79. {
  80. $session = JFactory::getSession();
  81. return $session->get('profile', null, 'akeeba');
  82. }
  83. /**
  84. * Creates an icon definition entry
  85. *
  86. * @param string $iconFile The filename of the icon on the GUI button
  87. * @param string $label The label below the GUI button
  88. * @param string $view The view to fire up when the button is clicked
  89. * @return array The icon definition array
  90. */
  91. public function _makeIconDefinition($iconFile, $label, $view = null, $task = null )
  92. {
  93. return array(
  94. 'icon' => $iconFile,
  95. 'label' => $label,
  96. 'view' => $view,
  97. 'task' => $task
  98. );
  99. }
  100. /**
  101. * Was the last backup a failed one? Used to apply magic settings as a means of
  102. * troubleshooting.
  103. *
  104. * @return bool
  105. */
  106. public function isLastBackupFailed()
  107. {
  108. // Get the last backup record ID
  109. $list = AEPlatform::getInstance()->get_statistics_list(array('limitstart' => 0, 'limit' => 1));
  110. if(empty($list)) return false;
  111. $id = $list[0];
  112. $record = AEPlatform::getInstance()->get_statistics($id);
  113. return ($record['status'] == 'fail');
  114. }
  115. /**
  116. * Checks that the media permissions are 0755 for directories and 0644 for files
  117. * and fixes them if they are incorrect.
  118. *
  119. * @param $force bool Forcibly check subresources, even if the parent has correct permissions
  120. *
  121. * @return bool False if we couldn't figure out what's going on
  122. */
  123. public function fixMediaPermissions($force = false)
  124. {
  125. // Are we on Windows?
  126. if (function_exists('php_uname'))
  127. {
  128. $isWindows = stristr(php_uname(), 'windows');
  129. }
  130. else
  131. {
  132. $isWindows = (DIRECTORY_SEPARATOR == '\\');
  133. }
  134. // No point changing permissions on Windows, as they have ACLs
  135. if($isWindows) return true;
  136. // Check the parent permissions
  137. $parent = JPATH_ROOT.'/media/com_akeeba';
  138. $parentPerms = fileperms($parent);
  139. // If we can't determine the parent's permissions, bail out
  140. if($parentPerms === false) return false;
  141. // Fix the parent's permissions if required
  142. if(($parentPerms != 0755) && ($parentPerms != 40755)) {
  143. $this->chmod($parent, 0755);
  144. } else {
  145. if(!$force) return true;
  146. }
  147. // During development we use symlinks and we don't wanna see that big fat warning
  148. if(@is_link($parent)) return true;
  149. jimport('joomla.filesystem.folder');
  150. $result = true;
  151. // Loop through subdirectories
  152. $folders = JFolder::folders($parent,'.',3,true);
  153. foreach($folders as $folder) {
  154. $perms = fileperms($folder);
  155. if(($perms != 0755) && ($perms != 40755)) $result &= $this->chmod($folder, 0755);
  156. }
  157. // Loop through files
  158. $files = JFolder::files($parent,'.',3,true);
  159. foreach($files as $file) {
  160. $perms = fileperms($file);
  161. if(($perms != 0644) && ($perms != 0100644)) {
  162. $result &= $this->chmod($file, 0644);
  163. }
  164. }
  165. return $result;
  166. }
  167. /**
  168. * Tries to change a folder/file's permissions using direct access or FTP
  169. *
  170. * @param string $path The full path to the folder/file to chmod
  171. * @param int $mode New permissions
  172. */
  173. private function chmod($path, $mode)
  174. {
  175. if(is_string($mode))
  176. {
  177. $mode = octdec($mode);
  178. if( ($mode < 0600) || ($mode > 0777) ) $mode = 0755;
  179. }
  180. // Initialize variables
  181. jimport('joomla.client.helper');
  182. $ftpOptions = JClientHelper::getCredentials('ftp');
  183. // Check to make sure the path valid and clean
  184. $path = JPath::clean($path);
  185. if ($ftpOptions['enabled'] == 1) {
  186. // Connect the FTP client
  187. jimport('joomla.client.ftp');
  188. if(version_compare(JVERSION,'3.0','ge')) {
  189. $ftp = JClientFTP::getInstance(
  190. $ftpOptions['host'], $ftpOptions['port'], null,
  191. $ftpOptions['user'], $ftpOptions['pass']
  192. );
  193. } else {
  194. $ftp = JFTP::getInstance(
  195. $ftpOptions['host'], $ftpOptions['port'], null,
  196. $ftpOptions['user'], $ftpOptions['pass']
  197. );
  198. }
  199. }
  200. if(@chmod($path, $mode))
  201. {
  202. $ret = true;
  203. } elseif ($ftpOptions['enabled'] == 1) {
  204. // Translate path and delete
  205. $path = JPath::clean(str_replace(JPATH_ROOT, $ftpOptions['root'], $path), '/');
  206. // FTP connector throws an error
  207. $ret = $ftp->chmod($path, $mode);
  208. } else {
  209. $ret = false;
  210. }
  211. return $ret;
  212. }
  213. /**
  214. * Checks if we should enable settings encryption and applies the change
  215. */
  216. public function checkSettingsEncryption()
  217. {
  218. // Do we have a key file?
  219. jimport('joomla.filesystem.file');
  220. $filename = JPATH_COMPONENT_ADMINISTRATOR.'/akeeba/serverkey.php';
  221. if(JFile::exists($filename)) {
  222. // We have a key file. Do we need to disable it?
  223. if(AEPlatform::getInstance()->get_platform_configuration_option('useencryption', -1) == 0) {
  224. // User asked us to disable encryption. Let's do it.
  225. $this->disableSettingsEncryption();
  226. }
  227. } else {
  228. if(!AEUtilSecuresettings::supportsEncryption()) return;
  229. if(AEPlatform::getInstance()->get_platform_configuration_option('useencryption', -1) != 0) {
  230. // User asked us to enable encryption (or he left us with the default setting!). Let's do it.
  231. $this->enableSettingsEncryption();
  232. }
  233. }
  234. }
  235. private function disableSettingsEncryption()
  236. {
  237. // Load the server key file if necessary
  238. jimport('joomla.filesystem.file');
  239. $filename = JPATH_COMPONENT_ADMINISTRATOR.'/akeeba/serverkey.php';
  240. $key = AEUtilSecuresettings::getKey();
  241. // Loop all profiles and decrypt their settings
  242. $profilesModel = FOFModel::getTmpInstance('Profiles','AkeebaModel');
  243. $profiles = $profilesModel->getItemsList(true);
  244. $db = $this->getDBO();
  245. foreach($profiles as $profile)
  246. {
  247. $id = $profile->id;
  248. $config = AEUtilSecuresettings::decryptSettings($profile->configuration, $key);
  249. $sql = $db->getQuery(true)
  250. ->update($db->qn('#__ak_profiles'))
  251. ->set($db->qn('configuration').' = '.$db->q($config))
  252. ->where($db->qn('id').' = '. $db->q($id));
  253. $db->setQuery($sql);
  254. $db->query();
  255. }
  256. // Finally, remove the key file
  257. JFile::delete($filename);
  258. }
  259. private function enableSettingsEncryption()
  260. {
  261. $key = $this->createSettingsKey();
  262. if(empty($key) || ($key==false)) return;
  263. // Loop all profiles and encrypt their settings
  264. $profilesModel = FOFModel::getTmpInstance('Profiles','AkeebaModel');
  265. $profiles = $profilesModel->getItemsList(true);
  266. $db = $this->getDBO();
  267. if(!empty($profiles)) foreach($profiles as $profile)
  268. {
  269. $id = $profile->id;
  270. $config = AEUtilSecuresettings::encryptSettings($profile->configuration, $key);
  271. $sql = $db->getQuery(true)
  272. ->update($db->qn('#__ak_profiles'))
  273. ->set($db->qn('configuration').' = '.$db->q($config))
  274. ->where($db->qn('id').' = '. $db->q($id));
  275. $db->setQuery($sql);
  276. $db->query();
  277. }
  278. }
  279. private function createSettingsKey()
  280. {
  281. jimport('joomla.filesystem.file');
  282. $seedA = md5( JFile::read(JPATH_ROOT.'/configuration.php') );
  283. $seedB = md5( microtime() );
  284. $seed = $seedA.$seedB;
  285. $md5 = md5($seed);
  286. for($i = 0; $i < 1000; $i++) {
  287. $md5 = md5( $md5 . md5(rand(0, 2147483647)) );
  288. }
  289. $key = base64_encode( $md5 );
  290. $filecontents = "<?php defined('AKEEBAENGINE') or die(); define('AKEEBA_SERVERKEY', '$key'); ?>";
  291. $filename = JPATH_COMPONENT_ADMINISTRATOR.'/akeeba/serverkey.php';
  292. $result = JFile::write($filename, $filecontents);
  293. if(!$result) {
  294. return false;
  295. } else {
  296. return base64_decode($key);
  297. }
  298. }
  299. /**
  300. * Update the cached live site's URL for the front-end backup feature (altbackup.php)
  301. * and the detected Joomla! libraries path
  302. */
  303. public function updateMagicParameters()
  304. {
  305. $component = JComponentHelper::getComponent( 'com_akeeba' );
  306. if(is_object($component->params) && ($component->params instanceof JRegistry)) {
  307. $params = $component->params;
  308. } else {
  309. $params = new JParameter($component->params);
  310. }
  311. $params->set( 'siteurl', str_replace('/administrator','',JURI::base()) );
  312. if(defined('JPATH_LIBRARIES')) {
  313. $params->set('jlibrariesdir', AEUtilFilesystem::TranslateWinPath(JPATH_LIBRARIES));
  314. } elseif(defined("JPATH_PLATFORM")) {
  315. $params->set('jlibrariesdir', AEUtilFilesystem::TranslateWinPath(JPATH_PLATFORM));
  316. }
  317. $joomla16 = true;
  318. $params->set( 'jversion', '1.6' );
  319. $db = JFactory::getDBO();
  320. $data = $params->toString();
  321. $sql = $db->getQuery(true)
  322. ->update($db->qn('#__extensions'))
  323. ->set($db->qn('params').' = '.$db->q($data))
  324. ->where($db->qn('element').' = '.$db->q('com_akeeba'))
  325. ->where($db->qn('type').' = '.$db->q('component'));
  326. $db->setQuery($sql);
  327. $db->query();
  328. }
  329. public function needsDownloadID()
  330. {
  331. // Do I need a Download ID?
  332. $ret = true;
  333. $isPro = AKEEBA_PRO;
  334. if(!$isPro) {
  335. $ret = false;
  336. } else {
  337. jimport('joomla.application.component.helper');
  338. $dlid = AEUtilComconfig::getValue('update_dlid', '');
  339. if(preg_match('/^[0-9a-f]{32}$/i', $dlid)) {
  340. $ret = false;
  341. }
  342. }
  343. // Deactivate update site for Akeeba Backup
  344. jimport('joomla.application.component.helper');
  345. $component = JComponentHelper::getComponent('com_akeeba');
  346. $db = JFactory::getDbo();
  347. $query = $db->getQuery(true)
  348. ->select('update_site_id')
  349. ->from($db->qn('#__update_sites_extensions'))
  350. ->where($db->qn('extension_id').' = '.$db->q($component->id));
  351. $db->setQuery($query);
  352. $updateSite = $db->loadResult();
  353. if($updateSite) {
  354. $query = $db->getQuery(true)
  355. ->delete($db->qn('#__update_sites'))
  356. ->where($db->qn('update_site_id').' = '.$db->q($updateSite));
  357. $db->setQuery($query);
  358. $db->query();
  359. $query = $db->getQuery(true)
  360. ->delete($db->qn('#__update_sites_extensions'))
  361. ->where($db->qn('update_site_id').' = '.$db->q($updateSite));
  362. $db->setQuery($query);
  363. $db->query();
  364. }
  365. // Deactivate the update site for FOF
  366. $query = $db->getQuery(true)
  367. ->select('update_site_id')
  368. ->from($db->qn('#__update_sites'))
  369. ->where($db->qn('location').' = '.$db->q('http://cdn.akeebabackup.com/updates/libraries/fof'));
  370. $db->setQuery($query);
  371. $updateSite = $db->loadResult();
  372. if($updateSite) {
  373. $query = $db->getQuery(true)
  374. ->delete($db->qn('#__update_sites'))
  375. ->where($db->qn('update_site_id').' = '.$db->q($updateSite));
  376. $db->setQuery($query);
  377. $db->query();
  378. $query = $db->getQuery(true)
  379. ->delete($db->qn('#__update_sites_extensions'))
  380. ->where($db->qn('update_site_id').' = '.$db->q($updateSite));
  381. $db->setQuery($query);
  382. $db->query();
  383. }
  384. return $ret;
  385. }
  386. /**
  387. * Makes sure that the Professional release can be updated using Joomla!'s
  388. * own update system. THIS IS AN AKEEBA ORIGINAL!
  389. */
  390. public function applyJoomlaExtensionUpdateChanges($isPro = -1)
  391. {
  392. $ret = true;
  393. // Don';'t bother if this is not Joomla! 1.7+
  394. if(!version_compare(JVERSION, '1.7.0', 'ge')) return $ret;
  395. // Do we have Admin Tools Professional?
  396. if($isPro === -1) {
  397. $isPro = AKEEBA_PRO;
  398. }
  399. // Action parameters
  400. $action = 'none'; // What to do: none, update, create, delete
  401. $purgeUpdates = false; // Should I purge existing updates?
  402. $fetchUpdates = false; // Should I fetch new udpater
  403. // Init
  404. $db = $this->getDbo();
  405. // Figure out the correct XML update stream URL
  406. if($isPro) {
  407. $update_url = 'https://www.akeebabackup.com/index.php?option=com_ars&view=update&task=stream&format=xml&id=6';
  408. jimport('joomla.application.component.helper');
  409. $params = JComponentHelper::getParams('com_akeeba');
  410. if(version_compare(JVERSION, '3.0', 'ge')) {
  411. $dlid = $params->get('update_dlid','');
  412. } else {
  413. $dlid = $params->getValue('update_dlid','');
  414. }
  415. if(!preg_match('/^[0-9a-f]{32}$/i', $dlid)) {
  416. $ret = false;
  417. $dlid = '';
  418. }
  419. if($dlid) {
  420. $dlid = $dlid;
  421. $url = $update_url.'&dlid='.$dlid.'/extension.xml';
  422. } else {
  423. $url = '';
  424. }
  425. } else {
  426. $url = 'http://cdn.akeebabackup.com/updates/atcore.xml';
  427. }
  428. // Get the extension ID
  429. $extensionID = JComponentHelper::getComponent('com_akeeba')->id;
  430. // Get the update site record
  431. $query = $db->getQuery(true)
  432. ->select(array(
  433. $db->qn('us').'.*',
  434. ))->from(
  435. $db->qn('#__update_sites_extensions').' AS '.$db->qn('map')
  436. )->innerJoin(
  437. $db->qn('#__update_sites').' AS '.$db->qn('us').' ON ('.
  438. $db->qn('us').'.'.$db->qn('update_site_id').' = '.
  439. $db->qn('map').'.'.$db->qn('update_site_id').')'
  440. )
  441. ->where(
  442. $db->qn('map').'.'.$db->qn('extension_id').' = '.$db->q($extensionID)
  443. );
  444. $db->setQuery($query);
  445. $update_site = $db->loadObject();
  446. // Decide on the course of action to take
  447. if($url) {
  448. if(!is_object($update_site)) {
  449. $action = 'create';
  450. $fetchUpdates = true;
  451. } else {
  452. $action = ($update_site->location != $url) ? 'update' : 'none';
  453. $purgeUpdates = $action == 'update';
  454. $fetchUpdates = $action == 'update';
  455. }
  456. } else {
  457. // Disable the update site for Akeeba Backup
  458. if(!is_object($update_site)) {
  459. $action = 'none';
  460. } else {
  461. $action = 'delete';
  462. $purgeUpdates = true;
  463. }
  464. }
  465. switch($action)
  466. {
  467. case 'none':
  468. // No change
  469. break;
  470. case 'create':
  471. case 'update':
  472. // Remove old update site
  473. $query = $db->getQuery(true)
  474. ->delete($db->qn('#__update_sites'))
  475. ->where($db->qn('name') .' = '. $db->q('Akeeba Backup updates'));
  476. $db->setQuery($query);
  477. $db->query();
  478. // Create new update site
  479. $oUpdateSite = (object)array(
  480. 'name' => 'Akeeba Backup updates',
  481. 'type' => 'extension',
  482. 'location' => $url,
  483. 'enabled' => 1,
  484. 'last_check_timestamp' => 0,
  485. );
  486. $db->insertObject('#__update_sites', $oUpdateSite);
  487. // Get the update site ID
  488. $usID = $db->insertid();
  489. // Delete existing #__update_sites_extensions records
  490. $query = $db->getQuery(true)
  491. ->delete($db->qn('#__update_sites_extensions'))
  492. ->where($db->qn('extension_id') .' = '. $db->q($extensionID));
  493. $db->setQuery($query);
  494. $db->query();
  495. // Create new #__update_sites_extensions record
  496. $oUpdateSitesExtensions = (object)array(
  497. 'update_site_id' => $usID,
  498. 'extension_id' => $extensionID
  499. );
  500. $db->insertObject('#__update_sites_extensions', $oUpdateSitesExtensions);
  501. break;
  502. case 'delete':
  503. // Remove update sites
  504. $query = $db->getQuery(true)
  505. ->delete($db->qn('#__update_sites'))
  506. ->where($db->qn('update_site_id') .' = '. $db->q($update_site->update_site_id));
  507. $db->setQuery($query);
  508. $db->query();
  509. // Delete existing #__update_sites_extensions records
  510. $query = $db->getQuery(true)
  511. ->delete($db->qn('#__update_sites_extensions'))
  512. ->where($db->qn('extension_id') .' = '. $db->q($extensionID));
  513. $db->setQuery($query);
  514. $db->query();
  515. break;
  516. }
  517. // Do I have to purge updates?
  518. if($purgeUpdates) {
  519. $query = $db->getQuery(true)
  520. ->delete($db->qn('#__updates'))
  521. ->where($db->qn('element').' = '.$db->q('com_akeeba'));
  522. $db->setQuery($query);
  523. $db->query();
  524. }
  525. // Do I have to fetch updates?
  526. if($fetchUpdates) {
  527. jimport('joomla.update.update');
  528. $x = new JUpdater();
  529. $x->findUpdates($extensionID);
  530. }
  531. return $ret;
  532. }
  533. }