Proper error handling for reading databases.

This commit is contained in:
Felix Geyer 2012-01-06 20:03:13 +01:00
parent fea148803c
commit 007a901dba
9 changed files with 62 additions and 48 deletions

View file

@ -38,7 +38,8 @@ KeePass2Reader::KeePass2Reader()
Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& key) Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& key)
{ {
m_db = new Database(); QScopedPointer<Database> db(new Database());
m_db = db.data();
m_device = device; m_device = device;
m_error = false; m_error = false;
m_errorStr = QString(); m_errorStr = QString();
@ -48,13 +49,13 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
quint32 signature1 = Endian::readUInt32(m_device, KeePass2::BYTEORDER, &ok); quint32 signature1 = Endian::readUInt32(m_device, KeePass2::BYTEORDER, &ok);
if (!ok || signature1 != KeePass2::SIGNATURE_1) { if (!ok || signature1 != KeePass2::SIGNATURE_1) {
raiseError("1"); raiseError(tr("Not a KeePass database."));
return 0; return 0;
} }
quint32 signature2 = Endian::readUInt32(m_device, KeePass2::BYTEORDER, &ok); quint32 signature2 = Endian::readUInt32(m_device, KeePass2::BYTEORDER, &ok);
if (!ok || signature2 != KeePass2::SIGNATURE_2) { if (!ok || signature2 != KeePass2::SIGNATURE_2) {
raiseError("2"); raiseError(tr("Not a KeePass database."));
return 0; return 0;
} }
@ -62,11 +63,11 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
quint32 expectedVersion = KeePass2::FILE_VERSION & KeePass2::FILE_VERSION_CRITICAL_MASK; quint32 expectedVersion = KeePass2::FILE_VERSION & KeePass2::FILE_VERSION_CRITICAL_MASK;
// TODO do we support old Kdbx versions? // TODO do we support old Kdbx versions?
if (!ok || (version != expectedVersion)) { if (!ok || (version != expectedVersion)) {
raiseError("3"); raiseError(tr("Unsupported KeePass database version."));
return 0; return 0;
} }
while (readHeaderField() && !error()) { while (readHeaderField() && !hasError()) {
} }
// TODO check if all header fields have been parsed // TODO check if all header fields have been parsed
@ -84,7 +85,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
QByteArray realStart = cipherStream.read(32); QByteArray realStart = cipherStream.read(32);
if (realStart != m_streamStartBytes) { if (realStart != m_streamStartBytes) {
raiseError("4"); raiseError(tr("Wrong key or database file is corrupt."));
return 0; return 0;
} }
@ -117,28 +118,41 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
KeePass2XmlReader xmlReader; KeePass2XmlReader xmlReader;
xmlReader.readDatabase(xmlDevice, m_db, &randomStream); xmlReader.readDatabase(xmlDevice, m_db, &randomStream);
// TODO forward error messages from xmlReader
return m_db; if (xmlReader.hasError()) {
raiseError(xmlReader.errorString());
return 0;
}
return db.take();
} }
Database* KeePass2Reader::readDatabase(const QString& filename, const CompositeKey& key) Database* KeePass2Reader::readDatabase(const QString& filename, const CompositeKey& key)
{ {
QFile file(filename); QFile file(filename);
file.open(QFile::ReadOnly); if (!file.open(QFile::ReadOnly)) {
Database* db = readDatabase(&file, key); raiseError(file.errorString());
// TODO check for QFile errors return 0;
return db; }
QScopedPointer<Database> db(readDatabase(&file, key));
if (file.error() != QFile::NoError) {
raiseError(file.errorString());
return 0;
}
return db.take();
} }
bool KeePass2Reader::error() bool KeePass2Reader::hasError()
{ {
return m_error; return m_error;
} }
QString KeePass2Reader::errorString() QString KeePass2Reader::errorString()
{ {
// TODO return m_errorStr;
return QString();
} }
void KeePass2Reader::setSaveXml(bool save) void KeePass2Reader::setSaveXml(bool save)
@ -153,9 +167,8 @@ QByteArray KeePass2Reader::xmlData()
void KeePass2Reader::raiseError(const QString& str) void KeePass2Reader::raiseError(const QString& str)
{ {
// TODO
qWarning("KeePass2Reader error: %s", qPrintable(str));
m_error = true; m_error = true;
m_errorStr = str;
} }
bool KeePass2Reader::readHeaderField() bool KeePass2Reader::readHeaderField()

View file

@ -33,7 +33,7 @@ public:
KeePass2Reader(); KeePass2Reader();
Database* readDatabase(QIODevice* device, const CompositeKey& key); Database* readDatabase(QIODevice* device, const CompositeKey& key);
Database* readDatabase(const QString& filename, const CompositeKey& key); Database* readDatabase(const QString& filename, const CompositeKey& key);
bool error(); bool hasError();
QString errorString(); QString errorString();
void setSaveXml(bool save); void setSaveXml(bool save);
QByteArray xmlData(); QByteArray xmlData();

View file

@ -34,6 +34,7 @@ KeePass2XmlReader::KeePass2XmlReader()
void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2RandomStream* randomStream) void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2RandomStream* randomStream)
{ {
m_xml.clear();
m_xml.setDevice(device); m_xml.setDevice(device);
m_db = db; m_db = db;
@ -48,9 +49,9 @@ void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2Ra
} }
} }
// TODO check if m_tmpParent doesn't have entries
if (!m_xml.error() && !m_tmpParent->children().isEmpty()) { if (!m_xml.error() && !m_tmpParent->children().isEmpty()) {
raiseError(); qWarning("KeePass2XmlReader::readDatabase: found %d invalid entry references",
m_tmpParent->children().size());
} }
delete m_tmpParent; delete m_tmpParent;
@ -70,7 +71,7 @@ Database* KeePass2XmlReader::readDatabase(const QString& filename)
return readDatabase(&file); return readDatabase(&file);
} }
bool KeePass2XmlReader::error() bool KeePass2XmlReader::hasError()
{ {
return m_xml.hasError(); return m_xml.hasError();
} }
@ -300,7 +301,7 @@ Group* KeePass2XmlReader::parseGroup()
if (m_xml.name() == "UUID") { if (m_xml.name() == "UUID") {
Uuid uuid = readUuid(); Uuid uuid = readUuid();
if (uuid.isNull()) { if (uuid.isNull()) {
raiseError(); raiseError(1);
} }
else { else {
group = getGroup(uuid); group = getGroup(uuid);
@ -315,7 +316,7 @@ Group* KeePass2XmlReader::parseGroup()
else if (m_xml.name() == "IconID") { else if (m_xml.name() == "IconID") {
int iconId = readNumber(); int iconId = readNumber();
if (iconId < 0) { if (iconId < 0) {
raiseError(); raiseError(2);
} }
else { else {
if (iconId >= databaseIcons()->iconCount()) { if (iconId >= databaseIcons()->iconCount()) {
@ -352,7 +353,7 @@ Group* KeePass2XmlReader::parseGroup()
group->setAutoTypeEnabled(Group::Disable); group->setAutoTypeEnabled(Group::Disable);
} }
else { else {
raiseError(); raiseError(3);
} }
} }
@ -369,7 +370,7 @@ Group* KeePass2XmlReader::parseGroup()
group->setSearchingEnabled(Group::Disable); group->setSearchingEnabled(Group::Disable);
} }
else { else {
raiseError(); raiseError(4);
} }
} }
else if (m_xml.name() == "LastTopVisibleEntry") { else if (m_xml.name() == "LastTopVisibleEntry") {
@ -419,7 +420,7 @@ void KeePass2XmlReader::parseDeletedObject()
if (m_xml.name() == "UUID") { if (m_xml.name() == "UUID") {
Uuid uuid = readUuid(); Uuid uuid = readUuid();
if (uuid.isNull()) { if (uuid.isNull()) {
raiseError(); raiseError(5);
} }
else { else {
delObj.uuid = uuid; delObj.uuid = uuid;
@ -445,7 +446,7 @@ Entry* KeePass2XmlReader::parseEntry(bool history)
if (m_xml.name() == "UUID") { if (m_xml.name() == "UUID") {
Uuid uuid = readUuid(); Uuid uuid = readUuid();
if (uuid.isNull()) { if (uuid.isNull()) {
raiseError(); raiseError(6);
} }
else { else {
if (history) { if (history) {
@ -460,7 +461,7 @@ Entry* KeePass2XmlReader::parseEntry(bool history)
else if (m_xml.name() == "IconID") { else if (m_xml.name() == "IconID") {
int iconId = readNumber(); int iconId = readNumber();
if (iconId < 0) { if (iconId < 0) {
raiseError(); raiseError(7);
} }
else { else {
entry->setIcon(iconId); entry->setIcon(iconId);
@ -498,7 +499,7 @@ Entry* KeePass2XmlReader::parseEntry(bool history)
} }
else if (m_xml.name() == "History") { else if (m_xml.name() == "History") {
if (history) { if (history) {
raiseError(); raiseError(8);
} }
else { else {
parseEntryHistory(entry); parseEntryHistory(entry);
@ -532,7 +533,7 @@ void KeePass2XmlReader::parseEntryString(Entry *entry)
value = m_randomStream->process(QByteArray::fromBase64(value.toAscii())); value = m_randomStream->process(QByteArray::fromBase64(value.toAscii()));
} }
else { else {
raiseError(); raiseError(9);
} }
} }
@ -679,7 +680,7 @@ bool KeePass2XmlReader::readBool()
return false; return false;
} }
else { else {
raiseError(); raiseError(10);
return false; return false;
} }
} }
@ -690,7 +691,7 @@ QDateTime KeePass2XmlReader::readDateTime()
QDateTime dt = QDateTime::fromString(str, Qt::ISODate); QDateTime dt = QDateTime::fromString(str, Qt::ISODate);
if (!dt.isValid()) { if (!dt.isValid()) {
raiseError(); raiseError(11);
} }
return dt; return dt;
@ -705,7 +706,7 @@ QColor KeePass2XmlReader::readColor()
} }
if (colorStr.length() != 7 || colorStr[0] != '#') { if (colorStr.length() != 7 || colorStr[0] != '#') {
raiseError(); raiseError(12);
return QColor(); return QColor();
} }
@ -715,7 +716,7 @@ QColor KeePass2XmlReader::readColor()
bool ok; bool ok;
int rgbPart = rgbPartStr.toInt(&ok, 16); int rgbPart = rgbPartStr.toInt(&ok, 16);
if (!ok || rgbPart > 255) { if (!ok || rgbPart > 255) {
raiseError(); raiseError(13);
return QColor(); return QColor();
} }
@ -738,7 +739,7 @@ int KeePass2XmlReader::readNumber()
bool ok; bool ok;
int result = readString().toInt(&ok); int result = readString().toInt(&ok);
if (!ok) { if (!ok) {
raiseError(); raiseError(14);
} }
return result; return result;
} }
@ -747,7 +748,7 @@ Uuid KeePass2XmlReader::readUuid()
{ {
QByteArray uuidBin = readBinary(); QByteArray uuidBin = readBinary();
if (uuidBin.length() != Uuid::LENGTH) { if (uuidBin.length() != Uuid::LENGTH) {
raiseError(); raiseError(15);
return Uuid(); return Uuid();
} }
else { else {
@ -798,9 +799,9 @@ Entry* KeePass2XmlReader::getEntry(const Uuid& uuid)
return entry; return entry;
} }
void KeePass2XmlReader::raiseError() void KeePass2XmlReader::raiseError(int internalNumber)
{ {
m_xml.raiseError(tr("Invalid database file")); m_xml.raiseError(tr("Invalid database file (error no %1).").arg(QString::number(internalNumber)));
} }
void KeePass2XmlReader::skipCurrentElement() void KeePass2XmlReader::skipCurrentElement()

View file

@ -42,7 +42,7 @@ public:
Database* readDatabase(QIODevice* device); Database* readDatabase(QIODevice* device);
void readDatabase(QIODevice* device, Database* db, KeePass2RandomStream* randomStream = 0); void readDatabase(QIODevice* device, Database* db, KeePass2RandomStream* randomStream = 0);
Database* readDatabase(const QString& filename); Database* readDatabase(const QString& filename);
bool error(); bool hasError();
QString errorString(); QString errorString();
private: private:
@ -75,7 +75,7 @@ private:
Group* getGroup(const Uuid& uuid); Group* getGroup(const Uuid& uuid);
Entry* getEntry(const Uuid& uuid); Entry* getEntry(const Uuid& uuid);
void raiseError(); void raiseError(int internalNumber);
void skipCurrentElement(); void skipCurrentElement();
QXmlStreamReader m_xml; QXmlStreamReader m_xml;

View file

@ -41,7 +41,7 @@ void TestKeePass2Reader::testNonAscii()
KeePass2Reader* reader = new KeePass2Reader(); KeePass2Reader* reader = new KeePass2Reader();
Database* db = reader->readDatabase(filename, key); Database* db = reader->readDatabase(filename, key);
QVERIFY(db); QVERIFY(db);
QVERIFY(!reader->error()); QVERIFY(!reader->hasError());
QCOMPARE(db->metadata()->name(), QString("NonAsciiTest")); QCOMPARE(db->metadata()->name(), QString("NonAsciiTest"));
delete db; delete db;
@ -56,7 +56,7 @@ void TestKeePass2Reader::testCompressed()
KeePass2Reader* reader = new KeePass2Reader(); KeePass2Reader* reader = new KeePass2Reader();
Database* db = reader->readDatabase(filename, key); Database* db = reader->readDatabase(filename, key);
QVERIFY(db); QVERIFY(db);
QVERIFY(!reader->error()); QVERIFY(!reader->hasError());
QCOMPARE(db->metadata()->name(), QString("Compressed")); QCOMPARE(db->metadata()->name(), QString("Compressed"));
delete db; delete db;
@ -71,7 +71,7 @@ void TestKeePass2Reader::testProtectedStrings()
KeePass2Reader* reader = new KeePass2Reader(); KeePass2Reader* reader = new KeePass2Reader();
Database* db = reader->readDatabase(filename, key); Database* db = reader->readDatabase(filename, key);
QVERIFY(db); QVERIFY(db);
QVERIFY(!reader->error()); QVERIFY(!reader->hasError());
QCOMPARE(db->metadata()->name(), QString("Protected Strings Test")); QCOMPARE(db->metadata()->name(), QString("Protected Strings Test"));
Entry* entry = db->rootGroup()->entries().at(0); Entry* entry = db->rootGroup()->entries().at(0);

View file

@ -60,7 +60,7 @@ void TestKeePass2Writer::initTestCase()
buffer.seek(0); buffer.seek(0);
KeePass2Reader reader; KeePass2Reader reader;
m_dbTest = reader.readDatabase(&buffer, key); m_dbTest = reader.readDatabase(&buffer, key);
QVERIFY(!reader.error()); QVERIFY(!reader.hasError());
QVERIFY(m_dbTest); QVERIFY(m_dbTest);
} }

View file

@ -71,7 +71,7 @@ void TestKeePass2XmlReader::initTestCase()
QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml"); QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml");
m_db = reader->readDatabase(xmlFile); m_db = reader->readDatabase(xmlFile);
QVERIFY(m_db); QVERIFY(m_db);
QVERIFY(!reader->error()); QVERIFY(!reader->hasError());
} }
void TestKeePass2XmlReader::testMetadata() void TestKeePass2XmlReader::testMetadata()

View file

@ -77,7 +77,7 @@ void TestKeys::testFileKey()
Database* db = reader.readDatabase(dbFilename, compositeKey); Database* db = reader.readDatabase(dbFilename, compositeKey);
QVERIFY(db); QVERIFY(db);
QVERIFY(!reader.error()); QVERIFY(!reader.hasError());
QCOMPARE(db->metadata()->name(), QString("%1 Database").arg(name)); QCOMPARE(db->metadata()->name(), QString("%1 Database").arg(name));
delete db; delete db;
@ -122,7 +122,7 @@ void TestKeys::testCreateFileKey()
KeePass2Reader reader; KeePass2Reader reader;
Database* dbRead = reader.readDatabase(&dbBuffer, compositeKey); Database* dbRead = reader.readDatabase(&dbBuffer, compositeKey);
QVERIFY(dbRead); QVERIFY(dbRead);
QVERIFY(!reader.error()); QVERIFY(!reader.hasError());
QCOMPARE(dbRead->metadata()->name(), dbName); QCOMPARE(dbRead->metadata()->name(), dbName);
delete dbRead; delete dbRead;
} }

View file

@ -57,7 +57,7 @@ int main(int argc, char **argv)
reader.setSaveXml(true); reader.setSaveXml(true);
reader.readDatabase(&dbFile, key); reader.readDatabase(&dbFile, key);
if (reader.error()) { if (reader.hasError()) {
qCritical("Error while reading the database."); qCritical("Error while reading the database.");
return 1; return 1;
} }