Skip to content

Commits on Source 4

......@@ -61,7 +61,7 @@ once again, and makes me support Zip4j as much as I can.
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>2.1.4</version>
<version>2.2.2</version>
</dependency>
~~~~
......
zip4j (2.2.2-1) unstable; urgency=medium
* New upstream version 2.2.2
-- Andrius Merkys <merkys@debian.org> Thu, 10 Oct 2019 09:24:10 -0400
zip4j (2.1.4-1) unstable; urgency=medium
* New upstream version 2.1.4
......
......@@ -6,7 +6,7 @@
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>2.1.5-SNAPSHOT</version>
<version>2.2.3-SNAPSHOT</version>
<name>Zip4j</name>
<description>Zip4j - A Java library for zip files and streams</description>
......
......@@ -37,8 +37,11 @@ import net.lingala.zip4j.tasks.ExtractAllFilesTask.ExtractAllFilesTaskParameters
import net.lingala.zip4j.tasks.ExtractFileTask;
import net.lingala.zip4j.tasks.ExtractFileTask.ExtractFileTaskParameters;
import net.lingala.zip4j.tasks.MergeSplitZipFileTask;
import net.lingala.zip4j.tasks.MergeSplitZipFileTask.MergeSplitZipFileTaskParameters;
import net.lingala.zip4j.tasks.RemoveEntryFromZipFileTask;
import net.lingala.zip4j.tasks.RemoveEntryFromZipFileTask.RemoveEntryFromZipFileTaskParameters;
import net.lingala.zip4j.tasks.SetCommentTask;
import net.lingala.zip4j.tasks.SetCommentTask.SetCommentTaskTaskParameters;
import net.lingala.zip4j.util.FileUtils;
import net.lingala.zip4j.util.Zip4jUtil;
......@@ -46,10 +49,12 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
import static net.lingala.zip4j.util.FileUtils.assertFilesExist;
import static net.lingala.zip4j.util.InternalZipConstants.CHARSET_UTF_8;
import static net.lingala.zip4j.util.UnzipUtil.createZipInputStream;
import static net.lingala.zip4j.util.Zip4jUtil.isStringNotNullAndNotEmpty;
......@@ -74,6 +79,7 @@ public class ZipFile {
private boolean runInThread;
private char[] password;
private HeaderWriter headerWriter = new HeaderWriter();
private Charset charset = CHARSET_UTF_8;
/**
* Creates a new ZipFile instance with the zip file at the location specified in zipFile.
......@@ -144,7 +150,7 @@ public class ZipFile {
zipModel.setSplitLength(splitLength);
new AddFilesToZipTask(progressMonitor, runInThread, zipModel, password, headerWriter).execute(
new AddFilesToZipTaskParameters(filesToAdd, parameters));
new AddFilesToZipTaskParameters(filesToAdd, parameters, charset));
}
/**
......@@ -285,7 +291,7 @@ public class ZipFile {
}
new AddFilesToZipTask(progressMonitor, runInThread, zipModel, password, headerWriter).execute(
new AddFilesToZipTaskParameters(filesToAdd, parameters));
new AddFilesToZipTaskParameters(filesToAdd, parameters, charset));
}
/**
......@@ -357,7 +363,7 @@ public class ZipFile {
}
new AddFolderToZipTask(progressMonitor, runInThread, zipModel, password, headerWriter).execute(
new AddFolderToZipTaskParameters(folderToAdd, zipParameters));
new AddFolderToZipTaskParameters(folderToAdd, zipParameters, charset));
}
/**
......@@ -394,7 +400,7 @@ public class ZipFile {
}
new AddStreamToZipTask(progressMonitor, runInThread, zipModel, password, headerWriter).execute(
new AddStreamToZipTaskParameters(inputStream, parameters));
new AddStreamToZipTaskParameters(inputStream, parameters, charset));
}
/**
......@@ -429,7 +435,7 @@ public class ZipFile {
}
new ExtractAllFilesTask(progressMonitor, runInThread, zipModel, password).execute(
new ExtractAllFilesTaskParameters(destinationPath));
new ExtractAllFilesTaskParameters(destinationPath, charset));
}
/**
......@@ -473,7 +479,7 @@ public class ZipFile {
readZipInfo();
new ExtractFileTask(progressMonitor, runInThread, zipModel, password).execute(
new ExtractFileTaskParameters(destinationPath, fileHeader, newFileName));
new ExtractFileTaskParameters(destinationPath, fileHeader, newFileName, charset));
}
/**
......@@ -670,7 +676,8 @@ public class ZipFile {
throw new ZipException("Zip file format does not allow updating split/spanned files");
}
new RemoveEntryFromZipFileTask(progressMonitor, runInThread, zipModel).execute(fileHeader);
new RemoveEntryFromZipFileTask(progressMonitor, runInThread, zipModel).execute(
new RemoveEntryFromZipFileTaskParameters(fileHeader, charset));
}
/**
......@@ -695,7 +702,8 @@ public class ZipFile {
throw new ZipException("zip model is null, corrupt zip file?");
}
new MergeSplitZipFileTask(progressMonitor, runInThread, zipModel).execute(outputZipFile);
new MergeSplitZipFileTask(progressMonitor, runInThread, zipModel).execute(
new MergeSplitZipFileTaskParameters(outputZipFile, charset));
}
/**
......@@ -723,7 +731,8 @@ public class ZipFile {
throw new ZipException("end of central directory is null, cannot set comment");
}
new SetCommentTask(progressMonitor, runInThread, zipModel).execute(comment);
new SetCommentTask(progressMonitor, runInThread, zipModel).execute(
new SetCommentTaskTaskParameters(comment, charset));
}
/**
......@@ -835,7 +844,7 @@ public class ZipFile {
try (RandomAccessFile randomAccessFile = new RandomAccessFile(zipFile, RandomAccessFileMode.READ.getValue())) {
HeaderReader headerReader = new HeaderReader();
zipModel = headerReader.readAllHeaders(randomAccessFile);
zipModel = headerReader.readAllHeaders(randomAccessFile, charset);
zipModel.setZipFile(zipFile);
} catch (IOException e) {
throw new ZipException(e);
......@@ -879,6 +888,17 @@ public class ZipFile {
return zipFile;
}
public Charset getCharset() {
return charset;
}
public void setCharset(Charset charset) throws IllegalArgumentException {
if(charset == null) {
throw new IllegalArgumentException("charset cannot be null");
}
this.charset = charset;
}
@Override
public String toString() {
return zipFile.toString();
......
......@@ -53,6 +53,7 @@ public class ZipException extends IOException {
public enum Type {
WRONG_PASSWORD,
TASK_CANCELLED_EXCEPTION,
CHECKSUM_MISMATCH,
UNKNOWN
}
}
......@@ -9,9 +9,10 @@ import net.lingala.zip4j.model.enums.AesKeyStrength;
import net.lingala.zip4j.model.enums.CompressionLevel;
import net.lingala.zip4j.model.enums.CompressionMethod;
import net.lingala.zip4j.model.enums.EncryptionMethod;
import net.lingala.zip4j.util.InternalZipConstants;
import net.lingala.zip4j.util.Zip4jUtil;
import java.nio.charset.StandardCharsets;
import java.nio.charset.Charset;
import static net.lingala.zip4j.util.BitUtils.setBit;
import static net.lingala.zip4j.util.BitUtils.unsetBit;
......@@ -19,7 +20,7 @@ import static net.lingala.zip4j.util.FileUtils.isZipEntryDirectory;
public class FileHeaderFactory {
public FileHeader generateFileHeader(ZipParameters zipParameters, boolean isSplitZip, int currentDiskNumberStart)
public FileHeader generateFileHeader(ZipParameters zipParameters, boolean isSplitZip, int currentDiskNumberStart, Charset charset)
throws ZipException {
FileHeader fileHeader = new FileHeader();
......@@ -63,7 +64,7 @@ public class FileHeaderFactory {
fileHeader.setCrc(zipParameters.getEntryCRC());
}
fileHeader.setGeneralPurposeFlag(determineGeneralPurposeBitFlag(fileHeader.isEncrypted(), zipParameters));
fileHeader.setGeneralPurposeFlag(determineGeneralPurposeBitFlag(fileHeader.isEncrypted(), zipParameters, charset));
fileHeader.setDataDescriptorExists(zipParameters.isWriteExtendedLocalFileHeader());
return fileHeader;
}
......@@ -87,10 +88,12 @@ public class FileHeaderFactory {
return localFileHeader;
}
private byte[] determineGeneralPurposeBitFlag(boolean isEncrypted, ZipParameters zipParameters) {
private byte[] determineGeneralPurposeBitFlag(boolean isEncrypted, ZipParameters zipParameters, Charset charset) {
byte[] generalPurposeBitFlag = new byte[2];
generalPurposeBitFlag[0] = generateFirstGeneralPurposeByte(isEncrypted, zipParameters);
if(charset.equals(InternalZipConstants.CHARSET_UTF_8)) {
generalPurposeBitFlag[1] = setBit(generalPurposeBitFlag[1], 3); // set 3rd bit which corresponds to utf-8 file name charset
}
return generalPurposeBitFlag;
}
......@@ -154,6 +157,6 @@ public class FileHeaderFactory {
}
private int determineFileNameLength(String fileName) {
return fileName.getBytes(StandardCharsets.UTF_8).length;
return fileName.getBytes(InternalZipConstants.CHARSET_UTF_8).length;
}
}
......@@ -33,13 +33,14 @@ import net.lingala.zip4j.model.enums.AesKeyStrength;
import net.lingala.zip4j.model.enums.AesVersion;
import net.lingala.zip4j.model.enums.CompressionMethod;
import net.lingala.zip4j.model.enums.EncryptionMethod;
import net.lingala.zip4j.util.InternalZipConstants;
import net.lingala.zip4j.util.RawIO;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
......@@ -47,6 +48,7 @@ import java.util.List;
import static net.lingala.zip4j.headers.HeaderUtil.decodeStringWithCharset;
import static net.lingala.zip4j.util.BitUtils.isBitSet;
import static net.lingala.zip4j.util.InternalZipConstants.ENDHDR;
import static net.lingala.zip4j.util.InternalZipConstants.ZIP_64_SIZE_LIMIT;
/**
* Helper class to read header information for the zip file
......@@ -57,7 +59,7 @@ public class HeaderReader {
private RawIO rawIO = new RawIO();
private byte[] intBuff = new byte[4];
public ZipModel readAllHeaders(RandomAccessFile zip4jRaf) throws IOException {
public ZipModel readAllHeaders(RandomAccessFile zip4jRaf, Charset charset) throws IOException {
zipModel = new ZipModel();
try {
......@@ -79,7 +81,7 @@ public class HeaderReader {
}
}
zipModel.setCentralDirectory(readCentralDirectory(zip4jRaf, rawIO));
zipModel.setCentralDirectory(readCentralDirectory(zip4jRaf, rawIO, charset));
return zipModel;
}
......@@ -118,7 +120,7 @@ public class HeaderReader {
if (commentLength > 0) {
byte[] commentBuf = new byte[commentLength];
zip4jRaf.readFully(commentBuf);
endOfCentralDirectoryRecord.setComment(new String(commentBuf, StandardCharsets.UTF_8));
endOfCentralDirectoryRecord.setComment(new String(commentBuf, InternalZipConstants.CHARSET_UTF_8));
} else {
endOfCentralDirectoryRecord.setComment(null);
}
......@@ -127,7 +129,7 @@ public class HeaderReader {
return endOfCentralDirectoryRecord;
}
private CentralDirectory readCentralDirectory(RandomAccessFile zip4jRaf, RawIO rawIO) throws IOException {
private CentralDirectory readCentralDirectory(RandomAccessFile zip4jRaf, RawIO rawIO, Charset charset) throws IOException {
CentralDirectory centralDirectory = new CentralDirectory();
List<FileHeader> fileHeaders = new ArrayList<>();
......@@ -195,7 +197,7 @@ public class HeaderReader {
if (fileNameLength > 0) {
byte[] fileNameBuff = new byte[fileNameLength];
zip4jRaf.readFully(fileNameBuff);
String fileName = decodeStringWithCharset(fileNameBuff, fileHeader.isFileNameUTF8Encoded());
String fileName = decodeStringWithCharset(fileNameBuff, fileHeader.isFileNameUTF8Encoded(), charset);
if (fileName.contains(":\\")) {
fileName = fileName.substring(fileName.indexOf(":\\") + 2);
......@@ -214,7 +216,7 @@ public class HeaderReader {
if (fileCommentLength > 0) {
byte[] fileCommentBuff = new byte[fileCommentLength];
zip4jRaf.readFully(fileCommentBuff);
fileHeader.setFileComment(decodeStringWithCharset(fileCommentBuff, fileHeader.isFileNameUTF8Encoded()));
fileHeader.setFileComment(decodeStringWithCharset(fileCommentBuff, fileHeader.isFileNameUTF8Encoded(), charset));
}
if (fileHeader.isEncrypted()) {
......@@ -410,18 +412,31 @@ public class HeaderReader {
return;
}
Zip64ExtendedInfo zip64ExtendedInfo = readZip64ExtendedInfo(fileHeader.getExtraDataRecords(), rawIO);
Zip64ExtendedInfo zip64ExtendedInfo = readZip64ExtendedInfo(fileHeader.getExtraDataRecords(), rawIO,
fileHeader.getUncompressedSize(), fileHeader.getCompressedSize(), fileHeader.getOffsetLocalHeader());
if (zip64ExtendedInfo == null) {
return;
}
fileHeader.setZip64ExtendedInfo(zip64ExtendedInfo);
if (zip64ExtendedInfo.getUncompressedSize() != -1) {
fileHeader.setUncompressedSize(zip64ExtendedInfo.getUncompressedSize());
}
if (zip64ExtendedInfo.getCompressedSize() != -1) {
fileHeader.setCompressedSize(zip64ExtendedInfo.getCompressedSize());
}
if (zip64ExtendedInfo.getOffsetLocalHeader() != -1) {
fileHeader.setOffsetLocalHeader(zip64ExtendedInfo.getOffsetLocalHeader());
}
if (zip64ExtendedInfo.getDiskNumberStart() != -1) {
fileHeader.setDiskNumberStart(zip64ExtendedInfo.getDiskNumberStart());
}
}
private void readZip64ExtendedInfo(LocalFileHeader localFileHeader, RawIO rawIO) throws ZipException {
if (localFileHeader == null) {
......@@ -432,18 +447,26 @@ public class HeaderReader {
return;
}
Zip64ExtendedInfo zip64ExtendedInfo = readZip64ExtendedInfo(localFileHeader.getExtraDataRecords(), rawIO);
Zip64ExtendedInfo zip64ExtendedInfo = readZip64ExtendedInfo(localFileHeader.getExtraDataRecords(), rawIO,
localFileHeader.getUncompressedSize(), localFileHeader.getCompressedSize(), 0);
if (zip64ExtendedInfo == null) {
return;
}
localFileHeader.setZip64ExtendedInfo(zip64ExtendedInfo);
if (zip64ExtendedInfo.getUncompressedSize() != -1) {
localFileHeader.setUncompressedSize(zip64ExtendedInfo.getUncompressedSize());
}
if (zip64ExtendedInfo.getCompressedSize() != -1) {
localFileHeader.setCompressedSize(zip64ExtendedInfo.getCompressedSize());
}
}
private Zip64ExtendedInfo readZip64ExtendedInfo(List<ExtraDataRecord> extraDataRecords, RawIO rawIO)
private Zip64ExtendedInfo readZip64ExtendedInfo(List<ExtraDataRecord> extraDataRecords, RawIO rawIO,
long uncompressedSize, long compressedSize, long offsetLocalHeader)
throws ZipException {
for (ExtraDataRecord extraDataRecord : extraDataRecords) {
......@@ -461,17 +484,17 @@ public class HeaderReader {
}
int counter = 0;
if (counter < extraDataRecord.getSizeOfData()) {
if (counter < extraDataRecord.getSizeOfData() && uncompressedSize == ZIP_64_SIZE_LIMIT) {
zip64ExtendedInfo.setUncompressedSize(rawIO.readLongLittleEndian(extraData, counter));
counter += 8;
}
if ( counter < extraDataRecord.getSizeOfData()) {
if ( counter < extraDataRecord.getSizeOfData() && compressedSize == ZIP_64_SIZE_LIMIT) {
zip64ExtendedInfo.setCompressedSize(rawIO.readLongLittleEndian(extraData, counter));
counter += 8;
}
if (counter < extraDataRecord.getSizeOfData()) {
if (counter < extraDataRecord.getSizeOfData() && offsetLocalHeader == ZIP_64_SIZE_LIMIT) {
zip64ExtendedInfo.setOffsetLocalHeader(rawIO.readLongLittleEndian(extraData, counter));
counter += 8;
}
......@@ -504,7 +527,7 @@ public class HeaderReader {
zip4jRaf.seek(zip4jRaf.getFilePointer() - 4 - 4 - 8 - 4 - 4);
}
public LocalFileHeader readLocalFileHeader(InputStream inputStream) throws IOException {
public LocalFileHeader readLocalFileHeader(InputStream inputStream, Charset charset) throws IOException {
LocalFileHeader localFileHeader = new LocalFileHeader();
byte[] intBuff = new byte[4];
......@@ -547,7 +570,7 @@ public class HeaderReader {
// Modified after user reported an issue http://www.lingala.net/zip4j/forum/index.php?topic=2.0
// String fileName = new String(fileNameBuf, "Cp850");
// String fileName = Zip4jUtil.getCp850EncodedString(fileNameBuf);
String fileName = decodeStringWithCharset(fileNameBuf, localFileHeader.isFileNameUTF8Encoded());
String fileName = decodeStringWithCharset(fileNameBuf, localFileHeader.isFileNameUTF8Encoded(), charset);
if (fileName == null) {
throw new ZipException("file name is null, cannot assign file name to local file header");
......
......@@ -3,9 +3,10 @@ package net.lingala.zip4j.headers;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.FileHeader;
import net.lingala.zip4j.model.ZipModel;
import net.lingala.zip4j.util.InternalZipConstants;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.charset.Charset;
import java.util.List;
import static net.lingala.zip4j.util.InternalZipConstants.ZIP_STANDARD_CHARSET;
......@@ -62,9 +63,13 @@ public class HeaderUtil {
return -1;
}
public static String decodeStringWithCharset(byte[] data, boolean isUtf8Encoded) {
public static String decodeStringWithCharset(byte[] data, boolean isUtf8Encoded, Charset charset) {
if(charset != null) {
return new String(data, charset);
}
if (isUtf8Encoded) {
return new String(data, StandardCharsets.UTF_8);
return new String(data, InternalZipConstants.CHARSET_UTF_8);
}
try {
......
......@@ -25,7 +25,6 @@ import net.lingala.zip4j.model.LocalFileHeader;
import net.lingala.zip4j.model.Zip64EndOfCentralDirectoryLocator;
import net.lingala.zip4j.model.Zip64EndOfCentralDirectoryRecord;
import net.lingala.zip4j.model.ZipModel;
import net.lingala.zip4j.util.BitUtils;
import net.lingala.zip4j.util.InternalZipConstants;
import net.lingala.zip4j.util.RawIO;
......@@ -34,7 +33,6 @@ import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import static net.lingala.zip4j.util.FileUtils.getZipFileNameWithoutExtension;
......@@ -43,13 +41,15 @@ import static net.lingala.zip4j.util.Zip4jUtil.isStringNotNullAndNotEmpty;
public class HeaderWriter {
private static final short ZIP64_EXTRA_DATA_RECORD_SIZE = 28;
private static final short ZIP64_EXTRA_DATA_RECORD_SIZE_LFH = 16;
private static final short ZIP64_EXTRA_DATA_RECORD_SIZE_FH = 28;
private static final short AES_EXTRA_DATA_RECORD_SIZE = 11;
private RawIO rawIO = new RawIO();
private byte[] longBuff = new byte[8];
public void writeLocalFileHeader(ZipModel zipModel, LocalFileHeader localFileHeader, OutputStream outputStream)
throws IOException {
public void writeLocalFileHeader(ZipModel zipModel, LocalFileHeader localFileHeader, OutputStream outputStream,
Charset charset) throws IOException {
try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
rawIO.writeIntLittleEndian(byteArrayOutputStream, (int) localFileHeader.getSignature().getValue());
......@@ -62,9 +62,11 @@ public class HeaderWriter {
rawIO.writeLongLittleEndian(longBuff, 0, localFileHeader.getCrc());
byteArrayOutputStream.write(longBuff, 0, 4);
long uncompressedSize = localFileHeader.getUncompressedSize();
if (uncompressedSize >= ZIP_64_SIZE_LIMIT) {
boolean writeZip64Header = localFileHeader.getCompressedSize() >= ZIP_64_SIZE_LIMIT
|| localFileHeader.getUncompressedSize() >= ZIP_64_SIZE_LIMIT;
if (writeZip64Header) {
rawIO.writeLongLittleEndian(longBuff, 0, ZIP_64_SIZE_LIMIT);
//Set the uncompressed size to ZipConstants.ZIP_64_SIZE_LIMIT as
......@@ -81,26 +83,21 @@ public class HeaderWriter {
rawIO.writeLongLittleEndian(longBuff, 0, localFileHeader.getUncompressedSize());
byteArrayOutputStream.write(longBuff, 0, 4);
zipModel.setZip64Format(false);
localFileHeader.setWriteCompressedSizeInZip64ExtraRecord(false);
}
byte[] fileNameBytes = new byte[0];
if (isStringNotNullAndNotEmpty(localFileHeader.getFileName())) {
Charset charset = Charset.forName(InternalZipConstants.ZIP_STANDARD_CHARSET);
if (BitUtils.isBitSet(localFileHeader.getGeneralPurposeFlag()[1], 3)) {
charset = StandardCharsets.UTF_8;
}
fileNameBytes = localFileHeader.getFileName().getBytes(charset);
}
rawIO.writeShortLittleEndian(byteArrayOutputStream, fileNameBytes.length);
int extraFieldLength = 0;
if (zipModel.isZip64Format()) {
extraFieldLength += 32;
extraFieldLength += ZIP64_EXTRA_DATA_RECORD_SIZE_LFH + 4; // 4 for signature + size of record
}
if (localFileHeader.getAesExtraDataRecord() != null) {
extraFieldLength += 11;
extraFieldLength += AES_EXTRA_DATA_RECORD_SIZE;
}
rawIO.writeShortLittleEndian(byteArrayOutputStream, extraFieldLength);
......@@ -112,14 +109,12 @@ public class HeaderWriter {
//This is NOT according to any specification but if this is changed
//corresponding logic for updateLocalFileHeader for compressed size
//has to be modified as well
if (zipModel.isZip64Format()) {
if (writeZip64Header) {
rawIO.writeShortLittleEndian(byteArrayOutputStream,
(int) HeaderSignature.ZIP64_EXTRA_FIELD_SIGNATURE.getValue());
rawIO.writeShortLittleEndian(byteArrayOutputStream, ZIP64_EXTRA_DATA_RECORD_SIZE);
rawIO.writeShortLittleEndian(byteArrayOutputStream, ZIP64_EXTRA_DATA_RECORD_SIZE_LFH);
rawIO.writeLongLittleEndian(byteArrayOutputStream, localFileHeader.getUncompressedSize());
rawIO.writeLongLittleEndian(byteArrayOutputStream, localFileHeader.getCompressedSize());
rawIO.writeLongLittleEndian(byteArrayOutputStream, 0); //Offset start of local file header
rawIO.writeIntLittleEndian(byteArrayOutputStream, 0); //Disk number start
}
if (localFileHeader.getAesExtraDataRecord() != null) {
......@@ -168,7 +163,7 @@ public class HeaderWriter {
}
}
public void finalizeZipFile(ZipModel zipModel, OutputStream outputStream) throws IOException {
public void finalizeZipFile(ZipModel zipModel, OutputStream outputStream, Charset charset) throws IOException {
if (zipModel == null || outputStream == null) {
throw new ZipException("input parameters is null, cannot finalize zip file");
}
......@@ -176,7 +171,7 @@ public class HeaderWriter {
try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
processHeaderData(zipModel, outputStream);
long offsetCentralDir = zipModel.getEndOfCentralDirectoryRecord().getOffsetOfStartOfCentralDirectory();
writeCentralDirectory(zipModel, byteArrayOutputStream, rawIO);
writeCentralDirectory(zipModel, byteArrayOutputStream, rawIO, charset);
int sizeOfCentralDir = byteArrayOutputStream.size();
if (zipModel.isZip64Format() || offsetCentralDir >= InternalZipConstants.ZIP_64_SIZE_LIMIT
......@@ -208,11 +203,11 @@ public class HeaderWriter {
}
writeEndOfCentralDirectoryRecord(zipModel, sizeOfCentralDir, offsetCentralDir, byteArrayOutputStream, rawIO);
writeZipHeaderBytes(zipModel, outputStream, byteArrayOutputStream.toByteArray());
writeZipHeaderBytes(zipModel, outputStream, byteArrayOutputStream.toByteArray(), charset);
}
}
public void finalizeZipFileWithoutValidations(ZipModel zipModel, OutputStream outputStream) throws IOException {
public void finalizeZipFileWithoutValidations(ZipModel zipModel, OutputStream outputStream, Charset charset) throws IOException {
if (zipModel == null || outputStream == null) {
throw new ZipException("input parameters is null, cannot finalize zip file without validations");
......@@ -220,7 +215,7 @@ public class HeaderWriter {
try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
long offsetCentralDir = zipModel.getEndOfCentralDirectoryRecord().getOffsetOfStartOfCentralDirectory();
writeCentralDirectory(zipModel, byteArrayOutputStream, rawIO);
writeCentralDirectory(zipModel, byteArrayOutputStream, rawIO, charset);
int sizeOfCentralDir = byteArrayOutputStream.size();
if (zipModel.isZip64Format() || offsetCentralDir >= InternalZipConstants.ZIP_64_SIZE_LIMIT
......@@ -242,7 +237,7 @@ public class HeaderWriter {
}
writeEndOfCentralDirectoryRecord(zipModel, sizeOfCentralDir, offsetCentralDir, byteArrayOutputStream, rawIO);
writeZipHeaderBytes(zipModel, outputStream, byteArrayOutputStream.toByteArray());
writeZipHeaderBytes(zipModel, outputStream, byteArrayOutputStream.toByteArray(), charset);
}
}
......@@ -301,7 +296,7 @@ public class HeaderWriter {
//2 - size of zip64 data
//8 - uncompressed size
//8 - compressed size
int zip64CompressedSizeOffset = 2 + 2 + fileHeader.getFileNameLength() + 2 + 2 + 2;
int zip64CompressedSizeOffset = 2 + 2 + fileHeader.getFileNameLength() + 2 + 2;
if (outputStream.skipBytes(zip64CompressedSizeOffset) != zip64CompressedSizeOffset) {
throw new ZipException("Unable to skip " + zip64CompressedSizeOffset + " bytes to update LFH");
}
......@@ -333,7 +328,7 @@ public class HeaderWriter {
return ((CountingOutputStream) outputStream).getCurrentSplitFileCounter();
}
private void writeZipHeaderBytes(ZipModel zipModel, OutputStream outputStream, byte[] buff) throws IOException {
private void writeZipHeaderBytes(ZipModel zipModel, OutputStream outputStream, byte[] buff, Charset charset) throws IOException {
if (buff == null) {
throw new ZipException("invalid buff to write as zip headers");
}
......@@ -341,7 +336,7 @@ public class HeaderWriter {
if (outputStream instanceof CountingOutputStream) {
if (((CountingOutputStream) outputStream).checkBuffSizeAndStartNextSplitFile(buff.length)) {
//TODO check if this is correct
finalizeZipFile(zipModel, outputStream);
finalizeZipFile(zipModel, outputStream, charset);
return;
}
}
......@@ -373,7 +368,7 @@ public class HeaderWriter {
zipModel.getEndOfCentralDirectoryRecord().setNumberOfThisDiskStartOfCentralDir(currentSplitFileCounter);
}
private void writeCentralDirectory(ZipModel zipModel, ByteArrayOutputStream byteArrayOutputStream, RawIO rawIO)
private void writeCentralDirectory(ZipModel zipModel, ByteArrayOutputStream byteArrayOutputStream, RawIO rawIO, Charset charset)
throws ZipException {
if (zipModel.getCentralDirectory() == null || zipModel.getCentralDirectory().getFileHeaders() == null
......@@ -382,12 +377,12 @@ public class HeaderWriter {
}
for (FileHeader fileHeader: zipModel.getCentralDirectory().getFileHeaders()) {
writeFileHeader(zipModel, fileHeader, byteArrayOutputStream, rawIO);
writeFileHeader(zipModel, fileHeader, byteArrayOutputStream, rawIO, charset);
}
}
private void writeFileHeader(ZipModel zipModel, FileHeader fileHeader, ByteArrayOutputStream byteArrayOutputStream,
RawIO rawIO) throws ZipException {
RawIO rawIO, Charset charset) throws ZipException {
if (fileHeader == null) {
throw new ZipException("input parameters is null, cannot write local file header");
}
......@@ -396,6 +391,10 @@ public class HeaderWriter {
final byte[] emptyShortByte = {0, 0};
boolean writeZip64ExtendedInfo = false;
if (isZip64Entry(fileHeader)) {
writeZip64ExtendedInfo = true;
}
rawIO.writeIntLittleEndian(byteArrayOutputStream, (int) fileHeader.getSignature().getValue());
rawIO.writeShortLittleEndian(byteArrayOutputStream, fileHeader.getVersionMadeBy());
rawIO.writeShortLittleEndian(byteArrayOutputStream, fileHeader.getVersionNeededToExtract());
......@@ -408,11 +407,11 @@ public class HeaderWriter {
rawIO.writeLongLittleEndian(longBuff, 0, fileHeader.getCrc());
byteArrayOutputStream.write(longBuff, 0, 4);
if (fileHeader.getCompressedSize() >= ZIP_64_SIZE_LIMIT || fileHeader.getUncompressedSize() >= ZIP_64_SIZE_LIMIT) {
if (writeZip64ExtendedInfo) {
rawIO.writeLongLittleEndian(longBuff, 0, ZIP_64_SIZE_LIMIT);
byteArrayOutputStream.write(longBuff, 0, 4);
byteArrayOutputStream.write(longBuff, 0, 4);
writeZip64ExtendedInfo = true;
zipModel.setZip64Format(true);
} else {
rawIO.writeLongLittleEndian(longBuff, 0, fileHeader.getCompressedSize());
byteArrayOutputStream.write(longBuff, 0, 4);
......@@ -422,10 +421,6 @@ public class HeaderWriter {
byte[] fileNameBytes = new byte[0];
if (isStringNotNullAndNotEmpty(fileHeader.getFileName())) {
Charset charset = Charset.forName(InternalZipConstants.ZIP_STANDARD_CHARSET);
if (BitUtils.isBitSet(fileHeader.getGeneralPurposeFlag()[1], 3)) {
charset = StandardCharsets.UTF_8;
}
fileNameBytes = fileHeader.getFileName().getBytes(charset);
}
rawIO.writeShortLittleEndian(byteArrayOutputStream, fileNameBytes.length);
......@@ -433,10 +428,9 @@ public class HeaderWriter {
//Compute offset bytes before extra field is written for Zip64 compatibility
//NOTE: this data is not written now, but written at a later point
byte[] offsetLocalHeaderBytes = new byte[4];
if (fileHeader.getOffsetLocalHeader() > ZIP_64_SIZE_LIMIT) {
if (writeZip64ExtendedInfo) {
rawIO.writeLongLittleEndian(longBuff, 0, ZIP_64_SIZE_LIMIT);
System.arraycopy(longBuff, 0, offsetLocalHeaderBytes, 0, 4);
writeZip64ExtendedInfo = true;
} else {
rawIO.writeLongLittleEndian(longBuff, 0, fileHeader.getOffsetLocalHeader());
System.arraycopy(longBuff, 0, offsetLocalHeaderBytes, 0, 4);
......@@ -444,20 +438,16 @@ public class HeaderWriter {
int extraFieldLength = 0;
if (writeZip64ExtendedInfo) {
extraFieldLength += 32;
extraFieldLength += ZIP64_EXTRA_DATA_RECORD_SIZE_FH + 4; // 4 for signature + size of record
}
if (fileHeader.getAesExtraDataRecord() != null) {
extraFieldLength += 11;
extraFieldLength += AES_EXTRA_DATA_RECORD_SIZE;
}
rawIO.writeShortLittleEndian(byteArrayOutputStream, extraFieldLength);
String fileComment = fileHeader.getFileComment();
byte[] fileCommentBytes = new byte[0];
if (isStringNotNullAndNotEmpty(fileComment)) {
Charset charset = Charset.forName(InternalZipConstants.ZIP_STANDARD_CHARSET);
if (BitUtils.isBitSet(fileHeader.getGeneralPurposeFlag()[1], 3)) {
charset = StandardCharsets.UTF_8;
}
fileCommentBytes = fileComment.getBytes(charset);
}
rawIO.writeShortLittleEndian(byteArrayOutputStream, fileCommentBytes.length);
......@@ -486,7 +476,7 @@ public class HeaderWriter {
(int) HeaderSignature.ZIP64_EXTRA_FIELD_SIGNATURE.getValue());
//size of data
rawIO.writeShortLittleEndian(byteArrayOutputStream, ZIP64_EXTRA_DATA_RECORD_SIZE);
rawIO.writeShortLittleEndian(byteArrayOutputStream, ZIP64_EXTRA_DATA_RECORD_SIZE_FH);
rawIO.writeLongLittleEndian(byteArrayOutputStream, fileHeader.getUncompressedSize());
rawIO.writeLongLittleEndian(byteArrayOutputStream, fileHeader.getCompressedSize());
rawIO.writeLongLittleEndian(byteArrayOutputStream, fileHeader.getOffsetLocalHeader());
......@@ -607,7 +597,7 @@ public class HeaderWriter {
String comment = zipModel.getEndOfCentralDirectoryRecord().getComment();
if (isStringNotNullAndNotEmpty(comment)) {
byte[] commentBytes = comment.getBytes(StandardCharsets.UTF_8);
byte[] commentBytes = comment.getBytes(InternalZipConstants.CHARSET_UTF_8);
rawIO.writeShortLittleEndian(byteArrayOutputStream, commentBytes.length);
byteArrayOutputStream.write(commentBytes);
} else {
......@@ -629,4 +619,9 @@ public class HeaderWriter {
return noEntries;
}
private boolean isZip64Entry(FileHeader fileHeader) {
return fileHeader.getCompressedSize() >= ZIP_64_SIZE_LIMIT
|| fileHeader.getUncompressedSize() >= ZIP_64_SIZE_LIMIT
|| fileHeader.getOffsetLocalHeader() >= ZIP_64_SIZE_LIMIT;
}
}
\ No newline at end of file
......@@ -32,6 +32,7 @@ import net.lingala.zip4j.util.InternalZipConstants;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.nio.charset.Charset;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.DataFormatException;
......@@ -50,14 +51,28 @@ public class ZipInputStream extends InputStream {
private byte[] endOfEntryBuffer;
private boolean extraDataRecordReadForThisEntry = false;
private boolean canSkipExtendedLocalFileHeader = false;
private Charset charset;
public ZipInputStream(InputStream inputStream) {
this(inputStream, null);
this(inputStream, null, InternalZipConstants.CHARSET_UTF_8);
}
public ZipInputStream(InputStream inputStream, Charset charset) {
this(inputStream, null, charset);
}
public ZipInputStream(InputStream inputStream, char[] password) {
this(inputStream, password, InternalZipConstants.CHARSET_UTF_8);
}
public ZipInputStream(InputStream inputStream, char[] password, Charset charset) {
if(charset == null) {
charset = InternalZipConstants.CHARSET_UTF_8;
}
this.inputStream = new PushbackInputStream(inputStream, 512);
this.password = password;
this.charset = charset;
}
public LocalFileHeader getNextEntry() throws IOException {
......@@ -69,7 +84,7 @@ public class ZipInputStream extends InputStream {
readUntilEndOfEntry();
}
localFileHeader = headerReader.readLocalFileHeader(inputStream);
localFileHeader = headerReader.readLocalFileHeader(inputStream, charset);
if (localFileHeader == null) {
return null;
......@@ -251,7 +266,7 @@ public class ZipInputStream extends InputStream {
}
if (localFileHeader.getCrc() != crc32.getValue()) {
ZipException.Type exceptionType = ZipException.Type.UNKNOWN;
ZipException.Type exceptionType = ZipException.Type.CHECKSUM_MISMATCH;
if (isEncryptionMethodZipStandard(localFileHeader)) {
exceptionType = ZipException.Type.WRONG_PASSWORD;
......@@ -313,5 +328,4 @@ public class ZipInputStream extends InputStream {
private boolean isEncryptionMethodZipStandard(LocalFileHeader localFileHeader) {
return localFileHeader.isEncrypted() && EncryptionMethod.ZIP_STANDARD.equals(localFileHeader.getEncryptionMethod());
}
}
......@@ -11,10 +11,12 @@ import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.AesVersion;
import net.lingala.zip4j.model.enums.CompressionMethod;
import net.lingala.zip4j.model.enums.EncryptionMethod;
import net.lingala.zip4j.util.InternalZipConstants;
import net.lingala.zip4j.util.RawIO;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.zip.CRC32;
public class ZipOutputStream extends OutputStream {
......@@ -30,18 +32,32 @@ public class ZipOutputStream extends OutputStream {
private CRC32 crc32 = new CRC32();
private RawIO rawIO = new RawIO();
private long uncompressedSizeForThisEntry = 0;
private Charset charset;
public ZipOutputStream(OutputStream outputStream) throws IOException {
this(outputStream, null);
this(outputStream, null, InternalZipConstants.CHARSET_UTF_8);
}
public ZipOutputStream(OutputStream outputStream, Charset charset) throws IOException {
this(outputStream, null, charset);
}
public ZipOutputStream(OutputStream outputStream, char[] password) throws IOException {
this(outputStream, password, new ZipModel());
this(outputStream, password, InternalZipConstants.CHARSET_UTF_8);
}
public ZipOutputStream(OutputStream outputStream, char[] password, Charset charset) throws IOException {
this(outputStream, password, charset, new ZipModel());
}
public ZipOutputStream(OutputStream outputStream, char[] password, Charset charset, ZipModel zipModel) throws IOException {
if(charset == null) {
charset = InternalZipConstants.CHARSET_UTF_8;
}
public ZipOutputStream(OutputStream outputStream, char[] password, ZipModel zipModel) throws IOException {
this.countingOutputStream = new CountingOutputStream(outputStream);
this.password = password;
this.charset = charset;
this.zipModel = initializeZipModel(zipModel, countingOutputStream);
writeSplitZipHeaderIfApplicable();
}
......@@ -98,7 +114,7 @@ public class ZipOutputStream extends OutputStream {
@Override
public void close() throws IOException {
zipModel.getEndOfCentralDirectoryRecord().setOffsetOfStartOfCentralDirectory(countingOutputStream.getNumberOfBytesWritten());
headerWriter.finalizeZipFile(zipModel, countingOutputStream);
headerWriter.finalizeZipFile(zipModel, countingOutputStream, charset);
countingOutputStream.close();
}
......@@ -117,11 +133,11 @@ public class ZipOutputStream extends OutputStream {
private void initializeAndWriteFileHeader(ZipParameters zipParameters) throws IOException {
fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, countingOutputStream.isSplitZipFile(),
countingOutputStream.getCurrentSplitFileCounter());
countingOutputStream.getCurrentSplitFileCounter(), charset);
fileHeader.setOffsetLocalHeader(countingOutputStream.getOffsetForNextEntry());
localFileHeader = fileHeaderFactory.generateLocalFileHeader(fileHeader);
headerWriter.writeLocalFileHeader(zipModel, localFileHeader, countingOutputStream);
headerWriter.writeLocalFileHeader(zipModel, localFileHeader, countingOutputStream, charset);
}
private void reset() throws IOException {
......
......@@ -33,7 +33,7 @@ public class ZipModel implements Cloneable {
private boolean splitArchive;
private long splitLength;
private File zipFile;
private boolean isZip64Format;
private boolean isZip64Format = false;
private boolean isNestedZipFile;
private long start;
private long end;
......
......@@ -12,11 +12,13 @@ import net.lingala.zip4j.model.enums.EncryptionMethod;
import net.lingala.zip4j.progress.ProgressMonitor;
import net.lingala.zip4j.util.FileUtils;
import net.lingala.zip4j.util.Zip4jUtil;
import net.lingala.zip4j.tasks.RemoveEntryFromZipFileTask.RemoveEntryFromZipFileTaskParameters;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
......@@ -47,13 +49,13 @@ public abstract class AbstractAddFileToZipTask<T> extends AsyncZipTask<T> {
this.headerWriter = headerWriter;
}
void addFilesToZip(List<File> filesToAdd, ProgressMonitor progressMonitor, ZipParameters zipParameters)
void addFilesToZip(List<File> filesToAdd, ProgressMonitor progressMonitor, ZipParameters zipParameters, Charset charset)
throws IOException {
List<File> updatedFilesToAdd = removeFilesIfExists(filesToAdd, zipParameters, progressMonitor);
List<File> updatedFilesToAdd = removeFilesIfExists(filesToAdd, zipParameters, progressMonitor, charset);
try (SplitOutputStream splitOutputStream = new SplitOutputStream(zipModel.getZipFile(), zipModel.getSplitLength());
ZipOutputStream zipOutputStream = initializeOutputStream(splitOutputStream)) {
ZipOutputStream zipOutputStream = initializeOutputStream(splitOutputStream, charset)) {
byte[] readBuff = new byte[BUFF_SIZE];
int readLen = -1;
......@@ -110,7 +112,7 @@ public abstract class AbstractAddFileToZipTask<T> extends AsyncZipTask<T> {
return totalWork;
}
ZipOutputStream initializeOutputStream(SplitOutputStream splitOutputStream) throws IOException {
ZipOutputStream initializeOutputStream(SplitOutputStream splitOutputStream, Charset charset) throws IOException {
if (zipModel.getZipFile().exists()) {
if (zipModel.getEndOfCentralDirectoryRecord() == null) {
throw new ZipException("invalid end of central directory record");
......@@ -118,7 +120,7 @@ public abstract class AbstractAddFileToZipTask<T> extends AsyncZipTask<T> {
splitOutputStream.seek(zipModel.getEndOfCentralDirectoryRecord().getOffsetOfStartOfCentralDirectory());
}
return new ZipOutputStream(splitOutputStream, password, zipModel);
return new ZipOutputStream(splitOutputStream, password, charset, zipModel);
}
void verifyZipParameters(ZipParameters parameters) throws ZipException {
......@@ -185,7 +187,7 @@ public abstract class AbstractAddFileToZipTask<T> extends AsyncZipTask<T> {
return clonedZipParameters;
}
private List<File> removeFilesIfExists(List<File> files, ZipParameters zipParameters, ProgressMonitor progressMonitor)
private List<File> removeFilesIfExists(List<File> files, ZipParameters zipParameters, ProgressMonitor progressMonitor, Charset charset)
throws ZipException {
List<File> filesToAdd = new ArrayList<>(files);
......@@ -200,7 +202,7 @@ public abstract class AbstractAddFileToZipTask<T> extends AsyncZipTask<T> {
if (fileHeader != null) {
if (zipParameters.isOverrideExistingFilesInZip()) {
progressMonitor.setCurrentTask(REMOVE_ENTRY);
removeFile(fileHeader, progressMonitor);
removeFile(fileHeader, progressMonitor, charset);
verifyIfTaskIsCancelled();
progressMonitor.setCurrentTask(ADD_ENTRY);
} else {
......@@ -212,10 +214,10 @@ public abstract class AbstractAddFileToZipTask<T> extends AsyncZipTask<T> {
return filesToAdd;
}
private void removeFile(FileHeader fileHeader, ProgressMonitor progressMonitor) throws ZipException {
private void removeFile(FileHeader fileHeader, ProgressMonitor progressMonitor, Charset charset) throws ZipException {
RemoveEntryFromZipFileTask removeEntryFromZipFileTask = new RemoveEntryFromZipFileTask(progressMonitor, false,
zipModel);
removeEntryFromZipFileTask.execute(fileHeader);
removeEntryFromZipFileTask.execute(new RemoveEntryFromZipFileTaskParameters(fileHeader, charset));
}
@Override
......
package net.lingala.zip4j.tasks;
import java.nio.charset.Charset;
public abstract class AbstractZipTaskParameters {
protected Charset charset;
protected AbstractZipTaskParameters(Charset charset) {
this.charset = charset;
}
}
......@@ -9,6 +9,7 @@ import net.lingala.zip4j.tasks.AddFilesToZipTask.AddFilesToZipTaskParameters;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
public class AddFilesToZipTask extends AbstractAddFileToZipTask<AddFilesToZipTaskParameters> {
......@@ -23,7 +24,7 @@ public class AddFilesToZipTask extends AbstractAddFileToZipTask<AddFilesToZipTas
throws IOException {
verifyZipParameters(taskParameters.zipParameters);
addFilesToZip(taskParameters.filesToAdd, progressMonitor, taskParameters.zipParameters);
addFilesToZip(taskParameters.filesToAdd, progressMonitor, taskParameters.zipParameters, taskParameters.charset);
}
@Override
......@@ -36,11 +37,12 @@ public class AddFilesToZipTask extends AbstractAddFileToZipTask<AddFilesToZipTas
return super.getTask();
}
public static class AddFilesToZipTaskParameters {
public static class AddFilesToZipTaskParameters extends AbstractZipTaskParameters {
private List<File> filesToAdd;
private ZipParameters zipParameters;
public AddFilesToZipTaskParameters(List<File> filesToAdd, ZipParameters zipParameters) {
public AddFilesToZipTaskParameters(List<File> filesToAdd, ZipParameters zipParameters, Charset charset) {
super(charset);
this.filesToAdd = filesToAdd;
this.zipParameters = zipParameters;
}
......
......@@ -9,6 +9,7 @@ import net.lingala.zip4j.tasks.AddFolderToZipTask.AddFolderToZipTaskParameters;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import static net.lingala.zip4j.util.FileUtils.getFilesInDirectoryRecursive;
......@@ -25,7 +26,7 @@ public class AddFolderToZipTask extends AbstractAddFileToZipTask<AddFolderToZipT
throws IOException {
List<File> filesToAdd = getFilesToAdd(taskParameters);
setDefaultFolderPath(taskParameters);
addFilesToZip(filesToAdd, progressMonitor, taskParameters.zipParameters);
addFilesToZip(filesToAdd, progressMonitor, taskParameters.zipParameters, taskParameters.charset);
}
@Override
......@@ -65,11 +66,12 @@ public class AddFolderToZipTask extends AbstractAddFileToZipTask<AddFolderToZipT
return filesToAdd;
}
public static class AddFolderToZipTaskParameters {
public static class AddFolderToZipTaskParameters extends AbstractZipTaskParameters {
private File folderToAdd;
private ZipParameters zipParameters;
public AddFolderToZipTaskParameters(File folderToAdd, ZipParameters zipParameters) {
public AddFolderToZipTaskParameters(File folderToAdd, ZipParameters zipParameters, Charset charset) {
super(charset);
this.folderToAdd = folderToAdd;
this.zipParameters = zipParameters;
}
......
package net.lingala.zip4j.tasks;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.headers.HeaderUtil;
import net.lingala.zip4j.headers.HeaderWriter;
import net.lingala.zip4j.io.outputstream.SplitOutputStream;
import net.lingala.zip4j.io.outputstream.ZipOutputStream;
......@@ -9,9 +11,12 @@ import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.CompressionMethod;
import net.lingala.zip4j.progress.ProgressMonitor;
import net.lingala.zip4j.tasks.AddStreamToZipTask.AddStreamToZipTaskParameters;
import net.lingala.zip4j.tasks.RemoveEntryFromZipFileTask.RemoveEntryFromZipFileTaskParameters;
import net.lingala.zip4j.util.Zip4jUtil;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import static net.lingala.zip4j.util.InternalZipConstants.BUFF_SIZE;
......@@ -28,6 +33,12 @@ public class AddStreamToZipTask extends AbstractAddFileToZipTask<AddStreamToZipT
verifyZipParameters(taskParameters.zipParameters);
if (!Zip4jUtil.isStringNotNullAndNotEmpty(taskParameters.zipParameters.getFileNameInZip())) {
throw new ZipException("fileNameInZip has to be set in zipParameters when adding stream");
}
removeFileIfExists(getZipModel(), taskParameters.charset, taskParameters.zipParameters.getFileNameInZip(), progressMonitor);
// For streams, it is necessary to write extended local file header because of Zip standard encryption.
// If we do not write extended local file header, zip standard encryption needs a crc upfront for key,
// which cannot be calculated until we read the complete stream. If we use extended local file header,
......@@ -40,7 +51,7 @@ public class AddStreamToZipTask extends AbstractAddFileToZipTask<AddStreamToZipT
}
try(SplitOutputStream splitOutputStream = new SplitOutputStream(getZipModel().getZipFile(), getZipModel().getSplitLength());
ZipOutputStream zipOutputStream = initializeOutputStream(splitOutputStream)) {
ZipOutputStream zipOutputStream = initializeOutputStream(splitOutputStream, taskParameters.charset)) {
byte[] readBuff = new byte[BUFF_SIZE];
int readLen = -1;
......@@ -68,11 +79,23 @@ public class AddStreamToZipTask extends AbstractAddFileToZipTask<AddStreamToZipT
return 0;
}
public static class AddStreamToZipTaskParameters {
private void removeFileIfExists(ZipModel zipModel, Charset charset, String fileNameInZip, ProgressMonitor progressMonitor)
throws ZipException {
FileHeader fileHeader = HeaderUtil.getFileHeader(zipModel, fileNameInZip);
if (fileHeader != null) {
RemoveEntryFromZipFileTask removeEntryFromZipFileTask = new RemoveEntryFromZipFileTask(progressMonitor, false,
zipModel);
removeEntryFromZipFileTask.execute(new RemoveEntryFromZipFileTaskParameters(fileHeader, charset));
}
}
public static class AddStreamToZipTaskParameters extends AbstractZipTaskParameters {
private InputStream inputStream;
private ZipParameters zipParameters;
public AddStreamToZipTaskParameters(InputStream inputStream, ZipParameters zipParameters) {
public AddStreamToZipTaskParameters(InputStream inputStream, ZipParameters zipParameters, Charset charset) {
super(charset);
this.inputStream = inputStream;
this.zipParameters = zipParameters;
}
......
......@@ -8,6 +8,7 @@ import net.lingala.zip4j.progress.ProgressMonitor;
import net.lingala.zip4j.tasks.ExtractAllFilesTask.ExtractAllFilesTaskParameters;
import java.io.IOException;
import java.nio.charset.Charset;
public class ExtractAllFilesTask extends AbstractExtractFileTask<ExtractAllFilesTaskParameters> {
......@@ -22,7 +23,7 @@ public class ExtractAllFilesTask extends AbstractExtractFileTask<ExtractAllFiles
@Override
protected void executeTask(ExtractAllFilesTaskParameters taskParameters, ProgressMonitor progressMonitor)
throws IOException {
try (ZipInputStream zipInputStream = prepareZipInputStream()) {
try (ZipInputStream zipInputStream = prepareZipInputStream(taskParameters.charset)) {
for (FileHeader fileHeader : getZipModel().getCentralDirectory().getFileHeaders()) {
if (fileHeader.getFileName().startsWith("__MACOSX")) {
progressMonitor.updateWorkCompleted(fileHeader.getUncompressedSize());
......@@ -58,7 +59,7 @@ public class ExtractAllFilesTask extends AbstractExtractFileTask<ExtractAllFiles
return totalWork;
}
private ZipInputStream prepareZipInputStream() throws IOException {
private ZipInputStream prepareZipInputStream(Charset charset) throws IOException {
splitInputStream = new SplitInputStream(getZipModel().getZipFile(),
getZipModel().isSplitArchive(), getZipModel().getEndOfCentralDirectoryRecord().getNumberOfThisDisk());
......@@ -67,7 +68,7 @@ public class ExtractAllFilesTask extends AbstractExtractFileTask<ExtractAllFiles
splitInputStream.prepareExtractionForFileHeader(fileHeader);
}
return new ZipInputStream(splitInputStream, password);
return new ZipInputStream(splitInputStream, password, charset);
}
private FileHeader getFirstFileHeader(ZipModel zipModel) {
......@@ -80,10 +81,11 @@ public class ExtractAllFilesTask extends AbstractExtractFileTask<ExtractAllFiles
return zipModel.getCentralDirectory().getFileHeaders().get(0);
}
public static class ExtractAllFilesTaskParameters {
public static class ExtractAllFilesTaskParameters extends AbstractZipTaskParameters {
private String outputPath;
public ExtractAllFilesTaskParameters(String outputPath) {
public ExtractAllFilesTaskParameters(String outputPath, Charset charset) {
super(charset);
this.outputPath = outputPath;
}
}
......
......@@ -8,6 +8,7 @@ import net.lingala.zip4j.progress.ProgressMonitor;
import net.lingala.zip4j.tasks.ExtractFileTask.ExtractFileTaskParameters;
import java.io.IOException;
import java.nio.charset.Charset;
public class ExtractFileTask extends AbstractExtractFileTask<ExtractFileTaskParameters> {
......@@ -22,7 +23,7 @@ public class ExtractFileTask extends AbstractExtractFileTask<ExtractFileTaskPara
@Override
protected void executeTask(ExtractFileTaskParameters taskParameters, ProgressMonitor progressMonitor)
throws IOException {
try(ZipInputStream zipInputStream = createZipInputStream(taskParameters.fileHeader)) {
try(ZipInputStream zipInputStream = createZipInputStream(taskParameters.fileHeader, taskParameters.charset)) {
extractFile(zipInputStream, taskParameters.fileHeader, taskParameters.outputPath, taskParameters.newFileName,
progressMonitor);
} finally {
......@@ -37,19 +38,20 @@ public class ExtractFileTask extends AbstractExtractFileTask<ExtractFileTaskPara
return taskParameters.fileHeader.getUncompressedSize();
}
protected ZipInputStream createZipInputStream(FileHeader fileHeader) throws IOException {
protected ZipInputStream createZipInputStream(FileHeader fileHeader, Charset charset) throws IOException {
splitInputStream = new SplitInputStream(getZipModel().getZipFile(),
getZipModel().isSplitArchive(), getZipModel().getEndOfCentralDirectoryRecord().getNumberOfThisDisk());
splitInputStream.prepareExtractionForFileHeader(fileHeader);
return new ZipInputStream(splitInputStream, password);
return new ZipInputStream(splitInputStream, password, charset);
}
public static class ExtractFileTaskParameters {
public static class ExtractFileTaskParameters extends AbstractZipTaskParameters {
private String outputPath;
private FileHeader fileHeader;
private String newFileName;
public ExtractFileTaskParameters(String outputPath, FileHeader fileHeader, String newFileName) {
public ExtractFileTaskParameters(String outputPath, FileHeader fileHeader, String newFileName, Charset charset) {
super(charset);
this.outputPath = outputPath;
this.fileHeader = fileHeader;
this.newFileName = newFileName;
......