76 : m_path(other.m_path)
77 , m_password(other.m_password)
78 , m_rootEntry(other.m_rootEntry ? make_unique<
NodeEntry>(*other.m_rootEntry) : nullptr)
79 , m_extendedHeader(other.m_extendedHeader)
80 , m_encryptedExtendedHeader(other.m_encryptedExtendedHeader)
81 , m_freader(BinaryReader(&m_file))
82 , m_fwriter(BinaryWriter(&m_file))
83 , m_version(other.m_version)
84 , m_openOptions(other.m_openOptions)
85 , m_saveOptions(other.m_saveOptions)
87 m_file.exceptions(ios_base::failbit | ios_base::badbit);
94 : m_path(std::move(other.m_path))
95 , m_password(std::move(other.m_password))
96 , m_rootEntry(std::move(other.m_rootEntry))
97 , m_extendedHeader(std::move(other.m_extendedHeader))
98 , m_encryptedExtendedHeader(std::move(other.m_encryptedExtendedHeader))
99 , m_file(std::move(other.m_file))
100 , m_freader(BinaryReader(&m_file))
101 , m_fwriter(BinaryWriter(&m_file))
102 , m_version(other.m_version)
103 , m_openOptions(other.m_openOptions)
104 , m_saveOptions(other.m_saveOptions)
179 if (!m_file.is_open()) {
187 if (m_freader.readUInt32LE() != 0x7770616DU) {
192 m_version = m_freader.readUInt32LE();
193 if (m_version > 0x6U) {
194 throw ParsingException(argsToString(
"Version \"", m_version,
"\" is unknown. Only versions 0 to 6 are supported."));
196 if (m_version >= 0x6U) {
199 bool decrypterUsed, ivUsed, compressionUsed;
200 if (m_version >= 0x3U) {
201 const auto flags = m_freader.readByte();
202 if ((decrypterUsed = flags & 0x80)) {
205 if ((compressionUsed = flags & 0x20)) {
208 ivUsed = flags & 0x40;
210 if ((decrypterUsed = m_version >= 0x1U)) {
213 compressionUsed =
false;
214 ivUsed = m_version == 0x2U;
220 if (m_version >= 0x4U) {
221 std::uint16_t extendedHeaderSize = m_freader.readUInt16BE();
222 m_extendedHeader = m_freader.readString(extendedHeaderSize);
224 m_extendedHeader.clear();
228 const auto headerSize =
static_cast<size_t>(m_file.tellg());
229 m_file.seekg(0, ios_base::end);
230 auto remainingSize =
static_cast<size_t>(m_file.tellg()) - headerSize;
231 m_file.seekg(
static_cast<streamoff
>(headerSize), ios_base::beg);
234 uint32_t hashCount = 0U;
236 if (remainingSize < 4) {
239 hashCount = m_freader.readUInt32BE();
245 if (decrypterUsed && ivUsed) {
252 if (!remainingSize) {
257 vector<char> rawData;
258 m_freader.read(rawData,
static_cast<streamoff
>(remainingSize));
259 vector<char> decryptedData;
261 if (remainingSize > numeric_limits<int>::max()) {
270 for (uint32_t i = 1; i < hashCount; ++i) {
278 EVP_CIPHER_CTX *ctx =
nullptr;
280 int outlen1, outlen2;
281 if ((ctx = EVP_CIPHER_CTX_new()) ==
nullptr || EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(),
nullptr,
password.data, iv) != 1
282 || EVP_DecryptUpdate(ctx,
reinterpret_cast<unsigned char *
>(decryptedData.data()), &outlen1,
283 reinterpret_cast<unsigned char *
>(rawData.data()),
static_cast<int>(remainingSize))
285 || EVP_DecryptFinal_ex(ctx,
reinterpret_cast<unsigned char *
>(decryptedData.data()) + outlen1, &outlen2) != 1) {
288 EVP_CIPHER_CTX_free(ctx);
291 auto errorCode = ERR_get_error();
296 msg += ERR_error_string(errorCode,
nullptr);
297 errorCode = ERR_get_error();
303 EVP_CIPHER_CTX_free(ctx);
305 const auto decryptedSize = outlen1 + outlen2;
306 if (decryptedSize < 0) {
309 remainingSize =
static_cast<size_t>(decryptedSize);
310 if (!remainingSize) {
316 decryptedData.swap(rawData);
320 if (compressionUsed) {
321 if (remainingSize < 8) {
324 if (remainingSize > numeric_limits<uLongf>::max()) {
327 const auto rawDecompressedSize = LE::toUInt64(decryptedData.data());
328 if (rawDecompressedSize > numeric_limits<uLongf>::max()) {
331 auto decompressedSize =
static_cast<uLongf
>(rawDecompressedSize);
332 rawData.resize(decompressedSize);
333 switch (uncompress(
reinterpret_cast<Bytef *
>(rawData.data()), &decompressedSize,
reinterpret_cast<Bytef *
>(decryptedData.data() + 8),
334 static_cast<uLongf
>(remainingSize - 8))) {
336 throw ParsingException(
"Decompressing failed. The source buffer was too small.");
338 throw ParsingException(
"Decompressing failed. The destination buffer was too small.");
340 throw ParsingException(
"Decompressing failed. The input data was corrupted or incomplete.");
342 decryptedData.swap(rawData);
343 remainingSize = decompressedSize;
346 if (!remainingSize) {
351 stringstream decryptedStream(stringstream::in | stringstream::out | stringstream::binary);
352 decryptedStream.exceptions(ios_base::failbit | ios_base::badbit);
354#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
355 decryptedStream.rdbuf()->pubsetbuf(decryptedData.data(),
static_cast<streamsize
>(remainingSize));
357 decryptedStream.write(decryptedData.data(),
static_cast<streamsize
>(remainingSize));
359 if (m_version >= 0x5u) {
360 BinaryReader reader(&decryptedStream);
361 const auto extendedHeaderSize = reader.readUInt16BE();
362 m_encryptedExtendedHeader = reader.readString(extendedHeaderSize);
364 m_encryptedExtendedHeader.clear();
366 m_rootEntry.reset(
new NodeEntry(decryptedStream));
367 }
catch (
const std::ios_base::failure &failure) {
368 if (decryptedStream.eof()) {
371 throw ParsingException(argsToString(
"An IO error occurred when reading internal buffer: ", failure.what()));
448 throw runtime_error(
"Root entry has not been created.");
452 m_fwriter.writeUInt32LE(0x7770616DU);
456 m_fwriter.writeUInt32LE(
version);
459 std::uint8_t flags = 0x00;
461 flags |= 0x80 | 0x40;
466 m_fwriter.writeByte(flags);
470 if (m_extendedHeader.size() > numeric_limits<std::uint16_t>::max()) {
471 throw runtime_error(
"Extended header exceeds maximum size.");
473 m_fwriter.writeUInt16BE(
static_cast<std::uint16_t
>(m_extendedHeader.size()));
474 m_fwriter.writeString(m_extendedHeader);
478 stringstream buffstr(stringstream::in | stringstream::out | stringstream::binary);
479 buffstr.exceptions(ios_base::failbit | ios_base::badbit);
483 if (m_encryptedExtendedHeader.size() > numeric_limits<std::uint16_t>::max()) {
484 throw runtime_error(
"Encrypted extended header exceeds maximum size.");
486 BinaryWriter buffstrWriter(&buffstr);
487 buffstrWriter.writeUInt16BE(
static_cast<std::uint16_t
>(m_encryptedExtendedHeader.size()));
488 buffstrWriter.writeString(m_encryptedExtendedHeader);
490 m_rootEntry->make(buffstr);
491 buffstr.seekp(0, ios_base::end);
492 auto size =
static_cast<size_t>(buffstr.tellp());
496 vector<char> decryptedData(
size, 0);
497 buffstr.read(decryptedData.data(),
static_cast<streamoff
>(
size));
498 vector<char> encryptedData;
502 uLongf compressedSize = compressBound(
size);
503 encryptedData.resize(8 + compressedSize);
504 LE::getBytes(
static_cast<std::uint64_t
>(
size), encryptedData.data());
506 compress(
reinterpret_cast<Bytef *
>(encryptedData.data() + 8), &compressedSize,
reinterpret_cast<Bytef *
>(decryptedData.data()),
size)) {
508 throw runtime_error(
"Compressing failed. The source buffer was too small.");
510 throw runtime_error(
"Compressing failed. The destination buffer was too small.");
512 encryptedData.swap(decryptedData);
513 size = 8 + compressedSize;
517 if (
size > numeric_limits<int>::max()) {
524 m_file.write(decryptedData.data(),
static_cast<streamsize
>(
size));
535 for (uint32_t i = 1; i < hashCount; ++i) {
543 EVP_CIPHER_CTX *ctx =
nullptr;
545 int outlen1, outlen2;
547 if (RAND_bytes(iv,
aes256cbcIvSize) != 1 || (ctx = EVP_CIPHER_CTX_new()) ==
nullptr
548 || EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(),
nullptr,
password.data, iv) != 1
549 || EVP_EncryptUpdate(ctx,
reinterpret_cast<unsigned char *
>(encryptedData.data()), &outlen1,
550 reinterpret_cast<unsigned char *
>(decryptedData.data()),
static_cast<int>(
size))
552 || EVP_EncryptFinal_ex(ctx,
reinterpret_cast<unsigned char *
>(encryptedData.data()) + outlen1, &outlen2) != 1) {
555 EVP_CIPHER_CTX_free(ctx);
558 auto errorCode = ERR_get_error();
563 msg += ERR_error_string(errorCode,
nullptr);
564 errorCode = ERR_get_error();
570 EVP_CIPHER_CTX_free(ctx);
575 m_fwriter.writeUInt32BE(hashCount);
578 m_file.write(encryptedData.data(),
static_cast<streamsize
>(outlen1 + outlen2));
612 throw runtime_error(
"Root entry has not been created.");
614 NativeFileStream output;
615 output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
616 output.open(targetPath, std::ios_base::out);
617 const auto printIndention = [&output](
int level) {
618 for (
int i = 0; i < level; ++i) {
622 function<void(
const Entry *entry,
int level)> printNode;
623 printNode = [&output, &printNode, &printIndention](
const Entry *entry,
int level) {
624 printIndention(level);
625 output <<
" - " << entry->
label() << endl;
626 switch (entry->
type()) {
628 for (
const Entry *child :
static_cast<const NodeEntry *
>(entry)->children()) {
629 printNode(child, level + 1);
634 printIndention(level);
635 output <<
" " << field.name();
636 for (
auto i = field.name().length(); i < 15; ++i) {
639 output << field.value() << endl;
643 printNode(m_rootEntry.get(), 0);
The exception that is thrown when a parsing error occurs.
Instances of the Entry class form a hierarchic data structure used to store account information.
virtual EntryType type() const =0
Returns the type of the entry.
const std::string & label() const
Returns the label.
The NodeEntry class acts as parent for other entries.