XoopsGTicket   F
last analyzed

Complexity

Total Complexity 68

Size/Duplication

Total Lines 453
Duplicated Lines 0.66 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 3
loc 453
rs 2.96
c 0
b 0
f 0
wmc 68
lcom 1
cbo 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 3 27 6
A getTicketHtml() 0 4 1
A getTicketXoopsForm() 0 4 1
A addTicketXoopsFormElement() 0 4 1
A getTicketArray() 0 4 1
A getTicketParamString() 0 4 2
C issue() 0 55 11
F check() 0 102 19
C draw_repost_form() 0 57 11
B extract_post_recursive() 0 30 7
A clear() 0 4 1
A using() 0 8 2
A getErrors() 0 14 3
A errorHandler4FindOutput() 0 8 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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
     * Class XoopsGTicket
8
     */
9
10
    class XoopsGTicket
11
    {
12
        public $_errors = [];
13
14
        public $_latest_token = '';
15
16
        public $messages = [];
17
18
        const PBKDF2_SALT_BYTES = 24;
19
20
        /**
21
         * XoopsGTicket constructor.
22
         */
23
24
        public function __construct()
25
        {
26
            global $xoopsConfig;
27
28
            // language file
29
30
            if (defined('XOOPS_ROOT_PATH') && !empty($xoopsConfig['language']) && !mb_strstr($xoopsConfig['language'], '/')) {
31 View Code Duplication
                if (is_file(dirname(__DIR__) . '/language/' . $xoopsConfig['language'] . '/gticket_messages.phtml')) {
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...
32
                    include dirname(__DIR__) . '/language/' . $xoopsConfig['language'] . '/gticket_messages.phtml';
33
                }
34
            }
35
36
            // default messages
37
38
            if (empty($this->messages)) {
39
                $this->messages = [
40
                    'err_general' => 'GTicket Error',
41
                    'err_nostubs' => 'No stubs found',
42
                    'err_noticket' => 'No ticket found',
43
                    'err_nopair' => 'No valid ticket-stub pair found',
44
                    'err_timeout' => 'Time out',
45
                    'err_areaorref' => 'Invalid area or referer',
46
                    '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?',
47
                    'btn_repost' => 'repost',
48
                ];
49
            }
50
        }
51
52
        // render form as plain html
53
54
        /**
55
         * @param string $salt
56
         * @param int    $timeout
57
         * @param string $area
58
         * @return string
59
         */
60
61
        public function getTicketHtml($salt = '', $timeout = 1800, $area = '')
62
        {
63
            return '<input type="hidden" name="XOOPS_G_TICKET" value="' . $this->issue($salt, $timeout, $area) . '" />';
64
        }
65
66
        // returns an object of XoopsFormHidden including theh ticket
67
68
        /**
69
         * @param string $salt
70
         * @param int    $timeout
71
         * @param string $area
72
         * @return \XoopsFormHidden
73
         */
74
75
        public function getTicketXoopsForm($salt = '', $timeout = 1800, $area = '')
76
        {
77
            return new XoopsFormHidden('XOOPS_G_TICKET', $this->issue($salt, $timeout, $area));
78
        }
79
80
        // add a ticket as Hidden Element into XoopsForm
81
82
        /**
83
         * @param        $form
84
         * @param string $salt
85
         * @param int    $timeout
86
         * @param string $area
87
         */
88
89
        public function addTicketXoopsFormElement($form, $salt = '', $timeout = 1800, $area = '')
90
        {
91
            $form->addElement(new XoopsFormHidden('XOOPS_G_TICKET', $this->issue($salt, $timeout, $area)));
92
        }
93
94
        // returns an array for xoops_confirm() ;
95
96
        /**
97
         * @param string $salt
98
         * @param int    $timeout
99
         * @param string $area
100
         * @return array
101
         */
102
103
        public function getTicketArray($salt = '', $timeout = 1800, $area = '')
104
        {
105
            return ['XOOPS_G_TICKET' => $this->issue($salt, $timeout, $area)];
106
        }
107
108
        // return GET parameter string.
109
110
        /**
111
         * @param string $salt
112
         * @param bool   $noamp
113
         * @param int    $timeout
114
         * @param string $area
115
         * @return string
116
         */
117
118
        public function getTicketParamString($salt = '', $noamp = false, $timeout = 1800, $area = '')
119
        {
120
            return ($noamp ? '' : '&amp;') . 'XOOPS_G_TICKET=' . $this->issue($salt, $timeout, $area);
121
        }
122
123
        // issue a ticket
124
125
        /**
126
         * @param string $salt
127
         * @param int    $timeout
128
         * @param string $area
129
         * @return string
130
         */
131
132
        public function issue($salt = '', $timeout = 1800, $area = '')
133
        {
134
            global $xoopsModule;
135
136
            if ('' === $salt) {
137
                if (version_compare(PHP_VERSION, '7.0.0') >= 0 && function_exists('random_bytes')) {
138
                    $salt = '$2y$07$' . str_replace('+', '.', base64_encode(random_bytes(self::PBKDF2_SALT_BYTES)));
139
                } elseif (function_exists('mcrypt_create_iv')) {
140
                    $salt = '$2y$07$' . str_replace('+', '.', base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)));
141
                }
142
            }
