    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 *   https://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019package org.apache.commons.compress.archivers.zip;
    020
    021/**
    022 * Parser/encoder for the "general purpose bit" field in ZIP's local file and central directory headers.
    023 *
    024 * @since 1.1
    025 * @NotThreadSafe
    026 */
    027public final class GeneralPurposeBit implements Cloneable {
    028
    029    /**
    030     * Indicates that the file is encrypted.
    031     */
    032    private static final int ENCRYPTION_FLAG = 1 << 0;
    033
    034    /**
    035     * Indicates the size of the sliding dictionary used by the compression method 6 (imploding).
    036     * <ul>
    037     * <li>0: 4096 bytes</li>
    038     * <li>1: 8192 bytes</li>
    039     * </ul>
    040     */
    041    private static final int SLIDING_DICTIONARY_SIZE_FLAG = 1 << 1;
    042
    043    /**
    044     * Indicates the number of Shannon-Fano trees used by the compression method 6 (imploding).
    045     * <ul>
    046     * <li>0: 2 trees (lengths, distances)</li>
    047     * <li>1: 3 trees (literals, lengths, distances)</li>
    048     * </ul>
    049     */
    050    private static final int NUMBER_OF_SHANNON_FANO_TREES_FLAG = 1 << 2;
    051
    052    /**
    053     * Indicates that a data descriptor stored after the file contents will hold CRC and size information.
    054     */
    055    private static final int DATA_DESCRIPTOR_FLAG = 1 << 3;
    056
    057    /**
    058     * Indicates strong encryption.
    059     */
    060    private static final int STRONG_ENCRYPTION_FLAG = 1 << 6;
    061
    062    /**
    063     * Indicates that file names are written in UTF-8.
    064     *
    065     * <p>
    066     * The only reason this is public is that {@link ZipArchiveOutputStream#EFS_FLAG} was public in Apache Commons Compress 1.0 and we needed a substitute for
    067     * it.
    068     * </p>
    069     */
    070    public static final int UFT8_NAMES_FLAG = 1 << 11;
    071
    072    /**
    073     * Parses the supported flags from the given archive data.
    074     *
    075     * @param data   local file header or a central directory entry.
    076     * @param offset offset at which the general purpose bit starts
    077     * @return parsed flags
    078     */
    079    public static GeneralPurposeBit parse(final byte[] data, final int offset) {
    080        final int generalPurposeFlag = ZipShort.getValue(data, offset);
    081        final GeneralPurposeBit b = new GeneralPurposeBit();
    082        b.useDataDescriptor((generalPurposeFlag & DATA_DESCRIPTOR_FLAG) != 0);
    083        b.useUTF8ForNames((generalPurposeFlag & UFT8_NAMES_FLAG) != 0);
    084        b.useStrongEncryption((generalPurposeFlag & STRONG_ENCRYPTION_FLAG) != 0);
    085        b.useEncryption((generalPurposeFlag & ENCRYPTION_FLAG) != 0);
    086        b.slidingDictionarySize = (generalPurposeFlag & SLIDING_DICTIONARY_SIZE_FLAG) != 0 ? 8192 : 4096;
    087        b.numberOfShannonFanoTrees = (generalPurposeFlag & NUMBER_OF_SHANNON_FANO_TREES_FLAG) != 0 ? 3 : 2;
    088        return b;
    089    }
    090
    091    private boolean languageEncodingFlag;
    092    private boolean dataDescriptorFlag;
    093    private boolean encryptionFlag;
    094    private boolean strongEncryptionFlag;
    095    private int slidingDictionarySize;
    096
    097    private int numberOfShannonFanoTrees;
    098
    099    /**
    100     * Constructs a new instance.
    101     */
    102    public GeneralPurposeBit() {
    103    }
    104
    105    @Override
    106    public Object clone() {
    107        try {
    108            return super.clone();
    109        } catch (final CloneNotSupportedException ex) {
    110            // impossible
    111            throw new UnsupportedOperationException("GeneralPurposeBit is not Cloneable?", ex); // NOSONAR
    112        }
    113    }
    114
    115    /**
    116     * Encodes the set bits in a form suitable for ZIP archives.
    117     *
    118     * @return the encoded general purpose bits
    119     */
    120    public byte[] encode() {
    121        final byte[] result = new byte[2];
    122        encode(result, 0);
    123        return result;
    124    }
    125
    126    /**
    127     * Encodes the set bits in a form suitable for ZIP archives.
    128     *
    129     * @param buf    the output buffer
    130     * @param offset The offset within the output buffer of the first byte to be written. must be non-negative and no larger than {@code buf.length-2}
    131     */
    132    public void encode(final byte[] buf, final int offset) {
    133        ZipShort.putShort((dataDescriptorFlag ? DATA_DESCRIPTOR_FLAG : 0) | (languageEncodingFlag ? UFT8_NAMES_FLAG : 0)
    134                | (encryptionFlag ? ENCRYPTION_FLAG : 0) | (strongEncryptionFlag ? STRONG_ENCRYPTION_FLAG : 0), buf, offset);
    135    }
    136
    137    @Override
    138    public boolean equals(final Object o) {
    139        if (!(o instanceof GeneralPurposeBit)) {
    140            return false;
    141        }
    142        final GeneralPurposeBit g = (GeneralPurposeBit) o;
    143        return g.encryptionFlag == encryptionFlag && g.strongEncryptionFlag == strongEncryptionFlag && g.languageEncodingFlag == languageEncodingFlag
    144                && g.dataDescriptorFlag == dataDescriptorFlag;
    145    }
    146
    147    /**
    148     * Returns the number of trees used by the compression method 6 (imploding).
    149     */
    150    int getNumberOfShannonFanoTrees() {
    151        return numberOfShannonFanoTrees;
    152    }
    153
    154    /**
    155     * Returns the sliding dictionary size used by the compression method 6 (imploding).
    156     */
    157    int getSlidingDictionarySize() {
    158        return slidingDictionarySize;
    159    }
    160
    161    @Override
    162    public int hashCode() {
    163        return 3 * (7 * (13 * (17 * (encryptionFlag ? 1 : 0) + (strongEncryptionFlag ? 1 : 0)) + (languageEncodingFlag ? 1 : 0))
    164                + (dataDescriptorFlag ? 1 : 0));
    165    }
    166
    167    /**
    168     * whether the current entry will use the data descriptor to store CRC and size information.
    169     *
    170     * @param b whether the current entry will use the data descriptor to store CRC and size information
    171     */
    172    public void useDataDescriptor(final boolean b) {
    173        dataDescriptorFlag = b;
    174    }
    175
    176    /**
    177     * whether the current entry will be encrypted.
    178     *
    179     * @param b whether the current entry will be encrypted
    180     */
    181    public void useEncryption(final boolean b) {
    182        encryptionFlag = b;
    183    }
    184
    185    /**
    186     * whether the current entry uses the data descriptor to store CRC and size information.
    187     *
    188     * @return whether the current entry uses the data descriptor to store CRC and size information
    189     */
    190    public boolean usesDataDescriptor() {
    191        return dataDescriptorFlag;
    192    }
    193
    194    /**
    195     * whether the current entry is encrypted.
    196     *
    197     * @return whether the current entry is encrypted
    198     */
    199    public boolean usesEncryption() {
    200        return encryptionFlag;
    201    }
    202
    203    /**
    204     * whether the current entry is encrypted using strong encryption.
    205     *
    206     * @return whether the current entry is encrypted using strong encryption
    207     */
    208    public boolean usesStrongEncryption() {
    209        return encryptionFlag && strongEncryptionFlag;
    210    }
    211
    212    /**
    213     * whether the current entry will be encrypted using strong encryption.
    214     *
    215     * @param b whether the current entry will be encrypted using strong encryption
    216     */
    217    public void useStrongEncryption(final boolean b) {
    218        strongEncryptionFlag = b;
    219        if (b) {
    220            useEncryption(true);
    221        }
    222    }
    223
    224    /**
    225     * whether the current entry uses UTF8 for file name and comment.
    226     *
    227     * @return whether the current entry uses UTF8 for file name and comment.
    228     */
    229    public boolean usesUTF8ForNames() {
    230        return languageEncodingFlag;
    231    }
    232
    233    /**
    234     * whether the current entry will use UTF8 for file name and comment.
    235     *
    236     * @param b whether the current entry will use UTF8 for file name and comment.
    237     */
    238    public void useUTF8ForNames(final boolean b) {
    239        languageEncodingFlag = b;
    240    }
    241}