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
|
||||
- name: Run tests
|
||||
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
|
||||
script:
|
||||
- sbt ++$TRAVIS_SCALA_VERSION compile
|
||||
- sbt "scalafixAll --check"
|
||||
|
|
32
build.sbt
32
build.sbt
|
@ -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")),
|
||||
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"
|
||||
|
||||
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"}
|
||||
unmanagedResourceDirectories in Compile += { baseDirectory.value / "lib" }
|
||||
scalacOptions ++= List(
|
||||
"-Yrangepos",
|
||||
"-Ywarn-unused"
|
||||
)
|
||||
javacOptions += "-g:none"
|
||||
scalafmtOnCompile := true
|
||||
compileOrder := CompileOrder.JavaThenScala
|
||||
|
||||
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.misc.HelperFunctions;
|
||||
|
||||
public class FontFunCaptcha implements ChallengeProvider{
|
||||
public class FontFunCaptcha implements ChallengeProvider {
|
||||
|
||||
public String getId() {
|
||||
return "FontFunCaptcha";
|
||||
}
|
||||
public String getId() {
|
||||
return "FontFunCaptcha";
|
||||
}
|
||||
|
||||
private String getFontName(String path, String level){
|
||||
File file = new File(path+level+"/");
|
||||
FilenameFilter txtFileFilter = new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name)
|
||||
{
|
||||
if(name.endsWith(".ttf"))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
private String getFontName(String path, String level) {
|
||||
File file = new File(path + level + "/");
|
||||
FilenameFilter txtFileFilter =
|
||||
new FilenameFilter() {
|
||||
@Override
|
||||
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();
|
||||
}
|
||||
File[] files = file.listFiles(txtFileFilter);
|
||||
return path
|
||||
+ level.toLowerCase()
|
||||
+ "/"
|
||||
+ files[HelperFunctions.randomNumber(0, files.length - 1)].getName();
|
||||
}
|
||||
|
||||
private Font loadCustomFont(String level, String path) {
|
||||
String fontName = getFontName(path,level);
|
||||
try{
|
||||
Font font = Font.createFont(Font.TRUETYPE_FONT, new File(fontName));
|
||||
font = font.deriveFont(Font.PLAIN, 48f);
|
||||
return font;
|
||||
} catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
private Font loadCustomFont(String level, String path) {
|
||||
String fontName = getFontName(path, level);
|
||||
try {
|
||||
Font font = Font.createFont(Font.TRUETYPE_FONT, new File(fontName));
|
||||
font = font.deriveFont(Font.PLAIN, 48f);
|
||||
return font;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[] fontFun(String captchaText, String level, String path){
|
||||
String[] colors = {"#f68787","#f8a978","#f1eb9a","#a4f6a5"};
|
||||
BufferedImage img = new BufferedImage(350, 100, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D graphics2D = img.createGraphics();
|
||||
for(int i=0; i< captchaText.length(); i++) {
|
||||
Font font = loadCustomFont(level,path);
|
||||
graphics2D.setFont(font);
|
||||
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.dispose();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try {
|
||||
ImageIO.write(img,"png",baos);
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return baos.toByteArray();
|
||||
private byte[] fontFun(String captchaText, String level, String path) {
|
||||
String[] colors = {"#f68787", "#f8a978", "#f1eb9a", "#a4f6a5"};
|
||||
BufferedImage img = new BufferedImage(350, 100, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D graphics2D = img.createGraphics();
|
||||
for (int i = 0; i < captchaText.length(); i++) {
|
||||
Font font = loadCustomFont(level, path);
|
||||
graphics2D.setFont(font);
|
||||
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.dispose();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try {
|
||||
ImageIO.write(img, "png", baos);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
public Challenge returnChallenge() {
|
||||
String secret = HelperFunctions.randomString(7);
|
||||
String path = "./lib/fonts/";
|
||||
return new Challenge(fontFun(secret,"medium",path),"image/png",secret.toLowerCase());
|
||||
}
|
||||
public Challenge returnChallenge() {
|
||||
String secret = HelperFunctions.randomString(7);
|
||||
String path = "./lib/fonts/";
|
||||
return new Challenge(fontFun(secret, "medium", path), "image/png", secret.toLowerCase());
|
||||
}
|
||||
|
||||
public boolean checkAnswer(String secret, String answer){
|
||||
return answer.toLowerCase().equals(secret);
|
||||
}
|
||||
public boolean checkAnswer(String secret, String answer) {
|
||||
return answer.toLowerCase().equals(secret);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,49 +14,50 @@ import lc.captchas.interfaces.ChallengeProvider;
|
|||
import lc.misc.HelperFunctions;
|
||||
import lc.misc.GifSequenceWriter;
|
||||
|
||||
public class GifCaptcha implements ChallengeProvider{
|
||||
public class GifCaptcha implements ChallengeProvider {
|
||||
|
||||
private BufferedImage charToImg(String text){
|
||||
BufferedImage img = new BufferedImage(250, 100, BufferedImage.TYPE_INT_RGB);
|
||||
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.setFont(font);
|
||||
graphics2D.setColor(new Color((int)(Math.random() * 0x1000000)));
|
||||
graphics2D.drawString( text , 45, 45);
|
||||
graphics2D.dispose();
|
||||
return img;
|
||||
}
|
||||
private BufferedImage charToImg(String text) {
|
||||
BufferedImage img = new BufferedImage(250, 100, BufferedImage.TYPE_INT_RGB);
|
||||
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.setFont(font);
|
||||
graphics2D.setColor(new Color((int) (Math.random() * 0x1000000)));
|
||||
graphics2D.drawString(text, 45, 45);
|
||||
graphics2D.dispose();
|
||||
return img;
|
||||
}
|
||||
|
||||
private byte[] gifCaptcha(String text){
|
||||
try {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
ImageOutputStream output = new MemoryCacheImageOutputStream(byteArrayOutputStream);
|
||||
GifSequenceWriter writer = new GifSequenceWriter( output, 1,1000, true );
|
||||
for(int i=0; i< text.length(); i++){
|
||||
BufferedImage nextImage = charToImg(String.valueOf(text.charAt(i)));
|
||||
writer.writeToSequence(nextImage);
|
||||
}
|
||||
writer.close();
|
||||
output.close();
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
} catch (IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
private byte[] gifCaptcha(String text) {
|
||||
try {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
ImageOutputStream output = new MemoryCacheImageOutputStream(byteArrayOutputStream);
|
||||
GifSequenceWriter writer = new GifSequenceWriter(output, 1, 1000, true);
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
BufferedImage nextImage = charToImg(String.valueOf(text.charAt(i)));
|
||||
writer.writeToSequence(nextImage);
|
||||
}
|
||||
writer.close();
|
||||
output.close();
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Challenge returnChallenge() {
|
||||
String secret = HelperFunctions.randomString(6);
|
||||
return new Challenge(gifCaptcha(secret),"image/gif",secret.toLowerCase());
|
||||
}
|
||||
public Challenge returnChallenge() {
|
||||
String secret = HelperFunctions.randomString(6);
|
||||
return new Challenge(gifCaptcha(secret), "image/gif", secret.toLowerCase());
|
||||
}
|
||||
|
||||
public boolean checkAnswer(String secret, String answer) {
|
||||
return answer.toLowerCase().equals(secret);
|
||||
}
|
||||
public boolean checkAnswer(String secret, String answer) {
|
||||
return answer.toLowerCase().equals(secret);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return "GifCaptcha";
|
||||
}
|
||||
public String getId() {
|
||||
return "GifCaptcha";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,54 +14,54 @@ import lc.misc.HelperFunctions;
|
|||
import lc.captchas.interfaces.Challenge;
|
||||
import lc.captchas.interfaces.ChallengeProvider;
|
||||
|
||||
public class ShadowTextCaptcha implements ChallengeProvider{
|
||||
public class ShadowTextCaptcha implements ChallengeProvider {
|
||||
|
||||
public String getId() {
|
||||
return "ShadowTextCaptcha";
|
||||
public String getId() {
|
||||
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) {
|
||||
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 Challenge returnChallenge() {
|
||||
String secret = HelperFunctions.randomString(6);
|
||||
return new Challenge(shadowText(secret),"image/png",secret.toLowerCase());
|
||||
}
|
||||
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 String getId();
|
||||
|
||||
public Challenge returnChallenge();
|
||||
|
||||
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
|
||||
|
||||
package lc.misc;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.metadata.*;
|
||||
import javax.imageio.stream.*;
|
||||
|
@ -10,136 +11,113 @@ import java.io.*;
|
|||
import java.util.Iterator;
|
||||
|
||||
public class GifSequenceWriter {
|
||||
protected ImageWriter gifWriter;
|
||||
protected ImageWriteParam imageWriteParam;
|
||||
protected IIOMetadata imageMetaData;
|
||||
protected ImageWriter gifWriter;
|
||||
protected ImageWriteParam imageWriteParam;
|
||||
protected IIOMetadata imageMetaData;
|
||||
|
||||
/**
|
||||
* Creates a new GifSequenceWriter
|
||||
*
|
||||
* @param outputStream the ImageOutputStream to be written to
|
||||
* @param imageType one of the imageTypes specified in BufferedImage
|
||||
* @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 {
|
||||
// my method to create a writer
|
||||
gifWriter = getWriter();
|
||||
imageWriteParam = gifWriter.getDefaultWriteParam();
|
||||
ImageTypeSpecifier imageTypeSpecifier =
|
||||
ImageTypeSpecifier.createFromBufferedImageType(imageType);
|
||||
/**
|
||||
* Creates a new GifSequenceWriter
|
||||
*
|
||||
* @param outputStream the ImageOutputStream to be written to
|
||||
* @param imageType one of the imageTypes specified in BufferedImage
|
||||
* @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 {
|
||||
// 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();
|
||||
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(
|
||||
"delayTime",
|
||||
Integer.toString(timeBetweenFramesMS / 10));
|
||||
graphicsControlExtensionNode.setAttribute(
|
||||
"transparentColorIndex",
|
||||
"0");
|
||||
graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
|
||||
graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
|
||||
graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE");
|
||||
graphicsControlExtensionNode.setAttribute(
|
||||
"delayTime", Integer.toString(timeBetweenFramesMS / 10));
|
||||
graphicsControlExtensionNode.setAttribute("transparentColorIndex", "0");
|
||||
|
||||
IIOMetadataNode commentsNode = getNode(root, "CommentExtensions");
|
||||
commentsNode.setAttribute("CommentExtension", "Created by MAH");
|
||||
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");
|
||||
IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
|
||||
|
||||
child.setAttribute("applicationID", "NETSCAPE");
|
||||
child.setAttribute("authenticationCode", "2.0");
|
||||
child.setAttribute("applicationID", "NETSCAPE");
|
||||
child.setAttribute("authenticationCode", "2.0");
|
||||
|
||||
int loop = loopContinuously ? 0 : 1;
|
||||
int loop = loopContinuously ? 0 : 1;
|
||||
|
||||
child.setUserObject(new byte[]{ 0x1, (byte) (loop & 0xFF), (byte)
|
||||
((loop >> 8) & 0xFF)});
|
||||
appEntensionsNode.appendChild(child);
|
||||
child.setUserObject(new byte[] {0x1, (byte) (loop & 0xFF), (byte) ((loop >> 8) & 0xFF)});
|
||||
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(
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,24 +4,24 @@ 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
public static String randomString(int n){
|
||||
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz23456789$#%@&?";
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for(int i=0; i<n; i++){
|
||||
int index = (int)(characters.length() * Math.random());
|
||||
stringBuilder.append(characters.charAt(index));
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
public static String randomString(int n) {
|
||||
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz23456789$#%@&?";
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (int i = 0; i < n; i++) {
|
||||
int index = (int) (characters.length() * Math.random());
|
||||
stringBuilder.append(characters.charAt(index));
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
public static int randomNumber(int min,int max){
|
||||
return (int)(Math.random() * ((max - min) +1)) + min;
|
||||
}
|
||||
public static int randomNumber(int min, int max) {
|
||||
return (int) (Math.random() * ((max - min) + 1)) + min;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,17 +1,12 @@
|
|||
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]) {
|
||||
val captcha = new Captcha()
|
||||
object LCFramework {
|
||||
def main(args: scala.Array[String]): Unit = {
|
||||
val captcha = new Captcha()
|
||||
val server = new Server(8888, captcha)
|
||||
val backgroudTask = new BackgroundTask(captcha, 10)
|
||||
backgroudTask.beginThread(2)
|
||||
|
@ -20,15 +15,16 @@ 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) =>
|
||||
val extensionMap = Map("image/png" -> "png", "image/gif" -> "gif")
|
||||
println(key + ": " + sample)
|
||||
samples.foreach {
|
||||
case (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))
|
||||
outStream.write(sample.content)
|
||||
outStream.close
|
||||
val outStream = new java.io.FileOutputStream("samples/" + key + "." + extensionMap(sample.contentType))
|
||||
outStream.write(sample.content)
|
||||
outStream.close
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,34 +5,33 @@ 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 {
|
||||
def run(): Unit = {
|
||||
try {
|
||||
private val task = new Runnable {
|
||||
def run(): Unit = {
|
||||
try {
|
||||
|
||||
val mapIdGCPstmt = Statements.tlStmts.get.mapIdGCPstmt
|
||||
mapIdGCPstmt.executeUpdate()
|
||||
val mapIdGCPstmt = Statements.tlStmts.get.mapIdGCPstmt
|
||||
mapIdGCPstmt.executeUpdate()
|
||||
|
||||
val challengeGCPstmt = Statements.tlStmts.get.challengeGCPstmt
|
||||
challengeGCPstmt.executeUpdate()
|
||||
val challengeGCPstmt = Statements.tlStmts.get.challengeGCPstmt
|
||||
challengeGCPstmt.executeUpdate()
|
||||
|
||||
val imageNum = Statements.tlStmts.get.getCountChallengeTable.executeQuery()
|
||||
var throttleIn = (throttle*1.1).toInt
|
||||
if(imageNum.next())
|
||||
throttleIn = (throttleIn-imageNum.getInt("total"))
|
||||
while(0 < throttleIn){
|
||||
captcha.generateChallenge(Parameters("","","",Option(Size(0,0))))
|
||||
throttleIn -= 1
|
||||
}
|
||||
} catch { case e: Exception => println(e) }
|
||||
val imageNum = Statements.tlStmts.get.getCountChallengeTable.executeQuery()
|
||||
var throttleIn = (throttle * 1.1).toInt
|
||||
if (imageNum.next())
|
||||
throttleIn = (throttleIn - imageNum.getInt("total"))
|
||||
while (0 < throttleIn) {
|
||||
captcha.generateChallenge(Parameters("", "", "", Option(Size(0, 0))))
|
||||
throttleIn -= 1
|
||||
}
|
||||
} 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.Challenge
|
||||
|
||||
|
||||
class FilterChallenge extends ChallengeProvider {
|
||||
def getId = "FilterChallenge"
|
||||
def returnChallenge(): Challenge = {
|
||||
|
@ -62,4 +61,3 @@ class FilterType2 extends FilterType {
|
|||
image
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,45 +4,45 @@ 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]()
|
||||
total += file -> 0
|
||||
for (file <- unknownFiles) {
|
||||
unknownAnswers += file -> Map[String, Int]()
|
||||
total += file -> 0
|
||||
}
|
||||
|
||||
def getId = "LabelCaptcha"
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
var knownImage = ImageIO.read(new File("known/"+knownImageFile))
|
||||
var unknownImage = ImageIO.read(new File("unknown/"+unknownImageFile))
|
||||
val mergedImage = merge(knownImage, unknownImage)
|
||||
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)
|
||||
val baos = new ByteArrayOutputStream()
|
||||
ImageIO.write(mergedImage,"png",baos)
|
||||
val token = encrypt(knownImageFile + "," + unknownImageFile)
|
||||
val baos = new ByteArrayOutputStream()
|
||||
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) = {
|
||||
val width = knownImage.getWidth()+unknownImage.getWidth()
|
||||
val width = knownImage.getWidth() + unknownImage.getWidth()
|
||||
val height = List(knownImage.getHeight(), unknownImage.getHeight()).max
|
||||
val imageType = knownImage.getType()
|
||||
val finalImage = new BufferedImage(width, height, imageType)
|
||||
|
@ -55,34 +55,39 @@ class LabelCaptcha extends ChallengeProvider {
|
|||
finalImage
|
||||
}
|
||||
|
||||
def checkAnswer(token: String, input: String): Boolean = synchronized {
|
||||
val parts = decrypt(token).split(",")
|
||||
val knownImage = parts(0)
|
||||
val unknownImage = parts(1)
|
||||
val expectedAnswer = knownImage.split('.')(0)
|
||||
val userAnswer = input.split(' ')
|
||||
if(userAnswer(0)==expectedAnswer) {
|
||||
val unknownFile = unknownImage
|
||||
if((unknownAnswers(unknownFile)).contains(userAnswer(1))) {
|
||||
unknownAnswers(unknownFile)(userAnswer(1)) += 1
|
||||
total(unknownFile) += 1
|
||||
} else {
|
||||
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
|
||||
def checkAnswer(token: String, input: String): Boolean =
|
||||
synchronized {
|
||||
val parts = decrypt(token).split(",")
|
||||
val knownImage = parts(0)
|
||||
val unknownImage = parts(1)
|
||||
val expectedAnswer = knownImage.split('.')(0)
|
||||
val userAnswer = input.split(' ')
|
||||
if (userAnswer(0) == expectedAnswer) {
|
||||
val unknownFile = unknownImage
|
||||
if ((unknownAnswers(unknownFile)).contains(userAnswer(1))) {
|
||||
unknownAnswers(unknownFile)(userAnswer(1)) += 1
|
||||
total(unknownFile) += 1
|
||||
} else {
|
||||
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
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -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,8 +17,8 @@ class Drop {
|
|||
var yOffset = 0
|
||||
var color = 0
|
||||
var colorChange = 10
|
||||
def mkColor = {
|
||||
new Color(color, color, math.min(200, color+100))
|
||||
def mkColor: Color = {
|
||||
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) = {
|
||||
drops.map(d => {
|
||||
val nd = new Drop()
|
||||
nd.x + xOffset*steps
|
||||
nd.y + d.yOffset*steps
|
||||
nd.x + xOffset * steps
|
||||
nd.y + d.yOffset * steps
|
||||
nd
|
||||
})
|
||||
}
|
||||
|
@ -48,20 +46,23 @@ class RainDropsCP extends ChallengeProvider {
|
|||
val width = 450
|
||||
val height = 100
|
||||
val imgType = BufferedImage.TYPE_INT_RGB
|
||||
val xOffset = 2+r.nextInt(3)
|
||||
val xOffset = 2 + r.nextInt(3)
|
||||
val xBias = (height / 10) - 2
|
||||
val dropsOrig = Array.fill[Drop](2000)( new Drop())
|
||||
val dropsOrig = Array.fill[Drop](2000)(new Drop())
|
||||
for (d <- dropsOrig) {
|
||||
d.x = r.nextInt(width) - (xBias/2)*xOffset
|
||||
d.yOffset = 6+r.nextInt(6)
|
||||
d.x = r.nextInt(width) - (xBias / 2) * xOffset
|
||||
d.yOffset = 6 + r.nextInt(6)
|
||||
d.y = r.nextInt(height)
|
||||
d.color = r.nextInt(240)
|
||||
if (d.color > 128) {
|
||||
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()
|
||||
|
@ -85,14 +86,14 @@ class RainDropsCP extends ChallengeProvider {
|
|||
// paint the rain
|
||||
for (d <- drops) {
|
||||
g.setColor(d.mkColor)
|
||||
g.drawLine(d.x, d.y, d.x+xOffset, d.y+d.yOffset)
|
||||
d.x += xOffset/2
|
||||
d.y += d.yOffset/2
|
||||
g.drawLine(d.x, d.y, d.x + xOffset, d.y + d.yOffset)
|
||||
d.x += xOffset / 2
|
||||
d.y += d.yOffset / 2
|
||||
d.color += d.colorChange
|
||||
if (d.x > width || d.y > height) {
|
||||
val ySteps = (height / d.yOffset) + 1
|
||||
d.x -= xOffset*ySteps
|
||||
d.y -= d.yOffset*ySteps
|
||||
val ySteps = (height / d.yOffset) + 1
|
||||
d.x -= xOffset * ySteps
|
||||
d.y -= d.yOffset * ySteps
|
||||
|
||||
}
|
||||
if (d.color > 200 || d.color < 21) {
|
||||
|
@ -103,7 +104,7 @@ class RainDropsCP extends ChallengeProvider {
|
|||
// center the text
|
||||
g.setFont(spacedFont)
|
||||
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
|
||||
g.setColor(textHighlightColor)
|
||||
|
|
|
@ -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
|
||||
|
@ -10,29 +9,30 @@ import lc.core.CaptchaProviders
|
|||
class Captcha {
|
||||
|
||||
def getCaptcha(id: Id): Array[Byte] = {
|
||||
var image :Array[Byte] = null
|
||||
var image: Array[Byte] = null
|
||||
var blob: Blob = null
|
||||
try {
|
||||
val imagePstmt = Statements.tlStmts.get.imagePstmt
|
||||
imagePstmt.setString(1, id.id)
|
||||
val rs: ResultSet = imagePstmt.executeQuery()
|
||||
if(rs.next()){
|
||||
imagePstmt.setString(1, id.id)
|
||||
val rs: ResultSet = imagePstmt.executeQuery()
|
||||
if (rs.next()) {
|
||||
blob = rs.getBlob("image")
|
||||
if(blob != null){
|
||||
image = blob.getBytes(1, blob.length().toInt)
|
||||
if (blob != null) {
|
||||
image = blob.getBytes(1, blob.length().toInt)
|
||||
}
|
||||
}
|
||||
image
|
||||
} catch { case e: Exception =>
|
||||
println(e)
|
||||
image
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
println(e)
|
||||
image
|
||||
}
|
||||
}
|
||||
|
||||
def generateChallenge(param: Parameters): Int = {
|
||||
//TODO: eval params to choose a provider
|
||||
val provider = CaptchaProviders.getProvider()
|
||||
val providerId = provider.getId()
|
||||
//TODO: eval params to choose a provider
|
||||
val provider = CaptchaProviders.getProvider()
|
||||
val providerId = provider.getId()
|
||||
val challenge = provider.returnChallenge()
|
||||
val blob = new ByteArrayInputStream(challenge.content)
|
||||
val insertPstmt = Statements.tlStmts.get.insertPstmt
|
||||
|
@ -43,10 +43,10 @@ class Captcha {
|
|||
insertPstmt.setBlob(5, blob)
|
||||
insertPstmt.executeUpdate()
|
||||
val rs: ResultSet = insertPstmt.getGeneratedKeys()
|
||||
val token = if(rs.next()){
|
||||
val token = if (rs.next()) {
|
||||
rs.getInt("token")
|
||||
}
|
||||
println("Added new challenge: "+ token.toString)
|
||||
println("Added new challenge: " + token.toString)
|
||||
token.asInstanceOf[Int]
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ class Captcha {
|
|||
try {
|
||||
val tokenPstmt = Statements.tlStmts.get.tokenPstmt
|
||||
val rs = tokenPstmt.executeQuery()
|
||||
val tokenOpt = if(rs.next()) {
|
||||
val tokenOpt = if (rs.next()) {
|
||||
Some(rs.getInt("token"))
|
||||
} else {
|
||||
None
|
||||
|
@ -64,44 +64,45 @@ class Captcha {
|
|||
updateAttemptedPstmt.setString(1, uuid)
|
||||
updateAttemptedPstmt.executeUpdate()
|
||||
Id(uuid)
|
||||
} catch {case e: Exception =>
|
||||
println(e)
|
||||
Id(getUUID(-1))
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
println(e)
|
||||
Id(getUUID(-1))
|
||||
}
|
||||
}
|
||||
|
||||
private def getUUID(id: Int): String = {
|
||||
val uuid = UUID.randomUUID().toString
|
||||
val mapPstmt = Statements.tlStmts.get.mapPstmt
|
||||
mapPstmt.setString(1,uuid)
|
||||
mapPstmt.setInt(2,id)
|
||||
mapPstmt.setString(1, uuid)
|
||||
mapPstmt.setInt(2, id)
|
||||
mapPstmt.executeUpdate()
|
||||
uuid
|
||||
}
|
||||
|
||||
def checkAnswer(answer: Answer): Result = {
|
||||
val selectPstmt = Statements.tlStmts.get.selectPstmt
|
||||
selectPstmt.setString(1, answer.id)
|
||||
val rs: ResultSet = selectPstmt.executeQuery()
|
||||
val psOpt = if (rs.first()) {
|
||||
val secret = rs.getString("secret")
|
||||
val provider = rs.getString("provider")
|
||||
val check = CaptchaProviders.getProviderById(provider).checkAnswer(secret, answer.answer)
|
||||
val result = if(check) "TRUE" else "FALSE"
|
||||
result
|
||||
} else {
|
||||
"EXPIRED"
|
||||
}
|
||||
val deleteAnswerPstmt = Statements.tlStmts.get.deleteAnswerPstmt
|
||||
deleteAnswerPstmt.setString(1, answer.id)
|
||||
deleteAnswerPstmt.executeUpdate()
|
||||
Result(psOpt)
|
||||
val selectPstmt = Statements.tlStmts.get.selectPstmt
|
||||
selectPstmt.setString(1, answer.id)
|
||||
val rs: ResultSet = selectPstmt.executeQuery()
|
||||
val psOpt = if (rs.first()) {
|
||||
val secret = rs.getString("secret")
|
||||
val provider = rs.getString("provider")
|
||||
val check = CaptchaProviders.getProviderById(provider).checkAnswer(secret, answer.answer)
|
||||
val result = if (check) "TRUE" else "FALSE"
|
||||
result
|
||||
} else {
|
||||
"EXPIRED"
|
||||
}
|
||||
val deleteAnswerPstmt = Statements.tlStmts.get.deleteAnswerPstmt
|
||||
deleteAnswerPstmt.setString(1, answer.id)
|
||||
deleteAnswerPstmt.executeUpdate()
|
||||
Result(psOpt)
|
||||
}
|
||||
|
||||
def display(): Unit = {
|
||||
val rs: ResultSet = Statements.tlStmts.get.getChallengeTable.executeQuery()
|
||||
println("token\t\tid\t\tsecret\t\tattempted")
|
||||
while(rs.next()) {
|
||||
while (rs.next()) {
|
||||
val token = rs.getInt("token")
|
||||
val id = rs.getString("id")
|
||||
val secret = rs.getString("secret")
|
||||
|
@ -111,11 +112,11 @@ class Captcha {
|
|||
|
||||
val rss: ResultSet = Statements.tlStmts.get.getMapIdTable.executeQuery()
|
||||
println("uuid\t\ttoken\t\tlastServed")
|
||||
while(rss.next()){
|
||||
while (rss.next()) {
|
||||
val uuid = rss.getString("uuid")
|
||||
val token = rss.getInt("token")
|
||||
val lastServed = rss.getTimestamp("lastServed")
|
||||
println(s"${uuid}\t\t${token}\t\t${lastServed}\n\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,30 +10,32 @@ 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) =>
|
||||
(key, provider.returnChallenge())
|
||||
def generateChallengeSamples(): Map[String, Challenge] = {
|
||||
providers.map {
|
||||
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 def getNextRandomInt(max: Int) = random.synchronized {
|
||||
random.nextInt(max)
|
||||
}
|
||||
private def getNextRandomInt(max: Int) =
|
||||
random.synchronized {
|
||||
random.nextInt(max)
|
||||
}
|
||||
|
||||
def getProviderById(id: String): ChallengeProvider = {
|
||||
return providers(id)
|
||||
}
|
||||
|
||||
|
||||
def getProvider(): ChallengeProvider = {
|
||||
val keys = providers.keys
|
||||
val providerIndex = keys.toVector(getNextRandomInt(keys.size))
|
||||
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 Id(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._
|
||||
|
||||
class DBConn(){
|
||||
class DBConn() {
|
||||
val con: Connection = DriverManager.getConnection("jdbc:h2:./data/H2/captcha", "sa", "")
|
||||
|
||||
def getStatement(): Statement = {
|
||||
|
@ -10,6 +10,6 @@ class DBConn(){
|
|||
}
|
||||
|
||||
def closeConnection(): Unit = {
|
||||
con.close()
|
||||
con.close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -7,46 +7,56 @@ 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: HTTPServer.VirtualHost = server.getVirtualHost(null)
|
||||
|
||||
class Server(port: Int, captcha: Captcha){
|
||||
val server = new HTTPServer(port)
|
||||
val host = server.getVirtualHost(null)
|
||||
implicit val formats: DefaultFormats.type = DefaultFormats
|
||||
|
||||
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) => {
|
||||
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/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"
|
||||
)
|
||||
|
||||
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")
|
||||
host.addContext(
|
||||
"/v1/answer",
|
||||
(req, resp) => {
|
||||
val body = req.getJson()
|
||||
val json = parse(body)
|
||||
val answer = json.extract[Answer]
|
||||
val result = captcha.checkAnswer(answer)
|
||||
resp.getHeaders().add("Content-Type", "application/json")
|
||||
resp.send(200, write(result))
|
||||
0
|
||||
},
|
||||
"POST"
|
||||
)
|
||||
|
||||
host.addContext("/v1/answer",(req, resp) => {
|
||||
val body = req.getJson()
|
||||
val json = parse(body)
|
||||
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()
|
||||
}
|
||||
def start(): Unit = {
|
||||
println("Starting server on port:" + port)
|
||||
server.start()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue