first commit

This commit is contained in:
2024-07-15 11:28:08 +02:00
commit f52d538ea5
21891 changed files with 6161164 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Controller;
use Awf\Text\Text;
class Alice extends ControllerDefault
{
public function ajax()
{
/** @var \Solo\Model\Alice $model */
$model = $this->getModel();
$model->setState('ajax', $this->input->get('ajax', '', 'cmd'));
$model->setState('log', $this->input->get('log', '', 'cmd'));
$ret_array = $model->runAnalysis();
@ob_end_clean();
header('Content-type: text/plain');
echo '###' . json_encode($ret_array) . '###';
flush();
$this->container->application->close();
}
public function domains()
{
$return = array();
$domains = \AliceUtilScripting::getDomainChain();
foreach($domains as $domain)
{
$return[] = array($domain['domain'], $domain['name']);
}
@ob_end_clean();
header('Content-type: text/plain');
echo '###'.json_encode($return).'###';
flush();
$this->container->application->close();
}
/**
* Translates language key in English strings
*/
public function translate()
{
$return = array();
$strings = $this->input->getString('keys', '');
$strings = json_decode($strings);
// Text always loads all the languages, so we have to convince him very hard to do what we want
// First of all let's empty the $strings variable
$property = new \ReflectionProperty('\Awf\Text\Text', 'strings');
$property->setAccessible(true);
$property->setValue(array());
// Then load only the English language
Text::loadLanguage('en-GB', 'akeebabackup', '.com_akeebabackup.ini', false, $this->container->languagePath);
Text::loadLanguage('en-GB', 'akeeba', '.com_akeeba.ini', false, $this->container->languagePath);
foreach ($strings as $string)
{
$temp['check'] = Text::_($string->check);
// If I have an array, it means that I have to use sprintf to translate the error
if (is_array($string->error))
{
$trans[] = Text::_($string->error[0]);
$args = array_merge($trans, array_slice($string->error, 1));
$error = call_user_func_array('sprintf', $args);
}
else
{
$error = Text::_($string->error);
}
$temp['error'] = $error;
$return[] = $temp;
}
@ob_end_clean();
header('Content-type: text/plain');
echo '###' . json_encode($return) . '###';
flush();
$this->container->application->close();
}
}

View File

