Other: fix bug in login screen <-> main window transition. [skip ci]

Other: fixed bug with split mode toggle. [skip ci]

Other: fix QML warnings. [skip ci]

Other: fix showMainWindow gRPC event binding. [skip ci].

QML Fixes [skip ci]

Other: wait for EventStreamReader thread to finish on exit.

Other: made BridgeMonitor generic, as ProcessMonitor. [skip ci]
This commit is contained in:
Xavier Michelon
2022-08-11 11:27:05 +02:00
committed by Jakub
parent 4ed9625959
commit ed904c2bdd
25 changed files with 149 additions and 125 deletions

View File

@ -19,7 +19,6 @@
#include "MainWindow.h" #include "MainWindow.h"
#include "AppController.h" #include "AppController.h"
#include "GRPCServerWorker.h" #include "GRPCServerWorker.h"
#include <bridgepp/BridgeUtils.h>
#include <bridgepp/Exception/Exception.h> #include <bridgepp/Exception/Exception.h>
#include <bridgepp/Worker/Overseer.h> #include <bridgepp/Worker/Overseer.h>
@ -58,35 +57,30 @@ int main(int argc, char **argv)
QApplication::setOrganizationDomain("proton.ch"); QApplication::setOrganizationDomain("proton.ch");
QApplication::setQuitOnLastWindowClosed(true); QApplication::setQuitOnLastWindowClosed(true);
app().log().setEchoInConsole(true); Log& log = app().log();
app().log().info(QString("%1 started.").arg(applicationName)); log.setEchoInConsole(true);
log.info(QString("%1 started.").arg(applicationName));
MainWindow window(nullptr); MainWindow window(nullptr);
app().setMainWindow(&window); app().setMainWindow(&window);
window.setWindowTitle(QApplication::applicationName()); window.setWindowTitle(QApplication::applicationName());
window.show(); window.show();
GRPCServerWorker *serverWorker = new GRPCServerWorker(nullptr); auto *serverWorker = new GRPCServerWorker(nullptr);
QObject::connect(serverWorker, &Worker::started, []() { app().log().info("Server worker started."); }); 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::finished, []() { app().log().info("Server worker finished."); });
QObject::connect(serverWorker, &Worker::error, [](QString const &message) { QObject::connect(serverWorker, &Worker::error, [&](QString const &message) { app().log().error(message); qApp->exit(EXIT_FAILURE); });
throw Exception(QString("gRPC Server encountered an error: %1").arg(message));
});
UPOverseer overseer = std::make_unique<Overseer>(serverWorker, nullptr); UPOverseer overseer = std::make_unique<Overseer>(serverWorker, nullptr);
overseer->startWorker(true); overseer->startWorker(true);
qint32 const exitCode = QApplication::exec(); qint32 const exitCode = QApplication::exec();
serverWorker->stop(); serverWorker->stop();
while (!overseer->isFinished()) if (!overseer->wait(5000))
{ log.warn("gRPC server took too long to finish.");
QThread::msleep(10);
}
app().log().info(QString("%1 exiting with code %2.").arg(applicationName).arg(exitCode)); app().log().info(QString("%1 exiting with code %2.").arg(applicationName).arg(exitCode));
return exitCode; return exitCode;
} }
catch (Exception const &e) catch (Exception const &e)
{ {

View File

@ -18,9 +18,9 @@
#include "AppController.h" #include "AppController.h"
#include "QMLBackend.h" #include "QMLBackend.h"
#include "BridgeMonitor.h"
#include <bridgepp/GRPC/GRPCClient.h> #include <bridgepp/GRPC/GRPCClient.h>
#include <bridgepp/Exception/Exception.h> #include <bridgepp/Exception/Exception.h>
#include <bridgepp/ProcessMonitor.h>
#include <bridgepp/Log/Log.h> #include <bridgepp/Log/Log.h>
@ -51,14 +51,14 @@ AppController::AppController()
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// \return The bridge worker, which can be null if the application was run in 'attach' mode (-a command-line switch). /// \return The bridge worker, which can be null if the application was run in 'attach' mode (-a command-line switch).
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
BridgeMonitor *AppController::bridgeMonitor() const ProcessMonitor *AppController::bridgeMonitor() const
{ {
if (!bridgeOverseer_) if (!bridgeOverseer_)
return nullptr; return nullptr;
// null bridgeOverseer is OK, it means we run in 'attached' mode (app attached to an already runnning instance of Bridge). // null bridgeOverseer is OK, it means we run in 'attached' mode (app attached to an already runnning instance of Bridge).
// but if bridgeOverseer is not null, its attached worker must be a valid BridgeMonitor instance. // but if bridgeOverseer is not null, its attached worker must be a valid ProcessMonitor instance.
auto *monitor = dynamic_cast<BridgeMonitor*>(bridgeOverseer_->worker()); auto *monitor = dynamic_cast<ProcessMonitor*>(bridgeOverseer_->worker());
if (!monitor) if (!monitor)
throw Exception("Could not retrieve bridge monitor"); throw Exception("Could not retrieve bridge monitor");

View File

@ -21,7 +21,6 @@
class QMLBackend; class QMLBackend;
class BridgeMonitor;
namespace bridgepp namespace bridgepp
@ -29,6 +28,7 @@ namespace bridgepp
class Log; class Log;
class Overseer; class Overseer;
class GRPCClient; class GRPCClient;
class ProcessMonitor;
} }
@ -50,7 +50,7 @@ public: // member functions.
bridgepp::GRPCClient& grpc() { return *grpc_; } ///< Return a reference to the GRPC client. bridgepp::GRPCClient& grpc() { return *grpc_; } ///< Return a reference to the GRPC client.
bridgepp::Log& log() { return *log_; } ///< Return a reference to the log. bridgepp::Log& log() { return *log_; } ///< Return a reference to the log.
std::unique_ptr<bridgepp::Overseer>& bridgeOverseer() { return bridgeOverseer_; }; ///< Returns a reference the bridge overseer std::unique_ptr<bridgepp::Overseer>& bridgeOverseer() { return bridgeOverseer_; }; ///< Returns a reference the bridge overseer
BridgeMonitor* bridgeMonitor() const; ///< Return the bridge worker. bridgepp::ProcessMonitor* bridgeMonitor() const; ///< Return the bridge worker.
private: // member functions private: // member functions
AppController(); ///< Default constructor. AppController(); ///< Default constructor.

View File

@ -82,7 +82,6 @@ endif()
add_executable(bridge-gui add_executable(bridge-gui
Resources.qrc Resources.qrc
AppController.cpp AppController.h AppController.cpp AppController.h
BridgeMonitor.cpp BridgeMonitor.h
EventStreamWorker.cpp EventStreamWorker.h EventStreamWorker.cpp EventStreamWorker.h
main.cpp main.cpp
Pch.h Pch.h

View File

@ -31,8 +31,8 @@ using namespace bridgepp;
EventStreamReader::EventStreamReader(QObject *parent) EventStreamReader::EventStreamReader(QObject *parent)
: Worker(parent) : Worker(parent)
{ {
connect(this, &EventStreamReader::started, [&]() { app().log().debug("EventStreamReader started");}); connect(this, &EventStreamReader::started, [&]() { app().log().debug("EventStreamReader started"); });
connect(this, &EventStreamReader::finished, [&]() { app().log().debug("EventStreamReader finished");}); connect(this, &EventStreamReader::finished, [&]() { app().log().debug("EventStreamReader finished"); });
connect(this, &EventStreamReader::error, &app().log(), &Log::error); connect(this, &EventStreamReader::error, &app().log(), &Log::error);
} }

View File

@ -73,6 +73,18 @@ void QMLBackend::init()
this->retrieveUserList(); this->retrieveUserList();
} }
//****************************************************************************************************************************************************
/// \param timeoutMs The timeout after which the function should return false if the event stream reader is not finished. if -1 one, the function
/// never times out.
/// \return false if and only if the timeout delay was reached.
//****************************************************************************************************************************************************
bool QMLBackend::waitForEventStreamReaderToFinish(qint32 timeoutMs)
{
return eventStreamOverseer_->wait(timeoutMs);
}
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
// //
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
@ -87,6 +99,7 @@ void QMLBackend::connectGrpcEvents()
connect(client, &GRPCClient::reportBugFinished, this, &QMLBackend::reportBugFinished); connect(client, &GRPCClient::reportBugFinished, this, &QMLBackend::reportBugFinished);
connect(client, &GRPCClient::reportBugSuccess, this, &QMLBackend::bugReportSendSuccess); connect(client, &GRPCClient::reportBugSuccess, this, &QMLBackend::bugReportSendSuccess);
connect(client, &GRPCClient::reportBugError, this, &QMLBackend::bugReportSendError); connect(client, &GRPCClient::reportBugError, this, &QMLBackend::bugReportSendError);
connect(client, &GRPCClient::showMainWindow, this, &QMLBackend::showMainWindow);
// cache events // cache events
connect(client, &GRPCClient::isCacheOnDiskEnabledChanged, this, &QMLBackend::isDiskCacheEnabledChanged); connect(client, &GRPCClient::isCacheOnDiskEnabledChanged, this, &QMLBackend::isDiskCacheEnabledChanged);
@ -168,15 +181,6 @@ void QMLBackend::retrieveUserList()
} }
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void QMLBackend::clearUserList()
{
users_->reset();
}
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
// //
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
@ -348,4 +352,4 @@ void QMLBackend::onResetFinished()
{ {
emit resetFinished(); emit resetFinished();
this->restart(); this->restart();
} }

View File

@ -42,7 +42,7 @@ public: // member functions.
QMLBackend &operator=(QMLBackend const &) = delete; ///< Disabled assignment operator. QMLBackend &operator=(QMLBackend const &) = delete; ///< Disabled assignment operator.
QMLBackend &operator=(QMLBackend &&) = delete; ///< Disabled move assignment operator. QMLBackend &operator=(QMLBackend &&) = delete; ///< Disabled move assignment operator.
void init(); ///< Initialize the backend. void init(); ///< Initialize the backend.
void clearUserList(); ///< Clear the user list. bool waitForEventStreamReaderToFinish(qint32 timeoutMs); ///< Wait for the event stream reader to finish.
// invokable methods can be called from QML. They generally return a value, which slots cannot do. // invokable methods can be called from QML. They generally return a value, which slots cannot do.
Q_INVOKABLE static QPoint getCursorPos(); // _ func() *core.QPoint `slot:"getCursorPos"` Q_INVOKABLE static QPoint getCursorPos(); // _ func() *core.QPoint `slot:"getCursorPos"`

View File

@ -91,6 +91,7 @@ void UserList::reset()
this->beginResetModel(); this->beginResetModel();
users_.clear(); users_.clear();
this->endResetModel(); this->endResetModel();
emit countChanged(0);
} }
@ -102,6 +103,7 @@ void UserList::reset(QList<SPUser> const &users)
this->beginResetModel(); this->beginResetModel();
users_ = users; users_ = users;
this->endResetModel(); this->endResetModel();
emit countChanged(users_.size());
} }
@ -114,6 +116,7 @@ void UserList::appendUser(SPUser const &user)
this->beginInsertRows(QModelIndex(), size, size); this->beginInsertRows(QModelIndex(), size, size);
users_.append(user); users_.append(user);
this->endInsertRows(); this->endInsertRows();
emit countChanged(users_.size());
} }
@ -127,6 +130,7 @@ void UserList::removeUserAt(int row)
this->beginRemoveRows(QModelIndex(), row, row); this->beginRemoveRows(QModelIndex(), row, row);
users_.removeAt(row); users_.removeAt(row);
this->endRemoveRows(); this->endRemoveRows();
emit countChanged(users_.size());
} }

