Skip to content
Snippets Groups Projects
Select Git revision
  • ab8af57625807921ca057575e37d1479760ec585
  • master default protected
  • release/202005
  • release/202001
  • release/201912
  • release/201911
  • release/releaseWindowsTestOne
  • release/windowsReleaseTest
  • release/releaseTest
  • release/releaseWindowsTest
  • release/201910
  • release/qt/201910
  • release/windows-test/201910
  • release/201908
  • release/201906
  • release/201905
  • release/201904
  • release/201903
  • release/201902
  • release/201901
  • release/201812
  • 4.0.0
  • 2.2.0
  • 2.1.0
  • 2.0.1
  • 2.0.0
  • 1.4.1
  • 1.4.0
  • 1.3.0
  • 1.2.0
  • 1.1.0
31 results

archiver.cpp

Blame
  • Adrien Béraud's avatar
    Adrien Béraud authored
    Change-Id: Idd489fcb185966fe421b6d544f4a131230ce35ca
    ab8af576
    History
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    archiver.cpp 16.27 KiB
    /*
     *  Copyright (C) 2004-2021 Savoir-faire Linux Inc.
     *
     *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
     *
     *  This program is free software; you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License as published by
     *  the Free Software Foundation; either version 3 of the License, or
     *  (at your option) any later version.
     *
     *  This program is distributed in the hope that it will be useful,
     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     *  GNU General Public License for more details.
     *
     *  You should have received a copy of the GNU General Public License
     *  along with this program; if not, write to the Free Software
     *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
     */
    
    #include "archiver.h"
    
    #include "client/ring_signal.h"
    #include "account_const.h"
    #include "configurationmanager_interface.h"
    
    #include "manager.h"
    #include "fileutils.h"
    #include "logger.h"
    
    #include <opendht/crypto.h>
    #include <json/json.h>
    #include <zlib.h>
    
    #ifdef ENABLE_PLUGIN
    extern "C" {
    #if defined(__APPLE__)
    #include <mz.h>
    #include <mz_strm.h>
    #include <mz_strm_os.h>
    #include <mz_zip.h>
    #include <mz_zip_rw.h>
    #include <filesystem>
    #else
    #include <archive.h>
    #include <archive_entry.h>
    #endif
    }
    #endif
    
    #include <sys/stat.h>
    #include <fstream>
    
    using namespace std::literals;
    
    namespace jami {
    namespace archiver {
    
    std::map<std::string, std::string>
    jsonValueToAccount(Json::Value& value, const std::string& accountId)
    {
        auto idPath_ = fileutils::get_data_dir() + DIR_SEPARATOR_STR + accountId;
        fileutils::check_dir(idPath_.c_str(), 0700);
        auto detailsMap = DRing::getAccountTemplate(
            value[DRing::Account::ConfProperties::TYPE].asString());
    
        for (Json::ValueIterator itr = value.begin(); itr != value.end(); itr++) {
            if (itr->asString().empty())
                continue;
            if (itr.key().asString().compare(DRing::Account::ConfProperties::TLS::CA_LIST_FILE) == 0) {
                std::string fileContent(itr->asString());
                fileutils::saveFile(idPath_ + DIR_SEPARATOR_STR "ca.key",
                                    {fileContent.begin(), fileContent.end()},
                                    0600);
    
            } else if (itr.key().asString().compare(
                           DRing::Account::ConfProperties::TLS::PRIVATE_KEY_FILE)
                       == 0) {
                std::string fileContent(itr->asString());
                fileutils::saveFile(idPath_ + DIR_SEPARATOR_STR "dht.key",
                                    {fileContent.begin(), fileContent.end()},
                                    0600);
    
            } else if (itr.key().asString().compare(
                           DRing::Account::ConfProperties::TLS::CERTIFICATE_FILE)
                       == 0) {
                std::string fileContent(itr->asString());
                fileutils::saveFile(idPath_ + DIR_SEPARATOR_STR "dht.crt",
                                    {fileContent.begin(), fileContent.end()},
                                    0600);
            } else
                detailsMap[itr.key().asString()] = itr->asString();
        }
    
        return detailsMap;
    }
    
    Json::Value
    accountToJsonValue(const std::map<std::string, std::string>& details)
    {
        Json::Value root;
        for (const auto& i : details) {
            if (i.first == DRing::Account::ConfProperties::Ringtone::PATH) {
                // Ringtone path is not exportable
            } else if (i.first == DRing::Account::ConfProperties::TLS::CA_LIST_FILE
                       || i.first == DRing::Account::ConfProperties::TLS::CERTIFICATE_FILE
                       || i.first == DRing::Account::ConfProperties::TLS::PRIVATE_KEY_FILE) {
                // replace paths by the files content
                std::ifstream ifs = fileutils::ifstream(i.second);
                std::string fileContent((std::istreambuf_iterator<char>(ifs)),
                                        std::istreambuf_iterator<char>());
                root[i.first] = fileContent;
            } else
                root[i.first] = i.second;
        }
    
        return root;
    }
    
    std::vector<uint8_t>
    compress(const std::string& str)
    {
        auto destSize = compressBound(str.size());
        std::vector<uint8_t> outbuffer(destSize);
        int ret = ::compress(reinterpret_cast<Bytef*>(outbuffer.data()),
                             &destSize,
                             (Bytef*) str.data(),
                             str.size());
        outbuffer.resize(destSize);
    
        if (ret != Z_OK) {
            std::ostringstream oss;
            oss << "Exception during zlib compression: (" << ret << ") ";
            throw std::runtime_error(oss.str());
        }
    
        return outbuffer;
    }
    
    void
    compressGzip(const std::string& str, const std::string& path)
    {
        auto fi = openGzip(path, "wb");
        gzwrite(fi, str.data(), str.size());
        gzclose(fi);
    }
    
    std::vector<uint8_t>
    decompressGzip(const std::string& path)
    {
        std::vector<uint8_t> out;
        auto fi = openGzip(path, "rb");
        gzrewind(fi);
        while (not gzeof(fi)) {
            std::array<uint8_t, 32768> outbuffer;
            int len = gzread(fi, outbuffer.data(), outbuffer.size());
            if (len == -1) {
                gzclose(fi);
                throw std::runtime_error("Exception during gzip decompression");
            }
            out.insert(out.end(), outbuffer.begin(), outbuffer.begin() + len);
        }
        gzclose(fi);
        return out;
    }
    
    std::vector<uint8_t>
    decompress(const std::vector<uint8_t>& str)
    {
        z_stream zs; // z_stream is zlib's control structure
        memset(&zs, 0, sizeof(zs));
    
        if (inflateInit(&zs) != Z_OK)
            throw std::runtime_error("inflateInit failed while decompressing.");
    
        zs.next_in = (Bytef*) str.data();
        zs.avail_in = str.size();
    
        int ret;
        std::vector<uint8_t> out;
    
        // get the decompressed bytes blockwise using repeated calls to inflate
        do {
            std::array<uint8_t, 32768> outbuffer;
            zs.next_out = reinterpret_cast<Bytef*>(outbuffer.data());
            zs.avail_out = outbuffer.size();
    
            ret = inflate(&zs, 0);
            if (ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
                break;
    
            if (out.size() < zs.total_out) {
                // append the block to the output string
                out.insert(out.end(), outbuffer.begin(), outbuffer.begin() + zs.total_out - out.size());
            }
        } while (ret == Z_OK);
    
        inflateEnd(&zs);
    
        // an error occurred that was not EOF
        if (ret != Z_STREAM_END) {
            std::ostringstream oss;
            oss << "Exception during zlib decompression: (" << ret << ") " << zs.msg;
            throw(std::runtime_error(oss.str()));
        }
    
        return out;
    }
    
    gzFile
    openGzip(const std::string& path, const char* mode)
    {
    #ifdef _WIN32
        return gzopen_w(jami::to_wstring(path).c_str(), mode);
    #else
        return gzopen(path.c_str(), mode);
    #endif
    }
    
    #ifdef ENABLE_PLUGIN
    #if !defined(__APPLE__)
    // LIBARCHIVE DEFINITIONS
    //==========================
    using ArchivePtr = std::unique_ptr<archive, void (*)(archive*)>;
    using ArchiveEntryPtr = std::unique_ptr<archive_entry, void (*)(archive_entry*)>;
    
    struct DataBlock
    {
        const void* buff;
        size_t size;
        int64_t offset;
    };
    
    long
    readDataBlock(const ArchivePtr& a, DataBlock& b)
    {
        return archive_read_data_block(a.get(), &b.buff, &b.size, &b.offset);
    }
    
    long
    writeDataBlock(const ArchivePtr& a, DataBlock& b)
    {
        return archive_write_data_block(a.get(), b.buff, b.size, b.offset);
    }
    
    ArchivePtr
    createArchiveReader()
    {
        ArchivePtr archivePtr {archive_read_new(), [](archive* a) {
                                   archive_read_close(a);
                                   archive_read_free(a);
                               }};
        return archivePtr;
    }
    
    static ArchivePtr
    createArchiveDiskWriter()
    {
        return {archive_write_disk_new(), [](archive* a) {
                    archive_write_close(a);
                    archive_write_free(a);
                }};
    }
    //==========================
    #endif
    #endif
    
    void
    uncompressArchive(const std::string& archivePath, const std::string& dir, const FileMatchPair& f)
    {
    #ifdef ENABLE_PLUGIN
    #if defined(__APPLE__)
        void* zip_handle = NULL;
        mz_zip_file* info = NULL;
    
        fileutils::check_dir(dir.c_str());
    
        mz_zip_create(&zip_handle);
        auto status = mz_zip_reader_open_file(zip_handle, archivePath.c_str());
        status |= mz_zip_reader_goto_first_entry(zip_handle);
    
        while (status == MZ_OK) {
            status |= mz_zip_reader_entry_get_info(zip_handle, &info);
            if (status != MZ_OK) {
                fileutils::removeAll(dir, true);
                break;
            }
            std::string_view filename(info->filename, (size_t)info->filename_size);
            const auto& fileMatchPair = f(filename);
            if (fileMatchPair.first) {
                auto filePath = dir + DIR_SEPARATOR_STR + fileMatchPair.second;
                std::filesystem::path directory(filePath);
                directory = directory.remove_filename();
                if (!std::filesystem::exists(directory))
                    fileutils::check_dir(directory.c_str());
                mz_zip_reader_entry_open(zip_handle);
                void* buffStream = NULL;
                buffStream = mz_stream_os_create(&buffStream);
                if (mz_stream_os_open(buffStream, filePath.c_str(), MZ_OPEN_MODE_WRITE | MZ_OPEN_MODE_CREATE) == MZ_OK) {
                    int chunkSize = 8192;
                    std::vector<uint8_t> fileContent;
                    fileContent.resize(chunkSize);
                    while (auto ret = mz_zip_reader_entry_read(zip_handle, (void*) fileContent.data(), chunkSize)) {
                        ret = mz_stream_os_write(buffStream, (void*) fileContent.data(), ret);
                        if (ret < 0) {
                            fileutils::removeAll(dir, true);
                            status = 1;
                        }
                    }
                    mz_stream_os_close(buffStream);
                    mz_stream_os_delete(&buffStream);
                } else {
                    fileutils::removeAll(dir, true);
                    status = 1;
                }
                mz_zip_reader_entry_close(zip_handle);
            }
            status |= mz_zip_reader_goto_next_entry(zip_handle);
        }
    
        mz_zip_reader_close(zip_handle);
        mz_zip_delete(&zip_handle);
    
    #else
        int r;
    
        ArchivePtr archiveReader = createArchiveReader();
        ArchivePtr archiveDiskWriter = createArchiveDiskWriter();
        struct archive_entry* entry;
    
        int flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_NO_HFS_COMPRESSION;
    
        // Set reader formats(archive) and filters(compression)
        archive_read_support_filter_all(archiveReader.get());
        archive_read_support_format_all(archiveReader.get());
    
        // Set written files flags and standard lookup(uid/gid)
        archive_write_disk_set_options(archiveDiskWriter.get(), flags);
        archive_write_disk_set_standard_lookup(archiveDiskWriter.get());
    
        // Try to read the archive
        if ((r = archive_read_open_filename(archiveReader.get(), archivePath.c_str(), 10240))) {
            throw std::runtime_error("Open Archive: " + archivePath + "\t"
                                     + archive_error_string(archiveReader.get()));
        }
    
        while (true) {
            // Read headers until End of File
            r = archive_read_next_header(archiveReader.get(), &entry);
            if (r == ARCHIVE_EOF) {
                break;
            }
            if (r != ARCHIVE_OK && r != ARCHIVE_WARN) {
                throw std::runtime_error("Error reading archive: "s + archive_error_string(archiveReader.get()));
            }
    
            std::string_view fileEntry(archive_entry_pathname(entry));
    
            // File is ok, copy its header to the ext writer
            const auto& fileMatchPair = f(fileEntry);
            if (fileMatchPair.first) {
                std::string entryDestinationPath = dir + DIR_SEPARATOR_CH + fileMatchPair.second;
                archive_entry_set_pathname(entry, entryDestinationPath.c_str());
                r = archive_write_header(archiveDiskWriter.get(), entry);
                if (r != ARCHIVE_OK) {
                    // Rollback if failed at a write operation
                    fileutils::removeAll(dir);
                    throw std::runtime_error("Write file header: " + fileEntry + "\t"
                                             + archive_error_string(archiveDiskWriter.get()));
                } else {
                    // Here both the reader and the writer have moved past the headers
                    // Copying the data content
                    DataBlock db;
    
                    while (true) {
                        r = readDataBlock(archiveReader, db);
                        if (r == ARCHIVE_EOF) {
                            break;
                        }
    
                        if (r != ARCHIVE_OK) {
                            throw std::runtime_error("Read file data: " + fileEntry + "\t"
                                                     + archive_error_string(archiveReader.get()));
                        }
    
                        r = writeDataBlock(archiveDiskWriter, db);
    
                        if (r != ARCHIVE_OK) {
                            // Rollback if failed at a write operation
                            fileutils::removeAll(dir);
                            throw std::runtime_error("Write file data: " + fileEntry + "\t"
                                                     + archive_error_string(archiveDiskWriter.get()));
                        }
                    }
                }
            }
        }
    #endif
    #endif
    }
    
    std::vector<uint8_t>
    readFileFromArchive(const std::string& archivePath, const std::string& fileRelativePathName)
    {
        std::vector<uint8_t> fileContent;
    #ifdef ENABLE_PLUGIN
    #if defined(__APPLE__)
        void* zip_handle = NULL;
        mz_zip_file* info;
    
        mz_zip_create(&zip_handle);
        auto status = mz_zip_reader_open_file(zip_handle, archivePath.c_str());
        status |= mz_zip_reader_goto_first_entry(zip_handle);
    
        while (status == MZ_OK) {
            status = mz_zip_reader_entry_get_info(zip_handle, &info);
            if (status != MZ_OK)
                break;
            std::string_view filename(info->filename, (size_t)info->filename_size);
            if (filename == fileRelativePathName) {
                mz_zip_reader_entry_open(zip_handle);
                fileContent.resize(info->uncompressed_size);
                mz_zip_reader_entry_read(zip_handle, (void*) fileContent.data(), info->uncompressed_size);
                mz_zip_reader_entry_close(zip_handle);
                status = -1;
            } else {
                status = mz_zip_reader_goto_next_entry(zip_handle);
            }
        }
    
        mz_zip_reader_close(zip_handle);
        mz_zip_delete(&zip_handle);
    #else
        long r;
        ArchivePtr archiveReader = createArchiveReader();
        struct archive_entry* entry;
    
        // Set reader formats(archive) and filters(compression)
        archive_read_support_filter_all(archiveReader.get());
        archive_read_support_format_all(archiveReader.get());
    
        // Try to read the archive
        if ((r = archive_read_open_filename(archiveReader.get(), archivePath.c_str(), 10240))) {
            throw std::runtime_error("Open Archive: " + archivePath + "\t"
                                     + archive_error_string(archiveReader.get()));
        }
    
        while (true) {
            // Read headers until End of File
            r = archive_read_next_header(archiveReader.get(), &entry);
            if (r == ARCHIVE_EOF) {
                break;
            }
    
            std::string fileEntry = archive_entry_pathname(entry) ? archive_entry_pathname(entry) : "";
    
            if (r != ARCHIVE_OK) {
                throw std::runtime_error("Read file pathname: " + fileEntry + "\t"
                                         + archive_error_string(archiveReader.get()));
            }
    
            // File is ok and the reader has moved past the header
            if (fileEntry == fileRelativePathName) {
                // Copying the data content
                DataBlock db;
    
                while (true) {
                    r = readDataBlock(archiveReader, db);
                    if (r == ARCHIVE_EOF) {
                        return fileContent;
                    }
    
                    if (r != ARCHIVE_OK) {
                        throw std::runtime_error("Read file data: " + fileEntry + "\t"
                                                 + archive_error_string(archiveReader.get()));
                    }
    
                    if (fileContent.size() < static_cast<size_t>(db.offset)) {
                        fileContent.resize(db.offset);
                    }
    
                    auto dat = static_cast<const uint8_t*>(db.buff);
                    // push the buffer data in the string stream
                    fileContent.insert(fileContent.end(), dat, dat + db.size);
                }
            }
        }
        throw std::runtime_error("File " + fileRelativePathName + " not found in the archive");
    #endif
    #endif
        return fileContent;
    }
    
    } // namespace archiver
    } // namespace jami