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

Profile-Actions.php ➔ getProfileData()   C

Complexity

Conditions 13
Paths 52

Size

Total Lines 90

Duplication

Lines 37
Ratio 41.11 %

Importance

Changes 0
Metric Value
cc 13
nc 52
nop 1
dl 37
loc 90
rs 5.5115
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 handles actions made on a user's profile.
5
 *
6
 * Simple Machines Forum (SMF)
7
 *
8
 * @package SMF
9
 * @author Simple Machines http://www.simplemachines.org
10
 * @copyright 2018 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 4
14
 */
15
16
if (!defined('SMF'))
17
	die('No direct access...');
18
19
/**
20
 * Activate an account.
21
 *
22
 * @param int $memID The ID of the member whose account we're activating
23
 */
24
function activateAccount($memID)
25
{
26
	global $sourcedir, $context, $user_profile, $modSettings;
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...
27
28
	isAllowedTo('moderate_forum');
29
30
	if (isset($_REQUEST['save']) && isset($user_profile[$memID]['is_activated']) && $user_profile[$memID]['is_activated'] != 1)
31
	{
32
		// If we are approving the deletion of an account, we do something special ;)
33
		if ($user_profile[$memID]['is_activated'] == 4)
34
		{
35
			require_once($sourcedir . '/Subs-Members.php');
36
			deleteMembers($context['id_member']);
37
			redirectexit();
38
		}
39
40
		// Let the integrations know of the activation.
41
		call_integration_hook('integrate_activate', array($user_profile[$memID]['member_name']));
42
43
		// Actually update this member now, as it guarantees the unapproved count can't get corrupted.
44
		updateMemberData($context['id_member'], array('is_activated' => $user_profile[$memID]['is_activated'] >= 10 ? 11 : 1, 'validation_code' => ''));
45
46
		// Log what we did?
47
		require_once($sourcedir . '/Logging.php');
48
		logAction('approve_member', array('member' => $memID), 'admin');
49
50
		// If we are doing approval, update the stats for the member just in case.
51
		if (in_array($user_profile[$memID]['is_activated'], array(3, 4, 5, 13, 14, 15)))
52
			updateSettings(array('unapprovedMembers' => ($modSettings['unapprovedMembers'] > 1 ? $modSettings['unapprovedMembers'] - 1 : 0)));
53
54
		// Make sure we update the stats too.
55
		updateStats('member', false);
56
	}
57
58
	// Leave it be...
59
	redirectexit('action=profile;u=' . $memID . ';area=summary');
60
}
61
62
/**
63
 * Issue/manage an user's warning status.
64
 *
65
 * @param int $memID The ID of the user
66
 */
