Move admin repository handling code to \SnappyMail\Repository

This commit is contained in:
the-djmaze 2022-03-04 16:09:50 +01:00
parent c5fc3d24a9
commit 3251611c6d
2 changed files with 262 additions and 231 deletions

View file

@ -466,222 +466,6 @@ trait Admin
));
}
private function snappyMailRepo() : string
{
return 'https://snappymail.eu/repository/v2/';
}
private function getRepositoryDataByUrl(bool &$bReal = false) : array
{
$bReal = false;
$aRep = null;
$sRep = '';
$sRepoFile = 'packages.json';
$iRepTime = 0;
$sCacheKey = KeyPathHelper::RepositoryCacheFile(\SnappyMail\Repository::BASE_URL, $sRepoFile);
$sRep = $this->Cacher()->Get($sCacheKey);
if ('' !== $sRep)
{
$iRepTime = $this->Cacher()->GetTimer($sCacheKey);
}
if ('' === $sRep || 0 === $iRepTime || \time() - 3600 > $iRepTime)
{
$sRep = \SnappyMail\Repository::get($sRepoFile,
$this->Config()->Get('labs', 'curl_proxy', ''),
$this->Config()->Get('labs', 'curl_proxy_auth', '')
);
if ($sRep)
{
$aRep = \json_decode($sRep);
$bReal = \is_array($aRep) && \count($aRep);
if ($bReal)
{
$this->Cacher()->Set($sCacheKey, $sRep);
$this->Cacher()->SetTimer($sCacheKey);
}
}
else
{
throw new \Exception('Cannot read remote repository file: '.$sRepoFile);
}
}
else if ('' !== $sRep)
{
$aRep = \json_decode($sRep, false, 10);
$bReal = \is_array($aRep) && \count($aRep);
}
return \is_array($aRep) ? $aRep : [];
}
private function getRepositoryData(bool &$bReal, string &$sError) : array
{
$aResult = array();
try {
$notDev = '0.0.0' !== APP_VERSION;
foreach ($this->getRepositoryDataByUrl($bReal) as $oItem) {
if ($oItem && isset($oItem->type, $oItem->id, $oItem->name,
$oItem->version, $oItem->release, $oItem->file, $oItem->description))
{
if (!empty($oItem->required) && $notDev && \version_compare(APP_VERSION, $oItem->required, '<')) {
continue;
}
if (!empty($oItem->depricated) && $notDev && \version_compare(APP_VERSION, $oItem->depricated, '>=')) {
continue;
}
if ('plugin' === $oItem->type) {
$aResult[$oItem->id] = array(
'type' => $oItem->type,
'id' => $oItem->id,
'name' => $oItem->name,
'installed' => '',
'enabled' => true,
'version' => $oItem->version,
'file' => $oItem->file,
'release' => $oItem->release,
'desc' => $oItem->description,
'canBeDeleted' => false,
'canBeUpdated' => true
);
}
}
}
} catch (\Throwable $e) {
$sError = "{$e->getCode()} {$e->getMessage()}";
$this->Logger()->Write($sError, \MailSo\Log\Enumerations\Type::ERROR, 'INSTALLER');
}
return $aResult;
}
public function DoAdminPackagesList() : array
{
$this->IsAdminLoggined();
$bReal = false;
$sError = '';
$aList = $this->getRepositoryData($bReal, $sError);
$aEnabledPlugins = \array_map('trim',
\explode(',', \strtolower($this->Config()->Get('plugins', 'enabled_list', '')))
);
$aInstalled = $this->Plugins()->InstalledPlugins();
foreach ($aInstalled as $aItem) {
if ($aItem) {
if (isset($aList[$aItem[0]])) {
$aList[$aItem[0]]['installed'] = $aItem[1];
$aList[$aItem[0]]['enabled'] = \in_array(\strtolower($aItem[0]), $aEnabledPlugins);
$aList[$aItem[0]]['canBeDeleted'] = true;
$aList[$aItem[0]]['canBeUpdated'] = \version_compare($aItem[1], $aList[$aItem[0]]['version'], '<');
} else {
\array_push($aList, array(
'type' => 'plugin',
'id' => $aItem[0],
'name' => $aItem[0],
'installed' => $aItem[1],
'enabled' => \in_array(\strtolower($aItem[0]), $aEnabledPlugins),
'version' => '',
'file' => '',
'release' => '',
'desc' => '',
'canBeDeleted' => true,
'canBeUpdated' => false
));
}
}
}
// \uksort($aList, function($a, $b){return \strcasecmp($a['name'], $b['name']);});
return $this->DefaultResponse(__FUNCTION__, array(
'Real' => $bReal,
'List' => \array_values($aList),
'Error' => $sError
));
}
public function DoAdminPackageDelete() : array
{
$this->IsAdminLoggined();
$sId = $this->GetActionParam('Id', '');
$bResult = static::deletePackageDir($sId);
$this->pluginEnable($sId, false);
return $this->DefaultResponse(__FUNCTION__, $bResult);
}
private static function deletePackageDir(string $sId) : bool
{
$sPath = APP_PLUGINS_PATH.$sId;
return (!\is_dir($sPath) || \MailSo\Base\Utils::RecRmDir($sPath))
&& (!\is_file("{$sPath}.phar") || \unlink("{$sPath}.phar"));
}
public function DoAdminPackageInstall() : array
{
$this->IsAdminLoggined();
$sType = $this->GetActionParam('Type', '');
$sId = $this->GetActionParam('Id', '');
$sFile = $this->GetActionParam('File', '');
$this->Logger()->Write('Start package install: '.$sFile.' ('.$sType.')', \MailSo\Log\Enumerations\Type::INFO, 'INSTALLER');
$sRealFile = '';
$bResult = false;
$sTmp = null;
try {
if ('plugin' === $sType) {
$bReal = false;
$sError = '';
$aList = $this->getRepositoryData($bReal, $sError);
if ($sError) {
throw new \Exception($sError);
}
if (isset($aList[$sId]) && $sFile === $aList[$sId]['file']) {
$sRealFile = $sFile;
$sTmp = \SnappyMail\Repository::download($sFile,
$this->Config()->Get('labs', 'curl_proxy', ''),
$this->Config()->Get('labs', 'curl_proxy_auth', '')
);
}
}
if ($sTmp) {
$oArchive = new \PharData($sTmp, 0, $sRealFile);
if (static::deletePackageDir($sId)) {
if ('.phar' === \substr($sRealFile, -5)) {
$bResult = \copy($sTmp, APP_PLUGINS_PATH . \basename($sRealFile));
} else {
$bResult = $oArchive->extractTo(\rtrim(APP_PLUGINS_PATH, '\\/'));
}
if (!$bResult) {
throw new \Exception('Cannot extract package files: '.$oArchive->getStatusString());
}
} else {
throw new \Exception('Cannot remove previous plugin folder: '.$sId);
}
}
} catch (\Throwable $e) {
$this->Logger()->Write("Install package {$sRealFile} failed: {$e->getMessage()}", \MailSo\Log\Enumerations\Type::ERROR, 'INSTALLER');
throw $e;
} finally {
$sTmp && \unlink($sTmp);
}
return $this->DefaultResponse(__FUNCTION__, $bResult ?
('plugin' !== $sType ? array('Reload' => true) : true) : false);
}
public function DoAdminPHPExtensions() : array
{
$aResult = [
@ -699,6 +483,31 @@ trait Admin
return $this->DefaultResponse(__FUNCTION__, $aResult);
}
public function DoAdminPackagesList() : array
{
return $this->DefaultResponse(__FUNCTION__, \SnappyMail\Repository::getPackagesList());
}
public function DoAdminPackageDelete() : array
{
$sId = $this->GetActionParam('Id', '');
$bResult = \SnappyMail\Repository::deletePackage($sId);
static::pluginEnable($sId, false);
return $this->DefaultResponse(__FUNCTION__, $bResult);
}
public function DoAdminPackageInstall() : array
{
$sType = $this->GetActionParam('Type', '');
$bResult = \SnappyMail\Repository::installPackage(
$sType,
$this->GetActionParam('Id', ''),
$this->GetActionParam('File', '')
);
return $this->DefaultResponse(__FUNCTION__, $bResult ?
('plugin' !== $sType ? array('Reload' => true) : true) : false);
}
private function pluginEnable(string $sName, bool $bEnable = true) : bool
{
if (!\strlen($sName))
@ -877,19 +686,6 @@ trait Admin
return $this->DefaultResponse(__FUNCTION__, true);
}
private function HasOneOfActionParams(array $aKeys) : bool
{
foreach ($aKeys as $sKey)
{
if ($this->HasActionParam($sKey))
{
return true;
}
}
return false;
}
private function setConfigFromParams(\RainLoop\Config\Application $oConfig, string $sParamName, string $sConfigSector, string $sConfigName, string $sType = 'string', ?callable $mStringCallback = null): void
{
$sValue = $this->GetActionParam($sParamName, '');

View file

@ -7,7 +7,7 @@ abstract class Repository
// snappyMailRepo
const BASE_URL = 'https://snappymail.eu/repository/v2/';
public static function get(string $path, string $proxy, string $proxy_auth) : string
private static function get(string $path, string $proxy, string $proxy_auth) : string
{
$oHTTP = HTTP\Request::factory(/*'socket' or 'curl'*/);
$oHTTP->proxy = $proxy;
@ -26,7 +26,7 @@ abstract class Repository
// $aRep = \json_decode($sRep);
public static function download(string $path, string $proxy, string $proxy_auth) : string
private static function download(string $path, string $proxy, string $proxy_auth) : string
{
$sTmp = APP_PRIVATE_DATA . \md5(\microtime(true).$path) . \preg_replace('/^.*?(\\.[a-z\\.]+)$/Di', '$1', $path);
$pDest = \fopen($sTmp, 'w+b');
@ -61,4 +61,239 @@ abstract class Repository
));
}
private static function getRepositoryDataByUrl(bool &$bReal = false) : array
{
$bReal = false;
$aRep = null;
$sRep = '';
$sRepoFile = 'packages.json';
$iRepTime = 0;
$oCache = \RainLoop\Api::Actions()->Cacher();
$sCacheKey = \RainLoop\KeyPathHelper::RepositoryCacheFile(static::BASE_URL, $sRepoFile);
$sRep = $oCache->Get($sCacheKey);
if ('' !== $sRep)
{
$iRepTime = $oCache->GetTimer($sCacheKey);
}
if ('' === $sRep || 0 === $iRepTime || \time() - 3600 > $iRepTime)
{
$sRep = static::get($sRepoFile,
\RainLoop\Api::Config()->Get('labs', 'curl_proxy', ''),
\RainLoop\Api::Config()->Get('labs', 'curl_proxy_auth', '')
);
if ($sRep)
{
$aRep = \json_decode($sRep);
$bReal = \is_array($aRep) && \count($aRep);
if ($bReal)
{
$oCache->Set($sCacheKey, $sRep);
$oCache->SetTimer($sCacheKey);
}
}
else
{
throw new \Exception('Cannot read remote repository file: '.$sRepoFile);
}
}
else if ('' !== $sRep)
{
$aRep = \json_decode($sRep, false, 10);
$bReal = \is_array($aRep) && \count($aRep);
}
return \is_array($aRep) ? $aRep : [];
}
private static function getRepositoryData(bool &$bReal, string &$sError) : array
{
$aResult = array();
try {
$notDev = '0.0.0' !== APP_VERSION;
foreach (static::getRepositoryDataByUrl($bReal) as $oItem) {
if ($oItem && isset($oItem->type, $oItem->id, $oItem->name,
$oItem->version, $oItem->release, $oItem->file, $oItem->description))
{
if ((!empty($oItem->required) && $notDev && \version_compare(APP_VERSION, $oItem->required, '<'))
|| (!empty($oItem->deprecated) && $notDev && \version_compare(APP_VERSION, $oItem->deprecated, '>='))
|| (isset($aResult[$oItem->id]) && \version_compare($aResult[$oItem->id]['version'], $oItem->version, '>'))
) {
continue;
}
if ('plugin' === $oItem->type) {
$aResult[$oItem->id] = array(
'type' => $oItem->type,
'id' => $oItem->id,
'name' => $oItem->name,
'installed' => '',
'enabled' => true,
'version' => $oItem->version,
'file' => $oItem->file,
'release' => $oItem->release,
'desc' => $oItem->description,
'canBeDeleted' => false,
'canBeUpdated' => true
);
}
}
}
} catch (\Throwable $e) {
$sError = "{$e->getCode()} {$e->getMessage()}";
\RainLoop\Api::Logger()->Write($sError, \MailSo\Log\Enumerations\Type::ERROR, 'INSTALLER');
}
return $aResult;
}
public static function getPackagesList() : array
{
\RainLoop\Api::Actions()->IsAdminLoggined();
$bReal = false;
$sError = '';
$aList = static::getRepositoryData($bReal, $sError);
$aEnabledPlugins = \array_map('trim',
\explode(',', \strtolower(\RainLoop\Api::Config()->Get('plugins', 'enabled_list', '')))
);
$aInstalled = \RainLoop\Api::Actions()->Plugins()->InstalledPlugins();
foreach ($aInstalled as $aItem) {
if ($aItem) {
if (isset($aList[$aItem[0]])) {
$aList[$aItem[0]]['installed'] = $aItem[1];
$aList[$aItem[0]]['enabled'] = \in_array(\strtolower($aItem[0]), $aEnabledPlugins);
$aList[$aItem[0]]['canBeDeleted'] = true;
$aList[$aItem[0]]['canBeUpdated'] = \version_compare($aItem[1], $aList[$aItem[0]]['version'], '<');
} else {
\array_push($aList, array(
'type' => 'plugin',
'id' => $aItem[0],
'name' => $aItem[0],
'installed' => $aItem[1],
'enabled' => \in_array(\strtolower($aItem[0]), $aEnabledPlugins),
'version' => '',
'file' => '',
'release' => '',
'desc' => '',
'canBeDeleted' => true,
'canBeUpdated' => false
));
}
}
}
// \uksort($aList, static function($a, $b){return \strcasecmp($a['name'], $b['name']);});
return array(
'Real' => $bReal,
'List' => \array_values($aList),
'Error' => $sError
);
}
public static function deletePackage(string $sId) : bool
{
\RainLoop\Api::Actions()->IsAdminLoggined();
return static::deletePackageDir($sId);
}
private static function deletePackageDir(string $sId) : bool
{
$sPath = APP_PLUGINS_PATH.$sId;
return (!\is_dir($sPath) || \MailSo\Base\Utils::RecRmDir($sPath))
&& (!\is_file("{$sPath}.phar") || \unlink("{$sPath}.phar"));
}
public static function installPackage(string $sType, string $sId, string $sFile) : bool
{
\RainLoop\Api::Actions()->IsAdminLoggined();
\RainLoop\Api::Logger()->Write('Start package install: '.$sFile.' ('.$sType.')', \MailSo\Log\Enumerations\Type::INFO, 'INSTALLER');
$sRealFile = '';
$bResult = false;
$sTmp = null;
try {
if ('plugin' === $sType) {
$bReal = false;
$sError = '';
$aList = static::getRepositoryData($bReal, $sError);
if ($sError) {
throw new \Exception($sError);
}
if (isset($aList[$sId]) && $sFile === $aList[$sId]['file']) {
$sRealFile = $sFile;
$sTmp = static::download($sFile,
\RainLoop\Api::Config()->Get('labs', 'curl_proxy', ''),
\RainLoop\Api::Config()->Get('labs', 'curl_proxy_auth', '')
);
}
}
if ($sTmp) {
$oArchive = new \PharData($sTmp, 0, $sRealFile);
if (static::deletePackageDir($sId)) {
if ('.phar' === \substr($sRealFile, -5)) {
$bResult = \copy($sTmp, APP_PLUGINS_PATH . \basename($sRealFile));
} else {
$bResult = $oArchive->extractTo(\rtrim(APP_PLUGINS_PATH, '\\/'));
}
if (!$bResult) {
throw new \Exception('Cannot extract package files: '.$oArchive->getStatusString());
}
} else {
throw new \Exception('Cannot remove previous plugin folder: '.$sId);
}
}
} catch (\Throwable $e) {
\RainLoop\Api::Logger()->Write("Install package {$sRealFile} failed: {$e->getMessage()}", \MailSo\Log\Enumerations\Type::ERROR, 'INSTALLER');
throw $e;
} finally {
$sTmp && \unlink($sTmp);
}
return $bResult;
}
private static function pluginEnable(string $sName, bool $bEnable = true) : bool
{
if (!\strlen($sName))
{
return false;
}
$oConfig = \RainLoop\Api::Config();
$sEnabledPlugins = $oConfig->Get('plugins', 'enabled_list', '');
$aEnabledPlugins = \explode(',', \strtolower($sEnabledPlugins));
$aEnabledPlugins = \array_map('trim', $aEnabledPlugins);
$aNewEnabledPlugins = array();
if ($bEnable)
{
$aNewEnabledPlugins = $aEnabledPlugins;
$aNewEnabledPlugins[] = $sName;
}
else
{
foreach ($aEnabledPlugins as $sPlugin)
{
if ($sName !== $sPlugin && \strlen($sPlugin))
{
$aNewEnabledPlugins[] = $sPlugin;
}
}
}
$oConfig->Set('plugins', 'enabled_list', \trim(\implode(',', \array_unique($aNewEnabledPlugins)), ' ,'));
return $oConfig->Save();
}
}