feat(GODT-2373): introducing bridgelib Go dynamic library in bridge-gui.

This commit is contained in:
Xavier Michelon
2023-02-17 16:47:35 +01:00
parent cf8284a489
commit a741ffb595
31 changed files with 1457 additions and 1305 deletions

View File

@ -0,0 +1,225 @@
// Copyright (c) 2023 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 "BridgeLib.h"
#include <bridgepp/Exception/Exception.h>
#include <bridgepp/BridgeUtils.h>
using namespace bridgepp;
#ifdef __cplusplus
extern "C" {
#endif
typedef char *(*FuncReturningCString)();
#ifdef __cplusplus
}
#endif
namespace {
FuncReturningCString goosFunc = nullptr; ///< A pointer to the dynamically loaded GoOS function.
FuncReturningCString userCacheDirFunc = nullptr; ///< A pointer to the dynamically loaded UserCache function.
FuncReturningCString userConfigDirFunc = nullptr; ///< A pointer to the dynamically loaded UserConfig function.
FuncReturningCString userDataDirFunc = nullptr; ///< A pointer to the dynamically loaded UserData function.
void (*deleteCStringFunc)(char *) = nullptr; ///< A pointer to the deleteCString function.
#if defined(Q_OS_WINDOWS)
typedef HINSTANCE LibHandle;
#else
typedef void *LibHandle;
#endif
LibHandle loadDynamicLibrary(QString const &path); ///< Load a dynamic library.
void *getFuncPointer(LibHandle lib, QString const &funcName); ///< Retrieve a function pointer from a dynamic library.
//****************************************************************************************************************************************************
/// \return The path to the bridgelib library file.
//****************************************************************************************************************************************************
QString bridgelibPath() {
QString const path = QDir(QCoreApplication::applicationDirPath()).absoluteFilePath("bridgelib.");
switch (os()) {
case OS::Windows:
return path + "dll";
case OS::MacOS:
return path + "dylib";
case OS::Linux:
default:
return path + "so";
}
}
#if defined(Q_OS_WINDOWS)
//****************************************************************************************************************************************************
/// \param[in] path The path of the library file.
/// \return A pointer to the library object
//****************************************************************************************************************************************************
LibHandle loadDynamicLibrary(QString const &path) {
if (!QFileInfo::exists(path)) {
throw Exception(QString("The dynamic library file bridgelib.dylib could not be found at '%1'.").arg(path));
}
LibHandle handle = LoadLibrary(reinterpret_cast<LPCWSTR>(path.toStdWString().c_str()));
if (!handle) {
throw Exception(QString("The bridgelib dynamic library file '%1' could not be opened.").arg(path));
}
return handle;
}
//****************************************************************************************************************************************************
/// \param[in] lib A handle to the library
/// \param[in] funcName The name of the function.
/// \return A pointer to the function
//****************************************************************************************************************************************************
void *getFuncPointer(LibHandle lib, QString const &funcName) {
void *pointer = reinterpret_cast<void*>(GetProcAddress(lib, funcName.toLocal8Bit()));
if (!pointer)
throw Exception(QString("Could not locate function %1 in bridgelib dynamic library").arg(funcName));
return pointer;
}
#else
#include <dlfcn.h>
//****************************************************************************************************************************************************
/// \param[in] path The path of the library file.
/// \return A pointer to the library object
//****************************************************************************************************************************************************
void *loadDynamicLibrary(QString const &path) {
if (!QFileInfo::exists(path)) {
throw Exception(QString("The dynamic library file bridgelib.dylib could not be found at '%1'.").arg(path));
}
void *lib = dlopen(path.toLocal8Bit().data(), RTLD_LAZY);
if (!lib) {
throw Exception(QString("The bridgelib dynamic library file '%1' could not be opened.").arg(path));
}
return lib;
}
//****************************************************************************************************************************************************
/// \param[in] lib A handle to the library
/// \param[in] funcName The name of the function.
/// \return A pointer to the function
//****************************************************************************************************************************************************
void *getFuncPointer(LibHandle lib, QString const &funcName) {
void *pointer = dlsym(lib, funcName.toLocal8Bit());
if (!pointer) {
throw Exception(QString("Could not locate function %1 in bridgelib dynamic library").arg(funcName));
}
return pointer;
}
#endif // defined(Q_OS_WINDOWS)
}
namespace bridgelib {
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void loadLibrary() {
try {
LibHandle lib = loadDynamicLibrary(bridgelibPath());
goosFunc = reinterpret_cast<FuncReturningCString>(getFuncPointer(lib, "GoOS"));
userCacheDirFunc = reinterpret_cast<FuncReturningCString>(getFuncPointer(lib, "UserCacheDir"));
userConfigDirFunc = reinterpret_cast<FuncReturningCString>(getFuncPointer(lib, "UserConfigDir"));
userDataDirFunc = reinterpret_cast<FuncReturningCString>(getFuncPointer(lib, "UserDataDir"));
deleteCStringFunc = reinterpret_cast<void (*)(char*)>(getFuncPointer(lib, "DeleteCString"));
} catch (Exception const &e) {
throw Exception("Error loading the bridgelib dynamic library file.", e.qwhat());
}
}
//****************************************************************************************************************************************************
/// \brief Converts a C-style string returned by a go library function to a QString, and release the memory allocated for the C-style string.
/// \param[in] cString The C-style string, in UTF-8 format.
/// \return A QString.
//****************************************************************************************************************************************************
QString goToQString(char *const cString) {
if (!cString) {
return QString();
}
QString const result = QString::fromUtf8(cString);
deleteCStringFunc(cString);
return result;
}
//****************************************************************************************************************************************************
/// \return The value of the Go runtime.GOOS constant.
//****************************************************************************************************************************************************
QString goos() {
return goToQString(goosFunc());
}
//****************************************************************************************************************************************************
/// \return The path to the user cache folder.
//****************************************************************************************************************************************************
QString userCacheDir() {
return goToQString(userCacheDirFunc());
}
//****************************************************************************************************************************************************
/// \return The path to the user cache folder.
//****************************************************************************************************************************************************
QString userConfigDir() {
return goToQString(userConfigDirFunc());
}
//****************************************************************************************************************************************************
/// \return The path to the user data folder.
//****************************************************************************************************************************************************
QString userDataDir() {
return goToQString(userDataDirFunc());
}
}

