Ring Daemon
Loading...
Searching...
No Matches
archiver.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2026 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "archiver.h"
19
20#include "fileutils.h"
21
22#include <opendht/crypto.h>
23#include <json/json.h>
24#include <zlib.h>
25
26#ifdef ENABLE_PLUGIN
27extern "C" {
28#if defined(__APPLE__)
29#include <minizip/mz.h>
30#include <minizip/mz_strm.h>
31#include <minizip/mz_strm_os.h>
32#include <minizip/mz_zip.h>
33#include <minizip/mz_zip_rw.h>
34#else
35#include <archive.h>
36#include <archive_entry.h>
37#endif
38}
39#endif
40
41#include <sys/stat.h>
42
43#include "string_utils.h" // to_string
44
45using namespace std::literals;
46
47namespace jami {
48namespace archiver {
49
50std::vector<uint8_t>
51compress(const std::string& str)
52{
53 auto destSize = compressBound(str.size());
54 std::vector<uint8_t> outbuffer(destSize);
55 int ret = ::compress(reinterpret_cast<Bytef*>(outbuffer.data()), &destSize, (Bytef*) str.data(), str.size());
56 outbuffer.resize(destSize);
57
58 if (ret != Z_OK) {
59 throw std::runtime_error(fmt::format("Exception during zlib compression: ({})", ret));
60 }
61
62 return outbuffer;
63}
64
65void
66compressGzip(const std::string& str, const std::string& path)
67{
68 auto* fi = openGzip(path, "wb");
69 gzwrite(fi, str.data(), str.size());
70 gzclose(fi);
71}
72
73void
74compressGzip(const std::vector<uint8_t>& dat, const std::string& path)
75{
76 auto* fi = openGzip(path, "wb");
77 gzwrite(fi, dat.data(), dat.size());
78 gzclose(fi);
79}
80
81std::vector<uint8_t>
82decompressGzip(const std::string& path)
83{
84 std::vector<uint8_t> out;
85 auto* fi = openGzip(path, "rb");
86 gzrewind(fi);
87 while (not gzeof(fi)) {
88 std::array<uint8_t, 32768> outbuffer;
89 int len = gzread(fi, outbuffer.data(), outbuffer.size());
90 if (len == -1) {
91 gzclose(fi);
92 throw std::runtime_error("Exception during gzip decompression");
93 }
94 out.insert(out.end(), outbuffer.begin(), outbuffer.begin() + len);
95 }
96 gzclose(fi);
97 return out;
98}
99
100std::vector<uint8_t>
101decompress(const std::vector<uint8_t>& str)
102{
103 z_stream zs; // z_stream is zlib's control structure
104 memset(&zs, 0, sizeof(zs));
105
106 if (inflateInit2(&zs, 32 + MAX_WBITS) != Z_OK)
107 throw std::runtime_error("inflateInit failed while decompressing.");
108
109 zs.next_in = (Bytef*) str.data();
110 zs.avail_in = str.size();
111
112 int ret;
113 std::vector<uint8_t> out;
114
115 // get the decompressed bytes blockwise using repeated calls to inflate
116 do {
117 std::array<uint8_t, 32768> outbuffer;
118 zs.next_out = reinterpret_cast<Bytef*>(outbuffer.data());
119 zs.avail_out = outbuffer.size();
120
121 ret = inflate(&zs, 0);
122 if (ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
123 break;
124
125 if (out.size() < zs.total_out) {
126 // append the block to the output string
127 out.insert(out.end(), outbuffer.begin(), outbuffer.begin() + zs.total_out - out.size());
128 }
129 } while (ret == Z_OK);
130
131 inflateEnd(&zs);
132
133 // an error occurred that was not EOF
134 if (ret != Z_STREAM_END) {
135 throw(std::runtime_error(fmt::format("Exception during zlib decompression: ({})", ret)));
136 }
137
138 return out;
139}
140
141gzFile
142openGzip(const std::string& path, const char* mode)
143{
144#ifdef _WIN32
145 return gzopen_w(jami::to_wstring(path).c_str(), mode);
146#else
147 return gzopen(path.c_str(), mode);
148#endif
149}
150
151#ifdef ENABLE_PLUGIN
152#if !defined(__APPLE__)
153// LIBARCHIVE DEFINITIONS
154//==========================
155using ArchivePtr = std::unique_ptr<archive, void (*)(archive*)>;
156using ArchiveEntryPtr = std::unique_ptr<archive_entry, void (*)(archive_entry*)>;
157
158struct DataBlock
159{
160 const void* buff;
161 size_t size;
162 int64_t offset;
163};
164
165static long
167{
168 return archive_read_data_block(a.get(), &b.buff, &b.size, &b.offset);
169}
170
171static long
173{
174 return archive_write_data_block(a.get(), b.buff, b.size, b.offset);
175}
176
177static ArchivePtr
179{
183 }};
184 return archivePtr;
185}
186
187static ArchivePtr
189{
190 return {archive_write_disk_new(), [](archive* a) {
193 }};
194}
195//==========================
196#endif
197#endif
198
199void
200uncompressArchive(const std::string& archivePath, const std::string& dir, const FileMatchPair& f)
201{
202#ifdef ENABLE_PLUGIN
203#if defined(__APPLE__)
204 mz_zip_file* info = NULL;
205
206 dhtnet::fileutils::check_dir(dir.c_str());
207
208 void* zip_handle = mz_zip_create();
209 auto status = mz_zip_reader_open_file(zip_handle, archivePath.c_str());
211
212 while (status == MZ_OK) {
214 if (status != MZ_OK) {
215 dhtnet::fileutils::removeAll(dir, true);
216 break;
217 }
218 std::string_view filename(info->filename, (size_t) info->filename_size);
219 const auto& fileMatchPair = f(filename);
220 if (fileMatchPair.first) {
222 std::string directory = filePath.substr(0, filePath.find_last_of(DIR_SEPARATOR_CH));
223 dhtnet::fileutils::check_dir(directory.c_str());
227 int chunkSize = 8192;
228 std::vector<uint8_t> fileContent;
229 fileContent.resize(chunkSize);
230 while (auto ret = mz_zip_reader_entry_read(zip_handle, (void*) fileContent.data(), chunkSize)) {
231 ret = mz_stream_os_write(buffStream, (void*) fileContent.data(), ret);
232 if (ret < 0) {
233 dhtnet::fileutils::removeAll(dir, true);
234 status = 1;
235 }
236 }
239 } else {
240 dhtnet::fileutils::removeAll(dir, true);
241 status = 1;
242 }
244 }
246 }
247
250
251#else
252 long r;
253
256 struct archive_entry* entry;
257
259
260 // Set reader formats(archive) and filters(compression)
263
264 // Set written files flags and standard lookup(uid/gid)
267
268 // Try to read the archive
269 if ((r = archive_read_open_filename(archiveReader.get(), archivePath.c_str(), 10240))) {
270 throw std::runtime_error("Open Archive: " + archivePath + "\t" + archive_error_string(archiveReader.get()));
271 }
272
273 while (true) {
274 // Read headers until End of File
276 if (r == ARCHIVE_EOF) {
277 break;
278 }
279 if (r != ARCHIVE_OK && r != ARCHIVE_WARN) {
280 throw std::runtime_error("Error reading archive: "s + archive_error_string(archiveReader.get()));
281 }
282
283 std::string_view fileEntry(archive_entry_pathname(entry));
284
285 // File is ok, copy its header to the ext writer
286 const auto& fileMatchPair = f(fileEntry);
287 if (fileMatchPair.first) {
291 if (r != ARCHIVE_OK) {
292 // Rollback if failed at a write operation
293 dhtnet::fileutils::removeAll(dir);
294 throw std::runtime_error("Write file header: " + fileEntry + "\t"
296 } else {
297 // Here both the reader and the writer have moved past the headers
298 // Copying the data content
300
301 while (true) {
303 if (r == ARCHIVE_EOF) {
304 break;
305 }
306
307 if (r != ARCHIVE_OK) {
308 throw std::runtime_error("Read file data: " + fileEntry + "\t"
310 }
311
313
314 if (r != ARCHIVE_OK) {
315 // Rollback if failed at a write operation
316 dhtnet::fileutils::removeAll(dir);
317 throw std::runtime_error("Write file data: " + fileEntry + "\t"
319 }
320 }
321 }
322 }
323 }
324#endif
325#endif
326}
327
328std::vector<uint8_t>
329readFileFromArchive(const std::string& archivePath, const std::string& fileRelativePathName)
330{
331 std::vector<uint8_t> fileContent;
332#ifdef ENABLE_PLUGIN
333#if defined(__APPLE__)
334 mz_zip_file* info;
335
336 void* zip_handle = mz_zip_create();
337 auto status = mz_zip_reader_open_file(zip_handle, archivePath.c_str());
339
340 while (status == MZ_OK) {
342 if (status != MZ_OK)
343 break;
344 std::string_view filename(info->filename, (size_t) info->filename_size);
347 fileContent.resize(info->uncompressed_size);
348 mz_zip_reader_entry_read(zip_handle, (void*) fileContent.data(), info->uncompressed_size);
350 status = -1;
351 } else {
353 }
354 }
355
358#else
359 long r;
361 struct archive_entry* entry;
362
363 // Set reader formats(archive) and filters(compression)
366
367 // Try to read the archive
368 if ((r = archive_read_open_filename(archiveReader.get(), archivePath.c_str(), 10240))) {
369 throw std::runtime_error("Open Archive: " + archivePath + "\t" + archive_error_string(archiveReader.get()));
370 }
371
372 while (true) {
373 // Read headers until End of File
375 if (r == ARCHIVE_EOF) {
376 break;
377 }
378
380 if (r != ARCHIVE_OK) {
381 throw std::runtime_error(
382 fmt::format("Read file pathname: {}: {}", fileEntry, archive_error_string(archiveReader.get())));
383 }
384
385 // File is ok and the reader has moved past the header
387 // Copying the data content
389
390 while (true) {
392 if (r == ARCHIVE_EOF) {
393 return fileContent;
394 }
395
396 if (r != ARCHIVE_OK) {
397 throw std::runtime_error("Read file data: " + fileEntry + "\t"
399 }
400
401 if (fileContent.size() < static_cast<size_t>(db.offset)) {
402 fileContent.resize(db.offset);
403 }
404
405 const auto* dat = static_cast<const uint8_t*>(db.buff);
406 // push the buffer data in the string stream
407 fileContent.insert(fileContent.end(), dat, dat + db.size);
408 }
409 }
410 }
411 throw std::runtime_error("File " + fileRelativePathName + " not found in the archive");
412#endif
413#endif
414 return fileContent;
415}
416std::vector<std::string>
417listFilesFromArchive(const std::string& path)
418{
419 std::vector<std::string> filePaths;
420#ifdef ENABLE_PLUGIN
421#if defined(__APPLE__)
422 mz_zip_file* info = NULL;
423
424 void* zip_handle = mz_zip_create();
425 auto status = mz_zip_reader_open_file(zip_handle, path.c_str());
427
428 // read all the file path of the archive
429 while (status == MZ_OK) {
431 if (status != MZ_OK)
432 break;
433 std::string filename(info->filename, (size_t) info->filename_size);
434 filePaths.push_back(filename);
436 }
439#else
441 struct archive_entry* entry;
442
444 if (archive_read_open_filename(archiveReader.get(), path.c_str(), 10240)) {
445 throw std::runtime_error("Open Archive: " + path + "\t" + archive_error_string(archiveReader.get()));
446 }
447
449 const char* name = archive_entry_pathname(entry);
450
451 filePaths.push_back(name);
452 }
453#endif
454#endif
455 return filePaths;
456}
457} // namespace archiver
458} // namespace jami
struct gzFile_s * gzFile
Definition archiver.h:28
#define DIR_SEPARATOR_STR
Definition fileutils.h:33
#define DIR_SEPARATOR_CH
Definition fileutils.h:34
void compressGzip(const std::string &str, const std::string &path)
Compress string to a Gzip file.
Definition archiver.cpp:66
std::vector< uint8_t > decompress(const std::vector< uint8_t > &str)
Decompress an STL string using zlib and return the original data.
Definition archiver.cpp:101
std::vector< std::string > listFilesFromArchive(const std::string &path)
listFilesFromArchive list all files from an archive
Definition archiver.cpp:417
void uncompressArchive(const std::string &archivePath, const std::string &dir, const FileMatchPair &f)
uncompressArchive Uncompresses an archive and puts the different files in dir folder according to a F...
Definition archiver.cpp:200
std::function< std::pair< bool, std::string_view >(std::string_view)> FileMatchPair
Definition archiver.h:37
gzFile openGzip(const std::string &path, const char *mode)
Open Gzip file (uses wide string version of gzopen on windows)
Definition archiver.cpp:142
std::vector< uint8_t > readFileFromArchive(const std::string &archivePath, const std::string &fileRelativePathName)
readFileFromArchive read a file from an archive without uncompressing the whole archive
Definition archiver.cpp:329
std::vector< uint8_t > decompressGzip(const std::string &path)
Decompress Gzip file to bytes.
Definition archiver.cpp:82
std::vector< uint8_t > compress(const std::string &str)
Compress a STL string using zlib with given compression level and return the binary data.
Definition archiver.cpp:51
void emitSignal(Args... args)
Definition jami_signal.h:64