Line data Source code
1 : /*
2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc.
3 : *
4 : * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, write to the Free Software
18 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 : */
20 :
21 : #include "archiver.h"
22 :
23 : #include "client/ring_signal.h"
24 : #include "account_const.h"
25 : #include "configurationmanager_interface.h"
26 :
27 : #include "manager.h"
28 : #include "fileutils.h"
29 : #include "logger.h"
30 :
31 : #include <opendht/crypto.h>
32 : #include <json/json.h>
33 : #include <zlib.h>
34 :
35 : #ifdef ENABLE_PLUGIN
36 : extern "C" {
37 : #if defined(__APPLE__)
38 : #include <mz.h>
39 : #include <mz_strm.h>
40 : #include <mz_strm_os.h>
41 : #include <mz_zip.h>
42 : #include <mz_zip_rw.h>
43 : #else
44 : #include <archive.h>
45 : #include <archive_entry.h>
46 : #endif
47 : }
48 : #endif
49 :
50 : #include <sys/stat.h>
51 : #include <fstream>
52 :
53 : using namespace std::literals;
54 :
55 : namespace jami {
56 : namespace archiver {
57 :
58 : std::vector<uint8_t>
59 17 : compress(const std::string& str)
60 : {
61 17 : auto destSize = compressBound(str.size());
62 17 : std::vector<uint8_t> outbuffer(destSize);
63 34 : int ret = ::compress(reinterpret_cast<Bytef*>(outbuffer.data()),
64 : &destSize,
65 17 : (Bytef*) str.data(),
66 : str.size());
67 17 : outbuffer.resize(destSize);
68 :
69 17 : if (ret != Z_OK) {
70 0 : std::ostringstream oss;
71 0 : oss << "Exception during zlib compression: (" << ret << ") ";
72 0 : throw std::runtime_error(oss.str());
73 0 : }
74 :
75 34 : return outbuffer;
76 0 : }
77 :
78 : void
79 802 : compressGzip(const std::string& str, const std::string& path)
80 : {
81 802 : auto fi = openGzip(path, "wb");
82 802 : gzwrite(fi, str.data(), str.size());
83 802 : gzclose(fi);
84 802 : }
85 :
86 : void
87 3 : compressGzip(const std::vector<uint8_t>& dat, const std::string& path)
88 : {
89 3 : auto fi = openGzip(path, "wb");
90 3 : gzwrite(fi, dat.data(), dat.size());
91 3 : gzclose(fi);
92 3 : }
93 :
94 : std::vector<uint8_t>
95 0 : decompressGzip(const std::string& path)
96 : {
97 0 : std::vector<uint8_t> out;
98 0 : auto fi = openGzip(path, "rb");
99 0 : gzrewind(fi);
100 0 : while (not gzeof(fi)) {
101 : std::array<uint8_t, 32768> outbuffer;
102 0 : int len = gzread(fi, outbuffer.data(), outbuffer.size());
103 0 : if (len == -1) {
104 0 : gzclose(fi);
105 0 : throw std::runtime_error("Exception during gzip decompression");
106 : }
107 0 : out.insert(out.end(), outbuffer.begin(), outbuffer.begin() + len);
108 : }
109 0 : gzclose(fi);
110 0 : return out;
111 0 : }
112 :
113 : std::vector<uint8_t>
114 97 : decompress(const std::vector<uint8_t>& str)
115 : {
116 : z_stream zs; // z_stream is zlib's control structure
117 97 : memset(&zs, 0, sizeof(zs));
118 :
119 97 : if (inflateInit2(&zs, 32+MAX_WBITS) != Z_OK)
120 0 : throw std::runtime_error("inflateInit failed while decompressing.");
121 :
122 97 : zs.next_in = (Bytef*) str.data();
123 97 : zs.avail_in = str.size();
124 :
125 : int ret;
126 97 : std::vector<uint8_t> out;
127 :
128 : // get the decompressed bytes blockwise using repeated calls to inflate
129 : do {
130 : std::array<uint8_t, 32768> outbuffer;
131 97 : zs.next_out = reinterpret_cast<Bytef*>(outbuffer.data());
132 97 : zs.avail_out = outbuffer.size();
133 :
134 97 : ret = inflate(&zs, 0);
135 97 : if (ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
136 : break;
137 :
138 97 : if (out.size() < zs.total_out) {
139 : // append the block to the output string
140 97 : out.insert(out.end(), outbuffer.begin(), outbuffer.begin() + zs.total_out - out.size());
141 : }
142 97 : } while (ret == Z_OK);
143 :
144 97 : inflateEnd(&zs);
145 :
146 : // an error occurred that was not EOF
147 97 : if (ret != Z_STREAM_END) {
148 0 : std::ostringstream oss;
149 0 : oss << "Exception during zlib decompression: (" << ret << ") " << zs.msg;
150 0 : throw(std::runtime_error(oss.str()));
151 0 : }
152 :
153 194 : return out;
154 0 : }
155 :
156 : gzFile
157 805 : openGzip(const std::string& path, const char* mode)
158 : {
159 : #ifdef _WIN32
160 : return gzopen_w(jami::to_wstring(path).c_str(), mode);
161 : #else
162 805 : return gzopen(path.c_str(), mode);
163 : #endif
164 : }
165 :
166 : #ifdef ENABLE_PLUGIN
167 : #if !defined(__APPLE__)
168 : // LIBARCHIVE DEFINITIONS
169 : //==========================
170 : using ArchivePtr = std::unique_ptr<archive, void (*)(archive*)>;
171 : using ArchiveEntryPtr = std::unique_ptr<archive_entry, void (*)(archive_entry*)>;
172 :
173 : struct DataBlock
174 : {
175 : const void* buff;
176 : size_t size;
177 : int64_t offset;
178 : };
179 :
180 : long
181 711 : readDataBlock(const ArchivePtr& a, DataBlock& b)
182 : {
183 711 : return archive_read_data_block(a.get(), &b.buff, &b.size, &b.offset);
184 : }
185 :
186 : long
187 168 : writeDataBlock(const ArchivePtr& a, DataBlock& b)
188 : {
189 168 : return archive_write_data_block(a.get(), b.buff, b.size, b.offset);
190 : }
191 :
192 : ArchivePtr
193 185 : createArchiveReader()
194 : {
195 185 : ArchivePtr archivePtr {archive_read_new(), [](archive* a) {
196 185 : archive_read_close(a);
197 185 : archive_read_free(a);
198 185 : }};
199 185 : return archivePtr;
200 : }
201 :
202 : static ArchivePtr
203 7 : createArchiveDiskWriter()
204 : {
205 7 : return {archive_write_disk_new(), [](archive* a) {
206 7 : archive_write_close(a);
207 7 : archive_write_free(a);
208 7 : }};
209 : }
210 : //==========================
211 : #endif
212 : #endif
213 :
214 : void
215 7 : uncompressArchive(const std::string& archivePath, const std::string& dir, const FileMatchPair& f)
216 : {
217 : #ifdef ENABLE_PLUGIN
218 : #if defined(__APPLE__)
219 : void* zip_handle = NULL;
220 : mz_zip_file* info = NULL;
221 :
222 : dhtnet::fileutils::check_dir(dir.c_str());
223 :
224 : mz_zip_create(&zip_handle);
225 : auto status = mz_zip_reader_open_file(zip_handle, archivePath.c_str());
226 : status |= mz_zip_reader_goto_first_entry(zip_handle);
227 :
228 : while (status == MZ_OK) {
229 : status |= mz_zip_reader_entry_get_info(zip_handle, &info);
230 : if (status != MZ_OK) {
231 : dhtnet::fileutils::removeAll(dir, true);
232 : break;
233 : }
234 : std::string_view filename(info->filename, (size_t) info->filename_size);
235 : const auto& fileMatchPair = f(filename);
236 : if (fileMatchPair.first) {
237 : auto filePath = dir + DIR_SEPARATOR_STR + fileMatchPair.second;
238 : std::string directory = filePath.substr(0, filePath.find_last_of(DIR_SEPARATOR_CH));
239 : dhtnet::fileutils::check_dir(directory.c_str());
240 : mz_zip_reader_entry_open(zip_handle);
241 : void* buffStream = NULL;
242 : buffStream = mz_stream_os_create(&buffStream);
243 : if (mz_stream_os_open(buffStream,
244 : filePath.c_str(),
245 : MZ_OPEN_MODE_WRITE | MZ_OPEN_MODE_CREATE)
246 : == MZ_OK) {
247 : int chunkSize = 8192;
248 : std::vector<uint8_t> fileContent;
249 : fileContent.resize(chunkSize);
250 : while (auto ret = mz_zip_reader_entry_read(zip_handle,
251 : (void*) fileContent.data(),
252 : chunkSize)) {
253 : ret = mz_stream_os_write(buffStream, (void*) fileContent.data(), ret);
254 : if (ret < 0) {
255 : dhtnet::fileutils::removeAll(dir, true);
256 : status = 1;
257 : }
258 : }
259 : mz_stream_os_close(buffStream);
260 : mz_stream_os_delete(&buffStream);
261 : } else {
262 : dhtnet::fileutils::removeAll(dir, true);
263 : status = 1;
264 : }
265 : mz_zip_reader_entry_close(zip_handle);
266 : }
267 : status |= mz_zip_reader_goto_next_entry(zip_handle);
268 : }
269 :
270 : mz_zip_reader_close(zip_handle);
271 : mz_zip_delete(&zip_handle);
272 :
273 : #else
274 : int r;
275 :
276 7 : ArchivePtr archiveReader = createArchiveReader();
277 7 : ArchivePtr archiveDiskWriter = createArchiveDiskWriter();
278 : struct archive_entry* entry;
279 :
280 7 : int flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_NO_HFS_COMPRESSION;
281 :
282 : // Set reader formats(archive) and filters(compression)
283 7 : archive_read_support_filter_all(archiveReader.get());
284 7 : archive_read_support_format_all(archiveReader.get());
285 :
286 : // Set written files flags and standard lookup(uid/gid)
287 7 : archive_write_disk_set_options(archiveDiskWriter.get(), flags);
288 7 : archive_write_disk_set_standard_lookup(archiveDiskWriter.get());
289 :
290 : // Try to read the archive
291 7 : if ((r = archive_read_open_filename(archiveReader.get(), archivePath.c_str(), 10240))) {
292 0 : throw std::runtime_error("Open Archive: " + archivePath + "\t"
293 0 : + archive_error_string(archiveReader.get()));
294 : }
295 :
296 : while (true) {
297 : // Read headers until End of File
298 84 : r = archive_read_next_header(archiveReader.get(), &entry);
299 84 : if (r == ARCHIVE_EOF) {
300 7 : break;
301 : }
302 77 : if (r != ARCHIVE_OK && r != ARCHIVE_WARN) {
303 0 : throw std::runtime_error("Error reading archive: "s
304 0 : + archive_error_string(archiveReader.get()));
305 : }
306 :
307 77 : std::string_view fileEntry(archive_entry_pathname(entry));
308 :
309 : // File is ok, copy its header to the ext writer
310 77 : const auto& fileMatchPair = f(fileEntry);
311 77 : if (fileMatchPair.first) {
312 77 : std::string entryDestinationPath = dir + DIR_SEPARATOR_CH + fileMatchPair.second;
313 77 : archive_entry_set_pathname(entry, entryDestinationPath.c_str());
314 77 : r = archive_write_header(archiveDiskWriter.get(), entry);
315 77 : if (r != ARCHIVE_OK) {
316 : // Rollback if failed at a write operation
317 0 : dhtnet::fileutils::removeAll(dir);
318 0 : throw std::runtime_error("Write file header: " + fileEntry + "\t"
319 0 : + archive_error_string(archiveDiskWriter.get()));
320 : } else {
321 : // Here both the reader and the writer have moved past the headers
322 : // Copying the data content
323 : DataBlock db;
324 :
325 : while (true) {
326 245 : r = readDataBlock(archiveReader, db);
327 245 : if (r == ARCHIVE_EOF) {
328 77 : break;
329 : }
330 :
331 168 : if (r != ARCHIVE_OK) {
332 0 : throw std::runtime_error("Read file data: " + fileEntry + "\t"
333 0 : + archive_error_string(archiveReader.get()));
334 : }
335 :
336 168 : r = writeDataBlock(archiveDiskWriter, db);
337 :
338 168 : if (r != ARCHIVE_OK) {
339 : // Rollback if failed at a write operation
340 0 : dhtnet::fileutils::removeAll(dir);
341 0 : throw std::runtime_error("Write file data: " + fileEntry + "\t"
342 0 : + archive_error_string(archiveDiskWriter.get()));
343 : }
344 : }
345 : }
346 77 : }
347 77 : }
348 : #endif
349 : #endif
350 7 : }
351 :
352 : std::vector<uint8_t>
353 167 : readFileFromArchive(const std::string& archivePath, const std::string& fileRelativePathName)
354 : {
355 167 : std::vector<uint8_t> fileContent;
356 : #ifdef ENABLE_PLUGIN
357 : #if defined(__APPLE__)
358 : void* zip_handle = NULL;
359 : mz_zip_file* info;
360 :
361 : mz_zip_create(&zip_handle);
362 : auto status = mz_zip_reader_open_file(zip_handle, archivePath.c_str());
363 : status |= mz_zip_reader_goto_first_entry(zip_handle);
364 :
365 : while (status == MZ_OK) {
366 : status = mz_zip_reader_entry_get_info(zip_handle, &info);
367 : if (status != MZ_OK)
368 : break;
369 : std::string_view filename(info->filename, (size_t) info->filename_size);
370 : if (filename == fileRelativePathName) {
371 : mz_zip_reader_entry_open(zip_handle);
372 : fileContent.resize(info->uncompressed_size);
373 : mz_zip_reader_entry_read(zip_handle,
374 : (void*) fileContent.data(),
375 : info->uncompressed_size);
376 : mz_zip_reader_entry_close(zip_handle);
377 : status = -1;
378 : } else {
379 : status = mz_zip_reader_goto_next_entry(zip_handle);
380 : }
381 : }
382 :
383 : mz_zip_reader_close(zip_handle);
384 : mz_zip_delete(&zip_handle);
385 : #else
386 : long r;
387 167 : ArchivePtr archiveReader = createArchiveReader();
388 : struct archive_entry* entry;
389 :
390 : // Set reader formats(archive) and filters(compression)
391 167 : archive_read_support_filter_all(archiveReader.get());
392 167 : archive_read_support_format_all(archiveReader.get());
393 :
394 : // Try to read the archive
395 167 : if ((r = archive_read_open_filename(archiveReader.get(), archivePath.c_str(), 10240))) {
396 0 : throw std::runtime_error("Open Archive: " + archivePath + "\t"
397 0 : + archive_error_string(archiveReader.get()));
398 : }
399 :
400 : while (true) {
401 : // Read headers until End of File
402 925 : r = archive_read_next_header(archiveReader.get(), &entry);
403 925 : if (r == ARCHIVE_EOF) {
404 1 : break;
405 : }
406 :
407 924 : std::string fileEntry = archive_entry_pathname(entry) ? archive_entry_pathname(entry) : "";
408 924 : if (r != ARCHIVE_OK) {
409 0 : throw std::runtime_error(fmt::format("Read file pathname: {}: {}", fileEntry, archive_error_string(archiveReader.get())));
410 : }
411 :
412 : // File is ok and the reader has moved past the header
413 924 : if (fileEntry == fileRelativePathName) {
414 : // Copying the data content
415 : DataBlock db;
416 :
417 : while (true) {
418 466 : r = readDataBlock(archiveReader, db);
419 466 : if (r == ARCHIVE_EOF) {
420 332 : return fileContent;
421 : }
422 :
423 300 : if (r != ARCHIVE_OK) {
424 0 : throw std::runtime_error("Read file data: " + fileEntry + "\t"
425 0 : + archive_error_string(archiveReader.get()));
426 : }
427 :
428 300 : if (fileContent.size() < static_cast<size_t>(db.offset)) {
429 0 : fileContent.resize(db.offset);
430 : }
431 :
432 300 : auto dat = static_cast<const uint8_t*>(db.buff);
433 : // push the buffer data in the string stream
434 300 : fileContent.insert(fileContent.end(), dat, dat + db.size);
435 300 : }
436 : }
437 1682 : }
438 1 : throw std::runtime_error("File " + fileRelativePathName + " not found in the archive");
439 : #endif
440 : #endif
441 : return fileContent;
442 168 : }
443 : std::vector<std::string>
444 11 : listFilesFromArchive(const std::string& path)
445 : {
446 11 : std::vector<std::string> filePaths;
447 : #ifdef ENABLE_PLUGIN
448 : #if defined(__APPLE__)
449 : void* zip_handle = NULL;
450 : mz_zip_file* info = NULL;
451 :
452 : mz_zip_create(&zip_handle);
453 : auto status = mz_zip_reader_open_file(zip_handle, path.c_str());
454 : status |= mz_zip_reader_goto_first_entry(zip_handle);
455 :
456 : // read all the file path of the archive
457 : while (status == MZ_OK) {
458 : status = mz_zip_reader_entry_get_info(zip_handle, &info);
459 : if (status != MZ_OK)
460 : break;
461 : std::string filename(info->filename, (size_t) info->filename_size);
462 : filePaths.push_back(filename);
463 : status = mz_zip_reader_goto_next_entry(zip_handle);
464 : }
465 : mz_zip_reader_close(zip_handle);
466 : mz_zip_delete(&zip_handle);
467 : #else
468 11 : ArchivePtr archiveReader = createArchiveReader();
469 : struct archive_entry* entry;
470 :
471 11 : archive_read_support_format_all(archiveReader.get());
472 11 : if(archive_read_open_filename(archiveReader.get(), path.c_str(), 10240)) {
473 0 : throw std::runtime_error("Open Archive: " + path + "\t"
474 0 : + archive_error_string(archiveReader.get()));
475 : }
476 :
477 130 : while (archive_read_next_header(archiveReader.get(), &entry) == ARCHIVE_OK) {
478 119 : const char* name = archive_entry_pathname(entry);
479 :
480 119 : filePaths.push_back(name);
481 : }
482 : #endif
483 : #endif
484 22 : return filePaths;
485 11 : }
486 : } // namespace archiver
487 : } // namespace jami
|