omnizip 0.3.8 → 0.3.10
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 +4 -4
- data/.rubocop_todo.yml +51 -50
- data/Gemfile +2 -0
- data/Rakefile +3 -0
- data/TODO.refactor/00-overview.md +67 -0
- data/TODO.refactor/01-registry-base-class.md +81 -0
- data/TODO.refactor/02-error-hierarchy.md +57 -0
- data/TODO.refactor/03-autoload-entry-point.md +69 -0
- data/TODO.refactor/04-filter-base-consolidation.md +49 -0
- data/TODO.refactor/05-send-private-methods.md +64 -0
- data/TODO.refactor/06-instance-variable-access.md +69 -0
- data/TODO.refactor/07-respond-to-replacement.md +91 -0
- data/TODO.refactor/08-spec-doubles.md +27 -0
- data/TODO.refactor/09-cli-shared-module.md +35 -0
- data/TODO.refactor/10-format-detector-ocp.md +34 -0
- data/TODO.refactor/11-convenience-decoupling.md +38 -0
- data/TODO.refactor/12-thread-safety.md +8 -0
- data/TODO.refactor/13-lutaml-model-migration.md +82 -0
- data/TODO.refactor/14-add-missing-specs.md +25 -0
- data/lib/omnizip/algorithm.rb +0 -2
- data/lib/omnizip/algorithm_registry.rb +29 -57
- data/lib/omnizip/algorithms/lzma/bit_model.rb +1 -1
- data/lib/omnizip/algorithms/lzma/dictionary.rb +4 -3
- data/lib/omnizip/algorithms/lzma/distance_coder.rb +5 -102
- data/lib/omnizip/algorithms/lzma/length_coder.rb +4 -92
- data/lib/omnizip/algorithms/lzma/literal_decoder.rb +4 -119
- data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +2 -0
- data/lib/omnizip/algorithms/lzma/match_finder.rb +59 -29
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +20 -12
- data/lib/omnizip/algorithms/lzma/range_decoder.rb +77 -269
- data/lib/omnizip/algorithms/lzma/range_encoder.rb +0 -9
- data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +0 -15
- data/lib/omnizip/algorithms/lzma/xz_state.rb +0 -8
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +189 -968
- data/lib/omnizip/algorithms/lzma2/encoder.rb +0 -2
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +0 -2
- data/lib/omnizip/algorithms/lzma2/properties.rb +0 -2
- data/lib/omnizip/algorithms/ppmd7/model.rb +1 -1
- data/lib/omnizip/algorithms/ppmd8/context.rb +0 -2
- data/lib/omnizip/algorithms/ppmd8/model.rb +1 -1
- data/lib/omnizip/algorithms/ppmd8.rb +0 -2
- data/lib/omnizip/algorithms/ppmd_base.rb +0 -2
- data/lib/omnizip/algorithms/sevenzip_lzma2.rb +0 -5
- data/lib/omnizip/algorithms/xz_lzma2.rb +0 -5
- data/lib/omnizip/algorithms/zstandard/frame/header.rb +3 -5
- data/lib/omnizip/algorithms.rb +1 -0
- data/lib/omnizip/archive_handler.rb +75 -0
- data/lib/omnizip/archive_handlers/tar_handler.rb +51 -0
- data/lib/omnizip/archive_handlers/zip_handler.rb +73 -0
- data/lib/omnizip/archive_handlers.rb +11 -0
- data/lib/omnizip/checksum_registry.rb +23 -96
- data/lib/omnizip/cli/shared.rb +31 -0
- data/lib/omnizip/cli.rb +6 -43
- data/lib/omnizip/commands/archive_create_command.rb +0 -6
- data/lib/omnizip/commands/archive_extract_command.rb +0 -4
- data/lib/omnizip/commands/archive_list_command.rb +0 -4
- data/lib/omnizip/commands/archive_repair_command.rb +0 -2
- data/lib/omnizip/commands/archive_verify_command.rb +0 -2
- data/lib/omnizip/commands/compress_command.rb +0 -2
- data/lib/omnizip/commands/decompress_command.rb +0 -2
- data/lib/omnizip/commands/list_command.rb +0 -2
- data/lib/omnizip/commands/metadata_command.rb +0 -5
- data/lib/omnizip/commands/parity_create_command.rb +0 -2
- data/lib/omnizip/commands/parity_repair_command.rb +0 -2
- data/lib/omnizip/commands/parity_verify_command.rb +0 -2
- data/lib/omnizip/commands/profile_list_command.rb +0 -2
- data/lib/omnizip/commands/profile_show_command.rb +0 -2
- data/lib/omnizip/convenience.rb +111 -234
- data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +1 -1
- data/lib/omnizip/crypto/aes256/cipher.rb +6 -1
- data/lib/omnizip/error.rb +22 -18
- data/lib/omnizip/filter.rb +5 -3
- data/lib/omnizip/filter_registry.rb +74 -98
- data/lib/omnizip/filters/bcj2/decoder.rb +196 -130
- data/lib/omnizip/filters/bcj2.rb +0 -2
- data/lib/omnizip/filters/bcj_x86.rb +0 -2
- data/lib/omnizip/filters/filter_base.rb +4 -70
- data/lib/omnizip/filters/registry.rb +0 -2
- data/lib/omnizip/format_detector.rb +21 -12
- data/lib/omnizip/format_registry.rb +79 -38
- data/lib/omnizip/formats/bzip2_file.rb +0 -2
- data/lib/omnizip/formats/cpio/entry.rb +0 -2
- data/lib/omnizip/formats/cpio/reader.rb +0 -1
- data/lib/omnizip/formats/cpio/writer.rb +0 -1
- data/lib/omnizip/formats/cpio.rb +0 -1
- data/lib/omnizip/formats/format_spec_loader.rb +1 -1
- data/lib/omnizip/formats/gzip.rb +0 -2
- data/lib/omnizip/formats/iso/directory_record.rb +6 -3
- data/lib/omnizip/formats/iso/reader.rb +2 -2
- data/lib/omnizip/formats/iso.rb +0 -1
- data/lib/omnizip/formats/lzip.rb +5 -25
- data/lib/omnizip/formats/lzma_alone.rb +7 -25
- data/lib/omnizip/formats/msi/cab_extractor.rb +181 -0
- data/lib/omnizip/formats/msi/constants.rb +157 -0
- data/lib/omnizip/formats/msi/directory_resolver.rb +188 -0
- data/lib/omnizip/formats/msi/entry.rb +128 -0
- data/lib/omnizip/formats/msi/reader.rb +301 -0
- data/lib/omnizip/formats/msi/string_pool.rb +246 -0
- data/lib/omnizip/formats/msi/table_parser.rb +244 -0
- data/lib/omnizip/formats/msi.rb +84 -0
- data/lib/omnizip/formats/ole/allocation_table.rb +10 -1
- data/lib/omnizip/formats/ole/dirent.rb +20 -3
- data/lib/omnizip/formats/ole/header.rb +0 -1
- data/lib/omnizip/formats/ole/ranges_io.rb +21 -3
- data/lib/omnizip/formats/ole/storage.rb +19 -4
- data/lib/omnizip/formats/ole.rb +0 -2
- data/lib/omnizip/formats/rar/block_parser.rb +0 -1
- data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +1 -1
- data/lib/omnizip/formats/rar/decompressor.rb +38 -1
- data/lib/omnizip/formats/rar/header.rb +0 -1
- data/lib/omnizip/formats/rar/rar5/writer.rb +0 -2
- data/lib/omnizip/formats/rar/reader.rb +2 -3
- data/lib/omnizip/formats/rar/writer.rb +0 -1
- data/lib/omnizip/formats/rar.rb +2 -2
- data/lib/omnizip/formats/rpm/entry.rb +0 -1
- data/lib/omnizip/formats/rpm/header.rb +3 -8
- data/lib/omnizip/formats/rpm/lead.rb +37 -26
- data/lib/omnizip/formats/rpm/tag.rb +0 -1
- data/lib/omnizip/formats/rpm/writer.rb +0 -1
- data/lib/omnizip/formats/seven_zip/bcj2_stream_decompressor.rb +98 -61
- data/lib/omnizip/formats/seven_zip/coder_chain.rb +1 -2
- data/lib/omnizip/formats/seven_zip/encoded_header.rb +0 -1
- data/lib/omnizip/formats/seven_zip/encrypted_header.rb +0 -1
- data/lib/omnizip/formats/seven_zip/header.rb +0 -1
- data/lib/omnizip/formats/seven_zip/header_encryptor.rb +0 -1
- data/lib/omnizip/formats/seven_zip/header_writer.rb +22 -61
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +1 -1
- data/lib/omnizip/formats/seven_zip/parser.rb +14 -13
- data/lib/omnizip/formats/seven_zip/reader.rb +82 -19
- data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +0 -15
- data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +0 -1
- data/lib/omnizip/formats/seven_zip/writer.rb +22 -68
- data/lib/omnizip/formats/seven_zip.rb +63 -5
- data/lib/omnizip/formats/tar/entry.rb +0 -2
- data/lib/omnizip/formats/tar/header.rb +0 -2
- data/lib/omnizip/formats/tar/reader.rb +0 -2
- data/lib/omnizip/formats/tar/writer.rb +0 -2
- data/lib/omnizip/formats/tar.rb +0 -1
- data/lib/omnizip/formats/xar/entry.rb +0 -1
- data/lib/omnizip/formats/xar/header.rb +0 -1
- data/lib/omnizip/formats/xar/reader.rb +0 -1
- data/lib/omnizip/formats/xar/toc.rb +3 -3
- data/lib/omnizip/formats/xar/writer.rb +11 -3
- data/lib/omnizip/formats/xar.rb +0 -1
- data/lib/omnizip/formats/xz/reader.rb +7 -3
- data/lib/omnizip/formats/xz.rb +3 -40
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +4 -10
- data/lib/omnizip/formats/xz_impl/block_encoder.rb +0 -2
- data/lib/omnizip/formats/xz_impl/block_header_parser.rb +0 -2
- data/lib/omnizip/formats/xz_impl/index_decoder.rb +0 -1
- data/lib/omnizip/formats/xz_impl/index_encoder.rb +0 -1
- data/lib/omnizip/formats/xz_impl/stream_encoder.rb +1 -1
- data/lib/omnizip/formats/xz_impl/stream_footer.rb +0 -1
- data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +0 -1
- data/lib/omnizip/formats/xz_impl/stream_header.rb +0 -1
- data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +0 -2
- data/lib/omnizip/formats/zip/central_directory_header.rb +2 -3
- data/lib/omnizip/formats/zip/end_of_central_directory.rb +0 -2
- data/lib/omnizip/formats/zip/local_file_header.rb +0 -2
- data/lib/omnizip/formats/zip/reader.rb +12 -2
- data/lib/omnizip/formats/zip/writer.rb +73 -1
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +0 -2
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +0 -2
- data/lib/omnizip/formats/zip/zip64_extra_field.rb +0 -2
- data/lib/omnizip/formats/zip.rb +0 -1
- data/lib/omnizip/formats.rb +9 -22
- data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +2 -2
- data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +1 -1
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +0 -6
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +18 -185
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +3 -3
- data/lib/omnizip/io/source.rb +136 -0
- data/lib/omnizip/io.rb +2 -0
- data/lib/omnizip/link_handler.rb +7 -3
- data/lib/omnizip/metadata/metadata_editor.rb +1 -1
- data/lib/omnizip/models/algorithm_metadata.rb +33 -60
- data/lib/omnizip/models/compression_options.rb +40 -53
- data/lib/omnizip/models/conversion_options.rb +9 -18
- data/lib/omnizip/models/parallel_options.rb +25 -0
- data/lib/omnizip/optimization_registry.rb +8 -44
- data/lib/omnizip/parallel/job_scheduler.rb +26 -8
- data/lib/omnizip/parallel/parallel_compressor.rb +10 -20
- data/lib/omnizip/parallel/parallel_extractor.rb +20 -11
- data/lib/omnizip/parity/par2_repairer.rb +5 -5
- data/lib/omnizip/parity/par2_verifier.rb +27 -22
- data/lib/omnizip/parity.rb +4 -4
- data/lib/omnizip/password/encryption_registry.rb +11 -50
- data/lib/omnizip/password/zip_crypto_strategy.rb +1 -4
- data/lib/omnizip/pipe/stream_decompressor.rb +2 -3
- data/lib/omnizip/profile/archive_profile.rb +0 -2
- data/lib/omnizip/profile/binary_profile.rb +0 -2
- data/lib/omnizip/profile/compression_profile.rb +0 -1
- data/lib/omnizip/profile/profile_detector.rb +0 -2
- data/lib/omnizip/profile/text_profile.rb +0 -2
- data/lib/omnizip/profile.rb +0 -2
- data/lib/omnizip/registry.rb +112 -0
- data/lib/omnizip/version.rb +1 -1
- data/lib/omnizip/zip/file.rb +20 -9
- data/lib/omnizip.rb +26 -55
- data/lib/tasks/msi_test_files.rake +66 -0
- metadata +57 -6
|
@@ -58,6 +58,12 @@ module Omnizip
|
|
|
58
58
|
# @return [Hash] Parsed PAR2 metadata
|
|
59
59
|
attr_reader :metadata
|
|
60
60
|
|
|
61
|
+
# @return [Array<Hash>] Parsed file list (one entry per managed file)
|
|
62
|
+
attr_reader :file_list
|
|
63
|
+
|
|
64
|
+
# @return [Array] Available recovery blocks
|
|
65
|
+
attr_reader :recovery_blocks
|
|
66
|
+
|
|
61
67
|
# Initialize verifier
|
|
62
68
|
#
|
|
63
69
|
# @param par2_file [String] Path to .par2 index file
|
|
@@ -125,11 +131,11 @@ module Omnizip
|
|
|
125
131
|
)
|
|
126
132
|
end
|
|
127
133
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
#
|
|
134
|
+
# Parse the PAR2 index file and populate +metadata+, +file_list+,
|
|
135
|
+
# and +recovery_blocks+. Idempotent — safe to call multiple times.
|
|
136
|
+
#
|
|
137
|
+
# @return [void]
|
|
131
138
|
def parse_par2_file
|
|
132
|
-
# Reset state to prevent accumulation if called multiple times
|
|
133
139
|
@metadata = {}
|
|
134
140
|
@file_list = []
|
|
135
141
|
@recovery_blocks = []
|
|
@@ -159,24 +165,6 @@ module Omnizip
|
|
|
159
165
|
load_recovery_volumes
|
|
160
166
|
end
|
|
161
167
|
|
|
162
|
-
# Process packet using model-based approach
|
|
163
|
-
#
|
|
164
|
-
# @param packet [Models::Packet] Parsed packet model
|
|
165
|
-
def process_packet_model(packet)
|
|
166
|
-
case packet
|
|
167
|
-
when Models::MainPacket
|
|
168
|
-
process_main_packet_model(packet)
|
|
169
|
-
when Models::FileDescriptionPacket
|
|
170
|
-
process_file_description_packet_model(packet)
|
|
171
|
-
when Models::IfscPacket
|
|
172
|
-
process_ifsc_packet_model(packet)
|
|
173
|
-
when Models::RecoverySlicePacket
|
|
174
|
-
process_recovery_packet_model(packet)
|
|
175
|
-
when Models::CreatorPacket
|
|
176
|
-
# Creator packets are informational only
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
|
|
180
168
|
# Process main packet model
|
|
181
169
|
#
|
|
182
170
|
# @param packet [Models::MainPacket] Main packet
|
|
@@ -358,6 +346,23 @@ module Omnizip
|
|
|
358
346
|
(file_info[:size].to_f / @metadata[:block_size]).ceil
|
|
359
347
|
end
|
|
360
348
|
end
|
|
349
|
+
|
|
350
|
+
private
|
|
351
|
+
|
|
352
|
+
def process_packet_model(packet)
|
|
353
|
+
case packet
|
|
354
|
+
when Models::MainPacket
|
|
355
|
+
process_main_packet_model(packet)
|
|
356
|
+
when Models::FileDescriptionPacket
|
|
357
|
+
process_file_description_packet_model(packet)
|
|
358
|
+
when Models::IfscPacket
|
|
359
|
+
process_ifsc_packet_model(packet)
|
|
360
|
+
when Models::RecoverySlicePacket
|
|
361
|
+
process_recovery_packet_model(packet)
|
|
362
|
+
when Models::CreatorPacket
|
|
363
|
+
# Creator packets are informational only
|
|
364
|
+
end
|
|
365
|
+
end
|
|
361
366
|
end
|
|
362
367
|
end
|
|
363
368
|
end
|
data/lib/omnizip/parity.rb
CHANGED
|
@@ -175,16 +175,16 @@ module Omnizip
|
|
|
175
175
|
return nil unless File.exist?(par2_file)
|
|
176
176
|
|
|
177
177
|
verifier = Par2Verifier.new(par2_file)
|
|
178
|
-
verifier.
|
|
178
|
+
verifier.parse_par2_file
|
|
179
179
|
|
|
180
|
-
total_blocks = verifier.
|
|
181
|
-
recovery_blocks = verifier.
|
|
180
|
+
total_blocks = verifier.calculate_total_blocks
|
|
181
|
+
recovery_blocks = verifier.recovery_blocks.size
|
|
182
182
|
|
|
183
183
|
{
|
|
184
184
|
par2_file: par2_file,
|
|
185
185
|
block_size: verifier.metadata[:block_size],
|
|
186
186
|
total_blocks: total_blocks,
|
|
187
|
-
file_count: verifier.metadata[:file_count] || verifier.
|
|
187
|
+
file_count: verifier.metadata[:file_count] || verifier.file_list.size,
|
|
188
188
|
recovery_blocks: recovery_blocks,
|
|
189
189
|
redundancy: calculate_redundancy(total_blocks, recovery_blocks),
|
|
190
190
|
}
|
|
@@ -2,64 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
module Omnizip
|
|
4
4
|
module Password
|
|
5
|
-
|
|
6
|
-
class EncryptionRegistry
|
|
7
|
-
@strategies = {}
|
|
8
|
-
|
|
5
|
+
class EncryptionRegistry < Omnizip::Registry
|
|
9
6
|
class << self
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
# @param strategy_class [Class] Strategy class
|
|
13
|
-
def register(name, strategy_class)
|
|
14
|
-
@strategies[name] = strategy_class
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Get a strategy by name
|
|
18
|
-
# @param name [Symbol] Strategy name
|
|
19
|
-
# @return [Class] Strategy class
|
|
20
|
-
# @raise [ArgumentError] If strategy not found
|
|
21
|
-
def get(name)
|
|
22
|
-
strategy = @strategies[name]
|
|
23
|
-
return strategy if strategy
|
|
24
|
-
|
|
25
|
-
raise ArgumentError, "Unknown encryption strategy: #{name}. " \
|
|
26
|
-
"Available: #{@strategies.keys.join(', ')}"
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Check if strategy is registered
|
|
30
|
-
# @param name [Symbol] Strategy name
|
|
31
|
-
# @return [Boolean] True if registered
|
|
32
|
-
def registered?(name)
|
|
33
|
-
@strategies.key?(name)
|
|
7
|
+
def not_found_error_class
|
|
8
|
+
Omnizip::UnknownEncryptionStrategyError
|
|
34
9
|
end
|
|
35
10
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def strategies
|
|
39
|
-
@strategies.keys
|
|
11
|
+
def label
|
|
12
|
+
"Encryption strategy"
|
|
40
13
|
end
|
|
41
14
|
|
|
42
|
-
# Create a strategy instance
|
|
43
|
-
# @param name [Symbol] Strategy name
|
|
44
|
-
# @param password [String] Password
|
|
45
|
-
# @param options [Hash] Strategy options
|
|
46
|
-
# @return [EncryptionStrategy] Strategy instance
|
|
47
15
|
def create(name, password, **options)
|
|
48
|
-
|
|
49
|
-
strategy_class.new(password, **options)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Reset registry (for testing)
|
|
53
|
-
def reset
|
|
54
|
-
@strategies = {}
|
|
16
|
+
get(name).new(password, **options)
|
|
55
17
|
end
|
|
56
18
|
end
|
|
57
19
|
end
|
|
58
|
-
|
|
59
|
-
# Register built-in strategies
|
|
60
|
-
EncryptionRegistry.register(:traditional, ZipCryptoStrategy)
|
|
61
|
-
EncryptionRegistry.register(:zip_crypto, ZipCryptoStrategy)
|
|
62
|
-
EncryptionRegistry.register(:winzip_aes, WinzipAesStrategy)
|
|
63
|
-
EncryptionRegistry.register(:aes256, WinzipAesStrategy)
|
|
64
20
|
end
|
|
65
21
|
end
|
|
22
|
+
|
|
23
|
+
Omnizip::Password::EncryptionRegistry.register(:traditional, Omnizip::Password::ZipCryptoStrategy)
|
|
24
|
+
Omnizip::Password::EncryptionRegistry.register(:zip_crypto, Omnizip::Password::ZipCryptoStrategy)
|
|
25
|
+
Omnizip::Password::EncryptionRegistry.register(:winzip_aes, Omnizip::Password::WinzipAesStrategy)
|
|
26
|
+
Omnizip::Password::EncryptionRegistry.register(:aes256, Omnizip::Password::WinzipAesStrategy)
|
|
@@ -114,10 +114,7 @@ module Omnizip
|
|
|
114
114
|
# @param byte [Integer] Byte to add
|
|
115
115
|
# @return [Integer] Updated CRC
|
|
116
116
|
def crc32_update(crc, byte)
|
|
117
|
-
|
|
118
|
-
crc32.instance_variable_set(:@crc, crc)
|
|
119
|
-
crc32.update(byte.chr)
|
|
120
|
-
crc32.value
|
|
117
|
+
Omnizip::Checksums::Crc32.new.process_byte(crc, byte)
|
|
121
118
|
end
|
|
122
119
|
|
|
123
120
|
# Generate encryption header
|
|
@@ -163,9 +163,8 @@ module Omnizip
|
|
|
163
163
|
end
|
|
164
164
|
|
|
165
165
|
# Preserve timestamp if available
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
end
|
|
166
|
+
mtime = entry.mtime if entry.is_a?(Omnizip::Metadata::EntryMetadata)
|
|
167
|
+
File.utime(mtime, mtime, dest_path) if mtime
|
|
169
168
|
|
|
170
169
|
bytes
|
|
171
170
|
end
|
data/lib/omnizip/profile.rb
CHANGED
|
@@ -161,8 +161,6 @@ module Omnizip
|
|
|
161
161
|
# @param mime_type [String] MIME type string
|
|
162
162
|
# @return [CompressionProfile] Best profile
|
|
163
163
|
def select_best_profile_for_mime(profiles, mime_type)
|
|
164
|
-
require "omnizip/file_type/mime_classifier"
|
|
165
|
-
|
|
166
164
|
# Determine category from MIME type
|
|
167
165
|
category = FileType::MimeClassifier.profile_category(mime_type)
|
|
168
166
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
# Generic, thread-safe registry base class.
|
|
5
|
+
#
|
|
6
|
+
# Subclasses override the hooks +#not_found_error_class+,
|
|
7
|
+
# +#normalize_key+, and +#label+ to specialize behavior. They may also
|
|
8
|
+
# override +#register+ / +#get+ to add domain-specific arguments
|
|
9
|
+
# (e.g., +#register_with_formats+ on FilterRegistry).
|
|
10
|
+
#
|
|
11
|
+
# The base class is intentionally minimal: it owns a Hash store and a
|
|
12
|
+
# Mutex, and exposes the canonical CRUD quintet (+register+, +get+,
|
|
13
|
+
# +registered?+, +available+, +reset!+). Backward-compat aliases are
|
|
14
|
+
# provided so existing call sites keep working.
|
|
15
|
+
class Registry
|
|
16
|
+
class << self
|
|
17
|
+
def not_found_error_class
|
|
18
|
+
Omnizip::Error
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def normalize_key(key)
|
|
22
|
+
key.to_sym
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def label
|
|
26
|
+
return "Item" unless name
|
|
27
|
+
|
|
28
|
+
name.gsub(/^Omnizip::/, "")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def register(key, value)
|
|
32
|
+
synchronize { storage[normalize_key(key)] = value }
|
|
33
|
+
value
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Register a lazy trigger for +key+. When +get(key)+ is called and
|
|
37
|
+
# the key isn't yet in storage, the block is invoked. The block is
|
|
38
|
+
# expected to trigger autoload of the implementation file (e.g., by
|
|
39
|
+
# referencing a constant), which then self-registers.
|
|
40
|
+
def register_lazy(key, &trigger)
|
|
41
|
+
lazy_triggers[normalize_key(key)] = trigger
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def get(key)
|
|
45
|
+
normalized = normalize_key(key)
|
|
46
|
+
value = storage[normalized]
|
|
47
|
+
return value if value
|
|
48
|
+
|
|
49
|
+
trigger = lazy_triggers[normalized]
|
|
50
|
+
if trigger
|
|
51
|
+
synchronize { lazy_triggers.delete(normalized) }
|
|
52
|
+
trigger.call
|
|
53
|
+
value = storage[normalized]
|
|
54
|
+
return value if value
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
raise not_found_error_class,
|
|
58
|
+
"#{label} not registered: #{key.inspect}. " \
|
|
59
|
+
"Available: #{available.join(', ')}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def registered?(key)
|
|
63
|
+
normalized = normalize_key(key)
|
|
64
|
+
storage.key?(normalized) || lazy_triggers.key?(normalized)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def available
|
|
68
|
+
storage.keys
|
|
69
|
+
end
|
|
70
|
+
alias all available
|
|
71
|
+
alias strategies available
|
|
72
|
+
|
|
73
|
+
def reset!
|
|
74
|
+
# Clear stored entries but keep lazy triggers — the builtin
|
|
75
|
+
# autoload list should survive a reset so subsequent +get+ calls
|
|
76
|
+
# for builtins still trigger the autoload (which re-registers).
|
|
77
|
+
synchronize { storage.clear }
|
|
78
|
+
end
|
|
79
|
+
alias clear reset!
|
|
80
|
+
alias clear! reset!
|
|
81
|
+
alias reset reset!
|
|
82
|
+
|
|
83
|
+
def entries
|
|
84
|
+
synchronize { storage.dup }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def each(&block)
|
|
88
|
+
return to_enum(:each) unless block
|
|
89
|
+
|
|
90
|
+
entries.each(&block)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def storage
|
|
96
|
+
@storage ||= {}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def lazy_triggers
|
|
100
|
+
@lazy_triggers ||= {}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def synchronize(&block)
|
|
104
|
+
mutex.synchronize(&block)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def mutex
|
|
108
|
+
@mutex ||= Mutex.new
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
data/lib/omnizip/version.rb
CHANGED
data/lib/omnizip/zip/file.rb
CHANGED
|
@@ -50,7 +50,6 @@ module Omnizip
|
|
|
50
50
|
@modified = false
|
|
51
51
|
@reader = nil
|
|
52
52
|
@writer = nil
|
|
53
|
-
|
|
54
53
|
# Load existing archive if it exists
|
|
55
54
|
if ::File.exist?(file_path) && !create
|
|
56
55
|
load_archive
|
|
@@ -189,6 +188,18 @@ module Omnizip
|
|
|
189
188
|
end
|
|
190
189
|
alias_method :length, :size
|
|
191
190
|
|
|
191
|
+
# Mark this archive as having unsaved changes. The next +#commit+
|
|
192
|
+
# or +#close+ will write the archive to disk.
|
|
193
|
+
def modified!
|
|
194
|
+
@modified = true
|
|
195
|
+
self
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# True if there are unsaved changes.
|
|
199
|
+
def modified?
|
|
200
|
+
@modified
|
|
201
|
+
end
|
|
202
|
+
|
|
192
203
|
# Check if archive contains an entry
|
|
193
204
|
# @param entry_name [String] Entry name to check
|
|
194
205
|
# @return [Boolean] True if entry exists
|
|
@@ -374,8 +385,7 @@ module Omnizip
|
|
|
374
385
|
extra_field: "",
|
|
375
386
|
comment: "",
|
|
376
387
|
).tap do |header|
|
|
377
|
-
|
|
378
|
-
header.instance_variable_set(:@_original_data, data)
|
|
388
|
+
header.cached_original_data = data
|
|
379
389
|
end
|
|
380
390
|
end
|
|
381
391
|
|
|
@@ -384,7 +394,7 @@ module Omnizip
|
|
|
384
394
|
return "" if entry.directory?
|
|
385
395
|
|
|
386
396
|
# Check if we have cached data (for new entries not yet written)
|
|
387
|
-
cached_data = entry.header.
|
|
397
|
+
cached_data = entry.header.cached_original_data
|
|
388
398
|
return cached_data if cached_data
|
|
389
399
|
|
|
390
400
|
# Otherwise read from file
|
|
@@ -416,10 +426,11 @@ module Omnizip
|
|
|
416
426
|
return "" unless compressed_data
|
|
417
427
|
|
|
418
428
|
# Decompress
|
|
419
|
-
@reader.
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
429
|
+
@reader.decompress(
|
|
430
|
+
compressed_data,
|
|
431
|
+
reader_entry.compression_method,
|
|
432
|
+
reader_entry.uncompressed_size,
|
|
433
|
+
)
|
|
423
434
|
end
|
|
424
435
|
end
|
|
425
436
|
|
|
@@ -447,7 +458,7 @@ module Omnizip
|
|
|
447
458
|
next if entry.directory?
|
|
448
459
|
|
|
449
460
|
header = entry.header
|
|
450
|
-
cached = header.
|
|
461
|
+
cached = header.cached_original_data
|
|
451
462
|
entry_data[entry.name] = cached || read_entry_data(entry)
|
|
452
463
|
end
|
|
453
464
|
|
data/lib/omnizip.rb
CHANGED
|
@@ -17,27 +17,30 @@
|
|
|
17
17
|
#
|
|
18
18
|
|
|
19
19
|
module Omnizip
|
|
20
|
-
# Core version - autoloaded
|
|
21
20
|
autoload :VERSION, "omnizip/version"
|
|
22
21
|
|
|
23
|
-
# Error classes - autoloaded
|
|
24
22
|
autoload :Error, "omnizip/error"
|
|
25
23
|
autoload :CompressionError, "omnizip/error"
|
|
26
24
|
autoload :DecompressionError, "omnizip/error"
|
|
27
25
|
autoload :AlgorithmNotFoundError, "omnizip/error"
|
|
28
26
|
autoload :UnknownAlgorithmError, "omnizip/error"
|
|
27
|
+
autoload :UnknownChecksumError, "omnizip/error"
|
|
28
|
+
autoload :UnknownFilterError, "omnizip/error"
|
|
29
|
+
autoload :UnknownEncryptionStrategyError, "omnizip/error"
|
|
30
|
+
autoload :ConversionNotSupportedError, "omnizip/error"
|
|
29
31
|
autoload :UnsupportedFormatError, "omnizip/error"
|
|
30
32
|
autoload :FormatError, "omnizip/error"
|
|
31
33
|
autoload :InvalidArchiveError, "omnizip/error"
|
|
34
|
+
autoload :IOOperationError, "omnizip/error"
|
|
32
35
|
autoload :IOError, "omnizip/error"
|
|
33
36
|
autoload :ChecksumError, "omnizip/error"
|
|
34
|
-
autoload :
|
|
37
|
+
autoload :OptimizationNotFoundError, "omnizip/error"
|
|
35
38
|
autoload :ProgressError, "omnizip/error"
|
|
36
39
|
autoload :ETAError, "omnizip/error"
|
|
37
40
|
autoload :NotLicensedError, "omnizip/error"
|
|
38
41
|
autoload :RarNotAvailableError, "omnizip/error"
|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
autoload :Registry, "omnizip/registry"
|
|
41
44
|
autoload :Algorithm, "omnizip/algorithm"
|
|
42
45
|
autoload :AlgorithmRegistry, "omnizip/algorithm_registry"
|
|
43
46
|
autoload :FormatRegistry, "omnizip/format_registry"
|
|
@@ -45,11 +48,9 @@ module Omnizip
|
|
|
45
48
|
autoload :ChecksumRegistry, "omnizip/checksum_registry"
|
|
46
49
|
autoload :FilterRegistry, "omnizip/filter_registry"
|
|
47
50
|
|
|
48
|
-
# Base classes
|
|
49
51
|
autoload :Filter, "omnizip/filter"
|
|
50
52
|
autoload :FilterPipeline, "omnizip/filter_pipeline"
|
|
51
53
|
|
|
52
|
-
# Feature modules
|
|
53
54
|
autoload :Buffer, "omnizip/buffer"
|
|
54
55
|
autoload :Pipe, "omnizip/pipe"
|
|
55
56
|
autoload :Chunked, "omnizip/chunked"
|
|
@@ -66,15 +67,25 @@ module Omnizip
|
|
|
66
67
|
autoload :Platform, "omnizip/platform"
|
|
67
68
|
autoload :Profiler, "omnizip/profiler"
|
|
68
69
|
autoload :Commands, "omnizip/commands"
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
autoload :CliOutputFormatter, "omnizip/cli/output_formatter"
|
|
71
|
+
|
|
72
|
+
# Implementation namespaces. Each namespace file declares autoloads
|
|
73
|
+
# for its concrete classes. The classes self-register with their
|
|
74
|
+
# registry on load (see lib/omnizip/algorithms/lzma.rb etc.).
|
|
75
|
+
autoload :Algorithms, "omnizip/algorithms"
|
|
76
|
+
autoload :Filters, "omnizip/filters"
|
|
77
|
+
autoload :Checksums, "omnizip/checksums"
|
|
71
78
|
autoload :IO, "omnizip/io"
|
|
72
79
|
autoload :Crypto, "omnizip/crypto"
|
|
73
80
|
autoload :Formats, "omnizip/formats"
|
|
74
81
|
autoload :Zip, "omnizip/zip"
|
|
82
|
+
autoload :Extraction, "omnizip/extraction"
|
|
83
|
+
autoload :Implementations, "omnizip/implementations"
|
|
84
|
+
autoload :ArchiveHandler, "omnizip/archive_handler"
|
|
85
|
+
autoload :ArchiveHandlers, "omnizip/archive_handlers"
|
|
86
|
+
autoload :Convenience, "omnizip/convenience"
|
|
75
87
|
end
|
|
76
88
|
|
|
77
|
-
# Models module with autoloaded classes
|
|
78
89
|
module Omnizip
|
|
79
90
|
module Models
|
|
80
91
|
autoload :AlgorithmMetadata, "omnizip/models/algorithm_metadata"
|
|
@@ -95,56 +106,15 @@ module Omnizip
|
|
|
95
106
|
end
|
|
96
107
|
end
|
|
97
108
|
|
|
98
|
-
# Algorithms (with registration - must be required explicitly)
|
|
99
|
-
require_relative "omnizip/algorithms/ppmd_base"
|
|
100
|
-
require_relative "omnizip/algorithms/lzma"
|
|
101
|
-
require_relative "omnizip/algorithms/lzma2"
|
|
102
|
-
require_relative "omnizip/algorithms/ppmd7"
|
|
103
|
-
require_relative "omnizip/algorithms/ppmd8"
|
|
104
|
-
require_relative "omnizip/algorithms/bzip2"
|
|
105
|
-
require_relative "omnizip/algorithms/deflate"
|
|
106
|
-
require_relative "omnizip/algorithms/deflate64"
|
|
107
|
-
require_relative "omnizip/algorithms/zstandard"
|
|
108
|
-
|
|
109
|
-
# Filters implementations (with registration)
|
|
110
|
-
require_relative "omnizip/filters/bcj"
|
|
111
|
-
require_relative "omnizip/filters/bcj_x86"
|
|
112
|
-
require_relative "omnizip/filters/bcj2"
|
|
113
|
-
require_relative "omnizip/filters/bcj_arm"
|
|
114
|
-
require_relative "omnizip/filters/bcj_arm64"
|
|
115
|
-
require_relative "omnizip/filters/bcj_ppc"
|
|
116
|
-
require_relative "omnizip/filters/bcj_sparc"
|
|
117
|
-
require_relative "omnizip/filters/bcj_ia64"
|
|
118
|
-
require_relative "omnizip/filters/delta"
|
|
119
|
-
require_relative "omnizip/filters/registry"
|
|
120
|
-
|
|
121
|
-
# Checksum implementations (with registration)
|
|
122
|
-
require_relative "omnizip/checksums/crc_base"
|
|
123
|
-
require_relative "omnizip/checksums/crc32"
|
|
124
|
-
require_relative "omnizip/checksums/crc64"
|
|
125
|
-
|
|
126
|
-
# Archive formats registry (with autoload declarations for lazy loading)
|
|
127
|
-
require_relative "omnizip/formats"
|
|
128
|
-
|
|
129
|
-
# Archive formats (with registration - must be required explicitly)
|
|
130
|
-
require_relative "omnizip/formats/seven_zip"
|
|
131
|
-
require_relative "omnizip/formats/zip"
|
|
132
|
-
require_relative "omnizip/formats/rar"
|
|
133
|
-
require_relative "omnizip/formats/tar"
|
|
134
|
-
require_relative "omnizip/formats/gzip"
|
|
135
|
-
require_relative "omnizip/formats/bzip2_file"
|
|
136
|
-
require_relative "omnizip/formats/xz"
|
|
137
|
-
|
|
138
|
-
# Platform-specific features
|
|
139
109
|
module Omnizip
|
|
140
110
|
module Platform
|
|
141
111
|
autoload :NtfsStreams, "omnizip/platform/ntfs_streams"
|
|
142
112
|
end
|
|
143
113
|
|
|
144
|
-
# Implementations module with autoloaded classes
|
|
145
114
|
module Implementations
|
|
146
115
|
autoload :SevenZip, "omnizip/implementations/seven_zip"
|
|
147
116
|
autoload :XZUtils, "omnizip/implementations/xz_utils"
|
|
117
|
+
|
|
148
118
|
module SevenZip
|
|
149
119
|
module LZMA
|
|
150
120
|
autoload :StateMachine,
|
|
@@ -172,7 +142,8 @@ module Omnizip
|
|
|
172
142
|
end
|
|
173
143
|
end
|
|
174
144
|
|
|
175
|
-
#
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
#
|
|
145
|
+
# Trigger autoload of Convenience so its methods are available on the
|
|
146
|
+
# Omnizip module (it calls `extend Convenience` at the bottom of the
|
|
147
|
+
# file). Without this, Omnizip.compress_file and friends would not be
|
|
148
|
+
# defined until the first manual reference.
|
|
149
|
+
Omnizip::Convenience
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "open-uri"
|
|
5
|
+
require "digest/sha2"
|
|
6
|
+
|
|
7
|
+
namespace :test_files do
|
|
8
|
+
desc "Download Adobe font pack MSI for testing (60MB)"
|
|
9
|
+
task :adobe_msi do
|
|
10
|
+
url = "https://web.archive.org/web/20200816153035/http://ardownload.adobe.com/pub/adobe/reader/win/AcrobatDC/misc/FontPack1902120058_XtdAlf_Lang_DC.msi"
|
|
11
|
+
output_dir = "spec/fixtures/adobe"
|
|
12
|
+
output_path = "#{output_dir}/FontPack1902120058_XtdAlf_Lang_DC.msi"
|
|
13
|
+
expected_sha256 = "b5b9e15791a177715fa5e93ea458f8627cada7ac3218531461cfd35cecef6c24"
|
|
14
|
+
|
|
15
|
+
FileUtils.mkdir_p(output_dir)
|
|
16
|
+
|
|
17
|
+
if File.exist?(output_path)
|
|
18
|
+
# Verify existing file
|
|
19
|
+
actual_sha256 = Digest::SHA256.file(output_path).hexdigest
|
|
20
|
+
if actual_sha256 == expected_sha256
|
|
21
|
+
puts "Adobe font pack MSI already exists and verified: #{output_path}"
|
|
22
|
+
return
|
|
23
|
+
else
|
|
24
|
+
puts "Existing file has wrong SHA256, re-downloading..."
|
|
25
|
+
File.delete(output_path)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
puts "Downloading Adobe font pack MSI (60MB)..."
|
|
30
|
+
puts "URL: #{url}"
|
|
31
|
+
|
|
32
|
+
begin
|
|
33
|
+
URI.open(url, "rb") do |remote|
|
|
34
|
+
File.binwrite(output_path, remote.read)
|
|
35
|
+
end
|
|
36
|
+
rescue OpenURI::HTTPError, Net::OpenTimeout, Errno::ECONNREFUSED => e
|
|
37
|
+
puts "Failed to download: #{e.message}"
|
|
38
|
+
puts "You may need to download manually from:"
|
|
39
|
+
puts " #{url}"
|
|
40
|
+
puts "And place it at: #{output_path}"
|
|
41
|
+
exit 1
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Verify SHA256
|
|
45
|
+
actual_sha256 = Digest::SHA256.file(output_path).hexdigest
|
|
46
|
+
if actual_sha256 == expected_sha256
|
|
47
|
+
puts "Downloaded and verified: #{output_path}"
|
|
48
|
+
puts "SHA256: #{actual_sha256}"
|
|
49
|
+
else
|
|
50
|
+
File.delete(output_path)
|
|
51
|
+
puts "SHA256 mismatch!"
|
|
52
|
+
puts "Expected: #{expected_sha256}"
|
|
53
|
+
puts "Got: #{actual_sha256}"
|
|
54
|
+
exit 1
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
desc "Clean up downloaded test files"
|
|
59
|
+
task :clean do
|
|
60
|
+
adobe_dir = "spec/fixtures/adobe"
|
|
61
|
+
if File.directory?(adobe_dir)
|
|
62
|
+
FileUtils.rm_rf(adobe_dir)
|
|
63
|
+
puts "Removed: #{adobe_dir}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|