X-Git-Url: https://vaikene.ee/gitweb/gitweb.cgi?p=evaf;a=blobdiff_plain;f=www%2Fpswgen07.html;fp=www%2Fpswgen07.html;h=d35d48405ace88d2fa9aeca056c7a8d51dce1732;hp=0000000000000000000000000000000000000000;hb=0a383af776fe4c1c0ac1059dd3732f32cb6e1c7a;hpb=adefe2aae67c52dc2aa41c45aedb8ad822478ee1 diff --git a/www/pswgen07.html b/www/pswgen07.html new file mode 100644 index 0000000..d35d484 --- /dev/null +++ b/www/pswgen07.html @@ -0,0 +1,563 @@ + + + +
+ +Create the module.cpp file in the src/apps/PswGen/Storage directory: + +
/** + * @file PswGen/Storage/module.cpp + */ + +#include "module.h" +#include <QtCore>+ +
Copy version information files from the Generator module:
+ +evaf/src/apps/PswGen/Storage $ cp ../Generator/version.{h,rc} .
+
+ Modify the version.h file:
+ +/** + * @file PswGen/Storage/version.h + */ + +#ifndef __PSWGEN_STORAGE_VERSION_H +#define __PSWGEN_STORAGE_VERSION_H + +#include <version_rc.h> + +/** + * Module/library version number in the form major,minor,release,build + */ +#define VER_FILE_VERSION 0,1,1,1 + +/** + * Module/library version number in the string format (shall end with \0) + */ +#define VER_FILE_VERSION_STR "0.1.1.1\0" + +/** + * Module/library name (shall end with \0) + */ +#define VER_MODULE_NAME_STR "PswStorage\0" + +/** + * Module type (see version_rc.h for all the types) + */ +#define VER_MODULE_TYPE MT_GENERIC + +/** + * Module type in the string format (see version_rc for all the types) + */ +#define VER_MODULE_TYPE_STR MT_GENERIC + +/** + * Original file name for windows (shall end with \0) + */ +#define VER_ORIGINAL_FILE_NAME_STR "PswStorage.dll\0" + +/** + * Description of the module/library (shall end with \0) + */ +#define VER_FILE_DESCRIPTION_STR "Module that stores data for generating strong passwords.\0" + +#endif // version.h+ +
Include the version.h header file in module.cpp and export version information:
+ ++#include "version.h" + +VER_EXPORT_VERSION_INFO()+ +
Make it a proper Qt plugin by using the Q_EXPORT_PLUGIN2() macro:
+ +Q_EXPORT_PLUGIN2(VER_MODULE_NAME_STR, eVaf::PswGen::Storage::Module)+ +
The Module class creates the internal StorageImpl object in the constructor, but initializes it in the + init() method. This way we can return errors from the module if initialization fails. Similarly, we finalize the + internal StorageImpl object in the done() method and delete in the destructor.
+ +The module is ready when the internal StorageImpl object is initalized and we set the mReady flag to + true when the initialization is done and back to false in the done() method.
+ +The rest of the code sets the name of the object and outputs info messages.
+ +Module::Module() + : Plugins::iPlugin() + , mReady(false) +{ + setObjectName(QString("%1.%2").arg(VER_MODULE_NAME_STR).arg(__FUNCTION__)); + + mStorage = new Internal::StorageImpl; + + EVAF_INFO("%s created", qPrintable(objectName())); +} + +Module::~Module() +{ + delete mStorage; + + EVAF_INFO("%s destroyed", qPrintable(objectName())); +} + +bool Module::init(QString const & args) +{ + Q_UNUSED(args); + + if (!mStorage->init()) + return false; + + mReady = true; + + EVAF_INFO("%s initialized", qPrintable(objectName())); + + return true; +} + +void Module::done() +{ + mReady = false; + + mStorage->done(); + + EVAF_INFO("%s finalized", qPrintable(objectName())); +}+ +
The StorageImpl class does very little in the constructor and in the destructor:
+ +StorageImpl::StorageImpl() + : QAbstractListModel() +{ + setObjectName(QString("%1.iGenerator").arg(VER_MODULE_NAME_STR)); + + EVAF_INFO("%s created", qPrintable(objectName())); +} + +StorageImpl::~StorageImpl() +{ + EVAF_INFO("%s destroyed", qPrintable(objectName())); +}+ +
Initialization of the StorageImpl class happens in the init() method, where we open the database connection, + create tables if necessary and load data from the database. We also register the iStorage interface in the global + registry of interfaces.
+ +We use the Common::iApp interface to find the data root directory where the SQLITE database file is going to be + located.
+ +bool StorageImpl::init() +{ + // Open the database + if (!QSqlDatabase::contains(DbConnectionName)) { + // No database connection yet + mDb = QSqlDatabase::addDatabase("QSQLITE", DbConnectionName); + mDb.setDatabaseName(Common::iApp::instance()->dataRootDir() + DbName); + if (!mDb.open()) { + QSqlError err = mDb.lastError(); + EVAF_ERROR("Failed to open database : %s", qPrintable(err.text())); + return false; + } + } + else { + // Database connection already exists + mDb = QSqlDatabase::database(DbConnectionName); + } + + // Create tables if necessary + if (!createTables()) + return false; + + // Load data + if (!loadData()) + return false; + + /// Register our interface + Common::iRegistry::instance()->registerInterface("iStorage", this); + + EVAF_INFO("%s initialized", qPrintable(objectName())); + + return true; +}+ +
The finalization of the StorageImpl class happens in the done() method, where we only need to clear the list + of shared data objects:
+ +void StorageImpl::done() +{ + mData.clear(); + EVAF_INFO("%s finalized", qPrintable(objectName())); +}+ +
The StorageImpl::save() method verifies that the shared data object is valid and the name not empty. Then it either + adds a new data record to the database or updates an existing one.
+ +bool StorageImpl::save(QString const & name, QExplicitlySharedDataPointer<Storage::Data> data) +{ + EVAF_TEST_X(data, "Data cannot be null"); + EVAF_TEST_X(!name.isEmpty(), "Name cannot be empty"); + + // Is it an update or a new data record? + if (mData.constFind(name) != mData.constEnd()) { + // This is an update + if (data->modified()) { + QSqlQuery q(mDb); + if (!q.exec(QString("UPDATE data SET length = \'%1\', flags = \'%2\' WHERE name = \'%3\';") + .arg(data->length()).arg(data->flags()).arg(name))) { + QSqlError err = mDb.lastError(); + EVAF_ERROR("Failed to update \'%s\' : %s", qPrintable(name), qPrintable(err.text())); + return false; + } + } + } + else { + // Store to the database + QSqlQuery q(mDb); + if (!q.exec(QString("INSERT INTO data (name, length, flags) VALUES (\'%1\', %2, %3);") + .arg(name).arg(data->length()) + .arg(int(data->flags())))) { + QSqlError err = mDb.lastError(); + EVAF_ERROR("Failed to insert \'%s\' : %s", qPrintable(name), qPrintable(err.text())); + return false; + } + + // Store also into the local hash + mData.insert(name, data); + + // Reset the model + reset(); + } + + data->reset(); + + return true; +}+ +
Calling the reset() method resets the QAbstractItemModel data model and assures that the associated widget + updates the QCompleter with a fresh list of auto completion words. Resetting the data model in this case is fine as + long as the data model is simple and the list short. The proper way would be finding out the actual insertion point and using + beginInsertRows() and endInsertRows() methods.
+ +Querying data is much simpler thanks to the internal QMap container:
+ +QExplicitlySharedDataPointer<Storage::Data> StorageImpl::query(QString const & name) const +{ + QMap<QString, QExplicitlySharedDataPointer<Storage::Data> >::const_iterator it = mData.constFind(name); + if (it != mData.constEnd()) + return it.value(); + else + return QExplicitlySharedDataPointer<Storage::Data>(); +}+ +
We return an empty QExplicitlySharedDataPointer object if no data objects with the given name exists.
+ +The data() method returns names of data objects for the QCompleter auto completion list of words:
+ +QVariant StorageImpl::data(QModelIndex const & index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= mData.size() || index.column() != 0) + return QVariant(); + + if (role == Qt::EditRole || role == Qt::DisplayRole) + return mData.keys().at(index.row()); + + return QVariant(); +}+ +
The createTabled() method creates the data table if it does not exist. This function could be improved to + alter the data table if it exists, but is from an older version. Right now we assume that if the table exists, it + is good for our application:
+ +bool StorageImpl::createTables() +{ + QSqlQuery q(mDb); + if (!q.exec("SELECT name FROM sqlite_master WHERE type=\'table\' AND name=\'data\';")) { + QSqlError err = mDb.lastError(); + EVAF_ERROR("Failed to query database : %s", qPrintable(err.text())); + return false; + } + + if (q.isActive() && q.isSelect() && q.first()) + return true; // We already have a table called 'data' + + // Create the 'data' table + if (!q.exec("CREATE TABLE data (name text primary key not null, length integer, flags integer);")) { + QSqlError err = mDb.lastError(); + EVAF_ERROR("Failed to create table \'data\' : %s", qPrintable(err.text())); + return false; + } + + return true; +}+ +
Finally, the loadData() method loads all the data records from the database to the memory:
+ +bool StorageImpl::loadData() +{ + QSqlQuery q(mDb); + if (!q.exec("SELECT name, length, flags FROM data;")) { + QSqlError err = mDb.lastError(); + EVAF_ERROR("Failed to query database : %s", qPrintable(err.text())); + return false; + } + + while (q.next()) { + QString name = q.value(0).toString(); + QExplicitlySharedDataPointer<Storage::Data> data(new Storage::Data(name, q.value(1).toInt(), uint(q.value(2).toInt()))); + mData.insert(name, data); + } + + reset(); + + return true; +}+ +
Resetting the QAbstractItemModel data model here is ok, becase before loading any data the model is supposed to be + empty.
+ +Here is the complete module.cpp file: + +
/** + * @file PswGen/Storage/module.cpp + */ + +#include "module.h" +#include "version.h" + +#include <Common/Globals> +#include <Common/iLogger> +#include <Common/iRegistry> +#include <Common/iApp> + +#include <QtCore> +#include <QtSql/QtSql> + +VER_EXPORT_VERSION_INFO() +Q_EXPORT_PLUGIN2(VER_MODULE_NAME_STR, eVaf::PswGen::Storage::Module) + +using namespace eVaf; +using namespace eVaf::PswGen; +using namespace eVaf::PswGen::Storage; + +Module::Module() + : Plugins::iPlugin() + , mReady(false) +{ + setObjectName(QString("%1.%2").arg(VER_MODULE_NAME_STR).arg(__FUNCTION__)); + + mStorage = new Internal::StorageImpl; + + EVAF_INFO("%s created", qPrintable(objectName())); +} + +Module::~Module() +{ + delete mStorage; + + EVAF_INFO("%s destroyed", qPrintable(objectName())); +} + +bool Module::init(QString const & args) +{ + Q_UNUSED(args); + + if (!mStorage->init()) + return false; + + mReady = true; + + EVAF_INFO("%s initialized", qPrintable(objectName())); + + return true; +} + +void Module::done() +{ + mReady = false; + + mStorage->done(); + + EVAF_INFO("%s finalized", qPrintable(objectName())); +} + +using namespace eVaf::PswGen::Storage::Internal; + +char const * const StorageImpl::DbConnectionName = "PswGenDB"; + +char const * const StorageImpl::DbName = "PswGen.sqlite"; + +StorageImpl::StorageImpl() + : QAbstractListModel() +{ + setObjectName(QString("%1.iGenerator").arg(VER_MODULE_NAME_STR)); + + EVAF_INFO("%s created", qPrintable(objectName())); +} + +StorageImpl::~StorageImpl() +{ + EVAF_INFO("%s destroyed", qPrintable(objectName())); +} + +bool StorageImpl::init() +{ + // Open the database + if (!QSqlDatabase::contains(DbConnectionName)) { + // No database connection yet + mDb = QSqlDatabase::addDatabase("QSQLITE", DbConnectionName); + mDb.setDatabaseName(Common::iApp::instance()->dataRootDir() + DbName); + if (!mDb.open()) { + QSqlError err = mDb.lastError(); + EVAF_ERROR("Failed to open database : %s", qPrintable(err.text())); + return false; + } + } + else { + // Database connection already exists + mDb = QSqlDatabase::database(DbConnectionName); + } + + // Create tables if necessary + if (!createTables()) + return false; + + // Load data + if (!loadData()) + return false; + + /// Register our interface + Common::iRegistry::instance()->registerInterface("iStorage", this); + + EVAF_INFO("%s initialized", qPrintable(objectName())); + + return true; +} + +void StorageImpl::done() +{ + mData.clear(); + EVAF_INFO("%s finalized", qPrintable(objectName())); +} + +bool StorageImpl::save(QString const & name, QExplicitlySharedDataPointer<Storage::Data> data) +{ + EVAF_TEST_X(data, "Data cannot be null"); + EVAF_TEST_X(!name.isEmpty(), "Name cannot be empty"); + + // Is it an update or a new data record? + if (mData.constFind(name) != mData.constEnd()) { + // This is an update + if (data->modified()) { + QSqlQuery q(mDb); + if (!q.exec(QString("UPDATE data SET length = \'%1\', flags = \'%2\' WHERE name = \'%3\';") + .arg(data->length()).arg(data->flags()).arg(name))) { + QSqlError err = mDb.lastError(); + EVAF_ERROR("Failed to update \'%s\' : %s", qPrintable(name), qPrintable(err.text())); + return false; + } + } + } + else { + // Store to the database + QSqlQuery q(mDb); + if (!q.exec(QString("INSERT INTO data (name, length, flags) VALUES (\'%1\', %2, %3);") + .arg(name).arg(data->length()) + .arg(int(data->flags())))) { + QSqlError err = mDb.lastError(); + EVAF_ERROR("Failed to insert \'%s\' : %s", qPrintable(name), qPrintable(err.text())); + return false; + } + + // Store also into the local hash + mData.insert(name, data); + + // Reset the model + reset(); + } + + data->reset(); + + return true; +} + +QExplicitlySharedDataPointer<Storage::Data> StorageImpl::query(QString const & name) const +{ + QMap<QString, QExplicitlySharedDataPointer<Storage::Data> >::const_iterator it = mData.constFind(name); + if (it != mData.constEnd()) + return it.value(); + else + return QExplicitlySharedDataPointer<Storage::Data>(); +} + +QVariant StorageImpl::data(QModelIndex const & index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= mData.size() || index.column() != 0) + return QVariant(); + + if (role == Qt::EditRole || role == Qt::DisplayRole) + return mData.keys().at(index.row()); + + return QVariant(); +} + +bool StorageImpl::createTables() +{ + QSqlQuery q(mDb); + if (!q.exec("SELECT name FROM sqlite_master WHERE type=\'table\' AND name=\'data\';")) { + QSqlError err = mDb.lastError(); + EVAF_ERROR("Failed to query database : %s", qPrintable(err.text())); + return false; + } + + if (q.isActive() && q.isSelect() && q.first()) + return true; // We already have a table called 'data' + + // Create the 'data' table + if (!q.exec("CREATE TABLE data (name text primary key not null, length integer, flags integer);")) { + QSqlError err = mDb.lastError(); + EVAF_ERROR("Failed to create table \'data\' : %s", qPrintable(err.text())); + return false; + } + + return true; +} + +bool StorageImpl::loadData() +{ + QSqlQuery q(mDb); + if (!q.exec("SELECT name, length, flags FROM data;")) { + QSqlError err = mDb.lastError(); + EVAF_ERROR("Failed to query database : %s", qPrintable(err.text())); + return false; + } + + while (q.next()) { + QString name = q.value(0).toString(); + QExplicitlySharedDataPointer<Storage::Data> data(new Storage::Data(name, q.value(1).toInt(), uint(q.value(2).toInt()))); + mData.insert(name, data); + } + + reset(); + + return true; +}+ +
Next -- Building Storage Module.
+ + +