Other: bridge-gui-tester.

WIP: Added bridge-gui-test app.
WIP: goos.
WIP: event sending.
WIP: include bridge-gui-tester in frontend project
WIP: app end login event buttons.
WIP: login events. .
WIP: moved and renamed tab code
WIP: wired login logic.
WIP: setColorScheme and loginAbort.
WIP: more calls implemented + random users and numbers.
WIP: mail calls.
WIP: bug reports.
WIP: more signal send via the grpc Qt proxy.
WIP: Qt proxy on the event stream tab.
WIP: user change events wiring.
WIP: GUI changes.
WIP: minor refactoring.
WIP: remove event stream tab.
WIP: GUI changes.
WIP: separate logs, cache and keychain implemented.
WIP: Automatic update.

Other: fix linux build.

WIP: fix for live addition/modification/removal of users on the server side.
This commit is contained in:
Xavier Michelon
2022-08-04 16:51:42 +02:00
committed by Jakub
parent 42e9b6d2f3
commit 4ed9625959
33 changed files with 5504 additions and 2 deletions

View File

@ -31,4 +31,4 @@ project(frontend)
add_subdirectory(bridgepp)
add_subdirectory(bridge-gui)
add_subdirectory(bridge-gui-tester)

View File

@ -0,0 +1,106 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "AppController.h"
#include "GRPCService.h"
#include <bridgepp/GRPC/GRPCUtils.h>
#include <bridgepp/Exception/Exception.h>
#include "MainWindow.h"
#include <bridgepp/Log/Log.h>
using namespace bridgepp;
//****************************************************************************************************************************************************
/// \return A reference to the application controller.
//****************************************************************************************************************************************************
AppController &app()
{
static AppController app;
return app;
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
AppController::AppController()
: log_(std::make_unique<Log>())
, bridgeGUILog_(std::make_unique<Log>())
, grpc_(std::make_unique<GRPCService>())
{
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
AppController::~AppController() // NOLINT(modernize-use-equals-default): implementation in cpp file is required because of forward declaration of Log in header
{
}
//****************************************************************************************************************************************************
/// \param[in] mainWindow The main window.
//****************************************************************************************************************************************************
void AppController::setMainWindow(MainWindow *mainWindow)
{
mainWindow_ = mainWindow;
grpc_->connectProxySignals();
}
//****************************************************************************************************************************************************
/// \return The main window.
//****************************************************************************************************************************************************
MainWindow &AppController::mainWindow()
{
if (!mainWindow_)
throw Exception("mainWindow has not yet been registered.");
return *mainWindow_;
}
//****************************************************************************************************************************************************
/// \return A reference to the log.
//****************************************************************************************************************************************************
bridgepp::Log &AppController::log()
{
return *log_;
}
//****************************************************************************************************************************************************
/// \return A reference to the bridge-gui log.
//****************************************************************************************************************************************************
bridgepp::Log &AppController::bridgeGUILog()
{
return *bridgeGUILog_;
}
//****************************************************************************************************************************************************
/// \return A reference to the gRPC service.
//****************************************************************************************************************************************************
GRPCService &AppController::grpc()
{
return *grpc_;
}

View File

@ -0,0 +1,63 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_APP_CONTROLLER_H
#define BRIDGE_GUI_TESTER_APP_CONTROLLER_H
class MainWindow;
class GRPCService;
namespace grpc { class StreamEvent; }
namespace bridgepp { class Log; }
//**********************************************************************************************************************
/// \brief Application controller class
//**********************************************************************************************************************
class AppController : public QObject
{
Q_OBJECT
public: // member functions.
friend AppController &app();
AppController(AppController const &) = delete; ///< Disabled copy-constructor.
AppController(AppController &&) = delete; ///< Disabled assignment copy-constructor.
~AppController() override; ///< Destructor.
AppController &operator=(AppController const &) = delete; ///< Disabled assignment operator.
AppController &operator=(AppController &&) = delete; ///< Disabled move assignment operator.
void setMainWindow(MainWindow *mainWindow); ///< Set the main window.
MainWindow &mainWindow(); ///< Return the main window.
bridgepp::Log &log(); ///< Return a reference to the log.
bridgepp::Log &bridgeGUILog(); ///< Return a reference to the bridge-gui log.
GRPCService &grpc(); ///< Return a reference to the gRPC service.
private: // member functions.
AppController(); ///< Default constructor.
private: // data members.
MainWindow *mainWindow_ { nullptr }; ///< The main window.
std::unique_ptr<bridgepp::Log> log_; ///< The log.
std::unique_ptr<bridgepp::Log> bridgeGUILog_; ///< The bridge-gui log.
std::unique_ptr<GRPCService> grpc_; ///< The gRPC service.
};
AppController &app(); ///< Return a reference to the app controller.
#endif // BRIDGE_GUI_TESTER_APP_CONTROLLER_H

View File

@ -0,0 +1,90 @@
# Copyright (c) 2022 Proton AG
#
# This file is part of Proton Mail Bridge.
#
# Proton Mail Bridge 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.
#
# Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
cmake_minimum_required(VERSION 3.22)
set(VCPKG_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../../../extern/vcpkg")
include(../BridgeSetup.cmake)
#*****************************************************************************************************************************************************
# Project
#*****************************************************************************************************************************************************
project(bridge-gui-tester LANGUAGES CXX)
if (NOT DEFINED BRIDGE_APP_VERSION)
message(FATAL_ERROR "BRIDGE_APP_VERSION is not defined.")
else()
message(STATUS "Bridge version is ${BRIDGE_APP_VERSION}")
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#*****************************************************************************************************************************************************
# Qt
#*****************************************************************************************************************************************************
if (NOT DEFINED ENV{QT6DIR})
message(FATAL_ERROR "QT6DIR needs to be defined and point to the root of your Qt 6 folder (e.g. /Users/MyName/Qt/6.3.1/clang_64).")
endif()
set(CMAKE_PREFIX_PATH $ENV{QT6DIR} ${CMAKE_PREFIX_PATH})
find_package(Qt6 COMPONENTS Core Gui Widgets Qml REQUIRED)
qt_standard_project_setup()
message(STATUS "Using Qt ${Qt6_VERSION}")
#*****************************************************************************************************************************************************
# Source files and output
#*****************************************************************************************************************************************************
if (NOT TARGET bridgepp)
add_subdirectory(../bridgepp bridgepp)
endif()
add_executable(bridge-gui-tester
AppController.cpp AppController.h
main.cpp
MainWindow.cpp MainWindow.h
GRPCQtProxy.cpp GRPCQtProxy.h
GRPCService.cpp GRPCService.h
GRPCServerWorker.cpp GRPCServerWorker.h
Tabs/SettingsTab.cpp Tabs/SettingsTab.h
Tabs/UsersTab.cpp Tabs/UsersTab.h
UserDialog.cpp UserDialog.h
UserTable.cpp UserTable.h
)
target_precompile_headers(bridge-gui-tester PRIVATE Pch.h)
target_include_directories(bridge-gui-tester PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(bridge-gui-tester PRIVATE BRIDGE_APP_VERSION=\"${BRIDGE_APP_VERSION}\")
target_link_libraries(bridge-gui-tester
Qt6::Core
Qt6::Gui
Qt6::Widgets
Qt6::Qml
bridgepp
)

View File

@ -0,0 +1,210 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "GRPCQtProxy.h"
#include "MainWindow.h"
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
GRPCQtProxy::GRPCQtProxy()
: QObject(nullptr)
{
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void GRPCQtProxy::connectSignals()
{
MainWindow &mainWindow = app().mainWindow();
SettingsTab &settingsTab = mainWindow.settingsTab();
UsersTab &usersTab = mainWindow.usersTab();
connect(this, &GRPCQtProxy::delayedEventRequested, &mainWindow, &MainWindow::sendDelayedEvent);
connect(this, &GRPCQtProxy::setIsAutostartOnReceived, &settingsTab, &SettingsTab::setIsAutostartOn);
connect(this, &GRPCQtProxy::setIsBetaEnabledReceived, &settingsTab, &SettingsTab::setIsBetaEnabled);
connect(this, &GRPCQtProxy::setColorSchemeNameReceived, &settingsTab, &SettingsTab::setColorSchemeName);
connect(this, &GRPCQtProxy::reportBugReceived, &settingsTab, &SettingsTab::setBugReport);
connect(this, &GRPCQtProxy::setIsStreamingReceived, &settingsTab, &SettingsTab::setIsStreaming);
connect(this, &GRPCQtProxy::setClientPlatformReceived, &settingsTab, &SettingsTab::setClientPlatform);
connect(this, &GRPCQtProxy::changePortsReceived, &settingsTab, &SettingsTab::changePorts);
connect(this, &GRPCQtProxy::setUseSSLForSMTPReceived, &settingsTab, &SettingsTab::setUseSSLForSMTP);
connect(this, &GRPCQtProxy::setIsDoHEnabledReceived, &settingsTab, &SettingsTab::setIsDoHEnabled);
connect(this, &GRPCQtProxy::changeLocalCacheReceived, &settingsTab, &SettingsTab::changeLocalCache);
connect(this, &GRPCQtProxy::setIsAutomaticUpdateOnReceived, &settingsTab, &SettingsTab::setIsAutomaticUpdateOn);
connect(this, &GRPCQtProxy::setUserSplitModeReceived, &usersTab, &UsersTab::setUserSplitMode);
connect(this, &GRPCQtProxy::removeUserReceived, &usersTab, &UsersTab::removeUser);
connect(this, &GRPCQtProxy::logoutUserReceived, &usersTab, &UsersTab::logoutUser);
connect(this, &GRPCQtProxy::setUserSplitModeReceived, &usersTab, &UsersTab::setUserSplitMode);
connect(this, &GRPCQtProxy::configureUserAppleMailReceived, &usersTab, &UsersTab::configureUserAppleMail);
}
//****************************************************************************************************************************************************
/// \param[in] event The event.
//****************************************************************************************************************************************************
void GRPCQtProxy::sendDelayedEvent(bridgepp::SPStreamEvent const &event)
{
emit delayedEventRequested(event);
}
//****************************************************************************************************************************************************
/// \param[in] on The value.
//****************************************************************************************************************************************************
void GRPCQtProxy::setIsAutostartOn(bool on)
{
emit setIsAutostartOnReceived(on);
}
//****************************************************************************************************************************************************
/// \param[in] enabled The value.
//****************************************************************************************************************************************************
void GRPCQtProxy::setIsBetaEnabled(bool enabled)
{
emit setIsBetaEnabledReceived(enabled);
}
//****************************************************************************************************************************************************
/// \param[in] name The color scheme.
//****************************************************************************************************************************************************
void GRPCQtProxy::setColorSchemeName(QString const &name)
{
emit setColorSchemeNameReceived(name);
}
//****************************************************************************************************************************************************
/// \param[in] osType The OS type.
/// \param[in] osVersion The OS version.
/// \param[in] emailClient The email client.
/// \param[in] address The address.
/// \param[in] description The description.
/// \param[in] includeLogs Should the logs be included.
//****************************************************************************************************************************************************
void GRPCQtProxy::reportBug(QString const &osType, QString const &osVersion, QString const &emailClient, QString const &address,
QString const &description, bool includeLogs)
{
emit reportBugReceived(osType, osVersion, emailClient, address, description, includeLogs);
}
//****************************************************************************************************************************************************
/// \param[in] isStreaming Is the gRPC server streaming.
//****************************************************************************************************************************************************
void GRPCQtProxy::setIsStreaming(bool isStreaming)
{
emit setIsStreamingReceived(isStreaming);
}
//****************************************************************************************************************************************************
/// \param[in] clientPlatform The client platform.
//****************************************************************************************************************************************************
void GRPCQtProxy::setClientPlatform(QString const &clientPlatform)
{
emit setClientPlatformReceived(clientPlatform);
}
//****************************************************************************************************************************************************
/// \param[in] imapPort The IMAP port
/// \param[in] smtpPort The SMTP port
//****************************************************************************************************************************************************
void GRPCQtProxy::changePorts(qint32 imapPort, qint32 smtpPort)
{
emit changePortsReceived(imapPort, smtpPort);
}
//****************************************************************************************************************************************************
/// \param[in] use Should SMTP use SSL?
//****************************************************************************************************************************************************
void GRPCQtProxy::setUseSSLForSMTP(bool use)
{
emit setUseSSLForSMTPReceived(use);
}
//****************************************************************************************************************************************************
/// \param[in] enabled Is DoH enabled?
//****************************************************************************************************************************************************
void GRPCQtProxy::setIsDoHEnabled(bool enabled)
{
emit setIsDoHEnabledReceived(enabled);
}
//****************************************************************************************************************************************************
/// \param[in] enabled is cache on disk enabled?
/// \param[in] path The path for the cache on disk.
//****************************************************************************************************************************************************
void GRPCQtProxy::changeLocalCache(bool enabled, QString const &path)
{
emit changeLocalCacheReceived(enabled, path);
}
//****************************************************************************************************************************************************
/// \param[in] on Is automatic update on?
//****************************************************************************************************************************************************
void GRPCQtProxy::setIsAutomaticUpdateOn(bool on)
{
emit setIsAutomaticUpdateOnReceived(on);
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
/// \param[in] makeItActive Should split mode be active.
//****************************************************************************************************************************************************
void GRPCQtProxy::setUserSplitMode(QString const &userID, bool makeItActive)
{
emit setUserSplitModeReceived(userID, makeItActive);
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
//****************************************************************************************************************************************************
void GRPCQtProxy::logoutUser(QString const &userID)
{
emit logoutUserReceived(userID);
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
//****************************************************************************************************************************************************
void GRPCQtProxy::removeUser(QString const &userID)
{
emit removeUserReceived(userID);
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
/// \param[in] address The address.
//****************************************************************************************************************************************************
void GRPCQtProxy::configureUserAppleMail(QString const &userID, QString const &address)
{
emit configureUserAppleMailReceived(userID, address);
}

View File

@ -0,0 +1,80 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_GRPC_QT_PROXY_H
#define BRIDGE_GUI_TESTER_GRPC_QT_PROXY_H
#include <bridgepp/GRPC/GRPCUtils.h>
//****************************************************************************************************************************************************
/// \brief Proxy object used by the gRPC service (which does not inherit QObject) to use the Qt Signal/Slot system.
//****************************************************************************************************************************************************
class GRPCQtProxy : public QObject
{
Q_OBJECT
public: // member functions.
GRPCQtProxy(); ///< Default constructor.
GRPCQtProxy(GRPCQtProxy const &) = delete; ///< Disabled copy-constructor.
GRPCQtProxy(GRPCQtProxy &&) = delete; ///< Disabled assignment copy-constructor.
~GRPCQtProxy() override = default; ///< Destructor.
GRPCQtProxy &operator=(GRPCQtProxy const &) = delete; ///< Disabled assignment operator.
GRPCQtProxy &operator=(GRPCQtProxy &&) = delete; ///< Disabled move assignment operator.
void connectSignals(); // connect the signals to the main window.
void sendDelayedEvent(bridgepp::SPStreamEvent const &event); ///< Sends a delayed stream event.
void setIsAutostartOn(bool on); ///< Forwards a SetIsAutostartOn call via a Qt signal.
void setIsBetaEnabled(bool enabled); ///< Forwards a SetIsBetaEnabled call via a Qt signal.
void setColorSchemeName(QString const &name); ///< Forward a SetColorSchemeName call via a Qt Signal
void reportBug(QString const &osType, QString const &osVersion, QString const &emailClient, QString const &address,
QString const &description, bool includeLogs); ///< Forwards a ReportBug call via a Qt signal.
void setIsStreaming(bool isStreaming); ///< Forward a isStreaming internal messages via a Qt signal.
void setClientPlatform(QString const &clientPlatform); ///< Forward s setClientPlatform call via a Qt signal.
void changePorts(qint32 imapPort, qint32 smtpPort); ///< Forwards a ChangePorts call via a Qt signal.
void setUseSSLForSMTP(bool use); ///< Forwards a SetUseSSLForSMTP call via a Qt signal.
void setIsDoHEnabled(bool enabled); ///< Forwards a setIsDoHEnabled call via a Qt signal.
void changeLocalCache(bool enabled, QString const &path); ///< Forwards a ChangeLocalPath call via a Qt signal.
void setIsAutomaticUpdateOn(bool on); ///< Forwards a SetIsAutomaticUpdateOn call via a Qt signal.
void setUserSplitMode(QString const &userID, bool makeItActive); ///< Forwards a setUserSplitMode call via a Qt signal.
void logoutUser(QString const &userID); ///< Forwards a logoutUser call via a Qt signal.
void removeUser(QString const &userID); ///< Forwards a removeUser call via a Qt signal.
void configureUserAppleMail(QString const &userID, QString const &address); ///< Forwards a configureUserAppleMail call via a Qt signal.
signals:
void delayedEventRequested(bridgepp::SPStreamEvent const &event); ///< Signal for sending a delayed event. delayed is set in the UI.
void setIsAutostartOnReceived(bool on); ///< Forwards a SetIsAutostartOn call via a Qt signal.
void setIsBetaEnabledReceived(bool enabled); ///< Forwards a SetIsBetaEnabled call via a Qt signal.
void setColorSchemeNameReceived(QString const &name); ///< Forward a SetColorScheme call via a Qt Signal
void reportBugReceived(QString const &osType, QString const &osVersion, QString const &emailClient, QString const &address,
QString const &description, bool includeLogs); ///< Signal for the ReportBug gRPC call
void setIsStreamingReceived(bool isStreaming); ///< Signal for the IsStreaming internal message.
void setClientPlatformReceived(QString const &clientPlatform); ///< Signal for the SetClientPlatform gRPC call.
void changePortsReceived(qint32 imapPort, qint32 smtpPort); ///< Signal for the ChangePorts gRPC call.
void setUseSSLForSMTPReceived(bool use); ///< Signal for the SetUseSSLForSMTP gRPC call.
void setIsDoHEnabledReceived(bool enabled); ///< Signal for the SetIsDoHEnabled gRPC call.
void changeLocalCacheReceived(bool enabled, QString const &path); ///< Signal for the ChangeLocalPath gRPC call.
void setIsAutomaticUpdateOnReceived(bool on); ///< Signal for the SetIsAutomaticUpdateOn gRPC call.
void setUserSplitModeReceived(QString const &userID, bool makeItActive); ///< Signal for the SetUserSplitModeReceived gRPC call.
void logoutUserReceived(QString const &userID); ///< Signal for the LogoutUserReceived gRPC call.
void removeUserReceived(QString const &userID); ///< Signal for the RemoveUserReceived gRPC call.
void configureUserAppleMailReceived(QString const &userID, QString const &address); ///< Signal for the ConfigureAppleMail gRPC call.
};
#endif //BRIDGE_GUI_TESTER_GRPC_QT_PROXY_H

View File

@ -0,0 +1,109 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "GRPCServerWorker.h"
#include "GRPCService.h"
#include <bridgepp/Exception/Exception.h>
#include <bridgepp/GRPC/GRPCUtils.h>
using namespace bridgepp;
using namespace grpc;
namespace
{
//****************************************************************************************************************************************************
/// \return The content of the file.
//****************************************************************************************************************************************************
QString loadAsciiTextFile(QString const &path) {
QFile file(path);
return file.open(QIODevice::ReadOnly | QIODevice::Text) ? QString::fromLocal8Bit(file.readAll()) : QString();
}
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
GRPCServerWorker::GRPCServerWorker(QObject *parent)
: Worker(parent)
{
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void GRPCServerWorker::run()
{
try
{
emit started();
QString cert = loadAsciiTextFile(serverCertificatePath());
if (cert.isEmpty())
throw Exception("Could not locate server certificate. Make sure to launch bridge once before starting this application");
QString key = loadAsciiTextFile(serverKeyPath());
if (key.isEmpty())
throw Exception("Could not locate server key. Make sure to launch bridge once before starting this application");
SslServerCredentialsOptions::PemKeyCertPair pair;
pair.private_key = key.toStdString();
pair.cert_chain = cert.toStdString();
SslServerCredentialsOptions ssl_opts;
ssl_opts.pem_root_certs="";
ssl_opts.pem_key_cert_pairs.push_back(pair);
std::shared_ptr<ServerCredentials> credentials = grpc::SslServerCredentials(ssl_opts);
ServerBuilder builder;
builder.AddListeningPort("127.0.0.1:9292", credentials);
builder.RegisterService(&app().grpc());
server_ = builder.BuildAndStart();
if (!server_)
throw Exception("Could not create gRPC server.");
app().log().debug("gRPC Server started.");
server_->Wait();
emit finished();
app().log().debug("gRPC Server exited.");
}
catch (Exception const &e)
{
if (server_)
server_->Shutdown();
emit error(e.qwhat());
}
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void GRPCServerWorker::stop()
{
if (server_)
server_->Shutdown();
}

View File

@ -0,0 +1,50 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_SERVER_WORKER_H
#define BRIDGE_GUI_TESTER_SERVER_WORKER_H
#include <bridgepp/Worker/Worker.h>
#include "GRPCService.h"
#include <grpcpp/grpcpp.h>
//**********************************************************************************************************************
/// \brief gRPC server worker
//**********************************************************************************************************************
class GRPCServerWorker : public bridgepp::Worker
{
Q_OBJECT
public: // member functions.
explicit GRPCServerWorker(QObject *parent); ///< Default constructor.
GRPCServerWorker(GRPCServerWorker const &) = delete; ///< Disabled copy-constructor.
GRPCServerWorker(GRPCServerWorker &&) = delete; ///< Disabled assignment copy-constructor.
~GRPCServerWorker() override = default; ///< Destructor.
GRPCServerWorker &operator=(GRPCServerWorker const &) = delete; ///< Disabled assignment operator.
GRPCServerWorker &operator=(GRPCServerWorker &&) = delete; ///< Disabled move assignment operator.
void run() override; ///< Run the worker.
void stop(); ///< Stop the gRPC service.
private: // data members
std::unique_ptr<grpc::Server> server_ { nullptr }; ///< The gRPC server.
};
#endif // BRIDGE_GUI_TESTER_SERVER_WORKER_H

View File

@ -0,0 +1,878 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "GRPCService.h"
#include "MainWindow.h"
#include <bridgepp/BridgeUtils.h>
#include <bridgepp/GRPC/EventFactory.h>
using namespace grpc;
using namespace google::protobuf;
using namespace bridgepp;
namespace
{
QString const defaultKeychain = "defaultKeychain"; ///< The default keychain.
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void GRPCService::connectProxySignals()
{
qtProxy_.connectSignals();
}
//****************************************************************************************************************************************************
/// \return true iff the service is streaming events.
//****************************************************************************************************************************************************
bool GRPCService::isStreaming() const
{
QMutexLocker locker(&eventStreamMutex_);
return isStreaming_;
}
//****************************************************************************************************************************************************
/// \param[in] request the request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::AddLogEntry(ServerContext *, AddLogEntryRequest const *request, Empty *)
{
app().bridgeGUILog().addEntry(logLevelFromGRPC(request->level()), QString::fromStdString(request->message()));
return Status::OK;
}
//****************************************************************************************************************************************************
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::GuiReady(ServerContext *, Empty const *, Empty *)
{
app().log().debug(__FUNCTION__);
app().mainWindow().settingsTab().setGUIReady(true);
return Status::OK;
}
//****************************************************************************************************************************************************
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::Quit(ServerContext *, Empty const *, Empty *)
{
// We do not actually quit.
app().log().debug(__FUNCTION__);
return Status::OK;
}
//****************************************************************************************************************************************************
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::Restart(ServerContext *, Empty const *, Empty *)
{
// we do not actually restart.
app().log().debug(__FUNCTION__);
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::ShowOnStartup(ServerContext *, Empty const *, BoolValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().showOnStartup());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::ShowSplashScreen(ServerContext *, Empty const *, BoolValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().showSplashScreen());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::IsFirstGuiStart(ServerContext *, Empty const *, BoolValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().isFirstGUIStart());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::SetIsAutostartOn(ServerContext *, BoolValue const *request, Empty *)
{
app().log().debug(__FUNCTION__);
app().mainWindow().settingsTab().setIsAutostartOn(request->value());
qtProxy_.sendDelayedEvent(newToggleAutostartFinishedEvent());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::IsAutostartOn(ServerContext *, Empty const *, BoolValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().isAutostartOn());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::SetIsBetaEnabled(ServerContext *, BoolValue const *request, Empty *)
{
app().log().debug(__FUNCTION__);
qtProxy_.setIsBetaEnabled(request->value());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::IsBetaEnabled(ServerContext *, Empty const *, BoolValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().isBetaEnabled());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::GoOs(ServerContext *, Empty const *, StringValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().os().toStdString());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::TriggerReset(ServerContext *, Empty const *, Empty *)
{
app().log().debug(__FUNCTION__);
app().log().info("Bridge GUI requested a reset");
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
grpc::Status GRPCService::Version(ServerContext *, Empty const *, StringValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().bridgeVersion().toStdString());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::LogsPath(ServerContext *, Empty const *, StringValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().logsPath().toStdString());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::LicensePath(ServerContext *, Empty const *, StringValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().licensePath().toStdString());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::ReleaseNotesPageLink(ServerContext *, Empty const *, StringValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().releaseNotesPageLink().toStdString());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::DependencyLicensesLink(ServerContext *, Empty const *, StringValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().dependencyLicenseLink().toStdString());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::LandingPageLink(ServerContext *, Empty const *, StringValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().landingPageLink().toStdString());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::SetColorSchemeName(ServerContext *, StringValue const *request, Empty *)
{
app().log().debug(__FUNCTION__);
qtProxy_.setColorSchemeName(QString::fromStdString(request->value()));
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::ColorSchemeName(ServerContext *, Empty const *, StringValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().colorSchemeName().toStdString());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::CurrentEmailClient(ServerContext *, Empty const *, StringValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().currentEmailClient().toStdString());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] response The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::ForceLauncher(ServerContext *, StringValue const *request, Empty *)
{
app().log().debug(__FUNCTION__);
app().log().info(QString("ForceLauncher: %1").arg(QString::fromStdString(request->value())));
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request
//****************************************************************************************************************************************************
Status GRPCService::ReportBug(ServerContext *, ReportBugRequest const *request, Empty *)
{
app().log().debug(__FUNCTION__);
SettingsTab &tab = app().mainWindow().settingsTab();
qtProxy_.reportBug(QString::fromStdString(request->ostype()), QString::fromStdString(request->osversion()),
QString::fromStdString(request->emailclient()), QString::fromStdString(request->address()), QString::fromStdString(request->description()),
request->includelogs());
qtProxy_.sendDelayedEvent(tab.nextBugReportWillSucceed() ? newReportBugSuccessEvent() : newReportBugErrorEvent());
qtProxy_.sendDelayedEvent(newReportBugFinishedEvent());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::Login(ServerContext *, LoginRequest const *request, Empty *)
{
app().log().debug(__FUNCTION__);
UsersTab &usersTab = app().mainWindow().usersTab();
loginUsername_ = QString::fromStdString(request->username());
if (usersTab.nextUserUsernamePasswordError())
{
qtProxy_.sendDelayedEvent(newLoginError(LoginErrorType::USERNAME_PASSWORD_ERROR, "Username/password error."));
return Status::OK;
}
if (usersTab.nextUserFreeUserError())
{
qtProxy_.sendDelayedEvent(newLoginError(LoginErrorType::FREE_USER, "Free user error."));
return Status::OK;
}
if (usersTab.nextUserTFARequired())
{
qtProxy_.sendDelayedEvent(newLoginTfaRequestedEvent(loginUsername_));
return Status::OK;
}
if (usersTab.nextUserTwoPasswordsRequired())
{
qtProxy_.sendDelayedEvent(newLoginTwoPasswordsRequestedEvent());
return Status::OK;
}
SPUser const user = randomUser();
QString const userID = user->id();
user->setUsername(QString::fromStdString(request->username()));
usersTab.userTable().append(user);
if (usersTab.nextUserAlreadyLoggedIn())
qtProxy_.sendDelayedEvent(newLoginAlreadyLoggedInEvent(userID));
qtProxy_.sendDelayedEvent(newLoginFinishedEvent(userID));
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::Login2FA(ServerContext *, LoginRequest const *request, Empty *)
{
app().log().debug(__FUNCTION__);
UsersTab &usersTab = app().mainWindow().usersTab();
if (usersTab.nextUserTFAError())
{
qtProxy_.sendDelayedEvent(newLoginError(LoginErrorType::TFA_ERROR, "2FA Error."));
return Status::OK;
}
if (usersTab.nextUserTFAAbort())
{
qtProxy_.sendDelayedEvent(newLoginError(LoginErrorType::TFA_ABORT, "2FA Abort."));
return Status::OK;
}
if (usersTab.nextUserTwoPasswordsRequired())
{
qtProxy_.sendDelayedEvent(newLoginTwoPasswordsRequestedEvent());
return Status::OK;
}
SPUser const user = randomUser();
QString const userID = user->id();
user->setUsername(QString::fromStdString(request->username()));
usersTab.userTable().append(user);
if (usersTab.nextUserAlreadyLoggedIn())
qtProxy_.sendDelayedEvent(newLoginAlreadyLoggedInEvent(userID));
qtProxy_.sendDelayedEvent(newLoginFinishedEvent(userID));
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::Login2Passwords(ServerContext *, LoginRequest const *request, Empty *)
{
app().log().debug(__FUNCTION__);
UsersTab &usersTab = app().mainWindow().usersTab();
if (usersTab.nextUserTwoPasswordsError())
{
qtProxy_.sendDelayedEvent(newLoginError(LoginErrorType::TWO_PASSWORDS_ERROR, "Two Passwords error."));
return Status::OK;
}
if (usersTab.nextUserTwoPasswordsAbort())
{
qtProxy_.sendDelayedEvent(newLoginError(LoginErrorType::TWO_PASSWORDS_ABORT, "Two Passwords abort."));
return Status::OK;
}
SPUser const user = randomUser();
QString const userID = user->id();
user->setUsername(QString::fromStdString(request->username()));
usersTab.userTable().append(user);
if (usersTab.nextUserAlreadyLoggedIn())
qtProxy_.sendDelayedEvent(newLoginAlreadyLoggedInEvent(userID));
qtProxy_.sendDelayedEvent(newLoginFinishedEvent(userID));
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::LoginAbort(ServerContext *, LoginAbortRequest const *request, Empty *)
{
app().log().debug(__FUNCTION__);
loginUsername_ = QString();
return Status::OK;
}
//****************************************************************************************************************************************************
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::CheckUpdate(ServerContext *, Empty const *, Empty *)
{
/// \todo simulate update availability.
app().log().debug(__FUNCTION__);
app().log().info("Check for updates");
return Status::OK;
}
//****************************************************************************************************************************************************
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::InstallUpdate(ServerContext *, Empty const *, Empty *)
{
/// Simulate update availability.
app().log().debug(__FUNCTION__);
app().log().info("Install update");
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::SetIsAutomaticUpdateOn(ServerContext *, BoolValue const *request, Empty *)
{
app().log().debug(__FUNCTION__);
qtProxy_.setIsAutomaticUpdateOn(request->value());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::IsAutomaticUpdateOn(ServerContext *, Empty const *, BoolValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().isAutomaticUpdateOn());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] response The response.
/// \return The status for the call
//****************************************************************************************************************************************************
Status GRPCService::IsCacheOnDiskEnabled(ServerContext *, Empty const *, BoolValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().isCacheOnDiskEnabled());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] response The response.
/// \return The status for the call
//****************************************************************************************************************************************************
Status GRPCService::DiskCachePath(ServerContext *, Empty const *, StringValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().diskCachePath().toStdString());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::ChangeLocalCache(ServerContext *, ChangeLocalCacheRequest const *request, Empty *)
{
app().log().debug(__FUNCTION__);
SettingsTab &tab = app().mainWindow().settingsTab();
QString const path = QString::fromStdString(request->diskcachepath());
// we mimic the behaviour of Bridge
if (!tab.nextCacheChangeWillSucceed())
qtProxy_.sendDelayedEvent(newCacheErrorEvent(grpc::CacheErrorType(tab.cacheError())));
else
qtProxy_.sendDelayedEvent(newCacheLocationChangeSuccessEvent());
qtProxy_.sendDelayedEvent(newDiskCachePathChanged(path));
qtProxy_.sendDelayedEvent(newIsCacheOnDiskEnabledChanged(request->enablediskcache()));
qtProxy_.sendDelayedEvent(newChangeLocalCacheFinishedEvent());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::SetIsDoHEnabled(ServerContext *, BoolValue const *request, Empty *)
{
app().log().debug(__FUNCTION__);
qtProxy_.setIsDoHEnabled(request->value());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::IsDoHEnabled(ServerContext *, Empty const *, BoolValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().isDoHEnabled());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::SetUseSslForSmtp(ServerContext *, BoolValue const *request, Empty *)
{
app().log().debug(__FUNCTION__);
qtProxy_.setUseSSLForSMTP(request->value());
qtProxy_.sendDelayedEvent(newUseSslForSmtpFinishedEvent());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::UseSslForSmtp(ServerContext *, Empty const *, BoolValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().useSSLForSMTP());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::Hostname(ServerContext *, Empty const *, StringValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().hostname().toStdString());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::ImapPort(ServerContext *, Empty const *, Int32Value *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().imapPort());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::SmtpPort(ServerContext *, Empty const *, Int32Value *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().smtpPort());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::ChangePorts(ServerContext *, ChangePortsRequest const *request, Empty *)
{
app().log().debug(__FUNCTION__);
qtProxy_.changePorts(request->imapport(), request->smtpport());
qtProxy_.sendDelayedEvent(newChangePortsFinishedEvent());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response the response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::IsPortFree(ServerContext *, Int32Value const *request, BoolValue *response)
{
app().log().debug(__FUNCTION__);
response->set_value(app().mainWindow().settingsTab().isPortFree());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call
//****************************************************************************************************************************************************
Status GRPCService::AvailableKeychains(ServerContext *, Empty const *, AvailableKeychainsResponse *response)
{
/// \todo Implement keychains configuration.
app().log().debug(__FUNCTION__);
response->clear_keychains();
response->add_keychains(defaultKeychain.toStdString());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call
//****************************************************************************************************************************************************
Status GRPCService::SetCurrentKeychain(ServerContext *, StringValue const *request, Empty *)
{
/// \todo Implement keychains configuration.
app().log().debug(__FUNCTION__);
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call
//****************************************************************************************************************************************************
Status GRPCService::CurrentKeychain(ServerContext *, Empty const *, StringValue *response)
{
/// \todo Implement keychains configuration.
app().log().debug(__FUNCTION__);
response->set_value(defaultKeychain.toStdString());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[out] response The response.
/// \return The status for the call
//****************************************************************************************************************************************************
Status GRPCService::GetUserList(ServerContext *, Empty const *, UserListResponse *response)
{
app().log().debug(__FUNCTION__);
response->clear_users();
QList<SPUser> userList = app().mainWindow().usersTab().userTable().users();
RepeatedPtrField<grpc::User> *users = response->mutable_users();
for (SPUser const &user: userList)
{
if (!user)
continue;
users->Add();
grpc::User &grpcUser = (*users)[users->size() - 1];
userToGRPC(*user, grpcUser);
}
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \param[out] response The response.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::GetUser(ServerContext *, StringValue const *request, grpc::User *response)
{
app().log().debug(__FUNCTION__);
QString userID = QString::fromStdString(request->value());
SPUser user = app().mainWindow().usersTab().userWithID(userID);
if (!user)
return Status(NOT_FOUND, QString("user not found %1").arg(userID).toStdString());
userToGRPC(*user, *response);
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::SetUserSplitMode(ServerContext *, UserSplitModeRequest const *request, Empty *)
{
app().log().debug(__FUNCTION__);
qtProxy_.setUserSplitMode(QString::fromStdString(request->userid()), request->active());
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::LogoutUser(ServerContext *, StringValue const *request, Empty *)
{
app().log().debug(__FUNCTION__);
qtProxy_.logoutUser(QString::fromStdString(request->value()));
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::RemoveUser(ServerContext *, StringValue const *request, Empty *)
{
app().log().debug(__FUNCTION__);
qtProxy_.removeUser(QString::fromStdString(request->value()));
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request.
//****************************************************************************************************************************************************
Status GRPCService::ConfigureUserAppleMail(ServerContext *, ConfigureAppleMailRequest const *request, Empty *)
{
app().log().debug(__FUNCTION__);
qtProxy_.configureUserAppleMail(QString::fromStdString(request->userid()), QString::fromStdString(request->address()));
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] request The request
/// \param[in] writer The writer
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::StartEventStream(ServerContext *, EventStreamRequest const *request, ServerWriter<StreamEvent> *writer)
{
app().log().debug(__FUNCTION__);
{
QMutexLocker locker(&eventStreamMutex_);
if (isStreaming_)
return { grpc::ALREADY_EXISTS, "the service is already streaming" };
isStreaming_ = true;
qtProxy_.setIsStreaming(true);
qtProxy_.setClientPlatform(QString::fromStdString(request->clientplatform()));
eventStreamShouldStop_ = false;
}
while (true)
{
QMutexLocker locker(&eventStreamMutex_);
if (eventStreamShouldStop_)
{
qtProxy_.setIsStreaming(false);
qtProxy_.setClientPlatform(QString());
isStreaming_ = false;
return Status::OK;
}
if (eventQueue_.isEmpty())
{
locker.unlock();
QThread::msleep(100);
continue;
}
SPStreamEvent const event = eventQueue_.front();
eventQueue_.pop_front();
locker.unlock();
if (writer->Write(*event))
app().log().debug(QString("event sent: %1").arg(QString::fromStdString(event->ShortDebugString())));
else
app().log().error(QString("Could not send event: %1").arg(QString::fromStdString(event->ShortDebugString())));
}
}
//****************************************************************************************************************************************************
/// \return The status for the call.
//****************************************************************************************************************************************************
Status GRPCService::StopEventStream(ServerContext *, Empty const *, Empty *)
{
app().log().debug(__FUNCTION__);
QMutexLocker mutex(&eventStreamMutex_);
if (!isStreaming_)
return Status(NOT_FOUND, "The service is not streaming");
eventStreamShouldStop_ = true;
return Status::OK;
}
//****************************************************************************************************************************************************
/// \param[in] event The event
/// \return true if the event was queued, and false if the server in not streaming.
//****************************************************************************************************************************************************
bool GRPCService::sendEvent(SPStreamEvent const &event)
{
QMutexLocker mutexLocker(&eventStreamMutex_);
if (isStreaming_)
eventQueue_.push_back(event);
return isStreaming_;
}

View File

@ -0,0 +1,112 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_GRPC_SERVER_H
#define BRIDGE_GUI_TESTER_GRPC_SERVER_H
#include "GRPCQtProxy.h"
#include <bridgepp/GRPC/bridge.grpc.pb.h>
#include <bridgepp/GRPC/GRPCUtils.h>
//**********************************************************************************************************************
/// \brief gRPC server implementation.
//**********************************************************************************************************************
class GRPCService : public grpc::Bridge::Service
{
public: // member functions.
GRPCService() = default; ///< Default constructor.
GRPCService(GRPCService const &) = delete; ///< Disabled copy-constructor.
GRPCService(GRPCService &&) = delete; ///< Disabled assignment copy-constructor.
~GRPCService() override = default; ///< Destructor.
GRPCService &operator=(GRPCService const &) = delete; ///< Disabled assignment operator.
GRPCService &operator=(GRPCService &&) = delete; ///< Disabled move assignment operator.
void connectProxySignals(); ///< Connect the signals of the Qt Proxy to the GUI components
bool isStreaming() const; ///< Check if the service is currently streaming events.
grpc::Status AddLogEntry(::grpc::ServerContext *context, ::grpc::AddLogEntryRequest const *request, ::google::protobuf::Empty *response) override;
grpc::Status GuiReady(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::Empty *response) override;
grpc::Status Quit(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::Empty *response) override;
grpc::Status Restart(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::Empty *response) override;
grpc::Status ShowOnStartup(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
grpc::Status ShowSplashScreen(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
grpc::Status IsFirstGuiStart(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
grpc::Status SetIsAutostartOn(::grpc::ServerContext *context, ::google::protobuf::BoolValue const *request, ::google::protobuf::Empty *response) override;
grpc::Status IsAutostartOn(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
grpc::Status SetIsBetaEnabled(::grpc::ServerContext *context, ::google::protobuf::BoolValue const *request, ::google::protobuf::Empty *response) override;
grpc::Status IsBetaEnabled(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
grpc::Status GoOs(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::StringValue *response) override;
grpc::Status TriggerReset(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::Empty *response) override;
grpc::Status Version(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::StringValue *response) override;
grpc::Status LogsPath(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::StringValue *response) override;
grpc::Status LicensePath(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::StringValue *response) override;
grpc::Status ReleaseNotesPageLink(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::StringValue *response) override;
grpc::Status DependencyLicensesLink(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::StringValue *response) override;
grpc::Status LandingPageLink(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::StringValue *response) override;
grpc::Status SetColorSchemeName(::grpc::ServerContext *context, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *response) override;
grpc::Status ColorSchemeName(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::StringValue *response) override;
grpc::Status CurrentEmailClient(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::StringValue *response) override;
grpc::Status ReportBug(::grpc::ServerContext *context, ::grpc::ReportBugRequest const *request, ::google::protobuf::Empty *response) override;
grpc::Status ForceLauncher(::grpc::ServerContext *context, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *response) override;
grpc::Status Login(::grpc::ServerContext *context, ::grpc::LoginRequest const *request, ::google::protobuf::Empty *response) override;
grpc::Status Login2FA(::grpc::ServerContext *context, ::grpc::LoginRequest const *request, ::google::protobuf::Empty *response) override;
grpc::Status Login2Passwords(::grpc::ServerContext *context, ::grpc::LoginRequest const *request, ::google::protobuf::Empty *response) override;
grpc::Status LoginAbort(::grpc::ServerContext *context, ::grpc::LoginAbortRequest const *request, ::google::protobuf::Empty *response) override;
grpc::Status CheckUpdate(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::Empty *response) override;
grpc::Status InstallUpdate(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::Empty *response) override;
grpc::Status SetIsAutomaticUpdateOn(::grpc::ServerContext *context, ::google::protobuf::BoolValue const *request, ::google::protobuf::Empty *response) override;
grpc::Status IsAutomaticUpdateOn(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
grpc::Status IsCacheOnDiskEnabled(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
grpc::Status DiskCachePath(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::StringValue *response) override;
grpc::Status ChangeLocalCache(::grpc::ServerContext *context, ::grpc::ChangeLocalCacheRequest const *request, ::google::protobuf::Empty *response) override;
grpc::Status SetIsDoHEnabled(::grpc::ServerContext *context, ::google::protobuf::BoolValue const *request, ::google::protobuf::Empty *response) override;
grpc::Status IsDoHEnabled(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
grpc::Status SetUseSslForSmtp(::grpc::ServerContext *context, ::google::protobuf::BoolValue const *request, ::google::protobuf::Empty *response) override;
grpc::Status UseSslForSmtp(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
grpc::Status Hostname(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::StringValue *response) override;
grpc::Status ImapPort(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::Int32Value *response) override;
grpc::Status SmtpPort(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::Int32Value *response) override;
grpc::Status ChangePorts(::grpc::ServerContext *context, ::grpc::ChangePortsRequest const *request, ::google::protobuf::Empty *response) override;
grpc::Status IsPortFree(::grpc::ServerContext *context, ::google::protobuf::Int32Value const *request, ::google::protobuf::BoolValue *response) override;
grpc::Status AvailableKeychains(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::grpc::AvailableKeychainsResponse *response) override;
grpc::Status SetCurrentKeychain(::grpc::ServerContext *context, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *response) override;
grpc::Status CurrentKeychain(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::StringValue *response) override;
grpc::Status GetUserList(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::grpc::UserListResponse *response) override;
grpc::Status GetUser(::grpc::ServerContext *context, ::google::protobuf::StringValue const *request, ::grpc::User *response) override;
grpc::Status SetUserSplitMode(::grpc::ServerContext *context, ::grpc::UserSplitModeRequest const *request, ::google::protobuf::Empty *response) override;
grpc::Status LogoutUser(::grpc::ServerContext *context, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *response) override;
grpc::Status RemoveUser(::grpc::ServerContext *context, ::google::protobuf::StringValue const *request, ::google::protobuf::Empty *response) override;
grpc::Status ConfigureUserAppleMail(::grpc::ServerContext *context, ::grpc::ConfigureAppleMailRequest const *request, ::google::protobuf::Empty *response) override;
grpc::Status StartEventStream(::grpc::ServerContext *context, ::grpc::EventStreamRequest const *request, ::grpc::ServerWriter<::grpc::StreamEvent> *writer) override;
grpc::Status StopEventStream(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::Empty *response) override;
bool sendEvent(bridgepp::SPStreamEvent const &event); ///< Queue an event for sending through the event stream.
private: // data member
mutable QMutex eventStreamMutex_; ///< Mutex used to access eventQueue_, isStreaming_ and shouldStopStreaming_;
QList<bridgepp::SPStreamEvent> eventQueue_; ///< The event queue. Acces protected by eventStreamMutex_;
bool isStreaming_; ///< Is the gRPC stream running. Access protected by eventStreamMutex_;
bool eventStreamShouldStop_; ///< Should the stream be stopped? Access protected by eventStreamMutex
QString loginUsername_; ///< The username used for the current login procedure.
GRPCQtProxy qtProxy_; ///< Qt Proxy used to send signals, as this class is not a QObject.
};
#endif // BRIDGE_GUI_TESTER_GRPC_SERVER_H

View File

@ -0,0 +1,111 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "MainWindow.h"
#include <bridgepp/Log/Log.h>
using namespace bridgepp;
namespace
{
//****************************************************************************************************************************************************
/// \param[in] level The log level.
/// \param[in] message The log message.
/// \param[in] logEdit The plain text edit widget that displays the log.
//****************************************************************************************************************************************************
void addEntryToLogEdit(bridgepp::Log::Level level, const QString &message, QPlainTextEdit &logEdit)
{
/// \todo This may cause performance issue when log grows big. A better alternative should be implemented.
QString log = logEdit.toPlainText().trimmed();
if (!log.isEmpty())
log += "\n";
logEdit.setPlainText(log + Log::logEntryToString(level, message));
}
} // Anonymous namespace
//****************************************************************************************************************************************************
/// \param[in] parent The parent widget of the window.
//****************************************************************************************************************************************************
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
ui_.setupUi(this);
ui_.tabTop->setCurrentIndex(0);
ui_.tabBottom->setCurrentIndex(0);
ui_.splitter->setStretchFactor(0, 0);
ui_.splitter->setStretchFactor(1, 1);
ui_.splitter->setSizes({100, 10000});
connect(&app().log(), &Log::entryAdded, this, &MainWindow::addLogEntry);
connect(&app().bridgeGUILog(), &Log::entryAdded, this, &MainWindow::addBridgeGUILogEntry);
}
//****************************************************************************************************************************************************
/// \return A reference to the 'General' tab.
//****************************************************************************************************************************************************
SettingsTab &MainWindow::settingsTab()
{
return *ui_.settingsTab;
}
//****************************************************************************************************************************************************
/// \return A reference to the users tab.
//****************************************************************************************************************************************************
UsersTab &MainWindow::usersTab()
{
return *ui_.usersTab;
}
//****************************************************************************************************************************************************
/// \param[in] level The log level.
/// \param[in] message The log message
//****************************************************************************************************************************************************
void MainWindow::addLogEntry(bridgepp::Log::Level level, const QString &message)
{
addEntryToLogEdit(level, message, *ui_.editLog);
}
//****************************************************************************************************************************************************
/// \param[in] level The log level.
/// \param[in] message The log message
//****************************************************************************************************************************************************
void MainWindow::addBridgeGUILogEntry(bridgepp::Log::Level level, const QString &message)
{
addEntryToLogEdit(level, message, *ui_.editBridgeGUILog);
}
//****************************************************************************************************************************************************
/// \param[in] event The event.
//****************************************************************************************************************************************************
void MainWindow::sendDelayedEvent(SPStreamEvent const &event)
{
QTimer::singleShot(this->settingsTab().eventDelayMs(), [event] { app().grpc().sendEvent(event); });
}

View File

@ -0,0 +1,57 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_MAIN_WINDOW_H
#define BRIDGE_GUI_TESTER_MAIN_WINDOW_H
#include "ui_MainWindow.h"
#include "GRPCService.h"
#include <bridgepp/Log/Log.h>
//**********************************************************************************************************************
/// \brief Main window class
//**********************************************************************************************************************
class MainWindow : public QMainWindow
{
Q_OBJECT
public: // member functions.
explicit MainWindow(QWidget *parent); ///< Default constructor.
MainWindow(MainWindow const &) = delete; ///< Disabled copy-constructor.
MainWindow(MainWindow &&) = delete; ///< Disabled assignment copy-constructor.
~MainWindow() override = default; ///< Destructor.
MainWindow &operator=(MainWindow const &) = delete; ///< Disabled assignment operator.
MainWindow &operator=(MainWindow &&) = delete; ///< Disabled move assignment operator.
SettingsTab &settingsTab(); ///< Returns a reference the 'Settings' tab.
UsersTab &usersTab(); ///< Returns a reference to the 'Users' tab.
public slots:
void sendDelayedEvent(bridgepp::SPStreamEvent const& event); ///< Sends a gRPC event after the delay specified in the UI. The call is non blocking.
private slots:
void addLogEntry(bridgepp::Log::Level level, QString const &message); ///< Add an entry to the log.
void addBridgeGUILogEntry(bridgepp::Log::Level level, const QString &message); ///< Add an entry to the log.
private:
Ui::MainWindow ui_ {}; ///< The GUI for the window.
};
#endif // BRIDGE_GUI_TESTER_MAIN_WINDOW_H

View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1226</width>
<height>1086</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QTabWidget" name="tabTop">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="SettingsTab" name="settingsTab">
<attribute name="title">
<string>Settings</string>
</attribute>
</widget>
<widget class="UsersTab" name="usersTab">
<attribute name="title">
<string>Users</string>
</attribute>
</widget>
</widget>
<widget class="QTabWidget" name="tabBottom">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tabLog">
<attribute name="title">
<string>Log</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPlainTextEdit" name="editLog">
<property name="font">
<font>
<family>Monaco</family>
</font>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabBridgeGUILog">
<attribute name="title">
<string>Bridge-GUI Log</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QPlainTextEdit" name="editBridgeGUILog">
<property name="font">
<font>
<family>Monaco</family>
</font>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>SettingsTab</class>
<extends>QWidget</extends>
<header>Tabs/SettingsTab.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>UsersTab</class>
<extends>QWidget</extends>
<header>Tabs/UsersTab.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,29 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_PCH_H
#define BRIDGE_GUI_PCH_H
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
#include "AppController.h"
#endif // BRIDGE_GUI_PCH_H

View File

@ -0,0 +1,496 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "SettingsTab.h"
#include "GRPCService.h"
#include <bridgepp/GRPC/EventFactory.h>
#include <bridgepp/BridgeUtils.h>
using namespace bridgepp;
namespace
{
QString const colorSchemeDark = "dark"; ///< The dark color scheme name.
QString const colorSchemeLight = "light"; ///< THe light color scheme name.
}
//****************************************************************************************************************************************************
/// \param[in] parent The parent widget of the tab.
//****************************************************************************************************************************************************
SettingsTab::SettingsTab(QWidget *parent)
: QWidget(parent)
{
ui_.setupUi(this);
connect(ui_.buttonInternetOn, &QPushButton::clicked, []() { app().grpc().sendEvent(newInternetStatusEvent(true)); });
connect(ui_.buttonInternetOff, &QPushButton::clicked, []() { app().grpc().sendEvent(newInternetStatusEvent(false)); });
connect(ui_.buttonShowMainWindow, &QPushButton::clicked, []() { app().grpc().sendEvent(newShowMainWindowEvent()); });
connect(ui_.checkNextCacheChangeWillSucceed, &QCheckBox::toggled, this, &SettingsTab::updateGUIState);
this->resetUI();
this->updateGUIState();
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void SettingsTab::updateGUIState()
{
bool connected = app().grpc().isStreaming();
for (QWidget *widget: { ui_.groupVersion, ui_.groupGeneral, ui_.groupMail, ui_.groupPaths, ui_.groupCache })
widget->setEnabled(!connected);
ui_.comboCacheError -> setEnabled(!ui_.checkNextCacheChangeWillSucceed->isChecked());
}
//****************************************************************************************************************************************************
/// \param[in] isStreaming Is the event stream on?
//****************************************************************************************************************************************************
void SettingsTab::setIsStreaming(bool isStreaming)
{
ui_.labelStreamingValue->setText(isStreaming ? "Yes" : "No");
this->updateGUIState();
}
//****************************************************************************************************************************************************
/// \param[in] clientPlatform The client platform.
//****************************************************************************************************************************************************
void SettingsTab::setClientPlatform(QString const &clientPlatform)
{
ui_.labelClientPlatformValue->setText(clientPlatform);
}
//****************************************************************************************************************************************************
/// \return The version of Bridge
//****************************************************************************************************************************************************
QString SettingsTab::bridgeVersion() const
{
return ui_.editVersion->text();
}
//****************************************************************************************************************************************************
/// \return The OS as a Go GOOS compatible value ("darwin", "linux" or "windows").
//****************************************************************************************************************************************************
QString SettingsTab::os() const
{
return ui_.comboOS->currentText();
}
//****************************************************************************************************************************************************
/// \return The value for the 'Current Email Client' edit.
//****************************************************************************************************************************************************
QString SettingsTab::currentEmailClient() const
{
return ui_.editCurrentEmailClient->text();
}
//****************************************************************************************************************************************************
/// \param[in] ready Is the GUI ready?
//****************************************************************************************************************************************************
void SettingsTab::setGUIReady(bool ready)
{
this->updateGUIState();
ui_.labelGUIReadyValue->setText(ready ? "Yes" : "No");
}
//****************************************************************************************************************************************************
/// \return true iff the 'Show On Startup' check box is checked.
//****************************************************************************************************************************************************
bool SettingsTab::showOnStartup() const
{
return ui_.checkShowOnStartup->isChecked();
}
//****************************************************************************************************************************************************
/// \return true iff the 'Show Splash Screen' check box is checked.
//****************************************************************************************************************************************************
bool SettingsTab::showSplashScreen() const
{
return ui_.checkShowSplashScreen->isChecked();
}
//****************************************************************************************************************************************************
/// \return true iff the 'Show Splash Screen' check box is checked.
//****************************************************************************************************************************************************
bool SettingsTab::isFirstGUIStart() const
{
return ui_.checkIsFirstGUIStart->isChecked();
}
//****************************************************************************************************************************************************
/// \return true iff the 'Show Splash Screen' check box is checked.
//****************************************************************************************************************************************************
bool SettingsTab::isAutostartOn() const
{
return ui_.checkAutostart->isChecked();
}
//****************************************************************************************************************************************************
/// \return true iff the 'Show Splash Screen' check box is checked.
//****************************************************************************************************************************************************
void SettingsTab::setIsAutostartOn(bool on)
{
ui_.checkAutostart->setChecked(on);
}
//****************************************************************************************************************************************************
/// \return true if the 'Use Dark Theme' check box is checked.
//****************************************************************************************************************************************************
QString SettingsTab::colorSchemeName() const
{
return ui_.checkDarkTheme->isChecked() ? colorSchemeDark : colorSchemeLight;
}
//****************************************************************************************************************************************************
/// \param[in] name True if the 'Use Dark Theme' check box should be checked.
//****************************************************************************************************************************************************
void SettingsTab::setColorSchemeName(QString const &name)
{
ui_.checkDarkTheme->setChecked(name == colorSchemeDark);
}
//****************************************************************************************************************************************************
/// \return true if the 'Beta Enabled' check box is checked.
//****************************************************************************************************************************************************
bool SettingsTab::isBetaEnabled() const
{
return ui_.checkBetaEnabled->isChecked();
}
//****************************************************************************************************************************************************
/// \param[in] enabled The new state for the 'Beta Enabled' check box.
//****************************************************************************************************************************************************
void SettingsTab::setIsBetaEnabled(bool enabled)
{
ui_.checkBetaEnabled->setChecked(enabled);
}
//****************************************************************************************************************************************************
/// \return The delay to apply before sending automatically generated events.
//****************************************************************************************************************************************************
qint32 SettingsTab::eventDelayMs() const
{
return ui_.spinEventDelay->value();
}
//****************************************************************************************************************************************************
/// \return The path
//****************************************************************************************************************************************************
QString SettingsTab::logsPath() const
{
return ui_.editLogsPath->text();
}
//****************************************************************************************************************************************************
/// \return The path
//****************************************************************************************************************************************************
QString SettingsTab::licensePath() const
{
return ui_.editLicensePath->text();
}
//****************************************************************************************************************************************************
/// \return The link.
//****************************************************************************************************************************************************
QString SettingsTab::releaseNotesPageLink() const
{
return ui_.editReleaseNotesLink->text();
}
//****************************************************************************************************************************************************
/// \return The link.
//****************************************************************************************************************************************************
QString SettingsTab::dependencyLicenseLink() const
{
return ui_.editDependencyLicenseLink->text();
}
//****************************************************************************************************************************************************
/// \return The link.
//****************************************************************************************************************************************************
QString SettingsTab::landingPageLink() const
{
return ui_.editLandingPageLink->text();
}
//****************************************************************************************************************************************************
/// \param[in] osType The OS type.
/// \param[in] osVersion The OS version.
/// \param[in] emailClient The email client.
/// \param[in] address The email address.
/// \param[in] description The description.
/// \param[in] includeLogs Are the log included.
//****************************************************************************************************************************************************
void SettingsTab::setBugReport(QString const &osType, QString const &osVersion, QString const &emailClient, QString const &address,
QString const &description, bool includeLogs)
{
ui_.editOSType->setText(osType);
ui_.editOSVersion->setText(osVersion);
ui_.editEmailClient->setText(emailClient);
ui_.editAddress->setText(address);
ui_.editDescription->setPlainText(description);
ui_.labelIncludeLogsValue->setText(includeLogs ? "Yes" : "No");
}
//****************************************************************************************************************************************************
/// \return The state of the check box.
//****************************************************************************************************************************************************
bool SettingsTab::nextBugReportWillSucceed() const
{
return ui_.checkNextBugReportWillSucceed->isChecked();
}
//****************************************************************************************************************************************************
/// \return The value of the 'Hostname' edit.
//****************************************************************************************************************************************************
QString SettingsTab::hostname() const
{
return ui_.editHostname->text();
}
//****************************************************************************************************************************************************
/// \return The value of the IMAP port spin box.
//****************************************************************************************************************************************************
qint32 SettingsTab::imapPort()
{
return ui_.spinPortIMAP->value();
}
//****************************************************************************************************************************************************
/// \return The value of the SMTP port spin box.
//****************************************************************************************************************************************************
qint32 SettingsTab::smtpPort()
{
return ui_.spinPortSMTP->value();
}
//****************************************************************************************************************************************************
/// \param[in] imapPort The IMAP port.
/// \param[in] smtpPort The SMTP port.
//****************************************************************************************************************************************************
void SettingsTab::changePorts(qint32 imapPort, qint32 smtpPort)
{
ui_.spinPortIMAP->setValue(imapPort);
ui_.spinPortSMTP->setValue(smtpPort);
}
//****************************************************************************************************************************************************
/// \return The state of the 'Use SSL for SMTP' check box.
//****************************************************************************************************************************************************
bool SettingsTab::useSSLForSMTP() const
{
return ui_.checkUseSSLForSMTP->isChecked();
}
//****************************************************************************************************************************************************
/// \param[in] use The state of the 'Use SSL for SMTP' check box.
//****************************************************************************************************************************************************
void SettingsTab::setUseSSLForSMTP(bool use)
{
ui_.checkUseSSLForSMTP->setChecked(use);
}
//****************************************************************************************************************************************************
/// \return The state of the the 'DoH enabled' check box.
//****************************************************************************************************************************************************
bool SettingsTab::isDoHEnabled() const
{
return ui_.checkDoHEnabled->isChecked();
}
//****************************************************************************************************************************************************
/// \param[in] enabled The state of the 'DoH enabled' check box.
//****************************************************************************************************************************************************
void SettingsTab::setIsDoHEnabled(bool enabled)
{
ui_.checkDoHEnabled->setChecked(enabled);
}
//****************************************************************************************************************************************************
/// \return The reply for the next IsPortFree gRPC call.
//****************************************************************************************************************************************************
bool SettingsTab::isPortFree() const
{
return ui_.checkIsPortFree->isChecked();
}
//****************************************************************************************************************************************************
/// \return true iff cache on disk is enabled.
//****************************************************************************************************************************************************
bool SettingsTab::isCacheOnDiskEnabled() const
{
return ui_.checkCacheOnDiskEnabled->isChecked();
}
//****************************************************************************************************************************************************
/// \param[in] enabled Is the cache on disk enabled?
//****************************************************************************************************************************************************
void SettingsTab::changeLocalCache(bool enabled, QString const &path)
{
ui_.checkCacheOnDiskEnabled->setChecked(enabled);
ui_.editDiskCachePath->setText(path);
}
//****************************************************************************************************************************************************
/// \return The disk cache path.
//****************************************************************************************************************************************************
QString SettingsTab::diskCachePath() const
{
return ui_.editDiskCachePath->text();
}
//****************************************************************************************************************************************************
/// \return The value for the 'Next Cache Change Will Succeed' check box.
//****************************************************************************************************************************************************
bool SettingsTab::nextCacheChangeWillSucceed() const
{
return ui_.checkNextCacheChangeWillSucceed->isChecked();
}
//****************************************************************************************************************************************************
/// \return The index of the selected cache error.
//****************************************************************************************************************************************************
qint32 SettingsTab::cacheError() const
{
return ui_.comboCacheError->currentIndex();
}
//****************************************************************************************************************************************************
/// \return the value for the 'Automatic Update' check.
//****************************************************************************************************************************************************
bool SettingsTab::isAutomaticUpdateOn() const
{
return ui_.checkAutomaticUpdate->isChecked();
}
//****************************************************************************************************************************************************
/// \param[in] on The value for the 'Automatic Update' check.
//****************************************************************************************************************************************************
void SettingsTab::setIsAutomaticUpdateOn(bool on)
{
ui_.checkAutomaticUpdate->setChecked(on);
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void SettingsTab::resetUI()
{
this->setGUIReady(false);
this->setIsStreaming(false);
this->setClientPlatform("Unknown");
ui_.editVersion->setText(BRIDGE_APP_VERSION);
ui_.comboOS->setCurrentText(bridgepp::goos());
ui_.editCurrentEmailClient->setText("Thunderbird/102.0.3");
ui_.checkShowOnStartup->setChecked(true);
ui_.checkShowSplashScreen->setChecked(false);
ui_.checkIsFirstGUIStart->setChecked(false);
ui_.checkAutostart->setChecked(true);
ui_.checkBetaEnabled->setChecked(true);
ui_.checkDarkTheme->setChecked(false);
QString const tmpDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
QString const logsDir = QDir(tmpDir).absoluteFilePath("logs");
QDir().mkpath(logsDir);
ui_.editLogsPath->setText(QDir::toNativeSeparators(logsDir));
QString const filePath = QDir(tmpDir).absoluteFilePath("LICENSE.txt");
QFile file(filePath);
if (!file.exists())
{
// we don't really care if it fails.
file.open(QIODevice::WriteOnly | QIODevice::Text);
file.write(QString("This is were the license should be.").toLocal8Bit());
file.close();
}
ui_.editLicensePath->setText(filePath);
ui_.editReleaseNotesLink->setText("https://en.wikipedia.org/wiki/Release_notes");
ui_.editDependencyLicenseLink->setText("https://en.wikipedia.org/wiki/Dependency_relation");
ui_.editLandingPageLink->setText("https://proton.me");
ui_.editOSType->setText(QString());
ui_.editOSVersion->setText(QString());
ui_.editEmailClient->setText(QString());
ui_.editAddress->setText(QString());
ui_.editDescription->setPlainText(QString());
ui_.labelIncludeLogsValue->setText(QString());
ui_.checkNextBugReportWillSucceed->setChecked(true);
ui_.editHostname->setText("localhost");
ui_.spinPortIMAP->setValue(1143);
ui_.spinPortSMTP->setValue(1025);
ui_.checkUseSSLForSMTP->setChecked(false);
ui_.checkDoHEnabled->setChecked(true);
ui_.checkIsPortFree->setChecked(true);
ui_.checkCacheOnDiskEnabled->setChecked(true);
QString const cacheDir = QDir(tmpDir).absoluteFilePath("cache");
QDir().mkpath(cacheDir);
ui_.editDiskCachePath->setText(QDir::toNativeSeparators(cacheDir));
ui_.checkNextCacheChangeWillSucceed->setChecked(true);
ui_.comboCacheError->setCurrentIndex(0);
ui_.checkAutomaticUpdate->setChecked(true);
}

View File

@ -0,0 +1,92 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_GENERAL_TAB_H
#define BRIDGE_GUI_TESTER_GENERAL_TAB_H
#include "Tab/ui_SettingsTab.h"
//****************************************************************************************************************************************************
/// \brief The 'General' tab of the main window.
//****************************************************************************************************************************************************
class SettingsTab : public QWidget
{
Q_OBJECT
public: // member functions.
explicit SettingsTab(QWidget *parent = nullptr); ///< Default constructor.
SettingsTab(SettingsTab const &) = delete; ///< Disabled copy-constructor.
SettingsTab(SettingsTab &&) = delete; ///< Disabled assignment copy-constructor.
~SettingsTab() = default; ///< Destructor.
SettingsTab &operator=(SettingsTab const &) = delete; ///< Disabled assignment operator.
SettingsTab &operator=(SettingsTab &&) = delete; ///< Disabled move assignment operator.
QString bridgeVersion() const; ///< Get the Bridge version.
QString os() const; ///< Return the OS string.
QString currentEmailClient() const; ///< Return the content of the current email client
void setGUIReady(bool ready); ///< Set the GUI as ready.
bool showOnStartup() const; ///< Get the value for the 'Show On Startup' check.
bool showSplashScreen() const; ///< Get the value for the 'Show Splash Screen' check.
bool isFirstGUIStart() const; ///< Get the value for the 'Is First GUI Start' check.
bool isAutostartOn() const; ///< Get the value for the 'Autostart' check.
bool isBetaEnabled() const; ///< Get the value for the 'Beta Enabled' check.
QString colorSchemeName() const; ///< Get the value of the 'Use Dark Theme' checkbox.
qint32 eventDelayMs() const; ///< Get the delay for sending automatically generated events.
QString logsPath() const; ///< Get the content of the 'Logs Path' edit.
QString licensePath() const; ///< Get the content of the 'License Path' edit.
QString releaseNotesPageLink() const; ///< Get the content of the 'Release Notes Page Link' edit.
QString dependencyLicenseLink() const; ///< Get the content of the 'Dependency License Link' edit.
QString landingPageLink() const; ///< Get the content of the 'Landing Page Link' edit.
bool nextBugReportWillSucceed() const; ///< Get the status of the 'Next Bug Report Will Fail' check box.
QString hostname() const; ///< Get the value of the 'Hostname' edit.
qint32 imapPort(); ///< Get the value of the IMAP port spin.
qint32 smtpPort(); ///< Get the value of the SMTP port spin.
bool useSSLForSMTP() const; ///< Get the value for the 'Use SSL for SMTP' check box.
bool isDoHEnabled() const; ///< Get the value for the 'DoH Enabled' check box.
bool isPortFree() const; ///< Get the value for the "Is Port Free" check box.
bool isCacheOnDiskEnabled() const; ///< get the value for the 'Cache On Disk Enabled' check box.
QString diskCachePath() const; ///< Get the value for the 'Disk Cache Path' edit.
bool nextCacheChangeWillSucceed() const; ///< Get the value for the 'Next Cache Change will succeed' edit.
qint32 cacheError() const; ///< Return the index of the selected cache error.
bool isAutomaticUpdateOn() const; ///<Get the value for the 'Automatic Update' check box.
public: // slots
void updateGUIState(); ///< Update the GUI state.
void setIsStreaming(bool isStreaming); ///< Set the isStreamingEvents value.
void setClientPlatform(QString const &clientPlatform); ///< Set the client platform.
void setIsAutostartOn(bool on); ///< Set the value for the 'Autostart' check.
void setIsBetaEnabled(bool enabled); ///< Get the value for the 'Beta Enabled' check.
void setColorSchemeName(QString const &name); ///< Set the value for the 'Use Dark Theme' checkbox.
void setBugReport(QString const &osType, QString const &osVersion, QString const &emailClient, QString const &address, QString const &description,
bool includeLogs); ///< Set the content of the bug report box.
void changePorts(qint32 imapPort, qint32 smtpPort); ///< Change the IMAP and SMTP ports.
void setUseSSLForSMTP(bool use); ///< Set the value for the 'Use SSL for SMTP' check box.
void setIsDoHEnabled(bool enabled); ///< Set the value for the 'DoH Enabled' check box.
void changeLocalCache(bool enabled, QString const &path); ///< Set the value for the 'Cache On Disk Enabled' check box.
void setIsAutomaticUpdateOn(bool on); ///< Set the value for the 'Automatic Update' check box.
private: // member functions.
void resetUI(); ///< Reset the widget.
private: // data members.
Ui::SettingsTab ui_; ///< The GUI for the tab
};
#endif //BRIDGE_GUI_TESTER_GENERAL_TAB_H

View File

@ -0,0 +1,839 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsTab</class>
<widget class="QWidget" name="SettingsTab">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1066</width>
<height>808</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupVersion">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Version Info</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="labelVersion">
<property name="text">
<string>Bridge Version</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="editVersion"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="labelOS">
<property name="text">
<string>OS</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboOS">
<item>
<property name="text">
<string notr="true">darwin</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">linux</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">windows</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="labelCurrentEmailClient">
<property name="text">
<string>Current Email Client</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="editCurrentEmailClient">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupGeneral">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>General Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QCheckBox" name="checkShowOnStartup">
<property name="text">
<string>Show On Startup</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkShowSplashScreen">
<property name="text">
<string>Show Splash Screen</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QCheckBox" name="checkIsFirstGUIStart">
<property name="text">
<string>Is FIrst GUI Start</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkAutostart">
<property name="text">
<string>Autostart</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBetaEnabled">
<property name="text">
<string>Beta Enabled</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="checkDarkTheme">
<property name="text">
<string>Dark Theme</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkAutomaticUpdate">
<property name="text">
<string>Automatic Update</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupMail">
<property name="title">
<string>Mail</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6" stretch="0,0">
<item>
<widget class="QLabel" name="labelHostname">
<property name="text">
<string>Hostname</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="editHostname">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7" stretch="0,0,0,0,1">
<item>
<widget class="QLabel" name="labelMAP">
<property name="text">
<string>IMAP Port</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinPortIMAP">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelSMTP">
<property name="text">
<string>SMTP Port</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinPortSMTP">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkUseSSLForSMTP">
<property name="text">
<string>Use SSL For SMTP</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkDoHEnabled">
<property name="text">
<string>DoH Enabled</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupPaths">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Paths &amp;&amp; Links</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="labelLogsPath">
<property name="text">
<string>Logs Path</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="editLogsPath"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelLicensePath">
<property name="text">
<string>License Path</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="editLicensePath"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelReleaseNotesLink">
<property name="text">
<string>Release Notes Page Link</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="editReleaseNotesLink"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelDependencyLicenseLink">
<property name="text">
<string>Dependency License Link</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="editDependencyLicenseLink"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="labelLandingPageLink">
<property name="text">
<string>Landing Page Link</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="editLandingPageLink"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupCache">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Cache</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QCheckBox" name="checkCacheOnDiskEnabled">
<property name="text">
<string>Cache On Disk Enabled</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLabel" name="labelDiskCachePath">
<property name="text">
<string>Disk Cache Path</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="editDiskCachePath"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QGroupBox" name="groupStatus">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Status</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="labelGUIReadyTitle">
<property name="text">
<string>GUI Ready:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelGUIReadyValue">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="labelStreamingTitle">
<property name="text">
<string>Streaming: </string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelStreamingValue">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="labelClientPlatformTitle">
<property name="text">
<string>Client Platform: </string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelClientPlatformValue">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBugReport">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Bug Report</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="3">
<widget class="QLineEdit" name="editOSVersion">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLineEdit" name="editAddress">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelOSType">
<property name="text">
<string>OS Type</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelIncludeLogs">
<property name="text">
<string>Include Logs</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="labelAddress">
<property name="text">
<string>Address</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="labelOSVersion">
<property name="text">
<string>OS Version</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="editOSType">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1" colspan="3">
<widget class="QLabel" name="labelIncludeLogsValue">
<property name="text">
<string>?</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelDescription">
<property name="text">
<string>Description</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="editEmailClient">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelEmailClient">
<property name="text">
<string>Email Cient</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="4">
<widget class="QPlainTextEdit" name="editDescription">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupApp">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Events &amp;&amp; Errors</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
<widget class="QLabel" name="labelEventDelay">
<property name="toolTip">
<string>Delay applied before sending automatically generated events</string>
</property>
<property name="text">
<string>Delay for asynchronous events</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinEventDelay">
<property name="suffix">
<string> ms</string>
</property>
<property name="maximum">
<number>3600000</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
<property name="value">
<number>1000</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QPushButton" name="buttonInternetOff">
<property name="text">
<string>Internet Off</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonInternetOn">
<property name="text">
<string>Internet On</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonShowMainWindow">
<property name="text">
<string>Show Main Window</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkIsPortFree">
<property name="text">
<string>Reply true to the next 'Is Port Free' request.</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="checkNextCacheChangeWillSucceed">
<property name="text">
<string>Next Cache Change will succeed</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QComboBox" name="comboCacheError">
<item>
<property name="text">
<string>Cache Unavailable</string>
</property>
</item>
<item>
<property name="text">
<string>Can't Move Cache</string>
</property>
</item>
<item>
<property name="text">
<string>Disk Full</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkNextBugReportWillSucceed">
<property name="text">
<string>Next Bug Report Will Succeed</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>checkShowOnStartup</tabstop>
<tabstop>checkShowSplashScreen</tabstop>
<tabstop>checkIsFirstGUIStart</tabstop>
<tabstop>checkAutostart</tabstop>
<tabstop>checkBetaEnabled</tabstop>
<tabstop>checkDarkTheme</tabstop>
<tabstop>editVersion</tabstop>
<tabstop>comboOS</tabstop>
<tabstop>editCurrentEmailClient</tabstop>
<tabstop>editOSType</tabstop>
<tabstop>editOSVersion</tabstop>
<tabstop>editEmailClient</tabstop>
<tabstop>editAddress</tabstop>
<tabstop>editDescription</tabstop>
<tabstop>editLogsPath</tabstop>
<tabstop>editLicensePath</tabstop>
<tabstop>editReleaseNotesLink</tabstop>
<tabstop>editDependencyLicenseLink</tabstop>
<tabstop>editLandingPageLink</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,318 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "UsersTab.h"
#include "MainWindow.h"
#include "UserDialog.h"
#include <bridgepp/BridgeUtils.h>
#include <bridgepp/Exception/Exception.h>
#include <bridgepp/GRPC/EventFactory.h>
using namespace bridgepp;
//****************************************************************************************************************************************************
/// \param[in] parent The parent widget of the tab.
//****************************************************************************************************************************************************
UsersTab::UsersTab(QWidget *parent)
: QWidget(parent)
, users_(nullptr)
{
ui_.setupUi(this);
ui_.tableUserList->setModel(&users_);
QItemSelectionModel *model = ui_.tableUserList->selectionModel();
if (!model)
throw Exception("Could not get user table selection model.");
connect(model, &QItemSelectionModel::selectionChanged, this, &UsersTab::onSelectionChanged);
ui_.tableUserList->setColumnWidth(0, 150);
ui_.tableUserList->setColumnWidth(1, 250);
ui_.tableUserList->setColumnWidth(2, 350);
connect(ui_.buttonNewUser, &QPushButton::clicked, this, &UsersTab::onAddUserButton);
connect(ui_.buttonEditUser, &QPushButton::clicked, this, &UsersTab::onEditUserButton);
connect(ui_.tableUserList, &QTableView::doubleClicked, this, &UsersTab::onEditUserButton);
connect(ui_.buttonRemoveUser, &QPushButton::clicked, this, &UsersTab::onRemoveUserButton);
users_.append(randomUser());
this->updateGUIState();
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void UsersTab::onAddUserButton()
{
SPUser user = randomUser();
UserDialog dialog(user, this);
if (QDialog::Accepted != dialog.exec())
return;
users_.append(user);
GRPCService &grpc = app().grpc();
if (grpc.isStreaming())
grpc.sendEvent(newLoginFinishedEvent(user->id()));
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void UsersTab::onEditUserButton()
{
int index = selectedIndex();
if ((index < 0) || (index >= users_.userCount()))
return;
SPUser user = this->selectedUser();
UserDialog dialog(user, this);
if (QDialog::Accepted != dialog.exec())
return;
users_.touch(index);
GRPCService &grpc = app().grpc();
if (grpc.isStreaming())
grpc.sendEvent(newUserChangedEvent(user->id()));
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void UsersTab::onRemoveUserButton()
{
int index = selectedIndex();
if ((index < 0) || (index >= users_.userCount()))
return;
SPUser const user = users_.userAtIndex(index);
users_.remove(index);
GRPCService &grpc = app().grpc();
if (grpc.isStreaming())
grpc.sendEvent(newUserChangedEvent(user->id()));
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void UsersTab::onSelectionChanged(QItemSelection, QItemSelection)
{
this->updateGUIState();
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void UsersTab::updateGUIState()
{
bool const hasSelectedUser = ui_.tableUserList->selectionModel()->hasSelection();
ui_.buttonEditUser->setEnabled(hasSelectedUser);
ui_.buttonRemoveUser->setEnabled(hasSelectedUser);
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
qint32 UsersTab::selectedIndex() const
{
return ui_.tableUserList->selectionModel()->hasSelection() ? ui_.tableUserList->currentIndex().row() : -1;
}
//****************************************************************************************************************************************************
/// \return The selected user.
/// \return A null pointer if no user is selected.
//****************************************************************************************************************************************************
bridgepp::SPUser UsersTab::selectedUser()
{
return users_.userAtIndex(this->selectedIndex());
}
//****************************************************************************************************************************************************
/// \return The list of users.
//****************************************************************************************************************************************************
UserTable &UsersTab::userTable()
{
return users_;
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
/// \return The user with the given userID.
/// \return A null pointer if the user is not in the list.
//****************************************************************************************************************************************************
bridgepp::SPUser UsersTab::userWithID(QString const &userID)
{
return users_.userWithID(userID);
}
//****************************************************************************************************************************************************
/// \return true iff the next login attempt should trigger a username/password error.
//****************************************************************************************************************************************************
bool UsersTab::nextUserUsernamePasswordError() const
{
return ui_.checkUsernamePasswordError->isChecked();
}
//****************************************************************************************************************************************************
/// \return true iff the next login attempt should trigger a free user error.
//****************************************************************************************************************************************************
bool UsersTab::nextUserFreeUserError() const
{
return ui_.checkFreeUserError->isChecked();
}
//****************************************************************************************************************************************************
/// \return true iff the next login attempt will require 2FA.
//****************************************************************************************************************************************************
bool UsersTab::nextUserTFARequired() const
{
return ui_.checkTFARequired->isChecked();
}
//****************************************************************************************************************************************************
/// \return true iff the next login attempt should trigger a 2FA error.
//****************************************************************************************************************************************************
bool UsersTab::nextUserTFAError() const
{
return ui_.checkTFAError->isChecked();
}
//****************************************************************************************************************************************************
/// \return true iff the next login attempt should trigger a 2FA error with abort.
//****************************************************************************************************************************************************
bool UsersTab::nextUserTFAAbort() const
{
return ui_.checkTFAAbort->isChecked();
}
//****************************************************************************************************************************************************
/// \return true iff the next login attempt will require a 2nd password.
//****************************************************************************************************************************************************
bool UsersTab::nextUserTwoPasswordsRequired() const
{
return ui_.checkTwoPasswordsRequired->isChecked();
}
//****************************************************************************************************************************************************
/// \return true iff the next login attempt should trigger a 2nd password error.
//****************************************************************************************************************************************************
bool UsersTab::nextUserTwoPasswordsError() const
{
return ui_.checkTwoPasswordsError->isChecked();
}
//****************************************************************************************************************************************************
/// \return true iff the next login attempt should trigger a 2nd password error with abort.
//****************************************************************************************************************************************************
bool UsersTab::nextUserTwoPasswordsAbort() const
{
return ui_.checkTwoPasswordsAbort->isChecked();
}
//****************************************************************************************************************************************************
/// \return true iff the next login attempt should trigger a 2nd password error with abort.
//****************************************************************************************************************************************************
bool UsersTab::nextUserAlreadyLoggedIn() const
{
return ui_.checkAlreadyLoggedIn->isChecked();
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
/// \param[in] makeItActive Should split mode be activated.
//****************************************************************************************************************************************************
void UsersTab::setUserSplitMode(QString const &userID, bool makeItActive)
{
qint32 const index = users_.indexOfUser(userID);
SPUser const user = users_.userAtIndex(index);
if (!user)
{
app().log().error(QString("%1 failed. unknown user %1").arg(__FUNCTION__, userID));
return;
}
user->setSplitMode(makeItActive);
users_.touch(index);
MainWindow &mainWindow = app().mainWindow();
mainWindow.sendDelayedEvent(newUserChangedEvent(userID));
mainWindow.sendDelayedEvent(newToggleSplitModeFinishedEvent(userID));
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
//****************************************************************************************************************************************************
void UsersTab::logoutUser(QString const &userID)
{
qint32 const index = users_.indexOfUser(userID);
SPUser const user = users_.userAtIndex(index);
if (!user)
{
app().log().error(QString("%1 failed. unknown user %1").arg(__FUNCTION__, userID));
return;
}
user->setLoggedIn(false);
users_.touch(index);
app().mainWindow().sendDelayedEvent(newUserChangedEvent(userID));
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
//****************************************************************************************************************************************************
void UsersTab::removeUser(QString const &userID)
{
qint32 const index = users_.indexOfUser(userID);
SPUser const user = users_.userAtIndex(index);
if (!user)
{
app().log().error(QString("%1 failed. unknown user %1").arg(__FUNCTION__, userID));
return;
}
users_.remove(index);
app().mainWindow().sendDelayedEvent(newUserChangedEvent(userID));
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
/// \param[in] address The address.
//****************************************************************************************************************************************************
void UsersTab::configureUserAppleMail(QString const &userID, QString const &address)
{
app().log().info(QString("Apple mail configuration was requested for user %1, address %2").arg(userID, address));
}

View File

@ -0,0 +1,75 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_USERS_TAB_H
#define BRIDGE_GUI_TESTER_USERS_TAB_H
#include "Tabs/ui_UsersTab.h"
#include "UserTable.h"
//****************************************************************************************************************************************************
/// \brief The 'Users' tab of the main window.
//****************************************************************************************************************************************************
class UsersTab : public QWidget
{
Q_OBJECT
public: // member functions.
explicit UsersTab(QWidget *parent = nullptr); ///< Default constructor.
UsersTab(UsersTab const &) = delete; ///< Disabled copy-constructor.
UsersTab(UsersTab &&) = delete; ///< Disabled assignment copy-constructor.
~UsersTab() override = default; ///< Destructor.
UsersTab &operator=(UsersTab const &) = delete; ///< Disabled assignment operator.
UsersTab &operator=(UsersTab &&) = delete; ///< Disabled move assignment operator.
UserTable &userTable(); ///< Returns a reference to the user table.
bridgepp::SPUser userWithID(QString const &userID); ///< Get the user with the given ID.
bool nextUserUsernamePasswordError() const; ///< Check if next user login should trigger a username/password error.
bool nextUserFreeUserError() const; ///< Check if next user login should trigger a Free user error.
bool nextUserTFARequired() const; ///< Check if next user login should requires 2FA.
bool nextUserTFAError() const; ///< Check if next user login should trigger 2FA error
bool nextUserTFAAbort() const; ///< Check if next user login should trigger 2FA abort.
bool nextUserTwoPasswordsRequired() const; ///< Check if next user login requires 2nd password
bool nextUserTwoPasswordsError() const; ///< Check if next user login should trigger 2nd password error.
bool nextUserTwoPasswordsAbort() const; ///< Check if next user login should trigger 2nd password abort.
bool nextUserAlreadyLoggedIn() const; ///< Check if next user login should report user as already logged in.
public slots:
void setUserSplitMode(QString const &userID, bool makeItActive); ///< Slot for the split mode.
void logoutUser(QString const &userID); ///< slot for the logging out of a user.
void removeUser(QString const &userID); ///< Slot for the removal of a user.
void configureUserAppleMail(QString const &userID, QString const &address); ///< Slot for the configuration of Apple mail.
private slots:
void onAddUserButton(); ///< Add a user to the user list.
void onEditUserButton(); ///< Edit the currently selected user.
void onRemoveUserButton(); ///< Remove the currently selected user.
void onSelectionChanged(QItemSelection, QItemSelection); ///< Slot for the change of the selection.
private: // member functions.
void updateGUIState(); ///< Update the GUI state.
qint32 selectedIndex() const; ///< Get the index of the selected row.
bridgepp::SPUser selectedUser(); ///< Get the selected user.
private: // data members.
Ui::UsersTab ui_ {}; ///< The UI for the tab.
UserTable users_; ///< The User list.
};
#endif //BRIDGE_GUI_TESTER_USERS_TAB_H

View File

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UsersTab</class>
<widget class="QWidget" name="UsersTab">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1207</width>
<height>709</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTableView" name="tableUserList">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="buttonNewUser">
<property name="text">
<string>New User</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonEditUser">
<property name="text">
<string>Edit User</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonRemoveUser">
<property name="text">
<string>Remove User</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="title">
<string>Next Login Attempt</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="checkUsernamePasswordError">
<property name="text">
<string>Username/password error</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkFreeUserError">
<property name="text">
<string>Free user error</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkTFARequired">
<property name="text">
<string>2FA required</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkTFAError">
<property name="text">
<string>2FA error</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkTFAAbort">
<property name="text">
<string>2FA abort</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkTwoPasswordsRequired">
<property name="text">
<string>2nd password required</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkTwoPasswordsError">
<property name="text">
<string>2nd password error</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkTwoPasswordsAbort">
<property name="text">
<string>2nd password abort</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkAlreadyLoggedIn">
<property name="text">
<string>Already logged in</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>buttonNewUser</tabstop>
<tabstop>buttonEditUser</tabstop>
<tabstop>buttonRemoveUser</tabstop>
<tabstop>tableUserList</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,65 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "UserDialog.h"
//****************************************************************************************************************************************************
/// \param[in] user The user.
/// \param[in] parent The parent widget of the dialog.
//****************************************************************************************************************************************************
UserDialog::UserDialog(bridgepp::SPUser &user, QWidget *parent)
: QDialog(parent)
, user_(user)
{
ui_.setupUi(this);
connect(ui_.buttonOK, &QPushButton::clicked, this, &UserDialog::onOK);
connect(ui_.buttonCancel, &QPushButton::clicked, this, &UserDialog::reject);
ui_.editUserID->setText(user_->id());
ui_.editUsername->setText(user_->username());
ui_.editPassword->setText(user->password());
ui_.editAddresses->setPlainText(user->addresses().join("\n"));
ui_.editAvatarText->setText(user_->avatarText());
ui_.checkLoggedIn->setChecked(user_->loggedIn());
ui_.checkSplitMode->setChecked(user_->splitMode());
ui_.checkSetupGuideSeen->setChecked(user_->setupGuideSeen());
ui_.spinUsedBytes->setValue(user->usedBytes());
ui_.spinTotalBytes->setValue(user->totalBytes());
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void UserDialog::onOK()
{
user_->setID(ui_.editUserID->text());
user_->setUsername(ui_.editUsername->text());
user_->setPassword(ui_.editPassword->text());
user_->setAddresses(ui_.editAddresses->toPlainText().split(QRegularExpression(R"(\s+)"), Qt::SkipEmptyParts));
user_->setAvatarText(ui_.editAvatarText->text());
user_->setLoggedIn(ui_.checkLoggedIn->isChecked());
user_->setSplitMode(ui_.checkSplitMode->isChecked());
user_->setSetupGuideSeen(ui_.checkSetupGuideSeen->isChecked());
user_->setUsedBytes(float(ui_.spinUsedBytes->value()));
user_->setTotalBytes(float(ui_.spinTotalBytes->value()));
this->accept();
}

View File

@ -0,0 +1,49 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_USER_DIALOG_H
#define BRIDGE_GUI_TESTER_USER_DIALOG_H
#include "ui_UserDialog.h"
#include <bridgepp/User/User.h>
//****************************************************************************************************************************************************
/// \brief User dialog class.
//****************************************************************************************************************************************************
class UserDialog : public QDialog
{
Q_OBJECT
public: // member functions.
UserDialog(bridgepp::SPUser &user, QWidget *parent); ///< Default constructor.
UserDialog(UserDialog const &) = delete; ///< Disabled copy-constructor.
UserDialog(UserDialog &&) = delete; ///< Disabled assignment copy-constructor.
~UserDialog() override = default; ///< Destructor.
UserDialog &operator=(UserDialog const &) = delete; ///< Disabled assignment operator.
UserDialog &operator=(UserDialog &&) = delete; ///< Disabled move assignment operator.
private slots:
void onOK(); ///< Slot for the OK button.
private:
Ui::UserDialog ui_ {}; ///< The UI for the dialog.
bridgepp::SPUser user_; ///< The user
};
#endif //BRIDGE_GUI_TESTER_USER_DIALOG_H

View File

@ -0,0 +1,220 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UserDialog</class>
<widget class="QDialog" name="UserDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>521</width>
<height>432</height>
</rect>
</property>
<property name="windowTitle">
<string>User</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1,0">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0">
<widget class="QLabel" name="labelAvatarText">
<property name="text">
<string>Avatar Text</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelPassword">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelUsername">
<property name="text">
<string>Account Name</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelUserID">
<property name="text">
<string>UserID</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelUsedBytes">
<property name="text">
<string>Used Bytes</string>
</property>
</widget>
</item>
<item row="9" column="0" colspan="2">
<widget class="QCheckBox" name="checkSetupGuideSeen">
<property name="text">
<string>Setup Guide Seen</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="editPassword"/>
</item>
<item row="6" column="0">
<widget class="QLabel" name="labelTotalBytes">
<property name="text">
<string>Total Bytes</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="editAvatarText"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelAddresses">
<property name="text">
<string>Adresses</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPlainTextEdit" name="editAddresses">
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>150</height>
</size>
</property>
<property name="tabChangesFocus">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDoubleSpinBox" name="spinUsedBytes">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>0</number>
</property>
<property name="maximum">
<double>1000000000000000.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="editUserID">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QCheckBox" name="checkLoggedIn">
<property name="text">
<string>Logged in</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="editUsername"/>
</item>
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="spinTotalBytes">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>0</number>
</property>
<property name="maximum">
<double>1000000000000000.000000000000000</double>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<widget class="QCheckBox" name="checkSplitMode">
<property name="text">
<string>Split Mode</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonCancel">
<property name="text">
<string>&amp;Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonOK">
<property name="text">
<string>&amp;OK</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>editUserID</tabstop>
<tabstop>editUsername</tabstop>
<tabstop>editPassword</tabstop>
<tabstop>editAddresses</tabstop>
<tabstop>editAvatarText</tabstop>
<tabstop>spinUsedBytes</tabstop>
<tabstop>spinTotalBytes</tabstop>
<tabstop>checkLoggedIn</tabstop>
<tabstop>checkSplitMode</tabstop>
<tabstop>checkSetupGuideSeen</tabstop>
<tabstop>buttonOK</tabstop>
<tabstop>buttonCancel</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,207 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "UserTable.h"
using namespace bridgepp;
//****************************************************************************************************************************************************
/// \param[in] parent The parent object of the class
//****************************************************************************************************************************************************
UserTable::UserTable(QObject *parent)
: QAbstractTableModel(parent)
{
}
//****************************************************************************************************************************************************
/// \return The number of rows in the table.
//****************************************************************************************************************************************************
int UserTable::rowCount(QModelIndex const &) const
{
return users_.size();
}
//****************************************************************************************************************************************************
/// \return The number of columns in the table.
//****************************************************************************************************************************************************
int UserTable::columnCount(QModelIndex const &) const
{
return 3;
}
//****************************************************************************************************************************************************
/// \param[in]
//****************************************************************************************************************************************************
QVariant UserTable::data(QModelIndex const &index, int role) const
{
int const row = index.row();
if ((row < 0) || (row >= users_.size()) || (Qt::DisplayRole != role))
return QVariant();
SPUser const user = users_[row];
if (!user)
return QVariant();
switch (index.column())
{
case 0:
return user->property("username");
case 1:
return user->property("addresses").toStringList().join(" ");
case 2:
return user->property("id");
default:
return QVariant();
}
}
//****************************************************************************************************************************************************
/// \param[in] section The section (column).
/// \param[in] orientation The orientation.
/// \param[in] role The role to retrieve data
//****************************************************************************************************************************************************
QVariant UserTable::headerData(int section, Qt::Orientation orientation, int role) const
{
if (Qt::DisplayRole != role)
return QAbstractTableModel::headerData(section, orientation, role);
if (Qt::Horizontal != orientation)
return QString();
switch (section)
{
case 0:
return "UserName";
case 1:
return "Addresses";
case 2:
return "UserID";
default:
return QString();
}
}
//****************************************************************************************************************************************************
/// \param[in] user The user to add.
//****************************************************************************************************************************************************
void UserTable::append(SPUser const &user)
{
qint32 const count = users_.size();
this->beginInsertRows(QModelIndex(), count, count);
users_.append(user);
this->endInsertRows();
}
//****************************************************************************************************************************************************
/// \return The number of users in the table.
//****************************************************************************************************************************************************
qint32 UserTable::userCount() const
{
return users_.count();
}
//****************************************************************************************************************************************************
/// \param[in] index The index of the user in the list.
/// \return the user at the given index.
/// \return null if the index is out of bounds.
//****************************************************************************************************************************************************
bridgepp::SPUser UserTable::userAtIndex(qint32 index)
{
return isIndexValid(index) ? users_[index] : nullptr;
}
//****************************************************************************************************************************************************
/// \return The user with the given userID.
/// \return A null pointer if the user is not in the list.
//****************************************************************************************************************************************************
bridgepp::SPUser UserTable::userWithID(QString const &userID)
{
QList<SPUser>::const_iterator it = std::find_if(users_.constBegin(), users_.constEnd(), [&userID](SPUser const& user) -> bool {
return user->id() == userID;
});
return it == users_.end() ? nullptr : *it;
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
/// \return the index of the user.
/// \return -1 if the user could not be found.
//****************************************************************************************************************************************************
qint32 UserTable::indexOfUser(QString const &userID)
{
QList<SPUser>::const_iterator it = std::find_if(users_.constBegin(), users_.constEnd(), [&userID](SPUser const& user) -> bool {
return user->id() == userID;
});
return it == users_.end() ? -1 : it - users_.constBegin();
}
//****************************************************************************************************************************************************
/// \param[in] index The index of the user in the list.
//****************************************************************************************************************************************************
void UserTable::touch(qint32 index)
{
if (isIndexValid(index))
emit dataChanged(this->index(index, 0), this->index(index, this->columnCount(QModelIndex()) - 1));
}
//****************************************************************************************************************************************************
/// \param[in] index The index of the user in the list.
//****************************************************************************************************************************************************
void UserTable::remove(qint32 index)
{
if (!isIndexValid(index))
return;
this->beginRemoveRows(QModelIndex(), index, index);
users_.removeAt(index);
this->endRemoveRows();
}
//****************************************************************************************************************************************************
/// \return true iff the index is valid.
//****************************************************************************************************************************************************
bool UserTable::isIndexValid(qint32 index) const
{
return (index >= 0) && (index < users_.count());
}
//****************************************************************************************************************************************************
/// \return The user list.
//****************************************************************************************************************************************************
QList<bridgepp::SPUser> UserTable::users() const
{
return users_;
}

View File

@ -0,0 +1,61 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_USER_TABLE_H
#define BRIDGE_GUI_TESTER_USER_TABLE_H
#include <bridgepp/User/User.h>
//****************************************************************************************************************************************************
/// \brief User table model class
//****************************************************************************************************************************************************
class UserTable : public QAbstractTableModel
{
Q_OBJECT
public: // member functions.
explicit UserTable(QObject *parent); ///< Default constructor.
UserTable(UserTable const &) = delete; ///< Disabled copy-constructor.
UserTable(UserTable &&) = delete; ///< Disabled assignment copy-constructor.
~UserTable() = default; ///< Destructor.
UserTable &operator=(UserTable const &) = delete; ///< Disabled assignment operator.
UserTable &operator=(UserTable &&) = delete; ///< Disabled move assignment operator.
qint32 userCount() const; ///< Return the number of users in the table.
void append(bridgepp::SPUser const& user); ///< Append a user.
bridgepp::SPUser userAtIndex(qint32 index); ///< Return the user at the given index.
bridgepp::SPUser userWithID(QString const &userID); ///< Return the user with a given id.
qint32 indexOfUser(QString const& userID); ///< Return the index of a given User.
void touch(qint32 index); ///< touch the user at a given index (indicates it has been modified).
void remove(qint32 index); ///< Remove the user at a given index.
QList<bridgepp::SPUser> users() const; ///< Return a copy of the user list.
private: // data members.
int rowCount(QModelIndex const &parent) const override; ///< Get the number of rows in the table.
int columnCount(QModelIndex const &parent) const override; ///< Get the number of columns in the table.
QVariant data(QModelIndex const &index, int role) const override; ///< Get the data for a role at a given index.
QVariant headerData(int section, Qt::Orientation orientation, int role) const override; ///< Get header data.
bool isIndexValid(qint32 index) const; ///< return true iff the index is valid.
public:
QList<bridgepp::SPUser> users_;
};
#endif //BRIDGE_GUI_TESTER_USER_TABLE_H

View File

@ -0,0 +1,98 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "MainWindow.h"
#include "AppController.h"
#include "GRPCServerWorker.h"
#include <bridgepp/BridgeUtils.h>
#include <bridgepp/Exception/Exception.h>
#include <bridgepp/Worker/Overseer.h>
#ifndef BRIDGE_APP_VERSION
#error "BRIDGE_APP_VERSION is not defined"
#endif
namespace
{
QString const applicationName = "Proton Mail Bridge GUI Tester"; ///< The name of the application.
}
using namespace bridgepp;
//****************************************************************************************************************************************************
/// \param[in] argc The number of command-line arguments.
/// \param[in] argv The list of command-line arguments.
/// \return The exit code for the application.
//****************************************************************************************************************************************************
int main(int argc, char **argv)
{
try
{
QApplication a(argc, argv);
QApplication::setApplicationName(applicationName);
QApplication::setOrganizationName("Proton AG");
QApplication::setOrganizationDomain("proton.ch");
QApplication::setQuitOnLastWindowClosed(true);
app().log().setEchoInConsole(true);
app().log().info(QString("%1 started.").arg(applicationName));
MainWindow window(nullptr);
app().setMainWindow(&window);
window.setWindowTitle(QApplication::applicationName());
window.show();
GRPCServerWorker *serverWorker = new GRPCServerWorker(nullptr);
QObject::connect(serverWorker, &Worker::started, []() { app().log().info("Server worker started."); });
QObject::connect(serverWorker, &Worker::finished, []() { app().log().info("Server worker finished."); });
QObject::connect(serverWorker, &Worker::error, [](QString const &message) {
throw Exception(QString("gRPC Server encountered an error: %1").arg(message));
});
UPOverseer overseer = std::make_unique<Overseer>(serverWorker, nullptr);
overseer->startWorker(true);
qint32 const exitCode = QApplication::exec();
serverWorker->stop();
while (!overseer->isFinished())
{
QThread::msleep(10);
}
app().log().info(QString("%1 exiting with code %2.").arg(applicationName).arg(exitCode));
return exitCode;
}
catch (Exception const &e)
{
QTextStream(stderr) << QString("A fatal error occurred: %1\n").arg(e.qwhat());
return EXIT_FAILURE;
}
}

View File

@ -108,8 +108,10 @@ void QMLBackend::connectGrpcEvents()
connect(client, &GRPCClient::login2PasswordError, this, &QMLBackend::login2PasswordError);
connect(client, &GRPCClient::login2PasswordErrorAbort, this, &QMLBackend::login2PasswordErrorAbort);
connect(client, &GRPCClient::loginFinished, this, [&](QString const &userID) {
this->retrieveUserList();
qint32 const index = users_->rowOfUserID(userID); emit loginFinished(index); });
connect(client, &GRPCClient::loginAlreadyLoggedIn, this, [&](QString const &userID) {
this->retrieveUserList();
qint32 const index = users_->rowOfUserID(userID); emit loginAlreadyLoggedIn(index); });
// update events

View File

@ -104,6 +104,7 @@ add_library(bridgepp
bridgepp/BridgeUtils.cpp bridgepp/BridgeUtils.h
bridgepp/Exception/Exception.h bridgepp/Exception/Exception.cpp
bridgepp/GRPC/GRPCClient.cpp bridgepp/GRPC/GRPCClient.h
bridgepp/GRPC/EventFactory.cpp bridgepp/GRPC/EventFactory.h
bridgepp/GRPC/GRPCUtils.cpp bridgepp/GRPC/GRPCUtils.h
${PROTO_CPP_FILE} ${PROTO_H_FILE} ${GRPC_CPP_FILE} ${GRPC_H_FILE}
bridgepp/Log/Log.h bridgepp/Log/Log.cpp

View File

@ -18,19 +18,55 @@
#include "BridgeUtils.h"
#include "Exception/Exception.h"
#include <random>
namespace bridgepp
{
namespace {
QString const configFolder = "protonmail/bridge";
QMutex rngMutex; ///< the mutex to use when accessing the rng.
QStringList const firstNames {
"James", "John", "Robert", "Michael", "William", "David", "Richard", "Charles", "Joseph", "Thomas", "Christopher", "Daniel", "Paul", "Mark",
"Donald", "George", "Kenneth", "Steven", "Edward", "Brian", "Ronald", "Anthony", "Kevin", "Jason", "Matthew", "Gary", "Timothy", "Jose", "Larry",
"Jeffrey", "Frank", "Scott", "Eric", "Stephen", "Andrew", "Raymond", "Jack", "Gregory", "Joshua", "Jerry", "Dennis", "Walter", "Patrick", "Peter",
"Harold", "Douglas", "Henry", "Carl", "Arthur", "Ryan", "Mary", "Patricia", "Barbara", "Linda", "Elizabeth", "Maria", "Jennifer", "Susan",
"Margaret", "Dorothy", "Lisa", "Nancy", "Karen", "Betty", "Helen", "Sandra", "Donna", "Ruth", "Sharon", "Michelle", "Laura", "Sarah", "Kimberly",
"Deborah", "Jessica", "Shirley", "Cynthia", "Angela", "Emily", "Brenda", "Amy", "Anna", "Rebecca", "Virginia", "Kathleen", "Pamela", "Martha",
"Debra", "Amanda", "Stephanie", "Caroline", "Christine", "Marie", "Janet", "Catherine", "Frances", "Ann", "Joyce", "Diane", "Alice",
}; ///< List of common US first names. (source: census.gov)
QStringList const lastNames {
"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez", "Hernandez", "Lopez", "Gonzalez",
"Wilson", "Anderson", "Thomas", "Taylor", "Moore", "Jackson", "Martin", "Lee", "Perez", "Thompson", "White", "Harris", "Sanchez", "Clark",
"Ramirez", "Lewis", "Robinson", "Walker", "Young", "Allen", "King", "Wright", "Scott", "Torres", "Nguyen", "Hill", "Flores", "Green", "Adams",
"Nelson", "Baker", "Hall", "Rivera", "Campbell", "Mitchell", "Carter", "Roberts", "Gomez", "Phillips", "Evans", "Turner", "Diaz", "Parker",
"Cruz", "Edwards", "Collins", "Reyes", "Stewart", "Morris", "Morales", "Murphy", "Cook", "Rogers", "Gutierrez", "Ortiz", "Morgan", "Cooper",
"Peterson", "Bailey", "Reed", "Kelly", "Howard", "Ramos", "Kim", "Cox", "Ward", "Richardson", "Watson", "Brooks", "Chavez", "Wood", "James",
"Bennett", "Gray", "Mendoza", "Ruiz", "Hughes", "Price", "Alvarez", "Castillo", "Sanders", "Patel", "Myers", "Long", "Ross", "Foster", "Jimenez"
}; ///< List of common US last names (source: census.gov)
//****************************************************************************************************************************************************
/// \brief Return the 64 bit Mersenne twister random number generator.
//****************************************************************************************************************************************************
std::mt19937_64& rng()
{
// Do not use for crypto. std::random_device is not good enough.
static std::mt19937_64 generator = std::mt19937_64(std::random_device()());
return generator;
}
} // anonymous namespace
//****************************************************************************************************************************************************
/// \return user configuration directory used by bridge (based on Golang OS/File's UserConfigDir).
//****************************************************************************************************************************************************
@ -93,4 +129,73 @@ QString userCacheDir()
}
//****************************************************************************************************************************************************
/// \return The value GOOS would return for the current platform.
//****************************************************************************************************************************************************
QString goos()
{
#if defined(Q_OS_DARWIN)
return "darwin";
#elif defined(Q_OS_WINDOWS)
return "windows";
#else
return "linux";
#endif
}
//****************************************************************************************************************************************************
/// Slow, but not biased. Not for use in crypto functions though, as the RNG use std::random_device as a seed.
///
/// \return a random number in the range [0, n-1]
//****************************************************************************************************************************************************
qint64 randN(qint64 n)
{
QMutexLocker locker(&rngMutex);
return (n > 0) ? std::uniform_int_distribution<qint64>(0, n - 1)(rng()) : 0;
}
//****************************************************************************************************************************************************
/// \return A random first name.
//****************************************************************************************************************************************************
QString randomFirstName()
{
return firstNames[randN(firstNames.size())];
}
//****************************************************************************************************************************************************
/// \return A random last name.
//****************************************************************************************************************************************************
QString randomLastName()
{
return lastNames[randN(lastNames.size())];
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
SPUser randomUser()
{
SPUser user = User::newUser(nullptr);
user->setID(QUuid::createUuid().toString());
QString const firstName = randomFirstName();
QString const lastName = randomLastName();
QString const username = QString("%1.%2").arg(firstName.toLower(), lastName.toLower());
user->setUsername(username);
user->setAddresses(QStringList() << (username + "@proton.me") << (username + "@protonmail.com") );
user->setPassword(QUuid().createUuid().toString(QUuid::StringFormat::WithoutBraces).left(20));
user->setAvatarText(firstName.left(1) + lastName.left(1));
user->setLoggedIn(true);
user->setSplitMode(false);
user->setSetupGuideSeen(true);
qint64 const totalBytes = (500 + randN(2501)) * 1000000;
user->setUsedBytes(float(bridgepp::randN(totalBytes + 1)) * 1.05f); // we maybe slightly over quota
user->setTotalBytes(float(totalBytes));
return user;
}
} // namespace bridgepp

View File

@ -20,12 +20,19 @@
#define BRIDGE_GUI_TESTER_BRIDGE_UTILS_H
#include <bridgepp/User/User.h>
namespace bridgepp {
QString userConfigDir(); ///< Get the path of the user configuration folder.
QString userCacheDir(); ///< Get the path of the user cache folder.
QString goos(); ///< return the value of Go's GOOS for the current platform ("darwin", "linux" and "windows" are supported).
qint64 randN(qint64 n); ///< return a random integer in the half open range [0,n)
QString randomFirstName(); ///< Get a random first name from a pre-determined list.
QString randomLastName(); ///< Get a random first name from a pre-determined list.
SPUser randomUser(); ///< Get a random user.
} // namespace

View File

@ -0,0 +1,620 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "EventFactory.h"
namespace bridgepp
{
namespace
{
//****************************************************************************************************************************************************
/// \return a new SPStreamEvent
//****************************************************************************************************************************************************
bridgepp::SPStreamEvent newStreamEvent()
{
return std::make_shared<grpc::StreamEvent>();
}
//****************************************************************************************************************************************************
/// \param[in] appEvent The app event.
/// \return The stream event.
//****************************************************************************************************************************************************
bridgepp::SPStreamEvent wrapAppEvent(grpc::AppEvent *appEvent)
{
auto event = newStreamEvent();
event->set_allocated_app(appEvent);
return event;
}
//****************************************************************************************************************************************************
/// \param[in] loginEvent The login event.
/// \return The stream event.
//****************************************************************************************************************************************************
bridgepp::SPStreamEvent wrapLoginEvent(grpc::LoginEvent *loginEvent)
{
auto event = newStreamEvent();
event->set_allocated_login(loginEvent);
return event;
}
//****************************************************************************************************************************************************
/// \param[in] updateEvent The app event.
/// \return The stream event.
//****************************************************************************************************************************************************
bridgepp::SPStreamEvent wrapUpdateEvent(grpc::UpdateEvent *updateEvent)
{
auto event = newStreamEvent();
event->set_allocated_update(updateEvent);
return event;
}
//****************************************************************************************************************************************************
/// \param[in] cacheEvent The cache event.
/// \return The stream event.
//****************************************************************************************************************************************************
bridgepp::SPStreamEvent wrapCacheEvent(grpc::CacheEvent *cacheEvent)
{
auto event = newStreamEvent();
event->set_allocated_cache(cacheEvent);
return event;
}
//****************************************************************************************************************************************************
/// \param[in] mailSettingsEvent The mail settings event.
/// \return The stream event.
//****************************************************************************************************************************************************
bridgepp::SPStreamEvent wrapMailSettingsEvent(grpc::MailSettingsEvent *mailSettingsEvent)
{
auto event = newStreamEvent();
event->set_allocated_mailsettings(mailSettingsEvent);
return event;
}
//****************************************************************************************************************************************************
/// \param[in] keychainEvent The keychain event.
/// \return The stream event.
//****************************************************************************************************************************************************
bridgepp::SPStreamEvent wrapKeychainEvent(grpc::KeychainEvent *keychainEvent)
{
auto event = newStreamEvent();
event->set_allocated_keychain(keychainEvent);
return event;
}
//****************************************************************************************************************************************************
/// \param[in] mailEvent The mail event.
/// \return The stream event.
//****************************************************************************************************************************************************
bridgepp::SPStreamEvent wrapMailEvent(grpc::MailEvent *mailEvent)
{
auto event = newStreamEvent();
event->set_allocated_mail(mailEvent);
return event;
}
//****************************************************************************************************************************************************
/// \param[in] userEvent The user event.
/// \return The stream event.
//****************************************************************************************************************************************************
bridgepp::SPStreamEvent wrapUserEvent(grpc::UserEvent *userEvent)
{
auto event = newStreamEvent();
event->set_allocated_user(userEvent);
return event;
}
} // namespace
//****************************************************************************************************************************************************
/// \param[in] connected The internet status.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newInternetStatusEvent(bool connected)
{
auto *internetStatusEvent = new grpc::InternetStatusEvent();
internetStatusEvent->set_connected(connected);
auto appEvent = new grpc::AppEvent;
appEvent->set_allocated_internetstatus(internetStatusEvent);
return wrapAppEvent(appEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newToggleAutostartFinishedEvent()
{
auto *event = new grpc::ToggleAutostartFinishedEvent;
auto appEvent = new grpc::AppEvent;
appEvent->set_allocated_toggleautostartfinished(event);
return wrapAppEvent(appEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newResetFinishedEvent()
{
auto event = new grpc::ResetFinishedEvent;
auto appEvent = new grpc::AppEvent;
appEvent->set_allocated_resetfinished(event);
return wrapAppEvent(appEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newReportBugFinishedEvent()
{
auto event = new grpc::ReportBugFinishedEvent;
auto appEvent = new grpc::AppEvent;
appEvent->set_allocated_reportbugfinished(event);
return wrapAppEvent(appEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newReportBugSuccessEvent()
{
auto event = new grpc::ReportBugSuccessEvent;
auto appEvent = new grpc::AppEvent;
appEvent->set_allocated_reportbugsuccess(event);
return wrapAppEvent(appEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newReportBugErrorEvent()
{
auto event = new grpc::ReportBugErrorEvent;
auto appEvent = new grpc::AppEvent;
appEvent->set_allocated_reportbugerror(event);
return wrapAppEvent(appEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newShowMainWindowEvent()
{
auto event = new grpc::ShowMainWindowEvent;
auto appEvent = new grpc::AppEvent;
appEvent->set_allocated_showmainwindow(event);
return wrapAppEvent(appEvent);
}
//****************************************************************************************************************************************************
/// \param[in] error The error.
/// \param[in] message The message.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newLoginError(grpc::LoginErrorType error, QString const &message)
{
auto event = new ::grpc::LoginErrorEvent;
event->set_type(error);
event->set_message(message.toStdString());
auto loginEvent = new grpc::LoginEvent;
loginEvent->set_allocated_error(event);
return wrapLoginEvent(loginEvent);
}
//****************************************************************************************************************************************************
/// \param[in] username The username.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newLoginTfaRequestedEvent(QString const &username)
{
auto event = new ::grpc::LoginTfaRequestedEvent;
event->set_username(username.toStdString());
auto loginEvent = new grpc::LoginEvent;
loginEvent->set_allocated_tfarequested(event);
return wrapLoginEvent(loginEvent);
}
//****************************************************************************************************************************************************
/// \param[in] username The username.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newLoginTwoPasswordsRequestedEvent()
{
auto event = new ::grpc::LoginTwoPasswordsRequestedEvent;
auto loginEvent = new grpc::LoginEvent;
loginEvent->set_allocated_twopasswordrequested(event);
return wrapLoginEvent(loginEvent);
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newLoginFinishedEvent(QString const &userID)
{
auto event = new ::grpc::LoginFinishedEvent;
event->set_userid(userID.toStdString());
auto loginEvent = new grpc::LoginEvent;
loginEvent->set_allocated_finished(event);
return wrapLoginEvent(loginEvent);
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newLoginAlreadyLoggedInEvent(QString const &userID)
{
auto event = new ::grpc::LoginFinishedEvent;
event->set_userid(userID.toStdString());
auto loginEvent = new grpc::LoginEvent;
loginEvent->set_allocated_alreadyloggedin(event);
return wrapLoginEvent(loginEvent);
}
//****************************************************************************************************************************************************
/// \param[in] errorType The error type.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newUpdateErrorEvent(grpc::UpdateErrorType errorType)
{
auto event = new grpc::UpdateErrorEvent;
event->set_type(errorType);
auto updateEvent = new grpc::UpdateEvent;
updateEvent->set_allocated_error(event);
return wrapUpdateEvent(updateEvent);
}
//****************************************************************************************************************************************************
/// \param[in] version The version.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newUpdateManualReadyEvent(QString const &version)
{
auto event = new grpc::UpdateManualReadyEvent;
event->set_version(version.toStdString());
auto updateEvent = new grpc::UpdateEvent;
updateEvent->set_allocated_manualready(event);
return wrapUpdateEvent(updateEvent);
}
//****************************************************************************************************************************************************
/// \return the event.
//****************************************************************************************************************************************************
SPStreamEvent newUpdateManualRestartNeededEvent()
{
auto event = new grpc::UpdateManualRestartNeededEvent;
auto updateEvent = new grpc::UpdateEvent;
updateEvent->set_allocated_manualrestartneeded(event);
return wrapUpdateEvent(updateEvent);
}
//****************************************************************************************************************************************************
/// \param[in] version The version.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newUpdateForceEvent(QString const &version)
{
auto event = new grpc::UpdateForceEvent;
event->set_version(version.toStdString());
auto updateEvent = new grpc::UpdateEvent;
updateEvent->set_allocated_force(event);
return wrapUpdateEvent(updateEvent);
}
//****************************************************************************************************************************************************
/// \return the event.
//****************************************************************************************************************************************************
SPStreamEvent newUpdateSilentRestartNeeded()
{
auto event = new grpc::UpdateSilentRestartNeeded;
auto updateEvent = new grpc::UpdateEvent;
updateEvent->set_allocated_silentrestartneeded(event);
return wrapUpdateEvent(updateEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newUpdateIsLatestVersion()
{
auto event = new grpc::UpdateIsLatestVersion;
auto updateEvent = new grpc::UpdateEvent;
updateEvent->set_allocated_islatestversion(event);
return wrapUpdateEvent(updateEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newUpdateCheckFinished()
{
auto event = new grpc::UpdateCheckFinished;
auto updateEvent = new grpc::UpdateEvent;
updateEvent->set_allocated_checkfinished(event);
return wrapUpdateEvent(updateEvent);
}
//****************************************************************************************************************************************************
/// \param[in] errorType The error type.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newCacheErrorEvent(grpc::CacheErrorType errorType)
{
auto event = new grpc::CacheErrorEvent;
event->set_type(errorType);
auto cacheEvent = new grpc::CacheEvent;
cacheEvent->set_allocated_error(event);
return wrapCacheEvent(cacheEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newCacheLocationChangeSuccessEvent()
{
auto event = new grpc::CacheLocationChangeSuccessEvent;
auto cacheEvent = new grpc::CacheEvent;
cacheEvent->set_allocated_locationchangedsuccess(event);
return wrapCacheEvent(cacheEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newChangeLocalCacheFinishedEvent()
{
auto event = new grpc::ChangeLocalCacheFinishedEvent;
auto cacheEvent = new grpc::CacheEvent;
cacheEvent->set_allocated_changelocalcachefinished(event);
return wrapCacheEvent(cacheEvent);
}
//****************************************************************************************************************************************************
/// \param[in] enabled The new state of the cache.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newIsCacheOnDiskEnabledChanged(bool enabled)
{
auto event = new grpc::IsCacheOnDiskEnabledChanged;
event->set_enabled(enabled);
auto cacheEvent = new grpc::CacheEvent;
cacheEvent->set_allocated_iscacheondiskenabledchanged(event);
return wrapCacheEvent(cacheEvent);
}
//****************************************************************************************************************************************************
/// \param[in] path The path of the cache.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newDiskCachePathChanged(QString const &path)
{
auto event = new grpc::DiskCachePathChanged;
event->set_path(path.toStdString());
auto cacheEvent = new grpc::CacheEvent;
cacheEvent->set_allocated_diskcachepathchanged(event);
return wrapCacheEvent(cacheEvent);
}
//****************************************************************************************************************************************************
/// \param[in] errorType The error type.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newMailSettingsErrorEvent(grpc::MailSettingsErrorType errorType)
{
auto event = new grpc::MailSettingsErrorEvent;
event->set_type(errorType);
auto mailSettingsEvent = new grpc::MailSettingsEvent;
mailSettingsEvent->set_allocated_error(event);
return wrapMailSettingsEvent(mailSettingsEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newUseSslForSmtpFinishedEvent()
{
auto event = new grpc::UseSslForSmtpFinishedEvent;
auto mailSettingsEvent = new grpc::MailSettingsEvent;
mailSettingsEvent->set_allocated_usesslforsmtpfinished(event);
return wrapMailSettingsEvent(mailSettingsEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newChangePortsFinishedEvent()
{
auto event = new grpc::ChangePortsFinishedEvent;
auto mailSettingsEvent = new grpc::MailSettingsEvent;
mailSettingsEvent->set_allocated_changeportsfinished(event);
return wrapMailSettingsEvent(mailSettingsEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newChangeKeychainFinishedEvent()
{
auto event = new grpc::ChangeKeychainFinishedEvent;
auto keychainEvent = new grpc::KeychainEvent;
keychainEvent->set_allocated_changekeychainfinished(event);
return wrapKeychainEvent(keychainEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newHasNoKeychainEvent()
{
auto event = new grpc::HasNoKeychainEvent;
auto keychainEvent = new grpc::KeychainEvent;
keychainEvent->set_allocated_hasnokeychain(event);
return wrapKeychainEvent(keychainEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newRebuildKeychainEvent()
{
auto event = new grpc::RebuildKeychainEvent;
auto keychainEvent = new grpc::KeychainEvent;
keychainEvent->set_allocated_rebuildkeychain(event);
return wrapKeychainEvent(keychainEvent);
}
//****************************************************************************************************************************************************
/// \param[in] email The email.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newNoActiveKeyForRecipientEvent(QString const &email)
{
auto event = new grpc::NoActiveKeyForRecipientEvent;
event->set_email(email.toStdString());
auto mailEvent = new grpc::MailEvent;
mailEvent->set_allocated_noactivekeyforrecipientevent(event);
return wrapMailEvent(mailEvent);
}
//****************************************************************************************************************************************************
/// \param[in] address The address.
/// /// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newAddressChangedEvent(QString const &address)
{
auto event = new grpc::AddressChangedEvent;
event->set_address(address.toStdString());
auto mailEvent = new grpc::MailEvent;
mailEvent->set_allocated_addresschanged(event);
return wrapMailEvent(mailEvent);
}
//****************************************************************************************************************************************************
/// \param[in] address The address.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newAddressChangedLogoutEvent(QString const &address)
{
auto event = new grpc::AddressChangedLogoutEvent;
event->set_address(address.toStdString());
auto mailEvent = new grpc::MailEvent;
mailEvent->set_allocated_addresschangedlogout(event);
return wrapMailEvent(mailEvent);
}
//****************************************************************************************************************************************************
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newApiCertIssueEvent()
{
auto event = new grpc::ApiCertIssueEvent;
auto mailEvent = new grpc::MailEvent;
mailEvent->set_allocated_apicertissue(event);
return wrapMailEvent(mailEvent);
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newToggleSplitModeFinishedEvent(QString const &userID)
{
auto event = new grpc::ToggleSplitModeFinishedEvent;
event->set_userid(userID.toStdString());
auto userEvent = new grpc::UserEvent;
userEvent->set_allocated_togglesplitmodefinished(event);
return wrapUserEvent(userEvent);
}
//****************************************************************************************************************************************************
/// \param[in] username The username.
/// /// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newUserDisconnectedEvent(QString const &username)
{
auto event = new grpc::UserDisconnectedEvent;
event->set_username(username.toStdString());
auto userEvent = new grpc::UserEvent;
userEvent->set_allocated_userdisconnected(event);
return wrapUserEvent(userEvent);
}
//****************************************************************************************************************************************************
/// \param[in] userID The userID.
/// \return The event.
//****************************************************************************************************************************************************
SPStreamEvent newUserChangedEvent(QString const &userID)
{
auto event = new grpc::UserChangedEvent;
event->set_userid(userID.toStdString());
auto userEvent = new grpc::UserEvent;
userEvent->set_allocated_userchanged(event);
return wrapUserEvent(userEvent);
}
} // namespace bridgepp

View File

@ -0,0 +1,88 @@
// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge 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.
//
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_EVENT_FACTORY_H
#define BRIDGE_GUI_TESTER_EVENT_FACTORY_H
#include "bridge.grpc.pb.h"
#include "GRPCUtils.h"
namespace bridgepp
{
// App events
SPStreamEvent newInternetStatusEvent(bool connected); ///< Create a new InternetStatusEvent event.
SPStreamEvent newToggleAutostartFinishedEvent(); ///< Create a new ToggleAutostartFinishedEvent event.
SPStreamEvent newResetFinishedEvent(); ///< Create a new ResetFinishedEvent event.
SPStreamEvent newReportBugFinishedEvent(); ///< Create a new ReportBugFinishedEvent event.
SPStreamEvent newReportBugSuccessEvent(); ///< Create a new ReportBugSuccessEvent event.
SPStreamEvent newReportBugErrorEvent(); ///< Create a new ReportBugErrorEvent event.
SPStreamEvent newShowMainWindowEvent(); ///< Create a new ShowMainWindowEvent event.
// Login events
SPStreamEvent newLoginError(grpc::LoginErrorType error, QString const &message); ///< Create a new LoginError event.
SPStreamEvent newLoginTfaRequestedEvent(QString const &username); ///< Create a new LoginTfaRequestedEvent event.
SPStreamEvent newLoginTwoPasswordsRequestedEvent(); ///< Create a new LoginTwoPasswordsRequestedEvent event.
SPStreamEvent newLoginFinishedEvent(QString const &userID); ///< Create a new LoginFinishedEvent event.
SPStreamEvent newLoginAlreadyLoggedInEvent(QString const &userID); ///< Create a new LoginAlreadyLoggedInEvent event.
// Update related events
SPStreamEvent newUpdateErrorEvent(grpc::UpdateErrorType errorType); ///< Create a new UpdateErrorEvent event.
SPStreamEvent newUpdateManualReadyEvent(QString const &version); ///< Create a new UpdateManualReadyEvent event.
SPStreamEvent newUpdateManualRestartNeededEvent(); ///< Create a new UpdateManualRestartNeededEvent event.
SPStreamEvent newUpdateForceEvent(QString const &version); ///< Create a new UpdateForceEvent event.
SPStreamEvent newUpdateSilentRestartNeeded(); ///< Create a new UpdateSilentRestartNeeded event.
SPStreamEvent newUpdateIsLatestVersion(); ///< Create a new UpdateIsLatestVersion event.
SPStreamEvent newUpdateCheckFinished(); ///< Create a new UpdateCheckFinished event.
// Cache on disk related events
SPStreamEvent newCacheErrorEvent(grpc::CacheErrorType errorType); ///< Create a new CacheErrorEvent event.
SPStreamEvent newCacheLocationChangeSuccessEvent(); ///< Create a new CacheLocationChangeSuccessEvent event.
SPStreamEvent newChangeLocalCacheFinishedEvent(); ///< Create a new ChangeLocalCacheFinishedEvent event.
SPStreamEvent newIsCacheOnDiskEnabledChanged(bool enabled); ///< Create a new IsCacheOnDiskEnabledChanged event.
SPStreamEvent newDiskCachePathChanged(QString const &path); ///< Create a new DiskCachePathChanged event.
// Mail settings related events
SPStreamEvent newMailSettingsErrorEvent(grpc::MailSettingsErrorType errorType); ///< Create a new MailSettingsErrorEvent event.
SPStreamEvent newUseSslForSmtpFinishedEvent(); ///< Create a new UseSslForSmtpFinishedEvent event.
SPStreamEvent newChangePortsFinishedEvent(); ///< Create a new ChangePortsFinishedEvent event.
// keychain related events
SPStreamEvent newChangeKeychainFinishedEvent(); ///< Create a new ChangeKeychainFinishedEvent event.
SPStreamEvent newHasNoKeychainEvent(); ///< Create a new HasNoKeychainEvent event.
SPStreamEvent newRebuildKeychainEvent(); ///< Create a new RebuildKeychainEvent event.
// Mail related events
SPStreamEvent newNoActiveKeyForRecipientEvent(QString const &email); ///< Create a new NoActiveKeyForRecipientEvent event.
SPStreamEvent newAddressChangedEvent(QString const &address); ///< Create a new AddressChangedEvent event.
SPStreamEvent newAddressChangedLogoutEvent(QString const &address); ///< Create a new AddressChangedLogoutEvent event.
SPStreamEvent newApiCertIssueEvent(); ///< Create a new ApiCertIssueEvent event.
// User list related event
SPStreamEvent newToggleSplitModeFinishedEvent(QString const &userID); ///< Create a new ToggleSplitModeFinishedEvent event.
SPStreamEvent newUserDisconnectedEvent(QString const &username); ///< Create a new UserDisconnectedEvent event.
SPStreamEvent newUserChangedEvent(QString const &userID); ///< Create a new UserChangedEvent event.
} // namespace bridgepp
#endif //BRIDGE_GUI_TESTER_EVENT_FACTORY_H

View File

@ -29,6 +29,9 @@ namespace bridgepp
{
typedef std::shared_ptr<grpc::StreamEvent> SPStreamEvent; ///< Type definition for shared pointer to grpc::StreamEvent.
QString serverCertificatePath(); ///< Return the path of the server certificate.
QString serverKeyPath(); ///< Return the path of the server key.
grpc::LogLevel logLevelToGRPC(Log::Level level); ///< Convert a Log::Level to gRPC enum value.