diff --git a/core/account.cpp b/core/account.cpp index 1dc0d55..6e7d782 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -40,6 +40,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& vm(client.findExtension()), um(new QXmppUploadRequestManager()), dm(client.findExtension()), + rcpm(new QXmppMessageReceiptManager()), contacts(), conferences(), maxReconnectTimes(0), @@ -58,6 +59,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& config.setDomain(p_server); config.setPassword(p_password); config.setAutoAcceptSubscriptions(true); + config.setAutoReconnectionEnabled(false); QObject::connect(&client, &QXmppClient::connected, this, &Account::onClientConnected); QObject::connect(&client, &QXmppClient::disconnected, this, &Account::onClientDisconnected); @@ -101,6 +103,10 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(network, &NetworkAccess::uploadFileComplete, this, &Account::onFileUploaded); QObject::connect(network, &NetworkAccess::uploadFileError, this, &Account::onFileUploadError); + client.addExtension(rcpm); + QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, this, &Account::onReceiptReceived); + + QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); path += "/" + name; QDir dir(path); @@ -160,6 +166,8 @@ Account::~Account() delete itr->second; } + delete rcpm; + delete dm; delete um; delete bm; delete mm; @@ -200,8 +208,8 @@ void Core::Account::onClientConnected() if (state == Shared::connecting) { reconnectTimes = maxReconnectTimes; state = Shared::connected; - cm->setCarbonsEnabled(true); dm->requestItems(getServer()); + dm->requestInfo(getServer()); emit connectionStateChanged(state); } else { qDebug() << "Something weird had happened - xmpp client reported about successful connection but account wasn't in" << state << "state"; @@ -560,14 +568,15 @@ void Core::Account::onMessageReceived(const QXmppMessage& msg) if (itr != pendingStateMessages.end()) { QString jid = itr->second; RosterItem* cnt = getRosterItem(jid); - if (cnt != 0) { - cnt->changeMessageState(id, Shared::Message::State::error); - } - ; - emit changeMessage(jid, id, { + QMap cData = { {"state", static_cast(Shared::Message::State::error)}, {"errorText", msg.error().text()} - }); + }; + if (cnt != 0) { + cnt->changeMessage(id, cData); + } + ; + emit changeMessage(jid, id, cData); pendingStateMessages.erase(itr); handled = true; } else { @@ -610,35 +619,43 @@ QString Core::Account::getFullJid() const return getLogin() + "@" + getServer() + "/" + getResource(); } -void Core::Account::sendMessage(const Shared::Message& data) +void Core::Account::sendMessage(Shared::Message data) { + QString jid = data.getPenPalJid(); + QString id = data.getId(); + RosterItem* ri = getRosterItem(jid); if (state == Shared::connected) { QXmppMessage msg(getFullJid(), data.getTo(), data.getBody(), data.getThread()); - msg.setId(data.getId()); + msg.setId(id); msg.setType(static_cast(data.getType())); //it is safe here, my type is compatible msg.setOutOfBandUrl(data.getOutOfBandUrl()); + msg.setReceiptRequested(true); - RosterItem* ri = 0; - std::map::const_iterator itr = contacts.find(data.getPenPalJid()); - if (itr != contacts.end()) { - ri = itr->second; + bool sent = client.sendPacket(msg); + + if (sent) { + data.setState(Shared::Message::State::sent); } else { - std::map::const_iterator ritr = conferences.find(data.getPenPalJid()); - if (ritr != conferences.end()) { - ri = ritr->second; - } + data.setState(Shared::Message::State::error); + data.setErrorText("Couldn't send message via QXMPP library check out logs"); } if (ri != 0) { ri->appendMessageToArchive(data); - pendingStateMessages.insert(std::make_pair(data.getId(), data.getPenPalJid())); + if (sent) { + pendingStateMessages.insert(std::make_pair(id, jid)); + } } - client.sendPacket(msg); - } else { - qDebug() << "An attempt to send message with not connected account " << name << ", skipping"; + data.setState(Shared::Message::State::error); + data.setErrorText("You are is offline or reconnecting"); } + + emit changeMessage(jid, id, { + {"state", static_cast(data.getState())}, + {"errorText", data.getErrorText()} + }); } void Core::Account::sendMessage(const Shared::Message& data, const QString& path) @@ -659,7 +676,7 @@ void Core::Account::sendMessage(const Shared::Message& data, const QString& path um->requestUploadSlot(file); } } else { - emit onFileUploadError(data.getId(), "Uploading file dissapeared or your system user has no permission to read it"); + emit onFileUploadError(data.getId(), "Uploading file no longer exists or your system user has no permission to read it"); qDebug() << "Requested upload slot in account" << name << "for file" << path << "but the file doesn't exist or is not readable"; } } else { @@ -717,18 +734,17 @@ bool Core::Account::handleChatMessage(const QXmppMessage& msg, bool outgoing, bo })); handleNewContact(cnt); } + if (outgoing) { + if (forwarded) { + sMsg.setState(Shared::Message::State::sent); + } + } else { + sMsg.setState(Shared::Message::State::delivered); + } cnt->appendMessageToArchive(sMsg); emit message(sMsg); - if (!forwarded && !outgoing) { - if (msg.isReceiptRequested() && id.size() > 0) { - QXmppMessage receipt(getFullJid(), msg.from(), ""); - receipt.setReceiptId(id); - client.sendPacket(receipt); - } - } - return true; } return false; @@ -752,9 +768,10 @@ bool Core::Account::handleGroupMessage(const QXmppMessage& msg, bool outgoing, b std::map::const_iterator pItr = pendingStateMessages.find(id); if (pItr != pendingStateMessages.end()) { - cnt->changeMessageState(id, Shared::Message::State::delivered); + QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; + cnt->changeMessage(id, cData); pendingStateMessages.erase(pItr); - emit changeMessage(jid, id, {{"state", static_cast(Shared::Message::State::delivered)}}); + emit changeMessage(jid, id, cData); } else { cnt->appendMessageToArchive(sMsg); QDateTime minAgo = QDateTime::currentDateTime().addSecs(-60); @@ -765,14 +782,6 @@ bool Core::Account::handleGroupMessage(const QXmppMessage& msg, bool outgoing, b } } - if (!forwarded && !outgoing) { - if (msg.isReceiptRequested() && id.size() > 0) { - QXmppMessage receipt(getFullJid(), msg.from(), ""); - receipt.setReceiptId(id); - client.sendPacket(receipt); - } - } - return true; } return false; @@ -1658,11 +1667,32 @@ void Core::Account::onFileUploadError(const QString& messageId, const QString& e void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items) { for (QXmppDiscoveryIq::Item item : items.items()) { - dm->requestInfo(item.jid()); + if (item.jid() != getServer()) { + dm->requestInfo(item.jid()); + } } } void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) { - + if (info.from() == getServer()) { + if (info.features().contains("urn:xmpp:carbons:2")) { + cm->setCarbonsEnabled(true); + } + } } + +void Core::Account::onReceiptReceived(const QString& jid, const QString& id) +{ + std::map::const_iterator itr = pendingStateMessages.find(id); + if (itr != pendingStateMessages.end()) { + QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; + RosterItem* ri = getRosterItem(itr->second); + if (ri != 0) { + ri->changeMessage(id, cData); + } + pendingStateMessages.erase(itr); + emit changeMessage(itr->second, id, cData); + } +} + diff --git a/core/account.h b/core/account.h index 69e17a7..02ae2ab 100644 --- a/core/account.h +++ b/core/account.h @@ -41,6 +41,8 @@ #include #include #include +#include + #include "global.h" #include "contact.h" #include "conference.h" @@ -76,7 +78,7 @@ public: void setResource(const QString& p_resource); void setAvailability(Shared::Availability avail); QString getFullJid() const; - void sendMessage(const Shared::Message& data); + void sendMessage(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); @@ -139,6 +141,7 @@ private: QXmppVCardManager* vm; QXmppUploadRequestManager* um; QXmppDiscoveryManager* dm; + QXmppMessageReceiptManager* rcpm; std::map contacts; std::map conferences; unsigned int maxReconnectTimes; @@ -206,6 +209,8 @@ private slots: void onFileUploadError(const QString& messageId, const QString& errMsg); void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items); void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info); + + void onReceiptReceived(const QString& jid, const QString &id); private: void addedAccount(const QString &bareJid); diff --git a/core/archive.cpp b/core/archive.cpp index 15d8011..3afd706 100644 --- a/core/archive.cpp +++ b/core/archive.cpp @@ -208,7 +208,7 @@ Shared::Message Core::Archive::getMessage(const std::string& id, MDB_txn* txn) } } -void Core::Archive::setMessageState(const QString& id, Shared::Message::State state) +void Core::Archive::changeMessage(const QString& id, const QMap& data) { if (!opened) { throw Closed("setMessageState", jid.toStdString()); @@ -220,21 +220,42 @@ void Core::Archive::setMessageState(const QString& id, Shared::Message::State st std::string strId(id.toStdString()); try { Shared::Message msg = getMessage(strId, txn); - msg.setState(state); + QDateTime oTime = msg.getTime(); + bool idChange = msg.change(data); MDB_val lmdbKey, lmdbData; QByteArray ba; QDataStream ds(&ba, QIODevice::WriteOnly); msg.serialize(ds); + lmdbKey.mv_size = strId.size(); lmdbKey.mv_data = (char*)strId.c_str(); + int rc; + if (idChange) { + rc = mdb_del(txn, main, &lmdbKey, &lmdbData); + if (rc == 0) { + strId = msg.getId().toStdString(); + lmdbKey.mv_size = strId.size(); + lmdbKey.mv_data = (char*)strId.c_str(); + + + quint64 stamp = oTime.toMSecsSinceEpoch(); + lmdbData.mv_data = (quint8*)&stamp; + lmdbData.mv_size = 8; + rc = mdb_put(txn, order, &lmdbData, &lmdbKey, 0); + if (rc != 0) { + throw Unknown(jid.toStdString(), mdb_strerror(rc)); + } + } else { + throw Unknown(jid.toStdString(), mdb_strerror(rc)); + } + } lmdbData.mv_size = ba.size(); lmdbData.mv_data = (uint8_t*)ba.data(); - int rc = mdb_put(txn, main, &lmdbKey, &lmdbData, 0); + rc = mdb_put(txn, main, &lmdbKey, &lmdbData, 0); if (rc == 0) { rc = mdb_txn_commit(txn); } else { - mdb_txn_abort(txn); throw Unknown(jid.toStdString(), mdb_strerror(rc)); } diff --git a/core/archive.h b/core/archive.h index 36dbdbd..15c91b9 100644 --- a/core/archive.h +++ b/core/archive.h @@ -24,9 +24,9 @@ #include #include -#include "../global.h" +#include "global.h" +#include "exception.h" #include -#include "../exception.h" #include namespace Core { @@ -46,7 +46,7 @@ public: bool addElement(const Shared::Message& message); unsigned int addElements(const std::list& messages); Shared::Message getElement(const QString& id); - void setMessageState(const QString& id, Shared::Message::State state); + void changeMessage(const QString& id, const QMap& data); Shared::Message oldest(); QString oldestId(); Shared::Message newest(); diff --git a/core/rosteritem.cpp b/core/rosteritem.cpp index 712e4a5..4d1cca2 100644 --- a/core/rosteritem.cpp +++ b/core/rosteritem.cpp @@ -221,12 +221,12 @@ void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg) } } -void Core::RosterItem::changeMessageState(const QString& id, Shared::Message::State newState) +void Core::RosterItem::changeMessage(const QString& id, const QMap& data) { bool found = false; for (Shared::Message& msg : appendCache) { if (msg.getId() == id) { - msg.setState(newState); + msg.change(data); found = true; break; } @@ -235,7 +235,7 @@ void Core::RosterItem::changeMessageState(const QString& id, Shared::Message::St if (!found) { for (Shared::Message& msg : hisoryCache) { if (msg.getId() == id) { - msg.setState(newState); + msg.change(data); found = true; break; } @@ -244,7 +244,7 @@ void Core::RosterItem::changeMessageState(const QString& id, Shared::Message::St if (!found) { try { - archive->setMessageState(id, newState); + archive->changeMessage(id, data); found = true; } catch (const Archive::NotFound& e) { qDebug() << "An attempt to change state to the message" << id << "but it couldn't be found"; @@ -254,7 +254,7 @@ void Core::RosterItem::changeMessageState(const QString& id, Shared::Message::St if (found) { for (Shared::Message& msg : responseCache) { if (msg.getId() == id) { - msg.setState(newState); + msg.change(data); break; } } diff --git a/core/rosteritem.h b/core/rosteritem.h index f18d42f..f9aa8be 100644 --- a/core/rosteritem.h +++ b/core/rosteritem.h @@ -72,7 +72,7 @@ public: virtual Shared::VCard handleResponseVCard(const QXmppVCardIq& card, const QString& resource); virtual void handlePresence(const QXmppPresence& pres) = 0; - void changeMessageState(const QString& id, Shared::Message::State newState); + void changeMessage(const QString& id, const QMap& data); signals: void nameChanged(const QString& name); diff --git a/ui/widgets/chat.cpp b/ui/widgets/chat.cpp index b8f49ba..41c79be 100644 --- a/ui/widgets/chat.cpp +++ b/ui/widgets/chat.cpp @@ -71,6 +71,7 @@ void Chat::handleSendMessage(const QString& text) msg.setOutgoing(true); msg.generateRandomId(); msg.setCurrentTime(); + msg.setState(Shared::Message::State::pending); addMessage(msg); emit sendMessage(msg); }