QtPass  1.2.3
Multi-platform GUI for pass, the standard unix password manager.
qtpass.cpp
Go to the documentation of this file.
1 #include "qtpass.h"
2 #include "mainwindow.h"
3 #include "qtpasssettings.h"
4 #include <QApplication>
5 #include <QClipboard>
6 
7 #ifndef Q_OS_WIN
8 #include <QInputDialog>
9 #include <QLineEdit>
10 #else
11 #define WIN32_LEAN_AND_MEAN /*_KILLING_MACHINE*/
12 #define WIN32_EXTRA_LEAN
13 #include <windows.h>
14 #include <winnetwk.h>
15 #undef DELETE
16 #endif
17 
18 #ifdef QT_DEBUG
19 #include "debughelper.h"
20 #endif
21 
22 QtPass::QtPass() : clippedText(QString()), freshStart(true) {
23  if (!setup()) {
24  // no working config so this should quit without config anything
25  QApplication::quit();
26  }
27 
29  clearClipboardTimer.setSingleShot(true);
30  connect(&clearClipboardTimer, SIGNAL(timeout()), this,
31  SLOT(clearClipboard()));
32 
33  QObject::connect(qApp, &QApplication::aboutToQuit, this,
35 }
36 
41 #ifdef Q_OS_WIN
43  WNetCancelConnection2A(QtPassSettings::getPassStore().toUtf8().constData(),
44  0, 1);
45 #else
46  if (fusedav.state() == QProcess::Running) {
47  fusedav.terminate();
48  fusedav.waitForFinished(2000);
49  }
50 #endif
51 }
52 
57 bool QtPass::setup() {
60 
62 
63  QString version = QtPassSettings::getVersion();
64  // dbg()<< version;
65 
66  // Config updates
67  if (version.isEmpty()) {
68 #ifdef QT_DEBUG
69  dbg() << "assuming fresh install";
70 #endif
71 
76  if (!QtPassSettings::getPwgenExecutable().isEmpty())
78  else
80  QtPassSettings::setPassTemplate("login\nurl");
81  } else {
82  // QStringList ver = version.split(".");
83  // dbg()<< ver;
84  // if (ver[0] == "0" && ver[1] == "8") {
86  // }
87  if (QtPassSettings::getPassTemplate().isEmpty())
88  QtPassSettings::setPassTemplate("login\nurl");
89  }
90 
92 
93  if (Util::checkConfig()) {
94  m_mainWindow->config();
95  if (freshStart && Util::checkConfig())
96  return false;
97  }
98 
99  // TODO(annejan): this needs to be before we try to access the store,
100  // but it would be better to do it after the Window is shown,
101  // as the long delay it can cause is irritating otherwise.
103  mountWebDav();
104 
105  freshStart = false;
106  // startupPhase = false;
107  return true;
108 }
109 
111  m_mainWindow = mW;
112  m_mainWindow->restoreWindow();
113 
114  fusedav.setParent(m_mainWindow);
115 
116  // TODO(bezet): this should be reconnected dynamically when pass changes
117  connectPassSignalHandlers(QtPassSettings::getRealPass());
118  connectPassSignalHandlers(QtPassSettings::getImitatePass());
119 
120  // only for ipass
122  m_mainWindow, &MainWindow::startReencryptPath);
124  m_mainWindow, &MainWindow::endReencryptPath);
125 
126  connect(m_mainWindow, &MainWindow::passGitInitNeeded, [=]() {
127 #ifdef QT_DEBUG
128  dbg() << "Pass git init called";
129 #endif
131  });
132 
133  connect(
134  m_mainWindow, &MainWindow::generateGPGKeyPair, [=](const QString &batch) {
136  m_mainWindow->showStatusMessage(tr("Generating GPG key pair"), 60000);
137  });
138 }
139 
140 void QtPass::connectPassSignalHandlers(Pass *pass) {
141  connect(pass, &Pass::error, this, &QtPass::processError);
142  connect(pass, &Pass::processErrorExit, this, &QtPass::processErrorExit);
143 
144  connect(pass, &Pass::critical, m_mainWindow, &MainWindow::critical);
145  connect(pass, &Pass::startingExecuteWrapper, m_mainWindow,
147  connect(pass, &Pass::statusMsg, m_mainWindow, &MainWindow::showStatusMessage);
148  connect(m_mainWindow, &MainWindow::passShowHandlerFinished, this,
149  &QtPass::passShowHandlerFinished);
150  connect(pass, &Pass::finishedShow, m_mainWindow,
152  connect(pass, &Pass::finishedOtpGenerate, m_mainWindow,
154 
155  connect(pass, &Pass::finishedGitInit, this, &QtPass::passStoreChanged);
156  connect(pass, &Pass::finishedGitPull, this, &QtPass::processFinished);
157  connect(pass, &Pass::finishedGitPush, this, &QtPass::processFinished);
158  connect(pass, &Pass::finishedInsert, this, &QtPass::finishedInsert);
159  connect(pass, &Pass::finishedRemove, this, &QtPass::passStoreChanged);
160  connect(pass, &Pass::finishedInit, this, &QtPass::passStoreChanged);
161  connect(pass, &Pass::finishedMove, this, &QtPass::passStoreChanged);
162  connect(pass, &Pass::finishedCopy, this, &QtPass::passStoreChanged);
163  connect(pass, &Pass::finishedGenerateGPGKeys, this,
164  &QtPass::onKeyGenerationComplete);
165 }
166 
170 void QtPass::mountWebDav() {
171 #ifdef Q_OS_WIN
172  char dst[20] = {0};
173  NETRESOURCEA netres;
174  memset(&netres, 0, sizeof(netres));
175  netres.dwType = RESOURCETYPE_DISK;
176  netres.lpLocalName = 0;
177  netres.lpRemoteName = QtPassSettings::getWebDavUrl().toUtf8().data();
178  DWORD size = sizeof(dst);
179  DWORD r = WNetUseConnectionA(
180  reinterpret_cast<HWND>(m_mainWindow->effectiveWinId()), &netres,
181  QtPassSettings::getWebDavPassword().toUtf8().constData(),
182  QtPassSettings::getWebDavUser().toUtf8().constData(),
183  CONNECT_TEMPORARY | CONNECT_INTERACTIVE | CONNECT_REDIRECT, dst, &size,
184  0);
185  if (r == NO_ERROR) {
187  } else {
188  char message[256] = {0};
189  FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, r, 0, message,
190  sizeof(message), 0);
191  m_mainWindow->flashText(tr("Failed to connect WebDAV:\n") + message +
192  " (0x" + QString::number(r, 16) + ")",
193  true);
194  }
195 #else
196  fusedav.start("fusedav -o nonempty -u \"" + QtPassSettings::getWebDavUser() +
197  "\" " + QtPassSettings::getWebDavUrl() + " \"" +
199  fusedav.waitForStarted();
200  if (fusedav.state() == QProcess::Running) {
201  QString pwd = QtPassSettings::getWebDavPassword();
202  bool ok = true;
203  if (pwd.isEmpty()) {
204  pwd = QInputDialog::getText(m_mainWindow, tr("QtPass WebDAV password"),
205  tr("Enter password to connect to WebDAV:"),
206  QLineEdit::Password, "", &ok);
207  }
208  if (ok && !pwd.isEmpty()) {
209  fusedav.write(pwd.toUtf8() + '\n');
210  fusedav.closeWriteChannel();
211  fusedav.waitForFinished(2000);
212  } else {
213  fusedav.terminate();
214  }
215  }
216  QString error = fusedav.readAllStandardError();
217  int prompt = error.indexOf("Password:");
218  if (prompt >= 0)
219  error.remove(0, prompt + 10);
220  if (fusedav.state() != QProcess::Running)
221  error = tr("fusedav exited unexpectedly\n") + error;
222  if (error.size() > 0) {
223  m_mainWindow->flashText(
224  tr("Failed to start fusedav to connect WebDAV:\n") + error, true);
225  }
226 #endif
227 }
228 
233 void QtPass::processError(QProcess::ProcessError error) {
234  QString errorString;
235  switch (error) {
236  case QProcess::FailedToStart:
237  errorString = tr("QProcess::FailedToStart");
238  break;
239  case QProcess::Crashed:
240  errorString = tr("QProcess::Crashed");
241  break;
242  case QProcess::Timedout:
243  errorString = tr("QProcess::Timedout");
244  break;
245  case QProcess::ReadError:
246  errorString = tr("QProcess::ReadError");
247  break;
248  case QProcess::WriteError:
249  errorString = tr("QProcess::WriteError");
250  break;
251  case QProcess::UnknownError:
252  errorString = tr("QProcess::UnknownError");
253  break;
254  }
255 
256  m_mainWindow->flashText(errorString, true);
257  m_mainWindow->setUiElementsEnabled(true);
258 }
259 
260 void QtPass::processErrorExit(int exitCode, const QString &p_error) {
261  if (!p_error.isEmpty()) {
262  QString output;
263  QString error = p_error;
264  error.replace(QRegExp("<"), "&lt;");
265  error.replace(QRegExp(">"), "&gt;");
266  error.replace(QRegExp(" "), "&nbsp;");
267  if (exitCode == 0) {
268  // https://github.com/IJHack/qtpass/issues/111
269  output = "<span style=\"color: darkgray;\">" + error + "</span><br />";
270  } else {
271  output = "<span style=\"color: red;\">" + error + "</span><br />";
272  }
273 
274  output.replace(
275  QRegExp("((?:https?|ftp|ssh|sftp|ftps|webdav|webdavs)://\\S+)"),
276  "<a href=\"\\1\">\\1</a>");
277  output.replace(QRegExp("\n"), "<br />");
278 
279  m_mainWindow->flashText(output, false, true);
280  }
281 
282  m_mainWindow->setUiElementsEnabled(true);
283 }
284 
292 void QtPass::processFinished(const QString &p_output, const QString &p_errout) {
293  showInTextBrowser(p_output);
294  // Sometimes there is error output even with 0 exit code, which is
295  // assumed in this function
296  processErrorExit(0, p_errout);
297 
298  m_mainWindow->setUiElementsEnabled(true);
299 }
300 
301 void QtPass::passStoreChanged(const QString &p_out, const QString &p_err) {
302  processFinished(p_out, p_err);
303  doGitPush();
304 }
305 
306 void QtPass::finishedInsert(const QString &p_output, const QString &p_errout) {
307  processFinished(p_output, p_errout);
308  doGitPush();
309  m_mainWindow->on_treeView_clicked(m_mainWindow->getCurrentTreeViewIndex());
310 }
311 
312 void QtPass::onKeyGenerationComplete(const QString &p_output,
313  const QString &p_errout) {
314  if (nullptr != m_mainWindow->getKeygenDialog()) {
315 #ifdef QT_DEBUG
316  qDebug() << "Keygen Done";
317 #endif
318 
319  m_mainWindow->cleanKeygenDialog();
320  // TODO(annejan) some sanity checking ?
321  }
322 
323  processFinished(p_output, p_errout);
324 }
325 
326 void QtPass::passShowHandlerFinished(QString output) {
327  showInTextBrowser(output);
328 }
329 
330 void QtPass::showInTextBrowser(QString output, QString prefix,
331  QString postfix) {
332  output.replace(QRegExp("<"), "&lt;");
333  output.replace(QRegExp(">"), "&gt;");
334  output.replace(QRegExp(" "), "&nbsp;");
335 
336  output.replace(
337  QRegExp("((?:https?|ftp|ssh|sftp|ftps|webdav|webdavs)://\\S+)"),
338  "<a href=\"\\1\">\\1</a>");
339  output.replace(QRegExp("\n"), "<br />");
340  output = prefix + output + postfix;
341 
342  m_mainWindow->flashText(output, false, true);
343 }
344 
345 void QtPass::doGitPush() {
347  m_mainWindow->onPush();
348 }
349 
350 void QtPass::setClippedText(const QString &password, const QString &p_output) {
352  !p_output.isEmpty()) {
353  clippedText = password;
355  copyTextToClipboard(password);
356  }
357 }
358 void QtPass::clearClippedText() { clippedText = ""; }
359 
361  clearClipboardTimer.setInterval(1000 * QtPassSettings::getAutoclearSeconds());
362 }
363 
368  QClipboard *clipboard = QApplication::clipboard();
369  bool cleared = false;
370  if (this->clippedText == clipboard->text(QClipboard::Selection)) {
371  clipboard->clear(QClipboard::Clipboard);
372  cleared = true;
373  }
374  if (this->clippedText == clipboard->text(QClipboard::Clipboard)) {
375  clipboard->clear(QClipboard::Clipboard);
376  cleared = true;
377  }
378  if (cleared) {
379  m_mainWindow->showStatusMessage(tr("Clipboard cleared"));
380  } else {
381  m_mainWindow->showStatusMessage(tr("Clipboard not cleared"));
382  }
383 
384  clippedText.clear();
385 }
386 
391 void QtPass::copyTextToClipboard(const QString &text) {
392  QClipboard *clip = QApplication::clipboard();
394  clip->setText(text, QClipboard::Clipboard);
395  } else {
396  clip->setText(text, QClipboard::Selection);
397  }
398 
399  clippedText = text;
400  m_mainWindow->showStatusMessage(tr("Copied to clipboard"));
402  clearClipboardTimer.start();
403  }
404 }
void finishedMove(const QString &, const QString &)
void passGitInitNeeded()
static void setAutoclearSeconds(const int &autoClearSeconds)
static int getAutoclearSeconds(const int &defaultValue=QVariant().toInt())
static void setUsePwgen(const bool &usePwgen)
const QModelIndex getCurrentTreeViewIndex()
Definition: mainwindow.cpp:197
void finishedCopy(const QString &, const QString &)
void generateGPGKeyPair(QString batch)
void on_treeView_clicked(const QModelIndex &index)
MainWindow::on_treeView_clicked read the selected password file.
Definition: mainwindow.cpp:319
void GenerateGPGKeys(QString batch)
Pass::GenerateGPGKeys internal gpg keypair generator . .
Definition: pass.cpp:114
void critical(QString, QString)
MainWindow::critical critical message popup wrapper.
void finishedGenerateGPGKeys(const QString &, const QString &)
void finishedGitPush(const QString &, const QString &)
void error(QProcess::ProcessError)
#define dbg()
Definition: debughelper.h:7
void passShowHandlerFinished(QString output)
static bool isUseSelection(const bool &defaultValue=QVariant().toBool())
static void setPassTemplate(const QString &passTemplate)
void finishedInsert(const QString &, const QString &)
QDialog * getKeygenDialog()
Definition: mainwindow.h:59
void restoreWindow()
Definition: mainwindow.cpp:465
static ImitatePass * getImitatePass()
void clearClippedText()
Definition: qtpass.cpp:358
static bool isUseWebDav(const bool &defaultValue=QVariant().toBool())
void passShowHandler(const QString &)
Definition: mainwindow.cpp:372
void setUiElementsEnabled(bool state)
MainWindow::setUiElementsEnabled enable or disable the relevant UI elements.
Definition: mainwindow.cpp:449
void finishedRemove(const QString &, const QString &)
static bool isUseAutoclear(const bool &defaultValue=QVariant().toBool())
virtual void GitInit()=0
void config()
MainWindow::config pops up the configuration screen and handles all inter-window communication.
Definition: mainwindow.cpp:230
Acts as an abstraction for pass or pass imitation.
Definition: pass.h:25
static QString getPassStore(const QString &defaultValue=QVariant().toString())
void setClipboardTimer()
Definition: qtpass.cpp:360
void finishedGitPull(const QString &, const QString &)
static void setAutoclearPanelSeconds(const int &autoClearPanelSeconds)
void critical(QString, QString)
void setClippedText(const QString &, const QString &p_output=QString())
Definition: qtpass.cpp:350
void flashText(const QString &text, const bool isError, const bool isHtml=false)
Definition: mainwindow.cpp:210
static QString getVersion(const QString &defaultValue=QVariant().toString())
void cleanKeygenDialog()
Definition: mainwindow.cpp:201
void startReencryptPath()
~QtPass()
QtPass::~QtPass destroy!
Definition: qtpass.cpp:40
static Pass * getPass()
void statusMsg(QString, int)
static QString getWebDavUser(const QString &defaultValue=QVariant().toString())
void finishedInit(const QString &, const QString &)
void endReencryptPath()
MainWindow::endReencryptPath re-enable ui elements.
void copyTextToClipboard(const QString &text)
MainWindow::copyTextToClipboard copies text to your clipboard.
Definition: qtpass.cpp:391
static QString getPassTemplate(const QString &defaultValue=QVariant().toString())
static Enums::clipBoardType getClipBoardType(const Enums::clipBoardType &defaultvalue=Enums::CLIPBOARD_NEVER)
static QString getWebDavUrl(const QString &defaultValue=QVariant().toString())
void onPush()
MainWindow::onPush do a git push.
Definition: mainwindow.cpp:289
static QString getPwgenExecutable(const QString &defaultValue=QVariant().toString())
void passOtpHandler(const QString &)
Definition: mainwindow.cpp:416
void startReencryptPath()
MainWindow::startReencryptPath disable ui elements and treeview.
static int getAutoclearPanelSeconds(const int &defaultValue=QVariant().toInt())
void finishedShow(const QString &)
void showStatusMessage(QString msg, int timeout=2000)
Displays message in status bar.
static QString getWebDavPassword(const QString &defaultValue=QVariant().toString())
static void initExecutables()
void finishedGitInit(const QString &, const QString &)
void startingExecuteWrapper()
void processErrorExit(int exitCode, const QString &err)
void finishedOtpGenerate(const QString &)
The MainWindow class does way too much, not only is it a switchboard, configuration handler and more...
Definition: mainwindow.h:39
void clearClipboard()
MainWindow::clearClipboard remove clipboard contents.
Definition: qtpass.cpp:367
static RealPass * getRealPass()
static QString findPasswordStore()
Util::findPasswordStore look for common .password-store folder location.
Definition: util.cpp:45
static bool checkConfig()
Util::checkConfig do we have prequisite settings?
Definition: util.cpp:131
static void setVersion(const QString &version)
void setMainWindow(MainWindow *mW)
Definition: qtpass.cpp:110
void executeWrapperStarted()
Definition: mainwindow.cpp:365
static void setPassStore(const QString &passStore)
QtPass()
Definition: qtpass.cpp:22
void endReencryptPath()
static bool isAutoPush(const bool &defaultValue=QVariant().toBool())