View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  package org.apache.commons.compress.archivers.zip;
19  
20  import static java.nio.charset.StandardCharsets.UTF_8;
21  
22  import java.util.Arrays;
23  import java.util.zip.CRC32;
24  import java.util.zip.ZipException;
25  
26  /**
27   * A common base class for Unicode extra information extra fields.
28   *
29   * @NotThreadSafe
30   */
31  public abstract class AbstractUnicodeExtraField implements ZipExtraField {
32      private long nameCRC32;
33      private byte[] unicodeName;
34      private byte[] data;
35  
36      /**
37       * Constructs a new instance.
38       */
39      protected AbstractUnicodeExtraField() {
40      }
41  
42      /**
43       * Assemble as unicode extension from the name/comment and encoding of the original ZIP entry.
44       *
45       * @param text  The file name or comment.
46       * @param bytes The encoded of the file name or comment in the ZIP file.
47       */
48      protected AbstractUnicodeExtraField(final String text, final byte[] bytes) {
49          this(text, bytes, 0, bytes.length);
50      }
51  
52      /**
53       * Assemble as unicode extension from the name/comment and encoding of the original ZIP entry.
54       *
55       * @param text  The file name or comment.
56       * @param bytes The encoded of the file name or comment in the ZIP file.
57       * @param off   The offset of the encoded file name or comment in {@code bytes}.
58       * @param len   The length of the encoded file name or comment in {@code bytes}.
59       */
60      protected AbstractUnicodeExtraField(final String text, final byte[] bytes, final int off, final int len) {
61          final CRC32 crc32 = new CRC32();
62          crc32.update(bytes, off, len);
63          nameCRC32 = crc32.getValue();
64  
65          unicodeName = text.getBytes(UTF_8);
66      }
67  
68      private void assembleData() {
69          if (unicodeName == null) {
70              return;
71          }
72  
73          data = new byte[5 + unicodeName.length];
74          // version 1
75          data[0] = 0x01;
76          System.arraycopy(ZipLong.getBytes(nameCRC32), 0, data, 1, 4);
77          System.arraycopy(unicodeName, 0, data, 5, unicodeName.length);
78      }
79  
80      @Override
81      public byte[] getCentralDirectoryData() {
82          if (data == null) {
83              this.assembleData();
84          }
85          byte[] b = null;
86          if (data != null) {
87              b = Arrays.copyOf(data, data.length);
88          }
89          return b;
90      }
91  
92      @Override
93      public ZipShort getCentralDirectoryLength() {
94          if (data == null) {
95              assembleData();
96          }
97          return new ZipShort(data != null ? data.length : 0);
98      }
99  
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 }