@@ -0,0 +1,145 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
use Akeeba\Engine\Platform;
use Solo\Helper\Utils;
/**
* The controller for the Backup view
*/
class Backup extends ControllerDefault
{
/**
* Default task; shows the initial page where the user selects a profile
* and enters description and comment
*
* @return void
*/
public function main()
{
// Push models to view
$model = $this->getModel();
$model->setState('returnform', $this->input->get('returnform', '', 'raw'));
$newProfile = (int)$this->input->get('profile', -10, 'int');
// Apply the CSRF protection if we're switching profile or passing variables for POST redirection
if ($newProfile > 0 || $model->getState('returnform', ''))
{
$this->csrfProtection(true);
$this->applyProfile();
}
$srpinfo = array(
'tag' => $this->input->get('tag', 'backend', 'cmd'),
'type' => $this->input->get('type', '', 'cmd'),
'name' => $this->input->get('name', '', 'cmd'),
'group' => $this->input->get('group', '', 'cmd'),
'customdirs' => $this->input->get('customdirs', array(), 'array'),
'extraprefixes' => $this->input->get('extraprefixes', array(), 'array'),
'customtables' => $this->input->get('customtables', array(), 'array'),
'skiptables' => $this->input->get('skiptables', array(), 'array'),
'xmlname' => $this->input->get('xmlname', '', 'string')
);
// Sanitize the return URL
$returnUrl = $this->input->get('returnurl', '', 'raw');
$returnUrl = Utils::safeDecodeReturnUrl($returnUrl);
$model->setState('srpinfo', $srpinfo);
$model->setState('description', $this->input->get('description', null, 'raw'));
$model->setState('comment', $this->input->get('comment', null, 'raw'));
$model->setState('jpskey', $this->input->get('jpskey', '', 'raw'));
$model->setState('angiekey', $this->input->get('angiekey', '', 'raw'));
$model->setState('returnurl', $returnUrl);
$model->setState('backupid', $this->input->get('backupid', null, 'cmd'));
$this->display();
}
/**
* Handle an AJAX request
*
* @return void
*/
public function ajax()
{
$model = $this->getModel();
$model->setState('profile', $this->input->get('profile', Platform::getInstance()->get_active_profile(), 'int'));
$model->setState('ajax', $this->input->get('ajax', '', 'cmd'));
$model->setState('description', $this->input->get('description', '', 'raw'));
$model->setState('comment', $this->input->get('comment', '','raw'));
$model->setState('jpskey', $this->input->get('jpskey', '', 'raw'));
$model->setState('angiekey', $this->input->get('angiekey', '', 'raw'));
$model->setState('backupid', $this->input->get('backupid', null, 'cmd'));
$model->setState('errorMessage', $this->input->get('errorMessage', '', 'string'));
$model->setState('tag', $this->input->get('tag', 'backend', 'cmd'));
$model->setState('type', strtolower($this->input->get('type', '', 'cmd')));
$model->setState('name', strtolower($this->input->get('name', '', 'cmd')));
$model->setState('group', strtolower($this->input->get('group', '', 'cmd')));
$model->setState('customdirs', $this->input->get('customdirs', array(),'array'));
$model->setState('customfiles', $this->input->get('customfiles', array(),'array'));
$model->setState('extraprefixes', $this->input->get('extraprefixes', array(),'array'));
$model->setState('customtables', $this->input->get('customtables', array(),'array'));
$model->setState('skiptables', $this->input->get('skiptables', array(),'array'));
$model->setState('langfiles', $this->input->get('langfiles', array(),'array'));
$model->setState('xmlname', $this->input->getString('xmlname', ''));
define('AKEEBA_BACKUP_ORIGIN', $this->input->get('tag', 'backend', 'cmd'));
$ret_array = $model->runBackup();
@ob_end_clean();
header('Content-type: text/plain');
header('Connection: close');
echo '#"\#\"#' . json_encode($ret_array) . '#"\#\"#';
flush();
$this->container->application->close();
}
/**
* Applies a profile change based on the request's "profile" parameter
*/
private function applyProfile()
{
// Get the currently active profile
$current_profile = Platform::getInstance()->get_active_profile();
// Get the profile from the request
$profile = (int)$this->input->get('profile', $current_profile, 'int');
// Sanity check
if (!is_numeric($profile) || ($profile <= 0))
{
$profile = $current_profile;
}
// Change and reload the profile if necessary
if ($profile != $current_profile)
{
$session = \Awf\Application\Application::getInstance()->getContainer()->segment;
$session->profile = $profile;
/**
* DO NOT REMOVE!
*
* The Model will only try to load the configuration after nuking the factory. This causes Profile 1 to be
* loaded first. Then it figures out it needs to load a different profile and it does but the protected keys
* are NOT replaced, meaning that certain configuration parameters are not replaced. Most notably, the chain.
* This causes backups to behave weirdly. So, DON'T REMOVE THIS UNLESS WE REFACTOR THE MODEL.
*/
Platform::getInstance()->load_configuration($profile);
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
class Browser extends ControllerDefault
{
/**
* Handle the directory listing display
*/
public function main()
{
$folder = $this->input->getString('folder', '');
$processfolder = $this->input->getInt('processfolder', 0);
/** @var \Solo\Model\Browser $model */
$model = $this->getModel();
$model->setState('folder', $folder);
$model->setState('processfolder', $processfolder);
$model->makeListing();
parent::display();
}
}

View File

@@ -0,0 +1,90 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Controller;
use Akeeba\Engine\Platform;
use Awf\Application\Application;
use Awf\Mvc\Model;
use Awf\Text\Text;
class Check extends ControllerDefault
{
public function execute($task)
{
$this->checkPermissions();
define('AKEEBA_BACKUP_ORIGIN', 'frontend');
return parent::execute('main');
}
public function main()
{
$cpanelModel = Model::getInstance('Solo', 'Main', $this->container);
$result = $cpanelModel->notifyFailed();
$message = $result['result'] ? '200 ' : '500 ';
$message .= implode(', ', $result['message']);
@ob_end_clean();
header('Content-type: text/plain');
header('Connection: close');
echo $message;
flush();
$this->container->application->close();
}
/**
* Check that the user has sufficient permissions, or die in error
*
*/
private function checkPermissions()
{
// Is frontend backup enabled?
$febEnabled = Platform::getInstance()->get_platform_configuration_option('frontend_enable', 0);
$febEnabled = in_array($febEnabled, array('on', 'checked', 'true', 1, 'yes'));
$validKey = Platform::getInstance()->get_platform_configuration_option('frontend_secret_word', '');
if (!\Akeeba\Engine\Util\Complexify::isStrongEnough($validKey, false))
{
$febEnabled = false;
}
$validKeyTrim = trim($validKey);
if (!$febEnabled || empty($validKey))
{
throw new \RuntimeException(Text::_('SOLO_REMOTE_ERROR_NOT_ENABLED'), 403);
}
// Is the key good?
$key = $this->input->get('key', '', 'none', 2);
if (($key != $validKey) || (empty($validKeyTrim)))
{
throw new \RuntimeException(Text::_('SOLO_REMOTE_ERROR_INVALID_KEY'), 403);
}
}
private function setProfile()
{
// Set profile
$profile = $this->input->get('profile', 1, 'int');
if (empty($profile))
{
$profile = 1;
}
$session = Application::getInstance()->getContainer()->segment;
$session->profile = $profile;
Platform::getInstance()->load_configuration($profile);
}
}

View File

@@ -0,0 +1,271 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
use Awf\Text\Text;
use Solo\Model\Profiles;
/**
* The Controller for the Configuration view
*/
class Configuration extends ControllerDefault
{
/**
* Handle the apply task which saves settings and shows the editor again
*
* @return void
*/
public function apply()
{
// CSRF prevention
$this->csrfProtection();
// Get the var array from the request
$data = $this->input->get('var', array(), 'array');
// Mark this profile as configured
$data['akeeba.flag.confwiz'] = 1;
/** @var \Solo\Model\Configuration $model */
$model = $this->getModel();
$model->setState('engineconfig', $data);
$model->saveEngineConfig();
// Finally, save the profile description if it has changed
$profileID = \Akeeba\Engine\Platform::getInstance()->get_active_profile();
// Get profile name
/** @var Profiles $profileRecord */
$profileRecord = $this->getModel('Profiles')->getClone()->setIgnoreRequest(1);
$profileRecord->reset(true, true)->find($profileID);
$oldProfileName = $profileRecord->description;
$oldQuickIcon = $profileRecord->quickicon;
$newProfileName = $this->input->getString('profilename', null);
$newProfileName = trim($newProfileName);
$newQuickIcon = $this->input->getCmd('quickicon', '');
$newQuickIcon = !empty($newQuickIcon);
$mustSaveProvile = !empty($newProfileName) && ($newProfileName != $oldProfileName);
$mustSaveProvile = $mustSaveProvile || ($newQuickIcon != $oldQuickIcon);
if ($mustSaveProvile)
{
$profileRecord->save(array(
'description' => $newProfileName,
'quickicon' => $newQuickIcon,
));
}
$router = $this->container->router;
$this->setRedirect($router->route('index.php?view=configuration'), Text::_('COM_AKEEBA_CONFIG_SAVE_OK'));
}
/**
* Handle the save task which saves settings and returns to the main page
*
* @return void
*/
public function save()
{
$this->apply();
$router = $this->container->router;
$this->setRedirect($router->route('index.php?view=main'), Text::_('COM_AKEEBA_CONFIG_SAVE_OK'));
}
/**
* Handle the save task which saves settings, creates a new backup profile, activates it and proceed to the
* configuration page once more.
*
* @return void
*/
public function savenew()
{
// Save the current profile
$this->apply();
// Create a new profile
/** @var Profiles $profileModel */
$profileModel = $this->getModel('Profiles')->getClone();
$profileID = \Akeeba\Engine\Platform::getInstance()->get_active_profile();
$profileModel->find($profileID);
$profileModel->id = null;
$profileModel->save(array(
'id' => 0,
'description' => Text::_('COM_AKEEBA_CONFIG_SAVENEW_DEFAULT_PROFILE_NAME')
));
$newProfileId = (int)($profileModel->getId());
// Activate and edit the new profile
$returnUrl = base64_encode($this->redirect);
$router = $this->container->router;
$token = $this->container->session->getCsrfToken()->getValue();
$url = $router->route('index.php?view=main&task=switchProfile&profile=' . $newProfileId .
'&returnurl=' . $returnUrl . '&' . $token . '=1');
$this->setRedirect($url);
}
/**
* Handle the cancel task which doesn't save anything and returns to the cpanel
*
* @return void
*/
public function cancel()
{
$this->csrfProtection();
$router = $this->container->router;
$this->setRedirect($router->route('index.php?view=main'));
}
/**
* Tests the validity of the FTP connection details
*
* @return void
*/
public function testftp()
{
/** @var \Solo\Model\Configuration $model */
$model = $this->getModel();
$model->setState('isCurl', $this->input->get('isCurl', 0, 'int'));
$model->setState('host', $this->input->get('host', '', 'raw'));
$model->setState('port', $this->input->get('port', 21, 'int'));
$model->setState('user', $this->input->get('user', '', 'raw'));
$model->setState('pass', $this->input->get('pass', '', 'raw'));
$model->setState('initdir', $this->input->get('initdir', '', 'raw'));
$model->setState('usessl', $this->input->get('usessl', '', 'raw') == 'true');
$model->setState('passive', $this->input->get('passive', '', 'raw') == 'true');
$model->setState('passive_mode_workaround', $this->input->get('passive_mode_workaround', '', 'raw') == 'true');
$result = true;
try
{
$model->testFTP();
}
catch (\Exception $e)
{
$result = $e->getMessage();
}
@ob_end_clean();
echo '#"\#\"#' . json_encode($result) . '#"\#\"#';
flush();
$this->container->application->close();
}
/**
* Tests the validity of the SFTP connection details
*
* @return void
*/
public function testsftp()
{
/** @var \Solo\Model\Configuration $model */
$model = $this->getModel();
$model->setState('isCurl', $this->input->get('isCurl', 0, 'int'));
$model->setState('host', $this->input->get('host', '', 'raw'));
$model->setState('port', $this->input->get('port', 21, 'int'));
$model->setState('user', $this->input->get('user', '', 'raw'));
$model->setState('pass', $this->input->get('pass', '', 'raw'));
$model->setState('privkey', $this->input->get('privkey', '', 'raw'));
$model->setState('pubkey', $this->input->get('pubkey', '', 'raw'));
$model->setState('initdir', $this->input->get('initdir', '', 'raw'));
$result = true;
try
{
$model->testSFTP();
}
catch (\Exception $e)
{
$result = $e->getMessage();
}
@ob_end_clean();
echo '#"\#\"#' . json_encode($result) . '#"\#\"#';
flush();
$this->container->application->close();
}
/**
* Opens an OAuth window for the selected data processing engine
*
* @return void
*/
public function dpeoauthopen()
{
/** @var \Solo\Model\Configuration $model */
$model = $this->getModel();
$model->setState('engine', $this->input->get('engine', '', 'raw'));
$model->setState('params', $this->input->get('params', array(), 'array'));
@ob_end_clean();
$model->dpeOAuthOpen();
flush();
$this->container->application->close();
}
/**
* Runs a custom API call against the selected data processing engine
*
* @return void
*/
public function dpecustomapi()
{
/** @var \Solo\Model\Configuration $model */
$model = $this->getModel();
$model->setState('engine', $this->input->get('engine', '', 'raw'));
$model->setState('method', $this->input->getVar('method', '', 'raw'));
$model->setState('params', $this->input->get('params', array(), 'array'));
@ob_end_clean();
echo '#"\#\"#' . json_encode($model->dpeCustomAPICall()) . '#"\#\"#';
flush();
$this->container->application->close();
}
/**
* Runs a custom API call against the selected data processing engine
*
* @return void
*/
public function dpecustomapiraw()
{
/** @var \Solo\Model\Configuration $model */
$model = $this->getModel();
$model->setState('engine', $this->input->get('engine', '', 'raw'));
$model->setState('method', $this->input->getVar('method', '', 'raw'));
$model->setState('params', $this->input->get('params', array(), 'array'));
@ob_end_clean();
echo $model->dpeCustomAPICall();
flush();
$this->container->application->close();
}
}

View File

@@ -0,0 +1,115 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
use Awf\Text\Text;
/**
* Common controller superclass. Reserved for future use.
*/
abstract class ControllerDefault extends \Awf\Mvc\Controller
{
protected $aclChecks = array(
'alice' => array('*' => array('configure')),
'backup' => array('*' => array('backup')),
'browser' => array('*' => array('configure')),
'configuration' => array('*' => array('configure')),
'dbfilters' => array('*' => array('configure')),
'discover' => array('*' => array('configure')),
'errortest' => array('*' => array('configure')),
'extradirs' => array('*' => array('configure')),
'fsfilters' => array('*' => array('configure')),
'log' => array('*' => array('configure')),
'manage' => array(
'manage' => array(),
'showComment' => array('backup'),
'cancel' => array('backup'),
'download' => array('download'),
'restore' => array('configure'),
'*' => array('download'),
),
'multidb' => array('*' => array('configure')),
'phpinfo' => array('*' => array('configure', 'backup', 'download')),
'profiles' => array('*' => array('configure')),
'profile' => array('*' => array('configure')),
'regexdbfilters' => array('*' => array('configure')),
'regexfsfilters' => array('*' => array('configure')),
'remotefiles' => array('*' => array('download')),
'restore' => array('*' => array('configure')),
's3import' => array('*' => array('configure')),
'schedule' => array('*' => array('configure')),
'sysconfig' => array('*' => array('configure', 'backup', 'download')),
'transfer' => array('*' => array('download')),
'update' => array('*' => array('configure', 'backup', 'download')),
'upload' => array('*' => array('backup')),
'users' => array('*' => array('configure', 'backup', 'download')),
'wizard' => array('*' => array('configure')),
);
/**
* Executes a given controller task. The onBefore<task> and onAfter<task>
* methods are called automatically if they exist.
*
* @param string $task The task to execute, e.g. "browse"
*
* @return null|bool False on execution failure
*
* @throws \Exception When the task is not found
*/
public function execute($task)
{
$view = $this->input->getCmd('view', 'main');
$this->aclCheck($view, $task);
return parent::execute($task);
}
/**
* Performs automatic access control checks
*
* @param string $view The view being accessed
* @param string $task The task being accessed
*
* @throws \RuntimeException
*/
protected function aclCheck($view, $task)
{
$view = strtolower($view);
$task = strtolower($task);
if (!isset($this->aclChecks[$view]))
{
return;
}
if (!isset($this->aclChecks[$view][$task]))
{
if (!isset($this->aclChecks[$view]['*']))
{
return;
}
$requiredPrivileges = $this->aclChecks[$view]['*'];
}
else
{
$requiredPrivileges = $this->aclChecks[$view][$task];
}
$user = $this->container->userManager->getUser();
foreach ($requiredPrivileges as $privilege)
{
if (!$user->getPrivilege('akeeba.' . $privilege))
{
throw new \RuntimeException(Text::_('SOLO_ERR_ACLDENIED'), 403);
}
}
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
use Awf\Mvc\DataController;
use Awf\Text\Text;
/**
* Common controller superclass. Reserved for future use.
*/
abstract class DataControllerDefault extends DataController
{
protected $aclChecks = array(
'alice' => array('*' => array('configure')),
'backup' => array('*' => array('backup')),
'browser' => array('*' => array('configure')),
'configuration' => array('*' => array('configure')),
'dbfilters' => array('*' => array('configure')),
'discover' => array('*' => array('configure')),
'extradirs' => array('*' => array('configure')),
'fsfilters' => array('*' => array('configure')),
'log' => array('*' => array('configure')),
'manage' => array(
'manage' => array(),
'showComment' => array('backup'),
'cancel' => array('backup'),
'download' => array('download'),
'restore' => array('configure'),
'*' => array('download'),
),
'multidb' => array('*' => array('configure')),
'profiles' => array('*' => array('configure')),
'profile' => array('*' => array('configure')),
'regexdbfilters' => array('*' => array('configure')),
'regexfsfilters' => array('*' => array('configure')),
'remotefiles' => array('*' => array('download')),
'restore' => array('*' => array('configure')),
's3import' => array('*' => array('configure')),
'schedule' => array('*' => array('configure')),
'sysconfig' => array('*' => array('configure', 'backup', 'download')),
'transfer' => array('*' => array('download')),
'update' => array('*' => array('configure', 'backup', 'download')),
'upload' => array('*' => array('backup')),
'users' => array('*' => array('configure', 'backup', 'download')),
'wizard' => array('*' => array('configure')),
);
public function execute($task)
{
$view = $this->input->getCmd('view', 'main');
$this->aclCheck($view, $task);
return parent::execute($task);
}
protected function aclCheck($view, $task)
{
$view = strtolower($view);
$task = strtolower($task);
if (!isset($this->aclChecks[$view]))
{
return;
}
if (!isset($this->aclChecks[$view][$task]))
{
if (!isset($this->aclChecks[$view]['*']))
{
return;
}
$requiredPrivileges = $this->aclChecks[$view]['*'];
}
else
{
$requiredPrivileges = $this->aclChecks[$view][$task];
}
$user = $this->container->userManager->getUser();
foreach ($requiredPrivileges as $privilege)
{
if (!$user->getPrivilege('akeeba.' . $privilege))
{
throw new \RuntimeException(Text::_('SOLO_ERR_ACLDENIED'), 403);
}
}
}
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
class Dbfilters extends ControllerDefault
{
public function __construct($config = array())
{
parent::__construct($config);
// Register the two additional tasks
$this->registerTask('normal', 'main');
$this->registerTask('tabular', 'main');
}
/**
* Default task
*
* @return void
*/
public function main()
{
$task = $this->input->getCmd('task', 'normal');
if ($task == 'main')
{
$task = 'normal';
}
$this->getModel()->setState('browse_task', $task);
$this->display();
}
/**
* AJAX proxy method
*
* @return void
*/
public function ajax()
{
// Parse the JSON data and reset the action query param to the resulting array
$action_json = $this->input->get('akaction', '', 'raw');
$action = json_decode($action_json);
/** @var \Solo\Model\Dbfilters $model */
$model = $this->getModel();
$model->setState('action', $action);
$ret = $model->doAjax();
@ob_end_clean();
echo '#"\#\"#' . json_encode($ret) . '#"\#\"#';
flush();
$this->container->application->close();
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
class Errortest extends ControllerDefault
{
public function main()
{
throw new \RuntimeException('I am a runtime exception with error code 500', 500);
}
public function notfound()
{
throw new \RuntimeException('I am a runtime exception with error code 404 Not Found', 404);
}
public function fatal()
{
kalimera();
}
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
class Fsfilters extends ControllerDefault
{
public function __construct($config = array())
{
parent::__construct($config);
// Register the two additional tasks
$this->registerTask('normal', 'main');
$this->registerTask('tabular', 'main');
}
/**
* Default task
*
* @return void
*/
public function main()
{
$task = $this->input->getCmd('task', 'normal');
if ($task == 'main')
{
$task = 'normal';
}
$this->getModel()->setState('browse_task', $task);
$this->display();
}
/**
* AJAX proxy method
*
* @return void
*/
public function ajax()
{
// Parse the JSON data and reset the action query param to the resulting array
$action_json = $this->input->get('akaction', '', 'raw');
$action = json_decode($action_json);
/** @var \Solo\Model\Fsfilters $model */
$model = $this->getModel();
$model->setState('action', $action);
$ret = $model->doAjax();
@ob_end_clean();
echo '#"\#\"#' . json_encode($ret) . '#"\#\"#';
flush();
$this->container->application->close();
}
}

View File

@@ -0,0 +1,53 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
/**
* The controller for FTP browser
*/
class Ftpbrowser extends ControllerDefault
{
public function execute($task)
{
// If we are running inside a CMS but there is no active user we have to throw a 403
$inCMS = $this->container->segment->get('insideCMS', false);
if ($inCMS && !$this->container->userManager->getUser()->getId())
{
return false;
}
return parent::execute($task);
}
public function main()
{
/** @var \Solo\Model\Ftpbrowser $model */
$model = $this->getModel();
// Grab the data and push them to the model
$model->setState('host', $this->input->getString('host', ''));
$model->setState('port', $this->input->getInt('port', 21));
$model->setState('passive', $this->input->getInt('passive', 1));
$model->setState('ssl', $this->input->getInt('ssl', 0));
$model->setState('username', $this->input->getRaw('username', ''));
$model->setState('password', $this->input->getRaw('password', ''));
$model->setState('directory', $this->input->getRaw('directory', ''));
$ret = $model->doBrowse();
@ob_end_clean();
echo '#"\#\"#'.json_encode($ret).'#"\#\"#';
flush();
$this->container->application->close();
}
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Controller;
class Json extends ControllerDefault
{
/**
* Always execute the 'json' task
*
* @param string $task
*
* @return boolean|null
*/
public function execute($task)
{
$this->input->set('task', 'json');
$task = 'json';
return parent::execute($task);
}
/**
* Handles API calls
*/
public function json()
{
// Use the model to parse the JSON message
if (function_exists('ob_start'))
{
@ob_start();
}
$sourceJSON = $this->input->get('json', null, 'raw');
// On some !@#$%^& servers where magic_quotes_gpc is On we might get extra slashes added
if (function_exists('get_magic_quotes_gpc'))
{
if (get_magic_quotes_gpc())
{
$sourceJSON = stripslashes($sourceJSON);
}
}
/** @var \Solo\Model\Json $model */
$model = $this->getModel();
$json = $model->execute($sourceJSON);
if (function_exists('ob_end_clean'))
{
@ob_end_clean();
}
// Just dump the JSON and tear down the application, without plugins executing
header('Content-type: text/plain');
header('Connection: close');
echo $json;
$this->container->application->close();
}
}

View File

@@ -0,0 +1,145 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
class Log extends ControllerDefault
{
/**
* Executes a given controller task. The onBefore<task> and onAfter<task>
* methods are called automatically if they exist.
*
* This method is overridden to add support for the profileid query parameter which switches the active
* backup profile.
*
* @param string $task The task to execute, e.g. "browse"
*
* @return null|bool False on execution failure
*
* @throws \Exception When the task is not found
*/
public function execute($task)
{
// If the profile_id parameter is defined and it's a positive integer change the active profile
$profile_id = $this->input->getInt('profileid', null);
if (!empty($profile_id) && is_numeric($profile_id) && ($profile_id > 0))
{
\Awf\Application\Application::getInstance()->getContainer()->segment->profile = $profile_id;
}
// Execute the controller
return parent::execute($task);
}
/**
* Allows the user to select the log origin to display or display the log file itself
*
* @return void
*/
public function main()
{
$tag = $this->input->get('tag', null, 'cmd');
$latest = $this->input->get('latest', false, 'int');
if (empty($tag))
{
$tag = null;
}
/** @var \Solo\Model\Log $model */
$model = $this->getModel();
if ($latest)
{
$logFiles = $model->getLogFiles();
$tag = array_shift($logFiles);
}
$model->setState('tag', $tag);
$this->display();
}
/**
* Renders the log contents for use in an iFrame
*
* @return void
*/
public function iframe()
{
$tag = $this->input->get('tag', null, 'cmd');
if (empty($tag))
{
$tag = null;
}
$model = $this->getModel();
$model->setState('tag', $tag);
$this->display();
}
/**
* Downloads the log file as a plain text file
*
* @return void
*/
public function download()
{
$tag = $this->input->get('tag', null, 'cmd');
if (empty($tag))
{
$tag = null;
}
$asAttachment = $this->input->getBool('attachment', true);
@ob_end_clean(); // In case some braindead plugin spits its own HTML
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Content-Description: File Transfer");
header('Content-Type: text/plain');
$inCMS = $this->container->segment->get('insideCMS', false);
$filename = 'Akeeba ' . ($inCMS ? 'Backup' : 'Solo') . ' Debug Log.txt';
if ($asAttachment)
{
header('Content-Disposition: attachment; filename="' . $filename . '"');
}
/** @var \Solo\Model\Log $model */
$model = $this->getModel();
$model->setState('tag', $tag);
$model->echoRawLog();
@flush();
$this->container->application->close();
}
public function inlineRaw()
{
$tag = $this->input->get('tag', null, 'cmd');
if (empty($tag))
{
$tag = null;
}
/** @var \Solo\Model\Log $model */
$model = $this->getModel();
$model->setState('tag', $tag);
$model->echoRawLog();
}
}

View File

@@ -0,0 +1,378 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use Akeeba\Engine\Util\RandomValue;
use AkeebaBackupWPUpdater;
use Awf\Application\Application;
use Awf\Mvc\Model;
use Awf\Text\Text;
use Exception;
use RuntimeException;
use Solo\Model\Update;
use Solo\View\Main\Html;
class Main extends ControllerDefault
{
public function switchProfile()
{
$this->csrfProtection();
// Switch the active profile
$session = Application::getInstance()->getContainer()->segment;
$session->profile = $this->input->getInt('profile', 1);
// Redirect
$url = $this->container->router->route('index.php?view=main');
$returnURL = $this->input->get('returnurl', '', 'raw');
if (!empty($returnURL))
{
$url = base64_decode($returnURL);
}
$this->setRedirect($url);
return true;
}
public function getUpdateInformation()
{
// Protect against direct access
$this->csrfProtection();
// Initialise
$ret = [
'hasUpdate' => false,
'version' => '',
'noticeHTML' => '',
];
// Am I running inside a CMS?
$inCMS = $this->container->segment->get('insideCMS', false);
/** @var Update $updateModel */
$updateModel = Model::getTmpInstance($this->container->application_name, 'Update', $this->container);
$ret['hasUpdate'] = $updateModel->getUpdateInformation()->get('hasUpdate', false);
$ret['version'] = $updateModel->getUpdateInformation()->get('version', 'dev');
if ($ret['hasUpdate'])
{
$router = $this->container->router;
$updateHeader = Text::sprintf('SOLO_UPDATE_LBL_MAINNOTICE_TEXT', '<span class="label label-success">' . $ret['version'] . '</span>');
$updateButton = Text::_('SOLO_UPDATE_BTN_UPDATE_NOW');
$updateLink = $router->route('index.php?view=update');
$ret['noticeHTML'] = <<< HTML
<div class="akeeba-block--warning">
<h3>
$updateHeader
</h3>
<p style="text-align: center">
<a href="$updateLink" class="akeeba-btn--large--teal">
<span class="akion-refresh"></span>
$updateButton
</a>
</p>
</div>
HTML;
}
echo '#"\#\"#' . json_encode($ret) . '#"\#\"#';
$this->container->application->close();
}
public function applyDownloadId()
{
// Protect against direct access
$this->csrfProtection();
$msg = Text::_('COM_AKEEBA_CPANEL_ERR_INVALIDDOWNLOADID');
$msgType = 'error';
$dlid = $this->input->getString('dlid', '');
// If the Download ID seems legit let's apply it
if (preg_match('/^([0-9]{1,}:)?[0-9a-f]{32}$/i', $dlid))
{
$msg = null;
$msgType = null;
$config = $this->container->appConfig;
$config->set('options.update_dlid', $dlid);
$config->saveConfiguration();
}
// Akeeba Backup for WordPress: reset update information
if (defined('WPINC'))
{
$transient = (object) [
'response' => [],
];
AkeebaBackupWPUpdater::getupdates($transient);
}
// Redirect
$url = $this->container->router->route('index.php?view=main');
$returnURL = $this->input->get('returnurl', '', 'raw');
if (!empty($returnURL))
{
$url = base64_decode($returnURL);
}
$this->setRedirect($url, $msg, $msgType);
return true;
}
/**
* Reset the Secret Word for front-end and remote backup
*
* @return bool
*/
public function resetSecretWord()
{
// CSRF prevention
$this->csrfProtection();
$session = $this->container->segment;
$newSecret = $session->get('newSecretWord', null);
if (empty($newSecret))
{
$random = new RandomValue();
$newSecret = $random->generateString(32);
$session->set('newSecretWord', $newSecret);
}
$config = $this->container->appConfig;
$config->set('options.frontend_secret_word', $newSecret);
$config->saveConfiguration();
$msg = Text::sprintf('COM_AKEEBA_CPANEL_MSG_FESECRETWORD_RESET', $newSecret);
$url = $this->container->router->route('index.php?view=Main');
$this->setRedirect($url, $msg);
return true;
}
/**
* Resets the "updatedb" flag and forces the database updates
*/
public function forceUpdateDb()
{
// Reset the flag so the updates could take place
$this->container->appConfig->set('updatedb', null);
$this->container->appConfig->saveConfiguration();
/** @var \Solo\Model\Main $model */
$model = $this->getModel();
try
{
$model->checkAndFixDatabase();
}
catch (RuntimeException $e)
{
// This should never happen, since we reset the flag before execute the update, but you never know
}
$url = $this->container->router->route('index.php?view=Main');
$this->setRedirect($url);
}
/**
* Dismisses the Core to Pro upsell for 15 days
*
* @return void
*/
public function dismissUpsell()
{
// Reset the flag so the updates could take place
$this->container->appConfig->set('lastUpsellDismiss', time());
$this->container->appConfig->saveConfiguration();
$url = $this->container->router->route('index.php?view=Main');
$this->setRedirect($url);
}
/**
* Check the security of the backup output directory and return the results for consumption through AJAX
*
* @return void
*
* @throws Exception
*
* @since 7.0.3
*/
public function checkOutputDirectory()
{
/** @var \Solo\Model\Main $model */
$model = $this->getModel();
$outDir = $model->getOutputDirectory();
$inCMS = $this->container->segment->get('insideCMS', false);
try
{
$result = $model->getOutputDirectoryWebAccessibleState($outDir);
if (!$inCMS)
{
$altResult = $model->getOutputDirectoryWebAccessibleState($outDir, true);
foreach ($altResult as $k => $v)
{
$result[$k] = $result[$k] || $altResult[$k];
}
}
}
catch (RuntimeException $e)
{
$result = [
'readFile' => false,
'listFolder' => false,
'isSystem' => $model->isOutputDirectoryInSystemFolder(),
'hasRandom' => $model->backupFilenameHasRandom(),
];
}
@ob_end_clean();
echo '#"\#\"#' . json_encode($result) . '#"\#\"#';
$this->container->application->close();
}
/**
* Add security files to the output directory of the currently configured backup profile
*
* @return void
*
* @throws Exception
*
* @since 7.0.3
*/
public function fixOutputDirectory()
{
// CSRF prevention
$this->csrfProtection();
/** @var \Solo\Model\Main $model */
$model = $this->getModel();
$outDir = $model->getOutputDirectory();
$fsUtils = Factory::getFilesystemTools();
$fsUtils->ensureNoAccess($outDir, true);
$this->setRedirect($this->container->router->route('index.php'));
}
/**
* Adds the [RANDOM] variable to the backup output filename, save the configuration and reload the Control Panel.
*
* @return void
*
* @throws Exception
*
* @since 7.0.3
*/
public function addRandomToFilename()
{
// CSRF prevention
$this->csrfProtection();
$registry = Factory::getConfiguration();
$templateName = $registry->get('akeeba.basic.archive_name');
if (strpos($templateName, '[RANDOM]') === false)
{
$templateName .= '-[RANDOM]';
$registry->set('akeeba.basic.archive_name', $templateName);
Platform::getInstance()->save_configuration();
}
$this->setRedirect($this->container->router->route('index.php'));
}
protected function onBeforeDefault()
{
// If we are running inside a CMS but there is no active user we have to throw a 403
$inCMS = $this->container->segment->get('insideCMS', false);
if ($inCMS && !$this->container->userManager->getUser()->getId())
{
return false;
}
/** @var \Solo\Model\Main $model */
$model = $this->getModel();
try
{
$model->checkAndFixDatabase();
}
catch (RuntimeException $e)
{
// The update is stuck. We will display a warning in the Control Panel
}
try
{
if ($inCMS)
{
$model->updateAutomationConfiguration();
}
}
catch (RuntimeException $e)
{
// Oh, well.
}
// Run the update scripts, if necessary
if ($model->postUpgradeActions())
{
$url = $this->container->router->route('index.php?view=main');
$this->container->application->redirect($url);
}
// Let's make sure the temporary and output directories are set correctly and writable...
$wizmodel = new \Solo\Model\Wizard($this->container);
$wizmodel->autofixDirectories();
// Rebase Off-site Folder Inclusion filters to use site path variables
if (class_exists('\Solo\Model\Extradirs'))
{
$incFoldersModel = new \Solo\Model\Extradirs($this->container);
$incFoldersModel->rebaseFiltersToSiteDirs();
}
// Apply settings encryption preferences
$model->checkEngineSettingsEncryption();
// Convert existing log files to the new .log.php format
$model->convertLogFiles();
// Update magic configuration parameters
$model->updateMagicParameters();
// Flag stuck backups
$model->flagStuckBackups();
// Reload the quirks definitions, since flagging stuck backups will reset the factory state,
// deleting temp objects and their settings
Platform::getInstance()->apply_quirk_definitions();
// Copy the ACL checks to the view. We'll use that information to show or hide icons
/** @var Html $view */
$view = $this->getView();
$view->aclChecks = $this->aclChecks;
return true;
}
}

View File

@@ -0,0 +1,575 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use Awf\Text\Text;
use Exception;
class Manage extends ControllerDefault
{
public function main()
{
$this->container->segment->set('solo_manage_task', 'main');
$this->display();
}
/**
* Allows the editing of the backup comment
*/
public function showComment()
{
$this->csrfProtection();
// Get the return URL
$router = $this->container->router;
$task = $this->container->segment->get('solo_manage_task', 'main');
$returnUrl = $router->route('index.php?view=manage&task=' . $task);
$model = $this->getModel();
// Get the ID
$id = $model->getState('id', 0);
$part = $this->input->get('part', -1, 'int');
$cid = $this->input->get('cid', array(), 'array');
if (empty($id))
{
if (is_array($cid) && !empty($cid))
{
$id = $cid[0];
}
else
{
$id = -1;
}
}
if ($id <= 0)
{
$this->setRedirect($returnUrl, Text::_('COM_AKEEBA_BUADMIN_ERROR_INVALIDID'), 'error');
}
else
{
$this->getModel()->setState('id', $id);
}
$this->getView()->setLayout('comment');
$this->display();
}
/**
* Downloads the backup file of a specific backup attempt, if it's available on the server
*
* @return void
*/
public function download()
{
$router = $this->container->router;
$model = $this->getModel();
$id = $model->getState('id', 0);
$part = $this->input->get('part', -1, 'int');
$cid = $this->input->get('cid', array(), 'array');
if (empty($id))
{
if (is_array($cid) && !empty($cid))
{
$id = $cid[0];
}
else
{
$id = -1;
}
}
if ($id <= 0)
{
$url = $router->route('index.php?view=manage');
$this->setRedirect($url, Text::_('COM_AKEEBA_BUADMIN_ERROR_INVALIDID'), 'error');
return;
}
$stat = Platform::getInstance()->get_statistics($id);
$allFileNames = Factory::getStatistics()->get_all_filenames($stat);
// Check single part files
if ((count($allFileNames) == 1) && ($part == -1))
{
$fileName = array_shift($allFileNames);
}
elseif ((count($allFileNames) > 0) && (count($allFileNames) > $part) && ($part >= 0))
{
$fileName = $allFileNames[$part];
}
else
{
$fileName = null;
}
if (is_null($fileName) || empty($fileName) || !@file_exists($fileName))
{
$url = $router->route('index.php?view=manage');
$this->setRedirect($url, Text::_('COM_AKEEBA_BUADMIN_ERROR_INVALIDDOWNLOAD'), 'error');
return;
}
else
{
// Remove PHP's time limit (is this actually still applicable in 2014?)
if (function_exists('ini_get') && function_exists('set_time_limit'))
{
if (!@ini_get('safe_mode'))
{
@set_time_limit(0);
}
}
$basename = @basename($fileName);
$fileSize = @filesize($fileName);
$extension = strtolower(str_replace(".", "", strrchr($fileName, ".")));
while (@ob_end_clean())
{
;
}
@clearstatcache();
// Send MIME headers
header('MIME-Version: 1.0');
header('Content-Disposition: attachment; filename="' . $basename . '"');
header('Content-Transfer-Encoding: binary');
header('Accept-Ranges: bytes');
switch ($extension)
{
case 'zip':
// ZIP MIME type
header('Content-Type: application/zip');
break;
default:
// Generic binary data MIME type
header('Content-Type: application/octet-stream');
break;
}
// Notify of the file size, if this info is available
if ($fileSize > 0)
{
header('Content-Length: ' . @filesize($fileName));
}
// Disable caching
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Expires: 0");
header('Pragma: no-cache');
flush();
if ($fileSize > 0)
{
// If the filesize is reported, use 1M chunks for echoing the data to the browser
$blocksize = 1048576; //1M chunks
$handle = @fopen($fileName, "r");
// Now we need to loop through the file and echo out chunks of file data
if ($handle !== false)
{
while (!@feof($handle))
{
echo @fread($handle, $blocksize);
@ob_flush();
flush();
}
}
if ($handle !== false)
{
@fclose($handle);
}
}
else
{
// If the file size is not reported, hope that readfile works
@readfile($fileName);
}
exit(0);
}
}
/**
* Deletes one or several backup statistics records and their associated backup files
*
* @return void
*/
public function remove()
{
// CSRF prevention
$this->csrfProtection();
// Get the return URL
$router = $this->container->router;
$task = $this->container->segment->get('solo_manage_task', 'main');
$returnUrl = $router->route('index.php?view=manage&task=' . $task);
// Get the ID
$cid = $this->input->get('cid', array(), 'array');
$id = $this->input->get('id', 0, 'int');
if (empty($id))
{
if (!empty($cid) && is_array($cid))
{
foreach ($cid as $id)
{
$result = $this->_remove($id);
if (!$result)
{
$this->setRedirect($returnUrl, Text::_('COM_AKEEBA_BUADMIN_ERROR_INVALIDID'), 'error');
return;
}
}
}
else
{
$this->setRedirect($returnUrl, Text::_('COM_AKEEBA_BUADMIN_ERROR_INVALIDID'), 'error');
return;
}
}
else
{
$result = $this->_remove($id);
if (!$result)
{
$this->setRedirect($returnUrl, Text::_('COM_AKEEBA_BUADMIN_ERROR_INVALIDID'), 'error');
}
}
$this->setRedirect($returnUrl, Text::_('COM_AKEEBA_BUADMIN_MSG_DELETED'));
}
/**
* Deletes backup files associated to one or several backup statistics records
*
* @return void
*/
public function deleteFiles()
{
// CSRF prevention
$this->csrfProtection();
// Get the return URL
$router = $this->container->router;
$task = $this->container->segment->get('solo_manage_task', 'main');
$returnUrl = $router->route('index.php?view=manage&task=' . $task);
// Get the ID
$cid = $this->input->get('cid', array(), 'array');
$id = $this->input->get('id', 0, 'int');
if (empty($id))
{
if (!empty($cid) && is_array($cid))
{
foreach ($cid as $id)
{
$result = $this->_removeFiles($id);
if (!$result)
{
$this->setRedirect($returnUrl, Text::_('COM_AKEEBA_BUADMIN_ERROR_INVALIDID'), 'error');
return;
}
}
}
else
{
$this->setRedirect($returnUrl, Text::_('COM_AKEEBA_BUADMIN_ERROR_INVALIDID'), 'error');
return;
}
}
else
{
$result = $this->_remove($id);
if (!$result)
{
$this->setRedirect($returnUrl, Text::_('COM_AKEEBA_BUADMIN_ERROR_INVALIDID'), 'error');
}
}
$this->setRedirect($returnUrl, Text::_('COM_AKEEBA_BUADMIN_MSG_DELETEDFILE'));
}
/**
* Freeze select records
*
* @throws Exception
*/
public function freeze()
{
$this->csrfProtection();
$id = $this->input->get('id', 0, 'int');
$ids = (array) $id;
/** @var \Solo\Model\Manage $model */
$model = $this->getModel();
$message = Text::_('COM_AKEEBA_BUADMIN_FREEZE_OK');
$type = 'message';
try
{
$model->freezeUnfreezeRecords($ids, 1);
}
catch (Exception $e)
{
$message = Text::sprintf('COM_AKEEBA_BUADMIN_FREEZE_ERROR', $e->getMessage());
$type = 'error';
}
$this->setRedirect($this->container->router->route('index.php?view=Manage'), $message, $type);
}
/**
* Unfreeze select records
*
* @throws Exception
*/
public function unfreeze()
{
$this->csrfProtection();
$id = $this->input->get('id', 0, 'int');
$ids = (array) $id;
/** @var \Solo\Model\Manage $model */
$model = $this->getModel();
$message = Text::_('COM_AKEEBA_BUADMIN_UNFREEZE_OK');
$type = 'message';
try
{
$model->freezeUnfreezeRecords($ids, 0);
}
catch (Exception $e)
{
$message = Text::sprintf('COM_AKEEBA_BUADMIN_UNFREEZE_ERROR', $e->getMessage());
$type = 'error';
}
$this->setRedirect($this->container->router->route('index.php?view=Manage'), $message, $type);
}
/**
* Removes the backup file linked to a statistics entry and the entry itself
*
* @param integer $id The ID of the backup record
*
* @return boolean True on success
*/
private function _remove($id)
{
// Get the return URL
$router = $this->container->router;
$task = $this->container->segment->get('solo_manage_task', 'main');
$returnUrl = $router->route('index.php?view=manage&task=' . $task);
if ($id <= 0)
{
$this->setRedirect($returnUrl, Text::_('COM_AKEEBA_BUADMIN_ERROR_INVALIDID'), 'error');
return true;
}
/** @var \Solo\Model\Manage $model */
$model = $this->getModel();
$model->setState('id', $id);
try
{
$model->delete();
return true;
}
catch (\RuntimeException $e)
{
return false;
}
}
/**
* Removes only the backup file linked to a statistics entry
*
* @param integer $id The ID of the backup record
*
* @return boolean True on success
*/
private function _removeFiles($id)
{
// Get the return URL
$router = $this->container->router;
$task = $this->container->segment->get('solo_manage_task', 'main');
$returnUrl = $router->route('index.php?view=manage&task=' . $task);
if ($id <= 0)
{
$this->setRedirect($returnUrl, Text::_('COM_AKEEBA_BUADMIN_ERROR_INVALIDID'), 'error');
return true;
}
/** @var \Solo\Model\Manage $model */
$model = $this->getModel();
$model->setState('id', $id);
try
{
$model->deleteFile();
return true;
}
catch (\RuntimeException $e)
{
return false;
}
}
/**
* Save an edited backup record
*
* @return void
*/
public function save()
{
// CSRF prevention
$this->csrfProtection();
// Get the return URL
$router = $this->container->router;
$task = $this->container->segment->get('solo_manage_task', 'main');
$returnUrl = $router->route('index.php?view=manage&task=' . $task);
$id = $this->input->get('id', 0, 'int');
$description = $this->input->get('description', '', 'string');
$comment = $this->input->get('comment', null, 'string', 4);
$statistic = Platform::getInstance()->get_statistics($id);
$statistic['description'] = $description;
$statistic['comment'] = $comment;
$result = Platform::getInstance()->set_or_update_statistics($id, $statistic);
if ($result !== false)
{
$message = Text::_('COM_AKEEBA_BUADMIN_LOG_SAVEDOK');
$type = 'message';
}
else
{
$message = Text::_('COM_AKEEBA_BUADMIN_LOG_SAVEERROR');
$type = 'error';
}
$this->setRedirect($returnUrl, $message, $type);
}
/**
* Redirect to the restoration page for this backup record
*
* @return void
*/
public function restore()
{
// CSRF prevention
$this->csrfProtection();
$router = $this->container->router;
$id = null;
$cid = $this->input->get('cid', array(), 'array');
if (!empty($cid))
{
$id = intval($cid[0]);
if ($id <= 0)
{
$id = null;
}
}
if (empty($id))
{
$id = $this->input->get('id', -1, 'int');
}
if ($id <= 0)
{
$id = null;
}
$url = $router->route('index.php?view=restore&id=' . $id);
$this->setRedirect($url);
return;
}
/**
* Cancel the editing operation
*
* @return void
*/
public function cancel()
{
// CSRF prevention
$this->csrfProtection();
// Get the return URL
$router = $this->container->router;
$task = $this->container->segment->get('solo_manage_task', 'main');
$returnUrl = $router->route('index.php?view=manage&task=' . $task);
$this->setRedirect($returnUrl);
}
public function hideModal()
{
/** @var \Solo\Model\Manage $model */
$model = $this->getModel();
$model->hideRestorationInstructionsModal();
// Get the return URL
$router = $this->container->router;
$task = $this->container->segment->get('solo_manage_task', 'main');
$returnUrl = $router->route('index.php?view=manage&task=' . $task);
$this->setRedirect($returnUrl);
}
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
/**
* The Controller for the Phpinfo view
*/
class Phpinfo extends ControllerDefault
{
public function phpinfo()
{
@ob_end_clean();
phpinfo();
$this->container->application->close(200);
}
}

View File

@@ -0,0 +1,143 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
use Awf\Inflector\Inflector;
use Awf\Text\Text;
use RuntimeException;
class Profiles extends DataControllerDefault
{
/**
* Imports an exported profile .json file
*
* @return void
*/
public function import()
{
// CSRF prevention
$this->csrfProtection();
// Get the reference to the uploaded file
$file = $_FILES['importfile'];
// Get a URL router
$router = $this->container->router;
if (!isset($file['name']))
{
$this->setRedirect($router->route('index.php?view=profiles'), Text::_('MSG_UPLOAD_INVALID_REQUEST'), 'error');
}
/** @var \Solo\Model\Profiles $model */
$model = $this->getModel();
// Load the file data
$data = file_get_contents($file['tmp_name']);
@unlink($file['tmp_name']);
// JSON decode
$data = json_decode($data, true);
// Import
$message = Text::_('COM_AKEEBA_PROFILES_MSG_IMPORT_COMPLETE');
$messageType = null;
try
{
$model->reset()->import($data);
}
catch (RuntimeException $e)
{
$message = $e->getMessage();
$messageType = 'error';
}
// Redirect back to the main page
$this->setRedirect($router->route('index.php?view=profiles'), $message, $messageType);
}
/**
* Enable the Quick Icon for a record
*
* @since 3.1.2
* @throws \Exception
*/
public function publish()
{
$this->setQuickIcon(1);
}
/**
* Disable the Quick Icon for a record
*
* @since 3.1.2
* @throws \Exception
*/
public function unpublish()
{
$this->setQuickIcon(0);
}
/**
* Sets the Quick Icon status for the record.
*
* @param int|bool $published Should this profile have a Quick Icon?
*
* @return void
* @throws \Exception
*
* @since 3.1.2
*/
public function setQuickIcon($published)
{
// CSRF prevention
$this->csrfProtection();
/** @var \Solo\Model\Profiles $model */
$model = $this->getModel();
$ids = $this->getIDsFromRequest($model, false);
try
{
$status = true;
foreach ($ids as $id)
{
$model->find($id);
$model->save(array(
'quickicon' => $published ? 1 : 0
));
}
}
catch (\Exception $e)
{
$status = false;
$error = $e->getMessage();
}
// Redirect
if ($customURL = $this->input->getBase64('returnurl', ''))
{
$customURL = base64_decode($customURL);
}
$router = $this->container->router;
$url = !empty($customURL) ? $customURL : $router->route('index.php?view=' . Inflector::pluralize($this->view));
if (!$status)
{
$this->setRedirect($url, $error, 'error');
}
else
{
$this->setRedirect($url);
}
}
}

View File

@@ -0,0 +1,224 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Controller;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use Awf\Application\Application;
use Awf\Date\Date;
use Awf\Mvc\Model;
use Awf\Text\Text;
use Solo\Model\Backup;
class Remote extends ControllerDefault
{
public function execute($task)
{
$this->checkPermissions();
define('AKEEBA_BACKUP_ORIGIN', 'frontend');
return parent::execute($task);
}
public function main()
{
// Set the profile
$this->setProfile();
// Get the backup ID
$backupId = $this->input->get('backupid', null, 'cmd');
if (empty($backupId))
{
$backupId = null;
}
/** @var Backup $model */
$model = Model::getTmpInstance($this->container->application_name, 'Backup', $this->container);
$dateNow = new Date();
$model->setState('tag', AKEEBA_BACKUP_ORIGIN);
$model->setState('backupid', $backupId);
$model->setState('description', Text::_('COM_AKEEBA_BACKUP_DEFAULT_DESCRIPTION') . ' ' . $dateNow->format(Text::_('DATE_FORMAT_LC2'), true));
$model->setState('comment', '');
$array = $model->startBackup();
$backupId = $model->getState('backupid', null, 'cmd');
$this->processEngineReturnArray($array, $backupId);
}
public function step()
{
// Set the profile
$this->setProfile();
// Get the backup ID
$backupId = $this->input->get('backupid', null, 'cmd');
if (empty($backupId))
{
$backupId = null;
}
/** @var Backup $model */
$model = Model::getTmpInstance($this->container->application_name, 'Backup', $this->container);
$model->setState('tag', AKEEBA_BACKUP_ORIGIN);
$model->setState('backupid', $backupId);
$array = $model->stepBackup();
$backupId = $model->getState('backupid', null, 'cmd');
$this->processEngineReturnArray($array, $backupId);
}
/**
* Used by the tasks to process Akeeba Engine's return array. Depending on the result and the component options we
* may throw text output or send an HTTP redirection header.
*
* @param array $array The return array to process
* @param string $backupId The backup ID (used to step the backup process)
*/
private function processEngineReturnArray($array, $backupId)
{
$noredirect = $this->input->get('noredirect', 0, 'int');
if ($array['Error'] != '')
{
// An error occured
if ($noredirect)
{
@ob_end_clean();
header('Content-type: text/plain');
header('Connection: close');
echo '500 ERROR -- ' . $array['Error'];
flush();
$this->container->application->close();
}
throw new \RuntimeException($array['Error'], 500);
}
if ($array['HasRun'] == 1)
{
// All done
Factory::nuke();
Factory::getFactoryStorage()->reset();
@ob_end_clean();
header('Content-type: text/plain');
header('Connection: close');
echo '200 OK';
flush();
$this->container->application->close();
}
if ($noredirect != 0)
{
@ob_end_clean();
header('Content-type: text/plain');
header('Connection: close');
echo "301 More work required -- BACKUPID ###$backupId###";
flush();
$this->container->application->close();
}
$router = $this->container->router;
$url = 'index.php?view=remote&task=step&key=' . $this->input->get('key', '', 'none', 2) . '&profile=' . $this->input->get('profile', 1, 'int');
if (!empty($backupId))
{
$url .= '&backupid=' . $backupId;
}
$this->setRedirect($router->route($url));
}
/**
* Check that the user has sufficient permissions, or die in error
*
* @return void
*/
private function checkPermissions()
{
// Is frontend backup enabled?
$febEnabled = Platform::getInstance()->get_platform_configuration_option('frontend_enable', 0);
$febEnabled = in_array($febEnabled, array('on', 'checked', 'true', 1, 'yes'));
$validKey = Platform::getInstance()->get_platform_configuration_option('frontend_secret_word', '');
if (!\Akeeba\Engine\Util\Complexify::isStrongEnough($validKey, false))
{
$febEnabled = false;
}
$validKeyTrim = trim($validKey);
if (!$febEnabled || empty($validKey))
{
@ob_end_clean();
header('Content-type: text/plain');
header('Connection: close');
echo "403 Operation not permitted";
flush();
$this->container->application->close();
throw new \RuntimeException('Operation not permitted', 403);
}
// Is the key good?
$key = $this->input->get('key', '', 'none', 2);
if (($key != $validKey) || (empty($validKeyTrim)))
{
@ob_end_clean();
header('Content-type: text/plain');
header('Connection: close');
echo "403 Operation not permitted";
flush();
$this->container->application->close();
throw new \RuntimeException('Operation not permitted', 403);
}
}
/**
* Set the active profile from the input parameters
*/
private function setProfile()
{
// Set profile
$profile = $this->input->get('profile', 1, 'int');
if (empty($profile))
{
$profile = 1;
}
$session = Application::getInstance()->getContainer()->segment;
$session->profile = $profile;
/**
* DO NOT REMOVE!
*
* The Model will only try to load the configuration after nuking the factory. This causes Profile 1 to be
* loaded first. Then it figures out it needs to load a different profile and it does but the protected keys
* are NOT replaced, meaning that certain configuration parameters are not replaced. Most notably, the chain.
* This causes backups to behave weirdly. So, DON'T REMOVE THIS UNLESS WE REFACTOR THE MODEL.
*/
Platform::getInstance()->load_configuration($profile);
}
}

View File

@@ -0,0 +1,150 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Controller;
use Awf\Text\Text;
class Restore extends ControllerDefault
{
/**
* Show the main page, where the user selects the restoration options
*
* @return void
*/
public function main()
{
/** @var \Solo\Model\Restore $model */
$model = $this->getModel();
// Get the ID
$id = $model->getState('id', 0);
$cid = $this->input->get('cid', array(), 'array');
if (empty($id))
{
if (is_array($cid) && !empty($cid))
{
$id = $cid[0];
}
else
{
$id = -1;
}
}
$model->setState('id', $id);
$profileID = $this->input->getInt('profileid', 0);
$model->setState('profileid', $profileID);
try
{
$model->validateRequest();
}
catch (\Exception $e)
{
$message = $e->getMessage();
$router = $this->container->router;
$this->setRedirect($router->route('index.php?view=manage'), $message, 'error');
$this->redirect();
return;
}
$model->setState('restorationstep', 0);
$this->display();
}
/**
* Show the restoration user interface and start the restoration
*
* @return void
*/
public function start()
{
$this->csrfProtection();
$this->getView()->setLayout('restore');
/** @var \Solo\Model\Restore $model */
$model = $this->getModel();
$model->setState('restorationstep', 1);
// This is required. validateRequest loads the correct backup profile. We need it to get the site directory.
try
{
$model->validateRequest();
}
catch (\Exception $e)
{
$message = $e->getMessage();
$router = $this->container->router;
$this->setRedirect($router->route('index.php?view=manage'), $message, 'error');
$this->redirect();
return;
}
// Set the model's state
$model->setState('jps_key', $this->input->get('jps_key', '', 'cmd'));
$model->setState('procengine', $this->input->get('procengine', 'direct', 'cmd'));
$model->setState('zapbefore', $this->input->get('zapbefore', 0, 'int'));
$model->setState('min_exec', $this->input->get('min_exec', 0, 'int'));
$model->setState('max_exec', $this->input->get('max_exec', 5, 'int'));
$model->setState('ftp_host', $this->input->get('ftp_host', '', 'none', 2));
$model->setState('ftp_port', $this->input->get('ftp_port', 21, 'int'));
$model->setState('ftp_user', $this->input->get('ftp_user', '', 'none', 2));
$model->setState('ftp_pass', $this->input->get('ftp_pass', '', 'none', 2));
$model->setState('ftp_root', $this->input->get('ftp_root', '', 'none', 2));
$model->setState('tmp_path', $this->input->get('tmp_path', '', 'none', 2));
$model->setState('ftp_ssl', $this->input->get('usessl', 'false', 'cmd') == 'true');
$model->setState('ftp_pasv', $this->input->get('passive', 'true', 'cmd') == 'true');
try
{
$model->createRestorationFile();
}
catch (\Exception $e)
{
$router = $this->container->router;
$this->setRedirect($router->route('index.php?view=manage'),
Text::_('COM_AKEEBA_RESTORE_ERROR_CANT_WRITE') . '<br/>' . $e->getMessage(), 'error');
$this->redirect();
return;
}
$this->display();
}
/**
* Perform an AJAX request, returning the result encoded in JSON and surrounded by triple hashes
*
* @return void
*/
public function ajax()
{
/** @var \Solo\Model\Restore $model */
$model = $this->getModel();
$ajax = $this->input->get('ajax', '', 'cmd');
$model->setState('ajax', $ajax);
$ret = $model->doAjax();
@ob_end_clean();
echo '###' . json_encode($ret) . '###';
flush();
$this->container->application->close();
}
}

View File

@@ -0,0 +1,13 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Controller;
class Schedule extends ControllerDefault
{
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
/**
* The controller for FTP browser
*/
class Sftpbrowser extends ControllerDefault
{
public function execute($task)
{
// If we are running inside a CMS but there is no active user we have to throw a 403
$inCMS = $this->container->segment->get('insideCMS', false);
if ($inCMS && !$this->container->userManager->getUser()->getId())
{
return false;
}
return parent::execute($task);
}
public function main()
{
/** @var \Solo\Model\Sftpbrowser $model */
$model = $this->getModel();
// Grab the data and push them to the model
$directory = $this->input->getRaw('directory', '');
$directory = '/' . ltrim($directory, '/');
$model->setState('host', $this->input->getString('host', ''));
$model->setState('port', $this->input->getInt('port', 22));
$model->setState('username', $this->input->getRaw('username', ''));
$model->setState('password', $this->input->getRaw('password', ''));
$model->setState('directory', $directory);
$model->setState('privKey', $this->input->getRaw('privkey', ''));
$model->setState('pubKey', $this->input->getRaw('pubkey', ''));
$ret = $model->doBrowse();
@ob_end_clean();
echo '#"\#\"#'.json_encode($ret).'#"\#\"#';
flush();
$this->container->application->close();
}
}

View File

@@ -0,0 +1,147 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
use Awf\Text\Text;
class Sysconfig extends ControllerDefault
{
public function save()
{
$this->csrfProtection();
$urlredirect = $this->input->get('urlredirect', null, 'raw');
$data = $this->input->getData();
unset($data['view']);
unset($data['task']);
unset($data['layout']);
unset($data['token']);
if (isset($data['urlredirect']))
{
unset($data['urlredirect']);
}
$keys = array_keys($data);
$checkboxKeys = array(
'mail.online', 'mail.smtpauth', 'options.legacyapi_enabled', 'options.jsonapi_enabled', 'options.frontend_email_on_finish',
'options.usesvnsource', 'options.displayphpwarning'
);
foreach ($keys as $key)
{
if (strpos($key, 'fs_') === 0)
{
$data['fs.' . substr($key, 3)] = $data[$key];
unset($data[$key]);
$key = 'fs.' . substr($key, 3);
}
elseif (strpos($key, 'mail_') === 0)
{
$data['mail.' . substr($key, 5)] = $data[$key];
unset($data[$key]);
$key = 'mail.' . substr($key, 5);
}
if (in_array($key, $checkboxKeys))
{
$data[$key] = in_array($data[$key], array('on', 'yes', 'true', 1, true));
}
elseif ($key == 'options')
{
foreach ($data[$key] as $k => $v)
{
$check = 'options.' . $k;
if (in_array($check, $checkboxKeys))
{
$data[$key][$k] = in_array($data[$key][$k], array('on', 'yes', 'true', 1, true));
}
}
}
}
$config = $this->container->appConfig;
foreach ($data as $k => $v)
{
if (is_array($v))
{
foreach ($v as $sk => $sv)
{
$config->set($k . '.' . $sk, $sv);
}
}
else
{
$config->set($k, $v);
}
}
$this->container->appConfig->saveConfiguration();
if ($urlredirect)
{
$url = base64_decode($urlredirect);
}
else
{
$url = $this->container->router->route('index.php');
}
$this->setRedirect($url, Text::_('SOLO_SYSCONFIG_SAVE'));
// Akeeba Backup for WordPress: reset update information
if (defined('WPINC'))
{
$transient = (object) [
'response' => []
];
\AkeebaBackupWPUpdater::getupdates($transient);
}
}
public function apply()
{
$this->save();
$url = $this->container->router->route('index.php?view=sysconfig');
$this->setRedirect($url, Text::_('SOLO_SYSCONFIG_SAVE'));
}
public function testemail()
{
$config = $this->container->appConfig;
$mailer = $this->container->mailer;
$user = $this->container->userManager->getUser();
$from = $config->get('mail.mailfrom');
$fromName = $config->get('mail.fromname');
$subject = Text::sprintf('SOLO_SYSCONFIG_TESTEMAIL_SUBJECT', $this->container->appConfig->get('base_url', ''));
$body = Text::_('SOLO_SYSCONFIG_TESTEMAIL_BODY');
try
{
$mailer->sendMail($from, $fromName, $user->getEmail(), $subject, $body);
$type = 'info';
$msg = Text::_('SOLO_SYSCONFIG_TESTMEMAIL_SENT');
}
catch(\Exception $e)
{
$type = 'error';
$msg = $e->getMessage();
}
$this->setRedirect($this->container->router->route('index.php?view=sysconfig'), $msg, $type);
}
}

View File

@@ -0,0 +1,256 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Controller;
use Solo\Model\Exception\TransferIgnorableError;
use Solo\Model\Transfers;
class Transfer extends ControllerDefault
{
/** @var array The tasks this controller is allowed to use */
private $allowedTasks = array('wizard', 'checkUrl', 'applyConnection', 'initialiseUpload', 'upload', 'reset');
/**
* Override execute() to only allow specific tasks to run.
*
* @param string $task The task we are asked to run.
*
* @return bool|null
* @throws \Exception
*/
public function execute($task)
{
if (!in_array($task, $this->allowedTasks))
{
$task = $this->allowedTasks[0];
}
return parent::execute($task);
}
/**
* Default task, shows the wizard interface
*/
public function wizard()
{
parent::display();
}
/**
* Reset the wizard
*
* @return void
*/
public function reset()
{
$session = $this->container->segment;
$session->set('transfer', null);
$session->set('transfer.url', null);
$session->set('transfer.url_status', null);
$session->set('transfer.ftpsupport', null);
/** @var Transfers $model */
$model = $this->getModel();
$model->resetUpload();
$this->setRedirect($this->container->router->route('index.php?view=transfer'));
}
/**
* Cleans and checks the validity of the new site's URL
*/
public function checkUrl()
{
$url = $this->input->get('url', '', 'raw');
/** @var Transfers $model */
$model = $this->getModel(null, array('savestate' => 1));
$result = $model->checkAndCleanUrl($url);
$session = $this->container->segment;
$session->set('transfer.url', $result['url']);
$session->set('transfer.url_status', $result['status']);
@ob_end_clean();
echo '###' . json_encode($result) . '###';
$this->container->application->close();
}
/**
* Applies the FTP/SFTP connection information and makes some preliminary validation
*/
public function applyConnection()
{
$result = (object)array(
'status' => true,
'message' => '',
'ignorable' => false,
);
// Get the parameters from the request
$transferOption = $this->input->getCmd('method', 'ftp');
$force = $this->input->getInt('force', 0);
$ftpHost = $this->input->get('host', '', 'raw');
$ftpPort = $this->input->getInt('port', null);
$ftpUsername = $this->input->get('username', '', 'raw');
$ftpPassword = $this->input->get('password', '', 'raw');
$ftpPubKey = $this->input->get('pubKey', '', 'raw');
$ftpPrivateKey = $this->input->get('privKey', '', 'raw');
$ftpPassive = $this->input->getInt('passive', 1);
$ftpPassiveFix = $this->input->getInt('passive_fix', 1);
$ftpDirectory = $this->input->get('directory', '', 'raw');
$chunkMode = $this->input->get('chunkMode', 'chunked', 'cmd');
$chunkSize = $this->input->get('chunkSize', '5242880', 'int');
// Fix the port if it's missing
if (empty($ftpPort))
{
switch ($transferOption)
{
case 'ftp':
case 'ftpcurl':
$ftpPort = 21;
break;
case 'ftps':
case 'ftpscurl':
$ftpPort = 990;
break;
case 'sftp':
case 'sftpcurl':
$ftpPort = 22;
break;
}
}
// Store everything in the session
$session = $this->container->segment;
$session->set('transfer.transferOption', $transferOption);
$session->set('transfer.force', $force);
$session->set('transfer.ftpHost', $ftpHost);
$session->set('transfer.ftpPort', $ftpPort);
$session->set('transfer.ftpUsername', $ftpUsername);
$session->set('transfer.ftpPassword', $ftpPassword);
$session->set('transfer.ftpPubKey', $ftpPubKey);
$session->set('transfer.ftpPrivateKey', $ftpPrivateKey);
$session->set('transfer.ftpDirectory', $ftpDirectory);
$session->set('transfer.ftpPassive', $ftpPassive ? 1 : 0);
$session->set('transfer.ftpPassiveFix', $ftpPassiveFix ? 1 : 0);
$session->set('transfer.chunkMode', $chunkMode);
$session->set('transfer.chunkSize', $chunkSize);
/** @var Transfers $model */
$model = $this->getModel();
try
{
$config = $model->getFtpConfig();
$model->testConnection($config);
}
catch (TransferIgnorableError $e)
{
$result = (object)array(
'status' => false,
'message' => $e->getMessage(),
'ignorable' => true,
);
}
catch (\Exception $e)
{
$result = (object)array(
'status' => false,
'message' => $e->getMessage(),
'ignorable' => false,
);
}
@ob_end_clean();
echo '###' . json_encode($result) . '###';
$this->container->application->close();
}
/**
* Initialise the upload: sends Kickstart and our add-on script to the remote server
*/
public function initialiseUpload()
{
$result = (object)array(
'status' => true,
'message' => '',
'ignorable' => false,
);
/** @var Transfers $model */
$model = $this->getModel();
try
{
$config = $model->getFtpConfig();
$model->initialiseUpload($config);
}
catch (TransferIgnorableError $e)
{
$result = (object) [
'status' => false,
'message' => $e->getMessage(),
'ignorable' => true,
];
}
catch (\Exception $e)
{
$result = (object)array(
'status' => false,
'message' => $e->getMessage(),
'ignorable' => false,
);
}
@ob_end_clean();
echo '###' . json_encode($result) . '###';
$this->container->application->close();
}
/**
* Perform an upload step. Pass start=1 to reset the upload and start over.
*/
public function upload()
{
/** @var Transfers $model */
$model = $this->getModel();
if ($this->input->getBool('start', false))
{
$model->resetUpload();
}
try
{
$config = $model->getFtpConfig();
$uploadResult = $model->uploadChunk($config);
}
catch (\Exception $e)
{
$uploadResult = (object)array(
'status' => false,
'message' => $e->getMessage(),
'totalSize' => 0,
'doneSize' => 0,
'done' => false
);
}
$result = (object)$uploadResult;
@ob_end_clean();
echo '###' . json_encode($result) . '###';
$this->container->application->close();
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
use Awf\Text\Text;
class Update extends ControllerDefault
{
public function main()
{
$force = $this->input->getInt('force', 0) == 1;
/** @var \Solo\Model\Update $model */
$model = $this->getModel();
$model->load($force);
parent::main();
}
public function download()
{
/** @var \Solo\Model\Update $model */
$model = $this->getModel();
$model->prepareDownload();
$this->layout = 'download';
$this->display();
}
public function downloader()
{
$json = $this->input->get('json', '', 'raw');
$params = json_decode($json, true);
/** @var \Solo\Model\Update $model */
$model = $this->getModel();
if (is_array($params) && !empty($params))
{
foreach ($params as $k => $v)
{
$model->setState($k, $v);
}
}
$ret = $model->stepDownload();
echo '#"\#\"#' . json_encode($ret) . '#"\#\"#';
}
public function extract()
{
$this->csrfProtection();
$this->layout = 'extract';
/** @var \Solo\Model\Update $model */
$model = $this->getModel();
$model->createRestorationINI();
$this->display();
}
public function finalise()
{
// Do not add CSRF protection in this view; it called after the
// installation of the update. At this point the session MAY have
// already expired.
/** @var \Solo\Model\Update $model */
$model = $this->getModel();
$model->finalise();
$router = $this->container->router;
$this->setRedirect($router->route('index.php?view=update&force=1'), Text::_('SOLO_UPDATE_COMPLETE_OK'), 'success');
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Solo\Controller;
/**
* The Configuration Wizard controller
*/
class Wizard extends ControllerDefault
{
/**
* Executes a given controller task. The onBefore<task> and onAfter<task>
* methods are called automatically if they exist.
*
* @param string $task The task to execute, e.g. "browse"
*
* @return null|bool False on execution failure
*
* @throws \Exception When the task is not found
*/
public function execute($task)
{
// If we are running inside another CMS skip the first page
$inCMS = $this->container->segment->get('insideCMS', false);
if ($inCMS && !in_array($task, array('wizard', 'ajax')))
{
$task = 'wizard';
}
return parent::execute($task);
}
/**
* Tests and saves the site configuration settings, then redirects to the wizard task
*/
public function applySiteSettings()
{
$this->csrfProtection();
$siteParams = $this->input->get('var', array(), 'array');
try
{
/** @var \Solo\Model\Wizard $model */
$model = $this->getModel();
$model->testSiteParams($siteParams);
$model->saveSiteParams($siteParams);
}
catch (\Exception $e)
{
$url = $this->container->router->route('index.php?view=wizard');
$this->setRedirect($url, $e->getMessage(), 'error');
return;
}
$url = $this->container->router->route('index.php?view=wizard&task=wizard');
$this->setRedirect($url);
}
/**
* Show the main page of the wizard
*
* @return void
*/
public function wizard()
{
$this->getView()->setLayout('wizard');
$this->display();
}
public function ajax()
{
$act = $this->input->getCmd('akact', '');
/** @var \Solo\Model\Wizard $model */
$model = $this->getModel();
$model->setState('act', $act);
$ret = $model->runAjax();
@ob_end_clean();
echo '#"\#\"#' . json_encode( $ret ) . '#"\#\"#';
flush();
$this->container->application->close();
}
}