GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Session::getNumberOfSessions()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 19

Duplication

Lines 19
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 19
loc 19
rs 9.6333
c 0
b 0
f 0
1
<?php
2
3
namespace phpMyFAQ;
4
5
/**
6
 * The main Session class.
7
 *
8
 * This Source Code Form is subject to the terms of the Mozilla Public License,
9
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
10
 * obtain one at http://mozilla.org/MPL/2.0/.
11
 *
12
 * @package phpMyFAQ
13
 * @author Thorsten Rinne <[email protected]>
14
 * @copyright 2007-2019 phpMyFAQ Team
15
 * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
16
 * @link https://www.phpmyfaq.de
17
 * @since 2007-03-31
18
 */
19
20
use phpMyFAQ\Configuration;
21
use phpMyFAQ\Db;
22
use phpMyFAQ\Exception;
23
use phpMyFAQ\Filter;
24
use phpMyFAQ\Network;
25
26
if (!defined('IS_VALID_PHPMYFAQ')) {
27
    exit();
28
}
29
30
/**
31
 * Class Session.
32
 *
33
 * @package phpMyFAQ
34
 * @author Thorsten Rinne <[email protected]>
35
 * @copyright 2007-2019 phpMyFAQ Team
36
 * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
37
 * @link https://www.phpmyfaq.de
38
 * @since 2007-03-31
39
 */
