Passed
Branch feature/php-docs2 (9f8cc0)
by Michael
04:49
created

XoopsGTicket   F

Complexity

Total Complexity 60

Size/Duplication

Total Lines 400
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 149
dl 0
loc 400
rs 3.6
c 0
b 0
f 0
wmc 60

14 Methods

Rating   Name   Duplication   Size   Complexity  
B issue() 0 41 8
A getTicketArray() 0 3 1
A getTicketXoopsForm() 0 3 1
A using() 0 6 2
A clear() 0 3 1
A getTicketHtml() 0 3 1
A getErrors() 0 12 3
A errorHandler4FindOutput() 0 9 2
A addTicketXoopsFormElement() 0 3 1
A __construct() 0 22 6
F check() 0 81 19
B draw_repost_form() 0 44 8
A getTicketParamString() 0 3 2
A extract_post_recursive() 0 24 5

How to fix   Complexity   

Complex Class

Complex classes like XoopsGTicket often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use XoopsGTicket, and based on these observations, apply Extract Interface, too.

1
<?php namespace XoopsModules\Protector;
2
3
// GIJOE's Ticket Class (based on Marijuana's Oreteki XOOPS)
4
// nobunobu's suggestions are applied
5
6
if (!class_exists('XoopsGTicket')) {
7
    /**
8
     * Class XoopsGTicket
9
     */
10
    class XoopsGTicket
11
    {
12
        /**
13
         * @var array
14
         */
15
        public $_errors = array();
16
        /**
17
         * @var string
18
         */
19
        public $_latest_token = '';
20
        /**
21
         * @var array
22
         */
23
        public $messages = array();
24
25
        /**
26
         * XoopsGTicket constructor.
27
         */
28
        public function __construct()
29
        {
30
            global $xoopsConfig;
31
32
            // language file
33
            if (defined('XOOPS_ROOT_PATH') && !empty($xoopsConfig['language']) && false === strpos($xoopsConfig['language'], '/')) {
34
                if (file_exists(dirname(__DIR__) . '/language/' . $xoopsConfig['language'] . '/gticket_messages.phtml')) {
35
                    include dirname(__DIR__) . '/language/' . $xoopsConfig['language'] . '/gticket_messages.phtml';
36
                }
37
            }
38
39
            // default messages
40
            if (empty($this->messages)) {
41
                $this->messages = array(
42
                    'err_general'       => 'GTicket Error',
43
                    'err_nostubs'       => 'No stubs found',
44
                    'err_noticket'      => 'No ticket found',
45
                    'err_nopair'        => 'No valid ticket-stub pair found',
46
                    'err_timeout'       => 'Time out',
47
                    'err_areaorref'     => 'Invalid area or referer',
48
                    'fmt_prompt4repost' => 'error(s) found:<br><span style="background-color:red;font-weight:bold;color:white;">%s</span><br>Confirm it.<br>And do you want to post again?',
49
                    'btn_repost'        => 'repost',
50
                );
51
            }
52
        }
53
54
        // render form as plain html
55
56
        /**
57
         * @param string $salt
58
         * @param int    $timeout
59
         * @param string $area
60
         *
61
         * @return string
62
         */
63
        public function getTicketHtml($salt = '', $timeout = 1800, $area = '')
64
        {
65
            return '<input type="hidden" name="XOOPS_G_TICKET" value="' . $this->issue($salt, $timeout, $area) . '" />';
66
        }
67
68
        // returns an object of XoopsFormHidden including theh ticket
69
70
        /**
71
         * @param string $salt
72
         * @param int    $timeout
73
         * @param string $area
74
         *
75
         * @return XoopsFormHidden
76
         */
77
        public function getTicketXoopsForm($salt = '', $timeout = 1800, $area = '')
78
        {
79
            return new XoopsFormHidden('XOOPS_G_TICKET', $this->issue($salt, $timeout, $area));
0 ignored issues
show
Bug introduced by
The type XoopsModules\Protector\XoopsFormHidden was not found. Did you mean XoopsFormHidden? If so, make sure to prefix the type with \.
Loading history...
80
        }
81
82
        // add a ticket as Hidden Element into XoopsForm
83
84
        /**
85
         * @param \XoopsForm $form
86
         * @param string $salt
87
         * @param int    $timeout
88
         * @param string $area
89
         */
90
        public function addTicketXoopsFormElement($form, $salt = '', $timeout = 1800, $area = '')
91
        {
92
            $form->addElement(new XoopsFormHidden('XOOPS_G_TICKET', $this->issue($salt, $timeout, $area)));
93
        }
94
95
        // returns an array for xoops_confirm() ;
96
97
        /**
98
         * @param string $salt
99
         * @param int    $timeout
100
         * @param string $area
101
         *
102
         * @return array
103
         */
104
        public function getTicketArray($salt = '', $timeout = 1800, $area = '')
105
        {
106
            return array('XOOPS_G_TICKET' => $this->issue($salt, $timeout, $area));
107
        }
108
109
        // return GET parameter string.
110
111
        /**
112
         * @param string $salt
113
         * @param bool   $noamp
114
         * @param int    $timeout
115
         * @param string $area
116
         *
117
         * @return string
118
         */
119
        public function getTicketParamString($salt = '', $noamp = false, $timeout = 1800, $area = '')
120
        {
121
            return ($noamp ? '' : '&amp;') . 'XOOPS_G_TICKET=' . $this->issue($salt, $timeout, $area);
122
        }
123
124
        // issue a ticket
125
126
        /**
127
         * @param string $salt
128
         * @param int    $timeout
129
         * @param string $area
130
         *
131
         * @return string
132
         */
133
        public function issue($salt = '', $timeout = 1800, $area = '')
134
        {
135
            global $xoopsModule;
136
137
            if ('' === $salt) {
138
                $salt = '$2y$07$' . str_replace('+', '.', base64_encode(random_bytes(16)));
139
            }
140
141
            // create a token
142
            list($usec, $sec) = explode(' ', microtime());
143
            $appendix_salt       = empty($_SERVER['PATH']) ? XOOPS_DB_NAME : $_SERVER['PATH'];
144
            $token               = crypt($salt . $usec . $appendix_salt . $sec, $salt);
145
            $this->_latest_token = $token;
146
147
            if (empty($_SESSION['XOOPS_G_STUBS'])) {
148
                $_SESSION['XOOPS_G_STUBS'] = array();
149
            }
150
151
            // limit max stubs 10
152
            if (count($_SESSION['XOOPS_G_STUBS']) > 10) {
153
                $_SESSION['XOOPS_G_STUBS'] = array_slice($_SESSION['XOOPS_G_STUBS'], -10);
154
            }
155
156
            // record referer if browser send it
157
            $referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['REQUEST_URI'];
158
159
            // area as module's dirname
160
            if (!$area && is_object(@$xoopsModule)) {
161
                $area = $xoopsModule->getVar('dirname');
162
            }
163
164
            // store stub
165
            $_SESSION['XOOPS_G_STUBS'][] = array(
166
                'expire'  => time() + $timeout,
167
                'referer' => $referer,
168
                'area'    => $area,
169
                'token'   => $token,
170
            );
171
172
            // paid md5ed token as a ticket
173
            return md5($token . XOOPS_DB_PREFIX);
174
        }
175
176
        // check a ticket
177
178
        /**
179
         * @param bool   $post
180
         * @param string $area
181
         * @param bool   $allow_repost
182
         *
183
         * @return bool
184
         */
185
        public function check($post = true, $area = '', $allow_repost = true)
186
        {
187
            global $xoopsModule;
188
189
            $this->_errors = array();
190
191
            // CHECK: stubs are not stored in session
192
            if (!is_array(@$_SESSION['XOOPS_G_STUBS'])) {
193
                $this->_errors[]           = $this->messages['err_nostubs'];
194
                $_SESSION['XOOPS_G_STUBS'] = array();
195
            }
196
197
            // get key&val of the ticket from a user's query
198
            $ticket = $post ? @$_POST['XOOPS_G_TICKET'] : @$_GET['XOOPS_G_TICKET'];
199
200
            // CHECK: no tickets found
201
            if (empty($ticket)) {
202
                $this->_errors[] = $this->messages['err_noticket'];
203
            }
204
205
            // gargage collection & find a right stub
206
            $stubs_tmp                 = $_SESSION['XOOPS_G_STUBS'];
207
            $_SESSION['XOOPS_G_STUBS'] = array();
208
            foreach ($stubs_tmp as $stub) {
209
                // default lifetime 30min
210
                if ($stub['expire'] >= time()) {
211
                    if (md5($stub['token'] . XOOPS_DB_PREFIX) === $ticket) {
212
                        $found_stub = $stub;
213
                    } else {
214
                        // store the other valid stubs into session
215
                        $_SESSION['XOOPS_G_STUBS'][] = $stub;
216
                    }
217
                } else {
218
                    if (md5($stub['token'] . XOOPS_DB_PREFIX) === $ticket) {
219
                        // not CSRF but Time-Out
220
                        $timeout_flag = true;
221
                    }
222
                }
223
            }
224
225
            // CHECK: the right stub found or not
226
            if (empty($found_stub)) {
227
                if (empty($timeout_flag)) {
228
                    $this->_errors[] = $this->messages['err_nopair'];
229
                } else {
230
                    $this->_errors[] = $this->messages['err_timeout'];
231
                }
232
            } else {
233
                // set area if necessary
234
                // area as module's dirname
235
                if (!$area && is_object(@$xoopsModule)) {
236
                    $area = $xoopsModule->getVar('dirname');
237
                }
238
239
                // check area or referer
240
                if (@$found_stub['area'] == $area) {
241
                    $area_check = true;
242
                }
243
                if (!empty($found_stub['referer']) && false !== strpos(@$_SERVER['HTTP_REFERER'], $found_stub['referer'])) {
244
                    $referer_check = true;
245
                }
246
247
                if (empty($area_check) && empty($referer_check)) { // loose
248
                    $this->_errors[] = $this->messages['err_areaorref'];
249
                }
250
            }
251
252
            if (!empty($this->_errors)) {
253
                if ($allow_repost) {
254
                    // repost form
255
                    $this->draw_repost_form($area);
256
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
257
                } else {
258
                    // failed
259
                    $this->clear();
260
261
                    return false;
262
                }
263
            } else {
264
                // all green
265
                return true;
266
            }
267
        }
268
269
        // draw form for repost
270
271
        /**
272
         * @param string $area
273
         */
274
        public function draw_repost_form($area = '')
275
        {
276
            // Notify which file is broken
277
            if (headers_sent()) {
278
                restore_error_handler();
279
                set_error_handler(array(
280
                                      &$this,
281
                                      'errorHandler4FindOutput',
282
                                  ));
283
                header('Dummy: for warning');
284
                restore_error_handler();
285
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
286
            }
287
288
            error_reporting(0);
289
            while (ob_get_level()) {
290
                ob_end_clean();
291
            }
292
293
            $table = '<table>';
294
            $form  = '<form action="?' . htmlspecialchars(@$_SERVER['QUERY_STRING'], ENT_QUOTES) . '" method="post" >';
295
            foreach ($_POST as $key => $val) {
296
                if ($key === 'XOOPS_G_TICKET') {
297
                    continue;
298
                }
299
                if (@get_magic_quotes_gpc()) {
300
                    $key = stripslashes($key);
301
                }
302
                if (is_array($val)) {
303
                    list($tmp_table, $tmp_form) = $this->extract_post_recursive(htmlspecialchars($key, ENT_QUOTES), $val);
304
                    $table .= $tmp_table;
305
                    $form  .= $tmp_form;
306
                } else {
307
                    if (@get_magic_quotes_gpc()) {
308
                        $val = stripslashes($val);
309
                    }
310
                    $table .= '<tr><th>' . htmlspecialchars($key, ENT_QUOTES) . '</th><td>' . htmlspecialchars($val, ENT_QUOTES) . '</td></tr>' . "\n";
311
                    $form  .= '<input type="hidden" name="' . htmlspecialchars($key, ENT_QUOTES) . '" value="' . htmlspecialchars($val, ENT_QUOTES) . '" />' . "\n";
312
                }
313
            }
314
            $table .= '</table>';
315
            $form  .= $this->getTicketHtml(__LINE__, 300, $area) . '<input type="submit" value="' . $this->messages['btn_repost'] . '" /></form>';
316
317
            echo '<html><head><title>' . $this->messages['err_general'] . '</title><style>table,td,th {border:solid black 1px; border-collapse:collapse;}</style></head><body>' . sprintf($this->messages['fmt_prompt4repost'], $this->getErrors()) . $table . $form . '</body></html>';
318
        }
319
320
        /**
321
         * @param $key_name
322
         * @param $tmp_array
323
         *
324
         * @return array
325
         */
326
        public function extract_post_recursive($key_name, $tmp_array)
327
        {
328
            $table = '';
329
            $form  = '';
330
            foreach ($tmp_array as $key => $val) {
331
                if (@get_magic_quotes_gpc()) {
332
                    $key = stripslashes($key);
333
                }
334
                if (is_array($val)) {
335
                    list($tmp_table, $tmp_form) = $this->extract_post_recursive($key_name . '[' . htmlspecialchars($key, ENT_QUOTES) . ']', $val);
336
                    $table .= $tmp_table;
337
                    $form  .= $tmp_form;
338
                } else {
339
                    if (@get_magic_quotes_gpc()) {
340
                        $val = stripslashes($val);
341
                    }
342
                    $table .= '<tr><th>' . $key_name . '[' . htmlspecialchars($key, ENT_QUOTES) . ']</th><td>' . htmlspecialchars($val, ENT_QUOTES) . '</td></tr>' . "\n";
343
                    $form  .= '<input type="hidden" name="' . $key_name . '[' . htmlspecialchars($key, ENT_QUOTES) . ']" value="' . htmlspecialchars($val, ENT_QUOTES) . '" />' . "\n";
344
                }
345
            }
346
347
            return array(
348
                $table,
349
                $form,
350
            );
351
        }
352
353
        // clear all stubs
354
        public function clear()
355
        {
356
            $_SESSION['XOOPS_G_STUBS'] = array();
357
        }
358
359
        // Ticket Using
360
361
        /**
362
         * @return bool
363
         */
364
        public function using()
365
        {
366
            if (!empty($_SESSION['XOOPS_G_STUBS'])) {
367
                return true;
368
            } else {
369
                return false;
370
            }
371
        }
372
373
        // return errors
374
375
        /**
376
         * @param bool $ashtml
377
         *
378
         * @return array|string
379
         */
380
        public function getErrors($ashtml = true)
381
        {
382
            if ($ashtml) {
383
                $ret = '';
384
                foreach ($this->_errors as $msg) {
385
                    $ret .= "$msg<br>\n";
386
                }
387
            } else {
388
                $ret = $this->_errors;
389
            }
390
391
            return $ret;
392
        }
393
394
        /**
395
         * @param $errNo
396
         * @param $errStr
397
         * @param $errFile
398
         * @param $errLine
399
         * @return null
400
         */
401
        public function errorHandler4FindOutput($errNo, $errStr, $errFile, $errLine)
0 ignored issues
show
Unused Code introduced by
The parameter $errFile is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

401
        public function errorHandler4FindOutput($errNo, $errStr, /** @scrutinizer ignore-unused */ $errFile, $errLine)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $errLine is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

401
        public function errorHandler4FindOutput($errNo, $errStr, $errFile, /** @scrutinizer ignore-unused */ $errLine)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
402
        {
403
            if (preg_match('?' . preg_quote(XOOPS_ROOT_PATH) . '([^:]+)\:(\d+)?', $errStr, $regs)) {
404
                echo 'Irregular output! check the file ' . htmlspecialchars($regs[1]) . ' line ' . htmlspecialchars($regs[2]);
405
            } else {
406
                echo 'Irregular output! check language files etc.';
407
            }
408
409
            return null;
410
        }
411
        // end of class
412
    }
413
414
    // create a instance in global scope
415
    $GLOBALS['xoopsGTicket'] = new XoopsGTicket();
416
}
417
418
if (!function_exists('admin_refcheck')) {
419
    //Admin Referer Check By Marijuana(Rev.011)
420
    /**
421
     * @param string $chkref
422
     *
423
     * @return bool
424
     */
425
    function admin_refcheck($chkref = '')
426
    {
427
        if (empty($_SERVER['HTTP_REFERER'])) {
428
            return true;
429
        } else {
430
            $ref = $_SERVER['HTTP_REFERER'];
431
        }
432
        $cr = XOOPS_URL;
433
        if ($chkref != '') {
434
            $cr .= $chkref;
435
        }
436
        return !(strpos($ref, $cr) !== 0);
437
    }
438
}
439