3) { $dirParts = array_splice($dirParts, 0, -3); $myDir = implode(DIRECTORY_SEPARATOR, $dirParts); } if (@file_exists(__DIR__ . '/../../helpers/integration.php')) { require_once __DIR__ . '/../../helpers/integration.php'; } elseif (@file_exists('../../helpers/integration.php')) { require_once '../../helpers/integration.php'; } elseif (@file_exists($myDir . '/helpers/integration.php')) { require_once $myDir . '/helpers/integration.php'; } // Load the platform defines if (!defined('APATH_BASE')) { require_once __DIR__ . '/../defines.php'; } // Load the version file if (@file_exists(__DIR__ . '/../version.php')) { require_once __DIR__ . '/../version.php'; } // Add our app to the autoloader, if it's not already set $prefixes = Autoloader::getInstance()->getPrefixes(); if (!array_key_exists('Solo\\', $prefixes)) { Autoloader::getInstance()->addMap('Solo\\', APATH_BASE . '/Solo'); } // Include the Akeeba Engine factory if (!defined('AKEEBAENGINE')) { define('AKEEBAENGINE', 1); $factoryPath = __DIR__ . '/../Solo/engine/Factory.php'; // Load the engine if (!file_exists($factoryPath)) { echo "ERROR!\n"; echo "Could not load the backup engine; file does not exist. Technical information:\n"; echo "Path to " . basename(__FILE__) . ": " . __DIR__ . "\n"; echo "Path to factory file: $factoryPath\n"; die("\n"); } else { try { require_once $factoryPath; } catch (\Exception $e) { echo "ERROR!\n"; echo "Backup engine returned an error. Technical information:\n"; echo "Error message:\n\n"; echo $e->getMessage() . "\n\n"; echo "Path to " . basename(__FILE__) . ":" . __DIR__ . "\n"; echo "Path to factory file: $factoryPath\n"; die("\n"); } } Platform::addPlatform('Solo', __DIR__ . '/../Solo/Platform/Solo'); Platform::getInstance()->load_version_defines(); Platform::getInstance()->apply_quirk_definitions(); } class BackupApplication extends \Awf\Application\Cli { const secretKeyRelativePath = '/engine/secretkey.php'; public function __construct(\Awf\Container\Container $container = null) { parent::__construct($container); if (empty($this->container->basePath)) { $this->container->basePath = APATH_BASE . '/Solo'; } } public function initialise() { // Load the extra language files Text::loadLanguage(null, 'akeeba', '.com_akeeba.ini', true, $this->container->languagePath); // Halt if the configuration does not exist yet $configPath = $this->getContainer()->appConfig->getDefaultPath(); if (!@file_exists($configPath)) { $this->out('Configuration not found; aborting'); $this->close(254); } // Load the configuration if it's present if (@file_exists($configPath)) { // Load the application's configuration $this->container->appConfig->loadConfiguration($configPath); // Load Akeeba Engine's settings encryption preferences $secretKeyFile = $this->getContainer()->basePath . static::secretKeyRelativePath; if (@file_exists($secretKeyFile)) { require_once $secretKeyFile; } // Load Akeeba Engine's configuration Platform::getInstance()->load_configuration(); } return $this; } /** * Language file processing callback. It converts _QQ_ to " and replaces the product name in the legacy INI files * imported from Akeeba Backup for Joomla!. * * @param string $filename The full path to the file being loaded * @param array $strings The key/value array of the translations * * @return boolean|array False to prevent loading the file, or array of processed language string, or true to * ignore this processing callback. */ public function processLanguageIniFile($filename, $strings) { foreach ($strings as $k => $v) { $v = str_replace('_QQ_', '"', $v); $v = str_replace('Akeeba Backup', 'Akeeba Solo', $v); $strings[$k] = $v; } return $strings; } /** * Method to run the application routines. Most likely you will want to instantiate a controller * and execute it, or perform some sort of task directly. * * @return void */ protected function doExecute() { // Get the backup profile and description $profile = $this->getContainer()->input->get('profile', 1, 'int'); if($profile <= 0) { $profile = 1; } $description = $this->getContainer()->input->get('description', 'Command-line backup', 'string'); $overrides = $this->getOption('override', array(), false); if (!empty($overrides)) { $override_message = "\nConfiguration variables overriden in the command line:\n"; $override_message .= implode(', ', array_keys($overrides)); $override_message .= "\n"; } else { $override_message = ""; } $debugmessage = ''; if ($this->getContainer()->input->get('debug', -1, 'int') != -1) { if (!defined('AKEEBADEBUG')) { define('AKEEBADEBUG', 1); } $debugmessage = "*** DEBUG MODE ENABLED ***\n"; } $version = AKEEBA_VERSION; $date = AKEEBA_DATE; $start_backup = time(); $memusage = $this->memUsage(); $phpversion = PHP_VERSION; $phpenvironment = PHP_SAPI; $phpos = PHP_OS; if ($this->getContainer()->input->get('quiet', -1, 'int') == -1) { $year = gmdate('Y'); echo <<getContainer()->input->get('quiet', -1, 'int') == -1) { echo "Unsetting time limit restrictions.\n"; } @set_time_limit(0); } elseif (!$safe_mode) { if ($this->getContainer()->input->get('quiet', -1, 'int') == -1) { echo "Could not unset time limit restrictions; you may get a timeout error\n"; } } else { if ($this->getContainer()->input->get('quiet', -1, 'int') == -1) { echo "You are using PHP's Safe Mode; you may get a timeout error\n"; } } if ($this->getContainer()->input->get('quiet', -1, 'int') == -1) { echo "\n"; } // Log some paths if ($this->getContainer()->input->get('quiet', -1, 'int') == -1) { echo "Site paths determined by this script:\n"; echo "APATH_BASE : " . APATH_BASE . "\n"; } // Forced CLI mode settings define('AKEEBA_PROFILE', $profile); define('AKEEBA_BACKUP_ORIGIN', 'cli'); // Load the profile Platform::getInstance()->load_configuration($profile); // Reset Kettenrad and its storage Factory::resetState(array( 'maxrun' => 0 )); Factory::getFactoryStorage()->reset(AKEEBA_BACKUP_ORIGIN); // Setup $kettenrad = Factory::getKettenrad(); $options = array( 'description' => $description, 'comment' => '' ); if (!empty($overrides)) { Platform::getInstance()->configOverrides = $overrides; } $kettenrad->setup($options); // Dummy array so that the loop iterates once $array = array( 'HasRun' => 0, 'Error' => '' ); $warnings_flag = false; while (($array['HasRun'] != 1) && (empty($array['Error']))) { // Recycle the database conenction to minimise problems with database timeouts $db = Factory::getDatabase(); $db->close(); $db->open(); Factory::getLog()->open(AKEEBA_BACKUP_ORIGIN); Factory::getLog()->unpause(); // Apply overrides in the command line if (!empty($overrides)) { $config = Factory::getConfiguration(); foreach ($overrides as $key => $value) { $config->set($key, $value); } } // Apply engine optimization overrides $config = Factory::getConfiguration(); $config->set('akeeba.tuning.min_exec_time', 0); $config->set('akeeba.tuning.nobreak.beforelargefile', 1); $config->set('akeeba.tuning.nobreak.afterlargefile', 1); $config->set('akeeba.tuning.nobreak.proactive', 1); $config->set('akeeba.tuning.nobreak.finalization', 1); $config->set('akeeba.tuning.settimelimit', 0); $config->set('akeeba.tuning.nobreak.domains', 0); $kettenrad->tick(); Factory::getTimer()->resetTime(); $array = $kettenrad->getStatusArray(); Factory::getLog()->close(); $time = date('Y-m-d H:i:s \G\M\TO (T)'); $memusage = $this->memUsage(); $warnings = "no warnings issued (good)"; $stepWarnings = false; if (!empty($array['Warnings'])) { $warnings_flag = true; $warnings = "POTENTIAL PROBLEMS DETECTED; " . count($array['Warnings']) . " warnings issued (see below).\n"; foreach ($array['Warnings'] as $line) { $warnings .= "\t$line\n"; } $stepWarnings = true; $kettenrad->resetWarnings(); } if (($this->getContainer()->input->get('quiet', -1, 'int') == -1) || $stepWarnings) echo <<reset(AKEEBA_BACKUP_ORIGIN); if (!empty($array['Error'])) { echo "An error has occurred:\n{$array['Error']}\n\n"; $exitCode = 2; } else { if ($this->getContainer()->input->get('quiet', -1, 'int') == -1) { echo "Backup job finished successfully after approximately " . $this->timeago($start_backup, time(), '', false) . "\n"; } $exitCode = 0; } if ($warnings_flag && ($this->getContainer()->input->get('quiet', -1, 'int') == -1)) { $exitCode = 1; echo "\n" . str_repeat('=', 79) . "\n"; echo "!!!!! W A R N I N G !!!!!\n\n"; echo "Akeeba Solo issued warnings during the backup process. You have to review them\n"; echo "and make sure that your backup has completed successfully. Always test a backup with\n"; echo "warnings to make sure that it is working properly, by restoring it to a local server.\n"; echo "DO NOT IGNORE THIS MESSAGE! AN UNTESTED BACKUP IS AS GOOD AS NO BACKUP AT ALL.\n"; echo "\n" . str_repeat('=', 79) . "\n"; } elseif ($warnings_flag) { $exitCode = 1; } if ($this->getContainer()->input->get('quiet', -1, 'int') == -1) { echo "Peak memory usage: " . $this->peakMemUsage() . "\n\n"; } $this->close($exitCode); } /** * Returns a fancy formatted time lapse code * * @param integer $referenceDateTime Timestamp of the reference date/time * @param string|integer $currentDateTime Timestamp of the current date/time * @param string $measureBy One of s, m, h, d, or y (time unit) * @param boolean $autoText Append text automatically? * * @return string */ private function timeago($referenceDateTime = 0, $currentDateTime = '', $measureBy = '', $autoText = true) { if ($currentDateTime == '') { $currentDateTime = time(); } // Raw time difference $Raw = $currentDateTime - $referenceDateTime; $Clean = abs($Raw); $calcNum = array( array('s', 60), array('m', 60 * 60), array('h', 60 * 60 * 60), array('d', 60 * 60 * 60 * 24), array('y', 60 * 60 * 60 * 24 * 365) ); $calc = array( 's' => array(1, 'second'), 'm' => array(60, 'minute'), 'h' => array(60 * 60, 'hour'), 'd' => array(60 * 60 * 24, 'day'), 'y' => array(60 * 60 * 24 * 365, 'year') ); if ($measureBy == '') { $usemeasure = 's'; for ($i = 0; $i < count($calcNum); $i++) { if ($Clean <= $calcNum[$i][1]) { $usemeasure = $calcNum[$i][0]; $i = count($calcNum); } } } else { $usemeasure = $measureBy; } $datedifference = floor($Clean / $calc[$usemeasure][0]); if ($autoText == true && ($currentDateTime == time())) { if ($Raw < 0) { $prospect = ' from now'; } else { $prospect = ' ago'; } } else { $prospect = ''; } if ($referenceDateTime != 0) { if ($datedifference == 1) { return $datedifference . ' ' . $calc[$usemeasure][1] . ' ' . $prospect; } else { return $datedifference . ' ' . $calc[$usemeasure][1] . 's ' . $prospect; } } else { return 'No input time referenced.'; } } /** * Returns the current memory usage * * @return string */ private function memUsage() { if (function_exists('memory_get_usage')) { $size = memory_get_usage(); $unit = array('b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb'); return @round($size / pow(1024, ($i = floor(log($size, 1024)))), 2) . ' ' . $unit[$i]; } else { return "(unknown)"; } } /** * Returns the peak memory usage * * @return string */ private function peakMemUsage() { if (function_exists('memory_get_peak_usage')) { $size = memory_get_peak_usage(); $unit = array('b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb'); return @round($size / pow(1024, ($i = floor(log($size, 1024)))), 2) . ' ' . $unit[$i]; } else { return "(unknown)"; } } /** * Parses POSIX command line options and returns them as an associative array. Each array item contains * a single dimensional array of values. Arguments without a dash are silently ignored. * * @return array */ private function parseOptions() { global $argc, $argv; // Workaround for PHP-CGI if (!isset($argc) && !isset($argv)) { $query = ""; if (!empty($_GET)) { foreach ($_GET as $k => $v) { $query .= " $k"; if ($v != "") { $query .= "=$v"; } } } $query = ltrim($query); $argv = explode(' ', $query); $argc = count($argv); } $currentName = ""; $options = array(); for ($i = 1; $i < $argc; $i++) { $argument = $argv[$i]; if (strpos($argument, "-") === 0) { $argument = ltrim($argument, '-'); if (strstr($argument, '=')) { list($name, $value) = explode('=', $argument, 2); } else { $name = $argument; $value = null; } $currentName = $name; if (!isset($options[$currentName]) || ($options[$currentName] == null)) { $options[$currentName] = array(); } } else { $value = $argument; } if ((!is_null($value)) && (!is_null($currentName))) { if (strstr($value, '=')) { $parts = explode('=', $value, 2); $key = $parts[0]; $value = $parts[1]; } else { $key = null; } $values = $options[$currentName]; if (is_null($values)) { $values = array(); } if (is_null($key)) { array_push($values, $value); } else { $values[$key] = $value; } $options[$currentName] = $values; } } return $options; } /** * Returns the value of a command line option * * @param string $key The full name of the option, e.g. "foobar" * @param mixed $default The default value to return * @param boolean $first_item_only Return only the first value specified (default = true) * * @return mixed */ private function getOption($key, $default = null, $first_item_only = true) { static $options = null; if (is_null($options)) { $options = $this->parseOptions(); } if (!array_key_exists($key, $options)) { return $default; } else { if ($first_item_only) { return $options[$key][0]; } else { return $options[$key]; } } } } if (!isset($container)) { $container = new \Solo\Container(); } $app = new BackupApplication($container); $app->initialise()->execute();