Line data Source code
1 : // SPDX-FileCopyrightText: 2016 Anne Jan Brouwer
2 : // SPDX-License-Identifier: GPL-3.0-or-later
3 : #include "imitatepass.h"
4 : #include "qtpasssettings.h"
5 : #include "util.h"
6 : #include <QDirIterator>
7 : #include <QRegularExpression>
8 : #include <utility>
9 :
10 : #ifdef QT_DEBUG
11 : #include "debughelper.h"
12 : #endif
13 :
14 : using Enums::CLIPBOARD_ALWAYS;
15 : using Enums::CLIPBOARD_NEVER;
16 : using Enums::CLIPBOARD_ON_DEMAND;
17 : using Enums::GIT_ADD;
18 : using Enums::GIT_COMMIT;
19 : using Enums::GIT_COPY;
20 : using Enums::GIT_INIT;
21 : using Enums::GIT_MOVE;
22 : using Enums::GIT_PULL;
23 : using Enums::GIT_PUSH;
24 : using Enums::GIT_RM;
25 : using Enums::GPG_GENKEYS;
26 : using Enums::INVALID;
27 : using Enums::PASS_COPY;
28 : using Enums::PASS_INIT;
29 : using Enums::PASS_INSERT;
30 : using Enums::PASS_MOVE;
31 : using Enums::PASS_OTP_GENERATE;
32 : using Enums::PASS_REMOVE;
33 : using Enums::PASS_SHOW;
34 : using Enums::PROCESS_COUNT;
35 :
36 : /**
37 : * @brief ImitatePass::ImitatePass for situaions when pass is not available
38 : * we imitate the behavior of pass https://www.passwordstore.org/
39 : */
40 46 : ImitatePass::ImitatePass() = default;
41 :
42 0 : static auto pgit(const QString &path) -> QString {
43 0 : if (!QtPassSettings::getGitExecutable().startsWith("wsl ")) {
44 : return path;
45 : }
46 0 : QString res = "$(wslpath " + path + ")";
47 0 : return res.replace('\\', '/');
48 : }
49 :
50 0 : static auto pgpg(const QString &path) -> QString {
51 0 : if (!QtPassSettings::getGpgExecutable().startsWith("wsl ")) {
52 : return path;
53 : }
54 0 : QString res = "$(wslpath " + path + ")";
55 0 : return res.replace('\\', '/');
56 : }
57 :
58 : /**
59 : * @brief ImitatePass::GitInit git init wrapper
60 : */
61 0 : void ImitatePass::GitInit() {
62 0 : executeGit(GIT_INIT, {"init", pgit(QtPassSettings::getPassStore())});
63 0 : }
64 :
65 : /**
66 : * @brief ImitatePass::GitPull git init wrapper
67 : */
68 0 : void ImitatePass::GitPull() { executeGit(GIT_PULL, {"pull"}); }
69 :
70 : /**
71 : * @brief ImitatePass::GitPull_b git pull wrapper
72 : */
73 0 : void ImitatePass::GitPull_b() {
74 0 : Executor::executeBlocking(QtPassSettings::getGitExecutable(), {"pull"});
75 0 : }
76 :
77 : /**
78 : * @brief ImitatePass::GitPush git init wrapper
79 : */
80 0 : void ImitatePass::GitPush() {
81 0 : if (QtPassSettings::isUseGit()) {
82 0 : executeGit(GIT_PUSH, {"push"});
83 : }
84 0 : }
85 :
86 : /**
87 : * @brief ImitatePass::Show shows content of file
88 : */
89 0 : void ImitatePass::Show(QString file) {
90 0 : file = QtPassSettings::getPassStore() + file + ".gpg";
91 : QStringList args = {"-d", "--quiet", "--yes", "--no-encrypt-to",
92 0 : "--batch", "--use-agent", pgpg(file)};
93 0 : executeGpg(PASS_SHOW, args);
94 0 : }
95 :
96 : /**
97 : * @brief ImitatePass::OtpGenerate generates an otp code
98 : */
99 0 : void ImitatePass::OtpGenerate(QString file) {
100 : #ifdef QT_DEBUG
101 : dbg() << "No OTP generation code for fake pass yet, attempting for file: " +
102 : file;
103 : #else
104 : Q_UNUSED(file)
105 : #endif
106 0 : }
107 :
108 : /**
109 : * @brief ImitatePass::Insert create new file with encrypted content
110 : *
111 : * @param file file to be created
112 : * @param newValue value to be stored in file
113 : * @param overwrite whether to overwrite existing file
114 : */
115 0 : void ImitatePass::Insert(QString file, QString newValue, bool overwrite) {
116 0 : file = file + ".gpg";
117 0 : QString gpgIdPath = Pass::getGpgIdPath(file);
118 0 : if (!verifyGpgIdFile(gpgIdPath)) {
119 0 : emit critical(tr("Check .gpgid file signature!"),
120 0 : tr("Signature for %1 is invalid.").arg(gpgIdPath));
121 0 : return;
122 : }
123 0 : transactionHelper trans(this, PASS_INSERT);
124 0 : QStringList recipients = Pass::getRecipientList(file);
125 0 : if (recipients.isEmpty()) {
126 : // TODO(bezet): probably throw here
127 0 : emit critical(tr("Can not edit"),
128 0 : tr("Could not read encryption key to use, .gpg-id "
129 : "file missing or invalid."));
130 : return;
131 : }
132 0 : QStringList args = {"--batch", "-eq", "--output", pgpg(file)};
133 0 : for (auto &r : recipients) {
134 0 : args.append("-r");
135 : args.append(r);
136 : }
137 0 : if (overwrite) {
138 0 : args.append("--yes");
139 : }
140 0 : args.append("-");
141 0 : executeGpg(PASS_INSERT, args, newValue);
142 0 : if (!QtPassSettings::isUseWebDav() && QtPassSettings::isUseGit()) {
143 : // TODO(bezet): why not?
144 0 : if (!overwrite) {
145 0 : executeGit(GIT_ADD, {"add", pgit(file)});
146 : }
147 0 : QString path = QDir(QtPassSettings::getPassStore()).relativeFilePath(file);
148 0 : path.replace(Util::endsWithGpg(), "");
149 : QString msg =
150 0 : QString(overwrite ? "Edit" : "Add") + " for " + path + " using QtPass.";
151 0 : GitCommit(file, msg);
152 : }
153 0 : }
154 :
155 : /**
156 : * @brief ImitatePass::GitCommit commit a file to git with an appropriate commit
157 : * message
158 : * @param file
159 : * @param msg
160 : */
161 0 : void ImitatePass::GitCommit(const QString &file, const QString &msg) {
162 0 : if (file.isEmpty()) {
163 0 : executeGit(GIT_COMMIT, {"commit", "-m", msg});
164 : } else {
165 0 : executeGit(GIT_COMMIT, {"commit", "-m", msg, "--", pgit(file)});
166 : }
167 0 : }
168 :
169 : /**
170 : * @brief ImitatePass::Remove custom implementation of "pass remove"
171 : */
172 0 : void ImitatePass::Remove(QString file, bool isDir) {
173 0 : file = QtPassSettings::getPassStore() + file;
174 0 : transactionHelper trans(this, PASS_REMOVE);
175 0 : if (!isDir) {
176 0 : file += ".gpg";
177 : }
178 0 : if (QtPassSettings::isUseGit()) {
179 0 : executeGit(GIT_RM, {"rm", (isDir ? "-rf" : "-f"), pgit(file)});
180 : // TODO(bezet): commit message used to have pass-like file name inside(ie.
181 : // getFile(file, true)
182 0 : GitCommit(file, "Remove for " + file + " using QtPass.");
183 : } else {
184 0 : if (isDir) {
185 0 : QDir dir(file);
186 0 : dir.removeRecursively();
187 0 : } else {
188 0 : QFile(file).remove();
189 : }
190 : }
191 0 : }
192 :
193 : /**
194 : * @brief ImitatePass::Init initialize pass repository
195 : *
196 : * @param path path in which new password-store will be created
197 : * @param users list of users who shall be able to decrypt passwords in
198 : * path
199 : */
200 0 : void ImitatePass::Init(QString path, const QList<UserInfo> &users) {
201 : #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
202 : QStringList signingKeys =
203 0 : QtPassSettings::getPassSigningKey().split(" ", Qt::SkipEmptyParts);
204 : #else
205 : QStringList signingKeys =
206 : QtPassSettings::getPassSigningKey().split(" ", QString::SkipEmptyParts);
207 : #endif
208 0 : QString gpgIdSigFile = path + ".gpg-id.sig";
209 : bool addSigFile = false;
210 0 : if (!signingKeys.isEmpty()) {
211 0 : QString out;
212 : QStringList args =
213 0 : QStringList{"--status-fd=1", "--list-secret-keys"} + signingKeys;
214 0 : Executor::executeBlocking(QtPassSettings::getGpgExecutable(), args, &out);
215 : bool found = false;
216 0 : for (auto &key : signingKeys) {
217 0 : if (out.contains("[GNUPG:] KEY_CONSIDERED " + key)) {
218 : found = true;
219 : break;
220 : }
221 : }
222 0 : if (!found) {
223 0 : emit critical(tr("No signing key!"),
224 0 : tr("None of the secret signing keys is available.\n"
225 : "You will not be able to change the user list!"));
226 : return;
227 : }
228 0 : QFileInfo checkFile(gpgIdSigFile);
229 0 : if (!checkFile.exists() || !checkFile.isFile()) {
230 : addSigFile = true;
231 : }
232 0 : }
233 :
234 0 : QString gpgIdFile = path + ".gpg-id";
235 0 : QFile gpgId(gpgIdFile);
236 : bool addFile = false;
237 0 : transactionHelper trans(this, PASS_INIT);
238 0 : if (QtPassSettings::isAddGPGId(true)) {
239 0 : QFileInfo checkFile(gpgIdFile);
240 0 : if (!checkFile.exists() || !checkFile.isFile()) {
241 : addFile = true;
242 : }
243 0 : }
244 0 : if (!gpgId.open(QIODevice::WriteOnly | QIODevice::Text)) {
245 0 : emit critical(tr("Cannot update"),
246 0 : tr("Failed to open .gpg-id for writing."));
247 0 : return;
248 : }
249 : bool secret_selected = false;
250 0 : foreach (const UserInfo &user, users) {
251 0 : if (user.enabled) {
252 0 : gpgId.write((user.key_id + "\n").toUtf8());
253 0 : secret_selected |= user.have_secret;
254 : }
255 : }
256 0 : gpgId.close();
257 0 : if (!secret_selected) {
258 0 : emit critical(
259 0 : tr("Check selected users!"),
260 0 : tr("None of the selected keys have a secret key available.\n"
261 : "You will not be able to decrypt any newly added passwords!"));
262 0 : return;
263 : }
264 :
265 0 : if (!signingKeys.isEmpty()) {
266 0 : QStringList args;
267 0 : for (auto &key : signingKeys) {
268 0 : args.append(QStringList{"--default-key", key});
269 : }
270 0 : args.append(QStringList{"--yes", "--detach-sign", gpgIdFile});
271 0 : Executor::executeBlocking(QtPassSettings::getGpgExecutable(), args);
272 0 : if (!verifyGpgIdFile(gpgIdFile)) {
273 0 : emit critical(tr("Check .gpgid file signature!"),
274 0 : tr("Signature for %1 is invalid.").arg(gpgIdFile));
275 : return;
276 : }
277 : }
278 :
279 0 : if (!QtPassSettings::isUseWebDav() && QtPassSettings::isUseGit() &&
280 0 : !QtPassSettings::getGitExecutable().isEmpty()) {
281 0 : if (addFile) {
282 0 : executeGit(GIT_ADD, {"add", pgit(gpgIdFile)});
283 : }
284 : QString commitPath = gpgIdFile;
285 0 : commitPath.replace(Util::endsWithGpg(), "");
286 0 : GitCommit(gpgIdFile, "Added " + commitPath + " using QtPass.");
287 0 : if (!signingKeys.isEmpty()) {
288 0 : if (addSigFile) {
289 0 : executeGit(GIT_ADD, {"add", pgit(gpgIdSigFile)});
290 : }
291 0 : commitPath = gpgIdSigFile;
292 0 : commitPath.replace(QRegularExpression("\\.gpg$"), "");
293 0 : GitCommit(gpgIdSigFile, "Added " + commitPath + " using QtPass.");
294 : }
295 : }
296 0 : reencryptPath(path);
297 0 : }
298 :
299 : /**
300 : * @brief ImitatePass::verifyGpgIdFile verify detached gpgid file signature.
301 : * @param file which gpgid file.
302 : * @return was verification succesful?
303 : */
304 0 : auto ImitatePass::verifyGpgIdFile(const QString &file) -> bool {
305 : #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
306 : QStringList signingKeys =
307 0 : QtPassSettings::getPassSigningKey().split(" ", Qt::SkipEmptyParts);
308 : #else
309 : QStringList signingKeys =
310 : QtPassSettings::getPassSigningKey().split(" ", QString::SkipEmptyParts);
311 : #endif
312 0 : if (signingKeys.isEmpty()) {
313 : return true;
314 : }
315 0 : QString out;
316 : QStringList args =
317 0 : QStringList{"--verify", "--status-fd=1", pgpg(file) + ".sig", pgpg(file)};
318 0 : Executor::executeBlocking(QtPassSettings::getGpgExecutable(), args, &out);
319 : QRegularExpression re(
320 0 : R"(^\[GNUPG:\] VALIDSIG ([A-F0-9]{40}) .* ([A-F0-9]{40})\r?$)",
321 0 : QRegularExpression::MultilineOption);
322 0 : QRegularExpressionMatch m = re.match(out);
323 0 : if (!m.hasMatch()) {
324 : return false;
325 : }
326 0 : QStringList fingerprints = m.capturedTexts();
327 0 : fingerprints.removeFirst();
328 0 : for (auto &key : signingKeys) {
329 0 : if (fingerprints.contains(key)) {
330 0 : return true;
331 : }
332 : }
333 0 : return false;
334 0 : }
335 :
336 : /**
337 : * @brief ImitatePass::removeDir delete folder recursive.
338 : * @param dirName which folder.
339 : * @return was removal succesful?
340 : */
341 0 : auto ImitatePass::removeDir(const QString &dirName) -> bool {
342 : bool result = true;
343 0 : QDir dir(dirName);
344 :
345 0 : if (dir.exists(dirName)) {
346 0 : Q_FOREACH (QFileInfo info,
347 : dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System |
348 : QDir::Hidden | QDir::AllDirs | QDir::Files,
349 : QDir::DirsFirst)) {
350 0 : if (info.isDir()) {
351 0 : result = removeDir(info.absoluteFilePath());
352 : } else {
353 0 : result = QFile::remove(info.absoluteFilePath());
354 : }
355 :
356 0 : if (!result) {
357 : return result;
358 : }
359 0 : }
360 0 : result = dir.rmdir(dirName);
361 : }
362 : return result;
363 0 : }
364 :
365 : /**
366 : * @brief ImitatePass::reencryptPath reencrypt all files under the chosen
367 : * directory
368 : *
369 : * This is stil quite experimental..
370 : * @param dir
371 : */
372 0 : void ImitatePass::reencryptPath(const QString &dir) {
373 0 : emit statusMsg(tr("Re-encrypting from folder %1").arg(dir), 3000);
374 0 : emit startReencryptPath();
375 0 : if (QtPassSettings::isAutoPull()) {
376 : // TODO(bezet): move statuses inside actions?
377 0 : emit statusMsg(tr("Updating password-store"), 2000);
378 0 : GitPull_b();
379 : }
380 0 : QDir currentDir;
381 0 : QDirIterator gpgFiles(dir, QStringList() << "*.gpg", QDir::Files,
382 0 : QDirIterator::Subdirectories);
383 0 : QStringList gpgIdFilesVerified;
384 0 : QStringList gpgId;
385 0 : while (gpgFiles.hasNext()) {
386 0 : QString fileName = gpgFiles.next();
387 0 : if (gpgFiles.fileInfo().path() != currentDir.path()) {
388 0 : QString gpgIdPath = Pass::getGpgIdPath(fileName);
389 0 : if (!gpgIdFilesVerified.contains(gpgIdPath)) {
390 0 : if (!verifyGpgIdFile(gpgIdPath)) {
391 0 : emit critical(tr("Check .gpgid file signature!"),
392 0 : tr("Signature for %1 is invalid.").arg(gpgIdPath));
393 0 : emit endReencryptPath();
394 : return;
395 : }
396 : gpgIdFilesVerified.append(gpgIdPath);
397 : }
398 0 : gpgId = getRecipientList(fileName);
399 : gpgId.sort();
400 : }
401 : // TODO(bezet): enable --with-colons for better future-proofness?
402 : QStringList args = {
403 : "-v", "--no-secmem-warning", "--no-permission-warning",
404 0 : "--list-only", "--keyid-format=long", pgpg(fileName)};
405 0 : QString keys;
406 0 : QString err;
407 0 : Executor::executeBlocking(QtPassSettings::getGpgExecutable(), args, &keys,
408 : &err);
409 0 : QStringList actualKeys;
410 : keys += err;
411 : #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
412 0 : QStringList key = keys.split(Util::newLinesRegex(), Qt::SkipEmptyParts);
413 : #else
414 : QStringList key =
415 : keys.split(Util::newLinesRegex(), QString::SkipEmptyParts);
416 : #endif
417 : QListIterator<QString> itr(key);
418 0 : while (itr.hasNext()) {
419 : QString current = itr.next();
420 0 : QStringList cur = current.split(" ");
421 0 : if (cur.length() > 4) {
422 0 : QString actualKey = cur.takeAt(4);
423 0 : if (actualKey.length() == 16) {
424 : actualKeys << actualKey;
425 : }
426 : }
427 : }
428 : actualKeys.sort();
429 0 : if (actualKeys != gpgId) {
430 : // dbg()<< actualKeys << gpgId << getRecipientList(fileName);
431 : #ifdef QT_DEBUG
432 : dbg() << "reencrypt " << fileName << " for " << gpgId;
433 : #endif
434 0 : QString local_lastDecrypt = "Could not decrypt";
435 0 : args = QStringList{
436 : "-d", "--quiet", "--yes", "--no-encrypt-to",
437 0 : "--batch", "--use-agent", pgpg(fileName)};
438 0 : Executor::executeBlocking(QtPassSettings::getGpgExecutable(), args,
439 : &local_lastDecrypt);
440 :
441 0 : if (!local_lastDecrypt.isEmpty() &&
442 : local_lastDecrypt != "Could not decrypt") {
443 0 : if (local_lastDecrypt.right(1) != "\n") {
444 0 : local_lastDecrypt += "\n";
445 : }
446 :
447 0 : QStringList recipients = Pass::getRecipientList(fileName);
448 0 : if (recipients.isEmpty()) {
449 0 : emit critical(tr("Can not edit"),
450 0 : tr("Could not read encryption key to use, .gpg-id "
451 : "file missing or invalid."));
452 : return;
453 : }
454 : args =
455 0 : QStringList{"--yes", "--batch", "-eq", "--output", pgpg(fileName)};
456 0 : for (auto &i : recipients) {
457 0 : args.append("-r");
458 : args.append(i);
459 : }
460 0 : args.append("-");
461 0 : Executor::executeBlocking(QtPassSettings::getGpgExecutable(), args,
462 : local_lastDecrypt);
463 :
464 0 : if (!QtPassSettings::isUseWebDav() && QtPassSettings::isUseGit()) {
465 0 : Executor::executeBlocking(QtPassSettings::getGitExecutable(),
466 : {"add", pgit(fileName)});
467 : QString path =
468 0 : QDir(QtPassSettings::getPassStore()).relativeFilePath(fileName);
469 0 : path.replace(Util::endsWithGpg(), "");
470 0 : Executor::executeBlocking(QtPassSettings::getGitExecutable(),
471 : {"commit", pgit(fileName), "-m",
472 0 : "Edit for " + path + " using QtPass."});
473 : }
474 :
475 : } else {
476 : #ifdef QT_DEBUG
477 : dbg() << "Decrypt error on re-encrypt";
478 : #endif
479 : }
480 : }
481 : }
482 0 : if (QtPassSettings::isAutoPush()) {
483 0 : emit statusMsg(tr("Updating password-store"), 2000);
484 : // TODO(bezet): this is non-blocking and shall be done outside
485 0 : GitPush();
486 : }
487 0 : emit endReencryptPath();
488 0 : }
489 :
490 0 : void ImitatePass::Move(const QString src, const QString dest,
491 : const bool force) {
492 0 : transactionHelper trans(this, PASS_MOVE);
493 0 : QFileInfo srcFileInfo(src);
494 0 : QFileInfo destFileInfo(dest);
495 0 : QString destFile;
496 0 : QString srcFileBaseName = srcFileInfo.fileName();
497 :
498 0 : if (srcFileInfo.isFile()) {
499 0 : if (destFileInfo.isFile()) {
500 0 : if (!force) {
501 : #ifdef QT_DEBUG
502 : dbg() << "Destination file already exists";
503 : #endif
504 : return;
505 : }
506 0 : } else if (destFileInfo.isDir()) {
507 0 : destFile = QDir(dest).filePath(srcFileBaseName);
508 : } else {
509 0 : destFile = dest;
510 : }
511 :
512 0 : if (destFile.endsWith(".gpg", Qt::CaseInsensitive)) {
513 0 : destFile.chop(4); // make sure suffix is lowercase
514 : }
515 0 : destFile.append(".gpg");
516 0 : } else if (srcFileInfo.isDir()) {
517 0 : if (destFileInfo.isDir()) {
518 0 : destFile = QDir(dest).filePath(srcFileBaseName);
519 0 : } else if (destFileInfo.isFile()) {
520 : #ifdef QT_DEBUG
521 : dbg() << "Destination is a file";
522 : #endif
523 : return;
524 : } else {
525 0 : destFile = dest;
526 : }
527 : } else {
528 : #ifdef QT_DEBUG
529 : dbg() << "Source file does not exist";
530 : #endif
531 : return;
532 : }
533 :
534 : #ifdef QT_DEBUG
535 : dbg() << "Move Source: " << src;
536 : dbg() << "Move Destination: " << destFile;
537 : #endif
538 :
539 0 : if (QtPassSettings::isUseGit()) {
540 0 : QStringList args;
541 0 : args << "mv";
542 0 : if (force) {
543 0 : args << "-f";
544 : }
545 0 : args << pgit(src);
546 0 : args << pgit(destFile);
547 0 : executeGit(GIT_MOVE, args);
548 :
549 0 : QString relSrc = QDir(QtPassSettings::getPassStore()).relativeFilePath(src);
550 0 : relSrc.replace(Util::endsWithGpg(), "");
551 : QString relDest =
552 0 : QDir(QtPassSettings::getPassStore()).relativeFilePath(destFile);
553 0 : relDest.replace(Util::endsWithGpg(), "");
554 0 : QString message = QString("Moved for %1 to %2 using QtPass.");
555 0 : message = message.arg(relSrc, relDest);
556 0 : GitCommit("", message);
557 : } else {
558 0 : QDir qDir;
559 0 : if (force) {
560 0 : qDir.remove(destFile);
561 : }
562 0 : qDir.rename(src, destFile);
563 0 : }
564 0 : }
565 :
566 0 : void ImitatePass::Copy(const QString src, const QString dest,
567 : const bool force) {
568 0 : QFileInfo destFileInfo(dest);
569 0 : transactionHelper trans(this, PASS_COPY);
570 0 : if (QtPassSettings::isUseGit()) {
571 0 : QStringList args;
572 0 : args << "cp";
573 0 : if (force) {
574 0 : args << "-f";
575 : }
576 0 : args << pgit(src);
577 0 : args << pgit(dest);
578 0 : executeGit(GIT_COPY, args);
579 :
580 0 : QString message = QString("copied from %1 to %2 using QTPass.");
581 0 : message = message.arg(src, dest);
582 0 : GitCommit("", message);
583 : } else {
584 0 : QDir qDir;
585 0 : if (force) {
586 0 : qDir.remove(dest);
587 : }
588 0 : QFile::copy(src, dest);
589 0 : }
590 : // reecrypt all files under the new folder
591 0 : if (destFileInfo.isDir()) {
592 0 : reencryptPath(destFileInfo.absoluteFilePath());
593 0 : } else if (destFileInfo.isFile()) {
594 0 : reencryptPath(destFileInfo.dir().path());
595 : }
596 0 : }
597 :
598 : /**
599 : * @brief ImitatePass::executeGpg easy wrapper for running gpg commands
600 : * @param args
601 : */
602 0 : void ImitatePass::executeGpg(PROCESS id, const QStringList &args, QString input,
603 : bool readStdout, bool readStderr) {
604 0 : executeWrapper(id, QtPassSettings::getGpgExecutable(), args, std::move(input),
605 : readStdout, readStderr);
606 0 : }
607 : /**
608 : * @brief ImitatePass::executeGit easy wrapper for running git commands
609 : * @param args
610 : */
611 0 : void ImitatePass::executeGit(PROCESS id, const QStringList &args, QString input,
612 : bool readStdout, bool readStderr) {
613 0 : executeWrapper(id, QtPassSettings::getGitExecutable(), args, std::move(input),
614 : readStdout, readStderr);
615 0 : }
616 :
617 : /**
618 : * @brief ImitatePass::finished this function is overloaded to ensure
619 : * identical behaviour to RealPass ie. only PASS_*
620 : * processes are visible inside Pass::finish, so
621 : * that interface-wise it all looks the same
622 : * @param id
623 : * @param exitCode
624 : * @param out
625 : * @param err
626 : */
627 0 : void ImitatePass::finished(int id, int exitCode, const QString &out,
628 : const QString &err) {
629 : #ifdef QT_DEBUG
630 : dbg() << "Imitate Pass";
631 : #endif
632 0 : static QString transactionOutput;
633 0 : PROCESS pid = transactionIsOver(static_cast<PROCESS>(id));
634 0 : transactionOutput.append(out);
635 :
636 0 : if (exitCode == 0) {
637 0 : if (pid == INVALID) {
638 : return;
639 : }
640 : } else {
641 0 : while (pid == INVALID) {
642 0 : id = exec.cancelNext();
643 0 : if (id == -1) {
644 : // this is probably irrecoverable and shall not happen
645 : #ifdef QT_DEBUG
646 : dbg() << "No such transaction!";
647 : #endif
648 : return;
649 : }
650 0 : pid = transactionIsOver(static_cast<PROCESS>(id));
651 : }
652 : }
653 0 : Pass::finished(pid, exitCode, transactionOutput, err);
654 0 : transactionOutput.clear();
655 : }
656 :
657 : /**
658 : * @brief executeWrapper overrided so that every execution is a transaction
659 : * @param id
660 : * @param app
661 : * @param args
662 : * @param input
663 : * @param readStdout
664 : * @param readStderr
665 : */
666 0 : void ImitatePass::executeWrapper(PROCESS id, const QString &app,
667 : const QStringList &args, QString input,
668 : bool readStdout, bool readStderr) {
669 0 : transactionAdd(id);
670 0 : Pass::executeWrapper(id, app, args, input, readStdout, readStderr);
671 0 : }
|