Compare commits
2 Commits
e9a278a248
...
4c6315b450
Author | SHA1 | Date |
---|---|---|
Martchus | 4c6315b450 | |
Martchus | 69f466be66 |
|
@ -11,7 +11,7 @@ set(META_APP_CATEGORIES "Network;FileTransfer")
|
|||
set(META_GUI_OPTIONAL false)
|
||||
set(META_VERSION_MAJOR 1)
|
||||
set(META_VERSION_MINOR 1)
|
||||
set(META_VERSION_PATCH 8)
|
||||
set(META_VERSION_PATCH 9)
|
||||
set(META_VERSION_EXACT_SONAME ON)
|
||||
set(META_ADD_DEFAULT_CPP_UNIT_TEST_APPLICATION ON)
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
#include <QTimer>
|
||||
|
||||
// uncomment to enforce stopSyncthing() via REST-API (for testing)
|
||||
//#define LIB_SYNCTHING_CONNECTOR_ENFORCE_STOP_VIA_API
|
||||
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_BOOST_PROCESS
|
||||
#include <c++utilities/io/ansiescapecodes.h>
|
||||
|
||||
|
@ -224,7 +227,7 @@ void SyncthingProcess::stopSyncthing(SyncthingConnection *currentConnection)
|
|||
{
|
||||
m_manuallyStopped = true;
|
||||
m_killTimer.start();
|
||||
#ifdef PLATFORM_UNIX
|
||||
#if defined(PLATFORM_UNIX) && !defined(LIB_SYNCTHING_CONNECTOR_ENFORCE_STOP_VIA_API)
|
||||
Q_UNUSED(currentConnection)
|
||||
#else
|
||||
if (currentConnection && !currentConnection->syncthingUrl().isEmpty() && !currentConnection->apiKey().isEmpty() && currentConnection->isLocal()) {
|
||||
|
|
|
@ -74,8 +74,8 @@ set(REQUIRED_ICONS
|
|||
go-up)
|
||||
|
||||
# find c++utilities
|
||||
find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.0.0 REQUIRED)
|
||||
use_cpp_utilities()
|
||||
find_package(c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.11.0 REQUIRED)
|
||||
use_cpp_utilities(VISIBILITY PUBLIC)
|
||||
|
||||
# find qtutilities
|
||||
find_package(qtutilities${CONFIGURATION_PACKAGE_SUFFIX_QTUTILITIES} 6.3.0 REQUIRED)
|
||||
|
|
|
@ -13,18 +13,18 @@ using namespace CppUtilities;
|
|||
|
||||
namespace QtGui {
|
||||
|
||||
SyncthingKiller::SyncthingKiller(std::vector<SyncthingProcess *> &&processes)
|
||||
SyncthingKiller::SyncthingKiller(std::vector<ProcessWithConnection> &&processes)
|
||||
: m_processes(processes)
|
||||
{
|
||||
for (auto *process : m_processes) {
|
||||
process->stopSyncthing();
|
||||
for (const auto [process, connection] : m_processes) {
|
||||
process->stopSyncthing(connection);
|
||||
connect(process, &SyncthingProcess::confirmKill, this, &SyncthingKiller::confirmKill);
|
||||
}
|
||||
}
|
||||
|
||||
void SyncthingKiller::waitForFinished()
|
||||
{
|
||||
for (auto *process : m_processes) {
|
||||
for (const auto [process, connection] : m_processes) {
|
||||
if (!process->isRunning()) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -8,15 +8,28 @@
|
|||
#include <vector>
|
||||
|
||||
namespace Data {
|
||||
class SyncthingConnection;
|
||||
class SyncthingProcess;
|
||||
}
|
||||
} // namespace Data
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
struct ProcessWithConnection {
|
||||
explicit ProcessWithConnection(Data::SyncthingProcess *process, Data::SyncthingConnection *connection = nullptr);
|
||||
Data::SyncthingProcess *const process;
|
||||
Data::SyncthingConnection *const connection;
|
||||
};
|
||||
|
||||
inline ProcessWithConnection::ProcessWithConnection(Data::SyncthingProcess *process, Data::SyncthingConnection *connection)
|
||||
: process(process)
|
||||
, connection(connection)
|
||||
{
|
||||
}
|
||||
|
||||
class SYNCTHINGWIDGETS_EXPORT SyncthingKiller : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SyncthingKiller(std::vector<Data::SyncthingProcess *> &&processes);
|
||||
explicit SyncthingKiller(std::vector<ProcessWithConnection> &&processes);
|
||||
|
||||
Q_SIGNALS:
|
||||
void ignored();
|
||||
|
@ -28,7 +41,7 @@ private Q_SLOTS:
|
|||
void confirmKill() const;
|
||||
|
||||
private:
|
||||
std::vector<Data::SyncthingProcess *> m_processes;
|
||||
std::vector<ProcessWithConnection> m_processes;
|
||||
};
|
||||
|
||||
} // namespace QtGui
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <string_view>
|
||||
|
||||
using namespace std;
|
||||
using namespace std::placeholders;
|
||||
|
@ -30,6 +31,8 @@ SyncthingLauncher *SyncthingLauncher::s_mainInstance = nullptr;
|
|||
*/
|
||||
SyncthingLauncher::SyncthingLauncher(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_guiListeningUrlSearch("Access the GUI via the following URL: ", "\n\r", std::string_view(),
|
||||
std::bind(&SyncthingLauncher::handleGuiListeningUrlFound, this, std::placeholders::_1, std::placeholders::_2))
|
||||
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
|
||||
, m_libsyncthingLogLevel(LibSyncthing::LogLevel::Info)
|
||||
#endif
|
||||
|
@ -94,7 +97,7 @@ void SyncthingLauncher::launch(const QString &program, const QStringList &argume
|
|||
if (isRunning() || m_stopFuture.isRunning()) {
|
||||
return;
|
||||
}
|
||||
m_manuallyStopped = false;
|
||||
resetState();
|
||||
|
||||
// start external process
|
||||
if (!program.isEmpty()) {
|
||||
|
@ -148,7 +151,7 @@ void SyncthingLauncher::launch(const LibSyncthing::RuntimeOptions &runtimeOption
|
|||
if (isRunning() || m_stopFuture.isRunning()) {
|
||||
return;
|
||||
}
|
||||
m_manuallyStopped = false;
|
||||
resetState();
|
||||
m_startFuture = QtConcurrent::run(std::bind(&SyncthingLauncher::runLibSyncthing, this, runtimeOptions));
|
||||
}
|
||||
#endif
|
||||
|
@ -207,6 +210,16 @@ void SyncthingLauncher::handleProcessFinished(int exitCode, QProcess::ExitStatus
|
|||
emit exited(exitCode, exitStatus);
|
||||
}
|
||||
|
||||
void SyncthingLauncher::resetState()
|
||||
{
|
||||
m_manuallyStopped = false;
|
||||
m_guiListeningUrlSearch.reset();
|
||||
if (!m_guiListeningUrl.isEmpty()) {
|
||||
m_guiListeningUrl.clear();
|
||||
emit guiUrlChanged(m_guiListeningUrl);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
|
||||
static const char *const logLevelStrings[] = {
|
||||
"[DEBUG] ",
|
||||
|
@ -234,6 +247,7 @@ void SyncthingLauncher::handleLoggingCallback(LibSyncthing::LogLevel level, cons
|
|||
|
||||
void SyncthingLauncher::handleOutputAvailable(QByteArray &&data)
|
||||
{
|
||||
m_guiListeningUrlSearch(data.data(), static_cast<std::size_t>(data.size()));
|
||||
if (isEmittingOutput()) {
|
||||
emit outputAvailable(data);
|
||||
} else {
|
||||
|
@ -241,6 +255,12 @@ void SyncthingLauncher::handleOutputAvailable(QByteArray &&data)
|
|||
}
|
||||
}
|
||||
|
||||
void SyncthingLauncher::handleGuiListeningUrlFound(CppUtilities::BufferSearch &, std::string &&searchResult)
|
||||
{
|
||||
m_guiListeningUrl.setUrl(QString::fromStdString(searchResult));
|
||||
emit guiUrlChanged(m_guiListeningUrl);
|
||||
}
|
||||
|
||||
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
|
||||
void SyncthingLauncher::runLibSyncthing(const LibSyncthing::RuntimeOptions &runtimeOptions)
|
||||
{
|
||||
|
|
|
@ -9,8 +9,11 @@
|
|||
|
||||
#include <syncthingconnector/syncthingprocess.h>
|
||||
|
||||
#include <c++utilities/io/buffersearch.h>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QFuture>
|
||||
#include <QUrl>
|
||||
|
||||
namespace Settings {
|
||||
struct Launcher;
|
||||
|
@ -26,6 +29,8 @@ class SYNCTHINGWIDGETS_EXPORT SyncthingLauncher : public QObject {
|
|||
Q_PROPERTY(CppUtilities::DateTime activeSince READ activeSince)
|
||||
Q_PROPERTY(bool manuallyStopped READ isManuallyStopped)
|
||||
Q_PROPERTY(bool emittingOutput READ isEmittingOutput WRITE setEmittingOutput)
|
||||
Q_PROPERTY(QUrl guiUrl READ guiUrl WRITE guiUrlChanged)
|
||||
Q_PROPERTY(SyncthingProcess *process READ process)
|
||||
|
||||
public:
|
||||
explicit SyncthingLauncher(QObject *parent = nullptr);
|
||||
|
@ -37,6 +42,9 @@ public:
|
|||
bool isEmittingOutput() const;
|
||||
void setEmittingOutput(bool emittingOutput);
|
||||
QString errorString() const;
|
||||
QUrl guiUrl() const;
|
||||
SyncthingProcess *process();
|
||||
const SyncthingProcess *process() const;
|
||||
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
|
||||
LibSyncthing::LogLevel libSyncthingLogLevel() const;
|
||||
void setLibSyncthingLogLevel(LibSyncthing::LogLevel logLevel);
|
||||
|
@ -55,6 +63,7 @@ Q_SIGNALS:
|
|||
void outputAvailable(const QByteArray &data);
|
||||
void exited(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void errorOccurred(QProcess::ProcessError error);
|
||||
void guiUrlChanged(const QUrl &newUrl);
|
||||
|
||||
public Q_SLOTS:
|
||||
void launch(const QString &program, const QStringList &arguments);
|
||||
|
@ -75,15 +84,19 @@ private Q_SLOTS:
|
|||
#endif
|
||||
|
||||
private:
|
||||
void resetState();
|
||||
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
|
||||
void handleLoggingCallback(LibSyncthing::LogLevel, const char *message, std::size_t messageSize);
|
||||
#endif
|
||||
void handleOutputAvailable(QByteArray &&data);
|
||||
void handleGuiListeningUrlFound(CppUtilities::BufferSearch &bufferSearch, std::string &&searchResult);
|
||||
|
||||
SyncthingProcess m_process;
|
||||
QUrl m_guiListeningUrl;
|
||||
QFuture<void> m_startFuture;
|
||||
QFuture<void> m_stopFuture;
|
||||
QByteArray m_outputBuffer;
|
||||
CppUtilities::BufferSearch m_guiListeningUrlSearch;
|
||||
CppUtilities::DateTime m_futureStarted;
|
||||
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
|
||||
LibSyncthing::LogLevel m_libsyncthingLogLevel;
|
||||
|
@ -138,6 +151,24 @@ inline QString SyncthingLauncher::errorString() const
|
|||
return m_process.errorString();
|
||||
}
|
||||
|
||||
/// \brief Returns the GUI listening URL determined from Syncthing's log.
|
||||
inline QUrl SyncthingLauncher::guiUrl() const
|
||||
{
|
||||
return m_guiListeningUrl;
|
||||
}
|
||||
|
||||
/// \brief Returns the underlying SyncthingProcess.
|
||||
inline SyncthingProcess *SyncthingLauncher::process()
|
||||
{
|
||||
return &m_process;
|
||||
}
|
||||
|
||||
/// \brief Returns the underlying SyncthingProcess.
|
||||
inline const SyncthingProcess *SyncthingLauncher::process() const
|
||||
{
|
||||
return &m_process;
|
||||
}
|
||||
|
||||
#ifdef SYNCTHINGWIDGETS_USE_LIBSYNCTHING
|
||||
/// \brief Returns the log level used for libsyncthing.
|
||||
inline LibSyncthing::LogLevel SyncthingLauncher::libSyncthingLogLevel() const
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <syncthingconnector/syncthingconnectionsettings.h>
|
||||
#include <syncthingconnector/syncthingnotifier.h>
|
||||
#include <syncthingconnector/syncthingprocess.h>
|
||||
#include <syncthingconnector/utils.h>
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
#include <syncthingconnector/syncthingservice.h>
|
||||
#endif
|
||||
|
@ -20,6 +21,8 @@
|
|||
#include <qtutilities/misc/dbusnotification.h>
|
||||
#endif
|
||||
|
||||
#include <c++utilities/io/ansiescapecodes.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCursor>
|
||||
#include <QFile>
|
||||
|
@ -29,11 +32,13 @@
|
|||
#include <QSslError>
|
||||
#include <QStringBuilder>
|
||||
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace std;
|
||||
using namespace Data;
|
||||
using namespace CppUtilities::EscapeCodes;
|
||||
using namespace QtUtilities;
|
||||
|
||||
namespace Settings {
|
||||
|
@ -64,15 +69,53 @@ SyncthingProcess &Launcher::toolProcess(const QString &tool)
|
|||
return toolProcesses[tool];
|
||||
}
|
||||
|
||||
std::vector<SyncthingProcess *> Launcher::allProcesses()
|
||||
static bool isLocalAndMatchesPort(const Data::SyncthingConnectionSettings &settings, int port)
|
||||
{
|
||||
vector<SyncthingProcess *> processes;
|
||||
processes.reserve(1 + toolProcesses.size());
|
||||
if (auto *const syncthingProcess = SyncthingProcess::mainInstance()) {
|
||||
processes.push_back(syncthingProcess);
|
||||
if (settings.syncthingUrl.isEmpty() || settings.apiKey.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (auto &process : toolProcesses) {
|
||||
processes.push_back(&process.second);
|
||||
const auto url = QUrl(settings.syncthingUrl);
|
||||
return ::Data::isLocal(url) && port == url.port(url.scheme() == QLatin1String("https") ? 443 : 80);
|
||||
}
|
||||
|
||||
Data::SyncthingConnection *Launcher::connectionForLauncher(Data::SyncthingLauncher *launcher)
|
||||
{
|
||||
const auto port = launcher->guiUrl().port(-1);
|
||||
if (port < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
auto &connectionSettings = values().connection;
|
||||
auto *relevantSetting = isLocalAndMatchesPort(connectionSettings.primary, port) ? &connectionSettings.primary : nullptr;
|
||||
if (!relevantSetting) {
|
||||
for (auto &secondarySetting : connectionSettings.secondary) {
|
||||
if (isLocalAndMatchesPort(secondarySetting, port)) {
|
||||
relevantSetting = &secondarySetting;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!relevantSetting) {
|
||||
return nullptr;
|
||||
}
|
||||
auto *const connection = new SyncthingConnection();
|
||||
connection->setParent(launcher);
|
||||
connection->applySettings(*relevantSetting);
|
||||
std::cerr << Phrases::Info << "Considering configured connection \"" << relevantSetting->label.toStdString()
|
||||
<< "\" (URL: " << relevantSetting->syncthingUrl.toStdString() << ") to terminate Syncthing" << Phrases::End;
|
||||
return connection;
|
||||
}
|
||||
|
||||
std::vector<QtGui::ProcessWithConnection> Launcher::allProcesses()
|
||||
{
|
||||
auto processes = std::vector<QtGui::ProcessWithConnection>();
|
||||
processes.reserve(1 + toolProcesses.size());
|
||||
if (auto *const launcher = SyncthingLauncher::mainInstance()) {
|
||||
processes.emplace_back(launcher->process(), connectionForLauncher(launcher));
|
||||
} else if (auto *const process = SyncthingProcess::mainInstance()) {
|
||||
processes.emplace_back(process);
|
||||
}
|
||||
for (auto &[tool, process] : toolProcesses) {
|
||||
processes.emplace_back(&process);
|
||||
}
|
||||
return processes;
|
||||
}
|
||||
|
|
|
@ -29,11 +29,16 @@ class QtSettings;
|
|||
|
||||
namespace Data {
|
||||
class SyncthingProcess;
|
||||
class SyncthingLauncher;
|
||||
class SyncthingNotifier;
|
||||
class SyncthingConnection;
|
||||
class SyncthingService;
|
||||
} // namespace Data
|
||||
|
||||
namespace QtGui {
|
||||
struct ProcessWithConnection;
|
||||
}
|
||||
|
||||
namespace Settings {
|
||||
|
||||
struct SYNCTHINGWIDGETS_EXPORT Connection {
|
||||
|
@ -94,7 +99,8 @@ struct SYNCTHINGWIDGETS_EXPORT Launcher {
|
|||
#endif
|
||||
|
||||
static Data::SyncthingProcess &toolProcess(const QString &tool);
|
||||
static std::vector<Data::SyncthingProcess *> allProcesses();
|
||||
static Data::SyncthingConnection *connectionForLauncher(Data::SyncthingLauncher *launcher);
|
||||
static std::vector<QtGui::ProcessWithConnection> allProcesses();
|
||||
void autostart() const;
|
||||
static void terminate();
|
||||
struct SYNCTHINGWIDGETS_EXPORT LauncherStatus {
|
||||
|
|
|
@ -1188,7 +1188,7 @@ void LauncherOptionPage::stop()
|
|||
m_process->stopSyncthing();
|
||||
}
|
||||
if (m_launcher) {
|
||||
m_launcher->terminate();
|
||||
m_launcher->terminate(Launcher::connectionForLauncher(m_launcher));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1209,6 +1209,10 @@ SystemdOptionPage::SystemdOptionPage(QWidget *parentWidget)
|
|||
|
||||
SystemdOptionPage::~SystemdOptionPage()
|
||||
{
|
||||
QObject::disconnect(m_unitChangedConn);
|
||||
QObject::disconnect(m_descChangedConn);
|
||||
QObject::disconnect(m_statusChangedConn);
|
||||
QObject::disconnect(m_enabledChangedConn);
|
||||
}
|
||||
|
||||
QWidget *SystemdOptionPage::setupWidget()
|
||||
|
@ -1218,14 +1222,18 @@ QWidget *SystemdOptionPage::setupWidget()
|
|||
return widget;
|
||||
}
|
||||
QObject::connect(ui()->syncthingUnitLineEdit, &QLineEdit::textChanged, m_service, &SyncthingService::setUnitName);
|
||||
QObject::connect(ui()->systemUnitCheckBox, &QCheckBox::clicked, m_service, bind(&SystemdOptionPage::handleSystemUnitChanged, this));
|
||||
QObject::connect(ui()->startPushButton, &QPushButton::clicked, m_service, &SyncthingService::start);
|
||||
QObject::connect(ui()->stopPushButton, &QPushButton::clicked, m_service, &SyncthingService::stop);
|
||||
QObject::connect(ui()->enablePushButton, &QPushButton::clicked, m_service, &SyncthingService::enable);
|
||||
QObject::connect(ui()->disablePushButton, &QPushButton::clicked, m_service, &SyncthingService::disable);
|
||||
QObject::connect(m_service, &SyncthingService::descriptionChanged, bind(&SystemdOptionPage::handleDescriptionChanged, this, _1));
|
||||
QObject::connect(m_service, &SyncthingService::stateChanged, bind(&SystemdOptionPage::handleStatusChanged, this, _1, _2, _3));
|
||||
QObject::connect(m_service, &SyncthingService::unitFileStateChanged, bind(&SystemdOptionPage::handleEnabledChanged, this, _1));
|
||||
m_unitChangedConn
|
||||
= QObject::connect(ui()->systemUnitCheckBox, &QCheckBox::clicked, m_service, bind(&SystemdOptionPage::handleSystemUnitChanged, this));
|
||||
m_descChangedConn
|
||||
= QObject::connect(m_service, &SyncthingService::descriptionChanged, bind(&SystemdOptionPage::handleDescriptionChanged, this, _1));
|
||||
m_statusChangedConn
|
||||
= QObject::connect(m_service, &SyncthingService::stateChanged, bind(&SystemdOptionPage::handleStatusChanged, this, _1, _2, _3));
|
||||
m_enabledChangedConn
|
||||
= QObject::connect(m_service, &SyncthingService::unitFileStateChanged, bind(&SystemdOptionPage::handleEnabledChanged, this, _1));
|
||||
return widget;
|
||||
}
|
||||
|
||||
|
|
|
@ -148,6 +148,10 @@ void handleDescriptionChanged(const QString &description);
|
|||
void handleStatusChanged(const QString &activeState, const QString &subState, CppUtilities::DateTime activeSince);
|
||||
void handleEnabledChanged(const QString &unitFileState);
|
||||
Data::SyncthingService *const m_service;
|
||||
QMetaObject::Connection m_unitChangedConn;
|
||||
QMetaObject::Connection m_descChangedConn;
|
||||
QMetaObject::Connection m_statusChangedConn;
|
||||
QMetaObject::Connection m_enabledChangedConn;
|
||||
END_DECLARE_OPTION_PAGE
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in New Issue