mirror of https://github.com/qTox/qTox.git
Browse Source
* Introduced ToxExt and CoreExt abstraction * Along with interfaces for mocking and unit testing * Add "supportedExtensions" concept to Friend * Dispatch messages to CoreExt instead of Core when friend supports extended messages * Only split messages for core when extended messages are unavailable * Offline message engine/History not altered. Currently only valid for an existing session after extension negotiation has completedreviewable/pr5932/r15
20 changed files with 659 additions and 30 deletions
@ -0,0 +1,159 @@
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
Copyright © 2019-2020 by The qTox Project Contributors |
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
qTox is libre 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. |
||||
|
||||
qTox 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 qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include "coreext.h" |
||||
#include "toxstring.h" |
||||
|
||||
#include <QDateTime> |
||||
#include <QTimeZone> |
||||
#include <QtCore> |
||||
|
||||
#include <memory> |
||||
#include <cassert> |
||||
|
||||
extern "C" { |
||||
#include <toxext/toxext.h> |
||||
#include <tox_extension_messages.h> |
||||
} |
||||
|
||||
std::unique_ptr<CoreExt> CoreExt::makeCoreExt(Tox* core) { |
||||
auto toxExtPtr = toxext_init(core); |
||||
if (!toxExtPtr) { |
||||
return nullptr; |
||||
} |
||||
|
||||
auto toxExt = ExtensionPtr<ToxExt>(toxExtPtr, toxext_free); |
||||
return std::unique_ptr<CoreExt>(new CoreExt(std::move(toxExt))); |
||||
} |
||||
|
||||
CoreExt::CoreExt(ExtensionPtr<ToxExt> toxExt_) |
||||
: toxExt(std::move(toxExt_)) |
||||
, toxExtMessages(nullptr, nullptr) |
||||
{ |
||||
toxExtMessages = ExtensionPtr<ToxExtensionMessages>( |
||||
tox_extension_messages_register( |
||||
toxExt.get(), |
||||
CoreExt::onExtendedMessageReceived, |
||||
CoreExt::onExtendedMessageReceipt, |
||||
CoreExt::onExtendedMessageNegotiation, |
||||
this, |
||||
TOX_EXTENSION_MESSAGES_DEFAULT_MAX_RECEIVING_MESSAGE_SIZE), |
||||
tox_extension_messages_free); |
||||
} |
||||
|
||||
void CoreExt::process() |
||||
{ |
||||
toxext_iterate(toxExt.get()); |
||||
} |
||||
|
||||
void CoreExt::onLosslessPacket(uint32_t friendId, const uint8_t* data, size_t length) |
||||
{ |
||||
if (is_toxext_packet(data, length)) { |
||||
toxext_handle_lossless_custom_packet(toxExt.get(), friendId, data, length); |
||||
} |
||||
} |
||||
|
||||
CoreExt::Packet::Packet( |
||||
ToxExtPacketList* packetList, |
||||
ToxExtensionMessages* toxExtMessages, |
||||
uint32_t friendId, |
||||
PacketPassKey) |
||||
: toxExtMessages(toxExtMessages) |
||||
, packetList(packetList) |
||||
, friendId(friendId) |
||||
{} |
||||
|
||||
std::unique_ptr<ICoreExtPacket> CoreExt::getPacket(uint32_t friendId) |
||||
{ |
||||
return std::unique_ptr<Packet>(new Packet( |
||||
toxext_packet_list_create(toxExt.get(), friendId), |
||||
toxExtMessages.get(), |
||||
friendId, |
||||
PacketPassKey{})); |
||||
} |
||||
|
||||
uint64_t CoreExt::Packet::addExtendedMessage(QString message) |
||||
{ |
||||
if (hasBeenSent) { |
||||
assert(false); |
||||
qWarning() << "Invalid use of CoreExt::Packet"; |
||||
// Hope that UINT64_MAX will never collide with an actual receipt num
|
||||
// that we care about
|
||||
return UINT64_MAX; |
||||
} |
||||
|
||||
ToxString toxString(message); |
||||
Tox_Extension_Messages_Error err; |
||||
|
||||
return tox_extension_messages_append( |
||||
toxExtMessages, |
||||
packetList, |
||||
toxString.data(), |
||||
toxString.size(), |
||||
friendId, |
||||
&err); |
||||
} |
||||
|
||||
bool CoreExt::Packet::send() |
||||
{ |
||||
auto ret = toxext_send(packetList); |
||||
if (ret != TOXEXT_SUCCESS) { |
||||
qWarning() << "Failed to send packet"; |
||||
} |
||||
// Indicate we've sent the packet even on failure since our packetlist will
|
||||
// be invalid no matter what
|
||||
hasBeenSent = true; |
||||
return ret == TOXEXT_SUCCESS; |
||||
} |
||||
|
||||
void CoreExt::onFriendStatusChanged(uint32_t friendId, Status::Status status) |
||||
{ |
||||
const auto prevStatusIt = currentStatuses.find(friendId); |
||||
const auto prevStatus = prevStatusIt == currentStatuses.end() |
||||
? Status::Status::Offline : prevStatusIt->second; |
||||
|
||||
currentStatuses[friendId] = status; |
||||
|
||||
// Does not depend on prevStatus since prevStatus could be newly
|
||||
// constructed. In this case we should still ensure the rest of the system
|
||||
// knows there is no extension support
|
||||
if (status == Status::Status::Offline) { |
||||
emit extendedMessageSupport(friendId, false); |
||||
} else if (prevStatus == Status::Status::Offline) { |
||||
tox_extension_messages_negotiate(toxExtMessages.get(), friendId); |
||||
} |
||||
} |
||||
|
||||
void CoreExt::onExtendedMessageReceived(uint32_t friendId, const uint8_t* data, size_t size, void* userData) |
||||
{ |
||||
QString msg = ToxString(data, size).getQString(); |
||||
emit static_cast<CoreExt*>(userData)->extendedMessageReceived(friendId, msg); |
||||
} |
||||
|
||||
void CoreExt::onExtendedMessageReceipt(uint32_t friendId, uint64_t receiptId, void* userData) |
||||
{ |
||||
emit static_cast<CoreExt*>(userData)->extendedReceiptReceived(friendId, receiptId); |
||||
} |
||||
|
||||
void CoreExt::onExtendedMessageNegotiation(uint32_t friendId, bool compatible, uint64_t maxMessageSize, void* userData) |
||||
{ |
||||
auto coreExt = static_cast<CoreExt*>(userData); |
||||
emit coreExt->extendedMessageSupport(friendId, compatible); |
||||
} |
||||
|
@ -0,0 +1,145 @@
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
Copyright © 2019-2020 by The qTox Project Contributors |
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
qTox is libre 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. |
||||
|
||||
qTox 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 qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include "src/model/status.h" |
||||
#include "icoreextpacket.h" |
||||
|
||||
#include <QObject> |
||||
#include <QMap> |
||||
|
||||
#include <bitset> |
||||
#include <memory> |
||||
#include <unordered_map> |
||||
|
||||
struct Tox; |
||||
struct ToxExt; |
||||
struct ToxExtensionMessages; |
||||
struct ToxExtPacketList; |
||||
|
||||
/**
|
||||
* Bridge between the toxext library and the rest of qTox. |
||||
*/ |
||||
class CoreExt : public QObject, public ICoreExtPacketAllocator |
||||
{ |
||||
Q_OBJECT |
||||
private: |
||||
// PassKey idiom to prevent others from making PacketBuilders
|
||||
struct PacketPassKey {}; |
||||
public: |
||||
|
||||
/**
|
||||
* @brief Creates a CoreExt instance. Using a pointer here makes our |
||||
* registrations with extensions significantly easier to manage |
||||
* |
||||
* @param[in] pointer to core tox instance |
||||
* @return CoreExt on success, nullptr on failure |
||||
*/ |
||||
static std::unique_ptr<CoreExt> makeCoreExt(Tox* core); |
||||
|
||||
// We do registration with our own pointer, need to ensure we're in a stable location
|
||||
CoreExt(CoreExt const& other) = delete; |
||||
CoreExt(CoreExt&& other) = delete; |
||||
CoreExt& operator=(CoreExt const& other) = delete; |
||||
CoreExt& operator=(CoreExt&& other) = delete; |
||||
|
||||
/**
|
||||
* @brief Periodic service function |
||||
*/ |
||||
void process(); |
||||
|
||||
/**
|
||||
* @brief Handles extension related lossless packets |
||||
* @param[in] friendId Core id of friend |
||||
* @param[in] data Packet data |
||||
* @param[in] length Length of packet data |
||||
*/ |
||||
void onLosslessPacket(uint32_t friendId, const uint8_t* data, size_t length); |
||||
|
||||
/**
|
||||
* See documentation of ICoreExtPacket |
||||
*/ |
||||
class Packet : public ICoreExtPacket |
||||
{ |
||||
public: |
||||
/**
|
||||
* @brief Internal constructor for a packet. |
||||
*/ |
||||
Packet( |
||||
ToxExtPacketList* packetList, |
||||
ToxExtensionMessages* toxExtMessages, |
||||
uint32_t friendId, |
||||
PacketPassKey); |
||||
|
||||
// Delete copy constructor, we shouldn't be able to copy
|
||||
Packet(Packet const& other) = delete; |
||||
|
||||
Packet(Packet&& other) |
||||
{ |
||||
toxExtMessages = other.toxExtMessages; |
||||
packetList = other.packetList; |
||||
friendId = other.friendId; |
||||
hasBeenSent = other.hasBeenSent; |
||||
other.toxExtMessages = nullptr; |
||||
other.packetList = nullptr; |
||||
other.friendId = 0; |
||||
other.hasBeenSent = false; |
||||
} |
||||
|
||||
uint64_t addExtendedMessage(QString message) override; |
||||
|
||||
bool send() override; |
||||
private: |
||||
bool hasBeenSent = false; |
||||
// Note: non-owning pointer
|
||||
ToxExtensionMessages* toxExtMessages; |
||||
// Note: packetList is freed on send() call
|
||||
ToxExtPacketList* packetList; |
||||
uint32_t friendId; |
||||
}; |
||||
|
||||
std::unique_ptr<ICoreExtPacket> getPacket(uint32_t friendId) override; |
||||
|
||||
signals: |
||||
void extendedMessageReceived(uint32_t friendId, const QString& message); |
||||
void extendedReceiptReceived(uint32_t friendId, uint64_t receiptId); |
||||
void extendedMessageSupport(uint32_t friendId, bool supported); |
||||
|
||||
public slots: |
||||
void onFriendStatusChanged(uint32_t friendId, Status::Status status); |
||||
|
||||
private: |
||||
|
||||
static void onExtendedMessageReceived(uint32_t friendId, const uint8_t* data, size_t size, void* userData); |
||||
static void onExtendedMessageReceipt(uint32_t friendId, uint64_t receiptId, void* userData); |
||||
static void onExtendedMessageNegotiation(uint32_t friendId, bool compatible, uint64_t maxMessageSize, void* userData); |
||||
|
||||
// A little extra cost to hide the deleters, but this lets us fwd declare
|
||||
// and prevent any extension headers from leaking out to the rest of the
|
||||
// system
|
||||
template <class T> |
||||
using ExtensionPtr = std::unique_ptr<T, void(*)(T*)>; |
||||
|
||||
CoreExt(ExtensionPtr<ToxExt> toxExt); |
||||
|
||||
std::unordered_map<uint32_t, Status::Status> currentStatuses; |
||||
ExtensionPtr<ToxExt> toxExt; |
||||
ExtensionPtr<ToxExtensionMessages> toxExtMessages; |
||||
}; |
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright © 2019 by The qTox Project Contributors |
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
qTox is libre 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. |
||||
|
||||
qTox 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 qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <bitset> |
||||
|
||||
// Do not use enum class because we use these as indexes frequently (see ExtensionSet)
|
||||
struct ExtensionType |
||||
{ |
||||
enum { |
||||
messages, |
||||
max |
||||
}; |
||||
}; |
||||
using ExtensionSet = std::bitset<ExtensionType::max>; |
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright © 2019 by The qTox Project Contributors |
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox. |
||||
|
||||
qTox is libre 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. |
||||
|
||||
qTox 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 qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <QDateTime> |
||||
|
||||
#include <cstdint> |
||||
#include <memory> |
||||
|
||||
/**
|
||||
* Abstraction around the toxext packet. The toxext flow is to allow several extensions |
||||
* to tack onto the same packet before sending it to avoid needing the toxext overhead |
||||
* for every single extension. This abstraction models a toxext packet list. |
||||
* |
||||
* Intent is to retrieve a ICoreExtPacket from an ICoreExtPacketAllocator, append all |
||||
* relevant extension data, and then finally send the packet. After sending the packet |
||||
* is no longer guaranteed to be valid. |
||||
*/ |
||||
class ICoreExtPacket |
||||
{ |
||||
public: |
||||
virtual ~ICoreExtPacket() = default; |
||||
|
||||
/**
|
||||
* @brief Adds message to packet |
||||
* @return Extended message receipt, UINT64_MAX on failure |
||||
* @note Any other extensions related to this message have to be added |
||||
* _before_ the message itself |
||||
*/ |
||||
virtual uint64_t addExtendedMessage(QString message) = 0; |
||||
|
||||
/**
|
||||
* @brief Consumes the packet constructed with PacketBuilder packet and |
||||
* sends it to toxext |
||||
*/ |
||||
virtual bool send() = 0; |
||||
}; |
||||
|
||||
/**
|
||||
* Provider of toxext packets |
||||
*/ |
||||
class ICoreExtPacketAllocator |
||||
{ |
||||
public: |
||||
virtual ~ICoreExtPacketAllocator() = default; |
||||
|
||||
/**
|
||||
* @brief Gets a new packet builder for friend with core friend id friendId |
||||
*/ |
||||
virtual std::unique_ptr<ICoreExtPacket> getPacket(uint32_t friendId) = 0; |
||||
}; |
Loading…
Reference in new issue