LCOV - code coverage report
Current view: top level - foo/src - archiver.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 35 147 23.8 %
Date: 2025-12-18 10:07:43 Functions: 5 15 33.3 %

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

Generated by: LCOV version 1.14