| |
| 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 { |
| |
| } |
| } |