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 /** {@inheritDoc} */
118 public byte[] getCentralDirectoryData() {
119 if (data == null) {
120 this.assembleData();
121 }
122 return data;
123 }
124
125 /** {@inheritDoc} */
126 public ZipShort getCentralDirectoryLength() {
127 if (data == null) {
128 assembleData();
129 }
130 return new ZipShort(data.length);
131 }
132
133 /** {@inheritDoc} */
134 public byte[] getLocalFileDataData() {
135 return getCentralDirectoryData();
136 }
137
138 /** {@inheritDoc} */
139 public ZipShort getLocalFileDataLength() {
140 return getCentralDirectoryLength();
141 }
142
143 /** {@inheritDoc} */
144 public void parseFromLocalFileData(byte[] buffer, int offset, int length)
145 throws ZipException {
146
147 if (length < 5) {
148 throw new ZipException("UniCode path extra data must have at least 5 bytes.");
149 }
150
151 int version = buffer[offset];
152
153 if (version != 0x01) {
154 throw new ZipException("Unsupported version [" + version
155 + "] for UniCode path extra data.");
156 }
157
158 nameCRC32 = ZipLong.getValue(buffer, offset + 1);
159 unicodeName = new byte[length - 5];
160 System.arraycopy(buffer, offset + 5, unicodeName, 0, length - 5);
161 data = null;
162 }
163
164 /**
165 * Doesn't do anything special since this class always uses the
166 * same data in central directory and local file data.
167 */
168 public void parseFromCentralDirectoryData(byte[] buffer, int offset,
169 int length)
170 throws ZipException {
171 parseFromLocalFileData(buffer, offset, length);
172 }
173 }