143
144
            // create a token
145
146
            list($usec, $sec) = explode(' ', microtime());
147
148
            $appendix_salt = empty($_SERVER['PATH']) ? XOOPS_DB_NAME : $_SERVER['PATH'];
149
150
            $token = crypt($salt . $usec . $appendix_salt . $sec, $salt);
151
152
            $this->_latest_token = $token;
153
154
            if (empty($_SESSION['XOOPS_G_STUBS'])) {
155
                $_SESSION['XOOPS_G_STUBS'] = [];
156
            }
157
158
            // limit max stubs 10
159
160
            if (count($_SESSION['XOOPS_G_STUBS']) > 10) {
161
                $_SESSION['XOOPS_G_STUBS'] = array_slice($_SESSION['XOOPS_G_STUBS'], -10);
162
            }
163
164
            // record referer if browser send it
165
166
            $referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['REQUEST_URI'];
167
168
            // area as module's dirname
169
170
            if (!$area && is_object(@$xoopsModule)) {
171
                $area = $xoopsModule->getVar('dirname');
172
            }
173
174
            // store stub
175
176
            $_SESSION['XOOPS_G_STUBS'][] = [
177
                'expire' => time() + $timeout,
178
                'referer' => $referer,
179
                'area' => $area,
180
                'token' => $token,
181
            ];
182
183
            // paid md5ed token as a ticket
184
185
            return md5($token . XOOPS_DB_PREFIX);
186
        }
187
188
        // check a ticket
189
190
        /**
191
         * @param bool   $post
192
         * @param string $area
193
         * @param bool   $allow_repost
194
         * @return bool
195
         */
0 ignored issues
show
Documentation introduced by
Should the return type not be null|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
196
197
        public function check($post = true, $area = '', $allow_repost = true)
