From 5adf150d2d10c362df288a51bf32576d3e832154 Mon Sep 17 00:00:00 2001 From: Gaelle Braud Date: Thu, 22 Aug 2024 14:31:28 +0200 Subject: [PATCH] reauthentication dialog --- Linphone/core/App.cpp | 32 ++++++ Linphone/core/login/LoginPage.hpp | 2 +- Linphone/model/core/CoreModel.cpp | 1 + Linphone/tool/CMakeLists.txt | 1 + .../tool/request/AuthenticationDialog.cpp | 25 +++++ .../tool/request/AuthenticationDialog.hpp | 42 +++++++ Linphone/view/App/AppWindow.qml | 22 +++- Linphone/view/CMakeLists.txt | 1 + .../Item/Account/AuthenticationDialog.qml | 105 ++++++++++++++++++ Linphone/view/Item/Contact/ContactEdition.qml | 3 + Linphone/view/Item/TextField.qml | 5 +- Linphone/view/Page/Login/SIPLoginPage.qml | 69 +++++++++--- Linphone/view/Page/Main/ContactPage.qml | 32 +++--- 13 files changed, 308 insertions(+), 32 deletions(-) create mode 100644 Linphone/tool/request/AuthenticationDialog.cpp create mode 100644 Linphone/tool/request/AuthenticationDialog.hpp create mode 100644 Linphone/view/Item/Account/AuthenticationDialog.qml diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index df54ca05d..49ce1c3f9 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -77,6 +77,7 @@ #include "tool/providers/AvatarProvider.hpp" #include "tool/providers/ImageProvider.hpp" #include "tool/providers/ScreenProvider.hpp" +#include "tool/request/AuthenticationDialog.hpp" #include "tool/request/RequestDialog.hpp" #include "tool/thread/Thread.hpp" @@ -167,6 +168,35 @@ void App::setSelf(QSharedPointer(me)) { } }); }); + mCoreModelConnection->makeConnectToModel( + &CoreModel::authenticationRequested, + [this](const std::shared_ptr &core, const std::shared_ptr &authInfo, + linphone::AuthMethod method) { + mCoreModelConnection->invokeToCore([this, core, authInfo, method]() { + if (method == linphone::AuthMethod::HttpDigest) { + auto window = App::getInstance()->getMainWindow(); + auto username = authInfo->getUsername(); + auto domain = authInfo->getDomain(); + AuthenticationDialog *obj = new AuthenticationDialog(Utils::coreStringToAppString(username), + Utils::coreStringToAppString(domain)); + connect(obj, &AuthenticationDialog::result, this, [this, obj, authInfo, core](QString password) { + mCoreModelConnection->invokeToModel([this, core, authInfo, password] { + mustBeInLinphoneThread("[App] reauthenticate"); + if (password.isEmpty()) { + lDebug() << "ERROR : empty password"; + } else { + lDebug() << "reset password for" << authInfo->getUsername(); + authInfo->setPassword(Utils::appStringToCoreString(password)); + core->addAuthInfo(authInfo); + core->refreshRegisters(); + } + }); + obj->deleteLater(); + }); + QMetaObject::invokeMethod(window, "reauthenticateAccount", QVariant::fromValue(obj)); + } + }); + }); //--------------------------------------------------------------------------------------------- mCliModelConnection = QSharedPointer>( new SafeConnection(me, CliModel::getInstance()), &QObject::deleteLater); @@ -364,6 +394,8 @@ void App::initCppInterfaces() { qmlRegisterUncreatableType(Constants::MainQmlUri, 1, 0, "RequestDialog", QLatin1String("Uncreatable")); + qmlRegisterUncreatableType(Constants::MainQmlUri, 1, 0, "AuthenticationDialogCpp", + QLatin1String("Uncreatable")); LinphoneEnums::registerMetaTypes(); } diff --git a/Linphone/core/login/LoginPage.hpp b/Linphone/core/login/LoginPage.hpp index 462d7fcac..cb62ea7db 100644 --- a/Linphone/core/login/LoginPage.hpp +++ b/Linphone/core/login/LoginPage.hpp @@ -40,7 +40,7 @@ public: const QString &password, QString displayName = QString(), QString domain = QString(), - LinphoneEnums::TransportType transportType = LinphoneEnums::TransportType::Udp); + LinphoneEnums::TransportType transportType = LinphoneEnums::TransportType::Tcp); linphone::RegistrationState getRegistrationState() const; void setRegistrationState(linphone::RegistrationState status); diff --git a/Linphone/model/core/CoreModel.cpp b/Linphone/model/core/CoreModel.cpp index 36c045a38..a95135cc7 100644 --- a/Linphone/model/core/CoreModel.cpp +++ b/Linphone/model/core/CoreModel.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include diff --git a/Linphone/tool/CMakeLists.txt b/Linphone/tool/CMakeLists.txt index 29b4ea944..ead22153c 100644 --- a/Linphone/tool/CMakeLists.txt +++ b/Linphone/tool/CMakeLists.txt @@ -14,6 +14,7 @@ list(APPEND _LINPHONEAPP_SOURCES tool/native/DesktopTools.hpp tool/request/RequestDialog.cpp + tool/request/AuthenticationDialog.cpp ) if (APPLE) diff --git a/Linphone/tool/request/AuthenticationDialog.cpp b/Linphone/tool/request/AuthenticationDialog.cpp new file mode 100644 index 000000000..4613fb43f --- /dev/null +++ b/Linphone/tool/request/AuthenticationDialog.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "AuthenticationDialog.hpp" + +AuthenticationDialog::AuthenticationDialog(QString username, QString domain, QObject *parent) + : QObject(parent), mUsername(username), mDomain(domain) { +} diff --git a/Linphone/tool/request/AuthenticationDialog.hpp b/Linphone/tool/request/AuthenticationDialog.hpp new file mode 100644 index 000000000..3d1ebd3dd --- /dev/null +++ b/Linphone/tool/request/AuthenticationDialog.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef AUTHENTICATION_DIALOG_H_ +#define AUTHENTICATION_DIALOG_H_ + +#include +#include +#include + +class AuthenticationDialog : public QObject { + Q_OBJECT + Q_PROPERTY(QString username MEMBER mUsername NOTIFY usernameChanged) + Q_PROPERTY(QString domain MEMBER mDomain NOTIFY domainChanged) +public: + AuthenticationDialog(QString username, QString domain, QObject *parent = nullptr); + + QString mUsername; + QString mDomain; +signals: + void usernameChanged(); + void domainChanged(); + void result(QString password); +}; +#endif diff --git a/Linphone/view/App/AppWindow.qml b/Linphone/view/App/AppWindow.qml index 5b8c787bc..7c7dd4bab 100644 --- a/Linphone/view/App/AppWindow.qml +++ b/Linphone/view/App/AppWindow.qml @@ -39,6 +39,20 @@ ApplicationWindow { } } + Component { + id: authenticationPopupComp + AuthenticationDialog{ + property var authenticationDialog + property var callback: authenticationDialog.result + identity: authenticationDialog.username + domain: authenticationDialog.domain + onAccepted: { + authenticationDialog ? authenticationDialog.result(password) : callback(password) + close() + } + } + } + Popup { id: startCallPopup property FriendGui contact @@ -214,13 +228,19 @@ ApplicationWindow { } function showConfirmationLambdaPopup(title,details,callback){ - console.log("Showing confirmation popup") + console.log("Showing confirmation lambda popup") var popup = confirmPopupComp.createObject(popupLayout, {"text": title, "details":details,"callback":callback}) popup.index = popupLayout.popupList.length popupLayout.popupList.push(popup) popup.open() popup.closePopup.connect(removeFromPopupLayout) } + + function reauthenticateAccount(authenticationDialog){ + console.log("Showing authentication dialog") + var popup = authenticationPopupComp.createObject(mainWindow, {"authenticationDialog": authenticationDialog}) + popup.open() + } ColumnLayout { id: popupLayout diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index cbc52a43e..37d372b31 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -28,6 +28,7 @@ list(APPEND _LINPHONEAPP_QML_FILES view/App/Layout/Account/AccountSettingsParametersLayout.qml view/Item/Account/Accounts.qml + view/Item/Account/AuthenticationDialog.qml view/Item/Call/CallContactsLists.qml view/Item/Call/InCallSettingsPanel.qml diff --git a/Linphone/view/Item/Account/AuthenticationDialog.qml b/Linphone/view/Item/Account/AuthenticationDialog.qml new file mode 100644 index 000000000..eaec4c60c --- /dev/null +++ b/Linphone/view/Item/Account/AuthenticationDialog.qml @@ -0,0 +1,105 @@ +import QtCore +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls as Control +import QtQuick.Dialogs + +import Linphone +import UtilsCpp +import SettingsCpp + +Dialog { + id: mainItem + property string identity + property string domain + readonly property string password: passwordEdit.text + onRejected: close() + modal: true + closePolicy: Popup.NoAutoClose + topPadding: 20 * DefaultStyle.dp + bottomPadding: 20 * DefaultStyle.dp + leftPadding: 20 * DefaultStyle.dp + rightPadding: 20 * DefaultStyle.dp + content: ColumnLayout { + spacing: 20 * DefaultStyle.dp + id: contentLayout + Text { + Layout.fillWidth: true + Layout.preferredWidth: 250 * DefaultStyle.dp + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + text: qsTr("Impossible de vous authentifier. Merci de vérifier votre mot de passe.") + font.pixelSize: 16 * DefaultStyle.dp + } + ColumnLayout { + spacing: 10 * DefaultStyle.dp + FormItemLayout { + Layout.fillWidth: true + label: qsTr("Identité") + contentItem: TextField { + enabled: false + customWidth: parent.width + initialText: mainItem.identity + } + } + FormItemLayout { + Layout.fillWidth: true + label: qsTr("Domaine") + contentItem: TextField { + enabled: false + initialText: mainItem.domain + } + } + FormItemLayout { + Layout.fillWidth: true + label: qsTr("Nom d'utilisateur (optionnel)") + contentItem: TextField { + id: usernameEdit + KeyNavigation.down: passwordEdit + } + } + FormItemLayout { + id: password + Layout.fillWidth: true + label: qsTr("Mot de passe") + enableErrorText: true + mandatory: true + contentItem: TextField { + id: passwordEdit + hidden: true + isError: password.errorTextVisible + KeyNavigation.up: usernameEdit + KeyNavigation.down: cancelButton + } + } + } + } + + buttons: [ + Button { + id: cancelButton + Layout.topMargin: 10 * DefaultStyle.dp + text: qsTr("Annuler") + inversedColors: true + onClicked: mainItem.rejected() + KeyNavigation.up: passwordEdit + KeyNavigation.right: connectButton + }, + Button { + id: connectButton + Layout.topMargin: 10 * DefaultStyle.dp + text: qsTr("Se connecter") + KeyNavigation.up: passwordEdit + KeyNavigation.right: cancelButton + onClicked: { + password.errorMessage = "" + if (passwordEdit.text.length == 0) { + password.errorMessage = qsTr("Veuillez saisir un mot de passe") + return + } + mainItem.accepted() + } + } + ] +} \ No newline at end of file diff --git a/Linphone/view/Item/Contact/ContactEdition.qml b/Linphone/view/Item/Contact/ContactEdition.qml index f84902d6e..b06b32925 100644 --- a/Linphone/view/Item/Contact/ContactEdition.qml +++ b/Linphone/view/Item/Contact/ContactEdition.qml @@ -84,6 +84,7 @@ RightPanelLayout { Layout.preferredHeight: 17 * DefaultStyle.dp iconSource: AppIcons.camera iconSize: 17 * DefaultStyle.dp + backgroundColor: "transparent" text: qsTr("Ajouter une image") KeyNavigation.down: editButton.visible ? editButton : givenNameEdit onClicked: fileDialog.open() @@ -97,6 +98,7 @@ RightPanelLayout { Layout.preferredHeight: 17 * DefaultStyle.dp iconSource: AppIcons.pencil iconSize: 17 * DefaultStyle.dp + backgroundColor: "transparent" text: qsTr("Modifier") KeyNavigation.down: givenNameEdit onClicked: fileDialog.open() @@ -118,6 +120,7 @@ RightPanelLayout { Layout.preferredWidth: width iconSize: 17 * DefaultStyle.dp iconSource: AppIcons.trashCan + backgroundColor: "transparent" text: qsTr("Supprimer") KeyNavigation.down: givenNameEdit onClicked: mainItem.contact.core.pictureUri = "" diff --git a/Linphone/view/Item/TextField.qml b/Linphone/view/Item/TextField.qml index 9f02a04b0..29e18b7c6 100644 --- a/Linphone/view/Item/TextField.qml +++ b/Linphone/view/Item/TextField.qml @@ -21,12 +21,14 @@ Control.TextField { } selectByMouse: true activeFocusOnTab: true + KeyNavigation.right: eyeButton property bool controlIsDown: false property bool hidden: false property bool isError: false property bool backgroundVisible: true property color backgroundColor: DefaultStyle.grey_100 + property color disabledBackgroundColor: DefaultStyle.grey_200 property color backgroundBorderColor: DefaultStyle.grey_200 property string initialText property int pixelSize: 14 * DefaultStyle.dp @@ -47,7 +49,7 @@ Control.TextField { visible: mainItem.backgroundVisible anchors.fill: parent radius: 79 * DefaultStyle.dp - color: mainItem.backgroundColor + color: mainItem.enabled ? mainItem.backgroundColor : mainItem.disabledBackgroundColor border.color: mainItem.isError ? DefaultStyle.danger_500main : mainItem.activeFocus @@ -105,6 +107,7 @@ Control.TextField { Button { id: eyeButton + KeyNavigation.left: mainItem property int rightMargin: 15 * DefaultStyle.dp z: 1 visible: mainItem.hidden diff --git a/Linphone/view/Page/Login/SIPLoginPage.qml b/Linphone/view/Page/Login/SIPLoginPage.qml index 61f1e4bf4..f9a9d5888 100644 --- a/Linphone/view/Page/Login/SIPLoginPage.qml +++ b/Linphone/view/Page/Login/SIPLoginPage.qml @@ -17,6 +17,7 @@ LoginLayout { visible: !SettingsCpp.assistantHideThirdPartyAccount spacing: 21 * DefaultStyle.dp Button { + id: backButton Layout.preferredHeight: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp icon.source: AppIcons.leftArrow @@ -81,20 +82,47 @@ LoginLayout { id: firstItem ColumnLayout { spacing: 0 - Text { - Layout.fillWidth: true - Layout.preferredWidth: rootStackView.width - wrapMode: Text.WordWrap - color: DefaultStyle.main2_600 - font { - pixelSize: 14 * DefaultStyle.dp - weight: 400* DefaultStyle.dp + ColumnLayout { + Text { + Layout.fillWidth: true + Layout.preferredWidth: rootStackView.width + wrapMode: Text.WordWrap + color: DefaultStyle.main2_600 + font { + pixelSize: 14 * DefaultStyle.dp + weight: 400* DefaultStyle.dp + } + text: "Certaines fonctionnalités nécessitent un compte Linphone, comme la messagerie de groupe, les vidéoconférences... + + Ces fonctionnalités sont cachées lorsque vous vous enregistrez avec un compte SIP tiers. + + Pour les activer dans un projet commercial, veuillez nous contacter. " + } + Text { + Layout.fillWidth: true + Layout.preferredWidth: rootStackView.width + wrapMode: Text.WordWrap + color: DefaultStyle.main2_600 + font { + pixelSize: 14 * DefaultStyle.dp + weight: 400* DefaultStyle.dp + } + text:"Ces fonctionnalités sont cachées lorsque vous vous enregistrez avec un compte SIP tiers." + } + Text { + Layout.fillWidth: true + Layout.preferredWidth: rootStackView.width + wrapMode: Text.WordWrap + color: DefaultStyle.main2_600 + font { + pixelSize: 14 * DefaultStyle.dp + weight: 400* DefaultStyle.dp + } + text: "Pour les activer dans un projet commercial, veuillez nous contacter. " } - text: "

