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