QtPass 1.6.0
Multi-platform GUI for pass, the standard unix password manager.
Loading...
Searching...
No Matches
imitatepass.cpp
Go to the documentation of this file.
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
17using Enums::GIT_ADD;
19using Enums::GIT_COPY;
20using Enums::GIT_INIT;
21using Enums::GIT_MOVE;
22using Enums::GIT_PULL;
23using Enums::GIT_PUSH;
24using Enums::GIT_RM;
26using Enums::INVALID;
35
40ImitatePass::ImitatePass() = default;
41
42static auto pgit(const QString &path) -> QString {
43 if (!QtPassSettings::getGitExecutable().startsWith("wsl ")) {
44 return path;
45 }
46 QString res = "$(wslpath " + path + ")";
47 return res.replace('\\', '/');
48}
49
50static auto pgpg(const QString &path) -> QString {
51 if (!QtPassSettings::getGpgExecutable().startsWith("wsl ")) {
52 return path;
53 }
54 QString res = "$(wslpath " + path + ")";
55 return res.replace('\\', '/');
56}
57
64
69
76
82 executeGit(GIT_PUSH, {"push"});
83 }
84}
85
89void ImitatePass::Show(QString file) {
90 file = QtPassSettings::getPassStore() + file + ".gpg";
91 QStringList args = {"-d", "--quiet", "--yes", "--no-encrypt-to",
92 "--batch", "--use-agent", pgpg(file)};
93 executeGpg(PASS_SHOW, args);
94}
95
99void 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}
107
115void ImitatePass::Insert(QString file, QString newValue, bool overwrite) {
116 file = file + ".gpg";
117 QString gpgIdPath = Pass::getGpgIdPath(file);
118 if (!verifyGpgIdFile(gpgIdPath)) {
119 emit critical(tr("Check .gpgid file signature!"),
120 tr("Signature for %1 is invalid.").arg(gpgIdPath));
121 return;
122 }
123 transactionHelper trans(this, PASS_INSERT);
124 QStringList recipients = Pass::getRecipientList(file);
125 if (recipients.isEmpty()) {
126 // Already emit critical signal to notify user of error - no need to throw
127 emit critical(tr("Can not edit"),
128 tr("Could not read encryption key to use, .gpg-id "
129 "file missing or invalid."));
130 return;
131 }
132 QStringList args = {"--batch", "-eq", "--output", pgpg(file)};
133 for (auto &r : recipients) {
134 args.append("-r");
135 args.append(r);
136 }
137 if (overwrite) {
138 args.append("--yes");
139 }
140 args.append("-");
141 executeGpg(PASS_INSERT, args, newValue);
143 // Git is used when enabled - this is the standard pass workflow
144 if (!overwrite) {
145 executeGit(GIT_ADD, {"add", pgit(file)});
146 }
147 QString path = QDir(QtPassSettings::getPassStore()).relativeFilePath(file);
148 path.replace(Util::endsWithGpg(), "");
149 QString msg =
150 QString(overwrite ? "Edit" : "Add") + " for " + path + " using QtPass.";
151 gitCommit(file, msg);
152 }
153}
154
161void ImitatePass::gitCommit(const QString &file, const QString &msg) {
162 if (file.isEmpty()) {
163 executeGit(GIT_COMMIT, {"commit", "-m", msg});
164 } else {
165 executeGit(GIT_COMMIT, {"commit", "-m", msg, "--", pgit(file)});
166 }
167}
168
172void ImitatePass::Remove(QString file, bool isDir) {
173 file = QtPassSettings::getPassStore() + file;
174 transactionHelper trans(this, PASS_REMOVE);
175 if (!isDir) {
176 file += ".gpg";
177 }
179 executeGit(GIT_RM, {"rm", (isDir ? "-rf" : "-f"), pgit(file)});
180 // Normalize path the same way as add/edit operations
181 QString path = QDir(QtPassSettings::getPassStore()).relativeFilePath(file);
182 path.replace(Util::endsWithGpg(), "");
183 gitCommit(file, "Remove for " + path + " using QtPass.");
184 } else {
185 if (isDir) {
186 QDir dir(file);
187 dir.removeRecursively();
188 } else {
189 QFile(file).remove();
190 }
191 }
192}
193
201auto ImitatePass::checkSigningKeys(const QStringList &signingKeys) -> bool {
202 QString out;
203 QStringList args =
204 QStringList{"--status-fd=1", "--list-secret-keys"} + signingKeys;
205 int result =
207 if (result != 0) {
208#ifdef QT_DEBUG
209 dbg() << "GPG list-secret-keys failed with code:" << result;
210#endif
211 return false;
212 }
213 for (auto &key : signingKeys) {
214 if (out.contains("[GNUPG:] KEY_CONSIDERED " + key)) {
215 return true;
216 }
217 }
218 return false;
219}
220
233void ImitatePass::writeGpgIdFile(const QString &gpgIdFile,
234 const QList<UserInfo> &users) {
235 QFile gpgId(gpgIdFile);
236 if (!gpgId.open(QIODevice::WriteOnly | QIODevice::Text)) {
237 emit critical(tr("Cannot update"),
238 tr("Failed to open .gpg-id for writing."));
239 return;
240 }
241 bool secret_selected = false;
242 for (const UserInfo &user : users) {
243 if (user.enabled) {
244 gpgId.write((user.key_id + "\n").toUtf8());
245 secret_selected |= user.have_secret;
246 }
247 }
248 gpgId.close();
249 if (!secret_selected) {
250 emit critical(
251 tr("Check selected users!"),
252 tr("None of the selected keys have a secret key available.\n"
253 "You will not be able to decrypt any newly added passwords!"));
254 }
255}
256
270auto ImitatePass::signGpgIdFile(const QString &gpgIdFile,
271 const QStringList &signingKeys) -> bool {
272 QStringList args;
273 // Use only the first signing key; multiple --default-key options would
274 // override each other and only the last one would take effect.
275 if (!signingKeys.isEmpty()) {
276#ifdef QT_DEBUG
277 if (signingKeys.size() > 1) {
278 dbg() << "Multiple signing keys configured; using only the first key:"
279 << signingKeys.first();
280 }
281#endif
282 args.append(QStringList{"--default-key", signingKeys.first()});
283 }
284 args.append(QStringList{"--yes", "--detach-sign", gpgIdFile});
285 int result =
287 if (result != 0) {
288#ifdef QT_DEBUG
289 dbg() << "GPG signing failed with code:" << result;
290#endif
291 emit critical(tr("GPG signing failed!"),
292 tr("Failed to sign %1.").arg(gpgIdFile));
293 return false;
294 }
295 if (!verifyGpgIdFile(gpgIdFile)) {
296 emit critical(tr("Check .gpgid file signature!"),
297 tr("Signature for %1 is invalid.").arg(gpgIdFile));
298 return false;
299 }
300 return true;
301}
302
316void ImitatePass::gitAddGpgId(const QString &gpgIdFile,
317 const QString &gpgIdSigFile, bool addFile,
318 bool addSigFile) {
319 if (addFile) {
320 executeGit(GIT_ADD, {"add", pgit(gpgIdFile)});
321 }
322 QString commitPath = gpgIdFile;
323 commitPath.replace(Util::endsWithGpg(), "");
324 gitCommit(gpgIdFile, "Added " + commitPath + " using QtPass.");
325 if (!addSigFile) {
326 return;
327 }
328 executeGit(GIT_ADD, {"add", pgit(gpgIdSigFile)});
329 commitPath = gpgIdSigFile;
330 commitPath.replace(QRegularExpression("\\.gpg$"), "");
331 gitCommit(gpgIdSigFile, "Added " + commitPath + " using QtPass.");
332}
333
347void ImitatePass::Init(QString path, const QList<UserInfo> &users) {
348#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
349 QStringList signingKeys =
350 QtPassSettings::getPassSigningKey().split(" ", Qt::SkipEmptyParts);
351#else
352 QStringList signingKeys =
353 QtPassSettings::getPassSigningKey().split(" ", QString::SkipEmptyParts);
354#endif
355 QString gpgIdSigFile = path + ".gpg-id.sig";
356 bool addSigFile = false;
357 if (!signingKeys.isEmpty()) {
358 if (!checkSigningKeys(signingKeys)) {
359 emit critical(tr("No signing key!"),
360 tr("None of the secret signing keys is available.\n"
361 "You will not be able to change the user list!"));
362 return;
363 }
364 QFileInfo checkFile(gpgIdSigFile);
365 if (!checkFile.exists() || !checkFile.isFile()) {
366 addSigFile = true;
367 }
368 }
369
370 QString gpgIdFile = path + ".gpg-id";
371 bool addFile = false;
372 transactionHelper trans(this, PASS_INIT);
373 if (QtPassSettings::isAddGPGId(true)) {
374 QFileInfo checkFile(gpgIdFile);
375 if (!checkFile.exists() || !checkFile.isFile()) {
376 addFile = true;
377 }
378 }
379 writeGpgIdFile(gpgIdFile, users);
380
381 if (!signingKeys.isEmpty()) {
382 if (!signGpgIdFile(gpgIdFile, signingKeys)) {
383 return;
384 }
385 }
386
389 gitAddGpgId(gpgIdFile, gpgIdSigFile, addFile, addSigFile);
390 }
391 reencryptPath(path);
392}
393
399auto ImitatePass::verifyGpgIdFile(const QString &file) -> bool {
400#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
401 QStringList signingKeys =
402 QtPassSettings::getPassSigningKey().split(" ", Qt::SkipEmptyParts);
403#else
404 QStringList signingKeys =
405 QtPassSettings::getPassSigningKey().split(" ", QString::SkipEmptyParts);
406#endif
407 if (signingKeys.isEmpty()) {
408 return true;
409 }
410 QString out;
411 QStringList args =
412 QStringList{"--verify", "--status-fd=1", pgpg(file) + ".sig", pgpg(file)};
413 int result =
415 if (result != 0) {
416#ifdef QT_DEBUG
417 dbg() << "GPG verify failed with code:" << result;
418#endif
419 return false;
420 }
421 QRegularExpression re(
422 R"(^\‍[GNUPG:\‍] VALIDSIG ([A-F0-9]{40}) .* ([A-F0-9]{40})\r?$)",
423 QRegularExpression::MultilineOption);
424 QRegularExpressionMatch m = re.match(out);
425 if (!m.hasMatch()) {
426 return false;
427 }
428 QStringList fingerprints = m.capturedTexts();
429 fingerprints.removeFirst();
430 for (auto &key : signingKeys) {
431 if (fingerprints.contains(key)) {
432 return true;
433 }
434 }
435 return false;
436}
437
443auto ImitatePass::removeDir(const QString &dirName) -> bool {
444 bool result = true;
445 QDir dir(dirName);
446
447 if (dir.exists(dirName)) {
448 for (const QFileInfo &info :
449 dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden |
450 QDir::AllDirs | QDir::Files,
451 QDir::DirsFirst)) {
452 if (info.isDir()) {
453 result = removeDir(info.absoluteFilePath());
454 } else {
455 result = QFile::remove(info.absoluteFilePath());
456 }
457
458 if (!result) {
459 return result;
460 }
461 }
462 result = dir.rmdir(dirName);
463 }
464 return result;
465}
466
474auto ImitatePass::verifyGpgIdForDir(const QString &file,
475 QStringList &gpgIdFilesVerified,
476 QStringList &gpgId) -> bool {
477 QString gpgIdPath = Pass::getGpgIdPath(file);
478 if (gpgIdFilesVerified.contains(gpgIdPath)) {
479 return true;
480 }
481 if (!verifyGpgIdFile(gpgIdPath)) {
482 emit critical(tr("Check .gpgid file signature!"),
483 tr("Signature for %1 is invalid.").arg(gpgIdPath));
484 return false;
485 }
486 gpgIdFilesVerified.append(gpgIdPath);
487 gpgId = getRecipientList(file);
488 gpgId.sort();
489 return true;
490}
491
504auto ImitatePass::getKeysFromFile(const QString &fileName) -> QStringList {
505 QStringList args = {
506 "-v", "--no-secmem-warning", "--no-permission-warning",
507 "--list-only", "--keyid-format=long", pgpg(fileName)};
508 QString keys;
509 QString err;
510 const int result = Executor::executeBlocking(
511 QtPassSettings::getGpgExecutable(), args, &keys, &err);
512 if (result != 0 && keys.isEmpty() && err.isEmpty()) {
513 return QStringList();
514 }
515 QStringList actualKeys;
516 keys += err;
517#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
518 QStringList key = keys.split(Util::newLinesRegex(), Qt::SkipEmptyParts);
519#else
520 QStringList key = keys.split(Util::newLinesRegex(), QString::SkipEmptyParts);
521#endif
522 QListIterator<QString> itr(key);
523 while (itr.hasNext()) {
524 QString current = itr.next();
525 QStringList cur = current.split(" ");
526 if (cur.length() > 4) {
527 QString actualKey = cur.takeAt(4);
528 if (actualKey.length() == 16) {
529 actualKeys << actualKey;
530 }
531 }
532 }
533 actualKeys.sort();
534 return actualKeys;
535}
536
550auto ImitatePass::reencryptSingleFile(const QString &fileName,
551 const QStringList &recipients) -> bool {
552#ifdef QT_DEBUG
553 dbg() << "reencrypt " << fileName << " for " << recipients;
554#endif
555 QString local_lastDecrypt;
556 QStringList args = {
557 "-d", "--quiet", "--yes", "--no-encrypt-to",
558 "--batch", "--use-agent", pgpg(fileName)};
560 args, &local_lastDecrypt);
561
562 if (result != 0 || local_lastDecrypt.isEmpty()) {
563#ifdef QT_DEBUG
564 dbg() << "Decrypt error on re-encrypt for:" << fileName;
565#endif
566 return false;
567 }
568
569 if (local_lastDecrypt.right(1) != "\n") {
570 local_lastDecrypt += "\n";
571 }
572
573 // Use passed recipients instead of re-reading from file
574 if (recipients.isEmpty()) {
575 emit critical(tr("Can not edit"),
576 tr("Could not read encryption key to use, .gpg-id "
577 "file missing or invalid."));
578 return false;
579 }
580
581 // Encrypt to temporary file for atomic replacement
582 QString tempPath = fileName + ".reencrypt.tmp";
583 args = QStringList{"--yes", "--batch", "-eq", "--output", pgpg(tempPath)};
584 for (const auto &i : recipients) {
585 args.append("-r");
586 args.append(i);
587 }
588 args.append("-");
590 local_lastDecrypt);
591
592 if (result != 0) {
593#ifdef QT_DEBUG
594 dbg() << "Encrypt error on re-encrypt for:" << fileName;
595#endif
596 QFile::remove(tempPath);
597 return false;
598 }
599
600 // Verify encryption worked by attempting to decrypt the temp file
601 QString verifyOutput;
602 args = QStringList{"-d", "--quiet", "--batch", "--use-agent", pgpg(tempPath)};
604 &verifyOutput);
605 if (result != 0 || verifyOutput.isEmpty()) {
606#ifdef QT_DEBUG
607 dbg() << "Verification failed for:" << tempPath;
608#endif
609 QFile::remove(tempPath);
610 return false;
611 }
612 // Verify content matches original decrypted content (defense in depth)
613 if (verifyOutput.trimmed() != local_lastDecrypt.trimmed()) {
614#ifdef QT_DEBUG
615 dbg() << "Verification content mismatch for:" << tempPath;
616#endif
617 QFile::remove(tempPath);
618 return false;
619 }
620
621 // Atomic replace with backup: rename original to .bak, rename temp to
622 // original, then remove backup
623 QString backupPath = fileName + ".reencrypt.bak";
624 if (!QFile::rename(fileName, backupPath)) {
625#ifdef QT_DEBUG
626 dbg() << "Failed to backup original file:" << fileName;
627#endif
628 QFile::remove(tempPath);
629 return false;
630 }
631 if (!QFile::rename(tempPath, fileName)) {
632#ifdef QT_DEBUG
633 dbg() << "Failed to rename temp file to:" << fileName;
634#endif
635 // Restore backup and clean up temp file
636 QFile::rename(backupPath, fileName);
637 QFile::remove(tempPath);
638 emit critical(
639 tr("Re-encryption failed"),
640 tr("Failed to replace %1. Original has been restored.").arg(fileName));
641 return false;
642 }
643 // Success - remove backup
644 QFile::remove(backupPath);
645
648 {"add", pgit(fileName)});
649 QString path =
650 QDir(QtPassSettings::getPassStore()).relativeFilePath(fileName);
651 path.replace(Util::endsWithGpg(), "");
653 {"commit", pgit(fileName), "-m",
654 "Re-encrypt for " + path + " using QtPass."});
655 }
656
657 return true;
658}
659
667 return true;
668 }
669 emit statusMsg(tr("Creating backup commit"), 2000);
670 const QString git = QtPassSettings::getGitExecutable();
671 QString statusOut;
672 if (Executor::executeBlocking(git, {"status", "--porcelain"}, &statusOut) !=
673 0) {
674 emit critical(
675 tr("Backup commit failed"),
676 tr("Could not inspect git status. Re-encryption was aborted."));
677 return false;
678 }
679 if (!statusOut.trimmed().isEmpty()) {
680 if (Executor::executeBlocking(git, {"add", "-A"}) != 0 ||
682 git, {"commit", "-m", "Backup before re-encryption"}) != 0) {
683 emit critical(tr("Backup commit failed"),
684 tr("Re-encryption was aborted because a git backup could "
685 "not be created."));
686 return false;
687 }
688 }
689 return true;
690}
691
705void ImitatePass::reencryptPath(const QString &dir) {
706 emit statusMsg(tr("Re-encrypting from folder %1").arg(dir), 3000);
707 emit startReencryptPath();
709 emit statusMsg(tr("Updating password-store"), 2000);
710 GitPull_b();
711 }
712
713 // Create backup before re-encryption - abort if it fails
714 if (!createBackupCommit()) {
715 emit endReencryptPath();
716 return;
717 }
718
719 QDir currentDir;
720 QDirIterator gpgFiles(dir, QStringList() << "*.gpg", QDir::Files,
721 QDirIterator::Subdirectories);
722 QStringList gpgIdFilesVerified;
723 QStringList gpgId;
724 int successCount = 0;
725 int failCount = 0;
726 while (gpgFiles.hasNext()) {
727 QString fileName = gpgFiles.next();
728 if (gpgFiles.fileInfo().path() != currentDir.path()) {
729 if (!verifyGpgIdForDir(fileName, gpgIdFilesVerified, gpgId)) {
730 emit endReencryptPath();
731 return;
732 }
733 if (gpgId.isEmpty() && !gpgIdFilesVerified.isEmpty()) {
734 emit critical(tr("GPG ID verification failed"),
735 tr("Could not verify .gpg-id for directory."));
736 emit endReencryptPath();
737 return;
738 }
739 }
740 QStringList actualKeys = getKeysFromFile(fileName);
741 if (actualKeys != gpgId) {
742 if (reencryptSingleFile(fileName, gpgId)) {
743 successCount++;
744 } else {
745 failCount++;
746 emit critical(tr("Re-encryption failed"),
747 tr("Failed to re-encrypt %1").arg(fileName));
748 }
749 }
750 }
751
752 if (failCount > 0) {
753 emit statusMsg(tr("Re-encryption completed: %1 succeeded, %2 failed")
754 .arg(successCount)
755 .arg(failCount),
756 5000);
757 } else {
758 emit statusMsg(
759 tr("Re-encryption completed: %1 files re-encrypted").arg(successCount),
760 3000);
761 }
762
764 emit statusMsg(tr("Updating password-store"), 2000);
765 GitPush();
766 }
767 emit endReencryptPath();
768}
769
785 const QString &dest, bool force)
786 -> QString {
787 QFileInfo srcFileInfo(src);
788 QFileInfo destFileInfo(dest);
789 QString destFile;
790 QString srcFileBaseName = srcFileInfo.fileName();
791
792 if (srcFileInfo.isFile()) {
793 if (destFileInfo.isFile()) {
794 if (!force) {
795#ifdef QT_DEBUG
796 dbg() << "Destination file already exists";
797#endif
798 return QString();
799 }
800 destFile = dest;
801 } else if (destFileInfo.isDir()) {
802 destFile = QDir(dest).filePath(srcFileBaseName);
803 } else {
804 destFile = dest;
805 }
806
807 if (destFile.endsWith(".gpg", Qt::CaseInsensitive)) {
808 destFile.chop(4);
809 }
810 destFile.append(".gpg");
811 } else if (srcFileInfo.isDir()) {
812 if (destFileInfo.isDir()) {
813 destFile = QDir(dest).filePath(srcFileBaseName);
814 } else if (destFileInfo.isFile()) {
815#ifdef QT_DEBUG
816 dbg() << "Destination is a file";
817#endif
818 return QString();
819 } else {
820 destFile = dest;
821 }
822 } else {
823#ifdef QT_DEBUG
824 dbg() << "Source file does not exist";
825#endif
826 return QString();
827 }
828 return destFile;
829}
830
842void ImitatePass::executeMoveGit(const QString &src, const QString &destFile,
843 bool force) {
844 QStringList args;
845 args << "mv";
846 if (force) {
847 args << "-f";
848 }
849 args << pgit(src);
850 args << pgit(destFile);
851 executeGit(GIT_MOVE, args);
852
853 QString relSrc = QDir(QtPassSettings::getPassStore()).relativeFilePath(src);
854 relSrc.replace(Util::endsWithGpg(), "");
855 QString relDest =
856 QDir(QtPassSettings::getPassStore()).relativeFilePath(destFile);
857 relDest.replace(Util::endsWithGpg(), "");
858 QString message = QString("Moved for %1 to %2 using QtPass.");
859 message = message.arg(relSrc, relDest);
860 gitCommit("", message);
861}
862
874void ImitatePass::Move(const QString src, const QString dest,
875 const bool force) {
876 transactionHelper trans(this, PASS_MOVE);
877 QString destFile = resolveMoveDestination(src, dest, force);
878 if (destFile.isEmpty()) {
879 return;
880 }
881
882#ifdef QT_DEBUG
883 dbg() << "Move Source: " << src;
884 dbg() << "Move Destination: " << destFile;
885#endif
886
888 executeMoveGit(src, destFile, force);
889 } else {
890 QDir qDir;
891 if (force) {
892 qDir.remove(destFile);
893 }
894 qDir.rename(src, destFile);
895 }
896}
897
910void ImitatePass::Copy(const QString src, const QString dest,
911 const bool force) {
912 QFileInfo destFileInfo(dest);
913 transactionHelper trans(this, PASS_COPY);
915 QStringList args;
916 args << "cp";
917 if (force) {
918 args << "-f";
919 }
920 args << pgit(src);
921 args << pgit(dest);
922 executeGit(GIT_COPY, args);
923
924 QString message = QString("Copied from %1 to %2 using QtPass.");
925 message = message.arg(src, dest);
926 gitCommit("", message);
927 } else {
928 QDir qDir;
929 if (force) {
930 qDir.remove(dest);
931 }
932 QFile::copy(src, dest);
933 }
934 // reecrypt all files under the new folder
935 if (destFileInfo.isDir()) {
936 reencryptPath(destFileInfo.absoluteFilePath());
937 } else if (destFileInfo.isFile()) {
938 reencryptPath(destFileInfo.dir().path());
939 }
940}
941
946void ImitatePass::executeGpg(PROCESS id, const QStringList &args, QString input,
947 bool readStdout, bool readStderr) {
948 executeWrapper(id, QtPassSettings::getGpgExecutable(), args, std::move(input),
949 readStdout, readStderr);
950}
951
956void ImitatePass::executeGit(PROCESS id, const QStringList &args, QString input,
957 bool readStdout, bool readStderr) {
958 executeWrapper(id, QtPassSettings::getGitExecutable(), args, std::move(input),
959 readStdout, readStderr);
960}
961
972void ImitatePass::finished(int id, int exitCode, const QString &out,
973 const QString &err) {
974#ifdef QT_DEBUG
975 dbg() << "Imitate Pass";
976#endif
977 static QString transactionOutput;
978 PROCESS pid = transactionIsOver(static_cast<PROCESS>(id));
979 transactionOutput.append(out);
980
981 if (exitCode == 0) {
982 if (pid == INVALID) {
983 return;
984 }
985 } else {
986 while (pid == INVALID) {
987 id = exec.cancelNext();
988 if (id == -1) {
989 // this is probably irrecoverable and shall not happen
990#ifdef QT_DEBUG
991 dbg() << "No such transaction!";
992#endif
993 return;
994 }
995 pid = transactionIsOver(static_cast<PROCESS>(id));
996 }
997 }
998 Pass::finished(pid, exitCode, transactionOutput, err);
999 transactionOutput.clear();
1000}
1001
1011void ImitatePass::executeWrapper(PROCESS id, const QString &app,
1012 const QStringList &args, QString input,
1013 bool readStdout, bool readStderr) {
1014 transactionAdd(id);
1015 Pass::executeWrapper(id, app, args, input, readStdout, readStderr);
1016}
static auto executeBlocking(QString app, const QStringList &args, const QString &input=QString(), QString *process_out=nullptr, QString *process_err=nullptr) -> int
Executor::executeBlocking blocking version of the executor, takes input and presents it as stdin.
Definition executor.cpp:223
RAII helper for wrapping operations in transactions.
auto createBackupCommit() -> bool
Create git backup commit before re-encryption.
void OtpGenerate(QString file) override
Generate OTP.
void GitInit() override
Initialize Git repository.
void executeGpg(PROCESS id, const QStringList &args, QString input=QString(), bool readStdout=true, bool readStderr=true)
Execute GPG command.
void GitPull_b() override
Pull with rebase.
auto checkSigningKeys(const QStringList &signingKeys) -> bool
Check if signing keys are valid.
ImitatePass()
Construct ImitatePass instance.
auto reencryptSingleFile(const QString &fileName, const QStringList &recipients) -> bool
Re-encrypt single file with new recipients.
auto verifyGpgIdFile(const QString &file) -> bool
Verify .gpg-id file exists and is valid.
void executeGit(PROCESS id, const QStringList &args, QString input=QString(), bool readStdout=true, bool readStderr=true)
Execute git command.
void Init(QString path, const QList< UserInfo > &users) override
Initialize store.
auto verifyGpgIdForDir(const QString &file, QStringList &gpgIdFilesVerified, QStringList &gpgId) -> bool
Verify .gpg-id file for a directory.
void Insert(QString file, QString newValue, bool overwrite=false) override
Insert new password.
auto removeDir(const QString &dirName) -> bool
Remove directory recursively.
void executeMoveGit(const QString &src, const QString &destFile, bool force)
Execute git move operation.
void Move(const QString src, const QString dest, const bool force=false) override
Move password file.
void Copy(const QString src, const QString dest, const bool force=false) override
Copy password file.
void endReencryptPath()
Emitted after finishing re-encryption.
void finished(int id, int exitCode, const QString &out, const QString &err) override
Handle process completion.
void executeWrapper(PROCESS id, const QString &app, const QStringList &args, QString input, bool readStdout=true, bool readStderr=true) override
Execute command wrapper.
auto signGpgIdFile(const QString &gpgIdFile, const QStringList &signingKeys) -> bool
Sign .gpg-id file with signing keys.
void Remove(QString file, bool isDir=false) override
Remove password.
void GitPull() override
Pull from remote.
void Show(QString file) override
Show decrypted password.
auto resolveMoveDestination(const QString &src, const QString &dest, bool force) -> QString
Resolve destination for move operation.
void GitPush() override
Push to remote.
void writeGpgIdFile(const QString &gpgIdFile, const QList< UserInfo > &users)
Write recipients to .gpg-id file.
void gitAddGpgId(const QString &gpgIdFile, const QString &gpgIdSigFile, bool addFile, bool addSigFile)
Add .gpg-id to git staging.
void startReencryptPath()
Emitted before starting re-encryption.
auto getKeysFromFile(const QString &fileName) -> QStringList
Read recipients from file.
void reencryptPath(const QString &dir)
Re-encrypt entire directory.
void gitCommit(const QString &file, const QString &msg)
Commit changes to git.
void critical(const QString &, const QString &)
Emit critical error.
Enums::PROCESS PROCESS
Definition pass.h:44
void statusMsg(const QString &, int)
Emit status message.
void executeWrapper(PROCESS id, const QString &app, const QStringList &args, bool readStdout=true, bool readStderr=true)
Execute external wrapper command.
Definition pass.cpp:57
static auto getRecipientList(const QString &for_file) -> QStringList
Get list of recipients for a password file.
Definition pass.cpp:550
static auto getGpgIdPath(const QString &for_file) -> QString
Get .gpg-id file path for a password file.
Definition pass.cpp:520
Executor exec
Definition pass.h:42
virtual void finished(int id, int exitCode, const QString &out, const QString &err)
Handle process completion.
Definition pass.cpp:429
static auto isAutoPull(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether automatic pull is enabled.
static auto isUseGit(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether Git integration is enabled.
static auto getPassStore(const QString &defaultValue=QVariant().toString()) -> QString
Get password store directory path.
static auto isAddGPGId(const bool &defaultValue=QVariant().toBool()) -> bool
Get whether to auto-add GPG ID when receiving files.
static auto getGpgExecutable(const QString &defaultValue=QVariant().toString()) -> QString
Get GPG executable path.
static auto getPassSigningKey(const QString &defaultValue=QVariant().toString()) -> QString
Get GPG signing key for pass.
static auto isUseWebDav(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether WebDAV integration is enabled.
static auto isAutoPush(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether automatic push is enabled.
static auto getGitExecutable(const QString &defaultValue=QVariant().toString()) -> QString
Get git executable path.
static auto endsWithGpg() -> const QRegularExpression &
Returns a regex to match .gpg file extensions.
Definition util.cpp:249
static auto newLinesRegex() -> const QRegularExpression &
Returns a regex to match newline characters.
Definition util.cpp:272
auto transactionIsOver(Enums::PROCESS) -> Enums::PROCESS
transactionIsOver checks wheather currently finished process is last in current transaction
void transactionAdd(Enums::PROCESS)
transactionAdd If called after call to transactionStart() and before transactionEnd(),...
Debug utilities for QtPass.
#define dbg()
Simple debug macro that includes file and line number.
Definition debughelper.h:21
@ PASS_INIT
Definition enums.h:36
@ PASS_INSERT
Definition enums.h:34
@ GIT_INIT
Definition enums.h:27
@ PASS_COPY
Definition enums.h:39
@ PASS_MOVE
Definition enums.h:38
@ GIT_MOVE
Definition enums.h:40
@ PASS_REMOVE
Definition enums.h:35
@ GIT_COPY
Definition enums.h:41
@ INVALID
Definition enums.h:43
@ GIT_COMMIT
Definition enums.h:29
@ GIT_RM
Definition enums.h:30
@ PASS_SHOW
Definition enums.h:33
@ GIT_ADD
Definition enums.h:28
@ GIT_PULL
Definition enums.h:31
@ GIT_PUSH
Definition enums.h:32
@ CLIPBOARD_ALWAYS
Definition enums.h:18
@ CLIPBOARD_NEVER
Definition enums.h:17
@ CLIPBOARD_ON_DEMAND
Definition enums.h:19
@ PASS_INIT
Definition enums.h:36
@ PASS_OTP_GENERATE
Definition enums.h:44
@ PASS_INSERT
Definition enums.h:34
@ GIT_INIT
Definition enums.h:27
@ PASS_COPY
Definition enums.h:39
@ PASS_MOVE
Definition enums.h:38
@ GIT_MOVE
Definition enums.h:40
@ GPG_GENKEYS
Definition enums.h:37
@ PASS_REMOVE
Definition enums.h:35
@ GIT_COPY
Definition enums.h:41
@ INVALID
Definition enums.h:43
@ GIT_COMMIT
Definition enums.h:29
@ GIT_RM
Definition enums.h:30
@ PASS_SHOW
Definition enums.h:33
@ GIT_ADD
Definition enums.h:28
@ PROCESS_COUNT
Definition enums.h:42
@ GIT_PULL
Definition enums.h:31
@ GIT_PUSH
Definition enums.h:32
Stores key info lines including validity, creation date and more.
Definition userinfo.h:13