• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IJHack / QtPass / 27722776996

17 Jun 2026 10:05PM UTC coverage: 55.052% (-0.07%) from 55.118%
27722776996

Pull #1566

github

web-flow
Merge c2ddaed5f into 14fd3928a
Pull Request #1566: refactor(#1511): fold QtPass::init() fresh-install writes into AppSettings

2 of 12 new or added lines in 1 file covered. (16.67%)

3 existing lines in 2 files now uncovered.

3672 of 6670 relevant lines covered (55.05%)

29.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.47
/src/qtpasssettings.cpp
1
// SPDX-FileCopyrightText: 2016 Anne Jan Brouwer
2
// SPDX-License-Identifier: GPL-3.0-or-later
3

4
/**
5
 * @class QtPassSettings
6
 * @brief Singleton settings manager implementation.
7
 *
8
 * Implementation of QtPassSettings singleton. Handles persistence using
9
 * QSettings with support for portable mode (qtpass.ini next to executable).
10
 *
11
 * @see qtpasssettings.h
12
 */
13

14
#include "qtpasssettings.h"
15
#include "pass.h"
16
#include "passbackendfactory.h"
17
#include "settingsserializer.h"
18

19
#include "util.h"
20

21
#include <QCoreApplication>
22
#include <QCursor>
23
#include <QDebug>
24
#include <QGuiApplication>
25
#include <QScreen>
26
#include <utility>
27

28
bool QtPassSettings::initialized = false;
29

30
QtPassSettings *QtPassSettings::m_instance = nullptr;
31
/**
32
 * @brief Returns the singleton instance of QtPassSettings, creating it on first
33
 * access.
34
 * @example
35
 * QtPassSettings *settings = QtPassSettings::getInstance();
36
 * std::cout << settings << std::endl; // Expected output sample: a valid
37
 * QtPassSettings pointer
38
 *
39
 * @return QtPassSettings* - Pointer to the shared QtPassSettings instance.
40
 */
41
auto QtPassSettings::getInstance() -> QtPassSettings * {
1,240✔
42
  if (!QtPassSettings::initialized) {
1,240✔
43
    QString portable_ini = QCoreApplication::applicationDirPath() +
8✔
44
                           QDir::separator() + "qtpass.ini";
8✔
45
    if (QFile(portable_ini).exists()) {
8✔
46
      m_instance = new QtPassSettings(portable_ini, QSettings::IniFormat);
×
47
    } else {
48
      m_instance = new QtPassSettings("IJHack", "QtPass");
16✔
49
    }
50

51
    initialized = true;
8✔
52
  }
53

54
  return m_instance;
1,240✔
55
}
56

57
auto QtPassSettings::load() -> AppSettings {
383✔
58
  AppSettings s = SettingsSerializer::load(*getInstance());
383✔
59
  if (!s.passStore.isEmpty()) {
383✔
60
    s.passStore = QDir::cleanPath(QDir(s.passStore).absolutePath());
752✔
61
    if (!s.passStore.endsWith('/'))
376✔
62
      s.passStore += '/';
376✔
63
  }
64
  return s;
383✔
65
}
×
66

67
void QtPassSettings::save(const AppSettings &settings) {
166✔
68
  SettingsSerializer::save(*getInstance(), settings);
166✔
69
  // A settings save may have changed the "use pass" mode; drop the cached
70
  // backend so the correct one is rebuilt on next use (matches the previous
71
  // setUsePass() side effect).
72
  PassBackendFactory::invalidate();
166✔
73
}
166✔
74

75
/**
76
 * @brief Retrieves the current password configuration from application
77
 * settings.
78
 * @example
79
 * PasswordConfiguration result = QtPassSettings::getPasswordConfiguration();
80
 * std::cout << result.length << std::endl; // Expected output sample
81
 *
82
 * @return PasswordConfiguration - The password configuration populated from
83
 * stored settings, including length, selected character set, and custom
84
 * characters.
85
 */
