~kubuntu-members/kgpg/4.11

1767 by Rolf Eike Beer
Add CAFF-like operation mode ("Sign and Mail User ID")
1
/*
1985 by Rolf Eike Beer
clean up includes
2
 * Copyright (C) 2009,2010,2012 Rolf Eike Beer <kde@opensource.sf-tec.de>
1767 by Rolf Eike Beer
Add CAFF-like operation mode ("Sign and Mail User ID")
3
 */
4
5
/***************************************************************************
6
 *                                                                         *
7
 *   This program is free software; you can redistribute it and/or modify  *
8
 *   it under the terms of the GNU General Public License as published by  *
9
 *   the Free Software Foundation; either version 2 of the License, or     *
10
 *   (at your option) any later version.                                   *
11
 *                                                                         *
12
 ***************************************************************************/
13
14
#include "caff.h"
15
#include "caff_p.h"
16
1985 by Rolf Eike Beer
clean up includes
17
#include "kgpginterface.h"
18
#include "kgpgsettings.h"
19
#include "core/KGpgKeyNode.h"
20
#include "core/KGpgSignableNode.h"
21
#include "transactions/kgpgdeluid.h"
22
#include "transactions/kgpgencrypt.h"
23
#include "transactions/kgpgexport.h"
24
#include "transactions/kgpgimport.h"
25
#include "transactions/kgpgsignuid.h"
1767 by Rolf Eike Beer
Add CAFF-like operation mode ("Sign and Mail User ID")
26
27
#include <KDebug>
28
#include <KLocale>
29
#include <KProcess>
30
#include <KTempDir>
31
#include <KTemporaryFile>
32
#include <KToolInvocation>
1985 by Rolf Eike Beer
clean up includes
33
#include <QDir>
1767 by Rolf Eike Beer
Add CAFF-like operation mode ("Sign and Mail User ID")
34
35
KGpgCaffPrivate::KGpgCaffPrivate(KGpgCaff *parent, const KGpgSignableNode::List &ids, const QStringList &signers,
36
		const KGpgCaff::OperationFlags flags, const KGpgSignTransactionHelper::carefulCheck checklevel)
37
	: QObject(parent),
38
	q_ptr(parent),
39
	m_tempdir(NULL),
40
	m_signers(signers),
41
	m_flags(flags),
42
	m_checklevel(checklevel),
43
	m_allids(ids)
