PageRenderTime 27ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/forum/ext/rmcgirr83/stopforumspam/event/main_listener.php

https://github.com/AJenbo/ubuntudanmark.dk
PHP | 442 lines | 303 code | 58 blank | 81 comment | 55 complexity | bce0363ff5a8a0d51664a853970d3c30 MD5 | raw file
  1. <?php
  2. /**
  3. *
  4. * Stop forum Spam extension for the phpBB Forum Software package.
  5. *
  6. * @copyright (c) 2015 Rich McGirr (RMcGirr83)
  7. * @license GNU General Public License, version 2 (GPL-2.0)
  8. *
  9. */
  10. namespace rmcgirr83\stopforumspam\event;
  11. /**
  12. * Event listener
  13. */
  14. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  15. class main_listener implements EventSubscriberInterface
  16. {
  17. private $sfs_admins_mods = array();
  18. /** @var \phpbb\auth\auth */
  19. protected $auth;
  20. /** @var \phpbb\config\config */
  21. protected $config;
  22. /** @var \phpbb\user */
  23. protected $user;
  24. /** @var \phpbb\controller\helper */
  25. protected $helper;
  26. /** @var \phpbb\log\log */
  27. protected $log;
  28. /** @var \phpbb\request\request */
  29. protected $request;
  30. /** @var \phpbb\template\template */
  31. protected $template;
  32. /* @var \rmcgirr83\stopforumspam\core\sfsgroups */
  33. protected $sfsgroups;
  34. /* @var \rmcgirr83\stopforumspam\core\sfsapi */
  35. protected $sfsapi;
  36. /** @var string phpBB root path */
  37. protected $root_path;
  38. /** @var string phpEx */
  39. protected $php_ext;
  40. /* @var \rmcgirr83\contactadmin\controller\main_controller */
  41. protected $contactadmin;
  42. public function __construct(
  43. \phpbb\auth\auth $auth,
  44. \phpbb\config\config $config,
  45. \phpbb\user $user,
  46. \phpbb\controller\helper $helper,
  47. \phpbb\log\log $log,
  48. \phpbb\request\request $request,
  49. \phpbb\template\template $template,
  50. \rmcgirr83\stopforumspam\core\sfsgroups $sfsgroups,
  51. \rmcgirr83\stopforumspam\core\sfsapi $sfsapi,
  52. $root_path,
  53. $php_ext,
  54. \rmcgirr83\contactadmin\controller\main_controller $contactadmin = null)
  55. {
  56. $this->auth = $auth;
  57. $this->config = $config;
  58. $this->user = $user;
  59. $this->helper = $helper;
  60. $this->log = $log;
  61. $this->request = $request;
  62. $this->template = $template;
  63. $this->sfsgroups = $sfsgroups;
  64. $this->sfsapi = $sfsapi;
  65. $this->root_path = $root_path;
  66. $this->php_ext = $php_ext;
  67. $this->contactadmin = $contactadmin;
  68. }
  69. static public function getSubscribedEvents()
  70. {
  71. return array(
  72. 'core.user_setup' => 'user_setup',
  73. 'core.ucp_register_data_after' => 'user_sfs_validate_registration',
  74. 'core.posting_modify_template_vars' => 'poster_data_email',
  75. 'core.posting_modify_message_text' => 'poster_modify_message_text',
  76. 'core.posting_modify_submission_errors' => 'user_sfs_validate_posting',
  77. // report to sfs?
  78. 'core.viewtopic_before_f_read_check' => 'viewtopic_before_f_read_check',
  79. 'core.viewtopic_post_rowset_data' => 'viewtopic_post_rowset_data',
  80. 'core.viewtopic_modify_post_row' => 'viewtopic_modify_post_row',
  81. // Custom events for integration with Contact Admin Extension
  82. 'rmcgirr83.contactadmin.modify_data_and_error' => 'user_sfs_validate_registration',
  83. );
  84. }
  85. public function user_setup($event)
  86. {
  87. //Need to load lang vars for mcp logs
  88. if ($this->user->page['page_name'] == 'mcp' . $this->php_ext)
  89. {
  90. $this->user->add_lang_ext('rmcgirr83/stopforumspam', 'sfs_mcp');
  91. }
  92. }
  93. public function user_sfs_validate_registration($event)
  94. {
  95. if ($this->config['allow_sfs'] == false)
  96. {
  97. return;
  98. }
  99. $this->user->add_lang_ext('rmcgirr83/stopforumspam', 'stopforumspam');
  100. $error_array = $event['error'];
  101. /* On registration and only when all errors have cleared
  102. * do not want the admin message area to fill up
  103. * stopforumspam only works with IPv4 not IPv6
  104. */
  105. if (!sizeof($error_array))
  106. {
  107. $check = $this->stopforumspam_check($event['data']['username'], $this->user->ip, $event['data']['email']);
  108. if ($check)
  109. {
  110. if ($this->config['sfs_down'] && is_string($check))
  111. {
  112. return;
  113. }
  114. $error_array[] = $this->show_message($check);
  115. // now ban the spammer by IP
  116. if ($this->config['sfs_ban_ip'] && !is_string($check))
  117. {
  118. $this->ban_by_ip($this->user->ip);
  119. }
  120. }
  121. }
  122. $event['error'] = $error_array;
  123. }
  124. /*
  125. * inject email for anonymous postings
  126. * it is strictly used as a check against SFS
  127. */
  128. public function poster_data_email($event)
  129. {
  130. if ($this->user->data['user_id'] == ANONYMOUS && $this->config['allow_sfs'])
  131. {
  132. // Output the data vars to the template
  133. $this->template->assign_vars(array(
  134. 'SFS' => true,
  135. 'EMAIL' => $this->request->variable('email', ''),
  136. ));
  137. }
  138. }
  139. public function poster_modify_message_text($event)
  140. {
  141. $event['post_data'] = array_merge($event['post_data'], array(
  142. 'email' => strtolower($this->request->variable('email', '')),
  143. ));
  144. }
  145. public function user_sfs_validate_posting($event)
  146. {
  147. $error_array = $event['error'];
  148. if ($this->user->data['user_id'] == ANONYMOUS && $this->config['allow_sfs'])
  149. {
  150. $this->user->add_lang_ext('rmcgirr83/stopforumspam', 'stopforumspam');
  151. $this->user->add_lang('ucp');
  152. if (!function_exists('phpbb_validate_email'))
  153. {
  154. include($this->root_path . 'includes/functions_user.' . $this->php_ext);
  155. }
  156. // ensure email is populated on posting
  157. $error = $this->validate_email($event['post_data']['email']);
  158. if ($error)
  159. {
  160. $error_array[] = $this->user->lang[$error . '_EMAIL'];
  161. }
  162. // I just hate empty usernames for guest posting
  163. if (empty($event['post_data']['username']))
  164. {
  165. $username_error = $this->validate_username($event['post_data']['username']);
  166. if ($username_error)
  167. {
  168. $error_array[] = $username_error;
  169. }
  170. }
  171. if (!sizeof($error_array))
  172. {
  173. $check = $this->stopforumspam_check($event['post_data']['username'], $this->user->ip, $event['post_data']['email']);
  174. if ($check)
  175. {
  176. if ($this->config['sfs_down'] && is_string($check))
  177. {
  178. return;
  179. }
  180. $error_array[] = $this->show_message($check);
  181. // now ban the spammer by IP
  182. if ($this->config['sfs_ban_ip'] && !is_string($check))
  183. {
  184. $this->ban_by_ip($this->user->ip);
  185. }
  186. }
  187. }
  188. }
  189. $event['error'] = $error_array;
  190. }
  191. /*
  192. * viewtopic_before_f_read_check() inject lang vars and grab admins and mods
  193. * @param $event \phpbb\event
  194. * @return null
  195. */
  196. public function viewtopic_before_f_read_check($event)
  197. {
  198. if ($this->config['allow_sfs'])
  199. {
  200. $this->user->add_lang_ext('rmcgirr83/stopforumspam', 'stopforumspam');
  201. // get mods and admins
  202. $this->sfs_admins_mods = $this->sfsgroups->getadminsmods($event['forum_id']);
  203. }
  204. }
  205. /*
  206. * viewtopic_post_rowset_data add the posters ip into the rowset
  207. * @param $event \phpbb\event
  208. * @return string
  209. */
  210. public function viewtopic_post_rowset_data($event)
  211. {
  212. $rowset = $event['rowset_data'];
  213. $row = $event['row'];
  214. $rowset['poster_ip'] = $row['poster_ip'];
  215. $rowset['user_email'] = $row['user_email'];
  216. $rowset['sfs_reported'] = $row['sfs_reported'];
  217. $event['rowset_data'] = $rowset;
  218. }
  219. /*
  220. * viewtopic_modify_post_row show a link to admins and mods to report the spammer
  221. * @param $event \phpbb\event
  222. * @return string
  223. */
  224. public function viewtopic_modify_post_row($event)
  225. {
  226. if (empty($this->config['allow_sfs']) || empty($this->config['sfs_api_key']))
  227. {
  228. return;
  229. }
  230. $row = $event['row'];
  231. // ensure we have an IP and email address..this may happen if users have "post" bots on the forum
  232. $sfs_report_allowed = (!empty($row['poster_ip']) && !empty($row['user_email']) && $event['poster_id'] != ANONYMOUS) ? true : false;
  233. if ($sfs_report_allowed && in_array($this->user->data['user_id'], $this->sfs_admins_mods) && !in_array((int) $event['poster_id'], $this->sfs_admins_mods))
  234. {
  235. $reporttosfs_url = $this->helper->route('rmcgirr83_stopforumspam_core_reporttosfs', array('username' => urlencode($row['username']), 'userip' => $row['poster_ip'], 'useremail' => $row['user_email'], 'postid' => (int) $row['post_id'], 'posterid' => (int) $event['poster_id'], 'forumid' => $event['topic_data']['forum_id']));
  236. $report_link = phpbb_version_compare(PHPBB_VERSION, '3.2', '>=') ? '<a href="' . $reporttosfs_url . '" title="' . $this->user->lang['REPORT_TO_SFS']. '" data-ajax="reporttosfs" class="button button-icon-only"><i class="icon fa-exchange fa-fw" aria-hidden="true"></i><span>' . $this->user->lang['REPORT_TO_SFS'] . '</span></a>' : '<a href="' . $reporttosfs_url . '" title="' . $this->user->lang['REPORT_TO_SFS']. '" data-ajax="reporttosfs" class="button icon-button"><span>' . $this->user->lang['REPORT_TO_SFS'] . '</span></a>';
  237. $event['post_row'] = array_merge($event['post_row'], array(
  238. 'SFS_LINK' => (!$row['sfs_reported']) ? $report_link : '',
  239. ));
  240. }
  241. }
  242. /*
  243. * show_message
  244. * @param $check the type of check we are, uhmmm, checking
  245. * @return string
  246. */
  247. private function show_message($check = '')
  248. {
  249. if ($check === 'sfs_down')
  250. {
  251. return $this->user->lang['SFS_ERROR_MESSAGE'];
  252. }
  253. else
  254. {
  255. if ($this->contactadmin !== null && !empty($this->config['contactadmin_enable']))
  256. {
  257. $message = $this->user->lang('NO_SOUP_FOR_YOU', '<a href="' . $this->helper->route('rmcgirr83_contactadmin_displayform') . '">', '</a>');
  258. }
  259. else if ($this->config['contact_admin_form_enable'])
  260. {
  261. $link = ($this->config['email_enable']) ? append_sid("{$this->root_path}memberlist.$this->php_ext", 'mode=contactadmin') : 'mailto:' . htmlspecialchars($this->config['board_contact']);
  262. $message = $this->user->lang('NO_SOUP_FOR_YOU', '<a href="'. $link .'">','</a>');
  263. }
  264. else
  265. {
  266. $message = $this->user->lang('NO_SOUP_FOR_YOU_NO_CONTACT');
  267. }
  268. return $message;
  269. }
  270. }
  271. /*
  272. * stopforumspam_check
  273. * @param $username username from the forum inputs
  274. * @param $ip the users ip
  275. * @param $email email from the forum inputs
  276. * @return bool true if found, false if not
  277. */
  278. private function stopforumspam_check($username, $ip, $email)
  279. {
  280. // Default value
  281. $spam_score = 0;
  282. $sfs_log_message = !empty($this->config['sfs_log_message']) ? $this->config['sfs_log_message'] : false;
  283. // Threshold score to reject registration and/or guest posting
  284. $sfs_threshold = !empty($this->config['sfs_threshold']) ? $this->config['sfs_threshold'] : 1;
  285. // Query the SFS database and pull the data into script
  286. $json = $this->sfsapi->sfsapi('query', $username, $ip, $email, $this->config['sfs_api_key']);
  287. $json_decode = json_decode($json, true);
  288. // Check if user is a spammer, but only if we successfully got the SFS data
  289. if ($json_decode['success'])
  290. {
  291. $username_freq = $json_decode['username']['frequency'];
  292. $email_freq = $json_decode['email']['frequency'];
  293. $ip_freq = $json_decode['ip']['frequency'];
  294. // ACP settings in effect
  295. if ($this->config['sfs_by_name'] == false)
  296. {
  297. $username_freq = 0;
  298. }
  299. if ($this->config['sfs_by_email'] == false)
  300. {
  301. $email_freq = 0;
  302. }
  303. if ($this->config['sfs_by_ip'] == false)
  304. {
  305. $ip_freq = 0;
  306. }
  307. // Return the total score
  308. $spam_score = ($username_freq + $email_freq + $ip_freq);
  309. // If we've got a spammer we'll take away their soup!
  310. if ($spam_score >= $sfs_threshold)
  311. {
  312. if ($sfs_log_message)
  313. {
  314. $this->log_message('user', $username, $ip, 'LOG_SFS_MESSAGE', $email);
  315. }
  316. //user is a spammer
  317. return true;
  318. }
  319. else
  320. {
  321. return false;
  322. }
  323. }
  324. else
  325. {
  326. if ($sfs_log_message)
  327. {
  328. if ($this->config['sfs_down'])
  329. {
  330. $this->log_message('admin', $username, $ip, 'LOG_SFS_DOWN_USER_ALLOWED', $email);
  331. }
  332. else
  333. {
  334. $this->log_message('admin', $username, $ip, 'LOG_SFS_DOWN', $email);
  335. }
  336. }
  337. return 'sfs_down';
  338. }
  339. }
  340. // log messages
  341. private function log_message($mode, $username, $ip, $message, $email)
  342. {
  343. $sfs_ip_check = $this->user->lang('SFS_IP_STOPPED', $ip);
  344. $sfs_username_check = $this->user->lang('SFS_USERNAME_STOPPED', $username);
  345. $sfs_email_check = $this->user->lang('SFS_EMAIL_STOPPED', $email);
  346. if ($mode === 'admin')
  347. {
  348. $this->log->add('admin', $this->user->data['user_id'], $ip, $message, false, array($sfs_username_check, $sfs_ip_check, $sfs_email_check));
  349. }
  350. else
  351. {
  352. $this->log->add('user', $this->user->data['user_id'], $ip, $message, false, array('reportee_id' => $this->user->data['user_id'], $sfs_username_check, $sfs_ip_check, $sfs_email_check));
  353. }
  354. }
  355. // validate email on posting
  356. private function validate_email($email)
  357. {
  358. $error = phpbb_validate_email($email);
  359. return $error;
  360. }
  361. // validate username on posting
  362. private function validate_username($username)
  363. {
  364. $error = array();
  365. if (($result = validate_username($username)) !== false)
  366. {
  367. $error[] = $this->user->lang[$result . '_USERNAME'];
  368. }
  369. if (($result = validate_string($username, false, $this->config['min_name_chars'], $this->config['max_name_chars'])) !== false)
  370. {
  371. $min_max_amount = ($result == 'TOO_SHORT') ? $this->config['min_name_chars'] : $this->config['max_name_chars'];
  372. $error[] = $this->user->lang('FIELD_' . $result, $min_max_amount, $this->user->lang['USERNAME']);
  373. }
  374. return $error;
  375. }
  376. // ban a nub
  377. private function ban_by_ip($ip)
  378. {
  379. $ban_reason = (!empty($this->config['sfs_ban_reason'])) ? $this->user->lang['SFS_BANNED'] : '';
  380. // ban the nub
  381. user_ban('ip', $ip, (int) $this->config['sfs_ban_time'], 0, false, $this->user->lang['SFS_BANNED'], $ban_reason);
  382. return;
  383. }
  384. }