Completed
Push — release-2.1 ( 6f6d35...abeae7 )
by Mathias
08:46
created

Profile.php ➔ ModifyProfile()   F

Complexity

Conditions 104
Paths 0

Size

Total Lines 714
Code Lines 438

Duplication

Lines 9
Ratio 1.26 %

Importance

Changes 0
Metric Value
cc 104
eloc 438
nc 0
nop 1
dl 9
loc 714
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file has the primary job of showing and editing people's profiles.
5
 * It also allows the user to change some of their or another's preferences,
6
 * and such things.
7
 *
8
 * Simple Machines Forum (SMF)
9
 *
10
 * @package SMF
11
 * @author Simple Machines http://www.simplemachines.org
12
 * @copyright 2017 Simple Machines and individual contributors
13
 * @license http://www.simplemachines.org/about/smf/license.php BSD
14
 *
15
 * @version 2.1 Beta 4
16
 */
17
18
if (!defined('SMF'))
19
	die('No direct access...');
20
21
/**
22
 * The main designating function for modifying profiles. Loads up info, determins what to do, etc.
23
 *
24
 * @param array $post_errors Any errors that occurred
25
 */
26
function ModifyProfile($post_errors = array())
27
{
28
	global $txt, $scripturl, $user_info, $context, $sourcedir, $user_profile, $cur_profile;
29
	global $modSettings, $memberContext, $profile_vars, $post_errors, $smcFunc;
30
31
	// Don't reload this as we may have processed error strings.
32
	if (empty($post_errors))
33
		loadLanguage('Profile+Drafts');
34
	loadTemplate('Profile');
35
36
	require_once($sourcedir . '/Subs-Menu.php');
37
38
	// Did we get the user by name...
39
	if (isset($_REQUEST['user']))
40
		$memberResult = loadMemberData($_REQUEST['user'], true, 'profile');
41
	// ... or by id_member?
42
	elseif (!empty($_REQUEST['u']))
43
		$memberResult = loadMemberData((int) $_REQUEST['u'], false, 'profile');
44
	// If it was just ?action=profile, edit your own profile, but only if you're not a guest.
45
	else
46
	{
47
		// Members only...
48
		is_not_guest();
49
		$memberResult = loadMemberData($user_info['id'], false, 'profile');
50
	}
51
52
	// Check if loadMemberData() has returned a valid result.
53
	if (!$memberResult)
0 ignored issues
show
Bug Best Practice introduced by
The expression $memberResult of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
54
		fatal_lang_error('not_a_user', false, 404);
0 ignored issues
show
Documentation introduced by
404 is of type integer, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
55
56
	// If all went well, we have a valid member ID!
57
	list ($memID) = $memberResult;
58
	$context['id_member'] = $memID;
59
	$cur_profile = $user_profile[$memID];
60
61
	// Let's have some information about this member ready, too.
62
	loadMemberContext($memID);
63
	$context['member'] = $memberContext[$memID];
64
65
	// Is this the profile of the user himself or herself?
66
	$context['user']['is_owner'] = $memID == $user_info['id'];
67
68
	// Group management isn't actually a permission. But we need it to be for this, so we need a phantom permission.
69
	// And we care about what the current user can do, not what the user whose profile it is.
70
	if ($user_info['mod_cache']['gq'] != '0=1')
71
		$user_info['permissions'][] = 'approve_group_requests';
72
73
	// If paid subscriptions are enabled, make sure we actually have at least one subscription available...
74
	$context['subs_available'] = false;
75
76
	if (!empty($modSettings['paid_enabled']))
77
	{
78
		$get_active_subs = $smcFunc['db_query']('', '
79
			SELECT COUNT(*)
80
			FROM {db_prefix}subscriptions
81
			WHERE active = {int:active}', array(
82
				'active' => 1,
83
			)
84
		);
85
86
		list ($num_subs) = $smcFunc['db_fetch_row']($get_active_subs);
87
88
		$context['subs_available'] = ($num_subs > 0);
89
90
		$smcFunc['db_free_result']($get_active_subs);
91
	}
92
93
	/* Define all the sections within the profile area!
94
		We start by defining the permission required - then SMF takes this and turns it into the relevant context ;)
95
		Possible fields:
96
			For Section:
97
				string $title:		Section title.
98
				array $areas:		Array of areas within this section.
99
100
			For Areas:
101
				string $label:		Text string that will be used to show the area in the menu.
102
				string $file:		Optional text string that may contain a file name that's needed for inclusion in order to display the area properly.
103
				string $custom_url:	Optional href for area.
104
				string $function:	Function to execute for this section. Can be a call to an static method: class::method
105
				string $class		If your function is a method, set the class field with your class's name and SMF will create a new instance for it.
106
				bool $enabled:		Should area be shown?
107
				string $sc:			Session check validation to do on save - note without this save will get unset - if set.
108
				bool $hidden:		Does this not actually appear on the menu?
109
				bool $password:		Whether to require the user's password in order to save the data in the area.
110
				array $subsections:	Array of subsections, in order of appearance.
111
				array $permission:	Array of permissions to determine who can access this area. Should contain arrays $own and $any.
112
	*/
