LCOV - code coverage report
Current view: top level - src - archiver.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 113 157 72.0 %
Date: 2024-11-15 09:04:49 Functions: 14 15 93.3 %

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

Generated by: LCOV version 1.14