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

IJHack / QtPass / 24585183540

17 Apr 2026 08:25PM UTC coverage: 21.818% (+0.5%) from 21.329%
24585183540

push

github

web-flow
fix: set PASSWORD_STORE_* env vars and grey out pass-managed controls (#1032)

* fix: set PASSWORD_STORE_* env vars and grey out pass-managed controls (#265 #443)

- Pass::updateEnv() now sets PASSWORD_STORE_GENERATED_LENGTH and
  PASSWORD_STORE_CHARACTER_SET from QtPass settings so the pass binary
  honours the configured length and character set
- ConfigDialog::setGroupBoxState() disables password-generation
  controls (length, charset, pwgen options) when the pass binary is
  selected, since those are managed by pass in that mode
- passworddialog.ui spinBox_pwdLength maximum was already 255

Closes #265
Closes #443

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: isolate generateRandomPassword, boundedRandom, configIsValid tests from QSettings

Tests were reading isUsePwgen() and isUsePass() from the user's saved
QSettings, causing failures when those flags were enabled. Add RAII
guards to force the relevant settings to the expected values and restore
them on exit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address code review findings on password generation controls and env vars

configdialog.cpp:
- setGroupBoxState() now disables all password-gen controls in pass mode
  and delegates to on_checkBoxUsePwgen_clicked() when restoring native
  mode, instead of blindly re-enabling everything
- on_checkBoxUsePwgen_clicked() guards against pass mode so usePwgen()
  called after usePass(true) in the constructor cannot re-enable the
  pwgen sub-controls

pass.cpp:
- Clamp passConfig.selected before array access to guard against
  out-of-range values in QSettings
- Remove stale PASSWORD_STORE_CHARACTER_SET env entry when charset
  becomes empty, instead of leaving the old value in place
- Add PASSWORD_STORE_GENERATED_LENGTH and PASSWORD_STORE_CHARACTER_SET
  to WSLENV so they propagate into the WSL environment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor: replace duplicate inline rollback ... (continued)

28 of 46 new or added lines in 3 files covered. (60.87%)

1 existing line in 1 file now uncovered.

1176 of 5390 relevant lines covered (21.82%)

8.68 hits per line

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

0.0
/src/configdialog.cpp
1
// SPDX-FileCopyrightText: 2014 Anne Jan Brouwer
2
// SPDX-License-Identifier: GPL-3.0-or-later
3
#include "configdialog.h"
4
#include "keygendialog.h"
5
#include "mainwindow.h"
6
#include "qtpasssettings.h"
7
#include "ui_configdialog.h"
8
#include "util.h"
9
#include <QClipboard>
10
#include <QDir>
11
#include <QFileDialog>
12
#include <QMessageBox>
13
#include <QPushButton>
14
#include <QSystemTrayIcon>
15
#include <QTableWidgetItem>
16
#include <utility>
17
#ifdef Q_OS_WIN
18
#include <windows.h>
19
#endif
20

21
#ifdef QT_DEBUG
22
#include "debughelper.h"
23
#endif
24

25
/**
26
 * @brief ConfigDialog::ConfigDialog this sets up the configuration screen.
27
 * @param parent
28
 */
29
ConfigDialog::ConfigDialog(MainWindow *parent)
×
30
    : QDialog(parent), ui(new Ui::ConfigDialog) {
×
31
  mainWindow = parent;
×
32
  ui->setupUi(this);
×
33

34
  // Restore dialog state
35
  QByteArray savedGeometry = QtPassSettings::getDialogGeometry("configDialog");
×
36
  bool hasSavedGeometry = !savedGeometry.isEmpty();
37
  if (hasSavedGeometry) {
×
38
    restoreGeometry(savedGeometry);
×
39
  }
40
  if (QtPassSettings::isDialogMaximized("configDialog")) {
×
41
    showMaximized();
×
42
  } else if (!hasSavedGeometry) {
43
    // Let window manager handle positioning for first launch
44
  }
45

46
  ui->passPath->setText(QtPassSettings::getPassExecutable());
×
47
  setGitPath(QtPassSettings::getGitExecutable());
×
48
  ui->gpgPath->setText(QtPassSettings::getGpgExecutable());
×
49
  ui->storePath->setText(QtPassSettings::getPassStore());
×
50

51
  ui->spinBoxAutoclearSeconds->setValue(QtPassSettings::getAutoclearSeconds());
×
52
  ui->spinBoxAutoclearPanelSeconds->setValue(
×
53
      QtPassSettings::getAutoclearPanelSeconds());
×
54
  ui->checkBoxHidePassword->setChecked(QtPassSettings::isHidePassword());
×
55
  ui->checkBoxHideContent->setChecked(QtPassSettings::isHideContent());
×
56
  ui->checkBoxUseMonospace->setChecked(QtPassSettings::isUseMonospace());
×
57
  ui->checkBoxDisplayAsIs->setChecked(QtPassSettings::isDisplayAsIs());
×
58
  ui->checkBoxNoLineWrapping->setChecked(QtPassSettings::isNoLineWrapping());
×
59
  ui->checkBoxAddGPGId->setChecked(QtPassSettings::isAddGPGId(true));
×
60

61
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
×
62
    ui->checkBoxHideOnClose->setChecked(QtPassSettings::isHideOnClose());
×
63
    ui->checkBoxStartMinimized->setChecked(QtPassSettings::isStartMinimized());
×
64
  } else {
65
    ui->checkBoxUseTrayIcon->setEnabled(false);
×
66
    ui->checkBoxUseTrayIcon->setToolTip(tr("System tray is not available"));
×
67
    ui->checkBoxHideOnClose->setEnabled(false);
×
68
    ui->checkBoxStartMinimized->setEnabled(false);
×
69
  }
70

71
  ui->checkBoxAvoidCapitals->setChecked(QtPassSettings::isAvoidCapitals());
×
72
  ui->checkBoxAvoidNumbers->setChecked(QtPassSettings::isAvoidNumbers());
×
73
  ui->checkBoxLessRandom->setChecked(QtPassSettings::isLessRandom());
×
74
  ui->checkBoxUseSymbols->setChecked(QtPassSettings::isUseSymbols());
×
75
  ui->plainTextEditTemplate->setPlainText(QtPassSettings::getPassTemplate());
×
76
  ui->checkBoxTemplateAllFields->setChecked(
×
77
      QtPassSettings::isTemplateAllFields());
×
78
  ui->checkBoxAutoPull->setChecked(QtPassSettings::isAutoPull());
×
79
  ui->checkBoxAutoPush->setChecked(QtPassSettings::isAutoPush());
×
80
  ui->checkBoxAlwaysOnTop->setChecked(QtPassSettings::isAlwaysOnTop());
×
81

82
#if defined(Q_OS_WIN)
83
  ui->checkBoxUseOtp->hide();
84
  ui->checkBoxUseQrencode->hide();
85
  ui->label_10->hide();
86
#endif
87

88
  if (!isPassOtpAvailable()) {
×
89
    ui->checkBoxUseOtp->setEnabled(false);
×
90
    ui->checkBoxUseOtp->setToolTip(
×
91
        tr("Pass OTP extension needs to be installed"));
×
92
  }
93

94
  if (!isQrencodeAvailable()) {
×
95
    ui->checkBoxUseQrencode->setEnabled(false);
×
96
    ui->checkBoxUseQrencode->setToolTip(tr("qrencode needs to be installed"));
×
97
  }
98

99
  setProfiles(QtPassSettings::getProfiles(), QtPassSettings::getProfile());
×
100
  setPwgenPath(QtPassSettings::getPwgenExecutable());
×
101
  setPasswordConfiguration(QtPassSettings::getPasswordConfiguration());
×
102

103
  usePass(QtPassSettings::isUsePass());
×
104
  useAutoclear(QtPassSettings::isUseAutoclear());
×
105
  useAutoclearPanel(QtPassSettings::isUseAutoclearPanel());
×
106
  useTrayIcon(QtPassSettings::isUseTrayIcon());
×
107
  useGit(QtPassSettings::isUseGit());
×
108

109
  useOtp(QtPassSettings::isUseOtp());
×
110
  useQrencode(QtPassSettings::isUseQrencode());
×
111

112
  usePwgen(QtPassSettings::isUsePwgen());
×
113
  useTemplate(QtPassSettings::isUseTemplate());
×
114

115
  ui->profileTable->verticalHeader()->hide();
×
116
  ui->profileTable->horizontalHeader()->setSectionResizeMode(
×
117
      1, QHeaderView::Stretch);
118
  ui->label->setText(ui->label->text() + VERSION);
×
119
  ui->comboBoxClipboard->clear();
×
120

121
  ui->comboBoxClipboard->addItem(tr("No Clipboard"));
×
122
  ui->comboBoxClipboard->addItem(tr("Always copy to clipboard"));
×
123
  ui->comboBoxClipboard->addItem(tr("On-demand copy to clipboard"));
×
124

125
  int currentIndex = QtPassSettings::getClipBoardTypeRaw();
×
126
  ui->comboBoxClipboard->setCurrentIndex(currentIndex);
×
127
  on_comboBoxClipboard_activated(currentIndex);
×
128

129
  QClipboard *clip = QApplication::clipboard();
×
130
  if (!clip->supportsSelection()) {
×
131
    useSelection(false);
×
132
    ui->checkBoxSelection->setVisible(false);
×
133
  } else {
134
    useSelection(QtPassSettings::isUseSelection());
×
135
  }
136

137
  if (!Util::configIsValid()) {
×
138
    // Show Programs tab, which is likely
139
    // what the user needs to fix now.
140
    ui->tabWidget->setCurrentIndex(1);
×
141
  }
142

143
  connect(ui->profileTable, &QTableWidget::itemChanged, this,
×
144
          &ConfigDialog::onProfileTableItemChanged);
×
145
  connect(this, &ConfigDialog::accepted, this, &ConfigDialog::on_accepted);
×
146
}
×
147

148
/**
149
 * @brief ConfigDialog::~ConfigDialog config destructor, makes sure the
150
 * mainWindow knows about git, gpg and pass executables.
151
 */
152
ConfigDialog::~ConfigDialog() {
×
153
  QtPassSettings::setGitExecutable(ui->gitPath->text());
×
154
  QtPassSettings::setGpgExecutable(ui->gpgPath->text());
×
155
  QtPassSettings::setPassExecutable(ui->passPath->text());
×
156
}
×
157

158
/**
159
 * @brief ConfigDialog::setGitPath set the git executable path.
160
 * Make sure the checkBoxUseGit is updated.
161
 * @param path
162
 */
163
void ConfigDialog::setGitPath(const QString &path) {
×
164
  ui->gitPath->setText(path);
×
165
  ui->checkBoxUseGit->setEnabled(!path.isEmpty());
×
166
  if (path.isEmpty()) {
×
167
    useGit(false);
×
168
  }
169
}
×
170

171
/**
172
 * @brief ConfigDialog::usePass set wether or not we want to use pass.
173
 * Update radio buttons accordingly.
174
 * @param usePass
175
 */
176
void ConfigDialog::usePass(bool usePass) {
×
177
  ui->radioButtonNative->setChecked(!usePass);
×
178
  ui->radioButtonPass->setChecked(usePass);
×
179
  setGroupBoxState();
×
180
}
×
181

182
/**
183
 * @brief Validates the configuration table fields and enables or disables the
184
 * OK button accordingly.
185
 * @example
186
 * ConfigDialog dialog;
187
 * QTableWidgetItem *item = dialog.findChild<QTableWidgetItem*>();
188
 * dialog.validate(item);
189
 *
190
 * @param QTableWidgetItem *item - The table item to validate; if null,
191
 * validates all relevant items in the profile table.
192
 * @return void - This function does not return a value.
193
 */
194
void ConfigDialog::validate(QTableWidgetItem *item) {
×
195
  bool status = true;
196

197
  if (item == nullptr) {
×
198
    for (int i = 0; i < ui->profileTable->rowCount(); i++) {
×
199
      for (int j = 0; j < ui->profileTable->columnCount(); j++) {
×
200
        QTableWidgetItem *_item = ui->profileTable->item(i, j);
×
201

202
        if (_item->text().isEmpty() && j != 2) {
×
203
          _item->setBackground(Qt::red);
×
204
          _item->setToolTip(tr("This field is required"));
×
205
          status = false;
206
          break;
207
        } else {
208
          _item->setBackground(QBrush());
×
209
          _item->setToolTip(QString());
×
210
        }
211
      }
212

213
      if (!status) {
214
        break;
215
      }
216
    }
217
  } else {
218
    if (item->text().isEmpty() && item->column() != 2) {
×
219
      item->setBackground(Qt::red);
×
220
      item->setToolTip(tr("This field is required"));
×
221
      status = false;
222
    } else {
223
      item->setBackground(QBrush());
×
224
      item->setToolTip(QString());
×
225
    }
226
  }
227

228
  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status);
×
229
}
×
230

231
/**
232
 * @brief Saves the configuration dialog settings to persistent application
233
 * settings.
234
 * @example
235
 * ConfigDialog dialog;
236
 * dialog.on_accepted();
237
 * // Expected output: All UI-selected configuration values are stored via
238
 * QtPassSettings.
239
 *
240
 * @return void - This method does not return a value.
241
 */
242
void ConfigDialog::on_accepted() {
×
243
  QtPassSettings::setPassExecutable(ui->passPath->text());
×
244
  QtPassSettings::setGitExecutable(ui->gitPath->text());
×
245
  QtPassSettings::setGpgExecutable(ui->gpgPath->text());
×
246
  QtPassSettings::setPassStore(
×
247
      Util::normalizeFolderPath(ui->storePath->text()));
×
248
  QtPassSettings::setUsePass(ui->radioButtonPass->isChecked());
×
249
  QtPassSettings::setClipBoardType(ui->comboBoxClipboard->currentIndex());
×
250
  QtPassSettings::setUseSelection(ui->checkBoxSelection->isChecked());
×
251
  QtPassSettings::setUseAutoclear(ui->checkBoxAutoclear->isChecked());
×
252
  QtPassSettings::setAutoclearSeconds(ui->spinBoxAutoclearSeconds->value());
×
253
  QtPassSettings::setUseAutoclearPanel(ui->checkBoxAutoclearPanel->isChecked());
×
254
  QtPassSettings::setAutoclearPanelSeconds(
×
255
      ui->spinBoxAutoclearPanelSeconds->value());
×
256
  QtPassSettings::setHidePassword(ui->checkBoxHidePassword->isChecked());
×
257
  QtPassSettings::setHideContent(ui->checkBoxHideContent->isChecked());
×
258
  QtPassSettings::setUseMonospace(ui->checkBoxUseMonospace->isChecked());
×
259
  QtPassSettings::setDisplayAsIs(ui->checkBoxDisplayAsIs->isChecked());
×
260
  QtPassSettings::setNoLineWrapping(ui->checkBoxNoLineWrapping->isChecked());
×
261
  QtPassSettings::setAddGPGId(ui->checkBoxAddGPGId->isChecked());
×
262
  QtPassSettings::setUseTrayIcon(ui->checkBoxUseTrayIcon->isEnabled() &&
×
263
                                 ui->checkBoxUseTrayIcon->isChecked());
×
264
  QtPassSettings::setHideOnClose(ui->checkBoxHideOnClose->isEnabled() &&
×
265
                                 ui->checkBoxHideOnClose->isChecked());
×
266
  QtPassSettings::setStartMinimized(ui->checkBoxStartMinimized->isEnabled() &&
×
267
                                    ui->checkBoxStartMinimized->isChecked());
×
268
  QtPassSettings::setProfiles(getProfiles());
×
269
  QtPassSettings::setUseGit(ui->checkBoxUseGit->isChecked());
×
270
  QtPassSettings::setUseOtp(ui->checkBoxUseOtp->isChecked());
×
271
  QtPassSettings::setUseQrencode(ui->checkBoxUseQrencode->isChecked());
×
272
  QtPassSettings::setPwgenExecutable(ui->pwgenPath->text());
×
273
  QtPassSettings::setUsePwgen(ui->checkBoxUsePwgen->isChecked());
×
274
  QtPassSettings::setAvoidCapitals(ui->checkBoxAvoidCapitals->isChecked());
×
275
  QtPassSettings::setAvoidNumbers(ui->checkBoxAvoidNumbers->isChecked());
×
276
  QtPassSettings::setLessRandom(ui->checkBoxLessRandom->isChecked());
×
277
  QtPassSettings::setUseSymbols(ui->checkBoxUseSymbols->isChecked());
×
278
  QtPassSettings::setPasswordConfiguration(getPasswordConfiguration());
×
279
  QtPassSettings::setUseTemplate(ui->checkBoxUseTemplate->isChecked());
×
280
  QtPassSettings::setPassTemplate(ui->plainTextEditTemplate->toPlainText());
×
281
  QtPassSettings::setTemplateAllFields(
×
282
      ui->checkBoxTemplateAllFields->isChecked());
×
283
  QtPassSettings::setAutoPush(ui->checkBoxAutoPush->isChecked());
×
284
  QtPassSettings::setAutoPull(ui->checkBoxAutoPull->isChecked());
×
285
  QtPassSettings::setAlwaysOnTop(ui->checkBoxAlwaysOnTop->isChecked());
×
286

287
  QtPassSettings::setVersion(VERSION);
×
288
}
×
289

290
/**
291
 * @brief Automatically detects required external binaries in the system PATH
292
 * and updates the dialog fields.
293
 * @example
294
 * ConfigDialog configDialog;
295
 * configDialog.on_autodetectButton_clicked();
296
 *
297
 * @return void - This function does not return a value.
298
 */
299
void ConfigDialog::on_autodetectButton_clicked() {
×
300
  QString pass = Util::findBinaryInPath("pass");
×
301
  if (!pass.isEmpty()) {
×
302
    ui->passPath->setText(pass);
×
303
  }
304
  usePass(!pass.isEmpty());
×
305
  QString gpg = Util::findBinaryInPath("gpg2");
×
306
  if (gpg.isEmpty()) {
×
307
    gpg = Util::findBinaryInPath("gpg");
×
308
  }
309
  if (!gpg.isEmpty()) {
×
310
    ui->gpgPath->setText(gpg);
×
311
  }
312
  QString git = Util::findBinaryInPath("git");
×
313
  if (!git.isEmpty()) {
×
314
    ui->gitPath->setText(git);
×
315
  }
316
  QString pwgen = Util::findBinaryInPath("pwgen");
×
317
  if (!pwgen.isEmpty()) {
×
318
    ui->pwgenPath->setText(pwgen);
×
319
  }
320
}
×
321

322
/**
323
 * @brief ConfigDialog::on_radioButtonNative_clicked wrapper for
324
 * ConfigDialog::setGroupBoxState()
325
 */
326
void ConfigDialog::on_radioButtonNative_clicked() { setGroupBoxState(); }
×
327

328
/**
329
 * @brief ConfigDialog::on_radioButtonPass_clicked wrapper for
330
 * ConfigDialog::setGroupBoxState()
331
 */
332
void ConfigDialog::on_radioButtonPass_clicked() { setGroupBoxState(); }
×
333

334
/**
335
 * @brief ConfigDialog::getSecretKeys get list of secret/private keys
336
 * @return QStringList keys
337
 */
338
auto ConfigDialog::getSecretKeys() -> QStringList {
×
339
  QList<UserInfo> keys = QtPassSettings::getPass()->listKeys("", true);
×
340
  QStringList names;
×
341

342
  if (keys.empty()) {
×
343
    return names;
344
  }
345

346
  foreach (const UserInfo &sec, keys)
×
347
    names << sec.name;
×
348

349
  return names;
×
350
}
351

352
/**
353
 * @brief ConfigDialog::setGroupBoxState update checkboxes.
354
 */
355
void ConfigDialog::setGroupBoxState() {
×
356
  bool state = ui->radioButtonPass->isChecked();
×
357
  ui->groupBoxNative->setEnabled(!state);
×
358
  ui->groupBoxPass->setEnabled(state);
×
NEW
359
  if (state) {
×
360
    // pass mode: disable all password generation controls
NEW
361
    ui->spinBoxPasswordLength->setEnabled(false);
×
NEW
362
    ui->checkBoxUsePwgen->setEnabled(false);
×
NEW
363
    ui->checkBoxAvoidCapitals->setEnabled(false);
×
NEW
364
    ui->checkBoxUseSymbols->setEnabled(false);
×
NEW
365
    ui->checkBoxLessRandom->setEnabled(false);
×
NEW
366
    ui->checkBoxAvoidNumbers->setEnabled(false);
×
NEW
367
    ui->labelPasswordChars->setEnabled(false);
×
NEW
368
    ui->passwordCharTemplateSelector->setEnabled(false);
×
NEW
369
    ui->lineEditPasswordChars->setEnabled(false);
×
370
  } else {
371
    // native mode: restore pwgen/charset state from existing handlers
NEW
372
    ui->spinBoxPasswordLength->setEnabled(true);
×
NEW
373
    ui->checkBoxUsePwgen->setEnabled(!ui->pwgenPath->text().isEmpty());
×
NEW
374
    on_checkBoxUsePwgen_clicked();
×
375
  }
UNCOV
376
}
×
377

378
/**
379
 * @brief ConfigDialog::selectExecutable pop-up to choose an executable.
380
 * @return
381
 */
382
auto ConfigDialog::selectExecutable() -> QString {
×
383
  QFileDialog dialog(this);
×
384
  dialog.setFileMode(QFileDialog::ExistingFile);
×
385
  dialog.setOption(QFileDialog::ReadOnly);
×
386
  if (dialog.exec()) {
×
387
    return dialog.selectedFiles().constFirst();
×
388
  }
389

390
  return {};
391
}
×
392

393
/**
394
 * @brief ConfigDialog::selectFolder pop-up to choose a folder.
395
 * @return
396
 */
397
auto ConfigDialog::selectFolder() -> QString {
×
398
  QFileDialog dialog(this);
×
399
  dialog.setFileMode(QFileDialog::Directory);
×
400
  dialog.setFilter(QDir::NoFilter);
×
401
  dialog.setOption(QFileDialog::ShowDirsOnly);
×
402
  if (dialog.exec()) {
×
403
    return dialog.selectedFiles().constFirst();
×
404
  }
405

406
  return {};
407
}
×
408

409
/**
410
 * @brief ConfigDialog::on_toolButtonGit_clicked get git application.
411
 * Enable checkboxes if found.
412
 */
413
void ConfigDialog::on_toolButtonGit_clicked() {
×
414
  QString git = selectExecutable();
×
415
  bool state = !git.isEmpty();
×
416
  if (state) {
×
417
    ui->gitPath->setText(git);
×
418
  } else {
419
    useGit(false);
×
420
  }
421

422
  ui->checkBoxUseGit->setEnabled(state);
×
423
}
×
424

425
/**
426
 * @brief ConfigDialog::on_toolButtonGpg_clicked get gpg application.
427
 */
428
void ConfigDialog::on_toolButtonGpg_clicked() {
×
429
  QString gpg = selectExecutable();
×
430
  if (!gpg.isEmpty()) {
×
431
    ui->gpgPath->setText(gpg);
×
432
  }
433
}
×
434

435
/**
436
 * @brief ConfigDialog::on_pushButtonGenerateKey_clicked open keygen dialog.
437
 */
438
void ConfigDialog::on_pushButtonGenerateKey_clicked() {
×
439
  KeygenDialog d(this);
×
440
  d.exec();
×
441
}
×
442

443
/**
444
 * @brief ConfigDialog::on_toolButtonPass_clicked get pass application.
445
 */
446
void ConfigDialog::on_toolButtonPass_clicked() {
×
447
  QString pass = selectExecutable();
×
448
  if (!pass.isEmpty()) {
×
449
    ui->passPath->setText(pass);
×
450
  }
451
}
×
452

453
/**
454
 * @brief ConfigDialog::on_toolButtonStore_clicked get .password-store
455
 * location.
456
 */
457
void ConfigDialog::on_toolButtonStore_clicked() {
×
458
  QString store = selectFolder();
×
459
  if (!store.isEmpty()) {
×
460
    ui->storePath->setText(store);
×
461
  }
462
}
×
463

464
/**
465
 * @brief ConfigDialog::on_comboBoxClipboard_activated show and hide options.
466
 * @param index of selectbox (0 = no clipboard).
467
 */
468
void ConfigDialog::on_comboBoxClipboard_activated(int index) {
×
469
  bool state = index > 0;
×
470

471
  ui->checkBoxSelection->setEnabled(state);
×
472
  ui->checkBoxAutoclear->setEnabled(state);
×
473
  ui->checkBoxHidePassword->setEnabled(state);
×
474
  ui->checkBoxHideContent->setEnabled(state);
×
475
  if (state) {
×
476
    ui->spinBoxAutoclearSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
477
    ui->labelSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
478
  } else {
479
    ui->spinBoxAutoclearSeconds->setEnabled(false);
×
480
    ui->labelSeconds->setEnabled(false);
×
481
  }
482
}
×
483

484
/**
485
 * @brief ConfigDialog::on_checkBoxAutoclearPanel_clicked enable and disable
486
 * options based on autoclear use.
487
 */
488
void ConfigDialog::on_checkBoxAutoclearPanel_clicked() {
×
489
  bool state = ui->checkBoxAutoclearPanel->isChecked();
×
490
  ui->spinBoxAutoclearPanelSeconds->setEnabled(state);
×
491
  ui->labelPanelSeconds->setEnabled(state);
×
492
}
×
493

494
/**
495
 * @brief ConfigDialog::useSelection set the clipboard type use from
496
 * MainWindow.
497
 * @param useSelection
498
 */
499
void ConfigDialog::useSelection(bool useSelection) {
×
500
  ui->checkBoxSelection->setChecked(useSelection);
×
501
  on_checkBoxSelection_clicked();
×
502
}
×
503

504
/**
505
 * @brief ConfigDialog::useAutoclear set the clipboard autoclear use from
506
 * MainWindow.
507
 * @param useAutoclear
508
 */
509
void ConfigDialog::useAutoclear(bool useAutoclear) {
×
510
  ui->checkBoxAutoclear->setChecked(useAutoclear);
×
511
  on_checkBoxAutoclear_clicked();
×
512
}
×
513

514
/**
515
 * @brief ConfigDialog::useAutoclearPanel set the panel autoclear use from
516
 * MainWindow.
517
 * @param useAutoclearPanel
518
 */
519
void ConfigDialog::useAutoclearPanel(bool useAutoclearPanel) {
×
520
  ui->checkBoxAutoclearPanel->setChecked(useAutoclearPanel);
×
521
  on_checkBoxAutoclearPanel_clicked();
×
522
}
×
523

524
/**
525
 * @brief ConfigDialog::on_checkBoxSelection_clicked checkbox clicked, update
526
 * state via ConfigDialog::on_comboBoxClipboard_activated
527
 */
528
void ConfigDialog::on_checkBoxSelection_clicked() {
×
529
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
530
}
×
531

532
/**
533
 * @brief ConfigDialog::on_checkBoxAutoclear_clicked checkbox clicked, update
534
 * state via ConfigDialog::on_comboBoxClipboard_activated
535
 */
536
void ConfigDialog::on_checkBoxAutoclear_clicked() {
×
537
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
538
}
×
539

540
/**
541
 * @brief ConfigDialog::genKey tunnel function to make MainWindow generate a
542
 * gpg key pair.
543
 * @todo refactor the process to not be entangled so much.
544
 * @param batch
545
 * @param dialog
546
 */
547
void ConfigDialog::genKey(const QString &batch, QDialog *dialog) {
×
548
  mainWindow->generateKeyPair(batch, dialog);
×
549
}
×
550

551
/**
552
 * @brief ConfigDialog::setProfiles set the profiles and chosen profile from
553
 * MainWindow.
554
 * @param profiles
555
 * @param profile
556
 */
557
void ConfigDialog::setProfiles(QHash<QString, QHash<QString, QString>> profiles,
×
558
                               const QString &currentProfile) {
559
  if (profiles.contains("")) {
×
560
    profiles.remove("");
×
561
    // remove weird "" key value pairs
562
  }
563

564
  ui->profileTable->setRowCount(profiles.count());
×
565
  QHashIterator<QString, QHash<QString, QString>> i(profiles);
×
566
  int n = 0;
567
  while (i.hasNext()) {
×
568
    i.next();
569
    if (!i.value().isEmpty() && !i.key().isEmpty()) {
×
570
      ui->profileTable->setItem(n, 0, new QTableWidgetItem(i.key()));
×
571
      ui->profileTable->setItem(n, 1,
×
572
                                new QTableWidgetItem(i.value().value("path")));
×
573
      ui->profileTable->setItem(
×
574
          n, 2, new QTableWidgetItem(i.value().value("signingKey")));
×
575
      if (i.key() == currentProfile) {
×
576
        ui->profileTable->selectRow(n);
×
577
      }
578
    }
579
    ++n;
×
580
  }
581
}
×
582

583
/**
584
 * @brief ConfigDialog::getProfiles return profile list.
585
 * @return
586
 */
587
auto ConfigDialog::getProfiles() -> QHash<QString, QHash<QString, QString>> {
×
588
  QHash<QString, QHash<QString, QString>> profiles;
×
589
  // Check?
590
  for (int i = 0; i < ui->profileTable->rowCount(); ++i) {
×
591
    QHash<QString, QString> profile;
×
592
    QTableWidgetItem *pathItem = ui->profileTable->item(i, 1);
×
593
    if (nullptr != pathItem) {
×
594
      QTableWidgetItem *item = ui->profileTable->item(i, 0);
×
595
      if (item == nullptr) {
×
596
        continue;
597
      }
598
      profile["path"] = pathItem->text();
×
599
      QTableWidgetItem *signingKeyItem = ui->profileTable->item(i, 2);
×
600
      if (nullptr != signingKeyItem) {
×
601
        profile["signingKey"] = signingKeyItem->text();
×
602
      }
603
      profiles.insert(item->text(), profile);
×
604
    }
605
  }
×
606
  return profiles;
×
607
}
×
608

609
/**
610
 * @brief ConfigDialog::on_addButton_clicked add a profile row.
611
 */
612
void ConfigDialog::on_addButton_clicked() {
×
613
  int n = ui->profileTable->rowCount();
×
614
  ui->profileTable->insertRow(n);
×
615
  ui->profileTable->setItem(n, 0, new QTableWidgetItem());
×
616
  ui->profileTable->setItem(n, 1, new QTableWidgetItem(ui->storePath->text()));
×
617
  ui->profileTable->setItem(n, 2, new QTableWidgetItem());
×
618
  ui->profileTable->selectRow(n);
×
619
  ui->deleteButton->setEnabled(true);
×
620

621
  validate();
×
622
}
×
623

624
/**
625
 * @brief ConfigDialog::on_deleteButton_clicked remove a profile row.
626
 */
627
void ConfigDialog::on_deleteButton_clicked() {
×
628
  QSet<int> selectedRows; //  we use a set to prevent doubles
629
  QList<QTableWidgetItem *> itemList = ui->profileTable->selectedItems();
×
630
  if (itemList.count() == 0) {
×
631
    QMessageBox::warning(this, tr("No profile selected"),
×
632
                         tr("No profile selected to delete"));
×
633
    return;
634
  }
635
  QTableWidgetItem *item;
636
  foreach (item, itemList)
×
637
    selectedRows.insert(item->row());
×
638
  // get a list, and sort it big to small
639
  QList<int> rows = selectedRows.values();
×
640
  std::sort(rows.begin(), rows.end());
×
641
  // now actually do the removing:
642
  foreach (int row, rows)
×
643
    ui->profileTable->removeRow(row);
×
644
  if (ui->profileTable->rowCount() < 1) {
×
645
    ui->deleteButton->setEnabled(false);
×
646
  }
647

648
  validate();
×
649
}
650

651
/**
652
 * @brief ConfigDialog::criticalMessage wrapper for showing critical messages
653
 * in a popup.
654
 * @param title
655
 * @param text
656
 */
657
void ConfigDialog::criticalMessage(const QString &title, const QString &text) {
×
658
  QMessageBox::critical(this, title, text, QMessageBox::Ok, QMessageBox::Ok);
×
659
}
×
660

661
/**
662
 * @brief Checks whether the qrencode executable is available on the system.
663
 * @example
664
 * bool result = ConfigDialog::isQrencodeAvailable();
665
 * std::cout << result << std::endl; // Expected output: true if qrencode is
666
 * found, otherwise false
667
 *
668
 * @return bool - True if qrencode is available; otherwise false. On Windows,
669
 * always returns false.
670
 */
671
auto ConfigDialog::isQrencodeAvailable() -> bool {
×
672
#ifdef Q_OS_WIN
673
  return false;
674
#else
675
  QProcess which;
×
676
  which.start("which", QStringList() << "qrencode");
×
677
  which.waitForFinished();
×
678
  QtPassSettings::setQrencodeExecutable(
×
679
      which.readAllStandardOutput().trimmed());
×
680
  return which.exitCode() == 0;
×
681
#endif
682
}
×
683

684
auto ConfigDialog::isPassOtpAvailable() -> bool {
×
685
#ifdef Q_OS_WIN
686
  return false;
687
#else
688
  QProcess pass;
×
689
  pass.start(QtPassSettings::getPassExecutable(), QStringList() << "otp"
×
690
                                                                << "--help");
×
691
  pass.waitForFinished(2000);
×
692
  return pass.exitCode() == 0;
×
693
#endif
694
}
×
695

696
/**
697
 * @brief ConfigDialog::wizard first-time use wizard.
698
 * @todo make this thing more reliable.
699
 */
700
void ConfigDialog::wizard() {
×
701
  (void)Util::configIsValid();
×
702
  on_autodetectButton_clicked();
×
703

704
  if (!checkGpgExistence()) {
×
705
    return;
706
  }
707
  if (!checkSecretKeys()) {
×
708
    return;
709
  }
710
  if (!checkPasswordStore()) {
×
711
    return;
712
  }
713
  handleGpgIdFile();
×
714

715
  ui->checkBoxHidePassword->setCheckState(Qt::Checked);
×
716
}
717

718
/**
719
 * @brief Checks whether the configured GnuPG executable exists.
720
 * @example
721
 * bool result = ConfigDialog::checkGpgExistence();
722
 * std::cout << result << std::endl; // Expected output: true if GnuPG is found,
723
 * false otherwise
724
 *
725
 * @return bool - True if the GnuPG path is valid or uses a WSL command prefix;
726
 * false if the executable cannot be found and an error message is shown.
727
 */
728
auto ConfigDialog::checkGpgExistence() -> bool {
×
729
  QString gpg = ui->gpgPath->text();
×
730
  if (!gpg.startsWith("wsl ") && !QFile(gpg).exists()) {
×
731
    criticalMessage(
×
732
        tr("GnuPG not found"),
×
733
#ifdef Q_OS_WIN
734
#ifdef WINSTORE
735
        tr("Please install GnuPG on your system.<br>Install "
736
           "<strong>Ubuntu</strong> from the Microsoft Store to get it.<br>"
737
           "If you already did so, make sure you started it once and<br>"
738
           "click \"Autodetect\" in the next dialog.")
739
#else
740
        tr("Please install GnuPG on your system.<br>Install "
741
           "<strong>Ubuntu</strong> from the Microsoft Store<br>or <a "
742
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
743
           "from GnuPG.org")
744
#endif
745
#else
746
        tr("Please install GnuPG on your system.<br>Install "
×
747
           "<strong>gpg</strong> using your favorite package manager<br>or "
748
           "<a "
749
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
750
           "from GnuPG.org")
751
#endif
752
    );
753
    return false;
×
754
  }
755
  return true;
756
}
757

758
/**
759
 * @brief Checks whether secret keys are available and, if needed, prompts the
760
 * user to generate them.
761
 * @example
762
 * bool result = ConfigDialog.checkSecretKeys();
763
 * std::cout << result << std::endl; // Expected output: true if keys are
764
 * present or key generation dialog is accepted, false otherwise
765
 *
766
 * @return bool - Returns true when secret keys are already available or the key
767
 * generation dialog is accepted; false if the dialog is rejected.
768
 */
