Completed
Pull Request — release-2.1 (#4090)
by Rick
27:33
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 2017 Simple Machines and individual contributors
18
 * @license http://www.simplemachines.org/about/smf/license.php BSD
19
 *
20
 * @version 2.1 Beta 3
21
 */
22
23
$software_year = '2017';
24
$forum_version = 'SMF 2.1 Beta 3';
25
26
// Get everything started up...
27
define('SMF', 1);
28
if (function_exists('set_magic_quotes_runtime') && strnatcmp(phpversion(), '5.3.0') < 0)
29
	@set_magic_quotes_runtime(0);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
30
error_reporting(defined('E_STRICT') ? E_ALL | E_STRICT : E_ALL);
31
$time_start = microtime();
32
33
// This makes it so headers can be sent!
34
ob_start();
35
36
// Do some cleaning, just in case.
37 View Code Duplication
foreach (array('db_character_set', 'cachedir') as $variable)
38
	if (isset($GLOBALS[$variable]))
39
		unset($GLOBALS[$variable], $GLOBALS[$variable]);
40
41
// Load the settings...
42
require_once(dirname(__FILE__) . '/Settings.php');
43
44
// Make absolutely sure the cache directory is defined.
45 View Code Duplication
if ((empty($cachedir) || !file_exists($cachedir)) && file_exists($boarddir . '/cache'))
46
	$cachedir = $boarddir . '/cache';
47
48
// Without those we can't go anywhere
49
require_once($sourcedir . '/QueryString.php');
50
require_once($sourcedir . '/Subs.php');
51
require_once($sourcedir . '/Subs-Auth.php');
52
require_once($sourcedir . '/Errors.php');
53
require_once($sourcedir . '/Load.php');
54
55
// If $maintenance is set specifically to 2, then we're upgrading or something.
56
if (!empty($maintenance) && $maintenance == 2)
57
	display_maintenance_message();
58
59
// Create a variable to store some SMF specific functions in.
60
$smcFunc = array();
61
62
// Initiate the database connection and define some database functions to use.
63
loadDatabase();
64
65
// Load the settings from the settings table, and perform operations like optimizing.
66
$context = array();
67
reloadSettings();
68
// Clean the request variables, add slashes, etc.
69
cleanRequest();
70
71
// Seed the random generator.
72
if (empty($modSettings['rand_seed']) || mt_rand(1, 250) == 69)
73
	smf_seed_generator();
74
75
// Before we get carried away, are we doing a scheduled task? If so save CPU cycles by jumping out!
76
if (isset($_GET['scheduled']))
77
{
78
	require_once($sourcedir . '/ScheduledTasks.php');
79
	AutoTask();
80
}
81
82
// And important includes.
83
require_once($sourcedir . '/Session.php');
84
require_once($sourcedir . '/Errors.php');
85
require_once($sourcedir . '/Logging.php');
86
require_once($sourcedir . '/Security.php');
87
require_once($sourcedir . '/Class-BrowserDetect.php');
88
89
// Check if compressed output is enabled, supported, and not already being done.
90
if (!empty($modSettings['enableCompressedOutput']) && !headers_sent())
91
{
92
	// If zlib is being used, turn off output compression.
93
	if (ini_get('zlib.output_compression') >= 1 || ini_get('output_handler') == 'ob_gzhandler')
94
		$modSettings['enableCompressedOutput'] = '0';
95
	else
96
	{
97
		ob_end_clean();
98
		ob_start('ob_gzhandler');
99
	}
100
}
101
102
/**
103
 * An autoloader for certain classes.
104
 *
105
 * @param string $class The fully-qualified class name.
106
 */
107
spl_autoload_register(function ($class) use ($sourcedir)
108
{
109
	$classMap = array(
110
		'ReCaptcha\\' => 'ReCaptcha/',
111
		'MatthiasMullie\\Minify\\' => 'minify/src/',
112
		'MatthiasMullie\\PathConverter\\' => 'minify/path-converter/src/',
113
	);
114
115
	// Do any third-party scripts want in on the fun?
116
	call_integration_hook('integrate_autoload', array(&$classMap));
117
118
	foreach ($classMap as $prefix => $dirName)
119
	{
120
		// does the class use the namespace prefix?
121
		$len = strlen($prefix);
122
		if (strncmp($prefix, $class, $len) !== 0)
123
		{
124
			continue;
125
		}
126
127
		// get the relative class name
128
		$relativeClass = substr($class, $len);
129
130
		// replace the namespace prefix with the base directory, replace namespace
131
		// separators with directory separators in the relative class name, append
132
		// with .php
133
		$fileName = $dirName . strtr($relativeClass, '\\', '/') . '.php';
134
135
		// if the file exists, require it
136
		if (file_exists($fileName = $sourcedir . '/' . $fileName))
137
		{
138
			require_once $fileName;
139
140
			return;
141
		}
142
	}
143
});
144
145
// Register an error handler.
146
set_error_handler('smf_error_handler');
147
148
// Start the session. (assuming it hasn't already been.)
149
loadSession();
150
151
// What function shall we execute? (done like this for memory's sake.)
152
call_user_func(smf_main());
153
154
// Call obExit specially; we're coming from the main area ;).
155
obExit(null, null, true);
156
157
/**
158
 * The main dispatcher.
159
 * This delegates to each area.
160
 * @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.
161
 */
162
function smf_main()
163
{
164
	global $modSettings, $settings, $user_info, $board, $topic;
165
	global $board_info, $maintenance, $sourcedir;
166
167
	// Special case: session keep-alive, output a transparent pixel.
168
	if (isset($_GET['action']) && $_GET['action'] == 'keepalive')
169
	{
170
		header('Content-Type: image/gif');
171
		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");
172
	}
173
174
	// We should set our security headers now.
175
	frameOptionsHeader();
176
177
	// Load the user's cookie (or set as guest) and load their settings.
178
	loadUserSettings();
179
180
	// Load the current board's information.
181
	loadBoard();
182
183
	// Load the current user's permissions.
184
	loadPermissions();
185
186
	// Attachments don't require the entire theme to be loaded.
187
	if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'dlattach')
188
		detectBrowser();
189
	// Load the current theme.  (note that ?theme=1 will also work, may be used for guest theming.)
190
	else
191
		loadTheme();
192
193
	// Check if the user should be disallowed access.
194
	is_not_banned();
195
196
	// If we are in a topic and don't have permission to approve it then duck out now.
197
	if (!empty($topic) && empty($board_info['cur_topic_approved']) && !allowedTo('approve_posts') && ($user_info['id'] != $board_info['cur_topic_starter'] || $user_info['is_guest']))
198
		fatal_lang_error('not_a_topic', false);
199
200
	$no_stat_actions = array('clock', 'dlattach', 'findmember', 'jsoption', 'likes', 'loadeditorlocale', 'modifycat', 'requestmembers', 'smstats', 'suggest', 'about:unknown', '.xml', 'xmlhttp', 'verificationcode', 'viewquery', 'viewsmfile');
201
	call_integration_hook('integrate_pre_log_stats', array(&$no_stat_actions));
202
	// Do some logging, unless this is an attachment, avatar, toggle of editor buttons, theme option, XML feed etc.
203
	if (empty($_REQUEST['action']) || !in_array($_REQUEST['action'], $no_stat_actions))
204
	{
205
		// Log this user as online.
206
		writeLog();
207
208
		// Track forum statistics and hits...?
209
		if (!empty($modSettings['hitStats']))
210
			trackStats(array('hits' => '+'));
211
	}
212
	unset($no_stat_actions);
213
214
	// Is the forum in maintenance mode? (doesn't apply to administrators.)
215
	if (!empty($maintenance) && !allowedTo('admin_forum'))
216
	{
217
		// You can only login.... otherwise, you're getting the "maintenance mode" display.
218
		if (isset($_REQUEST['action']) && (in_array($_REQUEST['action'], array('login2', 'logintfa', 'logout'))))
219
		{
220
			require_once($sourcedir . '/LogInOut.php');
221
			return ($_REQUEST['action'] == 'login2' ? 'Login2' : ($_REQUEST['action'] == 'logintfa' ? 'LoginTFA' : 'Logout'));
222
		}
223
		// Don't even try it, sonny.
224
		else
225
			return 'InMaintenance';
226
	}
227
	// If guest access is off, a guest can only do one of the very few following actions.
228
	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'))))
229
		return 'KickGuest';
230
	elseif (empty($_REQUEST['action']))
231
	{
232
		// Action and board are both empty... BoardIndex! Unless someone else wants to do something different.
233
		if (empty($board) && empty($topic))
234
		{
235
			if (!empty($modSettings['integrate_default_action']))
236
			{
237
				$defaultAction = explode(',', $modSettings['integrate_default_action']);
238
239
				// Sorry, only one default action is needed.
240
				$defaultAction = $defaultAction[0];
241
242
				$call = call_helper($defaultAction, true);
243
244
				if (!empty($call))
245
					return $call;
246
			}
247
248
			// No default action huh? then go to our good old BoardIndex.
249
			else
250
			{
251
				require_once($sourcedir . '/BoardIndex.php');
252
253
				return 'BoardIndex';
254
			}
255
		}
256
257
		// Topic is empty, and action is empty.... MessageIndex!
258
		elseif (empty($topic))
259
		{
260
			require_once($sourcedir . '/MessageIndex.php');
261
			return 'MessageIndex';
262
		}
263
264
		// Board is not empty... topic is not empty... action is empty.. Display!
265
		else
266
		{
267
			require_once($sourcedir . '/Display.php');
268
			return 'Display';
269
		}
270
	}
271
272
	// Here's the monstrous $_REQUEST['action'] array - $_REQUEST['action'] => array($file, $function).
273
	$actionArray = array(
274
		'activate' => array('Register.php', 'Activate'),
275
		'admin' => array('Admin.php', 'AdminMain'),
276
		'announce' => array('Post.php', 'AnnounceTopic'),
277
		'attachapprove' => array('ManageAttachments.php', 'ApproveAttach'),
278
		'buddy' => array('Subs-Members.php', 'BuddyListToggle'),
279
		'calendar' => array('Calendar.php', 'CalendarMain'),
280
		'clock' => array('Calendar.php', 'clock'),
281
		'coppa' => array('Register.php', 'CoppaForm'),
282
		'credits' => array('Who.php', 'Credits'),
283
		'deletemsg' => array('RemoveTopic.php', 'DeleteMessage'),
284
		'dlattach' => array('ShowAttachments.php', 'showAttachment'),
285
		'editpoll' => array('Poll.php', 'EditPoll'),
286
		'editpoll2' => array('Poll.php', 'EditPoll2'),
287
		'findmember' => array('Subs-Auth.php', 'JSMembers'),
288
		'groups' => array('Groups.php', 'Groups'),
289
		'help' => array('Help.php', 'ShowHelp'),
290
		'helpadmin' => array('Help.php', 'ShowAdminHelp'),
291
		'jsmodify' => array('Post.php', 'JavaScriptModify'),
292
		'jsoption' => array('Themes.php', 'SetJavaScript'),
293
		'likes' => array('Likes.php', 'Likes::call#'),
294
		'loadeditorlocale' => array('Subs-Editor.php', 'loadLocale'),
295
		'lock' => array('Topic.php', 'LockTopic'),
296
		'lockvoting' => array('Poll.php', 'LockVoting'),
297
		'login' => array('LogInOut.php', 'Login'),
298
		'login2' => array('LogInOut.php', 'Login2'),
299
		'logintfa' => array('LogInOut.php', 'LoginTFA'),
300
		'logout' => array('LogInOut.php', 'Logout'),
301
		'markasread' => array('Subs-Boards.php', 'MarkRead'),
302
		'mergetopics' => array('SplitTopics.php', 'MergeTopics'),
303
		'mlist' => array('Memberlist.php', 'Memberlist'),
304
		'moderate' => array('ModerationCenter.php', 'ModerationMain'),
305
		'modifycat' => array('ManageBoards.php', 'ModifyCat'),
306
		'movetopic' => array('MoveTopic.php', 'MoveTopic'),
307
		'movetopic2' => array('MoveTopic.php', 'MoveTopic2'),
308
		'notify' => array('Notify.php', 'Notify'),
309
		'notifyboard' => array('Notify.php', 'BoardNotify'),
310
		'notifytopic' => array('Notify.php', 'TopicNotify'),
311
		'pm' => array('PersonalMessage.php', 'MessageMain'),
312
		'post' => array('Post.php', 'Post'),
313
		'post2' => array('Post.php', 'Post2'),
314
		'printpage' => array('Printpage.php', 'PrintTopic'),
315
		'profile' => array('Profile.php', 'ModifyProfile'),
316
		'quotefast' => array('Post.php', 'QuoteFast'),
317
		'quickmod' => array('MessageIndex.php', 'QuickModeration'),
318
		'quickmod2' => array('Display.php', 'QuickInTopicModeration'),
319
		'recent' => array('Recent.php', 'RecentPosts'),
320
		'reminder' => array('Reminder.php', 'RemindMe'),
321
		'removepoll' => array('Poll.php', 'RemovePoll'),
322
		'removetopic2' => array('RemoveTopic.php', 'RemoveTopic2'),
323
		'reporttm' => array('ReportToMod.php', 'ReportToModerator'),
324
		'requestmembers' => array('Subs-Auth.php', 'RequestMembers'),
325
		'restoretopic' => array('RemoveTopic.php', 'RestoreTopic'),
326
		'search' => array('Search.php', 'PlushSearch1'),
327
		'search2' => array('Search.php', 'PlushSearch2'),
328
		'sendactivation' => array('Register.php', 'SendActivation'),
329
		'signup' => array('Register.php', 'Register'),
330
		'signup2' => array('Register.php', 'Register2'),
331
		'smstats' => array('Stats.php', 'SMStats'),
332
		'suggest' => array('Subs-Editor.php', 'AutoSuggestHandler'),
333
		'spellcheck' => array('Subs-Post.php', 'SpellCheck'),
334
		'splittopics' => array('SplitTopics.php', 'SplitTopics'),
335
		'stats' => array('Stats.php', 'DisplayStats'),
336
		'sticky' => array('Topic.php', 'Sticky'),
337
		'theme' => array('Themes.php', 'ThemesMain'),
338
		'trackip' => array('Profile-View.php', 'trackIP'),
339
		'about:unknown' => array('Likes.php', 'BookOfUnknown'),
340
		'unread' => array('Recent.php', 'UnreadTopics'),
341
		'unreadreplies' => array('Recent.php', 'UnreadTopics'),
342
		'uploadAttach' => array('Attachments.php', 'Attachments::call#'),
343
		'verificationcode' => array('Register.php', 'VerificationCode'),
344
		'viewprofile' => array('Profile.php', 'ModifyProfile'),
345
		'vote' => array('Poll.php', 'Vote'),
346
		'viewquery' => array('ViewQuery.php', 'ViewQuery'),
347
		'viewsmfile' => array('Admin.php', 'DisplayAdminFile'),
348
		'who' => array('Who.php', 'Who'),
349
		'.xml' => array('News.php', 'ShowXmlFeed'),
350
		'xmlhttp' => array('Xml.php', 'XMLhttpMain'),
351
	);
352
353
	// Allow modifying $actionArray easily.
354
	call_integration_hook('integrate_actions', array(&$actionArray));
355
356
	// Get the function and file to include - if it's not there, do the board index.
357
	if (!isset($_REQUEST['action']) || !isset($actionArray[$_REQUEST['action']]))
358
	{
359
		// Catch the action with the theme?
360
		if (!empty($settings['catch_action']))
361
		{
362
			require_once($sourcedir . '/Themes.php');
363
			return 'WrapAction';
364
		}
365
366
		if (!empty($modSettings['integrate_fallback_action']))
367
		{
368
			$fallbackAction = explode(',', $modSettings['integrate_fallback_action']);
369
370
			// Sorry, only one fallback action is needed.
371
			$fallbackAction = $fallbackAction[0];
372
373
			$call = call_helper($fallbackAction, true);
374
375
			if (!empty($call))
376
				return $call;
377
		}
378
379
		// No fallback action, huh?
380
		else
381
		{
382
			fatal_lang_error('not_found', false, array(), 404);
383
		}
384
	}
385
386
	// Otherwise, it was set - so let's go to that action.
387
	require_once($sourcedir . '/' . $actionArray[$_REQUEST['action']][0]);
388
389
	// Do the right thing.
390
	return call_helper($actionArray[$_REQUEST['action']][1], true);
391
}
392
393
?>