Linter and Formatter support (#58)

* Add scala linter and formatter

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

* Add java formatter

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

* Add linter support

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

* Increase maxColumn limit

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

* Reformat and lint

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

* Minor reformatting

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

* Add scala formatter on compile option

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

* Enable scala linter for CI

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

View File

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

8
.scalafix.conf Normal file
View File

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

2
.scalafmt.conf Normal file
View File

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

View File

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

View File

@ -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

3
project/plugins.sbt Normal file
View File

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

View File

@ -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);
} }
} }

View File

@ -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";
} }
} }

View File

@ -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());
}
} }

View File

@ -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
} }

View File

@ -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);
}
} }

View File

@ -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

View File

@ -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
} }
} }
} }

View File

@ -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)
}
}

View File

@ -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
} }
} }

View File

@ -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.

View File

@ -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)

View File

@ -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")
} }
} }
} }

View File

@ -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)
} }
} }

View File

@ -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)

View File

@ -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()
} }
} }

View File

@ -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))
} }

View File

@ -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()
}
} }