86
auto QtPassSettings::getPasswordConfiguration() -> PasswordConfiguration {
7✔
87
  PasswordConfiguration config;
7✔
88

89
  config.length =
7✔
90
      getInstance()->value(SettingsConstants::passwordLength, 16).toInt();
7✔
91
  if (config.length <= 0) {
7✔
92
    config.length = 16;
×
93
  }
94
  config.selected = static_cast<PasswordConfiguration::characterSet>(
7✔
95
      getInstance()
7✔
96
          ->value(SettingsConstants::passwordCharsSelection, 0)
14✔
97
          .toInt());
7✔
98
  config.Characters[PasswordConfiguration::CUSTOM] =
99
      getInstance()
7✔
100
          ->value(SettingsConstants::passwordChars, QString())
14✔
101
          .toString();
7✔
102

103
  return config;
7✔
104
}
×
105

106
/**
107
 * @brief Retrieves the stored profiles configuration as a nested hash map.
108
 * @details Reads profile data from the settings group, including legacy profile
109
 *          formats from versions <= v1.3.2, and returns each profile name
110
 * mapped to its properties such as path and signing key.
111
 * @example
112
 * QHash<QString, QHash<QString, QString>> profiles =
113
 * QtPassSettings::getProfiles(); std::cout << profiles.size() << std::endl; //
114
 * Expected output: number of profiles
115
 *
116
 * @return QHash<QString, QHash<QString, QString>> - A hash map of profile names
117
 *         mapped to their key/value properties.
118
 */
119
auto QtPassSettings::getProfiles() -> QHash<QString, QHash<QString, QString>> {
28✔
120
  getInstance()->beginGroup(SettingsConstants::profile);
28✔
121
  QHash<QString, QHash<QString, QString>> profiles;
28✔
122

123
  // migration from version <= v1.3.2: profiles datastructure
124
  QStringList childKeys = getInstance()->childKeys();
28✔
125
  if (!childKeys.empty()) {
28✔
126
    for (const auto &key : std::as_const(childKeys)) {
×
127
      QHash<QString, QString> profile;
×
128
      profile.insert("path", getInstance()->value(key).toString());
×
129
      profile.insert("signingKey", "");
×
130
      profile.insert("useGit", "");
×
131
      profile.insert("autoPush", "");
×
132
      profile.insert("autoPull", "");
×
133
      profiles.insert(key, profile);
134
    }
×
135
  }
136
  // /migration from version <= v1.3.2
137

138
  QStringList childGroups = getInstance()->childGroups();
28✔
139
  for (const auto &group : std::as_const(childGroups)) {
56✔
140
    QHash<QString, QString> profile;
28✔
141
    profile.insert("path", getInstance()->value(group + "/path").toString());
56✔
142
    profile.insert("signingKey",
56✔
143
                   getInstance()->value(group + "/signingKey").toString());
56✔
144
    profile.insert("useGit",
56✔
145
                   getInstance()->value(group + "/useGit").toString());
56✔
146
    profile.insert("autoPush",
56✔
147
                   getInstance()->value(group + "/autoPush").toString());
56✔
148
    profile.insert("autoPull",
56✔
149
                   getInstance()->value(group + "/autoPull").toString());
84✔
150
    profiles.insert(group, profile);
151
  }
28✔
152

153
  getInstance()->endGroup();
28✔
154

155
  return profiles;
28✔
156
}
×
157

158
/**
159
 * @brief Stores the profile settings in the application's configuration.
160
 * @example
161
 * QtPassSettings::setProfiles(profiles);
162
 *
163
 * @param profiles - A hash of profile names mapped to their key-value settings,
164
 * such as "path" and "signingKey".
165
 * @return void - This method does not return a value.
166
 */
167
void QtPassSettings::setProfiles(
3✔
168
    const QHash<QString, QHash<QString, QString>> &profiles) {
169
  getInstance()->remove(SettingsConstants::profile);
3✔
170
  getInstance()->beginGroup(SettingsConstants::profile);
3✔
171

172
  QHash<QString, QHash<QString, QString>>::const_iterator i = profiles.begin();
3✔
173
  for (; i != profiles.end(); ++i) {
3✔
174
    getInstance()->setValue(i.key() + "/path", i.value().value("path"));
6✔
175
    getInstance()->setValue(i.key() + "/signingKey",
9✔
176
                            i.value().value("signingKey"));
3✔
177
    getInstance()->setValue(i.key() + "/useGit", i.value().value("useGit"));
6✔
178
    getInstance()->setValue(i.key() + "/autoPush", i.value().value("autoPush"));
6✔
179
    getInstance()->setValue(i.key() + "/autoPull", i.value().value("autoPull"));
9✔
180
  }
181

182
  getInstance()->endGroup();
3✔
183
}
3✔
184

