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     *
017     */
018    
019    package org.apache.commons.compress.archivers.zip;
020    
021    import java.io.UnsupportedEncodingException;
022    import java.util.zip.CRC32;
023    import java.util.zip.ZipException;
024    
025    /**
026     * A common base class for Unicode extra information extra fields.
027     * @NotThreadSafe
028     */
029    public abstract class AbstractUnicodeExtraField implements ZipExtraField {
030        private long nameCRC32;
031        private byte[] unicodeName;
032        private byte[] data;
033    
034        protected AbstractUnicodeExtraField() {
035        }
036    
037        /**
038         * Assemble as unicode extension from the name/comment and
039         * encoding of the orginal zip entry.
040         * 
041         * @param text The file name or comment.
042         * @param bytes The encoded of the filename or comment in the zip
043         * file.
044         * @param off The offset of the encoded filename or comment in
045         * <code>bytes</code>.
046         * @param len The length of the encoded filename or commentin
047         * <code>bytes</code>.
048         */
049        protected AbstractUnicodeExtraField(String text, byte[] bytes, int off, int len) {
050            CRC32 crc32 = new CRC32();
051            crc32.update(bytes, off, len);
052            nameCRC32 = crc32.getValue();
053    
054            try {
055                unicodeName = text.getBytes("UTF-8");
056            } catch (UnsupportedEncodingException e) {
057                throw new RuntimeException("FATAL: UTF-8 encoding not supported.", e);
058            }
059        }
060    
061        /**
062         * Assemble as unicode extension from the name/comment and
063         * encoding of the orginal zip entry.
064         * 
065         * @param text The file name or comment.
066         * @param bytes The encoded of the filename or comment in the zip
067         * file.
068         */
069        protected AbstractUnicodeExtraField(String text, byte[] bytes) {
070            this(text, bytes, 0, bytes.length);
071        }
072    
073        private void assembleData() {
074            if (unicodeName == null) {
075                return;
076            }
077    
078            data = new byte[5 + unicodeName.length];
079            // version 1
080            data[0] = 0x01;
081            System.arraycopy(ZipLong.getBytes(nameCRC32), 0, data, 1, 4);
082            System.arraycopy(unicodeName, 0, data, 5, unicodeName.length);
083        }
084    
085        /**
086         * @return The CRC32 checksum of the filename or comment as
087         *         encoded in the central directory of the zip file.
088         */
089        public long getNameCRC32() {
090            return nameCRC32;
091        }
092    
093        /**
094         * @param nameCRC32 The CRC32 checksum of the filename as encoded
095         *         in the central directory of the zip file to set.
096         */
097        public void setNameCRC32(long nameCRC32) {
098            this.nameCRC32 = nameCRC32;
099            data = null;
100        }
101    
102        /**
103         * @return The utf-8 encoded name.
104         */
105        public byte[] getUnicodeName() {
106            return unicodeName;
107        }
108    
109        /**
110         * @param unicodeName The utf-8 encoded name to set.
111         */
112        public void setUnicodeName(byte[] unicodeName) {
113            this.unicodeName = unicodeName;
114            data = null;
115        }
116    
117        public byte[] getCentralDirectoryData() {
118            if (data == null) {
119                this.assembleData();
120            }
121            return data;
122        }
123    
124        public ZipShort getCentralDirectoryLength() {
125            if (data == null) {
126                assembleData();
127            }
128            return new ZipShort(data.length);
129        }
130    
131        public byte[] getLocalFileDataData() {
132            return getCentralDirectoryData();
133        }
134    
135        public ZipShort getLocalFileDataLength() {
136            return getCentralDirectoryLength();
137        }
138    
139        public void parseFromLocalFileData(byte[] buffer, int offset, int length)
140            throws ZipException {
141    
142            if (length < 5) {
143                throw new ZipException("UniCode path extra data must have at least 5 bytes.");
144            }
145    
146            int version = buffer[offset];
147    
148            if (version != 0x01) {
149                throw new ZipException("Unsupported version [" + version
150                                       + "] for UniCode path extra data.");
151            }
152    
153            nameCRC32 = ZipLong.getValue(buffer, offset + 1);
154            unicodeName = new byte[length - 5];
155            System.arraycopy(buffer, offset + 5, unicodeName, 0, length - 5);
156            data = null;
157        }
158    
159        /**
160         * Doesn't do anything special since this class always uses the
161         * same data in central directory and local file data.
162         */
163        public void parseFromCentralDirectoryData(byte[] buffer, int offset,
164                                                  int length)
165            throws ZipException {
166            parseFromLocalFileData(buffer, offset, length);
167        }
168    }