PageRenderTime 62ms CodeModel.GetById 36ms RepoModel.GetById 1ms app.codeStats 0ms

/cli/akeeba-altbackup.php

https://bitbucket.org/kraymitchell/saiu
PHP | 543 lines | 444 code | 40 blank | 59 comment | 43 complexity | 493ec436831c83e165efb00c7bd815c9 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)2010-2011 Nicholas K. Dionysopoulos
  5. * @license GNU General Public License version 3, or later
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. * --
  21. *
  22. * Command-line script to schedule the File Alteration Monitor check
  23. */
  24. // Timezone fix; avoids errors printed out by PHP 5.3.3+ (thanks Yannick!)
  25. if(function_exists('date_default_timezone_get') && function_exists('date_default_timezone_set')) {
  26. if(function_exists('error_reporting')) {
  27. $oldLevel = error_reporting(0);
  28. }
  29. $serverTimezone = @date_default_timezone_get();
  30. if(empty($serverTimezone) || !is_string($serverTimezone)) $serverTimezone = 'UTC';
  31. if(function_exists('error_reporting')) {
  32. error_reporting($oldLevel);
  33. }
  34. @date_default_timezone_set( $serverTimezone);
  35. }
  36. // Define ourselves as a parent file
  37. define( '_JEXEC', 1 );
  38. define('AKEEBAENGINE', 1); // Enable Akeeba Engine
  39. // Required by the CMS
  40. define('DS', DIRECTORY_SEPARATOR);
  41. // Load system defines
  42. if (file_exists(dirname(__FILE__).'/defines.php')) {
  43. include_once dirname(__FILE__).'/defines.php';
  44. }
  45. if (!defined('_JDEFINES')) {
  46. define('JPATH_BASE', dirname(__FILE__).'/../');
  47. require_once JPATH_BASE.'/includes/defines.php';
  48. }
  49. // Load the rest of the framework include files
  50. include_once JPATH_LIBRARIES.'/import.php';
  51. require_once JPATH_LIBRARIES.'/cms.php';
  52. // Load the JApplicationCli class
  53. jimport( 'joomla.application.cli' );
  54. /**
  55. * Akeeba Backup CLI application
  56. */
  57. class AkeebaBackupCLI extends JApplicationCli
  58. {
  59. /**
  60. * Joomla! Platform doesn't want to run on PHP CGI. The hell with it! I'm
  61. * sick and tired of people bitching about this, so let me fix it! Muwahaha!
  62. *
  63. * @param JInputCli $input
  64. * @param JRegistry $config
  65. * @param JDispatcher $dispatcher
  66. */
  67. public function __construct(JInputCli $input = null, JRegistry $config = null, JDispatcher $dispatcher = null)
  68. {
  69. // Close the application if we are not executed from the command line, Akeeba style (allow for PHP CGI)
  70. if( array_key_exists('REQUEST_METHOD', $_SERVER) ) {
  71. die('You are not supposed to access this script from the web. You have to run it from the command line. If you don\'t understand what this means, you must not try to use this file before reading the documentation. Thank you.');
  72. }
  73. // If a input object is given use it.
  74. if ($input instanceof JInput)
  75. {
  76. $this->input = $input;
  77. }
  78. // Create the input based on the application logic.
  79. else
  80. {
  81. if (class_exists('Jinput'))
  82. {
  83. $this->input = new JInputCLI;
  84. }
  85. }
  86. // If a config object is given use it.
  87. if ($config instanceof JRegistry)
  88. {
  89. $this->config = $config;
  90. }
  91. // Instantiate a new configuration object.
  92. else
  93. {
  94. $this->config = new JRegistry;
  95. }
  96. // If a dispatcher object is given use it.
  97. if ($dispatcher instanceof JDispatcher)
  98. {
  99. $this->dispatcher = $dispatcher;
  100. }
  101. // Create the dispatcher based on the application logic.
  102. else
  103. {
  104. $this->loadDispatcher();
  105. }
  106. // Load the configuration object.
  107. $this->loadConfiguration($this->fetchConfigurationData());
  108. // Set the execution datetime and timestamp;
  109. $this->set('execution.datetime', gmdate('Y-m-d H:i:s'));
  110. $this->set('execution.timestamp', time());
  111. // Set the current directory.
  112. $this->set('cwd', getcwd());
  113. }
  114. public function execute()
  115. {
  116. // Get the backup profile and description
  117. $profile = $this->input->get('profile', 1, 'int');
  118. $version = AKEEBA_VERSION;
  119. $date = AKEEBA_DATE;
  120. $start_backup = time();
  121. $memusage = $this->memUsage();
  122. if($this->input->get('quiet',-1,'int') == -1) {
  123. echo <<<ENDBLOCK
  124. Akeeba Backup Alternate CRON Helper Script version $version ($date)
  125. Copyright (C) 2010-2011 Nicholas K. Dionysopoulos
  126. -------------------------------------------------------------------------------
  127. Akeeba Backup is Free Software, distributed under the terms of the GNU General
  128. Public License version 3 or, at your option, any later version.
  129. This program comes with ABSOLUTELY NO WARRANTY as per sections 15 & 16 of the
  130. license. See http://www.gnu.org/licenses/gpl-3.0.html for details.
  131. -------------------------------------------------------------------------------
  132. ENDBLOCK;
  133. }
  134. // Attempt to use an infinite time limit, in case you are using the PHP CGI binary instead
  135. // of the PHP CLI binary. This will not work with Safe Mode, though.
  136. $safe_mode = true;
  137. if(function_exists('ini_get')) {
  138. $safe_mode = ini_get('safe_mode');
  139. }
  140. if(!$safe_mode && function_exists('set_time_limit')) {
  141. if($this->input->get('quiet',-1,'int') == -1) {
  142. echo "Unsetting time limit restrictions.\n";
  143. }
  144. @set_time_limit(0);
  145. } elseif (!$safe_mode) {
  146. if($this->input->get('quiet',-1,'int') == -1) {
  147. echo "Could not unset time limit restrictions; you may get a timeout error\n";
  148. }
  149. } else {
  150. if($this->input->get('quiet',-1,'int') == -1) {
  151. echo "You are using PHP's Safe Mode; you may get a timeout error\n";
  152. }
  153. }
  154. if($this->input->get('quiet',-1,'int') == -1) {
  155. echo "\n";
  156. }
  157. // Log some paths
  158. if($this->input->get('quiet',-1,'int') == -1) {
  159. echo "Site paths determined by this script:\n";
  160. echo "JPATH_BASE : ".JPATH_BASE."\n";
  161. echo "JPATH_ADMINISTRATOR : ".JPATH_ADMINISTRATOR."\n\n";
  162. }
  163. // Load the engine
  164. $factoryPath = JPATH_ADMINISTRATOR.'/components/com_akeeba/akeeba/factory.php';
  165. define('JPATH_COMPONENT_ADMINISTRATOR',JPATH_ADMINISTRATOR.'/components/com_akeeba');
  166. if(!file_exists($factoryPath)) {
  167. echo "ERROR!\n";
  168. echo "Could not load the backup engine; file does not exist. Technical information:\n";
  169. echo "Path to ".basename(__FILE__).": ".dirname(__FILE__)."\n";
  170. echo "Path to factory file: $factoryPath\n";
  171. die("\n");
  172. } else {
  173. try {
  174. require_once $factoryPath;
  175. } catch(Exception $e) {
  176. echo "ERROR!\n";
  177. echo "Backup engine returned an error. Technical information:\n";
  178. echo "Error message:\n\n";
  179. echo $e->getMessage()."\n\n";
  180. echo "Path to ".basename(__FILE__).":".dirname(__FILE__)."\n";
  181. echo "Path to factory file: $factoryPath\n";
  182. die("\n");
  183. }
  184. }
  185. $startup_check = true;
  186. // Get the live site's URL
  187. $url = AEPlatform::getInstance()->get_platform_configuration_option('siteurl','');
  188. if( empty($url) )
  189. {
  190. echo <<<ENDTEXT
  191. ERROR:
  192. This script could not detect your live site's URL. Please visit Akeeba
  193. Backup's Control Panel page at least once before running this script, so
  194. that this information can be stored for use by this script.
  195. ENDTEXT;
  196. $startup_check = false;
  197. }
  198. // Get the front-end backup settings
  199. $frontend_enabled = AEPlatform::getInstance()->get_platform_configuration_option('frontend_enable','');
  200. $secret = AEPlatform::getInstance()->get_platform_configuration_option('frontend_secret_word','');
  201. if(!$frontend_enabled)
  202. {
  203. echo <<<ENDTEXT
  204. ERROR:
  205. Your Akeeba Backup installation's front-end backup feature is currently
  206. disabled. Please log in to your site's back-end as a Super Administra-
  207. tor, go to Akeeba Backup's Control Panel, click on the Options icon in
  208. the top right corner and enable the front-end backup feature. Do not
  209. forget to also set a Secret Word!
  210. ENDTEXT;
  211. $startup_check = false;
  212. }
  213. elseif(empty($secret))
  214. {
  215. echo <<<ENDTEXT
  216. ERROR:
  217. You have enabled the front-end backup feature, but you forgot to set a
  218. secret word. Without a valid secret word this script can not continue.
  219. Please log in to your site's back-end as a Super Administrator, go to
  220. Akeeba Backup's Control Panel, click on the Options icon in the top
  221. right corner set a Secret Word.
  222. ENDTEXT;
  223. $startup_check = false;
  224. }
  225. // Detect cURL or fopen URL
  226. $method = null;
  227. if(function_exists('curl_init'))
  228. {
  229. $method = 'curl';
  230. } elseif( function_exists('fsockopen') ) {
  231. $method = 'fsockopen';
  232. }
  233. if(empty($method))
  234. {
  235. if(function_exists('ini_get'))
  236. {
  237. if( ini_get('allow_url_fopen') )
  238. {
  239. $method = 'fopen';
  240. }
  241. }
  242. }
  243. $overridemethod = $this->input->get('method','','cmd');
  244. if( !empty($overridemethod) )
  245. {
  246. $method = $overridemethod;
  247. }
  248. if(empty($method))
  249. {
  250. echo <<<ENDTEXT
  251. ERROR:
  252. Could not find any supported method for running the front-end backup
  253. feature of Akeeba Backup. Please check with your host that at least
  254. one of the following features are supported in your PHP configuration:
  255. 1. The cURL extension
  256. 2. The fsockopen() function
  257. 3. The fopen() URL wrappers, i.e. allow_url_fopen is enabled
  258. If neither method is available you will not be able to backup your
  259. site using this CRON helper script.
  260. ENDTEXT;
  261. $startup_check = false;
  262. }
  263. if(!$startup_check)
  264. {
  265. echo "\n\nBACKUP ABORTED DUE TO CONFIGURATION ERRORS\n\n";
  266. $this->close(255);
  267. }
  268. echo <<<ENDBLOCK
  269. Starting a new backup with the following parameters:
  270. Profile ID : $profile
  271. Backup Method : $method
  272. ENDBLOCK;
  273. // Perform the backup
  274. $url = rtrim($url, '/');
  275. $secret = urlencode($secret);
  276. $url .= "/index.php?option=com_akeeba&view=backup&key={$secret}&noredirect=1&profile=$profile";
  277. $inLoop = true;
  278. $step = 0;
  279. $timestamp = date('Y-m-d H:i:s');
  280. echo "[{$timestamp}] Beginning backing up\n";
  281. while($inLoop)
  282. {
  283. $result = $this->fetchURL($url, $method);
  284. if(empty($result) || ($result === false))
  285. {
  286. $timestamp = date('Y-m-d H:i:s');
  287. echo "[{$timestamp}] No message received\n";
  288. echo <<<ENDTEXT
  289. ERROR:
  290. Your backup attempt has timed out, or a fatal PHP error has occurred.
  291. Please check the backup log and your server's error log for more
  292. information.
  293. Backup failed.
  294. ENDTEXT;
  295. $inLoop = false;
  296. }
  297. elseif( strpos('301 More work required', $result) !== false )
  298. {
  299. if($step == 0) $old_url = $url;
  300. $step++;
  301. $url = $old_url.'&task=step&step='.$step;
  302. $timestamp = date('Y-m-d H:i:s');
  303. echo "[{$timestamp}] Backup progress signal received\n";
  304. }
  305. elseif( strpos('200 OK', $result) !== false )
  306. {
  307. $timestamp = date('Y-m-d H:i:s');
  308. echo "[{$timestamp}] Backup finalization message received\n";
  309. echo <<<ENDTEXT
  310. Your backup has finished successfully.
  311. Please review your backup log file for any warning messages. If you see any
  312. such messages, please make sure that your backup is working properly by trying
  313. to restore it on a local server.
  314. ENDTEXT;
  315. $inLoop = false;
  316. }
  317. elseif( strpos('500 ERROR -- ', $result) !== false )
  318. {
  319. // Backup error
  320. $timestamp = date('Y-m-d H:i:s');
  321. echo "[{$timestamp}] Error signal received\n";
  322. echo <<<ENDTEXT
  323. ERROR:
  324. A backup error has occured. The server's reponse was:
  325. $result
  326. Backup failed.
  327. ENDTEXT;
  328. $inLoop = false;
  329. }
  330. elseif( strpos('403 ', $result) !== false )
  331. {
  332. // This should never happen: invalid authentication or front-end backup disabled
  333. $timestamp = date('Y-m-d H:i:s');
  334. echo "[{$timestamp}] Connection denied (403) message received\n";
  335. echo <<<ENDTEXT
  336. ERROR:
  337. The server denied the connection. Please make sure that the front-end
  338. backup feature is enabled and a valid secret word is in place.
  339. Server response: $result
  340. Backup failed.
  341. ENDTEXT;
  342. $inLoop = false;
  343. }
  344. }
  345. }
  346. /**
  347. * Returns the current memory usage
  348. *
  349. * @return string
  350. */
  351. private function memUsage()
  352. {
  353. if(function_exists('memory_get_usage')) {
  354. $size = memory_get_usage();
  355. $unit=array('b','Kb','Mb','Gb','Tb','Pb');
  356. return @round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i];
  357. } else {
  358. return "(unknown)";
  359. }
  360. }
  361. /**
  362. * Returns the peak memory usage
  363. *
  364. * @return string
  365. */
  366. private function peakMemUsage()
  367. {
  368. if(function_exists('memory_get_peak_usage')) {
  369. $size = memory_get_peak_usage();
  370. $unit=array('b','Kb','Mb','Gb','Tb','Pb');
  371. return @round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i];
  372. } else {
  373. return "(unknown)";
  374. }
  375. }
  376. /**
  377. * Fetches a remote URL using curl, fsockopen or fopen
  378. *
  379. * @param string $url The remote URL to fetch
  380. * @param string $method The method to use: curl, fsockopen or fopen (optional)
  381. * @return string The contents of the URL which was fetched
  382. */
  383. private function fetchURL($url, $method = 'curl')
  384. {
  385. switch($method)
  386. {
  387. case 'curl':
  388. $ch = curl_init($url);
  389. $cacertPath = JPATH_ADMINISTRATOR.'/components/com_akeeba/akeeba/assets/cacert.pem';
  390. if(file_exists($cacertPath)) {
  391. @curl_setopt($ch, CURLOPT_CAINFO, $cacertPath);
  392. }
  393. @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  394. @curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
  395. @curl_setopt($ch, CURLOPT_HEADER, false);
  396. @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  397. @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  398. @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 180);
  399. @curl_setopt($ch, CURLOPT_TIMEOUT, 180);
  400. $result = curl_exec($ch);
  401. curl_close($ch);
  402. return $result;
  403. break;
  404. case 'fsockopen':
  405. $pos = strpos($url, '://');
  406. $protocol = strtolower(substr($url, 0, $pos));
  407. $req = substr($url, $pos+3);
  408. $pos = strpos($req, '/');
  409. if($pos === false)
  410. $pos = strlen($req);
  411. $host = substr($req, 0, $pos);
  412. if(strpos($host, ':') !== false)
  413. {
  414. list($host, $port) = explode(':', $host);
  415. }
  416. else
  417. {
  418. $host = $host;
  419. $port = ($protocol == 'https') ? 443 : 80;
  420. }
  421. $uri = substr($req, $pos);
  422. if($uri == '')
  423. $uri = '/';
  424. $crlf = "\r\n";
  425. $req = 'GET ' . $uri . ' HTTP/1.0' . $crlf
  426. . 'Host: ' . $host . $crlf
  427. . $crlf;
  428. $fp = fsockopen(($protocol == 'https' ? 'ssl://' : '') . $host, $port);
  429. fwrite($fp, $req);
  430. $response = '';
  431. while(is_resource($fp) && $fp && !feof($fp))
  432. $response .= fread($fp, 1024);
  433. fclose($fp);
  434. // split header and body
  435. $pos = strpos($response, $crlf . $crlf);
  436. if($pos === false)
  437. return($response);
  438. $header = substr($response, 0, $pos);
  439. $body = substr($response, $pos + 2 * strlen($crlf));
  440. // parse headers
  441. $headers = array();
  442. $lines = explode($crlf, $header);
  443. foreach($lines as $line)
  444. if(($pos = strpos($line, ':')) !== false)
  445. $headers[strtolower(trim(substr($line, 0, $pos)))] = trim(substr($line, $pos+1));
  446. //redirection?
  447. if(isset($headers['location']))
  448. {
  449. return $this->fetchURL($headers['location'], $method);
  450. }
  451. else
  452. {
  453. return($body);
  454. }
  455. break;
  456. case 'fopen':
  457. $opts = array(
  458. 'http'=>array(
  459. 'method'=>"GET",
  460. 'header'=>"Accept-language: en\r\n"
  461. )
  462. );
  463. $context = stream_context_create($opts);
  464. $result = @file_get_contents($url, false, $context);
  465. break;
  466. }
  467. return $result;
  468. }
  469. }
  470. // Load the version file
  471. require_once JPATH_ADMINISTRATOR.'/components/com_akeeba/version.php';
  472. // Instanciate and run the application
  473. JApplicationCli::getInstance( 'AkeebaBackupCLI' )->execute( );