PageRenderTime 58ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/components/com_akeeba/models/json.php

https://bitbucket.org/organicdevelopment/joomla-2.5
PHP | 975 lines | 739 code | 139 blank | 97 comment | 77 complexity | 9d19dfd59984afc7a638b95bcd04d867 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0, MIT, BSD-3-Clause, LGPL-2.1
  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. * @version $Id$
  7. * @since 3.0
  8. */
  9. // Protect from unauthorized access
  10. defined('_JEXEC') or die('Restricted Access');
  11. // JSON API version number
  12. define('AKEEBA_JSON_API_VERSION', '319');
  13. /*
  14. * Short API version history:
  15. * 300 First draft. Basic backup working. Encryption semi-broken.
  16. * 316 Fixed download feature.
  17. */
  18. // Load framework base classes
  19. jimport('joomla.application.component.model');
  20. // Force load the AEUtilEncrypt class if it's Akeeba Backup Professional
  21. if(AKEEBA_PRO == 1) $dummy = new AEUtilEncrypt;
  22. if(!defined('AKEEBA_BACKUP_ORIGIN'))
  23. {
  24. define('AKEEBA_BACKUP_ORIGIN','json');
  25. }
  26. class AkeebaModelJson extends JModel
  27. {
  28. const STATUS_OK = 200; // Normal reply
  29. const STATUS_NOT_AUTH = 401; // Invalid credentials
  30. const STATUS_NOT_ALLOWED = 403; // Not enough privileges
  31. const STATUS_NOT_FOUND = 404; // Requested resource not found
  32. const STATUS_INVALID_METHOD = 405; // Unknown JSON method
  33. const STATUS_ERROR = 500; // An error occurred
  34. const STATUS_NOT_IMPLEMENTED = 501; // Not implemented feature
  35. const STATUS_NOT_AVAILABLE = 503; // Remote service not activated
  36. const ENCAPSULATION_RAW = 1; // Data in plain-text JSON
  37. const ENCAPSULATION_AESCTR128 = 2; // Data in AES-128 stream (CTR) mode encrypted JSON
  38. const ENCAPSULATION_AESCTR256 = 3; // Data in AES-256 stream (CTR) mode encrypted JSON
  39. const ENCAPSULATION_AESCBC128 = 4; // Data in AES-128 standard (CBC) mode encrypted JSON
  40. const ENCAPSULATION_AESCBC256 = 5; // Data in AES-256 standard (CBC) mode encrypted JSON
  41. private $json_errors = array(
  42. 'JSON_ERROR_NONE' => 'No error has occurred (probably emtpy data passed)',
  43. 'JSON_ERROR_DEPTH' => 'The maximum stack depth has been exceeded',
  44. 'JSON_ERROR_CTRL_CHAR' => 'Control character error, possibly incorrectly encoded',
  45. 'JSON_ERROR_SYNTAX' => 'Syntax error'
  46. );
  47. /** @var int The status code */
  48. private $status = 200;
  49. /** @var int Data encapsulation format */
  50. private $encapsulation = 1;
  51. /** @var mixed Any data to be returned to the caller */
  52. private $data = '';
  53. /** @var string A password passed to us by the caller */
  54. private $password = null;
  55. /** @var string The method called by the client */
  56. private $method_name = null;
  57. public function execute($json)
  58. {
  59. // Check if we're activated
  60. $enabled = AEPlatform::getInstance()->get_platform_configuration_option('frontend_enable', 0);
  61. if(!$enabled)
  62. {
  63. $this->data = 'Access denied';
  64. $this->status = self::STATUS_NOT_AVAILABLE;
  65. $this->encapsulation = self::ENCAPSULATION_RAW;
  66. return $this->getResponse();
  67. }
  68. // Try to JSON-decode the request's input first
  69. $request = @$this->json_decode($json, false);
  70. if(is_null($request))
  71. {
  72. // Could not decode JSON
  73. $this->data = 'JSON decoding error';
  74. $this->status = self::STATUS_ERROR;
  75. $this->encapsulation = self::ENCAPSULATION_RAW;
  76. return $this->getResponse();
  77. }
  78. // Decode the request body
  79. // Request format: {encapsulation, body{ [key], [challenge], method, [data] }} or {[challenge], method, [data]}
  80. if( isset($request->encapsulation) && isset($request->body) )
  81. {
  82. if(!class_exists('AEUtilEncrypt') && !($request->encapsulation == self::ENCAPSULATION_RAW))
  83. {
  84. // Encrypted request found, but there is no encryption class available!
  85. $this->data = 'This server does not support encrypted requests';
  86. $this->status = self::STATUS_NOT_AVAILABLE;
  87. $this->encapsulation = self::ENCAPSULATION_RAW;
  88. return $this->getResponse();
  89. }
  90. // Fully specified request
  91. switch( $request->encapsulation )
  92. {
  93. case self::ENCAPSULATION_AESCBC128:
  94. if(!isset($body))
  95. {
  96. $request->body = base64_decode($request->body);
  97. $body = AEUtilEncrypt::AESDecryptCBC($request->body, $this->serverKey(), 128);
  98. }
  99. break;
  100. case self::ENCAPSULATION_AESCBC256:
  101. if(!isset($body))
  102. {
  103. $request->body = base64_decode($request->body);
  104. $body = AEUtilEncrypt::AESDecryptCBC($request->body, $this->serverKey(), 256);
  105. }
  106. break;
  107. case self::ENCAPSULATION_AESCTR128:
  108. if(!isset($body))
  109. {
  110. $body = AEUtilEncrypt::AESDecryptCtr($request->body, $this->serverKey(), 128);
  111. }
  112. break;
  113. case self::ENCAPSULATION_AESCTR256:
  114. if(!isset($body))
  115. {
  116. $body = AEUtilEncrypt::AESDecryptCtr($request->body, $this->serverKey(), 256);
  117. }
  118. break;
  119. case self::ENCAPSULATION_RAW:
  120. $body = $request->body;
  121. break;
  122. }
  123. if(!empty($request->body))
  124. {
  125. $body = rtrim( $body, chr(0) );
  126. $request->body = $this->json_decode($body);
  127. if(is_null($request->body))
  128. {
  129. // Decryption failed. The user is an imposter! Go away, hacker!
  130. $this->data = 'Authentication failed';
  131. $this->status = self::STATUS_NOT_AUTH;
  132. $this->encapsulation = self::ENCAPSULATION_RAW;
  133. return $this->getResponse();
  134. }
  135. }
  136. }
  137. elseif( isset($request->body) )
  138. {
  139. // Partially specified request, assume RAW encapsulation
  140. $request->encapsulation = self::ENCAPSULATION_RAW;
  141. $request->body = $this->json_decode($request->body);
  142. }
  143. else
  144. {
  145. // Legacy request
  146. $legacyRequest = clone $request;
  147. $request = (object) array( 'encapsulation' => self::ENCAPSULATION_RAW, 'body' => null );
  148. $request->body = $this->json_decode($legacyRequest);
  149. unset($legacyRequest);
  150. }
  151. // Authenticate the user. Do note that if an encrypted request was made, we can safely assume that
  152. // the user is authenticated (he already knows the server key!)
  153. if($request->encapsulation == self::ENCAPSULATION_RAW)
  154. {
  155. $authenticated = false;
  156. if(isset($request->body->challenge))
  157. {
  158. list($challenge,$check) = explode(':', $request->body->challenge);
  159. $crosscheck = strtolower(md5($challenge.$this->serverKey()));
  160. $authenticated = ($crosscheck == $check);
  161. }
  162. if(!$authenticated)
  163. {
  164. // If the challenge was missing or it was wrong, don't let him go any further
  165. $this->data = 'Invalid login credentials';
  166. $this->status = self::STATUS_NOT_AUTH;
  167. $this->encapsulation = self::ENCAPSULATION_RAW;
  168. return $this->getResponse();
  169. }
  170. }
  171. // Replicate the encapsulation preferences of the client for our own output
  172. $this->encapsulation = $request->encapsulation;
  173. // Store the client-specified key, or use the server key if none specified and the request
  174. // came encrypted.
  175. $this->password = isset($request->body->key) ? $request->body->key : null;
  176. $hasKey = property_exists($request->body, 'key') ? !is_null($request->body->key) : false;
  177. if(!$hasKey && ($request->encapsulation != self::ENCAPSULATION_RAW) )
  178. {
  179. $this->password = $this->serverKey();
  180. }
  181. // Does the specified method exist?
  182. $method_exists = false;
  183. $method_name = '';
  184. if(isset($request->body->method))
  185. {
  186. $method_name = ucfirst($request->body->method);
  187. $this->method_name = $method_name;
  188. $method_exists = method_exists($this, '_api'.$method_name );
  189. }
  190. if(!$method_exists)
  191. {
  192. // The requested method doesn't exist. Oops!
  193. $this->data = "Invalid method $method_name";
  194. $this->status = self::STATUS_INVALID_METHOD;
  195. $this->encapsulation = self::ENCAPSULATION_RAW;
  196. return $this->getResponse();
  197. }
  198. // Run the method
  199. $params = array();
  200. if(isset($request->body->data)) $params = (array)$request->body->data;
  201. $this->data = call_user_func( array($this, '_api'.$method_name) , $params);
  202. return $this->getResponse();
  203. }
  204. /**
  205. * Packages the response to a JSON-encoded object, optionally encrypting the
  206. * data part with a caller-supplied password.
  207. * @return string The JSON-encoded response
  208. */
  209. private function getResponse()
  210. {
  211. // Initialize the response
  212. $response = array(
  213. 'encapsulation' => $this->encapsulation,
  214. 'body' => array(
  215. 'status' => $this->status,
  216. 'data' => null
  217. )
  218. );
  219. switch($this->method_name)
  220. {
  221. case 'Download':
  222. $data = json_encode($this->data);
  223. break;
  224. default:
  225. $data = $this->json_encode($this->data);
  226. break;
  227. }
  228. if(empty($this->password)) $this->encapsulation = self::ENCAPSULATION_RAW;
  229. switch($this->encapsulation)
  230. {
  231. case self::ENCAPSULATION_RAW:
  232. break;
  233. case self::ENCAPSULATION_AESCTR128:
  234. $data = AEUtilEncrypt::AESEncryptCtr($data, $this->password, 128);
  235. break;
  236. case self::ENCAPSULATION_AESCTR256:
  237. $data = AEUtilEncrypt::AESEncryptCtr($data, $this->password, 256);
  238. break;
  239. case self::ENCAPSULATION_AESCBC128:
  240. $data = base64_encode(AEUtilEncrypt::AESEncryptCBC($data, $this->password, 128));
  241. break;
  242. case self::ENCAPSULATION_AESCBC256:
  243. $data = base64_encode(AEUtilEncrypt::AESEncryptCBC($data, $this->password, 256));
  244. break;
  245. }
  246. $response['body']['data'] = $data;
  247. switch($this->method_name)
  248. {
  249. case 'Download':
  250. return '###' . json_encode($response) . '###';
  251. break;
  252. default:
  253. return '###' . $this->json_encode($response) . '###';
  254. break;
  255. }
  256. }
  257. private function serverKey()
  258. {
  259. static $key = null;
  260. if(is_null($key))
  261. {
  262. $key = AEPlatform::getInstance()->get_platform_configuration_option('frontend_secret_word', '');
  263. }
  264. return $key;
  265. }
  266. private function _apiGetVersion()
  267. {
  268. require_once JPATH_ROOT.'/administrator/components/com_akeeba/liveupdate/liveupdate.php';
  269. $updateInformation = LiveUpdate::getUpdateInformation();
  270. return (object)array(
  271. 'api' => AKEEBA_JSON_API_VERSION,
  272. 'component' => AKEEBA_VERSION,
  273. 'date' => AKEEBA_DATE,
  274. 'updateinfo' => $updateInformation
  275. );
  276. }
  277. private function _apiGetProfiles()
  278. {
  279. require_once JPATH_SITE.'/administrator/components/com_akeeba/models/profiles.php';
  280. $model = new AkeebaModelProfiles();
  281. $profiles = $model->getProfilesList(true);
  282. $ret = array();
  283. if(count($profiles))
  284. {
  285. foreach($profiles as $profile)
  286. {
  287. $temp = new stdClass();
  288. $temp->id = $profile->id;
  289. $temp->name = $profile->description;
  290. $ret[] = $temp;
  291. }
  292. }
  293. return $ret;
  294. }
  295. private function _apiStartBackup($config)
  296. {
  297. // Get the passed configuration values
  298. $defConfig = array(
  299. 'profile' => 1,
  300. 'description' => '',
  301. 'comment' => ''
  302. );
  303. $config = array_merge($defConfig, $config);
  304. foreach($config as $key => $value) {
  305. if(!array_key_exists($key, $defConfig)) unset($config[$key]);
  306. }
  307. extract($config);
  308. // Nuke the factory
  309. AEFactory::nuke();
  310. // Set the profile
  311. $profile = (int)$profile;
  312. if(!is_numeric($profile)) $profile = 1;
  313. $session = JFactory::getSession();
  314. $session->set('profile', $profile, 'akeeba');
  315. AEPlatform::getInstance()->load_configuration($profile);
  316. // Use the default description if none specified
  317. if(empty($description))
  318. {
  319. jimport('joomla.utilities.date');
  320. $user = JFactory::getUser();
  321. $userTZ = $user->getParam('timezone',0);
  322. $dateNow = new JDate();
  323. $dateNow->setOffset($userTZ);
  324. if( AKEEBA_JVERSION == '16' ) {
  325. $description = JText::_('BACKUP_DEFAULT_DESCRIPTION').' '.$dateNow->format(JText::_('DATE_FORMAT_LC2'), true);
  326. } else {
  327. $description = JText::_('BACKUP_DEFAULT_DESCRIPTION').' '.$dateNow->toFormat(JText::_('DATE_FORMAT_LC2'));
  328. }
  329. }
  330. // Start the backup
  331. AECoreKettenrad::reset(array(
  332. 'maxrun' => 0
  333. ));
  334. AEUtilTempvars::reset(AKEEBA_BACKUP_ORIGIN);
  335. $kettenrad = AECoreKettenrad::load(AKEEBA_BACKUP_ORIGIN);
  336. $options = array(
  337. 'description' => $description,
  338. 'comment' => $comment,
  339. 'tag' => AKEEBA_BACKUP_ORIGIN
  340. );
  341. $kettenrad->setup($options); // Setting up the engine
  342. $array = $kettenrad->tick(); // Initializes the init domain
  343. AECoreKettenrad::save(AKEEBA_BACKUP_ORIGIN);
  344. $array = $kettenrad->getStatusArray();
  345. if($array['Error'] != '')
  346. {
  347. // A backup error had occurred. Why are we here?!
  348. $this->status = self::STATUS_ERROR;
  349. $this->encapsulation = self::ENCAPSULATION_RAW;
  350. return 'A backup error had occurred: '.$array['Error'];
  351. }
  352. else
  353. {
  354. $statistics = AEFactory::getStatistics();
  355. $array['BackupID'] = $statistics->getId();
  356. $array['HasRun'] = 1; // Force the backup to go on.
  357. return $array;
  358. }
  359. }
  360. private function _apiStepBackup($config)
  361. {
  362. $defConfig = array(
  363. 'profile' => null
  364. );
  365. $config = array_merge($defConfig, $config);
  366. extract($config);
  367. // Try to set the profile from the setup parameters
  368. if(!empty($profile))
  369. {
  370. $registry = AEFactory::getConfiguration();
  371. $session = JFactory::getSession();
  372. $session->set('profile', $profile, 'akeeba');
  373. }
  374. $kettenrad = AECoreKettenrad::load(AKEEBA_BACKUP_ORIGIN);
  375. $registry = AEFactory::getConfiguration();
  376. $session = JFactory::getSession();
  377. $session->set('profile', $registry->activeProfile, 'akeeba');
  378. $array = $kettenrad->tick();
  379. $array['Progress'] = $kettenrad->getProgress();
  380. AECoreKettenrad::save(AKEEBA_BACKUP_ORIGIN);
  381. if($array['Error'] != '')
  382. {
  383. // A backup error had occurred. Why are we here?!
  384. $this->status = self::STATUS_ERROR;
  385. $this->encapsulation = self::ENCAPSULATION_RAW;
  386. return 'A backup error had occurred: '.$array['Error'];
  387. } elseif($array['HasRun'] == false) {
  388. AEFactory::nuke();
  389. AEUtilTempvars::reset();
  390. }
  391. return $array;
  392. }
  393. private function _apiListBackups($config)
  394. {
  395. $defConfig = array(
  396. 'from' => 0,
  397. 'limit' => 50
  398. );
  399. $config = array_merge($defConfig, $config);
  400. extract($config);
  401. require_once JPATH_COMPONENT_ADMINISTRATOR.'/models/statistics.php';
  402. $model = new AkeebaModelStatistics();
  403. return $model->getStatisticsListWithMeta(true);
  404. }
  405. private function _apiGetBackupInfo($config)
  406. {
  407. $defConfig = array(
  408. 'backup_id' => '0'
  409. );
  410. $config = array_merge($defConfig, $config);
  411. extract($config);
  412. // Get the basic statistics
  413. $record = AEPlatform::getInstance()->get_statistics($backup_id);
  414. // Get a list of filenames
  415. $backup_stats = AEPlatform::getInstance()->get_statistics($backup_id);
  416. // Backup record doesn't exist
  417. if(empty($backup_stats))
  418. {
  419. $this->status = self::STATUS_NOT_FOUND;
  420. $this->encapsulation = self::ENCAPSULATION_RAW;
  421. return 'Invalid backup record identifier';
  422. }
  423. $filenames = AEUtilStatistics::get_all_filenames($record);
  424. if(empty($filenames))
  425. {
  426. // Archives are not stored on the server or no files produced
  427. $record['filenames'] = array();
  428. }
  429. else
  430. {
  431. $filedata = array();
  432. $i = 0;
  433. // Get file sizes per part
  434. foreach($filenames as $file)
  435. {
  436. $i++;
  437. $size = @filesize($file);
  438. $size = is_numeric($size) ? $size : 0;
  439. $filedata[] = array(
  440. 'part' => $i,
  441. 'name' => basename($file),
  442. 'size' => $size
  443. );
  444. }
  445. // Add the file info to $record['filenames']
  446. $record['filenames'] = $filedata;
  447. }
  448. return $record;
  449. }
  450. private function _apiDownload($config)
  451. {
  452. $defConfig = array(
  453. 'backup_id' => 0,
  454. 'part_id' => 1,
  455. 'segment' => 1,
  456. 'chunk_size' => 1
  457. );
  458. $config = array_merge($defConfig, $config);
  459. extract($config);
  460. $backup_stats = AEPlatform::getInstance()->get_statistics($backup_id);
  461. if(empty($backup_stats))
  462. {
  463. // Backup record doesn't exist
  464. $this->status = self::STATUS_NOT_FOUND;
  465. $this->encapsulation = self::ENCAPSULATION_RAW;
  466. return 'Invalid backup record identifier';
  467. }
  468. $files = AEUtilStatistics::get_all_filenames($backup_stats);
  469. if( (count($files) < $part_id) || ($part_id <= 0) )
  470. {
  471. // Invalid part
  472. $this->status = self::STATUS_NOT_FOUND;
  473. $this->encapsulation = self::ENCAPSULATION_RAW;
  474. return 'Invalid backup part';
  475. }
  476. $file = $files[$part_id-1];
  477. $filesize = @filesize($file);
  478. $seekPos = $chunk_size * 1048756 * ($segment - 1);
  479. if($seekPos > $filesize) {
  480. // Trying to seek past end of file
  481. $this->status = self::STATUS_NOT_FOUND;
  482. $this->encapsulation = self::ENCAPSULATION_RAW;
  483. return 'Invalid segment';
  484. }
  485. $fp = fopen($file, 'rb');
  486. if($fp === false)
  487. {
  488. // Could not read file
  489. $this->status = self::STATUS_ERROR;
  490. $this->encapsulation = self::ENCAPSULATION_RAW;
  491. return 'Error reading backup archive';
  492. }
  493. rewind($fp);
  494. if(fseek($fp, $seekPos, SEEK_SET) === -1)
  495. {
  496. // Could not seek to position
  497. $this->status = self::STATUS_ERROR;
  498. $this->encapsulation = self::ENCAPSULATION_RAW;
  499. return 'Error reading specified segment';
  500. }
  501. $buffer = fread($fp, 1048756);
  502. if($buffer === false)
  503. {
  504. // Could not read
  505. $this->status = self::STATUS_ERROR;
  506. $this->encapsulation = self::ENCAPSULATION_RAW;
  507. return 'Error reading specified segment';
  508. }
  509. fclose($fp);
  510. switch($this->encapsulation)
  511. {
  512. case self::ENCAPSULATION_RAW:
  513. return base64_encode($buffer);
  514. break;
  515. case self::ENCAPSULATION_AESCTR128:
  516. $this->encapsulation = self::ENCAPSULATION_AESCBC128;
  517. return $buffer;
  518. break;
  519. case self::ENCAPSULATION_AESCTR256:
  520. $this->encapsulation = self::ENCAPSULATION_AESCBC256;
  521. return $buffer;
  522. break;
  523. default:
  524. // On encrypted comms the encryption will take care of transport encoding
  525. return $buffer;
  526. break;
  527. }
  528. }
  529. private function _apiDelete($config)
  530. {
  531. $defConfig = array(
  532. 'backup_id' => 0
  533. );
  534. $config = array_merge($defConfig, $config);
  535. extract($config);
  536. require_once JPATH_COMPONENT_ADMINISTRATOR.'/models/statistics.php';
  537. $model = new AkeebaModelStatistics();
  538. $result = $model->delete((int)$backup_id);
  539. if(!$result)
  540. {
  541. $this->status = self::STATUS_ERROR;
  542. $this->encapsulation = self::ENCAPSULATION_RAW;
  543. return $model->getError();
  544. }
  545. else
  546. {
  547. return true;
  548. }
  549. }
  550. private function _apiDeleteFiles($config)
  551. {
  552. $defConfig = array(
  553. 'backup_id' => 0
  554. );
  555. $config = array_merge($defConfig, $config);
  556. extract($config);
  557. require_once JPATH_COMPONENT_ADMINISTRATOR.'/models/statistics.php';
  558. $model = new AkeebaModelStatistics();
  559. $result = $model->deleteFile((int)$backup_id);
  560. if(!$result)
  561. {
  562. $this->status = self::STATUS_ERROR;
  563. $this->encapsulation = self::ENCAPSULATION_RAW;
  564. return $model->getError();
  565. }
  566. else
  567. {
  568. return true;
  569. }
  570. }
  571. private function _apiGetLog($config)
  572. {
  573. $defConfig = array(
  574. 'tag' => 'remote'
  575. );
  576. $config = array_merge($defConfig, $config);
  577. extract($config);
  578. $filename = AEUtilLogger::logName($tag);
  579. $buffer = file_get_contents($filename);
  580. switch($this->encapsulation)
  581. {
  582. case self::ENCAPSULATION_RAW:
  583. return base64_encode($buffer);
  584. break;
  585. case self::ENCAPSULATION_AESCTR128:
  586. $this->encapsulation = self::ENCAPSULATION_AESCBC128;
  587. return $buffer;
  588. break;
  589. case self::ENCAPSULATION_AESCTR256:
  590. $this->encapsulation = self::ENCAPSULATION_AESCBC256;
  591. return $buffer;
  592. break;
  593. default:
  594. // On encrypted comms the encryption will take care of transport encoding
  595. return $buffer;
  596. break;
  597. }
  598. }
  599. private function _apiDownloadDirect($config)
  600. {
  601. $defConfig = array(
  602. 'backup_id' => 0,
  603. 'part_id' => 1
  604. );
  605. $config = array_merge($defConfig, $config);
  606. extract($config);
  607. $backup_stats = AEPlatform::getInstance()->get_statistics($backup_id);
  608. if(empty($backup_stats))
  609. {
  610. // Backup record doesn't exist
  611. $this->status = self::STATUS_NOT_FOUND;
  612. $this->encapsulation = self::ENCAPSULATION_RAW;
  613. @ob_end_clean();
  614. header('HTTP/1.1 500 Invalid backup record identifier');
  615. flush();
  616. JFactory::getApplication()->close();
  617. }
  618. $files = AEUtilStatistics::get_all_filenames($backup_stats);
  619. if( (count($files) < $part_id) || ($part_id <= 0) )
  620. {
  621. // Invalid part
  622. $this->status = self::STATUS_NOT_FOUND;
  623. $this->encapsulation = self::ENCAPSULATION_RAW;
  624. @ob_end_clean();
  625. header('HTTP/1.1 500 Invalid backup part');
  626. flush();
  627. JFactory::getApplication()->close();
  628. }
  629. $filename = $files[$part_id-1];
  630. @clearstatcache();
  631. // For a certain unmentionable browser -- Thank you, Nooku, for the tip
  632. if(function_exists('ini_get') && function_exists('ini_set')) {
  633. if(ini_get('zlib.output_compression')) {
  634. ini_set('zlib.output_compression', 'Off');
  635. }
  636. }
  637. // Remove php's time limit -- Thank you, Nooku, for the tip
  638. if(function_exists('ini_get') && function_exists('set_time_limit')) {
  639. if(!ini_get('safe_mode') ) {
  640. @set_time_limit(0);
  641. }
  642. }
  643. $basename = @basename($filename);
  644. $filesize = @filesize($filename);
  645. $extension = strtolower(str_replace(".", "", strrchr($filename, ".")));
  646. while (@ob_end_clean());
  647. @clearstatcache();
  648. // Send MIME headers
  649. header('MIME-Version: 1.0');
  650. header('Content-Disposition: attachment; filename='.$basename);
  651. header('Content-Transfer-Encoding: binary');
  652. header('Accept-Ranges: bytes');
  653. switch($extension)
  654. {
  655. case 'zip':
  656. // ZIP MIME type
  657. header('Content-Type: application/zip');
  658. break;
  659. default:
  660. // Generic binary data MIME type
  661. header('Content-Type: application/octet-stream');
  662. break;
  663. }
  664. // Notify of filesize, if this info is available
  665. if($filesize > 0) header('Content-Length: '.@filesize($filename));
  666. // Disable caching
  667. header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
  668. header("Expires: 0");
  669. header('Pragma: no-cache');
  670. flush();
  671. if($filesize > 0)
  672. {
  673. // If the filesize is reported, use 1M chunks for echoing the data to the browser
  674. $blocksize = 1048756; //1M chunks
  675. $handle = @fopen($filename, "r");
  676. // Now we need to loop through the file and echo out chunks of file data
  677. if($handle !== false) while(!@feof($handle)){
  678. echo @fread($handle, $blocksize);
  679. @ob_flush();
  680. flush();
  681. }
  682. if($handle !== false) @fclose($handle);
  683. } else {
  684. // If the filesize is not reported, hope that readfile works
  685. @readfile($filename);
  686. }
  687. flush();
  688. JFactory::getApplication()->close();
  689. }
  690. private function _apiUpdateGetInformation($config)
  691. {
  692. $defConfig = array(
  693. 'force' => 0
  694. );
  695. $config = array_merge($defConfig, $config);
  696. extract($config);
  697. require_once JPATH_ROOT.'/administrator/components/com_akeeba/liveupdate/liveupdate.php';
  698. $updateInformation = LiveUpdate::getUpdateInformation($force);
  699. return (object)$updateInformation;
  700. }
  701. private function _apiUpdateDownload($config)
  702. {
  703. require_once JPATH_ROOT.'/administrator/components/com_akeeba/liveupdate/liveupdate.php';
  704. require_once JPATH_ROOT.'/administrator/components/com_akeeba/liveupdate/classes/model.php';
  705. // Do we need to update?
  706. $updateInformation = LiveUpdate::getUpdateInformation();
  707. if(!$updateInformation->hasUpdates) {
  708. return (object)array(
  709. 'download' => 0
  710. );
  711. }
  712. $model = new LiveupdateModel();
  713. $ret = $model->download();
  714. $session = JFactory::getSession();
  715. $target = $session->get('target', '', 'liveupdate');
  716. $tempdir = $session->get('tempdir', '', 'liveupdate');
  717. // Save the target and tempdir
  718. $session = JFactory::getSession();
  719. $session->set('profile', 1, 'akeeba');
  720. AEPlatform::getInstance()->load_configuration(1);
  721. $config = AEFactory::getConfiguration();
  722. $config->set('remoteupdate.target', $target);
  723. $config->set('remoteupdate.tempdir', $tempdir);
  724. AEPlatform::getInstance()->save_configuration(1);
  725. if(!$ret) {
  726. // An error ocurred :(
  727. $this->status = self::STATUS_ERROR;
  728. $this->encapsulation = self::ENCAPSULATION_RAW;
  729. return "Could not download the update package";
  730. } else {
  731. return (object)array(
  732. 'download' => 1
  733. );
  734. }
  735. }
  736. private function _apiUpdateExtract($config)
  737. {
  738. $session = JFactory::getSession();
  739. $session->set('profile', 1, 'akeeba');
  740. AEPlatform::getInstance()->load_configuration(1);
  741. $config = AEFactory::getConfiguration();
  742. $target = $config->get('remoteupdate.target', '');
  743. $tempdir = $config->get('remoteupdate.tempdir', '');
  744. $session = JFactory::getSession();
  745. $session->set('target', $target, 'liveupdate');
  746. $session->set('tempdir', $tempdir, 'liveupdate');
  747. require_once JPATH_ROOT.'/administrator/components/com_akeeba/liveupdate/liveupdate.php';
  748. require_once JPATH_ROOT.'/administrator/components/com_akeeba/liveupdate/classes/model.php';
  749. $model = new LiveupdateModel();
  750. $ret = $model->extract();
  751. jimport('joomla.filesystem.file');
  752. JFile::delete($target);
  753. if(!$ret) {
  754. // An error ocurred :(
  755. $this->status = self::STATUS_ERROR;
  756. $this->encapsulation = self::ENCAPSULATION_RAW;
  757. return "Could not extract the update package";
  758. } else {
  759. return (object)array(
  760. 'extract' => 1
  761. );
  762. }
  763. }
  764. private function _apiUpdateInstall($config) {
  765. $session = JFactory::getSession();
  766. $session->set('profile', 1, 'akeeba');
  767. AEPlatform::getInstance()->load_configuration(1);
  768. $config = AEFactory::getConfiguration();
  769. $target = $config->get('remoteupdate.target', '');
  770. $tempdir = $config->get('remoteupdate.tempdir', '');
  771. $session = JFactory::getSession();
  772. $session->set('tempdir', $tempdir, 'liveupdate');
  773. require_once JPATH_ROOT.'/administrator/components/com_akeeba/liveupdate/liveupdate.php';
  774. require_once JPATH_ROOT.'/administrator/components/com_akeeba/liveupdate/classes/model.php';
  775. $model = new LiveupdateModel();
  776. $ret = $model->install();
  777. if(!$ret) {
  778. // An error ocurred :(
  779. $this->status = self::STATUS_ERROR;
  780. $this->encapsulation = self::ENCAPSULATION_RAW;
  781. return "Could not install the update package";
  782. } else {
  783. return (object)array(
  784. 'install' => 1
  785. );
  786. }
  787. }
  788. private function _apiUpdateCleanup($config) {
  789. $session = JFactory::getSession();
  790. $session->set('profile', 1, 'akeeba');
  791. AEPlatform::getInstance()->load_configuration(1);
  792. $config = AEFactory::getConfiguration();
  793. $target = $config->get('remoteupdate.target', '');
  794. $tempdir = $config->get('remoteupdate.tempdir', '');
  795. $session = JFactory::getSession();
  796. $session->set('target', $target, 'liveupdate');
  797. $session->set('tempdir', $tempdir, 'liveupdate');
  798. require_once JPATH_ROOT.'/administrator/components/com_akeeba/liveupdate/liveupdate.php';
  799. require_once JPATH_ROOT.'/administrator/components/com_akeeba/liveupdate/classes/model.php';
  800. $model = new LiveupdateModel();
  801. $ret = $model->cleanup();
  802. jimport('joomla.filesystem.file');
  803. JFile::delete($target);
  804. $config->set('remoteupdate.target', null);
  805. $config->set('remoteupdate.tempdir', null);
  806. AEPlatform::getInstance()->save_configuration(1);
  807. return (object)array(
  808. 'cleanup' => 1
  809. );
  810. }
  811. /**
  812. * Encodes a variable to JSON using PEAR's Services_JSON
  813. * @param mixed $value The value to encode
  814. * @param int $options Encoding preferences flags
  815. * @return string The JSON-encoded string
  816. */
  817. private function json_encode($value, $options = 0) {
  818. $flags = SERVICES_JSON_LOOSE_TYPE;
  819. if( $options & JSON_FORCE_OBJECT ) $flags = 0;
  820. $encoder = new Akeeba_Services_JSON($flags);
  821. return $encoder->encode($value);
  822. }
  823. /**
  824. * Decodes a JSON string to a variable using PEAR's Services_JSON
  825. * @param string $value The JSON-encoded string
  826. * @param bool $assoc True to return an associative array instead of an object
  827. * @return mixed The decoded variable
  828. */
  829. private function json_decode($value, $assoc = false)
  830. {
  831. $flags = 0;
  832. if($assoc) $flags = SERVICES_JSON_LOOSE_TYPE;
  833. $decoder = new Akeeba_Services_JSON($flags);
  834. return $decoder->decode($value);
  835. }
  836. }