113
	$profile_areas = array(
114
		'info' => array(
115
			'title' => $txt['profileInfo'],
116
			'areas' => array(
117
				'summary' => array(
118
					'label' => $txt['summary'],
119
					'file' => 'Profile-View.php',
120
					'function' => 'summary',
121
					'icon' => 'administration',
122
					'permission' => array(
123
						'own' => 'is_not_guest',
124
						'any' => 'profile_view',
125
					),
126
				),
127
				'popup' => array(
128
					'function' => 'profile_popup',
129
					'permission' => array(
130
						'own' => 'is_not_guest',
131
						'any' => array(),
132
					),
133
					'select' => 'summary',
134
				),
135
				'alerts_popup' => array(
136
					'function' => 'alerts_popup',
137
					'permission' => array(
138
						'own' => 'is_not_guest',
139
						'any' => array(),
140
					),
141
					'select' => 'summary',
142
				),
143
				'statistics' => array(
144
					'label' => $txt['statPanel'],
145
					'file' => 'Profile-View.php',
146
					'function' => 'statPanel',
147
					'icon' => 'stats',
148
					'permission' => array(
149
						'own' => 'is_not_guest',
150
						'any' => 'profile_view',
151
					),
152
				),
153
				'showposts' => array(
154
					'label' => $txt['showPosts'],
155
					'file' => 'Profile-View.php',
156
					'function' => 'showPosts',
157
					'icon' => 'posts',
158
					'subsections' => array(
159
						'messages' => array($txt['showMessages'], array('is_not_guest', 'profile_view')),
160
						'topics' => array($txt['showTopics'], array('is_not_guest', 'profile_view')),
161
						'unwatchedtopics' => array($txt['showUnwatched'], array('is_not_guest', 'profile_view'), 'enabled' => $context['user']['is_owner']),
162
						'attach' => array($txt['showAttachments'], array('is_not_guest', 'profile_view')),
163
					),
164
					'permission' => array(
165
						'own' => 'is_not_guest',
166
						'any' => 'profile_view',
167
					),
168
				),
169
				'showdrafts' => array(
170
					'label' => $txt['drafts_show'],
171
					'file' => 'Drafts.php',
172
					'function' => 'showProfileDrafts',
173
					'icon' => 'drafts',
174
					'enabled' => !empty($modSettings['drafts_post_enabled']) && $context['user']['is_owner'],
175
					'permission' => array(
176
						'own' => 'is_not_guest',
177
						'any' => array(),
178
					),
179
				),
180
				'showalerts' => array(
181
					'label' => $txt['alerts_show'],
182
					'file' => 'Profile-View.php',
183
					'function' => 'showAlerts',
184
					'icon' => 'alerts',
185
					'permission' => array(
186
						'own' => 'is_not_guest',
187
						'any' => array(),
188
					),
189
				),
190
				'permissions' => array(
191
					'label' => $txt['showPermissions'],
192
					'file' => 'Profile-View.php',
193
					'function' => 'showPermissions',
194
					'icon' => 'permissions',
195
					'permission' => array(
196
						'own' => 'manage_permissions',
197
						'any' => 'manage_permissions',
198
					),
199
				),
200
				'tracking' => array(
201
					'label' => $txt['trackUser'],
202
					'file' => 'Profile-View.php',
203
					'function' => 'tracking',
204
					'icon' => 'logs',
205
					'subsections' => array(
206
						'activity' => array($txt['trackActivity'], 'moderate_forum'),
207
						'ip' => array($txt['trackIP'], 'moderate_forum'),
208
						'edits' => array($txt['trackEdits'], 'moderate_forum', 'enabled' => !empty($modSettings['userlog_enabled'])),
209
						'groupreq' => array($txt['trackGroupRequests'], 'approve_group_requests', 'enabled' => !empty($modSettings['show_group_membership'])),
210
						'logins' => array($txt['trackLogins'], 'moderate_forum', 'enabled' => !empty($modSettings['loginHistoryDays'])),
211
					),
212
					'permission' => array(
213
						'own' => array('moderate_forum', 'approve_group_requests'),
214
						'any' => array('moderate_forum', 'approve_group_requests'),
215
					),
216
				),
217
				'viewwarning' => array(
218
					'label' => $txt['profile_view_warnings'],
219
					'enabled' => $modSettings['warning_settings'][0] == 1 && $cur_profile['warning'],
220
					'file' => 'Profile-View.php',
221
					'function' => 'viewWarning',
222
					'icon' => 'warning',
223
					'permission' => array(
224
						'own' => array('profile_warning_own', 'profile_warning_any', 'issue_warning', 'moderate_forum'),
225
						'any' => array('profile_warning_any', 'issue_warning', 'moderate_forum'),
226
					),
227
				),
228
			),
229
		),
230
		'edit_profile' => array(
231
			'title' => $txt['forumprofile'],
232
			'areas' => array(
233
				'account' => array(
234
					'label' => $txt['account'],
235
					'file' => 'Profile-Modify.php',
236
					'function' => 'account',
237
					'icon' => 'maintain',
238
					'enabled' => $context['user']['is_admin'] || ($cur_profile['id_group'] != 1 && !in_array(1, explode(',', $cur_profile['additional_groups']))),
239
					'sc' => 'post',
240
					'token' => 'profile-ac%u',
241
					'password' => true,
242
					'permission' => array(
243
						'own' => array('profile_identity_any', 'profile_identity_own', 'profile_password_any', 'profile_password_own', 'manage_membergroups'),
244
						'any' => array('profile_identity_any', 'profile_password_any', 'manage_membergroups'),
245
					),
246
				),
247
				'tfasetup' => array(
248
					'file' => 'Profile-Modify.php',
249
					'function' => 'tfasetup',
250
					'token' => 'profile-tfa%u',
251
					'enabled' => !empty($modSettings['tfa_mode']),
252
					'permission' => array(
253
						'own' => array('profile_password_own'),
254
						'any' => array('profile_password_any'),
255
					),
256
				),
257
				'forumprofile' => array(
258
					'label' => $txt['forumprofile'],
259
					'file' => 'Profile-Modify.php',
260
					'function' => 'forumProfile',
261
					'icon' => 'members',
262
					'sc' => 'post',
263
					'token' => 'profile-fp%u',
264
					'permission' => array(
265
						'own' => array('profile_forum_any', 'profile_forum_own'),
266
						'any' => array('profile_forum_any'),
267
					),
268
				),
269
				'theme' => array(
270
					'label' => $txt['theme'],
271
					'file' => 'Profile-Modify.php',
272
					'function' => 'theme',
273
					'icon' => 'features',
274
					'sc' => 'post',
275
					'token' => 'profile-th%u',
276
					'permission' => array(
277
						'own' => array('profile_extra_any', 'profile_extra_own'),
278
						'any' => array('profile_extra_any'),
279
					),
280
				),
281
				'notification' => array(
282
					'label' => $txt['notification'],
283
					'file' => 'Profile-Modify.php',
284
					'function' => 'notification',
285
					'icon' => 'mail',
286
					'sc' => 'post',
287
					//'token' => 'profile-nt%u', This is not checked here. We do it in the function itself - but if it was checked, this is what it'd be.
288
					'subsections' => array(
289
						'alerts' => array($txt['alert_prefs'], array('is_not_guest', 'profile_extra_any')),
290
						'topics' => array($txt['watched_topics'], array('is_not_guest', 'profile_extra_any')),
291
						'boards' => array($txt['watched_boards'], array('is_not_guest', 'profile_extra_any')),
292
					),
293
					'permission' => array(
294
						'own' => array('is_not_guest'),
295
						'any' => array('profile_extra_any'), // If you change this, update it in the functions themselves; we delegate all saving checks there.
296
					),
297
				),
298
				'ignoreboards' => array(
299
					'label' => $txt['ignoreboards'],
300
					'file' => 'Profile-Modify.php',
301
					'function' => 'ignoreboards',
302
					'icon' => 'boards',
303
					'enabled' => !empty($modSettings['allow_ignore_boards']),
304
					'sc' => 'post',
305
					'token' => 'profile-ib%u',
306
					'permission' => array(
307
						'own' => array('profile_extra_any', 'profile_extra_own'),
308
						'any' => array('profile_extra_any'),
309
					),
310
				),
311
				'lists' => array(
312
					'label' => $txt['editBuddyIgnoreLists'],
313
					'file' => 'Profile-Modify.php',
314
					'function' => 'editBuddyIgnoreLists',
315
					'icon' => 'frenemy',
316
					'enabled' => !empty($modSettings['enable_buddylist']) && $context['user']['is_owner'],
317
					'sc' => 'post',
318
					'subsections' => array(
319
						'buddies' => array($txt['editBuddies']),
320
						'ignore' => array($txt['editIgnoreList']),
321
					),
322
					'permission' => array(
323
						'own' => array('profile_extra_any', 'profile_extra_own'),
324
						'any' => array(),
325
					),
326
				),
327
				'groupmembership' => array(
328
					'label' => $txt['groupmembership'],
329
					'file' => 'Profile-Modify.php',
330
					'function' => 'groupMembership',
331
					'icon' => 'people',
332
					'enabled' => !empty($modSettings['show_group_membership']) && $context['user']['is_owner'],
333
					'sc' => 'request',
334
					'token' => 'profile-gm%u',
335
					'token_type' => 'request',
336
					'permission' => array(
337
						'own' => array('is_not_guest'),
338
						'any' => array('manage_membergroups'),
339
					),
340
				),
341
			),
342
		),
343
		'profile_action' => array(
344
			'title' => $txt['profileAction'],
345
			'areas' => array(
346
				'sendpm' => array(
347
					'label' => $txt['profileSendIm'],
348
					'custom_url' => $scripturl . '?action=pm;sa=send',
349
					'icon' => 'personal_message',
350
					'permission' => array(
351
						'own' => array(),
352
						'any' => array('pm_send'),
353
					),
354
				),
355
				'report' => array(
356
					'label' => $txt['report_profile'],
357
					'custom_url' => $scripturl . '?action=reporttm;' . $context['session_var'] . '=' . $context['session_id'],
358
					'icon' => 'warning',
359
					'permission' => array(
360
						'own' => array(),
361
						'any' => array('report_user'),
362
					),
363
				),
364
				'issuewarning' => array(
365
					'label' => $txt['profile_issue_warning'],
366
					'enabled' => $modSettings['warning_settings'][0] == 1,
367
					'file' => 'Profile-Actions.php',
368
					'function' => 'issueWarning',
369
					'icon' => 'warning',
370
					'token' => 'profile-iw%u',
371
					'permission' => array(
372
						'own' => array(),
373
						'any' => array('issue_warning'),
374
					),
375
				),
376
				'banuser' => array(
377
					'label' => $txt['profileBanUser'],
378
					'custom_url' => $scripturl . '?action=admin;area=ban;sa=add',
379
					'icon' => 'ban',
380
					'enabled' => $cur_profile['id_group'] != 1 && !in_array(1, explode(',', $cur_profile['additional_groups'])),
381
					'permission' => array(
382
						'own' => array(),
383
						'any' => array('manage_bans'),
384
					),
385
				),
386
				'subscriptions' => array(
387
					'label' => $txt['subscriptions'],
388
					'file' => 'Profile-Actions.php',
389
					'function' => 'subscriptions',
390
					'icon' => 'paid',
391
					'enabled' => !empty($modSettings['paid_enabled']) && $context['subs_available'],
392
					'permission' => array(
393
						'own' => array('is_not_guest'),
394
						'any' => array('moderate_forum'),
395
					),
396
				),
397
				'deleteaccount' => array(
398
					'label' => $txt['deleteAccount'],
399
					'file' => 'Profile-Actions.php',
400
					'function' => 'deleteAccount',
401
					'icon' => 'members_delete',
402
					'sc' => 'post',
403
					'token' => 'profile-da%u',
404
					'password' => true,
405
					'permission' => array(
406
						'own' => array('profile_remove_any', 'profile_remove_own'),
407
						'any' => array('profile_remove_any'),
408
					),
409
				),
410
				'activateaccount' => array(
411
					'file' => 'Profile-Actions.php',
412
					'function' => 'activateAccount',
413
					'icon' => 'regcenter',
414
					'sc' => 'get',
415
					'token' => 'profile-aa%u',
416
					'token_type' => 'get',
417
					'permission' => array(
418
						'own' => array(),
419
						'any' => array('moderate_forum'),
420
					),
421
				),
422
			),
423
		),
424
	);
