Split the Admin stuff from the main stuff
jdarwood007

jdarwood007 commited on 2020-01-04 09:06:43
Showing 5 changed files, with 1201 additions and 1114 deletions.

... ...
@@ -23,1007 +23,43 @@ class SFS
23 23
 	private $softwareVersion = '2.1';
24 24
 
25 25
 	/**
26
-	 * @var string The URL for the admin page.
27
-	 */
28
-	private $adminPageURL = null;
29
-	private $adminLogURL = null;
30
-
31
-	/**
32
-	 * @var array Our settings information used on saving/changing settings.
33
-	 */
34
-	private $changedSettings = array();
35
-	private $extraVerificationOptions = array();
36
-
37
-	/**
38
-	 * @var mixed Search area handling.
39
-	 */
40
-	private $search_types = array();
41
-	private $search_params = array();
42
-	private $search_params_column = '';
43
-	private $search_params_string = null;
44
-	private $search_params_type = null;
45
-	private $canDeleteLogs = false;
46
-	private $logSearch = array();
47
-
48
-	/**
49
-	 * @var int How long we disable removing logs.
50
-	 */
51
-	private $hoursDisabled = 24;
52
-
53
-	/**
54
-	 * Simple setup for the class to be used later correctly.
55
-	 * This simply loads the class into $smcFunc and we can grab this anywhere else later.
56
-	 *
57
-	 * @api
58
-	 * @CalledIn SMF 2.0, SMF 2.1
59
-	 * @version 1.0
60
-	 * @since 1.0
61
-	 * @uses integrate_pre_load - Hook SMF2.0
62
-	 * @uses integrate_pre_load - Hook SMF2.1
63
-	 * @return void No return is generated
64
-	 */
65
-	public static function hook_pre_load(): void
66
-	{
67
-		global $smcFunc;
68
-		
69
-		$smcFunc['classSFS'] = new SFS();
70
-	}
71
-
72
-	/**
73
-	 * Build the class, figure out what software/version we have.
74
-	 * Loads up the defaults.
75
-	 *
76
-	 * @CalledIn SMF 2.0, SMF 2.1
77
-	 * @version 1.0
78
-	 * @since 1.0
79
-	 * @return void No return is generated
80
-	 */
81
-	public function __construct()
82
-	{
83
-		global $smcFunc;
84
-
85
-		// Is this SMF 2.0?
86
-		if (!function_exists('loadCacheAccelerator'))
87
-			$this->softwareVersion = '2.0';
88
-
89
-		// Setup the defaults.
90
-		$this->loadDefaults();
91
-	}
92
-
93
-	/**
94
-	 * Creates the hook to the class for the admin areas.
95
-	 *
96
-	 * @param array $admin_areas A associate array from the software with all valid admin areas.
97
-	 *
98
-	 * @api
99
-	 * @CalledIn SMF 2.0, SMF 2.1
100
-	 * @see SFS::setupAdminAreas()
101
-	 * @version 1.0
102
-	 * @since 1.0
103
-	 * @uses integrate__admin_areas - Hook SMF2.0
104
-	 * @uses integrate__admin_areas - Hook SMF2.1
105
-	 * @return void No return is generated
106
-	 */
107
-	public static function hook_admin_areas(array &$admin_areas)
108
-	{
109
-		global $smcFunc;
110
-		return $smcFunc['classSFS']->setupAdminAreas($admin_areas);
111
-	}
112
-
113
-	/**
114
-	 * Startup the Admin Panels Additions.
115
-	 * Where things appear are based on what software/version you have.
116
-	 *
117
-	 * @param array $admin_areas A associate array from the software with all valid admin areas.
118
-	 *
119
-	 * @internal
120
-	 * @CalledIn SMF 2.0, SMF 2.1
121
-	 * @version 1.0
122
-	 * @since 1.0
123
-	 * @uses integrate__admin_areas - Hook SMF2.0
124
-	 * @uses integrate__admin_areas - Hook SMF2.1
125
-	 * @return void No return is generated
126
-	 */
127
-	private function setupAdminAreas(array &$admin_areas): void
128
-	{
129
-		global $scripturl;
130
-
131
-		// Add the menu item.
132
-		if ($this->versionCheck('2.0', 'smf'))
133
-		{
134
-			$this->adminPageURL = $scripturl . '?action=admin;area=modsettings;sa=sfs';
135
-			$this->adminLogURL = $scripturl . '?action=admin;area=modsettings;sa=sfslog';
136
-
137
-			$admin_areas['config']['areas']['modsettings']['subsections']['sfs'] = array(
138
-				$this->txt('sfs_admin_area')
139
-			);
140
-			$admin_areas['config']['areas']['modsettings']['subsections']['sfslog'] = array(
141
-				$this->txt('sfs_admin_logs')
142
-			);
143
-		}
144
-		else
145
-		{
146
-			$this->adminPageURL = $scripturl . '?action=admin;area=securitysettings;sa=sfs';
147
-			$this->adminLogURL = $scripturl . '?action=admin;area=logs;sa=sfslog';
148
-
149
-			$admin_areas['config']['areas']['securitysettings']['subsections']['sfs'] = array(
150
-				$this->txt('sfs_admin_area')
151
-			);
152
-			$admin_areas['config']['areas']['securitysettings']['subsections']['sfslog'] = array(
153
-				$this->txt('sfs_admin_logs')
154
-			);
155
-		}
156
-	}
157
-
158
-	/**
159
-	 * Setup the Modification's setup page.
160
-	 * For some versions, we put the logs into the modifications sections, its easier.
161
-	 *
162
-	 * @param array $subActions A associate array from the software with all valid modification sections.
163
-	 *
164
-	 * @api
165
-	 * @CalledIn SMF 2.0, SMF 2.1
166
-	 * @see SFS::setupModifyModifications()
167
-	 * @version 1.0
168
-	 * @since 1.0
169
-	 * @uses integrate_modify_modifications - Hook SMF2.0
170
-	 * @uses integrate_modify_modifications - Hook SMF2.1
171
-	 * @return void No return is generated
172
-	 */
173
-	public static function hook_modify_modifications(array &$subActions)
174
-	{
175
-		global $smcFunc;
176
-		return $smcFunc['classSFS']->setupModifyModifications($subActions);
177
-	}
178
-
179
-	/**
180
-	 * Setup the Modifications section links.
181
-	 * For some versions we add the logs here as well.
182
-	 *
183
-	 * @param array $subActions A associate array from the software with all valid modification sections.
184
-	 *
185
-	 * @internal
186
-	 * @CalledIn SMF 2.0, SMF 2.1
187
-	 * @version 1.0
188
-	 * @since 1.0
189
-	 * @uses integrate_modify_modifications - Hook SMF2.0
190
-	 * @uses integrate_modify_modifications - Hook SMF2.1
191
-	 * @return void No return is generated
192
-	 */
193
-	private function setupModifyModifications(array &$subActions): void
194
-	{
195
-		$subActions['sfs'] = 'SFS::startupAdminConfiguration';
196
-
197
-		// Only in SMF 2.0 do we drop logs here.
198
-		if ($this->versionCheck('2.0', 'smf'))
199
-			$subActions['sfslog'] = 'SFS::startupLogs';
200
-	}
201
-
202
-	/**
203
-	 * The configuration caller.
204
-	 *
205
-	 * @param bool $return_config If true, returns the configuration options for searches.
206
-	 *
207
-	 * @api
208
-	 * @CalledIn SMF 2.0, SMF 2.1
209
-	 * @see SFS::setupSFSConfiguration
210
-	 * @version 1.0
211
-	 * @since 1.0
212
-	 * @uses integrate_modify_modifications - Hook SMF2.0
213
-	 * @uses integrate_modify_modifications - Hook SMF2.1
214
-	 * @return void No return is generated
215
-	 */
216
-	public static function startupAdminConfiguration(bool $return_config = false)
217
-	{
218
-		global $smcFunc;
219
-		return $smcFunc['classSFS']->setupSFSConfiguration($return_config);
220
-	}
221
-
222
-	/**
223
-	 * The actual settings page.
224
-	 *
225
-	 * @param bool $return_config If true, returns the configuration options for searches.
226
-	 *
227
-	 * @internal
228
-	 * @CalledIn SMF 2.0, SMF 2.1
229
-	 * @version 1.0
230
-	 * @since 1.0
231
-	 * @uses integrate_modify_modifications - Hook SMF2.0
232
-	 * @uses integrate_modify_modifications - Hook SMF2.1
233
-	 * @return void No return is generated
234
-	 */
235
-	private function setupSFSConfiguration(bool $return_config = false): array
236
-	{
237
-		global $scripturl, $context, $settings, $sc, $modSettings;
238
-
239
-		$config_vars = array(
240
-				array('title', 'sfsgentitle', 'label' => $this->txt('sfs_general_title')),
241
-
242
-				array('check', 'sfs_enabled'),
243
-				array('int', 'sfs_expire'),
244
-			'',
245
-				array('check', 'sfs_emailcheck'),
246
-			'',
247
-				array('check', 'sfs_usernamecheck'),
248
-				array('float', 'sfs_username_confidence'),
249
-			'',
250
-				array('check', 'sfs_ipcheck'),
251
-				array('check', 'sfs_ipcheck_autoban'),
252
-			'',
253
-				array('select', 'sfs_region', $this->sfsServerMapping('config')),
254
-			'',
255
-				array('check', 'sfs_wildcard_email'),
256
-				array('check', 'sfs_wildcard_username'),
257
-				array('check', 'sfs_wildcard_ip'),
258
-			'',
259
-				array('select', 'sfs_tor_check', array(
260
-					0 => $this->txt('sfs_tor_check_block'),
261
-					1 => $this->txt('sfs_tor_check_ignore'),
262
-					2 => $this->txt('sfs_tor_check_bad'),
263
-				)),
264
-
265
-			'',
266
-				array('title', 'sfsverftitle', 'label' => $this->txt('sfs_verification_title')),
267
-				array('desc', 'sfsverfdesc', 'label' => $this->txt('sfs_verification_desc')),
268
-				array('select', 'sfs_verification_options', array(
269
-					'post' => $this->txt('sfs_verification_options_post'),
270
-					'report' => $this->txt('sfs_verification_options_report'),
271
-					'search' => $this->txt('sfs_verification_options_search'),
272
-				), 'multiple' => true),			
273
-				array('text', 'sfs_verification_options_extra', 'subtext' => $this->txt('sfs_verification_options_extra_subtext')),
274
-
275
-			'',
276
-				array('select', 'sfs_verOptionsMembers', array(
277
-					'post' => $this->txt('sfs_verification_options_post'),
278
-					'search' => $this->txt('sfs_verification_options_search'),
279
-				), 'multiple' => true),
280
-				array('text', 'sfs_verOptionsMemExtra', 'subtext' => $this->txt('sfs_verification_options_extra_subtext')),
281
-				array('int', 'sfs_verfOptMemPostThreshold'),
282
-			'',
283
-				array('check', 'sfs_log_debug'),
284
-		);
285
-
286
-		if ($return_config)
287
-			return $config_vars;
288
-
289
-		// Saving?
290
-		if (isset($_GET['save']))
291
-		{
292
-			// Turn the defaults off.
293
-			$this->unloadDefaults();
294
-			checkSession();
295
-
296
-			// If we are automatically banning IPs, make sure we have a ban group.
297
-			if (isset($_POST['sfs_ipcheck_autoban']) && empty($modSettings['sfs_ipcheck_autoban_group']))
298
-				$this->createBanGroup(true);
299
-
300
-			saveDBSettings($config_vars);
301
-
302
-			writeLog();
303
-			redirectexit($this->adminPageURL);
304
-		}
305
-
306
-		$context['post_url'] = $this->adminPageURL . ';save';
307
-
308
-		prepareDBSettingContext($config_vars);
309
-
310
-		return array();
311
-	}
312
-
313
-	/**
314
-	 * In some software/versions, we can hook into the logs section.
315
-	 * In others we hook into the modifications settings.
316
-	 *
317
-	 * @param array $log_functions All possible log functions.
318
-	 *
319
-	 * @api
320
-	 * @CalledIn SMF 2.1
321
-	 * @See SFS::startupLogs
322
-	 * @version 1.0
323
-	 * @since 1.0
324
-	 * @uses integrate_manage_logs - Hook SMF2.1
325
-	 * @return void No return is generated
326
-	 */
327
-	public static function hook_manage_logs(array &$log_functions): bool
328
-	{
329
-		global $smcFunc;
330
-
331
-		// Add our logs sub action.
332
-		$log_functions['sfslog'] = array('StopForumSpam.php', 'startupLogs');
333
-
334
-		global $smcFunc;
335
-		return $smcFunc['classSFS']->AddToLogMenu($log_functions);
336
-	}
337
-
338
-	/**
339
-	 * Add the SFS logs to the log menu.
340
-	 *
341
-	 * @param array $log_functions All possible log functions.
342
-	 *
343
-	 * @CalledIn SMF 2.1
344
-	 * @See SFS::startupLogs
345
-	 * @version 1.1
346
-	 * @since 1.1
347
-	 * @return void No return is generated
348
-	 */
349
-	public function AddToLogMenu(array &$log_functions): bool
350
-	{
351
-		global $context;
352
-
353
-		$context[$context['admin_menu_name']]['tab_data']['tabs']['sfslog'] = array(
354
-			'description' => $this->txt('sfs_admin_logs'),
355
-		);
356
-
357
-		return true;
358
-	}
359
-
360
-	/**
361
-	 * Log startup caller.
362
-	 * This has a $return_config just for simply complying with properly for searching the admin panel.
363
-	 *
364
-	 * @param bool $return_config If true, returns empty array to prevent breaking old SMF installs.
365
-	 *
366
-	 * @api
367
-	 * @CalledIn SMF 2.1
368
-	 * @See SFS::loadLogs
369
-	 * @version 1.0
370
-	 * @since 1.0
371
-	 * @uses hook_manage_logs - Hook SMF2.1
372
-	 * @uses setupModifyModifications - Injected SMF2.0
373
-	 * @return void No return is generated
374
-	 */
375
-	public static function startupLogs(bool $return_config = false): array
376
-	{
377
-		global $smcFunc;
378
-
379
-		return $smcFunc['classSFS']->loadLogs();
380
-	}
381
-
382
-	/**
383
-	 * Actually show the logs.
384
-	 * This has a $return_config just for simply complying with properly for searching the admin panel.
385
-	 *
386
-	 * @param bool $return_config If true, returns empty array to prevent breaking old SMF installs.
387
-	 *
388
-	 * @api
389
-	 * @CalledIn SMF2.0, SMF 2.1
390
-	 * @See SFS::getSFSLogEntries
391
-	 * @See SFS::getSFSLogEntriesCount
392
-	 * @version 1.0
393
-	 * @since 1.0
394
-	 * @uses hook_manage_logs - Hook SMF2.1
395
-	 * @uses setupModifyModifications - Injected SMF2.0
396
-	 * @return void No return is generated
397
-	 */
398
-	public function loadLogs(bool $return_config = false): array
399
-	{
400
-		global $context, $smcFunc, $sourcedir;
401
-
402
-		// No Configs.
403
-		if ($return_config)
404
-			return array();
405
-
406
-		loadLanguage('Modlog');
407
-
408
-		$context['form_url'] = $this->adminLogURL;
409
-		$context['log_url'] = $this->adminLogURL;
410
-		$context['page_title'] = $this->txt('sfs_admin_logs');
411
-		$this->canDeleteLogs = allowedTo('admin_forum');
412
-
413
-		// Remove all..
414
-		if ((isset($_POST['removeall']) || isset($_POST['delete'])) && $this->canDeleteLogs)
415
-			$this->handleLogDeletes();
416
-
417
-		$sort_types = array(
418
-			'id_type' =>'l.id_type',
419
-			'log_time' => 'l.log_time',
420
-			'url' => 'l.url',
421
-			'member' => 'mem.id_member',
422
-			'username' => 'l.username',
423
-			'email' => 'l.email',
424
-			'ip' => 'l.ip',
425
-			'ip2' => 'l.ip2',
426
-		);
427
-
428
-		$context['order'] = isset($_REQUEST['sort']) && isset($sort_types[$_REQUEST['sort']]) ? $_REQUEST['sort'] : 'time';
429
-
430
-		// Handle searches.
431
-		$this->handleLogSearch($context['log_url']);
432
-
433
-		require_once($sourcedir . '/Subs-List.php');
434
-
435
-		$listOptions = array(
436
-			'id' => 'sfslog_list',
437
-			'title' => $this->txt('sfs_admin_logs'),
438
-			'width' => '100%',
439
-			'items_per_page' => '50',
440
-			'no_items_label' => $this->txt('sfs_log_no_entries_found'),
441
-			'base_href' => $context['log_url'],
442
-			'default_sort_col' => 'time',
443
-			'get_items' => $this->loadLogsGetItems(),
444
-			'get_count' => $this->loadLogsGetCount(),
445
-			// This assumes we are viewing by user.
446
-			'columns' => array(
447
-				'type' => $this->loadLogsColumnType(),
448
-				'time' => $this->loadLogsColumnTime(),
449
-				'url' => $this->loadLogsColumnURL(),
450
-				'member' => $this->loadLogsColumnMember(),
451
-				'username' => $this->loadLogsColumnUsername(),
452
-				'email' => $this->loadLogsColumnEmail(),
453
-				'ip' => $this->loadLogsColumnIP(),
454
-				'ip2' => $this->loadLogsColumnIP(true),
455
-				'checks' => $this->loadLogsColumnChecks(),
456
-				'result' => $this->loadLogsColumnResult(),
457
-				'delete' => $this->loadLogsColumnDelete(),
458
-			),
459
-			'form' => array(
460
-				'href' => $context['form_url'],
461
-				'include_sort' => true,
462
-				'include_start' => true,
463
-				'hidden_fields' => array(
464
-					$context['session_var'] => $context['session_id'],
465
-					'params' => $this->search_params
466
-				),
467
-			),
468
-			'additional_rows' => array(
469
-				$this->loadLogsGetAddtionalRow(),
470
-			),
471
-		);
472
-
473
-		// Create the watched user list.
474
-		createList($listOptions);
475
-
476
-		$context['sub_template'] = 'show_list';
477
-		$context['default_list'] = 'sfslog_list';
478
-
479
-		return array();
480
-	}
481
-
482
-	/**
483
-	 * Handle when we want to delete a log and what to do.
484
-	 *
485
-	 * @internal
486
-	 * @CalledIn SMF2.0, SMF 2.1
487
-	 * @version 1.1
488
-	 * @since 1.1
489
-	 * @return void Nothing is returned, the logs are deleted as requested and admin redirected.
490
-	 */
491
-	private function handleLogDeletes(): void
492
-	{
493
-		if (isset($_POST['removeall']) && $this->canDeleteLogs)
494
-			$this->removeAllLogs();
495
-		elseif (!empty($_POST['remove']) && isset($_POST['delete']) && $this->canDeleteLogs)
496
-			$this->removeLogs(array_unique($_POST['delete']));
497
-	}
498
-
499
-	/**
500
-	 * loadLogs - Get Items.
501
-	 *
502
-	 * @internal
503
-	 * @CalledIn SMF2.0, SMF 2.1
504
-	 * @version 1.1
505
-	 * @since 1.1
506
-	 * @return array The options for the get_items
507
-	 */
508
-	private function loadLogsGetItems(): array
509
-	{
510
-		return array(
511
-			'function' => array($this, 'getSFSLogEntries'),
512
-			'params' => array(
513
-				(!empty($this->logSearch['string']) ? ' INSTR({raw:sql_type}, {string:search_string})' : ''),
514
-				array('sql_type' => $this->search_params_column, 'search_string' => $this->logSearch['string']),
515
-			),
516
-		);
517
-	}
518
-
519
-	/**
520
-	 * loadLogs - Get Count.
521
-	 *
522
-	 * @internal
523
-	 * @CalledIn SMF2.0, SMF 2.1
524
-	 * @version 1.1
525
-	 * @since 1.1
526
-	 * @return array The options for the get_items
527
-	 */
528
-	private function loadLogsGetCount(): array
529
-	{
530
-		return array(
531
-			'function' => array($this, 'getSFSLogEntriesCount'),
532
-			'params' => array(
533
-				(!empty($this->logSearch['string']) ? ' INSTR({raw:sql_type}, {string:search_string})' : ''),
534
-				array('sql_type' => $this->search_params_column, 'search_string' => $this->logSearch['string']),
535
-			),
536
-		);
537
-	}
538
-
539
-	/**
540
-	 * loadLogs - Load an additional row, for mostly deleting stuff.
541
-	 *
542
-	 * @internal
543
-	 * @CalledIn SMF2.0, SMF 2.1
544
-	 * @version 1.1
545
-	 * @since 1.1
546
-	 * @return array The options for the get_items
547
-	 */
548
-	private function loadLogsGetAddtionalRow(): array
549
-	{
550
-		global $smcFunc;
551
-
552
-		return array(
553
-			'position' => 'below_table_data',
554
-			'value' => '
555
-				' . $this->txt('sfs_log_search') . ' (' . $this->logSearch['label'] . '):
556
-				<input type="text" name="search" size="18" value="' . $smcFunc['htmlspecialchars']($this->logSearch['string']) . '" class="input_text" /> <input type="submit" name="is_search" value="' . $this->txt('modlog_go') . '" class="button_submit" />
557
-				' . ($this->canDeleteLogs ? ' |
558
-					<input type="submit" name="remove" value="' . $this->txt('modlog_remove') . '" class="button_submit" />
559
-					<input type="submit" name="removeall" value="' . $this->txt('modlog_removeall') . '" class="button_submit" />' : ''),
560
-		);
561
-	}
562
-
563
-
564
-	/**
565
-	 * loadLogs - Column - Type.
566
-	 *
567
-	 * @internal
568
-	 * @CalledIn SMF2.0, SMF 2.1
569
-	 * @version 1.1
570
-	 * @since 1.1
571
-	 * @return array The options for the column
572
-	 */
573
-	private function loadLogsColumnType(): array
574
-	{
575
-		return array(
576
-			'header' => array(
577
-				'value' => $this->txt('sfs_log_header_type'),
578
-				'class' => 'lefttext',
579
-			),
580
-			'data' => array(
581
-				'db' => 'type',
582
-				'class' => 'smalltext',
583
-			),
584
-			'sort' => array(
585
-			),
586
-		);
587
-	}
588
-
589
-	/**
590
-	 * loadLogs - Column - Time.
591
-	 *
592
-	 * @internal
593
-	 * @CalledIn SMF2.0, SMF 2.1
594
-	 * @version 1.1
595
-	 * @since 1.1
596
-	 * @return array The options for the column
597
-	 */
598
-	private function loadLogsColumnTime(): array
599
-	{
600
-		return array(
601
-			'header' => array(
602
-				'value' => $this->txt('sfs_log_header_time'),
603
-				'class' => 'lefttext',
604
-			),
605
-			'data' => array(
606
-				'db' => 'time',
607
-				'class' => 'smalltext',
608
-			),
609
-			'sort' => array(
610
-				'default' => 'l.log_time DESC',
611
-				'reverse' => 'l.log_time',
612
-			),
613
-		);
614
-	}
615
-
616
-	/**
617
-	 * loadLogs - Column - URL.
618
-	 *
619
-	 * @internal
620
-	 * @CalledIn SMF2.0, SMF 2.1
621
-	 * @version 1.1
622
-	 * @since 1.1
623
-	 * @return array The options for the column
624
-	 */
625
-	private function loadLogsColumnURL(): array
626
-	{
627
-		return array(
628
-			'header' => array(
629
-				'value' => $this->txt('sfs_log_header_url'),
630
-				'class' => 'lefttext',
631
-			),
632
-			'data' => array(
633
-				'db' => 'url',
634
-				'class' => 'smalltext',
635
-				'style' => 'word-break: break-word;',
636
-			),
637
-			'sort' => array(
638
-				'default' => 'l.url DESC',
639
-				'reverse' => 'l.url',
640
-			),
641
-		);
642
-	}
643
-
644
-	/**
645
-	 * loadLogs - Column - Member.
646
-	 *
647
-	 * @internal
648
-	 * @CalledIn SMF2.0, SMF 2.1
649
-	 * @version 1.1
650
-	 * @since 1.1
651
-	 * @return array The options for the column
652
-	 */
653
-	private function loadLogsColumnMember(): array
654
-	{
655
-		return array(
656
-			'header' => array(
657
-				'value' => $this->txt('sfs_log_header_member'),
658
-				'class' => 'lefttext',
659
-			),
660
-			'data' => array(
661
-				'db' => 'member_link',
662
-				'class' => 'smalltext',
663
-			),
664
-			'sort' => array(
665
-				'default' => 'mem.id_member',
666
-				'reverse' => 'mem.id_member DESC',
667
-			),
668
-		);
669
-	}
670
-
671
-	/**
672
-	 * loadLogs - Column - Username.
673
-	 *
674
-	 * @internal
675
-	 * @CalledIn SMF2.0, SMF 2.1
676
-	 * @version 1.1
677
-	 * @since 1.1
678
-	 * @return array The options for the column
679
-	 */
680
-	private function loadLogsColumnUsername(): array
681
-	{
682
-		return array(
683
-			'header' => array(
684
-				'value' => $this->txt('sfs_log_header_username'),
685
-				'class' => 'lefttext',
686
-			),
687
-			'data' => array(
688
-				'db' => 'username',
689
-				'class' => 'smalltext',
690
-			),
691
-			'sort' => array(
692
-				'default' => 'l.username',
693
-				'reverse' => 'l.username DESC',
694
-			),
695
-		);
696
-	}
697
-
698
-	/**
699
-	 * loadLogs - Column - Email.
700
-	 *
701
-	 * @internal
702
-	 * @CalledIn SMF2.0, SMF 2.1
703
-	 * @version 1.1
704
-	 * @since 1.1
705
-	 * @return array The options for the column
706
-	 */
707
-	private function loadLogsColumnEmail(): array
708
-	{
709
-		return array(
710
-			'header' => array(
711
-				'value' => $this->txt('sfs_log_header_email'),
712
-				'class' => 'lefttext',
713
-			),
714
-			'data' => array(
715
-				'db' => 'email',
716
-				'class' => 'smalltext',
717
-			),
718
-			'sort' => array(
719
-				'default' => 'l.email',
720
-				'reverse' => 'l.email DESC',
721
-			),
722
-		);
723
-	}
724
-
725
-	/**
726
-	 * loadLogs - Column - IP.
727
-	 *
728
-	 * @param string $ip2 If true, use ip2
729
-	 * @internal
730
-	 * @CalledIn SMF2.0, SMF 2.1
731
-	 * @version 1.1
732
-	 * @since 1.1
733
-	 * @return array The options for the column
734
-	 */
735
-	private function loadLogsColumnIP(bool $ip2 = false): array
736
-	{
737
-		return array(
738
-			'header' => array(
739
-				'value' => $this->txt('sfs_log_header_ip' . ($ip2 ? '2' : '')),
740
-				'class' => 'lefttext',
741
-			),
742
-			'data' => array(
743
-				'db' => 'ip' . ($ip2 ? '2' : ''),
744
-				'class' => 'smalltext',
745
-			),
746
-			'sort' => array(
747
-				'default' => 'l.ip' . ($ip2 ? '2' : ''),
748
-				'reverse' => 'l.ip' . ($ip2 ? '2' : '') . ' DESC',
749
-			),
750
-		);
751
-	}
752
-
753
-	/**
754
-	 * loadLogs - Column - Checks.
755
-	 *
756
-	 * @internal
757
-	 * @CalledIn SMF2.0, SMF 2.1
758
-	 * @version 1.1
759
-	 * @since 1.1
760
-	 * @return array The options for the column
761
-	 */
762
-	private function loadLogsColumnChecks(): array
763
-	{
764
-		return array(
765
-			'header' => array(
766
-				'value' => $this->txt('sfs_log_checks'),
767
-				'class' => 'lefttext',
768
-			),
769
-			'data' => array(
770
-				'db' => 'checks',
771
-				'class' => 'smalltext',
772
-				'style' => 'word-break: break-word;',
773
-			),
774
-			'sort' => array(),
775
-		);
776
-	}
777
-
778
-	/**
779
-	 * loadLogs - Column - Result.
780
-	 *
781
-	 * @internal
782
-	 * @CalledIn SMF2.0, SMF 2.1
783
-	 * @version 1.1
784
-	 * @since 1.1
785
-	 * @return array The options for the column
786
-	 */
787
-	private function loadLogsColumnResult(): array
788
-	{
789
-		return array(
790
-			'header' => array(
791
-				'value' => $this->txt('sfs_log_result'),
792
-				'class' => 'lefttext',
793
-			),
794
-			'data' => array(
795
-				'db' => 'result',
796
-				'class' => 'smalltext',
797
-				'style' => 'word-break: break-word;',
798
-			),
799
-			'sort' => array(),
800
-		);
801
-	}
802
-
803
-	/**
804
-	 * loadLogs - Column - Delete.
805
-	 *
806
-	 * @internal
807
-	 * @CalledIn SMF2.0, SMF 2.1
808
-	 * @version 1.1
809
-	 * @since 1.1
810
-	 * @return array The options for the column
811
-	 */
812
-	private function loadLogsColumnDelete(): array
813
-	{
814
-		return array(
815
-			'header' => array(
816
-				'value' => '<input type="checkbox" name="all" class="input_check" onclick="invertAll(this, this.form);" />',
817
-			),
818
-			'data' => array(
819
-				'function' => function($entry)
820
-				{
821
-					return '<input type="checkbox" class="input_check" name="delete[]" value="' . $entry['id'] . '"' . ($entry['editable'] ? '' : ' disabled="disabled"') . ' />';
822
-				},
823
-				'style' => 'text-align: center;',
824
-			),
825
-		);
826
-	}
827
-
828
-	/**
829
-	 * Get the log data and returns it ready to go for GenericList handling.
830
-	 *
831
-	 * @param int $start The index for where we offset or start at for the list
832
-	 * @param int $items_per_page How many items we are going to show on this page.
833
-	 * @param string $sort The column we are sorting by.
834
-	 * @param string $query_string The search string we are using to filter log data.
835
-	 * @param array $query_params Extra parameters for searching.
836
-	 *
837
-	 * @api
838
-	 * @CalledIn SMF 2.0, SMF 2.1
839
-	 * @See SFS::loadLogs
840
-	 * @version 1.0
841
-	 * @since 1.0
842
-	 * @uses hook_manage_logs - Hook SMF2.1
843
-	 * @uses setupModifyModifications - Injected SMF2.0
844
-	 * @return void No return is generated
845
-	 */
846
-	public function getSFSLogEntries(int $start, int $items_per_page, string $sort, string $query_string = '', array $query_params = array()): array
847
-	{
848
-		global $scripturl, $context, $smcFunc;
849
-
850
-		// Fetch all of our logs.
851
-		$result = $smcFunc['db_query']('', '
852
-			SELECT
853
-				l.id_sfs,
854
-				l.id_type,
855
-				l.log_time,
856
-				l.url,
857
-				l.id_member,
858
-				l.username,
859
-				l.email,
860
-				l.ip,
861
-				l.ip2,
862
-				l.checks,
863
-				l.result,
864
-				mem.real_name,
865
-				mg.group_name
866
-			FROM {db_prefix}log_sfs AS l
867
-				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = l.id_member)
868
-				LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_group_id} THEN mem.id_post_group ELSE mem.id_group END)
869
-				WHERE id_type IS NOT NULL'
870
-				. (!empty($query_string) ? '
871
-					AND ' . $query_string : '') . '
872
-			ORDER BY ' . $sort . '
873
-			LIMIT ' . $start . ', ' . $items_per_page,
874
-			array_merge($query_params, array(
875
-				'reg_group_id' => 0,
876
-			))
877
-		);
878
-
879
-		$entries = array();
880
-		while ($row = $smcFunc['db_fetch_assoc']($result))
881
-		{
882
-			$entries[$row['id_sfs']] = array(
883
-				'id' => $row['id_sfs'],
884
-				'type' => $this->txt('sfs_log_types_' . $row['id_type']),
885
-				'time' => timeformat($row['log_time']),
886
-				'url' => preg_replace('~http(s)?://~i', 'hxxp\\1://', $row['url']),
887
-				'timestamp' => forum_time(true, $row['log_time']),
888
-				'member_link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>' : (empty($row['real_name']) ? ($this->txt('guest') . (!empty($row['extra']['member_acted']) ? ' (' . $row['extra']['member_acted'] . ')' : '')) : $row['real_name']),
889
-				'username' => $row['username'],
890
-				'email' => $row['email'],
891
-				'ip' => '<a href="' . sprintf($this->urlSFSipCheck, $row['ip']) . '">' . $row['ip'] . '</a>',
892
-				'ip2' => '<a href="' . sprintf($this->urlSFSipCheck, $row['ip2']) . '">' . $row['ip2'] . '</a>',
893
-				'editable' => true, //time() > $row['log_time'] + $this->hoursDisabled * 3600,
894
-				'checks_raw' => $row['checks'],
895
-				'result_raw' => $row['result'],
896
-			);
897
-
898
-			$checksDecoded = $this->decodeJSON($row['checks']);
899
-
900
-			// If we know what check triggered this, link it up to be searched.
901
-			if ($row['id_type'] == 1)
902
-				$entries[$row['id_sfs']]['checks'] = '<a href="' . sprintf($this->urlSFSsearch, $checksDecoded['value']) . '">' . $checksDecoded['value'] . '</a>';
903
-			elseif ($row['id_type'] == 2)
904
-				$entries[$row['id_sfs']]['checks'] = '<a href="' . sprintf($this->urlSFSsearch, $checksDecoded['value']) . '">' . $checksDecoded['value'] . '</a>';
905
-			elseif ($row['id_type'] == 3)
906
-				$entries[$row['id_sfs']]['checks'] = '<a href="' . sprintf($this->urlSFSsearch, $checksDecoded['value']) . '">' . $checksDecoded['value'] . '</a>';
907
-			// No idea what triggered it, parse it out cleanly.  Could be debug data as well.
908
-			else
909
-			{
910
-				$entries[$row['id_sfs']]['checks'] = '';
911
-
912
-				foreach ($checksDecoded as $ckey => $vkey)
913
-					foreach ($vkey as $key => $value)
914
-						$entries[$row['id_sfs']]['checks'] .= ucfirst($key) . ':' . $value . '<br>';					
915
-			}
916
-
917
-			// This tells us what it matched on exactly.
918
-			if (strpos($row['result'], ',') !== false)
919
-			{
920
-				list($resultType, $resultMatch, $extra) = explode(',', $row['result'] . ',,,');
921
-				$entries[$row['id_sfs']]['result'] = sprintf($this->txt('sfs_log_matched_on'), $resultType, $resultMatch);
922
-
923
-				// If this was a IP ban, note it.
924
-				if ($resultType == 'ip' && !empty($extra))
925
-					$entries[$row['id_sfs']]['result'] .= ' ' . $this->txt('sfs_log_auto_banned');			
926
-				if ($resultType == 'username' && !empty($extra))
927
-					$entries[$row['id_sfs']]['result'] .= ' ' . sprintf($this->txt('sfs_log_confidence'), $extra);			
928
-			}
929
-			else
930
-				$entries[$row['id_sfs']]['result'] = $row['result'];
931
-			
932
-		}
933
-		$smcFunc['db_free_result']($result);
934
-
935
-		return $entries;
936
-	}
937
-
938
-	/**
939
-	 * Get the log counts and returns it ready to go for GenericList handling.
940
-	 *
941
-	 * @param string $query_string The search string we are using to filter log data.
942
-	 * @param array $query_params Extra parameters for searching.
943
-	 *
944
-	 * @api
945
-	 * @CalledIn SMF 2.0, SMF 2.1
946
-	 * @See SFS::loadLogs
947
-	 * @version 1.0
948
-	 * @since 1.0
949
-	 * @uses hook_manage_logs - Hook SMF2.1
950
-	 * @uses setupModifyModifications - Injected SMF2.0
951
-	 * @return void No return is generated
952
-	 */
953
-	public function getSFSLogEntriesCount(string $query_string = '', array $query_params = array()): int
954
-	{
955
-		global $smcFunc, $user_info;
956
-
957
-		$result = $smcFunc['db_query']('', '
958
-			SELECT COUNT(*)
959
-			FROM {db_prefix}log_sfs AS l
960
-				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = l.id_member)
961
-				LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_group_id} THEN mem.id_post_group ELSE mem.id_group END)
962
-				WHERE id_type IS NOT NULL'
963
-				. (!empty($query_string) ? '
964
-					AND ' . $query_string : ''),
965
-			array_merge($query_params, array(
966
-				'reg_group_id' => 0,
967
-			))
968
-		);
969
-		list ($entry_count) = $smcFunc['db_fetch_row']($result);
970
-		$smcFunc['db_free_result']($result);
971
-
972
-		return (int) $entry_count;
973
-	}
974
-
975
-	/**
976
-	 * Remove all logs, except those less than 24 hours old.
26
+	 * Simple setup for the class to be used later correctly.
27
+	 * This simply loads the class into $smcFunc and we can grab this anywhere else later.
977 28
 	 *
978 29
 	 * @api
979 30
 	 * @CalledIn SMF 2.0, SMF 2.1
980
-	 * @See SFS::loadLogs
981 31
 	 * @version 1.0
982 32
 	 * @since 1.0
33
+	 * @uses integrate_pre_load - Hook SMF2.0
34
+	 * @uses integrate_pre_load - Hook SMF2.1
983 35
 	 * @return void No return is generated
984 36
 	 */
985
-	private function removeAllLogs(): void
37
+	public static function hook_pre_load(): void
986 38
 	{
987
-		global $smcFunc;
988
-
989
-		checkSession();
39
+		global $smcFunc, $sourcedir;
990 40
 		
991
-		$smcFunc['db_query']('', '
992
-			DELETE FROM {db_prefix}log_sfs
993
-			WHERE log_time < {int:twenty_four_hours_wait}',
994
-			array(
995
-				'twenty_four_hours_wait' => time() - $this->hoursDisabled * 3600,
996
-			)
997
-		);
41
+		$smcFunc['classSFS'] = new SFS();
998 42
 	}
999 43
 
1000 44
 	/**
1001
-	 * Remove specific logs, except those less than 24 hours old.
1002
-	 *
1003
-	 * @param array $entries A array of the ids that we want to remove.
45
+	 * Build the class, figure out what software/version we have.
46
+	 * Loads up the defaults.
1004 47
 	 *
1005
-	 * @api
1006 48
 	 * @CalledIn SMF 2.0, SMF 2.1
1007
-	 * @See SFS::loadLogs
1008 49
 	 * @version 1.0
1009 50
 	 * @since 1.0
1010 51
 	 * @return void No return is generated
1011 52
 	 */
1012
-	private function removeLogs(array $entries): void
53
+	public function __construct()
1013 54
 	{
1014 55
 		global $smcFunc;
1015 56
 
1016
-		checkSession();
57
+		// Is this SMF 2.0?
58
+		if (!function_exists('loadCacheAccelerator'))
59
+			$this->softwareVersion = '2.0';
1017 60
 
1018
-		$smcFunc['db_query']('', '
1019
-			DELETE FROM {db_prefix}log_sfs
1020
-			WHERE id_sfs IN ({array_string:delete_actions})
1021
-				AND log_time < {int:twenty_four_hours_wait}',
1022
-			array(
1023
-				'twenty_four_hours_wait' => time() - $this->hoursDisabled * 3600,
1024
-				'delete_actions' => $entries,
1025
-			)
1026
-		);
61
+		// Setup the defaults.
62
+		$this->loadDefaults();
1027 63
 	}
