// Squawk messenger. // Copyright (C) 2019 Yury Gubich // // 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 . #include "trusthandler.h" #include "core/account.h" #include "core/adapterfunctions.h" Core::TrustHandler::TrustHandler(Account* account): QObject(), QXmppTrustStorage(), acc(account), db(acc->getName() + "/trust"), protocols(db.createDirectory() + "/protocols"), securityPolicies(db.addCache("securityPolicies")), ownKeys(db.addCache("ownKeys")), keysByProtocol() { if (!protocols.open(QIODevice::ReadWrite | QIODevice::Text)) //never supposed to happen since I have just created a directory; throw LMDBAL::Directory(protocols.fileName().toStdString()); QTextStream in(&protocols); while(!in.atEnd()) { QString protocol = in.readLine(); if (protocol.size() > 1) { //I'm afraid of reading some nonsence like separately standing \n or EF or BOM, so... let it be at least 2 chars long KeyCache* cache = db.addCache(protocol.toStdString()); keysByProtocol.insert(std::make_pair(protocol, cache)); } } protocols.close(); db.open(); } Core::TrustHandler::~TrustHandler() { protocols.close(); db.close(); } Core::TrustHandler::KeyCache * Core::TrustHandler::getCache(const QString& encryption) { std::map::iterator itr = keysByProtocol.find(encryption); if (itr == keysByProtocol.end()) return createNewCache(encryption); else return itr->second; } Core::TrustHandler::KeyCache * Core::TrustHandler::createNewCache(const QString& encryption) { db.close(); KeyCache* cache = db.addCache(encryption.toStdString()); keysByProtocol.insert(std::make_pair(encryption, cache)); if (!protocols.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) throw LMDBAL::Directory(protocols.fileName().toStdString()); QTextStream out(&protocols); out << encryption + "\n"; protocols.close(); db.open(); return cache; } QXmppTask Core::TrustHandler::resetAll(const QString& encryption) { securityPolicies->removeRecord(encryption); ownKeys->removeRecord(encryption); std::map::const_iterator itr = keysByProtocol.find(encryption); if (itr == keysByProtocol.end()) return Core::makeReadyTask(); LMDBAL::WriteTransaction txn = db.beginTransaction(); KeyCache* cache = itr->second; std::map keys = cache->readAll(txn); cache->drop(txn); txn.commit(); for (const std::pair& pair : keys) { bool empty = true; for (const std::pair& trust : pair.second) { if (trust.second != Shared::TrustLevel::undecided) { empty = false; break; } } if (!empty) emit trustLevelsChanged(pair.first, getSummary(pair.first)); } return Core::makeReadyTask(); } QXmppTask Core::TrustHandler::trustLevel( const QString& encryption, const QString& keyOwnerJid, const QByteArray& keyId ) { KeyCache* cache = getCache(encryption); Shared::TrustLevel level = Shared::TrustLevel::undecided; try { Keys map = cache->getRecord(keyOwnerJid); Keys::const_iterator itr = map.find(keyId); if (itr != map.end()) level = itr->second; } catch (const LMDBAL::NotFound& e) {} return Core::makeReadyTask(std::move(convert(level))); } QXmppTask>> Core::TrustHandler::setTrustLevel( const QString& encryption, const QList& keyOwnerJids, QXmpp::TrustLevel oldTrustLevel, QXmpp::TrustLevel newTrustLevel ) { QHash> modifiedKeys; Shared::TrustLevel oldLevel = convert(oldTrustLevel); Shared::TrustLevel newLevel = convert(newTrustLevel); std::set modifiedJids; KeyCache* cache = getCache(encryption); LMDBAL::WriteTransaction txn = db.beginTransaction(); for (const QString& keyOwnerJid : keyOwnerJids) { Keys map = cache->getRecord(keyOwnerJid, txn); uint count = 0; for (std::pair& pair : map) { Shared::TrustLevel& current = pair.second; if (current == oldLevel) { current = newLevel; modifiedKeys[encryption].insert(keyOwnerJid, pair.first); modifiedJids.insert(keyOwnerJid); ++count; } } if (count > 0) cache->changeRecord(keyOwnerJid, map, txn); } txn.commit(); for (const QString& jid : modifiedJids) emit trustLevelsChanged(jid, getSummary(jid)); return Core::makeReadyTask(std::move(modifiedKeys)); } QXmppTask>> Core::TrustHandler::setTrustLevel( const QString& encryption, const QMultiHash& keyIds, QXmpp::TrustLevel trustLevel ) { QHash> modifiedKeys; Shared::TrustLevel level = convert(trustLevel); std::set modifiedJids; KeyCache* cache = getCache(encryption); LMDBAL::WriteTransaction txn = db.beginTransaction(); for (MultySB::const_iterator itr = keyIds.begin(), end = keyIds.end(); itr != end; ++itr) { const QString& keyOwnerJid = itr.key(); const QByteArray& keyId = itr.value(); try { Keys map = cache->getRecord(keyOwnerJid, txn); std::pair result = map.insert(std::make_pair(keyId, level)); bool changed = result.second; if (!changed && result.first->second != level) { result.first->second = level; changed = true; } if (changed) { modifiedKeys[encryption].insert(keyOwnerJid, keyId); modifiedJids.insert(keyOwnerJid); cache->changeRecord(keyOwnerJid, map, txn); } } catch (const LMDBAL::NotFound& e) { Keys map({{keyId, level}}); modifiedKeys[encryption].insert(keyOwnerJid, keyId); modifiedJids.insert(keyOwnerJid); cache->addRecord(keyOwnerJid, map, txn); } } txn.commit(); for (const QString& jid : modifiedJids) emit trustLevelsChanged(jid, getSummary(jid)); return Core::makeReadyTask(std::move(modifiedKeys)); } QXmppTask Core::TrustHandler::hasKey( const QString& encryption, const QString& keyOwnerJid, QXmpp::TrustLevels trustLevels ) { KeyCache* cache = getCache(encryption); bool found = false; try { Keys map = cache->getRecord(keyOwnerJid); for (const std::pair& pair : map) { if (trustLevels.testFlag(convert(pair.second))) { found = true; break; } } } catch (const LMDBAL::NotFound& e) {} return Core::makeReadyTask(std::move(found)); } QXmppTask>> Core::TrustHandler::keys( const QString& encryption, const QList& keyOwnerJids, QXmpp::TrustLevels trustLevels ) { HSHBTL res; KeyCache* cache = getCache(encryption); for (const QString& keyOwnerJid : keyOwnerJids) { try { Keys map = cache->getRecord(keyOwnerJid); QHash& pRes = res[keyOwnerJid]; for (const std::pair& pair : map) { QXmpp::TrustLevel level = convert(pair.second); if (!trustLevels || trustLevels.testFlag(level)) { pRes.insert(pair.first, level); } } } catch (const LMDBAL::NotFound& e) {} } return Core::makeReadyTask(std::move(res)); } QXmppTask>> Core::TrustHandler::keys( const QString& encryption, QXmpp::TrustLevels trustLevels ) { QHash res; KeyCache* cache = getCache(encryption); std::map storage = cache->readAll(); for (const std::pair& value : storage) { for (const std::pair& pair : value.second) { QXmpp::TrustLevel level = convert(pair.second); if (!trustLevels || trustLevels.testFlag(level)) res[level].insert(value.first, pair.first); } } return Core::makeReadyTask(std::move(res)); } QXmppTask Core::TrustHandler::removeKeys(const QString& encryption) { std::map::const_iterator itr = keysByProtocol.find(encryption); if (itr == keysByProtocol.end()) return Core::makeReadyTask(); LMDBAL::WriteTransaction txn = db.beginTransaction(); KeyCache* cache = itr->second; std::map keys = cache->readAll(txn); cache->drop(txn); txn.commit(); for (const std::pair& pair : keys) { bool empty = true; for (const std::pair& trust : pair.second) { if (trust.second != Shared::TrustLevel::undecided) { empty = false; break; } } if (!empty) emit trustLevelsChanged(pair.first, getSummary(pair.first)); } return Core::makeReadyTask(); } QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const QString& keyOwnerJid) { std::map::const_iterator itr = keysByProtocol.find(encryption); if (itr == keysByProtocol.end()) return Core::makeReadyTask(); KeyCache* cache = itr->second; try { cache->removeRecord(keyOwnerJid); emit trustLevelsChanged(keyOwnerJid, getSummary(keyOwnerJid)); //TODO there is a probability of notification without the actial change } catch (const LMDBAL::NotFound& e) {} //if the movin entry was empty or if it consisted of Undecided keys return Core::makeReadyTask(); } QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const QList& keyIds) { std::set set; for (const QByteArray& keyId : keyIds) set.insert(keyId); LMDBAL::WriteTransaction txn = db.beginTransaction(); KeyCache* cache = getCache(encryption); std::map data = cache->readAll(txn); std::set modifiedJids; for (std::map::iterator cItr = data.begin(), cEnd = data.end(); cItr != cEnd; /*no increment*/) { Keys& byOwner = cItr->second; for (Keys::const_iterator itr = byOwner.begin(), end = byOwner.end(); itr != end; /*no increment*/) { const QByteArray& keyId = itr->first; if (set.erase(keyId)) { byOwner.erase(itr++); modifiedJids.insert(cItr->first); } else ++itr; } if (byOwner.size() > 0) data.erase(cItr++); else ++cItr; } if (modifiedJids.size() > 0) cache->replaceAll(data, txn); txn.commit(); for (const QString& jid : modifiedJids) emit trustLevelsChanged(jid, getSummary(jid)); return Core::makeReadyTask(); } QXmppTask Core::TrustHandler::addKeys( const QString& encryption, const QString& keyOwnerJid, const QList& keyIds, QXmpp::TrustLevel trustLevel ) { KeyCache* cache = getCache(encryption); Shared::TrustLevel level = convert(trustLevel); Keys data; bool had = false; LMDBAL::WriteTransaction txn = db.beginTransaction(); try { data = cache->getRecord(keyOwnerJid, txn); had = true; } catch (const LMDBAL::NotFound& e) {} for (const QByteArray& keyId : keyIds) { std::pair result = data.insert(std::make_pair(keyId, level)); if (!result.second) result.first->second = level; } if (had) cache->changeRecord(keyOwnerJid, data, txn); else cache->addRecord(keyOwnerJid, data, txn); txn.commit(); emit trustLevelsChanged(keyOwnerJid, getSummary(keyOwnerJid)); return Core::makeReadyTask(); } QXmppTask Core::TrustHandler::ownKey(const QString& encryption) { QByteArray res; try { res = ownKeys->getRecord(encryption); } catch (const LMDBAL::NotFound& e) {} return Core::makeReadyTask(std::move(res)); } QXmppTask Core::TrustHandler::resetOwnKey(const QString& encryption) { try { ownKeys->removeRecord(encryption); } catch (const LMDBAL::NotFound& e) {} return Core::makeReadyTask(); } QXmppTask Core::TrustHandler::setOwnKey(const QString& encryption, const QByteArray& keyId) { ownKeys->forceRecord(encryption, keyId); return Core::makeReadyTask(); } QXmppTask Core::TrustHandler::securityPolicy(const QString& encryption) { QXmpp::TrustSecurityPolicy res; try { res = static_cast(securityPolicies->getRecord(encryption)); } catch (const LMDBAL::NotFound& e) {} return Core::makeReadyTask(std::move(res)); } QXmppTask Core::TrustHandler::resetSecurityPolicy(const QString& encryption) { try { securityPolicies->removeRecord(encryption); } catch (const LMDBAL::NotFound& e) {} return Core::makeReadyTask(); } QXmppTask Core::TrustHandler::setSecurityPolicy( const QString& encryption, QXmpp::TrustSecurityPolicy securityPolicy) { uint8_t pol = securityPolicy; securityPolicies->forceRecord(encryption, pol); return Core::makeReadyTask(); } Core::TrustHandler::Keys Core::TrustHandler::getKeys(Shared::EncryptionProtocol protocol, const QString& jid) const { const QString& prt = Shared::TrustSummary::protocolKeys.at(protocol); std::map::const_iterator itr = keysByProtocol.find(prt); if (itr != keysByProtocol.end()) { try { Keys map = itr->second->getRecord(jid); return map; } catch (const LMDBAL::NotFound& e) { return Keys(); } } else { return Keys(); } } Shared::TrustSummary Core::TrustHandler::getSummary(const QString& jid) const { Shared::TrustSummary result; for (const std::pair& pair : keysByProtocol) { try { Keys keys = pair.second->getRecord(jid); Shared::EncryptionProtocol protocol = Shared::TrustSummary::protocolValues.at(pair.first); for (const std::pair& trust : keys) result.increment(protocol, trust.second); } catch (const LMDBAL::NotFound& e) {} } return result; } Shared::TrustLevel Core::TrustHandler::convert(Core::TrustHandler::TL level) { switch (level) { case QXmpp::TrustLevel::Undecided: return Shared::TrustLevel::undecided; case QXmpp::TrustLevel::AutomaticallyDistrusted: return Shared::TrustLevel::automaticallyDistrusted; case QXmpp::TrustLevel::ManuallyDistrusted: return Shared::TrustLevel::manuallyDistrusted; case QXmpp::TrustLevel::AutomaticallyTrusted: return Shared::TrustLevel::automaticallyTrusted; case QXmpp::TrustLevel::ManuallyTrusted: return Shared::TrustLevel::manuallyTrusted; case QXmpp::TrustLevel::Authenticated: return Shared::TrustLevel::authenticated; default: throw 2413; //never supposed to get here, switch case if complete, this line is just to suppress a warning } } Core::TrustHandler::TL Core::TrustHandler::convert(Shared::TrustLevel level) { switch (level) { case Shared::TrustLevel::undecided: return QXmpp::TrustLevel::Undecided; case Shared::TrustLevel::automaticallyDistrusted: return QXmpp::TrustLevel::AutomaticallyDistrusted; case Shared::TrustLevel::manuallyDistrusted: return QXmpp::TrustLevel::ManuallyDistrusted; case Shared::TrustLevel::automaticallyTrusted: return QXmpp::TrustLevel::AutomaticallyTrusted; case Shared::TrustLevel::manuallyTrusted: return QXmpp::TrustLevel::ManuallyTrusted; case Shared::TrustLevel::authenticated: return QXmpp::TrustLevel::Authenticated; default: throw 2413; //never supposed to get here, switch case if complete, this line is just to suppress a warning } }