View File

@ -19,6 +19,6 @@
#ifndef BRIDGE_GUI_VERSION_H #ifndef BRIDGE_GUI_VERSION_H
#define BRIDGE_GUI_VERSION_H #define BRIDGE_GUI_VERSION_H
#define PROJECT_VER "2.2.1+git" #define PROJECT_VER "2.2.2+git"
#endif // BRIDGE_GUI_VERSION_H #endif // BRIDGE_GUI_VERSION_H

View File

@ -17,11 +17,11 @@
#include "QMLBackend.h" #include "QMLBackend.h"
#include "BridgeMonitor.h"
#include "Version.h" #include "Version.h"
#include <bridgepp/Log/Log.h> #include <bridgepp/Log/Log.h>
#include <bridgepp/BridgeUtils.h> #include <bridgepp/BridgeUtils.h>
#include <bridgepp/Exception/Exception.h> #include <bridgepp/Exception/Exception.h>
#include <bridgepp/ProcessMonitor.h>
using namespace bridgepp; using namespace bridgepp;
@ -29,11 +29,30 @@ using namespace bridgepp;
namespace namespace
{ {
/// \brief The file extension for the bridge executable file.
#ifdef Q_OS_WIN32
QString const exeSuffix = ".exe";
#else
QString const exeSuffix;
#endif
QString const launcherFlag = "--launcher"; ///< launcher flag parameter used for bridge. QString const launcherFlag = "--launcher"; ///< launcher flag parameter used for bridge.
QString const bridgeLock = "bridge-gui.lock"; ///< file name used for the lock file. QString const bridgeLock = "bridge-gui.lock"; ///< file name used for the lock file.
QString const exeName = "proton-bridge" + exeSuffix; ///< The bridge executable file name.*
} }
//****************************************************************************************************************************************************
/// \return The path of the bridge executable.
/// \return A null string if the executable could not be located.
//****************************************************************************************************************************************************
QString locateBridgeExe()
{
QFileInfo const fileInfo(QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(exeName));
return (fileInfo.exists() && fileInfo.isFile() && fileInfo.isExecutable()) ? fileInfo.absoluteFilePath() : QString();
}
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// // initialize the Qt application. /// // initialize the Qt application.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
@ -93,7 +112,7 @@ QQmlComponent *createRootQmlComponent(QQmlApplicationEngine &engine)
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// \param[in] lock The lock file to be checked. /// \param[in] lock The lock file to be checked.
/// \return True if the lock can be taken, false otherwize. /// \return True if the lock can be taken, false otherwise.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
bool checkSingleInstance(QLockFile &lock) bool checkSingleInstance(QLockFile &lock)
{ {
@ -168,14 +187,14 @@ void launchBridge(QStringList const &args)
UPOverseer& overseer = app().bridgeOverseer(); UPOverseer& overseer = app().bridgeOverseer();
overseer.reset(); overseer.reset();
const QString bridgeExePath = BridgeMonitor::locateBridgeExe(); const QString bridgeExePath = locateBridgeExe();
if (bridgeExePath.isEmpty()) if (bridgeExePath.isEmpty())
throw Exception("Could not locate the bridge executable path"); throw Exception("Could not locate the bridge executable path");
else else
app().log().debug(QString("Bridge executable path: %1").arg(QDir::toNativeSeparators(bridgeExePath))); app().log().debug(QString("Bridge executable path: %1").arg(QDir::toNativeSeparators(bridgeExePath)));
overseer = std::make_unique<Overseer>(new BridgeMonitor(bridgeExePath, args, nullptr), nullptr); overseer = std::make_unique<Overseer>(new ProcessMonitor(bridgeExePath, args, nullptr), nullptr);
overseer->startWorker(true); overseer->startWorker(true);
} }
@ -234,16 +253,16 @@ int main(int argc, char *argv[])
if (!rootObject) if (!rootObject)
throw Exception("Could not create root object."); throw Exception("Could not create root object.");
BridgeMonitor *bridgeMonitor = app().bridgeMonitor(); ProcessMonitor *bridgeMonitor = app().bridgeMonitor();
bool bridgeExited = false; bool bridgeExited = false;
bool startError = false; bool startError = false;
QMetaObject::Connection connection; QMetaObject::Connection connection;
if (bridgeMonitor) if (bridgeMonitor)
{ {
const BridgeMonitor::MonitorStatus& status = bridgeMonitor->getStatus(); const ProcessMonitor::MonitorStatus& status = bridgeMonitor->getStatus();
if (!status.running && !attach) if (!status.running && !attach)
{ {
// BridgeMonitor already stopped meaning we are attached to an orphan Bridge. // ProcessMonitor already stopped meaning we are attached to an orphan Bridge.
// Restart the full process to be sure there is no more bridge orphans // Restart the full process to be sure there is no more bridge orphans
app().log().error("Found orphan bridge, need to restart."); app().log().error("Found orphan bridge, need to restart.");
app().backend().forceLauncher(launcher); app().backend().forceLauncher(launcher);
@ -254,7 +273,7 @@ int main(int argc, char *argv[])
else else
{ {
app().log().debug(QString("Monitoring Bridge PID : %1").arg(status.pid)); app().log().debug(QString("Monitoring Bridge PID : %1").arg(status.pid));
connection = QObject::connect(bridgeMonitor, &BridgeMonitor::processExited, [&](int returnCode) { connection = QObject::connect(bridgeMonitor, &ProcessMonitor::processExited, [&](int returnCode) {
bridgeExited = true;// clazy:exclude=lambda-in-connect bridgeExited = true;// clazy:exclude=lambda-in-connect
qGuiApp->exit(returnCode); qGuiApp->exit(returnCode);
}); });
@ -267,6 +286,8 @@ int main(int argc, char *argv[])
QObject::disconnect(connection); QObject::disconnect(connection);
app().grpc().stopEventStream(); app().grpc().stopEventStream();
if (!app().backend().waitForEventStreamReaderToFinish(5000))
log.warn("Event stream reader took too long to finish.");
// We manually delete the QML components to avoid warnings error due to order of deletion of C++ / JS objects and singletons. // We manually delete the QML components to avoid warnings error due to order of deletion of C++ / JS objects and singletons.
rootObject.reset(); rootObject.reset();
@ -280,7 +301,7 @@ int main(int argc, char *argv[])
} }
catch (Exception const &e) catch (Exception const &e)
{ {
app().log().error(e.qwhat()); QTextStream(stderr) << e.qwhat() << "\n";
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }

View File

@ -258,7 +258,7 @@ Item {
signIn.username = this.user.username signIn.username = this.user.username
rightContent.showSignIn() rightContent.showSignIn()
} }
onShowSetupGuide: { onShowSetupGuide: function(user, address) {
root.showSetupGuide(user,address) root.showSetupGuide(user,address)
} }
} }

View File

@ -45,7 +45,6 @@ SettingsView {
type: Label.Body type: Label.Body
color: root.colorScheme.text_weak color: root.colorScheme.text_weak
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumWidth: this.parent.Layout.maximumWidth
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }

View File

@ -52,7 +52,7 @@ ApplicationWindow {
target: Backend.users target: Backend.users
function onRowsInserted(parent, first, last) { function onRowsInserted(parent, first, last) {
// considerring that users are added one-by-one // considering that users are added one-by-one
var user = Backend.users.get(first) var user = Backend.users.get(first)
if (!user.loggedIn) { if (!user.loggedIn) {
@ -130,7 +130,7 @@ ApplicationWindow {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
onShowSetupGuide: { onShowSetupGuide: function(user, address) {
root.showSetup(user,address) root.showSetup(user,address)
} }
} }

View File

@ -40,7 +40,6 @@ SettingsView {
type: Label.Body type: Label.Body
color: root.colorScheme.text_weak color: root.colorScheme.text_weak
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumWidth: this.parent.Layout.maximumWidth
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }

View File

@ -108,6 +108,7 @@ add_library(bridgepp
bridgepp/GRPC/GRPCUtils.cpp bridgepp/GRPC/GRPCUtils.h bridgepp/GRPC/GRPCUtils.cpp bridgepp/GRPC/GRPCUtils.h
${PROTO_CPP_FILE} ${PROTO_H_FILE} ${GRPC_CPP_FILE} ${GRPC_H_FILE} ${PROTO_CPP_FILE} ${PROTO_H_FILE} ${GRPC_CPP_FILE} ${GRPC_H_FILE}
bridgepp/Log/Log.h bridgepp/Log/Log.cpp bridgepp/Log/Log.h bridgepp/Log/Log.cpp
bridgepp/ProcessMonitor.cpp bridgepp/ProcessMonitor.h
bridgepp/User/User.cpp bridgepp/User/User.h bridgepp/User/User.cpp bridgepp/User/User.h
bridgepp/Worker/Worker.h bridgepp/Worker/Overseer.h bridgepp/Worker/Overseer.cpp bridgepp/Worker/Worker.h bridgepp/Worker/Overseer.h bridgepp/Worker/Overseer.cpp
) )

View File

@ -16,14 +16,15 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>. // along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_BRIDGE_UTILS_H #ifndef BRIDGE_PP_TESTER_BRIDGE_UTILS_H
#define BRIDGE_GUI_TESTER_BRIDGE_UTILS_H #define BRIDGE_PP_TESTER_BRIDGE_UTILS_H
#include <bridgepp/User/User.h> #include <bridgepp/User/User.h>
namespace bridgepp { namespace bridgepp
{
QString userConfigDir(); ///< Get the path of the user configuration folder. QString userConfigDir(); ///< Get the path of the user configuration folder.
@ -34,8 +35,8 @@ QString randomFirstName(); ///< Get a random first name from a pre-determined li
QString randomLastName(); ///< 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. SPUser randomUser(); ///< Get a random user.
} // namespace } // namespace
#endif // BRIDGE_PP_TESTER_BRIDGE_UTILS_H
#endif // BRIDGE_GUI_TESTER_BRIDGE_UTILS_H

View File

@ -16,8 +16,8 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>. // along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_EVENT_FACTORY_H #ifndef BRIDGE_PP_EVENT_FACTORY_H
#define BRIDGE_GUI_TESTER_EVENT_FACTORY_H #define BRIDGE_PP_EVENT_FACTORY_H
#include "bridge.grpc.pb.h" #include "bridge.grpc.pb.h"
@ -85,4 +85,4 @@ SPStreamEvent newUserChangedEvent(QString const &userID); ///< Create a new User
} // namespace bridgepp } // namespace bridgepp
#endif //BRIDGE_GUI_TESTER_EVENT_FACTORY_H #endif //BRIDGE_PP_EVENT_FACTORY_H

View File

@ -16,8 +16,8 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>. // along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_RPC_CLIENT_H #ifndef BRIDGE_PP_RPC_CLIENT_H
#define BRIDGE_GUI_RPC_CLIENT_H #define BRIDGE_PP_RPC_CLIENT_H
#include "../User/User.h" #include "../User/User.h"
@ -233,4 +233,4 @@ private: // data members.
} }
#endif // BRIDGE_GUI_RPC_CLIENT_H #endif // BRIDGE_PP_RPC_CLIENT_H

View File

@ -16,8 +16,8 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>. // along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_GRPC_UTILS_H #ifndef BRIDGE_PP_GRPC_UTILS_H
#define BRIDGE_GUI_GRPC_UTILS_H #define BRIDGE_PP_GRPC_UTILS_H
#include "../User/User.h" #include "../User/User.h"
@ -42,4 +42,4 @@ SPUser userFromGRPC(grpc::User const &grpcUser); ///< Create a bridgepp::User fr
} }
#endif // BRIDGE_GUI_GRPC_UTILS_H #endif // BRIDGE_PP_GRPC_UTILS_H

View File

@ -16,62 +16,35 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>. // along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "BridgeMonitor.h" #include "ProcessMonitor.h"
#include <bridgepp/Exception/Exception.h> #include "Exception/Exception.h"
using namespace bridgepp; namespace bridgepp
namespace
{ {
/// \brief The file extension for the bridge executable file.
#ifdef Q_OS_WIN32
QString const exeSuffix = ".exe";
#else
QString const exeSuffix;
#endif
QString const exeName = "proton-bridge" + exeSuffix; ///< The bridge executable file name.*
}
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// \return The path of the bridge executable. /// \param[in] exePath The path of the executable.
/// \return A null string if the executable could not be located.
//****************************************************************************************************************************************************
QString BridgeMonitor::locateBridgeExe()
{
QFileInfo const fileInfo(QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(exeName));
return (fileInfo.exists() && fileInfo.isFile() && fileInfo.isExecutable()) ? fileInfo.absoluteFilePath() : QString();
}
//****************************************************************************************************************************************************
/// \param[in] exePath The path of the Bridge executable.
/// \param[in] parent The parent object of the worker. /// \param[in] parent The parent object of the worker.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
BridgeMonitor::BridgeMonitor(QString const &exePath, QStringList const &args, QObject *parent) ProcessMonitor::ProcessMonitor(QString const &exePath, QStringList const &args, QObject *parent)
: Worker(parent) : Worker(parent)
, exePath_(exePath) , exePath_(exePath)
, args_(args) , args_(args)
{ {
QFileInfo fileInfo(exePath); QFileInfo fileInfo(exePath);
if (!fileInfo.exists()) if (!fileInfo.exists())
throw Exception("Could not locate Bridge executable."); throw Exception(QString("Could not locate %1 executable.").arg(fileInfo.baseName()));
if ((!fileInfo.isFile()) || (!fileInfo.isExecutable())) if ((!fileInfo.isFile()) || (!fileInfo.isExecutable()))
throw Exception("Invalid bridge executable"); throw Exception(QString("Invalid %1 executable").arg(fileInfo.baseName()));
} }
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
// //
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
void BridgeMonitor::run() void ProcessMonitor::run()
{ {
try try
{ {
@ -92,9 +65,9 @@ void BridgeMonitor::run()
} }
status_.running = false; status_.running = false;
status_.returnCode = p.exitCode(); status_.returnCode = p.exitCode();
emit processExited(status_.returnCode ); emit processExited(status_.returnCode);
emit finished(); emit finished();
} }
catch (Exception const &e) catch (Exception const &e)
@ -103,10 +76,14 @@ void BridgeMonitor::run()
} }
} }
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// \return status of the monitored process /// \return status of the monitored process
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
const BridgeMonitor::MonitorStatus& BridgeMonitor::getStatus() const ProcessMonitor::MonitorStatus &ProcessMonitor::getStatus()
{ {
return status_; return status_;
} }
} // namespace bridgepp

