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

IJHack / QtPass / 23666274895

27 Mar 2026 08:32PM UTC coverage: 18.552% (+0.2%) from 18.391%
23666274895

push

github

web-flow
feat: use ed25519 for GPG key generation when available (#790)

* feat: use ed25519 for GPG key generation when available

Fixes #406

- Add gpgSupportsEd25519() to check if GPG 2.1+ is available
- Use --default-new-key-algo ed25519 when supported for faster key generation
- Add unit test for ed25519 support check

* feat: add Generate GPG key button to Config dialog

- Add 'Generate' button next to GPG path in Config dialog
- Opens KeygenDialog directly for easy key generation

Related to #406

* chore: update translation source references

10 of 19 new or added lines in 3 files covered. (52.63%)

167 existing lines in 6 files now uncovered.

930 of 5013 relevant lines covered (18.55%)

7.51 hits per line

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

0.0
/src/configdialog.cpp
1
// SPDX-FileCopyrightText: 2016 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
  ui->passPath->setText(QtPassSettings::getPassExecutable());
×
35
  setGitPath(QtPassSettings::getGitExecutable());
×
36
  ui->gpgPath->setText(QtPassSettings::getGpgExecutable());
×
37
  ui->storePath->setText(QtPassSettings::getPassStore());
×
38

39
  ui->spinBoxAutoclearSeconds->setValue(QtPassSettings::getAutoclearSeconds());
×
40
  ui->spinBoxAutoclearPanelSeconds->setValue(
×
41
      QtPassSettings::getAutoclearPanelSeconds());
×
42
  ui->checkBoxHidePassword->setChecked(QtPassSettings::isHidePassword());
×
43
  ui->checkBoxHideContent->setChecked(QtPassSettings::isHideContent());
×
44
  ui->checkBoxUseMonospace->setChecked(QtPassSettings::isUseMonospace());
×
45
  ui->checkBoxDisplayAsIs->setChecked(QtPassSettings::isDisplayAsIs());
×
46
  ui->checkBoxNoLineWrapping->setChecked(QtPassSettings::isNoLineWrapping());
×
47
  ui->checkBoxAddGPGId->setChecked(QtPassSettings::isAddGPGId(true));
×
48

49
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
×
50
    ui->checkBoxHideOnClose->setChecked(QtPassSettings::isHideOnClose());
×
51
    ui->checkBoxStartMinimized->setChecked(QtPassSettings::isStartMinimized());
×
52
  } else {
53
    ui->checkBoxUseTrayIcon->setEnabled(false);
×
54
    ui->checkBoxUseTrayIcon->setToolTip(tr("System tray is not available"));
×
55
    ui->checkBoxHideOnClose->setEnabled(false);
×
56
    ui->checkBoxStartMinimized->setEnabled(false);
×
57
  }
58

59
  ui->checkBoxAvoidCapitals->setChecked(QtPassSettings::isAvoidCapitals());
×
60
  ui->checkBoxAvoidNumbers->setChecked(QtPassSettings::isAvoidNumbers());
×
61
  ui->checkBoxLessRandom->setChecked(QtPassSettings::isLessRandom());
×
62
  ui->checkBoxUseSymbols->setChecked(QtPassSettings::isUseSymbols());
×
63
  ui->plainTextEditTemplate->setPlainText(QtPassSettings::getPassTemplate());
×
64
  ui->checkBoxTemplateAllFields->setChecked(
×
65
      QtPassSettings::isTemplateAllFields());
×
66
  ui->checkBoxAutoPull->setChecked(QtPassSettings::isAutoPull());
×
67
  ui->checkBoxAutoPush->setChecked(QtPassSettings::isAutoPush());
×
68
  ui->checkBoxAlwaysOnTop->setChecked(QtPassSettings::isAlwaysOnTop());
×
69

70
#if defined(Q_OS_WIN)
71
  ui->checkBoxUseOtp->hide();
72
  ui->checkBoxUseQrencode->hide();
73
  ui->label_10->hide();
74
#endif
75

76
  if (!isPassOtpAvailable()) {
×
77
    ui->checkBoxUseOtp->setEnabled(false);
×
78
    ui->checkBoxUseOtp->setToolTip(
×
79
        tr("Pass OTP extension needs to be installed"));
×
80
  }
81

82
  if (!isQrencodeAvailable()) {
×
83
    ui->checkBoxUseQrencode->setEnabled(false);
×
84
    ui->checkBoxUseQrencode->setToolTip(tr("qrencode needs to be installed"));
×
85
  }
86

87
  setProfiles(QtPassSettings::getProfiles(), QtPassSettings::getProfile());
×
88
  setPwgenPath(QtPassSettings::getPwgenExecutable());
×
89
  setPasswordConfiguration(QtPassSettings::getPasswordConfiguration());
×
90

91
  usePass(QtPassSettings::isUsePass());
×
92
  useAutoclear(QtPassSettings::isUseAutoclear());
×
93
  useAutoclearPanel(QtPassSettings::isUseAutoclearPanel());
×
94
  useTrayIcon(QtPassSettings::isUseTrayIcon());
×
95
  useGit(QtPassSettings::isUseGit());
×
96

