Failed Conditions
Branch release-2.1 (4e22cf)
by Rick
06:39
created

ManageBans.php ➔ list_getBanItems()   D

Complexity

Conditions 12
Paths 110

Size

Total Lines 93
Code Lines 53

Duplication

Lines 10
Ratio 10.75 %

Importance

Changes 0
Metric Value
cc 12
eloc 53
nc 110
nop 4
dl 10
loc 93
rs 4.8536
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 contains all the functions used for the ban center.
5
 * @todo refactor as controller-model
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
6
 *
7
 * Simple Machines Forum (SMF)
8
 *
9
 * @package SMF
10
 * @author Simple Machines http://www.simplemachines.org
11
 * @copyright 2017 Simple Machines and individual contributors
12
 * @license http://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1 Beta 4
15
 */
16
17
if (!defined('SMF'))
18
	die('No direct access...');
19
20
/**
21
 * Ban center. The main entrance point for all ban center functions.
22
 * It is accesssed by ?action=admin;area=ban.
23
 * It choses a function based on the 'sa' parameter, like many others.
24
 * The default sub-action is BanList().
25
 * It requires the ban_members permission.
26
 * It initializes the admin tabs.
27
 *
28
 * @uses ManageBans template.
29
 */
30
function Ban()
31
{
32
	global $context, $txt, $scripturl;
33
34
	isAllowedTo('manage_bans');
35
36
	loadTemplate('ManageBans');
37
38
	$subActions = array(
39
		'add' => 'BanEdit',
40
		'browse' => 'BanBrowseTriggers',
41
		'edittrigger' => 'BanEditTrigger',
42
		'edit' => 'BanEdit',
43
		'list' => 'BanList',
44
		'log' => 'BanLog',
45
	);
46
47
	// Default the sub-action to 'view ban list'.
48
	$_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'list';
49
50
	$context['page_title'] = $txt['ban_title'];
51
	$context['sub_action'] = $_REQUEST['sa'];
52
53
	// Tabs for browsing the different ban functions.
54
	$context[$context['admin_menu_name']]['tab_data'] = array(
55
		'title' => $txt['ban_title'],
56
		'help' => 'ban_members',
57
		'description' => $txt['ban_description'],
58
		'tabs' => array(
59
			'list' => array(
60
				'description' => $txt['ban_description'],
61
				'href' => $scripturl . '?action=admin;area=ban;sa=list',
62
				'is_selected' => $_REQUEST['sa'] == 'list' || $_REQUEST['sa'] == 'edit' || $_REQUEST['sa'] == 'edittrigger',
63
			),
64
			'add' => array(
65
				'description' => $txt['ban_description'],
66
				'href' => $scripturl . '?action=admin;area=ban;sa=add',
67
				'is_selected' => $_REQUEST['sa'] == 'add',
68
			),
69
			'browse' => array(
70
				'description' => $txt['ban_trigger_browse_description'],
71
				'href' => $scripturl . '?action=admin;area=ban;sa=browse',
72
				'is_selected' => $_REQUEST['sa'] == 'browse',
73
			),
74
			'log' => array(
75
				'description' => $txt['ban_log_description'],
76
				'href' => $scripturl . '?action=admin;area=ban;sa=log',
77
				'is_selected' => $_REQUEST['sa'] == 'log',
78
				'is_last' => true,
79
			),
80
		),
81
	);
82
83
	call_integration_hook('integrate_manage_bans', array(&$subActions));
84
85
	// Call the right function for this sub-action.
86
	call_helper($subActions[$_REQUEST['sa']]);
87
}
88
89
/**
90
 * Shows a list of bans currently set.
91
 * It is accessed by ?action=admin;area=ban;sa=list.
92
 * It removes expired bans.
93
 * It allows sorting on different criteria.
94
 * It also handles removal of selected ban items.
95
 *
96
 * @uses the main ManageBans template.
97
 */
98
function BanList()
99
{
100
	global $txt, $context, $scripturl;
101
	global $user_info, $sourcedir, $modSettings;
102
103
	// User pressed the 'remove selection button'.
104
	if (!empty($_POST['removeBans']) && !empty($_POST['remove']) && is_array($_POST['remove']))
105
	{
106
		checkSession();
107
108
		// Make sure every entry is a proper integer.
109
		array_map('intval', $_POST['remove']);
110
111
		// Unban them all!
112
		removeBanGroups($_POST['remove']);
113
		removeBanTriggers($_POST['remove']);
114
115
		// No more caching this ban!
116
		updateSettings(array('banLastUpdated' => time()));
117
118
		// Some members might be unbanned now. Update the members table.
119
		updateBanMembers();
120
	}
121
122
	// Create a date string so we don't overload them with date info.
123
	if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0]))
124
		$context['ban_time_format'] = $user_info['time_format'];
125
	else
126
		$context['ban_time_format'] = $matches[0];
127
128
	$listOptions = array(
129
		'id' => 'ban_list',
130
		'title' => $txt['ban_title'],
131
		'items_per_page' => $modSettings['defaultMaxListItems'],
132
		'base_href' => $scripturl . '?action=admin;area=ban;sa=list',
133
		'default_sort_col' => 'added',
134
		'default_sort_dir' => 'desc',
135
		'get_items' => array(
136
			'function' => 'list_getBans',
137
		),
138
		'get_count' => array(
139
			'function' => 'list_getNumBans',
140
		),
141
		'no_items_label' => $txt['ban_no_entries'],
142
		'columns' => array(
143
			'name' => array(
144
				'header' => array(
145
					'value' => $txt['ban_name'],
146
				),
147
				'data' => array(
148
					'db' => 'name',
149
				),
150
				'sort' => array(
151
					'default' => 'bg.name',
152
					'reverse' => 'bg.name DESC',
153
				),
154
			),
155
			'notes' => array(
156
				'header' => array(
157
					'value' => $txt['ban_notes'],
158
				),
159
				'data' => array(
160
					'db' => 'notes',
161
					'class' => 'smalltext',
162
				),
163
				'sort' => array(
164
					'default' => 'LENGTH(bg.notes) > 0 DESC, bg.notes',
165
					'reverse' => 'LENGTH(bg.notes) > 0, bg.notes DESC',
166
				),
167
			),
168
			'reason' => array(
169
				'header' => array(
170
					'value' => $txt['ban_reason'],
171
				),
172
				'data' => array(
173
					'db' => 'reason',
174
					'class' => 'smalltext',
175
				),
176
				'sort' => array(
177
					'default' => 'LENGTH(bg.reason) > 0 DESC, bg.reason',
178
					'reverse' => 'LENGTH(bg.reason) > 0, bg.reason DESC',
179
				),
180
			),
181
			'added' => array(
182
				'header' => array(
183
					'value' => $txt['ban_added'],
184
				),
185
				'data' => array(
186
					'function' => function($rowData) use ($context)
187
					{
188
						return timeformat($rowData['ban_time'], empty($context['ban_time_format']) ? true : $context['ban_time_format']);
189
					},
190
				),
191
				'sort' => array(
192
					'default' => 'bg.ban_time',
193
					'reverse' => 'bg.ban_time DESC',
194
				),
195
			),
196
			'expires' => array(
197
				'header' => array(
198
					'value' => $txt['ban_expires'],
199
				),
200
				'data' => array(
201
					'function' => function($rowData) use ($txt)
202
					{
203
						// This ban never expires...whahaha.
204
						if ($rowData['expire_time'] === null)
205
							return $txt['never'];
206
207
						// This ban has already expired.
208
						elseif ($rowData['expire_time'] < time())
209
							return sprintf('<span class="red">%1$s</span>', $txt['ban_expired']);
210
211
						// Still need to wait a few days for this ban to expire.
212
						else
213
							return sprintf('%1$d&nbsp;%2$s', ceil(($rowData['expire_time'] - time()) / (60 * 60 * 24)), $txt['ban_days']);
214
					},
215
				),
216
				'sort' => array(
217
					'default' => 'COALESCE(bg.expire_time, 1=1) DESC, bg.expire_time DESC',
218
					'reverse' => 'COALESCE(bg.expire_time, 1=1), bg.expire_time',
219
				),
220
			),
221
			'num_triggers' => array(
222
				'header' => array(
223
					'value' => $txt['ban_triggers'],
224
				),
225
				'data' => array(
226
					'db' => 'num_triggers',
227
				),
228
				'sort' => array(
229
					'default' => 'num_triggers DESC',
230
					'reverse' => 'num_triggers',
231
				),
232
			),
233
			'actions' => array(
234
				'header' => array(
235
					'value' => $txt['ban_actions'],
236
					'class' => 'centercol',
237
				),
238
				'data' => array(
239
					'sprintf' => array(
240
						'format' => '<a href="' . $scripturl . '?action=admin;area=ban;sa=edit;bg=%1$d">' . $txt['modify'] . '</a>',
241
						'params' => array(
242
							'id_ban_group' => false,
243
						),
244
					),
245
					'class' => 'centercol',
246
				),
247
			),
248
			'check' => array(
249
				'header' => array(
250
					'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check">',
251
					'class' => 'centercol',
252
				),
253
				'data' => array(
254
					'sprintf' => array(
255
						'format' => '<input type="checkbox" name="remove[]" value="%1$d" class="input_check">',
256
						'params' => array(
257
							'id_ban_group' => false,
258
						),
259
					),
260
					'class' => 'centercol',
261
				),
262
			),
263
		),
264
		'form' => array(
265
			'href' => $scripturl . '?action=admin;area=ban;sa=list',
266
		),
267
		'additional_rows' => array(
268
			array(
269
				'position' => 'top_of_list',
270
				'value' => '<input type="submit" name="removeBans" value="' . $txt['ban_remove_selected'] . '" data-confirm="' . $txt['ban_remove_selected_confirm'] . '" class="button_submit you_sure">',
271
			),
272
			array(
273
				'position' => 'bottom_of_list',
274
				'value' => '<input type="submit" name="removeBans" value="' . $txt['ban_remove_selected'] . '" data-confirm="' . $txt['ban_remove_selected_confirm'] . '" class="button_submit you_sure">',
275
			),
276
		),
277
	);
278
279
	require_once($sourcedir . '/Subs-List.php');
280
	createList($listOptions);
281
282
	$context['sub_template'] = 'show_list';
283
	$context['default_list'] = 'ban_list';
284
}
285
286
/**
287
 * Get bans, what else? For the given options.
288
 *
289
 * @param int $start Which item to start with (for pagination purposes)
290
 * @param int $items_per_page How many items to show on each page
291
 * @param string $sort A string telling ORDER BY how to sort the results
292
 * @return array An array of information about the bans for the list
293
 */
294 View Code Duplication
function list_getBans($start, $items_per_page, $sort)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in 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...
295
{
296
	global $smcFunc;
297
298
	$request = $smcFunc['db_query']('', '
299
		SELECT bg.id_ban_group, bg.name, bg.ban_time, bg.expire_time, bg.reason, bg.notes, COUNT(bi.id_ban) AS num_triggers
300
		FROM {db_prefix}ban_groups AS bg
301
			LEFT JOIN {db_prefix}ban_items AS bi ON (bi.id_ban_group = bg.id_ban_group)
302
		GROUP BY bg.id_ban_group, bg.name, bg.ban_time, bg.expire_time, bg.reason, bg.notes
303
		ORDER BY {raw:sort}
304
		LIMIT {int:offset}, {int:limit}',
305
		array(
306
			'sort' => $sort,
307
			'offset' => $start,
308
			'limit' => $items_per_page,
309
		)
310
	);
311
	$bans = array();
312
	while ($row = $smcFunc['db_fetch_assoc']($request))
313
		$bans[] = $row;
314
315
	$smcFunc['db_free_result']($request);
316
317
	return $bans;
318
}
319
320
/**
321
 * Get the total number of ban from the ban group table
322
 *
323
 * @return int The total number of bans
324
 */
325
function list_getNumBans()
326
{
327
	global $smcFunc;
328
329
	$request = $smcFunc['db_query']('', '
330
		SELECT COUNT(*) AS num_bans
331
		FROM {db_prefix}ban_groups',
332
		array(
333
		)
334
	);
335
	list ($numBans) = $smcFunc['db_fetch_row']($request);
336
	$smcFunc['db_free_result']($request);
337
338
	return $numBans;
339
}
340
341
/**
342
 * This function is behind the screen for adding new bans and modifying existing ones.
343
 * Adding new bans:
344
 * 	- is accessed by ?action=admin;area=ban;sa=add.
345
 * 	- uses the ban_edit sub template of the ManageBans template.
346
 * Modifying existing bans:
347
 *  - is accessed by ?action=admin;area=ban;sa=edit;bg=x
348
 *  - uses the ban_edit sub template of the ManageBans template.
349
 *  - shows a list of ban triggers for the specified ban.
350
 */
351
function BanEdit()
352
{
353
	global $txt, $modSettings, $context, $scripturl, $smcFunc, $sourcedir;
354
355
	if ((isset($_POST['add_ban']) || isset($_POST['modify_ban']) || isset($_POST['remove_selection'])) && empty($context['ban_errors']))
356
		BanEdit2();
357
358
	$ban_group_id = isset($context['ban']['id']) ? $context['ban']['id'] : (isset($_REQUEST['bg']) ? (int) $_REQUEST['bg'] : 0);
359
360
	// Template needs this to show errors using javascript
361
	loadLanguage('Errors');
362
	createToken('admin-bet');
363
	$context['form_url'] = $scripturl . '?action=admin;area=ban;sa=edit';
364
365
	if (!empty($context['ban_errors']))
366
	{
367
		foreach ($context['ban_errors'] as $error)
0 ignored issues
show
Bug introduced by
The expression $context['ban_errors'] of type string is not traversable.
Loading history...
368
			$context['error_messages'][$error] = $txt[$error];
369
	}
370
	else
371
	{
372
		// If we're editing an existing ban, get it from the database.
373
		if (!empty($ban_group_id))
374
		{
375
			$context['ban_group_id'] = $ban_group_id;
376
377
			// We're going to want this for making our list.
378
			require_once($sourcedir . '/Subs-List.php');
379
380
			$listOptions = array(
381
				'id' => 'ban_items',
382
				'base_href' => $scripturl . '?action=admin;area=ban;sa=edit;bg=' . $ban_group_id,
383
				'no_items_label' => $txt['ban_no_triggers'],
384
				'items_per_page' => $modSettings['defaultMaxListItems'],
385
				'get_items' => array(
386
					'function' => 'list_getBanItems',
387
					'params' => array(
388
						'ban_group_id' => $ban_group_id,
389
					),
390
				),
391
				'get_count' => array(
392
					'function' => 'list_getNumBanItems',
393
					'params' => array(
394
						'ban_group_id' => $ban_group_id,
395
					),
396
				),
397
				'columns' => array(
398
					'type' => array(
399
						'header' => array(
400
							'value' => $txt['ban_banned_entity'],
401
							'style' => 'width: 60%;text-align: left;',
402
						),
403
						'data' => array(
404
							'function' => function($ban_item) use ($txt)
405
							{
406
								if (in_array($ban_item['type'], array('ip', 'hostname', 'email')))
407
									return '<strong>' . $txt[$ban_item['type']] . ':</strong>&nbsp;' . $ban_item[$ban_item['type']];
408
								elseif ($ban_item['type'] == 'user')
409
									return '<strong>' . $txt['username'] . ':</strong>&nbsp;' . $ban_item['user']['link'];
410
								else
411
									return '<strong>' . $txt['unknown'] . ':</strong>&nbsp;' . $ban_item['no_bantype_selected'];
412
							},
413
							'style' => 'text-align: left;',
414
						),
415
					),
416
					'hits' => array(
417
						'header' => array(
418
							'value' => $txt['ban_hits'],
419
							'style' => 'width: 15%; text-align: center;',
420
						),
421
						'data' => array(
422
							'db' => 'hits',
423
							'style' => 'text-align: center;',
424
						),
425
					),
426
					'id' => array(
427
						'header' => array(
428
							'value' => $txt['ban_actions'],
429
							'style' => 'width: 15%; text-align: center;',
430
						),
431
						'data' => array(
432
							'function' => function($ban_item) use ($txt, $context, $scripturl)
433
							{
434
								return '<a href="' . $scripturl . '?action=admin;area=ban;sa=edittrigger;bg=' . $context['ban_group_id'] . ';bi=' . $ban_item['id'] . '">' . $txt['ban_edit_trigger'] . '</a>';
435
							},
436
							'style' => 'text-align: center;',
437
						),
438
					),
439
					'checkboxes' => array(
440
						'header' => array(
441
							'value' => '<input type="checkbox" onclick="invertAll(this, this.form, \'ban_items\');" class="input_check">',
442
							'style' => 'width: 5%; text-align: center;',
443
						),
444
						'data' => array(
445
							'sprintf' => array(
446
								'format' => '<input type="checkbox" name="ban_items[]" value="%1$d" class="input_check">',
447
								'params' => array(
448
									'id' => false,
449
								),
450
							),
451
							'style' => 'text-align: center;',
452
						),
453
					),
454
				),
455
				'form' => array(
456
					'href' => $scripturl . '?action=admin;area=ban;sa=edit;bg=' . $ban_group_id,
457
				),
458
				'additional_rows' => array(
459
					array(
460
						'position' => 'above_table_headers',
461
						'value' => '
462
						<input type="submit" name="remove_selection" value="' . $txt['ban_remove_selected_triggers'] . '" class="button_submit"> <a class="button_link" href="' . $scripturl . '?action=admin;area=ban;sa=edittrigger;bg=' . $ban_group_id . '">' . $txt['ban_add_trigger'] . '</a>',
463
						'style' => 'text-align: right;',
464
					),
465
					array(
466
						'position' => 'above_table_headers',
467
						'value' => '
468
						<input type="hidden" name="bg" value="' . $ban_group_id . '">
469
						<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '">
470
						<input type="hidden" name="' . $context['admin-bet_token_var'] . '" value="' . $context['admin-bet_token'] . '">',
471
					),
472
					array(
473
						'position' => 'below_table_data',
474
						'value' => '
475
						<input type="submit" name="remove_selection" value="' . $txt['ban_remove_selected_triggers'] . '" class="button_submit"> <a class="button_link" href="' . $scripturl . '?action=admin;area=ban;sa=edittrigger;bg=' . $ban_group_id . '">' . $txt['ban_add_trigger'] . '</a>',
476
						'style' => 'text-align: right;',
477
					),
478
					array(
479
						'position' => 'below_table_data',
480
						'value' => '
481
						<input type="hidden" name="bg" value="' . $ban_group_id . '">
482
						<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '">
483
						<input type="hidden" name="' . $context['admin-bet_token_var'] . '" value="' . $context['admin-bet_token'] . '">',
484
					),
485
				),
486
			);
487
			createList($listOptions);
488
		}
489
		// Not an existing one, then it's probably a new one.
490
		else
491
		{
492
			$context['ban'] = array(
493
				'id' => 0,
494
				'name' => '',
495
				'expiration' => array(
496
					'status' => 'never',
497
					'days' => 0
498
				),
499
				'reason' => '',
500
				'notes' => '',
501
				'ban_days' => 0,
502
				'cannot' => array(
503
					'access' => true,
504
					'post' => false,
505
					'register' => false,
506
					'login' => false,
507
				),
508
				'is_new' => true,
509
			);
510
			$context['ban_suggestions'] = array(
511
				'main_ip' => '',
512
				'hostname' => '',
513
				'email' => '',
514
				'member' => array(
515
					'id' => 0,
516
				),
517
			);
518
519
			// Overwrite some of the default form values if a user ID was given.
520
			if (!empty($_REQUEST['u']))
521
			{
522
				$request = $smcFunc['db_query']('', '
523
					SELECT id_member, real_name, member_ip, email_address
524
					FROM {db_prefix}members
525
					WHERE id_member = {int:current_user}
526
					LIMIT 1',
527
					array(
528
						'current_user' => (int) $_REQUEST['u'],
529
					)
530
				);
531
				if ($smcFunc['db_num_rows']($request) > 0)
532
				{
533
					list ($context['ban_suggestions']['member']['id'], $context['ban_suggestions']['member']['name'], $context['ban_suggestions']['main_ip'], $context['ban_suggestions']['email']) = $smcFunc['db_fetch_row']($request);
534
					$context['ban_suggestions']['main_ip'] = inet_dtop($context['ban_suggestions']['main_ip']);
535
				}
536
				$smcFunc['db_free_result']($request);
537
538
				if (!empty($context['ban_suggestions']['member']['id']))
539
				{
540
					$context['ban_suggestions']['href'] = $scripturl . '?action=profile;u=' . $context['ban_suggestions']['member']['id'];
541
					$context['ban_suggestions']['member']['link'] = '<a href="' . $context['ban_suggestions']['href'] . '">' . $context['ban_suggestions']['member']['name'] . '</a>';
542
543
					// Default the ban name to the name of the banned member.
544
					$context['ban']['name'] = $context['ban_suggestions']['member']['name'];
545
					// @todo: there should be a better solution...used to lock the "Ban on Username" input when banning from profile
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...
546
					$context['ban']['from_user'] = true;
547
548
					// Would be nice if we could also ban the hostname.
549
					if ((preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $context['ban_suggestions']['main_ip']) == 1 || isValidIPv6($context['ban_suggestions']['main_ip'])) && empty($modSettings['disableHostnameLookup']))
550
						$context['ban_suggestions']['hostname'] = host_from_ip($context['ban_suggestions']['main_ip']);
551
552
					$context['ban_suggestions']['other_ips'] = banLoadAdditionalIPs($context['ban_suggestions']['member']['id']);
553
				}
554
			}
555
556
			// We come from the mod center.
557
			elseif (isset($_GET['msg']) && !empty($_GET['msg']))
558
			{
559
				$request = $smcFunc['db_query']('', '
560
					SELECT poster_name, poster_ip, poster_email
561
					FROM {db_prefix}messages
562
					WHERE id_msg = {int:message}
563
					LIMIT 1',
564
					array(
565
						'message' => (int) $_REQUEST['msg'],
566
					)
567
				);
568
				if ($smcFunc['db_num_rows']($request) > 0)
569
				{
570
					list ($context['ban_suggestions']['member']['name'], $context['ban_suggestions']['main_ip'], $context['ban_suggestions']['email']) = $smcFunc['db_fetch_row']($request);
571
					$context['ban_suggestions']['main_ip'] = inet_dtop($context['ban_suggestions']['main_ip']);
572
				}
573
				$smcFunc['db_free_result']($request);
574
575
				// Can't hurt to ban base on the guest name...
576
				$context['ban']['name'] = $context['ban_suggestions']['member']['name'];
577
				$context['ban']['from_user'] = true;
578
			}
579
		}
580
	}
581
582
	loadJavaScriptFile('suggest.js', array(), 'smf_suggest');
583
	$context['sub_template'] = 'ban_edit';
584
585
}
586
587
/**
588
 * Retrieves all the ban items belonging to a certain ban group
589
 *
590
 * @param int $start Which item to start with (for pagination purposes)
591
 * @param int $items_per_page How many items to show on each page
592
 * @param int $sort Not used here
593
 * @param int $ban_group_id The ID of the group to get the bans for
594
 * @return array An array with information about the returned ban items
595
 */
