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