View File

@ -16,48 +16,52 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>. // along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_PP_PROCESS_MONITOR_H
#ifndef BRIDGE_GUI_BRIDGE_MONITOR_H #define BRIDGE_PP_PROCESS_MONITOR_H
#define BRIDGE_GUI_BRIDGE_MONITOR_H
#include <bridgepp/Worker/Worker.h> #include "Worker/Worker.h"
//********************************************************************************************************************** namespace bridgepp
/// \brief Bridge process launcher and monitor class.
//**********************************************************************************************************************
class BridgeMonitor: public bridgepp::Worker
{ {
Q_OBJECT
public: // static member functions
static QString locateBridgeExe(); ///< Try to find the bridge executable path.
struct MonitorStatus {
//**********************************************************************************************************************
/// \brief Process launcher and monitor class.
//**********************************************************************************************************************
class ProcessMonitor : public Worker
{
Q_OBJECT
public: // static member functions
struct MonitorStatus
{
bool running = false; bool running = false;
int returnCode = 0; int returnCode = 0;
qint64 pid = 0; qint64 pid = 0;
}; };
public: // member functions. public: // member functions.
BridgeMonitor(QString const& exePath, QStringList const &args, QObject *parent); ///< Default constructor. ProcessMonitor(QString const &exePath, QStringList const &args, QObject *parent); ///< Default constructor.
BridgeMonitor(BridgeMonitor const&) = delete; ///< Disabled copy-constructor. ProcessMonitor(ProcessMonitor const &) = delete; ///< Disabled copy-constructor.
BridgeMonitor(BridgeMonitor&&) = delete; ///< Disabled assignment copy-constructor. ProcessMonitor(ProcessMonitor &&) = delete; ///< Disabled assignment copy-constructor.
~BridgeMonitor() override = default; ///< Destructor. ~ProcessMonitor() override = default; ///< Destructor.
BridgeMonitor& operator=(BridgeMonitor const&) = delete; ///< Disabled assignment operator. ProcessMonitor &operator=(ProcessMonitor const &) = delete; ///< Disabled assignment operator.
BridgeMonitor& operator=(BridgeMonitor&&) = delete; ///< Disabled move assignment operator. ProcessMonitor &operator=(ProcessMonitor &&) = delete; ///< Disabled move assignment operator.
void run() override; ///< Run the worker. void run() override; ///< Run the worker.
MonitorStatus const &getStatus();
const MonitorStatus& getStatus();
signals: signals:
void processExited(int code); ///< Slot for the exiting of the process. void processExited(int code); ///< Slot for the exiting of the process.
private: // data members private: // data members
QString const exePath_; ///< The path to the bridge executable. QString const exePath_; ///< The path to the executable.
QStringList args_; ///< arguments to be passed to the brigde. QStringList args_; ///< arguments to be passed to the brigde.
MonitorStatus status_; ///< Status of the monitoring. MonitorStatus status_; ///< Status of the monitoring.
}; };
}
#endif // BRIDGE_GUI_BRIDGE_MONITOR_H
#endif //BRIDGE_PP_PROCESS_MONITOR_H