97
  useOtp(QtPassSettings::isUseOtp());
×
98
  useQrencode(QtPassSettings::isUseQrencode());
×
99

100
  usePwgen(QtPassSettings::isUsePwgen());
×
101
  useTemplate(QtPassSettings::isUseTemplate());
×
102

103
  ui->profileTable->verticalHeader()->hide();
×
104
  ui->profileTable->horizontalHeader()->setSectionResizeMode(
×
105
      1, QHeaderView::Stretch);
106
  ui->label->setText(ui->label->text() + VERSION);
×
107
  ui->comboBoxClipboard->clear();
×
108

109
  ui->comboBoxClipboard->addItem(tr("No Clipboard"));
×
110
  ui->comboBoxClipboard->addItem(tr("Always copy to clipboard"));
×
111
  ui->comboBoxClipboard->addItem(tr("On-demand copy to clipboard"));
×
112

113
  int currentIndex = QtPassSettings::getClipBoardTypeRaw();
×
114
  ui->comboBoxClipboard->setCurrentIndex(currentIndex);
×
115
  on_comboBoxClipboard_activated(currentIndex);
×
116

117
  QClipboard *clip = QApplication::clipboard();
×
118
  if (!clip->supportsSelection()) {
×
119
    useSelection(false);
×
120
    ui->checkBoxSelection->setVisible(false);
×
121
  } else {
122
    useSelection(QtPassSettings::isUseSelection());
×
123
  }
124

125
  if (Util::checkConfig()) {
×
126
    // Show Programs tab, which is likely
127
    // what the user needs to fix now.
128
    ui->tabWidget->setCurrentIndex(1);
×
129
  }
130

131
  connect(ui->profileTable, &QTableWidget::itemChanged, this,
×
132
          &ConfigDialog::onProfileTableItemChanged);
×
133
  connect(this, &ConfigDialog::accepted, this, &ConfigDialog::on_accepted);
×
134
}
×
135

136
/**
137
 * @brief ConfigDialog::~ConfigDialog config destructor, makes sure the
138
 * mainWindow knows about git, gpg and pass executables.
139
 */
140
ConfigDialog::~ConfigDialog() {
×
141
  QtPassSettings::setGitExecutable(ui->gitPath->text());
×
142
  QtPassSettings::setGpgExecutable(ui->gpgPath->text());
×
143
  QtPassSettings::setPassExecutable(ui->passPath->text());
×
144
}
×
145

146
/**
147
 * @brief ConfigDialog::setGitPath set the git executable path.
148
 * Make sure the checkBoxUseGit is updated.
149
 * @param path
150
 */
151
void ConfigDialog::setGitPath(const QString &path) {
×
152
  ui->gitPath->setText(path);
×
153
  ui->checkBoxUseGit->setEnabled(!path.isEmpty());
×
154
  if (path.isEmpty()) {
×
155
    useGit(false);
×
156
  }
157
}
×
158

159
/**
160
 * @brief ConfigDialog::usePass set wether or not we want to use pass.
161
 * Update radio buttons accordingly.
162
 * @param usePass
163
 */
164
void ConfigDialog::usePass(bool usePass) {
×
165
  ui->radioButtonNative->setChecked(!usePass);
×
166
  ui->radioButtonPass->setChecked(usePass);
×
167
  setGroupBoxState();
×
168
}
×
169

170
void ConfigDialog::validate(QTableWidgetItem *item) {
×
171
  bool status = true;
172

173
  if (item == nullptr) {
×
174
    for (int i = 0; i < ui->profileTable->rowCount(); i++) {
×
175
      for (int j = 0; j < ui->profileTable->columnCount(); j++) {
×
176
        QTableWidgetItem *_item = ui->profileTable->item(i, j);
×
177

178
        if (_item->text().isEmpty() && j != 2) {
×
179
          _item->setBackground(Qt::red);
×
180
          status = false;
181
          break;
182
        }
183
      }
184

185
      if (!status) {
186
        break;
187
      }
188
    }
189
  } else {
190
    if (item->text().isEmpty() && item->column() != 2) {
×
191
      item->setBackground(Qt::red);
×
192
      status = false;
193
    }
194
  }
195

196
  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status);
×
197
}
×
198