67
function issueWarning($memID)
68
{
69
	global $txt, $scripturl, $modSettings, $user_info, $mbname;
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...
70
	global $context, $cur_profile, $smcFunc, $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...
71
72
	// Get all the actual settings.
73
	list ($modSettings['warning_enable'], $modSettings['user_limit']) = explode(',', $modSettings['warning_settings']);
74
75
	// This stores any legitimate errors.
76
	$issueErrors = array();
77
78
	// Doesn't hurt to be overly cautious.
79
	if (empty($modSettings['warning_enable']) || ($context['user']['is_owner'] && !$cur_profile['warning']) || !allowedTo('issue_warning'))
80
		fatal_lang_error('no_access', false);
81
82
	// Get the base (errors related) stuff done.
83
	loadLanguage('Errors');
84
	$context['custom_error_title'] = $txt['profile_warning_errors_occured'];
85
86
	// Make sure things which are disabled stay disabled.
87
	$modSettings['warning_watch'] = !empty($modSettings['warning_watch']) ? $modSettings['warning_watch'] : 110;
88
	$modSettings['warning_moderate'] = !empty($modSettings['warning_moderate']) && !empty($modSettings['postmod_active']) ? $modSettings['warning_moderate'] : 110;
89
	$modSettings['warning_mute'] = !empty($modSettings['warning_mute']) ? $modSettings['warning_mute'] : 110;
90
91
	$context['warning_limit'] = allowedTo('admin_forum') ? 0 : $modSettings['user_limit'];
92
	$context['member']['warning'] = $cur_profile['warning'];
93
	$context['member']['name'] = $cur_profile['real_name'];
94
95
	// What are the limits we can apply?
96
	$context['min_allowed'] = 0;
97
	$context['max_allowed'] = 100;
98
	if ($context['warning_limit'] > 0)
99
	{
100
		// Make sure we cannot go outside of our limit for the day.
101
		$request = $smcFunc['db_query']('', '
102
			SELECT SUM(counter)
103
			FROM {db_prefix}log_comments
104
			WHERE id_recipient = {int:selected_member}
105
				AND id_member = {int:current_member}
106
				AND comment_type = {string:warning}
107
				AND log_time > {int:day_time_period}',
108
			array(
109
				'current_member' => $user_info['id'],
110
				'selected_member' => $memID,
111
				'day_time_period' => time() - 86400,
112
				'warning' => 'warning',
113
			)
114
		);
115
		list ($current_applied) = $smcFunc['db_fetch_row']($request);
116
		$smcFunc['db_free_result']($request);
117
118
		$context['min_allowed'] = max(0, $cur_profile['warning'] - $current_applied - $context['warning_limit']);
119
		$context['max_allowed'] = min(100, $cur_profile['warning'] - $current_applied + $context['warning_limit']);
120
	}
121
122
	// Defaults.
123
	$context['warning_data'] = array(
124
		'reason' => '',
125
		'notify' => '',
126
		'notify_subject' => '',
127
		'notify_body' => '',
128
	);
129
130
	// Are we saving?
131
	if (isset($_POST['save']))
132
	{
133
		// Security is good here.
134
		checkSession();
135
136
		// This cannot be empty!
137
		$_POST['warn_reason'] = isset($_POST['warn_reason']) ? trim($_POST['warn_reason']) : '';
138
		if ($_POST['warn_reason'] == '' && !$context['user']['is_owner'])
139
			$issueErrors[] = 'warning_no_reason';
140
		$_POST['warn_reason'] = $smcFunc['htmlspecialchars']($_POST['warn_reason']);
141
142
		$_POST['warning_level'] = (int) $_POST['warning_level'];
143
		$_POST['warning_level'] = max(0, min(100, $_POST['warning_level']));
144
		if ($_POST['warning_level'] < $context['min_allowed'])
145
			$_POST['warning_level'] = $context['min_allowed'];
146
		elseif ($_POST['warning_level'] > $context['max_allowed'])
147
			$_POST['warning_level'] = $context['max_allowed'];
148
149
		// Do we actually have to issue them with a PM?
150
		$id_notice = 0;
151
		if (!empty($_POST['warn_notify']) && empty($issueErrors))
152
		{
153
			$_POST['warn_sub'] = trim($_POST['warn_sub']);
154
			$_POST['warn_body'] = trim($_POST['warn_body']);
155
			if (empty($_POST['warn_sub']) || empty($_POST['warn_body']))
156
				$issueErrors[] = 'warning_notify_blank';
157
			// Send the PM?
158
			else
159
			{
160
				require_once($sourcedir . '/Subs-Post.php');
161
				$from = array(
162
					'id' => 0,
163
					'name' => $context['forum_name_html_safe'],
164
					'username' => $context['forum_name_html_safe'],
165
				);
166
				sendpm(array('to' => array($memID), 'bcc' => array()), $_POST['warn_sub'], $_POST['warn_body'], false, $from);
167
168
				// Log the notice!
169
				$id_notice = $smcFunc['db_insert']('',
170
					'{db_prefix}log_member_notices',
171
					array(
172
						'subject' => 'string-255', 'body' => 'string-65534',
173
					),
174
					array(
175
						$smcFunc['htmlspecialchars']($_POST['warn_sub']), $smcFunc['htmlspecialchars']($_POST['warn_body']),
176
					),
177
					array('id_notice'),
178
					1
179
				);
180
			}
181
		}
182
183
		// Just in case - make sure notice is valid!
184
		$id_notice = (int) $id_notice;
185
186
		// What have we changed?
187
		$level_change = $_POST['warning_level'] - $cur_profile['warning'];
188
189
		// No errors? Proceed! Only log if you're not the owner.
190
		if (empty($issueErrors))
191
		{
192
			// Log what we've done!
193
			if (!$context['user']['is_owner'])
194
				$smcFunc['db_insert']('',
195
					'{db_prefix}log_comments',
196
					array(
197
						'id_member' => 'int', 'member_name' => 'string', 'comment_type' => 'string', 'id_recipient' => 'int', 'recipient_name' => 'string-255',
198
						'log_time' => 'int', 'id_notice' => 'int', 'counter' => 'int', 'body' => 'string-65534',
199
					),
200
					array(
201
						$user_info['id'], $user_info['name'], 'warning', $memID, $cur_profile['real_name'],
202
						time(), $id_notice, $level_change, $_POST['warn_reason'],
203
					),
204
					array('id_comment')
205
				);
206
207
			// Make the change.
208
			updateMemberData($memID, array('warning' => $_POST['warning_level']));
209
210
			// Leave a lovely message.
211
			$context['profile_updated'] = $context['user']['is_owner'] ? $txt['profile_updated_own'] : $txt['profile_warning_success'];
212
		}
213
		else
214
		{
215
			// Try to remember some bits.
216
			$context['warning_data'] = array(
217
				'reason' => $_POST['warn_reason'],
218
				'notify' => !empty($_POST['warn_notify']),
219
				'notify_subject' => isset($_POST['warn_sub']) ? $_POST['warn_sub'] : '',
220
				'notify_body' => isset($_POST['warn_body']) ? $_POST['warn_body'] : '',
221
			);
222
		}
223
224
		// Show the new improved warning level.
225
		$context['member']['warning'] = $_POST['warning_level'];
226
	}
227
228
	if (isset($_POST['preview']))
229
	{
230
		$warning_body = !empty($_POST['warn_body']) ? trim(censorText($_POST['warn_body'])) : '';
231
		$context['preview_subject'] = !empty($_POST['warn_sub']) ? trim($smcFunc['htmlspecialchars']($_POST['warn_sub'])) : '';
232
		if (empty($_POST['warn_sub']) || empty($_POST['warn_body']))
233
			$issueErrors[] = 'warning_notify_blank';
234
235 View Code Duplication
		if (!empty($_POST['warn_body']))
236
		{
237
			require_once($sourcedir . '/Subs-Post.php');
238
239
			preparsecode($warning_body);
240
			$warning_body = parse_bbc($warning_body, true);
241
		}
242
243
		// Try to remember some bits.
244
		$context['warning_data'] = array(
245
			'reason' => $_POST['warn_reason'],
246
			'notify' => !empty($_POST['warn_notify']),
247
			'notify_subject' => isset($_POST['warn_sub']) ? $_POST['warn_sub'] : '',
248
			'notify_body' => isset($_POST['warn_body']) ? $_POST['warn_body'] : '',
249
			'body_preview' => $warning_body,
250
		);
251
	}
252
253
	if (!empty($issueErrors))
254
	{
255
		// Fill in the suite of errors.
256
		$context['post_errors'] = array();
257
		foreach ($issueErrors as $error)
258
			$context['post_errors'][] = $txt[$error];
259
	}
260
261
262
	$context['page_title'] = $txt['profile_issue_warning'];
263
264
	// Let's use a generic list to get all the current warnings
265
	require_once($sourcedir . '/Subs-List.php');
266
267
	// Work our the various levels.
268
	$context['level_effects'] = array(
269
		0 => $txt['profile_warning_effect_none'],
270
		$modSettings['warning_watch'] => $txt['profile_warning_effect_watch'],
271
		$modSettings['warning_moderate'] => $txt['profile_warning_effect_moderation'],
272
		$modSettings['warning_mute'] => $txt['profile_warning_effect_mute'],
273
	);
274
	$context['current_level'] = 0;
275 View Code Duplication
	foreach ($context['level_effects'] as $limit => $dummy)
276
		if ($context['member']['warning'] >= $limit)
277
			$context['current_level'] = $limit;
278
279
	$listOptions = array(
280
		'id' => 'view_warnings',
281
		'title' => $txt['profile_viewwarning_previous_warnings'],
282
		'items_per_page' => $modSettings['defaultMaxListItems'],
283
		'no_items_label' => $txt['profile_viewwarning_no_warnings'],
284
		'base_href' => $scripturl . '?action=profile;area=issuewarning;sa=user;u=' . $memID,
285
		'default_sort_col' => 'log_time',
286
		'get_items' => array(
287
			'function' => 'list_getUserWarnings',
288
			'params' => array(
289
				$memID,
290
			),
291
		),
292
		'get_count' => array(
293
			'function' => 'list_getUserWarningCount',
294
			'params' => array(
295
				$memID,
296
			),
297
		),
298
		'columns' => array(
299
			'issued_by' => array(
300
				'header' => array(
301
					'value' => $txt['profile_warning_previous_issued'],
302
					'style' => 'width: 20%;',
303
				),
304
				'data' => array(
305
					'function' => function($warning)
306
					{
307
						return $warning['issuer']['link'];
308
					},
309
				),
310
				'sort' => array(
311
					'default' => 'lc.member_name DESC',
312
					'reverse' => 'lc.member_name',
313
				),
314
			),
315
			'log_time' => array(
316
				'header' => array(
317
					'value' => $txt['profile_warning_previous_time'],
318
					'style' => 'width: 30%;',
319
				),
320
				'data' => array(
321
					'db' => 'time',
322
				),
323
				'sort' => array(
324
					'default' => 'lc.log_time DESC',
325
					'reverse' => 'lc.log_time',
326
				),
327
			),
328
			'reason' => array(
329
				'header' => array(
330
					'value' => $txt['profile_warning_previous_reason'],
331
				),
332
				'data' => array(
333 View Code Duplication
					'function' => function($warning) use ($scripturl, $txt)
334
					{
335
						$ret = '
336
						<div class="floatleft">
337
							' . $warning['reason'] . '
338
						</div>';
339
340
						if (!empty($warning['id_notice']))
341
							$ret .= '
342
						<div class="floatright">
343
							<a href="' . $scripturl . '?action=moderate;area=notice;nid=' . $warning['id_notice'] . '" onclick="window.open(this.href, \'\', \'scrollbars=yes,resizable=yes,width=400,height=250\');return false;" target="_blank" rel="noopener" title="' . $txt['profile_warning_previous_notice'] . '"><span class="generic_icons filter centericon"></span></a>
344
						</div>';
345
346
						return $ret;
347
					},
348
				),
349
			),
350
			'level' => array(
351
				'header' => array(
352
					'value' => $txt['profile_warning_previous_level'],
353
					'style' => 'width: 6%;',
354
				),
355
				'data' => array(
356
					'db' => 'counter',
357
				),
358
				'sort' => array(
359
					'default' => 'lc.counter DESC',
360
					'reverse' => 'lc.counter',
361
				),
362
			),
363
		),
364
	);
365
366
	// Create the list for viewing.
367
	require_once($sourcedir . '/Subs-List.php');
368
	createList($listOptions);
369
370
	// Are they warning because of a message?
371
	if (isset($_REQUEST['msg']) && 0 < (int) $_REQUEST['msg'])
372
	{
373
		$request = $smcFunc['db_query']('', '
374
			SELECT subject
375
			FROM {db_prefix}messages AS m
376
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
377
			WHERE id_msg = {int:message}
378
				AND {query_see_board}
379
			LIMIT 1',
380
			array(
381
				'message' => (int) $_REQUEST['msg'],
382
			)
383
		);
384
		if ($smcFunc['db_num_rows']($request) != 0)
385
		{
386
			$context['warning_for_message'] = (int) $_REQUEST['msg'];
387
			list ($context['warned_message_subject']) = $smcFunc['db_fetch_row']($request);
388
		}
389
		$smcFunc['db_free_result']($request);
390
	}
391
392
	// Didn't find the message?
393
	if (empty($context['warning_for_message']))
394
	{
395
		$context['warning_for_message'] = 0;
396
		$context['warned_message_subject'] = '';
397
	}
398
399
	// Any custom templates?
400
	$context['notification_templates'] = array();
401
402
	$request = $smcFunc['db_query']('', '
403
		SELECT recipient_name AS template_title, body
404
		FROM {db_prefix}log_comments
405
		WHERE comment_type = {literal:warntpl}
406
			AND (id_recipient = {int:generic} OR id_recipient = {int:current_member})',
407
		array(
408
			'generic' => 0,
409
			'current_member' => $user_info['id'],
410
		)
411
	);
412
	while ($row = $smcFunc['db_fetch_assoc']($request))
413
	{
414
		// If we're not warning for a message skip any that are.
415
		if (!$context['warning_for_message'] && strpos($row['body'], '{MESSAGE}') !== false)
416
			continue;
417
418
		$context['notification_templates'][] = array(
419
			'title' => $row['template_title'],
420
			'body' => $row['body'],
421
		);
422
	}
423
	$smcFunc['db_free_result']($request);
424
425
	// Setup the "default" templates.
426
	foreach (array('spamming', 'offence', 'insulting') as $type)
427
		$context['notification_templates'][] = array(
428
			'title' => $txt['profile_warning_notify_title_' . $type],
429
			'body' => sprintf($txt['profile_warning_notify_template_outline' . (!empty($context['warning_for_message']) ? '_post' : '')], $txt['profile_warning_notify_for_' . $type]),
430
		);
431
432
	// Replace all the common variables in the templates.
433
	foreach ($context['notification_templates'] as $k => $name)
434
		$context['notification_templates'][$k]['body'] = strtr($name['body'], array('{MEMBER}' => un_htmlspecialchars($context['member']['name']), '{MESSAGE}' => '[url=' . $scripturl . '?msg=' . $context['warning_for_message'] . ']' . un_htmlspecialchars($context['warned_message_subject']) . '[/url]', '{SCRIPTURL}' => $scripturl, '{FORUMNAME}' => $mbname, '{REGARDS}' => $txt['regards_team']));
435
}
436
437
/**
438
 * Get the number of warnings a user has. Callback for $listOptions['get_count'] in issueWarning()
439
 *
440
 * @param int $memID The ID of the user
441
 * @return int Total number of warnings for the user
442
 */
