5#include <QRegularExpression>
20static QString pgit(
const QString &path) {
23 QString res =
"$(wslpath " + path +
")";
24 return res.replace(
'\\',
'/');
27static QString pgpg(
const QString &path) {
30 QString res =
"$(wslpath " + path +
")";
31 return res.replace(
'\\',
'/');
67 QStringList args = {
"-d",
"--quiet",
"--yes",
"--no-encrypt-to",
68 "--batch",
"--use-agent", pgpg(file)};
77 dbg() <<
"No OTP generation code for fake pass yet, attempting for file: " +
94 if (!verifyGpgIdFile(gpgIdPath)) {
95 emit
critical(tr(
"Check .gpgid file signature!"),
96 tr(
"Signature for %1 is invalid.").arg(gpgIdPath));
101 if (recipients.isEmpty()) {
104 tr(
"Could not read encryption key to use, .gpg-id "
105 "file missing or invalid."));
108 QStringList args = {
"--batch",
"-eq",
"--output", pgpg(file)};
109 for (
auto &r : recipients) {
114 args.append(
"--yes");
120 executeGit(
GIT_ADD, {
"add", pgit(file)});
124 QString(overwrite ?
"Edit" :
"Add") +
" for " + path +
" using QtPass.";
125 GitCommit(file, msg);
135void ImitatePass::GitCommit(
const QString &file,
const QString &msg) {
137 executeGit(
GIT_COMMIT, {
"commit",
"-m", msg});
139 executeGit(
GIT_COMMIT, {
"commit",
"-m", msg,
"--", pgit(file)});
151 executeGit(
GIT_RM, {
"rm", (isDir ?
"-rf" :
"-f"), pgit(file)});
154 GitCommit(file,
"Remove for " + file +
" using QtPass.");
158 dir.removeRecursively();
160 QFile(file).remove();
172#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
173 QStringList signingKeys =
176 QStringList signingKeys =
179 QString gpgIdSigFile = path +
".gpg-id.sig";
180 bool addSigFile =
false;
181 if (!signingKeys.isEmpty()) {
184 QStringList{
"--status-fd=1",
"--list-secret-keys"} + signingKeys;
187 for (
auto &key : signingKeys) {
188 if (out.contains(
"[GNUPG:] KEY_CONSIDERED " + key)) {
194 emit
critical(tr(
"No signing key!"),
195 tr(
"None of the secret signing keys is available.\n"
196 "You will not be able to change the user list!"));
199 QFileInfo checkFile(gpgIdSigFile);
200 if (!checkFile.exists() || !checkFile.isFile())
204 QString gpgIdFile = path +
".gpg-id";
205 QFile gpgId(gpgIdFile);
206 bool addFile =
false;
207 transactionHelper trans(
this,
PASS_INIT);
209 QFileInfo checkFile(gpgIdFile);
210 if (!checkFile.exists() || !checkFile.isFile())
213 if (!gpgId.open(QIODevice::WriteOnly | QIODevice::Text)) {
215 tr(
"Failed to open .gpg-id for writing."));
218 bool secret_selected =
false;
219 foreach (
const UserInfo &user, users) {
221 gpgId.write((user.
key_id +
"\n").toUtf8());
226 if (!secret_selected) {
228 tr(
"Check selected users!"),
229 tr(
"None of the selected keys have a secret key available.\n"
230 "You will not be able to decrypt any newly added passwords!"));
234 if (!signingKeys.isEmpty()) {
236 for (
auto &key : signingKeys) {
237 args.append(QStringList{
"--default-key", key});
239 args.append(QStringList{
"--yes",
"--detach-sign", gpgIdFile});
241 if (!verifyGpgIdFile(gpgIdFile)) {
242 emit
critical(tr(
"Check .gpgid file signature!"),
243 tr(
"Signature for %1 is invalid.").arg(gpgIdFile));
251 executeGit(
GIT_ADD, {
"add", pgit(gpgIdFile)});
252 QString commitPath = gpgIdFile;
254 GitCommit(gpgIdFile,
"Added " + commitPath +
" using QtPass.");
255 if (!signingKeys.isEmpty()) {
257 executeGit(
GIT_ADD, {
"add", pgit(gpgIdSigFile)});
258 commitPath = gpgIdSigFile;
259 commitPath.replace(QRegularExpression(
"\\.gpg$"),
"");
260 GitCommit(gpgIdSigFile,
"Added " + commitPath +
" using QtPass.");
271bool ImitatePass::verifyGpgIdFile(
const QString &file) {
272#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
273 QStringList signingKeys =
276 QStringList signingKeys =
279 if (signingKeys.isEmpty())
283 QStringList{
"--verify",
"--status-fd=1", pgpg(file) +
".sig", pgpg(file)};
285 QRegularExpression re(
286 "^\\[GNUPG:\\] VALIDSIG ([A-F0-9]{40}) .* ([A-F0-9]{40})\\r?$",
287 QRegularExpression::MultilineOption);
288 QRegularExpressionMatch m = re.match(out);
291 QStringList fingerprints = m.capturedTexts();
292 fingerprints.removeFirst();
293 for (
auto &key : signingKeys) {
294 if (fingerprints.contains(key))
305bool ImitatePass::removeDir(
const QString &dirName) {
309 if (dir.exists(dirName)) {
310 Q_FOREACH (QFileInfo info,
311 dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System |
312 QDir::Hidden | QDir::AllDirs | QDir::Files,
315 result = removeDir(info.absoluteFilePath());
317 result = QFile::remove(info.absoluteFilePath());
322 result = dir.rmdir(dirName);
335 emit
statusMsg(tr(
"Re-encrypting from folder %1").arg(dir), 3000);
339 emit
statusMsg(tr(
"Updating password-store"), 2000);
343 QDirIterator gpgFiles(dir, QStringList() <<
"*.gpg", QDir::Files,
344 QDirIterator::Subdirectories);
345 QStringList gpgIdFilesVerified;
347 while (gpgFiles.hasNext()) {
348 QString fileName = gpgFiles.next();
349 if (gpgFiles.fileInfo().path() != currentDir.path()) {
351 if (!gpgIdFilesVerified.contains(gpgIdPath)) {
352 if (!verifyGpgIdFile(gpgIdPath)) {
353 emit
critical(tr(
"Check .gpgid file signature!"),
354 tr(
"Signature for %1 is invalid.").arg(gpgIdPath));
358 gpgIdFilesVerified.append(gpgIdPath);
365 "-v",
"--no-secmem-warning",
"--no-permission-warning",
366 "--list-only",
"--keyid-format=long", pgpg(fileName)};
369 QStringList actualKeys;
371 static const QRegularExpression newLines{
"[\r\n]"};
372#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
373 QStringList key = keys.split(newLines, Qt::SkipEmptyParts);
375 QStringList key = keys.split(newLines, QString::SkipEmptyParts);
377 QListIterator<QString> itr(key);
378 while (itr.hasNext()) {
379 QString current = itr.next();
380 QStringList cur = current.split(
" ");
381 if (cur.length() > 4) {
382 QString actualKey = cur.takeAt(4);
383 if (actualKey.length() == 16) {
384 actualKeys << actualKey;
389 if (actualKeys != gpgId) {
392 dbg() <<
"reencrypt " << fileName <<
" for " << gpgId;
394 QString local_lastDecrypt =
"Could not decrypt";
396 "-d",
"--quiet",
"--yes",
"--no-encrypt-to",
397 "--batch",
"--use-agent", pgpg(fileName)};
401 if (!local_lastDecrypt.isEmpty() &&
402 local_lastDecrypt !=
"Could not decrypt") {
403 if (local_lastDecrypt.right(1) !=
"\n")
404 local_lastDecrypt +=
"\n";
407 if (recipients.isEmpty()) {
409 tr(
"Could not read encryption key to use, .gpg-id "
410 "file missing or invalid."));
414 QStringList{
"--yes",
"--batch",
"-eq",
"--output", pgpg(fileName)};
415 for (
auto &i : recipients) {
425 {
"add", pgit(fileName)});
430 {
"commit", pgit(fileName),
"-m",
431 "Edit for " + path +
" using QtPass."});
436 dbg() <<
"Decrypt error on re-encrypt";
442 emit
statusMsg(tr(
"Updating password-store"), 2000);
451 transactionHelper trans(
this,
PASS_MOVE);
452 QFileInfo srcFileInfo(src);
453 QFileInfo destFileInfo(dest);
455 QString srcFileBaseName = srcFileInfo.fileName();
457 if (srcFileInfo.isFile()) {
458 if (destFileInfo.isFile()) {
461 dbg() <<
"Destination file already exists";
465 }
else if (destFileInfo.isDir()) {
466 destFile = QDir(dest).filePath(srcFileBaseName);
471 if (destFile.endsWith(
".gpg", Qt::CaseInsensitive))
473 destFile.append(
".gpg");
474 }
else if (srcFileInfo.isDir()) {
475 if (destFileInfo.isDir()) {
476 destFile = QDir(dest).filePath(srcFileBaseName);
477 }
else if (destFileInfo.isFile()) {
479 dbg() <<
"Destination is a file";
487 dbg() <<
"Source file does not exist";
493 dbg() <<
"Move Source: " << src;
494 dbg() <<
"Move Destination: " << destFile;
504 args << pgit(destFile);
512 QString message = QString(
"Moved for %1 to %2 using QtPass.");
513 message = message.arg(relSrc, relDest);
514 GitCommit(
"", message);
518 qDir.remove(destFile);
520 qDir.rename(src, destFile);
526 QFileInfo destFileInfo(dest);
527 transactionHelper trans(
this,
PASS_COPY);
538 QString message = QString(
"copied from %1 to %2 using QTPass.");
539 message = message.arg(src, dest);
540 GitCommit(
"", message);
546 QFile::copy(src, dest);
549 if (destFileInfo.isDir()) {
551 }
else if (destFileInfo.isFile()) {
560void ImitatePass::executeGpg(
PROCESS id,
const QStringList &args, QString input,
561 bool readStdout,
bool readStderr) {
563 readStdout, readStderr);
569void ImitatePass::executeGit(
PROCESS id,
const QStringList &args, QString input,
570 bool readStdout,
bool readStderr) {
572 readStdout, readStderr);
586 const QString &err) {
588 dbg() <<
"Imitate Pass";
590 static QString transactionOutput;
592 transactionOutput.append(out);
603 dbg() <<
"No such transaction!";
611 transactionOutput.clear();
624 const QStringList &args, QString input,
625 bool readStdout,
bool readStderr) {
static int executeBlocking(QString app, const QStringList &args, QString input=QString(), QString *process_out=Q_NULLPTR, QString *process_err=Q_NULLPTR)
Executor::executeBlocking blocking version of the executor, takes input and presents it as stdin.
int cancelNext()
Executor::cancelNext cancels execution of first process in queue if it's not already running.
virtual void OtpGenerate(QString file) Q_DECL_OVERRIDE
ImitatePass::OtpGenerate generates an otp code.
virtual void Show(QString file) Q_DECL_OVERRIDE
ImitatePass::Show shows content of file.
ImitatePass()
ImitatePass::ImitatePass for situaions when pass is not available we imitate the behavior of pass htt...
virtual void GitPush() Q_DECL_OVERRIDE
ImitatePass::GitPush git init wrapper.
void Copy(const QString src, const QString dest, const bool force=false) Q_DECL_OVERRIDE
virtual void executeWrapper(PROCESS id, const QString &app, const QStringList &args, QString input, bool readStdout=true, bool readStderr=true) Q_DECL_OVERRIDE
executeWrapper overrided so that every execution is a transaction
virtual void GitInit() Q_DECL_OVERRIDE
ImitatePass::GitInit git init wrapper.
virtual void GitPull_b() Q_DECL_OVERRIDE
ImitatePass::GitPull_b git pull wrapper.
virtual void Insert(QString file, QString newValue, bool overwrite=false) Q_DECL_OVERRIDE
ImitatePass::Insert create new file with encrypted content.
virtual void GitPull() Q_DECL_OVERRIDE
ImitatePass::GitPull git init wrapper.
virtual void Remove(QString file, bool isDir=false) Q_DECL_OVERRIDE
ImitatePass::Remove custom implementation of "pass remove".
void Move(const QString src, const QString dest, const bool force=false) Q_DECL_OVERRIDE
virtual void Init(QString path, const QList< UserInfo > &users) Q_DECL_OVERRIDE
ImitatePass::Init initialize pass repository.
void startReencryptPath()
void reencryptPath(const QString &dir)
ImitatePass::reencryptPath reencrypt all files under the chosen directory.
void statusMsg(QString, int)
void critical(QString, QString)
void executeWrapper(PROCESS id, const QString &app, const QStringList &args, bool readStdout=true, bool readStderr=true)
static QStringList getRecipientList(QString for_file)
Pass::getRecipientList return list of gpg-id's to encrypt for.
static QString getGpgIdPath(QString for_file)
Pass::getGpgIdPath return gpgid file path for some file (folder).
virtual void finished(int id, int exitCode, const QString &out, const QString &err)
Pass::processFinished reemits specific signal based on what process has finished.
static QString getPassSigningKey(const QString &defaultValue=QVariant().toString())
static bool isUseGit(const bool &defaultValue=QVariant().toBool())
static bool isAutoPull(const bool &defaultValue=QVariant().toBool())
static bool isUseWebDav(const bool &defaultValue=QVariant().toBool())
static QString getGpgExecutable(const QString &defaultValue=QVariant().toString())
static bool isAutoPush(const bool &defaultValue=QVariant().toBool())
static QString getPassStore(const QString &defaultValue=QVariant().toString())
static QString getGitExecutable(const QString &defaultValue=QVariant().toString())
static bool isAddGPGId(const bool &defaultValue=QVariant().toBool())
static const QRegularExpression & endsWithGpg()
void transactionAdd(Enums::PROCESS)
transactionAdd If called after call to transactionStart() and before transactionEnd(),...
Enums::PROCESS transactionIsOver(Enums::PROCESS)
transactionIsOver checks wheather currently finished process is last in current transaction
Enumerators for configuration and runtime items.
Stores key info lines including validity, creation date and more.
bool have_secret
UserInfo::have_secret secret key is available (can decrypt with this key)
bool enabled
UserInfo::enabled.
QString key_id
UserInfo::key_id hexadecimal representation.