[Git][java-team/zip4j][master] 7 commits: New upstream version 2.6.2

Andrius Merkys gitlab at salsa.debian.org
Fri Aug 28 07:23:19 BST 2020



Andrius Merkys pushed to branch master at Debian Java Maintainers / zip4j


Commits:
1f20aa7c by Andrius Merkys at 2020-08-28T01:35:02-04:00
New upstream version 2.6.2
- - - - -
ae6f3b02 by Andrius Merkys at 2020-08-28T01:35:10-04:00
Update upstream source from tag 'upstream/2.6.2'

Update to upstream version '2.6.2'
with Debian dir 26fb88da222483509eecbaee85db277277ec7b83
- - - - -
65ce8c84 by Andrius Merkys at 2020-08-28T01:36:42-04:00
Updating copyright years.

- - - - -
fce7f011 by Andrius Merkys at 2020-08-28T01:37:08-04:00
Adding 'Rules-Requires-Root: no'.

- - - - -
ebf3ab3f by Andrius Merkys at 2020-08-28T01:53:51-04:00
Adding Apache 2.0 license paragraph.

- - - - -
4d1fd868 by Andrius Merkys at 2020-08-28T01:54:06-04:00
Update changelog for 2.6.2-1 release

- - - - -
5b0a2029 by Andrius Merkys at 2020-08-28T02:02:59-04:00
Update changelog for 2.6.2-1 release

- - - - -


30 changed files:

- README.md
- debian/changelog
- debian/control
- debian/copyright
- pom.xml
- src/main/java/net/lingala/zip4j/ZipFile.java
- src/main/java/net/lingala/zip4j/headers/FileHeaderFactory.java
- src/main/java/net/lingala/zip4j/headers/HeaderUtil.java
- src/main/java/net/lingala/zip4j/io/outputstream/ZipStandardCipherOutputStream.java
- src/main/java/net/lingala/zip4j/model/AbstractFileHeader.java
- src/main/java/net/lingala/zip4j/tasks/AbstractAddFileToZipTask.java
- src/main/java/net/lingala/zip4j/tasks/AbstractExtractFileTask.java
- src/main/java/net/lingala/zip4j/tasks/AbstractModifyFileTask.java
- src/main/java/net/lingala/zip4j/tasks/AsyncZipTask.java
- src/main/java/net/lingala/zip4j/tasks/RemoveFilesFromZipTask.java
- src/main/java/net/lingala/zip4j/tasks/RenameFilesTask.java
- src/main/java/net/lingala/zip4j/util/FileUtils.java
- src/main/java/net/lingala/zip4j/util/Zip4jUtil.java
- src/test/java/net/lingala/zip4j/ExtractZipFileIT.java
- src/test/java/net/lingala/zip4j/MiscZipFileIT.java
- src/test/java/net/lingala/zip4j/RemoveFilesFromZipIT.java
- src/test/java/net/lingala/zip4j/RenameFilesInZipIT.java
- src/test/java/net/lingala/zip4j/headers/FileHeaderFactoryTest.java
- src/test/java/net/lingala/zip4j/headers/HeaderUtilTest.java
- src/test/java/net/lingala/zip4j/headers/HeaderWriterIT.java
- src/test/java/net/lingala/zip4j/io/outputstream/ZipOutputStreamIT.java
- src/test/java/net/lingala/zip4j/util/FileUtilsTest.java
- src/test/java/net/lingala/zip4j/util/Zip4jUtilTest.java
- + src/test/resources/test-archives/cen_dir_entries_diff_order_as_local_entries.zip
- + src/test/resources/test-archives/strong_encrypted.zip


Changes:

=====================================
README.md
=====================================
@@ -1,127 +1,137 @@
+[![javadoc](https://javadoc.io/badge2/net.lingala.zip4j/zip4j/javadoc.svg)](https://javadoc.io/doc/net.lingala.zip4j/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)
 
 [![Build Status](https://travis-ci.org/srikanth-lingala/zip4j.svg?branch=master)](https://travis-ci.org/srikanth-lingala/zip4j)
 [![Android Build Status](https://circleci.com/gh/srikanth-lingala/zip4j-android-test.svg?style=svg)](https://circleci.com/gh/srikanth-lingala/zip4j-android-test)
-[![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/srikanth-lingala/zip4j/badge.svg?targetFile=pom.xml)](https://snyk.io//test/github/srikanth-lingala/zip4j?targetFile=pom.xml)
 
 
 
-Zip4j - A java library for zip files / streams
+Zip4j - A Java library for zip files / streams
 =========================
 
 ## Thank you
 
-for rating Zip4j as the best java library for zip files <sup>[[1][1], [2][2], [3][3], [4][4]]</sup>. It has encouraged me to 
+for rating Zip4j as the best Java library for zip files <sup>[[1][1], [2][2], [3][3], [4][4]]</sup>. It has encouraged me to 
 bring this project to life again after a gap of several years. I tried to add some of the important features that 
-were requested over this time, and also made api much more neater. The newer version (> 2.0.0) now supports streams,
+were requested over this time, and also made the API much more neater. The newer version (> 2.0.0) now supports streams,
 which was understandably, one of the most requested feature. If you have any feedback, bugs to report, feature 
-requests, etc, please open an issue here on github. I will try to address them as soon as I can. I also monitor the
-tag `zip4j` on [stackoverflow][10].
+requests, etc, please open an issue here on GitHub. I will try to address them as soon as I can. I also monitor the
+tag `zip4j` on [Stack Overflow][10].
 
 ## About
 
-Zip4j is the most comprehensive java library for Zip files or streams. As of this writing, it is the only java library 
+Zip4j is the most comprehensive Java library for zip files or streams. As of this writing, it is the only Java library 
 which has support for zip encryption, apart from several other features. It tries to make handling zip files/streams 
 a lot more easier. No more clunky boiler plate code with input streams and output streams. As you can see in the usage 
 section below, working with zip files can now even be a single line of code, compared to [this][5]. I mean no offense
-to the Java's in-built zip support. In fact, this library depends on Java's in-built zip code and it would have been 
+to the Java's built-in zip support. In fact, this library depends on Java's built-in zip code and it would have been 
 significantly more ~~complicated~~ challenging if I had to write compression logic as well. But lets be honest, working with zip 
-files or streams can be a lot of boiler plate code. The main goal of this library is to provide a simple api for all 
+files or streams can be a lot of boiler plate code. The main goal of this library is to provide a simple API for all 
 usual actions of a zip file or streams by doing the heavy lifting within the library and not have developers worry about
-having to deal with streams, etc. Apart from usability, other important goal of this library is to provide support for
+having to deal with streams, etc. Apart from usability, another important goal of this library is to provide support for
 as many zip features as possible, which brings me to:
 
 ## Features
-~~~
- * Create, Add, Extract, Update, Remove files from a Zip file
- * Support for streams (ZipInputStream and ZipOutputStream)
- * Read/Write password protected Zip files and streams
- * Support for both AES and Zip-Standard encryption methods
- * Support for Zip64 format
- * Store (No Compression) and Deflate compression method
- * Create or extract files from Split Zip files (Ex: z01, z02,...zip)
- * Support for Unicode file names and comments in zip
- * Progress Monitor - for integration into apps and user facing applications
-~~~
+
+* Create, Add, Extract, Update, Remove files from a zip file
+* Support for streams (ZipInputStream and ZipOutputStream)
+* Read/Write password protected zip files and streams
+* Support for both AES and zip standard encryption methods
+* Support for Zip64 format
+* Store (No Compression) and Deflate compression method
+* Create or extract files from split zip files (Ex: z01, z02,...zip)
+* Support for Unicode file names and comments in zip
+* Progress Monitor - for integration into apps and user facing applications
 
 ## Background
 
 Zip4j was started by me (Srikanth Reddy Lingala) back in 2008/2009, when I realized the lack of support for majority of zip format 
 features in Java. And also working with zip files was, as mentioned several times above, a lot of boiler plate code, 
-having to deal with streams (worse still, it was back in the days when there was no try-with-resources in java). There
+having to deal with streams (worse still, it was back in the days when there was no try-with-resources in Java). There
 was also no comprehensive library which supports zip features. So, I decided to write one, and approximately after a 
 year, the first version was out. The response was truly overwhelming, and I got a lot of support right from the next
-day of release. It was not put on github as git/github was not as popular as it is now. Code was hosted on my website,
+day of release. It was not put on GitHub as git/GitHub was not as popular as it is now. Code was hosted on my website,
 as, guess what, a zip file :). And unfortunately, after a year or two after the initial release, life got busy and I was
 not able to support Zip4j as much as I wanted to. But the overwhelming encouragement I got over the years made me start working on Zip4j
 once again, and makes me support Zip4j as much as I can.
 
+## Requirements
+
+JDK 7 or later<sup>*</sup>
+
+* zip4j is written on JDK 8, as some of the features (NIO) that zip4j supports require some features available only in 
+JDK 8. However, considering the fact that zip4j is widely used in Android, and to support older versions of Android,
+zip4j supports JDK 7 as well. In cases where the feature/class from JDK 8 is missing, zip4j falls back to the features
+only available in JDK 7. In other words, when running on JDK 7, not all features might be supported.
+
 ## Maven
 
-~~~~
+```xml
 <dependency>
     <groupId>net.lingala.zip4j</groupId>
     <artifactId>zip4j</artifactId>
-    <version>2.6.1</version>
+    <version>2.6.2</version>
 </dependency>
-~~~~
+```
 
-Please check the latest version number on [Zip4j's Maven repository][6]
+Please check the latest version number on [Maven Central][6].
 
 ## Usage
 
 ### Creating a zip file with single file in it / Adding single file to an existing zip
 
-~~~~
+```java
 new ZipFile("filename.zip").addFile("filename.ext");
-~~~~
+```
 
    Or
 
-~~~~
+```java
 new ZipFile("filename.zip").addFile(new File("filename.ext"));
-~~~~
+```
 
 ### Creating a zip file with multiple files / Adding multiple files to an existing zip
 
-~~~~
+```java
 new ZipFile("filename.zip").addFiles(Arrays.asList(new File("first_file"), new File("second_file")));
-~~~~
+```
 
 ### Creating a zip file by adding a folder to it / Adding a folder to an existing zip
 
-~~~~
-new ZipFile("filename.zip").addFolder(new File("/user/myuser/folder_to_add"));
-~~~~
+```java
+new ZipFile("filename.zip").addFolder(new File("/users/some_user/folder_to_add"));
+```
 
 Since v2.6, it is possible to exclude certain files when adding a folder to zip by using an ExcludeFileFilter
 
-~~~~
-List<File> filesToExclude = Arrays.asList(new File("sample.pdf"), new File("sample_2.txt"));
+```java
 ExcludeFileFilter excludeFileFilter = filesToExclude::contains;
-new ZipFile("filename.zip").addFolder(new File("/user/myuser/folder_to_add"), new ZipParameters, excludeFileFilter);
-~~~~
+ZipParameters zipParameters = new ZipParameters();
+zipParameters.setExcludeFileFilter(excludeFileFilter);
+new ZipFile("filename.zip").addFolder(new File("/users/some_user/folder_to_add"), zipParameters);
+```
 
 ### Creating a zip file from stream / Adding a stream to an existing zip
 
-~~~~
+```java
 new ZipFile("filename.zip").addStream(inputStream, new ZipParameters());
-~~~~
+```
 
 Passing in `new ZipParameters()`, as in the above example, will make Zip4j use default zip parameters. Please look at
 [ZipParameters][7] to see the default configuration. 
 
-### Creating a zip file of compression method store / Adding entries to zip file of compression method store
+### Creating a zip file of compression method STORE / Adding entries to zip file of compression method STORE
 
 By default Zip4j uses Deflate compression algorithm to compress files. However, if you would like to not use any
 compression (called STORE compression), you can do so as shown in the example below: 
 
-~~~~
+```java
 ZipParameters zipParameters = new ZipParameters();
 zipParameters.setCompressionMethod(CompressionMethod.STORE);
 
 new ZipFile("filename.zip").addFile("fileToAdd", zipParameters);
-~~~~
+```
 
 You can similarly pass in zip parameters to all the other examples to create a zip file of STORE compression.
 
@@ -129,7 +139,7 @@ You can similarly pass in zip parameters to all the other examples to create a z
 
 ##### AES encryption
 
-~~~~
+```java
 ZipParameters zipParameters = new ZipParameters();
 zipParameters.setEncryptFiles(true);
 zipParameters.setEncryptionMethod(EncryptionMethod.AES);
@@ -143,22 +153,22 @@ List<File> filesToAdd = Arrays.asList(
 
 ZipFile zipFile = new ZipFile("filename.zip", "password".toCharArray());
 zipFile.addFiles(filesToAdd, zipParameters);
-~~~~
+```
 
-##### Zip Standard encryption:
+##### Zip standard encryption
 
 Instead of AES, replace `zipParameters.setEncryptionMethod(EncryptionMethod.AES);` with
-`zipParameters.setEncryptionMethod(EncryptionMethod.ZIP_STANDARD);`. You can omit the line to set Aes Key strength. As
+`zipParameters.setEncryptionMethod(EncryptionMethod.ZIP_STANDARD);`. You can omit the line to set AES key strength. As
 the name suggests, this is only applicable for AES encryption.
 
 In all the above examples, you can similarly pass in zip parameters with appropriate password configuration to create
-a password protected zip file
+a password protected zip file.
 
 ### Creating a split zip file
 
 If you want to split the zip file over several files when the size exceeds a particular limit, you can do so like this:
 
-~~~~
+```java
 List<File> filesToAdd = Arrays.asList(
     new File("somefile"), 
     new File("someotherfile")
@@ -166,17 +176,17 @@ List<File> filesToAdd = Arrays.asList(
 
 ZipFile zipFile = new ZipFile("filename.zip");
 zipFile.createSplitZipFile(filesToAdd, new ZipParameters(), true, 10485760); // using 10MB in this example
-~~~~
+```
 
 Passing in `new ZipParameters()`, as in the above example, will make Zip4j use default zip parameters. Please look at
 [ZipParameters][7] to see the default configuration. 
 
-Zip file format specifies a minimum of 65536 bytes (64kb) as a minimum length for split files. Zip4j will throw an
+Zip file format specifies a minimum of 65536 bytes (64KB) as a minimum length for split files. Zip4j will throw an
 exception if anything less than this value is specified.
 
 To create a split zip with password protection, pass in appropriate ZipParameters as shown in the example below:
 
-~~~~
+```java
 ZipParameters zipParameters = new ZipParameters();
 zipParameters.setEncryptFiles(true);
 zipParameters.setEncryptionMethod(EncryptionMethod.AES);
@@ -188,7 +198,7 @@ List<File> filesToAdd = Arrays.asList(
 
 ZipFile zipFile = new ZipFile("filename.zip", "password".toCharArray());
 zipFile.createSplitZipFile(filesToAdd, zipParameters, true, 10485760); // using 10MB in this example
-~~~~
+```
 
 ### Zip64 format
 
@@ -198,85 +208,85 @@ for file sizes. But with growing file sizes compared to a few decades back, zip
 sizes which extends 4 bytes by adding additional headers which uses 8 bytes for file sizes (compressed and 
 uncompressed file sizes). This feature is known as Zip64.
 
-Zip4j will automatically make a zip file a Zip64 format and add appropriate headers, when it detects the zip file to be
+Zip4j will automatically make a zip file with Zip64 format and add appropriate headers, when it detects the zip file to be
 crossing this file size limit. You do not have to explicitly specify any flag for Zip4j to use this feature. 
 
-### Extracting All files in a zip
+### Extracting all files from a zip
 
-~~~~
+```java
 new ZipFile("filename.zip").extractAll("/destination_directory");
-~~~~
+```
 
-### Extracting All files in a password protected zip
+### Extracting all files from a password protected zip
 
-~~~~
+```java
 new ZipFile("filename.zip", "password".toCharArray()).extractAll("/destination_directory");
-~~~~
+```
 
 ### Extracting a single file from zip
 
-~~~~
+```java
 new ZipFile("filename.zip").extractFile("fileNameInZip.txt", "/destination_directory");
-~~~~
+```
 
 ### Extracting a folder from zip (since v2.6.0)
 
-~~~~
+```java
 new ZipFile("filename.zip").extractFile("folderNameInZip/", "/destination_directory");
-~~~~
+```
 
 ### Extracting a single file from zip which is password protected
 
-~~~~
+```java
 new ZipFile("filename.zip", "password".toCharArray()).extractFile("fileNameInZip.txt", "/destination_directory");
-~~~~
+```
 
-Since v2.6.0: If the file name represents a directory, zip4j will extract all files in the zip that are part of this directory. 
+Since v2.6.0: If the file name represents a directory, Zip4j will extract all files in the zip that are part of this directory. 
 
 ### Extracting a single file from zip and giving it a new file name
 
 Below example will extract the file `fileNameInZip.txt` from the zip file to the output directory `/destination_directory` 
-and will give the file a name `newfileName.txt`. Without the third parameter of the new file name, the same name as the
+and will give the file the name `newfileName.txt`. Without the third parameter of the new file name, the same name as the
 file in the zip will be used, which in this case is `fileNameInZip.txt`. If the file being extracted is a directory,
 `newFileName` parameter will be used as the directory name. 
 
-~~~~
+```java
 new ZipFile("filename.zip", "password".toCharArray()).extractFile("fileNameInZip.txt", "/destination_directory", "newfileName.txt");
-~~~~
+```
 
 ### Get an input stream for an entry in a zip file
 
-~~~~
+```java
 ZipFile zipFile = new ZipFile("filename.zip");
 FileHeader fileHeader = zipFile.getFileHeader("entry_name_in_zip.txt");
 InputStream inputStream = zipFile.getInputStream(fileHeader);
-~~~~
+```
 
 You can now use this input stream to read content from it/write content to an output stream. Please note that the
 entry/file name is relative to the directory it is in. If `entry_name_in_zip.txt` is in a folder called "root_folder" in
-the zip, then you can use `zipFile.getFileHeader("root_folder/entry_name_in_zip.txt");`
+the zip, then you have to use `zipFile.getFileHeader("root_folder/entry_name_in_zip.txt");`.
 
 ### Remove a file/entry from a zip file
 
-~~~~
+```java
 new ZipFile("filename.zip").removeFile("fileNameInZipToRemove");
-~~~~
+```
 
-If `fileNameInZipToRemove` represents a folder. All the files and folders under this folder will be removed as well
-(this is valid since v2.5.0 of zip4j. All prior versions remove just the single entry even if it is a folder). 
+If `fileNameInZipToRemove` represents a folder all the files and folders under this folder will be removed as well
+(this is valid since v2.5.0 of Zip4j. All prior versions remove just the single entry even if it is a folder). 
 
 Please note that the file name is relative the root folder in zip. That is, if the file you want to remove exists in a 
-folder called "folder1", which in-turn exists in a folder called "root-folder", removing this file from zip can be done 
+folder called "folder1", which in-turn exists in a folder called "root-folder", removing this file from zip has to be done 
 as below:
 
-~~~~
+```java
 new ZipFile("filename.zip").removeFile("root-folder/folder1/fileNameInZipToRemove");
-~~~~
+```
 
 If you want to be sure that the file you want to remove exists in zip file or if you don't want to deal with file names
-as string when dealing `removeFile` api, you can use the other overloaded method which takes in a `FileHeader`:
+as string when using the `removeFile` API, you can use the other overloaded method which takes in a `FileHeader`:
 
-~~~~
+```java
 ZipFile zipFile = new ZipFile("someZip.zip");
 FileHeader fileHeader = zipFile.getFileHeader("fileNameInZipToRemove");
 
@@ -285,17 +295,17 @@ if (fileHeader == null) {
 }
 
 zipFile.removeFile(fileHeader);
-~~~~
+```
 
-Since v2.5.0 of zip4j, it is possible to remove multiple files and folders from a zip file. You can now pass in a list
+Since v2.5.0 of Zip4j, it is possible to remove multiple files and folders from a zip file. You can now pass in a list
 as shown in the code below:
 
-~~~~
+```java
 ZipFile zipFile = new ZipFile("someZip.zip");
 List<String> filesToRemove = Arrays.asList("file1.txt", "file2.txt", "some-folder/", "some-new-folder-1/somefile.pdf");
 
 zipFile.removeFiles(filesToRemove);
-~~~~
+```
 
 The above code will remove `file1.txt`, `file2.txt`, all files and folders under `some-folder` (including `some-folder`)
 and just the entry `somefile.pdf` in folder `some-new-folder-1`. All other files and folders are kept intact in the zip
@@ -303,128 +313,128 @@ file.
 
 ### Rename entries in the zip file
 
-There are three ways to rename an entry in a zip file with zip4j. One way is to pass in a file header and the new file 
+There are three ways to rename an entry in a zip file with Zip4j. One way is to pass in a file header and the new file 
 name:
 
-~~~~
+```java
 ZipFile zipFile = new ZipFile("sample.zip");
 FileHeader fileHeader = zipFile.getFileHeader("entry-to-be-changed.pdf");
 zipFile.renameFile(fileHeader, "new-file-name.pdf");
-~~~~
+```
 
 Second way is to pass in just the file name to be changed (instead of the file header), and the new file name. 
 
-~~~~
+```java
 new ZipFile("filename.zip").renameFile("entry-to-be-changed.pdf", "new-file-name.pdf");
-~~~~
+```
 
 It is also possible to change multiple file names at once. In this case you have to use a map, with the key of the entry 
 in the map being the entry to be changed, and the value of the map being the new file name:
 
-~~~~
+```java
 Map<String, String> fileNamesMap = new HashMap<>();
 fileNamesMap.put("firstFile.txt", "newFileFirst.txt");
 fileNamesMap.put("secondFile.pdf", "newSecondFile.pdf");
 fileNamesMap.put("some-folder/thirdFile.bin", "some-folder/newThirdFile.bin");
 new ZipFile("filename.zip").renameFile("entry-to-be-changed.pdf", "new-file-name.pdf");
-~~~~
+```
 
 To modify an entry name which is inside a folder, the new file name should contain the complete parent path as well.
 For example, if an entry by the name `some-entry.pdf` is in the folder `some-folder/some-sub-folder/`, to modify this 
 entry name to `some-new-entry.pdf`:
 
-~~~~
+```java
 new ZipFile("filename.zip").renameFile("some-folder/some-sub-folder/some-entry.pdf", "some-folder/some-sub-folder/new-entry.pdf");
-~~~~
+```
 
-if the parent path path is missing, then the file will be put at the root of the zip file. In the below example, after
+If the parent path is missing, then the file will be put at the root of the zip file. In the below example, after
 the file is renamed, `some-new-entry.pdf` will exist at the root of the zip file instead of at `some-folder/some-sub-folder/`:
 
-~~~~
+```java
 new ZipFile("filename.zip").renameFile("some-folder/some-sub-folder/some-entry.pdf", "some-new-entry.pdf");
-~~~~
+```
 
 This also gives the flexibility to "move" the entry to a different folder. The below example will move the 
 `some-entry.pdf` from `some-folder/some-sub-folder/` to `folder-to-be-moved-to/sub-folder/` and the file will also be 
 renamed to `new-entry.pdf`. To just move the file, use the same file name instead of a new file name.
 
-~~~~
+```java
 new ZipFile("filename.zip").renameFile("some-folder/some-sub-folder/some-entry.pdf", "folder-to-be-moved-to/sub-folder/new-entry.pdf");
-~~~~
+```
 
 If the entry being modified is a directory, all entries that are part of that directory will be renamed so that all of 
 them have the new folder name as parent. In zip format, all entry names under a directory will contain the full name as their file name.
 For example if there is an entry by the name `filename.txt` inside a directory `directoryName`, the file name for the entry 
 will be `directoryName/filename.txt`. And if the name of the directory has now been changed to `newDirectoryName`, the
-entry under it will also be changed to `newDirectoryName/filename.txt`, so the when the zip file is extracted, 
+entry under it will also be changed to `newDirectoryName/filename.txt`, so when the zip file is extracted, 
 `filename.txt` will be under `newDirectoryName`.
 
-Zip file format does not allow modifying split zip files, and zip4j will throw an exception if an attempt is made to 
+Zip file format does not allow modifying split zip files, and Zip4j will throw an exception if an attempt is made to 
 rename files in a split zip file.
 
 ### Merging split zip files into a single zip
 
 This is the reverse of creating a split zip file, that is, this feature will merge a zip file which is split across 
-several files into a single zip file
+several files into a single zip file:
 
-~~~~
-new ZipFile("split_zip_file.zip").mergeZipFile("merged_zip_file.zip");
-~~~~
+```java
+new ZipFile("split_zip_file.zip").mergeSplitFiles(new File("merged_zip_file.zip"));
+```
 
-This method will throw an exception if the split zip file (in this case `split_zip_file.zip`) is not a split zip file
+This method will throw an exception if the split zip file (in this case `split_zip_file.zip`) is not a split zip file.
 
 ### List all files in a zip
 
-~~~~
+```java
 List<FileHeader> fileHeaders = new ZipFile("zipfile.zip").getFileHeaders();
 fileHeaders.stream().forEach(fileHeader -> System.out.println(fileHeader.getFileName()));
-~~~~
+```
 
 You can get all other information from the `FileHeader` object corresponding to each file/entry in the zip.
 
 ### Check if a zip file is password protected
 
-~~~~
+```java
 new ZipFile("encrypted_zip_file.zip").isEncrypted();
-~~~~
+```
 
 ### Check if a zip file is a split zip file
 
-~~~~
+```java
 new ZipFile("split_zip_file.zip").isSplitArchive();
-~~~~
+```
 
 ### Set comment for a zip file
 
-~~~~
+```java
 new ZipFile("some_zip_file.zip").setComment("Some comment");
-~~~~
+```
 
 ### Remove comment of a zip file
 
-~~~~
+```java
 new ZipFile("some_zip_file.zip").setComment("");
-~~~~
+```
 
 ### Get comment of a zip file
 
-~~~~
+```java
 new ZipFile("some_zip_file.zip").getComment();
-~~~~
+```
 
 ### Check if a zip file is valid
 
 Note: This will only check for the validity of the headers and not the validity of each entry in the zip file.
 
-~~~~
+```java
 new ZipFile("valid_zip_file.zip").isValidZipFile();
-~~~~
+```
 
 ## Working with streams
 
 ### Adding entries with ZipOutputStream
 
-~~~~
+```java
 import net.lingala.zip4j.io.outputstream.ZipOutputStream;
 import net.lingala.zip4j.model.ZipParameters;
 import net.lingala.zip4j.model.enums.AesKeyStrength;
@@ -493,11 +503,11 @@ public class ZipOutputStreamExample {
     return zipParameters;
   }
 }
-~~~~
+```
 
 ### Extract files with ZipInputStream
 
-~~~~
+```java
 import net.lingala.zip4j.io.inputstream.ZipInputStream;
 import net.lingala.zip4j.model.LocalFileHeader;
 
@@ -527,19 +537,17 @@ public class ZipInputStreamExample {
     }
   }
 }
-
-
-~~~~
+```
 
 ## Working with Progress Monitor
 
 ProgressMonitor makes it easier for applications (especially user facing) to integrate Zip4j. It is useful to show
-progress (example: updating a progress bar, displaying the current action, show file name being worked on, etc). To use
+progress (example: updating a progress bar, displaying the current action, show file name being worked on, etc.). To use
 ProgressMonitor, you have to set `ZipFile.setRunInThread(true)`. This will make any actions being done on the zip file
 to run in a background thread. You can then access ProgressMonitor `Zipfile.getProgressMonitor()` and get details of the
 current action being done along with the percentage work done, etc. Below is an example:
 
-~~~
+```java
 ZipFile zipFile = new ZipFile(generatedZipFile, PASSWORD);
 ProgressMonitor progressMonitor = zipFile.getProgressMonitor();
 
@@ -561,7 +569,7 @@ if (progressMonitor.getResult().equals(ProgressMonitor.Result.SUCCESS)) {
 } else if (progressMonitor.getResult().equals(ProgressMonitor.Result.CANCELLED)) {
   System.out.println("Task cancelled");
 }
-~~~
+```
 
 Note that in the above example, `addFolder()` will almost immediately return back the control to the caller. The client
 code can then perform a loop until the state gets back to "Ready" as shown in the above example.
@@ -570,10 +578,10 @@ Similarly, ProgressMonitor can be used with other actions like, `addFiles`, `rem
 
 ## Contribution
 
-It is hard to find as much free time as I used to have when I first started Zip4j 10 years back in 2009. I would
+It is hard to find as much free time as I used to have when I first started Zip4j back in 2009. I would
 highly appreciate any support I can get for this project. You can fork this project, and send me pull requests for
 any bug fixes, issues mentioned here or new features. If you need any support in understanding the code or zip specification, 
-just drop me a mail and I will help you as best as I can. (See FAQ for my email id.)
+just drop me a mail and I will help you as best as I can. (See FAQ for my email address.)
 
 ## FAQ
 
@@ -587,29 +595,29 @@ just drop me a mail and I will help you as best as I can. (See FAQ for my email
 
 3. **Are unicode file names supported?**
 
-    Yes, unicode file names (UTF-8) are supported as specified by the zip format specification. Zip4j will use utf-8 file
-name and file comment encoding when creating a zip file. When extracting a zip file, Zip4j will only use utf-8 encoding,
+    Yes, unicode file names (UTF-8) are supported as specified by the zip format specification. Zip4j will use UTF-8 file
+name and file comment encoding when creating a zip file. When extracting a zip file, Zip4j will only use UTF-8 encoding,
 only if the appropriate header flag is set as specified by zip file format specification. If this flag is not set, 
-Zip4j will use Cp437 encoding which only supports English alphabetical characters.
+Zip4j will use CP437 encoding which only supports extended ASCII characters.
  
-4. **Where can I find Zip file format specification?**
+4. **Where can I find zip file format specification?**
 
     [Here][9]
 
 5. **Why are there so many changes in version 2.x compared to 1.x?**
 
-    Because 1.x was written about 10 years back, Zip4j was badly in need of a face-lift and code modernization. Also, my 
-coding standards have also improved over the years (or at least that's what I like to think). Although I am proud of 
-the work I did with Zip4j 10 years back, some parts of the code make me feel like hiding my face in shame. One such example
-is the usage of `ArrayList` instead of `List`. Api and code should look much neater now. And also, Zip4j now supports
+    Because when version 1.x was written back in 2009, Zip4j was badly in need of a face-lift and code modernization. Also, my 
+coding standards have improved over the years (or at least that's what I like to think). Although I am proud of 
+the work I did with Zip4j back in 2009, some parts of the code make me feel like hiding my face in shame. One such example
+is the usage of `ArrayList` instead of `List`. API and code should look much neater now. And also, Zip4j now supports
 a minimum of JRE 8, as compared to JRE 5 with 1.x, which obviously will bring some nice features that I can make use of. (For
 example: no more explicitly closing the streams all over the code). If you still feel like something can be improved (and
 I am pretty sure that there are things to be improved), please let me know by opening an issue here or writing to me 
-(My email id is in point #2 above).
+(my email address is in point #2 above).
 
-6. **What are the licensing conditions for older releases of zip4j?**
+6. **What are the licensing conditions for older releases of Zip4j?**
 
-    All releases of zip4j, from version 1.0, are licensed under Apache License 2.0
+    All releases of Zip4j, from version 1.0, are licensed under Apache License 2.0
 
 
 [1]: https://stackoverflow.com/questions/9324933/what-is-a-good-java-library-to-zip-unzip-files
@@ -618,7 +626,7 @@ I am pretty sure that there are things to be improved), please let me know by op
 [4]: https://stackoverflow.com/questions/18201279/file-compression-library-for-java/18201553
 [5]: https://www.baeldung.com/java-compress-and-uncompress
 [6]: https://mvnrepository.com/artifact/net.lingala.zip4j/zip4j
-[7]: https://github.com/srikanth-lingala/zip4j/blob/master/src/main/java/net/lingala/zip4j/model/ZipParameters.java
+[7]: https://javadoc.io/doc/net.lingala.zip4j/zip4j/latest/net/lingala/zip4j/model/ZipParameters.html#ZipParameters--
 [8]: https://www.baeldung.com/java-storing-passwords
 [9]: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
 [10]: https://stackoverflow.com/questions/tagged/zip4j


=====================================
debian/changelog
=====================================
@@ -1,3 +1,12 @@
+zip4j (2.6.2-1) unstable; urgency=medium
+
+  * New upstream version 2.6.2
+  * Updating copyright years.
+  * Adding 'Rules-Requires-Root: no'.
+  * Adding Apache 2.0 license paragraph.
+
+ -- Andrius Merkys <merkys at debian.org>  Fri, 28 Aug 2020 02:02:49 -0400
+
 zip4j (2.6.1-1) unstable; urgency=medium
 
   * New upstream version 2.6.1


=====================================
debian/control
=====================================
@@ -13,6 +13,7 @@ Standards-Version: 4.3.0
 Vcs-Git: https://salsa.debian.org/java-team/zip4j.git
 Vcs-Browser: https://salsa.debian.org/java-team/zip4j
 Homepage: http://www.lingala.net/zip4j
+Rules-Requires-Root: no
 
 Package: libzip4j-java
 Architecture: all


=====================================
debian/copyright
=====================================
@@ -3,13 +3,25 @@ Upstream-Name: zip4j
 Source: https://github.com/srikanth-lingala/zip4j/releases
 
 Files: *
-Copyright: 2019, Srikanth Reddy Lingala
+Copyright: 2019-2020, Srikanth Reddy Lingala <srikanth.mailbox at gmail.com>
 License: Apache-2.0
 
 Files: debian/*
-Copyright: 2019, Andrius Merkys <merkys at debian.org>
+Copyright: 2019-2020, Andrius Merkys <merkys at debian.org>
 License: Apache-2.0
 
 License: Apache-2.0
+ Licensed 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.
+ .
  On Debian systems, the full text of the Apache-2.0 license
  can be found in the file '/usr/share/common-licenses/Apache-2.0'


=====================================
pom.xml
=====================================
@@ -6,7 +6,7 @@
 
     <groupId>net.lingala.zip4j</groupId>
     <artifactId>zip4j</artifactId>
-    <version>2.6.2-SNAPSHOT</version>
+    <version>2.6.3-SNAPSHOT</version>
 
     <name>Zip4j</name>
     <description>Zip4j - A Java library for zip files and streams</description>


=====================================
src/main/java/net/lingala/zip4j/ZipFile.java
=====================================
@@ -27,24 +27,15 @@ import net.lingala.zip4j.model.ZipModel;
 import net.lingala.zip4j.model.ZipParameters;
 import net.lingala.zip4j.model.enums.RandomAccessFileMode;
 import net.lingala.zip4j.progress.ProgressMonitor;
-import net.lingala.zip4j.tasks.AddFilesToZipTask;
+import net.lingala.zip4j.tasks.*;
 import net.lingala.zip4j.tasks.AddFilesToZipTask.AddFilesToZipTaskParameters;
-import net.lingala.zip4j.tasks.AddFolderToZipTask;
 import net.lingala.zip4j.tasks.AddFolderToZipTask.AddFolderToZipTaskParameters;
-import net.lingala.zip4j.tasks.AddStreamToZipTask;
 import net.lingala.zip4j.tasks.AddStreamToZipTask.AddStreamToZipTaskParameters;
-import net.lingala.zip4j.tasks.AsyncZipTask;
-import net.lingala.zip4j.tasks.ExtractAllFilesTask;
 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.RemoveFilesFromZipTask;
 import net.lingala.zip4j.tasks.RemoveFilesFromZipTask.RemoveFilesFromZipTaskParameters;
-import net.lingala.zip4j.tasks.RenameFilesTask;
 import net.lingala.zip4j.tasks.RenameFilesTask.RenameFilesTaskParameters;
-import net.lingala.zip4j.tasks.SetCommentTask;
 import net.lingala.zip4j.tasks.SetCommentTask.SetCommentTaskTaskParameters;
 import net.lingala.zip4j.util.FileUtils;
 import net.lingala.zip4j.util.RawIO;
@@ -904,9 +895,14 @@ public class ZipFile {
   /**
    * Checks to see if the input zip file is a valid zip file. This method
    * will try to read zip headers. If headers are read successfully, this
-   * method returns true else false
+   * method returns true else false.
+   *
+   * Since v2.7.0: if the zip file is a split zip file, this method also checks to see if
+   * all the split files of the zip exists.
+   *
+   * @return boolean - true if a valid zip file, i.e, zip4j is able to read the
+   * zip headers, and in case of a split zip file, all split files of the zip exists; false otherwise
    *
-   * @return boolean
    * @since 1.2.3
    */
   public boolean isValidZipFile() {
@@ -916,6 +912,11 @@ public class ZipFile {
 
     try {
       readZipInfo();
+
+      if (zipModel.isSplitArchive() && !verifyAllSplitFilesOfZipExists(getSplitZipFiles())) {
+        return false;
+      }
+
       return true;
     } catch (Exception e) {
       return false;
@@ -1009,6 +1010,15 @@ public class ZipFile {
     return new AsyncZipTask.AsyncTaskParameters(executorService, runInThread, progressMonitor);
   }
 
+  private boolean verifyAllSplitFilesOfZipExists(List<File> allSplitFiles) {
+    for (File splitFile : allSplitFiles) {
+      if (!splitFile.exists()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
   public ProgressMonitor getProgressMonitor() {
     return progressMonitor;
   }


=====================================
src/main/java/net/lingala/zip4j/headers/FileHeaderFactory.java
=====================================
@@ -9,6 +9,7 @@ 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.FileUtils;
 import net.lingala.zip4j.util.InternalZipConstants;
 import net.lingala.zip4j.util.RawIO;
 import net.lingala.zip4j.util.Zip4jUtil;
@@ -55,14 +56,14 @@ public class FileHeaderFactory {
     fileHeader.setDiskNumberStart(isSplitZip ? currentDiskNumberStart : 0);
 
     if (zipParameters.getLastModifiedFileTime() > 0) {
-      fileHeader.setLastModifiedTime(Zip4jUtil.javaToDosTime(zipParameters.getLastModifiedFileTime()));
+      fileHeader.setLastModifiedTime(Zip4jUtil.epochToExtendedDosTime(zipParameters.getLastModifiedFileTime()));
     } else {
-      fileHeader.setLastModifiedTime(Zip4jUtil.javaToDosTime(System.currentTimeMillis()));
+      fileHeader.setLastModifiedTime(Zip4jUtil.epochToExtendedDosTime(System.currentTimeMillis()));
     }
 
-    //For files added by this library, this attribute will be set after closeEntry is done
-    fileHeader.setExternalFileAttributes(new byte[4]);
-    fileHeader.setDirectory(isZipEntryDirectory(fileName));
+    boolean isDirectory = isZipEntryDirectory(fileName);
+    fileHeader.setDirectory(isDirectory);
+    fileHeader.setExternalFileAttributes(FileUtils.getDefaultFileAttributes(isDirectory));
 
     if (zipParameters.isWriteExtendedLocalFileHeader() && zipParameters.getEntrySize() == -1) {
       fileHeader.setUncompressedSize(0);


=====================================
src/main/java/net/lingala/zip4j/headers/HeaderUtil.java
=====================================
@@ -32,39 +32,6 @@ public class HeaderUtil {
     return fileHeader;
   }
 
-  public static int getIndexOfFileHeader(ZipModel zipModel, FileHeader fileHeader) throws ZipException {
-
-    if (zipModel == null || fileHeader == null) {
-      throw new ZipException("input parameters is null, cannot determine index of file header");
-    }
-
-    if (zipModel.getCentralDirectory() == null
-        || zipModel.getCentralDirectory().getFileHeaders() == null
-        || zipModel.getCentralDirectory().getFileHeaders().size() <= 0) {
-      return -1;
-    }
-
-    String fileName = fileHeader.getFileName();
-
-    if (!isStringNotNullAndNotEmpty(fileName)) {
-      throw new ZipException("file name in file header is empty or null, cannot determine index of file header");
-    }
-
-    List<FileHeader> fileHeadersFromCentralDir = zipModel.getCentralDirectory().getFileHeaders();
-    for (int i = 0; i < fileHeadersFromCentralDir.size(); i++) {
-      FileHeader fileHeaderFromCentralDir = fileHeadersFromCentralDir.get(i);
-      String fileNameForHdr = fileHeaderFromCentralDir.getFileName();
-      if (!isStringNotNullAndNotEmpty(fileNameForHdr)) {
-        continue;
-      }
-
-      if (fileName.equalsIgnoreCase(fileNameForHdr)) {
-        return i;
-      }
-    }
-    return -1;
-  }
-
   public static String decodeStringWithCharset(byte[] data, boolean isUtf8Encoded, Charset charset) {
     if (InternalZipConstants.CHARSET_UTF_8.equals(charset) && !isUtf8Encoded) {
       try {
@@ -81,18 +48,6 @@ public class HeaderUtil {
     return new String(data, InternalZipConstants.CHARSET_UTF_8);
   }
 
-
-  public static long getOffsetOfNextEntry(ZipModel zipModel, FileHeader fileHeader) throws ZipException {
-    int indexOfFileHeader = getIndexOfFileHeader(zipModel, fileHeader);
-
-    List<FileHeader> fileHeaders = zipModel.getCentralDirectory().getFileHeaders();
-    if (indexOfFileHeader == fileHeaders.size() - 1) {
-      return getOffsetStartOfCentralDirectory(zipModel);
-    } else {
-      return fileHeaders.get(indexOfFileHeader + 1).getOffsetLocalHeader();
-    }
-  }
-
   public static long getOffsetStartOfCentralDirectory(ZipModel zipModel) {
     if (zipModel.isZip64Format()) {
       return zipModel.getZip64EndOfCentralDirectoryRecord().getOffsetStartCentralDirectoryWRTStartDiskNumber();


=====================================
src/main/java/net/lingala/zip4j/io/outputstream/ZipStandardCipherOutputStream.java
=====================================
@@ -39,7 +39,7 @@ class ZipStandardCipherOutputStream extends CipherOutputStream<StandardEncrypter
 
   private long getEncryptionKey(ZipParameters zipParameters) {
     if (zipParameters.isWriteExtendedLocalFileHeader()) {
-      long dosTime = Zip4jUtil.javaToDosTime(zipParameters.getLastModifiedFileTime());
+      long dosTime = Zip4jUtil.epochToExtendedDosTime(zipParameters.getLastModifiedFileTime());
       return (dosTime & 0x0000ffff) << 16;
     }
 


=====================================
src/main/java/net/lingala/zip4j/model/AbstractFileHeader.java
=====================================
@@ -2,6 +2,7 @@ package net.lingala.zip4j.model;
 
 import net.lingala.zip4j.model.enums.CompressionMethod;
 import net.lingala.zip4j.model.enums.EncryptionMethod;
+import net.lingala.zip4j.util.Zip4jUtil;
 
 import java.util.List;
 
@@ -59,6 +60,10 @@ public abstract class AbstractFileHeader extends ZipHeader {
     this.lastModifiedTime = lastModifiedTime;
   }
 
+  public long getLastModifiedTimeEpoch() {
+    return Zip4jUtil.dosToExtendedEpochTme(lastModifiedTime);
+  }
+
   public long getCrc() {
     return crc;
   }


=====================================
src/main/java/net/lingala/zip4j/tasks/AbstractAddFileToZipTask.java
=====================================
@@ -33,14 +33,12 @@ import static net.lingala.zip4j.model.enums.CompressionMethod.DEFLATE;
 import static net.lingala.zip4j.model.enums.CompressionMethod.STORE;
 import static net.lingala.zip4j.model.enums.EncryptionMethod.NONE;
 import static net.lingala.zip4j.model.enums.EncryptionMethod.ZIP_STANDARD;
-import static net.lingala.zip4j.progress.ProgressMonitor.Task.ADD_ENTRY;
-import static net.lingala.zip4j.progress.ProgressMonitor.Task.CALCULATE_CRC;
-import static net.lingala.zip4j.progress.ProgressMonitor.Task.REMOVE_ENTRY;
+import static net.lingala.zip4j.progress.ProgressMonitor.Task.*;
 import static net.lingala.zip4j.util.CrcUtil.computeFileCrc;
 import static net.lingala.zip4j.util.FileUtils.assertFilesExist;
 import static net.lingala.zip4j.util.FileUtils.getRelativeFileName;
 import static net.lingala.zip4j.util.InternalZipConstants.BUFF_SIZE;
-import static net.lingala.zip4j.util.Zip4jUtil.javaToDosTime;
+import static net.lingala.zip4j.util.Zip4jUtil.epochToExtendedDosTime;
 
 public abstract class AbstractAddFileToZipTask<T> extends AsyncZipTask<T> {
 
@@ -200,7 +198,7 @@ public abstract class AbstractAddFileToZipTask<T> extends AsyncZipTask<T> {
   private ZipParameters cloneAndAdjustZipParameters(ZipParameters zipParameters, File fileToAdd,
                                                     ProgressMonitor progressMonitor) throws IOException {
     ZipParameters clonedZipParameters = new ZipParameters(zipParameters);
-    clonedZipParameters.setLastModifiedFileTime(javaToDosTime((fileToAdd.lastModified())));
+    clonedZipParameters.setLastModifiedFileTime(epochToExtendedDosTime((fileToAdd.lastModified())));
 
     if (fileToAdd.isDirectory()) {
       clonedZipParameters.setEntrySize(0);


=====================================
src/main/java/net/lingala/zip4j/tasks/AbstractExtractFileTask.java
=====================================
@@ -128,6 +128,11 @@ public abstract class AbstractExtractFileTask<T> extends AsyncZipTask<T> {
   }
 
   private void verifyNextEntry(ZipInputStream zipInputStream, FileHeader fileHeader) throws IOException {
+    if (BitUtils.isBitSet(fileHeader.getGeneralPurposeFlag()[0], 6)) {
+      throw new ZipException("Entry with name " + fileHeader.getFileName() + " is encrypted with Strong Encryption. " +
+          "Zip4j does not support Strong Encryption, as this is patented.");
+    }
+
     LocalFileHeader localFileHeader = zipInputStream.getNextEntry(fileHeader);
 
     if (localFileHeader == null) {


=====================================
src/main/java/net/lingala/zip4j/tasks/AbstractModifyFileTask.java
=====================================
@@ -11,6 +11,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.RandomAccessFile;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
 
@@ -31,17 +32,16 @@ abstract class AbstractModifyFileTask<T> extends AsyncZipTask<T> {
     return tmpFile;
   }
 
-  void updateOffsetsForAllSubsequentFileHeaders(ZipModel zipModel, FileHeader fileHeaderModified, long offsetToAdd) throws ZipException {
-    int indexOfFileHeader = HeaderUtil.getIndexOfFileHeader(zipModel, fileHeaderModified);
+  void updateOffsetsForAllSubsequentFileHeaders(List<FileHeader> sortedFileHeaders, ZipModel zipModel,
+                                                FileHeader fileHeaderModified, long offsetToAdd) throws ZipException {
+    int indexOfFileHeader = getIndexOfFileHeader(sortedFileHeaders, fileHeaderModified);
 
     if (indexOfFileHeader == -1) {
       throw new ZipException("Could not locate modified file header in zipModel");
     }
 
-    List<FileHeader> allFileHeaders = zipModel.getCentralDirectory().getFileHeaders();
-
-    for (int i = indexOfFileHeader + 1; i < allFileHeaders.size(); i++) {
-      FileHeader fileHeaderToUpdate = allFileHeaders.get(i);
+    for (int i = indexOfFileHeader + 1; i < sortedFileHeaders.size(); i++) {
+      FileHeader fileHeaderToUpdate = sortedFileHeaders.get(i);
       fileHeaderToUpdate.setOffsetLocalHeader(fileHeaderToUpdate.getOffsetLocalHeader() + offsetToAdd);
 
       if (zipModel.isZip64Format()
@@ -71,6 +71,30 @@ abstract class AbstractModifyFileTask<T> extends AsyncZipTask<T> {
     return length;
   }
 
+  List<FileHeader> cloneAndSortFileHeadersByOffset(List<FileHeader> allFileHeaders) {
+    List<FileHeader> clonedFileHeaders = new ArrayList<>(allFileHeaders);
+    clonedFileHeaders.sort((o1, o2) -> {
+      if (o1.getFileName().equals(o2.getFileName())) {
+        return 0;
+      }
+
+      return o1.getOffsetLocalHeader() < o2.getOffsetLocalHeader() ? -1 : 1;
+    });
+
+    return clonedFileHeaders;
+  }
+
+  long getOffsetOfNextEntry(List<FileHeader> sortedFileHeaders, FileHeader fileHeader,
+                                   ZipModel zipModel) throws ZipException {
+    int indexOfFileHeader = getIndexOfFileHeader(sortedFileHeaders, fileHeader);
+
+    if (indexOfFileHeader == sortedFileHeaders.size() - 1) {
+      return HeaderUtil.getOffsetStartOfCentralDirectory(zipModel);
+    } else {
+      return sortedFileHeaders.get(indexOfFileHeader + 1).getOffsetLocalHeader();
+    }
+  }
+
   private void restoreFileName(File zipFile, File temporaryZipFile) throws ZipException {
     if (zipFile.delete()) {
       if (!temporaryZipFile.renameTo(zipFile)) {
@@ -80,4 +104,15 @@ abstract class AbstractModifyFileTask<T> extends AsyncZipTask<T> {
       throw new ZipException("cannot delete old zip file");
     }
   }
+
+  private int getIndexOfFileHeader(List<FileHeader> allFileHeaders, FileHeader fileHeaderForIndex) throws ZipException {
+    for (int i = 0; i < allFileHeaders.size(); i++) {
+      FileHeader fileHeader = allFileHeaders.get(i);
+      if (fileHeader.equals(fileHeaderForIndex)) {
+        return i;
+      }
+    }
+
+    throw new ZipException("Could not find file header in list of central directory file headers");
+  }
 }


=====================================
src/main/java/net/lingala/zip4j/tasks/AsyncZipTask.java
=====================================
@@ -32,6 +32,8 @@ public abstract class AsyncZipTask<T> {
           performTaskWithErrorHandling(taskParameters, progressMonitor);
         } catch (ZipException e) {
           //Do nothing. Exception will be passed through progress monitor
+        } finally {
+          executorService.shutdown();
         }
       });
     } else {


=====================================
src/main/java/net/lingala/zip4j/tasks/RemoveFilesFromZipTask.java
=====================================
@@ -49,12 +49,12 @@ public class RemoveFilesFromZipTask extends AbstractModifyFileTask<RemoveFilesFr
          RandomAccessFile inputStream = new RandomAccessFile(zipModel.getZipFile(), RandomAccessFileMode.READ.getValue())){
 
       long currentFileCopyPointer = 0;
-      List<FileHeader> allUnchangedFileHeaders = new ArrayList<>(zipModel.getCentralDirectory().getFileHeaders());
+      List<FileHeader> sortedFileHeaders = cloneAndSortFileHeadersByOffset(zipModel.getCentralDirectory().getFileHeaders());
 
-      for (FileHeader fileHeader : allUnchangedFileHeaders) {
-        long lengthOfCurrentEntry = HeaderUtil.getOffsetOfNextEntry(zipModel, fileHeader) - outputStream.getFilePointer();
+      for (FileHeader fileHeader : sortedFileHeaders) {
+        long lengthOfCurrentEntry = getOffsetOfNextEntry(sortedFileHeaders, fileHeader, zipModel) - outputStream.getFilePointer();
         if (shouldEntryBeRemoved(fileHeader, entriesToRemove)) {
-          updateHeaders(fileHeader, lengthOfCurrentEntry);
+          updateHeaders(sortedFileHeaders, fileHeader, lengthOfCurrentEntry);
 
           if (!zipModel.getCentralDirectory().getFileHeaders().remove(fileHeader)) {
             throw new ZipException("Could not remove entry from list of central directory headers");
@@ -102,8 +102,8 @@ public class RemoveFilesFromZipTask extends AbstractModifyFileTask<RemoveFilesFr
     return false;
   }
 
-  private void updateHeaders(FileHeader fileHeaderThatWasRemoved, long offsetToSubtract) throws ZipException {
-    updateOffsetsForAllSubsequentFileHeaders(zipModel, fileHeaderThatWasRemoved, negate(offsetToSubtract));
+  private void updateHeaders(List<FileHeader> sortedFileHeaders, FileHeader fileHeaderThatWasRemoved, long offsetToSubtract) throws ZipException {
+    updateOffsetsForAllSubsequentFileHeaders(sortedFileHeaders, zipModel, fileHeaderThatWasRemoved, negate(offsetToSubtract));
 
     EndOfCentralDirectoryRecord endOfCentralDirectoryRecord = zipModel.getEndOfCentralDirectoryRecord();
     endOfCentralDirectoryRecord.setOffsetOfStartOfCentralDirectory(


=====================================
src/main/java/net/lingala/zip4j/tasks/RenameFilesTask.java
=====================================
@@ -17,7 +17,6 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.io.RandomAccessFile;
 import java.nio.charset.Charset;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -56,13 +55,13 @@ public class RenameFilesTask extends AbstractModifyFileTask<RenameFilesTask.Rena
       // it might be that a file name has changed because of other file name, ex: if a directory name has to be changed
       // and the file is part of that directory, by the time the file has to be changed, its name might have changed
       // when changing the name of the directory. There is some overhead with this approach, but is safer.
-      List<FileHeader> allUnchangedFileHeaders = new ArrayList<>(zipModel.getCentralDirectory().getFileHeaders());
+      List<FileHeader> sortedFileHeaders = cloneAndSortFileHeadersByOffset(zipModel.getCentralDirectory().getFileHeaders());
 
-      for (FileHeader fileHeader : allUnchangedFileHeaders) {
+      for (FileHeader fileHeader : sortedFileHeaders) {
         Map.Entry<String, String> fileNameMapForThisEntry = getCorrespondingEntryFromMap(fileHeader, fileNamesMap);
         progressMonitor.setFileName(fileHeader.getFileName());
 
-        long lengthToCopy = HeaderUtil.getOffsetOfNextEntry(zipModel, fileHeader) - outputStream.getFilePointer();
+        long lengthToCopy = getOffsetOfNextEntry(sortedFileHeaders, fileHeader, zipModel) - outputStream.getFilePointer();
         if (fileNameMapForThisEntry == null) {
           // copy complete entry without any changes
           currentFileCopyPointer += copyFile(inputStream, outputStream, currentFileCopyPointer, lengthToCopy, progressMonitor);
@@ -74,7 +73,7 @@ public class RenameFilesTask extends AbstractModifyFileTask<RenameFilesTask.Rena
           currentFileCopyPointer = copyEntryAndChangeFileName(newFileNameBytes, fileHeader, currentFileCopyPointer, lengthToCopy,
               inputStream, outputStream, progressMonitor);
 
-          updateHeadersInZipModel(fileHeader, newFileName, newFileNameBytes, headersOffset);
+          updateHeadersInZipModel(sortedFileHeaders, fileHeader, newFileName, newFileNameBytes, headersOffset);
         }
 
         verifyIfTaskIsCancelled();
@@ -133,8 +132,8 @@ public class RenameFilesTask extends AbstractModifyFileTask<RenameFilesTask.Rena
     return null;
   }
 
-  private void updateHeadersInZipModel(FileHeader fileHeader, String newFileName, byte[] newFileNameBytes,
-                                       int headersOffset) throws ZipException {
+  private void updateHeadersInZipModel(List<FileHeader> sortedFileHeaders, FileHeader fileHeader, String newFileName,
+                                       byte[] newFileNameBytes, int headersOffset) throws ZipException {
 
     FileHeader fileHeaderToBeChanged = HeaderUtil.getFileHeader(zipModel, fileHeader.getFileName());
 
@@ -147,7 +146,7 @@ public class RenameFilesTask extends AbstractModifyFileTask<RenameFilesTask.Rena
     fileHeaderToBeChanged.setFileName(newFileName);
     fileHeaderToBeChanged.setFileNameLength(newFileNameBytes.length);
 
-    updateOffsetsForAllSubsequentFileHeaders(zipModel, fileHeaderToBeChanged, headersOffset);
+    updateOffsetsForAllSubsequentFileHeaders(sortedFileHeaders, zipModel, fileHeaderToBeChanged, headersOffset);
 
     zipModel.getEndOfCentralDirectoryRecord().setOffsetOfStartOfCentralDirectory(
         zipModel.getEndOfCentralDirectoryRecord().getOffsetOfStartOfCentralDirectory() + headersOffset);


=====================================
src/main/java/net/lingala/zip4j/util/FileUtils.java
=====================================
@@ -41,15 +41,17 @@ import static net.lingala.zip4j.util.Zip4jUtil.isStringNotNullAndNotEmpty;
 
 public class FileUtils {
 
+  public static final byte[] DEFAULT_POSIX_FILE_ATTRIBUTES = new byte[] {0, 0, -128, -127}; //-rw-------
+  public static final byte[] DEFAULT_POSIX_FOLDER_ATTRIBUTES = new byte[] {0, 0, -128, 65}; //drw-------
+
   public static void setFileAttributes(Path file, byte[] fileAttributes) {
     if (fileAttributes == null || fileAttributes.length == 0) {
       return;
     }
 
-    String os = System.getProperty("os.name").toLowerCase();
-    if (isWindows(os)) {
+    if (isWindows()) {
       applyWindowsFileAttributes(file, fileAttributes);
-    } else if (isMac(os) || isUnix(os)) {
+    } else if (isMac() || isUnix()) {
       applyPosixFileAttributes(file, fileAttributes);
     }
   }
@@ -60,14 +62,14 @@ public class FileUtils {
     }
 
     try {
-      Files.setLastModifiedTime(file, FileTime.fromMillis(Zip4jUtil.dosToJavaTme(lastModifiedTime)));
+      Files.setLastModifiedTime(file, FileTime.fromMillis(Zip4jUtil.dosToExtendedEpochTme(lastModifiedTime)));
     } catch (Exception e) {
       // Ignore
     }
   }
 
   public static void setFileLastModifiedTimeWithoutNio(File file, long lastModifiedTime) {
-    file.setLastModified(Zip4jUtil.dosToJavaTme(lastModifiedTime));
+    file.setLastModified(Zip4jUtil.dosToExtendedEpochTme(lastModifiedTime));
   }
 
   public static byte[] getFileAttributes(File file) {
@@ -78,10 +80,9 @@ public class FileUtils {
 
       Path path = file.toPath();
 
-      String os = System.getProperty("os.name").toLowerCase();
-      if (isWindows(os)) {
+      if (isWindows()) {
         return getWindowsFileAttributes(path);
-      } else if (isMac(os) || isUnix(os)) {
+      } else if (isMac() || isUnix()) {
         return getPosixFileAttributes(path);
       } else {
         return new byte[4];
@@ -396,6 +397,33 @@ public class FileUtils {
     }
   }
 
+  public static byte[] getDefaultFileAttributes(boolean isDirectory) {
+    byte[] permissions = new byte[4];
+    if (isUnix() || isMac()) {
+      if (isDirectory) {
+        System.arraycopy(DEFAULT_POSIX_FOLDER_ATTRIBUTES, 0, permissions, 0, permissions.length);
+      } else {
+        System.arraycopy(DEFAULT_POSIX_FILE_ATTRIBUTES, 0, permissions, 0, permissions.length);
+      }
+    }
+    return permissions;
+  }
+
+  public static boolean isWindows() {
+    String os = System.getProperty("os.name").toLowerCase();
+    return (os.contains("win"));
+  }
+
+  public static boolean isMac() {
+    String os = System.getProperty("os.name").toLowerCase();
+    return (os.contains("mac"));
+  }
+
+  public static boolean isUnix() {
+    String os = System.getProperty("os.name").toLowerCase();
+    return (os.contains("nux"));
+  }
+
   private static String getExtensionZerosPrefix(int index) {
     if (index < 9) {
       return "00";
@@ -521,22 +549,4 @@ public class FileUtils {
       posixFilePermissions.add(posixFilePermissionToAdd);
     }
   }
-
-  public static boolean isWindows() {
-    String os = System.getProperty("os.name").toLowerCase();
-    return isWindows(os);
-  }
-
-  private static boolean isWindows(String os) {
-    return (os.contains("win"));
-  }
-
-  private static boolean isMac(String os) {
-    return (os.contains("mac"));
-  }
-
-  private static boolean isUnix(String os) {
-    return (os.contains("nux"));
-  }
-
 }


=====================================
src/main/java/net/lingala/zip4j/util/Zip4jUtil.java
=====================================
@@ -27,6 +27,7 @@ import java.util.Calendar;
 
 public class Zip4jUtil {
 
+  private static final long DOSTIME_BEFORE_1980 = (1 << 21) | (1 << 16);
   private static final int MAX_RAW_READ_FULLY_RETRY_ATTEMPTS = 15;
 
   public static boolean isStringNotNullAndNotEmpty(String str) {
@@ -51,22 +52,37 @@ public class Zip4jUtil {
     return true;
   }
 
-  public static long javaToDosTime(long time) {
+  public static long epochToExtendedDosTime(long time) {
+    if (time < 0) {
+      return DOSTIME_BEFORE_1980;
+    }
+    long dostime = epochToDosTime(time);
+    return (dostime != DOSTIME_BEFORE_1980)
+            ? dostime + ((time % 2000) << 32)
+            : DOSTIME_BEFORE_1980;
+  }
 
+  private static long epochToDosTime(long time) {
     Calendar cal = Calendar.getInstance();
     cal.setTimeInMillis(time);
 
     int year = cal.get(Calendar.YEAR);
+
     if (year < 1980) {
-      return (1 << 21) | (1 << 16);
+      return DOSTIME_BEFORE_1980;
     }
     return (year - 1980) << 25 | (cal.get(Calendar.MONTH) + 1) << 21 |
-        cal.get(Calendar.DATE) << 16 | cal.get(Calendar.HOUR_OF_DAY) << 11 | cal.get(Calendar.MINUTE) << 5 |
-        cal.get(Calendar.SECOND) >> 1;
+            cal.get(Calendar.DATE) << 16 | cal.get(Calendar.HOUR_OF_DAY) << 11 | cal.get(Calendar.MINUTE) << 5 |
+            cal.get(Calendar.SECOND) >> 1;
+  }
+
+  public static long dosToExtendedEpochTme(long dosTime) {
+    long time = dosToEpochTime(dosTime);
+    return time + (dosTime >> 32);
   }
 
-  public static long dosToJavaTme(long dosTime) {
-    int sec = (int) (2 * (dosTime & 0x1f));
+  private static long dosToEpochTime(long dosTime) {
+    int sec = (int) ((dosTime << 1) & 0x3e);
     int min = (int) ((dosTime >> 5) & 0x3f);
     int hrs = (int) ((dosTime >> 11) & 0x1f);
     int day = (int) ((dosTime >> 16) & 0x1f);


=====================================
src/test/java/net/lingala/zip4j/ExtractZipFileIT.java
=====================================
@@ -485,6 +485,16 @@ public class ExtractZipFileIT extends AbstractIT {
     extractFile(TestUtils.getTestArchiveFromResources("jar-dir-lfh-and-fh-entry-size-2.jar"));
   }
 
+  @Test
+  public void testExtractZipStrongEncryptionThrowsException() throws IOException {
+    ZipFile zipFile = new ZipFile(getTestArchiveFromResources("strong_encrypted.zip"), "12345678".toCharArray());
+    expectedException.expect(ZipException.class);
+    expectedException.expectMessage("Entry with name test.txt is encrypted with Strong Encryption. " +
+        "Zip4j does not support Strong Encryption, as this is patented.");
+
+    zipFile.extractAll(outputFolder.getPath());
+  }
+
   private void addFileToZip(ZipFile zipFile, String fileName, EncryptionMethod encryptionMethod, String password) throws ZipException {
     ZipParameters zipParameters = new ZipParameters();
     zipParameters.setEncryptFiles(encryptionMethod != null);


=====================================
src/test/java/net/lingala/zip4j/MiscZipFileIT.java
=====================================
@@ -13,17 +13,15 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.io.*;
 import java.nio.charset.Charset;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import static java.util.Collections.singletonList;
 import static net.lingala.zip4j.testutils.TestUtils.getTestFileFromResources;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.fail;
@@ -517,6 +515,52 @@ public class MiscZipFileIT extends AbstractIT {
     assertThat(zipFile.getExecutorService()).isNotNull();
   }
 
+  @Test
+  public void testFileHeaderLastModifiedTimeEpoch() throws IOException {
+    ZipFile zipFile = new ZipFile(generatedZipFile);
+    File fileToAdd = TestUtils.getTestFileFromResources("file_PDF_1MB.pdf");
+    zipFile.addFile(fileToAdd);
+    FileHeader fileHeader = zipFile.getFileHeader("file_PDF_1MB.pdf");
+    assertThat(fileHeader.getLastModifiedTimeEpoch()).isEqualTo(fileToAdd.lastModified());
+  }
+
+  @Test
+  public void testVerifyZipFileForNonSplitZipFileReturnsTrue() throws IOException {
+    ZipFile zipFile = new ZipFile(generatedZipFile);
+    zipFile.addFile(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf"));
+
+    assertThat(zipFile.isValidZipFile()).isTrue();
+  }
+
+  @Test
+  public void testVerifyZipFileForNonZipFileReturnsFalse() throws IOException {
+    ZipFile zipFile = new ZipFile(TestUtils.getTestFileFromResources("sample.pdf"));
+    assertThat(zipFile.isValidZipFile()).isFalse();
+  }
+
+  @Test
+  public void testVerifyZipFileForSplitZipFileReturnsTrueWhenAllSplitFilesExists() throws IOException {
+    ZipFile zipFile = new ZipFile(generatedZipFile);
+    zipFile.createSplitZipFile(singletonList(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")), new ZipParameters(),
+            true, InternalZipConstants.MIN_SPLIT_LENGTH);
+
+    assertThat(zipFile.isValidZipFile()).isTrue();
+  }
+
+  @Test
+  public void testVerifyZipFileForSplitZipFileReturnsFalseWhenOneSplitFileDoesNotExist() throws IOException {
+    ZipFile zipFile = new ZipFile(generatedZipFile);
+    zipFile.createSplitZipFile(singletonList(TestUtils.getTestFileFromResources("file_PDF_1MB.pdf")), new ZipParameters(),
+            true, InternalZipConstants.MIN_SPLIT_LENGTH);
+    String firstSplitFileName = zipFile.getFile().getName().replace(".zip", ".z02");
+    File firstSplitFile = Paths.get(zipFile.getFile().getParentFile().getPath(), firstSplitFileName).toFile();
+    if (!firstSplitFile.delete()) {
+      throw new RuntimeException("Unable to delete a split file of zip which is a requirement to run this test");
+    }
+
+    assertThat(zipFile.isValidZipFile()).isFalse();
+  }
+
   private void verifyInputStream(InputStream inputStream, File fileToCompareAgainst) throws IOException {
     File outputFile = temporaryFolder.newFile();
     try (OutputStream outputStream = new FileOutputStream(outputFile)) {


=====================================
src/test/java/net/lingala/zip4j/RemoveFilesFromZipIT.java
=====================================
@@ -201,4 +201,31 @@ public class RemoveFilesFromZipIT extends AbstractIT {
     ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 2);
     verifyFileHeadersDoesNotExist(zipFile, filesToRemove);
   }
+
+  @Test
+  public void testRemoveFirstEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries() throws IOException {
+    testRemoveEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries("test-files/zero_byte_file.txt");
+  }
+
+  @Test
+  public void testRemoveLastEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries() throws IOException {
+    testRemoveEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries("test-files/бореиская.txt");
+  }
+
+  @Test
+  public void testRemoveMiddleEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries() throws IOException {
+    testRemoveEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries("test-files/file_PDF_1MB.pdf");
+  }
+
+  private void testRemoveEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries(
+      String fileNameToRemove) throws IOException {
+    TestUtils.copyFile(TestUtils.getTestArchiveFromResources("cen_dir_entries_diff_order_as_local_entries.zip"),
+        generatedZipFile);
+    ZipFile zipFile = new ZipFile(generatedZipFile);
+    zipFile.removeFile(fileNameToRemove);
+
+    zipFile = new ZipFile(generatedZipFile);
+    ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, outputFolder, 12);
+    verifyFileHeadersDoesNotExist(zipFile, Collections.singletonList(fileNameToRemove));
+  }
 }


=====================================
src/test/java/net/lingala/zip4j/RenameFilesInZipIT.java
=====================================
@@ -260,6 +260,33 @@ public class RenameFilesInZipIT extends AbstractIT {
     verifyFileNamesChanged(zipFile, fileNamesMap, false);
   }
 
+  @Test
+  public void testRenameFirstEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries() throws IOException {
+    testRenameEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries("test-files/zero_byte_file.txt");
+  }
+
+  @Test
+  public void testRenameLastEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries() throws IOException {
+    testRenameEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries("test-files/бореиская.txt");
+  }
+
+  @Test
+  public void testRenameMiddleEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries() throws IOException {
+    testRenameEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries("test-files/file_PDF_1MB.pdf");
+  }
+
+  private void testRenameEntryFromZipWhichHasCentralDirEntriesInDifferentOrderThanLocalEntries(
+      String fileNameToRename) throws IOException {
+    TestUtils.copyFile(TestUtils.getTestArchiveFromResources("cen_dir_entries_diff_order_as_local_entries.zip"),
+        generatedZipFile);
+    ZipFile zipFile = new ZipFile(generatedZipFile);
+    zipFile.renameFile(fileNameToRename, "somename.txt");
+
+    zipFile = new ZipFile(generatedZipFile);
+    ZipFileVerifier.verifyZipFileByExtractingAllFiles(generatedZipFile, null, outputFolder, 13, false);
+    verifyFileNamesChanged(zipFile, Collections.singletonMap(fileNameToRename, "somename.txt"), false);
+  }
+
   private void verifyFileNamesChanged(ZipFile zipFile, Map<String, String> fileNamesMap) throws IOException {
     verifyFileNamesChanged(zipFile, fileNamesMap, true);
   }


=====================================
src/test/java/net/lingala/zip4j/headers/FileHeaderFactoryTest.java
=====================================
@@ -10,6 +10,7 @@ import net.lingala.zip4j.model.enums.AesVersion;
 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.FileUtils;
 import net.lingala.zip4j.util.InternalZipConstants;
 import net.lingala.zip4j.util.RawIO;
 import org.junit.After;
@@ -21,7 +22,7 @@ import org.junit.rules.ExpectedException;
 import java.nio.charset.Charset;
 
 import static net.lingala.zip4j.util.BitUtils.isBitSet;
-import static net.lingala.zip4j.util.Zip4jUtil.javaToDosTime;
+import static net.lingala.zip4j.util.Zip4jUtil.epochToExtendedDosTime;
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class FileHeaderFactoryTest {
@@ -200,7 +201,7 @@ public class FileHeaderFactoryTest {
 
     FileHeader fileHeader = fileHeaderFactory.generateFileHeader(zipParameters, false, 0, InternalZipConstants.CHARSET_UTF_8, rawIO);
 
-    assertThat(fileHeader.getLastModifiedTime()).isEqualTo(javaToDosTime(zipParameters.getLastModifiedFileTime()));
+    assertThat(fileHeader.getLastModifiedTime()).isEqualTo(epochToExtendedDosTime(zipParameters.getLastModifiedFileTime()));
   }
 
   @Test
@@ -257,7 +258,7 @@ public class FileHeaderFactoryTest {
 
   @Test
   public void testGenerateLocalFileHeader() {
-    long lastModifiedFileTime = javaToDosTime(System.currentTimeMillis());
+    long lastModifiedFileTime = epochToExtendedDosTime(System.currentTimeMillis());
     FileHeader  fileHeader = generateFileHeader(lastModifiedFileTime);
 
     LocalFileHeader localFileHeader = fileHeaderFactory.generateLocalFileHeader(fileHeader);
@@ -266,13 +267,13 @@ public class FileHeaderFactoryTest {
   }
 
   @Test
-  public void testVersionMadeByWindowsWithUnixModeOff() throws ZipException {
+  public void testVersionMadeByWindowsWithUnixModeOff() {
     changeOsSystemPropertyToWindows();
     testVersionMadeBy(generateZipParameters(), 51);
   }
 
   @Test
-  public void testVersionMadeByWindowsWithUnixModeOn() throws ZipException {
+  public void testVersionMadeByWindowsWithUnixModeOn() {
     changeOsSystemPropertyToWindows();
     ZipParameters zipParameters = generateZipParameters();
     zipParameters.setUnixMode(true);
@@ -280,13 +281,13 @@ public class FileHeaderFactoryTest {
   }
 
   @Test
-  public void testVersionMadeByUnix() throws ZipException {
+  public void testVersionMadeByUnix() {
     changeOsSystemPropertyToUnix();
     testVersionMadeBy(generateZipParameters(), 819);
   }
 
   @Test
-  public void testVersionMadeByMac() throws ZipException {
+  public void testVersionMadeByMac() {
     changeOsSystemPropertyToMac();
     testVersionMadeBy(generateZipParameters(), 819);
   }
@@ -340,7 +341,7 @@ public class FileHeaderFactoryTest {
     verifyGeneralPurposeBytes(fileHeader.getGeneralPurposeFlag(), zipParameters);
     assertThat(fileHeader.getDiskNumberStart()).isEqualTo(isSplitZip ? diskNumberStart : 0);
     verifyLastModifiedFileTime(fileHeader, zipParameters);
-    assertThat(fileHeader.getExternalFileAttributes()).isEqualTo(new byte[4]);
+    verifyExternalFileAttributes(fileHeader);
     assertThat(fileHeader.isDirectory()).isEqualTo(false);
 
     if (zipParameters.isWriteExtendedLocalFileHeader()) {
@@ -370,6 +371,22 @@ public class FileHeaderFactoryTest {
     assertThat(localFileHeader.getGeneralPurposeFlag()).containsExactly(2, 28);
     assertThat(localFileHeader.isDataDescriptorExists()).isTrue();
     assertThat(localFileHeader.getExtraFieldLength()).isEqualTo(190);
+  }
+
+  private void verifyExternalFileAttributes(FileHeader fileHeader) {
+    if (FileUtils.isUnix() || FileUtils.isMac()) {
+      if (FileUtils.isZipEntryDirectory(fileHeader.getFileName())) {
+        assertThat(fileHeader.getExternalFileAttributes()).isEqualTo(FileUtils.DEFAULT_POSIX_FOLDER_ATTRIBUTES);
+      } else {
+        assertThat(fileHeader.getExternalFileAttributes()).isEqualTo(FileUtils.DEFAULT_POSIX_FILE_ATTRIBUTES);
+      }
+    } else {
+      assertThat(fileHeader.getExternalFileAttributes()).isEqualTo(new byte[4]);
+    }
+
+
+
+
   }
 
   private void verifyCompressionMethod(FileHeader fileHeader, ZipParameters zipParameters) {
@@ -382,7 +399,7 @@ public class FileHeaderFactoryTest {
 
   private void verifyLastModifiedFileTime(FileHeader fileHeader, ZipParameters zipParameters) {
     if (zipParameters.getLastModifiedFileTime() > 0) {
-      assertThat(fileHeader.getLastModifiedTime()).isEqualTo(javaToDosTime(
+      assertThat(fileHeader.getLastModifiedTime()).isEqualTo(epochToExtendedDosTime(
           zipParameters.getLastModifiedFileTime()));
     } else {
       assertThat(fileHeader.getLastModifiedTime()).isGreaterThan(0);


=====================================
src/test/java/net/lingala/zip4j/headers/HeaderUtilTest.java
=====================================
@@ -141,82 +141,6 @@ public class HeaderUtilTest {
     assertThat(HeaderUtil.getFileHeader(zipModel, "SHOULD_NOT_EXIST")).isNull();
   }
 
-  @Test
-  public void testGetIndexOfFileHeaderWhenZipModelIsNullThrowsException() throws ZipException {
-    expectZipException("input parameters is null, cannot determine index of file header");
-    HeaderUtil.getIndexOfFileHeader(null, new FileHeader());
-  }
-
-  @Test
-  public void testGetIndexOfFileHeaderWhenFileHeaderlIsNullThrowsException() throws ZipException {
-    expectZipException("input parameters is null, cannot determine index of file header");
-    HeaderUtil.getIndexOfFileHeader(new ZipModel(), null);
-  }
-
-  @Test
-  public void testGetIndexOfFileHeaderWhenCentralDirectoryIsNullReturnsNegativeOne() throws ZipException {
-    ZipModel zipModel = new ZipModel();
-    zipModel.setCentralDirectory(null);
-
-    assertThat(HeaderUtil.getIndexOfFileHeader(zipModel, new FileHeader())).isEqualTo(-1);
-  }
-
-  @Test
-  public void testGetIndexOfFileHeaderWhenFileHeadersIsNullReturnsNegativeOne() throws ZipException {
-    ZipModel zipModel = new ZipModel();
-    zipModel.getCentralDirectory().setFileHeaders(null);
-
-    assertThat(HeaderUtil.getIndexOfFileHeader(zipModel, new FileHeader())).isEqualTo(-1);
-  }
-
-  @Test
-  public void testGetIndexOfFileHeaderWhenFileHeadersIsEmptyReturnsNegativeOne() throws ZipException {
-    ZipModel zipModel = new ZipModel();
-    zipModel.getCentralDirectory().setFileHeaders(Collections.emptyList());
-
-    assertThat(HeaderUtil.getIndexOfFileHeader(zipModel, new FileHeader())).isEqualTo(-1);
-  }
-
-  @Test
-  public void testGetIndexOfFileHeaderWithNullFileNameInFileHeaderThrowsException() throws ZipException {
-    expectZipException("file name in file header is empty or null, cannot determine index of file header");
-
-    ZipModel zipModel = new ZipModel();
-    zipModel.getCentralDirectory().setFileHeaders(Collections.singletonList(new FileHeader()));
-
-    HeaderUtil.getIndexOfFileHeader(zipModel, new FileHeader());
-  }
-
-  @Test
-  public void testGetIndexOfFileHeaderWithEmptyFileNameInFileHeaderThrowsException() throws ZipException {
-    expectZipException("file name in file header is empty or null, cannot determine index of file header");
-
-    ZipModel zipModel = new ZipModel();
-    zipModel.getCentralDirectory().setFileHeaders(Collections.singletonList(new FileHeader()));
-    FileHeader fileHeader = new FileHeader();
-    fileHeader.setFileName("");
-
-    HeaderUtil.getIndexOfFileHeader(zipModel, new FileHeader());
-  }
-
-  @Test
-  public void testGetIndexOfFileHeaderGetsIndexSuccessfully() throws ZipException {
-    String fileNamePrefix = "FILE_NAME_";
-    int numberOfEntriesToAdd = 10;
-    List<FileHeader> fileHeadersInZipModel = generateFileHeaderWithFileNamesWithEmptyAndNullFileNames(fileNamePrefix, numberOfEntriesToAdd);
-    ZipModel zipModel = new ZipModel();
-    zipModel.getCentralDirectory().setFileHeaders(fileHeadersInZipModel);
-
-    FileHeader fileHeaderToFind = new FileHeader();
-    for (int i = 0; i < numberOfEntriesToAdd; i++) {
-      fileHeaderToFind.setFileName(fileNamePrefix + i);
-      assertThat(HeaderUtil.getIndexOfFileHeader(zipModel, fileHeaderToFind)).isEqualTo(i);
-    }
-
-    fileHeaderToFind.setFileName(fileNamePrefix + numberOfEntriesToAdd);
-    assertThat(HeaderUtil.getIndexOfFileHeader(zipModel, fileHeaderToFind)).isEqualTo(-1);
-  }
-
   @Test
   public void testDecodeStringWithCharsetForUtf8() {
     String utf8StringToEncode = "asdäüöö";


=====================================
src/test/java/net/lingala/zip4j/headers/HeaderWriterIT.java
=====================================
@@ -4,19 +4,8 @@ import net.lingala.zip4j.AbstractIT;
 import net.lingala.zip4j.exception.ZipException;
 import net.lingala.zip4j.io.outputstream.CountingOutputStream;
 import net.lingala.zip4j.io.outputstream.SplitOutputStream;
-import net.lingala.zip4j.model.AESExtraDataRecord;
-import net.lingala.zip4j.model.CentralDirectory;
-import net.lingala.zip4j.model.DataDescriptor;
-import net.lingala.zip4j.model.ExtraDataRecord;
-import net.lingala.zip4j.model.FileHeader;
-import net.lingala.zip4j.model.LocalFileHeader;
-import net.lingala.zip4j.model.Zip64ExtendedInfo;
-import net.lingala.zip4j.model.ZipModel;
-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.model.enums.RandomAccessFileMode;
+import net.lingala.zip4j.model.*;
+import net.lingala.zip4j.model.enums.*;
 import net.lingala.zip4j.util.BitUtils;
 import net.lingala.zip4j.util.InternalZipConstants;
 import net.lingala.zip4j.util.RawIO;
@@ -24,18 +13,12 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
+import java.io.*;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.List;
 
-import static net.lingala.zip4j.util.Zip4jUtil.javaToDosTime;
+import static net.lingala.zip4j.util.Zip4jUtil.epochToExtendedDosTime;
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class HeaderWriterIT extends AbstractIT {
@@ -47,9 +30,9 @@ public class HeaderWriterIT extends AbstractIT {
   private static final long UNCOMPRESSED_SIZE_ZIP64 = InternalZipConstants.ZIP_64_SIZE_LIMIT + 1;
   private static final int VERSION_MADE_BY = 20;
   private static final int VERSION_NEEDED_TO_EXTRACT = 20;
-  private static final long LAST_MODIFIED_FILE_TIME = javaToDosTime(System.currentTimeMillis());
   private static final byte[] EXTERNAL_FILE_ATTRIBUTES = new byte[] {23, 43, 0, 0};
   private static final String FILE_COMMENT_PREFIX = "FILE_COMMENT_PREFIX_";
+  private static final long LAST_MODIFIED_FILE_TIME = epochToExtendedDosTime(System.currentTimeMillis() / 1000);
 
   @Rule
   public ExpectedException expectedException = ExpectedException.none();


=====================================
src/test/java/net/lingala/zip4j/io/outputstream/ZipOutputStreamIT.java
=====================================
@@ -9,7 +9,9 @@ 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.testutils.TestUtils;
 import net.lingala.zip4j.util.BitUtils;
+import net.lingala.zip4j.util.FileUtils;
 import net.lingala.zip4j.util.InternalZipConstants;
 import org.junit.Rule;
 import org.junit.Test;
@@ -27,6 +29,8 @@ import java.util.List;
 
 import static net.lingala.zip4j.testutils.TestUtils.getTestFileFromResources;
 import static net.lingala.zip4j.testutils.ZipFileVerifier.verifyZipFileByExtractingAllFiles;
+import static net.lingala.zip4j.util.FileUtils.isMac;
+import static net.lingala.zip4j.util.FileUtils.isUnix;
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class ZipOutputStreamIT extends AbstractIT {
@@ -167,6 +171,38 @@ public class ZipOutputStreamIT extends AbstractIT {
     zos.setComment("SOME_COMMENT");
   }
 
+  @Test
+  public void testDefaultFileAttributes() throws IOException {
+    List<File> filesToAdd = new ArrayList<>(FILES_TO_ADD);
+    filesToAdd.add(TestUtils.getTestFileFromResources("/"));
+    byte[] buff = new byte[4096];
+    int readLen;
+    ZipParameters zipParameters = new ZipParameters();
+
+    try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(generatedZipFile))) {
+      for (File fileToAdd : filesToAdd) {
+        if (fileToAdd.isDirectory()) {
+          zipParameters.setFileNameInZip(fileToAdd.getName() + "/");
+          zipOutputStream.putNextEntry(zipParameters);
+          zipOutputStream.closeEntry();
+          continue;
+        }
+
+        zipParameters.setFileNameInZip(fileToAdd.getName());
+        zipOutputStream.putNextEntry(zipParameters);
+        InputStream inputStream = new FileInputStream(fileToAdd);
+        while ((readLen = inputStream.read(buff)) != -1) {
+          zipOutputStream.write(buff, 0, readLen);
+        }
+
+        inputStream.close();
+        zipOutputStream.closeEntry();
+      }
+    }
+
+    verifyDefaultFileAttributes();
+  }
+
   private void testZipOutputStream(CompressionMethod compressionMethod, boolean encrypt,
                                    EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength,
                                    AesVersion aesVersion)
@@ -332,6 +368,25 @@ public class ZipOutputStreamIT extends AbstractIT {
     assertThat(zipFile.getComment()).isEqualTo(expectedComment);
   }
 
+  private void verifyDefaultFileAttributes() throws ZipException {
+    ZipFile zipFile = new ZipFile(generatedZipFile);
+    List<FileHeader> fileHeaders = zipFile.getFileHeaders();
+    byte[] emptyAttributes = new byte[4];
+
+    for (FileHeader fileHeader : fileHeaders) {
+      if (isUnix() || isMac()) {
+        if (fileHeader.isDirectory()) {
+          assertThat(fileHeader.getExternalFileAttributes()).isEqualTo(FileUtils.DEFAULT_POSIX_FOLDER_ATTRIBUTES);
+        } else {
+          assertThat(fileHeader.getExternalFileAttributes()).isEqualTo(FileUtils.DEFAULT_POSIX_FILE_ATTRIBUTES);
+        }
+      } else {
+        assertThat(fileHeader.getExternalFileAttributes()).isEqualTo(emptyAttributes);
+      }
+
+    }
+  }
+
   private ZipFile initializeZipFileWithCharset(Charset charset) {
     ZipFile zipFile = new ZipFile(generatedZipFile);
 


=====================================
src/test/java/net/lingala/zip4j/util/FileUtilsTest.java
=====================================
@@ -18,10 +18,7 @@ import java.util.List;
 
 import static net.lingala.zip4j.util.InternalZipConstants.FILE_SEPARATOR;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
 
 public class FileUtilsTest {
 
@@ -58,7 +55,7 @@ public class FileUtilsTest {
     long currentTime = System.currentTimeMillis();
     FileUtils.setFileLastModifiedTime(path, currentTime);
 
-    verify(basicFileAttributeView).setTimes(FileTime.fromMillis(Zip4jUtil.dosToJavaTme(currentTime)), null, null);
+    verify(basicFileAttributeView).setTimes(FileTime.fromMillis(Zip4jUtil.dosToExtendedEpochTme(currentTime)), null, null);
   }
 
   @Test
@@ -79,7 +76,7 @@ public class FileUtilsTest {
 
     FileUtils.setFileLastModifiedTimeWithoutNio(file, currentTime);
 
-    verify(file).setLastModified(Zip4jUtil.dosToJavaTme(currentTime));
+    verify(file).setLastModified(Zip4jUtil.dosToExtendedEpochTme(currentTime));
   }
 
   @Test


=====================================
src/test/java/net/lingala/zip4j/util/Zip4jUtilTest.java
=====================================
@@ -97,7 +97,7 @@ public class Zip4jUtilTest {
   public void testJavaToDosTime() {
     TimeZone defaultTimeZone = TimeZone.getDefault();
     TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));
-    assertThat(Zip4jUtil.javaToDosTime(1560526564503L)).isEqualTo(1322159234);
+    assertThat(Zip4jUtil.epochToExtendedDosTime(1560526564000L)).isEqualTo(1322159234);
     TimeZone.setDefault(defaultTimeZone);
   }
 
@@ -105,7 +105,7 @@ public class Zip4jUtilTest {
   public void testDosToJavaTime() {
     TimeZone defaultTimeZone = TimeZone.getDefault();
     TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));
-    assertThat(Zip4jUtil.dosToJavaTme(1322159234)).isEqualTo((1560526564503L / 1000) * 1000);
+    assertThat(Zip4jUtil.dosToExtendedEpochTme(1322159234)).isEqualTo(1560526564000L);
     TimeZone.setDefault(defaultTimeZone);
   }
 


=====================================
src/test/resources/test-archives/cen_dir_entries_diff_order_as_local_entries.zip
=====================================
Binary files /dev/null and b/src/test/resources/test-archives/cen_dir_entries_diff_order_as_local_entries.zip differ


=====================================
src/test/resources/test-archives/strong_encrypted.zip
=====================================
Binary files /dev/null and b/src/test/resources/test-archives/strong_encrypted.zip differ



View it on GitLab: https://salsa.debian.org/java-team/zip4j/-/compare/55793df51848229d0b49d0edb0f752419521747c...5b0a20299058129db534a2bb849c27c9dc28fc46

-- 
View it on GitLab: https://salsa.debian.org/java-team/zip4j/-/compare/55793df51848229d0b49d0edb0f752419521747c...5b0a20299058129db534a2bb849c27c9dc28fc46
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20200828/9243b3e9/attachment.html>


More information about the pkg-java-commits mailing list