199
void ConfigDialog::on_accepted() {
×
200
  QtPassSettings::setPassExecutable(ui->passPath->text());
×
201
  QtPassSettings::setGitExecutable(ui->gitPath->text());
×
202
  QtPassSettings::setGpgExecutable(ui->gpgPath->text());
×
203
  QtPassSettings::setPassStore(
×
204
      Util::normalizeFolderPath(ui->storePath->text()));
×
205
  QtPassSettings::setUsePass(ui->radioButtonPass->isChecked());
×
206
  QtPassSettings::setClipBoardType(ui->comboBoxClipboard->currentIndex());
×
207
  QtPassSettings::setUseSelection(ui->checkBoxSelection->isChecked());
×
208
  QtPassSettings::setUseAutoclear(ui->checkBoxAutoclear->isChecked());
×
209
  QtPassSettings::setAutoclearSeconds(ui->spinBoxAutoclearSeconds->value());
×
210
  QtPassSettings::setUseAutoclearPanel(ui->checkBoxAutoclearPanel->isChecked());
×
211
  QtPassSettings::setAutoclearPanelSeconds(
×
212
      ui->spinBoxAutoclearPanelSeconds->value());
×
213
  QtPassSettings::setHidePassword(ui->checkBoxHidePassword->isChecked());
×
214
  QtPassSettings::setHideContent(ui->checkBoxHideContent->isChecked());
×
215
  QtPassSettings::setUseMonospace(ui->checkBoxUseMonospace->isChecked());
×
216
  QtPassSettings::setDisplayAsIs(ui->checkBoxDisplayAsIs->isChecked());
×
217
  QtPassSettings::setNoLineWrapping(ui->checkBoxNoLineWrapping->isChecked());
×
218
  QtPassSettings::setAddGPGId(ui->checkBoxAddGPGId->isChecked());
×
219
  QtPassSettings::setUseTrayIcon(ui->checkBoxUseTrayIcon->isEnabled() &&
×
220
                                 ui->checkBoxUseTrayIcon->isChecked());
×
221
  QtPassSettings::setHideOnClose(ui->checkBoxHideOnClose->isEnabled() &&
×
222
                                 ui->checkBoxHideOnClose->isChecked());
×
223
  QtPassSettings::setStartMinimized(ui->checkBoxStartMinimized->isEnabled() &&
×
224
                                    ui->checkBoxStartMinimized->isChecked());
×
225
  QtPassSettings::setProfiles(getProfiles());
×
226
  QtPassSettings::setUseGit(ui->checkBoxUseGit->isChecked());
×
227
  QtPassSettings::setUseOtp(ui->checkBoxUseOtp->isChecked());
×
228
  QtPassSettings::setUseQrencode(ui->checkBoxUseQrencode->isChecked());
×
229
  QtPassSettings::setPwgenExecutable(ui->pwgenPath->text());
×
230
  QtPassSettings::setUsePwgen(ui->checkBoxUsePwgen->isChecked());
×
231
  QtPassSettings::setAvoidCapitals(ui->checkBoxAvoidCapitals->isChecked());
×
232
  QtPassSettings::setAvoidNumbers(ui->checkBoxAvoidNumbers->isChecked());
×
233
  QtPassSettings::setLessRandom(ui->checkBoxLessRandom->isChecked());
×
234
  QtPassSettings::setUseSymbols(ui->checkBoxUseSymbols->isChecked());
×
235
  QtPassSettings::setPasswordConfiguration(getPasswordConfiguration());
×
236
  QtPassSettings::setUseTemplate(ui->checkBoxUseTemplate->isChecked());
×
237
  QtPassSettings::setPassTemplate(ui->plainTextEditTemplate->toPlainText());
×
238
  QtPassSettings::setTemplateAllFields(
×
239
      ui->checkBoxTemplateAllFields->isChecked());
×
240
  QtPassSettings::setAutoPush(ui->checkBoxAutoPush->isChecked());
×
241
  QtPassSettings::setAutoPull(ui->checkBoxAutoPull->isChecked());
×
242
  QtPassSettings::setAlwaysOnTop(ui->checkBoxAlwaysOnTop->isChecked());
×
243

244
  QtPassSettings::setVersion(VERSION);
×
245
}
×
246

247
void ConfigDialog::on_autodetectButton_clicked() {
×
248
  QString pass = Util::findBinaryInPath("pass");
×
249
  if (!pass.isEmpty()) {
×
250
    ui->passPath->setText(pass);
×
251
  }
252
  usePass(!pass.isEmpty());
×
253
  QString gpg = Util::findBinaryInPath("gpg2");
×
254
  if (gpg.isEmpty()) {
×
255
    gpg = Util::findBinaryInPath("gpg");
×
256
  }
257
  if (!gpg.isEmpty()) {
×
258
    ui->gpgPath->setText(gpg);
×
259
  }
260
  QString git = Util::findBinaryInPath("git");
×
261
  if (!git.isEmpty()) {
×
262
    ui->gitPath->setText(git);
×
263
  }
264
  QString pwgen = Util::findBinaryInPath("pwgen");
×
265
  if (!pwgen.isEmpty()) {
×
266
    ui->pwgenPath->setText(pwgen);
×
267
  }
268
}
×
269

270
/**
271
 * @brief ConfigDialog::on_radioButtonNative_clicked wrapper for
272
 * ConfigDialog::setGroupBoxState()
273
 */
274
void ConfigDialog::on_radioButtonNative_clicked() { setGroupBoxState(); }
×
275

276
/**
277
 * @brief ConfigDialog::on_radioButtonPass_clicked wrapper for
278
 * ConfigDialog::setGroupBoxState()
279
 */
280
void ConfigDialog::on_radioButtonPass_clicked() { setGroupBoxState(); }
×
281

282
/**
283
 * @brief ConfigDialog::getSecretKeys get list of secret/private keys
284
 * @return QStringList keys
285
 */
286
auto ConfigDialog::getSecretKeys() -> QStringList {
×
287
  QList<UserInfo> keys = QtPassSettings::getPass()->listKeys("", true);
×
288
  QStringList names;
×
289

290
  if (keys.empty()) {
×
291
    return names;
292
  }
293

294
  foreach (const UserInfo &sec, keys)
×
295
    names << sec.name;
×
296

297
  return names;
×
298
}
299

