QtPass 1.6.0
Multi-platform GUI for pass, the standard unix password manager.
Loading...
Searching...
No Matches
pass.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 "pass.h"
4#include "gpgkeystate.h"
5#include "helpers.h"
6#include "qtpasssettings.h"
7#include "util.h"
8#include <QCoreApplication>
9#include <QDebug>
10#include <QDir>
11#include <QFileInfo>
12#include <QProcess>
13#include <QRandomGenerator>
14#include <QRegularExpression>
15#include <algorithm>
16#include <utility>
17
18#ifdef QT_DEBUG
19#include "debughelper.h"
20#endif
21
22using Enums::GIT_INIT;
23using Enums::GIT_PULL;
24using Enums::GIT_PUSH;
34
35namespace {
36auto fallbackCharset(const QString &input, const QString &fallback) -> QString {
37 return input.isEmpty() ? fallback : input;
38}
39
40auto effectiveCharset(const PasswordConfiguration &passConfig) -> QString {
41 int sel = passConfig.selected;
42 if (sel < 0 || sel >= PasswordConfiguration::CHARSETS_COUNT)
44 return fallbackCharset(
45 passConfig.Characters[sel],
46 passConfig.Characters[PasswordConfiguration::ALLCHARS]);
47}
48} // namespace
49
53Pass::Pass() : wrapperRunning(false), env(QProcess::systemEnvironment()) {
54 connect(&exec,
55 static_cast<void (Executor::*)(int, int, const QString &,
56 const QString &)>(&Executor::finished),
57 this, &Pass::finished);
58
59 // This was previously using direct QProcess signals.
60 // The code now uses Executor instead of raw QProcess for better control.
61 // connect(&process, SIGNAL(error(QProcess::ProcessError)), this,
62 // SIGNAL(error(QProcess::ProcessError)));
63
65 // Merge our vars into WSLENV rather than blindly appending a duplicate entry
66 const QStringList wslenvVars = {
67 QStringLiteral("PASSWORD_STORE_DIR/p"),
68 QStringLiteral("PASSWORD_STORE_GENERATED_LENGTH/w"),
69 QStringLiteral("PASSWORD_STORE_CHARACTER_SET/w")};
70 const QString wslenvPrefix = QStringLiteral("WSLENV=");
71 auto it =
72 std::find_if(env.begin(), env.end(), [&wslenvPrefix](const QString &s) {
73 return s.startsWith(wslenvPrefix);
74 });
75 if (it == env.end()) {
76 env.append(wslenvPrefix + wslenvVars.join(':'));
77 } else {
78 QStringList parts =
79 it->mid(wslenvPrefix.size()).split(':', Qt::SkipEmptyParts);
80 for (const QString &v : wslenvVars) {
81 if (!parts.contains(v))
82 parts.append(v);
83 }
84 *it = wslenvPrefix + parts.join(':');
85 }
86}
87
96void Pass::executeWrapper(PROCESS id, const QString &app,
97 const QStringList &args, bool readStdout,
98 bool readStderr) {
99 executeWrapper(id, app, args, QString(), readStdout, readStderr);
100}
101
102void Pass::executeWrapper(PROCESS id, const QString &app,
103 const QStringList &args, QString input,
104 bool readStdout, bool readStderr) {
105#ifdef QT_DEBUG
106 dbg() << app << args;
107#endif
108 exec.execute(id, QtPassSettings::getPassStore(), app, args, std::move(input),
109 readStdout, readStderr);
110}
111
116#ifdef __APPLE__
117 // If it exists, add the gpgtools to PATH
118 if (QFile("/usr/local/MacGPG2/bin").exists())
119 env.replaceInStrings("PATH=", "PATH=/usr/local/MacGPG2/bin:");
120 // Add missing /usr/local/bin
121 if (env.filter("/usr/local/bin").isEmpty())
122 env.replaceInStrings("PATH=", "PATH=/usr/local/bin:");
123#endif
124
125 if (!QtPassSettings::getGpgHome().isEmpty()) {
126 QDir absHome(QtPassSettings::getGpgHome());
127 absHome.makeAbsolute();
128 env << "GNUPGHOME=" + absHome.path();
129 }
130}
131
139auto Pass::generatePassword(unsigned int length, const QString &charset)
140 -> QString {
141 if (length == 0) {
142 emit critical(tr("Invalid password length"),
143 tr("Can't generate password with zero length."));
144 return {};
145 }
146 QString passwd;
148 // --secure goes first as it overrides --no-* otherwise
149 QStringList args;
150 args.append("-1");
152 args.append("--secure");
153 }
154 args.append(QtPassSettings::isAvoidCapitals() ? "--no-capitalize"
155 : "--capitalize");
156 args.append(QtPassSettings::isAvoidNumbers() ? "--no-numerals"
157 : "--numerals");
159 args.append("--symbols");
160 }
161 args.append(QString::number(length));
162 // executeBlocking returns 0 on success, non-zero on failure
164 &passwd) == 0) {
165 static const QRegularExpression literalNewLines{"[\\n\\r]"};
166 passwd.remove(literalNewLines);
167 } else {
168 passwd.clear();
169#ifdef QT_DEBUG
170 qDebug() << __FILE__ << ":" << __LINE__ << "\t"
171 << "pwgen fail";
172#endif
173 // Error is already handled by clearing passwd; no need for critical
174 // signal here
175 }
176 } else {
177 // Validate charset - if CUSTOM is selected but chars are empty,
178 // fall back to ALLCHARS to prevent weak passwords (issue #780)
179 const QString cs = fallbackCharset(
182 if (cs.length() > 0) {
183 passwd = generateRandomPassword(cs, length);
184 } else {
185 emit critical(
186 tr("No characters chosen"),
187 tr("Can't generate password, there are no characters to choose from "
188 "set in the configuration!"));
189 }
190 }
191 return passwd;
192}
193
200 QString out, err;
202 {"--version"}, &out, &err) != 0) {
203 return false;
204 }
205 QRegularExpression versionRegex(R"(gpg \‍(GnuPG\) (\d+)\.(\d+))");
206 QRegularExpressionMatch match = versionRegex.match(out);
207 if (!match.hasMatch()) {
208 return false;
209 }
210 int major = match.captured(1).toInt();
211 int minor = match.captured(2).toInt();
212 return major > 2 || (major == 2 && minor >= 1);
213}
214
221 if (gpgSupportsEd25519()) {
222 return QStringLiteral("%echo Generating a default key\n"
223 "Key-Type: EdDSA\n"
224 "Key-Curve: Ed25519\n"
225 "Subkey-Type: ECDH\n"
226 "Subkey-Curve: Curve25519\n"
227 "Name-Real: \n"
228 "Name-Comment: QtPass\n"
229 "Name-Email: \n"
230 "Expire-Date: 0\n"
231 "%no-protection\n"
232 "%commit\n"
233 "%echo done");
234 }
235 return QStringLiteral("%echo Generating a default key\n"
236 "Key-Type: RSA\n"
237 "Subkey-Type: RSA\n"
238 "Name-Real: \n"
239 "Name-Comment: QtPass\n"
240 "Name-Email: \n"
241 "Expire-Date: 0\n"
242 "%no-protection\n"
243 "%commit\n"
244 "%echo done");
245}
246
247namespace {
248auto resolveWslGpgconfPath(const QString &lastPart) -> QString {
249 int lastSep = lastPart.lastIndexOf('/');
250 if (lastSep < 0) {
251 lastSep = lastPart.lastIndexOf('\\');
252 }
253 if (lastSep >= 0) {
254 return lastPart.left(lastSep + 1) + "gpgconf";
255 }
256 return QStringLiteral("gpgconf");
257}
258
272QString findGpgconfInGpgDir(const QString &gpgPath) {
273 QFileInfo gpgInfo(gpgPath);
274 if (!gpgInfo.isAbsolute()) {
275 return QString();
276 }
277
278 QDir dir(gpgInfo.absolutePath());
279
280#ifdef Q_OS_WIN
281 QFileInfo candidateExe(dir.filePath("gpgconf.exe"));
282 if (candidateExe.isExecutable()) {
283 return candidateExe.filePath();
284 }
285#endif
286
287 QFileInfo candidate(dir.filePath("gpgconf"));
288 if (candidate.isExecutable()) {
289 return candidate.filePath();
290 }
291 return QString();
292}
293
294// Compatibility shim for Qt < 5.15 where QProcess::splitCommand is not
295// available. Keep this fallback while supporting pre-5.15 builds; remove once
296// the project's minimum supported Qt version is raised to 5.15 or newer.
297#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
309QStringList splitCommandCompat(const QString &command) {
310 QStringList result;
311 QString current;
312 bool inSingleQuote = false;
313 bool inDoubleQuote = false;
314 bool escaping = false;
315 for (QChar ch : command) {
316 if (escaping) {
317 current.append(ch);
318 escaping = false;
319 continue;
320 }
321 if (ch == '\\') {
322 escaping = true;
323 continue;
324 }
325 if (ch == '\'' && !inDoubleQuote) {
326 inSingleQuote = !inSingleQuote;
327 continue;
328 }
329 if (ch == '"' && !inSingleQuote) {
330 inDoubleQuote = !inDoubleQuote;
331 continue;
332 }
333 if (ch.isSpace() && !inSingleQuote && !inDoubleQuote) {
334 if (!current.isEmpty()) {
335 result.append(current);
336 current.clear();
337 }
338 continue;
339 }
340 current.append(ch);
341 }
342 if (escaping) {
343 current.append('\\');
344 }
345 if (!current.isEmpty()) {
346 result.append(current);
347 }
348 return result;
349}
350#endif
351
352} // namespace
353
367auto Pass::resolveGpgconfCommand(const QString &gpgPath)
369 if (gpgPath.trimmed().isEmpty()) {
370 return {"gpgconf", {}};
371 }
372
373#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
374 QStringList parts = QProcess::splitCommand(gpgPath);
375#else
376 QStringList parts = splitCommandCompat(gpgPath);
377#endif
378
379 if (parts.isEmpty()) {
380 return {"gpgconf", {}};
381 }
382
383 const QString first = parts.first();
384 if (first.compare("wsl", Qt::CaseInsensitive) == 0 ||
385 first.compare("wsl.exe", Qt::CaseInsensitive) == 0) {
386 if (parts.size() >= 2 && parts.at(1).startsWith("sh")) {
387 return {"gpgconf", {}};
388 }
389 if (parts.size() >= 2 &&
390 QFileInfo(parts.last()).fileName().startsWith("gpg")) {
391 QString wslGpgconf = resolveWslGpgconfPath(parts.last());
392 parts.removeLast();
393 parts.append(wslGpgconf);
394 return {parts.first(), parts.mid(1)};
395 }
396 return {"gpgconf", {}};
397 }
398
399 if (!first.contains('/') && !first.contains('\\')) {
400 return {"gpgconf", {}};
401 }
402
403 QString gpgconfPath = findGpgconfInGpgDir(first);
404 if (!gpgconfPath.isEmpty()) {
405 return {gpgconfPath, {}};
406 }
407
408 return {"gpgconf", {}};
409}
410
415void Pass::GenerateGPGKeys(QString batch) {
416 // Kill any stale GPG agents that might be holding locks on the key database
417 // This helps avoid "database locked" timeouts during key generation
418 QString gpgPath = QtPassSettings::getGpgExecutable();
419 if (!gpgPath.isEmpty()) {
420 ResolvedGpgconfCommand resolvedGpgconf = resolveGpgconfCommand(gpgPath);
421 QStringList killArgs = resolvedGpgconf.arguments;
422 killArgs << "--kill";
423 killArgs << "gpg-agent";
424 // Use same environment as key generation to target correct gpg-agent
425 Executor::executeBlocking(env, resolvedGpgconf.program, killArgs);
426 }
427
428 executeWrapper(GPG_GENKEYS, gpgPath, {"--gen-key", "--no-tty", "--batch"},
429 std::move(batch));
430}
431
438auto Pass::listKeys(QStringList keystrings, bool secret) -> QList<UserInfo> {
439 QStringList args = {"--no-tty", "--with-colons", "--with-fingerprint"};
440 args.append(secret ? "--list-secret-keys" : "--list-keys");
441
442 for (const QString &keystring : AS_CONST(keystrings)) {
443 if (!keystring.isEmpty()) {
444 args.append(keystring);
445 }
446 }
447 QString p_out;
449 &p_out) != 0) {
450 return QList<UserInfo>();
451 }
452 return parseGpgColonOutput(p_out, secret);
453}
454
461auto Pass::listKeys(const QString &keystring, bool secret) -> QList<UserInfo> {
462 return listKeys(QStringList(keystring), secret);
463}
464
476namespace {
477
478auto containsAny(const QString &str, const QStringList &patterns) -> bool {
479 for (const QString &p : patterns) {
480 if (str.contains(p)) {
481 return true;
482 }
483 }
484 return false;
485}
486
494auto containsAnyCaseInsensitive(const QString &str, const QStringList &patterns)
495 -> bool {
496 const QString lower = str.toLower();
497 for (const QString &p : patterns) {
498 if (lower.contains(p)) {
499 return true;
500 }
501 }
502 return false;
503}
504
505} // namespace
506
507auto gpgErrorMessage(const QString &err) -> QString {
508 // Machine-readable status tokens added by --status-fd 2
509 if (containsAny(err, {QStringLiteral("[GNUPG:] KEYEXPIRED"),
510 QStringLiteral("[GNUPG:] INV_RECP 5 ")}))
511 return QCoreApplication::translate(
512 "Pass", "Encryption failed: GPG key has expired. Please renew or "
513 "replace it.");
514 if (containsAny(err, {QStringLiteral("[GNUPG:] KEYREVOKED"),
515 QStringLiteral("[GNUPG:] INV_RECP 4 ")}))
516 return QCoreApplication::translate(
517 "Pass", "Encryption failed: GPG key has been revoked.");
518 if (containsAny(err, {QStringLiteral("[GNUPG:] NO_PUBKEY"),
519 QStringLiteral("[GNUPG:] INV_RECP")}))
520 return QCoreApplication::translate(
521 "Pass", "Encryption failed: recipient GPG key not found or invalid. "
522 "Check that the key ID in .gpg-id is correct and imported.");
523 if (err.contains(QStringLiteral("[GNUPG:] FAILURE")))
524 return QCoreApplication::translate(
525 "Pass", "Encryption failed. Check that your GPG key is valid.");
526
527 // Locale-dependent fallbacks
528 if (containsAnyCaseInsensitive(err, {QLatin1String("key has expired"),
529 QLatin1String("key expired")}))
530 return QCoreApplication::translate(
531 "Pass", "Encryption failed: GPG key has expired. Please renew or "
532 "replace it.");
533 if (containsAnyCaseInsensitive(err, {QLatin1String("key has been revoked"),
534 QLatin1String("revoked")}))
535 return QCoreApplication::translate(
536 "Pass", "Encryption failed: GPG key has been revoked.");
537 if (containsAnyCaseInsensitive(err, {QLatin1String("no public key"),
538 QLatin1String("unusable public key"),
539 QLatin1String("no secret key")}))
540 return QCoreApplication::translate(
541 "Pass", "Encryption failed: recipient GPG key not found or invalid. "
542 "Check that the key ID in .gpg-id is correct and imported.");
543 if (containsAnyCaseInsensitive(err, {QLatin1String("encryption failed")}))
544 return QCoreApplication::translate(
545 "Pass", "Encryption failed. Check that your GPG key is valid.");
546
547 return {};
548}
549
550namespace {
551auto isGrepHeaderLine(const QString &rawLine, const QString &trimmedLine)
552 -> bool {
553 // ANSI-colored header starts with the blue escape; plain-text fallback:
554 // a non-indented line ending with ':' (pass grep format without color)
555 return rawLine.startsWith(QStringLiteral("\x1B[94m")) ||
556 (!rawLine.startsWith(' ') && !rawLine.startsWith('\t') &&
557 trimmedLine.endsWith(':'));
558}
559} // namespace
560
568auto parseGrepOutput(const QString &rawOut)
569 -> QList<QPair<QString, QStringList>> {
570 static const QRegularExpression ansi(
571 QStringLiteral(R"(\x1B\‍[[0-9;]*[a-zA-Z])"));
572 QList<QPair<QString, QStringList>> results;
573 QString currentEntry;
574 QStringList currentMatches;
575 for (const QString &rawLine : rawOut.split('\n')) {
576 QString line = rawLine;
577 line.remove('\r');
578 line.remove(ansi);
579 line = line.trimmed();
580 const bool isHeader = isGrepHeaderLine(rawLine, line);
581 if (isHeader) {
582 if (!currentEntry.isEmpty() && !currentMatches.isEmpty())
583 results.append({currentEntry, currentMatches});
584 currentEntry = line.endsWith(':') ? line.chopped(1) : line;
585 currentMatches.clear();
586 } else if (!currentEntry.isEmpty()) {
587 if (!line.isEmpty())
588 currentMatches << line;
589 }
590 }
591 if (!currentEntry.isEmpty() && !currentMatches.isEmpty())
592 results.append({currentEntry, currentMatches});
593 return results;
594}
595
606void Pass::finished(int id, int exitCode, const QString &out,
607 const QString &err) {
608 auto pid = static_cast<PROCESS>(id);
609
610 if (exitCode != 0) {
611 handleProcessError(pid, exitCode, out, err);
612 return;
613 }
614
615 emitProcessFinishedSignal(pid, out, err);
616}
617
618void Pass::handleProcessError(PROCESS pid, int exitCode, const QString &out,
619 const QString &err) {
620 if (pid == PASS_GREP) {
621 handleGrepError(exitCode, err);
622 return;
623 }
624
625 if (pid == PASS_INSERT) {
626 const QString friendly = gpgErrorMessage(err);
627 if (!friendly.isEmpty()) {
628 emit processErrorExit(exitCode, formatInsertError(friendly, err));
629 return;
630 }
631 }
632
633 emit processErrorExit(exitCode, err);
634}
635
636void Pass::handleGrepError(int exitCode, const QString &err) {
637 if (exitCode == 1) {
638 emit finishedGrep({});
639 } else {
640 emit processErrorExit(exitCode, err);
641 emit finishedGrep({});
642 }
643}
644
645auto Pass::formatInsertError(const QString &friendly, const QString &err)
646 -> QString {
647 QStringList humanLines;
648 for (const QString &line : err.split('\n')) {
649 QString cleanedLine = line;
650 cleanedLine.remove('\r');
651 if (!cleanedLine.startsWith(QLatin1String("[GNUPG:]")))
652 humanLines.append(cleanedLine);
653 }
654 const QString humanErr = humanLines.join('\n').trimmed();
655 return humanErr.isEmpty() ? friendly : friendly + "\n\n" + humanErr;
656}
657
658void Pass::emitProcessFinishedSignal(PROCESS pid, const QString &out,
659 const QString &err) {
660 switch (pid) {
661 case GIT_INIT:
662 emit finishedGitInit(out, err);
663 break;
664 case GIT_PULL:
665 emit finishedGitPull(out, err);
666 break;
667 case GIT_PUSH:
668 emit finishedGitPush(out, err);
669 break;
670 case PASS_SHOW:
671 emit finishedShow(out);
672 break;
674 emit finishedOtpGenerate(out);
675 break;
676 case PASS_INSERT:
677 emit finishedInsert(out, err);
678 break;
679 case PASS_REMOVE:
680 emit finishedRemove(out, err);
681 break;
682 case PASS_INIT:
683 emit finishedInit(out, err);
684 break;
685 case PASS_MOVE:
686 emit finishedMove(out, err);
687 break;
688 case PASS_COPY:
689 emit finishedCopy(out, err);
690 break;
691 case GPG_GENKEYS:
692 emit finishedGenerateGPGKeys(out, err);
693 break;
694 case PASS_GREP:
695 emit finishedGrep(parseGrepOutput(out));
696 break;
697 default:
698#ifdef QT_DEBUG
699 dbg() << "Unhandled process type" << pid;
700#endif
701 break;
702 }
703}
704
709// key must include the trailing '=' (e.g. "FOO="); env.filter() does substring
710// matching so the '=' anchors the lookup to avoid collisions with longer names.
711void Pass::setEnvVar(const QString &key, const QString &value) {
712 const bool hasEq = key.endsWith('=');
713 Q_ASSERT_X(hasEq, "Pass::setEnvVar",
714 "called with malformed key (missing '=')");
715 if (!hasEq) {
716 qWarning() << "Pass::setEnvVar called with malformed key (missing '='):"
717 << key;
718 return;
719 }
720 const QStringList existing = env.filter(key);
721 for (const QString &entry : existing)
722 env.removeAll(entry);
723 if (!value.isEmpty())
724 env.append(key + value);
725}
726
728 setEnvVar(QStringLiteral("PASSWORD_STORE_SIGNING_KEY="),
730 setEnvVar(QStringLiteral("PASSWORD_STORE_DIR="),
732
734 setEnvVar(QStringLiteral("PASSWORD_STORE_GENERATED_LENGTH="),
735 QString::number(passConfig.length));
736
737 setEnvVar(QStringLiteral("PASSWORD_STORE_CHARACTER_SET="),
738 effectiveCharset(passConfig));
739
740 exec.setEnvironment(env);
741}
742
748auto Pass::getGpgIdPath(const QString &for_file) -> QString {
749 QString passStore =
750 QDir::fromNativeSeparators(QtPassSettings::getPassStore());
751 QString normalizedFile = QDir::fromNativeSeparators(for_file);
752 QString fullPath = normalizedFile.startsWith(passStore)
753 ? normalizedFile
754 : passStore + "/" + normalizedFile;
755 QDir gpgIdDir(QFileInfo(fullPath).absoluteDir());
756 bool found = false;
757 while (gpgIdDir.exists() && gpgIdDir.absolutePath().startsWith(passStore)) {
758 if (QFile(gpgIdDir.absoluteFilePath(".gpg-id")).exists()) {
759 found = true;
760 break;
761 }
762 if (!gpgIdDir.cdUp()) {
763 break;
764 }
765 }
766 QString gpgIdPath(
767 found ? gpgIdDir.absoluteFilePath(".gpg-id")
768 : QDir(QtPassSettings::getPassStore()).filePath(".gpg-id"));
769
770 return gpgIdPath;
771}
772
778auto Pass::getRecipientList(const QString &for_file) -> QStringList {
779 QFile gpgId(getGpgIdPath(for_file));
780 if (!gpgId.open(QIODevice::ReadOnly | QIODevice::Text)) {
781 return {};
782 }
783 QStringList recipients;
784 while (!gpgId.atEnd()) {
785 QString recipient(gpgId.readLine());
786 recipient = recipient.split("#")[0].trimmed();
787 if (!recipient.isEmpty() && Util::isValidKeyId(recipient)) {
788 recipients += recipient;
789 }
790 }
791 return recipients;
792}
793
801auto Pass::getRecipientString(const QString &for_file, const QString &separator,
802 int *count) -> QStringList {
803 Q_UNUSED(separator)
804 QStringList recipients = Pass::getRecipientList(for_file);
805 if (count) {
806 *count = recipients.size();
807 }
808 return recipients;
809}
810
811/* Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
812 */
813
819auto Pass::boundedRandom(quint32 bound) -> quint32 {
820 if (bound < 2) {
821 return 0;
822 }
823
824 quint32 randval;
825 // Rejection-sampling threshold to avoid modulo bias.
826 // This follows the well-known "arc4random_uniform"-style approach:
827 // reject values in the low range [0, min), where
828 // min = 2^32 % bound
829 // so that the remaining range size is an exact multiple of `bound`.
830 //
831 // In quint32 arithmetic, (1 + ~bound) wraps to (2^32 - bound), therefore
832 // (1 + ~bound) % bound == 2^32 % bound.
833 const quint32 rejectionThreshold = (1 + ~bound) % bound;
834
835 do {
836 randval = QRandomGenerator::system()->generate();
837 } while (randval < rejectionThreshold);
838
839 return randval % bound;
840}
841
848auto Pass::generateRandomPassword(const QString &charset, unsigned int length)
849 -> QString {
850 if (charset.isEmpty() || length == 0U) {
851 return {};
852 }
853 QString out;
854 for (unsigned int i = 0; i < length; ++i) {
855 out.append(charset.at(static_cast<int>(
856 boundedRandom(static_cast<quint32>(charset.length())))));
857 }
858 return out;
859}
id Identifier provided by the caller for this queued request.
Definition executor.h:76
static auto executeBlocking(const 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
void starting()
starting signal that is emited when process starts
void init()
Initialize the Pass instance.
Definition pass.cpp:115
void startingExecuteWrapper()
Emitted before executing a command.
void GenerateGPGKeys(QString batch)
Generate GPG keys using batch script.
Definition pass.cpp:415
void critical(const QString &, const QString &)
Emit critical error.
void finishedCopy(const QString &, const QString &)
Emitted when copy finishes.
void finishedShow(const QString &)
Emitted when show finishes.
void finishedRemove(const QString &, const QString &)
Emitted when remove finishes.
Enums::PROCESS PROCESS
Definition pass.h:44
virtual auto generatePassword(unsigned int length, const QString &charset) -> QString
Generate random password.
Definition pass.cpp:139
void finishedMove(const QString &, const QString &)
Emitted when move finishes.
static bool gpgSupportsEd25519()
Check if GPG supports Ed25519 encryption.
Definition pass.cpp:199
void setEnvVar(const QString &key, const QString &value)
Set or remove an environment variable.
Definition pass.cpp:711
auto boundedRandom(quint32 bound) -> quint32
Generate random number in range.
Definition pass.cpp:819
void finishedGitInit(const QString &, const QString &)
Emitted when Git init finishes.
Pass()
Construct a Pass instance.
Definition pass.cpp:53
void executeWrapper(PROCESS id, const QString &app, const QStringList &args, bool readStdout=true, bool readStderr=true)
Execute external wrapper command.
Definition pass.cpp:96
static auto getRecipientList(const QString &for_file) -> QStringList
Get list of recipients for a password file.
Definition pass.cpp:778
static auto resolveGpgconfCommand(const QString &gpgPath) -> ResolvedGpgconfCommand
Resolve the gpgconf command to kill agents.
Definition pass.cpp:367
auto listKeys(QStringList keystrings, bool secret=false) -> QList< UserInfo >
List GPG keys matching patterns.
Definition pass.cpp:438
void finishedInsert(const QString &, const QString &)
Emitted when insert finishes.
void finishedGrep(const QList< QPair< QString, QStringList > > &results)
Emitted when grep finishes with matching results.
void finishedInit(const QString &, const QString &)
Emitted when init finishes.
static auto getGpgIdPath(const QString &for_file) -> QString
Get .gpg-id file path for a password file.
Definition pass.cpp:748
void finishedOtpGenerate(const QString &)
Emitted when OTP generation finishes.
Executor exec
Definition pass.h:42
void processErrorExit(int exitCode, const QString &err)
Emitted on process error exit.
void finishedGitPull(const QString &, const QString &)
Emitted when Git pull finishes.
void finishedGitPush(const QString &, const QString &)
Emitted when Git push finishes.
void updateEnv()
Update environment for subprocesses.
Definition pass.cpp:727
virtual void finished(int id, int exitCode, const QString &out, const QString &err)
Handle process completion.
Definition pass.cpp:606
static auto getRecipientString(const QString &for_file, const QString &separator=" ", int *count=nullptr) -> QStringList
Get recipients as string.
Definition pass.cpp:801
static QString getDefaultKeyTemplate()
Get default key template for new GPG keys.
Definition pass.cpp:220
void finishedGenerateGPGKeys(const QString &, const QString &)
Emitted when GPG key generation finishes.
auto generateRandomPassword(const QString &charset, unsigned int length) -> QString
Generate random password from charset.
Definition pass.cpp:848
static auto getPwgenExecutable(const QString &defaultValue=QVariant().toString()) -> QString
Get pwgen executable path.
static auto getPasswordConfiguration() -> PasswordConfiguration
Get complete password generation configuration.
static auto getGpgHome(const QString &defaultValue=QVariant().toString()) -> QString
Get GPG home directory.
static auto getPassStore(const QString &defaultValue=QVariant().toString()) -> QString
Get password store directory path.
static auto isAvoidCapitals(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether uppercase characters should be avoided.
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 isUseSymbols(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether symbol characters are enabled.
static auto isLessRandom(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether less random password generation is enabled.
static auto isUsePwgen(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether pwgen support is enabled.
static auto isAvoidNumbers(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether numeric characters should be avoided.
static auto isValidKeyId(const QString &keyId) -> bool
Check if a string looks like a valid GPG key ID. Accepts:
Definition util.cpp:280
Debug utilities for QtPass.
#define dbg()
Simple debug macro that includes file and line number.
Definition debughelper.h:21
auto parseGpgColonOutput(const QString &output, bool secret) -> QList< UserInfo >
Parse GPG –with-colons output into a list of UserInfo.
Utility macros for QtPass.
#define AS_CONST(x)
Cross-platform const_cast for range-based for loops.
Definition helpers.h:20
@ PASS_INIT
Definition enums.h:36
@ PASS_OTP_GENERATE
Definition enums.h:42
@ PASS_INSERT
Definition enums.h:34
@ GIT_INIT
Definition enums.h:27
@ PASS_COPY
Definition enums.h:39
@ PASS_MOVE
Definition enums.h:38
@ PASS_GREP
Definition enums.h:43
@ GPG_GENKEYS
Definition enums.h:37
@ PASS_REMOVE
Definition enums.h:35
@ PASS_SHOW
Definition enums.h:33
@ GIT_PULL
Definition enums.h:31
@ GIT_PUSH
Definition enums.h:32
@ PASS_INIT
Definition enums.h:36
@ PASS_OTP_GENERATE
Definition enums.h:42
@ PASS_INSERT
Definition enums.h:34
@ GIT_INIT
Definition enums.h:27
@ PASS_COPY
Definition enums.h:39
@ PASS_MOVE
Definition enums.h:38
@ PASS_GREP
Definition enums.h:43
@ GPG_GENKEYS
Definition enums.h:37
@ PASS_REMOVE
Definition enums.h:35
@ PASS_SHOW
Definition enums.h:33
@ GIT_PULL
Definition enums.h:31
@ GIT_PUSH
Definition enums.h:32
auto gpgErrorMessage(const QString &err) -> QString
Maps GPG stderr (which may include –status-fd 2 tokens) to a translated user-friendly encryption erro...
Definition pass.cpp:507
auto parseGrepOutput(const QString &rawOut) -> QList< QPair< QString, QStringList > >
Parses 'pass grep' raw output into (entry, matches) pairs.
Definition pass.cpp:568
QList< QPair< QString, QStringList > > parseGrepOutput(const QString &rawOut)
Parses 'pass grep' raw output into (entry, matches) pairs.
Definition pass.cpp:568
QString gpgErrorMessage(const QString &err)
Maps GPG stderr (which may include –status-fd 2 tokens) to a translated user-friendly encryption erro...
Definition pass.cpp:507
PROCESS
Identifies different subprocess operations used in QtPass.
Definition enums.h:26
Holds the Password configuration settings.
int length
Length of the password.
QStringList arguments
Definition pass.h:19