diff --git a/src/chatlog/content/filetransferwidget.cpp b/src/chatlog/content/filetransferwidget.cpp index 60eaad726..ceba1b2b2 100644 --- a/src/chatlog/content/filetransferwidget.cpp +++ b/src/chatlog/content/filetransferwidget.cpp @@ -21,6 +21,7 @@ #include "ui_filetransferwidget.h" #include "src/core/core.h" +#include "src/core/corefile.h" #include "src/persistence/settings.h" #include "src/widget/gui.h" #include "src/widget/style.h" @@ -85,19 +86,21 @@ FileTransferWidget::FileTransferWidget(QWidget* parent, ToxFile file) update(); }); - connect(Core::getInstance(), &Core::fileTransferInfo, this, + CoreFile* coreFile = Core::getInstance()->getCoreFile(); + + connect(coreFile, &CoreFile::fileTransferInfo, this, &FileTransferWidget::onFileTransferInfo); - connect(Core::getInstance(), &Core::fileTransferAccepted, this, + connect(coreFile, &CoreFile::fileTransferAccepted, this, &FileTransferWidget::onFileTransferAccepted); - connect(Core::getInstance(), &Core::fileTransferCancelled, this, + connect(coreFile, &CoreFile::fileTransferCancelled, this, &FileTransferWidget::onFileTransferCancelled); - connect(Core::getInstance(), &Core::fileTransferPaused, this, + connect(coreFile, &CoreFile::fileTransferPaused, this, &FileTransferWidget::onFileTransferPaused); - connect(Core::getInstance(), &Core::fileTransferFinished, this, + connect(coreFile, &CoreFile::fileTransferFinished, this, &FileTransferWidget::onFileTransferFinished); - connect(Core::getInstance(), &Core::fileTransferRemotePausedUnpaused, this, + connect(coreFile, &CoreFile::fileTransferRemotePausedUnpaused, this, &FileTransferWidget::fileTransferRemotePausedUnpaused); - connect(Core::getInstance(), &Core::fileTransferBrokenUnbroken, this, + connect(coreFile, &CoreFile::fileTransferBrokenUnbroken, this, &FileTransferWidget::fileTransferBrokenUnbroken); connect(ui->leftButton, &QPushButton::clicked, this, &FileTransferWidget::onLeftButtonClicked); connect(ui->rightButton, &QPushButton::clicked, this, &FileTransferWidget::onRightButtonClicked); @@ -149,7 +152,8 @@ void FileTransferWidget::autoAcceptTransfer(const QString& path) // Do not automatically accept the file-transfer if the path is not writable. // The user can still accept it manually. if (tryRemoveFile(filepath)) { - Core::getInstance()->acceptFileRecvRequest(fileInfo.friendId, fileInfo.fileNum, filepath); + CoreFile* coreFile = Core::getInstance()->getCoreFile(); + coreFile->acceptFileRecvRequest(fileInfo.friendId, fileInfo.fileNum, filepath); } else { qWarning() << "Cannot write to " << filepath; } @@ -176,7 +180,8 @@ void FileTransferWidget::acceptTransfer(const QString& filepath) } // everything ok! - Core::getInstance()->acceptFileRecvRequest(fileInfo.friendId, fileInfo.fileNum, filepath); + CoreFile* coreFile = Core::getInstance()->getCoreFile(); + coreFile->acceptFileRecvRequest(fileInfo.friendId, fileInfo.fileNum, filepath); } void FileTransferWidget::setBackgroundColor(const QColor& c, bool whiteFont) @@ -464,7 +469,7 @@ void FileTransferWidget::updateSignals(ToxFile const& file) case ToxFile::BROKEN: case ToxFile::FINISHED: active = false; - disconnect(Core::getInstance(), nullptr, this, nullptr); + disconnect(Core::getInstance()->getCoreFile(), nullptr, this, nullptr); break; case ToxFile::INITIALIZING: case ToxFile::PAUSED: @@ -553,22 +558,23 @@ void FileTransferWidget::setupButtons(ToxFile const& file) void FileTransferWidget::handleButton(QPushButton* btn) { + CoreFile* coreFile = Core::getInstance()->getCoreFile(); if (fileInfo.direction == ToxFile::SENDING) { if (btn->objectName() == "cancel") { - Core::getInstance()->cancelFileSend(fileInfo.friendId, fileInfo.fileNum); + coreFile->cancelFileSend(fileInfo.friendId, fileInfo.fileNum); } else if (btn->objectName() == "pause") { - Core::getInstance()->pauseResumeFile(fileInfo.friendId, fileInfo.fileNum); + coreFile->pauseResumeFile(fileInfo.friendId, fileInfo.fileNum); } else if (btn->objectName() == "resume") { - Core::getInstance()->pauseResumeFile(fileInfo.friendId, fileInfo.fileNum); + coreFile->pauseResumeFile(fileInfo.friendId, fileInfo.fileNum); } } else // receiving or paused { if (btn->objectName() == "cancel") { - Core::getInstance()->cancelFileRecv(fileInfo.friendId, fileInfo.fileNum); + coreFile->cancelFileRecv(fileInfo.friendId, fileInfo.fileNum); } else if (btn->objectName() == "pause") { - Core::getInstance()->pauseResumeFile(fileInfo.friendId, fileInfo.fileNum); + coreFile->pauseResumeFile(fileInfo.friendId, fileInfo.fileNum); } else if (btn->objectName() == "resume") { - Core::getInstance()->pauseResumeFile(fileInfo.friendId, fileInfo.fileNum); + coreFile->pauseResumeFile(fileInfo.friendId, fileInfo.fileNum); } else if (btn->objectName() == "accept") { QString path = QFileDialog::getSaveFileName(Q_NULLPTR, diff --git a/src/core/core.cpp b/src/core/core.cpp index 56eedca4a..8dcf757b8 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -165,10 +165,6 @@ 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_file_chunk_request(tox, CoreFile::onFileDataCallback); - tox_callback_file_recv(tox, CoreFile::onFileReceiveCallback); - tox_callback_file_recv_chunk(tox, CoreFile::onFileRecvChunkCallback); - tox_callback_file_recv_control(tox, CoreFile::onFileControlCallback); } /** @@ -293,6 +289,16 @@ ToxCorePtr Core::makeToxCore(const QByteArray& savedata, const ICoreSettings* co return {}; } + // create CoreFile + core->file = CoreFile::makeCoreFile(core.get(), core->tox.get(), core->coreLoopLock); + if (!core->file) { + qCritical() << "CoreFile failed to start"; + if (err) { + *err = ToxCoreErrors::FAILED_TO_START; + } + return {}; + } + registerCallbacks(core->tox.get()); // connect the thread with the Core @@ -359,6 +365,11 @@ CoreAV* Core::getAv() return av.get(); } +CoreFile* Core::getCoreFile() const +{ + return file.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. @@ -394,7 +405,7 @@ void Core::process() } unsigned sleeptime = - qMin(tox_iteration_interval(tox.get()), CoreFile::corefileIterationInterval()); + qMin(tox_iteration_interval(tox.get()), getCoreFile()->corefileIterationInterval()); toxTimer->start(sleeptime); } @@ -513,15 +524,15 @@ void Core::onUserStatusChanged(Tox*, uint32_t friendId, Tox_User_Status userstat emit static_cast(core)->friendStatusChanged(friendId, status); } -void Core::onConnectionStatusChanged(Tox*, uint32_t friendId, Tox_Connection status, void* core) +void Core::onConnectionStatusChanged(Tox*, uint32_t friendId, Tox_Connection status, void* vCore) { + Core* core = static_cast(vCore); Status friendStatus = status != TOX_CONNECTION_NONE ? Status::Online : Status::Offline; // Ignore Online because it will be emited from onUserStatusChanged bool isOffline = friendStatus == Status::Offline; if (isOffline) { - emit static_cast(core)->friendStatusChanged(friendId, friendStatus); - static_cast(core)->checkLastOnline(friendId); - CoreFile::onConnectionStatusChanged(static_cast(core), friendId, !isOffline); + emit core->friendStatusChanged(friendId, friendStatus); + core->checkLastOnline(friendId); } } @@ -757,55 +768,6 @@ void Core::changeGroupTitle(int groupId, const QString& title) } } -void Core::sendFile(uint32_t friendId, QString filename, QString filePath, long long filesize) -{ - QMutexLocker ml{&coreLoopLock}; - - CoreFile::sendFile(this, friendId, filename, filePath, filesize); -} - -void Core::sendAvatarFile(uint32_t friendId, const QByteArray& data) -{ - QMutexLocker ml{&coreLoopLock}; - - CoreFile::sendAvatarFile(this, friendId, data); -} - -void Core::pauseResumeFile(uint32_t friendId, uint32_t fileNum) -{ - QMutexLocker ml{&coreLoopLock}; - - CoreFile::pauseResumeFile(this, friendId, fileNum); -} - -void Core::cancelFileSend(uint32_t friendId, uint32_t fileNum) -{ - QMutexLocker ml{&coreLoopLock}; - - CoreFile::cancelFileSend(this, friendId, fileNum); -} - -void Core::cancelFileRecv(uint32_t friendId, uint32_t fileNum) -{ - QMutexLocker ml{&coreLoopLock}; - - CoreFile::cancelFileRecv(this, friendId, fileNum); -} - -void Core::rejectFileRecvRequest(uint32_t friendId, uint32_t fileNum) -{ - QMutexLocker ml{&coreLoopLock}; - - CoreFile::rejectFileRecvRequest(this, friendId, fileNum); -} - -void Core::acceptFileRecvRequest(uint32_t friendId, uint32_t fileNum, QString path) -{ - QMutexLocker ml{&coreLoopLock}; - - CoreFile::acceptFileRecvRequest(this, friendId, fileNum, path); -} - void Core::removeFriend(uint32_t friendId) { QMutexLocker ml{&coreLoopLock}; diff --git a/src/core/core.h b/src/core/core.h index 9fef4cb89..898ab7a89 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -36,6 +36,7 @@ #include class CoreAV; +class CoreFile; class ICoreSettings; class GroupInvite; class Profile; @@ -71,6 +72,7 @@ public: static Core* getInstance(); const CoreAV* getAv() const; CoreAV* getAv(); + CoreFile* getCoreFile() const; ~Core(); static const QString TOX_EXT; @@ -125,13 +127,6 @@ public slots: bool sendAction(uint32_t friendId, const QString& action, ReceiptNum& receipt); void sendTyping(uint32_t friendId, bool typing); - void sendAvatarFile(uint32_t friendId, const QByteArray& data); - void cancelFileSend(uint32_t friendId, uint32_t fileNum); - void cancelFileRecv(uint32_t friendId, uint32_t fileNum); - void rejectFileRecvRequest(uint32_t friendId, uint32_t fileNum); - void acceptFileRecvRequest(uint32_t friendId, uint32_t fileNum, QString path); - void pauseResumeFile(uint32_t friendId, uint32_t fileNum); - void setNospam(uint32_t nospam); signals: @@ -157,19 +152,6 @@ signals: void avReady(); - void fileSendStarted(ToxFile file); - void fileReceiveRequested(ToxFile file); - void fileTransferAccepted(ToxFile file); - void fileTransferCancelled(ToxFile file); - void fileTransferFinished(ToxFile file); - void fileUploadFinished(const QString& path); - void fileDownloadFinished(const QString& path); - void fileTransferPaused(ToxFile file); - void fileTransferInfo(ToxFile file); - void fileTransferRemotePausedUnpaused(ToxFile file, bool paused); - void fileTransferBrokenUnbroken(ToxFile file, bool broken); - void fileNameChanged(const ToxPk& friendPk); - void saveRequest(); /** @@ -204,8 +186,6 @@ signals: void failedToRemoveFriend(uint32_t friendId); - void fileSendFailed(uint32_t friendId, const QString& fname); - private: Core(QThread* coreThread); @@ -221,7 +201,7 @@ private: static void onUserStatusChanged(Tox* tox, uint32_t friendId, Tox_User_Status userstatus, void* core); static void onConnectionStatusChanged(Tox* tox, uint32_t friendId, Tox_Connection status, - void* core); + void* vCore); static void onGroupInvite(Tox* tox, uint32_t friendId, Tox_Conference_Type type, const uint8_t* cookie, size_t length, void* vCore); static void onGroupMessage(Tox* tox, uint32_t groupId, uint32_t peerId, Tox_Message_Type type, @@ -241,7 +221,6 @@ private: void checkEncryptedHistory(); void makeTox(QByteArray savedata, ICoreSettings* s); - void makeAv(); void loadFriends(); void loadGroups(); void bootstrapDht(); @@ -267,6 +246,7 @@ private: using ToxPtr = std::unique_ptr; ToxPtr tox; + std::unique_ptr file; std::unique_ptr av; QTimer* toxTimer = nullptr; // recursive, since we might call our own functions @@ -275,7 +255,6 @@ private: std::unique_ptr coreThread = nullptr; friend class Audio; ///< Audio can access our calls directly to reduce latency - friend class CoreFile; ///< CoreFile can access tox* and emit our signals friend class CoreAV; ///< CoreAV accesses our toxav* for now }; diff --git a/src/core/corefile.cpp b/src/core/corefile.cpp index 64949c7d0..0f116cde4 100644 --- a/src/core/corefile.cpp +++ b/src/core/corefile.cpp @@ -22,25 +22,36 @@ #include "core.h" #include "toxfile.h" #include "toxstring.h" -#include "src/persistence/profile.h" #include "src/persistence/settings.h" #include #include #include #include #include +#include #include /** * @class CoreFile - * @brief Implements Core's file transfer callbacks. - * - * Avoids polluting core.h with private internal callbacks. + * @brief Manages the file transfer service of toxcore */ -QMutex CoreFile::fileSendMutex; -QHash CoreFile::fileMap; -using namespace std; +CoreFilePtr CoreFile::makeCoreFile(Core *core, Tox *tox, QMutex &coreLoopLock) +{ + assert(core != nullptr); + assert(tox != nullptr); + connectCallbacks(*tox); + CoreFilePtr result = CoreFilePtr{new CoreFile{tox, coreLoopLock}}; + connect(core, &Core::friendStatusChanged, result.get(), &CoreFile::onConnectionStatusChanged); + + return result; +} + +CoreFile::CoreFile(Tox *core, QMutex &coreLoopLock) + : tox{core} + , coreLoopLock{&coreLoopLock} +{ +} /** * @brief Get corefile iteration interval. @@ -66,12 +77,21 @@ unsigned CoreFile::corefileIterationInterval() return idleInterval; } -void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& data) +void CoreFile::connectCallbacks(Tox &tox) { - QMutexLocker mlocker(&fileSendMutex); + // be careful not to to reconnect already used callbacks here + tox_callback_file_chunk_request(&tox, CoreFile::onFileDataCallback); + tox_callback_file_recv(&tox, CoreFile::onFileReceiveCallback); + tox_callback_file_recv_chunk(&tox, CoreFile::onFileRecvChunkCallback); + tox_callback_file_recv_control(&tox, CoreFile::onFileControlCallback); +} + +void CoreFile::sendAvatarFile(uint32_t friendId, const QByteArray& data) +{ + QMutexLocker{coreLoopLock}; if (data.isEmpty()) { - tox_file_send(core->tox.get(), friendId, TOX_FILE_KIND_AVATAR, 0, nullptr, nullptr, 0, nullptr); + tox_file_send(tox, friendId, TOX_FILE_KIND_AVATAR, 0, nullptr, nullptr, 0, nullptr); return; } @@ -81,7 +101,7 @@ void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& d uint64_t filesize = data.size(); Tox_Err_File_Send error; - uint32_t fileNum = tox_file_send(core->tox.get(), friendId, TOX_FILE_KIND_AVATAR, filesize, + uint32_t fileNum = tox_file_send(tox, friendId, TOX_FILE_KIND_AVATAR, filesize, avatarHash, avatarHash, TOX_HASH_LENGTH, &error); switch (error) { @@ -111,22 +131,23 @@ void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& d file.fileKind = TOX_FILE_KIND_AVATAR; file.avatarData = data; file.resumeFileId.resize(TOX_FILE_ID_LENGTH); - tox_file_get_file_id(core->tox.get(), friendId, fileNum, (uint8_t*)file.resumeFileId.data(), + tox_file_get_file_id(tox, friendId, fileNum, (uint8_t*)file.resumeFileId.data(), nullptr); addFile(friendId, fileNum, file); } -void CoreFile::sendFile(Core* core, uint32_t friendId, QString filename, QString filePath, +void CoreFile::sendFile(uint32_t friendId, QString filename, QString filePath, long long filesize) { - QMutexLocker mlocker(&fileSendMutex); + QMutexLocker{coreLoopLock}; + ToxString fileName(filename); TOX_ERR_FILE_SEND sendErr; - uint32_t fileNum = tox_file_send(core->tox.get(), friendId, TOX_FILE_KIND_DATA, filesize, + uint32_t fileNum = tox_file_send(tox, friendId, TOX_FILE_KIND_DATA, filesize, nullptr, fileName.data(), fileName.size(), &sendErr); if (sendErr != TOX_ERR_FILE_SEND_OK) { qWarning() << "sendFile: Can't create the Tox file sender (" << sendErr << ")"; - emit core->fileSendFailed(friendId, fileName.getQString()); + emit fileSendFailed(friendId, fileName.getQString()); return; } qDebug() << QString("sendFile: Created file sender %1 with friend %2").arg(fileNum).arg(friendId); @@ -134,7 +155,7 @@ void CoreFile::sendFile(Core* core, uint32_t friendId, QString filename, QString ToxFile file{fileNum, friendId, fileName.getQString(), filePath, ToxFile::SENDING}; file.filesize = filesize; file.resumeFileId.resize(TOX_FILE_ID_LENGTH); - tox_file_get_file_id(core->tox.get(), friendId, fileNum, (uint8_t*)file.resumeFileId.data(), + tox_file_get_file_id(tox, friendId, fileNum, (uint8_t*)file.resumeFileId.data(), nullptr); if (!file.open(false)) { qWarning() << QString("sendFile: Can't open file, error: %1").arg(file.file->errorString()); @@ -142,11 +163,13 @@ void CoreFile::sendFile(Core* core, uint32_t friendId, QString filename, QString addFile(friendId, fileNum, file); - emit core->fileSendStarted(file); + emit fileSendStarted(file); } -void CoreFile::pauseResumeFile(Core* core, uint32_t friendId, uint32_t fileId) +void CoreFile::pauseResumeFile(uint32_t friendId, uint32_t fileId) { + QMutexLocker{coreLoopLock}; + ToxFile* file = findFile(friendId, fileId); if (!file) { qWarning("pauseResumeFileSend: No such file in queue"); @@ -162,23 +185,25 @@ void CoreFile::pauseResumeFile(Core* core, uint32_t friendId, uint32_t fileId) if (file->pauseStatus.paused()) { file->status = ToxFile::PAUSED; - emit core->fileTransferPaused(*file); + emit fileTransferPaused(*file); } else { file->status = ToxFile::TRANSMITTING; - emit core->fileTransferAccepted(*file); + emit fileTransferAccepted(*file); } if (file->pauseStatus.localPaused()) { - tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_PAUSE, + tox_file_control(tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_PAUSE, nullptr); } else { - tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, + tox_file_control(tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr); } } -void CoreFile::cancelFileSend(Core* core, uint32_t friendId, uint32_t fileId) +void CoreFile::cancelFileSend(uint32_t friendId, uint32_t fileId) { + QMutexLocker{coreLoopLock}; + ToxFile* file = findFile(friendId, fileId); if (!file) { qWarning("cancelFileSend: No such file in queue"); @@ -186,39 +211,45 @@ void CoreFile::cancelFileSend(Core* core, uint32_t friendId, uint32_t fileId) } file->status = ToxFile::CANCELED; - emit core->fileTransferCancelled(*file); - tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr); + emit fileTransferCancelled(*file); + tox_file_control(tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr); removeFile(friendId, fileId); } -void CoreFile::cancelFileRecv(Core* core, uint32_t friendId, uint32_t fileId) +void CoreFile::cancelFileRecv(uint32_t friendId, uint32_t fileId) { + QMutexLocker{coreLoopLock}; + ToxFile* file = findFile(friendId, fileId); if (!file) { qWarning("cancelFileRecv: No such file in queue"); return; } file->status = ToxFile::CANCELED; - emit core->fileTransferCancelled(*file); - tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr); + emit fileTransferCancelled(*file); + tox_file_control(tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr); removeFile(friendId, fileId); } -void CoreFile::rejectFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId) +void CoreFile::rejectFileRecvRequest(uint32_t friendId, uint32_t fileId) { + QMutexLocker{coreLoopLock}; + ToxFile* file = findFile(friendId, fileId); if (!file) { qWarning("rejectFileRecvRequest: No such file in queue"); return; } file->status = ToxFile::CANCELED; - emit core->fileTransferCancelled(*file); - tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr); + emit fileTransferCancelled(*file); + tox_file_control(tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr); removeFile(friendId, fileId); } -void CoreFile::acceptFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId, QString path) +void CoreFile::acceptFileRecvRequest(uint32_t friendId, uint32_t fileId, QString path) { + QMutexLocker{coreLoopLock}; + ToxFile* file = findFile(friendId, fileId); if (!file) { qWarning("acceptFileRecvRequest: No such file in queue"); @@ -230,12 +261,14 @@ void CoreFile::acceptFileRecvRequest(Core* core, uint32_t friendId, uint32_t fil return; } file->status = ToxFile::TRANSMITTING; - emit core->fileTransferAccepted(*file); - tox_file_control(core->tox.get(), file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr); + emit fileTransferAccepted(*file); + tox_file_control(tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr); } ToxFile* CoreFile::findFile(uint32_t friendId, uint32_t fileId) { + QMutexLocker{coreLoopLock}; + uint64_t key = getFriendKey(friendId, fileId); if (fileMap.contains(key)) { return &fileMap[key]; @@ -276,11 +309,12 @@ QString CoreFile::getCleanFileName(QString filename) return filename; } -void CoreFile::onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId, uint32_t kind, +void CoreFile::onFileReceiveCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint32_t kind, uint64_t filesize, const uint8_t* fname, size_t fnameLen, void* vCore) { Core* core = static_cast(vCore); + CoreFile* coreFile = core->getCoreFile(); auto filename = ToxString(fname, fnameLen); const ToxPk friendPk = core->getFriendPublicKey(friendId); @@ -294,7 +328,7 @@ void CoreFile::onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId, u static_assert(TOX_HASH_LENGTH <= TOX_FILE_ID_LENGTH, "TOX_HASH_LENGTH > TOX_FILE_ID_LENGTH!"); uint8_t avatarHash[TOX_FILE_ID_LENGTH]; - tox_file_get_file_id(core->tox.get(), friendId, fileId, avatarHash, nullptr); + tox_file_get_file_id(tox, friendId, fileId, avatarHash, nullptr); QByteArray avatarBytes{static_cast(static_cast(avatarHash)), TOX_HASH_LENGTH}; emit core->fileAvatarOfferReceived(friendId, fileId, avatarBytes); @@ -305,7 +339,7 @@ void CoreFile::onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId, u if (cleanFileName != filename.getQString()) { qDebug() << QStringLiteral("Cleaned filename"); filename = ToxString(cleanFileName); - emit core->fileNameChanged(friendPk); + emit coreFile->fileNameChanged(friendPk); } else { qDebug() << QStringLiteral("filename already clean"); } @@ -316,24 +350,23 @@ void CoreFile::onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId, u file.filesize = filesize; file.fileKind = kind; file.resumeFileId.resize(TOX_FILE_ID_LENGTH); - tox_file_get_file_id(core->tox.get(), friendId, fileId, (uint8_t*)file.resumeFileId.data(), + tox_file_get_file_id(tox, friendId, fileId, (uint8_t*)file.resumeFileId.data(), nullptr); - addFile(friendId, fileId, file); - if (kind != TOX_FILE_KIND_AVATAR) - emit core->fileReceiveRequested(file); + coreFile->addFile(friendId, fileId, file); + if (kind != TOX_FILE_KIND_AVATAR) { + emit coreFile->fileReceiveRequested(file); + } } // TODO(sudden6): This whole method is a mess but needed to get stuff working for now void CoreFile::handleAvatarOffer(uint32_t friendId, uint32_t fileId, bool accept) { - // TODO(sudden6): evil evil evil - auto core = Core::getInstance(); if (!accept) { // If it's an avatar but we already have it cached, cancel qDebug() << QString("Received avatar request %1:%2, reject, since we have it in cache.") .arg(friendId) .arg(fileId); - tox_file_control(core->tox.get(), friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr); + tox_file_control(tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr); return; } @@ -342,21 +375,23 @@ void CoreFile::handleAvatarOffer(uint32_t friendId, uint32_t fileId, bool accept "in cache.") .arg(friendId) .arg(fileId); - tox_file_control(core->tox.get(), friendId, fileId, TOX_FILE_CONTROL_RESUME, nullptr); + tox_file_control(tox, friendId, fileId, TOX_FILE_CONTROL_RESUME, nullptr); ToxFile file{fileId, friendId, "", "", ToxFile::RECEIVING}; file.filesize = 0; file.fileKind = TOX_FILE_KIND_AVATAR; file.resumeFileId.resize(TOX_FILE_ID_LENGTH); - tox_file_get_file_id(core->tox.get(), friendId, fileId, (uint8_t*)file.resumeFileId.data(), + tox_file_get_file_id(tox, friendId, fileId, (uint8_t*)file.resumeFileId.data(), nullptr); addFile(friendId, fileId, file); } void CoreFile::onFileControlCallback(Tox*, uint32_t friendId, uint32_t fileId, - Tox_File_Control control, void* core) + Tox_File_Control control, void* vCore) { - ToxFile* file = findFile(friendId, fileId); + Core* core = static_cast(vCore); + CoreFile* coreFile = core->getCoreFile(); + ToxFile* file = coreFile->findFile(friendId, fileId); if (!file) { qWarning("onFileControlCallback: No such file in queue"); return; @@ -366,13 +401,13 @@ void CoreFile::onFileControlCallback(Tox*, uint32_t friendId, uint32_t fileId, if (file->fileKind != TOX_FILE_KIND_AVATAR) qDebug() << "File tranfer" << friendId << ":" << fileId << "cancelled by friend"; file->status = ToxFile::CANCELED; - emit static_cast(core)->fileTransferCancelled(*file); - removeFile(friendId, fileId); + emit coreFile->fileTransferCancelled(*file); + coreFile->removeFile(friendId, fileId); } else if (control == TOX_FILE_CONTROL_PAUSE) { qDebug() << "onFileControlCallback: Received pause for file " << friendId << ":" << fileId; file->pauseStatus.remotePause(); file->status = ToxFile::PAUSED; - emit static_cast(core)->fileTransferRemotePausedUnpaused(*file, true); + emit coreFile->fileTransferRemotePausedUnpaused(*file, true); } else if (control == TOX_FILE_CONTROL_RESUME) { if (file->direction == ToxFile::SENDING && file->fileKind == TOX_FILE_KIND_AVATAR) qDebug() << "Avatar transfer" << fileId << "to friend" << friendId << "accepted"; @@ -380,17 +415,19 @@ void CoreFile::onFileControlCallback(Tox*, uint32_t friendId, uint32_t fileId, qDebug() << "onFileControlCallback: Received resume for file " << friendId << ":" << fileId; file->pauseStatus.remoteResume(); file->status = file->pauseStatus.paused() ? ToxFile::PAUSED : ToxFile::TRANSMITTING; - emit static_cast(core)->fileTransferRemotePausedUnpaused(*file, false); + emit coreFile->fileTransferRemotePausedUnpaused(*file, false); } else { qWarning() << "Unhandled file control " << control << " for file " << friendId << ':' << fileId; } } void CoreFile::onFileDataCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint64_t pos, - size_t length, void* core) + size_t length, void* vCore) { - ToxFile* file = findFile(friendId, fileId); + Core* core = static_cast(vCore); + CoreFile* coreFile = core->getCoreFile(); + ToxFile* file = coreFile->findFile(friendId, fileId); if (!file) { qWarning("onFileDataCallback: No such file in queue"); return; @@ -400,14 +437,14 @@ void CoreFile::onFileDataCallback(Tox* tox, uint32_t friendId, uint32_t fileId, if (!length) { file->status = ToxFile::FINISHED; if (file->fileKind != TOX_FILE_KIND_AVATAR) { - emit static_cast(core)->fileTransferFinished(*file); - emit static_cast(core)->fileUploadFinished(file->filePath); + emit coreFile->fileTransferFinished(*file); + emit coreFile->fileUploadFinished(file->filePath); } - removeFile(friendId, fileId); + coreFile->removeFile(friendId, fileId); return; } - unique_ptr data(new uint8_t[length]); + std::unique_ptr data(new uint8_t[length]); int64_t nread; if (file->fileKind == TOX_FILE_KIND_AVATAR) { @@ -420,9 +457,9 @@ void CoreFile::onFileDataCallback(Tox* tox, uint32_t friendId, uint32_t fileId, if (nread <= 0) { qWarning("onFileDataCallback: Failed to read from file"); file->status = ToxFile::CANCELED; - emit static_cast(core)->fileTransferCancelled(*file); + emit coreFile->fileTransferCancelled(*file); tox_file_send_chunk(tox, friendId, fileId, pos, nullptr, 0, nullptr); - removeFile(friendId, fileId); + coreFile->removeFile(friendId, fileId); return; } file->bytesSent += length; @@ -433,15 +470,17 @@ void CoreFile::onFileDataCallback(Tox* tox, uint32_t friendId, uint32_t fileId, qWarning("onFileDataCallback: Failed to send data chunk"); return; } - if (file->fileKind != TOX_FILE_KIND_AVATAR) - emit static_cast(core)->fileTransferInfo(*file); + if (file->fileKind != TOX_FILE_KIND_AVATAR) { + emit coreFile->fileTransferInfo(*file); + } } void CoreFile::onFileRecvChunkCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint64_t position, const uint8_t* data, size_t length, void* vCore) { Core* core = static_cast(vCore); - ToxFile* file = findFile(friendId, fileId); + CoreFile* coreFile = core->getCoreFile(); + ToxFile* file = coreFile->findFile(friendId, fileId); if (!file) { qWarning("onFileRecvChunkCallback: No such file in queue"); tox_file_control(tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr); @@ -452,10 +491,10 @@ void CoreFile::onFileRecvChunkCallback(Tox* tox, uint32_t friendId, uint32_t fil qWarning("onFileRecvChunkCallback: Received a chunk out-of-order, aborting transfer"); if (file->fileKind != TOX_FILE_KIND_AVATAR) { file->status = ToxFile::CANCELED; - emit core->fileTransferCancelled(*file); + emit coreFile->fileTransferCancelled(*file); } tox_file_control(tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr); - removeFile(friendId, fileId); + coreFile->removeFile(friendId, fileId); return; } @@ -469,26 +508,29 @@ void CoreFile::onFileRecvChunkCallback(Tox* tox, uint32_t friendId, uint32_t fil emit core->friendAvatarChanged(core->getFriendPublicKey(friendId), file->avatarData); } } else { - emit core->fileTransferFinished(*file); - emit core->fileDownloadFinished(file->filePath); + emit coreFile->fileTransferFinished(*file); + emit coreFile->fileDownloadFinished(file->filePath); } - removeFile(friendId, fileId); + coreFile->removeFile(friendId, fileId); return; } - if (file->fileKind == TOX_FILE_KIND_AVATAR) + if (file->fileKind == TOX_FILE_KIND_AVATAR) { file->avatarData.append((char*)data, length); - else + } else { file->file->write((char*)data, length); + } file->bytesSent += length; file->hashGenerator->addData((const char*)data, length); - if (file->fileKind != TOX_FILE_KIND_AVATAR) - emit static_cast(core)->fileTransferInfo(*file); + if (file->fileKind != TOX_FILE_KIND_AVATAR) { + emit coreFile->fileTransferInfo(*file); + } } -void CoreFile::onConnectionStatusChanged(Core* core, uint32_t friendId, bool online) +void CoreFile::onConnectionStatusChanged(uint32_t friendId, Status state) { + bool isOffline = state == Status::Offline; // TODO: Actually resume broken file transfers // We need to: // - Start a new file transfer with the same 32byte file ID with toxcore @@ -496,11 +538,11 @@ void CoreFile::onConnectionStatusChanged(Core* core, uint32_t friendId, bool onl // - Update the fileNum in our ToxFile // - Update the users of our signals to check the 32byte tox file ID, not the uint32_t file_num // (fileId) - ToxFile::FileStatus status = online ? ToxFile::TRANSMITTING : ToxFile::BROKEN; + ToxFile::FileStatus status = !isOffline ? ToxFile::TRANSMITTING : ToxFile::BROKEN; for (uint64_t key : fileMap.keys()) { if (key >> 32 != friendId) continue; fileMap[key].status = status; - emit core->fileTransferBrokenUnbroken(fileMap[key], !online); + emit fileTransferBrokenUnbroken(fileMap[key], !isOffline); } } diff --git a/src/core/corefile.h b/src/core/corefile.h index f45b29de3..e38459d1e 100644 --- a/src/core/corefile.h +++ b/src/core/corefile.h @@ -27,60 +27,85 @@ #include #include "toxfile.h" +#include "src/core/core.h" +#include "src/core/toxpk.h" #include #include +#include #include struct Tox; -class Core; +class CoreFile; -class CoreFile +using CoreFilePtr = std::unique_ptr; + +class CoreFile : public QObject { - friend class Core; - + Q_OBJECT public: - static void handleAvatarOffer(uint32_t friendId, uint32_t fileId, bool accept); + void handleAvatarOffer(uint32_t friendId, uint32_t fileId, bool accept); + static CoreFilePtr makeCoreFile(Core* core, Tox* tox, QMutex& coreLoopLock); -private: - CoreFile() = delete; - - // Internal file sending APIs, used by Core. Public API in core.h -private: - static void sendFile(Core* core, uint32_t friendId, QString filename, QString filePath, + void sendFile(uint32_t friendId, QString filename, QString filePath, long long filesize); - static void sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& data); - static void pauseResumeFile(Core* core, uint32_t friendId, uint32_t fileId); - static void cancelFileSend(Core* core, uint32_t friendId, uint32_t fileId); - static void cancelFileRecv(Core* core, uint32_t friendId, uint32_t fileId); - static void rejectFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId); - static void acceptFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId, QString path); - static ToxFile* findFile(uint32_t friendId, uint32_t fileId); - static void addFile(uint32_t friendId, uint32_t fileId, const ToxFile& file); - static void removeFile(uint32_t friendId, uint32_t fileId); - static unsigned corefileIterationInterval(); + void sendAvatarFile(uint32_t friendId, const QByteArray& data); + void pauseResumeFile(uint32_t friendId, uint32_t fileId); + void cancelFileSend(uint32_t friendId, uint32_t fileId); + + void cancelFileRecv(uint32_t friendId, uint32_t fileId); + void rejectFileRecvRequest(uint32_t friendId, uint32_t fileId); + void acceptFileRecvRequest(uint32_t friendId, uint32_t fileId, QString path); + + unsigned corefileIterationInterval(); + +signals: + void fileSendStarted(ToxFile file); + void fileReceiveRequested(ToxFile file); + void fileTransferAccepted(ToxFile file); + void fileTransferCancelled(ToxFile file); + void fileTransferFinished(ToxFile file); + void fileUploadFinished(const QString& path); + void fileDownloadFinished(const QString& path); + void fileTransferPaused(ToxFile file); + void fileTransferInfo(ToxFile file); + void fileTransferRemotePausedUnpaused(ToxFile file, bool paused); + void fileTransferBrokenUnbroken(ToxFile file, bool broken); + void fileNameChanged(const ToxPk& friendPk); + void fileSendFailed(uint32_t friendId, const QString& fname); + +private: + CoreFile(Tox* core, QMutex& coreLoopLock); + + ToxFile* findFile(uint32_t friendId, uint32_t fileId); + void addFile(uint32_t friendId, uint32_t fileId, const ToxFile& file); + void removeFile(uint32_t friendId, uint32_t fileId); static constexpr uint64_t getFriendKey(uint32_t friendId, uint32_t fileId) { return (static_cast(friendId) << 32) + fileId; } -private: - static void onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId, uint32_t kind, + static void connectCallbacks(Tox& tox); + static void onFileReceiveCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint32_t kind, uint64_t filesize, const uint8_t* fname, size_t fnameLen, void* vCore); static void onFileControlCallback(Tox* tox, uint32_t friendId, uint32_t fileId, - Tox_File_Control control, void* core); + Tox_File_Control control, void* vCore); static void onFileDataCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint64_t pos, - size_t length, void* core); + size_t length, void* vCore); static void onFileRecvChunkCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint64_t position, const uint8_t* data, size_t length, void* vCore); - static void onConnectionStatusChanged(Core* core, uint32_t friendId, bool online); + + static QString getCleanFileName(QString filename); + +private slots: + void onConnectionStatusChanged(uint32_t friendId, Status state); private: - static QMutex fileSendMutex; - static QHash fileMap; - static QString getCleanFileName(QString filename); + QHash fileMap; + Tox* tox; + QMutex* coreLoopLock = nullptr; }; #endif // COREFILE_H diff --git a/src/net/avatarbroadcaster.cpp b/src/net/avatarbroadcaster.cpp index 75fdc6394..39eaaab4d 100644 --- a/src/net/avatarbroadcaster.cpp +++ b/src/net/avatarbroadcaster.cpp @@ -20,6 +20,7 @@ #include "avatarbroadcaster.h" #include "src/core/core.h" +#include "src/core/corefile.h" #include #include @@ -66,7 +67,8 @@ void AvatarBroadcaster::sendAvatarTo(uint32_t friendId) return; if (!Core::getInstance()->isFriendOnline(friendId)) return; - Core::getInstance()->sendAvatarFile(friendId, avatarData); + CoreFile* coreFile = Core::getInstance()->getCoreFile(); + coreFile->sendAvatarFile(friendId, avatarData); friendsSentTo[friendId] = true; } diff --git a/src/persistence/profile.cpp b/src/persistence/profile.cpp index 82ee49d83..7dc50464a 100644 --- a/src/persistence/profile.cpp +++ b/src/persistence/profile.cpp @@ -333,7 +333,7 @@ void Profile::onAvatarOfferReceived(uint32_t friendId, uint32_t fileId, const QB { // accept if we don't have it already const bool accept = getAvatarHash(core->getFriendPublicKey(friendId)) != avatarHash; - CoreFile::handleAvatarOffer(friendId, fileId, accept); + core->getCoreFile()->handleAvatarOffer(friendId, fileId, accept); } /** diff --git a/src/widget/form/chatform.cpp b/src/widget/form/chatform.cpp index 08c9a9777..7ce09de19 100644 --- a/src/widget/form/chatform.cpp +++ b/src/widget/form/chatform.cpp @@ -25,6 +25,7 @@ #include "src/chatlog/content/text.h" #include "src/core/core.h" #include "src/core/coreav.h" +#include "src/core/corefile.h" #include "src/model/friend.h" #include "src/nexus.h" #include "src/persistence/history.h" @@ -160,18 +161,19 @@ ChatForm::ChatForm(Friend* chatFriend, History* history) const Core* core = Core::getInstance(); const Profile* profile = Nexus::getProfile(); - connect(core, &Core::fileReceiveRequested, this, &ChatForm::onFileRecvRequest); + const CoreFile* coreFile = core->getCoreFile(); + connect(coreFile, &CoreFile::fileReceiveRequested, this, &ChatForm::onFileRecvRequest); connect(profile, &Profile::friendAvatarChanged, this, &ChatForm::onAvatarChanged); - connect(core, &Core::fileSendStarted, this, &ChatForm::startFileSend); - connect(core, &Core::fileTransferFinished, this, &ChatForm::onFileTransferFinished); - connect(core, &Core::fileTransferCancelled, this, &ChatForm::onFileTransferCancelled); - connect(core, &Core::fileTransferBrokenUnbroken, this, &ChatForm::onFileTransferBrokenUnbroken); - connect(core, &Core::fileSendFailed, this, &ChatForm::onFileSendFailed); + connect(coreFile, &CoreFile::fileSendStarted, this, &ChatForm::startFileSend); + connect(coreFile, &CoreFile::fileTransferFinished, this, &ChatForm::onFileTransferFinished); + connect(coreFile, &CoreFile::fileTransferCancelled, this, &ChatForm::onFileTransferCancelled); + connect(coreFile, &CoreFile::fileTransferBrokenUnbroken, this, &ChatForm::onFileTransferBrokenUnbroken); + connect(coreFile, &CoreFile::fileSendFailed, this, &ChatForm::onFileSendFailed); connect(core, &Core::receiptRecieved, this, &ChatForm::onReceiptReceived); connect(core, &Core::friendMessageReceived, this, &ChatForm::onFriendMessageReceived); connect(core, &Core::friendTypingChanged, this, &ChatForm::onFriendTypingChanged); connect(core, &Core::friendStatusChanged, this, &ChatForm::onFriendStatusChanged); - connect(core, &Core::fileNameChanged, this, &ChatForm::onFileNameChanged); + connect(coreFile, &CoreFile::fileNameChanged, this, &ChatForm::onFileNameChanged); const CoreAV* av = core->getAv(); @@ -297,7 +299,7 @@ void ChatForm::onAttachClicked() } qint64 filesize = file.size(); - core->sendFile(f->getId(), fileName, path, filesize); + core->getCoreFile()->sendFile(f->getId(), fileName, path, filesize); } } @@ -762,7 +764,7 @@ void ChatForm::dropEvent(QDropEvent* ev) } if (info.exists()) { - core->sendFile(f->getId(), fileName, info.absoluteFilePath(), info.size()); + core->getCoreFile()->sendFile(f->getId(), fileName, info.absoluteFilePath(), info.size()); } } } @@ -984,7 +986,8 @@ void ChatForm::sendImage(const QPixmap& pixmap) qint64 filesize = file.size(); file.close(); QFileInfo fi(file); - Core::getInstance()->sendFile(f->getId(), fi.fileName(), fi.filePath(), filesize); + CoreFile* coreFile = Core::getInstance()->getCoreFile(); + coreFile->sendFile(f->getId(), fi.fileName(), fi.filePath(), filesize); } else { QMessageBox::warning(this, tr("Failed to open temporary file", "Temporary file for screenshot"), diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 56f3d70a7..68a621c4c 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -51,6 +51,7 @@ #include "src/audio/audio.h" #include "src/core/core.h" #include "src/core/coreav.h" +#include "src/core/corefile.h" #include "src/model/chatroom/friendchatroom.h" #include "src/model/chatroom/groupchatroom.h" #include "src/model/friend.h" @@ -234,6 +235,7 @@ void Widget::init() #endif Core* core = Nexus::getCore(); + CoreFile* coreFile = core->getCoreFile(); Profile* profile = Nexus::getProfile(); profileInfo = new ProfileInfo(core, profile); profileForm = new ProfileForm(profileInfo); @@ -243,8 +245,8 @@ void Widget::init() connect(profile, &Profile::selfAvatarChanged, profileForm, &ProfileForm::onSelfAvatarLoaded); - connect(core, &Core::fileDownloadFinished, filesForm, &FilesForm::onFileDownloadComplete); - connect(core, &Core::fileUploadFinished, filesForm, &FilesForm::onFileUploadComplete); + connect(coreFile, &CoreFile::fileDownloadFinished, filesForm, &FilesForm::onFileDownloadComplete); + connect(coreFile, &CoreFile::fileUploadFinished, filesForm, &FilesForm::onFileUploadComplete); connect(ui->addButton, &QPushButton::clicked, this, &Widget::onAddClicked); connect(ui->groupButton, &QPushButton::clicked, this, &Widget::onGroupClicked); connect(ui->transferButton, &QPushButton::clicked, this, &Widget::onTransferClicked);