/******************************************************************************* * Copyright (c) 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Zhao - initial API and implementation *******************************************************************************/ package org.eclipse.php.internal.core.phar; import java.io.*; import java.util.*; import org.eclipse.php.internal.core.phar.digest.Digest; import org.eclipse.php.internal.core.tar.CBZip2InputStreamForPhar; import org.eclipse.php.internal.core.tar.GZIPInputStreamForPhar; public class PharFile { private File file; private int currentIndex; private int manifestLength; private int fileNumber; private int stubLength; private String version; private boolean hasSignature = false; private boolean hasZlibcompression = false; private boolean hasBzipcompression = false; private String alias; private String metadata; private List<PharEntry> pharEntryList = new ArrayList<PharEntry>(); private Map<String, PharEntry> pharEntryMap = new HashMap<String, PharEntry>(); private List<Integer> bytesAfterStub; private BufferedInputStream bis; private PharEntry stubEntry; private PharEntry signatureEntry; public PharFile(PharFile oldPharFile, File file) throws IOException, PharException { this.file = file; if (oldPharFile != null && oldPharFile.file.equals(file)) { copyProperties(oldPharFile); } else { init(); } // initManifest(); } public PharFile(File file) throws IOException, PharException { this(null, file); } private void copyProperties(PharFile oldPharFile) { this.currentIndex = oldPharFile.currentIndex; this.manifestLength = oldPharFile.manifestLength; this.fileNumber = oldPharFile.fileNumber; this.stubLength = oldPharFile.stubLength; this.version = oldPharFile.version; this.hasSignature = oldPharFile.hasSignature; this.hasZlibcompression = oldPharFile.hasZlibcompression; this.hasBzipcompression = oldPharFile.hasBzipcompression; this.alias = oldPharFile.alias; this.metadata = oldPharFile.metadata; this.pharEntryList = oldPharFile.pharEntryList; this.pharEntryMap = oldPharFile.pharEntryMap; // this.bytesAfterStub = oldPharFile.bytesAfterStub; this.stubEntry = oldPharFile.stubEntry; this.signatureEntry = oldPharFile.signatureEntry; } protected void init() throws IOException, PharException { bis = new BufferedInputStream(new FileInputStream(file)); try { getStub(); getManifest(); getEntries(); } finally { bis.close(); } } protected void getEntries() throws IOException, PharException { byte[] buffer; for (int j = 0; j < fileNumber; j++) { buffer = new byte[4]; PharEntry pharEntry = new PharEntry(); read(bis, buffer); int fileNameLength = getInt(buffer); // file Name String fileName = null; if (fileNameLength > 0) { buffer = new byte[fileNameLength]; read(bis, buffer); fileName = getString(buffer); pharEntry.setName(fileName); buffer = new byte[4]; } read(bis, buffer); pharEntry.setSizeByte(buffer); pharEntry.setSize(getInt(buffer)); read(bis, buffer); pharEntry.setTime(getInt(buffer)); read(bis, buffer); pharEntry.setCsize(getInt(buffer)); read(bis, buffer); pharEntry.setCrcByte(buffer); read(bis, buffer); pharEntry.setBitMappedFlag(buffer); read(bis, buffer); int metaFileLength = getInt(buffer); // file Name String metaFileData = null; if (metaFileLength > 0) { buffer = new byte[metaFileLength]; read(bis, buffer); metaFileData = getString(buffer); pharEntry.setMetadata(metaFileData); } pharEntryList.add(pharEntry); pharEntryMap.put(pharEntry.getName(), pharEntry); } for (int j = 0; j < pharEntryList.size(); j++) { PharEntry pharEntry = pharEntryList.get(j); if (j == 0) { pharEntry.setPosition(currentIndex); } else { pharEntry.setPosition(pharEntryList.get(j - 1).getEnd()); } } stubEntry = new PharEntry(); stubEntry.setName(PharConstants.STUB_PATH); // pharEntry.setSizeByte(buffer); stubEntry.setSize(stubLength); stubEntry.setCsize(stubLength); // pharEntry.setCrcByte(buffer); stubEntry.setBitMappedFlag(PharConstants.Default_Entry_Bitmap); stubEntry.setPosition(0); pharEntryList.add(stubEntry); pharEntryMap.put(stubEntry.getName(), stubEntry); if (hasSignature) { signatureEntry = new PharEntry(); signatureEntry.setName(PharConstants.SIGNATURE_PATH); signatureEntry.setBitMappedFlag(PharConstants.Default_Entry_Bitmap); int signatureLength = bis.available(); if (fileNumber == 0) {// no file,so the manifest's end is the // signature's begin signatureEntry.setPosition(currentIndex); } else { signatureEntry.setPosition(pharEntryList.get(fileNumber - 1).getEnd()); PharUtil.skip(bis, pharEntryList.get(fileNumber - 1).getEnd() - currentIndex); signatureLength = bis.available(); } if (signatureLength <= 4) { signatureEntry = null; return; } signatureEntry.setSize(signatureLength); signatureEntry.setCsize(signatureLength); if (signatureLength < 24) { throw new PharException(Messages.Phar_Signature_Corrupted); } else { bis.skip(signatureLength - 8); buffer = new byte[4]; read(bis, buffer); boolean found = false; for (Iterator<Digest> iterator = Digest.DIGEST_MAP.values().iterator(); iterator.hasNext();) { Digest digest = iterator.next(); if (PharUtil.byteArrayEquals(digest.getBitMap(), buffer)) { if (digest.getDigest().digest().length != signatureLength - 8 || !PharUtil.checkSignature(file, digest, signatureEntry.getPosition())) { throw new PharException(Messages.Phar_Signature_Corrupted); } else { found = true; break; } } } if (!found) { throw new PharException(Messages.Phar_Signature_Unsupported); } read(bis, buffer); if (!PharUtil.byteArrayEquals(PharConstants.GBMB, buffer)) { throw new PharException(Messages.Phar_Signature_End); } } pharEntryList.add(signatureEntry); pharEntryMap.put(signatureEntry.getName(), signatureEntry); } } protected void getManifest() throws IOException, PharException { int lineSeparatorLength = 0; bytesAfterStub.add(Integer.valueOf(read(bis))); bytesAfterStub.add(Integer.valueOf(read(bis))); if (bytesAfterStub.get(0).intValue() == PharConstants.R) { lineSeparatorLength++; if (bytesAfterStub.get(1).intValue() == PharConstants.N) { lineSeparatorLength++; } } else if (bytesAfterStub.get(0).intValue() == PharConstants.N) { lineSeparatorLength++; if (bytesAfterStub.get(1).intValue() == PharConstants.R) { lineSeparatorLength++; } } bytesAfterStub = bytesAfterStub.subList(lineSeparatorLength, bytesAfterStub.size()); stubLength = currentIndex - bytesAfterStub.size(); // read(bis); // read(bis); byte[] buffer = new byte[4]; for (int i = 0; i < buffer.length; i++) { if (i < bytesAfterStub.size()) { buffer[i] = ((Integer) bytesAfterStub.get(i)).byteValue(); } else { buffer[i] = (byte) read(bis); check(buffer[i]); } } manifestLength = getInt(buffer); read(bis, buffer); fileNumber = getInt(buffer); buffer = new byte[2]; read(bis, buffer); version = PharUtil.getVersion(buffer); buffer = new byte[4]; read(bis, buffer); if ((buffer[2] & 1) != 0) { hasSignature = true; } if ((buffer[1] & 16) != 0) { hasZlibcompression = true; } if ((buffer[1] & 32) != 0) { hasBzipcompression = true; } read(bis, buffer); int aliaslength = getInt(buffer); if (aliaslength > 0) { buffer = new byte[aliaslength]; read(bis, buffer); alias = getString(buffer); buffer = new byte[4]; } read(bis, buffer); int metadatalength = getInt(buffer); if (metadatalength > 0) { buffer = new byte[metadatalength]; read(bis, buffer); metadata = getString(buffer); // buffer = new byte[4]; } } protected void getStub() throws IOException, PharException { boolean stubHasBeenFound = false; int n = -1; // this is record for whether read a byte from the stream or not int currentByte = -1; bytesAfterStub = new ArrayList<Integer>(); // if currentByte is equal to char '_',we will not read the next byte while (!stubHasBeenFound && (currentByte == PharConstants.Underline || (n = read(bis)) != -1)) { if (n == PharConstants.Underline) { boolean match = false; int j = 1; for (; j < PharConstants.STUB_ENDS.length && n != -1; j++) { if ((n = read(bis)) == PharConstants.STUB_ENDS[j]) { if (j == PharConstants.STUB_ENDS.length - 1) { match = true; } } else { break; } } stubHasBeenFound = match; if (match) { // i = i + ENDS.length; j = 0; match = false; for (; j < PharConstants.STUB_TAIL.length && n != -1; j++) { n = read(bis); bytesAfterStub.add(Integer.valueOf(n)); if (n == PharConstants.STUB_TAIL[j]) { if (j == PharConstants.STUB_TAIL.length - 1) { match = true; } } else { break; } } if (match) { bytesAfterStub.clear(); } } } } if (!stubHasBeenFound) { PharUtil.throwPharException(Messages.Phar_No_Stub_End); } } public static String getString(byte[] subBytes) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < subBytes.length; i++) { sb.append((char) subBytes[i]); } return sb.toString(); } private int read(BufferedInputStream bis, byte[] buffer) throws IOException, PharException { int result = bis.read(buffer); if (result != buffer.length) { PharUtil.throwPharException(Messages.Phar_Corrupted); } currentIndex = currentIndex + result; return result; } private void check(byte b) throws IOException, PharException { if (b == -1) { PharUtil.throwPharException(Messages.Phar_Corrupted); } } public static int getInt(byte[] subBytes) { int result = 0; if (subBytes.length > 0) { result = PharUtil.getPositive(subBytes[subBytes.length - 1]); for (int i = 0; i < subBytes.length - 1; i++) { result = result * 256 + PharUtil.getPositive(subBytes[subBytes.length - 2 - i]); } } return result; } private int read(BufferedInputStream bis) throws IOException { currentIndex++; int result = bis.read(); return result; } public void close() throws IOException { } public PharEntry getEntry(String name) { return pharEntryMap.get(name); } public InputStream getInputStream(PharEntry pharEntry) throws IOException { InputStream result = null; InputStream is = new PharEntryBufferedRandomInputStream(file, pharEntry); if (pharEntry.isCompressed()) { int ctype = pharEntry.getCompressedType(); if (PharConstants.BZ2_COMPRESSED == ctype) { is = new CBZip2InputStreamForPhar(is); } else if (PharConstants.GZ_COMPRESSED == ctype) { is = new GZIPInputStreamForPhar(is); } } result = new BufferedInputStream(is); return result; } public String getName() { return file.getPath(); } public List<PharEntry> getPharEntryList() { return pharEntryList; } public File getFile() { return file; } public void setFile(File file) { this.file = file; } public int getCurrentIndex() { return currentIndex; } public void setCurrentIndex(int currentIndex) { this.currentIndex = currentIndex; } public int getManifestLength() { return manifestLength; } public void setManifestLength(int manifestLength) { this.manifestLength = manifestLength; } public int getFileNumber() { return fileNumber; } public void setFileNumber(int fileNumber) { this.fileNumber = fileNumber; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public boolean isHasSignature() { return hasSignature; } public void setHasSignature(boolean hasSignature) { this.hasSignature = hasSignature; } public boolean isHasZlibcompression() { return hasZlibcompression; } public void setHasZlibcompression(boolean hasZlibcompression) { this.hasZlibcompression = hasZlibcompression; } public boolean isHasBzipcompression() { return hasBzipcompression; } public void setHasBzipcompression(boolean hasBzipcompression) { this.hasBzipcompression = hasBzipcompression; } public String getAlias() { return alias; } public void setAlias(String alias) { this.alias = alias; } public String getMetadata() { return metadata; } public void setMetadata(String metadata) { this.metadata = metadata; } public Map<String, PharEntry> getPharEntryMap() { return pharEntryMap; } public void setPharEntryMap(Map<String, PharEntry> pharEntryMap) { this.pharEntryMap = pharEntryMap; } }