diff options
44 files changed, 3801 insertions, 451 deletions
@@ -1,7 +1,13 @@ -qtpass.pro.user +Makefile debian/qtpass* debian/files build-stamp qtpass QtPass.* +qtpass.pro.user +qtpass.xcodeproj/project.xcworkspace/xcuserdata/* +qtpass.xcodeproj/xcuserdata/* +qtpass.xcworkspace/xcuserdata/* +.DS_Store +.qmake.stash diff --git a/.travis.yml b/.travis.yml index a4ca80c3..85a9df89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,9 @@ language: cpp + +os: + - linux + - osx + compiler: - gcc - clang @@ -34,3 +39,7 @@ notifications: - "chat.freenode.net#IJhack" on_success: change on_failure: always + +matrix: + allow_failures: + - os: osx diff --git a/Info.plist b/Info.plist new file mode 100644 index 00000000..7b64cd4c --- /dev/null +++ b/Info.plist @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>NSPrincipalClass</key> + <string>NSApplication</string> + <key>CFBundleDisplayName</key> + <string>QtPass @SHORT_VERSION@</string> + <key>CFBundleIconFile</key> + <string>icon.icns</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleGetInfoString</key> + <string>@SHORT_VERSION@</string> + <key>NSHumanReadableCopyright</key> + <string>Copyright © 2014-2015 IJhack +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</string> + <key>CFBundleShortVersionString</key> + <string>@SHORT_VERSION@</string> + <key>CFBundleSignature</key> + <string>@TYPEINFO@</string> + <key>CFBundleExecutable</key> + <string>@EXECUTABLE@</string> + <key>CFBundleIdentifier</key> + <string>com.ijhack.QtPass</string> + <key>NOTE</key> + <string>QtPass is a multi-platform GUI for pass</string> +</dict> +</plist> @@ -10,12 +10,13 @@ Features * Configurable shoulder surfing protection options * Cross platform: Linux, BSD, OS X and Windows * Per-folder user selection for multi recipient encryption -* Multiple password store profiles While QtPass will work with Qt4, currently multi-line editing is restricted to Qt5 only. QtPass requires GCC 4.7 or later or any other 2011 ISO C++ standard compliant compiler. +Logo based on https://commons.wikimedia.org/wiki/File:Heart-padlock.svg by AnonMoos. + Security considerations ----------------------- Using this program will not magically keep your passwords secure against @@ -51,15 +52,15 @@ Known issues Planned features ---------------- +* Re-encryption after users-change (optional ofcourse) * Showing path in Add and Edit screen (currently sometimes confusing where I'm adding this password) * Right click handlers for file/folder and content -* First use wizards to set up password-store (and decryption key, currently always the gpg default key) +* ~~First use wizards to set up password-store (and decryption key, currently always the gpg default key)~~ * Profiles (to allow use of multiple password stores and decryption keys) with dropdown in main screen * Password generation with options for what kind you'd like * Templates (username, url etc) in Add / Edit screen (configurable templates) * Colour coding or disabling of people you can't encrypt for (trust settings) in User management * Colour coding folders (possibly disabling folders you can't decrypt) -* Trayicon support * WebDAV (configuration) support * Optional table view of decrypted folder contents * Opening of (basic auth) urls in default browser? Possibly with helper plugin for filling out forms? @@ -82,4 +83,3 @@ Further reading [Documentation](http://qtpass.org/) [Source code](https://github.com/IJHack/qtpass) - diff --git a/appdmg.json b/appdmg.json new file mode 100644 index 00000000..02c629a1 --- /dev/null +++ b/appdmg.json @@ -0,0 +1,12 @@ +{ + "title": "QtPass", + "icon": "artwork/icon.icns", + "background": "artwork/icon.png", + "icon-size": 80, + "contents": [ + { "x": 64, "y": 64, "type": "file", "path": "QtPass.app" }, + { "x": 448, "y": 64, "type": "link", "path": "/Applications" }, + { "x": 64, "y": 448, "type": "file", "path": "README.rtf" }, + { "x": 448, "y": 448, "type": "file", "path": "LICENSE" } + ] +} diff --git a/artwork/icon.icns b/artwork/icon.icns Binary files differindex 20b73e75..417a8fa7 100644 --- a/artwork/icon.icns +++ b/artwork/icon.icns diff --git a/artwork/icon@2x.png b/artwork/icon@2x.png Binary files differnew file mode 100644 index 00000000..7dbfa9cb --- /dev/null +++ b/artwork/icon@2x.png diff --git a/changelog b/changelog new file mode 120000 index 00000000..d526672c --- /dev/null +++ b/changelog @@ -0,0 +1 @@ +debian/changelog
\ No newline at end of file @@ -1,14 +1,20 @@ #include "dialog.h" #include "ui_dialog.h" +#include "mainwindow.h" +#include "keygendialog.h" +#include <QDebug> +#include <QMessageBox> +#include <QDir> /** * @brief Dialog::Dialog * @param parent */ -Dialog::Dialog(QWidget *parent) : +Dialog::Dialog(MainWindow *parent) : QDialog(parent), ui(new Ui::Dialog) { + mainWindow = parent; ui->setupUi(this); } @@ -17,6 +23,9 @@ Dialog::Dialog(QWidget *parent) : */ Dialog::~Dialog() { + mainWindow->setGitExecutable(ui->gitPath->text()); + mainWindow->setGpgExecutable(ui->gpgPath->text()); + mainWindow->setPassExecutable(ui->passPath->text()); } /** @@ -156,6 +165,7 @@ QString Dialog::selectExecutable() { QString Dialog::selectFolder() { QFileDialog dialog(this); dialog.setFileMode(QFileDialog::Directory); + dialog.setFilter(QDir::NoFilter); dialog.setOption(QFileDialog::ShowDirsOnly); if (dialog.exec()) { return dialog.selectedFiles().first(); @@ -202,7 +212,7 @@ void Dialog::on_toolButtonPass_clicked() void Dialog::on_toolButtonStore_clicked() { QString store = selectFolder(); - if (store != "") { + if (store != "") { // TODO call check ui->storePath->setText(store); } } @@ -348,3 +358,55 @@ void Dialog::addGPGId(bool addGPGId) { ui->checkBoxAddGPGId->setChecked(addGPGId); } +/** + * @brief Dialog::wizard + */ +void Dialog::wizard() +{ + //mainWindow->checkConfig(); + + QString gpg = ui->gpgPath->text(); + //QString gpg = mainWindow->getGpgExecutable(); + if(!QFile(gpg).exists()){ + QMessageBox::critical(this, 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")); + + // TODO REST ? + } + + QStringList names = mainWindow->getSecretKeys(); + //qDebug() << names; + if (QFile(gpg).exists() && names.empty()) { + KeygenDialog d(this); + d.exec(); + } + + QString passStore = ui->storePath->text(); + if(!QFile(passStore + ".gpg-id").exists()){ + QMessageBox::critical(this, 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(); + passStore = ui->storePath->text(); + } + if (!QFile(passStore + ".gpg-id").exists()) { + // apears not to be store + // init yes / no ? + mainWindow->userDialog(passStore); + } + } + + // Can you use the store? + + + //ui->gpgPath->setText(gpg); +} + +/** + * @brief Dialog::genKey + * @param QString batch + */ +void Dialog::genKey(QString batch, QDialog *dialog) +{ + mainWindow->genKey(batch, dialog); +} @@ -3,8 +3,12 @@ #include <QDialog> #include <QFileDialog> +#include "mainwindow.h" namespace Ui { + +struct UserInfo; + class Dialog; } @@ -13,7 +17,7 @@ class Dialog : public QDialog Q_OBJECT public: - explicit Dialog(QWidget *parent = 0); + explicit Dialog(MainWindow *parent); ~Dialog(); void setPassPath(QString); void setGitPath(QString); @@ -37,6 +41,8 @@ public: bool hidePassword(); bool hideContent(); bool addGPGId(); + void wizard(); + void genKey(QString, QDialog *); private slots: void on_radioButtonNative_clicked(); @@ -53,6 +59,7 @@ private: void setGroupBoxState(); QString selectExecutable(); QString selectFolder(); + MainWindow *mainWindow; }; #endif // DIALOG_H diff --git a/keygendialog.cpp b/keygendialog.cpp new file mode 100644 index 00000000..9932ea26 --- /dev/null +++ b/keygendialog.cpp @@ -0,0 +1,141 @@ +#include "keygendialog.h" +#include "ui_keygendialog.h" +#include "progressindicator.h" +#include <QDebug> +#include <QMessageBox> + +KeygenDialog::KeygenDialog(Dialog *parent) : + QDialog(parent), + ui(new Ui::KeygenDialog) +{ + ui->setupUi(this); + dialog = parent; +} + +KeygenDialog::~KeygenDialog() +{ + delete ui; +} + +void KeygenDialog::on_passphrase1_textChanged(const QString &arg1) +{ + if (ui->passphrase1->text() == ui->passphrase2->text()) { + ui->buttonBox->setEnabled(true); + replace("Passphrase", arg1); + if (arg1 == "") { + no_protection(true); + } else { + no_protection(false); + } + } else { + ui->buttonBox->setEnabled(false); + } +} + +void KeygenDialog::on_passphrase2_textChanged(const QString &arg1) +{ + on_passphrase1_textChanged(arg1); +} + +void KeygenDialog::on_checkBox_stateChanged(int arg1) +{ + if (arg1) { + ui->plainTextEdit->setReadOnly(false); + ui->plainTextEdit->setEnabled(true); + } else { + ui->plainTextEdit->setReadOnly(true); + ui->plainTextEdit->setEnabled(false); + } +} + +void KeygenDialog::on_email_textChanged(const QString &arg1) +{ + replace("Name-Email", arg1); +} + +void KeygenDialog::on_name_textChanged(const QString &arg1) +{ + replace("Name-Real", arg1); +} + +/** + * @brief KeygenDialog::replace + * @param key + * @param value + */ +void KeygenDialog::replace(QString key, QString value) +{ + QStringList clear; + QString expert = ui->plainTextEdit->toPlainText(); + QStringList lines = expert.split(QRegExp("[\r\n]"),QString::SkipEmptyParts); + foreach (QString line, lines) { + line.replace(QRegExp(key+":.*"), key + ": " + value); + if (key == "Passphrase") { + line.replace("%no-protection", "Passphrase: " + value); + } + clear.append(line); + } + ui->plainTextEdit->setPlainText(clear.join("\n")); +} + +/** + * @brief KeygenDialog::no_protection + * @param enable + */\ +void KeygenDialog::no_protection(bool enable) +{ + QStringList clear; + QString expert = ui->plainTextEdit->toPlainText(); + QStringList lines = expert.split(QRegExp("[\r\n]"),QString::SkipEmptyParts); + foreach (QString line, lines) { + bool remove = false; + if (!enable) { + if (line.indexOf("%no-protection") == 0) { + remove = true; + } + } else { + if (line.indexOf("Passphrase") == 0) { + line = "%no-protection"; + } + } + if (!remove) { + clear.append(line); + } + } + ui->plainTextEdit->setPlainText(clear.join("\n")); +} + +/** + * @brief KeygenDialog::done + * @param r + */ +void KeygenDialog::done(int r) +{ + if(QDialog::Accepted == r) // ok was pressed + { + ui->widget->setEnabled(false); + ui->buttonBox->setEnabled(false); + ui->checkBox->setEnabled(false); + ui->plainTextEdit->setEnabled(false); + + QProgressIndicator* pi = new QProgressIndicator(); + pi->startAnimation(); + pi->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + ui->frame->hide(); + ui->label->setText(QString("We need to generate a lot of random bytes. It is a good idea to perform<br/>") + + "some other action (type on the keyboard, move the mouse, utilize the<br/>" + + "disks) during the prime generation; this gives the random number<br/>" + + "generator a better chance to gain enough entropy."); + + this->layout()->addWidget(pi); + + this->show(); + dialog->genKey(ui->plainTextEdit->toPlainText(), this); + } + else // cancel, close or exc was pressed + { + QDialog::done(r); + return; + } +} diff --git a/keygendialog.h b/keygendialog.h new file mode 100644 index 00000000..d808816a --- /dev/null +++ b/keygendialog.h @@ -0,0 +1,35 @@ +#ifndef KEYGENDIALOG_H +#define KEYGENDIALOG_H + +#include <QDialog> +#include "dialog.h" + +namespace Ui { +class KeygenDialog; +} + +class KeygenDialog : public QDialog +{ + Q_OBJECT + +public: + explicit KeygenDialog(Dialog *parent = 0); + ~KeygenDialog(); + +private slots: + void on_passphrase1_textChanged(const QString &arg1); + void on_passphrase2_textChanged(const QString &arg1); + void on_checkBox_stateChanged(int arg1); + void on_email_textChanged(const QString &arg1); + void on_name_textChanged(const QString &arg1); + +private: + Ui::KeygenDialog *ui; + void replace(QString, QString); + void done(int r); + void no_protection(bool enable); + Dialog *dialog; + +}; + +#endif // KEYGENDIALOG_H diff --git a/keygendialog.ui b/keygendialog.ui new file mode 100644 index 00000000..740e80e2 --- /dev/null +++ b/keygendialog.ui @@ -0,0 +1,219 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>KeygenDialog</class> + <widget class="QDialog" name="KeygenDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>606</width> + <height>480</height> + </rect> + </property> + <property name="windowTitle"> + <string>Generate GnuPG keypair</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Generate a new key pair</string> + </property> + </widget> + </item> + <item> + <widget class="QFrame" name="frame"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <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="QWidget" name="widget" native="true"> + <layout class="QGridLayout" name="gridLayout"> + <item row="1" column="0" colspan="2"> + <widget class="QLabel" name="labelPassphrase"> + <property name="text"> + <string>Passphrase</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="labelEmail"> + <property name="text"> + <string>Email</string> + </property> + </widget> + </item> + <item row="2" column="2" colspan="3"> + <widget class="QLineEdit" name="passphrase2"> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="0" column="1" colspan="2"> + <widget class="QLineEdit" name="email"/> + </item> + <item row="0" column="3"> + <widget class="QLabel" name="labelName"> + <property name="text"> + <string>Name</string> + </property> + </widget> + </item> + <item row="1" column="2" colspan="3"> + <widget class="QLineEdit" name="passphrase1"> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="0" column="4"> + <widget class="QLineEdit" name="name"/> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QLabel" name="labelPassphraseInfo"> + <property name="text"> + <string><html><head/><body><p>There is no limit on the length of a passphrase, and it should be carefully chosen. From the perspective of security, the passphrase to unlock the private key is one of the weakest points in GnuPG (and other public-key encryption systems as well) since it is the only protection you have if another individual gets your private key. <br/>Ideally, the passphrase should not use words from a dictionary and should mix the case of alphabetic characters as well as use non-alphabetic characters.<br/>A good passphrase is crucial to the secure use of GnuPG.</p></body></html></string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="checkBox"> + <property name="text"> + <string>Expert</string> + </property> + </widget> + </item> + <item> + <widget class="QPlainTextEdit" name="plainTextEdit"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + <property name="plainText"> + <string># QtPass GPG key generator +# +# first test version please comment +# +%echo Generating a default key +Key-Type: default +Subkey-Type: default +Name-Real: +Name-Comment: QtPass +Name-Email: +Expire-Date: 0 +%no-protection +# Do a commit here, so that we can later print "done" :-) +%commit +%echo done</string> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + <property name="backgroundVisible"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="labelExpertInfo"> + <property name="text"> + <string>For expert options check out the <a href="https://www.gnupg.org/documentation/manuals/gnupg/Unattended-GPG-key-generation.html">GnuPG manual</a></string> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set> + </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> + </item> + </layout> + </widget> + <tabstops> + <tabstop>email</tabstop> + <tabstop>name</tabstop> + <tabstop>passphrase1</tabstop> + <tabstop>passphrase2</tabstop> + <tabstop>checkBox</tabstop> + <tabstop>plainTextEdit</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>KeygenDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>KeygenDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/localization/localization_de_DE.qm b/localization/localization_de_DE.qm Binary files differindex dfee88af..841b292a 100644 --- a/localization/localization_de_DE.qm +++ b/localization/localization_de_DE.qm diff --git a/localization/localization_de_DE.ts b/localization/localization_de_DE.ts index ce62a87a..9f78b6ad 100644 --- a/localization/localization_de_DE.ts +++ b/localization/localization_de_DE.ts @@ -9,80 +9,93 @@ <translation>Einstellungen</translation> </message> <message> |