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<std::size_t
>(buffstr.tellp());
493 if (
size > std::numeric_limits<uLong>::max()) {
494 throw std::runtime_error(
"File size exceeds maximum size.");
499 vector<char> decryptedData(
size, 0);
500 buffstr.read(decryptedData.data(),
static_cast<streamoff
>(
size));
501 vector<char> encryptedData;
505 auto compressedSize =
static_cast<uLongf
>(compressBound(
static_cast<uLong
>(
size)));
506 encryptedData.resize(8 + compressedSize);
507 LE::getBytes(
static_cast<std::uint64_t
>(
size), encryptedData.data());
508 switch (compress(
reinterpret_cast<Bytef *
>(encryptedData.data() + 8), &compressedSize,
reinterpret_cast<Bytef *
>(decryptedData.data()),
509 static_cast<uLong
>(
size))) {
511 throw runtime_error(
"Compressing failed. The source buffer was too small.");
513 throw runtime_error(
"Compressing failed. The destination buffer was too small.");
515 encryptedData.swap(decryptedData);
516 size = 8 + compressedSize;
520 if (
size > numeric_limits<int>::max()) {
527 m_file.write(decryptedData.data(),
static_cast<streamsize
>(
size));
538 for (uint32_t i = 1; i < hashCount; ++i) {
546 EVP_CIPHER_CTX *ctx =
nullptr;
548 int outlen1, outlen2;
550 if (RAND_bytes(iv,
aes256cbcIvSize) != 1 || (ctx = EVP_CIPHER_CTX_new()) ==
nullptr
551 || EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(),
nullptr,
password.data, iv) != 1
552 || EVP_EncryptUpdate(ctx,
reinterpret_cast<unsigned char *
>(encryptedData.data()), &outlen1,
553 reinterpret_cast<unsigned char *
>(decryptedData.data()),
static_cast<int>(
size))
555 || EVP_EncryptFinal_ex(ctx,
reinterpret_cast<unsigned char *
>(encryptedData.data()) + outlen1, &outlen2) != 1) {
558 EVP_CIPHER_CTX_free(ctx);
561 auto errorCode = ERR_get_error();
566 msg += ERR_error_string(errorCode,
nullptr);
567 errorCode = ERR_get_error();
573 EVP_CIPHER_CTX_free(ctx);
578 m_fwriter.writeUInt32BE(hashCount);
581 m_file.write(encryptedData.data(),
static_cast<streamsize
>(outlen1 + outlen2));
615 throw runtime_error(
"Root entry has not been created.");
617 NativeFileStream output;
618 output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
619 output.open(targetPath, std::ios_base::out);
620 const auto printIndention = [&output](
int level) {
621 for (
int i = 0; i < level; ++i) {
625 function<void(
const Entry *entry,
int level)> printNode;
626 printNode = [&output, &printNode, &printIndention](
const Entry *entry,
int level) {
627 printIndention(level);
628 output <<
" - " << entry->
label() << endl;
629 switch (entry->
type()) {
631 for (
const Entry *child :
static_cast<const NodeEntry *
>(entry)->children()) {
632 printNode(child, level + 1);
637 printIndention(level);
638 output <<
" " << field.name();
639 for (
auto i = field.name().length(); i < 15; ++i) {
642 output << field.value() << endl;
646 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.
const std::string & label() const
Returns the label.
virtual EntryType type() const =0
Returns the type of the entry.
The NodeEntry class acts as parent for other entries.