View File

@ -0,0 +1,36 @@
// Copyright (c) 2023 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_BRIDGELIB_H
#define BRIDGE_GUI_BRIDGELIB_H
namespace bridgelib {
void loadLibrary();
QString goos();
QString userCacheDir();
QString userConfigDir();
QString userDataDir();
} // namespace bridgelib
#endif //BRIDGE_GUI_BRIDGELIB_H

View File

@ -86,6 +86,48 @@ message(STATUS "Using Qt ${Qt6_VERSION}")
find_package(sentry CONFIG REQUIRED)
#*****************************************************************************************************************************************************
# bridgelib
#*****************************************************************************************************************************************************
find_program(GO_BIN "go")
if (NOT GO_BIN)
message(FATAL_ERROR "Could not location go compiler")
endif()
message(STATUS "go compiler is ${GO_BIN}")
if (APPLE) # set some env variable for go compiler on macOS. Note the CGO_ENABLED=1 is required when cross-compiling.
if (CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
set(GO_BIN "MACOSX_DEPLOYMENT_TARGET=11.0" "GOARCH=arm64" "CGO_CFLAGS=\"-mmacosx-version-min=11.0\"" CGO_ENABLED=1 ${GO_BIN})
else ()
set(GO_BIN "MACOSX_DEPLOYMENT_TARGET=10.15" "GOARCH=amd64" "CGO_CFLAGS=\"-mmacosx-version-min=10.15\"" CGO_ENABLED=1 ${GO_BIN})
endif()
endif()
file(REAL_PATH "pkg/bridgelib" BRIDGELIB_DIR BASE_DIRECTORY "${BRIDGE_REPO_ROOT}")
message(STATUS "bridgelib folder is ${BRIDGELIB_DIR}")
set(BRIDGELIB_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(BRIDGELIB_BASE_NAME "bridgelib")
if (UNIX AND NOT APPLE)
set(BRIDGELIB_LIB_FILE "${BRIDGELIB_BASE_NAME}.so")
endif()
if (APPLE)
set(BRIDGELIB_OUTPUT_DIR ${BRIDGELIB_OUTPUT_DIR}/${CMAKE_PROJECT_NAME}.app/Contents/MacOS)
set(BRIDGELIB_LIB_FILE "${BRIDGELIB_BASE_NAME}.dylib")
endif()
if (WIN32)
set(BRIDGELIB_LIB_FILE "${BRIDGELIB_BASE_NAME}.dll")
endif()
set(BRIDGELIB_OUTPUT_PATH "${BRIDGELIB_OUTPUT_DIR}/${BRIDGELIB_LIB_FILE}")
add_custom_target(
bridgelib
COMMAND ${GO_BIN} build -o ${BRIDGELIB_OUTPUT_PATH} --buildmode c-shared
WORKING_DIRECTORY ${BRIDGELIB_DIR}
COMMENT "Compile bridgelib library"
)
#*****************************************************************************************************************************************************
# Source files and output
#*****************************************************************************************************************************************************
@ -110,22 +152,23 @@ add_executable(bridge-gui
Resources.qrc
AppController.cpp AppController.h
BridgeApp.cpp BridgeApp.h
BridgeLib.cpp BridgeLib.h
CommandLine.cpp CommandLine.h
EventStreamWorker.cpp EventStreamWorker.h
Log.cpp Log.h
main.cpp
Pch.h
BuildConfig.h
QMLBackend.cpp QMLBackend.h
UserList.cpp UserList.h
SentryUtils.cpp SentryUtils.h
${DOCK_ICON_SRC_FILE} MacOS/DockIcon.h
)
if (APPLE)
target_sources(bridge-gui PRIVATE MacOS/SecondInstance.mm MacOS/SecondInstance.h)
endif(APPLE)
add_dependencies(bridge-gui bridgelib)
if (WIN32) # on Windows, we add a (non-Qt) resource file that contains the application icon and version information.
string(TIMESTAMP BRIDGE_BUILD_YEAR "%Y")

View File

@ -46,6 +46,9 @@ install(DIRECTORY "${QT_DIR}/lib/QtQuickDialogs2Utils.framework"
# PLUGINS
install(FILES "${QT_DIR}/plugins/imageformats/libqsvg.dylib"
DESTINATION "${CMAKE_INSTALL_PREFIX}/bridge-gui.app/Contents/PlugIns/imageformats")
# BRIDGELIB
install(FILES "${BRIDGELIB_OUTPUT_DIR}/${BRIDGELIB_LIB_FILE}"
DESTINATION "${CMAKE_INSTALL_PREFIX}/bridge-gui.app/Contents/MacOS")
# crash handler utils
## Build

View File

@ -43,6 +43,9 @@ macro( AppendQt6Lib LIB_NAME)
AppendLib("${LIB_NAME}" "${QT_DIR}/lib/")
endmacro()
# bridgelib
install(FILES ${BRIDGELIB_OUTPUT_PATH} DESTINATION ${CMAKE_INSTALL_PREFIX})
#Qt6
AppendQt6Lib("libQt6QuickControls2.so.6")
AppendQt6Lib("libQt6Quick.so.6")

View File

@ -47,6 +47,9 @@ endmacro()
# Force plugins to be installed near the exe.
install(SCRIPT ${deploy_script})
# bridgelib
install(FILES ${BRIDGELIB_OUTPUT_PATH} DESTINATION ${CMAKE_INSTALL_PREFIX})
# Vcpkg DLLs
AppendVCPKGLib("abseil_dll.dll")
AppendVCPKGLib("cares.dll")

View File

@ -0,0 +1,66 @@
// Copyright (c) 2023 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 "Log.h"
#include "BridgeLib.h"
#include "BuildConfig.h"
using namespace bridgepp;
//****************************************************************************************************************************************************
/// \return user logs directory used by bridge.
//****************************************************************************************************************************************************
QString userLogsDir() {
QString const path = QDir(bridgelib::userDataDir()).absoluteFilePath("logs");
QDir().mkpath(path);
return path;
}
//****************************************************************************************************************************************************
/// \return A reference to the log.
//****************************************************************************************************************************************************
Log &initLog() {
Log &log = app().log();
log.registerAsQtMessageHandler();
log.setEchoInConsole(true);
// remove old gui log files
QDir const logsDir(userLogsDir());
for (QFileInfo const fileInfo: logsDir.entryInfoList({ "gui_v*.log" }, QDir::Filter::Files)) { // entryInfolist apparently only support wildcards, not regex.
QFile(fileInfo.absoluteFilePath()).remove();
}
// create new GUI log file
QString error;
if (!log.startWritingToFile(logsDir.absoluteFilePath(QString("gui_v%1_%2.log").arg(PROJECT_VER).arg(QDateTime::currentSecsSinceEpoch())), &error)) {
log.error(error);
}
log.info("bridge-gui starting");
QString const qtCompileTimeVersion = QT_VERSION_STR;
QString const qtRuntimeVersion = qVersion();
QString msg = QString("Using Qt %1").arg(qtRuntimeVersion);
if (qtRuntimeVersion != qtCompileTimeVersion) {
msg += QString(" (compiled against %1)").arg(qtCompileTimeVersion);
}
log.info(msg);
return log;
}

View File

@ -0,0 +1,29 @@
// Copyright (c) 2023 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_LOG_H
#define BRIDGE_GUI_LOG_H
#include <bridgepp/Log/Log.h>
bridgepp::Log &initLog(); ///< Initialize the application log.
#endif //BRIDGE_GUI_LOG_H

View File

@ -17,8 +17,9 @@
#include "QMLBackend.h"
#include "EventStreamWorker.h"
#include "BuildConfig.h"
#include "BridgeLib.h"
#include "EventStreamWorker.h"
#include <bridgepp/GRPC/GRPCClient.h>
#include <bridgepp/Exception/Exception.h>
#include <bridgepp/Worker/Overseer.h>
@ -56,7 +57,7 @@ void QMLBackend::init(GRPCConfig const &serviceConfig) {
app().grpc().setLog(&log);
this->connectGrpcEvents();
app().grpc().connectToServer(serviceConfig, app().bridgeMonitor());
app().grpc().connectToServer(bridgelib::userConfigDir(), serviceConfig, app().bridgeMonitor());
app().log().info("Connected to backend via gRPC service.");
QString bridgeVer;
@ -73,7 +74,6 @@ void QMLBackend::init(GRPCConfig const &serviceConfig) {
});
// Grab from bridge the value that will not change during the execution of this app (or that will only change locally).
app().grpc().goos(goos_);
app().grpc().logsPath(logsPath_);
app().grpc().licensePath(licensePath_);
bool sslForIMAP = false, sslForSMTP = false;
@ -151,6 +151,16 @@ bool QMLBackend::areSameFileOrFolder(QUrl const &lhs, QUrl const &rhs) const {
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
QString QMLBackend::goOS() {
HANDLE_EXCEPTION_RETURN_QSTRING(
return bridgelib::goos();
)
}
//****************************************************************************************************************************************************
/// \return The value for the 'showOnStartup' property.
//****************************************************************************************************************************************************
@ -186,16 +196,6 @@ bool QMLBackend::showSplashScreen() const {
}
//****************************************************************************************************************************************************
/// \return The value for the 'GOOS' property.
//****************************************************************************************************************************************************
QString QMLBackend::goos() const {
HANDLE_EXCEPTION_RETURN_QSTRING(
return goos_;
)
}
//****************************************************************************************************************************************************
/// \return The value for the 'logsPath' property.
//****************************************************************************************************************************************************

View File

@ -50,11 +50,11 @@ public: // member functions.
Q_INVOKABLE bool isPortFree(int port) const; ///< Check if a given network port is available.
Q_INVOKABLE QString nativePath(QUrl const &url) const; ///< Retrieve the native path of a local URL.
Q_INVOKABLE bool areSameFileOrFolder(QUrl const &lhs, QUrl const &rhs) const; ///< Check if two local URL point to the same file.
Q_INVOKABLE QString goOS(); ///< Return the OS string from golang's runtime.GOOS.
public: // Qt/QML properties. Note that the NOTIFY-er signal is required even for read-only properties (QML warning otherwise)
Q_PROPERTY(bool showOnStartup READ showOnStartup NOTIFY showOnStartupChanged)
Q_PROPERTY(bool showSplashScreen READ showSplashScreen WRITE setShowSplashScreen NOTIFY showSplashScreenChanged)
Q_PROPERTY(QString goos READ goos NOTIFY goosChanged)
Q_PROPERTY(QUrl logsPath READ logsPath NOTIFY logsPathChanged)
Q_PROPERTY(QUrl licensePath READ licensePath NOTIFY licensePathChanged)
Q_PROPERTY(QUrl releaseNotesLink READ releaseNotesLink NOTIFY releaseNotesLinkChanged)
@ -86,7 +86,6 @@ public: // Qt/QML properties. Note that the NOTIFY-er signal is required even fo
bool showOnStartup() const; ///< Getter for the 'showOnStartup' property.
void setShowSplashScreen(bool show); ///< Setter for the 'showSplashScreen' property.
bool showSplashScreen() const; ///< Getter for the 'showSplashScreen' property.
QString goos() const; ///< Getter for the 'GOOS' property.
QUrl logsPath() const; ///< Getter for the 'logsPath' property.
QUrl licensePath() const; ///< Getter for the 'licensePath' property.
QUrl releaseNotesLink() const;///< Getter for the 'releaseNotesLink' property.
@ -120,7 +119,6 @@ public: // Qt/QML properties. Note that the NOTIFY-er signal is required even fo
signals: // Signal used by the Qt property system. Many of them are unused but required to avoid warning from the QML engine.
void showSplashScreenChanged(bool value); ///<Signal for the change of the 'showSplashScreen' property.
void showOnStartupChanged(bool value); ///<Signal for the change of the 'showOnStartup' property.
void goosChanged(QString const &value); ///<Signal for the change of the 'GOOS' property.
void diskCachePathChanged(QUrl const &url); ///<Signal for the change of the 'diskCachePath' property.
void imapPortChanged(int port); ///<Signal for the change of the 'imapPort' property.
void smtpPortChanged(int port); ///<Signal for the change of the 'smtpPort' property.
@ -247,7 +245,6 @@ private: // data members
UserList *users_ { nullptr }; ///< The user list. Owned by backend.
std::unique_ptr<bridgepp::Overseer> eventStreamOverseer_; ///< The event stream overseer.
bool showSplashScreen_ { false }; ///< The cached version of show splash screen. Retrieved on startup from bridge, and potentially modified locally.
QString goos_; ///< The cached version of the GOOS variable.
QUrl logsPath_; ///< The logs path. Retrieved from bridge on startup.
QUrl licensePath_; ///< The license path. Retrieved from bridge on startup.
int imapPort_ { 0 }; ///< The cached value for the IMAP port.

View File

@ -17,36 +17,82 @@
#include "SentryUtils.h"
#include "BuildConfig.h"
#include "BridgeLib.h"
#include <bridgepp/BridgeUtils.h>
#include <QByteArray>
#include <QCryptographicHash>
#include <QString>
#include <QSysInfo>
using namespace bridgepp;
static constexpr const char *LoggerName = "bridge-gui";
//****************************************************************************************************************************************************
/// \brief Get the path of the sentry cache folder.
///
/// \return sentry cache directory used by bridge.
//****************************************************************************************************************************************************
QString sentryCacheDir() {
QString const path = QDir(bridgelib::userDataDir()).absoluteFilePath("sentry_cache");
QDir().mkpath(path);
return path;
}
//****************************************************************************************************************************************************
/// \brief Get a hash of the computer's host name
//****************************************************************************************************************************************************
QByteArray getProtectedHostname() {
QByteArray hostname = QCryptographicHash::hash(QSysInfo::machineHostName().toUtf8(), QCryptographicHash::Sha256);
return hostname.toHex();
}
//****************************************************************************************************************************************************
/// \return The OS String used by sentry
//****************************************************************************************************************************************************
QString getApiOS() {
#if defined(Q_OS_DARWIN)
return "macos";
#elif defined(Q_OS_WINDOWS)
return "windows";
#else
return "linux";
#endif
switch (os()) {
case OS::MacOS:
return "macos";
case OS::Windows:
return "windows";
case OS::Linux:
default:
return "linux";
}
}
//****************************************************************************************************************************************************
/// \return The application version number.
//****************************************************************************************************************************************************
QString appVersion(const QString& version) {
return QString("%1-bridge@%2").arg(getApiOS()).arg(version);
return QString("%1-bridge@%2").arg(getApiOS(), version);
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void initSentry() {
sentry_options_t *sentryOptions = newSentryOptions(PROJECT_DSN_SENTRY, sentryCacheDir().toStdString().c_str());
if (!QString(PROJECT_CRASHPAD_HANDLER_PATH).isEmpty())
sentry_options_set_handler_path(sentryOptions, PROJECT_CRASHPAD_HANDLER_PATH);
if (sentry_init(sentryOptions) != 0) {
QTextStream(stderr) << "Failed to initialize sentry\n";
}
setSentryReportScope();
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void setSentryReportScope() {
sentry_set_tag("OS", bridgepp::goos().toUtf8());
sentry_set_tag("OS", bridgelib::goos().toUtf8());
sentry_set_tag("Client", PROJECT_FULL_NAME);
sentry_set_tag("Version", PROJECT_REVISION);
sentry_set_tag("HostArch", QSysInfo::currentCpuArchitecture().toUtf8());
@ -56,6 +102,10 @@ void setSentryReportScope() {
sentry_set_user(user);
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
sentry_options_t* newSentryOptions(const char *sentryDNS, const char *cacheDir) {
sentry_options_t *sentryOptions = sentry_options_new();
sentry_options_set_dsn(sentryOptions, sentryDNS);
@ -69,12 +119,19 @@ sentry_options_t* newSentryOptions(const char *sentryDNS, const char *cacheDir)
return sentryOptions;
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
sentry_uuid_t reportSentryEvent(sentry_level_t level, const char *message) {
auto event = sentry_value_new_message_event(level, LoggerName, message);
return sentry_capture_event(event);
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
sentry_uuid_t reportSentryException(sentry_level_t level, const char *message, const char *exceptionType, const char *exception) {
auto event = sentry_value_new_message_event(level, LoggerName, message);
sentry_event_add_exception(event, sentry_value_new_exception(exceptionType, exception));

View File

@ -21,6 +21,7 @@
#include <sentry.h>
void initSentry();
void setSentryReportScope();
sentry_options_t* newSentryOptions(const char * sentryDNS, const char * cacheDir);
sentry_uuid_t reportSentryEvent(sentry_level_t level, const char *message);

View File

@ -18,7 +18,9 @@
#include "Pch.h"
#include "BridgeApp.h"
#include "BridgeLib.h"
#include "CommandLine.h"
#include "Log.h"
#include "QMLBackend.h"
#include "SentryUtils.h"
#include "BuildConfig.h"
@ -27,8 +29,6 @@
#include <bridgepp/FocusGRPC/FocusGRPCClient.h>
#include <bridgepp/Log/Log.h>
#include <bridgepp/ProcessMonitor.h>
#include <sentry.h>
#include <SentryUtils.h>
#ifdef Q_OS_MACOS
@ -98,38 +98,6 @@ void initQtApplication() {
}
//****************************************************************************************************************************************************
/// \return A reference to the log.
//****************************************************************************************************************************************************
Log &initLog() {
Log &log = app().log();
log.registerAsQtMessageHandler();
log.setEchoInConsole(true);
// remove old gui log files
QDir const logsDir(userLogsDir());
for (QFileInfo const fileInfo: logsDir.entryInfoList({ "gui_v*.log" }, QDir::Filter::Files)) { // entryInfolist apparently only support wildcards, not regex.
QFile(fileInfo.absoluteFilePath()).remove();
}
// create new GUI log file
QString error;
if (!log.startWritingToFile(logsDir.absoluteFilePath(QString("gui_v%1_%2.log").arg(PROJECT_VER).arg(QDateTime::currentSecsSinceEpoch())), &error)) {
log.error(error);
}
log.info("bridge-gui starting");
QString const qtCompileTimeVersion = QT_VERSION_STR;
QString const qtRuntimeVersion = qVersion();
QString msg = QString("Using Qt %1").arg(qtRuntimeVersion);
if (qtRuntimeVersion != qtCompileTimeVersion) {
msg += QString(" (compiled against %1)").arg(qtCompileTimeVersion);
}
log.info(msg);
return log;
}
//****************************************************************************************************************************************************
/// \param[in] engine The QML component.
@ -189,7 +157,7 @@ QUrl getApiUrl() {
url.setPort(1042);
// override with what can be found in the prefs.json file.
QFile prefFile(QString("%1/%2").arg(bridgepp::userConfigDir(), "prefs.json"));
QFile prefFile(QString("%1/%2").arg(bridgelib::userConfigDir(), "prefs.json"));
if (prefFile.exists()) {
prefFile.open(QIODevice::ReadOnly | QIODevice::Text);
QByteArray data = prefFile.readAll();
@ -217,7 +185,7 @@ QUrl getApiUrl() {
/// \return true if an instance of bridge is already running.
//****************************************************************************************************************************************************
bool isBridgeRunning() {
QLockFile lockFile(QDir(userCacheDir()).absoluteFilePath(bridgeLock));
QLockFile lockFile(QDir(bridgelib::userCacheDir()).absoluteFilePath(bridgeLock));
return (!lockFile.tryLock()) && (lockFile.error() == QLockFile::LockFailedError);
}
@ -229,7 +197,7 @@ void focusOtherInstance() {
try {
FocusGRPCClient client;
GRPCConfig sc;
QString const path = FocusGRPCClient::grpcFocusServerConfigPath();
QString const path = FocusGRPCClient::grpcFocusServerConfigPath(bridgelib::userConfigDir());
QFile file(path);
if (file.exists()) {
if (!sc.load(path)) {
@ -252,7 +220,7 @@ void focusOtherInstance() {
catch (Exception const &e) {
app().log().error(e.qwhat());
auto uuid = reportSentryException(SENTRY_LEVEL_ERROR, "Exception occurred during focusOtherInstance()", "Exception", e.what());
app().log().fatal(QString("reportID: %1 Captured exception: %2").arg(QByteArray(uuid.bytes, 16).toHex()).arg(e.qwhat()));
app().log().fatal(QString("reportID: %1 Captured exception: %2").arg(QByteArray(uuid.bytes, 16).toHex(), e.qwhat()));
}
}
@ -303,31 +271,29 @@ void closeBridgeApp() {
/// \return The exit code for the application.
//****************************************************************************************************************************************************
int main(int argc, char *argv[]) {
// Init sentry.
sentry_options_t *sentryOptions = newSentryOptions(PROJECT_DSN_SENTRY, sentryCacheDir().toStdString().c_str());
if (!QString(PROJECT_CRASHPAD_HANDLER_PATH).isEmpty())
sentry_options_set_handler_path(sentryOptions, PROJECT_CRASHPAD_HANDLER_PATH);
if (sentry_init(sentryOptions) != 0) {
std::cerr << "Failed to initialize sentry" << std::endl;
}
setSentryReportScope();
auto sentryClose = qScopeGuard([] { sentry_close(); });
// The application instance is needed to display system message boxes. As we may have to do it in the exception handler,
// application instance is create outside the try/catch clause.
if (QSysInfo::productType() != "windows") {
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL); // must be called before instantiating the BridgeApp
}
BridgeApp guiApp(argc, argv);
try {
bridgelib::loadLibrary();
QString const& configDir = bridgelib::userConfigDir();
// Init sentry.
initSentry();
auto sentryClose = qScopeGuard([] { sentry_close(); });
initQtApplication();
Log &log = initLog();
QLockFile lock(bridgepp::userCacheDir() + "/" + bridgeGUILock);
QLockFile lock(bridgelib::userCacheDir() + "/" + bridgeGUILock);
if (!checkSingleInstance(lock)) {
focusOtherInstance();
return EXIT_FAILURE;
@ -351,15 +317,16 @@ int main(int argc, char *argv[]) {
}
// before launching bridge, we remove any trailing service config file, because we need to make sure we get a newly generated one.
FocusGRPCClient::removeServiceConfigFile();
GRPCClient::removeServiceConfigFile();
FocusGRPCClient::removeServiceConfigFile(configDir);
GRPCClient::removeServiceConfigFile(configDir);
launchBridge(cliOptions.bridgeArgs);
}
log.info(QString("Retrieving gRPC service configuration from '%1'").arg(QDir::toNativeSeparators(grpcServerConfigPath())));
app().backend().init(GRPCClient::waitAndRetrieveServiceConfig(cliOptions.attach ? 0 : grpcServiceConfigWaitDelayMs, app().bridgeMonitor()));
log.info(QString("Retrieving gRPC service configuration from '%1'").arg(QDir::toNativeSeparators(grpcServerConfigPath(configDir))));
app().backend().init(GRPCClient::waitAndRetrieveServiceConfig(configDir, cliOptions.attach ? 0 : grpcServiceConfigWaitDelayMs,
app().bridgeMonitor()));
if (!cliOptions.attach) {
GRPCClient::removeServiceConfigFile();
GRPCClient::removeServiceConfigFile(configDir);
}
// gRPC communication is established. From now on, log events will be sent to bridge via gRPC. bridge will write these to file,
@ -409,11 +376,11 @@ int main(int argc, char *argv[]) {
int result = 0;
if (!startError) {
// we succeeded in launching bridge, so we can be set as mainExecutable.
QString mainexec = QString::fromLocal8Bit(argv[0]);
app().grpc().setMainExecutable(mainexec);
QString mainExe = QString::fromLocal8Bit(argv[0]);
app().grpc().setMainExecutable(mainExe);
QStringList args = cliOptions.bridgeGuiArgs;
args.append(waitFlag);
args.append(mainexec);
args.append(mainExe);
app().setLauncherArgs(cliOptions.launcher, args);
result = QGuiApplication::exec();
}

View File

@ -231,7 +231,7 @@ QtObject {
}
function getTrayIconPath() {
var color = Backend.goos == "darwin" ? "mono" : "color"
var color = Backend.goOS() == "darwin" ? "mono" : "color"
var level = "norm"
if (_systrayfilter.topmost) {

View File

@ -45,7 +45,7 @@ Item {
property string link: "https://proton.me/support/protonmail-bridge-clients-apple-mail"
Component.onCompleted : {
if (Backend.goos == "darwin") {
if (Backend.goOS() == "darwin") {
append({
"name" : "Apple Mail",
"iconSource" : "/qml/icons/ic-apple-mail.svg",
@ -59,7 +59,7 @@ Item {
"link" : "https://proton.me/support/protonmail-bridge-clients-macos-outlook-2019"
})
}
if (Backend.goos == "windows") {
if (Backend.goOS() == "windows") {
append({
"name" : "Microsoft Outlook",
"iconSource" : "/qml/icons/ic-microsoft-outlook.svg",