| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- <?php defined('BX_DOL') or die('hack attempt');
- /**
- * Copyright (c) UNA, Inc - https://una.io
- * MIT License - https://opensource.org/licenses/MIT
- *
- * @defgroup Antispam Antispam
- * @ingroup UnaModules
- *
- * @{
- */
- class BxAntispamModule extends BxDolModule
- {
- private $_bLastSubmittedFormWasToxic;
- public function __construct(&$aModule)
- {
- parent::__construct($aModule);
- $this->_bLastSubmittedFormWasToxic = false;
- }
- public function serviceUpdateDisposableDomainsLists ()
- {
- $o = bx_instance('BxAntispamDisposableEmailDomains', array(), 'bx_antispam');
- $o->updateList('blacklist', 'https://raw.githubusercontent.com/martenson/disposable-email-domains/master/disposable_email_blocklist.conf');
- // TODO: uncomment after adding interface for whitelisting
- // $o->updateList('whitelist', 'https://raw.githubusercontent.com/martenson/disposable-email-domains/master/whitelist.conf');
- }
-
- public function serviceIpTable ()
- {
- $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
- $s = _t('_bx_antispam_ip_table_status', mb_strtolower($o->getIpTableConfigTitle((int)getParam('bx_antispam_ip_list_type'))));
- $s .= $this->_grid('bx_antispam_grid_ip_table');
- return $s;
- }
- public function serviceDnsblList ()
- {
- $s = _t('_bx_antispam_dnsbl_status',
- BxTemplFunctions::getInstance()->statusOnOff((bool)$this->_oConfig->getAntispamOption('dnsbl_enable'), true),
- BxTemplFunctions::getInstance()->statusOnOff((bool)$this->_oConfig->getAntispamOption('uridnsbl_enable'), true),
- mb_strtolower(_t('_bx_antispam_dnsbl_behaviour_login_' . $this->_oConfig->getAntispamOption('dnsbl_behaviour_login'))),
- mb_strtolower(_t('_bx_antispam_dnsbl_behaviour_join_' . $this->_oConfig->getAntispamOption('dnsbl_behaviour_join')))
- );
- $s .= $this->_grid('bx_antispam_grid_dnsbl');
- return $s;
- }
- public function serviceBlockLog ()
- {
- return $this->_grid('bx_antispam_grid_block_log');
- }
- /**
- * Filter undesired words
- *
- * @param $sContent content to filter
- * @param $sIp IP address of content poster
- * @return modified or the same content.
- */
- public function serviceFilterSpam ($mContent, $sIp = '')
- {
- if (defined('BX_DOL_CRON_EXECUTE') || isAdmin() || 'on' != $this->_oConfig->getAntispamOption('profanity_enable'))
- return $mContent;
-
- $oProfanityFilter = bx_instance('BxAntispamProfanityFilter', array(), $this->_aModule);
- return $oProfanityFilter->censor($mContent);
- }
-
- /**
- * Check text for spam.
- * First it check if IP is whitelisted(or under cron execution or user is admin) - for whitelisted IPs check for spam isn't performed,
- * then it checks URLs found in text for DNSURI black lists (@see BxAntispamDNSURIBlacklists),
- * then it checks text in Akismet service (@see BxAntispamAkismet).
- * It can send report if spam is found or tries to inform caller to block the content (depending on configuration).
- *
- * @param $sContent content to check for spam
- * @param $sIp IP address of content poster
- * @param $isStripSlashes slashes parameter:
- * BX_SLASHES_AUTO - automatically detect magic_quotes_gpc setting
- * BX_SLASHES_NO_ACTION - do not perform any action with slashes
- * @return true if spam detected and content shouln't be recorded, false if content should be processed as usual.
- */
- public function serviceIsSpam ($sContent, $sIp = '', $isStripSlashes = BX_SLASHES_AUTO)
- {
- if (defined('BX_DOL_CRON_EXECUTE') || isAdmin())
- return false;
- if ($this->serviceIsIpWhitelisted($sIp))
- return false;
- if (version_compare(phpversion(), '7.4.0', '<') && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc() && $isStripSlashes == BX_SLASHES_AUTO)
- $sContent = stripslashes($sContent);
-
- $bRet = false;
- if ('on' == $this->_oConfig->getAntispamOption('uridnsbl_enable')) {
- $oDNSURIBlacklists = bx_instance('BxAntispamDNSURIBlacklists', array(), $this->_aModule);
- if ($oDNSURIBlacklists->isSpam($sContent)) {
- $oDNSURIBlacklists->onPositiveDetection($sContent);
- $bRet = true;
- }
- }
- if (!$bRet && 'on' == $this->_oConfig->getAntispamOption('akismet_enable')) {
- $oAkismet = bx_instance('BxAntispamAkismet', array(), $this->_aModule);
- if ($oAkismet->isSpam($sContent)) {
- $oAkismet->onPositiveDetection($sContent);
- $bRet = true;
- }
- }
- if ($bRet && 'on' == $this->_oConfig->getAntispamOption('antispam_report')) {
- $oProfile = BxDolProfile::getInstance();
- $aPlus = array(
- 'SpammerUrl' => $oProfile->getUrl(),
- 'SpammerNickName' => $oProfile->getDisplayName(),
- 'Page' => htmlspecialchars_adv($_SERVER['PHP_SELF']),
- 'Get' => print_r($_GET, true),
- 'Post' => print_r($_POST, true),
- 'SpamContent' => htmlspecialchars_adv($sContent),
- );
- $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate('bx_antispam_spam_report', $aPlus);
- if (!$aTemplate)
- trigger_error('Email template or translation missing: bx_antispam_spam_report', E_USER_ERROR);
- sendMail(getParam('site_email'), $aTemplate['Subject'], $aTemplate['Body']);
- }
- if ($bRet && 'on' == $this->_oConfig->getAntispamOption('antispam_block'))
- return true;
- return false;
- }
- /**
- * Check text for toxicity.
- * It can send report if toxic content is found or tries to inform caller to block the content (depending on configuration).
- *
- * @param $sContent content to check for spam
- * @param $sIp IP address of content poster
- * @param $isStripSlashes slashes parameter:
- * BX_SLASHES_AUTO - automatically detect magic_quotes_gpc setting
- * BX_SLASHES_NO_ACTION - do not perform any action with slashes
- * @return true if toxic content detected and content shouln't be recorded, false if content should be processed as usual.
- */
- public function serviceIsToxic ($sContent, $sIp = '', $isStripSlashes = BX_SLASHES_AUTO, $bSupressNotification = false)
- {
- if (defined('BX_DOL_CRON_EXECUTE') || isAdmin())
- return false;
- static $aQuickCache;
- $key = md5($sContent);
- if (isset($aQuickCache[$key]))
- return $aQuickCache[$key];
- $aQuickCache[$key] = false;
- if (!$this->serviceIsIpWhitelisted($sIp)) {
- if (version_compare(phpversion(), '7.4.0', '<') && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc() && $isStripSlashes == BX_SLASHES_AUTO)
- $sContent = stripslashes($sContent);
- if ('on' == $this->_oConfig->getAntispamOption('toxicity_enable')) {
- $oPerspectiveAPI = bx_instance('BxAntispamPerspectiveAPI', array(), $this->_aModule);
- if ($oPerspectiveAPI->isToxic($sContent)) {
- $oPerspectiveAPI->onPositiveDetection($sContent);
- $aQuickCache[$key] = true;
- if (!$bSupressNotification) $this->serviceOnToxicContentFound($sContent);
- }
- }
- }
- return $aQuickCache[$key];
- }
- public function serviceCheckFormForToxicity(&$oForm) {
- $this->_bLastSubmittedFormWasToxic = false;
- if (!$oForm->isValid() || !$oForm->isSubmitted()) return false;
- $sSubmitName = false;
- $sFormText = '';
- foreach ($oForm->aInputs as $k => $a) {
- if (isset($a['visible_for_levels']) && !BxDolForm::isVisible($a))
- continue;
- if (empty($a['name']) || 'submit' == $a['type'] || 'reset' == $a['type'] || 'button' == $a['type'] || 'value' == $a['type']) {
- if (isset($a['type']) && 'submit' == $a['type'])
- $sSubmitName = $k;
- continue;
- }
- if ('input_set' == $a['type'])
- foreach ($a as $r)
- if (isset($r['type']) && 'submit' == $r['type'])
- $sSubmitName = $k;
- $a['name'] = str_replace('[]', '', $a['name']);
- if ($a['type'] != 'textarea' && $a['type'] != 'text')
- continue;
- $val = BxDolForm::getSubmittedValue($a['name'], $oForm->aFormAttrs['method']);
- if (!$val)
- continue;
- $sFormText .= " ".$val; // collect all text fields into a single text
- }
- if ($this->serviceIsToxic($sFormText, '', BX_SLASHES_AUTO, true)) {
- $this->_bLastSubmittedFormWasToxic = true;
- $sActionrequired = $this->_oConfig->getAntispamOption('toxicity_action');
- if ($sActionrequired == 'block' || $sActionrequired == 'disapprove' && !$oForm->isStatusFieldSupported()) {
- $oForm->setValid(false);
- $this->serviceOnToxicContentFound($sFormText);
- if ($sSubmitName)
- $oForm->aInputs[$sSubmitName]['error'] = _t('_bx_antispam_form_submission_error');
- } elseif ($sActionrequired == 'disapprove' && $oForm->isStatusFieldSupported()) {
- $oForm->setForceSetToPending(true);
- return false;
- }
- }
- }
- public function serviceIsLastFormSubmittedToxic() {
- return $this->_bLastSubmittedFormWasToxic;
- }
- public function serviceSetCommentAsPending($iCmtId) {
- return $this->_oDb->setCommentStatus($iCmtId, BX_CMT_STATUS_PENDING);
- }
- public function serviceOnFormSubmitted($sModule, $iEntry) {
- if ($this->_bLastSubmittedFormWasToxic) {
- $this->serviceOnToxicContentPosted($sModule, $iEntry);
- }
- }
- public function serviceOnToxicContentFound(&$sText) {
- if ('on' == $this->_oConfig->getAntispamOption('toxicity_report')) {
- $oProfile = BxDolProfile::getInstance();
- if (!$oProfile) return;
- $aPlus = array(
- 'AuthorUrl' => $oProfile->getUrl(),
- 'AuthorNickName' => $oProfile->getDisplayName(),
- 'Page' => htmlspecialchars_adv($_SERVER['PHP_SELF']),
- 'Content' => htmlspecialchars_adv(strip_tags($sText)),
- );
- $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate('bx_antispam_toxicity_blocked_report', $aPlus);
- if (!$aTemplate)
- trigger_error('Email template or translation missing: bx_antispam_toxicity_blocked_report', E_USER_ERROR);
- sendMail(getParam('site_email'), $aTemplate['Subject'], $aTemplate['Body']);
- bx_alert('bx_antispam', 'toxic_content_found', 0, bx_get_logged_profile_id(), array(
- 'text' => $sText,
- ));
- }
- }
- public function serviceOnToxicContentPosted($sModule, $iContentId) {
- if ('on' == $this->_oConfig->getAntispamOption('toxicity_report') && $sModule) {
- $oModule = BxDolModule::getInstance($sModule);
- $CNF = &$oModule->_oConfig->CNF;
- $sContentUrl = isset($CNF['URI_VIEW_ENTRY']) ? bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $iContentId)) : false;
- $sManageContentUrl = isset($CNF['URL_MANAGE_ADMINISTRATION']) ? bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URL_MANAGE_ADMINISTRATION'])) : false;
- $oProfile = BxDolProfile::getInstance();
- if (!$oProfile) return;
- $aPlus = array(
- 'AuthorUrl' => $oProfile->getUrl(),
- 'AuthorNickName' => $oProfile->getDisplayName(),
- 'Page' => htmlspecialchars_adv($_SERVER['PHP_SELF']),
- 'bx_if:content_url' => [
- 'condition' => boolval($sContentUrl),
- 'content' => ['c_url' => $sContentUrl],
- ],
- 'bx_if:manage_content_url' => [
- 'condition' => boolval($sManageContentUrl),
- 'content' => ['m_url' => $sManageContentUrl],
- ],
- );
- $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate('bx_antispam_toxicity_posted_report', $aPlus);
- if (!$aTemplate)
- trigger_error('Email template or translation missing: bx_antispam_toxicity_posted_report', E_USER_ERROR);
- sendMail(getParam('site_email'), $aTemplate['Subject'], $aTemplate['Body']);
- bx_alert('bx_antispam', 'toxic_content_posted', $iContentId, bx_get_logged_profile_id(), array(
- 'module' => $sModule,
- 'entry_id' => $iContentId,
- 'entry_url' => $sContentUrl,
- ));
- }
- }
- /**
- * Perform complex check if user is allowed to login.
- * First it checks if IP is directly blocked (@see serviceIsIpBlocked),
- * then it checks in DNS black lists (@see serviceIsIpDnsBlacklisted).
- *
- * @param $sIp IP to check, or empty for current IP
- * @return empty string - if join should be allowed, error message - if join should be blocked
- */
- public function serviceCheckLogin ($sIp = '')
- {
- $bLoginBlock = ('block' == $this->_oConfig->getAntispamOption('dnsbl_behaviour_login'));
- $sErrorMsg = '';
- $sNote = $bLoginBlock ? 'login block' : 'login log';
- if (!$sIp)
- $sIp = getVisitorIP();
- if (!$sErrorMsg && $this->serviceIsIpBlocked($sIp))
- $sErrorMsg = $this->getErrorMessageIpBlocked();
- if (!$sErrorMsg && 'on' == $this->_oConfig->getAntispamOption('dnsbl_enable') && $this->serviceIsIpDnsBlacklisted($sIp, $sNote) && $bLoginBlock)
- $sErrorMsg = $this->getErrorMessageSpam();
- return $sErrorMsg;
- }
- /**
- * Perform complex check if user is allowed to join.
- * First it checks if IP is directly blocked (@see serviceIsIpBlocked),
- * then it checks in DNS black lists (@see serviceIsIpDnsBlacklisted),
- * then it checks in StopForumSpam service (@see BxAntispamStopForumSpam).
- *
- * @param $sCurIP IP to check, or empty for current IP
- * @return empty string - if join should be allowed, error message - if join should be blocked
- */
- public function serviceCheckJoin ($sEmail, &$bApproval, $sIp = '')
- {
- $bJoinBlock = ('block' == $this->_oConfig->getAntispamOption('dnsbl_behaviour_join'));
- $bApproval = false;
- $sErrorMsg = '';
- $sNote = $bJoinBlock ? 'join block' : 'join approval';
- if (!$sIp)
- $sIp = getVisitorIP();
- // check if IP is blocked
- if (!$sErrorMsg && $this->serviceIsIpBlocked($sIp))
- $sErrorMsg = $this->getErrorMessageIpBlocked();
- // check in DNSBL lists
- if (!$sErrorMsg && 'on' == $this->_oConfig->getAntispamOption('dnsbl_enable') && $this->serviceIsIpDnsBlacklisted($sIp, $sNote)) {
- if ('approval' == $this->_oConfig->getAntispamOption('dnsbl_behaviour_join'))
- $bApproval = true;
- else
- $sErrorMsg = $this->getErrorMessageSpam();
- }
- // check in StopForumSpam service
- if (!$sErrorMsg) {
- $oStopForumSpam = bx_instance('BxAntispamStopForumSpam', array(), $this->_aModule);
- if ($oStopForumSpam->isSpammer(array('email' => $sEmail, 'ip' => $sIp), $sNote))
- $sErrorMsg = $this->getErrorMessageSpam();
- }
- // check for disposable email domains
- if (!$sErrorMsg && 'disable' != ($sMode = $this->_oConfig->getAntispamOption('disposable_email_domains_mode'))) {
- $bDisposableEmailDomainsBehaviour = $this->_oConfig->getAntispamOption('disposable_email_domains_behaviour_join');
- $oDisposableEmailDomains = bx_instance('BxAntispamDisposableEmailDomains', array(), 'bx_antispam');
-
- if ('blacklist' == $sMode && $oDisposableEmailDomains->isBlacklisted($sEmail)) {
- if ('approval' == $bDisposableEmailDomainsBehaviour)
- $bApproval = true;
- else
- $sErrorMsg = $oDisposableEmailDomains->getErrorMessageBlacklisted();
- }
- elseif ('whitelist' == $sMode && !$oDisposableEmailDomains->isWhitelisted($sEmail)) {
- if ('approval' == $bDisposableEmailDomainsBehaviour)
- $bApproval = true;
- else
- $sErrorMsg = $oDisposableEmailDomains->getErrorMessageNotWhitelisted();
- }
- }
- return $sErrorMsg;
- }
- /**
- * Check if IP is blacklisted in some DNS chain (@see BxAntispamDNSBlacklists).
- *
- * @param $sCurIP IP to check, or empty for current IP
- * @param $sNote [optional] place where checking is performed, for example 'join', 'login'
- * @return true if IP blacklisted and not whiteloisted, or false if under cron execution or if IP isn't blacklisted
- */
- public function serviceIsIpDnsBlacklisted($sCurIP = '', $sNote = '')
- {
- if (defined('BX_DOL_CRON_EXECUTE'))
- return false;
- if (!$sCurIP)
- $sCurIP = getVisitorIP();
- if ($this->serviceIsIpWhitelisted($sCurIP))
- return false;
- $o = bx_instance('BxAntispamDNSBlacklists', array(), $this->_aModule);
- 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)) {
- $o->onPositiveDetection ($sCurIP, $sNote);
- return true;
- }
- return false;
- }
- /**
- * @see BxAntispamIP::isIpWhitelisted
- */
- public function serviceIsIpWhitelisted($sIp = '')
- {
- $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
- return $o->isIpWhitelisted($sIp);
- }
- /**
- * @see BxAntispamIP::isIpBlocked
- */
- public function serviceIsIpBlocked($sIp = '')
- {
- $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
- return $o->isIpBlocked($sIp);
- }
- /**
- * @see BxAntispamIP::blockIp
- */
- public function serviceBlockIp($mixedIP, $iExpirationInSec = 86400, $sComment = '')
- {
- $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
- return $o->blockIp($mixedIP, $iExpirationInSec, $sComment);
- }
- /**
- * @see BxAntispamIP::pruning
- */
- public function servicePruning()
- {
- $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
- return $o->pruning();
- }
- /**
- * @see BxAntispamIP::pruning
- */
- public function serviceConfigValues($s)
- {
- switch ($s) {
- case 'ip_table':
- $o = bx_instance('BxAntispamIP', array(), $this->_aModule);
- return $o->getIpTableConfigValues();
- case 'dnsbl_login':
- $o = bx_instance('BxAntispamDNSBlacklists', array(), $this->_aModule);
- return $o->getDNSBLConfigValues();
- case 'dnsbl_join';
- $o = bx_instance('BxAntispamDNSURIBlacklists', array(), $this->_aModule);
- return $o->getURIDNSBLConfigValues();
- case 'disposable_email_domains_join';
- $o = bx_instance('BxAntispamDisposableEmailDomains', array(), $this->_aModule);
- return $o->getJoinBehaviourValues();
- case 'disposable_email_domains_mode';
- $o = bx_instance('BxAntispamDisposableEmailDomains', array(), $this->_aModule);
- return $o->getJoinBehaviourModes();
- }
- }
-
- /**
- * @return array with avaliable dictionaries languages
- */
- public function serviceGetProfanityFilterDicts ()
- {
- $oProfanityFilter = bx_instance('BxAntispamProfanityFilter', array(), $this->_aModule);
- return $oProfanityFilter->getDicts();
- }
- /**
- * @return array with avaliable toxicity filter actions
- */
- public function serviceGetToxicityFilterActions ()
- {
- return [
- 'none' => _t('_bx_antispam_option_toxicity_filter_action_none'),
- 'block' => _t('_bx_antispam_option_toxicity_filter_action_block'),
- 'disapprove' => _t('_bx_antispam_option_toxicity_filter_action_disapprove'),
- ];
- }
- protected function getErrorMessageIpBlocked ()
- {
- bx_import('BxDolLanguages');
- return _t('_bx_antispam_ip_blocked', $this->getErrorMessageSubmitFalsePositiveReport());
- }
- protected function getErrorMessageSpam ()
- {
- bx_import('BxDolLanguages');
- return _t('_bx_antispam_spam_detected', $this->getErrorMessageSubmitFalsePositiveReport());
- }
- protected function getErrorMessageSubmitFalsePositiveReport ()
- {
- if (BxDolRequest::serviceExists('bx_contact', 'get_contact_page_url') && ($sUrl = BxDolService::call('bx_contact', 'get_contact_page_url')))
- return _t('_bx_antispam_submit_false_positive_report', $sUrl);
- return '';
- }
- protected function _grid ($sObjectGrid)
- {
- $oGrid = BxDolGrid::getObjectInstance($sObjectGrid);
- return $oGrid ? $oGrid->getCode() : '';
- }
- }
- /** @} */
|