425
426
	// Let them modify profile areas easily.
427
	call_integration_hook('integrate_pre_profile_areas', array(&$profile_areas));
428
429
	// Do some cleaning ready for the menu function.
430
	$context['password_areas'] = array();
431
	$current_area = isset($_REQUEST['area']) ? $_REQUEST['area'] : '';
432
433
	foreach ($profile_areas as $section_id => $section)
434
	{
435
		// Do a bit of spring cleaning so to speak.
436
		foreach ($section['areas'] as $area_id => $area)
437
		{
438
			// If it said no permissions that meant it wasn't valid!
439
			if (empty($area['permission'][$context['user']['is_owner'] ? 'own' : 'any']))
440
				$profile_areas[$section_id]['areas'][$area_id]['enabled'] = false;
441
			// Otherwise pick the right set.
442
			else
443
				$profile_areas[$section_id]['areas'][$area_id]['permission'] = $area['permission'][$context['user']['is_owner'] ? 'own' : 'any'];
444
445
			// Password required in most cases
446
			if (!empty($area['password']))
447
				$context['password_areas'][] = $area_id;
448
		}
449
	}
450
451
	// Is there an updated message to show?
452
	if (isset($_GET['updated']))
453
		$context['profile_updated'] = $txt['profile_updated_own'];
