| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==================================================================== */ |
| |
| package org.apache.poi.xssf.streaming; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.zip.ZipEntry; |
| |
| import static java.nio.charset.StandardCharsets.US_ASCII; |
| |
| /** |
| * Excel compatible Zip64 implementation. |
| * For more information see https://github.com/rzymek/opczip |
| */ |
| class Zip64Impl { |
| private static final long PK0102 = 0x02014b50L; |
| private static final long PK0304 = 0x04034b50L; |
| private static final long PK0506 = 0x06054b50L; |
| private static final long PK0708 = 0x08074b50L; |
| |
| private static final int VERSION_20 = 20; |
| private static final int VERSION_45 = 45; |
| private static final int DATA_DESCRIPTOR_USED = 0x08; |
| private static final int ZIP64_FIELD = 0x0001; |
| private static final long MAX32 = 0xffffffffL; |
| |
| private final OutputStream out; |
| private int written = 0; |
| |
| static class Entry { |
| final String filename; |
| long crc; |
| long size; |
| int compressedSize; |
| int offset; |
| |
| Entry(String filename) { |
| this.filename = filename; |
| } |
| } |
| |
| Zip64Impl(OutputStream out) { |
| this.out = out; |
| } |
| |
| /** |
| * Write Local File Header |
| */ |
| int writeLFH(Entry entry) throws IOException { |
| written = 0; |
| writeInt(PK0304); // "PK\003\004" |
| writeShort(VERSION_45); // version required: 4.5 |
| writeShort(DATA_DESCRIPTOR_USED); // flags: 8 = data descriptor used |
| writeShort(ZipEntry.DEFLATED); // compression method: 8 = deflate |
| writeInt(0); // file modification time & date |
| writeInt(entry.crc); // CRC-32 |
| writeInt(0); // compressed file size |
| writeInt(0); // uncompressed file size |
| writeShort(entry.filename.length()); // filename length |
| writeShort(0); // extra flags size |
| byte[] filenameBytes = entry.filename.getBytes(US_ASCII); |
| out.write(filenameBytes); // filename characters |
| return written + filenameBytes.length; |
| } |
| |
| /** |
| * Write Data Descriptor |
| */ |
| int writeDAT(Entry entry) throws IOException { |
| written = 0; |
| writeInt(PK0708); // data descriptor signature "PK\007\008" |
| writeInt(entry.crc); // crc-32 |
| writeLong(entry.compressedSize); // compressed size (zip64) |
| writeLong(entry.size); // uncompressed size (zip64) |
| return written; |
| } |
| |
| /** |
| * Write Central directory file header |
| */ |
| int writeCEN(Entry entry) throws IOException { |
| written = 0; |
| boolean useZip64 = entry.size > MAX32; |
| writeInt(PK0102); // "PK\001\002" |
| writeShort(VERSION_45); // version made by: 4.5 |
| writeShort(useZip64 ? VERSION_45 : VERSION_20);// version required: 4.5 |
| writeShort(DATA_DESCRIPTOR_USED); // flags: 8 = data descriptor used |
| writeShort(ZipEntry.DEFLATED); // compression method: 8 = deflate |
| writeInt(0); // file modification time & date |
| writeInt(entry.crc); // CRC-32 |
| writeInt(entry.compressedSize); // compressed size |
| writeInt(useZip64 ? MAX32 : entry.size); // uncompressed size |
| writeShort(entry.filename.length()); // filename length |
| writeShort(useZip64 |
| ? (2 + 2 + 8) /* short + short + long*/ |
| : 0); // extra field len |
| writeShort(0); // comment length |
| writeShort(0); // disk number where file starts |
| writeShort(0); // internal file attributes (unused) |
| writeInt(0); // external file attributes (unused) |
| writeInt(entry.offset); // LFH offset |
| byte[] filenameBytes = entry.filename.getBytes(US_ASCII); |
| out.write(filenameBytes); // filename characters |
| if (useZip64) { |
| // Extra field: |
| writeShort(ZIP64_FIELD); // ZIP64 field signature |
| writeShort(8); // size of extra field (below) |
| writeLong(entry.size); // uncompressed size |
| } |
| return written + filenameBytes.length; |
| } |
| |
| /** |
| * Write End of central directory record (EOCD) |
| */ |
| int writeEND(int entriesCount, int offset, int length) throws IOException { |
| written = 0; |
| writeInt(PK0506); // "PK\005\006" |
| writeShort(0); // number of this disk |
| writeShort(0); // central directory start disk |
| writeShort(entriesCount); // number of directory entries on disk |
| writeShort(entriesCount); // total number of directory entries |
| writeInt(length); // length of central directory |
| writeInt(offset); // offset of central directory |
| writeShort(0); // comment length |
| return written; |
| } |
| |
| /** |
| * Writes a 16-bit short to the output stream in little-endian byte order. |
| */ |
| private void writeShort(int v) throws IOException { |
| OutputStream out = this.out; |
| out.write((v >>> 0) & 0xff); |
| out.write((v >>> 8) & 0xff); |
| written += 2; |
| } |
| |
| /** |
| * Writes a 32-bit int to the output stream in little-endian byte order. |
| */ |
| private void writeInt(long v) throws IOException { |
| OutputStream out = this.out; |
| out.write((int) ((v >>> 0) & 0xff)); |
| out.write((int) ((v >>> 8) & 0xff)); |
| out.write((int) ((v >>> 16) & 0xff)); |
| out.write((int) ((v >>> 24) & 0xff)); |
| written += 4; |
| } |
| |
| /** |
| * Writes a 64-bit int to the output stream in little-endian byte order. |
| */ |
| private void writeLong(long v) throws IOException { |
| OutputStream out = this.out; |
| out.write((int) ((v >>> 0) & 0xff)); |
| out.write((int) ((v >>> 8) & 0xff)); |
| out.write((int) ((v >>> 16) & 0xff)); |
| out.write((int) ((v >>> 24) & 0xff)); |
| out.write((int) ((v >>> 32) & 0xff)); |
| out.write((int) ((v >>> 40) & 0xff)); |
| out.write((int) ((v >>> 48) & 0xff)); |
| out.write((int) ((v >>> 56) & 0xff)); |
| written += 8; |
| } |
| |
| } |
| |