Changeset View
Changeset View
Standalone View
Standalone View
src/crypto/autodecryptverifyfilescontroller.cpp
Show First 20 Lines • Show All 78 Lines • ▼ Show 20 Line(s) | 77 | public: | |||
---|---|---|---|---|---|
79 | ~Private() { qCDebug(KLEOPATRA_LOG); } | 79 | ~Private() { qCDebug(KLEOPATRA_LOG); } | ||
80 | 80 | | |||
81 | void slotDialogCanceled(); | 81 | void slotDialogCanceled(); | ||
82 | void schedule(); | 82 | void schedule(); | ||
83 | 83 | | |||
84 | void exec(); | 84 | void exec(); | ||
85 | std::vector<std::shared_ptr<Task> > buildTasks(const QStringList &, QStringList &); | 85 | std::vector<std::shared_ptr<Task> > buildTasks(const QStringList &, QStringList &); | ||
86 | 86 | | |||
87 | struct CryptoFile { | ||||
88 | QString baseName; | ||||
89 | QString fileName; | ||||
90 | GpgME::Protocol protocol = GpgME::UnknownProtocol; | ||||
91 | int classification = 0; | ||||
92 | std::shared_ptr<Output> output; | ||||
93 | }; | ||||
94 | QVector<CryptoFile> classifyAndSortFiles(const QStringList &files); | ||||
95 | | ||||
87 | void reportError(int err, const QString &details) | 96 | void reportError(int err, const QString &details) | ||
88 | { | 97 | { | ||
89 | q->setLastError(err, details); | 98 | q->setLastError(err, details); | ||
90 | q->emitDoneOrError(); | 99 | q->emitDoneOrError(); | ||
91 | } | 100 | } | ||
92 | void cancelAllTasks(); | 101 | void cancelAllTasks(); | ||
93 | 102 | | |||
94 | QStringList m_passedFiles, m_filesAfterPreparation; | 103 | QStringList m_passedFiles, m_filesAfterPreparation; | ||
▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Line(s) | 175 | if (m_dialog->exec() == QDialog::Accepted) { | |||
205 | } | 214 | } | ||
206 | } | 215 | } | ||
207 | q->emitDoneOrError(); | 216 | q->emitDoneOrError(); | ||
208 | delete m_dialog; | 217 | delete m_dialog; | ||
209 | m_dialog = nullptr; | 218 | m_dialog = nullptr; | ||
210 | return; | 219 | return; | ||
211 | } | 220 | } | ||
212 | 221 | | |||
222 | QVector<AutoDecryptVerifyFilesController::Private::CryptoFile> AutoDecryptVerifyFilesController::Private::classifyAndSortFiles(const QStringList &files) | ||||
223 | { | ||||
224 | const auto isSignature = [](int classification) -> bool { | ||||
225 | return mayBeDetachedSignature(classification) | ||||
226 | || mayBeOpaqueSignature(classification) | ||||
227 | || (classification & Class::TypeMask) == Class::ClearsignedMessage; | ||||
228 | }; | ||||
229 | | ||||
230 | QVector<CryptoFile> out; | ||||
231 | for (const auto &file : files) { | ||||
232 | CryptoFile cFile; | ||||
233 | cFile.fileName = file; | ||||
234 | cFile.baseName = file.left(file.length() - 4); | ||||
235 | cFile.classification = classify(file); | ||||
236 | cFile.protocol = findProtocol(cFile.classification); | ||||
237 | | ||||
238 | auto it = std::find_if(out.begin(), out.end(), | ||||
239 | [&cFile](const CryptoFile &other) { | ||||
240 | return other.protocol == cFile.protocol | ||||
241 | && other.baseName == cFile.baseName; | ||||
242 | }); | ||||
243 | if (it != out.end()) { | ||||
244 | // If we found a file with the same basename, make sure that encrypted | ||||
245 | // file is before the signature file, so that we first decrypt and then | ||||
246 | // verify | ||||
247 | if (isSignature(cFile.classification) && isCipherText(it->classification)) { | ||||
248 | out.insert(it + 1, cFile); | ||||
249 | } else if (isCipherText(cFile.classification) && isSignature(it->classification)) { | ||||
250 | out.insert(it, cFile); | ||||
251 | } else { | ||||
252 | // both are signatures or both are encrypted files, in which | ||||
253 | // case order does not matter | ||||
254 | out.insert(it, cFile); | ||||
255 | } | ||||
256 | } else { | ||||
257 | out.push_back(cFile); | ||||
258 | } | ||||
259 | } | ||||
260 | | ||||
261 | return out; | ||||
262 | } | ||||
263 | | ||||
264 | | ||||
213 | std::vector< std::shared_ptr<Task> > AutoDecryptVerifyFilesController::Private::buildTasks(const QStringList &fileNames, QStringList &undetected) | 265 | std::vector< std::shared_ptr<Task> > AutoDecryptVerifyFilesController::Private::buildTasks(const QStringList &fileNames, QStringList &undetected) | ||
214 | { | 266 | { | ||
215 | std::vector<std::shared_ptr<Task> > tasks; | 267 | // sort files so that we make sure we first decrypt and then verify | ||
216 | for (const QString &fName : fileNames) { | 268 | QVector<CryptoFile> cryptoFiles = classifyAndSortFiles(fileNames); | ||
217 | const auto classification = classify(fName); | | |||
218 | const auto proto = findProtocol(classification); | | |||
219 | 269 | | |||
220 | QFileInfo fi(fName); | 270 | std::vector<std::shared_ptr<Task> > tasks; | ||
221 | qCDebug(KLEOPATRA_LOG) << "classified" << fName << "as" << printableClassification(classification); | 271 | for (auto it = cryptoFiles.begin(), end = cryptoFiles.end(); it != end; ++it) { | ||
272 | auto &cFile = (*it); | ||||
273 | QFileInfo fi(cFile.fileName); | ||||
274 | qCDebug(KLEOPATRA_LOG) << "classified" << cFile.fileName << "as" << printableClassification(cFile.classification); | ||||
222 | 275 | | |||
223 | if (!fi.isReadable()) { | 276 | if (!fi.isReadable()) { | ||
224 | reportError(makeGnuPGError(GPG_ERR_ASS_NO_INPUT), | 277 | reportError(makeGnuPGError(GPG_ERR_ASS_NO_INPUT), | ||
225 | xi18n("Cannot open <filename>%1</filename> for reading.", fName)); | 278 | xi18n("Cannot open <filename>%1</filename> for reading.", cFile.fileName)); | ||
226 | } else if (mayBeAnyCertStoreType(classification)) { | 279 | continue; | ||
280 | } | ||||
281 | | ||||
282 | if (mayBeAnyCertStoreType(cFile.classification)) { | ||||
227 | // Trying to verify a certificate. Possible because extensions are often similar | 283 | // Trying to verify a certificate. Possible because extensions are often similar | ||
228 | // for PGP Keys. | 284 | // for PGP Keys. | ||
229 | reportError(makeGnuPGError(GPG_ERR_ASS_NO_INPUT), | 285 | reportError(makeGnuPGError(GPG_ERR_ASS_NO_INPUT), | ||
230 | xi18n("The file <filename>%1</filename> contains certificates and can't be decrypted or verified.", fName)); | 286 | xi18n("The file <filename>%1</filename> contains certificates and can't be decrypted or verified.", cFile.fileName)); | ||
231 | qCDebug(KLEOPATRA_LOG) << "reported error"; | 287 | qCDebug(KLEOPATRA_LOG) << "reported error"; | ||
232 | } else if (isDetachedSignature(classification)) { | 288 | continue; | ||
289 | } | ||||
290 | | ||||
291 | // We can't reliably detect CMS detached signatures, so we will try to do | ||||
292 | // our best to use the current file as a detached signature and fallback to | ||||
293 | // opaque signature otherwise. | ||||
294 | if (cFile.protocol == GpgME::CMS && mayBeDetachedSignature(cFile.classification)) { | ||||
295 | // First, see if previous task was a decryption task for the same file | ||||
296 | // and "pipe" it's output into our input | ||||
297 | std::shared_ptr<Input> input; | ||||
298 | bool prepend = false; | ||||
299 | if (it != cryptoFiles.begin()) { | ||||
300 | const auto prev = it - 1; | ||||
301 | if (prev->protocol == cFile.protocol && prev->baseName == cFile.baseName) { | ||||
302 | input = Input::createFromOutput(prev->output); | ||||
303 | prepend = true; | ||||
304 | } | ||||
305 | } | ||||
306 | | ||||
307 | if (!input) { | ||||
308 | if (QFile::exists(cFile.baseName)) { | ||||
309 | input = Input::createFromFile(cFile.baseName); | ||||
310 | } | ||||
311 | } | ||||
312 | | ||||
313 | if (input) { | ||||
314 | qCDebug(KLEOPATRA_LOG) << "Detached CMS verify: " << cFile.fileName; | ||||
315 | std::shared_ptr<VerifyDetachedTask> t(new VerifyDetachedTask); | ||||
316 | t->setInput(Input::createFromFile(cFile.fileName)); | ||||
317 | t->setSignedData(input); | ||||
318 | t->setProtocol(cFile.protocol); | ||||
319 | if (prepend) { | ||||
320 | // Put the verify task BEFORE the decrypt task in the tasks queue, | ||||
321 | // because the tasks are executed in reverse order! | ||||
322 | tasks.insert(tasks.end() - 1, t); | ||||
323 | } else { | ||||
324 | tasks.push_back(t); | ||||
325 | } | ||||
326 | continue; | ||||
327 | } else { | ||||
328 | // No signed data, maybe not a detached signature | ||||
329 | } | ||||
330 | } | ||||
331 | | ||||
332 | if (isDetachedSignature(cFile.classification)) { | ||||
233 | // Detached signature, try to find data or ask the user. | 333 | // Detached signature, try to find data or ask the user. | ||
234 | QString signedDataFileName = findSignedData(fName); | 334 | QString signedDataFileName = cFile.baseName; | ||
235 | if (signedDataFileName.isEmpty()) { | 335 | if (signedDataFileName.isEmpty()) { | ||
236 | signedDataFileName = QFileDialog::getOpenFileName(nullptr, xi18n("Select the file to verify with \"%1\"", fi.fileName()), | 336 | signedDataFileName = QFileDialog::getOpenFileName(nullptr, xi18n("Select the file to verify with \"%1\"", fi.fileName()), | ||
237 | fi.dir().dirName()); | 337 | fi.dir().dirName()); | ||
238 | } | 338 | } | ||
239 | if (signedDataFileName.isEmpty()) { | 339 | if (signedDataFileName.isEmpty()) { | ||
240 | qCDebug(KLEOPATRA_LOG) << "No signed data selected. Verify abortet."; | 340 | qCDebug(KLEOPATRA_LOG) << "No signed data selected. Verify abortet."; | ||
241 | continue; | 341 | } else { | ||
242 | } | 342 | qCDebug(KLEOPATRA_LOG) << "Detached verify: " << cFile.fileName << " Data: " << signedDataFileName; | ||
243 | qCDebug(KLEOPATRA_LOG) << "Detached verify: " << fName << " Data: " << signedDataFileName; | | |||
244 | std::shared_ptr<VerifyDetachedTask> t(new VerifyDetachedTask); | 343 | std::shared_ptr<VerifyDetachedTask> t(new VerifyDetachedTask); | ||
245 | t->setInput(Input::createFromFile(fName)); | 344 | t->setInput(Input::createFromFile(cFile.fileName)); | ||
246 | t->setSignedData(Input::createFromFile(signedDataFileName)); | 345 | t->setSignedData(Input::createFromFile(signedDataFileName)); | ||
247 | t->setProtocol(proto); | 346 | t->setProtocol(cFile.protocol); | ||
248 | tasks.push_back(t); | 347 | tasks.push_back(t); | ||
249 | } else if (!mayBeAnyMessageType(classification)) { | 348 | } | ||
349 | continue; | ||||
350 | } | ||||
351 | | ||||
352 | if (!mayBeAnyMessageType(cFile.classification)) { | ||||
250 | // Not a Message? Maybe there is a signature for this file? | 353 | // Not a Message? Maybe there is a signature for this file? | ||
251 | const auto signatures = findSignatures(fName); | 354 | const auto signatures = findSignatures(cFile.fileName); | ||
252 | if (!signatures.empty()) { | 355 | if (!signatures.empty()) { | ||
253 | for (const QString &sig : signatures) { | 356 | for (const QString &sig : signatures) { | ||
254 | qCDebug(KLEOPATRA_LOG) << "Guessing: " << sig << " is a signature for: " << fName; | 357 | qCDebug(KLEOPATRA_LOG) << "Guessing: " << sig << " is a signature for: " << cFile.fileName; | ||
255 | std::shared_ptr<VerifyDetachedTask> t(new VerifyDetachedTask); | 358 | std::shared_ptr<VerifyDetachedTask> t(new VerifyDetachedTask); | ||
256 | t->setInput(Input::createFromFile(sig)); | 359 | t->setInput(Input::createFromFile(sig)); | ||
257 | t->setSignedData(Input::createFromFile(fName)); | 360 | t->setSignedData(Input::createFromFile(cFile.fileName)); | ||
258 | t->setProtocol(proto); | 361 | t->setProtocol(cFile.protocol); | ||
259 | tasks.push_back(t); | 362 | tasks.push_back(t); | ||
260 | } | 363 | } | ||
261 | } else { | 364 | } else { | ||
262 | undetected << fName; | 365 | undetected << cFile.fileName; | ||
263 | qCDebug(KLEOPATRA_LOG) << "Failed detection for: " << fName << " adding to undetected."; | 366 | qCDebug(KLEOPATRA_LOG) << "Failed detection for: " << cFile.fileName << " adding to undetected."; | ||
264 | } | 367 | } | ||
265 | } else { | 368 | } else { | ||
266 | // Any Message type so we have input and output. | 369 | // Any Message type so we have input and output. | ||
267 | const auto input = Input::createFromFile(fName); | 370 | const auto input = Input::createFromFile(cFile.fileName); | ||
268 | const auto archiveDefinitions = ArchiveDefinition::getArchiveDefinitions(); | 371 | const auto archiveDefinitions = ArchiveDefinition::getArchiveDefinitions(); | ||
269 | 372 | | |||
270 | const auto ad = q->pick_archive_definition(proto, archiveDefinitions, fName); | 373 | const auto ad = q->pick_archive_definition(cFile.protocol, archiveDefinitions, cFile.fileName); | ||
271 | 374 | | |||
272 | const auto wd = QDir(m_workDir.path()); | 375 | const auto wd = QDir(m_workDir.path()); | ||
273 | 376 | | |||
274 | const auto output = | 377 | const auto output = | ||
275 | ad ? ad->createOutputFromUnpackCommand(proto, fName, wd) : | 378 | ad ? ad->createOutputFromUnpackCommand(cFile.protocol, cFile.fileName, wd) : | ||
276 | /*else*/ Output::createFromFile(wd.absoluteFilePath(outputFileName(fi.fileName())), false); | 379 | /*else*/ Output::createFromFile(wd.absoluteFilePath(outputFileName(fi.fileName())), false); | ||
277 | 380 | | |||
278 | if (isOpaqueSignature(classification)) { | 381 | // If this might be opaque CMS signature, then try that. We already handled | ||
382 | // detached CMS signature above | ||||
383 | const auto isCMSOpaqueSignature = cFile.protocol == GpgME::CMS && mayBeOpaqueSignature(cFile.classification); | ||||
384 | | ||||
385 | if (isOpaqueSignature(cFile.classification) || isCMSOpaqueSignature) { | ||||
279 | qCDebug(KLEOPATRA_LOG) << "creating a VerifyOpaqueTask"; | 386 | qCDebug(KLEOPATRA_LOG) << "creating a VerifyOpaqueTask"; | ||
280 | std::shared_ptr<VerifyOpaqueTask> t(new VerifyOpaqueTask); | 387 | std::shared_ptr<VerifyOpaqueTask> t(new VerifyOpaqueTask); | ||
281 | t->setInput(input); | 388 | t->setInput(input); | ||
282 | t->setOutput(output); | 389 | t->setOutput(output); | ||
283 | t->setProtocol(proto); | 390 | t->setProtocol(cFile.protocol); | ||
284 | tasks.push_back(t); | 391 | tasks.push_back(t); | ||
285 | } else { | 392 | } else { | ||
286 | // Any message. That is not an opaque signature needs to be | 393 | // Any message. That is not an opaque signature needs to be | ||
287 | // decrypted. Verify we always do because we can't know if | 394 | // decrypted. Verify we always do because we can't know if | ||
288 | // an encrypted message is also signed. | 395 | // an encrypted message is also signed. | ||
289 | qCDebug(KLEOPATRA_LOG) << "creating a DecryptVerifyTask"; | 396 | qCDebug(KLEOPATRA_LOG) << "creating a DecryptVerifyTask"; | ||
290 | std::shared_ptr<DecryptVerifyTask> t(new DecryptVerifyTask); | 397 | std::shared_ptr<DecryptVerifyTask> t(new DecryptVerifyTask); | ||
291 | t->setInput(input); | 398 | t->setInput(input); | ||
292 | t->setOutput(output); | 399 | t->setOutput(output); | ||
293 | t->setProtocol(proto); | 400 | t->setProtocol(cFile.protocol); | ||
401 | cFile.output = output; | ||||
294 | tasks.push_back(t); | 402 | tasks.push_back(t); | ||
295 | } | 403 | } | ||
296 | } | 404 | } | ||
297 | } | 405 | } | ||
298 | 406 | | |||
299 | return tasks; | 407 | return tasks; | ||
300 | } | 408 | } | ||
301 | 409 | | |||
▲ Show 20 Lines • Show All 82 Lines • Show Last 20 Lines |