185
auto QtPassSettings::getPass() -> Pass * {
53✔
186
  return PassBackendFactory::getPass();
53✔
187
}
188

189
auto QtPassSettings::getVersion(const QString &defaultValue) -> QString {
13✔
190
  return getInstance()
13✔
191
      ->value(SettingsConstants::version, defaultValue)
26✔
192
      .toString();
26✔
193
}
194
void QtPassSettings::setVersion(const QString &version) {
13✔
195
  getInstance()->setValue(SettingsConstants::version, version);
13✔
196
}
13✔
197

198
auto QtPassSettings::getGeometry(const QByteArray &defaultValue) -> QByteArray {
13✔
199
  return getInstance()
13✔
200
      ->value(SettingsConstants::geometry, defaultValue)
26✔
201
      .toByteArray();
26✔
202
}
203
void QtPassSettings::setGeometry(const QByteArray &geometry) {
1✔
204
  getInstance()->setValue(SettingsConstants::geometry, geometry);
1✔
205
}
1✔
206

207
auto QtPassSettings::getSavestate(const QByteArray &defaultValue)
13✔
208
    -> QByteArray {
209
  return getInstance()
13✔
210
      ->value(SettingsConstants::savestate, defaultValue)
26✔
211
      .toByteArray();
26✔
212
}
213
void QtPassSettings::setSavestate(const QByteArray &saveState) {
1✔
214
  getInstance()->setValue(SettingsConstants::savestate, saveState);
1✔
215
}
1✔
216

217
auto QtPassSettings::getPos(const QPoint &defaultValue) -> QPoint {
13✔
218
  QPoint pos =
219
      getInstance()->value(SettingsConstants::pos, defaultValue).toPoint();
13✔
220
  if (pos == QPoint(0, 0)) {
221
    QScreen *screen = QGuiApplication::screenAt(QCursor::pos());
12✔
222
    if (!screen)
12✔
223
      screen = QGuiApplication::primaryScreen();
×
224
    if (screen)
×
225
      pos = screen->geometry().center();
12✔
226
  }
227
  return pos;
13✔
228
}
229
void QtPassSettings::setPos(const QPoint &pos) {
1✔
230
  if (pos == QPoint(0, 0))
231
    return;
×
232
  getInstance()->setValue(SettingsConstants::pos, pos);
1✔
233
}
234

235
auto QtPassSettings::getSize(const QSize &defaultValue) -> QSize {
13✔
236
  return getInstance()->value(SettingsConstants::size, defaultValue).toSize();
13✔
237
}
238
void QtPassSettings::setSize(const QSize &size) {
1✔
239
  getInstance()->setValue(SettingsConstants::size, size);
1✔
240
}
1✔
241

242
void QtPassSettings::setMaximized(const bool &maximized) {
×
243
  getInstance()->setValue(SettingsConstants::maximized, maximized);
×
244
}
×
245

246
auto QtPassSettings::getDialogGeometry(const QString &key,
24✔
247
                                       const QByteArray &defaultValue)
248
    -> QByteArray {
249
  return getInstance()
24✔
250
      ->value(SettingsConstants::dialogGeometry + "/" + key, defaultValue)
72✔
251
      .toByteArray();
48✔
252
}
253
void QtPassSettings::setDialogGeometry(const QString &key,
1✔
254
                                       const QByteArray &geometry) {
255
  getInstance()->setValue(SettingsConstants::dialogGeometry + "/" + key,
2✔
256
                          geometry);
257
}
1✔
258

259
auto QtPassSettings::getDialogPos(const QString &key,
1✔
260
                                  const QPoint &defaultValue) -> QPoint {
261
  return getInstance()
1✔
262
      ->value(SettingsConstants::dialogPos + "/" + key, defaultValue)
3✔
263
      .toPoint();
2✔
264
}
265
void QtPassSettings::setDialogPos(const QString &key, const QPoint &pos) {
1✔
266
  getInstance()->setValue(SettingsConstants::dialogPos + "/" + key, pos);
2✔
267
}
1✔
268

