/* * Copyright (C) 2018 KeePassXC Team * * 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 2 or (at your option) * version 3 of the License. * * 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 "KeeShare.h" #include "core/Config.h" #include "core/CustomData.h" #include "core/Database.h" #include "core/DatabaseIcons.h" #include "core/Group.h" #include "core/Metadata.h" #include "crypto/ssh/OpenSSHKey.h" #include "keeshare/ShareObserver.h" #include "keeshare/Signature.h" #include #include #include namespace { static const QString KeeShare_Reference("KeeShare/Reference"); static const QString KeeShare_Own("KeeShare/Settings.own"); static const QString KeeShare_Foreign("KeeShare/Settings.foreign"); static const QString KeeShare_Active("KeeShare/Settings.active"); } // namespace KeeShare* KeeShare::m_instance = nullptr; KeeShare* KeeShare::instance() { if (!m_instance) { qFatal("Race condition: instance wanted before it was initialized, this is a bug."); } return m_instance; } KeeShare::KeeShare(QObject* parent) : QObject(parent) { connect(config(), SIGNAL(changed(QString)), SLOT(handleSettingsChanged(QString))); } void KeeShare::init(QObject* parent) { Q_ASSERT(!m_instance); m_instance = new KeeShare(parent); } KeeShareSettings::Own KeeShare::own() { return KeeShareSettings::Own::deserialize(config()->get(KeeShare_Own).toString()); } KeeShareSettings::Active KeeShare::active() { return KeeShareSettings::Active::deserialize(config()->get(KeeShare_Active).toString()); } KeeShareSettings::Foreign KeeShare::foreign() { return KeeShareSettings::Foreign::deserialize(config()->get(KeeShare_Foreign).toString()); } void KeeShare::setForeign(const KeeShareSettings::Foreign& foreign) { config()->set(KeeShare_Foreign, KeeShareSettings::Foreign::serialize(foreign)); } void KeeShare::setActive(const KeeShareSettings::Active& active) { config()->set(KeeShare_Active, KeeShareSettings::Active::serialize(active)); } void KeeShare::setOwn(const KeeShareSettings::Own& own) { config()->set(KeeShare_Own, KeeShareSettings::Own::serialize(own)); } bool KeeShare::isShared(const Group* group) { return group && group->customData()->contains(KeeShare_Reference); } KeeShareSettings::Reference KeeShare::referenceOf(const Group* group) { static const KeeShareSettings::Reference s_emptyReference; const CustomData* customData = group->customData(); if (!customData->contains(KeeShare_Reference)) { return s_emptyReference; } const auto encoded = customData->value(KeeShare_Reference); const auto serialized = QString::fromUtf8(QByteArray::fromBase64(encoded.toLatin1())); KeeShareSettings::Reference reference = KeeShareSettings::Reference::deserialize(serialized); if (reference.isNull()) { qWarning("Invalid sharing reference detected - sharing disabled"); return s_emptyReference; } return reference; } void KeeShare::setReferenceTo(Group* group, const KeeShareSettings::Reference& reference) { CustomData* customData = group->customData(); if (reference.isNull()) { customData->remove(KeeShare_Reference); return; } const auto serialized = KeeShareSettings::Reference::serialize(reference); const auto encoded = serialized.toUtf8().toBase64(); customData->set(KeeShare_Reference, encoded); } bool KeeShare::isEnabled(const Group* group) { const auto reference = KeeShare::referenceOf(group); #if !defined(WITH_XC_KEESHARE_SECURE) if (reference.path.endsWith(signedContainerFileType(), Qt::CaseInsensitive)) { return false; } #endif #if !defined(WITH_XC_KEESHARE_INSECURE) if (reference.path.endsWith(unsignedContainerFileType(), Qt::CaseInsensitive)) { return false; } #endif const auto active = KeeShare::active(); return (reference.isImporting() && active.in) || (reference.isExporting() && active.out); } const Group* KeeShare::resolveSharedGroup(const Group* group) { while (group && group != group->database()->rootGroup()) { if (isShared(group)) { return group; } group = group->parentGroup(); } return nullptr; } QString KeeShare::sharingLabel(const Group* group) { auto* share = resolveSharedGroup(group); if (!share) { return {}; } const auto reference = referenceOf(share); switch (reference.type) { case KeeShareSettings::Inactive: return tr("Disabled share %1").arg(reference.path); case KeeShareSettings::ImportFrom: return tr("Import from share %1").arg(reference.path); case KeeShareSettings::ExportTo: return tr("Export to share %1").arg(reference.path); case KeeShareSettings::SynchronizeWith: return tr("Synchronize with share %1").arg(reference.path); } return {}; } QPixmap KeeShare::indicatorBadge(const Group* group, QPixmap pixmap) { if (!isShared(group)) { return pixmap; } const QPixmap badge = isEnabled(group) ? databaseIcons()->iconPixmap(DatabaseIcons::SharedIconIndex) : databaseIcons()->iconPixmap(DatabaseIcons::UnsharedIconIndex); QImage canvas = pixmap.toImage(); const QRectF target(canvas.width() * 0.4, canvas.height() * 0.4, canvas.width() * 0.6, canvas.height() * 0.6); QPainter painter(&canvas); painter.setCompositionMode(QPainter::CompositionMode_SourceOver); painter.drawPixmap(target, badge, badge.rect()); pixmap.convertFromImage(canvas); return pixmap; } QString KeeShare::referenceTypeLabel(const KeeShareSettings::Reference& reference) { switch (reference.type) { case KeeShareSettings::Inactive: return tr("Disabled share"); case KeeShareSettings::ImportFrom: return tr("Import from"); case KeeShareSettings::ExportTo: return tr("Export to"); case KeeShareSettings::SynchronizeWith: return tr("Synchronize with"); } return ""; } QString KeeShare::indicatorSuffix(const Group* group, const QString& text) { // we not adjust the display name for now - it's just an alternative to the icon Q_UNUSED(group); return text; } void KeeShare::connectDatabase(QSharedPointer newDb, QSharedPointer oldDb) { if (oldDb && m_observersByDatabase.contains(oldDb->uuid())) { QPointer observer = m_observersByDatabase.take(oldDb->uuid()); if (observer) { delete observer; } } if (newDb && !m_observersByDatabase.contains(newDb->uuid())) { QPointer observer(new ShareObserver(newDb, this)); m_observersByDatabase[newDb->uuid()] = observer; connect(observer.data(), SIGNAL(sharingMessage(QString, MessageWidget::MessageType)), SIGNAL(sharingMessage(QString, MessageWidget::MessageType))); } } const QString& KeeShare::signedContainerFileType() { static const QString filetype("kdbx.share"); return filetype; } const QString& KeeShare::unsignedContainerFileType() { static const QString filetype("kdbx"); return filetype; } void KeeShare::handleSettingsChanged(const QString& key) { if (key == KeeShare_Active) { emit activeChanged(); } }