QtPass 2.0.0
Developer documentation
Loading...
Searching...
No Matches
qtpass.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2018 Anne Jan Brouwer
2// SPDX-License-Identifier: GPL-3.0-or-later
8#include "qtpass.h"
9#include "executor.h"
10#include "imitatepass.h"
11#include "qtpasssettings.h"
12#include "settingsconstants.h"
13#include "util.h"
14#include <QApplication>
15#include <QDialog>
16#include <QLabel>
17#include <QPixmap>
18#include <QVBoxLayout>
19
20#include "qtpasslogging.h"
21
22QtPass::QtPass(QObject *parent) : QObject(parent), m_clipboard(this) {
23 // Both implementations are wired up: the active one can change through the
24 // configuration dialog, and reconnecting on every switch is not worth it.
27}
28
29QtPass::~QtPass() = default;
30
34
36
37 QString version = QtPassSettings::getVersion();
38
39 // Config updates
40 if (version.isEmpty()) {
41 qCDebug(lcQtPass) << "assuming fresh install";
43 if (s.autoclearSeconds < 5)
44 s.autoclearSeconds = 10;
45 if (s.autoclearPanelSeconds < 5)
47 s.usePwgen = !s.pwgenExecutable.isEmpty();
48 s.passTemplate = QStringLiteral("login\nurl\nOTP");
50 // A fresh profile already has the new useOtp default, so record the
51 // migration as done. Without this, turning OTP off during the first session
52 // would be undone by the migration branch on the next launch.
54 SettingsConstants::otpMigratedToNative, true);
55 } else {
57 bool changed = false;
58 if (s.passTemplate.isEmpty()) {
59 s.passTemplate = QStringLiteral("login\nurl\nOTP");
60 changed = true;
61 }
62 // `useOtp` used to gate the Unix-only pass-otp extension and now gates
63 // built-in TOTP, so an existing profile's stored false is stale rather than
64 // a preference. The new default of true never reaches these users: the
65 // fresh-install branch above already persisted the key, and the serializer
66 // writes it unconditionally. Enable it once, remembered by its own key so a
67 // deliberate opt-out is not overridden on the next launch.
69 ->value(SettingsConstants::otpMigratedToNative, false)
70 .toBool()) {
71 s.useOtp = true;
73 SettingsConstants::otpMigratedToNative, true);
74 changed = true;
75 }
76 if (changed) {
78 }
79 }
80
82}
83
97
98void QtPass::reportError(int exitCode, const QString &error) {
99 if (error.isEmpty()) {
100 return;
101 }
102 const QString colour =
103 exitCode == 0 ? QStringLiteral("darkgray") : QStringLiteral("red");
105 error, QStringLiteral("<span style=\"color: %1;\">").arg(colour),
106 QStringLiteral("</span><br />")));
107}
108
109void QtPass::processErrorExit(int exitCode, const QString &error) {
110 reportError(exitCode, error);
111 emit operationFinished();
112}
113
114void QtPass::processFinished(const QString &output, const QString &errout) {
115 // A silent command (git push with nothing to push, say) has nothing to
116 // show; re-setting the browser's HTML for it would only reset its scroll.
117 if (!output.isEmpty()) {
118 emit outputReady(formatOutput(output));
119 }
120 reportError(0, errout);
121 emit operationFinished();
122}
123
124void QtPass::passStoreChanged(const QString &output, const QString &errout) {
125 processFinished(output, errout);
126 doGitPush();
127}
128
129void QtPass::finishedInsert(const QString &output, const QString &errout) {
130 processFinished(output, errout);
131 doGitPush();
132 emit entryInserted();
133}
134
135void QtPass::onKeyGenerationComplete(const QString &output,
136 const QString &errout) {
137 // The KeygenDialog listens to the same signal and closes itself.
138 emit statusMessage(tr("GPG key pair generated successfully"), 10000);
139 processFinished(output, errout);
140}
141
142auto QtPass::formatOutput(QString output, const QString &prefix,
143 const QString &postfix) -> QString {
144 // Escapes and links only launchable http(s) URLs; anything else stays
145 // plain text (the text browser opens external links on click).
146 output = Util::linkifyUrls(output);
147 output.replace(QStringLiteral("\n"), "<br />");
148 return prefix + output + postfix;
149}
150
153 emit pushRequested();
154 }
155}
156
157void QtPass::showTextAsQRCode(const QString &text) {
159 const QString qrExe = s.qrencodeExecutable.isEmpty()
160 ? QStringLiteral("/usr/bin/qrencode")
162 QProcess qrencode;
164 qrencode.start(qrExe, QStringList() << "-o-"
165 << "-tPNG");
166 // A missing or non-executable binary never "finishes": exitStatus() stays
167 // NormalExit and exitCode() 0, which used to fall through to an empty popup.
168 if (!qrencode.waitForStarted()) {
169 emit statusMessage(tr("Could not start qrencode: %1").arg(qrExe), 2000);
170 return;
171 }
172 qrencode.write(text.toUtf8());
173 qrencode.closeWriteChannel();
174 // A hung qrencode also leaves exitStatus()/exitCode() at their defaults.
175 if (!qrencode.waitForFinished()) {
176 qrencode.kill();
177 emit statusMessage(tr("qrencode did not finish in time"), 2000);
178 return;
179 }
180 QByteArray output(qrencode.readAllStandardOutput());
181
182 const bool crashed = qrencode.exitStatus() != QProcess::NormalExit;
183 // exitCode() is meaningless after a crash, so only consult it otherwise.
184 const bool failed = !crashed && qrencode.exitCode() != 0;
185 if (crashed || failed) {
186 QString error(qrencode.readAllStandardError());
187 if (error.trimmed().isEmpty()) {
188 error = crashed
189 ? tr("qrencode crashed")
190 : tr("qrencode exited with code %1").arg(qrencode.exitCode());
191 }
192 emit statusMessage(error, 2000);
193 } else {
194 QPixmap image;
195 image.loadFromData(output, "PNG");
196 QDialog *popup = createQRCodePopup(image);
197 popup->exec();
198 }
199}
200
201QDialog *QtPass::createQRCodePopup(const QPixmap &image) {
202 auto *popup = new QDialog(nullptr, Qt::Popup | Qt::FramelessWindowHint);
203 popup->setAttribute(Qt::WA_DeleteOnClose);
204 auto *layout = new QVBoxLayout;
205 auto *popupLabel = new QLabel();
206 layout->addWidget(popupLabel);
207 popupLabel->setPixmap(image);
208 popupLabel->setScaledContents(true);
209 popupLabel->show();
210 popup->setLayout(layout);
211 popup->move(QCursor::pos());
212 return popup;
213}
static void detachFromTerminal(QProcess &process)
Make process start its child without a controlling terminal.
Definition executor.cpp:34
Abstract base class for password store operations.
Definition pass.h:75
void finishedInsert(const QString &out, const QString &err)
An Insert() succeeded: the entry is in the store and, with Git in use, committed.
void finishedGenerateGPGKeys(const QString &out, const QString &err)
GenerateGPGKeys() succeeded; the new key pair is in the keyring.
void finishedCopy(const QString &out, const QString &err)
A Copy() succeeded; the copy exists beside the original.
void finishedMove(const QString &out, const QString &err)
A Move() succeeded; the entry or folder is under its new name.
void finishedGitPull(const QString &out, const QString &err)
git pull succeeded; entries may have changed on disk.
void finishedGitPush(const QString &out, const QString &err)
git push succeeded.
void processErrorExit(int exitCode, const QString &err)
An operation ended in failure: its process exited non-zero, could not be started or crashed,...
void finishedGitInit(const QString &out, const QString &err)
git init in the store succeeded; the store is a repository now and the tree is re-read.
void finishedInit(const QString &out, const QString &err)
An Init() succeeded: the folder's .gpg-id holds the new recipient list.
void finishedRemove(const QString &out, const QString &err)
A Remove() succeeded; the entry or folder is gone from the store.
void operationFinished()
A backend operation is over, successfully or not; the interface can be enabled again.
void statusMessage(const QString &message, int timeout)
A short message for the status bar.
ClipboardManager m_clipboard
The clipboard handling for this application instance, exposed through clipboard(); parented to this o...
Definition qtpass.h:118
static auto formatOutput(QString output, const QString &prefix=QString(), const QString &postfix=QString()) -> QString
Turn a process' plain-text output into the HTML the text browser shows: http(s) URLs become links,...
Definition qtpass.cpp:142
void entryInserted()
An entry was written; the view of the current entry is stale.
void processFinished(const QString &output, const QString &errout)
Slot for Pass::finishedGitPull / finishedGitPush and the common tail of the other slots: emit the std...
Definition qtpass.cpp:114
void pushRequested()
The store changed and the settings ask for an automatic push.
void connectPassSignalHandlers(Pass *pass)
Connect the completion signals of one backend to the slots that turn them into output,...
Definition qtpass.cpp:84
void outputReady(const QString &html)
Formatted output (see formatOutput()) or an error rendered in colour, ready for the text browser.
void finishedInsert(const QString &output, const QString &errout)
Slot for Pass::finishedInsert: like passStoreChanged(), then emit entryInserted() so the window refre...
Definition qtpass.cpp:129
void reportError(int exitCode, const QString &error)
Show a process' stderr in colour through outputReady(): red for a failure, dark grey for the chatter ...
Definition qtpass.cpp:98
void onKeyGenerationComplete(const QString &output, const QString &errout)
Slot for Pass::finishedGenerateGPGKeys: a status message followed by processFinished()....
Definition qtpass.cpp:135
void passStoreChanged(const QString &output, const QString &errout)
Slot for the backend signals that change the store on disk (git init, remove, init,...
Definition qtpass.cpp:124
void init()
Resolve the password store and the executables and migrate the settings of an older version (fresh-in...
Definition qtpass.cpp:31
QtPass(QObject *parent=nullptr)
Construct the glue and connect it to both pass backends.
Definition qtpass.cpp:22
void doGitPush()
Emit pushRequested() when the settings ask for automatic push.
Definition qtpass.cpp:151
static QDialog * createQRCodePopup(const QPixmap &image)
Create a frameless popup dialog, placed at the cursor, showing the given QR code. Split out of showTe...
Definition qtpass.cpp:201
~QtPass() override
Destroy the QtPass instance and clean up signal connections.
void processErrorExit(int exitCode, const QString &error)
Slot for Pass::processErrorExit: report the error in red and emit operationFinished().
Definition qtpass.cpp:109
void showTextAsQRCode(const QString &text)
Render text as a QR code with qrencode and show it in a popup at the cursor; failures are reported th...
Definition qtpass.cpp:157
static auto getInstance() -> QtPassSettings *
Get the singleton instance.
static void setPassStore(const QString &passStore)
Save password store path.
static auto getPassStore(const QString &defaultValue=QVariant().toString()) -> QString
Get password store directory path.
static auto getVersion(const QString &defaultValue=QVariant().toString()) -> QString
Get saved application version.
static void initExecutables()
Initialize executable paths by auto-detecting them.
static auto load() -> AppSettings
Load all flat settings into an AppSettings value object.
static void setVersion(const QString &version)
Save application version.
static auto getRealPass() -> RealPass *
Get real pass backend instance.
static auto getImitatePass() -> ImitatePass *
Get imitate pass backend instance.
static void save(const AppSettings &settings)
Persist an AppSettings value object to the singleton store.
static auto isAutoPush(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether automatic push is enabled.
static auto findPasswordStore() -> QString
Locate the password store directory.
Definition util.cpp:73
static auto linkifyUrls(const QString &text, bool *linked=nullptr) -> QString
Turn plain text into HTML with clickable links for safe web URLs.
Definition util.cpp:627
Executor, the FIFO queue every gpg, git and pass child process of QtPass goes through,...
ImitatePass, the backend that does what pass does by running gpg and git itself.
QtPass: UI-neutral glue that turns backend completion signals into formatted output,...
The "qtpass" logging category.
QtPassSettings, the QSettings singleton behind every saved setting, the profiles and the Pass backend...
Plain value-object holding all flat QtPass configuration.
Definition appsettings.h:36
QString qrencodeExecutable
Path to the qrencode executable.
Definition appsettings.h:68
QString pwgenExecutable
Path to the pwgen executable.
Definition appsettings.h:67
bool useOtp
Enable native TOTP (OTP) support.
int autoclearPanelSeconds
Panel autoclear delay (seconds).
Definition appsettings.h:82
bool usePwgen
Use pwgen for password generation.
int autoclearSeconds
Clipboard autoclear delay (seconds).
Definition appsettings.h:80
QString passTemplate
Newline-separated template field names.
Util: static helpers for the process environment, binary discovery, store paths, URL safety and the l...