Completed
Push — master ( 01b1a5...81f493 )
by Michael
04:03
created

XoopsCaptcha::instance()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
c 6
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 2
eloc 5
nc 2
nop 0
1
<?php
2
3
/**
4
 * CAPTCHA class For XOOPS
5
 *
6
 * Currently there are two types of CAPTCHA forms, text and image
7
 * The default mode is "text", it can be changed in the priority:
8
 * 1 If mode is set through XoopsFormCaptcha::setConfig("mode", $mode), take it
9
 * 2 Elseif mode is set though captcha/config.php, take it
10
 * 3 Else, take "text"
11
 *
12
 * D.J.
13
 */
14
class XoopsCaptcha
15
{
16
    public $active = true;
17
    public $mode   = 'text';    // potential values: image, text
18
    public $config = array();
19
20
    public $message = array(); // Logging error messages
21
22
    /**
23
     * XoopsCaptcha constructor.
24
     */
25
    public function __construct()
26
    {
27
        // Loading default preferences
28
        $this->config = @include __DIR__ . '/config.php';
29
30
        $this->setMode($this->config['mode']);
31
    }
32
33
    /**
34
     * @return XoopsCaptcha
35
     */
36
    public static function getInstance()
37
    {
38
        static $instance;
39
        if (null === $instance) {
40
            $instance = new static();
41
        }
42
43
        return $instance;
44
    }
45
46
    /**
47
     * @param $name
48
     * @param $val
49
     * @return bool
50
     */
51
    public function setConfig($name, $val)
52
    {
53
        if ($name === 'mode') {
54
            $this->setMode($val);
55
        } elseif (isset($this->$name)) {
56
            $this->$name = $val;
57
        } else {
58
            $this->config[$name] = $val;
59
        }
60
61
        return true;
62
    }
63
64
    /**
65
     * Set CAPTCHA mode
66
     *
67
     * For future possible modes, right now force to use text or image
68
     *
69
     * @param string $mode if no mode is set, just verify current mode
70
     */
71
    public function setMode($mode = null)
72
    {
73
        if (!empty($mode) && in_array($mode, array('text', 'image'))) {
74
            $this->mode = $mode;
75
76
            if ($this->mode !== 'image') {
77
                return;
78
            }
79
        }
80
81
        // Disable image mode
82 View Code Duplication
        if (!extension_loaded('gd')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
83
            $this->mode = 'text';
84
        } else {
85
            $required_functions = array('imagecreatetruecolor', 'imagecolorallocate', 'imagefilledrectangle', 'imagejpeg', 'imagedestroy', 'imageftbbox');
86
            foreach ($required_functions as $func) {
87
                if (!function_exists($func)) {
88
                    $this->mode = 'text';
89
                    break;
90
                }
91
            }
92
        }
93
    }
94
95
    /**
96
     * Initializing the CAPTCHA class
97
     * @param string $name
98
     * @param null   $skipmember
99
     * @param null   $num_chars
100
     * @param null   $fontsize_min
101
     * @param null   $fontsize_max
102
     * @param null   $background_type
103
     * @param null   $background_num
104
     */
105
    public function init($name = 'xoopscaptcha', $skipmember = null, $num_chars = null, $fontsize_min = null, $fontsize_max = null, $background_type = null, $background_num = null)
106
    {
107
        // Loading RUN-TIME settings
108
        foreach (array_keys($this->config) as $key) {
109
            if (isset(${$key}) && ${$key} !== null) {
110
                $this->config[$key] = ${$key};
111
            }
112
        }
113
        $this->config['name'] = $name;
114
115
        // Skip CAPTCHA for member if set
116
        if ($this->config['skipmember'] && is_object($GLOBALS['xoopsUser'])) {
117
            $this->active = false;
118
        }
119
    }
120
121
    /**
122
     * Verify user submission
123
     * @param  null $skipMember
124
     * @return bool
125
     */
126
    public function verify($skipMember = null)
127
    {
128
        $sessionName = @$_SESSION['XoopsCaptcha_name'];
129
        $skipMember  = ($skipMember === null) ? @$_SESSION['XoopsCaptcha_skipmember'] : $skipMember;
130
        $maxAttempts = (int)(@$_SESSION['XoopsCaptcha_maxattempts']);
131
132
        $is_valid = false;
133
134
        // Skip CAPTCHA for member if set
135
        if (is_object($GLOBALS['xoopsUser']) && !empty($skipMember)) {
136
            $is_valid = true;
137
            // Kill too many attempts
138
        } elseif (!empty($maxAttempts) && $_SESSION['XoopsCaptcha_attempt_' . $sessionName] > $maxAttempts) {
139
            $this->message[] = XOOPS_CAPTCHA_TOOMANYATTEMPTS;
140
141
            // Verify the code
142
        } elseif (!empty($_SESSION['XoopsCaptcha_sessioncode'])) {
143
            $func     = $this->config['casesensitive'] ? 'strcmp' : 'strcasecmp';
144
            $is_valid = !$func(trim(@$_POST[$sessionName]), $_SESSION['XoopsCaptcha_sessioncode']);
145
        }
146
147
        if (!empty($maxAttempts)) {
148
            if (!$is_valid) {
149
                // Increase the attempt records on failure
150
                $_SESSION['XoopsCaptcha_attempt_' . $sessionName]++;
151
                // Log the error message
152
                $this->message[] = XOOPS_CAPTCHA_INVALID_CODE;
153
            } else {
154
155
                // reset attempt records on success
156
                $_SESSION['XoopsCaptcha_attempt_' . $sessionName] = null;
157
            }
158
        }
159
        $this->destroyGarbage(true);
160
161
        return $is_valid;
162
    }
163
164
    /**
165
     * @return mixed|string
166
     */
167
    public function getCaption()
168
    {
169
        return defined('XOOPS_CAPTCHA_CAPTION') ? constant('XOOPS_CAPTCHA_CAPTION') : '';
170
    }
171
172
    /**
173
     * @return string
174
     */
175
    public function getMessage()
176
    {
177
        return implode('<br>', $this->message);
178
    }
179
180
    /**
181
     * Destory historical stuff
182
     * @param  bool $clearSession
183
     * @return bool
184
     */
185
    public function destroyGarbage($clearSession = false)
186
    {
187
        require_once __DIR__ . '/' . $this->mode . '.php';
188
        $class          = 'XoopsCaptcha' . ucfirst($this->mode);
189
        $captchaHandler = new $class();
190
        if (method_exists($captchaHandler, 'destroyGarbage')) {
191
            $captchaHandler->loadConfig($this->config);
192
            $captchaHandler->destroyGarbage();
193
        }
194
195
        if ($clearSession) {
196
            $_SESSION['XoopsCaptcha_name']        = null;
197
            $_SESSION['XoopsCaptcha_skipmember']  = null;
198
            $_SESSION['XoopsCaptcha_sessioncode'] = null;
199
            $_SESSION['XoopsCaptcha_maxattempts'] = null;
200
        }
201
202
        return true;
203
    }
204
205
    /**
206
     * @return mixed|string
207
     */
208
    public function render()
209
    {
210
        $form = '';
211
212
        if (!$this->active || empty($this->config['name'])) {
213
            return $form;
214
        }
215
216
        $_SESSION['XoopsCaptcha_name']        = $this->config['name'];
217
        $_SESSION['XoopsCaptcha_skipmember']  = $this->config['skipmember'];
218
        $maxAttempts                          = $this->config['maxattempt'];
219
        $_SESSION['XoopsCaptcha_maxattempts'] = $maxAttempts;
220
        /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
221
         if (!empty($maxAttempts)) {
222
         $_SESSION['XoopsCaptcha_maxattempts_'.$_SESSION['XoopsCaptcha_name']] = $maxAttempts;
223
         }
224
         */
225
226
        // Fail on too many attempts
227
        if (!empty($maxAttempts) && @$_SESSION['XoopsCaptcha_attempt_' . $this->config['name']] > $maxAttempts) {
228
            $form = XOOPS_CAPTCHA_TOOMANYATTEMPTS;
229
            // Load the form element
230
        } else {
231
            $form = $this->loadForm();
232
        }
233
234
        return $form;
235
    }
236
237
    /**
238
     * @return mixed
239
     */
240
    public function loadForm()
241
    {
242
        require_once __DIR__ . '/' . $this->mode . '.php';
243
        $class          = 'XoopsCaptcha' . ucfirst($this->mode);
244
        $captchaHandler = new $class();
245
        $captchaHandler->loadConfig($this->config);
246
247
        $form = $captchaHandler->render();
248
249
        return $form;
250
    }
251
}
252