4
0
mirror of https://github.com/QuasarApp/qca.git synced 2025-05-12 02:39:34 +00:00
qca/plugins/qca-gnupg/mykeystorelist.cpp

488 lines
9.8 KiB
C++

/*
* Copyright (C) 2003-2008 Justin Karneges <justin@affinix.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "mykeystorelist.h"
#include "utils.h"
#include "mypgpkeycontext.h"
#include <QMutexLocker>
#include <QFileInfo>
using namespace QCA;
namespace gpgQCAPlugin
{
Q_GLOBAL_STATIC(QMutex, ksl_mutex)
static MyKeyStoreList *keyStoreList = nullptr;
MyKeyStoreList::MyKeyStoreList(Provider *p)
: KeyStoreListContext(p)
, initialized(false)
, gpg(find_bin(), this)
, pubdirty(false)
, secdirty(false)
, ringWatch(this)
{
QMutexLocker locker(ksl_mutex());
keyStoreList = this;
connect(&gpg, &GpgOp::finished, this, &MyKeyStoreList::gpg_finished);
connect(&ringWatch, &RingWatch::changed, this, &MyKeyStoreList::ring_changed);
}
MyKeyStoreList::~MyKeyStoreList()
{
QMutexLocker locker(ksl_mutex());
keyStoreList = nullptr;
}
Provider::Context *MyKeyStoreList::clone() const
{
return nullptr;
}
QString MyKeyStoreList::name(int) const
{
return "GnuPG Keyring";
}
KeyStore::Type MyKeyStoreList::type(int) const
{
return KeyStore::PGPKeyring;
}
QString MyKeyStoreList::storeId(int) const
{
return "qca-gnupg";
}
QList<int> MyKeyStoreList::keyStores()
{
// we just support one fixed keyring, if any
QList<int> list;
if(initialized)
list += 0;
return list;
}
void MyKeyStoreList::start()
{
// kick start our init procedure:
// ensure gpg is installed
// obtain keyring file names for monitoring
// cache initial keyrings
init_step = 0;
gpg.doCheck();
}
bool MyKeyStoreList::isReadOnly(int) const
{
return false;
}
QList<KeyStoreEntry::Type> MyKeyStoreList::entryTypes(int) const
{
QList<KeyStoreEntry::Type> list;
list += KeyStoreEntry::TypePGPSecretKey;
list += KeyStoreEntry::TypePGPPublicKey;
return list;
}
QList<KeyStoreEntryContext*> MyKeyStoreList::entryList(int)
{
QMutexLocker locker(&ringMutex);
QList<KeyStoreEntryContext*> out;
foreach(const GpgOp::Key &pkey, pubkeys)
{
PGPKey pub, sec;
QString id = pkey.keyItems.first().id;
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
// not secret, in keyring
kc->set(pkey, false, true, pkey.isTrusted);
pub.change(kc);
// optional
sec = getSecKey(id, pkey.userIds);
MyKeyStoreEntry *c = new MyKeyStoreEntry(pub, sec, provider());
c->_storeId = storeId(0);
c->_storeName = name(0);
out.append(c);
}
return out;
}
KeyStoreEntryContext *MyKeyStoreList::entry(int, const QString &entryId)
{
QMutexLocker locker(&ringMutex);
PGPKey pub = getPubKey(entryId);
if(pub.isNull())
return nullptr;
// optional
PGPKey sec = getSecKey(entryId, static_cast<MyPGPKeyContext *>(pub.context())->_props.userIds);
MyKeyStoreEntry *c = new MyKeyStoreEntry(pub, sec, provider());
c->_storeId = storeId(0);
c->_storeName = name(0);
return c;
}
KeyStoreEntryContext *MyKeyStoreList::entryPassive(const QString &serialized)
{
QMutexLocker locker(&ringMutex);
QStringList parts = serialized.split(':');
if(parts.count() < 2)
return nullptr;
if(unescape_string(parts[0]) != "qca-gnupg-1")
return nullptr;
QString entryId = unescape_string(parts[1]);
if(entryId.isEmpty())
return nullptr;
PGPKey pub = getPubKey(entryId);
if(pub.isNull())
return nullptr;
// optional
PGPKey sec = getSecKey(entryId, static_cast<MyPGPKeyContext *>(pub.context())->_props.userIds);
MyKeyStoreEntry *c = new MyKeyStoreEntry(pub, sec, provider());
c->_storeId = storeId(0);
c->_storeName = name(0);
return c;
}
// TODO: cache should reflect this change immediately
QString MyKeyStoreList::writeEntry(int, const PGPKey &key)
{
const MyPGPKeyContext *kc = static_cast<const MyPGPKeyContext *>(key.context());
QByteArray buf = kc->toBinary();
GpgOp gpg(find_bin());
gpg.doImport(buf);
gpg_waitForFinished(&gpg);
gpg_keyStoreLog(gpg.readDiagnosticText());
if(!gpg.success())
return QString();
return kc->_props.keyId;
}
// TODO: cache should reflect this change immediately
bool MyKeyStoreList::removeEntry(int, const QString &entryId)
{
ringMutex.lock();
PGPKey pub = getPubKey(entryId);
ringMutex.unlock();
const MyPGPKeyContext *kc = static_cast<const MyPGPKeyContext *>(pub.context());
QString fingerprint = kc->_props.fingerprint;
GpgOp gpg(find_bin());
gpg.doDeleteKey(fingerprint);
gpg_waitForFinished(&gpg);
gpg_keyStoreLog(gpg.readDiagnosticText());
return gpg.success();
}
MyKeyStoreList *MyKeyStoreList::instance()
{
QMutexLocker locker(ksl_mutex());
return keyStoreList;
}
void MyKeyStoreList::ext_keyStoreLog(const QString &str)
{
if(str.isEmpty())
return;
// FIXME: collect and emit in one pass
QMetaObject::invokeMethod(this, "diagnosticText", Qt::QueuedConnection, Q_ARG(QString, str));
}
PGPKey MyKeyStoreList::getPubKey(const QString &keyId) const
{
int at = -1;
for(int n = 0; n < pubkeys.count(); ++n)
{
if(pubkeys[n].keyItems.first().id == keyId)
{
at = n;
break;
}
}
if(at == -1)
return PGPKey();
const GpgOp::Key &pkey = pubkeys[at];
PGPKey pub;
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
// not secret, in keyring
kc->set(pkey, false, true, pkey.isTrusted);
pub.change(kc);
return pub;
}
PGPKey MyKeyStoreList::getSecKey(const QString &keyId, const QStringList &userIdsOverride) const
{
Q_UNUSED(userIdsOverride);
int at = -1;
for(int n = 0; n < seckeys.count(); ++n)
{
if(seckeys[n].keyItems.first().id == keyId)
{
at = n;
break;
}
}
if(at == -1)
return PGPKey();
const GpgOp::Key &skey = seckeys[at];
PGPKey sec;
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
// secret, in keyring, trusted
kc->set(skey, true, true, true);
//kc->_props.userIds = userIdsOverride;
sec.change(kc);
return sec;
}
PGPKey MyKeyStoreList::publicKeyFromId(const QString &keyId)
{
QMutexLocker locker(&ringMutex);
int at = -1;
for(int n = 0; n < pubkeys.count(); ++n)
{
const GpgOp::Key &pkey = pubkeys[n];
for(int k = 0; k < pkey.keyItems.count(); ++k)
{
const GpgOp::KeyItem &ki = pkey.keyItems[k];
if(ki.id == keyId)
{
at = n;
break;
}
}
if(at != -1)
break;
}
if(at == -1)
return PGPKey();
const GpgOp::Key &pkey = pubkeys[at];
PGPKey pub;
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
// not secret, in keyring
kc->set(pkey, false, true, pkey.isTrusted);
pub.change(kc);
return pub;
}
PGPKey MyKeyStoreList::secretKeyFromId(const QString &keyId)
{
QMutexLocker locker(&ringMutex);
int at = -1;
for(int n = 0; n < seckeys.count(); ++n)
{
const GpgOp::Key &skey = seckeys[n];
for(int k = 0; k < skey.keyItems.count(); ++k)
{
const GpgOp::KeyItem &ki = skey.keyItems[k];
if(ki.id == keyId)
{
at = n;
break;
}
}
if(at != -1)
break;
}
if(at == -1)
return PGPKey();
const GpgOp::Key &skey = seckeys[at];
PGPKey sec;
MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
// secret, in keyring, trusted
kc->set(skey, true, true, true);
sec.change(kc);
return sec;
}
void MyKeyStoreList::gpg_finished()
{
gpg_keyStoreLog(gpg.readDiagnosticText());
if(!initialized)
{
// any steps that fail during init, just give up completely
if(!gpg.success())
{
ringWatch.clear();
emit busyEnd();
return;
}
// check
if(init_step == 0)
{
// obtain keyring file names for monitoring
init_step = 1;
homeDir = gpg.homeDir();
gpg.doSecretKeyringFile();
}
// secret keyring filename
else if(init_step == 1)
{
secring = QFileInfo(gpg.keyringFile()).canonicalFilePath();
if(secring.isEmpty())
{
secring = homeDir + "/secring.gpg";
}
ringWatch.add(secring);
// obtain keyring file names for monitoring
init_step = 2;
gpg.doPublicKeyringFile();
}
// public keyring filename
else if(init_step == 2)
{
pubring = QFileInfo(gpg.keyringFile()).canonicalFilePath();
if(pubring.isEmpty())
{
pubring = homeDir + "/pubring.gpg";
}
ringWatch.add(pubring);
// cache initial keyrings
init_step = 3;
gpg.doSecretKeys();
}
else if(init_step == 3)
{
ringMutex.lock();
seckeys = gpg.keys();
ringMutex.unlock();
// cache initial keyrings
init_step = 4;
gpg.doPublicKeys();
}
else if(init_step == 4)
{
ringMutex.lock();
pubkeys = gpg.keys();
ringMutex.unlock();
initialized = true;
handleDirtyRings();
emit busyEnd();
}
}
else
{
if(!gpg.success())
return;
GpgOp::Type op = gpg.op();
if(op == GpgOp::SecretKeys)
{
ringMutex.lock();
seckeys = gpg.keys();
ringMutex.unlock();
secdirty = false;
}
else if(op == GpgOp::PublicKeys)
{
ringMutex.lock();
pubkeys = gpg.keys();
ringMutex.unlock();
pubdirty = false;
}
if(!secdirty && !pubdirty)
{
emit storeUpdated(0);
return;
}
handleDirtyRings();
}
}
void MyKeyStoreList::ring_changed(const QString &filePath)
{
ext_keyStoreLog(QString("ring_changed: [%1]\n").arg(filePath));
if(filePath == secring)
sec_changed();
else if(filePath == pubring)
pub_changed();
}
void MyKeyStoreList::pub_changed()
{
pubdirty = true;
handleDirtyRings();
}
void MyKeyStoreList::sec_changed()
{
secdirty = true;
handleDirtyRings();
}
void MyKeyStoreList::handleDirtyRings()
{
if(!initialized || gpg.isActive())
return;
if(secdirty)
gpg.doSecretKeys();
else if(pubdirty)
gpg.doPublicKeys();
}
} // end namespace gpgQCAPlugin