/** * Find where to store the backup files * @param $partNumber int The SQL part number, default is 0 (.sql) */ protected function getBackupFilePaths($partNumber = 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, 'XXX ' . __CLASS__ . " :: Getting temporary file"); $this->tempFile = AEUtilTempfiles::registerTempFile(dechex(crc32(microtime())) . '.sql'); AEUtilLogger::WriteLog(_AE_LOG_DEBUG, 'XXX ' . __CLASS__ . " :: Temporary file is {$this->tempFile}"); // Get the base name of the dump file $partNumber = intval($partNumber); $baseName = $this->dumpFile; if ($partNumber > 0) { // The file names are in the format dbname.sql, dbname.s01, dbname.s02, etc if (strtolower(substr($baseName, -4)) == '.sql') { $baseName = substr($baseName, 0, -4) . '.s' . sprintf('%02u', $partNumber); } else { $baseName = $baseName . '.s' . sprintf('%02u', $partNumber); } } switch (AEUtilScripting::getScriptingParameter('db.saveasname', 'normal')) { case 'output': // The SQL file will be stored uncompressed in the output directory $statistics =& AEFactory::getStatistics(); $statRecord = $statistics->getRecord(); $this->saveAsName = $statRecord['absolute_path']; break; case 'normal': // The SQL file will be stored in the SQL root of the archive, as // specified by the particular embedded installer's settings $this->saveAsName = $this->installerSettings->sqlroot . '/' . $baseName; break; case 'short': // The SQL file will be stored on archive's root $this->saveAsName = $baseName; break; } if ($partNumber > 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "AkeebaDomainDBBackup :: Creating new SQL dump part #{$partNumber}"); } AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "AkeebaDomainDBBackup :: SQL temp file is " . $this->tempFile); AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "AkeebaDomainDBBackup :: SQL file location in archive is " . $this->saveAsName); }
/** * Implements the _run() abstract method */ protected function _run() { if ($this->getState() == 'postrun') { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, __CLASS__ . " :: Already finished"); $this->setStep(''); $this->setSubstep(''); return; } else { $this->setState('running'); } // Load the version defines AEPlatform::getInstance()->load_version_defines(); $registry = AEFactory::getConfiguration(); // Write log file's header AEUtilLogger::WriteLog(_AE_LOG_INFO, "--------------------------------------------------------------------------------"); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Akeeba Backup " . AKEEBA_VERSION . ' (' . AKEEBA_DATE . ')'); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Got backup?"); AEUtilLogger::WriteLog(_AE_LOG_INFO, "--------------------------------------------------------------------------------"); // PHP configuration variables are tried to be logged only for debug and info log levels if ($registry->get('akeeba.basic.log_level') >= _AE_LOG_INFO) { AEUtilLogger::WriteLog(_AE_LOG_INFO, "--- System Information ---"); AEUtilLogger::WriteLog(_AE_LOG_INFO, "PHP Version :" . PHP_VERSION); AEUtilLogger::WriteLog(_AE_LOG_INFO, "PHP OS :" . PHP_OS); AEUtilLogger::WriteLog(_AE_LOG_INFO, "PHP SAPI :" . PHP_SAPI); if (function_exists('php_uname')) { AEUtilLogger::WriteLog(_AE_LOG_INFO, "OS Version :" . php_uname('s')); } $db = AEFactory::getDatabase(); AEUtilLogger::WriteLog(_AE_LOG_INFO, "DB Version :" . $db->getVersion()); if (isset($_SERVER['SERVER_SOFTWARE'])) { $server = $_SERVER['SERVER_SOFTWARE']; } else { if ($sf = getenv('SERVER_SOFTWARE')) { $server = $sf; } else { $server = 'n/a'; } } AEUtilLogger::WriteLog(_AE_LOG_INFO, "Web Server :" . $server); $platform = 'Unknown platform'; $version = '(unknown version)'; $platformData = AEPlatform::getInstance()->getPlatformVersion(); AEUtilLogger::WriteLog(_AE_LOG_INFO, $platformData['name'] . " version :" . $platformData['version']); if (isset($_SERVER['HTTP_USER_AGENT'])) { AEUtilLogger::WriteLog(_AE_LOG_INFO, "User agent :" . phpversion() <= "4.2.1" ? getenv("HTTP_USER_AGENT") : $_SERVER['HTTP_USER_AGENT']); } AEUtilLogger::WriteLog(_AE_LOG_INFO, "Safe mode :" . ini_get("safe_mode")); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Display errors :" . ini_get("display_errors")); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Error reporting :" . self::error2string()); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Error display :" . self::errordisplay()); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Disabled functions :" . ini_get("disable_functions")); AEUtilLogger::WriteLog(_AE_LOG_INFO, "open_basedir restr.:" . ini_get('open_basedir')); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Max. exec. time :" . ini_get("max_execution_time")); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Memory limit :" . ini_get("memory_limit")); if (function_exists("memory_get_usage")) { AEUtilLogger::WriteLog(_AE_LOG_INFO, "Current mem. usage :" . memory_get_usage()); } if (function_exists("gzcompress")) { AEUtilLogger::WriteLog(_AE_LOG_INFO, "GZIP Compression : available (good)"); } else { AEUtilLogger::WriteLog(_AE_LOG_INFO, "GZIP Compression : n/a (no compression)"); } AEPlatform::getInstance()->log_platform_special_directories(); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Output directory :" . $registry->get('akeeba.basic.output_directory')); AEUtilLogger::WriteLog(_AE_LOG_INFO, "--------------------------------------------------------------------------------"); } // Quirks reporting $quirks = AEUtilQuirks::get_quirks(true); if (!empty($quirks)) { AEUtilLogger::WriteLog(_AE_LOG_INFO, "Akeeba Backup has detected the following potential problems:"); foreach ($quirks as $q) { AEUtilLogger::WriteLog(_AE_LOG_INFO, '- ' . $q['code'] . ' ' . $q['description'] . ' (' . $q['severity'] . ')'); } AEUtilLogger::WriteLog(_AE_LOG_INFO, "You probably do not have to worry about them, but you should be aware of them."); AEUtilLogger::WriteLog(_AE_LOG_INFO, "--------------------------------------------------------------------------------"); } if (!version_compare(PHP_VERSION, '5.3.0', 'ge')) { AEUtilLogger::WriteLog(_AE_LOG_WARNING, "You are using an outdated version of PHP. Akeeba Engine may not work properly. Please upgrade to PHP 5.3 or later."); } // Report profile ID $profile_id = AEPlatform::getInstance()->get_active_profile(); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Loaded profile #{$profile_id}"); // Get archive name AEUtilFilesystem::get_archive_name($relativeArchiveName, $absoluteArchiveName); // ==== Stats initialisation === $origin = AEPlatform::getInstance()->get_backup_origin(); // Get backup origin $profile_id = AEPlatform::getInstance()->get_active_profile(); // Get active profile $registry = AEFactory::getConfiguration(); $backupType = $registry->get('akeeba.basic.backup_type'); AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Backup type is now set to '" . $backupType . "'"); // Substitute "variables" in the archive name $description = AEUtilFilesystem::replace_archive_name_variables($this->description); $comment = AEUtilFilesystem::replace_archive_name_variables($this->comment); if ($registry->get('volatile.writer.store_on_server', true)) { // Archive files are stored on our server $stat_relativeArchiveName = $relativeArchiveName; $stat_absoluteArchiveName = $absoluteArchiveName; } else { // Archive files are not stored on our server (FTP backup, cloud backup, sent by email, etc) $stat_relativeArchiveName = ''; $stat_absoluteArchiveName = ''; } $kettenrad = AEFactory::getKettenrad(); $temp = array('description' => $description, 'comment' => $comment, 'backupstart' => AEPlatform::getInstance()->get_timestamp_database(), 'status' => 'run', 'origin' => $origin, 'type' => $backupType, 'profile_id' => $profile_id, 'archivename' => $stat_relativeArchiveName, 'absolute_path' => $stat_absoluteArchiveName, 'multipart' => 0, 'filesexist' => 1, 'tag' => $kettenrad->getTag()); // Save the entry $statistics = AEFactory::getStatistics(); $statistics->setStatistics($temp); if ($statistics->getError()) { $this->setError($statistics->getError()); return; } $statistics->release_multipart_lock(); // Initialize the archive. if (AEUtilScripting::getScriptingParameter('core.createarchive', true)) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Expanded archive file name: " . $absoluteArchiveName); AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Initializing archiver engine"); $archiver = AEFactory::getArchiverEngine(); $archiver->initialize($absoluteArchiveName); $archiver->setComment($comment); // Add the comment to the archive itself. $archiver->propagateToObject($this); if ($this->getError()) { return; } } $this->setState('postrun'); }
/** * Creates a new archive part * @param bool $finalPart Set to true if it is the final part (therefore has the .jps extension) */ private function _createNewPart($finalPart = false) { // Push the previous part if we have to post-process it immediately $configuration = AEFactory::getConfiguration(); if ($configuration->get('engine.postproc.common.after_part', 0)) { $this->finishedPart[] = $this->_dataFileName; } $this->_totalFragments++; $this->_currentFragment = $this->_totalFragments; if ($finalPart) { $this->_dataFileName = $this->_dataFileNameBase . '.jps'; } else { $this->_dataFileName = $this->_dataFileNameBase . '.j' . sprintf('%02d', $this->_currentFragment); } AEUtilLogger::WriteLog(_AE_LOG_INFO, 'Creating new JPS part #' . $this->_currentFragment . ', file ' . $this->_dataFileName); // Inform that we have chenged the multipart number $statistics = AEFactory::getStatistics(); $statistics->updateMultipart($this->_totalFragments); // Try to remove any existing file @unlink($this->_dataFileName); // Touch the new file $result = @touch($this->_dataFileName); if (function_exists('chmod')) { chmod($this->_dataFileName, 0666); } return $result; }
/** * Performs one more step of dumping database data * @return type */ protected function stepDatabaseDump() { // Initialize local variables $db = $this->getDB(); if ($this->getError()) { return; } if (!is_object($db) || $db === false) { $this->setError(__CLASS__ . '::_run() Could not connect to database?!'); return; } $outData = ''; // Used for outputting INSERT INTO commands $this->enforceSQLCompatibility(); // Apply MySQL compatibility option if ($this->getError()) { return; } // Touch SQL dump file $nada = ""; $this->writeline($nada); // Get this table's information $tableName = $this->nextTable; $this->setStep($tableName); $this->setSubstep(''); $tableAbstract = trim($this->table_name_map[$tableName]); $dump_records = $this->tables_data[$tableName]['dump_records']; // If it is the first run, find number of rows and get the CREATE TABLE command if ($this->nextRange == 0) { if ($this->getError()) { return; } $outCreate = ''; if (is_array($this->tables_data[$tableName])) { if (array_key_exists('create', $this->tables_data[$tableName])) { $outCreate = $this->tables_data[$tableName]['create']; } } if (empty($outCreate) && !empty($tableName)) { // The CREATE command wasn't cached. Time to create it. The $type and $dependencies // variables will be thrown away. $type = 'table'; $dependencies = array(); $outCreate = $this->get_create($tableAbstract, $tableName, $type, $dependencies); } // Create drop statements if required (the key is defined by the scripting engine) $configuration = AEFactory::getConfiguration(); if (AEUtilScripting::getScriptingParameter('db.dropstatements', 0)) { if (array_key_exists('create', $this->tables_data[$tableName])) { // @todo This looks cheesy... $dropStatement = $this->createDrop($this->tables_data[$tableName]['create']); } else { $type = 'table'; $createStatement = $this->get_create($tableAbstract, $tableName, $type, $dependencies); $dropStatement = $this->createDrop($createStatement); } if (!empty($dropStatement)) { $dropStatement .= "\n"; if (!$this->writeDump($dropStatement)) { return; } } } // Write the CREATE command after any DROP command which might be necessary. if (!$this->writeDump($outCreate)) { return; } if ($dump_records) { // We are dumping data from a table, get the row count $this->getRowCount($tableAbstract); } else { // We should not dump any data AEUtilLogger::WriteLog(_AE_LOG_INFO, "Skipping dumping data of " . $tableAbstract); $this->maxRange = 0; $this->nextRange = 1; $outData = ''; $numRows = 0; } // Output any data preamble commands, e.g. SET IDENTITY_INSERT for SQL Server if ($dump_records && AEUtilScripting::getScriptingParameter('db.dropstatements', 0)) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Writing data dump preamble for " . $tableAbstract); $preamble = $this->getDataDumpPreamble($tableAbstract, $tableName, $this->maxRange); if (!empty($preamble)) { if (!$this->writeDump($preamble)) { return; } } } } // Check if we have more work to do on this table $configuration = AEFactory::getConfiguration(); $batchsize = intval($configuration->get('engine.dump.common.batchsize', 1000)); if ($batchsize <= 0) { $batchsize = 1000; } if ($this->nextRange < $this->maxRange) { $timer = AEFactory::getTimer(); // Get the number of rows left to dump from the current table $sql = $db->getQuery(true)->select('*')->from($db->nameQuote($tableAbstract)); if ($this->nextRange == 0) { // First run, get a cursor to all records $db->setQuery($sql, 0, $batchsize); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Beginning dump of " . $tableAbstract); } else { // Subsequent runs, get a cursor to the rest of the records $db->setQuery($sql, $this->nextRange, $batchsize); $this->setSubstep($this->nextRange . ' / ' . $this->maxRange); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Continuing dump of " . $tableAbstract . " from record #{$this->nextRange}"); } $this->query = ''; $numRows = 0; $use_abstract = AEUtilScripting::getScriptingParameter('db.abstractnames', 1); $filters = AEFactory::getFilters(); $mustFilter = $filters->hasFilterType('dbobject', 'children'); try { $cursor = $db->query(); } catch (Exception $exc) { $db->resetErrors(); $cursor = null; } while (is_array($myRow = $db->fetchAssoc()) && $numRows < $this->maxRange - $this->nextRange) { $this->createNewPartIfRequired(); $numRows++; $numOfFields = count($myRow); // On MS SQL Server there's always a RowNumber pseudocolumn added at the end, screwing up the backup (GRRRR!) if ($db->getDriverType() == 'mssql') { $numOfFields--; } // If row-level filtering is enabled, please run the filtering if ($mustFilter) { $isFiltered = $filters->isFiltered(array('table' => $tableAbstract, 'row' => $myRow), $configuration->get('volatile.database.root', '[SITEDB]'), 'dbobject', 'children'); if ($isFiltered) { continue; } } if (!$this->extendedInserts || $this->extendedInserts && empty($this->query)) { $newQuery = true; $fieldList = $this->getFieldListSQL(array_keys($myRow), $numOfFields); if ($numOfFields > 0) { $this->query = "INSERT INTO " . $db->nameQuote(!$use_abstract ? $tableName : $tableAbstract) . " {$fieldList} VALUES "; } } else { // On other cases, just mark that we should add a comma and start a new VALUES entry $newQuery = false; } $outData = '('; // Step through each of the row's values $fieldID = 0; // Used in running backup fix $isCurrentBackupEntry = false; // Fix 1.2a - NULL values were being skipped if ($numOfFields > 0) { foreach ($myRow as $value) { // The ID of the field, used to determine placement of commas $fieldID++; if ($fieldID > $numOfFields) { // This is required for SQL Server backups, do NOT remove! continue; } // Fix 2.0: Mark currently running backup as successful in the DB snapshot if ($tableAbstract == '#__ak_stats') { if ($fieldID == 1) { // Compare the ID to the currently running $statistics = AEFactory::getStatistics(); $isCurrentBackupEntry = $value == $statistics->getId(); } elseif ($fieldID == 6) { // Treat the status field $value = $isCurrentBackupEntry ? 'complete' : $value; } } // Post-process the value if (is_null($value)) { $outData .= "NULL"; // Cope with null values } else { // Accommodate for runtime magic quotes $value = @get_magic_quotes_runtime() ? stripslashes($value) : $value; $value = $db->Quote($value); if ($this->postProcessValues) { $value = $this->postProcessQuotedValue($value); } $outData .= $value; } if ($fieldID < $numOfFields) { $outData .= ', '; } } } $outData .= ')'; if ($numOfFields) { // If it's an existing query and we have extended inserts if ($this->extendedInserts && !$newQuery) { // Check the existing query size $query_length = strlen($this->query); $data_length = strlen($outData); if ($query_length + $data_length > $this->packetSize) { // We are about to exceed the packet size. Write the data so far. $this->query .= ";\n"; if (!$this->writeDump($this->query)) { return; } // Then, start a new query $this->query = ''; $this->query = "INSERT INTO " . $db->nameQuote(!$use_abstract ? $tableName : $tableAbstract) . " VALUES "; $this->query .= $outData; } else { // We have room for more data. Append $outData to the query. $this->query .= ', '; $this->query .= $outData; } } elseif ($this->extendedInserts && $newQuery) { // Append the data to the INSERT statement $this->query .= $outData; // Let's see the size of the dumped data... $query_length = strlen($this->query); if ($query_length >= $this->packetSize) { // This was a BIG query. Write the data to disk. $this->query .= ";\n"; if (!$this->writeDump($this->query)) { return; } // Then, start a new query $this->query = ''; } } else { // Append the data to the INSERT statement $this->query .= $outData; // Write the data to disk. $this->query .= ";\n"; if (!$this->writeDump($this->query)) { return; } // Then, start a new query $this->query = ''; } } $outData = ''; unset($myRow); // Check for imminent timeout if ($timer->getTimeLeft() <= 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Breaking dump of {$tableAbstract} after {$numRows} rows; will continue on next step"); break; } } $db->freeResult($cursor); // Advance the _nextRange pointer $this->nextRange += $numRows != 0 ? $numRows : 1; $this->setStep($tableName); $this->setSubstep($this->nextRange . ' / ' . $this->maxRange); } // Finalize any pending query // WARNING! If we do not do that now, the query will be emptied in the next operation and all // accumulated data will go away... if (!empty($this->query)) { $this->query .= ";\n"; if (!$this->writeDump($this->query)) { return; } $this->query = ''; } // Check for end of table dump (so that it happens inside the same operation) if (!($this->nextRange < $this->maxRange)) { // Tell the user we are done with the table AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Done dumping " . $tableAbstract); // Output any data preamble commands, e.g. SET IDENTITY_INSERT for SQL Server if ($dump_records && AEUtilScripting::getScriptingParameter('db.dropstatements', 0)) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Writing data dump epilogue for " . $tableAbstract); $epilogue = $this->getDataDumpEpilogue($tableAbstract, $tableName, $this->maxRange); if (!empty($epilogue)) { if (!$this->writeDump($epilogue)) { return; } } } if (count($this->tables) == 0) { // We have finished dumping the database! AEUtilLogger::WriteLog(_AE_LOG_INFO, "End of database detected; flushing the dump buffers..."); $null = null; $this->writeDump($null); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Database has been successfully dumped to SQL file(s)"); $this->setState('postrun'); $this->setStep(''); $this->setSubstep(''); $this->nextTable = ''; $this->nextRange = 0; } elseif (count($this->tables) != 0) { // Switch tables $this->nextTable = array_shift($this->tables); $this->nextRange = 0; $this->setStep($this->nextTable); $this->setSubstep(''); } } }
protected function is_excluded_by_api($test, $root) { static $filter_switch; static $last_backup; if (is_null($filter_switch)) { $config = AEFactory::getConfiguration(); $filter_switch = AEUtilScripting::getScriptingParameter('filter.incremental', 0); $filter_switch = $filter_switch == 1; $last_backup = $config->get('volatile.filter.last_backup', null); if (is_null($last_backup) && $filter_switch) { // Get a list of backups on this profile $backups = AEPlatform::getInstance()->get_statistics_list(array('filters' => AEPlatform::getInstance()->get_active_profile())); // Find this backup's ID $model = AEFactory::getStatistics(); $id = $model->getId(); if (is_null($id)) { $id = -1; } // Initialise JLoader::import('joomla.utilities.date'); $last_backup = time(); $now = $last_backup; // Find the last time a successful backup with this profile was made if (count($backups)) { foreach ($backups as $backup) { // Skip the current backup if ($backup['id'] == $id) { continue; } // Skip non-complete backups if ($backup['status'] != 'complete') { continue; } $jdate = new JDate($backup['backupstart']); $backuptime = $jdate->toUnix(); $last_backup = $backuptime; break; } } if ($last_backup == $now) { // No suitable backup found; disable this filter $config->set('volatile.scripting.incfile.filter.incremental', 0); $filter_switch = false; } else { // Cache the last backup timestamp $config->set('volatile.filter.last_backup', $last_backup); } } } if (!$filter_switch) { return false; } // Get the filesystem path for $root $config = AEFactory::getConfiguration(); $fsroot = $config->get('volatile.filesystem.current_root', ''); $ds = $fsroot == '' || $fsroot == '/' ? '' : DIRECTORY_SEPARATOR; $filename = $fsroot . $ds . $test; // Get the timestamp of the file $timestamp = @filemtime($filename); // If we could not get this information, include the file in the archive if ($timestamp === false) { return false; } // Compare it with the last backup timestamp and exclude if it's older than the last backup if ($timestamp <= $last_backup) { //AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Excluding $filename due to incremental backup restrictions"); return true; } // No match? Just include the file! return false; }
/** * Returns a copy of the class's status array * @return array */ public function getStatusArray() { if (empty($this->array_cache)) { // Get the default table $array = $this->_makeReturnTable(); // Add the archive name $statistics = AEFactory::getStatistics(); $record = $statistics->getRecord(); $array['Archive'] = isset($record['archivename']) ? $record['archivename'] : ''; // Translate HasRun to what the rest of the suite expects $array['HasRun'] = $this->getState() == 'finished' ? 1 : 0; // Translate no errors $array['Error'] = $array['Error'] == false ? '' : $array['Error']; $array['tag'] = $this->tag; $array['Progress'] = $this->getProgress(); $this->array_cache = $array; } return $this->array_cache; }
private function _createNewPart($finalPart = false) { // Push the previous part if we have to post-process it immediately $configuration =& AEFactory::getConfiguration(); if ($configuration->get('engine.postproc.common.after_part', 0)) { // The first part needs its header overwritten during archive // finalization. Skip it from immediate processing. if ($this->_currentFragment != 1) { $this->finishedPart[] = $this->_dataFileName; } } $this->_totalFragments++; $this->_currentFragment = $this->_totalFragments; if ($finalPart) { $this->_dataFileName = $this->_dataFileNameBase . '.jpa'; } else { $this->_dataFileName = $this->_dataFileNameBase . '.j' . sprintf('%02d', $this->_currentFragment); } AEUtilLogger::WriteLog(_AE_LOG_INFO, 'Creating new JPA part #' . $this->_currentFragment . ', file ' . $this->_dataFileName); $statistics =& AEFactory::getStatistics(); $statistics->updateMultipart($this->_totalFragments); // Try to remove any existing file @unlink($this->_dataFileName); // Touch the new file $result = @touch($this->_dataFileName); if (function_exists('chmod')) { chmod($this->_dataFileName, 0666); } // Try to write 6 bytes to it if ($result) { $result = @file_put_contents($this->_dataFileName, 'AKEEBA') == 6; } if ($result) { @unlink($this->_dataFileName); $result = @touch($this->_dataFileName); if (function_exists('chmod')) { chmod($this->_dataFileName, 0666); } } return $result; }
/** * Find where to store the backup files * @param $partNumber int The SQL part number, default is 0 (.sql) */ protected function getBackupFilePaths($partNumber = 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, __CLASS__ . " :: Getting temporary file"); $this->tempFile = AEUtilTempfiles::registerTempFile(dechex(crc32(microtime())) . '.sql'); AEUtilLogger::WriteLog(_AE_LOG_DEBUG, __CLASS__ . " :: Temporary file is {$this->tempFile}"); // Get the base name of the dump file $partNumber = intval($partNumber); $baseName = $this->dumpFile; if ($partNumber > 0) { // The file names are in the format dbname.sql, dbname.s01, dbname.s02, etc if (strtolower(substr($baseName, -4)) == '.sql') { $baseName = substr($baseName, 0, -4) . '.s' . sprintf('%02u', $partNumber); } else { $baseName = $baseName . '.s' . sprintf('%02u', $partNumber); } } if (empty($this->installerSettings)) { // Fetch the installer settings $this->installerSettings = (object) array('installerroot' => 'installation', 'sqlroot' => 'installation/sql', 'databasesini' => 1, 'readme' => 1, 'extrainfo' => 1); $config = AEFactory::getConfiguration(); $installerKey = $config->get('akeeba.advanced.embedded_installer'); $installerDescriptors = AEUtilInihelper::getInstallerList(); if (array_key_exists($installerKey, $installerDescriptors)) { // The selected installer exists, use it $this->installerSettings = (object) $installerDescriptors[$installerKey]; } elseif (array_key_exists('abi', $installerDescriptors)) { // The selected installer doesn't exist, but ABI exists; use that instead $this->installerSettings = (object) $installerDescriptors['abi']; } } switch (AEUtilScripting::getScriptingParameter('db.saveasname', 'normal')) { case 'output': // The SQL file will be stored uncompressed in the output directory $statistics = AEFactory::getStatistics(); $statRecord = $statistics->getRecord(); $this->saveAsName = $statRecord['absolute_path']; break; case 'normal': // The SQL file will be stored in the SQL root of the archive, as // specified by the particular embedded installer's settings $this->saveAsName = $this->installerSettings->sqlroot . '/' . $baseName; break; case 'short': // The SQL file will be stored on archive's root $this->saveAsName = $baseName; break; } if ($partNumber > 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "AkeebaDomainDBBackup :: Creating new SQL dump part #{$partNumber}"); } AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "AkeebaDomainDBBackup :: SQL temp file is " . $this->tempFile); AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "AkeebaDomainDBBackup :: SQL file location in archive is " . $this->saveAsName); }
/** * Returns a copy of the class's status array * * @return array */ public function getStatusArray() { if (empty($this->array_cache)) { // Get the default table $array = $this->_makeReturnTable(); // Get the current step number $stepCounter = AEFactory::getConfiguration()->get('volatile.step_counter', 0); // Add the archive name $statistics = AEFactory::getStatistics(); $record = $statistics->getRecord(); $array['Archive'] = isset($record['archivename']) ? $record['archivename'] : ''; // Translate HasRun to what the rest of the suite expects $array['HasRun'] = $this->getState() == 'finished' ? 1 : 0; // Translate no errors $array['Error'] = $array['Error'] == false ? '' : $array['Error']; $array['tag'] = $this->tag; $array['Progress'] = $this->getProgress(); $array['backupid'] = $this->getBackupId(); $array['sleepTime'] = $this->waitTimeMsec; $array['stepNumber'] = $stepCounter; $array['stepState'] = $this->getState(); $this->array_cache = $array; } return $this->array_cache; }
/** * Applies the size and count quotas * * @return bool */ private function get_remote_quotas() { // Get all records with a remote filename $allRecords = AEPlatform::getInstance()->get_valid_remote_records(); // Bail out if no records found if (empty($allRecords)) { return array(); } // Try to find the files to be deleted due to quota settings $statistics = AEFactory::getStatistics(); $latestBackupId = $statistics->getId(); // Filter out the current record $temp = array(); foreach ($allRecords as $item) { if ($item['id'] == $latestBackupId) { continue; } $item['files'] = $this->get_remote_files($item['remote_filename'], $item['multipart']); $temp[] = $item; } $allRecords = $temp; // Bail out if only the current backup was included in the list if (count($allRecords) == 0) { return array(); } // Get quota values $registry = AEFactory::getConfiguration(); $countQuota = $registry->get('akeeba.quota.count_quota'); $sizeQuota = $registry->get('akeeba.quota.size_quota'); $useCountQuotas = $registry->get('akeeba.quota.enable_count_quota'); $useSizeQuotas = $registry->get('akeeba.quota.enable_size_quota'); $useDayQuotas = $registry->get('akeeba.quota.maxage.enable'); $daysQuota = $registry->get('akeeba.quota.maxage.maxdays'); $preserveDay = $registry->get('akeeba.quota.maxage.keepday'); $leftover = array(); $ret = array(); $killids = array(); if ($useDayQuotas) { $killDatetime = new DateTime(); $killDatetime->modify('-' . $daysQuota . ($daysQuota == 1 ? ' day' : ' days')); $killTS = $killDatetime->format('U'); foreach ($allRecords as $def) { $backupstart = new DateTime($def['backupstart']); $backupTS = $backupstart->format('U'); $backupDay = $backupstart->format('d'); // Is this on a preserve day? if ($preserveDay > 0) { if ($preserveDay == $backupDay) { $leftover[] = $def; continue; } } // Otherwise, check the timestamp if ($backupTS < $killTS) { $ret[] = $def['files']; $killids[] = $def['id']; } else { $leftover[] = $def; } } } // Do we need to apply count quotas? if ($useCountQuotas && $countQuota >= 1 && !$useDayQuotas) { $countQuota--; // Are there more files than the quota limit? if (!(count($allRecords) > $countQuota)) { // No, effectively skip the quota checking $leftover = $allRecords; } else { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Processing remote count quotas"); // Yes, apply the quota setting. $totalRecords = count($allRecords); for ($count = 0; $count <= $totalRecords; $count++) { $def = array_pop($allRecords); if (count($leftover) >= $countQuota) { $ret[] = $def['files']; $killids[] = $def['id']; } else { $leftover[] = $def; } } unset($allRecords); } } else { // No count quotas are applied $leftover = $allRecords; } // Do we need to apply size quotas? if ($useSizeQuotas && $sizeQuota > 0 && count($leftover) > 0 && !$useDayQuotas) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Processing remote size quotas"); // OK, let's start counting bytes! $runningSize = 0; while (count($leftover) > 0) { // Each time, remove the last element of the backup array and calculate // running size. If it's over the limit, add the archive to the $ret array. $def = array_pop($leftover); $runningSize += $def['total_size']; if ($runningSize >= $sizeQuota) { $ret[] = $def['files']; $killids[] = $def['id']; } } } // Convert the $ret 2-dimensional array to single dimensional $quotaFiles = array(); foreach ($ret as $temp) { if (!is_array($temp) || empty($temp)) { continue; } foreach ($temp as $filename) { $quotaFiles[] = $filename; } } // Update the statistics record with the removed remote files if (!empty($killids)) { foreach ($killids as $id) { if (empty($id)) { continue; } $data = array('remote_filename' => ''); AEPlatform::getInstance()->set_or_update_statistics($id, $data, $this); } } return $quotaFiles; }
public function apply_srp_quotas($parent) { $parent->relayStep('Applying quotas'); $parent->relaySubstep(''); // If no quota settings are enabled, quit $registry =& AEFactory::getConfiguration(); $srpQuotas = $registry->get('akeeba.quota.srp_size_quota'); if($srpQuotas <= 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "No restore point quotas were defined; old restore point files will be kept intact" ); return true; // No quota limits were requested } // Get valid-looking backup ID's $validIDs =& AEPlatform::get_valid_backup_records(true, array('restorepoint')); $statistics =& AEFactory::getStatistics(); $latestBackupId = $statistics->getId(); // Create a list of valid files $allFiles = array(); if(count($validIDs)) { foreach($validIDs as $id) { $stat = AEPlatform::get_statistics($id); // Multipart processing $filenames = AEUtilStatistics::get_all_filenames($stat, true); if(!is_null($filenames)) { // Only process existing files $filesize = 0; foreach($filenames as $filename) { $filesize += @filesize($filename); } $allFiles[] = array('id' => $id, 'filenames' => $filenames, 'size' => $filesize); } } } unset($validIDs); // If there are no files, exit early if(count($allFiles) == 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "There were no old restore points to apply quotas on" ); return true; } // Init arrays $killids = array(); $ret = array(); $leftover = array(); // Do we need to apply size quotas? AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Processing restore point size quotas" ); // OK, let's start counting bytes! $runningSize = 0; while(count($allFiles) > 0) { // Each time, remove the last element of the backup array and calculate // running size. If it's over the limit, add the archive to the return array. $def = array_pop($allFiles); $runningSize += $def['size']; if($runningSize >= $srpQuotas) { if($latestBackupId == $def['id']) { $runningSize -= $def['size']; } else { $ret[] = $def['filenames']; $killids[] = $def['filenames']; } } } // Convert the $ret 2-dimensional array to single dimensional $quotaFiles = array(); foreach($ret as $temp) { foreach($temp as $filename) { $quotaFiles[] = $filename; } } // Update the statistics record with the removed remote files if(!empty($killids)) foreach($killids as $id) { $data = array('filesexist' => '0'); AEPlatform::set_or_update_statistics($id, $data, $parent); } // Apply quotas if(count($quotaFiles) > 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Applying quotas" ); jimport('joomla.filesystem.file'); foreach($quotaFiles as $file) { if(!@AEPlatform::unlink($file)) { $parent->setWarning("Failed to remove old system restore point file ".$file ); } } } return true; }
/** * Implements the _run() abstract method */ protected function _run() { // Check if we are already done if ($this->getState() == 'postrun') { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, __CLASS__." :: Already finished"); $this->setStep(""); $this->setSubstep(""); return; } // Mark ourselves as still running (we will test if we actually do towards the end ;) ) $this->setState('running'); // Check if we are still adding a database dump part to the archive, or if // we have to post-process a part if( AEUtilScripting::getScriptingParameter('db.saveasname','normal') != 'output' ) { $archiver =& AEFactory::getArchiverEngine(); $configuration =& AEFactory::getConfiguration(); if($configuration->get('engine.postproc.common.after_part',0)) { if(!empty($archiver->finishedPart)) { $filename = array_shift($archiver->finishedPart); AEUtilLogger::WriteLog(_AE_LOG_INFO, 'Preparing to post process '.basename($filename)); $post_proc =& AEFactory::getPostprocEngine(); $result = $post_proc->processPart( $filename ); $this->propagateFromObject($post_proc); if($result === false) { $this->setWarning('Failed to process file '.basename($filename)); } else { AEUtilLogger::WriteLog(_AE_LOG_INFO, 'Successfully processed file '.basename($filename)); } // Should we delete the file afterwards? if( $configuration->get('engine.postproc.common.delete_after',false) && $post_proc->allow_deletes && ($result !== false) ) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, 'Deleting already processed file '.basename($filename)); AEPlatform::unlink($filename); } if($post_proc->break_after) { $configuration->set('volatile.breakflag', true); return; } } } if($configuration->get('volatile.engine.archiver.processingfile',false)) { // We had already started archiving the db file, but it needs more time $finished = true; AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Continuing adding the SQL dump part to the archive"); $archiver->addFile(null,null,null); $this->propagateFromObject($archiver); if($this->getError()) return; $finished = !$configuration->get('volatile.engine.archiver.processingfile',false); if($finished) { $this->getNextDumpPart(); } else { return; } } } // Initialize local variables $db =& $this->getDB(); if($this->getError()) return; if( !is_object($db) || ($db === false) ) { $this->setError(__CLASS__.'::_run() Could not connect to database?!'); return; } $outData = ''; // Used for outputting INSERT INTO commands $this->enforceSQLCompatibility(); // Apply MySQL compatibility option if($this->getError()) return; // Touch SQL dump file $nada = ""; $this->writeline($nada); // Get this table's information $tableName = $this->nextTable; $tableAbstract = trim( $this->table_name_map[$tableName] ); $dump_records = $this->tables_data[$tableName]['dump_records']; // If it is the first run, find number of rows and get the CREATE TABLE command if( $this->nextRange == 0 ) { if($this->getError()) return; $outCreate = ''; if(is_array($this->tables_data[$tableName])) { if(array_key_exists('create', $this->tables_data[$tableName])) { $outCreate = $this->tables_data[$tableName]['create']; } } if(empty($outCreate) && !empty($tableName)) { // The CREATE command wasn't cached. Time to create it. The $type and $dependencies // variables will be thrown away. $type = 'table'; $outCreate = $this->get_create($tableAbstract, $tableName, $type, $dependencies); } // Write the CREATE command if(!$this->writeDump($outCreate)) return; // Create drop statements if required (the key is defined by the scripting engine) $configuration =& AEFactory::getConfiguration(); if( AEUtilScripting::getScriptingParameter('db.dropstatements',0) ) { if(array_key_exists('create', $this->tables_data[$tableName])) { // @todo This looks cheesy... $dropStatement = $this->createDrop($this->tables_data[$tableName]['create']); } else { $type = 'table'; $createStatement = $this->get_create($tableAbstract, $tableName, $type, $dependencies); $dropStatement = $this->createDrop($createStatement); } if(!empty($dropStatement)) { if(!$this->writeDump($outCreate)) return; } } if( $dump_records ) { // We are dumping data from a table, get the row count $this->getRowCount( $tableAbstract ); } else { // We should not dump any data AEUtilLogger::WriteLog(_AE_LOG_INFO, "Skipping dumping data of " . $tableAbstract); $this->maxRange = 0; $this->nextRange = 1; $outData = ''; $numRows = 0; } } // Check if we have more work to do on this table $configuration =& AEFactory::getConfiguration(); $batchsize = intval($configuration->get('engine.dump.common.batchsize', 1000)); if($batchsize <= 0) $batchsize = 1000; if( ($this->nextRange < $this->maxRange) ) { $timer =& AEFactory::getTimer(); // Get the number of rows left to dump from the current table $sql = "SELECT * FROM `$tableAbstract`"; if( $this->nextRange == 0 ) { // First run, get a cursor to all records $db->setQuery( $sql, 0, $batchsize ); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Beginning dump of " . $tableAbstract); } else { // Subsequent runs, get a cursor to the rest of the records $db->setQuery( $sql, $this->nextRange, $batchsize ); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Continuing dump of " . $tableAbstract . " from record #{$this->nextRange}"); } $this->query = ''; $numRows = 0; $use_abstract = AEUtilScripting::getScriptingParameter('db.abstractnames', 1); while( is_array($myRow = $db->loadAssoc(false)) && ( $numRows < ($this->maxRange - $this->nextRange) ) ) { $this->createNewPartIfRequired(); $numRows++; $numOfFields = count( $myRow ); if( (!$this->extendedInserts) || // Add header on simple INSERTs, or... ( $this->extendedInserts && empty($this->query) ) //...on extended INSERTs if there are no other data, yet ) { $newQuery = true; if( $numOfFields > 0 ) $this->query = "INSERT INTO `" . (!$use_abstract ? $tableName : $tableAbstract) . "` VALUES "; } else { // On other cases, just mark that we should add a comma and start a new VALUES entry $newQuery = false; } $outData = '('; // Step through each of the row's values $fieldID = 0; // Used in running backup fix $isCurrentBackupEntry = false; // Fix 1.2a - NULL values were being skipped if( $numOfFields > 0 ) foreach( $myRow as $value ) { // The ID of the field, used to determine placement of commas $fieldID++; // Fix 2.0: Mark currently running backup as successful in the DB snapshot if($tableAbstract == '#__ak_stats') { if($fieldID == 1) { // Compare the ID to the currently running $statistics =& AEFactory::getStatistics(); $isCurrentBackupEntry = ($value == $statistics->getId()); } elseif ($fieldID == 6) { // Treat the status field $value = $isCurrentBackupEntry ? 'complete' : $value; } } // Post-process the value if( is_null($value) ) { $outData .= "NULL"; // Cope with null values } else { // Accommodate for runtime magic quotes $value = @get_magic_quotes_runtime() ? stripslashes( $value ) : $value; $outData .= $db->Quote($value); } if( $fieldID < $numOfFields ) $outData .= ', '; } // foreach $outData .= ')'; if( $numOfFields ) { // If it's an existing query and we have extended inserts if($this->extendedInserts && !$newQuery) { // Check the existing query size $query_length = strlen($this->query); $data_length = strlen($outData); if( ($query_length + $data_length) > $this->packetSize ) { // We are about to exceed the packet size. Write the data so far. $this->query .= ";\n"; if(!$this->writeDump($this->query)) return; // Then, start a new query $this->query = ''; $this->query = "INSERT INTO `" . (!$use_abstract ? $tableName : $tableAbstract) . "` VALUES "; $this->query .= $outData; } else { // We have room for more data. Append $outData to the query. $this->query .= ', '; $this->query .= $outData; } } elseif($this->extendedInserts && $newQuery) // If it's a brand new insert statement in an extended INSERTs set { // Append the data to the INSERT statement $this->query .= $outData; // Let's see the size of the dumped data... $query_length = strlen($this->query); if($query_length >= $this->packetSize) { // This was a BIG query. Write the data to disk. $this->query .= ";\n"; if(!$this->writeDump($this->query)) return; // Then, start a new query $this->query = ''; } } else // It's a normal (not extended) INSERT statement { // Append the data to the INSERT statement $this->query .= $outData; // Write the data to disk. $this->query .= ";\n"; if(!$this->writeDump($this->query)) return; // Then, start a new query $this->query = ''; } } $outData = ''; // Check for imminent timeout if( $timer->getTimeLeft() <= 0 ) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Breaking dump of $tableAbstract after $numRows rows; will continue on next step"); break; } } // for (all rows left) // Advance the _nextRange pointer $this->nextRange += ($numRows != 0) ? $numRows : 1; $this->setStep($tableName); $this->setSubstep($this->nextRange . ' / ' . $this->maxRange); } // if more work on the table // Finalize any pending query // WARNING! If we do not do that now, the query will be emptied in the next operation and all // accumulated data will go away... if(!empty($this->query)) { $this->query .= ";\n"; if(!$this->writeDump($this->query)) return; $this->query = ''; } // Check for end of table dump (so that it happens inside the same operation) if( !($this->nextRange < $this->maxRange) ) { // Tell the user we are done with the table AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Done dumping " . $tableAbstract); if(count($this->tables) == 0) { // We have finished dumping the database! AEUtilLogger::WriteLog(_AE_LOG_INFO, "End of database detected; flushing the dump buffers..."); $null = null; $this->writeDump($null); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Database has been successfully dumped to SQL file(s)"); $this->setState('postrun'); $this->setStep(''); $this->setSubstep(''); $this->nextTable = ''; $this->nextRange = 0; } elseif(count($this->tables) != 0) { // Switch tables $this->nextTable = array_shift( $this->tables ); $this->nextRange = 0; $this->setStep($this->nextTable); $this->setSubstep(''); } } $null = null; $this->writeline($null); }
private function _apiStartSRPBackup($config) { // Get the passed configuration values $defConfig = array('tag' => 'restorepoint', 'type' => 'component', 'name' => 'akeeba', 'group' => '', 'customdirs' => array(), 'extraprefixes' => array(), 'customtables' => array(), 'skiptables' => array(), 'xmlname' => ''); $config = array_merge($defConfig, $config); foreach ($config as $key => $value) { if (!array_key_exists($key, $defConfig)) { unset($config[$key]); } } // Fetch the extension's version information require_once JPATH_ADMINISTRATOR . '/components/com_akeeba/assets/xmlslurp/xmlslurp.php'; $slurp = new LiveUpdateXMLSlurp(); $exttype = $config['type']; switch ($exttype) { case 'component': $extname = 'com_'; break; case 'module': $extname = 'mod_'; break; case 'plugin': $extname = 'plg_'; break; case 'template': $extname = 'tpl_'; break; } $extname .= $config['name']; $info = $slurp->getInfo($extname, ''); $configOverrides = array('akeeba.basic.archive_name' => 'restore-point-[DATE]-[TIME]', 'akeeba.basic.backup_type' => 'full', 'akeeba.basic.backup_type' => 'full', 'akeeba.advanced.archiver_engine' => 'jpa', 'akeeba.advanced.proc_engine' => 'none', 'akeeba.advanced.embedded_installer' => 'none', 'engine.archiver.common.dereference_symlinks' => true, 'core.filters.srp.type' => $config['type'], 'core.filters.srp.group' => $config['group'], 'core.filters.srp.name' => $config['name'], 'core.filters.srp.customdirs' => $config['customdirs'], 'core.filters.srp.customfiles' => $config['customfiles'], 'core.filters.srp.extraprefixes' => $config['extraprefixes'], 'core.filters.srp.customtables' => $config['customtables'], 'core.filters.srp.skiptables' => $config['skiptables'], 'core.filters.srp.langfiles' => $config['langfiles']); // Parse a local file stored in (backend)/assets/srpdefs/$extname.xml JLoader::import('joomla.filesystem.file'); $filename = JPATH_COMPONENT_ADMINISTRATOR . '/assets/srpdefs/' . $extname . '.xml'; if (JFile::exists($filename)) { $xml = JFactory::getXMLParser('simple'); if ($xml->loadFile($filename)) { $extraConfig = $this->parseRestorePointXML($xml->document); if ($extraConfig !== false) { $this->mergeSRPConfig($configOverrides, $extraConfig); } } unset($xml); } // Parse the extension's manifest file and look for a <restorepoint> tag if (!empty($info['xmlfile'])) { $xml = JFactory::getXMLParser('simple'); if ($xml->loadFile($info['xmlfile'])) { $restorepoint = $xml->document->getElementByPath('restorepoint'); if ($restorepoint) { $extraConfig = $this->parseRestorePointXML($restorepoint); if ($extraConfig !== false) { $this->mergeSRPConfig($configOverrides, $extraConfig); } } } unset($restorepoint); unset($xml); } // Create an SRP descriptor $srpdescriptor = array('type' => $config['type'], 'name' => $config['name'], 'group' => $config['group'], 'version' => $info['version'], 'date' => $info['date']); // Set the description and comment $description = "System Restore Point - " . JText::_($exttype) . ": {$extname}"; $comment = "---BEGIN SRP---\n" . json_encode($srpdescriptor) . "\n---END SRP---"; $jpskey = ''; $angiekey = ''; // Set a custom finalization action queue $configOverrides['volatile.core.finalization.action_handlers'] = array(new AEFinalizationSrpquotas()); $configOverrides['volatile.core.finalization.action_queue'] = array('remove_temp_files', 'update_statistics', 'update_filesizes', 'apply_srp_quotas'); // Apply the configuration overrides, please $platform = AEPlatform::getInstance(); $platform->configOverrides = $configOverrides; // Nuke the factory AEFactory::nuke(); $profile = 1; $session = JFactory::getSession(); $session->set('profile', $profile, 'akeeba'); AEPlatform::getInstance()->load_configuration($profile); AEUtilTempvars::reset('restorepoint'); $kettenrad = AECoreKettenrad::load('restorepoint'); $options = array('description' => $description, 'comment' => $comment, 'tag' => 'restorepoint'); $kettenrad->setup($options); // Setting up the engine $kettenrad->tick(); if ($kettenrad->getState() != 'running' && $tag == 'restorepoint') { $kettenrad->tick(); } $kettenrad->resetWarnings(); // So as not to have duplicate warnings reports AECoreKettenrad::save($tag); $array = $kettenrad->getStatusArray(); if ($array['Error'] != '') { // A backup error had occurred. Why are we here?! $this->status = self::STATUS_ERROR; $this->encapsulation = self::ENCAPSULATION_RAW; return 'A backup error had occurred: ' . $array['Error']; } else { $statistics = AEFactory::getStatistics(); $array['BackupID'] = $statistics->getId(); $array['HasRun'] = 1; // Force the backup to go on. return $array; } }
private function _apiStartBackup($config) { // Get the passed configuration values $defConfig = array('profile' => 1, 'description' => '', 'comment' => ''); $config = array_merge($defConfig, $config); foreach ($config as $key => $value) { if (!array_key_exists($key, $defConfig)) { unset($config[$key]); } } extract($config); // Nuke the factory AEFactory::nuke(); // Set the profile $profile = (int) $profile; if (!is_numeric($profile)) { $profile = 1; } $session = JFactory::getSession(); $session->set('profile', $profile, 'akeeba'); AEPlatform::getInstance()->load_configuration($profile); // Use the default description if none specified if (empty($description)) { jimport('joomla.utilities.date'); $user = JFactory::getUser(); $userTZ = $user->getParam('timezone', 0); $dateNow = new JDate(); $dateNow->setOffset($userTZ); if (AKEEBA_JVERSION == '16') { $description = JText::_('BACKUP_DEFAULT_DESCRIPTION') . ' ' . $dateNow->format(JText::_('DATE_FORMAT_LC2'), true); } else { $description = JText::_('BACKUP_DEFAULT_DESCRIPTION') . ' ' . $dateNow->toFormat(JText::_('DATE_FORMAT_LC2')); } } // Start the backup AECoreKettenrad::reset(array('maxrun' => 0)); AEUtilTempvars::reset(AKEEBA_BACKUP_ORIGIN); $kettenrad = AECoreKettenrad::load(AKEEBA_BACKUP_ORIGIN); $options = array('description' => $description, 'comment' => $comment, 'tag' => AKEEBA_BACKUP_ORIGIN); $kettenrad->setup($options); // Setting up the engine $array = $kettenrad->tick(); // Initializes the init domain AECoreKettenrad::save(AKEEBA_BACKUP_ORIGIN); $array = $kettenrad->getStatusArray(); if ($array['Error'] != '') { // A backup error had occurred. Why are we here?! $this->status = self::STATUS_ERROR; $this->encapsulation = self::ENCAPSULATION_RAW; return 'A backup error had occurred: ' . $array['Error']; } else { $statistics = AEFactory::getStatistics(); $array['BackupID'] = $statistics->getId(); $array['HasRun'] = 1; // Force the backup to go on. return $array; } }