From 998549dc68203059733b5e4f8f376bed85dc9dac Mon Sep 17 00:00:00 2001 From: garvinhicking Date: Tue, 18 Apr 2006 10:06:57 +0000 Subject: [PATCH] backport akismet support --- docs/NEWS | 2 + .../lang_en.inc.php | 6 + .../serendipity_event_spamblock.php | 258 ++++++++++++++---- 3 files changed, 217 insertions(+), 49 deletions(-) diff --git a/docs/NEWS b/docs/NEWS index 58d486c..beb22c3 100644 --- a/docs/NEWS +++ b/docs/NEWS @@ -3,6 +3,8 @@ Version 1.0 () ------------------------------------------------------------------------ + * Add Akismet antispam support (garvinhicking) + * Saving special crafterd configuration data as Admin superuser could lead to arbitrary PHP code inclusion from serendipity_config_local.inc.php. Since admins usually already have diff --git a/plugins/serendipity_event_spamblock/lang_en.inc.php b/plugins/serendipity_event_spamblock/lang_en.inc.php index bc98a2d..3a3fce8 100644 --- a/plugins/serendipity_event_spamblock/lang_en.inc.php +++ b/plugins/serendipity_event_spamblock/lang_en.inc.php @@ -100,3 +100,9 @@ @define('PLUGIN_EVENT_SPAMBLOCK_HIDE', 'Disable spamblock for Authors'); @define('PLUGIN_EVENT_SPAMBLOCK_HIDE_DESC', 'You can allow authors in the following usergroups to post comments without them being checked by the spamblock plugin.'); + +@define('PLUGIN_EVENT_SPAMBLOCK_AKISMET', 'Akismet API Key'); +@define('PLUGIN_EVENT_SPAMBLOCK_AKISMET_DESC', 'Akismet.com is a central anti-spam and blacklisting server. It can analyze your incoming comments and check if that comment has been listed as Spam. Akismet was developed for WordPress specifically, but can be used by other systems. You just need an API Key from http://www.akismet.com by registering an account at http://www.wordpress.com/. If you leave this API key empty, Akismet will not be used.'); +@define('PLUGIN_EVENT_SPAMBLOCK_AKISMET_FILTER', 'How to treat Akismet-reported spam'); +@define('PLUGIN_EVENT_SPAMBLOCK_REASON_AKISMET_SPAMLIST', 'Filtered by Akismet.com Blacklist'); + diff --git a/plugins/serendipity_event_spamblock/serendipity_event_spamblock.php b/plugins/serendipity_event_spamblock/serendipity_event_spamblock.php index 5c78d8e..c7f3ec9 100644 --- a/plugins/serendipity_event_spamblock/serendipity_event_spamblock.php +++ b/plugins/serendipity_event_spamblock/serendipity_event_spamblock.php @@ -34,7 +34,7 @@ var $filter_defaults; 'smarty' => '2.6.7', 'php' => '4.1.0' )); - $propbag->add('version', '1.40'); + $propbag->add('version', '1.50'); $propbag->add('event_hooks', array( 'frontend_saveComment' => true, 'external_plugin' => true, @@ -43,7 +43,31 @@ var $filter_defaults; 'backend_comments_top' => true, 'backend_view_comment' => true )); - $propbag->add('configuration', array('killswitch', 'hide_for_authors', 'bodyclone', 'entrytitle', 'ipflood', 'captchas', 'captchas_ttl', 'captcha_color', 'forcemoderation', 'disable_api_comments', 'trackback_check_url', 'links_moderate', 'links_reject', 'contentfilter_activate', 'contentfilter_urls', 'bloggdeblacklist', 'contentfilter_authors', 'hide_email', 'checkmail', 'required_fields', 'logtype', 'logfile')); + $propbag->add('configuration', array( + 'killswitch', + 'hide_for_authors', + 'bodyclone', + 'entrytitle', + 'ipflood', + 'captchas', + 'captchas_ttl', + 'captcha_color', + 'forcemoderation', + 'disable_api_comments', + 'trackback_check_url', + 'links_moderate', + 'links_reject', + 'contentfilter_activate', + 'contentfilter_urls', + 'contentfilter_authors', + 'bloggdeblacklist', + 'akismet', + 'akismet_filter', + 'hide_email', + 'checkmail', + 'required_fields', + 'logtype', + 'logfile')); $propbag->add('groups', array('ANTISPAM')); $this->filter_defaults = array( @@ -176,6 +200,32 @@ var $filter_defaults; break; + case 'akismet': + $propbag->add('type', 'string'); + $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_AKISMET); + $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_AKISMET_DESC); + $propbag->add('default', ''); + $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 'akismet_filter': + $propbag->add('type', 'radio'); + $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_AKISMET_FILTER); + $propbag->add('description', ''); + $propbag->add('default', 'reject'); + $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', 'text'); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FILTER_URLS); @@ -267,10 +317,95 @@ var $filter_defaults; return true; } - function getBlacklist($where) { + function &getBlacklist($where, $api_key = '', &$eventData, &$addData) { global $serendipity; + $ret = false; + if (function_exists('serendipity_request_start')) serendipity_request_start(); switch($where) { + case 'akismet.com': + $ret = array(); + $data = array( + 'blog' => $serendipity['baseURL'], + 'user_agent' => $_SERVER['HTTP_USER_AGENT'], + 'referrer' => $_SERVER['HTTP_REFERER'], + 'user_ip' => $_SERVER['REMOTE_ADDR'] != getenv('SERVER_ADDR') ? $_SERVER['REMOTE_ADDR'] : getenv('HTTP_X_FORWARDED_FOR'), + 'permalink' => serendipity_archiveURL($eventData['id'], $eventData['title'], 'serendipityHTTPPath', true, array('timestamp' => $eventData['timestamp'])), + 'comment_type' => ($addData['type'] == 'NORMAL' ? 'comment' : 'trackback'), + 'comment_author' => $addData['name'], + 'comment_author_email' => $addData['email'], + 'comment_author_url' => $addData['url'], + 'comment_content' => $addData['comment'] + ); + $opt = array( + 'method' => 'POST', + 'http' => '1.1', + 'timeout' => 20, + 'allowRedirects' => true, + 'maxRedirects' => 3 + ); + + require_once S9Y_PEAR_PATH . 'HTTP/Request.php'; + + $req = &new HTTP_Request( + 'http://rest.akismet.com/1.1/verify-key', + $opt + ); + + $req->addPostData('key', $api_key); + $req->addPostData('blog', $serendipity['baseURL']); + + if (PEAR::isError($req->sendRequest()) || $req->getResponseCode() != '200') { + $ret['is_spam'] = false; + $ret['message'] = 'API Verification Request failed'; + $this->log($this->logfile, $eventData['id'], 'API_ERROR', 'Akismet HTTP verification request failed.', $addData); + break; + } else { + // Fetch response + $reqdata = $req->getResponseBody(); + } + + if (!preg_match('@valid@i', $reqdata)) { + $ret['is_spam'] = false; + $ret['message'] = 'API Verification failed'; + $this->log($this->logfile, $eventData['id'], 'API_ERROR', 'Akismet API verification failed: ' . $reqdata, $addData); + break; + } + + $req = &new HTTP_Request( + 'http://' . $api_key . '.rest.akismet.com/1.1/comment-check', + $opt + ); + + foreach($data AS $key => $value) { + $req->addPostData($key, $value); + } + + if (PEAR::isError($req->sendRequest()) || $req->getResponseCode() != '200') { + $ret['is_spam'] = false; + $ret['message'] = 'Akismet Request failed'; + $this->log($this->logfile, $eventData['id'], 'API_ERROR', 'Akismet HTTP request failed.', $addData); + break; + } else { + // Fetch response + $reqdata = $req->getResponseBody(); + } + + if (preg_match('@true@i', $reqdata)) { + $ret['is_spam'] = true; + $ret['message'] = $reqdata; + } elseif (preg_match('@false@i', $reqdata)) { + $ret['is_spam'] = false; + $ret['message'] = $reqdata; + $this->log($this->logfile, $eventData['id'], 'API_ERROR', 'Akismet API verification failed: ' . $reqdata, $addData); + } else { + $ret['is_spam'] = false; + $ret['message'] = 'Akismet API failure'; + $this->log($this->logfile, $eventData['id'], 'API_ERROR', 'Akismet API failure: ' . $reqdata, $addData); + } + + break; + case 'blogg.de': $target = $serendipity['serendipityPath'] . PATH_SMARTY_COMPILE . '/blogg.de.blacklist.txt'; $timeout = 3600; // One hour @@ -279,7 +414,7 @@ var $filter_defaults; $data = file_get_contents($target); } else { $data = ''; - require_once S9Y_PEAR_PATH . 'HTTP/Request.php'; + $req = &new HTTP_Request('http://spam.blogg.de/blacklist.txt'); if (PEAR::isError($req->sendRequest()) || $req->getResponseCode() != '200') { @@ -299,11 +434,14 @@ var $filter_defaults; } $blacklist = explode("\n", $data); - return $blacklist; + $ret =& $blacklist; default: - return false; + break; } + + if (function_exists('serendipity_request_end')) serendipity_request_end(); + return $ret; } function checkScheme($maxVersion) { @@ -416,7 +554,7 @@ var $filter_defaults; } $serendipity['csuccess'] = 'true'; - $logfile = $this->get_config('logfile', $serendipity['serendipityPath'] . 'spamblock.log'); + $logfile = $this->logfile = $this->get_config('logfile', $serendipity['serendipityPath'] . 'spamblock.log'); $required_fields = $this->get_config('required_fields', ''); // Check required fields @@ -470,10 +608,31 @@ var $filter_defaults; } } + // Filter Akismet Blacklist? + $akismet_apikey = $this->get_config('akismet'); + $akismet = $this->get_config('akismet_filter'); + if (!empty($akismet_apikey) && ($akismet == 'moderate' || $akismet == 'reject')) { + $spam = $this->getBlacklist('akismet.com', $akismet_apikey, $eventData, $addData); + if ($spam['is_spam'] !== false) { + if ($akismet == 'moderate') { + $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_REASON_AKISMET_SPAMLIST . ': ' . $spam['message'], $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_REASON_AKISMET_SPAMLIST . ': ' . $spam['message'], $addData); + $eventData = array('allow_comments' => false); + $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; + return false; + } + } + } + // Check Trackback URLs? if ($addData['type'] == 'TRACKBACK' && serendipity_db_bool($this->get_config('trackback_check_url'))) { require_once S9Y_PEAR_PATH . 'HTTP/Request.php'; + if (function_exists('serendipity_request_start')) serendipity_request_start(); $req = &new HTTP_Request($addData['url'], array('allowRedirects' => true, 'maxRedirects' => 5)); $is_valid = false; if (PEAR::isError($req->sendRequest()) || $req->getResponseCode() != '200') { @@ -488,6 +647,7 @@ var $filter_defaults; $is_valid = false; } } + if (function_exists('serendipity_request_end')) serendipity_request_end(); if ($is_valid === false) { $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_TRACKBACKURL, $addData); @@ -544,54 +704,54 @@ var $filter_defaults; } } } + } - // Filter Content - $filter_bodys = explode(';', $this->get_config('contentfilter_words', $this->filter_defaults['words'])); - if (is_array($filter_bodys)) { - foreach($filter_bodys AS $filter_body) { - if (empty($filter_body)) { - continue; - } - if (preg_match('@' . $filter_body . '@', $addData['comment'])) { - if ($filter_type == 'moderate') { - $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_FILTER_WORDS, $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_WORDS, $addData); - $eventData = array('allow_comments' => false); - $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; - return false; - } + // Filter Content + $filter_bodys = explode(';', $this->get_config('contentfilter_words', $this->filter_defaults['words'])); + if (is_array($filter_bodys)) { + foreach($filter_bodys AS $filter_body) { + if (empty($filter_body)) { + continue; + } + if (preg_match('@' . $filter_body . '@', $addData['comment'])) { + if ($filter_type == 'moderate') { + $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_FILTER_WORDS, $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_WORDS, $addData); + $eventData = array('allow_comments' => false); + $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; + return false; } } } } + } // Content filtering end + + // Filter Blogg.de Blacklist? + $bloggdeblacklist = $this->get_config('bloggdeblacklist'); + if ($bloggdeblacklist == 'moderate' || $bloggdeblacklist == 'reject') { + $domains = $this->getBlacklist('blogg.de', '', $eventData, $addData); + if (is_array($domains)) { + foreach($domains AS $domain) { + $domain = trim($domain); + if (empty($domain)) { + continue; + } - // Filter Blogg.de Blacklist? - $bloggdeblacklist = $this->get_config('bloggdeblacklist'); - if ($bloggdeblacklist == 'moderate' || $bloggdeblacklist == 'reject') { - $domains = $this->getBlacklist('blogg.de'); - if (is_array($domains)) { - foreach($domains AS $domain) { - $domain = trim($domain); - if (empty($domain)) { - continue; - } - - if (preg_match('@' . preg_quote($domain) . '@i', $addData['url'])) { - if ($bloggdeblacklist == 'moderate') { - $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_REASON_BLOGG_SPAMLIST . ': ' . $domain, $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_REASON_BLOGG_SPAMLIST . ': ' . $domain, $addData); - $eventData = array('allow_comments' => false); - $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; - return false; - } + if (preg_match('@' . preg_quote($domain) . '@i', $addData['url'])) { + if ($bloggdeblacklist == 'moderate') { + $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_REASON_BLOGG_SPAMLIST . ': ' . $domain, $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_REASON_BLOGG_SPAMLIST . ': ' . $domain, $addData); + $eventData = array('allow_comments' => false); + $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; + return false; } } } -- 2.39.5