300
/**
301
 * @brief ConfigDialog::setGroupBoxState update checkboxes.
302
 */
303
void ConfigDialog::setGroupBoxState() {
×
304
  bool state = ui->radioButtonPass->isChecked();
×
305
  ui->groupBoxNative->setEnabled(!state);
×
306
  ui->groupBoxPass->setEnabled(state);
×
307
}
×
308

309
/**
310
 * @brief ConfigDialog::selectExecutable pop-up to choose an executable.
311
 * @return
312
 */
313
auto ConfigDialog::selectExecutable() -> QString {
×
314
  QFileDialog dialog(this);
×
315
  dialog.setFileMode(QFileDialog::ExistingFile);
×
316
  dialog.setOption(QFileDialog::ReadOnly);
×
317
  if (dialog.exec()) {
×
318
    return dialog.selectedFiles().constFirst();
×
319
  }
320

321
  return {};
322
}
×
323

324
/**
325
 * @brief ConfigDialog::selectFolder pop-up to choose a folder.
326
 * @return
327
 */
328
auto ConfigDialog::selectFolder() -> QString {
×
329
  QFileDialog dialog(this);
×
330
  dialog.setFileMode(QFileDialog::Directory);
×
331
  dialog.setFilter(QDir::NoFilter);
×
332
  dialog.setOption(QFileDialog::ShowDirsOnly);
×
333
  if (dialog.exec()) {
×
334
    return dialog.selectedFiles().constFirst();
×
335
  }
336

337
  return {};
338
}
×
339

340
/**
341
 * @brief ConfigDialog::on_toolButtonGit_clicked get git application.
342
 * Enable checkboxes if found.
343
 */
344
void ConfigDialog::on_toolButtonGit_clicked() {
×
345
  QString git = selectExecutable();
×
346
  bool state = !git.isEmpty();
×
347
  if (state) {
×
348
    ui->gitPath->setText(git);
×
349
  } else {
350
    useGit(false);
×
351
  }
352

353
  ui->checkBoxUseGit->setEnabled(state);
×
354
}
×
355

356
/**
357
 * @brief ConfigDialog::on_toolButtonGpg_clicked get gpg application.
358
 */
359
void ConfigDialog::on_toolButtonGpg_clicked() {
×
360
  QString gpg = selectExecutable();
×
361
  if (!gpg.isEmpty()) {
×
362
    ui->gpgPath->setText(gpg);
×
363
  }
364
}
×
365

366
/**
367
 * @brief ConfigDialog::on_pushButtonGenerateKey_clicked open keygen dialog.
368
 */
NEW
369
void ConfigDialog::on_pushButtonGenerateKey_clicked() {
×
NEW
370
  KeygenDialog d(this);
×
NEW
371
  d.exec();
×
NEW
372
}
×
373

374
/**
375
 * @brief ConfigDialog::on_toolButtonPass_clicked get pass application.
376
 */
377
void ConfigDialog::on_toolButtonPass_clicked() {
×
378
  QString pass = selectExecutable();
×
379
  if (!pass.isEmpty()) {
×
380
    ui->passPath->setText(pass);
×
381
  }
382
}
×
383

384
/**
385
 * @brief ConfigDialog::on_toolButtonStore_clicked get .password-store
386
 * location.s
387
 */
388
void ConfigDialog::on_toolButtonStore_clicked() {
×
389
  QString store = selectFolder();
×
390
  if (!store.isEmpty()) { // TODO(annejan): call check
×
391
    ui->storePath->setText(store);
×
392
  }
393
}
×
394

395
/**
396
 * @brief ConfigDialog::on_comboBoxClipboard_activated show and hide options.
397
 * @param index of selectbox (0 = no clipboard).
398
 */
399
void ConfigDialog::on_comboBoxClipboard_activated(int index) {
×
400
  bool state = index > 0;
×
401

402
  ui->checkBoxSelection->setEnabled(state);
×
403
  ui->checkBoxAutoclear->setEnabled(state);
×
404
  ui->checkBoxHidePassword->setEnabled(state);
×
405
  ui->checkBoxHideContent->setEnabled(state);
×
406
  if (state) {
×
407
    ui->spinBoxAutoclearSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
408
    ui->labelSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
409
  } else {
410
    ui->spinBoxAutoclearSeconds->setEnabled(false);
×
411
    ui->labelSeconds->setEnabled(false);
×
412
  }
413
}
×
414

415
/**
416
 * @brief ConfigDialog::on_checkBoxAutoclearPanel_clicked enable and disable
417
 * options based on autoclear use.
418
 */
419
void ConfigDialog::on_checkBoxAutoclearPanel_clicked() {
×
420
  bool state = ui->checkBoxAutoclearPanel->isChecked();
×
421
  ui->spinBoxAutoclearPanelSeconds->setEnabled(state);
×
422
  ui->labelPanelSeconds->setEnabled(state);
×
423
}
×
424

425
/**
426
 * @brief ConfigDialog::useSelection set the clipboard type use from
427
 * MainWindow.
428
 * @param useSelection
429
 */
