Current Directory: /home/astoriaah/www/old15/administrator/components/com_akeeba/models
Viewing File: /home/astoriaah/www/old15/administrator/components/com_akeeba/models/srprestores.php
<?php
/**
* @package AkeebaBackup
* @copyright Copyright (c)2009-2014 Nicholas K. Dionysopoulos
* @license GNU General Public License version 3, or later
*
* @since 3.3
*/
// Protect from unauthorized access
defined('_JEXEC') or die();
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
/**
* Integrated restoration Model
*
*/
class AkeebaModelSrprestores extends F0FModel
{
private $data;
private $extension;
private $path;
public $id;
public $info;
/**
* Sets the list of IDs from the request data
*/
public function setIDsFromRequest()
{
// Get the ID or list of IDs from the request or the configuration
$cid = $this->input->get('cid', null, 'array');
$id = $this->input->get('id', 0, 'int');
if (is_array($cid) && !empty($cid))
{
$this->setIds($cid);
}
else
{
if (empty($id))
{
$this->setId($id);
}
}
return $this;
}
/**
* Validates the data passed to the request.
*
* @return mixed True if all is OK, an error string if something is wrong
*/
function validateRequest()
{
// Does the ID exist?
$this->setIDsFromRequest();
$id = $this->getId();
if (empty($id))
{
return JText::_('RESTORE_ERROR_INVALID_RECORD');
}
// Is this a valid backup entry?
$data = Platform::getInstance()->get_statistics($id);
if (empty($data))
{
return JText::_('RESTORE_ERROR_INVALID_RECORD');
}
// Is this a complete backup?
if ($data['status'] != 'complete')
{
return JText::_('RESTORE_ERROR_INVALID_RECORD');
}
// Is it a restoration point backup?
if ($data['tag'] != 'restorepoint')
{
return JText::_('RESTORE_ERROR_NOT_AN_SRP');
}
$rawDataParts = explode("\n", $data['comment']);
$this->info = json_decode($rawDataParts[1]);
$this->info->srpdate = $data['backupstart'];
// Load the profile ID (so that we can find out the output directory)
$profile_id = $data['profile_id'];
Platform::getInstance()->load_configuration($profile_id);
$path = $data['absolute_path'];
$exists = @file_exists($path);
if ( !$exists)
{
// Let's try figuring out an alternative path
$config = Factory::getConfiguration();
$path = $config->get('akeeba.basic.output_directory', '') . '/' . $data['archivename'];
$exists = @file_exists($path);
}
if ( !$exists)
{
return JText::_('RESTORE_ERROR_ARCHIVE_MISSING');
}
$filename = basename($path);
$lastdot = strrpos($filename, '.');
$extension = strtoupper(substr($filename, $lastdot + 1));
if ( !in_array($extension, array('JPA', 'ZIP')))
{
return JText::_('RESTORE_ERROR_INVALID_TYPE');
}
$this->data = $data;
$this->path = $path;
$this->extension = $extension;
return true;
}
public function setRestorationParameters()
{
// Do we have to use FTP?
$procengine = $this->getState('procengine', 'direct');
// Get the absolute path to site's root
$siteroot = JPATH_SITE;
$restoration_setup = array(
'kickstart.enabled' => false,
'kickstart.tuning.max_exec_time' => '5',
'kickstart.tuning.run_time_bias' => '75',
'kickstart.tuning.min_exec_time' => '0',
'kickstart.procengine' => $procengine,
'kickstart.setup.sourcefile' => $this->path,
'kickstart.setup.destdir' => $siteroot,
'kickstart.setup.restoreperms' => '0',
'kickstart.setup.filetype' => $this->extension,
'kickstart.setup.dryrun' => '0'
);
if ($procengine == 'ftp')
{
$ftp_host = $this->getState('ftp_host', '');
$ftp_port = $this->getState('ftp_port', '21');
$ftp_user = $this->getState('ftp_user', '');
$ftp_pass = $this->getState('ftp_pass', '');
$ftp_root = $this->getState('ftp_root', '');
$ftp_ssl = $this->getState('ftp_ssl', 0);
$ftp_pasv = $this->getState('ftp_root', 1);
$tempdir = $this->getState('tmp_path', '');
$extraOptions = array(
'kickstart.ftp.ssl' => $ftp_ssl,
'kickstart.ftp.passive' => $ftp_pasv,
'kickstart.ftp.host' => $ftp_host,
'kickstart.ftp.port' => $ftp_port,
'kickstart.ftp.user' => $ftp_user,
'kickstart.ftp.pass' => $ftp_pass,
'kickstart.ftp.dir' => $ftp_root,
'kickstart.ftp.tempdir' => $tempdir
);
$restoration_setup = array_merge($restoration_setup, $extraOptions);
}
$json = json_encode($restoration_setup);
$session = JFactory::getSession();
$session->set('restoration_setup', $json, 'akeeba');
}
public function getRestorationParameters()
{
$session = JFactory::getSession();
$json = $session->get('restoration_setup', null, 'akeeba');
if ( !empty($json))
{
return json_decode($json, true);
}
else
{
return array();
}
}
function getFTPParams()
{
$config = JFactory::getConfig();
if (version_compare(JVERSION, '3.0', 'ge'))
{
return array(
'procengine' => $config->get('ftp_enable', 0) ? 'ftp' : 'direct',
'ftp_host' => $config->get('ftp_host', 'localhost'),
'ftp_port' => $config->get('ftp_port', '21'),
'ftp_user' => $config->get('ftp_user', ''),
'ftp_pass' => $config->get('ftp_pass', ''),
'ftp_root' => $config->get('ftp_root', ''),
'tempdir' => $config->get('tmp_path', '')
);
}
else
{
return array(
'procengine' => $config->getValue('config.ftp_enable', 0) ? 'ftp' : 'direct',
'ftp_host' => $config->getValue('config.ftp_host', 'localhost'),
'ftp_port' => $config->getValue('config.ftp_port', '21'),
'ftp_user' => $config->getValue('config.ftp_user', ''),
'ftp_pass' => $config->getValue('config.ftp_pass', ''),
'ftp_root' => $config->getValue('config.ftp_root', ''),
'tempdir' => $config->getValue('config.tmp_path', '')
);
}
}
function getExtractionModes()
{
$options = array();
$options[] = JHTML::_('select.option', 'direct', JText::_('RESTORE_LABEL_EXTRACTIONMETHOD_DIRECT'));
$options[] = JHTML::_('select.option', 'ftp', JText::_('RESTORE_LABEL_EXTRACTIONMETHOD_FTP'));
return $options;
}
function doAjax()
{
$ajax = $this->getState('ajax');
switch ($ajax)
{
// FTP Connection test for DirectFTP
case 'testftp':
// Grab request parameters
$config = array(
'host' => $this->input->get('host', '', 'none', 2),
'port' => $this->input->get('port', 21, 'int'),
'user' => $this->input->get('user', '', 'none', 2),
'pass' => $this->input->get('pass', '', 'none', 2),
'initdir' => $this->input->get('initdir', '', 'none', 2),
'usessl' => $this->input->get('usessl', 'cmd') == 'true',
'passive' => $this->input->get('passive', 'cmd') == 'true'
);
// Perform the FTP connection test
$test = new \Akeeba\Engine\Archiver\Directftp();
$test->initialize('', $config);
$errors = $test->getError();
if (empty($errors))
{
$result = true;
}
else
{
$result = $errors;
}
break;
case 'restoreFilesPing':
case 'restoreFilesStart':
case 'restoreFilesStep':
case 'restoreFilesFinalize':
global $restoration_setup;
$restoration_setup = $this->getRestorationParameters();
define('KICKSTART', 1);
include_once(JPATH_COMPONENT_ADMINISTRATOR . '/restore.php');
akeebaTinyHackForRestorationObserver();
masterSetup();
$retArray = array(
'status' => true,
'message' => null
);
switch ($ajax)
{
case 'restoreFilesPing':
// ping task - realy does nothing!
$timer = AKFactory::getTimer();
$timer->enforce_min_exec_time();
break;
case 'restoreFilesStart':
AKFactory::nuke(); // Reset the factory
case 'restoreFilesStep':
$config = JFactory::getConfig();
if (version_compare(JVERSION, '3.0', 'ge'))
{
$tmp_path = $config->get('tmp_path', '');
}
else
{
$tmp_path = $config->getValue('tmp_path', '');
}
$override = array(
'rename_dirs' => array('sql' => rtrim($tmp_path, '/\\') . '/sql')
);
$engine = AKFactory::getUnarchiver($override); // Get the engine
$observer = new RestorationObserver(); // Create a new observer
$engine->attach($observer); // Attach the observer
$engine->tick();
$ret = $engine->getStatusArray();
if ($ret['Error'] != '')
{
$retArray['status'] = false;
$retArray['done'] = true;
$retArray['message'] = $ret['Error'];
}
elseif ( !$ret['HasRun'])
{
$retArray['files'] = $observer->filesProcessed;
$retArray['bytesIn'] = $observer->compressedTotal;
$retArray['bytesOut'] = $observer->uncompressedTotal;
$retArray['status'] = true;
$retArray['done'] = true;
}
else
{
$retArray['files'] = $observer->filesProcessed;
$retArray['bytesIn'] = $observer->compressedTotal;
$retArray['bytesOut'] = $observer->uncompressedTotal;
$retArray['status'] = true;
$retArray['done'] = false;
$retArray['factory'] = AKFactory::serialize();
}
break;
case 'restoreFilesFinalize':
$root = AKFactory::get('kickstart.setup.destdir');
// Remove the sql dump directory
$config = JFactory::getConfig();
if (version_compare(JVERSION, '3.0', 'ge'))
{
$tmp_path = $config->get('tmp_path', '');
}
else
{
$tmp_path = $config->getValue('tmp_path', '');
}
$sqldir = rtrim($tmp_path, '/\\') . '/sql';
recursive_remove_directory($sqldir);
break;
}
return $retArray;
break;
case 'dbRestoreStart':
$this->dbRestorationInit();
case 'dbRestore':
$result = $this->dbRestore();
break;
// Unrecognized AJAX task
default:
$result = false;
break;
}
return $result;
}
private function dbRestorationInit()
{
// Get the path to SQL files
$config = JFactory::getConfig();
if (version_compare(JVERSION, '3.0', 'ge'))
{
$path = rtrim($config->get('tmp_path', ''), '/\\') . '/sql';
}
else
{
$path = rtrim($config->getValue('tmp_path', ''), '/\\') . '/sql';
}
$sqlfile = 'site.sql';
// Fallback for SRPs created with older versions of Akeeba Backup
if ( !file_exists($path . '/' . $sqlfile))
{
$sqlfile = 'joomla.sql';
}
$totalSize = 0;
$runSize = 0;
$partsMap = array();
$found = true;
$index = 0;
while ($found)
{
if ($index == 0)
{
$basename = $sqlfile;
}
else
{
$basename = substr($sqlfile, 0, -4) . '.s' . sprintf('%02u', $index);
}
$file = $path . '/' . $basename;
if ( !file_exists($file))
{
$found = false;
break;
}
$filesize = @filesize($file);
$totalSize += intval($filesize);
$partsMap[] = $file;
$index++;
}
$parts = count($partsMap);
$dbInfo = (object)array(
'totalSize' => $totalSize,
'runSize' => $runSize,
'parts' => $parts,
'partsMap' => $partsMap,
'curpart' => 0,
'foffset' => 0,
'file' => null,
'start' => 0
);
$session = JFactory::getSession();
$session->clear('restoration_setup', 'akeeba');
$session->set('dbinfo', $dbInfo, 'akeeba');
}
private function dbGetInfo()
{
$session = JFactory::getSession();
return $session->get('dbinfo', (object)array(), 'akeeba');
}
private function dbSetInfo($dbInfo)
{
$session = JFactory::getSession();
$session->set('dbinfo', $dbInfo, 'akeeba');
}
private function dbGetNextFile()
{
$dbInfo = $this->dbGetInfo();
if ($dbInfo->curpart >= ($dbInfo->parts - 1))
{
return false;
}
$dbInfo->curpart++;
$dbInfo->foffset = 0;
$this->dbSetInfo($dbInfo);
return $this->dbOpenFile();
}
private function dbOpenFile()
{
$dbInfo = $this->dbGetInfo();
$filename = $dbInfo->partsMap[$dbInfo->curpart];
if ( !$dbInfo->file = @fopen($filename, "rt"))
{
$this->setError("Could not open SQL dump file $filename");
return false;
}
else
{
// Get the file size
if (fseek($dbInfo->file, 0, SEEK_END) == 0)
{
$filesize = ftell($dbInfo->file);
}
else
{
$this->setError("Could not determine the file size of the SQL dump file");
return false;
}
}
// Check start and foffset are numeric values
if ( !is_numeric($dbInfo->start) || !is_numeric($dbInfo->foffset))
{
$this->setError("Invalid parameters");
return false;
}
$dbInfo->start = floor($dbInfo->start);
$dbInfo->foffset = floor($dbInfo->foffset);
// Check $foffset upon $filesize
if ($dbInfo->foffset > $filesize)
{
$this->setError("File offset is after EOF");
return false;
}
// Set file pointer to $foffset
if (fseek($dbInfo->file, $dbInfo->foffset) != 0)
{
$this->setError("Can not seek to specified file offset");
return false;
}
$this->dbSetInfo($dbInfo);
return true;
}
private function getTimeRemaining($reset = false, $newmax = 5)
{
static $start = 0;
static $maxAllowed = 5;
if ($reset)
{
$start = microtime(true);
$maxAllowed = $newmax;
}
return $maxAllowed - (microtime(true) - $start);
}
private function dbRestore()
{
$dbInfo = $this->dbGetInfo();
define('DATA_CHUNK_LENGTH', 65536); // How many bytes to read per step
define('MAX_QUERY_LINES', 300); // How many lines may be considered to be one query (except text lines)
$skipMySQLErrorNumbers = array(
1262,
1263,
1264,
1265, // "Data truncated" warning
1266,
1287,
1299
// , 1406 // "Data too long" error
);
$comment = array('#', '-- ', '---', '/*!');
$this->getTimeRemaining(true, 3);
$file = false;
if ( !$this->dbOpenFile())
{
return array('percent' => 0, 'message' => '', 'error' => $this->getError(), 'done' => 0);
}
$db = JFactory::getDBO();
$db->setQuery('SET FOREIGN_KEY_CHECKS = 0');
$db->execute();
$query = "";
$queries = 0;
$dbInfo->totalqueries = empty($dbInfo->totalqueries) ? 0 : $dbInfo->totalqueries;
$dbInfo->linenumber = $dbInfo->start;
$totalsizeread = 0;
$linenumber = 0;
while ($this->getTimeRemaining() > 0)
{
// Read one line (1 line = 1 query)
$query = "";
while ( !feof($dbInfo->file) && (strpos($query, "\n") === false))
{
$query .= fgets($dbInfo->file, DATA_CHUNK_LENGTH);
}
// An empty query is EOF. Are we done or should I skip to the next file?
if (empty($query) || ($query === false))
{
if ($dbInfo->curpart >= ($dbInfo->parts - 1))
{
break;
}
else
{
// Register the bytes read
$current_foffset = @ftell($dbInfo->file);
$dbInfo->runSize += $current_foffset - $dbInfo->foffset;
// Get the next file
if ( !$this->dbGetNextFile())
{
return array('percent' => 0, 'message' => '', 'error' => $this->getError(), 'done' => 0);
}
// Rerun the fetcher
continue;
}
}
if (substr($query, -1) != "\n")
{
// WTF? We read more data than we should?! Roll back the file
$rollback = strlen($query) - strpos($query, "\n");
fseek($dbInfo->file, -$rollback, SEEK_CUR);
// And chop the line
$query = substr($query, 0, $rollback);
}
// Handle DOS linebreaks
$query = str_replace("\r\n", "\n", $query);
$query = str_replace("\r", "\n", $query);
// Skip comments and blank lines only if NOT in parents
$skipline = false;
reset($comment);
foreach ($comment as $comment_value)
{
if (trim($query) == "" || strpos($query, $comment_value) === 0)
{
$skipline = true;
break;
}
}
if ($skipline)
{
$linenumber++;
continue;
}
$query = trim($query, " \n");
$query = rtrim($query, ';');
// CREATE TABLE query pre-processing
$replaceAll = false;
$changeEncoding = false;
if (substr($query, 0, 12) == 'CREATE TABLE')
{
// Yes, try to get the table name
$restOfQuery = trim(substr($query, 12, strlen($query) - 12)); // Rest of query, after CREATE TABLE
// Is there a backtick?
if (substr($restOfQuery, 0, 1) == '`')
{
// There is... Good, we'll just find the matching backtick
$pos = strpos($restOfQuery, '`', 1);
$tableName = substr($restOfQuery, 1, $pos - 1);
}
else
{
// Nope, let's assume the table name ends in the next blank character
$pos = strpos($restOfQuery, ' ', 1);
$tableName = substr($restOfQuery, 1, $pos - 1);
}
unset($restOfQuery);
// Try to drop the table anyway
$dropQuery = 'DROP TABLE IF EXISTS `' . $tableName . '`;';
$db->setQuery(trim($dropQuery), false);
if ( !$db->execute())
{
$this->setError($db->getError());
return array('percent' => 0, 'message' => '', 'error' => $this->getError(), 'done' => 0);
}
}
else
// CREATE VIEW query pre-processing
// In any case, drop the view before attempting to create it. (Views can't be renamed)
if ((substr($query, 0, 7) == 'CREATE ') && (strpos($query, ' VIEW ') !== false))
{
// Yes, try to get the view name
$view_pos = strpos($query, ' VIEW ');
$restOfQuery = trim(substr($query, $view_pos + 6)); // Rest of query, after VIEW string
// Is there a backtick?
if (substr($restOfQuery, 0, 1) == '`')
{
// There is... Good, we'll just find the matching backtick
$pos = strpos($restOfQuery, '`', 1);
$tableName = substr($restOfQuery, 1, $pos - 1);
}
else
{
// Nope, let's assume the table name ends in the next blank character
$pos = strpos($restOfQuery, ' ', 1);
$tableName = substr($restOfQuery, 1, $pos - 1);
}
unset($restOfQuery);
// Try to drop the view anyway
$dropQuery = 'DROP VIEW IF EXISTS `' . $tableName . '`;';
$db->setQuery(trim($dropQuery), false);
if ( !$db->execute())
{
$this->setError($db->getError());
return array('percent' => 0, 'message' => '', 'error' => $this->getError(), 'done' => 0);
}
}
if ( !empty($query))
{
$db->setQuery(trim($query));
if ( !$db->execute())
{
// Skip over errors we can safely ignore...
if (in_array($db->errno, $skipMySQLErrorNumbers))
{
continue;
}
$this->setError($db->getError());
return array('percent' => 0, 'message' => '', 'error' => $this->getError(), 'done' => 0);
}
}
$totalsizeread += strlen($query);
$dbInfo->totalqueries++;
$queries++;
$query = "";
$linenumber++;
}
// Get the current file position
$current_foffset = ftell($dbInfo->file);
if ($current_foffset === false)
{
if ($dbInfo->file)
{
fclose($dbInfo->file);
}
$this->setError("Can not read file pointer");
return array('percent' => 0, 'message' => '', 'error' => $this->getError(), 'done' => 0);
}
else
{
$dbInfo->runSize += $current_foffset - $dbInfo->foffset;
$dbInfo->foffset = $current_foffset;
}
// Return statistics
if ($dbInfo->totalSize < 1)
{
$pct_done = 0;
}
else
{
$pct_done = ceil($dbInfo->runSize / $dbInfo->totalSize * 100);
}
$bytes_done = $dbInfo->runSize;
$bytes_tota = $dbInfo->totalSize;
$bytes_togo = $dbInfo->totalSize - $dbInfo->runSize;
$kbytes_done = round($bytes_done / 1024, 2);
$kbytes_tota = round($bytes_tota / 1024, 2);
// Check for global EOF
if (($dbInfo->curpart >= ($dbInfo->parts - 1)) && feof($dbInfo->file))
{
$bytes_togo = 0;
}
if ($bytes_togo == 0)
{
// Clear stored variables if we're finished
$lines_togo = '0';
$lines_tota = $linenumber - 1;
$queries_togo = '0';
$queries_tota = $dbInfo->totalqueries;
}
else
{
$this->dbSetInfo($dbInfo);
}
// Close the file
if ($dbInfo->file)
{
fclose($dbInfo->file);
}
// Return meaningful data to AJAX
$ret = array(
'percent' => $pct_done,
'message' => 'Restored ' . sprintf('%1.2f', $kbytes_done) . ' of ' . sprintf('%1.2f', $kbytes_tota) . ' Kilobytes (' .
sprintf('%02.1f', $pct_done) . ' %)',
'error' => '',
'done' => ($bytes_togo == 0) ? '1' : '0'
);
return $ret;
}
}
function akeebaTinyHackForRestorationObserver()
{
// The observer class, used to report number of files and bytes processed
class RestorationObserver extends AKAbstractPartObserver
{
public $compressedTotal = 0;
public $uncompressedTotal = 0;
public $filesProcessed = 0;
public function update($object, $message)
{
if ( !is_object($message))
{
return;
}
if ( !array_key_exists('type', get_object_vars($message)))
{
return;
}
if ($message->type == 'startfile')
{
$this->filesProcessed++;
$this->compressedTotal += $message->content->compressed;
$this->uncompressedTotal += $message->content->uncompressed;
}
}
public function __toString()
{
return __CLASS__;
}
}
}