Linter and Formatter support (#58)

* Add scala linter and formatter

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Add java formatter

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Add linter support

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Increase maxColumn limit

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Reformat and lint

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Minor reformatting

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Add scala formatter on compile option

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Enable scala linter for CI

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>
This commit is contained in:
Rahul Rudragoudar 2021-02-25 23:49:39 +05:30 committed by GitHub
parent 6d04cdc3b4
commit de50d8123e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 3445 additions and 3410 deletions

View File

@ -19,3 +19,5 @@ jobs:
java-version: 1.8
- name: Run tests
run: sbt test
- name: Run linter
run: sbt "scalafixAll --check"

8
.scalafix.conf Normal file
View File

@ -0,0 +1,8 @@
rules=[
ExplicitResultTypes,
RemoveUnused,
DisableSyntax,
LeakingImplicitClassVal,
NoValInForComprehension,
ProcedureSyntax
]

2
.scalafmt.conf Normal file
View File

@ -0,0 +1,2 @@
version=2.5.2
maxColumn = 120

View File

@ -4,3 +4,4 @@ scala:
- 2.13.2
script:
- sbt ++$TRAVIS_SCALA_VERSION compile
- sbt "scalafixAll --check"

View File

@ -1,21 +1,27 @@
lazy val root = (project in file(".")).
settings(
inThisBuild(List(
lazy val root = (project in file(".")).settings(
inThisBuild(
List(
organization := "com.example",
scalaVersion := "2.13.3",
version := "0.1.0-SNAPSHOT")),
version := "0.1.0-SNAPSHOT",
semanticdbEnabled := true,
semanticdbVersion := scalafixSemanticdb.revision,
scalafixScalaBinaryVersion := "2.13"
)
),
name := "LibreCaptcha",
libraryDependencies += "com.sksamuel.scrimage" % "scrimage-core" % "4.0.5",
libraryDependencies += "com.sksamuel.scrimage" % "scrimage-filters" % "4.0.5",
libraryDependencies += "org.json4s" % "json4s-jackson_2.13" % "3.6.9"
)
unmanagedResourceDirectories in Compile += { baseDirectory.value / "lib" }
scalacOptions ++= List(
"-Yrangepos",
"-Ywarn-unused"
)
javacOptions += "-g:none"
scalafmtOnCompile := true
compileOrder := CompileOrder.JavaThenScala
fork in run := true

3
project/plugins.sbt Normal file
View File

@ -0,0 +1,3 @@
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.25")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.0")
addSbtPlugin("com.lightbend.sbt" % "sbt-java-formatter" % "0.6.0")

View File

@ -18,18 +18,19 @@ public class FontFunCaptcha implements ChallengeProvider{
private String getFontName(String path, String level) {
File file = new File(path + level + "/");
FilenameFilter txtFileFilter = new FilenameFilter() {
FilenameFilter txtFileFilter =
new FilenameFilter() {
@Override
public boolean accept(File dir, String name)
{
if(name.endsWith(".ttf"))
return true;
else
return false;
public boolean accept(File dir, String name) {
if (name.endsWith(".ttf")) return true;
else return false;
}
};
File[] files = file.listFiles(txtFileFilter);
return path+level.toLowerCase()+"/"+files[HelperFunctions.randomNumber(0,files.length-1)].getName();
return path
+ level.toLowerCase()
+ "/"
+ files[HelperFunctions.randomNumber(0, files.length - 1)].getName();
}
private Font loadCustomFont(String level, String path) {
@ -54,7 +55,8 @@ public class FontFunCaptcha implements ChallengeProvider{
FontMetrics fontMetrics = graphics2D.getFontMetrics();
HelperFunctions.setRenderingHints(graphics2D);
graphics2D.setColor(Color.decode(colors[HelperFunctions.randomNumber(0, 3)]));
graphics2D.drawString(String.valueOf(captchaText.charAt(i)), (i * 48), fontMetrics.getAscent());
graphics2D.drawString(
String.valueOf(captchaText.charAt(i)), (i * 48), fontMetrics.getAscent());
}
graphics2D.dispose();
ByteArrayOutputStream baos = new ByteArrayOutputStream();

View File

@ -21,7 +21,8 @@ public class GifCaptcha implements ChallengeProvider{
Font font = new Font("Bradley Hand", Font.ROMAN_BASELINE, 48);
Graphics2D graphics2D = img.createGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
graphics2D.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
graphics2D.setFont(font);
graphics2D.setColor(new Color((int) (Math.random() * 0x1000000)));
graphics2D.drawString(text, 45, 45);

View File

@ -29,7 +29,8 @@ public class ShadowTextCaptcha implements ChallengeProvider{
Font font = new Font("Arial", Font.ROMAN_BASELINE, 48);
Graphics2D graphics2D = img.createGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
graphics2D.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
TextLayout textLayout = new TextLayout(text, font, graphics2D.getFontRenderContext());
HelperFunctions.setRenderingHints(graphics2D);
@ -43,8 +44,7 @@ public class ShadowTextCaptcha implements ChallengeProvider{
1f / 9f, 1f / 9f, 1f / 9f,
1f / 9f, 1f / 9f, 1f / 9f
};
ConvolveOp op = new ConvolveOp(new Kernel(3, 3, kernel),
ConvolveOp.EDGE_NO_OP, null);
ConvolveOp op = new ConvolveOp(new Kernel(3, 3, kernel), ConvolveOp.EDGE_NO_OP, null);
BufferedImage img2 = op.filter(img, null);
Graphics2D g2d = img2.createGraphics();
HelperFunctions.setRenderingHints(g2d);

View File

@ -2,7 +2,9 @@ package lc.captchas.interfaces;
public interface ChallengeProvider {
public String getId();
public Challenge returnChallenge();
public boolean checkAnswer(String secret, String answer);
// TODO: def configure(): Unit

View File

@ -2,6 +2,7 @@
// It was available under CC By 3.0
package lc.misc;
import javax.imageio.*;
import javax.imageio.metadata.*;
import javax.imageio.stream.*;
@ -22,51 +23,39 @@ public class GifSequenceWriter {
* @param timeBetweenFramesMS the time between frames in miliseconds
* @param loopContinuously wether the gif should loop repeatedly
* @throws IIOException if no gif ImageWriters are found
*
* @author Elliot Kroo (elliot[at]kroo[dot]net)
*/
public GifSequenceWriter(
ImageOutputStream outputStream,
int imageType,
int timeBetweenFramesMS,
boolean loopContinuously) throws IIOException, IOException {
boolean loopContinuously)
throws IIOException, IOException {
// my method to create a writer
gifWriter = getWriter();
imageWriteParam = gifWriter.getDefaultWriteParam();
ImageTypeSpecifier imageTypeSpecifier =
ImageTypeSpecifier.createFromBufferedImageType(imageType);
imageMetaData =
gifWriter.getDefaultImageMetadata(imageTypeSpecifier,
imageWriteParam);
imageMetaData = gifWriter.getDefaultImageMetadata(imageTypeSpecifier, imageWriteParam);
String metaFormatName = imageMetaData.getNativeMetadataFormatName();
IIOMetadataNode root = (IIOMetadataNode)
imageMetaData.getAsTree(metaFormatName);
IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName);
IIOMetadataNode graphicsControlExtensionNode = getNode(
root,
"GraphicControlExtension");
IIOMetadataNode graphicsControlExtensionNode = getNode(root, "GraphicControlExtension");
graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE");
graphicsControlExtensionNode.setAttribute(
"transparentColorFlag",
"FALSE");
graphicsControlExtensionNode.setAttribute(
"delayTime",
Integer.toString(timeBetweenFramesMS / 10));
graphicsControlExtensionNode.setAttribute(
"transparentColorIndex",
"0");
"delayTime", Integer.toString(timeBetweenFramesMS / 10));
graphicsControlExtensionNode.setAttribute("transparentColorIndex", "0");
IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
commentsNode.setAttribute("CommentExtension", "Created by MAH");
IIOMetadataNode appEntensionsNode = getNode(
root,
"ApplicationExtensions");
IIOMetadataNode appEntensionsNode = getNode(root, "ApplicationExtensions");
IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
@ -75,8 +64,7 @@ public class GifSequenceWriter {
int loop = loopContinuously ? 0 : 1;
child.setUserObject(new byte[]{ 0x1, (byte) (loop & 0xFF), (byte)
((loop >> 8) & 0xFF)});
child.setUserObject(new byte[] {0x1, (byte) (loop & 0xFF), (byte) ((loop >> 8) & 0xFF)});
appEntensionsNode.appendChild(child);
imageMetaData.setFromTree(metaFormatName, root);
@ -87,25 +75,19 @@ public class GifSequenceWriter {
}
public void writeToSequence(RenderedImage img) throws IOException {
gifWriter.writeToSequence(
new IIOImage(
img,
null,
imageMetaData),
imageWriteParam);
gifWriter.writeToSequence(new IIOImage(img, null, imageMetaData), imageWriteParam);
}
/**
* Close this GifSequenceWriter object. This does not close the underlying
* stream, just finishes off the GIF.
* Close this GifSequenceWriter object. This does not close the underlying stream, just finishes
* off the GIF.
*/
public void close() throws IOException {
gifWriter.endWriteSequence();
}
/**
* Returns the first available GIF ImageWriter using
* ImageIO.getImageWritersBySuffix("gif").
* Returns the first available GIF ImageWriter using ImageIO.getImageWritersBySuffix("gif").
*
* @return a GIF ImageWriter object
* @throws IIOException if no GIF image writers are returned
@ -120,21 +102,17 @@ public class GifSequenceWriter {
}
/**
* Returns an existing child node, or creates and returns a new child node (if
* the requested node does not exist).
* Returns an existing child node, or creates and returns a new child node (if the requested node
* does not exist).
*
* @param rootNode the <tt>IIOMetadataNode</tt> to search for the child node.
* @param nodeName the name of the child node.
*
* @return the child node, if found or a new node created with the given name.
*/
private static IIOMetadataNode getNode(
IIOMetadataNode rootNode,
String nodeName) {
private static IIOMetadataNode getNode(IIOMetadataNode rootNode, String nodeName) {
int nNodes = rootNode.getLength();
for (int i = 0; i < nNodes; i++) {
if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName)
== 0) {
if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName) == 0) {
return ((IIOMetadataNode) rootNode.item(i));
}
}

View File

@ -5,10 +5,10 @@ import java.awt.*;
public class HelperFunctions {
public static void setRenderingHints(Graphics2D g2d) {
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setRenderingHint(
RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
}
public static String randomString(int n) {

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,11 @@
package lc
import org.json4s.DefaultFormats
import org.json4s.jackson.JsonMethods._
import scala.io.Source.fromFile
import lc.database.Statements
import lc.core.{Captcha, CaptchaProviders}
import lc.server.Server
import lc.background.BackgroundTask
object LCFramework {
def main(args: scala.Array[String]) {
def main(args: scala.Array[String]): Unit = {
val captcha = new Captcha()
val server = new Server(8888, captcha)
val backgroudTask = new BackgroundTask(captcha, 10)
@ -20,9 +15,10 @@ object LCFramework{
}
object MakeSamples {
def main(args: scala.Array[String]) {
def main(args: scala.Array[String]): Unit = {
val samples = CaptchaProviders.generateChallengeSamples()
samples.foreach {case (key, sample) =>
samples.foreach {
case (key, sample) =>
val extensionMap = Map("image/png" -> "png", "image/gif" -> "gif")
println(key + ": " + sample)

View File

@ -5,7 +5,6 @@ import java.util.concurrent.{ScheduledThreadPoolExecutor, TimeUnit}
import lc.core.Captcha
import lc.core.{Parameters, Size}
class BackgroundTask(captcha: Captcha, throttle: Int) {
private val task = new Runnable {
@ -32,7 +31,7 @@ class BackgroundTask(captcha: Captcha, throttle: Int) {
def beginThread(delay: Int): Unit = {
val ex = new ScheduledThreadPoolExecutor(1)
val thread = ex.scheduleWithFixedDelay(task, 1, delay, TimeUnit.SECONDS)
ex.scheduleWithFixedDelay(task, 1, delay, TimeUnit.SECONDS)
}
}

View File

@ -8,7 +8,6 @@ import java.awt.Color
import lc.captchas.interfaces.ChallengeProvider
import lc.captchas.interfaces.Challenge
class FilterChallenge extends ChallengeProvider {
def getId = "FilterChallenge"
def returnChallenge(): Challenge = {
@ -62,4 +61,3 @@ class FilterType2 extends FilterType {
image
}
}

View File

@ -4,17 +4,17 @@ import java.io.File
import java.io.ByteArrayOutputStream
import javax.imageio.ImageIO
import scala.collection.mutable.Map
import java.nio.file.{Files,Path,StandardCopyOption}
import java.nio.file.{Files, StandardCopyOption}
import java.awt.image.BufferedImage
import java.awt.{Graphics2D,Color}
import java.awt.Color
import lc.captchas.interfaces.ChallengeProvider
import lc.captchas.interfaces.Challenge
class LabelCaptcha extends ChallengeProvider {
private var knownFiles = new File("known").list.toList
private var unknownFiles = new File("unknown").list.toList
private var unknownAnswers = Map[String, Map[String, Int]]()
private var total = Map[String, Int]()
private val unknownAnswers = Map[String, Map[String, Int]]()
private val total = Map[String, Int]()
for (file <- unknownFiles) {
unknownAnswers += file -> Map[String, Int]()
@ -23,15 +23,15 @@ class LabelCaptcha extends ChallengeProvider {
def getId = "LabelCaptcha"
def returnChallenge(): Challenge = synchronized {
def returnChallenge(): Challenge =
synchronized {
val r = scala.util.Random.nextInt(knownFiles.length)
val s = scala.util.Random.nextInt(unknownFiles.length)
val knownImageFile = knownFiles(r)
val unknownImageFile = unknownFiles(s)
val ip = new ImagePair(knownImageFile, unknownImageFile)
var knownImage = ImageIO.read(new File("known/"+knownImageFile))
var unknownImage = ImageIO.read(new File("unknown/"+unknownImageFile))
val knownImage = ImageIO.read(new File("known/" + knownImageFile))
val unknownImage = ImageIO.read(new File("unknown/" + unknownImageFile))
val mergedImage = merge(knownImage, unknownImage)
val token = encrypt(knownImageFile + "," + unknownImageFile)
@ -55,7 +55,8 @@ class LabelCaptcha extends ChallengeProvider {
finalImage
}
def checkAnswer(token: String, input: String): Boolean = synchronized {
def checkAnswer(token: String, input: String): Boolean =
synchronized {
val parts = decrypt(token).split(",")
val knownImage = parts(0)
val unknownImage = parts(1)
@ -73,7 +74,11 @@ class LabelCaptcha extends ChallengeProvider {
if (total(unknownFile) >= 3) {
if ((unknownAnswers(unknownFile)(userAnswer(1)) / total(unknownFile)) >= 0.9) {
unknownAnswers -= unknownFile
Files.move(new File("unknown/"+unknownFile).toPath, new File("known/"+userAnswer(1)+".png").toPath, StandardCopyOption.REPLACE_EXISTING)
Files.move(
new File("unknown/" + unknownFile).toPath,
new File("known/" + userAnswer(1) + ".png").toPath,
StandardCopyOption.REPLACE_EXISTING
)
knownFiles = new File("known").list.toList
unknownFiles = new File("unknown").list.toList
}

View File

@ -5,9 +5,7 @@ import java.awt.RenderingHints
import java.awt.Font
import java.awt.font.TextAttribute
import java.awt.Color
import java.io.ByteArrayOutputStream
import javax.imageio.ImageIO
import javax.imageio.stream.ImageOutputStream;
import java.io.ByteArrayOutputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import lc.captchas.interfaces.ChallengeProvider
import lc.captchas.interfaces.Challenge
@ -19,7 +17,7 @@ class Drop {
var yOffset = 0
var color = 0
var colorChange = 10
def mkColor = {
def mkColor: Color = {
new Color(color, color, math.min(200, color + 100))
}
}
@ -60,8 +58,11 @@ class RainDropsCP extends ChallengeProvider {
d.colorChange *= -1
}
}
val drops = dropsOrig ++ extendDrops(dropsOrig, 1, xOffset) ++ extendDrops(dropsOrig, 2, xOffset) ++ extendDrops(dropsOrig, 3, xOffset)
val drops = dropsOrig ++ extendDrops(dropsOrig, 1, xOffset) ++ extendDrops(dropsOrig, 2, xOffset) ++ extendDrops(
dropsOrig,
3,
xOffset
)
val baseFont = new Font(Font.MONOSPACED, Font.BOLD, 80)
val attributes = new java.util.HashMap[TextAttribute, Object]()
@ -72,7 +73,7 @@ class RainDropsCP extends ChallengeProvider {
val baos = new ByteArrayOutputStream();
val ios = new MemoryCacheImageOutputStream(baos);
val writer = new GifSequenceWriter(ios, imgType, 60, true);
for(i <- 0 until 60){
for (_ <- 0 until 60) {
// val yOffset = 5+r.nextInt(5)
val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
val g = canvas.createGraphics()

View File

@ -1,6 +1,5 @@
package lc.core
import org.json4s.JsonAST.JValue
import java.sql.{Blob, ResultSet}
import java.util.UUID
import java.io.ByteArrayInputStream
@ -23,7 +22,8 @@ class Captcha {
}
}
image
} catch { case e: Exception =>
} catch {
case e: Exception =>
println(e)
image
}
@ -64,7 +64,8 @@ class Captcha {
updateAttemptedPstmt.setString(1, uuid)
updateAttemptedPstmt.executeUpdate()
Id(uuid)
} catch {case e: Exception =>
} catch {
case e: Exception =>
println(e)
Id(getUUID(-1))
}

View File

@ -2,6 +2,7 @@ package lc.core
import lc.captchas._
import lc.captchas.interfaces.ChallengeProvider
import lc.captchas.interfaces.Challenge
object CaptchaProviders {
private val providers = Map(
@ -9,12 +10,13 @@ object CaptchaProviders {
//"FontFunCaptcha" -> new FontFunCaptcha,
"GifCaptcha" -> new GifCaptcha,
"ShadowTextCaptcha" -> new ShadowTextCaptcha,
"RainDropsCaptcha" -> new RainDropsCP,
"RainDropsCaptcha" -> new RainDropsCP
//"LabelCaptcha" -> new LabelCaptcha
)
def generateChallengeSamples() = {
providers.map {case (key, provider) =>
def generateChallengeSamples(): Map[String, Challenge] = {
providers.map {
case (key, provider) =>
(key, provider.returnChallenge())
}
}
@ -22,7 +24,8 @@ object CaptchaProviders {
private val seed = System.currentTimeMillis.toString.substring(2, 6).toInt
private val random = new scala.util.Random(seed)
private def getNextRandomInt(max: Int) = random.synchronized {
private def getNextRandomInt(max: Int) =
random.synchronized {
random.nextInt(max)
}

View File

@ -2,30 +2,108 @@ package lc.database
import lc.database.DBConn
import java.sql.Statement
import java.sql.PreparedStatement
class Statements(dbConn: DBConn) {
private val stmt = dbConn.getStatement()
stmt.execute("CREATE TABLE IF NOT EXISTS challenge(token int auto_increment, id varchar, secret varchar, provider varchar, contentType varchar, image blob, attempted int default 0, PRIMARY KEY(token))")
stmt.execute("CREATE TABLE IF NOT EXISTS mapId(uuid varchar, token int, lastServed timestamp, PRIMARY KEY(uuid), FOREIGN KEY(token) REFERENCES challenge(token) ON DELETE CASCADE)")
stmt.execute(
"CREATE TABLE IF NOT EXISTS challenge" +
"(token int auto_increment, " +
"id varchar, " +
"secret varchar, " +
"provider varchar, " +
"contentType varchar, " +
"image blob, " +
"attempted int default 0, " +
"PRIMARY KEY(token))"
)
stmt.execute(
"CREATE TABLE IF NOT EXISTS mapId" +
"(uuid varchar, " +
"token int, " +
"lastServed timestamp, " +
"PRIMARY KEY(uuid), " +
"FOREIGN KEY(token) " +
"REFERENCES challenge(token) " +
"ON DELETE CASCADE)"
)
val insertPstmt = dbConn.con.prepareStatement("INSERT INTO challenge(id, secret, provider, contentType, image) VALUES (?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS )
val mapPstmt = dbConn.con.prepareStatement("INSERT INTO mapId(uuid, token, lastServed) VALUES (?, ?, CURRENT_TIMESTAMP)")
val selectPstmt = dbConn.con.prepareStatement("SELECT c.secret, c.provider FROM challenge c, mapId m WHERE m.token=c.token AND DATEDIFF(MINUTE, CURRENT_TIMESTAMP, DATEADD(MINUTE, 1, m.lastServed)) > 0 AND m.uuid = ?")
val imagePstmt = dbConn.con.prepareStatement("SELECT image FROM challenge c, mapId m WHERE c.token=m.token AND m.uuid = ?")
val updateAttemptedPstmt = dbConn.con.prepareStatement("UPDATE challenge SET attempted = attempted+1 WHERE token = (SELECT m.token FROM mapId m, challenge c WHERE m.token=c.token AND m.uuid = ?)")
val tokenPstmt = dbConn.con.prepareStatement("SELECT token FROM challenge WHERE attempted < 10 ORDER BY RAND() LIMIT 1")
val deleteAnswerPstmt = dbConn.con.prepareStatement("DELETE FROM mapId WHERE uuid = ?")
val challengeGCPstmt = dbConn.con.prepareStatement("DELETE FROM challenge WHERE attempted >= 10 AND token NOT IN (SELECT token FROM mapId)")
val mapIdGCPstmt = dbConn.con.prepareStatement("DELETE FROM mapId WHERE DATEDIFF(MINUTE, CURRENT_TIMESTAMP, DATEADD(MINUTE, 1, lastServed)) < 0")
val insertPstmt: PreparedStatement = dbConn.con.prepareStatement(
"INSERT INTO " +
"challenge(id, secret, provider, contentType, image) " +
"VALUES (?, ?, ?, ?, ?)",
Statement.RETURN_GENERATED_KEYS
)
val mapPstmt: PreparedStatement =
dbConn.con.prepareStatement(
"INSERT INTO " +
"mapId(uuid, token, lastServed) " +
"VALUES (?, ?, CURRENT_TIMESTAMP)"
)
val selectPstmt: PreparedStatement = dbConn.con.prepareStatement(
"SELECT c.secret, c.provider " +
"FROM challenge c, mapId m " +
"WHERE m.token=c.token AND " +
"DATEDIFF(MINUTE, CURRENT_TIMESTAMP, DATEADD(MINUTE, 1, m.lastServed)) > 0 AND " +
"m.uuid = ?"
)
val imagePstmt: PreparedStatement = dbConn.con.prepareStatement(
"SELECT image " +
"FROM challenge c, mapId m " +
"WHERE c.token=m.token AND " +
"m.uuid = ?"
)
val updateAttemptedPstmt: PreparedStatement = dbConn.con.prepareStatement(
"UPDATE challenge " +
"SET attempted = attempted+1 " +
"WHERE token = (SELECT m.token " +
"FROM mapId m, challenge c " +
"WHERE m.token=c.token AND " +
"m.uuid = ?)"
)
val tokenPstmt: PreparedStatement = dbConn.con.prepareStatement(
"SELECT token " +
"FROM challenge " +
"WHERE attempted < 10 " +
"ORDER BY RAND() LIMIT 1"
)
val deleteAnswerPstmt: PreparedStatement = dbConn.con.prepareStatement(
"DELETE FROM mapId WHERE uuid = ?"
)
val challengeGCPstmt: PreparedStatement = dbConn.con.prepareStatement(
"DELETE FROM challenge " +
"WHERE attempted >= 10 AND " +
"token NOT IN (SELECT token FROM mapId)"
)
val mapIdGCPstmt: PreparedStatement = dbConn.con.prepareStatement(
"DELETE FROM mapId WHERE DATEDIFF(MINUTE, CURRENT_TIMESTAMP, DATEADD(MINUTE, 1, lastServed)) < 0"
)
val getCountChallengeTable: PreparedStatement = dbConn.con.prepareStatement(
"SELECT COUNT(*) AS total FROM challenge"
)
val getChallengeTable: PreparedStatement = dbConn.con.prepareStatement(
"SELECT * FROM challenge"
)
val getMapIdTable: PreparedStatement = dbConn.con.prepareStatement(
"SELECT * FROM mapId"
)
val getCountChallengeTable = dbConn.con.prepareStatement("SELECT COUNT(*) AS total FROM challenge")
val getChallengeTable = dbConn.con.prepareStatement("SELECT * FROM challenge")
val getMapIdTable = dbConn.con.prepareStatement("SELECT * FROM mapId")
}
object Statements {
private val dbConn: DBConn = new DBConn()
val tlStmts = ThreadLocal.withInitial(() => new Statements(dbConn))
val tlStmts: ThreadLocal[Statements] = ThreadLocal.withInitial(() => new Statements(dbConn))
}

View File

@ -7,14 +7,15 @@ import lc.core.Captcha
import lc.core.{Parameters, Id, Answer}
import lc.server.HTTPServer
class Server(port: Int, captcha: Captcha) {
val server = new HTTPServer(port)
val host = server.getVirtualHost(null)
val host: HTTPServer.VirtualHost = server.getVirtualHost(null)
implicit val formats = DefaultFormats
implicit val formats: DefaultFormats.type = DefaultFormats
host.addContext("/v1/captcha",(req, resp) => {
host.addContext(
"/v1/captcha",
(req, resp) => {
val body = req.getJson()
val json = parse(body)
val param = json.extract[Parameters]
@ -22,18 +23,26 @@ class Server(port: Int, captcha: Captcha){
resp.getHeaders().add("Content-Type", "application/json")
resp.send(200, write(id))
0
},"POST")
},
"POST"
)
host.addContext("/v1/media",(req, resp) => {
host.addContext(
"/v1/media",
(req, resp) => {
val params = req.getParams()
val id = Id(params.get("id"))
val image = captcha.getCaptcha(id)
resp.getHeaders().add("Content-Type", "image/png")
resp.send(200, image)
0
},"GET")
},
"GET"
)
host.addContext("/v1/answer",(req, resp) => {
host.addContext(
"/v1/answer",
(req, resp) => {
val body = req.getJson()
val json = parse(body)
val answer = json.extract[Answer]
@ -41,8 +50,9 @@ class Server(port: Int, captcha: Captcha){
resp.getHeaders().add("Content-Type", "application/json")
resp.send(200, write(result))
0
},"POST")
},
"POST"
)
def start(): Unit = {
println("Starting server on port:" + port)