// 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 DataBase::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 DataBase::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); getCache(encryption)->drop(); 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 DataBase::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); KeyCache* cache = getCache(encryption); for (const QString& keyOwnerJid : keyOwnerJids) { Keys map = cache->getRecord(keyOwnerJid); uint count = 0; for (std::pair& pair : map) { Shared::TrustLevel& current = pair.second; if (current == oldLevel) { current = newLevel; modifiedKeys[encryption].insert(keyOwnerJid, pair.first); ++count; } } if (count > 0) { cache->changeRecord(keyOwnerJid, map); } } 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); KeyCache* cache = getCache(encryption); 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); 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); cache->changeRecord(keyOwnerJid, map); } } catch (const DataBase::NotFound& e) { Keys map({{keyId, level}}); modifiedKeys[encryption].insert(keyOwnerJid, keyId); cache->addRecord(keyOwnerJid, map); } } 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 DataBase::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 DataBase::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) { getCache(encryption)->drop(); return Core::makeReadyTask(); } QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const QString& keyOwnerJid) { getCache(encryption)->removeRecord(keyOwnerJid); return Core::makeReadyTask(); } QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const QList& keyIds) { std::set set; for (const QByteArray& keyId : keyIds) set.insert(keyId); KeyCache* cache = getCache(encryption); std::map data = cache->readAll(); bool changed = false; 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++); changed = true; } else ++itr; } if (byOwner.size() > 0) data.erase(cItr++); else ++cItr; } if (changed) cache->replaceAll(data); 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; try { data = cache->getRecord(keyOwnerJid); had = true; } catch (const DataBase::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); } else { cache->addRecord(keyOwnerJid, data); } return Core::makeReadyTask(); } QXmppTask Core::TrustHandler::ownKey(const QString& encryption) { QByteArray res; try { res = ownKeys->getRecord(encryption); } catch (const DataBase::NotFound& e) {} return Core::makeReadyTask(std::move(res)); } QXmppTask Core::TrustHandler::resetOwnKey(const QString& encryption) { try { ownKeys->removeRecord(encryption); } catch (const DataBase::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 DataBase::NotFound& e) {} return Core::makeReadyTask(std::move(res)); } QXmppTask Core::TrustHandler::resetSecurityPolicy(const QString& encryption) { try { securityPolicies->removeRecord(encryption); } catch (const DataBase::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 DataBase::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 DataBase::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 } }