596
function list_getBanItems($start = 0, $items_per_page = 0, $sort = 0, $ban_group_id = 0)
0 ignored issues
show
Unused Code introduced by
The parameter $sort 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...
597
{
598
	global $context, $smcFunc, $scripturl;
599
600
	$ban_items = array();
601
	$request = $smcFunc['db_query']('', '
602
		SELECT
603
			bi.id_ban, bi.hostname, bi.email_address, bi.id_member, bi.hits,
604
			bi.ip_low, bi.ip_high,
605
			bg.id_ban_group, bg.name, bg.ban_time, bg.expire_time, bg.reason, bg.notes, bg.cannot_access, bg.cannot_register, bg.cannot_login, bg.cannot_post,
606
			COALESCE(mem.id_member, 0) AS id_member, mem.member_name, mem.real_name
607
		FROM {db_prefix}ban_groups AS bg
608
			LEFT JOIN {db_prefix}ban_items AS bi ON (bi.id_ban_group = bg.id_ban_group)
609
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = bi.id_member)
610
		WHERE bg.id_ban_group = {int:current_ban}
611
		LIMIT {int:start}, {int:items_per_page}',
612
		array(
613
			'current_ban' => $ban_group_id,
614
			'start' => $start,
615
			'items_per_page' => $items_per_page,
616
		)
617
	);
618
	if ($smcFunc['db_num_rows']($request) == 0)
619
		fatal_lang_error('ban_not_found', false);
620
621
	while ($row = $smcFunc['db_fetch_assoc']($request))
622
	{
623
		if (!isset($context['ban']))
624
		{
625
			$context['ban'] = array(
626
				'id' => $row['id_ban_group'],
627
				'name' => $row['name'],
628
				'expiration' => array(
629
					'status' => $row['expire_time'] === null ? 'never' : ($row['expire_time'] < time() ? 'expired' : 'one_day'),
630
					'days' => $row['expire_time'] > time() ? floor(($row['expire_time'] - time()) / 86400) : 0
631
				),
632
				'reason' => $row['reason'],
633
				'notes' => $row['notes'],
634
				'cannot' => array(
635
					'access' => !empty($row['cannot_access']),
636
					'post' => !empty($row['cannot_post']),
637
					'register' => !empty($row['cannot_register']),
638
					'login' => !empty($row['cannot_login']),
639
				),
640
				'is_new' => false,
641
				'hostname' => '',
642
				'email' => '',
643
			);
644
		}
645
646
		if (!empty($row['id_ban']))
647
		{
648
			$ban_items[$row['id_ban']] = array(
649
				'id' => $row['id_ban'],
650
				'hits' => $row['hits'],
651
			);
652
			if (!empty($row['ip_high']))
653
			{
654
				$ban_items[$row['id_ban']]['type'] = 'ip';
655
				$ban_items[$row['id_ban']]['ip'] = range2ip($row['ip_low'], $row['ip_high']);
656
			}
657 View Code Duplication
			elseif (!empty($row['hostname']))
658
			{
659
				$ban_items[$row['id_ban']]['type'] = 'hostname';
660
				$ban_items[$row['id_ban']]['hostname'] = str_replace('%', '*', $row['hostname']);
661
			}
662 View Code Duplication
			elseif (!empty($row['email_address']))
663
			{
664
				$ban_items[$row['id_ban']]['type'] = 'email';
665
				$ban_items[$row['id_ban']]['email'] = str_replace('%', '*', $row['email_address']);
666
			}
667
			elseif (!empty($row['id_member']))
668
			{
669
				$ban_items[$row['id_ban']]['type'] = 'user';
670
				$ban_items[$row['id_ban']]['user'] = array(
671
					'id' => $row['id_member'],
672
					'name' => $row['real_name'],
673
					'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
674
					'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>',
675
				);
676
			}
677
			// Invalid ban (member probably doesn't exist anymore).
678
			else
679
			{
680
				unset($ban_items[$row['id_ban']]);
681
				removeBanTriggers($row['id_ban']);
682
			}
683
		}
684
	}
685
	$smcFunc['db_free_result']($request);
686
687
	return $ban_items;
688
}
689
690
/**
691
 * Gets the number of ban items belonging to a certain ban group
692
 *
693
 * @return int The number of ban items
694
 */
695
function list_getNumBanItems()
696
{
697
	global $smcFunc, $context;
698
699
	$ban_group_id = isset($context['ban_group_id']) ? $context['ban_group_id'] : 0;
700
701
	$request = $smcFunc['db_query']('', '
702
		SELECT COUNT(bi.id_ban)
703
		FROM {db_prefix}ban_groups AS bg
704
			LEFT JOIN {db_prefix}ban_items AS bi ON (bi.id_ban_group = bg.id_ban_group)
705
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = bi.id_member)
706
		WHERE bg.id_ban_group = {int:current_ban}',
707
		array(
708
			'current_ban' => $ban_group_id,
709
		)
710
	);
711
	list($banNumber) = $smcFunc['db_fetch_row']($request);
712
	$smcFunc['db_free_result']($request);
713
714
	return $banNumber;
715
}
716
717
/**
718
 * Finds additional IPs related to a certain user
719
 *
720
 * @param int $member_id The ID of the member to get additional IPs for
721
 * @return array An containing two arrays - ips_in_messages (IPs used in posts) and ips_in_errors (IPs used in error messages)
722
 */
723
function banLoadAdditionalIPs($member_id)
724
{
725
	// Borrowing a few language strings from profile.
726
	loadLanguage('Profile');
727
728
	$search_list = array();
729
	call_integration_hook('integrate_load_addtional_ip_ban', array(&$search_list));
730
	$search_list += array('ips_in_messages' => 'banLoadAdditionalIPsMember', 'ips_in_errors' => 'banLoadAdditionalIPsError');
731
732
	$return = array();
733
	foreach ($search_list as $key => $callable)
734
		if (is_callable($callable))
735
			$return[$key] = call_user_func($callable, $member_id);
736
737
	return $return;
738
}
739
740
/**
741
 * @param int $member_id The ID of the member
742
 * @return array An array of IPs used in posts by this member
743
 */
744 View Code Duplication
function banLoadAdditionalIPsMember($member_id)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in 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...
745
{
746
	global $smcFunc;
747
748
	// Find some additional IP's used by this member.
749
	$message_ips = array();
750
	$request = $smcFunc['db_query']('', '
751
		SELECT DISTINCT poster_ip
752
		FROM {db_prefix}messages
753
		WHERE id_member = {int:current_user}
754
			AND poster_ip IS NOT NULL
755
		ORDER BY poster_ip',
756
		array(
757
			'current_user' => $member_id,
758
		)
759
	);
760
	while ($row = $smcFunc['db_fetch_assoc']($request))
761
		$message_ips[] = inet_dtop($row['poster_ip']);
762
	$smcFunc['db_free_result']($request);
763
764
	return $message_ips;
765
}
766
767
/**
768
 * @param int $member_id The ID of the member
769
 * @return array An array of IPs associated with error messages generated by this user
770
 */
771
function banLoadAdditionalIPsError($member_id)
772
{
773
	global $smcFunc;
774
775
	$error_ips = array();
776
	$request = $smcFunc['db_query']('', '
777
		SELECT DISTINCT ip
778
		FROM {db_prefix}log_errors
779
		WHERE id_member = {int:current_user}
780
			AND ip IS NOT NULL
781
		ORDER BY ip',
782
		array(
783
			'current_user' => $member_id,
784
		)
785
	);
786
	while ($row = $smcFunc['db_fetch_assoc']($request))
787
	    $error_ips[] = inet_dtop($row['ip']);
788
	$smcFunc['db_free_result']($request);
789
790
	return $error_ips;
791
}
792
793
/**
794
 * This function handles submitted forms that add, modify or remove ban triggers.
795
 */
796
function banEdit2()
797
{
798
	global $smcFunc, $context;
799
800
	checkSession();
801
	validateToken('admin-bet');
802
803
	$context['ban_errors'] = array();
804
805
	// Adding or editing a ban group
806
	if (isset($_POST['add_ban']) || isset($_POST['modify_ban']))
807
	{
808
		// Let's collect all the information we need
809
		$ban_info['id'] = isset($_REQUEST['bg']) ? (int) $_REQUEST['bg'] : 0;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$ban_info was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ban_info = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
810
		$ban_info['is_new'] = empty($ban_info['id']);
811
		$ban_info['expire_date'] = !empty($_POST['expire_date']) ? (int) $_POST['expire_date'] : 0;
812
		$ban_info['expiration'] = array(
813
			'status' => isset($_POST['expiration']) && in_array($_POST['expiration'], array('never', 'one_day', 'expired')) ? $_POST['expiration'] : 'never',
814
			'days' => $ban_info['expire_date'],
815
		);
816
		$ban_info['db_expiration'] = $ban_info['expiration']['status'] == 'never' ? 'NULL' : ($ban_info['expiration']['status'] == 'one_day' ? time() + 24 * 60 * 60 * $ban_info['expire_date'] : 0);
817
		$ban_info['full_ban'] = empty($_POST['full_ban']) ? 0 : 1;
818
		$ban_info['reason'] = !empty($_POST['reason']) ? $smcFunc['htmlspecialchars']($_POST['reason'], ENT_QUOTES) : '';
819
		$ban_info['name'] = !empty($_POST['ban_name']) ? $smcFunc['htmlspecialchars']($_POST['ban_name'], ENT_QUOTES) : '';
820
		$ban_info['notes'] = isset($_POST['notes']) ? $smcFunc['htmlspecialchars']($_POST['notes'], ENT_QUOTES) : '';
821
		$ban_info['notes'] = str_replace(array("\r", "\n", '  '), array('', '<br>', '&nbsp; '), $ban_info['notes']);
822
		$ban_info['cannot']['access'] = empty($ban_info['full_ban']) ? 0 : 1;
823
		$ban_info['cannot']['post'] = !empty($ban_info['full_ban']) || empty($_POST['cannot_post']) ? 0 : 1;
824
		$ban_info['cannot']['register'] = !empty($ban_info['full_ban']) || empty($_POST['cannot_register']) ? 0 : 1;
825
		$ban_info['cannot']['login'] = !empty($ban_info['full_ban']) || empty($_POST['cannot_login']) ? 0 : 1;
826
827
		// Adding a new ban group
828
		if (empty($_REQUEST['bg']))
829
			$ban_group_id = insertBanGroup($ban_info);
830
		// Editing an existing ban group
831
		else
832
			$ban_group_id = updateBanGroup($ban_info);
833
834
		if (is_numeric($ban_group_id))
835
		{
836
			$ban_info['id'] = $ban_group_id;
837
			$ban_info['is_new'] = false;
838
		}
839
840
		$context['ban'] = $ban_info;
841
	}
842
843
	if (isset($_POST['ban_suggestions']))
844
		// @TODO: is $_REQUEST['bi'] ever set?
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% 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...
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...
845
		$saved_triggers = saveTriggers($_POST['ban_suggestions'], $ban_info['id'], isset($_REQUEST['u']) ? (int) $_REQUEST['u'] : 0, isset($_REQUEST['bi']) ? (int) $_REQUEST['bi'] : 0);
0 ignored issues
show
Bug introduced by
The variable $ban_info 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...
846
847
	// Something went wrong somewhere... Oh well, let's go back.
848
	if (!empty($context['ban_errors']))
849
	{
850
		$context['ban_suggestions'] = !empty($saved_triggers) ? $saved_triggers : array();
851
		$context['ban']['from_user'] = true;
852
		$context['ban_suggestions'] = array_merge($context['ban_suggestions'], getMemberData((int) $_REQUEST['u']));
853
854
		// Not strictly necessary, but it's nice
855
		if (!empty($context['ban_suggestions']['member']['id']))
856
			$context['ban_suggestions']['other_ips'] = banLoadAdditionalIPs($context['ban_suggestions']['member']['id']);
857
		return BanEdit();
858
	}
859
	$context['ban_suggestions']['saved_triggers'] = !empty($saved_triggers) ? $saved_triggers : array();
860
861
	if (isset($_POST['ban_items']))
862
	{
863
		$ban_group_id = isset($_REQUEST['bg']) ? (int) $_REQUEST['bg'] : 0;
864
		array_map('intval', $_POST['ban_items']);
865
866
		removeBanTriggers($_POST['ban_items'], $ban_group_id);
867
	}
868
869
	// Register the last modified date.
870
	updateSettings(array('banLastUpdated' => time()));
871
872
	// Update the member table to represent the new ban situation.
873
	updateBanMembers();
874
	redirectexit('action=admin;area=ban;sa=edit;bg=' . $ban_group_id);
0 ignored issues
show
Bug introduced by
The variable $ban_group_id 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...
875
}
876
877
/**
878
 * Saves one or more ban triggers into a ban item: according to the suggestions
879
 * checks the $_POST variable to verify if the trigger is present
880
 *
881
 * @param array $suggestions An array of suggestedtriggers (IP, email, etc.)
882
 * @param int $ban_group The ID of the group we're saving bans for
883
 * @param int $member The ID of the member associated with this ban (if applicable)
884
 * @param int $ban_id The ID of the ban (0 if this is a new ban)
885
 *
886
 * @return array|bool An array with the triggers if there were errors or false on success
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
887
 */
