Finds the path to the gpgconf executable in the same directory as the given GPG path.
QString result = findGpgconfInGpgDir(gpgPath); std::cout << result.toStdString() << std::endl; // Expected output: path to gpgconf or empty string.
Finds the path to the gpgconf executable in the same directory as the given GPG path.
QString result = findGpgconfInGpgDir(gpgPath); std::cout << result.toStdString() << std::endl; // Expected output: path to gpgconf or empty string
#include <QDir>
#include <QFileInfo>
#include <QProcess>
#include <QRandomGenerator>
#include <QRegularExpression>
#include <utility>
#ifdef QT_DEBUG
#endif
Pass::Pass() : wrapperRunning(false), env(QProcess::systemEnvironment()) {
connect(&exec,
static_cast<void (
Executor::*)(
int,
int,
const QString &,
const QString &)>(&Executor::finished),
env.append("WSLENV=PASSWORD_STORE_DIR/p");
}
const QStringList &args, bool readStdout,
bool readStderr) {
}
const QStringList &args, QString input,
bool readStdout, bool readStderr) {
#ifdef QT_DEBUG
#endif
readStdout, readStderr);
}
#ifdef __APPLE__
if (QFile("/usr/local/MacGPG2/bin").exists())
env.replaceInStrings("PATH=", "PATH=/usr/local/MacGPG2/bin:");
if (env.filter("/usr/local/bin").isEmpty())
env.replaceInStrings("PATH=", "PATH=/usr/local/bin:");
#endif
absHome.makeAbsolute();
env << "GNUPGHOME=" + absHome.path();
}
}
-> QString {
QString passwd;
QStringList args;
args.append("-1");
args.append("--secure");
}
: "--capitalize");
: "--numerals");
args.append("--symbols");
}
args.append(QString::number(length));
&passwd) == 0) {
static const QRegularExpression literalNewLines{"[\\n\\r]"};
passwd.remove(literalNewLines);
} else {
passwd.clear();
#ifdef QT_DEBUG
qDebug() << __FILE__ << ":" << __LINE__ << "\t"
<< "pwgen fail";
#endif
}
} else {
QString effectiveCharset = charset;
if (effectiveCharset.isEmpty()) {
}
if (effectiveCharset.length() > 0) {
passwd = generateRandomPassword(effectiveCharset, length);
} else {
emit critical(
tr("No characters chosen"),
tr("Can't generate password, there are no characters to choose from "
"set in the configuration!"));
}
}
return passwd;
}
QString out, err;
{"--version"}, &out, &err) != 0) {
return false;
}
QRegularExpression versionRegex(R"(gpg \(GnuPG\) (\d+)\.(\d+))");
QRegularExpressionMatch match = versionRegex.match(out);
if (!match.hasMatch()) {
return false;
}
int major = match.captured(1).toInt();
int minor = match.captured(2).toInt();
return major > 2 || (major == 2 && minor >= 1);
}
return QStringLiteral("%echo Generating a default key\n"
"Key-Type: EdDSA\n"
"Key-Curve: Ed25519\n"
"Subkey-Type: ECDH\n"
"Subkey-Curve: Curve25519\n"
"Name-Real: \n"
"Name-Comment: QtPass\n"
"Name-Email: \n"
"Expire-Date: 0\n"
"%no-protection\n"
"%commit\n"
"%echo done");
}
return QStringLiteral("%echo Generating a default key\n"
"Key-Type: RSA\n"
"Subkey-Type: RSA\n"
"Name-Real: \n"
"Name-Comment: QtPass\n"
"Name-Email: \n"
"Expire-Date: 0\n"
"%no-protection\n"
"%commit\n"
"%echo done");
}
namespace {
auto resolveWslGpgconfPath(const QString &lastPart) -> QString {
int lastSep = lastPart.lastIndexOf('/');
if (lastSep < 0) {
lastSep = lastPart.lastIndexOf('\\');
}
if (lastSep >= 0) {
return lastPart.left(lastSep + 1) + "gpgconf";
}
return QStringLiteral("gpgconf");
}
QString findGpgconfInGpgDir(const QString &gpgPath) {
QFileInfo gpgInfo(gpgPath);
if (!gpgInfo.isAbsolute()) {
return QString();
}
QDir dir(gpgInfo.absolutePath());
#ifdef Q_OS_WIN
QFileInfo candidateExe(dir.filePath("gpgconf.exe"));
if (candidateExe.isExecutable()) {
return candidateExe.filePath();
}
#endif
QFileInfo candidate(dir.filePath("gpgconf"));
if (candidate.isExecutable()) {
return candidate.filePath();
}
return QString();
}
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
QStringList splitCommandCompat(const QString &command) {
QStringList result;
QString current;
bool inSingleQuote = false;
bool inDoubleQuote = false;
bool escaping = false;
for (QChar ch : command) {
if (escaping) {
current.append(ch);
escaping = false;
continue;
}
if (ch == '\\') {
escaping = true;
continue;
}
if (ch == '\'' && !inDoubleQuote) {
inSingleQuote = !inSingleQuote;
continue;
}
if (ch == '"' && !inSingleQuote) {
inDoubleQuote = !inDoubleQuote;
continue;
}
if (ch.isSpace() && !inSingleQuote && !inDoubleQuote) {
if (!current.isEmpty()) {
result.append(current);
current.clear();
}
continue;
}
current.append(ch);
}
if (escaping) {
current.append('\\');
}
if (!current.isEmpty()) {
result.append(current);
}
return result;
}
#endif
}
if (gpgPath.trimmed().isEmpty()) {
return {"gpgconf", {}};
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
QStringList parts = QProcess::splitCommand(gpgPath);
#else
QStringList parts = splitCommandCompat(gpgPath);
#endif
if (parts.isEmpty()) {
return {"gpgconf", {}};
}
const QString first = parts.first();
if (first == "wsl" || first == "wsl.exe") {
if (parts.size() >= 2 && parts.at(1).startsWith("sh")) {
return {"gpgconf", {}};
}
if (parts.size() >= 2 &&
QFileInfo(parts.last()).fileName().startsWith("gpg")) {
QString wslGpgconf = resolveWslGpgconfPath(parts.last());
parts.removeLast();
parts.append(wslGpgconf);
return {parts.first(), parts.mid(1)};
}
return {"gpgconf", {}};
}
if (!first.contains('/') && !first.contains('\\')) {
return {"gpgconf", {}};
}
QString gpgconfPath = findGpgconfInGpgDir(gpgPath);
if (!gpgconfPath.isEmpty()) {
return {gpgconfPath, {}};
}
return {"gpgconf", {}};
}
if (!gpgPath.isEmpty()) {
killArgs << "--kill";
killArgs << "gpg-agent";
}
std::move(batch));
}
auto Pass::listKeys(QStringList keystrings,
bool secret) -> QList<UserInfo> {
QStringList args = {"--no-tty", "--with-colons", "--with-fingerprint"};
args.append(secret ? "--list-secret-keys" : "--list-keys");
for (
const QString &keystring :
AS_CONST(keystrings)) {
if (!keystring.isEmpty()) {
args.append(keystring);
}
}
QString p_out;
&p_out) != 0) {
return QList<UserInfo>();
}
}
auto Pass::listKeys(
const QString &keystring,
bool secret) -> QList<UserInfo> {
return listKeys(QStringList(keystring), secret);
}
const QString &err) {
auto pid =
static_cast<PROCESS>(id);
if (exitCode != 0) {
return;
}
switch (pid) {
break;
break;
break;
break;
break;
break;
break;
break;
break;
break;
break;
default:
#ifdef QT_DEBUG
dbg() <<
"Unhandled process type" << pid;
#endif
break;
}
}
QStringList envSigningKey = env.filter("PASSWORD_STORE_SIGNING_KEY=");
if (envSigningKey.isEmpty()) {
if (!currentSigningKey.isEmpty()) {
env.append("PASSWORD_STORE_SIGNING_KEY=" + currentSigningKey);
}
} else {
if (currentSigningKey.isEmpty()) {
env.removeAll(envSigningKey.first());
} else {
env.replaceInStrings(envSigningKey.first(),
"PASSWORD_STORE_SIGNING_KEY=" + currentSigningKey);
}
}
QStringList store = env.filter("PASSWORD_STORE_DIR=");
if (store.isEmpty()) {
} else {
env.replaceInStrings(store.first(), "PASSWORD_STORE_DIR=" +
}
exec.setEnvironment(env);
}
QString passStore =
QString normalizedFile = QDir::fromNativeSeparators(for_file);
QString fullPath = normalizedFile.startsWith(passStore)
? normalizedFile
: passStore + "/" + normalizedFile;
QDir gpgIdDir(QFileInfo(fullPath).absoluteDir());
bool found = false;
while (gpgIdDir.exists() && gpgIdDir.absolutePath().startsWith(passStore)) {
if (QFile(gpgIdDir.absoluteFilePath(".gpg-id")).exists()) {
found = true;
break;
}
if (!gpgIdDir.cdUp()) {
break;
}
}
QString gpgIdPath(
found ? gpgIdDir.absoluteFilePath(".gpg-id")
return gpgIdPath;
}
QFile gpgId(getGpgIdPath(for_file));
if (!gpgId.open(QIODevice::ReadOnly | QIODevice::Text)) {
return {};
}
QStringList recipients;
while (!gpgId.atEnd()) {
QString recipient(gpgId.readLine());
recipient = recipient.split("#")[0].trimmed();
recipients += recipient;
}
}
return recipients;
}
int *count) -> QStringList {
Q_UNUSED(separator)
if (count) {
*count = recipients.size();
}
return recipients;
}
if (bound < 2) {
return 0;
}
quint32 randval;
const quint32 max_mod_bound = (1 + ~bound) % bound;
do {
randval = QRandomGenerator::system()->generate();
} while (randval < max_mod_bound);
return randval % bound;
}
-> QString {
if (charset.isEmpty() || length == 0U) {
return {};
}
QString out;
for (unsigned int i = 0; i < length; ++i) {
out.append(charset.at(static_cast<int>(
boundedRandom(static_cast<quint32>(charset.length())))));
}
return out;
}
id Identifier provided by the caller for this queued request.
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.
void starting()
starting signal that is emited when process starts
void init()
Initialize the Pass instance.
void startingExecuteWrapper()
Emitted before executing a command.
void GenerateGPGKeys(QString batch)
Generate GPG keys using batch script.
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.
virtual auto generatePassword(unsigned int length, const QString &charset) -> QString
Generate random password.
void finishedMove(const QString &, const QString &)
Emitted when move finishes.
static bool gpgSupportsEd25519()
Check if GPG supports Ed25519 encryption.
auto boundedRandom(quint32 bound) -> quint32
Generate random number in range.
void finishedGitInit(const QString &, const QString &)
Emitted when Git init finishes.
Pass()
Construct a Pass instance.
void executeWrapper(PROCESS id, const QString &app, const QStringList &args, bool readStdout=true, bool readStderr=true)
Execute external wrapper command.
static auto getRecipientList(const QString &for_file) -> QStringList
Get list of recipients for a password file.
static auto resolveGpgconfCommand(const QString &gpgPath) -> ResolvedGpgconfCommand
Resolve the gpgconf command to kill agents.
auto listKeys(QStringList keystrings, bool secret=false) -> QList< UserInfo >
List GPG keys matching patterns.
void finishedInsert(const QString &, const QString &)
Emitted when insert finishes.
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.
void finishedOtpGenerate(const QString &)
Emitted when OTP generation finishes.
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.
virtual void finished(int id, int exitCode, const QString &out, const QString &err)
Handle process completion.
static auto getRecipientString(const QString &for_file, const QString &separator=" ", int *count=nullptr) -> QStringList
Get recipients as string.
static QString getDefaultKeyTemplate()
Get default key template for new GPG keys.
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.
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. Validates a GPG key ID after normalization:
Debug utilities for QtPass.
#define dbg()
Simple debug macro that includes file and line number.
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.
PROCESS
Identifies different subprocess operations used in QtPass.