198
        {
199
            global $xoopsModule;
200
201
            $this->_errors = [];
202
203
            // CHECK: stubs are not stored in session
204
205
            if (!is_array(@$_SESSION['XOOPS_G_STUBS'])) {
206
                $this->_errors[] = $this->messages['err_nostubs'];
207
208
                $_SESSION['XOOPS_G_STUBS'] = [];
209
            }
210
211
            // get key&val of the ticket from a user's query
212
213
            $ticket = $post ? @$_POST['XOOPS_G_TICKET'] : @$_GET['XOOPS_G_TICKET'];
214
215
            // CHECK: no tickets found
216
217
            if (empty($ticket)) {
218
                $this->_errors[] = $this->messages['err_noticket'];
219
            }
220
221
            // gargage collection & find a right stub
222
223
            $stubs_tmp = $_SESSION['XOOPS_G_STUBS'];
224
225
            $_SESSION['XOOPS_G_STUBS'] = [];
226
227
            foreach ($stubs_tmp as $stub) {
228
                // default lifetime 30min
229
230
                if ($stub['expire'] >= time()) {
231
                    if (md5($stub['token'] . XOOPS_DB_PREFIX) === $ticket) {
232
                        $found_stub = $stub;
233
                    } else {
234
                        // store the other valid stubs into session
235
236
                        $_SESSION['XOOPS_G_STUBS'][] = $stub;
237
                    }
238
                } else {
239
                    if (md5($stub['token'] . XOOPS_DB_PREFIX) === $ticket) {
240
                        // not CSRF but Time-Out
241
242
                        $timeout_flag = true;
243
                    }
244
                }
245
            }
246
247
            // CHECK: the right stub found or not
248
249
            if (empty($found_stub)) {
250
                if (empty($timeout_flag)) {
251
                    $this->_errors[] = $this->messages['err_nopair'];
252
                } else {
253
                    $this->_errors[] = $this->messages['err_timeout'];
254
                }
255
            } else {
256
                // set area if necessary
257
258
                // area as module's dirname
259
260
                if (!$area && is_object(@$xoopsModule)) {
261
                    $area = $xoopsModule->getVar('dirname');
262
                }
263
264
                // check area or referer
265
266
                if (@$found_stub['area'] == $area) {
267
                    $area_check = true;
268
                }
269
270
                if (!empty($found_stub['referer']) && mb_strstr(@$_SERVER['HTTP_REFERER'], $found_stub['referer'])) {
271
                    $referer_check = true;
272
                }
273
274
                if (empty($area_check) && empty($referer_check)) { // loose
275
                    $this->_errors[] = $this->messages['err_areaorref'];
276
                }
277
            }
278
279
            if (!empty($this->_errors)) {
280
                if ($allow_repost) {
281
                    // repost form
282
283
                    $this->draw_repost_form($area);
284
285
                    exit;
286
                }
287
288
                // failed
289
290
                $this->clear();
291
292
                return false;
293
            }
294
295
            // all green
296
297
            return true;
298
        }
299
300
        // draw form for repost
301
302
        /**
303
         * @param string $area
304
         */
305
306
        public function draw_repost_form($area = '')
307
        {
308
            // Notify which file is broken
309
310
            if (headers_sent()) {
311
                restore_error_handler();
312
313
                set_error_handler([&$this, 'errorHandler4FindOutput']);
314
315
                header('Dummy: for warning');
316
317
                restore_error_handler();
318
319
                exit;
320
            }
321
322
            $table = '<table>';
323
324
            $form = '<form action="?' . htmlspecialchars(@$_SERVER['QUERY_STRING'], ENT_QUOTES) . '" method="post" >';
325
326
            foreach ($_POST as $key => $val) {
327
                if ('XOOPS_G_TICKET' == $key) {
328
                    continue;
329
                }
330
331
                if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc()) {
332
                    $key = stripslashes($key);
333
                }
334
335
                if (is_array($val)) {
336
                    list($tmp_table, $tmp_form) = $this->extract_post_recursive(htmlspecialchars($key, ENT_QUOTES), $val);
337
338
                    $table .= $tmp_table;
339
340
                    $form .= $tmp_form;
341
                } else {
342
                    if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc()) {
343
                        $val = stripslashes($val);
344
                    }
345
346
                    $table .= '<tr><th>' . htmlspecialchars($key, ENT_QUOTES) . '</th><td>' . htmlspecialchars($val, ENT_QUOTES) . '</td></tr>' . "\n";
347
348
                    $form .= '<input type="hidden" name="' . htmlspecialchars($key, ENT_QUOTES) . '" value="' . htmlspecialchars($val, ENT_QUOTES) . '" />' . "\n";
349
                }
350
            }
351
352
            $table .= '</table>';
353
354
            $form .= $this->getTicketHtml(__LINE__, 300, $area) . '<input type="submit" value="' . $this->messages['btn_repost'] . '" /></form>';
