1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.compress.archivers.sevenz;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.FilterInputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.io.SequenceInputStream;
27 import java.util.Arrays;
28 import java.util.HashMap;
29 import java.util.Map;
30 import java.util.zip.Deflater;
31 import java.util.zip.DeflaterOutputStream;
32 import java.util.zip.Inflater;
33 import java.util.zip.InflaterInputStream;
34
35 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
36 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
37 import org.apache.commons.compress.compressors.deflate64.Deflate64CompressorInputStream;
38 import org.apache.commons.compress.utils.FlushShieldFilterOutputStream;
39 import org.tukaani.xz.ARMOptions;
40 import org.tukaani.xz.ARMThumbOptions;
41 import org.tukaani.xz.FilterOptions;
42 import org.tukaani.xz.FinishableWrapperOutputStream;
43 import org.tukaani.xz.IA64Options;
44 import org.tukaani.xz.PowerPCOptions;
45 import org.tukaani.xz.SPARCOptions;
46 import org.tukaani.xz.X86Options;
47
48 final class Coders {
49
50 static final class BCJDecoder extends AbstractCoder {
51 private final FilterOptions opts;
52
53 BCJDecoder(final FilterOptions opts) {
54 this.opts = opts;
55 }
56
57 @Override
58 InputStream decode(final String archiveName, final InputStream in, final long uncompressedLength, final Coder coder, final byte[] password,
59 final int maxMemoryLimitKiB) throws IOException {
60 try {
61 return opts.getInputStream(in);
62 } catch (final AssertionError e) {
63 throw new IOException("BCJ filter used in " + archiveName + " needs XZ for Java > 1.4 - see "
64 + "https://commons.apache.org/proper/commons-compress/limitations.html#7Z", e);
65 }
66 }
67
68 @SuppressWarnings("resource")
69 @Override
70 OutputStream encode(final OutputStream out, final Object options) {
71 return new FlushShieldFilterOutputStream(opts.getOutputStream(new FinishableWrapperOutputStream(out)));
72 }
73 }
74
75 static final class BZIP2Decoder extends AbstractCoder {
76 BZIP2Decoder() {
77 super(Number.class);
78 }
79
80 @Override
81 InputStream decode(final String archiveName, final InputStream in, final long uncompressedLength, final Coder coder, final byte[] password,
82 final int maxMemoryLimitKiB) throws IOException {
83 return new BZip2CompressorInputStream(in);
84 }
85
86 @Override
87 OutputStream encode(final OutputStream out, final Object options) throws IOException {
88 final int blockSize = toInt(options, BZip2CompressorOutputStream.MAX_BLOCKSIZE);
89 return new BZip2CompressorOutputStream(out, blockSize);
90 }
91 }
92
93 static final class CopyDecoder extends AbstractCoder {
94 @Override
95 InputStream decode(final String archiveName, final InputStream in, final long uncompressedLength, final Coder coder, final byte[] password,
96 final int maxMemoryLimitKiB) throws IOException {
97 return in;
98 }
99
100 @Override
101 OutputStream encode(final OutputStream out, final Object options) {
102 return out;
103 }
104 }
105
106 static final class Deflate64Decoder extends AbstractCoder {
107 Deflate64Decoder() {
108 super(Number.class);
109 }
110
111 @Override
112 InputStream decode(final String archiveName, final InputStream in, final long uncompressedLength, final Coder coder, final byte[] password,
113 final int maxMemoryLimitKiB) throws IOException {
114 return new Deflate64CompressorInputStream(in);
115 }
116 }
117
118 static final class DeflateDecoder extends AbstractCoder {
119
120 static final class DeflateDecoderInputStream extends FilterInputStream {
121
122 Inflater inflater;
123
124 DeflateDecoderInputStream(final InflaterInputStream inflaterInputStream, final Inflater inflater) {
125 super(inflaterInputStream);
126 this.inflater = inflater;
127 }
128
129 @Override
130 public void close() throws IOException {
131 try {
132 super.close();
133 } finally {
134 inflater.end();
135 }
136 }
137
138 }
139
140 static final class DeflateDecoderOutputStream extends OutputStream {
141
142 final DeflaterOutputStream deflaterOutputStream;
143 Deflater deflater;
144
145 DeflateDecoderOutputStream(final DeflaterOutputStream deflaterOutputStream, final Deflater deflater) {
146 this.deflaterOutputStream = deflaterOutputStream;
147 this.deflater = deflater;
148 }
149
150 @Override
151 public void close() throws IOException {
152 try {
153 deflaterOutputStream.close();
154 } finally {
155 deflater.end();
156 }
157 }
158
159 @Override
160 public void write(final byte[] b) throws IOException {
161 deflaterOutputStream.write(b);
162 }
163
164 @Override
165 public void write(final byte[] b, final int off, final int len) throws IOException {
166 deflaterOutputStream.write(b, off, len);
167 }
168
169 @Override
170 public void write(final int b) throws IOException {
171 deflaterOutputStream.write(b);
172 }
173 }
174
175 private static final byte[] ONE_ZERO_BYTE = new byte[1];
176
177 DeflateDecoder() {
178 super(Number.class);
179 }
180
181 @Override
182 InputStream decode(final String archiveName, final InputStream in, final long uncompressedLength, final Coder coder, final byte[] password,
183 final int maxMemoryLimitKiB) throws IOException {
184 final Inflater inflater = new Inflater(true);
185
186
187
188
189
190 final InflaterInputStream inflaterInputStream = new InflaterInputStream(new SequenceInputStream(in, new ByteArrayInputStream(ONE_ZERO_BYTE)),
191 inflater);
192 return new DeflateDecoderInputStream(inflaterInputStream, inflater);
193 }
194
195 @Override
196 OutputStream encode(final OutputStream out, final Object options) {
197 final int level = toInt(options, 9);
198 final Deflater deflater = new Deflater(level, true);
199 final DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(out, deflater);
200 return new DeflateDecoderOutputStream(deflaterOutputStream, deflater);
201 }
202 }
203
204 private static final Map<SevenZMethod, AbstractCoder> CODER_MAP = new HashMap<SevenZMethod, AbstractCoder>() {
205
206 private static final long serialVersionUID = 1664829131806520867L;
207
208 {
209 put(SevenZMethod.COPY, new CopyDecoder());
210 put(SevenZMethod.LZMA, new LZMADecoder());
211 put(SevenZMethod.LZMA2, new LZMA2Decoder());
212 put(SevenZMethod.DEFLATE, new DeflateDecoder());
213 put(SevenZMethod.DEFLATE64, new Deflate64Decoder());
214 put(SevenZMethod.BZIP2, new BZIP2Decoder());
215 put(SevenZMethod.AES256SHA256, new AES256SHA256Decoder());
216 put(SevenZMethod.BCJ_X86_FILTER, new BCJDecoder(new X86Options()));
217 put(SevenZMethod.BCJ_PPC_FILTER, new BCJDecoder(new PowerPCOptions()));
218 put(SevenZMethod.BCJ_IA64_FILTER, new BCJDecoder(new IA64Options()));
219 put(SevenZMethod.BCJ_ARM_FILTER, new BCJDecoder(new ARMOptions()));
220 put(SevenZMethod.BCJ_ARM_THUMB_FILTER, new BCJDecoder(new ARMThumbOptions()));
221 put(SevenZMethod.BCJ_SPARC_FILTER, new BCJDecoder(new SPARCOptions()));
222 put(SevenZMethod.DELTA_FILTER, new DeltaDecoder());
223 }
224 };
225
226 static InputStream addDecoder(final String archiveName, final InputStream is, final long uncompressedLength, final Coder coder, final byte[] password,
227 final int maxMemoryLimitKiB) throws IOException {
228 final AbstractCoder cb = findByMethod(SevenZMethod.byId(coder.decompressionMethodId));
229 if (cb == null) {
230 throw new IOException("Unsupported compression method " + Arrays.toString(coder.decompressionMethodId) + " used in " + archiveName);
231 }
232 return cb.decode(archiveName, is, uncompressedLength, coder, password, maxMemoryLimitKiB);
233 }
234
235 static OutputStream addEncoder(final OutputStream out, final SevenZMethod method, final Object options) throws IOException {
236 final AbstractCoder cb = findByMethod(method);
237 if (cb == null) {
238 throw new IOException("Unsupported compression method " + method);
239 }
240 return cb.encode(out, options);
241 }
242
243 static AbstractCoder findByMethod(final SevenZMethod method) {
244 return CODER_MAP.get(method);
245 }
246
247 }