443
function list_getUserWarningCount($memID)
444
{
445
	global $smcFunc;
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...
446
447
	$request = $smcFunc['db_query']('', '
448
		SELECT COUNT(*)
449
		FROM {db_prefix}log_comments
450
		WHERE id_recipient = {int:selected_member}
451
			AND comment_type = {literal:warning}',
452
		array(
453
			'selected_member' => $memID,
454
		)
455
	);
456
	list ($total_warnings) = $smcFunc['db_fetch_row']($request);
457
	$smcFunc['db_free_result']($request);
458
459
	return $total_warnings;
460
}
461
462
/**
463
 * Get the data about a user's warnings. Callback function for the list in issueWarning()
464
 *
465
 * @param int $start The item to start with (for pagination purposes)
466
 * @param int $items_per_page How many items to show on each page
467
 * @param string $sort A string indicating how to sort the results
468
 * @param int $memID The member ID
469
 * @return array An array of information about the user's warnings
470
 */
471
function list_getUserWarnings($start, $items_per_page, $sort, $memID)
472
{
473
	global $smcFunc, $scripturl;
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...
474
475
	$request = $smcFunc['db_query']('', '
476
		SELECT COALESCE(mem.id_member, 0) AS id_member, COALESCE(mem.real_name, lc.member_name) AS member_name,
477
			lc.log_time, lc.body, lc.counter, lc.id_notice
478
		FROM {db_prefix}log_comments AS lc
479
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lc.id_member)
480
		WHERE lc.id_recipient = {int:selected_member}
481
			AND lc.comment_type = {literal:warning}
482
		ORDER BY {raw:sort}
483
		LIMIT {int:start}, {int:max}',
484
		array(
485
			'selected_member' => $memID,
486
			'sort' => $sort,
487
			'start' => $start,
488
			'max' => $items_per_page,
489
		)
490
	);