1028 64
 
1029 65
 	/**
... ...
@@ -1652,7 +688,7 @@ class SFS
1652 688
 	 * @since 1.0
1653 689
 	 * @return array The parsed json string is now an array.
1654 690
 	 */
1655
-	private function decodeJSON(string $requestData): array
691
+	public function decodeJSON(string $requestData): array
1656 692
 	{
1657 693
 		global $smcFunc;
1658 694
 
... ...
@@ -1741,7 +777,7 @@ class SFS
1741 777
 	 * @since 1.0
1742 778
 	 * @return array The list of servers.
1743 779
 	 */
1744
-	private function sfsServerMapping($returnType = null)
780
+	public function sfsServerMapping($returnType = null)
1745 781
 	{
1746 782
 		// Global list of servers.
1747 783
 		$serverList = array(
... ...
@@ -1943,126 +979,6 @@ class SFS
1943 979
 		return $txt[$key];
1944 980
 	}
1945 981
 
1946
-	/**
1947
-	 * Handle searching for logs.
1948
-	 *
1949
-	 * @param string $url The base_href
1950
-	 * @internal
1951
-	 * @CalledIn SMF 2.0, SMF 2.1
1952
-	 * @version 1.0
1953
-	 * @since 1.0
1954
-	 * @return void No return is generated here.
1955
-	 */
1956
-	private function handleLogSearch(string &$url): void
1957
-	{
1958
-		global $context, $txt;
1959
-
1960
-		// If we have some data from a search, lets bring it back out.
1961
-		$this->search_params = $this->handleLogSearchParams();
1962
-
1963
-		// What we can search.
1964
-		$this->search_types = $this->handleLogSearchTypes();
1965
-		$this->search_params_string = $this->handleLogSearchParamsString();
1966
-		$this->search_params_type = $this->handleLogSearchParamsType();
1967
-
1968
-		$this->search_params_column = $this->search_types[$this->search_params_type]['sql'];
1969
-
1970
-		// Setup the search context.
1971
-		$this->search_params = empty($search_params_string) ? '' : base64_encode(json_encode(array(
1972
-			'string' => $this->search_params_string,
1973
-			'type' => $this->search_params_type,
1974
-		)));
1975
-		$this->logSearch = array(
1976
-			'string' => $this->search_params_string,
1977
-			'type' => $this->search_params_type,
1978
-			'label' => $this->search_types[$this->search_params_type]['label'],
1979
-		);
1980
-
1981
-		if (!empty($this->search_params))
1982
-			$url .= ';params=' . $this->search_params;
1983
-	}
1984
-
1985
-	/**
1986
-	 * Handle Search Params
1987
-	 *
1988
-	 * @internal
1989
-	 * @CalledIn SMF 2.0, SMF 2.1
1990
-	 * @version 1.1
1991
-	 * @since 1.0
1992
-	 * @return bool True upon success, false otherwise.
1993
-	 */
1994
-	private function handleLogSearchParams(): array
1995
-	{
1996
-		// If we have something to search for saved, get it back out.
1997
-		if (!empty($_REQUEST['params']) && empty($_REQUEST['is_search']))
1998
-		{
1999
-			$search_params = base64_decode(strtr($params, array(' ' => '+')));
2000
-			$search_params = $this->decodeJSON($this->search_params);
2001
-
2002
-			if (!empty($search_params))
2003
-				return $search_params;
2004
-		}
2005
-	
2006
-		return array();
2007
-	}
2008
-
2009
-	/**
2010
-	 * Handle Search Types
2011
-	 *
2012
-	 * @internal
2013
-	 * @CalledIn SMF 2.0, SMF 2.1
2014
-	 * @version 1.1
2015
-	 * @since 1.0
2016
-	 * @return array The valid Search Types.
2017
-	 */
2018
-	private function handleLogSearchTypes(): array
2019
-	{
2020
-		return array(
2021
-			'url' => array('sql' => 'l.url', 'label' => $this->txt('sfs_log_search_url')),
2022
-			'member' => array('sql' => 'mem.real_name', 'label' => $this->txt('sfs_log_search_member')),
2023
-			'username' => array('sql' => 'l.username', 'label' => $this->txt('sfs_log_search_username')),
2024
-			'email' => array('sql' => 'l.email', 'label' => $this->txt('sfs_log_search_email')),
2025
-			'ip' => array('sql' => 'lm.ip', 'label' => $this->txt('sfs_log_search_ip')),
2026
-			'ip2' => array('sql' => 'lm.ip2', 'label' => $this->txt('sfs_log_search_ip2'))
2027
-		);
2028
-	}
2029
- 
2030
- 	/**
2031
-	 * Handle Search Params String
2032
-	 *
2033
-	 * @internal
2034
-	 * @CalledIn SMF 2.0, SMF 2.1
2035
-	 * @version 1.1
2036
-	 * @since 1.0
2037
-	 * @return string What we are searching for, validated and cleaned.
2038
-	 */
2039
-	private function handleLogSearchParamsString(): string
2040
-	{
2041
-		if (!isset($this->search_params['string']) || (!empty($_REQUEST['search']) && $this->search_params['string'] != $_REQUEST['search']))
2042
-			return empty($_REQUEST['search']) ? '' : $_REQUEST['search'];
2043
-		else
2044
-			return $this->search_params['string'];
2045
-	}
2046
-
2047
- 	/**
2048
-	 * Handle Search Params Type
2049
-	 *
2050
-	 * @internal
2051
-	 * @CalledIn SMF 2.0, SMF 2.1
2052
-	 * @version 1.1
2053
-	 * @since 1.0
2054
-	 * @return string What we are searching for, validated and cleaned.
2055
-	 */
2056
-	private function handleLogSearchParamsType(): string
2057
-	{
2058
-		global $context;
2059
-
2060
-		if (isset($_REQUEST['search_type']) || empty($this->search_params['type']) || !isset($searchTypes[$this->search_params['type']]))
2061
-			return isset($_REQUEST['search_type']) && isset($this->search_types[$_REQUEST['search_type']]) ? $_REQUEST['search_type'] : (isset($this->search_types[$context['order']]) ? $context['order'] : 'member');
2062
-		else
2063
-			return $this->search_params['type'];
2064
-	}
2065
-
2066 982
 	/**
2067 983
 	 * Create a Ban Group if needed to handle automatic IP bans.
2068 984
 	 * We attempt to use the known ban function to create bans, otherwise we just fall back to a standard insert.
... ...
@@ -2073,7 +989,7 @@ class SFS
2073 989
 	 * @since 1.0
2074 990
 	 * @return bool True upon success, false otherwise.
2075 991
 	 */
2076
-	private function createBanGroup(bool $noChecks = false): bool
992
+	public function createBanGroup(bool $noChecks = false): bool
2077 993
 	{
2078 994
 		global $smcFunc, $modSettings, $sourcedir, $txt;
2079 995
 
... ...
@@ -2294,4 +1210,21 @@ class SFS
2294 1210
 
2295 1211
 		return true;
2296 1212
 	}
1213
+
1214
+	/**
1215
+	 * Get some data
1216
+	 *
1217
+	 * @param string $variable The data we are looking for..
1218
+	 *
1219
+	 * @internal
1220
+	 * @CalledIn SMF 2.0, SMF 2.1
1221
+	 * @version 1.1
1222
+	 * @since 1.0
1223
+	 * @return bool True upon success, false otherwise.
1224
+	 */
1225
+	public function get(string $variable)
1226
+	{
1227
+		if (in_array($variable, array('softwareName', 'softwareVersion')))
1228
+			return $this->{$variable};
1229
+	}
2297 1230
 }
2298 1231
\ No newline at end of file
... ...
@@ -0,0 +1,1136 @@
1
+<?php
2
+
3
+/**
4
+ * The Admin class for Stop Forum Spam
5
+ * @package StopForumSpam
6
+ * @author SleePy <sleepy @ simplemachines (dot) org>
7
+ * @copyright 2019
8
+ * @license 3-Clause BSD https://opensource.org/licenses/BSD-3-Clause
9
+ * @version 1.0.1
10
+ */
11
+class SFSA
12
+{
13
+	public static $SFSAclass = null;
14
+	private $SFSclass = null;
15
+
16
+	/**
17
+	 * @var string URLS we need to SFS for UI presentation.
18
+	 */
19
+	private $urlSFSipCheck = 'https://www.stopforumspam.com/ipcheck/%1$s';
20
+	private $urlSFSsearch = 'https://www.stopforumspam.com/search/%1$s';
21
+
22
+	/**
23
+	 * @var string The URL for the admin page.
24
+	 */
25
+	private $adminPageURL = null;
26
+	private $adminLogURL = null;
27
+
28
+	/**
29
+	 * @var array Our settings information used on saving/changing settings.
30
+	 */
31
+	private $changedSettings = array();
32
+	private $extraVerificationOptions = array();
33
+
34
+	/**
35
+	 * @var mixed Search area handling.
36
+	 */
37
+	private $search_types = array();
38
+	private $search_params = array();
39
+	private $search_params_column = '';
40
+	private $search_params_string = null;
41
+	private $search_params_type = null;
42
+	private $canDeleteLogs = false;
43
+	private $logSearch = array();
44
+
45
+	/**
46
+	 * @var int How long we disable removing logs.
47
+	 */
48
+	private $hoursDisabled = 24;
49
+
50
+	/**
51
+	 * Creates a self reference to the ASL class for use later.
52
+	 *
53
+	 * @version 1.0
54
+	 * @since 1.0
55
+	 * @return void No return is generated
56
+	 */
57
+	public static function selfClass()
58
+	{
59
+		global $smcFunc;
60
+
61
+		if (is_null(self::$SFSAclass))
62
+		{
63
+			self::$SFSAclass = new SFSA();
64
+			$smcFunc['SFSA'] = self::$SFSAclass;
65
+		}
66
+
67
+		return self::$SFSAclass;
68
+	}
69
+
70
+	/**
71
+	 * Build the class, figure out what software/version we have.
72
+	 * Loads up the defaults.
73
+	 *
74
+	 * @CalledIn SMF 2.0, SMF 2.1
75
+	 * @version 1.0
76
+	 * @since 1.0
77
+	 * @return void No return is generated
78
+	 */
79
+	public function __construct()
80
+	{
81
+		global $smcFunc;
82
+	
83
+		$this->SFSclass = &$smcFunc['classSFS'];
84
+
85
+		// Is this SMF 2.0?
86
+		$this->softwareVersion = $smcFunc['classSFS']->get('softwareVersion');
87
+	}
88
+
89
+	/**
90
+	 * Creates the hook to the class for the admin areas.
91
+	 *
92
+	 * @param array $admin_areas A associate array from the software with all valid admin areas.
93
+	 *
94
+	 * @api
95
+	 * @CalledIn SMF 2.0, SMF 2.1
96
+	 * @see SFSA::setupAdminAreas()
97
+	 * @version 1.0
98
+	 * @since 1.0
99
+	 * @uses integrate__admin_areas - Hook SMF2.0
100
+	 * @uses integrate__admin_areas - Hook SMF2.1
101
+	 * @return void No return is generated
102
+	 */
103
+	public static function hook_admin_areas(array &$admin_areas)
104
+	{
105
+		return self::selfClass()->setupAdminAreas($admin_areas);
106
+	}
107
+
108
+	/**
109
+	 * Startup the Admin Panels Additions.
110
+	 * Where things appear are based on what software/version you have.
111
+	 *
112
+	 * @param array $admin_areas A associate array from the software with all valid admin areas.
113
+	 *
114
+	 * @internal
115
+	 * @CalledIn SMF 2.0, SMF 2.1
116
+	 * @version 1.0
117
+	 * @since 1.0
118
+	 * @uses integrate__admin_areas - Hook SMF2.0
119
+	 * @uses integrate__admin_areas - Hook SMF2.1
120
+	 * @return void No return is generated
121
+	 */
122
+	private function setupAdminAreas(array &$admin_areas): void
123
+	{
124
+		global $scripturl;
125
+
126
+		// Add the menu item.
127
+		if ($this->SFSclass->versionCheck('2.0', 'smf'))
128
+		{
129
+			$this->adminPageURL = $scripturl . '?action=admin;area=modsettings;sa=sfs';
130
+			$this->adminLogURL = $scripturl . '?action=admin;area=modsettings;sa=sfslog';
131
+
132
+			$admin_areas['config']['areas']['modsettings']['subsections']['sfs'] = array(
133
+				$this->SFSclass->txt('sfs_admin_area')
134
+			);
135
+			$admin_areas['config']['areas']['modsettings']['subsections']['sfslog'] = array(
136
+				$this->SFSclass->txt('sfs_admin_logs')
137
+			);
138
+		}
139
+		else
140
+		{
141
+			$this->adminPageURL = $scripturl . '?action=admin;area=securitysettings;sa=sfs';
142
+			$this->adminLogURL = $scripturl . '?action=admin;area=logs;sa=sfslog';
143
+
144
+			$admin_areas['config']['areas']['securitysettings']['subsections']['sfs'] = array(
145
+				$this->SFSclass->txt('sfs_admin_area')
146
+			);
147
+			$admin_areas['config']['areas']['securitysettings']['subsections']['sfslog'] = array(
148
+				$this->SFSclass->txt('sfs_admin_logs')
149
+			);
150
+		}
151
+	}
152
+
153
+	/**
154
+	 * Setup the Modification's setup page.
155
+	 * For some versions, we put the logs into the modifications sections, its easier.
156
+	 *
157
+	 * @param array $subActions A associate array from the software with all valid modification sections.
158
+	 *
159
+	 * @api
160
+	 * @CalledIn SMF 2.0, SMF 2.1
161
+	 * @see SFSA::setupModifyModifications()
162
+	 * @version 1.0
163
+	 * @since 1.0
164
+	 * @uses integrate_modify_modifications - Hook SMF2.0
165
+	 * @uses integrate_modify_modifications - Hook SMF2.1
166
+	 * @return void No return is generated
167
+	 */
168
+	public static function hook_modify_modifications(array &$subActions)
169
+	{
170
+		return self::selfClass()->setupModifyModifications($subActions);
171
+	}
172
+
173
+	/**
174
+	 * Setup the Modifications section links.
175
+	 * For some versions we add the logs here as well.
176
+	 *
177
+	 * @param array $subActions A associate array from the software with all valid modification sections.
178
+	 *
179
+	 * @internal
180
+	 * @CalledIn SMF 2.0, SMF 2.1
181
+	 * @version 1.0
182
+	 * @since 1.0
183
+	 * @uses integrate_modify_modifications - Hook SMF2.0
184
+	 * @uses integrate_modify_modifications - Hook SMF2.1
185
+	 * @return void No return is generated
186
+	 */
187
+	private function setupModifyModifications(array &$subActions): void
188
+	{
189
+		$subActions['sfs'] = 'SFSA::startupAdminConfiguration';
190
+
191
+		// Only in SMF 2.0 do we drop logs here.
192
+		if ($this->SFSclass->versionCheck('2.0', 'smf'))
193
+			$subActions['sfslog'] = 'SFSA::startupLogs';
194
+	}
195
+
196
+	/**
197
+	 * The configuration caller.
198
+	 *
199
+	 * @param bool $return_config If true, returns the configuration options for searches.
200
+	 *
201
+	 * @api
202
+	 * @CalledIn SMF 2.0, SMF 2.1
203
+	 * @see SFSA::setupSFSConfiguration
204
+	 * @version 1.0
205
+	 * @since 1.0
206
+	 * @uses integrate_modify_modifications - Hook SMF2.0
207
+	 * @uses integrate_modify_modifications - Hook SMF2.1
208
+	 * @return void No return is generated
209
+	 */
210
+	public static function startupAdminConfiguration(bool $return_config = false)
211
+	{
212
+		return self::selfClass()->setupSFSConfiguration($return_config);
213
+	}
214
+
215
+	/**
216
+	 * The actual settings page.
217
+	 *
218
+	 * @param bool $return_config If true, returns the configuration options for searches.
219
+	 *
220
+	 * @internal
221
+	 * @CalledIn SMF 2.0, SMF 2.1
222
+	 * @version 1.0
223
+	 * @since 1.0
224
+	 * @uses integrate_modify_modifications - Hook SMF2.0
225
+	 * @uses integrate_modify_modifications - Hook SMF2.1
226
+	 * @return void No return is generated
227
+	 */
228
+	private function setupSFSConfiguration(bool $return_config = false): array
229
+	{
230
+		global $scripturl, $context, $settings, $sc, $modSettings;
231
+
232
+		$config_vars = array(
233
+				array('title', 'sfsgentitle', 'label' => $this->SFSclass->txt('sfs_general_title')),
234
+
235
+				array('check', 'sfs_enabled'),
236
+				array('int', 'sfs_expire'),
237
+			'',
238
+				array('check', 'sfs_emailcheck'),
239
+			'',
240
+				array('check', 'sfs_usernamecheck'),
241
+				array('float', 'sfs_username_confidence'),
242
+			'',
243
+				array('check', 'sfs_ipcheck'),
244
+				array('check', 'sfs_ipcheck_autoban'),
245
+			'',
246
+				array('select', 'sfs_region', $this->SFSclass->sfsServerMapping('config')),
247
+			'',
248
+				array('check', 'sfs_wildcard_email'),
249
+				array('check', 'sfs_wildcard_username'),
250
+				array('check', 'sfs_wildcard_ip'),
251
+			'',
252
+				array('select', 'sfs_tor_check', array(
253
+					0 => $this->SFSclass->txt('sfs_tor_check_block'),
254
+					1 => $this->SFSclass->txt('sfs_tor_check_ignore'),
255
+					2 => $this->SFSclass->txt('sfs_tor_check_bad'),
256
+				)),
257
+
258
+			'',
259
+				array('title', 'sfsverftitle', 'label' => $this->SFSclass->txt('sfs_verification_title')),
260
+				array('desc', 'sfsverfdesc', 'label' => $this->SFSclass->txt('sfs_verification_desc')),
261
+				array('select', 'sfs_verification_options', array(
262
+					'post' => $this->SFSclass->txt('sfs_verification_options_post'),
263
+					'report' => $this->SFSclass->txt('sfs_verification_options_report'),
264
+					'search' => $this->SFSclass->txt('sfs_verification_options_search'),
265
+				), 'multiple' => true),			
266
+				array('text', 'sfs_verification_options_extra', 'subtext' => $this->SFSclass->txt('sfs_verification_options_extra_subtext')),
267
+
268
+			'',
269
+				array('select', 'sfs_verOptionsMembers', array(
270
+					'post' => $this->SFSclass->txt('sfs_verification_options_post'),
271
+					'search' => $this->SFSclass->txt('sfs_verification_options_search'),
272
+				), 'multiple' => true),
273
+				array('text', 'sfs_verOptionsMemExtra', 'subtext' => $this->SFSclass->txt('sfs_verification_options_extra_subtext')),
274
+				array('int', 'sfs_verfOptMemPostThreshold'),
275
+			'',
276
+				array('check', 'sfs_log_debug'),
277
+		);
278
+
279
+		if ($return_config)
280
+			return $config_vars;
281
+
282
+		// Saving?
283
+		if (isset($_GET['save']))
284
+		{
285
+			// Turn the defaults off.
286
+			$this->unloadDefaults();
287
+			checkSession();
288
+
289
+			// If we are automatically banning IPs, make sure we have a ban group.
290
+			if (isset($_POST['sfs_ipcheck_autoban']) && empty($modSettings['sfs_ipcheck_autoban_group']))
291
+				$this->SFSclass->createBanGroup(true);
292
+
293
+			saveDBSettings($config_vars);
294
+
295
+			writeLog();
296
+			redirectexit($this->adminPageURL);
297
+		}
298
+
299
+		$context['post_url'] = $this->adminPageURL . ';save';
300
+
301
+		prepareDBSettingContext($config_vars);
302
+
303
+		return array();
304
+	}
305
+
306
+	/**
307
+	 * In some software/versions, we can hook into the logs section.
308
+	 * In others we hook into the modifications settings.
309
+	 *
310
+	 * @param array $log_functions All possible log functions.
311
+	 *
312
+	 * @api
313
+	 * @CalledIn SMF 2.1
314
+	 * @See SFSA::startupLogs
315
+	 * @version 1.0
316
+	 * @since 1.0
317
+	 * @uses integrate_manage_logs - Hook SMF2.1
318
+	 * @return void No return is generated
319
+	 */
320
+	public static function hook_manage_logs(array &$log_functions): bool
321
+	{
322
+		// Add our logs sub action.
323
+		$log_functions['sfslog'] = array('StopForumSpam.php', 'startupLogs');
324
+
325
+		return self::selfClass()->AddToLogMenu($log_functions);
326
+	}
327
+
328
+	/**
329
+	 * Add the SFS logs to the log menu.
330
+	 *
331
+	 * @param array $log_functions All possible log functions.
332
+	 *
333
+	 * @CalledIn SMF 2.1
334
+	 * @See SFSA::startupLogs
335
+	 * @version 1.1
336
+	 * @since 1.1
337
+	 * @return void No return is generated
338
+	 */
339
+	public function AddToLogMenu(array &$log_functions): bool
340
+	{
341
+		global $context;
342
+
343
+		$context[$context['admin_menu_name']]['tab_data']['tabs']['sfslog'] = array(
344
+			'description' => $this->SFSclass->txt('sfs_admin_logs'),
345
+		);
346
+
347
+		return true;
348
+	}
349
+
350
+	/**
351
+	 * Log startup caller.
352
+	 * This has a $return_config just for simply complying with properly for searching the admin panel.
353
+	 *
354
+	 * @param bool $return_config If true, returns empty array to prevent breaking old SMF installs.
355
+	 *
356
+	 * @api
357
+	 * @CalledIn SMF 2.1
358
+	 * @See SFSA::loadLogs
359
+	 * @version 1.0
360
+	 * @since 1.0
361
+	 * @uses hook_manage_logs - Hook SMF2.1
362
+	 * @uses setupModifyModifications - Injected SMF2.0
363
+	 * @return void No return is generated
364
+	 */
365
+	public static function startupLogs(bool $return_config = false): array
366
+	{
367
+		return self::selfClass()->loadLogs();
368
+	}
369
+
370
+	/**
371
+	 * Actually show the logs.
372
+	 * This has a $return_config just for simply complying with properly for searching the admin panel.
373
+	 *
374
+	 * @param bool $return_config If true, returns empty array to prevent breaking old SMF installs.
375
+	 *
376
+	 * @api
377
+	 * @CalledIn SMF2.0, SMF 2.1
378
+	 * @See SFSA::getSFSLogEntries
379
+	 * @See SFSA::getSFSLogEntriesCount
380
+	 * @version 1.0
381
+	 * @since 1.0
382
+	 * @uses hook_manage_logs - Hook SMF2.1
383
+	 * @uses setupModifyModifications - Injected SMF2.0
384
+	 * @return void No return is generated
385
+	 */
386
+	public function loadLogs(bool $return_config = false): array
387
+	{
388
+		global $context, $smcFunc, $sourcedir;
389
+
390
+		// No Configs.
391
+		if ($return_config)
392
+			return array();
393
+
394
+		loadLanguage('Modlog');
395
+
396
+		$context['form_url'] = $this->adminLogURL;
397
+		$context['log_url'] = $this->adminLogURL;
398
+		$context['page_title'] = $this->SFSclass->txt('sfs_admin_logs');
399
+		$this->canDeleteLogs = allowedTo('admin_forum');
400
+
401
+		// Remove all..
402
+		if ((isset($_POST['removeall']) || isset($_POST['delete'])) && $this->canDeleteLogs)
403
+			$this->handleLogDeletes();
404
+
405
+		$sort_types = array(
406
+			'id_type' =>'l.id_type',
407
+			'log_time' => 'l.log_time',
408
+			'url' => 'l.url',
409
+			'member' => 'mem.id_member',
410
+			'username' => 'l.username',
411
+			'email' => 'l.email',
412
+			'ip' => 'l.ip',
413
+			'ip2' => 'l.ip2',
414
+		);
415
+
416
+		$context['order'] = isset($_REQUEST['sort']) && isset($sort_types[$_REQUEST['sort']]) ? $_REQUEST['sort'] : 'time';
417
+
418
+		// Handle searches.
419
+		$this->handleLogSearch($context['log_url']);
420
+
421
+		require_once($sourcedir . '/Subs-List.php');
422
+
423
+		$listOptions = array(
424
+			'id' => 'sfslog_list',
425
+			'title' => $this->SFSclass->txt('sfs_admin_logs'),
426
+			'width' => '100%',
427
+			'items_per_page' => '50',
428
+			'no_items_label' => $this->SFSclass->txt('sfs_log_no_entries_found'),
429
+			'base_href' => $context['log_url'],
430
+			'default_sort_col' => 'time',
431
+			'get_items' => $this->loadLogsGetItems(),
432
+			'get_count' => $this->loadLogsGetCount(),
433
+			// This assumes we are viewing by user.
434
+			'columns' => array(
435
+				'type' => $this->loadLogsColumnType(),
436
+				'time' => $this->loadLogsColumnTime(),
437
+				'url' => $this->loadLogsColumnURL(),
438
+				'member' => $this->loadLogsColumnMember(),
439
+				'username' => $this->loadLogsColumnUsername(),
440
+				'email' => $this->loadLogsColumnEmail(),
441
+				'ip' => $this->loadLogsColumnIP(),
442
+				'ip2' => $this->loadLogsColumnIP(true),
443
+				'checks' => $this->loadLogsColumnChecks(),
444
+				'result' => $this->loadLogsColumnResult(),
445
+				'delete' => $this->loadLogsColumnDelete(),
446
+			),
447
+			'form' => array(
448
+				'href' => $context['form_url'],
449
+				'include_sort' => true,
450
+				'include_start' => true,
451
+				'hidden_fields' => array(
452
+					$context['session_var'] => $context['session_id'],
453
+					'params' => $this->search_params
454
+				),
455
+			),
456
+			'additional_rows' => array(
457
+				$this->loadLogsGetAddtionalRow(),
458
+			),
459
+		);
460
+
461
+		// Create the watched user list.
462
+		createList($listOptions);
463
+
464
+		$context['sub_template'] = 'show_list';
465
+		$context['default_list'] = 'sfslog_list';
466
+
467
+		return array();
468
+	}
469
+
470
+	/**
471
+	 * Handle when we want to delete a log and what to do.
472
+	 *
473
+	 * @internal
474
+	 * @CalledIn SMF2.0, SMF 2.1
475
+	 * @version 1.1
476
+	 * @since 1.1
477
+	 * @return void Nothing is returned, the logs are deleted as requested and admin redirected.
478
+	 */
479
+	private function handleLogDeletes(): void
480
+	{
481
+		if (isset($_POST['removeall']) && $this->canDeleteLogs)
482
+			$this->removeAllLogs();
483
+		elseif (!empty($_POST['remove']) && isset($_POST['delete']) && $this->canDeleteLogs)
484
+			$this->removeLogs(array_unique($_POST['delete']));
485
+	}
486
+
487
+	/**
488
+	 * loadLogs - Get Items.
489
+	 *
490
+	 * @internal
491
+	 * @CalledIn SMF2.0, SMF 2.1
492
+	 * @version 1.1
493
+	 * @since 1.1
494
+	 * @return array The options for the get_items
495
+	 */
496
+	private function loadLogsGetItems(): array
497
+	{
498
+		return array(
499
+			'function' => array($this, 'getSFSLogEntries'),
500
+			'params' => array(
501
+				(!empty($this->logSearch['string']) ? ' INSTR({raw:sql_type}, {string:search_string})' : ''),
502
+				array('sql_type' => $this->search_params_column, 'search_string' => $this->logSearch['string']),
503
+			),
504
+		);
505
+	}
506
+
507
+	/**
508
+	 * loadLogs - Get Count.
509
+	 *
510
+	 * @internal
511
+	 * @CalledIn SMF2.0, SMF 2.1
512
+	 * @version 1.1
513
+	 * @since 1.1
514
+	 * @return array The options for the get_items
515
+	 */
516
+	private function loadLogsGetCount(): array
517
+	{
518
+		return array(
519
+			'function' => array($this, 'getSFSLogEntriesCount'),
520
+			'params' => array(
521
+				(!empty($this->logSearch['string']) ? ' INSTR({raw:sql_type}, {string:search_string})' : ''),
522
+				array('sql_type' => $this->search_params_column, 'search_string' => $this->logSearch['string']),
523
+			),
524
+		);
525
+	}
526
+
527
+	/**
528
+	 * loadLogs - Load an additional row, for mostly deleting stuff.
529
+	 *
530
+	 * @internal
531
+	 * @CalledIn SMF2.0, SMF 2.1
532
+	 * @version 1.1
533
+	 * @since 1.1
534
+	 * @return array The options for the get_items
535
+	 */
536
+	private function loadLogsGetAddtionalRow(): array
537
+	{
538
+		global $smcFunc;
539
+
540
+		return array(
541
+			'position' => 'below_table_data',
542
+			'value' => '
543
+				' . $this->SFSclass->txt('sfs_log_search') . ' (' . $this->logSearch['label'] . '):
544
+				<input type="text" name="search" size="18" value="' . $smcFunc['htmlspecialchars']($this->logSearch['string']) . '" class="input_text" /> <input type="submit" name="is_search" value="' . $this->SFSclass->txt('modlog_go') . '" class="button_submit" />
545
+				' . ($this->canDeleteLogs ? ' |
546
+					<input type="submit" name="remove" value="' . $this->SFSclass->txt('modlog_remove') . '" class="button_submit" />
547
+					<input type="submit" name="removeall" value="' . $this->SFSclass->txt('modlog_removeall') . '" class="button_submit" />' : ''),
548
+		);
549
+	}
550
+
551
+
552
+	/**
553
+	 * loadLogs - Column - Type.
554
+	 *
555
+	 * @internal
556
+	 * @CalledIn SMF2.0, SMF 2.1
557
+	 * @version 1.1
558
+	 * @since 1.1
559
+	 * @return array The options for the column
560
+	 */
561
+	private function loadLogsColumnType(): array
562
+	{
563
+		return array(
564
+			'header' => array(
565
+				'value' => $this->SFSclass->txt('sfs_log_header_type'),
566
+				'class' => 'lefttext',
567
+			),
568
+			'data' => array(
569
+				'db' => 'type',
570
+				'class' => 'smalltext',
571
+			),
572
+			'sort' => array(
573
+			),
574
+		);
575
+	}
576
+
577
+	/**
578
+	 * loadLogs - Column - Time.
579
+	 *
580
+	 * @internal
581
+	 * @CalledIn SMF2.0, SMF 2.1
582
+	 * @version 1.1
583
+	 * @since 1.1
584
+	 * @return array The options for the column
585
+	 */
586
+	private function loadLogsColumnTime(): array
587
+	{
588
+		return array(
589
+			'header' => array(
590
+				'value' => $this->SFSclass->txt('sfs_log_header_time'),
591
+				'class' => 'lefttext',
592
+			),
593
+			'data' => array(
594
+				'db' => 'time',
595
+				'class' => 'smalltext',
596
+			),
597
+			'sort' => array(
598
+				'default' => 'l.log_time DESC',
599
+				'reverse' => 'l.log_time',
600
+			),
601
+		);
602
+	}
603
+
604
+	/**
605
+	 * loadLogs - Column - URL.
606
+	 *
607
+	 * @internal
608
+	 * @CalledIn SMF2.0, SMF 2.1
609
+	 * @version 1.1
610
+	 * @since 1.1
611
+	 * @return array The options for the column
612
+	 */
613
+	private function loadLogsColumnURL(): array
614
+	{
615
+		return array(
616
+			'header' => array(
617
+				'value' => $this->SFSclass->txt('sfs_log_header_url'),
618
+				'class' => 'lefttext',
619
+			),
620
+			'data' => array(
621
+				'db' => 'url',
622
+				'class' => 'smalltext',
623
+				'style' => 'word-break: break-word;',
624
+			),
625
+			'sort' => array(
626
+				'default' => 'l.url DESC',
627
+				'reverse' => 'l.url',
628
+			),
629
+		);
630
+	}
631
+
632
+	/**
633
+	 * loadLogs - Column - Member.
634
+	 *
635
+	 * @internal
636
+	 * @CalledIn SMF2.0, SMF 2.1
637
+	 * @version 1.1
638
+	 * @since 1.1
639
+	 * @return array The options for the column
640
+	 */
641
+	private function loadLogsColumnMember(): array
642
+	{
643
+		return array(
644
+			'header' => array(
645
+				'value' => $this->SFSclass->txt('sfs_log_header_member'),
646
+				'class' => 'lefttext',
647
+			),
648
+			'data' => array(
649
+				'db' => 'member_link',
650
+				'class' => 'smalltext',
651
+			),
652
+			'sort' => array(
653
+				'default' => 'mem.id_member',
654
+				'reverse' => 'mem.id_member DESC',
655
+			),
656
+		);
657
+	}
658
+
659
+	/**
660
+	 * loadLogs - Column - Username.
661
+	 *
662
+	 * @internal
663
+	 * @CalledIn SMF2.0, SMF 2.1
664
+	 * @version 1.1
665
+	 * @since 1.1
666
+	 * @return array The options for the column
667
+	 */
668
+	private function loadLogsColumnUsername(): array
669
+	{
670
+		return array(
671
+			'header' => array(
672
+				'value' => $this->SFSclass->txt('sfs_log_header_username'),
673
+				'class' => 'lefttext',
674
+			),
675
+			'data' => array(
676
+				'db' => 'username',
677
+				'class' => 'smalltext',
678
+			),
679
+			'sort' => array(
680
+				'default' => 'l.username',
681
+				'reverse' => 'l.username DESC',
682
+			),
683
+		);
684
+	}
685
+
686
+	/**
687
+	 * loadLogs - Column - Email.
688
+	 *
689
+	 * @internal
690
+	 * @CalledIn SMF2.0, SMF 2.1
691
+	 * @version 1.1
692
+	 * @since 1.1
693
+	 * @return array The options for the column
694
+	 */
695
+	private function loadLogsColumnEmail(): array
696
+	{
697
+		return array(
698
+			'header' => array(
699
+				'value' => $this->SFSclass->txt('sfs_log_header_email'),
700
+				'class' => 'lefttext',
701
+			),
702
+			'data' => array(
703
+				'db' => 'email',
704
+				'class' => 'smalltext',
705
+			),
706
+			'sort' => array(
707
+				'default' => 'l.email',
708
+				'reverse' => 'l.email DESC',
709
+			),
710
+		);
711
+	}
712
+
713
+	/**
714
+	 * loadLogs - Column - IP.
715
+	 *
716
+	 * @param string $ip2 If true, use ip2
717
+	 * @internal
718
+	 * @CalledIn SMF2.0, SMF 2.1
719
+	 * @version 1.1
720
+	 * @since 1.1
721
+	 * @return array The options for the column
722
+	 */
723
+	private function loadLogsColumnIP(bool $ip2 = false): array
724
+	{
725
+		return array(
726
+			'header' => array(
727
+				'value' => $this->SFSclass->txt('sfs_log_header_ip' . ($ip2 ? '2' : '')),
728
+				'class' => 'lefttext',
729
+			),
730
+			'data' => array(
731
+				'db' => 'ip' . ($ip2 ? '2' : ''),
732
+				'class' => 'smalltext',
733
+			),
734
+			'sort' => array(
735
+				'default' => 'l.ip' . ($ip2 ? '2' : ''),
736
+				'reverse' => 'l.ip' . ($ip2 ? '2' : '') . ' DESC',
737
+			),
738
+		);
739
+	}
740
+
741
+	/**
742
+	 * loadLogs - Column - Checks.
743
+	 *
744
+	 * @internal
745
+	 * @CalledIn SMF2.0, SMF 2.1
746
+	 * @version 1.1
747
+	 * @since 1.1
748
+	 * @return array The options for the column
749
+	 */
750
+	private function loadLogsColumnChecks(): array
751
+	{
752
+		return array(
753
+			'header' => array(
754
+				'value' => $this->SFSclass->txt('sfs_log_checks'),
755
+				'class' => 'lefttext',
756
+			),
757
+			'data' => array(
758
+				'db' => 'checks',
759
+				'class' => 'smalltext',
760
+				'style' => 'word-break: break-word;',
761
+			),
762
+			'sort' => array(),
763
+		);
764
+	}
765
+
766
+	/**
767
+	 * loadLogs - Column - Result.
768
+	 *
769
+	 * @internal
770
+	 * @CalledIn SMF2.0, SMF 2.1
771
+	 * @version 1.1
772
+	 * @since 1.1
773
+	 * @return array The options for the column
774
+	 */
775
+	private function loadLogsColumnResult(): array
776
+	{
777
+		return array(
778
+			'header' => array(
779
+				'value' => $this->SFSclass->txt('sfs_log_result'),
780
+				'class' => 'lefttext',
781
+			),
782
+			'data' => array(
783
+				'db' => 'result',
784
+				'class' => 'smalltext',
785
+				'style' => 'word-break: break-word;',
786
+			),
787
+			'sort' => array(),
788
+		);
789
+	}
790
+
791
+	/**
792
+	 * loadLogs - Column - Delete.
793
+	 *
794
+	 * @internal
795
+	 * @CalledIn SMF2.0, SMF 2.1
796
+	 * @version 1.1
797
+	 * @since 1.1
798
+	 * @return array The options for the column
799
+	 */
800
+	private function loadLogsColumnDelete(): array
801
+	{
802
+		return array(
803
+			'header' => array(
804
+				'value' => '<input type="checkbox" name="all" class="input_check" onclick="invertAll(this, this.form);" />',
805
+			),
806
+			'data' => array(
807
+				'function' => function($entry)
808
+				{
809
+					return '<input type="checkbox" class="input_check" name="delete[]" value="' . $entry['id'] . '"' . ($entry['editable'] ? '' : ' disabled="disabled"') . ' />';
810
+				},
811
+				'style' => 'text-align: center;',
812
+			),
813
+		);
814
+	}
815
+
816
+	/**
817
+	 * Get the log data and returns it ready to go for GenericList handling.
818
+	 *
819
+	 * @param int $start The index for where we offset or start at for the list
820
+	 * @param int $items_per_page How many items we are going to show on this page.
821
+	 * @param string $sort The column we are sorting by.
822
+	 * @param string $query_string The search string we are using to filter log data.
823
+	 * @param array $query_params Extra parameters for searching.
824
+	 *
825
+	 * @api
826
+	 * @CalledIn SMF 2.0, SMF 2.1
827
+	 * @See SFSA::loadLogs
828
+	 * @version 1.0
829
+	 * @since 1.0
830
+	 * @uses hook_manage_logs - Hook SMF2.1
831
+	 * @uses setupModifyModifications - Injected SMF2.0
832
+	 * @return void No return is generated
833
+	 */
834
+	public function getSFSLogEntries(int $start, int $items_per_page, string $sort, string $query_string = '', array $query_params = array()): array
835
+	{
836
+		global $scripturl, $context, $smcFunc;
837
+
838
+		// Fetch all of our logs.
839
+		$result = $smcFunc['db_query']('', '
840
+			SELECT
841
+				l.id_sfs,
842
+				l.id_type,
843
+				l.log_time,
844
+				l.url,
845
+				l.id_member,
846
+				l.username,
847
+				l.email,
848
+				l.ip,
849
+				l.ip2,
850
+				l.checks,
851
+				l.result,
852
+				mem.real_name,
853
+				mg.group_name
854
+			FROM {db_prefix}log_sfs AS l
855
+				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = l.id_member)
856
+				LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_group_id} THEN mem.id_post_group ELSE mem.id_group END)
857
+				WHERE id_type IS NOT NULL'
858
+				. (!empty($query_string) ? '
859
+					AND ' . $query_string : '') . '
860
+			ORDER BY ' . $sort . '
861
+			LIMIT ' . $start . ', ' . $items_per_page,
862
+			array_merge($query_params, array(
863
+				'reg_group_id' => 0,
864
+			))
865
+		);
866
+
867
+		$entries = array();
868
+		while ($row = $smcFunc['db_fetch_assoc']($result))
869
+		{
870
+			$entries[$row['id_sfs']] = array(
871
+				'id' => $row['id_sfs'],
872
+				'type' => $this->SFSclass->txt('sfs_log_types_' . $row['id_type']),
873
+				'time' => timeformat($row['log_time']),
874
+				'url' => preg_replace('~http(s)?://~i', 'hxxp\\1://', $row['url']),
875
+				'timestamp' => forum_time(true, $row['log_time']),
876
+				'member_link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>' : (empty($row['real_name']) ? ($this->SFSclass->txt('guest') . (!empty($row['extra']['member_acted']) ? ' (' . $row['extra']['member_acted'] . ')' : '')) : $row['real_name']),
877
+				'username' => $row['username'],
878
+				'email' => $row['email'],
879
+				'ip' => '<a href="' . sprintf($this->urlSFSipCheck, $row['ip']) . '">' . $row['ip'] . '</a>',
880
+				'ip2' => '<a href="' . sprintf($this->urlSFSipCheck, $row['ip2']) . '">' . $row['ip2'] . '</a>',
881
+				'editable' => true, //time() > $row['log_time'] + $this->hoursDisabled * 3600,
882
+				'checks_raw' => $row['checks'],
883
+				'result_raw' => $row['result'],
884
+			);
885
+
886
+			$checksDecoded = $this->SFSclass->decodeJSON($row['checks']);
887
+
888
+			// If we know what check triggered this, link it up to be searched.
889
+			if ($row['id_type'] == 1)
890
+				$entries[$row['id_sfs']]['checks'] = '<a href="' . sprintf($this->urlSFSsearch, $checksDecoded['value']) . '">' . $checksDecoded['value'] . '</a>';
891
+			elseif ($row['id_type'] == 2)
892
+				$entries[$row['id_sfs']]['checks'] = '<a href="' . sprintf($this->urlSFSsearch, $checksDecoded['value']) . '">' . $checksDecoded['value'] . '</a>';
893
+			elseif ($row['id_type'] == 3)
894
+				$entries[$row['id_sfs']]['checks'] = '<a href="' . sprintf($this->urlSFSsearch, $checksDecoded['value']) . '">' . $checksDecoded['value'] . '</a>';
895
+			// No idea what triggered it, parse it out cleanly.  Could be debug data as well.
896
+			else
897
+			{
898
+				$entries[$row['id_sfs']]['checks'] = '';
899
+
900
+				foreach ($checksDecoded as $ckey => $vkey)
901
+					foreach ($vkey as $key => $value)
902
+						$entries[$row['id_sfs']]['checks'] .= ucfirst($key) . ':' . $value . '<br>';					
903
+			}
904
+
905
+			// This tells us what it matched on exactly.
906
+			if (strpos($row['result'], ',') !== false)
907
+			{
908
+				list($resultType, $resultMatch, $extra) = explode(',', $row['result'] . ',,,');
909
+				$entries[$row['id_sfs']]['result'] = sprintf($this->SFSclass->txt('sfs_log_matched_on'), $resultType, $resultMatch);
910
+
911
+				// If this was a IP ban, note it.
912
+				if ($resultType == 'ip' && !empty($extra))
913
+					$entries[$row['id_sfs']]['result'] .= ' ' . $this->SFSclass->txt('sfs_log_auto_banned');			
914
+				if ($resultType == 'username' && !empty($extra))
915
+					$entries[$row['id_sfs']]['result'] .= ' ' . sprintf($this->SFSclass->txt('sfs_log_confidence'), $extra);			
916
+			}
917
+			else
918
+				$entries[$row['id_sfs']]['result'] = $row['result'];
919
+			
920
+		}
921
+		$smcFunc['db_free_result']($result);
922
+
923
+		return $entries;
924
+	}
925
+
926
+	/**
927
+	 * Get the log counts and returns it ready to go for GenericList handling.
928
+	 *
929
+	 * @param string $query_string The search string we are using to filter log data.
930
+	 * @param array $query_params Extra parameters for searching.
931
+	 *
932
+	 * @api
933
+	 * @CalledIn SMF 2.0, SMF 2.1
934
+	 * @See SFSA::loadLogs
935
+	 * @version 1.0
936
+	 * @since 1.0
937
+	 * @uses hook_manage_logs - Hook SMF2.1
938
+	 * @uses setupModifyModifications - Injected SMF2.0
939
+	 * @return void No return is generated
940
+	 */
941
+	public function getSFSLogEntriesCount(string $query_string = '', array $query_params = array()): int
942
+	{
943
+		global $smcFunc, $user_info;
944
+
945
+		$result = $smcFunc['db_query']('', '
946
+			SELECT COUNT(*)
947
+			FROM {db_prefix}log_sfs AS l
948
+				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = l.id_member)
949
+				LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_group_id} THEN mem.id_post_group ELSE mem.id_group END)
950
+				WHERE id_type IS NOT NULL'
951
+				. (!empty($query_string) ? '
952
+					AND ' . $query_string : ''),
953
+			array_merge($query_params, array(
954
+				'reg_group_id' => 0,
955
+			))
956
+		);
957
+		list ($entry_count) = $smcFunc['db_fetch_row']($result);
958
+		$smcFunc['db_free_result']($result);
959
+
960
+		return (int) $entry_count;
961
+	}
962
+
963
+	/**
964
+	 * Remove all logs, except those less than 24 hours old.
965
+	 *
966
+	 * @api
967
+	 * @CalledIn SMF 2.0, SMF 2.1
968
+	 * @See SFSA::loadLogs
969
+	 * @version 1.0
970
+	 * @since 1.0
971
+	 * @return void No return is generated
972
+	 */
973
+	private function removeAllLogs(): void
974
+	{
975
+		global $smcFunc;
976
+
977
+		checkSession();
978
+
979
+		$smcFunc['db_query']('', '
980
+			DELETE FROM {db_prefix}log_sfs
981
+			WHERE log_time < {int:twenty_four_hours_wait}',
982
+			array(
983
+				'twenty_four_hours_wait' => time() - $this->hoursDisabled * 3600,
984
+			)
985
+		);
986
+	}
987
+
988
+	/**
989
+	 * Remove specific logs, except those less than 24 hours old.
990
+	 *
991
+	 * @param array $entries A array of the ids that we want to remove.
992
+	 *
993
+	 * @api
994
+	 * @CalledIn SMF 2.0, SMF 2.1
995
+	 * @See SFSA::loadLogs
996
+	 * @version 1.0
997
+	 * @since 1.0
998
+	 * @return void No return is generated
999
+	 */
1000
+	private function removeLogs(array $entries): void
1001
+	{
1002
+		global $smcFunc;
1003
+
1004
+		checkSession();
1005
+
1006
+		$smcFunc['db_query']('', '
1007
+			DELETE FROM {db_prefix}log_sfs
1008
+			WHERE id_sfs IN ({array_string:delete_actions})
1009
+				AND log_time < {int:twenty_four_hours_wait}',
1010
+			array(
1011
+				'twenty_four_hours_wait' => time() - $this->hoursDisabled * 3600,
1012
+				'delete_actions' => $entries,
1013
+			)
1014
+		);
1015
+	}
1016
+
1017
+	/**
1018
+	 * Handle searching for logs.
1019
+	 *
1020
+	 * @param string $url The base_href
1021
+	 * @internal
1022
+	 * @CalledIn SMF 2.0, SMF 2.1
1023
+	 * @version 1.0
1024
+	 * @since 1.0
1025
+	 * @return void No return is generated here.
1026
+	 */
1027
+	private function handleLogSearch(string &$url): void
1028
+	{
1029
+		global $context, $txt;
1030
+
1031
+		// If we have some data from a search, lets bring it back out.
1032
+		$this->search_params = $this->handleLogSearchParams();
1033
+
1034
+		// What we can search.
1035
+		$this->search_types = $this->handleLogSearchTypes();
1036
+		$this->search_params_string = $this->handleLogSearchParamsString();
1037
+		$this->search_params_type = $this->handleLogSearchParamsType();
1038
+
1039
+		$this->search_params_column = $this->search_types[$this->search_params_type]['sql'];
1040
+
1041
+		// Setup the search context.
1042
+		$this->search_params = empty($this->search_params_string) ? '' : base64_encode(json_encode(array(
1043
+			'string' => $this->search_params_string,
1044
+			'type' => $this->search_params_type,
1045
+		)));
1046
+		$this->logSearch = array(
1047
+			'string' => $this->search_params_string,
1048
+			'type' => $this->search_params_type,
1049
+			'label' => $this->search_types[$this->search_params_type]['label'],
1050
+		);
1051
+
1052
+		if (!empty($this->search_params))
1053
+			$url .= ';params=' . $this->search_params;
1054
+	}
1055
+
1056
+	/**
1057
+	 * Handle Search Params
1058
+	 *
1059
+	 * @internal
1060
+	 * @CalledIn SMF 2.0, SMF 2.1
1061
+	 * @version 1.1
1062
+	 * @since 1.0
1063
+	 * @return bool True upon success, false otherwise.
1064
+	 */
1065
+	private function handleLogSearchParams(): array
1066
+	{
1067
+		// If we have something to search for saved, get it back out.
1068
+		if (!empty($_REQUEST['params']) && empty($_REQUEST['is_search']))
1069
+		{
1070
+			$search_params = base64_decode(strtr($params, array(' ' => '+')));
1071
+			$search_params = $this->SFSclass->decodeJSON($search_params);
1072
+
1073
+			if (!empty($search_params))
1074
+				return $search_params;
1075
+		}
1076
+	
1077
+		return array();
1078
+	}
1079
+
1080
+	/**
1081
+	 * Handle Search Types
1082
+	 *
1083
+	 * @internal
1084
+	 * @CalledIn SMF 2.0, SMF 2.1
1085
+	 * @version 1.1
1086
+	 * @since 1.0
1087
+	 * @return array The valid Search Types.
1088
+	 */
1089
+	private function handleLogSearchTypes(): array
1090
+	{
1091
+		return array(
1092
+			'url' => array('sql' => 'l.url', 'label' => $this->SFSclass->txt('sfs_log_search_url')),
1093
+			'member' => array('sql' => 'mem.real_name', 'label' => $this->SFSclass->txt('sfs_log_search_member')),
1094
+			'username' => array('sql' => 'l.username', 'label' => $this->SFSclass->txt('sfs_log_search_username')),
1095
+			'email' => array('sql' => 'l.email', 'label' => $this->SFSclass->txt('sfs_log_search_email')),
1096
+			'ip' => array('sql' => 'lm.ip', 'label' => $this->SFSclass->txt('sfs_log_search_ip')),
1097
+			'ip2' => array('sql' => 'lm.ip2', 'label' => $this->SFSclass->txt('sfs_log_search_ip2'))
1098
+		);
1099
+	}
1100
+ 
1101
+	/**
1102
+	 * Handle Search Params String
1103
+	 *
1104
+	 * @internal
1105
+	 * @CalledIn SMF 2.0, SMF 2.1
1106
+	 * @version 1.1
1107
+	 * @since 1.0
1108
+	 * @return string What we are searching for, validated and cleaned.
1109
+	 */
1110
+	private function handleLogSearchParamsString(): string
1111
+	{
1112
+		if (!isset($this->search_params['string']) || (!empty($_REQUEST['search']) && $this->search_params['string'] != $_REQUEST['search']))
1113
+			return empty($_REQUEST['search']) ? '' : $_REQUEST['search'];
1114
+		else
1115
+			return $this->search_params['string'];
1116
+	}
1117
+
1118
+ 	/**
1119
+	 * Handle Search Params Type
1120
+	 *
1121
+	 * @internal
1122
+	 * @CalledIn SMF 2.0, SMF 2.1
1123
+	 * @version 1.1
1124
+	 * @since 1.0
1125
+	 * @return string What we are searching for, validated and cleaned.
1126
+	 */
1127
+	private function handleLogSearchParamsType(): string
1128
+	{
1129
+		global $context;
1130
+
1131
+		if (isset($_REQUEST['search_type']) || empty($this->search_params['type']) || !isset($this->search_types[$this->search_params['type']]))
1132
+			return isset($_REQUEST['search_type']) && isset($this->search_types[$_REQUEST['search_type']]) ? $_REQUEST['search_type'] : (isset($this->search_types[$context['order']]) ? $context['order'] : 'member');
1133
+		else
1134
+			return $this->search_params['type'];
1135
+	}
1136
+}
0 1137
\ No newline at end of file
... ...
@@ -43,12 +43,17 @@
43 43
 		<require-file name="StopForumSpam.php" destination="$sourcedir" />
44 44
 
45 45
 		<!-- All the hooks -->
46
+			<!-- Main Section -->
46 47
 			<hook hook="integrate_pre_include" function="$sourcedir/StopForumSpam.php" />
47 48
 			<hook hook="integrate_pre_load" function="SFS::hook_pre_load" />
48
-		<hook hook="integrate_admin_areas" function="SFS::hook_admin_areas" />
49
-		<hook hook="integrate_modify_modifications" function="SFS::hook_modify_modifications" />
50 49
 			<hook hook="integrate_register" function="SFS::hook_register" />
51
-		<hook hook="integrate_manage_logs" function="SFS::hook_manage_logs" />
50
+			<hook hook="integrate_create_control_verification_test" function="SFS::hook_create_control_verification_test" />
51
+
52
+			<!-- Admin Section -->
53
+			<hook hook="integrate_admin_include" function="$sourcedir/StopForumSpamAdmin.php" />
54
+			<hook hook="integrate_admin_areas" function="SFSA::hook_admin_areas" />
55
+			<hook hook="integrate_modify_modifications" function="SFSA::hook_modify_modifications" />
56
+			<hook hook="integrate_manage_logs" function="SFSA::hook_manage_logs" />
52 57
 
53 58
 		<redirect url="?action=admin;area=securitysettings;sa=sfs" />
