Commit b4a751c9 authored by Andrea Scarpino's avatar Andrea Scarpino

Save sites

parent 1bda2d4e
......@@ -2,19 +2,26 @@ TARGET = harbour-mpw
CONFIG += sailfishapp
QT += sql
SOURCES += \
src/main.cpp \
src/asyncmasterkey.cpp \
src/dbmanager.cpp \
src/mpwmanager.cpp \
src/asyncmasterkey.cpp
src/sitessqlmodel.cpp
HEADERS += \
src/asyncmasterkey.h \
src/dbmanager.h \
src/mpwmanager.h \
src/asyncmasterkey.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 \
......
......@@ -28,13 +28,16 @@ import harbour.mpw 1.0
Page {
property bool masterKey: false
allowedOrientations: Orientation.All
Connections {
target: manager
onGeneratedMasterKey: {
site.enabled = true;
masterKey = true;
siteUrl.enabled = true;
password.text = "";
}
}
......@@ -52,9 +55,30 @@ Page {
}
MenuItem {
text: qsTr("Clear")
id: clearSites
text: qsTr("Clear sites")
enabled: sites.count > 0
onClicked: {
if (masterKey) {
password.text = "";
}
enabled = false;
manager.clearSites();
}
}
MenuItem {
id: clearPwd
text: qsTr("Clear password")
enabled: false
onClicked: clear()
onClicked: {
enabled = false;
copy.enabled = false;
password.text = "";
}
}
MenuItem {
......@@ -78,31 +102,31 @@ Page {
}
TextField {
id: site
id: siteUrl
width: parent.width
inputMethodHints: Qt.ImhUrlCharactersOnly
placeholderText: qsTr("Site name (e.g. google.com)")
enabled: false
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
id: siteCounter
width: parent.width
text: "1"
inputMethodHints: Qt.ImhDigitsOnly
validator: RegExpValidator { regExp: /^[0-9]+$/ }
placeholderText: qsTr("Counter")
EnterKey.enabled: site.text.length > 0 && counter.text.length > 0
EnterKey.enabled: siteUrl.text.length > 0 && siteCounter.text.length > 0
EnterKey.onClicked: getPassword()
}
ComboBox {
id: type
id: sitePwdType
label: qsTr("Password type")
currentIndex: 1
......@@ -126,22 +150,25 @@ Page {
wrapMode: Text.Wrap
text: qsTr("Please fill your name and master password in the Settings page!")
}
SectionHeader {
text: qsTr("Sites")
}
Repeater {
id: sites
model: recentSites
delegate: SiteDelegate {}
}
}
VerticalScrollDecorator {}
}
function getPassword() {
password.text = manager.getPassword(site.text, type.currentIndex, counter.text);
password.text = manager.getPassword(siteUrl.text, sitePwdType.currentIndex, siteCounter.text);
clearPwd.enabled = true;
copy.enabled = true;
}
function clear() {
site.text = "";
site.forceActiveFocus();
counter.text = "1"
type.currentIndex = 1;
password.text = "";
copy.enabled = false;
}
}
/*
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
Column {
Label {
x: Theme.horizontalPageMargin
font.pixelSize: Theme.fontSizeMedium
text: site
wrapMode: Text.Wrap
}
Label {
width: item.width - Theme.horizontalPageMargin
font.pixelSize: Theme.fontSizeSmall
text: type
horizontalAlignment: Text.AlignRight
}
}
ContextMenu {
id: itemMenu
MenuItem {
text: qsTr("Delete")
onClicked: deleteSite(site)
}
}
onClicked: {
siteUrl.text = site;
sitePwdType.currentIndex = typeIndexFromString(type);
siteCounter.text = counter;
if (masterKey) {
password.text = "";
}
}
function typeIndexFromString(type) {
if (type === "Maximum") {
return 0;
} else if (type === "Long") {
return 1;
} else if (type === "Minium") {
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);
}
}
/*
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,6 +27,7 @@
#include <sailfishapp.h>
#include "mpwmanager.h"
#include "sitessqlmodel.h"
int main(int argc, char *argv[])
{
......@@ -40,6 +41,8 @@ int main(int argc, char *argv[])
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();
......
......@@ -30,9 +30,12 @@
#include <QThread>
#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);
......@@ -43,6 +46,8 @@ MPWManager::MPWManager(QObject *parent) :
MPWManager::~MPWManager()
{
delete m_db;
delete m_model;
delete m_key;
delete m_settings;
}
......@@ -102,6 +107,8 @@ QString MPWManager::getPassword(const QString &site, PasswordType type, const ui
toMPAlgorithmVersion(m_algVersion));
if (p) {
m_db->insert(site, type, counter);
m_model->refresh();
return QString::fromUtf8(p);
} else {
qCritical() << "Error during password generation";
......@@ -141,6 +148,25 @@ MPSiteType MPWManager::toMPSiteType(PasswordType type)
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) {
......
......@@ -35,7 +35,10 @@ extern "C"
#endif
}
#include "sitessqlmodel.h"
class QSettings;
class DBManager;
class MPWManager : public QObject
{
......@@ -62,6 +65,10 @@ public:
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);
......@@ -74,6 +81,8 @@ protected Q_SLOTS:
private:
AlgorithmVersion algVersionFromInt(const uint &version);
DBManager *m_db;
SitesSqlModel* m_model;
QString m_name;
AlgorithmVersion m_algVersion;
QByteArray *m_key;
......
/*
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
......@@ -4,77 +4,87 @@
<context>
<name>MainPage</name>
<message>
<location filename="../qml/pages/MainPage.qml" line="49"/>
<location filename="../qml/pages/MainPage.qml" line="52"/>
<source>Settings</source>
<translation>Impostazioni</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="62"/>
<location filename="../qml/pages/MainPage.qml" line="86"/>
<source>Copy to clipboard</source>
<translation>Copia negli appunti</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="55"/>
<source>Clear</source>
<translation>Cancella</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="84"/>
<location filename="../qml/pages/MainPage.qml" line="108"/>
<source>Site name (e.g. google.com)</source>
<translation>Sito web (google.com, ...)</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="97"/>
<location filename="../qml/pages/MainPage.qml" line="121"/>
<source>Counter</source>
<translation>Contatore</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="106"/>
<location filename="../qml/pages/MainPage.qml" line="130"/>
<source>Password type</source>
<translation>Tipologia password</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="115"/>
<location filename="../qml/pages/MainPage.qml" line="139"/>
<source>PIN</source>
<translation>PIN</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="127"/>
<location filename="../qml/pages/MainPage.qml" line="151"/>
<source>Please fill your name and master password in the Settings page!</source>
<translation>Perfavore immetti il tuo nome e password nella pagina Impostazioni!</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="114"/>
<location filename="../qml/pages/MainPage.qml" line="155"/>
<source>Sites</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="138"/>
<source>Short</source>
<translation>Corta</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="113"/>
<location filename="../qml/pages/MainPage.qml" line="137"/>
<source>Basic</source>
<translation>Semplice</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="112"/>
<location filename="../qml/pages/MainPage.qml" line="136"/>
<source>Medium</source>
<translation>Media</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="111"/>
<location filename="../qml/pages/MainPage.qml" line="135"/>
<source>Long</source>
<translation>Lunga</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="110"/>
<location filename="../qml/pages/MainPage.qml" line="134"/>
<source>Maximum</source>
<translation>Massima</translation>
</message>
<message>