Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F23558452
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
24 KB
Subscribers
None
View Options
diff --git a/main.cpp b/main.cpp
index 009264d..33e06ac 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,44 +1,48 @@
#include "mainwindow.h"
#include <QApplication>
#include <QTranslator>
int main(int argc, char *argv[])
{
+#if SINGLE_APP
SingleApplication app(argc, argv, "ijhackQtPass");
if (app.isRunning()) {
if (argc == 1 ) {
app.sendMessage("show");
} else if (argc >= 2) {
QString text = "";
for (int i = 1; i < argc; ++i) {
text += argv[i];
if (argc >= (i - 2)) {
text += " ";
}
app.sendMessage(text);
}
}
return 0;
}
+#else
+ QApplication app(argc, argv);
+#endif
QCoreApplication::setOrganizationName("IJHack");
QCoreApplication::setOrganizationDomain("ijhack.org");
QCoreApplication::setApplicationName("QtPass");
QCoreApplication::setApplicationVersion("0.1.0");
//Setup and load translator for localization
QTranslator translator;
QString locale = QLocale::system().name();
translator.load(QString(":localization/localization_") + locale + QString(".qm"));
app.installTranslator(&translator);
MainWindow w;
app.setActiveWindow(&w);
app.setWindowIcon(QIcon(":artwork/icon.png"));
w.setApp(&app);
w.checkConfig();
w.show();
return app.exec();
}
diff --git a/mainwindow.cpp b/mainwindow.cpp
index 4400383..3e277fc 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -1,610 +1,612 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "dialog.h"
#include "util.h"
#include <QClipboard>
#include <QInputDialog>
#include <QMessageBox>
#include <QTimer>
/**
* @brief MainWindow::MainWindow
* @param parent
*/
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
process(new QProcess(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);
}
/**
* @brief MainWindow::~MainWindow
*/
MainWindow::~MainWindow()
{
}
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;
}
/**
* @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");
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("gpg");
}
gpgHome = settings.value("gpgHome").toString();
if (passExecutable == "" && (gitExecutable == "" || gpgExecutable == "")) {
config();
}
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);
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);
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();
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");
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 , "--no-tty --use-agent -dq \"" + 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) {
process->setWorkingDirectory(passStore);
if (!gpgHome.isEmpty()) {
QStringList env = QProcess::systemEnvironment();
QDir absHome(gpgHome);
absHome.makeAbsolute();
env << "GNUPGHOME=" + absHome.path();
process->setEnvironment(env);
}
process->start('"' + app + "\" " + args);
ui->textBrowser->clear();
ui->textBrowser->setTextColor(Qt::black);
enableUiElements(false);
if (!input.isEmpty()) {
process->write(input.toUtf8());
}
process->closeWriteChannel();
}
/**
* @brief MainWindow::readyRead
*/
void MainWindow::readyRead(bool finished = false) {
QString output = process->readAllStandardOutput();
if (finished && currentAction == GPG) {
lastDecrypt = output;
if (useClipboard) {
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 = tr("Content hidden");
}
}
}
output.replace(QRegExp("<"), "<");
output.replace(QRegExp(">"), ">");
QString error = process->readAllStandardError();
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 />");
ui->textBrowser->setHtml(ui->textBrowser->toHtml() + 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) {
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());
}
}
/**
* @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);
state &= ui->treeView->currentIndex().isValid();
ui->deleteButton->setEnabled(state);
ui->editButton->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();
}
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 {
QDir gpgIdPath(QFileInfo(file.startsWith(passStore) ? file : passStore + 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)) {
QMessageBox::critical(this, tr("Can not edit"),
tr("Password store lacks .gpg-id specifying encryption key"));
return;
}
QString recipients;
while (!gpgId.atEnd()) {
QString recipient(gpgId.readLine());
recipient = recipient.trimmed();
if (!recipient.isEmpty()) {
recipients += " -r \"" + recipient + '"';
}
}
if (recipients.isEmpty()) {
QMessageBox::critical(this, tr("Can not edit"),
tr("Could not read encryption key to use"));
return;
}
QString force(overwrite ? " --yes " : " ");
executeWrapper(gpgExecutable , force + "--batch -eq --output \"" + file + "\" " + recipients + " -", newValue);
}
}
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);
executeWrapper(gitExecutable, "add " + file);
// executeWrapper(gitExecutable, "commit -a -m \"Adding " + file + "\"");
}
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);
}
/**
* @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 == "show") {
ui->lineEdit->selectAll();
ui->lineEdit->setFocus();
} else {
ui->treeView->expandAll();
ui->lineEdit->setText(message);
on_lineEdit_returnPressed();
}
show();
raise();
}
diff --git a/mainwindow.h b/mainwindow.h
index 66c05a8..33ebdb1 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -1,83 +1,87 @@
#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;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
enum actionType { GPG, GIT, EDIT, DELETE };
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void setPassExecutable(QString);
void setGitExecutable(QString);
void setGpgExecutable(QString);
void checkConfig();
void setApp(SingleApplication* app);
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 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;
int autoclearSeconds;
QString passStore;
QString passExecutable;
QString gitExecutable;
QString gpgExecutable;
QString gpgHome;
QString clippedPass;
actionType currentAction;
QString lastDecrypt;
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();
};
#endif // MAINWINDOW_H
diff --git a/qtpass.pro b/qtpass.pro
index 0109363..89f0472 100644
--- a/qtpass.pro
+++ b/qtpass.pro
@@ -1,60 +1,67 @@
#-------------------------------------------------
#
# Project created by QtCreator 2014-07-30T21:56:15
#
#-------------------------------------------------
-QT += core gui network
+QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
macx {
TARGET = QtPass
} else {
TARGET = qtpass
}
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp \
dialog.cpp \
storemodel.cpp \
- singleapplication.cpp \
util.cpp
HEADERS += mainwindow.h \
dialog.h \
storemodel.h \
- singleapplication.h \
util.h
FORMS += mainwindow.ui \
dialog.ui
+nosingleapp {
+QMAKE_CXXFLAGS += -DSINGLE_APP=0
+} else {
+SOURCES += singleapplication.cpp
+HEADERS += singleapplication.h
+QT += network
+QMAKE_CXXFLAGS += -DSINGLE_APP=1
+}
+
TRANSLATIONS += localization/localization_nl_NL.ts \
localization/localization_de_DE.ts \
localization/localization_es_ES.ts \
localization/localization_gl_ES.ts \
localization/localization_hu_HU.ts \
localization/localization_sv_SE.ts \
localization/localization_pl_PL.ts \
localization/localization_ru_RU.ts
RESOURCES += resources.qrc
win32 {
RC_FILE = windows.rc
static {
QMAKE_LFLAGS += -static-libgcc -static-libstdc++
}
QMAKE_LFLAGS += -Wl,--dynamicbase -Wl,--nxcompat
} else:macx {
ICON = artwork/icon.icns
}
OTHER_FILES += LICENSE \
README.md
target.path = /usr/local/bin/
INSTALLS += target
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, May 31, 8:00 AM (9 h, 10 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
9c/ea/9d68bb2b7282031b635e1d1fe5de
Attached To
rGPGPASS GnuPG Password Manager
Event Timeline
Log In to Comment