QtPass  1.2.3
Multi-platform GUI for pass, the standard unix password manager.
imitatepass.cpp
Go to the documentation of this file.
1 #include "imitatepass.h"
2 #include "debughelper.h"
3 #include "qtpasssettings.h"
4 #include <QDirIterator>
5 
6 using namespace Enums;
7 
13 
18  executeGit(GIT_INIT, {"init", QtPassSettings::getPassStore()});
19 }
20 
24 void ImitatePass::GitPull() { executeGit(GIT_PULL, {"pull"}); }
25 
30  exec.executeBlocking(QtPassSettings::getGitExecutable(), {"pull"});
31 }
32 
38  executeGit(GIT_PUSH, {"push"});
39  }
40 }
41 
45 void ImitatePass::Show(QString file) {
46  file = QtPassSettings::getPassStore() + file + ".gpg";
47  QStringList args = {"-d", "--quiet", "--yes", "--no-encrypt-to",
48  "--batch", "--use-agent", file};
49  executeGpg(PASS_SHOW, args);
50 
51 }
52 
56 void ImitatePass::OtpGenerate(QString file) {}
57 
65 void ImitatePass::Insert(QString file, QString newValue, bool overwrite) {
66  file = file + ".gpg";
67  transactionHelper trans(this, PASS_INSERT);
68  QStringList recipients = Pass::getRecipientList(file);
69  if (recipients.isEmpty()) {
70  // TODO(bezet): probably throw here
71  emit critical(tr("Can not edit"),
72  tr("Could not read encryption key to use, .gpg-id "
73  "file missing or invalid."));
74  return;
75  }
76  QStringList args = {"--batch", "-eq", "--output", file};
77  for (auto &r : recipients) {
78  args.append("-r");
79  args.append(r);
80  };
81  if (overwrite)
82  args.append("--yes");
83  args.append("-");
84  executeGpg(PASS_INSERT, args, newValue);
86  // TODO(bezet) why not?
87  if (!overwrite)
88  executeGit(GIT_ADD, {"add", file});
89  QString path = QDir(QtPassSettings::getPassStore()).relativeFilePath(file);
90  path.replace(QRegExp("\\.gpg$"), "");
91  QString msg =
92  QString(overwrite ? "Edit" : "Add") + " for " + path + " using QtPass.";
93  GitCommit(file, msg);
94  }
95 }
96 
103 void ImitatePass::GitCommit(const QString &file, const QString &msg) {
104  executeGit(GIT_COMMIT, {"commit", "-m", msg, "--", file});
105 }
106 
110 void ImitatePass::Remove(QString file, bool isDir) {
111  file = QtPassSettings::getPassStore() + file;
112  transactionHelper trans(this, PASS_REMOVE);
113  if (!isDir)
114  file += ".gpg";
115  if (QtPassSettings::isUseGit()) {
116  executeGit(GIT_RM, {"rm", (isDir ? "-rf" : "-f"), file});
117  // TODO(bezet): commit message used to have pass-like file name inside(ie.
118  // getFile(file, true)
119  GitCommit(file, "Remove for " + file + " using QtPass.");
120  } else {
121  if (isDir) {
122 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
123  QDir dir(file);
124  dir.removeRecursively();
125 #else
126  removeDir(QtPassSettings::getPassStore() + file);
127 #endif
128  } else
129  QFile(file).remove();
130  }
131 }
132 
140 void ImitatePass::Init(QString path, const QList<UserInfo> &users) {
141  QString gpgIdFile = path + ".gpg-id";
142  QFile gpgId(gpgIdFile);
143  bool addFile = false;
144  transactionHelper trans(this, PASS_INIT);
145  if (QtPassSettings::isAddGPGId(true)) {
146  QFileInfo checkFile(gpgIdFile);
147  if (!checkFile.exists() || !checkFile.isFile())
148  addFile = true;
149  }
150  if (!gpgId.open(QIODevice::WriteOnly | QIODevice::Text)) {
151  emit critical(tr("Cannot update"),
152  tr("Failed to open .gpg-id for writing."));
153  return;
154  }
155  bool secret_selected = false;
156  foreach (const UserInfo &user, users) {
157  if (user.enabled) {
158  gpgId.write((user.key_id + "\n").toUtf8());
159  secret_selected |= user.have_secret;
160  }
161  }
162  gpgId.close();
163  if (!secret_selected) {
164  emit critical(
165  tr("Check selected users!"),
166  tr("None of the selected keys have a secret key available.\n"
167  "You will not be able to decrypt any newly added passwords!"));
168  return;
169  }
170 
172  !QtPassSettings::getGitExecutable().isEmpty()) {
173  if (addFile)
174  executeGit(GIT_ADD, {"add", gpgIdFile});
175  QString path = gpgIdFile;
176  path.replace(QRegExp("\\.gpg$"), "");
177  GitCommit(gpgIdFile, "Added " + path + " using QtPass.");
178  }
179  reencryptPath(path);
180 }
181 
187 bool ImitatePass::removeDir(const QString &dirName) {
188  bool result = true;
189  QDir dir(dirName);
190 
191  if (dir.exists(dirName)) {
192  Q_FOREACH (QFileInfo info,
193  dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System |
194  QDir::Hidden | QDir::AllDirs | QDir::Files,
195  QDir::DirsFirst)) {
196  if (info.isDir())
197  result = removeDir(info.absoluteFilePath());
198  else
199  result = QFile::remove(info.absoluteFilePath());
200 
201  if (!result)
202  return result;
203  }
204  result = dir.rmdir(dirName);
205  }
206  return result;
207 }
208 
216 void ImitatePass::reencryptPath(QString dir) {
217  emit statusMsg(tr("Re-encrypting from folder %1").arg(dir), 3000);
218  emit startReencryptPath();
220  // TODO(bezet): move statuses inside actions?
221  emit statusMsg(tr("Updating password-store"), 2000);
222  GitPull_b();
223  }
224  QDir currentDir;
225  QDirIterator gpgFiles(dir, QStringList() << "*.gpg", QDir::Files,
226  QDirIterator::Subdirectories);
227  QStringList gpgId;
228  while (gpgFiles.hasNext()) {
229  QString fileName = gpgFiles.next();
230  if (gpgFiles.fileInfo().path() != currentDir.path()) {
231  gpgId = getRecipientList(fileName);
232  gpgId.sort();
233  }
234  // TODO(bezet): enable --with-colons for better future-proofness?
235  QStringList args = {
236  "-v", "--no-secmem-warning", "--no-permission-warning",
237  "--list-only", "--keyid-format=long", fileName};
238  QString keys, err;
239  exec.executeBlocking(QtPassSettings::getGpgExecutable(), args, &keys, &err);
240  QStringList actualKeys;
241  keys += err;
242  QStringList key = keys.split("\n");
243  QListIterator<QString> itr(key);
244  while (itr.hasNext()) {
245  QString current = itr.next();
246  QStringList cur = current.split(" ");
247  if (cur.length() > 4) {
248  QString actualKey = cur.takeAt(4);
249  if (actualKey.length() == 16) {
250  actualKeys << actualKey;
251  }
252  }
253  }
254  actualKeys.sort();
255  if (actualKeys != gpgId) {
256  // dbg()<< actualKeys << gpgId << getRecipientList(fileName);
257  dbg() << "reencrypt " << fileName << " for " << gpgId;
258  QString local_lastDecrypt = "Could not decrypt";
259  args = QStringList{"-d", "--quiet", "--yes", "--no-encrypt-to",
260  "--batch", "--use-agent", fileName};
261  exec.executeBlocking(QtPassSettings::getGpgExecutable(), args,
262  &local_lastDecrypt);
263 
264  if (!local_lastDecrypt.isEmpty() &&
265  local_lastDecrypt != "Could not decrypt") {
266  if (local_lastDecrypt.right(1) != "\n")
267  local_lastDecrypt += "\n";
268 
269  QStringList recipients = Pass::getRecipientList(fileName);
270  if (recipients.isEmpty()) {
271  emit critical(tr("Can not edit"),
272  tr("Could not read encryption key to use, .gpg-id "
273  "file missing or invalid."));
274  return;
275  }
276  args = QStringList{"--yes", "--batch", "-eq", "--output", fileName};
277  for (auto &i : recipients) {
278  args.append("-r");
279  args.append(i);
280  }
281  args.append("-");
282  exec.executeBlocking(QtPassSettings::getGpgExecutable(), args,
283  local_lastDecrypt);
284 
286  exec.executeBlocking(QtPassSettings::getGitExecutable(),
287  {"add", fileName});
288  QString path =
289  QDir(QtPassSettings::getPassStore()).relativeFilePath(fileName);
290  path.replace(QRegExp("\\.gpg$"), "");
291  exec.executeBlocking(QtPassSettings::getGitExecutable(),
292  {"commit", fileName, "-m",
293  "Edit for " + path + " using QtPass."});
294  }
295 
296  } else {
297  dbg() << "Decrypt error on re-encrypt";
298  }
299  }
300  }
302  emit statusMsg(tr("Updating password-store"), 2000);
303  // TODO(bezet): this is non-blocking and shall be done outside
304  GitPush();
305  }
306  emit endReencryptPath();
307 }
308 
309 void ImitatePass::Move(const QString src, const QString dest,
310  const bool force) {
311  QFileInfo destFileInfo(dest);
312  transactionHelper trans(this, PASS_MOVE);
313  if (QtPassSettings::isUseGit()) {
314  QStringList args;
315  args << "mv";
316  if (force) {
317  args << "-f";
318  }
319  args << src;
320  args << dest;
321  executeGit(GIT_MOVE, args);
322 
323  QString message = QString("moved from %1 to %2 using QTPass.");
324  message = message.arg(src).arg(dest);
325  GitCommit("", message);
326  } else {
327  QDir qDir;
328  QFileInfo srcFileInfo(src);
329  QString destCopy = dest;
330  if (srcFileInfo.isFile() && destFileInfo.isDir()) {
331  destCopy = destFileInfo.absoluteFilePath() + QDir::separator() +
332  srcFileInfo.fileName();
333  }
334  if (force) {
335  qDir.remove(destCopy);
336  }
337  qDir.rename(src, destCopy);
338  }
339  // reecrypt all files under the new folder
340  if (destFileInfo.isDir()) {
341  reencryptPath(destFileInfo.absoluteFilePath());
342  } else if (destFileInfo.isFile()) {
343  reencryptPath(destFileInfo.dir().path());
344  }
345 }
346 
347 void ImitatePass::Copy(const QString src, const QString dest,
348  const bool force) {
349  QFileInfo destFileInfo(dest);
350  transactionHelper trans(this, PASS_COPY);
351  if (QtPassSettings::isUseGit()) {
352  QStringList args;
353  args << "cp";
354  if (force) {
355  args << "-f";
356  }
357  args << src;
358  args << dest;
359  executeGit(GIT_COPY, args);
360 
361  QString message = QString("copied from %1 to %2 using QTPass.");
362  message = message.arg(src).arg(dest);
363  GitCommit("", message);
364  } else {
365  QDir qDir;
366  if (force) {
367  qDir.remove(dest);
368  }
369  QFile::copy(src, dest);
370  }
371  // reecrypt all files under the new folder
372  if (destFileInfo.isDir()) {
373  reencryptPath(destFileInfo.absoluteFilePath());
374  } else if (destFileInfo.isFile()) {
375  reencryptPath(destFileInfo.dir().path());
376  }
377 }
378 
383 void ImitatePass::executeGpg(PROCESS id, const QStringList &args, QString input,
384  bool readStdout, bool readStderr) {
385  executeWrapper(id, QtPassSettings::getGpgExecutable(), args, input,
386  readStdout, readStderr);
387 }
392 void ImitatePass::executeGit(PROCESS id, const QStringList &args, QString input,
393  bool readStdout, bool readStderr) {
394  executeWrapper(id, QtPassSettings::getGitExecutable(), args, input,
395  readStdout, readStderr);
396 }
397 
408 void ImitatePass::finished(int id, int exitCode, const QString &out,
409  const QString &err) {
410  dbg() << "Imitate Pass";
411  static QString transactionOutput;
412  PROCESS pid = transactionIsOver(static_cast<PROCESS>(id));
413  transactionOutput.append(out);
414 
415  if (exitCode == 0) {
416  if (pid == INVALID)
417  return;
418  } else {
419  while (pid == INVALID) {
420  id = exec.cancelNext();
421  if (id == -1) {
422  // this is probably irrecoverable and shall not happen
423  dbg() << "No such transaction!";
424  return;
425  }
426  pid = transactionIsOver(static_cast<PROCESS>(id));
427  }
428  }
429  Pass::finished(pid, exitCode, transactionOutput, err);
430  transactionOutput.clear();
431 }
432 
442 void ImitatePass::executeWrapper(PROCESS id, const QString &app,
443  const QStringList &args, QString input,
444  bool readStdout, bool readStderr) {
445  transactionAdd(id);
446  Pass::executeWrapper(id, app, args, input, readStdout, readStderr);
447 }
void reencryptPath(QString dir)
ImitatePass::reencryptPath reencrypt all files under the chosen directory.
virtual void Init(QString path, const QList< UserInfo > &list) Q_DECL_OVERRIDE
ImitatePass::Init initialize pass repository.
virtual void OtpGenerate(QString file) Q_DECL_OVERRIDE
ImitatePass::OtpGenerate generates an otp code.
Definition: imitatepass.cpp:56
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 GitInit() Q_DECL_OVERRIDE
ImitatePass::GitInit git init wrapper.
Definition: imitatepass.cpp:17
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
PROCESS
Definition: enums.h:16
bool enabled
UserInfo::enabled.
Definition: userinfo.h:50
#define dbg()
Definition: debughelper.h:7
QString key_id
UserInfo::key_id hexadecimal representation.
Definition: userinfo.h:36
bool have_secret
UserInfo::have_secret secret key is available (can decrypt with this key)
Definition: userinfo.h:46
ImitatePass()
ImitatePass::ImitatePass for situaions when pass is not available we imitate the behavior of pass htt...
Definition: imitatepass.cpp:12
static bool isUseWebDav(const bool &defaultValue=QVariant().toBool())
virtual void Remove(QString file, bool isDir=false) Q_DECL_OVERRIDE
ImitatePass::Remove custom implementation of "pass remove".
static bool isAddGPGId(const bool &defaultValue=QVariant().toBool())
virtual void Insert(QString file, QString value, bool overwrite=false) Q_DECL_OVERRIDE
ImitatePass::Insert create new file with encrypted content.
Definition: imitatepass.cpp:65
void Move(const QString src, const QString dest, const bool force=false) Q_DECL_OVERRIDE
static bool isUseGit(const bool &defaultValue=QVariant().toBool())
static QString getPassStore(const QString &defaultValue=QVariant().toString())
virtual void GitPull() Q_DECL_OVERRIDE
ImitatePass::GitPull git init wrapper.
Definition: imitatepass.cpp:24
Enumerators for configuration and runtime items.
static QString getGitExecutable(const QString &defaultValue=QVariant().toString())
virtual void finished(int id, int exitCode, const QString &out, const QString &err)
Pass::processFinished reemits specific signal based on what process has finished. ...
Definition: pass.cpp:164
static QStringList getRecipientList(QString for_file)
Pass::getRecipientList return list of gpg-id&#39;s to encrypt for.
Definition: pass.cpp:233
virtual void GitPush() Q_DECL_OVERRIDE
ImitatePass::GitPush git init wrapper.
Definition: imitatepass.cpp:36
void executeWrapper(PROCESS id, const QString &app, const QStringList &args, bool readStdout=true, bool readStderr=true)
Definition: pass.cpp:25
Stores key info lines including validity, creation date and more.
Definition: userinfo.h:11
static QString getGpgExecutable(const QString &defaultValue=QVariant().toString())
virtual void Show(QString file) Q_DECL_OVERRIDE
ImitatePass::Show shows content of file.
Definition: imitatepass.cpp:45
virtual void GitPull_b() Q_DECL_OVERRIDE
ImitatePass::GitPull_b git pull wrapper.
Definition: imitatepass.cpp:29
void Copy(const QString src, const QString dest, const bool force=false) Q_DECL_OVERRIDE
static bool isAutoPull(const bool &defaultValue=QVariant().toBool())
static bool isAutoPush(const bool &defaultValue=QVariant().toBool())