Horriblesubs/fansub/class-fansub-captcha.php

420 lines
12 KiB
PHP

<?php
if(!function_exists('add_filter')) exit;
class FANSUB_Captcha {
private $chars;
private $length;
private $fonts;
private $background;
private $foreground;
private $code;
private $uppercase;
private $lowercase;
private $size;
private $base;
private $font_size;
private $file_mode;
private $image_type;
private $font_char_width;
private $save_path;
private $save_url;
private $session_name;
private $expired_minutes;
private $pixel;
private $pixel_color;
private $line;
private $line_color;
public function set_expired_minutes($minutes) {
$this->expired_minutes = $minutes;
}
public function get_expired_minutes() {
return $this->expired_minutes;
}
public function set_session_name($name) {
$this->session_name = $name;
}
public function get_session_name() {
return $this->session_name;
}
public function set_save_path($path) {
$this->save_path = $path;
}
public function get_save_path() {
return $this->save_path;
}
public function set_save_url($url) {
$this->save_url = $url;
}
public function get_save_url() {
return $this->save_url;
}
public function set_font_char_width($width) {
$this->font_char_width = $width;
}
public function get_font_char_width() {
return $this->font_char_width;
}
public function set_image_type($type) {
$this->image_type = $type;
}
public function get_image_type() {
return $this->image_type;
}
public function set_file_mode($mode) {
$this->file_mode = $mode;
}
public function get_file_mode() {
return $this->file_mode;
}
public function set_font_size($font_size) {
$this->font_size = $font_size;
}
public function get_font_size() {
return $this->font_size;
}
public function set_base($base) {
$this->base = $base;
}
public function get_base() {
return $this->base;
}
public function set_size($size) {
$this->size = $size;
}
public function get_size() {
return $this->size;
}
public function set_width($width) {
$this->size[0] = $width;
}
public function set_height($height) {
$this->size[1] = $height;
}
public function set_uppercase($uppercase) {
$this->uppercase = $uppercase;
}
public function get_uppercase() {
return $this->uppercase;
}
public function set_lowercase($lowercase) {
$this->lowercase = $lowercase;
}
public function get_lowercase() {
return $this->lowercase;
}
public function set_code($code) {
$this->code = $code;
}
public function get_code() {
return $this->code;
}
public function set_foreground($fg) {
$this->foreground = $fg;
}
public function get_foreground() {
return $this->foreground;
}
public function set_background($bg) {
$this->background = $bg;
}
public function get_background() {
return $this->background;
}
public function set_fonts($fonts) {
$this->fonts = $fonts;
}
public function add_font($font) {
$this->fonts[] = $font;
}
public function get_fonts() {
return $this->fonts;
}
public function set_length($length) {
$this->length = $length;
}
public function get_length() {
return $this->length;
}
public function set_chars($chars) {
$this->chars = $chars;
}
public function get_chars() {
return $this->chars;
}
public function set_pixel($bool) {
$this->pixel = $bool;
}
public function get_pixel() {
return (bool)$this->pixel;
}
public function set_pixel_color($color) {
$this->pixel_color = $color;
}
public function get_pixel_color() {
return $this->pixel_color;
}
public function set_line($bool) {
$this->line = $bool;
}
public function get_line() {
return (bool)$this->line;
}
public function set_line_color($color) {
$this->line_color = $color;
}
public function get_line_color() {
return $this->line_color;
}
public function __construct() {
$defaults = array(
'chars' => fansub_get_safe_captcha_characters(),
'length' => 5,
'uppercase' => true,
'lowercase' => true,
'size' => array(87, 25),
'background' => array(255, 255, 255),
'foreground' => array(138, 200, 67),
'pixel' => true,
'pixel_color' => array(205, 255, 205),
'line' => true,
'line_color' => array(205, 215, 205),
'font_char_width' => 14,
'file_mode' => 0444,
'image_type' => 'png',
'font_size' => 13,
'base' => array(10, 20),
'save_path' => FANSUB_CONTENT_PATH . '/captcha',
'save_url' => content_url('fansub/captcha'),
'session_name' => 'fansub_captcha',
'expired_minutes' => 3,
'fonts' => array(FANSUB_PATH . '/fonts/Tahoma.ttf')
);
$args = apply_filters('fansub_captcha_default_args', array());
$args = wp_parse_args($args, $defaults);
$this->set_chars($args['chars']);
$this->set_length($args['length']);
$this->set_uppercase($args['uppercase']);
$this->set_lowercase($args['lowercase']);
$this->set_size($args['size']);
$this->set_background($args['background']);
$this->set_foreground($args['foreground']);
$this->set_line($args['line']);
$this->set_line_color($args['line_color']);
$this->set_pixel($args['pixel']);
$this->set_pixel_color($args['pixel_color']);
$this->set_font_char_width($args['font_char_width']);
$this->set_file_mode($args['file_mode']);
$this->set_image_type($args['image_type']);
$this->set_font_size($args['font_size']);
$this->set_base($args['base']);
$this->set_save_path($args['save_path']);
$this->set_save_url($args['save_url']);
$this->set_session_name($args['session_name']);
$this->set_fonts($args['fonts']);
$this->set_expired_minutes($args['expired_minutes']);
}
public function generate_image() {
$dir = wp_normalize_path($this->get_save_path());
if(!wp_mkdir_p($dir)) {
return false;
}
$this->cleanup_expired();
$code = fansub_random_string($this->get_length(), $this->get_chars());
$filename = '';
$dir = trailingslashit($dir);
if($this->get_uppercase()) {
$code = strtoupper($code);
}
$size = $this->get_size();
if($im = @imagecreatetruecolor($size[0], $size[1])) {
$fonts = $this->get_fonts();
if(!fansub_array_has_value($fonts)) {
return false;
}
$filename = md5(fansub_random_string());
$background = $this->get_background();
$foreground = $this->get_foreground();
$bg = @imagecolorallocate($im, $background[0], $background[1], $background[2]);
$fg = @imagecolorallocate($im, $foreground[0], $foreground[1], $foreground[2]);
@imagefill($im, 0, 0, $bg);
if($this->get_pixel()) {
$pixel_colors = $this->get_pixel_color();
$pixel_color = @imagecolorallocate($im, $pixel_colors[0], $pixel_colors[1], $pixel_colors[2]);
$pixels = rand(300, 600);
for($i = 0; $i < $pixels; $i++) {
@imagesetpixel($im, rand(1, 100), rand(1, 100), $pixel_color);
}
}
$base = $this->get_base();
$x = $base[0] + mt_rand(-2, 2);
for($i = 0; $i < strlen($code); $i++) {
$font = $fonts[array_rand($fonts)];
$font = wp_normalize_path($font);
$y = $base[1] + mt_rand(-2, 2);
$angle = mt_rand(-20, 20);
@imagettftext($im, $this->get_font_size(), $angle, $x, $y, $fg, $font, $code[$i]);
$x += $this->get_font_char_width();
}
if($this->get_line()) {
$lines = rand(5, 10);
$line_colors = $this->get_line_color();
$line_color = @imagecolorallocate($im, $line_colors[0], $line_colors[1], $line_colors[2]);
for($i = 0; $i < $lines; $i++) {
@imageline($im, rand(1, 100), rand(1, 100), rand(1, 100), rand(1, 100), $line_color);
}
}
switch($this->get_image_type()) {
case 'jpeg':
$filename = sanitize_file_name($filename. '.jpeg');
$file = wp_normalize_path($dir . $filename);
@imagejpeg($im, $file);
break;
case 'gif':
$filename = sanitize_file_name($filename . '.gif');
$file = wp_normalize_path($dir . $filename);
@imagegif($im, $file);
break;
case 'png':
default:
$filename = sanitize_file_name($filename . '.png');
$file = wp_normalize_path($dir . $filename);
@imagepng($im, $file);
}
@imagedestroy($im);
@chmod($file, $this->get_file_mode());
}
if(!empty($filename)) {
if($this->get_lowercase()) {
$code = strtolower($code);
}
$data = array(
'hashed' => wp_hash_password($code),
'timestamp' => strtotime(fansub_get_current_date('Y-m-d H:i:s'))
);
$this->set_session_data($data);
}
$this->set_code($code);
return trailingslashit($this->get_save_url()) . $filename;
}
public function get_session_data() {
$session_name = $this->get_session_name();
$captcha_session = isset($_SESSION[$session_name]) ? maybe_unserialize($_SESSION[$session_name]) : array();
return $captcha_session;
}
public function set_session_data($data) {
$session_name = $this->get_session_name();
$_SESSION[$session_name] = maybe_serialize($data);
}
public function check($code) {
$current_datetime = fansub_get_current_date('Y-m-d H:i:s');
$captcha_session = $this->get_session_data();
$timestamp = fansub_get_value_by_key($captcha_session, 'timestamp');
if(!is_numeric($timestamp)) {
return false;
}
if(strtotime('+' . strval($this->get_expired_minutes()) . ' minutes', $timestamp) < strtotime($current_datetime)) {
return false;
}
if($this->get_lowercase()) {
$code = strtolower($code);
}
$save_code = fansub_get_value_by_key($captcha_session, 'hashed');
return wp_check_password($code, $save_code);
}
public function remove($prefix) {
$suffixes = array('.jpeg', '.gif', '.png', '.php', '.txt');
foreach($suffixes as $suffix) {
$dir = trailingslashit($this->get_save_path());
$filename = sanitize_file_name($prefix . $suffix);
$file = wp_normalize_path($dir . $filename);
if(@is_file($file)) {
@unlink($file);
}
}
}
public function cleanup_expired($minutes = 60) {
$dir = trailingslashit($this->get_save_path());
$dir = wp_normalize_path($dir);
if(!@is_dir($dir) || !@is_readable($dir)) {
return false;
}
$is_win = ('WIN' === strtoupper(substr(PHP_OS, 0, 3)));
if(!($is_win ? win_is_writable($dir) : @is_writable($dir))) {
return false;
}
fansub_delete_old_file($dir, $minutes * MINUTE_IN_SECONDS);
$count = 0;
if($handle = @opendir($dir)) {
while(false !== ($filename = readdir($handle))) {
if(!preg_match('/^[0-9]+\.(php|txt|png|gif|jpeg)$/', $filename)) {
continue;
}
$file = wp_normalize_path($dir . $filename);
$stat = @stat($file);
if(($stat['mtime'] + $minutes * MINUTE_IN_SECONDS) < time()) {
@unlink($file);
$count += 1;
}
}
closedir($handle);
}
return $count;
}
}