54 59
 	</install>
... ...
@@ -59,12 +64,17 @@
59 64
 		<code type="file">uninstall-sd-required.php</code>
60 65
 
61 66
 		<!-- All the hooks, removed -->
67
+			<!-- Main Section -->
62 68
 			<hook hook="integrate_pre_include" function="$sourcedir/StopForumSpam.php" reverse="true" />
63 69
 			<hook hook="integrate_pre_load" function="SFS::hook_pre_load" reverse="true" />
64
-		<hook hook="integrate_admin_areas" function="SFS::hook_admin_areas" reverse="true" />
65
-		<hook hook="integrate_modify_modifications" function="SFS::hook_modify_modifications" reverse="true" />
66 70
 			<hook hook="integrate_register" function="SFS::hook_register" reverse="true" />
67
-		<hook hook="integrate_manage_logs" function="SFS::hook_manage_logs" reverse="true" />
71
+			<hook hook="integrate_create_control_verification_test" function="SFSA::hook_create_control_verification_test" reverse="true" />
72
+
73
+			<!-- Admin Section -->
74
+			<hook hook="integrate_admin_include" function="$sourcedir/StopForumSpamAdmin.php" reverse="true" />
75
+			<hook hook="integrate_admin_areas" function="SFSA::hook_admin_areas" reverse="true" />
76
+			<hook hook="integrate_modify_modifications" function="SFSA::hook_modify_modifications" reverse="true" />
77
+			<hook hook="integrate_manage_logs" function="SFSA::hook_manage_logs" reverse="true" />
68 78
 
