package com.limegroup.gnutella.gui;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Enumeration;
import java.util.Properties;
import javax.swing.*;
import com.limegroup.gnutella.bugs.FatalBugManager;
/**
* This class constructs an <tt>Initializer</tt> instance that constructs
* all of the necessary classes for the application.
*/
//2345678|012345678|012345678|012345678|012345678|012345678|012345678|012345678|
public class GUILoader {
/**
* Creates an <tt>Initializer</tt> instance that constructs the
* necessary classes for the application.
*
* @param args the array of command line arguments
*/
public static void load(String args[], Frame frame) {
try {
Java14Notice.showIfNecessary();
sanityCheck();
Initializer.initialize(args, frame);
} catch(StartupFailedException sfe) {
try {
if(frame != null)
frame.dispose();
} catch(Throwable ignored) {}
showCorruptionError(sfe);
System.exit(1);
} catch(Throwable err) {
try {
if(frame != null)
frame.dispose();
} catch(Throwable ignored) {}
try {
FatalBugManager.handleFatalBug(err);
} catch(Throwable t) {
showCorruptionError(err);
System.exit(1);
}
}
}
/**
* Display a standardly formatted internal error message
* coming from the backend.
*
* @param message the message to display to the user
*
* @param err the <tt>Throwable</tt> object containing information
* about the error
*/
private static final void showCorruptionError(Throwable err) {
err.printStackTrace();
final Properties PROPS = System.getProperties();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.println("LimeWire version @version@");
pw.print("Java version ");
pw.print(System.getProperty("java.version", "?"));
pw.print(" from ");
pw.println(System.getProperty("java.vendor", "?"));
pw.print(System.getProperty("os.name", "?"));
pw.print(" v. ");
pw.print(System.getProperty("os.version", "?"));
pw.print(" on ");
pw.println(System.getProperty("os.arch", "?"));
Runtime runtime = Runtime.getRuntime();
pw.println("Free/total memory: "
+runtime.freeMemory()+"/"+runtime.totalMemory());
pw.println();
err.printStackTrace(pw);
pw.println();
pw.println("STARTUP ERROR!");
pw.println();
File propsFile = new File(getUserSettingsDir(), "limewire.props");
Properties props = new Properties();
try {
FileInputStream fis = new FileInputStream(propsFile);
props.load(fis);
fis.close();
// list the properties in the PrintWriter.
props.list(pw);
} catch(FileNotFoundException fnfe) {
} catch(IOException ioe) {
}
pw.println("");
pw.println("");
pw.println("");
pw.println("FILES IN CURRENT DIRECTORY:");
File curDir = new File(PROPS.getProperty("user.dir"));
String[] files = curDir.list();
for(int i=0; i<files.length; i++) {
File curFile = new File(curDir, files[i]);
pw.println(curFile.toString());
pw.println("LAST MODIFIED: "+curFile.lastModified());
pw.println("SIZE: "+curFile.length());
pw.println();
}
pw.flush();
displayError(sw.toString());
}
/**
* Gets the settings directory without using CommonUtils.
*/
private static File getUserSettingsDir() {
File dir = new File(System.getProperty("user.home"));
String os = System.getProperty("os.name").toLowerCase();
if(os.startsWith("mac os") && os.endsWith("x"))
return new File(dir, "/Library/Preferences/LimeWire");
else
return new File(dir, ".limewire");
}
/**
* Displays an internal error with specialized formatting.
*/
private static final void displayError(String error) {
System.out.println("Error: " + error);
final JDialog DIALOG = new JDialog();
DIALOG.setModal(true);
final Dimension DIALOG_DIMENSION = new Dimension(350, 200);
final Dimension INNER_SIZE = new Dimension(300, 150);
DIALOG.setSize(DIALOG_DIMENSION);
JPanel mainPanel = new JPanel();
mainPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
String instr0;
String instr1;
String instr2;
String instr3;
String instr4;
String instr5;
instr0 = "One or more necessary files appear to be invalid.";
instr1 = "This is generally caused by a corrupted installation.";
instr2 = "Please try downloading and installing LimeWire again.";
instr3 = "If the problem persists, please copy and paste the";
instr4 = "message below and send it to: bugs@limewire.com";
instr5 = "Thank you.";
JLabel label0 = new JLabel(instr0);
JLabel label1 = new JLabel(instr1);
JLabel label2 = new JLabel(instr2);
JLabel label3 = new JLabel(instr3);
JLabel label4 = new JLabel(instr4);
JLabel label5 = new JLabel(instr5);
JPanel labelPanel = new JPanel();
JPanel innerLabelPanel = new JPanel();
labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.X_AXIS));
innerLabelPanel.setLayout(new BoxLayout(innerLabelPanel, BoxLayout.Y_AXIS));
innerLabelPanel.add(label0);
innerLabelPanel.add(label1);
innerLabelPanel.add(label2);
innerLabelPanel.add(label3);
innerLabelPanel.add(label4);
innerLabelPanel.add(label5);
innerLabelPanel.add(Box.createVerticalStrut(6));
labelPanel.add(innerLabelPanel);
labelPanel.add(Box.createHorizontalGlue());
final JTextArea textArea = new JTextArea(error);
textArea.selectAll();
textArea.copy();
textArea.setColumns(50);
textArea.setEditable(false);
JScrollPane scroller = new JScrollPane(textArea);
scroller.setBorder(BorderFactory.createEtchedBorder());
scroller.setPreferredSize(INNER_SIZE);
JPanel buttonPanel = new JPanel();
JButton copyButton = new JButton("Copy Report");
copyButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textArea.selectAll();
textArea.copy();
}
});
JButton quitButton = new JButton("Ok");
quitButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
DIALOG.dispose();
}
});
buttonPanel.add(copyButton);
buttonPanel.add(quitButton);
mainPanel.add(labelPanel);
mainPanel.add(scroller);
mainPanel.add(buttonPanel);
DIALOG.getContentPane().add(mainPanel);
DIALOG.pack();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension dialogSize = DIALOG.getSize();
DIALOG.setLocation((screenSize.width - dialogSize.width)/2,
(screenSize.height - dialogSize.height)/2);
DIALOG.setVisible(true);
}
/**
* Determines whether or not specific files exist and are the correct size.
* If they do not exist or are the incorrect size,
* throws MissingResourceException.
*/
private static void sanityCheck() throws StartupFailedException {
File test = new File("gpl.txt");
boolean isCVS = false;
// If the gpl.txt exists, then we're running off of CVS.
if( test.exists() && test.isFile() ) {
isCVS = true;
}
// If it doesn't, we're a production version.
else {
isCVS = false;
}
String root = isCVS ? ".."+File.separator+"lib"+File.separator+"jars" : ".";
File themesJar = new File(root, "themes.jar");
File xercesJar = new File(root, "xerces.jar");
File updateVer = new File("update.ver");
File xmlWar = new File("xml.war");
File messagesBundlesJar = new File("MessagesBundles.jar");
File id3v2Jar = new File(root, "id3v2.jar");
if( !isCVS && (!themesJar.exists() || !themesJar.isFile()) )
throw new StartupFailedException("invalid themes.jar");
if( !updateVer.exists() || !updateVer.isFile() )
throw new StartupFailedException("invalid update.ver");
if( !xercesJar.exists() || !xercesJar.isFile())
throw new StartupFailedException("invalid xerces.jar");
if( !xmlWar.exists() || !xmlWar.isFile() )
throw new StartupFailedException("invalid xml.war");
if( !isCVS && (!messagesBundlesJar.exists() || !messagesBundlesJar.isFile()) )
throw new StartupFailedException("invalid MessagesBundles.jar");
if( !id3v2Jar.exists() || !id3v2Jar.isFile() )
throw new StartupFailedException("invalid id3v2.jar");
// Then do more advanced hash checks.
try {
verifyHashes(root);
} catch(IOException e) {
throw new StartupFailedException(e.getMessage());
}
}
/**
* Loads up a file 'hashes' and verifies that all the files/hashes
* in it match the hashes we have.
*/
private static void verifyHashes(String root) throws IOException, StartupFailedException {
// Load up the file with the hashes.
Properties props = new Properties();
FileInputStream fis = null;
try {
fis = new FileInputStream(new File(root, "hashes"));
props.load(fis);
} finally {
if( fis != null ) {
try {
fis.close();
fis = null;
} catch(IOException ignored) {}
}
}
//Iterate through each expected hash to verify that
//the files on disk are not corrupted.
Enumeration names = props.propertyNames();
while(names.hasMoreElements()) {
String name = (String)names.nextElement();
String storedHash = props.getProperty(name);
String realHash = hash(new File(root, name));
if(!realHash.equals(storedHash)) {
throw new StartupFailedException(
"file [" + name + "] has hash of [" + realHash +
"] instead of expected [" + storedHash + "]");
}
}
}
/**
* Determines the MD5 hash of the specified file.
*/
private static String hash(File f) throws IOException {
FileInputStream fis = null;
try {
fis = new FileInputStream(f);
MessageDigest md = null;
try {
md = MessageDigest.getInstance("MD5");
} catch(NoSuchAlgorithmException e) {
throw new IOException("Unknown algorithm: MD5");
}
byte[] buffer = new byte[16384];
int read;
while ((read=fis.read(buffer)) != -1) {
md.update(buffer, 0, read);
}
return toHexString(md.digest());
} finally {
if( fis != null ) {
try {
fis.close();
} catch(IOException ignored) {}
}
}
}
/**
* Converts a 16-byte array of unsigned bytes
* to a 32 character hex string.
*/
private static String toHexString(byte[] data) {
StringBuffer sb = new StringBuffer(32);
for(int i = 0; i < data.length; i++) {
int asInt = ((int)data[i]) & 0x000000FF;
String x = Integer.toHexString(asInt);
if(x.length() == 1)
sb.append("0");
sb.append(x);
}
return sb.toString().toUpperCase();
}
private static class StartupFailedException extends Exception {
StartupFailedException(String msg) {
super(msg);
}
StartupFailedException() {
super();
}
}
}