mirror of
https://github.com/Yetangitu/ampache
synced 2025-10-05 02:39:47 +02:00
303 lines
11 KiB
PHP
303 lines
11 KiB
PHP
<?php
|
|
/*
|
|
Does emit a CAPTCHA graphic and form fields, which allows to tell real
|
|
people from bots.
|
|
Though a textual description is generated as well, this sort of access
|
|
restriction will knock out visually impaired users, and frustrate all
|
|
others anyhow. Therefore this should only be used as last resort for
|
|
defending against spambots. Because of the readable text and the used
|
|
colorspaces this is a weak implementation, not completely OCR-secure.
|
|
|
|
captcha::form() will return a html string to be inserted into textarea/
|
|
[save] <forms> and alike. User input is veryfied with captcha::check().
|
|
You should leave the sample COLLEGE.ttf next to this script, else you
|
|
have to define the _FONT_DIR constant correctly. Use only one type.
|
|
|
|
Includes a sluggish workaround for Internet Explorer; but this script
|
|
must reside in a www-accessible directory then.
|
|
Public Domain, available via http://freshmeat.net/p/captchaphp
|
|
*/
|
|
|
|
|
|
#-- config
|
|
define("EWIKI_FONT_DIR", dirname(__FILE__)); // which fonts to use
|
|
define("CAPTCHA_INVERSE", 0); // white or black(=1)
|
|
define("CAPTCHA_TIMEOUT", 5000); // in seconds (=max 4 hours)
|
|
|
|
|
|
/* static - (you could instantiate it, but...) */
|
|
class captcha {
|
|
|
|
|
|
/* gets parameter from $_REQUEST[] array (POST vars) and so can
|
|
verify input, @returns boolean
|
|
*/
|
|
function check() {
|
|
if (($hash = $_REQUEST["captcha_hash"])
|
|
and ($pw = trim($_REQUEST["captcha_input"]))) {
|
|
return((captcha::hash($pw)==$hash) || (captcha::hash($pw,-1)==$hash));
|
|
}
|
|
}
|
|
|
|
|
|
/* yields <input> fields html string (no complete form), with captcha
|
|
image already embedded as data:-URI
|
|
*/
|
|
function form($title="→ retype that here", $more="<small><br>Enter the correct letters and numbers from the image into the text box. <br>This small test serves as access restriction against malicious bots. <br>Simply reload the page if this graphic is too hard to read.</small>") {
|
|
$pw = captcha::mkpass();
|
|
$hash = captcha::hash($pw);
|
|
// $maxsize = (strpos("MSIE", $_SERVER["HTTP_USER_AGENT"]) ? 1000 : 6000);
|
|
$maxsize = 100;
|
|
@header("Vary: User-Agent");
|
|
$img = "data:image/jpeg;base64,"
|
|
. base64_encode(captcha::image($pw, 200, 60, CAPTCHA_INVERSE, $maxsize));
|
|
$alt = htmlentities(captcha::textual_riddle($pw));
|
|
$test = substr($img,22);
|
|
$html =
|
|
'<table border="0" summary="captcha input"><tr>'
|
|
. '<td><img name="captcha_image" id="captcha_image" src="'.$img. '" height="60" width="200" alt="'.$alt. '" /></td>'
|
|
. '<td>'.$title. '<br/><input name="captcha_hash" type="hidden" value="'.$hash. '" />'
|
|
. '<font color="red">*</font><input name="captcha_input" type="text" size="7" maxlength="16" style="height:23px; font-size:16px; font-weight:450;" />'
|
|
. '</td><td>'.$more.'</td>'
|
|
. '</tr></table>';
|
|
|
|
// '<table border="0" summary="captcha input"><tr>'
|
|
// '<img name="captcha_image" id="captcha_image" src="'.$img. '" height="60" width="200" alt="'.$alt. '" /></td>'
|
|
// . '<td><img name="captcha_image" id="captcha_image" src="'.$img.'" height="60" width="200" alt="'.$alt. '" /></td>'
|
|
// . '<td>'.$title. '<br/><input name="captcha_hash" type="hidden" value="'.$hash. '" />'
|
|
// ''.$title. '<br/><input name="captcha_hash" type="hidden" value="'.$hash. '" />'
|
|
// . '<input name="captcha_input" type="text" size="7" maxlength="16" style="height:23px; font-size:16px; font-weight:450;" />'
|
|
// . '<td width="80%">'.$more.'</td>'
|
|
// . '</tr></table>';
|
|
|
|
#-- js/html fix if ("MSIE")
|
|
{
|
|
$base = "http://$_SERVER[SERVER_NAME]:$_SERVER[SERVER_PORT]/ampache/captcha.php";
|
|
// $base = "http://10.60.60.16/ampache/captcha.php";
|
|
// . substr(realpath(__FILE__), strlen($_SERVER["DOCUMENT_ROOT"]));
|
|
$html .= <<<END
|
|
<script language="Javascript"><!--
|
|
if (/Microsoft/.test(navigator.appName)) {
|
|
// var msg= "You are using IE Please download firefox in order to register. http://www.mozilla.org/products/firefox";
|
|
// alert(msg);
|
|
var img = document.captcha_image;
|
|
img.src = "$base?_ddu=$test";
|
|
//alert (img.src);
|
|
}
|
|
--></script>
|
|
END;
|
|
}
|
|
$html = "<div class=\"captcha\">$html</div>";
|
|
return($html);
|
|
}
|
|
/*<script language="Javascript"><!--
|
|
if (/Microsoft/.test(navigator.appName)) {
|
|
var img = document.captcha_image;
|
|
img.src = "$base?_ddu=" + img.src.substr(23);
|
|
}
|
|
--></script>/*
|
|
|
|
|
|
/* generates alternative (non-graphic), human-understandable
|
|
representation of the passphrase
|
|
*/
|
|
function textual_riddle($phrase) {
|
|
$symbols0 = '"\'-/_:';
|
|
$symbols1 = array("\n,", "\n;", ";", "\n&", "\n-", ",", ",", "\nand then", "\nfollowed by", "\nand", "\nand not a\n\"".chr(65+rand(0,26))."\",\nbut");
|
|
$s = "Guess the letters and numbers\n(passphrase riddle)\n--\n";
|
|
for ($p=0; $p<strlen($phrase); $p++) {
|
|
$c = $phrase[$p];
|
|
$add = "";
|
|
#-- asis
|
|
if (!rand(0,3)) {
|
|
$i = $symbols0[rand(0,strlen($symbols0)-1)];
|
|
$add = "$i$c$i";
|
|
}
|
|
#-- letter
|
|
elseif ($c >= 'A') {
|
|
$type = ($c >= 'a' ? "small " : "");
|
|
do {
|
|
$n = rand(-3,3);
|
|
$c2 = chr((ord($c) & 0x5F) + $n);
|
|
}
|
|
while (($c2 < 'A') || ($c2 > 'Z'));
|
|
if ($n < 0) {
|
|
$n = -$n;
|
|
$add .= "$type'$c2' +$n letters";
|
|
}
|
|
else {
|
|
$add .= "$n chars before $type$c2";
|
|
}
|
|
}
|
|
#-- number
|
|
else {
|
|
$add = "???";
|
|
$n = (int) $c;
|
|
do {
|
|
do { $x = rand(1, 10); } while (!$x);
|
|
$op = rand(0,11);
|
|
if ($op <= 2) {
|
|
$add = "($add * $x)"; $n *= $x;
|
|
}
|
|
elseif ($op == 3) {
|
|
$x = 2 * rand(1,2);
|
|
$add = "($add / $x)"; $n /= $x;
|
|
}
|
|
elseif ($sel % 2) {
|
|
$add = "($add + $x)"; $n += $x;
|
|
}
|
|
else {
|
|
$add = "($add - $x)"; $n -= $x;
|
|
}
|
|
}
|
|
while (rand(0,1));
|
|
$add .= " = $n";
|
|
}
|
|
$s .= "$add";
|
|
$s .= $symbols1[rand(0,count($symbols1)-1)] . "\n";
|
|
}
|
|
return($s);
|
|
}
|
|
|
|
|
|
/* returns jpeg file stream with unscannable letters encoded
|
|
in front of colorful disturbing background
|
|
*/
|
|
function image($phrase, $width=200, $height=60, $inverse=0, $maxsize=0xFFFFF) {
|
|
|
|
#-- initialize in-memory image with gd library
|
|
srand(microtime()*21017);
|
|
$img = imagecreatetruecolor($width, $height);
|
|
$R = $inverse ? 0xFF : 0x00;
|
|
imagefilledrectangle($img, 0,0, $width,$height, captcha::random_color($img, 222^$R, 255^$R));
|
|
$c1 = rand(150^$R, 185^$R);
|
|
$c2 = rand(195^$R, 230^$R);
|
|
|
|
#-- configuration
|
|
$fonts = array(
|
|
// "COLLEGE.ttf",
|
|
);
|
|
$fonts += glob(EWIKI_FONT_DIR."/*.ttf");
|
|
|
|
#-- encolour bg
|
|
$wd = 20;
|
|
$x = 0;
|
|
while ($x < $width) {
|
|
imagefilledrectangle($img, $x, 0, $x+=$wd, $height, captcha::random_color($img, 222^$R, 255^$R));
|
|
$wd += max(10, rand(0, 20) - 10);
|
|
}
|
|
|
|
#-- make interesting background I, lines
|
|
$wd = 4;
|
|
$w1 = 0;
|
|
$w2 = 0;
|
|
for ($x=0; $x<$width; $x+=(int)$wd) {
|
|
if ($x < $width) { // verical
|
|
imageline($img, $x+$w1, 0, $x+$w2, $height-1, captcha::random_color($img,$c1,$c2));
|
|
}
|
|
if ($x < $height) { // horizontally ("y")
|
|
imageline($img, 0, $x-$w2, $width-1, $x-$w1, captcha::random_color($img,$c1,$c2));
|
|
}
|
|
$wd += rand(0,8) - 4;
|
|
if ($wd < 1) { $wd = 2; }
|
|
$w1 += rand(0,8) - 4;
|
|
$w2 += rand(0,8) - 4;
|
|
if (($x > $height) && ($y > $height)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
#-- more disturbing II, random letters
|
|
$limit = rand(30,90);
|
|
for ($n=0; $n<$limit; $n++) {
|
|
$letter = "";
|
|
do {
|
|
$letter .= chr(rand(31,125)); // random symbol
|
|
} while (rand(0,1));
|
|
$size = rand(5, $height/2);
|
|
$half = (int) ($size / 2);
|
|
$x = rand(-$half, $width+$half);
|
|
$y = rand(+$half, $height);
|
|
$rotation = rand(60, 300);
|
|
$c1 = captcha::random_color($img, 130^$R, 240^$R);
|
|
$font = $fonts[rand(0, count($fonts)-1)];
|
|
imagettftext($img, $size, $rotation, $x, $y, $c1, $font, $letter);
|
|
}
|
|
|
|
#-- add the real text to it
|
|
$len = strlen($phrase);
|
|
$w1 = 10;
|
|
$w2 = $width / ($len+1);
|
|
for ($p=0; $p<$len; $p++) {
|
|
$letter = $phrase[$p];
|
|
$size = rand(18, $height/2.2);
|
|
$half = (int) $size / 2;
|
|
$rotation = rand(-33, 33);
|
|
$y = rand($size+3, $height-3);
|
|
$x = $w1 + $w2*$p;
|
|
$w1 += rand(-$width/90, $width/40); // @BUG: last char could be +30 pixel outside of image
|
|
$font = $fonts[rand(0, count($fonts)-1)];
|
|
$r=rand(30,99); $g=rand(30,99); $b=rand(30,99); // two colors for shadow
|
|
$c1 = imagecolorallocate($img, $r*1^$R, $g*1^$R, $b*1^$R);
|
|
$c2 = imagecolorallocate($img, $r*2^$R, $g*2^$R, $b*2^$R);
|
|
imagettftext($img, $size, $rotation, $x+1, $y, $c2, $font, $letter);
|
|
imagettftext($img, $size, $rotation, $x, $y-1, $c1, $font, $letter);
|
|
}
|
|
|
|
#-- let JFIF stream be generated
|
|
// $quality = 67;
|
|
$quality = 8;
|
|
$s = array();
|
|
do {
|
|
ob_start(); ob_implicit_flush(0);
|
|
imagejpeg($img, "", (int)$quality);
|
|
$jpeg = ob_get_contents(); ob_end_clean();
|
|
$size = strlen($jpeg);
|
|
$s_debug[] = ((int)($quality*10)/10) . "%=$size";
|
|
$quality = $quality * ($maxsize/$size) * 0.93 - 1.7; // -($quality/7.222)*
|
|
}
|
|
while (($size > $maxsize) && ($quality >= 16));
|
|
imagedestroy($img);
|
|
#print_r($s_debug);
|
|
return($jpeg);
|
|
}
|
|
|
|
|
|
/* helper code */
|
|
function random_color($img, $a,$b) {
|
|
return imagecolorallocate($img, rand($a,$b), rand($a,$b), rand($a,$b));
|
|
}
|
|
|
|
|
|
/* unreversable hash from passphrase, with time() slice encoded */
|
|
function hash($text, $dtime=0) {
|
|
$text = strtolower($text);
|
|
$pfix = (int) (time() / CAPTCHA_TIMEOUT) + $dtime;
|
|
return md5("captcha::$pfix:$text::".__FILE__.":$_SERVER[SERVER_NAME]:80");
|
|
}
|
|
|
|
|
|
/* makes string of random letters for embedding into image and for
|
|
encoding as hash, later verification
|
|
*/
|
|
function mkpass() {
|
|
$s = "";
|
|
for ($n=0; $n<10; $n++) {
|
|
$s .= chr(rand(0, 255));
|
|
}
|
|
$s = base64_encode($s); // base64-set, but filter out unwanted chars
|
|
$s = preg_replace("/[+\/=IG0ODQR]/i", "", $s); // (depends on YOUR font)
|
|
$s = substr($s, 0, rand(5,7));
|
|
return($s);
|
|
}
|
|
}
|
|
|
|
|
|
#-- IE workaround
|
|
if (isset($_REQUEST["_ddu"])) {
|
|
header("Content-Type: image/jpeg");
|
|
die(base64_decode(substr($_REQUEST["_ddu"], 0)));
|
|
}
|
|
|
|
|
|
?>
|