001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020package org.apache.commons.compress.archivers.zip; 021 022import static java.nio.charset.StandardCharsets.UTF_8; 023 024import java.util.Arrays; 025import java.util.zip.CRC32; 026import java.util.zip.ZipException; 027 028/** 029 * A common base class for Unicode extra information extra fields. 030 * 031 * @NotThreadSafe 032 */ 033public abstract class AbstractUnicodeExtraField implements ZipExtraField { 034 private long nameCRC32; 035 private byte[] unicodeName; 036 private byte[] data; 037 038 /** 039 * Constructs a new instance. 040 */ 041 protected AbstractUnicodeExtraField() { 042 } 043 044 /** 045 * Assemble as unicode extension from the name/comment and encoding of the original ZIP entry. 046 * 047 * @param text The file name or comment. 048 * @param bytes The encoded of the file name or comment in the ZIP file. 049 */ 050 protected AbstractUnicodeExtraField(final String text, final byte[] bytes) { 051 this(text, bytes, 0, bytes.length); 052 } 053 054 /** 055 * Assemble as unicode extension from the name/comment and 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 file. 059 * @param off The offset of the encoded file name or comment in {@code bytes}. 060 * @param len The length of the encoded file name or comment in {@code bytes}. 061 */ 062 protected AbstractUnicodeExtraField(final String text, final byte[] bytes, final int off, final int len) { 063 final CRC32 crc32 = new CRC32(); 064 crc32.update(bytes, off, len); 065 nameCRC32 = crc32.getValue(); 066 067 unicodeName = text.getBytes(UTF_8); 068 } 069 070 private void assembleData() { 071 if (unicodeName == null) { 072 return; 073 } 074 075 data = new byte[5 + unicodeName.length]; 076 // version 1 077 data[0] = 0x01; 078 System.arraycopy(ZipLong.getBytes(nameCRC32), 0, data, 1, 4); 079 System.arraycopy(unicodeName, 0, data, 5, unicodeName.length); 080 } 081 082 @Override 083 public byte[] getCentralDirectoryData() { 084 if (data == null) { 085 assembleData(); 086 } 087 byte[] b = null; 088 if (data != null) { 089 b = Arrays.copyOf(data, data.length); 090 } 091 return b; 092 } 093 094 @Override 095 public ZipShort getCentralDirectoryLength() { 096 if (data == null) { 097 assembleData(); 098 } 099 return ZipShort.lengthOf(data); 100 } 101 102 @Override 103 public byte[] getLocalFileDataData() { 104 return getCentralDirectoryData(); 105 } 106 107 @Override 108 public ZipShort getLocalFileDataLength() { 109 return getCentralDirectoryLength(); 110 } 111 112 /** 113 * Gets the CRC32 checksum of the file name or comment as encoded in the central directory of the ZIP file. 114 * 115 * @return The CRC32 checksum of the file name or comment as encoded in the central directory of the ZIP file. 116 */ 117 public long getNameCRC32() { 118 return nameCRC32; 119 } 120 121 /** 122 * Gets The UTF-8 encoded name. 123 * 124 * @return The UTF-8 encoded name. 125 */ 126 public byte[] getUnicodeName() { 127 return unicodeName != null ? Arrays.copyOf(unicodeName, unicodeName.length) : null; 128 } 129 130 /** 131 * Doesn't do anything special since this class always uses the same data in central directory and local file data. 132 */ 133 @Override 134 public void parseFromCentralDirectoryData(final byte[] buffer, final int offset, final int length) throws ZipException { 135 parseFromLocalFileData(buffer, offset, length); 136 } 137 138 @Override 139 public void parseFromLocalFileData(final byte[] buffer, final int offset, final int length) throws ZipException { 140 141 if (length < 5) { 142 throw new ZipException("UniCode path extra data must have at least 5 bytes."); 143 } 144 145 final int version = buffer[offset]; 146 147 if (version != 0x01) { 148 throw new ZipException("Unsupported version [" + version + "] for UniCode path extra data."); 149 } 150 151 nameCRC32 = ZipLong.getValue(buffer, offset + 1); 152 unicodeName = new byte[length - 5]; 153 System.arraycopy(buffer, offset + 5, unicodeName, 0, length - 5); 154 data = null; 155 } 156 157 /** 158 * Gets The CRC32 checksum of the file name as encoded in the central directory of the ZIP file to set. 159 * 160 * @param nameCRC32 The CRC32 checksum of the file name as encoded in the central directory of the ZIP file to set. 161 */ 162 public void setNameCRC32(final long nameCRC32) { 163 this.nameCRC32 = nameCRC32; 164 data = null; 165 } 166 167 /** 168 * Gets the UTF-8 encoded name to set. 169 * 170 * @param unicodeName The UTF-8 encoded name to set. 171 */ 172 public void setUnicodeName(final byte[] unicodeName) { 173 if (unicodeName != null) { 174 this.unicodeName = Arrays.copyOf(unicodeName, unicodeName.length); 175 } else { 176 this.unicodeName = null; 177 } 178 data = null; 179 } 180}