diff --git a/src/net/lingala/zip4j/core/HeaderReader.java b/src/net/lingala/zip4j/core/HeaderReader.java
new file mode 100644
index 0000000..1e719d4
--- /dev/null
+++ b/src/net/lingala/zip4j/core/HeaderReader.java
@@ -0,0 +1,1119 @@
+/*
+* 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.IOException;
+import java.io.RandomAccessFile;
+import java.util.ArrayList;
+
+import net.lingala.zip4j.exception.ZipException;
+import net.lingala.zip4j.exception.ZipExceptionConstants;
+import net.lingala.zip4j.model.AESExtraDataRecord;
+import net.lingala.zip4j.model.CentralDirectory;
+import net.lingala.zip4j.model.DigitalSignature;
+import net.lingala.zip4j.model.EndCentralDirRecord;
+import net.lingala.zip4j.model.ExtraDataRecord;
+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.Zip64ExtendedInfo;
+import net.lingala.zip4j.model.ZipModel;
+import net.lingala.zip4j.util.InternalZipConstants;
+import net.lingala.zip4j.util.Raw;
+import net.lingala.zip4j.util.Zip4jConstants;
+import net.lingala.zip4j.util.Zip4jUtil;
+
+/**
+ * Helper class to read header information for the zip file
+ *
+ */
+public class HeaderReader {
+
+ private RandomAccessFile zip4jRaf = null;
+ private ZipModel zipModel;
+
+ /**
+ * Creates a new HeaderReader object with the given input stream
+ * @param zip4jRaf
+ */
+ public HeaderReader(RandomAccessFile zip4jRaf) {
+ this.zip4jRaf = zip4jRaf;
+ }
+
+ /**
+ * Reads all the header information for the zip file.
+ *
Note: This method does not read local file header information
+ * @return {@link ZipModel}
+ * @throws ZipException
+ */
+ public ZipModel readAllHeaders() throws ZipException {
+ return readAllHeaders(null);
+ }
+
+ /**
+ * Reads all the header information for the zip file. File names are read with
+ * input charset name. If this parameter is null, default system charset is used.
+ *
Note: This method does not read local file header information
+ * @return {@link ZipModel}
+ * @throws ZipException
+ */
+ public ZipModel readAllHeaders(String fileNameCharset) throws ZipException {
+ zipModel = new ZipModel();
+ zipModel.setFileNameCharset(fileNameCharset);
+ zipModel.setEndCentralDirRecord(readEndOfCentralDirectoryRecord());
+
+ // If file is Zip64 format, then Zip64 headers have to be read before
+ // reading central directory
+ zipModel.setZip64EndCentralDirLocator(readZip64EndCentralDirLocator());
+
+ if (zipModel.isZip64Format()) {
+ zipModel.setZip64EndCentralDirRecord(readZip64EndCentralDirRec());
+ if(zipModel.getZip64EndCentralDirRecord() != null &&
+ zipModel.getZip64EndCentralDirRecord().getNoOfThisDisk() > 0){
+ zipModel.setSplitArchive(true);
+ } else {
+ zipModel.setSplitArchive(false);
+ }
+ }
+
+ zipModel.setCentralDirectory(readCentralDirectory());
+ //zipModel.setLocalFileHeaderList(readLocalFileHeaders()); //Donot read local headers now.
+ return zipModel;
+ }
+
+ /**
+ * Reads end of central directory record
+ * @return {@link EndCentralDirRecord}
+ * @throws ZipException
+ */
+ private EndCentralDirRecord readEndOfCentralDirectoryRecord() throws ZipException {
+
+ if (zip4jRaf == null) {
+ throw new ZipException("random access file was null", ZipExceptionConstants.randomAccessFileNull);
+ }
+
+ try {
+ byte[] ebs = new byte[4];
+ long pos = zip4jRaf.length() - InternalZipConstants.ENDHDR;
+
+ EndCentralDirRecord endCentralDirRecord = new EndCentralDirRecord();
+ int counter = 0;
+ do {
+ zip4jRaf.seek(pos--);
+ counter++;
+ } while ((Raw.readLeInt(zip4jRaf, ebs) != InternalZipConstants.ENDSIG) && counter <= 3000);
+
+ if ((Raw.readIntLittleEndian(ebs, 0) != InternalZipConstants.ENDSIG)) {
+ throw new ZipException("zip headers not found. probably not a zip file");
+ }
+ byte[] intBuff = new byte[4];
+ byte[] shortBuff = new byte[2];
+
+ //End of central record signature
+ endCentralDirRecord.setSignature(InternalZipConstants.ENDSIG);
+
+ //number of this disk
+ readIntoBuff(zip4jRaf, shortBuff);
+ endCentralDirRecord.setNoOfThisDisk(Raw.readShortLittleEndian(shortBuff, 0));
+
+ //number of the disk with the start of the central directory
+ readIntoBuff(zip4jRaf, shortBuff);
+ endCentralDirRecord.setNoOfThisDiskStartOfCentralDir(Raw.readShortLittleEndian(shortBuff, 0));
+
+ //total number of entries in the central directory on this disk
+ readIntoBuff(zip4jRaf, shortBuff);
+ endCentralDirRecord.setTotNoOfEntriesInCentralDirOnThisDisk(Raw.readShortLittleEndian(shortBuff, 0));
+
+ //total number of entries in the central directory
+ readIntoBuff(zip4jRaf, shortBuff);
+ endCentralDirRecord.setTotNoOfEntriesInCentralDir(Raw.readShortLittleEndian(shortBuff, 0));
+
+ //size of the central directory
+ readIntoBuff(zip4jRaf, intBuff);
+ endCentralDirRecord.setSizeOfCentralDir(Raw.readIntLittleEndian(intBuff, 0));
+
+ //offset of start of central directory with respect to the starting disk number
+ readIntoBuff(zip4jRaf, intBuff);
+ byte[] longBuff = getLongByteFromIntByte(intBuff);
+ endCentralDirRecord.setOffsetOfStartOfCentralDir(Raw.readLongLittleEndian(longBuff, 0));
+
+ //.ZIP file comment length
+ readIntoBuff(zip4jRaf, shortBuff);
+ int commentLength = Raw.readShortLittleEndian(shortBuff, 0);
+ endCentralDirRecord.setCommentLength(commentLength);
+
+ //.ZIP file comment
+ if (commentLength > 0) {
+ byte[] commentBuf = new byte[commentLength];
+ readIntoBuff(zip4jRaf, commentBuf);
+ endCentralDirRecord.setComment(new String(commentBuf));
+ endCentralDirRecord.setCommentBytes(commentBuf);
+ } else {
+ endCentralDirRecord.setComment(null);
+ }
+
+ int diskNumber = endCentralDirRecord.getNoOfThisDisk();
+ if (diskNumber > 0) {
+ zipModel.setSplitArchive(true);
+ } else {
+ zipModel.setSplitArchive(false);
+ }
+
+ return endCentralDirRecord;
+ } catch (IOException e) {
+ throw new ZipException("Probably not a zip file or a corrupted zip file", e, ZipExceptionConstants.notZipFile);
+ }
+ }
+
+ /**
+ * Reads central directory information for the zip file
+ * @return {@link CentralDirectory}
+ * @throws ZipException
+ */
+ private CentralDirectory readCentralDirectory() throws ZipException {
+
+ if (zip4jRaf == null) {
+ throw new ZipException("random access file was null", ZipExceptionConstants.randomAccessFileNull);
+ }
+
+ if (zipModel.getEndCentralDirRecord() == null) {
+ throw new ZipException("EndCentralRecord was null, maybe a corrupt zip file");
+ }
+
+ try {
+ CentralDirectory centralDirectory = new CentralDirectory();
+ ArrayList fileHeaderList = new ArrayList();
+
+ EndCentralDirRecord endCentralDirRecord = zipModel.getEndCentralDirRecord();
+ long offSetStartCentralDir = endCentralDirRecord.getOffsetOfStartOfCentralDir();
+ int centralDirEntryCount = endCentralDirRecord.getTotNoOfEntriesInCentralDir();
+
+ if (zipModel.isZip64Format()) {
+ offSetStartCentralDir = zipModel.getZip64EndCentralDirRecord().getOffsetStartCenDirWRTStartDiskNo();
+ centralDirEntryCount = (int)zipModel.getZip64EndCentralDirRecord().getTotNoOfEntriesInCentralDir();
+ }
+
+ zip4jRaf.seek(offSetStartCentralDir);
+
+ byte[] intBuff = new byte[4];
+ byte[] shortBuff = new byte[2];
+ byte[] longBuff = new byte[8];
+
+ for (int i = 0; i < centralDirEntryCount; i++) {
+ FileHeader fileHeader = new FileHeader();
+
+ //FileHeader Signature
+ readIntoBuff(zip4jRaf, intBuff);
+ int signature = Raw.readIntLittleEndian(intBuff, 0);
+ if (signature != InternalZipConstants.CENSIG) {
+ throw new ZipException("Expected central directory entry not found (#" + (i + 1) + ")");
+ }
+ fileHeader.setSignature(signature);
+
+ //version made by
+ readIntoBuff(zip4jRaf, shortBuff);
+ fileHeader.setVersionMadeBy(Raw.readShortLittleEndian(shortBuff, 0));
+
+ //version needed to extract
+ readIntoBuff(zip4jRaf, shortBuff);
+ fileHeader.setVersionNeededToExtract(Raw.readShortLittleEndian(shortBuff, 0));
+
+ //general purpose bit flag
+ readIntoBuff(zip4jRaf, shortBuff);
+ fileHeader.setFileNameUTF8Encoded((Raw.readShortLittleEndian(shortBuff, 0) & InternalZipConstants.UFT8_NAMES_FLAG) != 0);
+ int firstByte = shortBuff[0];
+ int result = firstByte & 1;
+ if (result != 0) {
+ fileHeader.setEncrypted(true);
+ }
+ fileHeader.setGeneralPurposeFlag((byte[])shortBuff.clone());
+
+ //Check if data descriptor exists for local file header
+ fileHeader.setDataDescriptorExists(firstByte>>3 == 1);
+
+ //compression method
+ readIntoBuff(zip4jRaf, shortBuff);
+ fileHeader.setCompressionMethod(Raw.readShortLittleEndian(shortBuff, 0));
+
+ //last mod file time
+ readIntoBuff(zip4jRaf, intBuff);
+ fileHeader.setLastModFileTime(Raw.readIntLittleEndian(intBuff, 0));
+
+ //crc-32
+ readIntoBuff(zip4jRaf, intBuff);
+ fileHeader.setCrc32(Raw.readIntLittleEndian(intBuff, 0));
+ fileHeader.setCrcBuff((byte[])intBuff.clone());
+
+ //compressed size
+ readIntoBuff(zip4jRaf, intBuff);
+ longBuff = getLongByteFromIntByte(intBuff);
+ fileHeader.setCompressedSize(Raw.readLongLittleEndian(longBuff, 0));
+
+ //uncompressed size
+ readIntoBuff(zip4jRaf, intBuff);
+ longBuff = getLongByteFromIntByte(intBuff);
+ fileHeader.setUncompressedSize(Raw.readLongLittleEndian(longBuff, 0));
+
+ //file name length
+ readIntoBuff(zip4jRaf, shortBuff);
+ int fileNameLength = Raw.readShortLittleEndian(shortBuff, 0);
+ fileHeader.setFileNameLength(fileNameLength);
+
+ //extra field length
+ readIntoBuff(zip4jRaf, shortBuff);
+ int extraFieldLength = Raw.readShortLittleEndian(shortBuff, 0);
+ fileHeader.setExtraFieldLength(extraFieldLength);
+
+ //file comment length
+ readIntoBuff(zip4jRaf, shortBuff);
+ int fileCommentLength = Raw.readShortLittleEndian(shortBuff, 0);
+ fileHeader.setFileComment(new String(shortBuff));
+
+ //disk number start
+ readIntoBuff(zip4jRaf, shortBuff);
+ fileHeader.setDiskNumberStart(Raw.readShortLittleEndian(shortBuff, 0));
+
+ //internal file attributes
+ readIntoBuff(zip4jRaf, shortBuff);
+ fileHeader.setInternalFileAttr((byte[])shortBuff.clone());
+
+ //external file attributes
+ readIntoBuff(zip4jRaf, intBuff);
+ fileHeader.setExternalFileAttr((byte[])intBuff.clone());
+
+ //relative offset of local header
+ readIntoBuff(zip4jRaf, intBuff);
+ //Commented on 26.08.2010. Revert back if any issues
+ //fileHeader.setOffsetLocalHeader((Raw.readIntLittleEndian(intBuff, 0) & 0xFFFFFFFFL) + zip4jRaf.getStart());
+ longBuff = getLongByteFromIntByte(intBuff);
+ fileHeader.setOffsetLocalHeader((Raw.readLongLittleEndian(longBuff, 0) & 0xFFFFFFFFL));
+
+ if (fileNameLength > 0) {
+ byte[] fileNameBuf = new byte[fileNameLength];
+ readIntoBuff(zip4jRaf, fileNameBuf);
+ // Modified after user reported an issue http://www.lingala.net/zip4j/forum/index.php?topic=2.0
+// String fileName = new String(fileNameBuf, "Cp850");
+ // Modified as per http://www.lingala.net/zip4j/forum/index.php?topic=41.0
+// String fileName = Zip4jUtil.getCp850EncodedString(fileNameBuf);
+
+ String fileName = null;
+
+ if (Zip4jUtil.isStringNotNullAndNotEmpty(zipModel.getFileNameCharset())) {
+ fileName = new String(fileNameBuf, zipModel.getFileNameCharset());
+ } else {
+ fileName = Zip4jUtil.decodeFileName(fileNameBuf, fileHeader.isFileNameUTF8Encoded());
+ }
+
+ if (fileName == null) {
+ throw new ZipException("fileName is null when reading central directory");
+ }
+
+ if (fileName.indexOf(":" + System.getProperty("file.separator")) >= 0) {
+ fileName = fileName.substring(fileName.indexOf(":" + System.getProperty("file.separator")) + 2);
+ }
+
+ fileHeader.setFileName(fileName);
+ fileHeader.setDirectory(fileName.endsWith("/") || fileName.endsWith("\\"));
+
+ } else {
+ fileHeader.setFileName(null);
+ }
+
+ //Extra field
+ readAndSaveExtraDataRecord(fileHeader);
+
+ //Read Zip64 Extra data records if exists
+ readAndSaveZip64ExtendedInfo(fileHeader);
+
+ //Read AES Extra Data record if exists
+ readAndSaveAESExtraDataRecord(fileHeader);
+
+// if (fileHeader.isEncrypted()) {
+//
+// if (fileHeader.getEncryptionMethod() == ZipConstants.ENC_METHOD_AES) {
+// //Do nothing
+// } else {
+// if ((firstByte & 64) == 64) {
+// //hardcoded for now
+// fileHeader.setEncryptionMethod(1);
+// } else {
+// fileHeader.setEncryptionMethod(ZipConstants.ENC_METHOD_STANDARD);
+// fileHeader.setCompressedSize(fileHeader.getCompressedSize()
+// - ZipConstants.STD_DEC_HDR_SIZE);
+// }
+// }
+//
+// }
+
+ if (fileCommentLength > 0) {
+ byte[] fileCommentBuf = new byte[fileCommentLength];
+ readIntoBuff(zip4jRaf, fileCommentBuf);
+ fileHeader.setFileComment(new String(fileCommentBuf));
+ }
+
+ fileHeaderList.add(fileHeader);
+ }
+ centralDirectory.setFileHeaders(fileHeaderList);
+
+ //Digital Signature
+ DigitalSignature digitalSignature = new DigitalSignature();
+ readIntoBuff(zip4jRaf, intBuff);
+ int signature = Raw.readIntLittleEndian(intBuff, 0);
+ if (signature != InternalZipConstants.DIGSIG) {
+ return centralDirectory;
+ }
+
+ digitalSignature.setHeaderSignature(signature);
+
+ //size of data
+ readIntoBuff(zip4jRaf, shortBuff);
+ int sizeOfData = Raw.readShortLittleEndian(shortBuff, 0);
+ digitalSignature.setSizeOfData(sizeOfData);
+
+ if (sizeOfData > 0) {
+ byte[] sigDataBuf = new byte[sizeOfData];
+ readIntoBuff(zip4jRaf, sigDataBuf);
+ digitalSignature.setSignatureData(new String(sigDataBuf));
+ }
+
+ return centralDirectory;
+ } catch (IOException e) {
+ throw new ZipException(e);
+ }
+ }
+
+ /**
+ * Reads extra data record and saves it in the {@link FileHeader}
+ * @param fileHeader
+ * @throws ZipException
+ */
+ private void readAndSaveExtraDataRecord(FileHeader fileHeader) throws ZipException {
+
+ if (zip4jRaf == null) {
+ throw new ZipException("invalid file handler when trying to read extra data record");
+ }
+
+ if (fileHeader == null) {
+ throw new ZipException("file header is null");
+ }
+
+ int extraFieldLength = fileHeader.getExtraFieldLength();
+ if (extraFieldLength <= 0) {
+ return;
+ }
+
+ fileHeader.setExtraDataRecords(readExtraDataRecords(extraFieldLength));
+
+ }
+
+ /**
+ * Reads extra data record and saves it in the {@link LocalFileHeader}
+ * @param localFileHeader
+ * @throws ZipException
+ */
+ private void readAndSaveExtraDataRecord(LocalFileHeader localFileHeader) throws ZipException {
+
+ if (zip4jRaf == null) {
+ throw new ZipException("invalid file handler when trying to read extra data record");
+ }
+
+ if (localFileHeader == null) {
+ throw new ZipException("file header is null");
+ }
+
+ int extraFieldLength = localFileHeader.getExtraFieldLength();
+ if (extraFieldLength <= 0) {
+ return;
+ }
+
+ localFileHeader.setExtraDataRecords(readExtraDataRecords(extraFieldLength));
+
+ }
+
+ /**
+ * Reads extra data records
+ * @param extraFieldLength
+ * @return ArrayList of {@link ExtraDataRecord}
+ * @throws ZipException
+ */
+ private ArrayList readExtraDataRecords(int extraFieldLength) throws ZipException {
+
+ if (extraFieldLength <= 0) {
+ return null;
+ }
+
+ try {
+ byte[] extraFieldBuf = new byte[extraFieldLength];
+ zip4jRaf.read(extraFieldBuf);
+
+ int counter = 0;
+ ArrayList extraDataList = new ArrayList();
+ while(counter < extraFieldLength) {
+ ExtraDataRecord extraDataRecord = new ExtraDataRecord();
+ int header = Raw.readShortLittleEndian(extraFieldBuf, counter);
+ extraDataRecord.setHeader(header);
+ counter = counter + 2;
+ int sizeOfRec = Raw.readShortLittleEndian(extraFieldBuf, counter);
+
+ if ((2 + sizeOfRec) > extraFieldLength) {
+ sizeOfRec = Raw.readShortBigEndian(extraFieldBuf, counter);
+ if ((2 + sizeOfRec) > extraFieldLength) {
+ //If this is the case, then extra data record is corrupt
+ //skip reading any further extra data records
+ break;
+ }
+ }
+
+ extraDataRecord.setSizeOfData(sizeOfRec);
+ counter = counter + 2;
+
+ if (sizeOfRec > 0) {
+ byte[] data = new byte[sizeOfRec];
+ System.arraycopy(extraFieldBuf, counter, data, 0, sizeOfRec);
+ extraDataRecord.setData(data);
+ }
+ counter = counter + sizeOfRec;
+ extraDataList.add(extraDataRecord);
+ }
+ if (extraDataList.size() > 0) {
+ return extraDataList;
+ } else {
+ return null;
+ }
+ } catch (IOException e) {
+ throw new ZipException(e);
+ }
+ }
+
+ /**
+ * Reads Zip64 End Of Central Directory Locator
+ * @return {@link Zip64EndCentralDirLocator}
+ * @throws ZipException
+ */
+ private Zip64EndCentralDirLocator readZip64EndCentralDirLocator() throws ZipException {
+
+ if (zip4jRaf == null) {
+ throw new ZipException("invalid file handler when trying to read Zip64EndCentralDirLocator");
+ }
+
+ try {
+ Zip64EndCentralDirLocator zip64EndCentralDirLocator = new Zip64EndCentralDirLocator();
+
+ setFilePointerToReadZip64EndCentralDirLoc();
+
+ byte[] intBuff = new byte[4];
+ byte[] longBuff = new byte[8];
+
+ readIntoBuff(zip4jRaf, intBuff);
+ int signature = Raw.readIntLittleEndian(intBuff, 0);
+ if (signature == InternalZipConstants.ZIP64ENDCENDIRLOC) {
+ zipModel.setZip64Format(true);
+ zip64EndCentralDirLocator.setSignature(signature);
+ } else {
+ zipModel.setZip64Format(false);
+ return null;
+ }
+
+ readIntoBuff(zip4jRaf, intBuff);
+ zip64EndCentralDirLocator.setNoOfDiskStartOfZip64EndOfCentralDirRec(
+ Raw.readIntLittleEndian(intBuff, 0));
+
+ readIntoBuff(zip4jRaf, longBuff);
+ zip64EndCentralDirLocator.setOffsetZip64EndOfCentralDirRec(
+ Raw.readLongLittleEndian(longBuff, 0));
+
+ readIntoBuff(zip4jRaf, intBuff);
+ zip64EndCentralDirLocator.setTotNumberOfDiscs(Raw.readIntLittleEndian(intBuff, 0));
+
+ return zip64EndCentralDirLocator;
+
+ } catch (Exception e) {
+ throw new ZipException(e);
+ }
+
+ }
+
+ /**
+ * Reads Zip64 End of Central Directory Record
+ * @return {@link Zip64EndCentralDirRecord}
+ * @throws ZipException
+ */
+ private Zip64EndCentralDirRecord readZip64EndCentralDirRec() throws ZipException {
+
+ if (zipModel.getZip64EndCentralDirLocator() == null) {
+ throw new ZipException("invalid zip64 end of central directory locator");
+ }
+
+ long offSetStartOfZip64CentralDir =
+ zipModel.getZip64EndCentralDirLocator().getOffsetZip64EndOfCentralDirRec();
+
+ if (offSetStartOfZip64CentralDir < 0) {
+ throw new ZipException("invalid offset for start of end of central directory record");
+ }
+
+ try {
+ zip4jRaf.seek(offSetStartOfZip64CentralDir);
+
+ Zip64EndCentralDirRecord zip64EndCentralDirRecord = new Zip64EndCentralDirRecord();
+
+ byte[] shortBuff = new byte[2];
+ byte[] intBuff = new byte[4];
+ byte[] longBuff = new byte[8];
+
+ //signature
+ readIntoBuff(zip4jRaf, intBuff);
+ int signature = Raw.readIntLittleEndian(intBuff, 0);
+ if (signature != InternalZipConstants.ZIP64ENDCENDIRREC) {
+ throw new ZipException("invalid signature for zip64 end of central directory record");
+ }
+ zip64EndCentralDirRecord.setSignature(signature);
+
+ //size of zip64 end of central directory record
+ readIntoBuff(zip4jRaf, longBuff);
+ zip64EndCentralDirRecord.setSizeOfZip64EndCentralDirRec(
+ Raw.readLongLittleEndian(longBuff, 0));
+
+ //version made by
+ readIntoBuff(zip4jRaf, shortBuff);
+ zip64EndCentralDirRecord.setVersionMadeBy(Raw.readShortLittleEndian(shortBuff, 0));
+
+ //version needed to extract
+ readIntoBuff(zip4jRaf, shortBuff);
+ zip64EndCentralDirRecord.setVersionNeededToExtract(Raw.readShortLittleEndian(shortBuff, 0));
+
+ //number of this disk
+ readIntoBuff(zip4jRaf, intBuff);
+ zip64EndCentralDirRecord.setNoOfThisDisk(Raw.readIntLittleEndian(intBuff, 0));
+
+ //number of the disk with the start of the central directory
+ readIntoBuff(zip4jRaf, intBuff);
+ zip64EndCentralDirRecord.setNoOfThisDiskStartOfCentralDir(
+ Raw.readIntLittleEndian(intBuff, 0));
+
+ //total number of entries in the central directory on this disk
+ readIntoBuff(zip4jRaf, longBuff);
+ zip64EndCentralDirRecord.setTotNoOfEntriesInCentralDirOnThisDisk(
+ Raw.readLongLittleEndian(longBuff, 0));
+
+ //total number of entries in the central directory
+ readIntoBuff(zip4jRaf, longBuff);
+ zip64EndCentralDirRecord.setTotNoOfEntriesInCentralDir(
+ Raw.readLongLittleEndian(longBuff, 0));
+
+ //size of the central directory
+ readIntoBuff(zip4jRaf, longBuff);
+ zip64EndCentralDirRecord.setSizeOfCentralDir(Raw.readLongLittleEndian(longBuff, 0));
+
+ //offset of start of central directory with respect to the starting disk number
+ readIntoBuff(zip4jRaf, longBuff);
+ zip64EndCentralDirRecord.setOffsetStartCenDirWRTStartDiskNo(
+ Raw.readLongLittleEndian(longBuff, 0));
+
+ //zip64 extensible data sector
+ //44 is the size of fixed variables in this record
+ long extDataSecSize = zip64EndCentralDirRecord.getSizeOfZip64EndCentralDirRec() - 44;
+ if (extDataSecSize > 0) {
+ byte[] extDataSecRecBuf = new byte[(int)extDataSecSize];
+ readIntoBuff(zip4jRaf, extDataSecRecBuf);
+ zip64EndCentralDirRecord.setExtensibleDataSector(extDataSecRecBuf);
+ }
+
+ return zip64EndCentralDirRecord;
+
+ } catch (IOException e) {
+ throw new ZipException(e);
+ }
+
+ }
+
+ /**
+ * Reads Zip64 Extended info and saves it in the {@link FileHeader}
+ * @param fileHeader
+ * @throws ZipException
+ */
+ private void readAndSaveZip64ExtendedInfo(FileHeader fileHeader) throws ZipException {
+ if (fileHeader == null) {
+ throw new ZipException("file header is null in reading Zip64 Extended Info");
+ }
+
+ if (fileHeader.getExtraDataRecords() == null || fileHeader.getExtraDataRecords().size() <= 0) {
+ return;
+ }
+
+ Zip64ExtendedInfo zip64ExtendedInfo = readZip64ExtendedInfo(
+ fileHeader.getExtraDataRecords(),
+ fileHeader.getUncompressedSize(),
+ fileHeader.getCompressedSize(),
+ fileHeader.getOffsetLocalHeader(),
+ fileHeader.getDiskNumberStart());
+
+ if (zip64ExtendedInfo != null) {
+ fileHeader.setZip64ExtendedInfo(zip64ExtendedInfo);
+ if (zip64ExtendedInfo.getUnCompressedSize() != -1)
+ fileHeader.setUncompressedSize(zip64ExtendedInfo.getUnCompressedSize());
+
+ if (zip64ExtendedInfo.getCompressedSize() != -1)
+ fileHeader.setCompressedSize(zip64ExtendedInfo.getCompressedSize());
+
+ if (zip64ExtendedInfo.getOffsetLocalHeader() != -1)
+ fileHeader.setOffsetLocalHeader(zip64ExtendedInfo.getOffsetLocalHeader());
+
+ if (zip64ExtendedInfo.getDiskNumberStart() != -1)
+ fileHeader.setDiskNumberStart(zip64ExtendedInfo.getDiskNumberStart());
+ }
+ }
+
+ /**
+ * Reads Zip64 Extended Info and saves it in the {@link LocalFileHeader}
+ * @param localFileHeader
+ * @throws ZipException
+ */
+ private void readAndSaveZip64ExtendedInfo(LocalFileHeader localFileHeader) throws ZipException {
+ if (localFileHeader == null) {
+ throw new ZipException("file header is null in reading Zip64 Extended Info");
+ }
+
+ if (localFileHeader.getExtraDataRecords() == null || localFileHeader.getExtraDataRecords().size() <= 0) {
+ return;
+ }
+
+ Zip64ExtendedInfo zip64ExtendedInfo = readZip64ExtendedInfo(
+ localFileHeader.getExtraDataRecords(),
+ localFileHeader.getUncompressedSize(),
+ localFileHeader.getCompressedSize(),
+ -1, -1);
+
+ if (zip64ExtendedInfo != null) {
+ localFileHeader.setZip64ExtendedInfo(zip64ExtendedInfo);
+
+ if (zip64ExtendedInfo.getUnCompressedSize() != -1)
+ localFileHeader.setUncompressedSize(zip64ExtendedInfo.getUnCompressedSize());
+
+ if (zip64ExtendedInfo.getCompressedSize() != -1)
+ localFileHeader.setCompressedSize(zip64ExtendedInfo.getCompressedSize());
+ }
+ }
+
+ /**
+ * Reads Zip64 Extended Info
+ * @param extraDataRecords
+ * @param unCompressedSize
+ * @param compressedSize
+ * @param offsetLocalHeader
+ * @param diskNumberStart
+ * @return {@link Zip64ExtendedInfo}
+ * @throws ZipException
+ */
+ private Zip64ExtendedInfo readZip64ExtendedInfo(
+ ArrayList extraDataRecords,
+ long unCompressedSize,
+ long compressedSize,
+ long offsetLocalHeader,
+ int diskNumberStart) throws ZipException {
+
+ for (int i = 0; i < extraDataRecords.size(); i++) {
+ ExtraDataRecord extraDataRecord = (ExtraDataRecord)extraDataRecords.get(i);
+ if (extraDataRecord == null) {
+ continue;
+ }
+
+ if (extraDataRecord.getHeader() == 0x0001) {
+
+ Zip64ExtendedInfo zip64ExtendedInfo = new Zip64ExtendedInfo();
+
+ byte[] byteBuff = extraDataRecord.getData();
+
+ if (extraDataRecord.getSizeOfData() <= 0) {
+ break;
+ }
+ byte[] longByteBuff = new byte[8];
+ byte[] intByteBuff = new byte[4];
+ int counter = 0;
+ boolean valueAdded = false;
+
+ if (((unCompressedSize & 0xFFFF) == 0xFFFF) && counter < extraDataRecord.getSizeOfData()) {
+ System.arraycopy(byteBuff, counter, longByteBuff, 0, 8);
+ long val = Raw.readLongLittleEndian(longByteBuff, 0);
+ zip64ExtendedInfo.setUnCompressedSize(val);
+ counter += 8;
+ valueAdded = true;
+ }
+
+ if (((compressedSize & 0xFFFF) == 0xFFFF) && counter < extraDataRecord.getSizeOfData()) {
+ System.arraycopy(byteBuff, counter, longByteBuff, 0, 8);
+ long val = Raw.readLongLittleEndian(longByteBuff, 0);
+ zip64ExtendedInfo.setCompressedSize(val);
+ counter += 8;
+ valueAdded = true;
+ }
+
+ if (((offsetLocalHeader & 0xFFFF) == 0xFFFF) && counter < extraDataRecord.getSizeOfData()) {
+ System.arraycopy(byteBuff, counter, longByteBuff, 0, 8);
+ long val = Raw.readLongLittleEndian(longByteBuff, 0);
+ zip64ExtendedInfo.setOffsetLocalHeader(val);
+ counter += 8;
+ valueAdded = true;
+ }
+
+ if (((diskNumberStart & 0xFFFF) == 0xFFFF) && counter < extraDataRecord.getSizeOfData()) {
+ System.arraycopy(byteBuff, counter, intByteBuff, 0, 4);
+ int val = Raw.readIntLittleEndian(intByteBuff, 0);
+ zip64ExtendedInfo.setDiskNumberStart(val);
+ counter += 8;
+ valueAdded = true;
+ }
+
+ if (valueAdded) {
+ return zip64ExtendedInfo;
+ }
+
+ break;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the current random access file pointer at the start of signature
+ * of the zip64 end of central directory record
+ * @throws ZipException
+ */
+ private void setFilePointerToReadZip64EndCentralDirLoc() throws ZipException {
+ try {
+ byte[] ebs = new byte[4];
+ long pos = zip4jRaf.length() - InternalZipConstants.ENDHDR;
+
+ do {
+ zip4jRaf.seek(pos--);
+ } while (Raw.readLeInt(zip4jRaf, ebs) != InternalZipConstants.ENDSIG);
+
+ // Now the file pointer is at the end of signature of Central Dir Rec
+ // Seek back with the following values
+ // 4 -> end of central dir signature
+ // 4 -> total number of disks
+ // 8 -> relative offset of the zip64 end of central directory record
+ // 4 -> number of the disk with the start of the zip64 end of central directory
+ // 4 -> zip64 end of central dir locator signature
+ // Refer to Appnote for more information
+ //TODO: Donot harcorde these values. Make use of ZipConstants
+ zip4jRaf.seek(zip4jRaf.getFilePointer() - 4 - 4 - 8 - 4 - 4);
+ } catch (IOException e) {
+ throw new ZipException(e);
+ }
+ }
+
+ /**
+ * Reads local file header for the given file header
+ * @param fileHeader
+ * @return {@link LocalFileHeader}
+ * @throws ZipException
+ */
+ public LocalFileHeader readLocalFileHeader(FileHeader fileHeader) throws ZipException {
+ if (fileHeader == null || zip4jRaf == null) {
+ throw new ZipException("invalid read parameters for local header");
+ }
+
+ long locHdrOffset = fileHeader.getOffsetLocalHeader();
+
+ if (fileHeader.getZip64ExtendedInfo() != null) {
+ Zip64ExtendedInfo zip64ExtendedInfo = fileHeader.getZip64ExtendedInfo();
+ if (zip64ExtendedInfo.getOffsetLocalHeader() > 0) {
+ locHdrOffset = fileHeader.getOffsetLocalHeader();
+ }
+ }
+
+ if (locHdrOffset < 0) {
+ throw new ZipException("invalid local header offset");
+ }
+
+ try {
+ zip4jRaf.seek(locHdrOffset);
+
+ int length = 0;
+ LocalFileHeader localFileHeader = new LocalFileHeader();
+
+ byte[] shortBuff = new byte[2];
+ byte[] intBuff = new byte[4];
+ byte[] longBuff = new byte[8];
+
+ //signature
+ readIntoBuff(zip4jRaf, intBuff);
+ int sig = Raw.readIntLittleEndian(intBuff, 0);
+ if (sig != InternalZipConstants.LOCSIG) {
+ throw new ZipException("invalid local header signature for file: " + fileHeader.getFileName());
+ }
+ localFileHeader.setSignature(sig);
+ length += 4;
+
+ //version needed to extract
+ readIntoBuff(zip4jRaf, shortBuff);
+ localFileHeader.setVersionNeededToExtract(Raw.readShortLittleEndian(shortBuff, 0));
+ length += 2;
+
+ //general purpose bit flag
+ readIntoBuff(zip4jRaf, shortBuff);
+ localFileHeader.setFileNameUTF8Encoded((Raw.readShortLittleEndian(shortBuff, 0) & InternalZipConstants.UFT8_NAMES_FLAG) != 0);
+ int firstByte = shortBuff[0];
+ int result = firstByte & 1;
+ if (result != 0) {
+ localFileHeader.setEncrypted(true);
+ }
+ localFileHeader.setGeneralPurposeFlag(shortBuff);
+ length += 2;
+
+ //Check if data descriptor exists for local file header
+ String binary = Integer.toBinaryString(firstByte);
+ if (binary.length() >= 4)
+ localFileHeader.setDataDescriptorExists(binary.charAt(3) == '1');
+
+ //compression method
+ readIntoBuff(zip4jRaf, shortBuff);
+ localFileHeader.setCompressionMethod(Raw.readShortLittleEndian(shortBuff, 0));
+ length += 2;
+
+ //last mod file time
+ readIntoBuff(zip4jRaf, intBuff);
+ localFileHeader.setLastModFileTime(Raw.readIntLittleEndian(intBuff, 0));
+ length += 4;
+
+ //crc-32
+ readIntoBuff(zip4jRaf, intBuff);
+ localFileHeader.setCrc32(Raw.readIntLittleEndian(intBuff, 0));
+ localFileHeader.setCrcBuff((byte[])intBuff.clone());
+ length += 4;
+
+ //compressed size
+ readIntoBuff(zip4jRaf, intBuff);
+ longBuff = getLongByteFromIntByte(intBuff);
+ localFileHeader.setCompressedSize(Raw.readLongLittleEndian(longBuff, 0));
+ length += 4;
+
+ //uncompressed size
+ readIntoBuff(zip4jRaf, intBuff);
+ longBuff = getLongByteFromIntByte(intBuff);
+ localFileHeader.setUncompressedSize(Raw.readLongLittleEndian(longBuff, 0));
+ length += 4;
+
+ //file name length
+ readIntoBuff(zip4jRaf, shortBuff);
+ int fileNameLength = Raw.readShortLittleEndian(shortBuff, 0);
+ localFileHeader.setFileNameLength(fileNameLength);
+ length += 2;
+
+ //extra field length
+ readIntoBuff(zip4jRaf, shortBuff);
+ int extraFieldLength = Raw.readShortLittleEndian(shortBuff, 0);
+ localFileHeader.setExtraFieldLength(extraFieldLength);
+ length += 2;
+
+ //file name
+ if (fileNameLength > 0) {
+ byte[] fileNameBuf = new byte[fileNameLength];
+ readIntoBuff(zip4jRaf, fileNameBuf);
+ // Modified after user reported an issue http://www.lingala.net/zip4j/forum/index.php?topic=2.0
+// String fileName = new String(fileNameBuf, "Cp850");
+// String fileName = Zip4jUtil.getCp850EncodedString(fileNameBuf);
+ String fileName = Zip4jUtil.decodeFileName(fileNameBuf, localFileHeader.isFileNameUTF8Encoded());
+
+ if (fileName == null) {
+ throw new ZipException("file name is null, cannot assign file name to local file header");
+ }
+
+ if (fileName.indexOf(":" + System.getProperty("file.separator")) >= 0) {
+ fileName = fileName.substring(fileName.indexOf(":" + System.getProperty("file.separator")) + 2);
+ }
+
+ localFileHeader.setFileName(fileName);
+ length += fileNameLength;
+ } else {
+ localFileHeader.setFileName(null);
+ }
+
+ //extra field
+ readAndSaveExtraDataRecord(localFileHeader);
+ length += extraFieldLength;
+
+ localFileHeader.setOffsetStartOfData(locHdrOffset + length);
+
+ //Copy password from fileHeader to localFileHeader
+ localFileHeader.setPassword(fileHeader.getPassword());
+
+ readAndSaveZip64ExtendedInfo(localFileHeader);
+
+ readAndSaveAESExtraDataRecord(localFileHeader);
+
+ if (localFileHeader.isEncrypted()) {
+
+ if (localFileHeader.getEncryptionMethod() == Zip4jConstants.ENC_METHOD_AES) {
+ //Do nothing
+ } else {
+ if ((firstByte & 64) == 64) {
+ //hardcoded for now
+ localFileHeader.setEncryptionMethod(1);
+ } else {
+ localFileHeader.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD);
+// localFileHeader.setCompressedSize(localFileHeader.getCompressedSize()
+// - ZipConstants.STD_DEC_HDR_SIZE);
+ }
+ }
+
+ }
+
+ if (localFileHeader.getCrc32() <= 0) {
+ localFileHeader.setCrc32(fileHeader.getCrc32());
+ localFileHeader.setCrcBuff(fileHeader.getCrcBuff());
+ }
+
+ if (localFileHeader.getCompressedSize() <= 0) {
+ localFileHeader.setCompressedSize(fileHeader.getCompressedSize());
+ }
+
+ if (localFileHeader.getUncompressedSize() <= 0) {
+ localFileHeader.setUncompressedSize(fileHeader.getUncompressedSize());
+ }
+
+ return localFileHeader;
+ } catch (IOException e) {
+ throw new ZipException(e);
+ }
+ }
+
+ /**
+ * Reads AES Extra Data Record and saves it in the {@link FileHeader}
+ * @param fileHeader
+ * @throws ZipException
+ */
+ private void readAndSaveAESExtraDataRecord(FileHeader fileHeader) throws ZipException {
+ if (fileHeader == null) {
+ throw new ZipException("file header is null in reading Zip64 Extended Info");
+ }
+
+ if (fileHeader.getExtraDataRecords() == null || fileHeader.getExtraDataRecords().size() <= 0) {
+ return;
+ }
+
+ AESExtraDataRecord aesExtraDataRecord = readAESExtraDataRecord(fileHeader.getExtraDataRecords());
+ if (aesExtraDataRecord != null) {
+ fileHeader.setAesExtraDataRecord(aesExtraDataRecord);
+ fileHeader.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);
+ }
+ }
+
+ /**
+ * Reads AES Extra Data Record and saves it in the {@link LocalFileHeader}
+ * @param localFileHeader
+ * @throws ZipException
+ */
+ private void readAndSaveAESExtraDataRecord(LocalFileHeader localFileHeader) throws ZipException {
+ if (localFileHeader == null) {
+ throw new ZipException("file header is null in reading Zip64 Extended Info");
+ }
+
+ if (localFileHeader.getExtraDataRecords() == null || localFileHeader.getExtraDataRecords().size() <= 0) {
+ return;
+ }
+
+ AESExtraDataRecord aesExtraDataRecord = readAESExtraDataRecord(localFileHeader.getExtraDataRecords());
+ if (aesExtraDataRecord != null) {
+ localFileHeader.setAesExtraDataRecord(aesExtraDataRecord);
+ localFileHeader.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);
+ }
+ }
+
+ /**
+ * Reads AES Extra Data Record
+ * @param extraDataRecords
+ * @return {@link AESExtraDataRecord}
+ * @throws ZipException
+ */
+ private AESExtraDataRecord readAESExtraDataRecord(ArrayList extraDataRecords) throws ZipException {
+
+ if (extraDataRecords == null) {
+ return null;
+ }
+
+ for (int i = 0; i < extraDataRecords.size(); i++) {
+ ExtraDataRecord extraDataRecord = (ExtraDataRecord)extraDataRecords.get(i);
+ if (extraDataRecord == null) {
+ continue;
+ }
+
+ if (extraDataRecord.getHeader() == InternalZipConstants.AESSIG) {
+
+ if (extraDataRecord.getData() == null) {
+ throw new ZipException("corrput AES extra data records");
+ }
+
+ AESExtraDataRecord aesExtraDataRecord = new AESExtraDataRecord();
+
+ aesExtraDataRecord.setSignature(InternalZipConstants.AESSIG);
+ aesExtraDataRecord.setDataSize(extraDataRecord.getSizeOfData());
+
+ byte[] aesData = extraDataRecord.getData();
+ aesExtraDataRecord.setVersionNumber(Raw.readShortLittleEndian(aesData, 0));
+ byte[] vendorIDBytes = new byte[2];
+ System.arraycopy(aesData, 2, vendorIDBytes, 0, 2);
+ aesExtraDataRecord.setVendorID(new String(vendorIDBytes));
+ aesExtraDataRecord.setAesStrength((int)(aesData[4] & 0xFF));
+ aesExtraDataRecord.setCompressionMethod(Raw.readShortLittleEndian(aesData, 5));
+
+ return aesExtraDataRecord;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Reads buf length of bytes from the input stream to buf
+ * @param zip4jRaf
+ * @param buf
+ * @return byte array
+ * @throws ZipException
+ */
+ private byte[] readIntoBuff(RandomAccessFile zip4jRaf, byte[] buf) throws ZipException {
+ try {
+ if (zip4jRaf.read(buf, 0, buf.length) != -1) {
+ return buf;
+ } else {
+ throw new ZipException("unexpected end of file when reading short buff");
+ }
+ } catch (IOException e) {
+ throw new ZipException("IOException when reading short buff", e);
+ }
+ }
+
+ /**
+ * Returns a long byte from an int byte by appending last 4 bytes as 0's
+ * @param intByte
+ * @return byte array
+ * @throws ZipException
+ */
+ private byte[] getLongByteFromIntByte(byte[] intByte) throws ZipException {
+ if (intByte == null) {
+ throw new ZipException("input parameter is null, cannot expand to 8 bytes");
+ }
+
+ if (intByte.length != 4) {
+ throw new ZipException("invalid byte length, cannot expand to 8 bytes");
+ }
+
+ byte[] longBuff = {intByte[0], intByte[1], intByte[2], intByte[3], 0, 0, 0, 0};
+ return longBuff;
+ }
+}
diff --git a/src/net/lingala/zip4j/core/HeaderWriter.java b/src/net/lingala/zip4j/core/HeaderWriter.java
new file mode 100644
index 0000000..79b2ab4
--- /dev/null
+++ b/src/net/lingala/zip4j/core/HeaderWriter.java
@@ -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;
+ }
+
+}
\ No newline at end of file
diff --git a/src/net/lingala/zip4j/core/ZipFile.java b/src/net/lingala/zip4j/core/ZipFile.java
new file mode 100644
index 0000000..796e8e2
--- /dev/null
+++ b/src/net/lingala/zip4j/core/ZipFile.java
@@ -0,0 +1,1041 @@
+/*
+* 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.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.lingala.zip4j.exception.ZipException;
+import net.lingala.zip4j.exception.ZipExceptionConstants;
+import net.lingala.zip4j.io.ZipInputStream;
+import net.lingala.zip4j.model.FileHeader;
+import net.lingala.zip4j.model.UnzipParameters;
+import net.lingala.zip4j.model.ZipModel;
+import net.lingala.zip4j.model.ZipParameters;
+import net.lingala.zip4j.progress.ProgressMonitor;
+import net.lingala.zip4j.unzip.Unzip;
+import net.lingala.zip4j.util.ArchiveMaintainer;
+import net.lingala.zip4j.util.InternalZipConstants;
+import net.lingala.zip4j.util.Zip4jUtil;
+import net.lingala.zip4j.zip.ZipEngine;
+
+/**
+ * Base class to handle zip files. Some of the operations supported
+ * in this class are:
+ *
+ * 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();
+ }
+}
diff --git a/src/net/lingala/zip4j/io/PartInputStream.java b/src/net/lingala/zip4j/io/PartInputStream.java
new file mode 100644
index 0000000..bdfdff9
--- /dev/null
+++ b/src/net/lingala/zip4j/io/PartInputStream.java
@@ -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;
+ }
+}
diff --git a/src/net/lingala/zip4j/io/SplitOutputStream.java b/src/net/lingala/zip4j/io/SplitOutputStream.java
new file mode 100644
index 0000000..2f4767d
--- /dev/null
+++ b/src/net/lingala/zip4j/io/SplitOutputStream.java
@@ -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;
+ }
+}
diff --git a/src/net/lingala/zip4j/io/ZipInputStream.java b/src/net/lingala/zip4j/io/ZipInputStream.java
new file mode 100644
index 0000000..9257242
--- /dev/null
+++ b/src/net/lingala/zip4j/io/ZipInputStream.java
@@ -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);
+ }
+
+}
diff --git a/src/net/lingala/zip4j/io/ZipOutputStream.java b/src/net/lingala/zip4j/io/ZipOutputStream.java
new file mode 100644
index 0000000..b00150b
--- /dev/null
+++ b/src/net/lingala/zip4j/io/ZipOutputStream.java
@@ -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);
+ }
+}
diff --git a/src/net/lingala/zip4j/model/AESExtraDataRecord.java b/src/net/lingala/zip4j/model/AESExtraDataRecord.java
new file mode 100644
index 0000000..a7400c5
--- /dev/null
+++ b/src/net/lingala/zip4j/model/AESExtraDataRecord.java
@@ -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;
+ }
+
+}
diff --git a/src/net/lingala/zip4j/model/ArchiveExtraDataRecord.java b/src/net/lingala/zip4j/model/ArchiveExtraDataRecord.java
new file mode 100644
index 0000000..703863b
--- /dev/null
+++ b/src/net/lingala/zip4j/model/ArchiveExtraDataRecord.java
@@ -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;
+ }
+
+}
diff --git a/src/net/lingala/zip4j/model/CentralDirectory.java b/src/net/lingala/zip4j/model/CentralDirectory.java
new file mode 100644
index 0000000..fe40302
--- /dev/null
+++ b/src/net/lingala/zip4j/model/CentralDirectory.java
@@ -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;
+ }
+
+
+}
diff --git a/src/net/lingala/zip4j/model/DataDescriptor.java b/src/net/lingala/zip4j/model/DataDescriptor.java
new file mode 100644
index 0000000..da4bca9
--- /dev/null
+++ b/src/net/lingala/zip4j/model/DataDescriptor.java
@@ -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;
+ }
+
+}
diff --git a/src/net/lingala/zip4j/model/DigitalSignature.java b/src/net/lingala/zip4j/model/DigitalSignature.java
new file mode 100644
index 0000000..75c6e11
--- /dev/null
+++ b/src/net/lingala/zip4j/model/DigitalSignature.java
@@ -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;
+ }
+
+}
diff --git a/src/net/lingala/zip4j/model/EndCentralDirRecord.java b/src/net/lingala/zip4j/model/EndCentralDirRecord.java
new file mode 100644
index 0000000..a3fa690
--- /dev/null
+++ b/src/net/lingala/zip4j/model/EndCentralDirRecord.java
@@ -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;
+ }
+
+}
diff --git a/src/net/lingala/zip4j/model/ExtraDataRecord.java b/src/net/lingala/zip4j/model/ExtraDataRecord.java
new file mode 100644
index 0000000..d688428
--- /dev/null
+++ b/src/net/lingala/zip4j/model/ExtraDataRecord.java
@@ -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;
+ }
+
+}
diff --git a/src/net/lingala/zip4j/model/FileHeader.java b/src/net/lingala/zip4j/model/FileHeader.java
new file mode 100644
index 0000000..cd77cdf
--- /dev/null
+++ b/src/net/lingala/zip4j/model/FileHeader.java
@@ -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;
+ }
+
+
+
+}
diff --git a/src/net/lingala/zip4j/model/LocalFileHeader.java b/src/net/lingala/zip4j/model/LocalFileHeader.java
new file mode 100644
index 0000000..f5bcd4a
--- /dev/null
+++ b/src/net/lingala/zip4j/model/LocalFileHeader.java
@@ -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;
+ }
+
+}
diff --git a/src/net/lingala/zip4j/model/UnzipEngineParameters.java b/src/net/lingala/zip4j/model/UnzipEngineParameters.java
new file mode 100644
index 0000000..e1f0673
--- /dev/null
+++ b/src/net/lingala/zip4j/model/UnzipEngineParameters.java
@@ -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;
+ }
+
+
+
+}
diff --git a/src/net/lingala/zip4j/model/UnzipParameters.java b/src/net/lingala/zip4j/model/UnzipParameters.java
new file mode 100644
index 0000000..6f54370
--- /dev/null
+++ b/src/net/lingala/zip4j/model/UnzipParameters.java
@@ -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;
+ }
+
+
+}
diff --git a/src/net/lingala/zip4j/model/Zip64EndCentralDirLocator.java b/src/net/lingala/zip4j/model/Zip64EndCentralDirLocator.java
new file mode 100644
index 0000000..78393c4
--- /dev/null
+++ b/src/net/lingala/zip4j/model/Zip64EndCentralDirLocator.java
@@ -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;
+ }
+
+
+
+}
diff --git a/src/net/lingala/zip4j/model/Zip64EndCentralDirRecord.java b/src/net/lingala/zip4j/model/Zip64EndCentralDirRecord.java
new file mode 100644
index 0000000..0677fce
--- /dev/null
+++ b/src/net/lingala/zip4j/model/Zip64EndCentralDirRecord.java
@@ -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;
+ }
+
+
+
+}
diff --git a/src/net/lingala/zip4j/model/Zip64ExtendedInfo.java b/src/net/lingala/zip4j/model/Zip64ExtendedInfo.java
new file mode 100644
index 0000000..f9a26f9
--- /dev/null
+++ b/src/net/lingala/zip4j/model/Zip64ExtendedInfo.java
@@ -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;
+ }
+
+
+
+}
diff --git a/src/net/lingala/zip4j/model/ZipModel.java b/src/net/lingala/zip4j/model/ZipModel.java
new file mode 100644
index 0000000..ea91a33
--- /dev/null
+++ b/src/net/lingala/zip4j/model/ZipModel.java
@@ -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;
+ }
+
+}
diff --git a/src/net/lingala/zip4j/model/ZipParameters.java b/src/net/lingala/zip4j/model/ZipParameters.java
new file mode 100644
index 0000000..b39e5f8
--- /dev/null
+++ b/src/net/lingala/zip4j/model/ZipParameters.java
@@ -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
+ * Note: 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;
+ }
+
+}
diff --git a/src/net/lingala/zip4j/progress/ProgressMonitor.java b/src/net/lingala/zip4j/progress/ProgressMonitor.java
new file mode 100644
index 0000000..314144e
--- /dev/null
+++ b/src/net/lingala/zip4j/progress/ProgressMonitor.java
@@ -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;
+ }
+}
diff --git a/src/net/lingala/zip4j/unzip/Unzip.java b/src/net/lingala/zip4j/unzip/Unzip.java
new file mode 100644
index 0000000..4e9be03
--- /dev/null
+++ b/src/net/lingala/zip4j/unzip/Unzip.java
@@ -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;
+ }
+
+}
diff --git a/src/net/lingala/zip4j/unzip/UnzipEngine.java b/src/net/lingala/zip4j/unzip/UnzipEngine.java
new file mode 100644
index 0000000..86d3f89
--- /dev/null
+++ b/src/net/lingala/zip4j/unzip/UnzipEngine.java
@@ -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;
+ }
+}
diff --git a/src/net/lingala/zip4j/unzip/UnzipUtil.java b/src/net/lingala/zip4j/unzip/UnzipUtil.java
new file mode 100644
index 0000000..4a55c22
--- /dev/null
+++ b/src/net/lingala/zip4j/unzip/UnzipUtil.java
@@ -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()));
+ }
+ }
+
+}
diff --git a/src/net/lingala/zip4j/util/ArchiveMaintainer.java b/src/net/lingala/zip4j/util/ArchiveMaintainer.java
new file mode 100644
index 0000000..a532dd2
--- /dev/null
+++ b/src/net/lingala/zip4j/util/ArchiveMaintainer.java
@@ -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;
+ }
+}
diff --git a/src/net/lingala/zip4j/util/CRCUtil.java b/src/net/lingala/zip4j/util/CRCUtil.java
new file mode 100644
index 0000000..f030b69
--- /dev/null
+++ b/src/net/lingala/zip4j/util/CRCUtil.java
@@ -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");
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/net/lingala/zip4j/util/InternalZipConstants.java b/src/net/lingala/zip4j/util/InternalZipConstants.java
new file mode 100644
index 0000000..8e60330
--- /dev/null
+++ b/src/net/lingala/zip4j/util/InternalZipConstants.java
@@ -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;
+}
diff --git a/src/net/lingala/zip4j/util/Raw.java b/src/net/lingala/zip4j/util/Raw.java
new file mode 100644
index 0000000..f4c1348
--- /dev/null
+++ b/src/net/lingala/zip4j/util/Raw.java
@@ -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
+ *
+ * Note: 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;
+ }
+}