Completed
Pull Request — release-2.1 (#5077)
by Mathias
07:41 queued 01:11
created

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * This, as you have probably guessed, is the crux on which SMF functions.
5
 * Everything should start here, so all the setup and security is done
6
 * properly.  The most interesting part of this file is the action array in
7
 * the smf_main() function.  It is formatted as so:
8
 * 	'action-in-url' => array('Source-File.php', 'FunctionToCall'),
9
 *
10
 * Then, you can access the FunctionToCall() function from Source-File.php
11
 * with the URL index.php?action=action-in-url.  Relatively simple, no?
12
 *
13
 * Simple Machines Forum (SMF)
14
 *
15
 * @package SMF
16
 * @author Simple Machines http://www.simplemachines.org
17
 * @copyright 2018 Simple Machines and individual contributors
18
 * @license http://www.simplemachines.org/about/smf/license.php BSD
19
 *
20
 * @version 2.1 Beta 4
21
 */
22
23
$software_year = '2018';
24
$forum_version = 'SMF 2.1 Beta 4';
25
26
// Get everything started up...
27
define('SMF', 1);
28
error_reporting(E_ALL);
29
$time_start = microtime(true);
30
31
// This makes it so headers can be sent!
32
ob_start();
33
34
// Do some cleaning, just in case.
35 View Code Duplication
foreach (array('db_character_set', 'cachedir') as $variable)
36
	if (isset($GLOBALS[$variable]))
37
		unset($GLOBALS[$variable], $GLOBALS[$variable]);
38
39
// Load the settings...
40
require_once(dirname(__FILE__) . '/Settings.php');
41
42
// Make absolutely sure the cache directory is defined.
43 View Code Duplication
if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
44
	$cachedir = $boarddir . '/cache';
45
46
// For php below 7
47
if (!function_exists('random_int'))
48
	require_once ($sourcedir . '/random_compat/random_int.php');
49
50
// Without those we can't go anywhere
51
require_once($sourcedir . '/QueryString.php');
52
require_once($sourcedir . '/Subs.php');
53
require_once($sourcedir . '/Subs-Auth.php');
54
require_once($sourcedir . '/Errors.php');
55
require_once($sourcedir . '/Load.php');
56
57
// If $maintenance is set specifically to 2, then we're upgrading or something.
58
if (!empty($maintenance) && $maintenance == 2)
59
	display_maintenance_message();
60
61
// Create a variable to store some SMF specific functions in.
62
$smcFunc = array();
63
64
// Initiate the database connection and define some database functions to use.
65
loadDatabase();
66
67
// Load the settings from the settings table, and perform operations like optimizing.
68
$context = array();
69
reloadSettings();
70
// Clean the request variables, add slashes, etc.
71
cleanRequest();
72
73
// Seed the random generator.
74
if (empty($modSettings['rand_seed']) || random_int(1, 250) == 69)
75
	smf_seed_generator();
76
77
// Before we get carried away, are we doing a scheduled task? If so save CPU cycles by jumping out!
78
if (isset($_GET['scheduled']))
79
{
80
	require_once($sourcedir . '/ScheduledTasks.php');
81
	AutoTask();
82
}
83
84
// And important includes.
85
require_once($sourcedir . '/Session.php');
86
require_once($sourcedir . '/Errors.php');
87
require_once($sourcedir . '/Logging.php');
88
require_once($sourcedir . '/Security.php');
89
require_once($sourcedir . '/Class-BrowserDetect.php');
90
91
// Check if compressed output is enabled, supported, and not already being done.
92
if (!empty($modSettings['enableCompressedOutput']) && !headers_sent())
93
{
94
	// If zlib is being used, turn off output compression.
95 View Code Duplication
	if (ini_get('zlib.output_compression') >= 1 || ini_get('output_handler') == 'ob_gzhandler')
96
		$modSettings['enableCompressedOutput'] = '0';
97
	else
98
	{
99
		ob_end_clean();
100
		ob_start('ob_gzhandler');
101
	}
102
}
103
104
/**
105
 * An autoloader for certain classes.
106
 *
107
 * @param string $class The fully-qualified class name.
108
 */
109 View Code Duplication
spl_autoload_register(function ($class) use ($sourcedir)
110
{
111
	$classMap = array(
112
		'ReCaptcha\\' => 'ReCaptcha/',
113
		'MatthiasMullie\\Minify\\' => 'minify/src/',
114
		'MatthiasMullie\\PathConverter\\' => 'minify/path-converter/src/',
115
	);
116
117
	// Do any third-party scripts want in on the fun?
118
	call_integration_hook('integrate_autoload', array(&$classMap));
119
120
	foreach ($classMap as $prefix => $dirName)
121
	{
122
		// does the class use the namespace prefix?
123
		$len = strlen($prefix);
124
		if (strncmp($prefix, $class, $len) !== 0)
125
		{
126
			continue;
127
		}
128
129
		// get the relative class name
130
		$relativeClass = substr($class, $len);
131
132
		// replace the namespace prefix with the base directory, replace namespace
133
		// separators with directory separators in the relative class name, append
134
		// with .php
135
		$fileName = $dirName . strtr($relativeClass, '\\', '/') . '.php';
136
137
		// if the file exists, require it
138
		if (file_exists($fileName = $sourcedir . '/' . $fileName))
139
		{
140
			require_once $fileName;
141
142
			return;
143
		}
144
	}
145
});
146
147
// Register an error handler.
148
set_error_handler('smf_error_handler');
149
150
// Start the session. (assuming it hasn't already been.)
151
loadSession();
152
153
// What function shall we execute? (done like this for memory's sake.)
154
call_user_func(smf_main());
155
156
// Call obExit specially; we're coming from the main area ;).
157
obExit(null, null, true);
158
159
/**
160
 * The main dispatcher.
161
 * This delegates to each area.
162
 * @return array|string|void An array containing the file to include and name of function to call, the name of a function to call or dies with a fatal_lang_error if we couldn't find anything to do.
0 ignored issues
show
Should the return type not be null|string|array|boolean? Also, consider making the array more specific, something like array<String>, or String[].

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.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
163
 */
164
function smf_main()
165
{
166
	global $modSettings, $settings, $user_info, $board, $topic;
167
	global $board_info, $maintenance, $sourcedir;
168
169
	// Special case: session keep-alive, output a transparent pixel.
170
	if (isset($_GET['action']) && $_GET['action'] == 'keepalive')
171
	{
172
		header('content-type: image/gif');
173
		die("\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B");
174
	}
175
176
	// We should set our security headers now.
177
	frameOptionsHeader();
178
179
	// Load the user's cookie (or set as guest) and load their settings.
180
	loadUserSettings();
181
182
	// Load the current board's information.
183
	loadBoard();
184
185
	// Load the current user's permissions.
186
	loadPermissions();
187
188
	// Attachments don't require the entire theme to be loaded.
189
	if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'dlattach')
190
		detectBrowser();
191
	// Load the current theme.  (note that ?theme=1 will also work, may be used for guest theming.)
192
	else
193
		loadTheme();
194
195
	// Check if the user should be disallowed access.
196
	is_not_banned();
197
198
	// If we are in a topic and don't have permission to approve it then duck out now.
199
	if (!empty($topic) && empty($board_info['cur_topic_approved']) && !allowedTo('approve_posts') && ($user_info['id'] != $board_info['cur_topic_starter'] || $user_info['is_guest']))
200
		fatal_lang_error('not_a_topic', false);
201
202
	$no_stat_actions = array('clock', 'dlattach', 'findmember', 'jsoption', 'likes', 'loadeditorlocale', 'modifycat', 'requestmembers', 'smstats', 'suggest', 'about:unknown', '.xml', 'xmlhttp', 'verificationcode', 'viewquery', 'viewsmfile');
203
	call_integration_hook('integrate_pre_log_stats', array(&$no_stat_actions));
204
	// Do some logging, unless this is an attachment, avatar, toggle of editor buttons, theme option, XML feed etc.
205
	if (empty($_REQUEST['action']) || !in_array($_REQUEST['action'], $no_stat_actions))
206
	{
207
		// Log this user as online.
208
		writeLog();
209
210
		// Track forum statistics and hits...?
211
		if (!empty($modSettings['hitStats']))
212
			trackStats(array('hits' => '+'));
213
	}
214
	unset($no_stat_actions);
215
216
	// Make sure that our scheduled tasks have been running as intended
217
	check_cron();
218
219
	// Is the forum in maintenance mode? (doesn't apply to administrators.)
220
	if (!empty($maintenance) && !allowedTo('admin_forum'))
221
	{
222
		// You can only login.... otherwise, you're getting the "maintenance mode" display.
223
		if (isset($_REQUEST['action']) && (in_array($_REQUEST['action'], array('login2', 'logintfa', 'logout'))))
224
		{
225
			require_once($sourcedir . '/LogInOut.php');
226
			return ($_REQUEST['action'] == 'login2' ? 'Login2' : ($_REQUEST['action'] == 'logintfa' ? 'LoginTFA' : 'Logout'));
227
		}
228
		// Don't even try it, sonny.
229
		else
230
			return 'InMaintenance';
231
	}
232
	// If guest access is off, a guest can only do one of the very few following actions.
233
	elseif (empty($modSettings['allow_guestAccess']) && $user_info['is_guest'] && (!isset($_REQUEST['action']) || !in_array($_REQUEST['action'], array('coppa', 'login', 'login2', 'logintfa', 'reminder', 'activate', 'help', 'helpadmin', 'smstats', 'verificationcode', 'signup', 'signup2'))))
234
		return 'KickGuest';
235
	elseif (empty($_REQUEST['action']))
236
	{
237
		// Action and board are both empty... BoardIndex! Unless someone else wants to do something different.
238
		if (empty($board) && empty($topic))
239
		{
240
			if (!empty($modSettings['integrate_default_action']))
241
			{
242
				$defaultAction = explode(',', $modSettings['integrate_default_action']);
243
244
				// Sorry, only one default action is needed.
245
				$defaultAction = $defaultAction[0];
246
247
				$call = call_helper($defaultAction, true);
248
249
				if (!empty($call))
250
					return $call;
251
			}
252
253
			// No default action huh? then go to our good old BoardIndex.
254
			else
255
			{
256
				require_once($sourcedir . '/BoardIndex.php');
257
258
				return 'BoardIndex';
259
			}
260
		}
261
262
		// Topic is empty, and action is empty.... MessageIndex!
263
		elseif (empty($topic))
264
		{
265
			require_once($sourcedir . '/MessageIndex.php');
266
			return 'MessageIndex';
267
		}
268
269
		// Board is not empty... topic is not empty... action is empty.. Display!
270
		else
271
		{
272
			require_once($sourcedir . '/Display.php');
273
			return 'Display';
274
		}
275
	}
276
277
	// Here's the monstrous $_REQUEST['action'] array - $_REQUEST['action'] => array($file, $function).
278
	$actionArray = array(
279
		'activate' => array('Register.php', 'Activate'),
280
		'admin' => array('Admin.php', 'AdminMain'),
281
		'announce' => array('Post.php', 'AnnounceTopic'),
282
		'attachapprove' => array('ManageAttachments.php', 'ApproveAttach'),
283
		'buddy' => array('Subs-Members.php', 'BuddyListToggle'),
284
		'calendar' => array('Calendar.php', 'CalendarMain'),
285
		'clock' => array('Calendar.php', 'clock'),
286
		'coppa' => array('Register.php', 'CoppaForm'),
287
		'credits' => array('Who.php', 'Credits'),
288
		'deletemsg' => array('RemoveTopic.php', 'DeleteMessage'),
289
		'dlattach' => array('ShowAttachments.php', 'showAttachment'),
290
		'editpoll' => array('Poll.php', 'EditPoll'),
291
		'editpoll2' => array('Poll.php', 'EditPoll2'),
292
		'findmember' => array('Subs-Auth.php', 'JSMembers'),
293
		'groups' => array('Groups.php', 'Groups'),
294
		'help' => array('Help.php', 'ShowHelp'),
295
		'helpadmin' => array('Help.php', 'ShowAdminHelp'),
296
		'jsmodify' => array('Post.php', 'JavaScriptModify'),
297
		'jsoption' => array('Themes.php', 'SetJavaScript'),
298
		'likes' => array('Likes.php', 'Likes::call#'),
299
		'loadeditorlocale' => array('Subs-Editor.php', 'loadLocale'),
300
		'lock' => array('Topic.php', 'LockTopic'),
301
		'lockvoting' => array('Poll.php', 'LockVoting'),
302
		'login' => array('LogInOut.php', 'Login'),
303
		'login2' => array('LogInOut.php', 'Login2'),
304
		'logintfa' => array('LogInOut.php', 'LoginTFA'),
305
		'logout' => array('LogInOut.php', 'Logout'),
306
		'markasread' => array('Subs-Boards.php', 'MarkRead'),
307
		'mergetopics' => array('SplitTopics.php', 'MergeTopics'),
308
		'mlist' => array('Memberlist.php', 'Memberlist'),
309
		'moderate' => array('ModerationCenter.php', 'ModerationMain'),
310
		'modifycat' => array('ManageBoards.php', 'ModifyCat'),
311
		'movetopic' => array('MoveTopic.php', 'MoveTopic'),
312
		'movetopic2' => array('MoveTopic.php', 'MoveTopic2'),
313
		'notify' => array('Notify.php', 'Notify'),
314
		'notifyboard' => array('Notify.php', 'BoardNotify'),
315
		'notifytopic' => array('Notify.php', 'TopicNotify'),
316
		'pm' => array('PersonalMessage.php', 'MessageMain'),
317
		'post' => array('Post.php', 'Post'),
318
		'post2' => array('Post.php', 'Post2'),
319
		'printpage' => array('Printpage.php', 'PrintTopic'),
320
		'profile' => array('Profile.php', 'ModifyProfile'),
321
		'quotefast' => array('Post.php', 'QuoteFast'),
322
		'quickmod' => array('MessageIndex.php', 'QuickModeration'),
323
		'quickmod2' => array('Display.php', 'QuickInTopicModeration'),
324
		'recent' => array('Recent.php', 'RecentPosts'),
325
		'reminder' => array('Reminder.php', 'RemindMe'),
326
		'removepoll' => array('Poll.php', 'RemovePoll'),
327
		'removetopic2' => array('RemoveTopic.php', 'RemoveTopic2'),
328
		'reporttm' => array('ReportToMod.php', 'ReportToModerator'),
329
		'requestmembers' => array('Subs-Auth.php', 'RequestMembers'),
330
		'restoretopic' => array('RemoveTopic.php', 'RestoreTopic'),
331
		'search' => array('Search.php', 'PlushSearch1'),
332
		'search2' => array('Search.php', 'PlushSearch2'),
333
		'sendactivation' => array('Register.php', 'SendActivation'),
334
		'signup' => array('Register.php', 'Register'),
335
		'signup2' => array('Register.php', 'Register2'),
336
		'smstats' => array('Stats.php', 'SMStats'),
337
		'suggest' => array('Subs-Editor.php', 'AutoSuggestHandler'),
338
		'spellcheck' => array('Subs-Post.php', 'SpellCheck'),
339
		'splittopics' => array('SplitTopics.php', 'SplitTopics'),
340
		'stats' => array('Stats.php', 'DisplayStats'),
341
		'sticky' => array('Topic.php', 'Sticky'),
342
		'theme' => array('Themes.php', 'ThemesMain'),
343
		'trackip' => array('Profile-View.php', 'trackIP'),
344
		'about:unknown' => array('Likes.php', 'BookOfUnknown'),
345
		'unread' => array('Recent.php', 'UnreadTopics'),
346
		'unreadreplies' => array('Recent.php', 'UnreadTopics'),
347
		'uploadAttach' => array('Attachments.php', 'Attachments::call#'),
348
		'verificationcode' => array('Register.php', 'VerificationCode'),
349
		'viewprofile' => array('Profile.php', 'ModifyProfile'),
350
		'vote' => array('Poll.php', 'Vote'),
351
		'viewquery' => array('ViewQuery.php', 'ViewQuery'),
352
		'viewsmfile' => array('Admin.php', 'DisplayAdminFile'),
353
		'who' => array('Who.php', 'Who'),
354
		'.xml' => array('News.php', 'ShowXmlFeed'),
355
		'xmlhttp' => array('Xml.php', 'XMLhttpMain'),
356
	);
357
358
	// Allow modifying $actionArray easily.
359
	call_integration_hook('integrate_actions', array(&$actionArray));
360
361
	// Get the function and file to include - if it's not there, do the board index.
362
	if (!isset($_REQUEST['action']) || !isset($actionArray[$_REQUEST['action']]))
363
	{
364
		// Catch the action with the theme?
365
		if (!empty($settings['catch_action']))
366
		{
367
			require_once($sourcedir . '/Themes.php');
368
			return 'WrapAction';
369
		}
370
371
		if (!empty($modSettings['integrate_fallback_action']))
372
		{
373
			$fallbackAction = explode(',', $modSettings['integrate_fallback_action']);
374
375
			// Sorry, only one fallback action is needed.
376
			$fallbackAction = $fallbackAction[0];
377
378
			$call = call_helper($fallbackAction, true);
379
380
			if (!empty($call))
381
				return $call;
382
		}
383
384
		// No fallback action, huh?
385
		else
386
		{
387
			fatal_lang_error('not_found', false, array(), 404);
388
		}
389
	}
390
391
	// Otherwise, it was set - so let's go to that action.
392
	if (!empty($actionArray[$_REQUEST['action']][0]))
393
		require_once($sourcedir . '/' . $actionArray[$_REQUEST['action']][0]);
394
395
	// Do the right thing.
396
	return call_helper($actionArray[$_REQUEST['action']][1], true);
397
}
398
399
?>