mirror of
https://github.com/librecaptcha/lc-core.git
synced 2025-03-31 09:13:38 -04:00
201 lines
6.7 KiB
Scala
201 lines
6.7 KiB
Scala
package lc
|
|
|
|
import com.sksamuel.scrimage._
|
|
import java.io.ByteArrayInputStream
|
|
import java.util.concurrent._
|
|
import java.util.UUID
|
|
import java.sql.{Blob, ResultSet}
|
|
import java.util.concurrent.atomic.AtomicInteger
|
|
|
|
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 ProviderSecret(provider: String, secret: String)
|
|
|
|
object CaptchaProviders {
|
|
val providers = Map(
|
|
"FilterChallenge" -> new FilterChallenge,
|
|
"FontFunCaptcha" -> new FontFunCaptcha,
|
|
"GifCaptcha" -> new GifCaptcha,
|
|
"ShadowTextCaptcha" -> new ShadowTextCaptcha,
|
|
"RainDropsCaptcha" -> new RainDropsCP,
|
|
"LabelCaptcha" -> new LabelCaptcha
|
|
)
|
|
|
|
def generateChallengeSamples() = {
|
|
providers.map {case (key, provider) =>
|
|
(key, provider.returnChallenge())
|
|
}
|
|
}
|
|
}
|
|
|
|
class Captcha(throttle: Int, dbConn: DBConn) {
|
|
import CaptchaProviders._
|
|
|
|
private val stmt = dbConn.getStatement()
|
|
stmt.execute("CREATE TABLE IF NOT EXISTS challenge(token varchar, id varchar, secret varchar, provider varchar, contentType varchar, image blob, solved boolean default False, PRIMARY KEY(token))")
|
|
stmt.execute("CREATE TABLE IF NOT EXISTS mapId(uuid varchar, token varchar, PRIMARY KEY(uuid), FOREIGN KEY(token) REFERENCES challenge(token))")
|
|
stmt.execute("CREATE TABLE IF NOT EXISTS users(email varchar, hash int)")
|
|
|
|
private val insertPstmt = dbConn.con.prepareStatement("INSERT INTO challenge(token, id, secret, provider, contentType, image) VALUES (?, ?, ?, ?, ?, ?)")
|
|
private val mapPstmt = dbConn.con.prepareStatement("INSERT INTO mapId(uuid, token) VALUES (?, ?)")
|
|
private val selectPstmt = dbConn.con.prepareStatement("SELECT secret, provider FROM challenge WHERE token = (SELECT m.token FROM mapId m, challenge c WHERE m.token=c.token AND m.uuid = ?)")
|
|
private val imagePstmt = dbConn.con.prepareStatement("SELECT image FROM challenge c, mapId m WHERE c.token=m.token AND m.uuid = ?")
|
|
private val updatePstmt = dbConn.con.prepareStatement("UPDATE challenge SET solved = True WHERE token = (SELECT m.token FROM mapId m, challenge c WHERE m.token=c.token AND m.uuid = ?)")
|
|
private val userPstmt = dbConn.con.prepareStatement("INSERT INTO users(email, hash) VALUES (?,?)")
|
|
|
|
def getProvider(): String = {
|
|
val random = new scala.util.Random
|
|
val keys = providers.keys
|
|
val providerIndex = keys.toVector(random.nextInt(keys.size))
|
|
providerIndex
|
|
}
|
|
|
|
def getCaptcha(id: Id): Array[Byte] = {
|
|
var image :Array[Byte] = null
|
|
var blob: Blob = null
|
|
val imageOpt = imagePstmt.synchronized {
|
|
imagePstmt.setString(1, id.id)
|
|
val rs: ResultSet = imagePstmt.executeQuery()
|
|
if(rs.next()){
|
|
blob = rs.getBlob("image")
|
|
updatePstmt.synchronized{
|
|
updatePstmt.setString(1,id.id)
|
|
updatePstmt.executeUpdate()
|
|
}
|
|
}
|
|
if(blob != null)
|
|
image = blob.getBytes(1, blob.length().toInt)
|
|
image
|
|
}
|
|
imageOpt
|
|
}
|
|
|
|
private val uniqueIntCount = new AtomicInteger()
|
|
|
|
def generateChallenge(param: Parameters): String = {
|
|
//TODO: eval params to choose a provider
|
|
val providerMap = getProvider()
|
|
val provider = providers(providerMap)
|
|
val challenge = provider.returnChallenge()
|
|
val blob = new ByteArrayInputStream(challenge.content)
|
|
// val token = scala.util.Random.nextInt(100000).toString
|
|
val token = uniqueIntCount.incrementAndGet().toString
|
|
insertPstmt.synchronized {
|
|
insertPstmt.setString(1, token)
|
|
insertPstmt.setString(2, provider.getId)
|
|
insertPstmt.setString(3, challenge.secret)
|
|
insertPstmt.setString(4, providerMap)
|
|
insertPstmt.setString(5, challenge.contentType)
|
|
insertPstmt.setBlob(6, blob)
|
|
insertPstmt.executeUpdate()
|
|
}
|
|
token
|
|
}
|
|
|
|
val task = new Runnable {
|
|
def run(): Unit = {
|
|
val imageNum = stmt.executeQuery("SELECT COUNT(*) AS total FROM challenge")
|
|
var throttleIn = (throttle*1.1).toInt
|
|
if(imageNum.next())
|
|
throttleIn = (throttleIn-imageNum.getInt("total"))
|
|
while(0 < throttleIn){
|
|
getChallenge(Parameters("","","",Option(Size(0,0))))
|
|
throttleIn -= 1
|
|
}
|
|
}
|
|
}
|
|
|
|
def beginThread(delay: Int) : Unit = {
|
|
val ex = new ScheduledThreadPoolExecutor(1)
|
|
val thread = ex.scheduleWithFixedDelay(task, 1, delay, TimeUnit.SECONDS)
|
|
}
|
|
|
|
def getChallenge(param: Parameters): Id = {
|
|
val idOpt = stmt.synchronized {
|
|
val rs = stmt.executeQuery("SELECT token FROM challenge WHERE solved=FALSE ORDER BY RAND() LIMIT 1")
|
|
if(rs.next()) {
|
|
Some(rs.getString("token"))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
val id = idOpt.getOrElse(generateChallenge(param))
|
|
val uuid = getUUID(id)
|
|
Id(uuid)
|
|
}
|
|
|
|
def getUUID(id: String): String = {
|
|
val uuid = UUID.randomUUID().toString
|
|
mapPstmt.synchronized {
|
|
mapPstmt.setString(1,uuid)
|
|
mapPstmt.setString(2,id)
|
|
mapPstmt.executeUpdate()
|
|
}
|
|
uuid
|
|
}
|
|
|
|
def checkAnswer(answer: Answer): Boolean = {
|
|
val psOpt:Option[ProviderSecret] = selectPstmt.synchronized {
|
|
selectPstmt.setString(1, answer.id)
|
|
val rs: ResultSet = selectPstmt.executeQuery()
|
|
if (rs.first()) {
|
|
val secret = rs.getString("secret")
|
|
val provider = rs.getString("provider")
|
|
Some(ProviderSecret(provider, secret))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
psOpt.map(ps => providers(ps.provider).checkAnswer(ps.secret, answer.answer)).getOrElse(false)
|
|
}
|
|
|
|
def getHash(email: String): Int = {
|
|
val secret = ""
|
|
val str = email+secret
|
|
val hash = str.hashCode()
|
|
userPstmt.setString(1, email)
|
|
userPstmt.setInt(2, hash)
|
|
userPstmt.executeUpdate()
|
|
hash
|
|
}
|
|
|
|
def display(): Unit = {
|
|
val rs: ResultSet = stmt.executeQuery("SELECT * FROM challenge")
|
|
println("token\t\tid\t\tsecret\t\tsolved")
|
|
while(rs.next()) {
|
|
val token = rs.getString("token")
|
|
val id = rs.getString("id")
|
|
val secret = rs.getString("secret")
|
|
val solved = rs.getString("solved")
|
|
println(s"${token}\t\t${id}\t\t${secret}\t\t${solved}")
|
|
}
|
|
}
|
|
}
|
|
|
|
object LCFramework{
|
|
def main(args: scala.Array[String]) {
|
|
val dbConn = new DBConn()
|
|
val captcha = new Captcha(2, dbConn)
|
|
val server = new Server(8888, captcha, dbConn)
|
|
captcha.beginThread(2)
|
|
server.start()
|
|
}
|
|
}
|
|
|
|
object MakeSamples {
|
|
def main(args: scala.Array[String]) {
|
|
val samples = CaptchaProviders.generateChallengeSamples()
|
|
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
|
|
}
|
|
}
|
|
}
|