Refactor code for more manageable classes
jdarwood007

jdarwood007 commited on 2023-04-13 17:22:42
Showing 11 changed files, with 2750 additions and 3257 deletions.


Fixes #12
Fixes #13
Fixes #14
Fixes #15
... ...
@@ -1,1322 +0,0 @@
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
-	private $adminTestURL = null;
28
-
29
-	/**
30
-	 * @var mixed Search area handling.
31
-	 */
32
-	private $search_types = array();
33
-	private $search_params = array();
34
-	private $search_params_column = '';
35
-	private $search_params_string = null;
36
-	private $search_params_type = null;
37
-	private $canDeleteLogs = false;
38
-	private $logSearch = array();
39
-
40
-	/**
41
-	 * @var int How long we disable removing logs.
42
-	 */
43
-	private $hoursDisabled = 24;
44
-
45
-	/**
46
-	 * Creates a self reference to the ASL class for use later.
47
-	 *
48
-	 * @version 1.0
49
-	 * @since 1.0
50
-	 * @return object The SFS Admin class is returned.
51
-	 */
52
-	public static function selfClass()
53
-	{
54
-		global $smcFunc;
55
-
56
-		if (is_null(self::$SFSAclass))
57
-		{
58
-			if (!empty($smcFunc['SFSA']))
59
-				self::$SFSAclass = $smcFunc['SFSA'];
60
-			else
61
-			{
62
-				self::$SFSAclass = new SFSA();
63
-				$smcFunc['SFSA'] = self::$SFSAclass;
64
-			}
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
-
86
-	/**
87
-	 * Creates the hook to the class for the admin areas.
88
-	 *
89
-	 * @param array $admin_areas A associate array from the software with all valid admin areas.
90
-	 *
91
-	 * @api
92
-	 * @CalledIn SMF 2.0, SMF 2.1
93
-	 * @see SFSA::setupAdminAreas()
94
-	 * @version 1.0
95
-	 * @since 1.0
96
-	 * @uses integrate__admin_areas - Hook SMF2.0
97
-	 * @uses integrate__admin_areas - Hook SMF2.1
98
-	 * @return void No return is generated
99
-	 */
100
-	public static function hook_admin_areas(array &$admin_areas)
101
-	{
102
-		return self::selfClass()->setupAdminAreas($admin_areas);
103
-	}
104
-
105
-	/**
106
-	 * Startup the Admin Panels Additions.
107
-	 * Where things appear are based on what software/version you have.
108
-	 *
109
-	 * @param array $admin_areas A associate array from the software with all valid admin areas.
110
-	 *
111
-	 * @internal
112
-	 * @CalledIn SMF 2.0, SMF 2.1
113
-	 * @version 1.4.0
114
-	 * @since 1.0
115
-	 * @uses integrate__admin_areas - Hook SMF2.0
116
-	 * @uses integrate__admin_areas - Hook SMF2.1
117
-	 * @return void No return is generated
118
-	 */
119
-	private function setupAdminAreas(array &$admin_areas): void
120
-	{
121
-		global $scripturl;
122
-
123
-		// Add the menu item.
124
-		if ($this->SFSclass->versionCheck('2.0', 'smf'))
125
-		{
126
-			$this->adminPageURL = $scripturl . '?action=admin;area=modsettings;sa=sfs';
127
-			$this->adminLogURL = $scripturl . '?action=admin;area=modsettings;sa=sfslog';
128
-			$this->adminTestURL = $scripturl . '?action=admin;area=modsettings;sa=sfstest';
129
-
130
-			$admin_areas['config']['areas']['modsettings']['subsections']['sfs'] = array(
131
-				$this->SFSclass->txt('sfs_admin_area')
132
-			);
133
-			$admin_areas['config']['areas']['modsettings']['subsections']['sfslog'] = array(
134
-				$this->SFSclass->txt('sfs_admin_logs')
135
-			);
136
-			$admin_areas['config']['areas']['modsettings']['subsections']['sfstest'] = array(
137
-				$this->SFSclass->txt('sfs_admin_test')
138
-			);
139
-		}
140
-		else
141
-		{
142
-			$this->adminPageURL = $scripturl . '?action=admin;area=modsettings;sa=sfs';
143
-			$this->adminLogURL = $scripturl . '?action=admin;area=logs;sa=sfslog';
144
-			$this->adminTestURL = $scripturl . '?action=admin;area=regcenter;sa=sfstest';
145
-
146
-			$admin_areas['config']['areas']['modsettings']['subsections']['sfs'] = array(
147
-				$this->SFSclass->txt('sfs_admin_area')
148
-			);
149
-			$admin_areas['members']['areas']['regcenter']['subsections']['sfstest'] = array(
150
-				$this->SFSclass->txt('sfs_admin_test')
151
-			);
152
-		}
153
-	}
154
-
155
-	/**
156
-	 * Setup the Modification's setup page.
157
-	 * For some versions, we put the logs into the modifications sections, its easier.
158
-	 *
159
-	 * @param array $subActions A associate array from the software with all valid modification sections.
160
-	 *
161
-	 * @api
162
-	 * @CalledIn SMF 2.0, SMF 2.1
163
-	 * @see SFSA::setupModifyModifications()
164
-	 * @version 1.0
165
-	 * @since 1.0
166
-	 * @uses integrate_modify_modifications - Hook SMF2.0
167
-	 * @uses integrate_modify_modifications - Hook SMF2.1
168
-	 * @return void No return is generated
169
-	 */
170
-	public static function hook_modify_modifications(array &$subActions)
171
-	{
172
-		return self::selfClass()->setupModifyModifications($subActions);
173
-	}
174
-
175
-	/**
176
-	 * Setup the Modifications section links.
177
-	 * For some versions we add the logs here as well.
178
-	 *
179
-	 * @param array $subActions A associate array from the software with all valid modification sections.
180
-	 *
181
-	 * @internal
182
-	 * @CalledIn SMF 2.0, SMF 2.1
183
-	 * @version 1.4.0
184
-	 * @since 1.0
185
-	 * @uses integrate_modify_modifications - Hook SMF2.0
186
-	 * @uses integrate_modify_modifications - Hook SMF2.1
187
-	 * @return void No return is generated
188
-	 */
189
-	private function setupModifyModifications(array &$subActions): void
190
-	{
191
-		$subActions['sfs'] = 'SFSA::startupAdminConfiguration';
192
-
193
-		// Only in SMF 2.0 do we drop logs here.
194
-		if ($this->SFSclass->versionCheck('2.0', 'smf'))
195
-		{
196
-			$subActions['sfslog'] = 'SFSA::startupLogs';
197
-			$subActions['sfstest'] = 'SFSA::startupTest';
198
-		}
199
-	}
200
-
201
-	/**
202
-	 * The configuration caller.
203
-	 *
204
-	 * @param bool $return_config If true, returns the configuration options for searches.
205
-	 *
206
-	 * @api
207
-	 * @CalledIn SMF 2.0, SMF 2.1
208
-	 * @see SFSA::setupSFSConfiguration
209
-	 * @version 1.0
210
-	 * @since 1.0
211
-	 * @uses integrate_modify_modifications - Hook SMF2.0
212
-	 * @uses integrate_modify_modifications - Hook SMF2.1
213
-	 * @return void No return is generated
214
-	 */
215
-	public static function startupAdminConfiguration(bool $return_config = false)
216
-	{
217
-		return self::selfClass()->setupSFSConfiguration($return_config);
218
-	}
219
-
220
-	/**
221
-	 * The actual settings page.
222
-	 *
223
-	 * @param bool $return_config If true, returns the configuration options for searches.
224
-	 *
225
-	 * @internal
226
-	 * @CalledIn SMF 2.0, SMF 2.1
227
-	 * @version 1.4.0
228
-	 * @since 1.0
229
-	 * @uses integrate_modify_modifications - Hook SMF2.0
230
-	 * @uses integrate_modify_modifications - Hook SMF2.1
231
-	 * @return void No return is generated
232
-	 */
233
-	private function setupSFSConfiguration(bool $return_config = false): array
234
-	{
235
-		global $scripturl, $context, $settings, $sc, $modSettings;
236
-
237
-		$config_vars = array(
238
-				array('title', 'sfsgentitle', 'label' => $this->SFSclass->txt('sfs_general_title')),
239
-
240
-				array('check', 'sfs_enabled'),
241
-				array('int', 'sfs_expire'),
242
-			'',
243
-				array('select', 'sfs_required', array(
244
-					'any' => $this->SFSclass->txt('sfs_required_any'),
245
-					'email|ip' => $this->SFSclass->txt('sfs_required_email_ip'),
246
-					'email|username' => $this->SFSclass->txt('sfs_required_email_username'),
247
-					'username|ip' => $this->SFSclass->txt('sfs_required_username_ip'),
248
-				)),
249
-			'',
250
-				array('check', 'sfs_emailcheck'),
251
-				array('check', 'sfs_usernamecheck'),
252
-				array('float', 'sfs_username_confidence', 'step' => '0.01'),
253
-				array('check', 'sfs_ipcheck'),
254
-				array('check', 'sfs_ipcheck_autoban'),
255
-			'',
256
-				array('select', 'sfs_region', $this->SFSclass->sfsServerMapping('config')),
257
-			'',
258
-				array('check', 'sfs_wildcard_email'),
259
-				array('check', 'sfs_wildcard_username'),
260
-				array('check', 'sfs_wildcard_ip'),
261
-			'',
262
-				array('select', 'sfs_tor_check', array(
263
-					0 => $this->SFSclass->txt('sfs_tor_check_block'),
264
-					1 => $this->SFSclass->txt('sfs_tor_check_ignore'),
265
-					2 => $this->SFSclass->txt('sfs_tor_check_bad'),
266
-				)),
267
-			'',
268
-				array('check', 'sfs_enablesubmission'),
269
-				array('text', 'sfs_apikey'),
270
-			'',
271
-				array('title', 'sfsverftitle', 'label' => $this->SFSclass->txt('sfs_verification_title')),
272
-				array('desc', 'sfsverfdesc', 'label' => $this->SFSclass->txt('sfs_verification_desc')),
273
-				array('select', 'sfs_verification_options', array(
274
-					'post' => $this->SFSclass->txt('sfs_verification_options_post'),
275
-					'report' => $this->SFSclass->txt('sfs_verification_options_report'),
276
-					'search' => $this->SFSclass->txt('sfs_verification_options_search'),
277
-				), 'multiple' => true),			
278
-				array('text', 'sfs_verification_options_extra', 'subtext' => $this->SFSclass->txt('sfs_verification_options_extra_subtext')),
279
-
280
-			'',
281
-				array('select', 'sfs_verOptionsMembers', array(
282
-					'post' => $this->SFSclass->txt('sfs_verification_options_post'),
283
-					'search' => $this->SFSclass->txt('sfs_verification_options_search'),
284
-				), 'multiple' => true),
285
-				array('text', 'sfs_verOptionsMemExtra', 'subtext' => $this->SFSclass->txt('sfs_verification_options_extra_subtext')),
286
-				array('int', 'sfs_verfOptMemPostThreshold'),
287
-			'',
288
-				array('check', 'sfs_log_debug'),
289
-		);
290
-
291
-		if ($return_config)
292
-			return $config_vars;
293
-
294
-		// Saving?
295
-		if (isset($_GET['save']))
296
-		{
297
-			// Turn the defaults off.
298
-			$this->SFSclass->unloadDefaults();
299
-			checkSession();
300
-
301
-			// If we are automatically banning IPs, make sure we have a ban group.
302
-			if (isset($_POST['sfs_ipcheck_autoban']) && empty($modSettings['sfs_ipcheck_autoban_group']))
303
-				$this->SFSclass->createBanGroup(true);
304
-
305
-			saveDBSettings($config_vars);
306
-
307
-			writeLog();
308
-			redirectexit($this->adminPageURL);
309
-		}
310
-
311
-		$context['post_url'] = $this->adminPageURL . ';save';
312
-
313
-		prepareDBSettingContext($config_vars);
314
-
315
-		return array();
316
-	}
317
-
318
-	/**
319
-	 * In some software/versions, we can hook into the logs section.
320
-	 * In others we hook into the modifications settings.
321
-	 *
322
-	 * @param array $log_functions All possible log functions.
323
-	 *
324
-	 * @api
325
-	 * @CalledIn SMF 2.1
326
-	 * @See SFSA::startupLogs
327
-	 * @version 1.0
328
-	 * @since 1.0
329
-	 * @uses integrate_manage_logs - Hook SMF2.1
330
-	 * @return void No return is generated
331
-	 */
332
-	public static function hook_manage_logs(array &$log_functions): bool
333
-	{
334
-		// Add our logs sub action.
335
-        $log_functions['sfslog'] = array('SFS-Subs-Logs.php', 'SFSL::startupLogs');
336
-
337
-		return self::selfClass()->AddToLogMenu($log_functions);
338
-	}
339
-
340
-	/**
341
-	 * Add the SFS logs to the log menu.
342
-	 *
343
-	 * @param array $log_functions All possible log functions.
344
-	 *
345
-	 * @CalledIn SMF 2.1
346
-	 * @See SFSA::startupLogs
347
-	 * @version 1.1
348
-	 * @since 1.1
349
-	 * @return void No return is generated
350
-	 */
351
-	public function AddToLogMenu(array &$log_functions): bool
352
-	{
353
-		global $context;
354
-
355
-		$context[$context['admin_menu_name']]['tab_data']['tabs']['sfslog'] = array(
356
-			'description' => $this->SFSclass->txt('sfs_admin_logs'),
357
-		);
358
-
359
-		return true;
360
-	}
361
-
362
-	/**
363
-	 * Log startup caller.
364
-	 * This has a $return_config just for simply complying with properly for searching the admin panel.
365
-	 *
366
-	 * @param bool $return_config If true, returns empty array to prevent breaking old SMF installs.
367
-	 *
368
-	 * @api
369
-	 * @CalledIn SMF 2.1
370
-	 * @See SFSA::loadLogs
371
-	 * @version 1.0
372
-	 * @since 1.0
373
-	 * @uses hook_manage_logs - Hook SMF2.1
374
-	 * @uses setupModifyModifications - Injected SMF2.0
375
-	 * @return void No return is generated
376
-	 */
377
-	public static function startupLogs(bool $return_config = false): array
378
-	{
379
-		return self::selfClass()->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 SFSA::getSFSLogEntries
391
-	 * @See SFSA::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->SFSclass->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->SFSclass->txt('sfs_admin_logs'),
438
-			'width' => '100%',
439
-			'items_per_page' => '50',
440
-			'no_items_label' => $this->SFSclass->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->SFSclass->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->SFSclass->txt('modlog_go') . '" class="button_submit" />
557
-				' . ($this->canDeleteLogs ? ' |
558
-					<input type="submit" name="remove" value="' . $this->SFSclass->txt('modlog_remove') . '" class="button_submit" />
559
-					<input type="submit" name="removeall" value="' . $this->SFSclass->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->SFSclass->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->SFSclass->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->SFSclass->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->SFSclass->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->SFSclass->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->SFSclass->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->SFSclass->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->SFSclass->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->SFSclass->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 SFSA::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->SFSclass->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' => $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->SFSclass->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->SFSclass->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->SFSclass->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->SFSclass->txt('sfs_log_auto_banned');			
926
-				if ($resultType == 'username' && !empty($extra))
927
-					$entries[$row['id_sfs']]['result'] .= ' ' . sprintf($this->SFSclass->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 SFSA::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
-	 * Get params
977
-	 *
978
-	 * @internal
979
-	 * @CalledIn SMF 2.0, SMF 2.1
980
-	 * @version 1.1
981
-	 * @since 1.0
982
-	 * @return string The column we are searching.
983
-	 */
984
-	public function get(string $var)
985
-	{
986
-		if (isset($this->{$var}))
987
-			return $this->{$var};
988
-	}
989
-
990
-	/**
991
-	 * Remove all logs, except those less than 24 hours old.
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 removeAllLogs(): void
1001
-	{
1002
-		global $smcFunc;
1003
-
1004
-		checkSession();
1005
-
1006
-		$smcFunc['db_query']('', '
1007
-			DELETE FROM {db_prefix}log_sfs
1008
-			WHERE log_time < {int:twenty_four_hours_wait}',
1009
-			array(
1010
-				'twenty_four_hours_wait' => time() - $this->hoursDisabled * 3600,
1011
-			)
1012
-		);
1013
-	}
1014
-
1015
-	/**
1016
-	 * Remove specific logs, except those less than 24 hours old.
1017
-	 *
1018
-	 * @param array $entries A array of the ids that we want to remove.
1019
-	 *
1020
-	 * @api
1021
-	 * @CalledIn SMF 2.0, SMF 2.1
1022
-	 * @See SFSA::loadLogs
1023
-	 * @version 1.0
1024
-	 * @since 1.0
1025
-	 * @return void No return is generated
1026
-	 */
1027
-	private function removeLogs(array $entries): void
1028
-	{
1029
-		global $smcFunc;
1030
-
1031
-		checkSession();
1032
-
1033
-		$smcFunc['db_query']('', '
1034
-			DELETE FROM {db_prefix}log_sfs
1035
-			WHERE id_sfs IN ({array_string:delete_actions})
1036
-				AND log_time < {int:twenty_four_hours_wait}',
1037
-			array(
1038
-				'twenty_four_hours_wait' => time() - $this->hoursDisabled * 3600,
1039
-				'delete_actions' => $entries,
1040
-			)
1041
-		);
1042
-	}
1043
-
1044
-	/**
1045
-	 * Handle searching for logs.
1046
-	 *
1047
-	 * @param string $url The base_href
1048
-	 * @internal
1049
-	 * @CalledIn SMF 2.0, SMF 2.1
1050
-	 * @version 1.0
1051
-	 * @since 1.0
1052
-	 * @return void No return is generated here.
1053
-	 */
1054
-	private function handleLogSearch(string &$url): void
1055
-	{
1056
-		global $context, $txt;
1057
-
1058
-		// If we have some data from a search, lets bring it back out.
1059
-		$this->search_params = $this->handleLogSearchParams();
1060
-
1061
-		// What we can search.
1062
-		$this->search_types = $this->handleLogSearchTypes();
1063
-		$this->search_params_string = $this->handleLogSearchParamsString();
1064
-		$this->search_params_type = $this->handleLogSearchParamsType();
1065
-
1066
-		$this->search_params_column = $this->search_types[$this->search_params_type]['sql'];
1067
-
1068
-		// Setup the search context.
1069
-		$this->search_params = empty($this->search_params_string) ? '' : base64_encode(json_encode(array(
1070
-			'string' => $this->search_params_string,
1071
-			'type' => $this->search_params_type,
1072
-		)));
1073
-		$this->logSearch = array(
1074
-			'string' => $this->search_params_string,
1075
-			'type' => $this->search_params_type,
1076
-			'label' => $this->search_types[$this->search_params_type]['label'],
1077
-		);
1078
-
1079
-		if (!empty($this->search_params))
1080
-			$url .= ';params=' . $this->search_params;
1081
-	}
1082
-
1083
-	/**
1084
-	 * Handle Search Params
1085
-	 *
1086
-	 * @internal
1087
-	 * @CalledIn SMF 2.0, SMF 2.1
1088
-	 * @version 1.1
1089
-	 * @since 1.0
1090
-	 * @return bool True upon success, false otherwise.
1091
-	 */
1092
-	private function handleLogSearchParams(): array
1093
-	{
1094
-		// If we have something to search for saved, get it back out.
1095
-		if (!empty($_REQUEST['params']) && empty($_REQUEST['is_search']))
1096
-		{
1097
-			$search_params = base64_decode(strtr($_REQUEST['params'], array(' ' => '+')));
1098
-			$search_params = $this->SFSclass->decodeJSON($search_params);
1099
-
1100
-			if (!empty($search_params))
1101
-				return $search_params;
1102
-		}
1103
-	
1104
-		return array();
1105
-	}
1106
-
1107
-	/**
1108
-	 * Handle Search Types
1109
-	 *
1110
-	 * @internal
1111
-	 * @CalledIn SMF 2.0, SMF 2.1
1112
-	 * @version 1.1
1113
-	 * @since 1.0
1114
-	 * @return array The valid Search Types.
1115
-	 */
1116
-	private function handleLogSearchTypes(): array
1117
-	{
1118
-		return array(
1119
-			'url' => array('sql' => 'l.url', 'label' => $this->SFSclass->txt('sfs_log_search_url')),
1120
-			'member' => array('sql' => 'mem.real_name', 'label' => $this->SFSclass->txt('sfs_log_search_member')),
1121
-			'username' => array('sql' => 'l.username', 'label' => $this->SFSclass->txt('sfs_log_search_username')),
1122
-			'email' => array('sql' => 'l.email', 'label' => $this->SFSclass->txt('sfs_log_search_email')),
1123
-			'ip' => array('sql' => 'lm.ip', 'label' => $this->SFSclass->txt('sfs_log_search_ip')),
1124
-			'ip2' => array('sql' => 'lm.ip2', 'label' => $this->SFSclass->txt('sfs_log_search_ip2'))
1125
-		);
1126
-	}
1127
- 
1128
-	/**
1129
-	 * Handle Search Params String
1130
-	 *
1131
-	 * @internal
1132
-	 * @CalledIn SMF 2.0, SMF 2.1
1133
-	 * @version 1.1
1134
-	 * @since 1.0
1135
-	 * @return string What we are searching for, validated and cleaned.
1136
-	 */
1137
-	private function handleLogSearchParamsString(): string
1138
-	{
1139
-		if (!isset($this->search_params['string']) || (!empty($_REQUEST['search']) && $this->search_params['string'] != $_REQUEST['search']))
1140
-			return empty($_REQUEST['search']) ? '' : $_REQUEST['search'];
1141
-		else
1142
-			return $this->search_params['string'];
1143
-	}
1144
-
1145
-	/**
1146
-	 * Handle Search Params Type
1147
-	 *
1148
-	 * @internal
1149
-	 * @CalledIn SMF 2.0, SMF 2.1
1150
-	 * @version 1.1
1151
-	 * @since 1.0
1152
-	 * @return string The column we are searching.
1153
-	 */
1154
-	private function handleLogSearchParamsType(): string
1155
-	{
1156
-		global $context;
1157
-
1158
-		if (isset($_REQUEST['search_type']) || empty($this->search_params['type']) || !isset($this->search_types[$this->search_params['type']]))
1159
-			return isset($_REQUEST['search_type']) && isset($this->search_types[$_REQUEST['search_type']]) ? $_REQUEST['search_type'] : (isset($this->search_types[$context['order']]) ? $context['order'] : 'member');
1160
-		else
1161
-			return $this->search_params['type'];
1162
-	}
1163
-
1164
-	/**
1165
-	 * In some software/versions, we can hook into the members registration center section.
1166
-	 * In others we hook into the modifications settings.
1167
-	 *
1168
-	 * @param array $subActions All possible sub actions.
1169
-	 *
1170
-	 * @api
1171
-	 * @CalledIn SMF 2.1
1172
-	 * @See SFSA::startupTest
1173
-	 * @version 1.4.0
1174
-	 * @since 1.4.0
1175
-	 * @uses integrate_manage_registrations - Hook SMF2.1
1176
-	 * @return void No return is generated
1177
-	 */
1178
-	public static function hook_manage_registrations(array &$subActions): bool
1179
-	{
1180
-		global $context;
1181
-
1182
-		// Add our logs sub action.
1183
-		$subActions['sfstest'] = array('SFSA::startupTest', 'admin_forum');
1184
-
1185
-		if (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'sfstest' && allowedTo('admin_forum'))
1186
-			$context['sub_action'] = 'sfstest';
1187
-
1188
-		return self::selfClass()->AddToRegCenterMenu($subActions);
1189
-	}
1190
-
1191
-	/**
1192
-	 * Add the SFS Test to the regcenter menu.
1193
-	 *
1194
-	 * @param array $log_functions All possible log functions.
1195
-	 *
1196
-	 * @CalledIn SMF 2.1
1197
-	 * @See SFSA::startupTest
1198
-	 * @version 1.4.0
1199
-	 * @since 1.4.0
1200
-	 * @return void No return is generated
1201
-	 */
1202
-	public function AddToRegCenterMenu(array &$subActions): bool
1203
-	{
1204
-		global $context;
1205
-
1206
-		$context[$context['admin_menu_name']]['tab_data']['tabs']['sfstest'] = array(
1207
-			'description' => $this->SFSclass->txt('sfs_admin_test_desc'),
1208
-		);
1209
-
1210
-		return true;
1211
-	}
1212
-
1213
-	/**
1214
-	 * Test API startup caller.
1215
-	 * This has a $return_config just for simply complying with properly for searching the admin panel.
1216
-	 *
1217
-	 * @param bool $return_config If true, returns empty array to prevent breaking old SMF installs.
1218
-	 *
1219
-	 * @api
1220
-	 * @CalledIn SMF 2.1
1221
-	 * @See SFSA::loadTestAPI
1222
-	 * @version 1.4.0
1223
-	 * @since 1.4.0
1224
-	 * @uses hook_manage_registrations - Hook SMF2.1
1225
-	 * @uses setupModifyModifications - Injected SMF2.0
1226
-	 * @return void No return is generated
1227
-	 */
1228
-	public static function startupTest(bool $return_config = false): array
1229
-	{
1230
-		return self::selfClass()->loadTestAPI();
1231
-	}
1232
-
1233
-	/**
1234
-	 * Actually do the test API.
1235
-	 * This has a $return_config just for simply complying with properly for searching the admin panel.
1236
-	 *
1237
-	 * @param bool $return_config If true, returns empty array to prevent breaking old SMF installs.
1238
-	 *
1239
-	 * @api
1240
-	 * @CalledIn SMF2.0, SMF 2.1
1241
-	 * @version 1.4.0
1242
-	 * @since 1.4.0
1243
-	 * @uses hook_manage_registrations - Hook SMF2.1
1244
-	 * @uses setupModifyModifications - Injected SMF2.0
1245
-	 * @return void No return is generated
1246
-	 */
1247
-	public function loadTestAPI(bool $return_config = false): array
1248
-	{
1249
-		global $context, $smcFunc, $user_info;
1250
-
1251
-		// No Configs.
1252
-		if ($return_config)
1253
-			return array();
1254
-
1255
-		$context['token_check'] = 'sfs_testapi';
1256
-		$this->SFSclass->loadLanguage();
1257
-
1258
-		// The reuslts output.
1259
-		$context['test_sent'] = isset($_POST['send']);
1260
-		$context['sfs_checks'] = array(
1261
-			'username' => array(
1262
-				0 => array(
1263
-					'enabled' => !empty($modSettings['sfs_usernamecheck']),
1264
-					'value' => !empty($_POST['username']) ? $smcFunc['htmlspecialchars']($_POST['username']) : $user_info['name'],
1265
-					'results' => null
1266
-				),
1267
-			),
1268
-			'email' => array(
1269
-				0 => array(
1270
-					'enabled' => !empty($modSettings['sfs_emailcheck']),
1271
-					'value' => !empty($_POST['email']) ? $smcFunc['htmlspecialchars']($_POST['email']) : $user_info['email'],
1272
-					'results' => null
1273
-				),
1274
-			),
1275
-			'ip' => array(
1276
-				0 => array(
1277
-					'enabled' => !empty($modSettings['sfs_ipcheck']),
1278
-					'value' => !empty($_POST['ip']) ? $smcFunc['htmlspecialchars']($_POST['ip']) : $user_info['ip'],
1279
-					'results' => null
1280
-				),
1281
-			),
1282
-		);
1283
-
1284
-		// Sending the data?
1285
-		if ($context['test_sent'])
1286
-		{
1287
-			checkSession();
1288
-			if (!$this->SFSclass->versionCheck('2.0', 'smf'))
1289
-				validateToken($context['token_check'], 'post');
1290
-
1291
-			$username = $smcFunc['htmlspecialchars']($_POST['username']);
1292
-			$email = $smcFunc['htmlspecialchars']($_POST['email']);
1293
-			$ip = $smcFunc['htmlspecialchars']($_POST['ip']);
1294
-				
1295
-			$response = $this->SFSclass->TestSFS(array(
1296
-					array('username' => $username),
1297
-					array('email' => $email),
1298
-					array('ip' => $ip),
1299
-			));
1300
-
1301
-			// No checks found? Can't do this.
1302
-			if (empty($response) || !is_array($response) || empty($response['success']))
1303
-				$context['test_api_error'] = $this->SFSclass->txt('sfs_request_failure_nodata');
1304
-			else
1305
-				// Parse all the responses out.
1306
-				foreach($context['sfs_checks'] as $key => &$res)
1307
-					$res[0] += $response[$key][0];
1308
-		}
1309
-
1310
-		// Load our template.
1311
-		loadTemplate('StopForumSpam');
1312
-		$context['sub_template'] = 'sfsa_testapi';
1313
-
1314
-		$context['sfs_test_url'] = $this->adminTestURL;
1315
-		if (!$this->SFSclass->versionCheck('2.0', 'smf'))
1316
-			createToken($context['token_check'], 'post');
1317
-		else
1318
-			unset($context['token_check']);
1319
-
1320
-		return array();
1321
-	}
1322
-}
1323 0
\ No newline at end of file
... ...
@@ -1,1581 +0,0 @@
1
-<?php
2
-
3
-/**
4
- * The Main 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.2
10
- */
11
-class SFS
12
-{
13
-	/**
14
-	 * @var array Our settings information used on saving/changing settings.
15
-	 */
16
-	private $changedSettings = array();
17
-	private $extraVerificationOptions = array();
18
-
19
-	/**
20
-	 * @var string Name of the software and its version.  This is so we can branch out from the same base.
21
-	 */
22
-	private $softwareName = 'smf';
23
-	private $softwareVersion = '2.1';
24
-
25
-	/**
26
-	 * @var array The block Types.
27
-	 */
28
-	private $blockTypeMap = array(
29
-		'username' => 1,
30
-		'email' => 2,
31
-		'ip' => 3
32
-	);
33
-
34
-	/**
35
-	 * Simple setup for the class to be used later correctly.
36
-	 * This simply loads the class into $smcFunc and we can grab this anywhere else later.
37
-	 *
38
-	 * @api
39
-	 * @CalledIn SMF 2.0, SMF 2.1
40
-	 * @version 1.0
41
-	 * @since 1.0
42
-	 * @uses integrate_pre_load - Hook SMF2.0
43
-	 * @uses integrate_pre_load - Hook SMF2.1
44
-	 * @return void No return is generated
45
-	 */
46
-	public static function hook_pre_load(): void
47
-	{
48
-		global $smcFunc, $sourcedir;
49
-
50
-		$smcFunc['classSFS'] = new SFS();
51
-	}
52
-
53
-	/**
54
-	 * Build the class, figure out what software/version we have.
55
-	 * Loads up the defaults.
56
-	 *
57
-	 * @CalledIn SMF 2.0, SMF 2.1
58
-	 * @version 1.0
59
-	 * @since 1.0
60
-	 * @return void No return is generated
61
-	 */
62
-	public function __construct()
63
-	{
64
-		global $smcFunc;
65
-
66
-		// Is this SMF 2.0?
67
-		if (!function_exists('loadCacheAccelerator'))
68
-			$this->softwareVersion = '2.0';
69
-
70
-		// Setup the defaults.
71
-		$this->loadDefaults();
72
-	}
73
-
74
-	/**
75
-	 * Handle registration events.
76
-	 *
77
-	 * @param array $regOptions An array from the software with all the registration optins we are going to use to register.
78
-	 * @param array $theme_vars An array from the software with all the possible theme settings we are going to use to register.
79
-	 *
80
-	 * @api
81
-	 * @CalledIn SMF 2.0, SMF 2.1
82
-	 * @CalledAt: action=signup, action=admin;area=regcenter;sa=register
83
-	 * @See SFS::checkRegisterRequest
84
-	 * @version 1.0
85
-	 * @since 1.0
86
-	 * @uses integrate_register - Hook SMF2.1
87
-	 * @uses integrate_register - Hook SMF2.0
88
-	 * @return bool True is success, no other bool is expeicifcly defined yet.
89
-	 */
90
-	public static function hook_register(array &$regOptions, array &$theme_vars): bool
91
-	{
92
-		global $smcFunc;
93
-		return $smcFunc['classSFS']->checkRegisterRequest($regOptions, $theme_vars);
94
-	}
95
-
96
-	/**
97
-	 * Something is attempting to register, we should check them out.
98
-	 *
99
-	 * @param array $regOptions An array from the software with all the registration optins we are going to use to register.
100
-	 * @param array $theme_vars An array from the software with all the possible theme settings we are going to use to register.
101
-	 *
102
-	 * @api
103
-	 * @CalledIn SMF 2.0, SMF 2.1
104
-	 * @CalledAt: action=signup, action=admin;area=regcenter;sa=register
105
-	 * @See SFS::checkRegisterRequest
106
-	 * @version 1.0
107
-	 * @since 1.0
108
-	 * @uses integrate_register - Hook SMF2.1
109
-	 * @uses integrate_register - Hook SMF2.0
110
-	 * @return bool True is success, no other bool is expeicifcly defined yet.
111
-	 */
112
-	private function checkRegisterRequest(array &$regOptions, array &$theme_vars): bool
113
-	{
114
-		// Admins are not spammers.. usually.
115
-		if ($regOptions['interface'] == 'admin')
116
-			return true;
117
-
118
-		// Pass everything and let us handle what options we pass on.  We pass the register_vars as these are what we have cleaned up.
119
-		return $this->sfsCheck(array(
120
-			array('username' => $regOptions['register_vars']['member_name']),
121
-			array('email' => $regOptions['register_vars']['email_address']),
122
-			array('ip' => $regOptions['register_vars']['member_ip']),
123
-			array('ip' => $regOptions['register_vars']['member_ip2']),
124
-		), 'register');
125
-	}
126
-
127
-	/**
128
-	 * The caller for a verification test.
129
-	 *
130
-	 * @param array $thisVerification An array from the software with all the verification information we have.
131
-	 * @param array $verification_errors An errors which exist from verification.
132
-	 *
133
-	 * @api
134
-	 * @CalledIn SMF 2.0, SMF 2.1
135
-	 * @See SFS::checkVerificationTest
136
-	 * @version 1.0
137
-	 * @since 1.0
138
-	 * @uses integrate_create_control_verification_test - Hook SMF2.1
139
-	 * @return bool True is success, no other bool is expeicifcly defined yet.
140
-	 */
141
-	public static function hook_create_control_verification_test(array $thisVerification, array &$verification_errors): bool
142
-	{
143
-		global $smcFunc;
144
-		return $smcFunc['classSFS']->checkVerificationTest($thisVerification, $verification_errors);
145
-	}
146
-
147
-	/**
148
-	 * The caller for a verification test.
149
-	 * SMF 2.0 calls this directly as we have no good hook.
150
-	 *
151
-	 * @param array $thisVerification An array from the software with all the verification information we have.
152
-	 * @param array $verification_errors An errors which exist from verification.
153
-	 *
154
-	 * @api
155
-	 * @CalledIn SMF 2.0, SMF 2.1
156
-	 * @version 1.2
157
-	 * @since 1.0
158
-	 * @uses create_control_verification - Hook SMF2.0
159
-	 * @uses integrate_create_control_verification_test - Hook SMF2.1
160
-	 * @return bool True is success, no other bool is expeicifcly defined yet.
161
-	 */
162
-	public function checkVerificationTest(array $thisVerification, array &$verification_errors): bool
163
-	{
164
-		global $user_info, $modSettings;
165
-
166
-		// Registration is skipped as we process that differently.
167
-		if ($thisVerification['id'] == 'register')
168
-			return true;
169
-
170
-		// Get our options data.
171
-		$options = $this->getVerificationOptions();
172
-
173
-		// Key => Extended checks.
174
-		$verificationMap = array(
175
-			'post' => true,
176
-			'report' => true,
177
-			'search' => $user_info['is_guest'] || empty($user_info['posts']) || $user_info['posts'] < $modSettings['sfs_verfOptMemPostThreshold'],
178
-		);
179
-
180
-		foreach ($verificationMap as $key => $extendedChecks)
181
-			if ($thisVerification['id'] == $key && in_array($key, $options))
182
-				return call_user_func(array($this, 'checkVerificationTest' . ucfirst($key)));
183
-
184
-		// Others areas.  We have to play a guessing game here.
185
-		return $this->checkVerificationTestExtra($thisVerification);
186
-	}
187
-
188
-	/**
189
-	 * The hook to setup profile menu.
190
-	 *
191
-	 * @param array $profile_areas All the profile areas.
192
-	 *
193
-	 * @api
194
-	 * @CalledIn SMF 2.1
195
-	 * @See SFS::setupProfileMenu
196
-	 * @version 1.1
197
-	 * @since 1.1
198
-	 * @uses integrate_pre_profile_areas - Hook SMF2.1
199
-	 * @return void the passed $profile_areas is modified.
200
-	 */
201
-	public static function hook_pre_profile_areas(array &$profile_areas): void
202
-	{
203
-		global $smcFunc;
204
-		$smcFunc['classSFS']->setupProfileMenu($profile_areas);
205
-	}
206
-
207
-	/**
208
-	 * The hook to setup profile menu.
209
-	 *
210
-	 * @param array $profile_areas All the profile areas.
211
-	 *
212
-	 * @api
213
-	 * @CalledIn SMF 2.1
214
-	 * @version 1.1
215
-	 * @since 1.1
216
-	 * @uses integrate_pre_profile_areas - Hook SMF2.1
217
-	 * @return void the passed $profile_areas is modified.
218
-	 */
219
-	public function setupProfileMenu(array &$profile_areas): void
220
-	{
221
-		$profile_areas['info']['areas']['sfs'] = [
222
-			'label' => $this->txt('sfs_profile'),
223
-			'file' => 'SFS.php',
224
-			'icon' => 'sfs.webp',
225
-			'function' => 'SFS::ProfileTrackSFS',
226
-			'permission' => [
227
-				'own' => ['moderate_forum'],
228
-				'any' => ['moderate_forum'],
229
-			],
230
-		];
231
-
232
-		// SMF 2.0 can't call objects or classes.
233
-		if ($this->versionCheck('2.0', 'smf'))
234
-		{
235
-			function ProfileTrackSFS20(int $memID)
236
-			{
237
-				return SFS::ProfileTrackSFS($memID);
238
-			}
239
-			$profile_areas['info']['areas']['sfs']['function'] = 'ProfileTrackSFS20';
240
-		}
241
-	}
242
-
243
-	/**
244
-	 * The caller for a profile check.
245
-	 *
246
-	 * @param int $memID The id of the member we are checking.
247
-	 *
248
-	 * @api
249
-	 * @CalledIn SMF 2.1
250
-	 * @version 1.1
251
-	 * @since 1.1
252
-	 * @return void the passed $profile_areas is modified.
253
-	 */
254
-	public static function ProfileTrackSFS(int $memID): void
255
-	{
256
-		global $smcFunc;
257
-		$smcFunc['classSFS']->TrackSFS($memID);
258
-	}
259
-
260
-	/**
261
-	 * The caller for a profile check.
262
-	 *
263
-	 * @param int $memID The id of the member we are checking.
264
-	 *
265
-	 * @api
266
-	 * @CalledIn SMF 2.1
267
-	 * @version 1.1
268
-	 * @since 1.1
269
-	 * @return void the passed $profile_areas is modified.
270
-	 */
271
-	public function TrackSFS(int $memID): void
272
-	{
273
-		global $user_profile, $context, $smcFunc, $scripturl, $modSettings, $sourcedir;
274
-
275
-		isAllowedTo('moderate_forum');
276
-
277
-		// We need this stuff.
278
-		$context['sfs_allow_submit'] = !empty($modSettings['sfs_enablesubmission']) && !empty($modSettings['sfs_apikey']);
279
-		$context['token_check'] = 'sfs_submit-' . $memID;
280
-		$cache_key = 'sfs_check_member-' . $memID;
281
-
282
-		// Do we have a message?
283
-		$poster_name = null;
284
-		$poster_email = null;
285
-		$poster_ip = null;
286
-		$post_body = null;
287
-		if (isset($_GET['msg']) && intval($_GET['msg']) > 0)
288
-		{
289
-			$request = $smcFunc['db_query']('', '
290
-				SELECT poster_name, poster_email, poster_ip, body
291
-				FROM {db_prefix}messages
292
-				WHERE id_msg = {int:id_msg}
293
-					AND (
294
-						id_member = {int:id_member}
295
-						OR id_member = 0
296
-					)
297
-					AND {query_see_message_board}
298
-				',
299
-				array(
300
-					'id_msg' => (int) $_GET['msg'],
301
-					'id_member' => $memID,
302
-					'actor_is_admin' => $context['user']['is_admin'] ? 1 : 0
303
-				));
304
-			if ($smcFunc['db_num_rows']($request) == 1)
305
-			{
306
-				list($poster_name, $poster_email, $poster_ip, $post_body) = $smcFunc['db_fetch_row']($request);
307
-				$poster_ip = inet_dtop($poster_ip);
308
-			}
309
-			$smcFunc['db_free_result']($request);
310
-			
311
-			$context['reason'] = $smcFunc['htmlspecialchars']($post_body);
312
-		}
313
-		else
314
-			$context['reason'] = '';
315
-
316
-		// Are we submitting this?
317
-		if ($context['sfs_allow_submit'] && (isset($_POST['sfs_submit']) || isset($_POST['sfs_submitban'])))
318
-		{
319
-			checkSession();
320
-			if (!$this->versionCheck('2.0', 'smf'))
321
-				validateToken($context['token_check'], 'post');
322
-
323
-			$data = [
324
-				'username' => !empty($poster_name) ? $poster_name : $user_profile[$memID]['real_name'],
325
-				'email' => !empty($poster_email) ? $poster_email : $user_profile[$memID]['email_address'],
326
-				'ip_addr' => !empty($poster_ip) ? $poster_ip : $user_profile[$memID]['member_ip'],
327
-				'api_key' => $modSettings['sfs_apikey']
328
-			];
329
-			$post_data = http_build_query($data, '', '&');
330
-
331
-			// SMF 2.0 has the fetch_web_data in the Subs-Packages, 2.1 it is in Subs.php.
332
-			if ($this->versionCheck('2.0', 'smf'))
333
-				require_once($sourcedir . '/Subs-Package.php');
334
-
335
-			// Now we have a URL, lets go get it.
336
-			$result = fetch_web_data('https://www.stopforumspam.com/add', $post_data);
337
-
338
-			if (strpos($result, 'data submitted successfully') === false)
339
-				$context['submission_failed'] = $this->txt('sfs_submission_error');
340
-			else if (isset($_POST['sfs_submitban']))
341
-				redirectexit($scripturl . '?action=admin;area=ban;sa=add;u=' . $memID);
342
-			else
343
-				$context['submission_success'] = $this->txt('sfs_submission_success');
344
-		}
345
-	
346
-		// Check if we have this info.
347
-		if (($cache = cache_get_data($cache_key)) === null || ($response = $this->decodeJSON($cache, true)) === null)
348
-		{
349
-			$checks = [
350
-				['username' => $user_profile[$memID]['real_name']],
351
-				['email' => $user_profile[$memID]['email_address']],
352
-				['ip' => $user_profile[$memID]['member_ip']],
353
-				['ip' => $user_profile[$memID]['member_ip2']],
354
-			];
355
-
356
-			$requestURL = $this->buildServerURL();
357
-			$this->buildCheckPath($requestURL, $checks, 'profile');
358
-			$response = (array) $this->sendSFSCheck($requestURL, $checks, 'profile');
359
-		
360
-			cache_put_data($cache_key, $this->encodeJSON($response), 600);
361
-		}
362
-
363
-		// Prepare for the template.
364
-		$context['sfs_overall'] = (bool) $response['success'];
365
-		$context['sfs_checks'] = $response;
366
-		unset($context['sfs_checks']['success']);
367
-
368
-		if ($context['sfs_allow_submit'])
369
-		{
370
-			$context['sfs_submit_url'] = $scripturl . '?action=profile;area=sfs;u=' . $memID;
371
-			if (!$this->versionCheck('2.0', 'smf'))
372
-				createToken($context['token_check'], 'post');
373
-			else
374
-				unset($context['token_check']);
375
-		}
376
-
377
-		loadTemplate('StopForumSpam');
378
-		$context['sub_template'] = 'profile_tracksfs';
379
-	}
380
-
381
-	/**
382
-	 * Test for a standard post.
383
-	 *
384
-	 * @internal
385
-	 * @CalledIn SMF 2.0, SMF 2.1
386
-	 * @version 1.1
387
-	 * @since 1.1
388
-	 * @return bool True is success, no other bool is expeicifcly defined yet.
389
-	 */
390
-	private function checkVerificationTestPost(): bool
391
-	{
392
-		global $user_info, $modSettings;
393
-
394
-		// Guests!
395
-		if ($user_info['is_guest'])
396
-		{
397
-			$guestname = !isset($_POST['guestname']) ? '' : trim($_POST['guestname']);
398
-			$email = !isset($_POST['email']) ? '' : trim($_POST['email']);
399
-
400
-			return $this->sfsCheck(array(
401
-				array('username' => $guestname),
402
-				array('email' => $email),
403
-				array('ip' => $user_info['ip']),
404
-				array('ip' => $user_info['ip2']),
405
-			), 'post');
406
-
407
-		}
408
-		// Members and they don't have enough posts?
409
-		elseif (empty($user_info['posts']) || $user_info['posts'] < $modSettings['sfs_verfOptMemPostThreshold'])
410
-			return $this->sfsCheck(array(
411
-				array('username' => $user_info['username']),
412
-				array('email' => $user_info['email']),
413
-				array('ip' => $user_info['ip']),
414
-				array('ip' => $user_info['ip2']),
415
-			), 'post');
416
-		else
417
-			return true;
418
-	}
419
-
420
-	/**
421
-	 * Test for a report.
422
-	 *
423
-	 * @internal
424
-	 * @CalledIn SMF 2.0, SMF 2.1
425
-	 * @version 1.1
426
-	 * @since 1.1
427
-	 * @return bool True is success, no other bool is expeicifcly defined yet.
428
-	 */
429
-	private function checkVerificationTestReport(): bool
430
-	{
431
-		global $user_info;
432
-
433
-		$email = !isset($_POST['email']) ? '' : trim($_POST['email']);
434
-
435
-		return $this->sfsCheck(array(
436
-			array('email' => $email),
437
-			array('ip' => $user_info['ip']),
438
-			array('ip' => $user_info['ip2']),
439
-		), 'post');
440
-	}
441
-
442
-	/**
443
-	 * Test for a Search.
444
-	 *
445
-	 * @internal
446
-	 * @CalledIn SMF 2.0, SMF 2.1
447
-	 * @version 1.1
448
-	 * @since 1.1
449
-	 * @return bool True is success, no other bool is expeicifcly defined yet.
450
-	 */
451
-	private function checkVerificationTestSearch(): bool
452
-	{
453
-		global $user_info;
454
-
455
-		return $this->sfsCheck(array(
456
-			array('ip' => $user_info['ip']),
457
-			array('ip' => $user_info['ip2']),
458
-		), 'search');
459
-	}
460
-
461
-	/**
462
-	 * Test for extras, customizations and other areas that we want to tie in.
463
-	 *
464
-	 * @internal
465
-	 * @CalledIn SMF 2.0, SMF 2.1
466
-	 * @version 1.1
467
-	 * @since 1.1
468
-	 * @return bool True is success, no other bool is expeicifcly defined yet.
469
-	 */
470
-	private function checkVerificationTestExtra(array $thisVerification): bool
471
-	{
472
-		global $user_info;
473
-
474
-		foreach ($this->extraVerificationOptions as $option)
475
-		{
476
-			// Not a match.
477
-			if ($thisVerification['id'] != $option)
478
-				continue;
479
-
480
-			// Always try to send off IPs.
481
-			$checks = array(
482
-				array('ip' => $user_info['ip']),
483
-				array('ip' => $user_info['ip2']),
484
-			);
485
-
486
-			// Can we find a username?
487
-			$possibleUserNames = array('username', 'user_name', 'user', 'name', 'realname');
488
-			foreach ($possibleUserNames as $searchKey)
489
-				if (!empty($_POST[$searchKey]))
490
-				{
491
-					$checks[] = array('username' => $_POST[$searchKey]);
492
-					break;
493
-				}
494
-
495
-			// Can we find a email?
496
-			$possibleUserNames = array('email', 'emailaddress', 'email_address');
497
-			foreach ($possibleUserNames as $searchKey)
498
-				if (!empty($_POST[$searchKey]))
499
-				{
500
-					$checks[] = array('email' => $_POST[$searchKey]);
501
-					break;
502
-				}
503
-
504
-			return $this->sfsCheck($checks, $option);
505
-		}
506
-
507
-		return true;
508
-	}
509
-
510
-	/**
511
-	 * Run checks against the SFS database.
512
-	 *
513
-	 * @param array $checks All the possible checks we would like to preform.
514
-	 * @param string $area The area this is coming from.
515
-	 *
516
-	 * @internal
517
-	 * @CalledIn SMF 2.0, SMF 2.1
518
-	 * @version 1.2
519
-	 * @since 1.0
520
-	 * @return bool True is success, no other bool is expeicifcly defined yet.
521
-	 */
522