• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  */
18 package org.apache.commons.compress.archivers.sevenz;
19 
20 import java.io.ByteArrayInputStream;
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 class Coders {
47     private static final Map<SevenZMethod, CoderBase> CODER_MAP = new HashMap<SevenZMethod, CoderBase>() {
48 
49         private static final long serialVersionUID = 1664829131806520867L;
50     {
51             put(SevenZMethod.COPY, new CopyDecoder());
52             put(SevenZMethod.LZMA, new LZMADecoder());
53             put(SevenZMethod.LZMA2, new LZMA2Decoder());
54             put(SevenZMethod.DEFLATE, new DeflateDecoder());
55             put(SevenZMethod.DEFLATE64, new Deflate64Decoder());
56             put(SevenZMethod.BZIP2, new BZIP2Decoder());
57             put(SevenZMethod.AES256SHA256, new AES256SHA256Decoder());
58             put(SevenZMethod.BCJ_X86_FILTER, new BCJDecoder(new X86Options()));
59             put(SevenZMethod.BCJ_PPC_FILTER, new BCJDecoder(new PowerPCOptions()));
60             put(SevenZMethod.BCJ_IA64_FILTER, new BCJDecoder(new IA64Options()));
61             put(SevenZMethod.BCJ_ARM_FILTER, new BCJDecoder(new ARMOptions()));
62             put(SevenZMethod.BCJ_ARM_THUMB_FILTER, new BCJDecoder(new ARMThumbOptions()));
63             put(SevenZMethod.BCJ_SPARC_FILTER, new BCJDecoder(new SPARCOptions()));
64             put(SevenZMethod.DELTA_FILTER, new DeltaDecoder());
65         }};
66 
findByMethod(final SevenZMethod method)67     static CoderBase findByMethod(final SevenZMethod method) {
68         return CODER_MAP.get(method);
69     }
70 
addDecoder(final String archiveName, final InputStream is, final long uncompressedLength, final Coder coder, final byte[] password)71     static InputStream addDecoder(final String archiveName, final InputStream is, final long uncompressedLength,
72             final Coder coder, final byte[] password) throws IOException {
73         final CoderBase cb = findByMethod(SevenZMethod.byId(coder.decompressionMethodId));
74         if (cb == null) {
75             throw new IOException("Unsupported compression method " +
76                                   Arrays.toString(coder.decompressionMethodId)
77                                   + " used in " + archiveName);
78         }
79         return cb.decode(archiveName, is, uncompressedLength, coder, password);
80     }
81 
addEncoder(final OutputStream out, final SevenZMethod method, final Object options)82     static OutputStream addEncoder(final OutputStream out, final SevenZMethod method,
83                                    final Object options) throws IOException {
84         final CoderBase cb = findByMethod(method);
85         if (cb == null) {
86             throw new IOException("Unsupported compression method " + method);
87         }
88         return cb.encode(out, options);
89     }
90 
91     static class CopyDecoder extends CoderBase {
92         @Override
decode(final String archiveName, final InputStream in, final long uncompressedLength, final Coder coder, final byte[] password)93         InputStream decode(final String archiveName, final InputStream in, final long uncompressedLength,
94                 final Coder coder, final byte[] password) throws IOException {
95             return in;
96         }
97         @Override
encode(final OutputStream out, final Object options)98         OutputStream encode(final OutputStream out, final Object options) {
99             return out;
100         }
101     }
102 
103     static class BCJDecoder extends CoderBase {
104         private final FilterOptions opts;
BCJDecoder(final FilterOptions opts)105         BCJDecoder(final FilterOptions opts) {
106             this.opts = opts;
107         }
108 
109         @Override
decode(final String archiveName, final InputStream in, final long uncompressedLength, final Coder coder, final byte[] password)110         InputStream decode(final String archiveName, final InputStream in, final long uncompressedLength,
111                 final Coder coder, final byte[] password) throws IOException {
112             try {
113                 return opts.getInputStream(in);
114             } catch (final AssertionError e) {
115                 throw new IOException("BCJ filter used in " + archiveName
116                                       + " needs XZ for Java > 1.4 - see "
117                                       + "https://commons.apache.org/proper/commons-compress/limitations.html#7Z",
118                                       e);
119             }
120         }
121 
122         @SuppressWarnings("resource")
123         @Override
encode(final OutputStream out, final Object options)124         OutputStream encode(final OutputStream out, final Object options) {
125             return new FlushShieldFilterOutputStream(opts.getOutputStream(new FinishableWrapperOutputStream(out)));
126         }
127     }
128 
129     static class DeflateDecoder extends CoderBase {
130         private static final byte[] ONE_ZERO_BYTE = new byte[1];
DeflateDecoder()131         DeflateDecoder() {
132             super(Number.class);
133         }
134 
135         @SuppressWarnings("resource") // caller must close the InputStream
136         @Override
decode(final String archiveName, final InputStream in, final long uncompressedLength, final Coder coder, final byte[] password)137         InputStream decode(final String archiveName, final InputStream in, final long uncompressedLength,
138                 final Coder coder, final byte[] password)
139             throws IOException {
140             final Inflater inflater = new Inflater(true);
141             // Inflater with nowrap=true has this odd contract for a zero padding
142             // byte following the data stream; this used to be zlib's requirement
143             // and has been fixed a long time ago, but the contract persists so
144             // we comply.
145             // https://docs.oracle.com/javase/7/docs/api/java/util/zip/Inflater.html#Inflater(boolean)
146             final InflaterInputStream inflaterInputStream = new InflaterInputStream(new SequenceInputStream(in,
147                 new ByteArrayInputStream(ONE_ZERO_BYTE)), inflater);
148             return new DeflateDecoderInputStream(inflaterInputStream, inflater);
149         }
150         @Override
encode(final OutputStream out, final Object options)151         OutputStream encode(final OutputStream out, final Object options) {
152             final int level = numberOptionOrDefault(options, 9);
153             final Deflater deflater = new Deflater(level, true);
154             final DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(out, deflater);
155             return new DeflateDecoderOutputStream(deflaterOutputStream, deflater);
156         }
157 
158          static class DeflateDecoderInputStream extends InputStream {
159 
160               InflaterInputStream inflaterInputStream;
161               Inflater inflater;
162 
DeflateDecoderInputStream(InflaterInputStream inflaterInputStream, Inflater inflater)163             public DeflateDecoderInputStream(InflaterInputStream inflaterInputStream,
164                 Inflater inflater) {
165                 this.inflaterInputStream = inflaterInputStream;
166                 this.inflater = inflater;
167             }
168 
169             @Override
read()170             public int read() throws IOException {
171                 return inflaterInputStream.read();
172             }
173 
174             @Override
read(final byte[] b, final int off, final int len)175             public int read(final byte[] b, final int off, final int len) throws IOException {
176                 return inflaterInputStream.read(b, off, len);
177             }
178 
179             @Override
read(final byte[] b)180             public int read(final byte[] b) throws IOException {
181                 return inflaterInputStream.read(b);
182             }
183 
184             @Override
close()185             public void close() throws IOException {
186                 try {
187                     inflaterInputStream.close();
188                 } finally {
189                     inflater.end();
190                 }
191             }
192         }
193 
194          static class DeflateDecoderOutputStream extends OutputStream {
195 
196               DeflaterOutputStream deflaterOutputStream;
197               Deflater deflater;
198 
DeflateDecoderOutputStream(DeflaterOutputStream deflaterOutputStream, Deflater deflater)199             public DeflateDecoderOutputStream(DeflaterOutputStream deflaterOutputStream,
200                 Deflater deflater) {
201                 this.deflaterOutputStream = deflaterOutputStream;
202                 this.deflater = deflater;
203             }
204 
205             @Override
write(final int b)206             public void write(final int b) throws IOException {
207                 deflaterOutputStream.write(b);
208             }
209 
210             @Override
write(final byte[] b)211             public void write(final byte[] b) throws IOException {
212                 deflaterOutputStream.write(b);
213             }
214 
215             @Override
write(final byte[] b, final int off, final int len)216             public void write(final byte[] b, final int off, final int len) throws IOException {
217                 deflaterOutputStream.write(b, off, len);
218             }
219 
220             @Override
close()221             public void close() throws IOException {
222                 try {
223                     deflaterOutputStream.close();
224                 } finally {
225                     deflater.end();
226                 }
227             }
228         }
229     }
230 
231     static class Deflate64Decoder extends CoderBase {
Deflate64Decoder()232         Deflate64Decoder() {
233             super(Number.class);
234         }
235 
236         @SuppressWarnings("resource") // caller must close the InputStream
237         @Override
decode(final String archiveName, final InputStream in, final long uncompressedLength, final Coder coder, final byte[] password)238         InputStream decode(final String archiveName, final InputStream in, final long uncompressedLength,
239                 final Coder coder, final byte[] password)
240             throws IOException {
241             return new Deflate64CompressorInputStream(in);
242         }
243     }
244 
245     static class BZIP2Decoder extends CoderBase {
BZIP2Decoder()246         BZIP2Decoder() {
247             super(Number.class);
248         }
249 
250         @Override
decode(final String archiveName, final InputStream in, final long uncompressedLength, final Coder coder, final byte[] password)251         InputStream decode(final String archiveName, final InputStream in, final long uncompressedLength,
252                 final Coder coder, final byte[] password)
253                 throws IOException {
254             return new BZip2CompressorInputStream(in);
255         }
256         @Override
encode(final OutputStream out, final Object options)257         OutputStream encode(final OutputStream out, final Object options)
258                 throws IOException {
259             final int blockSize = numberOptionOrDefault(options, BZip2CompressorOutputStream.MAX_BLOCKSIZE);
260             return new BZip2CompressorOutputStream(out, blockSize);
261         }
262     }
263 
264 }
265