2828#include " core/PasswordHealth.h"
2929#include " core/Resources.h"
3030#include " gui/Clipboard.h"
31+ #include " gui/FileDialog.h"
3132#include " gui/Icons.h"
33+ #include " gui/MessageBox.h"
3234#include " gui/styles/StateColorPalette.h"
3335
3436PasswordGeneratorWidget::PasswordGeneratorWidget (QWidget* parent)
@@ -43,6 +45,8 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
4345 m_ui->buttonGenerate ->setToolTip (
4446 tr (" Regenerate password (%1)" ).arg (m_ui->buttonGenerate ->shortcut ().toString (QKeySequence::NativeText)));
4547 m_ui->buttonCopy ->setIcon (icons ()->icon (" clipboard-text" ));
48+ m_ui->buttonDeleteWordList ->setIcon (icons ()->icon (" trash" ));
49+ m_ui->buttonAddWordList ->setIcon (icons ()->icon (" document-new" ));
4650 m_ui->buttonClose ->setShortcut (Qt::Key_Escape);
4751
4852 // Add two shortcuts to save the form CTRL+Enter and CTRL+S
@@ -60,6 +64,8 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
6064 connect (m_ui->buttonApply , SIGNAL (clicked ()), SLOT (applyPassword ()));
6165 connect (m_ui->buttonCopy , SIGNAL (clicked ()), SLOT (copyPassword ()));
6266 connect (m_ui->buttonGenerate , SIGNAL (clicked ()), SLOT (regeneratePassword ()));
67+ connect (m_ui->buttonDeleteWordList , SIGNAL (clicked ()), SLOT (deleteWordList ()));
68+ connect (m_ui->buttonAddWordList , SIGNAL (clicked ()), SLOT (addWordList ()));
6369 connect (m_ui->buttonClose , SIGNAL (clicked ()), SIGNAL (closed ()));
6470
6571 connect (m_ui->sliderLength , SIGNAL (valueChanged (int )), SLOT (passwordLengthChanged (int )));
@@ -92,15 +98,18 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
9298 m_ui->wordCaseComboBox ->addItem (tr (" UPPER CASE" ), PassphraseGenerator::UPPERCASE);
9399 m_ui->wordCaseComboBox ->addItem (tr (" Title Case" ), PassphraseGenerator::TITLECASE);
94100
101+ // load system-wide wordlists
95102 QDir path (resources ()->wordlistPath (" " ));
96- QStringList files = path.entryList (QDir::Files);
97- m_ui->comboBoxWordList ->addItems (files);
98- if (files.size () > 1 ) {
99- m_ui->comboBoxWordList ->setVisible (true );
100- m_ui->labelWordList ->setVisible (true );
101- } else {
102- m_ui->comboBoxWordList ->setVisible (false );
103- m_ui->labelWordList ->setVisible (false );
103+ for (const auto & fileName : path.entryList (QDir::Files)) {
104+ m_ui->comboBoxWordList ->addItem (tr (" (SYSTEM)" ) + " " + fileName, fileName);
105+ }
106+
107+ m_firstCustomWordlistIndex = m_ui->comboBoxWordList ->count ();
108+
109+ // load user-provided wordlists
110+ path = QDir (resources ()->userWordlistPath (" " ));
111+ for (const auto & fileName : path.entryList (QDir::Files)) {
112+ m_ui->comboBoxWordList ->addItem (fileName, path.absolutePath () + QDir::separator () + fileName);
104113 }
105114
106115 loadSettings ();
@@ -164,7 +173,10 @@ void PasswordGeneratorWidget::loadSettings()
164173 // Diceware config
165174 m_ui->spinBoxWordCount ->setValue (config ()->get (Config::PasswordGenerator_WordCount).toInt ());
166175 m_ui->editWordSeparator ->setText (config ()->get (Config::PasswordGenerator_WordSeparator).toString ());
167- m_ui->comboBoxWordList ->setCurrentText (config ()->get (Config::PasswordGenerator_WordList).toString ());
176+ int i = m_ui->comboBoxWordList ->findData (config ()->get (Config::PasswordGenerator_WordList).toString ());
177+ if (i > -1 ) {
178+ m_ui->comboBoxWordList ->setCurrentIndex (i);
179+ }
168180 m_ui->wordCaseComboBox ->setCurrentIndex (config ()->get (Config::PasswordGenerator_WordCase).toInt ());
169181
170182 // Password or diceware?
@@ -205,7 +217,7 @@ void PasswordGeneratorWidget::saveSettings()
205217 // Diceware config
206218 config ()->set (Config::PasswordGenerator_WordCount, m_ui->spinBoxWordCount ->value ());
207219 config ()->set (Config::PasswordGenerator_WordSeparator, m_ui->editWordSeparator ->text ());
208- config ()->set (Config::PasswordGenerator_WordList, m_ui->comboBoxWordList ->currentText ());
220+ config ()->set (Config::PasswordGenerator_WordList, m_ui->comboBoxWordList ->currentData ());
209221 config ()->set (Config::PasswordGenerator_WordCase, m_ui->wordCaseComboBox ->currentIndex ());
210222
211223 // Password or diceware?
@@ -329,6 +341,86 @@ bool PasswordGeneratorWidget::isPasswordVisible() const
329341 return m_ui->editNewPassword ->isPasswordVisible ();
330342}
331343
344+ void PasswordGeneratorWidget::deleteWordList ()
345+ {
346+ if (m_ui->comboBoxWordList ->currentIndex () < m_firstCustomWordlistIndex) {
347+ return ;
348+ }
349+
350+ QFile file (m_ui->comboBoxWordList ->currentData ().toString ());
351+ if (!file.exists ()) {
352+ return ;
353+ }
354+
355+ auto result = MessageBox::question (this ,
356+ tr (" Confirm Delete Wordlist" ),
357+ tr (" Do you really want to delete the wordlist \" %1\" ?" ).arg (file.fileName ()),
358+ MessageBox::Delete | MessageBox::Cancel,
359+ MessageBox::Cancel);
360+ if (result != MessageBox::Delete) {
361+ return ;
362+ }
363+
364+ if (!file.remove ()) {
365+ MessageBox::critical (this , tr (" Failed to delete wordlist" ), file.errorString ());
366+ return ;
367+ }
368+
369+ m_ui->comboBoxWordList ->removeItem (m_ui->comboBoxWordList ->currentIndex ());
370+ updateGenerator ();
371+ }
372+
373+ void PasswordGeneratorWidget::addWordList ()
374+ {
375+ auto filter = QString (" %1 (*.txt *.asc *.wordlist);;%2 (*)" ).arg (tr (" Wordlists" ), tr (" All files" ));
376+ auto filePath = fileDialog ()->getOpenFileName (this , tr (" Select Custom Wordlist" ), " " , filter);
377+ if (filePath.isEmpty ()) {
378+ return ;
379+ }
380+
381+ // create directory for user-specified wordlists, if necessary
382+ QDir destDir (resources ()->userWordlistPath (" " ));
383+ destDir.mkpath (" ." );
384+
385+ // check if destination wordlist already exists
386+ QString fileName = QFileInfo (filePath).fileName ();
387+ QString destPath = destDir.absolutePath () + QDir::separator () + fileName;
388+ QFile dest (destPath);
389+ if (dest.exists ()) {
390+ auto response = MessageBox::warning (this ,
391+ tr (" Overwrite Wordlist?" ),
392+ tr (" Wordlist \" %1\" already exists as a custom wordlist.\n "
393+ " Do you want to overwrite it?" )
394+ .arg (fileName),
395+ MessageBox::Overwrite | MessageBox::Cancel,
396+ MessageBox::Cancel);
397+ if (response != MessageBox::Overwrite) {
398+ return ;
399+ }
400+ if (!dest.remove ()) {
401+ MessageBox::critical (this , tr (" Failed to delete wordlist" ), dest.errorString ());
402+ return ;
403+ }
404+ }
405+
406+ // copy wordlist to destination path and add corresponding item to the combo box
407+ QFile file (filePath);
408+ if (!file.copy (destPath)) {
409+ MessageBox::critical (this , tr (" Failed to add wordlist" ), file.errorString ());
410+ return ;
411+ }
412+
413+ auto index = m_ui->comboBoxWordList ->findData (destPath);
414+ if (index == -1 ) {
415+ m_ui->comboBoxWordList ->addItem (fileName, destPath);
416+ index = m_ui->comboBoxWordList ->count () - 1 ;
417+ }
418+ m_ui->comboBoxWordList ->setCurrentIndex (index);
419+
420+ // update the password generator
421+ updateGenerator ();
422+ }
423+
332424void PasswordGeneratorWidget::setAdvancedMode (bool advanced)
333425{
334426 saveSettings ();
@@ -540,10 +632,15 @@ void PasswordGeneratorWidget::updateGenerator()
540632 static_cast <PassphraseGenerator::PassphraseWordCase>(m_ui->wordCaseComboBox ->currentData ().toInt ()));
541633
542634 m_dicewareGenerator->setWordCount (m_ui->spinBoxWordCount ->value ());
543- if (!m_ui->comboBoxWordList ->currentText ().isEmpty ()) {
544- QString path = resources ()->wordlistPath (m_ui->comboBoxWordList ->currentText ());
545- m_dicewareGenerator->setWordList (path);
635+ auto path = m_ui->comboBoxWordList ->currentData ().toString ();
636+ if (m_ui->comboBoxWordList ->currentIndex () < m_firstCustomWordlistIndex) {
637+ path = resources ()->wordlistPath (path);
638+ m_ui->buttonDeleteWordList ->setEnabled (false );
639+ } else {
640+ m_ui->buttonDeleteWordList ->setEnabled (true );
546641 }
642+ m_dicewareGenerator->setWordList (path);
643+
547644 m_dicewareGenerator->setWordSeparator (m_ui->editWordSeparator ->text ());
548645
549646 if (m_dicewareGenerator->isValid ()) {
0 commit comments