XoopsGTicket::extract_post_recursive()   A
last analyzed

Complexity

Conditions 5
Paths 7

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

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