Next: 05 - Building Generator Module, Previous: 03 - Generator Module

eVaf Tutorial

04 - Generator Module

Now we are going to implement all the classes declared in the module.h. Create the module.cpp file in the src/apps/PswGen/Generator directory. We obviously include the module.h header file, but also the QtCore header file for any non-GUI Qt classes.

/**
 * @file src/apps/PswGen/Generator/module.cpp
 */

#include "module.h"

#include <QtCore>

All the eVaf modules need to include version information. This is common for all the modules and we can simply copy existing version info files from another eVaf module:

evaf/src/apps/PswGen/Generator $ cp ../../../plugins/SdiWindow/version.{h,rc} .

The version.h file contains version information for the module. The version.rc is for Windows builds only and embeds the same version information into the dll or exe file. Modify the copied version.h file for our new module. The version.rc file uses the values from the version.h and does not need to be touched.

/**
 * @file src/apps/PswGen/Generator/version.h
 */
#ifndef __PSWGEN_GENERATOR_VERSION_H
#define __PSWGEN_GENERATOR_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             "PswGen\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      "PswGen.dll\0"

/**
 * Description of the module/library (shall end with \0)
 */
#define VER_FILE_DESCRIPTION_STR         "Module that generates strong passwords using MD5 hashes.\0"

#endif // version.h

Then include the version info file in the module.cpp file and use the VER_EXPORT_VERSION_INFO() macro to export version information from the module. This macro defines a public function that all the eVaf modules export and is used to collect version information from them. In your modules you only need to modify the version.h file and then use the VER_EXPORT_VERSION_INFO() macro once somewhere in your code.

#include "version.h"

VER_EXPORT_VERSION_INFO()

To make the Module class a proper Qt plugin, we use the Q_EXPORT_PLUGIN2 macro. The name of the module is already defined in the version.h header file as VER_MODULE_NAME_STR.

Q_EXPORT_PLUGIN2(VER_MODULE_NAME_STR, eVaf::PswGen::Generator::Module)

We make our life easier with several using namespace keywords:

using namespace eVaf;
using namespace eVaf::PswGen;
using namespace eVaf::PswGen::Generator;

The Module class needs to instantiate the iGenerator interface in the constructor. We also need to set the QObject's name property to the name of the plugin by combining the name of the module with the name of the class. While the application would work without the name property, it makes our life much easier if the name property is set.

Finally, we output an info message telling that the object was created. Every eVaf module and class is expected to output info messages when they are created, destroyed, initialized or destroyed.

Module::Module()
    : Plugins::iPlugin()
{
    setObjectName(QString("%1.%2").arg(VER_MODULE_NAME_STR).arg(__FUNCTION__));

    mGenerator = new Internal::GeneratorImpl;

    EVAF_INFO("%s created", qPrintable(objectName()));
}

The EVAF_INFO macro comes from the Common/iLogger header file ane we need to include it:

#include <Common/iLogger>

The destructor should delete the iGenerator interface object, which we created in the constructor. The common rule is that any resources allocated in the constructor shall be released in the destructor, preferrably in the opposite order, ie. the first resource allocated in the constructor is released last in the destructor.

Module::~Module()
{
    delete mGenerator;

    EVAF_INFO("%s destroyed", qPrintable(objectName()));
}

We also need to implement init() and done() functions, which are used to initialize and finalize modules. They are similar to the constructor and destructor with two major differences:

  1. The init() function can fail and return false to indicate a failure. eVaf does not use exceptions and this is the only way for a module to fail without terminating the whole application. A failed module will be disabled and the rest of the application can still run if it can.
  2. eVaf modules are loaded in two steps. At first, all the modules are created, which means that all the objects are constructed. Only then will eVaf call init() functions meaning that when the init() function is called, all the modules are already loaded and instantiated. Interfaces and resources from other modules that might be not available when the object is created, are available when the init() function is called.

The rule for init() and done() functions is the same than for constructors and destructors -- any resource allocated in the init() function shall be released in the done() function and preferrably in the opposite order.

This simple module needs no extra resources to be allocated and our init() and done() functions can be the following:

bool Module::init(QString const & args)
{
    Q_UNUSED(args);

    EVAF_INFO("%s initialized", qPrintable(objectName()));

    return true;
}

void Module::done()
{
    EVAF_INFO("%s finalized", qPrintable(objectName()));
}

We continue by implementing the iGenerator interface. There are no resources to be allocated in the constructor and we just set the QObject's name property and output the info message.

We also register the iGenerator interface in the global registry so that other modules can query for it and use our interface. This is done by using the Common::iRegistry interface, which we need to include:

#include <Common/iRegistry>

The GeneratorImpl class was declared in the eVaf::PswGen::Generator::Internal namespace, so we need another using namespace keyword before the implementation of the class:

using namespace eVaf::PswGen::Generator::Internal;

GeneratorImpl::GeneratorImpl()
    : iGenerator()
{
    setObjectName(QString("%1.iGenerator").arg(VER_MODULE_NAME_STR));

    Common::iRegistry::instance()->registerInterface("iGenerator", this);

    EVAF_INFO("%s created", qPrintable(objectName()));
}

GeneratorImpl::~GeneratorImpl()
{
    EVAF_INFO("%s destroyed", qPrintable(objectName()));
}

Finally, we write the generatePassword function that does the actual job of the module.

We use the MD5 cryptographic hash function to calculate a hash value over the name and masterPassword values. The result, which is a binary blob, needs to be convert into something that can be used as a password and we use base 64 encoding for this and cut the result to the requested length:

QString GeneratorImpl::generatePassword(QString const & name, QString const & masterPassword, int length, uint flags) const
{
    Q_UNUSED(flags);

    QByteArray inputString = QString("%1%2").arg(name).arg(masterPassword).toLatin1();
    QCryptographicHash hash(QCryptographicHash::Md5);
    hash.addData(inputString);
    QByteArray result = hash.result().toBase64();
    if (length > 0)
        return result.left(length);
    else
        return result;
}

We also know now the maximum length of the generated password, which is 24. Go back to the module.h header file and modify the GeneratorImpl::maxLength() function:

virtual int maxLength() const { return 24; }

Here is the final module.cpp file:

/**
 * @file src/apps/PswGen/Generator/module.cpp
 */

#include "module.h"
#include "version.h"

#include <Common/iLogger>
#include <Common/iRegistry>

#include <QtCore>

VER_EXPORT_VERSION_INFO()
Q_EXPORT_PLUGIN2(VER_MODULE_NAME_STR, eVaf::PswGen::Generator::Module)

using namespace eVaf;
using namespace eVaf::PswGen;
using namespace eVaf::PswGen::Generator;

Module::Module()
    : Plugins::iPlugin()
{
    setObjectName(QString("%1.%2").arg(VER_MODULE_NAME_STR).arg(__FUNCTION__));

    mGenerator = new Internal::GeneratorImpl;

    EVAF_INFO("%s created", qPrintable(objectName()));
}

Module::~Module()
{
    delete mGenerator;

    EVAF_INFO("%s destroyed", qPrintable(objectName()));
}

bool Module::init(QString const & args)
{
    Q_UNUSED(args);

    EVAF_INFO("%s initialized", qPrintable(objectName()));

    return true;
}

void Module::done()
{
    EVAF_INFO("%s finalized", qPrintable(objectName()));
}

using namespace eVaf::PswGen::Generator::Internal;

GeneratorImpl::GeneratorImpl()
    : iGenerator()
{
    setObjectName(QString("%1.iGenerator").arg(VER_MODULE_NAME_STR));

    Common::iRegistry::instance()->registerInterface("iGenerator", this);

    EVAF_INFO("%s created", qPrintable(objectName()));
}

GeneratorImpl::~GeneratorImpl()
{
    EVAF_INFO("%s destroyed", qPrintable(objectName()));
}

QString GeneratorImpl::generatePassword(QString const & name, QString const & masterPassword, int length, uint flags) const
{
    Q_UNUSED(flags);

    QByteArray inputString = QString("%1%2").arg(name).arg(masterPassword).toLatin1();
    QCryptographicHash hash(QCryptographicHash::Md5);
    hash.addData(inputString);
    QByteArray result = hash.result().toBase64();
    if (length > 0)
        return result.left(length);
    else
        return result;
}

Next -- 05 - Building Generator Module.