BxAntispamModule.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup Antispam Antispam
  7. * @ingroup UnaModules
  8. *
  9. * @{
  10. */
  11. class BxAntispamModule extends BxDolModule
  12. {
  13. public function __construct(&$aModule)
  14. {
  15. parent::__construct($aModule);
  16. }
  17. public function serviceUpdateDisposableDomainsLists ()
  18. {
  19. $o = bx_instance('BxAntispamDisposableEmailDomains', array(), 'bx_antispam');
  20. $o->updateList('blacklist', 'https://raw.githubusercontent.com/martenson/disposable-email-domains/master/disposable_email_blocklist.conf');
  21. // TODO: uncomment after adding interface for whitelisting
  22. // $o->updateList('whitelist', 'https://raw.githubusercontent.com/martenson/disposable-email-domains/master/whitelist.conf');
  23. }
  24. public function serviceIpTable ()
  25. {
  26. $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
  27. $s = _t('_bx_antispam_ip_table_status', mb_strtolower($o->getIpTableConfigTitle((int)getParam('bx_antispam_ip_list_type'))));
  28. $s .= $this->_grid('bx_antispam_grid_ip_table');
  29. return $s;
  30. }
  31. public function serviceDnsblList ()
  32. {
  33. $s = _t('_bx_antispam_dnsbl_status',
  34. BxTemplFunctions::getInstance()->statusOnOff((bool)$this->_oConfig->getAntispamOption('dnsbl_enable'), true),
  35. BxTemplFunctions::getInstance()->statusOnOff((bool)$this->_oConfig->getAntispamOption('uridnsbl_enable'), true),
  36. mb_strtolower(_t('_bx_antispam_dnsbl_behaviour_login_' . $this->_oConfig->getAntispamOption('dnsbl_behaviour_login'))),
  37. mb_strtolower(_t('_bx_antispam_dnsbl_behaviour_join_' . $this->_oConfig->getAntispamOption('dnsbl_behaviour_join')))
  38. );
  39. $s .= $this->_grid('bx_antispam_grid_dnsbl');
  40. return $s;
  41. }
  42. public function serviceBlockLog ()
  43. {
  44. return $this->_grid('bx_antispam_grid_block_log');
  45. }
  46. /**
  47. * Filter undesired words
  48. *
  49. * @param $sContent content to filter
  50. * @param $sIp IP address of content poster
  51. * @return modified or the same content.
  52. */
  53. public function serviceFilterSpam ($mContent, $sIp = '')
  54. {
  55. if (defined('BX_DOL_CRON_EXECUTE') || isAdmin() || 'on' != $this->_oConfig->getAntispamOption('profanity_enable'))
  56. return $mContent;
  57. $oProfanityFilter = bx_instance('BxAntispamProfanityFilter', array(), $this->_aModule);
  58. return $oProfanityFilter->censor($mContent);
  59. }
  60. /**
  61. * Check text for spam.
  62. * First it check if IP is whitelisted(or under cron execution or user is admin) - for whitelisted IPs check for spam isn't performed,
  63. * then it checks URLs found in text for DNSURI black lists (@see BxAntispamDNSURIBlacklists),
  64. * then it checks text in Akismet service (@see BxAntispamAkismet).
  65. * It can send report if spam is found or tries to inform caller to block the content (depending on configuration).
  66. *
  67. * @param $sContent content to check for spam
  68. * @param $sIp IP address of content poster
  69. * @param $isStripSlashes slashes parameter:
  70. * BX_SLASHES_AUTO - automatically detect magic_quotes_gpc setting
  71. * BX_SLASHES_NO_ACTION - do not perform any action with slashes
  72. * @return true if spam detected and content shouln't be recorded, false if content should be processed as usual.
  73. */
  74. public function serviceIsSpam ($sContent, $sIp = '', $isStripSlashes = BX_SLASHES_AUTO)
  75. {
  76. if (defined('BX_DOL_CRON_EXECUTE') || isAdmin())
  77. return false;
  78. if ($this->serviceIsIpWhitelisted($sIp))
  79. return false;
  80. if (version_compare(phpversion(), '7.4.0', '<') && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc() && $isStripSlashes == BX_SLASHES_AUTO)
  81. $sContent = stripslashes($sContent);
  82. $bRet = false;
  83. if ('on' == $this->_oConfig->getAntispamOption('uridnsbl_enable')) {
  84. $oDNSURIBlacklists = bx_instance('BxAntispamDNSURIBlacklists', array(), $this->_aModule);
  85. if ($oDNSURIBlacklists->isSpam($sContent)) {
  86. $oDNSURIBlacklists->onPositiveDetection($sContent);
  87. $bRet = true;
  88. }
  89. }
  90. if (!$bRet && 'on' == $this->_oConfig->getAntispamOption('akismet_enable')) {
  91. $oAkismet = bx_instance('BxAntispamAkismet', array(), $this->_aModule);
  92. if ($oAkismet->isSpam($sContent)) {
  93. $oAkismet->onPositiveDetection($sContent);
  94. $bRet = true;
  95. }
  96. }
  97. if ($bRet && 'on' == $this->_oConfig->getAntispamOption('antispam_report')) {
  98. $oProfile = BxDolProfile::getInstance();
  99. $aPlus = array(
  100. 'SpammerUrl' => $oProfile->getUrl(),
  101. 'SpammerNickName' => $oProfile->getDisplayName(),
  102. 'Page' => htmlspecialchars_adv($_SERVER['PHP_SELF']),
  103. 'Get' => print_r($_GET, true),
  104. 'Post' => print_r($_POST, true),
  105. 'SpamContent' => htmlspecialchars_adv($sContent),
  106. );
  107. $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate('bx_antispam_spam_report', $aPlus);
  108. if (!$aTemplate)
  109. trigger_error('Email template or translation missing: bx_antispam_spam_report', E_USER_ERROR);
  110. sendMail(getParam('site_email'), $aTemplate['Subject'], $aTemplate['Body']);
  111. }
  112. if ($bRet && 'on' == $this->_oConfig->getAntispamOption('antispam_block'))
  113. return true;
  114. return false;
  115. }
  116. /**
  117. * Perform complex check if user is allowed to login.
  118. * First it checks if IP is directly blocked (@see serviceIsIpBlocked),
  119. * then it checks in DNS black lists (@see serviceIsIpDnsBlacklisted).
  120. *
  121. * @param $sIp IP to check, or empty for current IP
  122. * @return empty string - if join should be allowed, error message - if join should be blocked
  123. */
  124. public function serviceCheckLogin ($sIp = '')
  125. {
  126. $bLoginBlock = ('block' == $this->_oConfig->getAntispamOption('dnsbl_behaviour_login'));
  127. $sErrorMsg = '';
  128. $sNote = $bLoginBlock ? 'login block' : 'login log';
  129. if (!$sIp)
  130. $sIp = getVisitorIP();
  131. if (!$sErrorMsg && $this->serviceIsIpBlocked($sIp))
  132. $sErrorMsg = $this->getErrorMessageIpBlocked();
  133. if (!$sErrorMsg && 'on' == $this->_oConfig->getAntispamOption('dnsbl_enable') && $this->serviceIsIpDnsBlacklisted($sIp, $sNote) && $bLoginBlock)
  134. $sErrorMsg = $this->getErrorMessageSpam();
  135. return $sErrorMsg;
  136. }
  137. /**
  138. * Perform complex check if user is allowed to join.
  139. * First it checks if IP is directly blocked (@see serviceIsIpBlocked),
  140. * then it checks in DNS black lists (@see serviceIsIpDnsBlacklisted),
  141. * then it checks in StopForumSpam service (@see BxAntispamStopForumSpam).
  142. *
  143. * @param $sCurIP IP to check, or empty for current IP
  144. * @return empty string - if join should be allowed, error message - if join should be blocked
  145. */
  146. public function serviceCheckJoin ($sEmail, &$bApproval, $sIp = '')
  147. {
  148. $bJoinBlock = ('block' == $this->_oConfig->getAntispamOption('dnsbl_behaviour_join'));
  149. $bApproval = false;
  150. $sErrorMsg = '';
  151. $sNote = $bJoinBlock ? 'join block' : 'join approval';
  152. if (!$sIp)
  153. $sIp = getVisitorIP();
  154. // check if IP is blocked
  155. if (!$sErrorMsg && $this->serviceIsIpBlocked($sIp))
  156. $sErrorMsg = $this->getErrorMessageIpBlocked();
  157. // check in DNSBL lists
  158. if (!$sErrorMsg && 'on' == $this->_oConfig->getAntispamOption('dnsbl_enable') && $this->serviceIsIpDnsBlacklisted($sIp, $sNote)) {
  159. if ('approval' == $this->_oConfig->getAntispamOption('dnsbl_behaviour_join'))
  160. $bApproval = true;
  161. else
  162. $sErrorMsg = $this->getErrorMessageSpam();
  163. }
  164. // check in StopForumSpam service
  165. if (!$sErrorMsg) {
  166. $oStopForumSpam = bx_instance('BxAntispamStopForumSpam', array(), $this->_aModule);
  167. if ($oStopForumSpam->isSpammer(array('email' => $sEmail, 'ip' => $sIp), $sNote))
  168. $sErrorMsg = $this->getErrorMessageSpam();
  169. }
  170. // check for disposable email domains
  171. if (!$sErrorMsg && 'disable' != ($sMode = $this->_oConfig->getAntispamOption('disposable_email_domains_mode'))) {
  172. $bDisposableEmailDomainsBehaviour = $this->_oConfig->getAntispamOption('disposable_email_domains_behaviour_join');
  173. $oDisposableEmailDomains = bx_instance('BxAntispamDisposableEmailDomains', array(), 'bx_antispam');
  174. if ('blacklist' == $sMode && $oDisposableEmailDomains->isBlacklisted($sEmail)) {
  175. if ('approval' == $bDisposableEmailDomainsBehaviour)
  176. $bApproval = true;
  177. else
  178. $sErrorMsg = $oDisposableEmailDomains->getErrorMessageBlacklisted();
  179. }
  180. elseif ('whitelist' == $sMode && !$oDisposableEmailDomains->isWhitelisted($sEmail)) {
  181. if ('approval' == $bDisposableEmailDomainsBehaviour)
  182. $bApproval = true;
  183. else
  184. $sErrorMsg = $oDisposableEmailDomains->getErrorMessageNotWhitelisted();
  185. }
  186. }
  187. return $sErrorMsg;
  188. }
  189. /**
  190. * Check if IP is blacklisted in some DNS chain (@see BxAntispamDNSBlacklists).
  191. *
  192. * @param $sCurIP IP to check, or empty for current IP
  193. * @param $sNote [optional] place where checking is performed, for example 'join', 'login'
  194. * @return true if IP blacklisted and not whiteloisted, or false if under cron execution or if IP isn't blacklisted
  195. */
  196. public function serviceIsIpDnsBlacklisted($sCurIP = '', $sNote = '')
  197. {
  198. if (defined('BX_DOL_CRON_EXECUTE'))
  199. return false;
  200. if (!$sCurIP)
  201. $sCurIP = getVisitorIP();
  202. if ($this->serviceIsIpWhitelisted($sCurIP))
  203. return false;
  204. $o = bx_instance('BxAntispamDNSBlacklists', array(), $this->_aModule);
  205. if (BX_DOL_DNSBL_POSITIVE == $o->dnsbl_lookup_ip(BX_DOL_DNSBL_CHAIN_SPAMMERS, $sCurIP) && BX_DOL_DNSBL_POSITIVE != $o->dnsbl_lookup_ip(BX_DOL_DNSBL_CHAIN_WHITELIST, $sCurIP)) {
  206. $o->onPositiveDetection ($sCurIP, $sNote);
  207. return true;
  208. }
  209. return false;
  210. }
  211. /**
  212. * @see BxAntispamIP::isIpWhitelisted
  213. */
  214. public function serviceIsIpWhitelisted($sIp = '')
  215. {
  216. $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
  217. return $o->isIpWhitelisted($sIp);
  218. }
  219. /**
  220. * @see BxAntispamIP::isIpBlocked
  221. */
  222. public function serviceIsIpBlocked($sIp = '')
  223. {
  224. $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
  225. return $o->isIpBlocked($sIp);
  226. }
  227. /**
  228. * @see BxAntispamIP::blockIp
  229. */
  230. public function serviceBlockIp($mixedIP, $iExpirationInSec = 86400, $sComment = '')
  231. {
  232. $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
  233. return $o->blockIp($mixedIP, $iExpirationInSec, $sComment);
  234. }
  235. /**
  236. * @see BxAntispamIP::pruning
  237. */
  238. public function servicePruning()
  239. {
  240. $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
  241. return $o->pruning();
  242. }
  243. /**
  244. * @see BxAntispamIP::pruning
  245. */
  246. public function serviceConfigValues($s)
  247. {
  248. switch ($s) {
  249. case 'ip_table':
  250. $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
  251. return $o->getIpTableConfigValues();
  252. case 'dnsbl_login':
  253. $o = bx_instance('BxAntispamDNSBlacklists', array(), $this->_aModule);
  254. return $o->getDNSBLConfigValues();
  255. case 'dnsbl_join';
  256. $o = bx_instance('BxAntispamDNSURIBlacklists', array(), $this->_aModule);
  257. return $o->getURIDNSBLConfigValues();
  258. case 'disposable_email_domains_join';
  259. $o = bx_instance('BxAntispamDisposableEmailDomains', array(), $this->_aModule);
  260. return $o->getJoinBehaviourValues();
  261. case 'disposable_email_domains_mode';
  262. $o = bx_instance('BxAntispamDisposableEmailDomains', array(), $this->_aModule);
  263. return $o->getJoinBehaviourModes();
  264. }
  265. }
  266. /**
  267. * @return array with avaliable dictionaries languages
  268. */
  269. public function serviceGetProfanityFilterDicts ()
  270. {
  271. $oProfanityFilter = bx_instance('BxAntispamProfanityFilter', array(), $this->_aModule);
  272. return $oProfanityFilter->getDicts();
  273. }
  274. protected function getErrorMessageIpBlocked ()
  275. {
  276. bx_import('BxDolLanguages');
  277. return _t('_bx_antispam_ip_blocked', $this->getErrorMessageSubmitFalsePositiveReport());
  278. }
  279. protected function getErrorMessageSpam ()
  280. {
  281. bx_import('BxDolLanguages');
  282. return _t('_bx_antispam_spam_detected', $this->getErrorMessageSubmitFalsePositiveReport());
  283. }
  284. protected function getErrorMessageSubmitFalsePositiveReport ()
  285. {
  286. if (BxDolRequest::serviceExists('bx_contact', 'get_contact_page_url') && ($sUrl = BxDolService::call('bx_contact', 'get_contact_page_url')))
  287. return _t('_bx_antispam_submit_false_positive_report', $sUrl);
  288. return '';
  289. }
  290. protected function _grid ($sObjectGrid)
  291. {
  292. $oGrid = BxDolGrid::getObjectInstance($sObjectGrid);
  293. return $oGrid ? $oGrid->getCode() : '';
  294. }
  295. }
  296. /** @} */