491
	$previous_warnings = array();
492
	while ($row = $smcFunc['db_fetch_assoc']($request))
493
	{
494
		$previous_warnings[] = array(
495
			'issuer' => array(
496
				'id' => $row['id_member'],
497
				'link' => $row['id_member'] ? ('<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['member_name'] . '</a>') : $row['member_name'],
498
			),
499
			'time' => timeformat($row['log_time']),
500
			'reason' => $row['body'],
501
			'counter' => $row['counter'] > 0 ? '+' . $row['counter'] : $row['counter'],
502
			'id_notice' => $row['id_notice'],
503
		);
504
	}
505
	$smcFunc['db_free_result']($request);
506
507
	return $previous_warnings;
508
}
509
510
/**
511
 * Present a screen to make sure the user wants to be deleted
512
 *
513
 * @param int $memID The member ID
514
 */
515
function deleteAccount($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...
516
{
517
	global $txt, $context, $modSettings, $cur_profile;
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...
518
519 View Code Duplication
	if (!$context['user']['is_owner'])
520
		isAllowedTo('profile_remove_any');
521
	elseif (!allowedTo('profile_remove_any'))
522
		isAllowedTo('profile_remove_own');
523
524
	// Permissions for removing stuff...
525
	$context['can_delete_posts'] = !$context['user']['is_owner'] && allowedTo('moderate_forum');
526
527
	// Show an extra option if recycling is enabled...
528
	$context['show_perma_delete'] = !empty($modSettings['recycle_enable']) && !empty($modSettings['recycle_board']);
529
530
	// Can they do this, or will they need approval?
531
	$context['needs_approval'] = $context['user']['is_owner'] && !empty($modSettings['approveAccountDeletion']) && !allowedTo('moderate_forum');
532
	$context['page_title'] = $txt['deleteAccount'] . ': ' . $cur_profile['real_name'];
533
}
534
535
/**
536
 * Actually delete an account.
537
 *
538
 * @param int $memID The member ID
539
 */
540
function deleteAccount2($memID)
541
{
542
	global $user_info, $sourcedir, $context, $cur_profile, $modSettings, $smcFunc;
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...
543
544
	// Try get more time...
545
	@set_time_limit(600);
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...
546
547
	// @todo Add a way to delete pms as well?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
548
549 View Code Duplication
	if (!$context['user']['is_owner'])
550
		isAllowedTo('profile_remove_any');
551
	elseif (!allowedTo('profile_remove_any'))
552
		isAllowedTo('profile_remove_own');
553
554
	checkSession();
555
556
	$old_profile = &$cur_profile;
557
558
	// Too often, people remove/delete their own only account.
559
	if (in_array(1, explode(',', $old_profile['additional_groups'])) || $old_profile['id_group'] == 1)
560
	{
561
		// Are you allowed to administrate the forum, as they are?
562
		isAllowedTo('admin_forum');
563
564
		$request = $smcFunc['db_query']('', '
565
			SELECT id_member
566
			FROM {db_prefix}members
567
			WHERE (id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0)
568
				AND id_member != {int:selected_member}
569
			LIMIT 1',
570
			array(
571
				'admin_group' => 1,
572
				'selected_member' => $memID,
573
			)
574
		);
575
		list ($another) = $smcFunc['db_fetch_row']($request);
576
		$smcFunc['db_free_result']($request);
577
578
		if (empty($another))
579
			fatal_lang_error('at_least_one_admin', 'critical');
580
	}
581
582
	// This file is needed for the deleteMembers function.
583
	require_once($sourcedir . '/Subs-Members.php');
584
585
	// Do you have permission to delete others profiles, or is that your profile you wanna delete?
586
	if ($memID != $user_info['id'])
587
	{
588
		isAllowedTo('profile_remove_any');
589
590
		// Before we go any further, handle possible poll vote deletion as well
591
		if (!empty($_POST['deleteVotes']) && allowedTo('moderate_forum'))
592
		{
593
			// First we find any polls that this user has voted in...
594
			$get_voted_polls = $smcFunc['db_query']('', '
595
				SELECT DISTINCT id_poll
596
				FROM {db_prefix}log_polls
597
				WHERE id_member = {int:selected_member}',
598
				array(
599
					'selected_member' => $memID,
600
				)
601
			);
602
603
			$polls_to_update = array();
604
605
			while ($row = $smcFunc['db_fetch_assoc']($get_voted_polls))
606
			{
607
				$polls_to_update[] = $row['id_poll'];
608
			}
609
610
			$smcFunc['db_free_result']($get_voted_polls);
611
612
			// Now we delete the votes and update the polls
613
			if (!empty($polls_to_update))
614
			{
615
				$smcFunc['db_query']('', '
616
					DELETE FROM {db_prefix}log_polls
617
					WHERE id_member = {int:selected_member}',
618
					array(
619
						'selected_member' => $memID,
620
					)
621
				);
622
623
				$smcFunc['db_query']('', '
624
					UPDATE {db_prefix}polls
625
					SET votes = votes - 1
626
					WHERE id_poll IN {array_int:polls_to_update}',
627
					array(
628
						'polls_to_update' => $polls_to_update
629
					)
630
				);
631
			}
632
		}
633
634
		// Now, have you been naughty and need your posts deleting?
635
		// @todo Should this check board permissions?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
636
		if (!empty($_POST['deletePosts']) && in_array($_POST['remove_type'], array('posts', 'topics')) && allowedTo('moderate_forum'))
637
		{
638
			// Include RemoveTopics - essential for this type of work!
639
			require_once($sourcedir . '/RemoveTopic.php');
640
641
			$extra = empty($_POST['perma_delete']) ? ' AND t.id_board != {int:recycle_board}' : '';
642
			$recycle_board = empty($modSettings['recycle_board']) ? 0 : $modSettings['recycle_board'];
643
644
			// First off we delete any topics the member has started - if they wanted topics being done.
645
			if ($_POST['remove_type'] == 'topics')
646
			{
647
				// Fetch all topics started by this user within the time period.
648
				$request = $smcFunc['db_query']('', '
649
					SELECT t.id_topic
650
					FROM {db_prefix}topics AS t
651
					WHERE t.id_member_started = {int:selected_member}' . $extra,
652
					array(
653
						'selected_member' => $memID,
654
						'recycle_board' => $recycle_board,
655
					)
656
				);
657
				$topicIDs = array();
658
				while ($row = $smcFunc['db_fetch_assoc']($request))
659
					$topicIDs[] = $row['id_topic'];
660
				$smcFunc['db_free_result']($request);
661
662
				// Actually remove the topics. Ignore recycling if we want to perma-delete things...
663
				// @todo This needs to check permissions, but we'll let it slide for now because of moderate_forum already being had.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
664
				removeTopics($topicIDs, true, !empty($extra));
665
			}
666
667
			// Now delete the remaining messages.
668
			$request = $smcFunc['db_query']('', '
669
				SELECT m.id_msg
670
				FROM {db_prefix}messages AS m
671
					INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic
672
						AND t.id_first_msg != m.id_msg)
673
				WHERE m.id_member = {int:selected_member}' . $extra,
674
				array(
675
					'selected_member' => $memID,
676
					'recycle_board' => $recycle_board,
677
				)
678
			);
679
			// This could take a while... but ya know it's gonna be worth it in the end.
680
			while ($row = $smcFunc['db_fetch_assoc']($request))
681
			{
682
				if (function_exists('apache_reset_timeout'))
683
					@apache_reset_timeout();
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...
684
685
				removeMessage($row['id_msg']);
686
			}
687
			$smcFunc['db_free_result']($request);
688
		}
689
690
		// Only delete this poor members account if they are actually being booted out of camp.
691
		if (isset($_POST['deleteAccount']))
692
			deleteMembers($memID);
693
	}
694
	// Do they need approval to delete?
695
	elseif (!empty($modSettings['approveAccountDeletion']) && !allowedTo('moderate_forum'))
696
	{
697
		// Setup their account for deletion ;)
698
		updateMemberData($memID, array('is_activated' => 4));
699
		// Another account needs approval...
700
		updateSettings(array('unapprovedMembers' => true), true);
701
	}
702
	// Also check if you typed your password correctly.
703
	else
704
	{
705
		deleteMembers($memID);
706
707
		require_once($sourcedir . '/LogInOut.php');
708
		LogOut(true);
709
710
		redirectexit();
711
	}
712
}
713
714
/**
715
 * Function for doing all the paid subscription stuff - kinda.
716
 *
717
 * @param int $memID The ID of the user whose subscriptions we're viewing
718
 */