454
455
	// Set a few options for the menu.
456
	$menuOptions = array(
457
		'disable_url_session_check' => true,
458
		'current_area' => $current_area,
459
		'extra_url_parameters' => array(
460
			'u' => $context['id_member'],
461
		),
462
	);
463
464
	// Actually create the menu!
465
	$profile_include_data = createMenu($profile_areas, $menuOptions);
466
467
	// No menu means no access.
468 View Code Duplication
	if (!$profile_include_data && (!$user_info['is_guest'] || validateSession()))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
469
		fatal_lang_error('no_access', false);
470
471
	// Make a note of the Unique ID for this menu.
472
	$context['profile_menu_id'] = $context['max_menu_id'];
473
	$context['profile_menu_name'] = 'menu_data_' . $context['profile_menu_id'];
474
475
	// Set the selected item - now it's been validated.
476
	$current_area = $profile_include_data['current_area'];
477
	$current_sa = $profile_include_data['current_subsection'];
478
	$context['menu_item_selected'] = $current_area;
479
480
	// Before we go any further, let's work on the area we've said is valid. Note this is done here just in case we ever compromise the menu function in error!
481
	$context['completed_save'] = false;
482
	$context['do_preview'] = isset($_REQUEST['preview_signature']);
483
484
	$security_checks = array();
485
	$found_area = false;
486
	foreach ($profile_areas as $section_id => $section)