269
auto QtPassSettings::getDialogSize(const QString &key,
1✔
270
                                   const QSize &defaultValue) -> QSize {
271
  return getInstance()
1✔
272
      ->value(SettingsConstants::dialogSize + "/" + key, defaultValue)
3✔
273
      .toSize();
2✔
274
}
275
void QtPassSettings::setDialogSize(const QString &key, const QSize &size) {
1✔
276
  getInstance()->setValue(SettingsConstants::dialogSize + "/" + key, size);
2✔
277
}
1✔
278

279
auto QtPassSettings::isDialogMaximized(const QString &key,
25✔
280
                                       const bool &defaultValue) -> bool {
281
  return getInstance()
25✔
282
      ->value(SettingsConstants::dialogMaximized + "/" + key, defaultValue)
75✔
283
      .toBool();
50✔
284
}
285
void QtPassSettings::setDialogMaximized(const QString &key,
2✔
286
                                        const bool &maximized) {
287
  getInstance()->setValue(SettingsConstants::dialogMaximized + "/" + key,
6✔
288
                          maximized);
2✔
289
}
2✔
290

291
void QtPassSettings::setUsePass(const bool &usePass) {
16✔
292
  getInstance()->setValue(SettingsConstants::usePass, usePass);
16✔
293
  // Backend selection changed: force re-selection on next getPass().
294
  PassBackendFactory::invalidate();
16✔
295
}
16✔
296

297
auto QtPassSettings::getAutoclearSeconds(const int &defaultValue) -> int {
12✔
298
  return getInstance()
12✔
299
      ->value(SettingsConstants::autoclearSeconds, defaultValue)
24✔
300
      .toInt();
24✔
301
}
302
/**
303
 * @brief Retrieves the password store path, normalizes it, and ensures the
304
 * directory exists.
305
 * @example
306
 * QString passStore =
307
 * QtPassSettings::getPassStore("/home/user/.password-store"); qDebug() <<
308
 * passStore; // Expected output: "/home/user/.password-store/"
309
 *
310
 * @param defaultValue - Fallback path used when no password store is
311
 * configured.
312
 * @return QString - The normalized absolute password store path, guaranteed to
313
 * end with a path separator.
314
 */
315
auto QtPassSettings::getPassStore(const QString &defaultValue) -> QString {
33✔
316
  QString returnValue = getInstance()
33✔
317
                            ->value(SettingsConstants::passStore, defaultValue)
66✔
318
                            .toString();
33✔
319

320
  // Normalize the path string
321
  returnValue = QDir(returnValue).absolutePath();
66✔
322

323
  // ensure directory exists if never used pass or misconfigured.
324
  // otherwise process->setWorkingDirectory(passStore); will fail on execution.
325
  if (!QDir(returnValue).exists()) {
33✔
326
    if (!QDir().mkdir(returnValue)) {
4✔
327
      qWarning() << "Failed to create password store directory:" << returnValue;
×
328
    }
329
  }
330

331
  // ensure path ends in /
332
  if (!returnValue.endsWith("/") && !returnValue.endsWith(QDir::separator())) {
66✔
333
    returnValue += QDir::separator();
33✔
334
  }
335

336
  return returnValue;
33✔
337
}
338
void QtPassSettings::setPassStore(const QString &passStore) {
51✔
339
  getInstance()->setValue(SettingsConstants::passStore, passStore);
51✔
340
}
51✔
341
void QtPassSettings::setPassSigningKey(const QString &passSigningKey) {
2✔
342
  getInstance()->setValue(SettingsConstants::passSigningKey, passSigningKey);
2✔
343
}
2✔
344

345
/**
346
 * @brief Initializes executable paths for Pass, Git, GPG, and Pwgen by locating
347
 * them in the system PATH.
348
 * @example
349
 * QtPassSettings::initExecutables();
350
 *
351
 * @return void - This method does not return a value.
352
 */