888
function saveTriggers($suggestions = array(), $ban_group, $member = 0, $ban_id = 0)
0 ignored issues
show
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
889
{
890
	global $context;
891
892
	$triggers = array(
893
		'main_ip' => '',
894
		'hostname' => '',
895
		'email' => '',
896
		'member' => array(
897
			'id' => $member,
898
		)
899
	);
900
901
	foreach ($suggestions as $key => $value)
902
	{
903
		if (is_array($value))
904
			$triggers[$key] = $value;
905
		else
906
			$triggers[$value] = !empty($_POST[$value]) ? $_POST[$value] : '';
907
	}
908
909
	$ban_triggers = validateTriggers($triggers);
910
911
	// Time to save!
912
	if (!empty($ban_triggers['ban_triggers']) && empty($context['ban_errors']))
913
	{
914
		if (empty($ban_id))
915
			addTriggers($ban_group, $ban_triggers['ban_triggers'], $ban_triggers['log_info']);
916
		else
917
			updateTriggers($ban_id, $ban_group, array_shift($ban_triggers['ban_triggers']), $ban_triggers['log_info']);
918
	}
919
	if (!empty($context['ban_errors']))
920
		return $triggers;
921
	else
922
		return false;
923
}
924
925
/**
926
 * This function removes a bunch of triggers based on ids
927
 * Doesn't clean the inputs
928
 *
929
 * @param array $items_ids The items to remove
930
 * @param bool|int $group_id The ID of the group these triggers are associated with or false if deleting them from all groups
931
 * @return bool Always returns true
932
 */
933
function removeBanTriggers($items_ids = array(), $group_id = false)
934
{
935
	global $smcFunc, $scripturl;
936
937
	if ($group_id !== false)
938
		$group_id = (int) $group_id;
939
940
	if (empty($group_id) && empty($items_ids))
941
		return false;
942
943
	if (!is_array($items_ids))
944
		$items_ids = array($items_ids);
945
946
	$log_info = array();
947
	$ban_items = array();
948
949
	// First order of business: Load up the info so we can log this...
950
	$request = $smcFunc['db_query']('', '
951
		SELECT
952
			bi.id_ban, bi.hostname, bi.email_address, bi.id_member, bi.hits,
953
			bi.ip_low, bi.ip_high,
954
			COALESCE(mem.id_member, 0) AS id_member, mem.member_name, mem.real_name
955
		FROM {db_prefix}ban_items AS bi
956
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = bi.id_member)
957
		WHERE bi.id_ban IN ({array_int:ban_list})',
958
		array(
959
			'ban_list' => $items_ids,
960
		)
961
	);
962
963
	// Get all the info for the log
964
	while ($row = $smcFunc['db_fetch_assoc']($request))
965
	{
966
		if (!empty($row['id_ban']))
967
		{
968
			$ban_items[$row['id_ban']] = array(
969
				'id' => $row['id_ban'],
970
			);
971
			if (!empty($row['ip_high']))
972
			{
973
				$ban_items[$row['id_ban']]['type'] = 'ip';
974
				$ban_items[$row['id_ban']]['ip'] = range2ip($row['ip_low'], $row['ip_high']);
975
976
				$is_range = (strpos($ban_items[$row['id_ban']]['ip'], '-') !== false || strpos($ban_items[$row['id_ban']]['ip'], '*') !== false);
977
978
				$log_info[] = array(
979
					'bantype' => ($is_range ? 'ip_range' : 'main_ip'),
980
					'value' => $ban_items[$row['id_ban']]['ip'],
981
				);
982
			}
983 View Code Duplication
			elseif (!empty($row['hostname']))
984
			{
985
				$ban_items[$row['id_ban']]['type'] = 'hostname';
986
				$ban_items[$row['id_ban']]['hostname'] = str_replace('%', '*', $row['hostname']);
987
				$log_info[] = array(
988
					'bantype' => 'hostname',
989
					'value' => $row['hostname'],
990
				);
991
			}
992 View Code Duplication
			elseif (!empty($row['email_address']))
993
			{
994
				$ban_items[$row['id_ban']]['type'] = 'email';
995
				$ban_items[$row['id_ban']]['email'] = str_replace('%', '*', $row['email_address']);
996
				$log_info[] = array(
997
					'bantype' => 'email',
998
					'value' => $ban_items[$row['id_ban']]['email'],
999
				);
1000
			}
1001
			elseif (!empty($row['id_member']))
1002
			{
1003
				$ban_items[$row['id_ban']]['type'] = 'user';
1004
				$ban_items[$row['id_ban']]['user'] = array(
1005
					'id' => $row['id_member'],
1006
					'name' => $row['real_name'],
1007
					'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
1008
					'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>',
1009
				);
1010
				$log_info[] = array(
1011
					'bantype' => 'user',
1012
					'value' => $row['id_member'],
1013
				);
1014
			}
1015
		}
1016
	}
1017
1018
	// Log this!
1019
	logTriggersUpdates($log_info, false, true);
1020
1021
	$smcFunc['db_free_result']($request);
1022
1023
	if ($group_id !== false)
1024
	{
1025
		$smcFunc['db_query']('', '
1026
			DELETE FROM {db_prefix}ban_items
1027
			WHERE id_ban IN ({array_int:ban_list})
1028
				AND id_ban_group = {int:ban_group}',
1029
			array(
1030
				'ban_list' => $items_ids,
1031
				'ban_group' => $group_id,
1032
			)
1033
		);
1034
	}
1035
	elseif (!empty($items_ids))
1036
	{
1037
		$smcFunc['db_query']('', '
1038
			DELETE FROM {db_prefix}ban_items
1039
			WHERE id_ban IN ({array_int:ban_list})',
1040
			array(
1041
				'ban_list' => $items_ids,
1042
			)
1043
		);
1044
	}
1045
1046
	return true;
1047
}
1048
1049
/**
1050
 * This function removes a bunch of ban groups based on ids
1051
 * Doesn't clean the inputs
1052
 *
1053
 * @param int[] $group_ids The IDs of the groups to remove
1054
 * @return bool Returns ture if successful or false if $group_ids is empty
1055
 */
1056
function removeBanGroups($group_ids)
1057
{
1058
	global $smcFunc;
1059
1060
	if (!is_array($group_ids))
1061
		$group_ids = array($group_ids);
1062
1063
	$group_ids = array_unique($group_ids);
1064
1065
	if (empty($group_ids))
1066
		return false;
1067
1068
	$smcFunc['db_query']('', '
1069
		DELETE FROM {db_prefix}ban_groups
1070
		WHERE id_ban_group IN ({array_int:ban_list})',
1071
		array(
1072
			'ban_list' => $group_ids,
1073
		)
1074
	);
1075
1076
	return true;
1077
}
1078
1079
/**
1080
 * Removes logs - by default truncate the table
1081
 * Doesn't clean the inputs
1082
 *
1083
 * @param array $ids Empty array to clear the ban log or the IDs of the log entries to remove
1084
 * @return bool Returns true if successful or false if $ids is invalid
1085
 */
1086
function removeBanLogs($ids = array())
1087
{
1088
	global $smcFunc;
1089
1090
	if (empty($ids))
1091
		$smcFunc['db_query']('truncate_table', '
1092
			TRUNCATE {db_prefix}log_banned',
1093
			array(
1094
			)
1095
		);
1096
	else
1097
	{
1098
		if (!is_array($ids))
1099
			$ids = array($ids);
1100
1101
		$ids = array_unique($ids);
1102
1103
		if (empty($ids))
1104
			return false;
1105
1106
		$smcFunc['db_query']('', '
1107
			DELETE FROM {db_prefix}log_banned
1108
			WHERE id_ban_log IN ({array_int:ban_list})',
1109
			array(
1110
				'ban_list' => $ids,
1111
			)
1112
		);
1113
	}
1114
1115
	return true;
1116
}
1117
1118
/**
1119
 * This function validates the ban triggers
1120
 *
1121
 * Errors in $context['ban_errors']
1122
 *
1123
 * @param array $triggers The triggers to validate
1124
 * @return array An array of riggers and log info ready to be used
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
1125
 */
1126
function validateTriggers(&$triggers)
1127
{
1128
	global $context, $smcFunc;
1129
1130
	if (empty($triggers))
1131
		$context['ban_erros'][] = 'ban_empty_triggers';
1132
1133
	$ban_triggers = array();
1134
	$log_info = array();
1135
1136
	foreach ($triggers as $key => $value)
1137
	{
1138
		if (!empty($value))
1139
		{
1140
			if ($key == 'member')
1141
				continue;
1142
1143
			if ($key == 'main_ip')
1144
			{
1145
				$value = trim($value);
1146
				$ip_parts = ip2range($value);
1147
				if (!checkExistingTriggerIP($ip_parts, $value))
1148
					$context['ban_erros'][] = 'invalid_ip';
1149
				else
1150
				{
1151
					$ban_triggers['main_ip'] = array(
1152
						'ip_low' => $ip_parts['low'],
1153
						'ip_high' => $ip_parts['high']
1154
					);
1155
				}
1156
			}
1157
			elseif ($key == 'hostname')
1158
			{
1159
				if (preg_match('/[^\w.\-*]/', $value) == 1)
1160
					$context['ban_erros'][] = 'invalid_hostname';
1161
				else
1162
				{
1163
					// Replace the * wildcard by a MySQL wildcard %.
1164
					$value = substr(str_replace('*', '%', $value), 0, 255);
1165
1166
					$ban_triggers['hostname']['hostname'] = $value;
1167
				}
1168
			}
1169
			elseif ($key == 'email')
1170
			{
1171
				if (preg_match('/[^\w.\-\+*@]/', $value) == 1)
1172
					$context['ban_erros'][] = 'invalid_email';
1173
1174
				// Check the user is not banning an admin.
1175
				$request = $smcFunc['db_query']('', '
1176
					SELECT id_member
1177
					FROM {db_prefix}members
1178
					WHERE (id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0)
1179
						AND email_address LIKE {string:email}
1180
					LIMIT 1',
1181
					array(
1182
						'admin_group' => 1,
1183
						'email' => $value,
1184
					)
1185
				);
1186
				if ($smcFunc['db_num_rows']($request) != 0)
1187
					$context['ban_erros'][] = 'no_ban_admin';
1188
				$smcFunc['db_free_result']($request);
1189
1190
				$value = substr(strtolower(str_replace('*', '%', $value)), 0, 255);
1191
1192
				$ban_triggers['email']['email_address'] = $value;
1193
			}
1194
			elseif ($key == 'user')
1195
			{
1196
				$user = preg_replace('~&amp;#(\d{4,5}|[2-9]\d{2,4}|1[2-9]\d);~', '&#$1;', $smcFunc['htmlspecialchars']($value, ENT_QUOTES));
1197
1198
				$request = $smcFunc['db_query']('', '
1199
					SELECT id_member, (id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0) AS isAdmin
1200
					FROM {db_prefix}members
1201
					WHERE member_name = {string:username} OR real_name = {string:username}
1202
					LIMIT 1',
1203
					array(
1204
						'admin_group' => 1,
1205
						'username' => $user,
1206
					)
1207
				);
1208
				if ($smcFunc['db_num_rows']($request) == 0)
1209
					$context['ban_erros'][] = 'invalid_username';
1210
				list ($value, $isAdmin) = $smcFunc['db_fetch_row']($request);
1211
				$smcFunc['db_free_result']($request);
1212
1213
				if ($isAdmin && strtolower($isAdmin) != 'f')
1214
				{
1215
					unset($value);
1216
					$context['ban_erros'][] = 'no_ban_admin';
1217
				}
1218
				else
1219
					$ban_triggers['user']['id_member'] = $value;
1220
			}
1221
			elseif (in_array($key, array('ips_in_messages', 'ips_in_errors')))
1222
			{
1223
				// Special case, those two are arrays themselves
1224
				$values = array_unique($value);
1225
				// Don't add the main IP again.
1226
				if (isset($triggers['main_ip']))
1227
					$values = array_diff($values, array($triggers['main_ip']));
1228
				unset($value);
1229
				foreach ($values as $val)
1230
				{
1231
					$val = trim($val);
1232
					$ip_parts = ip2range($val);
1233
					if (!checkExistingTriggerIP($ip_parts, $val))
1234
						$context['ban_erros'][] = 'invalid_ip';
1235
					else
1236
					{
1237
						$ban_triggers[$key][] = array(
1238
							'ip_low' => $ip_parts['low'],
1239
							'ip_high' => $ip_parts['high'],
1240
						);
1241
1242
						$log_info[] = array(
1243
							'value' => $val,
1244
							'bantype' => 'ip_range',
1245
						);
1246
					}
1247
				}
1248
			}
1249
			else
1250
				$context['ban_erros'][] = 'no_bantype_selected';
1251
1252
			if (isset($value) && !is_array($value))
1253
				$log_info[] = array(
1254
					'value' => $value,
1255
					'bantype' => $key,
1256
				);
1257
		}
1258
	}
1259
	return array('ban_triggers' => $ban_triggers, 'log_info' => $log_info);
1260
}
1261
1262
/**
1263
 * This function actually inserts the ban triggers into the database
1264
 *
1265
 * Errors in $context['ban_errors']
1266
 *
1267
 * @param int $group_id The ID of the group to add the triggers to (0 to create a new one)
1268
 * @param array $triggers The triggers to add
1269
 * @param array $logs The log data
1270
 * @return bool Whether or not the action was successful
1271
 */
