refactor: remove legacy deps pattern and hello back class

This commit is contained in:
Nick Bolton 2026-01-26 10:41:21 +00:00 committed by Chris Rizzitello
parent 6459eb40df
commit be2546c27b
6 changed files with 29 additions and 422 deletions

View file

@ -6,8 +6,6 @@
add_library(client STATIC
Client.cpp
Client.h
HelloBack.cpp
HelloBack.h
ServerProxy.cpp
ServerProxy.h
)

View file

@ -1,7 +1,7 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2012 - 2016, 2026 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
@ -14,9 +14,9 @@
#include "client/ServerProxy.h"
#include "common/Settings.h"
#include "deskflow/Clipboard.h"
#include "deskflow/DeskflowException.h"
#include "deskflow/IPlatformScreen.h"
#include "deskflow/PacketStreamFilter.h"
#include "deskflow/ProtocolTypes.h"
#include "deskflow/ProtocolUtil.h"
#include "deskflow/Screen.h"
#include "deskflow/StreamChunker.h"
@ -27,9 +27,6 @@
#include <cstdlib>
#include <cstring>
#include <memory>
using namespace deskflow::client;
//
// Client
@ -52,19 +49,6 @@ Client::Client(
// register suspend/resume event handlers
m_events->addHandler(EventTypes::ScreenSuspend, getEventTarget(), [this](const auto &) { handleSuspend(); });
m_events->addHandler(EventTypes::ScreenResume, getEventTarget(), [this](const auto &) { handleResume(); });
m_pHelloBack = std::make_unique<HelloBack>(std::make_shared<HelloBack::Deps>(
[this]() {
sendConnectionFailedEvent("got invalid hello message from server");
cleanupTimer();
cleanupConnection();
},
[this](int major, int minor) {
sendConnectionFailedEvent(IncompatibleClientException(major, minor).what());
cleanupTimer();
cleanupConnection();
}
));
}
Client::~Client()
@ -584,7 +568,32 @@ void Client::handleClipboardGrabbed(const Event &event)
void Client::handleHello()
{
m_pHelloBack->handleHello(m_stream, m_name);
int16_t serverMajor;
int16_t serverMinor;
// as luck would have it, both "Synergy" and "Barrier" are 7 chars,
// so we eat 7 chars and then test for either protocol name.
// we cannot re-use `readf` to check for various hello messages,
// as `readf` eats bytes (advances the stream position reference).
std::string protocolName;
ProtocolUtil::readf(m_stream, kMsgHello, &protocolName, &serverMajor, &serverMinor);
if (protocolName != kSynergyProtocolName && protocolName != kBarrierProtocolName) {
LOG_WARN("hello back received with protocol: '%s'", protocolName.c_str());
sendConnectionFailedEvent("got invalid hello message from server");
cleanupTimer();
cleanupConnection();
return;
}
LOG_DEBUG(
"saying hello back with version %s %d.%d", protocolName.c_str(), kProtocolMajorVersion, kProtocolMinorVersion
);
// dynamically build write format for hello back since `ProtocolUtil::writef`
// doesn't support formatting fixed length strings yet.
std::string helloBackMessage = protocolName + kMsgHelloBackArgs;
ProtocolUtil::writef(m_stream, helloBackMessage.c_str(), kProtocolMajorVersion, kProtocolMinorVersion, &m_name);
// now connected but waiting to complete handshake
setupScreen();

View file

@ -1,7 +1,7 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2012 - 2016, 2026 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
@ -10,13 +10,11 @@
#include "deskflow/IClient.h"
#include "HelloBack.h"
#include "base/EventTypes.h"
#include "deskflow/IClipboard.h"
#include "net/NetworkAddress.h"
#include <climits>
#include <memory>
class Event;
class EventQueueTimer;
@ -203,5 +201,4 @@ private:
bool m_enableClipboard = true;
size_t m_maximumClipboardSize = INT_MAX;
size_t m_resolvedAddressesCount = 0;
std::unique_ptr<deskflow::client::HelloBack> m_pHelloBack;
};

View file

