/*
* Copyright (C) 2016 Matteo Morena
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mamo.vanillaVotifier;
import jline.console.ConsoleReader;
import jline.console.UserInterruptException;
import jline.console.completer.ArgumentCompleter;
import jline.console.completer.ArgumentCompleter.WhitespaceArgumentDelimiter;
import jline.console.completer.Completer;
import jline.console.completer.StringsCompleter;
import mamo.vanillaVotifier.event.Event;
import mamo.vanillaVotifier.event.ServerStoppedEvent;
import mamo.vanillaVotifier.exception.InvalidPrivateKeyFileException;
import mamo.vanillaVotifier.exception.InvalidPublicKeyFileException;
import mamo.vanillaVotifier.exception.PrivateKeyFileNotFoundException;
import mamo.vanillaVotifier.exception.PublicKeyFileNotFoundException;
import mamo.vanillaVotifier.utils.RsaUtils;
import mamo.vanillaVotifier.utils.TimestampUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.json.JSONException;
import org.yaml.snakeyaml.parser.ParserException;
import org.yaml.snakeyaml.scanner.ScannerException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.Writer;
import java.net.BindException;
import java.util.AbstractMap.SimpleEntry;
import java.util.List;
public class VanillaVotifier {
@NotNull protected LanguagePack languagePack;
@NotNull protected Writer writer;
@NotNull protected Logger logger;
@NotNull protected YamlConfig config;
@NotNull protected VotifierServer server;
@NotNull protected Tester tester;
public VanillaVotifier(@NotNull LanguagePack languagePack, @NotNull Writer logWriter, @NotNull File configFile) {
this.languagePack = languagePack;
writer = logWriter;
logger = new Logger(this);
if (configFile.getName().toLowerCase().endsWith(".json")) {
config = new YamlConfig(new File(configFile.getParent(), configFile.getName().substring(0, configFile.getName().length() - ".json".length()) + ".yaml"), new JsonConfig(configFile));
} else {
config = new YamlConfig(configFile);
}
server = new VotifierServer(this);
tester = new Tester(this);
}
public static void main(@Nullable String[] arguments) {
String[] javaVersion = System.getProperty("java.version").split("\\.");
if (!(javaVersion.length >= 1 && Integer.parseInt(javaVersion[0]) >= 1 && javaVersion.length >= 2 && Integer.parseInt(javaVersion[1]) >= 6)) {
System.out.println(("You need at least Java 1.6 to run this program! Current version: " + System.getProperty("java.version") + "."));
return;
}
final ConsoleReader reader;
try {
reader = new ConsoleReader();
} catch (Exception e) { // IOException
System.out.println("Unexpected exception while initializing console reader: " + ExceptionUtils.getStackTrace(e));
return;
}
WhitespaceArgumentDelimiter delimiter = new WhitespaceArgumentDelimiter();
reader.setExpandEvents(false);
reader.setHandleUserInterrupt(true);
reader.addCompleter(new StringsCompleter("help", "info", "stop", "restart", "genkeypair", "showkey", "testquery", "testvote"));
reader.addCompleter(new ArgumentCompleter(delimiter, new StringsCompleter("showkey"), new StringsCompleter("pub", "priv")));
reader.addCompleter(new ArgumentCompleter(delimiter, new StringsCompleter("testquery"), new StringsCompleter("VOTE"), new AnyCompleter(), new AnyCompleter(), new AnyCompleter(), new Completer() {
@Override
public int complete(String buffer, int cursor, List<CharSequence> candidates) {
return new StringsCompleter("'" + TimestampUtils.getTimestamp() + "'").complete(buffer, cursor, candidates);
}
}));
final VanillaVotifier votifier = new VanillaVotifier(new LanguagePack("mamo/vanillaVotifier/lang", "lang"), reader.getOutput(), new File("config.json").exists() && !new File("config.yaml").exists() ? new File("config.json") : new File("config.yaml"));
if (!(votifier.loadConfig() && votifier.startServer())) {
return;
}
while (true) {
String[] args;
try {
String command = reader.readLine();
args = delimiter.delimit(command, command.length()).getArguments();
if (args == null) {
args = new String[]{};
}
for (int i = 0; i < args.length; i++) {
args[i] = StringEscapeUtils.unescapeJava(args[i]);
}
} catch (UserInterruptException e) {
votifier.stopServer();
break;
} catch (Exception e) {
votifier.getLogger().printlnTranslation("s57", new SimpleEntry<String, Object>("exception", e));
votifier.stopServer();
break;
}
if (args.length == 0) {
continue;
}
if (args[0].equals("stop")) {
if (args.length == 1) {
votifier.stopServer();
break;
} else {
votifier.getLogger().printlnTranslation("s17");
}
} else if (args[0].equals("restart")) {
if (args.length == 1) {
votifier.getServer().getListeners().add(new Listener() {
@Override
public void onEvent(@NotNull Event event) {
if (event instanceof ServerStoppedEvent) {
if (votifier.loadConfig() && votifier.startServer()) {
votifier.getServer().getListeners().remove(this);
} else {
System.exit(0);
}
}
}
});
votifier.stopServer();
} else {
votifier.getLogger().printlnTranslation("s56");
}
} else if (args[0].equals("genkeypair")) {
int keySize;
if (args.length == 1) {
keySize = 2048;
} else if (args.length == 2) {
try {
keySize = Integer.parseInt(args[1]);
} catch (NumberFormatException e) {
votifier.getLogger().printlnTranslation("s19");
continue;
}
if (keySize < 512) {
votifier.getLogger().printlnTranslation("s51");
continue;
}
if (keySize > 16384) {
votifier.getLogger().printlnTranslation("s52");
continue;
}
} else {
votifier.getLogger().printlnTranslation("s20");
continue;
}
votifier.getLogger().printlnTranslation("s16");
votifier.getConfig().generateKeyPair(keySize);
try {
votifier.getConfig().saveKeyPair();
} catch (Exception e) {
votifier.getLogger().printlnTranslation("s21", new SimpleEntry<String, Object>("exception", e));
}
votifier.getLogger().printlnTranslation("s23", new SimpleEntry<String, Object>("key", RsaUtils.keyToString(votifier.getConfig().getKeyPair().getPublic())));
} else if (args[0].equals("testvote")) {
if (args.length == 2) {
try {
votifier.getTester().testVote(new Vote("TesterService", args[1], votifier.getConfig().getInetSocketAddress().getAddress().getHostName()));
} catch (Exception e) { // GeneralSecurityException, IOException
votifier.getLogger().printlnTranslation("s27", new SimpleEntry<String, Object>("exception", e));
}
} else {
votifier.getLogger().printlnTranslation("s26");
}
} else if (args[0].equals("testquery")) {
if (args.length >= 2) {
try {
StringBuilder message = new StringBuilder();
for (int i = 1; i < args.length - 1; i++) {
message.append(args[i]).append("\n");
}
message.append(args[args.length - 1]);
votifier.getTester().testQuery(message.toString());
} catch (Exception e) { // GeneralSecurityException, IOException
votifier.getLogger().printlnTranslation("s35", new SimpleEntry<String, Object>("exception", e));
}
} else {
votifier.getLogger().printlnTranslation("s34");
}
} else if (args[0].equals("help")) {
if (args.length == 1) {
votifier.getLogger().printlnTranslation("s31");
} else {
votifier.getLogger().printlnTranslation("s32");
}
} else if (args[0].equals("info")) {
if (args.length == 1) {
votifier.getLogger().printlnTranslation("s40");
} else {
votifier.getLogger().printlnTranslation("s41");
}
} else if (args[0].equals("showkey")) {
if (args.length == 2) {
if (args[1].equals("pub") || args[1].equals("public")) {
votifier.getLogger().println(RsaUtils.keyToString(votifier.getConfig().getKeyPair().getPublic()));
continue;
} else if (args[1].equals("priv") || args[1].equals("private")) {
votifier.getLogger().println(RsaUtils.keyToString(votifier.getConfig().getKeyPair().getPrivate()));
continue;
}
}
votifier.getLogger().printTranslation("s63");
} else {
votifier.getLogger().printlnTranslation("s33");
}
}
}
protected boolean loadConfig() {
getLogger().printlnTranslation("s24");
try {
getConfig().load();
getLogger().printlnTranslation("s25");
return true;
} catch (JSONException e) {
getLogger().printlnTranslation("s45", new SimpleEntry<String, Object>("exception", e.getMessage().replaceAll("'", "\"")));
} catch (ScannerException e) {
getLogger().printlnTranslation("s66", new SimpleEntry<String, Object>("exception", e.getMessage().replaceAll("'", "\"")));
} catch (ParserException e) {
getLogger().printlnTranslation("s66", new SimpleEntry<String, Object>("exception", e.getMessage().replaceAll("'", "\"")));
} catch (PublicKeyFileNotFoundException e) {
getLogger().printlnTranslation("s49");
} catch (PrivateKeyFileNotFoundException e) {
getLogger().printlnTranslation("s50");
} catch (InvalidPublicKeyFileException e) {
getLogger().printlnTranslation("s47");
} catch (InvalidPrivateKeyFileException e) {
getLogger().printlnTranslation("s48");
} catch (FileNotFoundException e) {
if (getConfig().getConfigFile().exists()) {
getLogger().printlnTranslation("s18");
} else {
getLogger().printlnTranslation("s15", new SimpleEntry<String, Object>("exception", e));
}
} catch (Exception e) {
getLogger().printlnTranslation("s15", new SimpleEntry<String, Object>("exception", e));
}
return false;
}
protected boolean startServer() {
try {
getServer().start();
return true;
} catch (BindException e) {
getLogger().printlnTranslation("s38", new SimpleEntry<String, Object>("port", getConfig().getInetSocketAddress().getPort()));
} catch (Exception e) {
getLogger().printlnTranslation("s13", new SimpleEntry<String, Object>("exception", e));
}
return false;
}
protected boolean stopServer() {
try {
getServer().stop();
return true;
} catch (Exception e) {
getLogger().printlnTranslation("s12", new SimpleEntry<String, Object>("exception", e));
}
return false;
}
@NotNull
public Writer getWriter() {
return writer;
}
@NotNull
public LanguagePack getLanguagePack() {
return languagePack;
}
@NotNull
public Logger getLogger() {
return logger;
}
@NotNull
public Config getConfig() {
return config;
}
@NotNull
public VotifierServer getServer() {
return server;
}
public @NotNull Tester getTester() {
return tester;
}
}