430
void ConfigDialog::useSelection(bool useSelection) {
×
431
  ui->checkBoxSelection->setChecked(useSelection);
×
432
  on_checkBoxSelection_clicked();
×
433
}
×
434

435
/**
436
 * @brief ConfigDialog::useAutoclear set the clipboard autoclear use from
437
 * MainWindow.
438
 * @param useAutoclear
439
 */
440
void ConfigDialog::useAutoclear(bool useAutoclear) {
×
441
  ui->checkBoxAutoclear->setChecked(useAutoclear);
×
442
  on_checkBoxAutoclear_clicked();
×
443
}
×
444

445
/**
446
 * @brief ConfigDialog::useAutoclearPanel set the panel autoclear use from
447
 * MainWindow.
448
 * @param useAutoclearPanel
449
 */
450
void ConfigDialog::useAutoclearPanel(bool useAutoclearPanel) {
×
451
  ui->checkBoxAutoclearPanel->setChecked(useAutoclearPanel);
×
452
  on_checkBoxAutoclearPanel_clicked();
×
453
}
×
454

455
/**
456
 * @brief ConfigDialog::on_checkBoxSelection_clicked checkbox clicked, update
457
 * state via ConfigDialog::on_comboBoxClipboard_activated
458
 */
459
void ConfigDialog::on_checkBoxSelection_clicked() {
×
460
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
461
}
×
462

463
/**
464
 * @brief ConfigDialog::on_checkBoxAutoclear_clicked checkbox clicked, update
465
 * state via ConfigDialog::on_comboBoxClipboard_activated
466
 */
467
void ConfigDialog::on_checkBoxAutoclear_clicked() {
×
468
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
469
}
×
470

471
/**
472
 * @brief ConfigDialog::genKey tunnel function to make MainWindow generate a
473
 * gpg key pair.
474
 * @todo refactor the process to not be entangled so much.
475
 * @param batch
476
 * @param dialog
477
 */
478
void ConfigDialog::genKey(const QString &batch, QDialog *dialog) {
×
479
  mainWindow->generateKeyPair(batch, dialog);
×
480
}
×
481

482
/**
483
 * @brief ConfigDialog::setProfiles set the profiles and chosen profile from
484
 * MainWindow.
485
 * @param profiles
486
 * @param profile
487
 */
488
void ConfigDialog::setProfiles(QHash<QString, QHash<QString, QString>> profiles,
×
489
                               const QString &currentProfile) {
490
  // dbg()<< profiles;
491
  if (profiles.contains("")) {
×
492
    profiles.remove("");
×
493
    // remove weird "" key value pairs
494
  }
495

496
  ui->profileTable->setRowCount(profiles.count());
×
497
  QHashIterator<QString, QHash<QString, QString>> i(profiles);
×
498
  int n = 0;
499
  while (i.hasNext()) {
×
500
    i.next();
501
    if (!i.value().isEmpty() && !i.key().isEmpty()) {
×
502
      ui->profileTable->setItem(n, 0, new QTableWidgetItem(i.key()));
×
503
      ui->profileTable->setItem(n, 1,
×
504
                                new QTableWidgetItem(i.value().value("path")));
×
505
      ui->profileTable->setItem(
×
506
          n, 2, new QTableWidgetItem(i.value().value("signingKey")));
×
507
      // dbg()<< "naam:" + i.key();
508
      if (i.key() == currentProfile) {
×
509
        ui->profileTable->selectRow(n);
×
510
      }
511
    }
512
    ++n;
×
513
  }
514
}
×
515

516
/**
517
 * @brief ConfigDialog::getProfiles return profile list.
518
 * @return
519
 */
520
auto ConfigDialog::getProfiles() -> QHash<QString, QHash<QString, QString>> {
×
521
  QHash<QString, QHash<QString, QString>> profiles;
×
522
  // Check?
523
  for (int i = 0; i < ui->profileTable->rowCount(); ++i) {
×
524
    QHash<QString, QString> profile;
×
525
    QTableWidgetItem *pathItem = ui->profileTable->item(i, 1);
×
526
    if (nullptr != pathItem) {
×
527
      QTableWidgetItem *item = ui->profileTable->item(i, 0);
×
528
      if (item == nullptr) {
×
529
        continue;
530
      }
531
      profile["path"] = pathItem->text();
×
532
      QTableWidgetItem *signingKeyItem = ui->profileTable->item(i, 2);
×
533
      if (nullptr != signingKeyItem) {
×
534
        profile["signingKey"] = signingKeyItem->text();
×
535
      }
536
      profiles.insert(item->text(), profile);
×
537
    }
538
  }
×
539
  return profiles;
×
540
}
×
541

542
/**
543
 * @brief ConfigDialog::on_addButton_clicked add a profile row.
544
 */
545
void ConfigDialog::on_addButton_clicked() {
×
546
  int n = ui->profileTable->rowCount();
×
547
  ui->profileTable->insertRow(n);
×
548
  ui->profileTable->setItem(n, 0, new QTableWidgetItem());
×
549
  ui->profileTable->setItem(n, 1, new QTableWidgetItem(ui->storePath->text()));
×
550
  ui->profileTable->setItem(n, 2, new QTableWidgetItem());
×
551
  ui->profileTable->selectRow(n);
×
552
  ui->deleteButton->setEnabled(true);
×
553

554
  validate();
×
555
}
×
556

557
/**
558
 * @brief ConfigDialog::on_deleteButton_clicked remove a profile row.
559
 */
560
void ConfigDialog::on_deleteButton_clicked() {
×
561
  QSet<int> selectedRows; //  we use a set to prevent doubles
562
  QList<QTableWidgetItem *> itemList = ui->profileTable->selectedItems();
×
563
  if (itemList.count() == 0) {
×
564
    QMessageBox::warning(this, tr("No profile selected"),
×
565
                         tr("No profile selected to delete"));
×
566
    return;
567
  }
568
  QTableWidgetItem *item;
569
  foreach (item, itemList)
×
570
    selectedRows.insert(item->row());
×
571
  // get a list, and sort it big to small
572
  QList<int> rows = selectedRows.values();
×
573
  std::sort(rows.begin(), rows.end());
×
574
  // now actually do the removing:
575
  foreach (int row, rows)
×
576
    ui->profileTable->removeRow(row);
×
577
  if (ui->profileTable->rowCount() < 1) {
×
578
    ui->deleteButton->setEnabled(false);
×
579
  }
580

581
  validate();
×
582
}
583

584
/**
585
 * @brief ConfigDialog::criticalMessage weapper for showing critical messages
586
 * in a popup.
587
 * @param title
588
 * @param text
589
 */
590
void ConfigDialog::criticalMessage(const QString &title, const QString &text) {
×
591
  QMessageBox::critical(this, title, text, QMessageBox::Ok, QMessageBox::Ok);
×
592
}
×
593

594
auto ConfigDialog::isQrencodeAvailable() -> bool {
×
595
#ifdef Q_OS_WIN
596
  return false;
597
#else
598
  QProcess which;
×
599
  which.start("which", QStringList() << "qrencode");
×
600
  which.waitForFinished();
×
601
  QtPassSettings::setQrencodeExecutable(
×
602
      which.readAllStandardOutput().trimmed());
×
603
  return which.exitCode() == 0;
×
604
#endif
605
}
×
606

607
auto ConfigDialog::isPassOtpAvailable() -> bool {
×
608
#ifdef Q_OS_WIN
609
  return false;
610
#else
611
  QProcess pass;
×
612
  pass.start(QtPassSettings::getPassExecutable(), QStringList() << "otp"
×
613
                                                                << "--help");
×
614
  pass.waitForFinished(2000);
×
615
  return pass.exitCode() == 0;
×
616
#endif
617
}
×
618

619
/**
620
 * @brief ConfigDialog::wizard first-time use wizard.
621
 * @todo make this thing more reliable.
622
 */
623
void ConfigDialog::wizard() {
×
624
  Util::checkConfig();
×
625
  on_autodetectButton_clicked();
×
626

627
  if (!checkGpgExistence()) {
×
628
    return;
629
  }
630
  if (!checkSecretKeys()) {
×
631
    return;
632
  }
633
  if (!checkPasswordStore()) {
×
634
    return;
635
  }
636
  handleGpgIdFile();
×
637

638
  ui->checkBoxHidePassword->setCheckState(Qt::Checked);
×
639
}
640

641
auto ConfigDialog::checkGpgExistence() -> bool {
×
642
  QString gpg = ui->gpgPath->text();
×
643
  if (!gpg.startsWith("wsl ") && !QFile(gpg).exists()) {
×
644
    criticalMessage(
×
645
        tr("GnuPG not found"),
×
646
#ifdef Q_OS_WIN
647
#ifdef WINSTORE
648
        tr("Please install GnuPG on your system.<br>Install "
649
           "<strong>Ubuntu</strong> from the Microsoft Store to get it.<br>"
650
           "If you already did so, make sure you started it once and<br>"
651
           "click \"Autodetect\" in the next dialog.")
652
#else
653
        tr("Please install GnuPG on your system.<br>Install "
654
           "<strong>Ubuntu</strong> from the Microsoft Store<br>or <a "
655
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
656
           "from GnuPG.org")
657
#endif
658
#else
659
        tr("Please install GnuPG on your system.<br>Install "
×
660
           "<strong>gpg</strong> using your favorite package manager<br>or "
661
           "<a "
662
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
663
           "from GnuPG.org")
664
#endif
665
    );
666
    return false;
×
667
  }
668
  return true;
669
}
670

671
auto ConfigDialog::checkSecretKeys() -> bool {
×
672
  QString gpg = ui->gpgPath->text();
×
673
  QStringList names = getSecretKeys();
×
674

675
#ifdef QT_DEBUG
676
  dbg() << names;
677
#endif
678

679
  if ((gpg.startsWith("wsl ") || QFile(gpg).exists()) && names.empty()) {
×
680
    KeygenDialog d(this);
×
681
    return d.exec();
×
682
  }
×
683
  return true;
684
}
685

