package org.openslx.dozmod; import java.awt.AWTEvent; import java.awt.Font; import java.awt.Toolkit; import java.awt.event.AWTEventListener; import java.awt.event.ContainerEvent; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.SSLContext; import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIManager; import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.BasicConfigurator; import org.apache.log4j.FileAppender; import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; import org.apache.log4j.spi.LoggingEvent; import org.openslx.dozmod.Config.ProxyMode; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.MainWindow; import org.openslx.dozmod.gui.helper.MessageType; import org.openslx.dozmod.util.ClientVersion; import org.openslx.dozmod.util.FormatHelper; import org.openslx.dozmod.util.ProxyConfigurator; import org.openslx.thrifthelper.ThriftManager; import org.openslx.util.Util; public class App { // Logger private final static Logger LOGGER = Logger.getLogger(App.class); public static final int THRIFT_PORT = 9090; public static final int THRIFT_SSL_PORT = THRIFT_PORT + 1; public static final int THRIFT_TIMEOUT_MS = 15000; private static CountDownLatch proxyLatch = new CountDownLatch(1); private static boolean proxyInitDone = false; private static String masterServerHost = null; private static void setupLogger() { // path to the log file final String logFilePath = Config.getPath() + File.separator + Branding.getServiceName() + ".log"; // check if we had an old log file final File logFile = new File(logFilePath); if (logFile.exists() && !logFile.isDirectory()) { // we have one, rename it to 'bwSuite.log.old' LOGGER.info("renaming old log file"); try { File oldFile = new File(logFilePath + ".old"); oldFile.delete(); logFile.renameTo(oldFile); logFile.delete(); } catch (Exception e) { LOGGER.error("Could not move '" + logFilePath + "' to '" + logFilePath + ".old'", e); } } // add file appender to global logger FileAppender fa = null; try { fa = new FileAppender(new PatternLayout("%d [%F:%M] %m%n"), logFilePath); fa.setEncoding("UTF-8"); // All classes should log to file, configure global file appender. } catch (IOException e) { LOGGER.error("Failed to set logfile path to '" + logFilePath + "': ", e); BasicConfigurator.configure(); return; } final FileAppender ffa = fa; final Pattern re = Pattern.compile("authorization:(\\w|\\+|/|\\s)+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); AppenderSkeleton ap = new AppenderSkeleton() { @Override public boolean requiresLayout() { return ffa.requiresLayout(); } @Override public void close() { ffa.close(); } @Override protected void append(LoggingEvent event) { // TODO Set up filtering properly if ("org.apache.http.wire".equals(event.getLoggerName())) return; String s = event.getRenderedMessage(); if (s.contains("uthorization")) { Matcher m = re.matcher(s); if (m.find()) { s = m.replaceAll("Authorization: ***********"); } } ffa.append(new LoggingEvent(event.getFQNOfLoggerClass(), event.getLogger(), event.getTimeStamp(), event.getLevel(), s, event.getThreadName(), event.getThrowableInformation(), event.getNDC(), event.getLocationInformation(), event.getProperties())); } }; BasicConfigurator.configure(ap); LOGGER.info("Starting logging to: " + logFilePath); LOGGER.info(Branding.getApplicationName() + " Version: " + ClientVersion.getLocalRevision()); LOGGER.info(" " + FormatHelper.longDate(ClientVersion.getLocalRevTimestamp())); LOGGER.info("os.name: " + System.getProperty("os.name")); LOGGER.info("java.specification.vendor: " + System.getProperty("java.specification.vendor")); LOGGER.info("java.specification.name: " + System.getProperty("java.specification.name")); LOGGER.info("java.specification.version: " + System.getProperty("java.specification.version")); LOGGER.info("java.version: " + System.getProperty("java.version")); LOGGER.info("java.vm.version: " + System.getProperty("java.vm.version")); LOGGER.info("java.runtime.version: " + System.getProperty("java.runtime.version")); } public static void main(final String[] args) throws InvocationTargetException, InterruptedException { if (args.length >= 2) { if (args[0].equals("--json")) { writeJsonUpdateFile(args[1]); return; } if (args[0].equals("--dump")) { Branding.dump(args[1]); return; } } if (args.length >= 3) { if (args[0].equals("--pack")) { Branding.pack(args[1], args[2]); return; } } try { Config.init(); } catch (Exception e) { Gui.showMessageBox(null, "Error loading configuration", MessageType.ERROR, LOGGER, e); return; } setupLogger(); // Setup swing style System.setProperty("awt.useSystemAAFontSettings", "on"); System.setProperty("swing.aatext", "true"); try { if (System.getProperty("swing.defaultlaf") != null) { UIManager.setLookAndFeel(System.getProperty("swing.defaultlaf")); } else if(Config.getLookAndFeel() != null) { UIManager.setLookAndFeel(Config.getLookAndFeel()); } else { UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); Config.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); } } catch (Exception e1) { try { LOGGER.error("Something went wrong with the chosen 'LookAndFeel'. Falling back to default 'SystemLookAndFeel'"); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); if(Config.getLookAndFeel() == null) { Config.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } } catch (Exception e) { } } // Adjust font size adjustFontSize(Config.getFontScaling()); // Set up connection to master server final String host; int port; boolean useSsl; if (args.length == 3) { host = args[0]; port = Util.parseInt(args[1], -1); useSsl = Boolean.parseBoolean(args[2]); } else { host = Branding.getMasterServerAddress(); port = THRIFT_SSL_PORT; useSsl = true; } // remember masterserver host masterServerHost = host; // now start the proxy detection if (Config.getProxyMode() == ProxyMode.AUTO) { // Initialize the proxy settings new Thread() { @Override public void run() { ProxyConfigurator.init(); proxyInitDone = true; proxyLatch.countDown(); } }.start(); } else { proxyInitDone = true; proxyLatch.countDown(); } // SSL if (useSsl) { try { SSLContext ctx = SSLContext.getInstance("TLSv1.2"); ctx.init(null, null, null); ThriftManager.setMasterServerAddress(ctx, host, port, THRIFT_TIMEOUT_MS); } catch (final Exception e1) { SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { boolean ret = Gui.showMessageBox(null, "SSL nicht verfügbar. Wollen Sie sich trotzdem unverschlüsselt verbinden?", MessageType.QUESTION_YESNO, LOGGER, e1); if (!ret) { System.exit(1); } } }); useSsl = false; port = port - 1; // This assumes SSL port is always plain port + 1 } } // No "else", might be fallback for failed SSL if (!useSsl) { ThriftManager.setMasterServerAddress(null, host, port, THRIFT_TIMEOUT_MS); } // Setup global thrift connection error handler and then open the GUI SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { if (e instanceof ClassCastException) { // HACK HACK: Endless chains of exceptions from nowhere on Linux after suspend // (seems to be driver/version/model specific) if (e.getMessage().contains("SurfaceData")) return; } Gui.showMessageBox(null, "Ungefangene Ausnahme in Faden " + t.getName() + "\n\n" + "Die Anwendung könnte instabil laufen.\n" + "Zur Sicherheit sollten Sie sie neustarten.", MessageType.WARNING, LOGGER, e); } }); MainWindow.open(); } }); } private static void writeJsonUpdateFile(String destination) { try { ClientVersion.createJson(destination); } catch (IOException e) { LOGGER.error("Failed to write JSON update file to '" + destination + "': ", e); } } private static void adjustFontSize(int percent) { if (percent == 100 || percent <= 0 || percent > 1000) return; final float scaling = 0.01f * (float) percent; int size = determineDefaultFontSize(UIManager.getLookAndFeelDefaults()); if (size == -1) { size = determineDefaultFontSize(UIManager.getDefaults()); } if (size == -1) { size = 12; } final float defaultSize = size; Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() { @Override public void eventDispatched(AWTEvent event) { if (event instanceof ContainerEvent) { ContainerEvent containerEvent = (ContainerEvent) event; if (containerEvent.getID() == ContainerEvent.COMPONENT_ADDED) { Font font = containerEvent.getChild().getFont(); if (font != null && font.getSize2D() <= defaultSize) { containerEvent.getChild().setFont( new Font(font.getName(), font.getStyle(), Math.round(font.getSize2D() * scaling))); } } } } }, AWTEvent.COMPONENT_EVENT_MASK | AWTEvent.CONTAINER_EVENT_MASK); Font tbFont = UIManager.getFont("TitledBorder.font"); if (tbFont != null) { UIManager.put("TitledBorder.font", tbFont.deriveFont(tbFont.getSize2D() * scaling)); } } private static int determineDefaultFontSize(UIDefaults defaults) { if (defaults == null) return -1; int sizes[] = new int[100]; Set keys = new HashSet<>(defaults.keySet()); for (Object key : keys) { if (key == null) continue; Object value = defaults.get(key); if (value == null) continue; if (value instanceof Font) { Font font = (Font) value; if (font.getSize() > 0 && font.getSize() < sizes.length) { sizes[font.getSize()]++; } } } int best = -1; for (int index = 0; index < sizes.length; ++index) { if (best == -1 || sizes[best] < sizes[index]) { best = index; } } return sizes[best]; } /** * Blocks as long as initialization is still going on. Currently this is * just the proxy setup, so this should be used before any network * communication happens. */ public static void waitForInit() { if (proxyInitDone) return; try { proxyLatch.await(); } catch (InterruptedException e) { } } public static synchronized boolean isInitDone() { return proxyInitDone; } public static synchronized String getMasterServerAddress() { return masterServerHost; } }