Refactor modules (#56)

* Refactor:Modules

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

* Add config file

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

* Restore sample images

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>
This commit is contained in:
Rahul Rudragoudar
2021-02-24 15:21:16 +05:30
committed by GitHub
parent b66f777828
commit 6d04cdc3b4
21 changed files with 328 additions and 242 deletions

View File

@@ -0,0 +1,65 @@
package lc.captchas
import com.sksamuel.scrimage._
import com.sksamuel.scrimage.filter._
import java.awt.image.BufferedImage
import java.awt.Font
import java.awt.Color
import lc.captchas.interfaces.ChallengeProvider
import lc.captchas.interfaces.Challenge
class FilterChallenge extends ChallengeProvider {
def getId = "FilterChallenge"
def returnChallenge(): Challenge = {
val filterTypes = List(new FilterType1, new FilterType2)
val r = new scala.util.Random
val alphabet = "abcdefghijklmnopqrstuvwxyz"
val n = 8
val secret = Stream.continually(r.nextInt(alphabet.size)).map(alphabet).take(n).mkString
val canvas = new BufferedImage(225, 50, BufferedImage.TYPE_INT_RGB)
val g = canvas.createGraphics()
g.setColor(Color.WHITE)
g.fillRect(0, 0, canvas.getWidth, canvas.getHeight)
g.setColor(Color.BLACK)
g.setFont(new Font("Serif", Font.PLAIN, 30))
g.drawString(secret, 5, 30)
g.dispose()
var image = ImmutableImage.fromAwt(canvas)
val s = scala.util.Random.nextInt(2)
image = filterTypes(s).applyFilter(image)
new Challenge(image.bytes(new nio.PngWriter()), "image/png", secret)
}
def checkAnswer(secret: String, answer: String): Boolean = {
secret == answer
}
}
trait FilterType {
def applyFilter(image: ImmutableImage): ImmutableImage
}
class FilterType1 extends FilterType {
override def applyFilter(image: ImmutableImage): ImmutableImage = {
val blur = new GaussianBlurFilter(2)
val smear = new SmearFilter(com.sksamuel.scrimage.filter.SmearType.Circles, 10, 10, 10, 0, 1)
val diffuse = new DiffuseFilter(2)
blur.apply(image)
diffuse.apply(image)
smear.apply(image)
image
}
}
class FilterType2 extends FilterType {
override def applyFilter(image: ImmutableImage): ImmutableImage = {
val smear = new SmearFilter(com.sksamuel.scrimage.filter.SmearType.Circles, 10, 10, 10, 0, 1)
val diffuse = new DiffuseFilter(1)
val ripple = new RippleFilter(com.sksamuel.scrimage.filter.RippleType.Noise, 1, 1, 0.005.toFloat, 0.005.toFloat)
diffuse.apply(image)
ripple.apply(image)
smear.apply(image)
image
}
}

View File

@@ -0,0 +1,94 @@
package lc.captchas
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.awt.image.BufferedImage
import java.awt.{Graphics2D,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]()
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)
var knownImage = ImageIO.read(new File("known/"+knownImageFile))
var 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)
new Challenge(baos.toByteArray(), "image/png", token)
}
private def merge(knownImage: BufferedImage, unknownImage: BufferedImage) = {
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)
val g = finalImage.createGraphics()
g.setColor(Color.WHITE)
g.fillRect(0, 0, finalImage.getWidth(), finalImage.getHeight())
g.drawImage(knownImage, null, 0, 0)
g.drawImage(unknownImage, null, knownImage.getWidth(), 0)
g.dispose()
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
}
}
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.
// There are good ideas here: https://stackoverflow.com/questions/1205135/how-to-encrypt-string-in-java
private def encrypt(s: String) = s
private def decrypt(s: String) = s
}
class ImagePair(val known: String, val unknown: String)

View File

@@ -0,0 +1,127 @@
package lc.captchas
import java.awt.image.BufferedImage
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 javax.imageio.stream.MemoryCacheImageOutputStream;
import lc.captchas.interfaces.ChallengeProvider
import lc.captchas.interfaces.Challenge
import lc.misc.GifSequenceWriter
class Drop {
var x = 0
var y = 0
var yOffset = 0
var color = 0
var colorChange = 10
def mkColor = {
new Color(color, color, math.min(200, color+100))
}
}
class RainDropsCP extends ChallengeProvider {
private val alphabet = "abcdefghijklmnopqrstuvwxyz"
private val n = 6
private val bgColor = new Color(200, 200, 200)
private val textColor = new Color(208, 208, 218)
private val textHighlightColor = new Color(100, 100, 125)
def getId = "FilterChallenge"
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
})
}
def returnChallenge(): Challenge = {
val r = new scala.util.Random
val secret = Stream.continually(r.nextInt(alphabet.size)).map(alphabet).take(n).mkString
val width = 450
val height = 100
val imgType = BufferedImage.TYPE_INT_RGB
val xOffset = 2+r.nextInt(3)
val xBias = (height / 10) - 2
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.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 baseFont = new Font(Font.MONOSPACED, Font.BOLD, 80)
val attributes = new java.util.HashMap[TextAttribute, Object]()
attributes.put(TextAttribute.TRACKING, Double.box(0.2))
attributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_EXTRABOLD)
val spacedFont = baseFont.deriveFont(attributes)
val baos = new ByteArrayOutputStream();
val ios = new MemoryCacheImageOutputStream(baos);
val writer = new GifSequenceWriter(ios, imgType, 60, true);
for(i <- 0 until 60){
// val yOffset = 5+r.nextInt(5)
val canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
val g = canvas.createGraphics()
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
// clear the canvas
g.setColor(bgColor)
g.fillRect(0, 0, canvas.getWidth, canvas.getHeight)
// 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
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
}
if (d.color > 200 || d.color < 21) {
d.colorChange *= -1
}
}
// center the text
g.setFont(spacedFont)
val textWidth = g.getFontMetrics().charsWidth(secret.toCharArray, 0, secret.toCharArray.length)
val textX = (width - textWidth)/2
// paint the top outline
g.setColor(textHighlightColor)
g.drawString(secret, textX, 69)
// paint the text
g.setColor(textColor)
g.drawString(secret, textX, 70)
g.dispose()
writer.writeToSequence(canvas)
}
writer.close
ios.close
// ImageIO.write(canvas,"png",baos);
new Challenge(baos.toByteArray, "image/gif", secret)
}
def checkAnswer(secret: String, answer: String): Boolean = {
secret == answer
}
}