Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F31766634
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
146 KB
Subscribers
None
View Options
diff --git a/src/configdialog.cpp b/src/configdialog.cpp
index c535b9f..92a9005 100644
--- a/src/configdialog.cpp
+++ b/src/configdialog.cpp
@@ -1,687 +1,696 @@
#include "configdialog.h"
#include "debughelper.h"
#include "keygendialog.h"
#include "mainwindow.h"
#include "qtpasssettings.h"
#include "ui_configdialog.h"
#include <QClipboard>
#include <QDir>
#include <QFileDialog>
#include <QMessageBox>
#ifdef Q_OS_WIN
#include <windows.h>
#endif
/**
* @brief ConfigDialog::ConfigDialog this sets up the configuration screen.
* @param parent
*/
ConfigDialog::ConfigDialog(MainWindow *parent)
: QDialog(parent), ui(new Ui::ConfigDialog) {
mainWindow = parent;
ui->setupUi(this);
ui->passPath->setText(QtPassSettings::getPassExecutable());
setGitPath(QtPassSettings::getGitExecutable());
ui->gpgPath->setText(QtPassSettings::getGpgExecutable());
ui->storePath->setText(QtPassSettings::getPassStore());
ui->spinBoxAutoclearSeconds->setValue(QtPassSettings::getAutoclearSeconds());
ui->spinBoxAutoclearPanelSeconds->setValue(
QtPassSettings::getAutoclearPanelSeconds());
ui->checkBoxHidePassword->setChecked(QtPassSettings::isHidePassword());
ui->checkBoxHideContent->setChecked(QtPassSettings::isHideContent());
ui->checkBoxAddGPGId->setChecked(QtPassSettings::isAddGPGId(true));
ui->checkBoxHideOnClose->setChecked(QtPassSettings::isHideOnClose());
ui->checkBoxStartMinimized->setChecked(QtPassSettings::isStartMinimized());
ui->checkBoxAvoidCapitals->setChecked(QtPassSettings::isAvoidCapitals());
ui->checkBoxAvoidNumbers->setChecked(QtPassSettings::isAvoidNumbers());
ui->checkBoxLessRandom->setChecked(QtPassSettings::isLessRandom());
ui->checkBoxUseSymbols->setChecked(QtPassSettings::isUseSymbols());
ui->plainTextEditTemplate->setPlainText(QtPassSettings::getPassTemplate());
ui->checkBoxTemplateAllFields->setChecked(
QtPassSettings::isTemplateAllFields());
ui->checkBoxAutoPull->setChecked(QtPassSettings::isAutoPull());
ui->checkBoxAutoPush->setChecked(QtPassSettings::isAutoPush());
ui->checkBoxAlwaysOnTop->setChecked(QtPassSettings::isAlwaysOnTop());
+ #if defined(Q_OS_WIN ) || defined(__APPLE__)
+ ui->checkBoxUseOtp->hide();
+ ui->label_10->hide();
+ #endif
+
setProfiles(QtPassSettings::getProfiles(), QtPassSettings::getProfile());
setPwgenPath(QtPassSettings::getPwgenExecutable());
setPasswordConfiguration(QtPassSettings::getPasswordConfiguration());
usePass(QtPassSettings::isUsePass());
useAutoclear(QtPassSettings::isUseAutoclear());
useAutoclearPanel(QtPassSettings::isUseAutoclearPanel());
useTrayIcon(QtPassSettings::isUseTrayIcon());
useGit(QtPassSettings::isUseGit());
useOtp(QtPassSettings::isUseOtp());
usePwgen(QtPassSettings::isUsePwgen());
useTemplate(QtPassSettings::isUseTemplate());
ui->profileTable->verticalHeader()->hide();
ui->profileTable->horizontalHeader()->setStretchLastSection(true);
ui->label->setText(ui->label->text() + VERSION);
ui->comboBoxClipboard->clear();
ui->comboBoxClipboard->addItem(tr("No Clipboard"));
ui->comboBoxClipboard->addItem(tr("Always copy to clipboard"));
ui->comboBoxClipboard->addItem(tr("On-demand copy to clipboard"));
int currentIndex = QtPassSettings::getClipBoardTypeRaw();
ui->comboBoxClipboard->setCurrentIndex(currentIndex);
on_comboBoxClipboard_activated(currentIndex);
QClipboard *clip = QApplication::clipboard();
if (!clip->supportsSelection()) {
useSelection(false);
ui->checkBoxSelection->setVisible(false);
} else {
useSelection(QtPassSettings::isUseSelection());
}
connect(this, &ConfigDialog::accepted, this, &ConfigDialog::on_accepted);
}
/**
* @brief ConfigDialog::~ConfigDialog config destructor, makes sure the
* mainWindow knows about git, gpg and pass executables.
*/
ConfigDialog::~ConfigDialog() {
QtPassSettings::setGitExecutable(ui->gitPath->text());
QtPassSettings::setGpgExecutable(ui->gpgPath->text());
QtPassSettings::setPassExecutable(ui->passPath->text());
}
/**
* @brief ConfigDialog::setGitPath set the git executable path.
* Make sure the checkBoxUseGit is updated.
* @param path
*/
void ConfigDialog::setGitPath(QString path) {
ui->gitPath->setText(path);
ui->checkBoxUseGit->setEnabled(!path.isEmpty());
if (path.isEmpty()) {
useGit(false);
}
}
/**
* @brief ConfigDialog::usePass set wether or not we want to use pass.
* Update radio buttons accordingly.
* @param usePass
*/
void ConfigDialog::usePass(bool usePass) {
ui->radioButtonNative->setChecked(!usePass);
ui->radioButtonPass->setChecked(usePass);
setGroupBoxState();
}
void ConfigDialog::on_accepted() {
QtPassSettings::setPassExecutable(ui->passPath->text());
QtPassSettings::setGitExecutable(ui->gitPath->text());
QtPassSettings::setGpgExecutable(ui->gpgPath->text());
QtPassSettings::setPassStore(
Util::normalizeFolderPath(ui->storePath->text()));
QtPassSettings::setUsePass(ui->radioButtonPass->isChecked());
QtPassSettings::setClipBoardType(ui->comboBoxClipboard->currentIndex());
QtPassSettings::setUseSelection(ui->checkBoxSelection->isChecked());
QtPassSettings::setUseAutoclear(ui->checkBoxAutoclear->isChecked());
QtPassSettings::setAutoclearSeconds(ui->spinBoxAutoclearSeconds->value());
QtPassSettings::setUseAutoclearPanel(ui->checkBoxAutoclearPanel->isChecked());
QtPassSettings::setAutoclearPanelSeconds(
ui->spinBoxAutoclearPanelSeconds->value());
QtPassSettings::setHidePassword(ui->checkBoxHidePassword->isChecked());
QtPassSettings::setHideContent(ui->checkBoxHideContent->isChecked());
QtPassSettings::setAddGPGId(ui->checkBoxAddGPGId->isChecked());
QtPassSettings::setUseTrayIcon(ui->checkBoxUseTrayIcon->isChecked());
QtPassSettings::setHideOnClose(hideOnClose());
QtPassSettings::setStartMinimized(ui->checkBoxStartMinimized->isChecked());
QtPassSettings::setProfiles(getProfiles());
QtPassSettings::setUseGit(ui->checkBoxUseGit->isChecked());
QtPassSettings::setUseOtp(ui->checkBoxUseOtp->isChecked());
QtPassSettings::setPwgenExecutable(ui->pwgenPath->text());
QtPassSettings::setUsePwgen(ui->checkBoxUsePwgen->isChecked());
QtPassSettings::setAvoidCapitals(ui->checkBoxAvoidCapitals->isChecked());
QtPassSettings::setAvoidNumbers(ui->checkBoxAvoidNumbers->isChecked());
QtPassSettings::setLessRandom(ui->checkBoxLessRandom->isChecked());
QtPassSettings::setUseSymbols(ui->checkBoxUseSymbols->isChecked());
QtPassSettings::setPasswordConfiguration(getPasswordConfiguration());
QtPassSettings::setUseTemplate(ui->checkBoxUseTemplate->isChecked());
QtPassSettings::setPassTemplate(ui->plainTextEditTemplate->toPlainText());
QtPassSettings::setTemplateAllFields(
ui->checkBoxTemplateAllFields->isChecked());
QtPassSettings::setAutoPush(ui->checkBoxAutoPush->isChecked());
QtPassSettings::setAutoPull(ui->checkBoxAutoPull->isChecked());
QtPassSettings::setAlwaysOnTop(ui->checkBoxAlwaysOnTop->isChecked());
QtPassSettings::setVersion(VERSION);
}
/**
* @brief ConfigDialog::on_radioButtonNative_clicked wrapper for
* ConfigDialog::setGroupBoxState()
*/
void ConfigDialog::on_radioButtonNative_clicked() { setGroupBoxState(); }
/**
* @brief ConfigDialog::on_radioButtonPass_clicked wrapper for
* ConfigDialog::setGroupBoxState()
*/
void ConfigDialog::on_radioButtonPass_clicked() { setGroupBoxState(); }
/**
* @brief ConfigDialog::setGroupBoxState update checkboxes.
*/
void ConfigDialog::setGroupBoxState() {
bool state = ui->radioButtonPass->isChecked();
ui->groupBoxNative->setEnabled(!state);
ui->groupBoxPass->setEnabled(state);
}
/**
* @brief ConfigDialog::selectExecutable pop-up to choose an executable.
* @return
*/
QString ConfigDialog::selectExecutable() {
QFileDialog dialog(this);
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setOption(QFileDialog::ReadOnly);
if (dialog.exec())
return dialog.selectedFiles().first();
else
return QString();
}
/**
* @brief ConfigDialog::selectFolder pop-up to choose a folder.
* @return
*/
QString ConfigDialog::selectFolder() {
QFileDialog dialog(this);
dialog.setFileMode(QFileDialog::Directory);
dialog.setFilter(QDir::NoFilter);
dialog.setOption(QFileDialog::ShowDirsOnly);
if (dialog.exec())
return dialog.selectedFiles().first();
else
return QString();
}
/**
* @brief ConfigDialog::on_toolButtonGit_clicked get git application.
* Enable checkboxes if found.
*/
void ConfigDialog::on_toolButtonGit_clicked() {
QString git = selectExecutable();
bool state = !git.isEmpty();
if (state) {
ui->gitPath->setText(git);
} else {
useGit(false);
}
ui->checkBoxUseGit->setEnabled(state);
}
/**
* @brief ConfigDialog::on_toolButtonGpg_clicked get gpg application.
*/
void ConfigDialog::on_toolButtonGpg_clicked() {
QString gpg = selectExecutable();
if (!gpg.isEmpty())
ui->gpgPath->setText(gpg);
}
/**
* @brief ConfigDialog::on_toolButtonPass_clicked get pass application.
*/
void ConfigDialog::on_toolButtonPass_clicked() {
QString pass = selectExecutable();
if (!pass.isEmpty())
ui->passPath->setText(pass);
}
/**
* @brief ConfigDialog::on_toolButtonStore_clicked get .password-store
* location.s
*/
void ConfigDialog::on_toolButtonStore_clicked() {
QString store = selectFolder();
if (!store.isEmpty()) // TODO(annejan) call check
ui->storePath->setText(store);
}
/**
* @brief ConfigDialog::on_comboBoxClipboard_activated show and hide options.
* @param index of selectbox (0 = no clipboard).
*/
void ConfigDialog::on_comboBoxClipboard_activated(int index) {
bool state = index > 0;
ui->checkBoxSelection->setEnabled(state);
ui->checkBoxAutoclear->setEnabled(state);
ui->checkBoxHidePassword->setEnabled(state);
ui->checkBoxHideContent->setEnabled(state);
if (state) {
ui->spinBoxAutoclearSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
ui->labelSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
} else {
ui->spinBoxAutoclearSeconds->setEnabled(false);
ui->labelSeconds->setEnabled(false);
}
}
/**
* @brief ConfigDialog::on_checkBoxAutoclearPanel_clicked enable and disable
* options based on autoclear use.
*/
void ConfigDialog::on_checkBoxAutoclearPanel_clicked() {
bool state = ui->checkBoxAutoclearPanel->isChecked();
ui->spinBoxAutoclearPanelSeconds->setEnabled(state);
ui->labelPanelSeconds->setEnabled(state);
}
/**
* @brief ConfigDialog::useSelection set the clipboard type use from
* MainWindow.
* @param useSelection
*/
void ConfigDialog::useSelection(bool useSelection) {
ui->checkBoxSelection->setChecked(useSelection);
on_checkBoxSelection_clicked();
}
/**
* @brief ConfigDialog::useAutoclear set the clipboard autoclear use from
* MainWindow.
* @param useAutoclear
*/
void ConfigDialog::useAutoclear(bool useAutoclear) {
ui->checkBoxAutoclear->setChecked(useAutoclear);
on_checkBoxAutoclear_clicked();
}
/**
* @brief ConfigDialog::useAutoclearPanel set the panel autoclear use from
* MainWindow.
* @param useAutoclearPanel
*/
void ConfigDialog::useAutoclearPanel(bool useAutoclearPanel) {
ui->checkBoxAutoclearPanel->setChecked(useAutoclearPanel);
on_checkBoxAutoclearPanel_clicked();
}
/**
* @brief ConfigDialog::on_checkBoxSelection_clicked checkbox clicked, update
* state via ConfigDialog::on_comboBoxClipboard_activated
*/
void ConfigDialog::on_checkBoxSelection_clicked() {
on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
}
/**
* @brief ConfigDialog::on_checkBoxAutoclear_clicked checkbox clicked, update
* state via ConfigDialog::on_comboBoxClipboard_activated
*/
void ConfigDialog::on_checkBoxAutoclear_clicked() {
on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
}
/**
* @brief ConfigDialog::genKey tunnel function to make MainWindow generate a gpg
* key pair.
* @todo refactor the process to not be entangled so much.
* @param batch
* @param dialog
*/
void ConfigDialog::genKey(QString batch, QDialog *dialog) {
mainWindow->generateKeyPair(batch, dialog);
}
/**
* @brief ConfigDialog::setProfiles set the profiles and chosen profile from
* MainWindow.
* @param profiles
* @param profile
*/
void ConfigDialog::setProfiles(QHash<QString, QString> profiles,
QString profile) {
// dbg()<< profiles;
if (profiles.contains("")) {
profiles.remove("");
// remove weird "" key value pairs
}
ui->profileTable->setRowCount(profiles.count());
QHashIterator<QString, QString> i(profiles);
int n = 0;
while (i.hasNext()) {
i.next();
if (!i.value().isEmpty() && !i.key().isEmpty()) {
ui->profileTable->setItem(n, 0, new QTableWidgetItem(i.key()));
ui->profileTable->setItem(n, 1, new QTableWidgetItem(i.value()));
// dbg()<< "naam:" + i.key();
if (i.key() == profile)
ui->profileTable->selectRow(n);
}
++n;
}
}
/**
* @brief ConfigDialog::getProfiles return profile list.
* @return
*/
QHash<QString, QString> ConfigDialog::getProfiles() {
QHash<QString, QString> profiles;
// Check?
for (int i = 0; i < ui->profileTable->rowCount(); ++i) {
QTableWidgetItem *pathItem = ui->profileTable->item(i, 1);
if (0 != pathItem) {
QTableWidgetItem *item = ui->profileTable->item(i, 0);
if (item == 0) {
dbg() << "empty name, should fix in frontend";
continue;
}
profiles.insert(item->text(), pathItem->text());
}
}
return profiles;
}
/**
* @brief ConfigDialog::on_addButton_clicked add a profile row.
*/
void ConfigDialog::on_addButton_clicked() {
int n = ui->profileTable->rowCount();
ui->profileTable->insertRow(n);
ui->profileTable->setItem(n, 1, new QTableWidgetItem(ui->storePath->text()));
ui->profileTable->selectRow(n);
ui->deleteButton->setEnabled(true);
}
/**
* @brief ConfigDialog::on_deleteButton_clicked remove a profile row.
*/
void ConfigDialog::on_deleteButton_clicked() {
QSet<int> selectedRows; // we use a set to prevent doubles
QList<QTableWidgetItem *> itemList = ui->profileTable->selectedItems();
if (itemList.count() == 0) {
QMessageBox::warning(this, tr("No profile selected"),
tr("No profile selected to delete"));
return;
}
QTableWidgetItem *item;
foreach (item, itemList)
selectedRows.insert(item->row());
// get a list, and sort it big to small
QList<int> rows = selectedRows.toList();
qSort(rows.begin(), rows.end());
// now actually do the removing:
foreach (int row, rows)
ui->profileTable->removeRow(row);
if (ui->profileTable->rowCount() < 1)
ui->deleteButton->setEnabled(false);
}
/**
* @brief ConfigDialog::criticalMessage weapper for showing critical messages in
* a popup.
* @param title
* @param text
*/
void ConfigDialog::criticalMessage(const QString &title, const QString &text) {
QMessageBox::critical(this, title, text, QMessageBox::Ok, QMessageBox::Ok);
}
/**
* @brief ConfigDialog::wizard first-time use wizard.
* @todo make this thing more reliable.
*/
void ConfigDialog::wizard() {
// mainWindow->checkConfig();
bool clean = false;
QString gpg = ui->gpgPath->text();
// QString gpg = mainWindow->getGpgExecutable();
if (!QFile(gpg).exists()) {
criticalMessage(
tr("GnuPG not found"),
tr("Please install GnuPG on your system.<br>Install "
"<strong>gpg</strong> using your favorite package manager<br>or <a "
"href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
"from GnuPG.org"));
clean = true;
}
QStringList names = mainWindow->getSecretKeys();
dbg() << names;
if (QFile(gpg).exists() && names.empty()) {
KeygenDialog d(this);
if (!d.exec())
return;
}
QString passStore = ui->storePath->text();
if (!QFile(passStore).exists()) {
// TODO(annejan) pass version?
if (QMessageBox::question(
this, tr("Create password-store?"),
tr("Would you like to create a password-store at %1?")
.arg(passStore),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
QDir().mkdir(passStore);
#ifdef Q_OS_WIN
SetFileAttributes(passStore.toStdWString().c_str(),
FILE_ATTRIBUTE_HIDDEN);
#endif
if (ui->checkBoxUseGit->isChecked())
mainWindow->executePassGitInit();
mainWindow->userDialog(passStore);
}
}
if (!QFile(QDir(passStore).filePath(".gpg-id")).exists()) {
dbg() << ".gpg-id file does not exist";
if (!clean) {
criticalMessage(tr("Password store not initialised"),
tr("The folder %1 doesn't seem to be a password store or "
"is not yet initialised.")
.arg(passStore));
}
while (!QFile(passStore).exists()) {
on_toolButtonStore_clicked();
// allow user to cancel
if (passStore == ui->storePath->text())
return;
passStore = ui->storePath->text();
}
if (!QFile(passStore + ".gpg-id").exists()) {
dbg() << ".gpg-id file still does not exist :/";
// appears not to be store
// init yes / no ?
mainWindow->userDialog(passStore);
}
}
}
/**
* @brief ConfigDialog::hideOnClose return preference for hiding instead of
* closing (quitting) application.
* @return
*/
bool ConfigDialog::hideOnClose() {
return ui->checkBoxHideOnClose->isEnabled() &&
ui->checkBoxHideOnClose->isChecked();
}
/**
* @brief ConfigDialog::useTrayIcon set preference for using trayicon.
* Enable or disable related checkboxes accordingly.
* @param useSystray
*/
void ConfigDialog::useTrayIcon(bool useSystray) {
ui->checkBoxUseTrayIcon->setChecked(useSystray);
ui->checkBoxHideOnClose->setEnabled(useSystray);
ui->checkBoxStartMinimized->setEnabled(useSystray);
if (!useSystray) {
ui->checkBoxHideOnClose->setChecked(false);
ui->checkBoxStartMinimized->setChecked(false);
}
}
/**
* @brief ConfigDialog::on_checkBoxUseTrayIcon_clicked enable and disable
* related checkboxes.
*/
void ConfigDialog::on_checkBoxUseTrayIcon_clicked() {
bool state = ui->checkBoxUseTrayIcon->isChecked();
ui->checkBoxHideOnClose->setEnabled(state);
ui->checkBoxStartMinimized->setEnabled(state);
}
/**
* @brief ConfigDialog::closeEvent close this window.
* @param event
*/
void ConfigDialog::closeEvent(QCloseEvent *event) {
// TODO(annejan) save window size or something?
event->accept();
}
/**
* @brief ConfigDialog::useGit set preference for using git.
* @param useGit
*/
void ConfigDialog::useGit(bool useGit) {
ui->checkBoxUseGit->setChecked(useGit);
on_checkBoxUseGit_clicked();
}
+/**
+ * @brief ConfigDialog::useOtp set preference for using otp plugin.
+ * @param useOtp
+ */
void ConfigDialog::useOtp(bool useOtp) {
ui->checkBoxUseOtp->setChecked(useOtp);
}
/**
* @brief ConfigDialog::on_checkBoxUseGit_clicked enable or disable related
* checkboxes.
*/
void ConfigDialog::on_checkBoxUseGit_clicked() {
ui->checkBoxAddGPGId->setEnabled(ui->checkBoxUseGit->isChecked());
ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
}
/**
* @brief ConfigDialog::on_toolButtonPwgen_clicked enable or disable related
* options in the interface.
*/
void ConfigDialog::on_toolButtonPwgen_clicked() {
QString pwgen = selectExecutable();
if (!pwgen.isEmpty()) {
ui->pwgenPath->setText(pwgen);
ui->checkBoxUsePwgen->setEnabled(true);
} else {
ui->checkBoxUsePwgen->setEnabled(false);
ui->checkBoxUsePwgen->setChecked(false);
}
}
/**
* @brief ConfigDialog::setPwgenPath set pwgen executable path.
* Enable or disable related options in the interface.
* @param pwgen
*/
void ConfigDialog::setPwgenPath(QString pwgen) {
ui->pwgenPath->setText(pwgen);
if (pwgen.isEmpty()) {
ui->checkBoxUsePwgen->setChecked(false);
ui->checkBoxUsePwgen->setEnabled(false);
}
on_checkBoxUsePwgen_clicked();
}
/**
* @brief ConfigDialog::on_checkBoxUsPwgen_clicked enable or disable related
* options in the interface.
*/
void ConfigDialog::on_checkBoxUsePwgen_clicked() {
bool usePwgen = ui->checkBoxUsePwgen->isChecked();
ui->checkBoxAvoidCapitals->setEnabled(usePwgen);
ui->checkBoxAvoidNumbers->setEnabled(usePwgen);
ui->checkBoxLessRandom->setEnabled(usePwgen);
ui->checkBoxUseSymbols->setEnabled(usePwgen);
ui->lineEditPasswordChars->setEnabled(!usePwgen);
ui->labelPasswordChars->setEnabled(!usePwgen);
ui->passwordCharTemplateSelector->setEnabled(!usePwgen);
}
/**
* @brief ConfigDialog::usePwgen set preference for using pwgen (can be
* overruled buy empty pwgenPath).
* enable or disable related options in the interface via
* ConfigDialog::on_checkBoxUsePwgen_clicked
* @param usePwgen
*/
void ConfigDialog::usePwgen(bool usePwgen) {
if (ui->pwgenPath->text().isEmpty())
usePwgen = false;
ui->checkBoxUsePwgen->setChecked(usePwgen);
on_checkBoxUsePwgen_clicked();
}
void ConfigDialog::setPasswordConfiguration(
const PasswordConfiguration &config) {
ui->spinBoxPasswordLength->setValue(config.length);
ui->passwordCharTemplateSelector->setCurrentIndex(config.selected);
if (config.selected != PasswordConfiguration::CUSTOM)
ui->lineEditPasswordChars->setEnabled(false);
ui->lineEditPasswordChars->setText(config.Characters[config.selected]);
}
PasswordConfiguration ConfigDialog::getPasswordConfiguration() {
PasswordConfiguration config;
config.length = ui->spinBoxPasswordLength->value();
config.selected = static_cast<PasswordConfiguration::characterSet>(
ui->passwordCharTemplateSelector->currentIndex());
config.Characters[PasswordConfiguration::CUSTOM] =
ui->lineEditPasswordChars->text();
return config;
}
/**
* @brief ConfigDialog::on_passwordCharTemplateSelector_activated sets the
* passwordChar Template
* combo box to the desired entry
* @param entry of
*/
void ConfigDialog::on_passwordCharTemplateSelector_activated(int index) {
ui->lineEditPasswordChars->setText(
QtPassSettings::getPasswordConfiguration().Characters[index]);
if (index == 3) {
ui->lineEditPasswordChars->setEnabled(true);
} else {
ui->lineEditPasswordChars->setEnabled(false);
}
}
/**
* @brief ConfigDialog::on_checkBoxUseTemplate_clicked enable or disable the
* template field and options.
*/
void ConfigDialog::on_checkBoxUseTemplate_clicked() {
ui->plainTextEditTemplate->setEnabled(ui->checkBoxUseTemplate->isChecked());
ui->checkBoxTemplateAllFields->setEnabled(
ui->checkBoxUseTemplate->isChecked());
}
/**
* @brief ConfigDialog::useTemplate set preference for using templates.
* @param useTemplate
*/
void ConfigDialog::useTemplate(bool useTemplate) {
ui->checkBoxUseTemplate->setChecked(useTemplate);
on_checkBoxUseTemplate_clicked();
}
diff --git a/src/configdialog.ui b/src/configdialog.ui
index 6b242ac..c480cc9 100644
--- a/src/configdialog.ui
+++ b/src/configdialog.ui
@@ -1,1054 +1,1054 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigDialog</class>
<widget class="QDialog" name="ConfigDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>618</width>
<height>609</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Configuration</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="Tab_Settings">
<attribute name="title">
<string>Settings</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<layout class="QVBoxLayout" name="Layout_Clipboard">
<item>
<widget class="QLabel" name="label_8">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Clipboard behaviour:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="comboBoxClipboard">
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxSelection">
<property name="text">
<string>Use primary selection</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxAutoclear">
<property name="text">
<string>Autoclear after:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBoxAutoclearSeconds">
<property name="minimum">
<number>5</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelSeconds">
<property name="text">
<string>Seconds</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="Layout_PasswordBehaviour">
<item>
<widget class="QLabel" name="label_6">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Content panel behaviour:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="checkBoxHideContent">
<property name="text">
<string>Hide content</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxHidePassword">
<property name="text">
<string>Hide password</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QCheckBox" name="checkBoxAutoclearPanel">
<property name="text">
<string>Autoclear panel after:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBoxAutoclearPanelSeconds">
<property name="minimum">
<number>5</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelPanelSeconds">
<property name="minimumSize">
<size>
<width>55</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Seconds</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="Layout_PasswordGeneration">
<item>
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Password Generation:</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_2">
<property name="topMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Password Length:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_13">
<item>
<widget class="QSpinBox" name="spinBoxPasswordLength">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>8</number>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>16</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelLength">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Characters</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelPasswordChars">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Use characters:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="passwordCharTemplateSelector">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Select character set for password generation</string>
</property>
<item>
<property name="text">
<string>All Characters</string>
</property>
</item>
<item>
<property name="text">
<string>Alphabetical</string>
</property>
</item>
<item>
<property name="text">
<string>Alphanumerical</string>
</property>
</item>
<item>
<property name="text">
<string>Custom</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEditPasswordChars">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>600</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="topMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxUsePwgen">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Use pwgen</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkBoxAvoidCapitals">
<property name="text">
<string>Exclude capital letters</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="checkBoxUseSymbols">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Include special symbols </string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="checkBoxLessRandom">
<property name="text">
<string>Generate easy to memorize but less secure passwords</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBoxAvoidNumbers">
<property name="text">
<string>Exclude numbers</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="Layout_Git">
<item>
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Git:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QCheckBox" name="checkBoxUseGit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Use git</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="checkBoxAddGPGId">
<property name="text">
<string>Automatically add .gpg-id files</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxAutoPush">
<property name="text">
<string>Automatically push</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxAutoPull">
<property name="text">
<string>Automatically pull</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="Extensions">
<item>
- <widget class="QLabel" name="label_3">
+ <widget class="QLabel" name="label_10">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Extensions:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QCheckBox" name="checkBoxUseOtp">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Use pass otp extension</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="Layout_System">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>System:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="checkBoxUseTrayIcon">
<property name="text">
<string>Use TrayIcon</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxStartMinimized">
<property name="text">
<string>Start minimized</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxHideOnClose">
<property name="text">
<string>Hide on close</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxAlwaysOnTop">
<property name="text">
<string>Always on top</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="Tab_Programs">
<attribute name="title">
<string>Programs</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="label_9">
<property name="text">
<string>Select password storage program:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QRadioButton" name="radioButtonNative">
<property name="text">
<string>Nati&ve git/gpg</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButtonPass">
<property name="text">
<string>&Use pass</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBoxNative">
<property name="title">
<string>Native</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="labelGitPath">
<property name="text">
<string>git</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="gpgPath"/>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="toolButtonGpg">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="toolButtonGit">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelGpgPath">
<property name="text">
<string>gpg</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="gitPath"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="pwgenPath"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelPwgenPath">
<property name="text">
<string>pwgen</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QToolButton" name="toolButtonPwgen">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBoxPass">
<property name="title">
<string>Pass</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="QLabel" name="labelPassPath">
<property name="text">
<string>pass</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="passPath"/>
</item>
<item>
<widget class="QToolButton" name="toolButtonPass">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="zx2c4">
<property name="text">
<string><html><head/><body><p><a href="https://www.passwordstore.org/"><span style=" text-decoration: underline; color:#0000ff;">www.passwordstore.org</span></a></p></body></html></string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="Tab_Profiles">
<attribute name="title">
<string>Profiles</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QTableWidget" name="profileTable">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="editTriggers">
<set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Path</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_11">
<item row="0" column="0">
<widget class="QToolButton" name="addButton">
<property name="text">
<string>Add</string>
</property>
<property name="icon">
<iconset theme="list-add">
<normalon>:/icons/document-new.svg</normalon>
</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonFollowStyle</enum>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QToolButton" name="deleteButton">
<property name="text">
<string>Delete</string>
</property>
<property name="icon">
<iconset theme="list-remove">
<normalon>:/icons/edit-delete.svg</normalon>
</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonFollowStyle</enum>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="labelStorePath">
<property name="text">
<string>Current password-store</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QToolButton" name="toolButtonStore">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLineEdit" name="storePath"/>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="Tab_Template">
<attribute name="title">
<string>Template</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="locale">
<locale language="German" country="Germany"/>
</property>
<property name="text">
<string>Templates add extra fields in the password generation dialouge and in the password view.</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QCheckBox" name="checkBoxUseTemplate">
<property name="text">
<string>Use template</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxTemplateAllFields">
<property name="toolTip">
<string>Show all lines beginning with a word followed by a colon as fields in password fields, not only the listed ones</string>
</property>
<property name="text">
<string>Show all fields templated</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="plainTextEditTemplate">
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="plainText">
<string>login
url
email</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string><a href="https://QtPass.org/">QtPass</a> version </string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>radioButtonNative</tabstop>
<tabstop>radioButtonPass</tabstop>
<tabstop>gpgPath</tabstop>
<tabstop>toolButtonGpg</tabstop>
<tabstop>gitPath</tabstop>
<tabstop>toolButtonGit</tabstop>
<tabstop>pwgenPath</tabstop>
<tabstop>toolButtonPwgen</tabstop>
<tabstop>passPath</tabstop>
<tabstop>toolButtonPass</tabstop>
<tabstop>profileTable</tabstop>
<tabstop>addButton</tabstop>
<tabstop>deleteButton</tabstop>
<tabstop>storePath</tabstop>
<tabstop>toolButtonStore</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>556</x>
<y>518</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>214</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ConfigDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>556</x>
<y>518</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>214</y>
</hint>
</hints>
</connection>
</connections>
</ui>
diff --git a/src/imitatepass.cpp b/src/imitatepass.cpp
index ec7bfe0..379d23e 100644
--- a/src/imitatepass.cpp
+++ b/src/imitatepass.cpp
@@ -1,446 +1,444 @@
#include "imitatepass.h"
#include "debughelper.h"
#include "qtpasssettings.h"
#include <QDirIterator>
using namespace Enums;
/**
* @brief ImitatePass::ImitatePass for situaions when pass is not available
* we imitate the behavior of pass https://www.passwordstore.org/
*/
ImitatePass::ImitatePass() {}
/**
* @brief ImitatePass::GitInit git init wrapper
*/
void ImitatePass::GitInit() {
executeGit(GIT_INIT, {"init", QtPassSettings::getPassStore()});
}
/**
* @brief ImitatePass::GitPull git init wrapper
*/
void ImitatePass::GitPull() { executeGit(GIT_PULL, {"pull"}); }
/**
* @brief ImitatePass::GitPull_b git pull wrapper
*/
void ImitatePass::GitPull_b() {
exec.executeBlocking(QtPassSettings::getGitExecutable(), {"pull"});
}
/**
* @brief ImitatePass::GitPush git init wrapper
*/
void ImitatePass::GitPush() {
if (QtPassSettings::isUseGit()) {
executeGit(GIT_PUSH, {"push"});
}
}
/**
* @brief ImitatePass::Show shows content of file
*/
void ImitatePass::Show(QString file) {
file = QtPassSettings::getPassStore() + file + ".gpg";
QStringList args = {"-d", "--quiet", "--yes", "--no-encrypt-to",
"--batch", "--use-agent", file};
executeGpg(PASS_SHOW, args);
}
+void ImitatePass::OtpGenerate(QString file) {
-void ImitatePass::OtpShow(QString file) {
-
}
-
/**
* @brief ImitatePass::Insert create new file with encrypted content
*
* @param file file to be created
* @param newValue value to be stored in file
* @param overwrite whether to overwrite existing file
*/
void ImitatePass::Insert(QString file, QString newValue, bool overwrite) {
file = file + ".gpg";
transactionHelper trans(this, PASS_INSERT);
QStringList recipients = Pass::getRecipientList(file);
if (recipients.isEmpty()) {
// TODO(bezet): probably throw here
emit critical(tr("Can not edit"),
tr("Could not read encryption key to use, .gpg-id "
"file missing or invalid."));
return;
}
QStringList args = {"--batch", "-eq", "--output", file};
for (auto &r : recipients) {
args.append("-r");
args.append(r);
};
if (overwrite)
args.append("--yes");
args.append("-");
executeGpg(PASS_INSERT, args, newValue);
if (!QtPassSettings::isUseWebDav() && QtPassSettings::isUseGit()) {
// TODO(bezet) why not?
if (!overwrite)
executeGit(GIT_ADD, {"add", file});
QString path = QDir(QtPassSettings::getPassStore()).relativeFilePath(file);
path.replace(QRegExp("\\.gpg$"), "");
QString msg =
QString(overwrite ? "Edit" : "Add") + " for " + path + " using QtPass.";
GitCommit(file, msg);
}
}
/**
* @brief ImitatePass::GitCommit commit a file to git with an appropriate commit
* message
* @param file
* @param msg
*/
void ImitatePass::GitCommit(const QString &file, const QString &msg) {
executeGit(GIT_COMMIT, {"commit", "-m", msg, "--", file});
}
/**
* @brief ImitatePass::Remove custom implementation of "pass remove"
*/
void ImitatePass::Remove(QString file, bool isDir) {
file = QtPassSettings::getPassStore() + file;
transactionHelper trans(this, PASS_REMOVE);
if (!isDir)
file += ".gpg";
if (QtPassSettings::isUseGit()) {
executeGit(GIT_RM, {"rm", (isDir ? "-rf" : "-f"), file});
// TODO(bezet): commit message used to have pass-like file name inside(ie.
// getFile(file, true)
GitCommit(file, "Remove for " + file + " using QtPass.");
} else {
if (isDir) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QDir dir(file);
dir.removeRecursively();
#else
removeDir(QtPassSettings::getPassStore() + file);
#endif
} else
QFile(file).remove();
}
}
/**
* @brief ImitatePass::Init initialize pass repository
*
* @param path path in which new password-store will be created
* @param users list of users who shall be able to decrypt passwords in
* path
*/
void ImitatePass::Init(QString path, const QList<UserInfo> &users) {
QString gpgIdFile = path + ".gpg-id";
QFile gpgId(gpgIdFile);
bool addFile = false;
transactionHelper trans(this, PASS_INIT);
if (QtPassSettings::isAddGPGId(true)) {
QFileInfo checkFile(gpgIdFile);
if (!checkFile.exists() || !checkFile.isFile())
addFile = true;
}
if (!gpgId.open(QIODevice::WriteOnly | QIODevice::Text)) {
emit critical(tr("Cannot update"),
tr("Failed to open .gpg-id for writing."));
return;
}
bool secret_selected = false;
foreach (const UserInfo &user, users) {
if (user.enabled) {
gpgId.write((user.key_id + "\n").toUtf8());
secret_selected |= user.have_secret;
}
}
gpgId.close();
if (!secret_selected) {
emit critical(
tr("Check selected users!"),
tr("None of the selected keys have a secret key available.\n"
"You will not be able to decrypt any newly added passwords!"));
return;
}
if (!QtPassSettings::isUseWebDav() && QtPassSettings::isUseGit() &&
!QtPassSettings::getGitExecutable().isEmpty()) {
if (addFile)
executeGit(GIT_ADD, {"add", gpgIdFile});
QString path = gpgIdFile;
path.replace(QRegExp("\\.gpg$"), "");
GitCommit(gpgIdFile, "Added " + path + " using QtPass.");
}
reencryptPath(path);
}
/**
* @brief ImitatePass::removeDir delete folder recursive.
* @param dirName which folder.
* @return was removal succesful?
*/
bool ImitatePass::removeDir(const QString &dirName) {
bool result = true;
QDir dir(dirName);
if (dir.exists(dirName)) {
Q_FOREACH (QFileInfo info,
dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System |
QDir::Hidden | QDir::AllDirs | QDir::Files,
QDir::DirsFirst)) {
if (info.isDir())
result = removeDir(info.absoluteFilePath());
else
result = QFile::remove(info.absoluteFilePath());
if (!result)
return result;
}
result = dir.rmdir(dirName);
}
return result;
}
/**
* @brief ImitatePass::reencryptPath reencrypt all files under the chosen
* directory
*
* This is stil quite experimental..
* @param dir
*/
void ImitatePass::reencryptPath(QString dir) {
emit statusMsg(tr("Re-encrypting from folder %1").arg(dir), 3000);
emit startReencryptPath();
if (QtPassSettings::isAutoPull()) {
// TODO(bezet): move statuses inside actions?
emit statusMsg(tr("Updating password-store"), 2000);
GitPull_b();
}
QDir currentDir;
QDirIterator gpgFiles(dir, QStringList() << "*.gpg", QDir::Files,
QDirIterator::Subdirectories);
QStringList gpgId;
while (gpgFiles.hasNext()) {
QString fileName = gpgFiles.next();
if (gpgFiles.fileInfo().path() != currentDir.path()) {
gpgId = getRecipientList(fileName);
gpgId.sort();
}
// TODO(bezet): enable --with-colons for better future-proofness?
QStringList args = {
"-v", "--no-secmem-warning", "--no-permission-warning",
"--list-only", "--keyid-format=long", fileName};
QString keys, err;
exec.executeBlocking(QtPassSettings::getGpgExecutable(), args, &keys, &err);
QStringList actualKeys;
keys += err;
QStringList key = keys.split("\n");
QListIterator<QString> itr(key);
while (itr.hasNext()) {
QString current = itr.next();
QStringList cur = current.split(" ");
if (cur.length() > 4) {
QString actualKey = cur.takeAt(4);
if (actualKey.length() == 16) {
actualKeys << actualKey;
}
}
}
actualKeys.sort();
if (actualKeys != gpgId) {
// dbg()<< actualKeys << gpgId << getRecipientList(fileName);
dbg() << "reencrypt " << fileName << " for " << gpgId;
QString local_lastDecrypt = "Could not decrypt";
args = QStringList{"-d", "--quiet", "--yes", "--no-encrypt-to",
"--batch", "--use-agent", fileName};
exec.executeBlocking(QtPassSettings::getGpgExecutable(), args,
&local_lastDecrypt);
if (!local_lastDecrypt.isEmpty() &&
local_lastDecrypt != "Could not decrypt") {
if (local_lastDecrypt.right(1) != "\n")
local_lastDecrypt += "\n";
QStringList recipients = Pass::getRecipientList(fileName);
if (recipients.isEmpty()) {
emit critical(tr("Can not edit"),
tr("Could not read encryption key to use, .gpg-id "
"file missing or invalid."));
return;
}
args = QStringList{"--yes", "--batch", "-eq", "--output", fileName};
for (auto &i : recipients) {
args.append("-r");
args.append(i);
}
args.append("-");
exec.executeBlocking(QtPassSettings::getGpgExecutable(), args,
local_lastDecrypt);
if (!QtPassSettings::isUseWebDav() && QtPassSettings::isUseGit()) {
exec.executeBlocking(QtPassSettings::getGitExecutable(),
{"add", fileName});
QString path =
QDir(QtPassSettings::getPassStore()).relativeFilePath(fileName);
path.replace(QRegExp("\\.gpg$"), "");
exec.executeBlocking(QtPassSettings::getGitExecutable(),
{"commit", fileName, "-m",
"Edit for " + path + " using QtPass."});
}
} else {
dbg() << "Decrypt error on re-encrypt";
}
}
}
if (QtPassSettings::isAutoPush()) {
emit statusMsg(tr("Updating password-store"), 2000);
// TODO(bezet): this is non-blocking and shall be done outside
GitPush();
}
emit endReencryptPath();
}
void ImitatePass::Move(const QString src, const QString dest,
const bool force) {
QFileInfo destFileInfo(dest);
transactionHelper trans(this, PASS_MOVE);
if (QtPassSettings::isUseGit()) {
QStringList args;
args << "mv";
if (force) {
args << "-f";
}
args << src;
args << dest;
executeGit(GIT_MOVE, args);
QString message = QString("moved from %1 to %2 using QTPass.");
message = message.arg(src).arg(dest);
GitCommit("", message);
} else {
QDir qDir;
QFileInfo srcFileInfo(src);
QString destCopy = dest;
if (srcFileInfo.isFile() && destFileInfo.isDir()) {
destCopy = destFileInfo.absoluteFilePath() + QDir::separator() +
srcFileInfo.fileName();
}
if (force) {
qDir.remove(destCopy);
}
qDir.rename(src, destCopy);
}
// reecrypt all files under the new folder
if (destFileInfo.isDir()) {
reencryptPath(destFileInfo.absoluteFilePath());
} else if (destFileInfo.isFile()) {
reencryptPath(destFileInfo.dir().path());
}
}
void ImitatePass::Copy(const QString src, const QString dest,
const bool force) {
QFileInfo destFileInfo(dest);
transactionHelper trans(this, PASS_COPY);
if (QtPassSettings::isUseGit()) {
QStringList args;
args << "cp";
if (force) {
args << "-f";
}
args << src;
args << dest;
executeGit(GIT_COPY, args);
QString message = QString("copied from %1 to %2 using QTPass.");
message = message.arg(src).arg(dest);
GitCommit("", message);
} else {
QDir qDir;
if (force) {
qDir.remove(dest);
}
QFile::copy(src, dest);
}
// reecrypt all files under the new folder
if (destFileInfo.isDir()) {
reencryptPath(destFileInfo.absoluteFilePath());
} else if (destFileInfo.isFile()) {
reencryptPath(destFileInfo.dir().path());
}
}
/**
* @brief ImitatePass::executeGpg easy wrapper for running gpg commands
* @param args
*/
void ImitatePass::executeGpg(PROCESS id, const QStringList &args, QString input,
bool readStdout, bool readStderr) {
executeWrapper(id, QtPassSettings::getGpgExecutable(), args, input,
readStdout, readStderr);
}
/**
* @brief ImitatePass::executeGit easy wrapper for running git commands
* @param args
*/
void ImitatePass::executeGit(PROCESS id, const QStringList &args, QString input,
bool readStdout, bool readStderr) {
executeWrapper(id, QtPassSettings::getGitExecutable(), args, input,
readStdout, readStderr);
}
/**
* @brief ImitatePass::finished this function is overloaded to ensure
* identical behaviour to RealPass ie. only PASS_*
* processes are visible inside Pass::finish, so
* that interface-wise it all looks the same
* @param id
* @param exitCode
* @param out
* @param err
*/
void ImitatePass::finished(int id, int exitCode, const QString &out,
const QString &err) {
dbg() << "Imitate Pass";
static QString transactionOutput;
PROCESS pid = transactionIsOver(static_cast<PROCESS>(id));
transactionOutput.append(out);
if (exitCode == 0) {
if (pid == INVALID)
return;
} else {
while (pid == INVALID) {
id = exec.cancelNext();
if (id == -1) {
// this is probably irrecoverable and shall not happen
dbg() << "No such transaction!";
return;
}
pid = transactionIsOver(static_cast<PROCESS>(id));
}
}
Pass::finished(pid, exitCode, transactionOutput, err);
transactionOutput.clear();
}
/**
* @brief executeWrapper overrided so that every execution is a transaction
* @param id
* @param app
* @param args
* @param input
* @param readStdout
* @param readStderr
*/
void ImitatePass::executeWrapper(PROCESS id, const QString &app,
const QStringList &args, QString input,
bool readStdout, bool readStderr) {
transactionAdd(id);
Pass::executeWrapper(id, app, args, input, readStdout, readStderr);
}
diff --git a/src/imitatepass.h b/src/imitatepass.h
index 7247739..7c402aa 100644
--- a/src/imitatepass.h
+++ b/src/imitatepass.h
@@ -1,73 +1,73 @@
#ifndef IMITATEPASS_H
#define IMITATEPASS_H
#include "pass.h"
#include "simpletransaction.h"
/*!
\class ImitatePass
\brief Imitates pass features when pass is not enabled or available
*/
class ImitatePass : public Pass, private simpleTransaction {
Q_OBJECT
bool removeDir(const QString &dirName);
void GitCommit(const QString &file, const QString &msg);
void executeGit(PROCESS id, const QStringList &args,
QString input = QString(), bool readStdout = true,
bool readStderr = true);
void executeGpg(PROCESS id, const QStringList &args,
QString input = QString(), bool readStdout = true,
bool readStderr = true);
class transactionHelper {
simpleTransaction *m_transaction;
PROCESS m_result;
public:
transactionHelper(simpleTransaction *trans, PROCESS result)
: m_transaction(trans), m_result(result) {
m_transaction->transactionStart();
}
~transactionHelper() { m_transaction->transactionEnd(m_result); }
};
protected:
virtual void finished(int id, int exitCode, const QString &out,
const QString &err) Q_DECL_OVERRIDE;
virtual void executeWrapper(PROCESS id, const QString &app,
const QStringList &args, QString input,
bool readStdout = true,
bool readStderr = true) Q_DECL_OVERRIDE;
public:
ImitatePass();
virtual ~ImitatePass() {}
virtual void GitInit() Q_DECL_OVERRIDE;
virtual void GitPull() Q_DECL_OVERRIDE;
virtual void GitPull_b() Q_DECL_OVERRIDE;
virtual void GitPush() Q_DECL_OVERRIDE;
virtual void Show(QString file) Q_DECL_OVERRIDE;
- virtual void OtpShow(QString file) Q_DECL_OVERRIDE;
+ virtual void OtpGenerate(QString file) Q_DECL_OVERRIDE;
virtual void Insert(QString file, QString value,
bool overwrite = false) Q_DECL_OVERRIDE;
virtual void Remove(QString file, bool isDir = false) Q_DECL_OVERRIDE;
virtual void Init(QString path, const QList<UserInfo> &list) Q_DECL_OVERRIDE;
void reencryptPath(QString dir);
signals:
void startReencryptPath();
void endReencryptPath();
// Pass interface
public:
void Move(const QString src, const QString dest,
const bool force = false) Q_DECL_OVERRIDE;
void Copy(const QString src, const QString dest,
const bool force = false) Q_DECL_OVERRIDE;
};
#endif // IMITATEPASS_H
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 7b3e21a..e894b31 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1,1472 +1,1472 @@
#include "mainwindow.h"
#include "debughelper.h"
#include <QClipboard>
#include <QCloseEvent>
#include <QDesktopServices>
#include <QFileInfo>
#include <QInputDialog>
#include <QLabel>
#include <QMenu>
#include <QMessageBox>
#include <QQueue>
#include <QShortcut>
#include <QTextCodec>
#ifdef Q_OS_WIN
#define WIN32_LEAN_AND_MEAN /*_KILLING_MACHINE*/
#define WIN32_EXTRA_LEAN
#include <windows.h>
#include <winnetwk.h>
#undef DELETE
#endif
#include "configdialog.h"
#include "filecontent.h"
#include "keygendialog.h"
#include "passworddialog.h"
#include "qpushbuttonwithclipboard.h"
#include "qtpasssettings.h"
#include "settingsconstants.h"
#include "trayicon.h"
#include "ui_mainwindow.h"
#include "usersdialog.h"
#include "util.h"
/**
* @brief MainWindow::MainWindow handles all of the main functionality and also
* the main window.
* @param parent
*/
MainWindow::MainWindow(const QString &searchText, QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow), fusedav(this),
clippedText(QString()), freshStart(true), keygen(NULL),
startupPhase(true), tray(NULL) {
#ifdef __APPLE__
// extra treatment for mac os
// see http://doc.qt.io/qt-5/qkeysequence.html#qt_set_sequence_auto_mnemonic
qt_set_sequence_auto_mnemonic(true);
#endif
ui->setupUi(this);
// i think this should be moved out of MainWindow (in main.cpp as example)
if (!checkConfig()) {
// no working config so this should quit without config anything
QApplication::quit();
}
// register shortcut ctrl/cmd + Q to close the main window
new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this, SLOT(close()));
// register shortcut ctrl/cmd + C to copy the currently selected password
new QShortcut(QKeySequence(QKeySequence::StandardKey::Copy), this,
SLOT(copyPasswordFromTreeview()));
// TODO(bezet): this should be reconnected dynamically when pass changes
connectPassSignalHandlers(QtPassSettings::getRealPass());
connectPassSignalHandlers(QtPassSettings::getImitatePass());
// only for ipass
connect(QtPassSettings::getImitatePass(), SIGNAL(startReencryptPath()), this,
SLOT(startReencryptPath()));
connect(QtPassSettings::getImitatePass(), SIGNAL(endReencryptPath()), this,
SLOT(endReencryptPath()));
clearPanelTimer.setSingleShot(true);
connect(&clearPanelTimer, SIGNAL(timeout()), this, SLOT(clearPanel()));
clearClipboardTimer.setSingleShot(true);
connect(&clearClipboardTimer, SIGNAL(timeout()), this,
SLOT(clearClipboard()));
initToolBarButtons();
initStatusBar();
#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
ui->lineEdit->setClearButtonEnabled(true);
#endif
enableUiElements(true);
qsrand(static_cast<uint>(QTime::currentTime().msec()));
QTimer::singleShot(10, this, SLOT(focusInput()));
ui->lineEdit->setText(searchText);
}
/**
* @brief MainWindow::initToolBarButtons init main ToolBar and connect actions
*/
void MainWindow::initToolBarButtons() {
connect(ui->actionAddPassword, SIGNAL(triggered()), this,
SLOT(addPassword()));
connect(ui->actionAddFolder, SIGNAL(triggered()), this, SLOT(addFolder()));
connect(ui->actionEdit, SIGNAL(triggered()), this, SLOT(onEdit()));
connect(ui->actionDelete, SIGNAL(triggered()), this, SLOT(onDelete()));
connect(ui->actionPush, SIGNAL(triggered()), this, SLOT(onPush()));
connect(ui->actionUpdate, SIGNAL(triggered()), this, SLOT(onUpdate()));
connect(ui->actionUsers, SIGNAL(triggered()), this, SLOT(onUsers()));
connect(ui->actionConfig, SIGNAL(triggered()), this, SLOT(onConfig()));
-
- //if (check if pass otp is installed)
- connect(ui->actionOtp, SIGNAL(triggered()), this, SLOT(onOtp()));
+ connect(ui->actionOtp, SIGNAL(triggered()), this, SLOT(onOtp()));
ui->actionAddPassword->setIcon(
QIcon::fromTheme("document-new", QIcon(":/icons/document-new.svg")));
ui->actionAddFolder->setIcon(
QIcon::fromTheme("folder-new", QIcon(":/icons/folder-new.svg")));
ui->actionEdit->setIcon(QIcon::fromTheme(
"document-properties", QIcon(":/icons/document-properties.svg")));
ui->actionDelete->setIcon(
QIcon::fromTheme("edit-delete", QIcon(":/icons/edit-delete.svg")));
ui->actionPush->setIcon(
QIcon::fromTheme("go-up", QIcon(":/icons/go-top.svg")));
ui->actionUpdate->setIcon(
QIcon::fromTheme("go-down", QIcon(":/icons/go-bottom.svg")));
ui->actionUsers->setIcon(QIcon::fromTheme(
"x-office-address-book", QIcon(":/icons/x-office-address-book.svg")));
ui->actionConfig->setIcon(QIcon::fromTheme(
"applications-system", QIcon(":/icons/applications-system.svg")));
}
/**
* @brief MainWindow::initStatusBar init statusBar with default message and logo
*/
void MainWindow::initStatusBar() {
ui->statusBar->showMessage(tr("Welcome to QtPass %1").arg(VERSION), 2000);
QPixmap logo = QPixmap::fromImage(QImage(":/artwork/icon.svg"))
.scaledToHeight(statusBar()->height());
QLabel *logoApp = new QLabel(statusBar());
logoApp->setPixmap(logo);
statusBar()->addPermanentWidget(logoApp);
}
/**
* @brief MainWindow::focusInput selects any text (if applicable) in the search
* box and sets focus to it. Allows for easy searching, called at application
* start and when receiving empty message in MainWindow::messageAvailable when
* compiled with SINGLE_APP=1 (default).
*/
void MainWindow::focusInput() {
ui->lineEdit->selectAll();
ui->lineEdit->setFocus();
}
/**
* @brief MainWindow::~MainWindow destroy!
*/
MainWindow::~MainWindow() {
#ifdef Q_OS_WIN
if (QtPassSettings::isUseWebDav())
WNetCancelConnection2A(QtPassSettings::getPassStore().toUtf8().constData(),
0, 1);
#else
if (fusedav.state() == QProcess::Running) {
fusedav.terminate();
fusedav.waitForFinished(2000);
}
#endif
}
/**
* @brief MainWindow::changeEvent sets focus to the search box
* @param event
*/
void MainWindow::changeEvent(QEvent *event) {
QWidget::changeEvent(event);
if (event->type() == QEvent::ActivationChange) {
if (this->isActiveWindow()) {
focusInput();
}
}
}
/**
* @brief MainWindow::connectPassSignalHandlers this method connects Pass
* signals to approprite MainWindow
* slots
*
* @param pass pointer to pass instance
*/
void MainWindow::connectPassSignalHandlers(Pass *pass) {
// TODO(bezet): this is never emitted(should be), also naming(see
// critical())
connect(pass, &Pass::error, this, &MainWindow::processError);
connect(pass, &Pass::startingExecuteWrapper, this,
&MainWindow::executeWrapperStarted);
connect(pass, &Pass::critical, this, &MainWindow::critical);
connect(pass, &Pass::statusMsg, this, &MainWindow::showStatusMessage);
connect(pass, &Pass::processErrorExit, this, &MainWindow::processErrorExit);
connect(pass, &Pass::finishedGitInit, this, &MainWindow::passStoreChanged);
connect(pass, &Pass::finishedGitPull, this, &MainWindow::processFinished);
connect(pass, &Pass::finishedGitPush, this, &MainWindow::processFinished);
connect(pass, &Pass::finishedShow, this, &MainWindow::passShowHandler);
- connect(pass, &Pass::finishedOtpShow, this, &MainWindow::passOtpHandler);
+ connect(pass, &Pass::finishedOtpGenerate, this, &MainWindow::passOtpHandler);
connect(pass, &Pass::finishedInsert, this, &MainWindow::finishedInsert);
connect(pass, &Pass::finishedRemove, this, &MainWindow::passStoreChanged);
connect(pass, &Pass::finishedInit, this, &MainWindow::passStoreChanged);
connect(pass, &Pass::finishedMove, this, &MainWindow::passStoreChanged);
connect(pass, &Pass::finishedCopy, this, &MainWindow::passStoreChanged);
connect(pass, &Pass::finishedGenerateGPGKeys, this,
&MainWindow::keyGenerationComplete);
}
/**
* @brief MainWindow::mountWebDav is some scary voodoo magic
*/
void MainWindow::mountWebDav() {
#ifdef Q_OS_WIN
char dst[20] = {0};
NETRESOURCEA netres;
memset(&netres, 0, sizeof(netres));
netres.dwType = RESOURCETYPE_DISK;
netres.lpLocalName = 0;
netres.lpRemoteName = QtPassSettings::getWebDavUrl().toUtf8().data();
DWORD size = sizeof(dst);
DWORD r = WNetUseConnectionA(
reinterpret_cast<HWND>(effectiveWinId()), &netres,
QtPassSettings::getWebDavPassword().toUtf8().constData(),
QtPassSettings::getWebDavUser().toUtf8().constData(),
CONNECT_TEMPORARY | CONNECT_INTERACTIVE | CONNECT_REDIRECT, dst, &size,
0);
if (r == NO_ERROR) {
QtPassSettings::setPassStore(dst);
} else {
char message[256] = {0};
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, r, 0, message,
sizeof(message), 0);
ui->textBrowser->setTextColor(Qt::red);
ui->textBrowser->setText(tr("Failed to connect WebDAV:\n") + message +
" (0x" + QString::number(r, 16) + ")");
ui->textBrowser->setTextColor(Qt::black);
}
#else
fusedav.start("fusedav -o nonempty -u \"" + QtPassSettings::getWebDavUser() +
"\" " + QtPassSettings::getWebDavUrl() + " \"" +
QtPassSettings::getPassStore() + '"');
fusedav.waitForStarted();
if (fusedav.state() == QProcess::Running) {
QString pwd = QtPassSettings::getWebDavPassword();
bool ok = true;
if (pwd.isEmpty()) {
pwd = QInputDialog::getText(this, tr("QtPass WebDAV password"),
tr("Enter password to connect to WebDAV:"),
QLineEdit::Password, "", &ok);
}
if (ok && !pwd.isEmpty()) {
fusedav.write(pwd.toUtf8() + '\n');
fusedav.closeWriteChannel();
fusedav.waitForFinished(2000);
} else {
fusedav.terminate();
}
}
QString error = fusedav.readAllStandardError();
int prompt = error.indexOf("Password:");
if (prompt >= 0)
error.remove(0, prompt + 10);
if (fusedav.state() != QProcess::Running)
error = tr("fusedav exited unexpectedly\n") + error;
if (error.size() > 0) {
ui->textBrowser->setTextColor(Qt::red);
ui->textBrowser->setText(
tr("Failed to start fusedav to connect WebDAV:\n") + error);
ui->textBrowser->setTextColor(Qt::black);
}
#endif
}
/**
* @brief MainWindow::checkConfig make sure we are ready to go as soon as
* possible
*/
bool MainWindow::checkConfig() {
QString version = QtPassSettings::getVersion();
// if (freshStart) {
restoreWindow();
//}
QString passStore = QtPassSettings::getPassStore(Util::findPasswordStore());
QtPassSettings::setPassStore(passStore);
QtPassSettings::initExecutables();
if (QtPassSettings::isAlwaysOnTop()) {
Qt::WindowFlags flags = windowFlags();
this->setWindowFlags(flags | Qt::WindowStaysOnTopHint);
this->show();
}
if (QtPassSettings::isUseTrayIcon() && tray == NULL) {
initTrayIcon();
if (freshStart && QtPassSettings::isStartMinimized()) {
// since we are still in constructor, can't directly hide
QTimer::singleShot(10, this, SLOT(hide()));
}
} /*else if (!QtPassSettings::isUseTrayIcon() && tray != NULL) {
destroyTrayIcon();
}*/
// dbg()<< version;
// Config updates
if (version.isEmpty()) {
dbg() << "assuming fresh install";
if (QtPassSettings::getAutoclearSeconds() < 5)
QtPassSettings::setAutoclearSeconds(10);
if (QtPassSettings::getAutoclearPanelSeconds() < 5)
QtPassSettings::setAutoclearPanelSeconds(10);
if (!QtPassSettings::getPwgenExecutable().isEmpty())
QtPassSettings::setUsePwgen(true);
else
QtPassSettings::setUsePwgen(false);
QtPassSettings::setPassTemplate("login\nurl");
} else {
// QStringList ver = version.split(".");
// dbg()<< ver;
// if (ver[0] == "0" && ver[1] == "8") {
//// upgrade to 0.9
// }
if (QtPassSettings::getPassTemplate().isEmpty())
QtPassSettings::setPassTemplate("login\nurl");
}
QtPassSettings::setVersion(VERSION);
if (Util::checkConfig()) {
config();
if (freshStart && Util::checkConfig())
return false;
}
freshStart = false;
// TODO(annejan): this needs to be before we try to access the store,
// but it would be better to do it after the Window is shown,
// as the long delay it can cause is irritating otherwise.
if (QtPassSettings::isUseWebDav())
mountWebDav();
model.setNameFilters(QStringList() << "*.gpg");
model.setNameFilterDisables(false);
proxyModel.setSourceModel(&model);
proxyModel.setModelAndStore(&model, passStore);
selectionModel.reset(new QItemSelectionModel(&proxyModel));
model.fetchMore(model.setRootPath(passStore));
model.sort(0, Qt::AscendingOrder);
ui->treeView->setModel(&proxyModel);
ui->treeView->setRootIndex(
proxyModel.mapFromSource(model.setRootPath(passStore)));
ui->treeView->setColumnHidden(1, true);
ui->treeView->setColumnHidden(2, true);
ui->treeView->setColumnHidden(3, true);
ui->treeView->setHeaderHidden(true);
ui->treeView->setIndentation(15);
ui->treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
connect(ui->treeView, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(showContextMenu(const QPoint &)));
connect(ui->treeView, SIGNAL(emptyClicked()), this, SLOT(deselect()));
ui->textBrowser->setOpenExternalLinks(true);
ui->textBrowser->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->textBrowser, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(showBrowserContextMenu(const QPoint &)));
updateProfileBox();
QtPassSettings::getPass()->updateEnv();
clearPanelTimer.setInterval(1000 *
QtPassSettings::getAutoclearPanelSeconds());
clearClipboardTimer.setInterval(1000 * QtPassSettings::getAutoclearSeconds());
updateGitButtonVisibility();
updateOtpButtonVisibility();
startupPhase = false;
return true;
}
/**
* @brief MainWindow::config pops up the configuration screen and handles all
* inter-window communication
*/
void MainWindow::config() {
QScopedPointer<ConfigDialog> d(new ConfigDialog(this));
d->setModal(true);
// Automatically default to pass if it's available
if (freshStart && QFile(QtPassSettings::getPassExecutable()).exists()) {
QtPassSettings::setUsePass(true);
}
if (startupPhase)
d->wizard(); // does shit
if (d->exec()) {
if (d->result() == QDialog::Accepted) {
if (QtPassSettings::isAlwaysOnTop()) {
Qt::WindowFlags flags = windowFlags();
this->setWindowFlags(flags | Qt::WindowStaysOnTopHint);
} else {
this->setWindowFlags(Qt::Window);
}
this->show();
updateProfileBox();
ui->treeView->setRootIndex(proxyModel.mapFromSource(
model.setRootPath(QtPassSettings::getPassStore())));
if (freshStart && Util::checkConfig())
config();
QtPassSettings::getPass()->updateEnv();
clearPanelTimer.setInterval(1000 *
QtPassSettings::getAutoclearPanelSeconds());
clearClipboardTimer.setInterval(1000 *
QtPassSettings::getAutoclearSeconds());
updateGitButtonVisibility();
updateOtpButtonVisibility();
if (QtPassSettings::isUseTrayIcon() && tray == NULL)
initTrayIcon();
else if (!QtPassSettings::isUseTrayIcon() && tray != NULL) {
destroyTrayIcon();
}
}
freshStart = false;
}
}
/**
* @brief MainWindow::onUpdate do a git pull
*/
void MainWindow::onUpdate(bool block) {
ui->statusBar->showMessage(tr("Updating password-store"), 2000);
if (block)
QtPassSettings::getPass()->GitPull_b();
else
QtPassSettings::getPass()->GitPull();
}
/**
* @brief MainWindow::onPush do a git push
*/
void MainWindow::onPush() {
if (QtPassSettings::isUseGit()) {
ui->statusBar->showMessage(tr("Updating password-store"), 2000);
QtPassSettings::getPass()->GitPush();
}
}
/**
* @brief MainWindow::getFile get the selected file path
* @param index
* @param forPass returns relative path without '.gpg' extension
* @return path
* @return
*/
QString MainWindow::getFile(const QModelIndex &index, bool forPass) {
if (!index.isValid() ||
!model.fileInfo(proxyModel.mapToSource(index)).isFile())
return QString();
QString filePath = model.filePath(proxyModel.mapToSource(index));
if (forPass) {
filePath = QDir(QtPassSettings::getPassStore()).relativeFilePath(filePath);
filePath.replace(QRegExp("\\.gpg$"), "");
}
return filePath;
}
/**
* @brief MainWindow::on_treeView_clicked read the selected password file
* @param index
*/
void MainWindow::on_treeView_clicked(const QModelIndex &index) {
bool cleared = ui->treeView->currentIndex().flags() == Qt::NoItemFlags;
currentDir =
Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
// TODO(bezet): "Could not decrypt";
clippedText = "";
QString file = getFile(index, true);
ui->passwordName->setText(getFile(index, true));
if (!file.isEmpty() && !cleared) {
QtPassSettings::getPass()->Show(file);
} else {
clearPanel(false);
ui->actionEdit->setEnabled(false);
ui->actionDelete->setEnabled(true);
}
}
/**
* @brief MainWindow::on_treeView_doubleClicked when doubleclicked on
* TreeViewItem, open the edit Window
* @param index
*/
void MainWindow::on_treeView_doubleClicked(const QModelIndex &index) {
QFileInfo fileOrFolder =
model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
if (fileOrFolder.isFile()) {
editPassword(getFile(index, true));
}
}
/**
* @brief MainWindow::deselect clear the selection, password and copy buffer
*/
void MainWindow::deselect() {
currentDir = "/";
clearClipboard();
ui->passwordName->setText("");
clearPanel(false);
}
/**
* @brief MainWindow::executePassGitInit git init wrapper
*/
void MainWindow::executePassGitInit() {
dbg() << "Pass git init called";
QtPassSettings::getPass()->GitInit();
}
void MainWindow::executeWrapperStarted() {
clearTemplateWidgets();
ui->textBrowser->clear();
enableUiElements(false);
clearPanelTimer.stop();
}
void MainWindow::keyGenerationComplete(const QString &p_output,
const QString &p_errout) {
// qDebug() << p_output;
// qDebug() << p_errout;
if (0 != keygen) {
qDebug() << "Keygen Done";
keygen->close();
keygen = 0;
// TODO(annejan) some sanity checking ?
}
processFinished(p_output, p_errout);
}
void MainWindow::passShowHandler(const QString &p_output) {
QStringList templ = QtPassSettings::isUseTemplate()
? QtPassSettings::getPassTemplate().split("\n")
: QStringList();
bool allFields =
QtPassSettings::isUseTemplate() && QtPassSettings::isTemplateAllFields();
FileContent fileContent = FileContent::parse(p_output, templ, allFields);
QString output = p_output;
QString password = fileContent.getPassword();
// handle clipboard
if (QtPassSettings::getClipBoardType() != Enums::CLIPBOARD_NEVER &&
!p_output.isEmpty()) {
clippedText = password;
if (QtPassSettings::getClipBoardType() == Enums::CLIPBOARD_ALWAYS)
copyTextToClipboard(password);
}
// first clear the current view:
clearTemplateWidgets();
// show what is needed:
if (QtPassSettings::isHideContent()) {
output = "***" + tr("Content hidden") + "***";
} else {
if (!password.isEmpty()) {
// set the password, it is hidden if needed in addToGridLayout
addToGridLayout(0, tr("Password"), password);
}
NamedValues namedValues = fileContent.getNamedValues();
for (int j = 0; j < namedValues.length(); ++j) {
NamedValue nv = namedValues.at(j);
addToGridLayout(j + 1, nv.name, nv.value);
}
if (ui->gridLayout->count() == 0)
ui->verticalLayoutPassword->setSpacing(0);
else
ui->verticalLayoutPassword->setSpacing(6);
output = fileContent.getRemainingData();
}
if (QtPassSettings::isUseAutoclearPanel()) {
clearPanelTimer.start();
}
DisplayInTextBrowser(output);
enableUiElements(true);
}
void MainWindow::passOtpHandler(const QString &p_output) {
+ if (!p_output.isEmpty()) {
+ addToGridLayout(ui->gridLayout->count()+1, tr("OTP Code"), p_output);
+ copyTextToClipboard(p_output);
+ }
+ if (QtPassSettings::isUseAutoclearPanel()) {
+ clearPanelTimer.start();
+ }
enableUiElements(true);
}
void MainWindow::passStoreChanged(const QString &p_out, const QString &p_err) {
processFinished(p_out, p_err);
doGitPush();
}
void MainWindow::doGitPush() {
if (QtPassSettings::isAutoPush())
onPush();
}
void MainWindow::finishedInsert(const QString &p_output,
const QString &p_errout) {
processFinished(p_output, p_errout);
doGitPush();
on_treeView_clicked(ui->treeView->currentIndex());
}
void MainWindow::DisplayInTextBrowser(QString output, QString prefix,
QString postfix) {
output.replace(QRegExp("<"), "<");
output.replace(QRegExp(">"), ">");
output.replace(QRegExp(" "), " ");
output.replace(
QRegExp("((?:https?|ftp|ssh|sftp|ftps|webdav|webdavs)://\\S+)"),
"<a href=\"\\1\">\\1</a>");
output.replace(QRegExp("\n"), "<br />");
output = prefix + output + postfix;
if (!ui->textBrowser->toPlainText().isEmpty())
output = ui->textBrowser->toHtml() + output;
ui->textBrowser->setHtml(output);
}
void MainWindow::processErrorExit(int exitCode, const QString &p_error) {
if (!p_error.isEmpty()) {
QString output;
QString error = p_error;
error.replace(QRegExp("<"), "<");
error.replace(QRegExp(">"), ">");
error.replace(QRegExp(" "), " ");
if (exitCode == 0) {
// https://github.com/IJHack/qtpass/issues/111
output = "<span style=\"color: darkgray;\">" + error + "</span><br />";
} else {
output = "<span style=\"color: red;\">" + error + "</span><br />";
}
output.replace(
QRegExp("((?:https?|ftp|ssh|sftp|ftps|webdav|webdavs)://\\S+)"),
"<a href=\"\\1\">\\1</a>");
output.replace(QRegExp("\n"), "<br />");
if (!ui->textBrowser->toPlainText().isEmpty())
output = ui->textBrowser->toHtml() + output;
ui->textBrowser->setHtml(output);
}
enableUiElements(true);
}
/**
* @brief MainWindow::clearClipboard remove clipboard contents.
*/
void MainWindow::clearClipboard() {
QClipboard *clipboard = QApplication::clipboard();
bool cleared = false;
if (this->clippedText == clipboard->text(QClipboard::Selection)) {
clipboard->clear(QClipboard::Clipboard);
cleared = true;
}
if (this->clippedText == clipboard->text(QClipboard::Clipboard)) {
clipboard->clear(QClipboard::Clipboard);
cleared = true;
}
if (cleared) {
ui->statusBar->showMessage(tr("Clipboard cleared"), 2000);
} else {
ui->statusBar->showMessage(tr("Clipboard not cleared"), 2000);
}
this->clippedText.clear();
}
/**
* @brief MainWindow::clearPanel hide the information from shoulder surfers
*/
void MainWindow::clearPanel(bool notify) {
while (ui->gridLayout->count() > 0) {
QLayoutItem *item = ui->gridLayout->takeAt(0);
delete item->widget();
delete item;
}
if (notify) {
QString output = "***" + tr("Password and Content hidden") + "***";
ui->textBrowser->setHtml(output);
} else {
ui->textBrowser->setHtml("");
}
}
/**
* @brief MainWindow::processFinished background process has finished
* @param exitCode
* @param exitStatus
* @param output stdout from a process
* @param errout stderr from a process
*/
void MainWindow::processFinished(const QString &p_output,
const QString &p_errout) {
DisplayInTextBrowser(p_output);
// Sometimes there is error output even with 0 exit code, which is
// assumed in this function
processErrorExit(0, p_errout);
enableUiElements(true);
}
/**
* @brief MainWindow::enableUiElements enable or disable the relevant UI
* elements
* @param state
*/
void MainWindow::enableUiElements(bool state) {
ui->treeView->setEnabled(state);
ui->lineEdit->setEnabled(state);
ui->lineEdit->installEventFilter(this);
ui->actionAddPassword->setEnabled(state);
ui->actionAddFolder->setEnabled(state);
ui->actionUsers->setEnabled(state);
ui->actionConfig->setEnabled(state);
// is a file selected?
state &= ui->treeView->currentIndex().isValid();
ui->actionDelete->setEnabled(state);
ui->actionEdit->setEnabled(state);
updateGitButtonVisibility();
updateOtpButtonVisibility();
}
void MainWindow::restoreWindow() {
QByteArray geometry = QtPassSettings::getGeometry(saveGeometry());
restoreGeometry(geometry);
QByteArray savestate = QtPassSettings::getSavestate(saveState());
restoreState(savestate);
QPoint position = QtPassSettings::getPos(pos());
move(position);
QSize newSize = QtPassSettings::getSize(size());
resize(newSize);
if (QtPassSettings::isMaximized(isMaximized())) {
showMaximized();
}
}
/**
* @brief MainWindow::processError something went wrong
* @param error
*/
void MainWindow::processError(QProcess::ProcessError error) {
QString errorString;
switch (error) {
case QProcess::FailedToStart:
errorString = tr("QProcess::FailedToStart");
break;
case QProcess::Crashed:
errorString = tr("QProcess::Crashed");
break;
case QProcess::Timedout:
errorString = tr("QProcess::Timedout");
break;
case QProcess::ReadError:
errorString = tr("QProcess::ReadError");
break;
case QProcess::WriteError:
errorString = tr("QProcess::WriteError");
break;
case QProcess::UnknownError:
errorString = tr("QProcess::UnknownError");
break;
}
ui->textBrowser->setTextColor(Qt::red);
ui->textBrowser->setText(errorString);
ui->textBrowser->setTextColor(Qt::black);
enableUiElements(true);
}
/**
* @brief MainWindow::on_configButton_clicked run Mainwindow::config
*/
void MainWindow::onConfig() { config(); }
/**
* @brief Executes when the string in the search box changes, collapses the
* TreeView
* @param arg1
*/
void MainWindow::on_lineEdit_textChanged(const QString &arg1) {
ui->treeView->expandAll();
ui->statusBar->showMessage(tr("Looking for: %1").arg(arg1), 1000);
QString query = arg1;
query.replace(QRegExp(" "), ".*");
QRegExp regExp(query, Qt::CaseInsensitive);
proxyModel.setFilterRegExp(regExp);
ui->treeView->setRootIndex(proxyModel.mapFromSource(
model.setRootPath(QtPassSettings::getPassStore())));
selectFirstFile();
}
/**
* @brief MainWindow::on_lineEdit_returnPressed get searching
*
* Select the first possible file in the tree
*/
void MainWindow::on_lineEdit_returnPressed() {
dbg() << "on_lineEdit_returnPressed";
selectFirstFile();
on_treeView_clicked(ui->treeView->currentIndex());
}
/**
* @brief MainWindow::selectFirstFile select the first possible file in the
* tree
*/
void MainWindow::selectFirstFile() {
QModelIndex index = proxyModel.mapFromSource(
model.setRootPath(QtPassSettings::getPassStore()));
index = firstFile(index);
ui->treeView->setCurrentIndex(index);
}
/**
* @brief MainWindow::firstFile return location of first possible file
* @param parentIndex
* @return QModelIndex
*/
QModelIndex MainWindow::firstFile(QModelIndex parentIndex) {
QModelIndex index = parentIndex;
int numRows = proxyModel.rowCount(parentIndex);
for (int row = 0; row < numRows; ++row) {
index = proxyModel.index(row, 0, parentIndex);
if (model.fileInfo(proxyModel.mapToSource(index)).isFile())
return index;
if (proxyModel.hasChildren(index))
return firstFile(index);
}
return index;
}
/**
* @brief MainWindow::setPassword open passworddialog
* @param file which pgp file
* @param isNew insert (not update)
*/
void MainWindow::setPassword(QString file, bool isNew) {
PasswordDialog d(file, isNew, this);
connect(QtPassSettings::getPass(), &Pass::finishedShow, &d,
&PasswordDialog::setPass);
if (!d.exec()) {
this->ui->treeView->setFocus();
}
}
/**
* @brief MainWindow::addPassword add a new password by showing a
* number of dialogs.
*/
void MainWindow::addPassword() {
bool ok;
QString dir =
Util::getDir(ui->treeView->currentIndex(), true, model, proxyModel);
QString file =
QInputDialog::getText(this, tr("New file"),
tr("New password file: \n(Will be placed in %1 )")
.arg(QtPassSettings::getPassStore() +
Util::getDir(ui->treeView->currentIndex(),
true, model, proxyModel)),
QLineEdit::Normal, "", &ok);
if (!ok || file.isEmpty())
return;
file = dir + file;
setPassword(file);
}
/**
* @brief MainWindow::onDelete remove password, if you are
* sure.
*/
void MainWindow::onDelete() {
QFileInfo fileOrFolder =
model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
QString file = "";
bool isDir = false;
if (fileOrFolder.isFile()) {
file = getFile(ui->treeView->currentIndex(), true);
} else {
file = Util::getDir(ui->treeView->currentIndex(), true, model, proxyModel);
isDir = true;
}
QString dirMessage = tr(" and the whole content?");
if (isDir) {
QDirIterator it(model.rootPath() + "/" + file,
QDirIterator::Subdirectories);
bool okDir = true;
while (it.hasNext() && okDir) {
it.next();
if (QFileInfo(it.filePath()).isFile()) {
if (QFileInfo(it.filePath()).suffix() != "gpg") {
okDir = false;
dirMessage = tr(" and the whole content? <br><strong>Attention: "
"there are unexpected files in the given folder, "
"check them before continue.</strong>");
}
}
}
}
if (QMessageBox::question(
this, isDir ? tr("Delete folder?") : tr("Delete password?"),
tr("Are you sure you want to delete %1%2")
.arg(QDir::separator() + file)
.arg(isDir ? dirMessage : "?"),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
return;
QtPassSettings::getPass()->Remove(file, isDir);
}
/**
* @brief MainWindow::onOTP try and generate (selected) OTP code.
*/
void MainWindow::onOtp() {
QString file = getFile(ui->treeView->currentIndex(), true);
- generateOtp(file);
+ if (!file.isEmpty()) {
+ if (QtPassSettings::isUseOtp())
+ QtPassSettings::getPass()->OtpGenerate(file);
+ }
}
/**
* @brief MainWindow::onEdit try and edit (selected) password.
*/
void MainWindow::onEdit() {
QString file = getFile(ui->treeView->currentIndex(), true);
editPassword(file);
}
/**
* @brief MainWindow::userDialog see MainWindow::onUsers()
* @param dir folder to edit users for.
*/
void MainWindow::userDialog(QString dir) {
if (!dir.isEmpty())
currentDir = dir;
onUsers();
}
/**
* @brief MainWindow::onUsers edit users for the current
* folder,
* gets lists and opens UserDialog.
*/
void MainWindow::onUsers() {
QList<UserInfo> users = QtPassSettings::getPass()->listKeys();
if (users.size() == 0) {
QMessageBox::critical(this, tr("Can not get key list"),
tr("Unable to get list of available gpg keys"));
return;
}
QList<UserInfo> secret_keys = QtPassSettings::getPass()->listKeys("", true);
foreach (const UserInfo &sec, secret_keys) {
for (QList<UserInfo>::iterator it = users.begin(); it != users.end(); ++it)
if (sec.key_id == it->key_id)
it->have_secret = true;
}
QList<UserInfo> selected_users;
QString dir =
currentDir.isEmpty()
? Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel)
: currentDir;
int count = 0;
QString recipients = QtPassSettings::getPass()->getRecipientString(
dir.isEmpty() ? "" : dir, " ", &count);
if (!recipients.isEmpty())
selected_users = QtPassSettings::getPass()->listKeys(recipients);
foreach (const UserInfo &sel, selected_users) {
for (QList<UserInfo>::iterator it = users.begin(); it != users.end(); ++it)
if (sel.key_id == it->key_id)
it->enabled = true;
}
if (count > selected_users.size()) {
// Some keys seem missing from keyring, add them separately
QStringList recipients =
QtPassSettings::getPass()->getRecipientList(dir.isEmpty() ? "" : dir);
foreach (const QString recipient, recipients) {
if (QtPassSettings::getPass()->listKeys(recipient).size() < 1) {
UserInfo i;
i.enabled = true;
i.key_id = recipient;
i.name = " ?? " + tr("Key not found in keyring");
users.append(i);
}
}
}
UsersDialog d(this);
d.setUsers(&users);
if (!d.exec()) {
d.setUsers(NULL);
return;
}
d.setUsers(NULL);
QtPassSettings::getPass()->Init(dir, users);
}
/**
* @brief MainWindow::messageAvailable we have some text/message/search to do.
* @param message
*/
void MainWindow::messageAvailable(QString message) {
if (message.isEmpty()) {
focusInput();
} else {
ui->treeView->expandAll();
ui->lineEdit->setText(message);
on_lineEdit_returnPressed();
}
show();
raise();
}
/**
* @brief MainWindow::getSecretKeys get list of secret/private keys
* @return QStringList keys
*/
QStringList MainWindow::getSecretKeys() {
QList<UserInfo> keys = QtPassSettings::getPass()->listKeys("", true);
QStringList names;
if (keys.size() == 0)
return names;
foreach (const UserInfo &sec, keys)
names << sec.name;
return names;
}
/**
* @brief MainWindow::generateKeyPair internal gpg keypair generator . .
* @param batch
* @param keygenWindow
*/
void MainWindow::generateKeyPair(QString batch, QDialog *keygenWindow) {
keygen = keygenWindow;
ui->statusBar->showMessage(tr("Generating GPG key pair"), 60000);
QtPassSettings::getPass()->GenerateGPGKeys(batch);
}
/**
* @brief MainWindow::updateProfileBox update the list of profiles, optionally
* select a more appropriate one to view too
*/
void MainWindow::updateProfileBox() {
QHash<QString, QString> profiles = QtPassSettings::getProfiles();
if (profiles.isEmpty()) {
ui->profileWidget->hide();
} else {
ui->profileWidget->show();
ui->profileBox->setEnabled(profiles.size() > 1);
ui->profileBox->clear();
QHashIterator<QString, QString> i(profiles);
while (i.hasNext()) {
i.next();
if (!i.key().isEmpty())
ui->profileBox->addItem(i.key());
}
}
int index = ui->profileBox->findText(QtPassSettings::getProfile());
if (index != -1) // -1 for not found
ui->profileBox->setCurrentIndex(index);
}
/**
* @brief MainWindow::on_profileBox_currentIndexChanged make sure we show the
* correct "profile"
* @param name
*/
void MainWindow::on_profileBox_currentIndexChanged(QString name) {
if (startupPhase || name == QtPassSettings::getProfile())
return;
QtPassSettings::setProfile(name);
QtPassSettings::setPassStore(QtPassSettings::getProfiles()[name]);
ui->statusBar->showMessage(tr("Profile changed to %1").arg(name), 2000);
QtPassSettings::getPass()->updateEnv();
ui->treeView->setRootIndex(proxyModel.mapFromSource(
model.setRootPath(QtPassSettings::getPassStore())));
}
/**
* @brief MainWindow::initTrayIcon show a nice tray icon on systems that
* support
* it
*/
void MainWindow::initTrayIcon() {
this->tray = new TrayIcon(this);
// Setup tray icon
if (tray == NULL)
dbg() << "Allocating tray icon failed.";
if (!tray->getIsAllocated()) {
destroyTrayIcon();
}
}
/**
* @brief MainWindow::destroyTrayIcon remove that pesky tray icon
*/
void MainWindow::destroyTrayIcon() {
delete this->tray;
tray = NULL;
}
/**
* @brief MainWindow::closeEvent hide or quit
* @param event
*/
void MainWindow::closeEvent(QCloseEvent *event) {
if (QtPassSettings::isHideOnClose()) {
this->hide();
event->ignore();
} else {
clearClipboard();
QtPassSettings::setGeometry(saveGeometry());
QtPassSettings::setSavestate(saveState());
QtPassSettings::setMaximized(isMaximized());
if (!isMaximized()) {
QtPassSettings::setPos(pos());
QtPassSettings::setSize(size());
}
// QtPassSettings::setSplitterLeft(ui->splitter->sizes()[0]);
// QtPassSettings::setSplitterRight(ui->splitter->sizes()[1]);
event->accept();
}
}
/**
* @brief MainWindow::eventFilter filter out some events and focus the
* treeview
* @param obj
* @param event
* @return
*/
bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
if (obj == ui->lineEdit && event->type() == QEvent::KeyPress) {
QKeyEvent *key = static_cast<QKeyEvent *>(event);
if (key->key() == Qt::Key_Down) {
ui->treeView->setFocus();
}
}
return QObject::eventFilter(obj, event);
}
/**
* @brief MainWindow::keyPressEvent did anyone press return, enter or escape?
* @param event
*/
void MainWindow::keyPressEvent(QKeyEvent *event) {
switch (event->key()) {
case Qt::Key_Delete:
onDelete();
break;
case Qt::Key_Return:
case Qt::Key_Enter:
on_treeView_clicked(ui->treeView->currentIndex());
break;
case Qt::Key_Escape:
ui->lineEdit->clear();
break;
default:
break;
}
}
/**
* @brief MainWindow::showContextMenu show us the (file or folder) context
* menu
* @param pos
*/
void MainWindow::showContextMenu(const QPoint &pos) {
QModelIndex index = ui->treeView->indexAt(pos);
bool selected = true;
if (!index.isValid()) {
ui->treeView->clearSelection();
ui->actionDelete->setEnabled(false);
ui->actionEdit->setEnabled(false);
currentDir = "";
selected = false;
}
ui->treeView->setCurrentIndex(index);
QPoint globalPos = ui->treeView->viewport()->mapToGlobal(pos);
QFileInfo fileOrFolder =
model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
QMenu contextMenu;
if (!selected || fileOrFolder.isDir()) {
QAction *openFolder =
contextMenu.addAction(tr("Open folder with file manager"));
QAction *addFolder = contextMenu.addAction(tr("Add folder"));
QAction *addPassword = contextMenu.addAction(tr("Add password"));
QAction *users = contextMenu.addAction(tr("Users"));
connect(openFolder, SIGNAL(triggered()), this, SLOT(openFolder()));
connect(addFolder, SIGNAL(triggered()), this, SLOT(addFolder()));
connect(addPassword, SIGNAL(triggered()), this, SLOT(addPassword()));
connect(users, SIGNAL(triggered()), this, SLOT(onUsers()));
} else if (fileOrFolder.isFile()) {
QAction *edit = contextMenu.addAction(tr("Edit"));
connect(edit, SIGNAL(triggered()), this, SLOT(onEdit()));
}
if (selected) {
// if (useClipboard != CLIPBOARD_NEVER) {
// contextMenu.addSeparator();
// QAction* copyItem = contextMenu.addAction(tr("Copy Password"));
// if (getClippedPassword().length() == 0) copyItem->setEnabled(false);
// connect(copyItem, SIGNAL(triggered()), this,
// SLOT(copyPasswordToClipboard()));
// }
contextMenu.addSeparator();
QAction *deleteItem = contextMenu.addAction(tr("Delete"));
connect(deleteItem, SIGNAL(triggered()), this, SLOT(onDelete()));
}
contextMenu.exec(globalPos);
}
/**
* @brief MainWindow::showBrowserContextMenu show us the context menu in
* password window
* @param pos
*/
void MainWindow::showBrowserContextMenu(const QPoint &pos) {
QMenu *contextMenu = ui->textBrowser->createStandardContextMenu(pos);
QPoint globalPos = ui->textBrowser->viewport()->mapToGlobal(pos);
contextMenu->exec(globalPos);
}
/**
* @brief MainWindow::openFolder open the folder in the default file manager
*/
void MainWindow::openFolder() {
QString dir =
Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
QString path = QDir::toNativeSeparators(dir);
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}
/**
* @brief MainWindow::addFolder add a new folder to store passwords in
*/
void MainWindow::addFolder() {
bool ok;
QString dir =
Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
QString newdir =
QInputDialog::getText(this, tr("New file"),
tr("New Folder: \n(Will be placed in %1 )")
.arg(QtPassSettings::getPassStore() +
Util::getDir(ui->treeView->currentIndex(),
true, model, proxyModel)),
QLineEdit::Normal, "", &ok);
if (!ok || newdir.isEmpty())
return;
newdir.prepend(dir);
// dbg()<< newdir;
QDir().mkdir(newdir);
}
/**
* @brief MainWindow::editPassword read password and open edit window via
* MainWindow::onEdit()
*/
void MainWindow::editPassword(const QString &file) {
if (!file.isEmpty()) {
if (QtPassSettings::isUseGit() && QtPassSettings::isAutoPull())
onUpdate(true);
setPassword(file, false);
}
}
-/**
- * @brief Mainwindow::generateOTP read OTP url and generate an OTP code
- * via pass otp, then copies the code to the clipboard.
- */
-void MainWindow::generateOtp(const QString &file) {
- if (!file.isEmpty()) {
- if (QtPassSettings::isUseOtp())
- QtPassSettings::getPass()->OtpShow(file);
- }
-}
-
/**
* @brief MainWindow::clearTemplateWidgets empty the template widget fields in
* the UI
*/
void MainWindow::clearTemplateWidgets() {
while (ui->gridLayout->count() > 0) {
QLayoutItem *item = ui->gridLayout->takeAt(0);
delete item->widget();
delete item;
}
ui->verticalLayoutPassword->setSpacing(0);
}
/**
* @brief MainWindow::copyTextToClipboard copies text to your clipboard
* @param text
*/
void MainWindow::copyTextToClipboard(const QString &text) {
QClipboard *clip = QApplication::clipboard();
if (!QtPassSettings::isUseSelection()) {
clip->setText(text, QClipboard::Clipboard);
} else {
clip->setText(text, QClipboard::Selection);
}
clippedText = text;
ui->statusBar->showMessage(tr("Copied to clipboard"), 2000);
if (QtPassSettings::isUseAutoclear()) {
clearClipboardTimer.start();
}
}
void MainWindow::copyPasswordFromTreeview() {
QFileInfo fileOrFolder =
model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
if (fileOrFolder.isFile()) {
QString file = getFile(ui->treeView->currentIndex(), true);
connect(QtPassSettings::getPass(), &Pass::finishedShow, this,
&MainWindow::passwordFromFileToClipboard);
QtPassSettings::getPass()->Show(file);
}
}
void MainWindow::passwordFromFileToClipboard(const QString &text) {
QStringList tokens = text.split('\n');
copyTextToClipboard(tokens[0]);
}
/**
* @brief MainWindow::addToGridLayout add a field to the template grid
* @param position
* @param field
* @param value
*/
void MainWindow::addToGridLayout(int position, const QString &field,
const QString &value) {
QString trimmedField = field.trimmed();
QString trimmedValue = value.trimmed();
// Combine the Copy button and the line edit in one widget
QFrame *frame = new QFrame();
QLayout *ly = new QHBoxLayout();
ly->setContentsMargins(5, 2, 2, 2);
frame->setLayout(ly);
if (QtPassSettings::getClipBoardType() != Enums::CLIPBOARD_NEVER) {
QPushButtonWithClipboard *fieldLabel =
new QPushButtonWithClipboard(trimmedValue, this);
connect(fieldLabel, SIGNAL(clicked(QString)), this,
SLOT(copyTextToClipboard(QString)));
fieldLabel->setStyleSheet("border-style: none ; background: transparent;");
// fieldLabel->setContentsMargins(0,5,5,0);
frame->layout()->addWidget(fieldLabel);
}
// set the echo mode to password, if the field is "password"
if (QtPassSettings::isHidePassword() && trimmedField == tr("Password")) {
QLineEdit *line = new QLineEdit();
line->setObjectName(trimmedField);
line->setText(trimmedValue);
line->setReadOnly(true);
line->setStyleSheet("border-style: none ; background: transparent;");
line->setContentsMargins(0, 0, 0, 0);
line->setEchoMode(QLineEdit::Password);
frame->layout()->addWidget(line);
} else {
QTextBrowser *line = new QTextBrowser();
line->setOpenExternalLinks(true);
line->setOpenLinks(true);
line->setMaximumHeight(26);
line->setMinimumHeight(26);
line->setSizePolicy(
QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
line->setObjectName(trimmedField);
trimmedValue.replace(
QRegExp("((?:https?|ftp|ssh|sftp|ftps|webdav|webdavs)://\\S+)"),
"<a href=\"\\1\">\\1</a>");
line->setText(trimmedValue);
line->setReadOnly(true);
line->setStyleSheet("border-style: none ; background: transparent;");
line->setContentsMargins(0, 0, 0, 0);
frame->layout()->addWidget(line);
}
frame->setStyleSheet(
".QFrame{border: 1px solid lightgrey; border-radius: 5px;}");
// set into the layout
ui->gridLayout->addWidget(new QLabel(trimmedField), position, 0);
ui->gridLayout->addWidget(frame, position, 1);
}
/**
* @brief Displays message in status bar
*
* @param msg text to be displayed
* @param timeout time for which msg shall be visible
*/
void MainWindow::showStatusMessage(QString msg, int timeout) {
ui->statusBar->showMessage(msg, timeout);
}
/**
* @brief MainWindow::startReencryptPath disable ui elements and treeview
*/
void MainWindow::startReencryptPath() {
enableUiElements(false);
ui->treeView->setDisabled(true);
}
/**
* @brief MainWindow::endReencryptPath re-enable ui elements
*/
void MainWindow::endReencryptPath() { enableUiElements(true); }
/**
* @brief MainWindow::critical critical message popup wrapper.
* @param title
* @param msg
*/
void MainWindow::critical(QString title, QString msg) {
QMessageBox::critical(this, title, msg);
}
void MainWindow::updateGitButtonVisibility() {
if (!QtPassSettings::isUseGit() ||
(QtPassSettings::getGitExecutable().isEmpty() &&
QtPassSettings::getPassExecutable().isEmpty())) {
enableGitButtons(false);
} else {
enableGitButtons(true);
}
}
void MainWindow::updateOtpButtonVisibility() {
- if(!QtPassSettings::isUseOtp())
+ #if defined(Q_OS_WIN ) || defined(__APPLE__)
+ ui->actionOtp->setVisible(false);
+ #endif
+ if (!QtPassSettings::isUseOtp())
ui->actionOtp->setEnabled(false);
else
ui->actionOtp->setEnabled(true);
}
void MainWindow::enableGitButtons(const bool &state) {
// Following GNOME guidelines is preferable disable buttons instead of hide
ui->actionPush->setEnabled(state);
ui->actionUpdate->setEnabled(state);
}
diff --git a/src/mainwindow.h b/src/mainwindow.h
index bceb1b0..ab8911b 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -1,149 +1,148 @@
#ifndef MAINWINDOW_H_
#define MAINWINDOW_H_
#include "storemodel.h"
#include <QFileSystemModel>
#include <QItemSelectionModel>
#include <QMainWindow>
#include <QProcess>
#include <QTimer>
#if SINGLE_APP
class SingleApplication;
#else
#define SingleApplication QApplication
#endif
#ifdef __APPLE__
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
// http://doc.qt.io/qt-5/qkeysequence.html#qt_set_sequence_auto_mnemonic
void qt_set_sequence_auto_mnemonic(bool b);
#endif
#endif
namespace Ui {
class MainWindow;
}
/*!
\class MainWindow
\brief The MainWindow class does way too much, not only is it a switchboard,
configuration handler and more, it's also the process-manager.
This class could really do with an overhaul.
*/
class Pass;
class TrayIcon;
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(const QString &searchText = QString(),
QWidget *parent = nullptr);
~MainWindow();
bool checkConfig();
QStringList getSecretKeys();
void generateKeyPair(QString, QDialog *);
void userDialog(QString = "");
void config();
void executePassGitInit();
protected:
void closeEvent(QCloseEvent *event);
void keyPressEvent(QKeyEvent *event);
void changeEvent(QEvent *event);
bool eventFilter(QObject *obj, QEvent *event);
public slots:
void deselect();
private slots:
void addPassword();
void addFolder();
void onEdit();
void onDelete();
void onOtp();
void onPush();
void onUpdate(bool block = false);
void onUsers();
void onConfig();
void on_treeView_clicked(const QModelIndex &index);
void on_treeView_doubleClicked(const QModelIndex &index);
void processFinished(const QString &, const QString &);
void processError(QProcess::ProcessError);
void clearClipboard();
void clearPanel(bool notify = true);
void on_lineEdit_textChanged(const QString &arg1);
void on_lineEdit_returnPressed();
void messageAvailable(QString message);
void on_profileBox_currentIndexChanged(QString);
void showContextMenu(const QPoint &pos);
void showBrowserContextMenu(const QPoint &pos);
void openFolder();
void editPassword(const QString &);
- void generateOtp(const QString &);
void focusInput();
void copyTextToClipboard(const QString &text);
void copyPasswordFromTreeview();
void passwordFromFileToClipboard(const QString &text);
void executeWrapperStarted();
void showStatusMessage(QString msg, int timeout);
void startReencryptPath();
void endReencryptPath();
void critical(QString, QString);
void passShowHandler(const QString &);
void passOtpHandler(const QString &);
void passStoreChanged(const QString &, const QString &);
void doGitPush();
void processErrorExit(int exitCode, const QString &);
void finishedInsert(const QString &, const QString &);
void keyGenerationComplete(const QString &p_output, const QString &p_errout);
private:
QScopedPointer<Ui::MainWindow> ui;
QFileSystemModel model;
StoreModel proxyModel;
QScopedPointer<QItemSelectionModel> selectionModel;
QProcess fusedav;
QString clippedText;
QTimer clearPanelTimer;
QTimer clearClipboardTimer;
bool freshStart;
QDialog *keygen;
QString currentDir;
bool startupPhase;
TrayIcon *tray;
void initToolBarButtons();
void initStatusBar();
void updateText();
void enableUiElements(bool state);
void restoreWindow();
void selectFirstFile();
QModelIndex firstFile(QModelIndex parentIndex);
QString getFile(const QModelIndex &, bool);
void setPassword(QString, bool isNew = true);
void mountWebDav();
void updateProfileBox();
void initTrayIcon();
void destroyTrayIcon();
void clearTemplateWidgets();
void reencryptPath(QString dir);
void addToGridLayout(int position, const QString &field,
const QString &value);
void DisplayInTextBrowser(QString toShow, QString prefix = QString(),
QString postfix = QString());
void connectPassSignalHandlers(Pass *pass);
void updateGitButtonVisibility();
void updateOtpButtonVisibility();
void enableGitButtons(const bool &);
};
#endif // MAINWINDOW_H_
diff --git a/src/pass.cpp b/src/pass.cpp
index 1654c37..d4d1e75 100644
--- a/src/pass.cpp
+++ b/src/pass.cpp
@@ -1,317 +1,317 @@
#include "pass.h"
#include "debughelper.h"
#include "qtpasssettings.h"
#include "util.h"
using namespace std;
using namespace Enums;
/**
* @brief Pass::Pass wrapper for using either pass or the pass imitation
*/
Pass::Pass() : wrapperRunning(false), env(QProcess::systemEnvironment()) {
connect(&exec,
static_cast<void (Executor::*)(int, int, const QString &,
const QString &)>(&Executor::finished),
this, &Pass::finished);
// TODO(bezet): stop using process
// connect(&process, SIGNAL(error(QProcess::ProcessError)), this,
// SIGNAL(error(QProcess::ProcessError)));
connect(&exec, &Executor::starting, this, &Pass::startingExecuteWrapper);
}
void Pass::executeWrapper(PROCESS id, const QString &app,
const QStringList &args, bool readStdout,
bool readStderr) {
executeWrapper(id, app, args, QString(), readStdout, readStderr);
}
void Pass::executeWrapper(PROCESS id, const QString &app,
const QStringList &args, QString input,
bool readStdout, bool readStderr) {
dbg() << app << args;
exec.execute(id, QtPassSettings::getPassStore(), app, args, input, readStdout,
readStderr);
}
void Pass::init() {
#ifdef __APPLE__
// If it exists, add the gpgtools to PATH
if (QFile("/usr/local/MacGPG2/bin").exists())
env.replaceInStrings("PATH=", "PATH=/usr/local/MacGPG2/bin:");
// Add missing /usr/local/bin
if (env.filter("/usr/local/bin").isEmpty())
env.replaceInStrings("PATH=", "PATH=/usr/local/bin:");
#endif
if (!QtPassSettings::getGpgHome().isEmpty()) {
QDir absHome(QtPassSettings::getGpgHome());
absHome.makeAbsolute();
env << "GNUPGHOME=" + absHome.path();
}
}
/**
* @brief Pass::Generate use either pwgen or internal password
* generator
* @param length of the desired password
* @param charset to use for generation
* @return the password
*/
QString Pass::Generate_b(unsigned int length, const QString &charset) {
QString passwd;
if (QtPassSettings::isUsePwgen()) {
// --secure goes first as it overrides --no-* otherwise
QStringList args;
args.append("-1");
if (QtPassSettings::isLessRandom())
args.append("--secure");
args.append(QtPassSettings::isAvoidCapitals() ? "--no-capitalize"
: "--capitalize");
args.append(QtPassSettings::isAvoidNumbers() ? "--no-numerals"
: "--numerals");
if (QtPassSettings::isUseSymbols())
args.append("--symbols");
args.append(QString::number(length));
QString p_out;
// TODO(bezet): try-catch here(2 statuses to merge o_O)
if (exec.executeBlocking(QtPassSettings::getPwgenExecutable(), args,
&passwd) == 0)
passwd.remove(QRegExp("[\\n\\r]"));
else {
passwd.clear();
qDebug() << __FILE__ << ":" << __LINE__ << "\t"
<< "pwgen fail";
// TODO(bezet): emit critical ?
}
} else {
if (charset.length() > 0) {
passwd = generateRandomPassword(charset, length);
} else {
emit critical(
tr("No characters chosen"),
tr("Can't generate password, there are no characters to choose from "
"set in the configuration!"));
}
}
return passwd;
}
/**
* @brief Pass::GenerateGPGKeys internal gpg keypair generator . .
* @param batch GnuPG style configuration string
*/
void Pass::GenerateGPGKeys(QString batch) {
executeWrapper(GPG_GENKEYS, QtPassSettings::getGpgExecutable(),
{"--gen-key", "--no-tty", "--batch"}, batch);
// TODO check status / error messages - probably not here, it's just started
// here, see finished for details
// https://github.com/IJHack/QtPass/issues/202#issuecomment-251081688
}
/**
* @brief Pass::listKeys list users
* @param keystring
* @param secret list private keys
* @return QList<UserInfo> users
*/
QList<UserInfo> Pass::listKeys(QString keystring, bool secret) {
QList<UserInfo> users;
QStringList args = {"--no-tty", "--with-colons"};
args.append(secret ? "--list-secret-keys" : "--list-keys");
if (!keystring.isEmpty())
args.append(keystring);
QString p_out;
if (exec.executeBlocking(QtPassSettings::getGpgExecutable(), args, &p_out) !=
0)
return users;
QStringList keys = p_out.split(QRegExp("[\r\n]"), QString::SkipEmptyParts);
UserInfo current_user;
foreach (QString key, keys) {
QStringList props = key.split(':');
if (props.size() < 10)
continue;
if (props[0] == (secret ? "sec" : "pub")) {
if (!current_user.key_id.isEmpty())
users.append(current_user);
current_user = UserInfo();
current_user.key_id = props[4];
current_user.name = props[9].toUtf8();
current_user.validity = props[1][0].toLatin1();
current_user.created.setTime_t(props[5].toUInt());
current_user.expiry.setTime_t(props[6].toUInt());
} else if (current_user.name.isEmpty() && props[0] == "uid") {
current_user.name = props[9];
}
}
if (!current_user.key_id.isEmpty())
users.append(current_user);
return users;
}
/**
* @brief Pass::processFinished reemits specific signal based on what process
* has finished
* @param id id of Pass process that was scheduled and finished
* @param exitCode return code of a process
* @param out output generated by process(if capturing was requested, empty
* otherwise)
* @param err error output generated by process(if capturing was requested,
* or error occured)
*/
void Pass::finished(int id, int exitCode, const QString &out,
const QString &err) {
PROCESS pid = static_cast<PROCESS>(id);
if (exitCode != 0) {
emit processErrorExit(exitCode, err);
return;
}
switch (pid) {
case GIT_INIT:
emit finishedGitInit(out, err);
break;
case GIT_PULL:
emit finishedGitPull(out, err);
break;
case GIT_PUSH:
emit finishedGitPush(out, err);
break;
case PASS_SHOW:
emit finishedShow(out);
break;
case PASS_OTP_SHOW:
- emit finishedOtpShow(out);
+ emit finishedOtpGenerate(out);
break;
case PASS_INSERT:
emit finishedInsert(out, err);
break;
case PASS_REMOVE:
emit finishedRemove(out, err);
break;
case PASS_INIT:
emit finishedInit(out, err);
break;
case PASS_MOVE:
emit finishedMove(out, err);
break;
case PASS_COPY:
emit finishedCopy(out, err);
break;
default:
dbg() << "Unhandled process type" << pid;
break;
}
}
/**
* @brief Pass::updateEnv update the execution environment (used when
* switching profiles)
*/
void Pass::updateEnv() {
QStringList store = env.filter("PASSWORD_STORE_DIR");
// put PASSWORD_STORE_DIR in env
if (store.isEmpty()) {
// dbg()<< "Added
// PASSWORD_STORE_DIR";
env.append("PASSWORD_STORE_DIR=" + QtPassSettings::getPassStore());
} else {
// dbg()<< "Update
// PASSWORD_STORE_DIR with " + passStore;
env.replaceInStrings(store.first(), "PASSWORD_STORE_DIR=" +
QtPassSettings::getPassStore());
}
exec.setEnvironment(env);
}
/**
* @brief Pass::getRecipientList return list of gpg-id's to encrypt for
* @param for_file which file (folder) would you like recepients for
* @return recepients gpg-id contents
*/
QStringList Pass::getRecipientList(QString for_file) {
QDir gpgIdPath(QFileInfo(for_file.startsWith(QtPassSettings::getPassStore())
? for_file
: QtPassSettings::getPassStore() + for_file)
.absoluteDir());
bool found = false;
while (gpgIdPath.exists() &&
gpgIdPath.absolutePath().startsWith(QtPassSettings::getPassStore())) {
if (QFile(gpgIdPath.absoluteFilePath(".gpg-id")).exists()) {
found = true;
break;
}
if (!gpgIdPath.cdUp())
break;
}
QFile gpgId(found ? gpgIdPath.absoluteFilePath(".gpg-id")
: QtPassSettings::getPassStore() + ".gpg-id");
if (!gpgId.open(QIODevice::ReadOnly | QIODevice::Text))
return QStringList();
QStringList recipients;
while (!gpgId.atEnd()) {
QString recipient(gpgId.readLine());
recipient = recipient.trimmed();
if (!recipient.isEmpty())
recipients += recipient;
}
return recipients;
}
/**
* @brief Pass::getRecipientString formated string for use with GPG
* @param for_file which file (folder) would you like recepients for
* @param separator formating separator eg: " -r "
* @param count
* @return recepient string
*/
QString Pass::getRecipientString(QString for_file, QString separator,
int *count) {
QString recipients_str;
QStringList recipients_list = Pass::getRecipientList(for_file);
if (count)
*count = recipients_list.size();
foreach (const QString recipient, recipients_list)
recipients_str += separator + '"' + recipient + '"';
return recipients_str;
}
/* Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
quint32 Pass::boundedRandom(quint32 bound) {
if (bound < 2) {
return 0;
}
quint32 randval;
const quint32 max_mod_bound = (1 + ~bound) % bound;
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
static int fd = -1;
if (fd == -1) {
assert((fd = open("/dev/urandom", O_RDONLY)) >= 0);
}
#endif
do {
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
assert(read(fd, &randval, sizeof(randval)) == sizeof(randval));
#else
randval = QRandomGenerator::system()->generate();
#endif
} while (randval < max_mod_bound);
return randval % bound;
}
QString Pass::generateRandomPassword(const QString &charset,
unsigned int length) {
QString out;
for (unsigned int i = 0; i < length; ++i) {
out.append(charset.at(static_cast<int>(
boundedRandom(static_cast<quint32>(charset.length())))));
}
return out;
}
diff --git a/src/pass.h b/src/pass.h
index 8ba2f1f..ed1b08c 100644
--- a/src/pass.h
+++ b/src/pass.h
@@ -1,101 +1,101 @@
#ifndef PASS_H
#define PASS_H
#include "enums.h"
#include "executor.h"
#include "userinfo.h"
#include <QProcess>
#include <QQueue>
#include <QString>
#include <cassert>
#include <map>
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
#include <QRandomGenerator>
#else
#include <fcntl.h>
#include <unistd.h>
#endif
/*!
\class Pass
\brief Acts as an abstraction for pass or pass imitation
*/
class Pass : public QObject {
Q_OBJECT
bool wrapperRunning;
QStringList env;
protected:
Executor exec;
typedef Enums::PROCESS PROCESS;
public:
Pass();
void init();
virtual ~Pass() {}
virtual void GitInit() = 0;
virtual void GitPull() = 0;
virtual void GitPull_b() = 0;
virtual void GitPush() = 0;
virtual void Show(QString file) = 0;
- virtual void OtpShow(QString file) = 0;
+ virtual void OtpGenerate(QString file) = 0;
virtual void Insert(QString file, QString value, bool force) = 0;
virtual void Remove(QString file, bool isDir) = 0;
virtual void Move(const QString srcDir, const QString dest,
const bool force = false) = 0;
virtual void Copy(const QString srcDir, const QString dest,
const bool force = false) = 0;
virtual void Init(QString path, const QList<UserInfo> &users) = 0;
virtual QString Generate_b(unsigned int length, const QString &charset);
void GenerateGPGKeys(QString batch);
QList<UserInfo> listKeys(QString keystring = "", bool secret = false);
void updateEnv();
static QStringList getRecipientList(QString for_file);
// TODO(bezet): getRecipientString is useless, refactor
static QString getRecipientString(QString for_file, QString separator = " ",
int *count = NULL);
protected:
void executeWrapper(PROCESS id, const QString &app, const QStringList &args,
bool readStdout = true, bool readStderr = true);
QString generateRandomPassword(const QString &charset, unsigned int length);
quint32 boundedRandom(quint32 bound);
virtual void executeWrapper(PROCESS id, const QString &app,
const QStringList &args, QString input,
bool readStdout = true, bool readStderr = true);
protected slots:
virtual void finished(int id, int exitCode, const QString &out,
const QString &err);
signals:
void error(QProcess::ProcessError);
void startingExecuteWrapper();
void statusMsg(QString, int);
void critical(QString, QString);
void processErrorExit(int exitCode, const QString &err);
void finishedAny(const QString &, const QString &);
void finishedGitInit(const QString &, const QString &);
void finishedGitPull(const QString &, const QString &);
void finishedGitPush(const QString &, const QString &);
void finishedShow(const QString &);
- void finishedOtpShow(const QString &);
+ void finishedOtpGenerate(const QString &);
void finishedInsert(const QString &, const QString &);
void finishedRemove(const QString &, const QString &);
void finishedInit(const QString &, const QString &);
void finishedMove(const QString &, const QString &);
void finishedCopy(const QString &, const QString &);
void finishedGenerate(const QString &, const QString &);
void finishedGenerateGPGKeys(const QString &, const QString &);
};
#endif // PASS_H
diff --git a/src/realpass.cpp b/src/realpass.cpp
index 55d0099..f03e602 100644
--- a/src/realpass.cpp
+++ b/src/realpass.cpp
@@ -1,178 +1,182 @@
#include "realpass.h"
#include "qtpasssettings.h"
#include <QDir>
#include <QFileInfo>
using namespace Enums;
RealPass::RealPass() {}
/**
* @brief RealPass::GitInit pass git init wrapper
*/
void RealPass::GitInit() { executePass(GIT_INIT, {"git", "init"}); }
/**
* @brief RealPass::GitInit pass git pull wrapper which blocks until process
* finishes
*/
void RealPass::GitPull_b() {
exec.executeBlocking(QtPassSettings::getPassExecutable(), {"git", "pull"});
}
/**
* @brief RealPass::GitPull pass git pull wrapper
*/
void RealPass::GitPull() { executePass(GIT_PULL, {"git", "pull"}); }
/**
* @brief RealPass::GitPush pass git push wrapper
*/
void RealPass::GitPush() { executePass(GIT_PUSH, {"git", "push"}); }
/**
* @brief RealPass::Show pass show
*
* @param file file to decrypt
*
* @return if block is set, returns exit status of internal decryption
* process
* otherwise returns QProcess::NormalExit
*/
void RealPass::Show(QString file) {
executePass(PASS_SHOW, {"show", file}, "", true);
}
-void RealPass::OtpShow(QString file) {
- executePass(PASS_OTP_SHOW, {"otp", "-c", file}, "", true);
+/**
+ * @brief RealPass::OtpGenerate pass otp
+ * @param file file containig OTP uri
+ */
+void RealPass::OtpGenerate(QString file) {
+ executePass(PASS_OTP_SHOW, {"otp", file}, "", true);
}
/**
* @brief RealPass::Insert pass insert
*/
void RealPass::Insert(QString file, QString newValue, bool overwrite) {
QStringList args = {"insert", "-m"};
if (overwrite)
args.append("-f");
args.append(file);
executePass(PASS_INSERT, args, newValue);
}
/**
* @brief RealPass::Remove pass remove wrapper
*/
void RealPass::Remove(QString file, bool isDir) {
executePass(PASS_REMOVE, {"rm", (isDir ? "-rf" : "-f"), file});
}
/**
* @brief RealPass::Init initialize pass repository
*
* @param path Absolute path to new password-store
* @param users list of users with ability to decrypt new password-store
*/
void RealPass::Init(QString path, const QList<UserInfo> &users) {
// remove the passStore directory otherwise,
// pass would create a passStore/passStore/dir
// but you want passStore/dir
QString dirWithoutPassdir =
path.remove(0, QtPassSettings::getPassStore().size());
QStringList args = {"init", "--path=" + dirWithoutPassdir};
foreach (const UserInfo &user, users) {
if (user.enabled)
args.append(user.key_id);
}
executePass(PASS_INIT, args);
}
/**
* @brief RealPass::Move move a file (or folder)
* @param src source file or folder
* @param dest destination file or folder
* @param force overwrite
*/
void RealPass::Move(const QString src, const QString dest, const bool force) {
QFileInfo srcFileInfo = QFileInfo(src);
QFileInfo destFileInfo = QFileInfo(dest);
// force mode?
// pass uses always the force mode, when call from eg. QT. so we have to check
// if this are to files
// and the user didnt want to move force
if (force == false && srcFileInfo.isFile() && destFileInfo.isFile()) {
return;
}
QString passSrc = QDir(QtPassSettings::getPassStore())
.relativeFilePath(QDir(src).absolutePath());
QString passDest = QDir(QtPassSettings::getPassStore())
.relativeFilePath(QDir(dest).absolutePath());
// remove the .gpg because pass will not work
if (srcFileInfo.isFile() && srcFileInfo.suffix() == "gpg") {
passSrc.replace(QRegExp("\\.gpg$"), "");
}
if (destFileInfo.isFile() && destFileInfo.suffix() == "gpg") {
passDest.replace(QRegExp("\\.gpg$"), "");
}
QStringList args;
args << "mv";
if (force) {
args << "-f";
}
args << passSrc;
args << passDest;
executePass(PASS_MOVE, args);
}
/**
* @brief RealPass::Copy copy a file (or folder)
* @param src source file or folder
* @param dest destination file or folder
* @param force overwrite
*/
void RealPass::Copy(const QString src, const QString dest, const bool force) {
QFileInfo srcFileInfo = QFileInfo(src);
QFileInfo destFileInfo = QFileInfo(dest);
// force mode?
// pass uses always the force mode, when call from eg. QT. so we have to check
// if this are to files
// and the user didnt want to move force
if (force == false && srcFileInfo.isFile() && destFileInfo.isFile()) {
return;
}
QString passSrc = QDir(QtPassSettings::getPassStore())
.relativeFilePath(QDir(src).absolutePath());
QString passDest = QDir(QtPassSettings::getPassStore())
.relativeFilePath(QDir(dest).absolutePath());
// remove the .gpg because pass will not work
if (srcFileInfo.isFile() && srcFileInfo.suffix() == "gpg") {
passSrc.replace(QRegExp("\\.gpg$"), "");
}
if (destFileInfo.isFile() && destFileInfo.suffix() == "gpg") {
passDest.replace(QRegExp("\\.gpg$"), "");
}
QStringList args;
args << "cp";
if (force) {
args << "-f";
}
args << passSrc;
args << passDest;
executePass(PASS_COPY, args);
}
/**
* @brief RealPass::executePass easy wrapper for running pass
* @param args
*/
void RealPass::executePass(PROCESS id, const QStringList &args, QString input,
bool readStdout, bool readStderr) {
executeWrapper(id, QtPassSettings::getPassExecutable(), args, input,
readStdout, readStderr);
}
diff --git a/src/realpass.h b/src/realpass.h
index 53d01ee..abf96f1 100644
--- a/src/realpass.h
+++ b/src/realpass.h
@@ -1,37 +1,37 @@
#ifndef REALPASS_H
#define REALPASS_H
#include "pass.h"
/*!
\class RealPass
\brief Wrapper for executing pass to handle the password-store
*/
class RealPass : public Pass {
void executePass(PROCESS id, const QStringList &arg,
QString input = QString(), bool readStdout = true,
bool readStderr = true);
public:
RealPass();
virtual ~RealPass() {}
virtual void GitInit() Q_DECL_OVERRIDE;
virtual void GitPull() Q_DECL_OVERRIDE;
virtual void GitPull_b() Q_DECL_OVERRIDE;
virtual void GitPush() Q_DECL_OVERRIDE;
virtual void Show(QString file) Q_DECL_OVERRIDE;
- virtual void OtpShow(QString file) Q_DECL_OVERRIDE;
+ virtual void OtpGenerate(QString file) Q_DECL_OVERRIDE;
virtual void Insert(QString file, QString value,
bool overwrite = false) Q_DECL_OVERRIDE;
virtual void Remove(QString file, bool isDir = false) Q_DECL_OVERRIDE;
virtual void Init(QString path, const QList<UserInfo> &users) Q_DECL_OVERRIDE;
// Pass interface
public:
void Move(const QString src, const QString dest,
const bool force = false) Q_DECL_OVERRIDE;
void Copy(const QString src, const QString dest,
const bool force = false) Q_DECL_OVERRIDE;
};
#endif // REALPASS_H
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Nov 6, 3:15 PM (1 d, 1 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
9b/ac/256556cc920d0a49bf880fd73df5
Attached To
rGPGPASS GnuPG Password Manager
Event Timeline
Log In to Comment