View File

@ -16,8 +16,8 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>. // along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_USER_H #ifndef BRIDGE_PP_USER_H
#define BRIDGE_GUI_USER_H #define BRIDGE_PP_USER_H
namespace bridgepp namespace bridgepp
@ -127,4 +127,4 @@ private: // data members.
} // namespace bridgepp } // namespace bridgepp
#endif // BRIDGE_GUI_USER_H #endif // BRIDGE_PP_USER_H

View File

@ -108,6 +108,26 @@ bool Overseer::isFinished() const
} }
//****************************************************************************************************************************************************
/// \param timeoutMs The timeout after which the function should return false if the event stream reader is not finished. if -1 one, the function
/// never times out.
/// \return false if and only if the timeout delay was reached.
//****************************************************************************************************************************************************
bool Overseer::wait(qint32 timeoutMs) const
{
QElapsedTimer timer;
timer.start();
while (!this->isFinished()) {
if ((timeoutMs >= 0) && (timer.elapsed() > timeoutMs))
return false;
QThread::msleep(10);
}
return true;
}
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// \return The worker. /// \return The worker.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************

View File

@ -41,6 +41,7 @@ public: // member functions.
Overseer &operator=(Overseer const &) = delete; ///< Disabled assignment operator. Overseer &operator=(Overseer const &) = delete; ///< Disabled assignment operator.
Overseer &operator=(Overseer &&) = delete; ///< Disabled move assignment operator. Overseer &operator=(Overseer &&) = delete; ///< Disabled move assignment operator.
bool isFinished() const; ///< Check if the worker is finished. bool isFinished() const; ///< Check if the worker is finished.
bool wait(qint32 timeoutMs) const; ///< Wait for the worker to finish.
Worker *worker() const; ///< Return worker. Worker *worker() const; ///< Return worker.
public slots: public slots:

View File

@ -64,7 +64,7 @@ func grpcUserFromBridge(user types.User) *User {
Username: user.Username(), Username: user.Username(),
AvatarText: getInitials(user.Username()), AvatarText: getInitials(user.Username()),
LoggedIn: user.IsConnected(), LoggedIn: user.IsConnected(),
SplitMode: user.IsCombinedAddressMode(), SplitMode: !user.IsCombinedAddressMode(),
SetupGuideSeen: true, // users listed have already seen the setup guide. SetupGuideSeen: true, // users listed have already seen the setup guide.
UsedBytes: user.UsedBytes(), UsedBytes: user.UsedBytes(),
TotalBytes: user.TotalBytes(), TotalBytes: user.TotalBytes(),