686
auto ConfigDialog::checkPasswordStore() -> bool {
×
687
  QString passStore = ui->storePath->text();
×
688

689
  if (!QFile(passStore).exists()) {
×
690
    if (QMessageBox::question(
×
691
            this, tr("Create password-store?"),
×
692
            tr("Would you like to create a password-store at %1?")
×
693
                .arg(passStore),
×
694
            QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
695
      if (!QDir().mkdir(passStore)) {
×
696
        QMessageBox::warning(
×
697
            this, tr("Error"),
×
698
            tr("Failed to create password-store at: %1").arg(passStore));
×
699
        return false;
×
700
      }
701
#ifdef Q_OS_WIN
702
      SetFileAttributes(passStore.toStdWString().c_str(),
703
                        FILE_ATTRIBUTE_HIDDEN);
704
#endif
705
      if (ui->checkBoxUseGit->isChecked()) {
×
706
        emit mainWindow->passGitInitNeeded();
×
707
      }
708
      mainWindow->userDialog(passStore);
×
709
    }
710
  }
711
  return true;
712
}
713

714
void ConfigDialog::handleGpgIdFile() {
×
715
  QString passStore = ui->storePath->text();
×
716
  if (!QFile(QDir(passStore).filePath(".gpg-id")).exists()) {
×
717
#ifdef QT_DEBUG
718
    dbg() << ".gpg-id file does not exist";
719
#endif
720
    criticalMessage(tr("Password store not initialised"),
×
721
                    tr("The folder %1 doesn't seem to be a password store or "
×
722
                       "is not yet initialised.")
723
                        .arg(passStore));
×
724

725
    while (!QFile(passStore).exists()) {
×
726
      on_toolButtonStore_clicked();
×
727
      if (passStore == ui->storePath->text()) {
×
728
        return;
729
      }
730
      passStore = ui->storePath->text();
×
731
    }
732
    if (!QFile(passStore + ".gpg-id").exists()) {
×
733
#ifdef QT_DEBUG
734
      dbg() << ".gpg-id file still does not exist :/";
735
#endif
736
      mainWindow->userDialog(passStore);
×
737
    }
738
  }
739
}
740

741
/**
742
 * @brief ConfigDialog::useTrayIcon set preference for using trayicon.
743
 * Enable or disable related checkboxes accordingly.
744
 * @param useSystray
745
 */
746
void ConfigDialog::useTrayIcon(bool useSystray) {
×
747
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
×
748
    ui->checkBoxUseTrayIcon->setChecked(useSystray);
×
749
    ui->checkBoxHideOnClose->setEnabled(useSystray);
×
750
    ui->checkBoxStartMinimized->setEnabled(useSystray);
×
751

752
    if (!useSystray) {
×
753
      ui->checkBoxHideOnClose->setChecked(false);
×
754
      ui->checkBoxStartMinimized->setChecked(false);
×
755
    }
756
  }
757
}
×
758

759
/**
760
 * @brief ConfigDialog::on_checkBoxUseTrayIcon_clicked enable and disable
761
 * related checkboxes.
762
 */
763
void ConfigDialog::on_checkBoxUseTrayIcon_clicked() {
×
764
  bool state = ui->checkBoxUseTrayIcon->isChecked();
×
765
  ui->checkBoxHideOnClose->setEnabled(state);
×
766
  ui->checkBoxStartMinimized->setEnabled(state);
×
767
}
×
768

769
/**
770
 * @brief ConfigDialog::closeEvent close this window.
771
 * @param event
772
 */
773
void ConfigDialog::closeEvent(QCloseEvent *event) {
×
774
  // TODO(annejan): save window size or something?
775
  event->accept();
776
}
×
777

778
/**
779
 * @brief ConfigDialog::useGit set preference for using git.
780
 * @param useGit
781
 */
782
void ConfigDialog::useGit(bool useGit) {
×
783
  ui->checkBoxUseGit->setChecked(useGit);
×
784
  on_checkBoxUseGit_clicked();
×
785
}
×
786

787
/**
788
 * @brief ConfigDialog::useOtp set preference for using otp plugin.
789
 * @param useOtp
790
 */
791
void ConfigDialog::useOtp(bool useOtp) {
×
792
  ui->checkBoxUseOtp->setChecked(useOtp);
×
793
}
×
794

795
/**
796
 * @brief ConfigDialog::useOtp set preference for using otp plugin.
797
 * @param useOtp
798
 */
799
void ConfigDialog::useQrencode(bool useQrencode) {
×
800
  ui->checkBoxUseQrencode->setChecked(useQrencode);
×
801
}
×
802

803
/**
804
 * @brief ConfigDialog::on_checkBoxUseGit_clicked enable or disable related
805
 * checkboxes.
806
 */
807
void ConfigDialog::on_checkBoxUseGit_clicked() {
×
808
  ui->checkBoxAddGPGId->setEnabled(ui->checkBoxUseGit->isChecked());
×
809
  ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
×
810
  ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
×
811
}
×
812

813
/**
814
 * @brief ConfigDialog::on_toolButtonPwgen_clicked enable or disable related
815
 * options in the interface.
816
 */
