Home | History | Annotate | Download | only in sevenz
      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 
     67     static CoderBase findByMethod(final SevenZMethod method) {
     68         return CODER_MAP.get(method);
     69     }
     70 
     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 
     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
     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
     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;
    105         BCJDecoder(final FilterOptions opts) {
    106             this.opts = opts;
    107         }
    108 
    109         @Override
    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
    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];
    131         DeflateDecoder() {
    132             super(Number.class);
    133         }
    134 
    135         @SuppressWarnings("resource") // caller must close the InputStream
    136         @Override
    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
    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 
    163             public DeflateDecoderInputStream(InflaterInputStream inflaterInputStream,
    164                 Inflater inflater) {
    165                 this.inflaterInputStream = inflaterInputStream;
    166                 this.inflater = inflater;
    167             }
    168 
    169             @Override
    170             public int read() throws IOException {
    171                 return inflaterInputStream.read();
    172             }
    173 
    174             @Override
    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
    180             public int read(final byte[] b) throws IOException {
    181                 return inflaterInputStream.read(b);
    182             }
    183 
    184             @Override
    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 
    199             public DeflateDecoderOutputStream(DeflaterOutputStream deflaterOutputStream,
    200                 Deflater deflater) {
    201                 this.deflaterOutputStream = deflaterOutputStream;
    202                 this.deflater = deflater;
    203             }
    204 
    205             @Override
    206             public void write(final int b) throws IOException {
    207                 deflaterOutputStream.write(b);
    208             }
    209 
    210             @Override
    211             public void write(final byte[] b) throws IOException {
    212                 deflaterOutputStream.write(b);
    213             }
    214 
    215             @Override
    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
    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 {
    232         Deflate64Decoder() {
    233             super(Number.class);
    234         }
    235 
    236         @SuppressWarnings("resource") // caller must close the InputStream
    237         @Override
    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 {
    246         BZIP2Decoder() {
    247             super(Number.class);
    248         }
    249 
    250         @Override
    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
    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