diff --git a/pom.xml b/pom.xml
index 6c50ae7..d98c7a5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
4.0.0
com.ryanmichela
- Sponge-SSHD
+ sshd
1.3.7
jar
Sponge-SSHD
@@ -56,24 +56,6 @@
0.3.0
-
- org.apache.mina
- mina-core
- 2.1.3
-
-
-
- org.slf4j
- slf4j-api
- 1.7.28
-
-
-
- org.slf4j
- slf4j-jdk14
- 1.7.28
-
-
jline
jline
@@ -83,7 +65,7 @@
org.apache.logging.log4j
log4j-core
- 2.0
+ 2.1
@@ -91,25 +73,6 @@
log4j-api
2.1
-
-
- commons-codec
- commons-codec
- 1.10
- provided
-
-
-
- commons-lang
- commons-lang
- 2.6
-
-
-
- org.projectlombok
- lombok
- 1.18.4
-
org.spongepowered
@@ -123,7 +86,7 @@
clean package
- ${project.name}-${project.version}
+ ${project.artifactId}-${project.version}
@@ -138,60 +101,37 @@
-
-
- pl.project13.maven
- git-commit-id-plugin
- 2.2.5
+
+ maven-assembly-plugin
+ 3.1.1
+
+
+ package
+
+ single
+
+
+
+
+ ${project.artifactId}-${project.version}
+ false
+
+ jar-with-dependencies
+
+
+
-
- ${project.basedir}/.git
-
-
-
-
+
+
org.apache.maven.plugins
- maven-shade-plugin
- 3.2.1
-
-
- package
-
- shade
-
-
- true
-
-
-
- *:*:*:*
-
- META-INF/*.RSA
- META-INF/*.SF
- META-INF/*.DSA
-
-
-
- true
- ${java.io.tmpdir}/dependency-reduced-pom.xml
-
-
-
-
-
-
- org.codehaus.mojo
- templating-maven-plugin
- 1.0-alpha-3
-
-
- filter-src
-
- filter-sources
-
-
-
-
+ maven-compiler-plugin
+ 3.7.0
+
+
+ 1.8
+ true
+
+
diff --git a/src/main/java/com/ryanmichela/sshd/ChatColor.java b/src/main/java/com/ryanmichela/sshd/ChatColor.java
new file mode 100644
index 0000000..0508284
--- /dev/null
+++ b/src/main/java/com/ryanmichela/sshd/ChatColor.java
@@ -0,0 +1,222 @@
+package com.ryanmichela.sshd;
+// The below code was taken from md_5's BungeeCord project - Justin
+/*
+Copyright (c) 2012, md_5. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+The name of the author may not be used to endorse or promote products derived
+from this software without specific prior written permission.
+
+You may not use the software for commercial software hosting services without
+written permission from the author.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * Simplistic enumeration of all supported color values for chat.
+ */
+public enum ChatColor
+{
+ /**
+ * Represents black.
+ */
+ BLACK( '0', "black" ),
+ /**
+ * Represents dark blue.
+ */
+ DARK_BLUE( '1', "dark_blue" ),
+ /**
+ * Represents dark green.
+ */
+ DARK_GREEN( '2', "dark_green" ),
+ /**
+ * Represents dark blue (aqua).
+ */
+ DARK_AQUA( '3', "dark_aqua" ),
+ /**
+ * Represents dark red.
+ */
+ DARK_RED( '4', "dark_red" ),
+ /**
+ * Represents dark purple.
+ */
+ DARK_PURPLE( '5', "dark_purple" ),
+ /**
+ * Represents gold.
+ */
+ GOLD( '6', "gold" ),
+ /**
+ * Represents gray.
+ */
+ GRAY( '7', "gray" ),
+ /**
+ * Represents dark gray.
+ */
+ DARK_GRAY( '8', "dark_gray" ),
+ /**
+ * Represents blue.
+ */
+ BLUE( '9', "blue" ),
+ /**
+ * Represents green.
+ */
+ GREEN( 'a', "green" ),
+ /**
+ * Represents aqua.
+ */
+ AQUA( 'b', "aqua" ),
+ /**
+ * Represents red.
+ */
+ RED( 'c', "red" ),
+ /**
+ * Represents light purple.
+ */
+ LIGHT_PURPLE( 'd', "light_purple" ),
+ /**
+ * Represents yellow.
+ */
+ YELLOW( 'e', "yellow" ),
+ /**
+ * Represents white.
+ */
+ WHITE( 'f', "white" ),
+ /**
+ * Represents magical characters that change around randomly.
+ */
+ MAGIC( 'k', "obfuscated" ),
+ /**
+ * Makes the text bold.
+ */
+ BOLD( 'l', "bold" ),
+ /**
+ * Makes a line appear through the text.
+ */
+ STRIKETHROUGH( 'm', "strikethrough" ),
+ /**
+ * Makes the text appear underlined.
+ */
+ UNDERLINE( 'n', "underline" ),
+ /**
+ * Makes the text italic.
+ */
+ ITALIC( 'o', "italic" ),
+ /**
+ * Resets all previous chat colors or formats.
+ */
+ RESET( 'r', "reset" );
+ /**
+ * The special character which prefixes all chat colour codes. Use this if
+ * you need to dynamically convert colour codes from your custom format.
+ */
+ public static final char COLOR_CHAR = '\u00A7';
+ public static final String ALL_CODES = "0123456789AaBbCcDdEeFfKkLlMmNnOoRr";
+ /**
+ * Pattern to remove all colour codes.
+ */
+ public static final Pattern STRIP_COLOR_PATTERN = Pattern.compile( "(?i)" + String.valueOf( COLOR_CHAR ) + "[0-9A-FK-OR]" );
+ /**
+ * Colour instances keyed by their active character.
+ */
+ private static final Map BY_CHAR = new HashMap();
+ /**
+ * The code appended to {@link #COLOR_CHAR} to make usable colour.
+ */
+ private final char code;
+ /**
+ * This colour's colour char prefixed by the {@link #COLOR_CHAR}.
+ */
+ private final String toString;
+ private final String name;
+
+ public String getName() { return this.name; }
+
+ static
+ {
+ for ( ChatColor colour : values() )
+ {
+ BY_CHAR.put( colour.code, colour );
+ }
+ }
+
+ private ChatColor(char code, String name)
+ {
+ this.code = code;
+ this.name = name;
+ this.toString = new String( new char[]
+ {
+ COLOR_CHAR, code
+ } );
+ }
+
+ @Override
+ public String toString()
+ {
+ return toString;
+ }
+
+ /**
+ * Strips the given message of all color codes
+ *
+ * @param input String to strip of color
+ * @return A copy of the input string, without any coloring
+ */
+ public static String stripColor(final String input)
+ {
+ if ( input == null )
+ {
+ return null;
+ }
+
+ return STRIP_COLOR_PATTERN.matcher( input ).replaceAll( "" );
+ }
+
+ public static String translateAlternateColorCodes(char altColorChar, String textToTranslate)
+ {
+ char[] b = textToTranslate.toCharArray();
+ for ( int i = 0; i < b.length - 1; i++ )
+ {
+ if ( b[i] == altColorChar && ALL_CODES.indexOf( b[i + 1] ) > -1 )
+ {
+ b[i] = ChatColor.COLOR_CHAR;
+ b[i + 1] = Character.toLowerCase( b[i + 1] );
+ }
+ }
+ return new String( b );
+ }
+
+ /**
+ * Get the colour represented by the specified code.
+ *
+ * @param code the code to search for
+ * @return the mapped colour, or null if non exists
+ */
+ public static ChatColor getByChar(char code)
+ {
+ return BY_CHAR.get( code );
+ }
+}
diff --git a/src/main/java/com/ryanmichela/sshd/ConfigPasswordAuthenticator.java b/src/main/java/com/ryanmichela/sshd/ConfigPasswordAuthenticator.java
index 6d5aaa5..00b3911 100644
--- a/src/main/java/com/ryanmichela/sshd/ConfigPasswordAuthenticator.java
+++ b/src/main/java/com/ryanmichela/sshd/ConfigPasswordAuthenticator.java
@@ -23,7 +23,7 @@ public class ConfigPasswordAuthenticator implements PasswordAuthenticator {
String ConfigHash = SshdPlugin.GetInstance().config.configNode.getNode("Credentials", username.trim(), "password").getString();
if (ConfigHash == null)
- SshdPlugin.GetLogger().warn("Config has no such user: " + username);
+ SshdPlugin.GetInstance().logger.warn("Config has no such user: " + username);
else
{
try
@@ -69,7 +69,7 @@ public class ConfigPasswordAuthenticator implements PasswordAuthenticator {
}
}
- SshdPlugin.GetLogger().info("Failed login for " + username + " using " + HashType + "-based password authentication.");
+ SshdPlugin.GetInstance().logger.info("Failed login for " + username + " using " + HashType + "-based password authentication.");
Integer tries = SshdPlugin.GetInstance().LoginRetries;
try
@@ -83,7 +83,7 @@ public class ConfigPasswordAuthenticator implements PasswordAuthenticator {
if (this.FailCounts.get(username) >= tries)
{
this.FailCounts.put(username, 0);
- SshdPlugin.GetLogger().info("Too many failures for " + username + ", disconnecting.");
+ SshdPlugin.GetInstance().logger.info("Too many failures for " + username + ", disconnecting.");
ss.close(true);
}
}
diff --git a/src/main/java/com/ryanmichela/sshd/ConsoleCommandCompleter.java b/src/main/java/com/ryanmichela/sshd/ConsoleCommandCompleter.java
index 717f55c..f487fd0 100644
--- a/src/main/java/com/ryanmichela/sshd/ConsoleCommandCompleter.java
+++ b/src/main/java/com/ryanmichela/sshd/ConsoleCommandCompleter.java
@@ -6,13 +6,23 @@ package com.ryanmichela.sshd;
import jline.console.completer.Completer;
+import org.spongepowered.api.Sponge;
+import org.spongepowered.api.scheduler.SpongeExecutorService;
+
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
-public class ConsoleCommandCompleter //implements Completer
+public class ConsoleCommandCompleter implements Completer
{
-/*
+ private SpongeExecutorService MinecraftExecutor;
+
+ public ConsoleCommandCompleter()
+ {
+ super();
+ this.MinecraftExecutor = Sponge.getScheduler().createSyncExecutor(SshdPlugin.GetInstance());
+ }
+
public int complete(final String buffer, final int cursor, final List candidates)
{
Waitable> waitable = new Waitable>()
@@ -20,12 +30,11 @@ public class ConsoleCommandCompleter //implements Completer
@Override
protected List evaluate()
{
- CommandMap commandMap = ReflectionUtil.getProtectedValue(Bukkit.getServer(), "commandMap");
- return commandMap.tabComplete(Bukkit.getServer().getConsoleSender(), buffer);
+ return Sponge.getCommandManager().getSuggestions(Sponge.getServer().getConsole(), buffer, null);
}
};
- Bukkit.getScheduler().runTask(SshdPlugin.instance, waitable);
+ this.MinecraftExecutor.execute(waitable);
try
{
List offers = waitable.get();
@@ -47,7 +56,7 @@ public class ConsoleCommandCompleter //implements Completer
}
catch (ExecutionException e)
{
- SshdPlugin.instance.getLogger().log(Level.WARNING, "Unhandled exception when tab completing", e);
+ SshdPlugin.GetInstance().logger.warn("Unhandled exception when tab completing", e);
}
catch (InterruptedException e)
{
@@ -55,6 +64,5 @@ public class ConsoleCommandCompleter //implements Completer
}
return cursor;
}
- */
}
diff --git a/src/main/java/com/ryanmichela/sshd/ConsoleCommandFactory.java b/src/main/java/com/ryanmichela/sshd/ConsoleCommandFactory.java
index 7ef9cd9..72a0052 100644
--- a/src/main/java/com/ryanmichela/sshd/ConsoleCommandFactory.java
+++ b/src/main/java/com/ryanmichela/sshd/ConsoleCommandFactory.java
@@ -55,14 +55,14 @@ public class ConsoleCommandFactory implements CommandFactory {
{
try
{
- SshdPlugin.GetLogger()
+ SshdPlugin.GetInstance().logger
.info("[U: " + environment.getEnv().get(Environment.ENV_USER) + "] " + command);
Sponge.getCommandManager().process(Sponge.getServer().getConsole(), command);
}
catch (Exception e)
{
- SshdPlugin.GetLogger().error("Error processing command from SSH -" + e.getMessage());
+ SshdPlugin.GetInstance().logger.error("Error processing command from SSH -" + e.getMessage());
}
finally
{
diff --git a/src/main/java/com/ryanmichela/sshd/ConsoleLogFormatter.java b/src/main/java/com/ryanmichela/sshd/ConsoleLogFormatter.java
index b403c84..f0b2c2c 100644
--- a/src/main/java/com/ryanmichela/sshd/ConsoleLogFormatter.java
+++ b/src/main/java/com/ryanmichela/sshd/ConsoleLogFormatter.java
@@ -9,20 +9,12 @@ import org.fusesource.jansi.Ansi;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.text.SimpleDateFormat;
import java.util.EnumMap;
import java.util.Map;
-import java.util.logging.Formatter;
-import java.util.logging.LogRecord;
-public class ConsoleLogFormatter extends Formatter {
-
- private SimpleDateFormat dateFormat;
- private static final Map replacements = new EnumMap(ChatColor.class);
-
- public ConsoleLogFormatter() {
- this.dateFormat = new SimpleDateFormat("HH:mm:ss");
- }
+public class ConsoleLogFormatter
+{
+ private static final Map replacements = new EnumMap(ChatColor.class);
public static String ColorizeString(String str)
{
@@ -66,43 +58,5 @@ public class ConsoleLogFormatter extends Formatter {
result += Ansi.ansi().reset().toString();
return result;
}
-
- public String format(LogRecord logrecord)
- {
- try
- {
- Class.forName("org.bukkit.craftbukkit.command.ColouredConsoleSender");
- }
- catch (ClassNotFoundException ignored)
- {
- // MEANS WE'RE ON PAPER/TACO/OTHER SHIT
- colorize(logrecord);
- }
- StringBuilder stringbuilder = new StringBuilder();
-
- stringbuilder.append(" [");
- stringbuilder.append(this.dateFormat.format(logrecord.getMillis())).append(" ");
-
- stringbuilder.append(logrecord.getLevel().getName()).append("]: ");
- stringbuilder.append(this.formatMessage(logrecord));
- stringbuilder.append('\n');
- Throwable throwable = logrecord.getThrown();
-
- if (throwable != null)
- {
- StringWriter stringwriter = new StringWriter();
-
- throwable.printStackTrace(new PrintWriter(stringwriter));
- stringbuilder.append(stringwriter.toString());
- }
-
- return stringbuilder.toString().replace("\n", "\r\n");
- }
-
- private void colorize(LogRecord logrecord)
- {
- String result = ColorizeString(logrecord.getMessage());
- logrecord.setMessage(result);
- }
}
diff --git a/src/main/java/com/ryanmichela/sshd/ConsoleShellFactory.java b/src/main/java/com/ryanmichela/sshd/ConsoleShellFactory.java
index 0d876b8..a8b57a5 100644
--- a/src/main/java/com/ryanmichela/sshd/ConsoleShellFactory.java
+++ b/src/main/java/com/ryanmichela/sshd/ConsoleShellFactory.java
@@ -3,7 +3,6 @@ package com.ryanmichela.sshd;
import com.ryanmichela.sshd.ConsoleCommandCompleter;
import com.ryanmichela.sshd.ConsoleLogFormatter;
import com.ryanmichela.sshd.FlushyOutputStream;
-import com.ryanmichela.sshd.FlushyStreamHandler;
import com.ryanmichela.sshd.SshTerminal;
import com.ryanmichela.sshd.SshdPlugin;
import com.ryanmichela.sshd.StreamHandlerAppender;
@@ -34,7 +33,6 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.util.StringTokenizer;
-import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.StreamHandler;
@@ -97,10 +95,9 @@ public class ConsoleShellFactory implements ShellFactory {
{
this.ConsoleReader = new ConsoleReader(in, new FlushyOutputStream(out), new SshTerminal());
this.ConsoleReader.setExpandEvents(true);
- //this.ConsoleReader.addCompleter(new ConsoleCommandCompleter());
+ this.ConsoleReader.addCompleter(new ConsoleCommandCompleter());
- StreamHandler streamHandler = new FlushyStreamHandler(out, new ConsoleLogFormatter(), this.ConsoleReader);
- this.streamHandlerAppender = new StreamHandlerAppender(streamHandler);
+ this.streamHandlerAppender = new StreamHandlerAppender(this.ConsoleReader);
((Logger)LogManager.getRootLogger()).addAppender(this.streamHandlerAppender);
@@ -132,7 +129,7 @@ public class ConsoleShellFactory implements ShellFactory {
printPreamble(this.ConsoleReader);
while (true)
{
- String command = this.ConsoleReader.readLine("\r>", null);
+ String command = this.ConsoleReader.readLine("\r> ", null);
// The user sent CTRL+D to close the shell, terminate the session.
if (command == null)
break;
@@ -153,7 +150,7 @@ public class ConsoleShellFactory implements ShellFactory {
// Hide the mkpasswd command input from other users.
Boolean mkpasswd = command.split(" ")[0].equals("mkpasswd");
- MinecraftExecutor.schedule(() ->
+ MinecraftExecutor.submit(() ->
{
if (SshdPlugin.GetInstance().Mode.equals("RPC") && command.startsWith("rpc"))
{
@@ -168,7 +165,7 @@ public class ConsoleShellFactory implements ShellFactory {
// our plugin and the connected client.
if (!mkpasswd)
{
- SshdPlugin.GetLogger().info("<" + this.Username + "> " + command);
+ SshdPlugin.GetInstance().logger.info("<" + this.Username + "> " + command);
CmdManager.process(Sponge.getServer().getConsole(), command);
}
else
@@ -176,16 +173,16 @@ public class ConsoleShellFactory implements ShellFactory {
CmdManager.process(this.SshdCommandSender, command);
}
}
- }, 0, TimeUnit.SECONDS);
- }
+ });
+ }
}
catch (IOException e)
{
- SshdPlugin.GetLogger().error("Error processing command from SSH", e);
+ SshdPlugin.GetInstance().logger.error("Error processing command from SSH", e);
}
finally
{
- SshdPlugin.GetLogger().info(this.Username + " disconnected from SSH.");
+ SshdPlugin.GetInstance().logger.info(this.Username + " disconnected from SSH.");
callback.onExit(0);
}
}
@@ -203,7 +200,7 @@ public class ConsoleShellFactory implements ShellFactory {
}
catch (FileNotFoundException e)
{
- SshdPlugin.GetLogger().warn("Could not open " + f + ": File does not exist.");
+ SshdPlugin.GetInstance().logger.warn("Could not open " + f + ": File does not exist.");
// Not showing the SSH motd is not a fatal failure, let the session continue.
}
@@ -222,7 +219,7 @@ public class ConsoleShellFactory implements ShellFactory {
cr.println(str + "\r");
cr.println(ConsoleLogFormatter.ColorizeString(Sponge.getServer().getMotd().toPlain()).replaceAll("\n", "\r\n"));
cr.println("\r");
- cr.println("Type 'exit' to exit the shell." + "\r");
+ cr.println("Type 'exit' or CTRL+D to exit the shell." + "\r");
cr.println("===============================================" + "\r");
}
}
diff --git a/src/main/java/com/ryanmichela/sshd/FlushyStreamHandler.java b/src/main/java/com/ryanmichela/sshd/FlushyStreamHandler.java
deleted file mode 100644
index 10c042c..0000000
--- a/src/main/java/com/ryanmichela/sshd/FlushyStreamHandler.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.ryanmichela.sshd;
-
-import jline.console.ConsoleReader;
-import org.apache.sshd.common.SshException;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.logging.*;
-
-/**
- * Copyright 2013 Ryan Michela
- */
-public class FlushyStreamHandler extends StreamHandler {
-
- private ConsoleReader reader;
-
- public FlushyStreamHandler(OutputStream out, Formatter formatter, ConsoleReader reader) {
- super(out, formatter);
- this.reader = reader;
- setLevel(Level.INFO);
- }
-
- @Override
- public synchronized void publish(LogRecord record) {
- record.setMessage(record.getMessage().replace("\n", "\n\r"));
- super.publish(record);
- flush();
- }
-
- @Override
- public synchronized void flush() {
- try {
- reader.print(ConsoleReader.RESET_LINE + "");
- reader.flush();
- super.flush();
- try {
- reader.drawLine();
- } catch (Throwable ex) {
- reader.getCursorBuffer().clear();
- }
- reader.flush();
- super.flush();
- } catch (SshException ex) {
- // do nothing
- } catch (IOException ex) {
- Logger.getLogger(FlushyStreamHandler.class.getName()).log(Level.SEVERE, null, ex);
- }
- }
-}
diff --git a/src/main/java/com/ryanmichela/sshd/MkpasswdCommand.java b/src/main/java/com/ryanmichela/sshd/MkpasswdCommand.java
index 3cf5a54..ca51cd6 100644
--- a/src/main/java/com/ryanmichela/sshd/MkpasswdCommand.java
+++ b/src/main/java/com/ryanmichela/sshd/MkpasswdCommand.java
@@ -35,7 +35,7 @@ public class MkpasswdCommand implements CommandExecutor
.executor((CommandSource source, CommandContext args) -> {
try
{
- source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.PBKDF2_HashPassword(args.getOne("message").get())));
+ source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.PBKDF2_HashPassword(args.getOne("password").get())));
}
catch (Exception e)
{
@@ -53,7 +53,7 @@ public class MkpasswdCommand implements CommandExecutor
.executor((CommandSource source, CommandContext args) -> {
try
{
- source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.BCrypt_HashPassword(args.getOne("message").get())));
+ source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.BCrypt_HashPassword(args.getOne("password").get())));
}
catch (Exception e)
{
@@ -71,7 +71,7 @@ public class MkpasswdCommand implements CommandExecutor
.executor((CommandSource source, CommandContext args) -> {
try
{
- source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.SHA256_HashPassword(args.getOne("message").get())));
+ source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.SHA256_HashPassword(args.getOne("password").get())));
}
catch (Exception e)
{
@@ -82,33 +82,22 @@ public class MkpasswdCommand implements CommandExecutor
})
.build();
- // The plain text encryption method
+ // The plain text "encryption" method
CommandSpec plain = CommandSpec.builder()
.description(Text.of("Plain text password (insecure)"))
.permission("sshd.mkpasswd.plain")
+ .arguments(GenericArguments.remainingJoinedStrings(Text.of("password")))
.executor((CommandSource source, CommandContext args) -> {
source.sendMessage(Text.of("Bro... It's literally your unhashed password."));
return CommandResult.success();
})
.build();
- // The "help" command to show syntax usage
- CommandSpec helpcmd = CommandSpec.builder()
- .description(Text.of("Display's mkpasswd help"))
- .permission("sshd.mkpasswd.help")
- .executor((CommandSource source, CommandContext args) -> {
- source.sendMessage(Text.of("\u00A7a/mkpasswd \u00A7r"));
- source.sendMessage(Text.of("\u00A79Supported Hashes: SHA256, PBKDF2, BCRYPT, PLAIN\u00A7r"));
- return CommandResult.success();
- })
- .build();
-
// the root "mkpasswd" command
cmdspec = CommandSpec.builder()
.description(Text.of("Create an SSHd password using hashes"))
.extendedDescription(Text.of("Supported Hashes: SHA256, PBKDF2, BCRYPT, PLAIN"))
.permission("sshd.mkpasswd")
- .child(helpcmd, "help", "h")
.child(plain, "plain")
.child(sha256, "sha256")
.child(bcrypt, "bcrypt")
@@ -125,7 +114,7 @@ public class MkpasswdCommand implements CommandExecutor
public CommandResult execute(CommandSource src, CommandContext args) throws CommandException
{
// This command doesn't do anything.
- src.sendMessage(Text.of("\u00A7a/mkpasswd \u00A7r"));
+ src.sendMessage(Text.of("\u00A7a/mkpasswd \u00A7r"));
src.sendMessage(Text.of("\u00A79Supported Hashes: SHA256, PBKDF2, BCRYPT, PLAIN\u00A7r"));
return CommandResult.success();
}
diff --git a/src/main/java/com/ryanmichela/sshd/PublicKeyAuthenticator.java b/src/main/java/com/ryanmichela/sshd/PublicKeyAuthenticator.java
index 6de97e9..43fd275 100644
--- a/src/main/java/com/ryanmichela/sshd/PublicKeyAuthenticator.java
+++ b/src/main/java/com/ryanmichela/sshd/PublicKeyAuthenticator.java
@@ -46,7 +46,7 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
}
else
{
- SshdPlugin.GetLogger().info(
+ SshdPlugin.GetInstance().logger.info(
username + " failed authentication via SSH session using key file " + keyFile.getAbsolutePath());
}
@@ -59,7 +59,7 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
if (this.FailCounts.get(username) >= tries)
{
this.FailCounts.put(username, 0);
- SshdPlugin.GetLogger().info("Too many failures for " + username + ", disconnecting.");
+ SshdPlugin.GetInstance().logger.info("Too many failures for " + username + ", disconnecting.");
session.close(true);
}
@@ -67,12 +67,12 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
}
catch (Exception e)
{
- SshdPlugin.GetLogger().error("Failed to process public key " + keyFile.getAbsolutePath() + " " + e.getMessage());
+ SshdPlugin.GetInstance().logger.error("Failed to process public key " + keyFile.getAbsolutePath() + " " + e.getMessage());
}
}
else
{
- SshdPlugin.GetLogger().error("Could not locate public key for " + username
+ SshdPlugin.GetInstance().logger.error("Could not locate public key for " + username
+ ". Make sure the user's key is named the same as their user name "
+ "without a file extension.");
}
diff --git a/src/main/java/com/ryanmichela/sshd/ReflectionUtil.java b/src/main/java/com/ryanmichela/sshd/ReflectionUtil.java
deleted file mode 100644
index 3d8d2d1..0000000
--- a/src/main/java/com/ryanmichela/sshd/ReflectionUtil.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package com.ryanmichela.sshd;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-
-/**
- * Copyright 2013 Ryan Michela
- */
-public class ReflectionUtil {
-
- public static void setProtectedValue(Object o, String field, Object newValue) {
- setProtectedValue(o.getClass(), o, field, newValue);
- }
-
- public static void setProtectedValue(Class c, String field, Object newValue) {
- setProtectedValue(c, null, field, newValue);
- }
-
- public static void setProtectedValue(Class c, Object o, String field, Object newValue) {
- try {
-
- Field f = c.getDeclaredField(field);
-
- f.setAccessible(true);
-
- Field modifiersField = Field.class.getDeclaredField("modifiers");
- modifiersField.setAccessible(true);
- modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL);
-
- f.set(o, newValue);
- } catch (NoSuchFieldException | IllegalAccessException ex) {
- System.out.println("*** " + c.getName() + ":" + ex);
- }
- }
-
- public static T getProtectedValue(Object obj, String fieldName) {
- try {
- Class c = obj.getClass();
- while (c != Object.class) {
- Field[] fields = c.getDeclaredFields();
- for (Field f : fields) {
- if (f.getName() == fieldName) {
- f.setAccessible(true);
- return (T) f.get(obj);
- }
- }
- c = c.getSuperclass();
- }
- System.out.println("*** " + obj.getClass().getName() + ":No such field");
- return null;
- } catch (Exception ex) {
- System.out.println("*** " + obj.getClass().getName() + ":" + ex);
- return null;
- }
- }
-
- public static T getProtectedValue(Class c, String field) {
- try {
- Field f = c.getDeclaredField(field);
- f.setAccessible(true);
- return (T) f.get(c);
- } catch (Exception ex) {
- System.out.println("*** " + c.getName() + ":" + ex);
- return null;
- }
- }
-
- public static Object invokeProtectedMethod(Class c, String method, Object... args) {
- return invokeProtectedMethod(c, null, method, args);
- }
-
- public static Object invokeProtectedMethod(Object o, String method, Object... args) {
- return invokeProtectedMethod(o.getClass(), o, method, args);
- }
-
- public static Object invokeProtectedMethod(Class c, Object o, String method, Object... args) {
- try {
- Class[] pTypes = new Class[args.length];
- for (int i = 0; i < args.length; i++) {
- if (args[i] instanceof Integer) {
- pTypes[i] = int.class;
- } else {
- pTypes[i] = args[i].getClass();
- }
- }
-
- Method m = c.getDeclaredMethod(method, pTypes);
- m.setAccessible(true);
- return m.invoke(o, args);
- } catch (Exception ex) {
- System.out.println("*** " + c.getName() + "." + method + "(): " + ex);
- return null;
- }
- }
-}
diff --git a/src/main/java/com/ryanmichela/sshd/SshdPlugin.java b/src/main/java/com/ryanmichela/sshd/SshdPlugin.java
index 0372d68..342f7ac 100644
--- a/src/main/java/com/ryanmichela/sshd/SshdPlugin.java
+++ b/src/main/java/com/ryanmichela/sshd/SshdPlugin.java
@@ -51,7 +51,7 @@ public class SshdPlugin
private static SshdPlugin instance;
@Inject
- public static Logger logger;
+ public Logger logger;
@Inject
@DefaultConfig(sharedRoot = false)
@@ -71,6 +71,11 @@ public class SshdPlugin
config = new Config();
config.setup();
+ // Make sure our authorized_keys folder exists
+ File authorizedKeys = new File(this.ConfigDir.toFile(), "authorized_keys");
+ if (!authorizedKeys.exists())
+ authorizedKeys.mkdirs();
+
// Now include it in our dealio here
this.Mode = config.configNode.getNode("Mode").getString();
this.PasswordType = config.configNode.getNode("PasswordType").getString();
@@ -99,7 +104,6 @@ public class SshdPlugin
sshd.setHost(this.ListenAddress.equals("all") ? null : this.ListenAddress);
File hostKey = new File(this.ConfigDir.toFile(), "hostkey");
- File authorizedKeys = new File(this.ConfigDir.toFile(), "authorized_keys");
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(hostKey.toPath()));
sshd.setShellFactory(new ConsoleShellFactory());
@@ -109,8 +113,7 @@ public class SshdPlugin
if (this.EnableSFTP)
{
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
- sshd.setFileSystemFactory(
- new VirtualFileSystemFactory(this.ConfigDir.getParent().getParent()));
+ sshd.setFileSystemFactory(new VirtualFileSystemFactory(this.ConfigDir.getParent().getParent()));
}
MkpasswdCommand.BuildCommand();
@@ -133,8 +136,8 @@ public class SshdPlugin
return instance;
}
- public static Logger GetLogger()
+ public Logger GetLogger()
{
- return logger;
+ return this.logger;
}
}
diff --git a/src/main/java/com/ryanmichela/sshd/StreamHandlerAppender.java b/src/main/java/com/ryanmichela/sshd/StreamHandlerAppender.java
index 1af7efe..b8e42e9 100644
--- a/src/main/java/com/ryanmichela/sshd/StreamHandlerAppender.java
+++ b/src/main/java/com/ryanmichela/sshd/StreamHandlerAppender.java
@@ -1,48 +1,75 @@
package com.ryanmichela.sshd;
+import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.ErrorHandler;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import jline.console.ConsoleReader;
+import org.apache.sshd.common.SshException;
+
+import java.io.IOException;
import java.io.Serializable;
import java.util.UUID;
-import java.util.logging.LogRecord;
-import java.util.logging.StreamHandler;
+import java.nio.charset.Charset;
/**
* Copyright 2014 Ryan Michela
*/
-public class StreamHandlerAppender implements Appender {
-
- private StreamHandler streamHandler;
+public class StreamHandlerAppender implements Appender
+{
+ private ConsoleReader console;
private UUID uuid;
+ private PatternLayout MinecraftLayout = PatternLayout.newBuilder().withPattern("%highlightError{[%d{HH:mm:ss} %level] [%logger]: %minecraftFormatting{%msg}%xEx}").build();
+ private PatternLayout MojangLayout = PatternLayout.newBuilder().withPattern("%highlightError{[%d{HH:mm:ss} %level]: %minecraftFormatting{%msg}%xEx}").build();
- public StreamHandlerAppender(StreamHandler streamHandler) {
- this.streamHandler = streamHandler;
+ public StreamHandlerAppender(ConsoleReader console)
+ {
+ this.console = console;
uuid = UUID.randomUUID();
}
@Override
- public void append(LogEvent logEvent) {
- java.util.logging.Level level;
-
- if (logEvent.getLevel().equals(org.apache.logging.log4j.Level.DEBUG)) {
- level = java.util.logging.Level.FINE;
- } else if (logEvent.getLevel().equals(org.apache.logging.log4j.Level.INFO)) {
- level = java.util.logging.Level.INFO;
- } else if (logEvent.getLevel().equals(org.apache.logging.log4j.Level.WARN)) {
- level = java.util.logging.Level.WARNING;
- } else if (logEvent.getLevel().equals(org.apache.logging.log4j.Level.ERROR)) {
- level = java.util.logging.Level.SEVERE;
- } else {
- level = java.util.logging.Level.INFO;
+ public void append(LogEvent logEvent)
+ {
+ if(true) return;
+
+ if (logEvent.getLevel() == Level.DEBUG || logEvent.getLevel() == Level.TRACE)
+ return;
+
+ try
+ {
+ // Delete the jline's `> ` character
+ this.console.print(ConsoleReader.BACKSPACE + "" + ConsoleReader.BACKSPACE);
+ // Print our message
+ if (logEvent.getLoggerName().matches("net\\.minecraft\\..*|com\\.mojang\\..*"))
+ this.console.println(ConsoleLogFormatter.ColorizeString(this.MojangLayout.toSerializable(logEvent)).replaceAll("\n", "\r\n") + "\r");
+ else
+ this.console.println(ConsoleLogFormatter.ColorizeString(this.MinecraftLayout.toSerializable(logEvent)).replaceAll("\n", "\r\n") + "\r");
+ // Reset the console (colors, formatting, etc)
+ this.console.print(ConsoleReader.RESET_LINE + "");
+ try
+ {
+ // Attempt to draw new console line
+ this.console.drawLine();
+ }
+ catch (Throwable ex)
+ {
+ this.console.getCursorBuffer().clear();
+ }
+ // Push it to the end user.
+ this.console.flush();
+ }
+ catch (SshException ex)
+ {
+ // do nothing
+ }
+ catch (IOException ex)
+ {
+ ex.printStackTrace();
}
- String message = logEvent.getMessage().getFormattedMessage();
-
-
- LogRecord logRecord = new LogRecord(level, message);
- streamHandler.publish(logRecord);
}
@Override
diff --git a/src/main/java/com/ryanmichela/sshd/implementations/SSHDCommandSender.java b/src/main/java/com/ryanmichela/sshd/implementations/SSHDCommandSender.java
index 204303d..b16a77b 100644
--- a/src/main/java/com/ryanmichela/sshd/implementations/SSHDCommandSender.java
+++ b/src/main/java/com/ryanmichela/sshd/implementations/SSHDCommandSender.java
@@ -4,6 +4,7 @@ import com.ryanmichela.sshd.SshdPlugin;
import com.ryanmichela.sshd.ConsoleShellFactory;
import com.ryanmichela.sshd.ConsoleLogFormatter;
import org.spongepowered.api.command.source.ConsoleSource;
+import org.checkerframework.checker.nullness.Opt;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.channel.MessageChannel;
@@ -13,10 +14,13 @@ import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.service.permission.SubjectReference;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.util.Tristate;
+import jline.console.ConsoleReader;
import java.io.IOException;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.Set;
+import java.util.UUID;
import java.util.logging.Level;
import java.util.Optional;
import java.util.List;
@@ -28,17 +32,19 @@ public class SSHDCommandSender implements ConsoleSource
private Subject subjectDelegate;
// Set by the upstream allocating function
public ConsoleShellFactory.ConsoleShell console;
+ private UUID uuid = UUID.randomUUID();
// This is an override for Sponge to work with the SSH consoles.
+ @Override
public void sendMessage(Text message)
{
- this.sendRawMessage(message.toPlain() + "\r");
+ this.sendRawMessage(message.toPlain());
}
// Back port from Spigot/BungeeCord-style API calls.
public void sendMessage(String message)
{
- this.sendRawMessage(message + "\r");
+ this.sendRawMessage(message);
}
public void sendRawMessage(String message)
@@ -48,8 +54,9 @@ public class SSHDCommandSender implements ConsoleSource
return;
try
{
- this.console.ConsoleReader.println(ConsoleLogFormatter.ColorizeString(message).replace("\n", "\n\r"));
- this.console.ConsoleReader.print(this.console.ConsoleReader.RESET_LINE + "");
+ this.console.ConsoleReader.print(ConsoleReader.BACKSPACE + "" + ConsoleReader.BACKSPACE);
+ this.console.ConsoleReader.println(ConsoleLogFormatter.ColorizeString(message).replaceAll("\n", "\n\r") + "\r");
+ this.console.ConsoleReader.print(ConsoleReader.RESET_LINE + "");
this.console.ConsoleReader.flush();
try
{
@@ -63,7 +70,7 @@ public class SSHDCommandSender implements ConsoleSource
}
catch (IOException e)
{
- SshdPlugin.GetLogger().error("Error sending message to SSHDCommandSender", e);
+ SshdPlugin.GetInstance().logger.error("Error sending message to SSHDCommandSender", e);
}
}
@@ -86,75 +93,86 @@ public class SSHDCommandSender implements ConsoleSource
return "SSHD Console";
}
-@Override
- public String getIdentifier() {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public Set getActiveContexts() {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public boolean isSubjectDataPersisted() {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public boolean isChildOf(Set contexts, SubjectReference parent) {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public SubjectData getTransientSubjectData() {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public SubjectData getSubjectData() {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public Tristate getPermissionValue(Set contexts, String permission) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public List getParents(Set contexts) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public Optional getOption(Set contexts, String key) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public SubjectCollection getContainingCollection() {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public Optional getCommandSource() {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public SubjectReference asSubjectReference() {
- // TODO Auto-generated method stub
- return null;
- }
+ @Override
+ public String getIdentifier() {
+ return uuid.toString();
+ }
+
+ @Override
+ public Set getActiveContexts()
+ {
+ // No clue what any of this does but sponge needs it to work with this class. - Justin
+ Set set = new HashSet();
+ set.add(new Context(Context.USER_KEY, "SSHD"));
+ return set;
+ }
+
+ @Override
+ public boolean isSubjectDataPersisted()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isChildOf(Set contexts, SubjectReference parent)
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public SubjectData getTransientSubjectData()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public SubjectData getSubjectData()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Tristate getPermissionValue(Set contexts, String permission)
+ {
+ // We're allowed to view all permissions.
+ return Tristate.TRUE;
+ }
+
+ @Override
+ public List getParents(Set contexts)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Optional getOption(Set contexts, String key)
+ {
+ return Optional.empty();
+ }
+
+ @Override
+ public SubjectCollection getContainingCollection()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Optional getCommandSource()
+ {
+ // TODO Auto-generated method stub
+ return Optional.of(this);
+ }
+
+ @Override
+ public SubjectReference asSubjectReference()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
}
diff --git a/src/main/java/com/ryanmichela/sshd/utils/Config.java b/src/main/java/com/ryanmichela/sshd/utils/Config.java
index 52c0397..6cc58ea 100644
--- a/src/main/java/com/ryanmichela/sshd/utils/Config.java
+++ b/src/main/java/com/ryanmichela/sshd/utils/Config.java
@@ -74,54 +74,36 @@ public class Config
("The IP addresses(s) the SSH server will listen on. Use a comma separated list for multiple addresses.\n" +
"Leave as \"all\" for all addresses.");
this.configNode.getNode("Port").setValue("1025").setComment(
- "# The port the SSH server will listen on. Note that anything above 1024 will require you to run\n" +
- "# the whole minecraft server with elevated privileges, this is not recommended and you should\n" +
- "# use iptables to route packets from a lower port.");
+ "The port the SSH server will listen on. Note that anything above 1024 will require you to run\n" +
+ "the whole minecraft server with elevated privileges, this is not recommended and you should\n" +
+ "use iptables to route packets from a lower port.");
this.configNode.getNode("Mode").setValue("DEFAULT").setComment("Operational mode. Don't touch if you don't know what you're doing. Can be either DEFAULT or RPC");
this.configNode.getNode("EnableSFTP").setValue("true").setComment(
- "# Enable built-in SFTP server or not. You'll be able to connect and upload/download files via SFTP protocol.\n" +
- "# Might be useful for testing purposes as well , i. e. docker containers.");
+ "Enable built-in SFTP server or not. You'll be able to connect and upload/download files via SFTP protocol.\n" +
+ "Might be useful for testing purposes as well , i. e. docker containers.");
this.configNode.getNode("LoginRetries").setValue("3").setComment(
- "# Number of times a person can fail to use an SSH key or enter a password\n" +
- "# before it terminates the connection.");
+ "Number of times a person can fail to use an SSH key or enter a password\n" +
+ "before it terminates the connection.");
this.configNode.getNode("PasswordType").setValue("bcrypt").setComment
("########################################################################################\n" +
- "# By default, only public key authentication is enabled. This is the most secure mode.\n" +
- "# To authorize a user to login with their public key, install their key using the\n" +
- "# OpenSSH authorized_keys file format in the authorized_users directory. Name the key\n" +
- "# file with the user's username and no extension. Note: If you want to let a user have\n" +
- "# many keys, you can append the keys to their file in authorized_users.\n" +
+ "By default, only public key authentication is enabled. This is the most secure mode.\n" +
+ "To authorize a user to login with their public key, install their key using the\n" +
+ "OpenSSH authorized_keys file format in the authorized_users directory. Name the key\n" +
+ "file with the user's username and no extension. Note: If you want to let a user have\n" +
+ "many keys, you can append the keys to their file in authorized_users.\n" +
"########################################################################################\n" +
"For less secure username and password based authentication, complete the sections below.\n" +
"\n" +
- "# Type of hashing to use for the passwords below.\n" +
- "# Options are: PLAIN (insecure), bcrypt, pbkdf2, sha256\n" +
- "#\n" +
- "# You can use the console/in-game command `/mkpasswd [hash] PASSWORD` to\n" +
- "# generate a password hash string then copy it for your passwords below.\n" +
- "# You can also use `/mkpasswd help` to see what algorithms are supported.");
+ "Type of hashing to use for the passwords below.\n" +
+ "Options are: PLAIN (insecure), bcrypt, pbkdf2, sha256\n" +
+ "\n" +
+ "You can use the console/in-game command `/mkpasswd [hash] PASSWORD` to\n" +
+ "generate a password hash string then copy it for your passwords below.\n" +
+ "You can also use `/mkpasswd help` to see what algorithms are supported.");
- this.configNode.getNode("Credentials").setComment("# Associate each username with a password hash (or the password if the PasswordType is set to PLAIN)");
+ this.configNode.getNode("Credentials").setComment("Associate each username with a password hash (or the password if the PasswordType is set to PLAIN)");
this.configNode.getNode("Credentials", "user1", "password").setValue("MySecretPassword");
this.configNode.getNode("Credentials", "user2", "password").setValue("MyBestFriendsPassword");
- //this.configNode.getNode("").setValue("").setComment("");
-
-
- /*
- this.Mode = config.configNode.getNode("Mode").getString();
- this.PasswordType = config.configNode.getNode("PasswordType").getString();
- this.ListenAddress = config.configNode.getNode("ListenAddress").getString();
- this.Port = config.configNode.getNode("Port").getInt();
- this.LoginRetries = config.configNode.getNode("LoginRetries").getInt();
- this.EnableSFTP = config.configNode.getNode("EnableSFTP").getBoolean();
-
- this.configNode.getNode("mysql").setComment("MySQL database for Whitelisting");
- this.configNode.getNode("mysql", "port").setValue(3306).setComment("MySQL server port");
- this.configNode.getNode("mysql", "host").setValue("localhost").setComment("MySQL server to connect to");
- this.configNode.getNode("mysql", "database").setValue("WhitelistSync").setComment("MySQL database for Whitelisting");
- this.configNode.getNode("mysql", "username").setValue("Whitelist").setComment("MySQL username for the database");
- this.configNode.getNode("mysql", "password").setValue("letmein").setComment("MySQL password for the database");
- */
}
}