├── autotests ├── data │ ├── artest.a │ ├── out.epub │ ├── twofiles.gz │ ├── zip64.pkpass │ ├── threefiles.gz │ ├── 7z_coder_test_bcj.7z │ ├── corpus │ │ ├── sample.tar.lz │ │ ├── sample.tar.xz │ │ ├── sample.tar.zst │ │ ├── sample.ar │ │ └── sample.tar │ ├── dirpermissions.zip │ ├── readnumber_check.7z │ ├── runtime_resource.rcc │ ├── tar_rootdir.tar.gz │ ├── zip_within_zip.zip │ ├── 7z_coder_test_bcj2.7z │ ├── 7z_coder_test_bzip2.7z │ ├── 7z_coder_test_copy.7z │ ├── 7z_coder_test_lzma.7z │ ├── 7z_coder_test_lzma2.7z │ ├── 7z_coder_test_zstd.7z │ ├── password_protected.7z │ ├── tar_prefix_test.tar.gz │ ├── zip_deep_hierarchy.zip │ ├── 7z_coder_test_deflate.7z │ ├── filename_ends_in_slash.7z │ ├── global_header_test.tar.gz │ ├── tar_deep_hierarchy.tar.xz │ ├── zip64_datadescriptor.zip │ ├── multiple_nameless_files.7z │ ├── ossfuzz_issue_433303801.zip │ ├── ossfuzz_issue_440829292.7z │ ├── ossfuzz_issue_441906077.7z │ ├── tar_directory_twice.tar.gz │ ├── tar_directory_forgotten.tar.gz │ ├── tar_non_ascii_file_name.tar.gz │ ├── unusual_but_valid_364071.zip │ ├── zip64_extra_zip64_size.zip.gz │ ├── tar_emptyfile_missingdir.tar.gz │ ├── zip64_nested_stored_streamed.zip │ ├── zip64_end_of_central_directory.zip │ ├── zip64_extra_zip64_localheader.oxps │ ├── zip64_extra_zip64_size_first.zip.gz │ ├── ossfuzz_testcase_4566647131406336.7z │ ├── ossfuzz_testcase_4784793343819776.7z │ ├── ossfuzz_testcase_5082404562993152.7z │ ├── ossfuzz_testcase_5125023070486528.7z │ ├── ossfuzz_testcase_5132547098214400.7z │ ├── ossfuzz_testcase_5141245598171136.7z │ ├── ossfuzz_testcase_5161502865948672.7z │ ├── ossfuzz_testcase_5497856632094720.7z │ ├── ossfuzz_testcase_5503074996387840.7z │ ├── ossfuzz_testcase_5560695602348032.7z │ ├── ossfuzz_testcase_5581009572921344.7z │ ├── ossfuzz_testcase_5821682553257984.7z │ ├── ossfuzz_testcase_5991129817612288.7z │ ├── ossfuzz_testcase_6077171694370816.7z │ ├── ossfuzz_testcase_6096742417498112.7z │ ├── ossfuzz_testcase_6213340184772608.7z │ ├── ossfuzz_testcase_6248361801089024.7z │ ├── ossfuzz_testcase_6366650283917312.7z │ ├── ossfuzz_testcase_6532014901886976.7z │ ├── ossfuzz_testcase_6593198198947840.7z │ ├── ossfuzz_testcase_6597581431177216.7z │ ├── ossfuzz_testcase_6653406484955136.7z │ ├── ossfuzz_testcase_6740459604213760.7z │ ├── ossfuzz_testcase_6581632288227328.tar.gz │ ├── redundantDataDescriptorsNoSignature.zip │ ├── redundantDataDescriptorsWithSignature.zip │ ├── tar_relative_path_outside_archive.tar.bz2 │ └── dict │ │ ├── kar_fuzzer.dict │ │ ├── ktar_lz_fuzzer.dict │ │ ├── ktar_fuzzer.dict │ │ ├── ktar_bz2_fuzzer.dict │ │ ├── kzip_fuzzer.dict │ │ ├── ktar_xz_fuzzer.dict │ │ ├── ktar_zst_fuzzer.dict │ │ ├── ktar_gz_fuzzer.dict │ │ └── k7z_fuzzer.dict ├── klimitediodevicetest.h ├── kfiltertest.h ├── kcompressiondevicetest.h ├── ossfuzz │ ├── prepare_build.sh │ ├── kcompressiondevice_fuzzer.cc │ ├── CMakeLists.txt │ ├── karchive_fuzzer.cc │ ├── karchive_fuzzer_common.h │ ├── README.md │ └── build_fuzzers.sh ├── klimitediodevicetest.cpp ├── CMakeLists.txt ├── karchivetest.h └── kcompressiondevicetest.cpp ├── test_package ├── example.cpp ├── CMakeLists.txt └── conanfile.py ├── .git-blame-ignore-revs ├── REUSE.toml ├── examples ├── unzipper │ ├── CMakeLists.txt │ └── main.cpp ├── bzip2gzip │ ├── CMakeLists.txt │ └── main.cpp ├── tarlocalfiles │ ├── CMakeLists.txt │ └── main.cpp └── helloworld │ ├── CMakeLists.txt │ └── main.cpp ├── .kde-ci.yml ├── src ├── karchive.qdoc ├── Messages.sh ├── config-compression.h.cmake ├── kcompressiondevice_p.h ├── karchive-index.qdoc ├── karchive.qdocconf ├── klzfilter.h ├── kbzip2filter.h ├── kzstdfilter.h ├── knonefilter.h ├── kfilterbase.cpp ├── klimitediodevice_p.h ├── kxzfilter.h ├── karchive_p.h ├── kgzipfilter.h ├── klimitediodevice.cpp ├── knonefilter.cpp ├── kzipfileentry.h ├── karchiveentry.h ├── kfilterbase.h ├── krcc.h ├── kar.h ├── CMakeLists.txt ├── karchivefile.h ├── kzstdfilter.cpp ├── k7zip.h ├── ktar.h ├── krcc.cpp ├── karchivedirectory.h ├── kcompressiondevice.h ├── kbzip2filter.cpp ├── kzip.h ├── kar.cpp └── kxzfilter.cpp ├── AUTHORS ├── metainfo.yaml ├── .gitignore ├── tests ├── CMakeLists.txt ├── kartest.cpp ├── krcctest.cpp ├── ktartest.cpp └── k7ziptest.cpp ├── .gitlab-ci.yml ├── README.md ├── INSTALL ├── KF6ArchiveConfig.cmake.in ├── conanfile.py ├── LICENSES ├── BSD-2-Clause.txt └── CC0-1.0.txt └── CMakeLists.txt /autotests/data/artest.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/artest.a -------------------------------------------------------------------------------- /autotests/data/out.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/out.epub -------------------------------------------------------------------------------- /autotests/data/twofiles.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/twofiles.gz -------------------------------------------------------------------------------- /autotests/data/zip64.pkpass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/zip64.pkpass -------------------------------------------------------------------------------- /autotests/data/threefiles.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/threefiles.gz -------------------------------------------------------------------------------- /autotests/data/7z_coder_test_bcj.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/7z_coder_test_bcj.7z -------------------------------------------------------------------------------- /autotests/data/corpus/sample.tar.lz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/corpus/sample.tar.lz -------------------------------------------------------------------------------- /autotests/data/corpus/sample.tar.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/corpus/sample.tar.xz -------------------------------------------------------------------------------- /autotests/data/dirpermissions.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/dirpermissions.zip -------------------------------------------------------------------------------- /autotests/data/readnumber_check.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/readnumber_check.7z -------------------------------------------------------------------------------- /autotests/data/runtime_resource.rcc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/runtime_resource.rcc -------------------------------------------------------------------------------- /autotests/data/tar_rootdir.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/tar_rootdir.tar.gz -------------------------------------------------------------------------------- /autotests/data/zip_within_zip.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/zip_within_zip.zip -------------------------------------------------------------------------------- /autotests/data/7z_coder_test_bcj2.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/7z_coder_test_bcj2.7z -------------------------------------------------------------------------------- /autotests/data/7z_coder_test_bzip2.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/7z_coder_test_bzip2.7z -------------------------------------------------------------------------------- /autotests/data/7z_coder_test_copy.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/7z_coder_test_copy.7z -------------------------------------------------------------------------------- /autotests/data/7z_coder_test_lzma.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/7z_coder_test_lzma.7z -------------------------------------------------------------------------------- /autotests/data/7z_coder_test_lzma2.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/7z_coder_test_lzma2.7z -------------------------------------------------------------------------------- /autotests/data/7z_coder_test_zstd.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/7z_coder_test_zstd.7z -------------------------------------------------------------------------------- /autotests/data/corpus/sample.tar.zst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/corpus/sample.tar.zst -------------------------------------------------------------------------------- /autotests/data/password_protected.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/password_protected.7z -------------------------------------------------------------------------------- /autotests/data/tar_prefix_test.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/tar_prefix_test.tar.gz -------------------------------------------------------------------------------- /autotests/data/zip_deep_hierarchy.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/zip_deep_hierarchy.zip -------------------------------------------------------------------------------- /autotests/data/7z_coder_test_deflate.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/7z_coder_test_deflate.7z -------------------------------------------------------------------------------- /autotests/data/corpus/sample.ar: -------------------------------------------------------------------------------- 1 | ! 2 | file.txt/ 1748185727 1000 1000 100644 14 ` 3 | Hello, World! 4 | -------------------------------------------------------------------------------- /autotests/data/filename_ends_in_slash.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/filename_ends_in_slash.7z -------------------------------------------------------------------------------- /autotests/data/global_header_test.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/global_header_test.tar.gz -------------------------------------------------------------------------------- /autotests/data/tar_deep_hierarchy.tar.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/tar_deep_hierarchy.tar.xz -------------------------------------------------------------------------------- /autotests/data/zip64_datadescriptor.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/zip64_datadescriptor.zip -------------------------------------------------------------------------------- /autotests/data/multiple_nameless_files.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/multiple_nameless_files.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_issue_433303801.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_issue_433303801.zip -------------------------------------------------------------------------------- /autotests/data/ossfuzz_issue_440829292.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_issue_440829292.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_issue_441906077.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_issue_441906077.7z -------------------------------------------------------------------------------- /autotests/data/tar_directory_twice.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/tar_directory_twice.tar.gz -------------------------------------------------------------------------------- /autotests/data/tar_directory_forgotten.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/tar_directory_forgotten.tar.gz -------------------------------------------------------------------------------- /autotests/data/tar_non_ascii_file_name.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/tar_non_ascii_file_name.tar.gz -------------------------------------------------------------------------------- /autotests/data/unusual_but_valid_364071.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/unusual_but_valid_364071.zip -------------------------------------------------------------------------------- /autotests/data/zip64_extra_zip64_size.zip.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/zip64_extra_zip64_size.zip.gz -------------------------------------------------------------------------------- /autotests/data/tar_emptyfile_missingdir.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/tar_emptyfile_missingdir.tar.gz -------------------------------------------------------------------------------- /autotests/data/zip64_nested_stored_streamed.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/zip64_nested_stored_streamed.zip -------------------------------------------------------------------------------- /autotests/data/zip64_end_of_central_directory.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/zip64_end_of_central_directory.zip -------------------------------------------------------------------------------- /autotests/data/zip64_extra_zip64_localheader.oxps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/zip64_extra_zip64_localheader.oxps -------------------------------------------------------------------------------- /autotests/data/zip64_extra_zip64_size_first.zip.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/zip64_extra_zip64_size_first.zip.gz -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_4566647131406336.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_4566647131406336.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_4784793343819776.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_4784793343819776.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_5082404562993152.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_5082404562993152.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_5125023070486528.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_5125023070486528.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_5132547098214400.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_5132547098214400.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_5141245598171136.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_5141245598171136.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_5161502865948672.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_5161502865948672.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_5497856632094720.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_5497856632094720.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_5503074996387840.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_5503074996387840.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_5560695602348032.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_5560695602348032.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_5581009572921344.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_5581009572921344.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_5821682553257984.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_5821682553257984.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_5991129817612288.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_5991129817612288.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_6077171694370816.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_6077171694370816.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_6096742417498112.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_6096742417498112.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_6213340184772608.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_6213340184772608.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_6248361801089024.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_6248361801089024.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_6366650283917312.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_6366650283917312.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_6532014901886976.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_6532014901886976.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_6593198198947840.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_6593198198947840.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_6597581431177216.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_6597581431177216.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_6653406484955136.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_6653406484955136.7z -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_6740459604213760.7z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_6740459604213760.7z -------------------------------------------------------------------------------- /test_package/example.cpp: -------------------------------------------------------------------------------- 1 | #include "kzip.h" 2 | #include 3 | 4 | int main() 5 | { 6 | KZip a("somefile"); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /autotests/data/ossfuzz_testcase_6581632288227328.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/ossfuzz_testcase_6581632288227328.tar.gz -------------------------------------------------------------------------------- /autotests/data/redundantDataDescriptorsNoSignature.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/redundantDataDescriptorsNoSignature.zip -------------------------------------------------------------------------------- /autotests/data/redundantDataDescriptorsWithSignature.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/redundantDataDescriptorsWithSignature.zip -------------------------------------------------------------------------------- /autotests/data/tar_relative_path_outside_archive.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/karchive/HEAD/autotests/data/tar_relative_path_outside_archive.tar.bz2 -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | #clang-format/tidy 2 | 02f0505a0f56fd0680582eb565e439117f320b10 3 | c3cce473d8e3dab1d69617c48e76755326a99017 4 | f7d760797b5bb879e666110b3659b9963cb43a3a 5 | -------------------------------------------------------------------------------- /autotests/data/dict/kar_fuzzer.dict: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Azhar Momin 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | magic="!\\n" 5 | file_magic="`\\n" 6 | -------------------------------------------------------------------------------- /REUSE.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[annotations]] 4 | path = "autotests/data/corpus/*" 5 | SPDX-FileCopyrightText = "2025 Azhar Momin " 6 | SPDX-License-Identifier = "CC0-1.0" 7 | -------------------------------------------------------------------------------- /examples/unzipper/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Unzipper) 2 | 3 | find_package(KF6Archive ${KF_VERSION} REQUIRED) 4 | 5 | add_executable(unzipper main.cpp) 6 | target_link_libraries(unzipper KF6::Archive) 7 | -------------------------------------------------------------------------------- /examples/bzip2gzip/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(BZip2GZip) 2 | 3 | find_package(KF6Archive ${KF_VERSION} REQUIRED) 4 | 5 | add_executable(bzip2gzip main.cpp) 6 | target_link_libraries(bzip2gzip KF6::Archive) 7 | -------------------------------------------------------------------------------- /examples/tarlocalfiles/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(TarLocalFiles) 2 | 3 | find_package(KF6Archive ${KF_VERSION} REQUIRED) 4 | 5 | add_executable(tarlocalfiles main.cpp) 6 | target_link_libraries(tarlocalfiles KF6::Archive) 7 | -------------------------------------------------------------------------------- /.kde-ci.yml: -------------------------------------------------------------------------------- 1 | Dependencies: 2 | - 'on': ['@all'] 3 | 'require': 4 | 'frameworks/extra-cmake-modules': '@same' 5 | 6 | Options: 7 | test-before-installing: True 8 | require-passing-tests-on: ['Linux', 'FreeBSD', 'Windows'] 9 | -------------------------------------------------------------------------------- /src/karchive.qdoc: -------------------------------------------------------------------------------- 1 | /*! 2 | \module KArchive 3 | \title KArchive C++ Classes 4 | \ingroup modules 5 | \cmakepackage KF6 6 | \cmakecomponent Archive 7 | 8 | \brief Reading, creating, and manipulating file archives. 9 | */ 10 | -------------------------------------------------------------------------------- /autotests/data/dict/ktar_lz_fuzzer.dict: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Azhar Momin 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | magic="ustar" 5 | version="00" 6 | 7 | magic="LZIP" 8 | version_old="\x00" 9 | version="\x01" 10 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Maintainers: 2 | Mario Bensi 3 | David Faure 4 | 5 | Many other contributors, see git log. 6 | 7 | For questions about this package, email kde-frameworks-devel@kde.org. 8 | 9 | For bug reports, please use https://bugs.kde.org 10 | 11 | -------------------------------------------------------------------------------- /src/Messages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Extract strings from all source files. 4 | # EXTRACT_TR_STRINGS extracts strings with lupdate and convert them to .pot with 5 | # lconvert. 6 | $EXTRACT_TR_STRINGS `find . -name \*.cpp -o -name \*.h -o -name \*.ui -o -name \*.qml` -o $podir/karchive6_qt.pot 7 | -------------------------------------------------------------------------------- /autotests/data/dict/ktar_fuzzer.dict: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Azhar Momin 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | magic="ustar" 5 | version="00" 6 | 7 | # Interesting typeflags 8 | "5" 9 | "D" 10 | "x" 11 | "g" 12 | "1" 13 | 14 | longlink="././@LongLink" 15 | -------------------------------------------------------------------------------- /test_package/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(PackageTest CXX) 2 | 3 | include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 4 | conan_basic_setup() 5 | 6 | find_package(Qt6 6.4 CONFIG REQUIRED 7 | Core 8 | ) 9 | 10 | add_executable(example example.cpp) 11 | target_link_libraries(example ${CONAN_LIBS} 12 | Qt6::Core) 13 | -------------------------------------------------------------------------------- /autotests/data/dict/ktar_bz2_fuzzer.dict: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Azhar Momin 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | magic="ustar" 5 | version="00" 6 | 7 | magic="BZ" 8 | version_bzip2="h" 9 | version_bzip1="0" 10 | 11 | compressed_magic="\x31\x41\x59\x26\x53\x59" 12 | eos_magic="\x17\x72\x45\x38\x50\x90" 13 | -------------------------------------------------------------------------------- /autotests/data/dict/kzip_fuzzer.dict: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Azhar Momin 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | cd="\x50\x4B\x01\x02" 5 | lfh="\x50\x4B\x03\x04" 6 | eocd="\x50\x4B\x05\x06" 7 | zip64_eocd"\x50\x4B\x06\x06" 8 | zip64_eocd_locator="\x50\x4B\x06\x07" 9 | dd="\x50\x4B\x07\x08" 10 | 11 | # Compression methods 12 | store="\x00" 13 | deflate="\x08" 14 | -------------------------------------------------------------------------------- /src/config-compression.h.cmake: -------------------------------------------------------------------------------- 1 | #cmakedefine01 HAVE_BZIP2_SUPPORT 2 | 3 | /* Set to 1 if the libbz2 functions need the BZ2_ prefix */ 4 | #cmakedefine01 NEED_BZ2_PREFIX 5 | 6 | /* Set to 1 if you have xz */ 7 | #cmakedefine01 HAVE_XZ_SUPPORT 8 | 9 | /* Set to 1 if you have openssl */ 10 | #cmakedefine01 HAVE_OPENSSL_SUPPORT 11 | 12 | /* Set to 1 if you have zstd */ 13 | #cmakedefine01 HAVE_ZSTD_SUPPORT 14 | 15 | -------------------------------------------------------------------------------- /metainfo.yaml: -------------------------------------------------------------------------------- 1 | maintainer: 2 | description: File compression 3 | tier: 1 4 | type: functional 5 | platforms: 6 | - name: Linux 7 | - name: FreeBSD 8 | - name: Windows 9 | - name: macOS 10 | - name: Android 11 | portingAid: false 12 | deprecated: false 13 | release: true 14 | libraries: 15 | - cmake: "KF6::Archive" 16 | cmakename: KF6Archive 17 | 18 | public_lib: true 19 | group: Frameworks 20 | subgroup: Tier 1 21 | -------------------------------------------------------------------------------- /src/kcompressiondevice_p.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2000 David Faure 3 | SPDX-FileCopyrightText: 2011 Mario Bensi 4 | 5 | SPDX-License-Identifier: LGPL-2.0-or-later 6 | */ 7 | #ifndef __kcompressiondevice_p_h 8 | #define __kcompressiondevice_p_h 9 | 10 | #define BUFFER_SIZE 8 * 1024 11 | #define SEEK_BUFFER_SIZE 3 * BUFFER_SIZE 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore the following files 2 | *~ 3 | *.diff 4 | *.kate-swp 5 | *.kdev4 6 | .kdev_include_paths 7 | *.kdevelop.pcs 8 | *.moc 9 | *.moc.cpp 10 | *.orig 11 | *.user 12 | .*.swp 13 | .swp.* 14 | Doxyfile 15 | Makefile 16 | avail 17 | random_seed 18 | /build*/ 19 | /.vscode/ 20 | CMakeLists.txt.user* 21 | *.unc-backup* 22 | .cmake/ 23 | /.clang-format 24 | /compile_commands.json 25 | .clangd 26 | .idea 27 | /cmake-build* 28 | .cache 29 | -------------------------------------------------------------------------------- /src/karchive-index.qdoc: -------------------------------------------------------------------------------- 1 | /*! 2 | \page karchive-index.html 3 | \title KArchive 4 | 5 | Reading, creating, and manipulating file archives. 6 | 7 | \section1 Using the Module 8 | 9 | \include {module-use.qdocinc} {using the c++ api} 10 | 11 | \section2 Building with CMake 12 | 13 | \include {module-use.qdocinc} {building with cmake} {KF6} {Archive} {KF6::Archive} 14 | 15 | \section1 API Reference 16 | 17 | \list 18 | \li \l{KArchive C++ Classes} 19 | \endlist 20 | */ 21 | -------------------------------------------------------------------------------- /examples/helloworld/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information see the CMake documentation: 2 | # https://cmake.org/documentation/ 3 | # https://community.kde.org/Guidelines_and_HOWTOs/CMake 4 | 5 | # Project name 6 | project(HelloWorld) 7 | 8 | # Look for the KArchive module 9 | find_package(KF6Archive ${KF_VERSION} REQUIRED) 10 | 11 | find_package(Qt6Core REQUIRED) 12 | 13 | add_executable(helloworld main.cpp) 14 | 15 | # Link our executable with the KArchive library 16 | target_link_libraries(helloworld KF6::Archive Qt6::Core) 17 | -------------------------------------------------------------------------------- /autotests/data/dict/ktar_xz_fuzzer.dict: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Azhar Momin 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | magic="ustar" 5 | version="00" 6 | 7 | magic="\x5D\x37\x7A\x58\x5A\x00" 8 | footer_magic="\x59\x5A" 9 | 10 | check_none="\x00\x00" 11 | check_crc32="\x01\x00" 12 | check_crc64="\x04\x00" 13 | check_sha256="\x0A\x00" 14 | 15 | # Filter IDs 16 | lzma2="\x21" 17 | delta="\x03" 18 | x86="\x04" 19 | powerpc="\x05" 20 | ia64="\x06" 21 | arm="\x07" 22 | armthumb="\x08" 23 | sparc="\x09" 24 | -------------------------------------------------------------------------------- /autotests/data/dict/ktar_zst_fuzzer.dict: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Azhar Momin 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | magic="ustar" 5 | version="00" 6 | 7 | magic="\x28\xB5\x2F\xFD" 8 | 9 | # Skippable Frames 10 | "\x50\x2A\x4D\x18" 11 | "\x51\x2A\x4D\x18" 12 | "\x52\x2A\x4D\x18" 13 | "\x53\x2A\x4D\x18" 14 | "\x54\x2A\x4D\x18" 15 | "\x55\x2A\x4D\x18" 16 | "\x56\x2A\x4D\x18" 17 | "\x57\x2A\x4D\x18" 18 | "\x58\x2A\x4D\x18" 19 | "\x59\x2A\x4D\x18" 20 | "\x5A\x2A\x4D\x18" 21 | "\x5B\x2A\x4D\x18" 22 | "\x5C\x2A\x4D\x18" 23 | "\x5D\x2A\x4D\x18" 24 | "\x5E\x2A\x4D\x18" 25 | "\x5F\x2A\x4D\x18" 26 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | remove_definitions(-DQT_NO_CAST_FROM_ASCII) 2 | 3 | include(ECMMarkAsTest) 4 | 5 | macro(KARCHIVE_EXECUTABLE_TESTS) 6 | foreach(_testname ${ARGN}) 7 | add_executable(${_testname} ${_testname}.cpp) 8 | target_link_libraries(${_testname} KF6::Archive) 9 | ecm_mark_as_test(${_testname}) 10 | endforeach(_testname) 11 | endmacro(KARCHIVE_EXECUTABLE_TESTS) 12 | 13 | karchive_executable_tests( 14 | kartest 15 | ktartest 16 | krcctest 17 | kziptest 18 | ) 19 | 20 | if(LIBLZMA_FOUND) 21 | karchive_executable_tests( 22 | k7ziptest 23 | ) 24 | endif() 25 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Volker Krause 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | include: 5 | - project: sysadmin/ci-utilities 6 | file: 7 | - /gitlab-templates/linux-qt6.yml 8 | - /gitlab-templates/linux-qt6-next.yml 9 | - /gitlab-templates/linux-qt6-static.yml 10 | - /gitlab-templates/android-qt6.yml 11 | - /gitlab-templates/freebsd-qt6.yml 12 | - /gitlab-templates/windows-qt6.yml 13 | - /gitlab-templates/alpine-qt6.yml 14 | - /gitlab-templates/xml-lint.yml 15 | - /gitlab-templates/yaml-lint.yml 16 | - /gitlab-templates/oss-fuzz.yml 17 | -------------------------------------------------------------------------------- /autotests/data/dict/ktar_gz_fuzzer.dict: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Azhar Momin 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | magic="ustar" 5 | version="00" 6 | 7 | magic="\x1F\x8B" 8 | 9 | deflate="\x08" 10 | 11 | # File Flags 12 | ftxt="\x01" 13 | fhcrc="\x02" 14 | fextra="\x04" 15 | fname="\x08" 16 | fcomment="\x10" 17 | fall="\x1F" 18 | 19 | # Operating System 20 | fat="\x00" 21 | amiga="\x01" 22 | vms="\x02" 23 | unix="\x03" 24 | vm_cms="\x04" 25 | atari_tos="\x05" 26 | hpfs="\x06" 27 | mac="\x07" 28 | zsystem="\x08" 29 | cp_m="\x09" 30 | tops20="\x0A" 31 | ntfs="\x0B" 32 | qdos="\x0C" 33 | acorn_riscos="\x0D" 34 | unknown="\xFF" 35 | -------------------------------------------------------------------------------- /test_package/conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, CMake 2 | import os 3 | 4 | class KArchiveTestConan(ConanFile): 5 | settings = "os", "compiler", "build_type", "arch" 6 | generators = "cmake" 7 | 8 | def build(self): 9 | cmake = CMake(self) 10 | # Current dir is "test_package/build/" and CMakeLists.txt is in "test_package" 11 | cmake.configure(source_dir=self.conanfile_directory, build_dir="./") 12 | cmake.build() 13 | 14 | def imports(self): 15 | self.copy("*.dll", dst="bin", src="bin") 16 | self.copy("*.dylib*", dst="bin", src="lib") 17 | 18 | def test(self): 19 | os.chdir("bin") 20 | self.run(".%sexample" % os.sep) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KArchive 2 | 3 | Reading, creating, and manipulating file archives 4 | 5 | ## Introduction 6 | 7 | KArchive provides classes for easy reading, creation and manipulation of 8 | "archive" formats like ZIP and TAR. 9 | 10 | It also provides transparent compression and decompression of data, like the 11 | GZip format, via a subclass of QIODevice. 12 | 13 | ## Usage 14 | 15 | If you want to read and write compressed data, just create an instance of 16 | KCompressionDevice and write to or read from that. 17 | 18 | If you want to read and write archive formats, create an instance of the 19 | appropriate subclass of KArchive (eg: K7Zip for 7-Zip files). You may need to 20 | combine this with usage of KCompressionDevice (see the API documentation for the 21 | relevant KArchive subclass for details). 22 | 23 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Here's how to build this framework: 2 | 3 | * First create a convenient folder to build the code in: 4 | 5 | mkdir build 6 | cd build 7 | 8 | * Next run CMake to create the configuration files to be used in the build: 9 | 10 | cmake .. \ 11 | -DCMAKE_BUILD_TYPE=debug \ 12 | -DCMAKE_INSTALL_PREFIX=/usr/local 13 | Various options can be passed to CMake to control how a project gets built, but these are the most common: 14 | 15 | The first line tells CMake where it can find the source code that is to be built. 16 | The second line tells CMake what type of build is required, in this example a debug build that will include useful information for when we are debugging any the software. 17 | The third line tells CMake where to install the software. 18 | 19 | * Finally compile and install 20 | 21 | make 22 | make install 23 | 24 | -------------------------------------------------------------------------------- /autotests/klimitediodevicetest.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE project 2 | SPDX-FileCopyrightText: 2009 Pino Toscano 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #ifndef KLIMITEDIODEVICETEST_H 8 | #define KLIMITEDIODEVICETEST_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | struct ChunkData { 16 | QByteArray data; 17 | int offset; 18 | }; 19 | 20 | class KLimitedIODeviceTest : public QObject 21 | { 22 | Q_OBJECT 23 | private Q_SLOTS: 24 | void initTestCase(); 25 | 26 | void testReadChunks_data(); 27 | void testReadChunks(); 28 | void testSeeking(); 29 | 30 | private: 31 | void addChunk(const QByteArray &chunk); 32 | 33 | QByteArray m_data; 34 | QBuffer m_buffer; 35 | QList m_chunks; 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /autotests/data/dict/k7z_fuzzer.dict: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Azhar Momin 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | magic="7z\xBC\xAF\x27\x1C" 5 | 6 | # Header types 7 | end="\x00" 8 | header="\x01" 9 | archive_properties="\x02" 10 | additional_streams_info="\x03" 11 | main_streams_info="\x04" 12 | files_info="\x05" 13 | pack_info="\x06" 14 | unpack_info="\x07" 15 | substreams_info="\x08" 16 | size="\x09" 17 | crc="\x0A" 18 | folder="\x0B" 19 | coders_unpack_size="\x0C" 20 | num_unpack_stream="\x0D" 21 | empty_stream="\x0E" 22 | empty_file="\x0F" 23 | anti="\x10" 24 | name="\x11" 25 | ctime="\x12" 26 | atime="\x13" 27 | mtime="\x14" 28 | attributes="\x15" 29 | comment="\x16" 30 | encoded_header="\x17" 31 | start_pos="\x18" 32 | dummy="\x19" 33 | 34 | # Method IDs 35 | lzma2="\x21" 36 | lzma="\x03\x01\x01" 37 | bcj="\x03\x03\x01\x03" 38 | bcj2="\x03\x03\x01\x1B" 39 | ppmd="\x03\x04\x01" 40 | bzip2="\x04\x02\x02" 41 | aes="\x06\xF1\x07\x01" 42 | -------------------------------------------------------------------------------- /KF6ArchiveConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(CMakeFindDependencyMacro) 4 | find_dependency(Qt6Core @REQUIRED_QT_VERSION@) 5 | 6 | 7 | set(KArchive_HAVE_ZLIB "@ZLIB_FOUND@") 8 | set(KArchive_HAVE_BZIP2 "@BZIP2_FOUND@") 9 | set(KArchive_HAVE_LZMA "@LIBLZMA_FOUND@") 10 | set(KArchive_HAVE_OPENSSL "@OpenSSL_FOUND@") 11 | set(KArchive_HAVE_ZSTD "@LibZstd_FOUND@") 12 | 13 | if (NOT @BUILD_SHARED_LIBS@) 14 | if (@ZLIB_FOUND@) 15 | find_dependency(ZLIB) 16 | endif() 17 | 18 | if (@BZIP2_FOUND@) 19 | find_dependency(BZip2) 20 | endif() 21 | 22 | if (@LIBLZMA_FOUND@) 23 | find_dependency(LibLZMA) 24 | endif() 25 | 26 | if (@OpenSSL_FOUND@) 27 | find_dependency(OpenSSL) 28 | endif() 29 | 30 | if (@LibZstd_FOUND@) 31 | find_package(PkgConfig) 32 | pkg_check_modules(LibZstd IMPORTED_TARGET "libzstd") 33 | endif() 34 | endif() 35 | 36 | include("${CMAKE_CURRENT_LIST_DIR}/KF6ArchiveTargets.cmake") 37 | -------------------------------------------------------------------------------- /src/karchive.qdocconf: -------------------------------------------------------------------------------- 1 | include($KDE_DOCS/global/qt-module-defaults.qdocconf) 2 | 3 | project = KArchive 4 | description = Reading, creating, and manipulating file archives 5 | 6 | documentationinheaders = true 7 | 8 | headerdirs += . 9 | sourcedirs += . 10 | 11 | outputformats = HTML 12 | 13 | navigation.landingpage = "KArchive" 14 | 15 | depends += \ 16 | kde \ 17 | qtcore \ 18 | qtgui 19 | 20 | qhp.projects = KArchive 21 | 22 | qhp.KArchive.file = karchive.qhp 23 | qhp.KArchive.namespace = org.kde.karchive.$QT_VERSION_TAG 24 | qhp.KArchive.virtualFolder = karchive 25 | qhp.KArchive.indexTitle = KArchive 26 | qhp.KArchive.indexRoot = 27 | 28 | qhp.KArchive.subprojects = classes 29 | qhp.KArchive.subprojects.classes.title = C++ Classes 30 | qhp.KArchive.subprojects.classes.indexTitle = KArchive C++ Classes 31 | qhp.KArchive.subprojects.classes.selectors = class fake:headerfile 32 | qhp.KArchive.subprojects.classes.sortPages = true 33 | 34 | tagfile = karchive.tags 35 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, CMake, tools 2 | 3 | class KArchiveConan(ConanFile): 4 | name = "KArchive" 5 | version = "5.37.0" 6 | license = "LGPL-2.1" 7 | url = "https://api.kde.org/frameworks/karchive/html/index.html" 8 | settings = "os", "compiler", "build_type", "arch" 9 | 10 | # build this as shared library by default, but static builds are an option 11 | options = {"shared": [True, False]} 12 | default_options = "shared=True" 13 | generators = "cmake" 14 | exports_sources = "*" 15 | 16 | def build(self): 17 | cmake = CMake(self) 18 | 19 | # change the library install dir to just "lib" as that's what Conan expects in its packages 20 | args = ['-DCMAKE_INSTALL_PREFIX="%s"' % self.package_folder, 21 | '-DKDE_INSTALL_LIBDIR=lib'] 22 | self.run('cmake %s %s %s' % (self.source_folder, cmake.command_line, " ".join(args))) 23 | self.run("cmake --build . --target install %s" % cmake.build_config) 24 | 25 | def package(self): 26 | # ideally nothing here, cmake with install takes care of it 27 | pass 28 | 29 | def package_info(self): 30 | self.cpp_info.libs = ["KF6Archive"] 31 | self.cpp_info.includedirs = ['include/KF6', 'include/KF6/KArchive'] 32 | -------------------------------------------------------------------------------- /src/klzfilter.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2025 Azhar Momin 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #ifndef KLZFILTER_H 8 | #define KLZFILTER_H 9 | 10 | #include 11 | 12 | #if HAVE_XZ_SUPPORT 13 | 14 | #include "kfilterbase.h" 15 | 16 | /** 17 | * Internal class used by KCompressionDevice 18 | * @internal 19 | */ 20 | class KLzFilter : public KFilterBase 21 | { 22 | public: 23 | KLzFilter(); 24 | ~KLzFilter() override; 25 | 26 | bool init(int mode) override; 27 | int mode() const override; 28 | bool terminate() override; 29 | void reset() override; 30 | bool readHeader() override; 31 | bool readTrailer(); 32 | bool writeHeader(const QByteArray &) override; 33 | bool writeTrailer(); 34 | void setOutBuffer(char *data, uint maxlen) override; 35 | void setInBuffer(const char *data, uint size) override; 36 | int inBufferAvailable() const override; 37 | int outBufferAvailable() const override; 38 | Result uncompress() override; 39 | Result compress(bool finish) override; 40 | 41 | private: 42 | class Private; 43 | const std::unique_ptr d; 44 | }; 45 | 46 | #endif // HAVE_XZ_SUPPORT 47 | 48 | #endif // KLZFILTER_H 49 | -------------------------------------------------------------------------------- /src/kbzip2filter.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2000 David Faure 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #ifndef __kbzip2filter__h 8 | #define __kbzip2filter__h 9 | 10 | #include 11 | 12 | #if HAVE_BZIP2_SUPPORT 13 | 14 | #include "kfilterbase.h" 15 | 16 | /*! 17 | * Internal class used by KCompressionDevice 18 | * \internal 19 | */ 20 | class KBzip2Filter : public KFilterBase 21 | { 22 | public: 23 | KBzip2Filter(); 24 | ~KBzip2Filter() override; 25 | 26 | bool init(int) override; 27 | int mode() const override; 28 | bool terminate() override; 29 | void reset() override; 30 | bool readHeader() override 31 | { 32 | return true; // bzip2 handles it by itself ! Cool ! 33 | } 34 | bool writeHeader(const QByteArray &) override 35 | { 36 | return true; 37 | } 38 | void setOutBuffer(char *data, uint maxlen) override; 39 | void setInBuffer(const char *data, uint size) override; 40 | int inBufferAvailable() const override; 41 | int outBufferAvailable() const override; 42 | Result uncompress() override; 43 | Result compress(bool finish) override; 44 | 45 | private: 46 | class Private; 47 | Private *const d; 48 | }; 49 | 50 | #endif 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/kzstdfilter.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2021 Albert Astals Cid 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #ifndef KZSTDFILTER_H 8 | #define KZSTDFILTER_H 9 | 10 | #include 11 | 12 | #if HAVE_ZSTD_SUPPORT 13 | 14 | #include "kfilterbase.h" 15 | 16 | #include 17 | 18 | /*! 19 | * Internal class used by KCompressionDevice 20 | * \internal 21 | */ 22 | class KZstdFilter : public KFilterBase 23 | { 24 | public: 25 | KZstdFilter(); 26 | ~KZstdFilter() override; 27 | 28 | bool init(int) override; 29 | int mode() const override; 30 | bool terminate() override; 31 | void reset() override; 32 | bool readHeader() override 33 | { 34 | return true; 35 | } 36 | bool writeHeader(const QByteArray &) override 37 | { 38 | return true; 39 | } 40 | void setOutBuffer(char *data, uint maxlen) override; 41 | void setInBuffer(const char *data, uint size) override; 42 | int inBufferAvailable() const override; 43 | int outBufferAvailable() const override; 44 | Result uncompress() override; 45 | Result compress(bool finish) override; 46 | 47 | private: 48 | class Private; 49 | const std::unique_ptr d; 50 | }; 51 | 52 | #endif 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/knonefilter.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2011 Mario Bensi 3 | 4 | Based on kbzip2filter: 5 | SPDX-FileCopyrightText: 2000, 2009 David Faure 6 | 7 | SPDX-License-Identifier: LGPL-2.0-or-later 8 | */ 9 | 10 | #ifndef __knonefilter__h 11 | #define __knonefilter__h 12 | 13 | #include "kfilterbase.h" 14 | 15 | /*! 16 | * Internal class used by KCompressionDevice 17 | * 18 | * This header is not installed. 19 | * 20 | * \internal 21 | */ 22 | class KNoneFilter : public KFilterBase 23 | { 24 | public: 25 | KNoneFilter(); 26 | ~KNoneFilter() override; 27 | 28 | bool init(int mode) override; 29 | int mode() const override; 30 | bool terminate() override; 31 | void reset() override; 32 | bool readHeader() override; // this is about the GZIP header 33 | bool writeHeader(const QByteArray &fileName) override; 34 | void setOutBuffer(char *data, uint maxlen) override; 35 | void setInBuffer(const char *data, uint size) override; 36 | int inBufferAvailable() const override; 37 | int outBufferAvailable() const override; 38 | Result uncompress() override; 39 | Result compress(bool finish) override; 40 | 41 | private: 42 | Result copyData(); 43 | 44 | class Private; 45 | Private *const d; 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /LICENSES/BSD-2-Clause.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) . All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 22 | USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /autotests/kfiltertest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2002-2005 David Faure 3 | * 4 | * SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | #ifndef KFILTERTEST_H 7 | #define KFILTERTEST_H 8 | 9 | #include 10 | 11 | class KFilterTest : public QObject 12 | { 13 | Q_OBJECT 14 | 15 | private Q_SLOTS: 16 | void initTestCase(); 17 | void test_block_write(); 18 | void test_block_read(); 19 | void test_biggerWrites(); 20 | void test_getch(); 21 | void test_textstream(); 22 | void test_readall(); 23 | void test_uncompressed(); 24 | void test_findFilterByMimeType_data(); 25 | void test_findFilterByMimeType(); 26 | void test_deflateWithZlibHeader(); 27 | void test_pushData(); 28 | void test_saveFile_data(); 29 | void test_saveFile(); 30 | void test_twofilesgztogether(); 31 | void test_threefilesgztogether(); 32 | 33 | private: 34 | void test_block_write(const QString &fileName, const QByteArray &data, int nTimes = 1); 35 | void test_block_read(const QString &fileName); 36 | void test_getch(const QString &fileName); 37 | void test_textstream(const QString &fileName); 38 | void test_readall(const QString &fileName, const QString &mimeType, const QByteArray &expectedData); 39 | 40 | private: 41 | QString pathgz; 42 | QString pathbz2; 43 | QString pathlz; 44 | QString pathxz; 45 | QString pathnone; 46 | QString pathzstd; 47 | QByteArray testData; 48 | }; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /autotests/kcompressiondevicetest.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE project 2 | SPDX-FileCopyrightText: 2015 Luiz Romário Santana Rios 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #ifndef KCOMPRESSIONDEVICETEST_H 8 | #define KCOMPRESSIONDEVICETEST_H 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | class QNetworkReply; 18 | 19 | class KCompressionDeviceTest : public QObject 20 | { 21 | Q_OBJECT 22 | 23 | private: 24 | QNetworkReply *getArchive(const QString &extension); 25 | QString formatExtension(KCompressionDevice::CompressionType type) const; 26 | 27 | void setDeviceToArchive(QIODevice *d, KCompressionDevice::CompressionType type); 28 | 29 | void testBufferedDevice(KCompressionDevice::CompressionType type); 30 | void testExtraction(); 31 | 32 | QNetworkAccessManager qnam; 33 | std::unique_ptr device; 34 | std::unique_ptr archive; 35 | 36 | private Q_SLOTS: 37 | void regularKTarUsage(); 38 | void testGZipBufferedDevice(); 39 | void testBZip2BufferedDevice(); 40 | void testLzBufferedDevice(); 41 | void testXzBufferedDevice(); 42 | void testZstdBufferedDevice(); 43 | 44 | void testWriteErrorOnOpen(); 45 | void testWriteErrorOnClose(); 46 | 47 | void testSeekReadUncompressedBuffer_data(); 48 | void testSeekReadUncompressedBuffer(); 49 | }; 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /autotests/ossfuzz/prepare_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | # 3 | # SPDX-FileCopyrightText: 2019 Google Inc. 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | # Based on https://github.com/google/oss-fuzz/blob/33aab4a70dc4b5811143d214536584a8c8cb3924/projects/karchive/Dockerfile 7 | # Copyright 2019 Google Inc. 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | # 21 | ################################################################################ 22 | 23 | apt-get update && \ 24 | apt-get install -y cmake make autoconf automake autopoint libtool \ 25 | wget po4a ninja-build pkgconf 26 | 27 | git clone --depth 1 https://github.com/madler/zlib.git 28 | git clone --depth 1 https://github.com/facebook/zstd.git 29 | git clone --depth 1 https://github.com/openssl/openssl.git 30 | wget https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz 31 | git clone https://github.com/tukaani-project/xz.git 32 | git clone --depth 1 -b dev git://code.qt.io/qt/qtbase.git 33 | git clone --depth 1 -b master https://invent.kde.org/frameworks/extra-cmake-modules.git 34 | -------------------------------------------------------------------------------- /src/kfilterbase.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2000-2005 David Faure 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #include "kfilterbase.h" 8 | 9 | #include 10 | 11 | class KFilterBasePrivate 12 | { 13 | public: 14 | KFilterBasePrivate() 15 | : m_flags(KFilterBase::WithHeaders) 16 | , m_dev(nullptr) 17 | , m_bAutoDel(false) 18 | { 19 | } 20 | KFilterBase::FilterFlags m_flags; 21 | QIODevice *m_dev; 22 | bool m_bAutoDel; 23 | }; 24 | 25 | KFilterBase::KFilterBase() 26 | : d(new KFilterBasePrivate) 27 | { 28 | } 29 | 30 | KFilterBase::~KFilterBase() 31 | { 32 | if (d->m_bAutoDel) { 33 | delete d->m_dev; 34 | } 35 | delete d; 36 | } 37 | 38 | void KFilterBase::setDevice(QIODevice *dev, bool autodelete) 39 | { 40 | d->m_dev = dev; 41 | d->m_bAutoDel = autodelete; 42 | } 43 | 44 | QIODevice *KFilterBase::device() 45 | { 46 | return d->m_dev; 47 | } 48 | 49 | bool KFilterBase::inBufferEmpty() const 50 | { 51 | return inBufferAvailable() == 0; 52 | } 53 | 54 | bool KFilterBase::outBufferFull() const 55 | { 56 | return outBufferAvailable() == 0; 57 | } 58 | 59 | bool KFilterBase::terminate() 60 | { 61 | return true; 62 | } 63 | 64 | void KFilterBase::reset() 65 | { 66 | } 67 | 68 | void KFilterBase::setFilterFlags(FilterFlags flags) 69 | { 70 | d->m_flags = flags; 71 | } 72 | 73 | KFilterBase::FilterFlags KFilterBase::filterFlags() const 74 | { 75 | return d->m_flags; 76 | } 77 | 78 | void KFilterBase::virtual_hook(int, void *) 79 | { 80 | /*BASE::virtual_hook( id, data );*/ 81 | } 82 | -------------------------------------------------------------------------------- /examples/unzipper/main.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE project 2 | 3 | SPDX-FileCopyrightText: 2014 Maarten De Meyer 4 | 5 | SPDX-License-Identifier: BSD-2-Clause 6 | */ 7 | 8 | /* 9 | * Unzipper 10 | * This example shows how to extract all files from a zip archive. 11 | * 12 | * api: KArchive::directory() 13 | * api: KArchiveDirectory::copyTo(QString destination, bool recursive) 14 | * 15 | * Usage: ./unzipper 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | int main(int argc, char *argv[]) 24 | { 25 | QCoreApplication app(argc, argv); 26 | QStringList args(app.arguments()); 27 | 28 | if (args.size() != 2) { 29 | // Too many or too few arguments 30 | qWarning("Usage: ./unzipper "); 31 | return 1; 32 | } 33 | 34 | QString file = args.at(1); 35 | KZip archive(file); 36 | 37 | // Open the archive 38 | if (!archive.open(QIODevice::ReadOnly)) { 39 | qWarning("Cannot open " + file.toLatin1()); 40 | qWarning("Is it a valid zip file?"); 41 | return 1; 42 | } 43 | 44 | // Take the root folder from the archive and create a KArchiveDirectory object. 45 | // KArchiveDirectory represents a directory in a KArchive. 46 | const KArchiveDirectory *root = archive.directory(); 47 | 48 | // We can extract all contents from a KArchiveDirectory to a destination. 49 | // recursive true will also extract subdirectories. 50 | QString destination = QDir::currentPath(); 51 | bool recursive = true; 52 | root->copyTo(destination, recursive); 53 | 54 | archive.close(); 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /examples/tarlocalfiles/main.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE project 2 | 3 | SPDX-FileCopyrightText: 2013 Maarten De Meyer 4 | 5 | SPDX-License-Identifier: BSD-2-Clause 6 | */ 7 | 8 | /* 9 | * TarLocalFiles 10 | * This example shows how to add local files and directories to a KArchive 11 | * 12 | * api: addLocalFile(fileName, destName) 13 | * api: addLocalDirectory(dirName, destName) 14 | * 15 | * Usage: ./tarlocalfiles 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | int main(int argc, char *argv[]) 25 | { 26 | QCoreApplication app(argc, argv); 27 | QStringList files(app.arguments()); 28 | 29 | // Create or open an archive 30 | KTar archive(QStringLiteral("myFiles.tar.gz")); 31 | 32 | // Prepare the archive for writing. 33 | if (!archive.open(QIODevice::WriteOnly)) { 34 | // Failed to open file. 35 | return 1; 36 | } 37 | 38 | if (files.size() <= 1) { 39 | // No files given. 40 | qWarning("Usage: ./tarlocalfiles "); 41 | return 1; 42 | } 43 | 44 | for (int i = 1; i < files.size(); ++i) { 45 | QFileInfo localFileOrDir(files.at(i)); 46 | 47 | if (localFileOrDir.isFile()) { 48 | QString name = localFileOrDir.fileName(); 49 | archive.addLocalFile(name, name); 50 | } else if (localFileOrDir.isDir()) { 51 | QString name = QDir(files.at(i)).dirName(); 52 | // Add this folder and all its contents 53 | archive.addLocalDirectory(name, name); 54 | } 55 | } 56 | 57 | archive.close(); 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /src/klimitediodevice_p.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2001, 2002, 2007 David Faure 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #ifndef klimitediodevice_p_h 8 | #define klimitediodevice_p_h 9 | 10 | #include 11 | #include 12 | /*! 13 | * A readonly device that reads from an underlying device 14 | * from a given point to another (e.g. to give access to a single 15 | * file inside an archive). 16 | * @author David Faure 17 | * \internal - used by KArchive 18 | */ 19 | class KLimitedIODevice : public QIODevice 20 | { 21 | Q_OBJECT 22 | public: 23 | /*! 24 | * Creates a new KLimitedIODevice. 25 | * \a dev the underlying device, opened or not 26 | * This device itself auto-opens (in readonly mode), no need to open it. 27 | * \a start where to start reading (position in bytes) 28 | * \a length the length of the data to read (in bytes) 29 | */ 30 | KLimitedIODevice(QIODevice *dev, qint64 start, qint64 length); 31 | ~KLimitedIODevice() override 32 | { 33 | } 34 | 35 | bool isSequential() const override; 36 | 37 | [[nodiscard]] bool open(QIODevice::OpenMode m) override; 38 | void close() override; 39 | 40 | qint64 size() const override; 41 | 42 | qint64 readData(char *data, qint64 maxlen) override; 43 | qint64 writeData(const char *, qint64) override 44 | { 45 | return -1; // unsupported 46 | } 47 | 48 | // virtual qint64 pos() const { return m_dev->pos() - m_start; } 49 | bool seek(qint64 pos) override; 50 | qint64 bytesAvailable() const override; 51 | 52 | private: 53 | QIODevice *m_dev; 54 | qint64 m_start; 55 | qint64 m_length; 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/kxzfilter.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2007-2008 Per Øyvind Karlsen 3 | 4 | Based on kbzip2filter: 5 | SPDX-FileCopyrightText: 2000 David Faure 6 | 7 | SPDX-License-Identifier: LGPL-2.0-or-later 8 | */ 9 | 10 | #ifndef KXZFILTER_H 11 | #define KXZFILTER_H 12 | 13 | #include 14 | 15 | #if HAVE_XZ_SUPPORT 16 | 17 | #include "kfilterbase.h" 18 | 19 | /*! 20 | * Internal class used by KCompressionDevice 21 | * \internal 22 | */ 23 | class KXzFilter : public KFilterBase 24 | { 25 | public: 26 | KXzFilter(); 27 | ~KXzFilter() override; 28 | 29 | bool init(int) override; 30 | 31 | enum Flag { 32 | AUTO = 0, 33 | LZMA = 1, 34 | LZMA2 = 2, 35 | BCJ = 3, // X86 36 | POWERPC = 4, 37 | IA64 = 5, 38 | ARM = 6, 39 | ARMTHUMB = 7, 40 | SPARC = 8, 41 | }; 42 | 43 | virtual bool init(int, Flag flag, const QList &props); 44 | int mode() const override; 45 | bool terminate() override; 46 | void reset() override; 47 | bool readHeader() override 48 | { 49 | return true; // lzma handles it by itself ! Cool ! 50 | } 51 | bool writeHeader(const QByteArray &) override 52 | { 53 | return true; 54 | } 55 | void setOutBuffer(char *data, uint maxlen) override; 56 | void setInBuffer(const char *data, uint size) override; 57 | int inBufferAvailable() const override; 58 | int outBufferAvailable() const override; 59 | Result uncompress() override; 60 | Result compress(bool finish) override; 61 | 62 | private: 63 | class Private; 64 | Private *const d; 65 | }; 66 | 67 | #endif 68 | 69 | #endif // KXZFILTER_H 70 | -------------------------------------------------------------------------------- /autotests/ossfuzz/kcompressiondevice_fuzzer.cc: -------------------------------------------------------------------------------- 1 | /* 2 | # SPDX-FileCopyrightText: 2025 Google LLC 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Copyright 2025 Google LLC 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | ################################################################################ 20 | */ 21 | 22 | /* 23 | Usage: 24 | python infra/helper.py build_image karchive 25 | python infra/helper.py build_fuzzers --sanitizer undefined|address|memory karchive 26 | python infra/helper.py run_fuzzer karchive ktar_[gz|bz2|xz|zst|lz]_fuzzer 27 | */ 28 | #ifndef DEVICE 29 | #error "DEVICE is not defined" 30 | #endif 31 | 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | 38 | #include "karchive_fuzzer_common.h" 39 | 40 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) 41 | { 42 | int argc = 0; 43 | QCoreApplication a(argc, nullptr); 44 | 45 | QBuffer b; 46 | b.setData((const char *)data, size); 47 | 48 | KCompressionDevice kd(&b, false, KCompressionDevice::DEVICE); 49 | KTar ktar(&kd); 50 | 51 | if (ktar.open(QIODevice::ReadOnly)) { 52 | traverseArchive(ktar.directory()); 53 | ktar.close(); 54 | } 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /src/karchive_p.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2000-2005 David Faure 3 | SPDX-FileCopyrightText: 2003 Leo Savernik 4 | 5 | SPDX-License-Identifier: LGPL-2.0-or-later 6 | */ 7 | 8 | #ifndef KARCHIVE_P_H 9 | #define KARCHIVE_P_H 10 | 11 | #include "karchive.h" 12 | 13 | #include 14 | 15 | // Documentation says that QByteArray should be able to hold up to 2^63 on 64 bit platforms 16 | // but practice says it aborts with something like 2314885530818453536, so go with MAX_INT for now 17 | static constexpr int kMaxQByteArraySize = std::numeric_limits::max() - 32; 18 | 19 | class KArchivePrivate 20 | { 21 | Q_DECLARE_TR_FUNCTIONS(KArchivePrivate) 22 | 23 | public: 24 | KArchivePrivate(KArchive *parent) 25 | : q(parent) 26 | { 27 | } 28 | ~KArchivePrivate() 29 | { 30 | if (deviceOwned) { 31 | delete dev; // we created it ourselves in open() 32 | dev = nullptr; 33 | } 34 | 35 | delete rootDir; 36 | } 37 | 38 | KArchivePrivate(const KArchivePrivate &) = delete; 39 | KArchivePrivate &operator=(const KArchivePrivate &) = delete; 40 | 41 | static bool hasRootDir(KArchive *archive) 42 | { 43 | return archive->d->rootDir; 44 | } 45 | 46 | void abortWriting(); 47 | 48 | static QDateTime time_tToDateTime(uint seconds); 49 | 50 | KArchiveDirectory *findOrCreateDirectory(const QStringView path); 51 | 52 | KArchive *q = nullptr; 53 | KArchiveDirectory *rootDir = nullptr; 54 | std::unique_ptr saveFile; 55 | QIODevice *dev = nullptr; 56 | QString fileName; 57 | QIODevice::OpenMode mode = QIODevice::NotOpen; 58 | bool deviceOwned = false; // if true, we (KArchive) own dev and must delete it 59 | QString errorStr{tr("Unknown error")}; 60 | }; 61 | 62 | #endif // KARCHIVE_P_H 63 | -------------------------------------------------------------------------------- /autotests/ossfuzz/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Azhar Momin 2 | # SPDX-License-Identifier: BSD-2-Clause 3 | 4 | if(DEFINED ENV{LIB_FUZZING_ENGINE}) 5 | set(fuzzing_engine $ENV{LIB_FUZZING_ENGINE}) 6 | else() 7 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 8 | set(fuzzing_engine -fsanitize=fuzzer) 9 | else() 10 | message(FATAL_ERROR "Fuzzing engine not supported") 11 | endif() 12 | endif() 13 | 14 | function(add_fuzzer target_name source_file define_name define_value) 15 | add_executable(${target_name} ${source_file}) 16 | 17 | target_compile_definitions( 18 | ${target_name} 19 | PRIVATE ${define_name}=${define_value} 20 | ) 21 | 22 | target_link_libraries( 23 | ${target_name} 24 | PRIVATE KF6Archive ${fuzzing_engine} 25 | ) 26 | 27 | set_target_properties( 28 | ${target_name} PROPERTIES 29 | RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/fuzzers 30 | ) 31 | endfunction() 32 | 33 | 34 | if(LIBLZMA_FOUND) 35 | add_fuzzer(k7z_fuzzer karchive_fuzzer.cc HANDLER K7Zip) 36 | if(OpenSSL_FOUND) 37 | target_compile_definitions(k7z_fuzzer PRIVATE USE_PASSWORD=1) 38 | endif() 39 | endif() 40 | 41 | add_fuzzer(kar_fuzzer karchive_fuzzer.cc HANDLER KAr) 42 | add_fuzzer(ktar_fuzzer karchive_fuzzer.cc HANDLER KTar) 43 | add_fuzzer(kzip_fuzzer karchive_fuzzer.cc HANDLER KZip) 44 | 45 | if(BZIP2_FOUND) 46 | add_fuzzer(ktar_bz2_fuzzer kcompressiondevice_fuzzer.cc DEVICE BZip2) 47 | endif() 48 | 49 | add_fuzzer(ktar_gz_fuzzer kcompressiondevice_fuzzer.cc DEVICE GZip) 50 | 51 | if(LIBLZMA_FOUND) 52 | add_fuzzer(ktar_lz_fuzzer kcompressiondevice_fuzzer.cc DEVICE Lz) 53 | add_fuzzer(ktar_xz_fuzzer kcompressiondevice_fuzzer.cc DEVICE Xz) 54 | endif() 55 | 56 | if(LibZstd_FOUND) 57 | add_fuzzer(ktar_zst_fuzzer kcompressiondevice_fuzzer.cc DEVICE Zstd) 58 | endif() 59 | -------------------------------------------------------------------------------- /src/kgzipfilter.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2000, 2009 David Faure 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #ifndef __kgzipfilter__h 8 | #define __kgzipfilter__h 9 | 10 | #include "kfilterbase.h" 11 | 12 | /*! 13 | * Internal class used by KCompressionDevice 14 | * 15 | * This header is not installed. 16 | * 17 | * \internal 18 | */ 19 | class KGzipFilter : public KFilterBase 20 | { 21 | public: 22 | KGzipFilter(); 23 | ~KGzipFilter() override; 24 | 25 | bool init(int mode) override; 26 | 27 | // The top of zlib.h explains it: there are three cases. 28 | // - Raw deflate, no header (e.g. inside a ZIP file) 29 | // - Thin zlib header (1) (which is normally what HTTP calls "deflate" (2)) 30 | // - Gzip header, implemented here by readHeader 31 | // 32 | // (1) as written out by compress()/compress2() 33 | // (2) see https://www.zlib.net/zlib_faq.html#faq39 34 | enum Flag { 35 | RawDeflate = 0, // raw deflate data 36 | ZlibHeader = 1, // zlib headers (HTTP deflate) 37 | GZipHeader = 2, 38 | }; 39 | bool init(int mode, Flag flag); // for direct users of KGzipFilter 40 | int mode() const override; 41 | bool terminate() override; 42 | void reset() override; 43 | bool readHeader() override; // this is about the GZIP header 44 | bool writeHeader(const QByteArray &fileName) override; 45 | void writeFooter(); 46 | void setOutBuffer(char *data, uint maxlen) override; 47 | void setInBuffer(const char *data, uint size) override; 48 | int inBufferAvailable() const override; 49 | int outBufferAvailable() const override; 50 | Result uncompress() override; 51 | Result compress(bool finish) override; 52 | 53 | private: 54 | Result uncompress_noop(); 55 | class Private; 56 | Private *const d; 57 | }; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /autotests/klimitediodevicetest.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE project 2 | SPDX-FileCopyrightText: 2009 Pino Toscano 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #include "klimitediodevicetest.h" 8 | 9 | #include "klimitediodevice_p.h" 10 | 11 | #include 12 | 13 | QTEST_MAIN(KLimitedIODeviceTest) 14 | 15 | void KLimitedIODeviceTest::addChunk(const QByteArray &chunk) 16 | { 17 | ChunkData cd; 18 | cd.data = chunk; 19 | cd.offset = m_chunks.isEmpty() ? 0 : m_chunks.last().offset + m_chunks.last().data.size(); 20 | m_chunks.append(cd); 21 | m_data.append(chunk); 22 | } 23 | 24 | void KLimitedIODeviceTest::initTestCase() 25 | { 26 | addChunk("Test of string"); 27 | addChunk("second part of the large buffer"); 28 | addChunk("... which will be used to test the KLimitedIODevice"); 29 | 30 | m_buffer.setBuffer(&m_data); 31 | m_buffer.open(QIODevice::ReadOnly); 32 | } 33 | 34 | void KLimitedIODeviceTest::testReadChunks_data() 35 | { 36 | QTest::addColumn("index"); 37 | 38 | for (int i = 0; i < m_chunks.count(); ++i) { 39 | const ChunkData &d = m_chunks.at(i); 40 | QTest::newRow(d.data.constData()) << i; 41 | } 42 | } 43 | 44 | void KLimitedIODeviceTest::testReadChunks() 45 | { 46 | QFETCH(int, index); 47 | 48 | const ChunkData &chunk = m_chunks.at(index); 49 | 50 | KLimitedIODevice dev(&m_buffer, chunk.offset, chunk.data.size()); 51 | QVERIFY(dev.isOpen()); 52 | QCOMPARE(dev.readAll(), chunk.data); 53 | } 54 | 55 | void KLimitedIODeviceTest::testSeeking() 56 | { 57 | const ChunkData &chunk = m_chunks.at(2); 58 | 59 | KLimitedIODevice dev(&m_buffer, chunk.offset, chunk.data.size()); 60 | QVERIFY(dev.seek(dev.size() - 16)); 61 | QCOMPARE(dev.readAll(), chunk.data.right(16)); 62 | QVERIFY(dev.seek(0)); 63 | QCOMPARE(dev.readAll(), chunk.data); 64 | } 65 | 66 | #include "moc_klimitediodevicetest.cpp" 67 | -------------------------------------------------------------------------------- /tests/kartest.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE project 2 | SPDX-FileCopyrightText: 2002-2019 David Faure 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #include "kar.h" 8 | 9 | #include 10 | 11 | #include 12 | 13 | void recursive_print(const KArchiveDirectory *dir, const QString &path) 14 | { 15 | QStringList l = dir->entries(); 16 | l.sort(); 17 | QStringList::ConstIterator it = l.constBegin(); 18 | for (; it != l.constEnd(); ++it) { 19 | const KArchiveEntry *entry = dir->entry((*it)); 20 | printf("mode=%7o path=%s type=%s size=%lld\n", 21 | entry->permissions(), 22 | qPrintable(path + (*it)), 23 | entry->isFile() ? "file" : "dir", 24 | entry->isFile() ? static_cast(entry)->size() : 0); 25 | if (!entry->symLinkTarget().isEmpty()) { 26 | printf(" (symlink to %s)\n", qPrintable(entry->symLinkTarget())); 27 | } 28 | if (entry->isDirectory()) { 29 | recursive_print((KArchiveDirectory *)entry, path + (*it) + '/'); 30 | } 31 | } 32 | } 33 | 34 | // See karchivetest.cpp for the unittest that covers KAr. 35 | 36 | int main(int argc, char **argv) 37 | { 38 | if (argc != 2) { 39 | printf( 40 | "\n" 41 | " Usage :\n" 42 | " ./kartest /path/to/existing_file.a tests listing an existing archive\n"); 43 | return 1; 44 | } 45 | 46 | KAr archive(argv[1]); 47 | 48 | if (!archive.open(QIODevice::ReadOnly)) { 49 | printf("Could not open %s for reading\n", argv[1]); 50 | return 1; 51 | } 52 | 53 | const KArchiveDirectory *dir = archive.directory(); 54 | 55 | // printf("calling recursive_print\n"); 56 | recursive_print(dir, QLatin1String("")); 57 | // printf("recursive_print called\n"); 58 | 59 | archive.close(); 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /examples/helloworld/main.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE project 2 | 3 | SPDX-FileCopyrightText: 2013 Maarten De Meyer 4 | 5 | SPDX-License-Identifier: BSD-2-Clause 6 | */ 7 | 8 | /* 9 | * HelloWorld 10 | * 11 | * Example to show very basic usage of KArchive with CMake 12 | * 13 | * Usage: 14 | * mkdir build && cd build 15 | * cmake .. 16 | * make 17 | * ./helloworld 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | int main() 24 | { 25 | //@@snippet_begin(helloworld) 26 | // Create a zip archive 27 | KZip archive(QStringLiteral("hello.zip")); 28 | 29 | // Open our archive for writing 30 | if (archive.open(QIODevice::WriteOnly)) { 31 | // The archive is open, we can now write data 32 | archive.writeFile(QStringLiteral("world"), // File name 33 | QByteArray("The whole world inside a hello."), // Data 34 | 0100644, // Permissions 35 | QStringLiteral("owner"), // Owner 36 | QStringLiteral("users")); // Group 37 | 38 | // Don't forget to close! 39 | archive.close(); 40 | } 41 | 42 | if (archive.open(QIODevice::ReadOnly)) { 43 | const KArchiveDirectory *dir = archive.directory(); 44 | 45 | const KArchiveEntry *e = dir->entry("world"); 46 | if (!e) { 47 | qDebug() << "File not found!"; 48 | return -1; 49 | } 50 | const KArchiveFile *f = static_cast(e); 51 | QByteArray arr(f->data()); 52 | qDebug() << arr; // the file contents 53 | 54 | // To avoid reading everything into memory in one go, we can use createDevice() instead 55 | QIODevice *dev = f->createDevice(); 56 | while (!dev->atEnd()) { 57 | qDebug() << dev->readLine(); 58 | } 59 | delete dev; 60 | } 61 | //@@snippet_end 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /tests/krcctest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2002-2014 David Faure 3 | * 4 | * SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #include "krcc.h" 8 | 9 | #include 10 | 11 | #include 12 | 13 | void recursive_print(const KArchiveDirectory *dir, const QString &path) 14 | { 15 | QStringList l = dir->entries(); 16 | l.sort(); 17 | QStringList::ConstIterator it = l.constBegin(); 18 | for (; it != l.constEnd(); ++it) { 19 | const KArchiveEntry *entry = dir->entry((*it)); 20 | printf("mode=%07o %s %s %s%s %lld isdir=%d\n", 21 | entry->permissions(), 22 | entry->user().toLatin1().constData(), 23 | entry->group().toLatin1().constData(), 24 | path.toLatin1().constData(), 25 | (*it).toLatin1().constData(), 26 | entry->isFile() ? static_cast(entry)->size() : 0, 27 | entry->isDirectory()); 28 | if (!entry->symLinkTarget().isEmpty()) { 29 | printf(" (symlink to %s)\n", qPrintable(entry->symLinkTarget())); 30 | } 31 | if (entry->isDirectory()) { 32 | recursive_print((KArchiveDirectory *)entry, path + (*it) + '/'); 33 | } 34 | } 35 | } 36 | 37 | // See karchivetest.cpp for the unittest that covers KTar. 38 | 39 | int main(int argc, char **argv) 40 | { 41 | if (argc != 2) { 42 | printf( 43 | "\n" 44 | " Usage :\n" 45 | " ./ktartest /path/to/existing_file.tar.gz tests listing an existing tar.gz\n"); 46 | return 1; 47 | } 48 | 49 | KRcc rcc(argv[1]); 50 | 51 | if (!rcc.open(QIODevice::ReadOnly)) { 52 | printf("Could not open %s for reading\n", argv[1]); 53 | return 1; 54 | } 55 | 56 | const KArchiveDirectory *dir = rcc.directory(); 57 | 58 | // printf("calling recursive_print\n"); 59 | recursive_print(dir, QLatin1String("")); 60 | // printf("recursive_print called\n"); 61 | 62 | rcc.close(); 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /tests/ktartest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2002-2005 David Faure 3 | * 4 | * SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #include "ktar.h" 8 | 9 | #include 10 | 11 | #include 12 | 13 | void recursive_print(const KArchiveDirectory *dir, const QString &path) 14 | { 15 | QStringList l = dir->entries(); 16 | l.sort(); 17 | QStringList::ConstIterator it = l.constBegin(); 18 | for (; it != l.constEnd(); ++it) { 19 | const KArchiveEntry *entry = dir->entry((*it)); 20 | printf("mode=%07o %s %s %s%s %lld isdir=%d\n", 21 | entry->permissions(), 22 | entry->user().toLatin1().constData(), 23 | entry->group().toLatin1().constData(), 24 | path.toLatin1().constData(), 25 | (*it).toLatin1().constData(), 26 | entry->isFile() ? static_cast(entry)->size() : 0, 27 | entry->isDirectory()); 28 | if (!entry->symLinkTarget().isEmpty()) { 29 | printf(" (symlink to %s)\n", qPrintable(entry->symLinkTarget())); 30 | } 31 | if (entry->isDirectory()) { 32 | recursive_print((KArchiveDirectory *)entry, path + (*it) + '/'); 33 | } 34 | } 35 | } 36 | 37 | // See karchivetest.cpp for the unittest that covers KTar. 38 | 39 | int main(int argc, char **argv) 40 | { 41 | if (argc != 2) { 42 | printf( 43 | "\n" 44 | " Usage :\n" 45 | " ./ktartest /path/to/existing_file.tar.gz tests listing an existing tar.gz\n"); 46 | return 1; 47 | } 48 | 49 | KTar tar(argv[1]); 50 | 51 | if (!tar.open(QIODevice::ReadOnly)) { 52 | printf("Could not open %s for reading\n", argv[1]); 53 | return 1; 54 | } 55 | 56 | const KArchiveDirectory *dir = tar.directory(); 57 | 58 | // printf("calling recursive_print\n"); 59 | recursive_print(dir, QLatin1String("")); 60 | // printf("recursive_print called\n"); 61 | 62 | tar.close(); 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /autotests/ossfuzz/karchive_fuzzer.cc: -------------------------------------------------------------------------------- 1 | /* 2 | # SPDX-FileCopyrightText: 2019 Google Inc. 3 | # SPDX-FileCopyrightText: 2025 Azhar Momin 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | # Copyright 2019 Google Inc. 7 | # Copyright 2025 Azhar Momin 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | # 21 | ################################################################################ 22 | */ 23 | 24 | /* 25 | Usage: 26 | python infra/helper.py build_image karchive 27 | python infra/helper.py build_fuzzers --sanitizer undefined|address|memory karchive 28 | python infra/helper.py run_fuzzer karchive k[ar|tar|zip|7z]_fuzzer 29 | */ 30 | 31 | #ifndef HANDLER 32 | #error "HANDLER is not defined" 33 | #endif 34 | 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "karchive_fuzzer_common.h" 44 | 45 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) 46 | { 47 | int argc = 0; 48 | QCoreApplication a(argc, nullptr); 49 | 50 | QBuffer b; 51 | b.setData((const char *)data, size); 52 | 53 | HANDLER handler(&b); 54 | 55 | #ifdef USE_PASSWORD 56 | // Set a dummy password to force decryption 57 | handler.setPassword(QStringLiteral("youshallnotpass")); 58 | #endif 59 | 60 | if (handler.open(QIODevice::ReadOnly)) { 61 | traverseArchive(handler.directory()); 62 | handler.close(); 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /examples/bzip2gzip/main.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE project 2 | 3 | SPDX-FileCopyrightText: 2014 Maarten De Meyer 4 | 5 | SPDX-License-Identifier: BSD-2-Clause 6 | */ 7 | 8 | /* 9 | * bzip2gzip 10 | * This example shows the usage of KCompressionDevice. 11 | * It converts BZip2 files to GZip archives. 12 | * 13 | * api: KCompressionDevice(QIODevice * inputDevice, bool autoDeleteInputDevice, CompressionType type) 14 | * api: KCompressionDevice(const QString & fileName, CompressionType type) 15 | * api: QIODevice::readAll() 16 | * api: QIODevice::read(qint64 maxSize) 17 | * api: QIODevice::write(const QByteArray &data) 18 | * 19 | * Usage: ./bzip2gzip 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | int main(int argc, char *argv[]) 30 | { 31 | QCoreApplication app(argc, argv); 32 | QStringList args(app.arguments()); 33 | 34 | if (args.size() != 2) { 35 | qWarning("Usage: ./bzip2gzip "); 36 | return 1; 37 | } 38 | 39 | QString inputFile = args.at(1); 40 | QFile file(inputFile); 41 | QFileInfo info(inputFile); 42 | 43 | if (info.suffix() != QLatin1String("bz2")) { 44 | qCritical("Error: not a valid BZip2 file!"); 45 | return 1; 46 | } 47 | 48 | //@@snippet_begin(kcompressiondevice_example) 49 | // Open the input archive 50 | KCompressionDevice input(&file, false, KCompressionDevice::BZip2); 51 | input.open(QIODevice::ReadOnly); 52 | 53 | QString outputFile = (info.completeBaseName() + QLatin1String(".gz")); 54 | 55 | // Open the new output file 56 | KCompressionDevice output(outputFile, KCompressionDevice::GZip); 57 | output.open(QIODevice::WriteOnly); 58 | 59 | while (!input.atEnd()) { 60 | // Read and uncompress the data 61 | QByteArray data = input.read(512); 62 | 63 | // Write data like you would to any other QIODevice 64 | output.write(data); 65 | } 66 | 67 | input.close(); 68 | output.close(); 69 | //@@snippet_end 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /autotests/ossfuzz/karchive_fuzzer_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | # SPDX-FileCopyrightText: 2025 Google LLC 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Copyright 2025 Google LLC 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | ################################################################################ 20 | */ 21 | 22 | #include 23 | 24 | inline void traverseArchive(const KArchiveDirectory *dir, const QString &path = QString()) 25 | { 26 | for (const auto &entryName : dir->entries()) { 27 | auto entry = dir->entry(entryName); 28 | 29 | if (entry->isFile()) { 30 | auto file = static_cast(entry); 31 | QIODevice *device = file->createDevice(); 32 | if (device) { 33 | static const qint64 chunkSize = 1024 * 1024 * 100; 34 | qint64 remainingSize = device->size(); 35 | QByteArray array; 36 | array.resize(int(qMin(chunkSize, remainingSize))); 37 | while (remainingSize > 0) { 38 | const qint64 currentChunkSize = qMin(chunkSize, remainingSize); 39 | const qint64 n = device->read(array.data(), currentChunkSize); 40 | if (n != currentChunkSize) { 41 | delete device; 42 | return; 43 | } 44 | remainingSize -= currentChunkSize; 45 | } 46 | delete device; 47 | } 48 | } else if (entry->isDirectory()) { 49 | auto subDir = static_cast(entry); 50 | traverseArchive(subDir, path + QString::fromUtf8("/") + entryName); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/klimitediodevice.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2001, 2002, 2007 David Faure 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #include "klimitediodevice_p.h" 8 | #include "loggingcategory.h" 9 | 10 | #ifdef TEST_MODE 11 | #define WARNING qWarning() 12 | #else 13 | #define WARNING qCWarning(KArchiveLog) 14 | #endif 15 | 16 | KLimitedIODevice::KLimitedIODevice(QIODevice *dev, qint64 start, qint64 length) 17 | : m_dev(dev) 18 | , m_start(start) 19 | , m_length(length) 20 | { 21 | // qCDebug(KArchiveLog) << "start=" << start << "length=" << length; 22 | 23 | const bool res = open(QIODevice::ReadOnly); // krazy:exclude=syscalls 24 | 25 | // KLimitedIODevice always returns true 26 | Q_ASSERT(res); 27 | 28 | if(!res) { 29 | WARNING << "failed to open LimitedIO device for reading."; 30 | } 31 | } 32 | 33 | bool KLimitedIODevice::open(QIODevice::OpenMode m) 34 | { 35 | // qCDebug(KArchiveLog) << "m=" << m; 36 | if (m & QIODevice::ReadOnly) { 37 | /*bool ok = false; 38 | if ( m_dev->isOpen() ) 39 | ok = ( m_dev->mode() == QIODevice::ReadOnly ); 40 | else 41 | ok = m_dev->open( m ); 42 | if ( ok )*/ 43 | m_dev->seek(m_start); // No concurrent access ! 44 | } else { 45 | WARNING << "KLimitedIODevice::open only supports QIODevice::ReadOnly!"; 46 | } 47 | setOpenMode(QIODevice::ReadOnly); 48 | return true; 49 | } 50 | 51 | void KLimitedIODevice::close() 52 | { 53 | } 54 | 55 | qint64 KLimitedIODevice::size() const 56 | { 57 | return m_length; 58 | } 59 | 60 | qint64 KLimitedIODevice::readData(char *data, qint64 maxlen) 61 | { 62 | maxlen = qMin(maxlen, m_length - pos()); // Apply upper limit 63 | return m_dev->read(data, maxlen); 64 | } 65 | 66 | bool KLimitedIODevice::seek(qint64 pos) 67 | { 68 | Q_ASSERT(pos <= m_length); 69 | pos = qMin(pos, m_length); // Apply upper limit 70 | bool ret = m_dev->seek(m_start + pos); 71 | if (ret) { 72 | QIODevice::seek(pos); 73 | } 74 | return ret; 75 | } 76 | 77 | qint64 KLimitedIODevice::bytesAvailable() const 78 | { 79 | return QIODevice::bytesAvailable(); 80 | } 81 | 82 | bool KLimitedIODevice::isSequential() const 83 | { 84 | return m_dev->isSequential(); 85 | } 86 | 87 | #include "moc_klimitediodevice_p.cpp" 88 | -------------------------------------------------------------------------------- /autotests/ossfuzz/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # KArchive OSS-Fuzz Integration 5 | 6 | ## Fuzzing Locally 7 | Make sure you're using Clang (by setting `CC` and `CXX`), since fuzzing requires it. Then build KArchive with `BUILD_FUZZERS=ON` to generate the fuzzer binaries: 8 | ```sh 9 | cmake -B build -DBUILD_FUZZERS=ON 10 | cmake --build build 11 | # Then run one of the fuzzer binaries: 12 | ./build/bin/fuzzers/kzip_fuzzer 13 | ``` 14 | 15 | ## Testing OSS-Fuzz Integration 16 | Testing OSS-Fuzz integration requires: Python & Docker 17 | 18 | First clone the OSS-Fuzz repository: 19 | ```sh 20 | git clone https://github.com/google/oss-fuzz.git 21 | ``` 22 | 23 | After navigating to the cloned repository, run the following command to build the fuzzers: 24 | ```sh 25 | python3 infra/helper.py build_image karchive 26 | python3 infra/helper.py build_fuzzers --sanitizer address karchive 27 | ``` 28 | 29 | This may take a while since it builds the whole QtBase dependency alongside with zlib, libzstd, liblzma, libcrypto, etc and KArchive itself. Once the build is completed, you can run the fuzzers using the following command: 30 | ```sh 31 | python3 infra/helper.py run_fuzzer karchive kzip_fuzzer 32 | ``` 33 | 34 | The code for preparing the build lives in the `prepare_build.sh` script and the code for building the fuzzers lives in the `build_fuzzers.sh` script (which is also responsible for building the dependencies, creating the seed corpus and copying the dict file). 35 | 36 | For more information on OSS-Fuzz, visit the [official website](https://google.github.io/oss-fuzz/). 37 | 38 | ## Integrating New Fuzzers 39 | 40 | When you add a new archive format or compression device, you need to add a corresponding fuzzer for it. KArchive uses two main fuzzer types: 41 | - `karchive_fuzzer.cc` for archive formats (like zip, tar, etc) 42 | - `kcompressiondevice_fuzzer.cc` for compression devices (used through `KTar`) 43 | 44 | ### If You Are Adding a New Archive Format: 45 | - Update `CMakeLists.txt` to include the new fuzzer, following the pattern of existing ones 46 | - If the format needs extra dependencies, update `prepare_build.sh` accordingly 47 | - Update `build_fuzzers.sh` to build those dependencies and include the new fuzzer 48 | 49 | ### If You Are Adding a New Compression Device: 50 | - Do the same as above, but use `kcompressiondevice_fuzzer.cc` instead of `karchive_fuzzer.cc` 51 | -------------------------------------------------------------------------------- /tests/k7ziptest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2011 Mario Bensi 3 | * 4 | * SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #include "k7zip.h" 8 | 9 | #include 10 | 11 | #include 12 | 13 | void recursive_print(const KArchiveDirectory *dir, const QString &path) 14 | { 15 | QStringList l = dir->entries(); 16 | l.sort(); 17 | QStringList::ConstIterator it = l.constBegin(); 18 | for (; it != l.constEnd(); ++it) { 19 | const KArchiveEntry *entry = dir->entry((*it)); 20 | printf("mode=%07o %s %s %s %s%s %lld isdir=%d\n", 21 | entry->permissions(), 22 | entry->date().toString(QStringLiteral("yyyy-MM-dd hh:mm:ss")).toLatin1().constData(), 23 | entry->user().toLatin1().constData(), 24 | entry->group().toLatin1().constData(), 25 | path.toLatin1().constData(), 26 | (*it).toLatin1().constData(), 27 | entry->isFile() ? static_cast(entry)->size() : 0, 28 | entry->isDirectory()); 29 | if (!entry->symLinkTarget().isEmpty()) { 30 | printf(" (symlink to %s)\n", qPrintable(entry->symLinkTarget())); 31 | } 32 | if (entry->isDirectory()) { 33 | recursive_print((KArchiveDirectory *)entry, path + (*it) + '/'); 34 | } 35 | if (entry->isFile()) { 36 | const KArchiveFile *f = static_cast(entry); 37 | QByteArray arr(f->data()); 38 | qDebug() << "data" << arr; 39 | 40 | QIODevice *dev = f->createDevice(); 41 | QByteArray contents = dev->readAll(); 42 | qDebug() << "contents" << contents; 43 | delete dev; 44 | } 45 | } 46 | } 47 | 48 | // See karchivetest.cpp for the unittest that covers K7Zip. 49 | 50 | int main(int argc, char **argv) 51 | { 52 | if (argc != 2) { 53 | printf( 54 | "\n" 55 | " Usage :\n" 56 | " ./k7ziptest /path/to/existing_file.7z tests listing an existing .7z\n"); 57 | return 1; 58 | } 59 | 60 | K7Zip k7z(argv[1]); 61 | 62 | if (!k7z.open(QIODevice::ReadOnly)) { 63 | printf("Could not open %s for reading\n", argv[1]); 64 | return 1; 65 | } 66 | 67 | const KArchiveDirectory *dir = k7z.directory(); 68 | 69 | // printf("calling recursive_print\n"); 70 | recursive_print(dir, QLatin1String("")); 71 | // printf("recursive_print called\n"); 72 | 73 | k7z.close(); 74 | 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /src/knonefilter.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2011 Mario Bensi 3 | 4 | Based on kbzip2filter: 5 | SPDX-FileCopyrightText: 2000, 2009 David Faure 6 | 7 | SPDX-License-Identifier: LGPL-2.0-or-later 8 | */ 9 | 10 | #include "knonefilter.h" 11 | 12 | #include 13 | 14 | class Q_DECL_HIDDEN KNoneFilter::Private 15 | { 16 | public: 17 | Private() 18 | : mode(0) 19 | , avail_out(0) 20 | , avail_in(0) 21 | , next_in(nullptr) 22 | , next_out(nullptr) 23 | { 24 | } 25 | 26 | int mode; 27 | int avail_out; 28 | int avail_in; 29 | const char *next_in; 30 | char *next_out; 31 | }; 32 | 33 | KNoneFilter::KNoneFilter() 34 | : d(new Private) 35 | { 36 | } 37 | 38 | KNoneFilter::~KNoneFilter() 39 | { 40 | delete d; 41 | } 42 | 43 | bool KNoneFilter::init(int mode) 44 | { 45 | d->mode = mode; 46 | return true; 47 | } 48 | 49 | int KNoneFilter::mode() const 50 | { 51 | return d->mode; 52 | } 53 | 54 | bool KNoneFilter::terminate() 55 | { 56 | return true; 57 | } 58 | 59 | void KNoneFilter::reset() 60 | { 61 | } 62 | 63 | bool KNoneFilter::readHeader() 64 | { 65 | return true; 66 | } 67 | 68 | bool KNoneFilter::writeHeader(const QByteArray & /*fileName*/) 69 | { 70 | return true; 71 | } 72 | 73 | void KNoneFilter::setOutBuffer(char *data, uint maxlen) 74 | { 75 | d->avail_out = maxlen; 76 | d->next_out = data; 77 | } 78 | 79 | void KNoneFilter::setInBuffer(const char *data, uint size) 80 | { 81 | d->next_in = data; 82 | d->avail_in = size; 83 | } 84 | 85 | int KNoneFilter::inBufferAvailable() const 86 | { 87 | return d->avail_in; 88 | } 89 | 90 | int KNoneFilter::outBufferAvailable() const 91 | { 92 | return d->avail_out; 93 | } 94 | 95 | KNoneFilter::Result KNoneFilter::uncompress() 96 | { 97 | #ifndef NDEBUG 98 | if (d->mode != QIODevice::ReadOnly) { 99 | return KFilterBase::Error; 100 | } 101 | #endif 102 | return copyData(); 103 | } 104 | 105 | KNoneFilter::Result KNoneFilter::compress(bool finish) 106 | { 107 | Q_ASSERT(d->mode == QIODevice::WriteOnly); 108 | Q_UNUSED(finish); 109 | 110 | return copyData(); 111 | } 112 | 113 | KNoneFilter::Result KNoneFilter::copyData() 114 | { 115 | Q_ASSERT(d->avail_out > 0); 116 | if (d->avail_in > 0) { 117 | const int n = qMin(d->avail_in, d->avail_out); 118 | memcpy(d->next_out, d->next_in, n); 119 | d->avail_out -= n; 120 | d->next_in += n; 121 | d->next_out += n; 122 | d->avail_in -= n; 123 | return KFilterBase::Ok; 124 | } else { 125 | return KFilterBase::End; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/kzipfileentry.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2002 Holger Schroeder 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #ifndef KZIPFILEENTRY_H 8 | #define KZIPFILEENTRY_H 9 | 10 | #include "karchive.h" 11 | 12 | class KZip; 13 | /*! 14 | * \class KZipFileEntry 15 | * \inmodule KArchive 16 | * 17 | * \brief Represents a file in a zip archive. 18 | */ 19 | class KARCHIVE_EXPORT KZipFileEntry : public KArchiveFile 20 | { 21 | public: 22 | /*! 23 | * Creates a new zip file entry. Do not call this, KZip takes care of it. 24 | */ 25 | KZipFileEntry(KZip *zip, 26 | const QString &name, 27 | int access, 28 | const QDateTime &date, 29 | const QString &user, 30 | const QString &group, 31 | const QString &symlink, 32 | const QString &path, 33 | qint64 start, 34 | qint64 uncompressedSize, 35 | int encoding, 36 | qint64 compressedSize); 37 | 38 | ~KZipFileEntry() override; 39 | 40 | /*! 41 | * 42 | */ 43 | int encoding() const; 44 | 45 | /*! 46 | * Only used when writing 47 | */ 48 | qint64 compressedSize() const; 49 | 50 | /*! 51 | * Only used when writing 52 | */ 53 | void setCompressedSize(qint64 compressedSize); 54 | 55 | /*! 56 | * Header start: only used when writing 57 | */ 58 | void setHeaderStart(qint64 headerstart); 59 | /*! 60 | * Header start: only used when writing 61 | */ 62 | qint64 headerStart() const; 63 | 64 | /*! 65 | * CRC: only used when writing 66 | */ 67 | unsigned long crc32() const; 68 | 69 | /*! 70 | * CRC: only used when writing 71 | */ 72 | void setCRC32(unsigned long crc32); 73 | 74 | /*! 75 | * Name with complete path - KArchiveFile::name() is the filename only (no path) 76 | */ 77 | const QString &path() const; 78 | 79 | /*! 80 | * Returns the content of this file. 81 | * 82 | * \note The data returned by this call is not cached. 83 | * 84 | * \warning This method loads the entire file content into memory at once. For large files or untrusted archives, this could cause excessive memory 85 | * allocation. Consider reading in chunks using createDevice() instead when dealing with archives from untrusted sources. 86 | */ 87 | QByteArray data() const override; 88 | 89 | /*! 90 | * This method returns a QIODevice to read the file contents. 91 | * 92 | * This is obviously for reading only. 93 | * 94 | * Note that the ownership of the device is being transferred to the caller, 95 | * who will have to delete it. 96 | * 97 | * The returned device auto-opens (in readonly mode), no need to open it. 98 | * 99 | * \note It can return nullptr 100 | */ 101 | QIODevice *createDevice() const override; 102 | 103 | private: 104 | class KZipFileEntryPrivate; 105 | KZipFileEntryPrivate *const d; 106 | }; 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /autotests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | remove_definitions(-DQT_NO_CAST_FROM_ASCII) 2 | include(ECMAddTests) 3 | 4 | find_package(Qt6Test ${REQUIRED_QT_VERSION} CONFIG QUIET) 5 | find_package(Qt6Network) 6 | 7 | if(NOT Qt6Test_FOUND) 8 | message(STATUS "Qt6Test not found, autotests will not be built.") 9 | return() 10 | endif() 11 | 12 | ecm_add_tests( 13 | karchivetest.cpp 14 | kfiltertest.cpp 15 | LINK_LIBRARIES KF6::Archive Qt6::Test 16 | ) 17 | 18 | target_link_libraries(kfiltertest ZLIB::ZLIB) 19 | 20 | ########### klimitediodevicetest ############### 21 | 22 | ecm_add_test( 23 | klimitediodevicetest.cpp 24 | ../src/klimitediodevice.cpp 25 | TEST_NAME klimitediodevicetest 26 | LINK_LIBRARIES Qt6::Test 27 | ) 28 | 29 | target_include_directories(klimitediodevicetest 30 | PRIVATE $) 31 | 32 | target_compile_definitions(klimitediodevicetest PRIVATE TEST_MODE) 33 | 34 | ########## kcompressiondevicetest ############## 35 | function(check_and_create_tar help_option program_name compression_option extension) 36 | # cmake doesn't support creating zstd/lzip files so run tar directly 37 | # which is a bit annoying since not all tars support zstd/lzip either 38 | # so we first check that the installed tar has the --zstd/--lzip 39 | # option and then we check that the zstd/lzip binary is available 40 | 41 | execute_process(COMMAND tar --help OUTPUT_VARIABLE TAR_OUTPUT) 42 | if (TAR_OUTPUT MATCHES ${help_option}) 43 | find_program(${program_name}_PROGRAM_FOUND ${program_name}) 44 | if (${program_name}_PROGRAM_FOUND) 45 | add_custom_command(TARGET kcompressiondevicetest POST_BUILD 46 | COMMAND tar ${compression_option} -cf 47 | ${testDir}/kcompressiondevice_test.tar.${extension} examples 48 | WORKING_DIRECTORY ${topdir}) 49 | 50 | string(TOUPPER ${program_name} program_name_upper) 51 | target_compile_definitions(kcompressiondevicetest PRIVATE HAVE_${program_name_upper}_SUPPORT_FILE) 52 | endif() 53 | endif() 54 | endfunction() 55 | 56 | if (Qt6Network_FOUND) 57 | ecm_add_test( 58 | kcompressiondevicetest.cpp 59 | LINK_LIBRARIES KF6::Archive Qt6::Test Qt6::Network 60 | ) 61 | 62 | set(testDir $) 63 | get_filename_component(topdir ${CMAKE_CURRENT_SOURCE_DIR}/.. ABSOLUTE) 64 | 65 | add_custom_command(TARGET kcompressiondevicetest POST_BUILD 66 | COMMAND ${CMAKE_COMMAND} -E tar czf 67 | ${testDir}/kcompressiondevice_test.tar.gz examples 68 | WORKING_DIRECTORY ${topdir}) 69 | add_custom_command(TARGET kcompressiondevicetest POST_BUILD 70 | COMMAND ${CMAKE_COMMAND} -E tar cjf 71 | ${testDir}/kcompressiondevice_test.tar.bz2 examples 72 | WORKING_DIRECTORY ${topdir}) 73 | 74 | if (LIBLZMA_FOUND) 75 | add_custom_command(TARGET kcompressiondevicetest POST_BUILD 76 | COMMAND ${CMAKE_COMMAND} -E tar cJf 77 | ${testDir}/kcompressiondevice_test.tar.xz examples 78 | WORKING_DIRECTORY ${topdir}) 79 | 80 | check_and_create_tar(".*--lzip.*" "lzip" "--lzip" "lz") 81 | endif() 82 | 83 | if (LibZstd_FOUND) 84 | check_and_create_tar(".*--zstd.*" "zstd" "--zstd" "zst") 85 | endif() 86 | endif() 87 | -------------------------------------------------------------------------------- /src/karchiveentry.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2000-2005 David Faure 3 | SPDX-FileCopyrightText: 2003 Leo Savernik 4 | 5 | Moved from ktar.h by Roberto Teixeira 6 | 7 | SPDX-License-Identifier: LGPL-2.0-or-later 8 | */ 9 | #ifndef KARCHIVEENTRY_H 10 | #define KARCHIVEENTRY_H 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #ifdef Q_OS_WIN 21 | #include // mode_t 22 | #endif 23 | 24 | class KArchiveDirectory; 25 | class KArchiveFile; 26 | class KArchive; 27 | 28 | class KArchiveEntryPrivate; 29 | /*! 30 | * \class KArchiveEntry 31 | * \inmodule KArchive 32 | * 33 | * \brief Base class for the archive-file's directory structure. 34 | * 35 | * \sa KArchiveFile 36 | * \sa KArchiveDirectory 37 | */ 38 | class KARCHIVE_EXPORT KArchiveEntry 39 | { 40 | public: 41 | /*! 42 | * Creates a new entry. 43 | * 44 | * \a archive the entries archive 45 | * 46 | * \a name the name of the entry 47 | * 48 | * \a access the permissions in unix format 49 | * 50 | * \a date the date (in seconds since 1970) 51 | * 52 | * \a user the user that owns the entry 53 | * 54 | * \a group the group that owns the entry 55 | * 56 | * \a symlink the symlink, or QString() 57 | */ 58 | KArchiveEntry(KArchive *archive, const QString &name, int access, const QDateTime &date, const QString &user, const QString &group, const QString &symlink); 59 | 60 | virtual ~KArchiveEntry(); 61 | 62 | /*! 63 | * Creation date of the file. 64 | * 65 | * Returns the creation date 66 | */ 67 | QDateTime date() const; 68 | 69 | /*! 70 | * Name of the file without path. 71 | * 72 | * Returns the file name without path 73 | */ 74 | QString name() const; 75 | /*! 76 | * The permissions and mode flags as returned by the stat() function 77 | * in st_mode. 78 | * 79 | * Returns the permissions 80 | */ 81 | mode_t permissions() const; 82 | /*! 83 | * User who created the file. 84 | * 85 | * Returns the owner of the file 86 | */ 87 | QString user() const; 88 | /*! 89 | * Group of the user who created the file. 90 | * 91 | * Returns the group of the file 92 | */ 93 | QString group() const; 94 | 95 | /*! 96 | * Symlink if there is one. 97 | * 98 | * Returns the symlink, or QString() 99 | */ 100 | QString symLinkTarget() const; 101 | 102 | /*! 103 | * Checks whether the entry is a file. 104 | * 105 | * Returns true if this entry is a file 106 | */ 107 | virtual bool isFile() const; 108 | 109 | /*! 110 | * Checks whether the entry is a directory. 111 | * 112 | * Returns true if this entry is a directory 113 | */ 114 | virtual bool isDirectory() const; 115 | 116 | protected: 117 | KArchive *archive() const; 118 | 119 | protected: 120 | virtual void virtual_hook(int id, void *data); 121 | 122 | private: 123 | KArchiveEntryPrivate *const d; 124 | }; 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /src/kfilterbase.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2000 David Faure 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #ifndef __kfilterbase__h 8 | #define __kfilterbase__h 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | class KFilterBasePrivate; 15 | 16 | class QIODevice; 17 | 18 | /*! 19 | * \class KFilterBase 20 | * \inmodule KArchive 21 | * 22 | * This is the base class for compression filters 23 | * such as gzip and bzip2. It's pretty much internal. 24 | * Don't use directly, use KCompressionDevice instead. 25 | * \internal 26 | */ 27 | class KARCHIVE_EXPORT KFilterBase 28 | { 29 | public: 30 | KFilterBase(); 31 | virtual ~KFilterBase(); 32 | 33 | /*! 34 | * Sets the device on which the filter will work 35 | * 36 | * \a dev the device on which the filter will work 37 | * 38 | * \a autodelete if true, \a dev is deleted when the filter is deleted 39 | */ 40 | void setDevice(QIODevice *dev, bool autodelete = false); 41 | // Note that this isn't in the constructor, because of KLibFactory::create, 42 | // but it should be called before using the filterbase ! 43 | 44 | /*! 45 | * Returns the device on which the filter will work. 46 | * Returnss the device on which the filter will work 47 | */ 48 | QIODevice *device(); 49 | /*! \internal */ 50 | virtual bool init(int mode) = 0; 51 | /*! \internal */ 52 | virtual int mode() const = 0; 53 | /*! \internal */ 54 | virtual bool terminate(); 55 | /*! \internal */ 56 | virtual void reset(); 57 | /*! \internal */ 58 | virtual bool readHeader() = 0; 59 | /*! \internal */ 60 | virtual bool writeHeader(const QByteArray &filename) = 0; 61 | /*! \internal */ 62 | virtual void setOutBuffer(char *data, uint maxlen) = 0; 63 | /*! \internal */ 64 | virtual void setInBuffer(const char *data, uint size) = 0; 65 | /*! \internal */ 66 | virtual bool inBufferEmpty() const; 67 | /*! \internal */ 68 | virtual int inBufferAvailable() const = 0; 69 | /*! \internal */ 70 | virtual bool outBufferFull() const; 71 | /*! \internal */ 72 | virtual int outBufferAvailable() const = 0; 73 | 74 | /*! \internal */ 75 | enum Result { 76 | Ok, 77 | End, 78 | Error, 79 | }; 80 | /*! \internal */ 81 | virtual Result uncompress() = 0; 82 | /*! \internal */ 83 | virtual Result compress(bool finish) = 0; 84 | 85 | /*! 86 | * \internal 87 | * \since 4.3 88 | */ 89 | enum FilterFlags { 90 | NoHeaders = 0, 91 | WithHeaders = 1, 92 | ZlibHeaders = 2, // only use for gzip compression 93 | }; 94 | /*! 95 | * \internal 96 | * \since 4.3 97 | */ 98 | void setFilterFlags(FilterFlags flags); 99 | FilterFlags filterFlags() const; 100 | 101 | protected: 102 | /*! Virtual hook, used to add new "virtual" functions while maintaining 103 | binary compatibility. Unused in this class. 104 | */ 105 | virtual void virtual_hook(int id, void *data); 106 | 107 | private: 108 | Q_DISABLE_COPY(KFilterBase) 109 | KFilterBasePrivate *const d; 110 | }; 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /src/krcc.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2014 David Faure 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | #ifndef KRCC_H 7 | #define KRCC_H 8 | 9 | #include 10 | 11 | /*! 12 | * \class KRcc 13 | * \inmodule KArchive 14 | * 15 | * KRcc is a class for reading dynamic binary resources created by Qt's rcc tool 16 | * from a .qrc file and the files it points to. 17 | * 18 | * Writing is not supported. 19 | * \brief A class for reading rcc resources. 20 | * \since 5.3 21 | */ 22 | class KARCHIVE_EXPORT KRcc : public KArchive 23 | { 24 | Q_DECLARE_TR_FUNCTIONS(KRcc) 25 | 26 | public: 27 | /*! 28 | * Creates an instance that operates on the given filename. 29 | * 30 | * \a filename is a local path (e.g. "/home/holger/myfile.rcc") 31 | */ 32 | explicit KRcc(const QString &filename); 33 | 34 | /*! 35 | * If the rcc file is still opened, then it will be 36 | * closed automatically by the destructor. 37 | */ 38 | ~KRcc() override; 39 | 40 | protected: 41 | /* 42 | * Writing is not supported by this class, will always fail. 43 | * Returns always false 44 | */ 45 | bool doPrepareWriting(const QString &name, 46 | const QString &user, 47 | const QString &group, 48 | qint64 size, 49 | mode_t perm, 50 | const QDateTime &atime, 51 | const QDateTime &mtime, 52 | const QDateTime &ctime) override; 53 | 54 | /* 55 | * Writing is not supported by this class, will always fail. 56 | * Returns always false 57 | */ 58 | bool doFinishWriting(qint64 size) override; 59 | 60 | /* 61 | * Writing is not supported by this class, will always fail. 62 | * Returns always false 63 | */ 64 | bool doWriteDir(const QString &name, 65 | const QString &user, 66 | const QString &group, 67 | mode_t perm, 68 | const QDateTime &atime, 69 | const QDateTime &mtime, 70 | const QDateTime &ctime) override; 71 | 72 | /* 73 | * Writing is not supported by this class, will always fail. 74 | * Returns always false 75 | */ 76 | bool doWriteSymLink(const QString &name, 77 | const QString &target, 78 | const QString &user, 79 | const QString &group, 80 | mode_t perm, 81 | const QDateTime &atime, 82 | const QDateTime &mtime, 83 | const QDateTime &ctime) override; 84 | 85 | /*! 86 | * Registers the .rcc resource in the QResource system under a unique identifier, 87 | * then lists that, and creates the KArchiveFile/KArchiveDirectory entries. 88 | */ 89 | bool openArchive(QIODevice::OpenMode mode) override; 90 | /*! 91 | * Unregisters the .rcc resource from the QResource system. 92 | */ 93 | bool closeArchive() override; 94 | 95 | protected: 96 | void virtual_hook(int id, void *data) override; 97 | 98 | private: 99 | class KRccPrivate; 100 | KRccPrivate *const d; 101 | }; 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /src/kar.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2002 Laurence Anderson 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | #ifndef KAR_H 7 | #define KAR_H 8 | 9 | #include 10 | 11 | /*! 12 | * \class KAr 13 | * \module KArchive 14 | * 15 | * KAr is a class for reading archives in ar format. Writing 16 | * is not supported. Reading archives that contain files bigger than 17 | * INT_MAX - 32 bytes is not supported. 18 | * 19 | * \brief A class for reading ar archives. 20 | */ 21 | class KARCHIVE_EXPORT KAr : public KArchive 22 | { 23 | Q_DECLARE_TR_FUNCTIONS(KAr) 24 | 25 | public: 26 | /*! 27 | * Creates an instance that operates on the given filename. 28 | * 29 | * \a filename is a local path (e.g. "/home/holger/myfile.ar") 30 | */ 31 | explicit KAr(const QString &filename); 32 | 33 | /*! 34 | * Creates an instance that operates on the given device. 35 | * 36 | * The device can be compressed (KCompressionDevice) or not (QFile, etc.). 37 | * 38 | * \a dev the device to read from 39 | */ 40 | explicit KAr(QIODevice *dev); 41 | 42 | /*! 43 | * If the ar file is still opened, then it will be 44 | * closed automatically by the destructor. 45 | */ 46 | ~KAr() override; 47 | 48 | protected: 49 | /* 50 | * Writing is not supported by this class, will always fail. 51 | * Returns always false 52 | */ 53 | bool doPrepareWriting(const QString &name, 54 | const QString &user, 55 | const QString &group, 56 | qint64 size, 57 | mode_t perm, 58 | const QDateTime &atime, 59 | const QDateTime &mtime, 60 | const QDateTime &ctime) override; 61 | 62 | /* 63 | * Writing is not supported by this class, will always fail. 64 | * Returns always false 65 | */ 66 | bool doFinishWriting(qint64 size) override; 67 | 68 | /* 69 | * Writing is not supported by this class, will always fail. 70 | * Returns always false 71 | */ 72 | bool doWriteDir(const QString &name, 73 | const QString &user, 74 | const QString &group, 75 | mode_t perm, 76 | const QDateTime &atime, 77 | const QDateTime &mtime, 78 | const QDateTime &ctime) override; 79 | 80 | bool doWriteSymLink(const QString &name, 81 | const QString &target, 82 | const QString &user, 83 | const QString &group, 84 | mode_t perm, 85 | const QDateTime &atime, 86 | const QDateTime &mtime, 87 | const QDateTime &ctime) override; 88 | 89 | /* 90 | * Opens the archive for reading. 91 | * Parses the directory listing of the archive 92 | * and creates the KArchiveDirectory/KArchiveFile entries. 93 | * 94 | */ 95 | bool openArchive(QIODevice::OpenMode mode) override; 96 | bool closeArchive() override; 97 | 98 | protected: 99 | void virtual_hook(int id, void *data) override; 100 | 101 | private: 102 | class KArPrivate; 103 | KArPrivate *const d; 104 | }; 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(HAVE_BZIP2_SUPPORT ${BZIP2_FOUND}) 2 | if(BZIP2_FOUND AND BZIP2_NEED_PREFIX) 3 | set(NEED_BZ2_PREFIX 1) 4 | endif() 5 | 6 | set(HAVE_XZ_SUPPORT ${LIBLZMA_FOUND}) 7 | set(HAVE_OPENSSL_SUPPORT ${OpenSSL_FOUND}) 8 | set(HAVE_ZSTD_SUPPORT ${LibZstd_FOUND}) 9 | 10 | configure_file(config-compression.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-compression.h) 11 | 12 | add_library(KF6Archive) 13 | add_library(KF6::Archive ALIAS KF6Archive) 14 | 15 | set_target_properties(KF6Archive PROPERTIES 16 | VERSION ${KARCHIVE_VERSION} 17 | SOVERSION ${KARCHIVE_SOVERSION} 18 | EXPORT_NAME "Archive" 19 | ) 20 | 21 | ecm_create_qm_loader(KF6Archive karchive6_qt) 22 | 23 | if(BZIP2_FOUND) 24 | target_sources(KF6Archive PRIVATE kbzip2filter.cpp) 25 | target_link_libraries(KF6Archive PRIVATE BZip2::BZip2) 26 | endif() 27 | 28 | if(LIBLZMA_FOUND) 29 | target_sources(KF6Archive PRIVATE kxzfilter.cpp klzfilter.cpp k7zip.cpp) 30 | target_link_libraries(KF6Archive PRIVATE LibLZMA::LibLZMA) 31 | endif() 32 | 33 | if (OpenSSL_FOUND) 34 | target_link_libraries(KF6Archive PRIVATE OpenSSL::Crypto) 35 | endif() 36 | 37 | if (LibZstd_FOUND) 38 | target_sources(KF6Archive PRIVATE kzstdfilter.cpp) 39 | target_link_libraries(KF6Archive PRIVATE PkgConfig::LibZstd) 40 | endif() 41 | 42 | 43 | target_sources(KF6Archive PRIVATE karchive.cpp 44 | kar.cpp 45 | kcompressiondevice.cpp 46 | kfilterbase.cpp 47 | kgzipfilter.cpp 48 | klimitediodevice.cpp 49 | knonefilter.cpp 50 | ktar.cpp 51 | kzip.cpp 52 | krcc.cpp 53 | ) 54 | 55 | ecm_qt_declare_logging_category(KF6Archive 56 | HEADER loggingcategory.h 57 | IDENTIFIER KArchiveLog 58 | CATEGORY_NAME kf.archive 59 | OLD_CATEGORY_NAMES kf5.karchive 60 | DEFAULT_SEVERITY Warning 61 | DESCRIPTION "KArchive" 62 | EXPORT KARCHIVE 63 | ) 64 | 65 | ecm_generate_export_header(KF6Archive 66 | BASE_NAME KArchive 67 | GROUP_BASE_NAME KF 68 | VERSION ${KF_VERSION} 69 | USE_VERSION_HEADER 70 | DEPRECATED_BASE_VERSION 0 71 | DEPRECATION_VERSIONS 6.13 72 | EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT} 73 | ) 74 | 75 | target_include_directories(KF6Archive 76 | INTERFACE "$" 77 | ) 78 | 79 | target_link_libraries(KF6Archive 80 | PUBLIC 81 | Qt6::Core 82 | PRIVATE 83 | ZLIB::ZLIB 84 | ) 85 | 86 | ecm_generate_headers(KArchive_HEADERS 87 | HEADER_NAMES 88 | KArchive 89 | KArchiveEntry 90 | KArchiveFile 91 | KArchiveDirectory 92 | KAr 93 | KCompressionDevice 94 | KFilterBase 95 | KRcc 96 | KTar 97 | KZip 98 | KZipFileEntry 99 | 100 | REQUIRED_HEADERS KArchive_HEADERS 101 | ) 102 | 103 | install(TARGETS KF6Archive 104 | EXPORT KF6ArchiveTargets 105 | ${KF_INSTALL_TARGETS_DEFAULT_ARGS}) 106 | 107 | if(LIBLZMA_FOUND) 108 | ecm_generate_headers(KArchive_HEADERS 109 | HEADER_NAMES 110 | K7Zip 111 | REQUIRED_HEADERS KArchive_HEADERS 112 | ) 113 | endif() 114 | 115 | install(FILES 116 | ${CMAKE_CURRENT_BINARY_DIR}/karchive_export.h 117 | ${KArchive_HEADERS} 118 | DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KArchive 119 | COMPONENT Devel) 120 | 121 | ecm_qt_install_logging_categories( 122 | EXPORT KARCHIVE 123 | FILE karchive.categories 124 | DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR} 125 | ) 126 | 127 | ecm_generate_qdoc(KF6Archive karchive.qdocconf) 128 | -------------------------------------------------------------------------------- /src/karchivefile.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2000-2005 David Faure 3 | SPDX-FileCopyrightText: 2003 Leo Savernik 4 | 5 | Moved from ktar.h by Roberto Teixeira 6 | 7 | SPDX-License-Identifier: LGPL-2.0-or-later 8 | */ 9 | #ifndef KARCHIVEFILE_H 10 | #define KARCHIVEFILE_H 11 | 12 | #include 13 | 14 | class KArchiveFilePrivate; 15 | /*! 16 | * \class KArchiveFile 17 | * \inmodule KArchive 18 | * 19 | * \brief A file in an archive. 20 | * 21 | * \sa KArchive 22 | * \sa KArchiveDirectory 23 | */ 24 | class KARCHIVE_EXPORT KArchiveFile : public KArchiveEntry 25 | { 26 | public: 27 | /*! 28 | * Creates a new file entry. Do not call this, KArchive takes care of it. 29 | * \a archive the entries archive 30 | * \a name the name of the entry 31 | * \a access the permissions in unix format 32 | * \a date the date (in seconds since 1970) 33 | * \a user the user that owns the entry 34 | * \a group the group that owns the entry 35 | * \a symlink the symlink, or QString() 36 | * \a pos the position of the file in the directory 37 | * \a size the size of the file 38 | */ 39 | KArchiveFile(KArchive *archive, 40 | const QString &name, 41 | int access, 42 | const QDateTime &date, 43 | const QString &user, 44 | const QString &group, 45 | const QString &symlink, 46 | qint64 pos, 47 | qint64 size); 48 | 49 | /*! 50 | * Destructor. Do not call this, KArchive takes care of it. 51 | */ 52 | ~KArchiveFile() override; 53 | 54 | /*! 55 | * Position of the data in the [uncompressed] archive. 56 | * Returns the position of the file 57 | */ 58 | qint64 position() const; 59 | /*! 60 | * Size of the data. 61 | * Returns the size of the file 62 | */ 63 | qint64 size() const; 64 | /*! 65 | * Set size of data, usually after writing the file. 66 | * \a s the new size of the file 67 | */ 68 | void setSize(qint64 s); 69 | 70 | /*! 71 | * Returns the content of this file. 72 | * 73 | * \note The data returned by this call is not cached. 74 | * 75 | * \warning This method loads the entire file content into memory at once. For large files or untrusted archives, this could cause excessive memory 76 | * allocation. Consider reading in chunks using createDevice() instead when dealing with archives from untrusted sources. 77 | */ 78 | virtual QByteArray data() const; 79 | 80 | /*! 81 | * This method returns QIODevice (internal class: KLimitedIODevice) 82 | * on top of the underlying QIODevice. This is obviously for reading only. 83 | * 84 | * WARNING: Note that the ownership of the device is being transferred to the caller, 85 | * who will have to delete it. 86 | * 87 | * The returned device auto-opens (in readonly mode), no need to open it. 88 | * Returns the QIODevice of the file 89 | */ 90 | virtual QIODevice *createDevice() const; 91 | 92 | /*! 93 | * Checks whether this entry is a file. 94 | * Returns true, since this entry is a file 95 | */ 96 | bool isFile() const override; 97 | 98 | /*! 99 | * Extracts the file to the directory \a dest 100 | * \a dest the directory to extract to 101 | * Returns true on success, false if the file (dest + '/' + name()) couldn't be created 102 | */ 103 | bool copyTo(const QString &dest) const; 104 | 105 | protected: 106 | void virtual_hook(int id, void *data) override; 107 | 108 | private: 109 | KArchiveFilePrivate *const d; 110 | }; 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /src/kzstdfilter.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2021 Albert Astals Cid 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #include "kzstdfilter.h" 8 | #include "loggingcategory.h" 9 | 10 | #include 11 | 12 | #if HAVE_ZSTD_SUPPORT 13 | 14 | extern "C" { 15 | #include 16 | } 17 | 18 | class Q_DECL_HIDDEN KZstdFilter::Private 19 | { 20 | public: 21 | union { 22 | ZSTD_CStream *cStream; 23 | ZSTD_DStream *dStream; 24 | }; 25 | int mode; 26 | bool isInitialized = false; 27 | ZSTD_inBuffer inBuffer; 28 | ZSTD_outBuffer outBuffer; 29 | }; 30 | 31 | KZstdFilter::KZstdFilter() 32 | : d(new Private) 33 | { 34 | } 35 | 36 | KZstdFilter::~KZstdFilter() 37 | { 38 | } 39 | 40 | bool KZstdFilter::init(int mode) 41 | { 42 | if (d->isInitialized) { 43 | terminate(); 44 | } 45 | 46 | d->inBuffer.size = 0; 47 | d->inBuffer.pos = 0; 48 | 49 | if (mode == QIODevice::ReadOnly) { 50 | d->dStream = ZSTD_createDStream(); 51 | } else if (mode == QIODevice::WriteOnly) { 52 | d->cStream = ZSTD_createCStream(); 53 | } else { 54 | // qCWarning(KArchiveLog) << "Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported"; 55 | return false; 56 | } 57 | d->mode = mode; 58 | d->isInitialized = true; 59 | return true; 60 | } 61 | 62 | int KZstdFilter::mode() const 63 | { 64 | return d->mode; 65 | } 66 | 67 | bool KZstdFilter::terminate() 68 | { 69 | if (d->mode == QIODevice::ReadOnly) { 70 | ZSTD_freeDStream(d->dStream); 71 | } else if (d->mode == QIODevice::WriteOnly) { 72 | ZSTD_freeCStream(d->cStream); 73 | } else { 74 | // qCWarning(KArchiveLog) << "Unsupported mode " << d->mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported"; 75 | return false; 76 | } 77 | d->isInitialized = false; 78 | return true; 79 | } 80 | 81 | void KZstdFilter::reset() 82 | { 83 | terminate(); 84 | init(d->mode); 85 | } 86 | 87 | void KZstdFilter::setOutBuffer(char *data, uint maxlen) 88 | { 89 | d->outBuffer.dst = data; 90 | d->outBuffer.size = maxlen; 91 | d->outBuffer.pos = 0; 92 | } 93 | 94 | void KZstdFilter::setInBuffer(const char *data, unsigned int size) 95 | { 96 | d->inBuffer.src = data; 97 | d->inBuffer.size = size; 98 | d->inBuffer.pos = 0; 99 | } 100 | 101 | int KZstdFilter::inBufferAvailable() const 102 | { 103 | return d->inBuffer.size - d->inBuffer.pos; 104 | } 105 | 106 | int KZstdFilter::outBufferAvailable() const 107 | { 108 | return d->outBuffer.size - d->outBuffer.pos; 109 | } 110 | 111 | KZstdFilter::Result KZstdFilter::uncompress() 112 | { 113 | // qCDebug(KArchiveLog) << "Calling ZSTD_decompressStream with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable(); 114 | const size_t result = ZSTD_decompressStream(d->dStream, &d->outBuffer, &d->inBuffer); 115 | if (ZSTD_isError(result)) { 116 | qCWarning(KArchiveLog) << "ZSTD_decompressStream returned" << result << ZSTD_getErrorName(result); 117 | return KFilterBase::Error; 118 | } 119 | 120 | return result == 0 ? KFilterBase::End : KFilterBase::Ok; 121 | } 122 | 123 | KZstdFilter::Result KZstdFilter::compress(bool finish) 124 | { 125 | // qCDebug(KArchiveLog) << "Calling ZSTD_compressStream2 with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable(); 126 | const size_t result = ZSTD_compressStream2(d->cStream, &d->outBuffer, &d->inBuffer, finish ? ZSTD_e_end : ZSTD_e_continue); 127 | if (ZSTD_isError(result)) { 128 | return KFilterBase::Error; 129 | } 130 | 131 | return finish && result == 0 ? KFilterBase::End : KFilterBase::Ok; 132 | } 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /autotests/ossfuzz/build_fuzzers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | # 3 | # SPDX-FileCopyrightText: 2019 Google Inc. 4 | # SPDX-FileCopyrightText: 2025 Azhar Momin 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | # Copyright 2019 Google Inc. 8 | # Copyright 2025 Azhar Momin 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | # 22 | ################################################################################ 23 | 24 | rm -rf $WORK/* 25 | 26 | export PATH="$WORK/bin:$PATH" 27 | export PKG_CONFIG_PATH="$WORK/lib/pkgconfig:$WORK/lib/x86_64-linux-gnu/pkgconfig" 28 | 29 | # build zstd 30 | cd $SRC/zstd 31 | cmake -S build/cmake -G Ninja \ 32 | -DBUILD_SHARED_LIBS=OFF \ 33 | -DCMAKE_INSTALL_PREFIX=$WORK 34 | ninja install -j$(nproc) 35 | 36 | # Build zlib 37 | cd $SRC/zlib 38 | ./configure --static --prefix $WORK 39 | make install -j$(nproc) 40 | 41 | # Build bzip2 42 | # Inspired from ../bzip2/build 43 | cd $SRC 44 | tar xzf bzip2-*.tar.gz && rm -f bzip2-*.tar.gz 45 | cd bzip2-* 46 | SRCL=(blocksort.o huffman.o crctable.o randtable.o compress.o decompress.o bzlib.o) 47 | 48 | for source in ${SRCL[@]}; do 49 | name=$(basename $source .o) 50 | $CC $CFLAGS -c ${name}.c 51 | done 52 | rm -f libbz2.a 53 | ar cq libbz2.a ${SRCL[@]} 54 | cp -f bzlib.h $WORK/include 55 | cp -f libbz2.a $WORK/lib 56 | 57 | # Build xz 58 | export ORIG_CFLAGS="${CFLAGS}" 59 | export ORIG_CXXFLAGS="${CXXFLAGS}" 60 | unset CFLAGS 61 | unset CXXFLAGS 62 | cd $SRC/xz 63 | ./autogen.sh --no-po4a --no-doxygen 64 | ./configure --enable-static --disable-debug --disable-shared --disable-xz --disable-xzdec --disable-lzmainfo --prefix $WORK 65 | make install -j$(nproc) 66 | export CFLAGS="${ORIG_CFLAGS}" 67 | export CXXFLAGS="${ORIG_CXXFLAGS}" 68 | 69 | # Build openssl 70 | cd $SRC/openssl 71 | CONFIG_FLAGS="no-shared no-tests --prefix=$WORK --openssldir=$WORK" 72 | if [[ $CFLAGS = *sanitize=memory* ]] 73 | then 74 | # Disable assembly for proper instrumentation 75 | CONFIG_FLAGS+=" no-asm" 76 | fi 77 | ./config $CONFIG_FLAGS 78 | make build_generated 79 | make libcrypto.a -j$(nproc) 80 | make install_sw 81 | 82 | # Build extra-cmake-modules 83 | cd $SRC/extra-cmake-modules 84 | cmake . -G Ninja \ 85 | -DCMAKE_INSTALL_PREFIX=$WORK 86 | ninja install -j$(nproc) 87 | 88 | # Build qtbase 89 | cd $SRC/qtbase 90 | ./configure -no-glib -qt-libpng -qt-pcre -opensource -confirm-license -static -no-opengl \ 91 | -no-icu -platform linux-clang-libc++ -debug -prefix $WORK -no-feature-gui -no-feature-sql \ 92 | -no-feature-network -no-feature-xml -no-feature-dbus -no-feature-printsupport 93 | ninja install -j$(nproc) 94 | 95 | # Build karchive 96 | cd $SRC/karchive 97 | rm -rf poqm 98 | cmake . -G Ninja \ 99 | -DBUILD_SHARED_LIBS=OFF \ 100 | -DBUILD_TESTING=OFF \ 101 | -DBUILD_FUZZERS=ON \ 102 | -DCMAKE_INSTALL_PREFIX=$WORK 103 | ninja install -j$(nproc) 104 | 105 | EXTENSIONS="k7z_fuzzer 7z 106 | kar_fuzzer ar 107 | ktar_fuzzer tar 108 | kzip_fuzzer zip 109 | ktar_bz2_fuzzer tar.bz2 110 | ktar_gz_fuzzer tar.gz 111 | ktar_lz_fuzzer tar.lz 112 | ktar_xz_fuzzer tar.xz 113 | ktar_zst_fuzzer tar.zst" 114 | echo "$EXTENSIONS" | while read fuzzer_name extension; do 115 | # Copy the fuzzer 116 | cp bin/fuzzers/$fuzzer_name $OUT 117 | 118 | # Create a seed corpus 119 | find . -name "*.$extension" -exec zip -jq "$OUT/${fuzzer_name}_seed_corpus.zip" {} + 120 | 121 | # Copy the dictionary file 122 | if [ -f "autotests/data/dict/$fuzzer_name.dict" ]; then 123 | cp "autotests/data/dict/$fuzzer_name.dict" $OUT 124 | fi 125 | done 126 | -------------------------------------------------------------------------------- /src/k7zip.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2011 Mario Bensi 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | #ifndef K7ZIP_H 7 | #define K7ZIP_H 8 | 9 | #include 10 | 11 | /*! 12 | * \class K7Zip 13 | * \inmodule KArchive 14 | * 15 | * \brief A class for reading / writing p7zip archives. 16 | */ 17 | class KARCHIVE_EXPORT K7Zip : public KArchive 18 | { 19 | Q_DECLARE_TR_FUNCTIONS(K7Zip) 20 | 21 | public: 22 | /*! 23 | * Creates an instance that operates on the given filename 24 | * using the compression filter associated to given mimetype. 25 | * 26 | * \a filename is a local path (e.g. "/home/user/myfile.7z") 27 | */ 28 | explicit K7Zip(const QString &filename); 29 | 30 | /*! 31 | * Creates an instance that operates on the given device. 32 | * 33 | * The device can be compressed (KCompressionDevice) or not (QFile, etc.). 34 | * 35 | * \warning Do not assume that giving a QFile here will decompress the file, 36 | * in case it's compressed! 37 | * 38 | * \a dev the device to read from. If the source is compressed, the 39 | * QIODevice must take care of decompression 40 | */ 41 | explicit K7Zip(QIODevice *dev); 42 | 43 | /*! 44 | * If the archive is still opened, then it will be 45 | * closed automatically by the destructor. 46 | */ 47 | ~K7Zip() override; 48 | 49 | /*! 50 | * Sets the password to use for encrypted archives. 51 | * 52 | * This method must be called before opening the archive. 53 | * 54 | * \note Currently only AES decryption is supported. 55 | * 56 | * \a password the password to use for encrypted archive 57 | * \since 6.13 58 | */ 59 | void setPassword(const QString &password); 60 | 61 | /*! 62 | * Whether the archive needs a password to be opened. 63 | * 64 | * \note This can only be called after open() has been called once. 65 | * 66 | * Returns \c true if the archive requires a password to be opened 67 | * \since 6.13 68 | */ 69 | bool passwordNeeded() const; 70 | 71 | protected: 72 | // Reimplemented from KArchive 73 | bool doWriteSymLink(const QString &name, 74 | const QString &target, 75 | const QString &user, 76 | const QString &group, 77 | mode_t perm, 78 | const QDateTime &atime, 79 | const QDateTime &mtime, 80 | const QDateTime &ctime) override; 81 | // Reimplemented from KArchive 82 | bool doWriteDir(const QString &name, 83 | const QString &user, 84 | const QString &group, 85 | mode_t perm, 86 | const QDateTime &atime, 87 | const QDateTime &mtime, 88 | const QDateTime &ctime) override; 89 | // Reimplemented from KArchive 90 | bool doPrepareWriting(const QString &name, 91 | const QString &user, 92 | const QString &group, 93 | qint64 size, 94 | mode_t perm, 95 | const QDateTime &atime, 96 | const QDateTime &mtime, 97 | const QDateTime &ctime) override; 98 | // Reimplemented from KArchive 99 | bool doFinishWriting(qint64 size) override; 100 | 101 | // Reimplemented from KArchive 102 | bool doWriteData(const char *data, qint64 size) override; 103 | 104 | /*! 105 | * Opens the archive for reading. 106 | * 107 | * Parses the directory listing of the archive 108 | * and creates the KArchiveDirectory/KArchiveFile entries. 109 | * 110 | * \a mode the mode of the file 111 | */ 112 | bool openArchive(QIODevice::OpenMode mode) override; 113 | bool closeArchive() override; 114 | 115 | protected: 116 | void virtual_hook(int id, void *data) override; 117 | 118 | private: 119 | class K7ZipPrivate; 120 | K7ZipPrivate *const d; 121 | }; 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /autotests/karchivetest.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE project 2 | SPDX-FileCopyrightText: 2006 David Faure 3 | SPDX-FileCopyrightText: 2012 Mario Bensi 4 | 5 | SPDX-License-Identifier: LGPL-2.0-or-later 6 | */ 7 | 8 | #ifndef KARCHIVETEST_H 9 | #define KARCHIVETEST_H 10 | 11 | #include 12 | #include 13 | 14 | class KArchiveTest : public QObject 15 | { 16 | Q_OBJECT 17 | 18 | void setupData(); 19 | void setup7ZipData(); 20 | 21 | private Q_SLOTS: 22 | void initTestCase(); 23 | 24 | void testEmptyFilename(); 25 | void testNullDevice(); 26 | void testNonExistentFile(); 27 | void testCreateTar_data(); 28 | void testCreateTar(); 29 | void testCreateTarXXX_data() 30 | { 31 | setupData(); 32 | } 33 | void testCreateTarXXX(); 34 | void testReadTar_data() 35 | { 36 | setupData(); 37 | } 38 | void testReadTar(); 39 | void testUncompress_data() 40 | { 41 | setupData(); 42 | } 43 | void testUncompress(); 44 | void testTarFileData_data() 45 | { 46 | setupData(); 47 | } 48 | void testTarFileData(); 49 | void testTarCopyTo_data() 50 | { 51 | setupData(); 52 | } 53 | void testTarCopyTo(); 54 | void testTarReadWrite_data() 55 | { 56 | setupData(); 57 | } 58 | void testTarReadWrite(); 59 | void testTarMaxLength_data(); 60 | void testTarMaxLength(); 61 | void testTarGlobalHeader(); 62 | void testTarPrefix(); 63 | void testTarDirectoryForgotten(); 64 | void testTarEmptyFileMissingDir(); 65 | void testTarRootDir(); 66 | void testTarDirectoryTwice(); 67 | void testTarIgnoreRelativePathOutsideArchive(); 68 | void testTarLongNonASCIINames(); 69 | void testTarShortNonASCIINames(); 70 | void testTarGzHugeMemoryUsage(); 71 | void testTarDeepDirHierarchy(); 72 | void benchmarkTarDeepDirHierarchy(); 73 | 74 | void testCreateZip(); 75 | void testCreateZipError(); 76 | void testReadZipError(); 77 | void testReadZip(); 78 | void testZipFileData(); 79 | void testZipCopyTo(); 80 | void testZipMaxLength(); 81 | void testZipWithNonLatinFileNames(); 82 | void testZipWithOverwrittenFileName(); 83 | void testZipAddLocalDirectory(); 84 | void testZipReadRedundantDataDescriptor_data(); 85 | void testZipReadRedundantDataDescriptor(); 86 | void testZipDirectoryPermissions(); 87 | void testZipUnusualButValid(); 88 | void testZipDuplicateNames(); 89 | void testZipWithinZip(); 90 | void testZipPrependedData(); 91 | void testZip64(); 92 | void testZipReopenWithoutDoubleDeletion(); 93 | void testZip64NestedStored(); 94 | void testZip64NestedStoredStreamed(); 95 | void testZip64EndOfCentralDirectory(); 96 | void testZip64DataDescriptor(); 97 | void testZip64ExtraZip64Size(); 98 | void testZip64ExtraZip64SizeFirst(); 99 | void testZip64ExtraZip64Offset(); 100 | void testZipOssFuzzIssue433303801(); 101 | void testZipDeepDirHierarchy(); 102 | void benchmarkZipDeepDirHierarchy(); 103 | 104 | void testRcc(); 105 | 106 | void testAr(); 107 | 108 | #if HAVE_XZ_SUPPORT 109 | void testCreate7Zip_data() 110 | { 111 | setup7ZipData(); 112 | } 113 | void testCreate7Zip(); 114 | void testRead7Zip_data() 115 | { 116 | setup7ZipData(); 117 | } 118 | void testRead7Zip(); 119 | void test7ZipFileData_data() 120 | { 121 | setup7ZipData(); 122 | } 123 | void test7ZipFileData(); 124 | void test7ZipCopyTo_data() 125 | { 126 | setup7ZipData(); 127 | } 128 | void test7ZipCopyTo(); 129 | void test7ZipReadWrite_data() 130 | { 131 | setup7ZipData(); 132 | } 133 | void test7ZipReadWrite(); 134 | void test7ZipMaxLength_data() 135 | { 136 | setup7ZipData(); 137 | } 138 | void test7ZipMaxLength(); 139 | void test7ZipNamelessFile(); 140 | void test7ZipMultipleNamelessFiles(); 141 | void test7ZipReadNumber(); 142 | void test7ZipFileNameEndsInSlash(); 143 | void test7ZipOssFuzzIssues_data(); 144 | void test7ZipOssFuzzIssues(); 145 | #if HAVE_OPENSSL_SUPPORT 146 | void test7ZipPasswordProtected(); 147 | #endif 148 | void test7ZipReadCoder_data(); 149 | void test7ZipReadCoder(); 150 | #endif 151 | 152 | void cleanupTestCase(); 153 | }; 154 | 155 | #endif 156 | -------------------------------------------------------------------------------- /src/ktar.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2000-2005 David Faure 3 | SPDX-FileCopyrightText: 2003 Leo Savernik 4 | 5 | SPDX-License-Identifier: LGPL-2.0-or-later 6 | */ 7 | #ifndef KTAR_H 8 | #define KTAR_H 9 | 10 | #include 11 | 12 | /*! 13 | * \class KTar 14 | * \inmodule KArchive 15 | * 16 | * \brief A class for reading / writing (optionally compressed) tar archives. 17 | * 18 | * KTar allows you to read and write tar archives, including those 19 | * that are compressed using gzip, bzip2, xz or lzip. 20 | */ 21 | class KARCHIVE_EXPORT KTar : public KArchive 22 | { 23 | Q_DECLARE_TR_FUNCTIONS(KTar) 24 | 25 | public: 26 | /*! 27 | * Creates an instance that operates on the given filename 28 | * using the compression filter associated to given mimetype. 29 | * 30 | * \a filename is a local path (e.g. "/home/weis/myfile.tgz") 31 | * 32 | * \a mimetype "application/gzip" (before 5.85: "application/x-gzip"), "application/x-bzip", 33 | * "application/x-xz", "application/zstd" (since 5.82) 34 | * "application/x-lzip" (since 6.15) 35 | * Do not use application/x-compressed-tar or similar - you only need to 36 | * specify the compression layer ! If the mimetype is omitted, it 37 | * will be determined from the filename. 38 | */ 39 | explicit KTar(const QString &filename, const QString &mimetype = QString()); 40 | 41 | /*! 42 | * Creates an instance that operates on the given device. 43 | * 44 | * The device can be compressed (KCompressionDevice) or not (QFile, etc.). 45 | * 46 | * \warning Do not assume that giving a QFile here will decompress the file, 47 | * in case it's compressed! 48 | * 49 | * \a dev the device to read from. If the source is compressed, the 50 | * QIODevice must take care of decompression 51 | */ 52 | explicit KTar(QIODevice *dev); 53 | 54 | /*! 55 | * If the tar ball is still opened, then it will be 56 | * closed automatically by the destructor. 57 | */ 58 | ~KTar() override; 59 | 60 | /*! 61 | * Special function for setting the "original file name" in the gzip header, 62 | * when writing a tar.gz file. It appears when using in the "file" command, 63 | * for instance. Should only be called if the underlying device is a KCompressionDevice! 64 | * 65 | * \a fileName the original file name 66 | */ 67 | void setOrigFileName(const QByteArray &fileName); 68 | 69 | protected: 70 | /// Reimplemented from KArchive 71 | bool doWriteSymLink(const QString &name, 72 | const QString &target, 73 | const QString &user, 74 | const QString &group, 75 | mode_t perm, 76 | const QDateTime &atime, 77 | const QDateTime &mtime, 78 | const QDateTime &ctime) override; 79 | /// Reimplemented from KArchive 80 | bool doWriteDir(const QString &name, 81 | const QString &user, 82 | const QString &group, 83 | mode_t perm, 84 | const QDateTime &atime, 85 | const QDateTime &mtime, 86 | const QDateTime &ctime) override; 87 | /// Reimplemented from KArchive 88 | bool doPrepareWriting(const QString &name, 89 | const QString &user, 90 | const QString &group, 91 | qint64 size, 92 | mode_t perm, 93 | const QDateTime &atime, 94 | const QDateTime &mtime, 95 | const QDateTime &ctime) override; 96 | /// Reimplemented from KArchive 97 | bool doFinishWriting(qint64 size) override; 98 | 99 | /* 100 | * Opens the archive for reading. 101 | * Parses the directory listing of the archive 102 | * and creates the KArchiveDirectory/KArchiveFile entries. 103 | * \a mode the mode of the file 104 | */ 105 | bool openArchive(QIODevice::OpenMode mode) override; 106 | bool closeArchive() override; 107 | 108 | bool createDevice(QIODevice::OpenMode mode) override; 109 | 110 | private: 111 | protected: 112 | void virtual_hook(int id, void *data) override; 113 | 114 | private: 115 | class KTarPrivate; 116 | KTarPrivate *const d; 117 | }; 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | set(KF_VERSION "6.22.0") # handled by release scripts 4 | project(KArchive VERSION ${KF_VERSION}) 5 | 6 | include(FeatureSummary) 7 | find_package(ECM 6.21.0 NO_MODULE) 8 | set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules") 9 | feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) 10 | 11 | option(WITH_BZIP2 "Make bzip2 required" ON) 12 | option(WITH_LIBLZMA "Make liblzma required" ON) 13 | option(WITH_OPENSSL "Make openssl required" ON) 14 | option(WITH_LIBZSTD "Make libzstd required" ON) 15 | 16 | option(BUILD_FUZZERS "Build KArchive fuzzers" OFF) 17 | 18 | set(PKGCONFIG_REQUIRED_TYPE "") 19 | 20 | if(WITH_BZIP2) 21 | set(BZIP2_PACKAGE_TYPE "REQUIRED") 22 | else() 23 | set(BZIP2_PACKAGE_TYPE "RECOMMENDED") 24 | endif() 25 | 26 | if(WITH_LIBLZMA) 27 | set(LIBLZMA_PACKAGE_TYPE "REQUIRED") 28 | else() 29 | set(LIBLZMA_PACKAGE_TYPE "RECOMMENDED") 30 | endif() 31 | 32 | if(WITH_OPENSSL) 33 | set(OPENSSL_PACKAGE_TYPE "REQUIRED") 34 | else() 35 | set(OPENSSL_PACKAGE_TYPE "RECOMMENDED") 36 | endif() 37 | 38 | if(WITH_LIBZSTD) 39 | set(PKGCONFIG_REQUIRED_TYPE "REQUIRED") 40 | set(LIBZSTD_REQUIRED_TYPE "REQUIRED") 41 | else() 42 | set(LIBZSTD_REQUIRED_TYPE "") 43 | endif() 44 | 45 | set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) 46 | 47 | include(KDEInstallDirs) 48 | include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) 49 | include(KDECMakeSettings) 50 | include(KDEGitCommitHooks) 51 | include(ECMGenerateQDoc) 52 | 53 | include(ECMGenerateExportHeader) 54 | 55 | set(REQUIRED_QT_VERSION 6.8.0) 56 | find_package(Qt6Core ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE) 57 | 58 | find_package(ZLIB) 59 | set_package_properties(ZLIB PROPERTIES 60 | URL "https://www.zlib.net" 61 | DESCRIPTION "Support for gzip compressed files and data streams" 62 | TYPE REQUIRED 63 | PURPOSE "Support for gzip compressed files and data streams" 64 | ) 65 | 66 | find_package(BZip2) 67 | set_package_properties(BZip2 PROPERTIES 68 | URL "https://sourceware.org/bzip2/" 69 | DESCRIPTION "Support for BZip2 compressed files and data streams" 70 | TYPE ${BZIP2_PACKAGE_TYPE} 71 | PURPOSE "Support for BZip2 compressed files and data streams" 72 | ) 73 | 74 | find_package(LibLZMA) 75 | set_package_properties(LibLZMA PROPERTIES 76 | URL "https://tukaani.org/xz/" 77 | DESCRIPTION "Support for xz compressed files and data streams" 78 | TYPE ${LIBLZMA_PACKAGE_TYPE} 79 | PURPOSE "Support for xz compressed files and data streams" 80 | ) 81 | 82 | find_package(OpenSSL) 83 | set_package_properties(OpenSSL PROPERTIES 84 | URL "https://www.openssl.org/" 85 | DESCRIPTION "Support for encrypted archives" 86 | TYPE ${OPENSSL_PACKAGE_TYPE} 87 | PURPOSE "Support for encrypted archives" 88 | ) 89 | 90 | 91 | find_package(PkgConfig ${PKGCONFIG_REQUIRED_TYPE}) 92 | if (PkgConfig_FOUND) 93 | pkg_check_modules(LibZstd ${LIBZSTD_REQUIRED_TYPE} IMPORTED_TARGET "libzstd") 94 | endif() 95 | add_feature_info(LibZstd LibZstd_FOUND 96 | "Support for zstd compressed files and data streams" 97 | ) 98 | 99 | include(ECMSetupVersion) 100 | include(ECMGenerateHeaders) 101 | include(ECMQtDeclareLoggingCategory) 102 | include(ECMDeprecationSettings) 103 | include(ECMPoQmTools) 104 | 105 | set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].") 106 | 107 | set(karchive_version_header "${CMAKE_CURRENT_BINARY_DIR}/src/karchive_version.h") 108 | ecm_setup_version(PROJECT 109 | VARIABLE_PREFIX KARCHIVE 110 | VERSION_HEADER "${karchive_version_header}" 111 | PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF6ArchiveConfigVersion.cmake" 112 | SOVERSION 6) 113 | 114 | ecm_set_disabled_deprecation_versions( 115 | QT 6.11 116 | ) 117 | 118 | 119 | add_subdirectory(src) 120 | if (BUILD_TESTING) 121 | add_subdirectory(autotests) 122 | add_subdirectory(tests) 123 | endif() 124 | 125 | if (BUILD_FUZZERS) 126 | add_subdirectory(autotests/ossfuzz) 127 | endif() 128 | 129 | ecm_install_po_files_as_qm(poqm) 130 | 131 | # create a Config.cmake and a ConfigVersion.cmake file and install them 132 | set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF6Archive") 133 | 134 | include(CMakePackageConfigHelpers) 135 | 136 | configure_package_config_file( 137 | "${CMAKE_CURRENT_SOURCE_DIR}/KF6ArchiveConfig.cmake.in" 138 | "${CMAKE_CURRENT_BINARY_DIR}/KF6ArchiveConfig.cmake" 139 | INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} 140 | ) 141 | 142 | install(FILES ${karchive_version_header} 143 | DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KArchive 144 | COMPONENT Devel) 145 | 146 | install(FILES 147 | "${CMAKE_CURRENT_BINARY_DIR}/KF6ArchiveConfig.cmake" 148 | "${CMAKE_CURRENT_BINARY_DIR}/KF6ArchiveConfigVersion.cmake" 149 | DESTINATION "${CMAKECONFIG_INSTALL_DIR}" 150 | COMPONENT Devel) 151 | 152 | install(EXPORT KF6ArchiveTargets 153 | DESTINATION "${CMAKECONFIG_INSTALL_DIR}" 154 | FILE KF6ArchiveTargets.cmake 155 | NAMESPACE KF6::) 156 | 157 | include(ECMFeatureSummary) 158 | ecm_feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) 159 | 160 | kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT) 161 | -------------------------------------------------------------------------------- /src/krcc.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2014 David Faure 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #include "krcc.h" 8 | #include "karchive_p.h" 9 | #include "loggingcategory.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | class Q_DECL_HIDDEN KRcc::KRccPrivate 20 | { 21 | public: 22 | KRccPrivate() 23 | { 24 | } 25 | void createEntries(const QDir &dir, KArchiveDirectory *parentDir, KRcc *q); 26 | 27 | QString m_prefix; // '/' + uuid 28 | }; 29 | 30 | /*! 31 | * A KRccFileEntry represents a file in a rcc archive. 32 | */ 33 | class KRccFileEntry : public KArchiveFile 34 | { 35 | public: 36 | KRccFileEntry(KArchive *archive, 37 | const QString &name, 38 | int access, 39 | const QDateTime &date, 40 | const QString &user, 41 | const QString &group, 42 | qint64 size, 43 | const QString &resourcePath) 44 | : KArchiveFile(archive, name, access, date, user, group, QString(), 0, size) 45 | , m_resourcePath(resourcePath) 46 | { 47 | } 48 | 49 | QByteArray data() const override 50 | { 51 | QFile f(m_resourcePath); 52 | if (f.open(QIODevice::ReadOnly)) { 53 | return f.readAll(); 54 | } 55 | qCWarning(KArchiveLog) << "Couldn't open" << m_resourcePath; 56 | return QByteArray(); 57 | } 58 | QIODevice *createDevice() const override 59 | { 60 | return new QFile(m_resourcePath); 61 | } 62 | 63 | private: 64 | QString m_resourcePath; 65 | }; 66 | 67 | KRcc::KRcc(const QString &filename) 68 | : KArchive(filename) 69 | , d(new KRccPrivate) 70 | { 71 | } 72 | 73 | KRcc::~KRcc() 74 | { 75 | if (isOpen()) { 76 | close(); 77 | } 78 | delete d; 79 | } 80 | 81 | bool KRcc::doPrepareWriting(const QString &, const QString &, const QString &, qint64, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) 82 | { 83 | setErrorString(tr("Cannot write to RCC file")); 84 | qCWarning(KArchiveLog) << "doPrepareWriting not implemented for KRcc"; 85 | return false; 86 | } 87 | 88 | bool KRcc::doFinishWriting(qint64) 89 | { 90 | setErrorString(tr("Cannot write to RCC file")); 91 | qCWarning(KArchiveLog) << "doFinishWriting not implemented for KRcc"; 92 | return false; 93 | } 94 | 95 | bool KRcc::doWriteDir(const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) 96 | { 97 | setErrorString(tr("Cannot write to RCC file")); 98 | qCWarning(KArchiveLog) << "doWriteDir not implemented for KRcc"; 99 | return false; 100 | } 101 | 102 | bool KRcc::doWriteSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) 103 | { 104 | setErrorString(tr("Cannot write to RCC file")); 105 | qCWarning(KArchiveLog) << "doWriteSymLink not implemented for KRcc"; 106 | return false; 107 | } 108 | 109 | bool KRcc::openArchive(QIODevice::OpenMode mode) 110 | { 111 | // Open archive 112 | 113 | if (mode == QIODevice::WriteOnly) { 114 | return true; 115 | } 116 | if (mode != QIODevice::ReadOnly && mode != QIODevice::ReadWrite) { 117 | setErrorString(tr("Unsupported mode %1").arg(static_cast(mode))); 118 | return false; 119 | } 120 | 121 | QUuid uuid = QUuid::createUuid(); 122 | d->m_prefix = QLatin1Char('/') + uuid.toString(); 123 | if (!QResource::registerResource(fileName(), d->m_prefix)) { 124 | setErrorString(tr("Failed to register resource %1 under prefix %2").arg(fileName(), d->m_prefix)); 125 | return false; 126 | } 127 | 128 | QDir dir(QLatin1Char(':') + d->m_prefix); 129 | d->createEntries(dir, rootDir(), this); 130 | return true; 131 | } 132 | 133 | void KRcc::KRccPrivate::createEntries(const QDir &dir, KArchiveDirectory *parentDir, KRcc *q) 134 | { 135 | for (const QString &fileName : dir.entryList()) { 136 | const QString entryPath = dir.path() + QLatin1Char('/') + fileName; 137 | const QFileInfo info(entryPath); 138 | if (info.isFile()) { 139 | KArchiveEntry *entry = new KRccFileEntry(q, fileName, 0444, info.lastModified(), parentDir->user(), parentDir->group(), info.size(), entryPath); 140 | // We don't want to fail opening potentially malformed files, so void the return value 141 | (void)parentDir->addEntryV2(entry); 142 | } else { 143 | KArchiveDirectory *entry = 144 | new KArchiveDirectory(q, fileName, 0555, info.lastModified(), parentDir->user(), parentDir->group(), /*symlink*/ QString()); 145 | if (parentDir->addEntryV2(entry)) { 146 | createEntries(QDir(entryPath), entry, q); 147 | } 148 | } 149 | } 150 | } 151 | 152 | bool KRcc::closeArchive() 153 | { 154 | // Close the archive 155 | QResource::unregisterResource(fileName(), d->m_prefix); 156 | // ignore errors 157 | return true; 158 | } 159 | 160 | void KRcc::virtual_hook(int id, void *data) 161 | { 162 | KArchive::virtual_hook(id, data); 163 | } 164 | -------------------------------------------------------------------------------- /src/karchivedirectory.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2000-2005 David Faure 3 | SPDX-FileCopyrightText: 2003 Leo Savernik 4 | 5 | Moved from ktar.h by Roberto Teixeira 6 | 7 | SPDX-License-Identifier: LGPL-2.0-or-later 8 | */ 9 | #ifndef KARCHIVEDIRECTORY_H 10 | #define KARCHIVEDIRECTORY_H 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | class KArchiveDirectoryPrivate; 22 | class KArchiveFile; 23 | /*! 24 | * \class KArchiveDirectory 25 | * \inmodule KArchive 26 | * 27 | * \brief A directory in an archive. 28 | * 29 | * \sa KArchive 30 | * \sa KArchiveFile 31 | */ 32 | class KARCHIVE_EXPORT KArchiveDirectory : public KArchiveEntry 33 | { 34 | public: 35 | /*! 36 | * Creates a new directory entry. 37 | * 38 | * \a archive the entries archive 39 | * 40 | * \a name the name of the entry 41 | * 42 | * \a access the permissions in unix format 43 | * 44 | * \a date the date (in seconds since 1970) 45 | * 46 | * \a user the user that owns the entry 47 | * 48 | * \a group the group that owns the entry 49 | * 50 | * \a symlink the symlink, or QString() 51 | */ 52 | KArchiveDirectory(KArchive *archive, 53 | const QString &name, 54 | int access, 55 | const QDateTime &date, 56 | const QString &user, 57 | const QString &group, 58 | const QString &symlink); 59 | 60 | ~KArchiveDirectory() override; 61 | 62 | /*! 63 | * Returns a list of sub-entries. 64 | * 65 | * Note that the list is not sorted, it's even in random order (due to using a hashtable). 66 | * Use sort() on the result to sort the list by filename. 67 | * 68 | * Returns the names of all entries in this directory (filenames, no path). 69 | */ 70 | QStringList entries() const; 71 | 72 | /*! 73 | * Returns the entry in the archive with the given name. 74 | * 75 | * The entry could be a file or a directory, use isFile() to find out which one it is. 76 | * 77 | * \a name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc. 78 | * 79 | * Returns a pointer to the entry in the directory, or a null pointer if there is no such entry. 80 | */ 81 | const KArchiveEntry *entry(const QString &name) const; 82 | 83 | /*! 84 | * Returns the file entry in the archive with the given name. 85 | * 86 | * If the entry exists and is a file, a KArchiveFile is returned. 87 | * 88 | * Otherwise, a null pointer is returned. 89 | * 90 | * This is a convenience method for entry(), when we know the entry is expected to be a file. 91 | * 92 | * 93 | * \a name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc. 94 | * 95 | * Returns a pointer to the file entry in the directory, or a null pointer if there is no such file entry. 96 | * \since 5.3 97 | */ 98 | const KArchiveFile *file(const QString &name) const; 99 | 100 | #if KARCHIVE_ENABLE_DEPRECATED_SINCE(6, 13) 101 | /*! 102 | * \internal 103 | * Adds a new entry to the directory. 104 | * 105 | * Note: this can delete the entry if another one with the same name is already present 106 | * 107 | * \deprecated[6.13] 108 | * 109 | * Use addEntryV2() instead. 110 | */ 111 | KARCHIVE_DEPRECATED_VERSION(6, 13, "Use addEntryV2() instead.") 112 | void addEntry(KArchiveEntry *); // KF7 TODO: remove 113 | #endif 114 | 115 | /*! 116 | * \internal 117 | * Adds a new entry to the directory. 118 | * 119 | * Returns whether the entry was added or not. Non added entries are deleted 120 | * \since 6.13 121 | * 122 | * Returns whether the entry was added or not. Non added entries are deleted 123 | */ 124 | [[nodiscard]] bool addEntryV2(KArchiveEntry *); // KF7 TODO: rename to addEntry 125 | 126 | #if KARCHIVE_ENABLE_DEPRECATED_SINCE(6, 13) 127 | /*! 128 | * \internal 129 | * 130 | * Removes an entry from the directory. 131 | * 132 | * \deprecated[6.13] 133 | * Use removeEntryV2() instead. 134 | */ 135 | KARCHIVE_DEPRECATED_VERSION(6, 13, "Use removeEntryV2() instead.") 136 | void removeEntry(KArchiveEntry *); // KF7 TODO: remove 137 | #endif 138 | 139 | /*! 140 | * Removes an entry from the directory. 141 | * 142 | * Returns whether the entry was removed or not. 143 | * \since 6.13 144 | */ 145 | [[nodiscard]] bool removeEntryV2(KArchiveEntry *); // KF7 TODO: rename to removeEntry 146 | 147 | /* 148 | * Returns true, since this entry is a directory 149 | */ 150 | bool isDirectory() const override; 151 | 152 | /*! 153 | * Extracts all entries in this archive directory to the directory 154 | * 155 | * \a dest. 156 | * 157 | * \a dest the directory to extract to 158 | * 159 | * \a recursive if set to true, subdirectories are extracted as well 160 | * 161 | * Returns true on success, false if the directory (dest + '/' + name()) couldn't be created 162 | */ 163 | bool copyTo(const QString &dest, bool recursive = true) const; 164 | 165 | protected: 166 | void virtual_hook(int id, void *data) override; 167 | 168 | private: 169 | friend class KArchiveDirectoryPrivate; 170 | KArchiveDirectoryPrivate *const d; 171 | }; 172 | 173 | #endif 174 | -------------------------------------------------------------------------------- /src/kcompressiondevice.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2000 David Faure 3 | SPDX-FileCopyrightText: 2011 Mario Bensi 4 | 5 | SPDX-License-Identifier: LGPL-2.0-or-later 6 | */ 7 | #ifndef __kcompressiondevice_h 8 | #define __kcompressiondevice_h 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | class KCompressionDevicePrivate; 18 | 19 | class KFilterBase; 20 | 21 | /*! 22 | * \class KCompressionDevice 23 | * \inmodule KArchive 24 | * 25 | * A class for reading and writing compressed data onto a device 26 | * (e.g. file, but other usages are possible, like a buffer or a socket). 27 | * 28 | * Use this class to read/write compressed files. 29 | */ 30 | 31 | class KARCHIVE_EXPORT KCompressionDevice : public QIODevice // KF7 TODO: consider inheriting from QFileDevice, so apps can use error() generically ? 32 | { 33 | Q_OBJECT 34 | public: 35 | /*! 36 | * \value GZip 37 | * \value BZip2 38 | * \value Xz 39 | * \value None 40 | * \value[since 5.82] Zstd 41 | * \value[since 6.15] Lz 42 | */ 43 | enum CompressionType { 44 | GZip, 45 | BZip2, 46 | Xz, 47 | None, 48 | Zstd, 49 | Lz, 50 | }; 51 | 52 | /*! 53 | * Constructs a KCompressionDevice for a given CompressionType (e.g. GZip, BZip2 etc.). 54 | * 55 | * \a inputDevice input device. 56 | * 57 | * \a autoDeleteInputDevice if true, \a inputDevice will be deleted automatically 58 | * 59 | * \a type the CompressionType to use. 60 | */ 61 | KCompressionDevice(QIODevice *inputDevice, bool autoDeleteInputDevice, CompressionType type); 62 | 63 | /*! 64 | * Constructs a KCompressionDevice for a given CompressionType (e.g. GZip, BZip2 etc.). 65 | * 66 | * \a fileName the name of the file to filter. 67 | * 68 | * \a type the CompressionType to use. 69 | */ 70 | KCompressionDevice(const QString &fileName, CompressionType type); 71 | 72 | /*! 73 | * Constructs a KCompressionDevice for a given \a fileName. 74 | * 75 | * \a fileName the name of the file to filter. 76 | * 77 | * \since 5.85 78 | */ 79 | explicit KCompressionDevice(const QString &fileName); 80 | 81 | /*! 82 | * Constructs a KCompressionDevice for a given CompressionType (e.g. GZip, BZip2 etc.). 83 | * 84 | * \a inputDevice input device. 85 | * 86 | * \a type the CompressionType to use. 87 | * 88 | * \a size the size we know the inputDevice with CompressionType type has. If we know it. 89 | * 90 | * \since 6.16 91 | */ 92 | KCompressionDevice(std::unique_ptr inputDevice, CompressionType type, std::optional size = {}); 93 | 94 | /*! 95 | * Destructs the KCompressionDevice. 96 | * 97 | * Calls close() if the filter device is still open. 98 | */ 99 | ~KCompressionDevice() override; 100 | 101 | /*! 102 | * The compression actually used by this device. 103 | * 104 | * If the support for the compression requested in the constructor 105 | * is not available, then the device will use None. 106 | */ 107 | CompressionType compressionType() const; 108 | 109 | [[nodiscard]] bool open(QIODevice::OpenMode mode) override; 110 | 111 | void close() override; 112 | 113 | qint64 size() const override; 114 | 115 | /*! 116 | * For writing gzip compressed files only: 117 | * set the name of the original file, to be used in the gzip header. 118 | * 119 | * \a fileName the name of the original file 120 | */ 121 | void setOrigFileName(const QByteArray &fileName); 122 | 123 | /*! 124 | * Call this let this device skip the gzip headers when reading/writing. 125 | * This way KCompressionDevice (with gzip filter) can be used as a direct wrapper 126 | * around zlib - this is used by KZip. 127 | */ 128 | void setSkipHeaders(); 129 | 130 | /*! 131 | * \reimp 132 | * That one can be quite slow, when going back. Use with care. 133 | */ 134 | bool seek(qint64) override; 135 | 136 | bool atEnd() const override; 137 | 138 | /*! 139 | * Call this to create the appropriate filter for the CompressionType 140 | * named \a type. 141 | * 142 | * \a type the type of the compression filter 143 | * 144 | * Returns the filter for the \a type, or 0 if not found 145 | */ 146 | static KFilterBase *filterForCompressionType(CompressionType type); 147 | 148 | /*! 149 | * Returns the compression type for the given MIME type, if possible. Otherwise returns None. 150 | * 151 | * This handles simple cases like application/gzip, but also application/x-compressed-tar, and inheritance. 152 | * \since 5.85 153 | */ 154 | static CompressionType compressionTypeForMimeType(const QString &mimetype); 155 | 156 | /*! 157 | * Returns the error code from the last failing operation. 158 | * This is especially useful after calling close(), which unfortunately returns void 159 | * (see https://bugreports.qt.io/browse/QTBUG-70033), to see if the flushing done by close 160 | * was able to write all the data to disk. 161 | */ 162 | QFileDevice::FileError error() const; 163 | 164 | protected: 165 | friend class K7Zip; 166 | 167 | qint64 readData(char *data, qint64 maxlen) override; 168 | qint64 writeData(const char *data, qint64 len) override; 169 | 170 | KFilterBase *filterBase(); 171 | 172 | private: 173 | friend KCompressionDevicePrivate; 174 | KCompressionDevicePrivate *const d; 175 | }; 176 | 177 | Q_DECLARE_METATYPE(KCompressionDevice::CompressionType) 178 | 179 | #endif 180 | -------------------------------------------------------------------------------- /src/kbzip2filter.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2000-2005 David Faure 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #include "kbzip2filter.h" 8 | #include "loggingcategory.h" 9 | 10 | #if HAVE_BZIP2_SUPPORT 11 | 12 | // we don't need that 13 | #define BZ_NO_STDIO 14 | extern "C" { 15 | #include 16 | } 17 | 18 | #if NEED_BZ2_PREFIX 19 | #define bzDecompressInit(x, y, z) BZ2_bzDecompressInit(x, y, z) 20 | #define bzDecompressEnd(x) BZ2_bzDecompressEnd(x) 21 | #define bzCompressEnd(x) BZ2_bzCompressEnd(x) 22 | #define bzDecompress(x) BZ2_bzDecompress(x) 23 | #define bzCompress(x, y) BZ2_bzCompress(x, y) 24 | #define bzCompressInit(x, y, z, a) BZ2_bzCompressInit(x, y, z, a); 25 | #endif 26 | 27 | #include 28 | 29 | #include 30 | 31 | // For docu on this, see /usr/doc/bzip2-0.9.5d/bzip2-0.9.5d/manual_3.html 32 | 33 | class Q_DECL_HIDDEN KBzip2Filter::Private 34 | { 35 | public: 36 | Private() 37 | : isInitialized(false) 38 | { 39 | memset(&zStream, 0, sizeof(zStream)); 40 | mode = 0; 41 | } 42 | 43 | bz_stream zStream; 44 | int mode; 45 | bool isInitialized; 46 | }; 47 | 48 | KBzip2Filter::KBzip2Filter() 49 | : d(new Private) 50 | { 51 | } 52 | 53 | KBzip2Filter::~KBzip2Filter() 54 | { 55 | delete d; 56 | } 57 | 58 | bool KBzip2Filter::init(int mode) 59 | { 60 | if (d->isInitialized) { 61 | terminate(); 62 | } 63 | 64 | d->zStream.next_in = nullptr; 65 | d->zStream.avail_in = 0; 66 | if (mode == QIODevice::ReadOnly) { 67 | const int result = bzDecompressInit(&d->zStream, 0, 0); 68 | if (result != BZ_OK) { 69 | // qCDebug(KArchiveLog) << "bzDecompressInit returned " << result; 70 | return false; 71 | } 72 | } else if (mode == QIODevice::WriteOnly) { 73 | const int result = bzCompressInit(&d->zStream, 5, 0, 0); 74 | if (result != BZ_OK) { 75 | // qCDebug(KArchiveLog) << "bzDecompressInit returned " << result; 76 | return false; 77 | } 78 | } else { 79 | // qCWarning(KArchiveLog) << "Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported"; 80 | return false; 81 | } 82 | d->mode = mode; 83 | d->isInitialized = true; 84 | return true; 85 | } 86 | 87 | int KBzip2Filter::mode() const 88 | { 89 | return d->mode; 90 | } 91 | 92 | bool KBzip2Filter::terminate() 93 | { 94 | if (d->mode == QIODevice::ReadOnly) { 95 | const int result = bzDecompressEnd(&d->zStream); 96 | if (result != BZ_OK) { 97 | // qCDebug(KArchiveLog) << "bzDecompressEnd returned " << result; 98 | return false; 99 | } 100 | } else if (d->mode == QIODevice::WriteOnly) { 101 | const int result = bzCompressEnd(&d->zStream); 102 | if (result != BZ_OK) { 103 | // qCDebug(KArchiveLog) << "bzCompressEnd returned " << result; 104 | return false; 105 | } 106 | } else { 107 | // qCWarning(KArchiveLog) << "Unsupported mode " << d->mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported"; 108 | return false; 109 | } 110 | d->isInitialized = false; 111 | return true; 112 | } 113 | 114 | void KBzip2Filter::reset() 115 | { 116 | // bzip2 doesn't seem to have a reset call... 117 | terminate(); 118 | init(d->mode); 119 | } 120 | 121 | void KBzip2Filter::setOutBuffer(char *data, uint maxlen) 122 | { 123 | d->zStream.avail_out = maxlen; 124 | d->zStream.next_out = data; 125 | } 126 | 127 | void KBzip2Filter::setInBuffer(const char *data, unsigned int size) 128 | { 129 | d->zStream.avail_in = size; 130 | d->zStream.next_in = const_cast(data); 131 | } 132 | 133 | int KBzip2Filter::inBufferAvailable() const 134 | { 135 | return d->zStream.avail_in; 136 | } 137 | 138 | int KBzip2Filter::outBufferAvailable() const 139 | { 140 | return d->zStream.avail_out; 141 | } 142 | 143 | KBzip2Filter::Result KBzip2Filter::uncompress() 144 | { 145 | // qCDebug(KArchiveLog) << "Calling bzDecompress with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable(); 146 | int result = bzDecompress(&d->zStream); 147 | if (result < BZ_OK) { 148 | bzDecompressEnd(&d->zStream); 149 | } 150 | 151 | switch (result) { 152 | case BZ_OK: 153 | return KFilterBase::Ok; 154 | case BZ_STREAM_END: 155 | return KFilterBase::End; 156 | case BZ_MEM_ERROR: 157 | qCWarning(KArchiveLog) << "bzDecompress error, insufficient memory"; 158 | break; 159 | case BZ_DATA_ERROR: 160 | qCWarning(KArchiveLog) << "bzDecompress error, data integrity error"; 161 | break; 162 | case BZ_DATA_ERROR_MAGIC: 163 | qCWarning(KArchiveLog) << "bzDecompress error, stream does not start with the right magic bytes"; 164 | break; 165 | case BZ_PARAM_ERROR: 166 | qCWarning(KArchiveLog) << "bzDecompress error, parameter error"; 167 | break; 168 | default: 169 | qCWarning(KArchiveLog) << "bzDecompress error, returned:" << result; 170 | break; 171 | } 172 | return KFilterBase::Error; 173 | } 174 | 175 | KBzip2Filter::Result KBzip2Filter::compress(bool finish) 176 | { 177 | // qCDebug(KArchiveLog) << "Calling bzCompress with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable(); 178 | int result = bzCompress(&d->zStream, finish ? BZ_FINISH : BZ_RUN); 179 | 180 | switch (result) { 181 | case BZ_OK: 182 | case BZ_FLUSH_OK: 183 | case BZ_RUN_OK: 184 | case BZ_FINISH_OK: 185 | return KFilterBase::Ok; 186 | break; 187 | case BZ_STREAM_END: 188 | // qCDebug(KArchiveLog) << " bzCompress returned " << result; 189 | return KFilterBase::End; 190 | break; 191 | default: 192 | // qCDebug(KArchiveLog) << " bzCompress returned " << result; 193 | return KFilterBase::Error; 194 | break; 195 | } 196 | } 197 | 198 | #endif /* HAVE_BZIP2_SUPPORT */ 199 | -------------------------------------------------------------------------------- /src/kzip.h: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2002 Holger Schroeder 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | #ifndef KZIP_H 7 | #define KZIP_H 8 | 9 | #include 10 | 11 | #include "kzipfileentry.h" // for source compat 12 | 13 | class KZipFileEntry; 14 | /*! 15 | * \class KZip 16 | * \inmodule KArchive 17 | * 18 | * \brief A class for reading / writing zip archives. 19 | * 20 | * You can use it in QIODevice::ReadOnly or in QIODevice::WriteOnly mode, and it 21 | * behaves just as expected. 22 | * It can also be used in QIODevice::ReadWrite mode, in this case one can 23 | * append files to an existing zip archive. When you append new files, which 24 | * are not yet in the zip, it works as expected, i.e. the files are appended at the end. 25 | * When you append a file, which is already in the file, the reference to the 26 | * old file is dropped and the new one is added to the zip - but the 27 | * old data from the file itself is not deleted, it is still in the 28 | * zipfile. So when you want to have a small and garbage-free zipfile, 29 | * just read the contents of the appended zip file and write it to a new one 30 | * in QIODevice::WriteOnly mode. This is especially important when you don't want 31 | * to leak information of how intermediate versions of files in the zip 32 | * were looking. 33 | * 34 | * For more information on the zip fileformat go to 35 | * http://www.pkware.com/products/enterprise/white_papers/appnote.html 36 | */ 37 | class KARCHIVE_EXPORT KZip : public KArchive 38 | { 39 | Q_DECLARE_TR_FUNCTIONS(KZip) 40 | 41 | public: 42 | /*! 43 | * Creates an instance that operates on the given filename. 44 | * using the compression filter associated to given mimetype. 45 | * 46 | * \a filename is a local path (e.g. "/home/holger/myfile.zip") 47 | */ 48 | explicit KZip(const QString &filename); 49 | 50 | /*! 51 | * Creates an instance that operates on the given device. 52 | * 53 | * The device can be compressed (KCompressionDevice) or not (QFile, etc.). 54 | * 55 | * \a dev the device to access 56 | * 57 | * \warning Do not assume that giving a QFile here will decompress the file, 58 | * in case it's compressed! 59 | */ 60 | explicit KZip(QIODevice *dev); 61 | 62 | /*! 63 | * If the zip file is still opened, then it will be 64 | * closed automatically by the destructor. 65 | */ 66 | ~KZip() override; 67 | 68 | /*! 69 | * Describes the contents of the "extra field" for a given file in the Zip archive. 70 | * 71 | * \value NoExtraField No extra field 72 | * \value ModificationTime Modification time ("extended timestamp" header) 73 | * \omitvalue DefaultExtraField 74 | */ 75 | enum ExtraField { 76 | NoExtraField = 0, 77 | ModificationTime = 1, 78 | DefaultExtraField = 1, 79 | }; 80 | 81 | /*! 82 | * Call this before writeFile or prepareWriting, to define what the next 83 | * file to be written should have in its extra field. 84 | * 85 | * \a ef the type of "extra field" 86 | * \sa extraField() 87 | */ 88 | void setExtraField(ExtraField ef); 89 | 90 | /*! 91 | * The current type of "extra field" that will be used for new files. 92 | * 93 | * Returns the current type of "extra field" 94 | * \sa setExtraField() 95 | */ 96 | ExtraField extraField() const; 97 | 98 | /*! 99 | * Describes the compression type for a given file in the Zip archive. 100 | * 101 | * \value NoCompression Uncompressed 102 | * \value DeflateCompression Deflate compression method 103 | */ 104 | enum Compression { 105 | NoCompression = 0, 106 | DeflateCompression = 1, 107 | }; 108 | 109 | /*! 110 | * Call this before writeFile or prepareWriting, to define whether the next 111 | * files to be written should be compressed or not. 112 | * 113 | * \a c the new compression mode 114 | * \sa compression() 115 | */ 116 | void setCompression(Compression c); 117 | 118 | /*! 119 | * The current compression mode that will be used for new files. 120 | * 121 | * Returns the current compression mode 122 | * \sa setCompression() 123 | */ 124 | Compression compression() const; 125 | 126 | protected: 127 | /// Reimplemented from KArchive 128 | bool doWriteSymLink(const QString &name, 129 | const QString &target, 130 | const QString &user, 131 | const QString &group, 132 | mode_t perm, 133 | const QDateTime &atime, 134 | const QDateTime &mtime, 135 | const QDateTime &ctime) override; 136 | /// Reimplemented from KArchive 137 | bool doPrepareWriting(const QString &name, 138 | const QString &user, 139 | const QString &group, 140 | qint64 size, 141 | mode_t perm, 142 | const QDateTime &atime, 143 | const QDateTime &mtime, 144 | const QDateTime &creationTime) override; 145 | 146 | /* 147 | * Write data to a file that has been created using prepareWriting(). 148 | * \a size the size of the file 149 | * Returns true if successful, false otherwise 150 | */ 151 | bool doFinishWriting(qint64 size) override; 152 | 153 | /* 154 | * Write data to a file that has been created using prepareWriting(). 155 | * \a data a pointer to the data 156 | * \a size the size of the chunk 157 | * Returns true if successful, false otherwise 158 | */ 159 | bool doWriteData(const char *data, qint64 size) override; 160 | 161 | /* 162 | * Opens the archive for reading. 163 | * Parses the directory listing of the archive 164 | * and creates the KArchiveDirectory/KArchiveFile entries. 165 | * \a mode the mode of the file 166 | */ 167 | bool openArchive(QIODevice::OpenMode mode) override; 168 | 169 | /// Closes the archive 170 | bool closeArchive() override; 171 | 172 | /// Reimplemented from KArchive 173 | bool doWriteDir(const QString &name, 174 | const QString &user, 175 | const QString &group, 176 | mode_t perm, 177 | const QDateTime &atime, 178 | const QDateTime &mtime, 179 | const QDateTime &ctime) override; 180 | 181 | protected: 182 | void virtual_hook(int id, void *data) override; 183 | 184 | private: 185 | class KZipPrivate; 186 | KZipPrivate *const d; 187 | }; 188 | 189 | #endif 190 | -------------------------------------------------------------------------------- /src/kar.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2002 Laurence Anderson 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #include "kar.h" 8 | #include "karchive_p.h" 9 | #include "loggingcategory.h" 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #include "kcompressiondevice.h" 17 | //#include "klimitediodevice_p.h" 18 | 19 | //////////////////////////////////////////////////////////////////////// 20 | /////////////////////////// KAr /////////////////////////////////////// 21 | //////////////////////////////////////////////////////////////////////// 22 | 23 | class Q_DECL_HIDDEN KAr::KArPrivate 24 | { 25 | public: 26 | KArPrivate() 27 | { 28 | } 29 | }; 30 | 31 | KAr::KAr(const QString &filename) 32 | : KArchive(filename) 33 | , d(new KArPrivate) 34 | { 35 | } 36 | 37 | KAr::KAr(QIODevice *dev) 38 | : KArchive(dev) 39 | , d(new KArPrivate) 40 | { 41 | } 42 | 43 | KAr::~KAr() 44 | { 45 | if (isOpen()) { 46 | close(); 47 | } 48 | delete d; 49 | } 50 | 51 | bool KAr::doPrepareWriting(const QString &, const QString &, const QString &, qint64, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) 52 | { 53 | setErrorString(tr("Cannot write to AR file")); 54 | qCWarning(KArchiveLog) << "doPrepareWriting not implemented for KAr"; 55 | return false; 56 | } 57 | 58 | bool KAr::doFinishWriting(qint64) 59 | { 60 | setErrorString(tr("Cannot write to AR file")); 61 | qCWarning(KArchiveLog) << "doFinishWriting not implemented for KAr"; 62 | return false; 63 | } 64 | 65 | bool KAr::doWriteDir(const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) 66 | { 67 | setErrorString(tr("Cannot write to AR file")); 68 | qCWarning(KArchiveLog) << "doWriteDir not implemented for KAr"; 69 | return false; 70 | } 71 | 72 | bool KAr::doWriteSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) 73 | { 74 | setErrorString(tr("Cannot write to AR file")); 75 | qCWarning(KArchiveLog) << "doWriteSymLink not implemented for KAr"; 76 | return false; 77 | } 78 | 79 | bool KAr::openArchive(QIODevice::OpenMode mode) 80 | { 81 | // Open archive 82 | 83 | if (mode == QIODevice::WriteOnly) { 84 | return true; 85 | } 86 | if (mode != QIODevice::ReadOnly && mode != QIODevice::ReadWrite) { 87 | setErrorString(tr("Unsupported mode %1").arg(static_cast(mode))); 88 | return false; 89 | } 90 | 91 | QIODevice *dev = device(); 92 | if (!dev) { 93 | return false; 94 | } 95 | 96 | QByteArray magic = dev->read(7); 97 | if (magic != "!") { 98 | setErrorString(tr("Invalid main magic")); 99 | return false; 100 | } 101 | 102 | QByteArray ar_longnames; 103 | while (!dev->atEnd()) { 104 | QByteArray ar_header; 105 | ar_header.resize(60); 106 | 107 | dev->seek(dev->pos() + (2 - (dev->pos() % 2)) % 2); // Ar headers are padded to byte boundary 108 | 109 | if (dev->read(ar_header.data(), 60) != 60) { // Read ar header 110 | qCWarning(KArchiveLog) << "Couldn't read header"; 111 | return true; // Probably EOF / trailing junk 112 | } 113 | 114 | if (!ar_header.endsWith("`\n")) { // Check header magic // krazy:exclude=strings 115 | setErrorString(tr("Invalid magic")); 116 | return false; 117 | } 118 | 119 | QByteArray name = ar_header.mid(0, 16); // Process header 120 | const int date = ar_header.mid(16, 12).trimmed().toInt(); 121 | // const int uid = ar_header.mid( 28, 6 ).trimmed().toInt(); 122 | // const int gid = ar_header.mid( 34, 6 ).trimmed().toInt(); 123 | const int accessMode = ar_header.mid(40, 8).trimmed().toInt(nullptr, 8); 124 | const qint64 size = ar_header.mid(48, 10).trimmed().toInt(); 125 | if (size < 0 || size > kMaxQByteArraySize) { 126 | setErrorString(tr("Invalid size")); 127 | return false; 128 | } 129 | 130 | bool skip_entry = false; // Deal with special entries 131 | if (name.mid(0, 1) == "/") { 132 | if (name.mid(1, 1) == "/") { // Longfilename table entry 133 | ar_longnames.resize(size); 134 | // Read the table. Note that the QByteArray will contain NUL characters after each entry. 135 | dev->read(ar_longnames.data(), size); 136 | skip_entry = true; 137 | qCDebug(KArchiveLog) << "Read in longnames entry"; 138 | } else if (name.mid(1, 1) == " ") { // Symbol table entry 139 | qCDebug(KArchiveLog) << "Skipped symbol entry"; 140 | dev->seek(dev->pos() + size); 141 | skip_entry = true; 142 | } else { // Longfilename, look it up in the table 143 | const int ar_longnamesIndex = name.mid(1, 15).trimmed().toInt(); 144 | qCDebug(KArchiveLog) << "Longfilename #" << ar_longnamesIndex; 145 | if (ar_longnames.isEmpty()) { 146 | setErrorString(tr("Invalid longfilename reference")); 147 | return false; 148 | } 149 | if (ar_longnamesIndex < 0 || ar_longnamesIndex >= ar_longnames.size()) { 150 | setErrorString(tr("Invalid longfilename position reference")); 151 | return false; 152 | } 153 | name = QByteArray(ar_longnames.constData() + ar_longnamesIndex); 154 | name.truncate(name.indexOf('/')); 155 | } 156 | } 157 | if (skip_entry) { 158 | continue; 159 | } 160 | 161 | // Process filename 162 | name = name.trimmed(); 163 | name.replace('/', QByteArray()); 164 | qCDebug(KArchiveLog) << "Filename: " << name << " Size: " << size; 165 | 166 | KArchiveEntry *entry = new KArchiveFile(this, 167 | QString::fromLocal8Bit(name.constData()), 168 | accessMode, 169 | KArchivePrivate::time_tToDateTime(date), 170 | rootDir()->user(), 171 | rootDir()->group(), 172 | /*symlink*/ QString(), 173 | dev->pos(), 174 | size); 175 | // We don't want to fail opening potentially malformed files, so void the return value 176 | (void)rootDir()->addEntryV2(entry); // Ar files don't support directories, so everything in root 177 | dev->seek(dev->pos() + size); // Skip contents 178 | } 179 | 180 | return true; 181 | } 182 | 183 | bool KAr::closeArchive() 184 | { 185 | // Close the archive 186 | return true; 187 | } 188 | 189 | void KAr::virtual_hook(int id, void *data) 190 | { 191 | KArchive::virtual_hook(id, data); 192 | } 193 | -------------------------------------------------------------------------------- /LICENSES/CC0-1.0.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /autotests/kcompressiondevicetest.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE project 2 | SPDX-FileCopyrightText: 2015 Luiz Romário Santana Rios 3 | 4 | SPDX-License-Identifier: LGPL-2.0-or-later 5 | */ 6 | 7 | #include "kcompressiondevicetest.h" 8 | #include "kcompressiondevice_p.h" 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | QTEST_MAIN(KCompressionDeviceTest) 22 | 23 | static QString archiveFileName(const QString &extension) 24 | { 25 | return QFINDTESTDATA(QString("kcompressiondevice_test.%1").arg(extension)); 26 | } 27 | 28 | QNetworkReply *KCompressionDeviceTest::getArchive(const QString &extension) 29 | { 30 | const QString kcompressionTest = archiveFileName(extension); 31 | QNetworkReply *r = qnam.get(QNetworkRequest(QUrl::fromLocalFile(kcompressionTest))); 32 | 33 | QEventLoop l; 34 | connect(&qnam, &QNetworkAccessManager::finished, &l, &QEventLoop::quit); 35 | l.exec(); 36 | 37 | return r; 38 | } 39 | 40 | QString KCompressionDeviceTest::formatExtension(KCompressionDevice::CompressionType type) const 41 | { 42 | switch (type) { 43 | case KCompressionDevice::GZip: 44 | return "tar.gz"; 45 | case KCompressionDevice::BZip2: 46 | return "tar.bz2"; 47 | case KCompressionDevice::Lz: 48 | return "tar.lz"; 49 | case KCompressionDevice::Xz: 50 | return "tar.xz"; 51 | case KCompressionDevice::Zstd: 52 | return "tar.zst"; 53 | case KCompressionDevice::None: 54 | return QString(); 55 | } 56 | return QString(); // silence compiler warning 57 | } 58 | 59 | void KCompressionDeviceTest::setDeviceToArchive(QIODevice *d, KCompressionDevice::CompressionType type) 60 | { 61 | KCompressionDevice *devRawPtr = new KCompressionDevice(d, true, type); 62 | archive.reset(new KTar(devRawPtr)); 63 | device.reset(devRawPtr); 64 | } 65 | 66 | void KCompressionDeviceTest::testBufferedDevice(KCompressionDevice::CompressionType type) 67 | { 68 | QNetworkReply *r = getArchive(formatExtension(type)); 69 | const QByteArray data = r->readAll(); 70 | QVERIFY(!data.isEmpty()); 71 | const int expectedSize = QFileInfo(archiveFileName(formatExtension(type))).size(); 72 | QVERIFY(expectedSize > 0); 73 | QCOMPARE(data.size(), expectedSize); 74 | QBuffer *b = new QBuffer; 75 | b->setData(data); 76 | 77 | setDeviceToArchive(b, type); 78 | testExtraction(); 79 | } 80 | 81 | void KCompressionDeviceTest::testExtraction() 82 | { 83 | QTemporaryDir temp; 84 | QString oldCurrentDir = QDir::currentPath(); 85 | QDir::setCurrent(temp.path()); 86 | 87 | QVERIFY(archive->open(QIODevice::ReadOnly)); 88 | QVERIFY(archive->directory()->copyTo(".")); 89 | QVERIFY(QDir("examples").exists()); 90 | QVERIFY(QDir("examples/bzip2gzip").exists()); 91 | QVERIFY(QDir("examples/helloworld").exists()); 92 | QVERIFY(QDir("examples/tarlocalfiles").exists()); 93 | QVERIFY(QDir("examples/unzipper").exists()); 94 | 95 | const QStringList fileList = { 96 | QStringLiteral("examples/bzip2gzip/CMakeLists.txt"), 97 | QStringLiteral("examples/bzip2gzip/main.cpp"), 98 | QStringLiteral("examples/helloworld/CMakeLists.txt"), 99 | QStringLiteral("examples/helloworld/main.cpp"), 100 | QStringLiteral("examples/tarlocalfiles/CMakeLists.txt"), 101 | QStringLiteral("examples/tarlocalfiles/main.cpp"), 102 | QStringLiteral("examples/unzipper/CMakeLists.txt"), 103 | QStringLiteral("examples/unzipper/main.cpp"), 104 | }; 105 | 106 | for (const QString &s : fileList) { 107 | QFileInfo extractedFile(s); 108 | QFileInfo sourceFile(QFINDTESTDATA("../" + s)); 109 | 110 | QVERIFY(extractedFile.exists()); 111 | QCOMPARE(extractedFile.size(), sourceFile.size()); 112 | } 113 | QDir::setCurrent(oldCurrentDir); 114 | } 115 | 116 | void KCompressionDeviceTest::regularKTarUsage() 117 | { 118 | archive.reset(new KTar(QFINDTESTDATA("kcompressiondevice_test.tar.gz"))); 119 | device.reset(); 120 | 121 | testExtraction(); 122 | } 123 | 124 | void KCompressionDeviceTest::testGZipBufferedDevice() 125 | { 126 | testBufferedDevice(KCompressionDevice::GZip); 127 | } 128 | 129 | void KCompressionDeviceTest::testBZip2BufferedDevice() 130 | { 131 | #if HAVE_BZIP2_SUPPORT 132 | testBufferedDevice(KCompressionDevice::BZip2); 133 | #else 134 | QSKIP("This test needs bzip2 support"); 135 | #endif 136 | } 137 | 138 | void KCompressionDeviceTest::testLzBufferedDevice() 139 | { 140 | #ifdef HAVE_LZIP_SUPPORT_FILE 141 | testBufferedDevice(KCompressionDevice::Lz); 142 | #else 143 | QSKIP("This test needs lzip support"); 144 | #endif 145 | } 146 | 147 | void KCompressionDeviceTest::testXzBufferedDevice() 148 | { 149 | #if HAVE_XZ_SUPPORT 150 | testBufferedDevice(KCompressionDevice::Xz); 151 | #else 152 | QSKIP("This test needs xz support"); 153 | #endif 154 | } 155 | 156 | void KCompressionDeviceTest::testZstdBufferedDevice() 157 | { 158 | #ifdef HAVE_ZSTD_SUPPORT_FILE 159 | testBufferedDevice(KCompressionDevice::Zstd); 160 | #else 161 | QSKIP("This test needs zstd support"); 162 | #endif 163 | } 164 | 165 | void KCompressionDeviceTest::testWriteErrorOnOpen() 166 | { 167 | // GIVEN 168 | QString fileName("/I/do/not/exist/kcompressiondevicetest-write.gz"); 169 | KCompressionDevice dev(fileName, KCompressionDevice::GZip); 170 | // WHEN 171 | QVERIFY(!dev.open(QIODevice::WriteOnly)); 172 | // THEN 173 | QCOMPARE(dev.error(), QFileDevice::OpenError); 174 | #ifdef Q_OS_WIN 175 | QCOMPARE(dev.errorString(), QStringLiteral("The system cannot find the path specified.")); 176 | #else 177 | QCOMPARE(dev.errorString(), QStringLiteral("No such file or directory")); 178 | #endif 179 | } 180 | 181 | void KCompressionDeviceTest::testWriteErrorOnClose() 182 | { 183 | // GIVEN 184 | QFile file("kcompressiondevicetest-write.gz"); 185 | KCompressionDevice dev(&file, false, KCompressionDevice::GZip); 186 | QVERIFY(dev.open(QIODevice::WriteOnly)); 187 | const QByteArray data = "Hello world"; 188 | QCOMPARE(dev.write(data), data.size()); 189 | // This is nasty, it's just a way to try and trigger an error on flush, without filling up a partition first ;) 190 | file.close(); 191 | QVERIFY(file.open(QIODevice::ReadOnly)); 192 | QTest::ignoreMessage(QtWarningMsg, "QIODevice::write (QFile, \"kcompressiondevicetest-write.gz\"): ReadOnly device"); 193 | 194 | // WHEN 195 | dev.close(); // I want a QVERIFY here... https://bugreports.qt.io/browse/QTBUG-70033 196 | 197 | // THEN 198 | QCOMPARE(int(dev.error()), int(QFileDevice::WriteError)); 199 | } 200 | 201 | void KCompressionDeviceTest::testSeekReadUncompressedBuffer_data() 202 | { 203 | QTest::addColumn("dataSize"); 204 | QTest::addColumn("realDataPos"); 205 | QTest::newRow("1.5buffer") << BUFFER_SIZE + BUFFER_SIZE / 2 << BUFFER_SIZE; 206 | QTest::newRow("5seekbuffer") << 5 * SEEK_BUFFER_SIZE << 4 * SEEK_BUFFER_SIZE; 207 | } 208 | 209 | void KCompressionDeviceTest::testSeekReadUncompressedBuffer() 210 | { 211 | QFETCH(int, dataSize); 212 | QFETCH(int, realDataPos); 213 | 214 | QByteArray ba(dataSize, 0); 215 | 216 | // all data is zero except after realDataPos that it's 0 to 9 217 | for (int i = 0; i < 10; ++i) { 218 | ba[realDataPos + i] = i; 219 | } 220 | 221 | QBuffer b; 222 | b.setData(ba); 223 | QVERIFY(b.open(QIODevice::ReadOnly)); 224 | 225 | KCompressionDevice kcd(&b, false, KCompressionDevice::GZip); 226 | QVERIFY(kcd.open(QIODevice::ReadOnly)); 227 | QVERIFY(kcd.seek(realDataPos)); 228 | 229 | // the 10 bytes after realDataPos should be 0 to 9 230 | const QByteArray kcdData = kcd.read(10); 231 | QCOMPARE(kcdData.size(), 10); 232 | for (int i = 0; i < kcdData.size(); ++i) { 233 | QCOMPARE(kcdData[i], i); 234 | } 235 | } 236 | 237 | #include "moc_kcompressiondevicetest.cpp" 238 | -------------------------------------------------------------------------------- /src/kxzfilter.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of the KDE libraries 2 | SPDX-FileCopyrightText: 2007-2008 Per Øyvind Karlsen 3 | 4 | Based on kbzip2filter: 5 | SPDX-FileCopyrightText: 2000-2005 David Faure 6 | 7 | SPDX-License-Identifier: LGPL-2.0-or-later 8 | */ 9 | 10 | #include "kxzfilter.h" 11 | #include "loggingcategory.h" 12 | 13 | #if HAVE_XZ_SUPPORT 14 | extern "C" { 15 | #include 16 | } 17 | 18 | #include 19 | 20 | #include 21 | 22 | class Q_DECL_HIDDEN KXzFilter::Private 23 | { 24 | public: 25 | Private() 26 | : isInitialized(false) 27 | { 28 | memset(&zStream, 0, sizeof(zStream)); 29 | mode = 0; 30 | } 31 | 32 | lzma_stream zStream; 33 | int mode; 34 | bool isInitialized; 35 | KXzFilter::Flag flag; 36 | }; 37 | 38 | KXzFilter::KXzFilter() 39 | : d(new Private) 40 | { 41 | } 42 | 43 | KXzFilter::~KXzFilter() 44 | { 45 | delete d; 46 | } 47 | 48 | bool KXzFilter::init(int mode) 49 | { 50 | QList props; 51 | return init(mode, AUTO, props); 52 | } 53 | 54 | static void freeFilters(lzma_filter filters[]) 55 | { 56 | for (int i = 0; filters[i].id != LZMA_VLI_UNKNOWN; i++) { 57 | free(filters[i].options); 58 | } 59 | } 60 | 61 | bool KXzFilter::init(int mode, Flag flag, const QList &properties) 62 | { 63 | if (d->isInitialized) { 64 | terminate(); 65 | } 66 | 67 | d->flag = flag; 68 | lzma_ret result; 69 | d->zStream.next_in = nullptr; 70 | d->zStream.avail_in = 0; 71 | if (mode == QIODevice::ReadOnly) { 72 | lzma_filter filters[5]; 73 | const auto filtersCleanupGuard = qScopeGuard([&filters] { 74 | freeFilters(filters); 75 | }); 76 | 77 | filters[0].id = LZMA_VLI_UNKNOWN; 78 | 79 | switch (flag) { 80 | case AUTO: 81 | /* We set the memlimit for decompression to 100MiB which should be 82 | * more than enough to be sufficient for level 9 which requires 65 MiB. 83 | */ 84 | result = lzma_auto_decoder(&d->zStream, 100 << 20, 0); 85 | if (result != LZMA_OK) { 86 | qCWarning(KArchiveLog) << "lzma_auto_decoder returned" << result; 87 | return false; 88 | } 89 | break; 90 | case LZMA: { 91 | filters[0].id = LZMA_FILTER_LZMA1; 92 | filters[0].options = nullptr; 93 | filters[1].id = LZMA_VLI_UNKNOWN; 94 | filters[1].options = nullptr; 95 | 96 | if (properties.size() != 5) { 97 | qCWarning(KArchiveLog) << "KXzFilter::init: LZMA unexpected number of properties" << properties.size(); 98 | return false; 99 | } 100 | unsigned char props[5]; 101 | for (int i = 0; i < properties.size(); ++i) { 102 | props[i] = properties[i]; 103 | } 104 | 105 | result = lzma_properties_decode(&filters[0], nullptr, props, sizeof(props)); 106 | if (result != LZMA_OK) { 107 | qCWarning(KArchiveLog) << "lzma_properties_decode returned" << result; 108 | return false; 109 | } 110 | break; 111 | } 112 | case LZMA2: { 113 | filters[0].id = LZMA_FILTER_LZMA2; 114 | filters[0].options = nullptr; 115 | filters[1].id = LZMA_VLI_UNKNOWN; 116 | filters[1].options = nullptr; 117 | 118 | if (properties.size() != 1) { 119 | qCWarning(KArchiveLog) << "KXzFilter::init: LZMA2 unexpected number of properties" << properties.size(); 120 | return false; 121 | } 122 | unsigned char props[1]; 123 | props[0] = properties[0]; 124 | 125 | result = lzma_properties_decode(&filters[0], nullptr, props, sizeof(props)); 126 | if (result != LZMA_OK) { 127 | qCWarning(KArchiveLog) << "lzma_properties_decode returned" << result; 128 | return false; 129 | } 130 | break; 131 | } 132 | case BCJ: { 133 | filters[0].id = LZMA_FILTER_X86; 134 | filters[0].options = nullptr; 135 | 136 | unsigned char props[5] = {0x5d, 0x00, 0x00, 0x08, 0x00}; 137 | filters[1].id = LZMA_FILTER_LZMA1; 138 | filters[1].options = nullptr; 139 | result = lzma_properties_decode(&filters[1], nullptr, props, sizeof(props)); 140 | if (result != LZMA_OK) { 141 | qCWarning(KArchiveLog) << "lzma_properties_decode1 returned" << result; 142 | return false; 143 | } 144 | 145 | filters[2].id = LZMA_VLI_UNKNOWN; 146 | filters[2].options = nullptr; 147 | 148 | break; 149 | } 150 | case POWERPC: 151 | case IA64: 152 | case ARM: 153 | case ARMTHUMB: 154 | case SPARC: 155 | // qCDebug(KArchiveLog) << "flag" << flag << "props size" << properties.size(); 156 | break; 157 | } 158 | 159 | if (flag != AUTO) { 160 | result = lzma_raw_decoder(&d->zStream, filters); 161 | if (result != LZMA_OK) { 162 | qCWarning(KArchiveLog) << "lzma_raw_decoder returned" << result; 163 | return false; 164 | } 165 | } 166 | 167 | } else if (mode == QIODevice::WriteOnly) { 168 | if (flag == AUTO) { 169 | result = lzma_easy_encoder(&d->zStream, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC32); 170 | } else { 171 | lzma_filter filters[5]; 172 | if (flag == LZMA2) { 173 | lzma_options_lzma lzma_opt; 174 | lzma_lzma_preset(&lzma_opt, LZMA_PRESET_DEFAULT); 175 | 176 | filters[0].id = LZMA_FILTER_LZMA2; 177 | filters[0].options = &lzma_opt; 178 | filters[1].id = LZMA_VLI_UNKNOWN; 179 | filters[1].options = nullptr; 180 | } 181 | result = lzma_raw_encoder(&d->zStream, filters); 182 | } 183 | if (result != LZMA_OK) { 184 | qCWarning(KArchiveLog) << "lzma_easy_encoder returned" << result; 185 | return false; 186 | } 187 | } else { 188 | // qCWarning(KArchiveLog) << "Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported"; 189 | return false; 190 | } 191 | d->mode = mode; 192 | d->isInitialized = true; 193 | return true; 194 | } 195 | 196 | int KXzFilter::mode() const 197 | { 198 | return d->mode; 199 | } 200 | 201 | bool KXzFilter::terminate() 202 | { 203 | if (d->mode == QIODevice::ReadOnly || d->mode == QIODevice::WriteOnly) { 204 | lzma_end(&d->zStream); 205 | } else { 206 | // qCWarning(KArchiveLog) << "Unsupported mode " << d->mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported"; 207 | return false; 208 | } 209 | d->isInitialized = false; 210 | return true; 211 | } 212 | 213 | void KXzFilter::reset() 214 | { 215 | // qCDebug(KArchiveLog) << "KXzFilter::reset"; 216 | // liblzma doesn't have a reset call... 217 | terminate(); 218 | init(d->mode); 219 | } 220 | 221 | void KXzFilter::setOutBuffer(char *data, uint maxlen) 222 | { 223 | d->zStream.avail_out = maxlen; 224 | d->zStream.next_out = (uint8_t *)data; 225 | } 226 | 227 | void KXzFilter::setInBuffer(const char *data, unsigned int size) 228 | { 229 | d->zStream.avail_in = size; 230 | d->zStream.next_in = (uint8_t *)const_cast(data); 231 | } 232 | 233 | int KXzFilter::inBufferAvailable() const 234 | { 235 | return d->zStream.avail_in; 236 | } 237 | 238 | int KXzFilter::outBufferAvailable() const 239 | { 240 | return d->zStream.avail_out; 241 | } 242 | 243 | KXzFilter::Result KXzFilter::uncompress() 244 | { 245 | // qCDebug(KArchiveLog) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out =" << outBufferAvailable(); 246 | lzma_ret result; 247 | result = lzma_code(&d->zStream, LZMA_RUN); 248 | 249 | /*if (result != LZMA_OK) { 250 | qCDebug(KArchiveLog) << "lzma_code returned " << result; 251 | //qCDebug(KArchiveLog) << "KXzFilter::uncompress " << ( result == LZMA_STREAM_END ? KFilterBase::End : KFilterBase::Error ); 252 | }*/ 253 | 254 | switch (result) { 255 | case LZMA_OK: 256 | return KFilterBase::Ok; 257 | case LZMA_STREAM_END: 258 | return KFilterBase::End; 259 | default: 260 | return KFilterBase::Error; 261 | } 262 | } 263 | 264 | KXzFilter::Result KXzFilter::compress(bool finish) 265 | { 266 | // qCDebug(KArchiveLog) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable(); 267 | lzma_ret result = lzma_code(&d->zStream, finish ? LZMA_FINISH : LZMA_RUN); 268 | switch (result) { 269 | case LZMA_OK: 270 | return KFilterBase::Ok; 271 | break; 272 | case LZMA_STREAM_END: 273 | // qCDebug(KArchiveLog) << " lzma_code returned " << result; 274 | return KFilterBase::End; 275 | break; 276 | default: 277 | // qCDebug(KArchiveLog) << " lzma_code returned " << result; 278 | return KFilterBase::Error; 279 | break; 280 | } 281 | } 282 | 283 | #endif /* HAVE_XZ_SUPPORT */ 284 | -------------------------------------------------------------------------------- /autotests/data/corpus/sample.tar: -------------------------------------------------------------------------------- 1 | ./PaxHeaders/file.txt0000644000000000000000000000013215014631177011756 xustar0030 mtime=1748185727.894547981 2 | 30 atime=1748185731.806963004 3 | 30 ctime=1748185727.895962982 4 | file.txt0000644000175000017500000000001615014631177011574 0ustar00azharazharHello, World! 5 | --------------------------------------------------------------------------------