mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-24 10:52:53 +00:00
373 lines
10 KiB
C++
373 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2009,2010,2012,2013,2014 Rolf Eike Beer <kde@opensource.sf-tec.de>
|
|
*/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* 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 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "caff.h"
|
|
#include "caff_p.h"
|
|
|
|
#include "kgpginterface.h"
|
|
#include "kgpgsettings.h"
|
|
#include "core/KGpgKeyNode.h"
|
|
#include "core/KGpgSignableNode.h"
|
|
#include "transactions/kgpgdeluid.h"
|
|
#include "transactions/kgpgencrypt.h"
|
|
#include "transactions/kgpgexport.h"
|
|
#include "transactions/kgpgimport.h"
|
|
#include "transactions/kgpgsignuid.h"
|
|
|
|
#include <KDebug>
|
|
#include <KLocale>
|
|
#include <KProcess>
|
|
#include <KTempDir>
|
|
#include <KTemporaryFile>
|
|
#include <KToolInvocation>
|
|
#include <KMessageBox>
|
|
#include <QDir>
|
|
#include <QFileInfo>
|
|
|
|
KGpgCaffPrivate::KGpgCaffPrivate(KGpgCaff *parent, const KGpgSignableNode::List &ids, const QStringList &signers,
|
|
const KGpgCaff::OperationFlags flags, const KGpgSignTransactionHelper::carefulCheck checklevel)
|
|
: QObject(parent),
|
|
q_ptr(parent),
|
|
m_signers(signers),
|
|
m_flags(flags),
|
|
m_checklevel(checklevel),
|
|
m_allids(ids)
|
|
{
|
|
const QString gpgCfg = KGpgSettings::gpgConfigPath();
|
|
const QString secring = KgpgInterface::getGpgSetting(QLatin1String( "secret-keyring" ), gpgCfg);
|
|
|
|
if (!secring.isEmpty()) {
|
|
m_secringfile = secring;
|
|
} else {
|
|
QFileInfo fn(gpgCfg);
|
|
fn.setFile(fn.dir(), QLatin1String("secring.gpg"));
|
|
m_secringfile = QDir::toNativeSeparators(fn.absoluteFilePath());
|
|
}
|
|
}
|
|
|
|
KGpgCaffPrivate::~KGpgCaffPrivate()
|
|
{
|
|
}
|
|
|
|
void
|
|
KGpgCaffPrivate::reexportKey(const KGpgSignableNode *key)
|
|
{
|
|
Q_ASSERT(m_tempdir.isNull());
|
|
|
|
// find out if the given id can be used for encryption
|
|
const KGpgKeyNode *k;
|
|
if (key->getType() & KgpgCore::ITYPE_PAIR)
|
|
k = key->toKeyNode();
|
|
else
|
|
k = key->getParentKeyNode()->toKeyNode();
|
|
|
|
// skip if not
|
|
if (!k->canEncrypt()) {
|
|
m_noEncIds << key;
|
|
m_allids.removeFirst();
|
|
checkNextLoop();
|
|
return;
|
|
}
|
|
|
|
m_tempdir.reset(new KTempDir());
|
|
|
|
// export all keys necessary for signing
|
|
QStringList exportkeys(m_signers);
|
|
exportkeys << key->getKeyNode()->getId();
|
|
|
|
KGpgImport *imp = new KGpgImport(this);
|
|
|
|
QStringList expOptions(QLatin1String( "--export-options" ));
|
|
expOptions << QLatin1String( "export-clean,export-attribute" );
|
|
KGpgExport *exp = new KGpgExport(this, exportkeys, expOptions);
|
|
exp->setOutputTransaction(imp);
|
|
|
|
imp->setGnuPGHome(m_tempdir->name());
|
|
|
|
connect(imp, SIGNAL(done(int)), SLOT(slotReimportDone(int)));
|
|
imp->start();
|
|
}
|
|
|
|
void
|
|
KGpgCaffPrivate::slotReimportDone(int result)
|
|
{
|
|
KGpgImport *imp = qobject_cast<KGpgImport *>(sender());
|
|
|
|
if (result != KGpgTransaction::TS_OK) {
|
|
abortOperation(result);
|
|
} else {
|
|
bool ret = (imp->getImportedIds(0x1).count() == 1 + m_signers.count());
|
|
|
|
if (!ret) {
|
|
abortOperation(-1);
|
|
} else {
|
|
KGpgSignUid *signuid = new KGpgSignUid(this, m_signers.first(), m_allids.first(), false, m_checklevel);
|
|
signuid->setGnuPGHome(m_tempdir->name());
|
|
signuid->setSecringFile(m_secringfile);
|
|
connect(signuid, SIGNAL(done(int)), SLOT(slotSigningFinished(int)));
|
|
|
|
signuid->start();
|
|
}
|
|
}
|
|
|
|
sender()->deleteLater();
|
|
}
|
|
|
|
void
|
|
KGpgCaffPrivate::abortOperation(int result)
|
|
{
|
|
Q_Q(KGpgCaff);
|
|
|
|
kDebug(2100) << "transaction" << sender() << "failed, result" << result;
|
|
m_tempdir.reset();
|
|
|
|
emit q->aborted();
|
|
}
|
|
|
|
void
|
|
KGpgCaffPrivate::checkNextLoop()
|
|
{
|
|
Q_Q(KGpgCaff);
|
|
|
|
m_tempdir.reset();
|
|
|
|
if (m_allids.isEmpty()) {
|
|
if (!m_noEncIds.isEmpty()) {
|
|
QStringList ids;
|
|
|
|
foreach (const KGpgSignableNode *nd, m_noEncIds)
|
|
if (nd->getEmail().isEmpty())
|
|
ids << i18nc("%1 is the key id, %2 is the name and comment of the key or uid",
|
|
"%1: %2", nd->getId(), nd->getNameComment());
|
|
else
|
|
ids << i18nc("%1 is the key id, %2 is the name and comment of the key or uid, %3 is the email address of the uid",
|
|
"%1: %2 <%3>", nd->getId(), nd->getNameComment(), nd->getEmail());
|
|
|
|
KMessageBox::detailedSorry(qobject_cast<QWidget *>(q->parent()),
|
|
i18np("No mail was sent for the following user id because it belongs to a key without encryption capability:",
|
|
"No mail was sent for the following user ids because they belong to keys without encryption capability:",
|
|
m_noEncIds.count()),
|
|
ids.join(QLatin1String("\n")));
|
|
}
|
|
|
|
if (!m_alreadyIds.isEmpty()) {
|
|
QStringList ids;
|
|
|
|
foreach (const KGpgSignableNode *nd, m_alreadyIds)
|
|
if (nd->getEmail().isEmpty())
|
|
ids << i18nc("%1 is the key id, %2 is the name and comment of the key or uid",
|
|
"%1: %2", nd->getId(), nd->getNameComment());
|
|
else
|
|
ids << i18nc("%1 is the key id, %2 is the name and comment of the key or uid, %3 is the email address of the uid",
|
|
"%1: %2 <%3>", nd->getId(), nd->getNameComment(), nd->getEmail());
|
|
|
|
KMessageBox::detailedSorry(qobject_cast<QWidget *>(q->parent()),
|
|
i18np("No mail was sent for the following user id because it was already signed:",
|
|
"No mail was sent for the following user ids because they were already signed:",
|
|
m_alreadyIds.count()),
|
|
ids.join(QLatin1String("\n")));
|
|
}
|
|
|
|
emit q->done();
|
|
} else {
|
|
reexportKey(m_allids.first());
|
|
}
|
|
}
|
|
|
|
void
|
|
KGpgCaffPrivate::slotSigningFinished(int result)
|
|
{
|
|
sender()->deleteLater();
|
|
|
|
if (result != KGpgTransaction::TS_OK) {
|
|
if ((result == KGpgSignTransactionHelper::TS_ALREADY_SIGNED) && (m_flags & KGpgCaff::IgnoreAlreadySigned)) {
|
|
m_alreadyIds << m_allids.takeFirst();
|
|
checkNextLoop();
|
|
} else {
|
|
abortOperation(result);
|
|
}
|
|
return;
|
|
}
|
|
|
|
const KGpgSignableNode *uid = m_allids.first();
|
|
|
|
// if there is no email address we can't send this out anyway, so don't bother.
|
|
// could be improved: if this is the only selected uid from this key go and select
|
|
// a proper mail address to send this to
|
|
if (uid->getEmail().isEmpty()) {
|
|
m_allids.removeFirst();
|
|
checkNextLoop();
|
|
}
|
|
|
|
const KGpgKeyNode *key = uid->getKeyNode();
|
|
|
|
int uidnum;
|
|
|
|
if (uid == key) {
|
|
uidnum = -1;
|
|
} else {
|
|
uidnum = -uid->getId().toInt();
|
|
}
|
|
|
|
KGpgDelUid::RemoveMode removeMode;
|
|
switch (KGpgSettings::mailUats()) {
|
|
case 0:
|
|
removeMode = KGpgDelUid::RemoveWithEmail;
|
|
break;
|
|
case 1:
|
|
if (uid == key) {
|
|
removeMode = KGpgDelUid::RemoveWithEmail;
|
|
} else {
|
|
// check if this is the first uid with email address
|
|
const KGpgSignableNode *otherUid;
|
|
int index = 1;
|
|
removeMode = KGpgDelUid::RemoveAllOther;
|
|
|
|
while ( (otherUid = key->getUid(index++)) != NULL) {
|
|
if (otherUid == uid) {
|
|
removeMode = KGpgDelUid::RemoveWithEmail;
|
|
break;
|
|
}
|
|
if (!otherUid->getEmail().isEmpty())
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 2:
|
|
removeMode = KGpgDelUid::RemoveAllOther;
|
|
break;
|
|
default:
|
|
Q_ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
KGpgDelUid *deluid = new KGpgDelUid(this, key, uidnum, removeMode);
|
|
|
|
deluid->setGnuPGHome(m_tempdir->name());
|
|
|
|
connect(deluid, SIGNAL(done(int)), SLOT(slotDelUidFinished(int)));
|
|
|
|
deluid->start();
|
|
}
|
|
|
|
void
|
|
KGpgCaffPrivate::slotDelUidFinished(int result)
|
|
{
|
|
sender()->deleteLater();
|
|
|
|
const KGpgSignableNode *uid = m_allids.first();
|
|
const KGpgKeyNode *key = uid->getKeyNode();
|
|
|
|
if (result != KGpgTransaction::TS_OK) {
|
|
// it's no error if we tried to delete all other ids but there is no other id
|
|
if ((uid != key) || (result != KGpgDelUid::TS_NO_SUCH_UID)) {
|
|
abortOperation(result);
|
|
return;
|
|
}
|
|
}
|
|
|
|
QStringList expOptions(QLatin1String( "--export-options" ));
|
|
expOptions << QLatin1String( "export-attribute" );
|
|
|
|
KGpgExport *exp = new KGpgExport(this, QStringList(key->getId()), expOptions);
|
|
|
|
exp->setGnuPGHome(m_tempdir->name());
|
|
|
|
connect(exp, SIGNAL(done(int)), SLOT(slotExportFinished(int)));
|
|
|
|
exp->start();
|
|
}
|
|
|
|
void
|
|
KGpgCaffPrivate::slotExportFinished(int result)
|
|
{
|
|
sender()->deleteLater();
|
|
|
|
if (result != KGpgTransaction::TS_OK) {
|
|
abortOperation(result);
|
|
return;
|
|
}
|
|
|
|
const KGpgSignableNode *uid = m_allids.first();
|
|
const KGpgKeyNode *key = uid->getKeyNode();
|
|
|
|
KGpgExport *exp = qobject_cast<KGpgExport *>(sender());
|
|
Q_ASSERT(exp != NULL);
|
|
|
|
QString body = KGpgSettings::emailTemplate();
|
|
body.replace(QLatin1Char( '%' ) + i18nc("Email template placeholder for key id", "KEYID") + QLatin1Char( '%' ), key->getId());
|
|
body.replace(QLatin1Char( '%' ) + i18nc("Email template placeholder for key id", "UIDNAME") + QLatin1Char( '%' ), uid->getNameComment());
|
|
|
|
body += QLatin1Char( '\n' ) + QLatin1String( exp->getOutputData() );
|
|
|
|
KGpgEncrypt *enc = new KGpgEncrypt(this, QStringList(key->getId()), body, KGpgEncrypt::AsciiArmored | KGpgEncrypt::AllowUntrustedEncryption);
|
|
|
|
// Set the home directory to make sure custom encrypt options
|
|
// as well as the "always encrypt to" setting are not honored.
|
|
enc->setGnuPGHome(m_tempdir->name());
|
|
|
|
connect(enc, SIGNAL(done(int)), SLOT(slotTextEncrypted(int)));
|
|
|
|
enc->start();
|
|
}
|
|
|
|
void
|
|
KGpgCaffPrivate::slotTextEncrypted(int result)
|
|
{
|
|
sender()->deleteLater();
|
|
|
|
switch (result) {
|
|
case KGpgTransaction::TS_OK: {
|
|
KGpgEncrypt *enc = qobject_cast<KGpgEncrypt *>(sender());
|
|
Q_ASSERT(enc != NULL);
|
|
|
|
const QString text = enc->encryptedText().join(QLatin1String("\n"));
|
|
|
|
const KGpgSignableNode *uid = m_allids.takeFirst();
|
|
|
|
const QString email = uid->getEmail();
|
|
const QString keyid = uid->getKeyNode()->getId();
|
|
|
|
KToolInvocation::invokeMailer(email, QString(), QString(),
|
|
i18nc("%1 is 64 bit key id (in hex), text is used as email subject", "Your key %1", keyid),
|
|
text);
|
|
break;
|
|
}
|
|
default:
|
|
abortOperation(result);
|
|
break;
|
|
case KGpgTransaction::TS_USER_ABORTED:
|
|
m_allids.clear();
|
|
break;
|
|
}
|
|
|
|
checkNextLoop();
|
|
}
|
|
|
|
KGpgCaff::KGpgCaff(QObject *parent, const KGpgSignableNode::List &ids, const QStringList &signids,
|
|
const int checklevel, const OperationFlags flags)
|
|
: QObject(parent),
|
|
d_ptr(new KGpgCaffPrivate(this, ids, signids, flags, static_cast<KGpgSignTransactionHelper::carefulCheck>(checklevel)))
|
|
{
|
|
}
|
|
|
|
void
|
|
KGpgCaff::run()
|
|
{
|
|
Q_D(KGpgCaff);
|
|
|
|
d->reexportKey(d->m_allids.first());
|
|
}
|
|
|
|
#include "caff.moc"
|
|
#include "caff_p.moc"
|