Fix parser and add unit tests.

This commit is contained in:
Felix Geyer
2010-08-13 18:08:06 +02:00
parent b64dbce2da
commit bd1ea05017
18 changed files with 690 additions and 103 deletions

View File

@@ -13,8 +13,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
configure_file( config-keepassx.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-keepassx.h )
set(keepassx_SOURCES
main.cpp
core/Database.cpp
core/DbAttribute.cpp
core/Entry.cpp
@@ -25,18 +28,7 @@ set(keepassx_SOURCES
core/Uuid.cpp
)
set(keepassx_HEADERS
core/Database.h
core/DbAttribute.h
core/Entry.h
core/Group.h
core/Metadata.h
core/Parser.h
core/TimeInfo.h
core/Uuid.h
)
automoc4_add_library( keepassx_core STATIC ${keepassx_SOURCES} )
qt4_wrap_cpp( keepassx_MOC ${keepassx_HEADERS} )
add_executable( ${PROGNAME} WIN32 MACOSX_BUNDLE ${keepassx_SOURCES} ${keepassx_MOC} )
target_link_libraries( ${PROGNAME} ${QT_LIBRARIES} )
add_executable( ${PROGNAME} WIN32 MACOSX_BUNDLE main.cpp )
target_link_libraries( ${PROGNAME} keepassx_core ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} )

View File

@@ -0,0 +1,3 @@
/* config-keepassx.h. Generated by cmake from config-keepassx.h.cmake */
#define KEEPASSX_VERSION "${KEEPASSX_VERSION}"

View File

