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-03-28 08:00:27 Functions: 14 15 93.3 %

          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

Generated by: LCOV version 1.14