QtPass  1.3.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 #include <QDialog>
7 #include <QLabel>
8 #include <QPixmap>
9 #include <QVBoxLayout>
10 
11 #ifndef Q_OS_WIN
12 #include <QInputDialog>
13 #include <QLineEdit>
14 #include <utility>
15 #else
16 #define WIN32_LEAN_AND_MEAN /*_KILLING_MACHINE*/
17 #define WIN32_EXTRA_LEAN
18 #include <windows.h>
19 #include <winnetwk.h>
20 #undef DELETE
21 #endif
22 
23 #ifdef QT_DEBUG
24 #include "debughelper.h"
25 #endif
26 
28  : m_mainWindow(mainWindow), clippedText(QString()), freshStart(true) {
30  clearClipboardTimer.setSingleShot(true);
31  connect(&clearClipboardTimer, SIGNAL(timeout()), this,
32  SLOT(clearClipboard()));
33 
34  QObject::connect(qApp, &QApplication::aboutToQuit, this,
36 
37  setMainWindow();
38 }
39 
44 #ifdef Q_OS_WIN
46  WNetCancelConnection2A(QtPassSettings::getPassStore().toUtf8().constData(),
47  0, 1);
48 #else
49  if (fusedav.state() == QProcess::Running) {
50  fusedav.terminate();
51  fusedav.waitForFinished(2000);
52  }
53 #endif
54 }
55 
60 bool QtPass::init() {
63 
65 
66  QString version = QtPassSettings::getVersion();
67  // dbg()<< version;
68 
69  // Config updates
70  if (version.isEmpty()) {
71 #ifdef QT_DEBUG
72  dbg() << "assuming fresh install";
73 #endif
74 
79  if (!QtPassSettings::getPwgenExecutable().isEmpty())
81  else
83  QtPassSettings::setPassTemplate("login\nurl");
84  } else {
85  // QStringList ver = version.split(".");
86  // dbg()<< ver;
87  // if (ver[0] == "0" && ver[1] == "8") {
89  // }
90  if (QtPassSettings::getPassTemplate().isEmpty())
91  QtPassSettings::setPassTemplate("login\nurl");
92  }
93 
95 
96  if (Util::checkConfig()) {
97  m_mainWindow->config();
98  if (freshStart && Util::checkConfig())
99  return false;
100  }
101 
102  // TODO(annejan): this needs to be before we try to access the store,
103  // but it would be better to do it after the Window is shown,
104  // as the long delay it can cause is irritating otherwise.
106  mountWebDav();
107 
108  freshStart = false;
109  // startupPhase = false;
110  return true;
111 }
112 
113 void QtPass::setMainWindow(void) {
114  m_mainWindow->restoreWindow();
115 
116  fusedav.setParent(m_mainWindow);
117 
118  // TODO(bezet): this should be reconnected dynamically when pass changes
119  connectPassSignalHandlers(QtPassSettings::getRealPass());
120  connectPassSignalHandlers(QtPassSettings::getImitatePass());
121 
122  connect(m_mainWindow, &MainWindow::passShowHandlerFinished, this,
123  &QtPass::passShowHandlerFinished);
124 
125  // only for ipass
127  m_mainWindow, &MainWindow::startReencryptPath);
129  m_mainWindow, &MainWindow::endReencryptPath);
130 
131  connect(m_mainWindow, &MainWindow::passGitInitNeeded, [=]() {
132 #ifdef QT_DEBUG
133  dbg() << "Pass git init called";
134 #endif
136  });
137 
138  connect(
139  m_mainWindow, &MainWindow::generateGPGKeyPair, [=](const QString &batch) {
141  m_mainWindow->showStatusMessage(tr("Generating GPG key pair"), 60000);
142  });
143 }
144 
145 void QtPass::connectPassSignalHandlers(Pass *pass) {
146  connect(pass, &Pass::error, this, &QtPass::processError);
147  connect(pass, &Pass::processErrorExit, this, &QtPass::processErrorExit);
148  connect(pass, &Pass::critical, m_mainWindow, &MainWindow::critical);
149  connect(pass, &Pass::startingExecuteWrapper, m_mainWindow,
151  connect(pass, &Pass::statusMsg, m_mainWindow, &MainWindow::showStatusMessage);
152  connect(pass, &Pass::finishedShow, m_mainWindow,
154  connect(pass, &Pass::finishedOtpGenerate, m_mainWindow,
156 
157  connect(pass, &Pass::finishedGitInit, this, &QtPass::passStoreChanged);
158  connect(pass, &Pass::finishedGitPull, this, &QtPass::processFinished);
159  connect(pass, &Pass::finishedGitPush, this, &QtPass::processFinished);
160  connect(pass, &Pass::finishedInsert, this, &QtPass::finishedInsert);
161  connect(pass, &Pass::finishedRemove, this, &QtPass::passStoreChanged);
162  connect(pass, &Pass::finishedInit, this, &QtPass::passStoreChanged);
163  connect(pass, &Pass::finishedMove, this, &QtPass::passStoreChanged);
164  connect(pass, &Pass::finishedCopy, this, &QtPass::passStoreChanged);
165  connect(pass, &Pass::finishedGenerateGPGKeys, this,
166  &QtPass::onKeyGenerationComplete);
167 }
168 
172 void QtPass::mountWebDav() {
173 #ifdef Q_OS_WIN
174  char dst[20] = {0};
175  NETRESOURCEA netres;
176  memset(&netres, 0, sizeof(netres));
177  netres.dwType = RESOURCETYPE_DISK;
178  netres.lpLocalName = 0;
179  netres.lpRemoteName = QtPassSettings::getWebDavUrl().toUtf8().data();
180  DWORD size = sizeof(dst);
181  DWORD r = WNetUseConnectionA(
182  reinterpret_cast<HWND>(m_mainWindow->effectiveWinId()), &netres,
183  QtPassSettings::getWebDavPassword().toUtf8().constData(),
184  QtPassSettings::getWebDavUser().toUtf8().constData(),
185  CONNECT_TEMPORARY | CONNECT_INTERACTIVE | CONNECT_REDIRECT, dst, &size,
186  0);
187  if (r == NO_ERROR) {
189  } else {
190  char message[256] = {0};
191  FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, r, 0, message,
192  sizeof(message), 0);
193  m_mainWindow->flashText(tr("Failed to connect WebDAV:\n") + message +
194  " (0x" + QString::number(r, 16) + ")",
195  true);
196  }
197 #else
198  fusedav.start("fusedav -o nonempty -u \"" + QtPassSettings::getWebDavUser() +
199  "\" " + QtPassSettings::getWebDavUrl() + " \"" +
201  fusedav.waitForStarted();
202  if (fusedav.state() == QProcess::Running) {
203  QString pwd = QtPassSettings::getWebDavPassword();
204  bool ok = true;
205  if (pwd.isEmpty()) {
206  pwd = QInputDialog::getText(m_mainWindow, tr("QtPass WebDAV password"),
207  tr("Enter password to connect to WebDAV:"),
208  QLineEdit::Password, "", &ok);
209  }
210  if (ok && !pwd.isEmpty()) {
211  fusedav.write(pwd.toUtf8() + '\n');
212  fusedav.closeWriteChannel();
213  fusedav.waitForFinished(2000);
214  } else {
215  fusedav.terminate();
216  }
217  }
218  QString error = fusedav.readAllStandardError();
219  int prompt = error.indexOf("Password:");
220  if (prompt >= 0)
221  error.remove(0, prompt + 10);
222  if (fusedav.state() != QProcess::Running)
223  error = tr("fusedav exited unexpectedly\n") + error;
224  if (error.size() > 0) {
225  m_mainWindow->flashText(
226  tr("Failed to start fusedav to connect WebDAV:\n") + error, true);
227  }
228 #endif
229 }
230 
235 void QtPass::processError(QProcess::ProcessError error) {
236  QString errorString;
237  switch (error) {
238  case QProcess::FailedToStart:
239  errorString = tr("QProcess::FailedToStart");
240  break;
241  case QProcess::Crashed:
242  errorString = tr("QProcess::Crashed");
243  break;
244  case QProcess::Timedout:
245  errorString = tr("QProcess::Timedout");
246  break;
247  case QProcess::ReadError:
248  errorString = tr("QProcess::ReadError");
249  break;
250  case QProcess::WriteError:
251  errorString = tr("QProcess::WriteError");
252  break;
253  case QProcess::UnknownError:
254  errorString = tr("QProcess::UnknownError");
255  break;
256  }
257 
258  m_mainWindow->flashText(errorString, true);
259  m_mainWindow->setUiElementsEnabled(true);
260 }
261 
262 void QtPass::processErrorExit(int exitCode, const QString &p_error) {
263  if (!p_error.isEmpty()) {
264  QString output;
265  QString error = p_error;
266  error.replace(QRegExp("<"), "&lt;");
267  error.replace(QRegExp(">"), "&gt;");
268  error.replace(QRegExp(" "), "&nbsp;");
269  if (exitCode == 0) {
270  // https://github.com/IJHack/qtpass/issues/111
271  output = "<span style=\"color: darkgray;\">" + error + "</span><br />";
272  } else {
273  output = "<span style=\"color: red;\">" + error + "</span><br />";
274  }
275 
276  output.replace(
277  QRegExp("((?:https?|ftp|ssh|sftp|ftps|webdav|webdavs)://\\S+)"),
278  R"(<a href="\1">\1</a>)");
279  output.replace(QRegExp("\n"), "<br />");
280 
281  m_mainWindow->flashText(output, false, true);
282  }
283 
284  m_mainWindow->setUiElementsEnabled(true);
285 }
286 
294 void QtPass::processFinished(const QString &p_output, const QString &p_errout) {
295  showInTextBrowser(p_output);
296  // Sometimes there is error output even with 0 exit code, which is
297  // assumed in this function
298  processErrorExit(0, p_errout);
299 
300  m_mainWindow->setUiElementsEnabled(true);
301 }
302 
303 void QtPass::passStoreChanged(const QString &p_out, const QString &p_err) {
304  processFinished(p_out, p_err);
305  doGitPush();
306 }
307 
308 void QtPass::finishedInsert(const QString &p_output, const QString &p_errout) {
309  processFinished(p_output, p_errout);
310  doGitPush();
311  m_mainWindow->on_treeView_clicked(m_mainWindow->getCurrentTreeViewIndex());
312 }
313 
314 void QtPass::onKeyGenerationComplete(const QString &p_output,
315  const QString &p_errout) {
316  if (nullptr != m_mainWindow->getKeygenDialog()) {
317 #ifdef QT_DEBUG
318  qDebug() << "Keygen Done";
319 #endif
320 
321  m_mainWindow->cleanKeygenDialog();
322  // TODO(annejan) some sanity checking ?
323  }
324 
325  processFinished(p_output, p_errout);
326 }
327 
328 void QtPass::passShowHandlerFinished(QString output) {
329  showInTextBrowser(std::move(output));
330 }
331 
332 void QtPass::showInTextBrowser(QString output, QString prefix,
333  QString postfix) {
334  output.replace(QRegExp("<"), "&lt;");
335  output.replace(QRegExp(">"), "&gt;");
336  output.replace(QRegExp(" "), "&nbsp;");
337 
338  output.replace(
339  QRegExp("((?:https?|ftp|ssh|sftp|ftps|webdav|webdavs)://\\S+)"),
340  R"(<a href="\1">\1</a>)");
341  output.replace(QRegExp("\n"), "<br />");
342  output = prefix + output + postfix;
343 
344  m_mainWindow->flashText(output, false, true);
345 }
346 
347 void QtPass::doGitPush() {
349  m_mainWindow->onPush();
350 }
351 
352 void QtPass::setClippedText(const QString &password, const QString &p_output) {
354  !p_output.isEmpty()) {
355  clippedText = password;
357  copyTextToClipboard(password);
358  }
359 }
360 void QtPass::clearClippedText() { clippedText = ""; }
361 
363  clearClipboardTimer.setInterval(1000 * QtPassSettings::getAutoclearSeconds());
364 }
365 
370  QClipboard *clipboard = QApplication::clipboard();
371  bool cleared = false;
372  if (this->clippedText == clipboard->text(QClipboard::Selection)) {
373  clipboard->clear(QClipboard::Clipboard);
374  cleared = true;
375  }
376  if (this->clippedText == clipboard->text(QClipboard::Clipboard)) {
377  clipboard->clear(QClipboard::Clipboard);
378  cleared = true;
379  }
380  if (cleared) {
381  m_mainWindow->showStatusMessage(tr("Clipboard cleared"));
382  } else {
383  m_mainWindow->showStatusMessage(tr("Clipboard not cleared"));
384  }
385 
386  clippedText.clear();
387 }
388 
393 void QtPass::copyTextToClipboard(const QString &text) {
394  QClipboard *clip = QApplication::clipboard();
396  clip->setText(text, QClipboard::Clipboard);
397  } else {
398  clip->setText(text, QClipboard::Selection);
399  }
400 
401  clippedText = text;
402  m_mainWindow->showStatusMessage(tr("Copied to clipboard"));
404  clearClipboardTimer.start();
405  }
406 }
407 
412 void QtPass::showTextAsQRCode(const QString &text) {
413  QProcess qrencode;
414  qrencode.start(QtPassSettings::getQrencodeExecutable("/usr/bin/qrencode"),
415  QStringList() << "-o-"
416  << "-tPNG");
417  qrencode.write(text.toUtf8());
418  qrencode.closeWriteChannel();
419  qrencode.waitForFinished();
420  QByteArray output(qrencode.readAllStandardOutput());
421 
422  if (qrencode.exitStatus() || qrencode.exitCode()) {
423  QString error(qrencode.readAllStandardError());
424  m_mainWindow->showStatusMessage(error);
425  } else {
426  QPixmap image;
427  image.loadFromData(output, "PNG");
428 
429  QDialog *popup = new QDialog(0, Qt::Popup | Qt::FramelessWindowHint);
430  QVBoxLayout *layout = new QVBoxLayout;
431  QLabel *popupLabel = new QLabel();
432  layout->addWidget(popupLabel);
433  popupLabel->setPixmap(image);
434  popupLabel->setScaledContents(true);
435  popupLabel->show();
436  popup->setLayout(layout);
437  popup->move(QCursor::pos());
438  popup->exec();
439  }
440 }
QtPassSettings::getQrencodeExecutable
static QString getQrencodeExecutable(const QString &defaultValue=QVariant().toString())
Definition: qtpasssettings.cpp:408
Pass::processErrorExit
void processErrorExit(int exitCode, const QString &err)
QtPassSettings::setAutoclearSeconds
static void setAutoclearSeconds(const int &autoClearSeconds)
Definition: qtpasssettings.cpp:201
QtPassSettings::getPwgenExecutable
static QString getPwgenExecutable(const QString &defaultValue=QVariant().toString())
Definition: qtpasssettings.cpp:323
Pass::finishedGenerateGPGKeys
void finishedGenerateGPGKeys(const QString &, const QString &)
Pass::finishedGitPush
void finishedGitPush(const QString &, const QString &)
MainWindow::getCurrentTreeViewIndex
const QModelIndex getCurrentTreeViewIndex()
Definition: mainwindow.cpp:199
MainWindow::passShowHandler
void passShowHandler(const QString &)
Definition: mainwindow.cpp:370
QtPassSettings::getVersion
static QString getVersion(const QString &defaultValue=QVariant().toString())
Definition: qtpasssettings.cpp:99
QtPassSettings::getPassTemplate
static QString getPassTemplate(const QString &defaultValue=QVariant().toString())
Definition: qtpasssettings.cpp:529
Pass::GenerateGPGKeys
void GenerateGPGKeys(QString batch)
Pass::GenerateGPGKeys internal gpg keypair generator . .
Definition: pass.cpp:114
Pass::finishedInsert
void finishedInsert(const QString &, const QString &)
QtPass::clearClipboard
void clearClipboard()
MainWindow::clearClipboard remove clipboard contents.
Definition: qtpass.cpp:369
MainWindow::restoreWindow
void restoreWindow()
Definition: mainwindow.cpp:465
QtPass::clearClippedText
void clearClippedText()
Definition: qtpass.cpp:360
MainWindow::on_treeView_clicked
void on_treeView_clicked(const QModelIndex &index)
MainWindow::on_treeView_clicked read the selected password file.
Definition: mainwindow.cpp:317
QtPassSettings::initExecutables
static void initExecutables()
Definition: qtpasssettings.cpp:279
MainWindow::critical
void critical(QString, QString)
MainWindow::critical critical message popup wrapper.
Definition: mainwindow.cpp:1183
QtPassSettings::getImitatePass
static ImitatePass * getImitatePass()
Definition: qtpasssettings.cpp:562
debughelper.h
QtPass::showTextAsQRCode
void showTextAsQRCode(const QString &text)
displays the text as qrcode
Definition: qtpass.cpp:412
MainWindow::generateGPGKeyPair
void generateGPGKeyPair(QString batch)
Pass::critical
void critical(QString, QString)
QtPassSettings::setUsePwgen
static void setUsePwgen(const bool &usePwgen)
Definition: qtpasssettings.cpp:423
QtPass::setClipboardTimer
void setClipboardTimer()
Definition: qtpass.cpp:362
QtPassSettings::setVersion
static void setVersion(const QString &version)
Definition: qtpasssettings.cpp:104
QtPassSettings::isAutoPush
static bool isAutoPush(const bool &defaultValue=QVariant().toBool())
Definition: qtpasssettings.cpp:520
QtPass::~QtPass
~QtPass()
QtPass::~QtPass destroy!
Definition: qtpass.cpp:43
QtPassSettings::getWebDavPassword
static QString getWebDavPassword(const QString &defaultValue=QVariant().toString())
Definition: qtpasssettings.cpp:365
MainWindow::config
void config()
MainWindow::config pops up the configuration screen and handles all inter-window communication.
Definition: mainwindow.cpp:228
MainWindow::setUiElementsEnabled
void setUiElementsEnabled(bool state)
MainWindow::setUiElementsEnabled enable or disable the relevant UI elements.
Definition: mainwindow.cpp:449
Pass::GitInit
virtual void GitInit()=0
Enums::CLIPBOARD_ALWAYS
@ CLIPBOARD_ALWAYS
Definition: enums.h:12
MainWindow::startReencryptPath
void startReencryptPath()
MainWindow::startReencryptPath disable ui elements and treeview.
Definition: mainwindow.cpp:1142
QtPass::QtPass
QtPass(MainWindow *mainWindow)
Definition: qtpass.cpp:27
QtPassSettings::getPass
static Pass * getPass()
Definition: qtpasssettings.cpp:87
ImitatePass::endReencryptPath
void endReencryptPath()
Pass::finishedRemove
void finishedRemove(const QString &, const QString &)
Pass::finishedGitPull
void finishedGitPull(const QString &, const QString &)
QtPassSettings::getWebDavUrl
static QString getWebDavUrl(const QString &defaultValue=QVariant().toString())
Definition: qtpasssettings.cpp:347
MainWindow::getKeygenDialog
QDialog * getKeygenDialog()
Definition: mainwindow.h:58
MainWindow::passShowHandlerFinished
void passShowHandlerFinished(QString output)
MainWindow::passOtpHandler
void passOtpHandler(const QString &)
Definition: mainwindow.cpp:416
QtPass::init
bool init()
QtPass::init make sure we are ready to go as soon as possible.
Definition: qtpass.cpp:60
MainWindow::onPush
void onPush()
MainWindow::onPush do a git push.
Definition: mainwindow.cpp:287
MainWindow::showStatusMessage
void showStatusMessage(QString msg, int timeout=2000)
Displays message in status bar.
Definition: mainwindow.cpp:1135
MainWindow::flashText
void flashText(const QString &text, const bool isError, const bool isHtml=false)
Definition: mainwindow.cpp:208
QtPass::copyTextToClipboard
void copyTextToClipboard(const QString &text)
MainWindow::copyTextToClipboard copies text to your clipboard.
Definition: qtpass.cpp:393
QtPassSettings::getClipBoardType
static Enums::clipBoardType getClipBoardType(const Enums::clipBoardType &defaultvalue=Enums::CLIPBOARD_NEVER)
Definition: qtpasssettings.cpp:171
Pass::finishedGitInit
void finishedGitInit(const QString &, const QString &)
MainWindow::cleanKeygenDialog
void cleanKeygenDialog()
Definition: mainwindow.cpp:203
QtPassSettings::getAutoclearPanelSeconds
static int getAutoclearPanelSeconds(const int &defaultValue=QVariant().toInt())
Definition: qtpasssettings.cpp:216
Util::checkConfig
static bool checkConfig()
Util::checkConfig do we have prequisite settings?
Definition: util.cpp:142
QtPass::setClippedText
void setClippedText(const QString &, const QString &p_output=QString())
Definition: qtpass.cpp:352
QtPassSettings::setPassStore
static void setPassStore(const QString &passStore)
Definition: qtpasssettings.cpp:275
QtPassSettings::isUseSelection
static bool isUseSelection(const bool &defaultValue=QVariant().toBool())
Definition: qtpasssettings.cpp:178
QtPassSettings::setPassTemplate
static void setPassTemplate(const QString &passTemplate)
Definition: qtpasssettings.cpp:534
QtPassSettings::isUseAutoclear
static bool isUseAutoclear(const bool &defaultValue=QVariant().toBool())
Definition: qtpasssettings.cpp:187
MainWindow::executeWrapperStarted
void executeWrapperStarted()
Definition: mainwindow.cpp:363
Pass::statusMsg
void statusMsg(QString, int)
Pass::finishedInit
void finishedInit(const QString &, const QString &)
Enums::CLIPBOARD_NEVER
@ CLIPBOARD_NEVER
Definition: enums.h:11
Pass::startingExecuteWrapper
void startingExecuteWrapper()
QtPassSettings::getAutoclearSeconds
static int getAutoclearSeconds(const int &defaultValue=QVariant().toInt())
Definition: qtpasssettings.cpp:196
Pass::finishedOtpGenerate
void finishedOtpGenerate(const QString &)
qtpass.h
dbg
#define dbg()
Definition: debughelper.h:7
Util::findPasswordStore
static QString findPasswordStore()
Util::findPasswordStore look for common .password-store folder location.
Definition: util.cpp:56
Pass::finishedMove
void finishedMove(const QString &, const QString &)
MainWindow::passGitInitNeeded
void passGitInitNeeded()
Pass
Acts as an abstraction for pass or pass imitation.
Definition: pass.h:25
ImitatePass::startReencryptPath
void startReencryptPath()
Pass::finishedCopy
void finishedCopy(const QString &, const QString &)
MainWindow
The MainWindow class does way too much, not only is it a switchboard, configuration handler and more,...
Definition: mainwindow.h:39
MainWindow::endReencryptPath
void endReencryptPath()
MainWindow::endReencryptPath re-enable ui elements.
Definition: mainwindow.cpp:1150
QtPassSettings::getPassStore
static QString getPassStore(const QString &defaultValue=QVariant().toString())
Definition: qtpasssettings.cpp:254
Pass::finishedShow
void finishedShow(const QString &)
QtPassSettings::isUseWebDav
static bool isUseWebDav(const bool &defaultValue=QVariant().toBool())
Definition: qtpasssettings.cpp:338
mainwindow.h
QtPassSettings::getRealPass
static RealPass * getRealPass()
Definition: qtpasssettings.cpp:557
qtpasssettings.h
QtPassSettings::getWebDavUser
static QString getWebDavUser(const QString &defaultValue=QVariant().toString())
Definition: qtpasssettings.cpp:356
Pass::error
void error(QProcess::ProcessError)
QtPassSettings::setAutoclearPanelSeconds
static void setAutoclearPanelSeconds(const int &autoClearPanelSeconds)
Definition: qtpasssettings.cpp:221