thalassa/lib/captcha/captcha.c

446 lines
9.6 KiB
C
Raw Normal View History

2026-03-19 01:23:52 +00:00
/*
captcha
Copyright (c) Kopikova Sofia, 2020
-- Slightly modified by Andrey V. Stolyarov to remove some
-- unneeded dependencies and the like.
-- Portions copyright (c) Andrey V. Stolyarov, 2023
-- Segment shuffle logic replaced by Y. Kotov
-- Portions copyright (c) Y. Kotov, 2024
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#if 0
#include <stdio.h>
#endif
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include "lodepng.h"
#include "_letters.c"
#include "captcha.h"
#define NUM 33
#define LENGTH CAPTCHA_STRING_LENGTH
#define SEGS NUM_OF_SEGMENTS
#define LARR 6
#define DEG M_PI/6
#define Y1 22
#define Y2 186
#define WIDTH_ONE 22
#define HEIGHT_ONE 20
#define HEIGHT 200
#ifndef M_PI
#define M_PI 3.14159
#endif
typedef struct
{
int x1, x2;
int sz;
} xpair;
static const unsigned char* const letters[] = {
letter_image_A,
letter_image_B,
letter_image_C,
letter_image_D,
letter_image_E,
letter_image_F,
letter_image_G,
letter_image_H,
letter_image_I,
letter_image_J,
letter_image_K,
letter_image_L,
letter_image_M,
letter_image_N,
letter_image_P,
letter_image_Q,
letter_image_R,
letter_image_S,
letter_image_T,
letter_image_U,
letter_image_V,
letter_image_W,
letter_image_X,
letter_image_Y,
letter_image_Z,
letter_image_2,
letter_image_3,
letter_image_4,
letter_image_5,
letter_image_6,
letter_image_7,
letter_image_8,
letter_image_9
};
static const int letter_sizes[] = {
sizeof(letter_image_A),
sizeof(letter_image_B),
sizeof(letter_image_C),
sizeof(letter_image_D),
sizeof(letter_image_E),
sizeof(letter_image_F),
sizeof(letter_image_G),
sizeof(letter_image_H),
sizeof(letter_image_I),
sizeof(letter_image_J),
sizeof(letter_image_K),
sizeof(letter_image_L),
sizeof(letter_image_M),
sizeof(letter_image_N),
sizeof(letter_image_P),
sizeof(letter_image_Q),
sizeof(letter_image_R),
sizeof(letter_image_S),
sizeof(letter_image_T),
sizeof(letter_image_U),
sizeof(letter_image_V),
sizeof(letter_image_W),
sizeof(letter_image_X),
sizeof(letter_image_Y),
sizeof(letter_image_Z),
sizeof(letter_image_2),
sizeof(letter_image_3),
sizeof(letter_image_4),
sizeof(letter_image_5),
sizeof(letter_image_6),
sizeof(letter_image_7),
sizeof(letter_image_8),
sizeof(letter_image_9)
};
const char used_letters[] = "ABCDEFGHIJKLMNPQRSTUVWXYZ23456789";
#define NUM_OF_USED_LETTERS (sizeof(used_letters)-1)
static unsigned* generate()
{
/* printf("NUM_OF_USED_LETTERS: %d\n", (int)NUM_OF_USED_LETTERS);*/
unsigned x = 0;
/*srand(time(NULL)); too easy to guess! */
unsigned* s = (unsigned*)calloc(LENGTH + 1, sizeof(int));
int i;
for(i = 0; i < LENGTH; i++)
{
x = rand() % NUM_OF_USED_LETTERS;
s[i] = x;
}
s[LENGTH] = '\0';
return s;
}
static int asum(int arr[], int num)
{
int sum = 0;
int i;
for(i = 0; i < num; i++)
{
sum += arr[i];
}
return sum;
}
static void mix(char *ans, unsigned* s, xpair coords[])
{
int len_segs[SEGS];
int i;
for(i = 0; i < SEGS - 1; i++)
{
len_segs[i] = rand() % (LENGTH - (SEGS - i - 1)
- asum(len_segs, i))
+ 1;
coords[i].sz = len_segs[i];
}
len_segs[SEGS - 1] = LENGTH - asum(len_segs, SEGS - 1);
coords[SEGS - 1].sz = len_segs[SEGS - 1];
/* Calculate max order: factorial(num_segs). */
unsigned max_order = 1;
for(i = 2; i < SEGS+1; i++)
max_order *= i;
/* Calculate order of segments excluding the trivial one. */
unsigned order = 1 + rand() % (max_order - 1);
/* Prepare the list of no processed segments.
* The formula is a bit weird just to make order=0 match 0,1,2,3. */
unsigned segs[SEGS];
for (i = 0; i < SEGS; i++)
segs[i] = (SEGS - i) % SEGS;
int cur = 0;
int curc = 0;
int seg = 0;
for(seg = 0; seg < SEGS; seg++)
{
unsigned idx = order % (SEGS - seg);
unsigned n = segs[idx];
segs[idx] = segs[SEGS - seg - 1];
order /= SEGS - seg;
for(i = 0; i < len_segs[n]; i++)
{
int index = s[i + asum(len_segs, n)];
ans[cur] = used_letters[index];
cur++;
}
coords[n].x1 = WIDTH_ONE * asum(len_segs, n)
+ WIDTH_ONE * len_segs[n] / 2;
coords[n].x2 = curc + WIDTH_ONE * len_segs[n] / 2;
curc += WIDTH_ONE * len_segs[n];
}
/* printf("\n");
for (int i = 0; i < SEGS; i++)
{
printf("%d line at coords: %d %d, size: %d\n", i + 1,
coords[i].x1, coords[i].x2, coords[i].sz);
}*/
ans[LENGTH] = '\0';
/*printf("%s\n", ss);*/
}
static void hypotenuse(unsigned char** in, unsigned x1, unsigned y1,
unsigned x2, unsigned y2, unsigned w, unsigned h)
{
int color = (int)(1);
unsigned dx = abs((int)x2 - (int)x1);
unsigned dy = y2 - y1;
double err = 0;
double derr = (double)dx / (double)dy;
double errmax = 0.5;
unsigned x = x1;
unsigned dirx = (x2 > x1) ? 1 : -1;
unsigned y;
for(y = y1; y < y2; y++)
{
size_t byte_index = y * w + x;
(*in)[byte_index] = (unsigned char)(color);
err += derr;
while(err >= errmax)
{
byte_index = y * w + x;
(*in)[byte_index] = (unsigned char)(color);
x += dirx;
err -= 1.0;
}
}
}
static void
arrows(unsigned char** in, unsigned x1, unsigned y1, unsigned x2, unsigned y2,
unsigned w, unsigned h, unsigned l)
{
int x = x1 - x2;
int y = y2 - y1;
double v = sqrt(pow(x, 2) + pow(y, 2));
double dg1 = asin(x / v) + DEG;
double dg2 = dg1 - 2 * DEG;
unsigned x11 = x2 + (int)(l * sin(dg1) + 0.5);
unsigned y11 = y2 - (int)(l * cos(dg1) + 0.5);
unsigned x22 = x2 + (int)(l * sin(dg2) + 0.5);
unsigned y22 = y2 - (int)(l * cos(dg2) + 0.5);
/*printf("drawing arrows to: %d %d %d %d\n", x11, y11, x2, y2);
printf("drawing arrows to: %d %d %d %d\n", x22, y22, x2, y2);*/
hypotenuse(in, x11, y11, x2, y2, w, h);
hypotenuse(in, x22, y22, x2, y2, w, h);
}
static void line(unsigned char** in, unsigned x1, unsigned x2, unsigned y,
unsigned w, unsigned h)
{
int color = (int)(1);
unsigned x;
for(x = x1; x < x2; x++)
{
size_t byte_index = y * w + x;
(*in)[byte_index] = (unsigned char)(color);
}
}
static void rect(unsigned char** in, unsigned x1, unsigned x2,
unsigned w, unsigned h)
{
int color = (int)(125);
unsigned x, y;
for(y = h - 14; y < h - 2; y++)
for(x = x1; x < x2; x++)
{
size_t byte_index = y * w + x;
(*in)[byte_index] = (unsigned char)(color);
}
}
static unsigned char* reunis(const unsigned* str, unsigned* w, unsigned* h)
{
unsigned err;
unsigned char** images2 =
(unsigned char**)malloc(LENGTH * sizeof(size_t));
unsigned width1 = 0, height1 = 0;
unsigned i;
for(i = 0; i < LENGTH; i++)
{
size_t nn = str[i];
LodePNGState state;
lodepng_state_init(&state);
state.info_raw.colortype = LCT_GREY;
state.info_raw.bitdepth = 8;
err = lodepng_decode(images2 + i, &width1, &height1,
&state, letters[nn], letter_sizes[nn]);
lodepng_state_cleanup(&state);
#if 0 /* error handling removed by avst */
if(err)
{
printf("decoder error %u : %s\n", err,
lodepng_error_text(err));
exit(1);
}
#endif
if(err)
return NULL;
}
*w = width1 * LENGTH;
*h = HEIGHT;
unsigned char* image = (unsigned char*)malloc((*w) * (*h) * LENGTH + 7);
unsigned y;
for(y = 0; y < height1; y++)
{
for(i = 0; i < LENGTH; i++)
{
memcpy(image + y * (*w) + i * width1, *(images2 + i)
+ (y * width1), width1);
}
}
for(i = 0; i < LENGTH; i++)
{
free(images2[i]);
}
free(images2);
return image;
}
void generate_captcha(char** img, int* imgsize, char* answer)
{
size_t outsize;
#if 0
unsigned error;
#endif
unsigned char** out = (unsigned char**)img;
unsigned* s1 = generate();
/* for (int i = 0; i < LENGTH; i++)
{
printf("%c", used_letters[s1[i]]);
}
printf("\n");*/
xpair coords[SEGS];
mix(answer, s1, coords);
unsigned char* fin = 0;
unsigned width = 0, height = 0;
fin = reunis(s1, &width, &height);
free(s1);
/*printf("Width: %d\nHeight: %d\n", width, height);*/
unsigned char color = 255;
unsigned x, y;
for(y = HEIGHT_ONE; y < height; y++)
{
for(x = 0; x < width; x++)
{
size_t byte_index = y * width + x;
fin[byte_index] = color;
}
}
int i;
for(i = 0; i < SEGS; i++)
{
int x1 = coords[i].x1;
int x2 = coords[i].x2;
int sz = coords[i].sz;
hypotenuse(&fin, x1, Y1, x2, Y2, width, height);
arrows(&fin, x1, Y1, x2, Y2, width, height, LARR);
line(&fin, x1 - sz * WIDTH_ONE / 2 + 2,
x1 + sz * WIDTH_ONE / 2 - 2, HEIGHT_ONE + 1,
width, height);
rect(&fin, x2 - sz * WIDTH_ONE / 2 + 2,
x2 + sz * WIDTH_ONE / 2 - 2, width, height);
}
LodePNGState state;
lodepng_state_init(&state);
state.info_png.color.colortype = LCT_GREY;
state.info_png.color.bitdepth = 8;
state.info_raw.colortype = LCT_GREY;
state.info_raw.bitdepth = 8;
state.encoder.auto_convert = 0;
#if 0 /* error handling removed by avst */
error =
#endif
lodepng_encode(out, &outsize, fin, width, height, &state);
*imgsize = outsize;
free(fin);
#if 0 /* error handling removed by avst */
if(error)
{
printf("encoder error %u : %s\n", error,
lodepng_error_text(error));
exit(2);
}
#endif
lodepng_state_cleanup(&state);
}