- Notifications
You must be signed in to change notification settings - Fork16
[Lib][Version 2.2.0][Functional] C++ wrapper around minizip compression library
License
Lecrapouille/zipper
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Zipper is a C++11 wrapper around minizip compression library. Its goal is to bring the power and simplicity of minizip to a more object-oriented/c++ user-friendly library. Note: We are currently using C++14 because the unit tests library needs it.
This project is the continuation of the originalproject. The original project was born out of the necessity of a compression library that would be reliable, simple, and flexible. By flexibility I mean supporting all kinds of inputs and outputs, but specifically being able to compress into memory instead of being restricted to file compression only, and using data from memory instead of just files as well.
This current fork repo has been made because the original project was no longer maintained by the original authors and I, Lecrapouille, have some issues due to missing administration rights (needed for CI, branch management ...).
- Create zip in memory.
- Allow files, vectors, and generic streams as input to zip.
- File mappings for replacing strategies (overwrite if exists or use alternative names from mapping).
- Password-protected zip (EAS).
- Multi-platform.
- Project compiled as static and dynamic libraries.
- Protection against theZip Slip attack.
- Non-regression tests.
- Zipper currently follows an outdated (and probably vulnerable) version of thehttps://github.com/Lecrapouille/minizip library:SHA1 0bb5afeb0d3f23149b086ccda7e4fee7d48f4fdf ofhttps://github.com/zlib-ng/minizip-ng which dated of 2017).
- While some fixes have been added this lib may be still vulnerable to ZipSlip attacks and mitigations should be put in place by Zipper's users.
To download the project and compile it:
git clone https://github.com/lecrapouille/zipper.git --recursivecd zipper# Optionally: make download-external-libsmake compile-external-libsmake -j`nproc --all`# make -j`sysctl -n hw.logicalcpu` for MacOS X
Notes:
- Git cloning needs the recursivity option to install the Makefile helper and third-part libs (
zlib
andminizip
). They are based on fixed SHA1. - Optionaly
make download-external-libs
will git clone HEADs of zlib and minizip to theexternal
folder. This replaces git submodules. make compile-external-libs
will compile zlib and minizip but not install them on your operating system. They arecompiled as static libraries and merged into this library.
Installing C++ header files and compiled libraries, type:
sudo make install
For Linux, 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
Optionally, you can compile demos present in the folder doc/demos:
make demos -j8
See their README files for their usage.
- In your project add the needed headers in your c++ files:
#include<Zipper/Unzipper.hpp>#include<Zipper/Zipper.hpp>
- To compile your project against Zipper use pkg-config:
g++ -W -Wall --std=c++11 main.cpp -o prog`pkg-config zipper --cflags --libs`
For Makefile:
- set
LDFLAGS
topkg-config zipper --libs
- set
CPPFLAGS
topkg-config zipper --cflags
- set
For CMake:
include(FindPkgConfig)find_package(zipper)
There are two classes availableZipper
andUnzipper
. They behave in the same manner regarding constructors and storage parameters.
#include<Zipper/Zipper.hpp>usingnamespacezipper;
- Constructor without password and replace
ziptest.zip
if already present. The new zip archive is dummy.
Zipperzipper("ziptest.zip", Zipper::openFlags::Overwrite);// Or simply: zipper("ziptest.zip");
- Constructor without password and preserve
ziptest.zip
if already present.
Zipperzipper("ziptest.zip", Zipper::openFlags::Append);
- Constructor with password (using AES algorithm) and replace
ziptest.zip
if already present. The new zip archive is dummy.
Zipperzipper("ziptest.zip","mypassword", Zipper::openFlags::Overwrite);// Or simply: zipper("ziptest.zip");
- Constructor with a password and preserve
ziptest.zip
if already present.
Zipperzipper("ziptest.zip","mypassword", Zipper::openFlags::Append);
- Note: all constructors will throw
std::run_time
exception in case of failure.
try{ Zipperzipper("ziptest.zip", ...); ...}catch (std::run_timeconst& e){ std::cerr << e.what() << std::endl;}
Do not forget to call explicitlyclose()
(called implicitly from its destructor) elsethe zip will not be well formed andUnzipper
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()
. The implicit optionZipper::openFlags::Append
is to preserve the zip content. To replace the zipfile is to useZipper::openFlags::Overwrite
.
Zipperzipper("ziptest.zip", ...);...zipper.close();...zipper.open();// equivalent to zipper.open(Zipper::openFlags::Append);// or zipper.open(Zipper::openFlags::Overwrite);...zipper.close();
In case of success theopen()
will returntrue
else will returnfalse
and theerror()
should be used for getting thestd::error_code.
if (!zipper.open()){ std::cerr << zipper.error().message() << std::endl;}
Theadd()
method allows appending files or folders. TheZipper::zipFlags::Better
is set implicitly. Other options are (in the last option in the arguments):
- Store only:
Zipper::zipFlags::Store
. - Compress faster, less compressed:
Zipper::zipFlags::Faster
. - Compress intermediate time/compression:
Zipper::zipFlags::Medium
. - Compress faster better:
Zipper::zipFlags::Better
.
In case of success theopen()
will returntrue
else will returnfalse
and theerror()
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("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 their
std::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();
- Zipper has security againstZip Slip vulnerability.
zipper.add(input1,"../Test1");
Will always returnfalse
becauseTest1
will be extracted outside the destination folder. This prevents malicious attack from replacing your password files:
zipper.add(malicious_passwd,"../../../../../../../../../../../../../../../etc/passwd");
Becase in Unix try to go outside the root folder/
will stay in the root folder. Example
cd /pwdcd ../../../../../../../../../../../../../../..pwd
- The Zipper lib force canonical paths in the archive. The following code works (will return
true
):
zipper.add(input1,"foo/../Test1");
becausefoo/../Test1
is replaced byTest1
(even if the folderfoo
is not present in thezip archive.
- 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();zipper::Unzipperunzipper(zip_in_memory);unzipper.extractEntry(...
Or:
#include<vector>std::vector<unsignedchar> zip_vect;std::ifstreaminput1("some file");Zipperzipper(zip_vect);// You can pass passwordzipper.add(input1,"Test1");zipper.close();
- Creating a zip in-memory stream with files:
std::stringstream ss;std::ifstreaminput1("some file");Zipperzipper(ss);// You can pass passwordzipper.add(input1,"Test1");zipper.close();zipper::Unzipperunzipper(ss);unzipper.extractEntry(...
#include<Zipper/Unzipper.hpp>usingnamespacezipper;
- Constructor without password and opening
ziptest.zip
(shall be already present).
Zipperunzipper("ziptest.zip");...zipper.close();
- Constructor with a password and opening
ziptest.zip
(shall be already present).
Zipperunzipper("ziptest.zip","mypassword");...zipper.close();
- Note: all constructors will throw
std::run_time
exception in case of failure.
try{ Zipperzipper("ziptest.zip", ...); ...}catch (std::run_timeconst& e){ std::cerr << e.what() << std::endl;}
Unzipperunzipper("zipfile.zip");std::vector<ZipEntry> entries = unzipper.entries();for (auto& it: unzipper.entries()){ std::cout << it.name <<":" << it.timestamp << std::endl;}unzipper.close();
Two methodsextract()
for the whole archive andextractEntry()
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 do not care about replacing existing files or folders:
Unzipperunzipper("zipfile.zip");unzipper.extractAll(true);unzipper.close();
- If you care about replacing existing files or folders. The method will fail (return
false
)if a file is replaced.
Unzipperunzipper("zipfile.zip");unzipper.extractAll();// equivalent to unzipper.extractAll(false);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 (false argument is implicit)unzipper.extractAll("/the/destination/path",true);// 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.extractEntry("entry name");unzipper.close();
Returntrue
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.extractEntry("entry name","/the/destination/path");// Fail if a file exists (false argument is implicit)unzipper.extractEntry("entry name","/the/destination/path",true);// Replace existing fileunzipper.close();
Returntrue
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:
std::vector<unsignedchar> unzipped_entry;Unzipperunzipper("zipfile.zip");unzipper.extractEntryToMemory("entry name", unzipped_entry);unzipper.close();
Returntrue
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_vect;// Populated with Zipper zipper(zip_vect);Unzipperunzipper(zip_vect);unzipper.extractEntry("Test1");
- Zipper has security againstZip Slip vulnerability: if an entry has a path outside the extraction folder (like
../foo.txt
) itwill returnsfalse
even if the replace option is set.
Basic unzipper standalone application given as demo indoc/demos.
Depends on:
- googletest framework
- lcov for code coverage:
Two ways of running them:
- From the root folder:
make tests -j`nproc --all`
- From the tests/ folder:
cd testsmake coverage -j`nproc --all`
A coverage report has created an opening. If you do not desire to generate the report.
cd testsmake -j`nproc --all`./build/Zipper-UnitTest
- Q: I used a password when zipping with the Zipper lib, but now when I want to extract data with my operating system zip tool, I got an error.A: By default, Zipper encrypts with the EAS algorithm which is not the default encryption algorithm for zip files. Your operating system zip tools seemnot to understand EAS. You can extract it with the 7-Zip tool:
7za e your.zip
. If you want the default zip encryption (at your own risk since thepassword can be cracked) you can remove EAS option in the following files:Make andexternal/compile-external-libs.shand recompile the Zipper project.
About
[Lib][Version 2.2.0][Functional] C++ wrapper around minizip compression library