@@ -20,12 +20,12 @@
#include <QtCore/QFile>
#include <QtCore/QXmlStreamReader>
#include "Metadata.h"
#include "Parser.h"
Database::Database(const QString& filename)
Database::Database()
{
m_filename = filename;
m_metadata = new Metadata(this);
}
Group* Database::rootGroup()
@@ -35,8 +35,8 @@ Group* Database::rootGroup()
void Database::setRootGroup(Group* group)
{
Q_ASSERT(group == 0 || group->parent() == this);
m_rootGroup = group;
group->setParent(this);
}
Metadata* Database::metadata()
@@ -44,12 +44,6 @@ Metadata* Database::metadata()
return m_metadata;
}
void Database::open()
{
Parser* parser = new Parser(this);
parser->parse(m_filename);
}
QImage Database::icon(int number)
{
// TODO implement

View File

@@ -32,7 +32,7 @@ class Database : public QObject
Q_OBJECT
public:
Database(const QString& filename);
Database();
Group* rootGroup();
void setRootGroup(Group* group);
Metadata* metadata();
@@ -42,11 +42,9 @@ public:
Group* resolveGroup(const Uuid& uuid);
private:
void open();
Entry* recFindEntry(const Uuid& uuid, Group* group);
Group* recFindGroup(const Uuid& uuid, Group* group);
QString m_filename;
Metadata* m_metadata;
Group* m_rootGroup;
QHash<Uuid, QImage> m_customIcons;

View File

@@ -19,8 +19,9 @@
#include "Group.h"
Entry::Entry() : m_group(0)
Entry::Entry()
{
m_group = 0;
}
Uuid Entry::uuid() const
@@ -31,6 +32,7 @@ Uuid Entry::uuid() const
QImage Entry::icon() const
{
// TODO implement
return QImage();
}
QColor Entry::foregroundColor() const
@@ -85,6 +87,7 @@ const QHash<QString, QByteArray>& Entry::attachments() const
void Entry::setUuid(const Uuid& uuid)
{
Q_ASSERT(!uuid.isNull());
m_uuid = uuid;
}
@@ -153,7 +156,7 @@ void Entry::addAttachment(const QString& key, const QByteArray& value)
void Entry::setGroup(Group* group)
{
if (m_group) {
group->removeEntry(this);
m_group->removeEntry(this);
}
group->addEntry(this);
m_group = group;

View File

@@ -22,8 +22,10 @@
#include "Database.h"
Group::Group() : m_parent(0)
Group::Group()
{
m_parent = 0;
m_db = 0;
}
Uuid Group::uuid() const
@@ -118,23 +120,38 @@ void Group::setLastTopVisibleEntry(Entry* entry)
void Group::setParent(Group* parent)
{
Q_ASSERT(parent != 0);
if (m_parent) {
m_parent->m_children.removeAll(this);
}
else if (m_db) {
m_db->setRootGroup(0);
}
m_parent = parent;
m_db = parent->m_db;
QObject::setParent(parent);
parent->m_children << this;
}
void Group::setParent(Database* db)
{
if (m_db) {
Q_ASSERT(db != 0);
if (m_parent) {
m_parent->m_children.removeAll(this);
}
else if (m_db) {
m_db->setRootGroup(0);
}
m_parent = 0;
m_db = db;
QObject::setParent(db);
db->setRootGroup(this);
}
QList<Group*> Group::children() const

View File

@@ -17,8 +17,16 @@
#include "Metadata.h"
Metadata::Metadata()
#include "Database.h"
Metadata::Metadata(Database* parent) : QObject(parent)
{
m_generator = "KeePassX";
m_maintenanceHistoryDays = 365;
m_recycleBin = 0;
m_entryTemplatesGroup = 0;
m_lastSelectedGroup = 0;
m_lastTopVisibleGroup = 0;
}
QString Metadata::generator() const
@@ -101,9 +109,9 @@ bool Metadata::recycleBinEnabled() const
return m_recycleBinEnabled;
}
Uuid Metadata::recycleBinUuid() const
const Group* Metadata::recycleBin() const
{
return m_recycleBinUuid;
return m_recycleBin;
}
QDateTime Metadata::recycleBinChanged() const
@@ -111,7 +119,7 @@ QDateTime Metadata::recycleBinChanged() const
return m_recycleBinChanged;
}
Uuid Metadata::entryTemplatesGroup() const
const Group* Metadata::entryTemplatesGroup() const
{
return m_entryTemplatesGroup;
}
@@ -121,12 +129,12 @@ QDateTime Metadata::entryTemplatesGroupChanged() const
return m_entryTemplatesGroupChanged;
}
Uuid Metadata::lastSelectedGroup() const
const Group* Metadata::lastSelectedGroup() const
{
return m_lastSelectedGroup;
}
Uuid Metadata::lastTopVisibleGroup() const
const Group* Metadata::lastTopVisibleGroup() const
{
return m_lastTopVisibleGroup;
}
@@ -208,6 +216,7 @@ void Metadata::setAutoEnableVisualHiding(bool value)
void Metadata::addCustomIcon(const Uuid& uuid, const QImage& image)
{
Q_ASSERT(!uuid.isNull());
Q_ASSERT(!m_customIcons.contains(uuid));
m_customIcons.insert(uuid, image);
@@ -215,6 +224,7 @@ void Metadata::addCustomIcon(const Uuid& uuid, const QImage& image)
void Metadata::removeCustomIcon(const Uuid& uuid)
{
Q_ASSERT(!uuid.isNull());
Q_ASSERT(m_customIcons.contains(uuid));
m_customIcons.remove(uuid);
@@ -225,9 +235,9 @@ void Metadata::setRecycleBinEnabled(bool value)
m_recycleBinEnabled = value;
}
void Metadata::setRecycleBinUuid(const Uuid& value)
void Metadata::setRecycleBin(Group* group)
{
m_recycleBinUuid = value;
m_recycleBin = group;
}
void Metadata::setRecycleBinChanged(const QDateTime& value)
@@ -235,9 +245,9 @@ void Metadata::setRecycleBinChanged(const QDateTime& value)
m_recycleBinChanged = value;
}
void Metadata::setEntryTemplatesGroup(const Uuid& value)
void Metadata::setEntryTemplatesGroup(Group* group)
{
m_entryTemplatesGroup = value;
m_entryTemplatesGroup = group;
}
void Metadata::setEntryTemplatesGroupChanged(const QDateTime& value)
@@ -245,14 +255,14 @@ void Metadata::setEntryTemplatesGroupChanged(const QDateTime& value)
m_entryTemplatesGroupChanged = value;
}
void Metadata::setLastSelectedGroup(const Uuid& value)
void Metadata::setLastSelectedGroup(Group* group)
{
m_lastSelectedGroup = value;
m_lastSelectedGroup = group;
}
void Metadata::setLastTopVisibleGroup(const Uuid& value)
void Metadata::setLastTopVisibleGroup(Group* group)
{
m_lastTopVisibleGroup = value;
m_lastTopVisibleGroup = group;
}
void Metadata::addCustomField(const QString& key, const QString& value)

View File

@@ -24,10 +24,15 @@
#include <QtCore/QHash>
#include <QtGui/QImage>
class Metadata
class Database;
class Group;
class Metadata : public QObject
{
Q_OBJECT
public:
Metadata();
Metadata(Database* parent);
QString generator() const;
QString name() const;
@@ -45,12 +50,12 @@ public:
bool autoEnableVisualHiding() const;
QHash<Uuid, QImage> customIcons() const;
bool recycleBinEnabled() const;
Uuid recycleBinUuid() const;
const Group* recycleBin() const;
QDateTime recycleBinChanged() const;
Uuid entryTemplatesGroup() const;
const Group* entryTemplatesGroup() const;
QDateTime entryTemplatesGroupChanged() const;
Uuid lastSelectedGroup() const;
Uuid lastTopVisibleGroup() const;
const Group* lastSelectedGroup() const;
const Group* lastTopVisibleGroup() const;
QHash<QString, QString> customFields() const;
void setGenerator(const QString& value);
@@ -70,12 +75,12 @@ public:
void addCustomIcon(const Uuid& uuid, const QImage& image);
void removeCustomIcon(const Uuid& uuid);
void setRecycleBinEnabled(bool value);
void setRecycleBinUuid(const Uuid& value);
void setRecycleBin(Group* group);
void setRecycleBinChanged(const QDateTime& value);
void setEntryTemplatesGroup(const Uuid& value);
void setEntryTemplatesGroup(Group* group);
void setEntryTemplatesGroupChanged(const QDateTime& value);
void setLastSelectedGroup(const Uuid& value);
void setLastTopVisibleGroup(const Uuid& value);
void setLastSelectedGroup(Group* group);
void setLastTopVisibleGroup(Group* group);
void addCustomField(const QString& key, const QString& value);
void removeCustomField(const QString& key);
@@ -99,12 +104,12 @@ private:
QHash<Uuid, QImage> m_customIcons;
bool m_recycleBinEnabled;
Uuid m_recycleBinUuid;
Group* m_recycleBin;
QDateTime m_recycleBinChanged;
Uuid m_entryTemplatesGroup;
Group* m_entryTemplatesGroup;
QDateTime m_entryTemplatesGroupChanged;
Uuid m_lastSelectedGroup;
Uuid m_lastTopVisibleGroup;
Group* m_lastSelectedGroup;
Group* m_lastTopVisibleGroup;
QHash<QString, QString> m_customFields;
};

View File

