From 6b8020b8f51cdb323026eb286d29bab702c3db5f Mon Sep 17 00:00:00 2001 From: djmaze Date: Mon, 15 Nov 2021 10:56:52 +0100 Subject: [PATCH] Improved additional account management --- dev/App/User.js | 20 +-- dev/Common/Links.js | 6 - dev/Model/Account.js | 15 +-- dev/Settings/User/Accounts.js | 10 +- dev/Stores/User/Account.js | 3 +- dev/Styles/User/SettingsAccounts.less | 5 +- dev/View/Popup/Account.js | 2 +- dev/View/User/SystemDropDown.js | 2 +- .../0.0.0/app/libraries/RainLoop/Actions.php | 7 +- .../libraries/RainLoop/Actions/Accounts.php | 93 +------------- .../libraries/RainLoop/Actions/Contacts.php | 12 +- .../app/libraries/RainLoop/ServiceActions.php | 16 --- .../app/libraries/snappymail/upgrade.php | 114 ++++++++++++++++++ .../Views/User/SettingsAccounts.html | 14 ++- .../templates/Views/User/SystemDropDown.html | 3 +- 15 files changed, 161 insertions(+), 161 deletions(-) create mode 100644 snappymail/v/0.0.0/app/libraries/snappymail/upgrade.php diff --git a/dev/App/User.js b/dev/App/User.js index e93b336c9..44ec5a254 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -420,10 +420,10 @@ class AppUser extends AbstractApp { if (!iError) { const counts = {}, - sAccountEmail = AccountUserStore.email(); - let parentEmail = SettingsGet('ParentEmail') || sAccountEmail; + accounts = oData.Result.Accounts, + mainEmail = SettingsGet('MainEmail'); - if (isArray(oData.Result.Accounts)) { + if (isArray(accounts)) { AccountUserStore.accounts.forEach(oAccount => counts[oAccount.email] = oAccount.count() ); @@ -431,10 +431,12 @@ class AppUser extends AbstractApp { delegateRunOnDestroy(AccountUserStore.accounts()); AccountUserStore.accounts( - oData.Result.Accounts.map( - sValue => new AccountModel(sValue, sValue !== parentEmail, counts[sValue] || 0) + accounts.map( + sValue => new AccountModel(sValue, counts[sValue]) ) ); +// accounts.length && + AccountUserStore.accounts.unshift(new AccountModel(mainEmail, counts[mainEmail], false)); } if (isArray(oData.Result.Identities)) { @@ -442,9 +444,10 @@ class AppUser extends AbstractApp { IdentityUserStore( oData.Result.Identities.map(identityData => { - const id = pString(identityData.Id), - email = pString(identityData.Email), - identity = new IdentityModel(id, email); + const identity = new IdentityModel( + pString(identityData.Id), + pString(identityData.Email) + ); identity.name(pString(identityData.Name)); identity.replyTo(pString(identityData.ReplyTo)); @@ -715,7 +718,6 @@ class AppUser extends AbstractApp { NotificationUserStore.enableDesktopNotification(!!SettingsGet('DesktopNotifications')); AccountUserStore.email(SettingsGet('Email')); - AccountUserStore.parentEmail(SettingsGet('ParentEmail')); this.foldersReload(value => { try { diff --git a/dev/Common/Links.js b/dev/Common/Links.js index e4dc3333a..12279c6c5 100644 --- a/dev/Common/Links.js +++ b/dev/Common/Links.js @@ -51,12 +51,6 @@ export const */ serverRequest = type => SERVER_PREFIX + '/' + type + '/' + SUB_QUERY_PREFIX + '/0/', - /** - * @param {string} email - * @returns {string} - */ - change = email => serverRequest('Change') + encodeURIComponent(email) + '/', - /** * @param {string} lang * @param {boolean} isAdmin diff --git a/dev/Model/Account.js b/dev/Model/Account.js index c0789b790..02cb46b21 100644 --- a/dev/Model/Account.js +++ b/dev/Model/Account.js @@ -1,5 +1,3 @@ -import { change } from 'Common/Links'; - import { AbstractModel } from 'Knoin/AbstractModel'; export class AccountModel extends AbstractModel { @@ -8,23 +6,16 @@ export class AccountModel extends AbstractModel { * @param {boolean=} canBeDelete = true * @param {number=} count = 0 */ - constructor(email, canBeDelete = true, count = 0) { + constructor(email, count = 0, isAdditional = true) { super(); this.email = email; this.addObservables({ - count: count, + count: count || 0, deleteAccess: false, - canBeDeleted: !!canBeDelete + isAdditional: isAdditional }); - this.canBeEdit = this.canBeDeleted; } - /** - * @returns {string} - */ - changeAccountLink() { - return change(this.email); - } } diff --git a/dev/Settings/User/Accounts.js b/dev/Settings/User/Accounts.js index dec6b822a..a40cc3d74 100644 --- a/dev/Settings/User/Accounts.js +++ b/dev/Settings/User/Accounts.js @@ -1,7 +1,7 @@ import ko from 'ko'; import { Capa } from 'Common/Enums'; -import { Settings } from 'Common/Globals'; +import { Settings, SettingsGet } from 'Common/Globals'; import { AccountUserStore } from 'Stores/User/Account'; import { IdentityUserStore } from 'Stores/User/Identity'; @@ -20,6 +20,7 @@ export class AccountsUserSettings /*extends AbstractViewSettings*/ { this.accounts = AccountUserStore.accounts; this.loading = AccountUserStore.loading; this.identities = IdentityUserStore; + this.mainEmail = SettingsGet('MainEmail'); this.accountForDeletion = ko.observable(null).deleteAccessHelper(); this.identityForDeletion = ko.observable(null).deleteAccessHelper(); @@ -30,7 +31,7 @@ export class AccountsUserSettings /*extends AbstractViewSettings*/ { } editAccount(account) { - if (account && account.canBeEdit()) { + if (account && account.isAdditional()) { showScreenPopup(AccountPopupView, [account]); } } @@ -81,7 +82,10 @@ export class AccountsUserSettings /*extends AbstractViewSettings*/ { } accountsAndIdentitiesAfterMove() { - Remote.accountsAndIdentitiesSortOrder(null, AccountUserStore.getEmailAddresses(), IdentityUserStore.getIDS()); + Remote.accountsAndIdentitiesSortOrder(null, + AccountUserStore.getEmailAddresses().filter(v => v != SettingsGet('MainEmail')), + IdentityUserStore.getIDS() + ); } onBuild(oDom) { diff --git a/dev/Stores/User/Account.js b/dev/Stores/User/Account.js index a8ce79381..d54c8005b 100644 --- a/dev/Stores/User/Account.js +++ b/dev/Stores/User/Account.js @@ -5,7 +5,7 @@ export const AccountUserStore = { accounts: ko.observableArray(), loading: ko.observable(false).extend({ debounce: 100 }), - getEmailAddresses: () => AccountUserStore.accounts.map(item => item ? item.email : null).filter(v => v), + getEmailAddresses: () => AccountUserStore.accounts.map(item => item.email), accountsUnreadCount: ko.computed(() => 0), // accountsUnreadCount: ko.computed(() => { @@ -21,6 +21,5 @@ export const AccountUserStore = { addObservablesTo(AccountUserStore, { email: '', - parentEmail: '', signature: '' }); diff --git a/dev/Styles/User/SettingsAccounts.less b/dev/Styles/User/SettingsAccounts.less index 6bced7529..b83ee1a3c 100644 --- a/dev/Styles/User/SettingsAccounts.less +++ b/dev/Styles/User/SettingsAccounts.less @@ -25,10 +25,11 @@ white-space: nowrap; } - td + td { + td + td + td { text-align: right; } - td + td + td { + td:first-child, + td:last-child, { width: 1em; } diff --git a/dev/View/Popup/Account.js b/dev/View/Popup/Account.js index 99a811375..13b2d9789 100644 --- a/dev/View/Popup/Account.js +++ b/dev/View/Popup/Account.js @@ -63,7 +63,7 @@ class AccountPopupView extends AbstractViewPopup { } onShow(account) { - if (account && account.canBeEdit()) { + if (account && account.isAdditional()) { this.isNew(false); this.email(account.email); } else { diff --git a/dev/View/User/SystemDropDown.js b/dev/View/User/SystemDropDown.js index da1355285..20060e5ff 100644 --- a/dev/View/User/SystemDropDown.js +++ b/dev/View/User/SystemDropDown.js @@ -58,7 +58,7 @@ export class SystemDropDownUserView extends AbstractViewRight { if (iError) { AccountUserStore.loading(false); alert(getNotification(iError).replace('%EMAIL%', account.email)); - if (account.canBeEdit()) { + if (account.isAdditional()) { showScreenPopup(AccountPopupView, [account]); } } else { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php index d9008221b..1e6e70438 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -852,7 +852,7 @@ class Actions 'ReplySameFolder' => (bool) $oConfig->Get('defaults', 'mail_reply_same_folder', false), 'ContactsAutosave' => (bool) $oConfig->Get('defaults', 'contacts_autosave', true), 'HideUnsubscribed' => (bool) $oConfig->Get('labs', 'use_imap_list_subscribe', true), - 'ParentEmail' => '', + 'MainEmail' => '', 'InterfaceAnimation' => true, 'UserBackgroundName' => '', 'UserBackgroundHash' => '' @@ -920,9 +920,7 @@ class Actions $aResult['StartupUrl'] = $this->compileLogParams($aResult['StartupUrl'], $oAccount, true); } - if ($oAccount instanceof \RainLoop\Model\AdditionalAccount) { - $aResult['ParentEmail'] = $oAccount->ParentEmail(); - } + $aResult['MainEmail'] = \MailSo\Base\Utils::IdnToUtf8($this->getMainAccountFromToken()->Email()); $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); @@ -1048,7 +1046,6 @@ class Actions // IDN $aResult['Email'] = \MailSo\Base\Utils::IdnToUtf8($aResult['Email']); - $aResult['ParentEmail'] = \MailSo\Base\Utils::IdnToUtf8($aResult['ParentEmail']); $aResult['MailToEmail'] = \MailSo\Base\Utils::IdnToUtf8($aResult['MailToEmail']); $aResult['DevEmail'] = \MailSo\Base\Utils::IdnToUtf8($aResult['DevEmail']); diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php index 1c6b7e9b6..da393f06b 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php @@ -27,7 +27,7 @@ trait Accounts StorageType::CONFIG, 'additionalaccounts' ); - $aAccounts = $sAccounts ? \json_decode($sAccounts, true) : $this->ConvertInsecureAccounts($oAccount); + $aAccounts = $sAccounts ? \json_decode($sAccounts, true) : \SnappyMail\Upgrade::ConvertInsecureAccounts($this, $oAccount); if ($aAccounts && \is_array($aAccounts)) { return $aAccounts; } @@ -36,81 +36,6 @@ trait Accounts return array(); } - /** - * Attempt to convert the old less secure data into better secured data - */ - protected function ConvertInsecureAccounts(MainAccount $oMainAccount) : array - { - $sAccounts = $this->StorageProvider()->Get($oMainAccount, StorageType::CONFIG, 'accounts'); - if (!$sAccounts || '{' !== $sAccounts[0]) { - return []; - } - - $aAccounts = \json_decode($sAccounts, true); - if (!$aAccounts || !\is_array($aAccounts)) { - return []; - } - - $aNewAccounts = []; - if (1 < \count($aAccounts)) { - $sOrder = $this->StorageProvider()->Get($oMainAccount, StorageType::CONFIG, 'accounts_identities_order'); - $aOrder = $sOrder ? \json_decode($sOrder, true) : []; - if (!empty($aOrder['Accounts']) && \is_array($aOrder['Accounts']) && 1 < \count($aOrder['Accounts'])) { - $aAccounts = \array_filter(\array_merge( - \array_fill_keys($aOrder['Accounts'], null), - $aAccounts - )); - } - $sHash = $oMainAccount->CryptKey(); - foreach ($aAccounts as $sEmail => $sToken) { - try { - $aNewAccounts[$sEmail] = [ - 'account', - $sEmail, - $sEmail, - '', - '', - '', - '', - $oMainAccount->Email(), - \hash_hmac('sha1', '', $sHash) - ]; - if (!$sToken) { - \error_log("ConvertInsecureAccount {$sEmail} no token"); - continue; - } - $aAccountHash = \RainLoop\Utils::DecodeKeyValues($sToken); - if (empty($aAccountHash[0]) || 'token' !== $aAccountHash[0] // simple token validation - || 8 > \count($aAccountHash) // length checking - ) { - \error_log("ConvertInsecureAccount {$sEmail} invalid aAccountHash: " . print_r($aAccountHash,1)); - continue; - } - $aAccountHash[3] = \SnappyMail\Crypt::EncryptUrlSafe($aAccountHash[3], $sHash); - $aNewAccounts[$sEmail] = [ - 'account', - $aAccountHash[1], - $aAccountHash[2], - $aAccountHash[3], - $aAccountHash[11], - $aAccountHash[8], - $aAccountHash[9], - $oMainAccount->Email(), - \hash_hmac('sha1', $aAccountHash[3], $sHash) - ]; - } catch (\Throwable $e) { - \error_log("ConvertInsecureAccount {$sEmail} failed"); - } - } - - $this->SetAccounts($oMainAccount, $aNewAccounts); - } - - $this->StorageProvider()->Clear($oMainAccount, StorageType::CONFIG, 'accounts'); - - return $aNewAccounts; - } - protected function SetAccounts(MainAccount $oAccount, array $aAccounts = array()): void { $sParentEmail = $oAccount->Email(); @@ -206,7 +131,7 @@ trait Accounts $aResult['IncLogin'] = $oAccount->IncLogin(); $aResult['OutLogin'] = $oAccount->OutLogin(); $aResult['AccountHash'] = $oAccount->Hash(); - $aResult['ParentEmail'] = ($oAccount instanceof \RainLoop\Model\AdditionalAccount) + $aResult['MainEmail'] = ($oAccount instanceof \RainLoop\Model\AdditionalAccount) ? $oAccount->ParentEmail() : ''; $aResult['ContactsIsAllowed'] = $this->AddressBookProvider($oAccount)->IsActive(); $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); @@ -301,19 +226,11 @@ trait Accounts { $oAccount = $this->getMainAccountFromToken(); - $aAccounts = false; - if ($this->GetCapa(false, Capa::ADDITIONAL_ACCOUNTS, $oAccount)) { - $aAccounts = \array_map( + return $this->DefaultResponse(__FUNCTION__, array( + 'Accounts' => \array_map( 'MailSo\\Base\\Utils::IdnToUtf8', \array_keys($this->GetAccounts($oAccount)) - ); - if ($aAccounts) { - \array_unshift($aAccounts, \MailSo\Base\Utils::IdnToUtf8($oAccount->Email())); - } - } - - return $this->DefaultResponse(__FUNCTION__, array( - 'Accounts' => $aAccounts, + ), 'Identities' => $this->GetIdentities($oAccount) )); } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php index 0065d8efd..550390478 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php @@ -278,17 +278,7 @@ trait Contacts return $aData; } - // Try the old - $aData = \RainLoop\Utils::DecodeKeyValues($sData); - if ($aData) { - $this->setContactsSyncData($oAccount, $aData); - return array( - 'Enable' => isset($aData['Enable']) ? !!$aData['Enable'] : false, - 'Url' => isset($aData['Url']) ? \trim($aData['Url']) : '', - 'User' => isset($aData['User']) ? \trim($aData['User']) : '', - 'Password' => isset($aData['Password']) ? $aData['Password'] : '' - ); - } + return \SnappyMail\Upgrade::ConvertInsecureContactsSync($this, $oAccount); } return null; } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index 37006abce..81b0d7c54 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -811,22 +811,6 @@ class ServiceActions return ''; } - /** - * Switch to AdditionalAccount - */ - public function ServiceChange() : string - { - try { - $this->oActions->switchAccount( - empty($this->aPaths[2]) ? '' : \urldecode(\trim($this->aPaths[2])) - ); - $this->oActions->Location('./'); - } catch (\Exception $e) { - exit($e->getMessage()); - } - return ''; - } - /** * @return mixed */ diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/upgrade.php b/snappymail/v/0.0.0/app/libraries/snappymail/upgrade.php new file mode 100644 index 000000000..4b519f039 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/snappymail/upgrade.php @@ -0,0 +1,114 @@ +StorageProvider(); + $sAccounts = $oStorage->Get($oMainAccount, StorageType::CONFIG, 'accounts'); + if (!$sAccounts || '{' !== $sAccounts[0]) { + return []; + } + + $aAccounts = \json_decode($sAccounts, true); + if (!$aAccounts || !\is_array($aAccounts)) { + return []; + } + + $aNewAccounts = []; + if (1 < \count($aAccounts)) { + $sOrder = $oStorage->Get($oMainAccount, StorageType::CONFIG, 'accounts_identities_order'); + $aOrder = $sOrder ? \json_decode($sOrder, true) : []; + if (!empty($aOrder['Accounts']) && \is_array($aOrder['Accounts']) && 1 < \count($aOrder['Accounts'])) { + $aAccounts = \array_filter(\array_merge( + \array_fill_keys($aOrder['Accounts'], null), + $aAccounts + )); + } + $sHash = $oMainAccount->CryptKey(); + foreach ($aAccounts as $sEmail => $sToken) { + try { + $aNewAccounts[$sEmail] = [ + 'account', + $sEmail, + $sEmail, + '', + '', + '', + '', + $oMainAccount->Email(), + \hash_hmac('sha1', '', $sHash) + ]; + if (!$sToken) { + \error_log("ConvertInsecureAccount {$sEmail} no token"); + continue; + } + $aAccountHash = \RainLoop\Utils::DecodeKeyValues($sToken); + if (empty($aAccountHash[0]) || 'token' !== $aAccountHash[0] // simple token validation + || 8 > \count($aAccountHash) // length checking + ) { + \error_log("ConvertInsecureAccount {$sEmail} invalid aAccountHash: " . \print_r($aAccountHash,1)); + continue; + } + $aAccountHash[3] = Crypt::EncryptUrlSafe($aAccountHash[3], $sHash); + $aNewAccounts[$sEmail] = [ + 'account', + $aAccountHash[1], + $aAccountHash[2], + $aAccountHash[3], + $aAccountHash[11], + $aAccountHash[8], + $aAccountHash[9], + $oMainAccount->Email(), + \hash_hmac('sha1', $aAccountHash[3], $sHash) + ]; + } catch (\Throwable $e) { + \error_log("ConvertInsecureAccount {$sEmail} failed"); + } + } + + $oActions->SetAccounts($oMainAccount, $aNewAccounts); + } + + $oStorage->Clear($oMainAccount, StorageType::CONFIG, 'accounts'); + + return $aNewAccounts; + } + + /** + * Attempt to convert the old less secure data into better secured data + */ + public function ConvertInsecureContactsSync(\RainLoop\Actions $oActions, \RainLoop\Model\Account $oAccount) : ?array + { + $sData = $oActions->StorageProvider()->Get($oAccount, + \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, + 'contacts_sync' + ); + + if (!empty($sData)) { + $aData = \json_decode($sData); + if (!$aData) { + $aData = \RainLoop\Utils::DecodeKeyValues($sData); + if ($aData) { + $oActions->setContactsSyncData($oAccount, $aData); + return array( + 'Enable' => isset($aData['Enable']) ? !!$aData['Enable'] : false, + 'Url' => isset($aData['Url']) ? \trim($aData['Url']) : '', + 'User' => isset($aData['User']) ? \trim($aData['User']) : '', + 'Password' => isset($aData['Password']) ? $aData['Password'] : '' + ); + } + } + } + return null; + } + +} diff --git a/snappymail/v/0.0.0/app/templates/Views/User/SettingsAccounts.html b/snappymail/v/0.0.0/app/templates/Views/User/SettingsAccounts.html index 6ced15c76..8402f8746 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/SettingsAccounts.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/SettingsAccounts.html @@ -9,20 +9,28 @@ + + + + + - + +
+ + - - 🗑 + 🗑
diff --git a/snappymail/v/0.0.0/app/templates/Views/User/SystemDropDown.html b/snappymail/v/0.0.0/app/templates/Views/User/SystemDropDown.html index 7b7812184..38a8f928a 100644 --- a/snappymail/v/0.0.0/app/templates/Views/User/SystemDropDown.html +++ b/snappymail/v/0.0.0/app/templates/Views/User/SystemDropDown.html @@ -20,8 +20,7 @@
  • -