Current File : /home/pacjaorg/www/nsa/administrator/components/com_akeebabackup/src/Model/UploadModel.php |
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Component\AkeebaBackup\Administrator\Model;
defined('_JEXEC') || die;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use Exception;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Factory as JoomlaFactory;
use Joomla\CMS\MVC\Model\BaseModel;
use LogicException;
#[\AllowDynamicProperties]
class UploadModel extends BaseModel
{
/**
* Upload an archive part to remote storage
*
* @param int $id Backup record ID
* @param int $part Part to upload. 0 is the first part (e.g. .jpa), 1 the second (e.g. .j01) etc.
* @param int $frag Fragment (chunk) of the part to upload. The first fragment is 0.
*
* @return bool True if the upload of this file is done, false if more work is necessary
*
* @throws Exception
*/
public function upload(int $id, int $part, int $frag)
{
// Initialize
/** @var CMSApplication $app */
$app = JoomlaFactory::getApplication();
$stat = Platform::getInstance()->get_statistics($id);
$returnStatus = false;
$remote_filename = null;
// The number of parts must be AT LEAST 1. If it's 0 it mean no split archive, i.e. a single part.
$stat['multipart'] = max($stat['multipart'], 1);
// Load the Factory
$savedFactory = $app->getSession()->get('akeebabackup.upload_factory', null);
$logger = Factory::getLog();
if ($savedFactory)
{
Factory::unserialize($savedFactory);
Platform::getInstance()->load_configuration($stat['profile_id']);
$logger->open('backend');
$logger->info(sprintf(
'Continuing transfer of backup record #%d (part %d, fragment %d)',
$id, $part, $frag
));
}
else
{
Platform::getInstance()->load_configuration($stat['profile_id']);
$logger->reset('backend');
$logger->info(sprintf(
"Starting transfer of the files of backup record #%d to remote storage using post-processing engine %s.",
$id,
Factory::getConfiguration()->get('akeeba.advanced.postproc_engine')));
}
// Load the post-processing engine
$config = Factory::getConfiguration();
$engine_name = $config->get('akeeba.advanced.postproc_engine');
$engine = Factory::getPostprocEngine($engine_name);
// Get the timer and reset it
$timer = Factory::getTimer();
$timer->resetTime();
while ($timer->getTimeLeft() > 0.01)
{
// Start counting the time for this part
$startTime = $timer->getRunningTime();
// Calculate the filenames
$local_filename = $stat['absolute_path'];
$basename = basename($local_filename);
$extension = strtolower(str_replace(".", "", strrchr($basename, ".")));
$new_extension = $extension;
if ($part > 0)
{
$new_extension = substr($extension, 0, 1) . sprintf('%02u', $part);
}
$local_filename = substr($local_filename, 0, -strlen($extension)) . $new_extension;
// Start uploading
try
{
$result = $engine->processPart($local_filename);
}
catch (Exception $e)
{
$app->getSession()->remove('akeebabackup.upload_factory');
throw $e;
}
if (!is_bool($result))
{
throw new LogicException(sprintf("Unexpected result from %s: %s", get_class($engine), print_r($result, true)));
}
// Stop the running timer
$endTime = $timer->getRunningTime();
$elapsedTime = $endTime - $startTime;
// What is the next part and frag to upload?
$frag++;
if ($result === true)
{
$part++;
$frag = 0;
}
// Calculate the remote filename
$remote_filename = $config->get('akeeba.advanced.postproc_engine', '') . '://';
$remote_filename .= $engine->getRemotePath();
/**
* Am I already finished?
*
* Parts are uploaded in the order 0, 1, 2, ... Part 0 is the .jpa/.jps/.zip file. Part 1 is .j01/.z01 and
* so forth.
*
* $stat['multipart'] contains the total number of parts. Let's say it's 5. This means that we need to
* upload parts 0, 1, 2, 3 and 4. When $part is 5 (or greater, even though that'd be a bug) we must stop
* at one.
*
* In the edge case where $stat['multipart'] is 1 the same thing applies. The only part we must transfer is
* 0. If $part is 1 we're done.
*
* Therefore the condition for checking whether we're all done is that $part is greater than zero (we have
* already uploaded the first and possibly only part) AND $part is greater than OR EQUAL to the total number
* of parts in the backup archive set.
*/
if (($part >= 0) && ($part >= $stat['multipart']))
{
Factory::getLog()->info(sprintf(
'Finished transfer of backup record #%d',
$id
));
// Update stats with remote filename
$data = [
'remote_filename' => $remote_filename,
];
Platform::getInstance()->set_or_update_statistics($id, $data);
// Indicate that we're all done
$returnStatus = true;
break;
}
// Do I have enough time for another fragment's upload?
if ($timer->getTimeLeft() < (2.0 * $elapsedTime))
{
break;
}
$logger->info(sprintf(
'Continuing transfer of backup record #%d (part %d, fragment %d)',
$id, $part, $frag
));
}
// Serialize the factory
$app->getSession()->set('akeebabackup.upload_factory', $returnStatus ? null : Factory::serialize());
// Should I tell the user that we have more work to do?
if (!$returnStatus)
{
Factory::getLog()->info(sprintf(
'The transfer of backup record #%d will continue on the next step',
$id
));
}
// Update the Model state
$this->setState('id', $id);
$this->setState('part', $part);
$this->setState('frag', $frag);
$this->setState('stat', $stat);
$this->setState('remotename', $remote_filename);
$timer->enforce_min_exec_time();
return $returnStatus;
}
}