@@ -17,6 +17,7 @@
#include "Parser.h"
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include "Database.h"
@@ -35,24 +36,31 @@ bool Parser::parse(const QString& filename)
m_xml.setDevice(&file);
m_tmpParent = new Group();
m_tmpParent->setParent(m_db);
if (!m_xml.error() && m_xml.readNextStartElement()) {
if (m_xml.name() == "KeePassFile") {
parseKeePassFile();
}
else {
raiseError();
}
}
if (!m_tmpParent->children().isEmpty()) {
delete m_tmpParent;
if (!m_xml.error() && !m_tmpParent->children().isEmpty()) {
raiseError();
}
delete m_tmpParent;
return !m_xml.error();
}
QString Parser::errorMsg()
{
return QString("%1\nLine %2, column %3")
.arg(m_xml.errorString())
.arg(m_xml.lineNumber())
.arg(m_xml.columnNumber());
}
void Parser::parseKeePassFile()
{
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "KeePassFile");
@@ -65,7 +73,7 @@ void Parser::parseKeePassFile()
parseRoot();
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
}
@@ -109,28 +117,28 @@ void Parser::parseMeta()
m_meta->setRecycleBinEnabled(readBool());
}
else if (m_xml.name() == "RecycleBinUUID") {
m_meta->setRecycleBinUuid(readUuid());
m_meta->setRecycleBin(getGroup(readUuid()));
}
else if (m_xml.name() == "RecycleBinChanged") {
m_meta->setRecycleBinChanged(readDateTime());
}
else if (m_xml.name() == "EntryTemplatesGroup") {
m_meta->setEntryTemplatesGroup(readUuid());
m_meta->setEntryTemplatesGroup(getGroup(readUuid()));
}
else if (m_xml.name() == "EntryTemplatesGroupChanged") {
m_meta->setEntryTemplatesGroupChanged(readDateTime());
}
else if (m_xml.name() == "LastSelectedGroup") {
m_meta->setLastSelectedGroup(readUuid());
m_meta->setLastSelectedGroup(getGroup(readUuid()));
}
else if (m_xml.name() == "LastTopVisibleGroup") {
m_meta->setLastTopVisibleGroup(readUuid());
m_meta->setLastTopVisibleGroup(getGroup(readUuid()));
}
else if (m_xml.name() == "CustomData") {
parseCustomData();
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
}
@@ -159,7 +167,7 @@ void Parser::parseMemoryProtection()
m_meta->setAutoEnableVisualHiding(readBool());
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
}
@@ -173,7 +181,7 @@ void Parser::parseCustomIcons()
parseIcon();
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
}
@@ -193,7 +201,7 @@ void Parser::parseIcon()
m_meta->addCustomIcon(uuid, image);
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
}
@@ -202,7 +210,10 @@ void Parser::parseCustomData()
{
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "CustomData");
// TODO
// TODO implement
while (!m_xml.error() && m_xml.readNextStartElement()) {
skipCurrentElement();
}
}
void Parser::parseRoot()
@@ -218,9 +229,10 @@ void Parser::parseRoot()
}
else if (m_xml.name() == "DeletedObjects") {
// TODO implement
skipCurrentElement();
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
}
@@ -237,7 +249,7 @@ Group* Parser::parseGroup()
raiseError();
}
else {
group = getGroup(uuid);
group = getGroup(uuid);
}
}
else if (m_xml.name() == "Name") {
@@ -268,16 +280,14 @@ Group* Parser::parseGroup()
}
else if (m_xml.name() == "EnableAutoType") {
// TODO implement
skipCurrentElement();
}
else if (m_xml.name() == "EnableSearching") {
// TODO implement
skipCurrentElement();
}
else if (m_xml.name() == "LastTopVisibleEntry") {
Uuid uuid = readUuid();
if (uuid.isNull())
group->setLastTopVisibleEntry(0);
else
group->setLastTopVisibleEntry(getEntry(uuid));
group->setLastTopVisibleEntry(getEntry(readUuid()));
}
else if (m_xml.name() == "Group") {
Group* newGroup = parseGroup();
@@ -292,7 +302,7 @@ Group* Parser::parseGroup()
}
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
@@ -349,9 +359,11 @@ Entry* Parser::parseEntry()
parseEntryHistory();
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
return entry;
}
void Parser::parseEntryString(Entry *entry)
@@ -367,7 +379,7 @@ void Parser::parseEntryString(Entry *entry)
entry->addAttribute(key, readString());
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
}
@@ -385,7 +397,7 @@ void Parser::parseEntryBinary(Entry *entry)
entry->addAttachment(key, readBinary());
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
}
@@ -408,7 +420,7 @@ void Parser::parseAutoType(Entry* entry)
parseAutoTypeAssoc(entry);
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
}
@@ -427,7 +439,7 @@ void Parser::parseAutoTypeAssoc(Entry *entry)
entry->addAutoTypeAssociation(assoc);
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
}
@@ -439,9 +451,10 @@ void Parser::parseEntryHistory()
while (!m_xml.error() && m_xml.readNextStartElement()) {
if (m_xml.name() == "Entry") {
// TODO implement
skipCurrentElement();
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
}
@@ -474,7 +487,7 @@ TimeInfo Parser::parseTimes()
timeInfo.setLocationChanged(readDateTime());
}
else {
m_xml.skipCurrentElement();
skipCurrentElement();
}
}
@@ -490,14 +503,15 @@ bool Parser::readBool()
{
QString str = readString();
if (str == "True") {
if (str.compare(QLatin1String("True"), Qt::CaseInsensitive) == 0) {
return true;
}
else if (str == "False") {
else if (str.compare(QLatin1String("False"), Qt::CaseInsensitive) == 0) {
return false;
}
else {
raiseError();
return false;
}
}
@@ -516,6 +530,11 @@ QDateTime Parser::readDateTime()
QColor Parser::readColor()
{
QString colorStr = readString();
if (colorStr.isEmpty()) {
return QColor();
}
if (colorStr.length() != 7 || colorStr[0] != '#') {
raiseError();
return QColor();
@@ -525,7 +544,7 @@ QColor Parser::readColor()
for (int i=0; i<= 2; i++) {
QString rgbPartStr = colorStr.mid(1 + 2*i, 2);
bool ok;
int rgbPart = rgbPartStr.toInt(&ok);
int rgbPart = rgbPartStr.toInt(&ok, 16);
if (!ok || rgbPart > 255) {
raiseError();
return QColor();
@@ -563,7 +582,7 @@ Uuid Parser::readUuid()
return Uuid();
}
else {
return Uuid(readBinary());
return Uuid(uuidBin);
}
}
@@ -574,6 +593,10 @@ QByteArray Parser::readBinary()
Group* Parser::getGroup(const Uuid& uuid)
{
if (uuid.isNull()) {
return 0;
}
Q_FOREACH (Group* group, m_groups) {
if (group->uuid() == uuid) {
return group;
@@ -589,6 +612,10 @@ Group* Parser::getGroup(const Uuid& uuid)
Entry* Parser::getEntry(const Uuid& uuid)
{
if (uuid.isNull()) {
return 0;
}
Q_FOREACH (Entry* entry, m_entries) {
if (entry->uuid() == uuid) {
return entry;
@@ -606,3 +633,9 @@ void Parser::raiseError()
{
m_xml.raiseError(tr("Invalid database file"));
}
void Parser::skipCurrentElement()
{
qDebug() << "Parser::skipCurrentElement(): skip: " << m_xml.name();
m_xml.skipCurrentElement();
}

View File

@@ -37,6 +37,7 @@ class Parser : public QObject
public:
Parser(Database* db);
bool parse(const QString& filename);
QString errorMsg();
private:
void parseKeePassFile();
@@ -66,6 +67,7 @@ private:
Group* getGroup(const Uuid& uuid);
Entry* getEntry(const Uuid& uuid);
void raiseError();
void skipCurrentElement();
QXmlStreamReader m_xml;
Database* m_db;

View File

@@ -39,10 +39,9 @@ Uuid::Uuid(const QByteArray& data)
m_data = data;
}
QString Uuid::toString() const
QString Uuid::toBase64() const
{
return m_data.toHex();
return m_data.toBase64();
}
QByteArray Uuid::toByteArray() const

View File

@@ -27,7 +27,7 @@ public:
Uuid();
Uuid(bool generate);
Uuid(const QByteArray& data);
QString toString() const;
QString toBase64() const;
QByteArray toByteArray() const;
bool isNull() const;
bool operator==(const Uuid& other) const;