Completed
Pull Request — master (#77)
by Michael
11:21
created

XoopsCaptcha::instance()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 26 and the first side effect is on line 21.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * CAPTCHA configurations for Image mode
4
 *
5
 * Based on DuGris' SecurityImage
6
 *
7
 * You may not change or alter any portion of this comment or credits
8
 * of supporting developers from this source code or any supporting source code
9
 * which is considered copyrighted (c) material of the original comment or credit authors.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
 *
14
 * @copyright       (c) 2000-2016 XOOPS Project (www.xoops.org)
15
 * @license             GNU GPL 2 (http://www.gnu.org/licenses/gpl-2.0.html)
16
 * @package             class
17
 * @subpackage          CAPTCHA
18
 * @since               2.3.0
19
 * @author              Taiwen Jiang <[email protected]>
20
 */
21
defined('XOOPS_ROOT_PATH') || exit('Restricted access');
22
23
/**
24
 * Class XoopsCaptcha
25
 */
26
class XoopsCaptcha
27
{
28
    // static $instance;
29
    public $active;
30
    public $handler;
31
    public $path_basic;
32
    public $path_plugin;
33
    public $name;
34
    public $config  = array();
35
    public $message = array(); // Logging error messages
36
37
    /**
38
     * construct
39
     */
40
    public function __construct()
41
    {
42
        xoops_loadLanguage('captcha');
43
        // Load static configurations
44
        $this->path_basic  = XOOPS_ROOT_PATH . '/class/captcha';
45
        $this->path_plugin = XOOPS_ROOT_PATH . '/Frameworks/captcha';
46
        $this->config      = $this->loadConfig();
47
        $this->name        = $this->config['name'];
48
    }
49
50
    /**
51
     * Deprecated, use getInstance() instead
52
     */
53
    public static function instance()
54
    {
55
        return self::getInstance();
56
    }
57
58
    /**
59
     * Get Instance
60
     *
61
     * @return Instance
62
     */
63
    public static function getInstance()
64
    {
65
        static $instance;
66
        if (!isset($instance)) {
67
            $class    = __CLASS__;
68
            $instance = new $class();
69
        }
70
71
        return $instance;
72
    }
73
74
    /**
75
     * XoopsCaptcha::loadConfig()
76
     *
77
     * @param mixed $filename
78
     *
79
     * @return array
80
     */
81
    public function loadConfig($filename = null)
82
    {
83
        $basic_config  = array();
84
        $plugin_config = array();
85
        $filename      = empty($filename) ? 'config.php' : 'config.' . $filename . '.php';
86
        if (file_exists($file = $this->path_basic . '/' . $filename)) {
87
            $basic_config = include $file;
88
        }
89
        if (file_exists($file = $this->path_plugin . '/' . $filename)) {
90
            $plugin_config = include $file;
91
        }
92
93
        $config = array_merge($basic_config, $plugin_config);
94
        foreach ($config as $key => $val) {
95
            $config[$key] = $val;
96
        }
97
98
        return $config;
99
    }
100
101
    /**
102
     * XoopsCaptcha::isActive()
103
     *
104
     * @return bool
105
     */
106
    public function isActive()
107
    {
108
        if (isset($this->active)) {
109
            return $this->active;
110
        }
111
        if (!empty($this->config['disabled'])) {
112
            $this->active = false;
113
114
            return $this->active;
115
        }
116
        if (!empty($this->config['skipmember']) && is_object($GLOBALS['xoopsUser'])) {
117
            $this->active = false;
118
119
            return $this->active;
120
        }
121
        if (!isset($this->handler)) {
122
            $this->loadHandler();
123
        }
124
        $this->active = isset($this->handler);
125
126
        return $this->active;
127
    }
128
129
    /**
130
     * XoopsCaptcha::loadHandler()
131
     *
132
     * @param mixed $name
133
     * @return
134
     */
135
    public function loadHandler($name = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
136
    {
137
        $name  = !empty($name) ? $name : (empty($this->config['mode']) ? 'text' : $this->config['mode']);
138
        $class = 'XoopsCaptcha' . ucfirst($name);
139
        if (!empty($this->handler) && get_class($this->handler) == $class) {
140
            return $this->handler;
141
        }
142
        $this->handler = null;
143
        if (file_exists($file = $this->path_basic . '/' . $name . '.php')) {
144
            require_once $file;
145 View Code Duplication
        } else {
146
            if (file_exists($file = $this->path_plugin . '/' . $name . '.php')) {
147
                require_once $file;
148
            }
149
        }
150
151
        if (!class_exists($class)) {
152
            $class = 'XoopsCaptchaText';
153
            require_once $this->path_basic . '/text.php';
154
        }
155
        $handler = new $class($this);
156
        if ($handler->isActive()) {
157
            $this->handler = $handler;
158
            $this->handler->loadConfig($name);
159
        }
160
161
        return $this->handler;
162
    }
163
164
    /**
165
     * XoopsCaptcha::setConfigs()
166
     *
167
     * @param  mixed $configs
168
     * @return bool
169
     */
170
    public function setConfigs($configs)
171
    {
172
        foreach ($configs as $key => $val) {
173
            $this->setConfig($key, $val);
174
        }
175
176
        return true;
177
    }
178
179
    /**
180
     * XoopsCaptcha::setConfig()
181
     *
182
     * @param  mixed $name
183
     * @param  mixed $val
184
     * @return bool
185
     */
186
    public function setConfig($name, $val)
187
    {
188
        if (isset($this->$name)) {
189
            $this->$name = $val;
190
        } else {
191
            $this->config[$name] = $val;
192
        }
193
194
        return true;
195
    }
196
197
    /**
198
     * Verify user submission
199
     */
200
    /**
201
     * XoopsCaptcha::verify()
202
     *
203
     * @param  mixed $skipMember
204
     * @param  mixed $name
205
     * @return bool
206
     */
207
    public function verify($skipMember = null, $name = null)
208
    {
209
        $sessionName = empty($name) ? $this->name : $name;
210
        $skipMember  = ($skipMember === null) ? $_SESSION["{$sessionName}_skipmember"] : $skipMember;
211
        $maxAttempts = $_SESSION["{$sessionName}_maxattempts"];
212
        $attempt     = $_SESSION["{$sessionName}_attempt"];
213
        $is_valid    = false;
214
        // Skip CAPTCHA verification if disabled
215
        if (!$this->isActive()) {
216
            $is_valid = true;
217
            // Skip CAPTCHA for member if set
218
        } elseif (is_object($GLOBALS['xoopsUser']) && !empty($skipMember)) {
219
            $is_valid = true;
220
            // Kill too many attempts
221
        } elseif (!empty($maxAttempts) && $attempt > $maxAttempts) {
222
            $this->message[] = _CAPTCHA_TOOMANYATTEMPTS;
223
            // Verify the code
224
        } else {
225
            $is_valid = $this->handler->verify($sessionName);
226
        }
227
228
        if (!$is_valid) {
229
            // Increase the attempt records on failure
230
            $_SESSION["{$sessionName}_attempt"]++;
231
            // Log the error message
232
            $this->message[] = _CAPTCHA_INVALID_CODE;
233
        } else {
234
            // reset attempt records on success
235
            $_SESSION["{$sessionName}_attempt"] = null;
236
        }
237
        $this->destroyGarbage(true);
238
239
        return $is_valid;
240
    }
241
242
    /**
243
     * XoopsCaptcha::getCaption()
244
     *
245
     * @return mixed|string
246
     */
247
    public function getCaption()
248
    {
249
        return defined('_CAPTCHA_CAPTION') ? constant('_CAPTCHA_CAPTION') : '';
250
    }
251
252
    /**
253
     * XoopsCaptcha::getMessage()
254
     *
255
     * @return string
256
     */
257
    public function getMessage()
258
    {
259
        return implode('<br />', $this->message);
260
    }
261
262
    /**
263
     * Destroy historical stuff
264
     * @param bool $clearSession
265
     * @return bool
266
     */
267
    public function destroyGarbage($clearSession = false)
268
    {
269
        $this->loadHandler();
270
        if (is_callable($this->handler, 'destroyGarbage')) {
271
            $this->handler->destroyGarbage();
272
        }
273
        if ($clearSession) {
274
            $_SESSION[$this->name . '_name']        = null;
275
            $_SESSION[$this->name . '_skipmember']  = null;
276
            $_SESSION[$this->name . '_code']        = null;
277
            $_SESSION[$this->name . '_maxattempts'] = null;
278
        }
279
280
        return true;
281
    }
282
283
    /**
284
     * XoopsCaptcha::render()
285
     *
286
     * @return string
287
     */
288
    public function render()
289
    {
290
        $_SESSION[$this->name . '_name']       = $this->name;
291
        $_SESSION[$this->name . '_skipmember'] = $this->config['skipmember'];
292
        $form                                  = '';
293
        if (!$this->active || empty($this->config['name'])) {
294
            return $form;
295
        }
296
297
        $maxAttempts                            = $this->config['maxattempts'];
298
        $_SESSION[$this->name . '_maxattempts'] = $maxAttempts;
299
        $attempt                                = isset($_SESSION[$this->name . '_attempt']) ? $_SESSION[$this->name . '_attempt'] : 0;
300
        $_SESSION[$this->name . '_attempt']     = $attempt;
301
302
        // Failure on too many attempts
303
        if (!empty($maxAttempts) && $attempt > $maxAttempts) {
304
            $form = _CAPTCHA_TOOMANYATTEMPTS;
305
            // Load the form element
306
        } else {
307
            $form = $this->loadForm();
308
        }
309
310
        return $form;
311
    }
312
313
    /**
314
     * XoopsCaptcha::renderValidationJS()
315
     *
316
     * @return string
317
     */
318
    public function renderValidationJS()
319
    {
320
        if (!$this->active || empty($this->config['name'])) {
321
            return '';
322
        }
323
324
        return $this->handler->renderValidationJS();
325
    }
326
327
    /**
328
     * XoopsCaptcha::setCode()
329
     *
330
     * @param  mixed $code
331
     * @return bool
332
     */
333
    public function setCode($code = null)
334
    {
335
        $code = ($code === null) ? $this->handler->getCode() : $code;
336
        if (!empty($code)) {
337
            $_SESSION[$this->name . '_code'] = $code;
338
339
            return true;
340
        }
341
342
        return false;
343
    }
344
345
    /**
346
     * XoopsCaptcha::loadForm()
347
     *
348
     * @return
349
     */
350
    public function loadForm()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
351
    {
352
        $form = $this->handler->render();
353
        $this->setCode();
354
355
        return $form;
356
    }
357
}
358
359
/**
360
 * Abstract class for CAPTCHA method
361
 *
362
 * Currently there are two types of CAPTCHA forms, text and image
363
 * The default mode is "text", it can be changed in the priority:
364
 * 1 If mode is set through XoopsFormCaptcha::setConfig("mode", $mode), take it
365
 * 2 Elseif mode is set though captcha/config.php, take it
366
 * 3 Else, take "text"
367
 */
368
class XoopsCaptchaMethod
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
369
{
370
    public $handler;
371
    public $config;
372
    public $code;
373
374
    /**
375
     * XoopsCaptchaMethod::__construct()
376
     *
377
     * @param mixed $handler
378
     */
379
    public function __construct($handler = null)
380
    {
381
        $this->handler = $handler;
382
    }
383
384
    /**
385
     * XoopsCaptchaMethod::isActive()
386
     *
387
     * @return bool
388
     */
389
    public function isActive()
390
    {
391
        return true;
392
    }
393
394
    /**
395
     * XoopsCaptchaMethod::loadConfig()
396
     *
397
     * @param  string $name
398
     * @return void
399
     */
400
    public function loadConfig($name = '')
401
    {
402
        $this->config = empty($name) ? $this->handler->config : array_merge($this->handler->config, $this->handler->loadConfig($name));
403
    }
404
405
    /**
406
     * XoopsCaptchaMethod::getCode()
407
     *
408
     * @return string
409
     */
410
    public function getCode()
411
    {
412
        return (string)$this->code;
413
    }
414
415
    /**
416
     * XoopsCaptchaMethod::render()
417
     *
418
     * @return void
419
     */
420
    public function render()
421
    {
422
    }
423
424
    /**
425
     * @return string
426
     */
427
    public function renderValidationJS()
428
    {
429
        return '';
430
    }
431
432
    /**
433
     * XoopsCaptchaMethod::verify()
434
     *
435
     * @param  mixed $sessionName
436
     * @return bool
437
     */
438
    public function verify($sessionName = null)
439
    {
440
        $is_valid = false;
441
        if (!empty($_SESSION["{$sessionName}_code"])) {
442
            $func     = !empty($this->config['casesensitive']) ? 'strcmp' : 'strcasecmp';
443
            $is_valid = !$func(trim(@$_POST[$sessionName]), $_SESSION["{$sessionName}_code"]);
444
        }
445
446
        return $is_valid;
447
    }
448
}
449