769
auto ConfigDialog::checkSecretKeys() -> bool {
×
770
  QString gpg = ui->gpgPath->text();
×
771
  QStringList names = getSecretKeys();
×
772

773
#ifdef QT_DEBUG
774
  dbg() << names;
775
#endif
776

777
  if ((gpg.startsWith("wsl ") || QFile(gpg).exists()) && names.empty()) {
×
778
    KeygenDialog d(this);
×
779
    return d.exec();
×
780
  }
×
781
  return true;
782
}
783

784
/**
785
 * @brief Checks whether the password-store path exists and prompts the user to
786
 * create it if it does not.
787
 * @example
788
 * bool result = ConfigDialog::checkPasswordStore();
789
 * std::cout << std::boolalpha << result << std::endl; // Expected output: true
790
 * if the store exists or is created successfully, false if creation fails
791
 *
792
 * @return bool - True if the password-store exists or is successfully created,
793
 * false if creation fails.
794
 */
795
auto ConfigDialog::checkPasswordStore() -> bool {
×
796
  QString passStore = ui->storePath->text();
×
797

798
  if (!QFile(passStore).exists()) {
×
799
    if (QMessageBox::question(
×
800
            this, tr("Create password-store?"),
×
801
            tr("Would you like to create a password-store at %1?")
×
802
                .arg(passStore),
×
803
            QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
804
      if (!QDir().mkdir(passStore)) {
×
805
        QMessageBox::warning(
×
806
            this, tr("Error"),
×
807
            tr("Failed to create password-store at: %1").arg(passStore));
×
808
        return false;
×
809
      }
810
#ifdef Q_OS_WIN
811
      SetFileAttributes(passStore.toStdWString().c_str(),
812
                        FILE_ATTRIBUTE_HIDDEN);
813
#endif
814
      if (ui->checkBoxUseGit->isChecked()) {
×
815
        emit mainWindow->passGitInitNeeded();
×
816
      }
817
      mainWindow->userDialog(passStore);
×
818
    }
819
  }
820
  return true;
821
}
822

823
/**
824
 * @brief Handles selection and validation of the password store's .gpg-id file.
825
 * @example
826
 * ConfigDialog dialog;
827
 * dialog.handleGpgIdFile();
828
 *
829
 * @return void - This method does not return a value; it updates the UI flow
830
 * and may prompt the user to choose a valid password store.
831
 */
832
void ConfigDialog::handleGpgIdFile() {
×
833
  QString passStore = ui->storePath->text();
×
834
  if (!QFile(QDir(passStore).filePath(".gpg-id")).exists()) {
×
835
#ifdef QT_DEBUG
836
    dbg() << ".gpg-id file does not exist";
837
#endif
838
    criticalMessage(tr("Password store not initialised"),
×
839
                    tr("The folder %1 doesn't seem to be a password store or "
×
840
                       "is not yet initialised.")
841
                        .arg(passStore));
×
842

843
    while (!QFile(passStore).exists()) {
×
844
      on_toolButtonStore_clicked();
×
845
      if (passStore == ui->storePath->text()) {
×
846
        return;
847
      }
848
      passStore = ui->storePath->text();
×
849
    }
850
    if (!QFile(passStore + ".gpg-id").exists()) {
×
851
#ifdef QT_DEBUG
852
      dbg() << ".gpg-id file still does not exist :/";
853
#endif
854
      mainWindow->userDialog(passStore);
×
855
    }
856
  }
857
}
858

859
/**
860
 * @brief ConfigDialog::useTrayIcon set preference for using trayicon.
861
 * Enable or disable related checkboxes accordingly.
862
 * @param useSystray
863
 */
864
void ConfigDialog::useTrayIcon(bool useSystray) {
×
865
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
×
866
    ui->checkBoxUseTrayIcon->setChecked(useSystray);
×
867
    ui->checkBoxHideOnClose->setEnabled(useSystray);
×
868
    ui->checkBoxStartMinimized->setEnabled(useSystray);
×
869

870
    if (!useSystray) {
×
871
      ui->checkBoxHideOnClose->setChecked(false);
×
872
      ui->checkBoxStartMinimized->setChecked(false);
×
873
    }
874
  }
875
}
×
876

877
/**
878
 * @brief ConfigDialog::on_checkBoxUseTrayIcon_clicked enable and disable
879
 * related checkboxes.
880
 */
881
void ConfigDialog::on_checkBoxUseTrayIcon_clicked() {
×
882
  bool state = ui->checkBoxUseTrayIcon->isChecked();
×
883
  ui->checkBoxHideOnClose->setEnabled(state);
×
884
  ui->checkBoxStartMinimized->setEnabled(state);
×
885
}
×
886

887
/**
888
 * @brief ConfigDialog::closeEvent close this window.
889
 * @param event
890
 */
891
void ConfigDialog::closeEvent(QCloseEvent *event) {
×
892
  QtPassSettings::setDialogGeometry("configDialog", saveGeometry());
×
893
  if (!isMaximized()) {
×
894
    QtPassSettings::setDialogPos("configDialog", pos());
×
895
    QtPassSettings::setDialogSize("configDialog", size());
×
896
  }
897
  QtPassSettings::setDialogMaximized("configDialog", isMaximized());
×
898
  event->accept();
899
}
×
900

901
/**
902
 * @brief ConfigDialog::useGit set preference for using git.
903
 * @param useGit
904
 */
905
void ConfigDialog::useGit(bool useGit) {
×
906
  ui->checkBoxUseGit->setChecked(useGit);
×
907
  on_checkBoxUseGit_clicked();
×
908
}
×
909

910
/**
911
 * @brief ConfigDialog::useOtp set preference for using otp plugin.
912
 * @param useOtp
913
 */
914
void ConfigDialog::useOtp(bool useOtp) {
×
915
  ui->checkBoxUseOtp->setChecked(useOtp);
×
916
}
×
917

918
/**
919
 * @brief ConfigDialog::useQrencode set preference for using qrencode plugin.
920
 * @param useQrencode
921
 */
922
void ConfigDialog::useQrencode(bool useQrencode) {
×
923
  ui->checkBoxUseQrencode->setChecked(useQrencode);
×
924
}
×
925

