![JAR search and dependency download from the Maven repository](/logo.png)
net.lingala.zip4j.core.HeaderReader Maven / Gradle / Ivy
The newest version!
/*
* 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;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy