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&amp;confirm_id=' . $this->confirm_id . '&amp;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 .= '&amp;' . 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&amp;mode=visual&amp;i=' . $id . '&amp;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?>