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