40
class Session
41
{
42
    /** Constants. */
43
    const PMF_COOKIE_NAME_REMEMBERME = 'pmf_rememberme';
44
    const PMF_COOKIE_NAME_AUTH = 'pmf_auth';
45
    const PMF_COOKIE_NAME_SESSIONID = 'pmf_sid';
46
47
    /** @var Configuration */
48
    private $config;
49
50
    /**
51
     * Constructor.
52
     *
53
     * @param Configuration
54
     */
55
    public function __construct(Configuration $config)
56
    {
57
        $this->config = $config;
58
    }
59
60
    /**
61
     * Tracks the user and log what he did.
62
     *
63
     * @param string $action Action string
64
     * @param string $data
65
     *
66
     * @throws Exception
67
     */
68
    public function userTracking(string $action, $data = null)
69
    {
70
        global $sessionId, $user, $botBlacklist;
71
72
        if ($this->config->get('main.enableUserTracking')) {
73
            $bots = 0;
74
            $banned = false;
75
            $agent = $_SERVER['HTTP_USER_AGENT'];
76
            $sessionId = Filter::filterInput(INPUT_GET, PMF_GET_KEY_NAME_SESSIONID, FILTER_VALIDATE_INT);
77
            $cookieId = Filter::filterInput(INPUT_COOKIE, self::PMF_COOKIE_NAME_SESSIONID, FILTER_VALIDATE_INT);
78
79
            if (!is_null($cookieId)) {
80
                $sessionId = $cookieId;
81
            }
82
            if ($action == 'old_session') {
83
                $sessionId = null;
84
            }
85
86
            foreach ($botBlacklist as $bot) {
87
                if ((bool)Strings::strstr($agent, $bot)) {
88
                    ++$bots;
89
                }
90
            }
91
92
            $network = new Network($this->config);
93
94
            // if we're running behind a reverse proxy like nginx/varnish, fix the client IP
95
            $remoteAddress = $_SERVER['REMOTE_ADDR'];
96
            $localAddresses = ['127.0.0.1', '::1'];
97
98
            if (in_array($remoteAddress, $localAddresses) && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
99
                $remoteAddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
100
            }
101
            // clean up as well
102
            $remoteAddress = preg_replace('([^0-9a-z:\.]+)i', '', $remoteAddress);
103
104
            if (!$network->checkIp($remoteAddress)) {
105
                $banned = true;
106
            }
107
108
            if (0 === $bots && false === $banned) {
109
                if (!isset($sessionId)) {
110
                    $sessionId = $this->config->getDb()->nextId(Db::getTablePrefix().'faqsessions', 'sid');
111
                    // Sanity check: force the session cookie to contains the current $sid
112
                    if (!is_null($cookieId) && (!$cookieId != $sessionId)) {
113
                        self::setCookie(self::PMF_COOKIE_NAME_SESSIONID, $sessionId);
114
                    }
115
116
                    $query = sprintf("
117
                        INSERT INTO 
118
                            %sfaqsessions
119
                        (sid, user_id, ip, time)
120
                            VALUES
121
                        (%d, %d, '%s', %d)",
122
                        Db::getTablePrefix(),
123
                        $sessionId,
124
                        ($user ? $user->getUserId() : -1),
125
                        $remoteAddress,
126
                        $_SERVER['REQUEST_TIME']
127
                    );
128
                    $this->config->getDb()->query($query);
129
                }
130
131
                $data = $sessionId.';'.
132
                        str_replace(';', ',', $action).';'.
133
                        $data.';'.
134
                        $remoteAddress.';'.
135
                        str_replace(';', ',', isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '').';'.
136
                        str_replace(';', ',', isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '').';'.
137
                        str_replace(';', ',', urldecode($_SERVER['HTTP_USER_AGENT'])).';'.
138
                        $_SERVER['REQUEST_TIME'].";\n";
139
                $file = PMF_ROOT_DIR.'/data/tracking'.date('dmY');
140
141
                if (!is_file($file)) {
142
                    touch($file);
143
                }
144
145
                if (is_writeable($file)) {
146
                    file_put_contents($file, $data, FILE_APPEND | LOCK_EX);
147
                } else {
148
                    throw new Exception('Cannot write to '.$file);
149
                }
150
            }
151
        }
152
    }
153
154
    /**
155
     * Returns the timestamp of a session.
156
     *
157
     * @param int $sid Session ID
158
     *
159
     * @return int
160
     */
161 View Code Duplication
    public function getTimeFromSessionId(int $sid)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
162
    {
163
        $timestamp = 0;
164
165
        $query = sprintf('
166
            SELECT
167
                time
168
            FROM
169
                %sfaqsessions
170
            WHERE
171
                sid = %d',
172
            Db::getTablePrefix(),
173
            $sid);
174
175
        $result = $this->config->getDb()->query($query);
176
177
        if ($result) {
178
            $res = $this->config->getDb()->fetchObject($result);
179
            $timestamp = $res->time;
180
        }
181
182
        return $timestamp;
183
    }
184
185
    /**
186
     * Returns all session from a date.
187
     *
188
     * @param int $firstHour First hour
189
     * @param int $lastHour  Last hour
190
     *
191
     * @return array
192
     */
193 View Code Duplication
    public function getSessionsByDate(int $firstHour, int $lastHour): array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
194
    {
195
        $sessions = [];
196
197
        $query = sprintf('
198
            SELECT
199
                sid, ip, time
200
            FROM
201
                %sfaqsessions
202
            WHERE
203
                time > %d
204
            AND
205
                time < %d
206
            ORDER BY
207
                time',
208
            Db::getTablePrefix(),
209
            $firstHour,
210
            $lastHour
211
        );
212
213
        $result = $this->config->getDb()->query($query);
214
        while ($row = $this->config->getDb()->fetchObject($result)) {
215
            $sessions[$row->sid] = array(
216
                'ip' => $row->ip,
217
                'time' => $row->time,
218
            );
219
        }
220
221
        return $sessions;
222
    }
223
224
    /**
225
     * Returns the number of sessions.
226
     *
227
     * @return int
228
     */
229 View Code Duplication
    public function getNumberOfSessions(): int
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
230
    {
231
        $num = 0;
232
233
        $query = sprintf('
234
            SELECT
235
                COUNT(sid) as num_sessions
236
            FROM
237
                %sfaqsessions',
238
            Db::getTablePrefix());
239
240
        $result = $this->config->getDb()->query($query);
241
        if ($result) {
242
            $row = $this->config->getDb()->fetchObject($result);
243
            $num = $row->num_sessions;
244
        }
245
246
        return $num;
247
    }
248
249
    /**
250
     * Deletes the sessions for a given timespan.
251
     *
252
     * @param int $first Frist session ID
253
     * @param int $last  Last session ID
254
     *
255
     * @return bool
256
     */
257 View Code Duplication
    public function deleteSessions(int $first, int $last): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
258
    {
259
        $query = sprintf('
260
            DELETE FROM
261
                %sfaqsessions
262
            WHERE
263
                time >= %d
264
            AND
265
                time <= %d',
266
            Db::getTablePrefix(),
267
            $first,
268
            $last);
269
270
        $this->config->getDb()->query($query);
271
272
        return true;
273
    }
274
275
    /**
276
     * Deletes all entries in the table.
277
     *
278
     * @return mixed
279
     */
280
    public function deleteAllSessions()
281
    {
282
        $query = sprintf('DELETE FROM %sfaqsessions', Db::getTablePrefix());
283
284
        return $this->config->getDb()->query($query);
285
    }
286
287
    /**
288
     * Checks the Session ID.
289
     *
290
     * @param int $sessionIdToCheck Session ID
291
     * @param string $ip IP
292
     * @throws
293
     */
294
    public function checkSessionId(int $sessionIdToCheck, string $ip)
295
    {
296
        global $sessionId, $user;
297
298
        $query = sprintf("
299
            SELECT
300
                sid
301
            FROM
302
                %sfaqsessions
303
            WHERE
304
                sid = %d
305
            AND
306
                ip = '%s'
307
            AND
308
                time > %d",
309
            Db::getTablePrefix(),
310
            $sessionIdToCheck,
311
            $ip,
312
            $_SERVER['REQUEST_TIME'] - 86400
313
        );
314
        $result = $this->config->getDb()->query($query);
315
316
        if ($this->config->getDb()->numRows($result) == 0) {
317
            $this->userTracking('old_session', $sessionIdToCheck);
318
        } else {
319
            // Update global session id
320
            $sessionId = $sessionIdToCheck;
321
            // Update db tracking
322
            $query = sprintf("
323
                UPDATE
324
                    %sfaqsessions
325
                SET
326
                    time = %d,
327
                    user_id = %d
328
                WHERE
329
                    sid = %d
330
                    AND ip = '%s'",
331
                Db::getTablePrefix(),
332
                $_SERVER['REQUEST_TIME'],
333
                ($user ? $user->getUserId() : '-1'),
334
                $sessionIdToCheck,
335
                $ip
336
            );
337
            $this->config->getDb()->query($query);
338
        }
339
    }
340
    /**
341
     * Returns the number of anonymous users and registered ones.
342
     * These are the numbers of unique users who have performed
343
     * some activities within the last five minutes.
344
     *
345
     * @param int $activityTimeWindow Optionally set the time window size in sec. 
346
     *                                Default: 300sec, 5 minutes
347
     *
348
     * @return array
349
     */
350
    public function getUsersOnline(int $activityTimeWindow = 300): array
351
    {
352
        $users = array(0, 0);
353
354
        if ($this->config->get('main.enableUserTracking')) {
355
            $timeNow = ($_SERVER['REQUEST_TIME'] - $activityTimeWindow);
356
357
            if (!$this->config->get('security.enableLoginOnly')) {
358
                // Count all sids within the time window for public installations
359
                $query = sprintf('
360
                    SELECT
361
                        count(sid) AS anonymous_users
362
                    FROM
363
                        %sfaqsessions
364
                    WHERE
365
                        user_id = -1
366
                    AND
367
                        time > %d',
368
                    Db::getTablePrefix(),
369
                    $timeNow
370
                );
371
372
                $result = $this->config->getDb()->query($query);
373
374 View Code Duplication
                if (isset($result)) {
375
                    $row = $this->config->getDb()->fetchObject($result);
376
                    $users[0] = $row->anonymous_users;
377
                }
378
            }
379
380
            // Count all faquser records within the time window
381
            $query = sprintf('
382
                SELECT
383
                    count(session_id) AS registered_users
384
                FROM
385
                    %sfaquser
386
                WHERE
387
                    session_timestamp > %d',
388
                Db::getTablePrefix(),
389
                $timeNow
390
            );
391
392
            $result = $this->config->getDb()->query($query);
393
394 View Code Duplication
            if (isset($result)) {
395
                $row = $this->config->getDb()->fetchObject($result);
396
                $users[1] = $row->registered_users;
397
            }
398
        }
399
400
        return $users;
401
    }
402
403
    /**
404
     * Calculates the number of visits per day the last 30 days.
405
     *
406
     * @returns array
407
     */
408
    public function getLast30DaysVisits(): array
409
    {
410
        $stats = $visits = [];
411
412
        $startDate = strtotime('-1 month');
413
        $endDate = $_SERVER['REQUEST_TIME'];
414
415
        $query = sprintf('
416
            SELECT
417
                time
418
            FROM
419
                %sfaqsessions
420
            WHERE
421
                time > %d
422
            AND
423
                time < %d;',
424
            Db::getTablePrefix(),
425
            $startDate,
426
            $endDate
427
        );
428
        $result = $this->config->getDb()->query($query);
429
430
        while ($row = $this->config->getDb()->fetchObject($result)) {
431
            $visits[] = $row->time;
432
        }
433
434
        for ($date = $startDate; $date <= $endDate; $date += 86400) {
435
            $stats[date('Y-m-d', $date)] = 0;
436
        }
437
438
        foreach ($visits as $visitDate) {
439
            isset($stats[date('Y-m-d', $visitDate)]) ? $stats[date('Y-m-d', $visitDate)]++ : null;
440
        }
441
442
        return $stats;
443
    }
444
445
    /**
446
     * Store the Session ID into a persistent cookie expiring
447
     * PMF_SESSION_EXPIRED_TIME seconds after the page request.
448
     *
449
     * @param string $name Cookie name
450
     * @param string|null $sessionId Session ID
451
     * @param int $timeout Cookie timeout
452
     *
453
     * @return bool
454
     */
455
    public function setCookie(string $name, $sessionId = '', int $timeout = PMF_SESSION_EXPIRED_TIME): bool
456
    {
457
        $protocol = 'http';
458
        if (isset($_SERVER['HTTPS']) && strtoupper($_SERVER['HTTPS']) === 'ON') {
459
            $protocol = 'https';
460
        }
461
        return setcookie(
462
            $name,
463
            $sessionId,
464
            $_SERVER['REQUEST_TIME'] + $timeout,
465
            dirname($_SERVER['SCRIPT_NAME']),
466
            $this->config->getDefaultUrl(),
467
            ('https' === $protocol) ? true : false,
468
            true
469
        );
470
    }
471
}
472