Cleanup some classes and improve the Accounts and Contacts

This commit is contained in:
djmaze 2021-11-10 21:24:19 +01:00
parent 7ea4365ccb
commit ef4790d04e
25 changed files with 159 additions and 192 deletions

View file

@ -11,7 +11,7 @@
namespace MailSo\Imap\Commands; namespace MailSo\Imap\Commands;
use \MailSo\Imap\Responses\ACL as ACLResponse; use MailSo\Imap\Responses\ACL as ACLResponse;
/** /**
* @category MailSo * @category MailSo

View file

@ -11,7 +11,7 @@
namespace MailSo\Imap; namespace MailSo\Imap;
use \MailSo\Imap\Enumerations\ResponseType; use MailSo\Imap\Enumerations\ResponseType;
/** /**
* @category MailSo * @category MailSo

View file

@ -11,9 +11,9 @@
namespace MailSo\Imap\Traits; namespace MailSo\Imap\Traits;
use \MailSo\Imap\Response; use MailSo\Imap\Response;
use \MailSo\Imap\Enumerations\ResponseType; use MailSo\Imap\Enumerations\ResponseType;
use \MailSo\Imap\Exceptions\ResponseNotFoundException; use MailSo\Imap\Exceptions\ResponseNotFoundException;
/** /**
* @category MailSo * @category MailSo

View file

@ -11,7 +11,7 @@
namespace MailSo\Mail; namespace MailSo\Mail;
use \MailSo\Imap\Enumerations\FolderResponseStatus; use MailSo\Imap\Enumerations\FolderResponseStatus;
/** /**
* @category MailSo * @category MailSo

View file

@ -245,8 +245,6 @@ class Actions
$mResult = new Providers\AddressBook\PdoAddressBook($sDsn, $sUser, $sPassword, $sDsnType); $mResult = new Providers\AddressBook\PdoAddressBook($sDsn, $sUser, $sPassword, $sDsnType);
break; break;
case 'identities': case 'identities':
$mResult = [];
break;
case 'suggestions': case 'suggestions':
$mResult = []; $mResult = [];
break; break;
@ -1117,52 +1115,6 @@ class Actions
); );
} }
public function GetAccounts(Model\Account $oAccount): array
{
if ($this->GetCapa(false, Enumerations\Capa::ADDITIONAL_ACCOUNTS, $oAccount)) {
$sAccounts = $this->StorageProvider()->Get($oAccount,
Providers\Storage\Enumerations\StorageType::CONFIG,
'accounts'
);
$aAccounts = array();
if ('' !== $sAccounts && '{' === \substr($sAccounts, 0, 1)) {
$aAccounts = \json_decode($sAccounts, true);
}
if (\is_array($aAccounts) && \count($aAccounts)) {
if (1 === \count($aAccounts)) {
$this->SetAccounts($oAccount, array());
} else if (1 < \count($aAccounts)) {
$sOrder = $this->StorageProvider()->Get($oAccount,
Providers\Storage\Enumerations\StorageType::CONFIG,
'accounts_identities_order'
);
$aOrder = empty($sOrder) ? array() : \json_decode($sOrder, true);
if (isset($aOrder['Accounts']) && \is_array($aOrder['Accounts']) &&
1 < \count($aOrder['Accounts'])) {
$aAccounts = \array_merge(\array_flip($aOrder['Accounts']), $aAccounts);
$aAccounts = \array_filter($aAccounts, function ($sHash) {
return 5 < \strlen($sHash);
});
}
}
return $aAccounts;
}
}
$aAccounts = array();
if (!$oAccount->IsAdditionalAccount()) {
$aAccounts[$oAccount->Email()] = $oAccount->GetAuthToken();
}
return $aAccounts;
}
public function GetIdentityByID(Model\Account $oAccount, string $sID, bool $bFirstOnEmpty = false): ?Model\Identity public function GetIdentityByID(Model\Account $oAccount, string $sID, bool $bFirstOnEmpty = false): ?Model\Identity
{ {
$aIdentities = $this->GetIdentities($oAccount); $aIdentities = $this->GetIdentities($oAccount);
@ -1176,24 +1128,6 @@ class Actions
return $bFirstOnEmpty && isset($aIdentities[0]) ? $aIdentities[0] : null; return $bFirstOnEmpty && isset($aIdentities[0]) ? $aIdentities[0] : null;
} }
public function SetAccounts(Model\Account $oAccount, array $aAccounts = array()): void
{
$sParentEmail = $oAccount->ParentEmailHelper();
if (!$aAccounts ||
(1 === \count($aAccounts) && !empty($aAccounts[$sParentEmail]))) {
$this->StorageProvider()->Clear($oAccount,
Providers\Storage\Enumerations\StorageType::CONFIG,
'accounts'
);
} else {
$this->StorageProvider()->Put($oAccount,
Providers\Storage\Enumerations\StorageType::CONFIG,
'accounts',
\json_encode($aAccounts)
);
}
}
/** /**
* @throws \MailSo\Base\Exceptions\Exception * @throws \MailSo\Base\Exceptions\Exception
*/ */

