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 }