487
	{
488
		// Do a bit of spring cleaning so to speak.
489
		foreach ($section['areas'] as $area_id => $area)
490
		{
491
			// Is this our area?
492
			if ($current_area == $area_id)
493
			{
494
				// This can't happen - but is a security check.
495
				if ((isset($section['enabled']) && $section['enabled'] == false) || (isset($area['enabled']) && $area['enabled'] == false))
496
					fatal_lang_error('no_access', false);
497
498
				// Are we saving data in a valid area?
499
				if (isset($area['sc']) && (isset($_REQUEST['save']) || $context['do_preview']))
500
				{
501
					$security_checks['session'] = $area['sc'];
502
					$context['completed_save'] = true;
503
				}
504
505
				// Do we need to perform a token check?
506
				if (!empty($area['token']))
507
				{
508
					$security_checks[isset($_REQUEST['save']) ? 'validateToken' : 'needsToken'] = $area['token'];
509
					$token_name = $area['token'] !== true ? str_replace('%u', $context['id_member'], $area['token']) : 'profile-u' . $context['id_member'];
510
511
					$token_type = isset($area['token_type']) && in_array($area['token_type'], array('request', 'post', 'get')) ? $area['token_type'] : 'post';
512
				}
513
514
				// Does this require session validating?
515
				if (!empty($area['validate']) || (isset($_REQUEST['save']) && !$context['user']['is_owner']))
516
					$security_checks['validate'] = true;
517
518
				// Permissions for good measure.
519
				if (!empty($profile_include_data['permission']))
520
					$security_checks['permission'] = $profile_include_data['permission'];
521
522
				// Either way got something.
523
				$found_area = true;
524
			}
525
		}
526
	}
527
528
	// Oh dear, some serious security lapse is going on here... we'll put a stop to that!
529
	if (!$found_area)
530
		fatal_lang_error('no_access', false);
531
532
	// Release this now.
533
	unset($profile_areas);
534
535
	// Now the context is setup have we got any security checks to carry out additional to that above?
536
	if (isset($security_checks['session']))
537
		checkSession($security_checks['session']);
538
	if (isset($security_checks['validate']))
539
		validateSession();
540
	if (isset($security_checks['validateToken']))
541
		validateToken($token_name, $token_type);
0 ignored issues
show
Bug introduced by
The variable $token_name does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $token_type does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
542
	if (isset($security_checks['permission']))
543
		isAllowedTo($security_checks['permission']);
544
545
	// Create a token if needed.
546
	if (isset($security_checks['needsToken']) || isset($security_checks['validateToken']))
547
	{
548
		createToken($token_name, $token_type);
549
		$context['token_check'] = $token_name;
550
	}
551
552
	// File to include?
553
	if (isset($profile_include_data['file']))
554
		require_once($sourcedir . '/' . $profile_include_data['file']);
555
556
	// Build the link tree.
557
	$context['linktree'][] = array(
558
		'url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : ''),
559
		'name' => sprintf($txt['profile_of_username'], $context['member']['name']),
560
	);
561
562
	if (!empty($profile_include_data['label']))
563
		$context['linktree'][] = array(
564
			'url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : '') . ';area=' . $profile_include_data['current_area'],
565
			'name' => $profile_include_data['label'],
566
		);
