mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-20 17:16:46 +00:00
feat(GODT-2261): sync progress in GUI.
This commit is contained in:
225
internal/frontend/bridge-gui/bridgepp/bridgepp/BridgeLib.cpp
Normal file
225
internal/frontend/bridge-gui/bridgepp/bridgepp/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/bridgepp/bridgepp/BridgeLib.h
Normal file
36
internal/frontend/bridge-gui/bridgepp/bridgepp/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
|
||||
@ -601,6 +601,51 @@ SPStreamEvent newIMAPLoginFailedEvent(QString const &username) {
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] userID The userID.
|
||||
/// \return The event.
|
||||
//****************************************************************************************************************************************************
|
||||
SPStreamEvent newSyncStartedEvent(QString const &userID) {
|
||||
auto event = new grpc::SyncStartedEvent;
|
||||
event->set_userid(userID.toStdString());
|
||||
auto userEvent = new grpc::UserEvent;
|
||||
userEvent->set_allocated_syncstartedevent(event);
|
||||
return wrapUserEvent(userEvent);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] userID The userID.
|
||||
/// \return The event.
|
||||
//****************************************************************************************************************************************************
|
||||
SPStreamEvent newSyncFinishedEvent(QString const &userID) {
|
||||
auto event = new grpc::SyncFinishedEvent;
|
||||
event->set_userid(userID.toStdString());
|
||||
auto userEvent = new grpc::UserEvent;
|
||||
userEvent->set_allocated_syncfinishedevent(event);
|
||||
return wrapUserEvent(userEvent);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] userID The userID.
|
||||
/// \param[in] progress The progress ratio.
|
||||
/// \param[in] elapsedMs The elapsed time in milliseconds.
|
||||
/// \param[in] remainingMs The remaining time in milliseconds.
|
||||
/// \return The event.
|
||||
//****************************************************************************************************************************************************
|
||||
SPStreamEvent newSyncProgressEvent(QString const &userID, double progress, qint64 elapsedMs, qint64 remainingMs) {
|
||||
auto event = new grpc::SyncProgressEvent;
|
||||
event->set_userid(userID.toStdString());
|
||||
event->set_progress(progress);
|
||||
event->set_elapsedms(elapsedMs);
|
||||
event->set_remainingms(remainingMs);
|
||||
auto userEvent = new grpc::UserEvent;
|
||||
userEvent->set_allocated_syncprogressevent(event);
|
||||
return wrapUserEvent(userEvent);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] errorCode The error errorCode.
|
||||
/// \return The event.
|
||||
|
||||
@ -80,6 +80,9 @@ SPStreamEvent newUserChangedEvent(QString const &userID); ///< Create a new User
|
||||
SPStreamEvent newUserBadEvent(QString const &userID, QString const& errorMessage); ///< Create a new UserBadEvent event.
|
||||
SPStreamEvent newUsedBytesChangedEvent(QString const &userID, qint64 usedBytes); ///< Create a new UsedBytesChangedEvent event.
|
||||
SPStreamEvent newIMAPLoginFailedEvent(QString const &username); ///< Create a new ImapLoginFailedEvent event.
|
||||
SPStreamEvent newSyncStartedEvent(QString const &userID); ///< Create a new SyncStarted event.
|
||||
SPStreamEvent newSyncFinishedEvent(QString const &userID); ///< Create a new SyncFinished event.
|
||||
SPStreamEvent newSyncProgressEvent(QString const &userID, double progress, qint64 elapsedMs, qint64 remainingMs); ///< Create a new SyncFinished event.
|
||||
|
||||
// Generic error event
|
||||
SPStreamEvent newGenericErrorEvent(grpc::ErrorCode errorCode); ///< Create a new GenericErrrorEvent event.
|
||||
|
||||
@ -1380,6 +1380,28 @@ void GRPCClient::processUserEvent(UserEvent const &event) {
|
||||
emit imapLoginFailed(username);
|
||||
break;
|
||||
}
|
||||
case UserEvent::kSyncStartedEvent: {
|
||||
SyncStartedEvent const &e = event.syncstartedevent();
|
||||
QString const &userID = QString::fromStdString(e.userid());
|
||||
this->logTrace(QString("User event received: SyncStarted (userID = %1).:").arg(userID));
|
||||
emit syncStarted(userID);
|
||||
break;
|
||||
}
|
||||
case UserEvent::kSyncFinishedEvent: {
|
||||
SyncFinishedEvent const &e = event.syncfinishedevent();
|
||||
QString const &userID = QString::fromStdString(e.userid());
|
||||
this->logTrace(QString("User event received: SyncFinished (userID = %1).:").arg(userID));
|
||||
emit syncFinished(userID);
|
||||
break;
|
||||
}
|
||||
case UserEvent::kSyncProgressEvent: {
|
||||
SyncProgressEvent const &e = event.syncprogressevent();
|
||||
QString const &userID = QString::fromStdString(e.userid());
|
||||
this->logTrace(QString("User event received SyncProgress (userID = %1, progress = %2, elapsedMs = %3, remainingMs = %4).").arg(userID)
|
||||
.arg(e.progress()).arg(e.elapsedms()).arg(e.remainingms()));
|
||||
emit syncProgress(userID, e.progress(), e.elapsedms(), e.remainingms());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
this->logError("Unknown User event received.");
|
||||
}
|
||||
|
||||
@ -181,6 +181,9 @@ signals:
|
||||
void userBadEvent(QString const &userID, QString const& errorMessage);
|
||||
void usedBytesChanged(QString const &userID, qint64 usedBytes);
|
||||
void imapLoginFailed(QString const& username);
|
||||
void syncStarted(QString const &userID);
|
||||
void syncFinished(QString const &userID);
|
||||
void syncProgress(QString const &userID, double progress, qint64 elapsedMs, qint64 remainingMs);
|
||||
|
||||
public: // keychain related calls
|
||||
grpc::Status availableKeychains(QStringList &outKeychains);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -294,6 +294,48 @@ void User::setTotalBytes(float totalBytes) {
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return true iff a sync is in progress.
|
||||
//****************************************************************************************************************************************************
|
||||
bool User::isSyncing() const {
|
||||
return isSyncing_;
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] syncing The new value for the sync state.
|
||||
//****************************************************************************************************************************************************
|
||||
void User::setIsSyncing(bool syncing) {
|
||||
if (isSyncing_ == syncing) {
|
||||
return;
|
||||
}
|
||||
|
||||
isSyncing_ = syncing;
|
||||
emit isSyncingChanged(syncing);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return The sync progress ratio
|
||||
//****************************************************************************************************************************************************
|
||||
float User::syncProgress() const {
|
||||
return syncProgress_;
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] progress The progress ratio.
|
||||
//****************************************************************************************************************************************************
|
||||
void User::setSyncProgress(float progress) {
|
||||
if (qAbs(syncProgress_ - progress) < 0.00001) {
|
||||
return;
|
||||
}
|
||||
|
||||
syncProgress_ = progress;
|
||||
emit syncProgressChanged(progress);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] state The user state.
|
||||
/// \return A string describing the state.
|
||||
|
||||
@ -101,6 +101,8 @@ public:
|
||||
Q_PROPERTY(bool splitMode READ splitMode WRITE setSplitMode NOTIFY splitModeChanged)
|
||||
Q_PROPERTY(float usedBytes READ usedBytes WRITE setUsedBytes NOTIFY usedBytesChanged)
|
||||
Q_PROPERTY(float totalBytes READ totalBytes WRITE setTotalBytes NOTIFY totalBytesChanged)
|
||||
Q_PROPERTY(bool isSyncing READ isSyncing WRITE setIsSyncing NOTIFY isSyncingChanged)
|
||||
Q_PROPERTY(float syncProgress READ syncProgress WRITE setSyncProgress NOTIFY syncProgressChanged)
|
||||
|
||||
QString id() const;
|
||||
void setID(QString const &id);
|
||||
@ -120,6 +122,10 @@ public:
|
||||
void setUsedBytes(float usedBytes);
|
||||
float totalBytes() const;
|
||||
void setTotalBytes(float totalBytes);
|
||||
bool isSyncing() const;
|
||||
void setIsSyncing(bool syncing);
|
||||
float syncProgress() const;
|
||||
void setSyncProgress(float progress);
|
||||
|
||||
signals:
|
||||
// signals used for Qt properties
|
||||
@ -134,6 +140,8 @@ signals:
|
||||
void usedBytesChanged(float byteCount);
|
||||
void totalBytesChanged(float byteCount);
|
||||
void toggleSplitModeFinished();
|
||||
void isSyncingChanged(bool syncing);
|
||||
void syncProgressChanged(float syncProgress);
|
||||
|
||||
private: // member functions.
|
||||
User(QObject *parent); ///< Default constructor.
|
||||
@ -149,6 +157,8 @@ private: // data members.
|
||||
bool splitMode_ { false }; ///< Is split mode active.
|
||||
float usedBytes_ { 0.0f }; ///< The storage used by the user.
|
||||
float totalBytes_ { 1.0f }; ///< The storage quota of the user.
|
||||
bool isSyncing_ { false }; ///< Is a sync in progress for the user.
|
||||
float syncProgress_ { 0.0f }; ///< The sync progress.
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user