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" |