diff --git a/build.sbt b/build.sbt index 01eeee4..10e82b4 100644 --- a/build.sbt +++ b/build.sbt @@ -23,6 +23,7 @@ scalacOptions ++= List( ) javacOptions += "-g:none" compileOrder := CompileOrder.JavaThenScala +javafmtOnCompile := false assembly / mainClass := Some("lc.LCFramework") Compile / run / mainClass := Some("lc.LCFramework") assembly / assemblyJarName := "LibreCaptcha.jar" diff --git a/src/main/java/lc/misc/HelperFunctions.java b/src/main/java/lc/misc/HelperFunctions.java index bda31b8..47e8614 100644 --- a/src/main/java/lc/misc/HelperFunctions.java +++ b/src/main/java/lc/misc/HelperFunctions.java @@ -1,9 +1,16 @@ package lc.misc; import java.awt.*; +import java.util.Random; public class HelperFunctions { + private static Random random = new Random(); + + synchronized public static void setSeed(long seed){ + random.setSeed(seed); + } + public static void setRenderingHints(Graphics2D g2d) { g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); @@ -25,13 +32,18 @@ public class HelperFunctions { public static String randomString(final int n, final String characters) { final StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < n; i++) { - int index = (int) (characters.length() * Math.random()); + int index = randomNumber(characters.length()); stringBuilder.append(characters.charAt(index)); } return stringBuilder.toString(); } - public static int randomNumber(int min, int max) { - return (int) (Math.random() * ((max - min) + 1)) + min; + synchronized public static int randomNumber(int min, int max) { + return (random.nextInt() * ((max - min) + 1)) + min; } + + synchronized public static int randomNumber(int bound) { + return random.nextInt(bound); + } + } diff --git a/src/main/scala/lc/background/taskThread.scala b/src/main/scala/lc/background/taskThread.scala index 33ae997..8278469 100644 --- a/src/main/scala/lc/background/taskThread.scala +++ b/src/main/scala/lc/background/taskThread.scala @@ -2,8 +2,9 @@ package lc.background import lc.database.Statements import java.util.concurrent.{ScheduledThreadPoolExecutor, TimeUnit} -import lc.core.Captcha +import lc.core.{Captcha, Config} import lc.core.{Parameters, Size} +import lc.misc.HelperFunctions class BackgroundTask(throttle: Int, timeLimit: Int) { @@ -22,13 +23,26 @@ class BackgroundTask(throttle: Int, timeLimit: Int) { if (imageNum.next()) throttleIn = (throttleIn - imageNum.getInt("total")) while (0 < throttleIn) { - Captcha.generateChallenge(Parameters("medium", "image/png", "text", Option(Size(0, 0)))) + Captcha.generateChallenge(getRandomParam()) throttleIn -= 1 } } catch { case exception: Exception => println(exception) } } } + private def getRandomParam(): Parameters = { + val captcha = pickRandom(Config.captchaConfig) + val level = pickRandom(captcha.allowedLevels) + val media = pickRandom(captcha.allowedMedia) + val inputType = pickRandom(captcha.allowedInputType) + + Parameters(level, media, inputType, Some(Size(0, 0))) + } + + private def pickRandom[T](list: List[T]): T = { + list(HelperFunctions.randomNumber(list.size)) + } + def beginThread(delay: Int): Unit = { val ex = new ScheduledThreadPoolExecutor(1) ex.scheduleWithFixedDelay(task, 1, delay, TimeUnit.SECONDS) diff --git a/src/main/scala/lc/core/captchaProviders.scala b/src/main/scala/lc/core/captchaProviders.scala index eda74f0..f778037 100644 --- a/src/main/scala/lc/core/captchaProviders.scala +++ b/src/main/scala/lc/core/captchaProviders.scala @@ -4,6 +4,7 @@ import lc.captchas._ import lc.captchas.interfaces.ChallengeProvider import lc.captchas.interfaces.Challenge import scala.collection.mutable.Map +import lc.misc.HelperFunctions object CaptchaProviders { private val providers = Map( @@ -23,15 +24,8 @@ object CaptchaProviders { } } - private val seed = Config.seed - private val random = new scala.util.Random(seed) private val config = Config.captchaConfig - private def getNextRandomInt(max: Int): Int = - random.synchronized { - random.nextInt(max) - } - def getProviderById(id: String): ChallengeProvider = { return providers(id) } @@ -58,7 +52,7 @@ object CaptchaProviders { def getProvider(param: Parameters): Option[ChallengeProvider] = { val providerConfig = filterProviderByParam(param).toList if (providerConfig.length > 0) { - val randomIndex = getNextRandomInt(providerConfig.length) + val randomIndex = HelperFunctions.randomNumber(providerConfig.length) val providerIndex = providerConfig(randomIndex)._1 val selectedProvider = providers(providerIndex) selectedProvider.configure(providerConfig(randomIndex)._2) diff --git a/src/main/scala/lc/core/config.scala b/src/main/scala/lc/core/config.scala index 1ada43f..f2da049 100644 --- a/src/main/scala/lc/core/config.scala +++ b/src/main/scala/lc/core/config.scala @@ -6,6 +6,7 @@ import org.json4s.jackson.JsonMethods.{parse, render, pretty} import org.json4s.JsonDSL._ import java.io.{FileNotFoundException, File, PrintWriter} import java.{util => ju} +import lc.misc.HelperFunctions object Config { @@ -49,6 +50,8 @@ object Config { val allowedMedia: Set[String] = captchaConfig.flatMap(_.allowedMedia).toSet val allowedInputType: Set[String] = captchaConfig.flatMap(_.allowedInputType).toSet + HelperFunctions.setSeed(seed) + private def getDefaultConfig(): String = { val defaultConfigMap = (AttributesEnum.RANDOM_SEED.toString -> new ju.Random().nextInt()) ~ diff --git a/src/main/scala/lc/database/statements.scala b/src/main/scala/lc/database/statements.scala index fdbd637..2d3d276 100644 --- a/src/main/scala/lc/database/statements.scala +++ b/src/main/scala/lc/database/statements.scala @@ -116,7 +116,7 @@ object Statements { org.h2.jdbc.JdbcSQLNonTransientException: General error: "java.lang.NullPointerException"; SQL statement: SELECT image FROM challenge c, mapId m WHERE c.token=m.token AND m.uuid = ? [50000-200] ``` - */ + */ private val dbConn: DBConn = new DBConn() private val maxAttempts = 10 val tlStmts: ThreadLocal[Statements] = ThreadLocal.withInitial(() => new Statements(dbConn, maxAttempts)) diff --git a/src/main/scala/lc/server/Server.scala b/src/main/scala/lc/server/Server.scala index efc3d33..ffb3d62 100644 --- a/src/main/scala/lc/server/Server.scala +++ b/src/main/scala/lc/server/Server.scala @@ -11,37 +11,50 @@ import scala.io.Source import org.limium.picoserve.Server.StringResponse class Server(port: Int) { - val server: picoserve.Server = picoserve.Server.builder() + val server: picoserve.Server = picoserve.Server + .builder() .port(port) .backlog(32) - .POST("/v1/captcha", (request) => { - val json = parse(request.getBodyString()) - val param = json.extract[Parameters] - val id = Captcha.getChallenge(param) - getResponse(id) - }) - .GET("/v1/media", (request) => { - val params = request.getQueryParams() - val result = if (params.containsKey("id")) { - val paramId = params.get("id").get(0) - val id = Id(paramId) - Captcha.getCaptcha(id) - } else { - Left(Error(ErrorMessageEnum.INVALID_PARAM.toString + "=> id")) + .POST( + "/v1/captcha", + (request) => { + val json = parse(request.getBodyString()) + val param = json.extract[Parameters] + val id = Captcha.getChallenge(param) + getResponse(id) } - getResponse(result) - }) - .POST("/v1/answer", (request) => { - val json = parse(request.getBodyString()) - val answer = json.extract[Answer] - val result = Captcha.checkAnswer(answer) - getResponse(result) - }) - .GET("/demo/index.html", (_) => { - val resStream = getClass().getResourceAsStream("/index.html") - val str = Source.fromInputStream(resStream).mkString - new StringResponse(200, str) - }) + ) + .GET( + "/v1/media", + (request) => { + val params = request.getQueryParams() + val result = if (params.containsKey("id")) { + val paramId = params.get("id").get(0) + val id = Id(paramId) + Captcha.getCaptcha(id) + } else { + Left(Error(ErrorMessageEnum.INVALID_PARAM.toString + "=> id")) + } + getResponse(result) + } + ) + .POST( + "/v1/answer", + (request) => { + val json = parse(request.getBodyString()) + val answer = json.extract[Answer] + val result = Captcha.checkAnswer(answer) + getResponse(result) + } + ) + .GET( + "/demo/index.html", + (_) => { + val resStream = getClass().getResourceAsStream("/index.html") + val str = Source.fromInputStream(resStream).mkString + new StringResponse(200, str) + } + ) .build() private def getResponse(response: Either[Error, ByteConvert]): ByteResponse = {