diff --git a/core/account.cpp b/core/account.cpp index e3dd29f..49746b7 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -23,7 +23,7 @@ using namespace Core; -Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, QObject* parent): +Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, NetworkAccess* p_net, QObject* parent): QObject(parent), name(p_name), achiveQueries(), @@ -38,15 +38,19 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& bm(new QXmppBookmarkManager()), rm(client.findExtension()), vm(client.findExtension()), + um(new QXmppUploadRequestManager()), contacts(), conferences(), maxReconnectTimes(0), reconnectTimes(0), queuedContacts(), outOfRosterContacts(), + pendingMessages(), + uploadingSlotsQueue(), avatarHash(), avatarType(), - ownVCardRequestInProgress(false) + ownVCardRequestInProgress(false), + network(p_net) { config.setUser(p_login); config.setDomain(p_server); @@ -85,6 +89,10 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived); //QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler + client.addExtension(um); + QObject::connect(um, &QXmppUploadRequestManager::slotReceived, this, &Account::onUploadSlotReceived); + QObject::connect(um, &QXmppUploadRequestManager::requestFailed, this, &Account::onUploadSlotRequestFailed); + QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); path += "/" + name; QDir dir(path); @@ -141,6 +149,7 @@ Account::~Account() delete itr->second; } + delete um; delete bm; delete mm; delete am; @@ -623,6 +632,44 @@ void Core::Account::sendMessage(const Shared::Message& data) } } +void Core::Account::sendMessage(const Shared::Message& data, const QString& path) +{ + if (state == Shared::connected) { + QString url = network->getFileRemoteUrl(path); + if (url.size() != 0) { + sendMessageWithLocalUploadedFile(data, url); + } else { + if (network->isUploading(path, data.getId())) { + pendingMessages.emplace(data.getId(), data); + } else { + if (um->serviceFound()) { + QFileInfo file(path); + if (file.exists() && file.isReadable()) { + uploadingSlotsQueue.emplace_back(path, data); + if (uploadingSlotsQueue.size() == 1) { + um->requestUploadSlot(file); + } + } else { + qDebug() << "Requested upload slot in account" << name << "for file" << path << "but the file doesn't exist or is not readable"; + } + } else { + qDebug() << "Requested upload slot in account" << name << "for file" << path << "but upload manager didn't discover any upload services"; + } + } + } + } else { + qDebug() << "An attempt to send message with not connected account " << name << ", skipping"; + } +} + +void Core::Account::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url) +{ + msg.setOutOfBandUrl(url); + sendMessage(msg); + //TODO removal/progress update +} + + void Core::Account::onCarbonMessageReceived(const QXmppMessage& msg) { handleChatMessage(msg, false, true); @@ -1551,3 +1598,45 @@ void Core::Account::uploadVCard(const Shared::VCard& card) vm->setClientVCard(iq); onOwnVCardReceived(iq); } + +void Core::Account::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot) +{ + if (uploadingSlotsQueue.size() == 0) { + qDebug() << "HTTP Upload manager of account" << name << "reports about success requesting upload slot, but none was requested"; + } else { + const std::pair& pair = uploadingSlotsQueue.front(); + const QString& mId = pair.second.getId(); + network->uploadFile(mId, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders()); + pendingMessages.emplace(mId, pair.second); + uploadingSlotsQueue.pop_front(); + + if (uploadingSlotsQueue.size() > 0) { + um->requestUploadSlot(uploadingSlotsQueue.front().first); + } + } +} + +void Core::Account::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request) +{ + if (uploadingSlotsQueue.size() == 0) { + qDebug() << "HTTP Upload manager of account" << name << "reports about an error requesting upload slot, but none was requested"; + qDebug() << request.error().text(); + } else { + const std::pair& pair = uploadingSlotsQueue.front(); + qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << name << ":" << request.error().text(); + + if (uploadingSlotsQueue.size() > 0) { + um->requestUploadSlot(uploadingSlotsQueue.front().first); + } + uploadingSlotsQueue.pop_front(); + } +} + +void Core::Account::onFileUploaded(const QString& messageId, const QString& url) +{ + std::map::const_iterator itr = pendingMessages.find(messageId); + if (itr != pendingMessages.end()) { + sendMessageWithLocalUploadedFile(itr->second, url); + pendingMessages.erase(itr); + } +} diff --git a/core/account.h b/core/account.h index 262a334..8f22913 100644 --- a/core/account.h +++ b/core/account.h @@ -36,12 +36,14 @@ #include #include #include +#include #include #include #include -#include "../global.h" +#include "global.h" #include "contact.h" #include "conference.h" +#include "networkaccess.h" namespace Core { @@ -50,7 +52,7 @@ class Account : public QObject { Q_OBJECT public: - Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, QObject* parent = 0); + Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, NetworkAccess* p_net, QObject* parent = 0); ~Account(); void connect(); @@ -74,6 +76,7 @@ public: void setAvailability(Shared::Availability avail); QString getFullJid() const; void sendMessage(const Shared::Message& data); + void sendMessage(const Shared::Message& data, const QString& path); void requestArchive(const QString& jid, int count, const QString& before); void setReconnectTimes(unsigned int times); void subscribeToContact(const QString& jid, const QString& reason); @@ -113,6 +116,7 @@ signals: void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap& data); void removeRoomParticipant(const QString& jid, const QString& nickName); void receivedVCard(const QString& jid, const Shared::VCard& card); + void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap headers); private: QString name; @@ -128,6 +132,7 @@ private: QXmppBookmarkManager* bm; QXmppRosterManager* rm; QXmppVCardManager* vm; + QXmppUploadRequestManager* um; std::map contacts; std::map conferences; unsigned int maxReconnectTimes; @@ -136,10 +141,13 @@ private: std::map queuedContacts; std::set outOfRosterContacts; std::set pendingVCardRequests; + std::map pendingMessages; + std::deque> uploadingSlotsQueue; QString avatarHash; QString avatarType; bool ownVCardRequestInProgress; + NetworkAccess* network; private slots: void onClientConnected(); @@ -184,6 +192,11 @@ private slots: void onVCardReceived(const QXmppVCardIq& card); void onOwnVCardReceived(const QXmppVCardIq& card); + + void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot); + void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request); + void onFileUploaded(const QString& messageId, const QString& url); + void onFileUploadError(const QString& messageId, const QString& path); private: void addedAccount(const QString &bareJid); @@ -200,7 +213,7 @@ private: void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: "); void storeConferences(); void clearConferences(); - + void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url); }; void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card); diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index 095cf41..ea726f2 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -121,7 +121,7 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT QString url = rpl->url().toString(); std::map::const_iterator itr = downloads.find(url); if (itr == downloads.end()) { - qDebug() << "an error downloading" << url << ": the request had some progress but seems like noone is waiting for it, skipping"; + qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping"; } else { Transfer* dwn = itr->second; qreal received = bytesReceived; @@ -140,7 +140,7 @@ void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code) QString url = rpl->url().toString(); std::map::const_iterator itr = downloads.find(url); if (itr == downloads.end()) { - qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like noone is waiting for it, skipping"; + qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like no one is waiting for it, skipping"; } else { QString errorText = getErrorText(code); if (errorText.size() > 0) { @@ -328,7 +328,7 @@ void Core::NetworkAccess::onDownloadFinished() void Core::NetworkAccess::startDownload(const QString& messageId, const QString& url) { - Transfer* dwn = new Transfer({{messageId}, 0, 0, true, "", 0}); + Transfer* dwn = new Transfer({{messageId}, 0, 0, true, "", url, 0}); QNetworkRequest req(url); dwn->reply = manager->get(req); connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress); @@ -363,15 +363,16 @@ void Core::NetworkAccess::onUploadFinished() QString url = rpl->url().toString(); std::map::const_iterator itr = uploads.find(url); if (itr == downloads.end()) { - qDebug() << "an error uploading" << url << ": the request is done but seems like noone is waiting for it, skipping"; + qDebug() << "an error uploading" << url << ": the request is done but seems like no one is waiting for it, skipping"; } else { Transfer* upl = itr->second; if (upl->success) { qDebug() << "upload success for" << url; - files.addRecord(url, upl->path); + files.addRecord(upl->url, upl->path); for (std::set::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) { emit fileLocalPathResponse(*mItr, upl->path); + emit uploadFileComplete(*mItr, upl->url); } } @@ -389,7 +390,7 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot QString url = rpl->url().toString(); std::map::const_iterator itr = uploads.find(url); if (itr == uploads.end()) { - qDebug() << "an error downloading" << url << ": the request had some progress but seems like noone is waiting for it, skipping"; + qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping"; } else { Transfer* upl = itr->second; qreal received = bytesReceived; @@ -404,15 +405,15 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot void Core::NetworkAccess::startUpload(const QString& messageId, const QString& url, const QString& path) { - Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, 0}); + Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, url, 0}); QNetworkRequest req(url); QFile* file = new QFile(path); if (file->open(QIODevice::ReadOnly)) { upl->reply = manager->put(req, file); - connect(upl->reply, SIGNAL(uploadProgress(qint64, qint64)), SLOT(onUploadProgress(qint64, qint64))); - connect(upl->reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onUploadError(QNetworkReply::NetworkError))); - connect(upl->reply, SIGNAL(finished()), SLOT(onUploadFinished())); + connect(upl->reply, &QNetworkReply::uploadProgress, this, &NetworkAccess::onUploadProgress); + connect(upl->reply, qOverload(&QNetworkReply::error), this, &NetworkAccess::onUploadError); + connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished); uploads.insert(std::make_pair(url, upl)); emit downloadFileProgress(messageId, 0); } else { @@ -455,3 +456,36 @@ void Core::NetworkAccess::uploadFileRequest(const QString& messageId, const QStr } } } + +QString Core::NetworkAccess::getFileRemoteUrl(const QString& path) +{ + return ""; //TODO this is a way not to upload some file more then 1 time, here I'm supposed to return that file GET url +} + +bool Core::NetworkAccess::isUploading(const QString& path, const QString& messageId) +{ + return false; //TODO this is a way to avoid parallel uploading of the same files by different chats + // message is is supposed to be added to the uploading messageids list + // the result should be true if there was an uploading file with this path + // message id can be empty, then it's just to check and not to add +} + +void Core::NetworkAccess::uploadFile(const QString& messageId, const QString& path, const QUrl& put, const QUrl& get, const QMap headers) +{ + Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, get.toString(), 0}); + QNetworkRequest req(put); + QFile* file = new QFile(path); + if (file->open(QIODevice::ReadOnly)) { + upl->reply = manager->put(req, file); + + connect(upl->reply, &QNetworkReply::uploadProgress, this, &NetworkAccess::onUploadProgress); + connect(upl->reply, qOverload(&QNetworkReply::error), this, &NetworkAccess::onUploadError); + connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished); + uploads.insert(std::make_pair(put.toString(), upl)); + emit downloadFileProgress(messageId, 0); + } else { + qDebug() << "couldn't upload file" << path; + emit uploadFileError(messageId, "Error opening file"); + delete file; + } +} diff --git a/core/networkaccess.h b/core/networkaccess.h index de3f2bc..824b1af 100644 --- a/core/networkaccess.h +++ b/core/networkaccess.h @@ -47,12 +47,17 @@ public: void start(); void stop(); + QString getFileRemoteUrl(const QString& path); + bool isUploading(const QString& path, const QString& messageId = ""); + void uploadFile(const QString& messageId, const QString& path, const QUrl& put, const QUrl& get, const QMap headers); + signals: void fileLocalPathResponse(const QString& messageId, const QString& path); void downloadFileProgress(const QString& messageId, qreal value); void downloadFileError(const QString& messageId, const QString& path); void uploadFileProgress(const QString& messageId, qreal value); void uploadFileError(const QString& messageId, const QString& path); + void uploadFileComplete(const QString& messageId, const QString& url); public slots: void fileLocalPathRequest(const QString& messageId, const QString& url); @@ -85,6 +90,7 @@ private: QNetworkReply* reply; bool success; QString path; + QString url; QFile* file; }; }; diff --git a/core/squawk.cpp b/core/squawk.cpp index 1679eab..1842367 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -106,7 +106,7 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const QSettings settings; unsigned int reconnects = settings.value("reconnects", 2).toUInt(); - Account* acc = new Account(login, server, password, name); + Account* acc = new Account(login, server, password, name, &network); acc->setResource(resource); acc->setReconnectTimes(reconnects); accounts.push_back(acc); @@ -285,7 +285,13 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data, const QString& path) { + AccountsMap::const_iterator itr = amap.find(account); + if (itr == amap.end()) { + qDebug("An attempt to send a message with non existing account, skipping"); + return; + } + itr->second->sendMessage(data, path); } void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before)