diff options
-rw-r--r-- | phpBB/adm/style/acp_captcha.html | 90 | ||||
-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 | ||||
-rwxr-xr-x | phpBB/install/install_install.php | 16 | ||||
-rw-r--r-- | phpBB/install/schemas/schema_data.sql | 15 | ||||
-rw-r--r-- | phpBB/language/en/acp/board.php | 26 |
12 files changed, 1240 insertions, 4791 deletions
diff --git a/phpBB/adm/style/acp_captcha.html b/phpBB/adm/style/acp_captcha.html index b5524f26e1..4378e538f1 100644 --- a/phpBB/adm/style/acp_captcha.html +++ b/phpBB/adm/style/acp_captcha.html @@ -18,98 +18,16 @@ <dt><label for="enable_post_confirm">{L_VISUAL_CONFIRM_POST}:</label><br /><span>{L_VISUAL_CONFIRM_POST_EXPLAIN}</span></dt> <dd><input type="radio" class="radio" id="enable_post_confirm" name="enable_post_confirm" value="1"<!-- IF POST_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_ENABLED} <input type="radio" class="radio" name="enable_post_confirm" value="0"<!-- IF not POST_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_DISABLED}</dd> </dl> -</fieldset> - <!-- IF GD --> -<fieldset> - <legend>{L_CAPTCHA_OVERLAP}</legend> -<!-- IF TTF --> -<dl> - <dt><label for="policy_overlap">{L_CAPTCHA_OVERLAP}:</label><br /><span>{U_POLICY_OVERLAP}</span></dt> - <dd><input id="policy_overlap" name="policy_overlap" value="1" class="radio" type="radio"<!-- IF OVERLAP_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_YES} <input name="policy_overlap" value="0" class="radio" type="radio"<!-- IF not OVERLAP_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_NO}</dd> -</dl> -<dl> - <dt><label for="policy_overlap_noise_pixel">{L_OVERLAP_NOISE_PIXEL}:</label></dt> - <dd><select id="policy_overlap_noise_pixel" name="policy_overlap_noise_pixel"><option value="0"<!-- IF OVERLAP_NOISE_PIXEL eq '0' --> selected="selected"<!-- ENDIF -->>{L_NO_NOISE}</option><option value="1"<!-- IF OVERLAP_NOISE_PIXEL eq '1' --> selected="selected"<!-- ENDIF -->>{L_LIGHT}</option><option value="2"<!-- IF OVERLAP_NOISE_PIXEL eq '2' --> selected="selected"<!-- ENDIF -->>{L_MEDIUM}</option><option value="3"<!-- IF OVERLAP_NOISE_PIXEL eq '3' --> selected="selected"<!-- ENDIF -->>{L_HEAVY}</option></select></dd> -</dl> -<dl> - <dt><label for="policy_overlap_noise_line">{L_OVERLAP_NOISE_LINE}:</label></dt> - - <dd><input id="policy_overlap_noise_line" name="policy_overlap_noise_line" value="1" class="radio" type="radio"<!-- IF OVERLAP_NOISE_LINE_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_YES} <input name="policy_overlap_noise_line" value="0" class="radio" type="radio"<!-- IF not OVERLAP_NOISE_LINE_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_NO}</dd> -</dl> -</fieldset> -<!-- ENDIF --> - -<fieldset> - <legend>{L_CAPTCHA_ENTROPY}</legend> -<dl> - <dt><label for="policy_entropy">{L_CAPTCHA_ENTROPY}:</label><br /><span>{U_POLICY_ENTROPY}</span></dt> - <dd><input id="policy_entropy" name="policy_entropy" value="1" class="radio" type="radio"<!-- IF ENTROPY_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_YES} <input name="policy_entropy" value="0" class="radio" type="radio"<!-- IF not ENTROPY_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_NO}</dd> -</dl> -<dl> - <dt><label for="policy_entropy_noise_pixel">{L_ENTROPY_NOISE_PIXEL}:</label></dt> - <dd><select id="policy_entropy_noise_pixel" name="policy_entropy_noise_pixel"><option value="0"<!-- IF ENTROPY_NOISE_PIXEL eq '0' --> selected="selected"<!-- ENDIF -->>{L_NO_NOISE}</option><option value="1"<!-- IF ENTROPY_NOISE_PIXEL eq '1' --> selected="selected"<!-- ENDIF -->>{L_LIGHT}</option><option value="2"<!-- IF ENTROPY_NOISE_PIXEL eq '2' --> selected="selected"<!-- ENDIF -->>{L_MEDIUM}</option><option value="3"<!-- IF ENTROPY_NOISE_PIXEL eq '3' --> selected="selected"<!-- ENDIF -->>{L_HEAVY}</option></select></dd> -</dl> -<dl> - - <dt><label for="policy_entropy_noise_line">{L_ENTROPY_NOISE_LINE}:</label></dt> - <dd><input id="policy_entropy_noise_line" name="policy_entropy_noise_line" value="1" class="radio" type="radio"<!-- IF ENTROPY_NOISE_LINE_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_YES} <input name="policy_entropy_noise_line" value="0" class="radio" type="radio"<!-- IF not ENTROPY_NOISE_LINE_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_NO}</dd> -</dl> -</fieldset> - -<!-- IF TTF --> -<fieldset> - <legend>{L_CAPTCHA_SHAPE}</legend> -<dl> - <dt><label for="policy_shape">{L_CAPTCHA_SHAPE}:</label><br /><span>{U_POLICY_SHAPE}</span></dt> - <dd><input id="policy_shape" name="policy_shape" value="1" class="radio" type="radio"<!-- IF SHAPE_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_YES} <input name="policy_shape" value="0" class="radio" type="radio"<!-- IF not SHAPE_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_NO}</dd> - -</dl> -<dl> - <dt><label for="policy_shape_noise_pixel">{L_SHAPE_NOISE_PIXEL}:</label></dt> - <dd><select id="policy_shape_noise_pixel" name="policy_shape_noise_pixel"><option value="0"<!-- IF SHAPE_NOISE_PIXEL eq '0' --> selected="selected"<!-- ENDIF -->>{L_NO_NOISE}</option><option value="1"<!-- IF SHAPE_NOISE_PIXEL eq '1' --> selected="selected"<!-- ENDIF -->>{L_LIGHT}</option><option value="2"<!-- IF SHAPE_NOISE_PIXEL eq '2' --> selected="selected"<!-- ENDIF -->>{L_MEDIUM}</option><option value="3"<!-- IF SHAPE_NOISE_PIXEL eq '3' --> selected="selected"<!-- ENDIF -->>{L_HEAVY}</option></select></dd> -</dl> <dl> - <dt><label for="policy_shape_noise_line">{L_SHAPE_NOISE_LINE}:</label></dt> - <dd><input id="policy_shape_noise_line" name="policy_shape_noise_line" value="1" class="radio" type="radio"<!-- IF SHAPE_NOISE_LINE_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_YES} <input name="policy_shape_noise_line" value="0" class="radio" type="radio"<!-- IF not SHAPE_NOISE_LINE_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_NO}</dd> + <dt><label for="captcha_gd">{L_CAPTCHA_GD}:</label><br /><span>{L_CAPTCHA_GD_EXPLAIN}</span></dt> + <dd><input id="captcha_gd" name="captcha_gd" value="1" class="radio" type="radio"<!-- IF CAPTCHA_GD --> checked="checked"<!-- ENDIF --> /> {L_YES} <input name="captcha_gd" value="0" class="radio" type="radio"<!-- IF not CAPTCHA_GD --> checked="checked"<!-- ENDIF --> /> {L_NO}</dd> </dl> -</fieldset> -<!-- ENDIF --> - -<fieldset> - <legend>{L_CAPTCHA_3DBITMAP}</legend> -<dl> - <dt><label for="policy_3dbitmap">{L_CAPTCHA_3DBITMAP}:</label><br /><span>{U_POLICY_3DBITMAP}</span></dt> - <dd><input id="policy_3dbitmap" name="policy_3dbitmap" value="1" class="radio" type="radio"<!-- IF THREEDBITMAP_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_YES} <input name="policy_3dbitmap" value="0" class="radio" type="radio"<!-- IF not THREEDBITMAP_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_NO}</dd> -</dl> -</fieldset> - -<!-- IF TTF --> -<fieldset> - <legend>{L_CAPTCHA_CELLS}</legend> <dl> - <dt><label for="policy_cells">{L_CAPTCHA_CELLS}:</label><br /><span>{U_POLICY_CELLS}</span></dt> - <dd><input id="policy_cells" name="policy_cells" value="1" class="radio" type="radio"<!-- IF CELLS_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_YES} <input name="policy_cells" value="0" class="radio" type="radio"<!-- IF not CELLS_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_NO}</dd> + <dt><label for="captcha_gd_noise">{L_CAPTCHA_GD_NOISE}:</label><br /><span>{L_CAPTCHA_GD_NOISE_EXPLAIN}</span></dt> + <dd><input id="captcha_gd_noise" name="captcha_gd_noise" value="1" class="radio" type="radio"<!-- IF CAPTCHA_GD_NOISE --> checked="checked"<!-- ENDIF --> /> {L_YES} <input name="captcha_gd_noise" value="0" class="radio" type="radio"<!-- IF not CAPTCHA_GD_NOISE --> checked="checked"<!-- ENDIF --> /> {L_NO}</dd> </dl> </fieldset> - -<fieldset> - <legend>{L_CAPTCHA_STENCIL}</legend> -<dl> - <dt><label for="policy_stencil">{L_CAPTCHA_STENCIL}:</label><br /><span>{U_POLICY_STENCIL}</span></dt> - <dd><input id="policy_stencil" name="policy_stencil" value="1" class="radio" type="radio"<!-- IF STENCIL_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_YES} <input name="policy_stencil" value="0" class="radio" type="radio"<!-- IF not STENCIL_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_NO}</dd> -</dl> -</fieldset> - -<fieldset> - <legend>{L_CAPTCHA_COMPOSITE}</legend> -<dl> - <dt><label for="policy_composite">{L_CAPTCHA_COMPOSITE}:</label><br /><span>{U_POLICY_COMPOSITE}</span></dt> - <dd><input id="policy_composite" name="policy_composite" value="1" class="radio" type="radio"<!-- IF COMPOSITE_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_YES} <input name="policy_composite" value="0" class="radio" type="radio"<!-- IF not COMPOSITE_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_NO}</dd> -</dl> -</fieldset> -<!-- ENDIF --> - <!-- ENDIF --> <fieldset class="submit-buttons"> 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; } } diff --git a/phpBB/install/install_install.php b/phpBB/install/install_install.php index 36f99db0d3..df834a367e 100755 --- a/phpBB/install/install_install.php +++ b/phpBB/install/install_install.php @@ -1185,12 +1185,12 @@ class install_install extends module SET forum_last_post_time = $current_time", ); - // This is for people who have TTF disabled - if (!(@function_exists('imagettfbbox') && @function_exists('imagettftext'))) + // This is for people who have TTF and GD + if (@extension_loaded('gd') && function_exists('imagettfbbox') && function_exists('imagettftext')) { $sql_ary[] = 'UPDATE ' . $table_prefix . "config - SET config_value = '0' - WHERE config_name = 'policy_shape'"; + SET config_value = '1' + WHERE config_name = 'captcha_gd'"; } foreach ($sql_ary as $sql) @@ -1732,11 +1732,17 @@ class install_install extends module { case 'mysql': case 'mysqli': - case 'sqlite': $sql = 'SHOW TABLES'; $field = "Tables_in_{$dbname}"; break; + case 'sqlite': + $sql = 'SELECT name + FROM sqlite_master + WHERE type = "table"'; + $field = 'name'; + break; + case 'mssql': case 'mssql_odbc': $sql = "SELECT name diff --git a/phpBB/install/schemas/schema_data.sql b/phpBB/install/schemas/schema_data.sql index e7785f5d62..82a6608ae6 100644 --- a/phpBB/install/schemas/schema_data.sql +++ b/phpBB/install/schemas/schema_data.sql @@ -163,19 +163,8 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('pass_complex', '.* INSERT INTO phpbb_config (config_name, config_value) VALUES ('pm_edit_time', '0'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('pm_max_boxes', '4'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('pm_max_msgs', '50'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('policy_overlap', '0'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('policy_overlap_noise_pixel', '1'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('policy_overlap_noise_line', '1'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('policy_entropy', '1'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('policy_entropy_noise_pixel', '2'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('policy_entropy_noise_line', '1'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('policy_shape', '1'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('policy_shape_noise_pixel', '1'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('policy_shape_noise_line', '1'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('policy_3dbitmap', '0'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('policy_cells', '0'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('policy_stencil', '0'); -INSERT INTO phpbb_config (config_name, config_value) VALUES ('policy_composite', '0'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('captcha_gd', '0'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('captcha_gd_noise', '1'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('posts_per_page', '10'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('print_pm', '1'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('queue_interval', '600'); diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php index dc66d8261e..e2d2f30e6b 100644 --- a/phpBB/language/en/acp/board.php +++ b/phpBB/language/en/acp/board.php @@ -213,28 +213,10 @@ $lang = array_merge($lang, array( $lang = array_merge($lang, array( 'ACP_VC_SETTINGS_EXPLAIN' => 'Here you are able to define visual confirmation defaults and captcha settings.', - 'BAD_POLICY' => 'The policy you selected is invalid.', - 'CAPTCHA_3DBITMAP' => '3D Bitmap', - 'CAPTCHA_CELLS' => 'Cells', - 'CAPTCHA_COMPOSITE' => 'Composite', - 'CAPTCHA_ENTROPY' => 'Entropy', - 'CAPTCHA_OPTIONS' => 'Captcha options', - 'CAPTCHA_OVERLAP' => 'Overlap', - 'CAPTCHA_SHAPE' => 'Shape', - 'CAPTCHA_STENCIL' => 'Stencil', - 'CAPTCHA_EXPLAIN' => '%sPreview this CAPTCHA policy%s.', - 'ENTROPY_NOISE_LINE' => 'Entropy line noise', - 'ENTROPY_NOISE_PIXEL' => 'Entropy pixel noise', - 'HEAVY' => 'Heavy', - 'LIGHT' => 'Light', - 'MEDIUM' => 'Medium', - 'NO_GD' => 'The server does not support GD, an extension required to enable the advanced CAPTCHA policies. A fall-back method will be used if you decide to enable visual confirmation features.', - 'NO_NOISE' => 'None', - 'NO_TTF' => 'The policy you selected requires TTF enabled, the server has TTF disabled. If this policy is enabled, it will be skipped and another policy will be used in its place. If no valid policies are enabled, a fall-back method will be used if you decide to enable Visual Confirmation features.', - 'OVERLAP_NOISE_LINE' => 'Overlap line noise', - 'OVERLAP_NOISE_PIXEL' => 'Overlap pixel noise', - 'SHAPE_NOISE_LINE' => 'Shape line noise', - 'SHAPE_NOISE_PIXEL' => 'Shape pixel noise', + 'CAPTCHA_GD' => 'GD CAPTCHA', + 'CAPTCHA_GD_NOISE' => 'GD CAPTCHA Noise', + 'CAPTCHA_GD_EXPLAIN' => 'Use GD to make a more advanced CAPTCHA', + 'CAPTCHA_GD_NOISE_EXPLAIN' => 'Use noise to make the GD based CAPTCHA harder', 'VISUAL_CONFIRM_POST' => 'Enable visual confirmation for guest postings', 'VISUAL_CONFIRM_POST_EXPLAIN' => 'Requires anonymous users to enter a random code matching an image to help prevent mass postings.', 'VISUAL_CONFIRM_REG' => 'Enable visual confirmation for registrations', |