719
function subscriptions($memID)
720
{
721
	global $context, $txt, $sourcedir, $modSettings, $smcFunc, $scripturl;
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...
722
723
	// Load the paid template anyway.
724
	loadTemplate('ManagePaid');
725
	loadLanguage('ManagePaid');
726
727
	// Load all of the subscriptions.
728
	require_once($sourcedir . '/ManagePaid.php');
729
	loadSubscriptions();
730
	$context['member']['id'] = $memID;
731
732
	// Remove any invalid ones.
733
	foreach ($context['subscriptions'] as $id => $sub)
734
	{
735
		// Work out the costs.
736
		$costs = $smcFunc['json_decode']($sub['real_cost'], true);
737
738
		$cost_array = array();
739
		if ($sub['real_length'] == 'F')
740
		{
741
			foreach ($costs as $duration => $cost)
742
			{
743
				if ($cost != 0)
744
					$cost_array[$duration] = $cost;
745
			}
746
		}
747
		else
748
		{
749
			$cost_array['fixed'] = $costs['fixed'];
750
		}
751
752
		if (empty($cost_array))
753
			unset($context['subscriptions'][$id]);
754
		else
755
		{
756
			$context['subscriptions'][$id]['member'] = 0;
757
			$context['subscriptions'][$id]['subscribed'] = false;
758
			$context['subscriptions'][$id]['costs'] = $cost_array;
759
		}
760
	}
761
762
	// Work out what gateways are enabled.
763
	$gateways = loadPaymentGateways();
764
	foreach ($gateways as $id => $gateway)
765
	{
766
		$gateways[$id] = new $gateway['display_class']();
767
		if (!$gateways[$id]->gatewayEnabled())
768
			unset($gateways[$id]);
769
	}
770
771
	// No gateways yet?
772
	if (empty($gateways))
773
		fatal_error($txt['paid_admin_not_setup_gateway']);
774
775
	// Get the current subscriptions.
776
	$request = $smcFunc['db_query']('', '
777
		SELECT id_sublog, id_subscribe, start_time, end_time, status, payments_pending, pending_details
778
		FROM {db_prefix}log_subscribed
779
		WHERE id_member = {int:selected_member}',
780
		array(
781
			'selected_member' => $memID,
782
		)
783
	);
784
	$context['current'] = array();
785
	while ($row = $smcFunc['db_fetch_assoc']($request))
786
	{
787
		// The subscription must exist!
788
		if (!isset($context['subscriptions'][$row['id_subscribe']]))
789
			continue;
790
791
		$context['current'][$row['id_subscribe']] = array(
792
			'id' => $row['id_sublog'],
793
			'sub_id' => $row['id_subscribe'],
794
			'hide' => $row['status'] == 0 && $row['end_time'] == 0 && $row['payments_pending'] == 0,
795
			'name' => $context['subscriptions'][$row['id_subscribe']]['name'],
796
			'start' => timeformat($row['start_time'], false),
797
			'end' => $row['end_time'] == 0 ? $txt['not_applicable'] : timeformat($row['end_time'], false),
798
			'pending_details' => $row['pending_details'],
799
			'status' => $row['status'],
800
			'status_text' => $row['status'] == 0 ? ($row['payments_pending'] ? $txt['paid_pending'] : $txt['paid_finished']) : $txt['paid_active'],
801
		);
802
803
		if ($row['status'] == 1)
804
			$context['subscriptions'][$row['id_subscribe']]['subscribed'] = true;
805
	}
806
	$smcFunc['db_free_result']($request);
807
808
	// Simple "done"?
809
	if (isset($_GET['done']))
810
	{
811
		$_GET['sub_id'] = (int) $_GET['sub_id'];
812
813
		// Must exist but let's be sure...
814
		if (isset($context['current'][$_GET['sub_id']]))
815
		{
816
			// What are the details like?
817
			$current_pending = $smcFunc['json_decode']($context['current'][$_GET['sub_id']]['pending_details'], true);
818
			if (!empty($current_pending))
819
			{
820
				$current_pending = array_reverse($current_pending);
821
				foreach ($current_pending as $id => $sub)
822
				{
823
					// Just find one and change it.
824
					if ($sub[0] == $_GET['sub_id'] && $sub[3] == 'prepay')
825
					{
826
						$current_pending[$id][3] = 'payback';
827
						break;
828
					}
829
				}
830
831
				// Save the details back.
832
				$pending_details = $smcFunc['json_encode']($current_pending);
833
834
				$smcFunc['db_query']('', '
835
					UPDATE {db_prefix}log_subscribed
836
					SET payments_pending = payments_pending + 1, pending_details = {string:pending_details}
837
					WHERE id_sublog = {int:current_subscription_id}
838
						AND id_member = {int:selected_member}',
839
					array(
840
						'current_subscription_id' => $context['current'][$_GET['sub_id']]['id'],
841
						'selected_member' => $memID,
842
						'pending_details' => $pending_details,
843
					)
844
				);
845
			}
846
		}
847
848
		$context['sub_template'] = 'paid_done';
849
		return;
850
	}
851
	// If this is confirmation then it's simpler...
852
	if (isset($_GET['confirm']) && isset($_POST['sub_id']) && is_array($_POST['sub_id']))
853
	{
854
		// Hopefully just one.
855
		foreach ($_POST['sub_id'] as $k => $v)
856
			$ID_SUB = (int) $k;
857
858
		if (!isset($context['subscriptions'][$ID_SUB]) || $context['subscriptions'][$ID_SUB]['active'] == 0)
0 ignored issues
show
Bug introduced by
The variable $ID_SUB 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...
859
			fatal_lang_error('paid_sub_not_active');
860
861
		// Simplify...
862
		$context['sub'] = $context['subscriptions'][$ID_SUB];
863
		$period = 'xx';
864
		if ($context['sub']['flexible'])
865
			$period = isset($_POST['cur'][$ID_SUB]) && isset($context['sub']['costs'][$_POST['cur'][$ID_SUB]]) ? $_POST['cur'][$ID_SUB] : 'xx';
866
867
		// Check we have a valid cost.
868
		if ($context['sub']['flexible'] && $period == 'xx')
869
			fatal_lang_error('paid_sub_not_active');
870
871
		// Sort out the cost/currency.
872
		$context['currency'] = $modSettings['paid_currency_code'];
873
		$context['recur'] = $context['sub']['repeatable'];
874
875
		if ($context['sub']['flexible'])
876
		{
877
			// Real cost...
878
			$context['value'] = $context['sub']['costs'][$_POST['cur'][$ID_SUB]];
879
			$context['cost'] = sprintf($modSettings['paid_currency_symbol'], $context['value']) . '/' . $txt[$_POST['cur'][$ID_SUB]];
880
			// The period value for paypal.
881
			$context['paypal_period'] = strtoupper(substr($_POST['cur'][$ID_SUB], 0, 1));
882
		}
883
		else
884
		{
885
			// Real cost...
886
			$context['value'] = $context['sub']['costs']['fixed'];
887
			$context['cost'] = sprintf($modSettings['paid_currency_symbol'], $context['value']);
888
889
			// Recur?
890
			preg_match('~(\d*)(\w)~', $context['sub']['real_length'], $match);
891
			$context['paypal_unit'] = $match[1];
892
			$context['paypal_period'] = $match[2];
893
		}
894
895
		// Setup the gateway context.
896
		$context['gateways'] = array();
897
		foreach ($gateways as $id => $gateway)
898
		{
899
			$fields = $gateways[$id]->fetchGatewayFields($context['sub']['id'] . '+' . $memID, $context['sub'], $context['value'], $period, $scripturl . '?action=profile;u=' . $memID . ';area=subscriptions;sub_id=' . $context['sub']['id'] . ';done');
900
			if (!empty($fields['form']))
901
				$context['gateways'][] = $fields;
902
		}
903
904
		// Bugger?!
905
		if (empty($context['gateways']))
906
			fatal_error($txt['paid_admin_not_setup_gateway']);
907
908
		// Now we are going to assume they want to take this out ;)
909
		$new_data = array($context['sub']['id'], $context['value'], $period, 'prepay');
910
		if (isset($context['current'][$context['sub']['id']]))
911
		{
912
			// What are the details like?
913
			$current_pending = array();
914
			if ($context['current'][$context['sub']['id']]['pending_details'] != '')
915
				$current_pending = $smcFunc['json_decode']($context['current'][$context['sub']['id']]['pending_details'], true);
916
			// Don't get silly.
917
			if (count($current_pending) > 9)
918
				$current_pending = array();
919
			$pending_count = 0;
920
			// Only record real pending payments as will otherwise confuse the admin!
921
			foreach ($current_pending as $pending)
922
				if ($pending[3] == 'payback')
923
					$pending_count++;
924
925
			if (!in_array($new_data, $current_pending))
926
			{
927
				$current_pending[] = $new_data;
928
				$pending_details = $smcFunc['json_encode']($current_pending);
929
930
				$smcFunc['db_query']('', '
931
					UPDATE {db_prefix}log_subscribed
932
					SET payments_pending = {int:pending_count}, pending_details = {string:pending_details}
933
					WHERE id_sublog = {int:current_subscription_item}
934
						AND id_member = {int:selected_member}',
935
					array(
936
						'pending_count' => $pending_count,
937
						'current_subscription_item' => $context['current'][$context['sub']['id']]['id'],
938
						'selected_member' => $memID,
939
						'pending_details' => $pending_details,
940
					)
941
				);
942
			}
943
		}
944
		// Never had this before, lovely.
945
		else
946
		{
947
			$pending_details = $smcFunc['json_encode'](array($new_data));
948
			$smcFunc['db_insert']('',
949
				'{db_prefix}log_subscribed',
950
				array(
951
					'id_subscribe' => 'int', 'id_member' => 'int', 'status' => 'int', 'payments_pending' => 'int', 'pending_details' => 'string-65534',
952
					'start_time' => 'int', 'vendor_ref' => 'string-255',
953
				),
954
				array(
955
					$context['sub']['id'], $memID, 0, 0, $pending_details,
956
					time(), '',
957
				),
958
				array('id_sublog')
959
			);
960
		}
961
962
		// Change the template.
963
		$context['sub_template'] = 'choose_payment';
964
965
		// Quit.
966
		return;
967
	}
968
	else
969
		$context['sub_template'] = 'user_subscription';
970
}
971
972
/**
973
 * Function to allow the user to todo Privacy stuff
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
974
 *
975
 * @param int $memID The ID of the member
976
 */
