Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf72021439 | ||
|
|
dd76551cf9 | ||
|
|
a7918c68aa | ||
|
|
41b561a88c | ||
|
|
67aa5c63ea | ||
|
|
7c6ae57513 | ||
|
|
170a96eb94 | ||
|
|
ec1ed8cb9e | ||
|
|
67146ce589 | ||
|
|
e986cacc50 | ||
|
|
0afba39d57 | ||
|
|
1b4c7c2304 |
108
README.md
108
README.md
@@ -1,70 +1,100 @@
|
||||
Spigot-SSHD
|
||||
Minecraft-SSHD (Sponge Edition!)
|
||||
===========
|
||||
|
||||
[](https://travis-ci.org/Justasic/Spigot-SSHD)
|
||||
[](https://travis-ci.org/Justasic/Minecraft-SSHD)
|
||||
[](https://github.com/Justasic/Minecraft-SSHD/releases/latest)
|
||||
[](https://github.com/Justasic/Minecraft-SSHD/blob/master/LICENSE)
|
||||
|
||||
Have you ever wished you could remotely access your server's admin console without having to setup a complex remote access system? Now you can with SSHD.
|
||||
<img align="left" width="140" height="140" src="docs/ssh_logo.png?raw=true" hspace="5" vspace="5" alt="diskover"><br/>
|
||||
|
||||
SSHD securely exposes your Spigot admin console using the SSH protocol - the same protocol that serves as the secure foundation for nearly all remote server administration.
|
||||
**Have you ever wished you could remotely access your server's admin console without having to setup a complex remote access system? Now you can with Minecraft-SSHD!**
|
||||
|
||||
Minecraft-SSHD securely exposes your Sponge admin console and the server filesystem using the SSH protocol - the same protocol that serves as the secure foundation for nearly all remote server administration.<br/>
|
||||
|
||||
- Compatible with all ssh clients, regardless of operating system.
|
||||
- Remotely view your server log in real-time.
|
||||
- Remotely issue commands from the server console, just as if you were on the server itself.
|
||||
- Supports multiple concurrent remote connections.
|
||||
- Strong identity support using public key authentication.
|
||||
- Audit history who is running commands in the console
|
||||
- Run Spigot without using screen or tmux (by adding `-noconsole`)
|
||||
- Remotely script your server by issuing one-off console commands with ssh.
|
||||
|
||||
## Why should I use SSHD?
|
||||
### Why should I use Minecraft-SSHD?
|
||||
|
||||
- Your server runs on Windows.
|
||||
- You are in a shared hosting environment that only gives you access to the - log files.
|
||||
- You are in a shared hosting environment that only gives you access to the log files.
|
||||
- You want to share access to your server console, but don't want to give anybody access to the machine its running on.
|
||||
- You always wanted to use RCON, but want to see the server log as well.
|
||||
- You are tired of running your server in a Screen session.
|
||||
- You are tired of running your server in a GNU screen or tmux session.
|
||||
- You just want to access your server console using SSH.
|
||||
|
||||
## Configuration
|
||||
|
||||
- **listenAddress** - The network interface(s) SSHD should listen on. (Default all)
|
||||
- **port** - Specify the port SSHD should listen on. (Default 22)
|
||||
- **username/password** - The credentials used to log into the server console. (Default blank)
|
||||
|
||||
Note: By default, only public key authentication is enabled. This is the most secure authentication mode! Setting a username and password will make your server less secure.
|
||||
|
||||
## Setting Up Public Key Authentication
|
||||
Screenshots
|
||||
============
|
||||
|
||||
Setting up public key authentication with SSH requires first generating a public and private key pair and then installing just the public key on your Spigot server.
|
||||
<img align="left" width="390" src="docs/console.png?raw=true" hspace="5" vspace="5" alt="console">
|
||||
<img width="400" src="docs/session.png?raw=true" alt="session"><br>
|
||||
|
||||
On Windows
|
||||
|
||||
1. TODO
|
||||
Setting Up Public Key Authentication
|
||||
====================================
|
||||
|
||||
On Linux/OS X
|
||||
Setting up public key authentication with SSH requires first generating a public and private key pair and then installing just the public key on your Spigot server. This plugin supports all modern SSH key algoritms as OpenSSH. You can paste as many public keys from the methods below into each user's authorization file if they have multiple private keys. You can read [this guide from ssh.com](https://www.ssh.com/ssh/keygen/) if you want a better explanation on different key files.
|
||||
|
||||
1. TODO
|
||||
## Generating New Keys
|
||||
|
||||
#### On Windows
|
||||
|
||||
1. Ensure [Putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) is installed and open up `puttygen` (you can search for it in start search).
|
||||
2. Click `Generate` and follow the directions.
|
||||
3. When it finishes, set your key comment (if you like) and copy the text from the big `Public key for pasting into OpenSSH authorized_keys file`
|
||||
4. Create a new file inside of the `plugins/SSHD/authorized_users` folder and name the file just the username (example: `justasic`, there should ***NOT*** be a file extension or authentication does not work).
|
||||
5. Paste the key you copied from step 3 into the file you just created.
|
||||
6. SSH into the server and see if your key works
|
||||
|
||||
#### On Linux/OS X
|
||||
|
||||
1. Open a terminal and run `ssh-keygen` then follow the prompts.
|
||||
2. Copy the contents of your `id_<algorithm>.pub` file (example: if your key was generated with rsa, it will be named `id_rsa.pub`). This file is usually located in `/home/YOURUSERNAME/.ssh/`
|
||||
3. Paste the contents of the .pub file into a new file inside the `plugins/SSHD/authorized_users` folder and name the file just the username that the user will use to login with (example: `justasic`, there should ***NOT*** be a file extension or authentication does not work).
|
||||
|
||||
## Using existing keys
|
||||
|
||||
#### On Windows
|
||||
|
||||
1. Ensure [Putty](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) is installed and open up `puttygen` (you can search for it in start search).
|
||||
2. Click `Conversions` then click `Import Key` and select your .ppk file.
|
||||
3. Copy the text from the big `Public key for pasting into OpenSSH authorized_keys file`
|
||||
4. Create a new file inside of the `plugins/SSHD/authorized_users` folder and name the file just the username (example: `justasic`, there should ***NOT*** be a file extension or authentication does not work).
|
||||
5. Paste the key you copied from step 3 into the file you just created.
|
||||
6. SSH into the server and see if your key works
|
||||
|
||||
#### On Linux/OS X
|
||||
|
||||
1. Copy the contents of your `id_<algorithm>.pub` file (example: if your key was generated with rsa, it will be named `id_rsa.pub`). This file is usually located in `/home/YOURUSERNAME/.ssh/`
|
||||
2. Paste the contents of the .pub file into a new file inside the `plugins/SSHD/authorized_users` folder and name the file just the username that the user will use to login with (example: `justasic`, there should ***NOT*** be a file extension or authentication does not work).
|
||||
|
||||
Plugin Usage
|
||||
============
|
||||
|
||||
## Commands
|
||||
None - just install and go.
|
||||
|
||||
/mkpasswd <hash|help> <password>
|
||||
|
||||
mkpasswd supports the following hash algorithms:
|
||||
|
||||
- bcrypt - Using the OpenBSD-style Blowfish password hash
|
||||
- sha256 - Using a basic salted sha256 hash
|
||||
- pbkdf2 - Using the [PBKDF2](https://en.wikipedia.org/wiki/Pbkdf2) password hash
|
||||
- PLAIN - Using plain text passwords (very insecure)
|
||||
|
||||
## Permissions
|
||||
|
||||
None - SSHD uses cryptographic certificates or a secure username and password to verify remote access.
|
||||
`sshd.mkpasswd` - Checks if the in-game user has access to run the mkpasswd command.
|
||||
|
||||
Minecraft-SSHD uses cryptographic certificates or a secure username and password to verify remote access.
|
||||
|
||||
|
||||
## Source Code
|
||||
[Get the source on GitHub](https://github.com/Justasic/Spigot-SSHD "Source Code")
|
||||
|
||||
## Metrics
|
||||
|
||||
This plugin utilizes Hidendra's plugin metrics system. the following information is collected and sent to mcstats.org unless opted out:
|
||||
|
||||
- A unique identifier
|
||||
- The server's version of Java
|
||||
- Whether the server is in offline or online mode
|
||||
- Plugin's version
|
||||
- Server's version
|
||||
- OS version/name and architecture
|
||||
- core count for the CPU
|
||||
- Number of players online
|
||||
- Metrics version
|
||||
|
||||
Opting out of this service can be done by editing plugins/Plugin Metrics/config.yml and changing opt-out to true.
|
||||
[Get the source on GitHub](https://github.com/Justasic/Minecraft-SSHD "Source Code")
|
||||
|
||||
89
Sponge-SSHD.iml
Normal file
89
Sponge-SSHD.iml
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="minecraft" name="Minecraft">
|
||||
<configuration>
|
||||
<autoDetectTypes>
|
||||
<platformType>SPONGE</platformType>
|
||||
</autoDetectTypes>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Maven: org.apache.sshd:sshd-core:2.3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.sshd:sshd-mina:2.3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.sshd:sshd-common:2.3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.sshd:sshd-sftp:2.3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: net.i2p.crypto:eddsa:0.3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.mina:mina-core:2.1.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.28" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:slf4j-jdk14:1.7.28" level="project" />
|
||||
<orderEntry type="library" name="Maven: jline:jline:2.12.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.logging.log4j:log4j-core:2.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.logging.log4j:log4j-api:2.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-codec:commons-codec:1.10" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.spongepowered:spongeapi:7.1.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.guava:guava:21.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.errorprone:error_prone_annotations:2.0.15" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.gson:gson:2.8.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.commons:commons-lang3:3.5" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.findbugs:jsr305:3.0.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.inject:guice:4.1.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: javax.inject:javax.inject:1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: aopalliance:aopalliance:1.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.github.ben-manes.caffeine:caffeine:2.5.4" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.github.ben-manes.caffeine:guava:2.5.4" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.spongepowered:plugin-meta:0.4.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.spongepowered:configurate-hocon:3.6" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.spongepowered:configurate-core:3.6" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.checkerframework:checker-qual:2.4.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.typesafe:config:1.3.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.spongepowered:configurate-gson:3.6" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.spongepowered:configurate-yaml:3.6" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.yaml:snakeyaml:1.18" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.flowpowered:flow-math:1.0.3" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.flowpowered:flow-noise:1.0.1-SNAPSHOT" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.ow2.asm:asm:5.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: pl.project13.maven:git-commit-id-plugin:maven-plugin:3.0.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.maven:maven-plugin-api:3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.maven:maven-model:3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.maven:maven-artifact:3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.sonatype.sisu:sisu-inject-plexus:1.4.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.sonatype.sisu:sisu-inject-bean:1.4.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.sonatype.sisu:sisu-guice:noaop:2.1.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.maven:maven-core:3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.maven:maven-settings:3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.maven:maven-settings-builder:3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.maven:maven-repository-metadata:3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.maven:maven-model-builder:3.0" level="project" />
|
||||
<orderEntry type="library" scope="RUNTIME" name="Maven: org.apache.maven:maven-aether-provider:3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.sonatype.aether:aether-impl:1.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.sonatype.aether:aether-spi:1.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.sonatype.aether:aether-api:1.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.sonatype.aether:aether-util:1.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.codehaus.plexus:plexus-interpolation:1.14" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.codehaus.plexus:plexus-utils:2.0.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.codehaus.plexus:plexus-classworlds:2.2.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.codehaus.plexus:plexus-component-annotations:1.5.5" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.sonatype.plexus:plexus-sec-dispatcher:1.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.sonatype.plexus:plexus-cipher:1.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.sonatype.plexus:plexus-build-api:0.0.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.9.9.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.9.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.9.9" level="project" />
|
||||
<orderEntry type="library" name="Maven: joda-time:joda-time:2.10.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.eclipse.jgit:org.eclipse.jgit:5.2.2.201904231744-r" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.jcraft:jsch:0.1.54" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.jcraft:jzlib:1.1.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.googlecode.javaewah:JavaEWAH:1.1.6" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
BIN
docs/console.png
Normal file
BIN
docs/console.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 355 KiB |
BIN
docs/session.png
Normal file
BIN
docs/session.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 266 KiB |
BIN
docs/ssh_logo.png
Normal file
BIN
docs/ssh_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
133
docs/ssh_logo.svg
Normal file
133
docs/ssh_logo.svg
Normal file
@@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="800"
|
||||
height="800"
|
||||
viewBox="0 0 211.66666 211.66667"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="ssh_logo.svg"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14">
|
||||
<defs
|
||||
id="defs2">
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter5273">
|
||||
<feFlood
|
||||
flood-opacity="1"
|
||||
flood-color="rgb(0,0,0)"
|
||||
result="flood"
|
||||
id="feFlood5263" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite1"
|
||||
id="feComposite5265" />
|
||||
<feGaussianBlur
|
||||
in="composite1"
|
||||
stdDeviation="3.5"
|
||||
result="blur"
|
||||
id="feGaussianBlur5267" />
|
||||
<feOffset
|
||||
dx="1"
|
||||
dy="1"
|
||||
result="offset"
|
||||
id="feOffset5269" />
|
||||
<feComposite
|
||||
in="SourceGraphic"
|
||||
in2="offset"
|
||||
operator="over"
|
||||
result="composite2"
|
||||
id="feComposite5271" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.7"
|
||||
inkscape:cx="375.64503"
|
||||
inkscape:cy="367.3313"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer3"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="119"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Background"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-85.333343)">
|
||||
<circle
|
||||
style="fill:#4c4c4c;fill-opacity:1;stroke-width:0.30369404;filter:url(#filter5273)"
|
||||
id="path4533"
|
||||
cx="105"
|
||||
cy="192.00002"
|
||||
r="100" />
|
||||
<circle
|
||||
style="fill:#242424;fill-opacity:1;stroke-width:0.27332464;"
|
||||
id="path4533-3"
|
||||
cx="105"
|
||||
cy="192.00002"
|
||||
r="90" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="Text"
|
||||
style="opacity:0.98999999">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:145.88342285px;line-height:0;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.31260732"
|
||||
x="130.4019"
|
||||
y="91.524567"
|
||||
id="text5759"
|
||||
transform="scale(0.84637592,1.1815081)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5757"
|
||||
x="130.4019"
|
||||
y="91.524567"
|
||||
style="font-size:145.88342285px;line-height:0;fill:#ffffff;fill-opacity:1;stroke-width:0.31260732">_</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:145.88342285px;line-height:0;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;opacity:0.98999999;fill:#20bb00;fill-opacity:1;stroke:none;stroke-width:0.31260732"
|
||||
x="39.53738"
|
||||
y="118.43275"
|
||||
id="text5759-2"
|
||||
transform="scale(0.84637592,1.1815081)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5757-5"
|
||||
x="39.53738"
|
||||
y="118.43275"
|
||||
style="font-size:123.47222137px;line-height:0;fill:#20bb00;fill-opacity:1;stroke-width:0.31260732">></tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
199
pom.xml
199
pom.xml
@@ -1,49 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.ryanmichela</groupId>
|
||||
<artifactId>sshd</artifactId>
|
||||
<version>1.3.6</version>
|
||||
<url>https://github.com/Justasic/Bukkit-SSHD/</url>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<groupId>com.ryanmichela</groupId>
|
||||
<artifactId>sshd</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>Sponge-SSHD</name>
|
||||
<description>Sponge port for Minecraft-SSHD. SSH for your minecraft server!</description>
|
||||
<url>https://github.com/Justasic/Minecraft-SSHD/</url>
|
||||
|
||||
<!-- Repositories -->
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/groups/public</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<!-- License -->
|
||||
<licenses>
|
||||
<license>
|
||||
<name>GPL2</name>
|
||||
<url>http://www.gnu.org/licenses/gpl-2.0.html</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<!-- Dependencies -->
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<version>1.14.4-R0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>sponge</id>
|
||||
<url>https://repo.spongepowered.org/maven</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-core</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<scope>compile</scope>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -56,8 +42,6 @@
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-common</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<scope>compile</scope>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -71,24 +55,6 @@
|
||||
<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.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>jline</groupId>
|
||||
@@ -99,76 +65,73 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.0</version>
|
||||
<scope>provided</scope>
|
||||
<version>2.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.1</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>
|
||||
</dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.10</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.artifactId}-${project.version}</finalName>
|
||||
|
||||
<!-- Build -->
|
||||
<build>
|
||||
<defaultGoal>clean package</defaultGoal>
|
||||
<resources>
|
||||
<resource>
|
||||
<targetPath>.</targetPath>
|
||||
<filtering>true</filtering>
|
||||
<directory>${basedir}/src/main/resources</directory>
|
||||
<includes>
|
||||
<include>motd.txt</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
<targetPath>.</targetPath>
|
||||
<filtering>true</filtering>
|
||||
<directory>${basedir}/src/main/resources</directory>
|
||||
<includes>
|
||||
<include>plugin.yml</include>
|
||||
<include>config.yml</include>
|
||||
<include>motd.txt</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<!-- Plugins -->
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<finalName>${project.artifactId}-${project.version}</finalName>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<finalName>${project.name}-${project.version}</finalName>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Compile plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<showDeprecation>true</showDeprecation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
</project>
|
||||
<!-- Compile plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<showDeprecation>true</showDeprecation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
222
src/main/java/com/ryanmichela/sshd/ChatColor.java
Normal file
222
src/main/java/com/ryanmichela/sshd/ChatColor.java
Normal file
@@ -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<Character, ChatColor> BY_CHAR = new HashMap<Character, ChatColor>();
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
}
|
||||
@@ -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.GetInstance().logger.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.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.instance.getLogger().info("Too many failures for " + username + ", disconnecting.");
|
||||
SshdPlugin.GetInstance().logger.info("Too many failures for " + username + ", disconnecting.");
|
||||
ss.close(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,40 +5,61 @@ package com.ryanmichela.sshd;
|
||||
*/
|
||||
|
||||
import jline.console.completer.Completer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandMap;
|
||||
|
||||
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 int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
|
||||
Waitable<List<String>> waitable = new Waitable<List<String>>() {
|
||||
public ConsoleCommandCompleter()
|
||||
{
|
||||
super();
|
||||
this.MinecraftExecutor = Sponge.getScheduler().createSyncExecutor(SshdPlugin.GetInstance());
|
||||
}
|
||||
|
||||
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() {
|
||||
CommandMap commandMap = ReflectionUtil.getProtectedValue(Bukkit.getServer(), "commandMap");
|
||||
return commandMap.tabComplete(Bukkit.getServer().getConsoleSender(), buffer);
|
||||
protected List<String> evaluate()
|
||||
{
|
||||
return Sponge.getCommandManager().getSuggestions(Sponge.getServer().getConsole(), buffer, null);
|
||||
}
|
||||
};
|
||||
Bukkit.getScheduler().runTask(SshdPlugin.instance, waitable);
|
||||
try {
|
||||
|
||||
this.MinecraftExecutor.execute(waitable);
|
||||
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) {
|
||||
SshdPlugin.instance.getLogger().log(Level.WARNING, "Unhandled exception when tab completing", e);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
catch (ExecutionException e)
|
||||
{
|
||||
SshdPlugin.GetInstance().logger.warn("Unhandled exception when tab completing", e);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return cursor;
|
||||
|
||||
@@ -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.GetInstance().logger
|
||||
.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.GetInstance().logger.error("Error processing command from SSH -" + e.getMessage());
|
||||
}
|
||||
finally
|
||||
{
|
||||
callback.onExit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,25 +4,17 @@ 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;
|
||||
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<ChatColor, String> replacements = new EnumMap<ChatColor, String>(ChatColor.class);
|
||||
|
||||
public ConsoleLogFormatter() {
|
||||
this.dateFormat = new SimpleDateFormat("HH:mm:ss");
|
||||
}
|
||||
public class ConsoleLogFormatter
|
||||
{
|
||||
private static final Map<ChatColor, String> replacements = new EnumMap<ChatColor, String>(ChatColor.class);
|
||||
|
||||
public static String ColorizeString(String str)
|
||||
{
|
||||
@@ -66,44 +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();
|
||||
}
|
||||
|
||||
private void colorize(LogRecord logrecord)
|
||||
{
|
||||
String result = ColorizeString(logrecord.getMessage());
|
||||
logrecord.setMessage(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -18,7 +17,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;
|
||||
@@ -93,8 +97,7 @@ public class ConsoleShellFactory implements ShellFactory {
|
||||
this.ConsoleReader.setExpandEvents(true);
|
||||
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);
|
||||
|
||||
@@ -118,11 +121,15 @@ 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)
|
||||
{
|
||||
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;
|
||||
@@ -136,49 +143,53 @@ public class ConsoleShellFactory implements ShellFactory {
|
||||
if (command.equals("cls"))
|
||||
{
|
||||
this.ConsoleReader.clearScreen();
|
||||
this.ConsoleReader.drawLine();
|
||||
this.ConsoleReader.flush();
|
||||
continue;
|
||||
}
|
||||
// Hide the mkpasswd command input.
|
||||
// Hide the mkpasswd command input from other users.
|
||||
Boolean mkpasswd = command.split(" ")[0].equals("mkpasswd");
|
||||
|
||||
Bukkit.getScheduler().runTask(
|
||||
SshdPlugin.instance, () ->
|
||||
MinecraftExecutor.submit(() ->
|
||||
{
|
||||
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
|
||||
{
|
||||
// Don't send our mkpasswd command output. This will echo passwords back
|
||||
// to the console for all to see. This command is strictly between
|
||||
// our plugin and the connected client.
|
||||
if (!mkpasswd)
|
||||
{
|
||||
SshdPlugin.instance.getLogger().info("<" + this.Username + "> <" + (mkpasswd ? "True": "False") + "> " + command);
|
||||
|
||||
SshdPlugin.GetInstance().logger.info("<" + this.Username + "> " + command);
|
||||
CmdManager.process(Sponge.getServer().getConsole(), command);
|
||||
}
|
||||
else
|
||||
{
|
||||
CmdManager.process(this.SshdCommandSender, command);
|
||||
}
|
||||
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command);
|
||||
}
|
||||
});
|
||||
}
|
||||
// This should help stop one of the bugs where bytes are waiting to be written
|
||||
// but the client fucked off already so the plugin throws an exception.
|
||||
((Logger)LogManager.getRootLogger()).removeAppender(this.streamHandlerAppender);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
SshdPlugin.instance.getLogger().log(Level.SEVERE, "Error processing command from SSH", e);
|
||||
SshdPlugin.GetInstance().logger.error("Error processing command from SSH", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SshdPlugin.instance.getLogger().log(Level.INFO, this.Username + " disconnected from SSH.");
|
||||
SshdPlugin.GetInstance().logger.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));
|
||||
@@ -189,16 +200,26 @@ public class ConsoleShellFactory implements ShellFactory {
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
SshdPlugin.instance.getLogger().log(Level.WARNING, "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.
|
||||
}
|
||||
|
||||
// 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("Type 'exit' or CTRL+D to exit the shell." + "\r");
|
||||
cr.println("===============================================" + "\r");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +1,59 @@
|
||||
package com.ryanmichela.sshd;
|
||||
|
||||
import org.apache.sshd.common.SshException;
|
||||
import org.apache.sshd.common.channel.exception.SshChannelClosedException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Copyright 2013 Ryan Michela
|
||||
*/
|
||||
public class FlushyOutputStream extends OutputStream {
|
||||
public class FlushyOutputStream extends OutputStream
|
||||
{
|
||||
|
||||
private OutputStream base;
|
||||
private boolean isClosed = false;
|
||||
|
||||
public FlushyOutputStream(OutputStream base) {
|
||||
public FlushyOutputStream(OutputStream base)
|
||||
{
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
if (isClosed) return;
|
||||
base.write(b);
|
||||
base.flush();
|
||||
public void write(int b) throws IOException
|
||||
{
|
||||
this.write(BigInteger.valueOf(b).toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
if (isClosed) return;
|
||||
base.write(b);
|
||||
base.flush();
|
||||
public void write(byte[] b) throws IOException
|
||||
{
|
||||
this.write(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
if (isClosed) return;
|
||||
try {
|
||||
public void write(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
if (isClosed)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
base.write(b, off, len);
|
||||
base.flush();
|
||||
} catch (SshException e) {
|
||||
if (!e.getMessage().contains("channel already closed")) throw e;
|
||||
}
|
||||
catch (SshChannelClosedException e)
|
||||
{
|
||||
// ignored.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
public void close() throws IOException
|
||||
{
|
||||
isClosed = true;
|
||||
base.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,100 +1,121 @@
|
||||
package com.ryanmichela.sshd;
|
||||
|
||||
/*
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
*/
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.ryanmichela.sshd.Cryptography;
|
||||
import com.ryanmichela.sshd.SshdPlugin;
|
||||
|
||||
class MkpasswdCommand implements CommandExecutor
|
||||
{
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
|
||||
{
|
||||
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));
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e)
|
||||
{
|
||||
// ignore it.
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
|
||||
// If they're console, allow regardless.
|
||||
if (!(sender instanceof Player))
|
||||
{
|
||||
if (label.equalsIgnoreCase("mkpasswd"))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Dumb but whatever. Some people are really dense.
|
||||
if (algoritm.equalsIgnoreCase("PLAIN"))
|
||||
{
|
||||
sender.sendMessage("Your hash: " + password);
|
||||
// I mean c'mon...
|
||||
sender.sendMessage("Bro really? it's literally your unencrypted password...");
|
||||
}
|
||||
else if (algoritm.equalsIgnoreCase("pbkdf2"))
|
||||
sender.sendMessage("Your hash: " + Cryptography.PBKDF2_HashPassword(password));
|
||||
else if (algoritm.equalsIgnoreCase("bcrypt"))
|
||||
sender.sendMessage("Your hash: " + Cryptography.BCrypt_HashPassword(password));
|
||||
else if (algoritm.equalsIgnoreCase("sha256"))
|
||||
sender.sendMessage("Your hash: " + Cryptography.SHA256_HashPassword(password));
|
||||
else if (algoritm.equalsIgnoreCase("help"))
|
||||
sender.sendMessage("Supported hash algorithms: pbkdf2, bcrypt, sha256, plain");
|
||||
else
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// We're console, just print the stack trace.
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Player player = (Player) sender;
|
||||
if (label.equalsIgnoreCase("mkpasswd"))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (player.hasPermission("sshd.mkpasswd"))
|
||||
{
|
||||
// Dumb but whatever. Some people are really dense.
|
||||
if (algoritm.equalsIgnoreCase("PLAIN"))
|
||||
sender.sendMessage(password);
|
||||
else if (algoritm.equalsIgnoreCase("pbkdf2"))
|
||||
sender.sendMessage(Cryptography.PBKDF2_HashPassword(password));
|
||||
else if (algoritm.equalsIgnoreCase("bcrypt"))
|
||||
sender.sendMessage(Cryptography.BCrypt_HashPassword(password));
|
||||
else if (algoritm.equalsIgnoreCase("sha256"))
|
||||
sender.sendMessage(Cryptography.SHA256_HashPassword(password));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// since this is a player, send a failure message
|
||||
sender.sendMessage("An error occured, please check console.");
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public class MkpasswdCommand implements CommandExecutor
|
||||
{
|
||||
private static CommandSpec cmdspec;
|
||||
public static void BuildCommand()
|
||||
{
|
||||
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("password").get())));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return CommandResult.success();
|
||||
})
|
||||
.build();
|
||||
|
||||
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("password").get())));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return CommandResult.success();
|
||||
})
|
||||
.build();
|
||||
|
||||
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("password").get())));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return CommandResult.success();
|
||||
})
|
||||
.build();
|
||||
|
||||
// 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("\u00A79Your Hash: \u00A7cIt's literally your unhashed password."));
|
||||
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(plain, "plain")
|
||||
.child(sha256, "sha256")
|
||||
.child(bcrypt, "bcrypt")
|
||||
.child(pbkdf2, "pbkdf2")
|
||||
.executor(new MkpasswdCommand())
|
||||
.build();
|
||||
|
||||
// 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
|
||||
{
|
||||
// This command doesn't do anything.
|
||||
src.sendMessage(Text.of("\u00A7a/mkpasswd <hash> <password...>\u00A7r"));
|
||||
src.sendMessage(Text.of("\u00A79Supported Hashes: SHA256, PBKDF2, BCRYPT, PLAIN\u00A7r"));
|
||||
return CommandResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.ryanmichela.sshd;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
|
||||
import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
|
||||
import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
|
||||
@@ -27,7 +26,7 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
|
||||
{
|
||||
byte[] keyBytes = key.getEncoded();
|
||||
File keyFile = new File(authorizedKeysDir, username);
|
||||
Integer tries = SshdPlugin.instance.getConfig().getInt("LoginRetries");
|
||||
Integer tries = SshdPlugin.GetInstance().LoginRetries;
|
||||
|
||||
if (keyFile.exists())
|
||||
{
|
||||
@@ -47,7 +46,7 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
|
||||
}
|
||||
else
|
||||
{
|
||||
SshdPlugin.instance.getLogger().info(
|
||||
SshdPlugin.GetInstance().logger.info(
|
||||
username + " failed authentication via SSH session using key file " + keyFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
@@ -60,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.GetInstance().logger.info("Too many failures for " + username + ", disconnecting.");
|
||||
session.close(true);
|
||||
}
|
||||
|
||||
@@ -68,12 +67,12 @@ public class PublicKeyAuthenticator implements PublickeyAuthenticator
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
SshdPlugin.instance.getLogger().severe("Failed to process public key " + keyFile.getAbsolutePath() + " " + e.getMessage());
|
||||
SshdPlugin.GetInstance().logger.error("Failed to process public key " + keyFile.getAbsolutePath() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SshdPlugin.instance.getLogger().warning("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.");
|
||||
}
|
||||
|
||||
@@ -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> 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> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,18 @@
|
||||
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;
|
||||
|
||||
import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
|
||||
import org.apache.sshd.server.SshServer;
|
||||
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
|
||||
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import com.ryanmichela.sshd.ConsoleShellFactory;
|
||||
import com.ryanmichela.sshd.MkpasswdCommand;
|
||||
@@ -14,29 +22,71 @@ 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;
|
||||
|
||||
/**
|
||||
* Copyright 2013 Ryan Michela
|
||||
*/
|
||||
public
|
||||
class SshdPlugin extends JavaPlugin
|
||||
import com.google.inject.Inject;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.api.text.Text;
|
||||
|
||||
@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 = "";
|
||||
Integer Port = 1025;
|
||||
String Mode = "";
|
||||
Boolean EnableSFTP = true;
|
||||
Integer LoginRetries = 3;
|
||||
String PasswordType = "";
|
||||
// Credentials
|
||||
|
||||
private SshServer sshd;
|
||||
public static SshdPlugin instance;
|
||||
private SshServer sshd;
|
||||
private static SshdPlugin instance;
|
||||
|
||||
@Override public void onLoad()
|
||||
@Inject
|
||||
public 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)
|
||||
{
|
||||
saveDefaultConfig();
|
||||
File authorizedKeys = new File(getDataFolder(), "authorized_keys");
|
||||
instance = this;
|
||||
// Parse our config
|
||||
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();
|
||||
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();
|
||||
|
||||
try
|
||||
{
|
||||
File motd = new File(getDataFolder(), "motd.txt");
|
||||
File motd = new File(this.ConfigDir.toFile(), "motd.txt");
|
||||
if (!motd.exists())
|
||||
{
|
||||
InputStream link = (getClass().getResourceAsStream("/motd.txt"));
|
||||
@@ -48,38 +98,25 @@ class SshdPlugin extends JavaPlugin
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Don't go any lower than INFO or SSHD will cause a stack overflow exception.
|
||||
// SSHD will log that it wrote bites to the output stream, which writes
|
||||
// bytes to the output stream - ad nauseaum.
|
||||
getLogger().setLevel(Level.INFO);
|
||||
}
|
||||
|
||||
@Override public void onEnable()
|
||||
{
|
||||
instance = this;
|
||||
|
||||
// pls comment this shit so I know what it does.
|
||||
sshd = SshServer.setUpDefaultServer();
|
||||
sshd.setPort(getConfig().getInt("Port", 1025));
|
||||
String host = getConfig().getString("ListenAddress", "all");
|
||||
sshd.setHost(host.equals("all") ? null : host);
|
||||
sshd.setPort(this.Port);
|
||||
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");
|
||||
|
||||
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(hostKey.toPath()));
|
||||
sshd.setShellFactory(new ConsoleShellFactory());
|
||||
sshd.setPasswordAuthenticator(new ConfigPasswordAuthenticator());
|
||||
sshd.setPublickeyAuthenticator(new PublicKeyAuthenticator(authorizedKeys));
|
||||
|
||||
if (getConfig().getBoolean("EnableSFTP"))
|
||||
if (this.EnableSFTP)
|
||||
{
|
||||
sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
|
||||
sshd.setFileSystemFactory(
|
||||
new VirtualFileSystemFactory(FileSystems.getDefault().getPath(getDataFolder().getAbsolutePath()).getParent().getParent()));
|
||||
sshd.setFileSystemFactory(new VirtualFileSystemFactory(this.ConfigDir.getParent().getParent()));
|
||||
}
|
||||
|
||||
this.getCommand("mkpasswd").setExecutor(new MkpasswdCommand());
|
||||
MkpasswdCommand.BuildCommand();
|
||||
|
||||
sshd.setCommandFactory(new ConsoleCommandFactory());
|
||||
try
|
||||
@@ -88,19 +125,19 @@ class SshdPlugin extends JavaPlugin
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
getLogger().log(Level.SEVERE, "Failed to start SSH server! ", e);
|
||||
logger.error("Failed to start SSH server! ", e);
|
||||
}
|
||||
|
||||
logger.info("Loaded Minecraft-SSHD.");
|
||||
}
|
||||
|
||||
@Override public void onDisable()
|
||||
public static SshdPlugin GetInstance()
|
||||
{
|
||||
try
|
||||
{
|
||||
sshd.stop();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Logger GetLogger()
|
||||
{
|
||||
return this.logger;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,73 @@
|
||||
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 (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
|
||||
|
||||
@@ -1,35 +1,49 @@
|
||||
package com.ryanmichela.sshd.implementations;
|
||||
|
||||
import com.ryanmichela.sshd.SshdPlugin;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.conversations.Conversation;
|
||||
import org.bukkit.conversations.ConversationAbandonedEvent;
|
||||
import org.bukkit.conversations.ManuallyAbandonedConversationCanceller;
|
||||
import org.bukkit.permissions.PermissibleBase;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionAttachment;
|
||||
import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
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;
|
||||
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 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;
|
||||
|
||||
public class SSHDCommandSender implements ConsoleCommandSender, CommandSender {
|
||||
|
||||
private final PermissibleBase perm = new PermissibleBase(this);
|
||||
private final SSHDConversationTracker conversationTracker = new SSHDConversationTracker();
|
||||
// This is gonna be a mess.
|
||||
public class SSHDCommandSender implements ConsoleSource
|
||||
{
|
||||
private MessageChannel mc = MessageChannel.TO_CONSOLE;
|
||||
private Subject subjectDelegate;
|
||||
// Set by the upstream allocating function
|
||||
public ConsoleShellFactory.ConsoleShell console;
|
||||
private UUID uuid = UUID.randomUUID();
|
||||
|
||||
public void sendMessage(String message) {
|
||||
// This is an override for Sponge to work with the SSH consoles.
|
||||
@Override
|
||||
public void sendMessage(Text message)
|
||||
{
|
||||
this.sendRawMessage(message.toPlain());
|
||||
}
|
||||
|
||||
// Back port from Spigot/BungeeCord-style API calls.
|
||||
public void sendMessage(String message)
|
||||
{
|
||||
this.sendRawMessage(message);
|
||||
}
|
||||
|
||||
@@ -40,100 +54,125 @@ public class SSHDCommandSender implements ConsoleCommandSender, CommandSender {
|
||||
return;
|
||||
try
|
||||
{
|
||||
this.console.ConsoleReader.println(ChatColor.stripColor(message));
|
||||
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
|
||||
{
|
||||
this.console.ConsoleReader.drawLine();
|
||||
}
|
||||
catch (Throwable ex)
|
||||
{
|
||||
this.console.ConsoleReader.getCursorBuffer().clear();
|
||||
}
|
||||
this.console.ConsoleReader.flush();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
SshdPlugin.instance.getLogger().log(Level.SEVERE, "Error sending message to SSHDCommandSender", e);
|
||||
SshdPlugin.GetInstance().logger.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;
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return uuid.toString();
|
||||
}
|
||||
|
||||
public void setOp(boolean value) {
|
||||
throw new UnsupportedOperationException("Cannot change operator status of server console");
|
||||
@Override
|
||||
public Set<Context> getActiveContexts()
|
||||
{
|
||||
// No clue what any of this does but sponge needs it to work with this class. - Justin
|
||||
Set<Context> set = new HashSet<Context>();
|
||||
set.add(new Context(Context.USER_KEY, "SSHD"));
|
||||
return set;
|
||||
}
|
||||
|
||||
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() {
|
||||
@Override
|
||||
public boolean isSubjectDataPersisted()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
public Server getServer() {
|
||||
return Bukkit.getServer();
|
||||
@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)
|
||||
{
|
||||
// We're allowed to view all permissions.
|
||||
return Tristate.TRUE;
|
||||
}
|
||||
|
||||
@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)
|
||||
{
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubjectCollection getContainingCollection()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<CommandSource> getCommandSource()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return Optional.of(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubjectReference asSubjectReference()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
109
src/main/java/com/ryanmichela/sshd/utils/Config.java
Normal file
109
src/main/java/com/ryanmichela/sshd/utils/Config.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package com.ryanmichela.sshd.utils;
|
||||
|
||||
import ninja.leaping.configurate.loader.ConfigurationLoader;
|
||||
import ninja.leaping.configurate.commented.CommentedConfigurationNode;
|
||||
import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
|
||||
import org.spongepowered.api.config.DefaultConfig;
|
||||
import com.google.inject.Inject;
|
||||
import com.ryanmichela.sshd.SshdPlugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Config
|
||||
{
|
||||
// Give us a config!
|
||||
@Inject
|
||||
@DefaultConfig(sharedRoot = false)
|
||||
// idk what to do with this one.
|
||||
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(SshdPlugin.GetInstance().DefaultConfig))
|
||||
{
|
||||
try
|
||||
{
|
||||
Files.createFile(SshdPlugin.GetInstance().DefaultConfig);
|
||||
this.load();
|
||||
this.populate();
|
||||
this.save();
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
else
|
||||
this.load();
|
||||
}
|
||||
|
||||
public void load()
|
||||
{
|
||||
try
|
||||
{
|
||||
configNode = this.configLoader.load();
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void save()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.configLoader.save(this.configNode);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void populate()
|
||||
{
|
||||
this.configNode.getNode("ListenAddress").setValue("all").setComment
|
||||
("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.");
|
||||
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.");
|
||||
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.");
|
||||
|
||||
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" +
|
||||
"########################################################################################\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.");
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -1,350 +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
|
||||
*/
|
||||
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.getLogger().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.getLogger().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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ülcü
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
name: SSHD
|
||||
version: ${project.version}
|
||||
author: Ryan Michela, Haarolean, toxuin, Justin Crawford
|
||||
main: com.ryanmichela.sshd.SshdPlugin
|
||||
commands:
|
||||
mkpasswd:
|
||||
description: Make a SSHD password hash
|
||||
usage: /mkpasswd <hash|help> <password>
|
||||
Reference in New Issue
Block a user