353
void QtPassSettings::initExecutables() {
12✔
354
  AppSettings s = QtPassSettings::load();
12✔
355
  if (s.passExecutable.isEmpty())
12✔
356
    s.passExecutable = Util::findBinaryInPath("pass");
×
357
  if (s.gitExecutable.isEmpty())
12✔
358
    s.gitExecutable = Util::findBinaryInPath("git");
×
359
  if (s.gpgExecutable.isEmpty())
12✔
360
    s.gpgExecutable = Util::findBinaryInPath("gpg2");
×
361
  if (s.pwgenExecutable.isEmpty())
12✔
362
    s.pwgenExecutable = Util::findBinaryInPath("pwgen");
×
363
  QtPassSettings::save(s);
12✔
364
}
12✔
365
auto QtPassSettings::getPassExecutable(const QString &defaultValue) -> QString {
13✔
366
  return getInstance()
13✔
367
      ->value(SettingsConstants::passExecutable, defaultValue)
26✔
368
      .toString();
26✔
369
}
370

371
auto QtPassSettings::isUseWebDav(const bool &defaultValue) -> bool {
12✔
372
  return getInstance()
12✔
373
      ->value(SettingsConstants::useWebDav, defaultValue)
24✔
374
      .toBool();
24✔
375
}
376
auto QtPassSettings::getProfile(const QString &defaultValue) -> QString {
26✔
377
  return getInstance()
26✔
378
      ->value(SettingsConstants::profile, defaultValue)
52✔
379
      .toString();
52✔
380
}
381
void QtPassSettings::setProfile(const QString &profile) {
1✔
382
  getInstance()->setValue(SettingsConstants::profile, profile);
1✔
383
}
1✔
384

385
/**
386
 * @brief Gets the useGit setting for a specific profile.
387
 * @param profileName The profile name.
388
 * @param defaultValue The default value if not set.
389
 * @return The useGit setting for the profile.
390
 */
391
auto QtPassSettings::getProfileUseGit(const QString &profileName,
3✔
392
                                      const bool &defaultValue) -> bool {
393
  QString stored =
394
      getInstance()
3✔
395
          ->value(SettingsConstants::profile + "/" + profileName + "/useGit")
9✔
396
          .toString();
3✔
397
  // If empty or not set, return default (migration-friendly fallback)
398
  if (stored.isEmpty()) {
3✔
399
    return defaultValue;
1✔
400
  }
401
  return stored == "true";
402
}
403

404
/**
405
 * @brief Sets the useGit setting for a specific profile.
406
 * @param profileName The profile name.
407
 * @param useGit The useGit value to set.
408
 */
409
void QtPassSettings::setProfileUseGit(const QString &profileName,
2✔
410
                                      const bool &useGit) {
411
  getInstance()->setValue(SettingsConstants::profile + "/" + profileName +
6✔
412
                              "/useGit",
413
                          useGit ? "true" : "false");
2✔
414
}
2✔
415

416
/**
417
 * @brief Gets the autoPush setting for a specific profile.
418
 * @param profileName The profile name.
419
 * @param defaultValue The default value if not set.
420
 * @return The autoPush setting for the profile.
421
 */
422
auto QtPassSettings::getProfileAutoPush(const QString &profileName,
3✔
423
                                        const bool &defaultValue) -> bool {
424
  QString stored =
425
      getInstance()
3✔
426
          ->value(SettingsConstants::profile + "/" + profileName + "/autoPush")
9✔
427
          .toString();
3✔
428
  if (stored.isEmpty()) {
3✔
429
    return defaultValue;
1✔
430
  }
431
  return stored == "true";
432
}
433

434
/**
435
 * @brief Sets the autoPush setting for a specific profile.
436
 * @param profileName The profile name.
437
 * @param autoPush The autoPush value to set.
438
 */
439
void QtPassSettings::setProfileAutoPush(const QString &profileName,
2✔
440
                                        const bool &autoPush) {
441
  getInstance()->setValue(SettingsConstants::profile + "/" + profileName +
6✔
442
                              "/autoPush",
443
                          autoPush ? "true" : "false");
2✔
444
}
2✔
445

446
/**
447
 * @brief Gets the autoPull setting for a specific profile.
448
 * @param profileName The profile name.
449
 * @param defaultValue The default value if not set.
450
 * @return The autoPull setting for the profile.
451
 */