926
/**
927
 * @brief ConfigDialog::on_checkBoxUseGit_clicked enable or disable related
928
 * checkboxes.
929
 */
930
void ConfigDialog::on_checkBoxUseGit_clicked() {
×
931
  ui->checkBoxAddGPGId->setEnabled(ui->checkBoxUseGit->isChecked());
×
932
  ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
×
933
  ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
×
934
}
×
935

936
/**
937
 * @brief ConfigDialog::on_toolButtonPwgen_clicked enable or disable related
938
 * options in the interface.
939
 */
940
void ConfigDialog::on_toolButtonPwgen_clicked() {
×
941
  QString pwgen = selectExecutable();
×
942
  if (!pwgen.isEmpty()) {
×
943
    ui->pwgenPath->setText(pwgen);
×
944
    ui->checkBoxUsePwgen->setEnabled(true);
×
945
  } else {
946
    ui->checkBoxUsePwgen->setEnabled(false);
×
947
    ui->checkBoxUsePwgen->setChecked(false);
×
948
  }
949
}
×
950

951
/**
952
 * @brief ConfigDialog::setPwgenPath set pwgen executable path.
953
 * Enable or disable related options in the interface.
954
 * @param pwgen
955
 */
956
void ConfigDialog::setPwgenPath(const QString &pwgen) {
×
957
  ui->pwgenPath->setText(pwgen);
×
958
  if (pwgen.isEmpty()) {
×
959
    ui->checkBoxUsePwgen->setChecked(false);
×
960
    ui->checkBoxUsePwgen->setEnabled(false);
×
961
  }
962
  on_checkBoxUsePwgen_clicked();
×
963
}
×
964

965
/**
966
 * @brief ConfigDialog::on_checkBoxUsePwgen_clicked enable or disable related
967
 * options in the interface.
968
 */
969
void ConfigDialog::on_checkBoxUsePwgen_clicked() {
×
NEW
970
  if (ui->radioButtonPass->isChecked())
×
971
    return;
972
  bool usePwgen = ui->checkBoxUsePwgen->isChecked();
×
973
  ui->checkBoxAvoidCapitals->setEnabled(usePwgen);
×
974
  ui->checkBoxAvoidNumbers->setEnabled(usePwgen);
×
975
  ui->checkBoxLessRandom->setEnabled(usePwgen);
×
976
  ui->checkBoxUseSymbols->setEnabled(usePwgen);
×
977
  ui->lineEditPasswordChars->setEnabled(!usePwgen);
×
978
  ui->labelPasswordChars->setEnabled(!usePwgen);
×
979
  ui->passwordCharTemplateSelector->setEnabled(!usePwgen);
×
980
}
981

982
/**
983
 * @brief ConfigDialog::usePwgen set preference for using pwgen (can be
984
 * overruled by empty pwgenPath).
985
 * enable or disable related options in the interface via
986
 * ConfigDialog::on_checkBoxUsePwgen_clicked
987
 * @param usePwgen
988
 */
989
void ConfigDialog::usePwgen(bool usePwgen) {
×
990
  if (ui->pwgenPath->text().isEmpty()) {
×
991
    usePwgen = false;
992
  }
993
  ui->checkBoxUsePwgen->setChecked(usePwgen);
×
994
  on_checkBoxUsePwgen_clicked();
×
995
}
×
996

997
void ConfigDialog::setPasswordConfiguration(
×
998
    const PasswordConfiguration &config) {
999
  ui->spinBoxPasswordLength->setValue(config.length);
×
1000
  ui->passwordCharTemplateSelector->setCurrentIndex(config.selected);
×
1001
  if (config.selected != PasswordConfiguration::CUSTOM) {
×
1002
    ui->lineEditPasswordChars->setEnabled(false);
×
1003
  }
1004
  ui->lineEditPasswordChars->setText(config.Characters[config.selected]);
×
1005
}
×
1006

1007
auto ConfigDialog::getPasswordConfiguration() -> PasswordConfiguration {
×
1008
  PasswordConfiguration config;
×
1009
  config.length = ui->spinBoxPasswordLength->value();
×
1010
  config.selected = static_cast<PasswordConfiguration::characterSet>(
×
1011
      ui->passwordCharTemplateSelector->currentIndex());
×
1012
  config.Characters[PasswordConfiguration::CUSTOM] =
1013
      ui->lineEditPasswordChars->text();
×
1014
  return config;
×
1015
}
×
1016

1017
/**
1018
 * @brief ConfigDialog::on_passwordCharTemplateSelector_activated sets the
1019
 * passwordChar Template
1020
 * combo box to the desired entry
1021
 * @param entry of
1022
 */
1023
void ConfigDialog::on_passwordCharTemplateSelector_activated(int index) {
×
1024
  ui->lineEditPasswordChars->setText(
×
1025
      QtPassSettings::getPasswordConfiguration().Characters[index]);
×
1026
  if (index == PasswordConfiguration::CUSTOM) {
×
1027
    ui->lineEditPasswordChars->setEnabled(true);
×
1028
  } else {
1029
    ui->lineEditPasswordChars->setEnabled(false);
×
1030
  }
1031
}
×
1032

1033
/**
1034
 * @brief ConfigDialog::on_checkBoxUseTemplate_clicked enable or disable the
1035
 * template field and options.
1036
 */
1037
void ConfigDialog::on_checkBoxUseTemplate_clicked() {
×
1038
  ui->plainTextEditTemplate->setEnabled(ui->checkBoxUseTemplate->isChecked());
×
1039
  ui->checkBoxTemplateAllFields->setEnabled(
×
1040
      ui->checkBoxUseTemplate->isChecked());
×
1041
}
×
1042

1043
void ConfigDialog::onProfileTableItemChanged(QTableWidgetItem *item) {
×
1044
  validate(item);
×
1045
}
×
1046

1047
/**
1048
 * @brief ConfigDialog::useTemplate set preference for using templates.
1049
 * @param useTemplate
1050
 */
1051
void ConfigDialog::useTemplate(bool useTemplate) {
×
1052
  ui->checkBoxUseTemplate->setChecked(useTemplate);
×
1053
  on_checkBoxUseTemplate_clicked();
×
1054
}
×
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