fix: enforce clipboard receive size limits
This commit is contained in:
parent
205a3c803e
commit
8a535fd5dd
12 changed files with 358 additions and 31 deletions
|
|
@ -175,6 +175,11 @@ NetworkAddress Client::getServerAddress() const
|
||||||
return m_serverAddress;
|
return m_serverAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t Client::getMaximumClipboardSizeBytes() const
|
||||||
|
{
|
||||||
|
return m_maximumClipboardSize * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
void *Client::getEventTarget() const
|
void *Client::getEventTarget() const
|
||||||
{
|
{
|
||||||
return m_screen->getEventTarget();
|
return m_screen->getEventTarget();
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ public:
|
||||||
{
|
{
|
||||||
return m_resolvedAddressesCount;
|
return m_resolvedAddressesCount;
|
||||||
}
|
}
|
||||||
|
size_t getMaximumClipboardSizeBytes() const;
|
||||||
|
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -530,24 +530,29 @@ void ServerProxy::leave()
|
||||||
void ServerProxy::setClipboard()
|
void ServerProxy::setClipboard()
|
||||||
{
|
{
|
||||||
// parse
|
// parse
|
||||||
static std::string dataCached;
|
|
||||||
ClipboardID id;
|
ClipboardID id;
|
||||||
uint32_t seq;
|
uint32_t seq;
|
||||||
|
|
||||||
auto r = ClipboardChunk::assemble(m_stream, dataCached, id, seq);
|
auto r = ClipboardChunk::assemble(
|
||||||
|
m_stream, m_clipboardDataCached, id, seq, m_clipboardChunkState, m_client->getMaximumClipboardSizeBytes()
|
||||||
|
);
|
||||||
|
|
||||||
if (r == TransferState::Started) {
|
if (r == TransferState::Started) {
|
||||||
size_t size = ClipboardChunk::getExpectedSize();
|
size_t size = ClipboardChunk::getExpectedSize(m_clipboardChunkState);
|
||||||
LOG_DEBUG("receiving clipboard %d size=%d", id, size);
|
LOG_DEBUG("receiving clipboard %d size=%zu", id, size);
|
||||||
} else if (r == TransferState::Finished) {
|
} else if (r == TransferState::Finished) {
|
||||||
LOG_DEBUG("received clipboard %d size=%d", id, dataCached.size());
|
LOG_DEBUG("received clipboard %d size=%zu", id, m_clipboardDataCached.size());
|
||||||
|
|
||||||
// forward
|
// forward
|
||||||
Clipboard clipboard;
|
Clipboard clipboard;
|
||||||
clipboard.unmarshall(dataCached, 0);
|
clipboard.unmarshall(m_clipboardDataCached, 0);
|
||||||
m_client->setClipboard(id, &clipboard);
|
m_client->setClipboard(id, &clipboard);
|
||||||
|
m_clipboardDataCached.clear();
|
||||||
|
m_clipboardDataCached.shrink_to_fit();
|
||||||
|
|
||||||
LOG_INFO("clipboard was updated");
|
LOG_INFO("clipboard was updated");
|
||||||
|
} else if (r == TransferState::Error) {
|
||||||
|
m_client->disconnect("invalid clipboard data from server");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "deskflow/ClipboardChunk.h"
|
||||||
#include "deskflow/ClipboardTypes.h"
|
#include "deskflow/ClipboardTypes.h"
|
||||||
#include "deskflow/KeyTypes.h"
|
#include "deskflow/KeyTypes.h"
|
||||||
#include "deskflow/KeyboardLayoutManager.h"
|
#include "deskflow/KeyboardLayoutManager.h"
|
||||||
|
|
@ -124,6 +125,8 @@ private:
|
||||||
MessageParser m_parser = &ServerProxy::parseHandshakeMessage;
|
MessageParser m_parser = &ServerProxy::parseHandshakeMessage;
|
||||||
IEventQueue *m_events = nullptr;
|
IEventQueue *m_events = nullptr;
|
||||||
std::string m_serverLayout = "";
|
std::string m_serverLayout = "";
|
||||||
|
std::string m_clipboardDataCached;
|
||||||
|
ClipboardChunkAssemblyState m_clipboardChunkState;
|
||||||
bool m_isUserNotifiedAboutLayoutSyncError = false;
|
bool m_isUserNotifiedAboutLayoutSyncError = false;
|
||||||
deskflow::KeyboardLayoutManager m_layoutManager;
|
deskflow::KeyboardLayoutManager m_layoutManager;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,22 @@
|
||||||
#include "deskflow/ProtocolUtil.h"
|
#include "deskflow/ProtocolUtil.h"
|
||||||
#include "io/IStream.h"
|
#include "io/IStream.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
size_t ClipboardChunk::s_expectedSize = 0;
|
namespace {
|
||||||
|
|
||||||
|
void clearCachedData(std::string &dataCached)
|
||||||
|
{
|
||||||
|
dataCached.clear();
|
||||||
|
dataCached.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wouldExceed(size_t currentSize, size_t extraSize, size_t limit)
|
||||||
|
{
|
||||||
|
return currentSize > limit || extraSize > limit - currentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
ClipboardChunk::ClipboardChunk(size_t size) : Chunk(size)
|
ClipboardChunk::ClipboardChunk(size_t size) : Chunk(size)
|
||||||
{
|
{
|
||||||
|
|
@ -62,37 +76,88 @@ ClipboardChunk *ClipboardChunk::end(ClipboardID id, uint32_t sequence)
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
TransferState
|
TransferState ClipboardChunk::assemble(
|
||||||
ClipboardChunk::assemble(deskflow::IStream *stream, std::string &dataCached, ClipboardID &id, uint32_t &sequence)
|
deskflow::IStream *stream, std::string &dataCached, ClipboardID &id, uint32_t &sequence,
|
||||||
|
ClipboardChunkAssemblyState &state, size_t maxDataSize
|
||||||
|
)
|
||||||
{
|
{
|
||||||
using enum TransferState;
|
using enum TransferState;
|
||||||
uint8_t mark;
|
uint8_t mark;
|
||||||
std::string data;
|
std::string data;
|
||||||
|
auto reset = [&]() {
|
||||||
|
state = {};
|
||||||
|
clearCachedData(dataCached);
|
||||||
|
};
|
||||||
|
|
||||||
if (!ProtocolUtil::readf(stream, kMsgDClipboard + 4, &id, &sequence, &mark, &data)) {
|
if (!ProtocolUtil::readf(stream, kMsgDClipboard + 4, &id, &sequence, &mark, &data)) {
|
||||||
|
reset();
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id >= kClipboardEnd) {
|
||||||
|
LOG_ERR("clipboard chunk invalid id: %d", id);
|
||||||
|
reset();
|
||||||
return Error;
|
return Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mark == ChunkType::DataStart) {
|
if (mark == ChunkType::DataStart) {
|
||||||
s_expectedSize = QString::fromStdString(data).toULong();
|
bool ok = false;
|
||||||
LOG_DEBUG("start receiving clipboard data");
|
const auto expected = QString::fromStdString(data).toULongLong(&ok);
|
||||||
dataCached.clear();
|
if (!ok || expected > std::numeric_limits<size_t>::max()) {
|
||||||
|
LOG_ERR("clipboard invalid size header: %s", data.c_str());
|
||||||
|
reset();
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCachedData(dataCached);
|
||||||
|
state.expectedSize = static_cast<size_t>(expected);
|
||||||
|
state.active = true;
|
||||||
|
|
||||||
|
if (state.expectedSize > maxDataSize) {
|
||||||
|
LOG_ERR("clipboard size exceeds limit, size: %zu, limit: %zu", state.expectedSize, maxDataSize);
|
||||||
|
reset();
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("start receiving clipboard data, expected size=%zu", state.expectedSize);
|
||||||
return Started;
|
return Started;
|
||||||
} else if (mark == ChunkType::DataChunk) {
|
} else if (mark == ChunkType::DataChunk) {
|
||||||
|
if (!state.active) {
|
||||||
|
LOG_ERR("clipboard data chunk before start");
|
||||||
|
reset();
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wouldExceed(dataCached.size(), data.size(), state.expectedSize)) {
|
||||||
|
LOG_ERR(
|
||||||
|
"clipboard size exceeds declared, size: %zu, declared: %zu", dataCached.size() + data.size(),
|
||||||
|
state.expectedSize
|
||||||
|
);
|
||||||
|
reset();
|
||||||
|
return Error;
|
||||||
|
}
|
||||||
|
|
||||||
dataCached.append(data);
|
dataCached.append(data);
|
||||||
return TransferState::InProgress;
|
return TransferState::InProgress;
|
||||||
} else if (mark == ChunkType::DataEnd) {
|
} else if (mark == ChunkType::DataEnd) {
|
||||||
// validate
|
if (!state.active) {
|
||||||
if (id >= kClipboardEnd) {
|
LOG_ERR("clipboard end chunk before start");
|
||||||
|
reset();
|
||||||
return Error;
|
return Error;
|
||||||
} else if (s_expectedSize != dataCached.size()) {
|
}
|
||||||
LOG_ERR("corrupted clipboard data, expected size=%d actual size=%d", s_expectedSize, dataCached.size());
|
|
||||||
|
state.active = false;
|
||||||
|
|
||||||
|
if (state.expectedSize != dataCached.size()) {
|
||||||
|
LOG_ERR("corrupted clipboard data, expected size=%zu actual size=%zu", state.expectedSize, dataCached.size());
|
||||||
|
reset();
|
||||||
return Error;
|
return Error;
|
||||||
}
|
}
|
||||||
return Finished;
|
return Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_ERR("clipboard transmission failed: unknown error");
|
LOG_ERR("unknown clipboard chunk mark");
|
||||||
|
reset();
|
||||||
return Error;
|
return Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "deskflow/ClipboardTypes.h"
|
#include "deskflow/ClipboardTypes.h"
|
||||||
#include "deskflow/ProtocolTypes.h"
|
#include "deskflow/ProtocolTypes.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
constexpr static auto s_clipboardChunkMetaSize = 7;
|
constexpr static auto s_clipboardChunkMetaSize = 7;
|
||||||
|
|
@ -18,6 +19,12 @@ namespace deskflow {
|
||||||
class IStream;
|
class IStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ClipboardChunkAssemblyState
|
||||||
|
{
|
||||||
|
size_t expectedSize = 0;
|
||||||
|
bool active = false;
|
||||||
|
};
|
||||||
|
|
||||||
class ClipboardChunk : public Chunk
|
class ClipboardChunk : public Chunk
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -27,16 +34,15 @@ public:
|
||||||
static ClipboardChunk *data(ClipboardID id, uint32_t sequence, const std::string &data);
|
static ClipboardChunk *data(ClipboardID id, uint32_t sequence, const std::string &data);
|
||||||
static ClipboardChunk *end(ClipboardID id, uint32_t sequence);
|
static ClipboardChunk *end(ClipboardID id, uint32_t sequence);
|
||||||
|
|
||||||
static TransferState
|
static TransferState assemble(
|
||||||
assemble(deskflow::IStream *stream, std::string &dataCached, ClipboardID &id, uint32_t &sequence);
|
deskflow::IStream *stream, std::string &dataCached, ClipboardID &id, uint32_t &sequence,
|
||||||
|
ClipboardChunkAssemblyState &state, size_t maxDataSize
|
||||||
|
);
|
||||||
|
|
||||||
static void send(deskflow::IStream *stream, void *data);
|
static void send(deskflow::IStream *stream, void *data);
|
||||||
|
|
||||||
static size_t getExpectedSize()
|
static size_t getExpectedSize(const ClipboardChunkAssemblyState &state)
|
||||||
{
|
{
|
||||||
return s_expectedSize;
|
return state.expectedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
static size_t s_expectedSize;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -51,27 +51,34 @@ void ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard *clipboard)
|
||||||
bool ClientProxy1_6::recvClipboard()
|
bool ClientProxy1_6::recvClipboard()
|
||||||
{
|
{
|
||||||
// parse message
|
// parse message
|
||||||
static std::string dataCached;
|
|
||||||
ClipboardID id;
|
ClipboardID id;
|
||||||
uint32_t seq;
|
uint32_t seq;
|
||||||
|
|
||||||
if (auto r = ClipboardChunk::assemble(getStream(), dataCached, id, seq); r == TransferState::Started) {
|
auto r = ClipboardChunk::assemble(
|
||||||
size_t size = ClipboardChunk::getExpectedSize();
|
getStream(), m_clipboardDataCached, id, seq, m_clipboardChunkState, m_server->getMaximumClipboardSizeBytes()
|
||||||
LOG_DEBUG("receiving clipboard %d size=%d", id, size);
|
);
|
||||||
|
|
||||||
|
if (r == TransferState::Started) {
|
||||||
|
size_t size = ClipboardChunk::getExpectedSize(m_clipboardChunkState);
|
||||||
|
LOG_DEBUG("receiving clipboard %d size=%zu", id, size);
|
||||||
} else if (r == TransferState::Finished) {
|
} else if (r == TransferState::Finished) {
|
||||||
LOG(
|
LOG(
|
||||||
(CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seq,
|
(CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%zu", getName().c_str(), id, seq,
|
||||||
dataCached.size())
|
m_clipboardDataCached.size())
|
||||||
);
|
);
|
||||||
// save clipboard
|
// save clipboard
|
||||||
m_clipboard[id].m_clipboard.unmarshall(dataCached, 0);
|
m_clipboard[id].m_clipboard.unmarshall(m_clipboardDataCached, 0);
|
||||||
m_clipboard[id].m_sequenceNumber = seq;
|
m_clipboard[id].m_sequenceNumber = seq;
|
||||||
|
m_clipboardDataCached.clear();
|
||||||
|
m_clipboardDataCached.shrink_to_fit();
|
||||||
|
|
||||||
// notify
|
// notify
|
||||||
auto *info = new ClipboardInfo;
|
auto *info = new ClipboardInfo;
|
||||||
info->m_id = id;
|
info->m_id = id;
|
||||||
info->m_sequenceNumber = seq;
|
info->m_sequenceNumber = seq;
|
||||||
m_events->addEvent(Event(EventTypes::ClipboardChanged, getEventTarget(), info));
|
m_events->addEvent(Event(EventTypes::ClipboardChanged, getEventTarget(), info));
|
||||||
|
} else if (r == TransferState::Error) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "deskflow/ClipboardChunk.h"
|
||||||
#include "server/ClientProxy1_5.h"
|
#include "server/ClientProxy1_5.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class Server;
|
class Server;
|
||||||
class IEventQueue;
|
class IEventQueue;
|
||||||
|
|
||||||
|
|
@ -23,4 +26,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IEventQueue *m_events;
|
IEventQueue *m_events;
|
||||||
|
std::string m_clipboardDataCached;
|
||||||
|
ClipboardChunkAssemblyState m_clipboardChunkState;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,11 @@ Server::~Server()
|
||||||
removeClient(m_primaryClient);
|
removeClient(m_primaryClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t Server::getMaximumClipboardSizeBytes() const
|
||||||
|
{
|
||||||
|
return m_maximumClipboardSize * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
bool Server::setConfig(const ServerConfig &config)
|
bool Server::setConfig(const ServerConfig &config)
|
||||||
{
|
{
|
||||||
// refuse configuration if it doesn't include the primary screen
|
// refuse configuration if it doesn't include the primary screen
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,7 @@ public:
|
||||||
*/
|
*/
|
||||||
void getClients(std::vector<std::string> &list) const;
|
void getClients(std::vector<std::string> &list) const;
|
||||||
void sendConnectedClientsIpc() const;
|
void sendConnectedClientsIpc() const;
|
||||||
|
size_t getMaximumClipboardSizeBytes() const;
|
||||||
|
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,162 @@
|
||||||
|
|
||||||
#include "deskflow/ClipboardChunk.h"
|
#include "deskflow/ClipboardChunk.h"
|
||||||
#include "deskflow/ProtocolTypes.h"
|
#include "deskflow/ProtocolTypes.h"
|
||||||
|
#include "deskflow/ProtocolUtil.h"
|
||||||
|
#include "io/IStream.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class MemoryStream : public deskflow::IStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void push(const std::string &bytes)
|
||||||
|
{
|
||||||
|
m_queue.push_back(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() override
|
||||||
|
{
|
||||||
|
m_queue.clear();
|
||||||
|
m_inputShutdown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t read(void *buffer, uint32_t n) override
|
||||||
|
{
|
||||||
|
if (m_inputShutdown || m_queue.empty() || n == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &front = m_queue.front();
|
||||||
|
const size_t take = std::min(static_cast<size_t>(n), front.size());
|
||||||
|
if (buffer != nullptr) {
|
||||||
|
std::memcpy(buffer, front.data(), take);
|
||||||
|
}
|
||||||
|
|
||||||
|
front.erase(0, take);
|
||||||
|
if (front.empty()) {
|
||||||
|
m_queue.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<uint32_t>(take);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(const void *, uint32_t) override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush() override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdownInput() override
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdownOutput() override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void *getEventTarget() const override
|
||||||
|
{
|
||||||
|
return const_cast<MemoryStream *>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isReady() const override
|
||||||
|
{
|
||||||
|
return !m_inputShutdown && !m_queue.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getSize() const override
|
||||||
|
{
|
||||||
|
size_t total = 0;
|
||||||
|
for (const auto &chunk : m_queue) {
|
||||||
|
total += chunk.size();
|
||||||
|
}
|
||||||
|
return static_cast<uint32_t>(std::min<size_t>(total, UINT32_MAX));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::deque<std::string> m_queue;
|
||||||
|
bool m_inputShutdown = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BufferWriteStream : public deskflow::IStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const std::string &str() const
|
||||||
|
{
|
||||||
|
return m_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() override
|
||||||
|
{
|
||||||
|
m_outputShutdown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t read(void *, uint32_t) override
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(const void *buffer, uint32_t n) override
|
||||||
|
{
|
||||||
|
if (!m_outputShutdown && n != 0) {
|
||||||
|
m_buffer.append(static_cast<const char *>(buffer), n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush() override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdownInput() override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdownOutput() override
|
||||||
|
{
|
||||||
|
m_outputShutdown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *getEventTarget() const override
|
||||||
|
{
|
||||||
|
return const_cast<BufferWriteStream *>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isReady() const override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getSize() const override
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_buffer;
|
||||||
|
bool m_outputShutdown = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string encodeClipboardMsg(ClipboardID id, uint32_t seq, uint8_t mark, const std::string &data)
|
||||||
|
{
|
||||||
|
BufferWriteStream stream;
|
||||||
|
auto payload = data;
|
||||||
|
ProtocolUtil::writef(&stream, kMsgDClipboard + 4, id, seq, mark, &payload);
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void ClipboardChunksTests::initTestCase()
|
||||||
|
{
|
||||||
|
m_log.setFilter(LogLevel::Level::Debug);
|
||||||
|
}
|
||||||
|
|
||||||
void ClipboardChunksTests::startFormatData()
|
void ClipboardChunksTests::startFormatData()
|
||||||
{
|
{
|
||||||
|
|
@ -70,4 +226,63 @@ void ClipboardChunksTests::endFormatData()
|
||||||
delete chunk;
|
delete chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClipboardChunksTests::assembleAllowsDataAtExpectedSizeAndLimit()
|
||||||
|
{
|
||||||
|
MemoryStream stream;
|
||||||
|
stream.push(encodeClipboardMsg(0, 7, ChunkType::DataStart, "4"));
|
||||||
|
stream.push(encodeClipboardMsg(0, 7, ChunkType::DataChunk, "AB"));
|
||||||
|
stream.push(encodeClipboardMsg(0, 7, ChunkType::DataChunk, "CD"));
|
||||||
|
stream.push(encodeClipboardMsg(0, 7, ChunkType::DataEnd, ""));
|
||||||
|
|
||||||
|
std::string cached;
|
||||||
|
ClipboardID id = kClipboardEnd;
|
||||||
|
uint32_t seq = 0;
|
||||||
|
ClipboardChunkAssemblyState state;
|
||||||
|
|
||||||
|
QCOMPARE(ClipboardChunk::assemble(&stream, cached, id, seq, state, 4), TransferState::Started);
|
||||||
|
QCOMPARE(ClipboardChunk::assemble(&stream, cached, id, seq, state, 4), TransferState::InProgress);
|
||||||
|
QCOMPARE(ClipboardChunk::assemble(&stream, cached, id, seq, state, 4), TransferState::InProgress);
|
||||||
|
QCOMPARE(ClipboardChunk::assemble(&stream, cached, id, seq, state, 4), TransferState::Finished);
|
||||||
|
|
||||||
|
QCOMPARE(cached, std::string("ABCD"));
|
||||||
|
QCOMPARE(id, static_cast<ClipboardID>(0));
|
||||||
|
QCOMPARE(seq, static_cast<uint32_t>(7));
|
||||||
|
QCOMPARE(ClipboardChunk::getExpectedSize(state), static_cast<size_t>(4));
|
||||||
|
QVERIFY(!state.active);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClipboardChunksTests::assembleRejectsDataBeyondExpectedSize()
|
||||||
|
{
|
||||||
|
MemoryStream stream;
|
||||||
|
stream.push(encodeClipboardMsg(0, 7, ChunkType::DataStart, "1"));
|
||||||
|
stream.push(encodeClipboardMsg(0, 7, ChunkType::DataChunk, "AA"));
|
||||||
|
|
||||||
|
std::string cached;
|
||||||
|
ClipboardID id = kClipboardEnd;
|
||||||
|
uint32_t seq = 0;
|
||||||
|
ClipboardChunkAssemblyState state;
|
||||||
|
|
||||||
|
QCOMPARE(ClipboardChunk::assemble(&stream, cached, id, seq, state, 1024), TransferState::Started);
|
||||||
|
QCOMPARE(ClipboardChunk::assemble(&stream, cached, id, seq, state, 1024), TransferState::Error);
|
||||||
|
QVERIFY(cached.empty());
|
||||||
|
QCOMPARE(ClipboardChunk::getExpectedSize(state), static_cast<size_t>(0));
|
||||||
|
QVERIFY(!state.active);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClipboardChunksTests::assembleRejectsExpectedSizeBeyondLimit()
|
||||||
|
{
|
||||||
|
MemoryStream stream;
|
||||||
|
stream.push(encodeClipboardMsg(0, 7, ChunkType::DataStart, "8"));
|
||||||
|
|
||||||
|
std::string cached;
|
||||||
|
ClipboardID id = kClipboardEnd;
|
||||||
|
uint32_t seq = 0;
|
||||||
|
ClipboardChunkAssemblyState state;
|
||||||
|
|
||||||
|
QCOMPARE(ClipboardChunk::assemble(&stream, cached, id, seq, state, 4), TransferState::Error);
|
||||||
|
QVERIFY(cached.empty());
|
||||||
|
QCOMPARE(ClipboardChunk::getExpectedSize(state), static_cast<size_t>(0));
|
||||||
|
QVERIFY(!state.active);
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(ClipboardChunksTests)
|
QTEST_MAIN(ClipboardChunksTests)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "base/Log.h"
|
||||||
|
|
||||||
#include <QTest>
|
#include <QTest>
|
||||||
|
|
||||||
class ClipboardChunksTests : public QObject
|
class ClipboardChunksTests : public QObject
|
||||||
|
|
@ -11,7 +13,14 @@ class ClipboardChunksTests : public QObject
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
// Test are run in order top to bottom
|
// Test are run in order top to bottom
|
||||||
|
void initTestCase();
|
||||||
void startFormatData();
|
void startFormatData();
|
||||||
void formatDataChunk();
|
void formatDataChunk();
|
||||||
void endFormatData();
|
void endFormatData();
|
||||||
|
void assembleAllowsDataAtExpectedSizeAndLimit();
|
||||||
|
void assembleRejectsDataBeyondExpectedSize();
|
||||||
|
void assembleRejectsExpectedSizeBeyondLimit();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Log m_log;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue