Page MenuHome GnuPG

No OneTemporary

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&amp;ve git/gpg</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButtonPass">
<property name="text">
<string>&amp;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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://www.passwordstore.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;www.passwordstore.org&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;a href=&quot;https://QtPass.org/&quot;&gt;QtPass&lt;/a&gt; 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("<"), "&lt;");
output.replace(QRegExp(">"), "&gt;");
output.replace(QRegExp(" "), "&nbsp;");
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("<"), "&lt;");
error.replace(QRegExp(">"), "&gt;");
error.replace(QRegExp(" "), "&nbsp;");
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

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

Event Timeline