omnizip 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +32 -0
- data/.rubocop_todo.yml +754 -0
- data/COPYING +502 -0
- data/Gemfile +17 -0
- data/LICENSE +12 -0
- data/README.adoc +1045 -0
- data/Rakefile +12 -0
- data/benchmark/README.md +260 -0
- data/benchmark/benchmark_suite.rb +125 -0
- data/benchmark/compression_bench.rb +181 -0
- data/benchmark/filter_bench.rb +180 -0
- data/benchmark/models/benchmark_result.rb +59 -0
- data/benchmark/models/comparison_result.rb +69 -0
- data/benchmark/profile_suite.rb +167 -0
- data/benchmark/reporter.rb +150 -0
- data/benchmark/run_benchmarks.rb +66 -0
- data/benchmark/test_data.rb +137 -0
- data/config/formats/rar3_spec.yml +91 -0
- data/config/formats/rar5_spec.yml +102 -0
- data/docs/.github/workflows/docs.yml +142 -0
- data/docs/.gitignore +21 -0
- data/docs/.lychee.toml +67 -0
- data/docs/Gemfile +13 -0
- data/docs/RAR_WRITE_SUPPORT.md +26 -0
- data/docs/README.md +101 -0
- data/docs/_config.yml +112 -0
- data/docs/assets/logo.svg +1 -0
- data/docs/assets/omnizip-logo.pdf +1540 -11
- data/docs/comparison/feature-matrix.adoc +694 -0
- data/docs/comparison/index.adoc +113 -0
- data/docs/comparison/vs-7zip.adoc +309 -0
- data/docs/comparison/vs-peazip.adoc +77 -0
- data/docs/comparison/vs-rubyzip.adoc +342 -0
- data/docs/comparison/vs-winrar.adoc +100 -0
- data/docs/compatibility.adoc +579 -0
- data/docs/concepts/index.adoc +129 -0
- data/docs/developer/architecture.adoc +256 -0
- data/docs/developer/contributing.adoc +158 -0
- data/docs/developer/index.adoc +25 -0
- data/docs/developer/testing.adoc +212 -0
- data/docs/getting-started/basic-usage.adoc +271 -0
- data/docs/getting-started/index.adoc +42 -0
- data/docs/getting-started/installation.adoc +138 -0
- data/docs/getting-started/quick-start.adoc +185 -0
- data/docs/getting-started/your-first-archive.adoc +218 -0
- data/docs/guides/advanced-features/encryption.adoc +300 -0
- data/docs/guides/advanced-features/index.adoc +49 -0
- data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
- data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
- data/docs/guides/advanced-features/streaming.adoc +212 -0
- data/docs/guides/archive-formats/gzip-format.adoc +107 -0
- data/docs/guides/archive-formats/index.adoc +130 -0
- data/docs/guides/archive-formats/rar-format.adoc +104 -0
- data/docs/guides/archive-formats/rar5.adoc +521 -0
- data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
- data/docs/guides/archive-formats/tar-format.adoc +106 -0
- data/docs/guides/archive-formats/xz-format.adoc +118 -0
- data/docs/guides/archive-formats/zip-format.adoc +35 -0
- data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
- data/docs/guides/compression-algorithms/deflate.adoc +319 -0
- data/docs/guides/compression-algorithms/index.adoc +190 -0
- data/docs/guides/compression-algorithms/lzma.adoc +398 -0
- data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
- data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
- data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
- data/docs/guides/creating-archives.adoc +354 -0
- data/docs/guides/extracting-archives.adoc +53 -0
- data/docs/guides/format-conversion.adoc +64 -0
- data/docs/guides/index.adoc +49 -0
- data/docs/guides/migration-rubyzip.adoc +217 -0
- data/docs/guides/parity-archives.adoc +605 -0
- data/docs/guides/performance-tuning.adoc +88 -0
- data/docs/index.adoc +218 -0
- data/docs/lychee.toml +67 -0
- data/docs/reference/api/overview.adoc +188 -0
- data/docs/reference/cli/compress-command.adoc +114 -0
- data/docs/reference/cli/overview.adoc +140 -0
- data/docs/reference/index.adoc +26 -0
- data/docs/resources/faq.adoc +185 -0
- data/docs/resources/quick-reference.adoc +222 -0
- data/docs/troubleshooting/index.adoc +208 -0
- data/examples/api_comparison.rb +205 -0
- data/examples/deflate64_example.rb +96 -0
- data/examples/par2_demo.rb +121 -0
- data/examples/quick_start_native.rb +150 -0
- data/examples/quick_start_rubyzip.rb +115 -0
- data/examples/rubyzip_compatibility_demo.rb +194 -0
- data/exe/omnizip +27 -0
- data/lib/omnizip/algorithm.rb +130 -0
- data/lib/omnizip/algorithm_registry.rb +86 -0
- data/lib/omnizip/algorithms/.keep +0 -0
- data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
- data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
- data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
- data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
- data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
- data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
- data/lib/omnizip/algorithms/bzip2.rb +130 -0
- data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
- data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
- data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
- data/lib/omnizip/algorithms/deflate.rb +128 -0
- data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
- data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
- data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
- data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
- data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
- data/lib/omnizip/algorithms/deflate64.rb +109 -0
- data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
- data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
- data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
- data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
- data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
- data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
- data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
- data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
- data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
- data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
- data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
- data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
- data/lib/omnizip/algorithms/lzma/match.rb +32 -0
- data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
- data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
- data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
- data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
- data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
- data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
- data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
- data/lib/omnizip/algorithms/lzma/state.rb +127 -0
- data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
- data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
- data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
- data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
- data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
- data/lib/omnizip/algorithms/lzma.rb +238 -0
- data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
- data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
- data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
- data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
- data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
- data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
- data/lib/omnizip/algorithms/lzma2.rb +141 -0
- data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
- data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
- data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
- data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
- data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
- data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
- data/lib/omnizip/algorithms/ppmd7.rb +116 -0
- data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
- data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
- data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
- data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
- data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
- data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
- data/lib/omnizip/algorithms/ppmd8.rb +82 -0
- data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
- data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
- data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
- data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
- data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
- data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
- data/lib/omnizip/algorithms/zstandard.rb +138 -0
- data/lib/omnizip/buffer/memory_archive.rb +251 -0
- data/lib/omnizip/buffer/memory_extractor.rb +224 -0
- data/lib/omnizip/buffer.rb +176 -0
- data/lib/omnizip/checksum_registry.rb +114 -0
- data/lib/omnizip/checksums/crc32.rb +100 -0
- data/lib/omnizip/checksums/crc64.rb +101 -0
- data/lib/omnizip/checksums/crc_base.rb +158 -0
- data/lib/omnizip/checksums/verifier.rb +131 -0
- data/lib/omnizip/chunked/memory_manager.rb +194 -0
- data/lib/omnizip/chunked/reader.rb +78 -0
- data/lib/omnizip/chunked/writer.rb +120 -0
- data/lib/omnizip/chunked.rb +129 -0
- data/lib/omnizip/cli/output_formatter.rb +104 -0
- data/lib/omnizip/cli.rb +572 -0
- data/lib/omnizip/commands/.keep +0 -0
- data/lib/omnizip/commands/archive_create_command.rb +427 -0
- data/lib/omnizip/commands/archive_extract_command.rb +272 -0
- data/lib/omnizip/commands/archive_list_command.rb +218 -0
- data/lib/omnizip/commands/archive_repair_command.rb +131 -0
- data/lib/omnizip/commands/archive_verify_command.rb +117 -0
- data/lib/omnizip/commands/compress_command.rb +117 -0
- data/lib/omnizip/commands/decompress_command.rb +120 -0
- data/lib/omnizip/commands/list_command.rb +53 -0
- data/lib/omnizip/commands/metadata_command.rb +153 -0
- data/lib/omnizip/commands/parity_create_command.rb +122 -0
- data/lib/omnizip/commands/parity_repair_command.rb +122 -0
- data/lib/omnizip/commands/parity_verify_command.rb +124 -0
- data/lib/omnizip/commands/profile_list_command.rb +56 -0
- data/lib/omnizip/commands/profile_show_command.rb +44 -0
- data/lib/omnizip/convenience.rb +359 -0
- data/lib/omnizip/converter/conversion_registry.rb +49 -0
- data/lib/omnizip/converter/conversion_strategy.rb +121 -0
- data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
- data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
- data/lib/omnizip/converter.rb +105 -0
- data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
- data/lib/omnizip/crypto/aes256/constants.rb +28 -0
- data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
- data/lib/omnizip/crypto/aes256.rb +102 -0
- data/lib/omnizip/error.rb +106 -0
- data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
- data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
- data/lib/omnizip/eta/rate_calculator.rb +104 -0
- data/lib/omnizip/eta/sample_history.rb +143 -0
- data/lib/omnizip/eta/time_estimator.rb +106 -0
- data/lib/omnizip/eta.rb +63 -0
- data/lib/omnizip/extraction/filter_chain.rb +177 -0
- data/lib/omnizip/extraction/glob_pattern.rb +140 -0
- data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
- data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
- data/lib/omnizip/extraction/regex_pattern.rb +50 -0
- data/lib/omnizip/extraction/selective_extractor.rb +240 -0
- data/lib/omnizip/extraction.rb +111 -0
- data/lib/omnizip/file_type/mime_classifier.rb +144 -0
- data/lib/omnizip/file_type.rb +113 -0
- data/lib/omnizip/filter.rb +139 -0
- data/lib/omnizip/filter_pipeline.rb +108 -0
- data/lib/omnizip/filter_registry.rb +166 -0
- data/lib/omnizip/filters/bcj.rb +279 -0
- data/lib/omnizip/filters/bcj2/constants.rb +53 -0
- data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
- data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
- data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
- data/lib/omnizip/filters/bcj2.rb +99 -0
- data/lib/omnizip/filters/bcj_arm.rb +176 -0
- data/lib/omnizip/filters/bcj_arm64.rb +244 -0
- data/lib/omnizip/filters/bcj_ia64.rb +196 -0
- data/lib/omnizip/filters/bcj_ppc.rb +190 -0
- data/lib/omnizip/filters/bcj_sparc.rb +176 -0
- data/lib/omnizip/filters/bcj_x86.rb +193 -0
- data/lib/omnizip/filters/delta.rb +196 -0
- data/lib/omnizip/filters/filter_base.rb +72 -0
- data/lib/omnizip/filters/registry.rb +123 -0
- data/lib/omnizip/filters/xz_delta.rb +258 -0
- data/lib/omnizip/format_detector.rb +162 -0
- data/lib/omnizip/format_registry.rb +59 -0
- data/lib/omnizip/formats/.keep +0 -0
- data/lib/omnizip/formats/bzip2_file.rb +172 -0
- data/lib/omnizip/formats/cpio/constants.rb +55 -0
- data/lib/omnizip/formats/cpio/entry.rb +385 -0
- data/lib/omnizip/formats/cpio/reader.rb +196 -0
- data/lib/omnizip/formats/cpio/writer.rb +234 -0
- data/lib/omnizip/formats/cpio.rb +140 -0
- data/lib/omnizip/formats/format_spec_loader.rb +230 -0
- data/lib/omnizip/formats/gzip.rb +238 -0
- data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
- data/lib/omnizip/formats/iso/directory_record.rb +152 -0
- data/lib/omnizip/formats/iso/joliet.rb +204 -0
- data/lib/omnizip/formats/iso/path_table.rb +125 -0
- data/lib/omnizip/formats/iso/reader.rb +197 -0
- data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
- data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
- data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
- data/lib/omnizip/formats/iso/writer.rb +530 -0
- data/lib/omnizip/formats/iso.rb +140 -0
- data/lib/omnizip/formats/lzip.rb +175 -0
- data/lib/omnizip/formats/lzma_alone.rb +171 -0
- data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
- data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
- data/lib/omnizip/formats/rar/block_parser.rb +243 -0
- data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
- data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
- data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
- data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
- data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
- data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
- data/lib/omnizip/formats/rar/constants.rb +82 -0
- data/lib/omnizip/formats/rar/decompressor.rb +238 -0
- data/lib/omnizip/formats/rar/external_writer.rb +312 -0
- data/lib/omnizip/formats/rar/header.rb +192 -0
- data/lib/omnizip/formats/rar/license_validator.rb +109 -0
- data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
- data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
- data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
- data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
- data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
- data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
- data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
- data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
- data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
- data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
- data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
- data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
- data/lib/omnizip/formats/rar/reader.rb +366 -0
- data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
- data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
- data/lib/omnizip/formats/rar/writer.rb +431 -0
- data/lib/omnizip/formats/rar.rb +205 -0
- data/lib/omnizip/formats/rar3/compressor.rb +73 -0
- data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar3/reader.rb +386 -0
- data/lib/omnizip/formats/rar3/writer.rb +219 -0
- data/lib/omnizip/formats/rar5/compressor.rb +73 -0
- data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar5/reader.rb +342 -0
- data/lib/omnizip/formats/rar5/writer.rb +214 -0
- data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
- data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
- data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
- data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
- data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
- data/lib/omnizip/formats/seven_zip/header.rb +106 -0
- data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
- data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
- data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
- data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
- data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
- data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
- data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
- data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
- data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
- data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
- data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
- data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
- data/lib/omnizip/formats/seven_zip.rb +93 -0
- data/lib/omnizip/formats/tar/constants.rb +73 -0
- data/lib/omnizip/formats/tar/entry.rb +94 -0
- data/lib/omnizip/formats/tar/header.rb +168 -0
- data/lib/omnizip/formats/tar/reader.rb +121 -0
- data/lib/omnizip/formats/tar/writer.rb +216 -0
- data/lib/omnizip/formats/tar.rb +84 -0
- data/lib/omnizip/formats/xz/reader.rb +116 -0
- data/lib/omnizip/formats/xz.rb +237 -0
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
- data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
- data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
- data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
- data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
- data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
- data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
- data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
- data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
- data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
- data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
- data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
- data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
- data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
- data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
- data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
- data/lib/omnizip/formats/zip/constants.rb +69 -0
- data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
- data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
- data/lib/omnizip/formats/zip/reader.rb +250 -0
- data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
- data/lib/omnizip/formats/zip/writer.rb +375 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
- data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
- data/lib/omnizip/formats/zip.rb +50 -0
- data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
- data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
- data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
- data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
- data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
- data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
- data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
- data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
- data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
- data/lib/omnizip/io/buffered_input.rb +146 -0
- data/lib/omnizip/io/buffered_output.rb +105 -0
- data/lib/omnizip/io/stream_manager.rb +115 -0
- data/lib/omnizip/link_handler/hard_link.rb +79 -0
- data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
- data/lib/omnizip/link_handler.rb +124 -0
- data/lib/omnizip/metadata/archive_metadata.rb +114 -0
- data/lib/omnizip/metadata/entry_metadata.rb +146 -0
- data/lib/omnizip/metadata/metadata_editor.rb +171 -0
- data/lib/omnizip/metadata/metadata_registry.rb +64 -0
- data/lib/omnizip/metadata/metadata_validator.rb +99 -0
- data/lib/omnizip/metadata.rb +57 -0
- data/lib/omnizip/models/.keep +0 -0
- data/lib/omnizip/models/algorithm_metadata.rb +73 -0
- data/lib/omnizip/models/compression_options.rb +71 -0
- data/lib/omnizip/models/conversion_options.rb +87 -0
- data/lib/omnizip/models/conversion_result.rb +135 -0
- data/lib/omnizip/models/eta_result.rb +46 -0
- data/lib/omnizip/models/extraction_rule.rb +115 -0
- data/lib/omnizip/models/filter_chain.rb +144 -0
- data/lib/omnizip/models/filter_config.rb +183 -0
- data/lib/omnizip/models/match_result.rb +124 -0
- data/lib/omnizip/models/optimization_suggestion.rb +91 -0
- data/lib/omnizip/models/parallel_options.rb +104 -0
- data/lib/omnizip/models/performance_result.rb +79 -0
- data/lib/omnizip/models/profile_report.rb +82 -0
- data/lib/omnizip/models/progress_options.rb +38 -0
- data/lib/omnizip/models/split_options.rb +116 -0
- data/lib/omnizip/optimization_registry.rb +81 -0
- data/lib/omnizip/parallel/job_queue.rb +209 -0
- data/lib/omnizip/parallel/job_scheduler.rb +203 -0
- data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
- data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
- data/lib/omnizip/parallel/worker_pool.rb +223 -0
- data/lib/omnizip/parallel.rb +149 -0
- data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
- data/lib/omnizip/parity/galois16.rb +145 -0
- data/lib/omnizip/parity/models/creator_packet.rb +73 -0
- data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
- data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
- data/lib/omnizip/parity/models/main_packet.rb +128 -0
- data/lib/omnizip/parity/models/packet.rb +156 -0
- data/lib/omnizip/parity/models/packet_registry.rb +109 -0
- data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
- data/lib/omnizip/parity/par2_creator.rb +531 -0
- data/lib/omnizip/parity/par2_repairer.rb +407 -0
- data/lib/omnizip/parity/par2_verifier.rb +364 -0
- data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
- data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
- data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
- data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
- data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
- data/lib/omnizip/parity.rb +186 -0
- data/lib/omnizip/password/encryption_registry.rb +65 -0
- data/lib/omnizip/password/encryption_strategy.rb +96 -0
- data/lib/omnizip/password/password_validator.rb +129 -0
- data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
- data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
- data/lib/omnizip/password.rb +87 -0
- data/lib/omnizip/pipe/stream_compressor.rb +124 -0
- data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
- data/lib/omnizip/pipe.rb +121 -0
- data/lib/omnizip/platform/ntfs_streams.rb +201 -0
- data/lib/omnizip/platform.rb +189 -0
- data/lib/omnizip/profile/archive_profile.rb +39 -0
- data/lib/omnizip/profile/balanced_profile.rb +33 -0
- data/lib/omnizip/profile/binary_profile.rb +36 -0
- data/lib/omnizip/profile/compression_profile.rb +158 -0
- data/lib/omnizip/profile/custom_profile.rb +157 -0
- data/lib/omnizip/profile/fast_profile.rb +33 -0
- data/lib/omnizip/profile/maximum_profile.rb +33 -0
- data/lib/omnizip/profile/profile_detector.rb +110 -0
- data/lib/omnizip/profile/profile_registry.rb +161 -0
- data/lib/omnizip/profile/text_profile.rb +36 -0
- data/lib/omnizip/profile.rb +190 -0
- data/lib/omnizip/profiler/memory_profiler.rb +66 -0
- data/lib/omnizip/profiler/method_profiler.rb +49 -0
- data/lib/omnizip/profiler/report_generator.rb +169 -0
- data/lib/omnizip/profiler.rb +204 -0
- data/lib/omnizip/progress/callback_reporter.rb +36 -0
- data/lib/omnizip/progress/console_reporter.rb +62 -0
- data/lib/omnizip/progress/log_reporter.rb +91 -0
- data/lib/omnizip/progress/operation_progress.rb +118 -0
- data/lib/omnizip/progress/progress_bar.rb +156 -0
- data/lib/omnizip/progress/progress_reporter.rb +40 -0
- data/lib/omnizip/progress/progress_tracker.rb +190 -0
- data/lib/omnizip/progress/silent_reporter.rb +24 -0
- data/lib/omnizip/progress.rb +127 -0
- data/lib/omnizip/rubyzip_compat.rb +63 -0
- data/lib/omnizip/temp/safe_extract.rb +168 -0
- data/lib/omnizip/temp/temp_file.rb +124 -0
- data/lib/omnizip/temp/temp_file_pool.rb +109 -0
- data/lib/omnizip/temp.rb +181 -0
- data/lib/omnizip/version.rb +5 -0
- data/lib/omnizip/zip/entry.rb +156 -0
- data/lib/omnizip/zip/file.rb +485 -0
- data/lib/omnizip/zip/input_stream.rb +273 -0
- data/lib/omnizip/zip/output_stream.rb +324 -0
- data/lib/omnizip.rb +156 -0
- data/readme-docs/advanced-features.adoc +515 -0
- data/readme-docs/api-usage.adoc +444 -0
- data/readme-docs/architecture.adoc +449 -0
- data/readme-docs/archive-formats.adoc +479 -0
- data/readme-docs/cli-usage.adoc +222 -0
- data/readme-docs/compression-algorithms.adoc +442 -0
- data/readme-docs/compression-profiles.adoc +247 -0
- data/readme-docs/encryption-checksums.adoc +328 -0
- data/readme-docs/format-converter.adoc +325 -0
- data/readme-docs/installation.adoc +228 -0
- data/readme-docs/par2-archives.adoc +608 -0
- data/readme-docs/performance-profiler.adoc +389 -0
- data/readme-docs/preprocessing-filters.adoc +280 -0
- data/xz-file-format-1.2.1.txt +1174 -0
- metadata +617 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "conversion_strategy"
|
|
4
|
+
|
|
5
|
+
module Omnizip
|
|
6
|
+
module Converter
|
|
7
|
+
# Convert ZIP archives to 7-Zip format
|
|
8
|
+
class ZipToSevenZipStrategy < ConversionStrategy
|
|
9
|
+
# Perform ZIP to 7z conversion
|
|
10
|
+
# @return [ConversionResult] Conversion result
|
|
11
|
+
def convert
|
|
12
|
+
start_time = Time.now
|
|
13
|
+
entry_count = 0
|
|
14
|
+
|
|
15
|
+
require_relative "../zip/file"
|
|
16
|
+
require_relative "../formats/seven_zip"
|
|
17
|
+
|
|
18
|
+
# Open source ZIP archive
|
|
19
|
+
Omnizip::Zip::File.open(source_path) do |zip|
|
|
20
|
+
# Collect all entries and their data
|
|
21
|
+
entries_data = collect_entries(zip)
|
|
22
|
+
entry_count = entries_data.size
|
|
23
|
+
|
|
24
|
+
# Create target 7z archive
|
|
25
|
+
create_seven_zip(entries_data)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
create_result(start_time, entry_count)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Get source format
|
|
32
|
+
# @return [Symbol] Source format (:zip)
|
|
33
|
+
def source_format
|
|
34
|
+
:zip
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Get target format
|
|
38
|
+
# @return [Symbol] Target format (:seven_zip)
|
|
39
|
+
def target_format
|
|
40
|
+
:seven_zip
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Check if can convert
|
|
44
|
+
# @param source [String] Source file
|
|
45
|
+
# @param target [String] Target file
|
|
46
|
+
# @return [Boolean] True if can convert
|
|
47
|
+
def self.can_convert?(source, target)
|
|
48
|
+
source.end_with?(".zip") && target.end_with?(".7z")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def collect_entries(zip)
|
|
54
|
+
entries = []
|
|
55
|
+
|
|
56
|
+
zip.entries.each do |entry|
|
|
57
|
+
data = {
|
|
58
|
+
name: entry.name,
|
|
59
|
+
directory: entry.directory?,
|
|
60
|
+
mtime: entry.time,
|
|
61
|
+
content: nil,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
unless entry.directory?
|
|
65
|
+
data[:content] = zip.get_input_stream(entry)
|
|
66
|
+
|
|
67
|
+
if options.preserve_metadata && entry.unix_perms.positive?
|
|
68
|
+
data[:unix_perms] = entry.unix_perms
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
entries << data
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
entries
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def create_seven_zip(entries)
|
|
79
|
+
writer = Omnizip::Formats::SevenZip::Writer.new(target_path)
|
|
80
|
+
|
|
81
|
+
# Set compression options
|
|
82
|
+
compression = options.compression || :lzma2
|
|
83
|
+
level = options.compression_level || 5
|
|
84
|
+
options.solid.nil? || options.solid
|
|
85
|
+
|
|
86
|
+
# Add each entry
|
|
87
|
+
entries.each do |entry_data|
|
|
88
|
+
if entry_data[:directory]
|
|
89
|
+
# 7z doesn't have explicit directory entries
|
|
90
|
+
# Directories are implied by file paths
|
|
91
|
+
next
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
writer.add_data(
|
|
95
|
+
entry_data[:name],
|
|
96
|
+
entry_data[:content],
|
|
97
|
+
algorithm: compression,
|
|
98
|
+
level: level,
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Apply filter if specified
|
|
103
|
+
if options.filter
|
|
104
|
+
add_warning("Filters not yet supported in 7z conversion")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Write the archive
|
|
108
|
+
writer.write
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "models/conversion_options"
|
|
4
|
+
require_relative "models/conversion_result"
|
|
5
|
+
require_relative "converter/conversion_strategy"
|
|
6
|
+
require_relative "converter/zip_to_seven_zip_strategy"
|
|
7
|
+
require_relative "converter/seven_zip_to_zip_strategy"
|
|
8
|
+
require_relative "converter/conversion_registry"
|
|
9
|
+
|
|
10
|
+
module Omnizip
|
|
11
|
+
# Archive format conversion module
|
|
12
|
+
# Provides conversion between different archive formats
|
|
13
|
+
module Converter
|
|
14
|
+
class << self
|
|
15
|
+
# Convert archive from one format to another
|
|
16
|
+
# @param source_path [String] Source archive path
|
|
17
|
+
# @param target_path [String] Target archive path
|
|
18
|
+
# @param options [Hash, ConversionOptions] Conversion options
|
|
19
|
+
# @return [ConversionResult] Conversion result
|
|
20
|
+
def convert(source_path, target_path, **options)
|
|
21
|
+
# Find appropriate strategy first
|
|
22
|
+
strategy_class = ConversionRegistry.find_strategy(source_path,
|
|
23
|
+
target_path)
|
|
24
|
+
unless strategy_class
|
|
25
|
+
raise ArgumentError, "No conversion strategy available for " \
|
|
26
|
+
"#{source_path} -> #{target_path}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Then validate input files
|
|
30
|
+
unless File.exist?(source_path)
|
|
31
|
+
raise Errno::ENOENT, "Source file not found: #{source_path}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Create options object
|
|
35
|
+
opts = options.is_a?(Models::ConversionOptions) ? options : create_options(**options)
|
|
36
|
+
opts.validate
|
|
37
|
+
|
|
38
|
+
# Perform conversion
|
|
39
|
+
strategy = strategy_class.new(source_path, target_path, opts)
|
|
40
|
+
result = strategy.convert
|
|
41
|
+
|
|
42
|
+
# Delete source if requested
|
|
43
|
+
File.delete(source_path) if opts.delete_source && File.exist?(source_path)
|
|
44
|
+
|
|
45
|
+
result
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Convert with explicit options object
|
|
49
|
+
# @param source_path [String] Source archive path
|
|
50
|
+
# @param target_path [String] Target archive path
|
|
51
|
+
# @param options [ConversionOptions] Conversion options
|
|
52
|
+
# @return [ConversionResult] Conversion result
|
|
53
|
+
def convert_with_options(source_path, target_path, options)
|
|
54
|
+
convert(source_path, target_path, options)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Check if conversion is supported
|
|
58
|
+
# @param source_path [String] Source archive path
|
|
59
|
+
# @param target_path [String] Target archive path
|
|
60
|
+
# @return [Boolean] True if conversion is supported
|
|
61
|
+
def supported?(source_path, target_path)
|
|
62
|
+
ConversionRegistry.supported?(source_path, target_path)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Get available conversion strategies
|
|
66
|
+
# @return [Array<Class>] List of strategy classes
|
|
67
|
+
def strategies
|
|
68
|
+
ConversionRegistry.strategies
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Batch convert multiple archives
|
|
72
|
+
# @param sources [Array<String>] Source file paths
|
|
73
|
+
# @param target_format [Symbol] Target format (:zip or :seven_zip)
|
|
74
|
+
# @param options [Hash] Conversion options
|
|
75
|
+
# @yield [result] Optional block called for each conversion
|
|
76
|
+
# @return [Array<ConversionResult>] Conversion results
|
|
77
|
+
def batch_convert(sources, target_format: :seven_zip, **options, &block)
|
|
78
|
+
results = []
|
|
79
|
+
|
|
80
|
+
sources.each do |source|
|
|
81
|
+
target = generate_target_path(source, target_format)
|
|
82
|
+
result = convert(source, target, target_format: target_format,
|
|
83
|
+
**options)
|
|
84
|
+
results << result
|
|
85
|
+
yield(result) if block
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
results
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def create_options(**options)
|
|
94
|
+
Models::ConversionOptions.new(**options)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def generate_target_path(source, target_format)
|
|
98
|
+
base = File.basename(source, File.extname(source))
|
|
99
|
+
dir = File.dirname(source)
|
|
100
|
+
ext = target_format == :seven_zip ? ".7z" : ".zip"
|
|
101
|
+
File.join(dir, base + ext)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
4
|
+
|
|
5
|
+
require "openssl"
|
|
6
|
+
require_relative "constants"
|
|
7
|
+
|
|
8
|
+
module Omnizip
|
|
9
|
+
module Crypto
|
|
10
|
+
class Aes256
|
|
11
|
+
# AES-256-CBC cipher operations
|
|
12
|
+
#
|
|
13
|
+
# Wraps OpenSSL's AES-256-CBC implementation with proper
|
|
14
|
+
# padding and error handling for 7-Zip compatibility.
|
|
15
|
+
class Cipher
|
|
16
|
+
include Constants
|
|
17
|
+
|
|
18
|
+
attr_reader :key, :iv
|
|
19
|
+
|
|
20
|
+
# Initialize cipher with key and IV
|
|
21
|
+
#
|
|
22
|
+
# @param key [String] 32-byte AES-256 key
|
|
23
|
+
# @param iv [String] 16-byte initialization vector
|
|
24
|
+
def initialize(key, iv)
|
|
25
|
+
validate_key_iv(key, iv)
|
|
26
|
+
@key = key
|
|
27
|
+
@iv = iv
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Encrypt data using AES-256-CBC
|
|
31
|
+
#
|
|
32
|
+
# @param plaintext [String] Data to encrypt
|
|
33
|
+
# @return [String] Encrypted data
|
|
34
|
+
def encrypt(plaintext)
|
|
35
|
+
cipher = create_cipher(:encrypt)
|
|
36
|
+
|
|
37
|
+
# Handle empty data - just call final() for padding
|
|
38
|
+
if plaintext.empty?
|
|
39
|
+
return cipher.final
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
result = cipher.update(plaintext)
|
|
43
|
+
result << cipher.final
|
|
44
|
+
|
|
45
|
+
result
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Decrypt data using AES-256-CBC
|
|
49
|
+
#
|
|
50
|
+
# @param ciphertext [String] Encrypted data
|
|
51
|
+
# @return [String] Decrypted data
|
|
52
|
+
def decrypt(ciphertext)
|
|
53
|
+
cipher = create_cipher(:decrypt)
|
|
54
|
+
|
|
55
|
+
# Handle empty or very small ciphertext
|
|
56
|
+
if ciphertext.empty?
|
|
57
|
+
return ""
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
result = cipher.update(ciphertext)
|
|
61
|
+
result << cipher.final
|
|
62
|
+
|
|
63
|
+
result
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
# Validate key and IV parameters
|
|
69
|
+
#
|
|
70
|
+
# @param key [String] Key to validate
|
|
71
|
+
# @param iv [String] IV to validate
|
|
72
|
+
# @return [void]
|
|
73
|
+
# @raise [ArgumentError] If key or IV invalid
|
|
74
|
+
def validate_key_iv(key, iv)
|
|
75
|
+
if key.bytesize != KEY_SIZE
|
|
76
|
+
raise ArgumentError,
|
|
77
|
+
"Key must be #{KEY_SIZE} bytes"
|
|
78
|
+
end
|
|
79
|
+
return unless iv.bytesize != IV_SIZE
|
|
80
|
+
|
|
81
|
+
raise ArgumentError,
|
|
82
|
+
"IV must be #{IV_SIZE} bytes"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Create OpenSSL cipher instance
|
|
86
|
+
#
|
|
87
|
+
# @param mode [Symbol] :encrypt or :decrypt
|
|
88
|
+
# @return [OpenSSL::Cipher] Configured cipher
|
|
89
|
+
def create_cipher(mode)
|
|
90
|
+
cipher = OpenSSL::Cipher.new("AES-256-CBC")
|
|
91
|
+
cipher.send(mode) # Call encrypt or decrypt first
|
|
92
|
+
cipher.key = @key # Then set key
|
|
93
|
+
cipher.iv = @iv # Then set IV
|
|
94
|
+
cipher.padding = 1 # PKCS7 padding
|
|
95
|
+
cipher
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
4
|
+
|
|
5
|
+
module Omnizip
|
|
6
|
+
module Crypto
|
|
7
|
+
class Aes256
|
|
8
|
+
# Constants for AES-256 encryption in 7-Zip format
|
|
9
|
+
module Constants
|
|
10
|
+
# Key size for AES-256 (32 bytes = 256 bits)
|
|
11
|
+
KEY_SIZE = 32
|
|
12
|
+
|
|
13
|
+
# Maximum salt size (16 bytes)
|
|
14
|
+
SALT_SIZE_MAX = 16
|
|
15
|
+
SALT_SIZE = 16
|
|
16
|
+
|
|
17
|
+
# IV size (16 bytes = AES block size)
|
|
18
|
+
IV_SIZE = 16
|
|
19
|
+
BLOCK_SIZE = 16
|
|
20
|
+
|
|
21
|
+
# Default number of SHA-256 cycles (2^19 = 524288)
|
|
22
|
+
DEFAULT_CYCLES_POWER = 19
|
|
23
|
+
MIN_CYCLES_POWER = 0
|
|
24
|
+
MAX_CYCLES_POWER = 24
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
4
|
+
|
|
5
|
+
require "digest/sha2"
|
|
6
|
+
require_relative "constants"
|
|
7
|
+
|
|
8
|
+
module Omnizip
|
|
9
|
+
module Crypto
|
|
10
|
+
class Aes256
|
|
11
|
+
# 7-Zip password-based key derivation
|
|
12
|
+
#
|
|
13
|
+
# Uses SHA-256 iterative hashing to derive encryption keys
|
|
14
|
+
# from passwords. The number of iterations is 2^cycles_power.
|
|
15
|
+
class KeyDerivation
|
|
16
|
+
include Constants
|
|
17
|
+
|
|
18
|
+
# Derive AES-256 key from password
|
|
19
|
+
#
|
|
20
|
+
# This implements 7-Zip's key derivation:
|
|
21
|
+
# 1. Concatenate salt and password
|
|
22
|
+
# 2. Hash with SHA-256 for 2^cycles_power iterations
|
|
23
|
+
# 3. Extract first 32 bytes as key
|
|
24
|
+
#
|
|
25
|
+
# @param password [String] User password
|
|
26
|
+
# @param salt [String] Random salt
|
|
27
|
+
# @param cycles_power [Integer] Power of 2 for iterations
|
|
28
|
+
# @return [String] 32-byte AES-256 key
|
|
29
|
+
def self.derive_key(password, salt, cycles_power = DEFAULT_CYCLES_POWER)
|
|
30
|
+
validate_inputs(password, salt, cycles_power)
|
|
31
|
+
|
|
32
|
+
# Calculate number of iterations
|
|
33
|
+
num_cycles = 1 << cycles_power
|
|
34
|
+
|
|
35
|
+
# Initial input: salt + password (in UTF-16LE for 7-Zip compat)
|
|
36
|
+
# Force binary encoding to make concatenation work
|
|
37
|
+
encoded_password = encode_password(password).force_encoding(Encoding::BINARY)
|
|
38
|
+
input = salt.dup.force_encoding(Encoding::BINARY) + encoded_password
|
|
39
|
+
|
|
40
|
+
# Iterative SHA-256 hashing
|
|
41
|
+
key = perform_hashing(input, num_cycles)
|
|
42
|
+
|
|
43
|
+
# Return first 32 bytes
|
|
44
|
+
key[0, KEY_SIZE]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Validate key derivation inputs
|
|
48
|
+
#
|
|
49
|
+
# @param password [String] Password to validate
|
|
50
|
+
# @param salt [String] Salt to validate
|
|
51
|
+
# @param cycles_power [Integer] Cycles power to validate
|
|
52
|
+
# @return [void]
|
|
53
|
+
# @raise [ArgumentError] If inputs are invalid
|
|
54
|
+
def self.validate_inputs(password, salt, cycles_power)
|
|
55
|
+
if password.nil? || password.empty?
|
|
56
|
+
raise ArgumentError,
|
|
57
|
+
"Password cannot be empty"
|
|
58
|
+
end
|
|
59
|
+
if salt.bytesize != SALT_SIZE
|
|
60
|
+
raise ArgumentError,
|
|
61
|
+
"Salt must be #{SALT_SIZE} bytes"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
return if cycles_power.between?(MIN_CYCLES_POWER, MAX_CYCLES_POWER)
|
|
65
|
+
|
|
66
|
+
raise ArgumentError,
|
|
67
|
+
"Cycles power must be between #{MIN_CYCLES_POWER} and #{MAX_CYCLES_POWER}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Encode password to UTF-16LE (7-Zip format)
|
|
71
|
+
#
|
|
72
|
+
# @param password [String] Password in any encoding
|
|
73
|
+
# @return [String] UTF-16LE encoded password
|
|
74
|
+
def self.encode_password(password)
|
|
75
|
+
password.encode(Encoding::UTF_16LE)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Perform iterative SHA-256 hashing
|
|
79
|
+
#
|
|
80
|
+
# @param input [String] Initial input (salt + password)
|
|
81
|
+
# @param num_cycles [Integer] Number of hash iterations
|
|
82
|
+
# @return [String] Final hash result
|
|
83
|
+
def self.perform_hashing(input, num_cycles)
|
|
84
|
+
digest = Digest::SHA256.new
|
|
85
|
+
result = input
|
|
86
|
+
|
|
87
|
+
num_cycles.times do
|
|
88
|
+
digest.reset
|
|
89
|
+
digest << result
|
|
90
|
+
result = digest.digest
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
result
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private_class_method :validate_inputs, :encode_password,
|
|
97
|
+
:perform_hashing
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
6
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
7
|
+
# to deal in the Software without restriction, including without limitation
|
|
8
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
9
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
10
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
|
13
|
+
# all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
21
|
+
# DEALINGS IN THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
require "openssl"
|
|
24
|
+
require "digest/sha2"
|
|
25
|
+
require_relative "aes256/constants"
|
|
26
|
+
require_relative "aes256/key_derivation"
|
|
27
|
+
require_relative "aes256/cipher"
|
|
28
|
+
|
|
29
|
+
module Omnizip
|
|
30
|
+
module Crypto
|
|
31
|
+
# AES-256 encryption for 7-Zip archives
|
|
32
|
+
#
|
|
33
|
+
# Implements 7-Zip's AES-256-CBC encryption with:
|
|
34
|
+
# - Password-based key derivation (SHA-256)
|
|
35
|
+
# - Configurable salt and cycles
|
|
36
|
+
# - CBC mode with IV
|
|
37
|
+
#
|
|
38
|
+
# This follows 7-Zip's encryption specification.
|
|
39
|
+
class Aes256
|
|
40
|
+
include Constants
|
|
41
|
+
|
|
42
|
+
# Encrypt data with AES-256
|
|
43
|
+
#
|
|
44
|
+
# @param data [String] Data to encrypt
|
|
45
|
+
# @param password [String] Encryption password
|
|
46
|
+
# @param options [Hash] Encryption options
|
|
47
|
+
# @option options [Integer] :num_cycles_power Power of 2 for key
|
|
48
|
+
# derivation iterations
|
|
49
|
+
# @option options [String] :salt Random salt (auto-generated if nil)
|
|
50
|
+
# @option options [String] :iv Initialization vector
|
|
51
|
+
# (auto-generated if nil)
|
|
52
|
+
# @return [Hash] Encrypted data with metadata
|
|
53
|
+
def self.encrypt(data, password, options = {})
|
|
54
|
+
salt = options[:salt] || generate_salt
|
|
55
|
+
iv = options[:iv] || generate_iv
|
|
56
|
+
cycles_power = options[:num_cycles_power] || DEFAULT_CYCLES_POWER
|
|
57
|
+
|
|
58
|
+
key = KeyDerivation.derive_key(password, salt, cycles_power)
|
|
59
|
+
cipher = Cipher.new(key, iv)
|
|
60
|
+
|
|
61
|
+
encrypted_data = cipher.encrypt(data)
|
|
62
|
+
|
|
63
|
+
{
|
|
64
|
+
data: encrypted_data,
|
|
65
|
+
salt: salt,
|
|
66
|
+
iv: iv,
|
|
67
|
+
cycles_power: cycles_power,
|
|
68
|
+
}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Decrypt data with AES-256
|
|
72
|
+
#
|
|
73
|
+
# @param encrypted_data [String] Encrypted data
|
|
74
|
+
# @param password [String] Decryption password
|
|
75
|
+
# @param salt [String] Salt used during encryption
|
|
76
|
+
# @param iv [String] IV used during encryption
|
|
77
|
+
# @param cycles_power [Integer] Cycles power used during encryption
|
|
78
|
+
# @return [String] Decrypted data
|
|
79
|
+
def self.decrypt(encrypted_data, password, salt, iv, cycles_power)
|
|
80
|
+
key = KeyDerivation.derive_key(password, salt, cycles_power)
|
|
81
|
+
cipher = Cipher.new(key, iv)
|
|
82
|
+
|
|
83
|
+
cipher.decrypt(encrypted_data)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Generate random salt
|
|
87
|
+
#
|
|
88
|
+
# @param size [Integer] Salt size in bytes (default 16)
|
|
89
|
+
# @return [String] Random salt
|
|
90
|
+
def self.generate_salt(size = SALT_SIZE)
|
|
91
|
+
OpenSSL::Random.random_bytes(size)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Generate random IV
|
|
95
|
+
#
|
|
96
|
+
# @return [String] Random IV
|
|
97
|
+
def self.generate_iv
|
|
98
|
+
OpenSSL::Random.random_bytes(IV_SIZE)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
4
|
+
|
|
5
|
+
module Omnizip
|
|
6
|
+
# Base error class for all Omnizip errors
|
|
7
|
+
class Error < StandardError
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Error raised when compression fails
|
|
11
|
+
class CompressionError < Error
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Error raised when decompression fails
|
|
15
|
+
class DecompressionError < Error
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Error raised when an algorithm is not found
|
|
19
|
+
class AlgorithmNotFoundError < Error
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Error raised when an unknown algorithm is requested
|
|
23
|
+
class UnknownAlgorithmError < Error
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Error raised when a format is not supported
|
|
27
|
+
class UnsupportedFormatError < Error
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Error raised when format parsing fails
|
|
31
|
+
class FormatError < Error
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Error raised when archive is invalid
|
|
35
|
+
class InvalidArchiveError < Error
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Error raised when I/O operations fail
|
|
39
|
+
class IOError < Error
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Error raised when checksum validation fails
|
|
43
|
+
class ChecksumError < Error
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Error raised when optimization strategy is not found
|
|
47
|
+
class OptimizationNotFound < Error
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Error raised when progress tracking fails
|
|
51
|
+
class ProgressError < Error
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Error raised when ETA calculation fails
|
|
55
|
+
class ETAError < Error
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Error raised when RAR write is attempted without license
|
|
59
|
+
class NotLicensedError < Error
|
|
60
|
+
def initialize(message = default_message)
|
|
61
|
+
super
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def default_message
|
|
67
|
+
<<~MSG
|
|
68
|
+
RAR creation requires a licensed copy of WinRAR.
|
|
69
|
+
|
|
70
|
+
To use RAR creation:
|
|
71
|
+
1. Purchase a WinRAR license from https://www.rarlab.com/
|
|
72
|
+
2. Install WinRAR on your system
|
|
73
|
+
3. Confirm license ownership when prompted
|
|
74
|
+
|
|
75
|
+
Alternatively, use 7z format which provides similar compression
|
|
76
|
+
with no licensing restrictions:
|
|
77
|
+
|
|
78
|
+
Omnizip::Formats::SevenZip.create('archive.7z') do |sz|
|
|
79
|
+
sz.add_directory('files/')
|
|
80
|
+
end
|
|
81
|
+
MSG
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Error raised when RAR executable is not found
|
|
86
|
+
class RarNotAvailableError < Error
|
|
87
|
+
def initialize(message = default_message)
|
|
88
|
+
super
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def default_message
|
|
94
|
+
<<~MSG
|
|
95
|
+
WinRAR executable not found.
|
|
96
|
+
|
|
97
|
+
Please install WinRAR:
|
|
98
|
+
- Windows: Download from https://www.rarlab.com/
|
|
99
|
+
- Linux: Install 'rar' package (requires license)
|
|
100
|
+
- macOS: Install via Homebrew: brew install rar (requires license)
|
|
101
|
+
|
|
102
|
+
After installation, ensure 'rar' or 'Rar.exe' is in your PATH.
|
|
103
|
+
MSG
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|