/* * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved. * * http://izpack.org/ http://izpack.codehaus.org/ * * Copyright 2007 Dennis Reil * * 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 com.izforge.izpack.installer; import com.izforge.izpack.*; import com.izforge.izpack.event.InstallerListener; import com.izforge.izpack.io.CorruptVolumeException; import com.izforge.izpack.io.FileSpanningInputStream; import com.izforge.izpack.io.FileSpanningOutputStream; import com.izforge.izpack.io.VolumeNotFoundException; import com.izforge.izpack.panels.NextMediaDialog; import com.izforge.izpack.util.*; import javax.swing.*; import java.awt.*; import java.io.*; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Properties; /** * Unpacker class for a multi volume installation. * * @author Dennis Reil, <izpack@reil-online.de> */ public class MultiVolumeUnpacker extends UnpackerBase { public MultiVolumeUnpacker(AutomatedInstallData idata, AbstractUIProgressHandler handler) { super(idata, handler); } protected File enterNextMediaMessage(String volumename, boolean lastcorrupt) { if (lastcorrupt) { Component parent = null; if ((this.handler != null) && (this.handler instanceof IzPanel)) { parent = ((IzPanel) this.handler).getInstallerFrame(); } JOptionPane.showMessageDialog(parent, idata.langpack .getString("nextmedia.corruptmedia"), idata.langpack .getString("nextmedia.corruptmedia.title"), JOptionPane.ERROR_MESSAGE); } Debug.trace("Enter next media: " + volumename); File nextvolume = new File(volumename); NextMediaDialog nmd = null; while (!nextvolume.exists() || lastcorrupt) { if ((this.handler != null) && (this.handler instanceof IzPanel)) { InstallerFrame installframe = ((IzPanel) this.handler).getInstallerFrame(); nmd = new NextMediaDialog(installframe, idata, volumename); } else { nmd = new NextMediaDialog(null, idata, volumename); } nmd.setVisible(true); String nextmediainput = nmd.getNextMedia(); if (nextmediainput != null) { nextvolume = new File(nextmediainput); } else { Debug.trace("Input from NextMediaDialog was null"); nextvolume = new File(volumename); } // selection equal to last selected which was corrupt? if (!(volumename.equals(nextvolume.getAbsolutePath()) && lastcorrupt)) { lastcorrupt = false; } } return nextvolume; } protected File enterNextMediaMessage(String volumename) { return enterNextMediaMessage(volumename, false); } /** * The run method. */ public void run() { addToInstances(); try { // // Initialisations FileOutputStream out = null; ArrayList<ParsableFile> parsables = new ArrayList<ParsableFile>(); ArrayList<ExecutableFile> executables = new ArrayList<ExecutableFile>(); ArrayList<UpdateCheck> updatechecks = new ArrayList<UpdateCheck>(); List packs = idata.selectedPacks; int npacks = packs.size(); Debug.trace("Unpacker starting"); handler.startAction("Unpacking", npacks); udata = UninstallData.getInstance(); // Custom action listener stuff --- load listeners ---- List[] customActions = getCustomActions(); // Custom action listener stuff --- beforePacks ---- informListeners(customActions, InstallerListener.BEFORE_PACKS, idata, npacks, handler); // vs = new VariableSubstitutor(idata.getVariables()); packs = idata.selectedPacks; npacks = packs.size(); if (npacks == 0) { if (performInterrupted()) { // Interrupt was initiated; perform it. return; } // Custom action listener stuff --- afterPacks ---- informListeners(customActions, InstallerListener.AFTER_PACKS, idata, handler, null); if (performInterrupted()) { // Interrupt was initiated; perform it. return; } // The end :-) handler.stopAction(); return; } InputStream in = MultiVolumeUnpacker.class .getResourceAsStream(FileSpanningOutputStream.VOLUMES_INFO); // get volumes metadata ObjectInputStream metadataobj = new ObjectInputStream(in); // TODO: create MetadataObject int volumes = metadataobj.readInt(); String volumename = metadataobj.readUTF(); Debug.trace("Reading from " + volumes + " volumes with basename " + volumename + " "); metadataobj.close(); String mediadirectory = MultiVolumeInstaller.getMediadirectory(); if ((mediadirectory == null) || (mediadirectory.length() <= 0)) { Debug.trace("Mediadirectory wasn't set."); mediadirectory = System.getProperty("java.io.tmpdir"); // try the temporary // directory } Debug.trace("Using mediadirectory = " + mediadirectory); File volume = new File(mediadirectory + File.separator + volumename); if (!volume.exists()) { volume = enterNextMediaMessage(volume.getAbsolutePath()); } FileSpanningInputStream fin = new FileSpanningInputStream(volume, volumes); // We unpack the selected packs for (int i = 0; i < npacks; i++) { // We get the pack stream int n = idata.allPacks.indexOf(packs.get(i)); in = MultiVolumeUnpacker.class.getResourceAsStream("/packs/pack" + n); // Custom action listener stuff --- beforePack ---- informListeners(customActions, InstallerListener.BEFORE_PACK, packs.get(i), npacks, handler); // find next Entry ObjectInputStream objIn = new ObjectInputStream(in); // We unpack the files int nfiles = objIn.readInt(); // We get the internationalized name of the pack final Pack pack = ((Pack) packs.get(i)); // evaluate condition if (pack.hasCondition() && (rules != null)) { if (!rules.isConditionTrue(pack.getCondition())) { // skip pack, condition is not fullfilled. continue; } } String stepname = pack.name;// the message to be passed to the // installpanel if (langpack != null && !(pack.id == null || "".equals(pack.id))) { final String name = langpack.getString(pack.id); if (name != null && !"".equals(name)) { stepname = name; } } handler.nextStep(stepname, i + 1, nfiles); for (int j = 0; j < nfiles; j++) { // We read the header XPackFile pf = (XPackFile) objIn.readObject(); if (pf.hasCondition() && (rules != null)) { if (!rules.isConditionTrue(pf.getCondition())) { // skip file, condition is false continue; } } if (OsConstraint.oneMatchesCurrentSystem(pf.osConstraints())) { // We translate & build the path String path = IoHelper.translatePath(pf.getTargetPath(), vs); File pathFile = new File(path); File dest = pathFile; if (!pf.isDirectory()) { dest = pathFile.getParentFile(); } if (!dest.exists()) { // If there are custom actions which would be called // at // creating a directory, create it recursively. List fileListeners = customActions[customActions.length - 1]; if (fileListeners != null && fileListeners.size() > 0) { mkDirsWithEnhancement(dest, pf, customActions); } else // Create it in on step. { if (!dest.mkdirs()) { handler.emitError("Error creating directories", "Could not create directory\n" + dest.getPath()); handler.stopAction(); this.result = false; return; } } } if (pf.isDirectory()) { continue; } // Custom action listener stuff --- beforeFile ---- informListeners(customActions, InstallerListener.BEFORE_FILE, pathFile, pf, null); // We add the path to the log, udata.addFile(path, pack.uninstall); handler.progress(j, path); // if this file exists and should not be overwritten, // check // what to do if ((pathFile.exists()) && (pf.override() != PackFile.OVERRIDE_TRUE)) { boolean overwritefile = false; // don't overwrite file if the user said so if (pf.override() != PackFile.OVERRIDE_FALSE) { if (pf.override() == PackFile.OVERRIDE_TRUE) { overwritefile = true; } else if (pf.override() == PackFile.OVERRIDE_UPDATE) { // check mtime of involved files // (this is not 100% perfect, because the // already existing file might // still be modified but the new installed // is just a bit newer; we would // need the creation time of the existing // file or record with which mtime // it was installed...) overwritefile = (pathFile.lastModified() < pf.lastModified()); } else { int def_choice = -1; if (pf.override() == PackFile.OVERRIDE_ASK_FALSE) { def_choice = AbstractUIHandler.ANSWER_NO; } if (pf.override() == PackFile.OVERRIDE_ASK_TRUE) { def_choice = AbstractUIHandler.ANSWER_YES; } int answer = handler.askQuestion(idata.langpack .getString("InstallPanel.overwrite.title") + " - " + pathFile.getName(), idata.langpack .getString("InstallPanel.overwrite.question") + pathFile.getAbsolutePath(), AbstractUIHandler.CHOICES_YES_NO, def_choice); overwritefile = (answer == AbstractUIHandler.ANSWER_YES); } } if (!overwritefile) { if (!pf.isBackReference() && !((Pack) packs.get(i)).loose) { // objIn.skip(pf.length()); } continue; } } // We copy the file out = new FileOutputStream(pathFile); byte[] buffer = new byte[5120]; long bytesCopied = 0; // InputStream pis = objIn; InputStream pis = fin; if (((Pack) packs.get(i)).loose) { pis = new FileInputStream(pf.sourcePath); } // read in the position of this file // long fileposition = objIn.readLong(); long fileposition = pf.getArchivefileposition(); while (fin.getFilepointer() < fileposition) { // we have to skip some bytes Debug.trace("Skipping bytes to get to file " + pathFile.getName() + " (" + fin.getFilepointer() + "<" + fileposition + ") target is: " + (fileposition - fin.getFilepointer())); try { fin.skip(fileposition - fin.getFilepointer()); break; } catch (VolumeNotFoundException vnfe) { File nextmedia = enterNextMediaMessage(vnfe.getVolumename()); fin.setVolumename(nextmedia.getAbsolutePath()); } catch (CorruptVolumeException cve) { Debug.trace("corrupt media found. magic number is not correct"); File nextmedia = enterNextMediaMessage(cve.getVolumename(), true); fin.setVolumename(nextmedia.getAbsolutePath()); } } if (fin.getFilepointer() > fileposition) { Debug.trace("Error, can't access file in pack."); } while (bytesCopied < pf.length()) { try { if (performInterrupted()) { // Interrupt was initiated; perform it. out.close(); if (pis != objIn) { pis.close(); } return; } int maxBytes = (int) Math.min(pf.length() - bytesCopied, buffer.length); int bytesInBuffer = pis.read(buffer, 0, maxBytes); if (bytesInBuffer == -1) { Debug.trace("Unexpected end of stream (installer corrupted?)"); throw new IOException( "Unexpected end of stream (installer corrupted?)"); } out.write(buffer, 0, bytesInBuffer); bytesCopied += bytesInBuffer; } catch (VolumeNotFoundException vnfe) { File nextmedia = enterNextMediaMessage(vnfe.getVolumename()); fin.setVolumename(nextmedia.getAbsolutePath()); } catch (CorruptVolumeException cve) { Debug.trace("corrupt media found. magic number is not correct"); File nextmedia = enterNextMediaMessage(cve.getVolumename(), true); fin.setVolumename(nextmedia.getAbsolutePath()); } } // Cleanings out.close(); // if (pis != objIn) pis.close(); // Set file modification time if specified if (pf.lastModified() >= 0) { pathFile.setLastModified(pf.lastModified()); } // Custom action listener stuff --- afterFile ---- informListeners(customActions, InstallerListener.AFTER_FILE, pathFile, pf, null); } else { if (!pf.isBackReference()) { // objIn.skip(pf.length()); } } } // Load information about parsable files int numParsables = objIn.readInt(); Debug.trace("Looking for parsables"); for (int k = 0; k < numParsables; k++) { ParsableFile pf = null; while (true) { try { pf = (ParsableFile) objIn.readObject(); break; } catch (VolumeNotFoundException vnfe) { File nextmedia = enterNextMediaMessage(vnfe.getVolumename()); fin.setVolumename(nextmedia.getAbsolutePath()); } catch (CorruptVolumeException cve) { Debug.trace("corrupt media found. magic number is not correct"); File nextmedia = enterNextMediaMessage(cve.getVolumename(), true); fin.setVolumename(nextmedia.getAbsolutePath()); } catch (EOFException eofe) { File nextmedia = enterNextMediaMessage(""); fin.setVolumename(nextmedia.getAbsolutePath()); } } if (pf.hasCondition() && (rules != null)) { if (!rules.isConditionTrue(pf.getCondition())) { // skip parsable, condition is false continue; } } pf.path = IoHelper.translatePath(pf.path, vs); Debug.trace("Found parsable: " + pf.path); parsables.add(pf); } // Load information about executable files int numExecutables = objIn.readInt(); Debug.trace("Looking for executables..."); for (int k = 0; k < numExecutables; k++) { ExecutableFile ef = (ExecutableFile) objIn.readObject(); if (ef.hasCondition() && (rules != null)) { if (!rules.isConditionTrue(ef.getCondition())) { // skip, condition is false continue; } } ef.path = IoHelper.translatePath(ef.path, vs); if (null != ef.argList && !ef.argList.isEmpty()) { String arg = null; for (int j = 0; j < ef.argList.size(); j++) { arg = ef.argList.get(j); arg = IoHelper.translatePath(arg, vs); ef.argList.set(j, arg); } } Debug.trace("Found executable: " + ef.path); executables.add(ef); if (ef.executionStage == ExecutableFile.UNINSTALL) { udata.addExecutable(ef); } } // Custom action listener stuff --- uninstall data ---- handleAdditionalUninstallData(udata, customActions); // Load information about updatechecks int numUpdateChecks = objIn.readInt(); Debug.trace("Looking for updatechecks"); for (int k = 0; k < numUpdateChecks; k++) { UpdateCheck uc = (UpdateCheck) objIn.readObject(); Debug.trace("found updatecheck"); updatechecks.add(uc); } // objIn.close(); if (performInterrupted()) { // Interrupt was initiated; perform it. return; } // Custom action listener stuff --- afterPack ---- informListeners(customActions, InstallerListener.AFTER_PACK, packs.get(i), i, handler); } Debug.trace("Trying to parse files"); // We use the scripts parser ScriptParser parser = new ScriptParser(parsables, vs); parser.parseFiles(); Debug.trace("parsed files"); if (performInterrupted()) { // Interrupt was initiated; perform it. return; } Debug.trace("Trying to execute files"); // We use the file executor FileExecutor executor = new FileExecutor(executables); if (executor.executeFiles(ExecutableFile.POSTINSTALL, handler) != 0) { handler.emitError("File execution failed", "The installation was not completed"); this.result = false; Debug.trace("File execution failed"); } if (performInterrupted()) { // Interrupt was initiated; perform it. return; } Debug.trace("Create uninstaller"); // We put the uninstaller (it's not yet complete...) putUninstaller(); Debug.trace("Uninstaller created"); // update checks _after_ uninstaller was put, so we don't delete it Debug.trace("Perform updateChecks"); performUpdateChecks(updatechecks); Debug.trace("updatechecks performed."); if (performInterrupted()) { // Interrupt was initiated; perform it. return; } // Custom action listener stuff --- afterPacks ---- informListeners(customActions, InstallerListener.AFTER_PACKS, idata, handler, null); if (performInterrupted()) { // Interrupt was initiated; perform it. return; } // write installation information writeInstallationInformation(); this.writeConfigInformation(); // The end :-) handler.stopAction(); Debug.trace("Installation complete"); } catch (Exception err) { // TODO: finer grained error handling with useful error messages handler.stopAction(); handler.emitError("An error occured", err.toString()); err.printStackTrace(); Debug.trace("Error while installing: " + err.toString()); this.result = false; } finally { removeFromInstances(); } } protected void writeConfigInformation() { // save the variables Properties installerproperties = idata.getVariables(); Enumeration installerpropertieskeys = installerproperties.keys(); try { String installpath = idata.getVariable("INSTALL_PATH"); PrintWriter pw = new PrintWriter(new FileOutputStream(installpath + File.separator + "installer.properties")); pw.println("# Installer properties, written by MultiVolumeUnpacker."); while (installerpropertieskeys.hasMoreElements()) { String key = (String) installerpropertieskeys.nextElement(); if (key.startsWith("SYSTEM_")) { // skip continue; } else if (key.startsWith("password_")) { // skip continue; } pw.println(key + "=" + installerproperties.getProperty(key)); } pw.flush(); pw.close(); } catch (Exception e) { Debug.trace("Error while writing config information in MultiVolumeUnpacker: " + e.getMessage()); } } }