diff --git a/src/format/KeePass1Reader.cpp b/src/format/KeePass1Reader.cpp index 1390d2021..926399293 100644 --- a/src/format/KeePass1Reader.cpp +++ b/src/format/KeePass1Reader.cpp @@ -56,6 +56,9 @@ KeePass1Reader::KeePass1Reader() Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& password, QIODevice* keyfileDevice) { + m_error = false; + m_errorStr.clear(); + QByteArray keyfileData; FileKey newFileKey; @@ -63,13 +66,16 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor keyfileData = readKeyfile(keyfileDevice); if (keyfileData.isEmpty()) { + raiseError(tr("Unable to read keyfile.").append("\n").append(keyfileDevice->errorString())); return Q_NULLPTR; } if (!keyfileDevice->seek(0)) { + raiseError(tr("Unable to read keyfile.").append("\n").append(keyfileDevice->errorString())); return Q_NULLPTR; } if (!newFileKey.load(keyfileDevice)) { + raiseError(tr("Unable to read keyfile.").append("\n").append(keyfileDevice->errorString())); return Q_NULLPTR; } } @@ -79,8 +85,6 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor m_db = db.data(); m_tmpParent = tmpParent.data(); m_device = device; - m_error = false; - m_errorStr = QString(); bool ok; @@ -111,43 +115,43 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor m_masterSeed = m_device->read(16); if (m_masterSeed.size() != 16) { - // TODO: error + raiseError("Unable to read master seed"); return Q_NULLPTR; } m_encryptionIV = m_device->read(16); if (m_encryptionIV.size() != 16) { - // TODO: error + raiseError("Unable to read encryption IV"); return Q_NULLPTR; } quint32 numGroups = Endian::readUInt32(m_device, KeePass1::BYTEORDER, &ok); if (!ok) { - // TODO: error + raiseError("Invalid number of groups"); return Q_NULLPTR; } quint32 numEntries = Endian::readUInt32(m_device, KeePass1::BYTEORDER, &ok); if (!ok) { - // TODO: error + raiseError("Invalid number of entries"); return Q_NULLPTR; } m_contentHashHeader = m_device->read(32); if (m_contentHashHeader.size() != 32) { - // TODO: error + raiseError("Invalid content hash size"); return Q_NULLPTR; } m_transformSeed = m_device->read(32); if (m_transformSeed.size() != 32) { - // TODO: error + raiseError("Invalid transform seed size"); return Q_NULLPTR; } m_transformRounds = Endian::readUInt32(m_device, KeePass1::BYTEORDER, &ok); if (!ok) { - // TODO: error + raiseError("Invalid number of transform rounds"); return Q_NULLPTR; } m_db->setTransformRounds(m_transformRounds); @@ -157,7 +161,7 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor QScopedPointer cipherStream(testKeys(password, keyfileData, contentPos)); if (!cipherStream) { - // TODO: error + raiseError("Unable to create cipher stream"); return Q_NULLPTR; } @@ -180,6 +184,7 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor } if (!constructGroupTree(groups)) { + raiseError("Unable to construct group tree"); return Q_NULLPTR; } @@ -337,7 +342,12 @@ SymmetricCipherStream* KeePass1Reader::testKeys(const QString& password, const Q cipherStream->reset(); cipherStream->close(); if (!m_device->seek(contentPos)) { - // TODO: error + QString msg = "unable to seek to content position"; + if (!m_device->errorString().isEmpty()) { + msg.append("\n").append(m_device->errorString()); + } + raiseError(msg); + return Q_NULLPTR; } cipherStream->open(QIODevice::ReadOnly); @@ -402,17 +412,19 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream) do { quint16 fieldType = Endian::readUInt16(cipherStream, KeePass1::BYTEORDER, &ok); if (!ok) { + raiseError("Invalid group field type number"); return Q_NULLPTR; } int fieldSize = static_cast(Endian::readUInt32(cipherStream, KeePass1::BYTEORDER, &ok)); if (!ok) { + raiseError("Invalid group field size"); return Q_NULLPTR; } QByteArray fieldData = cipherStream->read(fieldSize); if (fieldData.size() != fieldSize) { - // TODO: error + raiseError("Read group field data doesn't match size"); return Q_NULLPTR; } @@ -422,6 +434,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream) break; case 0x0001: if (fieldSize != 4) { + raiseError("Incorrect group id field size"); return Q_NULLPTR; } groupId = Endian::bytesToUInt32(fieldData, KeePass1::BYTEORDER); @@ -433,6 +446,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream) case 0x0003: { if (fieldSize != 5) { + raiseError("Incorrect group creation time field size"); return Q_NULLPTR; } QDateTime dateTime = dateFromPackedStruct(fieldData); @@ -444,6 +458,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream) case 0x0004: { if (fieldSize != 5) { + raiseError("Incorrect group modification time field size"); return Q_NULLPTR; } QDateTime dateTime = dateFromPackedStruct(fieldData); @@ -455,7 +470,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream) case 0x0005: { if (fieldSize != 5) { - return Q_NULLPTR; + raiseError("Incorrect group access time field size"); } QDateTime dateTime = dateFromPackedStruct(fieldData); if (dateTime.isValid()) { @@ -466,7 +481,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream) case 0x0006: { if (fieldSize != 5) { - return Q_NULLPTR; + raiseError("Incorrect group expiry time field size"); } QDateTime dateTime = dateFromPackedStruct(fieldData); if (dateTime.isValid()) { @@ -478,6 +493,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream) case 0x0007: { if (fieldSize != 4) { + raiseError("Incorrect group icon field size"); return Q_NULLPTR; } quint32 iconNumber = Endian::bytesToUInt32(fieldData, KeePass1::BYTEORDER); @@ -487,6 +503,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream) case 0x0008: { if (fieldSize != 2) { + raiseError("Incorrect group level field size"); return Q_NULLPTR; } groupLevel = Endian::bytesToUInt16(fieldData, KeePass1::BYTEORDER); @@ -501,11 +518,13 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream) break; default: // invalid field + raiseError("Invalid group field type"); return Q_NULLPTR; } } while (!reachedEnd); if (!groupIdSet || !groupLevelSet) { + raiseError("Missing group id or level"); return Q_NULLPTR; } @@ -531,17 +550,19 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream) do { quint16 fieldType = Endian::readUInt16(cipherStream, KeePass1::BYTEORDER, &ok); if (!ok) { + raiseError("Missing entry field type number"); return Q_NULLPTR; } int fieldSize = static_cast(Endian::readUInt32(cipherStream, KeePass1::BYTEORDER, &ok)); if (!ok) { + raiseError("Invalid entry field size"); return Q_NULLPTR; } QByteArray fieldData = cipherStream->read(fieldSize); if (fieldData.size() != fieldSize) { - // TODO: error + raiseError("Read entry field data doesn't match size"); return Q_NULLPTR; } @@ -551,6 +572,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream) break; case 0x0001: if (fieldSize != 16) { + raiseError("Invalid entry uuid field size"); return Q_NULLPTR; } m_entryUuids.insert(fieldData, entry.data()); @@ -558,6 +580,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream) case 0x0002: { if (fieldSize != 4) { + raiseError("Invalid entry group id field size"); return Q_NULLPTR; } quint32 groupId = Endian::bytesToUInt32(fieldData, KeePass1::BYTEORDER); @@ -567,6 +590,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream) case 0x0003: { if (fieldSize != 4) { + raiseError("Invalid entry icon field size"); return Q_NULLPTR; } quint32 iconNumber = Endian::bytesToUInt32(fieldData, KeePass1::BYTEORDER); @@ -591,6 +615,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream) case 0x0009: { if (fieldSize != 5) { + raiseError("Invalid entry creation time field size"); return Q_NULLPTR; } QDateTime dateTime = dateFromPackedStruct(fieldData); @@ -602,6 +627,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream) case 0x000A: { if (fieldSize != 5) { + raiseError("Invalid entry modification time field size"); return Q_NULLPTR; } QDateTime dateTime = dateFromPackedStruct(fieldData); @@ -613,6 +639,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream) case 0x000B: { if (fieldSize != 5) { + raiseError("Invalid entry creation time field size"); return Q_NULLPTR; } QDateTime dateTime = dateFromPackedStruct(fieldData); @@ -624,6 +651,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream) case 0x000C: { if (fieldSize != 5) { + raiseError("Invalid entry expiry time field size"); return Q_NULLPTR; } QDateTime dateTime = dateFromPackedStruct(fieldData); @@ -646,6 +674,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream) break; default: // invalid field + raiseError("Invalid entry field type"); return Q_NULLPTR; } } while (!reachedEnd); @@ -870,10 +899,10 @@ bool KeePass1Reader::parseCustomIcons4(const QByteArray& data) return true; } -void KeePass1Reader::raiseError(const QString& str) +void KeePass1Reader::raiseError(const QString& errorMessage) { m_error = true; - m_errorStr = str; + m_errorStr = errorMessage; } QDateTime KeePass1Reader::dateFromPackedStruct(const QByteArray& data) diff --git a/src/format/KeePass1Reader.h b/src/format/KeePass1Reader.h index 208bd4b52..056a21504 100644 --- a/src/format/KeePass1Reader.h +++ b/src/format/KeePass1Reader.h @@ -62,7 +62,7 @@ private: void parseMetaStream(const Entry* entry); bool parseGroupTreeState(const QByteArray& data); bool parseCustomIcons4(const QByteArray& data); - void raiseError(const QString& str); + void raiseError(const QString& errorMessage); static QByteArray readKeyfile(QIODevice* device); static QDateTime dateFromPackedStruct(const QByteArray& data); static bool isMetaStream(const Entry* entry); diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp index 3eb3fe977..91482316c 100644 --- a/src/format/KeePass2Reader.cpp +++ b/src/format/KeePass2Reader.cpp @@ -33,8 +33,9 @@ #include "streams/SymmetricCipherStream.h" KeePass2Reader::KeePass2Reader() + : m_error(false) + , m_saveXml(false) { - m_saveXml = false; } Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& key) @@ -43,7 +44,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke m_db = db.data(); m_device = device; m_error = false; - m_errorStr = QString(); + m_errorStr.clear(); m_headerEnd = false; m_xmlData.clear(); m_masterSeed.clear(); @@ -83,11 +84,15 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke headerStream.close(); + if (hasError()) { + return Q_NULLPTR; + } + // check if all required headers were present if (m_masterSeed.isEmpty() || m_transformSeed.isEmpty() || m_encryptionIV.isEmpty() || m_streamStartBytes.isEmpty() || m_protectedStreamKey.isEmpty() || m_db->cipher().isNull()) { - raiseError(""); + raiseError("missing database headers"); return Q_NULLPTR; } @@ -149,7 +154,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke if (!xmlReader.headerHash().isEmpty()) { QByteArray headerHash = CryptoHash::hash(headerStream.storedData(), CryptoHash::Sha256); if (headerHash != xmlReader.headerHash()) { - raiseError(""); + raiseError("Head doesn't match hash"); return Q_NULLPTR; } } @@ -195,17 +200,17 @@ QByteArray KeePass2Reader::xmlData() return m_xmlData; } -void KeePass2Reader::raiseError(const QString& str) +void KeePass2Reader::raiseError(const QString& errorMessage) { m_error = true; - m_errorStr = str; + m_errorStr = errorMessage; } bool KeePass2Reader::readHeaderField() { QByteArray fieldIDArray = m_headerStream->read(1); if (fieldIDArray.size() != 1) { - raiseError(""); + raiseError("Invalid header id size"); return false; } quint8 fieldID = fieldIDArray.at(0); @@ -213,7 +218,7 @@ bool KeePass2Reader::readHeaderField() bool ok; quint16 fieldLen = Endian::readUInt16(m_headerStream, KeePass2::BYTEORDER, &ok); if (!ok) { - raiseError(""); + raiseError("Invalid header field length"); return false; } @@ -221,7 +226,7 @@ bool KeePass2Reader::readHeaderField() if (fieldLen != 0) { fieldData = m_headerStream->read(fieldLen); if (fieldData.size() != fieldLen) { - raiseError(""); + raiseError("Invalid header data length"); return false; } } @@ -278,13 +283,13 @@ bool KeePass2Reader::readHeaderField() void KeePass2Reader::setCipher(const QByteArray& data) { if (data.size() != Uuid::Length) { - raiseError(""); + raiseError("Invalid cipher uuid length"); } else { Uuid uuid(data); if (uuid != KeePass2::CIPHER_AES) { - raiseError(""); + raiseError("Unsupported cipher"); } else { m_db->setCipher(uuid); @@ -295,13 +300,13 @@ void KeePass2Reader::setCipher(const QByteArray& data) void KeePass2Reader::setCompressionFlags(const QByteArray& data) { if (data.size() != 4) { - raiseError(""); + raiseError("Invalid compression flags length"); } else { quint32 id = Endian::bytesToUInt32(data, KeePass2::BYTEORDER); if (id > Database::CompressionAlgorithmMax) { - raiseError(""); + raiseError("Unsupported compression algorithm"); } else { m_db->setCompressionAlgo(static_cast(id)); @@ -312,7 +317,7 @@ void KeePass2Reader::setCompressionFlags(const QByteArray& data) void KeePass2Reader::setMasterSeed(const QByteArray& data) { if (data.size() != 32) { - raiseError(""); + raiseError("Invalid master seed size"); } else { m_masterSeed = data; @@ -322,7 +327,7 @@ void KeePass2Reader::setMasterSeed(const QByteArray& data) void KeePass2Reader::setTransformSeed(const QByteArray& data) { if (data.size() != 32) { - raiseError(""); + raiseError("Invalid transform seed size"); } else { m_transformSeed = data; @@ -332,7 +337,7 @@ void KeePass2Reader::setTransformSeed(const QByteArray& data) void KeePass2Reader::setTansformRounds(const QByteArray& data) { if (data.size() != 8) { - raiseError(""); + raiseError("Invalid transform rounds size"); } else { m_db->setTransformRounds(Endian::bytesToUInt64(data, KeePass2::BYTEORDER)); @@ -342,7 +347,7 @@ void KeePass2Reader::setTansformRounds(const QByteArray& data) void KeePass2Reader::setEncryptionIV(const QByteArray& data) { if (data.size() != 16) { - raiseError(""); + raiseError("Invalid encryption iv size"); } else { m_encryptionIV = data; @@ -352,7 +357,7 @@ void KeePass2Reader::setEncryptionIV(const QByteArray& data) void KeePass2Reader::setProtectedStreamKey(const QByteArray& data) { if (data.size() != 32) { - raiseError(""); + raiseError("Invalid stream key size"); } else { m_protectedStreamKey = data; @@ -362,7 +367,7 @@ void KeePass2Reader::setProtectedStreamKey(const QByteArray& data) void KeePass2Reader::setStreamStartBytes(const QByteArray& data) { if (data.size() != 32) { - raiseError(""); + raiseError("Invalid start bytes size"); } else { m_streamStartBytes = data; @@ -372,13 +377,13 @@ void KeePass2Reader::setStreamStartBytes(const QByteArray& data) void KeePass2Reader::setInnerRandomStreamID(const QByteArray& data) { if (data.size() != 4) { - raiseError(""); + raiseError("Invalid random stream id size"); } else { quint32 id = Endian::bytesToUInt32(data, KeePass2::BYTEORDER); if (id != KeePass2::Salsa20) { - raiseError(""); + raiseError("Unsupported random stream algorithm"); } } } diff --git a/src/format/KeePass2Reader.h b/src/format/KeePass2Reader.h index 4b38de570..8af87adeb 100644 --- a/src/format/KeePass2Reader.h +++ b/src/format/KeePass2Reader.h @@ -39,7 +39,7 @@ public: QByteArray xmlData(); private: - void raiseError(const QString& str); + void raiseError(const QString& errorMessage); bool readHeaderField(); diff --git a/src/format/KeePass2Writer.cpp b/src/format/KeePass2Writer.cpp index 91ad84159..2a6b4eb9a 100644 --- a/src/format/KeePass2Writer.cpp +++ b/src/format/KeePass2Writer.cpp @@ -43,7 +43,7 @@ KeePass2Writer::KeePass2Writer() void KeePass2Writer::writeDatabase(QIODevice* device, Database* db) { m_error = false; - m_errorStr = QString(); + m_errorStr.clear(); QByteArray masterSeed = Random::randomArray(32); QByteArray encryptionIV = Random::randomArray(16); @@ -117,8 +117,7 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db) bool KeePass2Writer::writeData(const QByteArray& data) { if (m_device->write(data) != data.size()) { - m_error = true; - m_errorStr = m_device->errorString(); + raiseError(m_device->errorString()); return false; } else { @@ -143,11 +142,14 @@ bool KeePass2Writer::writeHeaderField(KeePass2::HeaderFieldID fieldId, const QBy void KeePass2Writer::writeDatabase(const QString& filename, Database* db) { QFile file(filename); - file.open(QIODevice::WriteOnly|QIODevice::Truncate); + if (!file.open(QIODevice::WriteOnly|QIODevice::Truncate)) { + raiseError(file.errorString()); + return; + } writeDatabase(&file, db); } -bool KeePass2Writer::error() +bool KeePass2Writer::hasError() { return m_error; } @@ -156,3 +158,9 @@ QString KeePass2Writer::errorString() { return m_errorStr; } + +void KeePass2Writer::raiseError(const QString& errorMessage) +{ + m_error = true; + m_errorStr = errorMessage; +} diff --git a/src/format/KeePass2Writer.h b/src/format/KeePass2Writer.h index 1d9dde419..1b3436dc6 100644 --- a/src/format/KeePass2Writer.h +++ b/src/format/KeePass2Writer.h @@ -30,12 +30,13 @@ public: KeePass2Writer(); void writeDatabase(QIODevice* device, Database* db); void writeDatabase(const QString& filename, Database* db); - bool error(); + bool hasError(); QString errorString(); private: bool writeData(const QByteArray& data); bool writeHeaderField(KeePass2::HeaderFieldID fieldId, const QByteArray& data); + void raiseError(const QString& errorMessage); QIODevice* m_device; bool m_error; diff --git a/src/format/KeePass2XmlReader.cpp b/src/format/KeePass2XmlReader.cpp index df62daf50..ecf5c0484 100644 --- a/src/format/KeePass2XmlReader.cpp +++ b/src/format/KeePass2XmlReader.cpp @@ -34,11 +34,15 @@ KeePass2XmlReader::KeePass2XmlReader() : m_randomStream(Q_NULLPTR) , m_db(Q_NULLPTR) , m_meta(Q_NULLPTR) + , m_error(false) { } void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2RandomStream* randomStream) { + m_error = false; + m_errorStr.clear(); + m_xml.clear(); m_xml.setDevice(device); @@ -60,7 +64,7 @@ void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2Ra } if (!m_xml.error() && !rootGroupParsed) { - raiseError(28); + raiseError("No root group"); } if (!m_xml.error()) { @@ -81,7 +85,7 @@ void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2Ra QSet unusedKeys = poolKeys - entryKeys; if (!unmappedKeys.isEmpty()) { - raiseError(17); + raiseError("Unmapped keys left."); } if (!m_xml.error()) { @@ -131,15 +135,29 @@ Database* KeePass2XmlReader::readDatabase(const QString& filename) bool KeePass2XmlReader::hasError() { - return m_xml.hasError(); + return m_error || m_xml.hasError(); } QString KeePass2XmlReader::errorString() { - return QString("%1\nLine %2, column %3") - .arg(m_xml.errorString()) - .arg(m_xml.lineNumber()) - .arg(m_xml.columnNumber()); + if (m_error) { + return m_errorStr; + } + else if (m_xml.hasError()) { + return QString("XML error:\n%1\nLine %2, column %3") + .arg(m_xml.errorString()) + .arg(m_xml.lineNumber()) + .arg(m_xml.columnNumber()); + } + else { + return QString(); + } +} + +void KeePass2XmlReader::raiseError(const QString& errorMessage) +{ + m_error = true; + m_errorStr = errorMessage; } QByteArray KeePass2XmlReader::headerHash() @@ -161,7 +179,7 @@ bool KeePass2XmlReader::parseKeePassFile() else if (m_xml.name() == "Root") { if (rootElementFound) { rootParsedSuccesfully = false; - raiseError(29); + raiseError("Multiple root elements"); } else { rootParsedSuccesfully = parseRoot(); @@ -253,7 +271,7 @@ void KeePass2XmlReader::parseMeta() m_meta->setHistoryMaxItems(value); } else { - raiseError(18); + raiseError("HistoryMaxItems invalid number"); } } else if (m_xml.name() == "HistoryMaxSize") { @@ -262,7 +280,7 @@ void KeePass2XmlReader::parseMeta() m_meta->setHistoryMaxSize(value); } else { - raiseError(19); + raiseError("HistoryMaxSize invalid number"); } } else if (m_xml.name() == "Binaries") { @@ -347,7 +365,7 @@ void KeePass2XmlReader::parseIcon() m_meta->addCustomIcon(uuid, icon); } else { - raiseError(20); + raiseError("Missing icon uuid or data"); } } @@ -423,7 +441,7 @@ void KeePass2XmlReader::parseCustomDataItem() m_meta->addCustomField(key, value); } else { - raiseError(21); + raiseError("Missing custom data key or value"); } } @@ -438,7 +456,7 @@ bool KeePass2XmlReader::parseRoot() if (m_xml.name() == "Group") { if (groupElementFound) { groupParsedSuccesfully = false; - raiseError(30); + raiseError("Multiple group elements"); continue; } @@ -475,7 +493,7 @@ Group* KeePass2XmlReader::parseGroup() if (m_xml.name() == "UUID") { Uuid uuid = readUuid(); if (uuid.isNull()) { - raiseError(1); + raiseError("Null group uuid"); } else { group->setUuid(uuid); @@ -490,7 +508,7 @@ Group* KeePass2XmlReader::parseGroup() else if (m_xml.name() == "IconID") { int iconId = readNumber(); if (iconId < 0) { - raiseError(2); + raiseError("Invalid group icon number"); } else { if (iconId >= DatabaseIcons::IconCount) { @@ -527,7 +545,7 @@ Group* KeePass2XmlReader::parseGroup() group->setAutoTypeEnabled(Group::Disable); } else { - raiseError(3); + raiseError("Invalid EnableAutoType value"); } } else if (m_xml.name() == "EnableSearching") { @@ -543,7 +561,7 @@ Group* KeePass2XmlReader::parseGroup() group->setSearchingEnabled(Group::Disable); } else { - raiseError(4); + raiseError("Invalid EnableSearching value"); } } else if (m_xml.name() == "LastTopVisibleEntry") { @@ -573,8 +591,8 @@ Group* KeePass2XmlReader::parseGroup() group->setUpdateTimeinfo(false); delete tmpGroup; } - else { - raiseError(22); + else if (!hasError()) { + raiseError("No group uuid found"); } Q_FOREACH (Group* child, children) { @@ -612,7 +630,7 @@ void KeePass2XmlReader::parseDeletedObject() if (m_xml.name() == "UUID") { Uuid uuid = readUuid(); if (uuid.isNull()) { - raiseError(5); + raiseError("Null DeleteObject uuid"); } else { delObj.uuid = uuid; @@ -630,7 +648,7 @@ void KeePass2XmlReader::parseDeletedObject() m_db->addDeletedObject(delObj); } else { - raiseError(23); + raiseError("Missing DeletedObject uuid or time"); } } @@ -647,7 +665,7 @@ Entry* KeePass2XmlReader::parseEntry(bool history) if (m_xml.name() == "UUID") { Uuid uuid = readUuid(); if (uuid.isNull()) { - raiseError(6); + raiseError("Null entry uuid"); } else { entry->setUuid(uuid); @@ -656,7 +674,7 @@ Entry* KeePass2XmlReader::parseEntry(bool history) else if (m_xml.name() == "IconID") { int iconId = readNumber(); if (iconId < 0) { - raiseError(7); + raiseError("Invalud entry icon number"); } else { entry->setIcon(iconId); @@ -697,7 +715,7 @@ Entry* KeePass2XmlReader::parseEntry(bool history) } else if (m_xml.name() == "History") { if (history) { - raiseError(8); + raiseError("History element in history entry"); } else { historyItems = parseEntryHistory(); @@ -708,20 +726,22 @@ Entry* KeePass2XmlReader::parseEntry(bool history) } } - if (entry->uuid().isNull()) { - raiseError(24); - } - else if (history) { - entry->setUpdateTimeinfo(false); - } - else { - Entry* tmpEntry = entry; + if (!entry->uuid().isNull()) { + if (history) { + entry->setUpdateTimeinfo(false); + } + else { + Entry* tmpEntry = entry; - entry = getEntry(tmpEntry->uuid()); - entry->copyDataFrom(tmpEntry); - entry->setUpdateTimeinfo(false); + entry = getEntry(tmpEntry->uuid()); + entry->copyDataFrom(tmpEntry); + entry->setUpdateTimeinfo(false); - delete tmpEntry; + delete tmpEntry; + } + } + else if (!hasError()) { + raiseError("No entry uuid found"); } Q_FOREACH (Entry* historyItem, historyItems) { @@ -762,7 +782,7 @@ void KeePass2XmlReader::parseEntryString(Entry* entry) value = QString::fromUtf8(m_randomStream->process(QByteArray::fromBase64(value.toAscii()))); } else { - raiseError(9); + raiseError("Unable to decrypt entry string"); } } @@ -778,7 +798,7 @@ void KeePass2XmlReader::parseEntryString(Entry* entry) entry->attributes()->set(key, value, protect); } else { - raiseError(25); + raiseError("Entry string key or value missing"); } } @@ -827,7 +847,7 @@ QPair KeePass2XmlReader::parseEntryBinary(Entry* entry) entry->attachments()->set(key, value); } else { - raiseError(26); + raiseError("Entry binary key or value missing"); } return poolRef; @@ -882,7 +902,7 @@ void KeePass2XmlReader::parseAutoTypeAssoc(Entry* entry) entry->autoTypeAssociations()->add(assoc); } else { - raiseError(27); + raiseError("Auto-type association window or sequence missing"); } } @@ -955,7 +975,7 @@ bool KeePass2XmlReader::readBool() return false; } else { - raiseError(10); + raiseError("Invalid bool value"); return false; } } @@ -966,7 +986,7 @@ QDateTime KeePass2XmlReader::readDateTime() QDateTime dt = QDateTime::fromString(str, Qt::ISODate); if (!dt.isValid()) { - raiseError(11); + raiseError("Invalid date time value"); } return dt; @@ -981,7 +1001,7 @@ QColor KeePass2XmlReader::readColor() } if (colorStr.length() != 7 || colorStr[0] != '#') { - raiseError(12); + raiseError("Invalid color value"); return QColor(); } @@ -991,7 +1011,7 @@ QColor KeePass2XmlReader::readColor() bool ok; int rgbPart = rgbPartStr.toInt(&ok, 16); if (!ok || rgbPart > 255) { - raiseError(13); + raiseError("Invalid color rgb part"); return QColor(); } @@ -1014,7 +1034,7 @@ int KeePass2XmlReader::readNumber() bool ok; int result = readString().toInt(&ok); if (!ok) { - raiseError(14); + raiseError("Invalid number value"); } return result; } @@ -1023,7 +1043,7 @@ Uuid KeePass2XmlReader::readUuid() { QByteArray uuidBin = readBinary(); if (uuidBin.length() != Uuid::Length) { - raiseError(15); + raiseError("Invalid uuid value"); return Uuid(); } else { @@ -1049,7 +1069,7 @@ QByteArray KeePass2XmlReader::readCompressedBinary() QByteArray result; if (!Tools::readAllFromDevice(&compressor, result)) { - raiseError(16); + raiseError("Unable to decompress binary"); } return result; } @@ -1092,11 +1112,6 @@ Entry* KeePass2XmlReader::getEntry(const Uuid& uuid) } } -void KeePass2XmlReader::raiseError(int internalNumber) -{ - m_xml.raiseError(tr("Invalid database file (error no %1).").arg(QString::number(internalNumber))); -} - void KeePass2XmlReader::skipCurrentElement() { qWarning("KeePass2XmlReader::skipCurrentElement: skip element \"%s\"", qPrintable(m_xml.name().toString())); diff --git a/src/format/KeePass2XmlReader.h b/src/format/KeePass2XmlReader.h index bfae0fd97..1eb41898b 100644 --- a/src/format/KeePass2XmlReader.h +++ b/src/format/KeePass2XmlReader.h @@ -80,7 +80,7 @@ private: Group* getGroup(const Uuid& uuid); Entry* getEntry(const Uuid& uuid); - void raiseError(int internalNumber); + void raiseError(const QString& errorMessage); void skipCurrentElement(); QXmlStreamReader m_xml; @@ -93,6 +93,8 @@ private: QHash m_binaryPool; QHash > m_binaryMap; QByteArray m_headerHash; + bool m_error; + QString m_errorStr; }; #endif // KEEPASSX_KEEPASS2XMLREADER_H diff --git a/src/streams/HashedBlockStream.cpp b/src/streams/HashedBlockStream.cpp index 1085242c7..39e16de2e 100644 --- a/src/streams/HashedBlockStream.cpp +++ b/src/streams/HashedBlockStream.cpp @@ -129,18 +129,21 @@ bool HashedBlockStream::readHashedBlock() quint32 index = Endian::readUInt32(m_baseDevice, ByteOrder, &ok); if (!ok || index != m_blockIndex) { m_error = true; + setErrorString("Invalid block index."); return false; } QByteArray hash = m_baseDevice->read(32); if (hash.size() != 32) { m_error = true; + setErrorString("Invalid hash size."); return false; } m_blockSize = Endian::readInt32(m_baseDevice, ByteOrder, &ok); if (!ok || m_blockSize < 0) { m_error = true; + setErrorString("Invalid block size."); return false; } @@ -157,6 +160,7 @@ bool HashedBlockStream::readHashedBlock() m_buffer = m_baseDevice->read(m_blockSize); if (m_buffer.size() != m_blockSize) { m_error = true; + setErrorString("Block too short."); return false; } diff --git a/tests/TestKeePass1Reader.cpp b/tests/TestKeePass1Reader.cpp index 79764817f..2bcd72c7a 100644 --- a/tests/TestKeePass1Reader.cpp +++ b/tests/TestKeePass1Reader.cpp @@ -273,7 +273,7 @@ void TestKeePass1Reader::reopenDatabase(Database* db, const QString& password, c KeePass2Writer writer; writer.writeDatabase(&buffer, db); - QVERIFY(!writer.error()); + QVERIFY(!writer.hasError()); QVERIFY(buffer.seek(0)); CompositeKey key; diff --git a/tests/TestKeePass2Writer.cpp b/tests/TestKeePass2Writer.cpp index cf9ed15c0..233634883 100644 --- a/tests/TestKeePass2Writer.cpp +++ b/tests/TestKeePass2Writer.cpp @@ -61,7 +61,7 @@ void TestKeePass2Writer::initTestCase() KeePass2Writer writer; writer.writeDatabase(&buffer, m_dbOrg); - QVERIFY(!writer.error()); + QVERIFY(!writer.hasError()); buffer.seek(0); KeePass2Reader reader; m_dbTest = reader.readDatabase(&buffer, key);