7#include <QRegularExpression>
42static auto pgit(
const QString &path) -> QString {
46 QString res =
"$(wslpath " + path +
")";
47 return res.replace(
'\\',
'/');
50static auto pgpg(
const QString &path) -> QString {
54 QString res =
"$(wslpath " + path +
")";
55 return res.replace(
'\\',
'/');
82 executeGit(GIT_PUSH, {
"push"});
91 QStringList args = {
"-d",
"--quiet",
"--yes",
"--no-encrypt-to",
92 "--batch",
"--use-agent", pgpg(file)};
93 executeGpg(PASS_SHOW, args);
101 dbg() <<
"No OTP generation code for fake pass yet, attempting for file: " +
116 file = file +
".gpg";
118 if (!verifyGpgIdFile(gpgIdPath)) {
119 emit
critical(tr(
"Check .gpgid file signature!"),
120 tr(
"Signature for %1 is invalid.").arg(gpgIdPath));
123 transactionHelper trans(
this, PASS_INSERT);
125 if (recipients.isEmpty()) {
128 tr(
"Could not read encryption key to use, .gpg-id "
129 "file missing or invalid."));
132 QStringList args = {
"--batch",
"-eq",
"--output", pgpg(file)};
133 for (
auto &r : recipients) {
138 args.append(
"--yes");
141 executeGpg(PASS_INSERT, args, newValue);
145 executeGit(GIT_ADD, {
"add", pgit(file)});
150 QString(overwrite ?
"Edit" :
"Add") +
" for " + path +
" using QtPass.";
151 GitCommit(file, msg);
161void ImitatePass::GitCommit(
const QString &file,
const QString &msg) {
162 if (file.isEmpty()) {
163 executeGit(GIT_COMMIT, {
"commit",
"-m", msg});
165 executeGit(GIT_COMMIT, {
"commit",
"-m", msg,
"--", pgit(file)});
174 transactionHelper trans(
this, PASS_REMOVE);
179 executeGit(GIT_RM, {
"rm", (isDir ?
"-rf" :
"-f"), pgit(file)});
182 GitCommit(file,
"Remove for " + file +
" using QtPass.");
186 dir.removeRecursively();
188 QFile(file).remove();
201#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
202 QStringList signingKeys =
205 QStringList signingKeys =
208 QString gpgIdSigFile = path +
".gpg-id.sig";
209 bool addSigFile =
false;
210 if (!signingKeys.isEmpty()) {
213 QStringList{
"--status-fd=1",
"--list-secret-keys"} + signingKeys;
216 for (
auto &key : signingKeys) {
217 if (out.contains(
"[GNUPG:] KEY_CONSIDERED " + key)) {
223 emit
critical(tr(
"No signing key!"),
224 tr(
"None of the secret signing keys is available.\n"
225 "You will not be able to change the user list!"));
228 QFileInfo checkFile(gpgIdSigFile);
229 if (!checkFile.exists() || !checkFile.isFile()) {
234 QString gpgIdFile = path +
".gpg-id";
235 QFile gpgId(gpgIdFile);
236 bool addFile =
false;
237 transactionHelper trans(
this, PASS_INIT);
239 QFileInfo checkFile(gpgIdFile);
240 if (!checkFile.exists() || !checkFile.isFile()) {
244 if (!gpgId.open(QIODevice::WriteOnly | QIODevice::Text)) {
246 tr(
"Failed to open .gpg-id for writing."));
249 bool secret_selected =
false;
250 foreach (
const UserInfo &user, users) {
252 gpgId.write((user.
key_id +
"\n").toUtf8());
257 if (!secret_selected) {
259 tr(
"Check selected users!"),
260 tr(
"None of the selected keys have a secret key available.\n"
261 "You will not be able to decrypt any newly added passwords!"));
265 if (!signingKeys.isEmpty()) {
267 for (
auto &key : signingKeys) {
268 args.append(QStringList{
"--default-key", key});
270 args.append(QStringList{
"--yes",
"--detach-sign", gpgIdFile});
272 if (!verifyGpgIdFile(gpgIdFile)) {
273 emit
critical(tr(
"Check .gpgid file signature!"),
274 tr(
"Signature for %1 is invalid.").arg(gpgIdFile));
282 executeGit(GIT_ADD, {
"add", pgit(gpgIdFile)});
284 QString commitPath = gpgIdFile;
286 GitCommit(gpgIdFile,
"Added " + commitPath +
" using QtPass.");
287 if (!signingKeys.isEmpty()) {
289 executeGit(GIT_ADD, {
"add", pgit(gpgIdSigFile)});
291 commitPath = gpgIdSigFile;
292 commitPath.replace(QRegularExpression(
"\\.gpg$"),
"");
293 GitCommit(gpgIdSigFile,
"Added " + commitPath +
" using QtPass.");
304auto ImitatePass::verifyGpgIdFile(
const QString &file) ->
bool {
305#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
306 QStringList signingKeys =
309 QStringList signingKeys =
312 if (signingKeys.isEmpty()) {
317 QStringList{
"--verify",
"--status-fd=1", pgpg(file) +
".sig", pgpg(file)};
319 QRegularExpression re(
320 R
"(^\[GNUPG:\] VALIDSIG ([A-F0-9]{40}) .* ([A-F0-9]{40})\r?$)",
321 QRegularExpression::MultilineOption);
322 QRegularExpressionMatch m = re.match(out);
326 QStringList fingerprints = m.capturedTexts();
327 fingerprints.removeFirst();
328 for (
auto &key : signingKeys) {
329 if (fingerprints.contains(key)) {
341auto ImitatePass::removeDir(
const QString &dirName) ->
bool {
345 if (dir.exists(dirName)) {
346 Q_FOREACH (QFileInfo info,
347 dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System |
348 QDir::Hidden | QDir::AllDirs | QDir::Files,
351 result = removeDir(info.absoluteFilePath());
353 result = QFile::remove(info.absoluteFilePath());
360 result = dir.rmdir(dirName);
373 emit
statusMsg(tr(
"Re-encrypting from folder %1").arg(dir), 3000);
377 emit
statusMsg(tr(
"Updating password-store"), 2000);
381 QDirIterator gpgFiles(dir, QStringList() <<
"*.gpg", QDir::Files,
382 QDirIterator::Subdirectories);
383 QStringList gpgIdFilesVerified;
385 while (gpgFiles.hasNext()) {
386 QString fileName = gpgFiles.next();
387 if (gpgFiles.fileInfo().path() != currentDir.path()) {
389 if (!gpgIdFilesVerified.contains(gpgIdPath)) {
390 if (!verifyGpgIdFile(gpgIdPath)) {
391 emit
critical(tr(
"Check .gpgid file signature!"),
392 tr(
"Signature for %1 is invalid.").arg(gpgIdPath));
396 gpgIdFilesVerified.append(gpgIdPath);
403 "-v",
"--no-secmem-warning",
"--no-permission-warning",
404 "--list-only",
"--keyid-format=long", pgpg(fileName)};
409 QStringList actualKeys;
411#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
417 QListIterator<QString> itr(key);
418 while (itr.hasNext()) {
419 QString current = itr.next();
420 QStringList cur = current.split(
" ");
421 if (cur.length() > 4) {
422 QString actualKey = cur.takeAt(4);
423 if (actualKey.length() == 16) {
424 actualKeys << actualKey;
429 if (actualKeys != gpgId) {
432 dbg() <<
"reencrypt " << fileName <<
" for " << gpgId;
434 QString local_lastDecrypt =
"Could not decrypt";
436 "-d",
"--quiet",
"--yes",
"--no-encrypt-to",
437 "--batch",
"--use-agent", pgpg(fileName)};
441 if (!local_lastDecrypt.isEmpty() &&
442 local_lastDecrypt !=
"Could not decrypt") {
443 if (local_lastDecrypt.right(1) !=
"\n") {
444 local_lastDecrypt +=
"\n";
448 if (recipients.isEmpty()) {
450 tr(
"Could not read encryption key to use, .gpg-id "
451 "file missing or invalid."));
455 QStringList{
"--yes",
"--batch",
"-eq",
"--output", pgpg(fileName)};
456 for (
auto &i : recipients) {
466 {
"add", pgit(fileName)});
471 {
"commit", pgit(fileName),
"-m",
472 "Edit for " + path +
" using QtPass."});
477 dbg() <<
"Decrypt error on re-encrypt";
483 emit
statusMsg(tr(
"Updating password-store"), 2000);
492 transactionHelper trans(
this, PASS_MOVE);
493 QFileInfo srcFileInfo(src);
494 QFileInfo destFileInfo(dest);
496 QString srcFileBaseName = srcFileInfo.fileName();
498 if (srcFileInfo.isFile()) {
499 if (destFileInfo.isFile()) {
502 dbg() <<
"Destination file already exists";
506 }
else if (destFileInfo.isDir()) {
507 destFile = QDir(dest).filePath(srcFileBaseName);
512 if (destFile.endsWith(
".gpg", Qt::CaseInsensitive)) {
515 destFile.append(
".gpg");
516 }
else if (srcFileInfo.isDir()) {
517 if (destFileInfo.isDir()) {
518 destFile = QDir(dest).filePath(srcFileBaseName);
519 }
else if (destFileInfo.isFile()) {
521 dbg() <<
"Destination is a file";
529 dbg() <<
"Source file does not exist";
535 dbg() <<
"Move Source: " << src;
536 dbg() <<
"Move Destination: " << destFile;
546 args << pgit(destFile);
547 executeGit(GIT_MOVE, args);
554 QString message = QString(
"Moved for %1 to %2 using QtPass.");
555 message = message.arg(relSrc, relDest);
556 GitCommit(
"", message);
560 qDir.remove(destFile);
562 qDir.rename(src, destFile);
568 QFileInfo destFileInfo(dest);
569 transactionHelper trans(
this, PASS_COPY);
578 executeGit(GIT_COPY, args);
580 QString message = QString(
"copied from %1 to %2 using QTPass.");
581 message = message.arg(src, dest);
582 GitCommit(
"", message);
588 QFile::copy(src, dest);
591 if (destFileInfo.isDir()) {
593 }
else if (destFileInfo.isFile()) {
602void ImitatePass::executeGpg(PROCESS
id,
const QStringList &args, QString input,
603 bool readStdout,
bool readStderr) {
605 readStdout, readStderr);
611void ImitatePass::executeGit(PROCESS
id,
const QStringList &args, QString input,
612 bool readStdout,
bool readStderr) {
614 readStdout, readStderr);
628 const QString &err) {
630 dbg() <<
"Imitate Pass";
632 static QString transactionOutput;
634 transactionOutput.append(out);
637 if (pid == INVALID) {
641 while (pid == INVALID) {
646 dbg() <<
"No such transaction!";
654 transactionOutput.clear();
667 const QStringList &args, QString input,
668 bool readStdout,
bool readStderr) {
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.
auto cancelNext() -> int
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 finished(int id, int exitCode, const QString &out, const QString &err) Q_DECL_OVERRIDE
ImitatePass::finished this function is overloaded to ensure identical behaviour to RealPass ie....
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 critical(const QString &, const QString &)
void statusMsg(const QString &, int)
void executeWrapper(PROCESS id, const QString &app, const QStringList &args, bool readStdout=true, bool readStderr=true)
static auto getRecipientList(const QString &for_file) -> QStringList
Pass::getRecipientList return list of gpg-id's to encrypt for.
static auto getGpgIdPath(const QString &for_file) -> QString
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 auto isAutoPull(const bool &defaultValue=QVariant().toBool()) -> bool
static auto isUseGit(const bool &defaultValue=QVariant().toBool()) -> bool
static auto getPassStore(const QString &defaultValue=QVariant().toString()) -> QString
static auto isAddGPGId(const bool &defaultValue=QVariant().toBool()) -> bool
static auto getGpgExecutable(const QString &defaultValue=QVariant().toString()) -> QString
static auto getPassSigningKey(const QString &defaultValue=QVariant().toString()) -> QString
static auto isUseWebDav(const bool &defaultValue=QVariant().toBool()) -> bool
static auto isAutoPush(const bool &defaultValue=QVariant().toBool()) -> bool
static auto getGitExecutable(const QString &defaultValue=QVariant().toString()) -> QString
static auto endsWithGpg() -> const QRegularExpression &
static auto newLinesRegex() -> const QRegularExpression &
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(),...
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.