mirror of
				https://github.com/librecaptcha/lc-core.git
				synced 2025-10-29 23:25:03 -04:00 
			
		
		
		
	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:
		
							parent
							
								
									b66f777828
								
							
						
					
					
						commit
						6d04cdc3b4
					
				
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -5,6 +5,9 @@ | ||||
| /project/** | ||||
| /target/ | ||||
| **__pycache__ | ||||
| .bloop | ||||
| .metals | ||||
| .vscode | ||||
| 
 | ||||
| # for various captcha | ||||
| /known/ | ||||
|  | ||||
							
								
								
									
										34
									
								
								config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								config.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| { | ||||
|     "randomSeed": 20, | ||||
|     "captchaExpiryTimeLimit": 5, | ||||
|     "captchas":{ | ||||
|         "FilterChallenge":{ | ||||
|             "name": "FilterChallenge", | ||||
|             "supportedLevels":["medium", "hard"], | ||||
|             "supportedMedia":["image"], | ||||
|             "supportedinputType":["text"], | ||||
|             "config":{} | ||||
|         }, | ||||
|         "GifCaptcha":{ | ||||
|             "name": "GifCaptcha", | ||||
|             "supportedLevels":["hard"], | ||||
|             "supportedMedia":["gif"], | ||||
|             "supportedinputType":["text"], | ||||
|             "config":{} | ||||
|         }, | ||||
|         "ShadowTextCaptcha":{ | ||||
|             "name": "ShadowTextCaptcha", | ||||
|             "supportedLevels":["easy"], | ||||
|             "supportedMedia":["image"], | ||||
|             "supportedinputType":["text"], | ||||
|             "config": {} | ||||
|         }, | ||||
|         "RainDropsCaptcha":{ | ||||
|             "name": "RainDropsCaptcha", | ||||
|             "supportedLevels":["easy","medium"], | ||||
|             "supportedMedia":["image"], | ||||
|             "supportedinputType":["text"], | ||||
|             "config":{} | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| package lc; | ||||
| package lc.captchas; | ||||
| 
 | ||||
| import javax.imageio.ImageIO; | ||||
| import java.awt.*; | ||||
| @ -6,6 +6,9 @@ import java.awt.image.BufferedImage; | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.File; | ||||
| import java.io.FilenameFilter; | ||||
| import lc.captchas.interfaces.Challenge; | ||||
| import lc.captchas.interfaces.ChallengeProvider; | ||||
| import lc.misc.HelperFunctions; | ||||
| 
 | ||||
| public class FontFunCaptcha implements ChallengeProvider{ | ||||
| 
 | ||||
| @ -1,4 +1,4 @@ | ||||
| package lc; | ||||
| package lc.captchas; | ||||
| 
 | ||||
| import java.awt.Font; | ||||
| import java.awt.Graphics2D; | ||||
| @ -9,6 +9,10 @@ import java.io.IOException; | ||||
| import javax.imageio.stream.ImageOutputStream; | ||||
| import javax.imageio.stream.MemoryCacheImageOutputStream; | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import lc.captchas.interfaces.Challenge; | ||||
| import lc.captchas.interfaces.ChallengeProvider; | ||||
| import lc.misc.HelperFunctions; | ||||
| import lc.misc.GifSequenceWriter; | ||||
| 
 | ||||
| public class GifCaptcha implements ChallengeProvider{ | ||||
| 
 | ||||
| @ -1,4 +1,4 @@ | ||||
| package lc; | ||||
| package lc.captchas; | ||||
| 
 | ||||
| import javax.imageio.ImageIO; | ||||
| import java.awt.Graphics2D; | ||||
| @ -10,11 +10,14 @@ import java.awt.image.BufferedImage; | ||||
| import java.awt.image.ConvolveOp; | ||||
| import java.awt.image.Kernel; | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import lc.misc.HelperFunctions; | ||||
| import lc.captchas.interfaces.Challenge; | ||||
| import lc.captchas.interfaces.ChallengeProvider; | ||||
| 
 | ||||
| public class ShadowTextCaptcha implements ChallengeProvider{ | ||||
| 
 | ||||
|     public String getId() { | ||||
|         return "ShadowText"; | ||||
|         return "ShadowTextCaptcha"; | ||||
|     } | ||||
| 
 | ||||
|     public boolean checkAnswer(String secret, String answer) { | ||||
| @ -1,4 +1,4 @@ | ||||
| package lc; | ||||
| package lc.captchas.interfaces; | ||||
| 
 | ||||
| public class Challenge { | ||||
|   public final byte[] content; | ||||
| @ -1,6 +1,6 @@ | ||||
| package lc; | ||||
| package lc.captchas.interfaces; | ||||
| 
 | ||||
| interface ChallengeProvider { | ||||
| public interface ChallengeProvider { | ||||
|   public String getId(); | ||||
|   public Challenge returnChallenge(); | ||||
|   public boolean checkAnswer(String secret, String answer); | ||||
| @ -1,7 +1,7 @@ | ||||
| // This code was adapted from http://elliot.kroo.net/software/java/GifSequenceWriter/ | ||||
| // It was available under CC By 3.0 | ||||
| 
 | ||||
| package lc; | ||||
| package lc.misc; | ||||
| import javax.imageio.*; | ||||
| import javax.imageio.metadata.*; | ||||
| import javax.imageio.stream.*; | ||||
| @ -1,4 +1,4 @@ | ||||
| package lc; | ||||
| package lc.misc; | ||||
| 
 | ||||
| import java.awt.*; | ||||
| 
 | ||||
| @ -1,4 +1,4 @@ | ||||
| package lc;/* | ||||
| package lc.server;/* | ||||
|  *  Copyright © 2005-2018 Amichai Rothman | ||||
|  * | ||||
|  *  This file is part of JLHTTP - the Java Lightweight HTTP Server. | ||||
| @ -1,222 +1,20 @@ | ||||
| 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 | ||||
| import java.io._ | ||||
| import java.sql.Statement | ||||
| 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 | ||||
| 
 | ||||
| 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 Statements(dbConn: DBConn) { | ||||
|   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") | ||||
| } | ||||
| 
 | ||||
| object Statements { | ||||
|   var dbConn: DBConn = _ | ||||
|   val tlStmts = ThreadLocal.withInitial(() => new Statements(dbConn)) | ||||
| } | ||||
| 
 | ||||
| class Captcha(throttle: Int, dbConn: DBConn) { | ||||
|   import CaptchaProviders._ | ||||
| 
 | ||||
|   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)") | ||||
| 
 | ||||
|   private val seed = System.currentTimeMillis.toString.substring(2,6).toInt | ||||
|   private val random = new scala.util.Random(seed) | ||||
| 
 | ||||
|   def getNextRandomInt(max: Int) = random.synchronized { | ||||
|     random.nextInt(max) | ||||
|   } | ||||
|    | ||||
|   def getProvider(): String = { | ||||
|     val keys = providers.keys | ||||
|     val providerIndex = keys.toVector(getNextRandomInt(keys.size)) | ||||
|     providerIndex | ||||
|   } | ||||
| 
 | ||||
|   def getCaptcha(id: Id): Array[Byte] = { | ||||
|     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()){ | ||||
|         blob = rs.getBlob("image") | ||||
|         if(blob != null){ | ||||
|     		  image =  blob.getBytes(1, blob.length().toInt) | ||||
|         } | ||||
|       } | ||||
|     image | ||||
|     } catch { case e: Exception => | ||||
|       println(e) | ||||
|       image | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private val uniqueIntCount = new AtomicInteger() | ||||
| 
 | ||||
|   def generateChallenge(param: Parameters): Int = { | ||||
|   	//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 insertPstmt = Statements.tlStmts.get.insertPstmt | ||||
|     insertPstmt.setString(1, provider.getId) | ||||
|     insertPstmt.setString(2, challenge.secret) | ||||
|     insertPstmt.setString(3, providerMap) | ||||
|     insertPstmt.setString(4, challenge.contentType) | ||||
|     insertPstmt.setBlob(5, blob) | ||||
|     insertPstmt.executeUpdate() | ||||
|     val rs: ResultSet = insertPstmt.getGeneratedKeys() | ||||
|     val token = if(rs.next()){ | ||||
|       rs.getInt("token") | ||||
|     } | ||||
|     println("Added new challenge: "+ token.toString) | ||||
|     token.asInstanceOf[Int] | ||||
|   } | ||||
| 
 | ||||
|   val task = new Runnable { | ||||
|   	def run(): Unit = { | ||||
|       try { | ||||
| 
 | ||||
|         val mapIdGCPstmt = Statements.tlStmts.get.mapIdGCPstmt | ||||
|         mapIdGCPstmt.executeUpdate() | ||||
| 
 | ||||
|         val challengeGCPstmt = Statements.tlStmts.get.challengeGCPstmt | ||||
|         challengeGCPstmt.executeUpdate() | ||||
| 
 | ||||
|         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){ | ||||
|           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 getChallenge(param: Parameters): Id = { | ||||
|     try { | ||||
|       val tokenPstmt = Statements.tlStmts.get.tokenPstmt | ||||
|       val rs = tokenPstmt.executeQuery() | ||||
|       val tokenOpt = if(rs.next()) { | ||||
|         Some(rs.getInt("token")) | ||||
|       } else { | ||||
|         None | ||||
|       } | ||||
|       val updateAttemptedPstmt = Statements.tlStmts.get.updateAttemptedPstmt | ||||
|       val uuid = getUUID(tokenOpt.getOrElse(generateChallenge(param))) | ||||
|       updateAttemptedPstmt.setString(1, uuid) | ||||
|       updateAttemptedPstmt.executeUpdate() | ||||
|       Id(uuid) | ||||
|     } catch {case e: Exception =>  | ||||
|       println(e) | ||||
|       Id(getUUID(-1)) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   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.executeUpdate() | ||||
|     uuid | ||||
|   } | ||||
| 
 | ||||
|   def checkAnswer(answer: Answer): String = { | ||||
|       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 = providers(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() | ||||
|       psOpt | ||||
|   } | ||||
| 
 | ||||
|   def display(): Unit = { | ||||
|     val rs: ResultSet = stmt.executeQuery("SELECT * FROM challenge") | ||||
|     println("token\t\tid\t\tsecret\t\tattempted") | ||||
|     while(rs.next()) { | ||||
|       val token = rs.getInt("token") | ||||
|       val id = rs.getString("id") | ||||
|       val secret = rs.getString("secret") | ||||
|       val attempted = rs.getString("attempted") | ||||
|       println(s"${token}\t\t${id}\t\t${secret}\t\t${attempted}\n\n") | ||||
|     } | ||||
| 
 | ||||
|     val rss: ResultSet = stmt.executeQuery("SELECT * FROM mapId") | ||||
|     println("uuid\t\ttoken\t\tlastServed") | ||||
|     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") | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| object LCFramework{ | ||||
|   def main(args: scala.Array[String]) { | ||||
|     val dbConn = new DBConn() | ||||
|     Statements.dbConn = dbConn | ||||
|   	val captcha = new Captcha(2, dbConn) | ||||
|     val server = new Server(8888, captcha, dbConn) | ||||
|     captcha.beginThread(2) | ||||
|   	val captcha = new Captcha() | ||||
|     val server = new Server(8888, captcha) | ||||
|     val backgroudTask = new BackgroundTask(captcha, 10) | ||||
|     backgroudTask.beginThread(2) | ||||
|     server.start() | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										38
									
								
								src/main/scala/lc/background/taskThread.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/main/scala/lc/background/taskThread.scala
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| package lc.background | ||||
| 
 | ||||
| import lc.database.Statements | ||||
| 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 { | ||||
| 
 | ||||
|                 val mapIdGCPstmt = Statements.tlStmts.get.mapIdGCPstmt | ||||
|                 mapIdGCPstmt.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) } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   def beginThread(delay: Int) : Unit = { | ||||
|     val ex = new ScheduledThreadPoolExecutor(1) | ||||
|     val thread = ex.scheduleWithFixedDelay(task, 1, delay, TimeUnit.SECONDS) | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| @ -1,10 +1,13 @@ | ||||
| package lc | ||||
| 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" | ||||
| @ -1,4 +1,4 @@ | ||||
| package lc | ||||
| package lc.captchas | ||||
| 
 | ||||
| import java.io.File | ||||
| import java.io.ByteArrayOutputStream | ||||
| @ -7,6 +7,8 @@ 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 | ||||
| @ -1,4 +1,4 @@ | ||||
| package lc | ||||
| package lc.captchas | ||||
| 
 | ||||
| import java.awt.image.BufferedImage | ||||
| import java.awt.RenderingHints | ||||
| @ -9,6 +9,9 @@ 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 | ||||
							
								
								
									
										121
									
								
								src/main/scala/lc/core/captcha.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								src/main/scala/lc/core/captcha.scala
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | ||||
| package lc.core | ||||
| 
 | ||||
| import org.json4s.JsonAST.JValue | ||||
| import java.sql.{Blob, ResultSet} | ||||
| import java.util.UUID | ||||
| import java.io.ByteArrayInputStream | ||||
| import lc.database.Statements | ||||
| import lc.core.CaptchaProviders | ||||
| 
 | ||||
| class Captcha { | ||||
| 
 | ||||
|   def getCaptcha(id: Id): Array[Byte] = { | ||||
|     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()){ | ||||
|         blob = rs.getBlob("image") | ||||
|         if(blob != null){ | ||||
|     		  image =  blob.getBytes(1, blob.length().toInt) | ||||
|         } | ||||
|       } | ||||
|     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() | ||||
|     val challenge = provider.returnChallenge() | ||||
|     val blob = new ByteArrayInputStream(challenge.content) | ||||
|     val insertPstmt = Statements.tlStmts.get.insertPstmt | ||||
|     insertPstmt.setString(1, provider.getId) | ||||
|     insertPstmt.setString(2, challenge.secret) | ||||
|     insertPstmt.setString(3, providerId) | ||||
|     insertPstmt.setString(4, challenge.contentType) | ||||
|     insertPstmt.setBlob(5, blob) | ||||
|     insertPstmt.executeUpdate() | ||||
|     val rs: ResultSet = insertPstmt.getGeneratedKeys() | ||||
|     val token = if(rs.next()){ | ||||
|       rs.getInt("token") | ||||
|     } | ||||
|     println("Added new challenge: "+ token.toString) | ||||
|     token.asInstanceOf[Int] | ||||
|   } | ||||
| 
 | ||||
|   def getChallenge(param: Parameters): Id = { | ||||
|     try { | ||||
|       val tokenPstmt = Statements.tlStmts.get.tokenPstmt | ||||
|       val rs = tokenPstmt.executeQuery() | ||||
|       val tokenOpt = if(rs.next()) { | ||||
|         Some(rs.getInt("token")) | ||||
|       } else { | ||||
|         None | ||||
|       } | ||||
|       val updateAttemptedPstmt = Statements.tlStmts.get.updateAttemptedPstmt | ||||
|       val uuid = getUUID(tokenOpt.getOrElse(generateChallenge(param))) | ||||
|       updateAttemptedPstmt.setString(1, uuid) | ||||
|       updateAttemptedPstmt.executeUpdate() | ||||
|       Id(uuid) | ||||
|     } 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.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) | ||||
|   } | ||||
| 
 | ||||
|   def display(): Unit = { | ||||
|     val rs: ResultSet = Statements.tlStmts.get.getChallengeTable.executeQuery() | ||||
|     println("token\t\tid\t\tsecret\t\tattempted") | ||||
|     while(rs.next()) { | ||||
|       val token = rs.getInt("token") | ||||
|       val id = rs.getString("id") | ||||
|       val secret = rs.getString("secret") | ||||
|       val attempted = rs.getString("attempted") | ||||
|       println(s"${token}\t\t${id}\t\t${secret}\t\t${attempted}\n\n") | ||||
|     } | ||||
| 
 | ||||
|     val rss: ResultSet = Statements.tlStmts.get.getMapIdTable.executeQuery() | ||||
|     println("uuid\t\ttoken\t\tlastServed") | ||||
|     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") | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										38
									
								
								src/main/scala/lc/core/captchaProviders.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/main/scala/lc/core/captchaProviders.scala
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| package lc.core | ||||
| 
 | ||||
| import lc.captchas._ | ||||
| import lc.captchas.interfaces.ChallengeProvider | ||||
| 
 | ||||
| object CaptchaProviders { | ||||
|   private 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()) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   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) | ||||
|   } | ||||
| 
 | ||||
|   def getProviderById(id: String): ChallengeProvider = { | ||||
|     return providers(id) | ||||
|   } | ||||
|    | ||||
|   def getProvider(): ChallengeProvider = { | ||||
|     val keys = providers.keys | ||||
|     val providerIndex = keys.toVector(getNextRandomInt(keys.size)) | ||||
|     providers(providerIndex) | ||||
|   } | ||||
| } | ||||
							
								
								
									
										7
									
								
								src/main/scala/lc/core/models.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/main/scala/lc/core/models.scala
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| package lc.core | ||||
| 
 | ||||
| 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) | ||||
| @ -1,4 +1,4 @@ | ||||
| package lc | ||||
| package lc.database | ||||
| 
 | ||||
| import java.sql._ | ||||
| 
 | ||||
							
								
								
									
										31
									
								
								src/main/scala/lc/database/statements.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/main/scala/lc/database/statements.scala
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| package lc.database | ||||
| 
 | ||||
| import lc.database.DBConn | ||||
| import java.sql.Statement | ||||
| 
 | ||||
| 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)") | ||||
| 
 | ||||
|   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 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)) | ||||
| } | ||||
| @ -1,15 +1,14 @@ | ||||
| package lc | ||||
| package lc.server | ||||
| 
 | ||||
| import java.io.File | ||||
| import org.json4s._ | ||||
| import org.json4s.jackson.JsonMethods._ | ||||
| import org.json4s.jackson.Serialization.{read, write} | ||||
| import org.json4s.DefaultFormats | ||||
| import org.json4s.jackson.JsonMethods.parse | ||||
| import org.json4s.jackson.Serialization.write | ||||
| import lc.core.Captcha | ||||
| import lc.core.{Parameters, Id, Answer} | ||||
| import lc.server.HTTPServer | ||||
| 
 | ||||
| import lc.HTTPServer._ | ||||
| 
 | ||||
| case class Secret(token: Int) | ||||
| 
 | ||||
| class Server(port: Int, captcha: Captcha, dbConn: DBConn){ | ||||
| class Server(port: Int, captcha: Captcha){ | ||||
| 	val server = new HTTPServer(port) | ||||
| 	val host = server.getVirtualHost(null) | ||||
| 
 | ||||
| @ -40,8 +39,7 @@ class Server(port: Int, captcha: Captcha, dbConn: DBConn){ | ||||
|     	val answer = json.extract[Answer] | ||||
|     	val result = captcha.checkAnswer(answer) | ||||
|     	resp.getHeaders().add("Content-Type","application/json") | ||||
|     	val responseContent = s"""{"result":"$result"}""" | ||||
|     	resp.send(200,responseContent) | ||||
| 		resp.send(200, write(result)) | ||||
|     	0 | ||||
|     },"POST") | ||||
| 
 | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user