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