mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-21 01:26:48 +00:00
feat(GODT-2373): introducing bridgelib Go dynamic library in bridge-gui.
This commit is contained in:
225
internal/frontend/bridge-gui/bridge-gui/BridgeLib.cpp
Normal file
225
internal/frontend/bridge-gui/bridge-gui/BridgeLib.cpp
Normal 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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
36
internal/frontend/bridge-gui/bridge-gui/BridgeLib.h
Normal file
36
internal/frontend/bridge-gui/bridge-gui/BridgeLib.h
Normal 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
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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")
|
||||
|
||||
66
internal/frontend/bridge-gui/bridge-gui/Log.cpp
Normal file
66
internal/frontend/bridge-gui/bridge-gui/Log.cpp
Normal 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;
|
||||
}
|
||||
29
internal/frontend/bridge-gui/bridge-gui/Log.h
Normal file
29
internal/frontend/bridge-gui/bridge-gui/Log.h
Normal 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
|
||||
@ -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.
|
||||
//****************************************************************************************************************************************************
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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",
|
||||
|
||||
Reference in New Issue
Block a user