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 018package org.apache.commons.compress.archivers.zip; 019 020import static java.nio.charset.StandardCharsets.UTF_8; 021 022import java.util.Arrays; 023import java.util.zip.CRC32; 024import java.util.zip.ZipException; 025 026/** 027 * A common base class for Unicode extra information extra fields. 028 * @NotThreadSafe 029 */ 030public abstract class AbstractUnicodeExtraField implements ZipExtraField { 031 private long nameCRC32; 032 private byte[] unicodeName; 033 private byte[] data; 034 035 /** 036 * Constructs a new instance. 037 */ 038 protected AbstractUnicodeExtraField() { 039 } 040 041 /** 042 * Assemble as unicode extension from the name/comment and 043 * encoding of the original ZIP entry. 044 * 045 * @param text The file name or comment. 046 * @param bytes The encoded of the file name or comment in the ZIP 047 * file. 048 */ 049 protected AbstractUnicodeExtraField(final String text, final byte[] bytes) { 050 this(text, bytes, 0, bytes.length); 051 } 052 053 /** 054 * Assemble as unicode extension from the name/comment and 055 * encoding of the original ZIP entry. 056 * 057 * @param text The file name or comment. 058 * @param bytes The encoded of the file name or comment in the ZIP 059 * file. 060 * @param off The offset of the encoded file name or comment in 061 * {@code bytes}. 062 * @param len The length of the encoded file name or comment in 063 * {@code bytes}. 064 */ 065 protected AbstractUnicodeExtraField(final String text, final byte[] bytes, final int off, final int len) { 066 final CRC32 crc32 = new CRC32(); 067 crc32.update(bytes, off, len); 068 nameCRC32 = crc32.getValue(); 069 070 unicodeName = text.getBytes(UTF_8); 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 @Override 086 public byte[] getCentralDirectoryData() { 087 if (data == null) { 088 this.assembleData(); 089 } 090 byte[] b = null; 091 if (data != null) { 092 b = Arrays.copyOf(data, data.length); 093 } 094 return b; 095 } 096 097 @Override 098 public ZipShort getCentralDirectoryLength() { 099 if (data == null) { 100 assembleData(); 101 } 102 return new ZipShort(data != null ? data.length : 0); 103 } 104 105 @Override 106 public byte[] getLocalFileDataData() { 107 return getCentralDirectoryData(); 108 } 109 110 @Override 111 public ZipShort getLocalFileDataLength() { 112 return getCentralDirectoryLength(); 113 } 114 115 /** 116 * Gets the CRC32 checksum of the file name or comment as encoded in the central directory of the ZIP file. 117 * 118 * @return The CRC32 checksum of the file name or comment as encoded in the central directory of the ZIP file. 119 */ 120 public long getNameCRC32() { 121 return nameCRC32; 122 } 123 124 /** 125 * Gets The UTF-8 encoded name. 126 * 127 * @return The UTF-8 encoded name. 128 */ 129 public byte[] getUnicodeName() { 130 return unicodeName != null ? Arrays.copyOf(unicodeName, unicodeName.length) : null; 131 } 132 133 /** 134 * Doesn't do anything special since this class always uses the 135 * same data in central directory and local file data. 136 */ 137 @Override 138 public void parseFromCentralDirectoryData(final byte[] buffer, final int offset, 139 final int length) 140 throws ZipException { 141 parseFromLocalFileData(buffer, offset, length); 142 } 143 144 @Override 145 public void parseFromLocalFileData(final byte[] buffer, final int offset, final int length) 146 throws ZipException { 147 148 if (length < 5) { 149 throw new ZipException("UniCode path extra data must have at least 5 bytes."); 150 } 151 152 final int version = buffer[offset]; 153 154 if (version != 0x01) { 155 throw new ZipException("Unsupported version [" + version 156 + "] for UniCode path extra data."); 157 } 158 159 nameCRC32 = ZipLong.getValue(buffer, offset + 1); 160 unicodeName = new byte[length - 5]; 161 System.arraycopy(buffer, offset + 5, unicodeName, 0, length - 5); 162 data = null; 163 } 164 165 /** 166 * Gets The CRC32 checksum of the file name as encoded in the central directory of the ZIP file to set. 167 * 168 * @param nameCRC32 The CRC32 checksum of the file name as encoded in the central directory of the ZIP file to set. 169 */ 170 public void setNameCRC32(final long nameCRC32) { 171 this.nameCRC32 = nameCRC32; 172 data = null; 173 } 174 175 /** 176 * Gets the UTF-8 encoded name to set. 177 * 178 * @param unicodeName The UTF-8 encoded name to set. 179 */ 180 public void setUnicodeName(final byte[] unicodeName) { 181 if (unicodeName != null) { 182 this.unicodeName = Arrays.copyOf(unicodeName, unicodeName.length); 183 } else { 184 this.unicodeName = null; 185 } 186 data = null; 187 } 188}