452
auto QtPassSettings::getProfileAutoPull(const QString &profileName,
3✔
453
                                        const bool &defaultValue) -> bool {
454
  QString stored =
455
      getInstance()
3✔
456
          ->value(SettingsConstants::profile + "/" + profileName + "/autoPull")
9✔
457
          .toString();
3✔
458
  if (stored.isEmpty()) {
3✔
459
    return defaultValue;
1✔
460
  }
461
  return stored == "true";
462
}
463

464
/**
465
 * @brief Sets the autoPull setting for a specific profile.
466
 * @param profileName The profile name.
467
 * @param autoPull The autoPull value to set.
468
 */
469
void QtPassSettings::setProfileAutoPull(const QString &profileName,
2✔
470
                                        const bool &autoPull) {
471
  getInstance()->setValue(SettingsConstants::profile + "/" + profileName +
6✔
472
                              "/autoPull",
473
                          autoPull ? "true" : "false");
2✔
474
}
2✔
475

476
/**
477
 * @brief Determines whether Git should be used for the current QtPass settings.
478
 * @example
479
 * bool result = QtPassSettings::isUseGit(true);
480
 * std::cout << result << std::endl; // Expected output: true or false
481
 *
482
 * @param const bool &defaultValue - The fallback value used when no explicit
483
 * setting is stored.
484
 * @return bool - True if Git usage is enabled, otherwise false.
485
 */
486
auto QtPassSettings::isUseGit(const bool &defaultValue) -> bool {
6✔
487
  bool storedValue =
488
      getInstance()->value(SettingsConstants::useGit, defaultValue).toBool();
6✔
489
  if (storedValue == defaultValue && defaultValue) {
6✔
490
    QString passStore = getPassStore();
4✔
491
    if (QFileInfo(passStore).isDir() &&
4✔
492
        QFileInfo(passStore + QDir::separator() + ".git").isDir()) {
6✔
493
      return true;
494
    }
495
  }
496
  return storedValue;
497
}
498
auto QtPassSettings::isUseGrepSearch(const bool &defaultValue) -> bool {
12✔
499
  return getInstance()
12✔
500
      ->value(SettingsConstants::useGrepSearch, defaultValue)
24✔
501
      .toBool();
24✔
502
}
503

504
auto QtPassSettings::isUseOtp(const bool &defaultValue) -> bool {
15✔
505
  return getInstance()->value(SettingsConstants::useOtp, defaultValue).toBool();
15✔
506
}
507

508
void QtPassSettings::setQrencodeExecutable(const QString &qrencodeExecutable) {
13✔
509
  getInstance()->setValue(SettingsConstants::qrencodeExecutable,
13✔
510
                          qrencodeExecutable);
511
}
13✔
512

UNCOV
513
auto QtPassSettings::isHideOnClose(const bool &defaultValue) -> bool {
×
UNCOV
514
  return getInstance()
×
515
      ->value(SettingsConstants::hideOnClose, defaultValue)
×
516
      .toBool();
×
517
}
518
auto QtPassSettings::isAlwaysOnTop(const bool &defaultValue) -> bool {
×
519
  return getInstance()
×
520
      ->value(SettingsConstants::alwaysOnTop, defaultValue)
×
521
      .toBool();
×
522
}
523

524
auto QtPassSettings::isAutoPush(const bool &defaultValue) -> bool {
×
525
  return getInstance()
×
526
      ->value(SettingsConstants::autoPush, defaultValue)
×
527
      .toBool();
×
528
}
529

530
auto QtPassSettings::isShowProcessOutput(const bool &defaultValue) -> bool {
15✔
531
  return getInstance()
15✔
532
      ->value(SettingsConstants::showProcessOutput, defaultValue)
30✔
533
      .toBool();
30✔
534
}
535
auto QtPassSettings::getRealPass() -> RealPass * {
12✔
536
  return PassBackendFactory::getRealPass();
12✔
537
}
538
auto QtPassSettings::getImitatePass() -> ImitatePass * {
36✔
539
  return PassBackendFactory::getImitatePass();
36✔
540
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc