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 original project. 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 ...).
:warning: Security Notice
To download the project and compile it:
git clone https://github.com/lecrapouille/zipper.git --recursive
cd zipper
# Optionally: make download-external-libs
make compile-external-libs
make -j`nproc --all`
# make -j`sysctl -n hw.logicalcpu` for MacOS X
Notes:
zlib
and minizip
). They are based on fixed SHA1.make download-external-libs
will git clone HEADs of zlib and minizip to the external
folder. This replaces git submodules.make compile-external-libs
will compile zlib and minizip but not install them on your operating system. They are
compiled 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.
#include <Zipper/Unzipper.hpp>
#include <Zipper/Zipper.hpp>
g++ -W -Wall --std=c++11 main.cpp -o prog `pkg-config zipper --cflags --libs`
For Makefile:
LDFLAGS
to pkg-config zipper --libs
CPPFLAGS
to pkg-config zipper --cflags
For CMake:
include(FindPkgConfig)
find_package(zipper)
There are two classes available Zipper
and Unzipper
. They behave in the same manner regarding constructors and storage parameters.
#include <Zipper/Zipper.hpp>
using namespace zipper;
ziptest.zip
if already present. The new zip archive is dummy.Zipper zipper("ziptest.zip", Zipper::openFlags::Overwrite);
// Or simply: zipper("ziptest.zip");
ziptest.zip
if already present.Zipper zipper("ziptest.zip", Zipper::openFlags::Append);
ziptest.zip
if already present. The new zip archive is dummy.Zipper zipper("ziptest.zip", "mypassword", Zipper::openFlags::Overwrite);
// Or simply: zipper("ziptest.zip");
ziptest.zip
if already present.Zipper zipper("ziptest.zip", "mypassword", Zipper::openFlags::Append);
std::run_time
exception in case of failure.try
{
Zipper zipper("ziptest.zip", ...);
...
}
catch (std::run_time const& e)
{
std::cerr << e.what() << std::endl;
}
Do not forget to call explicitly close()
(called implicitly from its destructor) else
the zip will not be well formed and Unzipper
will fail to open it for example.
Zipper zipper("ziptest.zip", ...);
...
zipper.close(); // Now Unzipper unzipper("ziptest.zip") can work
After close()
you can reopen the zip archive with open()
. The implicit option
Zipper::openFlags::Append
is to preserve the zip content. To replace the zip
file is to use Zipper::openFlags::Overwrite
.
Zipper zipper("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 the open()
will return true
else will return false
and the
error()
should be used for getting the std::error_code.
if (!zipper.open())
{
std::cerr << zipper.error().message() << std::endl;
}
The add()
method allows appending files or folders. The Zipper::zipFlags::Better
is set implicitly. Other options are (in the last option in the arguments):
Zipper::zipFlags::Store
.Zipper::zipFlags::Faster
.Zipper::zipFlags::Medium
.Zipper::zipFlags::Better
.In case of success the open()
will return true
else will return false
and the error()
should be used for getting the std::error_code.
Zipper zipper("ziptest.zip");
zipper.add("myFolder/");
zipper.close();
Zipper zipper("ziptest.zip");
zipper.add("somefile.txt");
zipper.close();
Zipper zipper("ziptest.zip");
zipper.add("somefile.txt", "new_name_in_archive");
zipper.close();
std::ifstream
and change their name in the archive:std::ifstream input1("first_file");
std::ifstream input2("second_file");
Zipper zipper("ziptest.zip");
zipper.add(input1, "Test1");
zipper.add(input2, "Test2");
zipper.close();
zipper.add(input1, "../Test1");
Will always return false
because Test1
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 /
pwd
cd ../../../../../../../../../../../../../../..
pwd
true
):
zipper.add(input1, "foo/../Test1");
because foo/../Test1
is replaced by Test1
(even if the folder foo
is not present in the
zip archive.
#include <boost/interprocess/streams/vectorstream.hpp>
...
boost::interprocess::basic_vectorstream<std::vector<char>> input_data(some_vector);
Zipper zipper("ziptest.zip");
zipper.add(input_data, "Test1");
zipper.close();
#include <boost/interprocess/streams/vectorstream.hpp>
...
boost::interprocess::basic_vectorstream<std::vector<char>> zip_in_memory;
std::ifstream input1("some file");
Zipper zipper(zip_in_memory); // You can pass password
zipper.add(input1, "Test1");
zipper.close();
zipper::Unzipper unzipper(zip_in_memory);
unzipper.extractEntry(...
Or:
#include <vector>
std::vector<unsigned char> zip_vect;
std::ifstream input1("some file");
Zipper zipper(zip_vect); // You can pass password
zipper.add(input1, "Test1");
zipper.close();
std::stringstream ss;
std::ifstream input1("some file");
Zipper zipper(ss); // You can pass password
zipper.add(input1, "Test1");
zipper.close();
zipper::Unzipper unzipper(ss);
unzipper.extractEntry(...
#include <Zipper/Unzipper.hpp>
using namespace zipper;
ziptest.zip
(shall be already present).Zipper unzipper("ziptest.zip");
...
zipper.close();
ziptest.zip
(shall be already present).Zipper unzipper("ziptest.zip", "mypassword");
...
zipper.close();
std::run_time
exception in case of failure.try
{
Zipper zipper("ziptest.zip", ...);
...
}
catch (std::run_time const& e)
{
std::cerr << e.what() << std::endl;
}
Unzipper unzipper("zipfile.zip");
std::vector<ZipEntry> entries = unzipper.entries();
for (auto& it: unzipper.entries())
{
std::cout << it.name << ": "
<< it.timestamp
<< std::endl;
}
unzipper.close();
Two methods extract()
for the whole archive and extractEntry()
for
a single element in the archive. They return true
in case of success
or false
in case of failure. In case of failure, use unzipper.error();
to get the std::error_code
.
Unzipper unzipper("zipfile.zip");
unzipper.extractAll(true);
unzipper.close();
false
)
if a file is replaced.Unzipper unzipper("zipfile.zip");
unzipper.extractAll(); // equivalent to unzipper.extractAll(false);
unzipper.close();
Unzipper unzipper("zipfile.zip");
unzipper.extractAll("/the/destination/path"); // Fail if a file exists (false argument is implicit)
unzipper.extractAll("/the/destination/path", true); // Replace existing files
unzipper.close();
std::map<std::string, std::string> alternativeNames = { {"Test1", "alternative_name_test1"} };
Unzipper unzipper("zipfile.zip");
unzipper.extractAll(".", alternativeNames);
unzipper.close();
Unzipper unzipper("zipfile.zip");
unzipper.extractEntry("entry name");
unzipper.close();
Return true
in case of success or false
in case of failure.
In case of failure, use unzipper.error();
to get the std::error_code
.
Unzipper unzipper("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 file
unzipper.close();
Return true
in case of success or false
in case of failure.
In case of failure, use unzipper.error();
to get the std::error_code
.
std::vector<unsigned char> unzipped_entry;
Unzipper unzipper("zipfile.zip");
unzipper.extractEntryToMemory("entry name", unzipped_entry);
unzipper.close();
Return true
in case of success or false
in case of failure.
In case of failure, use unzipper.error();
to get the std::error_code
.
std::vector<unsigned char> zip_vect; // Populated with Zipper zipper(zip_vect);
Unzipper unzipper(zip_vect);
unzipper.extractEntry("Test1");
../foo.txt
) it
will returns false
even if the replace option is set.Basic unzipper standalone application given as demo in doc/demos.
Depends on:
Two ways of running them:
From the root folder:
make check -j`nproc --all`
From the tests/ folder:
cd tests
make coverage -j`nproc --all`
A coverage report has created an opening. If you do not desire to generate the report.
cd tests
make -j`nproc --all`
./build/Zipper-UnitTest
7za e your.zip
. If you want the default zip encryption (at your own risk since the
password can be cracked) you can remove EAS option in the following files: Make and external/compile-external-libs.sh
and recompile the Zipper project.