977
function getProfileData($memID)
978
{
979
	global $txt, $user_profile, $context, $smcFunc, $user_info;
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...
980
981
	if ($memID == $user_info['id'])
982
		$context['pdc']['own'] = true;
983
	else
984
		$context['pdc']['name'] = $context['member']['name'];
985
	
986
	if (!empty($_REQUEST['nofound']))
987
		$context['pdc']['nofound'] = true;
988
	
989
	if (empty($_REQUEST['activity']))
990
		return;
991
	
992
	$mode = $_REQUEST['activity'];
993
994
	$profileData = array();
995
	if ($mode == 'profile') // profile
996
	{
997
		loadMemberData($memID, false, 'profile');
998
		$profile = $user_profile[$memID];
999
		$removeFields = array('secret_question','tfa_secret','password_salt');
1000
		foreach ($removeFields as $value)
1001
		{
1002
			unset($profile[$value]);
1003
		}
1004
		foreach($profile as $key => &$value)
1005
		{
1006
			if (is_array($value))
1007
				$value = $smcFunc['json_encode']($value);
1008
		}
1009
		$profileData[0] = array_keys($profile);
1010
		$profileData[1] = $profile;
1011
		call_integration_hook('integrate_getProfile_profile', array(&$profileData));
1012
	}
1013 View Code Duplication
	elseif ($mode == 'messages') // messages
1014
	{
1015
		
1016
		$request = $smcFunc['db_query']('','
1017
			SELECT id_msg, id_topic, poster_time, subject, modified_time, modified_name, modified_reason, body, likes, poster_ip
1018
			FROM {db_prefix}messages 
1019
			WHERE id_member = {int:memID}',
1020
			array(
1021
				'memID' => $memID,
1022
			)
1023
		);
1024
		$profileData = $smcFunc['db_fetch_all']($request);
1025
		if (!is_array($profileData))
1026
			redirectexit('action=profile;area=getprofiledata;u=' . $memID . ';nofound=1');
1027
		array_unshift($profileData, array_keys($profileData[0]));
1028
		$smcFunc['db_free_result']($request);
1029
1030
		call_integration_hook('integrate_getProfile_messages', array(&$profileData));
1031
	}
1032 View Code Duplication
	elseif ($mode == 'pmessages')
1033
	{
1034
		$request = $smcFunc['db_query']('','
1035
			SELECT pm.msgtime, pm.subject, pm.body
1036
			FROM {db_prefix}personal_messages pm
1037
			LEFT JOIN {db_prefix}pm_recipients pmr on (pm.id_pm = pmr.id_pm and pmr.id_member = {int:memID})
1038
			WHERE pm.id_member_from = {int:memID} or pmr.id_member = {int:memID}',
1039
			array(
1040
				'memID' => $memID,
1041
			)
1042
		);
1043
		$profileData = $smcFunc['db_fetch_all']($request);
1044
		if (!is_array($profileData))
1045
			redirectexit('action=profile;area=getprofiledata;u=' . $memID . ';nofound=1');
1046
		array_unshift($profileData, array_keys($profileData[0]));
1047
		$smcFunc['db_free_result']($request);
1048
		call_integration_hook('integrate_getProfile_pmessages', array(&$profileData));
1049
	}
1050
	$count = count($profileData);
1051
	$csv_data = '';
1052
	for($i = 0; $i < $count; $i++)
1053
	{
1054
		$csv_data .= arrayToCsv($profileData[$i]) . "\r\n";
1055
	}
1056
1057
1058
	header("Pragma: no-cache");
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal Pragma: no-cache does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1059
	header('Content-Disposition: attachment; filename="privacySMF'.$mode.'.csv";');
0 ignored issues
show
Security Response Splitting introduced by
'Content-Disposition: at...SMF' . $mode . '.csv";' can contain request data and is used in response header context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_REQUEST, and $mode is assigned
    in Sources/Profile-Actions.php on line 992

Response Splitting Attacks

Allowing an attacker to set a response header, opens your application to response splitting attacks; effectively allowing an attacker to send any response, he would like.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
1060
	header("Content-Length: " . strlen($csv_data));
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal Content-Length: does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1061
	header("Content-Transfer-Encoding: binary");
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal Content-Transfer-Encoding: binary does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1062
	header("Content-Type: application/force-download");
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal Content-Type: application/force-download does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1063
	echo $csv_data;
1064
1065
	exit;
1066
}
1067
1068
/**
1069
 * Function to allow the user to todo Policy stuff
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1070
 *
1071
 * @param int $memID The ID of the member
1072
 */
