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