From 499506d3abde5deba4950f1ffd17a0d1c1a9fd80 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Mon, 30 Sep 2024 15:34:14 +0300 Subject: [PATCH] ASiC-S TimeStamp creation support IB-8181 Signed-off-by: Raul Metsma --- src/ASiC_E.cpp | 46 ++++---------------- src/ASiC_E.h | 14 +++--- src/ASiC_S.cpp | 92 +++++++++++++++++++++++++-------------- src/ASiC_S.h | 6 ++- src/ASiContainer.cpp | 39 ++++++++++++++--- src/ASiContainer.h | 7 +-- src/SiVaContainer.cpp | 3 +- src/SignatureTST.cpp | 31 ++++++++++--- src/SignatureTST.h | 6 ++- src/XMLDocument.h | 12 ++--- src/crypto/Signer.cpp | 8 ++-- src/digidoc-tool.1.cmake | 2 +- src/digidoc-tool.cpp | 2 +- src/util/ZipSerialize.cpp | 32 +++++--------- src/util/ZipSerialize.h | 12 ++++- 15 files changed, 185 insertions(+), 127 deletions(-) diff --git a/src/ASiC_E.cpp b/src/ASiC_E.cpp index 5c69f5f03..32e6ed928 100644 --- a/src/ASiC_E.cpp +++ b/src/ASiC_E.cpp @@ -35,10 +35,6 @@ using namespace digidoc; using namespace digidoc::util; using namespace std; -const string_view ASiC_E::ASIC_TM_PROFILE = "time-mark"; -const string_view ASiC_E::ASIC_TS_PROFILE = "time-stamp"; -const string_view ASiC_E::ASIC_TSA_PROFILE = "time-stamp-archive"; -const string_view ASiC_E::ASIC_TMA_PROFILE = "time-mark-archive"; constexpr string_view MANIFEST_NS {"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"}; class ASiC_E::Private @@ -85,40 +81,21 @@ vector ASiC_E::metaFiles() const * document does not exist. * @throws Exception is thrown if ASiC_E class is not correctly initialized. */ -void ASiC_E::save(const string &path) +void ASiC_E::save(const ZipSerialize &s) { - if(dataFiles().empty()) - THROW("Can not save, container is empty."); - if(mediaType() != MIMETYPE_ASIC_E) - THROW("'%s' format is not supported", mediaType().c_str()); - - if(!path.empty()) - zpath(path); - ZipSerialize s(zpath(), true); - - stringstream mimetype; - mimetype << mediaType(); - s.addFile("mimetype", mimetype, zproperty("mimetype"), false); - - stringstream manifest; - createManifest(manifest); - s.addFile("META-INF/manifest.xml", manifest, zproperty("META-INF/manifest.xml")); - - for(const DataFile *file: dataFiles()) - s.addFile(file->fileName(), *(static_cast(file)->m_is), zproperty(file->fileName())); + if(!createManifest().save(s.addFile("META-INF/manifest.xml", zproperty("META-INF/manifest.xml")))) + THROW("Failed to create manifest XML"); std::set saved; unsigned int i = 0; for(Signature *iter: signatures()) { - string file = Log::format("META-INF/signatures%u.xml", i++); - auto *signature = static_cast(iter); + auto *signature = dynamic_cast(iter); if(!saved.insert(signature->signatures.get()).second) continue; - stringstream ofs; - if(!signature->signatures->save(ofs)) + string file = Log::format("META-INF/signatures%u.xml", i++); + if(!signature->signatures->save(s.addFile(file, zproperty(file)))) THROW("Failed to create signature XML file."); - s.addFile(file, ofs, zproperty(file)); } } @@ -164,14 +141,10 @@ unique_ptr ASiC_E::openInternal(const string &path) /** * Creates BDoc container manifest file and returns its path. * - * Note: If non-ascii characters are present in XML data, we depend on the LANG variable to be set properly - * (see iconv --list for the list of supported encoding values for libiconv). - * - * * @return returns created manifest file path. * @throws Exception exception is thrown if manifest file creation failed. */ -void ASiC_E::createManifest(ostream &os) +XMLDocument ASiC_E::createManifest() { DEBUG("ASiC_E::createManifest()"); auto doc = XMLDocument::create("manifest", MANIFEST_NS, "manifest"); @@ -184,8 +157,7 @@ void ASiC_E::createManifest(ostream &os) add("/", mediaType()); for(const DataFile *file: dataFiles()) add(file->fileName(), file->mediaType()); - if(!doc.save(os)) - THROW("Failed to create manifest XML"); + return doc; } /** @@ -291,7 +263,7 @@ Signature* ASiC_E::prepareSignature(Signer *signer) Signature *ASiC_E::sign(Signer* signer) { - auto *s = static_cast(prepareSignature(signer)); + auto *s = dynamic_cast(prepareSignature(signer)); try { s->setSignatureValue(signer->sign(s->signatureMethod(), s->dataToSign())); diff --git a/src/ASiC_E.h b/src/ASiC_E.h index 0a61ba87f..4e0823ddf 100644 --- a/src/ASiC_E.h +++ b/src/ASiC_E.h @@ -23,6 +23,8 @@ namespace digidoc { + class XMLDocument; + /** * Implements the BDOC specification of the signed digital document container. * Container can contain several files and all these files can be signed using @@ -34,13 +36,12 @@ namespace digidoc class ASiC_E final : public ASiContainer { public: - static const std::string_view ASIC_TM_PROFILE; - static const std::string_view ASIC_TS_PROFILE; - static const std::string_view ASIC_TMA_PROFILE; - static const std::string_view ASIC_TSA_PROFILE; + static constexpr std::string_view ASIC_TM_PROFILE = "time-mark"; + static constexpr std::string_view ASIC_TS_PROFILE = "time-stamp"; + static constexpr std::string_view ASIC_TMA_PROFILE = "time-mark-archive"; + static constexpr std::string_view ASIC_TSA_PROFILE = "time-stamp-archive"; ~ASiC_E() final; - void save(const std::string &path = {}) final; std::vector metaFiles() const; void addAdESSignature(std::istream &data) final; @@ -54,8 +55,9 @@ namespace digidoc ASiC_E(); ASiC_E(const std::string &path); DISABLE_COPY(ASiC_E); - void createManifest(std::ostream &os); + XMLDocument createManifest(); void parseManifestAndLoadFiles(const ZipSerialize &z); + void save(const ZipSerialize &s) final; class Private; std::unique_ptr d; diff --git a/src/ASiC_S.cpp b/src/ASiC_S.cpp index 78fa9b51e..8286fe5af 100644 --- a/src/ASiC_S.cpp +++ b/src/ASiC_S.cpp @@ -21,6 +21,7 @@ #include "SignatureTST.h" #include "SignatureXAdES_LTA.h" +#include "crypto/Signer.h" #include "util/File.h" #include "util/log.h" @@ -34,47 +35,51 @@ using namespace std; /** * Initialize ASiCS container. */ -ASiC_S::ASiC_S(): ASiContainer(MIMETYPE_ASIC_S) +ASiC_S::ASiC_S() + : ASiContainer(MIMETYPE_ASIC_S) {} /** * Opens ASiC-S container from a file */ -ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S) +ASiC_S::ASiC_S(const string &path) + : ASiContainer(MIMETYPE_ASIC_S) { auto z = load(path, false, {mediaType()}); - static const string_view metaInf = "META-INF/"; + auto starts_with = [](string_view str, string_view needle) constexpr { + return str.size() >= needle.size() && str.compare(0, needle.size(), needle) == 0; + }; for(const string &file: z.list()) { - if(file == "mimetype" || - (metaInf.size() < file.size() && file.compare(0, metaInf.size(), metaInf) == 0)) - { - if(file == "META-INF/timestamp.tst") - { - if(!signatures().empty()) - THROW("Can not add signature to ASiC-S container which already contains a signature."); - addSignature(make_unique(z.extract(file).str(), this)); - } - if(file == "META-INF/signatures.xml") - { - if(!signatures().empty()) - THROW("Can not add signature to ASiC-S container which already contains a signature."); - auto data = z.extract(file); - auto signatures = make_shared(data, this); - for(auto s = signatures->signature(); s; s++) - addSignature(make_unique(signatures, s, this)); - } + if(file == "mimetype") continue; + if(file == "META-INF/timestamp.tst") + { + if(!signatures().empty()) + THROW("Can not add signature to ASiC-S container which already contains a signature."); + addSignature(make_unique(z.extract(file).str(), this)); } - - const auto directory = File::directory(file); - if(directory.empty() || directory == "/" || directory == "./") + else if(file == "META-INF/signatures.xml") { - if(!dataFiles().empty()) - THROW("Can not add document to ASiC-S container which already contains a document."); - addDataFile(dataStream(file, z), file, "application/octet-stream"); + if(!signatures().empty()) + THROW("Can not add signature to ASiC-S container which already contains a signature."); + auto data = z.extract(file); + auto signatures = make_shared(data, this); + for(auto s = signatures->signature(); s; s++) + addSignature(make_unique(signatures, s, this)); } + else if(file == "META-INF/ASiCArchiveManifest.xml") + THROW("ASiCArchiveManifest are not supported."); + else if(starts_with(file, "META-INF/")) + continue; + else if(const auto directory = File::directory(file); + !directory.empty() && directory != "/" && directory != "./") + THROW("Subfolders are not supported %s", directory.c_str()); + else if(!dataFiles().empty()) + THROW("Can not add document to ASiC-S container which already contains a document."); + else + addDataFile(dataStream(file, z), file, "application/octet-stream"); } if(dataFiles().empty()) @@ -83,14 +88,21 @@ ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S) THROW("ASiC-S container does not contain any signatures."); } -void ASiC_S::save(const string & /*path*/) +void ASiC_S::addDataFileChecks(const string &fileName, const string &mediaType) { - THROW("Not implemented."); + ASiContainer::addDataFileChecks(fileName, mediaType); + if(!dataFiles().empty()) + THROW("Can not add document to ASiC-S container which already contains a document."); } -unique_ptr ASiC_S::createInternal(const string & /*path*/) +unique_ptr ASiC_S::createInternal(const string &path) { - return {}; + if(!util::File::fileExtension(path, {"asics", "scs"})) + return {}; + DEBUG("ASiC_S::createInternal(%s)", path.c_str()); + auto doc = unique_ptr(new ASiC_S()); + doc->zpath(path); + return doc; } void ASiC_S::addAdESSignature(istream & /*signature*/) @@ -111,9 +123,23 @@ Signature* ASiC_S::prepareSignature(Signer * /*signer*/) THROW("Not implemented."); } -Signature *ASiC_S::sign(Signer * /*signer*/) +void ASiC_S::save(const ZipSerialize &s) { - THROW("Not implemented."); + auto list = signatures(); + if(list.empty()) + return; + if(list.front()->profile() != ASIC_TST_PROFILE) + THROW("ASiC-S container supports only TimeStampToken signing."); + s.addFile("META-INF/timestamp.tst", zproperty("META-INF/timestamp.tst"))(dynamic_cast(list.front())->save()); +} + +Signature *ASiC_S::sign(Signer *signer) +{ + if(signer->profile() != ASIC_TST_PROFILE) + THROW("ASiC-S container supports only TimeStampToken signing."); + if(!signatures().empty()) + THROW("ASiC-S container supports only one TimeStampToken signature."); + return addSignature(make_unique(this)); } /** diff --git a/src/ASiC_S.h b/src/ASiC_S.h index e1a91be46..2f8512f22 100644 --- a/src/ASiC_S.h +++ b/src/ASiC_S.h @@ -30,9 +30,8 @@ namespace digidoc */ class ASiC_S : public ASiContainer { - public: - void save(const std::string &path = {}) override; + static constexpr std::string_view ASIC_TST_PROFILE = "TimeStampToken"; void addAdESSignature(std::istream &sigdata) override; Signature* prepareSignature(Signer *signer) override; @@ -46,6 +45,9 @@ namespace digidoc ASiC_S(const std::string &path); DISABLE_COPY(ASiC_S); + void addDataFileChecks(const std::string &path, const std::string &mediaType) override; + void save(const ZipSerialize &s) final; + static bool isContainerSimpleFormat(const std::string &path); }; } diff --git a/src/ASiContainer.cpp b/src/ASiContainer.cpp index 49cc96fd4..12e4ccf2f 100644 --- a/src/ASiContainer.cpp +++ b/src/ASiContainer.cpp @@ -25,6 +25,7 @@ #include "util/log.h" #include +#include #include #include #include @@ -51,7 +52,7 @@ class ASiContainer::Private ASiContainer::ASiContainer(string_view mimetype) : d(make_unique()) { - d->mimetype = mimetype; + d->mimetype = string(mimetype); } /** @@ -166,7 +167,7 @@ void ASiContainer::addDataFile(const string &path, const string &mediaType) is = std::move(data); } d->properties[fileName] = { appInfo(), File::modifiedTime(path), size }; - addDataFilePrivate(std::move(is), std::move(fileName), mediaType); + d->documents.push_back(new DataFilePrivate(std::move(is), std::move(fileName), mediaType)); } void ASiContainer::addDataFile(unique_ptr is, const string &fileName, const string &mediaType) @@ -174,7 +175,7 @@ void ASiContainer::addDataFile(unique_ptr is, const string &fileName, c addDataFileChecks(fileName, mediaType); if(fileName.find_last_of("/\\") != string::npos) THROW("Document file '%s' cannot contain directory path.", fileName.c_str()); - addDataFilePrivate(std::move(is), fileName, mediaType); + d->documents.push_back(new DataFilePrivate(std::move(is), fileName, mediaType)); } void ASiContainer::addDataFileChecks(const string &fileName, const string &mediaType) @@ -241,6 +242,34 @@ void ASiContainer::deleteSignature(Signature* s) delete s; } +void ASiContainer::save(const string &path) +{ + if(dataFiles().empty()) + THROW("Can not save, container is empty."); + + if(!path.empty()) + zpath(path); + ZipSerialize s(zpath(), true); + s.addFile("mimetype", zproperty("mimetype"), false)(mediaType()); + + array buf{}; + for(const DataFile *file: dataFiles()) + { + auto f = s.addFile(file->fileName(), zproperty(file->fileName())); + const auto &is = dynamic_cast(file)->m_is; + is->clear(); + is->seekg(0); + while(*is) + { + is->read(buf.data(), buf.size()); + if(auto size = is->gcount(); size > 0) + f(buf.data(), size_t(size)); + } + } + + save(s); +} + void ASiContainer::zpath(const string &file) { d->path = file; @@ -251,11 +280,11 @@ string ASiContainer::zpath() const return d->path; } -const ZipSerialize::Properties& ASiContainer::zproperty(const string &file) const +const ZipSerialize::Properties& ASiContainer::zproperty(string_view file) const { if(auto i = d->properties.find(file); i != d->properties.cend()) return i->second; - return d->properties[file] = { appInfo(), time(nullptr), 0 }; + return d->properties[string(file)] = { appInfo(), time(nullptr), 0 }; } /** diff --git a/src/ASiContainer.h b/src/ASiContainer.h index b71a076f5..eeaa34183 100644 --- a/src/ASiContainer.h +++ b/src/ASiContainer.h @@ -51,6 +51,7 @@ namespace digidoc std::vector dataFiles() const override; void removeDataFile(unsigned int id) override; void removeSignature(unsigned int id) override; + void save(const std::string &path) override; std::vector signatures() const override; static std::string readMimetype(const ZipSerialize &z); @@ -58,21 +59,21 @@ namespace digidoc protected: ASiContainer(std::string_view mimetype); + virtual void addDataFileChecks(const std::string &path, const std::string &mediaType); void addDataFilePrivate(std::unique_ptr is, std::string fileName, std::string mediaType); Signature* addSignature(std::unique_ptr &&signature); std::unique_ptr dataStream(std::string_view path, const ZipSerialize &z) const; ZipSerialize load(const std::string &path, bool requireMimetype, const std::set &supported); + virtual void save(const ZipSerialize &s) = 0; void deleteSignature(Signature* s); void zpath(const std::string &file); std::string zpath() const; - const ZipSerialize::Properties &zproperty(const std::string &file) const; + const ZipSerialize::Properties& zproperty(std::string_view file) const; private: DISABLE_COPY(ASiContainer); - void addDataFileChecks(const std::string &path, const std::string &mediaType); - class Private; std::unique_ptr d; }; diff --git a/src/SiVaContainer.cpp b/src/SiVaContainer.cpp index 81557dbd1..234c17ae9 100644 --- a/src/SiVaContainer.cpp +++ b/src/SiVaContainer.cpp @@ -365,7 +365,8 @@ unique_ptr SiVaContainer::parseDDoc(bool useHashCode) dataFile = std::string_view{}; } auto result = make_unique(); - doc.save(*result); + if(!doc.save([&result](const char *data, size_t size) { result->write(data, streamsize(size)); })) + THROW("Failed to save DDoc"); return result; } catch(const Exception &) diff --git a/src/SignatureTST.cpp b/src/SignatureTST.cpp index 6665da184..15a5491f3 100644 --- a/src/SignatureTST.cpp +++ b/src/SignatureTST.cpp @@ -20,6 +20,7 @@ #include "SignatureTST.h" #include "ASiC_S.h" +#include "Conf.h" #include "DataFile_p.h" #include "crypto/TS.h" #include "crypto/X509Cert.h" @@ -34,6 +35,15 @@ SignatureTST::SignatureTST(const string &data, ASiC_S *asicSDoc) , timestampToken(make_unique((const unsigned char*)data.data(), data.size())) {} +SignatureTST::SignatureTST(ASiC_S *asicSDoc) + : asicSDoc(asicSDoc) +{ + auto *dataFile = dynamic_cast(asicSDoc->dataFiles().front()); + Digest digest; + dataFile->digest(digest); + timestampToken = make_unique(CONF(TSUrl), digest); +} + SignatureTST::~SignatureTST() = default; X509Cert SignatureTST::TimeStampCertificate() const @@ -83,10 +93,9 @@ void SignatureTST::validate() const } try { - const string digestMethod = timestampToken->digestMethod(); - timestampToken->verify(asicSDoc->dataFiles().front()->calcDigest(digestMethod)); - - if(!Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) && + timestampToken->verify(dataToSign()); + if(auto digestMethod = signatureMethod(); + !Exception::hasWarningIgnore(Exception::ReferenceDigestWeak) && Digest::isWeakDigest(digestMethod)) { Exception e(EXCEPTION_PARAMS("TimeStamp '%s' digest weak", digestMethod.c_str())); @@ -105,7 +114,12 @@ void SignatureTST::validate() const std::vector SignatureTST::dataToSign() const { - THROW("Not implemented."); + return asicSDoc->dataFiles().front()->calcDigest(signatureMethod()); +} + +vector SignatureTST::messageImprint() const +{ + return timestampToken->messageImprint(); } void SignatureTST::setSignatureValue(const std::vector & /*signatureValue*/) @@ -116,5 +130,10 @@ void SignatureTST::setSignatureValue(const std::vector & /*signat // Xades properties string SignatureTST::profile() const { - return "TimeStampToken"; + return string(ASiC_S::ASIC_TST_PROFILE); +} + +std::vector SignatureTST::save() const +{ + return *timestampToken; } diff --git a/src/SignatureTST.h b/src/SignatureTST.h index 52f159eea..a375d18d1 100644 --- a/src/SignatureTST.h +++ b/src/SignatureTST.h @@ -32,8 +32,10 @@ class SignatureTST final: public Signature { public: SignatureTST(const std::string &data, ASiC_S *asicSDoc); + SignatureTST(ASiC_S *asicSDoc); ~SignatureTST(); + std::vector messageImprint() const override; std::string trustedSigningTime() const final; X509Cert TimeStampCertificate() const final; @@ -51,9 +53,11 @@ class SignatureTST final: public Signature // Xades properties std::string profile() const final; + std::vector save() const; + private: DISABLE_COPY(SignatureTST); - ASiC_S *asicSDoc = nullptr; + ASiC_S *asicSDoc {}; std::unique_ptr timestampToken; }; diff --git a/src/XMLDocument.h b/src/XMLDocument.h index 2cea1de81..8af1bee35 100644 --- a/src/XMLDocument.h +++ b/src/XMLDocument.h @@ -34,7 +34,6 @@ #include #include -#include namespace digidoc { @@ -403,12 +402,15 @@ struct XMLDocument: public unique_xml_t, public XMLNode return xmlSaveFormatFileEnc(path.data(), get(), "UTF-8", 0) > 0; } - bool save(std::ostream &os) const noexcept + template + std::enable_if_t, bool> + save(F &&f) const noexcept { auto *buf = xmlOutputBufferCreateIO([](void *context, const char *buffer, int len) { - auto *os = static_cast(context); - return os->write(buffer, len) ? len : -1; - }, nullptr, &os, nullptr); + auto *f = static_cast(context); + (*f)(buffer, size_t(len)); + return len; + }, nullptr, &f, nullptr); return xmlSaveFormatFileTo(buf, get(), "UTF-8", 0) > 0; } diff --git a/src/crypto/Signer.cpp b/src/crypto/Signer.cpp index deb69d37f..14cd2e79d 100644 --- a/src/crypto/Signer.cpp +++ b/src/crypto/Signer.cpp @@ -20,6 +20,7 @@ #include "Signer.h" #include "ASiC_E.h" +#include "ASiC_S.h" #include "Conf.h" #include "crypto/Digest.h" #include "crypto/X509Cert.h" @@ -38,7 +39,7 @@ class Signer::Private { public: optional method; - string profile = "time-stamp"; + string profile{ASiC_E::ASIC_TS_PROFILE}; bool ENProfile = false; string city, streetAddress, stateOrProvince, postalCode, countryName; vector signerRoles; @@ -171,9 +172,10 @@ void Signer::setProfile(const string &profile) {"TSA", ASiC_E::ASIC_TSA_PROFILE}, {ASiC_E::ASIC_TS_PROFILE, ASiC_E::ASIC_TS_PROFILE}, {ASiC_E::ASIC_TSA_PROFILE, ASiC_E::ASIC_TSA_PROFILE}, + {ASiC_S::ASIC_TST_PROFILE, ASiC_S::ASIC_TST_PROFILE}, + {"time-stamp-token", ASiC_S::ASIC_TST_PROFILE} }; - if(auto it = std::find_if(profiles.cbegin(), profiles.cend(), [&profile](const auto &elem) { return elem.first == profile; }); - it != profiles.cend()) + if(auto it = profiles.find(profile); it != profiles.cend()) d->profile = it->second; else THROW("Unsupported profile: %s", profile.c_str()); diff --git a/src/digidoc-tool.1.cmake b/src/digidoc-tool.1.cmake index 857a9e18d..c3286cdfc 100644 --- a/src/digidoc-tool.1.cmake +++ b/src/digidoc-tool.1.cmake @@ -49,7 +49,7 @@ Command websign: Command sign: Example: digidoc-tool sign demo-container.asice Available options: - --profile= - signature profile, TS, TSA, time-stamp, time-stamp-archive + --profile= - signature profile, TS, TSA, time-stamp, time-stamp-archive, TimeStampToken, time-stamp-token --XAdESEN - use XAdES EN profile --city= - city of production place --street= - streetAddress of production place in XAdES EN profile diff --git a/src/digidoc-tool.cpp b/src/digidoc-tool.cpp index 731830079..953662219 100644 --- a/src/digidoc-tool.cpp +++ b/src/digidoc-tool.cpp @@ -344,7 +344,7 @@ static int printUsage(const char *executable) << " Command sign:" << endl << " Example: " << executable << " sign demo-container.asice" << endl << " Available options:" << endl - << " --profile= - signature profile, TS, TSA, time-stamp, time-stamp-archive" << endl + << " --profile= - signature profile, TS, TSA, time-stamp, time-stamp-archive, TimeStampToken, time-stamp-token" << endl << " --XAdESEN - use XAdES EN profile" << endl << " --city= - city of production place" << endl << " --street= - streetAddress of production place in XAdES EN profile" << endl diff --git a/src/util/ZipSerialize.cpp b/src/util/ZipSerialize.cpp index 74e5ac0f6..f157ec661 100644 --- a/src/util/ZipSerialize.cpp +++ b/src/util/ZipSerialize.cpp @@ -166,9 +166,10 @@ template stringstream ZipSerialize::extract(string_view file) cons * @param containerPath file path inside ZIP file. * @param prop Properties added for file in ZIP file. * @param compress File should be compressed in ZIP file. - * @throws IOException throws exception if there were errors during locating files in zip. + * @return Write struct for data input + * @throws Exception throws exception if there were errors during locating files in zip. */ -void ZipSerialize::addFile(const string& containerPath, istream &is, const Properties &prop, bool compress) +ZipSerialize::Write ZipSerialize::addFile(string_view containerPath, const Properties &prop, bool compress) const { if(!d) THROW("Zip file is not open"); @@ -190,26 +191,7 @@ void ZipSerialize::addFile(const string& containerPath, istream &is, const Prope if(zipResult != ZIP_OK) THROW("Failed to create new file inside ZIP container. ZLib error: %d", zipResult); - is.clear(); - is.seekg(0); - array buf{}; - while(is) - { - is.read(buf.data(), buf.size()); - if(is.gcount() <= 0) - break; - - zipResult = zipWriteInFileInZip(d.get(), buf.data(), unsigned(is.gcount())); - if(zipResult != ZIP_OK) - { - zipCloseFileInZip(d.get()); - THROW("Failed to write bytes to current file inside ZIP container. ZLib error: %d", zipResult); - } - } - - zipResult = zipCloseFileInZip(d.get()); - if(zipResult != ZIP_OK) - THROW("Failed to close current file inside ZIP container. ZLib error: %d", zipResult); + return {{d.get(), zipCloseFileInZip}}; } ZipSerialize::Properties ZipSerialize::properties(const string &file) const @@ -239,3 +221,9 @@ ZipSerialize::Properties ZipSerialize::properties(const string &file) const return prop; } + +void ZipSerialize::Write::operator()(const void *data, size_t size) const +{ + if(auto result = zipWriteInFileInZip(d.get(), data, unsigned(size)); result != ZIP_OK) + THROW("Failed to write bytes to current file inside ZIP container. ZLib error: %d", result); +} diff --git a/src/util/ZipSerialize.h b/src/util/ZipSerialize.h index 6e8c82075..4f140e5fe 100644 --- a/src/util/ZipSerialize.h +++ b/src/util/ZipSerialize.h @@ -33,6 +33,16 @@ namespace digidoc class ZipSerialize { public: + struct Write { + void operator ()(const void *data, size_t size) const; + template + constexpr void operator ()(const T &data) const + { + operator ()(data.data(), data.size()); + } + std::unique_ptr d; + }; + struct Properties { std::string comment; time_t time; @@ -44,7 +54,7 @@ namespace digidoc std::vector list() const; template T extract(std::string_view file) const; - void addFile(const std::string &containerPath, std::istream &is, const Properties &prop, bool compress = true); + Write addFile(std::string_view containerPath, const Properties &prop, bool compress = true) const; Properties properties(const std::string &file) const; private: