Well. It compiles. Can't say much.

Code overhaul for the sponge API which is vastly different from
bungeecord port. This still requires lots of work though.
This commit is contained in:
Justin Crawford 2019-10-12 03:46:05 -07:00
parent 67aa5c63ea
commit 41b561a88c
No known key found for this signature in database
GPG Key ID: 0D84DEDBB8EF259C
15 changed files with 441 additions and 916 deletions

180
pom.xml
View File

@ -25,108 +25,116 @@
</repository>
</repositories>
<dependencies>
<dependencies>
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>2.3.0</version>
</dependency>
<!--Just include a *few* things.-->
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>2.3.0</version>
<scope>compile</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-mina</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-mina</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-common</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-common</artifactId>
<version>2.3.0</version>
<scope>compile</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-sftp</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-sftp</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>net.i2p.crypto</groupId>
<artifactId>eddsa</artifactId>
<version>0.3.0</version>
</dependency>
<dependency>
<groupId>net.i2p.crypto</groupId>
<artifactId>eddsa</artifactId>
<version>0.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
<scope>provided</scope>
</dependency>
<!-- include Sponge API -->
<dependency>
<groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId>
<version>7.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Used for getting the git hash -->
<dependency>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>3.0.1</version>
<type>maven-plugin</type>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
<!-- include Sponge API -->
<dependency>
<groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId>
<version>7.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<!-- Set our default goal to be clean and build a package -->
<defaultGoal>clean package</defaultGoal>
<!-- Include our git hash in the final name -->
<finalName>${project.name}-${project.version}-${git.commit.id.abbrev}</finalName>
<finalName>${project.name}-${project.version}</finalName>
<resources>
<resource>
<targetPath>.</targetPath>
<filtering>true</filtering>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>motd.txt</include>
</includes>
</resource>
</resources>
<!-- Plugins -->
<plugins>

View File

@ -19,11 +19,11 @@ public class ConfigPasswordAuthenticator implements PasswordAuthenticator {
public boolean authenticate(String username, String password, ServerSession ss)
{
// Depending on our hash type, we have to try and figure out what we're doing.
String HashType = SshdPlugin.instance.getConfig().getString("PasswordType");
String ConfigHash = SshdPlugin.instance.getConfig().getString("Credentials." + username.trim());
String HashType = SshdPlugin.GetInstance().PasswordType;
String ConfigHash = SshdPlugin.GetInstance().config.configNode.getNode("Credentials", username.trim(), "password").getString();
if (ConfigHash == null)
SshdPlugin.instance.getLogger().warning("Config has no such user: " + username);
SshdPlugin.GetLogger().warn("Config has no such user: " + username);
else
{
try
@ -69,8 +69,8 @@ public class ConfigPasswordAuthenticator implements PasswordAuthenticator {
}
}
SshdPlugin.instance.getLogger().info("Failed login for " + username + " using " + HashType + "-based password authentication.");
Integer tries = SshdPlugin.instance.getConfig().getInt("LoginRetries");
SshdPlugin.GetLogger().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.instance.getLogger().info("Too many failures for " + username + ", disconnecting.");
SshdPlugin.GetLogger().info("Too many failures for " + username + ", disconnecting.");
ss.close(true);
}
}

View File

@ -5,43 +5,56 @@ package com.ryanmichela.sshd;
*/
import jline.console.completer.Completer;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandMap;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
public class ConsoleCommandCompleter implements Completer {
public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
Waitable<List<String>> waitable = new Waitable<List<String>>() {
public class ConsoleCommandCompleter //implements Completer
{
/*
public int complete(final String buffer, final int cursor, final List<CharSequence> candidates)
{
Waitable<List<String>> waitable = new Waitable<List<String>>()
{
@Override
protected List<String> evaluate() {
protected List<String> evaluate()
{
CommandMap commandMap = ReflectionUtil.getProtectedValue(Bukkit.getServer(), "commandMap");
return commandMap.tabComplete(Bukkit.getServer().getConsoleSender(), buffer);
}
};
Bukkit.getScheduler().runTask(SshdPlugin.instance, waitable);
try {
try
{
List<String> offers = waitable.get();
if (offers == null) {
if (offers == null)
{
return cursor;
}
candidates.addAll(offers);
final int lastSpace = buffer.lastIndexOf(' ');
if (lastSpace == -1) {
if (lastSpace == -1)
{
return cursor - buffer.length();
} else {
}
else
{
return cursor - (buffer.length() - lastSpace - 1);
}
} catch (ExecutionException e) {
}
catch (ExecutionException e)
{
SshdPlugin.instance.getLogger().log(Level.WARNING, "Unhandled exception when tab completing", e);
} catch (InterruptedException e) {
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
return cursor;
}
*/
}

View File

@ -5,7 +5,7 @@ import org.apache.sshd.server.command.CommandFactory;
import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
import org.bukkit.Bukkit;
import org.spongepowered.api.Sponge;
import java.io.IOException;
import java.io.InputStream;
@ -51,14 +51,21 @@ public class ConsoleCommandFactory implements CommandFactory {
}
@Override
public void start(ChannelSession cs, Environment environment) throws IOException {
try {
SshdPlugin.instance.getLogger()
public void start(ChannelSession cs, Environment environment) throws IOException
{
try
{
SshdPlugin.GetLogger()
.info("[U: " + environment.getEnv().get(Environment.ENV_USER) + "] " + command);
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command);
} catch (Exception e) {
SshdPlugin.instance.getLogger().severe("Error processing command from SSH -" + e.getMessage());
} finally {
Sponge.getCommandManager().process(Sponge.getServer().getConsole(), command);
}
catch (Exception e)
{
SshdPlugin.GetLogger().error("Error processing command from SSH -" + e.getMessage());
}
finally
{
callback.onExit(0);
}
}

View File

@ -4,7 +4,7 @@ package com.ryanmichela.sshd;
* Copyright 2013 Ryan Michela
*/
import org.bukkit.ChatColor;
import com.ryanmichela.sshd.ChatColor;
import org.fusesource.jansi.Ansi;
import java.io.PrintWriter;

View File

@ -18,7 +18,12 @@ import org.apache.sshd.server.command.Command;
import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
import org.bukkit.Bukkit;
import org.spongepowered.api.MinecraftVersion;
import org.spongepowered.api.Platform;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandManager;
import org.spongepowered.api.plugin.PluginContainer;
import org.spongepowered.api.scheduler.SpongeExecutorService;
import java.io.File;
import java.io.FileNotFoundException;
@ -29,6 +34,7 @@ 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;
@ -91,7 +97,7 @@ 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);
@ -118,7 +124,11 @@ public class ConsoleShellFactory implements ShellFactory {
{
try
{
if (!SshdPlugin.instance.getConfig().getString("Mode").equals("RPC"))
// Get the sponge command manager so we can execute commands.
CommandManager CmdManager = Sponge.getCommandManager();
SpongeExecutorService MinecraftExecutor = Sponge.getScheduler().createSyncExecutor(SshdPlugin.GetInstance());
// Print the SSHD motd.
if (!SshdPlugin.GetInstance().Mode.equals("RPC"))
printPreamble(this.ConsoleReader);
while (true)
{
@ -143,14 +153,13 @@ public class ConsoleShellFactory implements ShellFactory {
// Hide the mkpasswd command input from other users.
Boolean mkpasswd = command.split(" ")[0].equals("mkpasswd");
Bukkit.getScheduler().runTask(
SshdPlugin.instance, () ->
MinecraftExecutor.schedule(() ->
{
if (SshdPlugin.instance.getConfig().getString("Mode").equals("RPC") && command.startsWith("rpc"))
if (SshdPlugin.GetInstance().Mode.equals("RPC") && command.startsWith("rpc"))
{
// NO ECHO NO PREAMBLE AND SHIT
String cmd = command.substring("rpc".length() + 1, command.length());
Bukkit.dispatchCommand(this.SshdCommandSender, cmd);
CmdManager.process(this.SshdCommandSender, cmd);
}
else
{
@ -159,31 +168,31 @@ public class ConsoleShellFactory implements ShellFactory {
// our plugin and the connected client.
if (!mkpasswd)
{
SshdPlugin.instance.getLogger().info("<" + this.Username + "> " + command);
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command);
SshdPlugin.GetLogger().info("<" + this.Username + "> " + command);
CmdManager.process(Sponge.getServer().getConsole(), command);
}
else
{
Bukkit.dispatchCommand(this.SshdCommandSender, command);
CmdManager.process(this.SshdCommandSender, command);
}
}
});
}, 0, TimeUnit.SECONDS);
}
}
catch (IOException e)
{
SshdPlugin.instance.getLogger().log(Level.SEVERE, "Error processing command from SSH", e);
SshdPlugin.GetLogger().error("Error processing command from SSH", e);
}
finally
{
SshdPlugin.instance.getLogger().log(Level.INFO, this.Username + " disconnected from SSH.");
SshdPlugin.GetLogger().info(this.Username + " disconnected from SSH.");
callback.onExit(0);
}
}
private void printPreamble(ConsoleReader cr) throws IOException
{
File f = new File(SshdPlugin.instance.getDataFolder(), "motd.txt");
File f = new File(SshdPlugin.GetInstance().ConfigDir.toFile(), "motd.txt");
try
{
BufferedReader br = new BufferedReader(new FileReader(f));
@ -194,14 +203,24 @@ public class ConsoleShellFactory implements ShellFactory {
}
catch (FileNotFoundException e)
{
SshdPlugin.instance.getLogger().log(Level.WARNING, "Could not open " + f + ": File does not exist.");
SshdPlugin.GetLogger().warn("Could not open " + f + ": File does not exist.");
// Not showing the SSH motd is not a fatal failure, let the session continue.
}
// Doesn't really guarantee our actual system hostname but
// it's better than not having one at all.
cr.println("Connected to: " + InetAddress.getLocalHost().getHostName() + " (" + Bukkit.getServer().getName() + ")\r");
cr.println(ConsoleLogFormatter.ColorizeString(Bukkit.getServer().getMotd()).replaceAll("\n", "\r\n"));
Platform p = Sponge.getPlatform();
MinecraftVersion mv = p.getMinecraftVersion();
PluginContainer pc = p.getContainer(Platform.Component.API);
String str = String.format(
"Connected to: %s -- Minecraft %s (%s %s)",
InetAddress.getLocalHost().getHostName(),
mv.getName(),
pc.getName(),
pc.getVersion().orElse("<Unknown>"));
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("===============================================" + "\r");

View File

@ -10,99 +10,123 @@ import java.util.Arrays;
import com.ryanmichela.sshd.Cryptography;
import com.ryanmichela.sshd.SshdPlugin;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandException;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.CommandContext;
import org.spongepowered.api.command.args.GenericArguments;
import org.spongepowered.api.command.spec.CommandExecutor;
import org.spongepowered.api.command.spec.CommandSpec;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.command.TabCompleteEvent;
import org.spongepowered.api.text.Text;
class MkpasswdCommand implements CommandExecutor
public class MkpasswdCommand implements CommandExecutor
{
// Because Spigot's failed syntax API is really less than ideal (you should be required to add a
// SendSyntax function override), we're just always going to return true even for syntax failures
// as we will handle the syntax message internally. This also lets us send the messages more
// securely to the client without people knowing we're using the command. This prevents password
// or hash leakages from the user to other connected users. Plus this syntax will show how
// to both use the command and what hashes we support which is important for people who don't
// know how to RTFM. - Justin
private void SendSyntax(CommandSource sender, boolean invalid)
private static CommandSpec cmdspec;
public static void BuildCommand()
{
if (invalid)
// So, to send a message in sponge, you must use "Text.of()" for some reason.
sender.sendMessage(Text.of("\u00A7cInvalid Syntax\u00A7r"));
sender.sendMessage(Text.of("\u00A7a/mkpasswd <help|hash> <password>\u00A7r"));
sender.sendMessage(Text.of("\u00A79Supported Hashes: SHA256, PBKDF2, BCRYPT, PLAIN\u00A7r"));
}
CommandSpec pbkdf2 = CommandSpec.builder()
.description(Text.of("PBKDF2 hashed password"))
.permission("sshd.mkpasswd.pbkdf2")
.arguments(GenericArguments.remainingJoinedStrings(Text.of("password")))
.executor((CommandSource source, CommandContext args) -> {
try
{
source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.PBKDF2_HashPassword(args.<String>getOne("message").get())));
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
return CommandResult.success();
})
.build();
public boolean onCommand(CommandSource sender, TabCompleteEvent.Command command, String label, String[] args)
{
// If we're not mkpasswd, just fuck off.
if (!label.equalsIgnoreCase("mkpasswd"))
return false;
CommandSpec bcrypt = CommandSpec.builder()
.description(Text.of("BCrypt hashed password"))
.permission("sshd.mkpasswd.bcrypt")
.arguments(GenericArguments.remainingJoinedStrings(Text.of("password")))
.executor((CommandSource source, CommandContext args) -> {
try
{
source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.BCrypt_HashPassword(args.<String>getOne("message").get())));
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
return CommandResult.success();
})
.build();
String algoritm, password;
try
{
// Stupid bukkit, we have to concatenate the arguments together if they're using
// spaces in their passwords otherwise it won't be as strong as it should be.
algoritm = args[0];
password = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
if (password.trim().isEmpty()) // Shortcut to the catch statement below.
throw new ArrayIndexOutOfBoundsException();
}
catch (ArrayIndexOutOfBoundsException e)
{
// ignore it.
this.SendSyntax(sender, true);
return true;
}
CommandSpec sha256 = CommandSpec.builder()
.description(Text.of("SHA256 hashed password"))
.permission("sshd.mkpasswd.sha256")
.arguments(GenericArguments.remainingJoinedStrings(Text.of("password")))
.executor((CommandSource source, CommandContext args) -> {
try
{
source.sendMessage(Text.of("\u00A79Your Hash: " + Cryptography.SHA256_HashPassword(args.<String>getOne("message").get())));
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
return CommandResult.success();
})
.build();
boolean hasperm = (!(sender instanceof Player)) || ((Player) sender).hasPermission("sshd.mkpasswd");
// The plain text encryption method
CommandSpec plain = CommandSpec.builder()
.description(Text.of("Plain text password (insecure)"))
.permission("sshd.mkpasswd.plain")
.executor((CommandSource source, CommandContext args) -> {
source.sendMessage(Text.of("Bro... It's literally your unhashed password."));
return CommandResult.success();
})
.build();
if (hasperm)
{
try
{
String hash = "";
// Dumb but whatever. Some people are really dense.
if (algoritm.equalsIgnoreCase("PLAIN"))
{
// I mean c'mon...
sender.sendMessage(Text.of("Bro really? it's literally your unencrypted password..."));
return true;
}
else if (algoritm.equalsIgnoreCase("pbkdf2"))
hash = Cryptography.PBKDF2_HashPassword(password);
else if (algoritm.equalsIgnoreCase("bcrypt"))
hash = Cryptography.BCrypt_HashPassword(password);
else if (algoritm.equalsIgnoreCase("sha256"))
hash = Cryptography.SHA256_HashPassword(password);
else
{
this.SendSyntax(sender, !algoritm.equalsIgnoreCase("help"));
return true;
}
// 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 <help|hash> <password>\u00A7r"));
source.sendMessage(Text.of("\u00A79Supported Hashes: SHA256, PBKDF2, BCRYPT, PLAIN\u00A7r"));
return CommandResult.success();
})
.build();
sender.sendMessage(Text.of("\u00A79Your Hash: " + hash + "\u00A7r"));
}
catch (Exception e)
{
// We're console, just print the stack trace.
e.printStackTrace();
sender.sendMessage(Text.of("\u00A7cAn error occured. Please check console for details.\u00A7r"));
}
}
// 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")
.child(pbkdf2, "pbkdf2")
.executor(new MkpasswdCommand())
.build();
return true;
// Register our command with Sponge.
Sponge.getCommandManager().register(SshdPlugin.GetInstance(), cmdspec, "mkpasswd");
}
// so sponge needed this, still figuring out the sponge API ~ Zach
@Override
public CommandResult execute(CommandSource src, CommandContext args) throws CommandException
{
return null;
// This command doesn't do anything.
src.sendMessage(Text.of("\u00A7a/mkpasswd <help|hash> <password>\u00A7r"));
src.sendMessage(Text.of("\u00A79Supported Hashes: SHA256, PBKDF2, BCRYPT, PLAIN\u00A7r"));
return CommandResult.success();
}
}

View File

@ -26,7 +26,7 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
{
byte[] keyBytes = key.getEncoded();
File keyFile = new File(authorizedKeysDir, username);
Integer tries = SshdPlugin.instance.LoginRetries;
Integer tries = SshdPlugin.GetInstance().LoginRetries;
if (keyFile.exists())
{
@ -46,7 +46,7 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
}
else
{
SshdPlugin.instance.getLogger().info(
SshdPlugin.GetLogger().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.instance.getLogger().info("Too many failures for " + username + ", disconnecting.");
SshdPlugin.GetLogger().info("Too many failures for " + username + ", disconnecting.");
session.close(true);
}
@ -67,12 +67,12 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
}
catch (Exception e)
{
SshdPlugin.instance.getLogger().error("Failed to process public key " + keyFile.getAbsolutePath() + " " + e.getMessage());
SshdPlugin.GetLogger().error("Failed to process public key " + keyFile.getAbsolutePath() + " " + e.getMessage());
}
}
else
{
SshdPlugin.instance.getLogger().error("Could not locate public key for " + username
SshdPlugin.GetLogger().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.");
}

View File

@ -3,6 +3,8 @@ package com.ryanmichela.sshd;
import com.ryanmichela.sshd.utils.Config;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.spec.CommandSpec;
import org.spongepowered.api.config.DefaultConfig;
import org.spongepowered.api.config.ConfigDir;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.game.state.GameStartedServerEvent;
@ -20,6 +22,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.logging.Level;
@ -27,7 +30,13 @@ import com.google.inject.Inject;
import org.slf4j.Logger;
import org.spongepowered.api.text.Text;
@Plugin(id = "spongesshd", name = "Sponge-SSHD", version = "1.3.7", description = "Sponge port for Minecraft-SSHD. SSH for your minecraft server!")
@Plugin(
id = "sshd",
name = "Minecraft-SSHD",
version = "1.3.7",
description = "Sponge port for Minecraft-SSHD. SSH for your minecraft server!",
authors={"Justin Crawford", "Zachery Coleman"}
)
public class SshdPlugin
{
String ListenAddress = "";
@ -38,31 +47,29 @@ public class SshdPlugin
String PasswordType = "";
// Credentials
private File modConfigFolder;
CommandSpec MkpasswdCommand = CommandSpec.builder()
.description(Text.of("Make a SSHD password hash"))
.permission("sshd.mkpasswd")
.build();
public File getDataFolder()
{
return modConfigFolder;
}
private SshServer sshd;
public static SshdPlugin instance;
private static SshdPlugin instance;
@Inject
public Logger logger;
public static Logger logger;
@Inject
@DefaultConfig(sharedRoot = false)
public Path DefaultConfig;
@Inject
@ConfigDir(sharedRoot = false)
public Path ConfigDir;
public Config config;
@Listener
public void onServerStart(GameStartedServerEvent event)
{
instance = this;
// Parse our config
config = new Config();
config.setup();
config.setup();
// Now include it in our dealio here
this.Mode = config.configNode.getNode("Mode").getString();
@ -70,33 +77,43 @@ public class SshdPlugin
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.EnableSFTP = config.configNode.getNode("EnableSFTP").getBoolean();
try
{
File motd = new File(this.ConfigDir.toFile(), "motd.txt");
if (!motd.exists())
{
InputStream link = (getClass().getResourceAsStream("/motd.txt"));
Files.copy(link, motd.getAbsoluteFile().toPath());
}
}
catch (IOException e)
{
e.printStackTrace();
}
sshd = SshServer.setUpDefaultServer();
sshd.setPort(this.Port);
String host = this.ListenAddress;
sshd.setHost(host.equals("all") ? null : host);
sshd.setHost(this.ListenAddress.equals("all") ? null : this.ListenAddress);
File hostKey = new File(getDataFolder(), "hostkey");
File authorizedKeys = new File(getDataFolder(), "authorized_keys");
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());
sshd.setPasswordAuthenticator(new ConfigPasswordAuthenticator());
sshd.setPublickeyAuthenticator(new PublicKeyAuthenticator(authorizedKeys));
if (this.EnableSFTP)
{
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
sshd.setFileSystemFactory(
new VirtualFileSystemFactory(FileSystems.getDefault().getPath(getDataFolder().getAbsolutePath()).getParent().getParent()));
new VirtualFileSystemFactory(this.ConfigDir.getParent().getParent()));
}
Sponge.getCommandManager().register(instance, MkpasswdCommand, "mkpasswd");
//this.getCommand("mkpasswd").setExecutor(new MkpasswdCommand());
MkpasswdCommand.BuildCommand();
sshd.setCommandFactory(new ConsoleCommandFactory());
try
@ -105,13 +122,18 @@ public class SshdPlugin
}
catch (IOException e)
{
getLogger().error("Failed to start SSH server! ", e);
logger.error("Failed to start SSH server! ", e);
}
logger.info("Successfully running ExamplePlugin!!!");
logger.info("Loaded Minecraft-SSHD.");
}
public Logger getLogger()
public static SshdPlugin GetInstance()
{
return instance;
}
public static Logger GetLogger()
{
return logger;
}

View File

@ -3,21 +3,39 @@ package com.ryanmichela.sshd.implementations;
import com.ryanmichela.sshd.SshdPlugin;
import com.ryanmichela.sshd.ConsoleShellFactory;
import com.ryanmichela.sshd.ConsoleLogFormatter;
import org.spongepowered.api.command.source.ConsoleSource;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.channel.MessageChannel;
import org.spongepowered.api.service.permission.Subject;
import org.spongepowered.api.service.permission.SubjectCollection;
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 java.io.IOException;
import java.util.Arrays;
import java.util.Set;
import java.util.logging.Level;
import java.util.Optional;
import java.util.List;
// This is gonna be a mess.
public class SSHDCommandSender implements ConsoleCommandSender, CommandSource
public class SSHDCommandSender implements ConsoleSource
{
private final PermissibleBase perm = new PermissibleBase(this);
private final SSHDConversationTracker conversationTracker = new SSHDConversationTracker();
private MessageChannel mc = MessageChannel.TO_CONSOLE;
private Subject subjectDelegate;
// Set by the upstream allocating function
public ConsoleShellFactory.ConsoleShell console;
// This is an override for Sponge to work with the SSH consoles.
public void sendMessage(Text message)
{
this.sendRawMessage(message.toPlain() + "\r");
}
// Back port from Spigot/BungeeCord-style API calls.
public void sendMessage(String message)
{
this.sendRawMessage(message + "\r");
@ -45,96 +63,98 @@ public class SSHDCommandSender implements ConsoleCommandSender, CommandSource
}
catch (IOException e)
{
SshdPlugin.instance.getLogger().log(Level.SEVERE, "Error sending message to SSHDCommandSender", e);
SshdPlugin.GetLogger().error("Error sending message to SSHDCommandSender", e);
}
}
public void sendMessage(String[] messages) {
public void sendMessage(String[] messages)
{
Arrays.asList(messages).forEach(this::sendMessage);
}
public MessageChannel getMessageChannel()
{
return mc;
}
public void setMessageChannel(MessageChannel channel)
{
mc = channel;
}
public String getName() {
return "SSHD Console";
}
public boolean isOp() {
return true;
}
public void setOp(boolean value) {
throw new UnsupportedOperationException("Cannot change operator status of server console");
}
public boolean beginConversation(Conversation conversation) {
return this.conversationTracker.beginConversation(conversation);
}
public void abandonConversation(Conversation conversation) {
this.conversationTracker.abandonConversation(conversation, new ConversationAbandonedEvent(conversation, new ManuallyAbandonedConversationCanceller()));
}
public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) {
this.conversationTracker.abandonConversation(conversation, details);
}
public void acceptConversationInput(String input) {
this.conversationTracker.acceptConversationInput(input);
}
public boolean isConversing() {
return this.conversationTracker.isConversing();
}
public boolean isPermissionSet(String name) {
return this.perm.isPermissionSet(name);
}
public boolean isPermissionSet(Permission perm) {
return this.perm.isPermissionSet(perm);
}
public boolean hasPermission(String name) {
return this.perm.hasPermission(name);
}
public boolean hasPermission(Permission perm) {
return this.perm.hasPermission(perm);
}
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) {
return this.perm.addAttachment(plugin, name, value);
}
public PermissionAttachment addAttachment(Plugin plugin) {
return this.perm.addAttachment(plugin);
}
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) {
return this.perm.addAttachment(plugin, name, value, ticks);
}
public PermissionAttachment addAttachment(Plugin plugin, int ticks) {
return this.perm.addAttachment(plugin, ticks);
}
public void removeAttachment(PermissionAttachment attachment) {
this.perm.removeAttachment(attachment);
}
public void recalculatePermissions() {
this.perm.recalculatePermissions();
}
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
return this.perm.getEffectivePermissions();
}
public boolean isPlayer() {
return false;
}
public Server getServer() {
return Bukkit.getServer();
}
@Override
public String getIdentifier() {
// TODO Auto-generated method stub
return null;
}
@Override
public Set<Context> getActiveContexts() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isSubjectDataPersisted() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isChildOf(Set<Context> 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<Context> contexts, String permission) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<SubjectReference> getParents(Set<Context> contexts) {
// TODO Auto-generated method stub
return null;
}
@Override
public Optional<String> getOption(Set<Context> contexts, String key) {
// TODO Auto-generated method stub
return null;
}
@Override
public SubjectCollection getContainingCollection() {
// TODO Auto-generated method stub
return null;
}
@Override
public Optional<CommandSource> getCommandSource() {
// TODO Auto-generated method stub
return null;
}
@Override
public SubjectReference asSubjectReference() {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -1,78 +0,0 @@
package com.ryanmichela.sshd.implementations;
import org.bukkit.Bukkit;
import org.bukkit.conversations.Conversation;
import org.bukkit.conversations.ConversationAbandonedEvent;
import org.bukkit.conversations.ManuallyAbandonedConversationCanceller;
import java.util.LinkedList;
import java.util.logging.Level;
public class SSHDConversationTracker {
private LinkedList<Conversation> conversationQueue = new LinkedList<>();
synchronized boolean beginConversation(Conversation conversation) {
if (!this.conversationQueue.contains(conversation)) {
this.conversationQueue.addLast(conversation);
if (this.conversationQueue.getFirst() == conversation) {
conversation.begin();
conversation.outputNextPrompt();
return true;
}
}
return true;
}
synchronized void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) {
if (!this.conversationQueue.isEmpty()) {
if (this.conversationQueue.getFirst() == conversation) {
conversation.abandon(details);
}
if (this.conversationQueue.contains(conversation)) {
this.conversationQueue.remove(conversation);
}
if (!this.conversationQueue.isEmpty()) {
this.conversationQueue.getFirst().outputNextPrompt();
}
}
}
public synchronized void abandonAllConversations() {
LinkedList<Conversation> oldQueue = this.conversationQueue;
this.conversationQueue = new LinkedList<>();
for (Conversation conversation : oldQueue) {
try {
conversation.abandon(new ConversationAbandonedEvent(conversation, new ManuallyAbandonedConversationCanceller()));
} catch (Throwable var5) {
Bukkit.getLogger().log(Level.SEVERE, "Unexpected exception while abandoning a conversation", var5);
}
}
}
synchronized void acceptConversationInput(String input) {
if (this.isConversing()) {
Conversation conversation = this.conversationQueue.getFirst();
try {
conversation.acceptInput(input);
} catch (Throwable var4) {
conversation.getContext().getPlugin().getLogger().log(Level.WARNING, String.format("Plugin %s generated an exception whilst handling conversation input", conversation.getContext().getPlugin().getDescription().getFullName()), var4);
}
}
}
synchronized boolean isConversing() {
return !this.conversationQueue.isEmpty();
}
public synchronized boolean isConversingModaly() {
return this.isConversing() && this.conversationQueue.getFirst().isModal();
}
}

View File

@ -17,21 +17,20 @@ public class Config
{
// Give us a config!
@Inject
@DefaultConfig(sharedRoot = true)
@DefaultConfig(sharedRoot = false)
// idk what to do with this one.
private ConfigurationLoader<CommentedConfigurationNode> configLoader = HoconConfigurationLoader.builder().setPath(SshdPlugin.instance.defaultConfig).build();
private ConfigurationLoader<CommentedConfigurationNode> configLoader = HoconConfigurationLoader.builder().setPath(SshdPlugin.GetInstance().DefaultConfig).build();
public CommentedConfigurationNode configNode;
public void setup()
{
// I'm not sure if this will even work, the sponge config API is confusing.
if (!Files.exists((Path) SshdPlugin.instance.config.configLoader.getDefaultOptions()))
if (!Files.exists(SshdPlugin.GetInstance().DefaultConfig))
{
try
{
Files.createFile((Path) SshdPlugin.instance.config.configLoader.getDefaultOptions());
Files.createFile(SshdPlugin.GetInstance().DefaultConfig);
this.load();
this.populate();
this.save();
@ -102,6 +101,10 @@ public class Config
"# 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", "user1", "password").setValue("MySecretPassword");
this.configNode.getNode("Credentials", "user2", "password").setValue("MyBestFriendsPassword");
//this.configNode.getNode("").setValue("").setComment("");

View File

@ -1,422 +0,0 @@
package org.slf4j.impl;
import com.ryanmichela.sshd.SshdPlugin;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;
import java.util.logging.Level;
/**
* Copyright 2013 Ryan Michela
*/
// figure out logging...
public class PluginSlf4jFactory implements ILoggerFactory
{
@Override
public Logger getLogger(String name)
{
return new PluginSlf4jAdapter(name);
}
public class PluginSlf4jAdapter implements Logger
{
private String name;
private boolean isEnabled(Level level)
{
// ???
return SshdPlugin.instance != null && SshdPlugin.instance.logger.isLoggable(level);
}
private void log(Level level, String s, Object[] objects)
{
if (SshdPlugin.instance != null && isEnabled(level))
{
FormattingTuple ft = MessageFormatter.arrayFormat(s, objects);
SshdPlugin.instance.getLogger().log(level, ft.getMessage(), ft.getThrowable());
}
}
private void log(Level level, String s, Throwable throwable)
{
if (SshdPlugin.instance != null && isEnabled(level))
{
SshdPlugin.instance.logger.log(level, s, throwable);
}
}
public PluginSlf4jAdapter(String name)
{
this.name = name;
}
@Override
public String getName()
{
return name;
}
@Override
public boolean isTraceEnabled()
{
return isEnabled(Level.FINEST);
}
@Override
public void trace(String s)
{
trace(s, new Object[]{});
}
@Override
public void trace(String s, Object o)
{
trace(s, new Object[]{o});
}
@Override
public void trace(String s, Object o, Object o1)
{
trace(s, new Object[]{o, o1});
}
@Override
public void trace(String s, Object[] objects)
{
log(Level.FINEST, s, objects);
}
@Override
public void trace(String s, Throwable throwable)
{
log(Level.FINEST, s, throwable);
}
@Override
public boolean isTraceEnabled(Marker marker)
{
return isTraceEnabled();
}
@Override
public void trace(Marker marker, String s)
{
trace(s);
}
@Override
public void trace(Marker marker, String s, Object o)
{
trace(s, o);
}
@Override
public void trace(Marker marker, String s, Object o, Object o1)
{
trace(s, o, o1);
}
@Override
public void trace(Marker marker, String s, Object[] objects)
{
trace(s, objects);
}
@Override
public void trace(Marker marker, String s, Throwable throwable)
{
trace(s, throwable);
}
@Override
public boolean isDebugEnabled()
{
return isEnabled(Level.FINE);
}
@Override
public void debug(String s)
{
debug(s, new Object[]{});
}
@Override
public void debug(String s, Object o)
{
debug(s, new Object[]{o});
}
@Override
public void debug(String s, Object o, Object o1)
{
debug(s, new Object[]{o, o1});
}
@Override
public void debug(String s, Object[] objects)
{
log(Level.FINE, s, objects);
}
@Override
public void debug(String s, Throwable throwable)
{
log(Level.FINE, s, throwable);
}
@Override
public boolean isDebugEnabled(Marker marker)
{
return isDebugEnabled();
}
@Override
public void debug(Marker marker, String s)
{
debug(s);
}
@Override
public void debug(Marker marker, String s, Object o)
{
debug(s, o);
}
@Override
public void debug(Marker marker, String s, Object o, Object o1)
{
debug(s, o, o1);
}
@Override
public void debug(Marker marker, String s, Object[] objects)
{
debug(s, objects);
}
@Override
public void debug(Marker marker, String s, Throwable throwable)
{
debug(s, throwable);
}
@Override
public boolean isInfoEnabled()
{
return isEnabled(Level.INFO);
}
@Override
public void info(String s)
{
info(s, new Object[]{});
}
@Override
public void info(String s, Object o)
{
info(s, new Object[]{o});
}
@Override
public void info(String s, Object o, Object o1)
{
info(s, new Object[]{o, o1});
}
@Override
public void info(String s, Object[] objects)
{
log(Level.INFO, s, objects);
}
@Override
public void info(String s, Throwable throwable)
{
log(Level.INFO, s, throwable);
}
@Override
public boolean isInfoEnabled(Marker marker)
{
return isInfoEnabled();
}
@Override
public void info(Marker marker, String s)
{
info(s);
}
@Override
public void info(Marker marker, String s, Object o)
{
info(s, o);
}
@Override
public void info(Marker marker, String s, Object o, Object o1)
{
info(s, o, o1);
}
@Override
public void info(Marker marker, String s, Object[] objects)
{
info(s, objects);
}
@Override
public void info(Marker marker, String s, Throwable throwable)
{
info(s, throwable);
}
@Override
public boolean isWarnEnabled()
{
return isEnabled(Level.WARNING);
}
@Override
public void warn(String s)
{
warn(s, new Object[]{});
}
@Override
public void warn(String s, Object o)
{
warn(s, new Object[]{o});
}
@Override
public void warn(String s, Object o, Object o1)
{
warn(s, new Object[]{o, o1});
}
@Override
public void warn(String s, Object[] objects)
{
log(Level.WARNING, s, objects);
}
@Override
public void warn(String s, Throwable throwable)
{
log(Level.WARNING, s, throwable);
}
@Override
public boolean isWarnEnabled(Marker marker)
{
return isWarnEnabled();
}
@Override
public void warn(Marker marker, String s)
{
warn(s);
}
@Override
public void warn(Marker marker, String s, Object o)
{
warn(s, o);
}
@Override
public void warn(Marker marker, String s, Object o, Object o1)
{
warn(s, o, o1);
}
@Override
public void warn(Marker marker, String s, Object[] objects)
{
warn(s, objects);
}
@Override
public void warn(Marker marker, String s, Throwable throwable)
{
warn(s, throwable);
}
@Override
public boolean isErrorEnabled()
{
return isEnabled(Level.SEVERE);
}
@Override
public void error(String s)
{
error(s, new Object[]{});
}
@Override
public void error(String s, Object o)
{
error(s, new Object[]{o});
}
@Override
public void error(String s, Object o, Object o1)
{
error(s, new Object[]{o, o1});
}
@Override
public void error(String s, Object[] objects)
{
log(Level.SEVERE, s, objects);
}
@Override
public void error(String s, Throwable throwable)
{
log(Level.SEVERE, s, throwable);
}
@Override
public boolean isErrorEnabled(Marker marker)
{
return isErrorEnabled();
}
@Override
public void error(Marker marker, String s)
{
error(s);
}
@Override
public void error(Marker marker, String s, Object o)
{
error(s, o);
}
@Override
public void error(Marker marker, String s, Object o, Object o1)
{
error(s, o, o1);
}
@Override
public void error(Marker marker, String s, Object[] objects)
{
error(s, objects);
}
@Override
public void error(Marker marker, String s, Throwable throwable)
{
error(s, throwable);
}
}
}

View File

@ -1,82 +0,0 @@
/**
* Copyright (c) 2004-2011 QOS.ch
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package org.slf4j.impl;
import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;
import org.slf4j.spi.LoggerFactoryBinder;
/**
* The binding of {@link LoggerFactory} class with an actual instance of
* {@link ILoggerFactory} is performed using information returned by this class.
*
* @author Ceki G&uuml;lc&uuml;
*/
public class StaticLoggerBinder implements LoggerFactoryBinder {
/**
* The unique instance of this class.
*
*/
private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
/**
* Return the singleton of this class.
*
* @return the StaticLoggerBinder singleton
*/
public static final StaticLoggerBinder getSingleton() {
return SINGLETON;
}
/**
* Declare the version of the SLF4J API this implementation is compiled against.
* The value of this field is usually modified with each release.
*/
// to avoid constant folding by the compiler, this field must *not* be final
public static String REQUESTED_API_VERSION = "1.6.99"; // !final
private static final String loggerFactoryClassStr = PluginSlf4jFactory.class.getName();
/** The ILoggerFactory instance returned by the {@link #getLoggerFactory} method
* should always be the same object
*/
private final ILoggerFactory loggerFactory;
private StaticLoggerBinder() {
// Note: JCL gets substituted at build time by an appropriate Ant task
loggerFactory = new PluginSlf4jFactory();
}
public ILoggerFactory getLoggerFactory() {
return loggerFactory;
}
public String getLoggerFactoryClassStr() {
return loggerFactoryClassStr;
}
}

View File

@ -1,9 +0,0 @@
name: SSHD
version: ${project.version}
author: Ryan Michela, Haarolean, toxuin, Justin Crawford, Zachery Coleman
main: com.ryanmichela.sshd.SshdPlugin
load: STARTUP
commands:
mkpasswd:
description: Make a SSHD password hash
usage: /mkpasswd <hash|help> <password>