Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F20064799
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
37 KB
Subscribers
None
View Options
diff --git a/mainwindow.cpp b/mainwindow.cpp
index 0900409..8d74c63 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -1,878 +1,893 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "dialog.h"
#include "usersdialog.h"
#include "util.h"
#include <QClipboard>
#include <QInputDialog>
#include <QMessageBox>
#include <QTimer>
#include <QFileInfo>
#include <QQueue>
#ifdef Q_OS_WIN
#include <windows.h>
#include <winnetwk.h>
#undef DELETE
#endif
/**
* @brief MainWindow::MainWindow
* @param parent
*/
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
process(new QProcess(this)),
fusedav(this)
{
// connect(process.data(), SIGNAL(readyReadStandardOutput()), this, SLOT(readyRead()));
connect(process.data(), SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
connect(process.data(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus)));
ui->setupUi(this);
enableUiElements(true);
wrapperRunning = false;
execQueue = new QQueue<execQueueItem>;
}
/**
* @brief MainWindow::~MainWindow
*/
MainWindow::~MainWindow()
{
#ifdef Q_OS_WIN
if (useWebDav) WNetCancelConnection2A(passStore.toUtf8().constData(), 0, 1);
#else
if (fusedav.state() == QProcess::Running) {
fusedav.terminate();
fusedav.waitForFinished(2000);
}
#endif
}
void MainWindow::normalizePassStore() {
if (!passStore.endsWith("/") && !passStore.endsWith(QDir::separator())) {
passStore += '/';
}
}
QSettings &MainWindow::getSettings() {
if (!settings) {
QString portable_ini = QCoreApplication::applicationDirPath() + "/qtpass.ini";
if (QFile(portable_ini).exists()) {
settings.reset(new QSettings(portable_ini, QSettings::IniFormat));
} else {
settings.reset(new QSettings("IJHack", "QtPass"));
}
}
return *settings;
}
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 = webDavUrl.toUtf8().data();
DWORD size = sizeof(dst);
DWORD r = WNetUseConnectionA(reinterpret_cast<HWND>(effectiveWinId()), &netres, webDavPassword.toUtf8().constData(),
webDavUser.toUtf8().constData(), CONNECT_TEMPORARY | CONNECT_INTERACTIVE | CONNECT_REDIRECT,
dst, &size, 0);
if (r == NO_ERROR) {
passStore = 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) + ")");
}
#else
fusedav.start("fusedav -o nonempty -u \"" + webDavUser + "\" " + webDavUrl + " \"" + passStore + '"');
fusedav.waitForStarted();
if (fusedav.state() == QProcess::Running) {
QString pwd = webDavPassword;
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);
}
#endif
}
/**
* @brief MainWindow::checkConfig
*/
void MainWindow::checkConfig() {
QSettings &settings(getSettings());
usePass = (settings.value("usePass") == "true");
useClipboard = (settings.value("useClipboard") == "true");
useAutoclear = (settings.value("useAutoclear") == "true");
autoclearSeconds = settings.value("autoclearSeconds").toInt();
hidePassword = (settings.value("hidePassword") == "true");
hideContent = (settings.value("hideContent") == "true");
addGPGId = (settings.value("addGPGId") != "false");
passStore = settings.value("passStore").toString();
if (passStore == "") {
passStore = Util::findPasswordStore();
settings.setValue("passStore", passStore);
}
normalizePassStore();
passExecutable = settings.value("passExecutable").toString();
if (passExecutable == "") {
passExecutable = Util::findBinaryInPath("pass");
}
gitExecutable = settings.value("gitExecutable").toString();
if (gitExecutable == "") {
gitExecutable = Util::findBinaryInPath("git");
}
gpgExecutable = settings.value("gpgExecutable").toString();
if (gpgExecutable == "") {
gpgExecutable = Util::findBinaryInPath("gpg2");
}
gpgHome = settings.value("gpgHome").toString();
useWebDav = (settings.value("useWebDav") == "true");
webDavUrl = settings.value("webDavUrl").toString();
webDavUser = settings.value("webDavUser").toString();
webDavPassword = settings.value("webDavPassword").toString();
if (passExecutable == "" && (gitExecutable == "" || gpgExecutable == "")) {
config();
}
// TODO: 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 (useWebDav) {
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->textBrowser->setOpenExternalLinks(true);
env = QProcess::systemEnvironment();
if (!gpgHome.isEmpty()) {
QDir absHome(gpgHome);
absHome.makeAbsolute();
env << "GNUPGHOME=" + absHome.path();
}
#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
//QMessageBox::information(this, "env", env.join("\n"));
ui->lineEdit->setFocus();
}
/**
* @brief MainWindow::config
*/
void MainWindow::config() {
QScopedPointer<Dialog> d(new Dialog());
d->setModal(true);
d->setPassPath(passExecutable);
d->setGitPath(gitExecutable);
d->setGpgPath(gpgExecutable);
d->setStorePath(passStore);
d->usePass(usePass);
d->useClipboard(useClipboard);
d->useAutoclear(useAutoclear);
d->setAutoclear(autoclearSeconds);
d->hidePassword(hidePassword);
d->hideContent(hideContent);
d->addGPGId(addGPGId);
if (d->exec()) {
if (d->result() == QDialog::Accepted) {
passExecutable = d->getPassPath();
gitExecutable = d->getGitPath();
gpgExecutable = d->getGpgPath();
passStore = d->getStorePath();
normalizePassStore();
usePass = d->usePass();
useClipboard = d->useClipboard();
useAutoclear = d->useAutoclear();
autoclearSeconds = d->getAutoclear();
hidePassword = d->hidePassword();
hideContent = d->hideContent();
addGPGId = d->addGPGId();
QSettings &settings(getSettings());
settings.setValue("passExecutable", passExecutable);
settings.setValue("gitExecutable", gitExecutable);
settings.setValue("gpgExecutable", gpgExecutable);
settings.setValue("passStore", passStore);
settings.setValue("usePass", usePass ? "true" : "false");
settings.setValue("useClipboard", useClipboard ? "true" : "false");
settings.setValue("useAutoclear", useAutoclear ? "true" : "false");
settings.setValue("autoclearSeconds", autoclearSeconds);
settings.setValue("hidePassword", hidePassword ? "true" : "false");
settings.setValue("hideContent", hideContent ? "true" : "false");
settings.setValue("addGPGId", addGPGId ? "true" : "false");
ui->treeView->setRootIndex(model.setRootPath(passStore));
}
}
}
/**
* @brief MainWindow::on_updateButton_clicked
*/
void MainWindow::on_updateButton_clicked()
{
ui->statusBar->showMessage(tr("Updating password-store"), 2000);
currentAction = GIT;
if (usePass) {
executePass("git pull");
} else {
executeWrapper(gitExecutable, "pull");
}
}
/**
* @brief MainWindow::on_pushButton_clicked
*/
void MainWindow::on_pushButton_clicked()
{
ui->statusBar->showMessage(tr("Updating password-store"), 2000);
currentAction = GIT;
if (usePass) {
executePass("git push");
} else {
executeWrapper(gitExecutable, "push");
}
}
QString MainWindow::getDir(const QModelIndex &index, bool forPass)
{
if (!index.isValid()) {
return forPass ? "" : passStore;
}
QFileInfo info = model.fileInfo(proxyModel.mapToSource(index));
QString filePath = (info.isFile() ? info.absolutePath() : info.absoluteFilePath()) + '/';
if (forPass) {
filePath.replace(QRegExp("^" + passStore), "");
}
return filePath;
}
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.replace(QRegExp("\\.gpg$"), "");
filePath.replace(QRegExp("^" + passStore), "");
}
return filePath;
}
/**
* @brief MainWindow::on_treeView_clicked
* @param index
*/
void MainWindow::on_treeView_clicked(const QModelIndex &index)
{
lastDecrypt = "Could not decrypt";
QString file = getFile(index, usePass);
if (!file.isEmpty()){
currentAction = GPG;
if (usePass) {
executePass('"' + file + '"');
} else {
executeWrapper(gpgExecutable , "-d --quiet --yes --no-encrypt-to --batch --use-agent \"" + file + '"');
}
}
}
/**
* @brief MainWindow::executePass
* @param args
*/
void MainWindow::executePass(QString args, QString input) {
executeWrapper(passExecutable, args, input);
}
/**
* @brief MainWindow::executeWrapper
* @param app
* @param args
*/
void MainWindow::executeWrapper(QString app, QString args, QString input) {
if (wrapperRunning) {
execQueueItem item;
item.app = app;
item.args = args;
item.input = input;
execQueue->enqueue(item);
//qDebug() << item.app + "," + item.args + "," + item.input;
return;
}
wrapperRunning = true;
process->setWorkingDirectory(passStore);
process->setEnvironment(env);
ui->textBrowser->clear();
ui->textBrowser->setTextColor(Qt::black);
enableUiElements(false);
process->start('"' + app + "\" " + args);
if (!input.isEmpty()) {
process->write(input.toUtf8());
}
process->closeWriteChannel();
}
/**
* @brief MainWindow::readyRead
*/
void MainWindow::readyRead(bool finished = false) {
QString output = "";
QString error = process->readAllStandardError();
if (currentAction != GPG_INTERNAL) {
output = process->readAllStandardOutput();
if (finished && currentAction == GPG) {
lastDecrypt = output;
if (useClipboard && error.size() == 0) {
QClipboard *clip = QApplication::clipboard();
QStringList tokens = output.split("\n");
clip->setText(tokens[0]);
ui->statusBar->showMessage(tr("Password copied to clipboard"), 3000);
if (useAutoclear) {
clippedPass = tokens[0];
QTimer::singleShot(1000*autoclearSeconds, this, SLOT(clearClipboard()));
}
if (hidePassword) {
tokens.pop_front();
output = tokens.join("\n");
}
if (hideContent) {
output = "<font color=\"blue\">" + tr("Content hidden") + "</font><br />";
}
}
}
output.replace(QRegExp("<"), "<");
output.replace(QRegExp(">"), ">");
}
if (error.size() > 0) {
output = "<font color=\"red\">" + error + "</font><br />" + output;
}
output.replace(QRegExp("((http|https|ftp)\\://[a-zA-Z0-9\\-\\.]+\\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\\-\\._\\?\\,\\'/\\\\+&%\\$#\\=~])*)"), "<a href=\"\\1\">\\1</a>");
output.replace(QRegExp("\n"), "<br />");
if (ui->textBrowser->toPlainText() != "") {
output = ui->textBrowser->toHtml() + output;
}
ui->textBrowser->setHtml(output);
}
/**
* @brief MainWindow::clearClipboard
* @TODO check clipboard content (only clear if contains the password)
*/
void MainWindow::clearClipboard()
{
QClipboard *clipboard = QApplication::clipboard();
if (clipboard->text() == clippedPass) {
clipboard->clear();
clippedPass = "";
ui->statusBar->showMessage(tr("Clipboard cleared"), 3000);
} else {
ui->statusBar->showMessage(tr("Clipboard not cleared"), 3000);
}
}
/**
* @brief MainWindow::processFinished
* @param exitCode
* @param exitStatus
*/
void MainWindow::processFinished(int exitCode, QProcess::ExitStatus exitStatus) {
wrapperRunning = false;
bool error = exitStatus != QProcess::NormalExit || exitCode > 0;
if (error) {
ui->textBrowser->setTextColor(Qt::red);
}
readyRead(true);
enableUiElements(true);
if (!error && currentAction == EDIT) {
on_treeView_clicked(ui->treeView->currentIndex());
}
if (!execQueue->isEmpty()) {
execQueueItem item = execQueue->dequeue();
executeWrapper(item.app, item.args, item.input);
}
}
/**
* @brief MainWindow::enableUiElements
* @param state
*/
void MainWindow::enableUiElements(bool state) {
ui->updateButton->setEnabled(state);
ui->treeView->setEnabled(state);
ui->lineEdit->setEnabled(state);
ui->addButton->setEnabled(state);
ui->usersButton->setEnabled(state);
state &= ui->treeView->currentIndex().isValid();
ui->deleteButton->setEnabled(state);
ui->editButton->setEnabled(state);
ui->pushButton->setEnabled(state);
}
/**
* @brief MainWindow::processError
* @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);
if (process->state() == QProcess::NotRunning)
enableUiElements(true);
}
/**
* @brief MainWindow::setPassExecutable
* @param path
*/
void MainWindow::setPassExecutable(QString path) {
passExecutable = path;
}
/**
* @brief MainWindow::setGitExecutable
* @param path
*/
void MainWindow::setGitExecutable(QString path) {
gitExecutable = path;
}
/**
* @brief MainWindow::setGpgExecutable
* @param path
*/
void MainWindow::setGpgExecutable(QString path) {
gpgExecutable = path;
}
/**
* @brief MainWindow::on_configButton_clicked
*/
void MainWindow::on_configButton_clicked()
{
config();
}
/**
* @brief MainWindow::on_lineEdit_textChanged
* @param arg1
*/
void MainWindow::on_lineEdit_textChanged(const QString &arg1)
{
ui->treeView->expandAll();
ui->statusBar->showMessage(tr("Looking for: ") + arg1, 1000);
QString query = arg1;
query.replace(QRegExp(" "), ".*");
QRegExp regExp(query, Qt::CaseInsensitive);
proxyModel.setFilterRegExp(regExp);
ui->treeView->setRootIndex(proxyModel.mapFromSource(model.setRootPath(passStore)));
selectFirstFile();
}
/**
* @brief MainWindow::on_lineEdit_returnPressed
*/
void MainWindow::on_lineEdit_returnPressed()
{
selectFirstFile();
on_treeView_clicked(ui->treeView->currentIndex());
}
/**
* @brief MainWindow::selectFirstFile
*/
void MainWindow::selectFirstFile()
{
QModelIndex index = proxyModel.mapFromSource(model.setRootPath(passStore));
index = firstFile(index);
ui->treeView->setCurrentIndex(index);
}
/**
* @brief MainWindow::firstFile
* @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::on_clearButton_clicked
*/
void MainWindow::on_clearButton_clicked()
{
ui->lineEdit->clear();
}
QStringList MainWindow::getRecipientList(QString for_file)
{
QDir gpgIdPath(QFileInfo(for_file.startsWith(passStore) ? for_file : passStore + for_file).absoluteDir());
bool found = false;
while (gpgIdPath.exists() && gpgIdPath.absolutePath().startsWith(passStore))
{
if (QFile(gpgIdPath.absoluteFilePath(".gpg-id")).exists()) {
found = true;
break;
}
if (!gpgIdPath.cdUp()) {
break;
}
}
QFile gpgId(found ? gpgIdPath.absoluteFilePath(".gpg-id") : passStore + ".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;
}
QString MainWindow::getRecipientString(QString for_file, QString separator, int *count)
{
QString recipients_str;
QStringList recipients_list = getRecipientList(for_file);
if (count)
{
*count = recipients_list.size();
}
foreach (const QString recipient, recipients_list)
{
recipients_str += separator + '"' + recipient + '"';
}
return recipients_str;
}
void MainWindow::setPassword(QString file, bool overwrite)
{
bool ok;
#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
QString newValue = QInputDialog::getMultiLineText(this, tr("New Value"),
tr("New password value:"),
lastDecrypt, &ok);
#else
QString newValue = QInputDialog::getText(this, tr("New Value"),
tr("New password value:"), QLineEdit::Normal,
lastDecrypt, &ok);
#endif
if (!ok || newValue.isEmpty()) {
return;
}
currentAction = EDIT;
if (usePass) {
QString force(overwrite ? " -f " : " ");
executePass("insert" + force + "-m \"" + file + '"', newValue);
} else {
QString recipients = getRecipientString(file, " -r ");
if (recipients.isEmpty()) {
QMessageBox::critical(this, tr("Can not edit"),
tr("Could not read encryption key to use, .gpg-id file missing or invalid."));
return;
}
QString force(overwrite ? " --yes " : " ");
executeWrapper(gpgExecutable , force + "--batch -eq --output \"" + file + "\" " + recipients + " -", newValue);
if (!useWebDav) {
if (!overwrite) {
executeWrapper(gitExecutable, "add \"" + file + '"');
}
QString path = file;
path.replace(QRegExp("\\.gpg$"), "");
path.replace(QRegExp("^" + passStore), "");
executeWrapper(gitExecutable, "commit \"" + file + "\" -m \"" + (overwrite ? "Edit" : "Add") + " for " + path + " using QtPass\"");
}
}
}
void MainWindow::on_addButton_clicked()
{
bool ok;
QString file = QInputDialog::getText(this, tr("New file"),
tr("New password file:"), QLineEdit::Normal,
"", &ok);
if (!ok || file.isEmpty()) {
return;
}
file = getDir(ui->treeView->currentIndex(), usePass) + file;
if (!usePass) {
file += ".gpg";
}
lastDecrypt = "";
setPassword(file, false);
}
void MainWindow::on_deleteButton_clicked()
{
QString file = getFile(ui->treeView->currentIndex(), usePass);
if (QMessageBox::question(this, tr("Delete password?"),
tr("Are you sure you want to delete %1?").arg(file),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
return;
}
currentAction = DELETE;
if (usePass) {
executePass("rm -f \"" + file + '"');
} else {
QFile(file).remove();
}
}
void MainWindow::on_editButton_clicked()
{
QString file = getFile(ui->treeView->currentIndex(), usePass);
if (file.isEmpty()) {
QMessageBox::critical(this, tr("Can not edit"),
tr("Selected password file does not exist, not able to edit"));
return;
}
setPassword(file, true);
}
-QList<UserInfo> MainWindow::listKeys(QString keystring)
+QList<UserInfo> MainWindow::listKeys(QString keystring, bool secret)
{
QList<UserInfo> users;
currentAction = GPG_INTERNAL;
- executeWrapper(gpgExecutable , "--no-tty --with-colons --list-keys " + keystring);
+ QString listopt = secret ? "--list-secret-keys " : "--list-keys ";
+ executeWrapper(gpgExecutable , "--no-tty --with-colons " + listopt + keystring);
process->waitForFinished(2000);
if (process->exitStatus() != QProcess::NormalExit) {
return users;
}
QStringList keys = QString(process->readAllStandardOutput()).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] == "pub") {
+ 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];
+ current_user.validity = props[8][0].toLatin1();
} 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;
}
void MainWindow::on_usersButton_clicked()
{
QList<UserInfo> users = 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 = 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 = getDir(ui->treeView->currentIndex(), false);
int count = 0;
QString recipients = getRecipientString(dir.isEmpty() ? "" : dir, " ", &count);
if (!recipients.isEmpty()) {
selected_users = 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 = getRecipientList(dir.isEmpty() ? "" : dir);
foreach (const QString recipient, recipients)
{
if (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);
QString gpgIdFile = dir + ".gpg-id";
QFile gpgId(gpgIdFile);
bool addFile = false;
if (addGPGId) {
QFileInfo checkFile(gpgIdFile);
if (!checkFile.exists() || !checkFile.isFile()) {
addFile = true;
}
}
if (!gpgId.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(this, 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) {
+ QMessageBox::critical(this, 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!"));
+ }
if (!useWebDav){
if (addFile) {
executeWrapper(gitExecutable, "add \"" + gpgIdFile + '"');
}
QString path = gpgIdFile;
path.replace(QRegExp("\\.gpg$"), "");
executeWrapper(gitExecutable, "commit \"" + gpgIdFile + "\" -m \"Added "+ path + " using QtPass\"");
}
}
/**
* @brief MainWindow::setApp
* @param app
*/
void MainWindow::setApp(SingleApplication *app)
{
#if SINGLE_APP
connect(app, SIGNAL(messageAvailable(QString)), this, SLOT(messageAvailable(QString)));
#endif
}
/**
* @brief MainWindow::messageAvailable
* @param message
*/
void MainWindow::messageAvailable(QString message)
{
if (message == "") {
ui->lineEdit->selectAll();
ui->lineEdit->setFocus();
} else {
ui->treeView->expandAll();
ui->lineEdit->setText(message);
on_lineEdit_returnPressed();
}
show();
raise();
}
/**
* @brief MainWindow::setText
* @param message
*/
void MainWindow::setText(QString text)
{
ui->lineEdit->setText(text);
}
diff --git a/mainwindow.h b/mainwindow.h
index 9d24731..ece97da 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -1,110 +1,110 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTreeView>
#include <QFileSystemModel>
#include <QProcess>
#include <QSettings>
#include "storemodel.h"
#if SINGLE_APP
#include "singleapplication.h"
#else
#define SingleApplication QApplication
#endif
namespace Ui {
class MainWindow;
}
struct execQueueItem {
QString app;
QString args;
QString input;
};
struct UserInfo;
class MainWindow : public QMainWindow
{
Q_OBJECT
enum actionType { GPG, GIT, EDIT, DELETE, GPG_INTERNAL };
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void setPassExecutable(QString);
void setGitExecutable(QString);
void setGpgExecutable(QString);
void checkConfig();
void setApp(SingleApplication* app);
void setText(QString);
private slots:
void on_updateButton_clicked();
void on_pushButton_clicked();
void on_treeView_clicked(const QModelIndex &index);
void on_configButton_clicked();
void readyRead(bool finished);
void processFinished(int, QProcess::ExitStatus);
void processError(QProcess::ProcessError);
void clearClipboard();
void on_lineEdit_textChanged(const QString &arg1);
void on_lineEdit_returnPressed();
void on_clearButton_clicked();
void on_addButton_clicked();
void on_deleteButton_clicked();
void on_editButton_clicked();
void on_usersButton_clicked();
void messageAvailable(QString message);
private:
QScopedPointer<QSettings> settings;
QScopedPointer<Ui::MainWindow> ui;
QFileSystemModel model;
StoreModel proxyModel;
QScopedPointer<QItemSelectionModel> selectionModel;
QScopedPointer<QProcess> process;
bool usePass;
bool useClipboard;
bool useAutoclear;
bool hidePassword;
bool hideContent;
bool addGPGId;
int autoclearSeconds;
QString passStore;
QString passExecutable;
QString gitExecutable;
QString gpgExecutable;
QString gpgHome;
bool useWebDav;
QString webDavUrl;
QString webDavUser;
QString webDavPassword;
QProcess fusedav;
QString clippedPass;
actionType currentAction;
QString lastDecrypt;
bool wrapperRunning;
QStringList env;
QQueue<execQueueItem> *execQueue;
void updateText();
void executePass(QString, QString = QString());
void executeWrapper(QString, QString, QString = QString());
void config();
void enableUiElements(bool);
void selectFirstFile();
QModelIndex firstFile(QModelIndex parentIndex);
QString getDir(const QModelIndex &, bool);
QString getFile(const QModelIndex &, bool);
void setPassword(QString, bool);
void normalizePassStore();
QSettings &getSettings();
- QList<UserInfo> listKeys(QString keystring = "");
+ QList<UserInfo> listKeys(QString keystring = "", bool secret = false);
QStringList getRecipientList(QString for_file);
QString getRecipientString(QString for_file, QString separator = " ", int *count = NULL);
void mountWebDav();
};
#endif // MAINWINDOW_H
diff --git a/usersdialog.cpp b/usersdialog.cpp
index bfeae47..64e69d1 100644
--- a/usersdialog.cpp
+++ b/usersdialog.cpp
@@ -1,64 +1,69 @@
#include "usersdialog.h"
#include "ui_usersdialog.h"
#include <QRegExp>
UsersDialog::UsersDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::UsersDialog)
{
ui->setupUi(this);
connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(ui->listWidget, SIGNAL(itemChanged(QListWidgetItem *)), this, SLOT(itemChange(QListWidgetItem *)));
}
UsersDialog::~UsersDialog()
{
delete ui;
}
Q_DECLARE_METATYPE(UserInfo *)
void UsersDialog::itemChange(QListWidgetItem *item)
{
if (!item) return;
UserInfo *info = item->data(Qt::UserRole).value<UserInfo *>();
if (!info) return;
info->enabled = item->checkState() == Qt::Checked;
}
void UsersDialog::setUsers(QList<UserInfo> *users)
{
userList = users;
populateList("");
}
void UsersDialog::populateList(const QString &filter)
{
QRegExp nameFilter("*"+filter+"*");
nameFilter.setPatternSyntax(QRegExp::Wildcard);
nameFilter.setCaseSensitivity(Qt::CaseInsensitive);
ui->listWidget->clear();
if (userList) {
for (QList<UserInfo>::iterator it = userList->begin(); it != userList->end(); ++it) {
UserInfo &user(*it);
if (filter.isEmpty() || nameFilter.exactMatch(user.name)) {
QListWidgetItem *item = new QListWidgetItem(user.name + "\n" + user.key_id, ui->listWidget);
item->setCheckState(user.enabled ? Qt::Checked : Qt::Unchecked);
item->setData(Qt::UserRole, QVariant::fromValue(&user));
+ if (user.have_secret) {
+ item->setForeground(Qt::blue);
+ } else if (user.validity == '-') {
+ item->setBackground(Qt::red);
+ }
ui->listWidget->addItem(item);
}
}
}
}
void UsersDialog::on_clearButton_clicked()
{
ui->lineEdit->clear();
on_lineEdit_textChanged("");
}
void UsersDialog::on_lineEdit_textChanged(const QString &filter)
{
populateList(filter);
}
diff --git a/usersdialog.h b/usersdialog.h
index c8a6970..b9bf5e6 100644
--- a/usersdialog.h
+++ b/usersdialog.h
@@ -1,42 +1,44 @@
#ifndef USERSDIALOG_H
#define USERSDIALOG_H
//#include <QAbstractListModel>
#include <QDialog>
#include <QList>
#include <QStandardItemModel>
namespace Ui {
class UsersDialog;
}
class QListWidgetItem;
struct UserInfo {
- UserInfo() : enabled(false) {}
+ UserInfo() : validity('-'), have_secret(false), enabled(false) {}
QString name;
QString key_id;
+ char validity;
+ bool have_secret;
bool enabled;
};
class UsersDialog : public QDialog
{
Q_OBJECT
public:
explicit UsersDialog(QWidget *parent = 0);
~UsersDialog();
void setUsers(QList<UserInfo> *);
private slots:
void itemChange(QListWidgetItem *);
void on_clearButton_clicked();
void on_lineEdit_textChanged(const QString &filter);
private:
Ui::UsersDialog *ui;
QList<UserInfo> *userList;
void populateList(const QString &filter);
};
#endif // USERSDIALOG_H
diff --git a/usersdialog.ui b/usersdialog.ui
index 28adab0..4529a84 100644
--- a/usersdialog.ui
+++ b/usersdialog.ui
@@ -1,78 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UsersDialog</class>
<widget class="QDialog" name="UsersDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>599</width>
<height>583</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Read access users</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Select which users should be able to decrypt passwords stored in this folder.
-Note: Existing files will not be modified and retain the old permissions until you edit them.</string>
+Note: Existing files will not be modified and retain the old permissions until you edit them.
+Blue entries have a secret key available, select one of these to be able to decrypt.
+Red entries are not valid, you will not be able to encrypt to these.</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item>
<widget class="QToolButton" name="clearButton">
<property name="text">
<string>X</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="listWidget">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</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>
</widget>
<resources/>
<connections/>
</ui>
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Feb 23, 7:36 PM (1 d, 9 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
41/36/f7fba38c153c571d5a0a046089bb
Attached To
rGPGPASS GnuPG Password Manager
Event Timeline
Log In to Comment