diff --git a/inc/captcha/captcha.php b/inc/captcha/captcha.php new file mode 100644 index 00000000..7c54fa3c --- /dev/null +++ b/inc/captcha/captcha.php @@ -0,0 +1,357 @@ +width = $left; + $this->height = $top; + + $this->charset = preg_split('//u', $charset); + + $this->style = ""; + + for ($i = 0; $i < $len; $i++) { + $this->content[] = array(mb_substr($text, $i, 1, 'utf-8'), "top" => $top / 2 - $top / 4, + "left" => $left/10 + 9*$left*$i/10/$len, + "position" => "absolute"); + } + + $this->color = "hsla(".rand(1,360).", 76%, 78%, 1)"; + + $this->add_junk(); + $this->mutate_sizes(); + $this->mutate_positions(); + $this->mutate_transform(); + $this->mutate_anchors(); + $this->randomize(); + $this->mutate_containers(); + $this->mutate_margins(); + $this->mutate_styles(); + $this->randomize(); + } + + function mutate_sizes() { + foreach ($this->content as &$v) { + if (!isset ($v['font-size'])) + $v['font-size'] = rand($this->height/3 - 4, $this->height/3 + 8); + } + } + function mutate_positions() { + foreach ($this->content as &$v) { + $v['top'] += rand(-10,10); + $v['left'] += rand(-10,10); + } + } + function mutate_transform() { + $fromto = array('6'=>'9', '9'=>'6', '8'=>'8', '0'=>'0', + 'z'=>'z', 's'=>'s', 'n'=>'u', 'u'=>'n', + 'a'=>'ɐ', 'e'=>'ə', 'p'=>'d', 'd'=>'p', + 'A'=>'∀', 'E'=>'∃', 'H'=>'H', 'o'=>'o', + 'O'=>'O'); + + foreach ($this->content as &$v) { + $basefrom = -20; + $baseto = 20; + + if (isset($fromto[$v[0]]) && rand(0,1)) { + $v[0] = $fromto[$v[0]]; + $basefrom = 160; + $baseto = 200; + } + + $v['transform'] = 'rotate('.rand($basefrom,$baseto).'deg)'; + $v['-ms-transform'] = 'rotate('.rand($basefrom,$baseto).'deg)'; + $v['-webkit-transform'] = 'rotate('.rand($basefrom,$baseto).'deg)'; + } + } + function randomize(&$a = false) { + if ($a === false) { + $a = &$this->content; + } + + shuffle($a); + + foreach ($a as &$v) { + $this->shuffle_assoc($v); + + if (is_array ($v[0])) { + $this->randomize($v[0]); + } + } + } + + function add_junk() { + $count = rand(200, 300); + + while ($count--) { + $elem = array(); + + $elem['top'] = rand(0, $this->height); + $elem['left'] = rand(0, $this->width); + + $elem['position'] = 'absolute'; + + $elem[0] = $this->charset[rand(0, count($this->charset)-1)]; + + switch($t = rand (0,9)) { + case 0: + $elem['display'] = 'none'; break; + case 1: + $elem['top'] = rand(-60, -90); break; + case 2: + $elem['left'] = rand(-40, -70); break; + case 3: + $elem['top'] = $this->height + rand(10, 60); break; + case 4: + $elem['left'] = $this->width + rand(10, 60); break; + case 5: + $elem['color'] = $this->color; break; + case 6: + $elem['visibility'] = 'hidden'; break; + case 7: + $elem['height'] = rand(0,2); + $elem['overflow'] = 'hidden'; break; + case 8: + $elem['width'] = rand(0,1); + $elem['overflow'] = 'hidden'; break; + case 9: + $elem['font-size'] = rand(2, 6); break; + } + + $this->content[] = $elem; + } + } + + function mutate_anchors() { + foreach ($this->content as &$elem) { + if (rand(0,1)) { + $elem['right'] = $this->width - $elem['left'] - (int)(0.5*$elem['font-size']); + unset($elem['left']); + } + if (rand(0,1)) { + $elem['bottom'] = $this->height - $elem['top'] - (int)(1.5*$elem['font-size']); + unset($elem['top']); + } + } + } + + function mutate_containers() { + for ($i = 0; $i <= 80; $i++) { + $new = []; + $new['width'] = rand(0, $this->width*2); + $new['height'] = rand(0, $this->height*2); + $new['top'] = rand(-$this->height * 2, $this->height * 2); + $new['bottom'] = $this->height - ($new['top'] + $new['height']); + $new['left'] = rand(-$this->width * 2, $this->width * 2); + $new['right'] = $this->width - ($new['left'] + $new['width']); + + $new['position'] = 'absolute'; + + $new[0] = []; + + $cnt = rand(0,10); + for ($j = 0; $j < $cnt; $j++) { + $elem = array_pop($this->content); + if (!$elem) break; + + if (isset($elem['top'])) $elem['top'] -= $new['top']; + if (isset($elem['bottom'])) $elem['bottom'] -= $new['bottom']; + if (isset($elem['left'])) $elem['left'] -= $new['left']; + if (isset($elem['right'])) $elem['right'] -= $new['right']; + + $new[0][] = $elem; + } + + if (rand (0,1)) unset($new['top']); + else unset($new['bottom']); + if (rand (0,1)) unset($new['left']); + else unset($new['right']); + + $this->content[] = $new; + + shuffle($this->content); + } + } + + function mutate_margins(&$a = false) { + if ($a === false) { + $a = &$this->content; + } + + foreach ($a as &$v) { + $ary = ['top', 'left', 'bottom', 'right']; + shuffle($ary); + $cnt = rand(0,4); + $ary = array_slice($ary, 0, $cnt); + + foreach ($ary as $prop) { + $margin = rand(-1000, 1000); + + $v['margin-'.$prop] = $margin; + + if (isset($v[$prop])) { + $v[$prop] -= $margin; + } + } + + if (is_array($v[0])) { + $this->mutate_margins($v[0]); + } + } + } + + function mutate_styles(&$a = false) { + if ($a === false) { + $a = &$this->content; + } + + foreach ($a as &$v) { + $content = $v[0]; + unset($v[0]); + $styles = array_splice($v, 0, rand(0, 6)); + $v[0] = $content; + + $id_or_class = rand(0,1); + $param = $id_or_class ? "id" : "class"; + $prefix = $id_or_class ? "#" : "."; + $genname = "zz-".base_convert(rand(1,999999999), 10, 36); + + if ($styles || rand(0,1)) { + $this->style .= $prefix.$genname."{"; + $this->style .= $this->rand_whitespace(); + + foreach ($styles as $k => $val) { + if (is_int($val)) { + $val = "".$val."px"; + } + + $this->style .= "$k:"; + $this->style .= $this->rand_whitespace(); + $this->style .= "$val;"; + $this->style .= $this->rand_whitespace(); + } + $this->style .= "}"; + $this->style .= $this->rand_whitespace(); + } + + $v[$param] = $genname; + + if (is_array($v[0])) { + $this->mutate_styles($v[0]); + } + } + } + + function to_html(&$a = false) { + $inside = true; + + if ($a === false) { + if ($this->style) { + echo ""; + } + + echo "
"; + } + + } + + function rand_whitespace($r = 0) { + switch (rand($r,4)) { + case 0: + return ""; + case 1: + return "\n"; + case 2: + return "\t"; + case 3: + return " "; + case 4: + return " "; + } + } + + + + function shuffle_assoc(&$array) { + $keys = array_keys($array); + + shuffle($keys); + + foreach($keys as $key) { + $new[$key] = $array[$key]; + } + + $array = $new; + + return true; + } +} + +//$charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789卐"; + +//(new CzaksCaptcha("hotwheels", 300, 80, $charset))->to_html(); +?> diff --git a/inc/captcha/config.php b/inc/captcha/config.php new file mode 100644 index 00000000..568c06c5 --- /dev/null +++ b/inc/captcha/config.php @@ -0,0 +1,16 @@ + 'SET NAMES utf8')); + + +// Captcha expiration: +$expires_in = 120; // 120 seconds + +// Captcha dimensions: +$width = 250; +$height = 80; + +// Captcha length: +$length = 6; diff --git a/inc/captcha/dbschema.sql b/inc/captcha/dbschema.sql new file mode 100644 index 00000000..504ea10c --- /dev/null +++ b/inc/captcha/dbschema.sql @@ -0,0 +1,9 @@ +SET NAMES utf8; + +CREATE TABLE `captchas` ( + `cookie` VARCHAR(50), + `extra` VARCHAR(200), + `text` VARCHAR(255), + `created_at` INT(11), + PRIMARY KEY (cookie, extra) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; diff --git a/inc/captcha/entrypoint.php b/inc/captcha/entrypoint.php new file mode 100644 index 00000000..3b468a2a --- /dev/null +++ b/inc/captcha/entrypoint.php @@ -0,0 +1,85 @@ +prepare("DELETE FROM `captchas` WHERE `created_at` < ?")->execute([time() - $expires_in]); +} + +switch ($mode) { +// Request: GET entrypoint.php?mode=get&extra=1234567890 +// Response: JSON: cookie => "generatedcookie", captchahtml => "captchahtml", expires_in => 120 +case "get": + if (!isset ($_GET['extra'])) { + die(); + } + + header("Content-type: application/json"); + + $extra = $_GET['extra']; + + require_once("config.php"); + + $text = rand_string($length, $extra); + + $captcha = new CzaksCaptcha($text, $width, $height, $extra); + + $cookie = rand_string(20, "abcdefghijklmnopqrstuvwxyz"); + + ob_start(); + $captcha->to_html(); + $html = ob_get_contents(); + ob_end_clean(); + + $query = $pdo->prepare("INSERT INTO `captchas` (`cookie`, `extra`, `text`, `created_at`) VALUES (?, ?, ?, ?)"); + $query->execute( [$cookie, $extra, $text, time()]); + + echo json_encode(["cookie" => $cookie, "captchahtml" => $html, "expires_in" => $expires_in]); + + break; + +// Request: GET entrypoint.php?mode=check&cookie=generatedcookie&extra=1234567890&text=captcha +// Response: 0 OR 1 +case "check": + if (!isset ($_GET['mode']) + || !isset ($_GET['cookie']) + || !isset ($_GET['extra']) + || !isset ($_GET['text'])) { + die(); + } + + require_once("config.php"); + + cleanup($pdo, $expires_in); + + $query = $pdo->prepare("SELECT * FROM `captchas` WHERE `cookie` = ? AND `extra` = ?"); + $query->execute([$_GET['cookie'], $_GET['extra']]); + + $ary = $query->fetchAll(); + + if (!$ary) { + echo "0"; + } + else { + $query = $pdo->prepare("DELETE FROM `captchas` WHERE `cookie` = ? AND `extra` = ?"); + $query->execute([$_GET['cookie'], $_GET['extra']]); + + if ($ary[0]['text'] !== $_GET['text']) { + echo "0"; + } + else { + echo "1"; + } + } + + break; +} diff --git a/inc/captcha/readme.md b/inc/captcha/readme.md new file mode 100644 index 00000000..8b1f538a --- /dev/null +++ b/inc/captcha/readme.md @@ -0,0 +1,10 @@ +I integrated this from: https://github.com/ctrlcctrlv/infinity/commit/62a6dac022cb338f7b719d0c35a64ab3efc64658 + +