@ -1,99 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2024 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "HelloBack.h"
#include "base/Log.h"
#include "deskflow/ProtocolTypes.h"
#include "deskflow/ProtocolUtil.h"
#include <map>
#include <set>
namespace deskflow::client {
//
// HelloBack::Deps
//
void HelloBack::Deps::invalidHello()
{
m_invalidHello();
}
void HelloBack::Deps::incompatible(int major, int minor)
{
m_incompatible(major, minor);
}
//
// HelloBack
//
void HelloBack::handleHello(deskflow::IStream *stream, const std::string &clientName) const
{
int16_t serverMajor;
int16_t serverMinor;
// as luck would have it, both "Synergy" and "Barrier" are 7 chars,
// so we eat 7 chars and then test for either protocol name.
// we cannot re-use `readf` to check for various hello messages,
// as `readf` eats bytes (advances the stream position reference).
std::string protocolName;
ProtocolUtil::readf(stream, kMsgHello, &protocolName, &serverMajor, &serverMinor);
if (protocolName != kSynergyProtocolName && protocolName != kBarrierProtocolName) {
m_deps->invalidHello();
return;
}
// check versions
LOG_DEBUG("got hello from %s, protocol v%d.%d", protocolName.c_str(), serverMajor, serverMinor);
const auto helloBackMajor = m_majorVersion;
auto helloBackMinor = m_minorVersion;
if (shouldDowngrade(serverMajor, serverMinor)) {
LOG_NOTE("downgrading to %d.%d protocol for server", serverMajor, serverMinor);
helloBackMinor = serverMinor;
} else if (serverMajor < m_majorVersion || (serverMajor == m_majorVersion && serverMinor < m_minorVersion)) {
m_deps->incompatible(serverMajor, serverMinor);
return;
}
// say hello back with same protocol name and version
LOG_DEBUG("saying hello back with version %s %d.%d", protocolName.c_str(), helloBackMajor, helloBackMinor);
// dynamically build write format for hello back since `ProtocolUtil::writef`
// doesn't support formatting fixed length strings yet.
std::string helloBackMessage = protocolName + kMsgHelloBackArgs;
ProtocolUtil::writef(stream, helloBackMessage.c_str(), helloBackMajor, helloBackMinor, &clientName);
}
bool HelloBack::shouldDowngrade(int major, int minor) const
{
const std::map<int, std::set<int>> map{
// 1.6 is compatible with 1.7 and 1.8
{6, {7, 8}},
// 1.7 is compatible with 1.8
{7, {8}},
};
if (major == m_majorVersion) {
auto versions = map.find(minor);
if (versions != map.end()) {
auto compatibleVersions = versions->second;
if (compatibleVersions.contains(m_minorVersion)) {
return true;
}
}
}
return false;
}
} // namespace deskflow::client

View file

@ -1,71 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2024 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include "deskflow/ProtocolTypes.h"
#include "io/IStream.h"
#include <functional>
#include <memory>
#include <string> //IWYU
namespace deskflow::client {
class HelloBack
{
public:
struct Deps
{
Deps() = default;
explicit Deps(std::function<void()> invalidHello, std::function<void(int, int)> incompatible)
: m_invalidHello(std::move(invalidHello)),
m_incompatible(std::move(incompatible))
{
// do nothing
}
virtual ~Deps() = default;
/**
* @brief Call when invalid hello message received from server.
*/
virtual void invalidHello();
/**
* @brief Call when the client is incompatible with the server.
*/
virtual void incompatible(int major, int minor);
private:
std::function<void()> m_invalidHello;
std::function<void(int, int)> m_incompatible;
};
explicit HelloBack(
std::shared_ptr<Deps> deps, const int16_t majorVersion = kProtocolMajorVersion,
const int16_t minorVersion = kProtocolMinorVersion
)
: m_deps(deps),
m_majorVersion(majorVersion),
m_minorVersion(minorVersion)
{
// do nothing
}
/**
* @brief Handle hello message from server and reply with hello back.
*/
void handleHello(deskflow::IStream *stream, const std::string &clientName) const;
private:
bool shouldDowngrade(int major, int minor) const;
std::shared_ptr<Deps> m_deps;
int16_t m_majorVersion;
int16_t m_minorVersion;
};
} // namespace deskflow::client