1272
function addTriggers($group_id = 0, $triggers = array(), $logs = array())
1273
{
1274
	global $smcFunc, $context;
1275
1276
	if (empty($group_id))
1277
		$context['ban_errors'][] = 'ban_id_empty';
1278
1279
	// Preset all values that are required.
1280
	$values = array(
1281
		'id_ban_group' => $group_id,
1282
		'hostname' => '',
1283
		'email_address' => '',
1284
		'id_member' => 0,
1285
		'ip_low' => 'null',
1286
		'ip_high' => 'null',
1287
	);
1288
1289
	$insertKeys = array(
1290
		'id_ban_group' => 'int',
1291
		'hostname' => 'string',
1292
		'email_address' => 'string',
1293
		'id_member' => 'int',
1294
		'ip_low' => 'inet',
1295
		'ip_high' => 'inet',
1296
	);
1297
1298
	$insertTriggers = array();
1299
	foreach ($triggers as $key => $trigger)
1300
	{
1301
		// Exceptions, exceptions, exceptions...always exceptions... :P
1302
		if (in_array($key, array('ips_in_messages', 'ips_in_errors')))
1303
			foreach ($trigger as $real_trigger)
1304
				$insertTriggers[] = array_merge($values, $real_trigger);
1305
		else
1306
			$insertTriggers[] = array_merge($values, $trigger);
1307
	}
1308
1309
	if (empty($insertTriggers))
1310
		$context['ban_errors'][] = 'ban_no_triggers';
1311
1312
	if (!empty($context['ban_errors']))
1313
		return false;
1314
1315
	$smcFunc['db_insert']('',
1316
		'{db_prefix}ban_items',
1317
		$insertKeys,
1318
		$insertTriggers,
1319
		array('id_ban')
1320
	);
1321
1322
	logTriggersUpdates($logs, true);
1323
1324
	return true;
1325
}
1326
1327
/**
1328
 * This function updates an existing ban trigger into the database
1329
 *
1330
 * Errors in $context['ban_errors']
1331
 *
1332
 * @param int $ban_item The ID of the ban item
1333
 * @param int $group_id The ID of the ban group
1334
 * @param array $trigger An array of triggers
1335
 * @param array $logs An array of log info
1336
 */
1337
function updateTriggers($ban_item = 0, $group_id = 0, $trigger = array(), $logs = array())
1338
{
1339
	global $smcFunc, $context;
1340
1341
	if (empty($ban_item))
1342
		$context['ban_errors'][] = 'ban_ban_item_empty';
1343
	if (empty($group_id))
1344
		$context['ban_errors'][] = 'ban_id_empty';
1345
	if (empty($trigger))
1346
		$context['ban_errors'][] = 'ban_no_triggers';
1347
1348
	if (!empty($context['ban_errors']))
1349
		return;
1350
1351
	// Preset all values that are required.
1352
	$values = array(
1353
		'id_ban_group' => $group_id,
1354
		'hostname' => '',
1355
		'email_address' => '',
1356
		'id_member' => 0,
1357
		'ip_low' => 'null',
1358
		'ip_high' => 'null',
1359
	);
1360
1361
	$trigger = array_merge($values, $trigger);
1362
1363
	$smcFunc['db_query']('', '
1364
		UPDATE {db_prefix}ban_items
1365
		SET
1366
			hostname = {string:hostname}, email_address = {string:email_address}, id_member = {int:id_member},
1367
			ip_low = {inet:ip_low}, ip_high = {inet:ip_high}
1368
		WHERE id_ban = {int:ban_item}
1369
			AND id_ban_group = {int:id_ban_group}',
1370
		array_merge($trigger, array(
1371
			'id_ban_group' => $group_id,
1372
			'ban_item' => $ban_item,
1373
		))
1374
	);
1375
1376
	logTriggersUpdates($logs, false);
1377
}
1378
1379
/**
1380
 * A small function to unify logging of triggers (updates and new)
1381
 *
1382
 * @param array $logs an array of logs, each log contains the following keys:
1383
 *                - bantype: a known type of ban (ip_range, hostname, email, user, main_ip)
1384
 *                - value: the value of the bantype (e.g. the IP or the email address banned)
1385
 * @param bool $new Whether the trigger is new or an update of an existing one
1386
 * @param bool $removal Whether the trigger is being deleted
1387
 */
1388
function logTriggersUpdates($logs, $new = true, $removal = false)
1389
{
1390
	if (empty($logs))
1391
		return;
1392
1393
	$log_name_map = array(
1394
		'main_ip' => 'ip_range',
1395
		'hostname' => 'hostname',
1396
		'email' => 'email',
1397
		'user' => 'member',
1398
		'ip_range' => 'ip_range',
1399
	);
1400
1401
	// Log the addion of the ban entries into the moderation log.
1402
	foreach ($logs as $log)
1403
		logAction('ban' . ($removal == true ? 'remove' : ''), array(
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1404
			$log_name_map[$log['bantype']] => $log['value'],
1405
			'new' => empty($new) ? 0 : 1,
1406
			'remove' => empty($removal) ? 0 : 1,
1407
			'type' => $log['bantype'],
1408
		));
1409
}
1410
1411
/**
1412
 * Updates an existing ban group
1413
 *
1414
 * Errors in $context['ban_errors']
1415
 *
1416
 * @param array $ban_info An array of info about the ban group. Should have name and may also have an id.
1417
 * @return int The ban group's ID
1418
 */
1419
function updateBanGroup($ban_info = array())
1420
{
1421
	global $smcFunc, $context;
1422
1423
	if (empty($ban_info['name']))
1424
		$context['ban_errors'][] = 'ban_name_empty';
1425
	if (empty($ban_info['id']))
1426
		$context['ban_errors'][] = 'ban_id_empty';
1427 View Code Duplication
	if (empty($ban_info['cannot']['access']) && empty($ban_info['cannot']['register']) && empty($ban_info['cannot']['post']) && empty($ban_info['cannot']['login']))
1428
		$context['ban_errors'][] = 'ban_unknown_restriction_type';
1429
1430
	if (!empty($ban_info['id']))
1431
	{
1432
		// Verify the ban group exists.
1433
		$request = $smcFunc['db_query']('', '
1434
			SELECT id_ban_group
1435
			FROM {db_prefix}ban_groups
1436
			WHERE id_ban_group = {int:ban_group}
1437
			LIMIT 1',
1438
			array(
1439
				'ban_group' => $ban_info['id']
1440
			)
1441
		);
1442
1443
		if ($smcFunc['db_num_rows']($request) == 0)
1444
			$context['ban_errors'][] = 'ban_not_found';
1445
		$smcFunc['db_free_result']($request);
1446
	}
1447
1448
	if (!empty($ban_info['name']))
1449
	{
1450
		// Make sure the name does not already exist (Of course, if it exists in the ban group we are editing, proceed.)
1451
		$request = $smcFunc['db_query']('', '
1452
			SELECT id_ban_group
1453
			FROM {db_prefix}ban_groups
1454
			WHERE name = {string:new_ban_name}
1455
				AND id_ban_group != {int:ban_group}
1456
			LIMIT 1',
1457
			array(
1458
				'ban_group' => empty($ban_info['id']) ? 0 : $ban_info['id'],
1459
				'new_ban_name' => $ban_info['name'],
1460
			)
1461
		);
1462
		if ($smcFunc['db_num_rows']($request) != 0)
1463
			$context['ban_errors'][] = 'ban_name_exists';
1464
		$smcFunc['db_free_result']($request);
1465
	}
1466
1467
	if (!empty($context['ban_errors']))
1468
		return $ban_info['id'];
1469
1470
	$smcFunc['db_query']('', '
1471
		UPDATE {db_prefix}ban_groups
1472
		SET
1473
			name = {string:ban_name},
1474
			reason = {string:reason},
1475
			notes = {string:notes},
1476
			expire_time = {raw:expiration},
1477
			cannot_access = {int:cannot_access},
1478
			cannot_post = {int:cannot_post},
1479
			cannot_register = {int:cannot_register},
1480
			cannot_login = {int:cannot_login}
1481
		WHERE id_ban_group = {int:id_ban_group}',
1482
		array(
1483
			'expiration' => $ban_info['db_expiration'],
1484
			'cannot_access' => $ban_info['cannot']['access'],
1485
			'cannot_post' => $ban_info['cannot']['post'],
1486
			'cannot_register' => $ban_info['cannot']['register'],
1487
			'cannot_login' => $ban_info['cannot']['login'],
1488
			'id_ban_group' => $ban_info['id'],
1489
			'ban_name' => $ban_info['name'],
1490
			'reason' => $ban_info['reason'],
1491
			'notes' => $ban_info['notes'],
1492
		)
1493
	);
1494
1495
	return $ban_info['id'];
1496
}
1497
1498
/**
1499
 * Creates a new ban group
1500
 * If the group is successfully created the ID is returned
1501
 * On error the error code is returned or false
1502
 *
1503
 * Errors in $context['ban_errors']
1504
 *
1505
 * @param array $ban_info An array containing 'name', which is the name of the ban group
1506
 * @return int The ban group's ID
1507
 */
