diff options
-rw-r--r-- | phpBB/adm/style/acp_captcha.html | 35 | ||||
-rw-r--r-- | phpBB/docs/AUTHORS | 5 | ||||
-rw-r--r-- | phpBB/includes/acp/acp_captcha.php | 54 | ||||
-rw-r--r-- | phpBB/includes/captcha/captcha_gd.php | 1577 | ||||
-rw-r--r-- | phpBB/install/database_update.php | 3 | ||||
-rwxr-xr-x | phpBB/install/install_install.php | 5 | ||||
-rw-r--r-- | phpBB/install/schemas/schema_data.sql | 4 | ||||
-rw-r--r-- | phpBB/language/en/acp/board.php | 23 |
8 files changed, 1597 insertions, 109 deletions
diff --git a/phpBB/adm/style/acp_captcha.html b/phpBB/adm/style/acp_captcha.html index 4421014b3d..b5c12f29b1 100644 --- a/phpBB/adm/style/acp_captcha.html +++ b/phpBB/adm/style/acp_captcha.html @@ -6,10 +6,12 @@ <p>{L_ACP_VC_SETTINGS_EXPLAIN}</p> + <form id="acp_captcha" method="post" action="{U_ACTION}"> <fieldset> - <legend>{L_GENERAL_OPTIONS}</legend> +<legend>{L_GENERAL_OPTIONS}</legend> + <dl> <dt><label for="enable_confirm">{L_VISUAL_CONFIRM_REG}:</label><br /><span>{L_VISUAL_CONFIRM_REG_EXPLAIN}</span></dt> <dd><label><input type="radio" class="radio" id="enable_confirm" name="enable_confirm" value="1"<!-- IF REG_ENABLE --> checked="checked"<!-- ENDIF --> /> {L_ENABLED}</label> @@ -27,17 +29,40 @@ <label><input name="captcha_gd" value="0" class="radio" type="radio"<!-- IF not CAPTCHA_GD --> checked="checked"<!-- ENDIF --> /> {L_NO}</label></dd> </dl> <dl> - <dt><label for="captcha_gd_noise">{L_CAPTCHA_GD_NOISE}:</label><br /><span>{L_CAPTCHA_GD_NOISE_EXPLAIN}</span></dt> - <dd><label><input id="captcha_gd_noise" name="captcha_gd_noise" value="1" class="radio" type="radio"<!-- IF CAPTCHA_GD_NOISE --> checked="checked"<!-- ENDIF --> /> {L_YES}</label> - <label><input name="captcha_gd_noise" value="0" class="radio" type="radio"<!-- IF not CAPTCHA_GD_NOISE --> checked="checked"<!-- ENDIF --> /> {L_NO}</label></dd> + <dt><label for="captcha_gd_foreground_noise">{L_CAPTCHA_GD_FOREGROUND_NOISE}:</label><br /><span>{L_CAPTCHA_GD_FOREGROUND_NOISE_EXPLAIN}</span></dt> + <dd><label><input id="captcha_gd_foreground_noise" name="captcha_gd_foreground_noise" value="1" class="radio" type="radio"<!-- IF CAPTCHA_GD_FOREGROUND_NOISE --> checked="checked"<!-- ENDIF --> /> {L_YES}</label> + <label><input name="captcha_gd_foreground_noise" value="0" class="radio" type="radio"<!-- IF not CAPTCHA_GD_FOREGROUND_NOISE --> checked="checked"<!-- ENDIF --> /> {L_NO}</label></dd> +</dl> +<dl> + <dt><label for="captcha_gd_x_grid">{L_CAPTCHA_GD_X_GRID}:</label><br /><span>{L_CAPTCHA_GD_X_GRID_EXPLAIN}</span></dt> + <dd><input id="captcha_gd_x_grid" name="captcha_gd_x_grid" value="{CAPTCHA_GD_X_GRID}" type="text" /></dd> +</dl> +<dl> + <dt><label for="captcha_gd_y_grid">{L_CAPTCHA_GD_Y_GRID}:</label><br /><span>{L_CAPTCHA_GD_Y_GRID_EXPLAIN}</span></dt> + <dd><input id="captcha_gd_y_grid" name="captcha_gd_y_grid" value="{CAPTCHA_GD_Y_GRID}" type="text" /></dd> </dl> <!-- ENDIF --> </fieldset> +<fieldset> + <legend>{L_PREVIEW}</legend> +<!-- IF PREVIEW --> + <div class="successbox"> + <h3>{L_WARNING}</h3> + <p>{L_CAPTCHA_PREVIEW_MSG}</p> + </div> +<!-- ENDIF --> +<dl> + <dt><label for="captcha_preview">{L_PREVIEW}:</label><br /><span>{L_CAPTCHA_PREVIEW_EXPLAIN}</span></dt> + <dd><img src="{CAPTCHA_PREVIEW}" alt="{L_PREVIEW}" <!-- IF CAPTCHA_GD_PREVIEWED -->width="360" height="96"<!-- ELSE --> width="320" height="50"<!-- ENDIF --> id="captcha_preview" /></dd> +</dl> +</fieldset> + <fieldset class="submit-buttons"> <legend>{L_SUBMIT}</legend> <input class="button1" type="submit" id="submit" name="submit" value="{L_SUBMIT}" /> - <input class="button2" type="reset" id="reset" name="reset" value="{L_RESET}" /> + <input class="button2" type="reset" id="reset" name="reset" value="{L_RESET}" /> + <input class="button2" type="submit" id="preview" name="preview" value="{L_PREVIEW}" /> </fieldset> </form> diff --git a/phpBB/docs/AUTHORS b/phpBB/docs/AUTHORS index eadc0f8711..02866d7dc1 100644 --- a/phpBB/docs/AUTHORS +++ b/phpBB/docs/AUTHORS @@ -20,7 +20,6 @@ phpBB Developers : DavidMJ (David M.) subBlue (Tom Beddard) Vic (Vic D'elfant) - -- Previous Contributors -- phpBB Project Manager : theFinn (James Atkinson) [Founder - 04/2007] @@ -34,6 +33,8 @@ phpBB Developers : Ashe (Ludovic Arnaud) [10/2002 - 11/2003, 06/2006 - 10 -- Copyrights -- +Visual Confirmation : Xore (Robert Hetzler) + Original subSilver by subBlue Design, Tom Beddard, (c) 2001 phpBB Group prosilver by subBlue Design, Tom Beddard, (c) 2004 phpBB Group subsilver2 by subBlue Design, Tom Beddard, (c) 2004 phpBB Group @@ -44,7 +45,7 @@ LGPL licenced: Smarty (c) 2001, 2002 by ispi of Lincoln, Inc, http://smarty.php.net/ GPL licenced: -phpMyAdmin (c) 2001,2003 phpMyAdmin Devel team, http://www.phpmyadmin.net/ +phpMyAdmin (c) 2001,2003 phpMyAdmin Devel team, http://www.phpmyadmin.net/ Jabber Class (c) 2004 Nathan Fritz, http://cjphp.netflint.net Chora (c) 2000-2006, The Horde Project. http://horde.org/chora/ Horde Project (c) 2000-2006, The Horde Project. http://horde.org/ diff --git a/phpBB/includes/acp/acp_captcha.php b/phpBB/includes/acp/acp_captcha.php index 7cef658e93..7386c378a3 100644 --- a/phpBB/includes/acp/acp_captcha.php +++ b/phpBB/includes/acp/acp_captcha.php @@ -21,16 +21,44 @@ class acp_captcha $user->add_lang('acp/board'); + + $captcha_vars = array( + 'captcha_gd_x_grid' => 'CAPTCHA_GD_X_GRID', + 'captcha_gd_y_grid' => 'CAPTCHA_GD_Y_GRID', + 'captcha_gd_foreground_noise' => 'CAPTCHA_GD_FOREGROUND_NOISE', + 'captcha_gd' => 'CAPTCHA_GD_PREVIEWED' + ); + + if (isset($_GET['demo'])) + { + $captcha_vars = array_keys($captcha_vars); + foreach ($captcha_vars as $captcha_var) + { + $config[$captcha_var] = (isset($_REQUEST[$captcha_var])) ? request_var($captcha_var, 0) : $config[$captcha_var]; + } + if ($config['captcha_gd']) + { + include($phpbb_root_path . 'includes/captcha/captcha_gd.' . $phpEx); + } + else + { + include($phpbb_root_path . 'includes/captcha/captcha_non_gd.' . $phpEx); + } + $captcha = new captcha(); + $captcha->execute(gen_rand_string(mt_rand(5, 8)), time()); + exit; + } + $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) { $config_vars = array_keys($config_vars); @@ -38,20 +66,36 @@ class acp_captcha { set_config($config_var, request_var($config_var, '')); } + $captcha_vars = array_keys($captcha_vars); + foreach ($captcha_vars as $captcha_var) + { + set_config($captcha_var, request_var($captcha_var, 0)); + } 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')) + + $preview_image_src = append_sid(append_sid("{$phpbb_admin_path}index.$phpEx", "i=$id&demo=demo")); + if (@extension_loaded('gd')) { $template->assign_var('GD', true); } foreach ($config_vars as $config_var => $template_var) { - $template->assign_var($template_var, $config[$config_var]); + $template->assign_var($template_var, (isset($_REQUEST[$config_var])) ? request_var($config_var, '') : $config[$config_var]) ; + } + foreach ($captcha_vars as $captcha_var => $template_var) + { + $var = (isset($_REQUEST[$captcha_var])) ? request_var($captcha_var, 0) : $config[$captcha_var]; + $template->assign_var($template_var, $var); + $preview_image_src .= "&$captcha_var=" . $var; } + $template->assign_vars(array( + 'CAPTCHA_PREVIEW' => $preview_image_src, + 'PREVIEW' => isset($_POST['preview']), + )); + } } } diff --git a/phpBB/includes/captcha/captcha_gd.php b/phpBB/includes/captcha/captcha_gd.php index 662d55f5f7..9ca073d9ac 100644 --- a/phpBB/includes/captcha/captcha_gd.php +++ b/phpBB/includes/captcha/captcha_gd.php @@ -8,9 +8,11 @@ * */ + + /** -* Based on PHP-Class hn_captcha Version 1.3, released 11-Apr-2006 -* Original Author - Horst Nogajski, horst@nogajski.de +* Original Author - Xore (Robert Hetzler) +* With contributions from Neothermic * * @package VC */ @@ -19,142 +21,1551 @@ class captcha var $width = 360; var $height = 96; + /** + * Create the image containing $code with a seed of $seed + */ function execute($code, $seed) { global $config; - $stats = gd_info(); - $bundled = (substr($stats['GD Version'], 0, 7) === 'bundled') ? true : false; + mt_srand($seed); + + // Create image + $img = imagecreatetruecolor($this->width, $this->height); + + // Generate colours + $colour = new colour_manager($img, array( + 'random' => true, + 'min_value' => 60, + ), 'hsv'); + + $scheme = $colour->colour_scheme('background', false); + $scheme = $colour->mono_range($scheme, 10, false); + shuffle($scheme); + $bg_colours = array_splice($scheme, mt_rand(6, 12)); - preg_match('/[\\d.]+/', $stats['GD Version'], $version); - $gd_version = (version_compare($version[0], '2.0.1', '>=')) ? 2 : 1; + // Generate code characters + $characters = $sizes = $bounding_boxes = array(); + $width_avail = $this->width - 10; + $code_len = strlen($code); - // create the image, stay compat with older versions of GD - if ($gd_version === 2) + $captcha_bitmaps = $this->captcha_bitmaps(); + for ($i = 0; $i < $code_len; ++$i) { - $func1 = 'imagecreatetruecolor'; - $func2 = 'imagecolorallocate'; + $characters[$i] = new char_cube3d($captcha_bitmaps, $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; } - else + + // Redistribute leftover x-space + $offset = array(); + for ($i = 0; $i < $code_len; ++$i) { - $func1 = 'imagecreate'; - $func2 = 'imagecolorclosest'; + $denom = ($code_len - $i); + $denom = max(1.3, $denom); + $offset[$i] = mt_rand(0, (1.5 * $width_avail) / $denom); + $width_avail -= $offset[$i]; } - $image = $func1($this->width, $this->height); + if ($config['captcha_gd_x_grid']) + { + $grid = (int) $config['captcha_gd_x_grid']; + for ($y = 0; $y < $this->height; $y += mt_rand($grid - 2, $grid + 2)) + { + $current_colour = $scheme[array_rand($scheme)]; + imageline($img, mt_rand(0,4), mt_rand($y - 3, $y), mt_rand($this->width - 5, $this->width), mt_rand($y - 3, $y), $current_colour); + } + } - if ($bundled) + if ($config['captcha_gd_y_grid']) { - imageantialias($image, true); + $grid = (int) $config['captcha_gd_y_grid']; + for ($x = 0; $x < $this->width; $x += mt_rand($grid - 2, $grid + 2)) + { + $current_colour = $scheme[array_rand($scheme)]; + imagedashedline($img, mt_rand($x -3, $x + 3), mt_rand(0, 4), mt_rand($x -3, $x + 3), mt_rand($this->height - 5, $this->height), $current_colour); + } } - // seed the random generator - mt_srand($seed); + $xoffset = 5; + for ($i = 0; $i < $code_len; ++$i) + { + $dimm = $bounding_boxes[$i]; + $xoffset += ($offset[$i] - $dimm[0]); + $yoffset = mt_rand(-$dimm[1], $this->height - $dimm[3]); + + $characters[$i]->drawchar($sizes[$i], $xoffset, $yoffset, $img, $colour->get_resource('background'), $scheme); + $xoffset += $dimm[2]; + } + + if ($config['captcha_gd_foreground_noise']) + { + $this->noise_line($img, 0, 0, $this->width, $this->height, $colour->get_resource('background'), $scheme, $bg_colours); + } + + // Send image + header('Content-Type: image/png'); + header('Cache-control: no-cache, no-store'); + imagepng($img); + imagedestroy($img); + } + + /** + * 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); + } + + /** + * 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), + ), + ) + ); + } +} + +/** +* @package VC +*/ +class char_cube3d +{ + var $bitmap; + var $bitmap_width; + var $bitmap_height; + + 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(&$bitmaps, $letter) + { + $this->bitmap = $bitmaps['data'][$letter]; + $this->bitmap_width = $bitmaps['width']; + $this->bitmap_height = $bitmaps['height']; + + $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); - // set background color - $back = imagecolorallocate($image, mt_rand(224, 255), mt_rand(224, 255), mt_rand(224, 255)); - imagefilledrectangle($image, 0, 0, $this->width, $this->height, $back); + $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]); - // allocates the 216 websafe color palette to the image - if ($gd_version === 1) + // $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) { - for ($r = 0; $r <= 255; $r += 51) + if ($this->basis_matrix[$i][2] < 0) { - for ($g = 0; $g <= 255; $g += 51) - { - for ($b = 0; $b <= 255; $b += 51) - { - imagecolorallocate($image, $r, $g, $b); - } - } + $this->basis_matrix[$i][0] *= -1; + $this->basis_matrix[$i][1] *= -1; + $this->basis_matrix[$i][2] *= -1; } } - // fill with noise or grid - if ($config['captcha_gd_noise']) + // Force our "z" basis vector to be the one with greatest absolute z value + $this->x = 0; + $this->y = 1; + $this->z = 2; + + // Swap "y" with "z" + if ($this->basis_matrix[1][2] > $this->basis_matrix[2][2]) + { + $this->z = 1; + $this->y = 2; + } + + // Swap "x" with "z" + if ($this->basis_matrix[0][2] > $this->basis_matrix[$this->z][2]) + { + $this->x = $this->z; + $this->z = 0; + } + + // 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])); + + // Swap "x" with "y" + if ($weight > 0) + { + list($this->x, $this->y) = array($this->y, $this->x); + } + + $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; + } + + /** + * Draw a character + */ + function drawchar($scale, $xoff, $yoff, $img, $background, $colours) + { + $width = $this->bitmap_width; + $height = $this->bitmap_height; + $bitmap = $this->bitmap; + + $colour1 = $colours[array_rand($colours)]; + $colour2 = $colours[array_rand($colours)]; + + $swapx = ($this->basis_matrix[$this->x][0] > 0); + $swapy = ($this->basis_matrix[$this->y][1] < 0); + + for ($y = 0; $y < $height; ++$y) { - $chars_allowed = array_merge(range('1', '9'), range('A', 'Z')); - // random characters in background with random position, angle, color - for ($i = 0 ; $i < 72; $i++) + for ($x = 0; $x < $width; ++$x) { - $size = mt_rand(8, 23); - $angle = mt_rand(0, 360); - $x = mt_rand(0, 360); - $y = mt_rand(0, (int)($this->height - ($size / 5))); - $color = $func2($image, mt_rand(160, 224), mt_rand(160, 224), mt_rand(160, 224)); - $text = $chars_allowed[mt_rand(0, sizeof($chars_allowed) - 1)]; - imagettftext($image, $size, $angle, $x, $y, $color, $this->get_font(), $text); + $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, $colour1); + imagefilledpolygon($img, $this->gen_poly($xo, $yo, $origin, $yvec, $y_corner,$zvec), 4, $colour2); + + $face = $this->gen_poly($xo, $yo, $origin, $xvec, $face_corner, $yvec); + + imagefilledpolygon($img, $face, 4, $background); + imagepolygon($img, $face, 4, $colour1); + } } - unset($chars_allowed); } - else + } + + /* + * return a roughly acceptable range of sizes for rendering with this texttype + */ + function range() + { + return array(3, 4); + } + + /** + * 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->bitmap_width / 2) * $size); + $xp = $this->scale($this->basis_matrix[$this->x], ($this->bitmap_width / 2) * $size); + $yn = $this->scale($this->basis_matrix[$this->y], -($this->bitmap_height / 2) * $size); + $yp = $this->scale($this->basis_matrix[$this->y], ($this->bitmap_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); + } +} + +/** +* @package VC +*/ +class colour_manager +{ + var $img; + var $mode; + var $colours; + var $named_colours; + + /** + * Create the colour manager, link it to the image resource + */ + function colour_manager($img, $background = false, $mode = 'ahsv') + { + $this->img = $img; + $this->mode = $mode; + $this->colours = array(); + $this->named_colours = array(); + + if ($background !== false) + { + $bg = $this->allocate_named('background', $background); + imagefill($this->img, 0, 0, $bg); + } + } + + /** + * Lookup a named colour resource + */ + function get_resource($named_colour) + { + if (isset($this->named_colours[$named_colour])) + { + return $this->named_colours[$named_colour]; + } + + if (isset($this->named_rgb[$named_colour])) + { + return $this->allocate_named($named_colour, $this->named_rgb[$named_colour], 'rgb'); + } + + return false; + } + + /** + * Assign a name to a colour resource + */ + function name_colour($name, $resource) + { + $this->named_colours[$name] = $resource; + } + + /** + * names and allocates a colour resource + */ + function allocate_named($name, $colour, $mode = false) + { + $resource = $this->allocate($colour, $mode); + + if ($resource !== false) + { + $this->name_colour($name, $resource); + } + return $resource; + } + + /** + * allocates a specified colour into the image + */ + function allocate($colour, $mode = false) + { + if ($mode === false) + { + $mode = $this->mode; + } + + if (!is_array($colour)) { - // generate grid - for ($i = 0; $i < $this->width; $i += 13) + if (isset($this->named_rgb[$colour])) { - $color = $func2($image, mt_rand(160, 224), mt_rand(160, 224), mt_rand(160, 224)); - imageline($image, $i, 0, $i, $this->height, $color); + return $this->allocate_named($colour, $this->named_rgb[$colour], 'rgb'); } - for ($i = 0; $i < $this->height; $i += 11) + if (!is_int($colour)) { - $color = $func2($image, mt_rand(160, 224), mt_rand(160, 224), mt_rand(160, 224)); - imageline($image, 0, $i, $this->width, $i, $color); + return false; } + + $mode = 'rgb'; + $colour = array(255 & ($colour >> 16), 255 & ($colour >> 8), 255 & $colour); } - $len = strlen($code); + if (isset($colour['mode'])) + { + $mode = $colour['mode']; + unset($colour['mode']); + } - for ($i = 0, $x = mt_rand(20, 40); $i < $len; $i++) + if (isset($colour['random'])) { - $text = strtoupper($code[$i]); - $angle = mt_rand(-30, 30); - $size = mt_rand(20, 40); - $y = mt_rand((int)($size * 1.5), (int)($this->height - ($size / 7))); + unset($colour['random']); + // everything else is params + return $this->random_colour($colour, $mode); + } + + $rgb = colour_manager::model_convert($colour, $mode, 'rgb'); + $store = ($this->mode == 'rgb') ? $rgb : colour_manager::model_convert($colour, $mode, $this->mode); + $resource = imagecolorallocate($this->img, $rgb[0], $rgb[1], $rgb[2]); - $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(); + $this->colours[$resource] = $store; - imagettftext($image, $size, $angle, $x + (int)($size / 15), $y, $shadow, $font, $text); - imagettftext($image, $size, $angle, $x, $y - (int)($size / 15), $color, $font, $text); + return $resource; + } - $x += $size + 4; + /** + * randomly generates a colour, with optional params + */ + function random_colour($params = array(), $mode = false) + { + if ($mode === false) + { + $mode = $this->mode; } - // Output image - header('Content-Type: image/png'); - header('Cache-control: no-cache, no-store'); - imagepng($image); - imagedestroy($image); + 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') ? true : false; + $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; + } } - function get_font() + /** + */ + function colour_scheme($resource, $include_original = true) { - static $fonts = array(); - - if (!sizeof($fonts)) + $mode = (in_array($this->mode, array('hsv', 'ahsv'), true) ? $this->mode : 'hsv'); + + if (($pre = $this->get_resource($resource)) !== false) { - global $phpbb_root_path; - - $dr = @opendir($phpbb_root_path . 'includes/captcha/fonts'); + $resource = $pre; + } + + $colour = colour_manager::model_convert($this->colours[$resource], $this->mode, $mode); + $results = ($include_original) ? array($resource) : array(); - if (!$dr) + $colour2 = $colour3 = $colour4 = $colour; + $colour2[0] += 140; + $colour3[0] += 220; + $colour4[0] += 300; + + $results[] = $this->allocate($colour2, $mode); + $results[] = $this->allocate($colour3, $mode); + $results[] = $this->allocate($colour4, $mode); + + return $results; + } + + /** + */ + function mono_range($resource, $count = 5, $include_original = true) + { + if (is_array($resource)) + { + $results = array(); + for ($i = 0, $size = sizeof($resource); $i < $size; ++$i) { - trigger_error('Unable to open includes/captcha/fonts directory.', E_USER_ERROR); + $results = array_merge($results, $this->mono_range($resource[$i], $count, $include_original)); } + return $results; + } + + $mode = (in_array($this->mode, array('hsv', 'ahsv'), true) ? $this->mode : 'ahsv'); + if (($pre = $this->get_resource($resource)) !== false) + { + $resource = $pre; + } - while (false !== ($entry = readdir($dr))) + $colour = colour_manager::model_convert($this->colours[$resource], $this->mode, $mode); + + $results = array(); + if ($include_original) + { + $results[] = $resource; + $count--; + } + + // 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) { - if (strtolower(pathinfo($entry, PATHINFO_EXTENSION)) == 'ttf') + $colour[1] = ($i * 100) / $length; + $colour[2] = ($j * 100) / $i; + $results[] = $this->allocate($colour, $mode); + $count--; + + if (!$count) + { + return $results; + } + } + } + + return $results; + } + + /** + * Convert from one colour model to another + */ + function model_convert($colour, $from_model, $to_model) + { + if ($from_model == $to_model) + { + return $colour; + } + + switch ($to_model) + { + case 'hsv': + + switch ($from_model) { - $fonts[] = $phpbb_root_path . 'includes/captcha/fonts/' . $entry; + case 'ahsv': + return colour_manager::ah2h($colour); + break; + + case 'rgb': + return colour_manager::rgb2hsv($colour); + break; + } + break; + + case 'ahsv': + + switch ($from_model) + { + case 'hsv': + return colour_manager::h2ah($colour); + break; + + case 'rgb': + return colour_manager::h2ah(colour_manager::rgb2hsv($colour)); + break; + } + break; + + case 'rgb': + switch ($from_model) + { + case 'hsv': + return colour_manager::hsv2rgb($colour); + break; + + case 'ahsv': + return colour_manager::hsv2rgb(colour_manager::ah2h($colour)); + break; } + break; + } + return false; + } + + /** + * Slightly altered from wikipedia's algorithm + */ + function hsv2rgb($hsv) + { + colour_manager::normalize_hue($hsv[0]); + + $h = $hsv[0]; + $s = min(1, max(0, $hsv[1] / 100)); + $v = min(1, max(0, $hsv[2] / 100)); + + // calculate hue sector + $hi = floor($hsv[0] / 60); + + // calculate opposite colour + $p = $v * (1 - $s); + + // calculate distance between hex vertices + $f = ($h / 60) - $hi; + + // coming in or going out? + if (!($hi & 1)) + { + $f = 1 - $f; + } + + // calculate adjacent colour + $q = $v * (1 - ($f * $s)); + + 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); + + // if max - min is 0, we want hue to be 0 anyway. + $h = $max - $min; + + 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); } + colour_manager::normalize_hue($h); + + return array($h, $s * 100, $v * 100); + } + + /** + */ + function normalize_hue(&$hue) + { + $hue %= 360; + + if ($hue < 0) + { + $hue += 360; + } + } + + /** + * Alternate hue to hue + */ + function ah2h($ahue) + { + if (is_array($ahue)) + { + $ahue[0] = colour_manager::ah2h($ahue[0]); + return $ahue; + } + colour_manager::normalize_hue($ahue); - return $fonts[mt_rand(0, sizeof($fonts) - 1)]; + // blue through red is already ok + if ($ahue >= 240) + { + return $ahue; + } + + // ahue green is at 180 + if ($ahue >= 180) + { + // return (240 - (2 * (240 - $ahue))); + return (2 * $ahue) - 240; // equivalent + } + + // ahue yellow is at 120 (RYB rather than RGB) + if ($ahue >= 120) + { + return $ahue - 60; + } + + return $ahue / 2; + } + + /** + * hue to Alternate hue + */ + function h2ah($hue) + { + if (is_array($hue)) + { + $hue[0] = colour_manager::h2ah($hue[0]); + return $hue; + } + colour_manager::normalize_hue($hue); + + // blue through red is already ok + if ($hue >= 240) + { + return $hue; + } + else if ($hue <= 60) + { + return $hue * 2; + } + else if ($hue <= 120) + { + return $hue + 60; + } + else + { + return ($hue + 240) / 2; + } } } diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index c9b7134674..395fcc4918 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -1110,6 +1110,9 @@ if (version_compare($current_version, '3.0.b5', '<=')) unset($sql_in); set_config('avatar_salt', md5(mt_rand())); + set_config('captcha_gd_x_grid', 25); + set_config('captcha_gd_y_grid', 25); + set_config('captcha_gd_foreground_noise', 1); $sql = 'UPDATE ' . ACL_OPTIONS_TABLE . " SET is_local = 0 diff --git a/phpBB/install/install_install.php b/phpBB/install/install_install.php index 2b54a2b1aa..3d6646df94 100755 --- a/phpBB/install/install_install.php +++ b/phpBB/install/install_install.php @@ -1307,11 +1307,6 @@ class install_install extends module if (!@extension_loaded('gd')) { can_load_dll('gd'); - } - - // 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 = '1' WHERE config_name = 'captcha_gd'"; diff --git a/phpBB/install/schemas/schema_data.sql b/phpBB/install/schemas/schema_data.sql index 6fa07c6f68..cb030fb1d6 100644 --- a/phpBB/install/schemas/schema_data.sql +++ b/phpBB/install/schemas/schema_data.sql @@ -59,7 +59,9 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('bump_interval', '1 INSERT INTO phpbb_config (config_name, config_value) VALUES ('bump_type', 'd'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('cache_gc', '7200'); 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 ('captcha_gd_foreground_noise', '1'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('captcha_gd_x_grid', '25'); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('captcha_gd_y_grid', '25'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('check_dnsbl', '0'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('chg_passforce', '0'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('cookie_domain', ''); diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php index e46f6eca3c..bbb2678498 100644 --- a/phpBB/language/en/acp/board.php +++ b/phpBB/language/en/acp/board.php @@ -217,14 +217,21 @@ $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.', - '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', - 'VISUAL_CONFIRM_REG_EXPLAIN' => 'Requires new users to enter a random code matching an image to help prevent mass registrations.', + 'CAPTCHA_GD' => 'GD CAPTCHA', + 'CAPTCHA_GD_FOREGROUND_NOISE' => 'GD CAPTCHA foreground noise', + 'CAPTCHA_GD_EXPLAIN' => 'Use GD to make a more advanced CAPTCHA', + 'CAPTCHA_GD_FOREGROUND_NOISE_EXPLAIN' => 'Use foreground noise to make the GD based CAPTCHA harder', + 'CAPTCHA_GD_X_GRID' => 'GD CAPTCHA background noise x-axis', + 'CAPTCHA_GD_X_GRID_EXPLAIN' => 'Use lower settings of this to make the GD based CAPTCHA harder. 0 will disable x-axis background noise.', + 'CAPTCHA_GD_Y_GRID' => 'GD CAPTCHA background noise y-axis', + 'CAPTCHA_GD_Y_GRID_EXPLAIN' => 'Use lower settings of this to make the GD based CAPTCHA harder. 0 will disable y-axis background noise.', + + 'CAPTCHA_PREVIEW_MSG' => 'Your changes to the visual confirmation setting were not saved. This is just a preview.', + 'CAPTCHA_PREVIEW_EXPLAIN' => 'The captcha as it will look like using the current settings. Use the preview button to refresh. Note that captchas are randomized and will differ from one view to the next.', + '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', + 'VISUAL_CONFIRM_REG_EXPLAIN' => 'Requires new users to enter a random code matching an image to help prevent mass registrations.', )); // Cookie Settings |