Completed
Pull Request — release-2.1 (#4892)
by Mathias
08:19
created

index.php (4 issues)

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
// Without those we can't go anywhere
47
require_once($sourcedir . '/QueryString.php');
48
require_once($sourcedir . '/Subs.php');
49
require_once($sourcedir . '/Subs-Auth.php');
50
require_once($sourcedir . '/Errors.php');
51
require_once($sourcedir . '/Load.php');
52
53
// If $maintenance is set specifically to 2, then we're upgrading or something.
54
if (!empty($maintenance) && $maintenance == 2)
55
	display_maintenance_message();
56
57
// Create a variable to store some SMF specific functions in.
58
$smcFunc = array();
59
60
// Initiate the database connection and define some database functions to use.
61
loadDatabase();
62
63
// Load the settings from the settings table, and perform operations like optimizing.
64
$context = array();
65
reloadSettings();
66
// Clean the request variables, add slashes, etc.
67
cleanRequest();
68
69
// Seed the random generator.
70
if (empty($modSettings['rand_seed']) || mt_rand(1, 250) == 69)
71
	smf_seed_generator();
72
73
// Before we get carried away, are we doing a scheduled task? If so save CPU cycles by jumping out!
74
if (isset($_GET['scheduled']))
75
{
76
	require_once($sourcedir . '/ScheduledTasks.php');
77
	AutoTask();
78
}
79
80
// And important includes.
81
require_once($sourcedir . '/Session.php');
82
require_once($sourcedir . '/Errors.php');
83
require_once($sourcedir . '/Logging.php');
84
require_once($sourcedir . '/Security.php');
85
require_once($sourcedir . '/Class-BrowserDetect.php');
86
87
// Check if compressed output is enabled, supported, and not already being done.
88
if (!empty($modSettings['enableCompressedOutput']) && !headers_sent())
89
{
90
	// If zlib is being used, turn off output compression.
91 View Code Duplication
	if (ini_get('zlib.output_compression') >= 1 || ini_get('output_handler') == 'ob_gzhandler')
92
		$modSettings['enableCompressedOutput'] = '0';
93
	else
94
	{
95
		ob_end_clean();
96
		ob_start('ob_gzhandler');
97
	}
98
}
99
100
/**
101
 * An autoloader for certain classes.
102
 *
103
 * @param string $class The fully-qualified class name.
104
 */
105 View Code Duplication
spl_autoload_register(function ($class) use ($sourcedir)
106
{
107
	$classMap = array(
108
		'ReCaptcha\\' => 'ReCaptcha/',
109
		'MatthiasMullie\\Minify\\' => 'minify/src/',
110
		'MatthiasMullie\\PathConverter\\' => 'minify/path-converter/src/',
111
	);
112
113
	// Do any third-party scripts want in on the fun?
114
	call_integration_hook('integrate_autoload', array(&$classMap));
115
116
	foreach ($classMap as $prefix => $dirName)
117
	{
118
		// does the class use the namespace prefix?
119
		$len = strlen($prefix);
120
		if (strncmp($prefix, $class, $len) !== 0)
121
		{
122
			continue;
123
		}
124
125
		// get the relative class name
126
		$relativeClass = substr($class, $len);
127
128
		// replace the namespace prefix with the base directory, replace namespace
129
		// separators with directory separators in the relative class name, append
130
		// with .php
131
		$fileName = $dirName . strtr($relativeClass, '\\', '/') . '.php';
132
133
		// if the file exists, require it
134
		if (file_exists($fileName = $sourcedir . '/' . $fileName))
135
		{
136
			require_once $fileName;
137
138
			return;
139
		}
140
	}
141
});
142
143
// Register an error handler.
144
set_error_handler('smf_error_handler');
145
146
// Start the session. (assuming it hasn't already been.)
147
loadSession();
148
149
// What function shall we execute? (done like this for memory's sake.)
150
call_user_func(smf_main());
151
152
// Call obExit specially; we're coming from the main area ;).
153
obExit(null, null, true);
154
155
/**
156
 * The main dispatcher.
157
 * This delegates to each area.
158
 * @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...
159
 */
160
function smf_main()
161
{
162
	global $modSettings, $settings, $user_info, $board, $topic;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
163
	global $board_info, $maintenance, $sourcedir;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
164
165
	// Special case: session keep-alive, output a transparent pixel.
166
	if (isset($_GET['action']) && $_GET['action'] == 'keepalive')
167
	{
168
		header('content-type: image/gif');
169
		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");
170
	}
171
172
	// We should set our security headers now.
173
	frameOptionsHeader();
174
175
	// Load the user's cookie (or set as guest) and load their settings.
176
	loadUserSettings();
177
178
	// Load the current board's information.
179
	loadBoard();
180
181
	// Load the current user's permissions.
182
	loadPermissions();
183
184
	// Attachments don't require the entire theme to be loaded.
185
	if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'dlattach')
186
		detectBrowser();
187
	// Load the current theme.  (note that ?theme=1 will also work, may be used for guest theming.)
188
	else
189
		loadTheme();
190
191
	// Check if the user should be disallowed access.
192
	is_not_banned();
193
194
	// If we are in a topic and don't have permission to approve it then duck out now.
195
	if (!empty($topic) && empty($board_info['cur_topic_approved']) && !allowedTo('approve_posts') && ($user_info['id'] != $board_info['cur_topic_starter'] || $user_info['is_guest']))
196
		fatal_lang_error('not_a_topic', false);
197
198
	$no_stat_actions = array('clock', 'dlattach', 'findmember', 'jsoption', 'likes', 'loadeditorlocale', 'modifycat', 'requestmembers', 'smstats', 'suggest', 'about:unknown', '.xml', 'xmlhttp', 'verificationcode', 'viewquery', 'viewsmfile');
199
	call_integration_hook('integrate_pre_log_stats', array(&$no_stat_actions));
200
	// Do some logging, unless this is an attachment, avatar, toggle of editor buttons, theme option, XML feed etc.
201
	if (empty($_REQUEST['action']) || !in_array($_REQUEST['action'], $no_stat_actions))
202
	{
203
		// Log this user as online.
204
		writeLog();
205
206
		// Track forum statistics and hits...?
207
		if (!empty($modSettings['hitStats']))
208
			trackStats(array('hits' => '+'));
209
	}
210
	unset($no_stat_actions);
211
212
	// Make sure that our scheduled tasks have been running as intended
213
	check_cron();
214
215
	// Privacy logic only when enabled, got no valid version, try not to call the policy data
216
	if (!empty($modSettings['enable_policy_function']) && !$user_info['is_guest'])
217
	{
218
		global $options;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
219
		if (empty($options['policy_isvalid']) && 
220
				(empty($_REQUEST['area']) || (!empty($_REQUEST['area']) && $_REQUEST['area'] != 'getpolicydata')) )
221
			$show_privacy = true;
222
	}
223
224
	// Is the forum in maintenance mode? (doesn't apply to administrators.)
225
	if (!empty($maintenance) && !allowedTo('admin_forum'))
226
	{
227
		// You can only login.... otherwise, you're getting the "maintenance mode" display.
228
		if (isset($_REQUEST['action']) && (in_array($_REQUEST['action'], array('login2', 'logintfa', 'logout'))))
229
		{
230
			require_once($sourcedir . '/LogInOut.php');
231
			return ($_REQUEST['action'] == 'login2' ? 'Login2' : ($_REQUEST['action'] == 'logintfa' ? 'LoginTFA' : 'Logout'));
232
		}
233
		// Don't even try it, sonny.
234
		else
235
			return 'InMaintenance';
236
	}
237
	// If guest access is off, a guest can only do one of the very few following actions.
238
	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'))))
239
		return 'KickGuest';
240
	elseif (!empty($show_privacy))
241
		redirectexit('action=profile;area=getpolicydata;u=' . $user_info['id']);
242
	elseif (empty($_REQUEST['action']))
243
	{
244
		// Action and board are both empty... BoardIndex! Unless someone else wants to do something different.
245
		if (empty($board) && empty($topic))
246
		{
247
			if (!empty($modSettings['integrate_default_action']))
248
			{
249
				$defaultAction = explode(',', $modSettings['integrate_default_action']);
250
251
				// Sorry, only one default action is needed.
252
				$defaultAction = $defaultAction[0];
253
254
				$call = call_helper($defaultAction, true);
255
256
				if (!empty($call))
257
					return $call;
258
			}
259
260
			// No default action huh? then go to our good old BoardIndex.
261
			else
262
			{
263
				require_once($sourcedir . '/BoardIndex.php');
264
265
				return 'BoardIndex';
266
			}
267
		}
268
269
		// Topic is empty, and action is empty.... MessageIndex!
270
		elseif (empty($topic))
271
		{
272
			require_once($sourcedir . '/MessageIndex.php');
273
			return 'MessageIndex';
274
		}
275
276
		// Board is not empty... topic is not empty... action is empty.. Display!
277
		else
278
		{
279
			require_once($sourcedir . '/Display.php');
280
			return 'Display';
281
		}
282
	}
283
284
	// Here's the monstrous $_REQUEST['action'] array - $_REQUEST['action'] => array($file, $function).
285
	$actionArray = array(
286
		'activate' => array('Register.php', 'Activate'),
287
		'admin' => array('Admin.php', 'AdminMain'),
288
		'announce' => array('Post.php', 'AnnounceTopic'),
289
		'attachapprove' => array('ManageAttachments.php', 'ApproveAttach'),
290
		'buddy' => array('Subs-Members.php', 'BuddyListToggle'),
291
		'calendar' => array('Calendar.php', 'CalendarMain'),
292
		'clock' => array('Calendar.php', 'clock'),
293
		'coppa' => array('Register.php', 'CoppaForm'),
294
		'credits' => array('Who.php', 'Credits'),
295
		'deletemsg' => array('RemoveTopic.php', 'DeleteMessage'),
296
		'dlattach' => array('ShowAttachments.php', 'showAttachment'),
297
		'editpoll' => array('Poll.php', 'EditPoll'),
298
		'editpoll2' => array('Poll.php', 'EditPoll2'),
299
		'findmember' => array('Subs-Auth.php', 'JSMembers'),
300
		'groups' => array('Groups.php', 'Groups'),
301
		'help' => array('Help.php', 'ShowHelp'),
302
		'helpadmin' => array('Help.php', 'ShowAdminHelp'),
303
		'jsmodify' => array('Post.php', 'JavaScriptModify'),
304
		'jsoption' => array('Themes.php', 'SetJavaScript'),
305
		'likes' => array('Likes.php', 'Likes::call#'),
306
		'loadeditorlocale' => array('Subs-Editor.php', 'loadLocale'),
307
		'lock' => array('Topic.php', 'LockTopic'),
308
		'lockvoting' => array('Poll.php', 'LockVoting'),
309
		'login' => array('LogInOut.php', 'Login'),
310
		'login2' => array('LogInOut.php', 'Login2'),
311
		'logintfa' => array('LogInOut.php', 'LoginTFA'),
312
		'logout' => array('LogInOut.php', 'Logout'),
313
		'markasread' => array('Subs-Boards.php', 'MarkRead'),
314
		'mergetopics' => array('SplitTopics.php', 'MergeTopics'),
315
		'mlist' => array('Memberlist.php', 'Memberlist'),
316
		'moderate' => array('ModerationCenter.php', 'ModerationMain'),
317
		'modifycat' => array('ManageBoards.php', 'ModifyCat'),
318
		'movetopic' => array('MoveTopic.php', 'MoveTopic'),
319
		'movetopic2' => array('MoveTopic.php', 'MoveTopic2'),
320
		'notify' => array('Notify.php', 'Notify'),
321
		'notifyboard' => array('Notify.php', 'BoardNotify'),
322
		'notifytopic' => array('Notify.php', 'TopicNotify'),
323
		'pm' => array('PersonalMessage.php', 'MessageMain'),
324
		'post' => array('Post.php', 'Post'),
325
		'post2' => array('Post.php', 'Post2'),
326
		'printpage' => array('Printpage.php', 'PrintTopic'),
327
		'profile' => array('Profile.php', 'ModifyProfile'),
328
		'quotefast' => array('Post.php', 'QuoteFast'),
329
		'quickmod' => array('MessageIndex.php', 'QuickModeration'),
330
		'quickmod2' => array('Display.php', 'QuickInTopicModeration'),
331
		'recent' => array('Recent.php', 'RecentPosts'),
332
		'reminder' => array('Reminder.php', 'RemindMe'),
333
		'removepoll' => array('Poll.php', 'RemovePoll'),
334
		'removetopic2' => array('RemoveTopic.php', 'RemoveTopic2'),
335
		'reporttm' => array('ReportToMod.php', 'ReportToModerator'),
336
		'requestmembers' => array('Subs-Auth.php', 'RequestMembers'),
337
		'restoretopic' => array('RemoveTopic.php', 'RestoreTopic'),
338
		'search' => array('Search.php', 'PlushSearch1'),
339
		'search2' => array('Search.php', 'PlushSearch2'),
340
		'sendactivation' => array('Register.php', 'SendActivation'),
341
		'signup' => array('Register.php', 'Register'),
342
		'signup2' => array('Register.php', 'Register2'),
343
		'smstats' => array('Stats.php', 'SMStats'),
344
		'suggest' => array('Subs-Editor.php', 'AutoSuggestHandler'),
345
		'spellcheck' => array('Subs-Post.php', 'SpellCheck'),
346
		'splittopics' => array('SplitTopics.php', 'SplitTopics'),
347
		'stats' => array('Stats.php', 'DisplayStats'),
348
		'sticky' => array('Topic.php', 'Sticky'),
349
		'theme' => array('Themes.php', 'ThemesMain'),
350
		'trackip' => array('Profile-View.php', 'trackIP'),
351
		'about:unknown' => array('Likes.php', 'BookOfUnknown'),
352
		'unread' => array('Recent.php', 'UnreadTopics'),
353
		'unreadreplies' => array('Recent.php', 'UnreadTopics'),
354
		'uploadAttach' => array('Attachments.php', 'Attachments::call#'),
355
		'verificationcode' => array('Register.php', 'VerificationCode'),
356
		'viewprofile' => array('Profile.php', 'ModifyProfile'),
357
		'vote' => array('Poll.php', 'Vote'),
358
		'viewquery' => array('ViewQuery.php', 'ViewQuery'),
359
		'viewsmfile' => array('Admin.php', 'DisplayAdminFile'),
360
		'who' => array('Who.php', 'Who'),
361
		'.xml' => array('News.php', 'ShowXmlFeed'),
362
		'xmlhttp' => array('Xml.php', 'XMLhttpMain'),
363
	);
364
365
	// Allow modifying $actionArray easily.
366
	call_integration_hook('integrate_actions', array(&$actionArray));
367
368
	// Get the function and file to include - if it's not there, do the board index.
369
	if (!isset($_REQUEST['action']) || !isset($actionArray[$_REQUEST['action']]))
370
	{
371
		// Catch the action with the theme?
372
		if (!empty($settings['catch_action']))
373
		{
374
			require_once($sourcedir . '/Themes.php');
375
			return 'WrapAction';
376
		}
377
378
		if (!empty($modSettings['integrate_fallback_action']))
379
		{
380
			$fallbackAction = explode(',', $modSettings['integrate_fallback_action']);
381
382
			// Sorry, only one fallback action is needed.
383
			$fallbackAction = $fallbackAction[0];
384
385
			$call = call_helper($fallbackAction, true);
386
387
			if (!empty($call))
388
				return $call;
389
		}
390
391
		// No fallback action, huh?
392
		else
393
		{
394
			fatal_lang_error('not_found', false, array(), 404);
395
		}
396
	}
397
398
	// Otherwise, it was set - so let's go to that action.
399
	if (!empty($actionArray[$_REQUEST['action']][0]))
400
		require_once($sourcedir . '/' . $actionArray[$_REQUEST['action']][0]);
401
402
	// Do the right thing.
403
	return call_helper($actionArray[$_REQUEST['action']][1], true);
404
}
405
406
?>