Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Failed Conditions
Pull Request — master (#1031)
by Dan
04:16
created

SmrSession::getCommonID()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
if (!defined('USING_AJAX')) {
4
	define('USING_AJAX', false);
5
}
6
7
class SmrSession {
8
9
	const TIME_BEFORE_EXPIRY = 3600;
10
11
	private const URL_LOAD_DELAY = array(
12
		'configure_hardware.php' => .4,
13
		'forces_drop.php' => .4,
14
		'forces_drop_processing.php' => .5,
15
		'forces_refresh_processing.php' => .4,
16
		'sector_jump_processing.php' => .4,
17
		'sector_move_processing.php' => .4,
18
		'sector_scan.php' => .4,
19
		'shop_goods_processing.php' => .4,
20
		'trader_attack_processing.php' => .75,
21
		'trader_examine.php' => .75
22
	);
23
24
	protected static MySqlDatabase $db;
25
26
	private static ?string $session_id;
27
	private static int $game_id;
28
	private static array $var;
29
	private static array $commonIDs;
30
	private static bool $generate;
31
	private static string $SN = '';
32
	private static string $lastSN;
33
	private static int $account_id;
34
	public static int $last_accessed;
35
	private static Time $pageRequestTime;
36
37
	protected static array $previousAjaxReturns;
38
	protected static array $ajaxReturns = array();
39
40
	/**
41
	 * Returns the time (in seconds) associated with this page request.
42
	 */
43
	public static function getTime() : int {
44
		return self::$pageRequestTime->getTime();
45
	}
46
47
	/**
48
	 * Returns the time (in seconds, with microsecond-level precision)
49
	 * associated with this page request.
50
	 */
51
	public static function getMicroTime() : float {
52
		return self::$pageRequestTime->getMicroTime();
53
	}
54
55
	/**
56
	 * Update the time associated with this page request.
57
	 *
58
	 * NOTE: This should never be called by normal page requests, and should
59
	 * only be used by the CLI programs that run continuously.
60
	 */
61
	public static function updateTime() : void {
62
		if (!defined('NPC_SCRIPT')) {
63
			throw new Exception('Only call this function from CLI programs!');
64
		}
65
		self::$pageRequestTime = new Time();
66
	}
67
68
	public static function init() : void {
69
		// Return immediately if the SmrSession is already initialized
70
		if (isset(self::$session_id)) {
71
			return;
72
		}
73
74
		// Initialize the page request time
75
		self::$pageRequestTime = new Time();
76
77
		// Initialize the db connector here, since `init` is always called
78
		self::$db = MySqlDatabase::getInstance();
79
80
		// now try the cookie
81
		if (isset($_COOKIE['session_id']) && strlen($_COOKIE['session_id']) === 32) {
82
			self::$session_id = $_COOKIE['session_id'];
83
		} else {
84
			// create a new session id
85
			do {
86
				self::$session_id = md5(uniqid(strval(rand())));
87
				self::$db->query('SELECT 1 FROM active_session WHERE session_id = ' . self::$db->escapeString(self::$session_id) . ' LIMIT 1');
88
			} while (self::$db->nextRecord()); //Make sure we haven't somehow clashed with someone else's session.
89
90
			// This is a minor hack to make sure that setcookie is not called
91
			// for CLI programs and tests (to avoid "headers already sent").
92
			if (headers_sent() === false) {
93
				setcookie('session_id', self::$session_id);
94
			}
95
		}
96
97
		// try to get current session
98
		self::$db->query('DELETE FROM active_session WHERE last_accessed < ' . self::$db->escapeNumber(time() - self::TIME_BEFORE_EXPIRY));
99
		self::fetchVarInfo();
100
101
		$sn = Request::get('sn', '');
102
		if (!USING_AJAX && !empty($sn) && !empty(self::$var[$sn])) {
103
			$var = self::$var[$sn];
104
			$currentPage = $var['url'] == 'skeleton.php' ? $var['body'] : $var['url'];
105
			$loadDelay = self::URL_LOAD_DELAY[$currentPage] ?? 0;
106
			$initialTimeBetweenLoads = microtime(true) - $var['PreviousRequestTime'];
107
			while (($timeBetweenLoads = microtime(true) - $var['PreviousRequestTime']) < $loadDelay) {
108
				$sleepTime = IRound(($loadDelay - $timeBetweenLoads) * 1000000);
109
			//	echo 'Sleeping for: ' . $sleepTime . 'us';
110
				usleep($sleepTime);
111
			}
112
			if (ENABLE_DEBUG) {
113
				self::$db->query('INSERT INTO debug VALUES (' . self::$db->escapeString('Delay: ' . $currentPage) . ',' . self::$db->escapeNumber(self::$account_id) . ',' . self::$db->escapeNumber($initialTimeBetweenLoads) . ',' . self::$db->escapeNumber($timeBetweenLoads) . ')');
114
			}
115
		}
116
	}
117
118
	public static function fetchVarInfo() : void {
119
		self::$db->query('SELECT * FROM active_session WHERE session_id = ' . self::$db->escapeString(self::$session_id) . ' LIMIT 1');
120
		if (self::$db->nextRecord()) {
121
			self::$generate = false;
122
			self::$session_id = self::$db->getField('session_id');
123
			self::$account_id = self::$db->getInt('account_id');
124
			self::$game_id = self::$db->getInt('game_id');
125
			self::$last_accessed = self::$db->getInt('last_accessed');
126
			self::$commonIDs = array();
127
			self::$lastSN = self::$db->getField('last_sn');
128
			// We may not have ajax_returns if ajax was disabled
129
			$ajaxReturns = self::$db->getField('ajax_returns');
130
			if (!empty($ajaxReturns)) {
131
				self::$previousAjaxReturns = unserialize(gzuncompress($ajaxReturns));
132
			}
133
134
			$var = self::$db->getField('session_var');
135
			if ($var === '') {
136
				self::$account_id = 0;
137
				self::$game_id = 0;
138
				self::$var = array();
139
			} else {
140
				self::$var = unserialize(gzuncompress($var));
141
			}
142
143
			foreach (self::$var as $key => $value) {
144
				if ($value['Expires'] > 0 && $value['Expires'] <= self::getTime()) { // Use 0 for infinity
145
					//This link is no longer valid
146
					unset(self::$var[$key]);
147
				} elseif ($value['RemainingPageLoads'] < 0) {
148
					//This link is no longer valid
149
					unset(self::$var[$key]);
150
				} else {
151
					--self::$var[$key]['RemainingPageLoads'];
152
					if (isset($value['CommonID'])) {
153
						self::$commonIDs[$value['CommonID']] = $key;
154
					}
155
				}
156
			}
157
		} else {
158
			self::$generate = true;
159
			self::$account_id = 0;
160
			self::$game_id = 0;
161
			self::$var = array();
162
			self::$commonIDs = array();
163
		}
164
	}
165
166
	public static function update() : void {
167
		foreach (self::$var as $key => $value) {
168
			if ($value['RemainingPageLoads'] <= 0) {
169
				//This link was valid this load but will not be in the future, removing it now saves database space and data transfer.
170
				unset(self::$var[$key]);
171
			}
172
		}
173
		$compressed = gzcompress(serialize(self::$var));
174
		if (!self::$generate) {
175
			self::$db->query('UPDATE active_session SET account_id=' . self::$db->escapeNumber(self::$account_id) . ',game_id=' . self::$db->escapeNumber(self::$game_id) . (!USING_AJAX ? ',last_accessed=' . self::$db->escapeNumber(self::getTime()) : '') . ',session_var=' . self::$db->escapeBinary($compressed) .
176
					',last_sn=' . self::$db->escapeString(self::$SN) .
177
					' WHERE session_id=' . self::$db->escapeString(self::$session_id) . (USING_AJAX ? ' AND last_sn=' . self::$db->escapeString(self::$lastSN) : '') . ' LIMIT 1');
178
		} else {
179
			self::$db->query('DELETE FROM active_session WHERE account_id = ' . self::$db->escapeNumber(self::$account_id) . ' AND game_id = ' . self::$db->escapeNumber(self::$game_id));
180
			self::$db->query('INSERT INTO active_session (session_id, account_id, game_id, last_accessed, session_var) VALUES(' . self::$db->escapeString(self::$session_id) . ',' . self::$db->escapeNumber(self::$account_id) . ',' . self::$db->escapeNumber(self::$game_id) . ',' . self::$db->escapeNumber(self::getTime()) . ',' . self::$db->escapeBinary($compressed) . ')');
181
			self::$generate = false;
182
		}
183
	}
184
185
	/**
186
	 * Returns the Game ID associated with the session.
187
	 */
188
	public static function getGameID() : int {
189
		return self::$game_id;
190
	}
191
192
	/**
193
	 * Returns true if the session is inside a game, false otherwise.
194
	 */
195
	public static function hasGame() : bool {
196
		return self::$game_id != 0;
197
	}
198
199
	public static function hasAccount() : bool {
200
		return self::$account_id > 0;
201
	}
202
203
	public static function getAccountID() : int {
204
		return self::$account_id;
205
	}
206
207
	public static function getAccount() : SmrAccount {
208
		return SmrAccount::getAccount(self::$account_id);
209
	}
210
211
	/**
212
	 * Sets the `account_id` attribute of this session.
213
	 */
214
	public static function setAccount(AbstractSmrAccount $account) : void {
215
		self::$account_id = $account->getAccountID();
216
	}
217
218
	/**
219
	 * Updates the `game_id` attribute of the session and deletes any other
220
	 * active sessions in this game for this account.
221
	 */
222
	public static function updateGame(int $gameID) : void {
223
		if (self::$game_id == $gameID) {
224
			return;
225
		}
226
		self::$game_id = $gameID;
227
		self::$db->query('DELETE FROM active_session WHERE account_id = ' . self::$db->escapeNumber(self::$account_id) . ' AND game_id = ' . self::$game_id);
228
		self::$db->query('UPDATE active_session SET game_id=' . self::$db->escapeNumber(self::$game_id) . ' WHERE session_id=' . self::$db->escapeString(self::$session_id));
229
	}
230
231
	/**
232
	 * Returns true if the current SN is different than the previous SN.
233
	 */
234
	public static function hasChangedSN() : bool {
235
		return self::$SN != self::$lastSN;
236
	}
237
238
	private static function updateSN() : void {
239
		if (!USING_AJAX) {
240
			self::$db->query('UPDATE active_session SET last_sn=' . self::$db->escapeString(self::$SN) .
241
				' WHERE session_id=' . self::$db->escapeString(self::$session_id) . ' LIMIT 1');
242
		}
243
	}
244
245
	public static function destroy() : void {
246
		self::$db->query('DELETE FROM active_session WHERE session_id = ' . self::$db->escapeString(self::$session_id));
247
		self::$session_id = null;
248
		self::$account_id = 0;
249
		self::$game_id = 0;
250
	}
251
252
	/**
253
	 * Retrieve the session var for the page given by $sn.
254
	 * If $sn is not specified, use the current page (i.e. self::$SN).
255
	 */
256
	public static function retrieveVar(string $sn = null) : Page|false {
257
		if (is_null($sn)) {
258
			$sn = self::$SN;
259
		}
260
		if (empty(self::$var[$sn])) {
261
			return false;
262
		}
263
		self::$SN = $sn;
264
		SmrSession::updateSN();
265
		if (isset(self::$var[$sn]['body']) && isset(self::$var[$sn]['CommonID'])) {
266
//			if(preg_match('/processing/',self::$var[$sn]['body']))
267
			unset(self::$commonIDs[self::$var[$sn]['CommonID']]); //Do not store common id for current page
268
			unset(self::$var[$sn]['CommonID']);
269
		}
270
271
		self::$var[$sn]['RemainingPageLoads'] += 1; // Allow refreshing
272
		self::$var[$sn]['Expires'] = 0; // Allow refreshing forever
273
		return self::$var[$sn];
274
	}
275
276
	/**
277
	 * Gets a var from $var, $_REQUEST, or $default. Then stores it in the
278
	 * session so that it can still be retrieved when the page auto-refreshes.
279
	 * This is the recommended way to get $_REQUEST data for display pages.
280
	 * For processing pages, see the Request class.
281
	 */
282
	public static function getRequestVar(string $varName, string $default = null) : string {
283
		$result = Request::getVar($varName, $default);
284
		self::updateVar($varName, $result);
285
		return $result;
286
	}
287
288
	public static function getRequestVarInt(string $varName, int $default = null) : int {
289
		$result = Request::getVarInt($varName, $default);
290
		self::updateVar($varName, $result);
291
		return $result;
292
	}
293
294
	public static function getRequestVarIntArray(string $varName, array $default = null) : array {
295
		$result = Request::getVarIntArray($varName, $default);
296
		self::updateVar($varName, $result);
297
		return $result;
298
	}
299
300
	public static function resetLink(Page $container, string $sn) : string {
301
		//Do not allow sharing SN, useful for forwarding.
302
		global $lock;
303
		if (isset(self::$var[$sn]['CommonID'])) {
304
			unset(self::$commonIDs[self::$var[$sn]['CommonID']]); //Do not store common id for reset page, to allow refreshing to always give the same page in response
305
		}
306
		self::$SN = $sn;
307
		if (!isset($container['Expires'])) {
308
			$container['Expires'] = 0; // Lasts forever
309
		}
310
		if (!isset($container['RemainingPageLoads'])) {
311
			$container['RemainingPageLoads'] = 1; // Allow refreshing
312
		}
313
		if (!isset($container['PreviousRequestTime'])) {
314
			if (isset(self::$var[$sn]['PreviousRequestTime'])) {
315
				$container['PreviousRequestTime'] = self::$var[$sn]['PreviousRequestTime']; // Copy across the previous request time if not explicitly set.
316
			}
317
		}
318
319
		self::$var[$sn] = $container;
320
		if (!$lock && !USING_AJAX) {
321
			self::update();
322
		}
323
		return $sn;
324
	}
325
326
	public static function updateVar(string $key, mixed $value) : void {
327
		global $var;
328
		if ($value === null) {
329
			if (isset($var[$key])) {
330
				unset($var[$key]);
331
			}
332
			if (isset($var[self::$SN][$key])) {
333
				unset(self::$var[self::$SN][$key]);
334
			}
335
		} else {
336
			$var[$key] = $value;
337
			self::$var[self::$SN][$key] = $value;
338
		}
339
	}
340
341
	public static function clearLinks() : void {
342
		self::$var = array(self::$SN => self::$var[self::$SN]);
343
		self::$commonIDs = array();
344
	}
345
346
	public static function addLink(Page $container) : string {
347
		$sn = self::generateSN($container);
348
		self::$var[$sn] = $container;
349
		return $sn;
350
	}
351
352
	protected static function generateSN(Page $container) : string {
353
		if (isset(self::$commonIDs[$container['CommonID']])) {
354
			$sn = self::$commonIDs[$container['CommonID']];
355
			$container['PreviousRequestTime'] = isset(self::$var[$sn]) ? self::$var[$sn]['PreviousRequestTime'] : self::getMicroTime();
356
		} else {
357
			do {
358
				$sn = random_alphabetic_string(6);
359
			} while (isset(self::$var[$sn]));
360
			$container['PreviousRequestTime'] = self::getMicroTime();
361
		}
362
		self::$commonIDs[$container['CommonID']] = $sn;
363
		return $sn;
364
	}
365
366
	public static function addAjaxReturns(string $element, string $contents) : bool {
367
		self::$ajaxReturns[$element] = $contents;
368
		return isset(self::$previousAjaxReturns[$element]) && self::$previousAjaxReturns[$element] == $contents;
369
	}
370
371
	public static function saveAjaxReturns() : void {
372
		if (empty(self::$ajaxReturns)) {
373
			return;
374
		}
375
		$compressed = gzcompress(serialize(self::$ajaxReturns));
376
		self::$db->query('UPDATE active_session SET ajax_returns=' . self::$db->escapeBinary($compressed) .
377
				' WHERE session_id=' . self::$db->escapeString(self::$session_id) . ' LIMIT 1');
378
	}
379
}
380
381
SmrSession::init();
382