355
356
            error_reporting(0);
357
358
            while (@ob_get_level() && @ob_end_clean()) {
359
            }
360
361
            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>';
362
        }
363
364
        /**
365
         * @param $key_name
366
         * @param $tmp_array
367
         * @return array
368
         */
369
370
        public function extract_post_recursive($key_name, $tmp_array)
371
        {
372
            $table = '';
373
374
            $form = '';
375
376
            foreach ($tmp_array as $key => $val) {
377
                if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc()) {
378
                    $key = stripslashes($key);
379
                }
380
381
                if (is_array($val)) {
382
                    list($tmp_table, $tmp_form) = $this->extract_post_recursive($key_name . '[' . htmlspecialchars($key, ENT_QUOTES) . ']', $val);
383
384
                    $table .= $tmp_table;
385
386
                    $form .= $tmp_form;
387
                } else {
388
                    if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc()) {
389
                        $val = stripslashes($val);
390
                    }
391
392
                    $table .= '<tr><th>' . $key_name . '[' . htmlspecialchars($key, ENT_QUOTES) . ']</th><td>' . htmlspecialchars($val, ENT_QUOTES) . '</td></tr>' . "\n";
393
394
                    $form .= '<input type="hidden" name="' . $key_name . '[' . htmlspecialchars($key, ENT_QUOTES) . ']" value="' . htmlspecialchars($val, ENT_QUOTES) . '" />' . "\n";
395
                }
396
            }
397
398
            return [$table, $form];
399
        }
400
401
        // clear all stubs
402
403
        public function clear()
404
        {
405
            $_SESSION['XOOPS_G_STUBS'] = [];
406
        }
407
408
        // Ticket Using
409
410
        /**
411
         * @return bool
412
         */
413
414
        public function using()
415
        {
416
            if (!empty($_SESSION['XOOPS_G_STUBS'])) {
417
                return true;
418
            }
419
420
            return false;
421
        }
422
423
        // return errors
424
425
        /**
426
         * @param bool $ashtml
427
         * @return array|string
428
         */
429
430
        public function getErrors($ashtml = true)
431
        {
432
            if ($ashtml) {
433
                $ret = '';
434
435
                foreach ($this->_errors as $msg) {
436
                    $ret .= "$msg<br />\n";
437
                }
438
            } else {
439
                $ret = $this->_errors;
440
            }
441
442
            return $ret;
443
        }
444
445
        /**
446
         * @param $errNo
447
         * @param $errStr
448
         * @param $errFile
449
         * @param $errLine
450
         */
451
452
        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.

This check looks from 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.

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

Loading history...
453
        {
454
            if (preg_match('?' . preg_quote(XOOPS_ROOT_PATH) . '([^:]+)\:(\d+)?', $errStr, $regs)) {
455
                echo 'Irregular output! check the file ' . htmlspecialchars($regs[1], ENT_QUOTES | ENT_HTML5) . ' line ' . htmlspecialchars($regs[2], ENT_QUOTES | ENT_HTML5);
456
            } else {
457
                echo 'Irregular output! check language files etc.';
458
            }
459
        }
460
461
        // end of class
462
    }
463
464
    // create a instance in global scope
465
466
    $GLOBALS['xoopsGTicket'] = new XoopsGTicket();
467
}
468
469
if (!function_exists('admin_refcheck')) {
470
    //Admin Referer Check By Marijuana(Rev.011)
471
472
    /**
473
     * @param string $chkref
474
     * @return bool
475
     */
476
477
    function admin_refcheck($chkref = '')
478
    {
479
        if (empty($_SERVER['HTTP_REFERER'])) {
480
            return true;
481
        }
482
483
        $ref = $_SERVER['HTTP_REFERER'];
484
485
        $cr = XOOPS_URL;
486
487
        if ('' != $chkref) {
488
            $cr .= $chkref;
489
        }
490
491
        if (0 !== mb_strpos($ref, $cr)) {
492
            return false;
493
        }
494
495
        return true;
496
    }
497
}
498