567
568 View Code Duplication
	if (!empty($profile_include_data['current_subsection']) && $profile_include_data['subsections'][$profile_include_data['current_subsection']][0] != $profile_include_data['label'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
569
		$context['linktree'][] = array(
570
			'url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : '') . ';area=' . $profile_include_data['current_area'] . ';sa=' . $profile_include_data['current_subsection'],
571
			'name' => $profile_include_data['subsections'][$profile_include_data['current_subsection']][0],
572
		);
573
574
	// Set the template for this area and add the profile layer.
575
	$context['sub_template'] = $profile_include_data['function'];
576
	$context['template_layers'][] = 'profile';
577
578
	// All the subactions that require a user password in order to validate.
579
	$check_password = $context['user']['is_owner'] && in_array($profile_include_data['current_area'], $context['password_areas']);
580
	$context['require_password'] = $check_password;
581
582
	loadJavaScriptFile('profile.js', array('defer' => false), 'smf_profile');
583
584
	// These will get populated soon!
585
	$post_errors = array();
586
	$profile_vars = array();
587
588
	// Right - are we saving - if so let's save the old data first.
589
	if ($context['completed_save'])
590
	{
591
		// Clean up the POST variables.
592
		$_POST = htmltrim__recursive($_POST);
593
		$_POST = htmlspecialchars__recursive($_POST);
594
595
		if ($check_password)
596
		{
597
			// Check to ensure we're forcing SSL for authentication
598 View Code Duplication
			if (!empty($modSettings['force_ssl']) && empty($maintenance) && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != 'on'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
Bug introduced by
The variable $maintenance seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
599
				fatal_lang_error('login_ssl_required');
600
601
			// You didn't even enter a password!
602
			if (trim($_POST['oldpasswrd']) == '')
603
				$post_errors[] = 'no_password';
604
605
			// Since the password got modified due to all the $_POST cleaning, lets undo it so we can get the correct password
606
			$_POST['oldpasswrd'] = un_htmlspecialchars($_POST['oldpasswrd']);
607
608
			// Does the integration want to check passwords?
609
			$good_password = in_array(true, call_integration_hook('integrate_verify_password', array($cur_profile['member_name'], $_POST['oldpasswrd'], false)), true);
610
611
			// Bad password!!!
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
612
			if (!$good_password && !hash_verify_password($user_profile[$memID]['member_name'], un_htmlspecialchars(stripslashes($_POST['oldpasswrd'])), $user_info['passwd']))
613
				$post_errors[] = 'bad_password';
614
615
			// Warn other elements not to jump the gun and do custom changes!
616
			if (in_array('bad_password', $post_errors))
617
				$context['password_auth_failed'] = true;
618
		}
619
620
		// Change the IP address in the database.
621
		if ($context['user']['is_owner'])
622
			$profile_vars['member_ip'] = $user_info['ip'];
623
624
		// Now call the sub-action function...
625
		if ($current_area == 'activateaccount')
626
		{
627
			if (empty($post_errors))
628
				activateAccount($memID);
629
		}
630
		elseif ($current_area == 'deleteaccount')
631
		{
632
			if (empty($post_errors))
633
			{
634
				deleteAccount2($memID);
635
				redirectexit();
636
			}
637
		}
638
		elseif ($current_area == 'groupmembership' && empty($post_errors))
639
		{
640
			$msg = groupMembership2($profile_vars, $post_errors, $memID);
641
642
			// Whatever we've done, we have nothing else to do here...
643
			redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=groupmembership' . (!empty($msg) ? ';msg=' . $msg : ''));
644
		}
645
		elseif (in_array($current_area, array('account', 'forumprofile', 'theme')))
646
			saveProfileFields();
647
		else
648
		{
649
			$force_redirect = true;
650
			// Ensure we include this.
651
			require_once($sourcedir . '/Profile-Modify.php');
652
			saveProfileChanges($profile_vars, $post_errors, $memID);
653
		}
654
655
		call_integration_hook('integrate_profile_save', array(&$profile_vars, &$post_errors, $memID, $cur_profile, $current_area));
656
657
		// There was a problem, let them try to re-enter.
658
		if (!empty($post_errors))
659
		{
660
			// Load the language file so we can give a nice explanation of the errors.
661
			loadLanguage('Errors');
662
			$context['post_errors'] = $post_errors;
663
		}
664
		elseif (!empty($profile_vars))
665
		{
666
			// If we've changed the password, notify any integration that may be listening in.
667
			if (isset($profile_vars['passwd']))
668
				call_integration_hook('integrate_reset_pass', array($cur_profile['member_name'], $cur_profile['member_name'], $_POST['passwrd2']));
669
670
			updateMemberData($memID, $profile_vars);
671
672
			// What if this is the newest member?
673
			if ($modSettings['latestMember'] == $memID)
674
				updateStats('member');
675
			elseif (isset($profile_vars['real_name']))
676
				updateSettings(array('memberlist_updated' => time()));
677
678
			// If the member changed his/her birthdate, update calendar statistics.
679
			if (isset($profile_vars['birthdate']) || isset($profile_vars['real_name']))
680
				updateSettings(array(
681
					'calendar_updated' => time(),
682
				));
683
684
			// Anything worth logging?
685
			if (!empty($context['log_changes']) && !empty($modSettings['modlog_enabled']))
686
			{
687
				$log_changes = array();
688
				require_once($sourcedir . '/Logging.php');
689
				foreach ($context['log_changes'] as $k => $v)
690
					$log_changes[] = array(
691
						'action' => $k,
692
						'log_type' => 'user',
693
						'extra' => array_merge($v, array(
694
							'applicator' => $user_info['id'],
695
							'member_affected' => $memID,
696
						)),
697
					);
698
699
				logActions($log_changes);
700
			}
701
702
			// Have we got any post save functions to execute?
703
			if (!empty($context['profile_execute_on_save']))
704
				foreach ($context['profile_execute_on_save'] as $saveFunc)
705
					$saveFunc();
706
707
			// Let them know it worked!
708
			$context['profile_updated'] = $context['user']['is_owner'] ? $txt['profile_updated_own'] : sprintf($txt['profile_updated_else'], $cur_profile['member_name']);
709
710
			// Invalidate any cached data.
711
			cache_put_data('member_data-profile-' . $memID, null, 0);
712
		}
713
	}
714
715
	// Have some errors for some reason?
716
	if (!empty($post_errors))
717
	{
718
		// Set all the errors so the template knows what went wrong.
719
		foreach ($post_errors as $error_type)
720
			$context['modify_error'][$error_type] = true;
721
	}
722
	// If it's you then we should redirect upon save.
723
	elseif (!empty($profile_vars) && $context['user']['is_owner'] && !$context['do_preview'])
724
		redirectexit('action=profile;area=' . $current_area . (!empty($current_sa) ? ';sa=' . $current_sa : '') . ';updated');
725
	elseif (!empty($force_redirect))
726
		redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=' . $current_area);
727
728
729
	// Get the right callable.
730
	$call = call_helper($profile_include_data['function'], true);
731
732
	// Is it valid?
733
	if (!empty($call))
734
		call_user_func($call, $memID);
735
736
	// Set the page title if it's not already set...
737
	if (!isset($context['page_title']))
738
		$context['page_title'] = $txt['profile'] . (isset($txt[$current_area]) ? ' - ' . $txt[$current_area] : '');
739
}
740
741
/**
742
 * Set up the requirements for the profile popup - the area that is shown as the popup menu for the current user.
743
 *
744
 * @param int $memID The ID of the member
745
 */
746
function profile_popup($memID)
0 ignored issues
show
Unused Code introduced by
The parameter $memID is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
747
{
748
	global $context, $scripturl, $txt, $db_show_debug;
749
750
	// We do not want to output debug information here.
751
	$db_show_debug = false;
752
753
	// We only want to output our little layer here.
754
	$context['template_layers'] = array();
755
756
	// This list will pull from the master list wherever possible. Hopefully it should be clear what does what.
757
	$profile_items = array(
758
		array(
759
			'menu' => 'info',
760
			'area' => 'summary',
761
			'title' => $txt['popup_summary'],
762
		),
763
		array(
764
			'menu' => 'edit_profile',
765
			'area' => 'account',
766
		),
767
		array(
768
			'menu' => 'info',
769
			'area' => 'showposts',
770
			'title' => $txt['popup_showposts'],
771
		),
772
		array(
773
			'menu' => 'edit_profile',
774
			'area' => 'forumprofile',
775
			'title' => $txt['forumprofile'],
776
		),
777
		array(
778
			'menu' => 'edit_profile',
779
			'area' => 'notification',
780
		),
781
		array(
782
			'menu' => 'edit_profile',
783
			'area' => 'theme',
784
			'title' => $txt['theme'],
785
		),
786
		array(
787
			'menu' => 'edit_profile',
788
			'area' => 'ignoreboards',
789
		),
790
		array(
791
			'menu' => 'edit_profile',
792
			'area' => 'lists',
793
			'url' => $scripturl . '?action=profile;area=lists;sa=ignore',
794
			'title' => $txt['popup_ignore'],
795
		),
796
		array(
797
			'menu' => 'edit_profile',
798
			'area' => 'groupmembership',
799
		),
800
		array(
801
			'menu' => 'profile_action',
802
			'area' => 'subscriptions',
803
		),
804
	);
805
806
	call_integration_hook('integrate_profile_popup', array(&$profile_items));
807
808
	// Now check if these items are available
809
	$context['profile_items'] = array();
810
	$menu_context = &$context[$context['profile_menu_name']]['sections'];
811
	foreach ($profile_items as $item)
812
	{
813
		if (isset($menu_context[$item['menu']]['areas'][$item['area']]))
814
		{
815
			$context['profile_items'][] = $item;
816
		}
817
	}
818
}
819
820
/**
821
 * Set up the requirements for the alerts popup - the area that shows all the alerts just quickly for the current user.
822
 *
823
 * @param int $memID The ID of the member
824
 */
825
function alerts_popup($memID)
826
{
827
	global $context, $sourcedir, $db_show_debug, $cur_profile;
828
829
	// Load the Alerts language file.
830
	loadLanguage('Alerts');
831
832
	// We do not want to output debug information here.
833
	$db_show_debug = false;
834
835
	// We only want to output our little layer here.
836
	$context['template_layers'] = array();
837
838
	$context['unread_alerts'] = array();
839
	if (empty($_REQUEST['counter']) || (int) $_REQUEST['counter'] < $cur_profile['alerts'])
840
	{
841
		// Now fetch me my unread alerts, pronto!
842
		require_once($sourcedir . '/Profile-View.php');
843
		$context['unread_alerts'] = fetch_alerts($memID, false, $cur_profile['alerts'] - (!empty($_REQUEST['counter']) ? (int) $_REQUEST['counter'] : 0));
844
	}
845
}
846
847
/**
848
 * Load any custom fields for this area... no area means load all, 'summary' loads all public ones.
849
 *
850
 * @param int $memID The ID of the member
851
 * @param string $area Which area to load fields for
852
 */
853
function loadCustomFields($memID, $area = 'summary')
854
{
855
	global $context, $txt, $user_profile, $smcFunc, $user_info, $settings, $scripturl;
856
857
	// Get the right restrictions in place...
858
	$where = 'active = 1';
859
	if (!allowedTo('admin_forum') && $area != 'register')
860
	{
861
		// If it's the owner they can see two types of private fields, regardless.
862
		if ($memID == $user_info['id'])
863
			$where .= $area == 'summary' ? ' AND private < 3' : ' AND (private = 0 OR private = 2)';
864
		else
865
			$where .= $area == 'summary' ? ' AND private < 2' : ' AND private = 0';
866
	}
867
868
	if ($area == 'register')
869
		$where .= ' AND show_reg != 0';
870
	elseif ($area != 'summary')
871
		$where .= ' AND show_profile = {string:area}';
872
873
	// Load all the relevant fields - and data.
874
	$request = $smcFunc['db_query']('', '
875
		SELECT
876
			col_name, field_name, field_desc, field_type, field_order, show_reg, field_length, field_options,
877
			default_value, bbc, enclose, placement
878
		FROM {db_prefix}custom_fields
879
		WHERE ' . $where . '
880
		ORDER BY field_order',
881
		array(
882
			'area' => $area,
883
		)
884
	);
885
	$context['custom_fields'] = array();
886
	$context['custom_fields_required'] = false;
887
	while ($row = $smcFunc['db_fetch_assoc']($request))
888
	{
889
		// Shortcut.
890
		$exists = $memID && isset($user_profile[$memID], $user_profile[$memID]['options'][$row['col_name']]);
891
		$value = $exists ? $user_profile[$memID]['options'][$row['col_name']] : '';
892
893
		// If this was submitted already then make the value the posted version.
894
		if (isset($_POST['customfield']) && isset($_POST['customfield'][$row['col_name']]))
895
		{
896
			$value = $smcFunc['htmlspecialchars']($_POST['customfield'][$row['col_name']]);
897
			if (in_array($row['field_type'], array('select', 'radio')))
898
					$value = ($options = explode(',', $row['field_options'])) && isset($options[$value]) ? $options[$value] : '';
899
		}
900
901
		// Don't show the "disabled" option for the "gender" field if we are on the "summary" area.
902
		if ($area == 'summary' && $row['col_name'] == 'cust_gender' && $value == 'Disabled')
903
			continue;
904
905
		// HTML for the input form.
906
		$output_html = $value;
907
		if ($row['field_type'] == 'check')
908
		{
909
			$true = (!$exists && $row['default_value']) || $value;
910
			$input_html = '<input type="checkbox" name="customfield[' . $row['col_name'] . ']" id="customfield[' . $row['col_name'] . ']"' . ($true ? ' checked' : '') . '>';
911
			$output_html = $true ? $txt['yes'] : $txt['no'];
912
		}
913
		elseif ($row['field_type'] == 'select')
914
		{
915
			$input_html = '<select name="customfield[' . $row['col_name'] . ']" id="customfield[' . $row['col_name'] . ']"><option value="-1"></option>';
916
			$options = explode(',', $row['field_options']);
917
			foreach ($options as $k => $v)
918
			{
919
				$true = (!$exists && $row['default_value'] == $v) || $value == $v;
920
				$input_html .= '<option value="' . $k . '"' . ($true ? ' selected' : '') . '>' . $v . '</option>';
921
				if ($true)
922
					$output_html = $v;
923
			}
924
925
			$input_html .= '</select>';
926
		}
927
		elseif ($row['field_type'] == 'radio')
928
		{
929
			$input_html = '<fieldset>';
930
			$options = explode(',', $row['field_options']);
931
			foreach ($options as $k => $v)
932
			{
933
				$true = (!$exists && $row['default_value'] == $v) || $value == $v;
934
				$input_html .= '<label for="customfield_' . $row['col_name'] . '_' . $k . '"><input type="radio" name="customfield[' . $row['col_name'] . ']" id="customfield_' . $row['col_name'] . '_' . $k . '" value="' . $k . '"' . ($true ? ' checked' : '') . '>' . $v . '</label><br>';
935
				if ($true)
936
					$output_html = $v;
937
			}
938
			$input_html .= '</fieldset>';
939
		}
940
		elseif ($row['field_type'] == 'text')
941
		{
942
			$input_html = '<input type="text" name="customfield[' . $row['col_name'] . ']" id="customfield[' . $row['col_name'] . ']"' . ($row['field_length'] != 0 ? ' maxlength="' . $row['field_length'] . '"' : '') . ' size="' . ($row['field_length'] == 0 || $row['field_length'] >= 50 ? 50 : ($row['field_length'] > 30 ? 30 : ($row['field_length'] > 10 ? 20 : 10))) . '" value="' . un_htmlspecialchars($value) . '"' . ($row['show_reg'] == 2 ? ' required' : '') . '>';
943
		}
944
		else
945
		{
946
			@list ($rows, $cols) = @explode(',', $row['default_value']);
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...
947
			$input_html = '<textarea name="customfield[' . $row['col_name'] . ']" id="customfield[' . $row['col_name'] . ']"' . (!empty($rows) ? ' rows="' . $rows . '"' : '') . (!empty($cols) ? ' cols="' . $cols . '"' : '') . ($row['show_reg'] == 2 ? ' required' : '') . '>' . un_htmlspecialchars($value) . '</textarea>';
948
		}
949
950
		// Parse BBCode
951
		if ($row['bbc'])
952
			$output_html = parse_bbc($output_html);
953
		elseif ($row['field_type'] == 'textarea')
954
			// Allow for newlines at least
955
			$output_html = strtr($output_html, array("\n" => '<br>'));
956
957
		// Enclosing the user input within some other text?
958 View Code Duplication
		if (!empty($row['enclose']) && !empty($output_html))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
959
			$output_html = strtr($row['enclose'], array(
960
				'{SCRIPTURL}' => $scripturl,
961
				'{IMAGES_URL}' => $settings['images_url'],
962
				'{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
963
				'{INPUT}' => un_htmlspecialchars($output_html),
964
			));
965
966
		$context['custom_fields'][] = array(
967
			'name' => $row['field_name'],
968
			'desc' => $row['field_desc'],
969
			'type' => $row['field_type'],
970
			'order' => $row['field_order'],
971
			'input_html' => $input_html,
972
			'output_html' => $output_html,
973
			'placement' => $row['placement'],
974
			'colname' => $row['col_name'],
975
			'value' => $value,
976
			'show_reg' => $row['show_reg'],
977
		);
978
		$context['custom_fields_required'] = $context['custom_fields_required'] || $row['show_reg'] == 2;
979
	}
980
	$smcFunc['db_free_result']($request);
981
982
	call_integration_hook('integrate_load_custom_profile_fields', array($memID, $area));
983
}
984
985
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...