1508
function insertBanGroup($ban_info = array())
1509
{
1510
	global $smcFunc, $context;
1511
1512
	if (empty($ban_info['name']))
1513
		$context['ban_errors'][] = 'ban_name_empty';
1514 View Code Duplication
	if (empty($ban_info['cannot']['access']) && empty($ban_info['cannot']['register']) && empty($ban_info['cannot']['post']) && empty($ban_info['cannot']['login']))
1515
		$context['ban_errors'][] = 'ban_unknown_restriction_type';
1516
1517
	if (!empty($ban_info['name']))
1518
	{
1519
		// Check whether a ban with this name already exists.
1520
		$request = $smcFunc['db_query']('', '
1521
			SELECT id_ban_group
1522
			FROM {db_prefix}ban_groups
1523
			WHERE name = {string:new_ban_name}' . '
1524
			LIMIT 1',
1525
			array(
1526
				'new_ban_name' => $ban_info['name'],
1527
			)
1528
		);
1529
1530
		if ($smcFunc['db_num_rows']($request) == 1)
1531
			$context['ban_errors'][] = 'ban_name_exists';
1532
		$smcFunc['db_free_result']($request);
1533
	}
1534
1535
	if (!empty($context['ban_errors']))
1536
		return;
1537
1538
	// Yes yes, we're ready to add now.
1539
	$ban_info['id'] = $smcFunc['db_insert']('',
1540
		'{db_prefix}ban_groups',
1541
		array(
1542
			'name' => 'string-20', 'ban_time' => 'int', 'expire_time' => 'raw', 'cannot_access' => 'int', 'cannot_register' => 'int',
1543
			'cannot_post' => 'int', 'cannot_login' => 'int', 'reason' => 'string-255', 'notes' => 'string-65534',
1544
		),
1545
		array(
1546
			$ban_info['name'], time(), $ban_info['db_expiration'], $ban_info['cannot']['access'], $ban_info['cannot']['register'],
1547
			$ban_info['cannot']['post'], $ban_info['cannot']['login'], $ban_info['reason'], $ban_info['notes'],
1548
		),
1549
		array('id_ban_group'),
1550
		1
1551
	);
1552
1553
	if (empty($ban_info['id']))
1554
		$context['ban_errors'][] = 'impossible_insert_new_bangroup';
1555
1556
	return $ban_info['id'];
1557
}
1558
1559
/**
1560
 * This function handles the ins and outs of the screen for adding new ban
1561
 * triggers or modifying existing ones.
1562
 * Adding new ban triggers:
1563
 * 	- is accessed by ?action=admin;area=ban;sa=edittrigger;bg=x
1564
 * 	- uses the ban_edit_trigger sub template of ManageBans.
1565
 * Editing existing ban triggers:
1566
 *  - is accessed by ?action=admin;area=ban;sa=edittrigger;bg=x;bi=y
1567
 *  - uses the ban_edit_trigger sub template of ManageBans.
1568
 */
1569
function BanEditTrigger()
1570
{
1571
	global $context, $smcFunc, $scripturl;
1572
1573
	$context['sub_template'] = 'ban_edit_trigger';
1574
	$context['form_url'] = $scripturl . '?action=admin;area=ban;sa=edittrigger';
1575
1576
	$ban_group = isset($_REQUEST['bg']) ? (int) $_REQUEST['bg'] : 0;
1577
	$ban_id = isset($_REQUEST['bi']) ? (int) $_REQUEST['bi'] : 0;
1578
1579
	if (empty($ban_group))
1580
		fatal_lang_error('ban_not_found', false);
1581
1582
	if (isset($_POST['add_new_trigger']) && !empty($_POST['ban_suggestions']))
1583
	{
1584
		saveTriggers($_POST['ban_suggestions'], $ban_group, 0, $ban_id);
1585
		redirectexit('action=admin;area=ban;sa=edit' . (!empty($ban_group) ? ';bg=' . $ban_group : ''));
1586
	}
1587
	elseif (isset($_POST['edit_trigger']) && !empty($_POST['ban_suggestions']))
1588
	{
1589
		// The first replaces the old one, the others are added new (simplification, otherwise it would require another query and some work...)
1590
		saveTriggers(array_shift($_POST['ban_suggestions']), $ban_group, 0, $ban_id);
1591
		if (!empty($_POST['ban_suggestions']))
1592
			saveTriggers($_POST['ban_suggestions'], $ban_group);
1593
1594
		redirectexit('action=admin;area=ban;sa=edit' . (!empty($ban_group) ? ';bg=' . $ban_group : ''));
1595
	}
1596
	elseif (isset($_POST['edit_trigger']))
1597
	{
1598
		removeBanTriggers($ban_id);
0 ignored issues
show
Documentation introduced by
$ban_id 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...
1599
		redirectexit('action=admin;area=ban;sa=edit' . (!empty($ban_group) ? ';bg=' . $ban_group : ''));
1600
	}
1601
1602
	loadJavaScriptFile('suggest.js', array(), 'smf_suggest');
1603
1604
	if (empty($ban_id))
1605
	{
1606
		$context['ban_trigger'] = array(
1607
			'id' => 0,
1608
			'group' => $ban_group,
1609
			'ip' => array(
1610
				'value' => '',
1611
				'selected' => true,
1612
			),
1613
			'hostname' => array(
1614
				'selected' => false,
1615
				'value' => '',
1616
			),
1617
			'email' => array(
1618
				'value' => '',
1619
				'selected' => false,
1620
			),
1621
			'banneduser' => array(
1622
				'value' => '',
1623
				'selected' => false,
1624
			),
1625
			'is_new' => true,
1626
		);
1627
	}
1628
	else
1629
	{
1630
		$request = $smcFunc['db_query']('', '
1631
			SELECT
1632
				bi.id_ban, bi.id_ban_group, bi.hostname, bi.email_address, bi.id_member,
1633
				bi.ip_low, bi.ip_high,
1634
				mem.member_name, mem.real_name
1635
			FROM {db_prefix}ban_items AS bi
1636
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = bi.id_member)
1637
			WHERE bi.id_ban = {int:ban_item}
1638
				AND bi.id_ban_group = {int:ban_group}
1639
			LIMIT 1',
1640
			array(
1641
				'ban_item' => $ban_id,
1642
				'ban_group' => $ban_group,
1643
			)
1644
		);
1645
		if ($smcFunc['db_num_rows']($request) == 0)
1646
			fatal_lang_error('ban_not_found', false);
1647
		$row = $smcFunc['db_fetch_assoc']($request);
1648
		$smcFunc['db_free_result']($request);
1649
1650
		$context['ban_trigger'] = array(
1651
			'id' => $row['id_ban'],
1652
			'group' => $row['id_ban_group'],
1653
			'ip' => array(
1654
				'value' => empty($row['ip_low']) ? '' : range2ip($row['ip_low'], $row['ip_high']),
1655
				'selected' => !empty($row['ip_low']),
1656
			),
1657
			'hostname' => array(
1658
				'value' => str_replace('%', '*', $row['hostname']),
1659
				'selected' => !empty($row['hostname']),
1660
			),
1661
			'email' => array(
1662
				'value' => str_replace('%', '*', $row['email_address']),
1663
				'selected' => !empty($row['email_address'])
1664
			),
1665
			'banneduser' => array(
1666
				'value' => $row['member_name'],
1667
				'selected' => !empty($row['member_name'])
1668
			),
1669
			'is_new' => false,
1670
		);
1671
	}
1672
1673
	createToken('admin-bet');
1674
}
1675
1676
/**
1677
 * This handles the screen for showing the banned entities
1678
 * It is accessed by ?action=admin;area=ban;sa=browse
1679
 * It uses sub-tabs for browsing by IP, hostname, email or username.
1680
 *
1681
 * @uses ManageBans template, browse_triggers sub template.
1682
 */
1683
function BanBrowseTriggers()
1684
{
1685
	global $modSettings, $context, $scripturl, $smcFunc, $txt;
1686
	global $sourcedir, $settings;
1687
1688
	if (!empty($_POST['remove_triggers']) && !empty($_POST['remove']) && is_array($_POST['remove']))
1689
	{
1690
		checkSession();
1691
1692
		removeBanTriggers($_POST['remove']);
1693
1694
		// Rehabilitate some members.
1695
		if ($_REQUEST['entity'] == 'member')
1696
			updateBanMembers();
1697
1698
		// Make sure the ban cache is refreshed.
1699
		updateSettings(array('banLastUpdated' => time()));
1700
	}
1701
1702
	$context['selected_entity'] = isset($_REQUEST['entity']) && in_array($_REQUEST['entity'], array('ip', 'hostname', 'email', 'member')) ? $_REQUEST['entity'] : 'ip';
1703
1704
	$listOptions = array(
1705
		'id' => 'ban_trigger_list',
1706
		'title' => $txt['ban_trigger_browse'],
1707
		'items_per_page' => $modSettings['defaultMaxListItems'],
1708
		'base_href' => $scripturl . '?action=admin;area=ban;sa=browse;entity=' . $context['selected_entity'],
1709
		'default_sort_col' => 'banned_entity',
1710
		'no_items_label' => $txt['ban_no_triggers'],
1711
		'get_items' => array(
1712
			'function' => 'list_getBanTriggers',
1713
			'params' => array(
1714
				$context['selected_entity'],
1715
			),
1716
		),
1717
		'get_count' => array(
1718
			'function' => 'list_getNumBanTriggers',
1719
			'params' => array(
1720
				$context['selected_entity'],
1721
			),
1722
		),
1723
		'columns' => array(
1724
			'banned_entity' => array(
1725
				'header' => array(
1726
					'value' => $txt['ban_banned_entity'],
1727
				),
1728
			),
1729
			'ban_name' => array(
1730
				'header' => array(
1731
					'value' => $txt['ban_name'],
1732
				),
1733
				'data' => array(
1734
					'sprintf' => array(
1735
						'format' => '<a href="' . $scripturl . '?action=admin;area=ban;sa=edit;bg=%1$d">%2$s</a>',
1736
						'params' => array(
1737
							'id_ban_group' => false,
1738
							'name' => false,
1739
						),
1740
					),
1741
				),
1742
				'sort' => array(
1743
					'default' => 'bg.name',
1744
					'reverse' => 'bg.name DESC',
1745
				),
1746
			),
1747
			'hits' => array(
1748
				'header' => array(
1749
					'value' => $txt['ban_hits'],
1750
				),
1751
				'data' => array(
1752
					'db' => 'hits',
1753
				),
1754
				'sort' => array(
1755
					'default' => 'bi.hits DESC',
1756
					'reverse' => 'bi.hits',
1757
				),
1758
			),
1759
			'check' => array(
1760
				'header' => array(
1761
					'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check">',
1762
					'class' => 'centercol',
1763
				),
1764
				'data' => array(
1765
					'sprintf' => array(
1766
						'format' => '<input type="checkbox" name="remove[]" value="%1$d" class="input_check">',
1767
						'params' => array(
1768
							'id_ban' => false,
1769
						),
1770
					),
1771
					'class' => 'centercol',
1772
				),
1773
			),
1774
		),
1775
		'form' => array(
1776
			'href' => $scripturl . '?action=admin;area=ban;sa=browse;entity=' . $context['selected_entity'],
1777
			'include_start' => true,
1778
			'include_sort' => true,
1779
		),
1780
		'additional_rows' => array(
1781
			array(
1782
				'position' => 'above_column_headers',
1783
				'value' => '<a href="' . $scripturl . '?action=admin;area=ban;sa=browse;entity=ip">' . ($context['selected_entity'] == 'ip' ? '<img src="' . $settings['images_url'] . '/selected.png" alt="&gt;"> ' : '') . $txt['ip'] . '</a>&nbsp;|&nbsp;<a href="' . $scripturl . '?action=admin;area=ban;sa=browse;entity=hostname">' . ($context['selected_entity'] == 'hostname' ? '<img src="' . $settings['images_url'] . '/selected.png" alt="&gt;"> ' : '') . $txt['hostname'] . '</a>&nbsp;|&nbsp;<a href="' . $scripturl . '?action=admin;area=ban;sa=browse;entity=email">' . ($context['selected_entity'] == 'email' ? '<img src="' . $settings['images_url'] . '/selected.png" alt="&gt;"> ' : '') . $txt['email'] . '</a>&nbsp;|&nbsp;<a href="' . $scripturl . '?action=admin;area=ban;sa=browse;entity=member">' . ($context['selected_entity'] == 'member' ? '<img src="' . $settings['images_url'] . '/selected.png" alt="&gt;"> ' : '') . $txt['username'] . '</a>',
1784
			),
1785
			array(
1786
				'position' => 'bottom_of_list',
1787
				'value' => '<input type="submit" name="remove_triggers" value="' . $txt['ban_remove_selected_triggers'] . '" data-confirm="' . $txt['ban_remove_selected_triggers_confirm'] . '" class="button_submit you_sure">',
1788
			),
1789
		),
1790
	);
1791
1792
	// Specific data for the first column depending on the selected entity.
1793
	if ($context['selected_entity'] === 'ip')
1794
	{
1795
		$listOptions['columns']['banned_entity']['data'] = array(
1796
			'function' => function($rowData)
1797
			{
1798
				return range2ip(
1799
					$rowData['ip_low']
1800
				,
1801
					$rowData['ip_high']
1802
				);
1803
			},
1804
		);
1805
		$listOptions['columns']['banned_entity']['sort'] = array(
1806
			'default' => 'bi.ip_low, bi.ip_high, bi.ip_low',
1807
			'reverse' => 'bi.ip_low DESC, bi.ip_high DESC',
1808
		);
1809
	}
1810 View Code Duplication
	elseif ($context['selected_entity'] === 'hostname')
1811
	{
1812
		$listOptions['columns']['banned_entity']['data'] = array(
1813
			'function' => function($rowData) use ($smcFunc)
1814
			{
1815
				return strtr($smcFunc['htmlspecialchars']($rowData['hostname']), array('%' => '*'));
1816
			},
1817
		);
1818
		$listOptions['columns']['banned_entity']['sort'] = array(
1819
			'default' => 'bi.hostname',
1820
			'reverse' => 'bi.hostname DESC',
1821
		);
1822
	}
1823 View Code Duplication
	elseif ($context['selected_entity'] === 'email')
1824
	{
1825
		$listOptions['columns']['banned_entity']['data'] = array(
1826
			'function' => function($rowData) use ($smcFunc)
1827
			{
1828
				return strtr($smcFunc['htmlspecialchars']($rowData['email_address']), array('%' => '*'));
1829
			},
1830
		);
1831
		$listOptions['columns']['banned_entity']['sort'] = array(
1832
			'default' => 'bi.email_address',
1833
			'reverse' => 'bi.email_address DESC',
1834
		);
1835
	}
1836
	elseif ($context['selected_entity'] === 'member')
1837
	{
1838
		$listOptions['columns']['banned_entity']['data'] = array(
1839
			'sprintf' => array(
1840
				'format' => '<a href="' . $scripturl . '?action=profile;u=%1$d">%2$s</a>',
1841
				'params' => array(
1842
					'id_member' => false,
1843
					'real_name' => false,
1844
				),
1845
			),
1846
		);
1847
		$listOptions['columns']['banned_entity']['sort'] = array(
1848
			'default' => 'mem.real_name',
1849
			'reverse' => 'mem.real_name DESC',
1850
		);
1851
	}
1852
1853
	// Create the list.
1854
	require_once($sourcedir . '/Subs-List.php');
1855
	createList($listOptions);
1856
1857
	// The list is the only thing to show, so make it the default sub template.
1858
	$context['sub_template'] = 'show_list';
1859
	$context['default_list'] = 'ban_trigger_list';
1860
}
1861
1862
/**
1863
 * Get ban triggers for the given parameters. Callback from $listOptions['get_items'] in BanBrowseTriggers()
1864
 *
1865
 * @param int $start The item to start with (for pagination purposes)
1866
 * @param int $items_per_page How many items to show on each page
1867
 * @param string $sort A string telling ORDER BY how to sort the results
1868
 * @param string $trigger_type The trigger type - can be 'ip', 'hostname' or 'email'
1869
 * @return array An array of ban trigger info for the list
1870
 */
