Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Lib][Version 3.0.1][Functional] C++14 wrapper around minizip compression library

License

NotificationsYou must be signed in to change notification settings

Lecrapouille/zipper

Repository files navigation

Zipper

Zipper is a C++14 wrapper around the minizip compression library. Its goal is to bring the power and simplicity of minizip to a more object-oriented and C++ user-friendly library.

This project is a continuation of the originalproject. The original project was created out of the need for a compression library that would be reliable, simple, and flexible. By flexibility, we mean supporting various types of inputs and outputs, specifically the ability to compress into memory instead of being restricted to file compression only, and using data from memory instead of just files.

This current fork repository was created because the original project was no longer maintained by its authors, and I, Lecrapouille, encountered issues due to missing administrative rights (needed for CI, branch management, API breaking changes, etc.).

Zipper Features

  • Create zip files in memory.
  • Support for files, vectors, and generic streams as input for zipping.
  • File mappings for replacement strategies (overwrite if exists or use alternative names from mapping).
  • Password-protected zip (AES).
  • Multi-platform support.
  • Project compiles as both static and dynamic libraries.
  • Protection flags against overwriting existing files during extraction.
  • Protection against theZip Slip attack.
  • API to detectZip Bomb attacks. Extraction is not recursive.
  • Non-regression tests.

⚠️ Security Notice

  • Zipper currently uses an outdated (and potentially vulnerable) version ofminizip from 2017 (SHA1 0bb5afeb0d3f23149b086ccda7e4fee7d48f4fdf) with some custom modifications.

Getting Started

There are two ways to compile the project:

  • Makefile: This is the official compilation method but only supports Linux and macOS
  • CMake: Recently added, it supports all operating systems and was primarily added for Windows support

Compiling / Installing with Makefile (Linux, MacOs, not working for Windows)

This is the official way to download the project and compile it:

git clone https://github.com/lecrapouille/zipper.git --recursivecd zippermake download-external-libsmake compile-external-libsmake -j8

Explanations of compilation commands:

  • Git cloning requires the recursive option to install the Makefile helper and third-party libs (zlib andminizip) in theexternal folder. They are based on fixed SHA1. They are installed in the folderexternal.
  • Optionallymake download-external-libs will git clone HEADs of third-party libs (zlib andminizip) in theexternal folder. It is optional since it was initially used instead of git submodule.
  • make compile-external-libs will compile third-party libs (zlib andminizip) but not install them on your operating system. They are compiled as static libraries and merged into this library inside thebuild folder.
  • make will compile this library against the third-party libs (zlib andminizip). Abuild folder is created with two demos inside. Note:-j8 should be adapted to your number of CPU cores.

See theREADME file for using the demos. To run demos, you can run them:

cd build./unzipper-demo -h./zipper-demo -h

To install C++ header files, shared and static libraries on your operating system, type:

sudo make install

You will see a message like:

*** Installing: doc => /usr/share/Zipper/2.0.0/doc*** Installing: libs => /usr/lib*** Installing: pkg-config => /usr/lib/pkgconfig*** Installing: headers => /usr/include/Zipper-2.0.0*** Installing: Zipper => /usr/include/Zipper-2.0.0

For developers, you can run non regression tests. They depend on:

make tests -j8

Compiling / Installing with CMake (Linux, MacOs, Windows)

As an alternative, you can also build the project using CMake:

git clone https://github.com/lecrapouille/zipper.git --recursivecd zippermkdir buildcd buildcmake .. -DZIPPER_SHARED_LIB=ON -DZIPPER_BUILD_DEMOS=ON -DZIPPER_BUILD_TESTS=ON# Either:cmake --build. --config Release# Or: make -j8

Optional options:

  • -DZIPPER_SHARED_LIB=ON allows creating a shared lib instead of static lib.
  • -DZIPPER_BUILD_DEMOS=ON allows compiling zipper and unzipper "hello world" demos.
  • -DZIPPER_BUILD_TESTS=ON allows compiling unit tests (if you are a developer).

Linking Zipper to your project

  • In your project, add the needed headers in your C++ files:
#include<Zipper/Unzipper.hpp>#include<Zipper/Zipper.hpp>
  • To compile your project "as it" against Zipper, the simplest way is to use thepkg-config command:
g++ -W -Wall --std=c++14 main.cpp -o prog`pkg-config zipper --cflags --libs`
  • For Makefile:

    • setLDFLAGS topkg-config zipper --libs
    • setCPPFLAGS topkg-config zipper --cflags
  • For CMake:

find_package(zipperREQUIRED)target_link_libraries(your_applicationzipper::zipper)

You have an exampledoc/demos/CMakeHelloWorld.

API

There are two classes available:Zipper andUnzipper. They behave in the same manner regarding constructors and storage parameters.

Zipping API

Header

#include<Zipper/Zipper.hpp>usingnamespacezipper;

Constructor

  • Constructor without password and replaceziptest.zip if already present. The new zip archive is empty. The flagZipper::OpenFlags::Overwrite is optional.
Zipperzipper("ziptest.zip", Zipper::OpenFlags::Overwrite);
  • Constructor without password and preserveziptest.zip if already present. The flagZipper::OpenFlags::Append is mandatory!
Zipperzipper("ziptest.zip", Zipper::OpenFlags::Append);
  • Constructor with password (using AES algorithm) and replaceziptest.zip if already present. The new zip archive is empty. The flagZipper::OpenFlags::Overwrite is optional.
Zipperzipper("ziptest.zip","my_password", Zipper::OpenFlags::Overwrite);
  • Constructor with a password and preserveziptest.zip if already present. The flagZipper::OpenFlags::Append is mandatory!
Zipperzipper("ziptest.zip","my_password", Zipper::OpenFlags::Append);
  • Constructor for in-memory zip compression (storage inside std::iostream):
std::stringstream zipStream;Zipperzipper(zipStream);Zipperzipper(zipStream, Zipper::OpenFlags::Overwrite);Zipperzipper(zipStream, Zipper::OpenFlags::Append);Zipperzipper(zipStream,"my_password");Zipperzipper(zipStream,"my_password", Zipper::OpenFlags::Overwrite);Zipperzipper(zipStream,"my_password", Zipper::OpenFlags::Append);
  • Constructor for in-memory zip compression (storage inside std::vector):
std::vector<unsignedchar> zipVector;Zipperzipper(zipVector);Zipperzipper(zipVector, Zipper::OpenFlags::Overwrite);Zipperzipper(zipVector, Zipper::OpenFlags::Append);Zipperzipper(zipVector,"my_password");Zipperzipper(zipVector,"my_password", Zipper::OpenFlags::Overwrite);Zipperzipper(zipVector,"my_password", Zipper::OpenFlags::Append);
  • Note: all constructors will throw astd::runtime_error exception in case of failure.
try{    Zipperzipper("ziptest.zip", ...);    ...}catch (std::runtime_errorconst& e){    std::cerr << e.what() << std::endl;}
  • If this is not a desired behavior, you can choose the alternative dummy constructor followed by theopen method which takes the same arguments as constructors. This method does not throw but will returnfalse in case of error, you can get the reason by callingerror().
// Dummy constructorZipper zipper;// Same arguments than seen previously with constructors.if (!zipper.open(...)){    std::cerr << zipper.error() << std::endl;}

Closing / Reopening

Do not forget to callclose() explicitly (it's called implicitly from the destructor) otherwisethe zip will not be well-formed andUnzipper (or any unzipper application) will fail to open it, for example.

Zipperzipper("ziptest.zip", ...);...zipper.close();// Now Unzipper unzipper("ziptest.zip") can work

Afterclose() you can reopen the zip archive withopen() without arguments. You can pass the same arguments than seen previously with constructors to open with new password or flags. Note: that any open method will call implicitly the close() method.

Zipperzipper("ziptest.zip", ...);...zipper.close();...zipper.open();...zipper.close();

Appending files or folders inside the archive

Theadd() method allows appending files or folders. TheZipper::ZipFlags::Better is set implicitly. Other options are (as the last argument):

  • Store only:Zipper::ZipFlags::Store.
  • Compress faster, less compressed:Zipper::ZipFlags::Faster.
  • Compress intermediate time/compression:Zipper::ZipFlags::Medium.
  • Compress better:Zipper::ZipFlags::Better.
  • To preserve directory hierarchy add| Zipper::ZipFlags::SaveHierarchy else files are only stored.

In case of success, theadd() will returntrue; otherwise it will returnfalse anderror() should be used for getting the std::error_code.

  • Adding an entire folder to a zip:
Zipperzipper("ziptest.zip");zipper.add("myFolder/");zipper.close();
  • Adding a file by name:
Zipperzipper("ziptest.zip");zipper.add("myFolder/somefile.txt");zipper.close();
  • You can change their name in the archive:
Zipperzipper("ziptest.zip");zipper.add("somefile.txt","new_name_in_archive");zipper.close();
  • Create a zip file with 2 files referred by theirstd::ifstream and change their name in the archive:
std::ifstreaminput1("first_file");std::ifstreaminput2("second_file");Zipperzipper("ziptest.zip");zipper.add(input1,"Test1");zipper.add(input2,"Test2");zipper.close();

Note that:

  • thezipper::close() updatesstd::ifstream and makes the in-memory zip well formed.

  • do not use std::ifstream before closed() was called.

  • be sure the std::ifstream is not deleted before closed() was called.

  • Add a file with a specific timestamp:

std::ifstreaminput("somefile.txt");std::tm timestamp;timestamp.tm_year =2024;timestamp.tm_mon =0;timestamp.tm_mday =1;timestamp.tm_hour =12;timestamp.tm_min =1;timestamp.tm_sec =2;Zipperzipper("ziptest.zip");zipper.add(input, timestamp,"somefile.txt");zipper.close();
zipper.add(input1,"../Test1");

Will always returnfalse becauseTest1 would be extracted outside the destination folder. This prevents malicious attacks from replacing your system files:

zipper.add(malicious_passwd,"../../../../../../../../../../../../../../../etc/passwd");

Because in Unix, trying to go outside the root folder/ will stay in the root folder. Example:

cd /pwdcd ../../../../../../../../../../../../../../..pwd
  • The Zipper lib forces canonical paths in the archive. The following code works (will returntrue):
zipper.add(input1,"foo/../Test1");

becausefoo/../Test1 is replaced byTest1 (even if the folderfoo is not present in thezip archive).

In-memory: Vector and stream

  • Do not forget that the close() finalized the in-memory zip file: you cannot use Unzipper on the same memory until the Zipper::close() has been closed (meaning: even if you have called Zipper::add, the zip is not well formed).
  • Creating a zip file using the awesome streams from theboost library that lets us use a vector as a stream:
#include<boost/interprocess/streams/vectorstream.hpp>...boost::interprocess::basic_vectorstream<std::vector<char>>input_data(some_vector);Zipperzipper("ziptest.zip");zipper.add(input_data,"Test1");zipper.close();
  • Creating a zip in a vector with files:
#include<boost/interprocess/streams/vectorstream.hpp>...boost::interprocess::basic_vectorstream<std::vector<char>> zip_in_memory;std::ifstreaminput1("some file");Zipperzipper(zip_in_memory);// You can pass passwordzipper.add(input1,"Test1");zipper.close();Unzipperunzipper(zip_in_memory);unzipper.extract(...

Or:

#include<vector>std::vector<unsignedchar> zip_vector;std::ifstreaminput1("some file");Zipperzipper(zip_vector);// You can pass passwordzipper.add(input1,"Test1");zipper.close();```- Creating a zip in-memory stream with files:```c++// Example of using stringstreamstd::stringstream zipStream;std::stringstreaminputStream("content to zip");Zipperzipper(zipStream);// You can pass passwordzipper.add(inputStream,"Test1");zipper.close();// Example of extractingzipper::Unzipperunzipper(zipData);// or unzipper(zipStream) for stringstreamunzipper.extract(...

Unzipping API

Header

#include<Zipper/Unzipper.hpp>usingnamespacezipper;

Constructor

  • Constructor without password and openingziptest.zip (should already be present).
Unzipperunzipper("ziptest.zip");...unzipper.close();
  • Constructor with a password and openingziptest.zip (should already be present).
Unzipperunzipper("ziptest.zip","my_password");...unzipper.close();
  • Constructor for in-memory zip extraction (from std::iostream):
std::stringstream zipStream;// Without passwordUnzipperunzipper(zipStream);// Or with password:Unzipperunzipper(zipStream,"my_password");
  • Constructor for in-memory zip extraction (from std::vector):
// Without passwordstd::vector<unsignedchar> zipVector;Unzipperunzipper(zipVector);// Or with password:Unzipperunzipper(zipVector,"my_password");
  • Note: all constructors will throw astd::runtime_error exception in case of failure.
try{    Unzipperunzipper("ziptest.zip", ...);    ...}catch (std::runtime_errorconst& e){    std::cerr << e.what() << std::endl;}

Getting the list of zip entries

Unzipperunzipper("zipfile.zip");std::vector<ZipEntry> entries = unzipper.entries();for (auto& it: unzipper.entries()){    std::cout << it.name <<":"              << it.timestamp              << std::endl;}unzipper.close();

Getting the total uncompressed size

Unzipperunzipper("zipfile.zip");size_t total_size = unzipper.sizeOnDisk();if (total_size > MAX_ALLOWED_SIZE) {// Prevent zip bomb attack    std::cerr <<"Zip file too large!" << std::endl;return;}unzipper.close();

Extracting all entries from the zip file

Two methods are available:extractAll() for the whole archive andextract() fora single element in the archive. They returntrue in case of successorfalse in case of failure. In case of failure, useunzipper.error();to get thestd::error_code.

If you are scared ofZip bomb attack you cancheck the total uncompressed size of all entries in the zip archive by callingunzipper.sizeOnDisk() beforeunzipper.extractAll(...).

// 1 gigabyteconstsize_tMAX_TOTAL_UNCOMPRESSED_BYTES (1 *1024 *1024 *1024)Unzipper unzipper("zipfile.zip");if (unzipper.sizeOnDisk() <= MAX_TOTAL_UNCOMPRESSED_BYTES){    unzipper.extractAll(Unzipper::OverwriteMode::Overwrite);}else{    std::cerr <<"Zip bomb attack prevented" << std::endl;}
  • If you do not care about replacing existing files or folders:
Unzipperunzipper("zipfile.zip");unzipper.extractAll(Unzipper::OverwriteMode::Overwrite);unzipper.close();
  • If you care about replacing existing files or folders. The method will fail (returnfalse)if a file would be replaced and the methoderror() will give you information.
Unzipperunzipper("zipfile.zip");unzipper.extractAll();// equivalent to unzipper.extractAll(Unzipper::OverwriteMode::DoNotOverwrite);unzipper.close();
  • Extracting all entries from the zip file to the desired destination:
Unzipperunzipper("zipfile.zip");unzipper.extractAll("/the/destination/path");// Fail if a file exists (DoNotOverwrite is implicit)unzipper.extractAll("/the/destination/path", Unzipper::OverwriteMode::Overwrite);// Replace existing filesunzipper.close();
  • Extracting all entries from the zip file using alternative names for existing files on disk:
std::map<std::string, std::string> alternativeNames = { {"Test1","alternative_name_test1"} };Unzipperunzipper("zipfile.zip");unzipper.extractAll(".", alternativeNames);unzipper.close();
  • Extracting a single entry from the zip file:
Unzipperunzipper("zipfile.zip");unzipper.extract("entry name");unzipper.close();

Returnstrue in case of success orfalse in case of failure.In case of failure, useunzipper.error(); to get thestd::error_code.

  • Extracting a single entry from the zip file to destination:
Unzipperunzipper("zipfile.zip");unzipper.extract("entry name","/the/destination/path");// Fail if a file exists (DoNotOverwrite is implicit)unzipper.extract("entry name","/the/destination/path", Unzipper::OverwriteMode::Overwrite);// Replace existing fileunzipper.close();

Returnstrue in case of success orfalse in case of failure.In case of failure, useunzipper.error(); to get thestd::error_code.

  • Extracting a single entry from the zip file to memory (stream):
std::stringstream output;Unzipperunzipper("zipfile.zip");unzipper.extract("entry name", output);unzipper.close();
  • Extracting a single entry from the zip file to memory (vector):
std::vector<unsignedchar> unzipped_entry;Unzipperunzipper("zipfile.zip");unzipper.extract("entry name", unzipped_entry);unzipper.close();

Returnstrue in case of success orfalse in case of failure.In case of failure, useunzipper.error(); to get thestd::error_code.

  • Extracting from a vector:
std::vector<unsignedchar> zip_vector;// Populated with Zipper zipper(zip_vect);Unzipperunzipper(zip_vector);unzipper.extract("Test1");
  • Zipper has security againstZip Slip vulnerability: if an entry has a path outside the extraction folder (like../foo.txt) itwill returnfalse even if the replace option is set.

Progress Callback

You can monitor the extraction progress by setting a callback function. The callback provides information about:

  • Current status (OK, KO, InProgress)
  • Current file being extracted
  • Number of bytes read
  • Total bytes to extract
  • Number of files extracted
  • Total number of files

Example:

Unzipperunzipper("zipfile.zip");// Set the progress callbackunzipper.setProgressCallback([](const Unzipper::Progress& progress) {switch (progress.status) {case Unzipper::Progress::Status::InProgress:            std::cout <<"Extracting:" << progress.current_file                      <<" (" << progress.files_extracted <<"/"                      << progress.total_files <<" files)"                      << (progress.bytes_read *100 / progress.total_bytes)                      <<"%" << std::endl;break;case Unzipper::Progress::Status::OK:            std::cout <<"Extraction completed successfully" << std::endl;break;case Unzipper::Progress::Status::KO:            std::cout <<"Extraction failed" << std::endl;break;    }});// Start extractionunzipper.extractAll();unzipper.close();

Same idea for Zipper.

FAQ

  • Q: I used a password when zipping with the Zipper lib, but now when I want to extract data with my operating system's zip tool, I get an error.

  • A: By default, Zipper uses the AES encryption algorithm, which is not the default encryption method for zip files. Your operating system's zip tools may not support AES. You can extract it using the 7-Zip tool:7za e your.zip. If you want to use the default zip encryption (at your own risk since the password can be cracked), you can remove the AES option in the following files:Make andexternal/compile-external-libs.sh and recompile the Zipper project.

  • Q: Why are there two classes: one for zipping and one for unzipping? Couldn't we have made a single class to handle both operations?

  • A: AFAIK this is a constraint imposed by the minizip API: No simultaneous operations are allowed - you cannot read from and write to a ZIP archive at the same time.

  • Q: How do I remove a file from the zip?

  • A: To modify an existing archive:

    • You generally need to create a temporary copy.
    • Copy the unchanged files.
    • Add the new files.
    • Replace the original.

About

[Lib][Version 3.0.1][Functional] C++14 wrapper around minizip compression library

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors3

  •  
  •  
  •  

[8]ページ先頭

©2009-2025 Movatter.jp