QtPass 1.5.1
Multi-platform GUI for pass, the standard unix password manager.
Loading...
Searching...
No Matches
qtpass.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2016 Anne Jan Brouwer
2// SPDX-License-Identifier: GPL-3.0-or-later
3#include "qtpass.h"
4#include "mainwindow.h"
5#include "qtpasssettings.h"
6#include "util.h"
7#include <QApplication>
8#include <QClipboard>
9#include <QDialog>
10#include <QLabel>
11#include <QPixmap>
12#include <QVBoxLayout>
13
14#ifndef Q_OS_WIN
15#include <QInputDialog>
16#include <QLineEdit>
17#include <utility>
18#else
19#define WIN32_LEAN_AND_MEAN /*_KILLING_MACHINE*/
20#define WIN32_EXTRA_LEAN
21#include <windows.h>
22#include <winnetwk.h>
23#undef DELETE
24#endif
25
26#ifdef QT_DEBUG
27#include "debughelper.h"
28#endif
29
31 : m_mainWindow(mainWindow), freshStart(true) {
33 clearClipboardTimer.setSingleShot(true);
34 connect(&clearClipboardTimer, &QTimer::timeout, this,
36
37#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
38#pragma GCC diagnostic push
39#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
40#endif
41 QObject::connect(qApp, &QApplication::aboutToQuit, this,
43#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
44#pragma GCC diagnostic pop
45#endif
46
47 setMainWindow();
48}
49
54#ifdef Q_OS_WIN
56 WNetCancelConnection2A(QtPassSettings::getPassStore().toUtf8().constData(),
57 0, 1);
58#else
59 if (fusedav.state() == QProcess::Running) {
60 fusedav.terminate();
61 fusedav.waitForFinished(2000);
62 }
63#endif
64}
65
70auto QtPass::init() -> bool {
73
75
76 QString version = QtPassSettings::getVersion();
77 // dbg()<< version;
78
79 // Config updates
80 if (version.isEmpty()) {
81#ifdef QT_DEBUG
82 dbg() << "assuming fresh install";
83#endif
84
87 }
90 }
91 if (!QtPassSettings::getPwgenExecutable().isEmpty()) {
93 } else {
95 }
97 } else {
98 // QStringList ver = version.split(".");
99 // dbg()<< ver;
100 // if (ver[0] == "0" && ver[1] == "8") {
102 // }
103 if (QtPassSettings::getPassTemplate().isEmpty()) {
105 }
106 }
107
109
110 if (Util::checkConfig()) {
111 m_mainWindow->config();
112 if (freshStart && Util::checkConfig()) {
113 return false;
114 }
115 }
116
117 // TODO(annejan): this needs to be before we try to access the store,
118 // but it would be better to do it after the Window is shown,
119 // as the long delay it can cause is irritating otherwise.
121 mountWebDav();
122 }
123
124 freshStart = false;
125 // startupPhase = false;
126 return true;
127}
128
129void QtPass::setMainWindow() {
130 m_mainWindow->restoreWindow();
131
132 fusedav.setParent(m_mainWindow);
133
134 // TODO(bezet): this should be reconnected dynamically when pass changes
135 connectPassSignalHandlers(QtPassSettings::getRealPass());
136 connectPassSignalHandlers(QtPassSettings::getImitatePass());
137
138 connect(m_mainWindow, &MainWindow::passShowHandlerFinished, this,
139 &QtPass::passShowHandlerFinished);
140
141 // only for ipass
143 m_mainWindow, &MainWindow::startReencryptPath);
145 m_mainWindow, &MainWindow::endReencryptPath);
146
147 connect(m_mainWindow, &MainWindow::passGitInitNeeded, [=]() -> void {
148#ifdef QT_DEBUG
149 dbg() << "Pass git init called";
150#endif
151 QtPassSettings::getPass()->GitInit();
152 });
153
154 connect(m_mainWindow, &MainWindow::generateGPGKeyPair, m_mainWindow,
155 [=](const QString &batch) -> void {
156 QtPassSettings::getPass()->GenerateGPGKeys(batch);
157 m_mainWindow->showStatusMessage(tr("Generating GPG key pair"),
158 60000);
159 });
160}
161
162void QtPass::connectPassSignalHandlers(Pass *pass) {
163 connect(pass, &Pass::error, this, &QtPass::processError);
164 connect(pass, &Pass::processErrorExit, this, &QtPass::processErrorExit);
165 connect(pass, &Pass::critical, m_mainWindow, &MainWindow::critical);
166 connect(pass, &Pass::startingExecuteWrapper, m_mainWindow,
168 connect(pass, &Pass::statusMsg, m_mainWindow, &MainWindow::showStatusMessage);
169 connect(pass, &Pass::finishedShow, m_mainWindow,
171 connect(pass, &Pass::finishedOtpGenerate, m_mainWindow,
173
174 connect(pass, &Pass::finishedGitInit, this, &QtPass::passStoreChanged);
175 connect(pass, &Pass::finishedGitPull, this, &QtPass::processFinished);
176 connect(pass, &Pass::finishedGitPush, this, &QtPass::processFinished);
177 connect(pass, &Pass::finishedInsert, this, &QtPass::finishedInsert);
178 connect(pass, &Pass::finishedRemove, this, &QtPass::passStoreChanged);
179 connect(pass, &Pass::finishedInit, this, &QtPass::passStoreChanged);
180 connect(pass, &Pass::finishedMove, this, &QtPass::passStoreChanged);
181 connect(pass, &Pass::finishedCopy, this, &QtPass::passStoreChanged);
182 connect(pass, &Pass::finishedGenerateGPGKeys, this,
183 &QtPass::onKeyGenerationComplete);
184}
185
189void QtPass::mountWebDav() {
190#ifdef Q_OS_WIN
191 char dst[20] = {0};
192 NETRESOURCEA netres;
193 memset(&netres, 0, sizeof(netres));
194 netres.dwType = RESOURCETYPE_DISK;
195 netres.lpLocalName = 0;
196 netres.lpRemoteName = QtPassSettings::getWebDavUrl().toUtf8().data();
197 DWORD size = sizeof(dst);
198 DWORD r = WNetUseConnectionA(
199 reinterpret_cast<HWND>(m_mainWindow->effectiveWinId()), &netres,
200 QtPassSettings::getWebDavPassword().toUtf8().constData(),
201 QtPassSettings::getWebDavUser().toUtf8().constData(),
202 CONNECT_TEMPORARY | CONNECT_INTERACTIVE | CONNECT_REDIRECT, dst, &size,
203 0);
204 if (r == NO_ERROR) {
206 } else {
207 char message[256] = {0};
208 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, r, 0, message,
209 sizeof(message), 0);
210 m_mainWindow->flashText(tr("Failed to connect WebDAV:\n") + message +
211 " (0x" + QString::number(r, 16) + ")",
212 true);
213 }
214#else
215 fusedav.start("fusedav", QStringList()
216 << "-o"
217 << "nonempty"
218 << "-u"
219 << "\"" + QtPassSettings::getWebDavUser() + "\""
221 << "\"" + QtPassSettings::getPassStore() + "\"");
222 fusedav.waitForStarted();
223 if (fusedav.state() == QProcess::Running) {
224 QString pwd = QtPassSettings::getWebDavPassword();
225 bool ok = true;
226 if (pwd.isEmpty()) {
227 pwd = QInputDialog::getText(m_mainWindow, tr("QtPass WebDAV password"),
228 tr("Enter password to connect to WebDAV:"),
229 QLineEdit::Password, "", &ok);
230 }
231 if (ok && !pwd.isEmpty()) {
232 fusedav.write(pwd.toUtf8() + '\n');
233 fusedav.closeWriteChannel();
234 fusedav.waitForFinished(2000);
235 } else {
236 fusedav.terminate();
237 }
238 }
239 QString error = fusedav.readAllStandardError();
240 int prompt = error.indexOf("Password:");
241 if (prompt >= 0) {
242 error.remove(0, prompt + 10);
243 }
244 if (fusedav.state() != QProcess::Running) {
245 error = tr("fusedav exited unexpectedly\n") + error;
246 }
247 if (error.size() > 0) {
248 m_mainWindow->flashText(
249 tr("Failed to start fusedav to connect WebDAV:\n") + error, true);
250 }
251#endif
252}
253
258void QtPass::processError(QProcess::ProcessError error) {
259 QString errorString;
260 switch (error) {
261 case QProcess::FailedToStart:
262 errorString = tr("QProcess::FailedToStart");
263 break;
264 case QProcess::Crashed:
265 errorString = tr("QProcess::Crashed");
266 break;
267 case QProcess::Timedout:
268 errorString = tr("QProcess::Timedout");
269 break;
270 case QProcess::ReadError:
271 errorString = tr("QProcess::ReadError");
272 break;
273 case QProcess::WriteError:
274 errorString = tr("QProcess::WriteError");
275 break;
276 case QProcess::UnknownError:
277 errorString = tr("QProcess::UnknownError");
278 break;
279 }
280
281 m_mainWindow->flashText(errorString, true);
282 m_mainWindow->setUiElementsEnabled(true);
283}
284
285void QtPass::processErrorExit(int exitCode, const QString &p_error) {
286 if (!p_error.isEmpty()) {
287 QString output;
288 QString error = p_error.toHtmlEscaped();
289 if (exitCode == 0) {
290 // https://github.com/IJHack/qtpass/issues/111
291 output = "<span style=\"color: darkgray;\">" + error + "</span><br />";
292 } else {
293 output = "<span style=\"color: red;\">" + error + "</span><br />";
294 }
295
296 output.replace(Util::protocolRegex(), R"(<a href="\1">\1</a>)");
297 output.replace(QStringLiteral("\n"), "<br />");
298
299 m_mainWindow->flashText(output, false, true);
300 }
301
302 m_mainWindow->setUiElementsEnabled(true);
303}
304
312void QtPass::processFinished(const QString &p_output, const QString &p_errout) {
313 showInTextBrowser(p_output);
314 // Sometimes there is error output even with 0 exit code, which is
315 // assumed in this function
316 processErrorExit(0, p_errout);
317
318 m_mainWindow->setUiElementsEnabled(true);
319}
320
321void QtPass::passStoreChanged(const QString &p_out, const QString &p_err) {
322 processFinished(p_out, p_err);
323 doGitPush();
324}
325
326void QtPass::finishedInsert(const QString &p_output, const QString &p_errout) {
327 processFinished(p_output, p_errout);
328 doGitPush();
329 m_mainWindow->on_treeView_clicked(m_mainWindow->getCurrentTreeViewIndex());
330}
331
332void QtPass::onKeyGenerationComplete(const QString &p_output,
333 const QString &p_errout) {
334 if (nullptr != m_mainWindow->getKeygenDialog()) {
335#ifdef QT_DEBUG
336 qDebug() << "Keygen Done";
337#endif
338
339 m_mainWindow->cleanKeygenDialog();
340 // TODO(annejan): some sanity checking ?
341 }
342
343 processFinished(p_output, p_errout);
344}
345
346void QtPass::passShowHandlerFinished(QString output) {
347 showInTextBrowser(std::move(output));
348}
349
350void QtPass::showInTextBrowser(QString output, const QString &prefix,
351 const QString &postfix) {
352 output = output.toHtmlEscaped();
353
354 output.replace(Util::protocolRegex(), R"(<a href="\1">\1</a>)");
355 output.replace(QStringLiteral("\n"), "<br />");
356 output = prefix + output + postfix;
357
358 m_mainWindow->flashText(output, false, true);
359}
360
361void QtPass::doGitPush() {
363 m_mainWindow->onPush();
364 }
365}
366
367void QtPass::setClippedText(const QString &password, const QString &p_output) {
369 !p_output.isEmpty()) {
370 clippedText = password;
372 copyTextToClipboard(password);
373 }
374 }
375}
376void QtPass::clearClippedText() { clippedText = ""; }
377
379 clearClipboardTimer.setInterval(MS_PER_SECOND *
381}
382
387 QClipboard *clipboard = QApplication::clipboard();
388 bool cleared = false;
389 if (this->clippedText == clipboard->text(QClipboard::Selection)) {
390 clipboard->clear(QClipboard::Selection);
391 clipboard->setText(QString(""), QClipboard::Selection);
392 cleared = true;
393 }
394 if (this->clippedText == clipboard->text(QClipboard::Clipboard)) {
395 clipboard->clear(QClipboard::Clipboard);
396 cleared = true;
397 }
398 if (cleared) {
399 m_mainWindow->showStatusMessage(tr("Clipboard cleared"));
400 } else {
401 m_mainWindow->showStatusMessage(tr("Clipboard not cleared"));
402 }
403
404 clippedText.clear();
405}
406
411void QtPass::copyTextToClipboard(const QString &text) {
412 QClipboard *clip = QApplication::clipboard();
414 clip->setText(text, QClipboard::Clipboard);
415 } else {
416 clip->setText(text, QClipboard::Selection);
417 }
418
419 clippedText = text;
420 m_mainWindow->showStatusMessage(tr("Copied to clipboard"));
422 clearClipboardTimer.start();
423 }
424}
425
430void QtPass::showTextAsQRCode(const QString &text) {
431 QProcess qrencode;
432 qrencode.start(QtPassSettings::getQrencodeExecutable("/usr/bin/qrencode"),
433 QStringList() << "-o-"
434 << "-tPNG");
435 qrencode.write(text.toUtf8());
436 qrencode.closeWriteChannel();
437 qrencode.waitForFinished();
438 QByteArray output(qrencode.readAllStandardOutput());
439
440 if (qrencode.exitStatus() || qrencode.exitCode()) {
441 QString error(qrencode.readAllStandardError());
442 m_mainWindow->showStatusMessage(error);
443 } else {
444 QPixmap image;
445 image.loadFromData(output, "PNG");
446
447 auto *popup = new QDialog(nullptr, Qt::Popup | Qt::FramelessWindowHint);
448 auto *layout = new QVBoxLayout;
449 auto *popupLabel = new QLabel();
450 layout->addWidget(popupLabel);
451 popupLabel->setPixmap(image);
452 popupLabel->setScaledContents(true);
453 popupLabel->show();
454 popup->setLayout(layout);
455 popup->move(QCursor::pos());
456 popup->exec();
457 }
458}
void endReencryptPath()
void startReencryptPath()
The MainWindow class does way too much, not only is it a switchboard, configuration handler and more,...
Definition mainwindow.h:39
void startReencryptPath()
MainWindow::startReencryptPath disable ui elements and treeview.
void passShowHandler(const QString &)
void endReencryptPath()
MainWindow::endReencryptPath re-enable ui elements.
void executeWrapperStarted()
void passGitInitNeeded()
void critical(const QString &, const QString &)
MainWindow::critical critical message popup wrapper.
void onPush()
MainWindow::onPush do a git push.
void showStatusMessage(const QString &msg, int timeout=2000)
Displays message in status bar.
void passOtpHandler(const QString &)
void passShowHandlerFinished(const QString &output)
void generateGPGKeyPair(const QString &batch)
void restoreWindow()
void flashText(const QString &text, const bool isError, const bool isHtml=false)
void setUiElementsEnabled(bool state)
MainWindow::setUiElementsEnabled enable or disable the relevant UI elements.
auto getCurrentTreeViewIndex() -> QModelIndex
void cleanKeygenDialog()
void on_treeView_clicked(const QModelIndex &index)
MainWindow::on_treeView_clicked read the selected password file.
auto getKeygenDialog() -> QDialog *
Definition mainwindow.h:58
Acts as an abstraction for pass or pass imitation.
Definition pass.h:20
void startingExecuteWrapper()
void critical(const QString &, const QString &)
void finishedCopy(const QString &, const QString &)
void finishedShow(const QString &)
void finishedRemove(const QString &, const QString &)
void finishedMove(const QString &, const QString &)
void statusMsg(const QString &, int)
void finishedGitInit(const QString &, const QString &)
void finishedInsert(const QString &, const QString &)
void finishedInit(const QString &, const QString &)
void finishedOtpGenerate(const QString &)
void processErrorExit(int exitCode, const QString &err)
void finishedGitPull(const QString &, const QString &)
void finishedGitPush(const QString &, const QString &)
void error(QProcess::ProcessError)
void finishedGenerateGPGKeys(const QString &, const QString &)
void clearClippedText()
Definition qtpass.cpp:376
void setClipboardTimer()
Definition qtpass.cpp:378
void clearClipboard()
MainWindow::clearClipboard remove clipboard contents.
Definition qtpass.cpp:386
~QtPass()
QtPass::~QtPass destroy!
Definition qtpass.cpp:53
void setClippedText(const QString &, const QString &p_output=QString())
Definition qtpass.cpp:367
QtPass(MainWindow *mainWindow)
Definition qtpass.cpp:30
void copyTextToClipboard(const QString &text)
MainWindow::copyTextToClipboard copies text to your clipboard.
Definition qtpass.cpp:411
auto init() -> bool
QtPass::init make sure we are ready to go as soon as possible.
Definition qtpass.cpp:70
void showTextAsQRCode(const QString &text)
displays the text as qrcode
Definition qtpass.cpp:430
static auto isUseSelection(const bool &defaultValue=QVariant().toBool()) -> bool
static auto isUseAutoclear(const bool &defaultValue=QVariant().toBool()) -> bool
static void setAutoclearPanelSeconds(const int &autoClearPanelSeconds)
static auto getWebDavPassword(const QString &defaultValue=QVariant().toString()) -> QString
static auto getPwgenExecutable(const QString &defaultValue=QVariant().toString()) -> QString
static auto getWebDavUser(const QString &defaultValue=QVariant().toString()) -> QString
static void setPassStore(const QString &passStore)
static auto getPass() -> Pass *
static void setUsePwgen(const bool &usePwgen)
static auto getPassStore(const QString &defaultValue=QVariant().toString()) -> QString
static auto getVersion(const QString &defaultValue=QVariant().toString()) -> QString
static void initExecutables()
static void setPassTemplate(const QString &passTemplate)
static auto getQrencodeExecutable(const QString &defaultValue=QVariant().toString()) -> QString
static void setVersion(const QString &version)
static auto getRealPass() -> RealPass *
static auto getPassTemplate(const QString &defaultValue=QVariant().toString()) -> QString
static auto getClipBoardType(const Enums::clipBoardType &defaultvalue=Enums::CLIPBOARD_NEVER) -> Enums::clipBoardType
static auto getWebDavUrl(const QString &defaultValue=QVariant().toString()) -> QString
static auto isUseWebDav(const bool &defaultValue=QVariant().toBool()) -> bool
static auto getAutoclearPanelSeconds(const int &defaultValue=QVariant().toInt()) -> int
static void setAutoclearSeconds(const int &autoClearSeconds)
static auto getImitatePass() -> ImitatePass *
static auto getAutoclearSeconds(const int &defaultValue=QVariant().toInt()) -> int
static auto isAutoPush(const bool &defaultValue=QVariant().toBool()) -> bool
static auto protocolRegex() -> const QRegularExpression &
Definition util.cpp:208
static auto findPasswordStore() -> QString
Util::findPasswordStore look for common .password-store folder location.
Definition util.cpp:58
static auto checkConfig() -> bool
Util::checkConfig do we have prequisite settings?
Definition util.cpp:146
#define dbg()
Definition debughelper.h:9
@ CLIPBOARD_ALWAYS
Definition enums.h:14
@ CLIPBOARD_NEVER
Definition enums.h:13
constexpr int MS_PER_SECOND
Definition util.h:12