1871
function list_getBanTriggers($start, $items_per_page, $sort, $trigger_type)
1872
{
1873
	global $smcFunc;
1874
1875
	$where = array(
1876
		'ip' => 'bi.ip_low is not null',
1877
		'hostname' => 'bi.hostname != {string:blank_string}',
1878
		'email' => 'bi.email_address != {string:blank_string}',
1879
	);
1880
1881
	$request = $smcFunc['db_query']('', '
1882
		SELECT
1883
			bi.id_ban, bi.ip_low, bi.ip_high, bi.hostname, bi.email_address, bi.hits,
1884
			bg.id_ban_group, bg.name' . ($trigger_type === 'member' ? ',
1885
			mem.id_member, mem.real_name' : '') . '
1886
		FROM {db_prefix}ban_items AS bi
1887
			INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group)' . ($trigger_type === 'member' ? '
1888
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = bi.id_member)' : '
1889
		WHERE ' . $where[$trigger_type]) . '
1890
		ORDER BY {raw:sort}
1891
		LIMIT {int:start}, {int:max}',
1892
		array(
1893
			'blank_string' => '',
1894
			'sort' => $sort,
1895
			'start' => $start,
1896
			'max' => $items_per_page,
1897
		)
1898
	);
1899
	$ban_triggers = array();
1900
	while ($row = $smcFunc['db_fetch_assoc']($request))
1901
		$ban_triggers[] = $row;
1902
	$smcFunc['db_free_result']($request);
1903
1904
	return $ban_triggers;
1905
}
1906
1907
/**
1908
 * This returns the total number of ban triggers of the given type. Callback for $listOptions['get_count'] in BanBrowseTriggers().
1909
 *
1910
 * @param string $trigger_type The trigger type. Can be 'ip', 'hostname' or 'email'
1911
 * @return int The number of triggers of the specified type
1912
 */
1913
function list_getNumBanTriggers($trigger_type)
1914
{
1915
	global $smcFunc;
1916
1917
	$where = array(
1918
		'ip' => 'bi.ip_low is not null',
1919
		'hostname' => 'bi.hostname != {string:blank_string}',
1920
		'email' => 'bi.email_address != {string:blank_string}',
1921
	);
1922
1923
	$request = $smcFunc['db_query']('', '
1924
		SELECT COUNT(*)
1925
		FROM {db_prefix}ban_items AS bi' . ($trigger_type === 'member' ? '
1926
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = bi.id_member)' : '
1927
		WHERE ' . $where[$trigger_type]),
1928
		array(
1929
			'blank_string' => '',
1930
		)
1931
	);
1932
	list ($num_triggers) = $smcFunc['db_fetch_row']($request);
1933
	$smcFunc['db_free_result']($request);
1934
1935
	return $num_triggers;
1936
}
1937
1938
/**
1939
 * This handles the listing of ban log entries, and allows their deletion.
1940
 * Shows a list of logged access attempts by banned users.
1941
 * It is accessed by ?action=admin;area=ban;sa=log.
1942
 * How it works:
1943
 *  - allows sorting of several columns.
1944
 *  - also handles deletion of (a selection of) log entries.
1945
 */
1946
function BanLog()
1947
{
1948
	global $scripturl, $context, $sourcedir, $txt, $modSettings;
1949
1950
	// Delete one or more entries.
1951
	if (!empty($_POST['removeAll']) || (!empty($_POST['removeSelected']) && !empty($_POST['remove'])))
1952
	{
1953
		checkSession();
1954
		validateToken('admin-bl');
1955
1956
		// 'Delete all entries' button was pressed.
1957
		if (!empty($_POST['removeAll']))
1958
			removeBanLogs();
1959
		// 'Delete selection' button was pressed.
1960
		else
1961
		{
1962
			array_map('intval', $_POST['remove']);
1963
			removeBanLogs($_POST['remove']);
1964
		}
1965
	}
1966
1967
	$listOptions = array(
1968
		'id' => 'ban_log',
1969
		'title' => $txt['ban_log'],
1970
		'items_per_page' => $modSettings['defaultMaxListItems'],
1971
		'base_href' => $context['admin_area'] == 'ban' ? $scripturl . '?action=admin;area=ban;sa=log' : $scripturl . '?action=admin;area=logs;sa=banlog',
1972
		'default_sort_col' => 'date',
1973
		'get_items' => array(
1974
			'function' => 'list_getBanLogEntries',
1975
		),
1976
		'get_count' => array(
1977
			'function' => 'list_getNumBanLogEntries',
1978
		),
1979
		'no_items_label' => $txt['ban_log_no_entries'],
1980
		'columns' => array(
1981
			'ip' => array(
1982
				'header' => array(
1983
					'value' => $txt['ban_log_ip'],
1984
				),
1985
				'data' => array(
1986
					'sprintf' => array(
1987
						'format' => '<a href="' . $scripturl . '?action=trackip;searchip=%1$s">%1$s</a>',
1988
						'params' => array(
1989
							'ip' => false,
1990
						),
1991
					),
1992
				),
1993
				'sort' => array(
1994
					'default' => 'lb.ip',
1995
					'reverse' => 'lb.ip DESC',
1996
				),
1997
			),
1998
			'email' => array(
1999
				'header' => array(
2000
					'value' => $txt['ban_log_email'],
2001
				),
2002
				'data' => array(
2003
					'db_htmlsafe' => 'email',
2004
				),
2005
				'sort' => array(
2006
					'default' => 'lb.email = \'\', lb.email',
2007
					'reverse' => 'lb.email != \'\', lb.email DESC',
2008
				),
2009
			),
2010
			'member' => array(
2011
				'header' => array(
2012
					'value' => $txt['ban_log_member'],
2013
				),
2014
				'data' => array(
2015
					'sprintf' => array(
2016
						'format' => '<a href="' . $scripturl . '?action=profile;u=%1$d">%2$s</a>',
2017
						'params' => array(
2018
							'id_member' => false,
2019
							'real_name' => false,
2020
						),
2021
					),
2022
				),
2023
				'sort' => array(
2024
					'default' => 'COALESCE(mem.real_name, 1=1), mem.real_name',
2025
					'reverse' => 'COALESCE(mem.real_name, 1=1) DESC, mem.real_name DESC',
2026
				),
2027
			),
2028
			'date' => array(
2029
				'header' => array(
2030
					'value' => $txt['ban_log_date'],
2031
				),
2032
				'data' => array(
2033
					'function' => function($rowData)
2034
					{
2035
						return timeformat($rowData['log_time']);
2036
					},
2037
				),
2038
				'sort' => array(
2039
					'default' => 'lb.log_time DESC',
2040
					'reverse' => 'lb.log_time',
2041
				),
2042
			),
2043
			'check' => array(
2044
				'header' => array(
2045
					'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check">',
2046
					'class' => 'centercol',
2047
				),
2048
				'data' => array(
2049
					'sprintf' => array(
2050
						'format' => '<input type="checkbox" name="remove[]" value="%1$d" class="input_check">',
2051
						'params' => array(
2052
							'id_ban_log' => false,
2053
						),
2054
					),
2055
					'class' => 'centercol',
2056
				),
2057
			),
2058
		),
2059
		'form' => array(
2060
			'href' => $context['admin_area'] == 'ban' ? $scripturl . '?action=admin;area=ban;sa=log' : $scripturl . '?action=admin;area=logs;sa=banlog',
2061
			'include_start' => true,
2062
			'include_sort' => true,
2063
			'token' => 'admin-bl',
2064
		),
2065
		'additional_rows' => array(
2066
			array(
2067
				'position' => 'top_of_list',
2068
				'value' => '
2069
					<input type="submit" name="removeSelected" value="' . $txt['ban_log_remove_selected'] . '" data-confirm="' . $txt['ban_log_remove_selected_confirm'] . '" class="button_submit you_sure">
2070
					<input type="submit" name="removeAll" value="' . $txt['ban_log_remove_all'] . '" data-confirm="' . $txt['ban_log_remove_all_confirm'] . '" class="button_submit you_sure">',
2071
			),
2072
			array(
2073
				'position' => 'bottom_of_list',
2074
				'value' => '
2075
					<input type="submit" name="removeSelected" value="' . $txt['ban_log_remove_selected'] . '" data-confirm="' . $txt['ban_log_remove_selected_confirm'] . '" class="button_submit you_sure">
2076
					<input type="submit" name="removeAll" value="' . $txt['ban_log_remove_all'] . '" data-confirm="' . $txt['ban_log_remove_all_confirm'] . '" class="button_submit you_sure">',
2077
			),
2078
		),
2079
	);
2080
2081
	createToken('admin-bl');
2082
2083
	require_once($sourcedir . '/Subs-List.php');
2084
	createList($listOptions);
2085
2086
	$context['page_title'] = $txt['ban_log'];
2087
	$context['sub_template'] = 'show_list';
2088
	$context['default_list'] = 'ban_log';
2089
}
2090
2091
/**
2092
 * Load a list of ban log entries from the database.
2093
 * (no permissions check). Callback for $listOptions['get_items'] in BanLog()
2094
 *
2095
 * @param int $start The item to start with (for pagination purposes)
2096
 * @param int $items_per_page How many items to show on each page
2097
 * @param string $sort A string telling ORDER BY how to sort the results
2098
 * @return array An array of info about the ban log entries for the list.
2099
 */
