1
0
forked from blue/squawk

full transition to lmdbal, DOESNT WORK, DONT TAKE!

This commit is contained in:
Blue 2023-11-02 19:55:11 -03:00
parent 23ec80ccba
commit 9d688e8596
Signed by untrusted user: blue
GPG Key ID: 9B203B252A63EE38
18 changed files with 497 additions and 1752 deletions

View File

@ -60,6 +60,7 @@ if (WITH_OMEMO)
if (PKG_CONFIG_FOUND) if (PKG_CONFIG_FOUND)
pkg_check_modules(OMEMO libomemo-c) pkg_check_modules(OMEMO libomemo-c)
if (OMEMO_FOUND) if (OMEMO_FOUND)
target_compile_definitions(squawk PRIVATE WITH_OMEMO)
message("Building with support of OMEMO") message("Building with support of OMEMO")
else () else ()
message("libomemo-c package wasn't found, trying to build without OMEMO support") message("libomemo-c package wasn't found, trying to build without OMEMO support")
@ -73,7 +74,11 @@ endif ()
## QXmpp ## QXmpp
if (SYSTEM_QXMPP) if (SYSTEM_QXMPP)
find_package(QXmpp CONFIG) if (WITH_OMEMO)
find_package(QXmpp CONFIG COMPONENTS Omemo)
else ()
find_package(QXmpp CONFIG)
endif ()
if (NOT QXmpp_FOUND) if (NOT QXmpp_FOUND)
set(SYSTEM_QXMPP OFF) set(SYSTEM_QXMPP OFF)
@ -138,7 +143,6 @@ if (NOT SYSTEM_QXMPP)
target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/omemo) target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/omemo)
target_include_directories(squawk PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src/omemo) target_include_directories(squawk PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src/omemo)
set(BUILD_OMEMO ON) set(BUILD_OMEMO ON)
target_compile_definitions(squawk PRIVATE WITH_OMEMO)
else () else ()
set(BUILD_OMEMO OFF) set(BUILD_OMEMO OFF)
endif () endif ()
@ -150,6 +154,9 @@ if (NOT SYSTEM_QXMPP)
endif () endif ()
else () else ()
target_link_libraries(squawk PRIVATE QXmpp::QXmpp) target_link_libraries(squawk PRIVATE QXmpp::QXmpp)
if (WITH_OMEMO)
target_link_libraries(squawk PRIVATE QXmpp::Omemo)
endif ()
endif () endif ()
## LMDBAL ## LMDBAL

View File

@ -63,7 +63,7 @@
#include "handlers/discoveryhandler.h" #include "handlers/discoveryhandler.h"
#ifdef WITH_OMEMO #ifdef WITH_OMEMO
#include <QXmppOmemoManager.h> #include <Omemo/QXmppOmemoManager.h>
#include <QXmppTrustManager.h> #include <QXmppTrustManager.h>
#include "handlers/trusthandler.h" #include "handlers/trusthandler.h"
#include "handlers/omemohandler.h" #include "handlers/omemohandler.h"

View File

