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:
parent
6d04cdc3b4
commit
de50d8123e
|
@ -19,3 +19,5 @@ jobs:
|
||||||
java-version: 1.8
|
java-version: 1.8
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: sbt test
|
run: sbt test
|
||||||
|
- name: Run linter
|
||||||
|
run: sbt "scalafixAll --check"
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
rules=[
|
||||||
|
ExplicitResultTypes,
|
||||||
|
RemoveUnused,
|
||||||
|
DisableSyntax,
|
||||||
|
LeakingImplicitClassVal,
|
||||||
|
NoValInForComprehension,
|
||||||
|
ProcedureSyntax
|
||||||
|
]
|
|
@ -0,0 +1,2 @@
|
||||||
|
version=2.5.2
|
||||||
|
maxColumn = 120
|
|
@ -4,3 +4,4 @@ scala:
|
||||||
- 2.13.2
|
- 2.13.2
|
||||||
script:
|
script:
|
||||||
- sbt ++$TRAVIS_SCALA_VERSION compile
|
- sbt ++$TRAVIS_SCALA_VERSION compile
|
||||||
|
- sbt "scalafixAll --check"
|
||||||
|
|
32
build.sbt
32
build.sbt
|
@ -1,21 +1,27 @@
|
||||||
lazy val root = (project in file(".")).
|
lazy val root = (project in file(".")).settings(
|
||||||
settings(
|
inThisBuild(
|
||||||
inThisBuild(List(
|
List(
|
||||||
organization := "com.example",
|
organization := "com.example",
|
||||||
scalaVersion := "2.13.3",
|
scalaVersion := "2.13.3",
|
||||||
version := "0.1.0-SNAPSHOT")),
|
version := "0.1.0-SNAPSHOT",
|
||||||
name := "LibreCaptcha",
|
semanticdbEnabled := true,
|
||||||
|
semanticdbVersion := scalafixSemanticdb.revision,
|
||||||
libraryDependencies += "com.sksamuel.scrimage" % "scrimage-core" % "4.0.5",
|
scalafixScalaBinaryVersion := "2.13"
|
||||||
|
)
|
||||||
libraryDependencies += "com.sksamuel.scrimage" % "scrimage-filters" % "4.0.5",
|
),
|
||||||
|
name := "LibreCaptcha",
|
||||||
libraryDependencies += "org.json4s" % "json4s-jackson_2.13" % "3.6.9"
|
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"}
|
unmanagedResourceDirectories in Compile += { baseDirectory.value / "lib" }
|
||||||
|
scalacOptions ++= List(
|
||||||
|
"-Yrangepos",
|
||||||
|
"-Ywarn-unused"
|
||||||
|
)
|
||||||
javacOptions += "-g:none"
|
javacOptions += "-g:none"
|
||||||
|
scalafmtOnCompile := true
|
||||||
compileOrder := CompileOrder.JavaThenScala
|
compileOrder := CompileOrder.JavaThenScala
|
||||||
|
|
||||||
fork in run := true
|
fork in run := true
|
||||||
|
|
|
@ -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")
|
|
@ -10,69 +10,71 @@ import lc.captchas.interfaces.Challenge;
|
||||||
import lc.captchas.interfaces.ChallengeProvider;
|
import lc.captchas.interfaces.ChallengeProvider;
|
||||||
import lc.misc.HelperFunctions;
|
import lc.misc.HelperFunctions;
|
||||||
|
|
||||||
public class FontFunCaptcha implements ChallengeProvider{
|
public class FontFunCaptcha implements ChallengeProvider {
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return "FontFunCaptcha";
|
return "FontFunCaptcha";
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getFontName(String path, String level){
|
private String getFontName(String path, String level) {
|
||||||
File file = new File(path+level+"/");
|
File file = new File(path + level + "/");
|
||||||
FilenameFilter txtFileFilter = new FilenameFilter() {
|
FilenameFilter txtFileFilter =
|
||||||
@Override
|
new FilenameFilter() {
|
||||||
public boolean accept(File dir, String name)
|
@Override
|
||||||
{
|
public boolean accept(File dir, String name) {
|
||||||
if(name.endsWith(".ttf"))
|
if (name.endsWith(".ttf")) return true;
|
||||||
return true;
|
else return false;
|
||||||
else
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
File[] files = file.listFiles(txtFileFilter);
|
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) {
|
private Font loadCustomFont(String level, String path) {
|
||||||
String fontName = getFontName(path,level);
|
String fontName = getFontName(path, level);
|
||||||
try{
|
try {
|
||||||
Font font = Font.createFont(Font.TRUETYPE_FONT, new File(fontName));
|
Font font = Font.createFont(Font.TRUETYPE_FONT, new File(fontName));
|
||||||
font = font.deriveFont(Font.PLAIN, 48f);
|
font = font.deriveFont(Font.PLAIN, 48f);
|
||||||
return font;
|
return font;
|
||||||
} catch (Exception e){
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private byte[] fontFun(String captchaText, String level, String path){
|
private byte[] fontFun(String captchaText, String level, String path) {
|
||||||
String[] colors = {"#f68787","#f8a978","#f1eb9a","#a4f6a5"};
|
String[] colors = {"#f68787", "#f8a978", "#f1eb9a", "#a4f6a5"};
|
||||||
BufferedImage img = new BufferedImage(350, 100, BufferedImage.TYPE_INT_RGB);
|
BufferedImage img = new BufferedImage(350, 100, BufferedImage.TYPE_INT_RGB);
|
||||||
Graphics2D graphics2D = img.createGraphics();
|
Graphics2D graphics2D = img.createGraphics();
|
||||||
for(int i=0; i< captchaText.length(); i++) {
|
for (int i = 0; i < captchaText.length(); i++) {
|
||||||
Font font = loadCustomFont(level,path);
|
Font font = loadCustomFont(level, path);
|
||||||
graphics2D.setFont(font);
|
graphics2D.setFont(font);
|
||||||
FontMetrics fontMetrics = graphics2D.getFontMetrics();
|
FontMetrics fontMetrics = graphics2D.getFontMetrics();
|
||||||
HelperFunctions.setRenderingHints(graphics2D);
|
HelperFunctions.setRenderingHints(graphics2D);
|
||||||
graphics2D.setColor(Color.decode(colors[HelperFunctions.randomNumber(0,3)]));
|
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();
|
|
||||||
try {
|
|
||||||
ImageIO.write(img,"png",baos);
|
|
||||||
}catch (Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return baos.toByteArray();
|
|
||||||
}
|
}
|
||||||
|
graphics2D.dispose();
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
ImageIO.write(img, "png", baos);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
public Challenge returnChallenge() {
|
public Challenge returnChallenge() {
|
||||||
String secret = HelperFunctions.randomString(7);
|
String secret = HelperFunctions.randomString(7);
|
||||||
String path = "./lib/fonts/";
|
String path = "./lib/fonts/";
|
||||||
return new Challenge(fontFun(secret,"medium",path),"image/png",secret.toLowerCase());
|
return new Challenge(fontFun(secret, "medium", path), "image/png", secret.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkAnswer(String secret, String answer){
|
public boolean checkAnswer(String secret, String answer) {
|
||||||
return answer.toLowerCase().equals(secret);
|
return answer.toLowerCase().equals(secret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,49 +14,50 @@ import lc.captchas.interfaces.ChallengeProvider;
|
||||||
import lc.misc.HelperFunctions;
|
import lc.misc.HelperFunctions;
|
||||||
import lc.misc.GifSequenceWriter;
|
import lc.misc.GifSequenceWriter;
|
||||||
|
|
||||||
public class GifCaptcha implements ChallengeProvider{
|
public class GifCaptcha implements ChallengeProvider {
|
||||||
|
|
||||||
private BufferedImage charToImg(String text){
|
private BufferedImage charToImg(String text) {
|
||||||
BufferedImage img = new BufferedImage(250, 100, BufferedImage.TYPE_INT_RGB);
|
BufferedImage img = new BufferedImage(250, 100, BufferedImage.TYPE_INT_RGB);
|
||||||
Font font = new Font("Bradley Hand", Font.ROMAN_BASELINE, 48);
|
Font font = new Font("Bradley Hand", Font.ROMAN_BASELINE, 48);
|
||||||
Graphics2D graphics2D = img.createGraphics();
|
Graphics2D graphics2D = img.createGraphics();
|
||||||
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
graphics2D.setRenderingHint(
|
||||||
graphics2D.setFont(font);
|
RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||||
graphics2D.setColor(new Color((int)(Math.random() * 0x1000000)));
|
graphics2D.setFont(font);
|
||||||
graphics2D.drawString( text , 45, 45);
|
graphics2D.setColor(new Color((int) (Math.random() * 0x1000000)));
|
||||||
graphics2D.dispose();
|
graphics2D.drawString(text, 45, 45);
|
||||||
return img;
|
graphics2D.dispose();
|
||||||
}
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
private byte[] gifCaptcha(String text){
|
private byte[] gifCaptcha(String text) {
|
||||||
try {
|
try {
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
ImageOutputStream output = new MemoryCacheImageOutputStream(byteArrayOutputStream);
|
ImageOutputStream output = new MemoryCacheImageOutputStream(byteArrayOutputStream);
|
||||||
GifSequenceWriter writer = new GifSequenceWriter( output, 1,1000, true );
|
GifSequenceWriter writer = new GifSequenceWriter(output, 1, 1000, true);
|
||||||
for(int i=0; i< text.length(); i++){
|
for (int i = 0; i < text.length(); i++) {
|
||||||
BufferedImage nextImage = charToImg(String.valueOf(text.charAt(i)));
|
BufferedImage nextImage = charToImg(String.valueOf(text.charAt(i)));
|
||||||
writer.writeToSequence(nextImage);
|
writer.writeToSequence(nextImage);
|
||||||
}
|
}
|
||||||
writer.close();
|
writer.close();
|
||||||
output.close();
|
output.close();
|
||||||
return byteArrayOutputStream.toByteArray();
|
return byteArrayOutputStream.toByteArray();
|
||||||
} catch (IOException e){
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public Challenge returnChallenge() {
|
public Challenge returnChallenge() {
|
||||||
String secret = HelperFunctions.randomString(6);
|
String secret = HelperFunctions.randomString(6);
|
||||||
return new Challenge(gifCaptcha(secret),"image/gif",secret.toLowerCase());
|
return new Challenge(gifCaptcha(secret), "image/gif", secret.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkAnswer(String secret, String answer) {
|
public boolean checkAnswer(String secret, String answer) {
|
||||||
return answer.toLowerCase().equals(secret);
|
return answer.toLowerCase().equals(secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return "GifCaptcha";
|
return "GifCaptcha";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,54 +14,54 @@ import lc.misc.HelperFunctions;
|
||||||
import lc.captchas.interfaces.Challenge;
|
import lc.captchas.interfaces.Challenge;
|
||||||
import lc.captchas.interfaces.ChallengeProvider;
|
import lc.captchas.interfaces.ChallengeProvider;
|
||||||
|
|
||||||
public class ShadowTextCaptcha implements ChallengeProvider{
|
public class ShadowTextCaptcha implements ChallengeProvider {
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return "ShadowTextCaptcha";
|
return "ShadowTextCaptcha";
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkAnswer(String secret, String answer) {
|
||||||
|
return answer.toLowerCase().equals(secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] shadowText(String text) {
|
||||||
|
BufferedImage img = new BufferedImage(350, 100, BufferedImage.TYPE_INT_RGB);
|
||||||
|
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);
|
||||||
|
|
||||||
|
TextLayout textLayout = new TextLayout(text, font, graphics2D.getFontRenderContext());
|
||||||
|
HelperFunctions.setRenderingHints(graphics2D);
|
||||||
|
graphics2D.setPaint(Color.WHITE);
|
||||||
|
graphics2D.fillRect(0, 0, 350, 100);
|
||||||
|
graphics2D.setPaint(Color.BLACK);
|
||||||
|
textLayout.draw(graphics2D, 15, 50);
|
||||||
|
graphics2D.dispose();
|
||||||
|
float[] kernel = {
|
||||||
|
1f / 9f, 1f / 9f, 1f / 9f,
|
||||||
|
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);
|
||||||
|
BufferedImage img2 = op.filter(img, null);
|
||||||
|
Graphics2D g2d = img2.createGraphics();
|
||||||
|
HelperFunctions.setRenderingHints(g2d);
|
||||||
|
g2d.setPaint(Color.WHITE);
|
||||||
|
textLayout.draw(g2d, 13, 50);
|
||||||
|
g2d.dispose();
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
ImageIO.write(img2, "png", baos);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean checkAnswer(String secret, String answer) {
|
public Challenge returnChallenge() {
|
||||||
return answer.toLowerCase().equals(secret);
|
String secret = HelperFunctions.randomString(6);
|
||||||
}
|
return new Challenge(shadowText(secret), "image/png", secret.toLowerCase());
|
||||||
|
}
|
||||||
private byte[] shadowText(String text){
|
|
||||||
BufferedImage img = new BufferedImage(350, 100, BufferedImage.TYPE_INT_RGB);
|
|
||||||
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);
|
|
||||||
|
|
||||||
TextLayout textLayout = new TextLayout(text, font, graphics2D.getFontRenderContext());
|
|
||||||
HelperFunctions.setRenderingHints(graphics2D);
|
|
||||||
graphics2D.setPaint(Color.WHITE);
|
|
||||||
graphics2D.fillRect(0, 0, 350, 100);
|
|
||||||
graphics2D.setPaint(Color.BLACK);
|
|
||||||
textLayout.draw(graphics2D, 15, 50);
|
|
||||||
graphics2D.dispose();
|
|
||||||
float[] kernel = {
|
|
||||||
1f / 9f, 1f / 9f, 1f / 9f,
|
|
||||||
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);
|
|
||||||
BufferedImage img2 = op.filter(img, null);
|
|
||||||
Graphics2D g2d = img2.createGraphics();
|
|
||||||
HelperFunctions.setRenderingHints(g2d);
|
|
||||||
g2d.setPaint(Color.WHITE);
|
|
||||||
textLayout.draw(g2d, 13, 50);
|
|
||||||
g2d.dispose();
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
try {
|
|
||||||
ImageIO.write(img2,"png",baos);
|
|
||||||
}catch(Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return baos.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Challenge returnChallenge() {
|
|
||||||
String secret = HelperFunctions.randomString(6);
|
|
||||||
return new Challenge(shadowText(secret),"image/png",secret.toLowerCase());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,10 @@ package lc.captchas.interfaces;
|
||||||
|
|
||||||
public interface ChallengeProvider {
|
public interface ChallengeProvider {
|
||||||
public String getId();
|
public String getId();
|
||||||
|
|
||||||
public Challenge returnChallenge();
|
public Challenge returnChallenge();
|
||||||
|
|
||||||
public boolean checkAnswer(String secret, String answer);
|
public boolean checkAnswer(String secret, String answer);
|
||||||
|
|
||||||
//TODO: def configure(): Unit
|
// TODO: def configure(): Unit
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// It was available under CC By 3.0
|
// It was available under CC By 3.0
|
||||||
|
|
||||||
package lc.misc;
|
package lc.misc;
|
||||||
|
|
||||||
import javax.imageio.*;
|
import javax.imageio.*;
|
||||||
import javax.imageio.metadata.*;
|
import javax.imageio.metadata.*;
|
||||||
import javax.imageio.stream.*;
|
import javax.imageio.stream.*;
|
||||||
|
@ -10,136 +11,113 @@ import java.io.*;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
public class GifSequenceWriter {
|
public class GifSequenceWriter {
|
||||||
protected ImageWriter gifWriter;
|
protected ImageWriter gifWriter;
|
||||||
protected ImageWriteParam imageWriteParam;
|
protected ImageWriteParam imageWriteParam;
|
||||||
protected IIOMetadata imageMetaData;
|
protected IIOMetadata imageMetaData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new GifSequenceWriter
|
* Creates a new GifSequenceWriter
|
||||||
*
|
*
|
||||||
* @param outputStream the ImageOutputStream to be written to
|
* @param outputStream the ImageOutputStream to be written to
|
||||||
* @param imageType one of the imageTypes specified in BufferedImage
|
* @param imageType one of the imageTypes specified in BufferedImage
|
||||||
* @param timeBetweenFramesMS the time between frames in miliseconds
|
* @param timeBetweenFramesMS the time between frames in miliseconds
|
||||||
* @param loopContinuously wether the gif should loop repeatedly
|
* @param loopContinuously wether the gif should loop repeatedly
|
||||||
* @throws IIOException if no gif ImageWriters are found
|
* @throws IIOException if no gif ImageWriters are found
|
||||||
*
|
* @author Elliot Kroo (elliot[at]kroo[dot]net)
|
||||||
* @author Elliot Kroo (elliot[at]kroo[dot]net)
|
*/
|
||||||
*/
|
public GifSequenceWriter(
|
||||||
public GifSequenceWriter(
|
ImageOutputStream outputStream,
|
||||||
ImageOutputStream outputStream,
|
int imageType,
|
||||||
int imageType,
|
int timeBetweenFramesMS,
|
||||||
int timeBetweenFramesMS,
|
boolean loopContinuously)
|
||||||
boolean loopContinuously) throws IIOException, IOException {
|
throws IIOException, IOException {
|
||||||
// my method to create a writer
|
// my method to create a writer
|
||||||
gifWriter = getWriter();
|
gifWriter = getWriter();
|
||||||
imageWriteParam = gifWriter.getDefaultWriteParam();
|
imageWriteParam = gifWriter.getDefaultWriteParam();
|
||||||
ImageTypeSpecifier imageTypeSpecifier =
|
ImageTypeSpecifier imageTypeSpecifier =
|
||||||
ImageTypeSpecifier.createFromBufferedImageType(imageType);
|
ImageTypeSpecifier.createFromBufferedImageType(imageType);
|
||||||
|
|
||||||
imageMetaData =
|
imageMetaData = gifWriter.getDefaultImageMetadata(imageTypeSpecifier, imageWriteParam);
|
||||||
gifWriter.getDefaultImageMetadata(imageTypeSpecifier,
|
|
||||||
imageWriteParam);
|
|
||||||
|
|
||||||
String metaFormatName = imageMetaData.getNativeMetadataFormatName();
|
String metaFormatName = imageMetaData.getNativeMetadataFormatName();
|
||||||
|
|
||||||
IIOMetadataNode root = (IIOMetadataNode)
|
IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName);
|
||||||
imageMetaData.getAsTree(metaFormatName);
|
|
||||||
|
|
||||||
IIOMetadataNode graphicsControlExtensionNode = getNode(
|
IIOMetadataNode graphicsControlExtensionNode = getNode(root, "GraphicControlExtension");
|
||||||
root,
|
|
||||||
"GraphicControlExtension");
|
|
||||||
|
|
||||||
graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
|
graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
|
||||||
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
|
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
|
||||||
graphicsControlExtensionNode.setAttribute(
|
graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE");
|
||||||
"transparentColorFlag",
|
graphicsControlExtensionNode.setAttribute(
|
||||||
"FALSE");
|
"delayTime", Integer.toString(timeBetweenFramesMS / 10));
|
||||||
graphicsControlExtensionNode.setAttribute(
|
graphicsControlExtensionNode.setAttribute("transparentColorIndex", "0");
|
||||||
"delayTime",
|
|
||||||
Integer.toString(timeBetweenFramesMS / 10));
|
|
||||||
graphicsControlExtensionNode.setAttribute(
|
|
||||||
"transparentColorIndex",
|
|
||||||
"0");
|
|
||||||
|
|
||||||
IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
|
IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
|
||||||
commentsNode.setAttribute("CommentExtension", "Created by MAH");
|
commentsNode.setAttribute("CommentExtension", "Created by MAH");
|
||||||
|
|
||||||
IIOMetadataNode appEntensionsNode = getNode(
|
IIOMetadataNode appEntensionsNode = getNode(root, "ApplicationExtensions");
|
||||||
root,
|
|
||||||
"ApplicationExtensions");
|
|
||||||
|
|
||||||
IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
|
IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
|
||||||
|
|
||||||
child.setAttribute("applicationID", "NETSCAPE");
|
child.setAttribute("applicationID", "NETSCAPE");
|
||||||
child.setAttribute("authenticationCode", "2.0");
|
child.setAttribute("authenticationCode", "2.0");
|
||||||
|
|
||||||
int loop = loopContinuously ? 0 : 1;
|
int loop = loopContinuously ? 0 : 1;
|
||||||
|
|
||||||
child.setUserObject(new byte[]{ 0x1, (byte) (loop & 0xFF), (byte)
|
child.setUserObject(new byte[] {0x1, (byte) (loop & 0xFF), (byte) ((loop >> 8) & 0xFF)});
|
||||||
((loop >> 8) & 0xFF)});
|
appEntensionsNode.appendChild(child);
|
||||||
appEntensionsNode.appendChild(child);
|
|
||||||
|
|
||||||
imageMetaData.setFromTree(metaFormatName, root);
|
imageMetaData.setFromTree(metaFormatName, root);
|
||||||
|
|
||||||
gifWriter.setOutput(outputStream);
|
gifWriter.setOutput(outputStream);
|
||||||
|
|
||||||
gifWriter.prepareWriteSequence(null);
|
gifWriter.prepareWriteSequence(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeToSequence(RenderedImage img) throws IOException {
|
||||||
|
gifWriter.writeToSequence(new IIOImage(img, null, imageMetaData), imageWriteParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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").
|
||||||
|
*
|
||||||
|
* @return a GIF ImageWriter object
|
||||||
|
* @throws IIOException if no GIF image writers are returned
|
||||||
|
*/
|
||||||
|
private static ImageWriter getWriter() throws IIOException {
|
||||||
|
Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
throw new IIOException("No GIF Image Writers Exist");
|
||||||
|
} else {
|
||||||
|
return iter.next();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void writeToSequence(RenderedImage img) throws IOException {
|
/**
|
||||||
gifWriter.writeToSequence(
|
* Returns an existing child node, or creates and returns a new child node (if the requested node
|
||||||
new IIOImage(
|
* does not exist).
|
||||||
img,
|
*
|
||||||
null,
|
* @param rootNode the <tt>IIOMetadataNode</tt> to search for the child node.
|
||||||
imageMetaData),
|
* @param nodeName the name of the child node.
|
||||||
imageWriteParam);
|
* @return the child node, if found or a new node created with the given name.
|
||||||
}
|
*/
|
||||||
|
private static IIOMetadataNode getNode(IIOMetadataNode rootNode, String nodeName) {
|
||||||
/**
|
int nNodes = rootNode.getLength();
|
||||||
* Close this GifSequenceWriter object. This does not close the underlying
|
for (int i = 0; i < nNodes; i++) {
|
||||||
* stream, just finishes off the GIF.
|
if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName) == 0) {
|
||||||
*/
|
return ((IIOMetadataNode) rootNode.item(i));
|
||||||
public void close() throws IOException {
|
}
|
||||||
gifWriter.endWriteSequence();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the first available GIF ImageWriter using
|
|
||||||
* ImageIO.getImageWritersBySuffix("gif").
|
|
||||||
*
|
|
||||||
* @return a GIF ImageWriter object
|
|
||||||
* @throws IIOException if no GIF image writers are returned
|
|
||||||
*/
|
|
||||||
private static ImageWriter getWriter() throws IIOException {
|
|
||||||
Iterator<ImageWriter> iter = ImageIO.getImageWritersBySuffix("gif");
|
|
||||||
if(!iter.hasNext()) {
|
|
||||||
throw new IIOException("No GIF Image Writers Exist");
|
|
||||||
} else {
|
|
||||||
return iter.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
int nNodes = rootNode.getLength();
|
|
||||||
for (int i = 0; i < nNodes; i++) {
|
|
||||||
if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName)
|
|
||||||
== 0) {
|
|
||||||
return((IIOMetadataNode) rootNode.item(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
IIOMetadataNode node = new IIOMetadataNode(nodeName);
|
|
||||||
rootNode.appendChild(node);
|
|
||||||
return(node);
|
|
||||||
}
|
}
|
||||||
|
IIOMetadataNode node = new IIOMetadataNode(nodeName);
|
||||||
|
rootNode.appendChild(node);
|
||||||
|
return (node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,24 +4,24 @@ import java.awt.*;
|
||||||
|
|
||||||
public class HelperFunctions {
|
public class HelperFunctions {
|
||||||
|
|
||||||
public static void setRenderingHints(Graphics2D g2d){
|
public static void setRenderingHints(Graphics2D g2d) {
|
||||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
|
g2d.setRenderingHint(
|
||||||
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||||
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
|
g2d.setRenderingHint(
|
||||||
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
|
RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String randomString(int n){
|
public static String randomString(int n) {
|
||||||
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz23456789$#%@&?";
|
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz23456789$#%@&?";
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
for(int i=0; i<n; i++){
|
for (int i = 0; i < n; i++) {
|
||||||
int index = (int)(characters.length() * Math.random());
|
int index = (int) (characters.length() * Math.random());
|
||||||
stringBuilder.append(characters.charAt(index));
|
stringBuilder.append(characters.charAt(index));
|
||||||
}
|
|
||||||
return stringBuilder.toString();
|
|
||||||
}
|
}
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
public static int randomNumber(int min,int max){
|
public static int randomNumber(int min, int max) {
|
||||||
return (int)(Math.random() * ((max - min) +1)) + min;
|
return (int) (Math.random() * ((max - min) + 1)) + min;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,17 +1,12 @@
|
||||||
package lc
|
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.core.{Captcha, CaptchaProviders}
|
||||||
import lc.server.Server
|
import lc.server.Server
|
||||||
import lc.background.BackgroundTask
|
import lc.background.BackgroundTask
|
||||||
|
|
||||||
|
object LCFramework {
|
||||||
object LCFramework{
|
def main(args: scala.Array[String]): Unit = {
|
||||||
def main(args: scala.Array[String]) {
|
val captcha = new Captcha()
|
||||||
val captcha = new Captcha()
|
|
||||||
val server = new Server(8888, captcha)
|
val server = new Server(8888, captcha)
|
||||||
val backgroudTask = new BackgroundTask(captcha, 10)
|
val backgroudTask = new BackgroundTask(captcha, 10)
|
||||||
backgroudTask.beginThread(2)
|
backgroudTask.beginThread(2)
|
||||||
|
@ -20,15 +15,16 @@ object LCFramework{
|
||||||
}
|
}
|
||||||
|
|
||||||
object MakeSamples {
|
object MakeSamples {
|
||||||
def main(args: scala.Array[String]) {
|
def main(args: scala.Array[String]): Unit = {
|
||||||
val samples = CaptchaProviders.generateChallengeSamples()
|
val samples = CaptchaProviders.generateChallengeSamples()
|
||||||
samples.foreach {case (key, sample) =>
|
samples.foreach {
|
||||||
val extensionMap = Map("image/png" -> "png", "image/gif" -> "gif")
|
case (key, sample) =>
|
||||||
println(key + ": " + sample)
|
val extensionMap = Map("image/png" -> "png", "image/gif" -> "gif")
|
||||||
|
println(key + ": " + sample)
|
||||||
|
|
||||||
val outStream = new java.io.FileOutputStream("samples/"+key+"."+extensionMap(sample.contentType))
|
val outStream = new java.io.FileOutputStream("samples/" + key + "." + extensionMap(sample.contentType))
|
||||||
outStream.write(sample.content)
|
outStream.write(sample.content)
|
||||||
outStream.close
|
outStream.close
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,34 +5,33 @@ import java.util.concurrent.{ScheduledThreadPoolExecutor, TimeUnit}
|
||||||
import lc.core.Captcha
|
import lc.core.Captcha
|
||||||
import lc.core.{Parameters, Size}
|
import lc.core.{Parameters, Size}
|
||||||
|
|
||||||
|
|
||||||
class BackgroundTask(captcha: Captcha, throttle: Int) {
|
class BackgroundTask(captcha: Captcha, throttle: Int) {
|
||||||
|
|
||||||
private val task = new Runnable {
|
private val task = new Runnable {
|
||||||
def run(): Unit = {
|
def run(): Unit = {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
val mapIdGCPstmt = Statements.tlStmts.get.mapIdGCPstmt
|
val mapIdGCPstmt = Statements.tlStmts.get.mapIdGCPstmt
|
||||||
mapIdGCPstmt.executeUpdate()
|
mapIdGCPstmt.executeUpdate()
|
||||||
|
|
||||||
val challengeGCPstmt = Statements.tlStmts.get.challengeGCPstmt
|
val challengeGCPstmt = Statements.tlStmts.get.challengeGCPstmt
|
||||||
challengeGCPstmt.executeUpdate()
|
challengeGCPstmt.executeUpdate()
|
||||||
|
|
||||||
val imageNum = Statements.tlStmts.get.getCountChallengeTable.executeQuery()
|
val imageNum = Statements.tlStmts.get.getCountChallengeTable.executeQuery()
|
||||||
var throttleIn = (throttle*1.1).toInt
|
var throttleIn = (throttle * 1.1).toInt
|
||||||
if(imageNum.next())
|
if (imageNum.next())
|
||||||
throttleIn = (throttleIn-imageNum.getInt("total"))
|
throttleIn = (throttleIn - imageNum.getInt("total"))
|
||||||
while(0 < throttleIn){
|
while (0 < throttleIn) {
|
||||||
captcha.generateChallenge(Parameters("","","",Option(Size(0,0))))
|
captcha.generateChallenge(Parameters("", "", "", Option(Size(0, 0))))
|
||||||
throttleIn -= 1
|
throttleIn -= 1
|
||||||
}
|
|
||||||
} catch { case e: Exception => println(e) }
|
|
||||||
}
|
}
|
||||||
|
} catch { case e: Exception => println(e) }
|
||||||
}
|
}
|
||||||
|
|
||||||
def beginThread(delay: Int) : Unit = {
|
|
||||||
val ex = new ScheduledThreadPoolExecutor(1)
|
|
||||||
val thread = ex.scheduleWithFixedDelay(task, 1, delay, TimeUnit.SECONDS)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
def beginThread(delay: Int): Unit = {
|
||||||
|
val ex = new ScheduledThreadPoolExecutor(1)
|
||||||
|
ex.scheduleWithFixedDelay(task, 1, delay, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import java.awt.Color
|
||||||
import lc.captchas.interfaces.ChallengeProvider
|
import lc.captchas.interfaces.ChallengeProvider
|
||||||
import lc.captchas.interfaces.Challenge
|
import lc.captchas.interfaces.Challenge
|
||||||
|
|
||||||
|
|
||||||
class FilterChallenge extends ChallengeProvider {
|
class FilterChallenge extends ChallengeProvider {
|
||||||
def getId = "FilterChallenge"
|
def getId = "FilterChallenge"
|
||||||
def returnChallenge(): Challenge = {
|
def returnChallenge(): Challenge = {
|
||||||
|
@ -62,4 +61,3 @@ class FilterType2 extends FilterType {
|
||||||
image
|
image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,45 +4,45 @@ import java.io.File
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import javax.imageio.ImageIO
|
import javax.imageio.ImageIO
|
||||||
import scala.collection.mutable.Map
|
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.image.BufferedImage
|
||||||
import java.awt.{Graphics2D,Color}
|
import java.awt.Color
|
||||||
import lc.captchas.interfaces.ChallengeProvider
|
import lc.captchas.interfaces.ChallengeProvider
|
||||||
import lc.captchas.interfaces.Challenge
|
import lc.captchas.interfaces.Challenge
|
||||||
|
|
||||||
class LabelCaptcha extends ChallengeProvider {
|
class LabelCaptcha extends ChallengeProvider {
|
||||||
private var knownFiles = new File("known").list.toList
|
private var knownFiles = new File("known").list.toList
|
||||||
private var unknownFiles = new File("unknown").list.toList
|
private var unknownFiles = new File("unknown").list.toList
|
||||||
private var unknownAnswers = Map[String, Map[String, Int]]()
|
private val unknownAnswers = Map[String, Map[String, Int]]()
|
||||||
private var total = Map[String, Int]()
|
private val total = Map[String, Int]()
|
||||||
|
|
||||||
for(file <- unknownFiles) {
|
for (file <- unknownFiles) {
|
||||||
unknownAnswers += file -> Map[String, Int]()
|
unknownAnswers += file -> Map[String, Int]()
|
||||||
total += file -> 0
|
total += file -> 0
|
||||||
}
|
}
|
||||||
|
|
||||||
def getId = "LabelCaptcha"
|
def getId = "LabelCaptcha"
|
||||||
|
|
||||||
def returnChallenge(): Challenge = synchronized {
|
def returnChallenge(): Challenge =
|
||||||
val r = scala.util.Random.nextInt(knownFiles.length)
|
synchronized {
|
||||||
val s = scala.util.Random.nextInt(unknownFiles.length)
|
val r = scala.util.Random.nextInt(knownFiles.length)
|
||||||
val knownImageFile = knownFiles(r)
|
val s = scala.util.Random.nextInt(unknownFiles.length)
|
||||||
val unknownImageFile = unknownFiles(s)
|
val knownImageFile = knownFiles(r)
|
||||||
val ip = new ImagePair(knownImageFile, unknownImageFile)
|
val unknownImageFile = unknownFiles(s)
|
||||||
|
|
||||||
var knownImage = ImageIO.read(new File("known/"+knownImageFile))
|
val knownImage = ImageIO.read(new File("known/" + knownImageFile))
|
||||||
var unknownImage = ImageIO.read(new File("unknown/"+unknownImageFile))
|
val unknownImage = ImageIO.read(new File("unknown/" + unknownImageFile))
|
||||||
val mergedImage = merge(knownImage, unknownImage)
|
val mergedImage = merge(knownImage, unknownImage)
|
||||||
|
|
||||||
val token = encrypt(knownImageFile + "," + unknownImageFile)
|
val token = encrypt(knownImageFile + "," + unknownImageFile)
|
||||||
val baos = new ByteArrayOutputStream()
|
val baos = new ByteArrayOutputStream()
|
||||||
ImageIO.write(mergedImage,"png",baos)
|
ImageIO.write(mergedImage, "png", baos)
|
||||||
|
|
||||||
new Challenge(baos.toByteArray(), "image/png", token)
|
new Challenge(baos.toByteArray(), "image/png", token)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def merge(knownImage: BufferedImage, unknownImage: BufferedImage) = {
|
private def merge(knownImage: BufferedImage, unknownImage: BufferedImage) = {
|
||||||
val width = knownImage.getWidth()+unknownImage.getWidth()
|
val width = knownImage.getWidth() + unknownImage.getWidth()
|
||||||
val height = List(knownImage.getHeight(), unknownImage.getHeight()).max
|
val height = List(knownImage.getHeight(), unknownImage.getHeight()).max
|
||||||
val imageType = knownImage.getType()
|
val imageType = knownImage.getType()
|
||||||
val finalImage = new BufferedImage(width, height, imageType)
|
val finalImage = new BufferedImage(width, height, imageType)
|
||||||
|
@ -55,34 +55,39 @@ class LabelCaptcha extends ChallengeProvider {
|
||||||
finalImage
|
finalImage
|
||||||
}
|
}
|
||||||
|
|
||||||
def checkAnswer(token: String, input: String): Boolean = synchronized {
|
def checkAnswer(token: String, input: String): Boolean =
|
||||||
val parts = decrypt(token).split(",")
|
synchronized {
|
||||||
val knownImage = parts(0)
|
val parts = decrypt(token).split(",")
|
||||||
val unknownImage = parts(1)
|
val knownImage = parts(0)
|
||||||
val expectedAnswer = knownImage.split('.')(0)
|
val unknownImage = parts(1)
|
||||||
val userAnswer = input.split(' ')
|
val expectedAnswer = knownImage.split('.')(0)
|
||||||
if(userAnswer(0)==expectedAnswer) {
|
val userAnswer = input.split(' ')
|
||||||
val unknownFile = unknownImage
|
if (userAnswer(0) == expectedAnswer) {
|
||||||
if((unknownAnswers(unknownFile)).contains(userAnswer(1))) {
|
val unknownFile = unknownImage
|
||||||
unknownAnswers(unknownFile)(userAnswer(1)) += 1
|
if ((unknownAnswers(unknownFile)).contains(userAnswer(1))) {
|
||||||
total(unknownFile) += 1
|
unknownAnswers(unknownFile)(userAnswer(1)) += 1
|
||||||
} else {
|
total(unknownFile) += 1
|
||||||
unknownAnswers(unknownFile)+=(userAnswer(1)) -> 1
|
} else {
|
||||||
total(unknownFile) += 1
|
unknownAnswers(unknownFile) += (userAnswer(1)) -> 1
|
||||||
}
|
total(unknownFile) += 1
|
||||||
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)
|
|
||||||
knownFiles = new File("known").list.toList
|
|
||||||
unknownFiles = new File("unknown").list.toList
|
|
||||||
}
|
}
|
||||||
|
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
|
||||||
|
)
|
||||||
|
knownFiles = new File("known").list.toList
|
||||||
|
unknownFiles = new File("unknown").list.toList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Encryption is not implemented for the POC, since the API re-maps the tokens anyway.
|
// TODO: Encryption is not implemented for the POC, since the API re-maps the tokens anyway.
|
||||||
// But we need to encrypt after POC, to avoid leaking file-names.
|
// But we need to encrypt after POC, to avoid leaking file-names.
|
||||||
|
|
|
@ -5,9 +5,7 @@ import java.awt.RenderingHints
|
||||||
import java.awt.Font
|
import java.awt.Font
|
||||||
import java.awt.font.TextAttribute
|
import java.awt.font.TextAttribute
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream;
|
||||||
import javax.imageio.ImageIO
|
|
||||||
import javax.imageio.stream.ImageOutputStream;
|
|
||||||
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
||||||
import lc.captchas.interfaces.ChallengeProvider
|
import lc.captchas.interfaces.ChallengeProvider
|
||||||
import lc.captchas.interfaces.Challenge
|
import lc.captchas.interfaces.Challenge
|
||||||
|
@ -19,8 +17,8 @@ class Drop {
|
||||||
var yOffset = 0
|
var yOffset = 0
|
||||||
var color = 0
|
var color = 0
|
||||||
var colorChange = 10
|
var colorChange = 10
|
||||||
def mkColor = {
|
def mkColor: Color = {
|
||||||
new Color(color, color, math.min(200, color+100))
|
new Color(color, color, math.min(200, color + 100))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,8 +34,8 @@ class RainDropsCP extends ChallengeProvider {
|
||||||
private def extendDrops(drops: Array[Drop], steps: Int, xOffset: Int) = {
|
private def extendDrops(drops: Array[Drop], steps: Int, xOffset: Int) = {
|
||||||
drops.map(d => {
|
drops.map(d => {
|
||||||
val nd = new Drop()
|
val nd = new Drop()
|
||||||
nd.x + xOffset*steps
|
nd.x + xOffset * steps
|
||||||
nd.y + d.yOffset*steps
|
nd.y + d.yOffset * steps
|
||||||
nd
|
nd
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -48,20 +46,23 @@ class RainDropsCP extends ChallengeProvider {
|
||||||
val width = 450
|
val width = 450
|
||||||
val height = 100
|
val height = 100
|
||||||
val imgType = BufferedImage.TYPE_INT_RGB
|
val imgType = BufferedImage.TYPE_INT_RGB
|
||||||
val xOffset = 2+r.nextInt(3)
|
val xOffset = 2 + r.nextInt(3)
|
||||||
val xBias = (height / 10) - 2
|
val xBias = (height / 10) - 2
|
||||||
val dropsOrig = Array.fill[Drop](2000)( new Drop())
|
val dropsOrig = Array.fill[Drop](2000)(new Drop())
|
||||||
for (d <- dropsOrig) {
|
for (d <- dropsOrig) {
|
||||||
d.x = r.nextInt(width) - (xBias/2)*xOffset
|
d.x = r.nextInt(width) - (xBias / 2) * xOffset
|
||||||
d.yOffset = 6+r.nextInt(6)
|
d.yOffset = 6 + r.nextInt(6)
|
||||||
d.y = r.nextInt(height)
|
d.y = r.nextInt(height)
|
||||||
d.color = r.nextInt(240)
|
d.color = r.nextInt(240)
|
||||||
if (d.color > 128) {
|
if (d.color > 128) {
|
||||||
d.colorChange *= -1
|
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 baseFont = new Font(Font.MONOSPACED, Font.BOLD, 80)
|
||||||
val attributes = new java.util.HashMap[TextAttribute, Object]()
|
val attributes = new java.util.HashMap[TextAttribute, Object]()
|
||||||
|
@ -72,7 +73,7 @@ class RainDropsCP extends ChallengeProvider {
|
||||||
val baos = new ByteArrayOutputStream();
|
val baos = new ByteArrayOutputStream();
|
||||||
val ios = new MemoryCacheImageOutputStream(baos);
|
val ios = new MemoryCacheImageOutputStream(baos);
|
||||||
val writer = new GifSequenceWriter(ios, imgType, 60, true);
|
val writer = new GifSequenceWriter(ios, imgType, 60, true);
|
||||||
for(i <- 0 until 60){
|
for (_ <- 0 until 60) {
|
||||||
// val yOffset = 5+r.nextInt(5)
|
// val yOffset = 5+r.nextInt(5)
|
||||||
val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
|
val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
|
||||||
val g = canvas.createGraphics()
|
val g = canvas.createGraphics()
|
||||||
|
@ -85,14 +86,14 @@ class RainDropsCP extends ChallengeProvider {
|
||||||
// paint the rain
|
// paint the rain
|
||||||
for (d <- drops) {
|
for (d <- drops) {
|
||||||
g.setColor(d.mkColor)
|
g.setColor(d.mkColor)
|
||||||
g.drawLine(d.x, d.y, d.x+xOffset, d.y+d.yOffset)
|
g.drawLine(d.x, d.y, d.x + xOffset, d.y + d.yOffset)
|
||||||
d.x += xOffset/2
|
d.x += xOffset / 2
|
||||||
d.y += d.yOffset/2
|
d.y += d.yOffset / 2
|
||||||
d.color += d.colorChange
|
d.color += d.colorChange
|
||||||
if (d.x > width || d.y > height) {
|
if (d.x > width || d.y > height) {
|
||||||
val ySteps = (height / d.yOffset) + 1
|
val ySteps = (height / d.yOffset) + 1
|
||||||
d.x -= xOffset*ySteps
|
d.x -= xOffset * ySteps
|
||||||
d.y -= d.yOffset*ySteps
|
d.y -= d.yOffset * ySteps
|
||||||
|
|
||||||
}
|
}
|
||||||
if (d.color > 200 || d.color < 21) {
|
if (d.color > 200 || d.color < 21) {
|
||||||
|
@ -103,7 +104,7 @@ class RainDropsCP extends ChallengeProvider {
|
||||||
// center the text
|
// center the text
|
||||||
g.setFont(spacedFont)
|
g.setFont(spacedFont)
|
||||||
val textWidth = g.getFontMetrics().charsWidth(secret.toCharArray, 0, secret.toCharArray.length)
|
val textWidth = g.getFontMetrics().charsWidth(secret.toCharArray, 0, secret.toCharArray.length)
|
||||||
val textX = (width - textWidth)/2
|
val textX = (width - textWidth) / 2
|
||||||
|
|
||||||
// paint the top outline
|
// paint the top outline
|
||||||
g.setColor(textHighlightColor)
|
g.setColor(textHighlightColor)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package lc.core
|
package lc.core
|
||||||
|
|
||||||
import org.json4s.JsonAST.JValue
|
|
||||||
import java.sql.{Blob, ResultSet}
|
import java.sql.{Blob, ResultSet}
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
|
@ -10,29 +9,30 @@ import lc.core.CaptchaProviders
|
||||||
class Captcha {
|
class Captcha {
|
||||||
|
|
||||||
def getCaptcha(id: Id): Array[Byte] = {
|
def getCaptcha(id: Id): Array[Byte] = {
|
||||||
var image :Array[Byte] = null
|
var image: Array[Byte] = null
|
||||||
var blob: Blob = null
|
var blob: Blob = null
|
||||||
try {
|
try {
|
||||||
val imagePstmt = Statements.tlStmts.get.imagePstmt
|
val imagePstmt = Statements.tlStmts.get.imagePstmt
|
||||||
imagePstmt.setString(1, id.id)
|
imagePstmt.setString(1, id.id)
|
||||||
val rs: ResultSet = imagePstmt.executeQuery()
|
val rs: ResultSet = imagePstmt.executeQuery()
|
||||||
if(rs.next()){
|
if (rs.next()) {
|
||||||
blob = rs.getBlob("image")
|
blob = rs.getBlob("image")
|
||||||
if(blob != null){
|
if (blob != null) {
|
||||||
image = blob.getBytes(1, blob.length().toInt)
|
image = blob.getBytes(1, blob.length().toInt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
image
|
|
||||||
} catch { case e: Exception =>
|
|
||||||
println(e)
|
|
||||||
image
|
image
|
||||||
|
} catch {
|
||||||
|
case e: Exception =>
|
||||||
|
println(e)
|
||||||
|
image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def generateChallenge(param: Parameters): Int = {
|
def generateChallenge(param: Parameters): Int = {
|
||||||
//TODO: eval params to choose a provider
|
//TODO: eval params to choose a provider
|
||||||
val provider = CaptchaProviders.getProvider()
|
val provider = CaptchaProviders.getProvider()
|
||||||
val providerId = provider.getId()
|
val providerId = provider.getId()
|
||||||
val challenge = provider.returnChallenge()
|
val challenge = provider.returnChallenge()
|
||||||
val blob = new ByteArrayInputStream(challenge.content)
|
val blob = new ByteArrayInputStream(challenge.content)
|
||||||
val insertPstmt = Statements.tlStmts.get.insertPstmt
|
val insertPstmt = Statements.tlStmts.get.insertPstmt
|
||||||
|
@ -43,10 +43,10 @@ class Captcha {
|
||||||
insertPstmt.setBlob(5, blob)
|
insertPstmt.setBlob(5, blob)
|
||||||
insertPstmt.executeUpdate()
|
insertPstmt.executeUpdate()
|
||||||
val rs: ResultSet = insertPstmt.getGeneratedKeys()
|
val rs: ResultSet = insertPstmt.getGeneratedKeys()
|
||||||
val token = if(rs.next()){
|
val token = if (rs.next()) {
|
||||||
rs.getInt("token")
|
rs.getInt("token")
|
||||||
}
|
}
|
||||||
println("Added new challenge: "+ token.toString)
|
println("Added new challenge: " + token.toString)
|
||||||
token.asInstanceOf[Int]
|
token.asInstanceOf[Int]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ class Captcha {
|
||||||
try {
|
try {
|
||||||
val tokenPstmt = Statements.tlStmts.get.tokenPstmt
|
val tokenPstmt = Statements.tlStmts.get.tokenPstmt
|
||||||
val rs = tokenPstmt.executeQuery()
|
val rs = tokenPstmt.executeQuery()
|
||||||
val tokenOpt = if(rs.next()) {
|
val tokenOpt = if (rs.next()) {
|
||||||
Some(rs.getInt("token"))
|
Some(rs.getInt("token"))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -64,44 +64,45 @@ class Captcha {
|
||||||
updateAttemptedPstmt.setString(1, uuid)
|
updateAttemptedPstmt.setString(1, uuid)
|
||||||
updateAttemptedPstmt.executeUpdate()
|
updateAttemptedPstmt.executeUpdate()
|
||||||
Id(uuid)
|
Id(uuid)
|
||||||
} catch {case e: Exception =>
|
} catch {
|
||||||
println(e)
|
case e: Exception =>
|
||||||
Id(getUUID(-1))
|
println(e)
|
||||||
|
Id(getUUID(-1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def getUUID(id: Int): String = {
|
private def getUUID(id: Int): String = {
|
||||||
val uuid = UUID.randomUUID().toString
|
val uuid = UUID.randomUUID().toString
|
||||||
val mapPstmt = Statements.tlStmts.get.mapPstmt
|
val mapPstmt = Statements.tlStmts.get.mapPstmt
|
||||||
mapPstmt.setString(1,uuid)
|
mapPstmt.setString(1, uuid)
|
||||||
mapPstmt.setInt(2,id)
|
mapPstmt.setInt(2, id)
|
||||||
mapPstmt.executeUpdate()
|
mapPstmt.executeUpdate()
|
||||||
uuid
|
uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
def checkAnswer(answer: Answer): Result = {
|
def checkAnswer(answer: Answer): Result = {
|
||||||
val selectPstmt = Statements.tlStmts.get.selectPstmt
|
val selectPstmt = Statements.tlStmts.get.selectPstmt
|
||||||
selectPstmt.setString(1, answer.id)
|
selectPstmt.setString(1, answer.id)
|
||||||
val rs: ResultSet = selectPstmt.executeQuery()
|
val rs: ResultSet = selectPstmt.executeQuery()
|
||||||
val psOpt = if (rs.first()) {
|
val psOpt = if (rs.first()) {
|
||||||
val secret = rs.getString("secret")
|
val secret = rs.getString("secret")
|
||||||
val provider = rs.getString("provider")
|
val provider = rs.getString("provider")
|
||||||
val check = CaptchaProviders.getProviderById(provider).checkAnswer(secret, answer.answer)
|
val check = CaptchaProviders.getProviderById(provider).checkAnswer(secret, answer.answer)
|
||||||
val result = if(check) "TRUE" else "FALSE"
|
val result = if (check) "TRUE" else "FALSE"
|
||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
"EXPIRED"
|
"EXPIRED"
|
||||||
}
|
}
|
||||||
val deleteAnswerPstmt = Statements.tlStmts.get.deleteAnswerPstmt
|
val deleteAnswerPstmt = Statements.tlStmts.get.deleteAnswerPstmt
|
||||||
deleteAnswerPstmt.setString(1, answer.id)
|
deleteAnswerPstmt.setString(1, answer.id)
|
||||||
deleteAnswerPstmt.executeUpdate()
|
deleteAnswerPstmt.executeUpdate()
|
||||||
Result(psOpt)
|
Result(psOpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
def display(): Unit = {
|
def display(): Unit = {
|
||||||
val rs: ResultSet = Statements.tlStmts.get.getChallengeTable.executeQuery()
|
val rs: ResultSet = Statements.tlStmts.get.getChallengeTable.executeQuery()
|
||||||
println("token\t\tid\t\tsecret\t\tattempted")
|
println("token\t\tid\t\tsecret\t\tattempted")
|
||||||
while(rs.next()) {
|
while (rs.next()) {
|
||||||
val token = rs.getInt("token")
|
val token = rs.getInt("token")
|
||||||
val id = rs.getString("id")
|
val id = rs.getString("id")
|
||||||
val secret = rs.getString("secret")
|
val secret = rs.getString("secret")
|
||||||
|
@ -111,11 +112,11 @@ class Captcha {
|
||||||
|
|
||||||
val rss: ResultSet = Statements.tlStmts.get.getMapIdTable.executeQuery()
|
val rss: ResultSet = Statements.tlStmts.get.getMapIdTable.executeQuery()
|
||||||
println("uuid\t\ttoken\t\tlastServed")
|
println("uuid\t\ttoken\t\tlastServed")
|
||||||
while(rss.next()){
|
while (rss.next()) {
|
||||||
val uuid = rss.getString("uuid")
|
val uuid = rss.getString("uuid")
|
||||||
val token = rss.getInt("token")
|
val token = rss.getInt("token")
|
||||||
val lastServed = rss.getTimestamp("lastServed")
|
val lastServed = rss.getTimestamp("lastServed")
|
||||||
println(s"${uuid}\t\t${token}\t\t${lastServed}\n\n")
|
println(s"${uuid}\t\t${token}\t\t${lastServed}\n\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package lc.core
|
||||||
|
|
||||||
import lc.captchas._
|
import lc.captchas._
|
||||||
import lc.captchas.interfaces.ChallengeProvider
|
import lc.captchas.interfaces.ChallengeProvider
|
||||||
|
import lc.captchas.interfaces.Challenge
|
||||||
|
|
||||||
object CaptchaProviders {
|
object CaptchaProviders {
|
||||||
private val providers = Map(
|
private val providers = Map(
|
||||||
|
@ -9,30 +10,32 @@ object CaptchaProviders {
|
||||||
//"FontFunCaptcha" -> new FontFunCaptcha,
|
//"FontFunCaptcha" -> new FontFunCaptcha,
|
||||||
"GifCaptcha" -> new GifCaptcha,
|
"GifCaptcha" -> new GifCaptcha,
|
||||||
"ShadowTextCaptcha" -> new ShadowTextCaptcha,
|
"ShadowTextCaptcha" -> new ShadowTextCaptcha,
|
||||||
"RainDropsCaptcha" -> new RainDropsCP,
|
"RainDropsCaptcha" -> new RainDropsCP
|
||||||
//"LabelCaptcha" -> new LabelCaptcha
|
//"LabelCaptcha" -> new LabelCaptcha
|
||||||
)
|
)
|
||||||
|
|
||||||
def generateChallengeSamples() = {
|
def generateChallengeSamples(): Map[String, Challenge] = {
|
||||||
providers.map {case (key, provider) =>
|
providers.map {
|
||||||
(key, provider.returnChallenge())
|
case (key, provider) =>
|
||||||
|
(key, provider.returnChallenge())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val seed = System.currentTimeMillis.toString.substring(2,6).toInt
|
private val seed = System.currentTimeMillis.toString.substring(2, 6).toInt
|
||||||
private val random = new scala.util.Random(seed)
|
private val random = new scala.util.Random(seed)
|
||||||
|
|
||||||
private def getNextRandomInt(max: Int) = random.synchronized {
|
private def getNextRandomInt(max: Int) =
|
||||||
random.nextInt(max)
|
random.synchronized {
|
||||||
}
|
random.nextInt(max)
|
||||||
|
}
|
||||||
|
|
||||||
def getProviderById(id: String): ChallengeProvider = {
|
def getProviderById(id: String): ChallengeProvider = {
|
||||||
return providers(id)
|
return providers(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
def getProvider(): ChallengeProvider = {
|
def getProvider(): ChallengeProvider = {
|
||||||
val keys = providers.keys
|
val keys = providers.keys
|
||||||
val providerIndex = keys.toVector(getNextRandomInt(keys.size))
|
val providerIndex = keys.toVector(getNextRandomInt(keys.size))
|
||||||
providers(providerIndex)
|
providers(providerIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,4 +4,4 @@ case class Size(height: Int, width: Int)
|
||||||
case class Parameters(level: String, media: String, input_type: String, size: Option[Size])
|
case class Parameters(level: String, media: String, input_type: String, size: Option[Size])
|
||||||
case class Id(id: String)
|
case class Id(id: String)
|
||||||
case class Answer(answer: String, id: String)
|
case class Answer(answer: String, id: String)
|
||||||
case class Result(result: String)
|
case class Result(result: String)
|
||||||
|
|
|
@ -2,7 +2,7 @@ package lc.database
|
||||||
|
|
||||||
import java.sql._
|
import java.sql._
|
||||||
|
|
||||||
class DBConn(){
|
class DBConn() {
|
||||||
val con: Connection = DriverManager.getConnection("jdbc:h2:./data/H2/captcha", "sa", "")
|
val con: Connection = DriverManager.getConnection("jdbc:h2:./data/H2/captcha", "sa", "")
|
||||||
|
|
||||||
def getStatement(): Statement = {
|
def getStatement(): Statement = {
|
||||||
|
@ -10,6 +10,6 @@ class DBConn(){
|
||||||
}
|
}
|
||||||
|
|
||||||
def closeConnection(): Unit = {
|
def closeConnection(): Unit = {
|
||||||
con.close()
|
con.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,30 +2,108 @@ package lc.database
|
||||||
|
|
||||||
import lc.database.DBConn
|
import lc.database.DBConn
|
||||||
import java.sql.Statement
|
import java.sql.Statement
|
||||||
|
import java.sql.PreparedStatement
|
||||||
|
|
||||||
class Statements(dbConn: DBConn) {
|
class Statements(dbConn: DBConn) {
|
||||||
|
|
||||||
private val stmt = dbConn.getStatement()
|
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(
|
||||||
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)")
|
"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 insertPstmt: PreparedStatement = dbConn.con.prepareStatement(
|
||||||
val mapPstmt = dbConn.con.prepareStatement("INSERT INTO mapId(uuid, token, lastServed) VALUES (?, ?, CURRENT_TIMESTAMP)")
|
"INSERT INTO " +
|
||||||
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 = ?")
|
"challenge(id, secret, provider, contentType, image) " +
|
||||||
val imagePstmt = dbConn.con.prepareStatement("SELECT image FROM challenge c, mapId m WHERE c.token=m.token AND m.uuid = ?")
|
"VALUES (?, ?, ?, ?, ?)",
|
||||||
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 = ?)")
|
Statement.RETURN_GENERATED_KEYS
|
||||||
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 mapPstmt: PreparedStatement =
|
||||||
val mapIdGCPstmt = dbConn.con.prepareStatement("DELETE FROM mapId WHERE DATEDIFF(MINUTE, CURRENT_TIMESTAMP, DATEADD(MINUTE, 1, lastServed)) < 0")
|
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 {
|
object Statements {
|
||||||
private val dbConn: DBConn = new DBConn()
|
private val dbConn: DBConn = new DBConn()
|
||||||
val tlStmts = ThreadLocal.withInitial(() => new Statements(dbConn))
|
val tlStmts: ThreadLocal[Statements] = ThreadLocal.withInitial(() => new Statements(dbConn))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,46 +7,56 @@ import lc.core.Captcha
|
||||||
import lc.core.{Parameters, Id, Answer}
|
import lc.core.{Parameters, Id, Answer}
|
||||||
import lc.server.HTTPServer
|
import lc.server.HTTPServer
|
||||||
|
|
||||||
|
class Server(port: Int, captcha: Captcha) {
|
||||||
|
val server = new HTTPServer(port)
|
||||||
|
val host: HTTPServer.VirtualHost = server.getVirtualHost(null)
|
||||||
|
|
||||||
class Server(port: Int, captcha: Captcha){
|
implicit val formats: DefaultFormats.type = DefaultFormats
|
||||||
val server = new HTTPServer(port)
|
|
||||||
val host = server.getVirtualHost(null)
|
|
||||||
|
|
||||||
implicit val formats = DefaultFormats
|
host.addContext(
|
||||||
|
"/v1/captcha",
|
||||||
|
(req, resp) => {
|
||||||
|
val body = req.getJson()
|
||||||
|
val json = parse(body)
|
||||||
|
val param = json.extract[Parameters]
|
||||||
|
val id = captcha.getChallenge(param)
|
||||||
|
resp.getHeaders().add("Content-Type", "application/json")
|
||||||
|
resp.send(200, write(id))
|
||||||
|
0
|
||||||
|
},
|
||||||
|
"POST"
|
||||||
|
)
|
||||||
|
|
||||||
host.addContext("/v1/captcha",(req, resp) => {
|
host.addContext(
|
||||||
val body = req.getJson()
|
"/v1/media",
|
||||||
val json = parse(body)
|
(req, resp) => {
|
||||||
val param = json.extract[Parameters]
|
val params = req.getParams()
|
||||||
val id = captcha.getChallenge(param)
|
val id = Id(params.get("id"))
|
||||||
resp.getHeaders().add("Content-Type","application/json")
|
val image = captcha.getCaptcha(id)
|
||||||
resp.send(200, write(id))
|
resp.getHeaders().add("Content-Type", "image/png")
|
||||||
0
|
resp.send(200, image)
|
||||||
},"POST")
|
0
|
||||||
|
},
|
||||||
|
"GET"
|
||||||
|
)
|
||||||
|
|
||||||
host.addContext("/v1/media",(req, resp) => {
|
host.addContext(
|
||||||
val params = req.getParams()
|
"/v1/answer",
|
||||||
val id = Id(params.get("id"))
|
(req, resp) => {
|
||||||
val image = captcha.getCaptcha(id)
|
val body = req.getJson()
|
||||||
resp.getHeaders().add("Content-Type","image/png")
|
val json = parse(body)
|
||||||
resp.send(200, image)
|
val answer = json.extract[Answer]
|
||||||
0
|
val result = captcha.checkAnswer(answer)
|
||||||
},"GET")
|
resp.getHeaders().add("Content-Type", "application/json")
|
||||||
|
resp.send(200, write(result))
|
||||||
|
0
|
||||||
|
},
|
||||||
|
"POST"
|
||||||
|
)
|
||||||
|
|
||||||
host.addContext("/v1/answer",(req, resp) => {
|
def start(): Unit = {
|
||||||
val body = req.getJson()
|
println("Starting server on port:" + port)
|
||||||
val json = parse(body)
|
server.start()
|
||||||
val answer = json.extract[Answer]
|
}
|
||||||
val result = captcha.checkAnswer(answer)
|
|
||||||
resp.getHeaders().add("Content-Type","application/json")
|
|
||||||
resp.send(200, write(result))
|
|
||||||
0
|
|
||||||
},"POST")
|
|
||||||
|
|
||||||
|
|
||||||
def start(): Unit = {
|
|
||||||
println("Starting server on port:" + port)
|
|
||||||
server.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue