1: <?php
2:
3: use Xmf\Request;
4:
5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
24: defined('XOOPS_ROOT_PATH') || exit('Restricted access');
25:
26: 27: 28:
29: class XoopsCaptcha
30: {
31:
32: public $active;
33: public $handler;
34: public $path_basic;
35: public $path_plugin;
36: public $name;
37: public $config = array();
38: public $message = array();
39:
40: 41: 42:
43: protected function __construct()
44: {
45: xoops_loadLanguage('captcha');
46:
47: $this->path_basic = XOOPS_ROOT_PATH . '/class/captcha';
48: $this->path_plugin = XOOPS_ROOT_PATH . '/Frameworks/captcha';
49: $this->config = $this->loadConfig();
50: $this->name = $this->config['name'];
51: }
52:
53: 54: 55: 56: 57:
58: public static function getInstance()
59: {
60: static $instance;
61: if (null === $instance) {
62: $instance = new static();
63: }
64:
65: return $instance;
66: }
67:
68: 69: 70: 71: 72: 73: 74:
75: public function loadConfig($filename = null)
76: {
77: $basic_config = array();
78: $plugin_config = array();
79: $filename = empty($filename) ? 'config.php' : 'config.' . $filename . '.php';
80: if (file_exists($file = $this->path_basic . '/' . $filename)) {
81: $basic_config = include $file;
82: }
83: if (file_exists($file = $this->path_plugin . '/' . $filename)) {
84: $plugin_config = include $file;
85: }
86:
87: $config = array_merge($basic_config, $plugin_config);
88: foreach ($config as $key => $val) {
89: $config[$key] = $val;
90: }
91:
92: return $config;
93: }
94:
95: 96: 97: 98: 99:
100: public function isActive()
101: {
102: if (null !== $this->active) {
103: return $this->active;
104: }
105: if (!empty($this->config['disabled'])) {
106: $this->active = false;
107:
108: return $this->active;
109: }
110: if (!empty($this->config['skipmember']) && is_object($GLOBALS['xoopsUser'])) {
111: $this->active = false;
112:
113: return $this->active;
114: }
115: if (null === $this->handler) {
116: $this->loadHandler();
117: }
118: $this->active = isset($this->handler);
119:
120: return $this->active;
121: }
122:
123: 124: 125: 126: 127: 128:
129: public function loadHandler($name = null)
130: {
131: $name = !empty($name) ? $name : (empty($this->config['mode']) ? 'text' : $this->config['mode']);
132: $class = 'XoopsCaptcha' . ucfirst($name);
133: if (!empty($this->handler) && get_class($this->handler) == $class) {
134: return $this->handler;
135: }
136: $this->handler = null;
137: if (file_exists($file = $this->path_basic . '/' . $name . '.php')) {
138: require_once $file;
139: } else {
140: if (file_exists($file = $this->path_plugin . '/' . $name . '.php')) {
141: require_once $file;
142: }
143: }
144:
145: if (!class_exists($class)) {
146: $class = 'XoopsCaptchaText';
147: require_once $this->path_basic . '/text.php';
148: }
149: $handler = new $class($this);
150: if ($handler->isActive()) {
151: $this->handler = $handler;
152: $this->handler->loadConfig($name);
153: }
154:
155: return $this->handler;
156: }
157:
158: 159: 160: 161: 162: 163:
164: public function setConfigs($configs)
165: {
166: foreach ($configs as $key => $val) {
167: $this->setConfig($key, $val);
168: }
169:
170: return true;
171: }
172:
173: 174: 175: 176: 177: 178: 179:
180: public function setConfig($name, $val)
181: {
182: if (isset($this->$name)) {
183: $this->$name = $val;
184: } else {
185: $this->config[$name] = $val;
186: }
187:
188: return true;
189: }
190:
191: 192: 193:
194: 195: 196: 197: 198: 199: 200:
201: public function verify($skipMember = null, $name = null)
202: {
203: $sessionName = empty($name) ? $this->name : $name;
204: $skipMember = ($skipMember === null) ? $_SESSION["{$sessionName}_skipmember"] : $skipMember;
205: $maxAttempts = $_SESSION["{$sessionName}_maxattempts"];
206: $attempt = $_SESSION["{$sessionName}_attempt"];
207: $is_valid = false;
208:
209: if (!$this->isActive()) {
210: $is_valid = true;
211:
212: } elseif (!empty($skipMember) && is_object($GLOBALS['xoopsUser'])) {
213: $is_valid = true;
214:
215: } elseif (!empty($maxAttempts) && $attempt > $maxAttempts) {
216: $this->message[] = _CAPTCHA_TOOMANYATTEMPTS;
217:
218: } else {
219: $is_valid = $this->handler->verify($sessionName);
220: $xoopsPreload = XoopsPreload::getInstance();
221: $xoopsPreload->triggerEvent('core.behavior.captcha.result', $is_valid);
222: }
223:
224: if (!$is_valid) {
225:
226: $_SESSION["{$sessionName}_attempt"]++;
227:
228: $this->message[] = _CAPTCHA_INVALID_CODE;
229: } else {
230:
231: $_SESSION["{$sessionName}_attempt"] = null;
232: }
233: $this->destroyGarbage(true);
234:
235: return $is_valid;
236: }
237:
238: 239: 240: 241: 242:
243: public function getCaption()
244: {
245: return defined('_CAPTCHA_CAPTION') ? constant('_CAPTCHA_CAPTION') : '';
246: }
247:
248: 249: 250: 251: 252:
253: public function getMessage()
254: {
255: return implode('<br>', $this->message);
256: }
257:
258: 259: 260: 261: 262:
263: public function destroyGarbage($clearSession = false)
264: {
265: $this->loadHandler();
266: if (is_callable($this->handler, 'destroyGarbage')) {
267: $this->handler->destroyGarbage();
268: }
269: if ($clearSession) {
270: $_SESSION[$this->name . '_name'] = null;
271: $_SESSION[$this->name . '_skipmember'] = null;
272: $_SESSION[$this->name . '_code'] = null;
273: $_SESSION[$this->name . '_maxattempts'] = null;
274: }
275:
276: return true;
277: }
278:
279: 280: 281: 282: 283:
284: public function render()
285: {
286: $_SESSION[$this->name . '_name'] = $this->name;
287: $_SESSION[$this->name . '_skipmember'] = $this->config['skipmember'];
288: $form = '';
289: if (!$this->active || empty($this->config['name'])) {
290: return $form;
291: }
292:
293: $maxAttempts = $this->config['maxattempts'];
294: $_SESSION[$this->name . '_maxattempts'] = $maxAttempts;
295: $attempt = isset($_SESSION[$this->name . '_attempt']) ? $_SESSION[$this->name . '_attempt'] : 0;
296: $_SESSION[$this->name . '_attempt'] = $attempt;
297:
298:
299: if (!empty($maxAttempts) && $attempt > $maxAttempts) {
300: $form = _CAPTCHA_TOOMANYATTEMPTS;
301:
302: } else {
303: $form = $this->loadForm();
304: }
305:
306: return $form;
307: }
308:
309: 310: 311: 312: 313:
314: public function renderValidationJS()
315: {
316: if (!$this->active || empty($this->config['name'])) {
317: return '';
318: }
319:
320: return $this->handler->renderValidationJS();
321: }
322:
323: 324: 325: 326: 327: 328:
329: public function setCode($code = null)
330: {
331: $code = ($code === null) ? $this->handler->getCode() : $code;
332: if (!empty($code)) {
333: $_SESSION[$this->name . '_code'] = $code;
334:
335: return true;
336: }
337:
338: return false;
339: }
340:
341: 342: 343: 344: 345:
346: public function loadForm()
347: {
348: $form = $this->handler->render();
349: $this->setCode();
350:
351: return $form;
352: }
353: }
354:
355: 356: 357: 358: 359: 360: 361: 362: 363:
364: class XoopsCaptchaMethod
365: {
366: public $handler;
367: public $config;
368: public $code;
369:
370: 371: 372: 373: 374:
375: public function __construct($handler = null)
376: {
377: $this->handler = $handler;
378: }
379:
380: 381: 382: 383: 384:
385: public function isActive()
386: {
387: return true;
388: }
389:
390: 391: 392: 393: 394: 395:
396: public function loadConfig($name = '')
397: {
398: $this->config = empty($name) ? $this->handler->config : array_merge($this->handler->config, $this->handler->loadConfig($name));
399: }
400:
401: 402: 403: 404: 405:
406: public function getCode()
407: {
408: return (string)$this->code;
409: }
410:
411: 412: 413: 414: 415:
416: public function render()
417: {
418: }
419:
420: 421: 422:
423: public function renderValidationJS()
424: {
425: return '';
426: }
427:
428: 429: 430: 431: 432: 433:
434: public function verify($sessionName = null)
435: {
436: $is_valid = false;
437: if (!empty($_SESSION["{$sessionName}_code"])) {
438: $func = !empty($this->config['casesensitive']) ? 'strcmp' : 'strcasecmp';
439:
440: $is_valid = !$func(trim(Request::getString($sessionName, '', 'POST')), $_SESSION["{$sessionName}_code"]);
441: }
442:
443: return $is_valid;
444: }
445: }
446: