From 3251611c6d5977f6d5f92f33971784bd2fad3f91 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Fri, 4 Mar 2022 16:09:50 +0100 Subject: [PATCH] Move admin repository handling code to \SnappyMail\Repository --- .../app/libraries/RainLoop/Actions/Admin.php | 254 ++---------------- .../app/libraries/snappymail/repository.php | 239 +++++++++++++++- 2 files changed, 262 insertions(+), 231 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php index 8d8eed88f..d2dd5a0eb 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Admin.php @@ -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, ''); diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/repository.php b/snappymail/v/0.0.0/app/libraries/snappymail/repository.php index 2f3d4bdf4..614eb1ed5 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/repository.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/repository.php @@ -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(); + } }