From 8f09769f8700f5ce513b6d7b8ebf8bf9b673dcaf Mon Sep 17 00:00:00 2001 From: Christophe Deschamps Date: Thu, 17 Oct 2024 06:39:53 +0200 Subject: [PATCH] - Unregister account prior removal (set expires to 0 to prevent receiving calls after account is removed) - Request user confirmation to force remove an account even if unable to unregister --- linphone-app/assets/languages/en.ts | 4 ++ linphone-app/assets/languages/fr_FR.ts | 4 ++ .../settings/AccountSettingsModel.cpp | 56 +++++++++++++------ .../settings/AccountSettingsModel.hpp | 11 ++-- .../views/App/Settings/SettingsSipAccounts.js | 17 +++++- .../App/Settings/SettingsSipAccounts.qml | 32 ++++++++++- .../Styles/Settings/SettingsWindowStyle.qml | 4 ++ 7 files changed, 103 insertions(+), 25 deletions(-) diff --git a/linphone-app/assets/languages/en.ts b/linphone-app/assets/languages/en.ts index 6634f9b08..11d74e8db 100644 --- a/linphone-app/assets/languages/en.ts +++ b/linphone-app/assets/languages/en.ts @@ -3410,6 +3410,10 @@ Click here: <a href="%1">%1</a> deleteAccountDescription Are you sure you wish to delete this account? + + deleteAccountDescriptionUnregisterFailed + Unable to unregister this account from server. Do you wish to delete it anyway? + eraseAllPasswordsDescription Are you sure you wish to remove all passwords? diff --git a/linphone-app/assets/languages/fr_FR.ts b/linphone-app/assets/languages/fr_FR.ts index 8074650e8..96f4a65d1 100644 --- a/linphone-app/assets/languages/fr_FR.ts +++ b/linphone-app/assets/languages/fr_FR.ts @@ -3385,6 +3385,10 @@ Cliquez ici : <a href="%1">%1</a> deleteAccountDescription Êtes-vous sûr de vouloir supprimer ce compte ? + + deleteAccountDescriptionUnregisterFailed + Le désenregistrement de ce compte auprès du serveur a échoué. Le supprimer quand même ? + eraseAllPasswordsDescription Êtes-vous sûr de vouloir supprimer tous vos mots de passe ? diff --git a/linphone-app/src/components/settings/AccountSettingsModel.cpp b/linphone-app/src/components/settings/AccountSettingsModel.cpp index 79bba2c8d..a95340a40 100644 --- a/linphone-app/src/components/settings/AccountSettingsModel.cpp +++ b/linphone-app/src/components/settings/AccountSettingsModel.cpp @@ -298,7 +298,7 @@ void AccountSettingsModel::enableRegister (std::shared_ptr ac account->setParams(params); } -void AccountSettingsModel::removeAccount (const shared_ptr &account) { +void AccountSettingsModel::removeAccount (const shared_ptr &account, bool unregisterFirst) { CoreManager *coreManager = CoreManager::getInstance(); std::shared_ptr newAccount = nullptr; @@ -312,6 +312,16 @@ void AccountSettingsModel::removeAccount (const shared_ptr &a } setDefaultAccount(newAccount); } + + if (!unregisterFirst || !account->getParams()->registerEnabled()) { + doRemoveAccount(account); + return; + } + if (account->getState() == linphone::RegistrationState::None) { + emit unregisterFailed(); + return; + } + // "message-expires" is used to keep contact for messages. Setting to 0 will remove the contact for messages too. // Check if a "message-expires" exists and set it to 0 QStringList parameters = Utils::coreStringToAppString(account->getParams()->getContactParameters()).split(";", Qt::SkipEmptyParts); @@ -321,16 +331,26 @@ void AccountSettingsModel::removeAccount (const shared_ptr &a parameters[i] = Constants::DefaultContactParametersOnRemove; } } + auto accountParams = account->getParams()->clone(); - accountParams->setContactParameters(Utils::appStringToCoreString(parameters.join(";"))); + accountParams->setContactParameters(Utils::appStringToCoreString(parameters.join(";"))); + accountParams->setExpires(0); bool isRegistered = account->getState() == linphone::RegistrationState::Ok; if (account->setParams(accountParams) == -1) { qWarning() << QStringLiteral("Unable to reset message-expiry property before removing account: `%1`.") .arg(QString::fromStdString(account->getParams()->getIdentityAddress()->asString())); - }else if(isRegistered && account->getParams()->registerEnabled()) { // Wait for update + }else if (isRegistered) { // Wait for unregistration mRemovingAccounts.push_back(account); + QTimer::singleShot(10000, [account, this](){// In case of unregistration failure. + if (!mRemovedAccounts.contains(account)) { + mRemovingAccounts.removeAll(account); + qInfo() << QStringLiteral("failed unregistering from server within 10 seconds, deleting account %1.").arg(Utils::coreStringToAppString(account->getParams()->getIdentityAddress()->asString())); + emit unregisterFailed(); + } else + mRemovedAccounts.removeAll(account); + }); }else{// Registration is not enabled : Removing without wait. - CoreManager::getInstance()->getCore()->removeAccount(account); + doRemoveAccount(account); } emit accountSettingsUpdated(); @@ -644,23 +664,27 @@ void AccountSettingsModel::handleRegistrationStateChanged ( const shared_ptr & account, linphone::RegistrationState state ) { - Q_UNUSED(state) auto coreManager = CoreManager::getInstance(); shared_ptr defaultAccount = coreManager->getCore()->getDefaultAccount(); - if( state == linphone::RegistrationState::Cleared){ - auto authInfo = account->findAuthInfo(); - if(authInfo) - QTimer::singleShot(60000, [authInfo](){// 60s is just to be sure. account_update remove deleted account only after 32s - CoreManager::getInstance()->getCore()->removeAuthInfo(authInfo); - }); - }else if(mRemovingAccounts.contains(account)){ + if(mRemovingAccounts.contains(account) && state == linphone::RegistrationState::Cleared) { mRemovingAccounts.removeAll(account); - QTimer::singleShot(100, [account, this](){// removeAccount cannot be called from callback - CoreManager::getInstance()->getCore()->removeAccount(account); - emit accountsChanged(); - }); + doRemoveAccount(account); } if(defaultAccount == account) emit defaultRegistrationChanged(); emit registrationStateChanged(); } + +void AccountSettingsModel::doRemoveAccount (const shared_ptr &account) { + auto authInfo = account->findAuthInfo(); + if(authInfo) + QTimer::singleShot(60000, [authInfo](){// 60s is just to be sure. account_update remove deleted account only after 32s + CoreManager::getInstance()->getCore()->removeAuthInfo(authInfo); + }); + QTimer::singleShot(100, [account, this](){// removeAccount cannot be called from callback + CoreManager::getInstance()->getCore()->removeAccount(account); + emit accountsChanged(); + }); +} + + diff --git a/linphone-app/src/components/settings/AccountSettingsModel.hpp b/linphone-app/src/components/settings/AccountSettingsModel.hpp index e523b487c..b54e47f33 100644 --- a/linphone-app/src/components/settings/AccountSettingsModel.hpp +++ b/linphone-app/src/components/settings/AccountSettingsModel.hpp @@ -95,7 +95,7 @@ public: Q_INVOKABLE bool addOrUpdateAccount (const std::shared_ptr &account, const QVariantMap &data); Q_INVOKABLE bool addOrUpdateAccount (const QVariantMap &data);// Create default account and apply data - Q_INVOKABLE void removeAccount (const std::shared_ptr &account); + Q_INVOKABLE void removeAccount (const std::shared_ptr &account, bool unregisterFirst); Q_INVOKABLE std::shared_ptr createAccount (const QString& assistantFile); @@ -122,7 +122,8 @@ signals: void primarySipAddressChanged(); void accountsChanged(); - + void unregisterFailed(); + void accountSettingsUpdated (); void defaultAccountChanged(); void publishPresenceChanged(); @@ -151,13 +152,15 @@ private: QVariantList getAccounts () const; // --------------------------------------------------------------------------- - + void handleRegistrationStateChanged ( const std::shared_ptr &account, linphone::RegistrationState state ); - + void doRemoveAccount (const std::shared_ptr &account); + QVector > mRemovingAccounts; + QVector > mRemovedAccounts; std::shared_ptr mSelectedAccount; }; diff --git a/linphone-app/ui/views/App/Settings/SettingsSipAccounts.js b/linphone-app/ui/views/App/Settings/SettingsSipAccounts.js index e6fc51807..7a966fbf8 100644 --- a/linphone-app/ui/views/App/Settings/SettingsSipAccounts.js +++ b/linphone-app/ui/views/App/Settings/SettingsSipAccounts.js @@ -33,16 +33,29 @@ function editAccount (account) { }) } -function deleteAccount (account) { +function confirmDeleteAccount (account, confirmedLambda) { window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('deleteAccountDescription'), }, function (status) { if (status) { - Linphone.AccountSettingsModel.removeAccount(account.account) + confirmedLambda() + Linphone.AccountSettingsModel.removeAccount(account.account, true) } }) } +function confirmDeleteAccountUnregisterFailed (account, cancelLambda) { + window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { + descriptionText: qsTr('deleteAccountDescriptionUnregisterFailed'), + }, function (status) { + if (status) { + Linphone.AccountSettingsModel.removeAccount(account.account, false) + } else { + cancelLambda() + } + }) +} + function eraseAllPasswords () { window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('eraseAllPasswordsDescription'), diff --git a/linphone-app/ui/views/App/Settings/SettingsSipAccounts.qml b/linphone-app/ui/views/App/Settings/SettingsSipAccounts.qml index 2f33ac6ea..a51bf1731 100644 --- a/linphone-app/ui/views/App/Settings/SettingsSipAccounts.qml +++ b/linphone-app/ui/views/App/Settings/SettingsSipAccounts.qml @@ -4,8 +4,10 @@ import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import UtilsCpp 1.0 +import 'qrc:/ui/scripts/Utils/utils.js' as Utils import App.Styles 1.0 +import Common.Styles 1.0 import 'SettingsSipAccounts.js' as Logic @@ -101,18 +103,42 @@ TabContainer { isCustom: true backgroundRadius: 4 colorSet: SettingsWindowStyle.buttons.editProxy - + enabled: !deleteButton.deleteInProgress onClicked: Logic.editAccount(modelData) } } FormTableEntry { ActionButton { + id: deleteButton isCustom: true backgroundRadius: 4 colorSet: SettingsWindowStyle.buttons.deleteProxy - - onClicked: Logic.deleteAccount(modelData) + property bool deleteInProgress: false + enabled: !deleteInProgress + onClicked: { + Logic.confirmDeleteAccount(modelData, function() { + deleteInProgress = true + }) + } + Connections { + target: AccountSettingsModel + enabled: deleteButton.deleteInProgress + onUnregisterFailed: { + Logic.confirmDeleteAccountUnregisterFailed(modelData, function() { + deleteButton.deleteInProgress = false + }) + } + } + BusyIndicator { + anchors { + horizontalCenter: parent.horizontalCenter + fill: parent + } + color: BusyIndicatorStyle.alternateColor.color + running: true + visible: deleteButton.deleteInProgress + } } } } diff --git a/linphone-app/ui/views/App/Styles/Settings/SettingsWindowStyle.qml b/linphone-app/ui/views/App/Styles/Settings/SettingsWindowStyle.qml index 2a8b38e28..6307f9aa1 100644 --- a/linphone-app/ui/views/App/Styles/Settings/SettingsWindowStyle.qml +++ b/linphone-app/ui/views/App/Styles/Settings/SettingsWindowStyle.qml @@ -37,9 +37,11 @@ QtObject { property string name : 'editProxy' property string icon : 'edit_custom' property var backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg') + property var backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_d_b_bg') property var backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg') property var backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg') property var foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg') + property var foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_d_b_fg') property var foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg') property var foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg') } @@ -48,9 +50,11 @@ QtObject { property string name : 'deleteProxy' property string icon : 'delete_custom' property var backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg') + property var backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_d_b_bg') property var backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg') property var backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg') property var foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg') + property var foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_d_b_fg') property var foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg') property var foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg') }