@ -41,18 +41,12 @@ void Core::UrlStorage::close() {
} }
void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, bool overwrite) { void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, bool overwrite) {
LMDBAL::TransactionID txn = base.beginTransaction(); LMDBAL::WriteTransaction txn = base.beginTransaction();
writeInfo(key, info, txn, overwrite);
try { txn.commit();
writeInfo(key, info, txn, overwrite);
base.commitTransaction(txn);
} catch (...) {
base.abortTransaction(txn);
throw;
}
} }
void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, MDB_txn* txn, bool overwrite) { void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, const LMDBAL::WriteTransaction& txn, bool overwrite) {
if (overwrite) if (overwrite)
urlToInfo->forceRecord(key, info, txn); urlToInfo->forceRecord(key, info, txn);
else else
@ -95,15 +89,11 @@ Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo(
const QString& path const QString& path
) { ) {
UrlInfo info; UrlInfo info;
LMDBAL::TransactionID txn = base.beginTransaction(); LMDBAL::WriteTransaction txn = base.beginTransaction();
try { try {
urlToInfo->getRecord(url, info, txn); urlToInfo->getRecord(url, info, txn);
} catch (const LMDBAL::NotFound& e) { } catch (const LMDBAL::NotFound& e) {}
} catch (...) {
base.abortTransaction(txn);
throw;
}
bool pathChange = false; bool pathChange = false;
bool listChange = false; bool listChange = false;
@ -118,15 +108,8 @@ Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo(
listChange = info.addMessage(account, jid, id); listChange = info.addMessage(account, jid, id);
if (pathChange || listChange) { if (pathChange || listChange) {
try { writeInfo(url, info, txn, true);
writeInfo(url, info, txn, true); txn.commit();
base.commitTransaction(txn);
} catch (...) {
base.abortTransaction(txn);
throw;
}
} else {
base.abortTransaction(txn);
} }
return info; return info;
@ -134,70 +117,51 @@ Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo(
std::list<Shared::MessageInfo> Core::UrlStorage::setPath(const QString& url, const QString& path) { std::list<Shared::MessageInfo> Core::UrlStorage::setPath(const QString& url, const QString& path) {
std::list<Shared::MessageInfo> list; std::list<Shared::MessageInfo> list;
LMDBAL::TransactionID txn = base.beginTransaction(); LMDBAL::WriteTransaction txn = base.beginTransaction();
UrlInfo info; UrlInfo info;
try { try {
urlToInfo->getRecord(url, info, txn); urlToInfo->getRecord(url, info, txn);
info.getMessages(list); info.getMessages(list);
} catch (const LMDBAL::NotFound& e) { } catch (const LMDBAL::NotFound& e) {}
} catch (...) {
base.abortTransaction(txn);
throw;
}
info.setPath(path); info.setPath(path);
try { writeInfo(url, info, txn, true);
writeInfo(url, info, txn, true); txn.commit();
base.commitTransaction(txn);
} catch (...) {
base.abortTransaction(txn);
throw;
}
return list; return list;
} }
std::list<Shared::MessageInfo> Core::UrlStorage::removeFile(const QString& url) { std::list<Shared::MessageInfo> Core::UrlStorage::removeFile(const QString& url) {
std::list<Shared::MessageInfo> list; std::list<Shared::MessageInfo> list;
LMDBAL::TransactionID txn = base.beginTransaction(); LMDBAL::WriteTransaction txn = base.beginTransaction();
UrlInfo info; UrlInfo info;
try { urlToInfo->getRecord(url, info, txn);
urlToInfo->getRecord(url, info, txn); urlToInfo->removeRecord(url, txn);
urlToInfo->removeRecord(url); info.getMessages(list);
info.getMessages(list);
if (info.hasPath()) if (info.hasPath())
pathToUrl->removeRecord(info.getPath()); pathToUrl->removeRecord(info.getPath(), txn);
base.commitTransaction(txn); txn.commit();
} catch (...) {
base.abortTransaction(txn);
throw;
}
return list; return list;
} }
std::list<Shared::MessageInfo> Core::UrlStorage::deletedFile(const QString& path) { std::list<Shared::MessageInfo> Core::UrlStorage::deletedFile(const QString& path) {
std::list<Shared::MessageInfo> list; std::list<Shared::MessageInfo> list;
LMDBAL::TransactionID txn = base.beginTransaction(); LMDBAL::WriteTransaction txn = base.beginTransaction();
try { QString url = pathToUrl->getRecord(path, txn);
QString url = pathToUrl->getRecord(path, txn); pathToUrl->removeRecord(path, txn);
pathToUrl->removeRecord(path);
UrlInfo info = urlToInfo->getRecord(url, txn); UrlInfo info = urlToInfo->getRecord(url, txn);
info.getMessages(list); info.getMessages(list);
info.setPath(QString()); info.setPath(QString());
urlToInfo->changeRecord(url, info, txn); urlToInfo->changeRecord(url, info, txn);
base.commitTransaction(txn); txn.commit();
} catch (...) {
base.abortTransaction(txn);
throw;
}
return list; return list;
} }

View File

@ -60,7 +60,7 @@ private:
private: private:
void writeInfo(const QString& key, const UrlInfo& info, bool overwrite = false); void writeInfo(const QString& key, const UrlInfo& info, bool overwrite = false);
void writeInfo(const QString& key, const UrlInfo& info, MDB_txn* txn, bool overwrite = false); void writeInfo(const QString& key, const UrlInfo& info, const LMDBAL::WriteTransaction& txn, bool overwrite = false);
UrlInfo addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path = "-s"); UrlInfo addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path = "-s");
public: public:

View File

@ -591,7 +591,7 @@ void Core::MessageHandler::resendMessage(const QString& jid, const QString& id)
} else { } else {
qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message seems to have been normally sent, this method was made to retry sending failed to be sent messages, skipping"; qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message seems to have been normally sent, this method was made to retry sending failed to be sent messages, skipping";
} }
} catch (const Archive::NotFound& err) { } catch (const LMDBAL::NotFound& err) {
qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message wasn't found in history, skipping"; qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message wasn't found in history, skipping";
} }
} else { } else {

View File

@ -21,7 +21,7 @@
#include <list> #include <list>
#include <functional> #include <functional>
#include <QXmppOmemoStorage.h> #include <Omemo/QXmppOmemoStorage.h>
#include <cache.h> #include <cache.h>
#include <shared/keyinfo.h> #include <shared/keyinfo.h>

View File

@ -27,7 +27,7 @@ Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObje
account(pAccount), account(pAccount),
name(), name(),
archiveState(empty), archiveState(empty),
archive(new Archive(jid)), archive(new Archive(account, jid)),
syncronizing(false), syncronizing(false),
requestedCount(0), requestedCount(0),
requestedBefore(), requestedBefore(),
@ -38,7 +38,7 @@ Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObje
toCorrect(), toCorrect(),
muc(false) muc(false)
{ {
archive->open(account); archive->open();
if (archive->size() != 0) { if (archive->size() != 0) {
if (archive->isFromTheBeginning()) if (archive->isFromTheBeginning())
@ -126,7 +126,8 @@ void Core::RosterItem::nextRequest() {
last = true; last = true;
} }
} }
} catch (const Archive::Empty& e) { //} catch (const Archive::Empty& e) {
} catch (const LMDBAL::NotFound& e) {
last = true; last = true;
} }
} else if (archiveState == empty && responseCache.size() == 0) { } else if (archiveState == empty && responseCache.size() == 0) {
@ -168,7 +169,8 @@ void Core::RosterItem::performRequest(int count, const QString& before) {
try { try {
Shared::Message msg = archive->newest(); Shared::Message msg = archive->newest();
emit needHistory("", getId(msg), msg.getTime()); emit needHistory("", getId(msg), msg.getTime());
} catch (const Archive::Empty& e) { //this can happen when the only message in archive is not server stored (error, for example) //} catch (const Archive::Empty& e) {
} catch (const LMDBAL::NotFound& e) { //this can happen when the only message in archive is not server stored (error, for example)
emit needHistory(before, ""); emit needHistory(before, "");
} }
} }
@ -186,14 +188,14 @@ void Core::RosterItem::performRequest(int count, const QString& before) {
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), lBefore); std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), lBefore);
responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
found = true; found = true;
} catch (const Archive::NotFound& e) { } catch (const LMDBAL::NotFound& e) {
requestCache.emplace_back(requestedCount, before);
requestedCount = -1;
emit needHistory(getId(archive->oldest()), "");
} catch (const Archive::Empty& e) {
requestCache.emplace_back(requestedCount, before); requestCache.emplace_back(requestedCount, before);
requestedCount = -1; requestedCount = -1;
emit needHistory(getId(archive->oldest()), ""); emit needHistory(getId(archive->oldest()), "");
// } catch (const Archive::Empty& e) {
// requestCache.emplace_back(requestedCount, before);
// requestedCount = -1;
// emit needHistory(getId(archive->oldest()), "");
} }
if (found) { if (found) {
@ -226,10 +228,10 @@ void Core::RosterItem::performRequest(int count, const QString& before) {
try { try {
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before); std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before);
responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
} catch (const Archive::NotFound& e) { } catch (const LMDBAL::NotFound& e) {
qDebug("requesting id hasn't been found in archive, skipping");
} catch (const Archive::Empty& e) {
qDebug("requesting id hasn't been found in archive, skipping"); qDebug("requesting id hasn't been found in archive, skipping");
// } catch (const Archive::Empty& e) {
// qDebug("requesting id hasn't been found in archive, skipping");
} }
nextRequest(); nextRequest();
break; break;
@ -311,7 +313,7 @@ bool Core::RosterItem::changeMessage(const QString& id, const QMap<QString, QVar
try { try {
archive->changeMessage(id, data); archive->changeMessage(id, data);
found = true; found = true;
} catch (const Archive::NotFound& e) { } catch (const LMDBAL::NotFound& e) {
qDebug() << "An attempt to change state to the message" << id << "but it couldn't be found"; qDebug() << "An attempt to change state to the message" << id << "but it couldn't be found";
} }
} }
@ -387,10 +389,8 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before); std::list<Shared::Message> arc = archive->getBefore(requestedCount - responseCache.size(), before);
responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); responseCache.insert(responseCache.begin(), arc.begin(), arc.end());
found = true; found = true;
} catch (const Archive::NotFound& e) { } catch (const LMDBAL::NotFound& e) {
// } catch (const Archive::Empty& e) {
} catch (const Archive::Empty& e) {
} }
if (!found || requestedCount > int(responseCache.size())) { if (!found || requestedCount > int(responseCache.size())) {
if (archiveState == complete) { if (archiveState == complete) {

File diff suppressed because it is too large Load Diff

View File

@ -29,18 +29,21 @@
#include <lmdb.h> #include <lmdb.h>
#include <list> #include <list>
#include <lmdbal/base.h>
#include <lmdbal/storage.h>
#include <lmdbal/cursor.h>
namespace Core { namespace Core {
class Archive : public QObject class Archive : public QObject {
{
Q_OBJECT Q_OBJECT
public: public:
class AvatarInfo; class AvatarInfo;
Archive(const QString& jid, QObject* parent = 0); Archive(const QString& account, const QString& jid, QObject* parent = 0);
~Archive(); ~Archive();
void open(const QString& account); void open();
void close(); void close();
bool addElement(const Shared::Message& message); bool addElement(const Shared::Message& message);
@ -48,13 +51,13 @@ public:
Shared::Message getElement(const QString& id) const; Shared::Message getElement(const QString& id) const;
bool hasElement(const QString& id) const; bool hasElement(const QString& id) const;
void changeMessage(const QString& id, const QMap<QString, QVariant>& data); void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
Shared::Message oldest(); Shared::Message oldest() const;
QString oldestId(); QString oldestId() const;
Shared::Message newest(); Shared::Message newest() const;
QString newestId(); QString newestId() const;
void clear(); void clear();
long unsigned int size() const; long unsigned int size() const;
std::list<Shared::Message> getBefore(int count, const QString& id); std::list<Shared::Message> getBefore(unsigned int count, const QString& id);
bool isFromTheBeginning() const; bool isFromTheBeginning() const;
void setFromTheBeginning(bool is); void setFromTheBeginning(bool is);
bool isEncryptionEnabled() const; bool isEncryptionEnabled() const;
@ -68,103 +71,14 @@ public:
public: public:
const QString jid; const QString jid;
const QString account;
public: public:
class Directory:
public Utils::Exception
{
public:
Directory(const std::string& p_path):Exception(), path(p_path){}
std::string getMessage() const{return "Can't create directory for database at " + path;}
private:
std::string path;
};
class Closed:
public Utils::Exception
{
public:
Closed(const std::string& op, const std::string& acc):Exception(), operation(op), account(acc){}
std::string getMessage() const{return "An attempt to perform operation " + operation + " on closed archive for " + account;}
private:
std::string operation;
std::string account;
};
class NotFound:
public Utils::Exception
{
public:
NotFound(const std::string& k, const std::string& acc):Exception(), key(k), account(acc){}
std::string getMessage() const{return "Element for id " + key + " wasn't found in database " + account;}
private:
std::string key;
std::string account;
};
class Empty:
public Utils::Exception
{
public:
Empty(const std::string& acc):Exception(), account(acc){}
std::string getMessage() const{return "An attempt to read ordered elements from database " + account + " but it's empty";}
private:
std::string account;
};
class Exist:
public Utils::Exception
{
public:
Exist(const std::string& acc, const std::string& p_key):Exception(), account(acc), key(p_key){}
std::string getMessage() const{return "An attempt to insert element " + key + " to database " + account + " but it already has an element with given id";}
private:
std::string account;
std::string key;
};
class NoAvatar:
public Utils::Exception
{
public:
NoAvatar(const std::string& el, const std::string& res):Exception(), element(el), resource(res){
if (resource.size() == 0) {
resource = "for himself";
}
}
std::string getMessage() const{return "Element " + element + " has no avatar for " + resource ;}
private:
std::string element;
std::string resource;
};
class Unknown:
public Utils::Exception
{
public:
Unknown(const std::string& acc, const std::string& message):Exception(), account(acc), msg(message){}
std::string getMessage() const{return "Unknown error on database " + account + ": " + msg;}
private:
std::string account;
std::string msg;
};
class AvatarInfo { class AvatarInfo {
public: public:
AvatarInfo(); AvatarInfo();
AvatarInfo(const QString& type, const QByteArray& hash, bool autogenerated); AvatarInfo(const QString& type, const QByteArray& hash, bool autogenerated);
void deserialize(char* pointer, uint32_t size);
void serialize(QByteArray* ba) const;
QString type; QString type;
QByteArray hash; QByteArray hash;
bool autogenerated; bool autogenerated;
@ -172,29 +86,18 @@ public:
private: private:
bool opened; bool opened;
bool fromTheBeginning; LMDBAL::Base db;
bool encryptionEnabled; LMDBAL::Storage<QString, Shared::Message>* messages;
MDB_env* environment; LMDBAL::Storage<uint64_t, QString>* order;
MDB_dbi main; //id to message LMDBAL::Storage<QString, QVariant>* stats;
MDB_dbi order; //time to id LMDBAL::Storage<QString, AvatarInfo>* avatars;
MDB_dbi stats; LMDBAL::Storage<QString, QString>* stanzaIdToId;
MDB_dbi avatars; mutable LMDBAL::Cursor<uint64_t, QString> cursor;
MDB_dbi sid; //stanzaId to id
bool getStatBoolValue(const std::string& id, MDB_txn* txn);
std::string getStatStringValue(const std::string& id, MDB_txn* txn);
bool setStatValue(const std::string& id, bool value, MDB_txn* txn);
bool setStatValue(const std::string& id, const std::string& value, MDB_txn* txn);
bool readAvatarInfo(AvatarInfo& target, const std::string& res, MDB_txn* txn) const;
void printOrder();
void printKeys();
bool dropAvatar(const std::string& resource);
Shared::Message getMessage(const std::string& id, MDB_txn* txn) const;
Shared::Message getStoredMessage(MDB_txn *txn, MDB_cursor* cursor, MDB_cursor_op op, MDB_val* key, MDB_val* value, int& rc);
Shared::Message edge(bool end);
}; };
} }
QDataStream& operator << (QDataStream &out, const Core::Archive::AvatarInfo& info);
QDataStream& operator >> (QDataStream &in, Core::Archive::AvatarInfo& info);
#endif // CORE_ARCHIVE_H #endif // CORE_ARCHIVE_H

View File

@ -1,55 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free 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.
//
// This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef CORE_CACHE_H
#define CORE_CACHE_H
#include <map>
#include <set>
#include <QString>
#include <core/storage/storage.h>
namespace Core {
template <class K, class V>
class Cache
{
public:
Cache(const QString& name);
~Cache();
void open();
void close();
void addRecord(const K& key, const V& value);
void changeRecord(const K& key, const V& value);
void removeRecord(const K& key);
V getRecord(const K& key) const;
bool checkRecord(const K& key) const;
private:
Core::Storage<K, V> storage;
std::map<K, V>* cache;
std::set<K>* abscent;
};
}
#include "cache.hpp"
#endif // CORE_CACHE_H

View File

@ -1,102 +0,0 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free 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.
//
// This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef CORE_CACHE_HPP
#define CORE_CACHE_HPP
#include "cache.h"
template <class K, class V>
Core::Cache<K, V>::Cache(const QString& name):
storage(name),
cache(new std::map<K, V> ()),
abscent(new std::set<K> ()) {}
template <class K, class V>
Core::Cache<K, V>::~Cache() {
close();
delete cache;
delete abscent;
}
template <class K, class V>
void Core::Cache<K, V>::open() {
storage.open();}
template <class K, class V>
void Core::Cache<K, V>::close() {
storage.close();}
template <class K, class V>
void Core::Cache<K, V>::addRecord(const K& key, const V& value) {
storage.addRecord(key, value);
cache->insert(std::make_pair(key, value));
abscent->erase(key);
}
template <class K, class V>
V Core::Cache<K, V>::getRecord(const K& key) const {
typename std::map<K, V>::const_iterator itr = cache->find(key);
if (itr == cache->end()) {
if (abscent->count(key) > 0) {
throw Archive::NotFound(std::to_string(key), storage.getName().toStdString());
}
try {
V value = storage.getRecord(key);
itr = cache->insert(std::make_pair(key, value)).first;
} catch (const Archive::NotFound& error) {
abscent->insert(key);
throw error;
}
}
return itr->second;
}
template<class K, class V>
bool Core::Cache<K, V>::checkRecord(const K& key) const {
typename std::map<K, V>::const_iterator itr = cache->find(key);
if (itr != cache->end())
return true;
if (abscent->count(key) > 0)
return false;
try {
V value = storage.getRecord(key);
itr = cache->insert(std::make_pair(key, value)).first;
} catch (const Archive::NotFound& error) {
return false;
}
return true;
}
template<class K, class V>
void Core::Cache<K, V>::changeRecord(const K& key, const V& value) {
storage.changeRecord(key, value); //there is a non straightforward behaviour: if there was no element at the sorage it will be added
cache->at(key) = value;
abscent->erase(key); //so... this line here is to make it coherent with the storage
}
template<class K, class V>
void Core::Cache<K, V>::removeRecord(const K& key) {
storage.removeRecord(key);
cache->erase(key);
abscent->insert(key);
}
#endif //CORE_CACHE_HPP

View File

@ -1,70 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CORE_STORAGE_H
#define CORE_STORAGE_H
#include <QString>
#include <lmdb.h>
#include "archive.h"
namespace Core {
/**
* @todo write docs
*/
template <class K, class V>
class Storage
{
public:
Storage(const QString& name);
~Storage();
void open();
void close();
void addRecord(const K& key, const V& value);
void changeRecord(const K& key, const V& value);
void removeRecord(const K& key);
V getRecord(const K& key) const;
QString getName() const;
private:
QString name;
bool opened;
MDB_env* environment;
MDB_dbi base;
};
}
MDB_val& operator << (MDB_val& data, QString& value);
MDB_val& operator >> (MDB_val& data, QString& value);
MDB_val& operator << (MDB_val& data, uint32_t& value);
MDB_val& operator >> (MDB_val& data, uint32_t& value);
namespace std {
std::string to_string(const QString& str);
}
#include "storage.hpp"
#endif // CORE_STORAGE_H

View File

@ -1,226 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CORE_STORAGE_HPP
#define CORE_STORAGE_HPP
#include <QStandardPaths>
#include <QDir>
#include "storage.h"
#include <cstring>
template <class K, class V>
Core::Storage<K, V>::Storage(const QString& p_name):
name(p_name),
opened(false),
environment(),
base()
{
}
template <class K, class V>
Core::Storage<K, V>::~Storage()
{
close();
}
template <class K, class V>
void Core::Storage<K, V>::open()
{
if (!opened) {
mdb_env_create(&environment);
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
path += "/" + name;
QDir cache(path);
if (!cache.exists()) {
bool res = cache.mkpath(path);
if (!res) {
throw Archive::Directory(path.toStdString());
}
}
mdb_env_set_maxdbs(environment, 1);
mdb_env_set_mapsize(environment, 10UL * 1024UL * 1024UL);
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
mdb_dbi_open(txn, "base", MDB_CREATE, &base);
mdb_txn_commit(txn);
opened = true;
}
}
template <class K, class V>
void Core::Storage<K, V>::close()
{
if (opened) {
mdb_dbi_close(environment, base);
mdb_env_close(environment);
opened = false;
}
}
template <class K, class V>
void Core::Storage<K, V>::addRecord(const K& key, const V& value)
{
if (!opened) {
throw Archive::Closed("addRecord", name.toStdString());
}
QByteArray ba;
QDataStream ds(&ba, QIODevice::WriteOnly);
ds << value;
MDB_val lmdbKey, lmdbData;
lmdbKey << key;
lmdbData.mv_size = ba.size();
lmdbData.mv_data = (uint8_t*)ba.data();
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
int rc;
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, MDB_NOOVERWRITE);
if (rc != 0) {
mdb_txn_abort(txn);
if (rc == MDB_KEYEXIST) {
throw Archive::Exist(name.toStdString(), std::to_string(key));
} else {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
} else {
mdb_txn_commit(txn);
}
}
template <class K, class V>
void Core::Storage<K, V>::changeRecord(const K& key, const V& value)
{
if (!opened) {
throw Archive::Closed("changeRecord", name.toStdString());
}
QByteArray ba;
QDataStream ds(&ba, QIODevice::WriteOnly);
ds << value;
MDB_val lmdbKey, lmdbData;
lmdbKey << key;
lmdbData.mv_size = ba.size();
lmdbData.mv_data = (uint8_t*)ba.data();
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
int rc;
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, 0);
if (rc != 0) {
mdb_txn_abort(txn);
if (rc) {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
} else {
mdb_txn_commit(txn);
}
}
template <class K, class V>
V Core::Storage<K, V>::getRecord(const K& key) const
{
if (!opened) {
throw Archive::Closed("addElement", name.toStdString());
}
MDB_val lmdbKey, lmdbData;
lmdbKey << key;
MDB_txn *txn;
int rc;
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
rc = mdb_get(txn, base, &lmdbKey, &lmdbData);
if (rc) {
mdb_txn_abort(txn);
if (rc == MDB_NOTFOUND) {
throw Archive::NotFound(std::to_string(key), name.toStdString());
} else {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
} else {
QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size);
QDataStream ds(&ba, QIODevice::ReadOnly);
V value;
ds >> value;
mdb_txn_abort(txn);
return value;
}
}
template <class K, class V>
void Core::Storage<K, V>::removeRecord(const K& key)
{
if (!opened) {
throw Archive::Closed("addElement", name.toStdString());
}
MDB_val lmdbKey;
lmdbKey << key;
MDB_txn *txn;
int rc;
mdb_txn_begin(environment, NULL, 0, &txn);
rc = mdb_del(txn, base, &lmdbKey, NULL);
if (rc) {
mdb_txn_abort(txn);
if (rc == MDB_NOTFOUND) {
throw Archive::NotFound(std::to_string(key), name.toStdString());
} else {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
} else {
mdb_txn_commit(txn);
}
}
template <class K, class V>
QString Core::Storage<K, V>::getName() const {
return name;}
MDB_val& operator << (MDB_val& data, const QString& value) {
QByteArray ba = value.toUtf8();
data.mv_size = ba.size();
data.mv_data = ba.data();
return data;
}
MDB_val& operator >> (MDB_val& data, QString& value) {
value = QString::fromUtf8((const char*)data.mv_data, data.mv_size);
return data;
}
MDB_val& operator << (MDB_val& data, uint32_t& value) {
data.mv_size = 4;
data.mv_data = &value;
return data;
}
MDB_val& operator >> (MDB_val& data, uint32_t& value) {
std::memcpy(&value, data.mv_data, data.mv_size);
return data;
}
std::string std::to_string(const QString& str) {
return str.toStdString();
}
#endif //CORE_STORAGE_HPP

2
external/qxmpp vendored

@ -1 +1 @@
Subproject commit ab4bdf2da41a26f462fe3a333a34e32c999e2a6d Subproject commit 9e9c22b16a39c7370fed31c6deea56d8abf72440

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.5)
project(simplecrypt LANGUAGES CXX) project(simplecrypt LANGUAGES CXX)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)

View File

@ -24,7 +24,7 @@
#include <QObject> #include <QObject>
#ifdef WITH_OMEMO #ifdef WITH_OMEMO
#include <QXmppOmemoStorage.h> #include <Omemo/QXmppOmemoStorage.h>
#endif #endif
int main(int argc, char *argv[]) int main(int argc, char *argv[])

View File

@ -61,50 +61,42 @@ Shared::Message::Message():
attachPath() attachPath()
{} {}
QString Shared::Message::getBody() const QString Shared::Message::getBody() const {
{
return body; return body;
} }
QString Shared::Message::getFrom() const QString Shared::Message::getFrom() const {
{
QString from = jFrom; QString from = jFrom;
if (rFrom.size() > 0) { if (rFrom.size() > 0)
from += "/" + rFrom; from += "/" + rFrom;
}
return from; return from;
} }
QString Shared::Message::getTo() const QString Shared::Message::getTo() const {
{
QString to = jTo; QString to = jTo;
if (rTo.size() > 0) { if (rTo.size() > 0)
to += "/" + rTo; to += "/" + rTo;
}
return to; return to;
} }
QString Shared::Message::getId() const QString Shared::Message::getId() const {
{ if (id.size() > 0)
if (id.size() > 0) {
return id; return id;
} else { else
return stanzaId; return stanzaId;
}
} }
QDateTime Shared::Message::getTime() const QDateTime Shared::Message::getTime() const {
{
return time; return time;
} }
void Shared::Message::setBody(const QString& p_body) void Shared::Message::setBody(const QString& p_body) {
{
body = p_body; body = p_body;
} }
void Shared::Message::setFrom(const QString& from) void Shared::Message::setFrom(const QString& from) {
{
QStringList list = from.split("/"); QStringList list = from.split("/");
if (list.size() == 1) { if (list.size() == 1) {
jFrom = from.toLower(); jFrom = from.toLower();
@ -114,8 +106,7 @@ void Shared::Message::setFrom(const QString& from)
} }
} }
void Shared::Message::setTo(const QString& to) void Shared::Message::setTo(const QString& to) {
{
QStringList list = to.split("/"); QStringList list = to.split("/");
if (list.size() == 1) { if (list.size() == 1) {
jTo = to.toLower(); jTo = to.toLower();
@ -125,153 +116,122 @@ void Shared::Message::setTo(const QString& to)
} }
} }
void Shared::Message::setId(const QString& p_id) void Shared::Message::setId(const QString& p_id) {
{
id = p_id; id = p_id;
} }
void Shared::Message::setTime(const QDateTime& p_time) void Shared::Message::setTime(const QDateTime& p_time) {
{
time = p_time; time = p_time;
} }
QString Shared::Message::getFromJid() const QString Shared::Message::getFromJid() const {
{
return jFrom; return jFrom;
} }
QString Shared::Message::getFromResource() const QString Shared::Message::getFromResource() const {
{
return rFrom; return rFrom;
} }
QString Shared::Message::getToJid() const QString Shared::Message::getToJid() const {
{
return jTo; return jTo;
} }
QString Shared::Message::getToResource() const QString Shared::Message::getToResource() const {
{
return rTo; return rTo;
} }
QString Shared::Message::getErrorText() const QString Shared::Message::getErrorText() const {
{
return errorText; return errorText;
} }
QString Shared::Message::getPenPalJid() const QString Shared::Message::getPenPalJid() const {
{ if (outgoing)
if (outgoing) {
return jTo; return jTo;
} else { else
return jFrom; return jFrom;
}
} }
QString Shared::Message::getPenPalResource() const QString Shared::Message::getPenPalResource() const {
{ if (outgoing)
if (outgoing) {
return rTo; return rTo;
} else { else
return rFrom; return rFrom;
}
} }
Shared::Message::State Shared::Message::getState() const Shared::Message::State Shared::Message::getState() const {
{
return state; return state;
} }
bool Shared::Message::getEdited() const bool Shared::Message::getEdited() const {
{
return edited; return edited;
} }
void Shared::Message::setFromJid(const QString& from) void Shared::Message::setFromJid(const QString& from) {
{
jFrom = from.toLower(); jFrom = from.toLower();
} }
void Shared::Message::setFromResource(const QString& from) void Shared::Message::setFromResource(const QString& from) {
{
rFrom = from; rFrom = from;
} }
void Shared::Message::setToJid(const QString& to) void Shared::Message::setToJid(const QString& to) {
{
jTo = to.toLower(); jTo = to.toLower();
} }
void Shared::Message::setToResource(const QString& to) void Shared::Message::setToResource(const QString& to) {
{
rTo = to; rTo = to;
} }
void Shared::Message::setErrorText(const QString& err) void Shared::Message::setErrorText(const QString& err) {
{ if (state == State::error)
if (state == State::error) {
errorText = err; errorText = err;
}
} }
bool Shared::Message::getOutgoing() const bool Shared::Message::getOutgoing() const {
{
return outgoing; return outgoing;
} }
void Shared::Message::setOutgoing(bool og) void Shared::Message::setOutgoing(bool og) {
{
outgoing = og; outgoing = og;
} }
bool Shared::Message::getForwarded() const bool Shared::Message::getForwarded() const {
{
return forwarded; return forwarded;
} }
void Shared::Message::generateRandomId() void Shared::Message::generateRandomId() {
{
id = generateUUID(); id = generateUUID();
} }
QString Shared::Message::getThread() const QString Shared::Message::getThread() const {
{
return thread; return thread;
} }
void Shared::Message::setForwarded(bool fwd) void Shared::Message::setForwarded(bool fwd) {
{
forwarded = fwd; forwarded = fwd;
} }
void Shared::Message::setThread(const QString& p_body) void Shared::Message::setThread(const QString& p_body) {
{
thread = p_body; thread = p_body;
} }
QDateTime Shared::Message::getLastModified() const QDateTime Shared::Message::getLastModified() const {
{
return lastModified; return lastModified;
} }
QString Shared::Message::getOriginalBody() const QString Shared::Message::getOriginalBody() const {
{
return originalMessage; return originalMessage;
} }
Shared::Message::Type Shared::Message::getType() const Shared::Message::Type Shared::Message::getType() const {
{
return type; return type;
} }
void Shared::Message::setType(Shared::Message::Type t) void Shared::Message::setType(Shared::Message::Type t) {
{
type = t; type = t;
} }
void Shared::Message::setState(Shared::Message::State p_state) void Shared::Message::setState(Shared::Message::State p_state) {
{
state = p_state; state = p_state;
if (state != State::error) { if (state != State::error) {
@ -279,96 +239,92 @@ void Shared::Message::setState(Shared::Message::State p_state)
} }
} }
bool Shared::Message::serverStored() const bool Shared::Message::serverStored() const {
{
return state == State::delivered || state == State::sent; return state == State::delivered || state == State::sent;
} }
void Shared::Message::setEdited(bool p_edited) void Shared::Message::setEdited(bool p_edited) {
{
edited = p_edited; edited = p_edited;
} }
void Shared::Message::serialize(QDataStream& data) const QDataStream& operator<<(QDataStream& out, const Shared::Message& info) {
{ out << info.jFrom;
data << jFrom; out << info.rFrom;
data << rFrom; out << info.jTo;
data << jTo; out << info.rTo;
data << rTo; out << info.id;
data << id; out << info.body;
data << body; out << info.time;
data << time; out << info.thread;
data << thread; out << (quint8)info.type;
data << (quint8)type; out << info.outgoing;
data << outgoing; out << info.forwarded;
data << forwarded; out << info.oob;
data << oob; out << (quint8)info.state;
data << (quint8)state; out << info.edited;
data << edited; if (info.state == Shared::Message::State::error)
if (state == State::error) { out << info.errorText;
data << errorText;
if (info.edited) {
out << info.originalMessage;
out << info.lastModified;
} }
if (edited) { out << info.stanzaId;
data << originalMessage; out << info.attachPath;
data << lastModified;
} return out;
data << stanzaId;
data << attachPath;
} }
void Shared::Message::deserialize(QDataStream& data) QDataStream & operator>>(QDataStream& in, Shared::Message& info) {
{ in >> info.jFrom;
data >> jFrom; in >> info.rFrom;
data >> rFrom; in >> info.jTo;
data >> jTo; in >> info.rTo;
data >> rTo; in >> info.id;
data >> id; in >> info.body;
data >> body; in >> info.time;
data >> time; in >> info.thread;
data >> thread;
quint8 t; quint8 t;
data >> t; in >> t;
type = static_cast<Type>(t); info.type = static_cast<Shared::Message::Type>(t);
data >> outgoing; in >> info.outgoing;
data >> forwarded; in >> info.forwarded;
data >> oob; in >> info.oob;
quint8 s; quint8 s;
data >> s; in >> s;
state = static_cast<State>(s); info.state = static_cast<Shared::Message::State>(s);
data >> edited; in >> info.edited;
if (state == State::error) { if (info.state == Shared::Message::State::error)
data >> errorText; in >> info.errorText;
if (info.edited) {
in >> info.originalMessage;
in >> info.lastModified;
} }
if (edited) { in >> info.stanzaId;
data >> originalMessage; in >> info.attachPath;
data >> lastModified;
} return in;
data >> stanzaId;
data >> attachPath;
} }
bool Shared::Message::change(const QMap<QString, QVariant>& data) bool Shared::Message::change(const QMap<QString, QVariant>& data)
{ {
QMap<QString, QVariant>::const_iterator itr = data.find("state"); QMap<QString, QVariant>::const_iterator itr = data.find("state");
if (itr != data.end()) { if (itr != data.end())
setState(static_cast<State>(itr.value().toUInt())); setState(static_cast<State>(itr.value().toUInt()));
}
itr = data.find("outOfBandUrl"); itr = data.find("outOfBandUrl");
if (itr != data.end()) { if (itr != data.end())
setOutOfBandUrl(itr.value().toString()); setOutOfBandUrl(itr.value().toString());
}
itr = data.find("attachPath"); itr = data.find("attachPath");
if (itr != data.end()) { if (itr != data.end())
setAttachPath(itr.value().toString()); setAttachPath(itr.value().toString());
}
if (state == State::error) { if (state == State::error) {
itr = data.find("errorText"); itr = data.find("errorText");
if (itr != data.end()) { if (itr != data.end())
setErrorText(itr.value().toString()); setErrorText(itr.value().toString());
}
} }
bool idChanged = false; bool idChanged = false;
@ -386,9 +342,8 @@ bool Shared::Message::change(const QMap<QString, QVariant>& data)
QString newId = itr.value().toString(); QString newId = itr.value().toString();
if (stanzaId != newId) { if (stanzaId != newId) {
setStanzaId(newId); setStanzaId(newId);
if (id.size() == 0) { if (id.size() == 0)
idChanged = true; idChanged = true;
}
} }
} }
@ -398,15 +353,15 @@ bool Shared::Message::change(const QMap<QString, QVariant>& data)
if (body != b) { if (body != b) {
QMap<QString, QVariant>::const_iterator dItr = data.find("stamp"); QMap<QString, QVariant>::const_iterator dItr = data.find("stamp");
QDateTime correctionDate; QDateTime correctionDate;
if (dItr != data.end()) { if (dItr != data.end())
correctionDate = dItr.value().toDateTime(); correctionDate = dItr.value().toDateTime();
} else { else
correctionDate = QDateTime::currentDateTimeUtc(); //in case there is no information about time of this correction it's applied correctionDate = QDateTime::currentDateTimeUtc(); //in case there is no information about time of this correction it's applied
}
if (!edited || lastModified < correctionDate) { if (!edited || lastModified < correctionDate) {
if (!edited) { if (!edited)
originalMessage = body; originalMessage = body;
}
lastModified = correctionDate; lastModified = correctionDate;
setBody(b); setBody(b);
setEdited(true); setEdited(true);
@ -416,57 +371,47 @@ bool Shared::Message::change(const QMap<QString, QVariant>& data)
QMap<QString, QVariant>::const_iterator dItr = data.find("stamp"); QMap<QString, QVariant>::const_iterator dItr = data.find("stamp");
if (dItr != data.end()) { if (dItr != data.end()) {
QDateTime ntime = dItr.value().toDateTime(); QDateTime ntime = dItr.value().toDateTime();
if (time != ntime) { if (time != ntime)
setTime(ntime); setTime(ntime);
}
} }
} }
return idChanged; return idChanged;
} }
void Shared::Message::setCurrentTime() void Shared::Message::setCurrentTime() {
{
time = QDateTime::currentDateTimeUtc(); time = QDateTime::currentDateTimeUtc();
} }
QString Shared::Message::getOutOfBandUrl() const QString Shared::Message::getOutOfBandUrl() const {
{
return oob; return oob;
} }
bool Shared::Message::hasOutOfBandUrl() const bool Shared::Message::hasOutOfBandUrl() const {
{
return oob.size() > 0; return oob.size() > 0;
} }
void Shared::Message::setOutOfBandUrl(const QString& url) void Shared::Message::setOutOfBandUrl(const QString& url) {
{
oob = url; oob = url;
} }
bool Shared::Message::storable() const bool Shared::Message::storable() const {
{
return id.size() > 0 && (body.size() > 0 || oob.size() > 0 || attachPath.size() > 0); return id.size() > 0 && (body.size() > 0 || oob.size() > 0 || attachPath.size() > 0);
} }
void Shared::Message::setStanzaId(const QString& sid) void Shared::Message::setStanzaId(const QString& sid) {
{
stanzaId = sid; stanzaId = sid;
} }
QString Shared::Message::getStanzaId() const QString Shared::Message::getStanzaId() const {
{
return stanzaId; return stanzaId;
} }
QString Shared::Message::getAttachPath() const QString Shared::Message::getAttachPath() const {
{
return attachPath; return attachPath;
} }
void Shared::Message::setAttachPath(const QString& path) void Shared::Message::setAttachPath(const QString& path) {
{
attachPath = path; attachPath = path;
} }
@ -474,18 +419,15 @@ Shared::Message::Change::Change(const QMap<QString, QVariant>& _data):
data(_data), data(_data),
idModified(false) {} idModified(false) {}
void Shared::Message::Change::operator()(Shared::Message& msg) void Shared::Message::Change::operator()(Shared::Message& msg) {
{
idModified = msg.change(data); idModified = msg.change(data);
} }
void Shared::Message::Change::operator()(Shared::Message* msg) void Shared::Message::Change::operator()(Shared::Message* msg) {
{
idModified = msg->change(data); idModified = msg->change(data);
} }
bool Shared::Message::Change::hasIdBeenModified() const bool Shared::Message::Change::hasIdBeenModified() const {
{
return idModified; return idModified;
} }

View File

@ -25,12 +25,20 @@
#include <QMap> #include <QMap>
#include <QDataStream> #include <QDataStream>
namespace Shared {
class Message;
}
QDataStream& operator << (QDataStream& out, const Shared::Message& info);
QDataStream& operator >> (QDataStream& in, Shared::Message& info);
namespace Shared { namespace Shared {
/** /**
* @todo write docs * @todo write docs
*/ */
class Message { class Message {
friend QDataStream& ::operator << (QDataStream& out, const Shared::Message& info);
friend QDataStream& ::operator >> (QDataStream& in, Shared::Message& info);
public: public:
enum Type { enum Type {
error, error,
@ -116,9 +124,6 @@ public:
QString getStanzaId() const; QString getStanzaId() const;
QString getAttachPath() const; QString getAttachPath() const;
void serialize(QDataStream& data) const;
void deserialize(QDataStream& data);
private: private:
QString jFrom; QString jFrom;
QString rFrom; QString rFrom;