View file

@ -1,227 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2024 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "client/HelloBack.h"
#include "unittests/legacytests/mock/io/MockStream.h"
#include <array>
#include <cstring>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <iomanip>
#include <sstream>
using HelloBack = deskflow::client::HelloBack;
using namespace testing;
namespace {
class MockDeps : public HelloBack::Deps
{
public:
~MockDeps() override = default;
MOCK_METHOD(void, invalidHello, (), (override));
MOCK_METHOD(void, incompatible, (int major, int minor), (override));
};
void intTo2ByteBuf(int16_t value, std::array<char, 2> &buf)
{
buf[0] = static_cast<char>((value >> 8) & 0xFF); // MSB
buf[1] = static_cast<char>(value & 0xFF); // LSB
}
void intTo4ByteBuf(int32_t value, std::array<char, 4> &buf)
{
buf[0] = static_cast<char>((value >> 24) & 0xFF); // MSB
buf[1] = static_cast<char>((value >> 16) & 0xFF);
buf[2] = static_cast<char>((value >> 8) & 0xFF);
buf[3] = static_cast<char>(value & 0xFF); // LSB
}
std::string printAsHex(const char *buffer, size_t size)
{
std::ostringstream hexStream;
for (size_t i = 0; i < size; ++i) {
hexStream << std::hex << std::setw(2) << std::setfill('0')
<< static_cast<int>(static_cast<unsigned char>(buffer[i])) << " ";
}
return hexStream.str();
}
void setupMockHelloRead(
MockStream &stream, const std::string &protocolName, const int16_t majorVersion, const int16_t minorVersion
)
{
std::array<char, 2> majorBuf;
std::array<char, 2> minorBuf;
intTo2ByteBuf(majorVersion, majorBuf);
intTo2ByteBuf(minorVersion, minorBuf);
EXPECT_CALL(stream, read(_, _))
.WillOnce(DoAll(
WithArg<0>([protocolName](void *vbuffer) {
auto buffer = static_cast<char *>(vbuffer);
std::copy(protocolName.begin(), protocolName.end(), buffer);
}),
Return(7)
))
.WillOnce(DoAll(
WithArg<0>([majorBuf](void *vbuffer) {
auto buffer = static_cast<char *>(vbuffer);
std::memcpy(buffer, majorBuf.data(), majorBuf.size());
}),
Return(2)
))
.WillOnce(DoAll(
WithArg<0>([minorBuf](void *vbuffer) {
auto buffer = static_cast<char *>(vbuffer);
std::memcpy(buffer, minorBuf.data(), minorBuf.size());
}),
Return(2)
));
}
void setupMockHelloBackWrite(
MockStream &stream, const std::string &protocolName, const int16_t majorVersion, const int16_t minorVersion,
const std::string &name
)
{
std::array<char, 2> majorBuf;
std::array<char, 2> minorBuf;
std::array<char, 4> nameLenBuf;
intTo2ByteBuf(majorVersion, majorBuf);
intTo2ByteBuf(minorVersion, minorBuf);
intTo4ByteBuf(static_cast<int32_t>(name.size()), nameLenBuf);
const auto versionIntSize = 4;
const auto clientNameIntSize = 4;
const uint32_t helloBackSize =
static_cast<uint32_t>(protocolName.size() + versionIntSize + clientNameIntSize + name.size());
std::vector<char> expect;
expect.reserve(helloBackSize);
expect.insert(expect.end(), protocolName.begin(), protocolName.end());
expect.insert(expect.end(), majorBuf.begin(), majorBuf.end());
expect.insert(expect.end(), minorBuf.begin(), minorBuf.end());
expect.insert(expect.end(), nameLenBuf.begin(), nameLenBuf.end());
expect.insert(expect.end(), name.begin(), name.end());
EXPECT_CALL(stream, write(_, helloBackSize)).WillOnce(WithArg<0>([expect, helloBackSize](const void *vbuffer) {
const auto buffer = static_cast<const char *>(vbuffer);
EXPECT_TRUE(std::memcmp(expect.data(), buffer, helloBackSize) == 0)
<< "Buffer mismatch\n"
<< "Expected: " << printAsHex(expect.data(), helloBackSize) << "\n"
<< "Actual: " << printAsHex(buffer, helloBackSize) << "\n";
}));
}
} // namespace
TEST(HelloBackTests, handleHello_nastyProtocol_invalidHello)
{
auto deps = std::make_shared<NiceMock<MockDeps>>();
HelloBack helloBack(deps);
NiceMock<MockStream> stream;
const std::string clientName = "stub";
setupMockHelloRead(stream, "ShareMouse", 0, 0);
EXPECT_CALL(*deps, invalidHello()).Times(1);
helloBack.handleHello(&stream, clientName);
}
TEST(HelloBackTests, handleHello_synergyProtocolCurrent_validMessage)
{
auto deps = std::make_shared<NiceMock<MockDeps>>();
HelloBack helloBack(deps, 1, 2);
NiceMock<MockStream> stream;
const std::string clientName = "stub";
setupMockHelloRead(stream, "Synergy", 1, 2);
EXPECT_CALL(*deps, incompatible(_, _)).Times(0);
EXPECT_CALL(*deps, invalidHello()).Times(0);
helloBack.handleHello(&stream, clientName);
}
TEST(HelloBackTests, handleHello_barrierProtocolCurrent_validMessage)
{
auto deps = std::make_shared<NiceMock<MockDeps>>();
HelloBack helloBack(deps, 1, 2);
NiceMock<MockStream> stream;
const std::string clientName = "stub";
setupMockHelloRead(stream, "Barrier", 1, 2);
EXPECT_CALL(*deps, incompatible(_, _)).Times(0);
EXPECT_CALL(*deps, invalidHello()).Times(0);
helloBack.handleHello(&stream, clientName);
}
TEST(HelloBackTests, handleHello_synergyProtocolOlder_validMessage)
{
auto deps = std::make_shared<NiceMock<MockDeps>>();
HelloBack helloBack(deps, 1, 2);
NiceMock<MockStream> stream;
const std::string clientName = "stub";
setupMockHelloRead(stream, "Synergy", 1, 1);
EXPECT_CALL(*deps, incompatible(1, 1)).Times(1);
helloBack.handleHello(&stream, clientName);
}
TEST(HelloBackTests, handleHello_synergyProtocolCurrent_wroteHelloBack)
{
auto deps = std::make_shared<NiceMock<MockDeps>>();
HelloBack helloBack(deps, 1, 2);
NiceMock<MockStream> stream;
const std::string clientName = "test client";
setupMockHelloRead(stream, "Synergy", 1, 2);
setupMockHelloBackWrite(stream, "Synergy", 1, 2, "test client");
helloBack.handleHello(&stream, clientName);
}
TEST(HelloBackTests, handleHello_barrierProtocolCurrent_wroteHelloBack)
{
auto deps = std::make_shared<NiceMock<MockDeps>>();
HelloBack helloBack(deps, 1, 2);
NiceMock<MockStream> stream;
const std::string clientName = "test client";
setupMockHelloRead(stream, "Barrier", 1, 2);
setupMockHelloBackWrite(stream, "Barrier", 1, 2, "test client");
helloBack.handleHello(&stream, clientName);
}
// If the client is protocol version 1.8 and the server is 1.6, the client
// should downgrade and respond with the server version.
TEST(HelloBackTests, handleHello_synergyProtocolCompat_wroteHelloBack)
{
auto deps = std::make_shared<NiceMock<MockDeps>>();
HelloBack helloBack(deps, 1, 8);
NiceMock<MockStream> stream;
const std::string clientName = "test client";
setupMockHelloRead(stream, "Synergy", 1, 6);
setupMockHelloBackWrite(stream, "Synergy", 1, 6, "test client");
helloBack.handleHello(&stream, clientName);
}