Line data Source code
1 : // SPDX-FileCopyrightText: 2016 Anne Jan Brouwer
2 : // SPDX-License-Identifier: GPL-3.0-or-later
3 : /**
4 : * @file
5 : * @brief Implements QtPassSettings; getInstance() picks the portable
6 : * `qtpass.ini` over the platform store, and getProfiles()/setProfiles()
7 : * read and write the `profile/<name>/` groups.
8 : */
9 : #include "qtpasssettings.h"
10 : #include "pass.h"
11 : #include "passbackendfactory.h"
12 : #include "settingsserializer.h"
13 :
14 : #include "util.h"
15 :
16 : #include <QCoreApplication>
17 : #include <QCursor>
18 : #include <QDebug>
19 : #include <QDir>
20 : #include <QFile>
21 : #include <QFileInfo>
22 : #include <QGuiApplication>
23 : #include <QScreen>
24 : #include <utility>
25 :
26 : bool QtPassSettings::initialized = false;
27 :
28 : QtPassSettings *QtPassSettings::m_instance = nullptr;
29 : // Portable mode: a qtpass.ini next to the executable wins over the platform
30 : // settings store.
31 8332 : auto QtPassSettings::getInstance() -> QtPassSettings * {
32 8332 : if (!QtPassSettings::initialized) {
33 15 : QString portable_ini = QCoreApplication::applicationDirPath() +
34 15 : QDir::separator() + "qtpass.ini";
35 15 : if (QFile(portable_ini).exists()) {
36 0 : m_instance = new QtPassSettings(portable_ini, QSettings::IniFormat);
37 : } else {
38 15 : m_instance = new QtPassSettings("IJHack", "QtPass");
39 : }
40 :
41 15 : initialized = true;
42 : }
43 :
44 8332 : return m_instance;
45 : }
46 :
47 2557 : auto QtPassSettings::load() -> AppSettings {
48 2557 : AppSettings s = SettingsSerializer::load(*getInstance());
49 2557 : if (!s.passStore.isEmpty()) {
50 4260 : s.passStore = QDir::cleanPath(QDir(s.passStore).absolutePath());
51 2130 : if (!s.passStore.endsWith('/'))
52 2097 : s.passStore += '/';
53 : }
54 2557 : return s;
55 0 : }
56 :
57 765 : void QtPassSettings::save(const AppSettings &settings) {
58 765 : SettingsSerializer::save(*getInstance(), settings);
59 : // The "use pass" mode may have changed; rebuild the backend on next use.
60 765 : PassBackendFactory::invalidate();
61 765 : }
62 :
63 13 : auto QtPassSettings::getPasswordConfiguration() -> PasswordConfiguration {
64 13 : return SettingsSerializer::loadPasswordConfiguration(*getInstance());
65 : }
66 :
67 276 : auto QtPassSettings::getProfiles() -> Profiles {
68 276 : getInstance()->beginGroup(SettingsConstants::profile);
69 276 : Profiles profiles;
70 :
71 : // migration from version <= v1.3.2: profiles datastructure
72 276 : QStringList childKeys = getInstance()->childKeys();
73 276 : for (const auto &key : std::as_const(childKeys)) {
74 0 : Profile profile;
75 0 : profile.path = getInstance()->value(key).toString();
76 0 : profiles.insert(key, profile);
77 : }
78 : // /migration from version <= v1.3.2
79 :
80 276 : QStringList childGroups = getInstance()->childGroups();
81 335 : for (const auto &group : std::as_const(childGroups)) {
82 59 : Profile profile;
83 118 : profile.path = getInstance()->value(group + "/path").toString();
84 118 : profile.signingKey = getInstance()->value(group + "/signingKey").toString();
85 59 : profile.useGit = Profile::flagFromString(
86 118 : getInstance()->value(group + "/useGit").toString());
87 59 : profile.autoPush = Profile::flagFromString(
88 118 : getInstance()->value(group + "/autoPush").toString());
89 59 : profile.autoPull = Profile::flagFromString(
90 118 : getInstance()->value(group + "/autoPull").toString());
91 59 : profiles.insert(group, profile);
92 : }
93 :
94 276 : getInstance()->endGroup();
95 :
96 276 : return profiles;
97 : }
98 :
99 84 : void QtPassSettings::setProfiles(const Profiles &profiles) {
100 : // "profile" is both the active-profile scalar (getProfile()) and the
101 : // profile/<name>/ group; remove() wipes both, which would switch the active
102 : // store on every save, so preserve the active name across the rewrite.
103 : const QString activeProfile =
104 84 : getInstance()->value(SettingsConstants::profile).toString();
105 84 : getInstance()->remove(SettingsConstants::profile);
106 84 : if (!activeProfile.isEmpty()) {
107 26 : getInstance()->setValue(SettingsConstants::profile, activeProfile);
108 : }
109 84 : getInstance()->beginGroup(SettingsConstants::profile);
110 :
111 128 : for (auto i = profiles.cbegin(); i != profiles.cend(); ++i) {
112 88 : getInstance()->setValue(i.key() + "/path", i.value().path);
113 88 : getInstance()->setValue(i.key() + "/signingKey", i.value().signingKey);
114 88 : getInstance()->setValue(i.key() + "/useGit",
115 88 : Profile::flagToString(i.value().useGit));
116 88 : getInstance()->setValue(i.key() + "/autoPush",
117 88 : Profile::flagToString(i.value().autoPush));
118 88 : getInstance()->setValue(i.key() + "/autoPull",
119 132 : Profile::flagToString(i.value().autoPull));
120 : }
121 :
122 84 : getInstance()->endGroup();
123 84 : }
124 :
125 498 : auto QtPassSettings::getPass() -> Pass * {
126 498 : return PassBackendFactory::getPass();
127 : }
128 :
129 138 : auto QtPassSettings::getVersion(const QString &defaultValue) -> QString {
130 138 : return getInstance()
131 276 : ->value(SettingsConstants::version, defaultValue)
132 276 : .toString();
133 : }
134 136 : void QtPassSettings::setVersion(const QString &version) {
135 136 : getInstance()->setValue(SettingsConstants::version, version);
136 136 : }
137 :
138 143 : auto QtPassSettings::getGeometry(const QByteArray &defaultValue) -> QByteArray {
139 143 : return getInstance()
140 286 : ->value(SettingsConstants::geometry, defaultValue)
141 286 : .toByteArray();
142 : }
143 138 : void QtPassSettings::setGeometry(const QByteArray &geometry) {
144 138 : getInstance()->setValue(SettingsConstants::geometry, geometry);
145 138 : }
146 :
147 141 : auto QtPassSettings::getSavestate(const QByteArray &defaultValue)
148 : -> QByteArray {
149 141 : return getInstance()
150 282 : ->value(SettingsConstants::savestate, defaultValue)
151 282 : .toByteArray();
152 : }
153 134 : void QtPassSettings::setSavestate(const QByteArray &saveState) {
154 134 : getInstance()->setValue(SettingsConstants::savestate, saveState);
155 134 : }
156 :
157 141 : auto QtPassSettings::getDialogGeometry(const QString &key,
158 : const QByteArray &defaultValue)
159 : -> QByteArray {
160 141 : return getInstance()
161 423 : ->value(SettingsConstants::dialogGeometry + "/" + key, defaultValue)
162 282 : .toByteArray();
163 : }
164 34 : void QtPassSettings::setDialogGeometry(const QString &key,
165 : const QByteArray &geometry) {
166 68 : getInstance()->setValue(SettingsConstants::dialogGeometry + "/" + key,
167 68 : geometry);
168 34 : }
169 :
170 118 : void QtPassSettings::setUsePass(const bool &usePass) {
171 118 : getInstance()->setValue(SettingsConstants::usePass, usePass);
172 : // Backend selection changed: force re-selection on next getPass().
173 118 : PassBackendFactory::invalidate();
174 118 : }
175 :
176 160 : auto QtPassSettings::getAutoclearSeconds(const int &defaultValue) -> int {
177 160 : return getInstance()
178 320 : ->value(SettingsConstants::autoclearSeconds, defaultValue)
179 320 : .toInt();
180 : }
181 : // Creates the directory if missing; the result always ends in a separator.
182 287 : auto QtPassSettings::getPassStore(const QString &defaultValue) -> QString {
183 287 : QString returnValue = getInstance()
184 574 : ->value(SettingsConstants::passStore, defaultValue)
185 287 : .toString();
186 :
187 574 : returnValue = QDir(returnValue).absolutePath();
188 :
189 : // ensure directory exists if never used pass or misconfigured.
190 : // otherwise process->setWorkingDirectory(passStore); will fail on execution.
191 287 : if (!QDir(returnValue).exists()) {
192 3 : if (!Util::mkpathForStore(returnValue)) {
193 0 : qWarning() << "Failed to create password store directory:" << returnValue;
194 : }
195 : }
196 :
197 574 : if (!returnValue.endsWith("/") && !returnValue.endsWith(QDir::separator())) {
198 287 : returnValue += QDir::separator();
199 : }
200 :
201 287 : return returnValue;
202 : }
203 287 : void QtPassSettings::setPassStore(const QString &passStore) {
204 287 : getInstance()->setValue(SettingsConstants::passStore, passStore);
205 287 : }
206 134 : void QtPassSettings::initExecutables() {
207 134 : AppSettings s = QtPassSettings::load();
208 134 : if (s.passExecutable.isEmpty())
209 268 : s.passExecutable = Util::findBinaryInPath("pass");
210 134 : if (s.gitExecutable.isEmpty())
211 12 : s.gitExecutable = Util::findBinaryInPath("git");
212 134 : if (s.gpgExecutable.isEmpty()) {
213 4 : s.gpgExecutable = Util::findBinaryInPath("gpg2");
214 2 : if (s.gpgExecutable.isEmpty())
215 0 : s.gpgExecutable = Util::findBinaryInPath("gpg");
216 : }
217 134 : if (s.pwgenExecutable.isEmpty())
218 266 : s.pwgenExecutable = Util::findBinaryInPath("pwgen");
219 134 : QtPassSettings::save(s);
220 134 : }
221 0 : auto QtPassSettings::getPassExecutable(const QString &defaultValue) -> QString {
222 0 : return getInstance()
223 0 : ->value(SettingsConstants::passExecutable, defaultValue)
224 0 : .toString();
225 : }
226 :
227 90 : auto QtPassSettings::getProfile(const QString &defaultValue) -> QString {
228 90 : return getInstance()
229 180 : ->value(SettingsConstants::profile, defaultValue)
230 180 : .toString();
231 : }
232 : // With a true default and nothing stored, a store with a .git dir means Git.
233 11 : auto QtPassSettings::isUseGit(const bool &defaultValue) -> bool {
234 : bool storedValue =
235 11 : getInstance()->value(SettingsConstants::useGit, defaultValue).toBool();
236 11 : if (storedValue == defaultValue && defaultValue) {
237 4 : QString passStore = getPassStore();
238 4 : if (QFileInfo(passStore).isDir() &&
239 6 : QFileInfo(passStore + QDir::separator() + ".git").isDir()) {
240 : return true;
241 : }
242 : }
243 : return storedValue;
244 : }
245 133 : auto QtPassSettings::isUseGrepSearch(const bool &defaultValue) -> bool {
246 133 : return getInstance()
247 266 : ->value(SettingsConstants::useGrepSearch, defaultValue)
248 266 : .toBool();
249 : }
250 :
251 267 : auto QtPassSettings::isUseOtp(const bool &defaultValue) -> bool {
252 267 : return getInstance()->value(SettingsConstants::useOtp, defaultValue).toBool();
253 : }
254 :
255 1 : void QtPassSettings::setQrencodeExecutable(const QString &qrencodeExecutable) {
256 2 : getInstance()->setValue(SettingsConstants::qrencodeExecutable,
257 2 : qrencodeExecutable);
258 1 : }
259 :
260 2 : auto QtPassSettings::isHideOnClose(const bool &defaultValue) -> bool {
261 2 : return getInstance()
262 4 : ->value(SettingsConstants::hideOnClose, defaultValue)
263 4 : .toBool();
264 : }
265 142 : auto QtPassSettings::isAlwaysOnTop(const bool &defaultValue) -> bool {
266 142 : return getInstance()
267 284 : ->value(SettingsConstants::alwaysOnTop, defaultValue)
268 284 : .toBool();
269 : }
270 :
271 8 : auto QtPassSettings::isAutoPush(const bool &defaultValue) -> bool {
272 8 : return getInstance()
273 16 : ->value(SettingsConstants::autoPush, defaultValue)
274 16 : .toBool();
275 : }
276 :
277 306 : auto QtPassSettings::isShowProcessOutput(const bool &defaultValue) -> bool {
278 306 : return getInstance()
279 612 : ->value(SettingsConstants::showProcessOutput, defaultValue)
280 612 : .toBool();
281 : }
282 285 : auto QtPassSettings::getRealPass() -> RealPass * {
283 285 : return PassBackendFactory::getRealPass();
284 : }
285 411 : auto QtPassSettings::getImitatePass() -> ImitatePass * {
286 411 : return PassBackendFactory::getImitatePass();
287 : }
288 :
289 134 : auto QtPassSettings::backendsIdle() -> bool {
290 134 : return PassBackendFactory::isIdle();
291 : }
|