...
 
Commits (55)
[main]
host = https://www.transifex.com
[harbour-SailHN.harbour-sailhnts]
file_filter = translations/harbour-mpw-<lang>.ts
source_file = translations/harbour-mpw.ts
source_lang = en
type = QT
......@@ -2,17 +2,26 @@ TARGET = harbour-mpw
CONFIG += sailfishapp
QT += sql
SOURCES += \
src/main.cpp \
src/mpwmanager.cpp
src/asyncmasterkey.cpp \
src/dbmanager.cpp \
src/mpwmanager.cpp \
src/sitessqlmodel.cpp
HEADERS += \
src/mpwmanager.h
src/asyncmasterkey.h \
src/dbmanager.h \
src/mpwmanager.h \
src/sitessqlmodel.h
OTHER_FILES += \
qml/cover/CoverPage.qml \
qml/pages/MainPage.qml \
qml/pages/Settings.qml \
qml/pages/SiteDelegate.qml \
qml/MPW.qml \
harbour-mpw.desktop \
harbour-mpw.png \
......@@ -26,3 +35,11 @@ CONFIG += sailfishapp_i18n
INCLUDEPATH += "/usr/include/mpw"
LIBS += -lmpw -lcrypto
TRANSLATIONS += \
translations/harbour-mpw-ar.ts \
translations/harbour-mpw-de.ts \
translations/harbour-mpw-el.ts \
translations/harbour-mpw-hu_HU.ts \
translations/harbour-mpw-it.ts \
translations/harbour-mpw-sv.ts
# harbour-MPW
# MPW
A [MasterPassword](http://masterpasswordapp.com/) client for [Sailfish OS](https://sailfishos.org).
[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=ascarpino&url=https://gitlab.com/ascarpino/harbour-MPW&title=harbour-MPW&language=&tags=jolla&category=software)
[Build status](https://build.merproject.org/package/live_build_log/home:ilpianista/harbour-mpw/sailfish_latest_armv7hl/armv8el)
Translations via [Transifex](https://www.transifex.com/organization/ilpianista-harbour/dashboard/harbour-MPW), thank you!
## Translations
[Build status](https://build.merproject.org/package/live_build_log/home:ilpianista/harbour-MPW/sailfish_latest_armv7hl/armv8el)
[![Translation status](https://hosted.weblate.org/widgets/harbour-mpw/-/svg-badge.svg)](https://hosted.weblate.org/engage/harbour-mpw/?utm_source=widget)
Translations via [Weblate](https://hosted.weblate.org/projects/harbour-mpw/), thank you!
## Donate
Donations via [Liberapay](https://liberapay.com/ilpianista) or Bitcoin (1Ph3hFEoQaD4PK6MhL3kBNNh9FZFBfisEH) are always welcomed, _thank you_!
## License
MIT
......@@ -27,7 +27,11 @@ import Sailfish.Silica 1.0
import "pages"
ApplicationWindow {
id: appWindow
property string password: ""
initialPage: Component { MainPage { } }
cover: Qt.resolvedUrl("cover/CoverPage.qml")
}
......@@ -28,7 +28,7 @@ import Sailfish.Silica 1.0
CoverBackground {
CoverPlaceholder {
text: "SailHN"
text: appWindow.password.length > 0 ? appWindow.password : "MPW"
icon.source: "/usr/share/icons/hicolor/86x86/apps/harbour-mpw.png"
}
}
......@@ -28,8 +28,19 @@ import harbour.mpw 1.0
Page {
property bool masterKey: false
allowedOrientations: Orientation.All
Connections {
target: manager
onGeneratedMasterKey: {
masterKey = true;
password.text = "";
}
}
SilicaFlickable {
anchors.fill: parent
contentHeight: column.height
......@@ -43,17 +54,40 @@ Page {
}
MenuItem {
text: qsTr("Copy to clipboard")
id: clearSites
text: qsTr("Clear sites")
enabled: sites.count > 0
onClicked: {
Clipboard.text = password.text;
if (masterKey) {
password.text = "";
}
enabled = false;
manager.clearSites();
}
}
MenuItem {
text: qsTr("Clear")
id: clearPwd
text: qsTr("Clear password")
enabled: false
onClicked: clear()
onClicked: {
enabled = false;
copy.enabled = false;
password.text = "";
}
}
MenuItem {
id: copy
text: qsTr("Copy to clipboard")
enabled: false
onClicked: {
Clipboard.text = password.text;
}
}
}
......@@ -67,41 +101,54 @@ Page {
}
TextField {
id: site
id: siteUrl
width: parent.width
inputMethodHints: Qt.ImhUrlCharactersOnly
placeholderText: qsTr("Site name (e.g. google.com)")
validator: RegExpValidator { regExp: /^[\w\.-]*$/ }
enabled: masterKey
EnterKey.enabled: site.text.length > 0 && counter.text.length > 0
EnterKey.enabled: siteUrl.text.length > 0 && siteCounter.text.length > 0
EnterKey.onClicked: getPassword()
}
TextField {
id: counter
Row {
width: parent.width
text: "1"
inputMethodHints: Qt.ImhDigitsOnly
placeholderText: qsTr("Counter")
spacing: Theme.paddingMedium
ComboBox {
id: sitePwdType
label: qsTr("Type")
currentIndex: 1
width: parent.width - siteCounter.width
enabled: masterKey
menu: ContextMenu {
MenuItem { text: qsTr("Maximum") }
MenuItem { text: qsTr("Long") }
MenuItem { text: qsTr("Medium") }
MenuItem { text: qsTr("Basic") }
MenuItem { text: qsTr("Short") }
MenuItem { text: qsTr("PIN") }
MenuItem { text: qsTr("Name") }
MenuItem { text: qsTr("Phrase") }
}
onValueChanged: getPassword()
}
EnterKey.enabled: site.text.length > 0 && counter.text.length > 0
EnterKey.onClicked: getPassword()
TextField {
id: siteCounter
text: "1"
width: 200
inputMethodHints: Qt.ImhDigitsOnly
validator: RegExpValidator { regExp: /^[0-9]+$/ }
placeholderText: qsTr("Counter")
enabled: masterKey
}
EnterKey.enabled: siteUrl.text.length > 0 && siteCounter.text.length > 0
EnterKey.onClicked: getPassword()
ComboBox {
id: type
label: qsTr("Password type")
currentIndex: 1
menu: ContextMenu {
MenuItem { text: qsTr("Maximum") }
MenuItem { text: qsTr("Long") }
MenuItem { text: qsTr("Medium") }
MenuItem { text: qsTr("Basic") }
MenuItem { text: qsTr("Short") }
MenuItem { text: qsTr("PIN") }
MenuItem { text: qsTr("Name") }
MenuItem { text: qsTr("Phrase") }
}
}
......@@ -111,34 +158,37 @@ Page {
color: Theme.secondaryColor
horizontalAlignment: TextInput.AlignHCenter
wrapMode: Text.Wrap
}
}
text: qsTr("Touch here to set your master password or use the Settings page!")
VerticalScrollDecorator {}
}
MouseArea {
anchors.fill: parent
onClicked: pageStack.push(Qt.resolvedUrl("Settings.qml"))
}
}
onStatusChanged: {
if (status === PageStatus.Activating) {
var isReady = manager.getName() !== "" && manager.getKey() !== "";
site.enabled = isReady;
SectionHeader {
text: qsTr("Sites")
}
if (!isReady) {
password.text = qsTr("Please fill your name and master key in the Settings page!");
} else {
password.text = "";
Repeater {
id: sites
model: recentSites
enabled: masterKey
delegate: SiteDelegate {}
}
}
VerticalScrollDecorator {}
}
function getPassword() {
password.text = manager.getPassword(site.text, type.currentIndex, counter.text);
if (masterKey) {
var pwd = manager.getPassword(siteUrl.text, sitePwdType.currentIndex, siteCounter.text);
password.text = pwd;
clearPwd.enabled = true;
copy.enabled = true;
appWindow.password = pwd;
}
}
function clear() {
site.text = "";
site.forceActiveFocus();
counter.text = "1"
type.currentIndex = 1;
password.text = "";
}
}
......@@ -26,13 +26,24 @@ import QtQuick 2.0
import Sailfish.Silica 1.0
Page {
id: page
allowedOrientations: Orientation.All
Connections {
target: manager
onGeneratedMasterKey: {
fprint.text = fingerprint;
busy.visible = busy.running = false;
name.enabled = password.enabled = version.enabled = true;
}
}
Column {
id: column
x: Theme.horizontalPageMargin
width: parent.width - Theme.horizontalPageMargin * 2
spacing: Theme.paddingMedium
PageHeader {
title: qsTr("Settings")
......@@ -41,32 +52,68 @@ Page {
TextField {
id: name
width: parent.width
focus: true
placeholderText: qsTr("Name")
text: manager.getName
placeholderText: qsTr("Full name")
onTextChanged: save.enabled = (text.length > 0 && password.text.length > 0)
}
TextField {
id: password
width: parent.width
placeholderText: qsTr("Master Password")
placeholderText: qsTr("Master password")
echoMode: TextInput.Password
onTextChanged: save.enabled = (text.length > 0 && name.text.length > 0)
}
ComboBox {
id: version
label: qsTr("Algorithm version")
currentIndex: manager.getAlgorithmVersion()
width: page.width
menu: ContextMenu {
MenuItem { text: "V0" }
MenuItem { text: "V1" }
MenuItem { text: "V2" }
MenuItem { text: "V3" }
}
}
BusyIndicator {
id: busy
visible: false
anchors.horizontalCenter: parent.horizontalCenter
}
Label {
id: fprint
anchors.horizontalCenter: parent.horizontalCenter
}
Button {
id: save
text: qsTr("Save");
text: qsTr("Generate");
anchors.horizontalCenter: parent.horizontalCenter
enabled: false
onClicked: {
if (name.text.length > 0 && password.text.length > 0) {
manager.setUserData(name.text, password.text);
pageStack.pop();
}
enabled = name.enabled = password.enabled = version.enabled = false;
busy.visible = busy.running = true;
fprint.text = "";
manager.generateMasterKey(name.text.trim(), password.text.trim(), version.currentIndex);
}
}
}
Component.onCompleted: {
name.text = manager.getName();
fprint.text = manager.getFingerprint();
if (name.text.length > 0) {
password.forceActiveFocus();
}
}
}
/*
The MIT License (MIT)
Copyright (c) 2015 Andrea Scarpino <me@andreascarpino.it>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
ListItem {
id: item
menu: itemMenu
Label {
x: Theme.horizontalPageMargin
font.pixelSize: Theme.fontSizeMedium
text: site
wrapMode: Text.Wrap
}
ContextMenu {
id: itemMenu
MenuItem {
text: qsTr("Delete")
onClicked: deleteSite(site)
}
}
onClicked: {
siteUrl.text = site;
sitePwdType.currentIndex = typeIndexFromString(type);
siteCounter.text = counter;
getPassword();
}
function typeIndexFromString(type) {
if (type === "Maximum") {
return 0;
} else if (type === "Long") {
return 1;
} else if (type === "Medium") {
return 2;
} else if (type === "Basic") {
return 3;
} else if (type === "Short") {
return 4;
} else if (type === "PIN") {
return 5;
} else if (type === "Name") {
return 6;
} else if (type === "Phrase") {
return 7;
}
}
function deleteSite(site) {
remorseAction(qsTr("Deleting"), function() { manager.deleteSite(site) }, 3000);
}
}
* Thu Oct 27 2016 Andrea Scarpino <me@andreascarpino.it> 0.2.2-1
- Always trim name and master password
- Allow only valid chars for websites
- Fix websites saving when type is Medium
- Allow to set the master key by touching the label in the home at first boot
* Wed Jul 20 2016 Andrea Scarpino <me@andreascarpino.it> 0.2.1-1
- Show a visible fingerprint for the generated password
* Tue Jun 07 2016 Andrea Scarpino <me@andreascarpino.it> 0.2.0-1
- Store sites and their preferences
- Show password in cover page
* Sat Jun 04 2016 Andrea Scarpino <me@andreascarpino.it> 0.1.3-1
- Store name and algorithm version
* Thu Apr 21 2016 Andrea Scarpino <me@andreascarpino.it> 0.1.2-1
- Don't block UI when saving master key
- Support all algorithm versions
- Enable copy to clipboard button only when there's something to copy
- Add Italian translation
- Add Swedish translation
* Thu Apr 21 2016 Andrea Scarpino <me@andreascarpino.it> 0.1.1-1
- Fix master key generation (#1)
* Thu Apr 21 2016 Andrea Scarpino <me@andreascarpino.it> 0.1-1
- Initial version
......@@ -13,7 +13,7 @@ Name: harbour-mpw
%{!?qtc_make:%define qtc_make make}
%{?qtc_builddir:%define _builddir %qtc_builddir}
Summary: MasterPassword client
Version: 0.1
Version: 0.2.2
Release: 1
Group: Qt/Qt
License: MIT
......@@ -26,7 +26,9 @@ BuildRequires: pkgconfig(sailfishapp) >= 1.0.2
BuildRequires: pkgconfig(Qt5Core)
BuildRequires: pkgconfig(Qt5Qml)
BuildRequires: pkgconfig(Qt5Quick)
BuildRequires: pkgconfig(openssl)
BuildRequires: libmpw
BuildRequires: libmpw-devel
BuildRequires: desktop-file-utils
%description
......
Name: harbour-mpw
Summary: MasterPassword client
Version: 0.1
Version: 0.2.2
Release: 1
# The contents of the Group field should be one of the groups listed here:
# http://gitorious.org/meego-developer-tools/spectacle/blobs/master/data/GROUPS
......@@ -25,14 +25,16 @@ PkgConfigBR:
- Qt5Core
- Qt5Qml
- Qt5Quick
- openssl
# Build dependencies without a pkgconfig setup can be listed here
PkgBR:
- libmpw
- libmpw-devel
# Runtime dependencies which are not automatically detected
Requires:
- sailfishsilica-qt5 >= 0.10.9
- sailfishsilica-qt5 >= 0.10.9
- libmpw
# All installed files
......
/*
The MIT License (MIT)
Copyright (c) 2016 Andrea Scarpino <me@andreascarpino.it>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "asyncmasterkey.h"
#include <QDebug>
extern "C"
{
#ifndef MPWALGORITHM_H
#define MPWALGORITHM_H
#include <mpw-algorithm.h>
#endif
#ifndef MPWUTIL_H
#define MPWUTIL_H
#include <mpw-util.h>
#endif
}
AsyncMasterKey::AsyncMasterKey(const QString &name, const QString &password,
MPWManager::AlgorithmVersion version)
: m_name(name), m_password(password), m_algVersion(version)
{
}
AsyncMasterKey::~AsyncMasterKey()
{
}
void AsyncMasterKey::generate()
{
const uint8_t* k = mpw_masterKeyForUser(m_name.toUtf8().data(), m_password.toUtf8().data(),
MPWManager::toMPAlgorithmVersion(m_algVersion));
QByteArray* key = 0;
if (k) {
key = new QByteArray((const char*) k, MP_dkLen);
} else {
qCritical() << "Error during master key generation.";
}
const char* fingerprint = mpw_identicon(m_name.toUtf8().data(), m_password.toUtf8().data());
Q_EMIT finished(key, QString::fromUtf8(fingerprint));
}
/*
The MIT License (MIT)
Copyright (c) 2016 Andrea Scarpino <me@andreascarpino.it>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef ASYNCMASTERKEY_H
#define ASYNCMASTERKEY_H
#include <QObject>
#include "mpwmanager.h"
class AsyncMasterKey : public QObject
{
Q_OBJECT
public:
explicit AsyncMasterKey(const QString &name, const QString &password,
MPWManager::AlgorithmVersion version);
virtual ~AsyncMasterKey();
Q_SIGNALS:
void finished(QByteArray *key, const QString &fingerprint);
public Q_SLOTS:
void generate();
private:
QString m_name;
QString m_password;
MPWManager::AlgorithmVersion m_algVersion;
};
#endif // ASYNCMASTERKEY_H
/*
The MIT License (MIT)
Copyright (c) 2016 Andrea Scarpino <me@andreascarpino.it>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "dbmanager.h"
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlRecord>
const static QString DB_NAME = QStringLiteral("mpw");
const static int DB_VERSION = 1;
const static QString CLEAR_SITES = QStringLiteral("DELETE FROM sites;");
const static QString CREATE_SITES_TABLE = QStringLiteral("CREATE TABLE IF NOT EXISTS sites(site TEXT PRIMARY KEY, type TEXT, counter SMALLINT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP);");
const static QString DELETE_SITE = QStringLiteral("DELETE FROM sites WHERE site=\"%1\";");
const static QString INSERT_INTO_SITES = QStringLiteral("INSERT INTO sites(site, type, counter) VALUES(\"%1\", \"%2\", %3);");
const static QString READ_DB_VERSION = QStringLiteral("PRAGMA user_version;");
const static QString UPDATE_DB_VERSION = QStringLiteral("PRAGMA user_version=%1;");
DBManager::DBManager(QObject *parent) :
QObject(parent)
{
db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"));
const QString dbPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation));
db.setDatabaseName(dbPath + QDir::separator() + DB_NAME + QStringLiteral(".sql"));
const QDir dir;
if (!dir.exists(dbPath)) {
if (!dir.mkpath(dbPath)) {
qCritical("Cannot create data folder!");
}
}
if (!db.open()) {
qCritical("Unable to open database!");
} else {
init();
}
}
DBManager::~DBManager()
{
db.close();
}
void DBManager::clearSites()
{
QSqlQuery clearSites(db);
if (!clearSites.exec(CLEAR_SITES)) {
qCritical("Cannot clear sites");
}
}
void DBManager::deleteSite(const QString &site)
{
QSqlQuery query(db);
if (!query.exec(DELETE_SITE.arg(site))) {
qCritical("Cannot delete data!");
}
}
void DBManager::init()
{
QSqlQuery query(db);
if (!query.exec(CREATE_SITES_TABLE)) {
qCritical("Cannot create table!");
return;
}
}
int DBManager::readDBVersion() const {
QSqlQuery query(db);
if (!query.exec(READ_DB_VERSION)) {
qCritical("Cannot determine DB version!");
return 0;
}
query.next();
return query.record().value(0).toInt();
}
void DBManager::insert(const QString &site, MPWManager::PasswordType type, const uint counter)
{
deleteSite(site);
QSqlQuery query(db);
if (!query.exec(INSERT_INTO_SITES.arg(site).arg(typeToString(type)).arg(counter))) {
qCritical("Cannot save data!");
}
}
QString DBManager::typeToString(MPWManager::PasswordType type) const
{
QString t;
switch (type) {
case MPWManager::Maximum: t = QStringLiteral("Maximum"); break;
case MPWManager::Medium: t = QStringLiteral("Medium"); break;
case MPWManager::Basic: t = QStringLiteral("Basic"); break;
case MPWManager::Short: t = QStringLiteral("Short"); break;
case MPWManager::PIN: t = QStringLiteral("PIN"); break;
case MPWManager::Name: t = QStringLiteral("Name"); break;
case MPWManager::Phrase: t = QStringLiteral("Phrase"); break;
case MPWManager::Long:
default: t = QStringLiteral("Long");
if (type != MPWManager::Long) {
qCritical() << "Unrecognized password type:" << type;
}
}
return t;
}
/*
The MIT License (MIT)
Copyright (c) 2016 Andrea Scarpino <me@andreascarpino.it>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef DBMANAGER_H
#define DBMANAGER_H
#include "mpwmanager.h"
#include "sitessqlmodel.h"
class QSqlDatabase;
class DBManager : public QObject
{
Q_OBJECT
public:
explicit DBManager(QObject *parent = 0);
virtual ~DBManager();
void clearSites();
void deleteSite(const QString &site);
void insert(const QString &site, MPWManager::PasswordType type, const uint counter);
private:
void init();
int readDBVersion() const;
QString typeToString(MPWManager::PasswordType type) const;
QSqlDatabase db;
};
#endif // DBMANAGER_H
......@@ -27,18 +27,22 @@
#include <sailfishapp.h>
#include "mpwmanager.h"
#include "sitessqlmodel.h"
int main(int argc, char *argv[])
{
QScopedPointer<QGuiApplication> app(SailfishApp::application(argc, argv));
QScopedPointer<QQuickView> view(SailfishApp::createView());
QCoreApplication::setApplicationName(QStringLiteral("MPW"));
QCoreApplication::setApplicationName(QStringLiteral("harbour-mpw"));
QCoreApplication::setOrganizationDomain(QStringLiteral("andreascarpino.it"));
qmlRegisterType<MPWManager>("harbour.mpw", 1, 0, "MPWManager");
MPWManager manager;
view->rootContext()->setContextProperty("manager", &manager);
SitesSqlModel* recentSites = manager.recentSites();
view->rootContext()->setContextProperty("recentSites", recentSites);
view->setSource(SailfishApp::pathTo("qml/MPW.qml"));
view->show();
......
......@@ -24,25 +24,37 @@
#include "mpwmanager.h"
#include <QCoreApplication>
#include <QDebug>
#include <QSettings>
#include <QThread>
extern "C"
{
#include <mpw-algorithm.h>
}
#include "asyncmasterkey.h"
#include "dbmanager.h"
MPWManager::MPWManager(QObject *parent) :
QObject(parent)
, m_db(new DBManager(this))
, m_model(new SitesSqlModel(this))
, m_key(0)
{
m_settings = new QSettings(QCoreApplication::applicationName(), QCoreApplication::applicationName(), this);
m_name = m_settings->value("Name").toString();
m_algVersion = algVersionFromInt(m_settings->value("Algorithm").toUInt());
}
MPWManager::~MPWManager()
{
delete m_db;
delete m_model;
delete m_key;
delete m_settings;
}
QString MPWManager::getKey() const
MPWManager::AlgorithmVersion MPWManager::getAlgorithmVersion() const
{
return m_key;
return m_algVersion;
}
QString MPWManager::getName() const
......@@ -50,17 +62,83 @@ QString MPWManager::getName() const
return m_name;
}
void MPWManager::setUserData(const QString &name, const QString &password)
QString MPWManager::getFingerprint() const
{
return m_fingerprint;
}
void MPWManager::setAlgorithmVersion(AlgorithmVersion version)
{
qDebug() << "Using algorithm version:" << version;
m_algVersion = version;
m_settings->setValue("Algorithm", version);
}
void MPWManager::setName(const QString &name)
{
m_name = name;
m_settings->setValue("Name", name);
}
m_key = QString::fromLatin1((const char*) mpw_masterKeyForUser(name.toLatin1().data(),
password.toLatin1().data(), MPAlgorithmVersionCurrent));
void MPWManager::generateMasterKey(const QString &name, const QString &password, AlgorithmVersion version)
{
setName(name);
setAlgorithmVersion(version);
QThread* thread = new QThread;
AsyncMasterKey* worker = new AsyncMasterKey(name, password, version);
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &AsyncMasterKey::generate);
connect(worker, &AsyncMasterKey::finished, this, &MPWManager::gotMasterKey);
connect(worker, &AsyncMasterKey::finished, thread, &QThread::quit);
connect(worker, &AsyncMasterKey::finished, worker, &AsyncMasterKey::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void MPWManager::gotMasterKey(QByteArray *key, const QString &fingerprint)
{
qDebug() << "Storing master key";
m_key = key;
m_fingerprint = fingerprint;
Q_EMIT generatedMasterKey(fingerprint);
}
QString MPWManager::getPassword(const QString &site, PasswordType type, const int counter) const
QString MPWManager::getPassword(const QString &site, PasswordType type, const uint counter) const
{
MPSiteType t = 0;
const char* p = mpw_passwordForSite((const unsigned char*) m_key->data(), site.toUtf8().data(),
toMPSiteType(type), counter, MPSiteVariantPassword, NULL,
toMPAlgorithmVersion(m_algVersion));
if (p) {
m_db->insert(site, type, counter);
m_model->refresh();
return QString::fromUtf8(p);
} else {
qCritical() << "Error during password generation";
return QString();
}
}
MPAlgorithmVersion MPWManager::toMPAlgorithmVersion(AlgorithmVersion version)
{
MPAlgorithmVersion v = MPAlgorithmVersionCurrent;
switch (version) {
case V0: v = MPAlgorithmVersion0; break;
case V1: v = MPAlgorithmVersion1; break;
case V2: v = MPAlgorithmVersion2; break;
case V3: v = MPAlgorithmVersion3; break;
default: qCritical() << "Unrecognized algorithm version:" << version;
}
return v;
}
MPSiteType MPWManager::toMPSiteType(PasswordType type)
{
MPSiteType t = MPSiteTypeGeneratedLong;
switch (type) {
case Maximum: t = MPSiteTypeGeneratedMaximum; break;
case Long: t = MPSiteTypeGeneratedLong; break;
......@@ -70,10 +148,40 @@ QString MPWManager::getPassword(const QString &site, PasswordType type, const in
case PIN: t = MPSiteTypeGeneratedPIN; break;
case Name: t = MPSiteTypeGeneratedName; break;
case Phrase: t = MPSiteTypeGeneratedPhrase; break;
default: qCritical() << "Unrecognized password type" << type;
default: qCritical() << "Unrecognized password type:" << type;
}
return QString::fromLatin1(mpw_passwordForSite((const unsigned char*) m_key.toLatin1().data(),
site.toLatin1().data(), t, counter, MPSiteVariantPassword, NULL,
MPAlgorithmVersionCurrent));
return t;
}
void MPWManager::clearSites()
{
m_db->clearSites();
m_model->refresh();
}
void MPWManager::deleteSite(const QString &site)
{
m_db->deleteSite(site);
m_model->refresh();
}
SitesSqlModel *MPWManager::recentSites()
{
//m_model = new SitesSqlModel(this);
return m_model;
}
MPWManager::AlgorithmVersion MPWManager::algVersionFromInt(const uint &version)
{
if (version == 0) {
return V0;
} else if (version == 1) {
return V1;
} else if (version == 2) {
return V2;
} else {
return V3;
}
}
......@@ -27,6 +27,19 @@
#include <QObject>
extern "C"
{
#ifndef MPWALGORITHM_H
#define MPWALGORITHM_H
#include <mpw-algorithm.h>
#endif
}
#include "sitessqlmodel.h"
class QSettings;
class DBManager;
class MPWManager : public QObject
{
Q_OBJECT
......@@ -36,17 +49,46 @@ public:
};
Q_ENUMS(PasswordType)
enum AlgorithmVersion {
V0, V1, V2, V3
};
Q_ENUMS(AlgorithmVersion)
explicit MPWManager(QObject *parent = 0);
virtual ~MPWManager();
Q_INVOKABLE QString getKey() const;
Q_INVOKABLE AlgorithmVersion getAlgorithmVersion() const;
Q_INVOKABLE QString getName() const;
Q_INVOKABLE void setUserData(const QString &name, const QString &password);
Q_INVOKABLE QString getPassword(const QString &site, PasswordType type, const int counter) const;
Q_INVOKABLE QString getFingerprint() const;
Q_INVOKABLE void setAlgorithmVersion(AlgorithmVersion version);
Q_INVOKABLE void setName(const QString &name);
Q_INVOKABLE void generateMasterKey(const QString &name, const QString &password, AlgorithmVersion version);
Q_INVOKABLE QString getPassword(const QString &site, PasswordType type, const uint counter) const;
Q_INVOKABLE void clearSites();
Q_INVOKABLE void deleteSite(const QString &site);
SitesSqlModel* recentSites();
static MPAlgorithmVersion toMPAlgorithmVersion(AlgorithmVersion version);
static MPSiteType toMPSiteType(PasswordType type);
Q_SIGNALS:
void generatedMasterKey(const QString &fingerprint);
protected Q_SLOTS:
void gotMasterKey(QByteArray *key, const QString &fingerprint);
private:
QString m_key;
AlgorithmVersion algVersionFromInt(const uint &version);
DBManager *m_db;
SitesSqlModel* m_model;
QString m_name;
QString m_fingerprint;
AlgorithmVersion m_algVersion;
QByteArray *m_key;
QSettings *m_settings;
};
#endif // MPWMANAGER_H
/*
The MIT License (MIT)
Copyright (c) 2016 Andrea Scarpino <me@andreascarpino.it>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "sitessqlmodel.h"
const static char* COLUMN_NAMES[] = {
"site",
"type",
"counter",
"timestamp",
NULL
};
const static QString SQL_SELECT = QStringLiteral("SELECT * FROM sites ORDER BY timestamp DESC;");
SitesSqlModel::SitesSqlModel(QObject *parent):
QSqlQueryModel(parent)
{
int idx = 0;
while (COLUMN_NAMES[idx]) {
m_roleNames[Qt::UserRole + idx + 1] = COLUMN_NAMES[idx];
idx++;
}
refresh();
}
void SitesSqlModel::refresh()
{
setQuery(SQL_SELECT);
}
QVariant SitesSqlModel::data(const QModelIndex &index, int role) const
{
QVariant value;
if (role < Qt::UserRole) {
value = QSqlQueryModel::data(index, role);
} else {
const int columnIdx = role - Qt::UserRole - 1;
const QModelIndex modelIndex = this->index(index.row(), columnIdx);
value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
}
return value;
}
QHash<int, QByteArray> SitesSqlModel::roleNames() const
{
return m_roleNames;
}
/*
The MIT License (MIT)
Copyright (c) 2016 Andrea Scarpino <me@andreascarpino.it>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef SITESSQLMODEL_H
#define SITESSQLMODEL_H
#include <QSqlQueryModel>
class SitesSqlModel : public QSqlQueryModel
{
Q_OBJECT
public:
explicit SitesSqlModel(QObject *parent = 0);
void refresh();
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual QHash<int,QByteArray> roleNames() const;
private:
QHash<int, QByteArray> m_roleNames;
};
#endif // SITESSQLMODEL_H
<?xml version="1.0" ?><!DOCTYPE TS><TS language="ar" version="2.1">
<context>
<name>MainPage</name>
<message>
<location filename="../qml/pages/MainPage.qml" line="51"/>
<source>Settings</source>
<translation>إعدادات</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="85"/>
<source>Copy to clipboard</source>
<translation>نسخ إلى الحافظة</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="107"/>
<source>Site name (e.g. google.com)</source>
<translation>إسم الموقع (مثلاُ google.com)</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="146"/>
<source>Counter</source>
<translation>عداد</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="132"/>
<source>PIN</source>
<translation>رمز شخصي</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="161"/>
<source>Touch here to set your master password or use the Settings page!</source>
<translation>إلمس هنا لكي يتم ضبط كلمة سرك الرئيسية او إستخدم صفحة الإعدادات!</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="170"/>
<source>Sites</source>
<translation>مواقع</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="131"/>
<source>Short</source>
<translation>قصير</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="130"/>
<source>Basic</source>
<translation>أولي</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="129"/>
<source>Medium</source>
<translation>متوسط</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="128"/>
<source>Long</source>
<translation>طويل</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="127"/>
<source>Maximum</source>
<translation>أعلى حد</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="58"/>
<source>Clear sites</source>
<translation>مواقع واضحة</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="73"/>
<source>Clear password</source>
<translation>كلمة سر واضحة</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="121"/>
<source>Type</source>
<translation>نوع</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="133"/>
<source>Name</source>
<translation>إسم</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="134"/>
<source>Phrase</source>
<translation>عبارة</translation>
</message>
</context>
<context>
<name>Settings</name>
<message>
<location filename="../qml/pages/Settings.qml" line="49"/>
<source>Settings</source>
<translation>إعدادات</translation>
</message>
<message>
<location filename="../qml/pages/Settings.qml" line="56"/>
<source>Full name</source>
<translation>إسم كاملاُ</translation>
</message>
<message>
<location filename="../qml/pages/Settings.qml" line="64"/>
<source>Master password</source>
<translation>كلمة السر الرئيسية</translation>
</message>
<message>
<location filename="../qml/pages/Settings.qml" line="72"/>
<source>Algorithm version</source>
<translation>إصدار الخوارزم</translation>
</message>
<message>
<location filename="../qml/pages/Settings.qml" line="97"/>
<source>Generate</source>
<translation>توليد</translation>
</message>
</context>
<context>
<name>SiteDelegate</name>
<message>
<location filename="../qml/pages/SiteDelegate.qml" line="43"/>
<source>Delete</source>
<translation>مسح</translation>
</message>
<message>
<location filename="../qml/pages/SiteDelegate.qml" line="76"/>
<source>Deleting</source>
<translation>يتم المسح</translation>
</message>
</context>
</TS>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS language="de" version="2.1">
<context>
<name>MainPage</name>
<message>
<location filename="../qml/pages/MainPage.qml" line="51"/>
<source>Settings</source>
<translation>Einstellungen</translation>
<