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 019package org.apache.commons.compress.archivers.zip; 020 021import static java.nio.charset.StandardCharsets.UTF_8; 022 023import java.util.Arrays; 024import java.util.zip.CRC32; 025import java.util.zip.ZipException; 026 027/** 028 * A common base class for Unicode extra information extra fields. 029 * @NotThreadSafe 030 */ 031public abstract class AbstractUnicodeExtraField implements ZipExtraField { 032 private long nameCRC32; 033 private byte[] unicodeName; 034 private byte[] data; 035 036 protected AbstractUnicodeExtraField() { 037 } 038 039 /** 040 * Assemble as unicode extension from the name/comment and 041 * encoding of the original ZIP entry. 042 * 043 * @param text The file name or comment. 044 * @param bytes The encoded of the file name or comment in the ZIP 045 * file. 046 */ 047 protected AbstractUnicodeExtraField(final String text, final byte[] bytes) { 048 this(text, bytes, 0, bytes.length); 049 } 050 051 /** 052 * Assemble as unicode extension from the name/comment and 053 * 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 057 * file. 058 * @param off The offset of the encoded file name or comment in 059 * {@code bytes}. 060 * @param len The length of the encoded file name or comment in 061 * {@code bytes}. 062 */ 063 protected AbstractUnicodeExtraField(final String text, final byte[] bytes, final int off, final int len) { 064 final CRC32 crc32 = new CRC32(); 065 crc32.update(bytes, off, len); 066 nameCRC32 = crc32.getValue(); 067 068 unicodeName = text.getBytes(UTF_8); 069 } 070 071 private void assembleData() { 072 if (unicodeName == null) { 073 return; 074 } 075 076 data = new byte[5 + unicodeName.length]; 077 // version 1 078 data[0] = 0x01; 079 System.arraycopy(ZipLong.getBytes(nameCRC32), 0, data, 1, 4); 080 System.arraycopy(unicodeName, 0, data, 5, unicodeName.length); 081 } 082 083 @Override 084 public byte[] getCentralDirectoryData() { 085 if (data == null) { 086 this.assembleData(); 087 } 088 byte[] b = null; 089 if (data != null) { 090 b = Arrays.copyOf(data, data.length); 091 } 092 return b; 093 } 094 095 @Override 096 public ZipShort getCentralDirectoryLength() { 097 if (data == null) { 098 assembleData(); 099 } 100 return new ZipShort(data != null ? data.length : 0); 101 } 102 103 @Override 104 public byte[] getLocalFileDataData() { 105 return getCentralDirectoryData(); 106 } 107 108 @Override 109 public ZipShort getLocalFileDataLength() { 110 return getCentralDirectoryLength(); 111 } 112 113 /** 114 * @return The CRC32 checksum of the file name or comment as 115 * encoded in the central directory of the ZIP file. 116 */ 117 public long getNameCRC32() { 118 return nameCRC32; 119 } 120 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 130 * same data in central directory and local file data. 131 */ 132 @Override 133 public void parseFromCentralDirectoryData(final byte[] buffer, final int offset, 134 final int length) 135 throws ZipException { 136 parseFromLocalFileData(buffer, offset, length); 137 } 138 139 @Override 140 public void parseFromLocalFileData(final byte[] buffer, final int offset, final int length) 141 throws ZipException { 142 143 if (length < 5) { 144 throw new ZipException("UniCode path extra data must have at least 5 bytes."); 145 } 146 147 final int version = buffer[offset]; 148 149 if (version != 0x01) { 150 throw new ZipException("Unsupported version [" + version 151 + "] for UniCode path extra data."); 152 } 153 154 nameCRC32 = ZipLong.getValue(buffer, offset + 1); 155 unicodeName = new byte[length - 5]; 156 System.arraycopy(buffer, offset + 5, unicodeName, 0, length - 5); 157 data = null; 158 } 159 160 /** 161 * @param nameCRC32 The CRC32 checksum of the file name as encoded 162 * in the central directory of the ZIP file to set. 163 */ 164 public void setNameCRC32(final long nameCRC32) { 165 this.nameCRC32 = nameCRC32; 166 data = null; 167 } 168 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}