817
void ConfigDialog::on_toolButtonPwgen_clicked() {
×
818
  QString pwgen = selectExecutable();
×
819
  if (!pwgen.isEmpty()) {
×
820
    ui->pwgenPath->setText(pwgen);
×
821
    ui->checkBoxUsePwgen->setEnabled(true);
×
822
  } else {
823
    ui->checkBoxUsePwgen->setEnabled(false);
×
824
    ui->checkBoxUsePwgen->setChecked(false);
×
825
  }
826
}
×
827

828
/**
829
 * @brief ConfigDialog::setPwgenPath set pwgen executable path.
830
 * Enable or disable related options in the interface.
831
 * @param pwgen
832
 */
833
void ConfigDialog::setPwgenPath(const QString &pwgen) {
×
834
  ui->pwgenPath->setText(pwgen);
×
835
  if (pwgen.isEmpty()) {
×
836
    ui->checkBoxUsePwgen->setChecked(false);
×
837
    ui->checkBoxUsePwgen->setEnabled(false);
×
838
  }
839
  on_checkBoxUsePwgen_clicked();
×
840
}
×
841

842
/**
843
 * @brief ConfigDialog::on_checkBoxUsPwgen_clicked enable or disable related
844
 * options in the interface.
845
 */
846
void ConfigDialog::on_checkBoxUsePwgen_clicked() {
×
847
  bool usePwgen = ui->checkBoxUsePwgen->isChecked();
×
848
  ui->checkBoxAvoidCapitals->setEnabled(usePwgen);
×
849
  ui->checkBoxAvoidNumbers->setEnabled(usePwgen);
×
850
  ui->checkBoxLessRandom->setEnabled(usePwgen);
×
851
  ui->checkBoxUseSymbols->setEnabled(usePwgen);
×
852
  ui->lineEditPasswordChars->setEnabled(!usePwgen);
×
853
  ui->labelPasswordChars->setEnabled(!usePwgen);
×
854
  ui->passwordCharTemplateSelector->setEnabled(!usePwgen);
×
855
}
×
856

857
/**
858
 * @brief ConfigDialog::usePwgen set preference for using pwgen (can be
859
 * overruled buy empty pwgenPath).
860
 * enable or disable related options in the interface via
861
 * ConfigDialog::on_checkBoxUsePwgen_clicked
862
 * @param usePwgen
863
 */
864
void ConfigDialog::usePwgen(bool usePwgen) {
×
865
  if (ui->pwgenPath->text().isEmpty()) {
×
866
    usePwgen = false;
867
  }
868
  ui->checkBoxUsePwgen->setChecked(usePwgen);
×
869
  on_checkBoxUsePwgen_clicked();
×
870
}
×
871

872
void ConfigDialog::setPasswordConfiguration(
×
873
    const PasswordConfiguration &config) {
874
  ui->spinBoxPasswordLength->setValue(config.length);
×
875
  ui->passwordCharTemplateSelector->setCurrentIndex(config.selected);
×
876
  if (config.selected != PasswordConfiguration::CUSTOM) {
×
877
    ui->lineEditPasswordChars->setEnabled(false);
×
878
  }
879
  ui->lineEditPasswordChars->setText(config.Characters[config.selected]);
×
880
}
×
881

882
auto ConfigDialog::getPasswordConfiguration() -> PasswordConfiguration {
×
883
  PasswordConfiguration config;
×
884
  config.length = ui->spinBoxPasswordLength->value();
×
885
  config.selected = static_cast<PasswordConfiguration::characterSet>(
×
886
      ui->passwordCharTemplateSelector->currentIndex());
×
887
  config.Characters[PasswordConfiguration::CUSTOM] =
888
      ui->lineEditPasswordChars->text();
×
889
  return config;
×
890
}
×
891

892
/**
893
 * @brief ConfigDialog::on_passwordCharTemplateSelector_activated sets the
894
 * passwordChar Template
895
 * combo box to the desired entry
896
 * @param entry of
897
 */
898
void ConfigDialog::on_passwordCharTemplateSelector_activated(int index) {
×
899
  ui->lineEditPasswordChars->setText(
×
900
      QtPassSettings::getPasswordConfiguration().Characters[index]);
×
901
  if (index == 3) {
×
902
    ui->lineEditPasswordChars->setEnabled(true);
×
903
  } else {
904
    ui->lineEditPasswordChars->setEnabled(false);
×
905
  }
906
}
×
907

908
/**
909
 * @brief ConfigDialog::on_checkBoxUseTemplate_clicked enable or disable the
910
 * template field and options.
911
 */
912
void ConfigDialog::on_checkBoxUseTemplate_clicked() {
×
913
  ui->plainTextEditTemplate->setEnabled(ui->checkBoxUseTemplate->isChecked());
×
914
  ui->checkBoxTemplateAllFields->setEnabled(
×
915
      ui->checkBoxUseTemplate->isChecked());
×
916
}
×
917

918
void ConfigDialog::onProfileTableItemChanged(QTableWidgetItem *item) {
×
919
  validate(item);
×
920
}
×
921

922
/**
923
 * @brief ConfigDialog::useTemplate set preference for using templates.
924
 * @param useTemplate
925
 */
926
void ConfigDialog::useTemplate(bool useTemplate) {
×
927
  ui->checkBoxUseTemplate->setChecked(useTemplate);
×
928
  on_checkBoxUseTemplate_clicked();
×
929
}
×
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