From 32059330dd2cc7cbb95aa022b8fffeaa9c5c26e2 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Mon, 10 Oct 2022 22:06:43 +0200 Subject: [PATCH] Redesign Nextcloud integration with better security and SSO #96 --- .../nextcloud/NextCloudSuggestions.php | 107 ------ .../_default_/plugins/nextcloud/index.php | 180 --------- .../snappymail/lib/AppInfo/Application.php | 16 +- .../lib/Controller/AjaxController.php | 4 +- .../lib/Controller/PageController.php | 80 +--- .../snappymail/lib/Util/SnappyMailHelper.php | 346 +++--------------- plugins/nextcloud/index.php | 91 ++++- 7 files changed, 162 insertions(+), 662 deletions(-) delete mode 100644 integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins/nextcloud/NextCloudSuggestions.php delete mode 100644 integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins/nextcloud/index.php diff --git a/integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins/nextcloud/NextCloudSuggestions.php b/integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins/nextcloud/NextCloudSuggestions.php deleted file mode 100644 index a99e9052e..000000000 --- a/integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins/nextcloud/NextCloudSuggestions.php +++ /dev/null @@ -1,107 +0,0 @@ -getContactsManager(); - if (!$cm && !$cm->isEnabled()) - { - return $aResult; - } - - $aSearchResult = $cm->search($sQuery, $aParams); - } - else if (\class_exists('OCP\Contacts') && \OCP\Contacts::isEnabled()) - { - $aSearchResult = \OCP\Contacts::search($sQuery, $aParams); - } - else - { - return $aResult; - } - - //$this->oLogger->WriteDump($aSearchResult); - - $aHashes = array(); - if (\is_array($aSearchResult) && 0 < \count($aSearchResult)) - { - foreach ($aSearchResult as $aContact) - { - if (0 >= $iLimit) - { - break; - } - - $sUid = empty($aContact['UID']) ? '' : $aContact['UID']; - if (!empty($sUid)) - { - $mEmails = isset($aContact['EMAIL']) ? $aContact['EMAIL'] : ''; - - $sFullName = isset($aContact['FN']) ? \trim($aContact['FN']) : ''; - if (empty($sFullName)) - { - $sFullName = isset($aContact['NICKNAME']) ? \trim($aContact['NICKNAME']) : ''; - } - - if (!\is_array($mEmails)) - { - $mEmails = array($mEmails); - } - - foreach ($mEmails as $sEmail) - { - $sHash = '"'.$sFullName.'" <'.$sEmail.'>'; - if (!isset($aHashes[$sHash])) - { - $aHashes[$sHash] = true; - $aResult[] = array($sEmail, $sFullName); - $iLimit--; - } - } - } - } - - $aResult = \array_slice($aResult, 0, $iInputLimit); - } - - unset($aSearchResult, $aHashes); - } - catch (\Throwable $oException) - { - if ($this->oLogger) - { - $this->oLogger->WriteException($oException); - } - } - - return $aResult; - } - - /** - * @param \MailSo\Log\Logger $oLogger - */ - public function SetLogger($oLogger) - { - $this->oLogger = $oLogger instanceof \MailSo\Log\Logger ? $oLogger : null; - } -} diff --git a/integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins/nextcloud/index.php b/integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins/nextcloud/index.php deleted file mode 100644 index 099d239a3..000000000 --- a/integrations/nextcloud/snappymail/app/data/_data_/_default_/plugins/nextcloud/index.php +++ /dev/null @@ -1,180 +0,0 @@ -addHook('main.fabrica', 'MainFabrica'); - $this->addHook('filter.app-data', 'FilterAppData'); - $this->addHook('json.attachments', 'DoAttachmentsActions'); - - $sAppPath = ''; - if (\class_exists('OC_App')) { - $sAppPath = \rtrim(\trim(\OC_App::getAppWebPath('snappymail')), '\\/').'/app/'; - } - if (!$sAppPath) { - $sUrl = \MailSo\Base\Http::SingletonInstance()->GetUrl(); - if ($sUrl && \preg_match('/\/index\.php\/apps\/snappymail/', $sUrl)) { - $sAppPath = \preg_replace('/\/index\.php\/apps\/snappymail.+$/', - '/apps/snappymail/app/', $sUrl); - } - } - $_SERVER['SCRIPT_NAME'] = $sAppPath; - } - } - - public function Supported() : string - { - if (!static::IsNextCloud()) { - return 'NextCloud not found to use this plugin'; - } - return ''; - } - - // DoAttachmentsActions - public function DoAttachmentsActions(\SnappyMail\AttachmentsAction $data) - { - if ('nextcloud' === $data->action) { - if (static::IsNextCloudLoggedIn() && \class_exists('OCP\Files')) { - $oFiles = \OCP\Files::getStorage('files'); - if ($oFiles && $data->filesProvider->IsActive() && \method_exists($oFiles, 'file_put_contents')) { - $sSaveFolder = $this->Config()->Get('plugin', 'save_folder', '') ?: 'Attachments'; - $oFiles->is_dir($sSaveFolder) || $oFiles->mkdir($sSaveFolder); - $data->result = true; - foreach ($data->items as $aItem) { - $sSavedFileName = isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat'; - $sSavedFileHash = !empty($aItem['FileHash']) ? $aItem['FileHash'] : ''; - if (!empty($sSavedFileHash)) { - $fFile = $data->filesProvider->GetFile($data->account, $sSavedFileHash, 'rb'); - if (\is_resource($fFile)) { - $sSavedFileNameFull = \MailSo\Base\Utils::SmartFileExists($sSaveFolder.'/'.$sSavedFileName, function ($sPath) use ($oFiles) { - return $oFiles->file_exists($sPath); - }); - - if (!$oFiles->file_put_contents($sSavedFileNameFull, $fFile)) { - $data->result = false; - } - - if (\is_resource($fFile)) { - \fclose($fFile); - } - } - } - } - } - } - - foreach ($data->items as $aItem) { - $sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : ''); - if (!empty($sFileHash)) { - $data->filesProvider->Clear($data->account, $sFileHash); - } - } - } - } - - /** - * TODO: create pre-login auth hook - */ - public function ServiceNextCloudAuth() - { -/* - $this->oHttp->ServerNoCache(); - - if (!static::IsNextCloud() || - !isset($_ENV['___snappymail_nextcloud_email']) || - !isset($_ENV['___snappymail_nextcloud_password']) || - empty($_ENV['___snappymail_nextcloud_email']) - ) - { - $this->oActions->SetAuthLogoutToken(); - $this->oActions->Location('./'); - return ''; - } - - $bLogout = true; - - $sEmail = $_ENV['___snappymail_nextcloud_email']; - $sPassword = $_ENV['___snappymail_nextcloud_password']; - - try - { - $oAccount = $this->oActions->LoginProcess($sEmail, $sPassword); - $this->oActions->AuthToken($oAccount); - - $bLogout = !($oAccount instanceof \snappymail\Model\Account); - } - catch (\Exception $oException) - { - $this->oActions->Logger()->WriteException($oException); - } - - if ($bLogout) - { - $this->oActions->SetAuthLogoutToken(); - } - - $this->oActions->Location('./'); - return ''; -*/ - } - - /** - * @return void - */ - public function FilterAppData($bAdmin, &$aResult) - { - if (!$bAdmin && \is_array($aResult) && static::IsNextCloud()) { - $key = \array_search(\RainLoop\Enumerations\Capa::AUTOLOGOUT, $aResult['Capa']); - if (false !== $key) { - unset($aResult['Capa'][$key]); - } - if (static::IsNextCloudLoggedIn() && \class_exists('OCP\Files')) { - $aResult['System']['attachmentsActions'][] = 'nextcloud'; - } - } - } - - /** - * @param string $sName - * @param mixed $mResult - */ - public function MainFabrica($sName, &$mResult) - { - if ('suggestions' === $sName && static::IsNextCloud() && $this->Config()->Get('plugin', 'suggestions', true)) { - include_once __DIR__.'/NextCloudSuggestions.php'; - if (!\is_array($mResult)) { - $mResult = array(); - } - $mResult[] = new NextCloudSuggestions(); - } - } - - protected function configMapping() : array - { - return array( - \RainLoop\Plugins\Property::NewInstance('save_folder')->SetLabel('Save Folder') - ->SetDefaultValue('Attachments'), - \RainLoop\Plugins\Property::NewInstance('suggestions')->SetLabel('Suggestions') - ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) - ->SetDefaultValue(true) - ); - } -} diff --git a/integrations/nextcloud/snappymail/lib/AppInfo/Application.php b/integrations/nextcloud/snappymail/lib/AppInfo/Application.php index d1d81e6bd..0b15bd017 100644 --- a/integrations/nextcloud/snappymail/lib/AppInfo/Application.php +++ b/integrations/nextcloud/snappymail/lib/AppInfo/Application.php @@ -16,8 +16,8 @@ class Application extends App { parent::__construct('snappymail', $urlParams); $container = $this->getContainer(); - $server = $container->getServer(); - $config = $server->getConfig(); +// $server = $container->getServer(); +// $config = $server->getConfig(); /** * Controllers @@ -26,10 +26,7 @@ class Application extends App { 'PageController', function($c) { return new PageController( $c->query('AppName'), - $c->query('Request'), - $c->getServer()->getAppManager(), - $c->query('ServerContainer')->getConfig(), - $c->getServer()->getSession() + $c->query('Request') ); } ); @@ -51,12 +48,7 @@ class Application extends App { */ $container->registerService( 'SnappyMailHelper', function($c) { - return new SnappyMailHelper( - $c->getServer()->getConfig(), - $c->getServer()->getUserSession(), - $c->getServer()->getAppManager(), - $c->getServer()->getSession() - ); + return new SnappyMailHelper(); } ); diff --git a/integrations/nextcloud/snappymail/lib/Controller/AjaxController.php b/integrations/nextcloud/snappymail/lib/Controller/AjaxController.php index e0a561138..8fbe5aeb2 100644 --- a/integrations/nextcloud/snappymail/lib/Controller/AjaxController.php +++ b/integrations/nextcloud/snappymail/lib/Controller/AjaxController.php @@ -67,10 +67,10 @@ class AjaxController extends Controller { $sPass = $_POST['snappymail-password']; if ('******' !== $sPass && '' !== $sPass) { - include_once $this->appManager->getAppPath('snappymail').'/lib/Util/SnappyMailHelper.php'; + require_once $this->appManager->getAppPath('snappymail').'/lib/Util/SnappyMailHelper.php'; $this->config->setUserValue($sUser, 'snappymail', 'snappymail-password', - SnappyMailHelper::encodePassword($sPass, md5($sPostEmail))); + SnappyMailHelper::encodePassword($sPass, \md5($sPostEmail))); } $sEmail = $this->config->getUserValue($sUser, 'snappymail', 'snappymail-email', ''); diff --git a/integrations/nextcloud/snappymail/lib/Controller/PageController.php b/integrations/nextcloud/snappymail/lib/Controller/PageController.php index 2279430e3..48d3e0d18 100644 --- a/integrations/nextcloud/snappymail/lib/Controller/PageController.php +++ b/integrations/nextcloud/snappymail/lib/Controller/PageController.php @@ -4,42 +4,26 @@ namespace OCA\SnappyMail\Controller; use OCA\SnappyMail\Util\SnappyMailHelper; -use OCP\App\IAppManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\TemplateResponse; -use OCP\IConfig; -use OCP\IRequest; -use OCP\ISession; - -class PageController extends Controller { - private $userId; - private $config; - private $appManager; - private $appPath; - private $session; - - public function __construct($AppName, IRequest $request, IAppManager $appManager, IConfig $config, ISession $session) { - parent::__construct($AppName, $request); - $this->appPath = $appManager->getAppPath('snappymail'); - $this->config = $config; - $this->appManager = $appManager; - $this->session = $session; - } +class PageController extends Controller +{ /** * @NoAdminRequired * @NoCSRFRequired */ - public function index() { + public function index() + { \OC::$server->getNavigationManager()->setActiveEntry('snappymail'); \OCP\Util::addStyle('snappymail', 'style'); - $sUrl = SnappyMailHelper::normalizeUrl(SnappyMailHelper::getAppUrl()); - + $session = \OC::$server->getSession(); $params = [ - 'snappymail-iframe-url' => SnappyMailHelper::normalizeUrl($sUrl).'?OwnCloudAuth' + 'snappymail-iframe-url' => SnappyMailHelper::normalizeUrl(SnappyMailHelper::getAppUrl()) + . (empty($session['snappymail-sso-hash']) ? '' : '?sso&hash=' . $session['snappymail-sso-hash']) ]; $response = new TemplateResponse('snappymail', 'index', $params); @@ -55,56 +39,18 @@ class PageController extends Controller { * @NoAdminRequired * @NoCSRFRequired */ - public function appGet() { - $this->app(); + public function appGet() + { + SnappyMailHelper::startApp(); } /** * @NoAdminRequired * @NoCSRFRequired */ - public function appPost() { - $this->app(); + public function appPost() + { + SnappyMailHelper::startApp(); } - - public function app() { - - SnappyMailHelper::regSnappyMailDataFunction(); - - if (isset($_GET['OwnCloudAuth'])) { - $sEmail = ''; - $sEncodedPassword = ''; - - $sUser = \OC::$server->getUserSession()->getUser()->getUID(); - - if ($this->config->getAppValue('snappymail', 'snappymail-autologin', false)) { - $sEmail = $sUser; - $sPasswordSalt = $sUser; - $sEncodedPassword = $this->session['snappymail-autologin-password']; - } else if ($this->config->getAppValue('snappymail', 'snappymail-autologin-with-email', false)) { - $sEmail = $this->config->getUserValue($sUser, 'settings', 'email', ''); - $sPasswordSalt = $sUser; - $sEncodedPassword = $this->session['snappymail-autologin-password']; - } - - // If the user has set credentials for SnappyMail in their personal - // settings, override everything before and use those instead. - $sIndividualEmail = $this->config->getUserValue($sUser, 'snappymail', 'snappymail-email', ''); - if ($sIndividualEmail) { - $sEmail = $sIndividualEmail; - $sPasswordSalt = $sEmail; - $sEncodedPassword = $this->config->getUserValue($sUser, 'snappymail', 'snappymail-password', ''); - } - - $sDecodedPassword = SnappyMailHelper::decodePassword($sEncodedPassword, md5($sPasswordSalt)); - - $_ENV['___snappymail_owncloud_email'] = $sEmail; - $_ENV['___snappymail_owncloud_password'] = $sDecodedPassword; - } - - include $this->appPath . '/app/index.php'; - - } - } diff --git a/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php b/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php index 9191b52f8..b9d5c6653 100644 --- a/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php +++ b/integrations/nextcloud/snappymail/lib/Util/SnappyMailHelper.php @@ -2,33 +2,59 @@ namespace OCA\SnappyMail\Util; -use OCP\App\IAppManager; -use OCP\IConfig; -use OCP\ISession; -use OCP\IUserSession; +class SnappyMailHelper +{ -class SnappyMailHelper { + public function registerHooks() + { + $config = \OC::$server->getConfig(); + $session = \OC::$server->getSession(); + $userSession = \OC::$server->getUserSession(); + $userSession->listen('\OC\User', 'postLogin', function($user, $loginName, $password, $isTokenLogin) { + $sEmail = ''; + // If the user has set credentials for SnappyMail in their personal + // settings, override everything before and use those instead. + $sIndividualEmail = $config->getUserValue($user->getUID(), 'snappymail', 'snappymail-email', ''); + if ($sIndividualEmail) { + $sEmail = $sIndividualEmail; + $password = SnappyMailHelper::decodePassword( + $this->config->getUserValue($sUser, 'snappymail', 'snappymail-password', ''), + \md5($sEmail) + ); + } + // Only store the user's password in the current session if they have + // enabled auto-login using Nextcloud username or email address. + else if ($config->getAppValue('snappymail', 'snappymail-autologin', false)) { + $sEmail = $user->getUID(); + } else if ($config->getAppValue('snappymail', 'snappymail-autologin-with-email', false)) { + $sEmail = $config->getUserValue($user->getUID(), 'settings', 'email', ''); + } + if ($sEmail) { + static::startApp(true); + $session['snappymail-sso-hash'] = \RainLoop\Api::CreateUserSsoHash($sEmail, $password/*, array $aAdditionalOptions = array(), bool $bUseTimeout = true*/); + } + }); - private $appManager; - private $config; - private $session; - private $userSession; - - public function __construct(IConfig $config, IUserSession $userSession, IAppManager $appManager, ISession $session) { - $this->appManager = $appManager; - $this->config = $config; - $this->userSession = $userSession; - $this->session = $session; + $userSession->listen('\OC\User', 'logout', function($user) { + $session['snappymail-sso-hash'] = ''; + static::startApp(true); + \RainLoop\Api::LogoutCurrentLogginedUser(); + }); } - public function registerHooks() { - $this->userSession->listen('\OC\User', 'postLogin', function($user, $loginName, $password, $isTokenLogin) { - $this->login($user, $loginName, $password, $isTokenLogin, $this->config, $this->session); - }); - - $this->userSession->listen('\OC\User', 'logout', function() { - $this->logout($this->appManager, $this->config, $this->session); - }); + public static function startApp(bool $api = false) + { + if (!\class_exists('RainLoop\\Api')) { + if ($api) { + $_ENV['SNAPPYMAIL_INCLUDE_AS_API'] = true; + } + $_ENV['SNAPPYMAIL_NEXTCLOUD'] = true; + $sData = \rtrim(\trim(\OC::$server->getSystemConfig()->getValue('datadirectory', '')), '\\/').'/'; + if (\is_dir($sData)) { + \define('APP_DATA_FOLDER_PATH', $sData); + } + require_once \OC::$server->getAppManager()->getAppPath('snappymail') . '/app/index.php'; + } } /** @@ -37,44 +63,13 @@ class SnappyMailHelper { public static function getAppUrl() { $sRequestUri = \OC::$server->getURLGenerator()->linkToRoute('snappymail.page.appGet'); - if ($sRequestUri) - { + if ($sRequestUri) { return $sRequestUri; } - - $sRequestUri = empty($_SERVER['REQUEST_URI']) ? '': trim($_SERVER['REQUEST_URI']); - $sRequestUri = preg_replace('/index.php\/.+$/', 'index.php/', $sRequestUri); + $sRequestUri = empty($_SERVER['REQUEST_URI']) ? '': \trim($_SERVER['REQUEST_URI']); + $sRequestUri = \preg_replace('/index.php\/.+$/', 'index.php/', $sRequestUri); $sRequestUri = $sRequestUri.'apps/snappymail/app/'; - - return '/'.ltrim($sRequestUri, '/\\'); - } - - /** - * @param string $sPath - * @param string $sEmail - * @param string $sPassword - * - * @return string - */ - public static function getSsoHash($sPath, $sEmail, $sPassword) - { - $SsoHash = ''; - - $sPath = rtrim(trim($sPath), '\\/').'/index.php'; - if (file_exists($sPath)) - { - self::regSnappyMailDataFunction(); - - $_ENV['SNAPPYMAIL_INCLUDE_AS_API'] = true; - include $sPath; - - if (class_exists('\\RainLoop\\Api')) - { - $SsoHash = \RainLoop\Api::GetUserSsoHash($sEmail, $sPassword); - } - } - - return $SsoHash; + return '/'.\ltrim($sRequestUri, '/\\'); } /** @@ -84,83 +79,14 @@ class SnappyMailHelper { */ public static function normalizeUrl($sUrl) { - $sUrl = rtrim(trim($sUrl), '/\\'); - if ('.php' !== strtolower(substr($sUrl, -4))) - { + $sUrl = \rtrim(\trim($sUrl), '/\\'); + if ('.php' !== \strtolower(\substr($sUrl, -4))) { $sUrl .= '/'; } return $sUrl; } - /** - * @return boolean - */ - public static function mcryptSupported() - { - return function_exists('mcrypt_encrypt') && - function_exists('mcrypt_decrypt') && - defined('MCRYPT_RIJNDAEL_256') && - defined('MCRYPT_MODE_ECB'); - } - - /** - * @return string - */ - public static function openSslSupportedMethod() - { - $method = 'AES-256-CBC'; - return function_exists('openssl_encrypt') && - function_exists('openssl_decrypt') && - function_exists('openssl_random_pseudo_bytes') && - function_exists('openssl_cipher_iv_length') && - function_exists('openssl_get_cipher_methods') && - defined('OPENSSL_RAW_DATA') && defined('OPENSSL_ZERO_PADDING') && - in_array($method, openssl_get_cipher_methods()) ? $method : ''; - } - - /** - * @param string $sMethod - * @param string $sPassword - * @param string $sSalt - * - * @return string - */ - public static function encodePasswordSsl($sMethod, $sPassword, $sSalt) - { - $sData = base64_encode($sPassword); - - $iv = @openssl_random_pseudo_bytes(openssl_cipher_iv_length($sMethod)); - $r = @openssl_encrypt($sData, $sMethod, md5($sSalt), OPENSSL_RAW_DATA, $iv); - - return @base64_encode(base64_encode($r).'|'.base64_encode($iv)); - } - - /** - * @param string $sMethod - * @param string $sPassword - * @param string $sSalt - * - * @return string - */ - public static function decodePasswordSsl($sMethod, $sPassword, $sSalt) - { - $sLine = base64_decode(trim($sPassword)); - $aParts = explode('|', $sLine, 2); - - if (is_array($aParts) && !empty($aParts[0]) && !empty($aParts[1])) { - - $sData = @base64_decode($aParts[0]); - $iv = @base64_decode($aParts[1]); - - return @base64_decode(trim( - @openssl_decrypt($sData, $sMethod, md5($sSalt), OPENSSL_RAW_DATA, $iv) - )); - } - - return ''; - } - /** * @param string $sPassword * @param string $sSalt @@ -169,19 +95,8 @@ class SnappyMailHelper { */ public static function encodePassword($sPassword, $sSalt) { - $method = self::openSslSupportedMethod(); - if ($method) - { - return self::encodePasswordSsl($method, $sPassword, $sSalt); - } - else if (self::mcryptSupported()) - { - return @trim(base64_encode( - @mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($sSalt), base64_encode($sPassword), MCRYPT_MODE_ECB) - )); - } - - return @trim(base64_encode($sPassword)); + static::startApp(true); + return \SnappyMail\Crypt::EncryptUrlSafe($sPassword, $sSalt); } /** @@ -192,150 +107,7 @@ class SnappyMailHelper { */ public static function decodePassword($sPassword, $sSalt) { - $method = self::openSslSupportedMethod(); - if ($method) - { - return self::decodePasswordSsl($method, $sPassword, $sSalt); - } - else if (self::mcryptSupported()) - { - return @base64_decode(trim( - @mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($sSalt), base64_decode(trim($sPassword)), MCRYPT_MODE_ECB) - )); - } - - return @base64_decode(trim($sPassword)); + static::startApp(true); + return \SnappyMail\Crypt::DecryptUrlSafe($sPassword, $sSalt); } - - /** - * @param array $aParams - * @return boolean - * @UseSession - */ - public static function login($user, $loginName, $password, $isTokenLogin, $config, &$session) - { - $sUser = $user->getUID(); - - $sEmail = $sUser; - $sPassword = $password; - $sEncodedPassword = self::encodePassword($sPassword, md5($sEmail)); - - // Only store the user's password in the current session if they have - // enabled auto-login using Nextcloud username or email address. - if ($config->getAppValue('snappymail', 'snappymail-autologin') || $config->getAppValue('snappymail', 'snappymail-autologin-with-email')) { - $session['snappymail-autologin-password'] = $sEncodedPassword; - } - } - - /** - * @UseSession - */ - public static function logout($appManager, $config, &$session) - { - $session['snappymail-autologin-password'] = ''; - - $sApiPath = $appManager->getAppPath('snappymail') . '/app/index.php'; - - self::regSnappyMailDataFunction(); - - $_ENV['SNAPPYMAIL_INCLUDE_AS_API'] = true; - include $sApiPath; - - \RainLoop\Api::LogoutCurrentLogginedUser(); - - return true; - } - - public static function regSnappyMailDataFunction() - { - if (!@function_exists('__get_custom_data_full_path')) - { - $_ENV['SNAPPYMAIL_NEXTCLOUD'] = true; - - function __get_custom_data_full_path() - { - $sData = rtrim(trim(OC::$server->getSystemConfig()->getValue('datadirectory', '')), '\\/').'/'; - return @is_dir($sData) ? $sData.'snappymail-storage' : ''; - } - } - } - - public static function mimeContentType($filename) { - - $mime_types = array( - - 'woff' => 'application/font-woff', - - 'txt' => 'text/plain', - 'htm' => 'text/html', - 'html' => 'text/html', - 'php' => 'text/html', - 'css' => 'text/css', - 'js' => 'application/javascript', - 'json' => 'application/json', - 'xml' => 'application/xml', - 'swf' => 'application/x-shockwave-flash', - 'flv' => 'video/x-flv', - - // images - 'png' => 'image/png', - 'jpe' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'gif' => 'image/gif', - 'bmp' => 'image/bmp', - 'ico' => 'image/vnd.microsoft.icon', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'svg' => 'image/svg+xml', - 'svgz' => 'image/svg+xml', - - // archives - 'zip' => 'application/zip', - 'rar' => 'application/x-rar-compressed', - 'exe' => 'application/x-msdownload', - 'msi' => 'application/x-msdownload', - 'cab' => 'application/vnd.ms-cab-compressed', - - // audio/video - 'mp3' => 'audio/mpeg', - 'qt' => 'video/quicktime', - 'mov' => 'video/quicktime', - - // adobe - 'pdf' => 'application/pdf', - 'psd' => 'image/vnd.adobe.photoshop', - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', - - // ms office - 'doc' => 'application/msword', - 'rtf' => 'application/rtf', - 'xls' => 'application/vnd.ms-excel', - 'ppt' => 'application/vnd.ms-powerpoint', - - // open office - 'odt' => 'application/vnd.oasis.opendocument.text', - 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', - ); - - if (0 < strpos($filename, '.')) - { - $ext = strtolower(array_pop(explode('.',$filename))); - if (array_key_exists($ext, $mime_types)) - { - return $mime_types[$ext]; - } - else if (function_exists('finfo_open')) - { - $finfo = finfo_open(FILEINFO_MIME); - $mimetype = finfo_file($finfo, $filename); - finfo_close($finfo); - return $mimetype; - } - } - - return 'application/octet-stream'; - } } diff --git a/plugins/nextcloud/index.php b/plugins/nextcloud/index.php index 731d5b663..0a21667f3 100644 --- a/plugins/nextcloud/index.php +++ b/plugins/nextcloud/index.php @@ -4,26 +4,39 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin { const NAME = 'Nextcloud', - VERSION = '1.0', + VERSION = '2.1', RELEASE = '2022-10-10', CATEGORY = 'Integrations', - DESCRIPTION = 'Integrate with Nextcloud', + DESCRIPTION = 'Integrate with Nextcloud v18+', REQUIRED = '2.15.2'; public function Init() : void { - $this->addHook('main.fabrica', 'MainFabrica'); + if (static::IsIntegrated()) { + $this->addHook('main.fabrica', 'MainFabrica'); + $this->addHook('filter.app-data', 'FilterAppData'); + $this->addHook('json.attachments', 'SaveAttachments'); + + $sAppPath = \rtrim(\trim(\OC::$server->getAppManager()->getAppWebPath()), '\\/').'/app/'; + if (!$sAppPath) { + $sUrl = \MailSo\Base\Http::SingletonInstance()->GetUrl(); + if (\str_contains($sUrl, '/index.php/apps/snappymail/')) { + $sAppPath = \preg_replace('/\/index\.php\/apps\/snappymail.+$/', + '/apps/snappymail/app/', $sUrl); + } + } + $_SERVER['SCRIPT_NAME'] = $sAppPath; + } } public function Supported() : string { - return static::IsIntegrated() ? '' : 'Not running inside Nextcloud'; + return static::IsIntegrated() ? '' : 'Nextcloud not found to use this plugin'; } public static function IsIntegrated() { -// return !empty($_ENV['RAINLOOP_OWNCLOUD']) && - return \class_exists('OC') && isset(\OC::$server); + return !empty($_ENV['SNAPPYMAIL_NEXTCLOUD']) && \class_exists('OC') && isset(\OC::$server); } public static function IsLoggedIn() @@ -31,13 +44,66 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin return static::IsIntegrated() && \OC::$server->getUserSession()->isLoggedIn(); } + // DoAttachmentsActions + public function SaveAttachments(\SnappyMail\AttachmentsAction $data) + { + if ('nextcloud' === $data->action) { + $oFiles = \OCP\Files::getStorage('files'); + if ($oFiles && $data->filesProvider->IsActive() && \method_exists($oFiles, 'file_put_contents')) { + $sSaveFolder = $this->Config()->Get('plugin', 'save_folder', '') ?: 'Attachments'; + $oFiles->is_dir($sSaveFolder) || $oFiles->mkdir($sSaveFolder); + $data->result = true; + foreach ($data->items as $aItem) { + $sSavedFileName = isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat'; + $sSavedFileHash = !empty($aItem['FileHash']) ? $aItem['FileHash'] : ''; + if (!empty($sSavedFileHash)) { + $fFile = $data->filesProvider->GetFile($data->account, $sSavedFileHash, 'rb'); + if (\is_resource($fFile)) { + $sSavedFileNameFull = \MailSo\Base\Utils::SmartFileExists($sSaveFolder.'/'.$sSavedFileName, function ($sPath) use ($oFiles) { + return $oFiles->file_exists($sPath); + }); + + if (!$oFiles->file_put_contents($sSavedFileNameFull, $fFile)) { + $data->result = false; + } + + if (\is_resource($fFile)) { + \fclose($fFile); + } + } + } + } + } + + foreach ($data->items as $aItem) { + $sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : ''); + if (!empty($sFileHash)) { + $data->filesProvider->Clear($data->account, $sFileHash); + } + } + } + } + + public function FilterAppData($bAdmin, &$aResult) : void + { + if (!$bAdmin && \is_array($aResult) && static::IsIntegrated()) { + $key = \array_search(\RainLoop\Enumerations\Capa::AUTOLOGOUT, $aResult['Capa']); + if (false !== $key) { + unset($aResult['Capa'][$key]); + } + if (static::IsLoggedIn() && \class_exists('OCP\Files')) { + $aResult['System']['attachmentsActions'][] = 'nextcloud'; + } + } + } + /** * @param mixed $mResult */ public function MainFabrica(string $sName, &$mResult) { if (static::isLoggedIn()) { - if ('suggestions' === $sName) { + if ('suggestions' === $sName && $this->Config()->Get('plugin', 'suggestions', true)) { if (!\is_array($mResult)) { $mResult = array(); } @@ -46,4 +112,15 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin } } } + + protected function configMapping() : array + { + return array( + \RainLoop\Plugins\Property::NewInstance('save_folder')->SetLabel('Save Folder') + ->SetDefaultValue('Attachments'), + \RainLoop\Plugins\Property::NewInstance('suggestions')->SetLabel('Suggestions') + ->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL) + ->SetDefaultValue(true) + ); + } }