title = PLUGIN_EVENT_SPAMBLOCK_TITLE; $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_TITLE); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_DESC); $propbag->add('stackable', false); $propbag->add('author', 'Garvin Hicking, Sebastian Nohn'); $propbag->add('version', '1.21'); $propbag->add('event_hooks', array( 'frontend_saveComment' => true, 'external_plugin' => true, 'frontend_comment' => true, 'fetchcomments' => true )); $propbag->add('configuration', array('killswitch', 'bodyclone', 'ipflood', 'surbl_enabled', 'rbl_enabled', 'rbllist', 'captchas', 'captchas_ttl', 'captcha_color', 'forcemoderation', 'disable_api_comments', 'links_moderate', 'links_reject', 'contentfilter_activate', 'contentfilter_urls', 'contentfilter_authors', 'hide_email', 'logtype', 'logfile')); $this->filter_defaults = array( 'authors' => 'casino;phentermine;credit;loans;poker', 'urls' => '8gold\.com;911easymoney\.com;canadianlabels\.net;condodream\.com;crepesuzette\.com;debt-help-bill-consolidation-elimination\.com;fidelityfunding\.net;flafeber\.com;gb\.com;houseofsevengables\.com;instant-quick-money-cash-advance-personal-loans-until-pay-day\.com;mediavisor\.com;newtruths\.com;oiline\.com;onlinegamingassociation\.com;online\-+poker\.com;popwow\.com;royalmailhotel\.com;spoodles\.com;sportsparent\.com;stmaryonline\.org;thatwhichis\.com;tmsathai\.org;uaeecommerce\.com;learnhowtoplay\.com' ); } function introspect_config_item($name, &$propbag) { global $serendipity; switch($name) { case 'disable_api_comments': $propbag->add('type', 'radio'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_API_COMMENTS); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_API_COMMENTS_DESC); $propbag->add('default', 'none'); $propbag->add('radio', array( 'value' => array('moderate', 'reject', 'none'), 'desc' => array(PLUGIN_EVENT_SPAMBLOCK_API_MODERATE, PLUGIN_EVENT_SPAMBLOCK_API_REJECT, NONE) )); $propbag->add('radio_per_row', '1'); break; case 'hide_email': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_HIDE_EMAIL); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_HIDE_EMAIL_DESC); $propbag->add('default', false); break; case 'bodyclone': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BODYCLONE); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BODYCLONE_DESC); $propbag->add('default', true); break; case 'captchas': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_CAPTCHAS); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_CAPTCHAS_DESC); $propbag->add('default', true); break; case 'killswitch': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_KILLSWITCH); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_KILLSWITCH_DESC); $propbag->add('default', false); break; case 'contentfilter_activate': $propbag->add('type', 'radio'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FILTER_ACTIVATE); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_FILTER_ACTIVATE_DESC); $propbag->add('default', 'moderate'); $propbag->add('radio', array( 'value' => array('moderate', 'reject', 'none'), 'desc' => array(PLUGIN_EVENT_SPAMBLOCK_API_MODERATE, PLUGIN_EVENT_SPAMBLOCK_API_REJECT, NONE) )); $propbag->add('radio_per_row', '1'); break; case 'contentfilter_urls': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FILTER_URLS); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_FILTER_URLS_DESC); $propbag->add('default', $this->filter_defaults['urls']); break; case 'contentfilter_authors': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FILTER_AUTHORS); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_FILTER_AUTHORS_DESC); $propbag->add('default', $this->filter_defaults['authors']); break; case 'logfile': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_LOGFILE); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_LOGFILE_DESC); $propbag->add('default', $serendipity['serendipityPath'] . 'spamblock.log'); break; case 'logtype': $propbag->add('type', 'radio'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_LOGTYPE); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_LOGTYPE_DESC); $propbag->add('default', 'db'); $propbag->add('radio', array( 'value' => array('file', 'db', 'none'), 'desc' => array(PLUGIN_EVENT_SPAMBLOCK_LOGTYPE_FILE, PLUGIN_EVENT_SPAMBLOCK_LOGTYPE_DB, PLUGIN_EVENT_SPAMBLOCK_LOGTYPE_NONE) )); $propbag->add('radio_per_row', '1'); break; case 'ipflood': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_IPFLOOD); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_IPFLOOD_DESC); $propbag->add('default', 0); break; case 'surbl_enabled': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_SURBL); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_SURBL_DESC); $propbag->add('default', false); break; case 'rbl_enabled': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_RBL); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_RBL_DESC); $propbag->add('default', false); break; case 'rbllist': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_RBLLIST); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_RBLLIST_DESC); $propbag->add('default', 'sbl-xbl.spamhaus.org, bl.spamcop.net'); break; case 'captchas_ttl': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_CAPTCHAS_TTL); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_CAPTCHAS_TTL_DESC); $propbag->add('default', '7'); break; case 'captcha_color': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_CAPTCHA_COLOR); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_CAPTCHA_COLOR_DESC); $propbag->add('default', '255,255,255'); break; case 'forcemoderation': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FORCEMODERATION); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_FORCEMODERATION_DESC); $propbag->add('default', '30'); break; case 'links_moderate': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_LINKS_MODERATE); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_LINKS_MODERATE_DESC); $propbag->add('default', '7'); break; case 'links_reject': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_LINKS_REJECT); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_LINKS_REJECT_DESC); $propbag->add('default', '13'); break; default: return false; } return true; } function checkScheme($maxVersion) { global $serendipity; $version = $this->get_config('version', '1.0'); if ($version != $maxVersion) { $q = "CREATE TABLE {$serendipity['dbPrefix']}spamblocklog ( timestamp int(10) {UNSIGNED} default null, type varchar(255), reason text, entry_id int(10) {UNSIGNED} not null default '0', author varchar(80), email varchar(200), url varchar(200), useragent varchar(255), ip varchar(15), referer varchar(255), body text)"; $sql = serendipity_db_schema_import($q); $q = "CREATE INDEX kspamidx ON {$serendipity['dbPrefix']}spamblocklog (timestamp);"; $sql = serendipity_db_schema_import($q); $q = "CREATE INDEX kspamtypeidx ON {$serendipity['dbPrefix']}spamblocklog (type);"; $sql = serendipity_db_schema_import($q); $q = "CREATE INDEX kspamentryidx ON {$serendipity['dbPrefix']}spamblocklog (entry_id);"; $sql = serendipity_db_schema_import($q); $this->set_config('version', $maxVersion); } return true; } function generate_content(&$title) { $title = $this->title; } function event_hook($event, &$bag, &$eventData, $addData = null) { global $serendipity; $hooks = &$bag->get('event_hooks'); if (isset($hooks[$event])) { $captchas_ttl = $this->get_config('captchas_ttl', 7); $captchas = (serendipity_db_bool($this->get_config('captchas', true)) ? true : false); // Check if the entry is older than the allowed amount of time. Enforce kaptchas if that is true // of if kaptchas are activated for every entry $show_captcha = ($captchas && isset($eventData['timestamp']) && ($captchas_ttl < 1 || ($eventData['timestamp'] < (time() - ($captchas_ttl*60*60*24)))) ? true : false); $forcemoderation = $this->get_config('forcemoderation', 60); $links_moderate = $this->get_config('links_moderate', 10); $links_reject = $this->get_config('links_reject', 20); if (function_exists('imagettftext') && function_exists('imagejpeg')) { $max_char = 5; $min_char = 3; $use_gd = true; } else { $max_char = $min_char = 5; $use_gd = false; } switch($event) { case 'fetchcomments': if (is_array($eventData) && !$_SESSION['serendipityAuthedUser'] && serendipity_db_bool($this->get_config('hide_email', false))) { // Will force emails to be not displayed in comments and RSS feed for comments. Will not apply to logged in admins (so not in the backend as well) @reset($eventData); while(list($idx, $comment) = each($eventData)) { $eventData[$idx]['no_email'] = true; } } break; case 'frontend_saveComment': if (!is_array($eventData) || serendipity_db_bool($eventData['allow_comments'])) { if ($this->get_config('logtype', 'db') == 'db' && $this->get_config('version') != $bag->get('version')) { $this->checkScheme($bag->get('version')); } $serendipity['csuccess'] = 'true'; $logfile = $this->get_config('logfile', $serendipity['serendipityPath'] . 'spamblock.log'); // Check for global emergency moderation if ( $this->get_config('killswitch', false) === true ) { $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_KILLSWITCH, $addData); $eventData = array('allow_comments' => false); $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_KILLSWITCH; return false; } // Check for not allowing trackbacks/wfwcomments if ( ($addData['type'] != 'NORMAL' || $addData['source'] == 'API') && $this->get_config('disable_api_comments', 'none') != 'none') { if ($this->get_config('disable_api_comments') == 'reject') { $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_API, $addData); $eventData = array('allow_comments' => false); $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_REASON_API; } elseif ($this->get_config('disable_api_comments') == 'moderate') { $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_REASON_API, $addData); $eventData['moderate_comments'] = true; $serendipity['csuccess'] = 'moderate'; $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_REASON_API; } return false; } // Check for word filtering if ($filter_type = $this->get_config('contentfilter_activate', 'moderate')) { // Filter authors names $filter_authors = explode(';', $this->get_config('contentfilter_authors', $this->filter_defaults['authors'])); if (is_array($filter_authors)) { foreach($filter_authors AS $filter_author) { if (preg_match('@' . $filter_author . '@', $eventData['name'])) { if ($filter_type == 'moderate') { $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_FILTER_AUTHORS, $addData); $eventData['moderate_comments'] = true; $serendipity['csuccess'] = 'moderate'; $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; } else { $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_FILTER_AUTHORS, $addData); $eventData = array('allow_comments' => false); $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; } } } } // Filter URL $filter_urls = explode(';', $this->get_config('contentfilter_urls', $this->filter_defaults['urls'])); if (is_array($filter_urls)) { foreach($filter_urls AS $filter_url) { if (preg_match('@' . $filter_url . '@', $eventData['url'])) { if ($filter_type == 'moderate') { $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_FILTER_URLS, $addData); $eventData['moderate_comments'] = true; $serendipity['csuccess'] = 'moderate'; $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; } else { $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_FILTER_URLS, $addData); $eventData = array('allow_comments' => false); $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; } } } } } // Check for forced moderation if ($forcemoderation > 0 && $eventData['timestamp'] < (time() - ($forcemoderation * 60 * 60 * 24))) { $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_REASON_FORCEMODERATION, $addData); $eventData['moderate_comments'] = true; $serendipity['csuccess'] = 'moderate'; $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_REASON_FORCEMODERATION; return false; } // Check for maximum number of links before rejecting $link_count = substr_count(strtolower($addData['comment']), 'http://'); if ($links_reject > 0 && $link_count > $links_reject) { $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_LINKS_REJECT, $addData); $eventData = array('allow_comments' => false); $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; return false; } // Check for maximum number of links before forcing moderation if ($links_moderate > 0 && $link_count > $links_moderate) { $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_LINKS_MODERATE, $addData); $eventData['moderate_comments'] = true; $serendipity['csuccess'] = 'moderate'; $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_REASON_LINKS_MODERATE; return false; } // Captcha checking if ($show_captcha && $addData['type'] == 'NORMAL') { if (!isset($_SESSION['spamblock']['captcha']) || !isset($serendipity['POST']['captcha']) || strtolower($serendipity['POST']['captcha']) != strtolower($_SESSION['spamblock']['captcha'])) { $this->log($logfile, $eventData['id'], 'REJECTED', sprintf(PLUGIN_EVENT_SPAMBLOCK_REASON_CAPTCHAS, $serendipity['POST']['captcha'], $_SESSION['spamblock']['captcha']), $addData); $eventData = array('allow_comments' => false); $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_CAPTCHAS; return false; } } // Check for identical comments. We allow to bypass trackbacks from our server to our own blog. if ( $this->get_config('bodyclone', true) === true && $_SERVER['REMOTE_ADDR'] != $_SERVER['SERVER_ADDR']) { $query = "SELECT count(id) AS counter FROM {$serendipity['dbPrefix']}comments WHERE body = '" . serendipity_db_escape_string($addData['comment']) . "'"; $row = serendipity_db_query($query, true); if (is_array($row) && $row['counter'] > 0) { $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_BODYCLONE, $addData); $eventData = array('allow_comments' => false); $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; return false; } } // Check last IP if ($addData['type'] == 'NORMAL' && $this->get_config('ipflood', 2) != 0 ) { $query = "SELECT max(timestamp) AS last_post FROM {$serendipity['dbPrefix']}comments WHERE ip = '" . serendipity_db_escape_string($_SERVER['REMOTE_ADDR']) . "'"; $row = serendipity_db_query($query, true); if (is_array($row) && $row['last_post'] > (time() - $this->get_config('ipflood', 2)*60)) { $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_IPFLOOD, $addData); $eventData = array('allow_comments' => false); $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_IP; return false; } } // Check for IP listed in RBL if (serendipity_db_bool($this->get_config('rbl_enabled', false))) { require_once 'bundled-libs/Net/DNSBL.php'; $dnsbl = new Net_DNSBL(); $remoteIP = $_SERVER['REMOTE_ADDR']; # $remoteIP = '81.70.69.193'; $dnsbl->setBlacklists(explode(',', $this->get_config('rbllist'))); if ($dnsbl->isListed($remoteIP)) { $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_RBL, $addData); $eventData = array('allow_comments' => false); $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_RBL; return false; } } // Check for IP listed in SURBL if (serendipity_db_bool($this->get_config('surbl_enabled', false))) { require_once 'bundled-libs/Net/DNSBL/SURBL.php'; $surbl = new Net_DNSBL_SURBL(); if ($surbl->isListed($addData['url'])) { $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_SURBL, $addData); $eventData = array('allow_comments' => false); $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_SURBL; return false; } // BEGIN Code copied from http://www.phpfreaks.com/quickcode/Extract_All_URLs_on_a_Page/15.php $urls = '(http|file|ftp)'; $ltrs = '\w'; $gunk = '/#~:.?+=&%@!\-'; $punc = '.:?\-'; $any = "$ltrs$gunk$punc"; preg_match_all("{ \b $urls : [$any] +? (?= [$punc] * [^$any] | $ ) }x", $addData['comment'], $matches); // END Code copied from http://www.phpfreaks.com/quickcode/Extract_All_URLs_on_a_Page/15.php foreach ($matches[0] as $surbl_check_url) { if ($surbl->isListed($surbl_check_url)) { $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_SURBL, $addData); $eventData = array('allow_comments' => false); $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_SURBL; return false; } } } } return true; break; case 'frontend_comment': if (serendipity_db_bool($this->get_config('hide_email', false))) { echo '