44
{
45
	const QString gpgCfg(KGpgSettings::gpgConfigPath());
46
1860 by Laurent Montel
Compile fine with "-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII"
47
	const QString secring = KgpgInterface::getGpgSetting(QLatin1String( "secret-keyring" ), gpgCfg);
1767 by Rolf Eike Beer
Add CAFF-like operation mode ("Sign and Mail User ID")
48
49
	if (!secring.isEmpty()) {
50
		m_secringfile = secring;
51
	} else {
1860 by Laurent Montel
Compile fine with "-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII"
52
		QDir dir(gpgCfg + QLatin1String( "/.." ));	// simplest way of removing the file name
1767 by Rolf Eike Beer
Add CAFF-like operation mode ("Sign and Mail User ID")
53
		dir = dir.absolutePath();
1860 by Laurent Montel
Compile fine with "-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII"
54
		m_secringfile = dir.toNativeSeparators(dir.filePath(QLatin1String( "secring.gpg" )));
1767 by Rolf Eike Beer
Add CAFF-like operation mode ("Sign and Mail User ID")
55
	}
56
}
57
58
KGpgCaffPrivate::~KGpgCaffPrivate()
59
{
60
	delete m_tempdir;
61
}
62
63
void
64
KGpgCaffPrivate::reexportKey(const KGpgSignableNode *key)
65
{
66
	Q_ASSERT(m_tempdir == NULL);
67
	m_tempdir = new KTempDir();
68
69
	// export all keys necessary for signing
70
	QStringList exportkeys(m_signers);
71
	exportkeys << key->getKeyNode()->getId();
72
73
	KGpgImport *imp = new KGpgImport(this);
74
1860 by Laurent Montel
Compile fine with "-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII"
75
	QStringList expOptions(QLatin1String( "--export-options" ));
76
	expOptions << QLatin1String( "export-clean,export-attribute" );
1767 by Rolf Eike Beer
Add CAFF-like operation mode ("Sign and Mail User ID")
77
	KGpgExport *exp = new KGpgExport(this, exportkeys, expOptions);
78
	exp->setOutputTransaction(imp);
79
80
	imp->setGnuPGHome(m_tempdir->name());
81
82
	connect(imp, SIGNAL(done(int)), SLOT(slotReimportDone(int)));
83
	imp->start();
84
}
85
86
void
87
KGpgCaffPrivate::slotReimportDone(int result)
88
{
89
	KGpgImport *imp = qobject_cast<KGpgImport *>(sender());
90
91
	if (result != KGpgTransaction::TS_OK) {
92
		abortOperation(result);
93
	} else {
94
		bool ret = (imp->getImportedIds(0x1).count() == 1 + m_signers.count());
95
96
		if (!ret) {
97
			abortOperation(-1);
98
		} else {
99
			KGpgSignUid *signuid = new KGpgSignUid(this, m_signers.first(), m_allids.first(), false, m_checklevel);
100
			signuid->setGnuPGHome(m_tempdir->name());
101
			signuid->setSecringFile(m_secringfile);
102
			connect(signuid, SIGNAL(done(int)), SLOT(slotSigningFinished(int)));
103
104
			signuid->start();
105
		}
106
	}
107
108
	sender()->deleteLater();
109
}
110
111
void
112
KGpgCaffPrivate::abortOperation(int result)
113
{
114
	Q_Q(KGpgCaff);
115
1940 by Rolf Eike Beer
improve debugging when caff transaction fails
116
	kDebug(2100) << "transaction" << sender() << "failed, result" << result;
1767 by Rolf Eike Beer
Add CAFF-like operation mode ("Sign and Mail User ID")
117
	delete m_tempdir;
118
	m_tempdir = NULL;
119
120
	emit q->aborted();
121
}
122
123
void
124
KGpgCaffPrivate::checkNextLoop()
125
{
126
	Q_Q(KGpgCaff);
127
128
	delete m_tempdir;
129
	m_tempdir = NULL;
130
131
	if (m_allids.isEmpty())
132
		emit q->done();
133
	else
134
		reexportKey(m_allids.first());
135
}
136
137
void
138
KGpgCaffPrivate::slotSigningFinished(int result)
139
{
140
	sender()->deleteLater();
141
142
	if (result != KGpgTransaction::TS_OK) {
143
		if ((result == KGpgSignTransactionHelper::TS_ALREADY_SIGNED) && (m_flags & KGpgCaff::IgnoreAlreadySigned)) {
144
			m_allids.removeFirst();
145
			checkNextLoop();
146
		} else {
147
			abortOperation(result);
148
		}
149
		return;
150
	}
151
152
	const KGpgSignableNode *uid = m_allids.first();
153
154
	// if there is no email address we can't send this out anyway, so don't bother.
155
	// could be improved: if this is the only selected uid from this key go and select
156
	// a proper mail address to send this to
157
	if (uid->getEmail().isEmpty()) {
158
		m_allids.removeFirst();
159
		checkNextLoop();
160
	}
161
162
	const KGpgKeyNode *key = uid->getKeyNode();
163
164
	int uidnum;
165
166
	if (uid == key) {
167
		uidnum = -1;
168
	} else {
169
		uidnum = -uid->getId().toInt();
170
	}
171
172
	KGpgDelUid::RemoveMode removeMode;
173
	switch (KGpgSettings::mailUats()) {
174
	case 0:
175
		removeMode = KGpgDelUid::RemoveWithEmail;
176
		break;
177
	case 1:
178
		if (uid == key) {
179
			removeMode = KGpgDelUid::RemoveWithEmail;
180
		} else {
181
			// check if this is the first uid with email address
182
			const KGpgSignableNode *otherUid;
183
			int index = 1;
184
			removeMode = KGpgDelUid::RemoveAllOther;
185
186
			while ( (otherUid = key->getUid(index++)) != NULL) {
187
				if (otherUid == uid) {
188
					removeMode = KGpgDelUid::RemoveWithEmail;
189
					break;
190
				}
191
				if (!otherUid->getEmail().isEmpty())
192
					break;
193
			}
194
		}
195
		break;
196
	case 2:
197
		removeMode = KGpgDelUid::RemoveAllOther;
198
		break;
199
	default:
200
		Q_ASSERT(0);
201
		return;
202
	}
203
204
	KGpgDelUid *deluid = new KGpgDelUid(this, key, uidnum, removeMode);
205
206
	deluid->setGnuPGHome(m_tempdir->name());
207
208
	connect(deluid, SIGNAL(done(int)), SLOT(slotDelUidFinished(int)));
209
210
	deluid->start();
211
}
212
213
void
214
KGpgCaffPrivate::slotDelUidFinished(int result)
215
{
216
	sender()->deleteLater();
217
218
	const KGpgSignableNode *uid = m_allids.first();
219
	const KGpgKeyNode *key = uid->getKeyNode();
220
221
	if ((result != KGpgTransaction::TS_OK)) {
222
		// it's no error if we tried to delete all other ids but there is no other id
223
		if ((uid != key) || (result != KGpgDelUid::TS_NO_SUCH_UID)) {
224
			abortOperation(result);
225
			return;
226
		}
227
	}
228
1860 by Laurent Montel
Compile fine with "-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII"
229
	QStringList expOptions(QLatin1String( "--export-options" ));
230
	expOptions << QLatin1String( "export-attribute" );
1767 by Rolf Eike Beer
Add CAFF-like operation mode ("Sign and Mail User ID")
231
232
	KGpgExport *exp = new KGpgExport(this, QStringList(key->getId()), expOptions);
233
234
	exp->setGnuPGHome(m_tempdir->name());
235
236
	connect(exp, SIGNAL(done(int)), SLOT(slotExportFinished(int)));
237
238
	exp->start();
239
}
240
241
void
242
KGpgCaffPrivate::slotExportFinished(int result)
243
{
244
	sender()->deleteLater();
245
246
	if ((result != KGpgTransaction::TS_OK)) {
247
		abortOperation(result);
248
		return;
249
	}
250
251
	const KGpgSignableNode *uid = m_allids.first();
252
	const KGpgKeyNode *key = uid->getKeyNode();
253
254
	KGpgExport *exp = qobject_cast<KGpgExport *>(sender());
255
	Q_ASSERT(exp != NULL);
256
257
	QString body = KGpgSettings::emailTemplate();
1860 by Laurent Montel
Compile fine with "-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII"
258
	body.replace(QLatin1Char( '%' ) + i18nc("Email template placeholder for key id", "KEYID") + QLatin1Char( '%' ), key->getId());
259
	body.replace(QLatin1Char( '%' ) + i18nc("Email template placeholder for key id", "UIDNAME") + QLatin1Char( '%' ), uid->getNameComment());
260
261
	body += QLatin1Char( '\n' ) + QLatin1String( exp->getOutputData() );
262
1938 by Rolf Eike Beer
convert Caff class to use the encrypt transaction
263
	KGpgEncrypt *enc = new KGpgEncrypt(this, QStringList(key->getId()), body, KGpgEncrypt::AsciiArmored | KGpgEncrypt::AllowUntrustedEncryption);
264
265
	connect(enc, SIGNAL(done(int)), SLOT(slotTextEncrypted(int)));
266
267
	enc->start();
1767 by Rolf Eike Beer
Add CAFF-like operation mode ("Sign and Mail User ID")
268
}
269
270
void
1938 by Rolf Eike Beer
convert Caff class to use the encrypt transaction
271
KGpgCaffPrivate::slotTextEncrypted(int result)
1767 by Rolf Eike Beer
Add CAFF-like operation mode ("Sign and Mail User ID")
272
{
1938 by Rolf Eike Beer
convert Caff class to use the encrypt transaction
273
	sender()->deleteLater();
274
275
	switch (result) {
276
	case KGpgTransaction::TS_OK: {
277
		KGpgEncrypt *enc = qobject_cast<KGpgEncrypt *>(sender());
278
		Q_ASSERT(enc != NULL);
279
280
		const QString text = enc->encryptedText().join(QLatin1String("\n"));
281
282
		const KGpgSignableNode *uid = m_allids.takeFirst();
283
284
		const QString email = uid->getEmail();
285
		const QString keyid = uid->getKeyNode()->getId();
286
287
		KToolInvocation::invokeMailer(email, QString(), QString(), i18n( "your key " ) + keyid, text);
288
		break;
289
		}
290
	// FIXME: unexpected results here should do some sort of warning
291
	// this would break string freeze and isn't that important so we
292
	// just stop here.
293
	default:
294
		kDebug(2100) << "encryption finished with status" << result;
2042.1.5 by Rolf Eike Beer
fix wrong announcement of user abortion when encrypting in caff mode
295
		break;
1938 by Rolf Eike Beer
convert Caff class to use the encrypt transaction
296
	case KGpgTransaction::TS_USER_ABORTED:
297
		m_allids.clear();
298
		break;
299
	}
1767 by Rolf Eike Beer
Add CAFF-like operation mode ("Sign and Mail User ID")
300
301
	checkNextLoop();
302
}
303
304
KGpgCaff::KGpgCaff(QObject *parent, const KGpgSignableNode::List &ids, const QStringList &signids,
305
		const int checklevel, const OperationFlags flags)
306
	: QObject(parent),
307
	d_ptr(new KGpgCaffPrivate(this, ids, signids, flags, static_cast<KGpgSignTransactionHelper::carefulCheck>(checklevel)))
308
{
309
}
310
311
void
312
KGpgCaff::run()
313
{
314
	Q_D(KGpgCaff);
315
316
	d->reexportKey(d->m_allids.first());
317
}
318
319
#include "caff.moc"
320
#include "caff_p.moc"