From 65e8641cf2c5de88678a0a37998157e53480980e Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Mon, 30 Sep 2024 15:25:57 +0300 Subject: [PATCH] Check duplicate files in Zip all code paths IB-8230 Signed-off-by: Raul Metsma --- src/ASiC_E.cpp | 41 +++-------- src/ASiC_E.h | 2 - src/ASiC_S.cpp | 14 ++-- src/ASiC_S.h | 2 - src/ASiContainer.cpp | 31 +++----- src/ASiContainer.h | 8 +-- src/SiVaContainer.cpp | 12 ++-- src/SignatureTST.cpp | 17 ++--- src/SignatureTST.h | 2 +- src/util/ZipSerialize.cpp | 146 ++++++++++++++++++-------------------- src/util/ZipSerialize.h | 26 ++++--- 11 files changed, 125 insertions(+), 176 deletions(-) diff --git a/src/ASiC_E.cpp b/src/ASiC_E.cpp index 46a2802ea..5c69f5f03 100644 --- a/src/ASiC_E.cpp +++ b/src/ASiC_E.cpp @@ -26,7 +26,6 @@ #include "crypto/Digest.h" #include "crypto/Signer.h" #include "util/File.h" -#include "util/ZipSerialize.h" #include #include @@ -65,7 +64,7 @@ ASiC_E::ASiC_E(const string &path) , d(make_unique()) { auto zip = load(path, true, {MIMETYPE_ASIC_E, MIMETYPE_ADOC}); - parseManifestAndLoadFiles(*zip); + parseManifestAndLoadFiles(zip); } ASiC_E::~ASiC_E() @@ -99,7 +98,7 @@ void ASiC_E::save(const string &path) stringstream mimetype; mimetype << mediaType(); - s.addFile("mimetype", mimetype, zproperty("mimetype"), ZipSerialize::DontCompress); + s.addFile("mimetype", mimetype, zproperty("mimetype"), false); stringstream manifest; createManifest(manifest); @@ -193,9 +192,6 @@ void ASiC_E::createManifest(ostream &os) * Parses manifest file and checks that files described in manifest exist, also * checks that no extra file do exist that are not described in manifest.xml. * - * 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). - * * @param path directory on disk of the BDOC container. * @throws Exception exception is thrown if the manifest.xml file parsing failed. */ @@ -203,22 +199,14 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z) { DEBUG("ASiC_E::readManifest()"); - const vector &list = z.list(); - auto mcount = size_t(count(list.cbegin(), list.cend(), "META-INF/manifest.xml")); - if(mcount < 1) - THROW("Manifest file is missing"); - if(mcount > 1) - THROW("Found multiple manifest files"); - try { - stringstream manifestdata; - z.extract("META-INF/manifest.xml", manifestdata); + auto manifestdata = z.extract("META-INF/manifest.xml"); + auto doc = XMLDocument::openStream(manifestdata, {"manifest", MANIFEST_NS}); + doc.validateSchema(File::path(Conf::instance()->xsdPath(), "OpenDocument_manifest_v1_2.xsd")); set manifestFiles; bool mimeFound = false; - auto doc = XMLDocument::openStream(manifestdata, {"manifest", MANIFEST_NS}); - doc.validateSchema(File::path(Conf::instance()->xsdPath(), "OpenDocument_manifest_v1_2.xsd")); for(auto file = doc/"file-entry"; file; file++) { auto full_path = file[{"full-path", MANIFEST_NS}]; @@ -239,24 +227,18 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z) if(full_path.back() == '/') // Skip Directory entries continue; - auto fcount = size_t(count(list.cbegin(), list.cend(), full_path)); - if(fcount < 1) - THROW("File described in manifest '%s' does not exist in container.", full_path.data()); - if(fcount > 1) - THROW("Found multiple references of file '%s' in zip container.", full_path.data()); - manifestFiles.insert(full_path); if(mediaType() == MIMETYPE_ADOC && (full_path.compare(0, 9, "META-INF/") == 0 || full_path.compare(0, 9, "metadata/") == 0)) - d->metadata.push_back(new DataFilePrivate(dataStream(string(full_path), z), string(full_path), string(media_type))); + d->metadata.push_back(new DataFilePrivate(dataStream(full_path, z), string(full_path), string(media_type))); else - addDataFilePrivate(dataStream(string(full_path), z), string(full_path), string(media_type)); + addDataFilePrivate(dataStream(full_path, z), string(full_path), string(media_type)); } if(!mimeFound) THROW("Manifest is missing mediatype file entry."); - for(const string &file: list) + for(const string &file: z.list()) { /** * http://www.etsi.org/deliver/etsi_ts/102900_102999/102918/01.03.01_60/ts_102918v010301p.pdf @@ -266,12 +248,9 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z) if(file.compare(0, 9, "META-INF/") == 0 && file.find("signatures") != string::npos) { - if(count(list.begin(), list.end(), file) > 1) - THROW("Multiple signature files with same name found '%s'", file.c_str()); try { - stringstream data; - z.extract(file, data); + auto data = z.extract(file); auto signatures = make_shared(data, this); for(auto s = signatures->signature(); s; s++) addSignature(make_unique(signatures, s, this)); @@ -285,7 +264,7 @@ void ASiC_E::parseManifestAndLoadFiles(const ZipSerialize &z) if(file == "mimetype" || file.compare(0, 8,"META-INF") == 0) continue; - if(manifestFiles.find(file) == manifestFiles.end()) + if(manifestFiles.count(file) == 0) THROW("File '%s' found in container is not described in manifest.", file.c_str()); } } diff --git a/src/ASiC_E.h b/src/ASiC_E.h index 0ed9b99fe..0a61ba87f 100644 --- a/src/ASiC_E.h +++ b/src/ASiC_E.h @@ -23,8 +23,6 @@ namespace digidoc { - class ZipSerialize; - /** * Implements the BDOC specification of the signed digital document container. * Container can contain several files and all these files can be signed using diff --git a/src/ASiC_S.cpp b/src/ASiC_S.cpp index 1dd0df8bc..78fa9b51e 100644 --- a/src/ASiC_S.cpp +++ b/src/ASiC_S.cpp @@ -23,7 +23,6 @@ #include "SignatureXAdES_LTA.h" #include "util/File.h" #include "util/log.h" -#include "util/ZipSerialize.h" #include #include @@ -46,7 +45,7 @@ ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S) auto z = load(path, false, {mediaType()}); static const string_view metaInf = "META-INF/"; - for(const string &file: z->list()) + for(const string &file: z.list()) { if(file == "mimetype" || (metaInf.size() < file.size() && file.compare(0, metaInf.size(), metaInf) == 0)) @@ -55,16 +54,13 @@ ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S) { if(!signatures().empty()) THROW("Can not add signature to ASiC-S container which already contains a signature."); - stringstream data; - z->extract(file, data); - addSignature(make_unique(data, this)); + 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."); - stringstream data; - z->extract(file, data); + auto data = z.extract(file); auto signatures = make_shared(data, this); for(auto s = signatures->signature(); s; s++) addSignature(make_unique(signatures, s, this)); @@ -77,7 +73,7 @@ ASiC_S::ASiC_S(const string &path): ASiContainer(MIMETYPE_ASIC_S) { 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"); + addDataFile(dataStream(file, z), file, "application/octet-stream"); } } @@ -139,7 +135,7 @@ bool ASiC_S::isContainerSimpleFormat(const string &path) { ZipSerialize z(path, false); vector list = z.list(); - return !list.empty() && list.front() == "mimetype" && readMimetype(z) == MIMETYPE_ASIC_S; + return list.front() == "mimetype" && readMimetype(z) == MIMETYPE_ASIC_S; } catch(const Exception &) { diff --git a/src/ASiC_S.h b/src/ASiC_S.h index 3af245268..e1a91be46 100644 --- a/src/ASiC_S.h +++ b/src/ASiC_S.h @@ -23,8 +23,6 @@ namespace digidoc { - class ZipSerialize; - /** * Implements the ASiC-S specification of the timestamped digital document container. * Container contains a single datafile object and one time assertion file. diff --git a/src/ASiContainer.cpp b/src/ASiContainer.cpp index e45c6c52a..49cc96fd4 100644 --- a/src/ASiContainer.cpp +++ b/src/ASiContainer.cpp @@ -42,7 +42,7 @@ class ASiContainer::Private string mimetype, path; vector documents; vector signatures; - map properties; + map> properties; }; /** @@ -62,14 +62,11 @@ ASiContainer::ASiContainer(string_view mimetype) * @param supported supported mimetypes. * @return returns zip serializer for the container. */ -unique_ptr ASiContainer::load(const string &path, bool mimetypeRequired, const set &supported) +ZipSerialize ASiContainer::load(const string &path, bool mimetypeRequired, const set &supported) { DEBUG("ASiContainer::ASiContainer(path = '%s')", path.c_str()); - auto z = make_unique(d->path = path, false); - - vector list = z->list(); - if(list.empty()) - THROW("Failed to parse container"); + ZipSerialize z(d->path = path, false); + vector list = z.list(); // ETSI TS 102 918: mimetype has to be the first in the archive if(mimetypeRequired && list.front() != "mimetype") @@ -77,14 +74,14 @@ unique_ptr ASiContainer::load(const string &path, bool mimetypeReq if(list.front() == "mimetype") { - d->mimetype = readMimetype(*z); + d->mimetype = readMimetype(z); if(supported.find(d->mimetype) == supported.cend()) THROW("Incorrect mimetype '%s'", d->mimetype.c_str()); } DEBUG("mimetype = '%s'", d->mimetype.c_str()); for(const string &file: list) - d->properties[file] = z->properties(file); + d->properties[file] = z.properties(file); return z; } @@ -134,15 +131,11 @@ vector ASiContainer::signatures() const * @param z Zip container. * @return returns data as a stream. */ -unique_ptr ASiContainer::dataStream(const string &path, const ZipSerialize &z) const +unique_ptr ASiContainer::dataStream(string_view path, const ZipSerialize &z) const { - unique_ptr data; - if(d->properties[path].size > MAX_MEM_FILE) - data = make_unique(File::tempFileName(), fstream::in|fstream::out|fstream::binary|fstream::trunc); - else - data = make_unique(); - z.extract(path, *data); - return data; + if(auto i = d->properties.find(path); i != d->properties.cend() && i->second.size > MAX_MEM_FILE) + return make_unique(z.extract(path)); + return make_unique(z.extract(path)); } /** @@ -275,9 +268,7 @@ const ZipSerialize::Properties& ASiContainer::zproperty(const string &file) cons string ASiContainer::readMimetype(const ZipSerialize &z) { DEBUG("ASiContainer::readMimetype()"); - stringstream is; - z.extract("mimetype", is); - string text = is.str(); + string text = z.extract("mimetype").str(); text.erase(text.find_last_not_of(" \n\r\f\t\v") + 1); if(text.empty()) THROW("Failed to read mimetype."); diff --git a/src/ASiContainer.h b/src/ASiContainer.h index 56162326c..b71a076f5 100644 --- a/src/ASiContainer.h +++ b/src/ASiContainer.h @@ -28,11 +28,11 @@ namespace digidoc { /** * Base class for the ASiC (Associated Signature Container) documents. - * Implements the operations and data structures common for more specific ASiC + * Implements the operations and data structures common for more specific ASiC * signature containers like ASiC-S and ASiC-E (e.g. Estonian BDoc). * See standards ETSI TS 102 918, ETSI TS 103 171, ETSI TS 103 174 for details. * - * Contains methods for detecting the container type and manipulating the container's + * Contains methods for detecting the container type and manipulating the container's * zip archive. */ class ASiContainer: public Container @@ -60,8 +60,8 @@ namespace digidoc void addDataFilePrivate(std::unique_ptr is, std::string fileName, std::string mediaType); Signature* addSignature(std::unique_ptr &&signature); - std::unique_ptr dataStream(const std::string &path, const ZipSerialize &z) const; - std::unique_ptr load(const std::string &path, bool requireMimetype, const std::set &supported); + std::unique_ptr dataStream(std::string_view path, const ZipSerialize &z) const; + ZipSerialize load(const std::string &path, bool requireMimetype, const std::set &supported); void deleteSignature(Signature* s); void zpath(const std::string &file); diff --git a/src/SiVaContainer.cpp b/src/SiVaContainer.cpp index 5fa400f8f..81557dbd1 100644 --- a/src/SiVaContainer.cpp +++ b/src/SiVaContainer.cpp @@ -159,7 +159,7 @@ SiVaContainer::SiVaContainer(const string &path, ContainerOpenCB *cb, bool useHa static const string_view metaInf = "META-INF/"; ZipSerialize z(path, false); vector list = z.list(); - if(list.empty() || list.front() != "mimetype") + if(list.front() != "mimetype") THROW("Missing mimetype"); if(d->mediaType = ASiContainer::readMimetype(z); d->mediaType != ASiContainer::MIMETYPE_ASIC_E && d->mediaType != ASiContainer::MIMETYPE_ASIC_S) @@ -173,13 +173,9 @@ SiVaContainer::SiVaContainer(const string &path, ContainerOpenCB *cb, bool useHa { if(file == "mimetype" || file.rfind(metaInf, 0) == 0) continue; - const auto directory = File::directory(file); - if(directory.empty() || directory == "/" || directory == "./") - { - auto data = make_unique(); - z.extract(file, *data); - d->dataFiles.push_back(new DataFilePrivate(std::move(data), file, "application/octet-stream")); - } + if(const auto directory = File::directory(file); + directory.empty() || directory == "/" || directory == "./") + d->dataFiles.push_back(new DataFilePrivate(make_unique(z.extract(file)), file, "application/octet-stream")); } } else diff --git a/src/SignatureTST.cpp b/src/SignatureTST.cpp index c1b8cd5fa..d50f54254 100644 --- a/src/SignatureTST.cpp +++ b/src/SignatureTST.cpp @@ -29,19 +29,10 @@ using namespace digidoc; using namespace std; -SignatureTST::SignatureTST(istream &is, ASiC_S *asicSDoc): asicSDoc(asicSDoc) -{ - is.seekg(0, istream::end); - istream::pos_type pos = is.tellg(); - const auto size = pos < 0 ? 0 : (unsigned long)pos; - is.clear(); - is.seekg(0, istream::beg); - - vector buf(size, 0); - is.read((char*)buf.data(), streamsize(buf.size())); - - timestampToken = make_unique(buf.data(), buf.size()); -} +SignatureTST::SignatureTST(const string &data, ASiC_S *asicSDoc) + : asicSDoc(asicSDoc) + , timestampToken(make_unique((const unsigned char*)data.data(), data.size())) +{} SignatureTST::~SignatureTST() = default; diff --git a/src/SignatureTST.h b/src/SignatureTST.h index 11053bd68..52f159eea 100644 --- a/src/SignatureTST.h +++ b/src/SignatureTST.h @@ -31,7 +31,7 @@ class TS; class SignatureTST final: public Signature { public: - SignatureTST(std::istream &sigdata, ASiC_S *asicSDoc); + SignatureTST(const std::string &data, ASiC_S *asicSDoc); ~SignatureTST(); std::string trustedSigningTime() const final; diff --git a/src/util/ZipSerialize.cpp b/src/util/ZipSerialize.cpp index 0e7d3fae4..62ba70503 100644 --- a/src/util/ZipSerialize.cpp +++ b/src/util/ZipSerialize.cpp @@ -21,6 +21,7 @@ #include "DateTime.h" #include "log.h" +#include "util/File.h" #include #include @@ -29,155 +30,150 @@ #include #endif +#include #include #include -#include +#include +#include using namespace digidoc; using namespace std; -class ZipSerialize::Private -{ -public: - zlib_filefunc_def def {}; - string path; - zipFile create {}; - unzFile open {}; -}; - - - /** * Initializes ZIP file serializer. * * @param path */ -ZipSerialize::ZipSerialize(string path, bool create) - : d(make_unique()) +ZipSerialize::ZipSerialize(const string &path, bool create) + : d{nullptr, create ? [](void *handle) { return zipClose(handle, nullptr); } : &unzClose} { + zlib_filefunc_def def {}; #ifdef _WIN32 - fill_win32_filefunc(&d->def); + fill_win32_filefunc(&def); #else - fill_fopen_filefunc(&d->def); + fill_fopen_filefunc(&def); #endif - d->path = std::move(path); if(create) { - DEBUG("ZipSerialize::create(%s)", d->path.c_str()); - d->create = zipOpen2((const char*)filesystem::u8path(d->path).c_str(), APPEND_STATUS_CREATE, nullptr, &d->def); - if(!d->create) - THROW("Failed to create ZIP file '%s'.", d->path.c_str()); + DEBUG("ZipSerialize::create(%s)", path.c_str()); + d.reset(zipOpen2((const char*)filesystem::u8path(path).c_str(), APPEND_STATUS_CREATE, nullptr, &def)); + if(!d) + THROW("Failed to create ZIP file '%s'.", path.c_str()); } else { - DEBUG("ZipSerialize::open(%s)", d->path.c_str()); - d->open = unzOpen2((const char*)filesystem::u8path(d->path).c_str(), &d->def); - if(!d->open) - THROW("Failed to open ZIP file '%s'.", d->path.c_str()); + DEBUG("ZipSerialize::open(%s)", path.c_str()); + d.reset(unzOpen2((const char*)filesystem::u8path(path).c_str(), &def)); + if(!d) + THROW("Failed to open ZIP file '%s'.", path.c_str()); } } /** - * Desctructs ZIP file serializer. - * - * @param path - */ -ZipSerialize::~ZipSerialize() -{ - if(d->create) zipClose(d->create, nullptr); - if(d->open) unzClose(d->open); -} - -/** - * Extracts all files from ZIP file to a temporary directory on disk. + * List all files from ZIP file. * - * @return returns path, where files from ZIP file were extracted. - * @throws IOException throws exception if there were errors during - * extracting files to disk. + * @return returns list of ZIP content. + * @throws Exception throws exception if there were errors during locating files in zip. */ vector ZipSerialize::list() const { - if(!d->open) + if(!d) THROW("Zip file is not open"); vector list; - for(int unzResult = unzGoToFirstFile(d->open); unzResult != UNZ_END_OF_LIST_OF_FILE; unzResult = unzGoToNextFile(d->open)) + for(int unzResult = unzGoToFirstFile(d.get()); unzResult != UNZ_END_OF_LIST_OF_FILE; unzResult = unzGoToNextFile(d.get())) { if(unzResult != UNZ_OK) THROW("Failed to go to the next file inside ZIP container. ZLib error: %d", unzResult); unz_file_info fileInfo{}; - unzResult = unzGetCurrentFileInfo(d->open, &fileInfo, nullptr, 0, nullptr, 0, nullptr, 0); + unzResult = unzGetCurrentFileInfo(d.get(), &fileInfo, nullptr, 0, nullptr, 0, nullptr, 0); if(unzResult != UNZ_OK) THROW("Failed to get filename of the current file inside ZIP container. ZLib error: %d", unzResult); auto &fileName = list.emplace_back(fileInfo.size_filename, 0); - unzResult = unzGetCurrentFileInfo(d->open, &fileInfo, fileName.data(), uLong(fileName.size()), nullptr, 0, nullptr, 0); + unzResult = unzGetCurrentFileInfo(d.get(), nullptr, fileName.data(), uLong(fileName.size()), nullptr, 0, nullptr, 0); if(unzResult != UNZ_OK) THROW("Failed to get filename of the current file inside ZIP container. ZLib error: %d", unzResult); + + if(count(list.cbegin(), list.cend(), fileName) > 1) + THROW("Found multiple references of file '%s' in zip container.", fileName.c_str()); } + if(list.empty()) + THROW("Failed to parse container"); + return list; } /** - * Extracts current file from ZIP file to directory pointed in directory parameter. + * Extracts current file from ZIP file to stream. * - * @param zipFile pointer to opened ZIP file. - * @param directory directory where current file from ZIP should be extracted. - * @throws IOException throws exception if the extraction of the current file fails from ZIP + * @param file path to opened ZIP in file. + * @throws Exception throws exception if the extraction of the current file fails from ZIP * file or creating new file to disk failed. */ -void ZipSerialize::extract(const string &file, ostream &os) const +template +T ZipSerialize::extract(string_view file) const { - DEBUG("ZipSerializePrivate::extract(%s)", file.c_str()); + DEBUG("ZipSerializePrivate::extract(%.*s)", int(file.size()), file.data()); + T os; if(file.empty() || file.back() == '/') - return; + return os; - int unzResult = unzLocateFile(d->open, file.c_str(), 1); + int unzResult = unzLocateFile(d.get(), file.data(), 1); if(unzResult != UNZ_OK) THROW("Failed to locate file inside ZIP container. ZLib error: %d", unzResult); - unzResult = unzOpenCurrentFile(d->open); + unzResult = unzOpenCurrentFile(d.get()); if(unzResult != UNZ_OK) THROW("Failed to open file inside ZIP container. ZLib error: %d", unzResult); + if constexpr(std::is_same_v) + { + os.open(util::File::tempFileName(), fstream::in|fstream::out|fstream::binary|fstream::trunc); + if(!os.is_open()) + THROW("Failed to open destination file"); + } array buf{}; for(int currentStreamSize = 0; - (unzResult = unzReadCurrentFile(d->open, buf.data(), buf.size())) > UNZ_EOF; currentStreamSize += unzResult) + (unzResult = unzReadCurrentFile(d.get(), buf.data(), buf.size())) > UNZ_EOF; currentStreamSize += unzResult) { if(!os.write(buf.data(), unzResult)) { - unzCloseCurrentFile(d->open); - THROW("Failed to write file '%s' data to stream. Stream size: %d", file.c_str(), currentStreamSize); + unzCloseCurrentFile(d.get()); + THROW("Failed to write file '%.*s' data to stream. Stream size: %d", int(file.size()), file.data(), currentStreamSize); } } if(unzResult < UNZ_EOF) { - unzCloseCurrentFile(d->open); + unzCloseCurrentFile(d.get()); THROW("Failed to read bytes from current file inside ZIP container. ZLib error: %d", unzResult); } - unzResult = unzCloseCurrentFile(d->open); + unzResult = unzCloseCurrentFile(d.get()); if(unzResult != UNZ_OK) THROW("Failed to close current file inside ZIP container. ZLib error: %d", unzResult); + return os; } +template fstream ZipSerialize::extract(string_view file) const; +template stringstream ZipSerialize::extract(string_view file) const; + /** - * Add new file to ZIP container. The file is actually archived to ZIP container after save() - * method is called. + * Add new file to ZIP container. * * @param containerPath file path inside ZIP file. - * @param path full path of the file that should be added to ZIP file. - * @see create() - * @see save() + * @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. */ -void ZipSerialize::addFile(const string& containerPath, istream &is, const Properties &prop, Flags flags) +void ZipSerialize::addFile(const string& containerPath, istream &is, const Properties &prop, bool compress) { - if(!d->create) + if(!d) THROW("Zip file is not open"); - DEBUG("ZipSerialize::addFile(%s)", containerPath.c_str()); + DEBUG("ZipSerialize::addFile(%.*s)", int(containerPath.size()), containerPath.data()); tm time = util::date::gmtime(prop.time); zip_fileinfo info { { time.tm_sec, time.tm_min, time.tm_hour, @@ -185,10 +181,10 @@ void ZipSerialize::addFile(const string& containerPath, istream &is, const Prope 0, 0, 0 }; // Create new file inside ZIP container. - int method = flags & DontCompress ? Z_NULL : Z_DEFLATED; - int level = flags & DontCompress ? Z_NO_COMPRESSION : Z_DEFAULT_COMPRESSION; + int method = compress ? Z_DEFLATED : Z_NULL; + int level = compress ? Z_DEFAULT_COMPRESSION : Z_NO_COMPRESSION; static constexpr uLong UTF8_encoding = 1 << 11; // general purpose bit 11 for unicode - int zipResult = zipOpenNewFileInZip4(d->create, containerPath.c_str(), + int zipResult = zipOpenNewFileInZip4(d.get(), containerPath.data(), &info, nullptr, 0, nullptr, 0, prop.comment.c_str(), method, level, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, nullptr, 0, 0, UTF8_encoding); if(zipResult != ZIP_OK) @@ -203,27 +199,27 @@ void ZipSerialize::addFile(const string& containerPath, istream &is, const Prope if(is.gcount() <= 0) break; - zipResult = zipWriteInFileInZip(d->create, buf.data(), unsigned(is.gcount())); + zipResult = zipWriteInFileInZip(d.get(), buf.data(), unsigned(is.gcount())); if(zipResult != ZIP_OK) { - zipCloseFileInZip(d->create); + zipCloseFileInZip(d.get()); THROW("Failed to write bytes to current file inside ZIP container. ZLib error: %d", zipResult); } } - zipResult = zipCloseFileInZip(d->create); + zipResult = zipCloseFileInZip(d.get()); if(zipResult != ZIP_OK) THROW("Failed to close current file inside ZIP container. ZLib error: %d", zipResult); } ZipSerialize::Properties ZipSerialize::properties(const string &file) const { - int unzResult = unzLocateFile(d->open, file.c_str(), 1); + int unzResult = unzLocateFile(d.get(), file.c_str(), 1); if(unzResult != UNZ_OK) THROW("Failed to open file inside ZIP container. ZLib error: %d", unzResult); unz_file_info info; - unzResult = unzGetCurrentFileInfo(d->open, &info, nullptr, 0, nullptr, 0, nullptr, 0); + unzResult = unzGetCurrentFileInfo(d.get(), &info, nullptr, 0, nullptr, 0, nullptr, 0); if(unzResult != UNZ_OK) THROW("Failed to get filename of the current file inside ZIP container. ZLib error: %d", unzResult); @@ -237,7 +233,7 @@ ZipSerialize::Properties ZipSerialize::properties(const string &file) const if(prop.comment.empty()) return prop; - unzResult = unzGetCurrentFileInfo(d->open, &info, nullptr, 0, nullptr, 0, prop.comment.data(), uLong(prop.comment.size())); + unzResult = unzGetCurrentFileInfo(d.get(), nullptr, nullptr, 0, nullptr, 0, prop.comment.data(), uLong(prop.comment.size())); if(unzResult != UNZ_OK) THROW("Failed to get filename of the current file inside ZIP container. ZLib error: %d", unzResult); diff --git a/src/util/ZipSerialize.h b/src/util/ZipSerialize.h index 0f7b79ced..6e8c82075 100644 --- a/src/util/ZipSerialize.h +++ b/src/util/ZipSerialize.h @@ -28,25 +28,29 @@ namespace digidoc { /** - * ZIP file implementation of the ISerialize interface. Saves files to ZIP file - * and extracts the ZIP file on demand. Uses ZLib to implement ZIP file operations. + * Saves files to ZIP file and extracts the ZIP file on demand. Uses ZLib to implement ZIP file operations. */ class ZipSerialize { public: - struct Properties { std::string comment; time_t time; unsigned long size; }; - enum Flags { NoFlags = 0, DontCompress = 1 }; - ZipSerialize(std::string path, bool create); - ~ZipSerialize(); + struct Properties { + std::string comment; + time_t time; + unsigned long size; + }; + + ZipSerialize(const std::string &path, bool create); std::vector list() const; - void extract(const std::string &file, std::ostream &os) const; - void addFile(const std::string &containerPath, std::istream &is, const Properties &prop, Flags flags = NoFlags); + template + T extract(std::string_view file) const; + void addFile(const std::string &containerPath, std::istream &is, const Properties &prop, bool compress = true); Properties properties(const std::string &file) const; private: - DISABLE_COPY(ZipSerialize); - class Private; - std::unique_ptr d; + std::unique_ptr d; }; + + extern template std::fstream ZipSerialize::extract(std::string_view file) const; + extern template std::stringstream ZipSerialize::extract(std::string_view file) const; }