+add libraries
This commit is contained in:
parent
b1b9dc12fa
commit
5c610d9922
55 changed files with 11770 additions and 0 deletions
1119
src/net/lingala/zip4j/core/HeaderReader.java
Normal file
1119
src/net/lingala/zip4j/core/HeaderReader.java
Normal file
File diff suppressed because it is too large
Load diff
943
src/net/lingala/zip4j/core/HeaderWriter.java
Normal file
943
src/net/lingala/zip4j/core/HeaderWriter.java
Normal file
|
@ -0,0 +1,943 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.core;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.io.SplitOutputStream;
|
||||
import net.lingala.zip4j.model.AESExtraDataRecord;
|
||||
import net.lingala.zip4j.model.FileHeader;
|
||||
import net.lingala.zip4j.model.LocalFileHeader;
|
||||
import net.lingala.zip4j.model.Zip64EndCentralDirLocator;
|
||||
import net.lingala.zip4j.model.Zip64EndCentralDirRecord;
|
||||
import net.lingala.zip4j.model.ZipModel;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
import net.lingala.zip4j.util.Raw;
|
||||
import net.lingala.zip4j.util.Zip4jUtil;
|
||||
|
||||
public class HeaderWriter {
|
||||
|
||||
private final int ZIP64_EXTRA_BUF = 50;
|
||||
|
||||
public int writeLocalFileHeader(ZipModel zipModel, LocalFileHeader localFileHeader,
|
||||
OutputStream outputStream) throws ZipException {
|
||||
if (localFileHeader == null) {
|
||||
throw new ZipException("input parameters are null, cannot write local file header");
|
||||
}
|
||||
|
||||
try {
|
||||
ArrayList byteArrayList = new ArrayList();
|
||||
|
||||
byte[] shortByte = new byte[2];
|
||||
byte[] intByte = new byte[4];
|
||||
byte[] longByte = new byte[8];
|
||||
byte[] emptyLongByte = {0,0,0,0,0,0,0,0};
|
||||
|
||||
Raw.writeIntLittleEndian(intByte, 0, localFileHeader.getSignature());
|
||||
copyByteArrayToArrayList(intByte, byteArrayList);
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)localFileHeader.getVersionNeededToExtract());
|
||||
copyByteArrayToArrayList(shortByte, byteArrayList);
|
||||
//General Purpose bit flags
|
||||
copyByteArrayToArrayList(localFileHeader.getGeneralPurposeFlag(), byteArrayList);
|
||||
//Compression Method
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)localFileHeader.getCompressionMethod());
|
||||
copyByteArrayToArrayList(shortByte, byteArrayList);
|
||||
//File modified time
|
||||
int dateTime = localFileHeader.getLastModFileTime();
|
||||
Raw.writeIntLittleEndian(intByte, 0, (int)dateTime);
|
||||
copyByteArrayToArrayList(intByte, byteArrayList);
|
||||
//Skip crc for now - this field will be updated after data is compressed
|
||||
Raw.writeIntLittleEndian(intByte, 0, (int)localFileHeader.getCrc32());
|
||||
copyByteArrayToArrayList(intByte, byteArrayList);
|
||||
boolean writingZip64Rec = false;
|
||||
|
||||
//compressed & uncompressed size
|
||||
long uncompressedSize = localFileHeader.getUncompressedSize();
|
||||
if (uncompressedSize + ZIP64_EXTRA_BUF >= InternalZipConstants.ZIP_64_LIMIT) {
|
||||
Raw.writeLongLittleEndian(longByte, 0, InternalZipConstants.ZIP_64_LIMIT);
|
||||
System.arraycopy(longByte, 0, intByte, 0, 4);
|
||||
|
||||
//Set the uncompressed size to ZipConstants.ZIP_64_LIMIT as
|
||||
//these values will be stored in Zip64 extra record
|
||||
copyByteArrayToArrayList(intByte, byteArrayList);
|
||||
|
||||
copyByteArrayToArrayList(intByte, byteArrayList);
|
||||
zipModel.setZip64Format(true);
|
||||
writingZip64Rec = true;
|
||||
localFileHeader.setWriteComprSizeInZip64ExtraRecord(true);
|
||||
} else {
|
||||
Raw.writeLongLittleEndian(longByte, 0, localFileHeader.getCompressedSize());
|
||||
System.arraycopy(longByte, 0, intByte, 0, 4);
|
||||
copyByteArrayToArrayList(intByte, byteArrayList);
|
||||
|
||||
Raw.writeLongLittleEndian(longByte, 0, localFileHeader.getUncompressedSize());
|
||||
System.arraycopy(longByte, 0, intByte, 0, 4);
|
||||
//Raw.writeIntLittleEndian(intByte, 0, (int)localFileHeader.getUncompressedSize());
|
||||
copyByteArrayToArrayList(intByte, byteArrayList);
|
||||
|
||||
localFileHeader.setWriteComprSizeInZip64ExtraRecord(false);
|
||||
}
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)localFileHeader.getFileNameLength());
|
||||
copyByteArrayToArrayList(shortByte, byteArrayList);
|
||||
// extra field length
|
||||
int extraFieldLength = 0;
|
||||
if (writingZip64Rec) {
|
||||
extraFieldLength += 20;
|
||||
}
|
||||
if (localFileHeader.getAesExtraDataRecord() != null) {
|
||||
extraFieldLength += 11;
|
||||
}
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)(extraFieldLength));
|
||||
copyByteArrayToArrayList(shortByte, byteArrayList);
|
||||
if (Zip4jUtil.isStringNotNullAndNotEmpty(zipModel.getFileNameCharset())) {
|
||||
byte[] fileNameBytes = localFileHeader.getFileName().getBytes(zipModel.getFileNameCharset());
|
||||
copyByteArrayToArrayList(fileNameBytes, byteArrayList);
|
||||
} else {
|
||||
copyByteArrayToArrayList(Zip4jUtil.convertCharset(localFileHeader.getFileName()), byteArrayList);
|
||||
}
|
||||
|
||||
//Zip64 should be the first extra data record that should be written
|
||||
//This is NOT according to any specification but if this is changed
|
||||
//then take care of updateLocalFileHeader for compressed size
|
||||
if (writingZip64Rec) {
|
||||
|
||||
|
||||
//Zip64 header
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)InternalZipConstants.EXTRAFIELDZIP64LENGTH);
|
||||
copyByteArrayToArrayList(shortByte, byteArrayList);
|
||||
//Zip64 extra data record size
|
||||
//hardcoded it to 16 for local file header as we will just write
|
||||
//compressed and uncompressed file sizes
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)16);
|
||||
copyByteArrayToArrayList(shortByte, byteArrayList);
|
||||
//uncompressed size
|
||||
Raw.writeLongLittleEndian(longByte, 0, localFileHeader.getUncompressedSize());
|
||||
copyByteArrayToArrayList(longByte, byteArrayList);
|
||||
//set compressed size to 0 for now
|
||||
copyByteArrayToArrayList(emptyLongByte, byteArrayList);
|
||||
}
|
||||
|
||||
if (localFileHeader.getAesExtraDataRecord() != null) {
|
||||
AESExtraDataRecord aesExtraDataRecord = localFileHeader.getAesExtraDataRecord();
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)aesExtraDataRecord.getSignature());
|
||||
copyByteArrayToArrayList(shortByte, byteArrayList);
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)aesExtraDataRecord.getDataSize());
|
||||
copyByteArrayToArrayList(shortByte, byteArrayList);
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)aesExtraDataRecord.getVersionNumber());
|
||||
copyByteArrayToArrayList(shortByte, byteArrayList);
|
||||
|
||||
copyByteArrayToArrayList(aesExtraDataRecord.getVendorID().getBytes(), byteArrayList);
|
||||
|
||||
byte[] aesStrengthBytes = new byte[1];
|
||||
aesStrengthBytes[0] = (byte)aesExtraDataRecord.getAesStrength();
|
||||
copyByteArrayToArrayList(aesStrengthBytes, byteArrayList);
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)aesExtraDataRecord.getCompressionMethod());
|
||||
copyByteArrayToArrayList(shortByte, byteArrayList);
|
||||
}
|
||||
byte[] lhBytes = byteArrayListToByteArray(byteArrayList);
|
||||
outputStream.write(lhBytes);
|
||||
return lhBytes.length;
|
||||
} catch (ZipException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int writeExtendedLocalHeader(LocalFileHeader localFileHeader,
|
||||
OutputStream outputStream) throws ZipException, IOException {
|
||||
if (localFileHeader == null || outputStream == null) {
|
||||
throw new ZipException("input parameters is null, cannot write extended local header");
|
||||
}
|
||||
|
||||
ArrayList byteArrayList = new ArrayList();
|
||||
byte[] intByte = new byte[4];
|
||||
|
||||
//Extended local file header signature
|
||||
Raw.writeIntLittleEndian(intByte, 0, (int)InternalZipConstants.EXTSIG);
|
||||
copyByteArrayToArrayList(intByte, byteArrayList);
|
||||
|
||||
//CRC
|
||||
Raw.writeIntLittleEndian(intByte, 0, (int)localFileHeader.getCrc32());
|
||||
copyByteArrayToArrayList(intByte, byteArrayList);
|
||||
|
||||
//compressed size
|
||||
long compressedSize = localFileHeader.getCompressedSize();
|
||||
if (compressedSize >= Integer.MAX_VALUE) {
|
||||
compressedSize = Integer.MAX_VALUE;
|
||||
}
|
||||
Raw.writeIntLittleEndian(intByte, 0, (int)compressedSize);
|
||||
copyByteArrayToArrayList(intByte, byteArrayList);
|
||||
|
||||
//uncompressed size
|
||||
long uncompressedSize = localFileHeader.getUncompressedSize();
|
||||
if (uncompressedSize >= Integer.MAX_VALUE) {
|
||||
uncompressedSize = Integer.MAX_VALUE;
|
||||
}
|
||||
Raw.writeIntLittleEndian(intByte, 0, (int)uncompressedSize);
|
||||
copyByteArrayToArrayList(intByte, byteArrayList);
|
||||
|
||||
byte[] extLocHdrBytes = byteArrayListToByteArray(byteArrayList);
|
||||
outputStream.write(extLocHdrBytes);
|
||||
return extLocHdrBytes.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes zip header data and writes this data to the zip file
|
||||
* @param zipModel
|
||||
* @param outputStream
|
||||
* @throws ZipException
|
||||
*/
|
||||
public void finalizeZipFile(ZipModel zipModel,
|
||||
OutputStream outputStream) throws ZipException {
|
||||
if (zipModel == null || outputStream == null) {
|
||||
throw new ZipException("input parameters is null, cannot finalize zip file");
|
||||
}
|
||||
|
||||
try {
|
||||
processHeaderData(zipModel, outputStream);
|
||||
|
||||
long offsetCentralDir = zipModel.getEndCentralDirRecord().getOffsetOfStartOfCentralDir();
|
||||
|
||||
List headerBytesList = new ArrayList();
|
||||
|
||||
int sizeOfCentralDir = writeCentralDirectory(zipModel, outputStream, headerBytesList);
|
||||
|
||||
if (zipModel.isZip64Format()) {
|
||||
if (zipModel.getZip64EndCentralDirRecord() == null) {
|
||||
zipModel.setZip64EndCentralDirRecord(new Zip64EndCentralDirRecord());
|
||||
}
|
||||
if (zipModel.getZip64EndCentralDirLocator() == null) {
|
||||
zipModel.setZip64EndCentralDirLocator(new Zip64EndCentralDirLocator());
|
||||
}
|
||||
|
||||
zipModel.getZip64EndCentralDirLocator().setOffsetZip64EndOfCentralDirRec(offsetCentralDir + sizeOfCentralDir);
|
||||
if (outputStream instanceof SplitOutputStream) {
|
||||
zipModel.getZip64EndCentralDirLocator().setNoOfDiskStartOfZip64EndOfCentralDirRec(((SplitOutputStream)outputStream).getCurrSplitFileCounter());
|
||||
zipModel.getZip64EndCentralDirLocator().setTotNumberOfDiscs(((SplitOutputStream)outputStream).getCurrSplitFileCounter() + 1);
|
||||
} else {
|
||||
zipModel.getZip64EndCentralDirLocator().setNoOfDiskStartOfZip64EndOfCentralDirRec(0);
|
||||
zipModel.getZip64EndCentralDirLocator().setTotNumberOfDiscs(1);
|
||||
}
|
||||
|
||||
writeZip64EndOfCentralDirectoryRecord(zipModel, outputStream, sizeOfCentralDir, offsetCentralDir, headerBytesList);
|
||||
|
||||
writeZip64EndOfCentralDirectoryLocator(zipModel, outputStream, headerBytesList);
|
||||
}
|
||||
|
||||
writeEndOfCentralDirectoryRecord(zipModel, outputStream, sizeOfCentralDir, offsetCentralDir, headerBytesList);
|
||||
|
||||
writeZipHeaderBytes(zipModel, outputStream, byteArrayListToByteArray(headerBytesList));
|
||||
} catch (ZipException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes zip header data and writes this data to the zip file without any validations.
|
||||
* This process is not intended to use for normal operations (adding, deleting, etc) of a zip file.
|
||||
* This method is used when certain validations need to be skipped (ex: Merging split zip files,
|
||||
* adding comment to a zip file, etc)
|
||||
* @param zipModel
|
||||
* @param outputStream
|
||||
* @throws ZipException
|
||||
*/
|
||||
public void finalizeZipFileWithoutValidations(ZipModel zipModel, OutputStream outputStream) throws ZipException {
|
||||
if (zipModel == null || outputStream == null) {
|
||||
throw new ZipException("input parameters is null, cannot finalize zip file without validations");
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
List headerBytesList = new ArrayList();
|
||||
|
||||
long offsetCentralDir = zipModel.getEndCentralDirRecord().getOffsetOfStartOfCentralDir();
|
||||
|
||||
int sizeOfCentralDir = writeCentralDirectory(zipModel, outputStream, headerBytesList);
|
||||
|
||||
if (zipModel.isZip64Format()) {
|
||||
if (zipModel.getZip64EndCentralDirRecord() == null) {
|
||||
zipModel.setZip64EndCentralDirRecord(new Zip64EndCentralDirRecord());
|
||||
}
|
||||
if (zipModel.getZip64EndCentralDirLocator() == null) {
|
||||
zipModel.setZip64EndCentralDirLocator(new Zip64EndCentralDirLocator());
|
||||
}
|
||||
|
||||
zipModel.getZip64EndCentralDirLocator().setOffsetZip64EndOfCentralDirRec(offsetCentralDir + sizeOfCentralDir);
|
||||
|
||||
writeZip64EndOfCentralDirectoryRecord(zipModel, outputStream, sizeOfCentralDir, offsetCentralDir, headerBytesList);
|
||||
writeZip64EndOfCentralDirectoryLocator(zipModel, outputStream, headerBytesList);
|
||||
}
|
||||
|
||||
writeEndOfCentralDirectoryRecord(zipModel, outputStream, sizeOfCentralDir, offsetCentralDir, headerBytesList);
|
||||
|
||||
writeZipHeaderBytes(zipModel, outputStream, byteArrayListToByteArray(headerBytesList));
|
||||
} catch(ZipException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the zip header data to the zip file
|
||||
* @param outputStream
|
||||
* @param buff
|
||||
* @throws ZipException
|
||||
*/
|
||||
private void writeZipHeaderBytes(ZipModel zipModel, OutputStream outputStream, byte[] buff) throws ZipException {
|
||||
if (buff == null) {
|
||||
throw new ZipException("invalid buff to write as zip headers");
|
||||
}
|
||||
|
||||
try {
|
||||
if (outputStream instanceof SplitOutputStream) {
|
||||
if (((SplitOutputStream)outputStream).checkBuffSizeAndStartNextSplitFile(buff.length)) {
|
||||
finalizeZipFile(zipModel, outputStream);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
outputStream.write(buff);
|
||||
} catch (IOException e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the header data in the zip model
|
||||
* @param zipModel
|
||||
* @param outputStream
|
||||
* @throws ZipException
|
||||
*/
|
||||
private void processHeaderData(ZipModel zipModel, OutputStream outputStream) throws ZipException {
|
||||
try {
|
||||
int currSplitFileCounter = 0;
|
||||
if (outputStream instanceof SplitOutputStream) {
|
||||
zipModel.getEndCentralDirRecord().setOffsetOfStartOfCentralDir(
|
||||
((SplitOutputStream)outputStream).getFilePointer());
|
||||
currSplitFileCounter = ((SplitOutputStream)outputStream).getCurrSplitFileCounter();
|
||||
|
||||
}
|
||||
|
||||
if (zipModel.isZip64Format()) {
|
||||
if (zipModel.getZip64EndCentralDirRecord() == null) {
|
||||
zipModel.setZip64EndCentralDirRecord(new Zip64EndCentralDirRecord());
|
||||
}
|
||||
if (zipModel.getZip64EndCentralDirLocator() == null) {
|
||||
zipModel.setZip64EndCentralDirLocator(new Zip64EndCentralDirLocator());
|
||||
}
|
||||
|
||||
zipModel.getZip64EndCentralDirLocator().setNoOfDiskStartOfZip64EndOfCentralDirRec(currSplitFileCounter);
|
||||
zipModel.getZip64EndCentralDirLocator().setTotNumberOfDiscs(currSplitFileCounter + 1);
|
||||
}
|
||||
zipModel.getEndCentralDirRecord().setNoOfThisDisk(currSplitFileCounter);
|
||||
zipModel.getEndCentralDirRecord().setNoOfThisDiskStartOfCentralDir(currSplitFileCounter);
|
||||
} catch (IOException e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes central directory header data to an array list
|
||||
* @param zipModel
|
||||
* @param outputStream
|
||||
* @param headerBytesList
|
||||
* @return size of central directory
|
||||
* @throws ZipException
|
||||
*/
|
||||
private int writeCentralDirectory(ZipModel zipModel,
|
||||
OutputStream outputStream, List headerBytesList) throws ZipException {
|
||||
if (zipModel == null || outputStream == null) {
|
||||
throw new ZipException("input parameters is null, cannot write central directory");
|
||||
}
|
||||
|
||||
if (zipModel.getCentralDirectory() == null ||
|
||||
zipModel.getCentralDirectory().getFileHeaders() == null ||
|
||||
zipModel.getCentralDirectory().getFileHeaders().size() <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sizeOfCentralDir = 0;
|
||||
for (int i = 0; i < zipModel.getCentralDirectory().getFileHeaders().size(); i++) {
|
||||
FileHeader fileHeader = (FileHeader)zipModel.getCentralDirectory().getFileHeaders().get(i);
|
||||
int sizeOfFileHeader = writeFileHeader(zipModel, fileHeader, outputStream, headerBytesList);
|
||||
sizeOfCentralDir += sizeOfFileHeader;
|
||||
}
|
||||
return sizeOfCentralDir;
|
||||
}
|
||||
|
||||
private int writeFileHeader(ZipModel zipModel, FileHeader fileHeader,
|
||||
OutputStream outputStream, List headerBytesList) throws ZipException {
|
||||
|
||||
if (fileHeader == null || outputStream == null) {
|
||||
throw new ZipException("input parameters is null, cannot write local file header");
|
||||
}
|
||||
|
||||
try {
|
||||
int sizeOfFileHeader = 0;
|
||||
|
||||
byte[] shortByte = new byte[2];
|
||||
byte[] intByte = new byte[4];
|
||||
byte[] longByte = new byte[8];
|
||||
final byte[] emptyShortByte = {0,0};
|
||||
final byte[] emptyIntByte = {0,0,0,0};
|
||||
|
||||
boolean writeZip64FileSize = false;
|
||||
boolean writeZip64OffsetLocalHeader = false;
|
||||
|
||||
Raw.writeIntLittleEndian(intByte, 0, fileHeader.getSignature());
|
||||
copyByteArrayToArrayList(intByte, headerBytesList);
|
||||
sizeOfFileHeader += 4;
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)fileHeader.getVersionMadeBy());
|
||||
copyByteArrayToArrayList(shortByte, headerBytesList);
|
||||
sizeOfFileHeader += 2;
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)fileHeader.getVersionNeededToExtract());
|
||||
copyByteArrayToArrayList(shortByte, headerBytesList);
|
||||
sizeOfFileHeader += 2;
|
||||
|
||||
copyByteArrayToArrayList(fileHeader.getGeneralPurposeFlag(), headerBytesList);
|
||||
sizeOfFileHeader += 2;
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)fileHeader.getCompressionMethod());
|
||||
copyByteArrayToArrayList(shortByte, headerBytesList);
|
||||
sizeOfFileHeader += 2;
|
||||
|
||||
int dateTime = fileHeader.getLastModFileTime();
|
||||
Raw.writeIntLittleEndian(intByte, 0, dateTime);
|
||||
copyByteArrayToArrayList(intByte, headerBytesList);
|
||||
sizeOfFileHeader += 4;
|
||||
|
||||
Raw.writeIntLittleEndian(intByte, 0, (int)(fileHeader.getCrc32()));
|
||||
copyByteArrayToArrayList(intByte, headerBytesList);
|
||||
sizeOfFileHeader += 4;
|
||||
|
||||
if (fileHeader.getCompressedSize() >= InternalZipConstants.ZIP_64_LIMIT ||
|
||||
fileHeader.getUncompressedSize() + ZIP64_EXTRA_BUF >= InternalZipConstants.ZIP_64_LIMIT) {
|
||||
Raw.writeLongLittleEndian(longByte, 0, InternalZipConstants.ZIP_64_LIMIT);
|
||||
System.arraycopy(longByte, 0, intByte, 0, 4);
|
||||
|
||||
copyByteArrayToArrayList(intByte, headerBytesList);
|
||||
sizeOfFileHeader += 4;
|
||||
|
||||
copyByteArrayToArrayList(intByte, headerBytesList);
|
||||
sizeOfFileHeader += 4;
|
||||
|
||||
writeZip64FileSize = true;
|
||||
} else {
|
||||
Raw.writeLongLittleEndian(longByte, 0, fileHeader.getCompressedSize());
|
||||
System.arraycopy(longByte, 0, intByte, 0, 4);
|
||||
// Raw.writeIntLittleEndian(intByte, 0, (int)fileHeader.getCompressedSize());
|
||||
copyByteArrayToArrayList(intByte, headerBytesList);
|
||||
sizeOfFileHeader += 4;
|
||||
|
||||
Raw.writeLongLittleEndian(longByte, 0, fileHeader.getUncompressedSize());
|
||||
System.arraycopy(longByte, 0, intByte, 0, 4);
|
||||
// Raw.writeIntLittleEndian(intByte, 0, (int)fileHeader.getUncompressedSize());
|
||||
copyByteArrayToArrayList(intByte, headerBytesList);
|
||||
sizeOfFileHeader += 4;
|
||||
}
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)fileHeader.getFileNameLength());
|
||||
copyByteArrayToArrayList(shortByte, 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 (fileHeader.getOffsetLocalHeader() > InternalZipConstants.ZIP_64_LIMIT) {
|
||||
Raw.writeLongLittleEndian(longByte, 0, InternalZipConstants.ZIP_64_LIMIT);
|
||||
System.arraycopy(longByte, 0, offsetLocalHeaderBytes, 0, 4);
|
||||
writeZip64OffsetLocalHeader = true;
|
||||
} else {
|
||||
Raw.writeLongLittleEndian(longByte, 0, fileHeader.getOffsetLocalHeader());
|
||||
System.arraycopy(longByte, 0, offsetLocalHeaderBytes, 0, 4);
|
||||
}
|
||||
|
||||
// extra field length
|
||||
int extraFieldLength = 0;
|
||||
if (writeZip64FileSize || writeZip64OffsetLocalHeader) {
|
||||
extraFieldLength += 4;
|
||||
if (writeZip64FileSize)
|
||||
extraFieldLength += 16;
|
||||
if (writeZip64OffsetLocalHeader)
|
||||
extraFieldLength += 8;
|
||||
}
|
||||
if (fileHeader.getAesExtraDataRecord() != null) {
|
||||
extraFieldLength += 11;
|
||||
}
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)(extraFieldLength));
|
||||
copyByteArrayToArrayList(shortByte, headerBytesList);
|
||||
sizeOfFileHeader += 2;
|
||||
|
||||
//Skip file comment length for now
|
||||
copyByteArrayToArrayList(emptyShortByte, headerBytesList);
|
||||
sizeOfFileHeader += 2;
|
||||
|
||||
//Skip disk number start for now
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)(fileHeader.getDiskNumberStart()));
|
||||
copyByteArrayToArrayList(shortByte, headerBytesList);
|
||||
sizeOfFileHeader += 2;
|
||||
|
||||
//Skip internal file attributes for now
|
||||
copyByteArrayToArrayList(emptyShortByte, headerBytesList);
|
||||
sizeOfFileHeader += 2;
|
||||
|
||||
//External file attributes
|
||||
if (fileHeader.getExternalFileAttr() != null) {
|
||||
copyByteArrayToArrayList(fileHeader.getExternalFileAttr(), headerBytesList);
|
||||
} else {
|
||||
copyByteArrayToArrayList(emptyIntByte, headerBytesList);
|
||||
}
|
||||
sizeOfFileHeader += 4;
|
||||
|
||||
//offset local header
|
||||
//this data is computed above
|
||||
copyByteArrayToArrayList(offsetLocalHeaderBytes, headerBytesList);
|
||||
sizeOfFileHeader += 4;
|
||||
|
||||
if (Zip4jUtil.isStringNotNullAndNotEmpty(zipModel.getFileNameCharset())) {
|
||||
byte[] fileNameBytes = fileHeader.getFileName().getBytes(zipModel.getFileNameCharset());
|
||||
copyByteArrayToArrayList(fileNameBytes, headerBytesList);
|
||||
sizeOfFileHeader += fileNameBytes.length;
|
||||
} else {
|
||||
copyByteArrayToArrayList(Zip4jUtil.convertCharset(fileHeader.getFileName()), headerBytesList);
|
||||
sizeOfFileHeader += Zip4jUtil.getEncodedStringLength(fileHeader.getFileName());
|
||||
}
|
||||
|
||||
if (writeZip64FileSize || writeZip64OffsetLocalHeader) {
|
||||
zipModel.setZip64Format(true);
|
||||
|
||||
//Zip64 header
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)InternalZipConstants.EXTRAFIELDZIP64LENGTH);
|
||||
copyByteArrayToArrayList(shortByte, headerBytesList);
|
||||
sizeOfFileHeader += 2;
|
||||
|
||||
//Zip64 extra data record size
|
||||
int dataSize = 0;
|
||||
|
||||
if (writeZip64FileSize) {
|
||||
dataSize += 16;
|
||||
}
|
||||
if (writeZip64OffsetLocalHeader) {
|
||||
dataSize += 8;
|
||||
}
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)dataSize);
|
||||
copyByteArrayToArrayList(shortByte, headerBytesList);
|
||||
sizeOfFileHeader += 2;
|
||||
|
||||
if (writeZip64FileSize) {
|
||||
Raw.writeLongLittleEndian(longByte, 0, fileHeader.getUncompressedSize());
|
||||
copyByteArrayToArrayList(longByte, headerBytesList);
|
||||
sizeOfFileHeader += 8;
|
||||
|
||||
Raw.writeLongLittleEndian(longByte, 0, fileHeader.getCompressedSize());
|
||||
copyByteArrayToArrayList(longByte, headerBytesList);
|
||||
sizeOfFileHeader += 8;
|
||||
}
|
||||
|
||||
if (writeZip64OffsetLocalHeader) {
|
||||
Raw.writeLongLittleEndian(longByte, 0, fileHeader.getOffsetLocalHeader());
|
||||
copyByteArrayToArrayList(longByte, headerBytesList);
|
||||
sizeOfFileHeader += 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileHeader.getAesExtraDataRecord() != null) {
|
||||
AESExtraDataRecord aesExtraDataRecord = fileHeader.getAesExtraDataRecord();
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)aesExtraDataRecord.getSignature());
|
||||
copyByteArrayToArrayList(shortByte, headerBytesList);
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)aesExtraDataRecord.getDataSize());
|
||||
copyByteArrayToArrayList(shortByte, headerBytesList);
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)aesExtraDataRecord.getVersionNumber());
|
||||
copyByteArrayToArrayList(shortByte, headerBytesList);
|
||||
|
||||
copyByteArrayToArrayList(aesExtraDataRecord.getVendorID().getBytes(), headerBytesList);
|
||||
|
||||
byte[] aesStrengthBytes = new byte[1];
|
||||
aesStrengthBytes[0] = (byte)aesExtraDataRecord.getAesStrength();
|
||||
copyByteArrayToArrayList(aesStrengthBytes, headerBytesList);
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)aesExtraDataRecord.getCompressionMethod());
|
||||
copyByteArrayToArrayList(shortByte, headerBytesList);
|
||||
|
||||
sizeOfFileHeader += 11;
|
||||
}
|
||||
|
||||
// outputStream.write(byteArrayListToByteArray(headerBytesList));
|
||||
|
||||
return sizeOfFileHeader;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeZip64EndOfCentralDirectoryRecord(ZipModel zipModel,
|
||||
OutputStream outputStream, int sizeOfCentralDir,
|
||||
long offsetCentralDir, List headerBytesList) throws ZipException {
|
||||
if (zipModel == null || outputStream == null) {
|
||||
throw new ZipException("zip model or output stream is null, cannot write zip64 end of central directory record");
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
byte[] shortByte = new byte[2];
|
||||
byte[] emptyShortByte = {0,0};
|
||||
byte[] intByte = new byte[4];
|
||||
byte[] longByte = new byte[8];
|
||||
|
||||
//zip64 end of central dir signature
|
||||
Raw.writeIntLittleEndian(intByte, 0, (int)InternalZipConstants.ZIP64ENDCENDIRREC);
|
||||
copyByteArrayToArrayList(intByte, headerBytesList);
|
||||
|
||||
//size of zip64 end of central directory record
|
||||
Raw.writeLongLittleEndian(longByte, 0, (long)44);
|
||||
copyByteArrayToArrayList(longByte, headerBytesList);
|
||||
|
||||
//version made by
|
||||
//version needed to extract
|
||||
if (zipModel.getCentralDirectory() != null &&
|
||||
zipModel.getCentralDirectory().getFileHeaders() != null &&
|
||||
zipModel.getCentralDirectory().getFileHeaders().size() > 0) {
|
||||
Raw.writeShortLittleEndian(shortByte, 0,
|
||||
(short)((FileHeader)zipModel.getCentralDirectory().getFileHeaders().get(0)).getVersionMadeBy());
|
||||
copyByteArrayToArrayList(shortByte, headerBytesList);
|
||||
|
||||
Raw.writeShortLittleEndian(shortByte, 0,
|
||||
(short)((FileHeader)zipModel.getCentralDirectory().getFileHeaders().get(0)).getVersionNeededToExtract());
|
||||
copyByteArrayToArrayList(shortByte, headerBytesList);
|
||||
} else {
|
||||
copyByteArrayToArrayList(emptyShortByte, headerBytesList);
|
||||
copyByteArrayToArrayList(emptyShortByte, headerBytesList);
|
||||
}
|
||||
|
||||
//number of this disk
|
||||
Raw.writeIntLittleEndian(intByte, 0, zipModel.getEndCentralDirRecord().getNoOfThisDisk());
|
||||
copyByteArrayToArrayList(intByte, headerBytesList);
|
||||
|
||||
//number of the disk with start of central directory
|
||||
Raw.writeIntLittleEndian(intByte, 0, zipModel.getEndCentralDirRecord().getNoOfThisDiskStartOfCentralDir());
|
||||
copyByteArrayToArrayList(intByte, headerBytesList);
|
||||
|
||||
//total number of entries in the central directory on this disk
|
||||
int numEntries = 0;
|
||||
int numEntriesOnThisDisk = 0;
|
||||
if (zipModel.getCentralDirectory() == null ||
|
||||
zipModel.getCentralDirectory().getFileHeaders() == null) {
|
||||
throw new ZipException("invalid central directory/file headers, " +
|
||||
"cannot write end of central directory record");
|
||||
} else {
|
||||
numEntries = zipModel.getCentralDirectory().getFileHeaders().size();
|
||||
if (zipModel.isSplitArchive()) {
|
||||
countNumberOfFileHeaderEntriesOnDisk(zipModel.getCentralDirectory().getFileHeaders(),
|
||||
zipModel.getEndCentralDirRecord().getNoOfThisDisk());
|
||||
} else {
|
||||
numEntriesOnThisDisk = numEntries;
|
||||
}
|
||||
}
|
||||
Raw.writeLongLittleEndian(longByte, 0, numEntriesOnThisDisk);
|
||||
copyByteArrayToArrayList(longByte, headerBytesList);
|
||||
|
||||
//Total number of entries in central directory
|
||||
Raw.writeLongLittleEndian(longByte, 0, numEntries);
|
||||
copyByteArrayToArrayList(longByte, headerBytesList);
|
||||
|
||||
//Size of central directory
|
||||
Raw.writeLongLittleEndian(longByte, 0, sizeOfCentralDir);
|
||||
copyByteArrayToArrayList(longByte, headerBytesList);
|
||||
|
||||
//offset of start of central directory with respect to the starting disk number
|
||||
Raw.writeLongLittleEndian(longByte, 0, offsetCentralDir);
|
||||
copyByteArrayToArrayList(longByte, headerBytesList);
|
||||
|
||||
} catch (ZipException zipException) {
|
||||
throw zipException;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeZip64EndOfCentralDirectoryLocator(ZipModel zipModel,
|
||||
OutputStream outputStream, List headerBytesList) throws ZipException {
|
||||
if (zipModel == null || outputStream == null) {
|
||||
throw new ZipException("zip model or output stream is null, cannot write zip64 end of central directory locator");
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
byte[] intByte = new byte[4];
|
||||
byte[] longByte = new byte[8];
|
||||
|
||||
//zip64 end of central dir locator signature
|
||||
Raw.writeIntLittleEndian(intByte, 0, (int)InternalZipConstants.ZIP64ENDCENDIRLOC);
|
||||
copyByteArrayToArrayList(intByte, headerBytesList);
|
||||
|
||||
//number of the disk with the start of the zip64 end of central directory
|
||||
Raw.writeIntLittleEndian(intByte, 0, zipModel.getZip64EndCentralDirLocator().getNoOfDiskStartOfZip64EndOfCentralDirRec());
|
||||
copyByteArrayToArrayList(intByte, headerBytesList);
|
||||
|
||||
//relative offset of the zip64 end of central directory record
|
||||
Raw.writeLongLittleEndian(longByte, 0, zipModel.getZip64EndCentralDirLocator().getOffsetZip64EndOfCentralDirRec());
|
||||
copyByteArrayToArrayList(longByte, headerBytesList);
|
||||
|
||||
//total number of disks
|
||||
Raw.writeIntLittleEndian(intByte, 0, zipModel.getZip64EndCentralDirLocator().getTotNumberOfDiscs());
|
||||
copyByteArrayToArrayList(intByte, headerBytesList);
|
||||
} catch (ZipException zipException) {
|
||||
throw zipException;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeEndOfCentralDirectoryRecord(ZipModel zipModel,
|
||||
OutputStream outputStream,
|
||||
int sizeOfCentralDir,
|
||||
long offsetCentralDir,
|
||||
List headrBytesList) throws ZipException {
|
||||
if (zipModel == null || outputStream == null) {
|
||||
throw new ZipException("zip model or output stream is null, cannot write end of central directory record");
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
byte[] shortByte = new byte[2];
|
||||
byte[] intByte = new byte[4];
|
||||
byte[] longByte = new byte[8];
|
||||
|
||||
//End of central directory signature
|
||||
Raw.writeIntLittleEndian(intByte, 0, (int)zipModel.getEndCentralDirRecord().getSignature());
|
||||
copyByteArrayToArrayList(intByte, headrBytesList);
|
||||
|
||||
//number of this disk
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)(zipModel.getEndCentralDirRecord().getNoOfThisDisk()));
|
||||
copyByteArrayToArrayList(shortByte, headrBytesList);
|
||||
|
||||
//number of the disk with start of central directory
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)(zipModel.getEndCentralDirRecord().getNoOfThisDiskStartOfCentralDir()));
|
||||
copyByteArrayToArrayList(shortByte, headrBytesList);
|
||||
|
||||
//Total number of entries in central directory on this disk
|
||||
int numEntries = 0;
|
||||
int numEntriesOnThisDisk = 0;
|
||||
if (zipModel.getCentralDirectory() == null ||
|
||||
zipModel.getCentralDirectory().getFileHeaders() == null) {
|
||||
throw new ZipException("invalid central directory/file headers, " +
|
||||
"cannot write end of central directory record");
|
||||
} else {
|
||||
numEntries = zipModel.getCentralDirectory().getFileHeaders().size();
|
||||
if (zipModel.isSplitArchive()) {
|
||||
numEntriesOnThisDisk = countNumberOfFileHeaderEntriesOnDisk(zipModel.getCentralDirectory().getFileHeaders(),
|
||||
zipModel.getEndCentralDirRecord().getNoOfThisDisk());
|
||||
} else {
|
||||
numEntriesOnThisDisk = numEntries;
|
||||
}
|
||||
}
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)numEntriesOnThisDisk);
|
||||
copyByteArrayToArrayList(shortByte, headrBytesList);
|
||||
|
||||
//Total number of entries in central directory
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)numEntries);
|
||||
copyByteArrayToArrayList(shortByte, headrBytesList);
|
||||
|
||||
//Size of central directory
|
||||
Raw.writeIntLittleEndian(intByte, 0, sizeOfCentralDir);
|
||||
copyByteArrayToArrayList(intByte, headrBytesList);
|
||||
|
||||
//Offset central directory
|
||||
if (offsetCentralDir > InternalZipConstants.ZIP_64_LIMIT) {
|
||||
Raw.writeLongLittleEndian(longByte, 0, InternalZipConstants.ZIP_64_LIMIT);
|
||||
System.arraycopy(longByte, 0, intByte, 0, 4);
|
||||
copyByteArrayToArrayList(intByte, headrBytesList);
|
||||
} else {
|
||||
Raw.writeLongLittleEndian(longByte, 0, offsetCentralDir);
|
||||
System.arraycopy(longByte, 0, intByte, 0, 4);
|
||||
// Raw.writeIntLittleEndian(intByte, 0, (int)offsetCentralDir);
|
||||
copyByteArrayToArrayList(intByte, headrBytesList);
|
||||
}
|
||||
|
||||
//Zip File comment length
|
||||
int commentLength = 0;
|
||||
if (zipModel.getEndCentralDirRecord().getComment() != null) {
|
||||
commentLength = zipModel.getEndCentralDirRecord().getCommentLength();
|
||||
}
|
||||
Raw.writeShortLittleEndian(shortByte, 0, (short)commentLength);
|
||||
copyByteArrayToArrayList(shortByte, headrBytesList);
|
||||
|
||||
//Comment
|
||||
if (commentLength > 0) {
|
||||
copyByteArrayToArrayList(zipModel.getEndCentralDirRecord().getCommentBytes(), headrBytesList);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLocalFileHeader(LocalFileHeader localFileHeader, long offset,
|
||||
int toUpdate, ZipModel zipModel, byte[] bytesToWrite, int noOfDisk, SplitOutputStream outputStream) throws ZipException {
|
||||
if (localFileHeader == null || offset < 0 || zipModel == null) {
|
||||
throw new ZipException("invalid input parameters, cannot update local file header");
|
||||
}
|
||||
|
||||
try {
|
||||
boolean closeFlag = false;
|
||||
SplitOutputStream currOutputStream = null;
|
||||
|
||||
if (noOfDisk != outputStream.getCurrSplitFileCounter()) {
|
||||
File zipFile = new File(zipModel.getZipFile());
|
||||
String parentFile = zipFile.getParent();
|
||||
String fileNameWithoutExt = Zip4jUtil.getZipFileNameWithoutExt(zipFile.getName());
|
||||
String fileName = parentFile + System.getProperty("file.separator");
|
||||
if (noOfDisk < 9) {
|
||||
fileName += fileNameWithoutExt + ".z0" + (noOfDisk + 1);
|
||||
} else {
|
||||
fileName += fileNameWithoutExt + ".z" + (noOfDisk + 1);
|
||||
}
|
||||
currOutputStream = new SplitOutputStream(new File(fileName));
|
||||
closeFlag = true;
|
||||
} else {
|
||||
currOutputStream = outputStream;
|
||||
}
|
||||
|
||||
long currOffset = currOutputStream.getFilePointer();
|
||||
|
||||
switch (toUpdate) {
|
||||
case InternalZipConstants.UPDATE_LFH_CRC:
|
||||
currOutputStream.seek(offset + toUpdate);
|
||||
currOutputStream.write(bytesToWrite);
|
||||
break;
|
||||
case InternalZipConstants.UPDATE_LFH_COMP_SIZE:
|
||||
case InternalZipConstants.UPDATE_LFH_UNCOMP_SIZE:
|
||||
updateCompressedSizeInLocalFileHeader(currOutputStream, localFileHeader,
|
||||
offset, toUpdate, bytesToWrite, zipModel.isZip64Format());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (closeFlag) {
|
||||
currOutputStream.close();
|
||||
} else {
|
||||
outputStream.seek(currOffset);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCompressedSizeInLocalFileHeader(SplitOutputStream outputStream, LocalFileHeader localFileHeader,
|
||||
long offset, long toUpdate, byte[] bytesToWrite, boolean isZip64Format) throws ZipException {
|
||||
|
||||
if (outputStream == null) {
|
||||
throw new ZipException("invalid output stream, cannot update compressed size for local file header");
|
||||
}
|
||||
|
||||
try {
|
||||
if (localFileHeader.isWriteComprSizeInZip64ExtraRecord()) {
|
||||
if (bytesToWrite.length != 8) {
|
||||
throw new ZipException("attempting to write a non 8-byte compressed size block for a zip64 file");
|
||||
}
|
||||
|
||||
//4 - compressed size
|
||||
//4 - uncomprssed size
|
||||
//2 - file name length
|
||||
//2 - extra field length
|
||||
//file name length
|
||||
//2 - Zip64 signature
|
||||
//2 - size of zip64 data
|
||||
//8 - crc
|
||||
//8 - compressed size
|
||||
//8 - uncompressed size
|
||||
long zip64CompressedSizeOffset = offset + toUpdate + 4 + 4 + 2 + 2 + localFileHeader.getFileNameLength() + 2 + 2 + 8;
|
||||
if (toUpdate == InternalZipConstants.UPDATE_LFH_UNCOMP_SIZE) {
|
||||
zip64CompressedSizeOffset += 8;
|
||||
}
|
||||
outputStream.seek(zip64CompressedSizeOffset);
|
||||
outputStream.write(bytesToWrite);
|
||||
} else {
|
||||
outputStream.seek(offset + toUpdate);
|
||||
outputStream.write(bytesToWrite);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void copyByteArrayToArrayList(byte[] byteArray, List arrayList) throws ZipException {
|
||||
if (arrayList == null || byteArray == null) {
|
||||
throw new ZipException("one of the input parameters is null, cannot copy byte array to array list");
|
||||
}
|
||||
|
||||
for (int i = 0; i < byteArray.length; i++) {
|
||||
arrayList.add(Byte.toString(byteArray[i]));
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] byteArrayListToByteArray(List arrayList) throws ZipException {
|
||||
if (arrayList == null) {
|
||||
throw new ZipException("input byte array list is null, cannot conver to byte array");
|
||||
}
|
||||
|
||||
if (arrayList.size() <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] retBytes = new byte[arrayList.size()];
|
||||
|
||||
for (int i = 0; i < arrayList.size(); i++) {
|
||||
retBytes[i] = Byte.parseByte((String)arrayList.get(i));
|
||||
}
|
||||
|
||||
return retBytes;
|
||||
}
|
||||
|
||||
private int countNumberOfFileHeaderEntriesOnDisk(ArrayList 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 (int i = 0; i < fileHeaders.size(); i++) {
|
||||
FileHeader fileHeader = (FileHeader)fileHeaders.get(i);
|
||||
if (fileHeader.getDiskNumberStart() == numOfDisk) {
|
||||
noEntries++;
|
||||
}
|
||||
}
|
||||
return noEntries;
|
||||
}
|
||||
|
||||
}
|
1041
src/net/lingala/zip4j/core/ZipFile.java
Normal file
1041
src/net/lingala/zip4j/core/ZipFile.java
Normal file
File diff suppressed because it is too large
Load diff
226
src/net/lingala/zip4j/crypto/AESDecrypter.java
Normal file
226
src/net/lingala/zip4j/crypto/AESDecrypter.java
Normal file
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.lingala.zip4j.crypto.PBKDF2.MacBasedPRF;
|
||||
import net.lingala.zip4j.crypto.PBKDF2.PBKDF2Engine;
|
||||
import net.lingala.zip4j.crypto.PBKDF2.PBKDF2Parameters;
|
||||
import net.lingala.zip4j.crypto.engine.AESEngine;
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.exception.ZipExceptionConstants;
|
||||
import net.lingala.zip4j.model.AESExtraDataRecord;
|
||||
import net.lingala.zip4j.model.LocalFileHeader;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
import net.lingala.zip4j.util.Raw;
|
||||
import net.lingala.zip4j.util.Zip4jConstants;
|
||||
|
||||
public class AESDecrypter implements IDecrypter {
|
||||
|
||||
private LocalFileHeader localFileHeader;
|
||||
private AESEngine aesEngine;
|
||||
private MacBasedPRF mac;
|
||||
|
||||
private final int PASSWORD_VERIFIER_LENGTH = 2;
|
||||
private int KEY_LENGTH;
|
||||
private int MAC_LENGTH;
|
||||
private int SALT_LENGTH;
|
||||
|
||||
private byte[] aesKey;
|
||||
private byte[] macKey;
|
||||
private byte[] derivedPasswordVerifier;
|
||||
private byte[] storedMac;
|
||||
|
||||
private int nonce = 1;
|
||||
private byte[] iv;
|
||||
private byte[] counterBlock;
|
||||
private int loopCount = 0;
|
||||
|
||||
public AESDecrypter(LocalFileHeader localFileHeader,
|
||||
byte[] salt, byte[] passwordVerifier) throws ZipException {
|
||||
|
||||
if (localFileHeader == null) {
|
||||
throw new ZipException("one of the input parameters is null in AESDecryptor Constructor");
|
||||
}
|
||||
|
||||
this.localFileHeader = localFileHeader;
|
||||
this.storedMac = null;
|
||||
iv = new byte[InternalZipConstants.AES_BLOCK_SIZE];
|
||||
counterBlock = new byte[InternalZipConstants.AES_BLOCK_SIZE];
|
||||
init(salt, passwordVerifier);
|
||||
}
|
||||
|
||||
private void init(byte[] salt, byte[] passwordVerifier) throws ZipException {
|
||||
if (localFileHeader == null) {
|
||||
throw new ZipException("invalid file header in init method of AESDecryptor");
|
||||
}
|
||||
|
||||
AESExtraDataRecord aesExtraDataRecord = localFileHeader.getAesExtraDataRecord();
|
||||
if (aesExtraDataRecord == null) {
|
||||
throw new ZipException("invalid aes extra data record - in init method of AESDecryptor");
|
||||
}
|
||||
|
||||
switch (aesExtraDataRecord.getAesStrength()) {
|
||||
case Zip4jConstants.AES_STRENGTH_128:
|
||||
KEY_LENGTH = 16;
|
||||
MAC_LENGTH = 16;
|
||||
SALT_LENGTH = 8;
|
||||
break;
|
||||
case Zip4jConstants.AES_STRENGTH_192:
|
||||
KEY_LENGTH = 24;
|
||||
MAC_LENGTH = 24;
|
||||
SALT_LENGTH = 12;
|
||||
break;
|
||||
case Zip4jConstants.AES_STRENGTH_256:
|
||||
KEY_LENGTH = 32;
|
||||
MAC_LENGTH = 32;
|
||||
SALT_LENGTH = 16;
|
||||
break;
|
||||
default:
|
||||
throw new ZipException("invalid aes key strength for file: " + localFileHeader.getFileName());
|
||||
}
|
||||
|
||||
if (localFileHeader.getPassword() == null || localFileHeader.getPassword().length <= 0) {
|
||||
throw new ZipException("empty or null password provided for AES Decryptor");
|
||||
}
|
||||
|
||||
byte[] derivedKey = deriveKey(salt, localFileHeader.getPassword());
|
||||
if (derivedKey == null ||
|
||||
derivedKey.length != (KEY_LENGTH + MAC_LENGTH + PASSWORD_VERIFIER_LENGTH)) {
|
||||
throw new ZipException("invalid derived key");
|
||||
}
|
||||
|
||||
aesKey = new byte[KEY_LENGTH];
|
||||
macKey = new byte[MAC_LENGTH];
|
||||
derivedPasswordVerifier = new byte[PASSWORD_VERIFIER_LENGTH];
|
||||
|
||||
System.arraycopy(derivedKey, 0, aesKey, 0, KEY_LENGTH);
|
||||
System.arraycopy(derivedKey, KEY_LENGTH, macKey, 0, MAC_LENGTH);
|
||||
System.arraycopy(derivedKey, KEY_LENGTH + MAC_LENGTH, derivedPasswordVerifier, 0, PASSWORD_VERIFIER_LENGTH);
|
||||
|
||||
if (derivedPasswordVerifier == null) {
|
||||
throw new ZipException("invalid derived password verifier for AES");
|
||||
}
|
||||
|
||||
if (!Arrays.equals(passwordVerifier, derivedPasswordVerifier)) {
|
||||
throw new ZipException("Wrong Password for file: " + localFileHeader.getFileName(), ZipExceptionConstants.WRONG_PASSWORD);
|
||||
}
|
||||
|
||||
aesEngine = new AESEngine(aesKey);
|
||||
mac = new MacBasedPRF("HmacSHA1");
|
||||
mac.init(macKey);
|
||||
}
|
||||
|
||||
public int decryptData(byte[] buff, int start, int len) throws ZipException {
|
||||
|
||||
if (aesEngine == null) {
|
||||
throw new ZipException("AES not initialized properly");
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
for (int j = start; j < (start + len); j += InternalZipConstants.AES_BLOCK_SIZE) {
|
||||
loopCount = (j + InternalZipConstants.AES_BLOCK_SIZE <= (start + len)) ?
|
||||
InternalZipConstants.AES_BLOCK_SIZE : ((start + len) - j);
|
||||
|
||||
mac.update(buff, j, loopCount);
|
||||
Raw.prepareBuffAESIVBytes(iv, nonce, InternalZipConstants.AES_BLOCK_SIZE);
|
||||
aesEngine.processBlock(iv, counterBlock);
|
||||
|
||||
for (int k = 0; k < loopCount; k++) {
|
||||
buff[j + k] = (byte)(buff[j + k] ^ counterBlock[k]);
|
||||
}
|
||||
|
||||
nonce++;
|
||||
}
|
||||
|
||||
return len;
|
||||
|
||||
} catch (ZipException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int decryptData(byte[] buff) throws ZipException {
|
||||
return decryptData(buff, 0, buff.length);
|
||||
}
|
||||
|
||||
private byte[] deriveKey(byte[] salt, char[] password) throws ZipException {
|
||||
try {
|
||||
PBKDF2Parameters p = new PBKDF2Parameters("HmacSHA1", "ISO-8859-1",
|
||||
salt, 1000);
|
||||
PBKDF2Engine e = new PBKDF2Engine(p);
|
||||
byte[] derivedKey = e.deriveKey(password, KEY_LENGTH + MAC_LENGTH + PASSWORD_VERIFIER_LENGTH);
|
||||
return derivedKey;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getPasswordVerifierLength() {
|
||||
return PASSWORD_VERIFIER_LENGTH;
|
||||
}
|
||||
|
||||
public int getSaltLength() {
|
||||
return SALT_LENGTH;
|
||||
}
|
||||
|
||||
public byte[] getCalculatedAuthenticationBytes() {
|
||||
return mac.doFinal();
|
||||
}
|
||||
|
||||
public void setStoredMac(byte[] storedMac) {
|
||||
this.storedMac = storedMac;
|
||||
}
|
||||
|
||||
public byte[] getStoredMac() {
|
||||
return storedMac;
|
||||
}
|
||||
|
||||
// public byte[] getStoredMac() throws ZipException {
|
||||
// if (raf == null) {
|
||||
// throw new ZipException("attempting to read MAC on closed file handle");
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// byte[] storedMacBytes = new byte[InternalZipConstants.AES_AUTH_LENGTH];
|
||||
// int bytesRead = raf.read(storedMacBytes);
|
||||
// if (bytesRead != InternalZipConstants.AES_AUTH_LENGTH) {
|
||||
// if (zipModel.isSplitArchive()) {
|
||||
//// unzipEngine.startNextSplitFile();
|
||||
// if (bytesRead == -1) bytesRead = 0;
|
||||
// int newlyRead = raf.read(storedMacBytes, bytesRead, InternalZipConstants.AES_AUTH_LENGTH - bytesRead);
|
||||
// bytesRead += newlyRead;
|
||||
// if (bytesRead != InternalZipConstants.AES_AUTH_LENGTH) {
|
||||
// throw new ZipException("invalid number of bytes read for stored MAC after starting split file");
|
||||
// }
|
||||
// } else {
|
||||
// throw new ZipException("invalid number of bytes read for stored MAC");
|
||||
// }
|
||||
// }
|
||||
// return storedMacBytes;
|
||||
// } catch (IOException e) {
|
||||
// throw new ZipException(e);
|
||||
// } catch (Exception e) {
|
||||
// throw new ZipException(e);
|
||||
// }
|
||||
//
|
||||
// }
|
||||
}
|
214
src/net/lingala/zip4j/crypto/AESEncrpyter.java
Normal file
214
src/net/lingala/zip4j/crypto/AESEncrpyter.java
Normal file
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import net.lingala.zip4j.crypto.PBKDF2.MacBasedPRF;
|
||||
import net.lingala.zip4j.crypto.PBKDF2.PBKDF2Engine;
|
||||
import net.lingala.zip4j.crypto.PBKDF2.PBKDF2Parameters;
|
||||
import net.lingala.zip4j.crypto.engine.AESEngine;
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
import net.lingala.zip4j.util.Raw;
|
||||
import net.lingala.zip4j.util.Zip4jConstants;
|
||||
|
||||
public class AESEncrpyter implements IEncrypter {
|
||||
|
||||
private char[] password;
|
||||
private int keyStrength;
|
||||
private AESEngine aesEngine;
|
||||
private MacBasedPRF mac;
|
||||
|
||||
private int KEY_LENGTH;
|
||||
private int MAC_LENGTH;
|
||||
private int SALT_LENGTH;
|
||||
private final int PASSWORD_VERIFIER_LENGTH = 2;
|
||||
|
||||
private byte[] aesKey;
|
||||
private byte[] macKey;
|
||||
private byte[] derivedPasswordVerifier;
|
||||
private byte[] saltBytes;
|
||||
|
||||
private boolean finished;
|
||||
|
||||
private int nonce = 1;
|
||||
private int loopCount = 0;
|
||||
|
||||
private byte[] iv;
|
||||
private byte[] counterBlock;
|
||||
|
||||
public AESEncrpyter(char[] password, int keyStrength) throws ZipException {
|
||||
if (password == null || password.length == 0) {
|
||||
throw new ZipException("input password is empty or null in AES encrypter constructor");
|
||||
}
|
||||
if (keyStrength != Zip4jConstants.AES_STRENGTH_128 &&
|
||||
keyStrength != Zip4jConstants.AES_STRENGTH_256) {
|
||||
throw new ZipException("Invalid key strength in AES encrypter constructor");
|
||||
}
|
||||
|
||||
this.password = password;
|
||||
this.keyStrength = keyStrength;
|
||||
this.finished = false;
|
||||
counterBlock = new byte[InternalZipConstants.AES_BLOCK_SIZE];
|
||||
iv = new byte[InternalZipConstants.AES_BLOCK_SIZE];
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() throws ZipException {
|
||||
switch (keyStrength) {
|
||||
case Zip4jConstants.AES_STRENGTH_128:
|
||||
KEY_LENGTH = 16;
|
||||
MAC_LENGTH = 16;
|
||||
SALT_LENGTH = 8;
|
||||
break;
|
||||
case Zip4jConstants.AES_STRENGTH_256:
|
||||
KEY_LENGTH = 32;
|
||||
MAC_LENGTH = 32;
|
||||
SALT_LENGTH = 16;
|
||||
break;
|
||||
default:
|
||||
throw new ZipException("invalid aes key strength, cannot determine key sizes");
|
||||
}
|
||||
|
||||
saltBytes = generateSalt(SALT_LENGTH);
|
||||
byte[] keyBytes = deriveKey(saltBytes, password);
|
||||
|
||||
if (keyBytes == null || keyBytes.length != (KEY_LENGTH + MAC_LENGTH + PASSWORD_VERIFIER_LENGTH)) {
|
||||
throw new ZipException("invalid key generated, cannot decrypt file");
|
||||
}
|
||||
|
||||
aesKey = new byte[KEY_LENGTH];
|
||||
macKey = new byte[MAC_LENGTH];
|
||||
derivedPasswordVerifier = new byte[PASSWORD_VERIFIER_LENGTH];
|
||||
|
||||
System.arraycopy(keyBytes, 0, aesKey, 0, KEY_LENGTH);
|
||||
System.arraycopy(keyBytes, KEY_LENGTH, macKey, 0, MAC_LENGTH);
|
||||
System.arraycopy(keyBytes, KEY_LENGTH + MAC_LENGTH, derivedPasswordVerifier, 0, PASSWORD_VERIFIER_LENGTH);
|
||||
|
||||
aesEngine = new AESEngine(aesKey);
|
||||
mac = new MacBasedPRF("HmacSHA1");
|
||||
mac.init(macKey);
|
||||
}
|
||||
|
||||
private byte[] deriveKey(byte[] salt, char[] password) throws ZipException {
|
||||
try {
|
||||
PBKDF2Parameters p = new PBKDF2Parameters("HmacSHA1", "ISO-8859-1",
|
||||
salt, 1000);
|
||||
PBKDF2Engine e = new PBKDF2Engine(p);
|
||||
byte[] derivedKey = e.deriveKey(password, KEY_LENGTH + MAC_LENGTH + PASSWORD_VERIFIER_LENGTH);
|
||||
return derivedKey;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int encryptData(byte[] buff) throws ZipException {
|
||||
|
||||
if (buff == null) {
|
||||
throw new ZipException("input bytes are null, cannot perform AES encrpytion");
|
||||
}
|
||||
return encryptData(buff, 0, buff.length);
|
||||
}
|
||||
|
||||
public int encryptData(byte[] buff, int start, int len) throws ZipException {
|
||||
|
||||
if (finished) {
|
||||
// A non 16 byte block has already been passed to encrypter
|
||||
// non 16 byte block should be the last block of compressed data in AES encryption
|
||||
// any more encryption will lead to corruption of data
|
||||
throw new ZipException("AES Encrypter is in finished state (A non 16 byte block has already been passed to encrypter)");
|
||||
}
|
||||
|
||||
if (len%16!=0) {
|
||||
this.finished = true;
|
||||
}
|
||||
|
||||
for (int j = start; j < (start + len); j += InternalZipConstants.AES_BLOCK_SIZE) {
|
||||
loopCount = (j + InternalZipConstants.AES_BLOCK_SIZE <= (start + len)) ?
|
||||
InternalZipConstants.AES_BLOCK_SIZE : ((start + len) - j);
|
||||
|
||||
Raw.prepareBuffAESIVBytes(iv, nonce, InternalZipConstants.AES_BLOCK_SIZE);
|
||||
aesEngine.processBlock(iv, counterBlock);
|
||||
|
||||
for (int k = 0; k < loopCount; k++) {
|
||||
buff[j + k] = (byte)(buff[j + k] ^ counterBlock[k]);
|
||||
}
|
||||
|
||||
mac.update(buff, j, loopCount);
|
||||
nonce++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
private static byte[] generateSalt(int size) throws ZipException {
|
||||
|
||||
if (size != 8 && size != 16) {
|
||||
throw new ZipException("invalid salt size, cannot generate salt");
|
||||
}
|
||||
|
||||
int rounds = 0;
|
||||
|
||||
if (size == 8)
|
||||
rounds = 2;
|
||||
if (size == 16)
|
||||
rounds = 4;
|
||||
|
||||
byte[] salt = new byte[size];
|
||||
for( int j = 0; j < rounds; j++ ) {
|
||||
Random rand = new Random();
|
||||
int i = rand.nextInt();
|
||||
salt[0+j*4] = (byte)(i>>24);
|
||||
salt[1+j*4] = (byte)(i>>16);
|
||||
salt[2+j*4] = (byte)(i>>8);
|
||||
salt[3+j*4] = (byte)i;
|
||||
}
|
||||
return salt;
|
||||
}
|
||||
|
||||
public byte[] getFinalMac() {
|
||||
byte[] rawMacBytes = mac.doFinal();
|
||||
byte[] macBytes = new byte[10];
|
||||
System.arraycopy(rawMacBytes, 0, macBytes, 0, 10);
|
||||
return macBytes;
|
||||
}
|
||||
|
||||
public byte[] getDerivedPasswordVerifier() {
|
||||
return derivedPasswordVerifier;
|
||||
}
|
||||
|
||||
public void setDerivedPasswordVerifier(byte[] derivedPasswordVerifier) {
|
||||
this.derivedPasswordVerifier = derivedPasswordVerifier;
|
||||
}
|
||||
|
||||
public byte[] getSaltBytes() {
|
||||
return saltBytes;
|
||||
}
|
||||
|
||||
public void setSaltBytes(byte[] saltBytes) {
|
||||
this.saltBytes = saltBytes;
|
||||
}
|
||||
|
||||
public int getSaltLength() {
|
||||
return SALT_LENGTH;
|
||||
}
|
||||
|
||||
public int getPasswordVeriifierLength() {
|
||||
return PASSWORD_VERIFIER_LENGTH;
|
||||
}
|
||||
}
|
27
src/net/lingala/zip4j/crypto/IDecrypter.java
Normal file
27
src/net/lingala/zip4j/crypto/IDecrypter.java
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
|
||||
public interface IDecrypter {
|
||||
|
||||
public int decryptData(byte[] buff, int start, int len) throws ZipException;
|
||||
|
||||
public int decryptData(byte[] buff) throws ZipException;
|
||||
|
||||
}
|
27
src/net/lingala/zip4j/crypto/IEncrypter.java
Normal file
27
src/net/lingala/zip4j/crypto/IEncrypter.java
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
|
||||
public interface IEncrypter {
|
||||
|
||||
public int encryptData(byte[] buff) throws ZipException;
|
||||
|
||||
public int encryptData(byte[] buff, int start, int len) throws ZipException;
|
||||
|
||||
}
|
85
src/net/lingala/zip4j/crypto/PBKDF2/BinTools.java
Normal file
85
src/net/lingala/zip4j/crypto/PBKDF2/BinTools.java
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto.PBKDF2;
|
||||
|
||||
/*
|
||||
* Source referred from Matthias Gartner's PKCS#5 implementation -
|
||||
* see http://rtner.de/software/PBKDF2.html
|
||||
*/
|
||||
|
||||
class BinTools
|
||||
{
|
||||
public static final String hex = "0123456789ABCDEF";
|
||||
|
||||
public static String bin2hex(final byte[] b)
|
||||
{
|
||||
if (b == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
StringBuffer sb = new StringBuffer(2 * b.length);
|
||||
for (int i = 0; i < b.length; i++)
|
||||
{
|
||||
int v = (256 + b[i]) % 256;
|
||||
sb.append(hex.charAt((v / 16) & 15));
|
||||
sb.append(hex.charAt((v % 16) & 15));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static byte[] hex2bin(final String s)
|
||||
{
|
||||
String m = s;
|
||||
if (s == null)
|
||||
{
|
||||
// Allow empty input string.
|
||||
m = "";
|
||||
}
|
||||
else if (s.length() % 2 != 0)
|
||||
{
|
||||
// Assume leading zero for odd string length
|
||||
m = "0" + s;
|
||||
}
|
||||
byte r[] = new byte[m.length() / 2];
|
||||
for (int i = 0, n = 0; i < m.length(); n++)
|
||||
{
|
||||
char h = m.charAt(i++);
|
||||
char l = m.charAt(i++);
|
||||
r[n] = (byte) (hex2bin(h) * 16 + hex2bin(l));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public static int hex2bin(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
{
|
||||
return (c - '0');
|
||||
}
|
||||
if (c >= 'A' && c <= 'F')
|
||||
{
|
||||
return (c - 'A' + 10);
|
||||
}
|
||||
if (c >= 'a' && c <= 'f')
|
||||
{
|
||||
return (c - 'a' + 10);
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"Input string may only contain hex digits, but found '" + c
|
||||
+ "'");
|
||||
}
|
||||
}
|
116
src/net/lingala/zip4j/crypto/PBKDF2/MacBasedPRF.java
Normal file
116
src/net/lingala/zip4j/crypto/PBKDF2/MacBasedPRF.java
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto.PBKDF2;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/*
|
||||
* Source referred from Matthias Gartner's PKCS#5 implementation -
|
||||
* see http://rtner.de/software/PBKDF2.html
|
||||
*/
|
||||
|
||||
public class MacBasedPRF implements PRF
|
||||
{
|
||||
protected Mac mac;
|
||||
|
||||
protected int hLen;
|
||||
|
||||
protected String macAlgorithm;
|
||||
|
||||
public MacBasedPRF(String macAlgorithm)
|
||||
{
|
||||
this.macAlgorithm = macAlgorithm;
|
||||
try
|
||||
{
|
||||
mac = Mac.getInstance(macAlgorithm);
|
||||
hLen = mac.getMacLength();
|
||||
}
|
||||
catch (NoSuchAlgorithmException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public MacBasedPRF(String macAlgorithm, String provider)
|
||||
{
|
||||
this.macAlgorithm = macAlgorithm;
|
||||
try
|
||||
{
|
||||
mac = Mac.getInstance(macAlgorithm, provider);
|
||||
hLen = mac.getMacLength();
|
||||
}
|
||||
catch (NoSuchAlgorithmException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (NoSuchProviderException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] doFinal(byte[] M)
|
||||
{
|
||||
byte[] r = mac.doFinal(M);
|
||||
return r;
|
||||
}
|
||||
|
||||
public byte[] doFinal() {
|
||||
byte[] r = mac.doFinal();
|
||||
return r;
|
||||
}
|
||||
|
||||
public int getHLen()
|
||||
{
|
||||
return hLen;
|
||||
}
|
||||
|
||||
public void init(byte[] P)
|
||||
{
|
||||
try
|
||||
{
|
||||
mac.init(new SecretKeySpec(P, macAlgorithm));
|
||||
}
|
||||
catch (InvalidKeyException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void update(byte[] U) {
|
||||
|
||||
try {
|
||||
mac.update(U);
|
||||
} catch (IllegalStateException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void update (byte[] U, int start, int len) {
|
||||
try {
|
||||
mac.update(U, start, len);
|
||||
} catch (IllegalStateException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
198
src/net/lingala/zip4j/crypto/PBKDF2/PBKDF2Engine.java
Normal file
198
src/net/lingala/zip4j/crypto/PBKDF2/PBKDF2Engine.java
Normal file
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto.PBKDF2;
|
||||
|
||||
import net.lingala.zip4j.util.Raw;
|
||||
|
||||
/*
|
||||
* Source referred from Matthias Gartner's PKCS#5 implementation -
|
||||
* see http://rtner.de/software/PBKDF2.html
|
||||
*/
|
||||
|
||||
public class PBKDF2Engine
|
||||
{
|
||||
protected PBKDF2Parameters parameters;
|
||||
|
||||
protected PRF prf;
|
||||
|
||||
public PBKDF2Engine()
|
||||
{
|
||||
this.parameters = null;
|
||||
prf = null;
|
||||
}
|
||||
|
||||
public PBKDF2Engine(PBKDF2Parameters parameters)
|
||||
{
|
||||
this.parameters = parameters;
|
||||
prf = null;
|
||||
}
|
||||
|
||||
public PBKDF2Engine(PBKDF2Parameters parameters, PRF prf)
|
||||
{
|
||||
this.parameters = parameters;
|
||||
this.prf = prf;
|
||||
}
|
||||
|
||||
public byte[] deriveKey(char[] inputPassword)
|
||||
{
|
||||
return deriveKey(inputPassword, 0);
|
||||
}
|
||||
|
||||
public byte[] deriveKey(char[] inputPassword, int dkLen)
|
||||
{
|
||||
byte[] r = null;
|
||||
byte P[] = null;
|
||||
if (inputPassword == null)
|
||||
{
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
P = Raw.convertCharArrayToByteArray(inputPassword);
|
||||
|
||||
assertPRF(P);
|
||||
if (dkLen == 0)
|
||||
{
|
||||
dkLen = prf.getHLen();
|
||||
}
|
||||
r = PBKDF2(prf, parameters.getSalt(), parameters.getIterationCount(),
|
||||
dkLen);
|
||||
return r;
|
||||
}
|
||||
|
||||
public boolean verifyKey(char[] inputPassword)
|
||||
{
|
||||
byte[] referenceKey = getParameters().getDerivedKey();
|
||||
if (referenceKey == null || referenceKey.length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
byte[] inputKey = deriveKey(inputPassword, referenceKey.length);
|
||||
|
||||
if (inputKey == null || inputKey.length != referenceKey.length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < inputKey.length; i++)
|
||||
{
|
||||
if (inputKey[i] != referenceKey[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void assertPRF(byte[] P)
|
||||
{
|
||||
if (prf == null)
|
||||
{
|
||||
prf = new MacBasedPRF(parameters.getHashAlgorithm());
|
||||
}
|
||||
prf.init(P);
|
||||
}
|
||||
|
||||
public PRF getPseudoRandomFunction()
|
||||
{
|
||||
return prf;
|
||||
}
|
||||
|
||||
protected byte[] PBKDF2(PRF prf, byte[] S, int c, int dkLen)
|
||||
{
|
||||
if (S == null)
|
||||
{
|
||||
S = new byte[0];
|
||||
}
|
||||
int hLen = prf.getHLen();
|
||||
int l = ceil(dkLen, hLen);
|
||||
int r = dkLen - (l - 1) * hLen;
|
||||
byte T[] = new byte[l * hLen];
|
||||
int ti_offset = 0;
|
||||
for (int i = 1; i <= l; i++)
|
||||
{
|
||||
_F(T, ti_offset, prf, S, c, i);
|
||||
ti_offset += hLen;
|
||||
}
|
||||
if (r < hLen)
|
||||
{
|
||||
// Incomplete last block
|
||||
byte DK[] = new byte[dkLen];
|
||||
System.arraycopy(T, 0, DK, 0, dkLen);
|
||||
return DK;
|
||||
}
|
||||
return T;
|
||||
}
|
||||
|
||||
protected int ceil(int a, int b)
|
||||
{
|
||||
int m = 0;
|
||||
if (a % b > 0)
|
||||
{
|
||||
m = 1;
|
||||
}
|
||||
return a / b + m;
|
||||
}
|
||||
|
||||
protected void _F(byte[] dest, int offset, PRF prf, byte[] S, int c,
|
||||
int blockIndex)
|
||||
{
|
||||
int hLen = prf.getHLen();
|
||||
byte U_r[] = new byte[hLen];
|
||||
|
||||
// U0 = S || INT (i);
|
||||
byte U_i[] = new byte[S.length + 4];
|
||||
System.arraycopy(S, 0, U_i, 0, S.length);
|
||||
INT(U_i, S.length, blockIndex);
|
||||
|
||||
for (int i = 0; i < c; i++)
|
||||
{
|
||||
U_i = prf.doFinal(U_i);
|
||||
xor(U_r, U_i);
|
||||
}
|
||||
System.arraycopy(U_r, 0, dest, offset, hLen);
|
||||
}
|
||||
|
||||
protected void xor(byte[] dest, byte[] src)
|
||||
{
|
||||
for (int i = 0; i < dest.length; i++)
|
||||
{
|
||||
dest[i] ^= src[i];
|
||||
}
|
||||
}
|
||||
|
||||
protected void INT(byte[] dest, int offset, int i)
|
||||
{
|
||||
dest[offset + 0] = (byte) (i / (256 * 256 * 256));
|
||||
dest[offset + 1] = (byte) (i / (256 * 256));
|
||||
dest[offset + 2] = (byte) (i / (256));
|
||||
dest[offset + 3] = (byte) (i);
|
||||
}
|
||||
|
||||
public PBKDF2Parameters getParameters()
|
||||
{
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public void setParameters(PBKDF2Parameters parameters)
|
||||
{
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public void setPseudoRandomFunction(PRF prf)
|
||||
{
|
||||
this.prf = prf;
|
||||
}
|
||||
}
|
56
src/net/lingala/zip4j/crypto/PBKDF2/PBKDF2HexFormatter.java
Normal file
56
src/net/lingala/zip4j/crypto/PBKDF2/PBKDF2HexFormatter.java
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto.PBKDF2;
|
||||
|
||||
/*
|
||||
* Source referred from Matthias Gartner's PKCS#5 implementation -
|
||||
* see http://rtner.de/software/PBKDF2.html
|
||||
*/
|
||||
|
||||
class PBKDF2HexFormatter
|
||||
{
|
||||
public boolean fromString(PBKDF2Parameters p, String s)
|
||||
{
|
||||
if (p == null || s == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
String[] p123 = s.split(":");
|
||||
if (p123 == null || p123.length != 3)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
byte salt[] = BinTools.hex2bin(p123[0]);
|
||||
int iterationCount = Integer.parseInt(p123[1]);
|
||||
byte bDK[] = BinTools.hex2bin(p123[2]);
|
||||
|
||||
p.setSalt(salt);
|
||||
p.setIterationCount(iterationCount);
|
||||
p.setDerivedKey(bDK);
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString(PBKDF2Parameters p)
|
||||
{
|
||||
String s = BinTools.bin2hex(p.getSalt()) + ":"
|
||||
+ String.valueOf(p.getIterationCount()) + ":"
|
||||
+ BinTools.bin2hex(p.getDerivedKey());
|
||||
return s;
|
||||
}
|
||||
}
|
112
src/net/lingala/zip4j/crypto/PBKDF2/PBKDF2Parameters.java
Normal file
112
src/net/lingala/zip4j/crypto/PBKDF2/PBKDF2Parameters.java
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto.PBKDF2;
|
||||
/*
|
||||
* Source referred from Matthias Gartner's PKCS#5 implementation -
|
||||
* see http://rtner.de/software/PBKDF2.html
|
||||
*/
|
||||
public class PBKDF2Parameters
|
||||
{
|
||||
protected byte[] salt;
|
||||
|
||||
protected int iterationCount;
|
||||
|
||||
protected String hashAlgorithm;
|
||||
|
||||
protected String hashCharset;
|
||||
|
||||
protected byte[] derivedKey;
|
||||
|
||||
public PBKDF2Parameters()
|
||||
{
|
||||
this.hashAlgorithm = null;
|
||||
this.hashCharset = "UTF-8";
|
||||
this.salt = null;
|
||||
this.iterationCount = 1000;
|
||||
this.derivedKey = null;
|
||||
}
|
||||
|
||||
public PBKDF2Parameters(String hashAlgorithm, String hashCharset,
|
||||
byte[] salt, int iterationCount)
|
||||
{
|
||||
this.hashAlgorithm = hashAlgorithm;
|
||||
this.hashCharset = hashCharset;
|
||||
this.salt = salt;
|
||||
this.iterationCount = iterationCount;
|
||||
this.derivedKey = null;
|
||||
}
|
||||
|
||||
public PBKDF2Parameters(String hashAlgorithm, String hashCharset,
|
||||
byte[] salt, int iterationCount, byte[] derivedKey)
|
||||
{
|
||||
this.hashAlgorithm = hashAlgorithm;
|
||||
this.hashCharset = hashCharset;
|
||||
this.salt = salt;
|
||||
this.iterationCount = iterationCount;
|
||||
this.derivedKey = derivedKey;
|
||||
}
|
||||
|
||||
public int getIterationCount()
|
||||
{
|
||||
return iterationCount;
|
||||
}
|
||||
|
||||
public void setIterationCount(int iterationCount)
|
||||
{
|
||||
this.iterationCount = iterationCount;
|
||||
}
|
||||
|
||||
public byte[] getSalt()
|
||||
{
|
||||
return salt;
|
||||
}
|
||||
|
||||
public void setSalt(byte[] salt)
|
||||
{
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
public byte[] getDerivedKey()
|
||||
{
|
||||
return derivedKey;
|
||||
}
|
||||
|
||||
public void setDerivedKey(byte[] derivedKey)
|
||||
{
|
||||
this.derivedKey = derivedKey;
|
||||
}
|
||||
|
||||
public String getHashAlgorithm()
|
||||
{
|
||||
return hashAlgorithm;
|
||||
}
|
||||
|
||||
public void setHashAlgorithm(String hashAlgorithm)
|
||||
{
|
||||
this.hashAlgorithm = hashAlgorithm;
|
||||
}
|
||||
|
||||
public String getHashCharset()
|
||||
{
|
||||
return hashCharset;
|
||||
}
|
||||
|
||||
public void setHashCharset(String hashCharset)
|
||||
{
|
||||
this.hashCharset = hashCharset;
|
||||
}
|
||||
}
|
31
src/net/lingala/zip4j/crypto/PBKDF2/PRF.java
Normal file
31
src/net/lingala/zip4j/crypto/PBKDF2/PRF.java
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto.PBKDF2;
|
||||
|
||||
/*
|
||||
* Source referred from Matthias Gartner's PKCS#5 implementation -
|
||||
* see http://rtner.de/software/PBKDF2.html
|
||||
*/
|
||||
|
||||
interface PRF
|
||||
{
|
||||
public void init(byte[] P);
|
||||
|
||||
public byte[] doFinal(byte[] M);
|
||||
|
||||
public int getHLen();
|
||||
}
|
97
src/net/lingala/zip4j/crypto/StandardDecrypter.java
Normal file
97
src/net/lingala/zip4j/crypto/StandardDecrypter.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto;
|
||||
|
||||
import net.lingala.zip4j.crypto.engine.ZipCryptoEngine;
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.exception.ZipExceptionConstants;
|
||||
import net.lingala.zip4j.model.FileHeader;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
|
||||
public class StandardDecrypter implements IDecrypter {
|
||||
|
||||
private FileHeader fileHeader;
|
||||
private byte[] crc = new byte[4];
|
||||
private ZipCryptoEngine zipCryptoEngine;
|
||||
|
||||
public StandardDecrypter(FileHeader fileHeader, byte[] headerBytes) throws ZipException{
|
||||
if (fileHeader == null) {
|
||||
throw new ZipException("one of more of the input parameters were null in StandardDecryptor");
|
||||
}
|
||||
|
||||
this.fileHeader = fileHeader;
|
||||
this.zipCryptoEngine = new ZipCryptoEngine();
|
||||
init(headerBytes);
|
||||
}
|
||||
|
||||
public int decryptData(byte[] buff) throws ZipException {
|
||||
return decryptData(buff, 0, buff.length);
|
||||
}
|
||||
|
||||
public int decryptData(byte[] buff, int start, int len) throws ZipException {
|
||||
if (start < 0 || len < 0) {
|
||||
throw new ZipException("one of the input parameters were null in standard decrpyt data");
|
||||
}
|
||||
|
||||
try {
|
||||
for (int i = start; i < start + len; i++) {
|
||||
int val = buff[i] & 0xff;
|
||||
val = (val ^ zipCryptoEngine.decryptByte()) & 0xff;
|
||||
zipCryptoEngine.updateKeys((byte) val);
|
||||
buff[i] = (byte)val;
|
||||
}
|
||||
return len;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void init(byte[] headerBytes) throws ZipException {
|
||||
byte[] crcBuff = fileHeader.getCrcBuff();
|
||||
crc[3] = (byte) (crcBuff[3] & 0xFF);
|
||||
crc[2] = (byte) ((crcBuff[3] >> 8) & 0xFF);
|
||||
crc[1] = (byte) ((crcBuff[3] >> 16) & 0xFF);
|
||||
crc[0] = (byte) ((crcBuff[3] >> 24) & 0xFF);
|
||||
|
||||
if(crc[2] > 0 || crc[1] > 0 || crc[0] > 0)
|
||||
throw new IllegalStateException("Invalid CRC in File Header");
|
||||
|
||||
if (fileHeader.getPassword() == null || fileHeader.getPassword().length <= 0) {
|
||||
throw new ZipException("Wrong password!", ZipExceptionConstants.WRONG_PASSWORD);
|
||||
}
|
||||
|
||||
zipCryptoEngine.initKeys(fileHeader.getPassword());
|
||||
|
||||
try {
|
||||
int result = headerBytes[0];
|
||||
for (int i = 0; i < InternalZipConstants.STD_DEC_HDR_SIZE; i++) {
|
||||
// Commented this as this check cannot always be trusted
|
||||
// New functionality: If there is an error in extracting a password protected file,
|
||||
// "Wrong Password?" text is appended to the exception message
|
||||
// if(i+1 == InternalZipConstants.STD_DEC_HDR_SIZE && ((byte)(result ^ zipCryptoEngine.decryptByte()) != crc[3]) && !isSplit)
|
||||
// throw new ZipException("Wrong password!", ZipExceptionConstants.WRONG_PASSWORD);
|
||||
|
||||
zipCryptoEngine.updateKeys((byte) (result ^ zipCryptoEngine.decryptByte()));
|
||||
if (i+1 != InternalZipConstants.STD_DEC_HDR_SIZE)
|
||||
result = headerBytes[i+1];
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
133
src/net/lingala/zip4j/crypto/StandardEncrypter.java
Normal file
133
src/net/lingala/zip4j/crypto/StandardEncrypter.java
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import net.lingala.zip4j.crypto.engine.ZipCryptoEngine;
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
|
||||
public class StandardEncrypter implements IEncrypter {
|
||||
|
||||
private ZipCryptoEngine zipCryptoEngine;
|
||||
private byte[] headerBytes;
|
||||
|
||||
public StandardEncrypter(char[] password, int crc) throws ZipException {
|
||||
if (password == null || password.length <= 0) {
|
||||
throw new ZipException("input password is null or empty in standard encrpyter constructor");
|
||||
}
|
||||
|
||||
this.zipCryptoEngine = new ZipCryptoEngine();
|
||||
|
||||
this.headerBytes = new byte[InternalZipConstants.STD_DEC_HDR_SIZE];
|
||||
init(password, crc);
|
||||
}
|
||||
|
||||
private void init(char[] password, int crc) throws ZipException {
|
||||
if (password == null || password.length <= 0) {
|
||||
throw new ZipException("input password is null or empty, cannot initialize standard encrypter");
|
||||
}
|
||||
zipCryptoEngine.initKeys(password);
|
||||
headerBytes = generateRandomBytes(InternalZipConstants.STD_DEC_HDR_SIZE);
|
||||
// Initialize again since the generated bytes were encrypted.
|
||||
zipCryptoEngine.initKeys(password);
|
||||
|
||||
headerBytes[InternalZipConstants.STD_DEC_HDR_SIZE - 1] = (byte)((crc >>> 24));
|
||||
headerBytes[InternalZipConstants.STD_DEC_HDR_SIZE - 2] = (byte)((crc >>> 16));
|
||||
|
||||
if (headerBytes.length < InternalZipConstants.STD_DEC_HDR_SIZE) {
|
||||
throw new ZipException("invalid header bytes generated, cannot perform standard encryption");
|
||||
}
|
||||
|
||||
encryptData(headerBytes);
|
||||
}
|
||||
|
||||
public int encryptData(byte[] buff) throws ZipException {
|
||||
if (buff == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
return encryptData(buff, 0, buff.length);
|
||||
}
|
||||
|
||||
public int encryptData(byte[] buff, int start, int len) throws ZipException {
|
||||
|
||||
if (len < 0) {
|
||||
throw new ZipException("invalid length specified to decrpyt data");
|
||||
}
|
||||
|
||||
try {
|
||||
for (int i = start; i < start + len; i++) {
|
||||
buff[i] = encryptByte(buff[i]);
|
||||
}
|
||||
return len;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected byte encryptByte(byte val) {
|
||||
byte temp_val = (byte) (val ^ zipCryptoEngine.decryptByte() & 0xff);
|
||||
zipCryptoEngine.updateKeys(val);
|
||||
return temp_val;
|
||||
}
|
||||
|
||||
protected byte[] generateRandomBytes(int size) throws ZipException {
|
||||
|
||||
if (size <= 0) {
|
||||
throw new ZipException("size is either 0 or less than 0, cannot generate header for standard encryptor");
|
||||
}
|
||||
|
||||
byte[] buff = new byte[size];
|
||||
|
||||
Random rand = new Random();
|
||||
|
||||
for (int i = 0; i < buff.length; i ++) {
|
||||
// Encrypted to get less predictability for poorly implemented
|
||||
// rand functions.
|
||||
buff[i] = encryptByte((byte) rand.nextInt(256));
|
||||
}
|
||||
|
||||
// buff[0] = (byte)87;
|
||||
// buff[1] = (byte)176;
|
||||
// buff[2] = (byte)-49;
|
||||
// buff[3] = (byte)-43;
|
||||
// buff[4] = (byte)93;
|
||||
// buff[5] = (byte)-204;
|
||||
// buff[6] = (byte)-105;
|
||||
// buff[7] = (byte)213;
|
||||
// buff[8] = (byte)-80;
|
||||
// buff[9] = (byte)-8;
|
||||
// buff[10] = (byte)21;
|
||||
// buff[11] = (byte)242;
|
||||
|
||||
// for( int j=0; j<2; j++ ) {
|
||||
// Random rand = new Random();
|
||||
// int i = rand.nextInt();
|
||||
// buff[0+j*4] = (byte)(i>>24);
|
||||
// buff[1+j*4] = (byte)(i>>16);
|
||||
// buff[2+j*4] = (byte)(i>>8);
|
||||
// buff[3+j*4] = (byte)i;
|
||||
// }
|
||||
return buff;
|
||||
}
|
||||
|
||||
public byte[] getHeaderBytes() {
|
||||
return headerBytes;
|
||||
}
|
||||
|
||||
}
|
291
src/net/lingala/zip4j/crypto/engine/AESEngine.java
Normal file
291
src/net/lingala/zip4j/crypto/engine/AESEngine.java
Normal file
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto.engine;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
|
||||
/**
|
||||
* Core Engine for AES Encryption
|
||||
* @author Srikanth Reddy Lingala
|
||||
*
|
||||
*/
|
||||
public class AESEngine {
|
||||
|
||||
private int rounds;
|
||||
private int[][] workingKey = null;
|
||||
private int C0, C1, C2, C3;
|
||||
|
||||
public AESEngine(byte[] key) throws ZipException {
|
||||
init(key);
|
||||
}
|
||||
|
||||
public void init(byte[] key) throws ZipException {
|
||||
workingKey = generateWorkingKey(key);
|
||||
}
|
||||
|
||||
private int[][] generateWorkingKey(byte[] key) throws ZipException {
|
||||
int kc = key.length / 4;
|
||||
int t;
|
||||
|
||||
if (((kc != 4) && (kc != 6) && (kc != 8)) || ((kc * 4) != key.length))
|
||||
{
|
||||
throw new ZipException("invalid key length (not 128/192/256)");
|
||||
}
|
||||
|
||||
rounds = kc + 6;
|
||||
int[][] W = new int[rounds+1][4];
|
||||
|
||||
t = 0;
|
||||
int i = 0;
|
||||
while (i < key.length)
|
||||
{
|
||||
W[t >> 2][t & 3] = (key[i]&0xff) | ((key[i+1]&0xff) << 8) | ((key[i+2]&0xff) << 16) | (key[i+3] << 24);
|
||||
i+=4;
|
||||
t++;
|
||||
}
|
||||
|
||||
int k = (rounds + 1) << 2;
|
||||
for (i = kc; (i < k); i++)
|
||||
{
|
||||
int temp = W[(i-1)>>2][(i-1)&3];
|
||||
if ((i % kc) == 0)
|
||||
{
|
||||
temp = subWord(shift(temp, 8)) ^ rcon[(i / kc)-1];
|
||||
}
|
||||
else if ((kc > 6) && ((i % kc) == 4))
|
||||
{
|
||||
temp = subWord(temp);
|
||||
}
|
||||
|
||||
W[i>>2][i&3] = W[(i - kc)>>2][(i-kc)&3] ^ temp;
|
||||
}
|
||||
return W;
|
||||
}
|
||||
|
||||
public int processBlock(byte[] in, byte[] out) throws ZipException {
|
||||
return processBlock(in, 0, out, 0);
|
||||
}
|
||||
|
||||
public int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws ZipException {
|
||||
if (workingKey == null)
|
||||
{
|
||||
throw new ZipException("AES engine not initialised");
|
||||
}
|
||||
|
||||
if ((inOff + (32 / 2)) > in.length)
|
||||
{
|
||||
throw new ZipException("input buffer too short");
|
||||
}
|
||||
|
||||
if ((outOff + (32 / 2)) > out.length)
|
||||
{
|
||||
throw new ZipException("output buffer too short");
|
||||
}
|
||||
|
||||
stateIn(in, inOff);
|
||||
encryptBlock(workingKey);
|
||||
stateOut(out, outOff);
|
||||
|
||||
return InternalZipConstants.AES_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
private final void stateIn(byte[] bytes, int off) {
|
||||
int index = off;
|
||||
|
||||
C0 = (bytes[index++] & 0xff);
|
||||
C0 |= (bytes[index++] & 0xff) << 8;
|
||||
C0 |= (bytes[index++] & 0xff) << 16;
|
||||
C0 |= bytes[index++] << 24;
|
||||
|
||||
C1 = (bytes[index++] & 0xff);
|
||||
C1 |= (bytes[index++] & 0xff) << 8;
|
||||
C1 |= (bytes[index++] & 0xff) << 16;
|
||||
C1 |= bytes[index++] << 24;
|
||||
|
||||
C2 = (bytes[index++] & 0xff);
|
||||
C2 |= (bytes[index++] & 0xff) << 8;
|
||||
C2 |= (bytes[index++] & 0xff) << 16;
|
||||
C2 |= bytes[index++] << 24;
|
||||
|
||||
C3 = (bytes[index++] & 0xff);
|
||||
C3 |= (bytes[index++] & 0xff) << 8;
|
||||
C3 |= (bytes[index++] & 0xff) << 16;
|
||||
C3 |= bytes[index++] << 24;
|
||||
}
|
||||
|
||||
private final void stateOut(byte[] bytes, int off) {
|
||||
int index = off;
|
||||
|
||||
bytes[index++] = (byte)C0;
|
||||
bytes[index++] = (byte)(C0 >> 8);
|
||||
bytes[index++] = (byte)(C0 >> 16);
|
||||
bytes[index++] = (byte)(C0 >> 24);
|
||||
|
||||
bytes[index++] = (byte)C1;
|
||||
bytes[index++] = (byte)(C1 >> 8);
|
||||
bytes[index++] = (byte)(C1 >> 16);
|
||||
bytes[index++] = (byte)(C1 >> 24);
|
||||
|
||||
bytes[index++] = (byte)C2;
|
||||
bytes[index++] = (byte)(C2 >> 8);
|
||||
bytes[index++] = (byte)(C2 >> 16);
|
||||
bytes[index++] = (byte)(C2 >> 24);
|
||||
|
||||
bytes[index++] = (byte)C3;
|
||||
bytes[index++] = (byte)(C3 >> 8);
|
||||
bytes[index++] = (byte)(C3 >> 16);
|
||||
bytes[index++] = (byte)(C3 >> 24);
|
||||
}
|
||||
|
||||
private final void encryptBlock(int[][] KW) {
|
||||
int r, r0, r1, r2, r3;
|
||||
|
||||
C0 ^= KW[0][0];
|
||||
C1 ^= KW[0][1];
|
||||
C2 ^= KW[0][2];
|
||||
C3 ^= KW[0][3];
|
||||
|
||||
r = 1;
|
||||
|
||||
while (r < rounds - 1)
|
||||
{
|
||||
r0 = T0[C0&255] ^ shift(T0[(C1>>8)&255], 24) ^ shift(T0[(C2>>16)&255],16) ^ shift(T0[(C3>>24)&255],8) ^ KW[r][0];
|
||||
r1 = T0[C1&255] ^ shift(T0[(C2>>8)&255], 24) ^ shift(T0[(C3>>16)&255], 16) ^ shift(T0[(C0>>24)&255], 8) ^ KW[r][1];
|
||||
r2 = T0[C2&255] ^ shift(T0[(C3>>8)&255], 24) ^ shift(T0[(C0>>16)&255], 16) ^ shift(T0[(C1>>24)&255], 8) ^ KW[r][2];
|
||||
r3 = T0[C3&255] ^ shift(T0[(C0>>8)&255], 24) ^ shift(T0[(C1>>16)&255], 16) ^ shift(T0[(C2>>24)&255], 8) ^ KW[r++][3];
|
||||
C0 = T0[r0&255] ^ shift(T0[(r1>>8)&255], 24) ^ shift(T0[(r2>>16)&255], 16) ^ shift(T0[(r3>>24)&255], 8) ^ KW[r][0];
|
||||
C1 = T0[r1&255] ^ shift(T0[(r2>>8)&255], 24) ^ shift(T0[(r3>>16)&255], 16) ^ shift(T0[(r0>>24)&255], 8) ^ KW[r][1];
|
||||
C2 = T0[r2&255] ^ shift(T0[(r3>>8)&255], 24) ^ shift(T0[(r0>>16)&255], 16) ^ shift(T0[(r1>>24)&255], 8) ^ KW[r][2];
|
||||
C3 = T0[r3&255] ^ shift(T0[(r0>>8)&255], 24) ^ shift(T0[(r1>>16)&255], 16) ^ shift(T0[(r2>>24)&255], 8) ^ KW[r++][3];
|
||||
}
|
||||
|
||||
r0 = T0[C0&255] ^ shift(T0[(C1>>8)&255], 24) ^ shift(T0[(C2>>16)&255], 16) ^ shift(T0[(C3>>24)&255], 8) ^ KW[r][0];
|
||||
r1 = T0[C1&255] ^ shift(T0[(C2>>8)&255], 24) ^ shift(T0[(C3>>16)&255], 16) ^ shift(T0[(C0>>24)&255], 8) ^ KW[r][1];
|
||||
r2 = T0[C2&255] ^ shift(T0[(C3>>8)&255], 24) ^ shift(T0[(C0>>16)&255], 16) ^ shift(T0[(C1>>24)&255], 8) ^ KW[r][2];
|
||||
r3 = T0[C3&255] ^ shift(T0[(C0>>8)&255], 24) ^ shift(T0[(C1>>16)&255], 16) ^ shift(T0[(C2>>24)&255], 8) ^ KW[r++][3];
|
||||
|
||||
C0 = (S[r0&255]&255) ^ ((S[(r1>>8)&255]&255)<<8) ^ ((S[(r2>>16)&255]&255)<<16) ^ (S[(r3>>24)&255]<<24) ^ KW[r][0];
|
||||
C1 = (S[r1&255]&255) ^ ((S[(r2>>8)&255]&255)<<8) ^ ((S[(r3>>16)&255]&255)<<16) ^ (S[(r0>>24)&255]<<24) ^ KW[r][1];
|
||||
C2 = (S[r2&255]&255) ^ ((S[(r3>>8)&255]&255)<<8) ^ ((S[(r0>>16)&255]&255)<<16) ^ (S[(r1>>24)&255]<<24) ^ KW[r][2];
|
||||
C3 = (S[r3&255]&255) ^ ((S[(r0>>8)&255]&255)<<8) ^ ((S[(r1>>16)&255]&255)<<16) ^ (S[(r2>>24)&255]<<24) ^ KW[r][3];
|
||||
|
||||
}
|
||||
|
||||
private int shift(int r, int shift) {
|
||||
return (r >>> shift) | (r << -shift);
|
||||
}
|
||||
|
||||
private int subWord(int x) {
|
||||
return (S[x&255]&255 | ((S[(x>>8)&255]&255)<<8) | ((S[(x>>16)&255]&255)<<16) | S[(x>>24)&255]<<24);
|
||||
}
|
||||
|
||||
private static final byte[] S = {
|
||||
(byte)99, (byte)124, (byte)119, (byte)123, (byte)242, (byte)107, (byte)111, (byte)197,
|
||||
(byte)48, (byte)1, (byte)103, (byte)43, (byte)254, (byte)215, (byte)171, (byte)118,
|
||||
(byte)202, (byte)130, (byte)201, (byte)125, (byte)250, (byte)89, (byte)71, (byte)240,
|
||||
(byte)173, (byte)212, (byte)162, (byte)175, (byte)156, (byte)164, (byte)114, (byte)192,
|
||||
(byte)183, (byte)253, (byte)147, (byte)38, (byte)54, (byte)63, (byte)247, (byte)204,
|
||||
(byte)52, (byte)165, (byte)229, (byte)241, (byte)113, (byte)216, (byte)49, (byte)21,
|
||||
(byte)4, (byte)199, (byte)35, (byte)195, (byte)24, (byte)150, (byte)5, (byte)154,
|
||||
(byte)7, (byte)18, (byte)128, (byte)226, (byte)235, (byte)39, (byte)178, (byte)117,
|
||||
(byte)9, (byte)131, (byte)44, (byte)26, (byte)27, (byte)110, (byte)90, (byte)160,
|
||||
(byte)82, (byte)59, (byte)214, (byte)179, (byte)41, (byte)227, (byte)47, (byte)132,
|
||||
(byte)83, (byte)209, (byte)0, (byte)237, (byte)32, (byte)252, (byte)177, (byte)91,
|
||||
(byte)106, (byte)203, (byte)190, (byte)57, (byte)74, (byte)76, (byte)88, (byte)207,
|
||||
(byte)208, (byte)239, (byte)170, (byte)251, (byte)67, (byte)77, (byte)51, (byte)133,
|
||||
(byte)69, (byte)249, (byte)2, (byte)127, (byte)80, (byte)60, (byte)159, (byte)168,
|
||||
(byte)81, (byte)163, (byte)64, (byte)143, (byte)146, (byte)157, (byte)56, (byte)245,
|
||||
(byte)188, (byte)182, (byte)218, (byte)33, (byte)16, (byte)255, (byte)243, (byte)210,
|
||||
(byte)205, (byte)12, (byte)19, (byte)236, (byte)95, (byte)151, (byte)68, (byte)23,
|
||||
(byte)196, (byte)167, (byte)126, (byte)61, (byte)100, (byte)93, (byte)25, (byte)115,
|
||||
(byte)96, (byte)129, (byte)79, (byte)220, (byte)34, (byte)42, (byte)144, (byte)136,
|
||||
(byte)70, (byte)238, (byte)184, (byte)20, (byte)222, (byte)94, (byte)11, (byte)219,
|
||||
(byte)224, (byte)50, (byte)58, (byte)10, (byte)73, (byte)6, (byte)36, (byte)92,
|
||||
(byte)194, (byte)211, (byte)172, (byte)98, (byte)145, (byte)149, (byte)228, (byte)121,
|
||||
(byte)231, (byte)200, (byte)55, (byte)109, (byte)141, (byte)213, (byte)78, (byte)169,
|
||||
(byte)108, (byte)86, (byte)244, (byte)234, (byte)101, (byte)122, (byte)174, (byte)8,
|
||||
(byte)186, (byte)120, (byte)37, (byte)46, (byte)28, (byte)166, (byte)180, (byte)198,
|
||||
(byte)232, (byte)221, (byte)116, (byte)31, (byte)75, (byte)189, (byte)139, (byte)138,
|
||||
(byte)112, (byte)62, (byte)181, (byte)102, (byte)72, (byte)3, (byte)246, (byte)14,
|
||||
(byte)97, (byte)53, (byte)87, (byte)185, (byte)134, (byte)193, (byte)29, (byte)158,
|
||||
(byte)225, (byte)248, (byte)152, (byte)17, (byte)105, (byte)217, (byte)142, (byte)148,
|
||||
(byte)155, (byte)30, (byte)135, (byte)233, (byte)206, (byte)85, (byte)40, (byte)223,
|
||||
(byte)140, (byte)161, (byte)137, (byte)13, (byte)191, (byte)230, (byte)66, (byte)104,
|
||||
(byte)65, (byte)153, (byte)45, (byte)15, (byte)176, (byte)84, (byte)187, (byte)22,
|
||||
};
|
||||
|
||||
private static final int[] rcon = {
|
||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
|
||||
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
|
||||
|
||||
private static final int[] T0 =
|
||||
{
|
||||
0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff,
|
||||
0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102,
|
||||
0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
|
||||
0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
|
||||
0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41,
|
||||
0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
|
||||
0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d,
|
||||
0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
|
||||
0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2,
|
||||
0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795,
|
||||
0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a,
|
||||
0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
|
||||
0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912,
|
||||
0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc,
|
||||
0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7,
|
||||
0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
|
||||
0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040,
|
||||
0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
|
||||
0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0,
|
||||
0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
|
||||
0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
|
||||
0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78,
|
||||
0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080,
|
||||
0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
|
||||
0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020,
|
||||
0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18,
|
||||
0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488,
|
||||
0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
|
||||
0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0,
|
||||
0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
|
||||
0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b,
|
||||
0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
|
||||
0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992,
|
||||
0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd,
|
||||
0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3,
|
||||
0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
|
||||
0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8,
|
||||
0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4,
|
||||
0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
|
||||
0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
|
||||
0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96,
|
||||
0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
|
||||
0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7,
|
||||
0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
|
||||
0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9,
|
||||
0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9,
|
||||
0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715,
|
||||
0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
|
||||
0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65,
|
||||
0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929,
|
||||
0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d,
|
||||
0x3a16162c};
|
||||
|
||||
}
|
65
src/net/lingala/zip4j/crypto/engine/ZipCryptoEngine.java
Normal file
65
src/net/lingala/zip4j/crypto/engine/ZipCryptoEngine.java
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.crypto.engine;
|
||||
|
||||
public class ZipCryptoEngine {
|
||||
|
||||
private final int keys[] = new int[3];
|
||||
private static final int[] CRC_TABLE = new int[256];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
int r = i;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if ((r & 1) == 1) {
|
||||
r = (r >>> 1) ^ 0xedb88320;
|
||||
} else {
|
||||
r >>>= 1;
|
||||
}
|
||||
}
|
||||
CRC_TABLE[i] = r;
|
||||
}
|
||||
}
|
||||
|
||||
public ZipCryptoEngine() {
|
||||
}
|
||||
|
||||
public void initKeys(char[] password) {
|
||||
keys[0] = 305419896;
|
||||
keys[1] = 591751049;
|
||||
keys[2] = 878082192;
|
||||
for (int i = 0; i < password.length; i++) {
|
||||
updateKeys((byte) (password[i] & 0xff));
|
||||
}
|
||||
}
|
||||
|
||||
public void updateKeys(byte charAt) {
|
||||
keys[0] = crc32(keys[0], charAt);
|
||||
keys[1] += keys[0] & 0xff;
|
||||
keys[1] = keys[1] * 134775813 + 1;
|
||||
keys[2] = crc32(keys[2], (byte) (keys[1] >> 24));
|
||||
}
|
||||
|
||||
private int crc32(int oldCrc, byte charAt) {
|
||||
return ((oldCrc >>> 8) ^ CRC_TABLE[(oldCrc ^ charAt) & 0xff]);
|
||||
}
|
||||
|
||||
public byte decryptByte() {
|
||||
int temp = keys[2] | 2;
|
||||
return (byte) ((temp * (temp ^ 1)) >>> 8);
|
||||
}
|
||||
}
|
59
src/net/lingala/zip4j/exception/ZipException.java
Normal file
59
src/net/lingala/zip4j/exception/ZipException.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.exception;
|
||||
|
||||
public class ZipException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int code = -1;
|
||||
|
||||
public ZipException() {
|
||||
}
|
||||
|
||||
public ZipException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ZipException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ZipException(String msg, int code) {
|
||||
super(msg);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public ZipException(String message, Throwable cause, int code) {
|
||||
super(message, cause);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public ZipException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ZipException(Throwable cause, int code) {
|
||||
super(cause);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
31
src/net/lingala/zip4j/exception/ZipExceptionConstants.java
Normal file
31
src/net/lingala/zip4j/exception/ZipExceptionConstants.java
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.exception;
|
||||
|
||||
public interface ZipExceptionConstants {
|
||||
|
||||
public static int inputZipParamIsNull = 0001;
|
||||
|
||||
public static int constuctorFileNotFoundException = 0002;
|
||||
|
||||
public static int randomAccessFileNull = 0003;
|
||||
|
||||
public static int notZipFile = 0004;
|
||||
|
||||
public static int WRONG_PASSWORD = 0005;
|
||||
|
||||
}
|
25
src/net/lingala/zip4j/io/BaseInputStream.java
Normal file
25
src/net/lingala/zip4j/io/BaseInputStream.java
Normal file
|
@ -0,0 +1,25 @@
|
|||
package net.lingala.zip4j.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.lingala.zip4j.unzip.UnzipEngine;
|
||||
|
||||
public abstract class BaseInputStream extends InputStream {
|
||||
|
||||
public int read() throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void seek(long pos) throws IOException {
|
||||
}
|
||||
|
||||
public int available() throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public UnzipEngine getUnzipEngine() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
27
src/net/lingala/zip4j/io/BaseOutputStream.java
Normal file
27
src/net/lingala/zip4j/io/BaseOutputStream.java
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public abstract class BaseOutputStream extends OutputStream {
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
}
|
||||
|
||||
}
|
570
src/net/lingala/zip4j/io/CipherOutputStream.java
Normal file
570
src/net/lingala/zip4j/io/CipherOutputStream.java
Normal file
|
@ -0,0 +1,570 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
import net.lingala.zip4j.core.HeaderWriter;
|
||||
import net.lingala.zip4j.crypto.AESEncrpyter;
|
||||
import net.lingala.zip4j.crypto.IEncrypter;
|
||||
import net.lingala.zip4j.crypto.StandardEncrypter;
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.model.AESExtraDataRecord;
|
||||
import net.lingala.zip4j.model.CentralDirectory;
|
||||
import net.lingala.zip4j.model.EndCentralDirRecord;
|
||||
import net.lingala.zip4j.model.FileHeader;
|
||||
import net.lingala.zip4j.model.LocalFileHeader;
|
||||
import net.lingala.zip4j.model.ZipModel;
|
||||
import net.lingala.zip4j.model.ZipParameters;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
import net.lingala.zip4j.util.Raw;
|
||||
import net.lingala.zip4j.util.Zip4jConstants;
|
||||
import net.lingala.zip4j.util.Zip4jUtil;
|
||||
|
||||
public class CipherOutputStream extends BaseOutputStream {
|
||||
|
||||
protected OutputStream outputStream;
|
||||
private File sourceFile;
|
||||
protected FileHeader fileHeader;
|
||||
protected LocalFileHeader localFileHeader;
|
||||
private IEncrypter encrypter;
|
||||
protected ZipParameters zipParameters;
|
||||
protected ZipModel zipModel;
|
||||
private long totalBytesWritten;
|
||||
protected CRC32 crc;
|
||||
private long bytesWrittenForThisFile;
|
||||
private byte[] pendingBuffer;
|
||||
private int pendingBufferLength;
|
||||
private long totalBytesRead;
|
||||
|
||||
public CipherOutputStream(OutputStream outputStream, ZipModel zipModel) {
|
||||
this.outputStream = outputStream;
|
||||
initZipModel(zipModel);
|
||||
crc = new CRC32();
|
||||
this.totalBytesWritten = 0;
|
||||
this.bytesWrittenForThisFile = 0;
|
||||
this.pendingBuffer = new byte[InternalZipConstants.AES_BLOCK_SIZE];
|
||||
this.pendingBufferLength = 0;
|
||||
this.totalBytesRead = 0;
|
||||
}
|
||||
|
||||
public void putNextEntry(File file, ZipParameters zipParameters) throws ZipException {
|
||||
if (!zipParameters.isSourceExternalStream() && file == null) {
|
||||
throw new ZipException("input file is null");
|
||||
}
|
||||
|
||||
if (!zipParameters.isSourceExternalStream() && !Zip4jUtil.checkFileExists(file)) {
|
||||
throw new ZipException("input file does not exist");
|
||||
}
|
||||
|
||||
try {
|
||||
sourceFile = file;
|
||||
|
||||
this.zipParameters = (ZipParameters)zipParameters.clone();
|
||||
|
||||
if (!zipParameters.isSourceExternalStream()) {
|
||||
if (sourceFile.isDirectory()) {
|
||||
this.zipParameters.setEncryptFiles(false);
|
||||
this.zipParameters.setEncryptionMethod(-1);
|
||||
this.zipParameters.setCompressionMethod(Zip4jConstants.COMP_STORE);
|
||||
}
|
||||
} else {
|
||||
if (!Zip4jUtil.isStringNotNullAndNotEmpty(this.zipParameters.getFileNameInZip())) {
|
||||
throw new ZipException("file name is empty for external stream");
|
||||
}
|
||||
if (this.zipParameters.getFileNameInZip().endsWith("/") ||
|
||||
this.zipParameters.getFileNameInZip().endsWith("\\")) {
|
||||
this.zipParameters.setEncryptFiles(false);
|
||||
this.zipParameters.setEncryptionMethod(-1);
|
||||
this.zipParameters.setCompressionMethod(Zip4jConstants.COMP_STORE);
|
||||
}
|
||||
}
|
||||
|
||||
createFileHeader();
|
||||
createLocalFileHeader();
|
||||
|
||||
if (zipModel.isSplitArchive()) {
|
||||
if (zipModel.getCentralDirectory() == null ||
|
||||
zipModel.getCentralDirectory().getFileHeaders() == null ||
|
||||
zipModel.getCentralDirectory().getFileHeaders().size() == 0) {
|
||||
byte[] intByte = new byte[4];
|
||||
Raw.writeIntLittleEndian(intByte, 0, (int)InternalZipConstants.SPLITSIG);
|
||||
outputStream.write(intByte);
|
||||
totalBytesWritten += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.outputStream instanceof SplitOutputStream) {
|
||||
if (totalBytesWritten == 4) {
|
||||
fileHeader.setOffsetLocalHeader(4);
|
||||
} else {
|
||||
fileHeader.setOffsetLocalHeader(((SplitOutputStream)outputStream).getFilePointer());
|
||||
}
|
||||
} else {
|
||||
if (totalBytesWritten == 4) {
|
||||
fileHeader.setOffsetLocalHeader(4);
|
||||
} else {
|
||||
fileHeader.setOffsetLocalHeader(totalBytesWritten);
|
||||
}
|
||||
}
|
||||
|
||||
HeaderWriter headerWriter = new HeaderWriter();
|
||||
totalBytesWritten += headerWriter.writeLocalFileHeader(zipModel, localFileHeader, outputStream);
|
||||
|
||||
if (this.zipParameters.isEncryptFiles()) {
|
||||
initEncrypter();
|
||||
if (encrypter != null) {
|
||||
if (zipParameters.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_STANDARD) {
|
||||
byte[] headerBytes = ((StandardEncrypter)encrypter).getHeaderBytes();
|
||||
outputStream.write(headerBytes);
|
||||
totalBytesWritten += headerBytes.length;
|
||||
bytesWrittenForThisFile += headerBytes.length;
|
||||
} else if (zipParameters.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES) {
|
||||
byte[] saltBytes = ((AESEncrpyter)encrypter).getSaltBytes();
|
||||
byte[] passwordVerifier = ((AESEncrpyter)encrypter).getDerivedPasswordVerifier();
|
||||
outputStream.write(saltBytes);
|
||||
outputStream.write(passwordVerifier);
|
||||
totalBytesWritten += saltBytes.length + passwordVerifier.length;
|
||||
bytesWrittenForThisFile += saltBytes.length + passwordVerifier.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crc.reset();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new ZipException(e);
|
||||
} catch (ZipException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void initEncrypter() throws ZipException {
|
||||
if (!zipParameters.isEncryptFiles()) {
|
||||
encrypter = null;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (zipParameters.getEncryptionMethod()) {
|
||||
case Zip4jConstants.ENC_METHOD_STANDARD:
|
||||
// Since we do not know the crc here, we use the modification time for encrypting.
|
||||
encrypter = new StandardEncrypter(zipParameters.getPassword(), (localFileHeader.getLastModFileTime() & 0x0000ffff) << 16);
|
||||
break;
|
||||
case Zip4jConstants.ENC_METHOD_AES:
|
||||
encrypter = new AESEncrpyter(zipParameters.getPassword(), zipParameters.getAesKeyStrength());
|
||||
break;
|
||||
default:
|
||||
throw new ZipException("invalid encprytion method");
|
||||
}
|
||||
}
|
||||
|
||||
private void initZipModel(ZipModel zipModel) {
|
||||
if (zipModel == null) {
|
||||
this.zipModel = new ZipModel();
|
||||
} else {
|
||||
this.zipModel = zipModel;
|
||||
}
|
||||
|
||||
if (this.zipModel.getEndCentralDirRecord() == null)
|
||||
this.zipModel.setEndCentralDirRecord(new EndCentralDirRecord());
|
||||
|
||||
if (this.zipModel.getCentralDirectory() == null)
|
||||
this.zipModel.setCentralDirectory(new CentralDirectory());
|
||||
|
||||
if (this.zipModel.getCentralDirectory().getFileHeaders() == null)
|
||||
this.zipModel.getCentralDirectory().setFileHeaders(new ArrayList());
|
||||
|
||||
if (this.zipModel.getLocalFileHeaderList() == null)
|
||||
this.zipModel.setLocalFileHeaderList(new ArrayList());
|
||||
|
||||
if (this.outputStream instanceof SplitOutputStream) {
|
||||
if (((SplitOutputStream)outputStream).isSplitZipFile()) {
|
||||
this.zipModel.setSplitArchive(true);
|
||||
this.zipModel.setSplitLength(((SplitOutputStream)outputStream).getSplitLength());
|
||||
}
|
||||
}
|
||||
|
||||
this.zipModel.getEndCentralDirRecord().setSignature(InternalZipConstants.ENDSIG);
|
||||
}
|
||||
|
||||
public void write(int bval) throws IOException {
|
||||
byte[] b = new byte[1];
|
||||
b[0] = (byte) bval;
|
||||
write(b, 0, 1);
|
||||
}
|
||||
|
||||
public void write(byte[] b) throws IOException {
|
||||
if (b == null)
|
||||
throw new NullPointerException();
|
||||
|
||||
if (b.length == 0) return;
|
||||
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
if (len == 0) return;
|
||||
|
||||
if (zipParameters.isEncryptFiles() &&
|
||||
zipParameters.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES) {
|
||||
if (pendingBufferLength != 0) {
|
||||
if (len >= (InternalZipConstants.AES_BLOCK_SIZE - pendingBufferLength)) {
|
||||
System.arraycopy(b, off, pendingBuffer, pendingBufferLength,
|
||||
(InternalZipConstants.AES_BLOCK_SIZE - pendingBufferLength));
|
||||
encryptAndWrite(pendingBuffer, 0, pendingBuffer.length);
|
||||
off = (InternalZipConstants.AES_BLOCK_SIZE - pendingBufferLength);
|
||||
len = len - off;
|
||||
pendingBufferLength = 0;
|
||||
} else {
|
||||
System.arraycopy(b, off, pendingBuffer, pendingBufferLength,
|
||||
len);
|
||||
pendingBufferLength += len;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (len != 0 && len % 16 != 0) {
|
||||
System.arraycopy(b, (len + off) - (len % 16), pendingBuffer, 0, len % 16);
|
||||
pendingBufferLength = len % 16;
|
||||
len = len - pendingBufferLength;
|
||||
}
|
||||
}
|
||||
if (len != 0)
|
||||
encryptAndWrite(b, off, len);
|
||||
}
|
||||
|
||||
private void encryptAndWrite(byte[] b, int off, int len) throws IOException {
|
||||
if (encrypter != null) {
|
||||
try {
|
||||
encrypter.encryptData(b, off, len);
|
||||
} catch (ZipException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
outputStream.write(b, off, len);
|
||||
totalBytesWritten += len;
|
||||
bytesWrittenForThisFile += len;
|
||||
}
|
||||
|
||||
public void closeEntry() throws IOException, ZipException {
|
||||
|
||||
if (this.pendingBufferLength != 0) {
|
||||
encryptAndWrite(pendingBuffer, 0, pendingBufferLength);
|
||||
pendingBufferLength = 0;
|
||||
}
|
||||
|
||||
if (this.zipParameters.isEncryptFiles() &&
|
||||
this.zipParameters.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES) {
|
||||
if (encrypter instanceof AESEncrpyter) {
|
||||
outputStream.write(((AESEncrpyter)encrypter).getFinalMac());
|
||||
bytesWrittenForThisFile += 10;
|
||||
totalBytesWritten += 10;
|
||||
} else {
|
||||
throw new ZipException("invalid encrypter for AES encrypted file");
|
||||
}
|
||||
}
|
||||
fileHeader.setCompressedSize(bytesWrittenForThisFile);
|
||||
localFileHeader.setCompressedSize(bytesWrittenForThisFile);
|
||||
|
||||
if (zipParameters.isSourceExternalStream()) {
|
||||
fileHeader.setUncompressedSize(totalBytesRead);
|
||||
if (localFileHeader.getUncompressedSize() != totalBytesRead) {
|
||||
localFileHeader.setUncompressedSize(totalBytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
long crc32 = crc.getValue();
|
||||
if (fileHeader.isEncrypted()) {
|
||||
if (fileHeader.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES) {
|
||||
crc32 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (zipParameters.isEncryptFiles() &&
|
||||
zipParameters.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES) {
|
||||
fileHeader.setCrc32(0);
|
||||
localFileHeader.setCrc32(0);
|
||||
} else {
|
||||
fileHeader.setCrc32(crc32);
|
||||
localFileHeader.setCrc32(crc32);
|
||||
}
|
||||
|
||||
zipModel.getLocalFileHeaderList().add(localFileHeader);
|
||||
zipModel.getCentralDirectory().getFileHeaders().add(fileHeader);
|
||||
|
||||
HeaderWriter headerWriter = new HeaderWriter();
|
||||
totalBytesWritten += headerWriter.writeExtendedLocalHeader(localFileHeader, outputStream);
|
||||
|
||||
crc.reset();
|
||||
bytesWrittenForThisFile = 0;
|
||||
encrypter = null;
|
||||
totalBytesRead = 0;
|
||||
}
|
||||
|
||||
public void finish() throws IOException, ZipException {
|
||||
zipModel.getEndCentralDirRecord().setOffsetOfStartOfCentralDir(totalBytesWritten);
|
||||
|
||||
HeaderWriter headerWriter = new HeaderWriter();
|
||||
headerWriter.finalizeZipFile(zipModel, outputStream);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (outputStream != null)
|
||||
outputStream.close();
|
||||
}
|
||||
|
||||
private void createFileHeader() throws ZipException {
|
||||
this.fileHeader = new FileHeader();
|
||||
fileHeader.setSignature((int)InternalZipConstants.CENSIG);
|
||||
fileHeader.setVersionMadeBy(20);
|
||||
fileHeader.setVersionNeededToExtract(20);
|
||||
if (zipParameters.isEncryptFiles() &&
|
||||
zipParameters.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES) {
|
||||
fileHeader.setCompressionMethod(Zip4jConstants.ENC_METHOD_AES);
|
||||
fileHeader.setAesExtraDataRecord(generateAESExtraDataRecord(zipParameters));
|
||||
} else {
|
||||
fileHeader.setCompressionMethod(zipParameters.getCompressionMethod());
|
||||
}
|
||||
if (zipParameters.isEncryptFiles()) {
|
||||
fileHeader.setEncrypted(true);
|
||||
fileHeader.setEncryptionMethod(zipParameters.getEncryptionMethod());
|
||||
}
|
||||
String fileName = null;
|
||||
if (zipParameters.isSourceExternalStream()) {
|
||||
fileHeader.setLastModFileTime((int) Zip4jUtil.javaToDosTime(System.currentTimeMillis()));
|
||||
if (!Zip4jUtil.isStringNotNullAndNotEmpty(zipParameters.getFileNameInZip())) {
|
||||
throw new ZipException("fileNameInZip is null or empty");
|
||||
}
|
||||
fileName = zipParameters.getFileNameInZip();
|
||||
} else {
|
||||
fileHeader.setLastModFileTime((int) Zip4jUtil.javaToDosTime((Zip4jUtil.getLastModifiedFileTime(
|
||||
sourceFile, zipParameters.getTimeZone()))));
|
||||
fileHeader.setUncompressedSize(sourceFile.length());
|
||||
fileName = Zip4jUtil.getRelativeFileName(
|
||||
sourceFile.getAbsolutePath(), zipParameters.getRootFolderInZip(), zipParameters.getDefaultFolderPath());
|
||||
|
||||
}
|
||||
|
||||
if (!Zip4jUtil.isStringNotNullAndNotEmpty(fileName)) {
|
||||
throw new ZipException("fileName is null or empty. unable to create file header");
|
||||
}
|
||||
|
||||
fileHeader.setFileName(fileName);
|
||||
|
||||
if (Zip4jUtil.isStringNotNullAndNotEmpty(zipModel.getFileNameCharset())) {
|
||||
fileHeader.setFileNameLength(Zip4jUtil.getEncodedStringLength(fileName,
|
||||
zipModel.getFileNameCharset()));
|
||||
} else {
|
||||
fileHeader.setFileNameLength(Zip4jUtil.getEncodedStringLength(fileName));
|
||||
}
|
||||
|
||||
if (outputStream instanceof SplitOutputStream) {
|
||||
fileHeader.setDiskNumberStart(((SplitOutputStream)outputStream).getCurrSplitFileCounter());
|
||||
} else {
|
||||
fileHeader.setDiskNumberStart(0);
|
||||
}
|
||||
|
||||
int fileAttrs = 0;
|
||||
if (!zipParameters.isSourceExternalStream())
|
||||
fileAttrs = getFileAttributes(sourceFile);
|
||||
byte[] externalFileAttrs = {(byte)fileAttrs, 0, 0, 0};
|
||||
fileHeader.setExternalFileAttr(externalFileAttrs);
|
||||
|
||||
if (zipParameters.isSourceExternalStream()) {
|
||||
fileHeader.setDirectory(fileName.endsWith("/") || fileName.endsWith("\\"));
|
||||
} else {
|
||||
fileHeader.setDirectory(this.sourceFile.isDirectory());
|
||||
}
|
||||
if (fileHeader.isDirectory()) {
|
||||
fileHeader.setCompressedSize(0);
|
||||
fileHeader.setUncompressedSize(0);
|
||||
} else {
|
||||
if (!zipParameters.isSourceExternalStream()) {
|
||||
long fileSize = Zip4jUtil.getFileLengh(sourceFile);
|
||||
if (zipParameters.getCompressionMethod() == Zip4jConstants.COMP_STORE) {
|
||||
if (zipParameters.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_STANDARD) {
|
||||
fileHeader.setCompressedSize(fileSize
|
||||
+ InternalZipConstants.STD_DEC_HDR_SIZE);
|
||||
} else if (zipParameters.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES) {
|
||||
int saltLength = 0;
|
||||
switch (zipParameters.getAesKeyStrength()) {
|
||||
case Zip4jConstants.AES_STRENGTH_128:
|
||||
saltLength = 8;
|
||||
break;
|
||||
case Zip4jConstants.AES_STRENGTH_256:
|
||||
saltLength = 16;
|
||||
break;
|
||||
default:
|
||||
throw new ZipException("invalid aes key strength, cannot determine key sizes");
|
||||
}
|
||||
fileHeader.setCompressedSize(fileSize + saltLength
|
||||
+ InternalZipConstants.AES_AUTH_LENGTH + 2); //2 is password verifier
|
||||
} else {
|
||||
fileHeader.setCompressedSize(0);
|
||||
}
|
||||
} else {
|
||||
fileHeader.setCompressedSize(0);
|
||||
}
|
||||
fileHeader.setUncompressedSize(fileSize);
|
||||
}
|
||||
}
|
||||
if (zipParameters.isEncryptFiles() &&
|
||||
zipParameters.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_STANDARD) {
|
||||
fileHeader.setCrc32(zipParameters.getSourceFileCRC());
|
||||
}
|
||||
byte[] shortByte = new byte[2];
|
||||
shortByte[0] = Raw.bitArrayToByte(generateGeneralPurposeBitArray(
|
||||
fileHeader.isEncrypted(), zipParameters.getCompressionMethod()));
|
||||
boolean isFileNameCharsetSet = Zip4jUtil.isStringNotNullAndNotEmpty(zipModel.getFileNameCharset());
|
||||
if ((isFileNameCharsetSet &&
|
||||
zipModel.getFileNameCharset().equalsIgnoreCase(InternalZipConstants.CHARSET_UTF8)) ||
|
||||
(!isFileNameCharsetSet &&
|
||||
Zip4jUtil.detectCharSet(fileHeader.getFileName()).equals(InternalZipConstants.CHARSET_UTF8))) {
|
||||
shortByte[1] = 8;
|
||||
} else {
|
||||
shortByte[1] = 0;
|
||||
}
|
||||
fileHeader.setGeneralPurposeFlag(shortByte);
|
||||
}
|
||||
|
||||
private void createLocalFileHeader() throws ZipException {
|
||||
if (fileHeader == null) {
|
||||
throw new ZipException("file header is null, cannot create local file header");
|
||||
}
|
||||
this.localFileHeader = new LocalFileHeader();
|
||||
localFileHeader.setSignature((int)InternalZipConstants.LOCSIG);
|
||||
localFileHeader.setVersionNeededToExtract(fileHeader.getVersionNeededToExtract());
|
||||
localFileHeader.setCompressionMethod(fileHeader.getCompressionMethod());
|
||||
localFileHeader.setLastModFileTime(fileHeader.getLastModFileTime());
|
||||
localFileHeader.setUncompressedSize(fileHeader.getUncompressedSize());
|
||||
localFileHeader.setFileNameLength(fileHeader.getFileNameLength());
|
||||
localFileHeader.setFileName(fileHeader.getFileName());
|
||||
localFileHeader.setEncrypted(fileHeader.isEncrypted());
|
||||
localFileHeader.setEncryptionMethod(fileHeader.getEncryptionMethod());
|
||||
localFileHeader.setAesExtraDataRecord(fileHeader.getAesExtraDataRecord());
|
||||
localFileHeader.setCrc32(fileHeader.getCrc32());
|
||||
localFileHeader.setCompressedSize(fileHeader.getCompressedSize());
|
||||
localFileHeader.setGeneralPurposeFlag((byte[])fileHeader.getGeneralPurposeFlag().clone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the file attributes and returns an integer
|
||||
* @param file
|
||||
* @return
|
||||
* @throws ZipException
|
||||
*/
|
||||
private int getFileAttributes(File file) throws ZipException {
|
||||
if (file == null) {
|
||||
throw new ZipException("input file is null, cannot get file attributes");
|
||||
}
|
||||
|
||||
if (!file.exists()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (file.isDirectory()) {
|
||||
if (file.isHidden()) {
|
||||
return InternalZipConstants.FOLDER_MODE_HIDDEN;
|
||||
} else {
|
||||
return InternalZipConstants.FOLDER_MODE_NONE;
|
||||
}
|
||||
} else {
|
||||
if (!file.canWrite() && file.isHidden()) {
|
||||
return InternalZipConstants.FILE_MODE_READ_ONLY_HIDDEN;
|
||||
} else if (!file.canWrite()) {
|
||||
return InternalZipConstants.FILE_MODE_READ_ONLY;
|
||||
} else if (file.isHidden()) {
|
||||
return InternalZipConstants.FILE_MODE_HIDDEN;
|
||||
} else {
|
||||
return InternalZipConstants.FILE_MODE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int[] generateGeneralPurposeBitArray(boolean isEncrpyted, int compressionMethod) {
|
||||
|
||||
int[] generalPurposeBits = new int[8];
|
||||
if (isEncrpyted) {
|
||||
generalPurposeBits[0] = 1;
|
||||
} else {
|
||||
generalPurposeBits[0] = 0;
|
||||
}
|
||||
|
||||
if (compressionMethod == Zip4jConstants.COMP_DEFLATE) {
|
||||
// Have to set flags for deflate
|
||||
} else {
|
||||
generalPurposeBits[1] = 0;
|
||||
generalPurposeBits[2] = 0;
|
||||
}
|
||||
|
||||
generalPurposeBits[3] = 1;
|
||||
|
||||
return generalPurposeBits;
|
||||
}
|
||||
|
||||
private AESExtraDataRecord generateAESExtraDataRecord(ZipParameters parameters) throws ZipException {
|
||||
|
||||
if (parameters == null) {
|
||||
throw new ZipException("zip parameters are null, cannot generate AES Extra Data record");
|
||||
}
|
||||
|
||||
AESExtraDataRecord aesDataRecord = new AESExtraDataRecord();
|
||||
aesDataRecord.setSignature(InternalZipConstants.AESSIG);
|
||||
aesDataRecord.setDataSize(7);
|
||||
aesDataRecord.setVendorID("AE");
|
||||
// Always set the version number to 2 as we do not store CRC for any AES encrypted files
|
||||
// only MAC is stored and as per the specification, if version number is 2, then MAC is read
|
||||
// and CRC is ignored
|
||||
aesDataRecord.setVersionNumber(2);
|
||||
if (parameters.getAesKeyStrength() == Zip4jConstants.AES_STRENGTH_128) {
|
||||
aesDataRecord.setAesStrength(Zip4jConstants.AES_STRENGTH_128);
|
||||
} else if (parameters.getAesKeyStrength() == Zip4jConstants.AES_STRENGTH_256) {
|
||||
aesDataRecord.setAesStrength(Zip4jConstants.AES_STRENGTH_256);
|
||||
} else {
|
||||
throw new ZipException("invalid AES key strength, cannot generate AES Extra data record");
|
||||
}
|
||||
aesDataRecord.setCompressionMethod(parameters.getCompressionMethod());
|
||||
|
||||
return aesDataRecord;
|
||||
}
|
||||
|
||||
public void decrementCompressedFileSize(int value) {
|
||||
if (value <= 0) return;
|
||||
|
||||
if (value <= this.bytesWrittenForThisFile) {
|
||||
this.bytesWrittenForThisFile -= value;
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateTotalBytesRead(int toUpdate) {
|
||||
if (toUpdate > 0) {
|
||||
totalBytesRead += toUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSourceFile(File sourceFile) {
|
||||
this.sourceFile = sourceFile;
|
||||
}
|
||||
|
||||
public File getSourceFile() {
|
||||
return sourceFile;
|
||||
}
|
||||
}
|
115
src/net/lingala/zip4j/io/DeflaterOutputStream.java
Normal file
115
src/net/lingala/zip4j/io/DeflaterOutputStream.java
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.model.ZipModel;
|
||||
import net.lingala.zip4j.model.ZipParameters;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
import net.lingala.zip4j.util.Zip4jConstants;
|
||||
|
||||
public class DeflaterOutputStream extends CipherOutputStream {
|
||||
|
||||
private byte[] buff;
|
||||
protected Deflater deflater;
|
||||
private boolean firstBytesRead;
|
||||
|
||||
public DeflaterOutputStream(OutputStream outputStream, ZipModel zipModel) {
|
||||
super(outputStream, zipModel);
|
||||
deflater = new Deflater();
|
||||
buff = new byte[InternalZipConstants.BUFF_SIZE];
|
||||
firstBytesRead = false;
|
||||
}
|
||||
|
||||
public void putNextEntry(File file, ZipParameters zipParameters)
|
||||
throws ZipException {
|
||||
super.putNextEntry(file, zipParameters);
|
||||
if (zipParameters.getCompressionMethod() == Zip4jConstants.COMP_DEFLATE) {
|
||||
deflater.reset();
|
||||
if ((zipParameters.getCompressionLevel() < 0 || zipParameters
|
||||
.getCompressionLevel() > 9)
|
||||
&& zipParameters.getCompressionLevel() != -1) {
|
||||
throw new ZipException(
|
||||
"invalid compression level for deflater. compression level should be in the range of 0-9");
|
||||
}
|
||||
deflater.setLevel(zipParameters.getCompressionLevel());
|
||||
}
|
||||
}
|
||||
|
||||
public void write(byte[] b) throws IOException {
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
private void deflate () throws IOException {
|
||||
int len = deflater.deflate(buff, 0, buff.length);
|
||||
if (len > 0) {
|
||||
if (deflater.finished()) {
|
||||
if (len == 4) return;
|
||||
if (len < 4) {
|
||||
decrementCompressedFileSize(4 - len);
|
||||
return;
|
||||
}
|
||||
len -= 4;
|
||||
}
|
||||
if (!firstBytesRead) {
|
||||
super.write(buff, 2, len - 2);
|
||||
firstBytesRead = true;
|
||||
} else {
|
||||
super.write(buff, 0, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void write(int bval) throws IOException {
|
||||
byte[] b = new byte[1];
|
||||
b[0] = (byte) bval;
|
||||
write(b, 0, 1);
|
||||
}
|
||||
|
||||
public void write(byte[] buf, int off, int len) throws IOException {
|
||||
if (zipParameters.getCompressionMethod() != Zip4jConstants.COMP_DEFLATE) {
|
||||
super.write(buf, off, len);
|
||||
} else {
|
||||
deflater.setInput(buf, off, len);
|
||||
while (!deflater.needsInput()) {
|
||||
deflate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void closeEntry() throws IOException, ZipException {
|
||||
if (zipParameters.getCompressionMethod() == Zip4jConstants.COMP_DEFLATE) {
|
||||
if (!deflater.finished()) {
|
||||
deflater.finish();
|
||||
while (!deflater.finished()) {
|
||||
deflate();
|
||||
}
|
||||
}
|
||||
firstBytesRead = false;
|
||||
}
|
||||
super.closeEntry();
|
||||
}
|
||||
|
||||
public void finish() throws IOException, ZipException {
|
||||
super.finish();
|
||||
}
|
||||
}
|
174
src/net/lingala/zip4j/io/InflaterInputStream.java
Normal file
174
src/net/lingala/zip4j/io/InflaterInputStream.java
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.io;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
import net.lingala.zip4j.unzip.UnzipEngine;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
import net.lingala.zip4j.util.Zip4jConstants;
|
||||
|
||||
public class InflaterInputStream extends PartInputStream {
|
||||
|
||||
private Inflater inflater;
|
||||
private byte[] buff;
|
||||
private byte[] oneByteBuff = new byte[1];
|
||||
private UnzipEngine unzipEngine;
|
||||
private long bytesWritten;
|
||||
private long uncompressedSize;
|
||||
|
||||
public InflaterInputStream(RandomAccessFile raf, long start, long len, UnzipEngine unzipEngine) {
|
||||
super(raf, start, len, unzipEngine);
|
||||
this.inflater = new Inflater(true);
|
||||
this.buff = new byte[InternalZipConstants.BUFF_SIZE];
|
||||
this.unzipEngine = unzipEngine;
|
||||
bytesWritten = 0;
|
||||
uncompressedSize = unzipEngine.getFileHeader().getUncompressedSize();
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
return read(oneByteBuff, 0, 1) == -1 ? -1 : oneByteBuff[0] & 0xff;
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException {
|
||||
if (b == null) {
|
||||
throw new NullPointerException("input buffer is null");
|
||||
}
|
||||
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
|
||||
if (b == null) {
|
||||
throw new NullPointerException("input buffer is null");
|
||||
} else if (off < 0 || len < 0 || len > b.length - off) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
int n;
|
||||
if (bytesWritten >= uncompressedSize) {
|
||||
finishInflating();
|
||||
return -1;
|
||||
}
|
||||
while ((n = inflater.inflate(b, off, len)) == 0) {
|
||||
if (inflater.finished() || inflater.needsDictionary()) {
|
||||
finishInflating();
|
||||
return -1;
|
||||
}
|
||||
if (inflater.needsInput()) {
|
||||
fill();
|
||||
}
|
||||
}
|
||||
bytesWritten += n;
|
||||
return n;
|
||||
} catch (DataFormatException e) {
|
||||
String s = "Invalid ZLIB data format";
|
||||
if (e.getMessage() != null) {
|
||||
s = e.getMessage();
|
||||
}
|
||||
if (unzipEngine != null) {
|
||||
if (unzipEngine.getLocalFileHeader().isEncrypted() &&
|
||||
unzipEngine.getLocalFileHeader().getEncryptionMethod() == Zip4jConstants.ENC_METHOD_STANDARD) {
|
||||
s += " - Wrong Password?";
|
||||
}
|
||||
}
|
||||
throw new IOException(s);
|
||||
}
|
||||
}
|
||||
|
||||
private void finishInflating() throws IOException {
|
||||
//In some cases, compelte data is not read even though inflater is complete
|
||||
//make sure to read complete data before returning -1
|
||||
byte[] b = new byte[1024];
|
||||
while (super.read(b, 0, 1024) != -1) {
|
||||
//read all data
|
||||
}
|
||||
checkAndReadAESMacBytes();
|
||||
}
|
||||
|
||||
private void fill() throws IOException {
|
||||
int len = super.read(buff, 0, buff.length);
|
||||
if (len == -1) {
|
||||
throw new EOFException("Unexpected end of ZLIB input stream");
|
||||
}
|
||||
inflater.setInput(buff, 0, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips specified number of bytes of uncompressed data.
|
||||
* @param n the number of bytes to skip
|
||||
* @return the actual number of bytes skipped.
|
||||
* @exception IOException if an I/O error has occurred
|
||||
* @exception IllegalArgumentException if n < 0
|
||||
*/
|
||||
public long skip(long n) throws IOException {
|
||||
if (n < 0) {
|
||||
throw new IllegalArgumentException("negative skip length");
|
||||
}
|
||||
int max = (int)Math.min(n, Integer.MAX_VALUE);
|
||||
int total = 0;
|
||||
byte[] b = new byte[512];
|
||||
while (total < max) {
|
||||
int len = max - total;
|
||||
if (len > b.length) {
|
||||
len = b.length;
|
||||
}
|
||||
len = read(b, 0, len);
|
||||
if (len == -1) {
|
||||
break;
|
||||
}
|
||||
total += len;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
public void seek(long pos) throws IOException {
|
||||
super.seek(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 0 after EOF has been reached, otherwise always return 1.
|
||||
* <p>
|
||||
* Programs should not count on this method to return the actual number
|
||||
* of bytes that could be read without blocking.
|
||||
*
|
||||
* @return 1 before EOF and 0 after EOF.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*
|
||||
*/
|
||||
public int available() {
|
||||
return inflater.finished() ? 0 : 1;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
inflater.end();
|
||||
super.close();
|
||||
}
|
||||
|
||||
public UnzipEngine getUnzipEngine() {
|
||||
return super.getUnzipEngine();
|
||||
}
|
||||
}
|
172
src/net/lingala/zip4j/io/PartInputStream.java
Normal file
172
src/net/lingala/zip4j/io/PartInputStream.java
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
import net.lingala.zip4j.crypto.AESDecrypter;
|
||||
import net.lingala.zip4j.crypto.IDecrypter;
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.unzip.UnzipEngine;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
import net.lingala.zip4j.util.Zip4jConstants;
|
||||
|
||||
public class PartInputStream extends BaseInputStream
|
||||
{
|
||||
private RandomAccessFile raf;
|
||||
private long bytesRead, length;
|
||||
private UnzipEngine unzipEngine;
|
||||
private IDecrypter decrypter;
|
||||
private byte[] oneByteBuff = new byte[1];
|
||||
private byte[] aesBlockByte = new byte[16];
|
||||
private int aesBytesReturned = 0;
|
||||
private boolean isAESEncryptedFile = false;
|
||||
private int count = -1;
|
||||
|
||||
public PartInputStream(RandomAccessFile raf, long start, long len, UnzipEngine unzipEngine) {
|
||||
this.raf = raf;
|
||||
this.unzipEngine = unzipEngine;
|
||||
this.decrypter = unzipEngine.getDecrypter();
|
||||
this.bytesRead = 0;
|
||||
this.length = len;
|
||||
this.isAESEncryptedFile = unzipEngine.getFileHeader().isEncrypted() &&
|
||||
unzipEngine.getFileHeader().getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES;
|
||||
}
|
||||
|
||||
public int available() {
|
||||
long amount = length - bytesRead;
|
||||
if (amount > Integer.MAX_VALUE)
|
||||
return Integer.MAX_VALUE;
|
||||
return (int) amount;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (bytesRead >= length)
|
||||
return -1;
|
||||
|
||||
if (isAESEncryptedFile) {
|
||||
if (aesBytesReturned == 0 || aesBytesReturned == 16) {
|
||||
if (read(aesBlockByte) == -1) {
|
||||
return -1;
|
||||
}
|
||||
aesBytesReturned = 0;
|
||||
}
|
||||
return aesBlockByte[aesBytesReturned++] & 0xff;
|
||||
} else {
|
||||
return read(oneByteBuff, 0, 1) == -1 ? -1 : oneByteBuff[0] & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException {
|
||||
return this.read(b, 0, b.length);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (len > length - bytesRead) {
|
||||
len = (int) (length - bytesRead);
|
||||
if (len == 0) {
|
||||
checkAndReadAESMacBytes();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (unzipEngine.getDecrypter() instanceof AESDecrypter) {
|
||||
if (bytesRead + len < length) {
|
||||
if (len % 16 != 0) {
|
||||
len = len - (len%16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (raf) {
|
||||
count = raf.read(b, off, len);
|
||||
if ((count < len) && unzipEngine.getZipModel().isSplitArchive()) {
|
||||
raf.close();
|
||||
raf = unzipEngine.startNextSplitFile();
|
||||
if (count < 0) count = 0;
|
||||
int newlyRead = raf.read(b, count, len-count);
|
||||
if (newlyRead > 0)
|
||||
count += newlyRead;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
if (decrypter != null) {
|
||||
try {
|
||||
decrypter.decryptData(b, off, count);
|
||||
} catch (ZipException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
bytesRead += count;
|
||||
}
|
||||
|
||||
if (bytesRead >= length) {
|
||||
checkAndReadAESMacBytes();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
protected void checkAndReadAESMacBytes() throws IOException {
|
||||
if (isAESEncryptedFile) {
|
||||
if (decrypter != null && decrypter instanceof AESDecrypter) {
|
||||
if (((AESDecrypter)decrypter).getStoredMac() != null) {
|
||||
//Stored mac already set
|
||||
return;
|
||||
}
|
||||
byte[] macBytes = new byte[InternalZipConstants.AES_AUTH_LENGTH];
|
||||
int readLen = -1;
|
||||
readLen = raf.read(macBytes);
|
||||
if (readLen != InternalZipConstants.AES_AUTH_LENGTH) {
|
||||
if (unzipEngine.getZipModel().isSplitArchive()) {
|
||||
raf.close();
|
||||
raf = unzipEngine.startNextSplitFile();
|
||||
int newlyRead = raf.read(macBytes, readLen, InternalZipConstants.AES_AUTH_LENGTH - readLen);
|
||||
readLen += newlyRead;
|
||||
} else {
|
||||
throw new IOException("Error occured while reading stored AES authentication bytes");
|
||||
}
|
||||
}
|
||||
|
||||
((AESDecrypter)unzipEngine.getDecrypter()).setStoredMac(macBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long skip(long amount) throws IOException {
|
||||
if (amount < 0)
|
||||
throw new IllegalArgumentException();
|
||||
if (amount > length - bytesRead)
|
||||
amount = length - bytesRead;
|
||||
bytesRead += amount;
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
raf.close();
|
||||
}
|
||||
|
||||
public void seek(long pos) throws IOException {
|
||||
raf.seek(pos);
|
||||
}
|
||||
|
||||
public UnzipEngine getUnzipEngine() {
|
||||
return this.unzipEngine;
|
||||
}
|
||||
}
|
234
src/net/lingala/zip4j/io/SplitOutputStream.java
Normal file
234
src/net/lingala/zip4j/io/SplitOutputStream.java
Normal file
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
import net.lingala.zip4j.util.Raw;
|
||||
import net.lingala.zip4j.util.Zip4jUtil;
|
||||
|
||||
public class SplitOutputStream extends OutputStream {
|
||||
|
||||
private RandomAccessFile raf;
|
||||
private long splitLength;
|
||||
private File zipFile;
|
||||
private File outFile;
|
||||
private int currSplitFileCounter;
|
||||
private long bytesWrittenForThisPart;
|
||||
|
||||
public SplitOutputStream(String name) throws FileNotFoundException, ZipException {
|
||||
this(Zip4jUtil.isStringNotNullAndNotEmpty(name) ?
|
||||
new File(name) : null);
|
||||
}
|
||||
|
||||
public SplitOutputStream(File file) throws FileNotFoundException, ZipException {
|
||||
this(file, -1);
|
||||
}
|
||||
|
||||
public SplitOutputStream(String name, long splitLength) throws FileNotFoundException, ZipException {
|
||||
this(!Zip4jUtil.isStringNotNullAndNotEmpty(name) ?
|
||||
new File(name) : null, splitLength);
|
||||
}
|
||||
|
||||
public SplitOutputStream(File file, long splitLength) throws FileNotFoundException, ZipException {
|
||||
if (splitLength >= 0 && splitLength < InternalZipConstants.MIN_SPLIT_LENGTH) {
|
||||
throw new ZipException("split length less than minimum allowed split length of " + InternalZipConstants.MIN_SPLIT_LENGTH +" Bytes");
|
||||
}
|
||||
this.raf = new RandomAccessFile(file, InternalZipConstants.WRITE_MODE);
|
||||
this.splitLength = splitLength;
|
||||
this.outFile = file;
|
||||
this.zipFile = file;
|
||||
this.currSplitFileCounter = 0;
|
||||
this.bytesWrittenForThisPart = 0;
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
byte[] buff = new byte[1];
|
||||
buff[0] = (byte) b;
|
||||
write(buff, 0, 1);
|
||||
}
|
||||
|
||||
public void write(byte[] b) throws IOException {
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
if (len <= 0) return;
|
||||
|
||||
if (splitLength != -1) {
|
||||
|
||||
if (splitLength < InternalZipConstants.MIN_SPLIT_LENGTH) {
|
||||
throw new IOException("split length less than minimum allowed split length of " + InternalZipConstants.MIN_SPLIT_LENGTH +" Bytes");
|
||||
}
|
||||
|
||||
if (bytesWrittenForThisPart >= splitLength) {
|
||||
startNextSplitFile();
|
||||
raf.write(b, off, len);
|
||||
bytesWrittenForThisPart = len;
|
||||
} else if (bytesWrittenForThisPart + len > splitLength) {
|
||||
if (isHeaderData(b)) {
|
||||
startNextSplitFile();
|
||||
raf.write(b, off, len);
|
||||
bytesWrittenForThisPart = len;
|
||||
} else {
|
||||
raf.write(b, off, (int)(splitLength - bytesWrittenForThisPart));
|
||||
startNextSplitFile();
|
||||
raf.write(b, off + (int)(splitLength - bytesWrittenForThisPart), (int)(len - (splitLength - bytesWrittenForThisPart)));
|
||||
bytesWrittenForThisPart = len - (splitLength - bytesWrittenForThisPart);
|
||||
}
|
||||
} else {
|
||||
raf.write(b, off, len);
|
||||
bytesWrittenForThisPart += len;
|
||||
}
|
||||
|
||||
} else {
|
||||
raf.write(b, off, len);
|
||||
bytesWrittenForThisPart += len;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void startNextSplitFile() throws IOException {
|
||||
try {
|
||||
String zipFileWithoutExt = Zip4jUtil.getZipFileNameWithoutExt(outFile.getName());
|
||||
File currSplitFile = null;
|
||||
String zipFileName = zipFile.getAbsolutePath();
|
||||
String parentPath = (outFile.getParent() == null)?"":outFile.getParent() + System.getProperty("file.separator");
|
||||
|
||||
if (currSplitFileCounter < 9) {
|
||||
currSplitFile = new File(parentPath + zipFileWithoutExt + ".z0" + (currSplitFileCounter + 1));
|
||||
} else {
|
||||
currSplitFile = new File(parentPath + zipFileWithoutExt + ".z" + (currSplitFileCounter + 1));
|
||||
}
|
||||
|
||||
raf.close();
|
||||
|
||||
if (currSplitFile.exists()) {
|
||||
throw new IOException("split file: " + currSplitFile.getName() + " already exists in the current directory, cannot rename this file");
|
||||
}
|
||||
|
||||
if (!zipFile.renameTo(currSplitFile)) {
|
||||
throw new IOException("cannot rename newly created split file");
|
||||
}
|
||||
|
||||
zipFile = new File(zipFileName);
|
||||
raf = new RandomAccessFile(zipFile, InternalZipConstants.WRITE_MODE);
|
||||
currSplitFileCounter++;
|
||||
} catch (ZipException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isHeaderData(byte[] buff) {
|
||||
if (buff == null || buff.length < 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int signature = Raw.readIntLittleEndian(buff, 0);
|
||||
long[] allHeaderSignatures = Zip4jUtil.getAllHeaderSignatures();
|
||||
if (allHeaderSignatures != null && allHeaderSignatures.length > 0) {
|
||||
for (int i = 0; i < allHeaderSignatures.length; i++) {
|
||||
//Ignore split signature
|
||||
if (allHeaderSignatures[i] != InternalZipConstants.SPLITSIG &&
|
||||
allHeaderSignatures[i] == signature) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the buffer size is sufficient for the current split file. If not
|
||||
* a new split file will be started.
|
||||
* @param bufferSize
|
||||
* @return true if a new split file was started else false
|
||||
* @throws ZipException
|
||||
*/
|
||||
public boolean checkBuffSizeAndStartNextSplitFile(int bufferSize) throws ZipException {
|
||||
if (bufferSize < 0) {
|
||||
throw new ZipException("negative buffersize for checkBuffSizeAndStartNextSplitFile");
|
||||
}
|
||||
|
||||
if (!isBuffSizeFitForCurrSplitFile(bufferSize)) {
|
||||
try {
|
||||
startNextSplitFile();
|
||||
bytesWrittenForThisPart = 0;
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given buffer size will be fit in the current split file.
|
||||
* If this output stream is a non-split file, then this method always returns true
|
||||
* @param bufferSize
|
||||
* @return true if the buffer size is fit in the current split file or else false.
|
||||
* @throws ZipException
|
||||
*/
|
||||
public boolean isBuffSizeFitForCurrSplitFile(int bufferSize) throws ZipException {
|
||||
if (bufferSize < 0) {
|
||||
throw new ZipException("negative buffersize for isBuffSizeFitForCurrSplitFile");
|
||||
}
|
||||
|
||||
if (splitLength >= InternalZipConstants.MIN_SPLIT_LENGTH) {
|
||||
return (bytesWrittenForThisPart + bufferSize <= splitLength);
|
||||
} else {
|
||||
//Non split zip -- return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void seek(long pos) throws IOException {
|
||||
raf.seek(pos);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (raf != null)
|
||||
raf.close();
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
}
|
||||
|
||||
public long getFilePointer() throws IOException {
|
||||
return raf.getFilePointer();
|
||||
}
|
||||
|
||||
public boolean isSplitZipFile() {
|
||||
return splitLength!=-1;
|
||||
}
|
||||
|
||||
public long getSplitLength() {
|
||||
return splitLength;
|
||||
}
|
||||
|
||||
public int getCurrSplitFileCounter() {
|
||||
return currSplitFileCounter;
|
||||
}
|
||||
}
|
89
src/net/lingala/zip4j/io/ZipInputStream.java
Normal file
89
src/net/lingala/zip4j/io/ZipInputStream.java
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
|
||||
public class ZipInputStream extends InputStream {
|
||||
|
||||
private BaseInputStream is;
|
||||
|
||||
public ZipInputStream(BaseInputStream is) {
|
||||
this.is = is;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
int readByte = is.read();
|
||||
if (readByte != -1) {
|
||||
is.getUnzipEngine().updateCRC(readByte);
|
||||
}
|
||||
return readByte;
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
int readLen = is.read(b, off, len);
|
||||
if (readLen > 0 && is.getUnzipEngine() != null) {
|
||||
is.getUnzipEngine().updateCRC(b, off, readLen);
|
||||
}
|
||||
return readLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the input stream and releases any resources.
|
||||
* This method also checks for the CRC of the extracted file.
|
||||
* If CRC check has to be skipped use close(boolean skipCRCCheck) method
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
close(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the input stream and releases any resources.
|
||||
* If skipCRCCheck flag is set to true, this method skips CRC Check
|
||||
* of the extracted file
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void close(boolean skipCRCCheck) throws IOException {
|
||||
try {
|
||||
is.close();
|
||||
if (!skipCRCCheck && is.getUnzipEngine() != null) {
|
||||
is.getUnzipEngine().checkCRC();
|
||||
}
|
||||
} catch (ZipException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public int available() throws IOException {
|
||||
return is.available();
|
||||
}
|
||||
|
||||
public long skip(long n) throws IOException {
|
||||
return is.skip(n);
|
||||
}
|
||||
|
||||
}
|
33
src/net/lingala/zip4j/io/ZipOutputStream.java
Normal file
33
src/net/lingala/zip4j/io/ZipOutputStream.java
Normal file
|
@ -0,0 +1,33 @@
|
|||
package net.lingala.zip4j.io;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.lingala.zip4j.model.ZipModel;
|
||||
|
||||
public class ZipOutputStream extends DeflaterOutputStream {
|
||||
|
||||
public ZipOutputStream(OutputStream outputStream) {
|
||||
this(outputStream, null);
|
||||
}
|
||||
|
||||
public ZipOutputStream(OutputStream outputStream, ZipModel zipModel) {
|
||||
super(outputStream, zipModel);
|
||||
}
|
||||
|
||||
public void write(int bval) throws IOException {
|
||||
byte[] b = new byte[1];
|
||||
b[0] = (byte) bval;
|
||||
write(b, 0, 1);
|
||||
}
|
||||
|
||||
public void write(byte[] b) throws IOException {
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
crc.update(b, off, len);
|
||||
updateTotalBytesRead(len);
|
||||
super.write(b, off, len);
|
||||
}
|
||||
}
|
97
src/net/lingala/zip4j/model/AESExtraDataRecord.java
Normal file
97
src/net/lingala/zip4j/model/AESExtraDataRecord.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
public class AESExtraDataRecord {
|
||||
|
||||
private long signature;
|
||||
private int dataSize;
|
||||
private int versionNumber;
|
||||
private String vendorID;
|
||||
private int aesStrength;
|
||||
private int compressionMethod;
|
||||
|
||||
public AESExtraDataRecord() {
|
||||
signature = -1;
|
||||
dataSize = -1;
|
||||
versionNumber = -1;
|
||||
vendorID = null;
|
||||
aesStrength = -1;
|
||||
compressionMethod = -1;
|
||||
}
|
||||
|
||||
|
||||
public long getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
|
||||
public void setSignature(long signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
|
||||
public int getDataSize() {
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
|
||||
public void setDataSize(int dataSize) {
|
||||
this.dataSize = dataSize;
|
||||
}
|
||||
|
||||
|
||||
public int getVersionNumber() {
|
||||
return versionNumber;
|
||||
}
|
||||
|
||||
|
||||
public void setVersionNumber(int versionNumber) {
|
||||
this.versionNumber = versionNumber;
|
||||
}
|
||||
|
||||
|
||||
public String getVendorID() {
|
||||
return vendorID;
|
||||
}
|
||||
|
||||
|
||||
public void setVendorID(String vendorID) {
|
||||
this.vendorID = vendorID;
|
||||
}
|
||||
|
||||
|
||||
public int getAesStrength() {
|
||||
return aesStrength;
|
||||
}
|
||||
|
||||
|
||||
public void setAesStrength(int aesStrength) {
|
||||
this.aesStrength = aesStrength;
|
||||
}
|
||||
|
||||
|
||||
public int getCompressionMethod() {
|
||||
return compressionMethod;
|
||||
}
|
||||
|
||||
|
||||
public void setCompressionMethod(int compressionMethod) {
|
||||
this.compressionMethod = compressionMethod;
|
||||
}
|
||||
|
||||
}
|
51
src/net/lingala/zip4j/model/ArchiveExtraDataRecord.java
Normal file
51
src/net/lingala/zip4j/model/ArchiveExtraDataRecord.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
public class ArchiveExtraDataRecord {
|
||||
|
||||
private int signature;
|
||||
|
||||
private int extraFieldLength;
|
||||
|
||||
private String extraFieldData;
|
||||
|
||||
public int getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void setSignature(int signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public int getExtraFieldLength() {
|
||||
return extraFieldLength;
|
||||
}
|
||||
|
||||
public void setExtraFieldLength(int extraFieldLength) {
|
||||
this.extraFieldLength = extraFieldLength;
|
||||
}
|
||||
|
||||
public String getExtraFieldData() {
|
||||
return extraFieldData;
|
||||
}
|
||||
|
||||
public void setExtraFieldData(String extraFieldData) {
|
||||
this.extraFieldData = extraFieldData;
|
||||
}
|
||||
|
||||
}
|
44
src/net/lingala/zip4j/model/CentralDirectory.java
Normal file
44
src/net/lingala/zip4j/model/CentralDirectory.java
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class CentralDirectory {
|
||||
|
||||
private ArrayList fileHeaders;
|
||||
|
||||
private DigitalSignature digitalSignature;
|
||||
|
||||
public ArrayList getFileHeaders() {
|
||||
return fileHeaders;
|
||||
}
|
||||
|
||||
public void setFileHeaders(ArrayList fileHeaders) {
|
||||
this.fileHeaders = fileHeaders;
|
||||
}
|
||||
|
||||
public DigitalSignature getDigitalSignature() {
|
||||
return digitalSignature;
|
||||
}
|
||||
|
||||
public void setDigitalSignature(DigitalSignature digitalSignature) {
|
||||
this.digitalSignature = digitalSignature;
|
||||
}
|
||||
|
||||
|
||||
}
|
51
src/net/lingala/zip4j/model/DataDescriptor.java
Normal file
51
src/net/lingala/zip4j/model/DataDescriptor.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
public class DataDescriptor {
|
||||
|
||||
private String crc32;
|
||||
|
||||
private int compressedSize;
|
||||
|
||||
private int uncompressedSize;
|
||||
|
||||
public String getCrc32() {
|
||||
return crc32;
|
||||
}
|
||||
|
||||
public void setCrc32(String crc32) {
|
||||
this.crc32 = crc32;
|
||||
}
|
||||
|
||||
public int getCompressedSize() {
|
||||
return compressedSize;
|
||||
}
|
||||
|
||||
public void setCompressedSize(int compressedSize) {
|
||||
this.compressedSize = compressedSize;
|
||||
}
|
||||
|
||||
public int getUncompressedSize() {
|
||||
return uncompressedSize;
|
||||
}
|
||||
|
||||
public void setUncompressedSize(int uncompressedSize) {
|
||||
this.uncompressedSize = uncompressedSize;
|
||||
}
|
||||
|
||||
}
|
51
src/net/lingala/zip4j/model/DigitalSignature.java
Normal file
51
src/net/lingala/zip4j/model/DigitalSignature.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
public class DigitalSignature {
|
||||
|
||||
private int headerSignature;
|
||||
|
||||
private int sizeOfData;
|
||||
|
||||
private String signatureData;
|
||||
|
||||
public int getHeaderSignature() {
|
||||
return headerSignature;
|
||||
}
|
||||
|
||||
public void setHeaderSignature(int headerSignature) {
|
||||
this.headerSignature = headerSignature;
|
||||
}
|
||||
|
||||
public int getSizeOfData() {
|
||||
return sizeOfData;
|
||||
}
|
||||
|
||||
public void setSizeOfData(int sizeOfData) {
|
||||
this.sizeOfData = sizeOfData;
|
||||
}
|
||||
|
||||
public String getSignatureData() {
|
||||
return signatureData;
|
||||
}
|
||||
|
||||
public void setSignatureData(String signatureData) {
|
||||
this.signatureData = signatureData;
|
||||
}
|
||||
|
||||
}
|
122
src/net/lingala/zip4j/model/EndCentralDirRecord.java
Normal file
122
src/net/lingala/zip4j/model/EndCentralDirRecord.java
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
public class EndCentralDirRecord {
|
||||
|
||||
private long signature;
|
||||
|
||||
private int noOfThisDisk;
|
||||
|
||||
private int noOfThisDiskStartOfCentralDir;
|
||||
|
||||
private int totNoOfEntriesInCentralDirOnThisDisk;
|
||||
|
||||
private int totNoOfEntriesInCentralDir;
|
||||
|
||||
private int sizeOfCentralDir;
|
||||
|
||||
private long offsetOfStartOfCentralDir;
|
||||
|
||||
private int commentLength;
|
||||
|
||||
private String comment;
|
||||
|
||||
private byte[] commentBytes;
|
||||
|
||||
public long getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void setSignature(long signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public int getNoOfThisDisk() {
|
||||
return noOfThisDisk;
|
||||
}
|
||||
|
||||
public void setNoOfThisDisk(int noOfThisDisk) {
|
||||
this.noOfThisDisk = noOfThisDisk;
|
||||
}
|
||||
|
||||
public int getNoOfThisDiskStartOfCentralDir() {
|
||||
return noOfThisDiskStartOfCentralDir;
|
||||
}
|
||||
|
||||
public void setNoOfThisDiskStartOfCentralDir(int noOfThisDiskStartOfCentralDir) {
|
||||
this.noOfThisDiskStartOfCentralDir = noOfThisDiskStartOfCentralDir;
|
||||
}
|
||||
|
||||
public int getTotNoOfEntriesInCentralDirOnThisDisk() {
|
||||
return totNoOfEntriesInCentralDirOnThisDisk;
|
||||
}
|
||||
|
||||
public void setTotNoOfEntriesInCentralDirOnThisDisk(
|
||||
int totNoOfEntriesInCentralDirOnThisDisk) {
|
||||
this.totNoOfEntriesInCentralDirOnThisDisk = totNoOfEntriesInCentralDirOnThisDisk;
|
||||
}
|
||||
|
||||
public int getTotNoOfEntriesInCentralDir() {
|
||||
return totNoOfEntriesInCentralDir;
|
||||
}
|
||||
|
||||
public void setTotNoOfEntriesInCentralDir(int totNoOfEntrisInCentralDir) {
|
||||
this.totNoOfEntriesInCentralDir = totNoOfEntrisInCentralDir;
|
||||
}
|
||||
|
||||
public int getSizeOfCentralDir() {
|
||||
return sizeOfCentralDir;
|
||||
}
|
||||
|
||||
public void setSizeOfCentralDir(int sizeOfCentralDir) {
|
||||
this.sizeOfCentralDir = sizeOfCentralDir;
|
||||
}
|
||||
|
||||
public long getOffsetOfStartOfCentralDir() {
|
||||
return offsetOfStartOfCentralDir;
|
||||
}
|
||||
|
||||
public void setOffsetOfStartOfCentralDir(long offSetOfStartOfCentralDir) {
|
||||
this.offsetOfStartOfCentralDir = offSetOfStartOfCentralDir;
|
||||
}
|
||||
|
||||
public int getCommentLength() {
|
||||
return commentLength;
|
||||
}
|
||||
|
||||
public void setCommentLength(int commentLength) {
|
||||
this.commentLength = commentLength;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public byte[] getCommentBytes() {
|
||||
return commentBytes;
|
||||
}
|
||||
|
||||
public void setCommentBytes(byte[] commentBytes) {
|
||||
this.commentBytes = commentBytes;
|
||||
}
|
||||
|
||||
}
|
51
src/net/lingala/zip4j/model/ExtraDataRecord.java
Normal file
51
src/net/lingala/zip4j/model/ExtraDataRecord.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
public class ExtraDataRecord {
|
||||
|
||||
private long header;
|
||||
|
||||
private int sizeOfData;
|
||||
|
||||
private byte[] data;
|
||||
|
||||
public long getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public void setHeader(long header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
public int getSizeOfData() {
|
||||
return sizeOfData;
|
||||
}
|
||||
|
||||
public void setSizeOfData(int sizeOfData) {
|
||||
this.sizeOfData = sizeOfData;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
}
|
369
src/net/lingala/zip4j/model/FileHeader.java
Normal file
369
src/net/lingala/zip4j/model/FileHeader.java
Normal file
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.progress.ProgressMonitor;
|
||||
import net.lingala.zip4j.unzip.Unzip;
|
||||
import net.lingala.zip4j.util.Zip4jUtil;
|
||||
|
||||
public class FileHeader {
|
||||
|
||||
private int signature;
|
||||
|
||||
private int versionMadeBy;
|
||||
|
||||
private int versionNeededToExtract;
|
||||
|
||||
private byte[] generalPurposeFlag;
|
||||
|
||||
private int compressionMethod;
|
||||
|
||||
private int lastModFileTime;
|
||||
|
||||
private long crc32;
|
||||
|
||||
private byte[] crcBuff;
|
||||
|
||||
private long compressedSize;
|
||||
|
||||
private long uncompressedSize;
|
||||
|
||||
private int fileNameLength;
|
||||
|
||||
private int extraFieldLength;
|
||||
|
||||
private int fileCommentLength;
|
||||
|
||||
private int diskNumberStart;
|
||||
|
||||
private byte[] internalFileAttr;
|
||||
|
||||
private byte[] externalFileAttr;
|
||||
|
||||
private long offsetLocalHeader;
|
||||
|
||||
private String fileName;
|
||||
|
||||
private String fileComment;
|
||||
|
||||
private boolean isDirectory;
|
||||
|
||||
private boolean isEncrypted;
|
||||
|
||||
private int encryptionMethod;
|
||||
|
||||
private char[] password;
|
||||
|
||||
private boolean dataDescriptorExists;
|
||||
|
||||
private Zip64ExtendedInfo zip64ExtendedInfo;
|
||||
|
||||
private AESExtraDataRecord aesExtraDataRecord;
|
||||
|
||||
private ArrayList extraDataRecords;
|
||||
|
||||
private boolean fileNameUTF8Encoded;
|
||||
|
||||
public FileHeader() {
|
||||
encryptionMethod = -1;
|
||||
crc32 = 0;
|
||||
uncompressedSize = 0;
|
||||
}
|
||||
|
||||
public int getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void setSignature(int signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public int getVersionMadeBy() {
|
||||
return versionMadeBy;
|
||||
}
|
||||
|
||||
public void setVersionMadeBy(int versionMadeBy) {
|
||||
this.versionMadeBy = versionMadeBy;
|
||||
}
|
||||
|
||||
public int getVersionNeededToExtract() {
|
||||
return versionNeededToExtract;
|
||||
}
|
||||
|
||||
public void setVersionNeededToExtract(int versionNeededToExtract) {
|
||||
this.versionNeededToExtract = versionNeededToExtract;
|
||||
}
|
||||
|
||||
public byte[] getGeneralPurposeFlag() {
|
||||
return generalPurposeFlag;
|
||||
}
|
||||
|
||||
public void setGeneralPurposeFlag(byte[] generalPurposeFlag) {
|
||||
this.generalPurposeFlag = generalPurposeFlag;
|
||||
}
|
||||
|
||||
public int getCompressionMethod() {
|
||||
return compressionMethod;
|
||||
}
|
||||
|
||||
public void setCompressionMethod(int compressionMethod) {
|
||||
this.compressionMethod = compressionMethod;
|
||||
}
|
||||
|
||||
public int getLastModFileTime() {
|
||||
return lastModFileTime;
|
||||
}
|
||||
|
||||
public void setLastModFileTime(int lastModFileTime) {
|
||||
this.lastModFileTime = lastModFileTime;
|
||||
}
|
||||
|
||||
public long getCrc32() {
|
||||
return crc32 & 0xffffffffL;
|
||||
}
|
||||
|
||||
public void setCrc32(long crc32) {
|
||||
this.crc32 = crc32;
|
||||
}
|
||||
|
||||
public long getCompressedSize() {
|
||||
return compressedSize;
|
||||
}
|
||||
|
||||
public void setCompressedSize(long compressedSize) {
|
||||
this.compressedSize = compressedSize;
|
||||
}
|
||||
|
||||
public long getUncompressedSize() {
|
||||
return uncompressedSize;
|
||||
}
|
||||
|
||||
public void setUncompressedSize(long uncompressedSize) {
|
||||
this.uncompressedSize = uncompressedSize;
|
||||
}
|
||||
|
||||
public int getFileNameLength() {
|
||||
return fileNameLength;
|
||||
}
|
||||
|
||||
public void setFileNameLength(int fileNameLength) {
|
||||
this.fileNameLength = fileNameLength;
|
||||
}
|
||||
|
||||
public int getExtraFieldLength() {
|
||||
return extraFieldLength;
|
||||
}
|
||||
|
||||
public void setExtraFieldLength(int extraFieldLength) {
|
||||
this.extraFieldLength = extraFieldLength;
|
||||
}
|
||||
|
||||
public int getFileCommentLength() {
|
||||
return fileCommentLength;
|
||||
}
|
||||
|
||||
public void setFileCommentLength(int fileCommentLength) {
|
||||
this.fileCommentLength = fileCommentLength;
|
||||
}
|
||||
|
||||
public int getDiskNumberStart() {
|
||||
return diskNumberStart;
|
||||
}
|
||||
|
||||
public void setDiskNumberStart(int diskNumberStart) {
|
||||
this.diskNumberStart = diskNumberStart;
|
||||
}
|
||||
|
||||
public byte[] getInternalFileAttr() {
|
||||
return internalFileAttr;
|
||||
}
|
||||
|
||||
public void setInternalFileAttr(byte[] internalFileAttr) {
|
||||
this.internalFileAttr = internalFileAttr;
|
||||
}
|
||||
|
||||
public byte[] getExternalFileAttr() {
|
||||
return externalFileAttr;
|
||||
}
|
||||
|
||||
public void setExternalFileAttr(byte[] externalFileAttr) {
|
||||
this.externalFileAttr = externalFileAttr;
|
||||
}
|
||||
|
||||
public long getOffsetLocalHeader() {
|
||||
return offsetLocalHeader;
|
||||
}
|
||||
|
||||
public void setOffsetLocalHeader(long offsetLocalHeader) {
|
||||
this.offsetLocalHeader = offsetLocalHeader;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public String getFileComment() {
|
||||
return fileComment;
|
||||
}
|
||||
|
||||
public void setFileComment(String fileComment) {
|
||||
this.fileComment = fileComment;
|
||||
}
|
||||
|
||||
public boolean isDirectory() {
|
||||
return isDirectory;
|
||||
}
|
||||
|
||||
public void setDirectory(boolean isDirectory) {
|
||||
this.isDirectory = isDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts file to the specified directory
|
||||
* @param zipModel
|
||||
* @param outPath
|
||||
* @throws ZipException
|
||||
*/
|
||||
public void extractFile(ZipModel zipModel, String outPath,
|
||||
ProgressMonitor progressMonitor, boolean runInThread) throws ZipException {
|
||||
extractFile(zipModel, outPath, null, progressMonitor, runInThread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts file to the specified directory using any
|
||||
* user defined parameters in UnzipParameters
|
||||
* @param zipModel
|
||||
* @param outPath
|
||||
* @param unzipParameters
|
||||
* @throws ZipException
|
||||
*/
|
||||
public void extractFile(ZipModel zipModel, String outPath,
|
||||
UnzipParameters unzipParameters, ProgressMonitor progressMonitor, boolean runInThread) throws ZipException {
|
||||
extractFile(zipModel, outPath, unzipParameters, null, progressMonitor, runInThread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts file to the specified directory using any
|
||||
* user defined parameters in UnzipParameters. Output file name
|
||||
* will be overwritten with the value in newFileName. If this
|
||||
* parameter is null, then file name will be the same as in
|
||||
* FileHeader.getFileName
|
||||
* @param zipModel
|
||||
* @param outPath
|
||||
* @param unzipParameters
|
||||
* @throws ZipException
|
||||
*/
|
||||
public void extractFile(ZipModel zipModel, String outPath,
|
||||
UnzipParameters unzipParameters, String newFileName,
|
||||
ProgressMonitor progressMonitor, boolean runInThread) throws ZipException {
|
||||
if (zipModel == null) {
|
||||
throw new ZipException("input zipModel is null");
|
||||
}
|
||||
|
||||
if (!Zip4jUtil.checkOutputFolder(outPath)) {
|
||||
throw new ZipException("Invalid output path");
|
||||
}
|
||||
|
||||
if (this == null) {
|
||||
throw new ZipException("invalid file header");
|
||||
}
|
||||
Unzip unzip = new Unzip(zipModel);
|
||||
unzip.extractFile(this, outPath, unzipParameters, newFileName, progressMonitor, runInThread);
|
||||
}
|
||||
|
||||
public boolean isEncrypted() {
|
||||
return isEncrypted;
|
||||
}
|
||||
|
||||
public void setEncrypted(boolean isEncrypted) {
|
||||
this.isEncrypted = isEncrypted;
|
||||
}
|
||||
|
||||
public int getEncryptionMethod() {
|
||||
return encryptionMethod;
|
||||
}
|
||||
|
||||
public void setEncryptionMethod(int encryptionMethod) {
|
||||
this.encryptionMethod = encryptionMethod;
|
||||
}
|
||||
|
||||
public char[] getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(char[] password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public byte[] getCrcBuff() {
|
||||
return crcBuff;
|
||||
}
|
||||
|
||||
public void setCrcBuff(byte[] crcBuff) {
|
||||
this.crcBuff = crcBuff;
|
||||
}
|
||||
|
||||
public ArrayList getExtraDataRecords() {
|
||||
return extraDataRecords;
|
||||
}
|
||||
|
||||
public void setExtraDataRecords(ArrayList extraDataRecords) {
|
||||
this.extraDataRecords = extraDataRecords;
|
||||
}
|
||||
|
||||
public boolean isDataDescriptorExists() {
|
||||
return dataDescriptorExists;
|
||||
}
|
||||
|
||||
public void setDataDescriptorExists(boolean dataDescriptorExists) {
|
||||
this.dataDescriptorExists = dataDescriptorExists;
|
||||
}
|
||||
|
||||
public Zip64ExtendedInfo getZip64ExtendedInfo() {
|
||||
return zip64ExtendedInfo;
|
||||
}
|
||||
|
||||
public void setZip64ExtendedInfo(Zip64ExtendedInfo zip64ExtendedInfo) {
|
||||
this.zip64ExtendedInfo = zip64ExtendedInfo;
|
||||
}
|
||||
|
||||
public AESExtraDataRecord getAesExtraDataRecord() {
|
||||
return aesExtraDataRecord;
|
||||
}
|
||||
|
||||
public void setAesExtraDataRecord(AESExtraDataRecord aesExtraDataRecord) {
|
||||
this.aesExtraDataRecord = aesExtraDataRecord;
|
||||
}
|
||||
|
||||
public boolean isFileNameUTF8Encoded() {
|
||||
return fileNameUTF8Encoded;
|
||||
}
|
||||
|
||||
public void setFileNameUTF8Encoded(boolean fileNameUTF8Encoded) {
|
||||
this.fileNameUTF8Encoded = fileNameUTF8Encoded;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
261
src/net/lingala/zip4j/model/LocalFileHeader.java
Normal file
261
src/net/lingala/zip4j/model/LocalFileHeader.java
Normal file
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class LocalFileHeader {
|
||||
|
||||
private int signature;
|
||||
|
||||
private int versionNeededToExtract;
|
||||
|
||||
private byte[] generalPurposeFlag;
|
||||
|
||||
private int compressionMethod;
|
||||
|
||||
private int lastModFileTime;
|
||||
|
||||
private long crc32;
|
||||
|
||||
private byte[] crcBuff;
|
||||
|
||||
private long compressedSize;
|
||||
|
||||
private long uncompressedSize;
|
||||
|
||||
private int fileNameLength;
|
||||
|
||||
private int extraFieldLength;
|
||||
|
||||
private String fileName;
|
||||
|
||||
private byte[] extraField;
|
||||
|
||||
private long offsetStartOfData;
|
||||
|
||||
private boolean isEncrypted;
|
||||
|
||||
private int encryptionMethod;
|
||||
|
||||
private char[] password;
|
||||
|
||||
private ArrayList extraDataRecords;
|
||||
|
||||
private Zip64ExtendedInfo zip64ExtendedInfo;
|
||||
|
||||
private AESExtraDataRecord aesExtraDataRecord;
|
||||
|
||||
private boolean dataDescriptorExists;
|
||||
|
||||
private boolean writeComprSizeInZip64ExtraRecord;
|
||||
|
||||
private boolean fileNameUTF8Encoded;
|
||||
|
||||
public LocalFileHeader() {
|
||||
encryptionMethod = -1;
|
||||
writeComprSizeInZip64ExtraRecord = false;
|
||||
crc32 = 0;
|
||||
uncompressedSize = 0;
|
||||
}
|
||||
|
||||
public int getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void setSignature(int signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public int getVersionNeededToExtract() {
|
||||
return versionNeededToExtract;
|
||||
}
|
||||
|
||||
public void setVersionNeededToExtract(int versionNeededToExtract) {
|
||||
this.versionNeededToExtract = versionNeededToExtract;
|
||||
}
|
||||
|
||||
public byte[] getGeneralPurposeFlag() {
|
||||
return generalPurposeFlag;
|
||||
}
|
||||
|
||||
public void setGeneralPurposeFlag(byte[] generalPurposeFlag) {
|
||||
this.generalPurposeFlag = generalPurposeFlag;
|
||||
}
|
||||
|
||||
public int getCompressionMethod() {
|
||||
return compressionMethod;
|
||||
}
|
||||
|
||||
public void setCompressionMethod(int compressionMethod) {
|
||||
this.compressionMethod = compressionMethod;
|
||||
}
|
||||
|
||||
public int getLastModFileTime() {
|
||||
return lastModFileTime;
|
||||
}
|
||||
|
||||
public void setLastModFileTime(int lastModFileTime) {
|
||||
this.lastModFileTime = lastModFileTime;
|
||||
}
|
||||
|
||||
public long getCrc32() {
|
||||
return crc32;
|
||||
}
|
||||
|
||||
public void setCrc32(long crc32) {
|
||||
this.crc32 = crc32;
|
||||
}
|
||||
|
||||
public long getCompressedSize() {
|
||||
return compressedSize;
|
||||
}
|
||||
|
||||
public void setCompressedSize(long compressedSize) {
|
||||
this.compressedSize = compressedSize;
|
||||
}
|
||||
|
||||
public long getUncompressedSize() {
|
||||
return uncompressedSize;
|
||||
}
|
||||
|
||||
public void setUncompressedSize(long uncompressedSize) {
|
||||
this.uncompressedSize = uncompressedSize;
|
||||
}
|
||||
|
||||
public int getFileNameLength() {
|
||||
return fileNameLength;
|
||||
}
|
||||
|
||||
public void setFileNameLength(int fileNameLength) {
|
||||
this.fileNameLength = fileNameLength;
|
||||
}
|
||||
|
||||
public int getExtraFieldLength() {
|
||||
return extraFieldLength;
|
||||
}
|
||||
|
||||
public void setExtraFieldLength(int extraFieldLength) {
|
||||
this.extraFieldLength = extraFieldLength;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public byte[] getExtraField() {
|
||||
return extraField;
|
||||
}
|
||||
|
||||
public void setExtraField(byte[] extraField) {
|
||||
this.extraField = extraField;
|
||||
}
|
||||
|
||||
public long getOffsetStartOfData() {
|
||||
return offsetStartOfData;
|
||||
}
|
||||
|
||||
public void setOffsetStartOfData(long offsetStartOfData) {
|
||||
this.offsetStartOfData = offsetStartOfData;
|
||||
}
|
||||
|
||||
public boolean isEncrypted() {
|
||||
return isEncrypted;
|
||||
}
|
||||
|
||||
public void setEncrypted(boolean isEncrypted) {
|
||||
this.isEncrypted = isEncrypted;
|
||||
}
|
||||
|
||||
public int getEncryptionMethod() {
|
||||
return encryptionMethod;
|
||||
}
|
||||
|
||||
public void setEncryptionMethod(int encryptionMethod) {
|
||||
this.encryptionMethod = encryptionMethod;
|
||||
}
|
||||
|
||||
public byte[] getCrcBuff() {
|
||||
return crcBuff;
|
||||
}
|
||||
|
||||
public void setCrcBuff(byte[] crcBuff) {
|
||||
this.crcBuff = crcBuff;
|
||||
}
|
||||
|
||||
public char[] getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(char[] password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public ArrayList getExtraDataRecords() {
|
||||
return extraDataRecords;
|
||||
}
|
||||
|
||||
public void setExtraDataRecords(ArrayList extraDataRecords) {
|
||||
this.extraDataRecords = extraDataRecords;
|
||||
}
|
||||
|
||||
public boolean isDataDescriptorExists() {
|
||||
return dataDescriptorExists;
|
||||
}
|
||||
|
||||
public void setDataDescriptorExists(boolean dataDescriptorExists) {
|
||||
this.dataDescriptorExists = dataDescriptorExists;
|
||||
}
|
||||
|
||||
public Zip64ExtendedInfo getZip64ExtendedInfo() {
|
||||
return zip64ExtendedInfo;
|
||||
}
|
||||
|
||||
public void setZip64ExtendedInfo(Zip64ExtendedInfo zip64ExtendedInfo) {
|
||||
this.zip64ExtendedInfo = zip64ExtendedInfo;
|
||||
}
|
||||
|
||||
public AESExtraDataRecord getAesExtraDataRecord() {
|
||||
return aesExtraDataRecord;
|
||||
}
|
||||
|
||||
public void setAesExtraDataRecord(AESExtraDataRecord aesExtraDataRecord) {
|
||||
this.aesExtraDataRecord = aesExtraDataRecord;
|
||||
}
|
||||
|
||||
public boolean isWriteComprSizeInZip64ExtraRecord() {
|
||||
return writeComprSizeInZip64ExtraRecord;
|
||||
}
|
||||
|
||||
public void setWriteComprSizeInZip64ExtraRecord(
|
||||
boolean writeComprSizeInZip64ExtraRecord) {
|
||||
this.writeComprSizeInZip64ExtraRecord = writeComprSizeInZip64ExtraRecord;
|
||||
}
|
||||
|
||||
public boolean isFileNameUTF8Encoded() {
|
||||
return fileNameUTF8Encoded;
|
||||
}
|
||||
|
||||
public void setFileNameUTF8Encoded(boolean fileNameUTF8Encoded) {
|
||||
this.fileNameUTF8Encoded = fileNameUTF8Encoded;
|
||||
}
|
||||
|
||||
}
|
88
src/net/lingala/zip4j/model/UnzipEngineParameters.java
Normal file
88
src/net/lingala/zip4j/model/UnzipEngineParameters.java
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import net.lingala.zip4j.crypto.IDecrypter;
|
||||
import net.lingala.zip4j.unzip.UnzipEngine;
|
||||
|
||||
public class UnzipEngineParameters {
|
||||
|
||||
private ZipModel zipModel;
|
||||
|
||||
private FileHeader fileHeader;
|
||||
|
||||
private LocalFileHeader localFileHeader;
|
||||
|
||||
private IDecrypter iDecryptor;
|
||||
|
||||
private FileOutputStream outputStream;
|
||||
|
||||
private UnzipEngine unzipEngine;
|
||||
|
||||
public ZipModel getZipModel() {
|
||||
return zipModel;
|
||||
}
|
||||
|
||||
public void setZipModel(ZipModel zipModel) {
|
||||
this.zipModel = zipModel;
|
||||
}
|
||||
|
||||
public FileHeader getFileHeader() {
|
||||
return fileHeader;
|
||||
}
|
||||
|
||||
public void setFileHeader(FileHeader fileHeader) {
|
||||
this.fileHeader = fileHeader;
|
||||
}
|
||||
|
||||
public LocalFileHeader getLocalFileHeader() {
|
||||
return localFileHeader;
|
||||
}
|
||||
|
||||
public void setLocalFileHeader(LocalFileHeader localFileHeader) {
|
||||
this.localFileHeader = localFileHeader;
|
||||
}
|
||||
|
||||
public IDecrypter getIDecryptor() {
|
||||
return iDecryptor;
|
||||
}
|
||||
|
||||
public void setIDecryptor(IDecrypter decrypter) {
|
||||
iDecryptor = decrypter;
|
||||
}
|
||||
|
||||
public FileOutputStream getOutputStream() {
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
public void setOutputStream(FileOutputStream outputStream) {
|
||||
this.outputStream = outputStream;
|
||||
}
|
||||
|
||||
public UnzipEngine getUnzipEngine() {
|
||||
return unzipEngine;
|
||||
}
|
||||
|
||||
public void setUnzipEngine(UnzipEngine unzipEngine) {
|
||||
this.unzipEngine = unzipEngine;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
77
src/net/lingala/zip4j/model/UnzipParameters.java
Normal file
77
src/net/lingala/zip4j/model/UnzipParameters.java
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
public class UnzipParameters {
|
||||
|
||||
private boolean ignoreReadOnlyFileAttribute;
|
||||
private boolean ignoreHiddenFileAttribute;
|
||||
private boolean ignoreArchiveFileAttribute;
|
||||
private boolean ignoreSystemFileAttribute;
|
||||
private boolean ignoreAllFileAttributes;
|
||||
private boolean ignoreDateTimeAttributes;
|
||||
|
||||
public boolean isIgnoreReadOnlyFileAttribute() {
|
||||
return ignoreReadOnlyFileAttribute;
|
||||
}
|
||||
|
||||
public void setIgnoreReadOnlyFileAttribute(boolean ignoreReadOnlyFileAttribute) {
|
||||
this.ignoreReadOnlyFileAttribute = ignoreReadOnlyFileAttribute;
|
||||
}
|
||||
|
||||
public boolean isIgnoreHiddenFileAttribute() {
|
||||
return ignoreHiddenFileAttribute;
|
||||
}
|
||||
|
||||
public void setIgnoreHiddenFileAttribute(boolean ignoreHiddenFileAttribute) {
|
||||
this.ignoreHiddenFileAttribute = ignoreHiddenFileAttribute;
|
||||
}
|
||||
|
||||
public boolean isIgnoreArchiveFileAttribute() {
|
||||
return ignoreArchiveFileAttribute;
|
||||
}
|
||||
|
||||
public void setIgnoreArchiveFileAttribute(boolean ignoreArchiveFileAttribute) {
|
||||
this.ignoreArchiveFileAttribute = ignoreArchiveFileAttribute;
|
||||
}
|
||||
|
||||
public boolean isIgnoreSystemFileAttribute() {
|
||||
return ignoreSystemFileAttribute;
|
||||
}
|
||||
|
||||
public void setIgnoreSystemFileAttribute(boolean ignoreSystemFileAttribute) {
|
||||
this.ignoreSystemFileAttribute = ignoreSystemFileAttribute;
|
||||
}
|
||||
|
||||
public boolean isIgnoreAllFileAttributes() {
|
||||
return ignoreAllFileAttributes;
|
||||
}
|
||||
|
||||
public void setIgnoreAllFileAttributes(boolean ignoreAllFileAttributes) {
|
||||
this.ignoreAllFileAttributes = ignoreAllFileAttributes;
|
||||
}
|
||||
|
||||
public boolean isIgnoreDateTimeAttributes() {
|
||||
return ignoreDateTimeAttributes;
|
||||
}
|
||||
|
||||
public void setIgnoreDateTimeAttributes(boolean ignoreDateTimeAttributes) {
|
||||
this.ignoreDateTimeAttributes = ignoreDateTimeAttributes;
|
||||
}
|
||||
|
||||
|
||||
}
|
64
src/net/lingala/zip4j/model/Zip64EndCentralDirLocator.java
Normal file
64
src/net/lingala/zip4j/model/Zip64EndCentralDirLocator.java
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
public class Zip64EndCentralDirLocator {
|
||||
|
||||
private long signature;
|
||||
|
||||
private int noOfDiskStartOfZip64EndOfCentralDirRec;
|
||||
|
||||
private long offsetZip64EndOfCentralDirRec;
|
||||
|
||||
private int totNumberOfDiscs;
|
||||
|
||||
public long getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void setSignature(long signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public int getNoOfDiskStartOfZip64EndOfCentralDirRec() {
|
||||
return noOfDiskStartOfZip64EndOfCentralDirRec;
|
||||
}
|
||||
|
||||
public void setNoOfDiskStartOfZip64EndOfCentralDirRec(
|
||||
int noOfDiskStartOfZip64EndOfCentralDirRec) {
|
||||
this.noOfDiskStartOfZip64EndOfCentralDirRec = noOfDiskStartOfZip64EndOfCentralDirRec;
|
||||
}
|
||||
|
||||
public long getOffsetZip64EndOfCentralDirRec() {
|
||||
return offsetZip64EndOfCentralDirRec;
|
||||
}
|
||||
|
||||
public void setOffsetZip64EndOfCentralDirRec(long offsetZip64EndOfCentralDirRec) {
|
||||
this.offsetZip64EndOfCentralDirRec = offsetZip64EndOfCentralDirRec;
|
||||
}
|
||||
|
||||
public int getTotNumberOfDiscs() {
|
||||
return totNumberOfDiscs;
|
||||
}
|
||||
|
||||
public void setTotNumberOfDiscs(int totNumberOfDiscs) {
|
||||
this.totNumberOfDiscs = totNumberOfDiscs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
135
src/net/lingala/zip4j/model/Zip64EndCentralDirRecord.java
Normal file
135
src/net/lingala/zip4j/model/Zip64EndCentralDirRecord.java
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
public class Zip64EndCentralDirRecord {
|
||||
|
||||
private long signature;
|
||||
|
||||
private long sizeOfZip64EndCentralDirRec;
|
||||
|
||||
private int versionMadeBy;
|
||||
|
||||
private int versionNeededToExtract;
|
||||
|
||||
private int noOfThisDisk;
|
||||
|
||||
private int noOfThisDiskStartOfCentralDir;
|
||||
|
||||
private long totNoOfEntriesInCentralDirOnThisDisk;
|
||||
|
||||
private long totNoOfEntriesInCentralDir;
|
||||
|
||||
private long sizeOfCentralDir;
|
||||
|
||||
private long offsetStartCenDirWRTStartDiskNo;
|
||||
|
||||
private byte[] extensibleDataSector;
|
||||
|
||||
public long getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void setSignature(long signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public long getSizeOfZip64EndCentralDirRec() {
|
||||
return sizeOfZip64EndCentralDirRec;
|
||||
}
|
||||
|
||||
public void setSizeOfZip64EndCentralDirRec(long sizeOfZip64EndCentralDirRec) {
|
||||
this.sizeOfZip64EndCentralDirRec = sizeOfZip64EndCentralDirRec;
|
||||
}
|
||||
|
||||
public int getVersionMadeBy() {
|
||||
return versionMadeBy;
|
||||
}
|
||||
|
||||
public void setVersionMadeBy(int versionMadeBy) {
|
||||
this.versionMadeBy = versionMadeBy;
|
||||
}
|
||||
|
||||
public int getVersionNeededToExtract() {
|
||||
return versionNeededToExtract;
|
||||
}
|
||||
|
||||
public void setVersionNeededToExtract(int versionNeededToExtract) {
|
||||
this.versionNeededToExtract = versionNeededToExtract;
|
||||
}
|
||||
|
||||
public int getNoOfThisDisk() {
|
||||
return noOfThisDisk;
|
||||
}
|
||||
|
||||
public void setNoOfThisDisk(int noOfThisDisk) {
|
||||
this.noOfThisDisk = noOfThisDisk;
|
||||
}
|
||||
|
||||
public int getNoOfThisDiskStartOfCentralDir() {
|
||||
return noOfThisDiskStartOfCentralDir;
|
||||
}
|
||||
|
||||
public void setNoOfThisDiskStartOfCentralDir(int noOfThisDiskStartOfCentralDir) {
|
||||
this.noOfThisDiskStartOfCentralDir = noOfThisDiskStartOfCentralDir;
|
||||
}
|
||||
|
||||
public long getTotNoOfEntriesInCentralDirOnThisDisk() {
|
||||
return totNoOfEntriesInCentralDirOnThisDisk;
|
||||
}
|
||||
|
||||
public void setTotNoOfEntriesInCentralDirOnThisDisk(
|
||||
long totNoOfEntriesInCentralDirOnThisDisk) {
|
||||
this.totNoOfEntriesInCentralDirOnThisDisk = totNoOfEntriesInCentralDirOnThisDisk;
|
||||
}
|
||||
|
||||
public long getTotNoOfEntriesInCentralDir() {
|
||||
return totNoOfEntriesInCentralDir;
|
||||
}
|
||||
|
||||
public void setTotNoOfEntriesInCentralDir(long totNoOfEntriesInCentralDir) {
|
||||
this.totNoOfEntriesInCentralDir = totNoOfEntriesInCentralDir;
|
||||
}
|
||||
|
||||
public long getSizeOfCentralDir() {
|
||||
return sizeOfCentralDir;
|
||||
}
|
||||
|
||||
public void setSizeOfCentralDir(long sizeOfCentralDir) {
|
||||
this.sizeOfCentralDir = sizeOfCentralDir;
|
||||
}
|
||||
|
||||
public long getOffsetStartCenDirWRTStartDiskNo() {
|
||||
return offsetStartCenDirWRTStartDiskNo;
|
||||
}
|
||||
|
||||
public void setOffsetStartCenDirWRTStartDiskNo(
|
||||
long offsetStartCenDirWRTStartDiskNo) {
|
||||
this.offsetStartCenDirWRTStartDiskNo = offsetStartCenDirWRTStartDiskNo;
|
||||
}
|
||||
|
||||
public byte[] getExtensibleDataSector() {
|
||||
return extensibleDataSector;
|
||||
}
|
||||
|
||||
public void setExtensibleDataSector(byte[] extensibleDataSector) {
|
||||
this.extensibleDataSector = extensibleDataSector;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
90
src/net/lingala/zip4j/model/Zip64ExtendedInfo.java
Normal file
90
src/net/lingala/zip4j/model/Zip64ExtendedInfo.java
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
public class Zip64ExtendedInfo {
|
||||
|
||||
private int header;
|
||||
|
||||
private int size;
|
||||
|
||||
private long compressedSize;
|
||||
|
||||
private long unCompressedSize;
|
||||
|
||||
private long offsetLocalHeader;
|
||||
|
||||
private int diskNumberStart;
|
||||
|
||||
public Zip64ExtendedInfo() {
|
||||
compressedSize = -1;
|
||||
unCompressedSize = -1;
|
||||
offsetLocalHeader = -1;
|
||||
diskNumberStart = -1;
|
||||
}
|
||||
|
||||
public int getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public void setHeader(int header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public long getCompressedSize() {
|
||||
return compressedSize;
|
||||
}
|
||||
|
||||
public void setCompressedSize(long compressedSize) {
|
||||
this.compressedSize = compressedSize;
|
||||
}
|
||||
|
||||
public long getUnCompressedSize() {
|
||||
return unCompressedSize;
|
||||
}
|
||||
|
||||
public void setUnCompressedSize(long unCompressedSize) {
|
||||
this.unCompressedSize = unCompressedSize;
|
||||
}
|
||||
|
||||
public long getOffsetLocalHeader() {
|
||||
return offsetLocalHeader;
|
||||
}
|
||||
|
||||
public void setOffsetLocalHeader(long offsetLocalHeader) {
|
||||
this.offsetLocalHeader = offsetLocalHeader;
|
||||
}
|
||||
|
||||
public int getDiskNumberStart() {
|
||||
return diskNumberStart;
|
||||
}
|
||||
|
||||
public void setDiskNumberStart(int diskNumberStart) {
|
||||
this.diskNumberStart = diskNumberStart;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
184
src/net/lingala/zip4j/model/ZipModel.java
Normal file
184
src/net/lingala/zip4j/model/ZipModel.java
Normal file
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ZipModel implements Cloneable {
|
||||
|
||||
private List localFileHeaderList;
|
||||
|
||||
private List dataDescriptorList;
|
||||
|
||||
private ArchiveExtraDataRecord archiveExtraDataRecord;
|
||||
|
||||
private CentralDirectory centralDirectory;
|
||||
|
||||
private EndCentralDirRecord endCentralDirRecord;
|
||||
|
||||
private Zip64EndCentralDirLocator zip64EndCentralDirLocator;
|
||||
|
||||
private Zip64EndCentralDirRecord zip64EndCentralDirRecord;
|
||||
|
||||
private boolean splitArchive;
|
||||
|
||||
private long splitLength;
|
||||
|
||||
private String zipFile;
|
||||
|
||||
private boolean isZip64Format;
|
||||
|
||||
private boolean isNestedZipFile;
|
||||
|
||||
private long start;
|
||||
|
||||
private long end;
|
||||
|
||||
private String fileNameCharset;
|
||||
|
||||
public ZipModel() {
|
||||
splitLength = -1;
|
||||
}
|
||||
|
||||
public List getLocalFileHeaderList() {
|
||||
return localFileHeaderList;
|
||||
}
|
||||
|
||||
public void setLocalFileHeaderList(List localFileHeaderList) {
|
||||
this.localFileHeaderList = localFileHeaderList;
|
||||
}
|
||||
|
||||
public List getDataDescriptorList() {
|
||||
return dataDescriptorList;
|
||||
}
|
||||
|
||||
public void setDataDescriptorList(List dataDescriptorList) {
|
||||
this.dataDescriptorList = dataDescriptorList;
|
||||
}
|
||||
|
||||
public CentralDirectory getCentralDirectory() {
|
||||
return centralDirectory;
|
||||
}
|
||||
|
||||
public void setCentralDirectory(CentralDirectory centralDirectory) {
|
||||
this.centralDirectory = centralDirectory;
|
||||
}
|
||||
|
||||
public EndCentralDirRecord getEndCentralDirRecord() {
|
||||
return endCentralDirRecord;
|
||||
}
|
||||
|
||||
public void setEndCentralDirRecord(EndCentralDirRecord endCentralDirRecord) {
|
||||
this.endCentralDirRecord = endCentralDirRecord;
|
||||
}
|
||||
|
||||
public ArchiveExtraDataRecord getArchiveExtraDataRecord() {
|
||||
return archiveExtraDataRecord;
|
||||
}
|
||||
|
||||
public void setArchiveExtraDataRecord(
|
||||
ArchiveExtraDataRecord archiveExtraDataRecord) {
|
||||
this.archiveExtraDataRecord = archiveExtraDataRecord;
|
||||
}
|
||||
|
||||
public boolean isSplitArchive() {
|
||||
return splitArchive;
|
||||
}
|
||||
|
||||
public void setSplitArchive(boolean splitArchive) {
|
||||
this.splitArchive = splitArchive;
|
||||
}
|
||||
|
||||
public String getZipFile() {
|
||||
return zipFile;
|
||||
}
|
||||
|
||||
public void setZipFile(String zipFile) {
|
||||
this.zipFile = zipFile;
|
||||
}
|
||||
|
||||
public Zip64EndCentralDirLocator getZip64EndCentralDirLocator() {
|
||||
return zip64EndCentralDirLocator;
|
||||
}
|
||||
|
||||
public void setZip64EndCentralDirLocator(
|
||||
Zip64EndCentralDirLocator zip64EndCentralDirLocator) {
|
||||
this.zip64EndCentralDirLocator = zip64EndCentralDirLocator;
|
||||
}
|
||||
|
||||
public Zip64EndCentralDirRecord getZip64EndCentralDirRecord() {
|
||||
return zip64EndCentralDirRecord;
|
||||
}
|
||||
|
||||
public void setZip64EndCentralDirRecord(
|
||||
Zip64EndCentralDirRecord zip64EndCentralDirRecord) {
|
||||
this.zip64EndCentralDirRecord = zip64EndCentralDirRecord;
|
||||
}
|
||||
|
||||
public boolean isZip64Format() {
|
||||
return isZip64Format;
|
||||
}
|
||||
|
||||
public void setZip64Format(boolean isZip64Format) {
|
||||
this.isZip64Format = isZip64Format;
|
||||
}
|
||||
|
||||
public boolean isNestedZipFile() {
|
||||
return isNestedZipFile;
|
||||
}
|
||||
|
||||
public void setNestedZipFile(boolean isNestedZipFile) {
|
||||
this.isNestedZipFile = isNestedZipFile;
|
||||
}
|
||||
|
||||
public long getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public void setStart(long start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public long getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public void setEnd(long end) {
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public long getSplitLength() {
|
||||
return splitLength;
|
||||
}
|
||||
|
||||
public void setSplitLength(long splitLength) {
|
||||
this.splitLength = splitLength;
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
public String getFileNameCharset() {
|
||||
return fileNameCharset;
|
||||
}
|
||||
|
||||
public void setFileNameCharset(String fileNameCharset) {
|
||||
this.fileNameCharset = fileNameCharset;
|
||||
}
|
||||
|
||||
}
|
195
src/net/lingala/zip4j/model/ZipParameters.java
Normal file
195
src/net/lingala/zip4j/model/ZipParameters.java
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.model;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
import net.lingala.zip4j.util.Zip4jConstants;
|
||||
import net.lingala.zip4j.util.Zip4jUtil;
|
||||
|
||||
public class ZipParameters implements Cloneable {
|
||||
|
||||
private int compressionMethod;
|
||||
private int compressionLevel;
|
||||
private boolean encryptFiles;
|
||||
private int encryptionMethod;
|
||||
private boolean readHiddenFiles;
|
||||
private char[] password;
|
||||
private int aesKeyStrength;
|
||||
private boolean includeRootFolder;
|
||||
private String rootFolderInZip;
|
||||
private TimeZone timeZone;
|
||||
private int sourceFileCRC;
|
||||
private String defaultFolderPath;
|
||||
private String fileNameInZip;
|
||||
private boolean isSourceExternalStream;
|
||||
|
||||
public ZipParameters() {
|
||||
compressionMethod = Zip4jConstants.COMP_DEFLATE;
|
||||
encryptFiles = false;
|
||||
readHiddenFiles = true;
|
||||
encryptionMethod = Zip4jConstants.ENC_NO_ENCRYPTION;
|
||||
aesKeyStrength = -1;
|
||||
includeRootFolder = true;
|
||||
timeZone = TimeZone.getDefault();
|
||||
}
|
||||
|
||||
public int getCompressionMethod() {
|
||||
return compressionMethod;
|
||||
}
|
||||
|
||||
public void setCompressionMethod(int compressionMethod) {
|
||||
this.compressionMethod = compressionMethod;
|
||||
}
|
||||
|
||||
public boolean isEncryptFiles() {
|
||||
return encryptFiles;
|
||||
}
|
||||
|
||||
public void setEncryptFiles(boolean encryptFiles) {
|
||||
this.encryptFiles = encryptFiles;
|
||||
}
|
||||
|
||||
public int getEncryptionMethod() {
|
||||
return encryptionMethod;
|
||||
}
|
||||
|
||||
public void setEncryptionMethod(int encryptionMethod) {
|
||||
this.encryptionMethod = encryptionMethod;
|
||||
}
|
||||
|
||||
public int getCompressionLevel() {
|
||||
return compressionLevel;
|
||||
}
|
||||
|
||||
public void setCompressionLevel(int compressionLevel) {
|
||||
this.compressionLevel = compressionLevel;
|
||||
}
|
||||
|
||||
public boolean isReadHiddenFiles() {
|
||||
return readHiddenFiles;
|
||||
}
|
||||
|
||||
public void setReadHiddenFiles(boolean readHiddenFiles) {
|
||||
this.readHiddenFiles = readHiddenFiles;
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
public char[] getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password for the zip file or the file being added<br>
|
||||
* <b>Note</b>: For security reasons, usage of this method is discouraged. Use
|
||||
* setPassword(char[]) instead. As strings are immutable, they cannot be wiped
|
||||
* out from memory explicitly after usage. Therefore, usage of Strings to store
|
||||
* passwords is discouraged. More info here:
|
||||
* http://docs.oracle.com/javase/1.5.0/docs/guide/security/jce/JCERefGuide.html#PBEEx
|
||||
* @param password
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
if (password == null) return;
|
||||
setPassword(password.toCharArray());
|
||||
}
|
||||
|
||||
public void setPassword(char[] password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public int getAesKeyStrength() {
|
||||
return aesKeyStrength;
|
||||
}
|
||||
|
||||
public void setAesKeyStrength(int aesKeyStrength) {
|
||||
this.aesKeyStrength = aesKeyStrength;
|
||||
}
|
||||
|
||||
public boolean isIncludeRootFolder() {
|
||||
return includeRootFolder;
|
||||
}
|
||||
|
||||
public void setIncludeRootFolder(boolean includeRootFolder) {
|
||||
this.includeRootFolder = includeRootFolder;
|
||||
}
|
||||
|
||||
public String getRootFolderInZip() {
|
||||
return rootFolderInZip;
|
||||
}
|
||||
|
||||
public void setRootFolderInZip(String rootFolderInZip) {
|
||||
if (Zip4jUtil.isStringNotNullAndNotEmpty(rootFolderInZip)) {
|
||||
|
||||
if (!rootFolderInZip.endsWith("\\") && !rootFolderInZip.endsWith("/")) {
|
||||
rootFolderInZip = rootFolderInZip + InternalZipConstants.FILE_SEPARATOR;
|
||||
}
|
||||
|
||||
rootFolderInZip = rootFolderInZip.replaceAll("\\\\", "/");
|
||||
|
||||
// if (rootFolderInZip.endsWith("/")) {
|
||||
// rootFolderInZip = rootFolderInZip.substring(0, rootFolderInZip.length() - 1);
|
||||
// rootFolderInZip = rootFolderInZip + "\\";
|
||||
// }
|
||||
}
|
||||
this.rootFolderInZip = rootFolderInZip;
|
||||
}
|
||||
|
||||
public TimeZone getTimeZone() {
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
public void setTimeZone(TimeZone timeZone) {
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
public int getSourceFileCRC() {
|
||||
return sourceFileCRC;
|
||||
}
|
||||
|
||||
public void setSourceFileCRC(int sourceFileCRC) {
|
||||
this.sourceFileCRC = sourceFileCRC;
|
||||
}
|
||||
|
||||
public String getDefaultFolderPath() {
|
||||
return defaultFolderPath;
|
||||
}
|
||||
|
||||
public void setDefaultFolderPath(String defaultFolderPath) {
|
||||
this.defaultFolderPath = defaultFolderPath;
|
||||
}
|
||||
|
||||
public String getFileNameInZip() {
|
||||
return fileNameInZip;
|
||||
}
|
||||
|
||||
public void setFileNameInZip(String fileNameInZip) {
|
||||
this.fileNameInZip = fileNameInZip;
|
||||
}
|
||||
|
||||
public boolean isSourceExternalStream() {
|
||||
return isSourceExternalStream;
|
||||
}
|
||||
|
||||
public void setSourceExternalStream(boolean isSourceExternalStream) {
|
||||
this.isSourceExternalStream = isSourceExternalStream;
|
||||
}
|
||||
|
||||
}
|
180
src/net/lingala/zip4j/progress/ProgressMonitor.java
Normal file
180
src/net/lingala/zip4j/progress/ProgressMonitor.java
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.progress;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
|
||||
/**
|
||||
* If Zip4j is set to run in thread mode, this class helps retrieve current progress
|
||||
*
|
||||
*/
|
||||
public class ProgressMonitor {
|
||||
|
||||
private int state;
|
||||
private long totalWork;
|
||||
private long workCompleted;
|
||||
private int percentDone;
|
||||
private int currentOperation;
|
||||
private String fileName;
|
||||
private int result;
|
||||
private Throwable exception;
|
||||
private boolean cancelAllTasks;
|
||||
private boolean pause;
|
||||
|
||||
//Progress monitor States
|
||||
public static final int STATE_READY = 0;
|
||||
public static final int STATE_BUSY = 1;
|
||||
|
||||
//Progress monitor result codes
|
||||
public static final int RESULT_SUCCESS = 0;
|
||||
public static final int RESULT_WORKING = 1;
|
||||
public static final int RESULT_ERROR = 2;
|
||||
public static final int RESULT_CANCELLED = 3;
|
||||
|
||||
//Operation Types
|
||||
public static final int OPERATION_NONE = -1;
|
||||
public static final int OPERATION_ADD = 0;
|
||||
public static final int OPERATION_EXTRACT = 1;
|
||||
public static final int OPERATION_REMOVE = 2;
|
||||
public static final int OPERATION_CALC_CRC = 3;
|
||||
public static final int OPERATION_MERGE = 4;
|
||||
|
||||
public ProgressMonitor() {
|
||||
reset();
|
||||
percentDone = 0;
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public long getTotalWork() {
|
||||
return totalWork;
|
||||
}
|
||||
|
||||
public void setTotalWork(long totalWork) {
|
||||
this.totalWork = totalWork;
|
||||
}
|
||||
|
||||
public long getWorkCompleted() {
|
||||
return workCompleted;
|
||||
}
|
||||
|
||||
public void updateWorkCompleted(long workCompleted) {
|
||||
this.workCompleted += workCompleted;
|
||||
|
||||
if (totalWork > 0) {
|
||||
percentDone = (int)((this.workCompleted*100/totalWork));
|
||||
if (percentDone > 100) {
|
||||
percentDone = 100;
|
||||
}
|
||||
}
|
||||
while (pause) {
|
||||
try {
|
||||
Thread.sleep(150);
|
||||
} catch (InterruptedException e) {
|
||||
//Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getPercentDone() {
|
||||
return percentDone;
|
||||
}
|
||||
|
||||
public void setPercentDone(int percentDone) {
|
||||
this.percentDone = percentDone;
|
||||
}
|
||||
|
||||
public int getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(int result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public int getCurrentOperation() {
|
||||
return currentOperation;
|
||||
}
|
||||
|
||||
public void setCurrentOperation(int currentOperation) {
|
||||
this.currentOperation = currentOperation;
|
||||
}
|
||||
|
||||
public Throwable getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
public void setException(Throwable exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public void endProgressMonitorSuccess() throws ZipException {
|
||||
reset();
|
||||
result = ProgressMonitor.RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
public void endProgressMonitorError(Throwable e) throws ZipException {
|
||||
reset();
|
||||
result = ProgressMonitor.RESULT_ERROR;
|
||||
exception = e;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
currentOperation = OPERATION_NONE;
|
||||
state = STATE_READY;
|
||||
fileName = null;
|
||||
totalWork = 0;
|
||||
workCompleted = 0;
|
||||
percentDone = 0;
|
||||
}
|
||||
|
||||
public void fullReset() {
|
||||
reset();
|
||||
exception = null;
|
||||
result = RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
public boolean isCancelAllTasks() {
|
||||
return cancelAllTasks;
|
||||
}
|
||||
|
||||
public void cancelAllTasks() {
|
||||
this.cancelAllTasks = true;
|
||||
}
|
||||
|
||||
public boolean isPause() {
|
||||
return pause;
|
||||
}
|
||||
|
||||
public void setPause(boolean pause) {
|
||||
this.pause = pause;
|
||||
}
|
||||
}
|
231
src/net/lingala/zip4j/unzip/Unzip.java
Normal file
231
src/net/lingala/zip4j/unzip/Unzip.java
Normal file
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.unzip;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.io.ZipInputStream;
|
||||
import net.lingala.zip4j.model.CentralDirectory;
|
||||
import net.lingala.zip4j.model.FileHeader;
|
||||
import net.lingala.zip4j.model.UnzipParameters;
|
||||
import net.lingala.zip4j.model.ZipModel;
|
||||
import net.lingala.zip4j.progress.ProgressMonitor;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
import net.lingala.zip4j.util.Zip4jUtil;
|
||||
|
||||
public class Unzip {
|
||||
|
||||
private ZipModel zipModel;
|
||||
|
||||
public Unzip(ZipModel zipModel) throws ZipException {
|
||||
|
||||
if (zipModel == null) {
|
||||
throw new ZipException("ZipModel is null");
|
||||
}
|
||||
|
||||
this.zipModel = zipModel;
|
||||
}
|
||||
|
||||
public void extractAll(final UnzipParameters unzipParameters, final String outPath,
|
||||
final ProgressMonitor progressMonitor, boolean runInThread) throws ZipException {
|
||||
|
||||
CentralDirectory centralDirectory = zipModel.getCentralDirectory();
|
||||
|
||||
if (centralDirectory == null ||
|
||||
centralDirectory.getFileHeaders() == null) {
|
||||
throw new ZipException("invalid central directory in zipModel");
|
||||
}
|
||||
|
||||
final ArrayList fileHeaders = centralDirectory.getFileHeaders();
|
||||
|
||||
progressMonitor.setCurrentOperation(ProgressMonitor.OPERATION_EXTRACT);
|
||||
progressMonitor.setTotalWork(calculateTotalWork(fileHeaders));
|
||||
progressMonitor.setState(ProgressMonitor.STATE_BUSY);
|
||||
|
||||
if (runInThread) {
|
||||
Thread thread = new Thread(InternalZipConstants.THREAD_NAME) {
|
||||
public void run() {
|
||||
try {
|
||||
initExtractAll(fileHeaders, unzipParameters, progressMonitor, outPath);
|
||||
progressMonitor.endProgressMonitorSuccess();
|
||||
} catch (ZipException e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
thread.start();
|
||||
} else {
|
||||
initExtractAll(fileHeaders, unzipParameters, progressMonitor, outPath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void initExtractAll(ArrayList fileHeaders, UnzipParameters unzipParameters,
|
||||
ProgressMonitor progressMonitor, String outPath) throws ZipException {
|
||||
|
||||
for (int i = 0; i < fileHeaders.size(); i++) {
|
||||
FileHeader fileHeader = (FileHeader)fileHeaders.get(i);
|
||||
initExtractFile(fileHeader, outPath, unzipParameters, null, progressMonitor);
|
||||
if (progressMonitor.isCancelAllTasks()) {
|
||||
progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
|
||||
progressMonitor.setState(ProgressMonitor.STATE_READY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void extractFile(final FileHeader fileHeader, final String outPath,
|
||||
final UnzipParameters unzipParameters, final String newFileName,
|
||||
final ProgressMonitor progressMonitor, boolean runInThread) throws ZipException {
|
||||
if (fileHeader == null) {
|
||||
throw new ZipException("fileHeader is null");
|
||||
}
|
||||
|
||||
progressMonitor.setCurrentOperation(ProgressMonitor.OPERATION_EXTRACT);
|
||||
progressMonitor.setTotalWork(fileHeader.getCompressedSize());
|
||||
progressMonitor.setState(ProgressMonitor.STATE_BUSY);
|
||||
progressMonitor.setPercentDone(0);
|
||||
progressMonitor.setFileName(fileHeader.getFileName());
|
||||
|
||||
if (runInThread) {
|
||||
Thread thread = new Thread(InternalZipConstants.THREAD_NAME) {
|
||||
public void run() {
|
||||
try {
|
||||
initExtractFile(fileHeader, outPath, unzipParameters, newFileName, progressMonitor);
|
||||
progressMonitor.endProgressMonitorSuccess();
|
||||
} catch (ZipException e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
thread.start();
|
||||
} else {
|
||||
initExtractFile(fileHeader, outPath, unzipParameters, newFileName, progressMonitor);
|
||||
progressMonitor.endProgressMonitorSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void initExtractFile(FileHeader fileHeader, String outPath,
|
||||
UnzipParameters unzipParameters, String newFileName, ProgressMonitor progressMonitor) throws ZipException {
|
||||
|
||||
if (fileHeader == null) {
|
||||
throw new ZipException("fileHeader is null");
|
||||
}
|
||||
|
||||
try {
|
||||
progressMonitor.setFileName(fileHeader.getFileName());
|
||||
|
||||
if (!outPath.endsWith(InternalZipConstants.FILE_SEPARATOR)) {
|
||||
outPath += InternalZipConstants.FILE_SEPARATOR;
|
||||
}
|
||||
|
||||
// If file header is a directory, then check if the directory exists
|
||||
// If not then create a directory and return
|
||||
if (fileHeader.isDirectory()) {
|
||||
try {
|
||||
String fileName = fileHeader.getFileName();
|
||||
if (!Zip4jUtil.isStringNotNullAndNotEmpty(fileName)) {
|
||||
return;
|
||||
}
|
||||
String completePath = outPath + fileName;
|
||||
File file = new File(completePath);
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
progressMonitor.endProgressMonitorError(e);
|
||||
throw new ZipException(e);
|
||||
}
|
||||
} else {
|
||||
//Create Directories
|
||||
checkOutputDirectoryStructure(fileHeader, outPath, newFileName);
|
||||
|
||||
UnzipEngine unzipEngine = new UnzipEngine(zipModel, fileHeader);
|
||||
try {
|
||||
unzipEngine.unzipFile(progressMonitor, outPath, newFileName, unzipParameters);
|
||||
} catch (Exception e) {
|
||||
progressMonitor.endProgressMonitorError(e);
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
} catch (ZipException e) {
|
||||
progressMonitor.endProgressMonitorError(e);
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
progressMonitor.endProgressMonitorError(e);
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public ZipInputStream getInputStream(FileHeader fileHeader) throws ZipException {
|
||||
UnzipEngine unzipEngine = new UnzipEngine(zipModel, fileHeader);
|
||||
return unzipEngine.getInputStream();
|
||||
}
|
||||
|
||||
private void checkOutputDirectoryStructure(FileHeader fileHeader, String outPath, String newFileName) throws ZipException {
|
||||
if (fileHeader == null || !Zip4jUtil.isStringNotNullAndNotEmpty(outPath)) {
|
||||
throw new ZipException("Cannot check output directory structure...one of the parameters was null");
|
||||
}
|
||||
|
||||
String fileName = fileHeader.getFileName();
|
||||
|
||||
if (Zip4jUtil.isStringNotNullAndNotEmpty(newFileName)) {
|
||||
fileName = newFileName;
|
||||
}
|
||||
|
||||
if (!Zip4jUtil.isStringNotNullAndNotEmpty(fileName)) {
|
||||
// Do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
String compOutPath = outPath + fileName;
|
||||
try {
|
||||
File file = new File(compOutPath);
|
||||
String parentDir = file.getParent();
|
||||
File parentDirFile = new File(parentDir);
|
||||
if (!parentDirFile.exists()) {
|
||||
parentDirFile.mkdirs();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private long calculateTotalWork(ArrayList fileHeaders) throws ZipException {
|
||||
|
||||
if (fileHeaders == null) {
|
||||
throw new ZipException("fileHeaders is null, cannot calculate total work");
|
||||
}
|
||||
|
||||
long totalWork = 0;
|
||||
|
||||
for (int i = 0; i < fileHeaders.size(); i++) {
|
||||
FileHeader fileHeader = (FileHeader)fileHeaders.get(i);
|
||||
if (fileHeader.getZip64ExtendedInfo() != null &&
|
||||
fileHeader.getZip64ExtendedInfo().getUnCompressedSize() > 0) {
|
||||
totalWork += fileHeader.getZip64ExtendedInfo().getCompressedSize();
|
||||
} else {
|
||||
totalWork += fileHeader.getCompressedSize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return totalWork;
|
||||
}
|
||||
|
||||
}
|
521
src/net/lingala/zip4j/unzip/UnzipEngine.java
Normal file
521
src/net/lingala/zip4j/unzip/UnzipEngine.java
Normal file
|
@ -0,0 +1,521 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.unzip;
|
||||
|
||||
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.RandomAccessFile;
|
||||
import java.util.Arrays;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
import net.lingala.zip4j.core.HeaderReader;
|
||||
import net.lingala.zip4j.crypto.AESDecrypter;
|
||||
import net.lingala.zip4j.crypto.IDecrypter;
|
||||
import net.lingala.zip4j.crypto.StandardDecrypter;
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.io.InflaterInputStream;
|
||||
import net.lingala.zip4j.io.PartInputStream;
|
||||
import net.lingala.zip4j.io.ZipInputStream;
|
||||
import net.lingala.zip4j.model.AESExtraDataRecord;
|
||||
import net.lingala.zip4j.model.FileHeader;
|
||||
import net.lingala.zip4j.model.LocalFileHeader;
|
||||
import net.lingala.zip4j.model.UnzipParameters;
|
||||
import net.lingala.zip4j.model.ZipModel;
|
||||
import net.lingala.zip4j.progress.ProgressMonitor;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
import net.lingala.zip4j.util.Raw;
|
||||
import net.lingala.zip4j.util.Zip4jConstants;
|
||||
import net.lingala.zip4j.util.Zip4jUtil;
|
||||
|
||||
public class UnzipEngine {
|
||||
|
||||
private ZipModel zipModel;
|
||||
private FileHeader fileHeader;
|
||||
private int currSplitFileCounter = 0;
|
||||
private LocalFileHeader localFileHeader;
|
||||
private IDecrypter decrypter;
|
||||
private CRC32 crc;
|
||||
|
||||
public UnzipEngine(ZipModel zipModel, FileHeader fileHeader) throws ZipException {
|
||||
if (zipModel == null || fileHeader == null) {
|
||||
throw new ZipException("Invalid parameters passed to StoreUnzip. One or more of the parameters were null");
|
||||
}
|
||||
|
||||
this.zipModel = zipModel;
|
||||
this.fileHeader = fileHeader;
|
||||
this.crc = new CRC32();
|
||||
}
|
||||
|
||||
public void unzipFile(ProgressMonitor progressMonitor,
|
||||
String outPath, String newFileName, UnzipParameters unzipParameters) throws ZipException {
|
||||
if (zipModel == null || fileHeader == null || !Zip4jUtil.isStringNotNullAndNotEmpty(outPath)) {
|
||||
throw new ZipException("Invalid parameters passed during unzipping file. One or more of the parameters were null");
|
||||
}
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
try {
|
||||
byte[] buff = new byte[InternalZipConstants.BUFF_SIZE];
|
||||
int readLength = -1;
|
||||
|
||||
is = getInputStream();
|
||||
os = getOutputStream(outPath, newFileName);
|
||||
|
||||
while ((readLength = is.read(buff)) != -1) {
|
||||
os.write(buff, 0, readLength);
|
||||
progressMonitor.updateWorkCompleted(readLength);
|
||||
if (progressMonitor.isCancelAllTasks()) {
|
||||
progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
|
||||
progressMonitor.setState(ProgressMonitor.STATE_READY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
closeStreams(is, os);
|
||||
|
||||
UnzipUtil.applyFileAttributes(fileHeader, new File(getOutputFileNameWithPath(outPath, newFileName)), unzipParameters);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new ZipException(e);
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
} finally {
|
||||
closeStreams(is, os);
|
||||
}
|
||||
}
|
||||
|
||||
public ZipInputStream getInputStream() throws ZipException {
|
||||
if (fileHeader == null) {
|
||||
throw new ZipException("file header is null, cannot get inputstream");
|
||||
}
|
||||
|
||||
RandomAccessFile raf = null;
|
||||
try {
|
||||
raf = createFileHandler(InternalZipConstants.READ_MODE);
|
||||
String errMsg = "local header and file header do not match";
|
||||
//checkSplitFile();
|
||||
|
||||
if (!checkLocalHeader())
|
||||
throw new ZipException(errMsg);
|
||||
|
||||
init(raf);
|
||||
|
||||
long comprSize = localFileHeader.getCompressedSize();
|
||||
long offsetStartOfData = localFileHeader.getOffsetStartOfData();
|
||||
|
||||
if (localFileHeader.isEncrypted()) {
|
||||
if (localFileHeader.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES) {
|
||||
if (decrypter instanceof AESDecrypter) {
|
||||
comprSize -= (((AESDecrypter)decrypter).getSaltLength() +
|
||||
((AESDecrypter)decrypter).getPasswordVerifierLength() + 10);
|
||||
offsetStartOfData += (((AESDecrypter)decrypter).getSaltLength() +
|
||||
((AESDecrypter)decrypter).getPasswordVerifierLength());
|
||||
} else {
|
||||
throw new ZipException("invalid decryptor when trying to calculate " +
|
||||
"compressed size for AES encrypted file: " + fileHeader.getFileName());
|
||||
}
|
||||
} else if (localFileHeader.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_STANDARD) {
|
||||
comprSize -= InternalZipConstants.STD_DEC_HDR_SIZE;
|
||||
offsetStartOfData += InternalZipConstants.STD_DEC_HDR_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
int compressionMethod = fileHeader.getCompressionMethod();
|
||||
if (fileHeader.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES) {
|
||||
if (fileHeader.getAesExtraDataRecord() != null) {
|
||||
compressionMethod = fileHeader.getAesExtraDataRecord().getCompressionMethod();
|
||||
} else {
|
||||
throw new ZipException("AESExtraDataRecord does not exist for AES encrypted file: " + fileHeader.getFileName());
|
||||
}
|
||||
}
|
||||
raf.seek(offsetStartOfData);
|
||||
switch (compressionMethod) {
|
||||
case Zip4jConstants.COMP_STORE:
|
||||
return new ZipInputStream(new PartInputStream(raf, offsetStartOfData, comprSize, this));
|
||||
case Zip4jConstants.COMP_DEFLATE:
|
||||
return new ZipInputStream(new InflaterInputStream(raf, offsetStartOfData, comprSize, this));
|
||||
default:
|
||||
throw new ZipException("compression type not supported");
|
||||
}
|
||||
} catch (ZipException e) {
|
||||
if (raf != null) {
|
||||
try {
|
||||
raf.close();
|
||||
} catch (IOException e1) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
if (raf != null) {
|
||||
try {
|
||||
raf.close();
|
||||
} catch (IOException e1) {
|
||||
}
|
||||
}
|
||||
throw new ZipException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void init(RandomAccessFile raf) throws ZipException {
|
||||
|
||||
if (localFileHeader == null) {
|
||||
throw new ZipException("local file header is null, cannot initialize input stream");
|
||||
}
|
||||
|
||||
try {
|
||||
initDecrypter(raf);
|
||||
} catch (ZipException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void initDecrypter(RandomAccessFile raf) throws ZipException {
|
||||
if (localFileHeader == null) {
|
||||
throw new ZipException("local file header is null, cannot init decrypter");
|
||||
}
|
||||
|
||||
if (localFileHeader.isEncrypted()) {
|
||||
if (localFileHeader.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_STANDARD) {
|
||||
decrypter = new StandardDecrypter(fileHeader, getStandardDecrypterHeaderBytes(raf));
|
||||
} else if (localFileHeader.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES) {
|
||||
decrypter = new AESDecrypter(localFileHeader, getAESSalt(raf), getAESPasswordVerifier(raf));
|
||||
} else {
|
||||
throw new ZipException("unsupported encryption method");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getStandardDecrypterHeaderBytes(RandomAccessFile raf) throws ZipException {
|
||||
try {
|
||||
byte[] headerBytes = new byte[InternalZipConstants.STD_DEC_HDR_SIZE];
|
||||
raf.seek(localFileHeader.getOffsetStartOfData());
|
||||
raf.read(headerBytes, 0, 12);
|
||||
return headerBytes;
|
||||
} catch (IOException e) {
|
||||
throw new ZipException(e);
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getAESSalt(RandomAccessFile raf) throws ZipException {
|
||||
if (localFileHeader.getAesExtraDataRecord() == null)
|
||||
return null;
|
||||
|
||||
try {
|
||||
AESExtraDataRecord aesExtraDataRecord = localFileHeader.getAesExtraDataRecord();
|
||||
byte[] saltBytes = new byte[calculateAESSaltLength(aesExtraDataRecord)];
|
||||
raf.seek(localFileHeader.getOffsetStartOfData());
|
||||
raf.read(saltBytes);
|
||||
return saltBytes;
|
||||
} catch (IOException e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getAESPasswordVerifier(RandomAccessFile raf) throws ZipException {
|
||||
try {
|
||||
byte[] pvBytes = new byte[2];
|
||||
raf.read(pvBytes);
|
||||
return pvBytes;
|
||||
} catch (IOException e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private int calculateAESSaltLength(AESExtraDataRecord aesExtraDataRecord) throws ZipException {
|
||||
if (aesExtraDataRecord == null) {
|
||||
throw new ZipException("unable to determine salt length: AESExtraDataRecord is null");
|
||||
}
|
||||
switch (aesExtraDataRecord.getAesStrength()) {
|
||||
case Zip4jConstants.AES_STRENGTH_128:
|
||||
return 8;
|
||||
case Zip4jConstants.AES_STRENGTH_192:
|
||||
return 12;
|
||||
case Zip4jConstants.AES_STRENGTH_256:
|
||||
return 16;
|
||||
default:
|
||||
throw new ZipException("unable to determine salt length: invalid aes key strength");
|
||||
}
|
||||
}
|
||||
|
||||
public void checkCRC() throws ZipException {
|
||||
if (fileHeader != null) {
|
||||
if (fileHeader.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES) {
|
||||
if (decrypter != null && decrypter instanceof AESDecrypter) {
|
||||
byte[] tmpMacBytes = ((AESDecrypter)decrypter).getCalculatedAuthenticationBytes();
|
||||
byte[] storedMac = ((AESDecrypter)decrypter).getStoredMac();
|
||||
byte[] calculatedMac = new byte[InternalZipConstants.AES_AUTH_LENGTH];
|
||||
|
||||
if (calculatedMac == null || storedMac == null) {
|
||||
throw new ZipException("CRC (MAC) check failed for " + fileHeader.getFileName());
|
||||
}
|
||||
|
||||
System.arraycopy(tmpMacBytes, 0, calculatedMac, 0, InternalZipConstants.AES_AUTH_LENGTH);
|
||||
|
||||
if (!Arrays.equals(calculatedMac, storedMac)) {
|
||||
throw new ZipException("invalid CRC (MAC) for file: " + fileHeader.getFileName());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
long calculatedCRC = crc.getValue() & 0xffffffffL;
|
||||
if (calculatedCRC != fileHeader.getCrc32()) {
|
||||
String errMsg = "invalid CRC for file: " + fileHeader.getFileName();
|
||||
if (localFileHeader.isEncrypted() &&
|
||||
localFileHeader.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_STANDARD) {
|
||||
errMsg += " - Wrong Password?";
|
||||
}
|
||||
throw new ZipException(errMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// private void checkCRC() throws ZipException {
|
||||
// if (fileHeader != null) {
|
||||
// if (fileHeader.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES) {
|
||||
// if (decrypter != null && decrypter instanceof AESDecrypter) {
|
||||
// byte[] tmpMacBytes = ((AESDecrypter)decrypter).getCalculatedAuthenticationBytes();
|
||||
// byte[] actualMacBytes = ((AESDecrypter)decrypter).getStoredMac();
|
||||
// if (tmpMacBytes == null || actualMacBytes == null) {
|
||||
// throw new ZipException("null mac value for AES encrypted file: " + fileHeader.getFileName());
|
||||
// }
|
||||
// byte[] calcMacBytes = new byte[10];
|
||||
// System.arraycopy(tmpMacBytes, 0, calcMacBytes, 0, 10);
|
||||
// if (!Arrays.equals(calcMacBytes, actualMacBytes)) {
|
||||
// throw new ZipException("invalid CRC(mac) for file: " + fileHeader.getFileName());
|
||||
// }
|
||||
// } else {
|
||||
// throw new ZipException("invalid decryptor...cannot calculate mac value for file: "
|
||||
// + fileHeader.getFileName());
|
||||
// }
|
||||
// } else if (unzipEngine != null) {
|
||||
// long calculatedCRC = unzipEngine.getCRC();
|
||||
// long actualCRC = fileHeader.getCrc32();
|
||||
// if (calculatedCRC != actualCRC) {
|
||||
// throw new ZipException("invalid CRC for file: " + fileHeader.getFileName());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private boolean checkLocalHeader() throws ZipException {
|
||||
RandomAccessFile rafForLH = null;
|
||||
try {
|
||||
rafForLH = checkSplitFile();
|
||||
|
||||
if (rafForLH == null) {
|
||||
rafForLH = new RandomAccessFile(new File(this.zipModel.getZipFile()), InternalZipConstants.READ_MODE);
|
||||
}
|
||||
|
||||
HeaderReader headerReader = new HeaderReader(rafForLH);
|
||||
this.localFileHeader = headerReader.readLocalFileHeader(fileHeader);
|
||||
|
||||
if (localFileHeader == null) {
|
||||
throw new ZipException("error reading local file header. Is this a valid zip file?");
|
||||
}
|
||||
|
||||
//TODO Add more comparision later
|
||||
if (localFileHeader.getCompressionMethod() != fileHeader.getCompressionMethod()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new ZipException(e);
|
||||
} finally {
|
||||
if (rafForLH != null) {
|
||||
try {
|
||||
rafForLH.close();
|
||||
} catch (IOException e) {
|
||||
// Ignore this
|
||||
} catch (Exception e) {
|
||||
//Ignore this
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RandomAccessFile checkSplitFile() throws ZipException {
|
||||
if (zipModel.isSplitArchive()) {
|
||||
int diskNumberStartOfFile = fileHeader.getDiskNumberStart();
|
||||
currSplitFileCounter = diskNumberStartOfFile + 1;
|
||||
String curZipFile = zipModel.getZipFile();
|
||||
String partFile = null;
|
||||
if (diskNumberStartOfFile == zipModel.getEndCentralDirRecord().getNoOfThisDisk()) {
|
||||
partFile = zipModel.getZipFile();
|
||||
} else {
|
||||
if (diskNumberStartOfFile >= 9) {
|
||||
partFile = curZipFile.substring(0, curZipFile.lastIndexOf(".")) + ".z" + (diskNumberStartOfFile+ 1);
|
||||
} else{
|
||||
partFile = curZipFile.substring(0, curZipFile.lastIndexOf(".")) + ".z0" + (diskNumberStartOfFile+ 1);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
RandomAccessFile raf = new RandomAccessFile(partFile, InternalZipConstants.READ_MODE);
|
||||
|
||||
if (currSplitFileCounter == 1) {
|
||||
byte[] splitSig = new byte[4];
|
||||
raf.read(splitSig);
|
||||
if (Raw.readIntLittleEndian(splitSig, 0) != InternalZipConstants.SPLITSIG) {
|
||||
throw new ZipException("invalid first part split file signature");
|
||||
}
|
||||
}
|
||||
return raf;
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new ZipException(e);
|
||||
} catch (IOException e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private RandomAccessFile createFileHandler(String mode) throws ZipException {
|
||||
if (this.zipModel == null || !Zip4jUtil.isStringNotNullAndNotEmpty(this.zipModel.getZipFile())) {
|
||||
throw new ZipException("input parameter is null in getFilePointer");
|
||||
}
|
||||
|
||||
try {
|
||||
RandomAccessFile raf = null;
|
||||
if (zipModel.isSplitArchive()) {
|
||||
raf = checkSplitFile();
|
||||
} else {
|
||||
raf = new RandomAccessFile(new File(this.zipModel.getZipFile()), mode);
|
||||
}
|
||||
return raf;
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new ZipException(e);
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private FileOutputStream getOutputStream(String outPath, String newFileName) throws ZipException {
|
||||
if (!Zip4jUtil.isStringNotNullAndNotEmpty(outPath)) {
|
||||
throw new ZipException("invalid output path");
|
||||
}
|
||||
|
||||
try {
|
||||
File file = new File(getOutputFileNameWithPath(outPath, newFileName));
|
||||
|
||||
if (!file.getParentFile().exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(file);
|
||||
return fileOutputStream;
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getOutputFileNameWithPath(String outPath, String newFileName) throws ZipException {
|
||||
String fileName = null;
|
||||
if (Zip4jUtil.isStringNotNullAndNotEmpty(newFileName)) {
|
||||
fileName = newFileName;
|
||||
} else {
|
||||
fileName = fileHeader.getFileName();
|
||||
}
|
||||
return outPath + System.getProperty("file.separator") + fileName;
|
||||
}
|
||||
|
||||
public RandomAccessFile startNextSplitFile() throws IOException, FileNotFoundException {
|
||||
String currZipFile = zipModel.getZipFile();
|
||||
String partFile = null;
|
||||
if (currSplitFileCounter == zipModel.getEndCentralDirRecord().getNoOfThisDisk()) {
|
||||
partFile = zipModel.getZipFile();
|
||||
} else {
|
||||
if (currSplitFileCounter >= 9) {
|
||||
partFile = currZipFile.substring(0, currZipFile.lastIndexOf(".")) + ".z" + (currSplitFileCounter + 1);
|
||||
} else {
|
||||
partFile = currZipFile.substring(0, currZipFile.lastIndexOf(".")) + ".z0" + (currSplitFileCounter + 1);
|
||||
}
|
||||
}
|
||||
currSplitFileCounter++;
|
||||
try {
|
||||
if(!Zip4jUtil.checkFileExists(partFile)) {
|
||||
throw new IOException("zip split file does not exist: " + partFile);
|
||||
}
|
||||
} catch (ZipException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
return new RandomAccessFile(partFile, InternalZipConstants.READ_MODE);
|
||||
}
|
||||
|
||||
private void closeStreams(InputStream is, OutputStream os) throws ZipException {
|
||||
try {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
is = null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (e != null && Zip4jUtil.isStringNotNullAndNotEmpty(e.getMessage())) {
|
||||
if (e.getMessage().indexOf(" - Wrong Password?") >= 0) {
|
||||
throw new ZipException(e.getMessage());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
if (os != null) {
|
||||
os.close();
|
||||
os = null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateCRC(int b) {
|
||||
crc.update(b);
|
||||
}
|
||||
|
||||
public void updateCRC(byte[] buff, int offset, int len) {
|
||||
if (buff != null) {
|
||||
crc.update(buff, offset, len);
|
||||
}
|
||||
}
|
||||
|
||||
public FileHeader getFileHeader() {
|
||||
return fileHeader;
|
||||
}
|
||||
|
||||
public IDecrypter getDecrypter() {
|
||||
return decrypter;
|
||||
}
|
||||
|
||||
public ZipModel getZipModel() {
|
||||
return zipModel;
|
||||
}
|
||||
|
||||
public LocalFileHeader getLocalFileHeader() {
|
||||
return localFileHeader;
|
||||
}
|
||||
}
|
113
src/net/lingala/zip4j/unzip/UnzipUtil.java
Normal file
113
src/net/lingala/zip4j/unzip/UnzipUtil.java
Normal file
|
@ -0,0 +1,113 @@
|
|||
package net.lingala.zip4j.unzip;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.model.FileHeader;
|
||||
import net.lingala.zip4j.model.UnzipParameters;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
import net.lingala.zip4j.util.Zip4jUtil;
|
||||
|
||||
public class UnzipUtil {
|
||||
|
||||
public static void applyFileAttributes(FileHeader fileHeader, File file) throws ZipException {
|
||||
applyFileAttributes(fileHeader, file, null);
|
||||
}
|
||||
|
||||
public static void applyFileAttributes(FileHeader fileHeader, File file,
|
||||
UnzipParameters unzipParameters) throws ZipException{
|
||||
|
||||
if (fileHeader == null) {
|
||||
throw new ZipException("cannot set file properties: file header is null");
|
||||
}
|
||||
|
||||
if (file == null) {
|
||||
throw new ZipException("cannot set file properties: output file is null");
|
||||
}
|
||||
|
||||
if (!Zip4jUtil.checkFileExists(file)) {
|
||||
throw new ZipException("cannot set file properties: file doesnot exist");
|
||||
}
|
||||
|
||||
if (unzipParameters == null || !unzipParameters.isIgnoreDateTimeAttributes()) {
|
||||
setFileLastModifiedTime(fileHeader, file);
|
||||
}
|
||||
|
||||
if (unzipParameters == null) {
|
||||
setFileAttributes(fileHeader, file, true, true, true, true);
|
||||
} else {
|
||||
if (unzipParameters.isIgnoreAllFileAttributes()) {
|
||||
setFileAttributes(fileHeader, file, false, false, false, false);
|
||||
} else {
|
||||
setFileAttributes(fileHeader, file, !unzipParameters.isIgnoreReadOnlyFileAttribute(),
|
||||
!unzipParameters.isIgnoreHiddenFileAttribute(),
|
||||
!unzipParameters.isIgnoreArchiveFileAttribute(),
|
||||
!unzipParameters.isIgnoreSystemFileAttribute());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setFileAttributes(FileHeader fileHeader, File file, boolean setReadOnly,
|
||||
boolean setHidden, boolean setArchive, boolean setSystem) throws ZipException {
|
||||
if (fileHeader == null) {
|
||||
throw new ZipException("invalid file header. cannot set file attributes");
|
||||
}
|
||||
|
||||
byte[] externalAttrbs = fileHeader.getExternalFileAttr();
|
||||
if (externalAttrbs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int atrrib = externalAttrbs[0];
|
||||
switch (atrrib) {
|
||||
case InternalZipConstants.FILE_MODE_READ_ONLY:
|
||||
if (setReadOnly) Zip4jUtil.setFileReadOnly(file);
|
||||
break;
|
||||
case InternalZipConstants.FILE_MODE_HIDDEN:
|
||||
case InternalZipConstants.FOLDER_MODE_HIDDEN:
|
||||
if (setHidden) Zip4jUtil.setFileHidden(file);
|
||||
break;
|
||||
case InternalZipConstants.FILE_MODE_ARCHIVE:
|
||||
case InternalZipConstants.FOLDER_MODE_ARCHIVE:
|
||||
if (setArchive) Zip4jUtil.setFileArchive(file);
|
||||
break;
|
||||
case InternalZipConstants.FILE_MODE_READ_ONLY_HIDDEN:
|
||||
if (setReadOnly) Zip4jUtil.setFileReadOnly(file);
|
||||
if (setHidden) Zip4jUtil.setFileHidden(file);
|
||||
break;
|
||||
case InternalZipConstants.FILE_MODE_READ_ONLY_ARCHIVE:
|
||||
if (setArchive) Zip4jUtil.setFileArchive(file);
|
||||
if (setReadOnly) Zip4jUtil.setFileReadOnly(file);
|
||||
break;
|
||||
case InternalZipConstants.FILE_MODE_HIDDEN_ARCHIVE:
|
||||
case InternalZipConstants.FOLDER_MODE_HIDDEN_ARCHIVE:
|
||||
if (setArchive) Zip4jUtil.setFileArchive(file);
|
||||
if (setHidden) Zip4jUtil.setFileHidden(file);
|
||||
break;
|
||||
case InternalZipConstants.FILE_MODE_READ_ONLY_HIDDEN_ARCHIVE:
|
||||
if (setArchive) Zip4jUtil.setFileArchive(file);
|
||||
if (setReadOnly) Zip4jUtil.setFileReadOnly(file);
|
||||
if (setHidden) Zip4jUtil.setFileHidden(file);
|
||||
break;
|
||||
case InternalZipConstants.FILE_MODE_SYSTEM:
|
||||
if (setReadOnly) Zip4jUtil.setFileReadOnly(file);
|
||||
if (setHidden) Zip4jUtil.setFileHidden(file);
|
||||
if (setSystem) Zip4jUtil.setFileSystemMode(file);
|
||||
break;
|
||||
default:
|
||||
//do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void setFileLastModifiedTime(FileHeader fileHeader, File file) throws ZipException {
|
||||
if (fileHeader.getLastModFileTime() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.exists()) {
|
||||
file.setLastModified(Zip4jUtil.dosToJavaTme(fileHeader.getLastModFileTime()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
727
src/net/lingala/zip4j/util/ArchiveMaintainer.java
Normal file
727
src/net/lingala/zip4j/util/ArchiveMaintainer.java
Normal file
|
@ -0,0 +1,727 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import net.lingala.zip4j.core.HeaderReader;
|
||||
import net.lingala.zip4j.core.HeaderWriter;
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.io.SplitOutputStream;
|
||||
import net.lingala.zip4j.model.FileHeader;
|
||||
import net.lingala.zip4j.model.LocalFileHeader;
|
||||
import net.lingala.zip4j.model.Zip64EndCentralDirLocator;
|
||||
import net.lingala.zip4j.model.Zip64EndCentralDirRecord;
|
||||
import net.lingala.zip4j.model.ZipModel;
|
||||
import net.lingala.zip4j.progress.ProgressMonitor;
|
||||
|
||||
public class ArchiveMaintainer {
|
||||
|
||||
public ArchiveMaintainer() {
|
||||
}
|
||||
|
||||
public HashMap removeZipFile(final ZipModel zipModel,
|
||||
final FileHeader fileHeader, final ProgressMonitor progressMonitor, boolean runInThread) throws ZipException {
|
||||
|
||||
if (runInThread) {
|
||||
Thread thread = new Thread(InternalZipConstants.THREAD_NAME) {
|
||||
public void run() {
|
||||
try {
|
||||
initRemoveZipFile(zipModel, fileHeader, progressMonitor);
|
||||
progressMonitor.endProgressMonitorSuccess();
|
||||
} catch (ZipException e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
thread.start();
|
||||
return null;
|
||||
} else {
|
||||
HashMap retMap = initRemoveZipFile(zipModel, fileHeader, progressMonitor);
|
||||
progressMonitor.endProgressMonitorSuccess();
|
||||
return retMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public HashMap initRemoveZipFile(ZipModel zipModel,
|
||||
FileHeader fileHeader, ProgressMonitor progressMonitor) throws ZipException {
|
||||
|
||||
if (fileHeader == null || zipModel == null) {
|
||||
throw new ZipException("input parameters is null in maintain zip file, cannot remove file from archive");
|
||||
}
|
||||
|
||||
OutputStream outputStream = null;
|
||||
File zipFile = null;
|
||||
RandomAccessFile inputStream = null;
|
||||
boolean successFlag = false;
|
||||
String tmpZipFileName = null;
|
||||
HashMap retMap = new HashMap();
|
||||
|
||||
try {
|
||||
int indexOfFileHeader = Zip4jUtil.getIndexOfFileHeader(zipModel, fileHeader);
|
||||
|
||||
if (indexOfFileHeader < 0) {
|
||||
throw new ZipException("file header not found in zip model, cannot remove file");
|
||||
}
|
||||
|
||||
if (zipModel.isSplitArchive()) {
|
||||
throw new ZipException("This is a split archive. Zip file format does not allow updating split/spanned files");
|
||||
}
|
||||
|
||||
long currTime = System.currentTimeMillis();
|
||||
tmpZipFileName = zipModel.getZipFile() + currTime%1000;
|
||||
File tmpFile = new File(tmpZipFileName);
|
||||
|
||||
while (tmpFile.exists()) {
|
||||
currTime = System.currentTimeMillis();
|
||||
tmpZipFileName = zipModel.getZipFile() + currTime%1000;
|
||||
tmpFile = new File(tmpZipFileName);
|
||||
}
|
||||
|
||||
try {
|
||||
outputStream = new SplitOutputStream(new File(tmpZipFileName));
|
||||
} catch (FileNotFoundException e1) {
|
||||
throw new ZipException(e1);
|
||||
}
|
||||
|
||||
zipFile = new File(zipModel.getZipFile());
|
||||
|
||||
inputStream = createFileHandler(zipModel, InternalZipConstants.READ_MODE);
|
||||
|
||||
HeaderReader headerReader = new HeaderReader(inputStream);
|
||||
LocalFileHeader localFileHeader = headerReader.readLocalFileHeader(fileHeader);
|
||||
if (localFileHeader == null) {
|
||||
throw new ZipException("invalid local file header, cannot remove file from archive");
|
||||
}
|
||||
|
||||
long offsetLocalFileHeader = fileHeader.getOffsetLocalHeader();
|
||||
|
||||
if (fileHeader.getZip64ExtendedInfo() != null &&
|
||||
fileHeader.getZip64ExtendedInfo().getOffsetLocalHeader() != -1) {
|
||||
offsetLocalFileHeader = fileHeader.getZip64ExtendedInfo().getOffsetLocalHeader();
|
||||
}
|
||||
|
||||
long offsetEndOfCompressedFile = -1;
|
||||
|
||||
long offsetStartCentralDir = zipModel.getEndCentralDirRecord().getOffsetOfStartOfCentralDir();
|
||||
if (zipModel.isZip64Format()) {
|
||||
if (zipModel.getZip64EndCentralDirRecord() != null) {
|
||||
offsetStartCentralDir = zipModel.getZip64EndCentralDirRecord().getOffsetStartCenDirWRTStartDiskNo();
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList fileHeaderList = zipModel.getCentralDirectory().getFileHeaders();
|
||||
|
||||
if (indexOfFileHeader == fileHeaderList.size() - 1) {
|
||||
offsetEndOfCompressedFile = offsetStartCentralDir - 1;
|
||||
} else {
|
||||
FileHeader nextFileHeader = (FileHeader)fileHeaderList.get(indexOfFileHeader + 1);
|
||||
if (nextFileHeader != null) {
|
||||
offsetEndOfCompressedFile = nextFileHeader.getOffsetLocalHeader() - 1;
|
||||
if (nextFileHeader.getZip64ExtendedInfo() != null &&
|
||||
nextFileHeader.getZip64ExtendedInfo().getOffsetLocalHeader() != -1) {
|
||||
offsetEndOfCompressedFile = nextFileHeader.getZip64ExtendedInfo().getOffsetLocalHeader() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (offsetLocalFileHeader < 0 || offsetEndOfCompressedFile < 0) {
|
||||
throw new ZipException("invalid offset for start and end of local file, cannot remove file");
|
||||
}
|
||||
|
||||
if(indexOfFileHeader == 0) {
|
||||
if (zipModel.getCentralDirectory().getFileHeaders().size() > 1) {
|
||||
// if this is the only file and it is deleted then no need to do this
|
||||
copyFile(inputStream, outputStream, offsetEndOfCompressedFile + 1, offsetStartCentralDir, progressMonitor);
|
||||
}
|
||||
} else if (indexOfFileHeader == fileHeaderList.size() - 1) {
|
||||
copyFile(inputStream, outputStream, 0, offsetLocalFileHeader, progressMonitor);
|
||||
} else {
|
||||
copyFile(inputStream, outputStream, 0, offsetLocalFileHeader, progressMonitor);
|
||||
copyFile(inputStream, outputStream, offsetEndOfCompressedFile + 1, offsetStartCentralDir, progressMonitor);
|
||||
}
|
||||
|
||||
if (progressMonitor.isCancelAllTasks()) {
|
||||
progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
|
||||
progressMonitor.setState(ProgressMonitor.STATE_READY);
|
||||
return null;
|
||||
}
|
||||
|
||||
zipModel.getEndCentralDirRecord().setOffsetOfStartOfCentralDir(((SplitOutputStream)outputStream).getFilePointer());
|
||||
zipModel.getEndCentralDirRecord().setTotNoOfEntriesInCentralDir(
|
||||
zipModel.getEndCentralDirRecord().getTotNoOfEntriesInCentralDir() - 1);
|
||||
zipModel.getEndCentralDirRecord().setTotNoOfEntriesInCentralDirOnThisDisk(
|
||||
zipModel.getEndCentralDirRecord().getTotNoOfEntriesInCentralDirOnThisDisk() - 1);
|
||||
|
||||
zipModel.getCentralDirectory().getFileHeaders().remove(indexOfFileHeader);
|
||||
|
||||
for (int i = indexOfFileHeader; i < zipModel.getCentralDirectory().getFileHeaders().size(); i++) {
|
||||
long offsetLocalHdr = ((FileHeader)zipModel.getCentralDirectory().getFileHeaders().get(i)).getOffsetLocalHeader();
|
||||
if (((FileHeader)zipModel.getCentralDirectory().getFileHeaders().get(i)).getZip64ExtendedInfo() != null &&
|
||||
((FileHeader)zipModel.getCentralDirectory().getFileHeaders().get(i)).getZip64ExtendedInfo().getOffsetLocalHeader() != -1) {
|
||||
offsetLocalHdr = ((FileHeader)zipModel.getCentralDirectory().getFileHeaders().get(i)).getZip64ExtendedInfo().getOffsetLocalHeader();
|
||||
}
|
||||
|
||||
((FileHeader)zipModel.getCentralDirectory().getFileHeaders().get(i)).setOffsetLocalHeader(
|
||||
offsetLocalHdr - (offsetEndOfCompressedFile - offsetLocalFileHeader) - 1);
|
||||
}
|
||||
|
||||
HeaderWriter headerWriter = new HeaderWriter();
|
||||
headerWriter.finalizeZipFile(zipModel, outputStream);
|
||||
|
||||
successFlag = true;
|
||||
|
||||
retMap.put(InternalZipConstants.OFFSET_CENTRAL_DIR,
|
||||
Long.toString(zipModel.getEndCentralDirRecord().getOffsetOfStartOfCentralDir()));
|
||||
|
||||
} catch (ZipException e) {
|
||||
progressMonitor.endProgressMonitorError(e);
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
progressMonitor.endProgressMonitorError(e);
|
||||
throw new ZipException(e);
|
||||
} finally {
|
||||
try {
|
||||
if (inputStream != null)
|
||||
inputStream.close();
|
||||
if (outputStream != null)
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
throw new ZipException("cannot close input stream or output stream when trying to delete a file from zip file");
|
||||
}
|
||||
|
||||
if (successFlag) {
|
||||
restoreFileName(zipFile, tmpZipFileName);
|
||||
} else {
|
||||
File newZipFile = new File(tmpZipFileName);
|
||||
newZipFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
return retMap;
|
||||
}
|
||||
|
||||
private void restoreFileName(File zipFile, String tmpZipFileName) throws ZipException {
|
||||
if (zipFile.delete())
|
||||
{
|
||||
File newZipFile = new File(tmpZipFileName);
|
||||
if (!newZipFile.renameTo(zipFile)) {
|
||||
throw new ZipException("cannot rename modified zip file");
|
||||
}
|
||||
} else {
|
||||
throw new ZipException("cannot delete old zip file");
|
||||
}
|
||||
}
|
||||
|
||||
private void copyFile(RandomAccessFile inputStream,
|
||||
OutputStream outputStream, long start, long end, ProgressMonitor progressMonitor) throws ZipException {
|
||||
|
||||
if (inputStream == null || outputStream == null) {
|
||||
throw new ZipException("input or output stream is null, cannot copy file");
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
throw new ZipException("starting offset is negative, cannot copy file");
|
||||
}
|
||||
|
||||
if (end < 0) {
|
||||
throw new ZipException("end offset is negative, cannot copy file");
|
||||
}
|
||||
|
||||
if (start > end) {
|
||||
throw new ZipException("start offset is greater than end offset, cannot copy file");
|
||||
}
|
||||
|
||||
if (start == end) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (progressMonitor.isCancelAllTasks()) {
|
||||
progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
|
||||
progressMonitor.setState(ProgressMonitor.STATE_READY);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
inputStream.seek(start);
|
||||
|
||||
int readLen = -2;
|
||||
byte[] buff;
|
||||
long bytesRead = 0;
|
||||
long bytesToRead = end - start;
|
||||
|
||||
if ((end - start) < InternalZipConstants.BUFF_SIZE) {
|
||||
buff = new byte[(int)(end - start)];
|
||||
} else {
|
||||
buff = new byte[InternalZipConstants.BUFF_SIZE];
|
||||
}
|
||||
|
||||
while ((readLen = inputStream.read(buff)) != -1) {
|
||||
outputStream.write(buff, 0, readLen);
|
||||
|
||||
progressMonitor.updateWorkCompleted(readLen);
|
||||
if (progressMonitor.isCancelAllTasks()) {
|
||||
progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
|
||||
return;
|
||||
}
|
||||
|
||||
bytesRead += readLen;
|
||||
|
||||
if(bytesRead == bytesToRead) {
|
||||
break;
|
||||
} else if (bytesRead + buff.length > bytesToRead) {
|
||||
buff = new byte[(int)(bytesToRead - bytesRead)];
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new ZipException(e);
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private RandomAccessFile createFileHandler(ZipModel zipModel, String mode) throws ZipException {
|
||||
if (zipModel == null || !Zip4jUtil.isStringNotNullAndNotEmpty(zipModel.getZipFile())) {
|
||||
throw new ZipException("input parameter is null in getFilePointer, cannot create file handler to remove file");
|
||||
}
|
||||
|
||||
try {
|
||||
return new RandomAccessFile(new File(zipModel.getZipFile()), mode);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges split Zip files into a single Zip file
|
||||
* @param zipModel
|
||||
* @throws ZipException
|
||||
*/
|
||||
public void mergeSplitZipFiles(final ZipModel zipModel, final File outputZipFile,
|
||||
final ProgressMonitor progressMonitor, boolean runInThread) throws ZipException {
|
||||
if (runInThread) {
|
||||
Thread thread = new Thread(InternalZipConstants.THREAD_NAME) {
|
||||
public void run() {
|
||||
try {
|
||||
initMergeSplitZipFile(zipModel, outputZipFile, progressMonitor);
|
||||
} catch (ZipException e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
thread.start();
|
||||
} else {
|
||||
initMergeSplitZipFile(zipModel, outputZipFile, progressMonitor);
|
||||
}
|
||||
}
|
||||
|
||||
private void initMergeSplitZipFile(ZipModel zipModel, File outputZipFile,
|
||||
ProgressMonitor progressMonitor) throws ZipException {
|
||||
if (zipModel == null) {
|
||||
ZipException e = new ZipException("one of the input parameters is null, cannot merge split zip file");
|
||||
progressMonitor.endProgressMonitorError(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (!zipModel.isSplitArchive()) {
|
||||
ZipException e = new ZipException("archive not a split zip file");
|
||||
progressMonitor.endProgressMonitorError(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
OutputStream outputStream = null;
|
||||
RandomAccessFile inputStream = null;
|
||||
ArrayList fileSizeList = new ArrayList();
|
||||
long totBytesWritten = 0;
|
||||
boolean splitSigRemoved = false;
|
||||
try {
|
||||
|
||||
int totNoOfSplitFiles = zipModel.getEndCentralDirRecord().getNoOfThisDisk();
|
||||
|
||||
if (totNoOfSplitFiles <= 0) {
|
||||
throw new ZipException("corrupt zip model, archive not a split zip file");
|
||||
}
|
||||
|
||||
outputStream = prepareOutputStreamForMerge(outputZipFile);
|
||||
for (int i = 0; i <= totNoOfSplitFiles; i++) {
|
||||
inputStream = createSplitZipFileHandler(zipModel, i);
|
||||
|
||||
int start = 0;
|
||||
Long end = new Long(inputStream.length());
|
||||
|
||||
if (i == 0) {
|
||||
if (zipModel.getCentralDirectory() != null &&
|
||||
zipModel.getCentralDirectory().getFileHeaders() != null &&
|
||||
zipModel.getCentralDirectory().getFileHeaders().size() > 0) {
|
||||
byte[] buff = new byte[4];
|
||||
inputStream.seek(0);
|
||||
inputStream.read(buff);
|
||||
if (Raw.readIntLittleEndian(buff, 0) == InternalZipConstants.SPLITSIG) {
|
||||
start = 4;
|
||||
splitSigRemoved = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == totNoOfSplitFiles) {
|
||||
end = new Long(zipModel.getEndCentralDirRecord().getOffsetOfStartOfCentralDir());
|
||||
}
|
||||
|
||||
copyFile(inputStream, outputStream, start, end.longValue(), progressMonitor);
|
||||
totBytesWritten += (end.longValue() - start);
|
||||
if (progressMonitor.isCancelAllTasks()) {
|
||||
progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
|
||||
progressMonitor.setState(ProgressMonitor.STATE_READY);
|
||||
return;
|
||||
}
|
||||
|
||||
fileSizeList.add(end);
|
||||
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
ZipModel newZipModel = (ZipModel)zipModel.clone();
|
||||
newZipModel.getEndCentralDirRecord().setOffsetOfStartOfCentralDir(totBytesWritten);
|
||||
|
||||
updateSplitZipModel(newZipModel, fileSizeList, splitSigRemoved);
|
||||
|
||||
HeaderWriter headerWriter = new HeaderWriter();
|
||||
headerWriter.finalizeZipFileWithoutValidations(newZipModel, outputStream);
|
||||
|
||||
progressMonitor.endProgressMonitorSuccess();
|
||||
|
||||
} catch (IOException e) {
|
||||
progressMonitor.endProgressMonitorError(e);
|
||||
throw new ZipException(e);
|
||||
} catch (Exception e) {
|
||||
progressMonitor.endProgressMonitorError(e);
|
||||
throw new ZipException(e);
|
||||
} finally {
|
||||
if (outputStream != null) {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an input stream for the split part of the zip file
|
||||
* @return Zip4jInputStream
|
||||
* @throws ZipException
|
||||
*/
|
||||
|
||||
private RandomAccessFile createSplitZipFileHandler(ZipModel zipModel, int partNumber) throws ZipException {
|
||||
if (zipModel == null) {
|
||||
throw new ZipException("zip model is null, cannot create split file handler");
|
||||
}
|
||||
|
||||
if (partNumber < 0) {
|
||||
throw new ZipException("invlaid part number, cannot create split file handler");
|
||||
}
|
||||
|
||||
try {
|
||||
String curZipFile = zipModel.getZipFile();
|
||||
String partFile = null;
|
||||
if (partNumber == zipModel.getEndCentralDirRecord().getNoOfThisDisk()) {
|
||||
partFile = zipModel.getZipFile();
|
||||
} else {
|
||||
if (partNumber >= 9) {
|
||||
partFile = curZipFile.substring(0, curZipFile.lastIndexOf(".")) + ".z" + (partNumber+ 1);
|
||||
} else{
|
||||
partFile = curZipFile.substring(0, curZipFile.lastIndexOf(".")) + ".z0" + (partNumber+ 1);
|
||||
}
|
||||
}
|
||||
File tmpFile = new File(partFile);
|
||||
|
||||
if (!Zip4jUtil.checkFileExists(tmpFile)) {
|
||||
throw new ZipException("split file does not exist: " + partFile);
|
||||
}
|
||||
|
||||
return new RandomAccessFile(tmpFile, InternalZipConstants.READ_MODE);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new ZipException(e);
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private OutputStream prepareOutputStreamForMerge(File outFile) throws ZipException {
|
||||
if (outFile == null) {
|
||||
throw new ZipException("outFile is null, cannot create outputstream");
|
||||
}
|
||||
|
||||
try {
|
||||
return new FileOutputStream(outFile);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new ZipException(e);
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSplitZipModel(ZipModel zipModel, ArrayList fileSizeList, boolean splitSigRemoved) throws ZipException {
|
||||
if (zipModel == null) {
|
||||
throw new ZipException("zip model is null, cannot update split zip model");
|
||||
}
|
||||
|
||||
zipModel.setSplitArchive(false);
|
||||
updateSplitFileHeader(zipModel, fileSizeList, splitSigRemoved);
|
||||
updateSplitEndCentralDirectory(zipModel);
|
||||
if (zipModel.isZip64Format()) {
|
||||
updateSplitZip64EndCentralDirLocator(zipModel, fileSizeList);
|
||||
updateSplitZip64EndCentralDirRec(zipModel, fileSizeList);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSplitFileHeader(ZipModel zipModel, ArrayList fileSizeList, boolean splitSigRemoved) throws ZipException {
|
||||
try {
|
||||
|
||||
if (zipModel.getCentralDirectory()== null) {
|
||||
throw new ZipException("corrupt zip model - getCentralDirectory, cannot update split zip model");
|
||||
}
|
||||
|
||||
int fileHeaderCount = zipModel.getCentralDirectory().getFileHeaders().size();
|
||||
int splitSigOverhead = 0;
|
||||
if (splitSigRemoved)
|
||||
splitSigOverhead = 4;
|
||||
|
||||
for (int i = 0; i < fileHeaderCount; i++) {
|
||||
long offsetLHToAdd = 0;
|
||||
|
||||
for (int j = 0; j < ((FileHeader)zipModel.getCentralDirectory().getFileHeaders().get(i)).getDiskNumberStart(); j++) {
|
||||
offsetLHToAdd += ((Long)fileSizeList.get(j)).longValue();
|
||||
}
|
||||
((FileHeader)zipModel.getCentralDirectory().getFileHeaders().get(i)).setOffsetLocalHeader(
|
||||
((FileHeader)zipModel.getCentralDirectory().getFileHeaders().get(i)).getOffsetLocalHeader() +
|
||||
offsetLHToAdd - splitSigOverhead);
|
||||
((FileHeader)zipModel.getCentralDirectory().getFileHeaders().get(i)).setDiskNumberStart(0);
|
||||
}
|
||||
|
||||
} catch (ZipException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSplitEndCentralDirectory(ZipModel zipModel) throws ZipException {
|
||||
try {
|
||||
if (zipModel == null) {
|
||||
throw new ZipException("zip model is null - cannot update end of central directory for split zip model");
|
||||
}
|
||||
|
||||
if (zipModel.getCentralDirectory()== null) {
|
||||
throw new ZipException("corrupt zip model - getCentralDirectory, cannot update split zip model");
|
||||
}
|
||||
|
||||
zipModel.getEndCentralDirRecord().setNoOfThisDisk(0);
|
||||
zipModel.getEndCentralDirRecord().setNoOfThisDiskStartOfCentralDir(0);
|
||||
zipModel.getEndCentralDirRecord().setTotNoOfEntriesInCentralDir(
|
||||
zipModel.getCentralDirectory().getFileHeaders().size());
|
||||
zipModel.getEndCentralDirRecord().setTotNoOfEntriesInCentralDirOnThisDisk(
|
||||
zipModel.getCentralDirectory().getFileHeaders().size());
|
||||
|
||||
} catch (ZipException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSplitZip64EndCentralDirLocator(ZipModel zipModel, ArrayList fileSizeList) throws ZipException {
|
||||
if (zipModel == null) {
|
||||
throw new ZipException("zip model is null, cannot update split Zip64 end of central directory locator");
|
||||
}
|
||||
|
||||
if (zipModel.getZip64EndCentralDirLocator() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
zipModel.getZip64EndCentralDirLocator().setNoOfDiskStartOfZip64EndOfCentralDirRec(0);
|
||||
long offsetZip64EndCentralDirRec = 0;
|
||||
|
||||
for (int i = 0; i < fileSizeList.size(); i++) {
|
||||
offsetZip64EndCentralDirRec += ((Long)fileSizeList.get(i)).longValue();
|
||||
}
|
||||
zipModel.getZip64EndCentralDirLocator().setOffsetZip64EndOfCentralDirRec(
|
||||
((Zip64EndCentralDirLocator)zipModel.getZip64EndCentralDirLocator()).getOffsetZip64EndOfCentralDirRec() +
|
||||
offsetZip64EndCentralDirRec);
|
||||
zipModel.getZip64EndCentralDirLocator().setTotNumberOfDiscs(1);
|
||||
}
|
||||
|
||||
private void updateSplitZip64EndCentralDirRec(ZipModel zipModel, ArrayList fileSizeList) throws ZipException {
|
||||
if (zipModel == null) {
|
||||
throw new ZipException("zip model is null, cannot update split Zip64 end of central directory record");
|
||||
}
|
||||
|
||||
if (zipModel.getZip64EndCentralDirRecord() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
zipModel.getZip64EndCentralDirRecord().setNoOfThisDisk(0);
|
||||
zipModel.getZip64EndCentralDirRecord().setNoOfThisDiskStartOfCentralDir(0);
|
||||
zipModel.getZip64EndCentralDirRecord().setTotNoOfEntriesInCentralDirOnThisDisk(
|
||||
zipModel.getEndCentralDirRecord().getTotNoOfEntriesInCentralDir());
|
||||
|
||||
long offsetStartCenDirWRTStartDiskNo = 0;
|
||||
|
||||
for (int i = 0; i < fileSizeList.size(); i++) {
|
||||
offsetStartCenDirWRTStartDiskNo += ((Long)fileSizeList.get(i)).longValue();
|
||||
}
|
||||
|
||||
zipModel.getZip64EndCentralDirRecord().setOffsetStartCenDirWRTStartDiskNo(
|
||||
((Zip64EndCentralDirRecord)zipModel.getZip64EndCentralDirRecord()).getOffsetStartCenDirWRTStartDiskNo() +
|
||||
offsetStartCenDirWRTStartDiskNo);
|
||||
}
|
||||
|
||||
public void setComment(ZipModel zipModel, String comment) throws ZipException {
|
||||
if (comment == null) {
|
||||
throw new ZipException("comment is null, cannot update Zip file with comment");
|
||||
}
|
||||
|
||||
if (zipModel == null) {
|
||||
throw new ZipException("zipModel is null, cannot update Zip file with comment");
|
||||
}
|
||||
|
||||
String encodedComment = comment;
|
||||
byte[] commentBytes = comment.getBytes();
|
||||
int commentLength = comment.length();
|
||||
|
||||
if (Zip4jUtil.isSupportedCharset(InternalZipConstants.CHARSET_COMMENTS_DEFAULT)) {
|
||||
try {
|
||||
encodedComment = new String(comment.getBytes(InternalZipConstants.CHARSET_COMMENTS_DEFAULT), InternalZipConstants.CHARSET_COMMENTS_DEFAULT);
|
||||
commentBytes = encodedComment.getBytes(InternalZipConstants.CHARSET_COMMENTS_DEFAULT);
|
||||
commentLength = encodedComment.length();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
encodedComment = comment;
|
||||
commentBytes = comment.getBytes();
|
||||
commentLength = comment.length();
|
||||
}
|
||||
}
|
||||
|
||||
if (commentLength > InternalZipConstants.MAX_ALLOWED_ZIP_COMMENT_LENGTH) {
|
||||
throw new ZipException("comment length exceeds maximum length");
|
||||
}
|
||||
|
||||
zipModel.getEndCentralDirRecord().setComment(encodedComment);
|
||||
zipModel.getEndCentralDirRecord().setCommentBytes(commentBytes);
|
||||
zipModel.getEndCentralDirRecord().setCommentLength(commentLength);
|
||||
|
||||
SplitOutputStream outputStream = null;
|
||||
|
||||
try {
|
||||
HeaderWriter headerWriter = new HeaderWriter();
|
||||
outputStream = new SplitOutputStream(zipModel.getZipFile());
|
||||
|
||||
if (zipModel.isZip64Format()) {
|
||||
outputStream.seek(zipModel.getZip64EndCentralDirRecord().getOffsetStartCenDirWRTStartDiskNo());
|
||||
} else {
|
||||
outputStream.seek(zipModel.getEndCentralDirRecord().getOffsetOfStartOfCentralDir());
|
||||
}
|
||||
|
||||
headerWriter.finalizeZipFileWithoutValidations(zipModel, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void initProgressMonitorForRemoveOp(ZipModel zipModel,
|
||||
FileHeader fileHeader, ProgressMonitor progressMonitor) throws ZipException {
|
||||
if (zipModel == null || fileHeader == null || progressMonitor == null) {
|
||||
throw new ZipException("one of the input parameters is null, cannot calculate total work");
|
||||
}
|
||||
|
||||
progressMonitor.setCurrentOperation(ProgressMonitor.OPERATION_REMOVE);
|
||||
progressMonitor.setFileName(fileHeader.getFileName());
|
||||
progressMonitor.setTotalWork(calculateTotalWorkForRemoveOp(zipModel, fileHeader));
|
||||
progressMonitor.setState(ProgressMonitor.STATE_BUSY);
|
||||
}
|
||||
|
||||
private long calculateTotalWorkForRemoveOp(ZipModel zipModel, FileHeader fileHeader) throws ZipException {
|
||||
return Zip4jUtil.getFileLengh(new File(zipModel.getZipFile())) - fileHeader.getCompressedSize();
|
||||
}
|
||||
|
||||
public void initProgressMonitorForMergeOp(ZipModel zipModel, ProgressMonitor progressMonitor) throws ZipException {
|
||||
if (zipModel == null) {
|
||||
throw new ZipException("zip model is null, cannot calculate total work for merge op");
|
||||
}
|
||||
|
||||
progressMonitor.setCurrentOperation(ProgressMonitor.OPERATION_MERGE);
|
||||
progressMonitor.setFileName(zipModel.getZipFile());
|
||||
progressMonitor.setTotalWork(calculateTotalWorkForMergeOp(zipModel));
|
||||
progressMonitor.setState(ProgressMonitor.STATE_BUSY);
|
||||
}
|
||||
|
||||
private long calculateTotalWorkForMergeOp(ZipModel zipModel) throws ZipException {
|
||||
long totSize = 0;
|
||||
if (zipModel.isSplitArchive()) {
|
||||
int totNoOfSplitFiles = zipModel.getEndCentralDirRecord().getNoOfThisDisk();
|
||||
String partFile = null;
|
||||
String curZipFile = zipModel.getZipFile();
|
||||
int partNumber = 0;
|
||||
for (int i = 0; i <= totNoOfSplitFiles; i++) {
|
||||
if (partNumber == zipModel.getEndCentralDirRecord().getNoOfThisDisk()) {
|
||||
partFile = zipModel.getZipFile();
|
||||
} else {
|
||||
if (partNumber >= 9) {
|
||||
partFile = curZipFile.substring(0, curZipFile.lastIndexOf(".")) + ".z" + (partNumber+ 1);
|
||||
} else{
|
||||
partFile = curZipFile.substring(0, curZipFile.lastIndexOf(".")) + ".z0" + (partNumber+ 1);
|
||||
}
|
||||
}
|
||||
|
||||
totSize += Zip4jUtil.getFileLengh(new File(partFile));
|
||||
}
|
||||
|
||||
}
|
||||
return totSize;
|
||||
}
|
||||
}
|
85
src/net/lingala/zip4j/util/CRCUtil.java
Normal file
85
src/net/lingala/zip4j/util/CRCUtil.java
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.progress.ProgressMonitor;
|
||||
|
||||
public class CRCUtil {
|
||||
|
||||
private static final int BUF_SIZE = 1 << 14; //16384
|
||||
|
||||
public static long computeFileCRC(String inputFile) throws ZipException {
|
||||
return computeFileCRC(inputFile, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates CRC of a file
|
||||
* @param inputFile - file for which crc has to be calculated
|
||||
* @return crc of the file
|
||||
* @throws ZipException
|
||||
*/
|
||||
public static long computeFileCRC(String inputFile, ProgressMonitor progressMonitor) throws ZipException {
|
||||
|
||||
if (!Zip4jUtil.isStringNotNullAndNotEmpty(inputFile)) {
|
||||
throw new ZipException("input file is null or empty, cannot calculate CRC for the file");
|
||||
}
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
Zip4jUtil.checkFileReadAccess(inputFile);
|
||||
|
||||
inputStream = new FileInputStream(new File(inputFile));
|
||||
|
||||
byte[] buff = new byte[BUF_SIZE];
|
||||
int readLen = -2;
|
||||
CRC32 crc32 = new CRC32();
|
||||
|
||||
while ((readLen = inputStream.read(buff)) != -1) {
|
||||
crc32.update(buff, 0, readLen);
|
||||
if (progressMonitor != null) {
|
||||
progressMonitor.updateWorkCompleted(readLen);
|
||||
if (progressMonitor.isCancelAllTasks()) {
|
||||
progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
|
||||
progressMonitor.setState(ProgressMonitor.STATE_READY);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return crc32.getValue();
|
||||
} catch (IOException e) {
|
||||
throw new ZipException(e);
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
throw new ZipException("error while closing the file after calculating crc");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
174
src/net/lingala/zip4j/util/InternalZipConstants.java
Normal file
174
src/net/lingala/zip4j/util/InternalZipConstants.java
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.util;
|
||||
|
||||
public interface InternalZipConstants {
|
||||
|
||||
/*
|
||||
* Header signatures
|
||||
*/
|
||||
// Whenever a new Signature is added here, make sure to add it
|
||||
// in Zip4jUtil.getAllHeaderSignatures()
|
||||
static long LOCSIG = 0x04034b50L; // "PK\003\004"
|
||||
static long EXTSIG = 0x08074b50L; // "PK\007\008"
|
||||
static long CENSIG = 0x02014b50L; // "PK\001\002"
|
||||
static long ENDSIG = 0x06054b50L; // "PK\005\006"
|
||||
static long DIGSIG = 0x05054b50L;
|
||||
static long ARCEXTDATREC = 0x08064b50L;
|
||||
static long SPLITSIG = 0x08074b50L;
|
||||
static long ZIP64ENDCENDIRLOC = 0x07064b50L;
|
||||
static long ZIP64ENDCENDIRREC = 0x06064b50;
|
||||
static int EXTRAFIELDZIP64LENGTH = 0x0001;
|
||||
static int AESSIG = 0x9901;
|
||||
|
||||
/*
|
||||
* Header sizes in bytes (including signatures)
|
||||
*/
|
||||
static final int LOCHDR = 30; // LOC header size
|
||||
static final int EXTHDR = 16; // EXT header size
|
||||
static final int CENHDR = 46; // CEN header size
|
||||
static final int ENDHDR = 22; // END header size
|
||||
|
||||
/*
|
||||
* Local file (LOC) header field offsets
|
||||
*/
|
||||
static final int LOCVER = 4; // version needed to extract
|
||||
static final int LOCFLG = 6; // general purpose bit flag
|
||||
static final int LOCHOW = 8; // compression method
|
||||
static final int LOCTIM = 10; // modification time
|
||||
static final int LOCCRC = 14; // uncompressed file crc-32 value
|
||||
static final int LOCSIZ = 18; // compressed size
|
||||
static final int LOCLEN = 22; // uncompressed size
|
||||
static final int LOCNAM = 26; // filename length
|
||||
static final int LOCEXT = 28; // extra field length
|
||||
|
||||
/*
|
||||
* Extra local (EXT) header field offsets
|
||||
*/
|
||||
static final int EXTCRC = 4; // uncompressed file crc-32 value
|
||||
static final int EXTSIZ = 8; // compressed size
|
||||
static final int EXTLEN = 12; // uncompressed size
|
||||
|
||||
/*
|
||||
* Central directory (CEN) header field offsets
|
||||
*/
|
||||
static final int CENVEM = 4; // version made by
|
||||
static final int CENVER = 6; // version needed to extract
|
||||
static final int CENFLG = 8; // encrypt, decrypt flags
|
||||
static final int CENHOW = 10; // compression method
|
||||
static final int CENTIM = 12; // modification time
|
||||
static final int CENCRC = 16; // uncompressed file crc-32 value
|
||||
static final int CENSIZ = 20; // compressed size
|
||||
static final int CENLEN = 24; // uncompressed size
|
||||
static final int CENNAM = 28; // filename length
|
||||
static final int CENEXT = 30; // extra field length
|
||||
static final int CENCOM = 32; // comment length
|
||||
static final int CENDSK = 34; // disk number start
|
||||
static final int CENATT = 36; // internal file attributes
|
||||
static final int CENATX = 38; // external file attributes
|
||||
static final int CENOFF = 42; // LOC header offset
|
||||
|
||||
/*
|
||||
* End of central directory (END) header field offsets
|
||||
*/
|
||||
static final int ENDSUB = 8; // number of entries on this disk
|
||||
static final int ENDTOT = 10; // total number of entries
|
||||
static final int ENDSIZ = 12; // central directory size in bytes
|
||||
static final int ENDOFF = 16; // offset of first CEN header
|
||||
static final int ENDCOM = 20; // zip file comment length
|
||||
|
||||
static final int STD_DEC_HDR_SIZE = 12;
|
||||
|
||||
//AES Constants
|
||||
static final int AES_AUTH_LENGTH = 10;
|
||||
static final int AES_BLOCK_SIZE = 16;
|
||||
|
||||
static final int MIN_SPLIT_LENGTH = 65536;
|
||||
|
||||
static final long ZIP_64_LIMIT = 4294967295L;
|
||||
|
||||
public static String OFFSET_CENTRAL_DIR = "offsetCentralDir";
|
||||
|
||||
public static final String VERSION = "1.3.2";
|
||||
|
||||
public static final int MODE_ZIP = 1;
|
||||
|
||||
public static final int MODE_UNZIP = 2;
|
||||
|
||||
public static final String WRITE_MODE = "rw";
|
||||
|
||||
public static final String READ_MODE = "r";
|
||||
|
||||
public static final int BUFF_SIZE = 1024 * 4;
|
||||
|
||||
public static final int FILE_MODE_NONE = 0;
|
||||
|
||||
public static final int FILE_MODE_READ_ONLY = 1;
|
||||
|
||||
public static final int FILE_MODE_HIDDEN = 2;
|
||||
|
||||
public static final int FILE_MODE_ARCHIVE = 32;
|
||||
|
||||
public static final int FILE_MODE_READ_ONLY_HIDDEN = 3;
|
||||
|
||||
public static final int FILE_MODE_READ_ONLY_ARCHIVE = 33;
|
||||
|
||||
public static final int FILE_MODE_HIDDEN_ARCHIVE = 34;
|
||||
|
||||
public static final int FILE_MODE_READ_ONLY_HIDDEN_ARCHIVE = 35;
|
||||
|
||||
public static final int FILE_MODE_SYSTEM = 38;
|
||||
|
||||
public static final int FOLDER_MODE_NONE = 16;
|
||||
|
||||
public static final int FOLDER_MODE_HIDDEN = 18;
|
||||
|
||||
public static final int FOLDER_MODE_ARCHIVE = 48;
|
||||
|
||||
public static final int FOLDER_MODE_HIDDEN_ARCHIVE = 50;
|
||||
|
||||
// Update local file header constants
|
||||
// This value holds the number of bytes to skip from
|
||||
// the offset of start of local header
|
||||
public static final int UPDATE_LFH_CRC = 14;
|
||||
|
||||
public static final int UPDATE_LFH_COMP_SIZE = 18;
|
||||
|
||||
public static final int UPDATE_LFH_UNCOMP_SIZE = 22;
|
||||
|
||||
public static final int LIST_TYPE_FILE = 1;
|
||||
|
||||
public static final int LIST_TYPE_STRING = 2;
|
||||
|
||||
public static final int UFT8_NAMES_FLAG = 1 << 11;
|
||||
|
||||
public static final String CHARSET_UTF8 = "UTF8";
|
||||
|
||||
public static final String CHARSET_CP850 = "Cp850";
|
||||
|
||||
public static final String CHARSET_COMMENTS_DEFAULT = "windows-1254";
|
||||
|
||||
public static final String CHARSET_DEFAULT = System.getProperty("file.encoding");
|
||||
|
||||
public static final String FILE_SEPARATOR = System.getProperty("file.separator");
|
||||
|
||||
public static final String ZIP_FILE_SEPARATOR = "/";
|
||||
|
||||
public static final String THREAD_NAME = "Zip4j";
|
||||
|
||||
public static final int MAX_ALLOWED_ZIP_COMMENT_LENGTH = 0xFFFF;
|
||||
}
|
184
src/net/lingala/zip4j/util/Raw.java
Normal file
184
src/net/lingala/zip4j/util/Raw.java
Normal file
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.util;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
|
||||
public class Raw
|
||||
{
|
||||
public static long readLongLittleEndian(byte[] array,int pos){
|
||||
long temp = 0;
|
||||
temp |= array[pos+7]&0xff;
|
||||
temp <<=8;
|
||||
temp |= array[pos+6]&0xff;
|
||||
temp <<=8;
|
||||
temp |= array[pos+5]&0xff;
|
||||
temp <<=8;
|
||||
temp |= array[pos+4]&0xff;
|
||||
temp <<=8;
|
||||
temp |= array[pos+3]&0xff;
|
||||
temp <<=8;
|
||||
temp |= array[pos+2]&0xff;
|
||||
temp <<=8;
|
||||
temp |= array[pos+1]&0xff;
|
||||
temp <<=8;
|
||||
temp |= array[pos]&0xff;
|
||||
return temp;
|
||||
}
|
||||
|
||||
public static int readLeInt(DataInput di, byte[] b) throws ZipException{
|
||||
try {
|
||||
di.readFully(b, 0, 4);
|
||||
} catch (IOException e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
return ((b[0] & 0xff) | (b[1] & 0xff) << 8)
|
||||
| ((b[2] & 0xff) | (b[3] & 0xff) << 8) << 16;
|
||||
}
|
||||
|
||||
public static int readShortLittleEndian(byte[] b, int off){
|
||||
return (b[off] & 0xff) | (b[off+1] & 0xff) << 8;
|
||||
}
|
||||
|
||||
public static final short readShortBigEndian(byte[] array, int pos) {
|
||||
short temp = 0;
|
||||
temp |= array[pos] & 0xff;
|
||||
temp <<= 8;
|
||||
temp |= array[pos + 1] & 0xff;
|
||||
return temp;
|
||||
}
|
||||
|
||||
public static int readIntLittleEndian(byte[] b, int off){
|
||||
return ((b[off] & 0xff) | (b[off+1] & 0xff) << 8)
|
||||
| ((b[off+2] & 0xff) | (b[off+3] & 0xff) << 8) << 16;
|
||||
}
|
||||
|
||||
public static byte[] toByteArray(int in,int outSize) {
|
||||
byte[] out = new byte[outSize];
|
||||
byte[] intArray = toByteArray(in);
|
||||
for( int i=0; i<intArray.length && i<outSize; i++ ) {
|
||||
out[i] = intArray[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static byte[] toByteArray(int in) {
|
||||
byte[] out = new byte[4];
|
||||
|
||||
out[0] = (byte)in;
|
||||
out[1] = (byte)(in >> 8);
|
||||
out[2] = (byte)(in >> 16);
|
||||
out[3] = (byte)(in >> 24);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public static final void writeShortLittleEndian(byte[] array, int pos,
|
||||
short value) {
|
||||
array[pos +1] = (byte) (value >>> 8);
|
||||
array[pos ] = (byte) (value & 0xFF);
|
||||
|
||||
}
|
||||
|
||||
public static final void writeIntLittleEndian(byte[] array, int pos,int value) {
|
||||
array[pos+3] = (byte) (value >>>24);
|
||||
array[pos+2] = (byte) (value >>>16);
|
||||
array[pos+1] = (byte) (value >>>8);
|
||||
array[pos] = (byte) (value &0xFF);
|
||||
|
||||
}
|
||||
|
||||
public static void writeLongLittleEndian(byte[] array, int pos, long value){
|
||||
array[pos+7] = (byte) (value >>>56);
|
||||
array[pos+6] = (byte) (value >>>48);
|
||||
array[pos+5] = (byte) (value >>>40);
|
||||
array[pos+4] = (byte) (value >>>32);
|
||||
array[pos+3] = (byte) (value >>>24);
|
||||
array[pos+2] = (byte) (value >>>16);
|
||||
array[pos+1] = (byte) (value >>>8);
|
||||
array[pos] = (byte) (value &0xFF);
|
||||
}
|
||||
|
||||
public static byte bitArrayToByte(int[] bitArray) throws ZipException {
|
||||
if (bitArray == null) {
|
||||
throw new ZipException("bit array is null, cannot calculate byte from bits");
|
||||
}
|
||||
|
||||
if (bitArray.length != 8) {
|
||||
throw new ZipException("invalid bit array length, cannot calculate byte");
|
||||
}
|
||||
|
||||
if(!checkBits(bitArray)) {
|
||||
throw new ZipException("invalid bits provided, bits contain other values than 0 or 1");
|
||||
}
|
||||
|
||||
int retNum = 0;
|
||||
for (int i = 0; i < bitArray.length; i++) {
|
||||
retNum += Math.pow(2, i) * bitArray[i];
|
||||
}
|
||||
|
||||
return (byte)retNum;
|
||||
}
|
||||
|
||||
private static boolean checkBits(int[] bitArray) {
|
||||
for (int i = 0; i < bitArray.length; i++) {
|
||||
if (bitArray[i] != 0 && bitArray[i] != 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void prepareBuffAESIVBytes(byte[] buff, int nonce, int length) {
|
||||
buff[0] = (byte)nonce;
|
||||
buff[1] = (byte)(nonce >> 8);
|
||||
buff[2] = (byte)(nonce >> 16);
|
||||
buff[3] = (byte)(nonce >> 24);
|
||||
buff[4] = 0;
|
||||
buff[5] = 0;
|
||||
buff[6] = 0;
|
||||
buff[7] = 0;
|
||||
buff[8] = 0;
|
||||
buff[9] = 0;
|
||||
buff[10] = 0;
|
||||
buff[11] = 0;
|
||||
buff[12] = 0;
|
||||
buff[13] = 0;
|
||||
buff[14] = 0;
|
||||
buff[15] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a char array to byte array
|
||||
* @param charArray
|
||||
* @return byte array representation of the input char array
|
||||
*/
|
||||
public static byte[] convertCharArrayToByteArray(char[] charArray) {
|
||||
if (charArray == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[charArray.length];
|
||||
for(int i=0;i<charArray.length;i++) {
|
||||
bytes[i] = (byte) charArray[i];
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
57
src/net/lingala/zip4j/util/Zip4jConstants.java
Normal file
57
src/net/lingala/zip4j/util/Zip4jConstants.java
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.util;
|
||||
|
||||
public interface Zip4jConstants {
|
||||
|
||||
// Compression Types
|
||||
static final int COMP_STORE = 0;
|
||||
// static final int COMP_FILE_SHRUNK = 1;
|
||||
// static final int COMP_FILE_RED_COMP_FACTOR_1 = 2;
|
||||
// static final int COMP_FILE_RED_COMP_FACTOR_2 = 3;
|
||||
// static final int COMP_FILE_RED_COMP_FACTOR_3 = 4;
|
||||
// static final int COMP_FILE_RED_COMP_FACTOR_4 = 5;
|
||||
// static final int COMP_FILE_IMPLODED = 6;
|
||||
static final int COMP_DEFLATE = 8;
|
||||
// static final int COMP_FILE_ENHANCED_DEFLATED = 9;
|
||||
// static final int COMP_PKWARE_DATA_COMP_LIB_IMPL = 10;
|
||||
// static final int COMP_BZIP2 = 12;
|
||||
// static final int COMP_LZMA = 14;
|
||||
// static final int COMP_IBM_TERSE = 18;
|
||||
// static final int COMP_IBM_LZ77 =19;
|
||||
// static final int COMP_WAVPACK = 97;
|
||||
// static final int COMP_PPMD = 98;
|
||||
static final int COMP_AES_ENC = 99;
|
||||
|
||||
//Compression level for deflate algorithm
|
||||
static final int DEFLATE_LEVEL_FASTEST = 1;
|
||||
static final int DEFLATE_LEVEL_FAST = 3;
|
||||
static final int DEFLATE_LEVEL_NORMAL = 5;
|
||||
static final int DEFLATE_LEVEL_MAXIMUM = 7;
|
||||
static final int DEFLATE_LEVEL_ULTRA = 9;
|
||||
|
||||
//Encryption types
|
||||
static final int ENC_NO_ENCRYPTION = -1;
|
||||
static final int ENC_METHOD_STANDARD = 0;
|
||||
// static final int ENC_METHOD_STRONG = 1;
|
||||
static final int ENC_METHOD_AES = 99;
|
||||
|
||||
//AES Key Strength
|
||||
static final int AES_STRENGTH_128 = 0x01;
|
||||
static final int AES_STRENGTH_192 = 0x02;
|
||||
static final int AES_STRENGTH_256 = 0x03;
|
||||
}
|
773
src/net/lingala/zip4j/util/Zip4jUtil.java
Normal file
773
src/net/lingala/zip4j/util/Zip4jUtil.java
Normal file
|
@ -0,0 +1,773 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.model.FileHeader;
|
||||
import net.lingala.zip4j.model.ZipModel;
|
||||
|
||||
public class Zip4jUtil {
|
||||
|
||||
public static boolean isStringNotNullAndNotEmpty(String str) {
|
||||
if (str == null || str.trim().length() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean checkOutputFolder(String path) throws ZipException {
|
||||
if (!isStringNotNullAndNotEmpty(path)) {
|
||||
throw new ZipException(new NullPointerException("output path is null"));
|
||||
}
|
||||
|
||||
File file = new File(path);
|
||||
|
||||
if (file.exists()) {
|
||||
|
||||
if (!file.isDirectory()) {
|
||||
throw new ZipException("output folder is not valid");
|
||||
}
|
||||
|
||||
if (!file.canWrite()) {
|
||||
throw new ZipException("no write access to output folder");
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
file.mkdirs();
|
||||
if (!file.isDirectory()) {
|
||||
throw new ZipException("output folder is not valid");
|
||||
}
|
||||
|
||||
if (!file.canWrite()) {
|
||||
throw new ZipException("no write access to destination folder");
|
||||
}
|
||||
|
||||
// SecurityManager manager = new SecurityManager();
|
||||
// try {
|
||||
// manager.checkWrite(file.getAbsolutePath());
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// throw new ZipException("no write access to destination folder");
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
throw new ZipException("Cannot create destination folder");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean checkFileReadAccess(String path) throws ZipException {
|
||||
if (!isStringNotNullAndNotEmpty(path)) {
|
||||
throw new ZipException("path is null");
|
||||
}
|
||||
|
||||
if (!checkFileExists(path)) {
|
||||
throw new ZipException("file does not exist: " + path);
|
||||
}
|
||||
|
||||
try {
|
||||
File file = new File(path);
|
||||
return file.canRead();
|
||||
} catch (Exception e) {
|
||||
throw new ZipException("cannot read zip file");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkFileWriteAccess(String path) throws ZipException {
|
||||
if (!isStringNotNullAndNotEmpty(path)) {
|
||||
throw new ZipException("path is null");
|
||||
}
|
||||
|
||||
if (!checkFileExists(path)) {
|
||||
throw new ZipException("file does not exist: " + path);
|
||||
}
|
||||
|
||||
try {
|
||||
File file = new File(path);
|
||||
return file.canWrite();
|
||||
} catch (Exception e) {
|
||||
throw new ZipException("cannot read zip file");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkFileExists(String path) throws ZipException {
|
||||
if (!isStringNotNullAndNotEmpty(path)) {
|
||||
throw new ZipException("path is null");
|
||||
}
|
||||
|
||||
File file = new File(path);
|
||||
return checkFileExists(file);
|
||||
}
|
||||
|
||||
public static boolean checkFileExists(File file) throws ZipException {
|
||||
if (file == null) {
|
||||
throw new ZipException("cannot check if file exists: input file is null");
|
||||
}
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
public static boolean isWindows(){
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
return (os.indexOf( "win" ) >= 0);
|
||||
}
|
||||
|
||||
public 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();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setFileHidden(File file) throws ZipException {
|
||||
// if (file == null) {
|
||||
// throw new ZipException("input file is null. cannot set hidden file attribute");
|
||||
// }
|
||||
//
|
||||
// if (!isWindows()) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (file.exists()) {
|
||||
// try {
|
||||
// Runtime.getRuntime().exec("attrib +H \"" + file.getAbsolutePath() + "\"");
|
||||
// } catch (IOException e) {
|
||||
// // do nothing as this is not of a higher priority
|
||||
// // add log statements here when logging is done
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
public static void setFileArchive(File file) throws ZipException {
|
||||
// if (file == null) {
|
||||
// throw new ZipException("input file is null. cannot set archive file attribute");
|
||||
// }
|
||||
//
|
||||
// if (!isWindows()) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (file.exists()) {
|
||||
// try {
|
||||
// if (file.isDirectory()) {
|
||||
// Runtime.getRuntime().exec("attrib +A \"" + file.getAbsolutePath() + "\"");
|
||||
// } else {
|
||||
// Runtime.getRuntime().exec("attrib +A \"" + file.getAbsolutePath() + "\"");
|
||||
// }
|
||||
//
|
||||
// } catch (IOException e) {
|
||||
// // do nothing as this is not of a higher priority
|
||||
// // add log statements here when logging is done
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
public static void setFileSystemMode(File file) throws ZipException {
|
||||
// if (file == null) {
|
||||
// throw new ZipException("input file is null. cannot set archive file attribute");
|
||||
// }
|
||||
//
|
||||
// if (!isWindows()) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (file.exists()) {
|
||||
// try {
|
||||
// Runtime.getRuntime().exec("attrib +S \"" + file.getAbsolutePath() + "\"");
|
||||
// } catch (IOException e) {
|
||||
// // do nothing as this is not of a higher priority
|
||||
// // add log statements here when logging is done
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
public static long getLastModifiedFileTime(File file, TimeZone timeZone) throws ZipException {
|
||||
if (file == null) {
|
||||
throw new ZipException("input file is null, cannot read last modified file time");
|
||||
}
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new ZipException("input file does not exist, cannot read last modified file time");
|
||||
}
|
||||
|
||||
return file.lastModified();
|
||||
}
|
||||
|
||||
public 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();
|
||||
}
|
||||
|
||||
public static long getFileLengh(String file) throws ZipException {
|
||||
if (!isStringNotNullAndNotEmpty(file)) {
|
||||
throw new ZipException("invalid file name");
|
||||
}
|
||||
|
||||
return getFileLengh(new File(file));
|
||||
}
|
||||
|
||||
public static long getFileLengh(File file) throws ZipException {
|
||||
if (file == null) {
|
||||
throw new ZipException("input file is null, cannot calculate file length");
|
||||
}
|
||||
|
||||
if (file.isDirectory()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return file.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts input time from Java to DOS format
|
||||
* @param time
|
||||
* @return time in DOS format
|
||||
*/
|
||||
public static long javaToDosTime(long time) {
|
||||
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(time);
|
||||
|
||||
int year = cal.get(Calendar.YEAR);
|
||||
if (year < 1980) {
|
||||
return (1 << 21) | (1 << 16);
|
||||
}
|
||||
return (year - 1980) << 25 | (cal.get(Calendar.MONTH) + 1) << 21 |
|
||||
cal.get(Calendar.DATE) << 16 | cal.get(Calendar.HOUR_OF_DAY) << 11 | cal.get(Calendar.MINUTE) << 5 |
|
||||
cal.get(Calendar.SECOND) >> 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts time in dos format to Java format
|
||||
* @param dosTime
|
||||
* @return time in java format
|
||||
*/
|
||||
public static long dosToJavaTme(int dosTime) {
|
||||
int sec = 2 * (dosTime & 0x1f);
|
||||
int min = (dosTime >> 5) & 0x3f;
|
||||
int hrs = (dosTime >> 11) & 0x1f;
|
||||
int day = (dosTime >> 16) & 0x1f;
|
||||
int mon = ((dosTime >> 21) & 0xf) - 1;
|
||||
int year = ((dosTime >> 25) & 0x7f) + 1980;
|
||||
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.set(year, mon, day, hrs, min, sec);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
return cal.getTime().getTime();
|
||||
}
|
||||
|
||||
public static FileHeader getFileHeader(ZipModel zipModel, String fileName) throws ZipException {
|
||||
if (zipModel == null) {
|
||||
throw new ZipException("zip model is null, cannot determine file header for fileName: " + fileName);
|
||||
}
|
||||
|
||||
if (!isStringNotNullAndNotEmpty(fileName)) {
|
||||
throw new ZipException("file name is null, cannot determine file header for fileName: " + fileName);
|
||||
}
|
||||
|
||||
FileHeader fileHeader = null;
|
||||
fileHeader = getFileHeaderWithExactMatch(zipModel, fileName);
|
||||
|
||||
if (fileHeader == null) {
|
||||
fileName = fileName.replaceAll("\\\\", "/");
|
||||
fileHeader = getFileHeaderWithExactMatch(zipModel, fileName);
|
||||
|
||||
if (fileHeader == null) {
|
||||
fileName = fileName.replaceAll("/", "\\\\");
|
||||
fileHeader = getFileHeaderWithExactMatch(zipModel, fileName);
|
||||
}
|
||||
}
|
||||
|
||||
return fileHeader;
|
||||
}
|
||||
|
||||
public static FileHeader getFileHeaderWithExactMatch(ZipModel zipModel, String fileName) throws ZipException {
|
||||
if (zipModel == null) {
|
||||
throw new ZipException("zip model is null, cannot determine file header with exact match for fileName: " + fileName);
|
||||
}
|
||||
|
||||
if (!isStringNotNullAndNotEmpty(fileName)) {
|
||||
throw new ZipException("file name is null, cannot determine file header with exact match for fileName: " + fileName);
|
||||
}
|
||||
|
||||
if (zipModel.getCentralDirectory() == null) {
|
||||
throw new ZipException("central directory is null, cannot determine file header with exact match for fileName: " + fileName);
|
||||
}
|
||||
|
||||
if (zipModel.getCentralDirectory().getFileHeaders() == null) {
|
||||
throw new ZipException("file Headers are null, cannot determine file header with exact match for fileName: " + fileName);
|
||||
}
|
||||
|
||||
if (zipModel.getCentralDirectory().getFileHeaders().size() <= 0) {
|
||||
return null;
|
||||
}
|
||||
ArrayList fileHeaders = zipModel.getCentralDirectory().getFileHeaders();
|
||||
for (int i = 0; i < fileHeaders.size(); i++) {
|
||||
FileHeader fileHeader = (FileHeader)fileHeaders.get(i);
|
||||
String fileNameForHdr = fileHeader.getFileName();
|
||||
if (!isStringNotNullAndNotEmpty(fileNameForHdr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fileName.equalsIgnoreCase(fileNameForHdr)) {
|
||||
return fileHeader;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int getIndexOfFileHeader(ZipModel zipModel,
|
||||
FileHeader fileHeader) throws ZipException {
|
||||
|
||||
if (zipModel == null || fileHeader == null) {
|
||||
throw new ZipException("input parameters is null, cannot determine index of file header");
|
||||
}
|
||||
|
||||
if (zipModel.getCentralDirectory() == null) {
|
||||
throw new ZipException("central directory is null, ccannot determine index of file header");
|
||||
}
|
||||
|
||||
if (zipModel.getCentralDirectory().getFileHeaders() == null) {
|
||||
throw new ZipException("file Headers are null, cannot determine index of file header");
|
||||
}
|
||||
|
||||
if (zipModel.getCentralDirectory().getFileHeaders().size() <= 0) {
|
||||
return -1;
|
||||
}
|
||||
String fileName = fileHeader.getFileName();
|
||||
|
||||
if (!isStringNotNullAndNotEmpty(fileName)) {
|
||||
throw new ZipException("file name in file header is empty or null, cannot determine index of file header");
|
||||
}
|
||||
|
||||
ArrayList fileHeaders = zipModel.getCentralDirectory().getFileHeaders();
|
||||
for (int i = 0; i < fileHeaders.size(); i++) {
|
||||
FileHeader fileHeaderTmp = (FileHeader)fileHeaders.get(i);
|
||||
String fileNameForHdr = fileHeaderTmp.getFileName();
|
||||
if (!isStringNotNullAndNotEmpty(fileNameForHdr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fileName.equalsIgnoreCase(fileNameForHdr)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static ArrayList getFilesInDirectoryRec(File path,
|
||||
boolean readHiddenFiles) throws ZipException {
|
||||
|
||||
if (path == null) {
|
||||
throw new ZipException("input path is null, cannot read files in the directory");
|
||||
}
|
||||
|
||||
ArrayList result = new ArrayList();
|
||||
File[] filesAndDirs = path.listFiles();
|
||||
List filesDirs = Arrays.asList(filesAndDirs);
|
||||
|
||||
if (!path.canRead()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for(int i = 0; i < filesDirs.size(); i++) {
|
||||
File file = (File)filesDirs.get(i);
|
||||
if (file.isHidden() && !readHiddenFiles) {
|
||||
return result;
|
||||
}
|
||||
result.add(file);
|
||||
if (file.isDirectory()) {
|
||||
List deeperList = getFilesInDirectoryRec(file, readHiddenFiles);
|
||||
result.addAll(deeperList);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getZipFileNameWithoutExt(String zipFile) throws ZipException {
|
||||
if (!isStringNotNullAndNotEmpty(zipFile)) {
|
||||
throw new ZipException("zip file name is empty or null, cannot determine zip file name");
|
||||
}
|
||||
String tmpFileName = zipFile;
|
||||
if (zipFile.indexOf(System.getProperty("file.separator")) >= 0) {
|
||||
tmpFileName = zipFile.substring(zipFile.lastIndexOf(System.getProperty("file.separator")));
|
||||
}
|
||||
|
||||
if (tmpFileName.indexOf(".") > 0) {
|
||||
tmpFileName = tmpFileName.substring(0, tmpFileName.lastIndexOf("."));
|
||||
}
|
||||
return tmpFileName;
|
||||
}
|
||||
|
||||
public static byte[] convertCharset(String str) throws ZipException {
|
||||
try {
|
||||
byte[] converted = null;
|
||||
String charSet = detectCharSet(str);
|
||||
if (charSet.equals(InternalZipConstants.CHARSET_CP850)) {
|
||||
converted = str.getBytes(InternalZipConstants.CHARSET_CP850);
|
||||
} else if (charSet.equals(InternalZipConstants.CHARSET_UTF8)) {
|
||||
converted = str.getBytes(InternalZipConstants.CHARSET_UTF8);
|
||||
} else {
|
||||
converted = str.getBytes();
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
catch (UnsupportedEncodingException err) {
|
||||
return str.getBytes();
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes file name based on encoding. If file name is UTF 8 encoded
|
||||
* returns an UTF8 encoded string, else return Cp850 encoded String. If
|
||||
* appropriate charset is not supported, then returns a System default
|
||||
* charset encoded String
|
||||
* @param data
|
||||
* @param isUTF8
|
||||
* @return String
|
||||
*/
|
||||
public static String decodeFileName(byte[] data, boolean isUTF8) {
|
||||
if (isUTF8) {
|
||||
try {
|
||||
return new String(data, InternalZipConstants.CHARSET_UTF8);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return new String(data);
|
||||
}
|
||||
} else {
|
||||
return getCp850EncodedString(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string in Cp850 encoding from the input bytes.
|
||||
* If this encoding is not supported, then String with the default encoding is returned.
|
||||
* @param data
|
||||
* @return String
|
||||
*/
|
||||
public static String getCp850EncodedString(byte[] data) {
|
||||
try {
|
||||
String retString = new String(data, InternalZipConstants.CHARSET_CP850);
|
||||
return retString;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return new String(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an absoulte path for the given file path
|
||||
* @param filePath
|
||||
* @return String
|
||||
*/
|
||||
public static String getAbsoluteFilePath(String filePath) throws ZipException {
|
||||
if (!isStringNotNullAndNotEmpty(filePath)) {
|
||||
throw new ZipException("filePath is null or empty, cannot get absolute file path");
|
||||
}
|
||||
|
||||
File file = new File(filePath);
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if all the elements in the arraylist match the given type
|
||||
* @param sourceList - list to be checked
|
||||
* @param type - type of elements to be present in the list (ex: File, String, etc)
|
||||
* @return true if all elements match the given type, if not returns false
|
||||
*/
|
||||
public static boolean checkArrayListTypes(ArrayList sourceList, int type) throws ZipException {
|
||||
|
||||
if (sourceList == null) {
|
||||
throw new ZipException("input arraylist is null, cannot check types");
|
||||
}
|
||||
|
||||
if (sourceList.size() <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean invalidFound = false;
|
||||
|
||||
switch (type) {
|
||||
case InternalZipConstants.LIST_TYPE_FILE:
|
||||
for (int i = 0; i < sourceList.size(); i++) {
|
||||
if (!(sourceList.get(i) instanceof File)) {
|
||||
invalidFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case InternalZipConstants.LIST_TYPE_STRING:
|
||||
for (int i = 0; i < sourceList.size(); i++) {
|
||||
if (!(sourceList.get(i) instanceof String)) {
|
||||
invalidFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return !invalidFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the encoding charset for the input string
|
||||
* @param str
|
||||
* @return String - charset for the String
|
||||
* @throws ZipException - if input string is null. In case of any other exception
|
||||
* this method returns default System charset
|
||||
*/
|
||||
public static String detectCharSet(String str) throws ZipException {
|
||||
if (str == null) {
|
||||
throw new ZipException("input string is null, cannot detect charset");
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] byteString = str.getBytes(InternalZipConstants.CHARSET_CP850);
|
||||
String tempString = new String(byteString, InternalZipConstants.CHARSET_CP850);
|
||||
|
||||
if (str.equals(tempString)) {
|
||||
return InternalZipConstants.CHARSET_CP850;
|
||||
}
|
||||
|
||||
byteString = str.getBytes(InternalZipConstants.CHARSET_UTF8);
|
||||
tempString = new String(byteString, InternalZipConstants.CHARSET_UTF8);
|
||||
|
||||
if (str.equals(tempString)) {
|
||||
return InternalZipConstants.CHARSET_UTF8;
|
||||
}
|
||||
|
||||
return InternalZipConstants.CHARSET_DEFAULT;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return InternalZipConstants.CHARSET_DEFAULT;
|
||||
} catch (Exception e) {
|
||||
return InternalZipConstants.CHARSET_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the length of the string by wrapping it in a byte buffer with
|
||||
* the appropriate charset of the input string and returns the limit of the
|
||||
* byte buffer
|
||||
* @param str
|
||||
* @return length of the string
|
||||
* @throws ZipException
|
||||
*/
|
||||
public static int getEncodedStringLength(String str) throws ZipException {
|
||||
if (!isStringNotNullAndNotEmpty(str)) {
|
||||
throw new ZipException("input string is null, cannot calculate encoded String length");
|
||||
}
|
||||
|
||||
String charset = detectCharSet(str);
|
||||
return getEncodedStringLength(str, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the length of the string in the input encoding
|
||||
* @param str
|
||||
* @param charset
|
||||
* @return int
|
||||
* @throws ZipException
|
||||
*/
|
||||
public static int getEncodedStringLength(String str, String charset) throws ZipException {
|
||||
if (!isStringNotNullAndNotEmpty(str)) {
|
||||
throw new ZipException("input string is null, cannot calculate encoded String length");
|
||||
}
|
||||
|
||||
if (!isStringNotNullAndNotEmpty(charset)) {
|
||||
throw new ZipException("encoding is not defined, cannot calculate string length");
|
||||
}
|
||||
|
||||
ByteBuffer byteBuffer = null;
|
||||
|
||||
try {
|
||||
if (charset.equals(InternalZipConstants.CHARSET_CP850)) {
|
||||
byteBuffer = ByteBuffer.wrap(str.getBytes(InternalZipConstants.CHARSET_CP850));
|
||||
} else if (charset.equals(InternalZipConstants.CHARSET_UTF8)) {
|
||||
byteBuffer = ByteBuffer.wrap(str.getBytes(InternalZipConstants.CHARSET_UTF8));
|
||||
} else {
|
||||
byteBuffer = ByteBuffer.wrap(str.getBytes(charset));
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
byteBuffer = ByteBuffer.wrap(str.getBytes());
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
|
||||
return byteBuffer.limit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the input charset is supported
|
||||
* @param charset
|
||||
* @return boolean
|
||||
* @throws ZipException
|
||||
*/
|
||||
public static boolean isSupportedCharset(String charset) throws ZipException {
|
||||
if (!isStringNotNullAndNotEmpty(charset)) {
|
||||
throw new ZipException("charset is null or empty, cannot check if it is supported");
|
||||
}
|
||||
|
||||
try {
|
||||
new String("a".getBytes(), charset);
|
||||
return true;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ArrayList getSplitZipFiles(ZipModel zipModel) throws ZipException {
|
||||
if (zipModel == null) {
|
||||
throw new ZipException("cannot get split zip files: zipmodel is null");
|
||||
}
|
||||
|
||||
if (zipModel.getEndCentralDirRecord() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayList retList = new ArrayList();
|
||||
String currZipFile = zipModel.getZipFile();
|
||||
String zipFileName = (new File(currZipFile)).getName();
|
||||
String partFile = null;
|
||||
|
||||
if (!isStringNotNullAndNotEmpty(currZipFile)) {
|
||||
throw new ZipException("cannot get split zip files: zipfile is null");
|
||||
}
|
||||
|
||||
if (!zipModel.isSplitArchive()) {
|
||||
retList.add(currZipFile);
|
||||
return retList;
|
||||
}
|
||||
|
||||
int numberOfThisDisk = zipModel.getEndCentralDirRecord().getNoOfThisDisk();
|
||||
|
||||
if (numberOfThisDisk == 0) {
|
||||
retList.add(currZipFile);
|
||||
return retList;
|
||||
} else {
|
||||
for (int i = 0; i <= numberOfThisDisk; i++) {
|
||||
if (i == numberOfThisDisk) {
|
||||
retList.add(zipModel.getZipFile());
|
||||
} else {
|
||||
String fileExt = ".z0";
|
||||
if (i > 9) {
|
||||
fileExt = ".z";
|
||||
}
|
||||
partFile = (zipFileName.indexOf(".") >= 0) ? currZipFile.substring(0, currZipFile.lastIndexOf(".")) : currZipFile;
|
||||
partFile = partFile + fileExt + (i + 1);
|
||||
retList.add(partFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retList;
|
||||
}
|
||||
|
||||
public static String getRelativeFileName(String file, String rootFolderInZip, String rootFolderPath) throws ZipException {
|
||||
if (!Zip4jUtil.isStringNotNullAndNotEmpty(file)) {
|
||||
throw new ZipException("input file path/name is empty, cannot calculate relative file name");
|
||||
}
|
||||
|
||||
String fileName = null;
|
||||
|
||||
if (Zip4jUtil.isStringNotNullAndNotEmpty(rootFolderPath)) {
|
||||
|
||||
File rootFolderFile = new File(rootFolderPath);
|
||||
|
||||
String rootFolderFileRef = rootFolderFile.getPath();
|
||||
|
||||
if (!rootFolderFileRef.endsWith(InternalZipConstants.FILE_SEPARATOR)) {
|
||||
rootFolderFileRef += InternalZipConstants.FILE_SEPARATOR;
|
||||
}
|
||||
|
||||
String tmpFileName = file.substring(rootFolderFileRef.length());
|
||||
if (tmpFileName.startsWith(System.getProperty("file.separator"))) {
|
||||
tmpFileName = tmpFileName.substring(1);
|
||||
}
|
||||
|
||||
File tmpFile = new File(file);
|
||||
|
||||
if (tmpFile.isDirectory()) {
|
||||
tmpFileName = tmpFileName.replaceAll("\\\\", "/");
|
||||
tmpFileName += InternalZipConstants.ZIP_FILE_SEPARATOR;
|
||||
} else {
|
||||
String bkFileName = tmpFileName.substring(0, tmpFileName.lastIndexOf(tmpFile.getName()));
|
||||
bkFileName = bkFileName.replaceAll("\\\\", "/");
|
||||
tmpFileName = bkFileName + tmpFile.getName();
|
||||
}
|
||||
|
||||
fileName = tmpFileName;
|
||||
} else {
|
||||
File relFile = new File(file);
|
||||
if (relFile.isDirectory()) {
|
||||
fileName = relFile.getName() + InternalZipConstants.ZIP_FILE_SEPARATOR;
|
||||
} else {
|
||||
fileName = Zip4jUtil.getFileNameFromFilePath(new File(file));
|
||||
}
|
||||
}
|
||||
|
||||
if (Zip4jUtil.isStringNotNullAndNotEmpty(rootFolderInZip)) {
|
||||
fileName = rootFolderInZip + fileName;
|
||||
}
|
||||
|
||||
if (!Zip4jUtil.isStringNotNullAndNotEmpty(fileName)) {
|
||||
throw new ZipException("Error determining file name");
|
||||
}
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public static long[] getAllHeaderSignatures() {
|
||||
long[] allSigs = new long[11];
|
||||
|
||||
allSigs[0] = InternalZipConstants.LOCSIG;
|
||||
allSigs[1] = InternalZipConstants.EXTSIG;
|
||||
allSigs[2] = InternalZipConstants.CENSIG;
|
||||
allSigs[3] = InternalZipConstants.ENDSIG;
|
||||
allSigs[4] = InternalZipConstants.DIGSIG;
|
||||
allSigs[5] = InternalZipConstants.ARCEXTDATREC;
|
||||
allSigs[6] = InternalZipConstants.SPLITSIG;
|
||||
allSigs[7] = InternalZipConstants.ZIP64ENDCENDIRLOC;
|
||||
allSigs[8] = InternalZipConstants.ZIP64ENDCENDIRREC;
|
||||
allSigs[9] = InternalZipConstants.EXTRAFIELDZIP64LENGTH;
|
||||
allSigs[10] = InternalZipConstants.AESSIG;
|
||||
|
||||
return allSigs;
|
||||
}
|
||||
}
|
485
src/net/lingala/zip4j/zip/ZipEngine.java
Normal file
485
src/net/lingala/zip4j/zip/ZipEngine.java
Normal file
|
@ -0,0 +1,485 @@
|
|||
/*
|
||||
* Copyright 2010 Srikanth Reddy Lingala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.lingala.zip4j.zip;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import net.lingala.zip4j.exception.ZipException;
|
||||
import net.lingala.zip4j.io.SplitOutputStream;
|
||||
import net.lingala.zip4j.io.ZipOutputStream;
|
||||
import net.lingala.zip4j.model.EndCentralDirRecord;
|
||||
import net.lingala.zip4j.model.FileHeader;
|
||||
import net.lingala.zip4j.model.ZipModel;
|
||||
import net.lingala.zip4j.model.ZipParameters;
|
||||
import net.lingala.zip4j.progress.ProgressMonitor;
|
||||
import net.lingala.zip4j.util.ArchiveMaintainer;
|
||||
import net.lingala.zip4j.util.CRCUtil;
|
||||
import net.lingala.zip4j.util.InternalZipConstants;
|
||||
import net.lingala.zip4j.util.Zip4jConstants;
|
||||
import net.lingala.zip4j.util.Zip4jUtil;
|
||||
|
||||
public class ZipEngine {
|
||||
|
||||
private ZipModel zipModel;
|
||||
|
||||
public ZipEngine(ZipModel zipModel) throws ZipException {
|
||||
|
||||
if (zipModel == null) {
|
||||
throw new ZipException("zip model is null in ZipEngine constructor");
|
||||
}
|
||||
|
||||
this.zipModel = zipModel;
|
||||
}
|
||||
|
||||
public void addFiles(final ArrayList fileList, final ZipParameters parameters,
|
||||
final ProgressMonitor progressMonitor, boolean runInThread) throws ZipException {
|
||||
|
||||
if (fileList == null || parameters == null) {
|
||||
throw new ZipException("one of the input parameters is null when adding files");
|
||||
}
|
||||
|
||||
if(fileList.size() <= 0) {
|
||||
throw new ZipException("no files to add");
|
||||
}
|
||||
|
||||
progressMonitor.setCurrentOperation(ProgressMonitor.OPERATION_ADD);
|
||||
progressMonitor.setState(ProgressMonitor.STATE_BUSY);
|
||||
progressMonitor.setResult(ProgressMonitor.RESULT_WORKING);
|
||||
|
||||
if (runInThread) {
|
||||
progressMonitor.setTotalWork(calculateTotalWork(fileList, parameters));
|
||||
progressMonitor.setFileName(((File)fileList.get(0)).getAbsolutePath());
|
||||
|
||||
Thread thread = new Thread(InternalZipConstants.THREAD_NAME) {
|
||||
public void run() {
|
||||
try {
|
||||
initAddFiles(fileList, parameters, progressMonitor);
|
||||
} catch (ZipException e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
thread.start();
|
||||
|
||||
} else {
|
||||
initAddFiles(fileList, parameters, progressMonitor);
|
||||
}
|
||||
}
|
||||
|
||||
private void initAddFiles(ArrayList fileList, ZipParameters parameters,
|
||||
ProgressMonitor progressMonitor) throws ZipException {
|
||||
|
||||
if (fileList == null || parameters == null) {
|
||||
throw new ZipException("one of the input parameters is null when adding files");
|
||||
}
|
||||
|
||||
if(fileList.size() <= 0) {
|
||||
throw new ZipException("no files to add");
|
||||
}
|
||||
|
||||
if (zipModel.getEndCentralDirRecord() == null) {
|
||||
zipModel.setEndCentralDirRecord(createEndOfCentralDirectoryRecord());
|
||||
}
|
||||
|
||||
ZipOutputStream outputStream = null;
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
checkParameters(parameters);
|
||||
|
||||
removeFilesIfExists(fileList, parameters, progressMonitor);
|
||||
|
||||
boolean isZipFileAlreadExists = Zip4jUtil.checkFileExists(zipModel.getZipFile());
|
||||
|
||||
SplitOutputStream splitOutputStream = new SplitOutputStream(new File(zipModel.getZipFile()), zipModel.getSplitLength());
|
||||
outputStream = new ZipOutputStream(splitOutputStream, this.zipModel);
|
||||
|
||||
if (isZipFileAlreadExists) {
|
||||
if (zipModel.getEndCentralDirRecord() == null) {
|
||||
throw new ZipException("invalid end of central directory record");
|
||||
}
|
||||
splitOutputStream.seek(zipModel.getEndCentralDirRecord().getOffsetOfStartOfCentralDir());
|
||||
}
|
||||
byte[] readBuff = new byte[InternalZipConstants.BUFF_SIZE];
|
||||
int readLen = -1;
|
||||
for (int i = 0; i < fileList.size(); i++) {
|
||||
|
||||
if (progressMonitor.isCancelAllTasks()) {
|
||||
progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
|
||||
progressMonitor.setState(ProgressMonitor.STATE_READY);
|
||||
return;
|
||||
}
|
||||
|
||||
ZipParameters fileParameters = (ZipParameters) parameters.clone();
|
||||
|
||||
progressMonitor.setFileName(((File)fileList.get(i)).getAbsolutePath());
|
||||
|
||||
if (!((File)fileList.get(i)).isDirectory()) {
|
||||
if (fileParameters.isEncryptFiles() && fileParameters.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_STANDARD) {
|
||||
progressMonitor.setCurrentOperation(ProgressMonitor.OPERATION_CALC_CRC);
|
||||
fileParameters.setSourceFileCRC((int)CRCUtil.computeFileCRC(((File)fileList.get(i)).getAbsolutePath(), progressMonitor));
|
||||
progressMonitor.setCurrentOperation(ProgressMonitor.OPERATION_ADD);
|
||||
|
||||
if (progressMonitor.isCancelAllTasks()) {
|
||||
progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
|
||||
progressMonitor.setState(ProgressMonitor.STATE_READY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Zip4jUtil.getFileLengh((File)fileList.get(i)) == 0) {
|
||||
fileParameters.setCompressionMethod(Zip4jConstants.COMP_STORE);
|
||||
}
|
||||
}
|
||||
|
||||
outputStream.putNextEntry((File)fileList.get(i), fileParameters);
|
||||
if (((File)fileList.get(i)).isDirectory()) {
|
||||
outputStream.closeEntry();
|
||||
continue;
|
||||
}
|
||||
|
||||
inputStream = new FileInputStream((File)fileList.get(i));
|
||||
|
||||
while ((readLen = inputStream.read(readBuff)) != -1) {
|
||||
if (progressMonitor.isCancelAllTasks()) {
|
||||
progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
|
||||
progressMonitor.setState(ProgressMonitor.STATE_READY);
|
||||
return;
|
||||
}
|
||||
|
||||
outputStream.write(readBuff, 0, readLen);
|
||||
progressMonitor.updateWorkCompleted(readLen);
|
||||
}
|
||||
|
||||
outputStream.closeEntry();
|
||||
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
outputStream.finish();
|
||||
progressMonitor.endProgressMonitorSuccess();
|
||||
} catch (ZipException e) {
|
||||
progressMonitor.endProgressMonitorError(e);
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
progressMonitor.endProgressMonitorError(e);
|
||||
throw new ZipException(e);
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (outputStream != null) {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addStreamToZip(InputStream inputStream, ZipParameters parameters) throws ZipException {
|
||||
if (inputStream == null || parameters == null) {
|
||||
throw new ZipException("one of the input parameters is null, cannot add stream to zip");
|
||||
}
|
||||
|
||||
ZipOutputStream outputStream = null;
|
||||
|
||||
try {
|
||||
checkParameters(parameters);
|
||||
|
||||
boolean isZipFileAlreadExists = Zip4jUtil.checkFileExists(zipModel.getZipFile());
|
||||
|
||||
SplitOutputStream splitOutputStream = new SplitOutputStream(new File(zipModel.getZipFile()), zipModel.getSplitLength());
|
||||
outputStream = new ZipOutputStream(splitOutputStream, this.zipModel);
|
||||
|
||||
if (isZipFileAlreadExists) {
|
||||
if (zipModel.getEndCentralDirRecord() == null) {
|
||||
throw new ZipException("invalid end of central directory record");
|
||||
}
|
||||
splitOutputStream.seek(zipModel.getEndCentralDirRecord().getOffsetOfStartOfCentralDir());
|
||||
}
|
||||
|
||||
byte[] readBuff = new byte[InternalZipConstants.BUFF_SIZE];
|
||||
int readLen = -1;
|
||||
|
||||
outputStream.putNextEntry(null, parameters);
|
||||
|
||||
if (!parameters.getFileNameInZip().endsWith("/") &&
|
||||
!parameters.getFileNameInZip().endsWith("\\")) {
|
||||
while ((readLen = inputStream.read(readBuff)) != -1) {
|
||||
outputStream.write(readBuff, 0, readLen);
|
||||
}
|
||||
}
|
||||
|
||||
outputStream.closeEntry();
|
||||
outputStream.finish();
|
||||
|
||||
} catch (ZipException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(e);
|
||||
} finally {
|
||||
if (outputStream != null) {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addFolderToZip(File file, ZipParameters parameters,
|
||||
ProgressMonitor progressMonitor, boolean runInThread) throws ZipException {
|
||||
if (file == null || parameters == null) {
|
||||
throw new ZipException("one of the input parameters is null, cannot add folder to zip");
|
||||
}
|
||||
|
||||
if (!Zip4jUtil.checkFileExists(file.getAbsolutePath())) {
|
||||
throw new ZipException("input folder does not exist");
|
||||
}
|
||||
|
||||
if (!file.isDirectory()) {
|
||||
throw new ZipException("input file is not a folder, user addFileToZip method to add files");
|
||||
}
|
||||
|
||||
if (!Zip4jUtil.checkFileReadAccess(file.getAbsolutePath())) {
|
||||
throw new ZipException("cannot read folder: " + file.getAbsolutePath());
|
||||
}
|
||||
|
||||
String rootFolderPath = null;
|
||||
if (parameters.isIncludeRootFolder()) {
|
||||
if (file.getAbsolutePath() != null) {
|
||||
rootFolderPath = file.getAbsoluteFile().getParentFile() != null ? file.getAbsoluteFile().getParentFile().getAbsolutePath() : "";
|
||||
} else {
|
||||
rootFolderPath = file.getParentFile() != null ? file.getParentFile().getAbsolutePath() : "";
|
||||
}
|
||||
} else {
|
||||
rootFolderPath = file.getAbsolutePath();
|
||||
}
|
||||
|
||||
parameters.setDefaultFolderPath(rootFolderPath);
|
||||
|
||||
ArrayList fileList = Zip4jUtil.getFilesInDirectoryRec(file, parameters.isReadHiddenFiles());
|
||||
|
||||
if (parameters.isIncludeRootFolder()) {
|
||||
if (fileList == null) {
|
||||
fileList = new ArrayList();
|
||||
}
|
||||
fileList.add(file);
|
||||
}
|
||||
|
||||
addFiles(fileList, parameters, progressMonitor, runInThread);
|
||||
}
|
||||
|
||||
|
||||
private void checkParameters(ZipParameters parameters) throws ZipException {
|
||||
|
||||
if (parameters == null) {
|
||||
throw new ZipException("cannot validate zip parameters");
|
||||
}
|
||||
|
||||
if ((parameters.getCompressionMethod() != Zip4jConstants.COMP_STORE) &&
|
||||
parameters.getCompressionMethod() != Zip4jConstants.COMP_DEFLATE) {
|
||||
throw new ZipException("unsupported compression type");
|
||||
}
|
||||
|
||||
if (parameters.getCompressionMethod() == Zip4jConstants.COMP_DEFLATE) {
|
||||
if (parameters.getCompressionLevel() < 0 && parameters.getCompressionLevel() > 9) {
|
||||
throw new ZipException("invalid compression level. compression level dor deflate should be in the range of 0-9");
|
||||
}
|
||||
}
|
||||
|
||||
if (parameters.isEncryptFiles()) {
|
||||
if (parameters.getEncryptionMethod() != Zip4jConstants.ENC_METHOD_STANDARD &&
|
||||
parameters.getEncryptionMethod() != Zip4jConstants.ENC_METHOD_AES) {
|
||||
throw new ZipException("unsupported encryption method");
|
||||
}
|
||||
|
||||
if (parameters.getPassword() == null || parameters.getPassword().length <= 0) {
|
||||
throw new ZipException("input password is empty or null");
|
||||
}
|
||||
} else {
|
||||
parameters.setAesKeyStrength(-1);
|
||||
parameters.setEncryptionMethod(-1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Before adding a file to a zip file, we check if a file already exists in the zip file
|
||||
* with the same fileName (including path, if exists). If yes, then we remove this file
|
||||
* before adding the file<br><br>
|
||||
*
|
||||
* <b>Note:</b> Relative path has to be passed as the fileName
|
||||
*
|
||||
* @param zipModel
|
||||
* @param fileName
|
||||
* @throws ZipException
|
||||
*/
|
||||
private void removeFilesIfExists(ArrayList fileList, ZipParameters parameters, ProgressMonitor progressMonitor) throws ZipException {
|
||||
|
||||
if (zipModel == null || zipModel.getCentralDirectory() == null ||
|
||||
zipModel.getCentralDirectory().getFileHeaders() == null ||
|
||||
zipModel.getCentralDirectory().getFileHeaders().size() <= 0) {
|
||||
//For a new zip file, this condition satisfies, so do nothing
|
||||
return;
|
||||
}
|
||||
RandomAccessFile outputStream = null;
|
||||
|
||||
try {
|
||||
for (int i = 0; i < fileList.size(); i++) {
|
||||
File file = (File) fileList.get(i);
|
||||
|
||||
String fileName = Zip4jUtil.getRelativeFileName(file.getAbsolutePath(),
|
||||
parameters.getRootFolderInZip(), parameters.getDefaultFolderPath());
|
||||
|
||||
FileHeader fileHeader = Zip4jUtil.getFileHeader(zipModel, fileName);
|
||||
if (fileHeader != null) {
|
||||
|
||||
if (outputStream != null) {
|
||||
outputStream.close();
|
||||
outputStream = null;
|
||||
}
|
||||
|
||||
ArchiveMaintainer archiveMaintainer = new ArchiveMaintainer();
|
||||
progressMonitor.setCurrentOperation(ProgressMonitor.OPERATION_REMOVE);
|
||||
HashMap retMap = archiveMaintainer.initRemoveZipFile(zipModel,
|
||||
fileHeader, progressMonitor);
|
||||
|
||||
if (progressMonitor.isCancelAllTasks()) {
|
||||
progressMonitor.setResult(ProgressMonitor.RESULT_CANCELLED);
|
||||
progressMonitor.setState(ProgressMonitor.STATE_READY);
|
||||
return;
|
||||
}
|
||||
|
||||
progressMonitor
|
||||
.setCurrentOperation(ProgressMonitor.OPERATION_ADD);
|
||||
|
||||
if (outputStream == null) {
|
||||
outputStream = prepareFileOutputStream();
|
||||
|
||||
if (retMap != null) {
|
||||
if (retMap.get(InternalZipConstants.OFFSET_CENTRAL_DIR) != null) {
|
||||
long offsetCentralDir = -1;
|
||||
try {
|
||||
offsetCentralDir = Long
|
||||
.parseLong((String) retMap
|
||||
.get(InternalZipConstants.OFFSET_CENTRAL_DIR));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ZipException(
|
||||
"NumberFormatException while parsing offset central directory. " +
|
||||
"Cannot update already existing file header");
|
||||
} catch (Exception e) {
|
||||
throw new ZipException(
|
||||
"Error while parsing offset central directory. " +
|
||||
"Cannot update already existing file header");
|
||||
}
|
||||
|
||||
if (offsetCentralDir >= 0) {
|
||||
outputStream.seek(offsetCentralDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ZipException(e);
|
||||
} finally {
|
||||
if (outputStream != null) {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RandomAccessFile prepareFileOutputStream() throws ZipException {
|
||||
String outPath = zipModel.getZipFile();
|
||||
if (!Zip4jUtil.isStringNotNullAndNotEmpty(outPath)) {
|
||||
throw new ZipException("invalid output path");
|
||||
}
|
||||
|
||||
try {
|
||||
File outFile = new File(outPath);
|
||||
if (!outFile.getParentFile().exists()) {
|
||||
outFile.getParentFile().mkdirs();
|
||||
}
|
||||
return new RandomAccessFile(outFile, InternalZipConstants.WRITE_MODE);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new ZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private EndCentralDirRecord createEndOfCentralDirectoryRecord() {
|
||||
EndCentralDirRecord endCentralDirRecord = new EndCentralDirRecord();
|
||||
endCentralDirRecord.setSignature(InternalZipConstants.ENDSIG);
|
||||
endCentralDirRecord.setNoOfThisDisk(0);
|
||||
endCentralDirRecord.setTotNoOfEntriesInCentralDir(0);
|
||||
endCentralDirRecord.setTotNoOfEntriesInCentralDirOnThisDisk(0);
|
||||
endCentralDirRecord.setOffsetOfStartOfCentralDir(0);
|
||||
return endCentralDirRecord;
|
||||
}
|
||||
|
||||
private long calculateTotalWork(ArrayList fileList, ZipParameters parameters) throws ZipException {
|
||||
if (fileList == null) {
|
||||
throw new ZipException("file list is null, cannot calculate total work");
|
||||
}
|
||||
|
||||
long totalWork = 0;
|
||||
|
||||
for (int i = 0; i < fileList.size(); i++) {
|
||||
if(fileList.get(i) instanceof File) {
|
||||
if (((File)fileList.get(i)).exists()) {
|
||||
if (parameters.isEncryptFiles() &&
|
||||
parameters.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_STANDARD) {
|
||||
totalWork += (Zip4jUtil.getFileLengh((File)fileList.get(i)) * 2);
|
||||
} else {
|
||||
totalWork += Zip4jUtil.getFileLengh((File)fileList.get(i));
|
||||
}
|
||||
|
||||
if (zipModel.getCentralDirectory() != null &&
|
||||
zipModel.getCentralDirectory().getFileHeaders() != null &&
|
||||
zipModel.getCentralDirectory().getFileHeaders().size() > 0) {
|
||||
String relativeFileName = Zip4jUtil.getRelativeFileName(
|
||||
((File)fileList.get(i)).getAbsolutePath(), parameters.getRootFolderInZip(), parameters.getDefaultFolderPath());
|
||||
FileHeader fileHeader = Zip4jUtil.getFileHeader(zipModel, relativeFileName);
|
||||
if (fileHeader != null) {
|
||||
totalWork += (Zip4jUtil.getFileLengh(new File(zipModel.getZipFile())) - fileHeader.getCompressedSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return totalWork;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue