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

IJHack / QtPass / 25612087677

09 May 2026 09:20PM UTC coverage: 28.263% (-0.05%) from 28.31%
25612087677

push

github

web-flow
fix(ssh-auth-sock): warn when override path looks bogus (#1439)

* fix(ssh-auth-sock): warn user when override path looks bogus

Two reviewer findings on the SSH_AUTH_SOCK override path (post-merge of
#1438):

- src/util.h: doc tweak — clarify that the "no validation" line refers
  to the runtime environment-setup step. The Settings dialog now does a
  soft check at save time.

- src/configdialog.cpp: when the user types a non-empty override and
  saves, do a soft check before writing the setting:
  - QFileInfo::exists() and isReadable()
  - on Unix, stat() + S_ISSOCK to confirm it's a Unix domain socket
    (Windows ssh-agent uses a named pipe, so we skip the socket check
    there)

  When any check fails, show a non-blocking QMessageBox::warning telling
  the user the path looks invalid and that the value will still be saved
  as entered. This preserves the "QtPass trusts the user choice" stance
  (e.g. user might start their agent after launching QtPass) while
  giving immediate feedback on typos. The runtime probe in
  Util::initialiseSshAuthSock() is unchanged — it still uses the
  override verbatim, no ssh-add validation, matching the existing
  contract.

No new tests: the warning is a UI-side soft check on data the user just
typed, with no side effects beyond the dialog. The existing
initialiseSshAuthSockOverrideSkipsAgentValidation test continues to
verify the runtime "no validation" contract.

lupdate refreshed the .ts files to pick up the four new tr() strings
(dialog title + 3 reasons + body template).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: apply CodeRabbit auto-fixes

Fixed 17 file(s) based on 18 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>

* i18n: drop generated ui_*.h locations + 3 reviewer fixes

distclean+qmake6 regenerated the .ts files without the spurious
location pointers into generated headers (ui_configdialog.h,
ui_mainwindow.h, etc.) that lupdate had ca... (continued)

0 of 19 new or added lines in 1 file covered. (0.0%)

9 existing lines in 1 file now uncovered.

1888 of 6680 relevant lines covered (28.26%)

27.11 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 "profileinit.h"
7
#include "qtpasssettings.h"
8
#include "ui_configdialog.h"
9
#include "usersdialog.h"
10
#include "util.h"
11
#include <QClipboard>
12
#include <QDir>
13
#include <QFileDialog>
14
#include <QFileInfo>
15
#include <QMessageBox>
16
#include <QPushButton>
17
#include <QSystemTrayIcon>
18
#include <QTableWidgetItem>
19
#include <utility>
20
#ifdef Q_OS_WIN
21
#include <windows.h>
22
#endif
23

24
#ifdef Q_OS_UNIX
25
#include <sys/stat.h>
26
#endif
27

28
#ifdef QT_DEBUG
29
#include "debughelper.h"
30
#endif
31

32
/**
33
 * @brief ConfigDialog::ConfigDialog this sets up the configuration screen.
34
 * @param parent
35
 */
36
ConfigDialog::ConfigDialog(MainWindow *parent)
×
37
    : QDialog(parent), ui(new Ui::ConfigDialog) {
×
38
  mainWindow = parent;
×
39
  ui->setupUi(this);
×
40

41
  // Restore dialog state
42
  QByteArray savedGeometry = QtPassSettings::getDialogGeometry("configDialog");
×
43
  bool hasSavedGeometry = !savedGeometry.isEmpty();
44
  if (hasSavedGeometry) {
×
45
    restoreGeometry(savedGeometry);
×
46
  }
47
  if (QtPassSettings::isDialogMaximized("configDialog")) {
×
48
    showMaximized();
×
49
  } else if (!hasSavedGeometry) {
50
    // Let window manager handle positioning for first launch
51
  }
52

53
  ui->passPath->setText(QtPassSettings::getPassExecutable());
×
54
  setGitPath(QtPassSettings::getGitExecutable());
×
55
  ui->gpgPath->setText(QtPassSettings::getGpgExecutable());
×
56
  ui->storePath->setText(QtPassSettings::getPassStore());
×
57
  ui->sshAuthSockOverride->setText(QtPassSettings::getSshAuthSockOverride());
×
58

59
  ui->spinBoxAutoclearSeconds->setValue(QtPassSettings::getAutoclearSeconds());
×
60
  ui->spinBoxAutoclearPanelSeconds->setValue(
×
61
      QtPassSettings::getAutoclearPanelSeconds());
×
62
  ui->checkBoxHidePassword->setChecked(QtPassSettings::isHidePassword());
×
63
  ui->checkBoxHideContent->setChecked(QtPassSettings::isHideContent());
×
64
  ui->checkBoxUseMonospace->setChecked(QtPassSettings::isUseMonospace());
×
65
  ui->checkBoxDisplayAsIs->setChecked(QtPassSettings::isDisplayAsIs());
×
66
  ui->checkBoxNoLineWrapping->setChecked(QtPassSettings::isNoLineWrapping());
×
67
  ui->checkBoxAddGPGId->setChecked(QtPassSettings::isAddGPGId(true));
×
68

69
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
×
70
    ui->checkBoxHideOnClose->setChecked(QtPassSettings::isHideOnClose());
×
71
    ui->checkBoxStartMinimized->setChecked(QtPassSettings::isStartMinimized());
×
72
  } else {
73
    ui->checkBoxUseTrayIcon->setEnabled(false);
×
74
    ui->checkBoxUseTrayIcon->setToolTip(tr("System tray is not available"));
×
75
    ui->checkBoxHideOnClose->setEnabled(false);
×
76
    ui->checkBoxStartMinimized->setEnabled(false);
×
77
  }
78

79
  ui->checkBoxAvoidCapitals->setChecked(QtPassSettings::isAvoidCapitals());
×
80
  ui->checkBoxAvoidNumbers->setChecked(QtPassSettings::isAvoidNumbers());
×
81
  ui->checkBoxLessRandom->setChecked(QtPassSettings::isLessRandom());
×
82
  ui->checkBoxUseSymbols->setChecked(QtPassSettings::isUseSymbols());
×
83
  ui->plainTextEditTemplate->setPlainText(QtPassSettings::getPassTemplate());
×
84
  ui->checkBoxTemplateAllFields->setChecked(
×
85
      QtPassSettings::isTemplateAllFields());
×
86
  ui->checkBoxShowProcessOutput->setChecked(
×
87
      QtPassSettings::isShowProcessOutput());
×
88
  ui->checkBoxAutoPull->setChecked(QtPassSettings::isAutoPull());
×
89
  ui->checkBoxAutoPush->setChecked(QtPassSettings::isAutoPush());
×
90
  ui->checkBoxAlwaysOnTop->setChecked(QtPassSettings::isAlwaysOnTop());
×
91

92
#if defined(Q_OS_WIN)
93
  ui->checkBoxUseOtp->hide();
94
  ui->checkBoxUseQrencode->hide();
95
  ui->label_10->hide();
96
#endif
97

98
  if (!isPassOtpAvailable()) {
×
99
    ui->checkBoxUseOtp->setEnabled(false);
×
100
    ui->checkBoxUseOtp->setToolTip(
×
101
        tr("Pass OTP extension needs to be installed"));
×
102
  }
103

104
  if (!isQrencodeAvailable()) {
×
105
    ui->checkBoxUseQrencode->setEnabled(false);
×
106
    ui->checkBoxUseQrencode->setToolTip(tr("qrencode needs to be installed"));
×
107
  }
108

109
  usePass(QtPassSettings::isUsePass());
×
110
  useAutoclear(QtPassSettings::isUseAutoclear());
×
111
  useAutoclearPanel(QtPassSettings::isUseAutoclearPanel());
×
112
  useTrayIcon(QtPassSettings::isUseTrayIcon());
×
113

114
  setProfiles(QtPassSettings::getProfiles(), QtPassSettings::getProfile());
×
115

116
  useGit(QtPassSettings::isUseGit());
×
117

118
  useOtp(QtPassSettings::isUseOtp());
×
119
  useGrepSearch(QtPassSettings::isUseGrepSearch());
×
120
  useQrencode(QtPassSettings::isUseQrencode());
×
121

122
  usePwgen(QtPassSettings::isUsePwgen());
×
123
  useTemplate(QtPassSettings::isUseTemplate());
×
124

125
  ui->profileTable->verticalHeader()->hide();
×
126
  ui->profileTable->horizontalHeader()->setSectionResizeMode(
×
127
      1, QHeaderView::Stretch);
128
  ui->label->setText(ui->label->text() + VERSION);
×
129
  ui->comboBoxClipboard->clear();
×
130

131
  ui->comboBoxClipboard->addItem(tr("No Clipboard"));
×
132
  ui->comboBoxClipboard->addItem(tr("Always copy to clipboard"));
×
133
  ui->comboBoxClipboard->addItem(tr("On-demand copy to clipboard"));
×
134

135
  int currentIndex = QtPassSettings::getClipBoardTypeRaw();
×
136
  ui->comboBoxClipboard->setCurrentIndex(currentIndex);
×
137
  on_comboBoxClipboard_activated(currentIndex);
×
138

139
  QClipboard *clip = QApplication::clipboard();
×
140
  if (!clip->supportsSelection()) {
×
141
    useSelection(false);
×
142
    ui->checkBoxSelection->setVisible(false);
×
143
  } else {
144
    useSelection(QtPassSettings::isUseSelection());
×
145
  }
146

147
  if (!Util::configIsValid()) {
×
148
    // Show Programs tab, which is likely
149
    // what the user needs to fix now.
150
    ui->tabWidget->setCurrentIndex(1);
×
151
  }
152

153
  connect(ui->profileTable, &QTableWidget::itemChanged, this,
×
154
          &ConfigDialog::onProfileTableItemChanged);
×
155
  connect(ui->profileTable, &QTableWidget::itemSelectionChanged, this,
×
156
          &ConfigDialog::onProfileTableSelectionChanged);
×
157
  connect(this, &ConfigDialog::accepted, this, &ConfigDialog::on_accepted);
×
158
}
×
159

160
/**
161
 * @brief ConfigDialog::~ConfigDialog config destructor, makes sure the
162
 * mainWindow knows about git, gpg and pass executables.
163
 */
164
ConfigDialog::~ConfigDialog() {
×
165
  QtPassSettings::setGitExecutable(ui->gitPath->text());
×
166
  QtPassSettings::setGpgExecutable(ui->gpgPath->text());
×
167
  QtPassSettings::setPassExecutable(ui->passPath->text());
×
168
}
×
169

170
/**
171
 * @brief ConfigDialog::setGitPath set the git executable path.
172
 * Make sure the checkBoxUseGit is updated.
173
 * @param path
174
 */
175
void ConfigDialog::setGitPath(const QString &path) {
×
176
  ui->gitPath->setText(path);
×
177
  ui->checkBoxUseGit->setEnabled(!path.isEmpty());
×
178
  if (path.isEmpty()) {
×
179
    useGit(false);
×
180
  }
181
}
×
182

183
/**
184
 * @brief ConfigDialog::usePass set whether or not we want to use pass.
185
 * Update radio buttons accordingly.
186
 * @param usePass
187
 */
188
void ConfigDialog::usePass(bool usePass) {
×
189
  ui->radioButtonNative->setChecked(!usePass);
×
190
  ui->radioButtonPass->setChecked(usePass);
×
191
  setGroupBoxState();
×
192
}
×
193

194
/**
195
 * @brief Validates the configuration table fields and enables or disables the
196
 * OK button accordingly.
197
 * @example
198
 * ConfigDialog dialog;
199
 * QTableWidgetItem *item = dialog.findChild<QTableWidgetItem*>();
200
 * dialog.validate(item);
201
 *
202
 * @param QTableWidgetItem *item - The table item to validate; if null,
203
 * validates all relevant items in the profile table.
204
 * @return void - This function does not return a value.
205
 */
206
void ConfigDialog::validate(QTableWidgetItem *item) {
×
207
  bool status = true;
208

209
  if (item == nullptr) {
×
210
    for (int i = 0; i < ui->profileTable->rowCount(); i++) {
×
211
      for (int j = 0; j < ui->profileTable->columnCount(); j++) {
×
212
        QTableWidgetItem *_item = ui->profileTable->item(i, j);
×
213

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

225
      if (!status) {
226
        break;
227
      }
228
    }
229
  } else {
230
    if (item->text().isEmpty() && item->column() != 2) {
×
231
      item->setBackground(Qt::red);
×
232
      item->setToolTip(tr("This field is required"));
×
233
      status = false;
234
    } else {
235
      item->setBackground(QBrush());
×
236
      item->setToolTip(QString());
×
237
    }
238
  }
239

240
  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status);
×
241
}
×
242

243
/**
244
 * @brief Saves the configuration dialog settings to persistent application
245
 * settings.
246
 * @example
247
 * ConfigDialog dialog;
248
 * dialog.on_accepted();
249
 * // Expected output: All UI-selected configuration values are stored via
250
 * QtPassSettings.
251
 *
252
 * @return void - This method does not return a value.
253
 */
254
void ConfigDialog::on_accepted() {
×
255
  QtPassSettings::setPassExecutable(ui->passPath->text());
×
256
  QtPassSettings::setGitExecutable(ui->gitPath->text());
×
257
  QtPassSettings::setGpgExecutable(ui->gpgPath->text());
×
NEW
258
  const QString sshAuthSockOverride = ui->sshAuthSockOverride->text().trimmed();
×
NEW
259
  if (!sshAuthSockOverride.isEmpty()) {
×
NEW
260
    QString reason;
×
NEW
261
    QFileInfo fi(sshAuthSockOverride);
×
NEW
262
    if (!fi.exists()) {
×
NEW
263
      reason = tr("The path does not exist.");
×
NEW
264
    } else if (!fi.isReadable()) {
×
NEW
265
      reason = tr("The path is not readable.");
×
266
    }
267
#ifdef Q_OS_UNIX
NEW
268
    if (reason.isEmpty()) {
×
269
      struct stat st;
NEW
270
      if (::stat(sshAuthSockOverride.toLocal8Bit().constData(), &st) != 0 ||
×
NEW
271
          !S_ISSOCK(st.st_mode)) {
×
NEW
272
        reason = tr("The path is not a Unix domain socket.");
×
273
      }
274
    }
275
#endif
NEW
276
    if (!reason.isEmpty()) {
×
NEW
277
      QMessageBox::warning(
×
NEW
278
          this, tr("Potentially invalid SSH_AUTH_SOCK override"),
×
NEW
279
          tr("The SSH_AUTH_SOCK override value may be invalid.\n\n%1\n\n"
×
280
             "The value will still be saved as entered.")
NEW
281
              .arg(reason));
×
282
    }
NEW
283
  }
×
NEW
284
  QtPassSettings::setSshAuthSockOverride(sshAuthSockOverride);
×
285
  QtPassSettings::setPassStore(
×
286
      Util::normalizeFolderPath(ui->storePath->text()));
×
287
  QtPassSettings::setUsePass(ui->radioButtonPass->isChecked());
×
288
  QtPassSettings::setClipBoardType(ui->comboBoxClipboard->currentIndex());
×
289
  QtPassSettings::setUseSelection(ui->checkBoxSelection->isChecked());
×
290
  QtPassSettings::setUseAutoclear(ui->checkBoxAutoclear->isChecked());
×
291
  QtPassSettings::setAutoclearSeconds(ui->spinBoxAutoclearSeconds->value());
×
292
  QtPassSettings::setUseAutoclearPanel(ui->checkBoxAutoclearPanel->isChecked());
×
293
  QtPassSettings::setAutoclearPanelSeconds(
×
294
      ui->spinBoxAutoclearPanelSeconds->value());
×
295
  QtPassSettings::setHidePassword(ui->checkBoxHidePassword->isChecked());
×
296
  QtPassSettings::setHideContent(ui->checkBoxHideContent->isChecked());
×
297
  QtPassSettings::setUseMonospace(ui->checkBoxUseMonospace->isChecked());
×
298
  QtPassSettings::setDisplayAsIs(ui->checkBoxDisplayAsIs->isChecked());
×
299
  QtPassSettings::setNoLineWrapping(ui->checkBoxNoLineWrapping->isChecked());
×
300
  QtPassSettings::setAddGPGId(ui->checkBoxAddGPGId->isChecked());
×
301
  QtPassSettings::setUseTrayIcon(ui->checkBoxUseTrayIcon->isEnabled() &&
×
302
                                 ui->checkBoxUseTrayIcon->isChecked());
×
303
  QtPassSettings::setHideOnClose(ui->checkBoxHideOnClose->isEnabled() &&
×
304
                                 ui->checkBoxHideOnClose->isChecked());
×
305
  QtPassSettings::setStartMinimized(ui->checkBoxStartMinimized->isEnabled() &&
×
306
                                    ui->checkBoxStartMinimized->isChecked());
×
307
  QHash<QString, QHash<QString, QString>> existingProfiles =
308
      QtPassSettings::getProfiles();
×
309
  QtPassSettings::setProfiles(getProfiles());
×
310
  QtPassSettings::setUseGit(ui->checkBoxUseGit->isChecked());
×
311
  QtPassSettings::setUseOtp(ui->checkBoxUseOtp->isChecked());
×
312
  QtPassSettings::setUseGrepSearch(ui->checkBoxUseGrepSearch->isChecked());
×
313
  QtPassSettings::setUseQrencode(ui->checkBoxUseQrencode->isChecked());
×
314
  QtPassSettings::setPwgenExecutable(ui->pwgenPath->text());
×
315
  QtPassSettings::setUsePwgen(ui->checkBoxUsePwgen->isChecked());
×
316
  QtPassSettings::setAvoidCapitals(ui->checkBoxAvoidCapitals->isChecked());
×
317
  QtPassSettings::setAvoidNumbers(ui->checkBoxAvoidNumbers->isChecked());
×
318
  QtPassSettings::setLessRandom(ui->checkBoxLessRandom->isChecked());
×
319
  QtPassSettings::setUseSymbols(ui->checkBoxUseSymbols->isChecked());
×
320
  QtPassSettings::setPasswordConfiguration(getPasswordConfiguration());
×
321
  QtPassSettings::setUseTemplate(ui->checkBoxUseTemplate->isChecked());
×
322
  QtPassSettings::setPassTemplate(ui->plainTextEditTemplate->toPlainText());
×
323
  QtPassSettings::setTemplateAllFields(
×
324
      ui->checkBoxTemplateAllFields->isChecked());
×
325
  QtPassSettings::setShowProcessOutput(
×
326
      ui->checkBoxShowProcessOutput->isChecked());
×
327
  QtPassSettings::setAlwaysOnTop(ui->checkBoxAlwaysOnTop->isChecked());
×
328

329
  QtPassSettings::setVersion(VERSION);
×
330

331
  // Initialize new profiles that need pass/git initialization
332
  initializeNewProfiles(existingProfiles);
×
333
}
×
334

335
/**
336
 * @brief Automatically detects required external binaries in the system PATH
337
 * and updates the dialog fields.
338
 * @example
339
 * ConfigDialog configDialog;
340
 * configDialog.on_autodetectButton_clicked();
341
 *
342
 * @return void - This function does not return a value.
343
 */
344
void ConfigDialog::on_autodetectButton_clicked() {
×
345
  QString pass = Util::findBinaryInPath("pass");
×
346
  if (!pass.isEmpty()) {
×
347
    ui->passPath->setText(pass);
×
348
  }
349
  usePass(!pass.isEmpty());
×
350
  QString gpg = Util::findBinaryInPath("gpg2");
×
351
  if (gpg.isEmpty()) {
×
352
    gpg = Util::findBinaryInPath("gpg");
×
353
  }
354
  if (!gpg.isEmpty()) {
×
355
    ui->gpgPath->setText(gpg);
×
356
  }
357
  QString git = Util::findBinaryInPath("git");
×
358
  if (!git.isEmpty()) {
×
359
    ui->gitPath->setText(git);
×
360
  }
361
  QString pwgen = Util::findBinaryInPath("pwgen");
×
362
  if (!pwgen.isEmpty()) {
×
363
    ui->pwgenPath->setText(pwgen);
×
364
  }
365
}
×
366

367
/**
368
 * @brief ConfigDialog::on_radioButtonNative_clicked wrapper for
369
 * ConfigDialog::setGroupBoxState()
370
 */
371
void ConfigDialog::on_radioButtonNative_clicked() { setGroupBoxState(); }
×
372

373
/**
374
 * @brief ConfigDialog::on_radioButtonPass_clicked wrapper for
375
 * ConfigDialog::setGroupBoxState()
376
 */
377
void ConfigDialog::on_radioButtonPass_clicked() { setGroupBoxState(); }
×
378

379
/**
380
 * @brief ConfigDialog::getSecretKeys get list of secret/private keys
381
 * @return QStringList keys
382
 */
383
auto ConfigDialog::getSecretKeys() -> QStringList {
×
384
  QList<UserInfo> keys = QtPassSettings::getPass()->listKeys("", true);
×
385
  QStringList names;
×
386

387
  if (keys.empty()) {
×
388
    return names;
389
  }
390

391
  foreach (const UserInfo &sec, keys)
×
392
    names << sec.name;
×
393

394
  return names;
×
395
}
396

397
/**
398
 * @brief ConfigDialog::setGroupBoxState update checkboxes.
399
 */
400
void ConfigDialog::setGroupBoxState() {
×
401
  bool state = ui->radioButtonPass->isChecked();
×
402
  ui->groupBoxNative->setEnabled(!state);
×
403
  ui->groupBoxPass->setEnabled(state);
×
404
  if (state) {
×
405
    // pass mode: disable all password generation controls
406
    ui->spinBoxPasswordLength->setEnabled(false);
×
407
    ui->checkBoxUsePwgen->setEnabled(false);
×
408
    ui->checkBoxAvoidCapitals->setEnabled(false);
×
409
    ui->checkBoxUseSymbols->setEnabled(false);
×
410
    ui->checkBoxLessRandom->setEnabled(false);
×
411
    ui->checkBoxAvoidNumbers->setEnabled(false);
×
412
    ui->labelPasswordChars->setEnabled(false);
×
413
    ui->passwordCharTemplateSelector->setEnabled(false);
×
414
    ui->lineEditPasswordChars->setEnabled(false);
×
415
  } else {
416
    // native mode: restore pwgen/charset state from existing handlers
417
    ui->spinBoxPasswordLength->setEnabled(true);
×
418
    ui->checkBoxUsePwgen->setEnabled(!ui->pwgenPath->text().isEmpty());
×
419
    on_checkBoxUsePwgen_clicked();
×
420
  }
421
}
×
422

423
/**
424
 * @brief ConfigDialog::selectExecutable pop-up to choose an executable.
425
 * @return
426
 */
427
auto ConfigDialog::selectExecutable() -> QString {
×
428
  QFileDialog dialog(this);
×
429
  dialog.setFileMode(QFileDialog::ExistingFile);
×
430
  dialog.setOption(QFileDialog::ReadOnly);
×
431
  if (dialog.exec()) {
×
432
    return dialog.selectedFiles().constFirst();
×
433
  }
434

435
  return {};
436
}
×
437

438
/**
439
 * @brief ConfigDialog::selectFolder pop-up to choose a folder.
440
 * @return
441
 */
442
auto ConfigDialog::selectFolder() -> QString {
×
443
  QFileDialog dialog(this);
×
444
  dialog.setFileMode(QFileDialog::Directory);
×
445
  dialog.setFilter(QDir::NoFilter);
×
446
  dialog.setOption(QFileDialog::ShowDirsOnly);
×
447
  if (dialog.exec()) {
×
448
    return dialog.selectedFiles().constFirst();
×
449
  }
450

451
  return {};
452
}
×
453

454
/**
455
 * @brief ConfigDialog::on_toolButtonGit_clicked get git application.
456
 * Enable checkboxes if found.
457
 */
458
void ConfigDialog::on_toolButtonGit_clicked() {
×
459
  QString git = selectExecutable();
×
460
  bool state = !git.isEmpty();
×
461
  if (state) {
×
462
    ui->gitPath->setText(git);
×
463
  } else {
464
    useGit(false);
×
465
  }
466

467
  ui->checkBoxUseGit->setEnabled(state);
×
468
}
×
469

470
/**
471
 * @brief ConfigDialog::on_toolButtonGpg_clicked get gpg application.
472
 */
473
void ConfigDialog::on_toolButtonGpg_clicked() {
×
474
  QString gpg = selectExecutable();
×
475
  if (!gpg.isEmpty()) {
×
476
    ui->gpgPath->setText(gpg);
×
477
  }
478
}
×
479

480
/**
481
 * @brief ConfigDialog::on_pushButtonGenerateKey_clicked open keygen dialog.
482
 */
483
void ConfigDialog::on_pushButtonGenerateKey_clicked() {
×
484
  KeygenDialog d(this);
×
485
  d.exec();
×
486
}
×
487

488
/**
489
 * @brief ConfigDialog::on_toolButtonPass_clicked get pass application.
490
 */
491
void ConfigDialog::on_toolButtonPass_clicked() {
×
492
  QString pass = selectExecutable();
×
493
  if (!pass.isEmpty()) {
×
494
    ui->passPath->setText(pass);
×
495
  }
496
}
×
497

498
/**
499
 * @brief ConfigDialog::on_toolButtonStore_clicked get .password-store
500
 * location.
501
 */
502
void ConfigDialog::on_toolButtonStore_clicked() {
×
503
  QString store = selectFolder();
×
504
  if (!store.isEmpty()) {
×
505
    ui->storePath->setText(store);
×
506
  }
507
}
×
508

509
/**
510
 * @brief ConfigDialog::on_comboBoxClipboard_activated show and hide options.
511
 * @param index of selectbox (0 = no clipboard).
512
 */
513
void ConfigDialog::on_comboBoxClipboard_activated(int index) {
×
514
  bool state = index > 0;
×
515

516
  ui->checkBoxSelection->setEnabled(state);
×
517
  ui->checkBoxAutoclear->setEnabled(state);
×
518
  ui->checkBoxHidePassword->setEnabled(state);
×
519
  ui->checkBoxHideContent->setEnabled(state);
×
520
  if (state) {
×
521
    ui->spinBoxAutoclearSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
522
    ui->labelSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
523
  } else {
524
    ui->spinBoxAutoclearSeconds->setEnabled(false);
×
525
    ui->labelSeconds->setEnabled(false);
×
526
  }
527
}
×
528

529
/**
530
 * @brief ConfigDialog::on_checkBoxAutoclearPanel_clicked enable and disable
531
 * options based on autoclear use.
532
 */
533
void ConfigDialog::on_checkBoxAutoclearPanel_clicked() {
×
534
  bool state = ui->checkBoxAutoclearPanel->isChecked();
×
535
  ui->spinBoxAutoclearPanelSeconds->setEnabled(state);
×
536
  ui->labelPanelSeconds->setEnabled(state);
×
537
}
×
538

539
/**
540
 * @brief ConfigDialog::useSelection set the clipboard type use from
541
 * MainWindow.
542
 * @param useSelection
543
 */
544
void ConfigDialog::useSelection(bool useSelection) {
×
545
  ui->checkBoxSelection->setChecked(useSelection);
×
546
  on_checkBoxSelection_clicked();
×
547
}
×
548

549
/**
550
 * @brief ConfigDialog::useAutoclear set the clipboard autoclear use from
551
 * MainWindow.
552
 * @param useAutoclear
553
 */
554
void ConfigDialog::useAutoclear(bool useAutoclear) {
×
555
  ui->checkBoxAutoclear->setChecked(useAutoclear);
×
556
  on_checkBoxAutoclear_clicked();
×
557
}
×
558

559
/**
560
 * @brief ConfigDialog::useAutoclearPanel set the panel autoclear use from
561
 * MainWindow.
562
 * @param useAutoclearPanel
563
 */
564
void ConfigDialog::useAutoclearPanel(bool useAutoclearPanel) {
×
565
  ui->checkBoxAutoclearPanel->setChecked(useAutoclearPanel);
×
566
  on_checkBoxAutoclearPanel_clicked();
×
567
}
×
568

569
/**
570
 * @brief ConfigDialog::on_checkBoxSelection_clicked checkbox clicked, update
571
 * state via ConfigDialog::on_comboBoxClipboard_activated
572
 */
573
void ConfigDialog::on_checkBoxSelection_clicked() {
×
574
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
575
}
×
576

577
/**
578
 * @brief ConfigDialog::on_checkBoxAutoclear_clicked checkbox clicked, update
579
 * state via ConfigDialog::on_comboBoxClipboard_activated
580
 */
581
void ConfigDialog::on_checkBoxAutoclear_clicked() {
×
582
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
583
}
×
584

585
/**
586
 * @brief ConfigDialog::genKey tunnel function to make MainWindow generate a
587
 * gpg key pair.
588
 * @param batch
589
 * @param dialog
590
 */
591
void ConfigDialog::genKey(const QString &batch, QDialog *dialog) {
×
592
  mainWindow->generateKeyPair(batch, dialog);
×
593
}
×
594

595
/**
596
 * @brief ConfigDialog::setProfiles set the profiles and chosen profile from
597
 * MainWindow.
598
 * @param profiles
599
 * @param profile
600
 */
601
void ConfigDialog::setProfiles(QHash<QString, QHash<QString, QString>> profiles,
×
602
                               const QString &currentProfile) {
603
  if (profiles.contains("")) {
×
604
    profiles.remove("");
×
605
    // remove weird "" key value pairs
606
  }
607

608
  // Cache profiles for use in onProfileTableSelectionChanged
609
  m_profiles = profiles;
×
610

611
  ui->profileTable->setRowCount(static_cast<int>(profiles.count()));
×
612
  QHashIterator<QString, QHash<QString, QString>> i(profiles);
×
613
  int n = 0;
614
  while (i.hasNext()) {
×
615
    i.next();
616
    if (!i.value().isEmpty() && !i.key().isEmpty()) {
×
617
      ui->profileTable->setItem(n, 0, new QTableWidgetItem(i.key()));
×
618
      ui->profileTable->setItem(n, 1,
×
619
                                new QTableWidgetItem(i.value().value("path")));
×
620
      ui->profileTable->setItem(
×
621
          n, 2, new QTableWidgetItem(i.value().value("signingKey")));
×
622
      if (i.key() == currentProfile) {
×
623
        ui->profileTable->selectRow(n);
×
624
        // Load git settings for current profile
625
        loadGitSettingsForProfile(currentProfile, m_profiles);
×
626
      }
627
    }
628
    ++n;
×
629
  }
630
}
×
631

632
/**
633
 * @brief Load git settings for a specific profile.
634
 * @param profileName The profile name.
635
 * @param profiles The profiles hash containing git settings.
636
 */
637
void ConfigDialog::loadGitSettingsForProfile(
×
638
    const QString &profileName,
639
    const QHash<QString, QHash<QString, QString>> &profiles) {
640
  if (profiles.contains(profileName)) {
×
641
    const QHash<QString, QString> &profile = profiles.value(profileName);
×
642
    QString useGitStr = profile.value("useGit");
×
643
    QString autoPushStr = profile.value("autoPush");
×
644
    QString autoPullStr = profile.value("autoPull");
×
645

646
    // Load profile-specific git settings if set, otherwise use global settings
647
    if (!useGitStr.isEmpty()) {
×
648
      useGit(useGitStr == "true");
×
649
      ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
×
650
      ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
×
651
      if (autoPushStr == "true" || autoPushStr == "false") {
×
652
        ui->checkBoxAutoPush->setChecked(autoPushStr == "true");
×
653
      }
654
      if (autoPullStr == "true" || autoPullStr == "false") {
×
655
        ui->checkBoxAutoPull->setChecked(autoPullStr == "true");
×
656
      }
657
    }
658
    // If not set (empty), leave global settings as-is for migration
659
  }
×
660
}
×
661

662
/**
663
 * @brief ConfigDialog::getProfiles return profile list.
664
 * @return
665
 */
666
auto ConfigDialog::getProfiles() -> QHash<QString, QHash<QString, QString>> {
×
667
  // Get currently selected profile name
668
  QList<QTableWidgetItem *> selected = ui->profileTable->selectedItems();
×
669
  QString selectedProfile;
×
670
  if (!selected.isEmpty()) {
×
671
    selectedProfile =
672
        ui->profileTable->item(selected.first()->row(), 0)->text();
×
673
  }
674

675
  // Use cached m_profiles to preserve git settings for non-selected profiles
676
  QHash<QString, QHash<QString, QString>> existingProfiles = m_profiles;
677

678
  QHash<QString, QHash<QString, QString>> profiles;
×
679
  // Check?
680
  for (int i = 0; i < ui->profileTable->rowCount(); ++i) {
×
681
    QHash<QString, QString> profile;
×
682
    QTableWidgetItem *pathItem = ui->profileTable->item(i, 1);
×
683
    if (nullptr != pathItem) {
×
684
      QTableWidgetItem *item = ui->profileTable->item(i, 0);
×
685
      if (item == nullptr) {
×
686
        continue;
687
      }
688
      profile["path"] = pathItem->text();
×
689
      QTableWidgetItem *signingKeyItem = ui->profileTable->item(i, 2);
×
690
      if (nullptr != signingKeyItem) {
×
691
        profile["signingKey"] = signingKeyItem->text();
×
692
      }
693

694
      // Only update git settings for the currently selected profile
695
      // Preserve existing git settings for other profiles
696
      if (item->text() == selectedProfile) {
×
697
        profile["useGit"] = ui->checkBoxUseGit->isChecked() ? "true" : "false";
×
698
        profile["autoPush"] =
×
699
            ui->checkBoxAutoPush->isChecked() ? "true" : "false";
×
700
        profile["autoPull"] =
×
701
            ui->checkBoxAutoPull->isChecked() ? "true" : "false";
×
702
      } else if (existingProfiles.contains(item->text())) {
×
703
        // Preserve existing git settings for non-selected profiles
704
        const QHash<QString, QString> &existing =
705
            existingProfiles.value(item->text());
×
706
        if (existing.contains("useGit")) {
×
707
          profile["useGit"] = existing.value("useGit");
×
708
        }
709
        if (existing.contains("autoPush")) {
×
710
          profile["autoPush"] = existing.value("autoPush");
×
711
        }
712
        if (existing.contains("autoPull")) {
×
713
          profile["autoPull"] = existing.value("autoPull");
×
714
        }
715
      }
×
716
      profiles.insert(item->text(), profile);
×
717
    }
718
  }
×
719
  // Update cache with current in-dialog state
720
  m_profiles = profiles;
×
721
  return profiles;
×
722
}
×
723

724
/**
725
 * @brief Initialize new profiles that need pass/git initialization.
726
 * @param existingProfiles The profiles that existed before the dialog was
727
 * opened.
728
 */
729
void ConfigDialog::initializeNewProfiles(
×
730
    const QHash<QString, QHash<QString, QString>> &existingProfiles) {
731
  QHash<QString, QHash<QString, QString>> newProfiles = getProfiles();
×
732

733
  // Collect keys and sort for deterministic iteration
734
  QStringList keys = newProfiles.keys();
×
735
  keys.sort();
736

737
  for (const QString &name : keys) {
×
738
    const QString &path = newProfiles.value(name).value("path");
×
739

740
    // Skip if already existed before (check by name and path)
741
    if (existingProfiles.contains(name) &&
×
742
        existingProfiles.value(name).value("path") == path) {
×
743
      continue;
×
744
    }
745

746
    // This is a new profile - create directory if needed and initialize
747
    // Note: needsInit returns false for non-existent directories, so we
748
    // must create the directory first.
749
    QString cleanPath = QDir::cleanPath(path);
×
750
    QDir dir(cleanPath);
×
751
    if (!dir.exists()) {
×
752
      if (QMessageBox::question(
×
753
              this, tr("Create profile directory?"),
×
754
              tr("Would you like to create a password store at %1?")
×
755
                  .arg(cleanPath),
×
756
              QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
757
        continue;
×
758
      }
759
      if (!QDir().mkpath(cleanPath)) {
×
760
        QMessageBox::warning(
×
761
            this, tr("Error"),
×
762
            tr("Could not create profile directory: %1").arg(cleanPath));
×
763
        continue;
×
764
      }
765
    }
766

767
    // Now check if initialization is needed (directory exists with no .gpg-id)
768
    if (!ProfileInit::needsInit(cleanPath)) {
×
769
      continue;
×
770
    }
771

772
    // Temporarily switch the active store so pass/git init operate on
773
    // the new profile's directory rather than the currently-saved one.
774
    const QString prevStore = QtPassSettings::getPassStore();
×
775
    QtPassSettings::setPassStore(cleanPath);
×
776

777
    // Show user selection dialog for GPG recipients
778
    // UsersDialog will run pass init when accepted
779
    UsersDialog usersDialog(cleanPath, this);
×
780
    usersDialog.setWindowTitle(tr("Select recipients for %1").arg(name));
×
781
    const int result = usersDialog.exec();
×
782

783
    // Use per-profile useGit setting, falling back to global if not set
784
    QString useGitStr = newProfiles.value(name).value("useGit");
×
785
    bool useGit =
786
        useGitStr.isEmpty() ? QtPassSettings::isUseGit() : useGitStr == "true";
×
787

788
    if (result == QDialog::Accepted && useGit) {
×
789
      QtPassSettings::getPass()->GitInit();
×
790
    }
791

792
    // Restore previous store setting
793
    QtPassSettings::setPassStore(prevStore);
×
794
  }
×
795
}
×
796

797
/**
798
 * @brief ConfigDialog::on_addButton_clicked add a profile row.
799
 */
800
void ConfigDialog::on_addButton_clicked() {
×
801
  bool sortingEnabled = ui->profileTable->isSortingEnabled();
×
802
  ui->profileTable->setSortingEnabled(false);
×
803

804
  int n = ui->profileTable->rowCount();
×
805
  ui->profileTable->insertRow(n);
×
806
  ui->profileTable->setItem(n, 0, new QTableWidgetItem(tr("New Profile")));
×
807
  ui->profileTable->setItem(n, 1, new QTableWidgetItem(ui->storePath->text()));
×
808
  ui->profileTable->setItem(n, 2, new QTableWidgetItem());
×
809

810
  ui->profileTable->setSortingEnabled(sortingEnabled);
×
811

812
  int currentRow = ui->profileTable->row(ui->profileTable->item(n, 0));
×
813
  ui->profileTable->selectRow(currentRow);
×
814
  ui->deleteButton->setEnabled(true);
×
815

816
  ui->profileTable->editItem(ui->profileTable->item(currentRow, 0));
×
817
  ui->profileTable->item(currentRow, 0)->setSelected(true);
×
818

819
  validate();
×
820
  updateProfileStatus(currentRow);
×
821
}
×
822

823
/**
824
 * @brief ConfigDialog::on_profileTable_cellDoubleClicked open folder browser
825
 * for path column (column 1).
826
 */
827
void ConfigDialog::on_profileTable_cellDoubleClicked(int row, int column) {
×
828
  if (column == 1) {
×
829
    QString dir = selectFolder();
×
830
    if (!dir.isEmpty()) {
×
831
      ui->profileTable->item(row, 1)->setText(dir);
×
832
    }
833
  }
834
}
×
835

836
/**
837
 * @brief ConfigDialog::on_deleteButton_clicked remove a profile row.
838
 */
839
void ConfigDialog::on_deleteButton_clicked() {
×
840
  QSet<int> selectedRows; //  we use a set to prevent doubles
841
  QList<QTableWidgetItem *> itemList = ui->profileTable->selectedItems();
×
842
  if (itemList.count() == 0) {
×
843
    QMessageBox::warning(this, tr("No profile selected"),
×
844
                         tr("No profile selected to delete"));
×
845
    return;
846
  }
847
  QTableWidgetItem *item;
848
  foreach (item, itemList)
×
849
    selectedRows.insert(item->row());
×
850
  // get a list, and sort it big to small
851
  QList<int> rows = selectedRows.values();
×
852
  std::sort(rows.begin(), rows.end());
×
853
  // now actually do the removing:
854
  foreach (int row, rows)
×
855
    ui->profileTable->removeRow(row);
×
856
  if (ui->profileTable->rowCount() < 1) {
×
857
    ui->deleteButton->setEnabled(false);
×
858
  }
859

860
  validate();
×
861
  updateProfileStatus(-1);
×
862
}
863

864
/**
865
 * @brief ConfigDialog::criticalMessage wrapper for showing critical messages
866
 * in a popup.
867
 * @param title
868
 * @param text
869
 */
870
void ConfigDialog::criticalMessage(const QString &title, const QString &text) {
×
871
  QMessageBox::critical(this, title, text, QMessageBox::Ok, QMessageBox::Ok);
×
872
}
×
873

874
/**
875
 * @brief Checks whether the qrencode executable is available on the system.
876
 * @example
877
 * bool result = ConfigDialog::isQrencodeAvailable();
878
 * std::cout << result << std::endl; // Expected output: true if qrencode is
879
 * found, otherwise false
880
 *
881
 * @return bool - True if qrencode is available; otherwise false. On Windows,
882
 * always returns false.
883
 */
884
auto ConfigDialog::isQrencodeAvailable() -> bool {
×
885
#ifdef Q_OS_WIN
886
  return false;
887
#else
888
  QProcess which;
×
889
  which.start("which", QStringList() << "qrencode");
×
890
  which.waitForFinished();
×
891
  QtPassSettings::setQrencodeExecutable(
×
892
      which.readAllStandardOutput().trimmed());
×
893
  return which.exitCode() == 0;
×
894
#endif
895
}
×
896

897
auto ConfigDialog::isPassOtpAvailable() -> bool {
×
898
#ifdef Q_OS_WIN
899
  return false;
900
#else
901
  QProcess pass;
×
902
  pass.start(QtPassSettings::getPassExecutable(), QStringList() << "otp"
×
903
                                                                << "--help");
×
904
  pass.waitForFinished(2000);
×
905
  return pass.exitCode() == 0;
×
906
#endif
907
}
×
908

909
/**
910
 * @brief ConfigDialog::wizard first-time use wizard.
911
 */
912
void ConfigDialog::wizard() {
×
913
  (void)Util::configIsValid();
×
914
  on_autodetectButton_clicked();
×
915

916
  if (!checkGpgExistence()) {
×
917
    return;
918
  }
919
  if (!checkSecretKeys()) {
×
920
    return;
921
  }
922
  if (!checkPasswordStore()) {
×
923
    return;
924
  }
925
  handleGpgIdFile();
×
926

927
  ui->checkBoxHidePassword->setCheckState(Qt::Checked);
×
928
}
929

930
/**
931
 * @brief Checks whether the configured GnuPG executable exists.
932
 * @example
933
 * bool result = ConfigDialog::checkGpgExistence();
934
 * std::cout << result << std::endl; // Expected output: true if GnuPG is found,
935
 * false otherwise
936
 *
937
 * @return bool - True if the GnuPG path is valid or uses a WSL command prefix;
938
 * false if the executable cannot be found and an error message is shown.
939
 */
940
auto ConfigDialog::checkGpgExistence() -> bool {
×
941
  QString gpg = ui->gpgPath->text();
×
942
  if (!gpg.startsWith("wsl ") && !QFile(gpg).exists()) {
×
943
    criticalMessage(
×
944
        tr("GnuPG not found"),
×
945
#ifdef Q_OS_WIN
946
#ifdef WINSTORE
947
        tr("Please install GnuPG on your system.<br>Install "
948
           "<strong>Ubuntu</strong> from the Microsoft Store to get it.<br>"
949
           "If you already did so, make sure you started it once and<br>"
950
           "click \"Autodetect\" in the next dialog.")
951
#else
952
        tr("Please install GnuPG on your system.<br>Install "
953
           "<strong>Ubuntu</strong> from the Microsoft Store<br>or <a "
954
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
955
           "from GnuPG.org")
956
#endif
957
#else
958
        tr("Please install GnuPG on your system.<br>Install "
×
959
           "<strong>gpg</strong> using your favorite package manager<br>or "
960
           "<a "
961
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
962
           "from GnuPG.org")
963
#endif
964
    );
965
    return false;
×
966
  }
967
  return true;
968
}
969

970
/**
971
 * @brief Checks whether secret keys are available and, if needed, prompts the
972
 * user to generate them.
973
 * @example
974
 * bool result = ConfigDialog.checkSecretKeys();
975
 * std::cout << result << std::endl; // Expected output: true if keys are
976
 * present or key generation dialog is accepted, false otherwise
977
 *
978
 * @return bool - Returns true when secret keys are already available or the key
979
 * generation dialog is accepted; false if the dialog is rejected.
980
 */
981
auto ConfigDialog::checkSecretKeys() -> bool {
×
982
  QString gpg = ui->gpgPath->text();
×
983
  QStringList names = getSecretKeys();
×
984

985
#ifdef QT_DEBUG
986
  dbg() << names;
987
#endif
988

989
  if ((gpg.startsWith("wsl ") || QFile(gpg).exists()) && names.empty()) {
×
990
    KeygenDialog d(this);
×
991
    return d.exec();
×
992
  }
×
993
  return true;
994
}
995

996
/**
997
 * @brief Checks whether the password-store path exists and prompts the user to
998
 * create it if it does not.
999
 * @example
1000
 * bool result = ConfigDialog::checkPasswordStore();
1001
 * std::cout << std::boolalpha << result << std::endl; // Expected output: true
1002
 * if the store exists or is created successfully, false if creation fails
1003
 *
1004
 * @return bool - True if the password-store exists or is successfully created,
1005
 * false if creation fails.
1006
 */
1007
auto ConfigDialog::checkPasswordStore() -> bool {
×
1008
  QString passStore = ui->storePath->text();
×
1009

1010
  if (!QFile(passStore).exists()) {
×
1011
    if (QMessageBox::question(
×
1012
            this, tr("Create password-store?"),
×
1013
            tr("Would you like to create a password-store at %1?")
×
1014
                .arg(passStore),
×
1015
            QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
1016
      if (!QDir().mkdir(passStore)) {
×
1017
        QMessageBox::warning(
×
1018
            this, tr("Error"),
×
1019
            tr("Failed to create password-store at: %1").arg(passStore));
×
1020
        return false;
×
1021
      }
1022
#ifdef Q_OS_WIN
1023
      SetFileAttributes(passStore.toStdWString().c_str(),
1024
                        FILE_ATTRIBUTE_HIDDEN);
1025
#endif
1026
      if (ui->checkBoxUseGit->isChecked()) {
×
1027
        emit mainWindow->passGitInitNeeded();
×
1028
      }
1029
      mainWindow->userDialog(passStore);
×
1030
    }
1031
  }
1032
  return true;
1033
}
1034

1035
/**
1036
 * @brief Handles selection and validation of the password store's .gpg-id file.
1037
 * @example
1038
 * ConfigDialog dialog;
1039
 * dialog.handleGpgIdFile();
1040
 *
1041
 * @return void - This method does not return a value; it updates the UI flow
1042
 * and may prompt the user to choose a valid password store.
1043
 */
1044
void ConfigDialog::handleGpgIdFile() {
×
1045
  QString passStore = ui->storePath->text();
×
1046
  if (!QFile(QDir(passStore).filePath(".gpg-id")).exists()) {
×
1047
#ifdef QT_DEBUG
1048
    dbg() << ".gpg-id file does not exist";
1049
#endif
1050
    criticalMessage(tr("Password store not initialised"),
×
1051
                    tr("The folder %1 doesn't seem to be a password store or "
×
1052
                       "is not yet initialised.")
1053
                        .arg(passStore));
×
1054

1055
    while (!QFile(passStore).exists()) {
×
1056
      on_toolButtonStore_clicked();
×
1057
      if (passStore == ui->storePath->text()) {
×
1058
        return;
1059
      }
1060
      passStore = ui->storePath->text();
×
1061
    }
1062
    if (!QFile(passStore + ".gpg-id").exists()) {
×
1063
#ifdef QT_DEBUG
1064
      dbg() << ".gpg-id file still does not exist :/";
1065
#endif
1066
      mainWindow->userDialog(passStore);
×
1067
    }
1068
  }
1069
}
1070

1071
/**
1072
 * @brief ConfigDialog::useTrayIcon set preference for using trayicon.
1073
 * Enable or disable related checkboxes accordingly.
1074
 * @param useSystray
1075
 */
1076
void ConfigDialog::useTrayIcon(bool useSystray) {
×
1077
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
×
1078
    ui->checkBoxUseTrayIcon->setChecked(useSystray);
×
1079
    ui->checkBoxHideOnClose->setEnabled(useSystray);
×
1080
    ui->checkBoxStartMinimized->setEnabled(useSystray);
×
1081

1082
    if (!useSystray) {
×
1083
      ui->checkBoxHideOnClose->setChecked(false);
×
1084
      ui->checkBoxStartMinimized->setChecked(false);
×
1085
    }
1086
  }
1087
}
×
1088

1089
/**
1090
 * @brief ConfigDialog::on_checkBoxUseTrayIcon_clicked enable and disable
1091
 * related checkboxes.
1092
 */
1093
void ConfigDialog::on_checkBoxUseTrayIcon_clicked() {
×
1094
  bool state = ui->checkBoxUseTrayIcon->isChecked();
×
1095
  ui->checkBoxHideOnClose->setEnabled(state);
×
1096
  ui->checkBoxStartMinimized->setEnabled(state);
×
1097
}
×
1098

1099
/**
1100
 * @brief ConfigDialog::closeEvent close this window.
1101
 * @param event
1102
 */
1103
void ConfigDialog::closeEvent(QCloseEvent *event) {
×
1104
  QtPassSettings::setDialogGeometry("configDialog", saveGeometry());
×
1105
  if (!isMaximized()) {
×
1106
    QtPassSettings::setDialogPos("configDialog", pos());
×
1107
    QtPassSettings::setDialogSize("configDialog", size());
×
1108
  }
1109
  QtPassSettings::setDialogMaximized("configDialog", isMaximized());
×
1110
  event->accept();
1111
}
×
1112

1113
/**
1114
 * @brief ConfigDialog::useGit set preference for using git.
1115
 * @param useGit
1116
 */
1117
void ConfigDialog::useGit(bool useGit) {
×
1118
  ui->checkBoxUseGit->setChecked(useGit);
×
1119
  on_checkBoxUseGit_clicked();
×
1120
}
×
1121

1122
/**
1123
 * @brief ConfigDialog::useOtp set preference for using otp plugin.
1124
 * @param useOtp
1125
 */
1126
void ConfigDialog::useOtp(bool useOtp) {
×
1127
  ui->checkBoxUseOtp->setChecked(useOtp);
×
1128
}
×
1129

1130
void ConfigDialog::useGrepSearch(bool useGrepSearch) {
×
1131
  ui->checkBoxUseGrepSearch->setChecked(useGrepSearch);
×
1132
}
×
1133

1134
/**
1135
 * @brief ConfigDialog::useQrencode set preference for using qrencode plugin.
1136
 * @param useQrencode
1137
 */
1138
void ConfigDialog::useQrencode(bool useQrencode) {
×
1139
  ui->checkBoxUseQrencode->setChecked(useQrencode);
×
1140
}
×
1141

1142
/**
1143
 * @brief ConfigDialog::on_checkBoxUseGit_clicked enable or disable related
1144
 * checkboxes.
1145
 */
1146
void ConfigDialog::on_checkBoxUseGit_clicked() {
×
1147
  ui->checkBoxAddGPGId->setEnabled(ui->checkBoxUseGit->isChecked());
×
1148
  ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
×
1149
  ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
×
1150
}
×
1151

1152
/**
1153
 * @brief ConfigDialog::on_toolButtonPwgen_clicked enable or disable related
1154
 * options in the interface.
1155
 */
1156
void ConfigDialog::on_toolButtonPwgen_clicked() {
×
1157
  QString pwgen = selectExecutable();
×
1158
  if (!pwgen.isEmpty()) {
×
1159
    ui->pwgenPath->setText(pwgen);
×
1160
    ui->checkBoxUsePwgen->setEnabled(true);
×
1161
  } else {
1162
    ui->checkBoxUsePwgen->setEnabled(false);
×
1163
    ui->checkBoxUsePwgen->setChecked(false);
×
1164
  }
1165
}
×
1166

1167
/**
1168
 * @brief ConfigDialog::setPwgenPath set pwgen executable path.
1169
 * Enable or disable related options in the interface.
1170
 * @param pwgen
1171
 */
1172
void ConfigDialog::setPwgenPath(const QString &pwgen) {
×
1173
  ui->pwgenPath->setText(pwgen);
×
1174
  if (pwgen.isEmpty()) {
×
1175
    ui->checkBoxUsePwgen->setChecked(false);
×
1176
    ui->checkBoxUsePwgen->setEnabled(false);
×
1177
  }
1178
  on_checkBoxUsePwgen_clicked();
×
1179
}
×
1180

1181
/**
1182
 * @brief ConfigDialog::on_checkBoxUsePwgen_clicked enable or disable related
1183
 * options in the interface.
1184
 */
1185
void ConfigDialog::on_checkBoxUsePwgen_clicked() {
×
1186
  if (ui->radioButtonPass->isChecked())
×
1187
    return;
1188
  bool usePwgen = ui->checkBoxUsePwgen->isChecked();
×
1189
  ui->checkBoxAvoidCapitals->setEnabled(usePwgen);
×
1190
  ui->checkBoxAvoidNumbers->setEnabled(usePwgen);
×
1191
  ui->checkBoxLessRandom->setEnabled(usePwgen);
×
1192
  ui->checkBoxUseSymbols->setEnabled(usePwgen);
×
1193
  ui->lineEditPasswordChars->setEnabled(!usePwgen);
×
1194
  ui->labelPasswordChars->setEnabled(!usePwgen);
×
1195
  ui->passwordCharTemplateSelector->setEnabled(!usePwgen);
×
1196
}
1197

1198
/**
1199
 * @brief ConfigDialog::usePwgen set preference for using pwgen (can be
1200
 * overruled by empty pwgenPath).
1201
 * enable or disable related options in the interface via
1202
 * ConfigDialog::on_checkBoxUsePwgen_clicked
1203
 * @param usePwgen
1204
 */
1205
void ConfigDialog::usePwgen(bool usePwgen) {
×
1206
  if (ui->pwgenPath->text().isEmpty()) {
×
1207
    usePwgen = false;
1208
  }
1209
  ui->checkBoxUsePwgen->setChecked(usePwgen);
×
1210
  on_checkBoxUsePwgen_clicked();
×
1211
}
×
1212

1213
void ConfigDialog::setPasswordConfiguration(
×
1214
    const PasswordConfiguration &config) {
1215
  ui->spinBoxPasswordLength->setValue(config.length);
×
1216
  ui->passwordCharTemplateSelector->setCurrentIndex(config.selected);
×
1217
  if (config.selected != PasswordConfiguration::CUSTOM) {
×
1218
    ui->lineEditPasswordChars->setEnabled(false);
×
1219
  }
1220
  ui->lineEditPasswordChars->setText(config.Characters[config.selected]);
×
1221
}
×
1222

1223
auto ConfigDialog::getPasswordConfiguration() -> PasswordConfiguration {
×
1224
  PasswordConfiguration config;
×
1225
  config.length = ui->spinBoxPasswordLength->value();
×
1226
  config.selected = static_cast<PasswordConfiguration::characterSet>(
×
1227
      ui->passwordCharTemplateSelector->currentIndex());
×
1228
  config.Characters[PasswordConfiguration::CUSTOM] =
1229
      ui->lineEditPasswordChars->text();
×
1230
  return config;
×
1231
}
×
1232

1233
/**
1234
 * @brief ConfigDialog::on_passwordCharTemplateSelector_activated sets the
1235
 * passwordChar Template
1236
 * combo box to the desired entry
1237
 * @param entry of
1238
 */
1239
void ConfigDialog::on_passwordCharTemplateSelector_activated(int index) {
×
1240
  ui->lineEditPasswordChars->setText(
×
1241
      QtPassSettings::getPasswordConfiguration().Characters[index]);
×
1242
  if (index == PasswordConfiguration::CUSTOM) {
×
1243
    ui->lineEditPasswordChars->setEnabled(true);
×
1244
  } else {
1245
    ui->lineEditPasswordChars->setEnabled(false);
×
1246
  }
1247
}
×
1248

1249
/**
1250
 * @brief ConfigDialog::on_checkBoxUseTemplate_clicked enable or disable the
1251
 * template field and options.
1252
 */
1253
void ConfigDialog::on_checkBoxUseTemplate_clicked() {
×
1254
  ui->plainTextEditTemplate->setEnabled(ui->checkBoxUseTemplate->isChecked());
×
1255
  ui->checkBoxTemplateAllFields->setEnabled(
×
1256
      ui->checkBoxUseTemplate->isChecked());
×
1257
}
×
1258

1259
void ConfigDialog::onProfileTableItemChanged(QTableWidgetItem *item) {
×
1260
  validate(item);
×
1261
  updateProfileStatus(item ? item->row() : -1);
×
1262
}
×
1263

1264
void ConfigDialog::onProfileTableSelectionChanged() {
×
1265
  QList<QTableWidgetItem *> selected = ui->profileTable->selectedItems();
×
1266
  if (selected.isEmpty()) {
×
1267
    return;
1268
  }
1269
  QTableWidgetItem *nameItem =
1270
      ui->profileTable->item(selected.first()->row(), 0);
×
1271
  if (nameItem == nullptr) {
×
1272
    return;
1273
  }
1274
  QString profileName = nameItem->text();
×
1275
  loadGitSettingsForProfile(profileName, m_profiles);
×
1276
}
1277

1278
/**
1279
 * @brief Update status bar with profile preview for given row.
1280
 * @param row The row index to preview, or -1 to clear.
1281
 */
1282
void ConfigDialog::updateProfileStatus(int row) {
×
1283
  if (row < 0 || row >= ui->profileTable->rowCount()) {
×
1284
    ui->statusLabel->setText(QString());
×
1285
    return;
×
1286
  }
1287

1288
  QTableWidgetItem *nameItem = ui->profileTable->item(row, 0);
×
1289
  QTableWidgetItem *pathItem = ui->profileTable->item(row, 1);
×
1290

1291
  QString statusMessage;
×
1292
  if (nameItem && !nameItem->text().isEmpty() && pathItem &&
×
1293
      !pathItem->text().isEmpty()) {
×
1294
    QDir dir(QDir::cleanPath(pathItem->text()));
×
1295
    if (!dir.exists()) {
×
1296
      statusMessage = tr("New profile: %1 at %2")
×
1297
                          .arg(nameItem->text())
×
1298
                          .arg(QDir::cleanPath(pathItem->text()));
×
1299
    } else {
1300
      statusMessage = tr("Profile: %1 at %2")
×
1301
                          .arg(nameItem->text())
×
1302
                          .arg(QDir::cleanPath(pathItem->text()));
×
1303
    }
1304
  } else {
×
1305
    statusMessage = tr("Fill in all required fields");
×
1306
  }
1307

1308
  ui->statusLabel->setText(statusMessage);
×
1309
}
1310

1311
/**
1312
 * @brief ConfigDialog::useTemplate set preference for using templates.
1313
 * @param useTemplate
1314
 */
1315
void ConfigDialog::useTemplate(bool useTemplate) {
×
1316
  ui->checkBoxUseTemplate->setChecked(useTemplate);
×
1317
  on_checkBoxUseTemplate_clicked();
×
1318
}
×
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