Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.nervousync.commons.zip.ZipFile Maven / Gradle / Ivy
/*
* Licensed to the Nervousync Studio (NSYC) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nervousync.commons.zip;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.nervousync.commons.core.Globals;
import com.nervousync.commons.core.zip.ZipConstants;
import com.nervousync.commons.core.zip.ZipOptions;
import com.nervousync.commons.io.NervousyncRandomAccessFile;
import com.nervousync.commons.zip.crypto.Decryptor;
import com.nervousync.commons.zip.crypto.impl.AESDecryptor;
import com.nervousync.commons.zip.crypto.impl.StandardDecryptor;
import com.nervousync.commons.zip.io.SplitOutputStream;
import com.nervousync.commons.zip.io.ZipOutputStream;
import com.nervousync.commons.zip.io.input.InflaterInputStream;
import com.nervousync.commons.zip.io.input.PartInputStream;
import com.nervousync.commons.zip.io.input.ZipInputStream;
import com.nervousync.commons.zip.models.AESExtraDataRecord;
import com.nervousync.commons.zip.models.ArchiveExtraDataRecord;
import com.nervousync.commons.zip.models.ExtraDataRecord;
import com.nervousync.commons.zip.models.Zip64ExtendInfo;
import com.nervousync.commons.zip.models.central.CentralDirectory;
import com.nervousync.commons.zip.models.central.DigitalSignature;
import com.nervousync.commons.zip.models.central.EndCentralDirectoryRecord;
import com.nervousync.commons.zip.models.central.Zip64EndCentralDirectoryLocator;
import com.nervousync.commons.zip.models.central.Zip64EndCentralDirectoryRecord;
import com.nervousync.commons.zip.models.header.FileHeader;
import com.nervousync.commons.zip.models.header.GeneralFileHeader;
import com.nervousync.commons.zip.models.header.LocalFileHeader;
import com.nervousync.commons.zip.models.header.utils.HeaderOperator;
import com.nervousync.exceptions.zip.ZipException;
import com.nervousync.utils.DateTimeUtils;
import com.nervousync.utils.FileUtils;
import com.nervousync.utils.RawUtils;
import com.nervousync.utils.StringUtils;
/**
* Zip File
* @author Steven Wee [email protected]
* @version $Revision: 1.0 $ $Date: Nov 28, 2017 5:01:20 PM $
*/
public final class ZipFile implements Cloneable {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* Zip file path
*/
private String filePath = null;
/**
* File name charset encoding
*/
private String fileNameCharset = null;
/**
* List of local file headers
*/
private List localFileHeaderList = null;
/**
* Record of archive extra data
* @see com.nervousync.commons.zip.models.ArchiveExtraDataRecord
*/
private ArchiveExtraDataRecord archiveExtraDataRecord = null;
/**
* Object of central directory
* @see com.nervousync.commons.zip.models.central.CentralDirectory
*/
private CentralDirectory centralDirectory = null;
/**
* Record of end central directory
* @see com.nervousync.commons.zip.models.central.EndCentralDirectoryRecord
*/
private EndCentralDirectoryRecord endCentralDirectoryRecord = null;
/**
* Locator of Zip64 end central directory
* @see com.nervousync.commons.zip.models.central.Zip64EndCentralDirectoryLocator
*/
private Zip64EndCentralDirectoryLocator zip64EndCentralDirectoryLocator = null;
/**
* Record of Zip64 end central directory
* @see com.nervousync.commons.zip.models.central.Zip64EndCentralDirectoryRecord
*/
private Zip64EndCentralDirectoryRecord zip64EndCentralDirectoryRecord = null;
/**
* Decryptor instance
* @see com.nervousync.commons.zip.crypto.Decryptor
*/
private Decryptor decryptor = null;
/**
* Archive is split file status
*/
private boolean splitArchive = Globals.DEFAULT_VALUE_BOOLEAN;
/**
* Maximum length of split item
*/
private long splitLength = Globals.DEFAULT_VALUE_LONG;
/**
* Current index of split item
*/
private int currentSplitIndex = 0;
/**
* Is Zip64 format
*/
private boolean zip64Format = Globals.DEFAULT_VALUE_BOOLEAN;
/**
* ZipFile Constructor
* @param filePath Zip file path
* @throws ZipException Zip file cannot access and read
*/
public ZipFile(String filePath) throws ZipException {
this(filePath, null);
}
/**
* ZipFile Constructor
* @param file File object
* @throws ZipException Zip file cannot access and read
*/
public ZipFile(File file) throws ZipException {
this(file == null ? null : file.getPath());
}
/**
* ZipFile Constructor
* @param file File object
* @param fileNameCharset File name charset encoding
* @throws ZipException Zip file cannot access and read
*/
public ZipFile(File file, String fileNameCharset) throws ZipException {
this(file == null ? null : file.getPath(), fileNameCharset);
}
/**
* ZipFile Constructor
* @param filePath Zip file path
* @param fileNameCharset File name charset encoding
* @throws ZipException Zip file cannot access and read
*/
public ZipFile(String filePath, String fileNameCharset) throws ZipException {
this.filePath = filePath;
this.fileNameCharset = fileNameCharset;
if (FileUtils.isExists(this.filePath)) {
if (!FileUtils.canRead(this.filePath)) {
throw new ZipException("Current file doesn't have read access!");
}
this.readHeaders();
}
}
/**
* Create zip file
* @param filePath Zip file path
* @param zipOptions Zip options
* @see com.nervousync.commons.core.zip.ZipOptions
* @param addFiles List of files in zip file
* @return ZipFile instance
* @throws ZipException If target file was exists or add files is null or empty
*/
public static ZipFile createZipFile(String filePath, ZipOptions zipOptions,
String... addFiles) throws ZipException {
return ZipFile.createZipFile(filePath, null, zipOptions,
Globals.DEFAULT_VALUE_BOOLEAN, Globals.DEFAULT_VALUE_LONG, addFiles);
}
/**
* Create zip file
* @param filePath Zip file path
* @param charsetName File name charset encoding
* @param zipOptions Zip options
* @see com.nervousync.commons.core.zip.ZipOptions
* @param addFiles List of files in zip file
* @return ZipFile instance
* @throws ZipException If target file was exists or add files is null or empty
*/
public static ZipFile createZipFile(String filePath, String charsetName, ZipOptions zipOptions,
String... addFiles) throws ZipException {
return ZipFile.createZipFile(filePath, charsetName, zipOptions,
Globals.DEFAULT_VALUE_BOOLEAN, Globals.DEFAULT_VALUE_LONG, addFiles);
}
/**
* Create a split archive zip file
* @param filePath Zip file path
* @param zipOptions Zip options
* @see com.nervousync.commons.core.zip.ZipOptions
* @param splitArchive Status of split archive
* @param splitLength Maximum size of split file
* @param addFiles List of files in zip file
* @return ZipFile instance
* @throws ZipException If target file was exists or add files is null or empty
*/
public static ZipFile createZipFile(String filePath, ZipOptions zipOptions,
boolean splitArchive, long splitLength, String... addFiles) throws ZipException {
return ZipFile.createZipFile(filePath, null, zipOptions, splitArchive, splitLength, addFiles);
}
/**
* Create a split archive zip file
* @param filePath Zip file path
* @param charsetName File name charset encoding
* @param zipOptions Zip options
* @see com.nervousync.commons.core.zip.ZipOptions
* @param splitArchive Status of split archive
* @param splitLength Maximum size of split file
* @param addFiles List of files in zip file
* @return ZipFile instance
* @throws ZipException If target file was exists or add files is null or empty
*/
public static ZipFile createZipFile(String filePath, String charsetName, ZipOptions zipOptions,
boolean splitArchive, long splitLength, String... addFiles) throws ZipException {
if (!StringUtils.isNotNullAndNotEmpty(filePath)) {
throw new ZipException("zip file path is empty");
}
if (FileUtils.isExists(filePath)) {
throw new ZipException("zip file: " + filePath
+ " already exists. To add files to existing zip file use addFile method");
}
if (addFiles == null || addFiles.length == 0) {
throw new ZipException("Zip file entity is null");
}
ZipFile zipFile = new ZipFile(filePath, charsetName);
zipFile.createZipFile(Arrays.asList(addFiles), zipOptions, splitArchive, splitLength);
return zipFile;
}
/**
* Create zip file and add folder to zip file
* @param filePath Zip file path
* @param zipOptions Zip options
* @see com.nervousync.commons.core.zip.ZipOptions
* @param folderPath Folder will add to zip file
* @return ZipFile instance
* @throws ZipException If target file was exists or folder is empty
*/
public static ZipFile createZipFileFromFolder(String filePath, ZipOptions zipOptions,
String folderPath) throws ZipException {
return ZipFile.createZipFileFromFolder(filePath, null, zipOptions,
Globals.DEFAULT_VALUE_BOOLEAN, Globals.DEFAULT_VALUE_LONG, folderPath);
}
/**
* Create zip file and add folder to zip file
* @param filePath Zip file path
* @param charsetName File name charset encoding
* @param zipOptions Zip options
* @see com.nervousync.commons.core.zip.ZipOptions
* @param folderPath Folder will add to zip file
* @return ZipFile instance
* @throws ZipException If target file was exists or folder is empty
*/
public static ZipFile createZipFileFromFolder(String filePath, String charsetName, ZipOptions zipOptions,
String folderPath) throws ZipException {
return ZipFile.createZipFileFromFolder(filePath, charsetName, zipOptions,
Globals.DEFAULT_VALUE_BOOLEAN, Globals.DEFAULT_VALUE_LONG, folderPath);
}
/**
* Create zip file and add folder to zip file
* @param filePath Zip file path
* @param zipOptions Zip options
* @see com.nervousync.commons.core.zip.ZipOptions
* @param splitArchive Status of split archive
* @param splitLength Maximum size of split file
* @param folderPath Folder will add to zip file
* @return ZipFile instance
* @throws ZipException If target file was exists or folder is empty
*/
public static ZipFile createZipFileFromFolder(String filePath, ZipOptions zipOptions,
boolean splitArchive, long splitLength, String folderPath) throws ZipException {
return ZipFile.createZipFileFromFolder(filePath, null, zipOptions, splitArchive, splitLength, folderPath);
}
/**
* Create zip file and add folder to zip file
* @param filePath Zip file path
* @param charsetName File name charset encoding
* @param zipOptions Zip options
* @see com.nervousync.commons.core.zip.ZipOptions
* @param splitArchive Status of split archive
* @param splitLength Maximum size of split file
* @param folderPath Folder will add to zip file
* @return ZipFile instance
* @throws ZipException If target file was exists or folder is empty
*/
public static ZipFile createZipFileFromFolder(String filePath, String charsetName, ZipOptions zipOptions,
boolean splitArchive, long splitLength, String folderPath) throws ZipException {
if (!StringUtils.isNotNullAndNotEmpty(filePath)) {
throw new ZipException("zip file path is empty");
}
if (FileUtils.isExists(filePath)) {
throw new ZipException("zip file: " + filePath
+ " already exists. To add files to existing zip file use addFile method");
}
if (!StringUtils.isNotNullAndNotEmpty(folderPath)) {
throw new ZipException("Zip file entity is null");
}
ZipFile zipFile = new ZipFile(filePath, charsetName);
zipFile.addFolder(folderPath, zipOptions, Globals.DEFAULT_VALUE_BOOLEAN);
if (zipOptions.getPassword() != null) {
zipFile.setPassword(zipOptions.getPassword());
}
return zipFile;
}
/**
* Generate entity path
* @param file Which file path will add to zip file
* @param rootFolderInZip prefix path of zip file
* @param rootFolderPath root path of folder
* @return Generated entry path
* @throws ZipException given file is null
*/
public static String getRelativeFileName(String file, String rootFolderInZip, String rootFolderPath) throws ZipException {
if (!StringUtils.isNotNullAndNotEmpty(file)) {
throw new ZipException("input file path/name is empty, cannot calculate relative file name");
}
String fileName = null;
if (StringUtils.isNotNullAndNotEmpty(rootFolderPath)) {
File rootFolderFile = new File(rootFolderPath);
String rootFolderFileRef = rootFolderFile.getPath();
if (!rootFolderFileRef.endsWith(Globals.DEFAULT_PAGE_SEPARATOR)) {
rootFolderFileRef += Globals.DEFAULT_PAGE_SEPARATOR;
}
String tmpFileName = file.substring(rootFolderFileRef.length());
if (tmpFileName.startsWith(Globals.DEFAULT_PAGE_SEPARATOR)) {
tmpFileName = tmpFileName.substring(1);
}
File tmpFile = new File(file);
if (tmpFile.isDirectory()) {
tmpFileName = StringUtils.replace(tmpFileName, Globals.DEFAULT_PAGE_SEPARATOR,
ZipConstants.ZIP_FILE_SEPARATOR);
if (!tmpFileName.endsWith(ZipConstants.ZIP_FILE_SEPARATOR)) {
tmpFileName += ZipConstants.ZIP_FILE_SEPARATOR;
}
} else {
String bkFileName = tmpFileName.substring(0, tmpFileName.lastIndexOf(tmpFile.getName()));
bkFileName = StringUtils.replace(bkFileName, Globals.DEFAULT_PAGE_SEPARATOR,
ZipConstants.ZIP_FILE_SEPARATOR);
tmpFileName = bkFileName + tmpFile.getName();
}
fileName = tmpFileName;
} else {
File relFile = new File(file);
if (relFile.isDirectory()) {
fileName = relFile.getName() + ZipConstants.ZIP_FILE_SEPARATOR;
} else {
fileName = getFileNameFromFilePath(relFile);
}
}
if (!StringUtils.isNotNullAndNotEmpty(rootFolderInZip)) {
fileName = rootFolderInZip + fileName;
}
if (!StringUtils.isNotNullAndNotEmpty(fileName)) {
throw new ZipException("Error determining file name");
}
return fileName;
}
/**
* Get entry path list
* @return entry path list
*/
public List entryList() {
List entryList = new ArrayList();
for (GeneralFileHeader generalFileHeader : this.centralDirectory.getFileHeaders()) {
entryList.add(generalFileHeader.getEntryPath());
}
return entryList;
}
/**
* Check the given entry path is extsis
* @param entryPath entry path
* @return check result
*/
public boolean isEntryExists(String entryPath) {
for (GeneralFileHeader generalFileHeader : this.centralDirectory.getFileHeaders()) {
if (generalFileHeader.getEntryPath().equals(entryPath)) {
return true;
}
}
return Globals.DEFAULT_VALUE_BOOLEAN;
}
/**
* Read entry datas
* @param entryPath Check entry path
* @return entry datas as byte arrays
* @throws ZipException file list is empty or zipOptions is null
*/
public byte[] readEntry(String entryPath) throws ZipException {
if (FileUtils.isExists(this.filePath) && this.splitArchive) {
throw new ZipException("This is a split archive. Zip file format does not allow updating split/spanned files");
}
return this.readEntry(this.retrieveGeneralFileHeader(entryPath));
}
/**
* Add file to zip file
* @param file Target file will add to zip file
* @throws ZipException file list is empty or zipOptions is null
*/
public void addFile(File file) throws ZipException {
this.addFile(file, ZipOptions.newOptions());
}
/**
* Add file to zip file with zip options
* @param file Target file will add to zip file
* @param zipOptions Zip options
* @see com.nervousync.commons.core.zip.ZipOptions
* @throws ZipException file list is empty or zipOptions is null
*/
public void addFile(File file, ZipOptions zipOptions) throws ZipException {
this.addFiles(Arrays.asList(file.getAbsolutePath()), zipOptions);
}
/**
* Add files to zip file
* @param fileList Target files will add to zip file
* @throws ZipException file list is empty or zipOptions is null
*/
public void addFiles(List fileList) throws ZipException {
this.addFiles(fileList, ZipOptions.newOptions());
}
/**
* Add files to zip file with zip options
* @param fileList Target files will add to zip file
* @param zipOptions Zip options
* @see com.nervousync.commons.core.zip.ZipOptions
* @throws ZipException file list is empty or zipOptions is null
*/
public void addFiles(List fileList, ZipOptions zipOptions) throws ZipException {
if (fileList == null || fileList.isEmpty()) {
throw new ZipException("Input file array list is null!");
}
if (zipOptions == null) {
throw new ZipException("Zip options is null!");
}
if (FileUtils.isExists(this.filePath) && this.splitArchive) {
throw new ZipException("This is a split archive. Zip file format does not allow updating split/spanned files");
}
this.addFilesToZip(fileList, zipOptions);
}
/**
* Add InputStream to zip file
* @param inputStream Entity input stream
* @throws ZipException Input stream is null
*/
public void addStream(InputStream inputStream) throws ZipException {
this.addStream(inputStream, ZipOptions.newOptions());
}
/**
* Add InputStream to zip file with zip options
* @param inputStream Entity input stream
* @param zipOptions Zip options
* @see com.nervousync.commons.core.zip.ZipOptions
* @throws ZipException input stream is null or zipOptions is null
*/
public void addStream(InputStream inputStream, ZipOptions zipOptions) throws ZipException {
if (inputStream == null) {
throw new ZipException("Input stream is null! ");
}
if (zipOptions == null) {
throw new ZipException("Zip options is null!");
}
if (FileUtils.isExists(this.filePath) && this.splitArchive) {
throw new ZipException("This is a split archive. Zip file format does not allow updating split/spanned files");
}
this.addStreamToZip(inputStream, zipOptions);
}
/**
* Add folder to zip file
* @param folderPath Target folder path will add to zip file
* @throws ZipException folder path is null or folder was not exists
*/
public void addFolder(String folderPath) throws ZipException {
this.addFolder(folderPath, ZipOptions.newOptions(), true);
}
/**
* Add folder to zip file with zip options
* @param folderPath Target folder path will add to zip file
* @param zipOptions Zip options
* @see com.nervousync.commons.core.zip.ZipOptions
* @throws ZipException folder path is null or folder was not exists or zipOptions is null
*/
public void addFolder(String folderPath, ZipOptions zipOptions) throws ZipException {
this.addFolder(folderPath, zipOptions, true);
}
/**
* Entract all entries in zip file to target extract file path
* @param destPath Target extract file path
* @throws ZipException Target path is null or file exists
*/
public void extractAll(String destPath) throws ZipException {
this.extractAll(destPath, Globals.DEFAULT_VALUE_BOOLEAN);
}
/**
* Entract all entries in zip file to target extract file path
* @param destPath Target extract file path
* @param ignoreFileAttr Status of process file attribute
* @throws ZipException Target path is null or zip file invalid
*/
public void extractAll(String destPath, boolean ignoreFileAttr) throws ZipException {
if (!StringUtils.isNotNullAndNotEmpty(destPath)) {
throw new ZipException("Destination path is null!");
}
if (this.centralDirectory == null || this.centralDirectory.getFileHeaders() == null) {
throw new ZipException("Invalid central directory in zip entity");
}
for (GeneralFileHeader generalFileHeader : this.centralDirectory.getFileHeaders()) {
this.extractFile(generalFileHeader, destPath, ignoreFileAttr);
}
}
/**
* Extract entry path file to target extra file path
* @param entryPath Which entry path will extract
* @param destPath Target extract file path
* @throws ZipException Target path is null or entry path is null/not exists or zip file invalid
*/
public void extractFile(String entryPath, String destPath) throws ZipException {
this.extractFile(entryPath, destPath, Globals.DEFAULT_VALUE_BOOLEAN);
}
/**
* Extract entry path file to target extra file path
* @param entryPath Which entry path will extract
* @param destPath Target extract file path
* @param ignoreFileAttr Status of process file attribute
* @throws ZipException Target path is null or entry path is null/not exists or zip file invalid
*/
public void extractFile(String entryPath, String destPath, boolean ignoreFileAttr) throws ZipException {
if (!StringUtils.isNotNullAndNotEmpty(entryPath)) {
throw new ZipException("extract file name is null!");
}
if (!StringUtils.isNotNullAndNotEmpty(destPath)) {
throw new ZipException("Destination path is null!");
}
this.extractFile(this.retrieveGeneralFileHeader(entryPath), destPath, ignoreFileAttr);
}
/**
* Remove entry folder from zip file
* @param folderPath Which entry folder will removed
* @throws ZipException Given path was not a directory
*/
public void removeFolder(String folderPath) throws ZipException {
if (this.isDirectory(folderPath)) {
this.removeFilesIfExists(this.listFolderGeneralFileHeaders(folderPath));
}
throw new ZipException("Entry path: " + folderPath + " is not directory entry!");
}
/**
* Remove entry path from zip file
* @param entryPath Which entry path will removed
* @throws ZipException given entry path is null or zip file was not exists
*/
public void removeExistsEntry(String entryPath) throws ZipException {
this.removeExistsEntries(new String[]{entryPath});
}
/**
* Remove entry paths from zip file
* @param existsEntries Which entry paths will removed
* @throws ZipException given entry path is null or zip file was not exists
*/
public void removeExistsEntries(String... existsEntries) throws ZipException {
if (existsEntries == null) {
throw new ZipException("Input entry path is null!");
}
if (FileUtils.isExists(this.filePath) && this.splitArchive) {
throw new ZipException("This is a split archive. Zip file format does not allow updating split/spanned files");
}
this.removeFilesIfExists(Arrays.asList(existsEntries));
if (this.isNoEntry()) {
FileUtils.removeFile(this.filePath);
}
}
/**
* Setting password
* @param password password
* @throws ZipException given password is null
*/
public void setPassword(String password) throws ZipException {
if (!StringUtils.isNotNullAndNotEmpty(password)) {
throw new ZipException("Password is null");
}
this.setPassword(password.toCharArray());
}
/**
* Setting password
* @param password password char arrays
* @throws ZipException given password is null
*/
public void setPassword(char[] password) throws ZipException {
if (this.centralDirectory == null
|| this.centralDirectory.getFileHeaders() == null) {
throw new ZipException("Invalid zip file");
}
for (int i = 0 ; i < this.centralDirectory.getFileHeaders().size() ; i++) {
if (this.centralDirectory.getFileHeaders().get(i) != null
&& this.centralDirectory.getFileHeaders().get(i).isEncrypted()) {
this.centralDirectory.getFileHeaders().get(i).setPassword(password);
}
}
}
/**
* Setting comment
* @param comment comment information
* @throws ZipException comment is null or zip file was not exists
*/
public void setComment(String comment) throws ZipException {
if (comment == null) {
throw new ZipException("input comment is null, cannot update zip file");
}
if (!FileUtils.isExists(this.filePath)) {
throw new ZipException("zip file does not exist, cannot set comment for zip file");
}
if (this.endCentralDirectoryRecord == null) {
throw new ZipException("end of central directory is null, cannot set comment");
}
String encodedComment = null;
byte[] commentBytes = null;
int commentLength = Globals.DEFAULT_VALUE_INT;
if (StringUtils.supportedCharset(ZipConstants.CHARSET_COMMENTS_DEFAULT)) {
try {
encodedComment = new String(comment.getBytes(ZipConstants.CHARSET_COMMENTS_DEFAULT), ZipConstants.CHARSET_COMMENTS_DEFAULT);
commentBytes = encodedComment.getBytes(ZipConstants.CHARSET_COMMENTS_DEFAULT);
} catch (UnsupportedEncodingException e) {
encodedComment = comment;
}
} else {
encodedComment = comment;
}
if (commentBytes == null) {
commentBytes = encodedComment.getBytes();
}
commentLength = commentBytes.length;
if (commentLength > ZipConstants.MAX_ALLOWED_ZIP_COMMENT_LENGTH) {
throw new ZipException("comment length exceeds maximum length");
}
this.endCentralDirectoryRecord.setCommentBytes(commentBytes);
this.endCentralDirectoryRecord.setCommentLength(commentLength);
SplitOutputStream outputStream = null;
try {
outputStream = new SplitOutputStream(this.filePath);
if (this.zip64Format) {
outputStream.seek(this.zip64EndCentralDirectoryRecord.getOffsetStartCenDirWRTStartDiskNo());
} else {
outputStream.seek(this.endCentralDirectoryRecord.getOffsetOfStartOfCentralDirectory());
}
this.finalizeZipFileWithoutValidations(outputStream);
} catch (FileNotFoundException e) {
throw new ZipException(e);
} catch (IOException e) {
throw new ZipException(e);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
// Ignore
}
}
}
}
/**
* Read comment
* @return Readed comment content
* @throws ZipException zip file was not exists
*/
public String getComment() throws ZipException {
return this.getComment(null);
}
/**
* Read comment by given charset encoding
* @param charset charset encoding
* @return Readed comment content
* @throws ZipException zip file was not exists, zip file does not include comment content or charset encoding was not supported
*/
public String getComment(String charset) throws ZipException {
if (charset == null) {
if (StringUtils.supportedCharset(ZipConstants.CHARSET_COMMENTS_DEFAULT)) {
charset = ZipConstants.CHARSET_COMMENTS_DEFAULT;
} else {
charset = Globals.DEFAULT_SYSTEM_CHARSET;
}
}
if (!FileUtils.isExists(this.filePath)) {
throw new ZipException("Zip file does not exists!");
}
if (this.endCentralDirectoryRecord == null) {
throw new ZipException("end of central directory is null, cannot set comment");
}
if (this.endCentralDirectoryRecord.getCommentBytes() == null
|| this.endCentralDirectoryRecord.getCommentBytes().length == 0) {
return null;
}
try {
return new String(this.endCentralDirectoryRecord.getCommentBytes(), charset);
} catch (UnsupportedEncodingException e) {
throw new ZipException(e);
}
}
/**
* Merge split files and write merge file to target output path
* @param outputPath Merge file output path
* @throws ZipException Zip file was not a split file or zip file invalid
*/
public void mergeSplitFile(String outputPath) throws ZipException {
if (!this.splitArchive) {
throw new ZipException("archive not a split zip file");
}
if (this.endCentralDirectoryRecord.getIndexOfThisDisk() <= 0) {
throw new ZipException("corrupt zip entity, archive not a split zip file");
}
OutputStream outputStream = null;
NervousyncRandomAccessFile input = null;
List sizeList = new ArrayList();
long totalWriteBytes = 0L;
boolean removeSplitSig = Globals.DEFAULT_VALUE_BOOLEAN;
try {
outputStream = this.openMergeOutputStream(outputPath);
for (int i = 0 ; i <= this.endCentralDirectoryRecord.getIndexOfThisDisk() ; i++) {
input = this.openSplitFile(i);
int start = 0;
if (i == 0) {
if (this.centralDirectory != null
&& this.centralDirectory.getFileHeaders() != null
&& this.centralDirectory.getFileHeaders().size() > 0) {
byte[] buffer = new byte[4];
input.seek(0L);
input.read(buffer);
if (RawUtils.readIntFromLittleEndian(buffer, 0) == ZipConstants.SPLITSIG) {
start = 4;
removeSplitSig = true;
}
}
}
long end = input.length();
if (i == this.endCentralDirectoryRecord.getIndexOfThisDisk()) {
end = this.endCentralDirectoryRecord.getOffsetOfStartOfCentralDirectory();
}
this.copyFile(input, outputStream, start, end);
totalWriteBytes += (end - start);
sizeList.add(end);
}
ZipFile newFile = (ZipFile)this.clone();
newFile.endCentralDirectoryRecord.setOffsetOfStartOfCentralDirectory(totalWriteBytes);
newFile.updateSplitZipEntity(sizeList, removeSplitSig);
newFile.finalizeZipFileWithoutValidations(outputStream);
} catch (Exception e) {
if (e instanceof ZipException) {
throw (ZipException)e;
} else {
throw new ZipException(e);
}
} finally {
try {
if (input != null) {
input.close();
}
} catch (IOException e) {
}
}
}
/**
* Finallize zip file
* @param outputStream Output stream
* @throws ZipException Write datas to output stream error
*/
public void finalizeZipFile(OutputStream outputStream) throws ZipException {
if (outputStream == null) {
throw new ZipException("input parameters is null, cannot finalize zip file");
}
this.processHeaderData(outputStream);
long offsetCentralDirectory = this.endCentralDirectoryRecord.getOffsetOfStartOfCentralDirectory();
List headerBytesList = new ArrayList();
int sizeOfCentralDirectory = this.writeCentralDirectory(outputStream, headerBytesList);
if (this.zip64Format) {
if (this.zip64EndCentralDirectoryRecord == null) {
this.zip64EndCentralDirectoryRecord = new Zip64EndCentralDirectoryRecord();
}
if (this.zip64EndCentralDirectoryLocator == null) {
this.zip64EndCentralDirectoryLocator = new Zip64EndCentralDirectoryLocator();
}
this.zip64EndCentralDirectoryLocator
.setOffsetZip64EndOfCentralDirectoryRecord(offsetCentralDirectory + sizeOfCentralDirectory);
if (outputStream instanceof SplitOutputStream) {
this.zip64EndCentralDirectoryLocator.setIndexOfZip64EndOfCentralDirectoryRecord(
((SplitOutputStream) outputStream).getCurrentSplitFileIndex());
this.zip64EndCentralDirectoryLocator
.setTotalNumberOfDiscs(((SplitOutputStream) outputStream).getCurrentSplitFileIndex() + 1);
} else {
this.zip64EndCentralDirectoryLocator.setIndexOfZip64EndOfCentralDirectoryRecord(0);
this.zip64EndCentralDirectoryLocator.setTotalNumberOfDiscs(1);
}
this.writeZip64EndOfCentralDirectoryRecord(outputStream, sizeOfCentralDirectory,
offsetCentralDirectory, headerBytesList);
this.writeZip64EndOfCentralDirectoryLocator(outputStream, headerBytesList);
}
this.writeEndOfCentralDirectoryRecord(outputStream, sizeOfCentralDirectory, offsetCentralDirectory, headerBytesList);
this.writeZipHeaderBytes(outputStream, HeaderOperator.convertByteArrayListToByteArray(headerBytesList));
}
/**
* Open new split file
* @return NervousyncRandomAccessFile instance
* @throws IOException Read next split file error
* @throws ZipException Can't found next split file
*/
public NervousyncRandomAccessFile startNextSplitFile() throws IOException, ZipException {
String currentSplitFile = null;
if (this.currentSplitIndex == this.endCentralDirectoryRecord.getIndexOfThisDisk()) {
currentSplitFile = this.filePath;
} else {
currentSplitFile = this.filePath.substring(0, this.filePath.lastIndexOf('.'));
if (this.currentSplitIndex < 9) {
currentSplitFile += (".zip.0" + (this.currentSplitIndex + 1));
} else {
currentSplitFile += (".zip." + (this.currentSplitIndex + 1));
}
}
this.currentSplitIndex++;
if (currentSplitFile == null || !FileUtils.isExists(currentSplitFile)) {
throw new ZipException("Next split file not found!");
}
return new NervousyncRandomAccessFile(currentSplitFile, Globals.WRITE_MODE);
}
/**
* @return the centralDirectory
*/
public CentralDirectory getCentralDirectory() {
return centralDirectory;
}
/**
* @param centralDirectory the centralDirectory to set
*/
public void setCentralDirectory(CentralDirectory centralDirectory) {
this.centralDirectory = centralDirectory;
}
/**
* @return the fileNameCharset
*/
public String getFileNameCharset() {
return fileNameCharset;
}
/**
* @return the localFileHeaderList
*/
public List getLocalFileHeaderList() {
return localFileHeaderList;
}
/**
* @param localFileHeaderList the localFileHeaderList to set
*/
public void setLocalFileHeaderList(List localFileHeaderList) {
this.localFileHeaderList = localFileHeaderList;
}
/**
* @return the archiveExtraDataRecord
*/
public ArchiveExtraDataRecord getArchiveExtraDataRecord() {
return archiveExtraDataRecord;
}
/**
* @param archiveExtraDataRecord the archiveExtraDataRecord to set
*/
public void setArchiveExtraDataRecord(ArchiveExtraDataRecord archiveExtraDataRecord) {
this.archiveExtraDataRecord = archiveExtraDataRecord;
}
/**
* @return the endCentralDirectoryRecord
*/
public EndCentralDirectoryRecord getEndCentralDirectoryRecord() {
return endCentralDirectoryRecord;
}
/**
* @param endCentralDirectoryRecord the endCentralDirectoryRecord to set
*/
public void setEndCentralDirectoryRecord(EndCentralDirectoryRecord endCentralDirectoryRecord) {
this.endCentralDirectoryRecord = endCentralDirectoryRecord;
}
public boolean isSplitArchive() throws ZipException {
return this.splitArchive;
}
/**
* @param splitArchive the splitArchive to set
*/
public void setSplitArchive(boolean splitArchive) {
this.splitArchive = splitArchive;
}
/**
* @param splitLength the splitLength to set
*/
public void setSplitLength(long splitLength) {
this.splitLength = splitLength;
}
private void createZipFile(List fileList, ZipOptions zipOptions,
boolean splitArchive, long splitLength) throws ZipException {
if (!StringUtils.isNotNullAndNotEmpty(this.filePath)) {
throw new ZipException("zip file path is empty");
}
if (FileUtils.isExists(this.filePath)) {
throw new ZipException("zip file: " + this.filePath
+ " already exists. To add files to existing zip file use addFile method");
}
if (fileList == null || fileList.size() == 0) {
throw new ZipException("Zip file entity is null");
}
this.splitArchive = splitArchive;
this.splitLength = splitLength;
this.addFiles(fileList, zipOptions);
}
private void addFolder(String folderPath, ZipOptions zipOptions, boolean checkSplitArchive) throws ZipException {
if (folderPath == null) {
throw new ZipException("Input folder path is null! ");
}
if (zipOptions == null) {
throw new ZipException("Zip options is null!");
}
if (checkSplitArchive && this.splitArchive) {
throw new ZipException("This is a split archive. Zip file format does not allow updating split/spanned files");
}
this.addFolderToZip(folderPath, zipOptions);
}
private static String getFileNameFromFilePath(File file) throws ZipException {
if (file == null) {
throw new ZipException("input file is null, cannot get file name");
}
if (file.isDirectory()) {
return null;
}
return file.getName();
}
private boolean isNoEntry() {
return this.centralDirectory.getFileHeaders().size() == 0;
}
private boolean isDirectory(String entryPath) {
GeneralFileHeader generalFileHeader = this.retrieveGeneralFileHeader(entryPath);
if (generalFileHeader != null) {
return generalFileHeader.isDirectory();
}
return Globals.DEFAULT_VALUE_BOOLEAN;
}
private List listFolderGeneralFileHeaders(String folderPath) {
if (StringUtils.isNotNullAndNotEmpty(folderPath)) {
if (this.centralDirectory == null) {
throw new ZipException("central directory is null, cannot determine file header with exact match for entry path: " + folderPath);
}
return this.centralDirectory.listFolderGeneralFileHeaders(folderPath);
}
throw new ZipException("file name is null, cannot determine file header for entry path: " + folderPath);
}
private GeneralFileHeader retrieveGeneralFileHeader(String entryPath) throws ZipException {
if (StringUtils.isNotNullAndNotEmpty(entryPath)) {
if (this.centralDirectory == null) {
throw new ZipException("central directory is null, cannot determine file header with exact match for entry path: " + entryPath);
}
return this.centralDirectory.retrieveGeneralFileHeader(entryPath);
}
throw new ZipException("file name is null, cannot determine file header for entry path: " + entryPath);
}
private void removeFilesIfExists(List entryList) throws ZipException {
if (this.centralDirectory == null
|| this.centralDirectory.getFileHeaders() == null
|| this.centralDirectory.getFileHeaders().size() == 0) {
// This file is new zip file
return;
}
for (String entryPath : entryList) {
GeneralFileHeader generalFileHeader = this.retrieveGeneralFileHeader(entryPath);
if (generalFileHeader != null) {
this.removeExistsFile(generalFileHeader);
}
}
}
private void addStreamToZip(InputStream inputStream, ZipOptions zipOptions) throws ZipException {
if (zipOptions == null) {
throw new ZipException("Zip options is null!");
}
if (inputStream == null) {
throw new ZipException("No data to added");
}
ZipOutputStream outputStream = null;
try {
this.checkOptions(zipOptions);
SplitOutputStream splitOutputStream = new SplitOutputStream(this.filePath, this.splitLength);
outputStream = new ZipOutputStream(splitOutputStream, this);
if (FileUtils.isExists(this.filePath)) {
if (this.endCentralDirectoryRecord == null) {
throw new ZipException("invalid end of central directory record");
}
splitOutputStream.seek(this.endCentralDirectoryRecord.getOffsetOfStartOfCentralDirectory());
}
byte[] readBuffer = new byte[Globals.DEFAULT_BUFFER_SIZE];
int readLength = Globals.DEFAULT_VALUE_INT;
outputStream.putNextEntry(null, zipOptions);
if (!zipOptions.getFileNameInZip().endsWith(ZipConstants.ZIP_FILE_SEPARATOR)
&& !zipOptions.getFileNameInZip().endsWith(Globals.DEFAULT_PAGE_SEPARATOR)) {
while ((readLength = inputStream.read(readBuffer)) != Globals.DEFAULT_VALUE_INT) {
outputStream.write(readBuffer, 0, readLength);
}
}
outputStream.closeEntry();
outputStream.finish();
} catch (Exception e) {
if (e instanceof ZipException) {
throw (ZipException)e;
} else {
throw new ZipException(e);
}
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
// Ignore
}
}
}
}
private void addFolderToZip(String folderPath, ZipOptions zipOptions) throws ZipException {
if (folderPath == null || !FileUtils.isExists(folderPath)) {
throw new ZipException("No folder to added!");
}
if (!FileUtils.isDirectory(folderPath)) {
throw new ZipException("Given path is not folder path");
}
if (!FileUtils.canRead(folderPath)) {
throw new ZipException("Cannot read folder: " + folderPath);
}
if (zipOptions == null) {
throw new ZipException("Zip options is null!");
}
String rootFolderPath = null;
if (zipOptions.isIncludeRootFolder()) {
try {
File file = FileUtils.getFile(folderPath);
if (file.getAbsoluteFile() != null) {
rootFolderPath = file.getAbsoluteFile().getParentFile() != null ? file.getAbsoluteFile().getParentFile().getAbsolutePath() : "";
} else {
rootFolderPath = file.getAbsolutePath();
}
} catch (FileNotFoundException e) {
throw new ZipException("Cannot read folder: " + folderPath);
}
} else {
rootFolderPath = folderPath;
}
zipOptions.setDefaultFolderPath(rootFolderPath);
List fileList = new ArrayList();
try {
File folder = FileUtils.getFile(folderPath);
if (zipOptions.isIncludeRootFolder()) {
fileList.add(folderPath);
}
fileList.addAll(FileUtils.listFiles(folder, zipOptions.isReadHiddenFiles(), zipOptions.isIncludeRootFolder()));
} catch (Exception e) {
throw new ZipException(e);
}
this.addFiles(fileList, zipOptions);
}
private void finalizeZipFileWithoutValidations(OutputStream outputStream)
throws ZipException {
if (outputStream == null) {
throw new ZipException("Output stream parameter is null, cannot finalize zip file");
}
try {
List headerBytesList = new ArrayList();
long offsetCentralDirectory = this.endCentralDirectoryRecord.getOffsetOfStartOfCentralDirectory();
int sizeOfCentralDirectory = this.writeCentralDirectory(outputStream, headerBytesList);
if (this.zip64Format) {
if (this.zip64EndCentralDirectoryRecord == null) {
this.zip64EndCentralDirectoryRecord = new Zip64EndCentralDirectoryRecord();
}
if (this.zip64EndCentralDirectoryLocator == null) {
this.zip64EndCentralDirectoryLocator = new Zip64EndCentralDirectoryLocator();
}
this.zip64EndCentralDirectoryLocator
.setOffsetZip64EndOfCentralDirectoryRecord(offsetCentralDirectory + sizeOfCentralDirectory);
this.writeZip64EndOfCentralDirectoryRecord(outputStream, sizeOfCentralDirectory,
offsetCentralDirectory, headerBytesList);
this.writeZip64EndOfCentralDirectoryLocator(outputStream, headerBytesList);
}
this.writeEndOfCentralDirectoryRecord(outputStream, sizeOfCentralDirectory, offsetCentralDirectory,
headerBytesList);
this.writeZipHeaderBytes(outputStream, HeaderOperator.convertByteArrayListToByteArray(headerBytesList));
} catch (Exception e) {
if (e instanceof ZipException) {
throw (ZipException) e;
} else {
throw new ZipException(e);
}
}
}
private void extractFile(GeneralFileHeader generalFileHeader, String destPath,
boolean ignoreFileAttr) throws ZipException {
if (generalFileHeader == null) {
throw new ZipException("General file header is null!");
}
try {
if (!destPath.endsWith(Globals.DEFAULT_PAGE_SEPARATOR)) {
destPath += Globals.DEFAULT_PAGE_SEPARATOR;
}
if (generalFileHeader.isDirectory()) {
if (!FileUtils.makeDir(destPath + generalFileHeader.getEntryPath())) {
throw new ZipException("Create output folder error!");
}
} else {
if (!FileUtils.isExists(destPath)) {
FileUtils.makeDir(destPath);
}
if (!FileUtils.isDirectory(destPath)) {
throw new ZipException("Output folder is not exists");
}
this.extractFileToPath(generalFileHeader, destPath, ignoreFileAttr);
}
} catch (Exception e) {
if (e instanceof ZipException) {
throw (ZipException)e;
} else {
throw new ZipException(e);
}
}
}
private void addFilesToZip(List fileList, ZipOptions zipOptions) throws ZipException {
if (fileList == null || fileList.isEmpty()) {
throw new ZipException("No file to added");
}
if (!FileUtils.canWrite(this.filePath)) {
throw new ZipException("Zip file cannot writeable");
}
if (this.endCentralDirectoryRecord == null) {
this.endCentralDirectoryRecord = new EndCentralDirectoryRecord();
this.endCentralDirectoryRecord.setSignature(ZipConstants.ENDSIG);
this.endCentralDirectoryRecord.setIndexOfThisDisk(0);
this.endCentralDirectoryRecord.setTotalOfEntriesInCentralDirectory(0);
this.endCentralDirectoryRecord.setTotalOfEntriesInCentralDirectoryOnThisDisk(0);
this.endCentralDirectoryRecord.setOffsetOfStartOfCentralDirectory(0);
}
ZipOutputStream outputStream = null;
InputStream inputStream = null;
try {
this.checkOptions(zipOptions);
List entryList = new ArrayList();
for (String filePath : fileList) {
entryList.add(ZipFile.getRelativeFileName(filePath,
zipOptions.getRootFolderInZip(), zipOptions.getDefaultFolderPath()));
}
this.removeFilesIfExists(entryList);
SplitOutputStream splitOutputStream = new SplitOutputStream(this.filePath, this.splitLength);
outputStream = new ZipOutputStream(splitOutputStream, this);
if (FileUtils.isExists(this.filePath)) {
if (this.endCentralDirectoryRecord == null) {
throw new ZipException("invalid end of central directory record");
}
splitOutputStream.seek(this.endCentralDirectoryRecord.getOffsetOfStartOfCentralDirectory());
}
byte[] readBuffer = new byte[ZipConstants.BUFFER_SIZE];
int readLength = Globals.DEFAULT_VALUE_INT;
for (String filePath : fileList) {
ZipOptions fileOptions = (ZipOptions)zipOptions.clone();
if (!FileUtils.isDirectory(filePath)) {
if (fileOptions.isEncryptFiles()
&& fileOptions.getEncryptionMethod() == ZipConstants.ENC_METHOD_STANDARD) {
fileOptions.setSourceFileCRC(FileUtils.calcFileCRC(filePath));
}
if (FileUtils.getFileSize(filePath) == 0L) {
fileOptions.setCompressionMethod(ZipConstants.COMP_STORE);
}
}
outputStream.putNextEntry(FileUtils.getFile(filePath), fileOptions);
if (FileUtils.isDirectory(filePath)) {
outputStream.closeEntry();
continue;
}
inputStream = FileUtils.loadFile(filePath);
if (inputStream == null) {
throw new ZipException("Load file error!");
}
while ((readLength = inputStream.read(readBuffer)) != Globals.DEFAULT_VALUE_INT) {
outputStream.write(readBuffer, 0, readLength);
}
outputStream.closeEntry();
inputStream.close();
}
outputStream.finish();
} catch (Exception e) {
if (e instanceof ZipException) {
throw (ZipException)e;
} else {
throw new ZipException(e);
}
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
}
}
}
}
private void checkOptions(ZipOptions zipOptions) throws ZipException {
if (zipOptions == null) {
throw new ZipException("Zip options is null!");
}
if (zipOptions.getCompressionMethod() != ZipConstants.COMP_STORE
&& zipOptions.getCompressionMethod() != ZipConstants.COMP_DEFLATE) {
throw new ZipException("Unsupported compression type!");
}
if (zipOptions.getCompressionMethod() == ZipConstants.COMP_DEFLATE
&& (zipOptions.getCompressionLevel() < 0 && zipOptions.getCompressionLevel() > 9)) {
throw new ZipException("invalid compression level. compression level dor deflate should be in the range of 0-9");
}
if (zipOptions.isEncryptFiles()) {
if (zipOptions.getEncryptionMethod() != ZipConstants.ENC_METHOD_STANDARD
&& zipOptions.getEncryptionMethod() != ZipConstants.ENC_METHOD_STRONG
&& zipOptions.getEncryptionMethod() != ZipConstants.ENC_METHOD_AES) {
throw new ZipException("Unsupported encryption method!");
}
if (zipOptions.getPassword() == null || zipOptions.getPassword().length == 0) {
throw new ZipException("Need password for encrypt!");
}
}
}
private void removeExistsFile(GeneralFileHeader generalFileHeader) throws ZipException {
if (generalFileHeader == null) {
throw new ZipException("input parameters is null in maintain zip file, cannot remove file from archive");
}
if (this.splitArchive) {
throw new ZipException("Unsupported updating split/spanned zip file! ");
}
SplitOutputStream outputStream = null;
NervousyncRandomAccessFile input = null;
boolean success = Globals.DEFAULT_VALUE_BOOLEAN;
String tempFileName = this.filePath + System.currentTimeMillis() % 1000L;
try {
int indexOfHeader = this.retrieveIndexOfGeneralFileHeader(generalFileHeader);
if (indexOfHeader < 0) {
throw new ZipException("File header not found in zip entity, cannot remove file!");
}
while (FileUtils.isExists(tempFileName)) {
tempFileName = this.filePath + System.currentTimeMillis() % 1000L;
}
try {
outputStream = new SplitOutputStream(tempFileName);
} catch (FileNotFoundException e) {
throw new ZipException(e);
}
input = this.createFileHandler(Globals.READ_MODE);
LocalFileHeader localFileHeader =
this.readLocalFileHeader(input, generalFileHeader);
if (localFileHeader == null) {
throw new ZipException("invalid local file header, cannot remove file from archive");
}
if (!localFileHeader.verifyPassword(input)) {
throw new ZipException("Wrong password or Unsupported encryption method!");
}
long offsetLocalFileHeader = generalFileHeader.getOffsetLocalHeader();
if (generalFileHeader.getZip64ExtendInfo() != null
&& generalFileHeader.getZip64ExtendInfo().getOffsetLocalHeader() != Globals.DEFAULT_VALUE_LONG) {
offsetLocalFileHeader = generalFileHeader.getZip64ExtendInfo().getOffsetLocalHeader();
}
long offsetStartCentralDirectory = this.endCentralDirectoryRecord.getOffsetOfStartOfCentralDirectory();
if (this.zip64Format && this.zip64EndCentralDirectoryRecord != null) {
offsetStartCentralDirectory = this.zip64EndCentralDirectoryRecord.getOffsetStartCenDirWRTStartDiskNo();
}
long offsetEndOfCompressedFile = Globals.DEFAULT_VALUE_LONG;
List fileHeaders = this.centralDirectory.getFileHeaders();
if (indexOfHeader == fileHeaders.size() - 1) {
offsetEndOfCompressedFile = offsetStartCentralDirectory - 1;
} else {
GeneralFileHeader nextFileHeader = fileHeaders.get(indexOfHeader + 1);
if (nextFileHeader != null) {
offsetEndOfCompressedFile = nextFileHeader.getOffsetLocalHeader() - 1;
if (nextFileHeader.getZip64ExtendInfo() != null
&& nextFileHeader.getZip64ExtendInfo().getOffsetLocalHeader() != Globals.DEFAULT_VALUE_LONG) {
offsetEndOfCompressedFile = nextFileHeader.getZip64ExtendInfo().getOffsetLocalHeader() - 1;
}
}
}
if (offsetLocalFileHeader < 0L || offsetEndOfCompressedFile < 0L) {
throw new ZipException("invalid offset for start and end of local file, cannot remove file");
}
if (indexOfHeader == 0) {
if (this.centralDirectory.getFileHeaders().size() > 1) {
this.copyFile(input, outputStream, offsetEndOfCompressedFile + 1L, offsetStartCentralDirectory);
}
} else if (indexOfHeader == (fileHeaders.size() - 1)) {
this.copyFile(input, outputStream, 0, offsetLocalFileHeader);
} else {
this.copyFile(input, outputStream, 0, offsetLocalFileHeader);
this.copyFile(input, outputStream, offsetEndOfCompressedFile + 1L, offsetStartCentralDirectory);
}
this.endCentralDirectoryRecord.setOffsetOfStartOfCentralDirectory(outputStream.getFilePointer());
this.endCentralDirectoryRecord.setTotalOfEntriesInCentralDirectory(this.endCentralDirectoryRecord.getTotalOfEntriesInCentralDirectory() - 1);
this.endCentralDirectoryRecord.setTotalOfEntriesInCentralDirectoryOnThisDisk(this.endCentralDirectoryRecord.getTotalOfEntriesInCentralDirectoryOnThisDisk() - 1);
this.centralDirectory.getFileHeaders().remove(indexOfHeader);
for (int i = indexOfHeader ; i < this.centralDirectory.getFileHeaders().size() ; i++) {
long offsetLocalHeader = this.centralDirectory.getFileHeaders().get(i).getOffsetLocalHeader();
if (this.centralDirectory.getFileHeaders().get(i).getZip64ExtendInfo() != null
&& this.centralDirectory.getFileHeaders().get(i).getZip64ExtendInfo().getOffsetLocalHeader() != Globals.DEFAULT_VALUE_LONG) {
offsetLocalHeader = this.centralDirectory.getFileHeaders().get(i).getZip64ExtendInfo().getOffsetLocalHeader();
}
this.centralDirectory.getFileHeaders().get(i).setOffsetLocalHeader(offsetLocalHeader - (offsetEndOfCompressedFile - offsetLocalFileHeader) - 1);
}
this.finalizeZipFile(outputStream);
success = true;
} catch (Exception e) {
if (e instanceof ZipException) {
throw (ZipException)e;
} else {
throw new ZipException(e);
}
} finally {
try {
if (input != null) {
input.close();
}
} catch (IOException e) {
throw new ZipException(e);
}
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
throw new ZipException(e);
}
if (success) {
FileUtils.copyFile(tempFileName, this.filePath);
}
if (FileUtils.isExists(tempFileName)) {
FileUtils.removeFile(tempFileName);
}
}
}
private int retrieveIndexOfGeneralFileHeader(GeneralFileHeader generalFileHeader) {
if (generalFileHeader == null) {
throw new ZipException("File header is null!");
}
if (this.centralDirectory == null) {
throw new ZipException("central directory is null, cannot determine index of file header");
}
return this.centralDirectory.retrieveIndexOfGeneralFileHeader(generalFileHeader);
}
private NervousyncRandomAccessFile createFileHandler(String mode) throws FileNotFoundException {
if (StringUtils.isNotNullAndNotEmpty(this.filePath)) {
return new NervousyncRandomAccessFile(this.filePath, mode);
}
throw new ZipException("cannot create file handler to remove file");
}
private void copyFile(NervousyncRandomAccessFile input,
OutputStream outputStream, long start, long end) throws ZipException {
if (input == null) {
throw new ZipException("Input stream is null!");
}
if (outputStream == null) {
throw new ZipException("Output stream is null!");
}
if (start < 0 || end < 0 || start > end) {
throw new IndexOutOfBoundsException();
}
if (start == end) {
return;
}
try {
input.seek(start);
int bufferSize = Globals.DEFAULT_BUFFER_SIZE;
if ((end - start) < Globals.DEFAULT_BUFFER_SIZE) {
bufferSize = (int)(end - start);
}
int readLength = Globals.DEFAULT_VALUE_INT;
byte[] readBuffer = new byte[bufferSize];
long totalRead = 0L;
long limitRead = end - start;
do {
readLength = input.read(readBuffer);
outputStream.write(readBuffer, 0, readLength);
totalRead += readLength;
if (totalRead == limitRead) {
break;
}
if (totalRead + readBuffer.length > limitRead) {
readBuffer = new byte[(int)(limitRead - totalRead)];
}
} while (readLength != Globals.DEFAULT_VALUE_INT);
} catch (Exception e) {
if (e instanceof ZipException) {
throw (ZipException)e;
} else {
throw new ZipException(e);
}
}
}
private void extractFileToPath(GeneralFileHeader generalFileHeader,
String destPath, boolean ignoreFileAttr) throws ZipException {
if (generalFileHeader == null) {
throw new ZipException("General file header is null!");
}
ZipInputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = this.openInputStream(generalFileHeader);
outputStream = this.openOutputStream(destPath, generalFileHeader.getEntryPath());
byte[] buffer = new byte[Globals.DEFAULT_BUFFER_SIZE];
int readLength = Globals.DEFAULT_VALUE_INT; ;
while ((readLength = inputStream.read(buffer)) != Globals.DEFAULT_VALUE_INT) {
outputStream.write(buffer, 0, readLength);
}
if (generalFileHeader.getEncryptionMethod() == ZipConstants.ENC_METHOD_AES) {
if (this.decryptor != null && (this.decryptor instanceof AESDecryptor)) {
byte[] tempMacBytes = ((AESDecryptor)this.decryptor).calculateAuthenticationBytes();
byte[] storedMac = ((AESDecryptor)this.decryptor).getStoredMac();
byte[] calculateMac = new byte[ZipConstants.AES_AUTH_LENGTH];
if (calculateMac == null || storedMac == null) {
throw new ZipException("CRC check failed!");
}
System.arraycopy(tempMacBytes, 0, calculateMac, 0, ZipConstants.AES_AUTH_LENGTH);
if (!Arrays.equals(calculateMac, storedMac)) {
throw new ZipException("CRC check failed!");
}
}
} else {
long calculatedCRC = inputStream.crcValue();
if (calculatedCRC != generalFileHeader.getCrc32()) {
throw new ZipException("CRC check failed!");
}
}
} catch (IOException e) {
throw new ZipException(e);
} finally {
try {
if (inputStream != null) {
inputStream.close();
inputStream = null;
}
} catch (IOException e) {
throw new ZipException(e);
} finally {
if (outputStream != null) {
try {
outputStream.close();
outputStream = null;
} catch (IOException e) {
}
}
}
}
try {
String filePath = destPath + generalFileHeader.getEntryPath();
if (generalFileHeader.getExternalFileAttr() != null && !ignoreFileAttr) {
if (generalFileHeader.getExternalFileAttr()[0] == ZipConstants.FILE_MODE_READ_ONLY) {
setFileReadOnly(FileUtils.getFile(filePath));
}
setFileLastModify(FileUtils.getFile(filePath),
DateTimeUtils.dosToJavaTme(generalFileHeader.getLastModFileTime()));
}
} catch (FileNotFoundException e) {
throw new ZipException(e);
}
}
private byte[] readEntry(GeneralFileHeader generalFileHeader) throws ZipException {
if (generalFileHeader == null) {
throw new ZipException("General file header is null!");
}
ZipInputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] buffer = new byte[Globals.DEFAULT_BUFFER_SIZE];
int readLength = Globals.DEFAULT_VALUE_INT;
inputStream = this.openInputStream(generalFileHeader);
outputStream = new ByteArrayOutputStream((int)generalFileHeader.getOriginalSize());
while ((readLength = inputStream.read(buffer)) != Globals.DEFAULT_VALUE_INT) {
outputStream.write(buffer, 0, readLength);
}
if (generalFileHeader.getEncryptionMethod() == ZipConstants.ENC_METHOD_AES) {
if (this.decryptor != null && (this.decryptor instanceof AESDecryptor)) {
byte[] tempMacBytes = ((AESDecryptor)this.decryptor).calculateAuthenticationBytes();
byte[] storedMac = ((AESDecryptor)this.decryptor).getStoredMac();
byte[] calculateMac = new byte[ZipConstants.AES_AUTH_LENGTH];
if (calculateMac == null || storedMac == null) {
throw new ZipException("CRC check failed!");
}
System.arraycopy(tempMacBytes, 0, calculateMac, 0, ZipConstants.AES_AUTH_LENGTH);
if (!Arrays.equals(calculateMac, storedMac)) {
throw new ZipException("CRC check failed!");
}
}
} else {
long calculatedCRC = inputStream.crcValue();
if (calculatedCRC != generalFileHeader.getCrc32()) {
throw new ZipException("CRC check failed!");
}
}
return ((ByteArrayOutputStream)outputStream).toByteArray();
} catch (IOException e) {
throw new ZipException(e);
} finally {
try {
if (inputStream != null) {
inputStream.close();
inputStream = null;
}
} catch (IOException e) {
throw new ZipException(e);
} finally {
if (outputStream != null) {
try {
outputStream.close();
outputStream = null;
} catch (IOException e) {
}
}
}
}
}
private ZipInputStream openInputStream(GeneralFileHeader generalFileHeader) throws ZipException {
NervousyncRandomAccessFile input = null;
try {
input = this.createFileHandler(Globals.READ_MODE);
LocalFileHeader localFileHeader =
this.readLocalFileHeader(input, generalFileHeader);
if (localFileHeader == null) {
throw new ZipException("Error reading local header!");
}
if (localFileHeader.getCompressionMethod() != generalFileHeader.getCompressionMethod()) {
throw new ZipException("local header does not matched with general header");
}
if (localFileHeader.isEncrypted()) {
if (localFileHeader.getEncryptionMethod() == ZipConstants.ENC_METHOD_AES) {
byte[] salt = null;
if (localFileHeader.getAesExtraDataRecord() != null) {
int saltLength = Globals.DEFAULT_VALUE_INT;
switch (localFileHeader.getAesExtraDataRecord().getAesStrength()) {
case ZipConstants.AES_STRENGTH_128:
saltLength = 8;
break;
case ZipConstants.AES_STRENGTH_192:
saltLength = 12;
break;
case ZipConstants.AES_STRENGTH_256:
saltLength = 16;
break;
default:
throw new ZipException("unable to determine salt length: invalid aes key strength");
}
salt = new byte[saltLength];
input.seek(localFileHeader.getOffsetStartOfData());
input.read(salt);
}
byte[] passwordBytes = new byte[2];
input.read(passwordBytes);
this.decryptor = new AESDecryptor(localFileHeader, salt, passwordBytes);
} else if (localFileHeader.getEncryptionMethod() == ZipConstants.ENC_METHOD_STANDARD) {
byte[] decryptorHeader = new byte[ZipConstants.STD_DEC_HDR_SIZE];
input.seek(localFileHeader.getOffsetStartOfData());
input.read(decryptorHeader);
this.decryptor = new StandardDecryptor(localFileHeader, decryptorHeader);
} else {
throw new ZipException("Unsupported encryption method");
}
}
long compressedSize = localFileHeader.getCompressedSize();
long offsetStartOfData = localFileHeader.getOffsetStartOfData();
if (localFileHeader.isEncrypted()) {
if (localFileHeader.getEncryptionMethod() == ZipConstants.ENC_METHOD_AES) {
if (this.decryptor instanceof AESDecryptor) {
compressedSize -= (((AESDecryptor)this.decryptor).getSaltLength() +
ZipConstants.PASSWORD_VERIFIER_LENGTH + 10);
offsetStartOfData += (((AESDecryptor)this.decryptor).getSaltLength() +
ZipConstants.PASSWORD_VERIFIER_LENGTH);
} else {
throw new ZipException("invalid decryptor when trying to calculate " +
"compressed size for AES encrypted file: " + localFileHeader.getEntryPath());
}
} else if (localFileHeader.getEncryptionMethod() == ZipConstants.ENC_METHOD_STANDARD) {
compressedSize -= ZipConstants.STD_DEC_HDR_SIZE;
offsetStartOfData += ZipConstants.STD_DEC_HDR_SIZE;
}
}
int compressionMethod = localFileHeader.getCompressionMethod();
if (generalFileHeader.getEncryptionMethod() == ZipConstants.ENC_METHOD_AES) {
if (generalFileHeader.getAesExtraDataRecord() == null) {
throw new ZipException("AES extra data record does not exists!");
}
compressionMethod = generalFileHeader.getAesExtraDataRecord().getCompressionMethod();
}
input.seek(offsetStartOfData);
boolean isAESEncryptedFile = generalFileHeader.isEncrypted()
&& generalFileHeader.getEncryptionMethod() == ZipConstants.ENC_METHOD_AES;
switch (compressionMethod) {
case ZipConstants.COMP_STORE:
return new ZipInputStream(new PartInputStream(this, input,
compressedSize, this.decryptor, isAESEncryptedFile));
case ZipConstants.COMP_DEFLATE:
return new ZipInputStream(new InflaterInputStream(this, input,
compressedSize, generalFileHeader.getOriginalSize(), this.decryptor, isAESEncryptedFile));
default:
throw new ZipException("Compression type not supported");
}
} catch (Exception e) {
if (input != null) {
try {
input.close();
}catch (IOException ex) {
}
}
if (e instanceof ZipException) {
throw (ZipException)e;
} else {
throw new ZipException(e);
}
}
}
private FileOutputStream openOutputStream(String folderPath, String fileName) throws ZipException {
if (!StringUtils.isNotNullAndNotEmpty(folderPath)) {
throw new ZipException("Output path is null");
}
if (!StringUtils.isNotNullAndNotEmpty(fileName)) {
throw new ZipException("Output file name is null");
}
if (!FileUtils.makeHome(folderPath)) {
throw new ZipException("Create output folder error");
}
try {
if (!folderPath.endsWith(Globals.DEFAULT_PAGE_SEPARATOR)) {
folderPath += Globals.DEFAULT_PAGE_SEPARATOR;
}
String fullPath = folderPath + fileName;
fullPath = StringUtils.replace(fullPath, ZipConstants.ZIP_FILE_SEPARATOR, Globals.DEFAULT_PAGE_SEPARATOR);
FileUtils.makeHome(fullPath.substring(0, fullPath.lastIndexOf(Globals.DEFAULT_PAGE_SEPARATOR)));
return new FileOutputStream(FileUtils.getFile(fullPath));
} catch (FileNotFoundException e) {
throw new ZipException(e);
}
}
private OutputStream openMergeOutputStream(String outputPath) throws ZipException {
if (outputPath == null) {
throw new ZipException("Output path is null, cannot create outputstream");
}
try {
return new FileOutputStream(FileUtils.getFile(outputPath));
} catch (Exception e) {
if (e instanceof ZipException) {
throw (ZipException)e;
} else {
throw new ZipException(e);
}
}
}
private NervousyncRandomAccessFile openSplitFile(int index) throws ZipException {
if (index < 0) {
throw new ZipException("invlaid index, cannot create split file handler");
}
try {
String currentSplitFile = null;
if (index == this.endCentralDirectoryRecord.getIndexOfThisDisk()) {
currentSplitFile = this.filePath;
} else {
currentSplitFile = this.filePath.substring(0, this.filePath.lastIndexOf('.'));
if (this.currentSplitIndex < 9) {
currentSplitFile += (".zip.0" + (this.currentSplitIndex + 1));
} else {
currentSplitFile += (".zip." + (this.currentSplitIndex + 1));
}
}
if (!FileUtils.isExists(currentSplitFile)) {
throw new ZipException("Split file not found!");
}
return new NervousyncRandomAccessFile(currentSplitFile, Globals.READ_MODE);
} catch (Exception e) {
if (e instanceof ZipException) {
throw (ZipException)e;
} else {
throw new ZipException(e);
}
}
}
private void updateSplitZipEntity(List sizeList,
boolean removeSplitSig) throws ZipException {
this.splitArchive = Globals.DEFAULT_VALUE_BOOLEAN;
this.updateSplitZipHeader(sizeList, removeSplitSig);
this.updateSplitEndCentralDirectory();
if (this.zip64Format) {
this.updateSplitZip64EndCentralDirectoryLocator(sizeList);
this.updateSplitZip64EndCentralDirectoryRecord(sizeList);
}
}
private void updateSplitZipHeader(List sizeList,
boolean removeSplitSig) throws ZipException {
if (this.centralDirectory == null) {
throw new ZipException("corrupt zip entity, cannot update split zip model");
}
int splitSigOverhead = 0;
if (removeSplitSig) {
splitSigOverhead = 4;
}
List newFileHeaders = new ArrayList();
for (GeneralFileHeader generalFileHeader : this.centralDirectory.getFileHeaders()) {
long offsetHeaderToAdd = 0L;
for (int i = 0 ; i < generalFileHeader.getDiskNumberStart() ; i++) {
offsetHeaderToAdd += sizeList.get(i);
}
generalFileHeader.setOffsetLocalHeader(generalFileHeader.getOffsetLocalHeader() + offsetHeaderToAdd - splitSigOverhead);
generalFileHeader.setDiskNumberStart(0);
newFileHeaders.add(generalFileHeader);
}
this.centralDirectory.setFileHeaders(newFileHeaders);
}
private void updateSplitEndCentralDirectory() throws ZipException {
if (this.centralDirectory == null) {
throw new ZipException("corrupt zip entity, cannot update split zip model");
}
this.endCentralDirectoryRecord.setIndexOfThisDisk(0);
this.endCentralDirectoryRecord.setIndexOfThisDiskStartOfCentralDirectory(0);
this.endCentralDirectoryRecord.setTotalOfEntriesInCentralDirectory(
this.centralDirectory.getFileHeaders().size());
this.endCentralDirectoryRecord.setTotalOfEntriesInCentralDirectoryOnThisDisk(
this.centralDirectory.getFileHeaders().size());
}
private void updateSplitZip64EndCentralDirectoryLocator(List sizeList) throws ZipException {
if (this.zip64EndCentralDirectoryLocator == null) {
return;
}
this.zip64EndCentralDirectoryLocator.setIndexOfZip64EndOfCentralDirectoryRecord(0);
long offsetZip64EndCentralDirRec = 0;
for (Long recordSize : sizeList) {
offsetZip64EndCentralDirRec += recordSize.longValue();
}
this.zip64EndCentralDirectoryLocator.setOffsetZip64EndOfCentralDirectoryRecord(
this.zip64EndCentralDirectoryLocator.getOffsetZip64EndOfCentralDirectoryRecord() +
offsetZip64EndCentralDirRec);
this.zip64EndCentralDirectoryLocator.setTotalNumberOfDiscs(1);
}
private void updateSplitZip64EndCentralDirectoryRecord(List sizeList) throws ZipException {
if (this.zip64EndCentralDirectoryRecord == null) {
return;
}
this.zip64EndCentralDirectoryRecord.setIndex(0);
this.zip64EndCentralDirectoryRecord.setStartOfCentralDirectory(0);
this.zip64EndCentralDirectoryRecord.setTotalEntriesInCentralDirectoryOnThisDisk(
this.endCentralDirectoryRecord.getTotalOfEntriesInCentralDirectory());
long offsetStartCenDirWRTStartDiskNo = 0;
for (Long recordSize : sizeList) {
offsetStartCenDirWRTStartDiskNo += recordSize.longValue();
}
this.zip64EndCentralDirectoryRecord.setOffsetStartCenDirWRTStartDiskNo(
this.zip64EndCentralDirectoryRecord.getOffsetStartCenDirWRTStartDiskNo() +
offsetStartCenDirWRTStartDiskNo);
}
private void readHeaders() throws ZipException {
NervousyncRandomAccessFile input = null;
try {
input = new NervousyncRandomAccessFile(this.filePath, Globals.READ_MODE);
this.readEndOfCentralDirectoryRecord(input);
// Check and set zip64 format
this.readZip64EndCentralDirectoryLocator(input);
if (this.zip64Format) {
this.readZip64EndCentralDirectoryRecord(input);
}
this.readCentralDirectory(input);
} catch (Exception e) {
if (e instanceof ZipException) {
throw (ZipException)e;
} else {
throw new ZipException(e);
}
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Close random access file error! ", e);
}
}
}
}
}
private LocalFileHeader readLocalFileHeader(NervousyncRandomAccessFile input,
GeneralFileHeader generalFileHeader) throws ZipException {
if (generalFileHeader == null || input == null) {
throw new ZipException("invalid read parameters for local header");
}
try {
long localHeaderOffset = generalFileHeader.getOffsetLocalHeader();
if (generalFileHeader.getZip64ExtendInfo() != null
&& generalFileHeader.getZip64ExtendInfo().getOffsetLocalHeader() > 0L) {
localHeaderOffset = generalFileHeader.getZip64ExtendInfo().getOffsetLocalHeader();
}
if (localHeaderOffset < 0) {
throw new ZipException("Invalid local header offset");
}
input.seek(localHeaderOffset + 26);
byte[] tempBuffer = new byte[4];
input.read(tempBuffer);
byte[] shortBuffer = new byte[2];
System.arraycopy(tempBuffer, 0, shortBuffer, 0, 2);
int fileNameLength = RawUtils.readShortFromLittleEndian(shortBuffer, 0);
System.arraycopy(tempBuffer, 2, shortBuffer, 0, 2);
int extraFieldLength = RawUtils.readShortFromLittleEndian(shortBuffer, 0);
input.seek(localHeaderOffset);
int length = 0;
LocalFileHeader localFileHeader = new LocalFileHeader();
byte[] readBuffer = new byte[30 + fileNameLength + extraFieldLength];
input.read(readBuffer);
byte[] intBuffer = new byte[4];
// Signature
System.arraycopy(readBuffer, 0, intBuffer, 0, 4);
int signature = RawUtils.readIntFromLittleEndian(intBuffer, 0);
if (signature != ZipConstants.LOCSIG) {
throw new ZipException("invalid local header signature for file: " + generalFileHeader.getEntryPath());
}
localFileHeader.setSignature(signature);
length += 4;
// Extract needed
System.arraycopy(readBuffer, 4, shortBuffer, 0, 2);
localFileHeader.setExtractNeeded(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
length += 2;
// General purpose bit flag
System.arraycopy(readBuffer, 6, shortBuffer, 0, 2);
localFileHeader.setFileNameUTF8Encoded(
(RawUtils.readShortFromLittleEndian(shortBuffer, 0) & ZipConstants.UFT8_NAMES_FLAG) != 0);
int firstByte = shortBuffer[0];
if ((firstByte & 1) != 0) {
localFileHeader.setEncrypted(true);
}
localFileHeader.setGeneralPurposeFlag(shortBuffer);
length += 2;
// Check if data descriptor exists for local file header
String binaryData = Integer.toBinaryString(firstByte);
if (binaryData.length() >= 4) {
localFileHeader.setDataDescriptorExists(binaryData.charAt(3) == '1');
}
// Compression method
System.arraycopy(readBuffer, 8, shortBuffer, 0, 2);
localFileHeader.setCompressionMethod(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
length += 2;
// Lase modify time
System.arraycopy(readBuffer, 10, intBuffer, 0, 4);
localFileHeader.setLastModFileTime(RawUtils.readIntFromLittleEndian(intBuffer, 0));
length += 4;
// CRC
System.arraycopy(readBuffer, 14, intBuffer, 0, 4);
localFileHeader.setCrc32(RawUtils.readIntFromLittleEndian(intBuffer, 0));
localFileHeader.setCrcBuffer(intBuffer.clone());
length += 4;
// Compressed size
System.arraycopy(readBuffer, 18, intBuffer, 0, 4);
localFileHeader.setCompressedSize(RawUtils.readLongFromLittleEndian(readLongByteFromIntByte(intBuffer), 0));
length += 4;
// Original size
System.arraycopy(readBuffer, 22, intBuffer, 0, 4);
localFileHeader.setOriginalSize(RawUtils.readLongFromLittleEndian(readLongByteFromIntByte(intBuffer), 0));
length += 4;
// File name length
localFileHeader.setFileNameLength(fileNameLength);
length += 2;
// Extra field length
localFileHeader.setExtraFieldLength(extraFieldLength);
length += 2;
// File name
if (fileNameLength > 0) {
byte[] fileNameBuffer = new byte[fileNameLength];
System.arraycopy(readBuffer, 30, fileNameBuffer, 0, fileNameLength);
String entryPath = null;
try {
if (localFileHeader.isFileNameUTF8Encoded()) {
entryPath = new String(fileNameBuffer, Globals.DEFAULT_ENCODING);
} else {
entryPath = new String(fileNameBuffer, ZipConstants.CHARSET_CP850);
}
} catch (UnsupportedEncodingException e) {
entryPath = new String(fileNameBuffer);
}
if (entryPath.indexOf(ZipConstants.ZIP_ENTRY_SEPARATOR) >= 0) {
entryPath = entryPath.substring(entryPath.indexOf(ZipConstants.ZIP_ENTRY_SEPARATOR)
+ ZipConstants.ZIP_ENTRY_SEPARATOR.length());
}
localFileHeader.setEntryPath(entryPath);
length += fileNameLength;
} else {
localFileHeader.setEntryPath(null);
}
// Extra field
if (localFileHeader.getExtraFieldLength() > 0) {
byte[] extraFieldBuffer = new byte[extraFieldLength];
System.arraycopy(readBuffer, 30 + fileNameLength, extraFieldBuffer, 0, extraFieldLength);
localFileHeader.setExtraDataRecords(readExtraDataRecords(extraFieldBuffer, extraFieldLength));
}
length += extraFieldLength;
localFileHeader.setOffsetStartOfData(localHeaderOffset + length);
// Copy password
localFileHeader.setPassword(generalFileHeader.getPassword());
readAndSaveZip64ExtendInfo(localFileHeader);
readAndSaveAESExtraDataRecord(localFileHeader);
if (localFileHeader.isEncrypted() && localFileHeader.getEncryptionMethod() != ZipConstants.ENC_METHOD_AES) {
if ((firstByte & 64) == 64) {
localFileHeader.setEncryptionMethod(ZipConstants.ENC_METHOD_STRONG);
} else {
localFileHeader.setEncryptionMethod(ZipConstants.ENC_METHOD_STANDARD);
}
}
if (localFileHeader.getCrc32() <= 0L) {
localFileHeader.setCrc32(generalFileHeader.getCrc32());
localFileHeader.setCrcBuffer(generalFileHeader.getCrcBuffer());
}
if (localFileHeader.getCompressedSize() <= 0L) {
localFileHeader.setCompressedSize(generalFileHeader.getCompressedSize());
}
if (localFileHeader.getOriginalSize() <= 0L) {
localFileHeader.setOriginalSize(generalFileHeader.getOriginalSize());
}
return localFileHeader;
} catch (IOException e) {
throw new ZipException(e);
}
}
private void processHeaderData(OutputStream outputStream) throws ZipException {
try {
int currentSplitFileCount = 0;
if (outputStream instanceof SplitOutputStream) {
this.endCentralDirectoryRecord
.setOffsetOfStartOfCentralDirectory(((SplitOutputStream) outputStream).getFilePointer());
currentSplitFileCount = ((SplitOutputStream) outputStream).getCurrentSplitFileIndex();
}
if (this.zip64Format) {
if (this.zip64EndCentralDirectoryRecord == null) {
this.zip64EndCentralDirectoryRecord = new Zip64EndCentralDirectoryRecord();
}
if (this.zip64EndCentralDirectoryLocator == null) {
this.zip64EndCentralDirectoryLocator = new Zip64EndCentralDirectoryLocator();
}
this.zip64EndCentralDirectoryLocator
.setIndexOfZip64EndOfCentralDirectoryRecord(currentSplitFileCount);
this.zip64EndCentralDirectoryLocator.setTotalNumberOfDiscs(currentSplitFileCount + 1);
}
this.endCentralDirectoryRecord.setIndexOfThisDisk(currentSplitFileCount);
this.endCentralDirectoryRecord.setIndexOfThisDiskStartOfCentralDirectory(currentSplitFileCount);
} catch (IOException e) {
throw new ZipException(e);
}
}
private int writeCentralDirectory(OutputStream outputStream,
List headerBytesList) throws ZipException {
if (outputStream == null) {
throw new ZipException("output parameters is null, cannot write central directory");
}
if (this.centralDirectory == null || this.centralDirectory.getFileHeaders() == null
|| this.centralDirectory.getFileHeaders().size() <= 0) {
return 0;
}
int sizeOfCentralDirectory = 0;
for (GeneralFileHeader generalFileHeader : this.centralDirectory.getFileHeaders()) {
sizeOfCentralDirectory += writeFileHeader(generalFileHeader, outputStream, headerBytesList);
}
return sizeOfCentralDirectory;
}
private int writeFileHeader(GeneralFileHeader generalFileHeader,
OutputStream outputStream, List headerBytesList) throws ZipException {
if (generalFileHeader == null || outputStream == null) {
throw new ZipException("input parameters is null, cannot write local file header");
}
try {
int sizeOfFileHeader = 0;
byte[] shortBuffer = new byte[2];
byte[] intBuffer = new byte[4];
byte[] longBuffer = new byte[8];
final byte[] EMPTY_SHORT_BUFFER = { 0, 0 };
final byte[] EMPTY_INT_BUFFER = { 0, 0, 0, 0 };
boolean writeZip64FileSize = false;
boolean writeZip64OffsetLocalHeader = false;
RawUtils.writeIntFromLittleEndian(intBuffer, 0, generalFileHeader.getSignature());
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
sizeOfFileHeader += 4;
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) generalFileHeader.getMadeVersion());
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
sizeOfFileHeader += 2;
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) generalFileHeader.getExtractNeeded());
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
sizeOfFileHeader += 2;
HeaderOperator.copyByteArrayToArrayList(generalFileHeader.getGeneralPurposeFlag(), headerBytesList);
sizeOfFileHeader += 2;
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) generalFileHeader.getCompressionMethod());
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
sizeOfFileHeader += 2;
RawUtils.writeIntFromLittleEndian(intBuffer, 0, generalFileHeader.getLastModFileTime());
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
sizeOfFileHeader += 4;
RawUtils.writeIntFromLittleEndian(intBuffer, 0, (int) (generalFileHeader.getCrc32()));
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
sizeOfFileHeader += 4;
if (generalFileHeader.getCompressedSize() >= ZipConstants.ZIP_64_LIMIT
|| generalFileHeader.getOriginalSize()
+ ZipConstants.ZIP64_EXTRA_BUFFER_SIZE >= ZipConstants.ZIP_64_LIMIT) {
RawUtils.writeLongFromLittleEndian(longBuffer, 0, ZipConstants.ZIP_64_LIMIT);
System.arraycopy(longBuffer, 0, intBuffer, 0, 4);
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
sizeOfFileHeader += 4;
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
sizeOfFileHeader += 4;
writeZip64FileSize = true;
} else {
RawUtils.writeLongFromLittleEndian(longBuffer, 0, generalFileHeader.getCompressedSize());
System.arraycopy(longBuffer, 0, intBuffer, 0, 4);
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
sizeOfFileHeader += 4;
RawUtils.writeLongFromLittleEndian(longBuffer, 0, generalFileHeader.getOriginalSize());
System.arraycopy(longBuffer, 0, intBuffer, 0, 4);
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
sizeOfFileHeader += 4;
}
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) generalFileHeader.getFileNameLength());
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
sizeOfFileHeader += 2;
// Compute offset bytes before extra field is written for Zip64
// compatibility
// NOTE: this data is not written now, but written at a later point
byte[] offsetLocalHeaderBytes = new byte[4];
if (generalFileHeader.getOffsetLocalHeader() > ZipConstants.ZIP_64_LIMIT) {
RawUtils.writeLongFromLittleEndian(longBuffer, 0, ZipConstants.ZIP_64_LIMIT);
System.arraycopy(longBuffer, 0, offsetLocalHeaderBytes, 0, 4);
writeZip64OffsetLocalHeader = true;
} else {
RawUtils.writeLongFromLittleEndian(longBuffer, 0, generalFileHeader.getOffsetLocalHeader());
System.arraycopy(longBuffer, 0, offsetLocalHeaderBytes, 0, 4);
}
// extra field length
int extraFieldLength = 0;
if (writeZip64FileSize || writeZip64OffsetLocalHeader) {
extraFieldLength += 4;
if (writeZip64FileSize)
extraFieldLength += 16;
if (writeZip64OffsetLocalHeader)
extraFieldLength += 8;
}
if (generalFileHeader.getAesExtraDataRecord() != null) {
extraFieldLength += 11;
}
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) (extraFieldLength));
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
sizeOfFileHeader += 2;
// Skip file comment length for now
HeaderOperator.copyByteArrayToArrayList(EMPTY_SHORT_BUFFER, headerBytesList);
sizeOfFileHeader += 2;
// Skip disk number start for now
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) (generalFileHeader.getDiskNumberStart()));
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
sizeOfFileHeader += 2;
// Skip internal file attributes for now
HeaderOperator.copyByteArrayToArrayList(EMPTY_SHORT_BUFFER, headerBytesList);
sizeOfFileHeader += 2;
// External file attributes
if (generalFileHeader.getExternalFileAttr() != null) {
HeaderOperator.copyByteArrayToArrayList(generalFileHeader.getExternalFileAttr(), headerBytesList);
} else {
HeaderOperator.copyByteArrayToArrayList(EMPTY_INT_BUFFER, headerBytesList);
}
sizeOfFileHeader += 4;
// offset local header
// this data is computed above
HeaderOperator.copyByteArrayToArrayList(offsetLocalHeaderBytes, headerBytesList);
sizeOfFileHeader += 4;
if (StringUtils.isNotNullAndNotEmpty(this.fileNameCharset)) {
byte[] fileNameBytes = generalFileHeader.getEntryPath().getBytes(this.fileNameCharset);
HeaderOperator.copyByteArrayToArrayList(fileNameBytes, headerBytesList);
sizeOfFileHeader += fileNameBytes.length;
} else {
HeaderOperator.copyByteArrayToArrayList(StringUtils.convertCharset(generalFileHeader.getEntryPath()), headerBytesList);
sizeOfFileHeader += StringUtils.getEncodedStringLength(generalFileHeader.getEntryPath());
}
if (writeZip64FileSize || writeZip64OffsetLocalHeader) {
this.zip64Format = true;
// Zip64 header
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) ZipConstants.EXTRAFIELDZIP64LENGTH);
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
sizeOfFileHeader += 2;
// Zip64 extra data record size
int dataSize = 0;
if (writeZip64FileSize) {
dataSize += 16;
}
if (writeZip64OffsetLocalHeader) {
dataSize += 8;
}
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) dataSize);
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
sizeOfFileHeader += 2;
if (writeZip64FileSize) {
RawUtils.writeLongFromLittleEndian(longBuffer, 0, generalFileHeader.getOriginalSize());
HeaderOperator.copyByteArrayToArrayList(longBuffer, headerBytesList);
sizeOfFileHeader += 8;
RawUtils.writeLongFromLittleEndian(longBuffer, 0, generalFileHeader.getCompressedSize());
HeaderOperator.copyByteArrayToArrayList(longBuffer, headerBytesList);
sizeOfFileHeader += 8;
}
if (writeZip64OffsetLocalHeader) {
RawUtils.writeLongFromLittleEndian(longBuffer, 0, generalFileHeader.getOffsetLocalHeader());
HeaderOperator.copyByteArrayToArrayList(longBuffer, headerBytesList);
sizeOfFileHeader += 8;
}
}
if (generalFileHeader.getAesExtraDataRecord() != null) {
AESExtraDataRecord aesExtraDataRecord = generalFileHeader.getAesExtraDataRecord();
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) aesExtraDataRecord.getSignature());
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) aesExtraDataRecord.getDataSize());
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) aesExtraDataRecord.getVersionNumber());
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
HeaderOperator.copyByteArrayToArrayList(aesExtraDataRecord.getVendorID().getBytes(), headerBytesList);
byte[] aesStrengthBytes = new byte[1];
aesStrengthBytes[0] = (byte) aesExtraDataRecord.getAesStrength();
HeaderOperator.copyByteArrayToArrayList(aesStrengthBytes, headerBytesList);
RawUtils.writeShortFromLittleEndian(shortBuffer, 0,
(short) aesExtraDataRecord.getCompressionMethod());
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
sizeOfFileHeader += 11;
}
return sizeOfFileHeader;
} catch (Exception e) {
throw new ZipException(e);
}
}
private void writeZip64EndOfCentralDirectoryRecord(OutputStream outputStream,
int sizeOfCentralDirectory, long offsetCentralDirectory, List headerBytesList) throws ZipException {
if (outputStream == null) {
throw new ZipException("Output stream is null, cannot write zip64 end of central directory record");
}
try {
byte[] shortBuffer = new byte[2];
byte[] intBuffer = new byte[4];
byte[] longBuffer = new byte[8];
final byte[] EMPTY_SHORT_BUFFER = { 0, 0 };
// zip64 end of central dir signature
RawUtils.writeIntFromLittleEndian(intBuffer, 0, (int) ZipConstants.ZIP64ENDCENDIRREC);
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
// size of zip64 end of central directory record
RawUtils.writeLongFromLittleEndian(longBuffer, 0, (long) 44);
HeaderOperator.copyByteArrayToArrayList(longBuffer, headerBytesList);
// version made by
// version needed to extract
if (this.centralDirectory != null && this.centralDirectory.getFileHeaders() != null
&& this.centralDirectory.getFileHeaders().size() > 0) {
RawUtils.writeShortFromLittleEndian(shortBuffer, 0,
(short) this.centralDirectory.getFileHeaders().get(0).getMadeVersion());
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
RawUtils.writeShortFromLittleEndian(shortBuffer, 0,
(short) this.centralDirectory.getFileHeaders().get(0).getExtractNeeded());
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
} else {
HeaderOperator.copyByteArrayToArrayList(EMPTY_SHORT_BUFFER, headerBytesList);
HeaderOperator.copyByteArrayToArrayList(EMPTY_SHORT_BUFFER, headerBytesList);
}
// number of this disk
RawUtils.writeIntFromLittleEndian(intBuffer, 0,
this.endCentralDirectoryRecord.getIndexOfThisDisk());
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
// number of the disk with start of central directory
RawUtils.writeIntFromLittleEndian(intBuffer, 0,
this.endCentralDirectoryRecord.getIndexOfThisDiskStartOfCentralDirectory());
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
// total number of entries in the central directory on this disk
int numEntries = 0;
int numEntriesOnThisDisk = 0;
if (this.centralDirectory == null || this.centralDirectory.getFileHeaders() == null) {
throw new ZipException(
"invalid central directory/file headers, " + "cannot write end of central directory record");
} else {
numEntries = this.centralDirectory.getFileHeaders().size();
if (this.splitArchive) {
countNumberOfFileHeaderEntriesOnDisk(this.centralDirectory.getFileHeaders(),
this.endCentralDirectoryRecord.getIndexOfThisDisk());
} else {
numEntriesOnThisDisk = numEntries;
}
}
RawUtils.writeLongFromLittleEndian(longBuffer, 0, numEntriesOnThisDisk);
HeaderOperator.copyByteArrayToArrayList(longBuffer, headerBytesList);
// Total number of entries in central directory
RawUtils.writeLongFromLittleEndian(longBuffer, 0, numEntries);
HeaderOperator.copyByteArrayToArrayList(longBuffer, headerBytesList);
// Size of central directory
RawUtils.writeLongFromLittleEndian(longBuffer, 0, sizeOfCentralDirectory);
HeaderOperator.copyByteArrayToArrayList(longBuffer, headerBytesList);
// offset of start of central directory with respect to the starting
// disk number
RawUtils.writeLongFromLittleEndian(longBuffer, 0, offsetCentralDirectory);
HeaderOperator.copyByteArrayToArrayList(longBuffer, headerBytesList);
} catch (Exception e) {
if (e instanceof ZipException) {
throw e;
} else {
throw new ZipException(e);
}
}
}
private void writeZip64EndOfCentralDirectoryLocator(OutputStream outputStream,
List headerBytesList) throws ZipException {
if (outputStream == null) {
throw new ZipException("Output stream is null, cannot write zip64 end of central directory locator");
}
try {
byte[] intBuffer = new byte[4];
byte[] longBuffer = new byte[8];
// zip64 end of central dir locator signature
RawUtils.writeIntFromLittleEndian(intBuffer, 0, (int) ZipConstants.ZIP64ENDCENDIRLOC);
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
// number of the disk with the start of the zip64 end of central
// directory
RawUtils.writeIntFromLittleEndian(intBuffer, 0,
this.zip64EndCentralDirectoryLocator.getIndexOfZip64EndOfCentralDirectoryRecord());
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
// relative offset of the zip64 end of central directory record
RawUtils.writeLongFromLittleEndian(longBuffer, 0,
this.zip64EndCentralDirectoryLocator.getOffsetZip64EndOfCentralDirectoryRecord());
HeaderOperator.copyByteArrayToArrayList(longBuffer, headerBytesList);
// total number of disks
RawUtils.writeIntFromLittleEndian(intBuffer, 0,
this.zip64EndCentralDirectoryLocator.getTotalNumberOfDiscs());
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
} catch (Exception e) {
if (e instanceof ZipException) {
throw e;
} else {
throw new ZipException(e);
}
}
}
private void writeEndOfCentralDirectoryRecord(OutputStream outputStream,
int sizeOfCentralDirectory, long offsetCentralDirectory, List headerBytesList) throws ZipException {
if (outputStream == null) {
throw new ZipException("Output stream is null, cannot write end of central directory record");
}
try {
byte[] shortBuffer = new byte[2];
byte[] intBuffer = new byte[4];
byte[] longBuffer = new byte[8];
// End of central directory signature
RawUtils.writeIntFromLittleEndian(intBuffer, 0,
(int) this.endCentralDirectoryRecord.getSignature());
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
// number of this disk
RawUtils.writeShortFromLittleEndian(shortBuffer, 0,
(short) (this.endCentralDirectoryRecord.getIndexOfThisDisk()));
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
// number of the disk with start of central directory
RawUtils.writeShortFromLittleEndian(shortBuffer, 0,
(short) (this.endCentralDirectoryRecord.getIndexOfThisDiskStartOfCentralDirectory()));
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
// Total number of entries in central directory on this disk
int numEntries = 0;
int numEntriesOnThisDisk = 0;
if (this.centralDirectory == null || this.centralDirectory.getFileHeaders() == null) {
throw new ZipException(
"invalid central directory/file headers, " + "cannot write end of central directory record");
} else {
numEntries = this.centralDirectory.getFileHeaders().size();
if (this.splitArchive) {
numEntriesOnThisDisk = countNumberOfFileHeaderEntriesOnDisk(
this.centralDirectory.getFileHeaders(),
this.endCentralDirectoryRecord.getIndexOfThisDisk());
} else {
numEntriesOnThisDisk = numEntries;
}
}
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) numEntriesOnThisDisk);
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
// Total number of entries in central directory
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) numEntries);
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
// Size of central directory
RawUtils.writeIntFromLittleEndian(intBuffer, 0, sizeOfCentralDirectory);
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
// Offset central directory
if (offsetCentralDirectory > ZipConstants.ZIP_64_LIMIT) {
RawUtils.writeLongFromLittleEndian(longBuffer, 0, ZipConstants.ZIP_64_LIMIT);
System.arraycopy(longBuffer, 0, intBuffer, 0, 4);
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
} else {
RawUtils.writeLongFromLittleEndian(longBuffer, 0, offsetCentralDirectory);
System.arraycopy(longBuffer, 0, intBuffer, 0, 4);
HeaderOperator.copyByteArrayToArrayList(intBuffer, headerBytesList);
}
// Zip File comment length
int commentLength = 0;
if (this.endCentralDirectoryRecord.getCommentBytes() != null) {
commentLength = this.endCentralDirectoryRecord.getCommentLength();
}
RawUtils.writeShortFromLittleEndian(shortBuffer, 0, (short) commentLength);
HeaderOperator.copyByteArrayToArrayList(shortBuffer, headerBytesList);
// Comment
if (commentLength > 0) {
HeaderOperator.copyByteArrayToArrayList(this.endCentralDirectoryRecord.getCommentBytes(), headerBytesList);
}
} catch (Exception e) {
if (e instanceof ZipException) {
throw e;
} else {
throw new ZipException(e);
}
}
}
private void writeZipHeaderBytes(OutputStream outputStream, byte[] buffer)
throws ZipException {
if (buffer == null) {
throw new ZipException("invalid buffer to write as zip headers");
}
try {
if (outputStream instanceof SplitOutputStream) {
if (((SplitOutputStream) outputStream).checkBufferSizeAndStartNextSplitFile(buffer.length)) {
this.finalizeZipFile(outputStream);
return;
}
}
outputStream.write(buffer);
} catch (IOException e) {
throw new ZipException(e);
}
}
private void readEndOfCentralDirectoryRecord(NervousyncRandomAccessFile input)
throws ZipException {
if (input == null) {
throw new ZipException("Random access file is null!");
}
try {
byte[] buffer = new byte[4];
long position = Globals.DEFAULT_VALUE_LONG;
try {
position = input.length();
} catch (IOException e) {
position = Globals.DEFAULT_VALUE_LONG;
}
if (position == Globals.DEFAULT_VALUE_LONG) {
throw new ZipException("Read end of central directory record error! ");
}
position -= ZipConstants.ENDHDR;
this.endCentralDirectoryRecord = new EndCentralDirectoryRecord();
int count = 0;
do {
input.seek(position--);
count++;
} while ((readIntFromDataInput(input, buffer) != ZipConstants.ENDSIG)
&& count <= 3000);
if (RawUtils.readIntFromLittleEndian(buffer, 0) != ZipConstants.ENDSIG) {
throw new ZipException("zip headers not found. probably not a zip file");
}
byte[] readBuffer = new byte[18];
input.read(readBuffer);
byte[] intBuffer = new byte[4];
byte[] shortBuffer = new byte[2];
this.endCentralDirectoryRecord.setSignature(ZipConstants.ENDSIG);
System.arraycopy(readBuffer, 0, shortBuffer, 0, 2);
this.endCentralDirectoryRecord.setIndexOfThisDisk(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
System.arraycopy(readBuffer, 2, shortBuffer, 0, 2);
this.endCentralDirectoryRecord
.setIndexOfThisDiskStartOfCentralDirectory(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
System.arraycopy(readBuffer, 4, shortBuffer, 0, 2);
this.endCentralDirectoryRecord.setTotalOfEntriesInCentralDirectoryOnThisDisk(
RawUtils.readShortFromLittleEndian(shortBuffer, 0));
System.arraycopy(readBuffer, 6, shortBuffer, 0, 2);
this.endCentralDirectoryRecord
.setTotalOfEntriesInCentralDirectory(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
System.arraycopy(readBuffer, 8, intBuffer, 0, 4);
this.endCentralDirectoryRecord.setSizeOfCentralDirectory(RawUtils.readIntFromLittleEndian(intBuffer, 0));
System.arraycopy(readBuffer, 12, intBuffer, 0, 4);
this.endCentralDirectoryRecord.setOffsetOfStartOfCentralDirectory(
RawUtils.readLongFromLittleEndian(readLongByteFromIntByte(intBuffer), 0));
System.arraycopy(readBuffer, 16, shortBuffer, 0, 2);
this.endCentralDirectoryRecord.setCommentLength(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
if (this.endCentralDirectoryRecord.getCommentLength() > 0) {
byte[] commentBuffer = new byte[endCentralDirectoryRecord.getCommentLength()];
input.read(commentBuffer);
endCentralDirectoryRecord.setCommentBytes(commentBuffer);
}
if (this.endCentralDirectoryRecord.getIndexOfThisDisk() > 0) {
this.splitArchive = true;
} else {
this.splitArchive = false;
}
} catch (IOException e) {
throw new ZipException(e);
}
}
private void readZip64EndCentralDirectoryLocator(NervousyncRandomAccessFile input)
throws ZipException {
try {
this.zip64EndCentralDirectoryLocator = new Zip64EndCentralDirectoryLocator();
byte[] buffer = new byte[4];
long position = Globals.DEFAULT_VALUE_LONG;
try {
position = input.length();
} catch (Exception e) {
position = Globals.DEFAULT_VALUE_LONG;
}
if (position == Globals.DEFAULT_VALUE_LONG) {
throw new ZipException("Read end of central directory record error! ");
}
position -= ZipConstants.ENDHDR;
do {
input.seek(position--);
} while (readIntFromDataInput(input, buffer) != ZipConstants.ENDSIG);
// Now the file pointer is at the end of signature of Central Dir
// Rec
// Seek back with the following values
// 4 -> total number of disks
// 8 -> relative offset of the zip64 end of central directory record
// 4 -> number of the disk with the start of the zip64 end of
// central directory
// 4 -> zip64 end of central dir locator signature
// Refer to Appnote for more information
input.seek(position - 4 - 8 - 4 - 4);
byte[] readBuffer = new byte[20];
input.read(readBuffer);
byte[] intBuffer = new byte[4];
byte[] longBuffer = new byte[8];
System.arraycopy(readBuffer, 0, intBuffer, 0, 4);
int signature = RawUtils.readIntFromLittleEndian(intBuffer, 0);
if (signature == ZipConstants.ZIP64ENDCENDIRLOC) {
this.zip64Format = true;
this.zip64EndCentralDirectoryLocator.setSignature(signature);
} else {
this.zip64Format = false;
return;
}
System.arraycopy(readBuffer, 4, intBuffer, 0, 4);
this.zip64EndCentralDirectoryLocator
.setIndexOfZip64EndOfCentralDirectoryRecord(RawUtils.readIntFromLittleEndian(intBuffer, 0));
System.arraycopy(readBuffer, 8, longBuffer, 0, 8);
this.zip64EndCentralDirectoryLocator
.setOffsetZip64EndOfCentralDirectoryRecord(RawUtils.readLongFromLittleEndian(longBuffer, 0));
System.arraycopy(readBuffer, 16, intBuffer, 0, 4);
this.zip64EndCentralDirectoryLocator.setTotalNumberOfDiscs(RawUtils.readIntFromLittleEndian(intBuffer, 0));
} catch (Exception e) {
throw new ZipException(e);
}
}
private void readCentralDirectory(NervousyncRandomAccessFile input)
throws ZipException {
if (this.endCentralDirectoryRecord == null) {
throw new ZipException("End Central Record is null!");
}
try {
List fileHeaderList = new ArrayList();
long offsetOfStartOfCentralDirectory = this.endCentralDirectoryRecord.getOffsetOfStartOfCentralDirectory();
int centralDirectoryEntryCount = this.endCentralDirectoryRecord.getTotalOfEntriesInCentralDirectory();
if (this.zip64Format) {
offsetOfStartOfCentralDirectory = this.zip64EndCentralDirectoryRecord.getOffsetStartCenDirWRTStartDiskNo();
centralDirectoryEntryCount = (int)this.zip64EndCentralDirectoryRecord.getTotalEntriesInCentralDirectory();
}
input.seek(offsetOfStartOfCentralDirectory);
long bufferSize = input.length() - offsetOfStartOfCentralDirectory;
byte[] readBuffer = new byte[(int)bufferSize];
input.read(readBuffer);
byte[] shortBuffer = new byte[2];
byte[] intBuffer = new byte[4];
int pos = 0;
for (int i = 0; i < centralDirectoryEntryCount; i++) {
GeneralFileHeader fileHeader = new GeneralFileHeader();
System.arraycopy(readBuffer, pos + 0, intBuffer, 0, 4);
int signature = RawUtils.readIntFromLittleEndian(intBuffer, 0);
if (signature != ZipConstants.CENSIG) {
throw new ZipException("Expected central directory entry not found! Index: " + i);
}
fileHeader.setSignature(signature);
// Made version
System.arraycopy(readBuffer, pos + 4, shortBuffer, 0, 2);
fileHeader.setMadeVersion(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
// Extract needed
System.arraycopy(readBuffer, pos + 6, shortBuffer, 0, 2);
fileHeader.setExtractNeeded(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
// Purpose bit flag
System.arraycopy(readBuffer, pos + 8, shortBuffer, 0, 2);
fileHeader.setFileNameUTF8Encoded(
(RawUtils.readShortFromLittleEndian(shortBuffer, 0) & ZipConstants.UFT8_NAMES_FLAG) != 0);
if ((shortBuffer[0] & 1) != 0) {
fileHeader.setEncrypted(true);
}
fileHeader.setGeneralPurposeFlag(shortBuffer.clone());
fileHeader.setDataDescriptorExists((shortBuffer[0] >> 3) == 1);
// Compression method
System.arraycopy(readBuffer, pos + 10, shortBuffer, 0, 2);
fileHeader.setCompressionMethod(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
// Last modify file time
System.arraycopy(readBuffer, pos + 12, intBuffer, 0, 4);
fileHeader.setLastModFileTime(RawUtils.readIntFromLittleEndian(intBuffer, 0));
// Crc32
System.arraycopy(readBuffer, pos + 16, intBuffer, 0, 4);
fileHeader.setCrc32(RawUtils.readIntFromLittleEndian(intBuffer, 0));
fileHeader.setCrcBuffer(intBuffer.clone());
// Compressed size
System.arraycopy(readBuffer, pos + 20, intBuffer, 0, 4);
fileHeader.setCompressedSize(RawUtils.readLongFromLittleEndian(readLongByteFromIntByte(intBuffer), 0));
// Original size
System.arraycopy(readBuffer, pos + 24, intBuffer, 0, 4);
fileHeader.setOriginalSize(RawUtils.readLongFromLittleEndian(readLongByteFromIntByte(intBuffer), 0));
// File name length
System.arraycopy(readBuffer, pos + 28, shortBuffer, 0, 2);
fileHeader.setFileNameLength(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
// Extra field length
System.arraycopy(readBuffer, pos + 30, shortBuffer, 0, 2);
fileHeader.setExtraFieldLength(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
// Comment length
System.arraycopy(readBuffer, pos + 32, shortBuffer, 0, 2);
fileHeader.setFileCommentLength(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
// Disk number of start
System.arraycopy(readBuffer, pos + 34, shortBuffer, 0, 2);
fileHeader.setDiskNumberStart(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
// Internal file attributes
System.arraycopy(readBuffer, pos + 36, shortBuffer, 0, 2);
fileHeader.setInternalFileAttr(shortBuffer.clone());
// External file attributes
System.arraycopy(readBuffer, pos + 38, intBuffer, 0, 4);
fileHeader.setExternalFileAttr(intBuffer.clone());
// Relative offset of local header
System.arraycopy(readBuffer, pos + 42, intBuffer, 0, 4);
fileHeader.setOffsetLocalHeader(
RawUtils.readLongFromLittleEndian(readLongByteFromIntByte(intBuffer), 0) & 0xFFFFFFFFL);
if (fileHeader.getFileNameLength() > 0) {
byte[] fileNameBuffer = new byte[fileHeader.getFileNameLength()];
System.arraycopy(readBuffer, pos + 46, fileNameBuffer, 0, fileHeader.getFileNameLength());
String entryPath = null;
if (this.fileNameCharset != null) {
entryPath = new String(fileNameBuffer, this.fileNameCharset);
} else {
if (fileHeader.isFileNameUTF8Encoded()) {
entryPath = new String(fileNameBuffer, Globals.DEFAULT_ENCODING);
} else {
entryPath = new String(fileNameBuffer);
}
}
if (entryPath.indexOf(ZipConstants.ZIP_ENTRY_SEPARATOR) >= 0) {
entryPath = entryPath.substring(entryPath.indexOf(ZipConstants.ZIP_ENTRY_SEPARATOR)
+ ZipConstants.ZIP_ENTRY_SEPARATOR.length());
}
fileHeader.setEntryPath(entryPath);
if (entryPath.endsWith(ZipConstants.ZIP_FILE_SEPARATOR)
|| entryPath.endsWith(Globals.DEFAULT_PAGE_SEPARATOR)) {
fileHeader.setDirectory(true);
} else {
fileHeader.setDirectory(Globals.DEFAULT_VALUE_BOOLEAN);
}
} else {
fileHeader.setEntryPath(null);
}
// Extra field
if (fileHeader.getExtraFieldLength() > 0) {
byte[] extraFieldBuffer = new byte[fileHeader.getExtraFieldLength()];
System.arraycopy(readBuffer, pos + 46 + fileHeader.getFileNameLength(), extraFieldBuffer, 0, fileHeader.getExtraFieldLength());
fileHeader.setExtraDataRecords(readExtraDataRecords(extraFieldBuffer, fileHeader.getExtraFieldLength()));
}
// Read zip64 extra data record if exists
readAndSaveZip64ExtendInfo(fileHeader);
// Read AES Extra data record if exists
readAndSaveAESExtraDataRecord(fileHeader);
if (fileHeader.getFileCommentLength() > 0) {
byte[] commentBuffer = new byte[fileHeader.getFileCommentLength()];
System.arraycopy(readBuffer, pos + 46 + fileHeader.getFileNameLength() + fileHeader.getExtraFieldLength(), commentBuffer, 0, fileHeader.getFileCommentLength());
fileHeader.setFileComment(new String(commentBuffer));
}
fileHeaderList.add(fileHeader);
pos += (46 + fileHeader.getFileNameLength() + fileHeader.getExtraFieldLength() + fileHeader.getFileCommentLength());
}
this.centralDirectory = new CentralDirectory();
this.centralDirectory.setFileHeaders(fileHeaderList);
System.arraycopy(readBuffer, pos, intBuffer, 0, 4);
int signature = RawUtils.readIntFromLittleEndian(intBuffer, 0);
if (signature == ZipConstants.DIGSIG) {
DigitalSignature digitalSignature = new DigitalSignature();
digitalSignature.setSignature(signature);
System.arraycopy(readBuffer, pos + 4, shortBuffer, 0, 2);
digitalSignature.setDataSize(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
if (digitalSignature.getDataSize() > 0) {
byte[] signatureDataBuffer = new byte[digitalSignature.getDataSize()];
System.arraycopy(readBuffer, pos + 6, signatureDataBuffer, 0, digitalSignature.getDataSize());
digitalSignature.setSignatureData(new String(signatureDataBuffer));
}
this.centralDirectory.setDigitalSignature(digitalSignature);
}
} catch (IOException e) {
throw new ZipException(e);
}
}
private void readZip64EndCentralDirectoryRecord(NervousyncRandomAccessFile input)
throws ZipException {
if (this.zip64EndCentralDirectoryLocator == null) {
throw new ZipException("Invalid zip64 end of central directory locator");
}
try {
long offsetZip64EndOfCentralDirectoryRecord =
this.zip64EndCentralDirectoryLocator.getOffsetZip64EndOfCentralDirectoryRecord();
if (offsetZip64EndOfCentralDirectoryRecord < 0L) {
throw new ZipException("Invalid offset for start of end of central directory record");
}
input.seek(offsetZip64EndOfCentralDirectoryRecord);
this.zip64EndCentralDirectoryRecord = new Zip64EndCentralDirectoryRecord();
byte[] readBuffer = new byte[56];
input.read(readBuffer);
byte[] shortBuffer = new byte[2];
byte[] intBuffer = new byte[4];
byte[] longBuffer = new byte[8];
System.arraycopy(readBuffer, 0, intBuffer, 0, 4);
int signature = RawUtils.readIntFromLittleEndian(intBuffer, 0);
if (signature != ZipConstants.ZIP64ENDCENDIRREC) {
throw new ZipException("Invalid signature for zip64 end of central directory record");
}
this.zip64EndCentralDirectoryRecord.setSignature(signature);
// Read size of zip64 end of central directory record
System.arraycopy(readBuffer, 4, longBuffer, 0, 8);
this.zip64EndCentralDirectoryRecord.setRecordSize(RawUtils.readLongFromLittleEndian(longBuffer, 0));
// Made version
System.arraycopy(readBuffer, 12, shortBuffer, 0, 2);
this.zip64EndCentralDirectoryRecord.setMadeVersion(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
// Extract needed
System.arraycopy(readBuffer, 14, shortBuffer, 0, 2);
this.zip64EndCentralDirectoryRecord.setExtractNeeded(RawUtils.readShortFromLittleEndian(shortBuffer, 0));
// Number of this disk
System.arraycopy(readBuffer, 16, intBuffer, 0, 4);
this.zip64EndCentralDirectoryRecord.setIndex(RawUtils.readIntFromLittleEndian(intBuffer, 0));
// Start of central directory
System.arraycopy(readBuffer, 20, intBuffer, 0, 4);
this.zip64EndCentralDirectoryRecord
.setStartOfCentralDirectory(RawUtils.readIntFromLittleEndian(intBuffer, 0));
// Total of entries in the central directory on this disk
System.arraycopy(readBuffer, 24, longBuffer, 0, 8);
this.zip64EndCentralDirectoryRecord
.setTotalEntriesInCentralDirectoryOnThisDisk(RawUtils.readLongFromLittleEndian(longBuffer, 0));
// Total of entries in the central directory
System.arraycopy(readBuffer, 32, longBuffer, 0, 8);
this.zip64EndCentralDirectoryRecord
.setTotalEntriesInCentralDirectory(RawUtils.readLongFromLittleEndian(longBuffer, 0));
// Size of the central directory
System.arraycopy(readBuffer, 40, longBuffer, 0, 8);
this.zip64EndCentralDirectoryRecord
.setSizeOfCentralDirectory(RawUtils.readLongFromLittleEndian(longBuffer, 0));
// Offset of start of central directory with respect to the starting
// disk number
System.arraycopy(readBuffer, 48, longBuffer, 0, 8);
this.zip64EndCentralDirectoryRecord
.setOffsetStartCenDirWRTStartDiskNo(RawUtils.readLongFromLittleEndian(longBuffer, 0));
// Zip64 extensible data sector
long extDataSize = zip64EndCentralDirectoryRecord.getRecordSize() - 44L;
if (extDataSize > 0) {
byte[] extensibleDataSector = new byte[(int) extDataSize];
input.read(extensibleDataSector);
this.zip64EndCentralDirectoryRecord.setExtensibleDataSector(extensibleDataSector);
}
if (this.zip64EndCentralDirectoryRecord.getIndex() > 0) {
this.splitArchive = true;
} else {
this.splitArchive = false;
}
} catch (IOException e) {
throw new ZipException(e);
}
}
private static void readAndSaveAESExtraDataRecord(FileHeader fileHeader) throws ZipException {
if (fileHeader == null) {
throw new ZipException("File header is null!");
}
if (fileHeader.getExtraDataRecords() != null && fileHeader.getExtraDataRecords().size() > 0) {
for (ExtraDataRecord extraDataRecord : fileHeader.getExtraDataRecords()) {
if (extraDataRecord != null) {
if (extraDataRecord.getHeader() == ZipConstants.AESSIG) {
if (extraDataRecord.getDataContent() == null) {
throw new ZipException("Corrput AES extra data records");
}
AESExtraDataRecord aesExtraDataRecord = new AESExtraDataRecord();
aesExtraDataRecord.setSignature(ZipConstants.AESSIG);
aesExtraDataRecord.setDataSize(extraDataRecord.getDataSize());
byte[] aesData = extraDataRecord.getDataContent();
aesExtraDataRecord.setVersionNumber(RawUtils.readShortFromLittleEndian(aesData, 0));
byte[] vendorIDBuffer = new byte[2];
System.arraycopy(aesData, 2, vendorIDBuffer, 0, 2);
aesExtraDataRecord.setVendorID(new String(vendorIDBuffer));
aesExtraDataRecord.setAesStrength((int) (aesData[4] & 0xFF));
aesExtraDataRecord.setCompressionMethod(RawUtils.readShortFromLittleEndian(aesData, 5));
fileHeader.setAesExtraDataRecord(aesExtraDataRecord);
fileHeader.setEncryptionMethod(ZipConstants.ENC_METHOD_AES);
break;
}
}
}
}
}
private static Zip64ExtendInfo readZip64ExtendInfo(List extraDataRecords, long originalSize,
long compressedSize, long offsetLocalHeader, int diskNumberStart) throws ZipException {
for (ExtraDataRecord extraDataRecord : extraDataRecords) {
Zip64ExtendInfo zip64ExtendInfo = new Zip64ExtendInfo();
if (extraDataRecord.getHeader() == 0x0001) {
if (extraDataRecord.getDataSize() <= 0) {
break;
}
byte[] intBuffer = new byte[4];
byte[] longBuffer = new byte[8];
int count = 0;
boolean addValue = false;
if (((originalSize & 0xFFFF) == 0xFFFF) && count < extraDataRecord.getDataSize()) {
System.arraycopy(extraDataRecord.getDataContent(), count, longBuffer, 0, 8);
zip64ExtendInfo.setOriginalSize(RawUtils.readLongFromLittleEndian(longBuffer, 0));
count += 8;
addValue = true;
}
if (((compressedSize & 0xFFFF) == 0xFFFF) && count < extraDataRecord.getDataSize()) {
System.arraycopy(extraDataRecord.getDataContent(), count, longBuffer, 0, 8);
zip64ExtendInfo.setCompressedSize(RawUtils.readLongFromLittleEndian(longBuffer, 0));
count += 8;
addValue = true;
}
if (((offsetLocalHeader & 0xFFFF) == 0xFFFF) && count < extraDataRecord.getDataSize()) {
System.arraycopy(extraDataRecord.getDataContent(), count, longBuffer, 0, 8);
zip64ExtendInfo.setOffsetLocalHeader(RawUtils.readLongFromLittleEndian(longBuffer, 0));
count += 8;
addValue = true;
}
if (((diskNumberStart & 0xFFFF) == 0xFFFF) && count < extraDataRecord.getDataSize()) {
System.arraycopy(extraDataRecord.getDataContent(), count, intBuffer, 0, 4);
zip64ExtendInfo.setDiskNumberStart(RawUtils.readIntFromLittleEndian(intBuffer, 0));
count += 8;
addValue = true;
}
if (addValue) {
return zip64ExtendInfo;
}
break;
}
}
return null;
}
private static void readAndSaveZip64ExtendInfo(FileHeader fileHeader) throws ZipException {
if (fileHeader == null) {
throw new ZipException("File header is null");
}
if (fileHeader instanceof GeneralFileHeader) {
if (fileHeader.getExtraDataRecords() != null && fileHeader.getExtraDataRecords().size() > 0) {
Zip64ExtendInfo zip64ExtendInfo = readZip64ExtendInfo(fileHeader.getExtraDataRecords(),
fileHeader.getOriginalSize(), fileHeader.getCompressedSize(),
((GeneralFileHeader) fileHeader).getOffsetLocalHeader(),
((GeneralFileHeader) fileHeader).getDiskNumberStart());
if (zip64ExtendInfo != null) {
fileHeader.setZip64ExtendInfo(zip64ExtendInfo);
if (zip64ExtendInfo.getOriginalSize() != -1) {
fileHeader.setOriginalSize(zip64ExtendInfo.getOriginalSize());
}
if (zip64ExtendInfo.getCompressedSize() != -1) {
fileHeader.setCompressedSize(zip64ExtendInfo.getCompressedSize());
}
if (zip64ExtendInfo.getOffsetLocalHeader() != -1) {
((GeneralFileHeader) fileHeader).setOffsetLocalHeader(zip64ExtendInfo.getOffsetLocalHeader());
}
if (zip64ExtendInfo.getDiskNumberStart() != -1) {
((GeneralFileHeader) fileHeader).setDiskNumberStart(zip64ExtendInfo.getDiskNumberStart());
}
}
}
} else if (fileHeader instanceof LocalFileHeader) {
if (fileHeader.getExtraDataRecords() == null || fileHeader.getExtraDataRecords().size() == 0) {
return;
}
Zip64ExtendInfo zip64ExtendInfo = readZip64ExtendInfo(fileHeader.getExtraDataRecords(),
fileHeader.getOriginalSize(), fileHeader.getCompressedSize(), Globals.DEFAULT_VALUE_LONG,
Globals.DEFAULT_VALUE_INT);
if (zip64ExtendInfo != null) {
fileHeader.setZip64ExtendInfo(zip64ExtendInfo);
if (zip64ExtendInfo.getOriginalSize() != -1) {
fileHeader.setOriginalSize(zip64ExtendInfo.getOriginalSize());
}
if (zip64ExtendInfo.getCompressedSize() != -1) {
fileHeader.setCompressedSize(zip64ExtendInfo.getCompressedSize());
}
}
} else {
throw new ZipException("Unknown file header");
}
}
private static List readExtraDataRecords(byte[] extraFieldBuffer, int extraFieldLength)
throws ZipException {
int count = 0;
List extraDataRecords = new ArrayList();
while (count < extraFieldLength) {
ExtraDataRecord extraDataRecord = new ExtraDataRecord();
extraDataRecord.setHeader(RawUtils.readShortFromLittleEndian(extraFieldBuffer, count));
count += 2;
int dataSize = RawUtils.readShortFromLittleEndian(extraFieldBuffer, count);
if ((dataSize + 2) > extraFieldLength) {
dataSize = RawUtils.readShortFromBigEndian(extraFieldBuffer, count);
if ((dataSize + 2) > extraFieldLength) {
break;
}
}
extraDataRecord.setDataSize(dataSize);
count += 2;
if (dataSize > 0) {
byte[] dataContent = new byte[dataSize];
System.arraycopy(extraFieldBuffer, count, dataContent, 0, dataSize);
extraDataRecord.setDataContent(dataContent);
}
count += dataSize;
extraDataRecords.add(extraDataRecord);
}
if (extraDataRecords.size() > 0) {
return extraDataRecords;
}
return null;
}
private static int countNumberOfFileHeaderEntriesOnDisk(List fileHeaders, int numOfDisk)
throws ZipException {
if (fileHeaders == null) {
throw new ZipException("file headers are null, cannot calculate number of entries on this disk");
}
int noEntries = 0;
for (GeneralFileHeader generalFileHeader : fileHeaders) {
if (generalFileHeader.getDiskNumberStart() == numOfDisk) {
noEntries++;
}
}
return noEntries;
}
private static byte[] readLongByteFromIntByte(byte[] intByte) throws ZipException {
if (intByte == null) {
throw new ZipException("int bytes is null");
}
if (intByte.length != 4) {
throw new ZipException("Invalid byte length");
}
byte[] longBuffer = { intByte[0], intByte[1], intByte[2], intByte[3], 0, 0, 0, 0 };
return longBuffer;
}
private static int readIntFromDataInput(NervousyncRandomAccessFile input, byte[] bytes) throws ZipException {
try {
input.read(bytes, 0, 4);
} catch (IOException e) {
throw new ZipException(e);
}
return RawUtils.readIntFromLittleEndian(bytes, 0);
}
private static void setFileReadOnly(File file) throws ZipException {
if (file == null) {
throw new ZipException("input file is null. cannot set read only file attribute");
}
if (file.exists()) {
file.setReadOnly();
}
}
private static void setFileLastModify(File file, long lastModify) throws ZipException {
if (file == null) {
throw new ZipException("input file is null. cannot set read only file attribute");
}
if (lastModify < 0L) {
throw new ZipException("last modify time invalid");
}
if (file.exists()) {
file.setLastModified(lastModify);
}
}
}