69 79
 		<!-- language files, removed -->
70 80
 		<remove-file name="$themes_dir/default/languages/StopForumSpam.english.php" />
... ...
@@ -19,12 +19,16 @@ if (SMF == 'SSI')
19 19
 	db_extend('packages');
20 20
 
21 21
 $hooks = array(
22
+	// Main sections.
22 23
 	'integrate_pre_include' => '$sourcedir/StopForumSpam.php',
23 24
 	'integrate_pre_load' => 'SFS::hook_pre_load',
24
-	'integrate_admin_areas' => 'SFS::hook_admin_areas',
25
-	'integrate_modify_modifications' => 'SFS::hook_modify_modifications',
26 25
 	'integrate_register' => 'SFS::hook_register',
27
-	'integrate_manage_logs' => 'SFS::hook_manage_logs'
26
+
27
+	// Admin Sections.
28
+	'integrate_admin_include' => '$sourcedir/StopForumSpamAdmin.php',
29
+	'integrate_admin_areas' => 'SFSA::hook_admin_areas',
30
+	'integrate_modify_modifications' => 'SFSA::hook_modify_modifications',
31
+	'integrate_manage_logs' => 'SFSA::hook_manage_logs'
28 32
 );
29 33
 
30 34
 foreach ($hooks as $hook => $func)
... ...
@@ -19,12 +19,16 @@ if (SMF == 'SSI')
19 19
 	db_extend('packages');
20 20
 
21 21
 $hooks = array(
22
+	// Main sections.
22 23
 	'integrate_pre_include' => '$sourcedir/StopForumSpam.php',
23 24
 	'integrate_pre_load' => 'SFS::hook_pre_load',
24
-	'integrate_admin_areas' => 'SFS::hook_admin_areas',
25
-	'integrate_modify_modifications' => 'SFS::hook_modify_modifications',
26 25
 	'integrate_register' => 'SFS::hook_register',
27
-	'integrate_manage_logs' => 'SFS::hook_manage_logs'
26
+
27
+	// Admin Sections.
28
+	'integrate_admin_include' => '$sourcedir/StopForumSpamAdmin.php',
29
+	'integrate_admin_areas' => 'SFSA::hook_admin_areas',
30
+	'integrate_modify_modifications' => 'SFSA::hook_modify_modifications',
31
+	'integrate_manage_logs' => 'SFSA::hook_manage_logs'
28 32
 );
29 33
 
30 34
 foreach ($hooks as $hook => $func)
31 35