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.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.ProxyConfigurator;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.util.Util;
public class App {
// Logger
private final static Logger LOGGER = Logger.getLogger(App.class);
private static final String MASTER_SERVER_ADDRESS = "bwlp-masterserver.ruf.uni-freiburg.de";
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 Thread proxyThread = null;
private static String masterServerHost = null;
private static void setupLogger() {
// path to the log file
final String logFilePath = Config.getPath() + File.separator + "bwSuite.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);
// All classes should log to file, configure global file appender.
} catch (IOException e) {
e.printStackTrace();
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) {
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);
}
public static void main(final String[] args) throws InvocationTargetException, InterruptedException {
if (args.length >= 2 && args[0].equals("--json")) {
writeJsonUpdateFile(args[1]);
return;
}
try {
Config.init();
} catch (Exception e) {
Gui.showMessageBox(null, "Error loading configuration", MessageType.ERROR, LOGGER, e);
return;
}
setupLogger();
if (Config.getProxyMode() == ProxyMode.AUTO) {
// Initialize the proxy settings
proxyThread = new Thread() {
@Override
public void run() {
ProxyConfigurator.init();
}
};
proxyThread.start();
}
// Setup swing style
System.setProperty("awt.useSystemAAFontSettings", "on");
System.setProperty("swing.aatext", "true");
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
} catch (Exception e1) {
try {
UIManager.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.getBoolean(args[2]);
} else {
host = MASTER_SERVER_ADDRESS;
port = THRIFT_SSL_PORT;
useSsl = true;
}
// remember masterserver host
masterServerHost = host;
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) {
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) {
e.printStackTrace();
}
}
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<Object> 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 synchronized void waitForInit() {
if (proxyThread == null)
return;
try {
proxyThread.join();
} catch (InterruptedException e) {
}
proxyThread = null;
}
public static synchronized boolean isInitDone() {
return proxyThread == null;
}
public static synchronized String getMasterServerAddress() {
return masterServerHost;
}
}