1<?php 2/** 3* 4* @package VC 5* @version $Id$ 6* @copyright (c) 2006, 2008 phpBB Group 7* @license http://opensource.org/licenses/gpl-license.php GNU Public License 8* 9*/ 10 11/** 12* @ignore 13*/ 14if (!defined('IN_PHPBB')) 15{ 16 exit; 17} 18 19 20/** 21* This class holds the code shared by the two default 3.0.x CAPTCHAs. 22* 23* @package VC 24*/ 25class phpbb_default_captcha 26{ 27 var $confirm_id; 28 var $confirm_code; 29 var $code; 30 var $seed; 31 var $attempts = 0; 32 var $type; 33 var $solved = 0; 34 var $captcha_vars = false; 35 36 function init($type) 37 { 38 global $config, $db, $user; 39 40 // read input 41 $this->confirm_id = request_var('confirm_id', ''); 42 $this->confirm_code = request_var('confirm_code', ''); 43 $refresh = request_var('refresh_vc', false) && $config['confirm_refresh']; 44 45 $this->type = (int) $type; 46 47 if (!strlen($this->confirm_id) || !$this->load_code()) 48 { 49 // we have no confirm ID, better get ready to display something 50 $this->generate_code(); 51 } 52 else if ($refresh) 53 { 54 $this->regenerate_code(); 55 } 56 } 57 58 function execute_demo() 59 { 60 global $user; 61 62 $this->code = gen_rand_string_friendly(mt_rand(CAPTCHA_MIN_CHARS, CAPTCHA_MAX_CHARS)); 63 $this->seed = hexdec(substr(unique_id(), 4, 10)); 64 65 // compute $seed % 0x7fffffff 66 $this->seed -= 0x7fffffff * floor($this->seed / 0x7fffffff); 67 68 $captcha = new captcha(); 69 define('IMAGE_OUTPUT', 1); 70 $captcha->execute($this->code, $this->seed); 71 } 72 73 function execute() 74 { 75 if (empty($this->code)) 76 { 77 if (!$this->load_code()) 78 { 79 // invalid request, bail out 80 return false; 81 } 82 } 83 $captcha = new captcha(); 84 define('IMAGE_OUTPUT', 1); 85 $captcha->execute($this->code, $this->seed); 86 } 87 88 function get_template() 89 { 90 global $config, $user, $template, $phpEx, $phpbb_root_path; 91 92 if ($this->is_solved()) 93 { 94 return false; 95 } 96 else 97 { 98 $link = append_sid($phpbb_root_path . 'ucp.' . $phpEx, 'mode=confirm&confirm_id=' . $this->confirm_id . '&type=' . $this->type); 99 $explain = $user->lang(($this->type != CONFIRM_POST) ? 'CONFIRM_EXPLAIN' : 'POST_CONFIRM_EXPLAIN', '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>'); 100 101 $template->assign_vars(array( 102 'CONFIRM_IMAGE_LINK' => $link, 103 'CONFIRM_IMAGE' => '<img src="' . $link . '" />', 104 'CONFIRM_IMG' => '<img src="' . $link . '" />', 105 'CONFIRM_ID' => $this->confirm_id, 106 'S_CONFIRM_CODE' => true, 107 'S_TYPE' => $this->type, 108 'S_CONFIRM_REFRESH' => ($config['enable_confirm'] && $config['confirm_refresh'] && $this->type == CONFIRM_REG) ? true : false, 109 'L_CONFIRM_EXPLAIN' => $explain, 110 )); 111 112 return 'captcha_default.html'; 113 } 114 } 115 116 function get_demo_template($id) 117 { 118 global $config, $user, $template, $phpbb_admin_path, $phpEx; 119 120 $variables = ''; 121 122 if (is_array($this->captcha_vars)) 123 { 124 foreach ($this->captcha_vars as $captcha_var => $template_var) 125 { 126 $variables .= '&' . rawurlencode($captcha_var) . '=' . request_var($captcha_var, (int) $config[$captcha_var]); 127 } 128 } 129 130 // acp_captcha has a delivery function; let's use it 131 $template->assign_vars(array( 132 'CONFIRM_IMAGE' => append_sid($phpbb_admin_path . 'index.' . $phpEx, 'captcha_demo=1&mode=visual&i=' . $id . '&select_captcha=' . $this->get_class_name()) . $variables, 133 'CONFIRM_ID' => $this->confirm_id, 134 )); 135 136 return 'captcha_default_acp_demo.html'; 137 } 138 139 function get_hidden_fields() 140 { 141 $hidden_fields = array(); 142 143 // this is required for posting.php - otherwise we would forget about the captcha being already solved 144 if ($this->solved) 145 { 146 $hidden_fields['confirm_code'] = $this->confirm_code; 147 } 148 $hidden_fields['confirm_id'] = $this->confirm_id; 149 return $hidden_fields; 150 } 151 152 function garbage_collect($type) 153 { 154 global $db, $config; 155 156 $sql = 'SELECT DISTINCT c.session_id 157 FROM ' . CONFIRM_TABLE . ' c 158 LEFT JOIN ' . SESSIONS_TABLE . ' s ON (c.session_id = s.session_id) 159 WHERE s.session_id IS NULL' . 160 ((empty($type)) ? '' : ' AND c.confirm_type = ' . (int) $type); 161 $result = $db->sql_query($sql); 162 163 if ($row = $db->sql_fetchrow($result)) 164 { 165 $sql_in = array(); 166 do 167 { 168 $sql_in[] = (string) $row['session_id']; 169 } 170 while ($row = $db->sql_fetchrow($result)); 171 172 if (sizeof($sql_in)) 173 { 174 $sql = 'DELETE FROM ' . CONFIRM_TABLE . ' 175 WHERE ' . $db->sql_in_set('session_id', $sql_in); 176 $db->sql_query($sql); 177 } 178 } 179 $db->sql_freeresult($result); 180 } 181 182 function uninstall() 183 { 184 $this->garbage_collect(0); 185 } 186 187 function install() 188 { 189 return; 190 } 191 192 function validate() 193 { 194 global $config, $db, $user; 195 196 if (empty($user->lang)) 197 { 198 $user->setup(); 199 } 200 201 $error = ''; 202 if (!$this->confirm_id) 203 { 204 $error = $user->lang['CONFIRM_CODE_WRONG']; 205 } 206 else 207 { 208 if ($this->check_code()) 209 { 210 // $this->delete_code(); commented out to allow posting.php to repeat the question 211 $this->solved = true; 212 } 213 else 214 { 215 $error = $user->lang['CONFIRM_CODE_WRONG']; 216 } 217 } 218 219 if (strlen($error)) 220 { 221 // okay, incorrect answer. Let's ask a new question. 222 $this->new_attempt(); 223 return $error; 224 } 225 else 226 { 227 return false; 228 } 229 } 230 231 /** 232 * The old way to generate code, suitable for GD and non-GD. Resets the internal state. 233 */ 234 function generate_code() 235 { 236 global $db, $user; 237 238 $this->code = gen_rand_string_friendly(mt_rand(CAPTCHA_MIN_CHARS, CAPTCHA_MAX_CHARS)); 239 $this->confirm_id = md5(unique_id($user->ip)); 240 $this->seed = hexdec(substr(unique_id(), 4, 10)); 241 $this->solved = 0; 242 // compute $seed % 0x7fffffff 243 $this->seed -= 0x7fffffff * floor($this->seed / 0x7fffffff); 244 245 $sql = 'INSERT INTO ' . CONFIRM_TABLE . ' ' . $db->sql_build_array('INSERT', array( 246 'confirm_id' => (string) $this->confirm_id, 247 'session_id' => (string) $user->session_id, 248 'confirm_type' => (int) $this->type, 249 'code' => (string) $this->code, 250 'seed' => (int) $this->seed) 251 ); 252 $db->sql_query($sql); 253 } 254 255 /** 256 * New Question, if desired. 257 */ 258 function regenerate_code() 259 { 260 global $db, $user; 261 262 $this->code = gen_rand_string_friendly(mt_rand(CAPTCHA_MIN_CHARS, CAPTCHA_MAX_CHARS)); 263 $this->seed = hexdec(substr(unique_id(), 4, 10)); 264 $this->solved = 0; 265 // compute $seed % 0x7fffffff 266 $this->seed -= 0x7fffffff * floor($this->seed / 0x7fffffff); 267 268 $sql = 'UPDATE ' . CONFIRM_TABLE . ' SET ' . $db->sql_build_array('UPDATE', array( 269 'code' => (string) $this->code, 270 'seed' => (int) $this->seed)) . ' 271 WHERE 272 confirm_id = \'' . $db->sql_escape($this->confirm_id) . '\' 273 AND session_id = \'' . $db->sql_escape($user->session_id) . '\''; 274 $db->sql_query($sql); 275 } 276 277 /** 278 * New Question, if desired. 279 */ 280 function new_attempt() 281 { 282 global $db, $user; 283 284 $this->code = gen_rand_string_friendly(mt_rand(CAPTCHA_MIN_CHARS, CAPTCHA_MAX_CHARS)); 285 $this->seed = hexdec(substr(unique_id(), 4, 10)); 286 $this->solved = 0; 287 // compute $seed % 0x7fffffff 288 $this->seed -= 0x7fffffff * floor($this->seed / 0x7fffffff); 289 290 $sql = 'UPDATE ' . CONFIRM_TABLE . ' SET ' . $db->sql_build_array('UPDATE', array( 291 'code' => (string) $this->code, 292 'seed' => (int) $this->seed)) . ' 293 , attempts = attempts + 1 294 WHERE 295 confirm_id = \'' . $db->sql_escape($this->confirm_id) . '\' 296 AND session_id = \'' . $db->sql_escape($user->session_id) . '\''; 297 $db->sql_query($sql); 298 } 299 300 /** 301 * Look up everything we need for painting&checking. 302 */ 303 function load_code() 304 { 305 global $db, $user; 306 307 $sql = 'SELECT code, seed, attempts 308 FROM ' . CONFIRM_TABLE . " 309 WHERE confirm_id = '" . $db->sql_escape($this->confirm_id) . "' 310 AND session_id = '" . $db->sql_escape($user->session_id) . "' 311 AND confirm_type = " . $this->type; 312 $result = $db->sql_query($sql); 313 $row = $db->sql_fetchrow($result); 314 $db->sql_freeresult($result); 315 316 if ($row) 317 { 318 $this->code = $row['code']; 319 $this->seed = $row['seed']; 320 $this->attempts = $row['attempts']; 321 return true; 322 } 323 324 return false; 325 } 326 327 function check_code() 328 { 329 return (strcasecmp($this->code, $this->confirm_code) === 0); 330 } 331 332 function delete_code() 333 { 334 global $db, $user; 335 336 $sql = 'DELETE FROM ' . CONFIRM_TABLE . " 337 WHERE confirm_id = '" . $db->sql_escape($confirm_id) . "' 338 AND session_id = '" . $db->sql_escape($user->session_id) . "' 339 AND confirm_type = " . $this->type; 340 $db->sql_query($sql); 341 } 342 343 function get_attempt_count() 344 { 345 return $this->attempts; 346 } 347 348 function reset() 349 { 350 global $db, $user; 351 352 $sql = 'DELETE FROM ' . CONFIRM_TABLE . " 353 WHERE session_id = '" . $db->sql_escape($user->session_id) . "' 354 AND confirm_type = " . (int) $this->type; 355 $db->sql_query($sql); 356 357 // we leave the class usable by generating a new question 358 $this->generate_code(); 359 } 360 361 function is_solved() 362 { 363 if (request_var('confirm_code', false) && $this->solved === 0) 364 { 365 $this->validate(); 366 } 367 return (bool) $this->solved; 368 } 369 370 /** 371 * API function 372 */ 373 function has_config() 374 { 375 return false; 376 } 377 378} 379 380?>