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 std::ostringstream oss;
68 oss << "Exception during zlib compression: (" << ret << ") ";
69 throw std::runtime_error(oss.str());
70 }
71
72 return outbuffer;
73}
74
75void
76compressGzip(const std::string& str, const std::string& path)
77{
78 auto fi = openGzip(path, "wb");
79 gzwrite(fi, str.data(), str.size());
80 gzclose(fi);
81}
82
83void
84compressGzip(const std::vector<uint8_t>& dat, const std::string& path)
85{
86 auto fi = openGzip(path, "wb");
87 gzwrite(fi, dat.data(), dat.size());
88 gzclose(fi);
89}
90
91std::vector<uint8_t>
92decompressGzip(const std::string& path)
93{
94 std::vector<uint8_t> out;
95 auto fi = openGzip(path, "rb");
96 gzrewind(fi);
97 while (not gzeof(fi)) {
98 std::array<uint8_t, 32768> outbuffer;
99 int len = gzread(fi, outbuffer.data(), outbuffer.size());
100 if (len == -1) {
101 gzclose(fi);
102 throw std::runtime_error("Exception during gzip decompression");
103 }
104 out.insert(out.end(), outbuffer.begin(), outbuffer.begin() + len);
105 }
106 gzclose(fi);
107 return out;
108}
109
110std::vector<uint8_t>
111decompress(const std::vector<uint8_t>& str)
112{
113 z_stream zs; // z_stream is zlib's control structure
114 memset(&zs, 0, sizeof(zs));
115
116 if (inflateInit2(&zs, 32+MAX_WBITS) != Z_OK)
117 throw std::runtime_error("inflateInit failed while decompressing.");
118
119 zs.next_in = (Bytef*) str.data();
120 zs.avail_in = str.size();
121
122 int ret;
123 std::vector<uint8_t> out;
124
125 // get the decompressed bytes blockwise using repeated calls to inflate
126 do {
127 std::array<uint8_t, 32768> outbuffer;
128 zs.next_out = reinterpret_cast<Bytef*>(outbuffer.data());
129 zs.avail_out = outbuffer.size();
130
131 ret = inflate(&zs, 0);
132 if (ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
133 break;
134
135 if (out.size() < zs.total_out) {
136 // append the block to the output string
137 out.insert(out.end(), outbuffer.begin(), outbuffer.begin() + zs.total_out - out.size());
138 }
139 } while (ret == Z_OK);
140
141 inflateEnd(&zs);
142
143 // an error occurred that was not EOF
144 if (ret != Z_STREAM_END) {
145 std::ostringstream oss;
146 oss << "Exception during zlib decompression: (" << ret << ") " << zs.msg;
147 throw(std::runtime_error(oss.str()));
148 }
149
150 return out;
151}
152
153gzFile
154openGzip(const std::string& path, const char* mode)
155{
156#ifdef _WIN32
157 return gzopen_w(jami::to_wstring(path).c_str(), mode);
158#else
159 return gzopen(path.c_str(), mode);
160#endif
161}
162
163#ifdef ENABLE_PLUGIN
164#if !defined(__APPLE__)
165// LIBARCHIVE DEFINITIONS
166//==========================
167using ArchivePtr = std::unique_ptr<archive, void (*)(archive*)>;
168using ArchiveEntryPtr = std::unique_ptr<archive_entry, void (*)(archive_entry*)>;
169
170struct DataBlock
171{
172 const void* buff;
173 size_t size;
174 int64_t offset;
175};
176
177long
179{
180 return archive_read_data_block(a.get(), &b.buff, &b.size, &b.offset);
181}
182
183long
185{
186 return archive_write_data_block(a.get(), b.buff, b.size, b.offset);
187}
188
191{
195 }};
196 return archivePtr;
197}
198
199static ArchivePtr
201{
202 return {archive_write_disk_new(), [](archive* a) {
205 }};
206}
207//==========================
208#endif
209#endif
210
211void
212uncompressArchive(const std::string& archivePath, const std::string& dir, const FileMatchPair& f)
213{
214#ifdef ENABLE_PLUGIN
215#if defined(__APPLE__)
216 mz_zip_file* info = NULL;
217
218 dhtnet::fileutils::check_dir(dir.c_str());
219
220 void* zip_handle = mz_zip_create();
221 auto status = mz_zip_reader_open_file(zip_handle, archivePath.c_str());
223
224 while (status == MZ_OK) {
226 if (status != MZ_OK) {
227 dhtnet::fileutils::removeAll(dir, true);
228 break;
229 }
230 std::string_view filename(info->filename, (size_t) info->filename_size);
231 const auto& fileMatchPair = f(filename);
232 if (fileMatchPair.first) {
234 std::string directory = filePath.substr(0, filePath.find_last_of(DIR_SEPARATOR_CH));
235 dhtnet::fileutils::check_dir(directory.c_str());
239 filePath.c_str(),
241 == MZ_OK) {
242 int chunkSize = 8192;
243 std::vector<uint8_t> fileContent;
244 fileContent.resize(chunkSize);
246 (void*) fileContent.data(),
247 chunkSize)) {
248 ret = mz_stream_os_write(buffStream, (void*) fileContent.data(), ret);
249 if (ret < 0) {
250 dhtnet::fileutils::removeAll(dir, true);
251 status = 1;
252 }
253 }
256 } else {
257 dhtnet::fileutils::removeAll(dir, true);
258 status = 1;
259 }
261 }
263 }
264
267
268#else
269 int r;
270
273 struct archive_entry* entry;
274
276
277 // Set reader formats(archive) and filters(compression)
280
281 // Set written files flags and standard lookup(uid/gid)
284
285 // Try to read the archive
286 if ((r = archive_read_open_filename(archiveReader.get(), archivePath.c_str(), 10240))) {
287 throw std::runtime_error("Open Archive: " + archivePath + "\t"
289 }
290
291 while (true) {
292 // Read headers until End of File
294 if (r == ARCHIVE_EOF) {
295 break;
296 }
297 if (r != ARCHIVE_OK && r != ARCHIVE_WARN) {
298 throw std::runtime_error("Error reading archive: "s
300 }
301
302 std::string_view fileEntry(archive_entry_pathname(entry));
303
304 // File is ok, copy its header to the ext writer
305 const auto& fileMatchPair = f(fileEntry);
306 if (fileMatchPair.first) {
310 if (r != ARCHIVE_OK) {
311 // Rollback if failed at a write operation
312 dhtnet::fileutils::removeAll(dir);
313 throw std::runtime_error("Write file header: " + fileEntry + "\t"
315 } else {
316 // Here both the reader and the writer have moved past the headers
317 // Copying the data content
319
320 while (true) {
322 if (r == ARCHIVE_EOF) {
323 break;
324 }
325
326 if (r != ARCHIVE_OK) {
327 throw std::runtime_error("Read file data: " + fileEntry + "\t"
329 }
330
332
333 if (r != ARCHIVE_OK) {
334 // Rollback if failed at a write operation
335 dhtnet::fileutils::removeAll(dir);
336 throw std::runtime_error("Write file data: " + fileEntry + "\t"
338 }
339 }
340 }
341 }
342 }
343#endif
344#endif
345}
346
347std::vector<uint8_t>
348readFileFromArchive(const std::string& archivePath, const std::string& fileRelativePathName)
349{
350 std::vector<uint8_t> fileContent;
351#ifdef ENABLE_PLUGIN
352#if defined(__APPLE__)
353 mz_zip_file* info;
354
355 void* zip_handle = mz_zip_create();
356 auto status = mz_zip_reader_open_file(zip_handle, archivePath.c_str());
358
359 while (status == MZ_OK) {
361 if (status != MZ_OK)
362 break;
363 std::string_view filename(info->filename, (size_t) info->filename_size);
364 if (filename == fileRelativePathName) {
366 fileContent.resize(info->uncompressed_size);
368 (void*) fileContent.data(),
369 info->uncompressed_size);
371 status = -1;
372 } else {
374 }
375 }
376
379#else
380 long r;
382 struct archive_entry* entry;
383
384 // Set reader formats(archive) and filters(compression)
387
388 // Try to read the archive
389 if ((r = archive_read_open_filename(archiveReader.get(), archivePath.c_str(), 10240))) {
390 throw std::runtime_error("Open Archive: " + archivePath + "\t"
392 }
393
394 while (true) {
395 // Read headers until End of File
397 if (r == ARCHIVE_EOF) {
398 break;
399 }
400
402 if (r != ARCHIVE_OK) {
403 throw std::runtime_error(fmt::format("Read file pathname: {}: {}", fileEntry, archive_error_string(archiveReader.get())));
404 }
405
406 // File is ok and the reader has moved past the header
408 // Copying the data content
410
411 while (true) {
413 if (r == ARCHIVE_EOF) {
414 return fileContent;
415 }
416
417 if (r != ARCHIVE_OK) {
418 throw std::runtime_error("Read file data: " + fileEntry + "\t"
420 }
421
422 if (fileContent.size() < static_cast<size_t>(db.offset)) {
423 fileContent.resize(db.offset);
424 }
425
426 auto dat = static_cast<const uint8_t*>(db.buff);
427 // push the buffer data in the string stream
428 fileContent.insert(fileContent.end(), dat, dat + db.size);
429 }
430 }
431 }
432 throw std::runtime_error("File " + fileRelativePathName + " not found in the archive");
433#endif
434#endif
435 return fileContent;
436}
437std::vector<std::string>
438listFilesFromArchive(const std::string& path)
439{
440 std::vector<std::string> filePaths;
441#ifdef ENABLE_PLUGIN
442#if defined(__APPLE__)
443 mz_zip_file* info = NULL;
444
445 void* zip_handle = mz_zip_create();
446 auto status = mz_zip_reader_open_file(zip_handle, path.c_str());
448
449 // read all the file path of the archive
450 while (status == MZ_OK) {
452 if (status != MZ_OK)
453 break;
454 std::string filename(info->filename, (size_t) info->filename_size);
455 filePaths.push_back(filename);
457 }
460#else
462 struct archive_entry* entry;
463
465 if(archive_read_open_filename(archiveReader.get(), path.c_str(), 10240)) {
466 throw std::runtime_error("Open Archive: " + path + "\t"
468 }
469
471 const char* name = archive_entry_pathname(entry);
472
473 filePaths.push_back(name);
474 }
475#endif
476#endif
477 return filePaths;
478}
479} // namespace archiver
480} // 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:76
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:111
std::vector< std::string > listFilesFromArchive(const std::string &path)
listFilesFromArchive list all files from an archive
Definition archiver.cpp:438
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:212
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:154
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:348
std::vector< uint8_t > decompressGzip(const std::string &path)
Decompress Gzip file to bytes.
Definition archiver.cpp:92
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