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