blob: 5dee673f9c164491e1ea90250b9eb3cbf673c71d [file] [log] [blame]
package org.eclipse.epp.installer.internal.archive.sfx;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.eclipse.epp.installer.archive.IArchive;
import org.eclipse.epp.installer.archive.IArchiveEntry;
/**
* @author itsuart
* Xored SelfExtracing Archive
*/
public class XSEArchive implements IArchive {
private static class XSEArchiveIterator implements Iterator{
private Iterator iterator;
/**
*
*/
public XSEArchiveIterator(Iterator base) {
iterator = base;
}
/* (non-Javadoc)
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext() {
return iterator.hasNext();
}
/* (non-Javadoc)
* @see java.util.Iterator#next()
*/
public Object next() {
return iterator.next();
}
/* (non-Javadoc)
* @see java.util.Iterator#remove()
*/
public void remove() {
throw new RuntimeException("R/O Iterator");
}
}
private File archiveFile;
public static final int MAX_SUPPORTED_FORMAT_VERSION = 1;
private int archiveflags = 0;
private Hashtable nameEntryMap = new Hashtable();
private Hashtable entryOffsetMap = new Hashtable();
private String autorunPath;
private String autorunArgs;
private ArrayList addedEntries = new ArrayList(); //array of XSEArchiveEntry
private OutputStream tempFilesContentsStream;
private File tempFilesContentsFile;
private ArrayList entries = new ArrayList();
public XSEArchive()
{
}
/**
* @throws IOException
*
*/
public XSEArchive(File archiveFile) throws IOException {
this.archiveFile = archiveFile;
processArchive();
}
private OutputStream getTempContentStream() throws FileNotFoundException, IOException{
if ( tempFilesContentsStream == null)
{
tempFilesContentsFile = File.createTempFile("xse", ".tmp");
tempFilesContentsStream = new FileOutputStream(tempFilesContentsFile);
tempFilesContentsFile.deleteOnExit();
}
return tempFilesContentsStream;
}
public void addContent(String name, InputStream content, long contentSize, boolean usePack200, boolean useZip) throws FileNotFoundException, IOException
{
addContent(name, content, contentSize, -1, usePack200, useZip);
}
public void addContent(String name, InputStream content, long contentSize, long contentTime, boolean usePack200, boolean useZip) throws FileNotFoundException, IOException
{
File srcFile = null;
File dstFile = null;
if (usePack200 && name.endsWith(".jar")) {
System.out.print("pack200...");
//pack200
// IPath path = new Path(name);
String lastName = name;
int i = lastName.lastIndexOf("\\");
if (i != -1) lastName = lastName.substring(i + 1);
i = lastName.lastIndexOf("/");
if (i != -1) lastName = lastName.substring(i + 1);
srcFile = File.createTempFile(lastName, ".jar");
FileOutputStream srcOut = new FileOutputStream(srcFile);
Utils.copyContent(content, srcOut, contentSize);
srcOut.flush();
srcOut.close();
dstFile = File.createTempFile(lastName, ".pack.gz");
String pack200bin = System.getProperty("java.home");
if (!pack200bin.endsWith("\\") && !pack200bin.endsWith("/")) pack200bin = pack200bin + "/";
pack200bin = pack200bin + "bin/pack200";
String[] commandLine = new String[] {
pack200bin,
dstFile.getAbsolutePath(),
srcFile.getAbsolutePath()
};
Process process = Runtime.getRuntime().exec(commandLine);
InputStream is = process.getInputStream();
InputStream es = process.getErrorStream();
try {
process.waitFor();
} catch (InterruptedException e) {
//Ignore
}
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader = new BufferedReader(new InputStreamReader(es));
while ((line = reader.readLine()) != null) {
System.err.println(line);
}
content = new FileInputStream(dstFile);
contentSize = dstFile.length();
name = name + ".pack.gz";
}
if ( contentSize != 0 && content != null)
Utils.copyContent(content, getTempContentStream(), contentSize);
//create entry for file and put it's content to tempFile
XSEArchiveEntry entry = new XSEArchiveEntry(name,contentTime,(int) contentSize);
nameEntryMap.put(name, entry);
addedEntries.add(entry);
if (srcFile != null) {
srcFile.delete();
}
if (dstFile != null) {
dstFile.delete();
}
}
public void setExecutablePath(String executablePath)
{
autorunPath = executablePath;
}
public String getExecutablePath()
{
return autorunPath;
}
public void setExecutableArgs(String arguments)
{
autorunArgs = arguments;
}
public String getExecutableArgs()
{
return autorunArgs;
}
public void setFileRequired(String name)
{
//find it's XSEArchiveEntry
XSEArchiveEntry entry = (XSEArchiveEntry) nameEntryMap.get(name);
if ( entry == null)
throw new IllegalArgumentException(name + " not contained in archive");
entry.markAsRequired();
}
public void setFlags(int flags)
{
archiveflags = flags;
}
public int getFlags()
{
return archiveflags;
}
public void saveToFile(File outFile, boolean usePack200, boolean useZip) throws IOException{
tempFilesContentsStream.flush();
tempFilesContentsStream.close();
tempFilesContentsStream = null;
OutputStream archiveWriter = new FileOutputStream(outFile);
//write header
XSEArchiveHeader header = new XSEArchiveHeader(addedEntries.size());
header.write(archiveWriter);
int bytesWritten = header.getSize();
//write execinfo
Utils.writeInt(archiveWriter,archiveflags);
Utils.writeInt(archiveWriter,addedEntries.size());
Utils.writeInt(archiveWriter,addedEntries.size()+ 1);
bytesWritten += (4 + 4 + 4);
//write FIT
for ( int i = 0; i < addedEntries.size(); i++){
Utils.writeInt(archiveWriter,(int)((XSEArchiveEntry)addedEntries.get(i)).getSize());
Utils.writeInt(archiveWriter, (int)((XSEArchiveEntry)addedEntries.get(i)).getTime());
}
bytesWritten += ( 8 * addedEntries.size());
//write req info
ArrayList reqIds = new ArrayList();
for ( int i = 0; i < addedEntries.size(); i++)
if ( ((XSEArchiveEntry)addedEntries.get(i)).isRequired() )
reqIds.add(new Integer(i));
Utils.writeInt(archiveWriter,reqIds.size());
bytesWritten += 4;
for ( int i = 0; i < reqIds.size(); i++){
Utils.writeInt(archiveWriter, ((Integer)reqIds.get(i)).intValue());
}
bytesWritten += ( 4 * reqIds.size() );
//write string table
for ( int i = 0; i < addedEntries.size(); i++){
String string = ((XSEArchiveEntry)addedEntries.get(i)).getName();
int size = string.length();
Utils.writeInt(archiveWriter,size);
archiveWriter.write(string.getBytes());
bytesWritten += (4 + size);
}
//add executable and arguments
Utils.writeInt(archiveWriter,autorunPath.length());
archiveWriter.write(autorunPath.getBytes());
bytesWritten += (4 + autorunPath.length());
Utils.writeInt(archiveWriter,autorunArgs.length());
archiveWriter.write(autorunArgs.getBytes());
bytesWritten += (4 + autorunArgs.length());
if (useZip) {
System.out.print("Compressing archive data...");
String entryName = outFile.getName();
File tempZipFile = File.createTempFile(entryName, ".zip");
tempZipFile.deleteOnExit();
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tempZipFile));
ZipEntry zipEntry = new ZipEntry(entryName);
zos.putNextEntry(zipEntry);
FileInputStream content = new FileInputStream(tempFilesContentsFile);
Utils.copyContent(content, zos, tempFilesContentsFile.length());
content.close();
zos.finish();
zos.flush();
zos.close();
System.out.println("OK");
Utils.copyContent(new FileInputStream(tempZipFile), archiveWriter, tempZipFile.length());
bytesWritten += tempZipFile.length();
}
else {
//append files
Utils.copyContent(new FileInputStream(tempFilesContentsFile), archiveWriter, tempFilesContentsFile.length());
bytesWritten += tempFilesContentsFile.length();
}
//append footer
archiveWriter.flush();
/*for unknown reason, outFile.length sometimes returns 0
*int packSize = (int) outFile.length();
*/
Utils.writeInt(archiveWriter, bytesWritten);
int crc32 = 0;
Utils.writeInt(archiveWriter,crc32);
archiveWriter.flush();
archiveWriter.close();
//clean up
tempFilesContentsFile = null;
}
/**
* @throws IOException
*
*/
private void processArchive() throws IOException {
RandomAccessFile raf = new RandomAccessFile(archiveFile,"r");
if ( archiveFile.isDirectory())
throw new IllegalArgumentException("XSEArchive container can be only file");
//go to Footer
raf.seek(raf.length() - 8);
int packSize = Utils.readInt(raf);
//go to header
raf.seek(raf.length() - 8 - packSize);
XSEArchiveHeader header = XSEArchiveHeader.fromFile(raf);
if ( header == null ||
! header.getSignature().equals(XSEArchiveHeader.SIGNATURE) ||
header.getVersion() > MAX_SUPPORTED_FORMAT_VERSION )
throw new RuntimeException("Invalid header");
raf.skipBytes(12); //skip execution info
FileInfo[] tempFIs = new FileInfo[header.getFilesCount()];
int offset = 0;
for ( int i = 0; i < header.getFilesCount(); i++)
{
int fileSize = Utils.readInt(raf);
int fileTime = Utils.readInt(raf);
FileInfo currentFi = new FileInfo(fileSize, offset, fileTime);
tempFIs[i] = currentFi;
offset += fileSize;
}
int reqEntriesSize = Utils.readInt(raf);
ArrayList reqList = new ArrayList(reqEntriesSize);
for ( int i = 0; i < reqEntriesSize; i++)
reqList.add(new Integer(Utils.readInt(raf)));
//now read the string table
String[] stringTable = new String[ header.getFilesCount()];
for ( int i = 0; i < header.getFilesCount(); i++)
{
int strSize = Utils.readInt(raf);
byte[] stringBytes = new byte[strSize];
raf.readFully(stringBytes);
String str = new String(stringBytes);
stringTable[i] = str;
}
//read executable and args
int exeStrSize = Utils.readInt(raf);
//raf.readInt();
byte[] stringBytes = new byte[exeStrSize];
raf.readFully(stringBytes);
autorunPath = new String(stringBytes);
int argsStrSize = Utils.readInt(raf);
stringBytes = new byte[argsStrSize];
raf.readFully(stringBytes);
autorunArgs = new String(stringBytes);
//finally we at begining of file section, update all file infos, and fill collections
int fileDatasBeginOffset = (int) raf.getFilePointer();
for ( int i = 0; i < tempFIs.length; i++)
{
long entryOffset = tempFIs[i].offset + fileDatasBeginOffset;
String entryName = stringTable[i];
XSEArchiveEntry entry = new XSEArchiveEntry(entryName, tempFIs[i].time, tempFIs[i].size, reqList.contains(new Integer(i)));
nameEntryMap.put(entryName, entry);
entryOffsetMap.put(entry, new Long(entryOffset));
entries.add(entry);
}
raf.close();
}
/* (non-Javadoc)
* @see org.eclipse.epp.installer.archive.Archive#getInputStream(org.eclipse.epp.installer.archive.ArchiveEntry)
*/
public InputStream getInputStream(IArchiveEntry entry) throws IOException {
if (entry instanceof XSEArchiveEntry) {
XSEArchiveEntry new_name = (XSEArchiveEntry) entry;
//navigate to file start
//create stream
RandomAccessFile raf = new RandomAccessFile(archiveFile,"r");
Long offset = (Long) entryOffsetMap.get(entry);
if ( offset == null)
throw new IllegalArgumentException(entry.getName() + " are not exists in this archive");
raf.skipBytes((int)offset.longValue());
return new BoundedInputStream(raf,entry.getSize());
}
else
throw new IllegalArgumentException(entry.getName());
}
/* (non-Javadoc)
* @see org.eclipse.epp.installer.archive.Archive#getName()
*/
public String getName() {
return archiveFile.getName();
}
public Iterator entries() {
return new XSEArchiveIterator(entries.iterator());
}
public IArchiveEntry getEntry(String name) {
return (IArchiveEntry) nameEntryMap.get(name);
}
/* (non-Javadoc)
* @see com.instantiations.installer.archive.IArchive#close()
*/
public void close() throws IOException {
}
}