Skip to content
Commits on Source (7)
[![Build Status](https://travis-ci.org/srikanth-lingala/zip4j.svg?branch=master)](https://travis-ci.org/srikanth-lingala/zip4j)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/net.lingala.zip4j/zip4j/badge.svg)](https://maven-badges.herokuapp.com/maven-central/net.lingala.zip4j/zip4j)
[![Known Vulnerabilities](https://snyk.io/test/github/dwyl/hapi-auth-jwt2/badge.svg?targetFile=package.json)](https://snyk.io/test/github/dwyl/hapi-auth-jwt2?targetFile=package.json)
[![Known Vulnerabilities](https://snyk.io//test/github/srikanth-lingala/zip4j/badge.svg?targetFile=pom.xml)](https://snyk.io//test/github/srikanth-lingala/zip4j?targetFile=pom.xml)
......@@ -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.2.2</version>
<version>2.2.6</version>
</dependency>
~~~~
......
zip4j (2.2.6-1) unstable; urgency=medium
* New upstream version 2.2.6
-- Andrius Merkys <merkys@debian.org> Mon, 25 Nov 2019 02:47:57 -0500
zip4j (2.2.2-1) unstable; urgency=medium
* New upstream version 2.2.2
......
......@@ -7,11 +7,12 @@ Uploaders:
Build-Depends:
debhelper (>= 12),
default-jdk,
maven-debian-helper (>= 2.1),
junit4,
libpowermock-java,
libassertj-core-java,
libmaven-bundle-plugin-java,
libmockito-java,
libpowermock-java,
maven-debian-helper (>= 2.1),
Standards-Version: 4.3.0
Vcs-Git: https://salsa.debian.org/java-team/zip4j.git
Vcs-Browser: https://salsa.debian.org/java-team/zip4j
......
......@@ -125,7 +125,7 @@ Author: Andrius Merkys <merkys@debian.org>
\ No newline at end of file
--- a/src/test/java/net/lingala/zip4j/headers/FileHeaderFactoryTest.java
+++ /dev/null
@@ -1,375 +0,0 @@
@@ -1,380 +0,0 @@
-package net.lingala.zip4j.headers;
-
-import net.lingala.zip4j.exception.ZipException;
......@@ -180,7 +180,7 @@ Author: Andrius Merkys <merkys@debian.org>
- assertThat(fileHeader.getAesExtraDataRecord()).isNull();
- assertThat(fileHeader.getLastModifiedTime()).isNotZero();
- assertThat(fileHeader.getCompressedSize()).isEqualTo(0);
- assertThat(fileHeader.getUncompressedSize()).isEqualTo(-1);
- assertThat(fileHeader.getUncompressedSize()).isEqualTo(0);
- }
-
- @Test
......@@ -409,7 +409,12 @@ Author: Andrius Merkys <merkys@debian.org>
- verifyLastModifiedFileTime(fileHeader, zipParameters);
- assertThat(fileHeader.getExternalFileAttributes()).isEqualTo(new byte[4]);
- assertThat(fileHeader.isDirectory()).isEqualTo(false);
- assertThat(fileHeader.getUncompressedSize()).isEqualTo(zipParameters.getEntrySize());
-
- if (zipParameters.isWriteExtendedLocalFileHeader()) {
- assertThat(fileHeader.getUncompressedSize()).isEqualTo(0);
- } else {
- assertThat(fileHeader.getUncompressedSize()).isEqualTo(zipParameters.getEntrySize());
- }
- verifyCrc(fileHeader);
- assertThat(fileHeader.isDataDescriptorExists()).isEqualTo(zipParameters.isWriteExtendedLocalFileHeader());
- assertThat(fileHeader.getAesExtraDataRecord() != null).isEqualTo(aesExtraDataRecordPresent);
......@@ -637,7 +642,7 @@ Author: Andrius Merkys <merkys@debian.org>
\ No newline at end of file
--- a/src/test/java/net/lingala/zip4j/util/FileUtilsTest.java
+++ /dev/null
@@ -1,317 +0,0 @@
@@ -1,336 +0,0 @@
-package net.lingala.zip4j.util;
-
-import net.lingala.zip4j.exception.ZipException;
......@@ -888,7 +893,26 @@ Author: Andrius Merkys <merkys@debian.org>
-
- @Test
- public void testGetRelativeFileNameWhenRootFoldersAreNull() throws ZipException {
- assertThat(FileUtils.getRelativeFileName("somefile.txt", null)).isEqualTo("somefile.txt");
- assertThat(FileUtils.getRelativeFileName("somefile.txt", null, null)).isEqualTo("somefile.txt");
- }
-
- @Test
- public void testGetRelativeFileWithRootFolderNameInZip() throws ZipException {
- String expectedRootFolder = "rootfolder" + InternalZipConstants.FILE_SEPARATOR + "somefile.txt";
- assertThat(FileUtils.getRelativeFileName("somefile.txt", null, "rootfolder")).isEqualTo(expectedRootFolder);
- }
-
- @Test
- public void testGetRelativeFileWithRootFolderNameInZipWithFileSeparator() throws ZipException {
- String expectedRootFolder = "rootfolder" + InternalZipConstants.FILE_SEPARATOR + "somefile.txt";
- assertThat(FileUtils.getRelativeFileName("somefile.txt", null, "rootfolder" + File.separator))
- .isEqualTo(expectedRootFolder);
- }
-
- @Test
- public void testGetRelativeFileWithRootFolderNameInZipWithSeparatorsInName() throws ZipException {
- String expectedRootFolder = "rootfolder" + InternalZipConstants.FILE_SEPARATOR + "somefile.txt";
- assertThat(FileUtils.getRelativeFileName("somefile.txt", null, "rootfolder\\")).isEqualTo(expectedRootFolder);
- }
-
- @Test
......@@ -1130,7 +1154,7 @@ Author: Andrius Merkys <merkys@debian.org>
\ No newline at end of file
--- a/src/test/java/net/lingala/zip4j/AddFilesToZipIT.java
+++ /dev/null
@@ -1,944 +0,0 @@
@@ -1,978 +0,0 @@
-package net.lingala.zip4j;
-
-import net.lingala.zip4j.exception.ZipException;
......@@ -1145,6 +1169,7 @@ Author: Andrius Merkys <merkys@debian.org>
-import net.lingala.zip4j.model.enums.CompressionMethod;
-import net.lingala.zip4j.model.enums.EncryptionMethod;
-import net.lingala.zip4j.progress.ProgressMonitor;
-import net.lingala.zip4j.testutils.HeaderVerifier;
-import net.lingala.zip4j.testutils.TestUtils;
-import net.lingala.zip4j.testutils.ZipFileVerifier;
-import net.lingala.zip4j.util.BitUtils;
......@@ -1171,6 +1196,8 @@ Author: Andrius Merkys <merkys@debian.org>
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private HeaderVerifier headerVerifier = new HeaderVerifier();
-
- @Test
- public void testAddFileAsStringParameterThrowsExceptionWhenFileDoesNotExist() throws ZipException {
- expectedException.expectMessage("File does not exist: somefile.txt");
......@@ -1671,6 +1698,36 @@ Author: Andrius Merkys <merkys@debian.org>
- }
-
- @Test
- public void testAddFolderWithRootFolderNameInZipAndWithoutRootFolder() throws IOException {
- ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256);
- zipParameters.setIncludeRootFolder(false);
- zipParameters.setRootFolderNameInZip("root_folder_name");
- ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD);
-
- zipFile.addFolder(TestUtils.getTestFileFromResources(""), zipParameters);
-
- List<FileHeader> fileHeaders = getFileHeaders(generatedZipFile);
- verifyAllFilesInZipContainsPath(fileHeaders, "root_folder_name/");
- verifyAllFilesInZipDoesNotContainPath(fileHeaders, "root_folder_name/test-files/");
- verifyFoldersInZip(fileHeaders, generatedZipFile, PASSWORD);
- }
-
- @Test
- public void testAddFolderWithRootFolderNameInZipAndWithRootFolder() throws IOException {
- ZipParameters zipParameters = createZipParameters(EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256);
- zipParameters.setIncludeRootFolder(true);
- zipParameters.setRootFolderNameInZip("root_folder_name");
- ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD);
-
- zipFile.addFolder(TestUtils.getTestFileFromResources(""), zipParameters);
-
- List<FileHeader> fileHeaders = getFileHeaders(generatedZipFile);
- verifyAllFilesInZipContainsPath(fileHeaders, "root_folder_name/");
- verifyAllFilesInZipContainsPath(fileHeaders, "root_folder_name/test-files/");
- verifyFoldersInZip(fileHeaders, generatedZipFile, PASSWORD);
- }
-
- @Test
- public void testAddFolderWithProgressMonitor() throws IOException, InterruptedException {
- ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD);
- ProgressMonitor progressMonitor = zipFile.getProgressMonitor();
......@@ -1853,6 +1910,7 @@ Author: Andrius Merkys <merkys@debian.org>
- ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, PASSWORD, outputFolder, 1);
- verifyZipFileContainsFiles(generatedZipFile, singletonList("бореиская.txt"), CompressionMethod.DEFLATE,
- EncryptionMethod.AES, AesKeyStrength.KEY_STRENGTH_256);
- headerVerifier.verifyLocalFileHeaderUncompressedSize(generatedZipFile, "бореиская.txt", 0);
- }
-
- @Test
......@@ -2077,10 +2135,11 @@ Author: Andrius Merkys <merkys@debian.org>
-}
--- a/src/test/java/net/lingala/zip4j/ExtractZipFileIT.java
+++ /dev/null
@@ -1,343 +0,0 @@
@@ -1,432 +0,0 @@
-package net.lingala.zip4j;
-
-import net.lingala.zip4j.exception.ZipException;
-import net.lingala.zip4j.io.inputstream.ZipInputStream;
-import net.lingala.zip4j.model.FileHeader;
-import net.lingala.zip4j.model.ZipParameters;
-import net.lingala.zip4j.model.enums.AesKeyStrength;
......@@ -2095,6 +2154,7 @@ Author: Andrius Merkys <merkys@debian.org>
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.util.HashSet;
-import java.util.List;
......@@ -2409,6 +2469,93 @@ Author: Andrius Merkys <merkys@debian.org>
- }
- }
-
- @Test
- public void testExtractNestedZipFileWithNoEncryptionOnInnerAndOuter() throws IOException {
- testExtractNestedZipFileWithEncrpytion(EncryptionMethod.NONE, EncryptionMethod.NONE);
- }
-
- @Test
- public void testExtractNestedZipFileWithNoEncryptionOnInnerAndZipStandardOuter() throws IOException {
- testExtractNestedZipFileWithEncrpytion(EncryptionMethod.NONE, EncryptionMethod.ZIP_STANDARD);
- }
-
- @Test
- public void testExtractNestedZipFileWithNoEncryptionOnInnerAndAesdOuter() throws IOException {
- testExtractNestedZipFileWithEncrpytion(EncryptionMethod.NONE, EncryptionMethod.AES);
- }
-
- @Test
- public void testExtractNestedZipFileWithZipStandardEncryptionOnInnerAndNoneOuter() throws IOException {
- testExtractNestedZipFileWithEncrpytion(EncryptionMethod.ZIP_STANDARD, EncryptionMethod.NONE);
- }
-
- @Test
- public void testExtractNestedZipFileWitAesOnInnerAndNoneOuter() throws IOException {
- testExtractNestedZipFileWithEncrpytion(EncryptionMethod.AES, EncryptionMethod.NONE);
- }
-
- @Test
- public void testExtractNestedZipFileWithZipStandardEncryptionOnInnerAndAesOuter() throws IOException {
- testExtractNestedZipFileWithEncrpytion(EncryptionMethod.ZIP_STANDARD, EncryptionMethod.AES);
- }
-
- @Test
- public void testExtractNestedZipFileWithAesOnInnerAndZipStandardOuter() throws IOException {
- testExtractNestedZipFileWithEncrpytion(EncryptionMethod.AES, EncryptionMethod.ZIP_STANDARD);
- }
-
- private void testExtractNestedZipFileWithEncrpytion(EncryptionMethod innerZipEncryption,
- EncryptionMethod outerZipEncryption) throws IOException {
- File innerZipFile = temporaryFolder.newFile("inner.zip");
- File outerZipFile = temporaryFolder.newFile("outer.zip");
-
- innerZipFile.delete();
- outerZipFile.delete();
-
- createNestedZip(innerZipFile, outerZipFile, innerZipEncryption, outerZipEncryption);
-
- verifyNestedZipFile(outerZipFile, FILES_TO_ADD.size());
- }
-
- private void createNestedZip(File innerSourceZipFile, File outerSourceZipFile, EncryptionMethod innerEncryption,
- EncryptionMethod outerEncryption) throws ZipException {
-
- ZipFile innerZipFile = new ZipFile(innerSourceZipFile, PASSWORD);
- ZipParameters innerZipParameters = createZipParametersForNestedZip(innerEncryption);
- innerZipFile.addFiles(FILES_TO_ADD, innerZipParameters);
-
- ZipFile outerZipFile = new ZipFile(outerSourceZipFile, PASSWORD);
- ZipParameters outerZipParameters = createZipParametersForNestedZip(outerEncryption);
- outerZipFile.addFile(innerSourceZipFile, outerZipParameters);
- }
-
- private void verifyNestedZipFile(File outerZipFileToVerify, int numberOfFilesInNestedZip) throws IOException {
- ZipFile zipFile = new ZipFile(outerZipFileToVerify, PASSWORD);
- FileHeader fileHeader = zipFile.getFileHeader("inner.zip");
-
- assertThat(fileHeader).isNotNull();
-
- int actualNumberOfFilesInNestedZip = 0;
- try(InputStream inputStream = zipFile.getInputStream(fileHeader)) {
- try(ZipInputStream zipInputStream = new ZipInputStream(inputStream, PASSWORD)) {
- while (zipInputStream.getNextEntry() != null) {
- actualNumberOfFilesInNestedZip++;
- }
- }
- }
-
- assertThat(actualNumberOfFilesInNestedZip).isEqualTo(numberOfFilesInNestedZip);
- }
-
- private ZipParameters createZipParametersForNestedZip(EncryptionMethod encryptionMethod) {
- ZipParameters zipParameters = new ZipParameters();
- if (encryptionMethod != null && !encryptionMethod.equals(EncryptionMethod.NONE)) {
- zipParameters.setEncryptFiles(true);
- zipParameters.setEncryptionMethod(encryptionMethod);
- }
- return zipParameters;
- }
-
- private void verifyNumberOfFilesInOutputFolder(File outputFolder, int numberOfExpectedFiles) {
- assertThat(outputFolder.listFiles()).hasSize(numberOfExpectedFiles);
- }
......
......@@ -6,7 +6,7 @@
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>2.2.3-SNAPSHOT</version>
<version>2.2.7-SNAPSHOT</version>
<name>Zip4j</name>
<description>Zip4j - A Java library for zip files and streams</description>
......@@ -152,6 +152,25 @@
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Export-Package>net.lingala.zip4j.*</Export-Package>
</instructions>
</configuration>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>package</phase>
<goals>
<goal>bundle</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
......@@ -846,6 +846,8 @@ public class ZipFile {
HeaderReader headerReader = new HeaderReader();
zipModel = headerReader.readAllHeaders(randomAccessFile, charset);
zipModel.setZipFile(zipFile);
} catch (ZipException e) {
throw e;
} catch (IOException e) {
throw new ZipException(e);
}
......
......@@ -54,6 +54,7 @@ public class ZipException extends IOException {
WRONG_PASSWORD,
TASK_CANCELLED_EXCEPTION,
CHECKSUM_MISMATCH,
UNKNOWN_COMPRESSION_METHOD,
UNKNOWN
}
}
......@@ -46,7 +46,7 @@ public class FileHeaderFactory {
String fileName = validateAndGetFileName(zipParameters.getFileNameInZip());
fileHeader.setFileName(fileName);
fileHeader.setFileNameLength(determineFileNameLength(fileName));
fileHeader.setFileNameLength(determineFileNameLength(fileName, charset));
fileHeader.setDiskNumberStart(isSplitZip ? currentDiskNumberStart : 0);
if (zipParameters.getLastModifiedFileTime() > 0) {
......@@ -58,7 +58,12 @@ public class FileHeaderFactory {
//For files added by this library, this attribute will be set after closeEntry is done
fileHeader.setExternalFileAttributes(new byte[4]);
fileHeader.setDirectory(isZipEntryDirectory(fileName));
fileHeader.setUncompressedSize(zipParameters.getEntrySize());
if (zipParameters.isWriteExtendedLocalFileHeader() && zipParameters.getEntrySize() == -1) {
fileHeader.setUncompressedSize(0);
} else {
fileHeader.setUncompressedSize(zipParameters.getEntrySize());
}
if (zipParameters.isEncryptFiles() && zipParameters.getEncryptionMethod() == EncryptionMethod.ZIP_STANDARD) {
fileHeader.setCrc(zipParameters.getEntryCRC());
......@@ -156,7 +161,7 @@ public class FileHeaderFactory {
return aesExtraDataRecord;
}
private int determineFileNameLength(String fileName) {
return fileName.getBytes(InternalZipConstants.CHARSET_UTF_8).length;
private int determineFileNameLength(String fileName, Charset charset) {
return fileName.getBytes(charset).length;
}
}
......@@ -49,6 +49,7 @@ 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;
import static net.lingala.zip4j.util.Zip4jUtil.readFully;
/**
* Helper class to read header information for the zip file
......@@ -64,6 +65,8 @@ public class HeaderReader {
try {
zipModel.setEndOfCentralDirectoryRecord(readEndOfCentralDirectoryRecord(zip4jRaf, rawIO));
} catch (ZipException e){
throw e;
} catch (IOException e) {
throw new ZipException("Zip headers not found. Probably not a zip file or a corrupted zip file", e);
}
......@@ -101,7 +104,7 @@ public class HeaderReader {
!= HeaderSignature.END_OF_CENTRAL_DIRECTORY.getValue()) && counter <= 3000);
if (headerSignature != HeaderSignature.END_OF_CENTRAL_DIRECTORY.getValue()) {
throw new ZipException("zip headers not found. probably not a zip file");
throw new ZipException("Zip headers not found. Probably not a zip file");
}
endOfCentralDirectoryRecord.setSignature(HeaderSignature.END_OF_CENTRAL_DIRECTORY);
......@@ -302,7 +305,7 @@ public class HeaderReader {
}
byte[] extraFieldBuf = new byte[extraFieldLength];
inputStream.read(extraFieldBuf);
readFully(inputStream, extraFieldBuf);
try {
return parseExtraDataRecords(extraFieldBuf, extraFieldLength);
......@@ -540,7 +543,7 @@ public class HeaderReader {
localFileHeader.setVersionNeededToExtract(rawIO.readShortLittleEndian(inputStream));
byte[] generalPurposeFlags = new byte[2];
if (inputStream.read(generalPurposeFlags) != 2) {
if (readFully(inputStream, generalPurposeFlags) != 2) {
throw new ZipException("Could not read enough bytes for generalPurposeFlags");
}
localFileHeader.setEncrypted(isBitSet(generalPurposeFlags[0], 0));
......@@ -552,7 +555,7 @@ public class HeaderReader {
rawIO.readShortLittleEndian(inputStream)));
localFileHeader.setLastModifiedTime(rawIO.readIntLittleEndian(inputStream));
inputStream.read(intBuff);
readFully(inputStream, intBuff);
localFileHeader.setCrc(rawIO.readLongLittleEndian(intBuff, 0));
localFileHeader.setCrcRawData(intBuff.clone());
......@@ -566,7 +569,7 @@ public class HeaderReader {
if (fileNameLength > 0) {
byte[] fileNameBuf = new byte[fileNameLength];
inputStream.read(fileNameBuf);
readFully(inputStream, fileNameBuf);
// 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);
......@@ -612,7 +615,7 @@ public class HeaderReader {
DataDescriptor dataDescriptor = new DataDescriptor();
byte[] intBuff = new byte[4];
inputStream.read(intBuff);
readFully(inputStream, intBuff);
long sigOrCrc = rawIO.readLongLittleEndian(intBuff, 0);
//According to zip specification, presence of extra data record header signature is optional.
......@@ -620,7 +623,7 @@ public class HeaderReader {
//If signature not present, assign the read 4 bytes for crc
if (sigOrCrc == HeaderSignature.EXTRA_DATA_RECORD.getValue()) {
dataDescriptor.setSignature(HeaderSignature.EXTRA_DATA_RECORD);
inputStream.read(intBuff);
readFully(inputStream, intBuff);
dataDescriptor.setCrc(rawIO.readLongLittleEndian(intBuff, 0));
} else {
dataDescriptor.setCrc(sigOrCrc);
......
......@@ -93,7 +93,7 @@ public class HeaderWriter {
rawIO.writeShortLittleEndian(byteArrayOutputStream, fileNameBytes.length);
int extraFieldLength = 0;
if (zipModel.isZip64Format()) {
if (writeZip64Header) {
extraFieldLength += ZIP64_EXTRA_DATA_RECORD_SIZE_LFH + 4; // 4 for signature + size of record
}
if (localFileHeader.getAesExtraDataRecord() != null) {
......@@ -335,7 +335,6 @@ public class HeaderWriter {
if (outputStream instanceof CountingOutputStream) {
if (((CountingOutputStream) outputStream).checkBuffSizeAndStartNextSplitFile(buff.length)) {
//TODO check if this is correct
finalizeZipFile(zipModel, outputStream, charset);
return;
}
......
......@@ -13,6 +13,7 @@ import java.io.InputStream;
import java.util.Arrays;
import static net.lingala.zip4j.util.InternalZipConstants.AES_AUTH_LENGTH;
import static net.lingala.zip4j.util.Zip4jUtil.readFully;
class AesCipherInputStream extends CipherInputStream<AESDecrypter> {
......@@ -142,7 +143,7 @@ class AesCipherInputStream extends CipherInputStream<AESDecrypter> {
protected byte[] readStoredMac(InputStream inputStream) throws IOException {
byte[] storedMac = new byte[AES_AUTH_LENGTH];
int readLen = inputStream.read(storedMac);
int readLen = readFully(inputStream, storedMac);
if (readLen != AES_AUTH_LENGTH) {
throw new ZipException("Invalid AES Mac bytes. Could not read sufficient data");
......
......@@ -8,6 +8,8 @@ import net.lingala.zip4j.model.enums.CompressionMethod;
import java.io.IOException;
import java.io.InputStream;
import static net.lingala.zip4j.util.Zip4jUtil.readFully;
abstract class CipherInputStream<T extends Decrypter> extends InputStream {
private ZipEntryInputStream zipEntryInputStream;
......@@ -44,7 +46,7 @@ abstract class CipherInputStream<T extends Decrypter> extends InputStream {
@Override
public int read(byte[] b, int off, int len) throws IOException {
int readLen = zipEntryInputStream.read(b, off, len);
int readLen = readFully(zipEntryInputStream, b, off, len);
if (readLen > 0) {
cacheRawData(b, readLen);
......@@ -64,7 +66,7 @@ abstract class CipherInputStream<T extends Decrypter> extends InputStream {
}
protected int readRaw(byte[] b) throws IOException {
return zipEntryInputStream.readHeaders(b);
return zipEntryInputStream.readRawFully(b);
}
private void cacheRawData(byte[] b, int len) {
......
......@@ -65,7 +65,7 @@ public class InflaterInputStream extends DecompressedInputStream {
private void fill() throws IOException {
len = super.read(buff, 0, buff.length);
if (len == -1) {
throw new EOFException("Unexpected end of ZLIB input stream");
throw new EOFException("Unexpected end of input stream");
}
inflater.setInput(buff, 0, len);
}
......
......@@ -5,6 +5,8 @@ import java.io.InputStream;
class ZipEntryInputStream extends InputStream {
private static final int MAX_RAW_READ_FULLY_RETRY_ATTEMPTS = 15;
private InputStream inputStream;
private long numberOfBytesRead = 0;
private byte[] singleByteArray = new byte[1];
......@@ -52,8 +54,38 @@ class ZipEntryInputStream extends InputStream {
return readLen;
}
public int readHeaders(byte[] b) throws IOException {
return inputStream.read(b);
public int readRawFully(byte[] b) throws IOException {
int readLen = inputStream.read(b);
if (readLen != b.length) {
readLen = readUntilBufferIsFull(b, readLen);
if (readLen != b.length) {
throw new IOException("Cannot read fully into byte buffer");
}
}
return readLen;
}
private int readUntilBufferIsFull(byte[] b, int readLength) throws IOException {
int remainingLength = b.length - readLength;
int loopReadLength = 0;
int retryAttempt = 0;
while (readLength < b.length && loopReadLength != -1 && retryAttempt < MAX_RAW_READ_FULLY_RETRY_ATTEMPTS) {
loopReadLength += inputStream.read(b, readLength, remainingLength);
if (loopReadLength > 0) {
readLength += loopReadLength;
remainingLength -= loopReadLength;
}
retryAttempt++;
}
return readLength;
}
@Override
......
......@@ -40,6 +40,7 @@ public class ZipParameters {
private long entrySize = -1;
private boolean writeExtendedLocalFileHeader = true;
private boolean overrideExistingFilesInZip = true;
private String rootFolderNameInZip;
public ZipParameters() {
}
......@@ -61,6 +62,7 @@ public class ZipParameters {
this.entrySize = zipParameters.getEntrySize();
this.writeExtendedLocalFileHeader = zipParameters.isWriteExtendedLocalFileHeader();
this.overrideExistingFilesInZip = zipParameters.isOverrideExistingFilesInZip();
this.rootFolderNameInZip = zipParameters.getRootFolderNameInZip();
}
public CompressionMethod getCompressionMethod() {
......@@ -198,4 +200,12 @@ public class ZipParameters {
public void setOverrideExistingFilesInZip(boolean overrideExistingFilesInZip) {
this.overrideExistingFilesInZip = overrideExistingFilesInZip;
}
public String getRootFolderNameInZip() {
return rootFolderNameInZip;
}
public void setRootFolderNameInZip(String rootFolderNameInZip) {
this.rootFolderNameInZip = rootFolderNameInZip;
}
}
package net.lingala.zip4j.model.enums;
import net.lingala.zip4j.exception.ZipException;
public enum CompressionMethod {
STORE(0),
......@@ -16,13 +18,13 @@ public enum CompressionMethod {
return code;
}
public static CompressionMethod getCompressionMethodFromCode(int code) {
public static CompressionMethod getCompressionMethodFromCode(int code) throws ZipException {
for (CompressionMethod compressionMethod : values()) {
if (compressionMethod.getCode() == code) {
return compressionMethod;
}
}
return null;
throw new ZipException("Unknown compression method", ZipException.Type.UNKNOWN_COMPRESSION_METHOD);
}
}
......@@ -10,9 +10,9 @@ import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.CompressionMethod;
import net.lingala.zip4j.model.enums.EncryptionMethod;
import net.lingala.zip4j.progress.ProgressMonitor;
import net.lingala.zip4j.tasks.RemoveEntryFromZipFileTask.RemoveEntryFromZipFileTaskParameters;
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;
......@@ -102,7 +102,8 @@ public abstract class AbstractAddFileToZipTask<T> extends AsyncZipTask<T> {
//If an entry already exists, we have to remove that entry first and then add content again.
//In this case, add corresponding work
String relativeFileName = getRelativeFileName(fileToAdd.getAbsolutePath(), zipParameters.getDefaultFolderPath());
String relativeFileName = getRelativeFileName(fileToAdd.getAbsolutePath(), zipParameters.getDefaultFolderPath(),
zipParameters.getRootFolderNameInZip());
FileHeader fileHeader = getFileHeader(getZipModel(), relativeFileName);
if (fileHeader != null) {
totalWork += (getZipModel().getZipFile().length() - fileHeader.getCompressedSize());
......@@ -164,7 +165,8 @@ public abstract class AbstractAddFileToZipTask<T> extends AsyncZipTask<T> {
clonedZipParameters.setLastModifiedFileTime(fileToAdd.lastModified());
if (!Zip4jUtil.isStringNotNullAndNotEmpty(zipParameters.getFileNameInZip())) {
String relativeFileName = getRelativeFileName(fileToAdd.getAbsolutePath(), zipParameters.getDefaultFolderPath());
String relativeFileName = getRelativeFileName(fileToAdd.getAbsolutePath(), zipParameters.getDefaultFolderPath(),
zipParameters.getRootFolderNameInZip());
clonedZipParameters.setFileNameInZip(relativeFileName);
}
......@@ -196,7 +198,8 @@ public abstract class AbstractAddFileToZipTask<T> extends AsyncZipTask<T> {
}
for (File file : files) {
String fileName = getRelativeFileName(file.getAbsolutePath(), zipParameters.getDefaultFolderPath());
String fileName = getRelativeFileName(file.getAbsolutePath(), zipParameters.getDefaultFolderPath(),
zipParameters.getRootFolderNameInZip());
FileHeader fileHeader = getFileHeader(zipModel, fileName);
if (fileHeader != null) {
......
......@@ -13,6 +13,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.regex.Matcher;
import static net.lingala.zip4j.util.InternalZipConstants.BUFF_SIZE;
import static net.lingala.zip4j.util.InternalZipConstants.FILE_SEPARATOR;
......@@ -27,19 +28,18 @@ public abstract class AbstractExtractFileTask<T> extends AsyncZipTask<T> {
this.zipModel = zipModel;
}
protected void extractFile(ZipInputStream zipInputStream, FileHeader fileHeader, String outPath, String newFileName,
ProgressMonitor progressMonitor) throws IOException {
protected void extractFile(ZipInputStream zipInputStream, FileHeader fileHeader, String outputPath,
String newFileName, ProgressMonitor progressMonitor) throws IOException {
progressMonitor.setFileName(fileHeader.getFileName());
if (!outPath.endsWith(FILE_SEPARATOR)) {
outPath += FILE_SEPARATOR;
if (!outputPath.endsWith(FILE_SEPARATOR)) {
outputPath += FILE_SEPARATOR;
}
File outputFile = determineOutputFile(fileHeader, outputPath, newFileName);
progressMonitor.setFileName(outputFile.getAbsolutePath());
// make sure no file is extracted outside of the target directory (a.k.a zip slip)
String fileName = fileHeader.getFileName();
String completePath = outPath + fileName;
if (!new File(completePath).getCanonicalPath().startsWith(new File(outPath).getCanonicalPath())) {
if (!outputFile.getCanonicalPath().startsWith(new File(outputPath).getCanonicalPath())) {
throw new ZipException("illegal file name that breaks out of the target directory: "
+ fileHeader.getFileName());
}
......@@ -47,24 +47,19 @@ public abstract class AbstractExtractFileTask<T> extends AsyncZipTask<T> {
verifyNextEntry(zipInputStream, fileHeader);
if (fileHeader.isDirectory()) {
File file = new File(completePath);
if (!file.exists()) {
if (!file.mkdirs()) {
throw new ZipException("Could not create directory: " + file);
if (!outputFile.exists()) {
if (!outputFile.mkdirs()) {
throw new ZipException("Could not create directory: " + outputFile);
}
}
} else {
checkOutputDirectoryStructure(fileHeader, outPath, newFileName);
unzipFile(zipInputStream, fileHeader, outPath, newFileName, progressMonitor);
checkOutputDirectoryStructure(outputFile);
unzipFile(zipInputStream, fileHeader, outputFile, progressMonitor);
}
}
private void unzipFile(ZipInputStream inputStream, FileHeader fileHeader, String outputPath, String newFileName,
private void unzipFile(ZipInputStream inputStream, FileHeader fileHeader, File outputFile,
ProgressMonitor progressMonitor) throws IOException {
String outputFileName = Zip4jUtil.isStringNotNullAndNotEmpty(newFileName) ? newFileName : fileHeader.getFileName();
File outputFile = new File(outputPath + System.getProperty("file.separator") + outputFileName);
int readLength;
try (OutputStream outputStream = new FileOutputStream(outputFile)) {
while ((readLength = inputStream.read(buff)) != -1) {
......@@ -95,24 +90,25 @@ public abstract class AbstractExtractFileTask<T> extends AsyncZipTask<T> {
}
}
private void checkOutputDirectoryStructure(FileHeader fileHeader, String outPath, String newFileName)
throws ZipException {
private void checkOutputDirectoryStructure(File outputFile) throws ZipException {
if (!outputFile.getParentFile().exists() && !outputFile.getParentFile().mkdirs()) {
throw new ZipException("Unable to create parent directories: " + outputFile.getParentFile());
}
}
String fileName = fileHeader.getFileName();
private File determineOutputFile(FileHeader fileHeader, String outputPath, String newFileName) {
String outputFileName;
if (Zip4jUtil.isStringNotNullAndNotEmpty(newFileName)) {
fileName = newFileName;
outputFileName = newFileName;
} else {
outputFileName = getFileNameWithSystemFileSeparators(fileHeader.getFileName()); // replace all slashes with file separator
}
if (!Zip4jUtil.isStringNotNullAndNotEmpty(fileName)) {
// Do nothing
return;
}
return new File(outputPath + FILE_SEPARATOR + outputFileName);
}
String compOutPath = outPath + fileName;
File file = new File(compOutPath);
if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {
throw new ZipException("Unable to create parent directories: " + file.getParentFile());
}
private String getFileNameWithSystemFileSeparators(String fileNameToReplace) {
return fileNameToReplace.replaceAll("[/\\\\]", Matcher.quoteReplacement(FILE_SEPARATOR));
}
@Override
......
......@@ -46,7 +46,12 @@ public class AddFolderToZipTask extends AbstractAddFileToZipTask<AddFolderToZipT
String rootFolderPath;
File folderToAdd = taskParameters.folderToAdd;
if (taskParameters.zipParameters.isIncludeRootFolder()) {
rootFolderPath = folderToAdd.getParentFile().getCanonicalPath();
File parentFile = folderToAdd.getCanonicalFile().getParentFile();
if (parentFile == null) {
rootFolderPath = folderToAdd.getCanonicalPath();
} else {
rootFolderPath = folderToAdd.getCanonicalFile().getParentFile().getCanonicalPath();
}
} else {
rootFolderPath = folderToAdd.getCanonicalPath();
}
......
......@@ -27,7 +27,6 @@ public class ExtractAllFilesTask extends AbstractExtractFileTask<ExtractAllFiles
for (FileHeader fileHeader : getZipModel().getCentralDirectory().getFileHeaders()) {
if (fileHeader.getFileName().startsWith("__MACOSX")) {
progressMonitor.updateWorkCompleted(fileHeader.getUncompressedSize());
zipInputStream.getNextEntry(fileHeader);
continue;
}
......