Ring Daemon 16.0.0
Loading...
Searching...
No Matches
archiver.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 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 "client/ring_signal.h"
21#include "account_const.h"
23
24#include "manager.h"
25#include "fileutils.h"
26#include "logger.h"
27
28#include <opendht/crypto.h>
29#include <json/json.h>
30#include <zlib.h>
31
32#ifdef ENABLE_PLUGIN
33extern "C" {
34#if defined(__APPLE__)
35#include <minizip/mz.h>
36#include <minizip/mz_strm.h>
37#include <minizip/mz_strm_os.h>
38#include <minizip/mz_zip.h>
39#include <minizip/mz_zip_rw.h>
40#else
41#include <archive.h>
42#include <archive_entry.h>
43#endif
44}
45#endif
46
47#include <sys/stat.h>
48#include <fstream>
49
50using namespace std::literals;
51
52namespace jami {
53namespace archiver {
54
55std::vector<uint8_t>
56compress(const std::string& str)
57{
58 auto destSize = compressBound(str.size());
59 std::vector<uint8_t> outbuffer(destSize);
60 int ret = ::compress(reinterpret_cast<Bytef*>(outbuffer.data()),
61 &destSize,
62 (Bytef*) str.data(),
63 str.size());
64 outbuffer.resize(destSize);
65
66 if (ret != Z_OK) {
67 throw std::runtime_error(fmt::format("Exception during zlib compression: ({})", ret));
68 }
69
70 return outbuffer;
71}
72
73void
74compressGzip(const std::string& str, const std::string& path)
75{
76 auto fi = openGzip(path, "wb");
77 gzwrite(fi, str.data(), str.size());
78 gzclose(fi);
79}
80
81void
82compressGzip(const std::vector<uint8_t>& dat, const std::string& path)
83{
84 auto fi = openGzip(path, "wb");
85 gzwrite(fi, dat.data(), dat.size());
86 gzclose(fi);
87}
88
89std::vector<uint8_t>
90decompressGzip(const std::string& path)
91{
92 std::vector<uint8_t> out;
93 auto fi = openGzip(path, "rb");
94 gzrewind(fi);
95 while (not gzeof(fi)) {
96 std::array<uint8_t, 32768> outbuffer;
97 int len = gzread(fi, outbuffer.data(), outbuffer.size());
98 if (len == -1) {
99 gzclose(fi);
100 throw std::runtime_error("Exception during gzip decompression");
101 }
102 out.insert(out.end(), outbuffer.begin(), outbuffer.begin() + len);
103 }
104 gzclose(fi);
105 return out;
106}
107
108std::vector<uint8_t>
109decompress(const std::vector<uint8_t>& str)
110{
111 z_stream zs; // z_stream is zlib's control structure
112 memset(&zs, 0, sizeof(zs));
113
114 if (inflateInit2(&zs, 32+MAX_WBITS) != Z_OK)
115 throw std::runtime_error("inflateInit failed while decompressing.");
116
117 zs.next_in = (Bytef*) str.data();
118 zs.avail_in = str.size();
119
120 int ret;
121 std::vector<uint8_t> out;
122
123 // get the decompressed bytes blockwise using repeated calls to inflate
124 do {
125 std::array<uint8_t, 32768> outbuffer;
126 zs.next_out = reinterpret_cast<Bytef*>(outbuffer.data());
127 zs.avail_out = outbuffer.size();
128
129 ret = inflate(&zs, 0);
130 if (ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
131 break;
132
133 if (out.size() < zs.total_out) {
134 // append the block to the output string
135 out.insert(out.end(), outbuffer.begin(), outbuffer.begin() + zs.total_out - out.size());
136 }
137 } while (ret == Z_OK);
138
139 inflateEnd(&zs);
140
141 // an error occurred that was not EOF
142 if (ret != Z_STREAM_END) {
143 throw(std::runtime_error(fmt::format("Exception during zlib decompression: ({})", ret)));
144 }
145
146 return out;
147}
148
149gzFile
150openGzip(const std::string& path, const char* mode)
151{
152#ifdef _WIN32
153 return gzopen_w(jami::to_wstring(path).c_str(), mode);
154#else
155 return gzopen(path.c_str(), mode);
156#endif
157}
158
159#ifdef ENABLE_PLUGIN
160#if !defined(__APPLE__)
161// LIBARCHIVE DEFINITIONS
162//==========================
163using ArchivePtr = std::unique_ptr<archive, void (*)(archive*)>;
164using ArchiveEntryPtr = std::unique_ptr<archive_entry, void (*)(archive_entry*)>;
165
166struct DataBlock
167{
168 const void* buff;
169 size_t size;
170 int64_t offset;
171};
172
173long
175{
176 return archive_read_data_block(a.get(), &b.buff, &b.size, &b.offset);
177}
178
179long
181{
182 return archive_write_data_block(a.get(), b.buff, b.size, b.offset);
183}
184
187{
191 }};
192 return archivePtr;
193}
194
195static ArchivePtr
197{
198 return {archive_write_disk_new(), [](archive* a) {
201 }};
202}
203//==========================
204#endif
205#endif
206
207void
208uncompressArchive(const std::string& archivePath, const std::string& dir, const FileMatchPair& f)
209{
210#ifdef ENABLE_PLUGIN
211#if defined(__APPLE__)
212 mz_zip_file* info = NULL;
213
214 dhtnet::fileutils::check_dir(dir.c_str());
215
216 void* zip_handle = mz_zip_create();
217 auto status = mz_zip_reader_open_file(zip_handle, archivePath.c_str());
219
220 while (status == MZ_OK) {
222 if (status != MZ_OK) {
223 dhtnet::fileutils::removeAll(dir, true);
224 break;
225 }
226 std::string_view filename(info->filename, (size_t) info->filename_size);
227 const auto& fileMatchPair = f(filename);
228 if (fileMatchPair.first) {
230 std::string directory = filePath.substr(0, filePath.find_last_of(DIR_SEPARATOR_CH));
231 dhtnet::fileutils::check_dir(directory.c_str());
235 filePath.c_str(),
237 == MZ_OK) {
238 int chunkSize = 8192;
239 std::vector<uint8_t> fileContent;
240 fileContent.resize(chunkSize);
242 (void*) fileContent.data(),
243 chunkSize)) {
244 ret = mz_stream_os_write(buffStream, (void*) fileContent.data(), ret);
245 if (ret < 0) {
246 dhtnet::fileutils::removeAll(dir, true);
247 status = 1;
248 }
249 }
252 } else {
253 dhtnet::fileutils::removeAll(dir, true);
254 status = 1;
255 }
257 }
259 }
260
263
264#else
265 int r;
266
269 struct archive_entry* entry;
270
272
273 // Set reader formats(archive) and filters(compression)
276
277 // Set written files flags and standard lookup(uid/gid)
280
281 // Try to read the archive
282 if ((r = archive_read_open_filename(archiveReader.get(), archivePath.c_str(), 10240))) {
283 throw std::runtime_error("Open Archive: " + archivePath + "\t"
285 }
286
287 while (true) {
288 // Read headers until End of File
290 if (r == ARCHIVE_EOF) {
291 break;
292 }
293 if (r != ARCHIVE_OK && r != ARCHIVE_WARN) {
294 throw std::runtime_error("Error reading archive: "s
296 }
297
298 std::string_view fileEntry(archive_entry_pathname(entry));
299
300 // File is ok, copy its header to the ext writer
301 const auto& fileMatchPair = f(fileEntry);
302 if (fileMatchPair.first) {
306 if (r != ARCHIVE_OK) {
307 // Rollback if failed at a write operation
308 dhtnet::fileutils::removeAll(dir);
309 throw std::runtime_error("Write file header: " + fileEntry + "\t"
311 } else {
312 // Here both the reader and the writer have moved past the headers
313 // Copying the data content
315
316 while (true) {
318 if (r == ARCHIVE_EOF) {
319 break;
320 }
321
322 if (r != ARCHIVE_OK) {
323 throw std::runtime_error("Read file data: " + fileEntry + "\t"
325 }
326
328
329 if (r != ARCHIVE_OK) {
330 // Rollback if failed at a write operation
331 dhtnet::fileutils::removeAll(dir);
332 throw std::runtime_error("Write file data: " + fileEntry + "\t"
334 }
335 }
336 }
337 }
338 }
339#endif
340#endif
341}
342
343std::vector<uint8_t>
344readFileFromArchive(const std::string& archivePath, const std::string& fileRelativePathName)
345{
346 std::vector<uint8_t> fileContent;
347#ifdef ENABLE_PLUGIN
348#if defined(__APPLE__)
349 mz_zip_file* info;
350
351 void* zip_handle = mz_zip_create();
352 auto status = mz_zip_reader_open_file(zip_handle, archivePath.c_str());
354
355 while (status == MZ_OK) {
357 if (status != MZ_OK)
358 break;
359 std::string_view filename(info->filename, (size_t) info->filename_size);
360 if (filename == fileRelativePathName) {
362 fileContent.resize(info->uncompressed_size);
364 (void*) fileContent.data(),
365 info->uncompressed_size);
367 status = -1;
368 } else {
370 }
371 }
372
375#else
376 long r;
378 struct archive_entry* entry;
379
380 // Set reader formats(archive) and filters(compression)
383
384 // Try to read the archive
385 if ((r = archive_read_open_filename(archiveReader.get(), archivePath.c_str(), 10240))) {
386 throw std::runtime_error("Open Archive: " + archivePath + "\t"
388 }
389
390 while (true) {
391 // Read headers until End of File
393 if (r == ARCHIVE_EOF) {
394 break;
395 }
396
398 if (r != ARCHIVE_OK) {
399 throw std::runtime_error(fmt::format("Read file pathname: {}: {}", fileEntry, archive_error_string(archiveReader.get())));
400 }
401
402 // File is ok and the reader has moved past the header
404 // Copying the data content
406
407 while (true) {
409 if (r == ARCHIVE_EOF) {
410 return fileContent;
411 }
412
413 if (r != ARCHIVE_OK) {
414 throw std::runtime_error("Read file data: " + fileEntry + "\t"
416 }
417
418 if (fileContent.size() < static_cast<size_t>(db.offset)) {
419 fileContent.resize(db.offset);
420 }
421
422 auto dat = static_cast<const uint8_t*>(db.buff);
423 // push the buffer data in the string stream
424 fileContent.insert(fileContent.end(), dat, dat + db.size);
425 }
426 }
427 }
428 throw std::runtime_error("File " + fileRelativePathName + " not found in the archive");
429#endif
430#endif
431 return fileContent;
432}
433std::vector<std::string>
434listFilesFromArchive(const std::string& path)
435{
436 std::vector<std::string> filePaths;
437#ifdef ENABLE_PLUGIN
438#if defined(__APPLE__)
439 mz_zip_file* info = NULL;
440
441 void* zip_handle = mz_zip_create();
442 auto status = mz_zip_reader_open_file(zip_handle, path.c_str());
444
445 // read all the file path of the archive
446 while (status == MZ_OK) {
448 if (status != MZ_OK)
449 break;
450 std::string filename(info->filename, (size_t) info->filename_size);
451 filePaths.push_back(filename);
453 }
456#else
458 struct archive_entry* entry;
459
461 if(archive_read_open_filename(archiveReader.get(), path.c_str(), 10240)) {
462 throw std::runtime_error("Open Archive: " + path + "\t"
464 }
465
467 const char* name = archive_entry_pathname(entry);
468
469 filePaths.push_back(name);
470 }
471#endif
472#endif
473 return filePaths;
474}
475} // namespace archiver
476} // namespace jami
struct gzFile_s * gzFile
Definition archiver.h:31
#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:74
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:109
std::vector< std::string > listFilesFromArchive(const std::string &path)
listFilesFromArchive list all files from an archive
Definition archiver.cpp:434
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:208
std::function< std::pair< bool, std::string_view >(std::string_view)> FileMatchPair
Definition archiver.h:40
gzFile openGzip(const std::string &path, const char *mode)
Open Gzip file (uses wide string version of gzopen on windows)
Definition archiver.cpp:150
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:344
std::vector< uint8_t > decompressGzip(const std::string &path)
Decompress Gzip file to bytes.
Definition archiver.cpp:90
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:56
void emitSignal(Args... args)
Definition ring_signal.h:64