diff options
Diffstat (limited to 'phpBB/includes')
-rw-r--r-- | phpBB/includes/acp/acp_captcha.php | 114 | ||||
-rw-r--r-- | phpBB/includes/captcha/captcha_gd.php | 3434 | ||||
-rw-r--r-- | phpBB/includes/functions.php | 8 | ||||
-rw-r--r-- | phpBB/includes/functions_admin.php | 2 | ||||
-rw-r--r-- | phpBB/includes/functions_compress.php | 54 | ||||
-rw-r--r-- | phpBB/includes/ucp/ucp_confirm.php | 41 | ||||
-rw-r--r-- | phpBB/includes/utf/utf_normalizer.php | 2227 | ||||
-rw-r--r-- | phpBB/includes/utf/utf_tools.php | 4 |
8 files changed, 1219 insertions, 4665 deletions
diff --git a/phpBB/includes/acp/acp_captcha.php b/phpBB/includes/acp/acp_captcha.php index b16b76495f..7cef658e93 100644 --- a/phpBB/includes/acp/acp_captcha.php +++ b/phpBB/includes/acp/acp_captcha.php @@ -21,91 +21,37 @@ class acp_captcha $user->add_lang('acp/board'); - $config_vars = array('enable_confirm' => 'REG_ENABLE', - 'enable_post_confirm' => 'POST_ENABLE', - 'policy_overlap' => 'OVERLAP_ENABLE', - 'policy_overlap_noise_pixel' => 'OVERLAP_NOISE_PIXEL', - 'policy_overlap_noise_line' => 'OVERLAP_NOISE_LINE_ENABLE', - 'policy_entropy' => 'ENTROPY_ENABLE', - 'policy_entropy_noise_pixel' => 'ENTROPY_NOISE_PIXEL', - 'policy_entropy_noise_line' => 'ENTROPY_NOISE_LINE_ENABLE', - 'policy_shape' => 'SHAPE_ENABLE', - 'policy_shape_noise_pixel' => 'SHAPE_NOISE_PIXEL', - 'policy_shape_noise_line' => 'SHAPE_NOISE_LINE_ENABLE', - 'policy_3dbitmap' => 'THREEDBITMAP_ENABLE', - 'policy_cells' => 'CELLS_ENABLE', - 'policy_stencil' => 'STENCIL_ENABLE', - 'policy_composite' => 'COMPOSITE_ENABLE' - ); - - $policy_modules = array('policy_entropy', 'policy_3dbitmap', 'policy_overlap', 'policy_shape', 'policy_cells', 'policy_stencil', 'policy_composite'); - - switch ($mode) + $config_vars = array( + 'enable_confirm' => 'REG_ENABLE', + 'enable_post_confirm' => 'POST_ENABLE', + 'captcha_gd' => 'CAPTCHA_GD', + 'captcha_gd_noise' => 'CAPTCHA_GD_NOISE', + ); + + $this->tpl_name = 'acp_captcha'; + $this->page_title = 'ACP_VC_SETTINGS'; + $submit = request_var('submit', ''); + if ($submit) { - case 'visual': - $this->tpl_name = 'acp_captcha'; - $this->page_title = 'ACP_VC_SETTINGS'; - $submit = request_var('submit', ''); - if ($submit) - { - $config_vars = array_keys($config_vars); - foreach ($config_vars as $config_var) - { - set_config($config_var, request_var($config_var, '')); - } - trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($this->u_action)); - } - else - { - $array = array(); - - foreach ($config_vars as $config_var => $template_var) - { - $array[$template_var] = $config[$config_var]; - } - $template->assign_vars($array); - - - if (@extension_loaded('gd')) - { - $template->assign_var('GD', true); - foreach ($policy_modules as $module_name) - { - $template->assign_var('U_' . strtoupper($module_name), sprintf($user->lang['CAPTCHA_EXPLAIN'], '<a href="' . append_sid("{$phpbb_root_path}adm/index.$phpEx", 'i=captcha&mode=img&policy=' . $module_name) . '">', '</a>')); - } - if (function_exists('imagettfbbox') && function_exists('imagettftext')) - { - $template->assign_var('TTF', true); - } - } - } - break; - - case 'img': - $policy = request_var('policy', ''); - - if (!@extension_loaded('gd')) - { - trigger_error($user->lang['NO_GD'] . adm_back_link($this->u_action), E_USER_WARNING); - } - - if (!($policy === 'policy_entropy' || $policy === 'policy_3dbitmap') && (!function_exists('imagettfbbox') || !function_exists('imagettftext'))) - { - trigger_error($user->lang['NO_TTF'] . adm_back_link($this->u_action), E_USER_WARNING); - } - - if (!in_array($policy, $policy_modules)) - { - trigger_error($user->lang['BAD_POLICY'] . adm_back_link($this->u_action), E_USER_WARNING); - } - - $user->add_lang('ucp'); - - include($phpbb_root_path . 'includes/captcha/captcha_gd.' . $phpEx); - - $captcha = new captcha(); - $captcha->execute(gen_rand_string(), $policy); - break; + $config_vars = array_keys($config_vars); + foreach ($config_vars as $config_var) + { + set_config($config_var, request_var($config_var, '')); + } + trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($this->u_action)); + } + else + { + $array = array(); + + if (@extension_loaded('gd') && function_exists('imagettfbbox') && function_exists('imagettftext')) + { + $template->assign_var('GD', true); + } + foreach ($config_vars as $config_var => $template_var) + { + $template->assign_var($template_var, $config[$config_var]); + } } } } diff --git a/phpBB/includes/captcha/captcha_gd.php b/phpBB/includes/captcha/captcha_gd.php index 34b1684f3c..e249a46c04 100644 --- a/phpBB/includes/captcha/captcha_gd.php +++ b/phpBB/includes/captcha/captcha_gd.php @@ -9,3429 +9,157 @@ */ /** -* This file is getting too large. -* -* Only bugfixes allowed from now on. -* If a policy can not be fixed with the minimum amount of code it gets removed. -*/ - -/** -* Main gd based captcha class -* -* Thanks to Robert Hetzler (Xore) +* Based on PHP-Class hn_captcha Version 1.3, released 11-Apr-2006 +* Original Author - Horst Nogajski, horst@nogajski.de * * @package VC */ class captcha { - /** - * Create the image containing $code - */ - function execute($code, $policy) - { - $this->$policy(str_split($code)); - } - - /** - * Send image and destroy - */ - function send_image(&$image) - { - header('Content-Type: image/png'); - header('Cache-control: no-cache, no-store'); - imagepng($image); - imagedestroy($image); - } - - /** - * - */ - function wave_height($x, $y, $factor = 1, $tweak = 1) - { - return ((sin($x / (3 * $factor)) + sin($y / (3 * $factor))) * 10 * $tweak); - } - - /** - * - */ - function grid_height($x, $y, $factor = 1, $x_grid, $y_grid) - { - return ( (!($x % ($x_grid * $factor)) || !($y % ($y_grid * $factor))) ? 3 : 0); - } - - /** - * - */ - function draw_shape($type, $img, $x_min, $y_min, $x_max, $y_max, $color) - { - switch ($type) - { - case 'Square': - imagefilledpolygon($img, array($x_min, $y_max, $x_min, $y_min, $x_max, $y_min, $x_max, $y_max), 4, $color); - break; - - case 'TriangleUp': - imagefilledpolygon($img, array($x_min, $y_max, ($x_min + $x_max) / 2, $y_min, $x_max, $y_max), 3, $color); - break; - - case 'TriangleDown': - imagefilledpolygon($img, array($x_min, $y_min, ($x_min + $x_max) / 2, $y_max, $x_max, $y_min), 3, $color); - break; - - case 'Circle': - imagefilledellipse($img, ($x_min + $x_max) / 2, ($y_min + $y_max) / 2, $x_max - $x_min, $y_max - $y_min, $color); - break; - } - } - - /** - * - */ - function draw_pattern($seed, $img, $x_min, $y_min, $x_max, $y_max, $colors, $thickness = 1) - { - $x_size = ($x_max - $x_min) / 4; - $y_size = ($y_max - $y_min) / 4; - $bitmap = substr($seed, 16, 4); - $numcolors = sizeof($colors) - 1; - for ($y = 0; $y < 4; ++$y) - { - $map = hexdec(substr($bitmap, $y, 1)); - for ($x = 0; $x < 4; ++$x) - { - if ($map & (1 << $x)) - { - $char = hexdec(substr($seed, ($y << 2) + $x, 1)); - if (!($char >> 2)) - { - switch ($char & 3) - { - case 0: - $shape = 'Circle'; - break; - - case 1: - $shape = 'Square'; - break; - - case 2: - $shape = 'TriangleUp'; - break; - - case 3: - $shape = 'TriangleDown'; - break; - } - $this->draw_shape($shape, $img, $x_min + ($x * $x_size), $y_min + ($y * $y_size), $x_min + (($x + 1) * $x_size), $y_min + (($y + 1) * $y_size), $colors[array_rand($colors)]); - } - } - } - } - - $cells = array(); - for ($i = 0; $i < 6; ++$i) - { - $cells = hexdec(substr($seed, 20 + ($i << 1), 2)); - $x1 = $cells & 3; - $cells = $cells >> 2; - $y1 = $cells & 3; - $cells = $cells >> 2; - $x2 = $cells & 3; - $cells = $cells >> 2; - $y2 = $cells & 3; - $x1_real = $x_min + (($x1 + 0.5) * $x_size); - $y1_real = $y_min + (($y1 + 0.5) * $y_size); - $x2_real = $x_min + (($x2 + 0.5) * $x_size); - $y2_real = $y_min + (($y2 + 0.5) * $y_size); - if ($thickness > 1) - { - imagesetthickness($img, $thickness); - } - imageline($img, $x1_real, $y1_real, $x2_real, $y2_real, $colors[array_rand($colors)]); - if ($thickness > 1) - { - imagesetthickness($img, 1); - } - } - } - - /** - * - */ - function get_char_string() - { - static $chars = false; - static $charcount = 0; - if (!$chars) - { - $chars = array_merge(range('A', 'Z'), range('1', '9')); - } - $word = ''; - for ($i = mt_rand(6, 8); $i > 0; --$i) - { - $word .= $chars[array_rand($chars)]; - } - return $word; - } - - /** - * shape - */ - function policy_shape($code) - { - global $config, $user; - // Generate image - $img_x = 800; - $img_y = 250; - $img = imagecreatetruecolor($img_x, $img_y); - - // Generate colors - $c = new color_manager($img, array( - 'random' => true, - 'min_saturation' => 70, - 'min_value' => 65, - )); - - $primaries = $c->color_scheme('background', 'tetradic', false); - - $noise = array_shift($primaries); - $noise = $c->mono_range($noise, 'value', 5, false); - $primaries = $c->mono_range($primaries, 'value', 5, false); - - // Generate code characters - $characters = array(); - $sizes = array(); - $bounding_boxes = array(); - $width_avail = $img_x; - $code_num = sizeof($code); - $char_class = $this->captcha_char('char_ttf'); - for ( $i = 0; $i < $code_num; ++$i ) - { - $characters[$i] = new $char_class($code[$i]); - list($min, $max) = $characters[$i]->range(); - $sizes[$i] = mt_rand($min, $max / 2); - $box = $characters[$i]->dimensions($sizes[$i]); - $width_avail -= ($box[2] - $box[0]); - $bounding_boxes[$i] = $box; - } - - // Redistribute leftover x-space - $offset = array(); - for ( $i = 0; $i < $code_num; ++$i ) - { - $denom = ($code_num - $i); - $denom = max(1.5, $denom); - $offset[$i] = mt_rand(0, (1.5 * $width_avail) / $denom); - $width_avail -= $offset[$i]; - } - - // Add some line noise - if ($config['policy_shape_noise_line']) - { - $this->noise_line($img, 0, 0, $img_x, $img_y, $c->r('background'), $primaries, $noise); - } - - $real = mt_rand(0, 3); - $patterns = array('', '', '', ''); - for ($i = 32; $i > 0; --$i) - { - $patterns[$i & 3] .= str_pad(dechex(mt_rand(0, 65535)), 4, '0', STR_PAD_LEFT); - } - - - for ($i = 0; $i < 4; ++$i) - { - /*if ($i) - { - $y = 5 + ($i * 60); - imageline($img, 550, $y, 650, $y, $fontcolors[0]); - }*/ - $this->draw_pattern($patterns[$i], $img, 525, 10 + ($i * 60), 575, ($i + 1) * 60, $primaries); - if ($i == $real) - { - $this->draw_pattern($patterns[$i], $img, 25, 25, 225, 225, $primaries, 3); - for ($j = 0; $j < $code_num; ++$j) - { - $character = new $char_class($code[$j]); - $character->drawchar(25, 600 + ($j * 25), 35 + ($i * 60), $img, $c->r('background'), $primaries); - } - } - else - { - $word = $this->get_char_string(); - for ($j = strlen($word) - 1; $j >= 0; --$j) - { - $character = new $char_class(substr($word, $j, 1)); - $character->drawchar(25, 600 + ($j * 25), 35 + ($i * 60), $img, $c->r('background'), $primaries); - } - } - } - - $count = sizeof($user->lang['CAPTCHA']['shape']); - $line_height = $img_y / ($count + 1); - for ($i = 0; $i < $count; ++$i) - { - $text = $user->lang['CAPTCHA']['shape'][$i]; - $line_width = strlen($text) * 4.5; // ( / 2, * 9 ) - imagestring($img, 6, ($img_x / 2) - $line_width - 1, $line_height * ($i + 1) - 1, $text, $c->r('black')); - imagestring($img, 6, ($img_x / 2) - $line_width - 1, $line_height * ($i + 1) + 1, $text, $c->r('black')); - imagestring($img, 6, ($img_x / 2) - $line_width + 1, $line_height * ($i + 1) + 1, $text, $c->r('black')); - imagestring($img, 6, ($img_x / 2) - $line_width + 1, $line_height * ($i + 1) - 1, $text, $c->r('black')); - imagestring($img, 6, ($img_x / 2) - $line_width, $line_height * ($i + 1), $text, $c->r('white')); - } - - - // Add some pixel noise - if ($config['policy_shape_noise_pixel']) - { - $this->noise_pixel($img, 0, 0, $img_x, $img_y, $c->r('background'), $primaries, $noise, $config['policy_shape_noise_pixel']); - } - - // Send image - $this->send_image($img); - } - - function policy_composite($code) - { - // Generate image - $img_x = 800; - $img_y = 250; - $img = imagecreate($img_x, $img_y); - - $map = captcha_vectors(); - $fonts = captcha_load_ttf_fonts(); - - // Generate basic colors - $c = new color_manager($img, 'white'); - $c->allocate_named('primary', array( - 'random' => true, - 'min_saturation' => 50, - 'min_value' => 75, - )); - $bg_colors = $c->color_scheme('primary', 'triadic', false); - $text_colors = $c->mono_range('primary', 'saturation', 6); - $bg_colors = $c->mono_range($bg_colors, 'saturation', 6); - - // Specificy image portion dimensions. - $count = sizeof($code); - $cellsize = $img_x / $count; - $y_range = min($cellsize, $img_y); - $y_max = $img_y - $y_range; - $y_off = array(); // consecutive vertical offset of characters - $color = array(); // color of characters - $y_off[0] = mt_rand(0, $y_max); - for ($i = 1; $i < $count; ++$i) - { - // each consective character can be as much as 50% closer to the top or bottom of the image as the previous - $diff = mt_rand(-50, 50); - if ($diff > 0) - { - $y_off[$i] = $y_off[$i - 1] + ((($y_max - $y_off[$i - 1]) * $diff) / 100); - } - else - { - $y_off[$i] = $y_off[$i - 1] * ((100 + $diff) / 100); - } - } - - $range = 0.075; - - $chars = array_merge(range('A', 'Z'), range('1', '9')); - - // draw some characters. if they're within the vector spec of the code character, color them differently - for ($i = 0; $i < 8000; ++$i) - { - $degree = mt_rand(-30, 30); - $x = mt_rand(0, $img_x - 1); - $y = mt_rand(0, $img_y); - $text = $chars[array_rand($chars)]; - $char = $x / $cellsize; - $meta_x = ((($x % $cellsize) / $cellsize) * 1.5) - 0.25; - $meta_y = (($img_y - $y) - $y_off[$char]) / $y_range; - $font = $fonts[array_rand($fonts)]; - - $distance = vector_distance($map[$code[$char]], $meta_x, $meta_y, $range); - - $switch = !(rand() % 100); - - imagettftext($img, 10, $degree, $x, $y, - (($distance <= $range) xor $switch) ? - $c->r_rand($text_colors) : - $c->r_rand($bg_colors), - $font, $text); - - } - - // Send image - $this->send_image($img); - } - - function policy_stencil($code) - { - // Generate image - $img_x = 800; - $img_y = 250; - $img = imagecreatetruecolor($img_x, $img_y); - $stencil = imagecreatetruecolor($img_x, $img_y); - - $map = captcha_vectors(); - $fonts = captcha_load_ttf_fonts(); - - // Generate colors - $c = new color_manager($img, 'black'); - $cs = new color_manager($stencil, 'gray'); - - $c->allocate_named('primary', array( - 'random' => true, - 'min_saturation' => 75, - 'min_value' => 80, - )); - - $secondary = $c->color_scheme('primary', 'triadic', false); - - //imagefill($stencil, 0, 0, $black2); - //imagefill($img, 0, 0, $white1); - - $chars = array_merge(range('A', 'Z'), range('1', '9')); - $step = 20; - $density = 4; - for ($i = 0; $i < $img_x; $i += $step) - { - for ($j = 0; $j < $img_y; $j += $step) - { - for ($k = 0; $k < $density; ++$k) - { - $degree = mt_rand(-30, 30); - $x = mt_rand($i, $i + $step); - $y = mt_rand($j, $j + $step); - $char = $chars[array_rand($chars)]; - $font = $fonts[array_rand($fonts)]; - imagettftext($stencil, mt_rand(20, 30), $degree, $x, $y, $cs->r('black'), $font, $char); - } - } - } - - for ($i = 0; $i < 3; ++$i) - { - $degree1 = mt_rand(-30, 30); - $degree2 = mt_rand(-30, 30); - $x1 = mt_rand(0, $img_x - 1); - $x2 = mt_rand(0, $img_x - 1); - $y1 = mt_rand(0, $img_y); - $y2 = mt_rand(0, $img_y); - $char1 = $chars[array_rand($chars)]; - $char2 = $chars[array_rand($chars)]; - $font1 = $fonts[array_rand($fonts)]; - $font2 = $fonts[array_rand($fonts)]; - - imagettftext($img, mt_rand(75, 100), $degree1, $x1, $y1, $secondary[0], $font1, $char1); - imagettftext($img, mt_rand(75, 100), $degree2, $x2, $y2, $secondary[1], $font2, $char2); - } - - $characters = array(); - $sizes = array(); - $bounding_boxes = array(); - $width_avail = $img_x; - $code_num = sizeof($code); - $char_class = $this->captcha_char('char_ttf'); - for ($i = 0; $i < $code_num; ++$i) - { - $characters[$i] = new $char_class($code[$i]); - $sizes[$i] = mt_rand(75, 100); - $box = $characters[$i]->dimensions($sizes[$i]); - $width_avail -= ($box[2] - $box[0]); - $bounding_boxes[$i] = $box; - } - - // - // Redistribute leftover x-space - // - $offset = array(); - for ($i = 0; $i < $code_num; ++$i) - { - $denom = ($code_num - $i); - $denom = max(1.5, $denom); - $offset[$i] = mt_rand(0, (1.5 * $width_avail) / $denom); - $width_avail -= $offset[$i]; - } - - // Draw the text - $xoffset = 0; - for ($i = 0; $i < $code_num; ++$i) - { - $characters[$i] = new $char_class($code[$i]); - $dimm = $bounding_boxes[$i]; - $xoffset += ($offset[$i] - $dimm[0]); - $yoffset = mt_rand(-$dimm[1], $img_y - $dimm[3]); - $characters[$i]->drawchar($sizes[$i], $xoffset, $yoffset, $img, $c->r('background'), array($c->r('primary'))); - $xoffset += $dimm[2]; - } - - for ($i = 0; $i < $img_x; ++$i) - { - for ($j = 0; $j < $img_y; ++$j) - { - // if the stencil is not black, set the pixel in the image to gray - if (imagecolorat($stencil, $i, $j)) - { - imagesetpixel($img, $i, $j, $c->r('gray')); - } - } - } - - // Send image - $this->send_image($img); - } - - function policy_cells($code) - { - global $user; - // Generate image - $img_x = 800; - $img_y = 250; - $img = imagecreate($img_x, $img_y); - - $fonts = captcha_load_ttf_fonts(); - - $map = captcha_vectors(); - - // - // Generate colors - // - $c = new color_manager($img, 'white'); - - $c->allocate_named('primary', array( - 'random' => true, - 'min_saturation' => 30, - 'min_value' => 65, - )); - $primaries = $c->color_scheme('primary', 'tetradic'); - $bg_colors = $c->mono_range($primaries, 'value', 4, false); - shuffle($primaries); - shuffle($bg_colors); - - // Randomize the characters on the right and the left - $left_characters = array(); - $right_characters = array(); - $chars = array_merge(range('A', 'Z'), range('1', '9')); - $chars_size = sizeof($chars) - 1; - $alpha = range('A', 'Z'); - $alpha_size = sizeof($alpha) - 1; - for ($i = 0; $i < 25; ++$i) - { - $left_characters[$i] = $alpha[mt_rand(0, $alpha_size)]; - $right_characters[$i] = $chars[mt_rand(0, $chars_size)]; - } - - // Pick locations for our code, shuffle the rest into 3 separate queues - $code_count = sizeof($code); - $code_order = range(0, 24); - shuffle($code_order); - $remaining = array_splice($code_order, $code_count); - $lineups = array($code_order, array(), array(), array()); - for ($i = sizeof($remaining) - 1; $i >= 0; --$i) - { - $lineups[mt_rand(1, 3)][] = $remaining[$i]; - } - - // overwrite the randomized left and right values with our code, where applicable - for ($i = 0; $i < $code_count; ++$i) - { - $left_characters[$code_order[$i]] = $i + 1; - $right_characters[$code_order[$i]] = $code[$i]; - } - - - $offset1 = 50; - $offset2 = 550; - - // Draw the cells and right hand characters - $xs = $ys = array(); - for ($i = 0; $i < 25; ++$i) - { - $xs[$i] = $offset1 + 20 + (($i % 5) * 40) + mt_rand(-13, 13); - $ys[$i] = 45 + (intval($i / 5) * 40) + mt_rand(-13, 13); - - $bg = $c->r_rand($bg_colors); - - // fill the cells with the background colors - imagefilledrectangle($img, - $offset1 + 1 + (($i % 5) * 40), 26 + (intval($i / 5) * 40), - $offset1 + 39 + (($i % 5) * 40), 64 + (intval($i / 5) * 40), - $bg); - imagefilledrectangle($img, - $offset2 + 1 + (($i % 5) * 40), 26 + (intval($i / 5) * 40), - $offset2 + 39 + (($i % 5) * 40), 64 + (intval($i / 5) * 40), - $bg); - - $level = intval($i / 5); - $pos = $i % 5; - imagettftext($img, 12, 0, - $offset2 + 15 + ($pos * 40), 50 + ($level * 40), - $c->is_dark($bg) ? $c->r('white'): $c->r('black'), $fonts['genr102.ttf'], $right_characters[$i]); - } - - // draw the lines that appear between nodes (visual hint) - for ($k = 0; $k < 4; ++$k ) - { - $lineup = $lineups[$k]; - for ($i = 1, $size = sizeof($lineup); $i < $size; ++$i ) - { - imageline($img, - $xs[$lineup[$i - 1]], $ys[$lineup[$i - 1]], - $xs[$lineup[$i]], $ys[$lineup[$i]], - $primaries[$k]); - } - } - - // draw the actual nodes - $textcolor = $c->is_dark($primaries[0]) ? $c->r('white') : $c->r('black'); - for ($k = 0; $k < 4; ++$k ) - { - for ($j = 0, $size = sizeof($lineups[$k]); $j < $size; ++$j ) - { - $i = $lineups[$k][$j]; - imagefilledellipse($img, - $xs[$i], $ys[$i], - 20, 20, - $primaries[$k]); - imagettftext($img, 12, 0, - $xs[$i] - 5, $ys[$i] + 5, - $textcolor, $fonts['genr102.ttf'], $left_characters[$i]); - } - } - - // Draw poly behind explain text - $points = mt_rand(3, 6); - $arc = 360 / $points; - $vertices = array(); - $c_x = $img_x / 2; - $c_y = $img_y / 2; - $radius = $img_y / 2.5; - $start = deg2rad(mt_rand(0, 360)); - for ($i = 0; $i < $points; ++$i) - { - $rad = $start + deg2rad(($arc * $i) + mt_rand(-10, 10)); - $vertices[] = $c_x + (cos($rad) * $radius); - $vertices[] = $c_y + (sin($rad) * $radius); - } - imagefilledpolygon($img, $vertices, $points, $primaries[mt_rand(0,3)]); - - // draw explain text - $count = sizeof($user->lang['CAPTCHA']['cells']); - $line_height = $img_y / ($count + 1); - for ($i = 0; $i < $count; ++$i) - { - $text = $user->lang['CAPTCHA']['cells'][$i]; - $line_width = strlen($text) * 4.5; // ( / 2, * 9 ) - imagestring($img, 6, ($img_x / 2) - $line_width - 1, $line_height * ($i + 1) - 1, $text, $c->r('black')); - imagestring($img, 6, ($img_x / 2) - $line_width - 1, $line_height * ($i + 1) + 1, $text, $c->r('black')); - imagestring($img, 6, ($img_x / 2) - $line_width + 1, $line_height * ($i + 1) + 1, $text, $c->r('black')); - imagestring($img, 6, ($img_x / 2) - $line_width + 1, $line_height * ($i + 1) - 1, $text, $c->r('black')); - imagestring($img, 6, ($img_x / 2) - $line_width, $line_height * ($i + 1), $text, $c->r('white')); - } - - // Send image - $this->send_image($img); - } - - /** - * entropy - */ - function policy_entropy($code) + function execute($code) { global $config; - // Generate image - $img_x = 800; - $img_y = 250; - $img = imagecreatetruecolor($img_x, $img_y); - - // Generate colors - $c = new color_manager($img, array( - 'random' => true, - 'min_value' => 60, - ), 'hsv'); + $stats = gd_info(); - $scheme = $c->color_scheme('background', 'triadic', false); - $scheme = $c->mono_range($scheme, 'both', 10, false); - shuffle($scheme); - $bg_colors = array_splice($scheme, mt_rand(6, 12)); - - // Generate code characters - $characters = $sizes = $bounding_boxes = array(); - $width_avail = $img_x; - $code_num = sizeof($code); - - for ($i = 0; $i < $code_num; ++$i) - { - $char_class = $this->captcha_char(); - $characters[$i] = new $char_class($code[$i]); - - list($min, $max) = $characters[$i]->range(); - $sizes[$i] = mt_rand($min, $max); - $box = $characters[$i]->dimensions($sizes[$i]); - $width_avail -= ($box[2] - $box[0]); - $bounding_boxes[$i] = $box; - } - - // Redistribute leftover x-space - $offset = array(); - for ($i = 0; $i < $code_num; ++$i) + if (substr($stats['GD Version'], 0, 7) === 'bundled') { - $denom = ($code_num - $i); - $denom = max(1.5, $denom); - $offset[$i] = mt_rand(0, (1.5 * $width_avail) / $denom); - $width_avail -= $offset[$i]; + $bundled = true; } - - // Add some line noise - if ($config['policy_entropy_noise_line']) - { - $this->noise_line($img, 0, 0, $img_x, $img_y, $c->r('background'), $scheme, $bg_colors); - } - - // Draw the text - $xoffset = 0; - for ($i = 0; $i < $code_num; ++$i) - { - $dimm = $bounding_boxes[$i]; - $xoffset += ($offset[$i] - $dimm[0]); - $yoffset = mt_rand(-$dimm[1], $img_y - $dimm[3]); - $characters[$i]->drawchar($sizes[$i], $xoffset, $yoffset, $img, $c->r('background'), $scheme); - $xoffset += $dimm[2]; - } - - // Add some pixel noise - if ($config['policy_entropy_noise_pixel']) + else { - $this->noise_pixel($img, 0, 0, $img_x, $img_y, $c->r('background'), $scheme, $bg_colors, $config['policy_entropy_noise_pixel']); + $bundled = false; } - // Send image - $this->send_image($img); - } - - /** - * 3dbitmap - */ - function policy_3dbitmap($code) - { - // Generate image - $img_x = 700; - $img_y = 225; - $img = imagecreatetruecolor($img_x, $img_y); - $x_grid = mt_rand(6, 10); - $y_grid = mt_rand(6, 10); - - // Ok, so lets cut to the chase. We could accurately represent this in 3d and - // do all the appropriate linear transforms. my questions is... why bother? - // The computational overhead is unnecessary when you consider the simple fact: - // we're not here to accurately represent a model, but to just show off some random-ish - // polygons - - // Conceive of 3 spaces. - // 1) planar-space (discrete "pixel" grid) - // 2) 3-space. (planar-space with z/height aspect) - // 3) image space (pixels on the screen) - - // resolution of the planar-space we're embedding the text code in - $plane_x = 90; - $plane_y = 25; - - $subdivision_factor = 2; - - // $box is the 4 points in img_space that correspond to the corners of the plane in 3-space - $box = array(array(), array(), array(), array()); - - // Top left - $box[0][0] = mt_rand(20, 40); - $box[0][1] = mt_rand(40, 60); - - // Top right - $box[1][0] = mt_rand($img_x - 80, $img_x - 60); - $box[1][1] = mt_rand(10, 30); - - // Bottom right - $box[2][0] = mt_rand($img_x - 40, $img_x - 20); - $box[2][1] = mt_rand($img_y - 50, $img_y - 30); - - // Bottom left. - // because we want to be able to make shortcuts in the 3d->2d, - // we'll calculate the 4th point so that it forms a proper trapezoid - $box[3][0] = $box[2][0] + $box[0][0] - $box[1][0]; - $box[3][1] = $box[2][1] + $box[0][1] - $box[1][1]; - $c = new color_manager($img, array( - 'random' => true, - 'min_saturation' => 50, - 'min_value' => 65, - )); - - $r1 = $c->random_color(array( - 'min_value' => 20, - 'max_value' => 50, - )); - $r2 = $c->random_color(array( - 'min_value' => 70, - 'max_value' => 100, - )); - $rdata = mt_rand(0,1) ? array( - $c->colors[$r1], - $c->colors[$r2], - ) : array( - $c->colors[$r2], - $c->colors[$r1], - ); - - $colors = array(); - for ($i = 0; $i < 60; ++$i) + preg_match('/[\\d.]+/', $stats['GD Version'], $version); + if (version_compare($version[0], '2.0.1', '>=')) { - $colors[$i - 30] = $c->allocate(array( - $rdata[0][0], - (($i * $rdata[0][1]) + ((60 - $i) * $rdata[1][1])) / 60, - (($i * $rdata[0][2]) + ((60 - $i) * $rdata[1][2])) / 60, - )); + $gd_version = 2; } - - // $img_buffer is the last row of 3-space positions (converted to img-space), cached - // (using this means we don't need to recalculate all 4 positions for each new polygon, - // merely the newest point that we're adding, which is then cached. - $img_buffer = array(array(), array()); - - // In image-space, the x- and y-offset necessary to move one unit in the x-direction in planar-space - $dxx = ($box[1][0] - $box[0][0]) / ($subdivision_factor * $plane_x); - $dxy = ($box[1][1] - $box[0][1]) / ($subdivision_factor * $plane_x); - - // In image-space, the x- and y-offset necessary to move one unit in the y-direction in planar-space - $dyx = ($box[3][0] - $box[0][0]) / ($subdivision_factor * $plane_y); - $dyy = ($box[3][1] - $box[0][1]) / ($subdivision_factor * $plane_y); - - // Initial captcha-letter offset in planar-space - $plane_offset_x = 2; - $plane_offset_y = 5; - - // character map - $map = captcha_bitmaps(); - - // matrix - $plane = array(); - - // for each character, we'll silkscreen it into our boolean pixel plane - for ($c = 0, $code_num = sizeof($code); $c < $code_num; ++$c) + else { - $letter = $code[$c]; - - for ($x = $map['width'] - 1; $x >= 0; --$x) - { - for ($y = $map['height'] - 1; $y >= 0; --$y) - { - if ($map['data'][$letter][$y][$x]) - { - $plane[$y + $plane_offset_y + (($c & 1) ? 1 : -1)][$x + $plane_offset_x] = true; - } - } - } - $plane_offset_x += 11; + $gd_version = 1; } - // calculate our first buffer, we can't actually draw polys with these yet - // img_pos_prev == screen x,y location to our immediate left. - // img_pos_cur == current screen x,y location - // we calculate screen position of our - // current cell based on the difference from the previous cell - // rather than recalculating from absolute coordinates - // What we cache into the $img_buffer contains the raised text coordinates. - $img_pos_prev = $img_buffer[0][0] = $box[0]; - $cur_height = $prev_height = $this->wave_height(0, 0, $subdivision_factor); - $full_x = $plane_x * $subdivision_factor; - $full_y = $plane_y * $subdivision_factor; + // set dimension of image + $lx = 360; + $ly = 96; - for ($x = 1; $x <= $full_x; ++$x) + // create the image, stay compat with older versions of GD + if ($gd_version === 2) { - $cur_height = $this->wave_height($x, 0, $subdivision_factor); - $offset = $cur_height - $prev_height; - $img_pos_cur = array($img_pos_prev[0] + $dxx, - $img_pos_prev[1] + $dxy + $offset); - - $img_buffer[0][$x] = $img_pos_cur; - $img_pos_prev = $img_pos_cur; - $prev_height = $cur_height; + $func1 = 'imagecreatetruecolor'; + $func2 = 'imagecolorallocate'; } - - for ($y = 1; $y <= $full_y; ++$y) + else { - // swap buffers - $buffer_cur = $y & 1; - $buffer_prev = 1 - $buffer_cur; - - $prev_height = $this->wave_height(0, $y, $subdivision_factor); - $offset = $prev_height - $this->wave_height(0, $y - 1, $subdivision_factor); - $img_pos_cur = array($img_buffer[$buffer_prev][0][0] + $dyx, - $img_buffer[$buffer_prev][0][1] + $dyy + $offset); - $img_pos_prev = $img_pos_cur; - - $img_buffer[$buffer_cur][0] = $img_pos_cur; - - for ($x = 1; $x <= $full_x; ++$x) - { - $cur_height = $this->wave_height($x, $y, $subdivision_factor) + $this->grid_height($x, $y, 1, $x_grid, $y_grid); - - //height is a z-factor, not a y-factor - $offset = $cur_height - $prev_height; - $img_pos_cur = array($img_pos_prev[0] + $dxx, - $img_pos_prev[1] + $dxy + $offset); - - //(height is float, index it to an int, get closest color) - $color = $colors[intval($cur_height)]; - $img_pos_prev = $img_pos_cur; - $prev_height = $cur_height; - - $y_index_old = intval(($y - 1) / $subdivision_factor); - $y_index_new = intval($y / $subdivision_factor); - $x_index_old = intval(($x - 1) / $subdivision_factor); - $x_index_new = intval($x / $subdivision_factor); - - if (!empty($plane[$y_index_new][$x_index_new])) - { - $offset2 = $this->wave_height($x, $y, $subdivision_factor, 1) - 30 - $cur_height; - $img_pos_cur[1] += $offset2; - $color = $colors[20]; - } - $img_buffer[$buffer_cur][$x] = $img_pos_cur; - - // Smooth the edges as much as possible by having not more than one low<->high traingle per square - // Otherwise, just - $diag_down = (empty($plane[$y_index_old][$x_index_old]) == empty($plane[$y_index_new][$x_index_new])); - $diag_up = (empty($plane[$y_index_old][$x_index_new]) == empty($plane[$y_index_new][$x_index_old])); - - // natural switching - $mode = ($x + $y) & 1; - - // override if it requires it - if ($diag_down != $diag_up) - { - $mode = $diag_up; - } - - if ($mode) - { - // +-/ / - // 1 |/ 2 /| - // / /-+ - $poly1 = array_merge($img_buffer[$buffer_cur][$x - 1], $img_buffer[$buffer_prev][$x - 1], $img_buffer[$buffer_prev][$x]); - $poly2 = array_merge($img_buffer[$buffer_cur][$x - 1], $img_buffer[$buffer_cur][$x], $img_buffer[$buffer_prev][$x]); - } - else - { - // \ \-+ - // 1 |\ 2 \| - // +-\ \ - $poly1 = array_merge($img_buffer[$buffer_cur][$x - 1], $img_buffer[$buffer_prev][$x - 1], $img_buffer[$buffer_cur][$x]); - $poly2 = array_merge($img_buffer[$buffer_prev][$x - 1], $img_buffer[$buffer_prev][$x], $img_buffer[$buffer_cur][$x]); - } - - imagefilledpolygon($img, $poly1, 3, $color); - imagefilledpolygon($img, $poly2, 3, $color); - } + $func1 = 'imagecreate'; + $func2 = 'imagecolorclosest'; } - // Send image on it's merry way - $this->send_image($img); - } + $image = $func1($lx, $ly); - /** - * overlap - */ - function policy_overlap($code) - { - global $config; - $char_size = 40; - $overlap_factor = .32; - - // Generate image - $img_x = 250; - $img_y = 120; - $img = imagecreatetruecolor($img_x, $img_y); - - // Generate colors - $c = new color_manager($img, array( - 'random' => true, - 'min_saturation' => 70, - 'min_value' => 65, - )); - - $primaries = $c->color_scheme('background', 'triadic', false); - $text = mt_rand(0, 1); - $c->name_color('text', $primaries[$text]); - $noise = $c->mono_range($primaries[1 - $text], 'both', 6, false); - - // Generate code characters - $characters = $bounding_boxes = array(); - $width_avail = $img_x; - - // Get the character rendering scheme - $char_class = $this->captcha_char('char_ttf'); - $code_num = sizeof($code); - - for ($i = 0; $i < $code_num; ++$i) + if ($bundled) { - $characters[$i] = new $char_class($code[$i], array('angle' => 0)); - $box = $characters[$i]->dimensions($char_size); - $width_avail -= ((1 - $overlap_factor) * ($box[2] - $box[0])); - $bounding_boxes[$i] = $box; + imageantialias($image, true); } - // Redistribute leftover x-space - $offset = mt_rand(0, $width_avail); - - // Add some line noise - if ($config['policy_overlap_noise_line']) - { - $this->noise_line($img, 0, 0, $img_x, $img_y, $c->r('background'), array($c->r('text')), $noise); - } + // set background color + $back = imagecolorallocate($image, mt_rand(224, 255), mt_rand(224, 255), mt_rand(224, 255)); + imagefilledrectangle($image, 0, 0, $lx, $ly, $back); - // Draw the text - $min = 10 - $bounding_boxes[0][1]; - $max = ($img_y - 10) - $bounding_boxes[0][3]; - $med = ($max + $min) / 2; - - $yoffset = mt_rand($med, $max); - $char_num = sizeof($characters); - - imagesetthickness($img, 3); - for ($i = 0; $i < $char_num; ++$i) + // allocates the 216 websafe color palette to the image + if ($gd_version === 1) { - if ($i) + for ($r = 0; $r <= 255; $r += 51) { - imageline($img, $old_x + mt_rand(-3, 3), $old_y - 70 + mt_rand(-3, 3), $offset + mt_rand(-3, 3), $yoffset - 70 + mt_rand(-3, 3), $c->r('text')); - imageline($img, $old_x + mt_rand(-3, 3), $old_y + 30 + mt_rand(-3, 3), $offset + mt_rand(-3, 3), $yoffset + 30 + mt_rand(-3, 3), $c->r('text')); - } - - $dimm = $bounding_boxes[$i]; - $offset -= $dimm[0]; - $characters[$i]->drawchar($char_size, $offset, $yoffset, $img, $c->r('background'), array($c->r('text'))); - - $old_x = $offset; - $old_y = $yoffset; - - $offset += $dimm[2]; - $offset -= (($dimm[2] - $dimm[0]) * $overlap_factor); - $yoffset += ($i & 1) ? ((1 - $overlap_factor) * ($dimm[3] - $dimm[1])) : ((1 - $overlap_factor) * ($dimm[1] - $dimm[3])); - } - - imagesetthickness($img, 1); - - // Add some medium pixel noise - if ($config['policy_overlap_noise_pixel']) - { - $this->noise_pixel($img, 0, 0, $img_x, $img_y, $c->r('background'), array($c->r('text')), $noise, $config['policy_overlap_noise_pixel']); - } - - // Send image - $this->send_image($img); - } - - /** - * Noise pixel - */ - function noise_pixel($img, $min_x, $min_y, $max_x, $max_y, $bg, $font, $non_font, $override = false) - { - $noise_modules = array('noise_pixel_light', 'noise_pixel_medium', 'noise_pixel_heavy'); - - if ($override == false) - { - $override = array_rand($override); - } - - // Use the module $override, else a random picked one... - $module = $noise_modules[intval($override) - 1]; - - switch ($module) - { - case 'noise_pixel_light': - - for ($x = $min_x; $x < $max_x; $x += mt_rand(9, 18)) + for ($g = 0; $g <= 255; $g += 51) { - for ($y = $min_y; $y < $max_y; $y += mt_rand(4, 9)) + for ($b = 0; $b <= 255; $b += 51) { - imagesetpixel($img, $x, $y, $non_font[array_rand($non_font)]); + imagecolorallocate($image, $r, $g, $b); } } - - for ($y = $min_y; $y < $max_y; $y += mt_rand(9, 18)) - { - for ($x = $min_x; $x < $max_x; $x += mt_rand(4, 9)) - { - imagesetpixel($img, $x, $y, $non_font[array_rand($non_font)]); - } - } - - break; - - case 'noise_pixel_medium': - - for ($x = $min_x; $x < $max_x; $x += mt_rand(4, 9)) - { - for ($y = $min_y; $y < $max_y; $y += mt_rand(2, 5)) - { - imagesetpixel($img, $x, $y, $non_font[array_rand($non_font)]); - } - } - - for ($y = $min_y; $y < $max_y; $y += mt_rand(4, 9)) - { - for ($x = $min_x; $x < $max_x; $x += mt_rand(2, 5)) - { - imagesetpixel($img, $x, $y, $non_font[array_rand($non_font)]); - } - } - - break; - - case 'noise_pixel_heavy': - - for ($x = $min_x; $x < $max_x; $x += mt_rand(4, 9)) - { - for ($y = $min_y; $y < $max_y; $y++) - { - imagesetpixel($img, $x, $y, $non_font[array_rand($non_font)]); - } - } - - for ($y = $min_y; $y < $max_y; $y+= mt_rand(4, 9)) - { - for ($x = $min_x; $x < $max_x; $x++) - { - imagesetpixel($img, $x, $y, $non_font[array_rand($non_font)]); - } - } - - break; - } - } - - /** - * Noise line - */ - function noise_line($img, $min_x, $min_y, $max_x, $max_y, $bg, $font, $non_font) - { - imagesetthickness($img, 2); - $x1 = $min_x; - $x2 = $max_x; - $y1 = $min_y; - $y2 = $min_y; - - do - { - $line = array_merge( - array_fill(0, mt_rand(30, 60), $non_font[array_rand($non_font)]), - array_fill(0, mt_rand(30, 60), $bg) - ); - - imagesetstyle($img, $line); - imageline($img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED); - - $y1 += mt_rand(12, 35); - $y2 += mt_rand(12, 35); - } - while ($y1 < $max_y && $y2 < $max_y); - - $x1 = $min_x; - $x2 = $min_x; - $y1 = $min_y; - $y2 = $max_y; - - do - { - $line = array_merge( - array_fill(0, mt_rand(30, 60), $non_font[array_rand($non_font)]), - array_fill(0, mt_rand(30, 60), $bg) - ); - - imagesetstyle($img, $line); - imageline($img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED); - - $x1 += mt_rand(12, 35); - $x2 += mt_rand(12, 35); - } - while ($x1 < $max_x && $x2 < $max_x); - imagesetthickness($img, 1); - } - - /** - * Randomly determine which char class to use - * Able to define static one with override - */ - function captcha_char($override = false) - { - static $character_classes = array(); - - // Some people have GD but no TTF support - if (sizeof($character_classes) == 0) - { - $character_classes = array('char_vector', 'char_hatches', 'char_cube3d', 'char_dots'); - - if (function_exists('imagettfbbox') && function_exists('imagettftext')) - { - $character_classes[] = 'char_ttf'; - } - } - - // Use the module $override, else a random picked one... - $class = ($override !== false && in_array($override, $character_classes)) ? $override : $character_classes[array_rand($character_classes)]; - - return $class; - } -} - -/** -* @package VC -*/ -class char_dots -{ - var $vectors; - var $space; - var $radius; - var $letter; - var $width_percent; - - /** - * Constuctor - */ - function char_dots($letter = '', $args = false) - { - $width_percent = false; - if (is_array($args)) - { - $width_percent = (!empty($args['width_percent'])) ? $args['width_percent'] : false; - } - - $this->vectors = captcha_vectors(); - $this->width_percent = (!empty($width_percent)) ? max(25, min(150, intval($width_percent))) : mt_rand(60, 90); - - $this->space = 10; - $this->radius = 3; - $this->density = 3; - $this->letter = $letter; - } - - /** - * Draw a character - */ - function drawchar($scale, $xoff, $yoff, $img, $background, $colors) - { - $vectorset = $this->vectors[$this->letter]; - $height = $scale; - $width = (($scale * $this->width_percent) / 100); - $color = $colors[array_rand($colors)]; - - if (sizeof($vectorset)) - { - foreach ($vectorset as $veclist) - { - switch ($veclist[0]) - { - case 'line': - - $dx = ($veclist[3] - $veclist[1]) * $width; - $dy = ($veclist[4] - $veclist[2]) * -$height; - - $len = sqrt(($dx * $dx) + ($dy * $dy)); - - $inv_dx = -($dy / $len); - $inv_dy = ($dx / $len); - - for ($i = 0; $i < $len; ++$i) - { - for ($k = 0; $k <= $this->density; ++$k) - { - $shift = mt_rand(-$this->radius, $this->radius); - imagesetpixel($img, - $xoff + ($veclist[1] * $width) + (($i * $dx) / $len) + ($inv_dx * $shift), - $yoff + ((1 - $veclist[2]) * $height) + (($i * $dy) / $len) + ($inv_dy * $shift), - $color); - } - } - - break; - - case 'arc': - - $arclengthdeg = $veclist[6] - $veclist[5]; - $arclengthdeg += ( $arclengthdeg < 0 ) ? 360 : 0; - - $arclength = ((($veclist[3] * $width) + ($veclist[4] * $height)) * M_PI) / 2; - - $arclength = ($arclength * $arclengthdeg) / 360; - - $x_c = $veclist[1] * $width; - $y_c = (1 - $veclist[2]) * $height; - $increment = ($arclengthdeg / $arclength); - - for ($i = 0; $i < $arclengthdeg; $i += $increment) - { - $theta = deg2rad(($i + $veclist[5]) % 360); - $x_o = cos($theta); - $y_o = sin($theta); - $pre_width = ($veclist[3] * 0.5 * $width); - $pre_height = ($veclist[4] * 0.5 * $height); - for ($k = 0; $k <= $this->density; ++$k) - { - $shift = mt_rand(-$this->radius, $this->radius); - $x_o1 = $x_o * ($pre_width + $shift); - $y_o1 = $y_o * ($pre_height + $shift); - imagesetpixel($img, - $xoff + $x_c + $x_o1, - $yoff + $y_c + $y_o1, - $color); - } - } - - break; - - default: - // Do nothing with bad input - break; - } } } - } - - /* - * return a roughly acceptable range of sizes for rendering with this texttype - */ - function range() - { - return array(60, 80); - } - /** - * dimensions - */ - function dimensions($size) - { - return array(-4, -4, (($size * $this->width_percent) / 100) + 4, $size + 4); - } -} -/** -* @package VC -*/ -class char_vector -{ - var $vectors; - var $width_percent; - var $letter; - - /** - * Constructor - */ - function char_vector($letter = '', $args = false) - { - $width_percent = false; - if (is_array($args)) - { - $width_percent = (!empty($args['width_percent'])) ? $args['width_percent'] : false; - } - - $this->vectors = captcha_vectors(); - $this->width_percent = (!empty($width_percent)) ? max(25, min(150, intval($width_percent))) : mt_rand(60,90); - $this->letter = $letter; - } - - /** - * Draw a character - */ - function drawchar($scale, $xoff, $yoff, $img, $background, $colors) - { - $vectorset = $this->vectors[$this->letter]; - $height = $scale; - $width = (($scale * $this->width_percent) / 100); - $color = $colors[array_rand($colors)]; - - if (sizeof($vectorset)) + // fill with noise or grid + if ($config['captcha_gd_noise']) { - foreach ($vectorset as $veclist) + // random characters in background with random position, angle, color + for ($i = 0 ; $i < 72; $i++) { - for ($i = 0; $i < 9; ++$i) - { - $xp = $i % 3; - $yp = ($i - $xp) / 3; - $xp--; - $yp--; - - switch ($veclist[0]) - { - case 'line': - imageline($img, - $xoff + $xp + ($veclist[1] * $width), - $yoff + $yp + ((1 - $veclist[2]) * $height), - $xoff + $xp + ($veclist[3] * $width), - $yoff + $yp + ((1 - $veclist[4]) * $height), - $color - ); - break; - - case 'arc': - imagearc($img, - $xoff + $xp + ($veclist[1] * $width), - $yoff + $yp + ((1 - $veclist[2]) * $height), - $veclist[3] * $width, - $veclist[4] * $height, - $veclist[5], - $veclist[6], - $color - ); - break; - } - } + $size = mt_rand(8, 23); + $angle = mt_rand(0, 360); + $x = mt_rand(0, 360); + $y = mt_rand(0, (int)($ly - ($size / 5))); + $color = $func2($image, mt_rand(160, 224), mt_rand(160, 224), mt_rand(160, 224)); + $text = chr(mt_rand(45, 250)); + imagettftext($image, $size, $angle, $x, $y, $color, $this->get_font(), $text); } } - } - - /* - * return a roughly acceptable range of sizes for rendering with this texttype - */ - function range() - { - return array(50, 80); - } - - /** - * dimensions - */ - function dimensions($size) - { - return array(-2, -2, (($size * $this->width_percent) / 100 ) + 2, $size + 2); - } -} - -/** -* @package VC -*/ -class char_ttf -{ - var $angle = 0; - var $fontfile = ''; - var $letter = ''; - - /** - * Constructor - */ - function char_ttf($letter = '', $args = false) - { - $font = $angle = false; - - if (is_array($args)) - { - $font = (!empty($args['font'])) ? $args['font'] : false; - $angle = (isset($args['angle'])) ? $args['angle'] : false; - } - - $fonts = captcha_load_ttf_fonts(); - - if (empty($font) || !isset($fonts[$font])) - { - $font = array_rand($fonts); - } - - $this->fontfile = $fonts[$font]; - $this->angle = ($angle !== false) ? intval($angle) : mt_rand(-40, 40); - $this->letter = $letter; - } - - /** - * Draw a character - */ - function drawchar($scale, $xoff, $yoff, $img, $background, $colors) - { - $color = $colors[array_rand($colors)]; - imagettftext($img, $scale, $this->angle, $xoff, $yoff, $color, $this->fontfile, $this->letter); - } - - /* - * return a roughly acceptable range of sizes for rendering with this texttype - */ - function range() - { - return array(36, 150); - } - - /** - * Dimensions - */ - function dimensions($scale) - { - $data = imagettfbbox($scale, $this->angle, $this->fontfile, $this->letter); - return ($this->angle > 0) ? array($data[6], $data[5], $data[2], $data[1]) : array($data[0], $data[7], $data[4], $data[3]); - } -} - -/** -* @package VC -*/ -class char_hatches -{ - var $vectors; - var $space; - var $radius; - var $letter; - - /** - * Constructor - */ - function char_hatches($letter = '', $args = false) - { - $width_percent = false; - if (is_array($args)) - { - $width_percent = (!empty($args['width_percent'])) ? $args['width_percent'] : false; - } - - $this->vectors = captcha_vectors(); - $this->width_percent = (!empty($width_percent)) ? max(25, min(150, intval($width_percent))) : mt_rand(60, 90); - - $this->space = 10; - $this->radius = 3; - $this->letter = $letter; - } - - /** - * Draw a character - */ - function drawchar($scale, $xoff, $yoff, $img, $background, $colors) - { - $vectorset = $this->vectors[$this->letter]; - $height = $scale; - $width = (($scale * $this->width_percent) / 100); - $color = $colors[array_rand($colors)]; - - if (sizeof($vectorset)) + else { - foreach ($vectorset as $veclist) + // generate grid + for ($i = 0; $i < $lx; $i += 13) { - switch ($veclist[0]) - { - case 'line': - $dx = ($veclist[3] - $veclist[1]) * $width; - $dy = ($veclist[4] - $veclist[2]) * -$height; - - $idx = -$dy; - $idy = $dx; - - $length = sqrt(pow($dx, 2) + pow($dy, 2)); - - $hatches = $length / $this->space; - - for ($p = 0; $p <= $hatches; ++$p) - { - if (!$p && !mt_rand(0, 9) && ($hatches > 3)) - { - continue; - } - - $xp = 1; - $yp = -2; - for ($i = 0; $i < 9; ++$i) - { - $xp += !($i % 3) ? -2 : 1; - $yp += !($i % 3) ? 1 : 0; - - $x_o = ((($p * $veclist[1]) + (($hatches - $p) * $veclist[3])) * $width ) / $hatches; - $y_o = $height - (((($p * $veclist[2]) + (($hatches - $p) * $veclist[4])) * $height ) / $hatches); - $x_1 = $xoff + $xp + $x_o; - $y_1 = $yoff + $yp + $y_o; - - $x_d1 = (($dx - $idx) * $this->radius) / $length; - $y_d1 = (($dy - $idy) * $this->radius) / $length; - - $x_d2 = (($dx - $idx) * -$this->radius) / $length; - $y_d2 = (($dy - $idy) * -$this->radius) / $length; - - imageline($img, $x_1 + $x_d1, $y_1 + $y_d1, $x_1 + $x_d2, $y_1 + $y_d2, $color); - } - } - break; - - case 'arc': - $arclengthdeg = $veclist[6] - $veclist[5]; - $arclengthdeg += ( $arclengthdeg < 0 ) ? 360 : 0; - - $arclength = ((($veclist[3] * $width) + ($veclist[4] * $height)) * M_PI) / 2; - $arclength = ($arclength * $arclengthdeg) / 360; - - $hatches = $arclength / $this->space; - - $hatchdeg = ($arclengthdeg * $this->space) / $arclength; - $shiftdeg = ($arclengthdeg * $this->radius) / $arclength; - - $x_c = $veclist[1] * $width; - $y_c = (1 - $veclist[2]) * $height; - - for ($p = 0; $p <= $arclengthdeg; $p += $hatchdeg) - { - if (!mt_rand(0, 9) && ($hatches > 3) && !$p) - { - continue; - } - - $theta1 = deg2rad(($p + $veclist[5] - $shiftdeg) % 360); - $theta2 = deg2rad(($p + $veclist[5] + $shiftdeg) % 360); - $x_o1 = cos($theta1) * (($veclist[3] * 0.5 * $width) - $this->radius); - $y_o1 = sin($theta1) * (($veclist[4] * 0.5 * $height) - $this->radius); - $x_o2 = cos($theta2) * (($veclist[3] * 0.5 * $width) + $this->radius); - $y_o2 = sin($theta2) * (($veclist[4] * 0.5 * $height) + $this->radius); - - $xp = 1; - $yp = -2; - for ($i = 0; $i < 9; ++$i) - { - $xp += !($i % 3) ? -2 : 1; - $yp += !($i % 3) ? 1 : 0; - - imageline($img, - $xoff + $xp + $x_c + $x_o1, - $yoff + $yp + $y_c + $y_o1, - $xoff + $xp + $x_c + $x_o2, - $yoff + $yp + $y_c + $y_o2, - $color - ); - } - } - break; - } + $color = $func2($image, mt_rand(160, 224), mt_rand(160, 224), mt_rand(160, 224)); + imageline($image, $i, 0, $i, $ly, $color); } - } - } - - /* - * return a roughly acceptable range of sizes for rendering with this texttype - */ - function range() - { - return array(60, 80); - } - - /** - * Dimensions - */ - function dimensions($size) - { - return array(-4, -4, (($size * $this->width_percent) / 100) + 4, $size + 4); - } -} - -/** -* @package VC -*/ -class char_cube3d -{ - // need to abstract out the cube3d from the cubechar - var $bitmaps; - - var $basis_matrix = array(array(1, 0, 0), array(0, 1, 0), array(0, 0, 1)); - var $abs_x = array(1, 0); - var $abs_y = array(0, 1); - var $x = 0; - var $y = 1; - var $z = 2; - var $letter = ''; - - function char_cube3d($letter) - { - $this->bitmaps = captcha_bitmaps(); - - $this->basis_matrix[0][0] = mt_rand(-600, 600); - $this->basis_matrix[0][1] = mt_rand(-600, 600); - $this->basis_matrix[0][2] = (mt_rand(0, 1) * 2000) - 1000; - $this->basis_matrix[1][0] = mt_rand(-1000, 1000); - $this->basis_matrix[1][1] = mt_rand(-1000, 1000); - $this->basis_matrix[1][2] = mt_rand(-1000, 1000); - - $this->normalize($this->basis_matrix[0]); - $this->normalize($this->basis_matrix[1]); - $this->basis_matrix[2] = $this->cross_product($this->basis_matrix[0], $this->basis_matrix[1]); - $this->normalize($this->basis_matrix[2]); - - // $this->basis_matrix[1] might not be (probably isn't) orthogonal to $basis_matrix[0] - $this->basis_matrix[1] = $this->cross_product($this->basis_matrix[0], $this->basis_matrix[2]); - $this->normalize($this->basis_matrix[1]); - - // Make sure our cube is facing into the canvas (assuming +z == in) - for ($i = 0; $i < 3; ++$i) - { - if ($this->basis_matrix[$i][2] < 0) + for ($i = 0; $i < $ly; $i += 11) { - $this->basis_matrix[$i][0] *= -1; - $this->basis_matrix[$i][1] *= -1; - $this->basis_matrix[$i][2] *= -1; + $color = $func2($image, mt_rand(160, 224), mt_rand(160, 224), mt_rand(160, 224)); + imageline($image, 0, $i, $lx, $i, $color); } } - // Force our "z" basis vector to be the one with greatest absolute z value - $this->x = 0; - $this->y = 1; - $this->z = 2; + $len = strlen($code); - // Swap "y" with "z" - if ($this->basis_matrix[1][2] > $this->basis_matrix[2][2]) + for ($i = 0, $x = mt_rand(20, 40); $i < $len; $i++) { - $this->z = 1; - $this->y = 2; - } + $text = strtoupper($code[$i]); + $angle = mt_rand(-30, 30); + $size = mt_rand(20, 40); + $y = mt_rand((int)($size * 1.5), (int)($ly - ($size / 7))); - // Swap "x" with "z" - if ($this->basis_matrix[0][2] > $this->basis_matrix[$this->z][2]) - { - $this->x = $this->z; - $this->z = 0; - } + $color = $func2($image, mt_rand(0, 127), mt_rand(0, 127), mt_rand(0, 127)); + $shadow = $func2($image, mt_rand(127, 254), mt_rand(127, 254), mt_rand(127, 254)); + $font = $this->get_font(); - // Still need to determine which of $x,$y are which. - // wrong orientation if y's y-component is less than it's x-component - // likewise if x's x-component is less than it's y-component - // if they disagree, go with the one with the greater weight difference. - // rotate if positive - $weight = (abs($this->basis_matrix[$this->x][1]) - abs($this->basis_matrix[$this->x][0])) + - (abs($this->basis_matrix[$this->y][0]) - abs($this->basis_matrix[$this->y][1])); + imagettftext($image, $size, $angle, $x + (int)($size / 15), $y, $shadow, $font, $text); + imagettftext($image, $size, $angle, $x, $y - (int)($size / 15), $color, $font, $text); - // Swap "x" with "y" - if ($weight > 0) - { - list($this->x, $this->y) = array($this->y, $this->x); + $x += $size + 4; } - $this->abs_x = array($this->basis_matrix[$this->x][0], $this->basis_matrix[$this->x][1]); - $this->abs_y = array($this->basis_matrix[$this->y][0], $this->basis_matrix[$this->y][1]); - - if ($this->abs_x[0] < 0) - { - $this->abs_x[0] *= -1; - $this->abs_x[1] *= -1; - } - - if ($this->abs_y[1] > 0) - { - $this->abs_y[0] *= -1; - $this->abs_y[1] *= -1; - } - - $this->letter = $letter; - } - - /** - * - */ - function draw($im, $scale, $xoff, $yoff, $face, $xshadow, $yshadow) - { - $origin = array(0, 0, 0); - $xvec = $this->scale($this->basis_matrix[$this->x], $scale); - $yvec = $this->scale($this->basis_matrix[$this->y], $scale); - $face_corner = $this->sum2($xvec, $yvec); - - $zvec = $this->scale($this->basis_matrix[$this->z], $scale); - $x_corner = $this->sum2($xvec, $zvec); - $y_corner = $this->sum2($yvec, $zvec); - - imagefilledpolygon($im, $this->gen_poly($xoff, $yoff, $origin, $xvec, $x_corner, $zvec), 4, $yshadow); - imagefilledpolygon($im, $this->gen_poly($xoff, $yoff, $origin, $yvec, $y_corner, $zvec), 4, $xshadow); - imagefilledpolygon($im, $this->gen_poly($xoff, $yoff, $origin, $xvec, $face_corner, $yvec), 4, $face); - } - - /** - * Draw a character - */ - function drawchar($scale, $xoff, $yoff, $img, $background, $colors) - { - $width = $this->bitmaps['width']; - $height = $this->bitmaps['height']; - $bitmap = $this->bitmaps['data'][$this->letter]; - - $color1 = $colors[array_rand($colors)]; - $color2 = $colors[array_rand($colors)]; - - $swapx = ($this->basis_matrix[$this->x][0] > 0); - $swapy = ($this->basis_matrix[$this->y][1] < 0); - - for ($y = 0; $y < $height; ++$y) - { - for ($x = 0; $x < $width; ++$x) - { - $xp = ($swapx) ? ($width - $x - 1) : $x; - $yp = ($swapy) ? ($height - $y - 1) : $y; - - if ($bitmap[$height - $yp - 1][$xp]) - { - $dx = $this->scale($this->abs_x, ($xp - ($swapx ? ($width / 2) : ($width / 2) - 1)) * $scale); - $dy = $this->scale($this->abs_y, ($yp - ($swapy ? ($height / 2) : ($height / 2) - 1)) * $scale); - $xo = $xoff + $dx[0] + $dy[0]; - $yo = $yoff + $dx[1] + $dy[1]; - - $origin = array(0, 0, 0); - $xvec = $this->scale($this->basis_matrix[$this->x], $scale); - $yvec = $this->scale($this->basis_matrix[$this->y], $scale); - $face_corner = $this->sum2($xvec, $yvec); - - $zvec = $this->scale($this->basis_matrix[$this->z], $scale); - $x_corner = $this->sum2($xvec, $zvec); - $y_corner = $this->sum2($yvec, $zvec); - - imagefilledpolygon($img, $this->gen_poly($xo, $yo, $origin, $xvec, $x_corner,$zvec), 4, $color1); - imagefilledpolygon($img, $this->gen_poly($xo, $yo, $origin, $yvec, $y_corner,$zvec), 4, $color2); - - $face = $this->gen_poly($xo, $yo, $origin, $xvec, $face_corner, $yvec); - - imagefilledpolygon($img, $face, 4, $background); - imagepolygon($img, $face, 4, $color1); - } - } - } - } - - /* - * return a roughly acceptable range of sizes for rendering with this texttype - */ - function range() - { - return array(5, 10); - } - - /** - * Vector length - */ - function vectorlen($vector) - { - return sqrt(pow($vector[0], 2) + pow($vector[1], 2) + pow($vector[2], 2)); - } - - /** - * Normalize - */ - function normalize(&$vector, $length = 1) - { - $length = (( $length < 1) ? 1 : $length); - $length /= $this->vectorlen($vector); - $vector[0] *= $length; - $vector[1] *= $length; - $vector[2] *= $length; - } - - /** - * - */ - function cross_product($vector1, $vector2) - { - $retval = array(0, 0, 0); - $retval[0] = (($vector1[1] * $vector2[2]) - ($vector1[2] * $vector2[1])); - $retval[1] = -(($vector1[0] * $vector2[2]) - ($vector1[2] * $vector2[0])); - $retval[2] = (($vector1[0] * $vector2[1]) - ($vector1[1] * $vector2[0])); - - return $retval; - } - - /** - * - */ - function sum($vector1, $vector2) - { - return array($vector1[0] + $vector2[0], $vector1[1] + $vector2[1], $vector1[2] + $vector2[2]); - } - - /** - * - */ - function sum2($vector1, $vector2) - { - return array($vector1[0] + $vector2[0], $vector1[1] + $vector2[1]); - } - - /** - * - */ - function scale($vector, $length) - { - if (sizeof($vector) == 2) - { - return array($vector[0] * $length, $vector[1] * $length); - } - - return array($vector[0] * $length, $vector[1] * $length, $vector[2] * $length); - } - - /** - * - */ - function gen_poly($xoff, $yoff, &$vec1, &$vec2, &$vec3, &$vec4) - { - $poly = array(); - $poly[0] = $xoff + $vec1[0]; - $poly[1] = $yoff + $vec1[1]; - $poly[2] = $xoff + $vec2[0]; - $poly[3] = $yoff + $vec2[1]; - $poly[4] = $xoff + $vec3[0]; - $poly[5] = $yoff + $vec3[1]; - $poly[6] = $xoff + $vec4[0]; - $poly[7] = $yoff + $vec4[1]; - - return $poly; - } - - /** - * dimensions - */ - function dimensions($size) - { - $xn = $this->scale($this->basis_matrix[$this->x], -($this->bitmaps['width'] / 2) * $size); - $xp = $this->scale($this->basis_matrix[$this->x], ($this->bitmaps['width'] / 2) * $size); - $yn = $this->scale($this->basis_matrix[$this->y], -($this->bitmaps['height'] / 2) * $size); - $yp = $this->scale($this->basis_matrix[$this->y], ($this->bitmaps['height'] / 2) * $size); - - $p = array(); - $p[0] = $this->sum2($xn, $yn); - $p[1] = $this->sum2($xp, $yn); - $p[2] = $this->sum2($xp, $yp); - $p[3] = $this->sum2($xn, $yp); - - $min_x = $max_x = $p[0][0]; - $min_y = $max_y = $p[0][1]; - - for ($i = 1; $i < 4; ++$i) - { - $min_x = ($min_x > $p[$i][0]) ? $p[$i][0] : $min_x; - $min_y = ($min_y > $p[$i][1]) ? $p[$i][1] : $min_y; - $max_x = ($max_x < $p[$i][0]) ? $p[$i][0] : $max_x; - $max_y = ($max_y < $p[$i][1]) ? $p[$i][1] : $max_y; - } - - return array($min_x, $min_y, $max_x, $max_y); - } -} - -/** -* Return bitmaps -*/ -function captcha_bitmaps() -{ - return array( - 'width' => 9, - 'height' => 15, - 'data' => array( - 'A' => array( - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,1,0,1,0,0,0), - array(0,0,0,1,0,1,0,0,0), - array(0,0,0,1,0,1,0,0,0), - array(0,0,1,0,0,0,1,0,0), - array(0,0,1,0,0,0,1,0,0), - array(0,0,1,0,0,0,1,0,0), - array(0,1,0,0,0,0,0,1,0), - array(0,1,0,0,0,0,0,1,0), - array(0,1,1,1,1,1,1,1,0), - array(0,1,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - ), - 'B' => array( - array(1,1,1,1,1,1,1,0,0), - array(1,0,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,1,0), - array(1,1,1,1,1,1,1,0,0), - array(1,0,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,1,0), - array(1,1,1,1,1,1,1,0,0), - ), - 'C' => array( - array(0,0,1,1,1,1,1,0,0), - array(0,1,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,1,1,1,1,0,0), - ), - 'D' => array( - array(1,1,1,1,1,1,1,0,0), - array(1,0,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,1,0), - array(1,1,1,1,1,1,1,0,0), - ), - 'E' => array( - array(1,1,1,1,1,1,1,1,1), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,1,1,1,1,1,1,1,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,1,1,1,1,1,1,1,1), - ), - 'F' => array( - array(1,1,1,1,1,1,1,1,1), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,1,1,1,1,1,1,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - ), - 'G' => array( - array(0,0,1,1,1,1,1,0,0), - array(0,1,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,1,1,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,1,1,1,1,0,0), - ), - 'H' => array( - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,1,1,1,1,1,1,1,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - ), - 'I' => array( - array(1,1,1,1,1,1,1,1,1), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(1,1,1,1,1,1,1,1,1), - ), - 'J' => array( - array(1,1,1,1,1,1,1,1,1), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,0,1,0,0,0), - array(1,0,0,0,0,1,0,0,0), - array(1,0,0,0,0,1,0,0,0), - array(0,1,0,0,1,0,0,0,0), - array(0,0,1,1,0,0,0,0,0), - ), - 'K' => array( // New 'K', supplied by NeoThermic - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,1,0,0), - array(1,0,0,0,0,1,0,0,0), - array(1,0,0,0,1,0,0,0,0), - array(1,0,0,1,0,0,0,0,0), - array(1,0,1,0,0,0,0,0,0), - array(1,1,0,0,0,0,0,0,0), - array(1,0,1,0,0,0,0,0,0), - array(1,0,0,1,0,0,0,0,0), - array(1,0,0,0,1,0,0,0,0), - array(1,0,0,0,0,1,0,0,0), - array(1,0,0,0,0,0,1,0,0), - array(1,0,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - ), - 'L' => array( - array(0,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,1,1,1,1,1,1,1,1), - ), - 'M' => array( - array(1,1,0,0,0,0,0,1,1), - array(1,1,0,0,0,0,0,1,1), - array(1,0,1,0,0,0,1,0,1), - array(1,0,1,0,0,0,1,0,1), - array(1,0,1,0,0,0,1,0,1), - array(1,0,0,1,0,1,0,0,1), - array(1,0,0,1,0,1,0,0,1), - array(1,0,0,1,0,1,0,0,1), - array(1,0,0,0,1,0,0,0,1), - array(1,0,0,0,1,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - ), - 'N' => array( - array(1,1,0,0,0,0,0,0,1), - array(1,1,0,0,0,0,0,0,1), - array(1,0,1,0,0,0,0,0,1), - array(1,0,1,0,0,0,0,0,1), - array(1,0,0,1,0,0,0,0,1), - array(1,0,0,1,0,0,0,0,1), - array(1,0,0,0,1,0,0,0,1), - array(1,0,0,0,1,0,0,0,1), - array(1,0,0,0,1,0,0,0,1), - array(1,0,0,0,0,1,0,0,1), - array(1,0,0,0,0,1,0,0,1), - array(1,0,0,0,0,0,1,0,1), - array(1,0,0,0,0,0,1,0,1), - array(1,0,0,0,0,0,0,1,1), - array(1,0,0,0,0,0,0,1,1), - ), - 'O' => array( - array(0,0,1,1,1,1,1,0,0), - array(0,1,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,1,1,1,1,0,0), - ), - 'P' => array( - array(1,1,1,1,1,1,1,0,0), - array(1,0,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,1,0), - array(1,1,1,1,1,1,1,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - ), - 'Q' => array( - array(0,0,1,1,1,1,1,0,0), - array(0,1,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,1,0,0,1), - array(1,0,0,0,0,0,1,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,1,1,1,1,0,1), - ), - 'R' => array( - array(1,1,1,1,1,1,1,0,0), - array(1,0,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,1,0), - array(1,1,1,1,1,1,1,0,0), - array(1,1,1,0,0,0,0,0,0), - array(1,0,0,1,0,0,0,0,0), - array(1,0,0,0,1,0,0,0,0), - array(1,0,0,0,0,1,0,0,0), - array(1,0,0,0,0,0,1,0,0), - array(1,0,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - ), - 'S' => array( - array(0,0,1,1,1,1,1,0,0), - array(0,1,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(0,1,0,0,0,0,0,0,0), - array(0,0,1,1,1,1,1,0,0), - array(0,0,0,0,0,0,0,1,0), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,1,1,1,1,0,0), - ), - 'T' => array( - array(1,1,1,1,1,1,1,1,1), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - ), - 'U' => array( - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,1,1,1,1,0,0), - ), - 'V' => array( - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,1,0,0,0,0,0,1,0), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,0,0,0,1,0,0), - array(0,0,1,0,0,0,1,0,0), - array(0,0,1,0,0,0,1,0,0), - array(0,0,1,0,0,0,1,0,0), - array(0,0,0,1,0,1,0,0,0), - array(0,0,0,1,0,1,0,0,0), - array(0,0,0,1,0,1,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - ), - 'W' => array( // New 'W', supplied by MHobbit - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,1,0,0,0,1), - array(1,0,0,0,1,0,0,0,1), - array(1,0,0,1,0,1,0,0,1), - array(1,0,0,1,0,1,0,0,1), - array(1,0,0,1,0,1,0,0,1), - array(1,0,1,0,0,0,1,0,1), - array(1,0,1,0,0,0,1,0,1), - array(1,0,1,0,0,0,1,0,1), - array(1,1,0,0,0,0,0,1,1), - array(1,1,0,0,0,0,0,1,1), - ), - 'X' => array( - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,0,0,0,1,0,0), - array(0,0,0,1,0,1,0,0,0), - array(0,0,0,1,0,1,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,1,0,1,0,0,0), - array(0,0,0,1,0,1,0,0,0), - array(0,0,1,0,0,0,1,0,0), - array(0,1,0,0,0,0,1,0,0), - array(0,1,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - ), - 'Y' => array( - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,0,0,0,1,0,0), - array(0,0,1,0,0,0,1,0,0), - array(0,0,0,1,0,1,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - ), - 'Z' => array( // New 'Z' supplied by Anon - array(1,1,1,1,1,1,1,1,1), - array(1,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,1,0), - array(0,0,0,0,0,0,1,0,0), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,1,0,0,0,0,0), - array(0,0,0,1,0,0,0,0,0), - array(0,0,1,0,0,0,0,0,0), - array(0,1,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,1), - array(1,1,1,1,1,1,1,1,1), - ), - '1' => array( - array(0,0,0,1,1,0,0,0,0), - array(0,0,1,0,1,0,0,0,0), - array(0,1,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,1,1,1,1,1,1,1,0), - ), - '2' => array( // New '2' supplied by Anon - array(0,0,0,1,1,1,0,0,0), - array(0,0,1,0,0,0,1,0,0), - array(0,1,0,0,0,0,1,1,0), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,1,1), - array(0,0,0,0,0,0,0,1,0), - array(0,0,0,0,0,0,1,0,0), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,1,0,0,0,0,0), - array(0,0,1,0,0,0,0,0,0), - array(0,1,0,0,0,0,0,0,0), - array(1,1,1,1,1,1,1,1,1), - array(0,0,0,0,0,0,0,0,0), - ), - '3' => array( - array(0,0,1,1,1,1,1,0,0), - array(0,1,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,1,0), - array(0,0,0,0,0,1,1,0,0), - array(0,0,0,0,0,0,0,1,0), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,1,1,1,1,0,0), - ), - '4' => array( - array(0,0,0,0,0,0,1,1,0), - array(0,0,0,0,0,1,0,1,0), - array(0,0,0,0,1,0,0,1,0), - array(0,0,0,1,0,0,0,1,0), - array(0,0,1,0,0,0,0,1,0), - array(0,1,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,1,0), - array(1,1,1,1,1,1,1,1,1), - array(0,0,0,0,0,0,0,1,0), - array(0,0,0,0,0,0,0,1,0), - array(0,0,0,0,0,0,0,1,0), - array(0,0,0,0,0,0,0,1,0), - array(0,0,0,0,0,0,0,1,0), - array(0,0,0,0,0,0,0,1,0), - ), - '5' => array( - array(1,1,1,1,1,1,1,1,1), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(0,1,0,0,0,0,0,0,0), - array(0,0,1,1,1,1,1,0,0), - array(0,0,0,0,0,0,0,1,0), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,1,1,1,1,0,0), - ), - '6' => array( - array(0,0,1,1,1,1,1,0,0), - array(0,1,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,1,1,1,1,0,0), - array(1,0,1,0,0,0,0,1,0), - array(1,1,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,1,1,1,1,0,0), - ), - '7' => array( - array(1,1,1,1,1,1,1,1,1), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,1,0), - array(0,0,0,0,0,0,0,1,0), - array(0,0,0,0,0,0,1,0,0), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,0,1,0,0,0), - array(0,0,0,0,1,0,0,0,0), - array(0,0,0,1,0,0,0,0,0), - array(0,0,0,1,0,0,0,0,0), - array(0,0,1,0,0,0,0,0,0), - array(0,1,0,0,0,0,0,0,0), - array(0,1,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - array(1,0,0,0,0,0,0,0,0), - ), - '8' => array( - array(0,0,1,1,1,1,1,0,0), - array(0,1,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,1,1,1,1,0,0), - array(0,1,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,1,1,1,1,0,0), - ), - '9' => array( - array(0,0,1,1,1,1,1,0,0), - array(0,1,0,0,0,0,0,1,0), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,1,1), - array(0,1,0,0,0,0,1,0,1), - array(0,0,1,1,1,1,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(0,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(1,0,0,0,0,0,0,0,1), - array(0,1,0,0,0,0,0,1,0), - array(0,0,1,1,1,1,1,0,0), - ), - ) - ); -} - - -/** -* Load True Type Fonts -*/ -function captcha_load_ttf_fonts() -{ - static $load_files = array(); - - if (sizeof($load_files) > 0) - { - return $load_files; + // Output image + header('Content-Type: image/png'); + header('Cache-control: no-cache, no-store'); + imagepng($image); + imagedestroy($image); } - global $phpbb_root_path; - - $dr = opendir($phpbb_root_path . 'includes/captcha/fonts'); - while (false !== ($entry = readdir($dr))) + function get_font() { - if (strtolower(pathinfo($entry, PATHINFO_EXTENSION)) == 'ttf') - { - $load_files[$entry] = $phpbb_root_path . 'includes/captcha/fonts/' . $entry; - } - } - closedir($dr); - - return $load_files; -} - - -/** -* Return vectors -*/ -function captcha_vectors() -{ - return array( - 'A' => array( - array('line', 0.00, 0.00, 0.50, 1.00, 1.10714871779, 1.11803398875), - array('line', 1.00, 0.00, 0.50, 1.00, 2.0344439358, 1.11803398875), - array('line', 0.25, 0.50, 0.75, 0.50, 0.00, 0.50), - ), - 'B' => array( - array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00), - array('line', 0.00, 1.00, 0.70, 1.00, 0.00, 0.70), - array('line', 0.00, 0.50, 0.70, 0.50, 0.00, 0.70), - array('line', 0.00, 0.00, 0.70, 0.00, 0.00, 0.70), - array('arc', 0.70, 0.75, 0.60, 0.50, 270, 90), - array('arc', 0.70, 0.25, 0.60, 0.50, 270, 90), - ), - 'C' => array( - array('arc', 0.50, 0.50, 1.00, 1.00, 45, 315), - ), - 'D' => array( - array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00), - array('line', 0.00, 0.00, 0.50, 0.00, 0.00, 0.50), - array('line', 0.00, 1.00, 0.50, 1.00, 0.00, 0.50), - array('arc', 0.50, 0.50, 1.00, 1.00, 270, 90), - ), - 'E' => array( - array('line', 0.00, 0.00, 1.00, 0.00, 0.00, 1.00), - array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00), - array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00), - array('line', 0.00, 0.50, 0.50, 0.50, 0.00, 0.50), - ), - 'F' => array( - array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00), - array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00), - array('line', 0.00, 0.50, 0.50, 0.50, 0.00, 0.50), - ), - 'G' => array( - array('line', 0.50, 0.50, 1.00, 0.50, 0.00, 0.50), - array('line', 1.00, 0.00, 1.00, 0.50, 1.57079632679, 0.50), - array('arc', 0.50, 0.50, 1.00, 1.00, 0, 315), - ), - 'H' => array( - array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00), - array('line', 1.00, 0.00, 1.00, 1.00, 1.57079632679, 1.00), - array('line', 0.00, 0.50, 1.00, 0.50, 0.00, 1.00), - ), - 'I' => array( - array('line', 0.00, 0.00, 1.00, 0.00, 0.00, 1.00), - array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00), - array('line', 0.50, 0.00, 0.50, 1.00, 1.57079632679, 1.00), - ), - 'J' => array( - array('line', 1.00, 1.00, 1.00, 0.25, -1.57079632679, 0.75), - array('arc', 0.50, 0.25, 1.00, 0.50, 0, 180), - ), - 'K' => array( - array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00), - array('line', 0.00, 0.50, 1.00, 1.00, 0.463647609001, 1.11803398875), - array('line', 0.00, 0.50, 1.00, 0.00, -0.463647609001, 1.11803398875), - ), - 'L' => array( - array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00), - array('line', 0.00, 0.00, 1.00, 0.00, 0.00, 1.00), - ), - 'M' => array( - array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00), - array('line', 0.50, 0.50, 0.00, 1.00, 2.35619449019, 0.707106781187), - array('line', 0.50, 0.50, 1.00, 1.00, 0.785398163397, 0.707106781187), - array('line', 1.00, 0.00, 1.00, 1.00, 1.57079632679, 1.00), - ), - 'N' => array( - array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00), - array('line', 0.00, 1.00, 1.00, 0.00, -0.785398163397, 1.41421356237), - array('line', 1.00, 0.00, 1.00, 1.00, 1.57079632679, 1.00), - ), - 'O' => array( - array('arc', 0.50, 0.50, 1.00, 1.00, 0, 360), - ), - 'P' => array( - array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00), - array('line', 0.00, 1.00, 0.70, 1.00, 0.00, 0.70), - array('line', 0.00, 0.50, 0.70, 0.50, 0.00, 0.70), - array('arc', 0.70, 0.75, 0.60, 0.50, 270, 90), - ), - 'Q' => array( - array('line', 0.70, 0.30, 1.00, 0.00, -0.785398163397, 0.424264068712), - array('arc', 0.50, 0.50, 1.00, 1.00, 0, 360), - ), - 'R' => array( - array('line', 0.00, 0.00, 0.00, 1.00, 1.57079632679, 1.00), - array('line', 0.00, 1.00, 0.70, 1.00, 0.00, 0.70), - array('line', 0.00, 0.50, 0.70, 0.50, 0.00, 0.70), - array('line', 0.50, 0.50, 1.00, 0.00, -0.785398163397, 0.707106781187), - array('arc', 0.70, 0.75, 0.60, 0.50, 270, 90), - ), - 'S' => array( - array('arc', 0.50, 0.75, 1.00, 0.50, 90, 360), - array('arc', 0.50, 0.25, 1.00, 0.50, 270, 180), - ), - 'T' => array( - array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00), - array('line', 0.50, 0.00, 0.50, 1.00, 1.57079632679, 1.00), - ), - 'U' => array( - array('line', 0.00, 1.00, 0.00, 0.25, -1.57079632679, 0.75), - array('line', 1.00, 1.00, 1.00, 0.25, -1.57079632679, 0.75), - array('arc', 0.50, 0.25, 1.00, 0.50, 0, 180), - ), - 'V' => array( - array('line', 0.00, 1.00, 0.50, 0.00, -1.10714871779, 1.11803398875), - array('line', 1.00, 1.00, 0.50, 0.00, -2.0344439358, 1.11803398875), - ), - 'W' => array( - array('line', 0.00, 1.00, 0.25, 0.00, -1.32581766367, 1.0307764064), - array('line', 0.50, 0.50, 0.25, 0.00, -2.0344439358, 0.559016994375), - array('line', 0.50, 0.50, 0.75, 0.00, -1.10714871779, 0.559016994375), - array('line', 1.00, 1.00, 0.75, 0.00, -1.81577498992, 1.0307764064), - ), - 'X' => array( - array('line', 0.00, 1.00, 1.00, 0.00, -0.785398163397, 1.41421356237), - array('line', 0.00, 0.00, 1.00, 1.00, 0.785398163397, 1.41421356237), - ), - 'Y' => array( - array('line', 0.00, 1.00, 0.50, 0.50, -0.785398163397, 0.707106781187), - array('line', 1.00, 1.00, 0.50, 0.50, -2.35619449019, 0.707106781187), - array('line', 0.50, 0.50, 0.50, 0.00, -1.57079632679, 0.50), - ), - 'Z' => array( - array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00), - array('line', 0.00, 0.00, 1.00, 1.00, 0.785398163397, 1.41421356237), - array('line', 0.00, 0.00, 1.00, 0.00, 0.00, 1.00), - ), - '1' => array( - array('line', 0.00, 0.75, 0.50, 1.00, 0.463647609001, 0.559016994375), - array('line', 0.50, 0.00, 0.50, 1.00, 1.57079632679, 1.00), - array('line', 0.00, 0.00, 1.00, 0.00, 0.00, 1.00), - ), - '2' => array( - array('line', 0.00, 0.00, 1.00, 0.00, 0.00, 1.00), - array('arc', 0.50, 0.70, 1.00, 0.60, 180, 360), - array('arc', 0.50, 0.70, 1.00, 0.70, 0, 90), - array('arc', 0.50, 0.00, 1.00, 0.70, 180, 270), - ), - '3' => array( - array('arc', 0.50, 0.75, 1.00, 0.50, 180, 90), - array('arc', 0.50, 0.25, 1.00, 0.50, 270, 180), - ), - '4' => array( - array('line', 0.70, 0.00, 0.70, 1.00, 1.57079632679, 1.00), - array('line', 0.00, 0.50, 0.70, 1.00, 0.620249485983, 0.860232526704), - array('line', 0.00, 0.50, 1.00, 0.50, 0.00, 1.00), - ), - '5' => array( - array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00), - array('line', 0.00, 1.00, 0.00, 0.60, -1.57079632679, 0.4), - array('line', 0.00, 0.60, 0.50, 0.60, 0.00, 0.50), - array('arc', 0.50, 0.30, 1.00, 0.60, 270, 180), - ), - '6' => array( - array('arc', 0.50, 0.50, 1.00, 1.00, 90, 315), - array('arc', 0.50, 0.30, 0.80, 0.60, 0, 360), - ), - '7' => array( - array('line', 0.00, 1.00, 1.00, 1.00, 0.00, 1.00), - array('line', 0.50, 0.00, 1.00, 1.00, 1.10714871779, 1.11803398875), - ), - '8' => array( - array('arc', 0.50, 0.75, 1.00, 0.50, 0, 360), - array('arc', 0.50, 0.25, 1.00, 0.50, 0, 360), - ), - '9' => array( - array('arc', 0.50, 0.50, 1.00, 1.00, 270, 135), - array('arc', 0.50, 0.70, 0.80, 0.60, 0, 360), - ) - ); -} - -/** -* @package VC -*/ -class color_manager -{ - var $img; - var $mode; - var $colors; - var $named_colors; - var $named_rgb = array( - 'red' => array(0xff, 0x00, 0x00), - 'maroon' => array(0x80, 0x00, 0x00), - 'yellow' => array(0xff, 0xff, 0x00), - 'olive' => array(0x80, 0x80, 0x00), - 'lime' => array(0x00, 0xff, 0x00), - 'green' => array(0x00, 0x80, 0x00), - 'aqua' => array(0x00, 0xff, 0xff), - 'teal' => array(0x00, 0x80, 0x80), - 'blue' => array(0x00, 0x00, 0xff), - 'navy' => array(0x00, 0x00, 0x80), - 'fuchsia' => array(0xff, 0x00, 0xff), - 'purple' => array(0x80, 0x00, 0x80), - 'white' => array(0xff, 0xff, 0xff), - 'silver' => array(0xc0, 0xc0, 0xc0), - 'gray' => array(0x80, 0x80, 0x80), - 'black' => array(0x00, 0x00, 0x00), - ); - - /** - * Create the color manager, link it to - * the image resource - */ - function color_manager($img, $background = false, $mode = 'ahsv') - { - $this->img = $img; - $this->mode = $mode; - $this->colors = array(); - $this->named_colors = array(); - if ($background !== false) - { - $bg = $this->allocate_named('background', $background); - imagefill($this->img, 0, 0, $bg); - } - } - - /** - * Lookup a named color resource - */ - function r($named_color) - { - if (isset($this->named_colors[$named_color])) - { - return $this->named_colors[$named_color]; - } - if (isset($this->named_rgb[$named_color])) - { - return $this->allocate_named($named_color, $this->named_rgb[$named_color], 'rgb'); - } - return false; - } - - /** - * Assign a name to a color resource - */ - function name_color($name, $resource) - { - $this->named_colors[$name] = $resource; - } - - /** - * random color resource - */ - function r_rand($colors) - { - return $colors[array_rand($colors)]; - } - - /** - * names and allocates a color resource - */ - function allocate_named($name, $color, $mode = false) - { - $resource = $this->allocate($color, $mode); - if ($resource !== false) - { - $this->name_color($name, $resource); - } - return $resource; - } - - /** - * allocates a specified color into the image - */ - function allocate($color, $mode = false) - { - if ($mode === false) - { - $mode = $this->mode; - } - if (!is_array($color)) - { - if (isset($this->named_rgb[$color])) - { - return $this->allocate_named($color, $this->named_rgb[$color], 'rgb'); - } - if (!is_int($color)) - { - return false; - } - $mode = 'rgb'; - $color = array( - 255 & ($color >> 16), - 255 & ($color >> 8), - 255 & $color, - ); - } - - if (isset($color['mode'])) - { - $mode = $color['mode']; - unset($color['mode']); - } - if (isset($color['random'])) - { - unset($color['random']); - // everything else is params - return $this->random_color($color, $mode); - } - - $rgb = color_manager::model_convert($color, $mode, 'rgb'); - $store = ($this->mode == 'rgb') ? $rgb : color_manager::model_convert($color, $mode, $this->mode); - $resource = imagecolorallocate($this->img, $rgb[0], $rgb[1], $rgb[2]); - - $this->colors[$resource] = $store; - - return $resource; - } - - /** - * randomly generates a color, with optional params - */ - function random_color($params = array(), $mode = false) - { - if ($mode === false) - { - $mode = $this->mode; - } - switch ($mode) - { - case 'rgb': - - // @TODO random rgb generation. do we intend to do this, or is it just too tedious? - - break; - - case 'ahsv': - case 'hsv': - default: - - $default_params = array( - 'hue_bias' => false, // degree / 'r'/'g'/'b'/'c'/'m'/'y' /'o' - 'hue_range' => false, // if hue bias, then difference range +/- from bias - 'min_saturation' => 30, // 0 - 100 - 'max_saturation' => 100, // 0 - 100 - 'min_value' => 30, // 0 - 100 - 'max_value' => 100, // 0 - 100 - ); - - $alt = ($mode == 'ahsv'); - - $params = array_merge($default_params, $params); - - $min_hue = 0; - $max_hue = 359; - $min_saturation = max(0, $params['min_saturation']); - $max_saturation = min(100, $params['max_saturation']); - $min_value = max(0, $params['min_value']); - $max_value = min(100, $params['max_value']); - - if ($params['hue_bias'] !== false) - { - if (is_numeric($params['hue_bias'])) - { - $h = intval($params['hue_bias']) % 360; - } - else - { - switch ($params['hue_bias']) - { - case 'o': - $h = $alt ? 60 : 30; - break; - - case 'y': - $h = $alt ? 120 : 60; - break; - - case 'g': - $h = $alt ? 180 : 120; - break; - - case 'c': - $h = $alt ? 210 : 180; - break; - - case 'b': - $h = 240; - break; - - case 'm': - $h = 300; - break; - - case 'r': - default: - $h = 0; - break; - } - } - - $min_hue = $h + 360; - $max_hue = $h + 360; - - if ($params['hue_range']) - { - $min_hue -= min(180, $params['hue_range']); - $max_hue += min(180, $params['hue_range']); - } - } - - $h = mt_rand($min_hue, $max_hue); - $s = mt_rand($min_saturation, $max_saturation); - $v = mt_rand($min_value, $max_value); - - return $this->allocate(array($h, $s, $v), $mode); - - break; - } - } + static $fonts = array(); - function color_scheme($resource, $scheme, $include_original = true) - { - $mode = (in_array($this->mode, array('hsv', 'ahsv'), true) ? $this->mode : 'hsv'); - if (($pre = $this->r($resource)) !== false) - { - $resource = $pre; - } - $color = color_manager::model_convert($this->colors[$resource], $this->mode, $mode); - $results = $include_original ? array($resource) : array(); - - switch ($scheme) + if (!sizeof($fonts)) { - case 'complement': - - $color2 = $color; - $color2[0] += 180; - $results[] = $this->allocate($color2, $mode); - - break; - - case 'triadic': - - $color2 = $color3 = $color; - $color2[0] += 120; - $color3[0] += 240; - $results[] = $this->allocate($color2, $mode); - $results[] = $this->allocate($color3, $mode); - - break; - - case 'tetradic': - - $color2 = $color3 = $color4 = $color; - $color2[0] += 30; - $color3[0] += 180; - $color4[0] += 210; - $results[] = $this->allocate($color2, $mode); - $results[] = $this->allocate($color3, $mode); - $results[] = $this->allocate($color4, $mode); - - break; - - case 'analogous': - - $color2 = $color3 = $color; - $color2[0] += 30; - $color3[0] += 330; - $results[] = $this->allocate($color2, $mode); - $results[] = $this->allocate($color3, $mode); - - break; - } - return $results; - } + global $phpbb_root_path; - function mono_range($resource, $type = 'both', $count = 5, $include_original = true) - { - if (is_array($resource)) - { - $results = array(); - for ($i = 0, $size = sizeof($resource); $i < $size; ++$i) + $dr = opendir($phpbb_root_path . 'includes/captcha/fonts'); + while (false !== ($entry = readdir($dr))) { - $results = array_merge($results, $this->mono_range($resource[$i], $type, $count, $include_original)); - } - return $results; - } - $mode = (in_array($this->mode, array('hsv', 'ahsv'), true) ? $this->mode : 'ahsv'); - if (($pre = $this->r($resource)) !== false) - { - $resource = $pre; - } - $color = color_manager::model_convert($this->colors[$resource], $this->mode, $mode); - - $results = array(); - if ($include_original) - { - $results[] = $resource; - $count--; - } - - switch ($type) - { - case 'saturation': - - $pivot = $color[1]; - $num_below = intval(($pivot * $count) / 100); - $num_above = $count - $num_below; - - for ($i = $num_above; $i > 0; --$i) - { - $color[1] = (($i * 100) + (($num_above - $i) * $pivot)) / $num_above; - $results[] = $this->allocate($color, $mode); - } - - ++$num_below; - - for ($i = $num_below - 1; $i > 0; --$i) - { - $color[1] = ($i * $pivot) / $num_below;; - $results[] = $this->allocate($color, $mode); - } - - return $results; - - break; - - case 'value': - - $pivot = $color[2]; - $num_below = intval(($pivot * $count) / 100); - $num_above = $count - $num_below; - - for ($i = $num_above; $i > 0; --$i) - { - $color[2] = (($i * 100) + (($num_above - $i) * $pivot)) / $num_above; - $results[] = $this->allocate($color, $mode); - } - - ++$num_below; - - for ($i = $num_below - 1; $i > 0; --$i) - { - $color[2] = ($i * $pivot) / $num_below;; - $results[] = $this->allocate($color, $mode); - } - - return $results; - - break; - - case 'both': - - // This is a hard problem. I chicken out and do an even triangle - // the problem is that it disregards the original saturation and value, - // and as such a generated result might come arbitrarily close to our original value. - $length = ceil(sqrt($count * 2)); - for ($i = $length; $i > 0; --$i) - { - for ($j = $i; $j > 0; --$j) - { - $color[1] = ($i * 100) / $length; - $color[2] = ($j * 100) / $i; - $results[] = $this->allocate($color, $mode); - --$count; - if (!$count) - { - return $results; - } - } - } - - return $results; - - break; - } - - return false; - } - - function is_dark($resource) - { - $color = (($pre = $this->r($resource)) !== false) ? $this->colors[$pre] : $this->colors[$resource]; - switch($this->mode) - { - case 'ahsv': - case 'hsv': - - return ($color[2] <= 50); - - break; - - case 'rgb': - - return (max($color[0], $color[1], $color[2]) <= 128); - - break; - } - return false; - } - - /** - * Convert from one color model to another - * - * note: properly following coding standards here yields unweildly amounts of whitespace, rendering this less than easily readable - * - */ - function model_convert($color, $from_model, $to_model) - { - if ($from_model == $to_model) - { - return $color; - } - switch ($to_model) - { - case 'hsv': - switch($from_model) - { - case 'ahsv': - return color_manager::ah2h($color); - break; - - case 'rgb': - return color_manager::rgb2hsv($color); - break; - } - break; - - case 'ahsv': - switch($from_model) - { - case 'hsv': - return color_manager::h2ah($color); - break; - - case 'rgb': - return color_manager::h2ah(color_manager::rgb2hsv($color)); - break; - } - break; - - case 'rgb': - switch($from_model) + if (strtolower(pathinfo($entry, PATHINFO_EXTENSION)) == 'ttf') { - case 'hsv': - return color_manager::hsv2rgb($color); - break; - - case 'ahsv': - return color_manager::hsv2rgb(color_manager::ah2h($color)); - break; + $fonts[] = $phpbb_root_path . 'includes/captcha/fonts/' . $entry; } - break; - } - return false; - } - - /** - * Slightly altered from wikipedia's algorithm - */ - function hsv2rgb($hsv) - { - color_manager::normalize_hue($hsv[0]); - $h = $hsv[0]; - $s = min(1, max(0, $hsv[1] / 100)); - $v = min(1, max(0, $hsv[2] / 100)); - - $hi = floor($hsv[0] / 60); // calculate hue sector - - $p = $v * (1 - $s); // calculate opposite color - $f = ($h / 60) - $hi; // calculate distance between hex vertices - if (!($hi & 1)) // coming in or going out? - { - $f = 1 - $f; - } - $q = $v * (1 - ($f * $s)); // calculate adjacent color - - switch ($hi) - { - case 0: - $rgb = array($v, $q, $p); - break; - - case 1: - $rgb = array($q, $v, $p); - break; - - case 2: - $rgb = array($p, $v, $q); - break; - - case 3: - $rgb = array($p, $q, $v); - break; - - case 4: - $rgb = array($q, $p, $v); - break; - - case 5: - $rgb = array($v, $p, $q); - break; - - default: - return array(0, 0, 0); - break; - } - return array(255 * $rgb[0], 255 * $rgb[1], 255 * $rgb[2]); - } - - /** - * (more than) Slightly altered from wikipedia's algorithm - */ - function rgb2hsv($rgb) - { - $r = min(255, max(0, $rgb[0])); - $g = min(255, max(0, $rgb[1])); - $b = min(255, max(0, $rgb[2])); - $max = max($r, $g, $b); - $min = min($r, $g, $b); - - $v = $max / 255; - $s = (!$max) ? 0 : 1 - ($min / $max); - $h = $max - $min; // if max - min is 0, we want hue to be 0 anyway. - if ($h) - { - switch ($max) - { - case $g: - $h = 120 + (60 * ($b - $r) / $h); - break; - - case $b: - $h = 240 + (60 * ($r - $g) / $h); - break; - - case $r: - $h = 360 + (60 * ($g - $b) / $h); - break; } + closedir($dr); } - color_manager::normalize_hue($h); - return array($h, $s * 100, $v * 100); - } - - /** - * Bleh - */ - function normalize_hue(&$hue) - { - $hue %= 360; - if ($hue < 0) - { - $hue += 360; - } - } - - /** - * Alternate hue to hue - */ - function ah2h($ahue) - { - if (is_array($ahue)) - { - $ahue[0] = color_manager::ah2h($ahue[0]); - return $ahue; - } - color_manager::normalize_hue($ahue); - if ($ahue >= 240) // blue through red is already ok - { - return $ahue; - } - if ($ahue >= 180) // ahue green is at 180 - { - // return (240 - (2 * (240 - $ahue))); - return (2 * $ahue) - 240; // equivalent - } - if ($ahue >= 120) // ahue yellow is at 120 (RYB rather than RGB) - { - return $ahue - 60; - } - return $ahue / 2; - } - /** - * hue to Alternate hue - */ - function h2ah($hue) - { - if (is_array($hue)) - { - $hue[0] = color_manager::h2ah($hue[0]); - return $hue; - } - color_manager::normalize_hue($hue); - if ($hue >= 240) // blue through red is already ok - { - return $hue; - } - else if ($hue <= 60) - { - return $hue * 2; - } - else if ($hue <= 120) - { - return $hue + 60; - } - else - { - return ($hue + 240) / 2; - } - } -} - -function vector_distance(&$char, $x, $y, $range = 0.1) -{ - $distance = $range + 1; - foreach ($char AS $vector) - { - $d = $range + 1; - switch ($vector[0]) - { - case 'arc': - - $dx = $x - $vector[1]; - $dy = -($y - $vector[2]); //because our arcs are upside-down.... - if (abs($dx) > abs($dy)) - { - $phi = rad2deg(atan(($dy * $vector[3])/($dx * $vector[4]))); - $phi += ($dx < 0) ? 180 : 360; - $phi %= 360; - } - else - { - $phi = 90 - rad2deg(atan(($dx * $vector[4])/($dy * $vector[3]))); - $phi += ($dy < 0) ? 180 : 360; - $phi %= 360; - } - - $internal = $vector[6] > $vector[5]; //external wraps over the 360 point - $low = $phi >= $vector[5]; //phi is above our low range - $high = $phi <= $vector[6]; //phi is below our high range. - if ($internal ? ($low && $high) : ($low || $high)) //if it wraps, it can only be one or the other - { - $radphi = deg2rad($phi); // i'm awesome. or not. - $px = cos($radphi) * 0.5 * $vector[3]; - $py = sin($radphi) * 0.5 * $vector[4]; - $d = sqrt(pow($px - $dx, 2) + pow($py - $dy, 2)); - } - - break; - - case 'line': - - $bx = $x - $vector[1]; - $by = $y - $vector[2]; - $dx = cos($vector[5]); - $dy = sin($vector[5]); - $r = ($by * $dx) - ($bx * $dy); - if ($r < $range && $r > -$range) - { - if (abs($dx) > abs($dy)) - { - $s = (($bx + ($dy * $r)) / $dx); - } - else - { - $s = (($by + ($dx * $r)) / $dy); - } - if ($s > -$range) - { - if ($s < 0) - { - $d = sqrt(pow($s, 2) + pow($r, 2)); - } - elseif ($s < $vector[6]) - { - $d = $r; - } - elseif ($s < $vector[6] + $range) - { - $d = sqrt(pow($s - $vector[6], 2) + pow($r, 2)); - } - } - } - - break; - } - $distance = min($distance, abs($d)); + return $fonts[array_rand($fonts)]; } - return $distance; } ?>
\ No newline at end of file diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php index c30bd867ae..1dc036b4b6 100644 --- a/phpBB/includes/functions.php +++ b/phpBB/includes/functions.php @@ -612,6 +612,14 @@ else } } +if (!function_exists('htmlspecialchars_decode')) +{ + function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT) + { + return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style))); + } +} + // functions used for building option fields /** diff --git a/phpBB/includes/functions_admin.php b/phpBB/includes/functions_admin.php index 70c5b2bae3..c85802dafb 100644 --- a/phpBB/includes/functions_admin.php +++ b/phpBB/includes/functions_admin.php @@ -2430,7 +2430,7 @@ function get_database_size() { if ($table_prefix != '') { - if (strstr($row['Name'], $table_prefix)) + if (strpos($row['Name'], $table_prefix) !== false) { $database_size += $row['Data_length'] + $row['Index_length']; } diff --git a/phpBB/includes/functions_compress.php b/phpBB/includes/functions_compress.php index cbada8a76d..93a3547e21 100644 --- a/phpBB/includes/functions_compress.php +++ b/phpBB/includes/functions_compress.php @@ -478,17 +478,17 @@ class compress_tar extends compress // Run through the file and grab directory entries while ($buffer = $fzread($this->fp, 512)) { - $tmp = unpack("A6magic", substr($buffer, 257, 6)); + $tmp = unpack('A6magic', substr($buffer, 257, 6)); if (trim($tmp['magic']) == 'ustar') { - $tmp = unpack("A100name", $buffer); + $tmp = unpack('A100name', $buffer); $filename = trim($tmp['name']); - $tmp = unpack("Atype", substr($buffer, 156, 1)); + $tmp = unpack('Atype', substr($buffer, 156, 1)); $filetype = (int) trim($tmp['type']); - $tmp = unpack("A12size", substr($buffer, 124, 12)); + $tmp = unpack('A12size', substr($buffer, 124, 12)); $filesize = octdec((int) trim($tmp['size'])); if ($filetype == 5) @@ -508,12 +508,12 @@ class compress_tar extends compress { trigger_error("Could not create directory $folder"); } - @chmod("$str", 0777); + @chmod($str, 0777); } } } } - else if($filesize != 0 && ($filetype == 0 || $filetype == "\0")) + else if ($filesize != 0 && ($filetype == 0 || $filetype == "\0")) { // Write out the files if (!($fp = fopen("$dst$filename", 'wb'))) @@ -523,7 +523,7 @@ class compress_tar extends compress @chmod("$dst$filename", 0777); // Grab the file contents - fwrite($fp, $fzread($this->fp, $filesize + 512 - $filesize % 512), $filesize); + fwrite($fp, $fzread($this->fp, ($filesize + 511) &~ 511), $filesize); fclose($fp); } } @@ -542,7 +542,7 @@ class compress_tar extends compress $fzwrite = ($this->isbz && function_exists('bzwrite')) ? 'bzwrite' : (($this->isgz && @extension_loaded('zlib')) ? 'gzwrite' : 'fwrite'); // Symbolizes that there are no more files - $fzwrite($this->fp, pack("a512", "")); + $fzwrite($this->fp, str_repeat("\0", 512)); } $fzclose($this->fp); @@ -560,37 +560,37 @@ class compress_tar extends compress // This is the header data, it contains all the info we know about the file or folder that we are about to archive $header = ''; - $header .= pack("a100", $name); // file name - $header .= pack("a8", sprintf("%07o", $stat[2])); // file mode - $header .= pack("a8", sprintf("%07o", $stat[4])); // owner id - $header .= pack("a8", sprintf("%07o", $stat[5])); // group id - $header .= pack("a12", sprintf("%011o", $stat[7])); // file size - $header .= pack("a12", sprintf("%011o", $stat[9])); // last mod time + $header .= pack('a100', $name); // file name + $header .= pack('a8', sprintf("%07o", $stat[2])); // file mode + $header .= pack('a8', sprintf("%07o", $stat[4])); // owner id + $header .= pack('a8', sprintf("%07o", $stat[5])); // group id + $header .= pack('a12', sprintf("%011o", $stat[7])); // file size + $header .= pack('a12', sprintf("%011o", $stat[9])); // last mod time // Checksum $checksum = 0; for ($i = 0; $i < 148; $i++) { - $checksum += ord(substr($header, $i, 1)); + $checksum += ord($header[$i]); } // We precompute the rest of the hash, this saves us time in the loop and allows us to insert our hash without resorting to string functions $checksum += 2415 + (($is_dir) ? 53 : 0); - $header .= pack("a8", sprintf("%07o", $checksum)); // checksum - $header .= pack("a1", $typeflag); // link indicator - $header .= pack("a100", ''); // name of linked file - $header .= pack("a6", 'ustar'); // ustar indicator - $header .= pack("a2", '00'); // ustar version - $header .= pack("a32", 'Unknown'); // owner name - $header .= pack("a32", 'Unknown'); // group name - $header .= pack("a8", ''); // device major number - $header .= pack("a8", ''); // device minor number - $header .= pack("a155", ''); // filename prefix - $header .= pack("a12", ''); // end + $header .= pack('a8', sprintf("%07o", $checksum)); // checksum + $header .= pack('a1', $typeflag); // link indicator + $header .= pack('a100', ''); // name of linked file + $header .= pack('a6', 'ustar'); // ustar indicator + $header .= pack('a2', '00'); // ustar version + $header .= pack('a32', 'Unknown'); // owner name + $header .= pack('a32', 'Unknown'); // group name + $header .= pack('a8', ''); // device major number + $header .= pack('a8', ''); // device minor number + $header .= pack('a155', ''); // filename prefix + $header .= pack('a12', ''); // end // This writes the entire file in one shot. Header, followed by data and then null padded to a multiple of 512 - $fzwrite($this->fp, $header . (($stat[7] !== 0 && !$is_dir) ? $data . (($stat[7] % 512 > 0) ? str_repeat("\0", 512 - $stat[7] % 512) : '') : '')); + $fzwrite($this->fp, $header . (($stat[7] !== 0 && !$is_dir) ? $data . str_repeat("\0", (($stat[7] + 511) &~ 511) - $stat[7]) : '')); unset($data); } diff --git a/phpBB/includes/ucp/ucp_confirm.php b/phpBB/includes/ucp/ucp_confirm.php index e40e89cbf5..087a186fa7 100644 --- a/phpBB/includes/ucp/ucp_confirm.php +++ b/phpBB/includes/ucp/ucp_confirm.php @@ -54,46 +54,9 @@ class ucp_confirm exit; } - // Some people might want the olde style CAPTCHA even if they have GD enabled, this also saves us from people who have GD but no TTF - $policy_modules = array('policy_entropy', 'policy_3dbitmap'); - - if (function_exists('imagettfbbox') && function_exists('imagettftext')) - { - $policy_modules = array_merge($policy_modules, array('policy_overlap', 'policy_shape', 'policy_cells', 'policy_stencil', 'policy_composite')); - } - - foreach ($policy_modules as $key => $name) - { - if ($config[$name] === '0') - { - unset($policy_modules[$key]); - } - } - - $policy = ''; - if (@extension_loaded('gd') && sizeof($policy_modules)) + if ($config['captcha_gd']) { - $change_lang = request_var('change_lang', ''); - - if ($change_lang) - { - $change_lang = basename($change_lang); - - if (file_exists($phpbb_root_path . 'language/' . $change_lang . '/')) - { - $user->lang_name = $lang = $change_lang; - $user->lang_path = $phpbb_root_path . 'language/' . $lang . '/'; - $user->lang = array(); - $user->add_lang(array('common', 'ucp')); - } - else - { - $change_lang = ''; - } - } - include($phpbb_root_path . 'includes/captcha/captcha_gd.' . $phpEx); - $policy = $policy_modules[array_rand($policy_modules)]; } else { @@ -101,7 +64,7 @@ class ucp_confirm } $captcha = new captcha(); - $captcha->execute($row['code'], $policy); + $captcha->execute($row['code']); exit; } } diff --git a/phpBB/includes/utf/utf_normalizer.php b/phpBB/includes/utf/utf_normalizer.php index d8e0ba0fa6..0d1d74539a 100644 --- a/phpBB/includes/utf/utf_normalizer.php +++ b/phpBB/includes/utf/utf_normalizer.php @@ -27,1181 +27,1055 @@ define('UTF8_CJK_LAST', "\xE9\xBE\xBB"); define('UTF8_CJK_B_FIRST', "\xF0\xA0\x80\x80"); define('UTF8_CJK_B_LAST', "\xF0\xAA\x9B\x96"); +// Unset global variables +unset($GLOBALS['utf_jamo_index'], $GLOBALS['utf_jamo_type'], $GLOBALS['utf_nfc_qc'], $GLOBALS['utf_combining_class'], $GLOBALS['utf_canonical_comp'], $GLOBALS['utf_canonical_decomp'], $GLOBALS['utf_nfkc_qc'], $GLOBALS['utf_compatibility_decomp']); + +// NFC_QC and NFKC_QC values +define('UNICODE_QC_MAYBE', 0); +define('UNICODE_QC_NO', 1); + +// Contains all the ASCII characters appearing in UTF-8, sorted by frequency +define('UTF8_ASCII_RANGE', "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"); + +// Contains all the tail bytes that can appear in the composition of a UTF-8 char +define('UTF8_TRAILING_BYTES', "\xA9\xA0\xA8\x80\xAA\x99\xA7\xBB\xAB\x89\x94\x82\xB4\xA2\xAE\x83\xB0\xB9\xB8\x93\xAF\xBC\xB3\x81\xA4\xB2\x9C\xA1\xB5\xBE\xBD\xBA\x98\xAD\xB1\x84\x95\xA6\xB6\x88\x8D\x90\xB7\xBF\x92\x85\xA5\x97\x8C\x86\xA3\x8E\x9F\x8F\x87\x91\x9D\xAC\x9E\x8B\x96\x9B\x8A\x9A"); + +// Constants used by the Hangul [de]composition algorithms +define('UNICODE_HANGUL_SBASE', 0xAC00); +define('UNICODE_HANGUL_LBASE', 0x1100); +define('UNICODE_HANGUL_VBASE', 0x1161); +define('UNICODE_HANGUL_TBASE', 0x11A7); +define('UNICODE_HANGUL_SCOUNT', 11172); +define('UNICODE_HANGUL_LCOUNT', 19); +define('UNICODE_HANGUL_VCOUNT', 21); +define('UNICODE_HANGUL_TCOUNT', 28); +define('UNICODE_HANGUL_NCOUNT', 588); +define('UNICODE_JAMO_L', 0); +define('UNICODE_JAMO_V', 1); +define('UNICODE_JAMO_T', 2); -// Wrapper for the utfnormal extension, ICU wrapper -if (function_exists('utf8_normalize')) +/** +* Unicode normalization routines +* +* @package phpBB3 +*/ +class utf_normalizer { - define('UNORM_NONE', 1); - define('UNORM_NFD', 2); - define('UNORM_NFKD', 3); - define('UNORM_NFC', 4); - define('UNORM_NFKC', 5); - define('UNORM_FCD', 6); - define('UNORM_DEFAULT', UNORM_NFC); - /** - * utf_normalizer class for the utfnormal extension + * Validate, cleanup and normalize a string + * + * The ultimate convenience function! Clean up invalid UTF-8 sequences, + * and convert to Normal Form C, canonical composition. * - * @ignore - * @package phpBB3 + * @param string $str The dirty string + * @return string The same string, all shiny and cleaned-up */ - class utf_normalizer + function cleanup($str) { - function cleanup($str) - { - /** - * The string below is the list of all autorized characters, sorted by - * frequency in latin text - */ - $pos = strspn($str, "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x0D"); + // The string below is the list of all autorized characters, sorted by frequency in latin text + $pos = strspn($str, "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x0D"); + $len = strlen($str); - if (!isset($str[$pos])) - { - // ASCII strings with no special chars return immediately - return $str; - } + if ($pos == $len) + { + // ASCII strings with no special chars return immediately + return $str; + } - // Check if there is potentially a U+FFFE or U+FFFF char (UTF sequence 0xEFBFBE or 0xEFBFBF) and replace them - // Note: we start searching at position $pos - if (is_int(strpos($str, "\xEF\xBF", $pos))) - { - $str = str_replace( - array("\xEF\xBF\xBE", "\xEF\xBF\xBF"), - array(UTF8_REPLACEMENT, UTF8_REPLACEMENT), - $str - ); - } + // Note: we do not check for $GLOBALS['utf_canonical_decomp']. It is assumed they are always loaded together + if (!isset($GLOBALS['utf_nfc_qc'])) + { + global $phpbb_root_path, $phpEx; + include($phpbb_root_path . 'includes/utf/data/utf_nfc_qc.' . $phpEx); + } - // Replace any byte in the range 0x00..0x1F, except for \r, \n and \t - // We replace those characters with a 0xFF byte, which is illegal in - // UTF-8 and will in turn be replaced with a Unicode replacement char - $str = strtr( + // Replace any byte in the range 0x00..0x1F, except for \r, \n and \t + // We replace those characters with a 0xFF byte, which is illegal in UTF-8 and will in turn be replaced with a UTF replacement char + return utf_normalizer::recompose( + strtr( $str, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" - ); + ), + $pos, $len, $GLOBALS['utf_nfc_qc'], $GLOBALS['utf_canonical_decomp'] + ); + } - // As per the original implementation, "the UnicodeString constructor fails if the string ends with a head byte". - // Therefore, if the string ends with a leading byte we replace it with 0xFF, which is illegal too and will be - // replaced with a Unicode replacement character - if (substr($str, -1) >= "\xC0") - { - $str[strlen($str) - 1] = "\xFF"; - } + /** + * Validate and normalize a UTF string to NFC + * + * @param string $str Unchecked UTF string + * @return string The string, validated and in normal form + */ + function nfc($str) + { + $pos = strspn($str, UTF8_ASCII_RANGE); + $len = strlen($str); - return utf8_normalize($str, UNORM_NFC); + if ($pos == $len) + { + // ASCII strings return immediately + return $str; } - function nfc($str) + if (!isset($GLOBALS['utf_nfc_qc'])) { - return utf8_normalize($str, UNORM_NFC); + global $phpbb_root_path, $phpEx; + include($phpbb_root_path . 'includes/utf/data/utf_nfc_qc.' . $phpEx); } - function nfkc($str) + return utf_normalizer::recompose($str, $pos, $len, $GLOBALS['utf_nfc_qc'], $GLOBALS['utf_canonical_decomp']); + } + + /** + * Validate and normalize a UTF string to NFKC + * + * @param string $str Unchecked UTF string + * @return string The string, validated and in normal form + */ + function nfkc($str) + { + $pos = strspn($str, UTF8_ASCII_RANGE); + $len = strlen($str); + + if ($pos == $len) { - return utf8_normalize($str, UNORM_NFKC); + // ASCII strings return immediately + return $str; } - function nfd($str) + if (!isset($GLOBALS['utf_nfkc_qc'])) { - return utf8_normalize($str, UNORM_NFD); + global $phpbb_root_path, $phpEx; + include($phpbb_root_path . 'includes/utf/data/utf_nfkc_qc.' . $phpEx); } - function nfkd($str) + if (!isset($GLOBALS['utf_canonical_comp'])) { - return utf8_normalize($str, UNORM_NFKD); + global $phpbb_root_path, $phpEx; + include($phpbb_root_path . 'includes/utf/data/utf_canonical_comp.' . $phpEx); } + + return utf_normalizer::recompose($str, $pos, $len, $GLOBALS['utf_nfkc_qc'], $GLOBALS['utf_compatibility_decomp']); } -} -else -{ - // This block will NOT be loaded if the utfnormal extension is - - // Unset global variables - unset($GLOBALS['utf_jamo_index'], $GLOBALS['utf_jamo_type'], $GLOBALS['utf_nfc_qc'], $GLOBALS['utf_combining_class'], $GLOBALS['utf_canonical_comp'], $GLOBALS['utf_canonical_decomp'], $GLOBALS['utf_nfkc_qc'], $GLOBALS['utf_compatibility_decomp']); - - // NFC_QC and NFKC_QC values - define('UNICODE_QC_MAYBE', 0); - define('UNICODE_QC_NO', 1); - - // Contains all the ASCII characters appearing in UTF-8, sorted by frequency - define('UTF8_ASCII_RANGE', "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"); - - // Contains all the tail bytes that can appear in the composition of a UTF-8 char - define('UTF8_TRAILING_BYTES', "\xA9\xA0\xA8\x80\xAA\x99\xA7\xBB\xAB\x89\x94\x82\xB4\xA2\xAE\x83\xB0\xB9\xB8\x93\xAF\xBC\xB3\x81\xA4\xB2\x9C\xA1\xB5\xBE\xBD\xBA\x98\xAD\xB1\x84\x95\xA6\xB6\x88\x8D\x90\xB7\xBF\x92\x85\xA5\x97\x8C\x86\xA3\x8E\x9F\x8F\x87\x91\x9D\xAC\x9E\x8B\x96\x9B\x8A\x9A"); - - // Constants used by the Hangul [de]composition algorithms - define('UNICODE_HANGUL_SBASE', 0xAC00); - define('UNICODE_HANGUL_LBASE', 0x1100); - define('UNICODE_HANGUL_VBASE', 0x1161); - define('UNICODE_HANGUL_TBASE', 0x11A7); - define('UNICODE_HANGUL_SCOUNT', 11172); - define('UNICODE_HANGUL_LCOUNT', 19); - define('UNICODE_HANGUL_VCOUNT', 21); - define('UNICODE_HANGUL_TCOUNT', 28); - define('UNICODE_HANGUL_NCOUNT', 588); - define('UNICODE_JAMO_L', 0); - define('UNICODE_JAMO_V', 1); - define('UNICODE_JAMO_T', 2); /** - * Unicode normalization routines + * Validate and normalize a UTF string to NFD * - * @package phpBB3 + * @param string $str Unchecked UTF string + * @return string The string, validated and in normal form */ - class utf_normalizer + function nfd($str) { - /** - * Validate, cleanup and normalize a string - * - * The ultimate convenience function! Clean up invalid UTF-8 sequences, - * and convert to Normal Form C, canonical composition. - * - * @param string $str The dirty string - * @return string The same string, all shiny and cleaned-up - */ - function cleanup($str) - { - // The string below is the list of all autorized characters, sorted by frequency in latin text - $pos = strspn($str, "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x0D"); - $len = strlen($str); - - if ($pos == $len) - { - // ASCII strings with no special chars return immediately - return $str; - } + $pos = strspn($str, UTF8_ASCII_RANGE); + $len = strlen($str); - // Note: we do not check for $GLOBALS['utf_canonical_decomp']. It is assumed they are always loaded together - if (!isset($GLOBALS['utf_nfc_qc'])) - { - global $phpbb_root_path, $phpEx; - include($phpbb_root_path . 'includes/utf/data/utf_nfc_qc.' . $phpEx); - } - - // Replace any byte in the range 0x00..0x1F, except for \r, \n and \t - // We replace those characters with a 0xFF byte, which is illegal in UTF-8 and will in turn be replaced with a UTF replacement char - return utf_normalizer::recompose( - strtr( - $str, - "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", - "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" - ), - $pos, $len, $GLOBALS['utf_nfc_qc'], $GLOBALS['utf_canonical_decomp'] - ); + if ($pos == $len) + { + // ASCII strings return immediately + return $str; } - /** - * Validate and normalize a UTF string to NFC - * - * @param string $str Unchecked UTF string - * @return string The string, validated and in normal form - */ - function nfc($str) + if (!isset($GLOBALS['utf_canonical_decomp'])) { - $pos = strspn($str, UTF8_ASCII_RANGE); - $len = strlen($str); + global $phpbb_root_path, $phpEx; + include($phpbb_root_path . 'includes/utf/data/utf_canonical_decomp.' . $phpEx); + } - if ($pos == $len) - { - // ASCII strings return immediately - return $str; - } + return utf_normalizer::decompose($str, $pos, $len, $GLOBALS['utf_canonical_decomp']); + } - if (!isset($GLOBALS['utf_nfc_qc'])) - { - global $phpbb_root_path, $phpEx; - include($phpbb_root_path . 'includes/utf/data/utf_nfc_qc.' . $phpEx); - } + /** + * Validate and normalize a UTF string to NFKD + * + * @param string $str Unchecked UTF string + * @return string The string, validated and in normal form + */ + function nfkd($str) + { + $pos = strspn($str, UTF8_ASCII_RANGE); + $len = strlen($str); - return utf_normalizer::recompose($str, $pos, $len, $GLOBALS['utf_nfc_qc'], $GLOBALS['utf_canonical_decomp']); + if ($pos == $len) + { + // ASCII strings return immediately + return $str; } - /** - * Validate and normalize a UTF string to NFKC - * - * @param string $str Unchecked UTF string - * @return string The string, validated and in normal form - */ - function nfkc($str) + if (!isset($GLOBALS['utf_compatibility_decomp'])) { - $pos = strspn($str, UTF8_ASCII_RANGE); - $len = strlen($str); - - if ($pos == $len) - { - // ASCII strings return immediately - return $str; - } + global $phpbb_root_path, $phpEx; + include($phpbb_root_path . 'includes/utf/data/utf_compatibility_decomp.' . $phpEx); + } - if (!isset($GLOBALS['utf_nfkc_qc'])) - { - global $phpbb_root_path, $phpEx; - include($phpbb_root_path . 'includes/utf/data/utf_nfkc_qc.' . $phpEx); - } + return utf_normalizer::decompose($str, $pos, $len, $GLOBALS['utf_compatibility_decomp']); + } - if (!isset($GLOBALS['utf_canonical_comp'])) - { - global $phpbb_root_path, $phpEx; - include($phpbb_root_path . 'includes/utf/data/utf_canonical_comp.' . $phpEx); - } - return utf_normalizer::recompose($str, $pos, $len, $GLOBALS['utf_nfkc_qc'], $GLOBALS['utf_compatibility_decomp']); - } + /** + * Recompose a UTF string + * + * @param string $str Unchecked UTF string + * @param integer $pos Position of the first UTF char (in bytes) + * @param integer $len Length of the string (in bytes) + * @param array $qc Quick-check array, passed by reference but never modified + * @param array $decomp_map Decomposition mapping, passed by reference but never modified + * @return string The string, validated and recomposed + * + * @access private + */ + function recompose($str, $pos, $len, &$qc, &$decomp_map) + { + global $utf_combining_class, $utf_canonical_comp, $utf_jamo_type, $utf_jamo_index; - /** - * Validate and normalize a UTF string to NFD - * - * @param string $str Unchecked UTF string - * @return string The string, validated and in normal form - */ - function nfd($str) + // Load some commonly-used tables + if (!isset($utf_jamo_index, $utf_jamo_type, $utf_combining_class)) { - $pos = strspn($str, UTF8_ASCII_RANGE); - $len = strlen($str); - - if ($pos == $len) - { - // ASCII strings return immediately - return $str; - } + global $phpbb_root_path; + include($phpbb_root_path . 'includes/utf/data/utf_normalizer_common.php'); + } - if (!isset($GLOBALS['utf_canonical_decomp'])) - { - global $phpbb_root_path, $phpEx; - include($phpbb_root_path . 'includes/utf/data/utf_canonical_decomp.' . $phpEx); - } + // Buffer the last ASCII char before the UTF-8 stuff if applicable + $tmp = ''; + $i = $tmp_pos = $last_cc = 0; - return utf_normalizer::decompose($str, $pos, $len, $GLOBALS['utf_canonical_decomp']); + if ($pos) + { + $buffer = array(++$i => $str[$pos - 1]); } - - /** - * Validate and normalize a UTF string to NFKD - * - * @param string $str Unchecked UTF string - * @return string The string, validated and in normal form - */ - function nfkd($str) + else { - $pos = strspn($str, UTF8_ASCII_RANGE); - $len = strlen($str); - - if ($pos == $len) - { - // ASCII strings return immediately - return $str; - } - - if (!isset($GLOBALS['utf_compatibility_decomp'])) - { - global $phpbb_root_path, $phpEx; - include($phpbb_root_path . 'includes/utf/data/utf_compatibility_decomp.' . $phpEx); - } - - return utf_normalizer::decompose($str, $pos, $len, $GLOBALS['utf_compatibility_decomp']); + $buffer = array(); } - - /** - * Recompose a UTF string - * - * @param string $str Unchecked UTF string - * @param integer $pos Position of the first UTF char (in bytes) - * @param integer $len Length of the string (in bytes) - * @param array $qc Quick-check array, passed by reference but never modified - * @param array $decomp_map Decomposition mapping, passed by reference but never modified - * @return string The string, validated and recomposed - * - * @access private - */ - function recompose($str, $pos, $len, &$qc, &$decomp_map) + // UTF char length array + // This array is used to determine the length of a UTF character. + // Be $c the result of ($str[$pos] & "\xF0") --where $str is the string we're operating on and $pos + // the position of the cursor--, if $utf_len_mask[$c] does not exist, the byte is an ASCII char. + // Otherwise, if $utf_len_mask[$c] is greater than 0, we have a the leading byte of a multibyte character + // whose length is $utf_len_mask[$c] and if it is equal to 0, the byte is a trailing byte. + $utf_len_mask = array( + // Leading bytes masks + "\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4, + // Trailing bytes masks + "\x80" => 0, "\x90" => 0, "\xA0" => 0, "\xB0" => 0 + ); + + $extra_check = array( + "\xED" => 1, "\xEF" => 1, "\xC0" => 1, "\xC1" => 1, "\xE0" => 1, "\xF0" => 1, + "\xF4" => 1, "\xF5" => 1, "\xF6" => 1, "\xF7" => 1, "\xF8" => 1, "\xF9" => 1, + "\xFA" => 1, "\xFB" => 1, "\xFC" => 1, "\xFD" => 1, "\xFE" => 1, "\xFF" => 1 + ); + + $utf_validation_mask = array( + 2 => "\xE0\xC0", + 3 => "\xF0\xC0\xC0", + 4 => "\xF8\xC0\xC0\xC0" + ); + + $utf_validation_check = array( + 2 => "\xC0\x80", + 3 => "\xE0\x80\x80", + 4 => "\xF0\x80\x80\x80" + ); + + // Main loop + do { - global $utf_combining_class, $utf_canonical_comp, $utf_jamo_type, $utf_jamo_index; - - // Load some commonly-used tables - if (!isset($utf_jamo_index, $utf_jamo_type, $utf_combining_class)) - { - global $phpbb_root_path; - include($phpbb_root_path . 'includes/utf/data/utf_normalizer_common.php'); - } + // STEP 0: Capture the current char and buffer it + $c = $str[$pos]; + $c_mask = $c & "\xF0"; - // Buffer the last ASCII char before the UTF-8 stuff if applicable - $tmp = ''; - $i = $tmp_pos = $last_cc = 0; - - if ($pos) - { - $buffer = array(++$i => $str[$pos - 1]); - } - else - { - $buffer = array(); - } - - // UTF char length array - // This array is used to determine the length of a UTF character. - // Be $c the result of ($str[$pos] & "\xF0") --where $str is the string we're operating on and $pos - // the position of the cursor--, if $utf_len_mask[$c] does not exist, the byte is an ASCII char. - // Otherwise, if $utf_len_mask[$c] is greater than 0, we have a the leading byte of a multibyte character - // whose length is $utf_len_mask[$c] and if it is equal to 0, the byte is a trailing byte. - $utf_len_mask = array( - // Leading bytes masks - "\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4, - // Trailing bytes masks - "\x80" => 0, "\x90" => 0, "\xA0" => 0, "\xB0" => 0 - ); - - $extra_check = array( - "\xED" => 1, "\xEF" => 1, "\xC0" => 1, "\xC1" => 1, "\xE0" => 1, "\xF0" => 1, - "\xF4" => 1, "\xF5" => 1, "\xF6" => 1, "\xF7" => 1, "\xF8" => 1, "\xF9" => 1, - "\xFA" => 1, "\xFB" => 1, "\xFC" => 1, "\xFD" => 1, "\xFE" => 1, "\xFF" => 1 - ); - - $utf_validation_mask = array( - 2 => "\xE0\xC0", - 3 => "\xF0\xC0\xC0", - 4 => "\xF8\xC0\xC0\xC0" - ); - - $utf_validation_check = array( - 2 => "\xC0\x80", - 3 => "\xE0\x80\x80", - 4 => "\xF0\x80\x80\x80" - ); - - // Main loop - do + if (isset($utf_len_mask[$c_mask])) { - // STEP 0: Capture the current char and buffer it - $c = $str[$pos]; - $c_mask = $c & "\xF0"; - - if (isset($utf_len_mask[$c_mask])) + // Byte at $pos is either a leading byte or a missplaced trailing byte + if ($utf_len = $utf_len_mask[$c_mask]) { - // Byte at $pos is either a leading byte or a missplaced trailing byte - if ($utf_len = $utf_len_mask[$c_mask]) - { - // Capture the char - $buffer[++$i & 7] = $utf_char = substr($str, $pos, $utf_len); + // Capture the char + $buffer[++$i & 7] = $utf_char = substr($str, $pos, $utf_len); - // Let's find out if a thorough check is needed - if (isset($qc[$utf_char])) + // Let's find out if a thorough check is needed + if (isset($qc[$utf_char])) + { + // If the UTF char is in the qc array then it may not be in normal form. We do nothing here, the actual processing is below this "if" block + } + else if (isset($utf_combining_class[$utf_char])) + { + if ($utf_combining_class[$utf_char] < $last_cc) { - // If the UTF char is in the qc array then it may not be in normal form. We do nothing here, the actual processing is below this "if" block + // A combining character that is NOT canonically ordered } - else if (isset($utf_combining_class[$utf_char])) + else + { + // A combining character that IS canonically ordered, skip to the next char + $last_cc = $utf_combining_class[$utf_char]; + + $pos += $utf_len; + continue; + } + } + else + { + // At this point, $utf_char holds a UTF char that we know is not a NF[K]C_QC and is not a combining character. + // It can be a singleton, a canonical composite, a replacement char or an even an ill-formed bunch of bytes. Let's find out + $last_cc = 0; + + // Check that we have the correct number of trailing bytes + if (($utf_char & $utf_validation_mask[$utf_len]) != $utf_validation_check[$utf_len]) { - if ($utf_combining_class[$utf_char] < $last_cc) + // Current char isn't well-formed or legal: either one or several trailing bytes are missing, or the Unicode char + // has been encoded in a five- or six- byte sequence + if ($utf_char[0] >= "\xF8") { - // A combining character that is NOT canonically ordered + if ($utf_char[0] < "\xF8") + { + $trailing_bytes = 3; + } + else if ($utf_char[0] < "\xFC") + { + $trailing_bytes = 4; + } + + if ($utf_char[0] > "\xFD") + { + $trailing_bytes = 0; + } + else + { + $trailing_bytes = 5; + } } else { - // A combining character that IS canonically ordered, skip to the next char - $last_cc = $utf_combining_class[$utf_char]; - - $pos += $utf_len; - continue; + $trailing_bytes = $utf_len - 1; } + + $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; + $pos += strspn($str, UTF8_TRAILING_BYTES, ++$pos, $trailing_bytes); + $tmp_pos = $pos; + + continue; } - else - { - // At this point, $utf_char holds a UTF char that we know is not a NF[K]C_QC and is not a combining character. - // It can be a singleton, a canonical composite, a replacement char or an even an ill-formed bunch of bytes. Let's find out - $last_cc = 0; - // Check that we have the correct number of trailing bytes - if (($utf_char & $utf_validation_mask[$utf_len]) != $utf_validation_check[$utf_len]) + if (isset($extra_check[$c])) + { + switch ($c) { - // Current char isn't well-formed or legal: either one or several trailing bytes are missing, or the Unicode char - // has been encoded in a five- or six- byte sequence - if ($utf_char[0] >= "\xF8") - { - if ($utf_char[0] < "\xF8") + // Note: 0xED is quite common in Korean + case "\xED": + if ($utf_char >= "\xED\xA0\x80") { - $trailing_bytes = 3; + // Surrogates (U+D800..U+DFFF) are not allowed in UTF-8 (UTF sequence 0xEDA080..0xEDBFBF) + $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; + $pos += $utf_len; + $tmp_pos = $pos; + continue 2; } - else if ($utf_char[0] < "\xFC") + break; + + // Note: 0xEF is quite common in Japanese + case "\xEF": + if ($utf_char == "\xEF\xBF\xBE" || $utf_char == "\xEF\xBF\xBF") { - $trailing_bytes = 4; + // U+FFFE and U+FFFF are explicitly disallowed (UTF sequence 0xEFBFBE..0xEFBFBF) + $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; + $pos += $utf_len; + $tmp_pos = $pos; + continue 2; } + break; - if ($utf_char[0] > "\xFD") + case "\xC0": + case "\xC1": + if ($utf_char <= "\xC1\xBF") { - $trailing_bytes = 0; + // Overlong sequence: Unicode char U+0000..U+007F encoded as a double-byte UTF char + $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; + $pos += $utf_len; + $tmp_pos = $pos; + continue 2; } - else + break; + + case "\xE0": + if ($utf_char <= "\xE0\x9F\xBF") { - $trailing_bytes = 5; + // Unicode char U+0000..U+07FF encoded in 3 bytes + $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; + $pos += $utf_len; + $tmp_pos = $pos; + continue 2; } - } - else - { - $trailing_bytes = $utf_len - 1; - } - - $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; - $pos += strspn($str, UTF8_TRAILING_BYTES, ++$pos, $trailing_bytes); - $tmp_pos = $pos; + break; - continue; - } + case "\xF0": + if ($utf_char <= "\xF0\x8F\xBF\xBF") + { + // Unicode char U+0000..U+FFFF encoded in 4 bytes + $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; + $pos += $utf_len; + $tmp_pos = $pos; + continue 2; + } + break; - if (isset($extra_check[$c])) - { - switch ($c) - { - // Note: 0xED is quite common in Korean - case "\xED": - if ($utf_char >= "\xED\xA0\x80") + default: + // Five- and six- byte sequences do not need being checked for here anymore + if ($utf_char > UTF8_MAX) + { + // Out of the Unicode range + if ($utf_char[0] < "\xF8") { - // Surrogates (U+D800..U+DFFF) are not allowed in UTF-8 (UTF sequence 0xEDA080..0xEDBFBF) - $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; - $pos += $utf_len; - $tmp_pos = $pos; - continue 2; + $trailing_bytes = 3; } - break; - - // Note: 0xEF is quite common in Japanese - case "\xEF": - if ($utf_char == "\xEF\xBF\xBE" || $utf_char == "\xEF\xBF\xBF") + else if ($utf_char[0] < "\xFC") { - // U+FFFE and U+FFFF are explicitly disallowed (UTF sequence 0xEFBFBE..0xEFBFBF) - $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; - $pos += $utf_len; - $tmp_pos = $pos; - continue 2; + $trailing_bytes = 4; } - break; - - case "\xC0": - case "\xC1": - if ($utf_char <= "\xC1\xBF") + else if ($utf_char[0] > "\xFD") { - // Overlong sequence: Unicode char U+0000..U+007F encoded as a double-byte UTF char - $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; - $pos += $utf_len; - $tmp_pos = $pos; - continue 2; + $trailing_bytes = 0; } - break; - - case "\xE0": - if ($utf_char <= "\xE0\x9F\xBF") - { - // Unicode char U+0000..U+07FF encoded in 3 bytes - $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; - $pos += $utf_len; - $tmp_pos = $pos; - continue 2; - } - break; - - case "\xF0": - if ($utf_char <= "\xF0\x8F\xBF\xBF") + else { - // Unicode char U+0000..U+FFFF encoded in 4 bytes - $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; - $pos += $utf_len; - $tmp_pos = $pos; - continue 2; + $trailing_bytes = 5; } - break; - - default: - // Five- and six- byte sequences do not need being checked for here anymore - if ($utf_char > UTF8_MAX) - { - // Out of the Unicode range - if ($utf_char[0] < "\xF8") - { - $trailing_bytes = 3; - } - else if ($utf_char[0] < "\xFC") - { - $trailing_bytes = 4; - } - else if ($utf_char[0] > "\xFD") - { - $trailing_bytes = 0; - } - else - { - $trailing_bytes = 5; - } - $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; - $pos += strspn($str, UTF8_TRAILING_BYTES, ++$pos, $trailing_bytes); - $tmp_pos = $pos; - continue 2; - } - break; - } + $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . UTF8_REPLACEMENT; + $pos += strspn($str, UTF8_TRAILING_BYTES, ++$pos, $trailing_bytes); + $tmp_pos = $pos; + continue 2; + } + break; } - - // The char is a valid starter, move the cursor and go on - $pos += $utf_len; - continue; } - } - else - { - // A trailing byte came out of nowhere, we will advance the cursor and treat the this byte and all following trailing bytes as if - // each of them was a Unicode replacement char - $spn = strspn($str, UTF8_TRAILING_BYTES, $pos); - $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . str_repeat(UTF8_REPLACEMENT, $spn); - $pos += $spn; - $tmp_pos = $pos; + // The char is a valid starter, move the cursor and go on + $pos += $utf_len; continue; } + } + else + { + // A trailing byte came out of nowhere, we will advance the cursor and treat the this byte and all following trailing bytes as if + // each of them was a Unicode replacement char + $spn = strspn($str, UTF8_TRAILING_BYTES, $pos); + $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . str_repeat(UTF8_REPLACEMENT, $spn); + $pos += $spn; + $tmp_pos = $pos; + continue; + } - // STEP 1: Decompose current char - // We have found a character that is either: - // - in the NFC_QC/NFKC_QC list - // - a non-starter char that is not canonically ordered - // - // We are going to capture the shortest UTF sequence that satisfies these two conditions: - // - // 1 - If the sequence does not start at the begginning of the string, it must begin with a starter, - // and that starter must not have the NF[K]C_QC property equal to "MAYBE" - // - // 2 - If the sequence does not end at the end of the string, it must end with a non-starter and be - // immediately followed by a starter that is not on the QC list - // - $utf_seq = array(); - $last_cc = 0; - $lpos = $pos; - $pos += $utf_len; + // STEP 1: Decompose current char + + // We have found a character that is either: + // - in the NFC_QC/NFKC_QC list + // - a non-starter char that is not canonically ordered + // + // We are going to capture the shortest UTF sequence that satisfies these two conditions: + // + // 1 - If the sequence does not start at the begginning of the string, it must begin with a starter, + // and that starter must not have the NF[K]C_QC property equal to "MAYBE" + // + // 2 - If the sequence does not end at the end of the string, it must end with a non-starter and be + // immediately followed by a starter that is not on the QC list + // + $utf_seq = array(); + $last_cc = 0; + $lpos = $pos; + $pos += $utf_len; + + if (isset($decomp_map[$utf_char])) + { + $_pos = 0; + $_len = strlen($decomp_map[$utf_char]); - if (isset($decomp_map[$utf_char])) + do { - $_pos = 0; - $_len = strlen($decomp_map[$utf_char]); + $_utf_len =& $utf_len_mask[$decomp_map[$utf_char][$_pos] & "\xF0"]; - do + if (isset($_utf_len)) { - $_utf_len =& $utf_len_mask[$decomp_map[$utf_char][$_pos] & "\xF0"]; - - if (isset($_utf_len)) - { - $utf_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len); - $_pos += $_utf_len; - } - else - { - $utf_seq[] = $decomp_map[$utf_char][$_pos]; - ++$_pos; - } + $utf_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len); + $_pos += $_utf_len; + } + else + { + $utf_seq[] = $decomp_map[$utf_char][$_pos]; + ++$_pos; } - while ($_pos < $_len); - } - else - { - // The char is not decomposable - $utf_seq = array($utf_char); } + while ($_pos < $_len); + } + else + { + // The char is not decomposable + $utf_seq = array($utf_char); + } - // STEP 2: Capture the starter + // STEP 2: Capture the starter - // Check out the combining class of the first character of the UTF sequence - $k = 0; - if (isset($utf_combining_class[$utf_seq[0]]) || $qc[$utf_char] == UNICODE_QC_MAYBE) + // Check out the combining class of the first character of the UTF sequence + $k = 0; + if (isset($utf_combining_class[$utf_seq[0]]) || $qc[$utf_char] == UNICODE_QC_MAYBE) + { + // Not a starter, inspect previous characters + // The last 8 characters are kept in a buffer so that we don't have to capture them everytime. + // This is enough for all real-life strings but even if it wasn't, we can capture characters in backward mode, + // although it is slower than this method. + // + // In the following loop, $j starts at the previous buffered character ($i - 1, because current character is + // at offset $i) and process them in backward mode until we find a starter. + // + // $k is the index on each UTF character inside of our UTF sequence. At this time, $utf_seq contains one or more + // characters numbered 0 to n. $k starts at 0 and for each char we prepend we pre-decrement it and for numbering + $starter_found = 0; + $j_min = max(1, $i - 7); + + for ($j = $i - 1; $j >= $j_min && $lpos > $tmp_pos; --$j) { - // Not a starter, inspect previous characters - // The last 8 characters are kept in a buffer so that we don't have to capture them everytime. - // This is enough for all real-life strings but even if it wasn't, we can capture characters in backward mode, - // although it is slower than this method. - // - // In the following loop, $j starts at the previous buffered character ($i - 1, because current character is - // at offset $i) and process them in backward mode until we find a starter. - // - // $k is the index on each UTF character inside of our UTF sequence. At this time, $utf_seq contains one or more - // characters numbered 0 to n. $k starts at 0 and for each char we prepend we pre-decrement it and for numbering - $starter_found = 0; - $j_min = max(1, $i - 7); + $utf_char = $buffer[$j & 7]; + $lpos -= strlen($utf_char); - for ($j = $i - 1; $j >= $j_min && $lpos > $tmp_pos; --$j) + if (isset($decomp_map[$utf_char])) { - $utf_char = $buffer[$j & 7]; - $lpos -= strlen($utf_char); + // The char is a composite, decompose for storage + $decomp_seq = array(); + $_pos = 0; + $_len = strlen($decomp_map[$utf_char]); - if (isset($decomp_map[$utf_char])) + do { - // The char is a composite, decompose for storage - $decomp_seq = array(); - $_pos = 0; - $_len = strlen($decomp_map[$utf_char]); + $c = $decomp_map[$utf_char][$_pos]; + $_utf_len =& $utf_len_mask[$c & "\xF0"]; - do + if (isset($_utf_len)) { - $c = $decomp_map[$utf_char][$_pos]; - $_utf_len =& $utf_len_mask[$c & "\xF0"]; - - if (isset($_utf_len)) - { - $decomp_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len); - $_pos += $_utf_len; - } - else - { - $decomp_seq[] = $c; - ++$_pos; - } + $decomp_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len); + $_pos += $_utf_len; } - while ($_pos < $_len); - - // Prepend the UTF sequence with our decomposed sequence - if (isset($decomp_seq[1])) + else { - // The char expanded into several chars - $decomp_cnt = sizeof($decomp_seq); - - foreach ($decomp_seq as $decomp_i => $decomp_char) - { - $utf_seq[$k + $decomp_i - $decomp_cnt] = $decomp_char; - } - $k -= $decomp_cnt; + $decomp_seq[] = $c; + ++$_pos; } - else + } + while ($_pos < $_len); + + // Prepend the UTF sequence with our decomposed sequence + if (isset($decomp_seq[1])) + { + // The char expanded into several chars + $decomp_cnt = sizeof($decomp_seq); + + foreach ($decomp_seq as $decomp_i => $decomp_char) { - // Decomposed to a single char, easier to prepend - $utf_seq[--$k] = $decomp_seq[0]; + $utf_seq[$k + $decomp_i - $decomp_cnt] = $decomp_char; } + $k -= $decomp_cnt; } else { - $utf_seq[--$k] = $utf_char; + // Decomposed to a single char, easier to prepend + $utf_seq[--$k] = $decomp_seq[0]; } + } + else + { + $utf_seq[--$k] = $utf_char; + } - if (!isset($utf_combining_class[$utf_seq[$k]])) - { - // We have found our starter - $starter_found = 1; - break; - } + if (!isset($utf_combining_class[$utf_seq[$k]])) + { + // We have found our starter + $starter_found = 1; + break; } + } - if (!$starter_found && $lpos > $tmp_pos) + if (!$starter_found && $lpos > $tmp_pos) + { + // The starter was not found in the buffer, let's rewind some more + do { - // The starter was not found in the buffer, let's rewind some more - do - { - // $utf_len_mask contains the masks of both leading bytes and trailing bytes. If $utf_en > 0 then it's a leading byte, otherwise it's a trailing byte. - $c = $str[--$lpos]; - $c_mask = $c & "\xF0"; + // $utf_len_mask contains the masks of both leading bytes and trailing bytes. If $utf_en > 0 then it's a leading byte, otherwise it's a trailing byte. + $c = $str[--$lpos]; + $c_mask = $c & "\xF0"; - if (isset($utf_len_mask[$c_mask])) + if (isset($utf_len_mask[$c_mask])) + { + // UTF byte + if ($utf_len = $utf_len_mask[$c_mask]) { - // UTF byte - if ($utf_len = $utf_len_mask[$c_mask]) + // UTF *leading* byte + $utf_char = substr($str, $lpos, $utf_len); + + if (isset($decomp_map[$utf_char])) { - // UTF *leading* byte - $utf_char = substr($str, $lpos, $utf_len); + // Decompose the character + $decomp_seq = array(); + $_pos = 0; + $_len = strlen($decomp_map[$utf_char]); - if (isset($decomp_map[$utf_char])) + do { - // Decompose the character - $decomp_seq = array(); - $_pos = 0; - $_len = strlen($decomp_map[$utf_char]); + $c = $decomp_map[$utf_char][$_pos]; + $_utf_len =& $utf_len_mask[$c & "\xF0"]; - do + if (isset($_utf_len)) { - $c = $decomp_map[$utf_char][$_pos]; - $_utf_len =& $utf_len_mask[$c & "\xF0"]; - - if (isset($_utf_len)) - { - $decomp_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len); - $_pos += $_utf_len; - } - else - { - $decomp_seq[] = $c; - ++$_pos; - } + $decomp_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len); + $_pos += $_utf_len; } - while ($_pos < $_len); - - // Prepend the UTF sequence with our decomposed sequence - if (isset($decomp_seq[1])) + else { - // The char expanded into several chars - $decomp_cnt = sizeof($decomp_seq); - foreach ($decomp_seq as $decomp_i => $utf_char) - { - $utf_seq[$k + $decomp_i - $decomp_cnt] = $utf_char; - } - $k -= $decomp_cnt; + $decomp_seq[] = $c; + ++$_pos; } - else + } + while ($_pos < $_len); + + // Prepend the UTF sequence with our decomposed sequence + if (isset($decomp_seq[1])) + { + // The char expanded into several chars + $decomp_cnt = sizeof($decomp_seq); + foreach ($decomp_seq as $decomp_i => $utf_char) { - // Decomposed to a single char, easier to prepend - $utf_seq[--$k] = $decomp_seq[0]; + $utf_seq[$k + $decomp_i - $decomp_cnt] = $utf_char; } + $k -= $decomp_cnt; } else { - $utf_seq[--$k] = $utf_char; + // Decomposed to a single char, easier to prepend + $utf_seq[--$k] = $decomp_seq[0]; } } + else + { + $utf_seq[--$k] = $utf_char; + } } - else - { - // ASCII char - $utf_seq[--$k] = $c; - } } - while ($lpos > $tmp_pos); + else + { + // ASCII char + $utf_seq[--$k] = $c; + } } + while ($lpos > $tmp_pos); } + } - // STEP 3: Capture following combining modifiers + // STEP 3: Capture following combining modifiers - while ($pos < $len) + while ($pos < $len) + { + $c_mask = $str[$pos] & "\xF0"; + + if (isset($utf_len_mask[$c_mask])) { - $c_mask = $str[$pos] & "\xF0"; + if ($utf_len = $utf_len_mask[$c_mask]) + { + $utf_char = substr($str, $pos, $utf_len); + } + else + { + // A trailing byte came out of nowhere + // Trailing bytes are replaced with Unicode replacement chars, we will just ignore it for now, break out of the loop + // as if it was a starter (replacement chars ARE starters) and let the next loop replace it + break; + } - if (isset($utf_len_mask[$c_mask])) + if (isset($utf_combining_class[$utf_char]) || isset($qc[$utf_char])) { - if ($utf_len = $utf_len_mask[$c_mask]) - { - $utf_char = substr($str, $pos, $utf_len); - } - else + // Combining character, add it to the sequence and move the cursor + if (isset($decomp_map[$utf_char])) { - // A trailing byte came out of nowhere - // Trailing bytes are replaced with Unicode replacement chars, we will just ignore it for now, break out of the loop - // as if it was a starter (replacement chars ARE starters) and let the next loop replace it - break; - } + // Decompose the character + $_pos = 0; + $_len = strlen($decomp_map[$utf_char]); - if (isset($utf_combining_class[$utf_char]) || isset($qc[$utf_char])) - { - // Combining character, add it to the sequence and move the cursor - if (isset($decomp_map[$utf_char])) + do { - // Decompose the character - $_pos = 0; - $_len = strlen($decomp_map[$utf_char]); + $c = $decomp_map[$utf_char][$_pos]; + $_utf_len =& $utf_len_mask[$c & "\xF0"]; - do + if (isset($_utf_len)) { - $c = $decomp_map[$utf_char][$_pos]; - $_utf_len =& $utf_len_mask[$c & "\xF0"]; - - if (isset($_utf_len)) - { - $utf_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len); - $_pos += $_utf_len; - } - else - { - $utf_seq[] = $c; - ++$_pos; - } + $utf_seq[] = substr($decomp_map[$utf_char], $_pos, $_utf_len); + $_pos += $_utf_len; + } + else + { + $utf_seq[] = $c; + ++$_pos; } - while ($_pos < $_len); - } - else - { - $utf_seq[] = $utf_char; } - - $pos += $utf_len; + while ($_pos < $_len); } else { - // Combining class 0 and no QC, break out of the loop - // Note: we do not know if that character is valid. If it's not, the next iteration will replace it - break; + $utf_seq[] = $utf_char; } + + $pos += $utf_len; } else { - // ASCII chars are starters + // Combining class 0 and no QC, break out of the loop + // Note: we do not know if that character is valid. If it's not, the next iteration will replace it break; } } + else + { + // ASCII chars are starters + break; + } + } - // STEP 4: Sort and combine + // STEP 4: Sort and combine - // Here we sort... - $k_max = $k + sizeof($utf_seq); + // Here we sort... + $k_max = $k + sizeof($utf_seq); - if (!$k && $k_max == 1) - { - // There is only one char in the UTF sequence, add it then jump to the next iteration of main loop + if (!$k && $k_max == 1) + { + // There is only one char in the UTF sequence, add it then jump to the next iteration of main loop // Note: the two commented lines below can be enabled under PHP5 for a very small performance gain in most cases // if (substr_compare($str, $utf_seq[0], $lpos, $pos - $lpos)) // { - $tmp .= substr($str, $tmp_pos, $lpos - $tmp_pos) . $utf_seq[0]; - $tmp_pos = $pos; + $tmp .= substr($str, $tmp_pos, $lpos - $tmp_pos) . $utf_seq[0]; + $tmp_pos = $pos; // } - continue; - } + continue; + } + + // ...there we combine + if (isset($utf_combining_class[$utf_seq[$k]])) + { + $starter = $nf_seq = ''; + } + else + { + $starter = $utf_seq[$k++]; + $nf_seq = ''; + } + $utf_sort = array(); + + // We add an empty char at the end of the UTF char sequence. It will act as a starter and trigger the sort/combine routine + // at the end of the string without altering it + $utf_seq[] = ''; + + do + { + $utf_char = $utf_seq[$k++]; - // ...there we combine - if (isset($utf_combining_class[$utf_seq[$k]])) + if (isset($utf_combining_class[$utf_char])) { - $starter = $nf_seq = ''; + $utf_sort[$utf_combining_class[$utf_char]][] = $utf_char; } else { - $starter = $utf_seq[$k++]; - $nf_seq = ''; - } - $utf_sort = array(); - - // We add an empty char at the end of the UTF char sequence. It will act as a starter and trigger the sort/combine routine - // at the end of the string without altering it - $utf_seq[] = ''; - - do - { - $utf_char = $utf_seq[$k++]; - - if (isset($utf_combining_class[$utf_char])) - { - $utf_sort[$utf_combining_class[$utf_char]][] = $utf_char; - } - else + if (empty($utf_sort)) { - if (empty($utf_sort)) + // No combining characters... check for a composite of the two starters + if (isset($utf_canonical_comp[$starter . $utf_char])) { - // No combining characters... check for a composite of the two starters - if (isset($utf_canonical_comp[$starter . $utf_char])) - { - // Good ol' composite character - $starter = $utf_canonical_comp[$starter . $utf_char]; - } - else if (isset($utf_jamo_type[$utf_char])) + // Good ol' composite character + $starter = $utf_canonical_comp[$starter . $utf_char]; + } + else if (isset($utf_jamo_type[$utf_char])) + { + // Current char is a composable jamo + if (isset($utf_jamo_type[$starter]) && $utf_jamo_type[$starter] == UNICODE_JAMO_L && $utf_jamo_type[$utf_char] == UNICODE_JAMO_V) { - // Current char is a composable jamo - if (isset($utf_jamo_type[$starter]) && $utf_jamo_type[$starter] == UNICODE_JAMO_L && $utf_jamo_type[$utf_char] == UNICODE_JAMO_V) + // We have a L jamo followed by a V jamo, we are going to prefetch the next char to see if it's a T jamo + if (isset($utf_jamo_type[$utf_seq[$k]]) && $utf_jamo_type[$utf_seq[$k]] == UNICODE_JAMO_T) { - // We have a L jamo followed by a V jamo, we are going to prefetch the next char to see if it's a T jamo - if (isset($utf_jamo_type[$utf_seq[$k]]) && $utf_jamo_type[$utf_seq[$k]] == UNICODE_JAMO_T) - { - // L+V+T jamos, combine to a LVT Hangul syllable ($k is incremented) - $cp = $utf_jamo_index[$starter] + $utf_jamo_index[$utf_char] + $utf_jamo_index[$utf_seq[$k]]; - ++$k; - } - else - { - // L+V jamos, combine to a LV Hangul syllable - $cp = $utf_jamo_index[$starter] + $utf_jamo_index[$utf_char]; - } - - $starter = chr(0xE0 | ($cp >> 12)) . chr(0x80 | (($cp >> 6) & 0x3F)) . chr(0x80 | ($cp & 0x3F)); + // L+V+T jamos, combine to a LVT Hangul syllable ($k is incremented) + $cp = $utf_jamo_index[$starter] + $utf_jamo_index[$utf_char] + $utf_jamo_index[$utf_seq[$k]]; + ++$k; } else { - // Non-composable jamo, just add it to the sequence - $nf_seq .= $starter; - $starter = $utf_char; + // L+V jamos, combine to a LV Hangul syllable + $cp = $utf_jamo_index[$starter] + $utf_jamo_index[$utf_char]; } + + $starter = chr(0xE0 | ($cp >> 12)) . chr(0x80 | (($cp >> 6) & 0x3F)) . chr(0x80 | ($cp & 0x3F)); } else { - // No composite, just add the first starter to the sequence then continue with the other one + // Non-composable jamo, just add it to the sequence $nf_seq .= $starter; $starter = $utf_char; } } else { - ksort($utf_sort); + // No composite, just add the first starter to the sequence then continue with the other one + $nf_seq .= $starter; + $starter = $utf_char; + } + } + else + { + ksort($utf_sort); - // For each class of combining characters - foreach ($utf_sort as $cc => $utf_chars) - { - $j = 0; + // For each class of combining characters + foreach ($utf_sort as $cc => $utf_chars) + { + $j = 0; - do + do + { + // Look for a composite + if (isset($utf_canonical_comp[$starter . $utf_chars[$j]])) { - // Look for a composite - if (isset($utf_canonical_comp[$starter . $utf_chars[$j]])) - { - // Found a composite, replace the starter - $starter = $utf_canonical_comp[$starter . $utf_chars[$j]]; - unset($utf_sort[$cc][$j]); - } - else - { - // No composite, all following characters in that class are blocked - break; - } + // Found a composite, replace the starter + $starter = $utf_canonical_comp[$starter . $utf_chars[$j]]; + unset($utf_sort[$cc][$j]); + } + else + { + // No composite, all following characters in that class are blocked + break; } - while (isset($utf_sort[$cc][++$j])); } + while (isset($utf_sort[$cc][++$j])); + } - // Add the starter to the normalized sequence, followed by non-starters in canonical order - $nf_seq .= $starter; + // Add the starter to the normalized sequence, followed by non-starters in canonical order + $nf_seq .= $starter; - foreach ($utf_sort as $utf_chars) + foreach ($utf_sort as $utf_chars) + { + if (!empty($utf_chars)) { - if (!empty($utf_chars)) - { - $nf_seq .= implode('', $utf_chars); - } + $nf_seq .= implode('', $utf_chars); } - - // Reset the array and go on - $utf_sort = array(); - $starter = $utf_char; } + + // Reset the array and go on + $utf_sort = array(); + $starter = $utf_char; } } - while ($k <= $k_max); - - $tmp .= substr($str, $tmp_pos, $lpos - $tmp_pos) . $nf_seq; - $tmp_pos = $pos; } - else + while ($k <= $k_max); + + $tmp .= substr($str, $tmp_pos, $lpos - $tmp_pos) . $nf_seq; + $tmp_pos = $pos; + } + else + { + // Only a ASCII char can make the program get here + // + // First we skip the current byte with ++$pos, then we quickly skip following ASCII chars with strspn(). + // + // The first two "if"'s here can be removed, with the consequences of being faster on latin text (lots of ASCII) and slower on + // multi-byte text (where the only ASCII chars are spaces and punctuation) + if (++$pos != $len) { - // Only a ASCII char can make the program get here - // - // First we skip the current byte with ++$pos, then we quickly skip following ASCII chars with strspn(). - // - // The first two "if"'s here can be removed, with the consequences of being faster on latin text (lots of ASCII) and slower on - // multi-byte text (where the only ASCII chars are spaces and punctuation) - if (++$pos != $len) + if ($str[$pos] < "\x80") { - if ($str[$pos] < "\x80") - { - $pos += strspn($str, UTF8_ASCII_RANGE, ++$pos); - $buffer[++$i & 7] = $str[$pos - 1]; - } - else - { - $buffer[++$i & 7] = $c; - } + $pos += strspn($str, UTF8_ASCII_RANGE, ++$pos); + $buffer[++$i & 7] = $str[$pos - 1]; + } + else + { + $buffer[++$i & 7] = $c; } } } - while ($pos < $len); + } + while ($pos < $len); - // Now is time to return the string - if ($tmp_pos) + // Now is time to return the string + if ($tmp_pos) + { + // If the $tmp_pos cursor is not at the beggining of the string then at least one character was not in normal form. Replace $str with the fixed version + if ($tmp_pos == $len) { - // If the $tmp_pos cursor is not at the beggining of the string then at least one character was not in normal form. Replace $str with the fixed version - if ($tmp_pos == $len) - { - // The $tmp_pos cursor is at the end of $str, therefore $tmp holds the whole $str - return $tmp; - } - else - { - // The rightmost chunk of $str has not been appended to $tmp yet - return $tmp . substr($str, $tmp_pos); - } + // The $tmp_pos cursor is at the end of $str, therefore $tmp holds the whole $str + return $tmp; + } + else + { + // The rightmost chunk of $str has not been appended to $tmp yet + return $tmp . substr($str, $tmp_pos); } - - // The string was already in normal form - return $str; } - /** - * Decompose a UTF string - * - * @param string $str UTF string - * @param integer $pos Position of the first UTF char (in bytes) - * @param integer $len Length of the string (in bytes) - * @param array $decomp_map Decomposition mapping, passed by reference but never modified - * @return string The string, decomposed and sorted canonically - * - * @access private - */ - function decompose($str, $pos, $len, &$decomp_map) + // The string was already in normal form + return $str; + } + + /** + * Decompose a UTF string + * + * @param string $str UTF string + * @param integer $pos Position of the first UTF char (in bytes) + * @param integer $len Length of the string (in bytes) + * @param array $decomp_map Decomposition mapping, passed by reference but never modified + * @return string The string, decomposed and sorted canonically + * + * @access private + */ + function decompose($str, $pos, $len, &$decomp_map) + { + global $utf_combining_class, $utf_canonical_decomp, $phpbb_root_path; + + // Load some commonly-used tables + if (!isset($utf_combining_class)) { - global $utf_combining_class, $utf_canonical_decomp, $phpbb_root_path; + include($phpbb_root_path . 'includes/utf/data/utf_normalizer_common.php'); + } - // Load some commonly-used tables - if (!isset($utf_combining_class)) - { - include($phpbb_root_path . 'includes/utf/data/utf_normalizer_common.php'); - } + // UTF char length array + $utf_len_mask = array( + // Leading bytes masks + "\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4, + // Trailing bytes masks + "\x80" => 0, "\x90" => 0, "\xA0" => 0, "\xB0" => 0 + ); + + // Some extra checks are triggered on the first byte of a UTF sequence + $extra_check = array( + "\xED" => 1, "\xEF" => 1, "\xC0" => 1, "\xC1" => 1, "\xE0" => 1, "\xF0" => 1, + "\xF4" => 1, "\xF5" => 1, "\xF6" => 1, "\xF7" => 1, "\xF8" => 1, "\xF9" => 1, + "\xFA" => 1, "\xFB" => 1, "\xFC" => 1, "\xFD" => 1, "\xFE" => 1, "\xFF" => 1 + ); + + // These masks are used to check if a UTF sequence is well formed. Here are the only 3 lengths we acknowledge: + // - 2-byte: 110? ???? 10?? ???? + // - 3-byte: 1110 ???? 10?? ???? 10?? ???? + // - 4-byte: 1111 0??? 10?? ???? 10?? ???? 10?? ???? + // Note that 5- and 6- byte sequences are automatically discarded + $utf_validation_mask = array( + 2 => "\xE0\xC0", + 3 => "\xF0\xC0\xC0", + 4 => "\xF8\xC0\xC0\xC0" + ); + + $utf_validation_check = array( + 2 => "\xC0\x80", + 3 => "\xE0\x80\x80", + 4 => "\xF0\x80\x80\x80" + ); + + $tmp = ''; + $starter_pos = $pos; + $tmp_pos = $last_cc = $sort = $dump = 0; + $utf_sort = array(); + + + // Main loop + do + { + // STEP 0: Capture the current char - // UTF char length array - $utf_len_mask = array( - // Leading bytes masks - "\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4, - // Trailing bytes masks - "\x80" => 0, "\x90" => 0, "\xA0" => 0, "\xB0" => 0 - ); - - // Some extra checks are triggered on the first byte of a UTF sequence - $extra_check = array( - "\xED" => 1, "\xEF" => 1, "\xC0" => 1, "\xC1" => 1, "\xE0" => 1, "\xF0" => 1, - "\xF4" => 1, "\xF5" => 1, "\xF6" => 1, "\xF7" => 1, "\xF8" => 1, "\xF9" => 1, - "\xFA" => 1, "\xFB" => 1, "\xFC" => 1, "\xFD" => 1, "\xFE" => 1, "\xFF" => 1 - ); - - // These masks are used to check if a UTF sequence is well formed. Here are the only 3 lengths we acknowledge: - // - 2-byte: 110? ???? 10?? ???? - // - 3-byte: 1110 ???? 10?? ???? 10?? ???? - // - 4-byte: 1111 0??? 10?? ???? 10?? ???? 10?? ???? - // Note that 5- and 6- byte sequences are automatically discarded - $utf_validation_mask = array( - 2 => "\xE0\xC0", - 3 => "\xF0\xC0\xC0", - 4 => "\xF8\xC0\xC0\xC0" - ); - - $utf_validation_check = array( - 2 => "\xC0\x80", - 3 => "\xE0\x80\x80", - 4 => "\xF0\x80\x80\x80" - ); - - $tmp = ''; - $starter_pos = $pos; - $tmp_pos = $last_cc = $sort = $dump = 0; - $utf_sort = array(); - - - // Main loop - do + $cur_mask = $str[$pos] & "\xF0"; + if (isset($utf_len_mask[$cur_mask])) { - // STEP 0: Capture the current char - - $cur_mask = $str[$pos] & "\xF0"; - if (isset($utf_len_mask[$cur_mask])) + if ($utf_len = $utf_len_mask[$cur_mask]) { - if ($utf_len = $utf_len_mask[$cur_mask]) - { - // Multibyte char - $utf_char = substr($str, $pos, $utf_len); - $pos += $utf_len; - } - else + // Multibyte char + $utf_char = substr($str, $pos, $utf_len); + $pos += $utf_len; + } + else + { + // A trailing byte came out of nowhere, we will treat it and all following trailing bytes as if each of them was a Unicode + // replacement char and we will advance the cursor + $spn = strspn($str, UTF8_TRAILING_BYTES, $pos); + + if ($dump) { - // A trailing byte came out of nowhere, we will treat it and all following trailing bytes as if each of them was a Unicode - // replacement char and we will advance the cursor - $spn = strspn($str, UTF8_TRAILING_BYTES, $pos); + $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); - if ($dump) + // Dump combiners + if (!empty($utf_sort)) { - $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); - - // Dump combiners - if (!empty($utf_sort)) + if ($sort) { - if ($sort) - { - ksort($utf_sort); - } - - foreach($utf_sort as $utf_chars) - { - $tmp .= implode('', $utf_chars); - } + ksort($utf_sort); } - $tmp .= str_repeat(UTF8_REPLACEMENT, $spn); - $dump = $sort = 0; - } - else - { - $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . str_repeat(UTF8_REPLACEMENT, $spn); + foreach($utf_sort as $utf_chars) + { + $tmp .= implode('', $utf_chars); + } } - $pos += $spn; - $tmp_pos = $starter_pos = $pos; + $tmp .= str_repeat(UTF8_REPLACEMENT, $spn); + $dump = $sort = 0; + } + else + { + $tmp .= substr($str, $tmp_pos, $pos - $tmp_pos) . str_repeat(UTF8_REPLACEMENT, $spn); + } - $utf_sort = array(); - $last_cc = 0; + $pos += $spn; + $tmp_pos = $starter_pos = $pos; + + $utf_sort = array(); + $last_cc = 0; + + continue; + } - continue; - } + // STEP 1: Decide what to do with current char - // STEP 1: Decide what to do with current char + // Now, in that order: + // - check if that character is decomposable + // - check if that character is a non-starter + // - check if that character requires extra checks to be performed + if (isset($decomp_map[$utf_char])) + { + // Decompose the char + $_pos = 0; + $_len = strlen($decomp_map[$utf_char]); - // Now, in that order: - // - check if that character is decomposable - // - check if that character is a non-starter - // - check if that character requires extra checks to be performed - if (isset($decomp_map[$utf_char])) + do { - // Decompose the char - $_pos = 0; - $_len = strlen($decomp_map[$utf_char]); + $c = $decomp_map[$utf_char][$_pos]; + $_utf_len =& $utf_len_mask[$c & "\xF0"]; - do + if (isset($_utf_len)) { - $c = $decomp_map[$utf_char][$_pos]; - $_utf_len =& $utf_len_mask[$c & "\xF0"]; + $_utf_char = substr($decomp_map[$utf_char], $_pos, $_utf_len); + $_pos += $_utf_len; - if (isset($_utf_len)) + if (isset($utf_combining_class[$_utf_char])) { - $_utf_char = substr($decomp_map[$utf_char], $_pos, $_utf_len); - $_pos += $_utf_len; + // The character decomposed to a non-starter, buffer it for sorting + $utf_sort[$utf_combining_class[$_utf_char]][] = $_utf_char; - if (isset($utf_combining_class[$_utf_char])) + if ($utf_combining_class[$_utf_char] < $last_cc) { - // The character decomposed to a non-starter, buffer it for sorting - $utf_sort[$utf_combining_class[$_utf_char]][] = $_utf_char; - - if ($utf_combining_class[$_utf_char] < $last_cc) - { - // Not canonically ordered, will require sorting - $sort = $dump = 1; - } - else - { - $dump = 1; - $last_cc = $utf_combining_class[$_utf_char]; - } + // Not canonically ordered, will require sorting + $sort = $dump = 1; } else { - // This character decomposition contains a starter, dump the buffer and continue - if ($dump) - { - $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); - - // Dump combiners - if (!empty($utf_sort)) - { - if ($sort) - { - ksort($utf_sort); - } - - foreach ($utf_sort as $utf_chars) - { - $tmp .= implode('', $utf_chars); - } - } - - $tmp .= $_utf_char; - $dump = $sort = 0; - } - else - { - $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos) . $_utf_char; - } - - $tmp_pos = $starter_pos = $pos; - $utf_sort = array(); - $last_cc = 0; + $dump = 1; + $last_cc = $utf_combining_class[$_utf_char]; } } else { - // This character decomposition contains an ASCII char, which is a starter. Dump the buffer and continue - ++$_pos; - + // This character decomposition contains a starter, dump the buffer and continue if ($dump) { $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); @@ -1220,12 +1094,12 @@ else } } - $tmp .= $c; + $tmp .= $_utf_char; $dump = $sort = 0; } else { - $tmp .= substr($str, $tmp_pos, $pos - $utf_len - $tmp_pos) . $c; + $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos) . $_utf_char; } $tmp_pos = $starter_pos = $pos; @@ -1233,285 +1107,290 @@ else $last_cc = 0; } } - while ($_pos < $_len); - } - else if (isset($utf_combining_class[$utf_char])) - { - // Combining character - if ($utf_combining_class[$utf_char] < $last_cc) - { - // Not in canonical order - $sort = $dump = 1; - } else { - $last_cc = $utf_combining_class[$utf_char]; - } + // This character decomposition contains an ASCII char, which is a starter. Dump the buffer and continue + ++$_pos; - $utf_sort[$utf_combining_class[$utf_char]][] = $utf_char; - } - else - { - // Non-decomposable starter, check out if it's a Hangul syllable - if ($utf_char < UTF8_HANGUL_FIRST || $utf_char > UTF8_HANGUL_LAST) - { - // Nope, regular UTF char, check that we have the correct number of trailing bytes - if (($utf_char & $utf_validation_mask[$utf_len]) != $utf_validation_check[$utf_len]) + if ($dump) { - // Current char isn't well-formed or legal: either one or several trailing bytes are missing, or the Unicode char - // has been encoded in a five- or six- byte sequence. - // Move the cursor back to its original position then advance it to the position it should really be at - $pos -= $utf_len; $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); + // Dump combiners if (!empty($utf_sort)) { - ksort($utf_sort); + if ($sort) + { + ksort($utf_sort); + } foreach ($utf_sort as $utf_chars) { $tmp .= implode('', $utf_chars); } - $utf_sort = array(); } - // Add a replacement char then another replacement char for every trailing byte. - // - // @todo I'm not entirely sure that's how we're supposed to mark invalidated byte sequences, check this - $spn = strspn($str, UTF8_TRAILING_BYTES, ++$pos); - $tmp .= str_repeat(UTF8_REPLACEMENT, $spn + 1); - + $tmp .= $c; $dump = $sort = 0; - - $pos += $spn; - $tmp_pos = $pos; - continue; } + else + { + $tmp .= substr($str, $tmp_pos, $pos - $utf_len - $tmp_pos) . $c; + } + + $tmp_pos = $starter_pos = $pos; + $utf_sort = array(); + $last_cc = 0; + } + } + while ($_pos < $_len); + } + else if (isset($utf_combining_class[$utf_char])) + { + // Combining character + if ($utf_combining_class[$utf_char] < $last_cc) + { + // Not in canonical order + $sort = $dump = 1; + } + else + { + $last_cc = $utf_combining_class[$utf_char]; + } - if (isset($extra_check[$utf_char[0]])) + $utf_sort[$utf_combining_class[$utf_char]][] = $utf_char; + } + else + { + // Non-decomposable starter, check out if it's a Hangul syllable + if ($utf_char < UTF8_HANGUL_FIRST || $utf_char > UTF8_HANGUL_LAST) + { + // Nope, regular UTF char, check that we have the correct number of trailing bytes + if (($utf_char & $utf_validation_mask[$utf_len]) != $utf_validation_check[$utf_len]) + { + // Current char isn't well-formed or legal: either one or several trailing bytes are missing, or the Unicode char + // has been encoded in a five- or six- byte sequence. + // Move the cursor back to its original position then advance it to the position it should really be at + $pos -= $utf_len; + $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); + + if (!empty($utf_sort)) { - switch ($utf_char[0]) + ksort($utf_sort); + + foreach ($utf_sort as $utf_chars) { - // Note: 0xED is quite common in Korean - case "\xED": - if ($utf_char >= "\xED\xA0\x80") + $tmp .= implode('', $utf_chars); + } + $utf_sort = array(); + } + + // Add a replacement char then another replacement char for every trailing byte. + // + // @todo I'm not entirely sure that's how we're supposed to mark invalidated byte sequences, check this + $spn = strspn($str, UTF8_TRAILING_BYTES, ++$pos); + $tmp .= str_repeat(UTF8_REPLACEMENT, $spn + 1); + + $dump = $sort = 0; + + $pos += $spn; + $tmp_pos = $pos; + continue; + } + + if (isset($extra_check[$utf_char[0]])) + { + switch ($utf_char[0]) + { + // Note: 0xED is quite common in Korean + case "\xED": + if ($utf_char >= "\xED\xA0\x80") + { + // Surrogates (U+D800..U+DFFF) are not allowed in UTF-8 (UTF sequence 0xEDA080..0xEDBFBF) + $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); + + if (!empty($utf_sort)) { - // Surrogates (U+D800..U+DFFF) are not allowed in UTF-8 (UTF sequence 0xEDA080..0xEDBFBF) - $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); + ksort($utf_sort); - if (!empty($utf_sort)) + foreach ($utf_sort as $utf_chars) { - ksort($utf_sort); - - foreach ($utf_sort as $utf_chars) - { - $tmp .= implode('', $utf_chars); - } - $utf_sort = array(); + $tmp .= implode('', $utf_chars); } + $utf_sort = array(); + } - $tmp .= UTF8_REPLACEMENT; - $dump = $sort = 0; + $tmp .= UTF8_REPLACEMENT; + $dump = $sort = 0; - $tmp_pos = $starter_pos = $pos; - continue 2; - } - break; + $tmp_pos = $starter_pos = $pos; + continue 2; + } + break; + + // Note: 0xEF is quite common in Japanese + case "\xEF": + if ($utf_char == "\xEF\xBF\xBE" || $utf_char == "\xEF\xBF\xBF") + { + // U+FFFE and U+FFFF are explicitly disallowed (UTF sequence 0xEFBFBE..0xEFBFBF) + $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); - // Note: 0xEF is quite common in Japanese - case "\xEF": - if ($utf_char == "\xEF\xBF\xBE" || $utf_char == "\xEF\xBF\xBF") + if (!empty($utf_sort)) { - // U+FFFE and U+FFFF are explicitly disallowed (UTF sequence 0xEFBFBE..0xEFBFBF) - $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); + ksort($utf_sort); - if (!empty($utf_sort)) + foreach ($utf_sort as $utf_chars) { - ksort($utf_sort); - - foreach ($utf_sort as $utf_chars) - { - $tmp .= implode('', $utf_chars); - } - $utf_sort = array(); + $tmp .= implode('', $utf_chars); } + $utf_sort = array(); + } - $tmp .= UTF8_REPLACEMENT; - $dump = $sort = 0; + $tmp .= UTF8_REPLACEMENT; + $dump = $sort = 0; - $tmp_pos = $starter_pos = $pos; - continue 2; - } - break; + $tmp_pos = $starter_pos = $pos; + continue 2; + } + break; + + case "\xC0": + case "\xC1": + if ($utf_char <= "\xC1\xBF") + { + // Overlong sequence: Unicode char U+0000..U+007F encoded as a double-byte UTF char + $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); - case "\xC0": - case "\xC1": - if ($utf_char <= "\xC1\xBF") + if (!empty($utf_sort)) { - // Overlong sequence: Unicode char U+0000..U+007F encoded as a double-byte UTF char - $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); + ksort($utf_sort); - if (!empty($utf_sort)) + foreach ($utf_sort as $utf_chars) { - ksort($utf_sort); - - foreach ($utf_sort as $utf_chars) - { - $tmp .= implode('', $utf_chars); - } - $utf_sort = array(); + $tmp .= implode('', $utf_chars); } + $utf_sort = array(); + } - $tmp .= UTF8_REPLACEMENT; - $dump = $sort = 0; + $tmp .= UTF8_REPLACEMENT; + $dump = $sort = 0; - $tmp_pos = $starter_pos = $pos; - continue 2; - } - break; + $tmp_pos = $starter_pos = $pos; + continue 2; + } + break; - case "\xE0": - if ($utf_char <= "\xE0\x9F\xBF") + case "\xE0": + if ($utf_char <= "\xE0\x9F\xBF") + { + // Unicode char U+0000..U+07FF encoded in 3 bytes + $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); + + if (!empty($utf_sort)) { - // Unicode char U+0000..U+07FF encoded in 3 bytes - $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); + ksort($utf_sort); - if (!empty($utf_sort)) + foreach ($utf_sort as $utf_chars) { - ksort($utf_sort); - - foreach ($utf_sort as $utf_chars) - { - $tmp .= implode('', $utf_chars); - } - $utf_sort = array(); + $tmp .= implode('', $utf_chars); } + $utf_sort = array(); + } - $tmp .= UTF8_REPLACEMENT; - $dump = $sort = 0; + $tmp .= UTF8_REPLACEMENT; + $dump = $sort = 0; - $tmp_pos = $starter_pos = $pos; - continue 2; - } - break; + $tmp_pos = $starter_pos = $pos; + continue 2; + } + break; - case "\xF0": - if ($utf_char <= "\xF0\x8F\xBF\xBF") + case "\xF0": + if ($utf_char <= "\xF0\x8F\xBF\xBF") + { + // Unicode char U+0000..U+FFFF encoded in 4 bytes + $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); + + if (!empty($utf_sort)) { - // Unicode char U+0000..U+FFFF encoded in 4 bytes - $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); + ksort($utf_sort); - if (!empty($utf_sort)) + foreach ($utf_sort as $utf_chars) { - ksort($utf_sort); - - foreach ($utf_sort as $utf_chars) - { - $tmp .= implode('', $utf_chars); - } - $utf_sort = array(); + $tmp .= implode('', $utf_chars); } + $utf_sort = array(); + } - $tmp .= UTF8_REPLACEMENT; - $dump = $sort = 0; + $tmp .= UTF8_REPLACEMENT; + $dump = $sort = 0; - $tmp_pos = $starter_pos = $pos; - continue 2; - } - break; + $tmp_pos = $starter_pos = $pos; + continue 2; + } + break; - default: - if ($utf_char > UTF8_MAX) + default: + if ($utf_char > UTF8_MAX) + { + // Out of the Unicode range + $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); + + if (!empty($utf_sort)) { - // Out of the Unicode range - $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); + ksort($utf_sort); - if (!empty($utf_sort)) + foreach ($utf_sort as $utf_chars) { - ksort($utf_sort); - - foreach ($utf_sort as $utf_chars) - { - $tmp .= implode('', $utf_chars); - } - $utf_sort = array(); + $tmp .= implode('', $utf_chars); } + $utf_sort = array(); + } - $tmp .= UTF8_REPLACEMENT; - $dump = $sort = 0; + $tmp .= UTF8_REPLACEMENT; + $dump = $sort = 0; - $tmp_pos = $starter_pos = $pos; - continue 2; - } - break; - } + $tmp_pos = $starter_pos = $pos; + continue 2; + } + break; } } - else - { - // Hangul syllable - $idx = (((ord($utf_char[0]) & 0x0F) << 12) | ((ord($utf_char[1]) & 0x3F) << 6) | (ord($utf_char[2]) & 0x3F)) - UNICODE_HANGUL_SBASE; + } + else + { + // Hangul syllable + $idx = (((ord($utf_char[0]) & 0x0F) << 12) | ((ord($utf_char[1]) & 0x3F) << 6) | (ord($utf_char[2]) & 0x3F)) - UNICODE_HANGUL_SBASE; - // LIndex can only range from 0 to 18, therefore it cannot influence the first two bytes of the L Jamo, which allows us to hardcode them (based on LBase). - // - // The same goes for VIndex, but for TIndex there's a catch: the value of the third byte could exceed 0xBF and we would have to increment the second byte - if ($tIndex = $idx % UNICODE_HANGUL_TCOUNT) + // LIndex can only range from 0 to 18, therefore it cannot influence the first two bytes of the L Jamo, which allows us to hardcode them (based on LBase). + // + // The same goes for VIndex, but for TIndex there's a catch: the value of the third byte could exceed 0xBF and we would have to increment the second byte + if ($tIndex = $idx % UNICODE_HANGUL_TCOUNT) + { + if ($tIndex < 25) { - if ($tIndex < 25) - { - $utf_char = "\xE1\x84\x00\xE1\x85\x00\xE1\x86\x00"; - $utf_char[8] = chr(0xA7 + $tIndex); - } - else - { - $utf_char = "\xE1\x84\x00\xE1\x85\x00\xE1\x87\x00"; - $utf_char[8] = chr(0x67 + $tIndex); - } + $utf_char = "\xE1\x84\x00\xE1\x85\x00\xE1\x86\x00"; + $utf_char[8] = chr(0xA7 + $tIndex); } else { - $utf_char = "\xE1\x84\x00\xE1\x85\x00"; + $utf_char = "\xE1\x84\x00\xE1\x85\x00\xE1\x87\x00"; + $utf_char[8] = chr(0x67 + $tIndex); } - - $utf_char[2] = chr(0x80 + (int) ($idx / UNICODE_HANGUL_NCOUNT)); - $utf_char[5] = chr(0xA1 + (int) (($idx % UNICODE_HANGUL_NCOUNT) / UNICODE_HANGUL_TCOUNT)); - - // Just like other decompositions, the resulting Jamos must be dumped to the tmp string - $dump = 1; } - - // Do we need to dump stuff to the tmp string? - if ($dump) + else { - $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); - - // Dump combiners - if (!empty($utf_sort)) - { - if ($sort) - { - ksort($utf_sort); - } - - foreach ($utf_sort as $utf_chars) - { - $tmp .= implode('', $utf_chars); - } - } - - $tmp .= $utf_char; - $dump = $sort = 0; - $tmp_pos = $pos; + $utf_char = "\xE1\x84\x00\xE1\x85\x00"; } - $last_cc = 0; - $utf_sort = array(); - $starter_pos = $pos; + $utf_char[2] = chr(0x80 + (int) ($idx / UNICODE_HANGUL_NCOUNT)); + $utf_char[5] = chr(0xA1 + (int) (($idx % UNICODE_HANGUL_NCOUNT) / UNICODE_HANGUL_TCOUNT)); + + // Just like other decompositions, the resulting Jamos must be dumped to the tmp string + $dump = 1; } - } - else - { - // ASCII char, which happens to be a starter (as any other ASCII char) + + // Do we need to dump stuff to the tmp string? if ($dump) { $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); @@ -1530,15 +1409,9 @@ else } } - $tmp .= $str[$pos]; + $tmp .= $utf_char; $dump = $sort = 0; - $tmp_pos = ++$pos; - - $pos += strspn($str, UTF8_ASCII_RANGE, $pos); - } - else - { - $pos += strspn($str, UTF8_ASCII_RANGE, ++$pos); + $tmp_pos = $pos; } $last_cc = 0; @@ -1546,48 +1419,84 @@ else $starter_pos = $pos; } } - while ($pos < $len); - - // Now is time to return the string - if ($dump) + else { - $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); - - // Dump combiners - if (!empty($utf_sort)) + // ASCII char, which happens to be a starter (as any other ASCII char) + if ($dump) { - if ($sort) - { - ksort($utf_sort); - } + $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); - foreach ($utf_sort as $utf_chars) + // Dump combiners + if (!empty($utf_sort)) { - $tmp .= implode('', $utf_chars); + if ($sort) + { + ksort($utf_sort); + } + + foreach ($utf_sort as $utf_chars) + { + $tmp .= implode('', $utf_chars); + } } - } - return $tmp; + $tmp .= $str[$pos]; + $dump = $sort = 0; + $tmp_pos = ++$pos; + $pos += strspn($str, UTF8_ASCII_RANGE, $pos); + } + else + { + $pos += strspn($str, UTF8_ASCII_RANGE, ++$pos); + } + + $last_cc = 0; + $utf_sort = array(); + $starter_pos = $pos; } - else if ($tmp_pos) + } + while ($pos < $len); + + // Now is time to return the string + if ($dump) + { + $tmp .= substr($str, $tmp_pos, $starter_pos - $tmp_pos); + + // Dump combiners + if (!empty($utf_sort)) { - // If the $tmp_pos cursor was moved then at least one character was not in normal form. Replace $str with the fixed version - if ($tmp_pos == $len) + if ($sort) { - // The $tmp_pos cursor is at the end of $str, therefore $tmp holds the whole $str - return $tmp; + ksort($utf_sort); } - else + + foreach ($utf_sort as $utf_chars) { - // The rightmost chunk of $str has not been appended to $tmp yet - return $tmp . substr($str, $tmp_pos); + $tmp .= implode('', $utf_chars); } } - // The string was already in normal form - return $str; + return $tmp; + } + else if ($tmp_pos) + { + // If the $tmp_pos cursor was moved then at least one character was not in normal form. Replace $str with the fixed version + if ($tmp_pos == $len) + { + // The $tmp_pos cursor is at the end of $str, therefore $tmp holds the whole $str + return $tmp; + } + else + { + // The rightmost chunk of $str has not been appended to $tmp yet + return $tmp . substr($str, $tmp_pos); + } + } + + // The string was already in normal form + return $str; } } diff --git a/phpBB/includes/utf/utf_tools.php b/phpBB/includes/utf/utf_tools.php index aa29159d5e..7a2b536e97 100644 --- a/phpBB/includes/utf/utf_tools.php +++ b/phpBB/includes/utf/utf_tools.php @@ -781,7 +781,7 @@ function utf8_recode($string, $encoding) */ function utf8_encode_ncr($text) { - return preg_replace_callback('#[\\xC2-\\xF4][\\x80-\\xBF]?[\\x80-\\xBF]?[\\x80-\\xBF]+#', 'utf8_encode_ncr_callback', $text); + return preg_replace_callback('#[\\xC2-\\xF4][\\x80-\\xBF]{1,3}#', 'utf8_encode_ncr_callback', $text); } /** @@ -824,7 +824,7 @@ function utf8_ord($chr) break; default: - return $m; + return $chr; } } |