AbstractUnicodeExtraField.java

  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. package org.apache.commons.compress.archivers.zip;

  18. import static java.nio.charset.StandardCharsets.UTF_8;

  19. import java.util.Arrays;
  20. import java.util.zip.CRC32;
  21. import java.util.zip.ZipException;

  22. /**
  23.  * A common base class for Unicode extra information extra fields.
  24.  *
  25.  * @NotThreadSafe
  26.  */
  27. public abstract class AbstractUnicodeExtraField implements ZipExtraField {
  28.     private long nameCRC32;
  29.     private byte[] unicodeName;
  30.     private byte[] data;

  31.     /**
  32.      * Constructs a new instance.
  33.      */
  34.     protected AbstractUnicodeExtraField() {
  35.     }

  36.     /**
  37.      * Assemble as unicode extension from the name/comment and encoding of the original ZIP entry.
  38.      *
  39.      * @param text  The file name or comment.
  40.      * @param bytes The encoded of the file name or comment in the ZIP file.
  41.      */
  42.     protected AbstractUnicodeExtraField(final String text, final byte[] bytes) {
  43.         this(text, bytes, 0, bytes.length);
  44.     }

  45.     /**
  46.      * Assemble as unicode extension from the name/comment and encoding of the original ZIP entry.
  47.      *
  48.      * @param text  The file name or comment.
  49.      * @param bytes The encoded of the file name or comment in the ZIP file.
  50.      * @param off   The offset of the encoded file name or comment in {@code bytes}.
  51.      * @param len   The length of the encoded file name or comment in {@code bytes}.
  52.      */
  53.     protected AbstractUnicodeExtraField(final String text, final byte[] bytes, final int off, final int len) {
  54.         final CRC32 crc32 = new CRC32();
  55.         crc32.update(bytes, off, len);
  56.         nameCRC32 = crc32.getValue();

  57.         unicodeName = text.getBytes(UTF_8);
  58.     }

  59.     private void assembleData() {
  60.         if (unicodeName == null) {
  61.             return;
  62.         }

  63.         data = new byte[5 + unicodeName.length];
  64.         // version 1
  65.         data[0] = 0x01;
  66.         System.arraycopy(ZipLong.getBytes(nameCRC32), 0, data, 1, 4);
  67.         System.arraycopy(unicodeName, 0, data, 5, unicodeName.length);
  68.     }

  69.     @Override
  70.     public byte[] getCentralDirectoryData() {
  71.         if (data == null) {
  72.             assembleData();
  73.         }
  74.         byte[] b = null;
  75.         if (data != null) {
  76.             b = Arrays.copyOf(data, data.length);
  77.         }
  78.         return b;
  79.     }

  80.     @Override
  81.     public ZipShort getCentralDirectoryLength() {
  82.         if (data == null) {
  83.             assembleData();
  84.         }
  85.         return new ZipShort(data != null ? data.length : 0);
  86.     }

  87.     @Override
  88.     public byte[] getLocalFileDataData() {
  89.         return getCentralDirectoryData();
  90.     }

  91.     @Override
  92.     public ZipShort getLocalFileDataLength() {
  93.         return getCentralDirectoryLength();
  94.     }

  95.     /**
  96.      * Gets the CRC32 checksum of the file name or comment as encoded in the central directory of the ZIP file.
  97.      *
  98.      * @return The CRC32 checksum of the file name or comment as encoded in the central directory of the ZIP file.
  99.      */
  100.     public long getNameCRC32() {
  101.         return nameCRC32;
  102.     }

  103.     /**
  104.      * Gets The UTF-8 encoded name.
  105.      *
  106.      * @return The UTF-8 encoded name.
  107.      */
  108.     public byte[] getUnicodeName() {
  109.         return unicodeName != null ? Arrays.copyOf(unicodeName, unicodeName.length) : null;
  110.     }

  111.     /**
  112.      * Doesn't do anything special since this class always uses the same data in central directory and local file data.
  113.      */
  114.     @Override
  115.     public void parseFromCentralDirectoryData(final byte[] buffer, final int offset, final int length) throws ZipException {
  116.         parseFromLocalFileData(buffer, offset, length);
  117.     }

  118.     @Override
  119.     public void parseFromLocalFileData(final byte[] buffer, final int offset, final int length) throws ZipException {

  120.         if (length < 5) {
  121.             throw new ZipException("UniCode path extra data must have at least 5 bytes.");
  122.         }

  123.         final int version = buffer[offset];

  124.         if (version != 0x01) {
  125.             throw new ZipException("Unsupported version [" + version + "] for UniCode path extra data.");
  126.         }

  127.         nameCRC32 = ZipLong.getValue(buffer, offset + 1);
  128.         unicodeName = new byte[length - 5];
  129.         System.arraycopy(buffer, offset + 5, unicodeName, 0, length - 5);
  130.         data = null;
  131.     }

  132.     /**
  133.      * Gets The CRC32 checksum of the file name as encoded in the central directory of the ZIP file to set.
  134.      *
  135.      * @param nameCRC32 The CRC32 checksum of the file name as encoded in the central directory of the ZIP file to set.
  136.      */
  137.     public void setNameCRC32(final long nameCRC32) {
  138.         this.nameCRC32 = nameCRC32;
  139.         data = null;
  140.     }

  141.     /**
  142.      * Gets the UTF-8 encoded name to set.
  143.      *
  144.      * @param unicodeName The UTF-8 encoded name to set.
  145.      */
  146.     public void setUnicodeName(final byte[] unicodeName) {
  147.         if (unicodeName != null) {
  148.             this.unicodeName = Arrays.copyOf(unicodeName, unicodeName.length);
  149.         } else {
  150.             this.unicodeName = null;
  151.         }
  152.         data = null;
  153.     }
  154. }