2100
function list_getBanLogEntries($start, $items_per_page, $sort)
2101
{
2102
	global $smcFunc;
2103
2104
	$dash = '-';
2105
2106
	$request = $smcFunc['db_query']('', '
2107
		SELECT lb.id_ban_log, lb.id_member, lb.ip AS ip, COALESCE(lb.email, {string:dash}) AS email, lb.log_time, COALESCE(mem.real_name, {string:blank_string}) AS real_name
2108
		FROM {db_prefix}log_banned AS lb
2109
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lb.id_member)
2110
		ORDER BY {raw:sort}
2111
		LIMIT {int:start}, {int:items}',
2112
		array(
2113
			'blank_string' => '',
2114
			'dash' => $dash,
2115
			'sort' => $sort,
2116
			'start' => $start,
2117
			'items' => $items_per_page,
2118
		)
2119
	);
2120
	$log_entries = array();
2121 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
2122
	{
2123
		$row['ip'] = $row['ip'] === null ? $dash : inet_dtop($row['ip']);
2124
		$log_entries[] = $row;
2125
	}
2126
	$smcFunc['db_free_result']($request);
2127
2128
	return $log_entries;
2129
}
2130
2131
/**
2132
 * This returns the total count of ban log entries. Callback for $listOptions['get_count'] in BanLog().
2133
 * @return int The total number of ban log entries.
2134
 */
2135
function list_getNumBanLogEntries()
2136
{
2137
	global $smcFunc;
2138
2139
	$request = $smcFunc['db_query']('', '
2140
		SELECT COUNT(*)
2141
		FROM {db_prefix}log_banned AS lb',
2142
		array(
2143
		)
2144
	);
2145
	list ($num_entries) = $smcFunc['db_fetch_row']($request);
2146
	$smcFunc['db_free_result']($request);
2147
2148
	return $num_entries;
2149
}
2150
2151
/**
2152
 * Convert a range of given IP number into a single string.
2153
 * It's practically the reverse function of ip2range().
2154
 *
2155
 * @example
2156
 * range2ip(array(10, 10, 10, 0), array(10, 10, 20, 255)) returns '10.10.10-20.*
2157
 *
2158
 * @param array $low The low end of the range in IPv4 format
2159
 * @param array $high The high end of the range in IPv4 format
2160
 * @return string A string indicating the range
0 ignored issues
show
Documentation introduced by
Should the return type not be string|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2161
 */
2162
function range2ip($low, $high)
2163
{
2164
	$low = inet_dtop($low);
0 ignored issues
show
Documentation introduced by
$low is of type array, but the function expects a string.

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...
2165
	$high = inet_dtop($high);
0 ignored issues
show
Documentation introduced by
$high is of type array, but the function expects a string.

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...
2166
2167
	if ($low == '255.255.255.255') return 'unknown';
2168
	if ($low == $high)
2169
	    return $low;
2170
	else
2171
	    return $low . '-' . $high;
2172
}
2173
2174
/**
2175
 * Checks whether a given IP range already exists in the trigger list.
2176
 * If yes, it returns an error message. Otherwise, it returns an array
2177
 *  optimized for the database.
2178
 *
2179
 * @param array $ip_array An array of IP trigger data
2180
 * @param string $fullip The full IP
2181
 * @return boolean|array False if the trigger array is invalid or the passed array if the value doesn't exist in the database
2182
 */
2183
function checkExistingTriggerIP($ip_array, $fullip = '')
2184
{
2185
	global $smcFunc, $scripturl;
2186
2187
2188
	$values = array(
2189
		'ip_low' => $ip_array['low'],
2190
		'ip_high' => $ip_array['high']
2191
	);
2192
2193
2194
	$request = $smcFunc['db_query']('', '
2195
		SELECT bg.id_ban_group, bg.name
2196
		FROM {db_prefix}ban_groups AS bg
2197
		INNER JOIN {db_prefix}ban_items AS bi ON
2198
			(bi.id_ban_group = bg.id_ban_group)
2199
			AND ip_low = {inet:ip_low} AND ip_high = {inet:ip_high}
2200
		LIMIT 1',
2201
		$values
2202
	);
2203
	if ($smcFunc['db_num_rows']($request) != 0)
2204
	{
2205
		list ($error_id_ban, $error_ban_name) = $smcFunc['db_fetch_row']($request);
2206
		fatal_lang_error('ban_trigger_already_exists', false, array(
2207
			$fullip,
2208
			'<a href="' . $scripturl . '?action=admin;area=ban;sa=edit;bg=' . $error_id_ban . '">' . $error_ban_name . '</a>',
2209
		));
2210
	}
2211
	$smcFunc['db_free_result']($request);
2212
2213
	return $values;
2214
}
2215
2216
/**
2217
 * As it says... this tries to review the list of banned members, to match new bans.
2218
 * Note: is_activated >= 10: a member is banned.
2219
 */
2220
function updateBanMembers()
2221
{
2222
	global $smcFunc;
2223
2224
	$updates = array();
2225
	$allMembers = array();
2226
	$newMembers = array();
2227
2228
	// Start by getting all active bans - it's quicker doing this in parts...
2229
	$request = $smcFunc['db_query']('', '
2230
		SELECT bi.id_member, bi.email_address
2231
		FROM {db_prefix}ban_items AS bi
2232
			INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group)
2233
		WHERE (bi.id_member > {int:no_member} OR bi.email_address != {string:blank_string})
2234
			AND bg.cannot_access = {int:cannot_access_on}
2235
			AND (bg.expire_time IS NULL OR bg.expire_time > {int:current_time})',
2236
		array(
2237
			'no_member' => 0,
2238
			'cannot_access_on' => 1,
2239
			'current_time' => time(),
2240
			'blank_string' => '',
2241
		)
2242
	);
2243
	$memberIDs = array();
2244
	$memberEmails = array();
2245
	$memberEmailWild = array();
2246
	while ($row = $smcFunc['db_fetch_assoc']($request))
2247
	{
2248
		if ($row['id_member'])
2249
			$memberIDs[$row['id_member']] = $row['id_member'];
2250
		if ($row['email_address'])
2251
		{
2252
			// Does it have a wildcard - if so we can't do a IN on it.
2253
			if (strpos($row['email_address'], '%') !== false)
2254
				$memberEmailWild[$row['email_address']] = $row['email_address'];
2255
			else
2256
				$memberEmails[$row['email_address']] = $row['email_address'];
2257
		}
2258
	}
2259
	$smcFunc['db_free_result']($request);
2260
2261
	// Build up the query.
2262
	$queryPart = array();
2263
	$queryValues = array();
2264
	if (!empty($memberIDs))
2265
	{
2266
		$queryPart[] = 'mem.id_member IN ({array_string:member_ids})';
2267
		$queryValues['member_ids'] = $memberIDs;
2268
	}
2269
	if (!empty($memberEmails))
2270
	{
2271
		$queryPart[] = 'mem.email_address IN ({array_string:member_emails})';
2272
		$queryValues['member_emails'] = $memberEmails;
2273
	}
2274
	$count = 0;
2275
	foreach ($memberEmailWild as $email)
2276
	{
2277
		$queryPart[] = 'mem.email_address LIKE {string:wild_' . $count . '}';
2278
		$queryValues['wild_' . $count++] = $email;
0 ignored issues
show
Coding Style introduced by
Increment and decrement operators must be bracketed when used in string concatenation
Loading history...
2279
	}
2280
2281
	// Find all banned members.
2282
	if (!empty($queryPart))
2283
	{
2284
		$request = $smcFunc['db_query']('', '
2285
			SELECT mem.id_member, mem.is_activated
2286
			FROM {db_prefix}members AS mem
2287
			WHERE ' . implode(' OR ', $queryPart),
2288
			$queryValues
2289
		);
2290
		while ($row = $smcFunc['db_fetch_assoc']($request))
2291
		{
2292
			if (!in_array($row['id_member'], $allMembers))
2293
			{
2294
				$allMembers[] = $row['id_member'];
2295
				// Do they need an update?
2296
				if ($row['is_activated'] < 10)
2297
				{
2298
					$updates[($row['is_activated'] + 10)][] = $row['id_member'];
2299
					$newMembers[] = $row['id_member'];
2300
				}
2301
			}
2302
		}
2303
		$smcFunc['db_free_result']($request);
2304
	}
2305
2306
	// We welcome our new members in the realm of the banned.
2307
	if (!empty($newMembers))
2308
		$smcFunc['db_query']('', '
2309
			DELETE FROM {db_prefix}log_online
2310
			WHERE id_member IN ({array_int:new_banned_members})',
2311
			array(
2312
				'new_banned_members' => $newMembers,
2313
			)
2314
		);
2315
2316
	// Find members that are wrongfully marked as banned.
2317
	$request = $smcFunc['db_query']('', '
2318
		SELECT mem.id_member, mem.is_activated - 10 AS new_value
2319
		FROM {db_prefix}members AS mem
2320
			LEFT JOIN {db_prefix}ban_items AS bi ON (bi.id_member = mem.id_member OR mem.email_address LIKE bi.email_address)
2321
			LEFT JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group AND bg.cannot_access = {int:cannot_access_activated} AND (bg.expire_time IS NULL OR bg.expire_time > {int:current_time}))
2322
		WHERE (bi.id_ban IS NULL OR bg.id_ban_group IS NULL)
2323
			AND mem.is_activated >= {int:ban_flag}',
2324
		array(
2325
			'cannot_access_activated' => 1,
2326
			'current_time' => time(),
2327
			'ban_flag' => 10,
2328
		)
2329
	);
2330 View Code Duplication
	while ($row = $smcFunc['db_fetch_assoc']($request))
2331
	{
2332
		// Don't do this twice!
2333
		if (!in_array($row['id_member'], $allMembers))
2334
		{
2335
			$updates[$row['new_value']][] = $row['id_member'];
2336
			$allMembers[] = $row['id_member'];
2337
		}
2338
	}
2339
	$smcFunc['db_free_result']($request);
2340
2341
	if (!empty($updates))
2342
		foreach ($updates as $newStatus => $members)
2343
			updateMemberData($members, array('is_activated' => $newStatus));
2344
2345
	// Update the latest member and our total members as banning may change them.
2346
	updateStats('member');
2347
}
2348
2349
/**
2350
 * Gets basic member data for the ban
2351
 *
2352
 * @param int $id The ID of the member to get data for
2353
 * @return array An aray containing the ID, name, main IP and email address of the specified user
2354
 */
2355
function getMemberData($id)
2356
{
2357
	global $smcFunc;
2358
2359
	$suggestions = array();
2360
	$request = $smcFunc['db_query']('', '
2361
		SELECT id_member, real_name, member_ip, email_address
2362
		FROM {db_prefix}members
2363
		WHERE id_member = {int:current_user}
2364
		LIMIT 1',
2365
		array(
2366
			'current_user' => $id,
2367
		)
2368
	);
2369
	if ($smcFunc['db_num_rows']($request) > 0)
2370
	{
2371
		list ($suggestions['member']['id'], $suggestions['member']['name'], $suggestions['main_ip'], $suggestions['email']) = $smcFunc['db_fetch_row']($request);
2372
		$suggestions['main_ip'] = inet_dtop($suggestions['main_ip']);
2373
	}
2374
	$smcFunc['db_free_result']($request);
2375
2376
	return $suggestions;
2377
}
2378
2379
?>