diff --git a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp index 3bdd3012..8631a823 100644 --- a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp +++ b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp @@ -1099,6 +1099,28 @@ void QMLBackend::setUpdateTrayIcon(QString const &stateString, QString const &st } +//**************************************************************************************************************************************************** +/// \param[in] helpFileName The name of the help file with extension (e.g. "WhyBridge.html"). +//**************************************************************************************************************************************************** +void QMLBackend::showHelpOverlay(QString const &helpFileName) { + QDir const basePath(":/qml/Resources/Help"); + QString const templatePath = basePath.filePath("Template.html"); + QFile templateFile(templatePath); + if (!templateFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + app().log().error("Could not load help overlay HTML template"); + return; + } + + QFile helpFile(basePath.filePath(helpFileName)); + if (!helpFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + app().log().error(QString("Could not load help overlay HTML file %1").arg(helpFileName)); + return; + } + + emit showWebFrameOverlayHTML(QString::fromUtf8(templateFile.readAll()).arg(QString::fromUtf8(helpFile.readAll()))); +} + + //**************************************************************************************************************************************************** /// \param[in] isOn Does bridge consider internet as on. //**************************************************************************************************************************************************** diff --git a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h index 0220f15e..035fbca8 100644 --- a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h +++ b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h @@ -211,6 +211,7 @@ public slots: // slots for functions that need to be processed locally. void setErrorTrayIcon(QString const& stateString, QString const &statusIcon); ///< Set the tray icon to 'error' state. void setWarnTrayIcon(QString const& stateString, QString const &statusIcon); ///< Set the tray icon to 'warn' state. void setUpdateTrayIcon(QString const& stateString, QString const &statusIcon); ///< Set the tray icon to 'update' state. + void showHelpOverlay(QString const &helpFileName); ///< Slot triggering the display of help content as an overlay. public slots: // slot for signals received from gRPC that need transformation instead of simple forwarding void internetStatusChanged(bool isOn); ///< Check if bridge considers internet as on. @@ -279,6 +280,7 @@ signals: // Signals received from the Go backend, to be forwarded to QML void showSettings(); ///< Signal for the 'showHelp' event (from the context menu). void showWebFrameWindow(QString const &url); ///< Signal the the 'showWebFrameWindow' event void showWebFrameOverlay(QString const &url); ////< Signal for the 'showWebFrameOverlay' event. + void showWebFrameOverlayHTML(QString const &html); ///< Signal to display HTML content in a web frame overlay. void selectUser(QString const& userID, bool forceShowWindow); ///< Signal emitted in order to selected a user with a given ID in the list. void genericError(QString const &title, QString const &description); ///< Signal for the 'genericError' gRPC stream event. void imapLoginWhileSignedOut(QString const& username); ///< Signal for the notification of IMAP login attempt on a signed out account. diff --git a/internal/frontend/bridge-gui/bridge-gui/Resources.qrc b/internal/frontend/bridge-gui/bridge-gui/Resources.qrc index b79a8b40..dccfa1ca 100644 --- a/internal/frontend/bridge-gui/bridge-gui/Resources.qrc +++ b/internal/frontend/bridge-gui/bridge-gui/Resources.qrc @@ -110,6 +110,10 @@ qml/Proton/WebFrame.qml qml/QuestionItem.qml qml/Resources/bug_report_flow.json + qml/Resources/Help/Template.html + qml/Resources/Help/WhyBridge.html + qml/Resources/Help/WhyCertificate.html + qml/Resources/Help/WhyProfileWarning.html qml/SettingsItem.qml qml/SettingsView.qml qml/SetupWizard/ClientListItem.qml diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Bridge.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Bridge.qml index ab759e83..96a9e5d7 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/Bridge.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/Bridge.qml @@ -53,6 +53,9 @@ QtObject { function onShowWebFrameOverlay(url) { mainWindow.showWebFrameOverlay(url); } + function onShowWebFrameOverlayHTML(html) { + mainWindow.showWebFrameOverlayHTML(html) + } function onShowWebFrameWindow(url) { webFrameWindow.url = url; webFrameWindow.show(); diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/MainWindow.qml b/internal/frontend/bridge-gui/bridge-gui/qml/MainWindow.qml index ea0763d4..575981f2 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/MainWindow.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/MainWindow.qml @@ -72,6 +72,11 @@ ApplicationWindow { webFrameOverlay.url = url; } + function showWebFrameOverlayHTML(html) { + webFrameOverlay.loadHTML(html); + webFrameOverlay.visible = true; + } + colorScheme: ProtonStyle.currentStyle height: ProtonStyle.window_default_height minimumHeight:ProtonStyle.window_minimum_height @@ -176,9 +181,6 @@ ApplicationWindow { onWizardEnded: { contentLayout.currentIndex = 0; } - onShowUnderConstruction: { - webFrameOverlay.showUnderConstruction(); - } } } WebFrame { diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Style.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Style.qml index 504bfdad..95c6d431 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Style.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Style.qml @@ -363,10 +363,11 @@ QtObject { property int web_view_button_width: 320 property int web_view_corner_radius: 10 property int web_view_overlay_button_vertical_margin: 10 - property int web_view_overlay_horizontal_margin: 10 - property int web_view_overlay_margin: 50 + property int web_view_overlay_horizontal_padding: 10 + property int web_view_overlay_horizontal_margin: 250 + property int web_view_overlay_vertical_margin: 50 property real web_view_overlay_opacity: 0.6 - property int web_view_overlay_vertical_margin: web_view_corner_radius + property int web_view_overlay_vertical_padding: web_view_corner_radius property int web_view_overley_border_width: 1 property int window_default_height: 780 diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/WebFrame.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/WebFrame.qml index a8f5812d..99e8d357 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/WebFrame.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/WebFrame.qml @@ -27,15 +27,8 @@ Item { function showBlankPage() { webView.loadHtml("blank", ""); } - - function showUnderConstruction() { - webView.loadHtml(` - - -Coming soon -

