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 byte[] b = null;
107 if (unicodeName != null) {
108 b = new byte[unicodeName.length];
109 System.arraycopy(unicodeName, 0, b, 0, b.length);
110 }
111 return b;
112 }
113
114 /**
115 * @param unicodeName The utf-8 encoded name to set.
116 */
117 public void setUnicodeName(byte[] unicodeName) {
118 if (unicodeName != null) {
119 this.unicodeName = new byte[unicodeName.length];
120 System.arraycopy(unicodeName, 0, this.unicodeName, 0,
121 unicodeName.length);
122 } else {
123 this.unicodeName = null;
124 }
125 data = null;
126 }
127
128 /** {@inheritDoc} */
129 public byte[] getCentralDirectoryData() {
130 if (data == null) {
131 this.assembleData();
132 }
133 byte[] b = null;
134 if (data != null) {
135 b = new byte[data.length];
136 System.arraycopy(data, 0, b, 0, b.length);
137 }
138 return b;
139 }
140
141 /** {@inheritDoc} */
142 public ZipShort getCentralDirectoryLength() {
143 if (data == null) {
144 assembleData();
145 }
146 return new ZipShort(data.length);
147 }
148
149 /** {@inheritDoc} */
150 public byte[] getLocalFileDataData() {
151 return getCentralDirectoryData();
152 }
153
154 /** {@inheritDoc} */
155 public ZipShort getLocalFileDataLength() {
156 return getCentralDirectoryLength();
157 }
158
159 /** {@inheritDoc} */
160 public void parseFromLocalFileData(byte[] buffer, int offset, int length)
161 throws ZipException {
162
163 if (length < 5) {
164 throw new ZipException("UniCode path extra data must have at least 5 bytes.");
165 }
166
167 int version = buffer[offset];
168
169 if (version != 0x01) {
170 throw new ZipException("Unsupported version [" + version
171 + "] for UniCode path extra data.");
172 }
173
174 nameCRC32 = ZipLong.getValue(buffer, offset + 1);
175 unicodeName = new byte[length - 5];
176 System.arraycopy(buffer, offset + 5, unicodeName, 0, length - 5);
177 data = null;
178 }
179
180 /**
181 * Doesn't do anything special since this class always uses the
182 * same data in central directory and local file data.
183 */
184 public void parseFromCentralDirectoryData(byte[] buffer, int offset,
185 int length)
186 throws ZipException {
187 parseFromLocalFileData(buffer, offset, length);
188 }
189 }