• 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 
19 package org.apache.commons.compress.archivers.zip;
20 
21 import java.io.UnsupportedEncodingException;
22 import java.util.zip.CRC32;
23 import java.util.zip.ZipException;
24 
25 import org.apache.commons.compress.utils.CharsetNames;
26 
27 /**
28  * A common base class for Unicode extra information extra fields.
29  * @NotThreadSafe
30  */
31 public abstract class AbstractUnicodeExtraField implements ZipExtraField {
32     private long nameCRC32;
33     private byte[] unicodeName;
34     private byte[] data;
35 
AbstractUnicodeExtraField()36     protected AbstractUnicodeExtraField() {
37     }
38 
39     /**
40      * Assemble as unicode extension from the name/comment and
41      * encoding of the original zip entry.
42      *
43      * @param text The file name or comment.
44      * @param bytes The encoded of the filename or comment in the zip
45      * file.
46      * @param off The offset of the encoded filename or comment in
47      * <code>bytes</code>.
48      * @param len The length of the encoded filename or commentin
49      * <code>bytes</code>.
50      */
AbstractUnicodeExtraField(final String text, final byte[] bytes, final int off, final int len)51     protected AbstractUnicodeExtraField(final String text, final byte[] bytes, final int off, final int len) {
52         final CRC32 crc32 = new CRC32();
53         crc32.update(bytes, off, len);
54         nameCRC32 = crc32.getValue();
55 
56         try {
57             unicodeName = text.getBytes(CharsetNames.UTF_8);
58         } catch (final UnsupportedEncodingException e) {
59             throw new RuntimeException("FATAL: UTF-8 encoding not supported.", e); //NOSONAR
60         }
61     }
62 
63     /**
64      * Assemble as unicode extension from the name/comment and
65      * encoding of the original zip entry.
66      *
67      * @param text The file name or comment.
68      * @param bytes The encoded of the filename or comment in the zip
69      * file.
70      */
AbstractUnicodeExtraField(final String text, final byte[] bytes)71     protected AbstractUnicodeExtraField(final String text, final byte[] bytes) {
72         this(text, bytes, 0, bytes.length);
73     }
74 
assembleData()75     private void assembleData() {
76         if (unicodeName == null) {
77             return;
78         }
79 
80         data = new byte[5 + unicodeName.length];
81         // version 1
82         data[0] = 0x01;
83         System.arraycopy(ZipLong.getBytes(nameCRC32), 0, data, 1, 4);
84         System.arraycopy(unicodeName, 0, data, 5, unicodeName.length);
85     }
86 
87     /**
88      * @return The CRC32 checksum of the filename or comment as
89      *         encoded in the central directory of the zip file.
90      */
getNameCRC32()91     public long getNameCRC32() {
92         return nameCRC32;
93     }
94 
95     /**
96      * @param nameCRC32 The CRC32 checksum of the filename as encoded
97      *         in the central directory of the zip file to set.
98      */
setNameCRC32(final long nameCRC32)99     public void setNameCRC32(final long nameCRC32) {
100         this.nameCRC32 = nameCRC32;
101         data = null;
102     }
103 
104     /**
105      * @return The UTF-8 encoded name.
106      */
getUnicodeName()107     public byte[] getUnicodeName() {
108         byte[] b = null;
109         if (unicodeName != null) {
110             b = new byte[unicodeName.length];
111             System.arraycopy(unicodeName, 0, b, 0, b.length);
112         }
113         return b;
114     }
115 
116     /**
117      * @param unicodeName The UTF-8 encoded name to set.
118      */
setUnicodeName(final byte[] unicodeName)119     public void setUnicodeName(final byte[] unicodeName) {
120         if (unicodeName != null) {
121             this.unicodeName = new byte[unicodeName.length];
122             System.arraycopy(unicodeName, 0, this.unicodeName, 0,
123                              unicodeName.length);
124         } else {
125             this.unicodeName = null;
126         }
127         data = null;
128     }
129 
130     @Override
getCentralDirectoryData()131     public byte[] getCentralDirectoryData() {
132         if (data == null) {
133             this.assembleData();
134         }
135         byte[] b = null;
136         if (data != null) {
137             b = new byte[data.length];
138             System.arraycopy(data, 0, b, 0, b.length);
139         }
140         return b;
141     }
142 
143     @Override
getCentralDirectoryLength()144     public ZipShort getCentralDirectoryLength() {
145         if (data == null) {
146             assembleData();
147         }
148         return new ZipShort(data != null ? data.length : 0);
149     }
150 
151     @Override
getLocalFileDataData()152     public byte[] getLocalFileDataData() {
153         return getCentralDirectoryData();
154     }
155 
156     @Override
getLocalFileDataLength()157     public ZipShort getLocalFileDataLength() {
158         return getCentralDirectoryLength();
159     }
160 
161     @Override
parseFromLocalFileData(final byte[] buffer, final int offset, final int length)162     public void parseFromLocalFileData(final byte[] buffer, final int offset, final int length)
163         throws ZipException {
164 
165         if (length < 5) {
166             throw new ZipException("UniCode path extra data must have at least 5 bytes.");
167         }
168 
169         final int version = buffer[offset];
170 
171         if (version != 0x01) {
172             throw new ZipException("Unsupported version [" + version
173                                    + "] for UniCode path extra data.");
174         }
175 
176         nameCRC32 = ZipLong.getValue(buffer, offset + 1);
177         unicodeName = new byte[length - 5];
178         System.arraycopy(buffer, offset + 5, unicodeName, 0, length - 5);
179         data = null;
180     }
181 
182     /**
183      * Doesn't do anything special since this class always uses the
184      * same data in central directory and local file data.
185      */
186     @Override
parseFromCentralDirectoryData(final byte[] buffer, final int offset, final int length)187     public void parseFromCentralDirectoryData(final byte[] buffer, final int offset,
188                                               final int length)
189         throws ZipException {
190         parseFromLocalFileData(buffer, offset, length);
191     }
192 }
193