Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.61% |
71 / 72 |
|
92.31% |
12 / 13 |
CRAP | |
0.00% |
0 / 1 |
base | |
98.61% |
71 / 72 |
|
92.31% |
12 / 13 |
22 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
init | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
validate | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
reset | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
get_attempt_count | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
load_confirm_data | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
2 | |||
generate_confirm_data | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
1 | |||
increment_attempts | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
get_hidden_fields | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
is_solved | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
get_error | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
garbage_collect | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
4 | |||
acp_page | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * |
4 | * This file is part of the phpBB Forum Software package. |
5 | * |
6 | * @copyright (c) phpBB Limited <https://www.phpbb.com> |
7 | * @license GNU General Public License, version 2 (GPL-2.0) |
8 | * |
9 | * For full copyright and license information, please see |
10 | * the docs/CREDITS.txt file. |
11 | * |
12 | */ |
13 | |
14 | namespace phpbb\captcha\plugins; |
15 | |
16 | use phpbb\config\config; |
17 | use phpbb\db\driver\driver_interface; |
18 | use phpbb\language\language; |
19 | use phpbb\request\request_interface; |
20 | use phpbb\user; |
21 | |
22 | abstract class base implements plugin_interface |
23 | { |
24 | /** @var config */ |
25 | protected config $config; |
26 | |
27 | /** @var driver_interface */ |
28 | protected driver_interface $db; |
29 | |
30 | /** @var language */ |
31 | protected language $language; |
32 | |
33 | /** @var request_interface */ |
34 | protected request_interface $request; |
35 | |
36 | /** @var user */ |
37 | protected user $user; |
38 | |
39 | /** @var int Attempts at solving the CAPTCHA */ |
40 | protected int $attempts = 0; |
41 | |
42 | /** @var string Stored random CAPTCHA code */ |
43 | protected string $code = ''; |
44 | |
45 | /** @var bool Resolved state of captcha */ |
46 | protected bool $solved = false; |
47 | |
48 | /** @var string User supplied confirm code */ |
49 | protected string $confirm_code = ''; |
50 | |
51 | /** @var string Confirm id hash */ |
52 | protected string $confirm_id = ''; |
53 | |
54 | /** @var confirm_type Confirmation type */ |
55 | protected confirm_type $type = confirm_type::UNDEFINED; |
56 | |
57 | /** @var string Last error message */ |
58 | protected string $last_error = ''; |
59 | |
60 | /** |
61 | * Constructor for abstract captcha base class |
62 | * |
63 | * @param config $config |
64 | * @param driver_interface $db |
65 | * @param language $language |
66 | * @param request_interface $request |
67 | * @param user $user |
68 | */ |
69 | public function __construct(config $config, driver_interface $db, language $language, request_interface $request, user $user) |
70 | { |
71 | $this->config = $config; |
72 | $this->db = $db; |
73 | $this->language = $language; |
74 | $this->request = $request; |
75 | $this->user = $user; |
76 | } |
77 | |
78 | /** |
79 | * {@inheritDoc} |
80 | */ |
81 | public function init(confirm_type $type): void |
82 | { |
83 | $this->confirm_id = $this->request->variable('confirm_id', ''); |
84 | $this->confirm_code = $this->request->variable('confirm_code', ''); |
85 | $this->type = $type; |
86 | |
87 | if (empty($this->confirm_id) || !$this->load_confirm_data()) |
88 | { |
89 | // we have no confirm ID, better get ready to display something |
90 | $this->generate_confirm_data(); |
91 | } |
92 | } |
93 | |
94 | /** |
95 | * {@inheritDoc} |
96 | */ |
97 | public function validate(): bool |
98 | { |
99 | if ($this->confirm_id && hash_equals($this->code, $this->confirm_code)) |
100 | { |
101 | $this->solved = true; |
102 | return true; |
103 | } |
104 | |
105 | $this->increment_attempts(); |
106 | $this->last_error = $this->language->lang('CONFIRM_CODE_WRONG'); |
107 | return false; |
108 | } |
109 | |
110 | /** |
111 | * {@inheritDoc} |
112 | */ |
113 | public function reset(): void |
114 | { |
115 | $sql = 'DELETE FROM ' . CONFIRM_TABLE . " |
116 | WHERE session_id = '" . $this->db->sql_escape($this->user->session_id) . "' |
117 | AND confirm_type = " . $this->type->value; |
118 | $this->db->sql_query($sql); |
119 | |
120 | $this->generate_confirm_data(); |
121 | } |
122 | |
123 | /** |
124 | * {@inheritDoc} |
125 | */ |
126 | public function get_attempt_count(): int |
127 | { |
128 | return $this->attempts; |
129 | } |
130 | |
131 | /** |
132 | * Look up attempts from confirm table |
133 | */ |
134 | protected function load_confirm_data(): bool |
135 | { |
136 | $sql = 'SELECT code, attempts |
137 | FROM ' . CONFIRM_TABLE . " |
138 | WHERE confirm_id = '" . $this->db->sql_escape($this->confirm_id) . "' |
139 | AND session_id = '" . $this->db->sql_escape($this->user->session_id) . "' |
140 | AND confirm_type = " . $this->type->value; |
141 | $result = $this->db->sql_query($sql); |
142 | $row = $this->db->sql_fetchrow($result); |
143 | $this->db->sql_freeresult($result); |
144 | |
145 | if ($row) |
146 | { |
147 | $this->attempts = $row['attempts']; |
148 | $this->code = $row['code']; |
149 | |
150 | return true; |
151 | } |
152 | |
153 | return false; |
154 | } |
155 | |
156 | /** |
157 | * Generate confirm data for tracking attempts |
158 | * |
159 | * @return void |
160 | */ |
161 | protected function generate_confirm_data(): void |
162 | { |
163 | $this->code = gen_rand_string_friendly(CAPTCHA_MAX_CHARS); |
164 | $this->confirm_id = md5(unique_id()); |
165 | $this->attempts = 0; |
166 | |
167 | $sql = 'INSERT INTO ' . CONFIRM_TABLE . ' ' . $this->db->sql_build_array('INSERT', array( |
168 | 'confirm_id' => $this->confirm_id, |
169 | 'session_id' => (string) $this->user->session_id, |
170 | 'confirm_type' => $this->type->value, |
171 | 'code' => $this->code, |
172 | )); |
173 | $this->db->sql_query($sql); |
174 | } |
175 | |
176 | /** |
177 | * Increment number of attempts for confirm ID and session |
178 | * |
179 | * @return void |
180 | */ |
181 | protected function increment_attempts(): void |
182 | { |
183 | $sql = 'UPDATE ' . CONFIRM_TABLE . " |
184 | SET attempts = attempts + 1 |
185 | WHERE confirm_id = '{$this->db->sql_escape($this->confirm_id)}' |
186 | AND session_id = '{$this->db->sql_escape($this->user->session_id)}'"; |
187 | $this->db->sql_query($sql); |
188 | |
189 | $this->attempts++; |
190 | } |
191 | |
192 | /** |
193 | * {@inheritDoc} |
194 | */ |
195 | public function get_hidden_fields(): array |
196 | { |
197 | return [ |
198 | 'confirm_id' => $this->confirm_id, |
199 | 'confirm_code' => $this->solved === true ? $this->confirm_code : '', |
200 | ]; |
201 | } |
202 | |
203 | /** |
204 | * {@inheritDoc} |
205 | */ |
206 | public function is_solved(): bool |
207 | { |
208 | return $this->solved; |
209 | } |
210 | |
211 | /** |
212 | * {@inheritDoc} |
213 | */ |
214 | public function get_error(): string |
215 | { |
216 | return $this->last_error; |
217 | } |
218 | |
219 | /** |
220 | * @inheritDoc |
221 | */ |
222 | public function garbage_collect(confirm_type $confirm_type = confirm_type::UNDEFINED): void |
223 | { |
224 | $sql = 'SELECT DISTINCT c.session_id |
225 | FROM ' . CONFIRM_TABLE . ' c |
226 | LEFT JOIN ' . SESSIONS_TABLE . ' s ON (c.session_id = s.session_id) |
227 | WHERE s.session_id IS NULL' . |
228 | ((empty($confirm_type)) ? '' : ' AND c.confirm_type = ' . $confirm_type->value); |
229 | $result = $this->db->sql_query($sql); |
230 | |
231 | if ($row = $this->db->sql_fetchrow($result)) |
232 | { |
233 | $sql_in = []; |
234 | do |
235 | { |
236 | $sql_in[] = (string) $row['session_id']; |
237 | } |
238 | while ($row = $this->db->sql_fetchrow($result)); |
239 | |
240 | if (count($sql_in)) |
241 | { |
242 | $sql = 'DELETE FROM ' . CONFIRM_TABLE . ' |
243 | WHERE ' . $this->db->sql_in_set('session_id', $sql_in); |
244 | $this->db->sql_query($sql); |
245 | } |
246 | } |
247 | $this->db->sql_freeresult($result); |
248 | } |
249 | |
250 | /** |
251 | * {@inheritDoc} |
252 | */ |
253 | public function acp_page(mixed $id, mixed $module): void |
254 | { |
255 | } |
256 | } |