/**
   * Writes the "End of central dir record".
   *
   * @throws IOException on error
   * @throws Zip64RequiredException if the archive's size exceeds 4 GByte or there are more than
   *     65535 entries inside the archive and {@link Zip64Mode #setUseZip64} is {@link
   *     Zip64Mode#Never}.
   */
  protected void writeCentralDirectoryEnd() throws IOException {
    writeOut(EOCD_SIG);

    // disk numbers
    writeOut(ZERO);
    writeOut(ZERO);

    // number of entries
    int numberOfEntries = entries.size();
    if (numberOfEntries > ZIP64_MAGIC_SHORT && zip64Mode == Zip64Mode.Never) {
      throw new Zip64RequiredException(Zip64RequiredException.TOO_MANY_ENTRIES_MESSAGE);
    }
    if (cdOffset > ZIP64_MAGIC && zip64Mode == Zip64Mode.Never) {
      throw new Zip64RequiredException(Zip64RequiredException.ARCHIVE_TOO_BIG_MESSAGE);
    }

    byte[] num = ZipShort.getBytes(Math.min(numberOfEntries, ZIP64_MAGIC_SHORT));
    writeOut(num);
    writeOut(num);

    // length and location of CD
    writeOut(ZipLong.getBytes(Math.min(cdLength, ZIP64_MAGIC)));
    writeOut(ZipLong.getBytes(Math.min(cdOffset, ZIP64_MAGIC)));

    // ZIP file comment
    ByteBuffer data = this.zipEncoding.encode(comment);
    writeOut(ZipShort.getBytes(data.limit()));
    writeOut(data.array(), data.arrayOffset(), data.limit() - data.position());
  }
 /**
  * Looks up an extra field by its header id.
  *
  * @param type the header id
  * @return null if no such field exists.
  */
 public ZipExtraField getExtraField(ZipShort type) {
   if (extraFields != null) {
     for (ZipExtraField extraField : extraFields) {
       if (type.equals(extraField.getHeaderId())) {
         return extraField;
       }
     }
   }
   return null;
 }
  /**
   * Remove an extra field.
   *
   * @param type the type of extra field to remove
   */
  public void removeExtraField(ZipShort type) {
    if (extraFields == null) {
      throw new java.util.NoSuchElementException();
    }

    List<ZipExtraField> newResult = new ArrayList<ZipExtraField>();
    for (ZipExtraField extraField : extraFields) {
      if (!type.equals(extraField.getHeaderId())) {
        newResult.add(extraField);
      }
    }
    if (extraFields.length == newResult.size()) {
      throw new java.util.NoSuchElementException();
    }
    extraFields = newResult.toArray(new ZipExtraField[newResult.size()]);
    setExtra();
  }
  /**
   * When using random access output, write the local file header and potentiall the ZIP64 extra
   * containing the correct CRC and compressed/uncompressed sizes.
   */
  private void rewriteSizesAndCrc(boolean actuallyNeedsZip64) throws IOException {
    long save = raf.getFilePointer();

    raf.seek(entry.localDataStart);
    writeOut(ZipLong.getBytes(entry.entry.getCrc()));
    if (!hasZip64Extra(entry.entry) || !actuallyNeedsZip64) {
      writeOut(ZipLong.getBytes(entry.entry.getCompressedSize()));
      writeOut(ZipLong.getBytes(entry.entry.getSize()));
    } else {
      writeOut(ZipLong.ZIP64_MAGIC.getBytes());
      writeOut(ZipLong.ZIP64_MAGIC.getBytes());
    }

    if (hasZip64Extra(entry.entry)) {
      // seek to ZIP64 extra, skip header and size information
      raf.seek(
          entry.localDataStart + 3 * WORD + 2 * SHORT + getName(entry.entry).limit() + 2 * SHORT);
      // inside the ZIP64 extra uncompressed size comes
      // first, unlike the LFH, CD or data descriptor
      writeOut(ZipEightByteInteger.getBytes(entry.entry.getSize()));
      writeOut(ZipEightByteInteger.getBytes(entry.entry.getCompressedSize()));

      if (!actuallyNeedsZip64) {
        // do some cleanup:
        // * rewrite version needed to extract
        raf.seek(entry.localDataStart - 5 * SHORT);
        writeOut(ZipShort.getBytes(INITIAL_VERSION));

        // * remove ZIP64 extra so it doesn't get written
        //   to the central directory
        entry.entry.removeExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
        entry.entry.setExtra();

        // * reset hasUsedZip64 if it has been set because
        //   of this entry
        if (entry.causedUseOfZip64) {
          hasUsedZip64 = false;
        }
      }
    }
    raf.seek(save);
  }
  private void writeVersionNeededToExtractAndGeneralPurposeBits(
      final int zipMethod, final boolean utfFallback, final boolean zip64) throws IOException {

    // CheckStyle:MagicNumber OFF
    int versionNeededToExtract = INITIAL_VERSION;
    GeneralPurposeBit b = new GeneralPurposeBit();
    b.useUTF8ForNames(useUTF8Flag || utfFallback);
    if (zipMethod == DEFLATED && raf == null) {
      // requires version 2 as we are going to store length info
      // in the data descriptor
      versionNeededToExtract = DATA_DESCRIPTOR_MIN_VERSION;
      b.useDataDescriptor(true);
    }
    if (zip64) {
      versionNeededToExtract = ZIP64_MIN_VERSION;
    }
    // CheckStyle:MagicNumber ON

    // version needed to extract
    writeOut(ZipShort.getBytes(versionNeededToExtract));
    // general purpose bit flag
    writeOut(b.encode());
  }
  /**
   * Writes the central file header entry.
   *
   * @param ze the entry to write
   * @throws IOException on error
   * @throws Zip64RequiredException if the archive's size exceeds 4 GByte and {@link Zip64Mode
   *     #setUseZip64} is {@link Zip64Mode#Never}.
   */
  protected void writeCentralFileHeader(ZipArchiveEntry ze) throws IOException {
    writeOut(CFH_SIG);
    written += WORD;

    final long lfhOffset = offsets.get(ze).longValue();
    final boolean needsZip64Extra =
        hasZip64Extra(ze)
            || ze.getCompressedSize() >= ZIP64_MAGIC
            || ze.getSize() >= ZIP64_MAGIC
            || lfhOffset >= ZIP64_MAGIC;

    if (needsZip64Extra && zip64Mode == Zip64Mode.Never) {
      // must be the offset that is too big, otherwise an
      // exception would have been throw in putArchiveEntry or
      // closeArchiveEntry
      throw new Zip64RequiredException(Zip64RequiredException.ARCHIVE_TOO_BIG_MESSAGE);
    }

    handleZip64Extra(ze, lfhOffset, needsZip64Extra);

    // version made by
    // CheckStyle:MagicNumber OFF
    writeOut(
        ZipShort.getBytes(
            (ze.getPlatform() << 8)
                | (!hasUsedZip64 ? DATA_DESCRIPTOR_MIN_VERSION : ZIP64_MIN_VERSION)));
    written += SHORT;

    final int zipMethod = ze.getMethod();
    final boolean encodable = zipEncoding.canEncode(ze.getName());
    writeVersionNeededToExtractAndGeneralPurposeBits(
        zipMethod, !encodable && fallbackToUTF8, needsZip64Extra);
    written += WORD;

    // compression method
    writeOut(ZipShort.getBytes(zipMethod));
    written += SHORT;

    // last mod. time and date
    writeOut(ZipUtil.toDosTime(ze.getTime()));
    written += WORD;

    // CRC
    // compressed length
    // uncompressed length
    writeOut(ZipLong.getBytes(ze.getCrc()));
    if (ze.getCompressedSize() >= ZIP64_MAGIC || ze.getSize() >= ZIP64_MAGIC) {
      writeOut(ZipLong.ZIP64_MAGIC.getBytes());
      writeOut(ZipLong.ZIP64_MAGIC.getBytes());
    } else {
      writeOut(ZipLong.getBytes(ze.getCompressedSize()));
      writeOut(ZipLong.getBytes(ze.getSize()));
    }
    // CheckStyle:MagicNumber OFF
    written += 12;
    // CheckStyle:MagicNumber ON

    ByteBuffer name = getName(ze);

    writeOut(ZipShort.getBytes(name.limit()));
    written += SHORT;

    // extra field length
    byte[] extra = ze.getCentralDirectoryExtra();
    writeOut(ZipShort.getBytes(extra.length));
    written += SHORT;

    // file comment length
    String comm = ze.getComment();
    if (comm == null) {
      comm = "";
    }

    ByteBuffer commentB = getEntryEncoding(ze).encode(comm);

    writeOut(ZipShort.getBytes(commentB.limit()));
    written += SHORT;

    // disk number start
    writeOut(ZERO);
    written += SHORT;

    // internal file attributes
    writeOut(ZipShort.getBytes(ze.getInternalAttributes()));
    written += SHORT;

    // external file attributes
    writeOut(ZipLong.getBytes(ze.getExternalAttributes()));
    written += WORD;

    // relative offset of LFH
    writeOut(ZipLong.getBytes(Math.min(lfhOffset, ZIP64_MAGIC)));
    written += WORD;

    // file name
    writeOut(name.array(), name.arrayOffset(), name.limit() - name.position());
    written += name.limit();

    // extra field
    writeOut(extra);
    written += extra.length;

    // file comment
    writeOut(commentB.array(), commentB.arrayOffset(), commentB.limit() - commentB.position());
    written += commentB.limit();
  }
  /**
   * Writes the local file header entry
   *
   * @param ze the entry to write
   * @throws IOException on error
   */
  protected void writeLocalFileHeader(ZipArchiveEntry ze) throws IOException {

    boolean encodable = zipEncoding.canEncode(ze.getName());
    ByteBuffer name = getName(ze);

    if (createUnicodeExtraFields != UnicodeExtraFieldPolicy.NEVER) {
      addUnicodeExtraFields(ze, encodable, name);
    }

    offsets.put(ze, Long.valueOf(written));

    writeOut(LFH_SIG);
    written += WORD;

    // store method in local variable to prevent multiple method calls
    final int zipMethod = ze.getMethod();

    writeVersionNeededToExtractAndGeneralPurposeBits(
        zipMethod, !encodable && fallbackToUTF8, hasZip64Extra(ze));
    written += WORD;

    // compression method
    writeOut(ZipShort.getBytes(zipMethod));
    written += SHORT;

    // last mod. time and date
    writeOut(ZipUtil.toDosTime(ze.getTime()));
    written += WORD;

    // CRC
    // compressed length
    // uncompressed length
    entry.localDataStart = written;
    if (zipMethod == DEFLATED || raf != null) {
      writeOut(LZERO);
      if (hasZip64Extra(entry.entry)) {
        // point to ZIP64 extended information extra field for
        // sizes, may get rewritten once sizes are known if
        // stream is seekable
        writeOut(ZipLong.ZIP64_MAGIC.getBytes());
        writeOut(ZipLong.ZIP64_MAGIC.getBytes());
      } else {
        writeOut(LZERO);
        writeOut(LZERO);
      }
    } else {
      writeOut(ZipLong.getBytes(ze.getCrc()));
      byte[] size = ZipLong.ZIP64_MAGIC.getBytes();
      if (!hasZip64Extra(ze)) {
        size = ZipLong.getBytes(ze.getSize());
      }
      writeOut(size);
      writeOut(size);
    }
    // CheckStyle:MagicNumber OFF
    written += 12;
    // CheckStyle:MagicNumber ON

    // file name length
    writeOut(ZipShort.getBytes(name.limit()));
    written += SHORT;

    // extra field length
    byte[] extra = ze.getLocalFileDataExtra();
    writeOut(ZipShort.getBytes(extra.length));
    written += SHORT;

    // file name
    writeOut(name.array(), name.arrayOffset(), name.limit() - name.position());
    written += name.limit();

    // extra field
    writeOut(extra);
    written += extra.length;

    entry.dataStart = written;
  }
  /**
   * Writes the &quot;ZIP64 End of central dir record&quot; and &quot;ZIP64 End of central dir
   * locator&quot;.
   *
   * @throws IOException on error
   * @since 1.3
   */
  protected void writeZip64CentralDirectory() throws IOException {
    if (zip64Mode == Zip64Mode.Never) {
      return;
    }

    if (!hasUsedZip64
        && (cdOffset >= ZIP64_MAGIC
            || cdLength >= ZIP64_MAGIC
            || entries.size() >= ZIP64_MAGIC_SHORT)) {
      // actually "will use"
      hasUsedZip64 = true;
    }

    if (!hasUsedZip64) {
      return;
    }

    long offset = written;

    writeOut(ZIP64_EOCD_SIG);
    // size, we don't have any variable length as we don't support
    // the extensible data sector, yet
    writeOut(
        ZipEightByteInteger.getBytes(
            SHORT /* version made by */
                + SHORT /* version needed to extract */
                + WORD /* disk number */
                + WORD /* disk with central directory */
                + DWORD /* number of entries in CD on this disk */
                + DWORD /* total number of entries */
                + DWORD /* size of CD */
                + DWORD /* offset of CD */));

    // version made by and version needed to extract
    writeOut(ZipShort.getBytes(ZIP64_MIN_VERSION));
    writeOut(ZipShort.getBytes(ZIP64_MIN_VERSION));

    // disk numbers - four bytes this time
    writeOut(LZERO);
    writeOut(LZERO);

    // number of entries
    byte[] num = ZipEightByteInteger.getBytes(entries.size());
    writeOut(num);
    writeOut(num);

    // length and location of CD
    writeOut(ZipEightByteInteger.getBytes(cdLength));
    writeOut(ZipEightByteInteger.getBytes(cdOffset));

    // no "zip64 extensible data sector" for now

    // and now the "ZIP64 end of central directory locator"
    writeOut(ZIP64_EOCD_LOC_SIG);

    // disk number holding the ZIP64 EOCD record
    writeOut(LZERO);
    // relative offset of ZIP64 EOCD record
    writeOut(ZipEightByteInteger.getBytes(offset));
    // total number of disks
    writeOut(ONE);
  }
Example #9
0
 /**
  * Create instance from the two bytes starting at offset.
  *
  * @param bytes the bytes to store as a ZipShort
  * @param offset the offset to start
  */
 public ZipShort(byte[] bytes, int offset) {
   value = ZipShort.getValue(bytes, offset);
 }