The content of this page is under construction.

- `, "") - root.visible = true; + function loadHTML(html) { + webView.loadHtml(html) } Rectangle { @@ -46,16 +39,19 @@ Item { } Rectangle { anchors.fill: parent - anchors.margins: overlay ? ProtonStyle.web_view_overlay_margin : 0 + anchors.bottomMargin: overlay ? ProtonStyle.web_view_overlay_vertical_margin : 0 + anchors.leftMargin: overlay ? ProtonStyle.web_view_overlay_horizontal_margin : 0 + anchors.rightMargin: overlay ? ProtonStyle.web_view_overlay_horizontal_margin : 0 + anchors.topMargin: overlay ? ProtonStyle.web_view_overlay_vertical_margin : 0 color: root.colorScheme.background_norm radius: ProtonStyle.web_view_corner_radius ColumnLayout { anchors.bottomMargin: 0 anchors.fill: parent - anchors.leftMargin: overlay ? ProtonStyle.web_view_overlay_horizontal_margin : 0 - anchors.rightMargin: overlay ? ProtonStyle.web_view_overlay_horizontal_margin : 0 - anchors.topMargin: overlay ? ProtonStyle.web_view_overlay_vertical_margin : 0 + anchors.leftMargin: overlay ? ProtonStyle.web_view_overlay_horizontal_padding : 0 + anchors.rightMargin: overlay ? ProtonStyle.web_view_overlay_horizontal_padding : 0 + anchors.topMargin: overlay ? ProtonStyle.web_view_overlay_vertical_padding : 0 spacing: 0 Rectangle { diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/Template.html b/internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/Template.html new file mode 100644 index 00000000..b9fad96e --- /dev/null +++ b/internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/Template.html @@ -0,0 +1,16 @@ + + + + + + + + +%1 + + \ No newline at end of file diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/WhyBridge.html b/internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/WhyBridge.html new file mode 100644 index 00000000..a26f4486 --- /dev/null +++ b/internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/WhyBridge.html @@ -0,0 +1,19 @@ +

Why do I need bridge?

+

+ Proton does not have access to the content of your messages, so it cannot share your unencrypted messages with your email client from the + Proton servers. +

+

+ Email clients such as Microsoft Outlook, Mozilla Thunderbird and Apple Mail use standard protocols named IMAP and SMTP to receive and send emails. +

+

+ Even though the IMAP and SMTP protocols can use secure channels (using SSL/TLS), they do not offer support for encrypted messages. + Because Proton does not have access to the content of your messages, it is not possible to configure your email client to connect directly to + Proton servers. +

+

+ The key to solving this problem is Bridge. Once installed on your computer and connected to your Proton account, Bridge can access your + encrypted messages stored on the Proton servers. Bridge integrates an IMAP and a SMTP server that run on your computer and are accessible only + to applications executing on your machine. Your email client connects to these local servers and Bridge is responsible for seamlessly encrypting + and decrypting the messages that you send and receive. +

diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/WhyCertificate.html b/internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/WhyCertificate.html new file mode 100644 index 00000000..045ab10b --- /dev/null +++ b/internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/WhyCertificate.html @@ -0,0 +1,19 @@ +

Why do I need to install a certificate when configuring Apple Mail with Bridge?

+

+ Apple Mail requires a secure channel for communications with email servers, and the server needs to be acknowledged as trusted. +

+

+ In order to communicate with Bridge, Apple Mail requires secure connections using SSL/TLS. This cryptographic protocol includes an identity + verification system using certificates. For publicly available servers, certificates are normally issued and digitally signed by a certificate + authority, such as Let's Encrypt. This is not possible for Bridge, as the IMAP and SMTP servers are running on your own computer, and are not + accessible from any network (local or internet). +

+

+ The solution is to use a self-signed certificate. When setting up an email account where the server provides a self-signed certificate, most + email clients will issue a warning asking you whether you trust the server or not, because the certificate was not issued by a certificate + authority. +

+

+ Apple Mail requires an extra step. It will simply refuse to connect if the certificate is not set as trusted. Bridge solves this by storing this + certificate in the macOS keychain. This operation requires that you provide your macOS account password. +

diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/WhyProfileWarning.html b/internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/WhyProfileWarning.html new file mode 100644 index 00000000..3b426460 --- /dev/null +++ b/internal/frontend/bridge-gui/bridge-gui/qml/Resources/Help/WhyProfileWarning.html @@ -0,0 +1,21 @@ +

Why is there a warning sign when installing the Bridge profile on macOS?

+

+ This warning indicates that the certificate used to secure the communication channel between Bridge and your email client is not signed by a + trusted third party. +

+

+ In order to communicate with Bridge, Apple Mail requires secure connections using SSL/TLS. This cryptographic protocol includes an identity + verification system using certificates. For publicly available servers, certificates are normally issued and digitally signed by a certificate + authority, such as Let's Encrypt. This is not possible for Bridge, as the IMAP and SMTP servers are running on your own computer, and are not + accessible from any network (local or internet). +

+

+ The solution is to use a self-signed certificate. When setting up an email account where the server provides a self-signed certificate, most + email clients will issue a warning asking you whether you trust the server or not, because the certificate was not issued by a certificate + authority. The client has no way of verifying that the server is who it pretends to be. +

+

+ You can safely ignore this warning. The check concerns only the communication between your email client and Bridge, which occurs within your + computer. On the other end, the communication between Bridge and the Proton servers uses the HTTPS protocol, and the identity of the remote + server is verified by Bridge. +

diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/ClientConfigAppleMail.qml b/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/ClientConfigAppleMail.qml index d7516a3b..b537c44e 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/ClientConfigAppleMail.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/ClientConfigAppleMail.qml @@ -28,7 +28,6 @@ Item { signal appleMailAutoconfigProfileInstallPageShow function showAutoconfig() { - certificateInstall.waitingForCert = false; if (Backend.isTLSCertificateInstalled()) { showProfileInstall(); } else { @@ -41,6 +40,7 @@ Item { appleMailAutoconfigCertificateInstallPageShown(); } function showProfileInstall() { + profileInstall.reset(); stack.currentIndex = ClientConfigAppleMail.Screen.ProfileInstall; appleMailAutoconfigProfileInstallPageShow(); } @@ -193,6 +193,13 @@ Item { // stack index 1 Item { id: profileInstall + + property bool profilePaneLaunched: false + + function reset() { + profilePaneLaunched = false; + } + Layout.fillHeight: true Layout.fillWidth: true @@ -239,11 +246,15 @@ Item { Button { Layout.fillWidth: true colorScheme: wizard.colorScheme - text: qsTr("Install the profile") + text: profileInstall.profilePaneLaunched ? qsTr("I have installed the profile") : qsTr("Install the profile") onClicked: { - wizard.user.configureAppleMail(wizard.address); - wizard.showClientConfigEnd(); + if (profileInstall.profilePaneLaunched) { + wizard.showClientConfigEnd(); + } else { + wizard.user.configureAppleMail(wizard.address); + profileInstall.profilePaneLaunched = true; + } } } Button { diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/LeftPane.qml b/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/LeftPane.qml index 88cf9e27..0def652c 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/LeftPane.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/LeftPane.qml @@ -26,7 +26,7 @@ Item { function showAppleMailAutoconfigCertificateInstall() { showAppleMailAutoconfigCommon(); descriptionLabel.text = qsTr("Apple Mail configuration is mostly automated, but in order to work, Bridge needs to install a certificate in your keychain."); - linkLabel1.setCallback(showUnderConstruction, qsTr("Why is this certificate needed?"), false); + linkLabel1.setCallback(function() { Backend.showHelpOverlay("WhyCertificate.html"); }, qsTr("Why is this certificate needed?"), false); } function showAppleMailAutoconfigCommon() { titleLabel.text = ""; @@ -39,7 +39,7 @@ Item { function showAppleMailAutoconfigProfileInstall() { showAppleMailAutoconfigCommon(); descriptionLabel.text = qsTr("The final step before you can start using Apple Mail is to install the Bridge server profile in the system preferences.\n\nAdding a server profile is necessary to ensure that your Mac can receive and send Proton Mails."); - linkLabel1.setCallback(showUnderConstruction, qsTr("Why is there a yellow warning sign?"), false); + linkLabel1.setCallback(function() { Backend.showHelpOverlay("WhyProfileWarning.html"); }, qsTr("Why is there a yellow warning sign?"), false); linkLabel2.setCallback(wizard.showClientParams, qsTr("Configure Apple Mail manually"), false); } function showClientSelector(newAccount = true) { @@ -63,15 +63,12 @@ Item { function showOnboarding() { titleLabel.text = (Backend.users.count === 0) ? qsTr("Welcome to\nProton Mail Bridge") : qsTr("Add a Proton Mail account"); descriptionLabel.text = qsTr("Bridge is the gateway between your Proton account and your email client. It runs in the background and encrypts and decrypts your messages seamlessly. "); - linkLabel1.setCallback(showUnderConstruction, qsTr("Why do I need Bridge?"), false); + linkLabel1.setCallback(function() { Backend.showHelpOverlay("WhyBridge.html"); }, qsTr("Why do I need Bridge?"), false); linkLabel2.clear(); root.iconSource = "/qml/icons/img-welcome.svg"; root.iconHeight = 148; root.iconWidth = 265; } - function showUnderConstruction() { - wizard.showUnderConstruction(); - } Connections { function onLogin2FARequested() { diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/SetupWizard.qml b/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/SetupWizard.qml index 060d3b93..3248184a 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/SetupWizard.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/SetupWizard/SetupWizard.qml @@ -43,7 +43,6 @@ Item { signal bugReportRequested signal wizardEnded - signal showUnderConstruction function _showClientConfig() { showClientConfig(root.user, root.address, false);