1073
function getPolicyData($memID)
1074
{
1075
	global $txt, $user_profile, $context, $smcFunc, $user_info, $modSettings;
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...
1076
1077
	$context['poc']['own'] = false;
1078
1079
	if ($memID == $user_info['id'])
1080
		$context['poc']['own'] = true;
1081
1082
	if (!empty($user_profile[$memID]['options']['policy_approved']) &&
1083
			!empty($modSettings[$user_profile[$memID]['options']['policy_approved']]))
1084
	{
1085
		$context['poc']['approved_text'] = $modSettings[$user_profile[$memID]['options']['policy_approved']];
1086
	}
1087
1088
	if (((!empty($user_profile[$memID]['options']['policy_approved']) &&
1089
			!empty($modSettings[$user_profile[$memID]['options']['policy_approved']]) &&
1090
			!empty($modSettings['policy_version']) &&
1091
			!empty($modSettings[$modSettings['policy_version']]) &&
1092
			$user_profile[$memID]['options']['policy_approved'] != $modSettings['policy_version'])) || 
1093
		empty($user_profile[$memID]['options']['policy_approved']))
1094
	{
1095
		$context['poc']['newVersionText'] = $modSettings[$modSettings['policy_version']];
1096
	}
1097
1098
	if (!empty($context['poc']['own']) && (!empty($context['poc']['newVersionText']) || empty($user_profile[$memID]['options']['policy_isvalid'])))
1099
		$context['poc']['showAccept'] = true;
1100
1101
	if (empty($_REQUEST['activity']))
1102
		return;
1103
1104
	$mode = $_REQUEST['activity'];
1105
1106
	$profileData = array();
0 ignored issues
show
Unused Code introduced by
$profileData is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1107
	if ($mode == 'save') // profile
1108
	{
1109
		if (!$context['poc']['own']) //only for yourself
1110
			exit;
1111
1112
		$data = array(
1113
			array($memID, 1, 'policy_approved', $modSettings['policy_version']),
1114
			array($memID, 1, 'policy_isvalid', 1)
1115
		);
1116
		$smcFunc['db_insert']('replace',
1117
			'{db_prefix}themes',
1118
			array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string', 'value' => 'string'),
1119
			$data,
1120
			array('id_member', 'id_theme', 'variable')
1121
		);
1122
		
1123
		redirectexit('action=profile;area=getpolicydata;u=' . $memID);
1124
	}
1125
}
1126
1127
/**
1128
  * Formats a line (passed as a fields  array) as CSV and returns the CSV as a string.
1129
  * from https://stackoverflow.com/questions/3933668/convert-array-into-csv
1130
 * 
1131
 * @param array $fields a line of the array
1132
 * @param string @delimiter the delimiter char
1133
 * @param string @enclosure the enclosure char
1134
 * @param boolean $encloseAll enclose all
1135
 * @param boolean $nullToMysqlNull
1136
  */
1137
function arrayToCsv( array $fields, $delimiter = ';', $enclosure = '"', $encloseAll = false, $nullToMysqlNull = false ) {
1138
	$delimiter_esc = preg_quote($delimiter, '/');
1139
	$enclosure_esc = preg_quote($enclosure, '/');
1140
1141
	$output = array();
1142
	foreach ( $fields as $field )
1143
	{
1144
		if ($field === null && $nullToMysqlNull)
1145
		{
1146
			$output[] = 'NULL';
1147
			continue;
1148
		}
1149
1150
		// Enclose fields containing $delimiter, $enclosure or whitespace
1151
		if ( $encloseAll || preg_match( "/(?:${delimiter_esc}|${enclosure_esc}|\s)/", $field ) )
1152
		{
1153
			$output[] = $enclosure . str_replace($enclosure, $enclosure . $enclosure, $field) . $enclosure;
1154
		}
1155
		else
1156
		{
1157
			$output[] = $field;
1158
		}
1159
	}
1160
1161
	return implode( $delimiter, $output );
1162
}
1163
1164
?>