Compare commits
215 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a53903a757 | ||
|
|
12ddf59bd1 | ||
|
|
5a5c9a73d9 | ||
|
|
a8f50f6098 | ||
|
|
ab1387814c | ||
|
|
f0d2ddda44 | ||
|
|
2fefc62ba2 | ||
|
|
2ec93c4b95 | ||
|
|
f60cb8dd05 | ||
|
|
5c73a0fea0 | ||
|
|
45299fb8eb | ||
|
|
3c6bbe8c9f | ||
|
|
e122976878 | ||
|
|
f702a22dcd | ||
|
|
b9bd305599 | ||
|
|
f4737424f7 | ||
|
|
355e8c553a | ||
|
|
777be7cf08 | ||
|
|
c7c89a6396 | ||
|
|
471a5828cd | ||
|
|
c7327f07ee | ||
|
|
aa31b2d9cf | ||
|
|
592a85257e | ||
|
|
5c3160157a | ||
|
|
24ac9c5ee7 | ||
|
|
6cf0b322fc | ||
|
|
967145b73b | ||
|
|
332aac57f0 | ||
|
|
f6e0130a53 | ||
|
|
20f32bdabb | ||
|
|
660d18d6f2 | ||
|
|
b786330bff | ||
|
|
dbb675b975 | ||
|
|
220eec02b0 | ||
|
|
3e822ef02a | ||
|
|
1c273c1996 | ||
|
|
7887622040 | ||
|
|
bf303108aa | ||
|
|
cc7e1232f2 | ||
|
|
a9b148c1a9 | ||
|
|
13459fbf21 | ||
|
|
fa366be315 | ||
|
|
9e39f6d293 | ||
|
|
f0050494a1 | ||
|
|
52156b1677 | ||
|
|
4749486112 | ||
|
|
7c7d7f6633 | ||
|
|
17db5767f4 | ||
|
|
3d7b7e8bd9 | ||
|
|
1991375d6b | ||
|
|
b77bd94ae6 | ||
|
|
b14415add5 | ||
|
|
ac44980f50 | ||
|
|
fba5b10e77 | ||
|
|
c4515d3f77 | ||
|
|
7c394b4359 | ||
|
|
6db9934bec | ||
|
|
39b776fde8 | ||
|
|
4c3a7ad0fa | ||
|
|
7f70d60dbb | ||
|
|
4d79567227 | ||
|
|
f6b7278ed1 | ||
|
|
984bbacb69 | ||
|
|
0dd00f5d3e | ||
|
|
d355333bd4 | ||
|
|
55064c8532 | ||
|
|
1742772de1 | ||
|
|
1234bd4acb | ||
|
|
df82593d90 | ||
|
|
b782ded765 | ||
|
|
aa6a6a1015 | ||
|
|
157054d9d8 | ||
|
|
daa8903933 | ||
|
|
de3a2bad6f | ||
|
|
121b190c32 | ||
|
|
b95350b1e2 | ||
|
|
26bc159342 | ||
|
|
476ef499e3 | ||
|
|
3b925adfde | ||
|
|
16a8282206 | ||
|
|
4cedff874d | ||
|
|
d8360c6192 | ||
|
|
e81e8a77e8 | ||
|
|
c045682708 | ||
|
|
414c07beaf | ||
|
|
aa6607237a | ||
|
|
931ab0a086 | ||
|
|
bc4acb1bae | ||
|
|
2dc2066ad8 | ||
|
|
bc8c6ad227 | ||
|
|
be0612b94d | ||
|
|
fe2315fc89 | ||
|
|
1696050702 | ||
|
|
5ddfab95d1 | ||
|
|
15c95747e8 | ||
|
|
12bb1dd1e7 | ||
|
|
1de046362d | ||
|
|
f9a089bed1 | ||
|
|
caac0552c3 | ||
|
|
1701a497b6 | ||
|
|
ecabb6baae | ||
|
|
065748e339 | ||
|
|
707d02a71f | ||
|
|
a69bd1afaf | ||
|
|
f0f62eb4fc | ||
|
|
fbaa0f24a8 | ||
|
|
ddd4054db1 | ||
|
|
66c93c5069 | ||
|
|
75ac7b2f88 | ||
|
|
367eef011f | ||
|
|
8be4ad2be8 | ||
|
|
f7568ffd5f | ||
|
|
061dc82c68 | ||
|
|
6ef321c418 | ||
|
|
d3048b3b4a | ||
|
|
33fbd9a49b | ||
|
|
f9e626415d | ||
|
|
ddb333a1df | ||
|
|
62c0c5c363 | ||
|
|
cff297a48f | ||
|
|
10ca28ba07 | ||
|
|
afab1b4803 | ||
|
|
205e652e1e | ||
|
|
03ac5e5e83 | ||
|
|
ce71407d74 | ||
|
|
2a33fe1e90 | ||
|
|
9399adf2c3 | ||
|
|
ef19bbc0c1 | ||
|
|
1c95665f67 | ||
|
|
6bb9fbd7ea | ||
|
|
6e6daf6c36 | ||
|
|
daa6c49f8a | ||
|
|
cb9b0d361b | ||
|
|
ae9163d106 | ||
|
|
ba21b133fc | ||
|
|
3b0b464109 | ||
|
|
1663e2b306 | ||
|
|
d77bc658a0 | ||
|
|
33ad07c768 | ||
|
|
beccf4dff6 | ||
|
|
e2688f7e51 | ||
|
|
ef4d1690e1 | ||
|
|
43291222a2 | ||
|
|
044d39ad44 | ||
|
|
b0f75677c9 | ||
|
|
cda8bf6aa5 | ||
|
|
57b43e0f31 | ||
|
|
bfa2cfdc88 | ||
|
|
5cbb23b714 | ||
|
|
a1739e5288 | ||
|
|
d5f7fdcad1 | ||
|
|
5950e42973 | ||
|
|
59934539a7 | ||
|
|
399ee06b62 | ||
|
|
5d7ccbb1c5 | ||
|
|
25fb901e9e | ||
|
|
dd48e9e4e7 | ||
|
|
43cb991e16 | ||
|
|
737ebd76ae | ||
|
|
30c978ea1c | ||
|
|
7721df414b | ||
|
|
901759e06e | ||
|
|
2dab3e0638 | ||
|
|
f2b19baca8 | ||
|
|
56b42801d9 | ||
|
|
448bb5fe9f | ||
|
|
fadfffafb0 | ||
|
|
f7d38f5f88 | ||
|
|
85cfdf642c | ||
|
|
7dea087e2d | ||
|
|
f7d03ac5cf | ||
|
|
a40b33d492 | ||
|
|
6eb38929d2 | ||
|
|
a88068b865 | ||
|
|
5e9c9e2d5d | ||
|
|
f9ae1e3970 | ||
|
|
57ce691a00 | ||
|
|
55e68d23f4 | ||
|
|
b27bcc8e52 | ||
|
|
6ce5bff8c5 | ||
|
|
9ef3d162f2 | ||
|
|
515782ea8b | ||
|
|
70ca5673d1 | ||
|
|
2a092b777e | ||
|
|
a2fc659eda | ||
|
|
a020426329 | ||
|
|
907210f229 | ||
|
|
58c6e96dc9 | ||
|
|
7beecd7b80 | ||
|
|
2f18edef10 | ||
|
|
76dcd1b41e | ||
|
|
32ac8f5baa | ||
|
|
5794693f51 | ||
|
|
c02f284cc9 | ||
|
|
055525bd74 | ||
|
|
dc5d9d86ff | ||
|
|
ffe3142fbf | ||
|
|
5537d907a1 | ||
|
|
15dfc1576e | ||
|
|
603e6eda22 | ||
|
|
3532f2ec29 | ||
|
|
0a4ca07ed4 | ||
|
|
95a7ee1710 | ||
|
|
aa484e4bc0 | ||
|
|
0549d9cfcf | ||
|
|
94dddd99c0 | ||
|
|
353ecaa1eb | ||
|
|
b402d34be8 | ||
|
|
37d840b365 | ||
|
|
a9b630724d | ||
|
|
b50df7ec2f | ||
|
|
b3c0a83594 | ||
|
|
35909261ce | ||
|
|
073740b98b | ||
|
|
c7726edc1c |
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Scala Steward: Reformat with sbt-java-formatter 0.8.0
|
||||||
|
57ce691a00babb03e0cae03a26fe56d63fc609af
|
||||||
|
|
||||||
|
# Scala Steward: Reformat with scalafmt 3.6.1
|
||||||
|
f2b19baca828a4d88b46bc009aef6d7115e63924
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# If true, Scala Steward will sign off all commits (e.g. `git --signoff`).
|
||||||
|
# Default: false
|
||||||
|
signoffCommits = true
|
||||||
|
|
@ -16,6 +16,7 @@ jobs:
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 1.11
|
java-version: 1.11
|
||||||
|
- uses: sbt/setup-sbt@v1
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: sbt test assembly
|
run: sbt test assembly
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,10 @@ jobs:
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
platforms: |
|
||||||
|
linux/amd64
|
||||||
|
linux/arm64
|
||||||
|
linux/arm/v7
|
||||||
|
|
||||||
- name: Image digest
|
- name: Image digest
|
||||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version="3.5.0"
|
version="3.8.3"
|
||||||
maxColumn = 120
|
maxColumn = 120
|
||||||
runner.dialect = scala3
|
runner.dialect = scala3
|
||||||
|
|
|
||||||
13
Dockerfile
13
Dockerfile
|
|
@ -1,9 +1,10 @@
|
||||||
FROM adoptopenjdk/openjdk16:alpine AS base-builder
|
FROM eclipse-temurin:17-jre-jammy AS base-builder
|
||||||
ARG SBT_VERSION=1.6.2
|
ARG SBT_VERSION=1.7.1
|
||||||
RUN apk add --no-cache bash
|
|
||||||
ENV JAVA_HOME="/usr/lib/jvm/default-jvm/"
|
ENV JAVA_HOME="/usr/lib/jvm/default-jvm/"
|
||||||
ENV PATH=$PATH:${JAVA_HOME}/bin
|
ENV PATH=$PATH:${JAVA_HOME}/bin
|
||||||
RUN \
|
RUN \
|
||||||
|
apt update && \
|
||||||
|
apt install -y wget && \
|
||||||
wget -O sbt-$SBT_VERSION.tgz https://github.com/sbt/sbt/releases/download/v$SBT_VERSION/sbt-$SBT_VERSION.tgz && \
|
wget -O sbt-$SBT_VERSION.tgz https://github.com/sbt/sbt/releases/download/v$SBT_VERSION/sbt-$SBT_VERSION.tgz && \
|
||||||
tar -xzvf sbt-$SBT_VERSION.tgz && \
|
tar -xzvf sbt-$SBT_VERSION.tgz && \
|
||||||
rm sbt-$SBT_VERSION.tgz
|
rm sbt-$SBT_VERSION.tgz
|
||||||
|
|
@ -22,15 +23,15 @@ FROM sbt-builder as builder
|
||||||
COPY src/ src/
|
COPY src/ src/
|
||||||
RUN sbt assembly
|
RUN sbt assembly
|
||||||
|
|
||||||
FROM adoptopenjdk/openjdk16:alpine-jre AS base-core
|
FROM eclipse-temurin:17-jre-jammy AS base-core
|
||||||
ENV JAVA_HOME="/usr/lib/jvm/default-jvm/"
|
ENV JAVA_HOME="/usr/lib/jvm/default-jvm/"
|
||||||
RUN apk add --update ttf-dejavu
|
RUN apt update && apt install -y fonts-dejavu
|
||||||
ENV PATH=$PATH:${JAVA_HOME}/bin
|
ENV PATH=$PATH:${JAVA_HOME}/bin
|
||||||
|
|
||||||
|
|
||||||
FROM base-core
|
FROM base-core
|
||||||
WORKDIR /lc-core
|
WORKDIR /lc-core
|
||||||
COPY --from=builder /build/target/scala-3.1.1/LibreCaptcha.jar .
|
COPY --from=builder /build/target/scala-3.6.2/LibreCaptcha.jar .
|
||||||
RUN mkdir data/
|
RUN mkdir data/
|
||||||
|
|
||||||
EXPOSE 8888
|
EXPOSE 8888
|
||||||
|
|
|
||||||
11
README.md
11
README.md
|
|
@ -47,18 +47,25 @@ docker-compose up
|
||||||
Using `docker`:
|
Using `docker`:
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run -v lcdata:/lc-core/data librecaptcha/lc-core:1.1.0-stable
|
docker run -p=8888:8888 -v ./lcdata:/lc-core/data librecaptcha/lc-core:2.0
|
||||||
```
|
```
|
||||||
|
|
||||||
A default `config.json` is automatically created in the mounted volume.
|
A default `config.json` is automatically created in the mounted volume.
|
||||||
|
|
||||||
|
The above commands should work with `podman` as well, if docker.io registry is pre-configured. Otherwise,
|
||||||
|
you can manually specify the repository like so:
|
||||||
|
|
||||||
|
```
|
||||||
|
podman run -p=8888:8888 -v ./lcdata:/lc-core/data docker.io/librecaptcha/lc-core:2.0
|
||||||
|
```
|
||||||
|
|
||||||
## Quick test
|
## Quick test
|
||||||
Open [localhost:8888/demo/index.html](http://localhost:8888/demo/index.html) in browser.
|
Open [localhost:8888/demo/index.html](http://localhost:8888/demo/index.html) in browser.
|
||||||
|
|
||||||
Alternatively, on the command line, try:
|
Alternatively, on the command line, try:
|
||||||
|
|
||||||
```
|
```
|
||||||
> $ curl -d '{"media":"image/png","level":"easy","input_type":"text"}' localhost:8888/v1/captcha
|
> $ curl -d '{"media":"image/png","level":"easy","input_type":"text","size":"350x100"}' localhost:8888/v2/captcha
|
||||||
{"id":"3bf928ce-a1e7-4616-b34f-8252d777855d"}
|
{"id":"3bf928ce-a1e7-4616-b34f-8252d777855d"}
|
||||||
|
|
||||||
> $ curl "localhost:8888/v1/media?id=3bf928ce-a1e7-4616-b34f-8252d777855d" -o sample.png
|
> $ curl "localhost:8888/v1/media?id=3bf928ce-a1e7-4616-b34f-8252d777855d" -o sample.png
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
FROM adoptopenjdk/openjdk16:alpine-jre AS base-core
|
FROM eclipse-temurin:17-jre-jammy AS base-core
|
||||||
ENV JAVA_HOME="/usr/lib/jvm/default-jvm/"
|
ENV JAVA_HOME="/usr/lib/jvm/default-jvm/"
|
||||||
RUN apk add --update ttf-dejavu
|
RUN apt update && apt install -y fonts-dejavu
|
||||||
ENV PATH=$PATH:${JAVA_HOME}/bin
|
ENV PATH=$PATH:${JAVA_HOME}/bin
|
||||||
|
|
||||||
|
|
||||||
FROM base-core
|
FROM base-core
|
||||||
RUN mkdir /lc-core
|
RUN mkdir /lc-core
|
||||||
COPY target/scala-3.1.1/LibreCaptcha.jar /lc-core
|
COPY target/scala-3.6.2/LibreCaptcha.jar /lc-core
|
||||||
WORKDIR /lc-core
|
WORKDIR /lc-core
|
||||||
RUN mkdir data/
|
RUN mkdir data/
|
||||||
|
|
||||||
|
|
|
||||||
10
build.sbt
10
build.sbt
|
|
@ -2,8 +2,8 @@ lazy val root = (project in file(".")).settings(
|
||||||
inThisBuild(
|
inThisBuild(
|
||||||
List(
|
List(
|
||||||
organization := "com.example",
|
organization := "com.example",
|
||||||
scalaVersion := "3.1.1",
|
scalaVersion := "3.6.2",
|
||||||
version := "0.2.0-beta",
|
version := "0.2.1-snapshot",
|
||||||
semanticdbEnabled := true,
|
semanticdbEnabled := true,
|
||||||
semanticdbVersion := scalafixSemanticdb.revision
|
semanticdbVersion := scalafixSemanticdb.revision
|
||||||
|
|
||||||
|
|
@ -12,9 +12,9 @@ lazy val root = (project in file(".")).settings(
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
name := "LibreCaptcha",
|
name := "LibreCaptcha",
|
||||||
libraryDependencies += "com.sksamuel.scrimage" % "scrimage-core" % "4.0.31",
|
libraryDependencies += "com.sksamuel.scrimage" % "scrimage-core" % "4.3.0",
|
||||||
libraryDependencies += "com.sksamuel.scrimage" % "scrimage-filters" % "4.0.31",
|
libraryDependencies += "com.sksamuel.scrimage" % "scrimage-filters" % "4.3.0",
|
||||||
libraryDependencies += "org.json4s" %% "json4s-jackson" % "4.0.5"
|
libraryDependencies += "org.json4s" %% "json4s-jackson" % "4.0.7"
|
||||||
)
|
)
|
||||||
|
|
||||||
Compile / unmanagedResourceDirectories += { baseDirectory.value / "lib" }
|
Compile / unmanagedResourceDirectories += { baseDirectory.value / "lib" }
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1 +1 @@
|
||||||
sbt.version=1.6.2
|
sbt.version=1.10.6
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.34")
|
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.13.0")
|
||||||
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6")
|
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2")
|
||||||
addSbtPlugin("com.lightbend.sbt" % "sbt-java-formatter" % "0.7.0")
|
addSbtPlugin("com.lightbend.sbt" % "sbt-java-formatter" % "0.8.0")
|
||||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0")
|
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.0")
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FilenameFilter;
|
import java.io.FilenameFilter;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import lc.captchas.interfaces.Challenge;
|
import lc.captchas.interfaces.Challenge;
|
||||||
import lc.captchas.interfaces.ChallengeProvider;
|
import lc.captchas.interfaces.ChallengeProvider;
|
||||||
import lc.misc.PngImageWriter;
|
|
||||||
import lc.misc.HelperFunctions;
|
import lc.misc.HelperFunctions;
|
||||||
|
import lc.misc.PngImageWriter;
|
||||||
|
|
||||||
public class FontFunCaptcha implements ChallengeProvider {
|
public class FontFunCaptcha implements ChallengeProvider {
|
||||||
|
|
||||||
|
|
@ -58,7 +58,8 @@ public class FontFunCaptcha implements ChallengeProvider {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] fontFun(final int width, final int height, String captchaText, String level, String path) {
|
private byte[] fontFun(
|
||||||
|
final int width, final int height, String captchaText, String level, String path) {
|
||||||
String[] colors = {"#f68787", "#f8a978", "#f1eb9a", "#a4f6a5"};
|
String[] colors = {"#f68787", "#f8a978", "#f1eb9a", "#a4f6a5"};
|
||||||
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||||
Graphics2D graphics2D = img.createGraphics();
|
Graphics2D graphics2D = img.createGraphics();
|
||||||
|
|
@ -87,7 +88,8 @@ public class FontFunCaptcha implements ChallengeProvider {
|
||||||
final int width = size2D[0];
|
final int width = size2D[0];
|
||||||
final int height = size2D[1];
|
final int height = size2D[1];
|
||||||
String path = "./lib/fonts/";
|
String path = "./lib/fonts/";
|
||||||
return new Challenge(fontFun(width, height, secret, "medium", path), "image/png", secret.toLowerCase());
|
return new Challenge(
|
||||||
|
fontFun(width, height, secret, "medium", path), "image/png", secret.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkAnswer(String secret, String answer) {
|
public boolean checkAnswer(String secret, String answer) {
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,26 @@
|
||||||
package lc.captchas;
|
package lc.captchas;
|
||||||
|
|
||||||
import java.awt.Font;
|
|
||||||
import java.awt.RenderingHints;
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
import java.awt.Font;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.RenderingHints;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import lc.captchas.interfaces.Challenge;
|
import lc.captchas.interfaces.Challenge;
|
||||||
import lc.captchas.interfaces.ChallengeProvider;
|
import lc.captchas.interfaces.ChallengeProvider;
|
||||||
import lc.misc.HelperFunctions;
|
|
||||||
import lc.misc.GifSequenceWriter;
|
import lc.misc.GifSequenceWriter;
|
||||||
|
import lc.misc.HelperFunctions;
|
||||||
|
|
||||||
public class PoppingCharactersCaptcha implements ChallengeProvider {
|
public class PoppingCharactersCaptcha implements ChallengeProvider {
|
||||||
|
|
||||||
private int[] computeOffsets(final Font font, final int width, final int height, final String text) {
|
private int[] computeOffsets(
|
||||||
|
final Font font, final int width, final int height, final String text) {
|
||||||
final var img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
final var img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||||
final var graphics2D = img.createGraphics();
|
final var graphics2D = img.createGraphics();
|
||||||
final var frc = graphics2D.getFontRenderContext();
|
final var frc = graphics2D.getFontRenderContext();
|
||||||
|
|
@ -32,17 +32,20 @@ public class PoppingCharactersCaptcha implements ChallengeProvider {
|
||||||
advances[i] = currX;
|
advances[i] = currX;
|
||||||
currX += font.getStringBounds(String.valueOf(c), frc).getWidth();
|
currX += font.getStringBounds(String.valueOf(c), frc).getWidth();
|
||||||
currX += spacing;
|
currX += spacing;
|
||||||
};
|
}
|
||||||
|
;
|
||||||
advances[text.length()] = currX;
|
advances[text.length()] = currX;
|
||||||
graphics2D.dispose();
|
graphics2D.dispose();
|
||||||
return advances;
|
return advances;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BufferedImage makeImage(final Font font, final int width, final int height, final Consumer<Graphics2D> f) {
|
private BufferedImage makeImage(
|
||||||
|
final Font font, final int width, final int height, final Consumer<Graphics2D> f) {
|
||||||
final var img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
final var img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||||
final var graphics2D = img.createGraphics();
|
final var 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(
|
||||||
|
RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||||
graphics2D.setFont(font);
|
graphics2D.setFont(font);
|
||||||
f.accept(graphics2D);
|
f.accept(graphics2D);
|
||||||
graphics2D.dispose();
|
graphics2D.dispose();
|
||||||
|
|
@ -64,24 +67,38 @@ public class PoppingCharactersCaptcha implements ChallengeProvider {
|
||||||
final var expectedWidth = advances[advances.length - 1];
|
final var expectedWidth = advances[advances.length - 1];
|
||||||
final var scale = width / (float) expectedWidth;
|
final var scale = width / (float) expectedWidth;
|
||||||
final var prevColor = Color.getHSBColor(0f, 0f, 0.1f);
|
final var prevColor = Color.getHSBColor(0f, 0f, 0.1f);
|
||||||
IntStream.range(0, text.length()).forEach(i -> {
|
IntStream.range(0, text.length())
|
||||||
final var color = Color.getHSBColor(HelperFunctions.randomNumber(0, 100)/100.0f, 0.6f, 1.0f);
|
.forEach(
|
||||||
final var nextImage = makeImage(font, width, height, (g) -> {
|
i -> {
|
||||||
g.scale(scale, 1);
|
final var color =
|
||||||
if (i > 0) {
|
Color.getHSBColor(HelperFunctions.randomNumber(0, 100) / 100.0f, 0.6f, 1.0f);
|
||||||
final var prevI = (i - 1) % text.length();
|
final var nextImage =
|
||||||
g.setColor(prevColor);
|
makeImage(
|
||||||
g.drawString(String.valueOf(text.charAt(prevI)), advances[prevI] + jitter(), fontHeight*1.1f + jitter());
|
font,
|
||||||
}
|
width,
|
||||||
g.setColor(color);
|
height,
|
||||||
g.drawString(String.valueOf(text.charAt(i)), advances[i] + jitter(), fontHeight*1.1f + jitter());
|
(g) -> {
|
||||||
});
|
g.scale(scale, 1);
|
||||||
try {
|
if (i > 0) {
|
||||||
writer.writeToSequence(nextImage);
|
final var prevI = (i - 1) % text.length();
|
||||||
} catch (final IOException e) {
|
g.setColor(prevColor);
|
||||||
e.printStackTrace();
|
g.drawString(
|
||||||
}
|
String.valueOf(text.charAt(prevI)),
|
||||||
});
|
advances[prevI] + jitter(),
|
||||||
|
fontHeight * 1.1f + jitter());
|
||||||
|
}
|
||||||
|
g.setColor(color);
|
||||||
|
g.drawString(
|
||||||
|
String.valueOf(text.charAt(i)),
|
||||||
|
advances[i] + jitter(),
|
||||||
|
fontHeight * 1.1f + jitter());
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
writer.writeToSequence(nextImage);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
writer.close();
|
writer.close();
|
||||||
output.close();
|
output.close();
|
||||||
return byteArrayOutputStream.toByteArray();
|
return byteArrayOutputStream.toByteArray();
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,18 @@
|
||||||
package lc.captchas;
|
package lc.captchas;
|
||||||
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.RenderingHints;
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.ConvolveOp;
|
import java.awt.image.ConvolveOp;
|
||||||
import java.awt.image.Kernel;
|
import java.awt.image.Kernel;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import lc.misc.HelperFunctions;
|
|
||||||
import lc.misc.PngImageWriter;
|
|
||||||
import lc.captchas.interfaces.Challenge;
|
import lc.captchas.interfaces.Challenge;
|
||||||
import lc.captchas.interfaces.ChallengeProvider;
|
import lc.captchas.interfaces.ChallengeProvider;
|
||||||
|
import lc.misc.HelperFunctions;
|
||||||
|
import lc.misc.PngImageWriter;
|
||||||
|
|
||||||
public class ShadowTextCaptcha implements ChallengeProvider {
|
public class ShadowTextCaptcha implements ChallengeProvider {
|
||||||
|
|
||||||
|
|
@ -56,20 +54,24 @@ public class ShadowTextCaptcha implements ChallengeProvider {
|
||||||
graphics2D.setPaint(Color.BLACK);
|
graphics2D.setPaint(Color.BLACK);
|
||||||
graphics2D.setFont(font);
|
graphics2D.setFont(font);
|
||||||
final var stringWidth = graphics2D.getFontMetrics().stringWidth(text);
|
final var stringWidth = graphics2D.getFontMetrics().stringWidth(text);
|
||||||
final var padding = (stringWidth > width) ? 0 : (width - stringWidth)/2;
|
final var padding = (stringWidth > width) ? 0 : (width - stringWidth) / 2;
|
||||||
final var scaleX = (stringWidth > width) ? width/((double) stringWidth) : 1d;
|
final var scaleX = (stringWidth > width) ? width / ((double) stringWidth) : 1d;
|
||||||
graphics2D.scale(scaleX, 1d);
|
graphics2D.scale(scaleX, 1d);
|
||||||
graphics2D.drawString(text, padding, fontHeight*1.1f);
|
graphics2D.drawString(text, padding, fontHeight * 1.1f);
|
||||||
graphics2D.dispose();
|
graphics2D.dispose();
|
||||||
final int kernelSize = (int) Math.ceil((Math.min(width, height) / 50.0));
|
final int kernelSize = (int) Math.ceil((Math.min(width, height) / 50.0));
|
||||||
ConvolveOp op = new ConvolveOp(new Kernel(kernelSize, kernelSize, makeKernel(kernelSize)), ConvolveOp.EDGE_NO_OP, null);
|
ConvolveOp op =
|
||||||
|
new ConvolveOp(
|
||||||
|
new Kernel(kernelSize, kernelSize, makeKernel(kernelSize)),
|
||||||
|
ConvolveOp.EDGE_NO_OP,
|
||||||
|
null);
|
||||||
BufferedImage img2 = op.filter(img, null);
|
BufferedImage img2 = op.filter(img, null);
|
||||||
Graphics2D g2d = img2.createGraphics();
|
Graphics2D g2d = img2.createGraphics();
|
||||||
HelperFunctions.setRenderingHints(g2d);
|
HelperFunctions.setRenderingHints(g2d);
|
||||||
g2d.setPaint(Color.WHITE);
|
g2d.setPaint(Color.WHITE);
|
||||||
g2d.scale(scaleX, 1d);
|
g2d.scale(scaleX, 1d);
|
||||||
g2d.setFont(font);
|
g2d.setFont(font);
|
||||||
g2d.drawString(text, padding-kernelSize, fontHeight*1.1f);
|
g2d.drawString(text, padding - kernelSize, fontHeight * 1.1f);
|
||||||
g2d.dispose();
|
g2d.dispose();
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package lc.captchas.interfaces;
|
package lc.captchas.interfaces;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public interface ChallengeProvider {
|
public interface ChallengeProvider {
|
||||||
public String getId();
|
public String getId();
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
|
|
||||||
package lc.misc;
|
package lc.misc;
|
||||||
|
|
||||||
import javax.imageio.*;
|
|
||||||
import javax.imageio.metadata.*;
|
|
||||||
import javax.imageio.stream.*;
|
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import javax.imageio.*;
|
||||||
|
import javax.imageio.metadata.*;
|
||||||
|
import javax.imageio.stream.*;
|
||||||
|
|
||||||
public class GifSequenceWriter {
|
public class GifSequenceWriter {
|
||||||
protected ImageWriter gifWriter;
|
protected ImageWriter gifWriter;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package lc.misc;
|
package lc.misc;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import javax.imageio.IIOImage;
|
import javax.imageio.IIOImage;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.ImageTypeSpecifier;
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
|
@ -13,7 +13,6 @@ import javax.imageio.metadata.IIOInvalidTreeException;
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
import javax.imageio.metadata.IIOMetadataNode;
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
import javax.imageio.stream.ImageOutputStream;
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
|
|
||||||
public class PngImageWriter {
|
public class PngImageWriter {
|
||||||
|
|
||||||
|
|
@ -26,7 +25,8 @@ public class PngImageWriter {
|
||||||
iw.hasNext(); ) {
|
iw.hasNext(); ) {
|
||||||
ImageWriter writer = iw.next();
|
ImageWriter writer = iw.next();
|
||||||
ImageWriteParam writeParam = writer.getDefaultWriteParam();
|
ImageWriteParam writeParam = writer.getDefaultWriteParam();
|
||||||
ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
|
ImageTypeSpecifier typeSpecifier =
|
||||||
|
ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
|
||||||
IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
|
IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
|
||||||
if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
|
if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,21 @@
|
||||||
|
|
||||||
package org.limium.picoserve;
|
package org.limium.picoserve;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
import com.sun.net.httpserver.HttpHandler;
|
||||||
|
import com.sun.net.httpserver.HttpServer;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import com.sun.net.httpserver.HttpServer;
|
import java.util.Arrays;
|
||||||
import com.sun.net.httpserver.HttpHandler;
|
import java.util.LinkedList;
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public final class Server {
|
public final class Server {
|
||||||
private final HttpServer server;
|
private final HttpServer server;
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ class BackgroundTask(config: Config, captchaManager: CaptchaManager) {
|
||||||
(captcha.allowedLevels).flatMap { level =>
|
(captcha.allowedLevels).flatMap { level =>
|
||||||
(captcha.allowedMedia).flatMap { media =>
|
(captcha.allowedMedia).flatMap { media =>
|
||||||
(captcha.allowedInputType).flatMap { inputType =>
|
(captcha.allowedInputType).flatMap { inputType =>
|
||||||
(captcha.allowedSizes).map {size =>
|
(captcha.allowedSizes).map { size =>
|
||||||
Parameters(level, media, inputType, size)
|
Parameters(level, media, inputType, size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,16 +43,16 @@ class FilterChallenge extends ChallengeProvider {
|
||||||
val height = size2D(1)
|
val height = size2D(1)
|
||||||
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()
|
||||||
val fontHeight = (height*0.6).toInt
|
val fontHeight = (height * 0.6).toInt
|
||||||
g.setColor(Color.WHITE)
|
g.setColor(Color.WHITE)
|
||||||
g.fillRect(0, 0, canvas.getWidth, canvas.getHeight)
|
g.fillRect(0, 0, canvas.getWidth, canvas.getHeight)
|
||||||
g.setColor(Color.BLACK)
|
g.setColor(Color.BLACK)
|
||||||
val font = new Font("Serif", Font.BOLD, fontHeight)
|
val font = new Font("Serif", Font.BOLD, fontHeight)
|
||||||
g.setFont(font)
|
g.setFont(font)
|
||||||
val stringWidth = g.getFontMetrics().stringWidth(secret)
|
val stringWidth = g.getFontMetrics().stringWidth(secret)
|
||||||
val scaleX = if (stringWidth > width) width/(stringWidth.toDouble) else 1d
|
val scaleX = if (stringWidth > width) width / (stringWidth.toDouble) else 1d
|
||||||
val margin = if (stringWidth > width) 0 else (width - stringWidth)
|
val margin = if (stringWidth > width) 0 else (width - stringWidth)
|
||||||
val xOffset = (margin*r.nextDouble).toInt
|
val xOffset = (margin * r.nextDouble).toInt
|
||||||
g.scale(scaleX, 1d)
|
g.scale(scaleX, 1d)
|
||||||
g.drawString(secret, xOffset, fontHeight)
|
g.drawString(secret, xOffset, fontHeight)
|
||||||
g.dispose()
|
g.dispose()
|
||||||
|
|
|
||||||
|
|
@ -128,13 +128,13 @@ class RainDropsCP extends ChallengeProvider {
|
||||||
val textX = if (textWidth > width) 0 else ((width - textWidth) / 2)
|
val textX = if (textWidth > width) 0 else ((width - textWidth) / 2)
|
||||||
|
|
||||||
// this will be overlapped by the following text to show the top outline because of the offset
|
// this will be overlapped by the following text to show the top outline because of the offset
|
||||||
val yOffset = (fontHeight*0.01).ceil.toInt
|
val yOffset = (fontHeight * 0.01).ceil.toInt
|
||||||
g.setColor(textHighlightColor)
|
g.setColor(textHighlightColor)
|
||||||
g.drawString(secret, textX, (fontHeight*1.1).toInt - yOffset)
|
g.drawString(secret, textX, (fontHeight * 1.1).toInt - yOffset)
|
||||||
|
|
||||||
// paint the text
|
// paint the text
|
||||||
g.setColor(textColor)
|
g.setColor(textColor)
|
||||||
g.drawString(secret, textX, (fontHeight*1.1).toInt)
|
g.drawString(secret, textX, (fontHeight * 1.1).toInt)
|
||||||
|
|
||||||
g.dispose()
|
g.dispose()
|
||||||
writer.writeToSequence(canvas)
|
writer.writeToSequence(canvas)
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ case class ConfigField(
|
||||||
lazy val captchaExpiryTimeLimitInt: Option[Int] = mapInt(captchaExpiryTimeLimit)
|
lazy val captchaExpiryTimeLimitInt: Option[Int] = mapInt(captchaExpiryTimeLimit)
|
||||||
lazy val threadDelayInt: Option[Int] = mapInt(threadDelay)
|
lazy val threadDelayInt: Option[Int] = mapInt(threadDelay)
|
||||||
lazy val maxAttemptsRatioFloat: Option[Float] = mapFloat(maxAttemptsRatio)
|
lazy val maxAttemptsRatioFloat: Option[Float] = mapFloat(maxAttemptsRatio)
|
||||||
lazy val playgroundEnabledBool: Option[Boolean] = playgroundEnabled.map(_ || true)
|
lazy val playgroundEnabledBool: Option[Boolean] = playgroundEnabled.map(_ || false)
|
||||||
|
|
||||||
private def mapInt(x: Option[Integer]): Option[Int] = {
|
private def mapInt(x: Option[Integer]): Option[Int] = {
|
||||||
x.map(_ + 0)
|
x.map(_ + 0)
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,7 @@ class Server(
|
||||||
new StringResponse(200, str)
|
new StringResponse(200, str)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
println("Playground enabled on /demo/index.html")
|
||||||
}
|
}
|
||||||
|
|
||||||
val server: picoserve.Server = serverBuilder.build()
|
val server: picoserve.Server = serverBuilder.build()
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ python3 -m venv testEnv
|
||||||
source ./testEnv/bin/activate
|
source ./testEnv/bin/activate
|
||||||
pip install locust
|
pip install locust
|
||||||
mkdir -p data/
|
mkdir -p data/
|
||||||
java -jar target/scala-3.1.1/LibreCaptcha.jar &
|
java -jar target/scala-3.6.2/LibreCaptcha.jar &
|
||||||
JAVA_PID=$!
|
JAVA_PID=$!
|
||||||
sleep 4
|
sleep 4
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ echo Run functional test
|
||||||
cp data/config.json data/config.json.bak
|
cp data/config.json data/config.json.bak
|
||||||
cp tests/debug-config.json data/config.json
|
cp tests/debug-config.json data/config.json
|
||||||
|
|
||||||
java -jar target/scala-3.1.1/LibreCaptcha.jar &
|
java -jar target/scala-3.6.2/LibreCaptcha.jar &
|
||||||
JAVA_PID=$!
|
JAVA_PID=$!
|
||||||
sleep 4
|
sleep 4
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue