Skip to content

Commit

Permalink
feat(messages): Multipacket message support
Browse files Browse the repository at this point in the history
* 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 completed
  • Loading branch information
sphaerophoria authored and anthonybilinski committed Jan 30, 2021
1 parent abad3fc commit 7474c6d
Show file tree
Hide file tree
Showing 20 changed files with 659 additions and 30 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ set(${PROJECT_NAME}_SOURCES
src/chatlog/textformatter.h
src/core/coreav.cpp
src/core/coreav.h
src/core/coreext.cpp
src/core/coreext.h
src/core/core.cpp
src/core/corefile.cpp
src/core/corefile.h
Expand Down
7 changes: 7 additions & 0 deletions cmake/Dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ find_package(Qt5Test REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Xml REQUIRED)

find_package(ToxExt REQUIRED)
find_package(ToxExtensionMessages REQUIRED)

function(add_dependency)
set(ALL_LIBRARIES ${ALL_LIBRARIES} ${ARGN} PARENT_SCOPE)
endfunction()
Expand All @@ -47,6 +50,10 @@ add_dependency(
Qt5::Widgets
Qt5::Xml)

add_dependency(
ToxExt::ToxExt
ToxExtensionMessages::ToxExtensionMessages)

include(CMakeParseArguments)

function(search_dependency pkg)
Expand Down
27 changes: 27 additions & 0 deletions src/core/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "core.h"
#include "coreav.h"
#include "corefile.h"

#include "src/core/coreext.h"
#include "src/core/dhtserver.h"
#include "src/core/icoresettings.h"
#include "src/core/toxlogger.h"
Expand Down Expand Up @@ -515,6 +517,7 @@ void Core::registerCallbacks(Tox* tox)
tox_callback_conference_peer_list_changed(tox, onGroupPeerListChange);
tox_callback_conference_peer_name(tox, onGroupPeerNameChange);
tox_callback_conference_title(tox, onGroupTitleChange);
tox_callback_friend_lossless_packet(tox, onLosslessPacket);
}

/**
Expand Down Expand Up @@ -639,6 +642,9 @@ ToxCorePtr Core::makeToxCore(const QByteArray& savedata, const ICoreSettings* co
return {};
}

core->ext = CoreExt::makeCoreExt(core->tox.get());
connect(core.get(), &Core::friendStatusChanged, core->ext.get(), &CoreExt::onFriendStatusChanged);

registerCallbacks(core->tox.get());

// connect the thread with the Core
Expand Down Expand Up @@ -714,6 +720,16 @@ QMutex &Core::getCoreLoopLock() const
return coreLoopLock;
}

const CoreExt* Core::getExt() const
{
return ext.get();
}

CoreExt* Core::getExt()
{
return ext.get();
}

/* Using the now commented out statements in checkConnection(), I watched how
* many ticks disconnects-after-initial-connect lasted. Out of roughly 15 trials,
* 5 disconnected; 4 were DCd for less than 20 ticks, while the 5th was ~50 ticks.
Expand All @@ -734,6 +750,7 @@ void Core::process()

static int tolerance = CORE_DISCONNECT_TOLERANCE;
tox_iterate(tox.get(), this);
ext->process();

#ifdef DEBUG
// we want to see the debug messages immediately
Expand Down Expand Up @@ -988,6 +1005,16 @@ void Core::onGroupTitleChange(Tox*, uint32_t groupId, uint32_t peerId, const uin
emit core->groupTitleChanged(groupId, author, ToxString(cTitle, length).getQString());
}

/**
* @brief Handling of custom lossless packets received by toxcore. Currently only used to forward toxext packets to CoreExt
*/
void Core::onLosslessPacket(Tox*, uint32_t friendId,
const uint8_t* data, size_t length, void* vCore)
{
Core* core = static_cast<Core*>(vCore);
core->ext->onLosslessPacket(friendId, data, length);
}

void Core::onReadReceiptCallback(Tox*, uint32_t friendId, uint32_t receipt, void* core)
{
emit static_cast<Core*>(core)->receiptRecieved(friendId, ReceiptNum{receipt});
Expand Down
7 changes: 7 additions & 0 deletions src/core/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

class CoreAV;
class CoreFile;
class CoreExt;
class IAudioControl;
class ICoreSettings;
class GroupInvite;
Expand Down Expand Up @@ -79,6 +80,8 @@ class Core : public QObject,
Tox* getTox() const;
QMutex& getCoreLoopLock() const;

const CoreExt* getExt() const;
CoreExt* getExt();
~Core();

static const QString TOX_EXT;
Expand Down Expand Up @@ -215,6 +218,9 @@ public slots:
size_t length, void* core);
static void onGroupTitleChange(Tox* tox, uint32_t groupId, uint32_t peerId,
const uint8_t* cTitle, size_t length, void* vCore);

static void onLosslessPacket(Tox* tox, uint32_t friendId,
const uint8_t* data, size_t length, void* core);
static void onReadReceiptCallback(Tox* tox, uint32_t friendId, uint32_t receipt, void* core);

void sendGroupMessageWithType(int groupId, const QString& message, Tox_Message_Type type);
Expand Down Expand Up @@ -249,6 +255,7 @@ private slots:

std::unique_ptr<CoreFile> file;
CoreAV* av = nullptr;
std::unique_ptr<CoreExt> ext;
QTimer* toxTimer = nullptr;
// recursive, since we might call our own functions
mutable QMutex coreLoopLock{QMutex::Recursive};
Expand Down
159 changes: 159 additions & 0 deletions src/core/coreext.cpp
Original file line number Diff line number Diff line change
@@ -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);
}

Loading

0 comments on commit 7474c6d

Please sign in to comment.