XoopsGTicket   F
last analyzed

Complexity

Total Complexity 72

Size/Duplication

Total Lines 394
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 155
c 0
b 0
f 0
dl 0
loc 394
rs 2.64
wmc 72

14 Methods

Rating   Name   Duplication   Size   Complexity  
A addTicketXoopsFormElement() 0 3 1
A getTicketHtml() 0 3 1
A getTicketArray() 0 3 1
A getTicketXoopsForm() 0 3 1
A getTicketParamString() 0 3 2
A __construct() 0 22 6
F check() 0 93 25
B issue() 0 40 9
B draw_repost_form() 0 46 11
B extract_post_recursive() 0 26 7
A getErrors() 0 12 3
A using() 0 6 2
A clear() 0 3 1
A errorHandler4FindOutput() 0 9 2

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
2
// GIJOE's Ticket Class (based on Marijuana's Oreteki XOOPS)
3
// nobunobu's suggestions are applied
4
5
if (!class_exists('XoopsGTicket')) {
6
7
    /**
8
     * Class XoopsGTicket
9
     */
10
    class XoopsGTicket
11
    {
12
        public $_errors       = array();
13
        public $_latest_token = '';
14
        public $messages      = array();
15
16
        /**
17
         * XoopsGTicket constructor.
18
         */
19
        public function __construct()
20
        {
21
            global $xoopsConfig;
22
23
            // language file
24
            if (defined('XOOPS_ROOT_PATH') && !empty($xoopsConfig['language']) && false === strpos($xoopsConfig['language'], '/')) {
25
                if (file_exists(dirname(__DIR__) . '/language/' . $xoopsConfig['language'] . '/gticket_messages.phtml')) {
26
                    include dirname(__DIR__) . '/language/' . $xoopsConfig['language'] . '/gticket_messages.phtml';
27
                }
28
            }
29
30
            // default messages
31
            if (empty($this->messages)) {
32
                $this->messages = array(
33
                    'err_general'       => 'GTicket Error',
34
                    'err_nostubs'       => 'No stubs found',
35
                    'err_noticket'      => 'No ticket found',
36
                    'err_nopair'        => 'No valid ticket-stub pair found',
37
                    'err_timeout'       => 'Time out',
38
                    'err_areaorref'     => 'Invalid area or referer',
39
                    '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?',
40
                    'btn_repost'        => 'repost');
41
            }
42
        }
43
44
        // render form as plain html
45
        /**
46
         * @param string $salt
47
         * @param int    $timeout
48
         * @param string $area
49
         *
50
         * @return string
51
         */
52
        public function getTicketHtml($salt = '', $timeout = 1800, $area = '')
53
        {
54
            return '<input type="hidden" name="XOOPS_G_TICKET" value="' . $this->issue($salt, $timeout, $area) . '" />';
55
        }
56
57
        // returns an object of XoopsFormHidden including theh ticket
58
        /**
59
         * @param string $salt
60
         * @param int    $timeout
61
         * @param string $area
62
         *
63
         * @return XoopsFormHidden
64
         */
65
        public function getTicketXoopsForm($salt = '', $timeout = 1800, $area = '')
66
        {
67
            return new XoopsFormHidden('XOOPS_G_TICKET', $this->issue($salt, $timeout, $area));
68
        }
69
70
        // add a ticket as Hidden Element into XoopsForm
71
        /**
72
         * @param        $form
73
         * @param string $salt
74
         * @param int    $timeout
75
         * @param string $area
76
         */
77
        public function addTicketXoopsFormElement(&$form, $salt = '', $timeout = 1800, $area = '')
78
        {
79
            $form->addElement(new XoopsFormHidden('XOOPS_G_TICKET', $this->issue($salt, $timeout, $area)));
80
        }
81
82
        // returns an array for xoops_confirm() ;
83
        /**
84
         * @param string $salt
85
         * @param int    $timeout
86
         * @param string $area
87
         *
88
         * @return array
89
         */
90
        public function getTicketArray($salt = '', $timeout = 1800, $area = '')
91
        {
92
            return array('XOOPS_G_TICKET' => $this->issue($salt, $timeout, $area));
93
        }
94
95
        // return GET parameter string.
96
        /**
97
         * @param string $salt
98
         * @param bool   $noamp
99
         * @param int    $timeout
100
         * @param string $area
101
         *
102
         * @return string
103
         */
104
        public function getTicketParamString($salt = '', $noamp = false, $timeout = 1800, $area = '')
105
        {
106
            return ($noamp ? '' : '&amp;') . 'XOOPS_G_TICKET=' . $this->issue($salt, $timeout, $area);
107
        }
108
109
        // issue a ticket
110
        /**
111
         * @param string $salt
112
         * @param int    $timeout
113
         * @param string $area
114
         *
115
         * @return string
116
         */
117
        public function issue($salt = '', $timeout = 1800, $area = '')
118
        {
119
            global $xoopsModule;
120
121
            if ('' === $salt) {
122
				$salt = '$2y$07$' . str_replace('+', '.', base64_encode(random_bytes(16)));
123
            }
124
125
            // create a token
126
            list($usec, $sec) = explode(' ', microtime());
127
            $appendix_salt       = empty($_SERVER['PATH']) ? XOOPS_DB_NAME : $_SERVER['PATH'];
128
            $token               = crypt($salt . $usec . $appendix_salt . $sec, $salt);
129
            $this->_latest_token = $token;
130
131
            if (empty($_SESSION['XOOPS_G_STUBS'])) {
132
                $_SESSION['XOOPS_G_STUBS'] = array();
133
            }
134
135
            // limit max stubs 10
136
            if (count($_SESSION['XOOPS_G_STUBS']) > 10) {
137
                $_SESSION['XOOPS_G_STUBS'] = array_slice($_SESSION['XOOPS_G_STUBS'], -10);
138
            }
139
140
            // record referer if browser send it
141
            $referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['REQUEST_URI'];
142
143
            // area as module's dirname
144
            if (!$area && isset($xoopsModule) && is_object($xoopsModule)) {
145
                $area = $xoopsModule->getVar('dirname');
146
            }
147
148
            // store stub
149
            $_SESSION['XOOPS_G_STUBS'][] = array(
150
                'expire'  => time() + $timeout,
151
                'referer' => $referer,
152
                'area'    => $area,
153
                'token'   => $token);
154
155
            // paid md5ed token as a ticket
156
            return md5($token . XOOPS_DB_PREFIX);
157
        }
158
159
        // check a ticket
160
        /**
161
         * @param bool   $post
162
         * @param string $area
163
         * @param bool   $allow_repost
164
         *
165
         * @return bool
166
         */
167
        public function check($post = true, $area = '', $allow_repost = true)
168
        {
169
            global $xoopsModule;
170
171
            $this->_errors = array();
172
173
            // CHECK: stubs are not stored in session
174
            if (!isset($_SESSION['XOOPS_G_STUBS']) || !is_array($_SESSION['XOOPS_G_STUBS'])) {
175
                $this->_errors[]           = $this->messages['err_nostubs'];
176
                $_SESSION['XOOPS_G_STUBS'] = array();
177
            }
178
179
            // get key&val of the ticket from a user's query
180
            $ticket = '';
181
            if ($post) {
182
                if (isset($_POST['XOOPS_G_TICKET'])) {
183
                    $ticket = $_POST['XOOPS_G_TICKET'];
184
                }
185
            } else {
186
                if (isset($_GET['XOOPS_G_TICKET'])) {
187
                    $ticket = $_GET['XOOPS_G_TICKET'];
188
                }
189
            }
190
191
            // CHECK: no tickets found
192
            if (empty($ticket)) {
193
                $this->_errors[] = $this->messages['err_noticket'];
194
            }
195
196
            // gargage collection & find a right stub
197
            $stubs_tmp                 = $_SESSION['XOOPS_G_STUBS'];
198
            $_SESSION['XOOPS_G_STUBS'] = array();
199
            foreach ($stubs_tmp as $stub) {
200
                // default lifetime 30min
201
                if ($stub['expire'] >= time()) {
202
                    if (md5($stub['token'] . XOOPS_DB_PREFIX) === $ticket) {
203
                        $found_stub = $stub;
204
                    } else {
205
                        // store the other valid stubs into session
206
                        $_SESSION['XOOPS_G_STUBS'][] = $stub;
207
                    }
208
                } else {
209
                    if (md5($stub['token'] . XOOPS_DB_PREFIX) === $ticket) {
210
                        // not CSRF but Time-Out
211
                        $timeout_flag = true;
212
                    }
213
                }
214
            }
215
216
            // CHECK: the right stub found or not
217
            if (empty($found_stub)) {
218
                if (empty($timeout_flag)) {
219
                    $this->_errors[] = $this->messages['err_nopair'];
220
                } else {
221
                    $this->_errors[] = $this->messages['err_timeout'];
222
                }
223
            } else {
224
225
                // set area if necessary
226
                // area as module's dirname
227
                if (!$area && isset($xoopsModule) && is_object($xoopsModule)) {
228
                    $area = $xoopsModule->getVar('dirname');
229
                }
230
231
                // check area or referer
232
                if (isset($found_stub['area']) && $found_stub['area'] == $area) {
233
                    $area_check = true;
234
                }
235
236
                if (!empty($found_stub['referer']) && isset($_SERVER['HTTP_REFERER']) && false !== strpos($_SERVER['HTTP_REFERER'], $found_stub['referer'])) {
237
                    $referer_check = true;
238
                }
239
240
241
                if (empty($area_check) && empty($referer_check)) { // loose
242
                    $this->_errors[] = $this->messages['err_areaorref'];
243
                }
244
            }
245
246
            if (!empty($this->_errors)) {
247
                if ($allow_repost) {
248
                    // repost form
249
                    $this->draw_repost_form($area);
250
                    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...
251
                } else {
252
                    // failed
253
                    $this->clear();
254
255
                    return false;
256
                }
257
            } else {
258
                // all green
259
                return true;
260
            }
261
        }
262
263
        // draw form for repost
264
        /**
265
         * @param string $area
266
         */
267
        public function draw_repost_form($area = '')
268
        {
269
            // Notify which file is broken
270
            if (headers_sent()) {
271
                restore_error_handler();
272
                set_error_handler(array(&$this, 'errorHandler4FindOutput'));
273
                header('Dummy: for warning');
274
                restore_error_handler();
275
                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...
276
            }
277
278
            error_reporting(0);
279
            while (ob_get_level()) {
280
                ob_end_clean();
281
            }
282
283
            $table = '<table>';
284
            $form = '<form action="?' . htmlspecialchars(isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '', ENT_QUOTES | ENT_HTML5) . '" method="post">';
285
286
            foreach ($_POST as $key => $val) {
287
                if ($key === 'XOOPS_G_TICKET') {
288
                    continue;
289
                }
290
                if (PHP_VERSION_ID < 50400) {
291
                    if (get_magic_quotes_gpc()) {
292
                        $key = stripslashes($key);
293
                    }
294
                }
295
                if (is_array($val)) {
296
                    list($tmp_table, $tmp_form) = $this->extract_post_recursive(htmlspecialchars($key, ENT_QUOTES | ENT_HTML5), $val);
297
                    $table .= $tmp_table;
298
                    $form .= $tmp_form;
299
                } else {
300
                    if (PHP_VERSION_ID < 50400) {
301
                        if (get_magic_quotes_gpc()) {
302
                            $val = stripslashes($val);
303
                        }
304
                    }
305
                    $table .= '<tr><th>' . htmlspecialchars($key, ENT_QUOTES | ENT_HTML5) . '</th><td>' . htmlspecialchars($val, ENT_QUOTES | ENT_HTML5) . '</td></tr>' . "\n";
306
                    $form .= '<input type="hidden" name="' . htmlspecialchars($key, ENT_QUOTES | ENT_HTML5) . '" value="' . htmlspecialchars($val, ENT_QUOTES | ENT_HTML5) . '" />' . "\n";
307
                }
308
            }
309
            $table .= '</table>';
310
            $form .= $this->getTicketHtml(__LINE__, 300, $area) . '<input type="submit" value="' . $this->messages['btn_repost'] . '" /></form>';
311
312
            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>';
313
        }
314
315
        /**
316
         * @param $key_name
317
         * @param $tmp_array
318
         *
319
         * @return array
320
         */
321
        public function extract_post_recursive($key_name, $tmp_array)
322
        {
323
            $table = '';
324
            $form  = '';
325
            foreach ($tmp_array as $key => $val) {
326
                if (PHP_VERSION_ID < 50400) {
327
                    if (get_magic_quotes_gpc()) {
328
                        $key = stripslashes($key);
329
                    }
330
                }
331
                if (is_array($val)) {
332
                    list($tmp_table, $tmp_form) = $this->extract_post_recursive($key_name . '[' . htmlspecialchars($key, ENT_QUOTES | ENT_HTML5) . ']', $val);
333
                    $table .= $tmp_table;
334
                    $form .= $tmp_form;
335
                } else {
336
                    if (PHP_VERSION_ID < 50400) {
337
                        if (get_magic_quotes_gpc()) {
338
                            $val = stripslashes($val);
339
                        }
340
                    }
341
                    $table .= '<tr><th>' . $key_name . '[' . htmlspecialchars($key, ENT_QUOTES | ENT_HTML5) . ']</th><td>' . htmlspecialchars($val, ENT_QUOTES | ENT_HTML5) . '</td></tr>' . "\n";
342
                    $form .= '<input type="hidden" name="' . $key_name . '[' . htmlspecialchars($key, ENT_QUOTES | ENT_HTML5) . ']" value="' . htmlspecialchars($val, ENT_QUOTES | ENT_HTML5) . '" />' . "\n";
343
                }
344
            }
345
346
            return array($table, $form);
347
        }
348
349
        // clear all stubs
350
        public function clear()
351
        {
352
            $_SESSION['XOOPS_G_STUBS'] = array();
353
        }
354
355
        // Ticket Using
356
        /**
357
         * @return bool
358
         */
359
        public function using()
360
        {
361
            if (!empty($_SESSION['XOOPS_G_STUBS'])) {
362
                return true;
363
            } else {
364
                return false;
365
            }
366
        }
367
368
        // return errors
369
        /**
370
         * @param bool $ashtml
371
         *
372
         * @return array|string
373
         */
374
        public function getErrors($ashtml = true)
375
        {
376
            if ($ashtml) {
377
                $ret = '';
378
                foreach ($this->_errors as $msg) {
379
                    $ret .= "$msg<br>\n";
380
                }
381
            } else {
382
                $ret = $this->_errors;
383
            }
384
385
            return $ret;
386
        }
387
388
        /**
389
         * @param $errNo
390
         * @param $errStr
391
         * @param $errFile
392
         * @param $errLine
393
         * @return null
394
         */
395
        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

395
        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

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