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