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

Generated by: LCOV version 1.14