diff --git a/cdoc/CDoc1Writer.cpp b/cdoc/CDoc1Writer.cpp index 96ba07a2..eb2d02dd 100644 --- a/cdoc/CDoc1Writer.cpp +++ b/cdoc/CDoc1Writer.cpp @@ -32,7 +32,7 @@ using namespace libcdoc; -#define RET_ERROR(F) if (auto rv = F; rv != OK) return rv +#define RET_ERROR(F) if (auto rv = F; rv < 0) return rv struct FileEntry { std::string name; @@ -45,19 +45,24 @@ struct FileEntry { * @brief CDoc1Writer is used for encrypt data. */ -class CDoc1Writer::Private +struct CDoc1Writer::Private final: public XMLWriter { -public: - std::unique_ptr _xml; - std::vector files; - std::vector rcpts; - - static const XMLWriter::NS DENC, DS, XENC11, DSIG11; - std::string method, documentFormat = "ENCDOC-XML|1.1", lastError; - - uint64_t writeEncryptionProperties(bool use_ddoc); - uint64_t writeKeyInfo(bool use_ddoc, const libcdoc::Crypto::Key& transportKey); - uint64_t writeRecipient(const std::vector &recipient, const libcdoc::Crypto::Key& transportKey); + static const XMLWriter::NS DENC, DS, XENC11, DSIG11; + + Private(DataConsumer &dst, std::string &last_error) + : XMLWriter(dst) + , lastError(last_error) + {} + + std::string method = "http://www.w3.org/2009/xmlenc11#aes256-gcm"; + std::string documentFormat = "ENCDOC-XML|1.1"; + std::string &lastError; + std::vector files; + std::vector rcpts; + + int64_t writeEncryptionProperties(bool use_ddoc); + int64_t writeKeyInfo(bool use_ddoc, const Crypto::Key& transportKey); + int64_t writeRecipient(const std::vector &recipient, const Crypto::Key& transportKey); }; const XMLWriter::NS CDoc1Writer::Private::DENC{ "denc", "http://www.w3.org/2001/04/xmlenc#" }; @@ -65,45 +70,40 @@ const XMLWriter::NS CDoc1Writer::Private::DS{ "ds", "http://www.w3.org/2000/09/x const XMLWriter::NS CDoc1Writer::Private::XENC11{ "xenc11", "http://www.w3.org/2009/xmlenc11#" }; const XMLWriter::NS CDoc1Writer::Private::DSIG11{ "dsig11", "http://www.w3.org/2009/xmldsig11#" }; -CDoc1Writer::CDoc1Writer(libcdoc::DataConsumer *dst, bool take_ownership, const std::string &method) - : CDocWriter(1, dst, take_ownership), d(new Private()) -{ - d->method = method; -} +CDoc1Writer::CDoc1Writer(DataConsumer *dst, bool take_ownership) + : CDocWriter(1, dst, take_ownership) +{} -CDoc1Writer::~CDoc1Writer() -{ - delete d; -} +CDoc1Writer::~CDoc1Writer() noexcept = default; -uint64_t CDoc1Writer::Private::writeEncryptionProperties(bool use_ddoc) +int64_t CDoc1Writer::Private::writeEncryptionProperties(bool use_ddoc) { - RET_ERROR(_xml->writeElement(DENC, "EncryptionProperties", [&]() -> uint64_t { - RET_ERROR(_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "LibraryVersion"}}, "cdoc|0.0.1")); - RET_ERROR(_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "DocumentFormat"}}, documentFormat)); - RET_ERROR(_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "Filename"}}, use_ddoc ? "tmp.ddoc" : files.at(0).name)); + RET_ERROR(writeElement(DENC, "EncryptionProperties", [&]() -> int64_t { + RET_ERROR(writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "LibraryVersion"}}, "cdoc|0.0.1")); + RET_ERROR(writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "DocumentFormat"}}, documentFormat)); + RET_ERROR(writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "Filename"}}, use_ddoc ? "tmp.ddoc" : files.at(0).name)); for(const FileEntry &file: files) { - RET_ERROR(_xml->writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "orig_file"}}, + RET_ERROR(writeTextElement(Private::DENC, "EncryptionProperty", {{"Name", "orig_file"}}, file.name + "|" + std::to_string(file.size) + "|" + "application/octet-stream" + "|D0")); } return OK; })); - return _xml->writeEndElement(Private::DENC); // EncryptedData + return writeEndElement(Private::DENC); // EncryptedData } -uint64_t CDoc1Writer::Private::writeKeyInfo(bool use_ddoc, const libcdoc::Crypto::Key& transportKey) +int64_t CDoc1Writer::Private::writeKeyInfo(bool use_ddoc, const Crypto::Key& transportKey) { - RET_ERROR(_xml->writeStartElement(Private::DENC, "EncryptedData", {{"MimeType", use_ddoc ? "http://www.sk.ee/DigiDoc/v1.3.0/digidoc.xsd" : "application/octet-stream"}})); - RET_ERROR(_xml->writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", method}})); - return _xml->writeElement(Private::DS, "KeyInfo", {}, [&]() -> uint64_t { - for (const libcdoc::Recipient& key : rcpts) { + RET_ERROR(writeStartElement(Private::DENC, "EncryptedData", {{"MimeType", use_ddoc ? "http://www.sk.ee/DigiDoc/v1.3.0/digidoc.xsd" : "application/octet-stream"}})); + RET_ERROR(writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", method}})); + return writeElement(Private::DS, "KeyInfo", {}, [&]() -> int64_t { + for (const Recipient& key : rcpts) { if (!key.isCertificate()) { lastError = "Invalid recipient type"; LOG_ERROR("{}", lastError); - return libcdoc::UNSPECIFIED_ERROR; + return UNSPECIFIED_ERROR; } - if(auto rv = writeRecipient(key.cert, transportKey); rv != OK) { + if(auto rv = writeRecipient(key.cert, transportKey); rv < 0) { lastError = "Failed to write Recipient info"; LOG_ERROR("{}", lastError); return rv; @@ -113,9 +113,9 @@ uint64_t CDoc1Writer::Private::writeKeyInfo(bool use_ddoc, const libcdoc::Crypto }); } -uint64_t CDoc1Writer::Private::writeRecipient(const std::vector &recipient, const libcdoc::Crypto::Key& transportKey) +int64_t CDoc1Writer::Private::writeRecipient(const std::vector &recipient, const Crypto::Key& transportKey) { - auto peerCert = libcdoc::Crypto::toX509(recipient); + auto peerCert = Crypto::toX509(recipient); if(!peerCert) return UNSPECIFIED_ERROR; std::string cn = [&]{ @@ -136,7 +136,7 @@ uint64_t CDoc1Writer::Private::writeRecipient(const std::vector &recipi OPENSSL_free(data); return cn; }(); - return _xml->writeElement(Private::DENC, "EncryptedKey", {{"Recipient", cn}}, [&]() -> uint64_t { + return writeElement(Private::DENC, "EncryptedKey", {{"Recipient", cn}}, [&]() -> int64_t { std::vector encryptedData; auto *peerPKey = X509_get0_pubkey(peerCert.get()); switch(EVP_PKEY_base_id(peerPKey)) @@ -147,10 +147,10 @@ uint64_t CDoc1Writer::Private::writeRecipient(const std::vector &recipi encryptedData.resize(size_t(RSA_size(rsa.get()))); RSA_public_encrypt(int(transportKey.key.size()), transportKey.key.data(), encryptedData.data(), rsa.get(), RSA_PKCS1_PADDING); - RET_ERROR(_xml->writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", libcdoc::Crypto::RSA_MTH}})); - RET_ERROR(_xml->writeElement(Private::DS, "KeyInfo", [&]{ - return _xml->writeElement(Private::DS, "X509Data", [&]{ - return _xml->writeBase64Element(Private::DS, "X509Certificate", recipient); + RET_ERROR(writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", Crypto::RSA_MTH}})); + RET_ERROR(writeElement(Private::DS, "KeyInfo", [&] { + return writeElement(Private::DS, "X509Data", [&] { + return writeBase64Element(Private::DS, "X509Certificate", recipient); }); })); break; @@ -188,28 +188,28 @@ uint64_t CDoc1Writer::Private::writeRecipient(const std::vector &recipi LOG_TRACE_KEY("iv {}", transportKey.iv); LOG_TRACE_KEY("transport {}", transportKey.key); - RET_ERROR(_xml->writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", encryptionMethod}})); - RET_ERROR(_xml->writeElement(Private::DS, "KeyInfo", [&]{ - return _xml->writeElement(Private::DENC, "AgreementMethod", {{"Algorithm", libcdoc::Crypto::AGREEMENT_MTH}}, [&]{ - RET_ERROR(_xml->writeElement(Private::XENC11, "KeyDerivationMethod", {{"Algorithm", libcdoc::Crypto::CONCATKDF_MTH}}, [&]{ - return _xml->writeElement(Private::XENC11, "ConcatKDFParams", { - {"AlgorithmID", "00" + libcdoc::toHex(AlgorithmID)}, - {"PartyUInfo", "00" + libcdoc::toHex(SsDer)}, - {"PartyVInfo", "00" + libcdoc::toHex(recipient)}}, [&]{ - return _xml->writeElement(Private::DS, "DigestMethod", {{"Algorithm", concatDigest}}); + RET_ERROR(writeElement(Private::DENC, "EncryptionMethod", {{"Algorithm", encryptionMethod}})); + RET_ERROR(writeElement(Private::DS, "KeyInfo", [&] { + return writeElement(Private::DENC, "AgreementMethod", {{"Algorithm", Crypto::AGREEMENT_MTH}}, [&] { + RET_ERROR(writeElement(Private::XENC11, "KeyDerivationMethod", {{"Algorithm", Crypto::CONCATKDF_MTH}}, [&] { + return writeElement(Private::XENC11, "ConcatKDFParams", { + {"AlgorithmID", "00" + toHex(AlgorithmID)}, + {"PartyUInfo", "00" + toHex(SsDer)}, + {"PartyVInfo", "00" + toHex(recipient)}}, [&] { + return writeElement(Private::DS, "DigestMethod", {{"Algorithm", concatDigest}}); }); })); - RET_ERROR(_xml->writeElement(Private::DENC, "OriginatorKeyInfo", [&]{ - return _xml->writeElement(Private::DS, "KeyValue", [&]{ - return _xml->writeElement(Private::DSIG11, "ECKeyValue", [&]{ - RET_ERROR(_xml->writeElement(Private::DSIG11, "NamedCurve", {{"URI", "urn:oid:" + oid}})); - return _xml->writeBase64Element(Private::DSIG11, "PublicKey", SsDer); + RET_ERROR(writeElement(Private::DENC, "OriginatorKeyInfo", [&] { + return writeElement(Private::DS, "KeyValue", [&] { + return writeElement(Private::DSIG11, "ECKeyValue", [&] { + RET_ERROR(writeElement(Private::DSIG11, "NamedCurve", {{"URI", "urn:oid:" + oid}})); + return writeBase64Element(Private::DSIG11, "PublicKey", SsDer); }); }); })); - return _xml->writeElement(Private::DENC, "RecipientKeyInfo", [&]{ - return _xml->writeElement(Private::DS, "X509Data", [&]{ - return _xml->writeBase64Element(Private::DS, "X509Certificate", recipient); + return writeElement(Private::DENC, "RecipientKeyInfo", [&] { + return writeElement(Private::DS, "X509Data", [&] { + return writeBase64Element(Private::DS, "X509Certificate", recipient); }); }); }); @@ -222,8 +222,8 @@ uint64_t CDoc1Writer::Private::writeRecipient(const std::vector &recipi if (encryptedData.empty()) return UNSPECIFIED_ERROR; - return _xml->writeElement(Private::DENC, "CipherData", [&]{ - return _xml->writeBase64Element(Private::DENC, "CipherValue", encryptedData); + return writeElement(Private::DENC, "CipherData", [&] { + return writeBase64Element(Private::DENC, "CipherValue", encryptedData); }); }); } @@ -234,44 +234,44 @@ uint64_t CDoc1Writer::Private::writeRecipient(const std::vector &recipi libcdoc::result_t CDoc1Writer::encrypt(libcdoc::MultiDataSource& src, const std::vector& keys) { - libcdoc::Crypto::Key transportKey = libcdoc::Crypto::generateKey(d->method); + RET_ERROR(beginEncryption()); + d->rcpts = keys; + Crypto::Key transportKey = Crypto::generateKey(d->method); int n_components = src.getNumComponents(); bool use_ddoc = (n_components > 1) || (n_components == libcdoc::NOT_IMPLEMENTED); - d->rcpts = keys; - d->_xml = std::make_unique(dst); RET_ERROR(d->writeKeyInfo(use_ddoc, transportKey)); - RET_ERROR(d->_xml->writeElement(Private::DENC, "CipherData", [&]() -> uint64_t { + RET_ERROR(d->writeElement(Private::DENC, "CipherData", [&]() -> int64_t { std::vector data; + data.reserve(16384); + VectorConsumer vcons(data); + std::string name; + int64_t size; if(use_ddoc) { - data.reserve(16384); - DDOCWriter ddoc(data); - std::string name; - int64_t size; - for (auto result = src.next(name, size); result == libcdoc::OK; result = src.next(name, size)) { - std::vector contents; - libcdoc::VectorConsumer vcons(contents); - if (src.readAll(vcons) < 0) - return IO_ERROR; - d->files.push_back({name, (size_t) result}); - if(auto rv = ddoc.addFile(name, "application/octet-stream", contents); rv != OK) - return rv; - } - } else { - std::string name; - int64_t size; - if (src.next(name, size) < 0) - return IO_ERROR; - libcdoc::VectorConsumer vcons(data); - auto result = src.readAll(vcons); - if (result < 0) - return IO_ERROR; - d->files.push_back({std::move(name), (size_t) result}); + DDOCWriter ddoc(vcons); + result_t result; + for (result = src.next(name, size); result == OK; result = src.next(name, size)) { + std::vector contents; + VectorConsumer vcons(contents); + RET_ERROR(src.readAll(vcons)); + RET_ERROR(vcons.close()); + RET_ERROR(ddoc.addFile(name, "application/octet-stream", contents)); + d->files.push_back({name, contents.size()}); + } + if(result != END_OF_STREAM) + return result; + } else { + RET_ERROR(src.next(name, size)); + if(auto rv = src.readAll(vcons); rv >= 0) + d->files.push_back({std::move(name), size_t(rv)}); + else + return rv; } - return d->_xml->writeBase64Element(Private::DENC, "CipherValue", libcdoc::Crypto::encrypt(d->method, transportKey, data)); + RET_ERROR(vcons.close()); + return d->writeBase64Element(Private::DENC, "CipherValue", libcdoc::Crypto::encrypt(d->method, transportKey, data)); })); RET_ERROR(d->writeEncryptionProperties(use_ddoc)); - d->_xml.reset(); + d.reset(); if (owned) return dst->close(); return OK; } @@ -279,13 +279,17 @@ CDoc1Writer::encrypt(libcdoc::MultiDataSource& src, const std::vector_xml = std::make_unique(dst); + if(!dst) + return WORKFLOW_ERROR; + d = std::make_unique(*dst, last_error); return libcdoc::OK; } libcdoc::result_t CDoc1Writer::addRecipient(const libcdoc::Recipient& rcpt) { + if(!d) + return WORKFLOW_ERROR; d->rcpts.push_back(rcpt); return libcdoc::OK; } @@ -293,6 +297,8 @@ CDoc1Writer::addRecipient(const libcdoc::Recipient& rcpt) libcdoc::result_t CDoc1Writer::addFile(const std::string& name, size_t size) { + if(!d) + return WORKFLOW_ERROR; d->files.push_back({name, size, {}}); return libcdoc::OK; } @@ -300,7 +306,8 @@ CDoc1Writer::addFile(const std::string& name, size_t size) libcdoc::result_t CDoc1Writer::writeData(const uint8_t *src, size_t size) { - if (d->files.empty()) return libcdoc::WORKFLOW_ERROR; + if(!d) + return WORKFLOW_ERROR; d->files.back().data.insert(d->files.back().data.end(), src, src + size); return libcdoc::OK; } @@ -308,25 +315,26 @@ CDoc1Writer::writeData(const uint8_t *src, size_t size) libcdoc::result_t CDoc1Writer::finishEncryption() { - if (!d->_xml) return libcdoc::WORKFLOW_ERROR; - if (d->rcpts.empty()) return libcdoc::WORKFLOW_ERROR; - if (d->files.empty()) return libcdoc::WORKFLOW_ERROR; + if(!d || d->rcpts.empty() || d->files.empty()) + return WORKFLOW_ERROR; bool use_ddoc = d->files.size() > 1; libcdoc::Crypto::Key transportKey = libcdoc::Crypto::generateKey(d->method); RET_ERROR(d->writeKeyInfo(use_ddoc, transportKey)); - RET_ERROR(d->_xml->writeElement(Private::DENC, "CipherData", [&]{ + RET_ERROR(d->writeElement(Private::DENC, "CipherData", [&] { if(!use_ddoc) - return d->_xml->writeBase64Element(Private::DENC, "CipherValue", libcdoc::Crypto::encrypt(d->method, transportKey, d->files.back().data)); + return d->writeBase64Element(Private::DENC, "CipherValue", libcdoc::Crypto::encrypt(d->method, transportKey, d->files.back().data)); std::vector data; - data.reserve(4096); - for (DDOCWriter ddoc(data); const FileEntry& file : d->files) { - ddoc.addFile(file.name, "application/octet-stream", file.data); + data.reserve(16384); + VectorConsumer vcons(data); + for (DDOCWriter ddoc(vcons); const FileEntry& file : d->files) { + RET_ERROR(ddoc.addFile(file.name, "application/octet-stream", file.data)); } - return d->_xml->writeBase64Element(Private::DENC, "CipherValue", libcdoc::Crypto::encrypt(d->method, transportKey, data)); + RET_ERROR(vcons.close()); + return d->writeBase64Element(Private::DENC, "CipherValue", libcdoc::Crypto::encrypt(d->method, transportKey, data)); })); RET_ERROR(d->writeEncryptionProperties(use_ddoc)); - d->_xml.reset(); + d.reset(); if (owned) return dst->close(); return libcdoc::OK; } diff --git a/cdoc/CDoc1Writer.h b/cdoc/CDoc1Writer.h index 25b44a02..9ba0ba9d 100644 --- a/cdoc/CDoc1Writer.h +++ b/cdoc/CDoc1Writer.h @@ -20,22 +20,24 @@ #include "CDocWriter.h" -class CDoc1Writer : public libcdoc::CDocWriter +#include + +class CDoc1Writer final: public libcdoc::CDocWriter { public: - CDoc1Writer(libcdoc::DataConsumer *dst, bool take_ownership, const std::string &method = "http://www.w3.org/2009/xmlenc11#aes256-gcm"); - ~CDoc1Writer(); + CDoc1Writer(libcdoc::DataConsumer *dst, bool take_ownership); + ~CDoc1Writer() noexcept final; - libcdoc::result_t beginEncryption() override final; - libcdoc::result_t addRecipient(const libcdoc::Recipient& rcpt) override final; - libcdoc::result_t addFile(const std::string& name, size_t size) override final; - libcdoc::result_t writeData(const uint8_t *src, size_t size) override final; - libcdoc::result_t finishEncryption() override final; + libcdoc::result_t beginEncryption() final; + libcdoc::result_t addRecipient(const libcdoc::Recipient& rcpt) final; + libcdoc::result_t addFile(const std::string& name, size_t size) final; + libcdoc::result_t writeData(const uint8_t *src, size_t size) final; + libcdoc::result_t finishEncryption() final; libcdoc::result_t encrypt(libcdoc::MultiDataSource& src, const std::vector& keys) final; + private: - CDoc1Writer(const CDoc1Writer &) = delete; - CDoc1Writer &operator=(const CDoc1Writer &) = delete; - class Private; - Private *d; + CDOC_DISABLE_COPY(CDoc1Writer) + struct Private; + std::unique_ptr d; }; diff --git a/cdoc/DDocWriter.cpp b/cdoc/DDocWriter.cpp index 6bc89ad8..44ea52e5 100644 --- a/cdoc/DDocWriter.cpp +++ b/cdoc/DDocWriter.cpp @@ -27,13 +27,13 @@ using namespace libcdoc; const XMLWriter::NS DDOCWriter::DDOC{ "", "http://www.sk.ee/DigiDoc/v1.3.0#" }; -DDOCWriter::DDOCWriter(std::vector& vec) - : XMLWriter(vec) +DDOCWriter::DDOCWriter(DataConsumer &dst) + : XMLWriter(dst) { writeStartElement(DDOC, "SignedDoc", {{"format", "DIGIDOC-XML"}, {"version", "1.3"}}); } -DDOCWriter::~DDOCWriter() +DDOCWriter::~DDOCWriter() noexcept { writeEndElement(DDOC); // SignedDoc } @@ -44,13 +44,13 @@ DDOCWriter::~DDOCWriter() * @param mime File mime type * @param data File content */ -uint64_t DDOCWriter::addFile(const std::string &file, const std::string &mime, const std::vector &data) +int64_t DDOCWriter::addFile(const std::string &file, const std::string &mime, const std::vector &data) { return writeBase64Element(DDOC, "DataFile", data, { - {"ContentType", "EMBEDDED_BASE64"}, - {"Filename", file}, + {"ContentType", "EMBEDDED_BASE64"}, + {"Filename", file}, {"Id", "D" + std::to_string(fileCount++)}, - {"MimeType", mime}, - {"Size", std::to_string(data.size())} - }); + {"MimeType", mime}, + {"Size", std::to_string(data.size())} + }); } diff --git a/cdoc/DDocWriter.h b/cdoc/DDocWriter.h index eeb4d3dc..68981d69 100644 --- a/cdoc/DDocWriter.h +++ b/cdoc/DDocWriter.h @@ -22,20 +22,20 @@ namespace libcdoc { -class DDOCWriter: public XMLWriter +class DDOCWriter final: public XMLWriter { public: - DDOCWriter(std::vector& vec); - ~DDOCWriter(); + DDOCWriter(DataConsumer &dst); + ~DDOCWriter() noexcept final; - uint64_t addFile(const std::string &name, const std::string &mime, const std::vector &data); + int64_t addFile(const std::string &name, const std::string &mime, const std::vector &data); private: - DDOCWriter(const DDOCWriter &) = delete; - DDOCWriter &operator=(const DDOCWriter &) = delete; + DDOCWriter(const DDOCWriter &) = delete; + DDOCWriter &operator=(const DDOCWriter &) = delete; int fileCount = 0; static const NS DDOC; }; -} \ No newline at end of file +} diff --git a/cdoc/Exports.h b/cdoc/Exports.h index 8632325e..8185ad8a 100644 --- a/cdoc/Exports.h +++ b/cdoc/Exports.h @@ -58,5 +58,11 @@ #define CDOC_DISABLE_MOVE(Class) \ Class(Class&&) noexcept = delete; \ Class& operator=(Class&&) noexcept = delete; +#define CDOC_DISABLE_COPY(Class) \ + Class(const Class&) noexcept = delete; \ + Class& operator=(const Class&) noexcept = delete; +#define CDOC_DISABLE_MOVE_COPY(Class) \ + CDOC_DISABLE_MOVE(Class) \ + CDOC_DISABLE_COPY(Class) #endif // EXPOORTS_H diff --git a/cdoc/XmlWriter.cpp b/cdoc/XmlWriter.cpp index 902a5ea9..07533650 100644 --- a/cdoc/XmlWriter.cpp +++ b/cdoc/XmlWriter.cpp @@ -26,74 +26,59 @@ using namespace libcdoc; -typedef const xmlChar *pcxmlChar; +using pcxmlChar = xmlChar *; struct XMLWriter::Private { - unique_ptr_t w = make_unique_ptr(xmlNewTextWriter( - xmlOutputBufferCreateIO(xmlOutputWriteCallback, xmlOutputCloseCallback, this, nullptr))); - std::map nsmap; + Private(libcdoc::DataConsumer &_dst): dst(_dst) {} + libcdoc::DataConsumer &dst; + std::map nsmap; - libcdoc::DataConsumer* dst = nullptr; - bool dst_owned = false; + unique_ptr_t w = make_unique_ptr(xmlNewTextWriter( + xmlOutputBufferCreateIO(xmlOutputWriteCallback, nullptr, this, nullptr))); - static int xmlOutputWriteCallback (void *context, const char *buffer, int len); - static int xmlOutputCloseCallback (void *context); + static int xmlOutputWriteCallback (void *context, const char *buffer, int len); }; int XMLWriter::Private::xmlOutputWriteCallback (void *context, const char *buffer, int len) { - auto *d = reinterpret_cast(context); - return d->dst->write((uint8_t *) buffer, len); -} - -int -XMLWriter::Private::xmlOutputCloseCallback (void *context) -{ - auto *d = reinterpret_cast(context); - return d->dst->close(); -} - -XMLWriter::XMLWriter(libcdoc::DataConsumer* dst) - : d(new Private) -{ - d->dst = dst; - xmlTextWriterStartDocument(d->w.get(), nullptr, "UTF-8", nullptr); + auto *d = reinterpret_cast(context); + return d->dst.write((uint8_t *) buffer, len); } -XMLWriter::XMLWriter(std::vector& vec) - : XMLWriter(new libcdoc::VectorConsumer(vec)) +XMLWriter::XMLWriter(libcdoc::DataConsumer &dst) + : d(std::make_unique(dst)) { - d->dst_owned = true; + if(d->w) + xmlTextWriterStartDocument(d->w.get(), nullptr, "UTF-8", nullptr); } -XMLWriter::~XMLWriter() +XMLWriter::~XMLWriter() noexcept { - xmlTextWriterEndDocument(d->w.get()); - // Force XmlTextWriter to finish before deleting consumer - d->w.reset(); - if(d->dst && d->dst_owned) delete d->dst; - delete d; + if(d->w) + xmlTextWriterEndDocument(d->w.get()); } int64_t XMLWriter::writeStartElement(const NS &ns, const std::string &name, const std::map &attr) { if(!d->w) return WRONG_ARGUMENTS; - std::map::iterator pos = d->nsmap.find(ns.prefix); - if (pos != d->nsmap.cend()) - pos->second++; - else - pos = d->nsmap.insert({ns.prefix, 1}).first; - if(xmlTextWriterStartElementNS(d->w.get(), ns.prefix.empty() ? nullptr : pcxmlChar(ns.prefix.c_str()), - pcxmlChar(name.c_str()), pos->second > 1 ? nullptr : pcxmlChar(ns.ns.c_str())) == -1) + auto pos = d->nsmap.find(ns.prefix); + if (pos != d->nsmap.cend()) + pos->second++; + else + pos = d->nsmap.insert({ns.prefix, 1}).first; + if(xmlTextWriterStartElementNS(d->w.get(), + ns.prefix.empty() ? nullptr : pcxmlChar(ns.prefix.c_str()), + pcxmlChar(name.c_str()), + pos->second > 1 ? nullptr : pcxmlChar(ns.ns.c_str())) == -1) return IO_ERROR; - for(auto i = attr.cbegin(), end = attr.cend(); i != end; ++i) - { - if(xmlTextWriterWriteAttribute(d->w.get(), pcxmlChar(i->first.c_str()), pcxmlChar(i->second.c_str())) == -1) + for(const auto &[name, content]: attr) + { + if(xmlTextWriterWriteAttribute(d->w.get(), pcxmlChar(name.c_str()), pcxmlChar(content.c_str())) == -1) return IO_ERROR; - } + } return OK; } @@ -103,26 +88,25 @@ int64_t XMLWriter::writeEndElement(const NS &ns) return WRONG_ARGUMENTS; if(xmlTextWriterEndElement(d->w.get()) == -1) return IO_ERROR; - if(std::map::iterator pos = d->nsmap.find(ns.prefix); - pos != d->nsmap.cend()) - pos->second--; + if(auto pos = d->nsmap.find(ns.prefix); pos != d->nsmap.cend()) + pos->second--; return OK; } -int64_t XMLWriter::writeElement(const NS &ns, const std::string &name, const std::function &f) +int64_t XMLWriter::writeElement(const NS &ns, const std::string &name, const std::function &f) { if(auto rv = writeStartElement(ns, name, {}); rv != OK) return rv; - if(uint64_t rv = OK; f && (rv = f()) != OK) + if(int64_t rv = OK; f && (rv = f()) != OK) return rv; return writeEndElement(ns); } -int64_t XMLWriter::writeElement(const NS &ns, const std::string &name, const std::map &attr, const std::function &f) +int64_t XMLWriter::writeElement(const NS &ns, const std::string &name, const std::map &attr, const std::function &f) { if(auto rv = writeStartElement(ns, name, attr); rv != OK) return rv; - if(uint64_t rv = OK; f && (rv = f()) != OK) + if(int64_t rv = OK; f && (rv = f()) != OK) return rv; return writeEndElement(ns); } diff --git a/cdoc/XmlWriter.h b/cdoc/XmlWriter.h index 6f0b8714..0ae06a54 100644 --- a/cdoc/XmlWriter.h +++ b/cdoc/XmlWriter.h @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include @@ -32,22 +32,21 @@ struct DataConsumer; class XMLWriter { public: - struct NS { std::string prefix, ns; }; + struct NS { std::string prefix, ns; }; - XMLWriter(std::vector& vec); - XMLWriter(DataConsumer *dst); - virtual ~XMLWriter(); + XMLWriter(DataConsumer &dst); + virtual ~XMLWriter() noexcept; int64_t writeStartElement(const NS &ns, const std::string &name, const std::map &attr); int64_t writeEndElement(const NS &ns); - int64_t writeElement(const NS &ns, const std::string &name, const std::function &f = nullptr); - int64_t writeElement(const NS &ns, const std::string &name, const std::map &attr, const std::function &f = nullptr); + int64_t writeElement(const NS &ns, const std::string &name, const std::function &f = nullptr); + int64_t writeElement(const NS &ns, const std::string &name, const std::map &attr, const std::function &f = nullptr); int64_t writeBase64Element(const NS &ns, const std::string &name, const std::vector &data, const std::map &attr = {}); int64_t writeTextElement(const NS &ns, const std::string &name, const std::map &attr, const std::string &data); private: - struct Private; - Private *d; + struct Private; + std::unique_ptr d; }; -} // namespace libcdoc \ No newline at end of file +} // namespace libcdoc