257 lines
6.6 KiB
C
257 lines
6.6 KiB
C
#include <sys/time.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <gd.h>
|
|
#include <gdfontl.h>
|
|
|
|
/*
|
|
This code is only a C implementation (with some minor modifications) of this captcha generator :
|
|
http://logz.org/captchaz/
|
|
|
|
Copyright November 2012 Grégory Soutadé
|
|
Copyright July 2007 Loz
|
|
|
|
LICENCE : Copyleft, Licence Art Libre (http://artlibre.org/licence/lal)
|
|
|
|
Modifications :
|
|
* Group letters
|
|
* Add another layer for letter rendering (more confusing)
|
|
* Change start, not appropriate for big fonts
|
|
* Change min/max font size
|
|
|
|
You need libgd2-xpm-dev package
|
|
|
|
It's recommanded the included font "scrawl.ttf"
|
|
*/
|
|
|
|
#define MAX(a, b) (((a) >= (b)) ? (a) : (b))
|
|
|
|
// If not defined
|
|
/* int overflow2(int a, int b) */
|
|
/* { */
|
|
/* if(a < 0 || b < 0) { */
|
|
/* fprintf(stderr, "gd warning: one parameter to a memory allocation multiplication is negative, failing operation gracefully\n"); */
|
|
/* return 1; */
|
|
/* } */
|
|
/* if(b == 0) */
|
|
/* return 0; */
|
|
/* if(a > 0xFFFFFFFF / b) { */
|
|
/* fprintf(stderr, "gd warning: product of memory allocation multiplication would exceed INT_MAX, failing operation gracefully\n"); */
|
|
/* return 1; */
|
|
/* } */
|
|
/* return 0; */
|
|
/* } */
|
|
|
|
static int my_rand(int min, int max)
|
|
{
|
|
int val;
|
|
struct timeval tv;
|
|
static unsigned int seed = 0;
|
|
int interval;
|
|
|
|
if (seed == 0)
|
|
{
|
|
gettimeofday(&tv, NULL);
|
|
seed = tv.tv_usec;
|
|
}
|
|
|
|
val = rand_r(&seed);
|
|
|
|
if (min < 0)
|
|
{
|
|
if (max < 0)
|
|
interval = -(min-max);
|
|
else
|
|
interval = max-min;
|
|
}
|
|
else
|
|
interval = max-min;
|
|
|
|
val = (int)(((double)val/(double)RAND_MAX)*(double)(interval));
|
|
|
|
val += min;
|
|
|
|
/* printf("rand [%d..%d] %d\n", min, max, val); */
|
|
|
|
return val;
|
|
}
|
|
|
|
static inline int random_color(gdImagePtr im, int a, int b) {
|
|
return gdImageColorAllocate(im, my_rand(a,b), my_rand(a,b), my_rand(a,b));
|
|
}
|
|
|
|
static int generate_captcha(int width, int height, int inverse, char* font, char* phrase, char* out)
|
|
{
|
|
gdImagePtr im;
|
|
int R, c1, c2;
|
|
int wd, x, y, w1, w2, limit, n, p, r, g, b;
|
|
int size, half, len;
|
|
double rotation;
|
|
char letter[2] = {0, 0};
|
|
int brect[8];
|
|
char* error;
|
|
const double rad = 3.14/(double)180;
|
|
|
|
// initialize in-memory image with gd library
|
|
im = gdImageCreateTrueColor(width, height);
|
|
|
|
R = (inverse) ? 0xFF : 0x00;
|
|
|
|
gdImageFilledRectangle(im, 0, 0, width, height, random_color(im, 222^R, 255^R));
|
|
|
|
c1 = my_rand(150^R, 185^R);
|
|
c2 = my_rand(195^R, 230^R);
|
|
|
|
// encolour bg
|
|
wd=20; x=0;
|
|
while (x < width)
|
|
{
|
|
gdImageFilledRectangle(im, x, 0, x+wd, height, random_color(im, 222^R, 255^R));
|
|
x += wd;
|
|
wd += MAX(10, my_rand(0, 20) - 10);
|
|
}
|
|
|
|
// make interesting background I, lines
|
|
wd=4; w1=0; w2=0;
|
|
for(x=0; x<width; x+=wd)
|
|
{
|
|
if (x < width)
|
|
gdImageLine(im, x+w1, 0, x+w2, height-1, random_color(im, c1, c2));
|
|
|
|
if (x < height)
|
|
gdImageLine(im, 0, x-w2, width-1, x-w1, random_color(im, c1, c2));
|
|
|
|
wd += my_rand(0, 8)-4;
|
|
if (wd < 1) wd = 2;
|
|
w1 += my_rand(0, 8)-4;
|
|
w2 += my_rand(0, 8)-4;
|
|
|
|
if ((x > height) && (x > height)) // ???
|
|
break;
|
|
}
|
|
|
|
// more disturbing II, random letters
|
|
limit = my_rand(30, 90);
|
|
for(n=0; n<limit; n++)
|
|
{
|
|
letter[0] = my_rand(31, 125); // only one letter
|
|
size = my_rand(5, height/2);
|
|
half = size / 2;
|
|
x = my_rand(-half, width+half);
|
|
y = my_rand(half, height);
|
|
rotation = my_rand(60, 300);
|
|
c1 = random_color(im, 130^R, 240^R);
|
|
error = gdImageStringFT(im, brect, c1, font, size, rotation, x, y, letter);
|
|
if (error)
|
|
{
|
|
fprintf(stderr, "%s, font is %s\n", error, font);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// add the real text to it
|
|
len = strlen(phrase);
|
|
w1 = 10;
|
|
w2 = width / (height/4); // Change start, not appropriate for big fonts
|
|
x = w2;
|
|
|
|
for(p=0; p<len; p++)
|
|
{
|
|
letter[0] = phrase[p];
|
|
size = my_rand(height/4, height/3); // Change min/max font size
|
|
half = size / 2;
|
|
rotation = (double)my_rand(-33, 33)*rad;
|
|
y = my_rand(size+3, height-3);
|
|
/* x = w1 + w2*p; */
|
|
w1 += my_rand(-width/90, width/40);
|
|
r = my_rand(30, 99) ; g = my_rand(30, 99) ; b = my_rand(30, 99);
|
|
c1 = gdImageColorAllocate(im, r*1^R, g*1^R, b*1^R);
|
|
c2 = gdImageColorAllocate(im, r*2^R, g*2^R, b*2^R);
|
|
gdImageStringFT(im, brect, c2, font, size, rotation, x+(size/40), y, letter);
|
|
gdImageStringFT(im, brect, c1, font, size, rotation, x, y-(size/40), letter);
|
|
// Add another layer
|
|
gdImageStringFT(im, brect, c1, font, size, rotation, x+(size/10), y+(size/10), letter);
|
|
// Group letters
|
|
x += size;
|
|
}
|
|
|
|
FILE* f= fopen(out, "wb");
|
|
|
|
if (!f)
|
|
{
|
|
fprintf(stderr, "Error opening %s\n", out);
|
|
return 1;
|
|
}
|
|
|
|
gdImagePng(im, f);
|
|
fclose(f);
|
|
|
|
gdImageDestroy(im);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void usage()
|
|
{
|
|
printf("gcpatchaz is a command line captcha generator\n\n");
|
|
printf("usage : gcpatchaz [options]\n");
|
|
printf("\t-w WIDTH\n");
|
|
printf("\t-h HEIGHT\n");
|
|
printf("\t-o OUT.PNG\n");
|
|
printf("\t-f FONT\n");
|
|
printf("\t-s STRING\n");
|
|
printf("\t-i : inverse\n");
|
|
printf("\t-h : help\n");
|
|
printf("\nLICENCE : Copyleft, Licence Art Libre (http://artlibre.org/licence/lal)\n");
|
|
printf("Copyright Grégory Soutadé (2012)\n");
|
|
printf("Copyright Loz (2007)\n");
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
int opt;
|
|
int width=180, height=40;
|
|
char* out = "captcha.png", *font = "ttf-japanese-gothic.ttf", *string = NULL;
|
|
int inverse= 0;
|
|
int i, res;
|
|
|
|
while ((opt = getopt(argc, argv, "w:H:o:f:s:ih")) != -1)
|
|
{
|
|
switch(opt)
|
|
{
|
|
case 'w': width = atoi(optarg); break;
|
|
case 'H': height = atoi(optarg); break;
|
|
case 'o': out = optarg; break;
|
|
case 'f': font = optarg; break;
|
|
case 's': string = strdup(optarg); break;
|
|
case 'i': inverse = 1; break;
|
|
case 'h':
|
|
usage();
|
|
return 0;
|
|
default:
|
|
usage();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (string == NULL)
|
|
{
|
|
string = malloc(6);
|
|
for(i=0; i<5; i++)
|
|
{
|
|
string[i] = my_rand(65, 90);
|
|
}
|
|
string[i] = 0;
|
|
}
|
|
|
|
res = generate_captcha(width, height, inverse, font, string, out);
|
|
|
|
if (res == 0)
|
|
printf("Captcha generated for text \"%s\"\n", string);
|
|
|
|
free(string);
|
|
|
|
return 0;
|
|
}
|