diff --git a/src/main/java/dev/mikchan/mcnp/votereceiver/core/config/IConfig.kt b/src/main/java/dev/mikchan/mcnp/votereceiver/core/config/IConfig.kt index 75644a6..e35828b 100644 --- a/src/main/java/dev/mikchan/mcnp/votereceiver/core/config/IConfig.kt +++ b/src/main/java/dev/mikchan/mcnp/votereceiver/core/config/IConfig.kt @@ -21,6 +21,11 @@ interface IConfig { */ val mineServTopKey: String? + /** + * A secret word of `mctop.im` + */ + val mcTopImSecretWord: String? + /** * Determines, if the test route accessible. * diff --git a/src/main/java/dev/mikchan/mcnp/votereceiver/core/config/boosted/BoostedYamlConfig.kt b/src/main/java/dev/mikchan/mcnp/votereceiver/core/config/boosted/BoostedYamlConfig.kt index af62463..5432b5a 100644 --- a/src/main/java/dev/mikchan/mcnp/votereceiver/core/config/boosted/BoostedYamlConfig.kt +++ b/src/main/java/dev/mikchan/mcnp/votereceiver/core/config/boosted/BoostedYamlConfig.kt @@ -26,5 +26,6 @@ internal class BoostedYamlConfig(document: File, resource: InputStream) : IConfi override val port: Int get() = config.getInt("port", 6418) override val mineServTopKey: String? get() = config.getString("mineserv-top-key") + override val mcTopImSecretWord: String? get() = config.getString("mctop-im-secret") override val testEnabled: Boolean get() = config.getBoolean("enable-test-route", false) } diff --git a/src/main/java/dev/mikchan/mcnp/votereceiver/core/config/fallback/FallbackConfig.kt b/src/main/java/dev/mikchan/mcnp/votereceiver/core/config/fallback/FallbackConfig.kt index af23d11..1e4c907 100644 --- a/src/main/java/dev/mikchan/mcnp/votereceiver/core/config/fallback/FallbackConfig.kt +++ b/src/main/java/dev/mikchan/mcnp/votereceiver/core/config/fallback/FallbackConfig.kt @@ -9,5 +9,6 @@ internal class FallbackConfig : IConfig { override val port: Int get() = 6418 override val mineServTopKey: String? get() = null + override val mcTopImSecretWord: String? get() = null override val testEnabled: Boolean get() = false } diff --git a/src/main/java/dev/mikchan/mcnp/votereceiver/core/utility/IUtility.kt b/src/main/java/dev/mikchan/mcnp/votereceiver/core/utility/IUtility.kt index 8f7ef67..afd7be5 100644 --- a/src/main/java/dev/mikchan/mcnp/votereceiver/core/utility/IUtility.kt +++ b/src/main/java/dev/mikchan/mcnp/votereceiver/core/utility/IUtility.kt @@ -17,4 +17,11 @@ interface IUtility { * @return SHA-256 hash string */ fun sha256(input: String): String + + /** + * Md5 hash algorithm + * + * @return Md5 hash string + */ + fun md5(input: String): String } diff --git a/src/main/java/dev/mikchan/mcnp/votereceiver/core/utility/base/Utility.kt b/src/main/java/dev/mikchan/mcnp/votereceiver/core/utility/base/Utility.kt index 30a87b3..f7b8791 100644 --- a/src/main/java/dev/mikchan/mcnp/votereceiver/core/utility/base/Utility.kt +++ b/src/main/java/dev/mikchan/mcnp/votereceiver/core/utility/base/Utility.kt @@ -4,9 +4,12 @@ import dev.mikchan.mcnp.votereceiver.core.utility.IUtility import java.security.MessageDigest internal class Utility : IUtility { - override fun sha256(input: String): String { - val md = MessageDigest.getInstance("SHA-256") + private fun hash(input: String, alg: String): String { + val md = MessageDigest.getInstance(alg) val bytes = md.digest(input.toByteArray(Charsets.UTF_8)) return bytes.fold("") { str, b -> str + "%02x".format(b) } } + + override fun sha256(input: String): String = hash(input, "SHA-256") + override fun md5(input: String): String = hash(input, "MD5") } diff --git a/src/main/java/dev/mikchan/mcnp/votereceiver/core/web/BuildRoutes.kt b/src/main/java/dev/mikchan/mcnp/votereceiver/core/web/BuildRoutes.kt index 33b815b..6be7b9a 100644 --- a/src/main/java/dev/mikchan/mcnp/votereceiver/core/web/BuildRoutes.kt +++ b/src/main/java/dev/mikchan/mcnp/votereceiver/core/web/BuildRoutes.kt @@ -8,6 +8,7 @@ internal fun Application.buildRoutes(plugin: IPlugin) { routing { createMineServTopRoute(plugin) createTMonitoringComRoute(plugin) + createMcTopImRoute(plugin) if (plugin.config.testEnabled) { createTestRoute(plugin) diff --git a/src/main/java/dev/mikchan/mcnp/votereceiver/core/web/McTopImRoute.kt b/src/main/java/dev/mikchan/mcnp/votereceiver/core/web/McTopImRoute.kt new file mode 100644 index 0000000..bdcf81a --- /dev/null +++ b/src/main/java/dev/mikchan/mcnp/votereceiver/core/web/McTopImRoute.kt @@ -0,0 +1,50 @@ +package dev.mikchan.mcnp.votereceiver.core.web + +import com.vexsoftware.votifier.model.Vote +import com.vexsoftware.votifier.net.VotifierSession +import dev.mikchan.mcnp.votereceiver.core.IPlugin +import io.ktor.http.* +import io.ktor.server.application.* +import io.ktor.server.plugins.* +import io.ktor.server.request.* +import io.ktor.server.response.* +import io.ktor.server.routing.* + +/** + * Reference: `https://mctop.im/awards.zip` + */ +internal fun Route.createMcTopImRoute(plugin: IPlugin) { + post("/mctop.im/award.php") { + val secretWord = plugin.config.mcTopImSecretWord + if (secretWord == null) { + call.respond(HttpStatusCode.InternalServerError, "-1") + return@post + } + + val parameters = try { + call.receiveParameters() + } catch (ex: Exception) { + call.respond(HttpStatusCode.InternalServerError, "-1") + return@post + } + + val nick = parameters["nick"] + val token = parameters["token"] + + if (nick == null || token == null) { + call.respond(HttpStatusCode.InternalServerError, "-1") + return@post + } + + val hash = plugin.utility.md5(nick + secretWord) + if (token != hash) { + call.respond(HttpStatusCode.InternalServerError, "-2") + return@post + } + + val vote = Vote("mctop.im", nick, call.request.origin.remoteHost, System.currentTimeMillis().toString(10)) + plugin.voteHandler?.onVoteReceived(vote, VotifierSession.ProtocolVersion.UNKNOWN, vote.address) + + call.respond("1") + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index d7d109d..78e89b2 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,11 +1,14 @@ # Automatic property, do not touch! -config-version: 2 +config-version: 3 port: 6418 # mineserv.top mineserv-top-key: "" +# mctop.im +mctop-im-secret: "" + # Enables the route /test?username=[username] # Warning: Should NEVER EVER be true # in production, for test purposes only.