View file

@ -8,11 +8,72 @@ use RainLoop\Model\Account;
use RainLoop\Model\Identity; use RainLoop\Model\Identity;
use RainLoop\Notifications; use RainLoop\Notifications;
use RainLoop\Providers\Storage\Enumerations\StorageType; use RainLoop\Providers\Storage\Enumerations\StorageType;
use function trim;
trait Accounts trait Accounts
{ {
public function GetAccounts(Account $oAccount): array
{
if ($this->GetCapa(false, Capa::ADDITIONAL_ACCOUNTS, $oAccount)) {
$sAccounts = $this->StorageProvider()->Get($oAccount,
StorageType::CONFIG,
'accounts'
);
$aAccounts = $sAccounts ? \json_decode($sAccounts, true) : array();
if (\is_array($aAccounts) && \count($aAccounts)) {
if (1 === \count($aAccounts)) {
$this->SetAccounts($oAccount, array());
} else if (1 < \count($aAccounts)) {
$sOrder = $this->StorageProvider()->Get($oAccount,
StorageType::CONFIG,
'accounts_identities_order'
);
$aOrder = empty($sOrder) ? array() : \json_decode($sOrder, true);
if (isset($aOrder['Accounts']) && \is_array($aOrder['Accounts']) && 1 < \count($aOrder['Accounts'])) {
$aAccounts = \array_merge(\array_flip($aOrder['Accounts']), $aAccounts);
$aAccounts = \array_filter($aAccounts, function ($sHash) {
return 5 < \strlen($sHash);
});
}
}
return $aAccounts;
}
}
$aAccounts = array();
if (!$oAccount->IsAdditionalAccount()) {
$aAccounts[$oAccount->Email()] = $oAccount->GetAuthToken();
}
return $aAccounts;
}
protected function SetAccounts(Account $oAccount, array $aAccounts = array()): void
{
$sParentEmail = $oAccount->ParentEmailHelper();
if (!$aAccounts ||
(1 === \count($aAccounts) && !empty($aAccounts[$sParentEmail]))) {
$this->StorageProvider()->Clear(
$oAccount,
StorageType::CONFIG,
'accounts'
);
} else {
$this->StorageProvider()->Put(
$oAccount,
StorageType::CONFIG,
'accounts',
\json_encode($aAccounts)
);
}
}
/** /**
* @throws \MailSo\Base\Exceptions\Exception * @throws \MailSo\Base\Exceptions\Exception
*/ */
@ -28,7 +89,7 @@ trait Accounts
$aAccounts = $this->GetAccounts($oAccount); $aAccounts = $this->GetAccounts($oAccount);
$sEmail = trim($this->GetActionParam('Email', '')); $sEmail = \trim($this->GetActionParam('Email', ''));
$sPassword = $this->GetActionParam('Password', ''); $sPassword = $this->GetActionParam('Password', '');
$bNew = '1' === (string)$this->GetActionParam('New', '1'); $bNew = '1' === (string)$this->GetActionParam('New', '1');
@ -63,7 +124,7 @@ trait Accounts
} }
$sParentEmail = $oAccount->ParentEmailHelper(); $sParentEmail = $oAccount->ParentEmailHelper();
$sEmailToDelete = trim($this->GetActionParam('EmailToDelete', '')); $sEmailToDelete = \trim($this->GetActionParam('EmailToDelete', ''));
$sEmailToDelete = \MailSo\Base\Utils::IdnToAscii($sEmailToDelete, true); $sEmailToDelete = \MailSo\Base\Utils::IdnToAscii($sEmailToDelete, true);
$aAccounts = $this->GetAccounts($oAccount); $aAccounts = $this->GetAccounts($oAccount);
@ -93,7 +154,7 @@ trait Accounts
{ {
$oAccount = $this->getAccountFromToken(); $oAccount = $this->getAccountFromToken();
$oIdentity = new \RainLoop\Model\Identity(); $oIdentity = new Identity();
if (!$oIdentity->FromJSON($this->GetActionParams(), true)) { if (!$oIdentity->FromJSON($this->GetActionParams(), true)) {
throw new ClientException(Notifications::InvalidInputArgument); throw new ClientException(Notifications::InvalidInputArgument);
} }
@ -113,7 +174,7 @@ trait Accounts
return $this->FalseResponse(__FUNCTION__); return $this->FalseResponse(__FUNCTION__);
} }
$sId = trim($this->GetActionParam('IdToDelete', '')); $sId = \trim($this->GetActionParam('IdToDelete', ''));
if (empty($sId)) { if (empty($sId)) {
throw new ClientException(Notifications::UnknownError); throw new ClientException(Notifications::UnknownError);
} }
@ -137,7 +198,7 @@ trait Accounts
} }
return $this->DefaultResponse(__FUNCTION__, $this->StorageProvider()->Put($oAccount, return $this->DefaultResponse(__FUNCTION__, $this->StorageProvider()->Put($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, 'accounts_identities_order', StorageType::CONFIG, 'accounts_identities_order',
\json_encode(array( \json_encode(array(
'Accounts' => \is_array($aAccounts) ? $aAccounts : array(), 'Accounts' => \is_array($aAccounts) ? $aAccounts : array(),
'Identities' => \is_array($aIdentities) ? $aIdentities : array() 'Identities' => \is_array($aIdentities) ? $aIdentities : array()

View file

@ -2,12 +2,12 @@
namespace RainLoop\Actions; namespace RainLoop\Actions;
use \RainLoop\Enumerations\Capa; use RainLoop\Enumerations\Capa;
use \RainLoop\Enumerations\PluginPropertyType; use RainLoop\Enumerations\PluginPropertyType;
use \RainLoop\Exceptions\ClientException; use RainLoop\Exceptions\ClientException;
use \RainLoop\KeyPathHelper; use RainLoop\KeyPathHelper;
use \RainLoop\Notifications; use RainLoop\Notifications;
use \RainLoop\Utils; use RainLoop\Utils;
trait Admin trait Admin
{ {

View file

@ -22,20 +22,13 @@ trait Contacts
$mData = $this->getContactsSyncData($oAccount); $mData = $this->getContactsSyncData($oAccount);
$sPassword = APP_DUMMY === $sPassword && isset($mData['Password']) $bResult = $this->setContactsSyncData(array(
? $mData['Password'] : (APP_DUMMY === $sPassword ? '' : $sPassword); 'Enable' => $bEnabled,
'User' => $sUser,
$bResult = $this->StorageProvider()->Put( 'Password' => APP_DUMMY === $sPassword && isset($mData['Password'])
$oAccount, ? $mData['Password'] : (APP_DUMMY === $sPassword ? '' : $sPassword),
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, 'Url' => $sUrl
'contacts_sync', ));
\json_encode(array(
'Enable' => $bEnabled,
'User' => $sUser,
'Password' => $sPassword ? \SnappyMail\Crypt::EncryptToJSON($sPassword, $oAccount->PasswordHash()) : null,
'Url' => $sUrl
))
);
return $this->DefaultResponse(__FUNCTION__, $bResult); return $this->DefaultResponse(__FUNCTION__, $bResult);
} }
@ -241,6 +234,20 @@ trait Contacts
return $this->DefaultResponse(__FUNCTION__, $mResponse); return $this->DefaultResponse(__FUNCTION__, $mResponse);
} }
protected function setContactsSyncData(\RainLoop\Model\Account $oAccount, array $aData) : bool
{
if ($aData['Password']) {
$aData['Password'] = \SnappyMail\Crypt::EncryptToJSON($aData['Password'], $oAccount->PasswordHash());
}
$aData['PasswordHMAC'] = $aData['Password'] ? \hash_hmac('sha1', $aData['Password'], $oAccount->PasswordHash()) : null;
return $this->StorageProvider()->Put(
$oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'contacts_sync',
\json_encode($aData)
);
}
protected function getContactsSyncData(\RainLoop\Model\Account $oAccount) : ?array protected function getContactsSyncData(\RainLoop\Model\Account $oAccount) : ?array
{ {
$sData = $this->StorageProvider()->Get($oAccount, $sData = $this->StorageProvider()->Get($oAccount,
@ -251,10 +258,17 @@ trait Contacts
$aData = \json_decode($sData); $aData = \json_decode($sData);
if ($aData) { if ($aData) {
if ($aData['Password']) { if ($aData['Password']) {
$aData['Password'] = \SnappyMail\Crypt::DecryptFromJSON( // Verify oAccount password hasn't changed so that Password can be decrypted
$aData['Password'], if ($aData['PasswordHMAC'] !== \hash_hmac('sha1', $aData['Password'], $oAccount->PasswordHash())) {
$oAccount->PasswordHash() // Failed
); $aData['Password'] = null;
} else {
// Success
$aData['Password'] = \SnappyMail\Crypt::DecryptFromJSON(
$aData['Password'],
$oAccount->PasswordHash()
);
}
} }
return $aData; return $aData;
} }
@ -262,6 +276,7 @@ trait Contacts
// Try the old // Try the old
$aData = \RainLoop\Utils::DecodeKeyValues($sData); $aData = \RainLoop\Utils::DecodeKeyValues($sData);
if ($aData) { if ($aData) {
$this->setContactsSyncData($oAccount, $aData);
return array( return array(
'Enable' => isset($aData['Enable']) ? !!$aData['Enable'] : false, 'Enable' => isset($aData['Enable']) ? !!$aData['Enable'] : false,
'Url' => isset($aData['Url']) ? \trim($aData['Url']) : '', 'Url' => isset($aData['Url']) ? \trim($aData['Url']) : '',

View file

@ -2,7 +2,7 @@
namespace RainLoop\Actions; namespace RainLoop\Actions;
use \RainLoop\Enumerations\Capa; use RainLoop\Enumerations\Capa;
trait Filters trait Filters
{ {

View file

@ -2,10 +2,10 @@
namespace RainLoop\Actions; namespace RainLoop\Actions;
use \RainLoop\Enumerations\Capa; use RainLoop\Enumerations\Capa;
use \RainLoop\Exceptions\ClientException; use RainLoop\Exceptions\ClientException;
use \RainLoop\Notifications; use RainLoop\Notifications;
use \MailSo\Imap\Enumerations\FolderType; use MailSo\Imap\Enumerations\FolderType;
trait Folders trait Folders
{ {

View file

@ -2,10 +2,10 @@
namespace RainLoop\Actions; namespace RainLoop\Actions;
use \RainLoop\Enumerations\Capa; use RainLoop\Enumerations\Capa;
use \RainLoop\Exceptions\ClientException; use RainLoop\Exceptions\ClientException;
use \RainLoop\Model\Account; use RainLoop\Model\Account;
use \RainLoop\Notifications; use RainLoop\Notifications;
trait Messages trait Messages
{ {

View file

@ -2,9 +2,9 @@
namespace RainLoop\Actions; namespace RainLoop\Actions;
use \RainLoop\Enumerations\Capa; use RainLoop\Enumerations\Capa;
use \RainLoop\Notifications; use RainLoop\Notifications;
use \RainLoop\Utils; use RainLoop\Utils;
trait Response trait Response
{ {

View file

@ -2,10 +2,10 @@
namespace RainLoop\Actions; namespace RainLoop\Actions;
use \RainLoop\Enumerations\Capa; use RainLoop\Enumerations\Capa;
use \RainLoop\Exceptions\ClientException; use RainLoop\Exceptions\ClientException;
use \RainLoop\Notifications; use RainLoop\Notifications;
use \RainLoop\Utils; use RainLoop\Utils;
trait User trait User
{ {

View file

@ -2,11 +2,11 @@
namespace RainLoop\Actions; namespace RainLoop\Actions;
use \RainLoop\Notifications; use RainLoop\Notifications;
use \RainLoop\Utils; use RainLoop\Utils;
use \RainLoop\Model\Account; use RainLoop\Model\Account;
use \RainLoop\Providers\Storage\Enumerations\StorageType; use RainLoop\Providers\Storage\Enumerations\StorageType;
use \RainLoop\Exceptions\ClientException; use RainLoop\Exceptions\ClientException;
trait UserAuth trait UserAuth
{ {
@ -145,12 +145,7 @@ trait UserAuth
*/ */
public function GetAccountFromCustomToken(string $sToken): ?Account public function GetAccountFromCustomToken(string $sToken): ?Account
{ {
return empty($sToken) ? null return empty($sToken) ? null : Account::NewInstanceFromAuthToken($sToken);
: Account::NewInstanceFromTokenArray(
$this,
Utils::DecodeKeyValues($sToken),
false
);
} }
/** /**

View file

@ -2,8 +2,8 @@
namespace RainLoop\Model; namespace RainLoop\Model;
use \RainLoop\Utils; use RainLoop\Utils;
use \RainLoop\Exceptions\ClientException; use RainLoop\Exceptions\ClientException;
class Account implements \JsonSerializable class Account implements \JsonSerializable
{ {
@ -187,6 +187,19 @@ class Account implements \JsonSerializable
*/ */
} }
/**
* @throws \RainLoop\Exceptions\ClientException
*/
public static function NewInstanceFromAuthToken(\RainLoop\Actions $oActions, string $sToken): ?Account
{
return empty($sToken) ? null
: static::NewInstanceFromTokenArray(
$oActions,
Utils::DecodeKeyValues($sToken),
false
);
}
public static function NewInstanceByLogin(\RainLoop\Actions $oActions, string $sEmail, string $sLogin, string $sPassword, string $sClientCert = '', bool $bThrowException = false): ?self public static function NewInstanceByLogin(\RainLoop\Actions $oActions, string $sEmail, string $sLogin, string $sPassword, string $sClientCert = '', bool $bThrowException = false): ?self
{ {
$oAccount = null; $oAccount = null;

View file

@ -2,10 +2,9 @@
namespace RainLoop\Model; namespace RainLoop\Model;
use JsonSerializable;
use MailSo\Base\Utils; use MailSo\Base\Utils;
class Identity implements JsonSerializable class Identity implements \JsonSerializable
{ {
/** /**
* @var string * @var string

View file

@ -2,7 +2,7 @@
namespace RainLoop\Providers; namespace RainLoop\Providers;
use \RainLoop\Providers\AddressBook\Enumerations\PropertyType as PropertyType; use RainLoop\Providers\AddressBook\Enumerations\PropertyType as PropertyType;
class AddressBook extends \RainLoop\Providers\AbstractProvider class AddressBook extends \RainLoop\Providers\AbstractProvider
{ {

View file

@ -2,7 +2,7 @@
namespace RainLoop\Providers\AddressBook; namespace RainLoop\Providers\AddressBook;
use \SnappyMail\DAV\Client as DAVClient; use SnappyMail\DAV\Client as DAVClient;
trait CardDAV trait CardDAV
{ {

View file

@ -2,7 +2,7 @@
namespace RainLoop\Providers\AddressBook; namespace RainLoop\Providers\AddressBook;
use \RainLoop\Providers\AddressBook\Enumerations\PropertyType; use RainLoop\Providers\AddressBook\Enumerations\PropertyType;
class PdoAddressBook class PdoAddressBook
extends \RainLoop\Common\PdoAbstract extends \RainLoop\Common\PdoAbstract

View file

@ -1,47 +0,0 @@
<?php
namespace RainLoop\Providers\Identities;
use RainLoop\Exceptions\Exception;
use RainLoop\Model\Account;
use RainLoop\Model\Identity;
class TestIdentities implements IIdentities
{
/**
* @param Account $account
* @return Identity[]
*/
public function GetIdentities(Account $account): array
{
$oIdentity = new Identity('', $account->Email());
$oIdentity->SetName("Test Name");
return [$oIdentity];
}
/**
* @param Account $account
* @param Identity[] $identities
*
* @return void
* @throws Exception
*/
public function SetIdentities(Account $account, array $identities): void
{
throw new Exception("Not implemented");
}
/**
* @return bool
*/
public function SupportsStore(): bool
{
return false;
}
public function Name(): string
{
return "Test";
}
}

View file

@ -2,7 +2,7 @@
namespace RainLoop\Providers\Storage; namespace RainLoop\Providers\Storage;
use \RainLoop\Providers\Storage\Enumerations\StorageType; use RainLoop\Providers\Storage\Enumerations\StorageType;
class FileStorage implements \RainLoop\Providers\Storage\IStorage class FileStorage implements \RainLoop\Providers\Storage\IStorage
{ {

View file

@ -811,10 +811,11 @@ class ServiceActions
return ''; return '';
} }
private function changeAction() public function ServiceChange() : string
{ {
$this->oHttp->ServerNoCache(); $this->oHttp->ServerNoCache();
// $oAccount = $this->oActions->getAccountFromToken(false);
$oAccount = $this->oActions->GetAccount(); $oAccount = $this->oActions->GetAccount();
if ($oAccount && $this->oActions->GetCapa(false, Enumerations\Capa::ADDITIONAL_ACCOUNTS, $oAccount)) if ($oAccount && $this->oActions->GetCapa(false, Enumerations\Capa::ADDITIONAL_ACCOUNTS, $oAccount))
@ -837,11 +838,7 @@ class ServiceActions
$this->oActions->SetAuthToken($oAccountToLogin); $this->oActions->SetAuthToken($oAccountToLogin);
} }
} }
}
public function ServiceChange() : string
{
$this->changeAction();
$this->oActions->Location('./'); $this->oActions->Location('./');
return ''; return '';
} }

View file

@ -2,7 +2,7 @@
namespace SnappyMail\HTTP\Request; namespace SnappyMail\HTTP\Request;
use \SnappyMail\HTTP\Response; use SnappyMail\HTTP\Response;
class CURL extends \SnappyMail\HTTP\Request class CURL extends \SnappyMail\HTTP\Request
{ {

View file

@ -2,7 +2,7 @@
namespace SnappyMail\HTTP\Request; namespace SnappyMail\HTTP\Request;
use \SnappyMail\HTTP\Response; use SnappyMail\HTTP\Response;
class Socket extends \SnappyMail\HTTP\Request class Socket extends \SnappyMail\HTTP\Request
{ {

View file

@ -44,7 +44,7 @@ if (defined('APP_VERSION'))
unset($sSite); unset($sSite);
$sPrivateDataFolderInternalName = defined('MULTIDOMAIN') ? APP_SITE : ''; $sPrivateDataFolderInternalName = defined('MULTIDOMAIN') ? APP_SITE : '';
define('APP_PRIVATE_DATA_NAME', 0 === strlen($sPrivateDataFolderInternalName) ? '_default_' : $sPrivateDataFolderInternalName); define('APP_PRIVATE_DATA_NAME', $sPrivateDataFolderInternalName ?: '_default_');
define('APP_DUMMY', '********'); define('APP_DUMMY', '********');
define('APP_DEV_VERSION', '0.0.0'); define('APP_DEV_VERSION', '0.0.0');