Line data Source code
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 0 : ConfigDialog::ConfigDialog(MainWindow *parent)
30 0 : : QDialog(parent), ui(new Ui::ConfigDialog) {
31 0 : mainWindow = parent;
32 0 : ui->setupUi(this);
33 :
34 0 : ui->passPath->setText(QtPassSettings::getPassExecutable());
35 0 : setGitPath(QtPassSettings::getGitExecutable());
36 0 : ui->gpgPath->setText(QtPassSettings::getGpgExecutable());
37 0 : ui->storePath->setText(QtPassSettings::getPassStore());
38 :
39 0 : ui->spinBoxAutoclearSeconds->setValue(QtPassSettings::getAutoclearSeconds());
40 0 : ui->spinBoxAutoclearPanelSeconds->setValue(
41 0 : QtPassSettings::getAutoclearPanelSeconds());
42 0 : ui->checkBoxHidePassword->setChecked(QtPassSettings::isHidePassword());
43 0 : ui->checkBoxHideContent->setChecked(QtPassSettings::isHideContent());
44 0 : ui->checkBoxUseMonospace->setChecked(QtPassSettings::isUseMonospace());
45 0 : ui->checkBoxDisplayAsIs->setChecked(QtPassSettings::isDisplayAsIs());
46 0 : ui->checkBoxNoLineWrapping->setChecked(QtPassSettings::isNoLineWrapping());
47 0 : ui->checkBoxAddGPGId->setChecked(QtPassSettings::isAddGPGId(true));
48 :
49 0 : if (QSystemTrayIcon::isSystemTrayAvailable()) {
50 0 : ui->checkBoxHideOnClose->setChecked(QtPassSettings::isHideOnClose());
51 0 : ui->checkBoxStartMinimized->setChecked(QtPassSettings::isStartMinimized());
52 : } else {
53 0 : ui->checkBoxUseTrayIcon->setEnabled(false);
54 0 : ui->checkBoxUseTrayIcon->setToolTip(tr("System tray is not available"));
55 0 : ui->checkBoxHideOnClose->setEnabled(false);
56 0 : ui->checkBoxStartMinimized->setEnabled(false);
57 : }
58 :
59 0 : ui->checkBoxAvoidCapitals->setChecked(QtPassSettings::isAvoidCapitals());
60 0 : ui->checkBoxAvoidNumbers->setChecked(QtPassSettings::isAvoidNumbers());
61 0 : ui->checkBoxLessRandom->setChecked(QtPassSettings::isLessRandom());
62 0 : ui->checkBoxUseSymbols->setChecked(QtPassSettings::isUseSymbols());
63 0 : ui->plainTextEditTemplate->setPlainText(QtPassSettings::getPassTemplate());
64 0 : ui->checkBoxTemplateAllFields->setChecked(
65 0 : QtPassSettings::isTemplateAllFields());
66 0 : ui->checkBoxAutoPull->setChecked(QtPassSettings::isAutoPull());
67 0 : ui->checkBoxAutoPush->setChecked(QtPassSettings::isAutoPush());
68 0 : 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 0 : if (!isPassOtpAvailable()) {
77 0 : ui->checkBoxUseOtp->setEnabled(false);
78 0 : ui->checkBoxUseOtp->setToolTip(
79 0 : tr("Pass OTP extension needs to be installed"));
80 : }
81 :
82 0 : if (!isQrencodeAvailable()) {
83 0 : ui->checkBoxUseQrencode->setEnabled(false);
84 0 : ui->checkBoxUseQrencode->setToolTip(tr("qrencode needs to be installed"));
85 : }
86 :
87 0 : setProfiles(QtPassSettings::getProfiles(), QtPassSettings::getProfile());
88 0 : setPwgenPath(QtPassSettings::getPwgenExecutable());
89 0 : setPasswordConfiguration(QtPassSettings::getPasswordConfiguration());
90 :
91 0 : usePass(QtPassSettings::isUsePass());
92 0 : useAutoclear(QtPassSettings::isUseAutoclear());
93 0 : useAutoclearPanel(QtPassSettings::isUseAutoclearPanel());
94 0 : useTrayIcon(QtPassSettings::isUseTrayIcon());
95 0 : useGit(QtPassSettings::isUseGit());
96 :
97 0 : useOtp(QtPassSettings::isUseOtp());
98 0 : useQrencode(QtPassSettings::isUseQrencode());
99 :
100 0 : usePwgen(QtPassSettings::isUsePwgen());
101 0 : useTemplate(QtPassSettings::isUseTemplate());
102 :
103 0 : ui->profileTable->verticalHeader()->hide();
104 0 : ui->profileTable->horizontalHeader()->setSectionResizeMode(
105 : 1, QHeaderView::Stretch);
106 0 : ui->label->setText(ui->label->text() + VERSION);
107 0 : ui->comboBoxClipboard->clear();
108 :
109 0 : ui->comboBoxClipboard->addItem(tr("No Clipboard"));
110 0 : ui->comboBoxClipboard->addItem(tr("Always copy to clipboard"));
111 0 : ui->comboBoxClipboard->addItem(tr("On-demand copy to clipboard"));
112 :
113 0 : int currentIndex = QtPassSettings::getClipBoardTypeRaw();
114 0 : ui->comboBoxClipboard->setCurrentIndex(currentIndex);
115 0 : on_comboBoxClipboard_activated(currentIndex);
116 :
117 0 : QClipboard *clip = QApplication::clipboard();
118 0 : if (!clip->supportsSelection()) {
119 0 : useSelection(false);
120 0 : ui->checkBoxSelection->setVisible(false);
121 : } else {
122 0 : useSelection(QtPassSettings::isUseSelection());
123 : }
124 :
125 0 : if (Util::checkConfig()) {
126 : // Show Programs tab, which is likely
127 : // what the user needs to fix now.
128 0 : ui->tabWidget->setCurrentIndex(1);
129 : }
130 :
131 0 : connect(ui->profileTable, &QTableWidget::itemChanged, this,
132 0 : &ConfigDialog::onProfileTableItemChanged);
133 0 : connect(this, &ConfigDialog::accepted, this, &ConfigDialog::on_accepted);
134 0 : }
135 :
136 : /**
137 : * @brief ConfigDialog::~ConfigDialog config destructor, makes sure the
138 : * mainWindow knows about git, gpg and pass executables.
139 : */
140 0 : ConfigDialog::~ConfigDialog() {
141 0 : QtPassSettings::setGitExecutable(ui->gitPath->text());
142 0 : QtPassSettings::setGpgExecutable(ui->gpgPath->text());
143 0 : QtPassSettings::setPassExecutable(ui->passPath->text());
144 0 : }
145 :
146 : /**
147 : * @brief ConfigDialog::setGitPath set the git executable path.
148 : * Make sure the checkBoxUseGit is updated.
149 : * @param path
150 : */
151 0 : void ConfigDialog::setGitPath(const QString &path) {
152 0 : ui->gitPath->setText(path);
153 0 : ui->checkBoxUseGit->setEnabled(!path.isEmpty());
154 0 : if (path.isEmpty()) {
155 0 : useGit(false);
156 : }
157 0 : }
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 0 : void ConfigDialog::usePass(bool usePass) {
165 0 : ui->radioButtonNative->setChecked(!usePass);
166 0 : ui->radioButtonPass->setChecked(usePass);
167 0 : setGroupBoxState();
168 0 : }
169 :
170 0 : void ConfigDialog::validate(QTableWidgetItem *item) {
171 : bool status = true;
172 :
173 0 : if (item == nullptr) {
174 0 : for (int i = 0; i < ui->profileTable->rowCount(); i++) {
175 0 : for (int j = 0; j < ui->profileTable->columnCount(); j++) {
176 0 : QTableWidgetItem *_item = ui->profileTable->item(i, j);
177 :
178 0 : if (_item->text().isEmpty() && j != 2) {
179 0 : _item->setBackground(Qt::red);
180 : status = false;
181 : break;
182 : }
183 : }
184 :
185 : if (!status) {
186 : break;
187 : }
188 : }
189 : } else {
190 0 : if (item->text().isEmpty() && item->column() != 2) {
191 0 : item->setBackground(Qt::red);
192 : status = false;
193 : }
194 : }
195 :
196 0 : ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status);
197 0 : }
198 :
199 0 : void ConfigDialog::on_accepted() {
200 0 : QtPassSettings::setPassExecutable(ui->passPath->text());
201 0 : QtPassSettings::setGitExecutable(ui->gitPath->text());
202 0 : QtPassSettings::setGpgExecutable(ui->gpgPath->text());
203 0 : QtPassSettings::setPassStore(
204 0 : Util::normalizeFolderPath(ui->storePath->text()));
205 0 : QtPassSettings::setUsePass(ui->radioButtonPass->isChecked());
206 0 : QtPassSettings::setClipBoardType(ui->comboBoxClipboard->currentIndex());
207 0 : QtPassSettings::setUseSelection(ui->checkBoxSelection->isChecked());
208 0 : QtPassSettings::setUseAutoclear(ui->checkBoxAutoclear->isChecked());
209 0 : QtPassSettings::setAutoclearSeconds(ui->spinBoxAutoclearSeconds->value());
210 0 : QtPassSettings::setUseAutoclearPanel(ui->checkBoxAutoclearPanel->isChecked());
211 0 : QtPassSettings::setAutoclearPanelSeconds(
212 0 : ui->spinBoxAutoclearPanelSeconds->value());
213 0 : QtPassSettings::setHidePassword(ui->checkBoxHidePassword->isChecked());
214 0 : QtPassSettings::setHideContent(ui->checkBoxHideContent->isChecked());
215 0 : QtPassSettings::setUseMonospace(ui->checkBoxUseMonospace->isChecked());
216 0 : QtPassSettings::setDisplayAsIs(ui->checkBoxDisplayAsIs->isChecked());
217 0 : QtPassSettings::setNoLineWrapping(ui->checkBoxNoLineWrapping->isChecked());
218 0 : QtPassSettings::setAddGPGId(ui->checkBoxAddGPGId->isChecked());
219 0 : QtPassSettings::setUseTrayIcon(ui->checkBoxUseTrayIcon->isEnabled() &&
220 0 : ui->checkBoxUseTrayIcon->isChecked());
221 0 : QtPassSettings::setHideOnClose(ui->checkBoxHideOnClose->isEnabled() &&
222 0 : ui->checkBoxHideOnClose->isChecked());
223 0 : QtPassSettings::setStartMinimized(ui->checkBoxStartMinimized->isEnabled() &&
224 0 : ui->checkBoxStartMinimized->isChecked());
225 0 : QtPassSettings::setProfiles(getProfiles());
226 0 : QtPassSettings::setUseGit(ui->checkBoxUseGit->isChecked());
227 0 : QtPassSettings::setUseOtp(ui->checkBoxUseOtp->isChecked());
228 0 : QtPassSettings::setUseQrencode(ui->checkBoxUseQrencode->isChecked());
229 0 : QtPassSettings::setPwgenExecutable(ui->pwgenPath->text());
230 0 : QtPassSettings::setUsePwgen(ui->checkBoxUsePwgen->isChecked());
231 0 : QtPassSettings::setAvoidCapitals(ui->checkBoxAvoidCapitals->isChecked());
232 0 : QtPassSettings::setAvoidNumbers(ui->checkBoxAvoidNumbers->isChecked());
233 0 : QtPassSettings::setLessRandom(ui->checkBoxLessRandom->isChecked());
234 0 : QtPassSettings::setUseSymbols(ui->checkBoxUseSymbols->isChecked());
235 0 : QtPassSettings::setPasswordConfiguration(getPasswordConfiguration());
236 0 : QtPassSettings::setUseTemplate(ui->checkBoxUseTemplate->isChecked());
237 0 : QtPassSettings::setPassTemplate(ui->plainTextEditTemplate->toPlainText());
238 0 : QtPassSettings::setTemplateAllFields(
239 0 : ui->checkBoxTemplateAllFields->isChecked());
240 0 : QtPassSettings::setAutoPush(ui->checkBoxAutoPush->isChecked());
241 0 : QtPassSettings::setAutoPull(ui->checkBoxAutoPull->isChecked());
242 0 : QtPassSettings::setAlwaysOnTop(ui->checkBoxAlwaysOnTop->isChecked());
243 :
244 0 : QtPassSettings::setVersion(VERSION);
245 0 : }
246 :
247 0 : void ConfigDialog::on_autodetectButton_clicked() {
248 0 : QString pass = Util::findBinaryInPath("pass");
249 0 : if (!pass.isEmpty()) {
250 0 : ui->passPath->setText(pass);
251 : }
252 0 : usePass(!pass.isEmpty());
253 0 : QString gpg = Util::findBinaryInPath("gpg2");
254 0 : if (gpg.isEmpty()) {
255 0 : gpg = Util::findBinaryInPath("gpg");
256 : }
257 0 : if (!gpg.isEmpty()) {
258 0 : ui->gpgPath->setText(gpg);
259 : }
260 0 : QString git = Util::findBinaryInPath("git");
261 0 : if (!git.isEmpty()) {
262 0 : ui->gitPath->setText(git);
263 : }
264 0 : QString pwgen = Util::findBinaryInPath("pwgen");
265 0 : if (!pwgen.isEmpty()) {
266 0 : ui->pwgenPath->setText(pwgen);
267 : }
268 0 : }
269 :
270 : /**
271 : * @brief ConfigDialog::on_radioButtonNative_clicked wrapper for
272 : * ConfigDialog::setGroupBoxState()
273 : */
274 0 : void ConfigDialog::on_radioButtonNative_clicked() { setGroupBoxState(); }
275 :
276 : /**
277 : * @brief ConfigDialog::on_radioButtonPass_clicked wrapper for
278 : * ConfigDialog::setGroupBoxState()
279 : */
280 0 : void ConfigDialog::on_radioButtonPass_clicked() { setGroupBoxState(); }
281 :
282 : /**
283 : * @brief ConfigDialog::getSecretKeys get list of secret/private keys
284 : * @return QStringList keys
285 : */
286 0 : auto ConfigDialog::getSecretKeys() -> QStringList {
287 0 : QList<UserInfo> keys = QtPassSettings::getPass()->listKeys("", true);
288 0 : QStringList names;
289 :
290 0 : if (keys.empty()) {
291 : return names;
292 : }
293 :
294 0 : foreach (const UserInfo &sec, keys)
295 0 : names << sec.name;
296 :
297 0 : return names;
298 : }
299 :
300 : /**
301 : * @brief ConfigDialog::setGroupBoxState update checkboxes.
302 : */
303 0 : void ConfigDialog::setGroupBoxState() {
304 0 : bool state = ui->radioButtonPass->isChecked();
305 0 : ui->groupBoxNative->setEnabled(!state);
306 0 : ui->groupBoxPass->setEnabled(state);
307 0 : }
308 :
309 : /**
310 : * @brief ConfigDialog::selectExecutable pop-up to choose an executable.
311 : * @return
312 : */
313 0 : auto ConfigDialog::selectExecutable() -> QString {
314 0 : QFileDialog dialog(this);
315 0 : dialog.setFileMode(QFileDialog::ExistingFile);
316 0 : dialog.setOption(QFileDialog::ReadOnly);
317 0 : if (dialog.exec()) {
318 0 : return dialog.selectedFiles().constFirst();
319 : }
320 :
321 0 : return {};
322 0 : }
323 :
324 : /**
325 : * @brief ConfigDialog::selectFolder pop-up to choose a folder.
326 : * @return
327 : */
328 0 : auto ConfigDialog::selectFolder() -> QString {
329 0 : QFileDialog dialog(this);
330 0 : dialog.setFileMode(QFileDialog::Directory);
331 0 : dialog.setFilter(QDir::NoFilter);
332 0 : dialog.setOption(QFileDialog::ShowDirsOnly);
333 0 : if (dialog.exec()) {
334 0 : return dialog.selectedFiles().constFirst();
335 : }
336 :
337 0 : return {};
338 0 : }
339 :
340 : /**
341 : * @brief ConfigDialog::on_toolButtonGit_clicked get git application.
342 : * Enable checkboxes if found.
343 : */
344 0 : void ConfigDialog::on_toolButtonGit_clicked() {
345 0 : QString git = selectExecutable();
346 0 : bool state = !git.isEmpty();
347 0 : if (state) {
348 0 : ui->gitPath->setText(git);
349 : } else {
350 0 : useGit(false);
351 : }
352 :
353 0 : ui->checkBoxUseGit->setEnabled(state);
354 0 : }
355 :
356 : /**
357 : * @brief ConfigDialog::on_toolButtonGpg_clicked get gpg application.
358 : */
359 0 : void ConfigDialog::on_toolButtonGpg_clicked() {
360 0 : QString gpg = selectExecutable();
361 0 : if (!gpg.isEmpty()) {
362 0 : ui->gpgPath->setText(gpg);
363 : }
364 0 : }
365 :
366 : /**
367 : * @brief ConfigDialog::on_toolButtonPass_clicked get pass application.
368 : */
369 0 : void ConfigDialog::on_toolButtonPass_clicked() {
370 0 : QString pass = selectExecutable();
371 0 : if (!pass.isEmpty()) {
372 0 : ui->passPath->setText(pass);
373 : }
374 0 : }
375 :
376 : /**
377 : * @brief ConfigDialog::on_toolButtonStore_clicked get .password-store
378 : * location.s
379 : */
380 0 : void ConfigDialog::on_toolButtonStore_clicked() {
381 0 : QString store = selectFolder();
382 0 : if (!store.isEmpty()) { // TODO(annejan): call check
383 0 : ui->storePath->setText(store);
384 : }
385 0 : }
386 :
387 : /**
388 : * @brief ConfigDialog::on_comboBoxClipboard_activated show and hide options.
389 : * @param index of selectbox (0 = no clipboard).
390 : */
391 0 : void ConfigDialog::on_comboBoxClipboard_activated(int index) {
392 0 : bool state = index > 0;
393 :
394 0 : ui->checkBoxSelection->setEnabled(state);
395 0 : ui->checkBoxAutoclear->setEnabled(state);
396 0 : ui->checkBoxHidePassword->setEnabled(state);
397 0 : ui->checkBoxHideContent->setEnabled(state);
398 0 : if (state) {
399 0 : ui->spinBoxAutoclearSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
400 0 : ui->labelSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
401 : } else {
402 0 : ui->spinBoxAutoclearSeconds->setEnabled(false);
403 0 : ui->labelSeconds->setEnabled(false);
404 : }
405 0 : }
406 :
407 : /**
408 : * @brief ConfigDialog::on_checkBoxAutoclearPanel_clicked enable and disable
409 : * options based on autoclear use.
410 : */
411 0 : void ConfigDialog::on_checkBoxAutoclearPanel_clicked() {
412 0 : bool state = ui->checkBoxAutoclearPanel->isChecked();
413 0 : ui->spinBoxAutoclearPanelSeconds->setEnabled(state);
414 0 : ui->labelPanelSeconds->setEnabled(state);
415 0 : }
416 :
417 : /**
418 : * @brief ConfigDialog::useSelection set the clipboard type use from
419 : * MainWindow.
420 : * @param useSelection
421 : */
422 0 : void ConfigDialog::useSelection(bool useSelection) {
423 0 : ui->checkBoxSelection->setChecked(useSelection);
424 0 : on_checkBoxSelection_clicked();
425 0 : }
426 :
427 : /**
428 : * @brief ConfigDialog::useAutoclear set the clipboard autoclear use from
429 : * MainWindow.
430 : * @param useAutoclear
431 : */
432 0 : void ConfigDialog::useAutoclear(bool useAutoclear) {
433 0 : ui->checkBoxAutoclear->setChecked(useAutoclear);
434 0 : on_checkBoxAutoclear_clicked();
435 0 : }
436 :
437 : /**
438 : * @brief ConfigDialog::useAutoclearPanel set the panel autoclear use from
439 : * MainWindow.
440 : * @param useAutoclearPanel
441 : */
442 0 : void ConfigDialog::useAutoclearPanel(bool useAutoclearPanel) {
443 0 : ui->checkBoxAutoclearPanel->setChecked(useAutoclearPanel);
444 0 : on_checkBoxAutoclearPanel_clicked();
445 0 : }
446 :
447 : /**
448 : * @brief ConfigDialog::on_checkBoxSelection_clicked checkbox clicked, update
449 : * state via ConfigDialog::on_comboBoxClipboard_activated
450 : */
451 0 : void ConfigDialog::on_checkBoxSelection_clicked() {
452 0 : on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
453 0 : }
454 :
455 : /**
456 : * @brief ConfigDialog::on_checkBoxAutoclear_clicked checkbox clicked, update
457 : * state via ConfigDialog::on_comboBoxClipboard_activated
458 : */
459 0 : void ConfigDialog::on_checkBoxAutoclear_clicked() {
460 0 : on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
461 0 : }
462 :
463 : /**
464 : * @brief ConfigDialog::genKey tunnel function to make MainWindow generate a
465 : * gpg key pair.
466 : * @todo refactor the process to not be entangled so much.
467 : * @param batch
468 : * @param dialog
469 : */
470 0 : void ConfigDialog::genKey(const QString &batch, QDialog *dialog) {
471 0 : mainWindow->generateKeyPair(batch, dialog);
472 0 : }
473 :
474 : /**
475 : * @brief ConfigDialog::setProfiles set the profiles and chosen profile from
476 : * MainWindow.
477 : * @param profiles
478 : * @param profile
479 : */
480 0 : void ConfigDialog::setProfiles(QHash<QString, QHash<QString, QString>> profiles,
481 : const QString ¤tProfile) {
482 : // dbg()<< profiles;
483 0 : if (profiles.contains("")) {
484 0 : profiles.remove("");
485 : // remove weird "" key value pairs
486 : }
487 :
488 0 : ui->profileTable->setRowCount(profiles.count());
489 0 : QHashIterator<QString, QHash<QString, QString>> i(profiles);
490 : int n = 0;
491 0 : while (i.hasNext()) {
492 : i.next();
493 0 : if (!i.value().isEmpty() && !i.key().isEmpty()) {
494 0 : ui->profileTable->setItem(n, 0, new QTableWidgetItem(i.key()));
495 0 : ui->profileTable->setItem(n, 1,
496 0 : new QTableWidgetItem(i.value().value("path")));
497 0 : ui->profileTable->setItem(
498 0 : n, 2, new QTableWidgetItem(i.value().value("signingKey")));
499 : // dbg()<< "naam:" + i.key();
500 0 : if (i.key() == currentProfile) {
501 0 : ui->profileTable->selectRow(n);
502 : }
503 : }
504 0 : ++n;
505 : }
506 0 : }
507 :
508 : /**
509 : * @brief ConfigDialog::getProfiles return profile list.
510 : * @return
511 : */
512 0 : auto ConfigDialog::getProfiles() -> QHash<QString, QHash<QString, QString>> {
513 0 : QHash<QString, QHash<QString, QString>> profiles;
514 : // Check?
515 0 : for (int i = 0; i < ui->profileTable->rowCount(); ++i) {
516 0 : QHash<QString, QString> profile;
517 0 : QTableWidgetItem *pathItem = ui->profileTable->item(i, 1);
518 0 : if (nullptr != pathItem) {
519 0 : QTableWidgetItem *item = ui->profileTable->item(i, 0);
520 0 : if (item == nullptr) {
521 : continue;
522 : }
523 0 : profile["path"] = pathItem->text();
524 0 : QTableWidgetItem *signingKeyItem = ui->profileTable->item(i, 2);
525 0 : if (nullptr != signingKeyItem) {
526 0 : profile["signingKey"] = signingKeyItem->text();
527 : }
528 0 : profiles.insert(item->text(), profile);
529 : }
530 0 : }
531 0 : return profiles;
532 0 : }
533 :
534 : /**
535 : * @brief ConfigDialog::on_addButton_clicked add a profile row.
536 : */
537 0 : void ConfigDialog::on_addButton_clicked() {
538 0 : int n = ui->profileTable->rowCount();
539 0 : ui->profileTable->insertRow(n);
540 0 : ui->profileTable->setItem(n, 0, new QTableWidgetItem());
541 0 : ui->profileTable->setItem(n, 1, new QTableWidgetItem(ui->storePath->text()));
542 0 : ui->profileTable->setItem(n, 2, new QTableWidgetItem());
543 0 : ui->profileTable->selectRow(n);
544 0 : ui->deleteButton->setEnabled(true);
545 :
546 0 : validate();
547 0 : }
548 :
549 : /**
550 : * @brief ConfigDialog::on_deleteButton_clicked remove a profile row.
551 : */
552 0 : void ConfigDialog::on_deleteButton_clicked() {
553 : QSet<int> selectedRows; // we use a set to prevent doubles
554 0 : QList<QTableWidgetItem *> itemList = ui->profileTable->selectedItems();
555 0 : if (itemList.count() == 0) {
556 0 : QMessageBox::warning(this, tr("No profile selected"),
557 0 : tr("No profile selected to delete"));
558 : return;
559 : }
560 : QTableWidgetItem *item;
561 0 : foreach (item, itemList)
562 0 : selectedRows.insert(item->row());
563 : // get a list, and sort it big to small
564 0 : QList<int> rows = selectedRows.values();
565 0 : std::sort(rows.begin(), rows.end());
566 : // now actually do the removing:
567 0 : foreach (int row, rows)
568 0 : ui->profileTable->removeRow(row);
569 0 : if (ui->profileTable->rowCount() < 1) {
570 0 : ui->deleteButton->setEnabled(false);
571 : }
572 :
573 0 : validate();
574 : }
575 :
576 : /**
577 : * @brief ConfigDialog::criticalMessage weapper for showing critical messages
578 : * in a popup.
579 : * @param title
580 : * @param text
581 : */
582 0 : void ConfigDialog::criticalMessage(const QString &title, const QString &text) {
583 0 : QMessageBox::critical(this, title, text, QMessageBox::Ok, QMessageBox::Ok);
584 0 : }
585 :
586 0 : auto ConfigDialog::isQrencodeAvailable() -> bool {
587 : #ifdef Q_OS_WIN
588 : return false;
589 : #else
590 0 : QProcess which;
591 0 : which.start("which", QStringList() << "qrencode");
592 0 : which.waitForFinished();
593 0 : QtPassSettings::setQrencodeExecutable(
594 0 : which.readAllStandardOutput().trimmed());
595 0 : return which.exitCode() == 0;
596 : #endif
597 0 : }
598 :
599 0 : auto ConfigDialog::isPassOtpAvailable() -> bool {
600 : #ifdef Q_OS_WIN
601 : return false;
602 : #else
603 0 : QProcess pass;
604 0 : pass.start(QtPassSettings::getPassExecutable(), QStringList() << "otp"
605 0 : << "--help");
606 0 : pass.waitForFinished(2000);
607 0 : return pass.exitCode() == 0;
608 : #endif
609 0 : }
610 :
611 : /**
612 : * @brief ConfigDialog::wizard first-time use wizard.
613 : * @todo make this thing more reliable.
614 : */
615 0 : void ConfigDialog::wizard() {
616 0 : Util::checkConfig();
617 0 : on_autodetectButton_clicked();
618 :
619 0 : if (!checkGpgExistence()) {
620 : return;
621 : }
622 0 : if (!checkSecretKeys()) {
623 : return;
624 : }
625 0 : if (!checkPasswordStore()) {
626 : return;
627 : }
628 0 : handleGpgIdFile();
629 :
630 0 : ui->checkBoxHidePassword->setCheckState(Qt::Checked);
631 : }
632 :
633 0 : auto ConfigDialog::checkGpgExistence() -> bool {
634 0 : QString gpg = ui->gpgPath->text();
635 0 : if (!gpg.startsWith("wsl ") && !QFile(gpg).exists()) {
636 0 : criticalMessage(
637 0 : tr("GnuPG not found"),
638 : #ifdef Q_OS_WIN
639 : #ifdef WINSTORE
640 : tr("Please install GnuPG on your system.<br>Install "
641 : "<strong>Ubuntu</strong> from the Microsoft Store to get it.<br>"
642 : "If you already did so, make sure you started it once and<br>"
643 : "click \"Autodetect\" in the next dialog.")
644 : #else
645 : tr("Please install GnuPG on your system.<br>Install "
646 : "<strong>Ubuntu</strong> from the Microsoft Store<br>or <a "
647 : "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
648 : "from GnuPG.org")
649 : #endif
650 : #else
651 0 : tr("Please install GnuPG on your system.<br>Install "
652 : "<strong>gpg</strong> using your favorite package manager<br>or "
653 : "<a "
654 : "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
655 : "from GnuPG.org")
656 : #endif
657 : );
658 0 : return false;
659 : }
660 : return true;
661 : }
662 :
663 0 : auto ConfigDialog::checkSecretKeys() -> bool {
664 0 : QString gpg = ui->gpgPath->text();
665 0 : QStringList names = getSecretKeys();
666 :
667 : #ifdef QT_DEBUG
668 : dbg() << names;
669 : #endif
670 :
671 0 : if ((gpg.startsWith("wsl ") || QFile(gpg).exists()) && names.empty()) {
672 0 : KeygenDialog d(this);
673 0 : return d.exec();
674 0 : }
675 : return true;
676 : }
677 :
678 0 : auto ConfigDialog::checkPasswordStore() -> bool {
679 0 : QString passStore = ui->storePath->text();
680 :
681 0 : if (!QFile(passStore).exists()) {
682 0 : if (QMessageBox::question(
683 0 : this, tr("Create password-store?"),
684 0 : tr("Would you like to create a password-store at %1?")
685 0 : .arg(passStore),
686 : QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
687 0 : if (!QDir().mkdir(passStore)) {
688 0 : QMessageBox::warning(
689 0 : this, tr("Error"),
690 0 : tr("Failed to create password-store at: %1").arg(passStore));
691 0 : return false;
692 : }
693 : #ifdef Q_OS_WIN
694 : SetFileAttributes(passStore.toStdWString().c_str(),
695 : FILE_ATTRIBUTE_HIDDEN);
696 : #endif
697 0 : if (ui->checkBoxUseGit->isChecked()) {
698 0 : emit mainWindow->passGitInitNeeded();
699 : }
700 0 : mainWindow->userDialog(passStore);
701 : }
702 : }
703 : return true;
704 : }
705 :
706 0 : void ConfigDialog::handleGpgIdFile() {
707 0 : QString passStore = ui->storePath->text();
708 0 : if (!QFile(QDir(passStore).filePath(".gpg-id")).exists()) {
709 : #ifdef QT_DEBUG
710 : dbg() << ".gpg-id file does not exist";
711 : #endif
712 0 : criticalMessage(tr("Password store not initialised"),
713 0 : tr("The folder %1 doesn't seem to be a password store or "
714 : "is not yet initialised.")
715 0 : .arg(passStore));
716 :
717 0 : while (!QFile(passStore).exists()) {
718 0 : on_toolButtonStore_clicked();
719 0 : if (passStore == ui->storePath->text()) {
720 : return;
721 : }
722 0 : passStore = ui->storePath->text();
723 : }
724 0 : if (!QFile(passStore + ".gpg-id").exists()) {
725 : #ifdef QT_DEBUG
726 : dbg() << ".gpg-id file still does not exist :/";
727 : #endif
728 0 : mainWindow->userDialog(passStore);
729 : }
730 : }
731 : }
732 :
733 : /**
734 : * @brief ConfigDialog::useTrayIcon set preference for using trayicon.
735 : * Enable or disable related checkboxes accordingly.
736 : * @param useSystray
737 : */
738 0 : void ConfigDialog::useTrayIcon(bool useSystray) {
739 0 : if (QSystemTrayIcon::isSystemTrayAvailable()) {
740 0 : ui->checkBoxUseTrayIcon->setChecked(useSystray);
741 0 : ui->checkBoxHideOnClose->setEnabled(useSystray);
742 0 : ui->checkBoxStartMinimized->setEnabled(useSystray);
743 :
744 0 : if (!useSystray) {
745 0 : ui->checkBoxHideOnClose->setChecked(false);
746 0 : ui->checkBoxStartMinimized->setChecked(false);
747 : }
748 : }
749 0 : }
750 :
751 : /**
752 : * @brief ConfigDialog::on_checkBoxUseTrayIcon_clicked enable and disable
753 : * related checkboxes.
754 : */
755 0 : void ConfigDialog::on_checkBoxUseTrayIcon_clicked() {
756 0 : bool state = ui->checkBoxUseTrayIcon->isChecked();
757 0 : ui->checkBoxHideOnClose->setEnabled(state);
758 0 : ui->checkBoxStartMinimized->setEnabled(state);
759 0 : }
760 :
761 : /**
762 : * @brief ConfigDialog::closeEvent close this window.
763 : * @param event
764 : */
765 0 : void ConfigDialog::closeEvent(QCloseEvent *event) {
766 : // TODO(annejan): save window size or something?
767 : event->accept();
768 0 : }
769 :
770 : /**
771 : * @brief ConfigDialog::useGit set preference for using git.
772 : * @param useGit
773 : */
774 0 : void ConfigDialog::useGit(bool useGit) {
775 0 : ui->checkBoxUseGit->setChecked(useGit);
776 0 : on_checkBoxUseGit_clicked();
777 0 : }
778 :
779 : /**
780 : * @brief ConfigDialog::useOtp set preference for using otp plugin.
781 : * @param useOtp
782 : */
783 0 : void ConfigDialog::useOtp(bool useOtp) {
784 0 : ui->checkBoxUseOtp->setChecked(useOtp);
785 0 : }
786 :
787 : /**
788 : * @brief ConfigDialog::useOtp set preference for using otp plugin.
789 : * @param useOtp
790 : */
791 0 : void ConfigDialog::useQrencode(bool useQrencode) {
792 0 : ui->checkBoxUseQrencode->setChecked(useQrencode);
793 0 : }
794 :
795 : /**
796 : * @brief ConfigDialog::on_checkBoxUseGit_clicked enable or disable related
797 : * checkboxes.
798 : */
799 0 : void ConfigDialog::on_checkBoxUseGit_clicked() {
800 0 : ui->checkBoxAddGPGId->setEnabled(ui->checkBoxUseGit->isChecked());
801 0 : ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
802 0 : ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
803 0 : }
804 :
805 : /**
806 : * @brief ConfigDialog::on_toolButtonPwgen_clicked enable or disable related
807 : * options in the interface.
808 : */
809 0 : void ConfigDialog::on_toolButtonPwgen_clicked() {
810 0 : QString pwgen = selectExecutable();
811 0 : if (!pwgen.isEmpty()) {
812 0 : ui->pwgenPath->setText(pwgen);
813 0 : ui->checkBoxUsePwgen->setEnabled(true);
814 : } else {
815 0 : ui->checkBoxUsePwgen->setEnabled(false);
816 0 : ui->checkBoxUsePwgen->setChecked(false);
817 : }
818 0 : }
819 :
820 : /**
821 : * @brief ConfigDialog::setPwgenPath set pwgen executable path.
822 : * Enable or disable related options in the interface.
823 : * @param pwgen
824 : */
825 0 : void ConfigDialog::setPwgenPath(const QString &pwgen) {
826 0 : ui->pwgenPath->setText(pwgen);
827 0 : if (pwgen.isEmpty()) {
828 0 : ui->checkBoxUsePwgen->setChecked(false);
829 0 : ui->checkBoxUsePwgen->setEnabled(false);
830 : }
831 0 : on_checkBoxUsePwgen_clicked();
832 0 : }
833 :
834 : /**
835 : * @brief ConfigDialog::on_checkBoxUsPwgen_clicked enable or disable related
836 : * options in the interface.
837 : */
838 0 : void ConfigDialog::on_checkBoxUsePwgen_clicked() {
839 0 : bool usePwgen = ui->checkBoxUsePwgen->isChecked();
840 0 : ui->checkBoxAvoidCapitals->setEnabled(usePwgen);
841 0 : ui->checkBoxAvoidNumbers->setEnabled(usePwgen);
842 0 : ui->checkBoxLessRandom->setEnabled(usePwgen);
843 0 : ui->checkBoxUseSymbols->setEnabled(usePwgen);
844 0 : ui->lineEditPasswordChars->setEnabled(!usePwgen);
845 0 : ui->labelPasswordChars->setEnabled(!usePwgen);
846 0 : ui->passwordCharTemplateSelector->setEnabled(!usePwgen);
847 0 : }
848 :
849 : /**
850 : * @brief ConfigDialog::usePwgen set preference for using pwgen (can be
851 : * overruled buy empty pwgenPath).
852 : * enable or disable related options in the interface via
853 : * ConfigDialog::on_checkBoxUsePwgen_clicked
854 : * @param usePwgen
855 : */
856 0 : void ConfigDialog::usePwgen(bool usePwgen) {
857 0 : if (ui->pwgenPath->text().isEmpty()) {
858 : usePwgen = false;
859 : }
860 0 : ui->checkBoxUsePwgen->setChecked(usePwgen);
861 0 : on_checkBoxUsePwgen_clicked();
862 0 : }
863 :
864 0 : void ConfigDialog::setPasswordConfiguration(
865 : const PasswordConfiguration &config) {
866 0 : ui->spinBoxPasswordLength->setValue(config.length);
867 0 : ui->passwordCharTemplateSelector->setCurrentIndex(config.selected);
868 0 : if (config.selected != PasswordConfiguration::CUSTOM) {
869 0 : ui->lineEditPasswordChars->setEnabled(false);
870 : }
871 0 : ui->lineEditPasswordChars->setText(config.Characters[config.selected]);
872 0 : }
873 :
874 0 : auto ConfigDialog::getPasswordConfiguration() -> PasswordConfiguration {
875 0 : PasswordConfiguration config;
876 0 : config.length = ui->spinBoxPasswordLength->value();
877 0 : config.selected = static_cast<PasswordConfiguration::characterSet>(
878 0 : ui->passwordCharTemplateSelector->currentIndex());
879 : config.Characters[PasswordConfiguration::CUSTOM] =
880 0 : ui->lineEditPasswordChars->text();
881 0 : return config;
882 0 : }
883 :
884 : /**
885 : * @brief ConfigDialog::on_passwordCharTemplateSelector_activated sets the
886 : * passwordChar Template
887 : * combo box to the desired entry
888 : * @param entry of
889 : */
890 0 : void ConfigDialog::on_passwordCharTemplateSelector_activated(int index) {
891 0 : ui->lineEditPasswordChars->setText(
892 0 : QtPassSettings::getPasswordConfiguration().Characters[index]);
893 0 : if (index == 3) {
894 0 : ui->lineEditPasswordChars->setEnabled(true);
895 : } else {
896 0 : ui->lineEditPasswordChars->setEnabled(false);
897 : }
898 0 : }
899 :
900 : /**
901 : * @brief ConfigDialog::on_checkBoxUseTemplate_clicked enable or disable the
902 : * template field and options.
903 : */
904 0 : void ConfigDialog::on_checkBoxUseTemplate_clicked() {
905 0 : ui->plainTextEditTemplate->setEnabled(ui->checkBoxUseTemplate->isChecked());
906 0 : ui->checkBoxTemplateAllFields->setEnabled(
907 0 : ui->checkBoxUseTemplate->isChecked());
908 0 : }
909 :
910 0 : void ConfigDialog::onProfileTableItemChanged(QTableWidgetItem *item) {
911 0 : validate(item);
912 0 : }
913 :
914 : /**
915 : * @brief ConfigDialog::useTemplate set preference for using templates.
916 : * @param useTemplate
917 : */
918 0 : void ConfigDialog::useTemplate(bool useTemplate) {
919 0 : ui->checkBoxUseTemplate->setChecked(useTemplate);
920 0 : on_checkBoxUseTemplate_clicked();
921 0 : }
|