Some features require a Linphone account, such as group messaging, video conferences...

-

These features are hidden when you register with a third party SIP account.

-

To enable it in a commercial projet, please contact us.

" } Button { + id: openLinkButton Layout.alignment: Qt.AlignCenter Layout.topMargin: 18 * DefaultStyle.dp text: "linphone.org/contact" @@ -107,12 +135,15 @@ LoginLayout { onClicked: { Qt.openUrlExternally(ConstantsCpp.ContactUrl) } + KeyNavigation.up: backButton + KeyNavigation.down: createAccountButton } Button { + id: createAccountButton Layout.topMargin: 85 * DefaultStyle.dp Layout.fillWidth: true inversedColors: true - text: qsTr("I prefer creating an account") + text: qsTr("Créer un compte linphone") leftPadding: 20 * DefaultStyle.dp rightPadding: 20 * DefaultStyle.dp topPadding: 11 * DefaultStyle.dp @@ -121,11 +152,14 @@ LoginLayout { console.debug("[SIPLoginPage] User: click register") mainItem.goToRegister() } + KeyNavigation.up: openLinkButton + KeyNavigation.down: continueButton } Button { + id: continueButton Layout.topMargin: 20 * DefaultStyle.dp Layout.fillWidth: true - text: qsTr("I understand") + text: qsTr("Je comprends") leftPadding: 20 * DefaultStyle.dp rightPadding: 20 * DefaultStyle.dp topPadding: 11 * DefaultStyle.dp @@ -133,6 +167,7 @@ LoginLayout { onClicked: { rootStackView.replace(secondItem) } + KeyNavigation.up: createAccountButton } Item { Layout.fillHeight: true @@ -154,6 +189,7 @@ LoginLayout { id: usernameEdit isError: username.errorTextVisible Layout.preferredWidth: 360 * DefaultStyle.dp + KeyNavigation.down: passwordEdit } } FormItemLayout { @@ -166,6 +202,8 @@ LoginLayout { isError: password.errorTextVisible hidden: true Layout.preferredWidth: 360 * DefaultStyle.dp + KeyNavigation.up: usernameEdit + KeyNavigation.down: domainEdit } } FormItemLayout { @@ -177,6 +215,8 @@ LoginLayout { id: domainEdit isError: domain.errorTextVisible Layout.preferredWidth: 360 * DefaultStyle.dp + KeyNavigation.up: passwordEdit + KeyNavigation.down: displayName } } FormItemLayout { @@ -184,6 +224,8 @@ LoginLayout { contentItem: TextField { id: displayName Layout.preferredWidth: 360 * DefaultStyle.dp + KeyNavigation.up: domainEdit + KeyNavigation.down: transportCbox } } FormItemLayout { @@ -289,6 +331,7 @@ LoginLayout { else if(password.activeFocus) domain.forceActiveFocus() } onPressed: connectionButton.trigger() + KeyNavigation.up: transportCbox } Item { Layout.fillHeight: true diff --git a/Linphone/view/Page/Main/ContactPage.qml b/Linphone/view/Page/Main/ContactPage.qml index abe75798e..fb3a86509 100644 --- a/Linphone/view/Page/Main/ContactPage.qml +++ b/Linphone/view/Page/Main/ContactPage.qml @@ -58,15 +58,19 @@ AbstractMainPage { aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend } - Dialog { - id: dialog - property var contact - text: (contact ? contact.core.displayName : "Contact") + " is about to be deleted. Do you want to continue ?" - onAccepted: { - var name = contact.core.displayName - contact.core.remove() - UtilsCpp.showInformationPopup(qsTr("Supprimé"), qsTr("%1 a été supprimé").arg(name)) - } + function deleteContact(contact) { + if (!contact) return + var mainWin = UtilsCpp.getMainWindow() + mainWin.showConfirmationLambdaPopup( + contact.core.displayName + qsTr("sera supprimé des contacts. Voulez-vous continuer ?"), + "", + function (confirmed) { + if (confirmed) { + var name = contact.core.displayName + contact.core.remove() + UtilsCpp.showInformationPopup(qsTr("Supprimé"), qsTr("%1 a été supprimé").arg(name)) } + } + ) } Popup { @@ -279,8 +283,7 @@ AbstractMainPage { mainItem.selectedContact = selectedContact } onContactDeletionRequested: (contact) => { - dialog.contact = contact - dialog.open() + mainItem.deleteContact(contact) } } } @@ -344,8 +347,7 @@ AbstractMainPage { mainItem.selectedContact = selectedContact } onContactDeletionRequested: (contact) => { - dialog.contact = contact - dialog.open() + mainItem.deleteContact(contact) } } } @@ -782,9 +784,7 @@ AbstractMainPage { color: DefaultStyle.danger_500main text: qsTr("Delete this contact") onClicked: { - // mainItem.selectedContact.core.remove() - dialog.contact = mainItem.selectedContact - dialog.open() + mainItem.deleteContact(contact) } } }