add a linebreak and fix some whitespaces

This commit is contained in:
Ian 2015-09-03 11:43:49 +02:00
parent bec25c8f5f
commit d6166ffaa7
5 changed files with 218 additions and 207 deletions

View file

@ -1,3 +1,9 @@
Version 1.2.9
* add a linebreak
Version 1.2.8
* Size honepot form field in case it is actually shown
Version 1.2.7 Version 1.2.7
* Made it work more nice with the contactform plugin * Made it work more nice with the contactform plugin

View file

@ -7,22 +7,22 @@
var url = typeof loadData.url != 'undefined' ? loadData.url : null; var url = typeof loadData.url != 'undefined' ? loadData.url : null;
var answer = typeof loadData.answer != 'undefined' ? loadData.answer : null; var answer = typeof loadData.answer != 'undefined' ? loadData.answer : null;
var scrambleKey = typeof loadData.scrambleKey != 'undefined' ? loadData.scrambleKey : null; var scrambleKey = typeof loadData.scrambleKey != 'undefined' ? loadData.scrambleKey : null;
this.attachToLoadEvent = function() { this.attachToLoadEvent = function() {
var handlerCalled = false; var handlerCalled = false;
var eventHandler = function() { var eventHandler = function() {
// Since we use multiple handlers, only run this function once // Since we use multiple handlers, only run this function once
if (handlerCalled) return; if (handlerCalled) return;
handlerCalled = true; handlerCalled = true;
that.initCaptcha(); that.initCaptcha();
// We don't need any additional load events anymore // We don't need any additional load events anymore
if (document.addEventListener) { if (document.addEventListener) {
document.removeEventListener('load', eventHandler, true); document.removeEventListener('load', eventHandler, true);
} }
} }
if (document.addEventListener) { if (document.addEventListener) {
// Use DOMContentLoaded for modern browsers, load for older ones // Use DOMContentLoaded for modern browsers, load for older ones
document.addEventListener('DOMContentLoaded', eventHandler, true); document.addEventListener('DOMContentLoaded', eventHandler, true);
@ -39,35 +39,35 @@
} }
} }
} }
this.initCaptcha = function() { this.initCaptcha = function() {
if (null === inputCaptcha) { if (null === inputCaptcha) {
return; return;
} }
if ('default' == method && null !== answer) { if ('default' == method && null !== answer) {
fillCaptcha(answer, scrambleKey) fillCaptcha(answer, scrambleKey)
} else if ('json' == method && null !== url) { } else if ('json' == method && null !== url) {
fetchJsonData(); fetchJsonData();
} }
} }
this.hideBeeElement = function() { this.hideBeeElement = function() {
var elementClass = divCaptcha.className; var elementClass = divCaptcha.className;
if (null === elementClass.match(/\bspambeehidden\b/)) { if (null === elementClass.match(/\bspambeehidden\b/)) {
divCaptcha.className = elementClass + ' spambeehidden'; divCaptcha.className = elementClass + ' spambeehidden';
} }
} }
function fillCaptcha(answer, scrambleKey) { function fillCaptcha(answer, scrambleKey) {
if (typeof scrambleKey != 'undefined' && null !== scrambleKey) { if (typeof scrambleKey != 'undefined' && null !== scrambleKey) {
answer = xorDescramble(decodeUtf8(unescape(answer)), scrambleKey); answer = xorDescramble(decodeUtf8(unescape(answer)), scrambleKey);
} }
inputCaptcha.value = answer; inputCaptcha.value = answer;
that.hideBeeElement(); that.hideBeeElement();
} }
function fetchJsonData() { function fetchJsonData() {
if (window.XMLHttpRequest) { // Mozilla, Safari, Opera, IE7 if (window.XMLHttpRequest) { // Mozilla, Safari, Opera, IE7
var httpRequest = new XMLHttpRequest(); var httpRequest = new XMLHttpRequest();
@ -81,35 +81,35 @@
httpRequest.setRequestHeader('content-Type', 'application/x-www-form-urlencoded; charset=utf-8'); httpRequest.setRequestHeader('content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
httpRequest.send(); httpRequest.send();
} }
function fetchJsonDataReady(httpRequest) { function fetchJsonDataReady(httpRequest) {
if (null !== httpRequest && 4 == httpRequest.readyState && 200 == httpRequest.status) { if (null !== httpRequest && 4 == httpRequest.readyState && 200 == httpRequest.status) {
var response = httpRequest.responseText; var response = httpRequest.responseText;
var jsonResponse = typeof JSON != 'undefined' ? JSON.parse(response) : eval('(' + response + ')'); var jsonResponse = typeof JSON != 'undefined' ? JSON.parse(response) : eval('(' + response + ')');
var answer = jsonResponse.answer; var answer = jsonResponse.answer;
var scrambleKey = typeof jsonResponse.scrambleKey != 'undefined' ? jsonResponse.scrambleKey : null; var scrambleKey = typeof jsonResponse.scrambleKey != 'undefined' ? jsonResponse.scrambleKey : null;
if (typeof answer != 'string' || 'ERROR' != answer.toUpperCase()) { if (typeof answer != 'string' || 'ERROR' != answer.toUpperCase()) {
fillCaptcha(answer, scrambleKey); fillCaptcha(answer, scrambleKey);
} }
} }
} }
function decodeUtf8(string) { function decodeUtf8(string) {
return decodeURIComponent(escape(string)); return decodeURIComponent(escape(string));
} }
function xorDescramble(string, key) { function xorDescramble(string, key) {
var decoded = ''; var decoded = '';
for (i = 0; i < string.length; ++i) { for (i = 0; i < string.length; ++i) {
decoded += String.fromCharCode(string.charCodeAt(i) ^ key); decoded += String.fromCharCode(string.charCodeAt(i) ^ key);
} }
return decoded; return decoded;
} }
} }
var spamBeeObj = new SpamBeeCaptcha(spamBeeData); var spamBeeObj = new SpamBeeCaptcha(spamBeeData);
spamBeeObj.attachToLoadEvent(); spamBeeObj.attachToLoadEvent();
})(); })();

View file

@ -1,6 +1,5 @@
<?php <?php
if (IN_serendipity !== true) { if (IN_serendipity !== true) {
die ("Don't hack!"); die ("Don't hack!");
} }
@ -24,7 +23,7 @@ require_once dirname(__FILE__) . '/json/json.php4.include.php';
/** /**
* Serendipity plug-in for providing spam protection via a * Serendipity plug-in for providing spam protection via a
* Honey Pot field and a hidden Captcha. * Honey Pot field and a hidden Captcha.
* *
* @author Grischa Brockhaus * @author Grischa Brockhaus
* @author Janek Bevendorff * @author Janek Bevendorff
*/ */
@ -35,62 +34,62 @@ class serendipity_event_spamblock_bee extends serendipity_event
* @var string * @var string
*/ */
var $title = PLUGIN_EVENT_SPAMBLOCK_BEE_TITLE; var $title = PLUGIN_EVENT_SPAMBLOCK_BEE_TITLE;
/** /**
* Whether to use a Honey Pot * Whether to use a Honey Pot
* @var boolean * @var boolean
*/ */
var $useHoneyPot = true; var $useHoneyPot = true;
/** /**
* Whether the Captcha is enabled and what to do when validation failed * Whether the Captcha is enabled and what to do when validation failed
* @var string * @var string
*/ */
var $hiddenCaptchaHandle = null; var $hiddenCaptchaHandle = null;
/** /**
* Method for retrieving the correct answer of the hidden Captcha. * Method for retrieving the correct answer of the hidden Captcha.
* Either 'default', 'json' or 'smarty'. * Either 'default', 'json' or 'smarty'.
* @var string * @var string
*/ */
var $answerRetrievalMethod = null; var $answerRetrievalMethod = null;
/** /**
* Correct answer for the Captcha. If RegExp matching is on, it'll * Correct answer for the Captcha. If RegExp matching is on, it'll
* also contain an index 'pattern' * also contain an index 'pattern'
* @var array * @var array
*/ */
var $captchaAnswer = array('answer' => null); var $captchaAnswer = array('answer' => null);
/** /**
* Type of question asked in the Captcha. This is either 'math' or 'custom' * Type of question asked in the Captcha. This is either 'math' or 'custom'
* @var string * @var string
*/ */
var $captchaQuestionType = null; var $captchaQuestionType = null;
/** /**
* Whether to use RegExp matching for the hidden Captcha * Whether to use RegExp matching for the hidden Captcha
* @var boolean * @var boolean
*/ */
var $useRegularExpressions = false; var $useRegularExpressions = false;
/** /**
* Constructor. Initialize class variables from configuration * Constructor. Initialize class variables from configuration
* @return void * @return void
*/ */
function serendipity_event_spamblock_bee($instance) { function serendipity_event_spamblock_bee($instance) {
$this->instance = $instance; $this->instance = $instance;
$this->answerRetrievalMethod = $this->get_config('answer_retrieval_method', 'default'); $this->answerRetrievalMethod = $this->get_config('answer_retrieval_method', 'default');
$this->captchaQuestionType = $this->get_config('question_type', 'math'); $this->captchaQuestionType = $this->get_config('question_type', 'math');
$this->useHoneyPot = $this->get_config('do_honeypot', true); $this->useHoneyPot = $this->get_config('do_honeypot', true);
$this->hiddenCaptchaHandle = $this->get_config('do_hiddencaptcha', PLUGIN_EVENT_SPAMBLOCK_SWTCH_MODERATE); $this->hiddenCaptchaHandle = $this->get_config('do_hiddencaptcha', PLUGIN_EVENT_SPAMBLOCK_SWTCH_MODERATE);
$this->useRegularExpressions = $this->get_config('use_regexp', false); $this->useRegularExpressions = $this->get_config('use_regexp', false);
} }
/** /**
* Declare Serendipity backend properties. * Declare Serendipity backend properties.
* *
* @param serendipity_property_bag $propbag * @param serendipity_property_bag $propbag
*/ */
function introspect(&$propbag) function introspect(&$propbag)
@ -106,28 +105,29 @@ class serendipity_event_spamblock_bee extends serendipity_event
'smarty' => '2.6.7', 'smarty' => '2.6.7',
'php' => '4.1.0' 'php' => '4.1.0'
)); ));
$propbag->add('version', PLUGIN_SPAMBLOCK_BEE_VERSION); // setup via version.inc.php $propbag->add('version', PLUGIN_SPAMBLOCK_BEE_VERSION); // setup via version.inc.php
$propbag->add('event_hooks', array( $propbag->add('event_hooks', array(
'frontend_comment' => true, 'frontend_comment' => true,
'frontend_saveComment' => true, 'frontend_saveComment' => true,
'frontend_footer' => true, 'frontend_footer' => true,
'css' => true, 'css' => true,
'external_plugin' => true, 'external_plugin' => true
)); ));
$propbag->add('groups', array('ANTISPAM')); $propbag->add('groups', array('ANTISPAM'));
$configuration = array('header_desc','do_honeypot', 'do_hiddencaptcha' ); $configuration = array('header_desc','do_honeypot', 'do_hiddencaptcha' );
if (!class_exists('serendipity_event_spamblock')) { // Only do that, if spamblock is not installed. // Only do that, if spamblock is not installed
$configuration =array_merge($configuration, array('entrytitle', 'samebody', 'required_fields')); if (!class_exists('serendipity_event_spamblock')) {
$configuration = array_merge($configuration, array('entrytitle', 'samebody', 'required_fields'));
} }
$configuration =array_merge($configuration, array('spamlogtype', 'spamlogfile', 'plugin_path')); $configuration = array_merge($configuration, array('spamlogtype', 'spamlogfile', 'plugin_path'));
$configuration =array_merge($configuration, array( $configuration = array_merge($configuration, array(
'advanced_cc_desc', 'answer_retrieval_method', 'question_type', 'advanced_cc_desc', 'answer_retrieval_method', 'question_type',
'questions', 'answers', 'use_regexp' 'questions', 'answers', 'use_regexp'
)); ));
$propbag->add('configuration', $configuration ); $propbag->add('configuration', $configuration );
$propbag->add('config_groups', array( $propbag->add('config_groups', array(
PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_SECTION_LOGGING => array( PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_SECTION_LOGGING => array(
@ -140,19 +140,19 @@ class serendipity_event_spamblock_bee extends serendipity_event
) )
); );
} }
/** /**
* Set plug-in title. * Set plug-in title.
* *
* @param string $title * @param string $title
*/ */
function generate_content(&$title) { function generate_content(&$title) {
$title = PLUGIN_EVENT_SPAMBLOCK_BEE_TITLE; $title = PLUGIN_EVENT_SPAMBLOCK_BEE_TITLE;
} }
/** /**
* Generate backend configuration fields * Generate backend configuration fields
* *
* @param string $name field name * @param string $name field name
* @param serendipity_property_bag $propbag properties * @param serendipity_property_bag $propbag properties
* @return bool * @return bool
@ -160,27 +160,27 @@ class serendipity_event_spamblock_bee extends serendipity_event
function introspect_config_item($name, &$propbag) function introspect_config_item($name, &$propbag)
{ {
global $serendipity; global $serendipity;
$rejectType = array( $rejectType = array(
PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF => PLUGIN_EVENT_SPAMBLOCK_BEE_RESULT_OFF, PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF => PLUGIN_EVENT_SPAMBLOCK_BEE_RESULT_OFF,
PLUGIN_EVENT_SPAMBLOCK_SWTCH_MODERATE => PLUGIN_EVENT_SPAMBLOCK_BEE_RESULT_MODERATE, PLUGIN_EVENT_SPAMBLOCK_SWTCH_MODERATE => PLUGIN_EVENT_SPAMBLOCK_BEE_RESULT_MODERATE,
PLUGIN_EVENT_SPAMBLOCK_SWTCH_REJECT => PLUGIN_EVENT_SPAMBLOCK_BEE_RESULT_REJECT, PLUGIN_EVENT_SPAMBLOCK_SWTCH_REJECT => PLUGIN_EVENT_SPAMBLOCK_BEE_RESULT_REJECT,
); );
$retrievalMethod = array( $retrievalMethod = array(
'default' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_RM_DEFAULT, 'default' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_RM_DEFAULT,
'json' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_RM_JSON, 'json' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_RM_JSON,
'smarty' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_RM_SMARTY, 'smarty' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_RM_SMARTY,
'smarty_enc' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_RM_SMARTY_ENC 'smarty_enc' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_RM_SMARTY_ENC
); );
$questionType = array( $questionType = array(
'math' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QT_MATH, 'math' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QT_MATH,
'custom' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QT_CUSTOM 'custom' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QT_CUSTOM
); );
switch($name) { switch($name) {
case 'header_desc': case 'header_desc':
$propbag->add('type', 'content'); $propbag->add('type', 'content');
$propbag->add('default', PLUGIN_EVENT_SPAMBLOCK_BEE_EXTRA_DESC . $propbag->add('default', PLUGIN_EVENT_SPAMBLOCK_BEE_EXTRA_DESC .
'<img src="' . $serendipity['baseURL'] . 'index.php?/plugin/spamblockbee.png" alt="" title="' . PLUGIN_EVENT_SPAMBLOCK_BEE_TITLE . '" style="float:right">' ); '<img src="' . $serendipity['baseURL'] . 'index.php?/plugin/spamblockbee.png" alt="" title="' . PLUGIN_EVENT_SPAMBLOCK_BEE_TITLE . '" style="float:right">' );
@ -192,7 +192,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
$propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_SPAM_HONEYPOT_DESC); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_SPAM_HONEYPOT_DESC);
$propbag->add('default', true); $propbag->add('default', true);
break; break;
case 'do_hiddencaptcha': case 'do_hiddencaptcha':
$propbag->add('type', 'select'); $propbag->add('type', 'select');
$propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_SPAM_HCAPTCHA); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_SPAM_HCAPTCHA);
@ -207,7 +207,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
$propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BEE_REQUIRED_FIELDS_DESC); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BEE_REQUIRED_FIELDS_DESC);
$propbag->add('default', ''); $propbag->add('default', '');
break; break;
case 'entrytitle': case 'entrytitle':
$propbag->add('type', 'select'); $propbag->add('type', 'select');
$propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_FILTER_TITLE); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_FILTER_TITLE);
@ -215,7 +215,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
$propbag->add('select_values', $rejectType); $propbag->add('select_values', $rejectType);
$propbag->add('default', PLUGIN_EVENT_SPAMBLOCK_SWTCH_REJECT); $propbag->add('default', PLUGIN_EVENT_SPAMBLOCK_SWTCH_REJECT);
break; break;
case 'samebody': case 'samebody':
$propbag->add('type', 'select'); $propbag->add('type', 'select');
$propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_FILTER_SAMEBODY); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_FILTER_SAMEBODY);
@ -223,7 +223,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
$propbag->add('select_values', $rejectType); $propbag->add('select_values', $rejectType);
$propbag->add('default', PLUGIN_EVENT_SPAMBLOCK_SWTCH_REJECT); $propbag->add('default', PLUGIN_EVENT_SPAMBLOCK_SWTCH_REJECT);
break; break;
case 'spamlogtype': case 'spamlogtype':
$logtypevalues = array ( $logtypevalues = array (
'none' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_SPAM_LOGTYPE_NONE, 'none' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_SPAM_LOGTYPE_NONE,
@ -236,7 +236,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
$propbag->add('select_values', $logtypevalues); $propbag->add('select_values', $logtypevalues);
$propbag->add('default', 'none'); $propbag->add('default', 'none');
break; break;
case 'spamlogfile': case 'spamlogfile':
$propbag->add('type', 'string'); $propbag->add('type', 'string');
$propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_SPAM_LOGFILE); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_SPAM_LOGFILE);
@ -250,12 +250,12 @@ class serendipity_event_spamblock_bee extends serendipity_event
$propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BEE_PATH_DESC); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BEE_PATH_DESC);
$propbag->add('default', $serendipity['serendipityHTTPPath'] . 'plugins/serendipity_event_spamblock_bee/'); $propbag->add('default', $serendipity['serendipityHTTPPath'] . 'plugins/serendipity_event_spamblock_bee/');
break; break;
case 'advanced_cc_desc': case 'advanced_cc_desc':
$propbag->add('type', 'content'); $propbag->add('type', 'content');
$propbag->add('default', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_DESC); $propbag->add('default', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_DESC);
break; break;
case 'answer_retrieval_method': case 'answer_retrieval_method':
$propbag->add('type', 'select'); $propbag->add('type', 'select');
$propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_ANSWER_RETRIEVAL); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_ANSWER_RETRIEVAL);
@ -263,7 +263,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
$propbag->add('select_values', $retrievalMethod); $propbag->add('select_values', $retrievalMethod);
$propbag->add('default', 'default'); $propbag->add('default', 'default');
break; break;
case 'question_type': case 'question_type':
$propbag->add('type', 'select'); $propbag->add('type', 'select');
$propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QUESTION_TYPE); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QUESTION_TYPE);
@ -271,7 +271,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
$propbag->add('select_values', $questionType); $propbag->add('select_values', $questionType);
$propbag->add('default', 'math'); $propbag->add('default', 'math');
break; break;
case 'questions': case 'questions':
$propbag->add('type', 'text'); $propbag->add('type', 'text');
$propbag->add('rows', 8); $propbag->add('rows', 8);
@ -279,7 +279,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
$propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QUESTIONS_DESC); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QUESTIONS_DESC);
$propbag->add('default', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_DEFAULT_QUESTIONS); $propbag->add('default', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_DEFAULT_QUESTIONS);
break; break;
case 'answers': case 'answers':
$propbag->add('type', 'text'); $propbag->add('type', 'text');
$propbag->add('rows', 8); $propbag->add('rows', 8);
@ -287,23 +287,23 @@ class serendipity_event_spamblock_bee extends serendipity_event
$propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_ANSWERS_DESC); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_ANSWERS_DESC);
$propbag->add('default', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_DEFAULT_ANSWERS); $propbag->add('default', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_DEFAULT_ANSWERS);
break; break;
case 'use_regexp': case 'use_regexp':
$propbag->add('type', 'boolean'); $propbag->add('type', 'boolean');
$propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_USE_REGEXP); $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_USE_REGEXP);
$propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_USE_REGEXP_DESC); $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_USE_REGEXP_DESC);
$propbag->add('default', false); $propbag->add('default', false);
break; break;
default: default:
return false; return false;
} }
return true; return true;
} }
/** /**
* Hook for Serendipity events, initialize plug-in features * Hook for Serendipity events, initialize plug-in features
* *
* @param string $event * @param string $event
* @param serendipity_property_bag $bag * @param serendipity_property_bag $bag
* @param array $eventData * @param array $eventData
@ -312,7 +312,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
*/ */
function event_hook($event, &$bag, &$eventData, $addData = null) { function event_hook($event, &$bag, &$eventData, $addData = null) {
global $serendipity; global $serendipity;
$hooks = &$bag->get('event_hooks'); $hooks = &$bag->get('event_hooks');
if (isset($hooks[$event])) { if (isset($hooks[$event])) {
switch($event) { switch($event) {
@ -327,7 +327,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
break; break;
} }
break; break;
case 'frontend_saveComment': case 'frontend_saveComment':
// Check only, if no one else denied it before // Check only, if no one else denied it before
if (!is_array ( $eventData ) || serendipity_db_bool ( $eventData ['allow_comments'] )) { if (!is_array ( $eventData ) || serendipity_db_bool ( $eventData ['allow_comments'] )) {
@ -335,9 +335,11 @@ class serendipity_event_spamblock_bee extends serendipity_event
} }
return true; return true;
break; break;
case 'frontend_comment': case 'frontend_comment':
$this->printCommentEditExtras($eventData, $addData); $this->printCommentEditExtras($eventData, $addData);
break; break;
case 'frontend_footer': case 'frontend_footer':
// Comment header code only if in single article mode or contactform // Comment header code only if in single article mode or contactform
// If contact form is installed, display on any page not being the article list // If contact form is installed, display on any page not being the article list
@ -349,9 +351,11 @@ class serendipity_event_spamblock_bee extends serendipity_event
$this->printJsExtras(); $this->printJsExtras();
} }
break; break;
case 'css':
case 'css':
$this->printCss($eventData, $addData); $this->printCss($eventData, $addData);
break; break;
default: default:
return false; return false;
break; break;
@ -361,20 +365,20 @@ class serendipity_event_spamblock_bee extends serendipity_event
return false; return false;
} }
} }
/** /**
* Check if Honey Pot or Captcha have been filled correctly (or if any * Check if Honey Pot or Captcha have been filled correctly (or if any
* other indications for spam can be found). * other indications for spam can be found).
* *
* @param array $eventData * @param array $eventData
* @param array $addData * @param array $addData
* @return bool * @return bool
*/ */
function checkComment(&$eventData, &$addData) { function checkComment(&$eventData, &$addData) {
global $serendipity; global $serendipity;
if ("NORMAL" == $addData['type']) { // only supported for normal comments if ("NORMAL" == $addData['type']) { // only supported for normal comments
// Check for Honey Pot: // Check for Honey Pot:
$phone = $serendipity['POST']['phone']; $phone = $serendipity['POST']['phone'];
if ($this->useHoneyPot && (!empty($phone) || $phone == '0') ) { if ($this->useHoneyPot && (!empty($phone) || $phone == '0') ) {
@ -385,28 +389,28 @@ class serendipity_event_spamblock_bee extends serendipity_event
$eventData = array('allow_comments' => false); $eventData = array('allow_comments' => false);
return false; return false;
} }
// Check hidden Captcha // Check hidden Captcha
if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $this->hiddenCaptchaHandle) { if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $this->hiddenCaptchaHandle) {
$answer = trim(strtolower($serendipity['POST']['beecaptcha'])); $answer = trim(strtolower($serendipity['POST']['beecaptcha']));
$correctAnswer = $this->getCaptchaAnswer(); $correctAnswer = $this->getCaptchaAnswer();
$correctAnswer['answer'] = strtolower($correctAnswer['answer']); $correctAnswer['answer'] = strtolower($correctAnswer['answer']);
$isCorrect = false; $isCorrect = false;
// If provided answer is longer than 1000 characters and RegExp matching is on, // If provided answer is longer than 1000 characters and RegExp matching is on,
// reject comment for security reasons (minimize risk of ReDoS) // reject comment for security reasons (minimize risk of ReDoS)
if ($this->useRegularExpressions && mb_strlen($answer) > 1000) { if ($this->useRegularExpressions && mb_strlen($answer) > 1000) {
$this->processComment($this->hiddenCaptchaHandle, $eventData, $addData, PLUGIN_EVENT_SPAMBLOCK_BEE_ERROR_HCAPTCHA, "BEE HiddenCaptcha [ Captcha input too long ]"); $this->processComment($this->hiddenCaptchaHandle, $eventData, $addData, PLUGIN_EVENT_SPAMBLOCK_BEE_ERROR_HCAPTCHA, "BEE HiddenCaptcha [ Captcha input too long ]");
return false; return false;
} }
if ($this->captchaQuestionType == 'custom' && $this->useRegularExpressions) { if ($this->captchaQuestionType == 'custom' && $this->useRegularExpressions) {
// Sanitize regular expression and remove answer part // Sanitize regular expression and remove answer part
$pattern = preg_replace('/^\s*\/(.*)\/\s*[imsxeADSUXJu]*\s*$/s', '$1', $correctAnswer['pattern']); $pattern = preg_replace('/^\s*\/(.*)\/\s*[imsxeADSUXJu]*\s*$/s', '$1', $correctAnswer['pattern']);
// Try to match pattern with given answer // Try to match pattern with given answer
$match = @preg_match('/' . $pattern . '/si', $answer); $match = @preg_match('/' . $pattern . '/si', $answer);
// If pattern contains errors, fall back to basic string comparison // If pattern contains errors, fall back to basic string comparison
if ($match === false) { if ($match === false) {
$this->useRegularExpressions = false; $this->useRegularExpressions = false;
@ -414,17 +418,17 @@ class serendipity_event_spamblock_bee extends serendipity_event
$isCorrect = ($match === 1); $isCorrect = ($match === 1);
} }
} }
if ($this->captchaQuestionType != 'custom' || !$this->useRegularExpressions) { if ($this->captchaQuestionType != 'custom' || !$this->useRegularExpressions) {
$isCorrect = ($answer == $correctAnswer['answer']); $isCorrect = ($answer == $correctAnswer['answer']);
} }
// Also allow numbers as words // Also allow numbers as words
if (!$isCorrect && $this->captchaQuestionType == 'math') { if (!$isCorrect && $this->captchaQuestionType == 'math') {
$number = $this->generateNumberString($correctAnswer['answer']); $number = $this->generateNumberString($correctAnswer['answer']);
$isCorrect = ($answer == $number && $number != 'ERROR'); $isCorrect = ($answer == $number && $number != 'ERROR');
} }
if (!$isCorrect) { if (!$isCorrect) {
if (mb_strlen($answer) > 40) { if (mb_strlen($answer) > 40) {
$answer = mb_substr($answer, 0, 40) . '..'; $answer = mb_substr($answer, 0, 40) . '..';
@ -435,7 +439,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
} }
// AntiSpam check, the general spamblock supports, too: Only if spamblock is not installed. // AntiSpam check, the general spamblock supports, too: Only if spamblock is not installed.
if (!class_exists('serendipity_event_spamblock')) { if (!class_exists('serendipity_event_spamblock')) {
// Check for required fields. Don't log but tell the user about the fields. // Check for required fields. Don't log but tell the user about the fields.
$required_fields = $this->get_config('required_fields', ''); $required_fields = $this->get_config('required_fields', '');
if (!empty($required_fields)) { if (!empty($required_fields)) {
@ -450,7 +454,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
} }
} }
} }
// AntiSpam check, the general spamblock supports, too: Only if spamblock is not installed. // AntiSpam check, the general spamblock supports, too: Only if spamblock is not installed.
if (!class_exists('serendipity_event_spamblock')) { if (!class_exists('serendipity_event_spamblock')) {
@ -461,7 +465,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
$comment = str_replace($serendipity['blogTitle'], '', $addData['comment']); $comment = str_replace($serendipity['blogTitle'], '', $addData['comment']);
$comment = str_replace($eventData['title'], '', $comment); $comment = str_replace($eventData['title'], '', $comment);
// Now blog- and entry title was stripped from comment. // Now blog- and entry title was stripped from comment.
// Remove special letters, that might have been between them: // Remove special letters, that might have been between them:
$comment = trim(preg_replace('@[\s\-_:\(\)\|/]*@', '', $comment)); $comment = trim(preg_replace('@[\s\-_:\(\)\|/]*@', '', $comment));
// Now that we stripped blog and entry title: Do we have an empty comment? // Now that we stripped blog and entry title: Do we have an empty comment?
@ -470,33 +474,33 @@ class serendipity_event_spamblock_bee extends serendipity_event
return false; return false;
} }
} }
// This check loads from DB, so do it last! // This check loads from DB, so do it last!
// Check if we already have a comment with the same body. (it's a reload normaly) // Check if we already have a comment with the same body. (it's a reload normaly)
$spamHandle = $this->get_config('samebody', PLUGIN_EVENT_SPAMBLOCK_SWTCH_REJECT); $spamHandle = $this->get_config('samebody', PLUGIN_EVENT_SPAMBLOCK_SWTCH_REJECT);
if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF!=$spamHandle) { if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF!=$spamHandle) {
$query = "SELECT count(id) AS counter FROM {$serendipity['dbPrefix']}comments WHERE type = '" . $addData['type'] . "' AND body = '" . serendipity_db_escape_string($addData['comment']) . "'"; $query = "SELECT count(id) AS counter FROM {$serendipity['dbPrefix']}comments WHERE type = '" . $addData['type'] . "' AND body = '" . serendipity_db_escape_string($addData['comment']) . "'";
// This is a little different to the normal Spam Plugin: // This is a little different to the normal Spam Plugin:
// We allow the same comment, if it is a trackback, but never on the same article // We allow the same comment, if it is a trackback, but never on the same article
// (One article sending trackbacks to more than one local article) // (One article sending trackbacks to more than one local article)
if ($addData['type'] == 'PINGBACK' || $addData['type'] == 'TRACKBACK') { if ($addData['type'] == 'PINGBACK' || $addData['type'] == 'TRACKBACK') {
$query .= ' AND entry_id=' . $eventData['id']; $query .= ' AND entry_id=' . $eventData['id'];
} }
$row = serendipity_db_query($query, true); $row = serendipity_db_query($query, true);
if (is_array($row) && $row['counter'] > 0) { if (is_array($row) && $row['counter'] > 0) {
$this->processComment($spamHandle, $eventData, $addData, PLUGIN_EVENT_SPAMBLOCK_BEE_ERROR_BODY, "BEE Body already saved"); $this->processComment($spamHandle, $eventData, $addData, PLUGIN_EVENT_SPAMBLOCK_BEE_ERROR_BODY, "BEE Body already saved");
return false; return false;
} }
} }
} }
return true; return true;
} }
/** /**
* Reject or moderate a comment. Convenience function. * Reject or moderate a comment. Convenience function.
* *
* @param string $spamHandle * @param string $spamHandle
* @param array $eventData * @param array $eventData
* @param array $addData * @param array $addData
@ -512,10 +516,10 @@ class serendipity_event_spamblock_bee extends serendipity_event
$this->reject($eventData, $addData, $remoteResponse, $logResponse); $this->reject($eventData, $addData, $remoteResponse, $logResponse);
} }
} }
/** /**
* Reject a comment with optional log entry. * Reject a comment with optional log entry.
* *
* @param array $eventData * @param array $eventData
* @param array $addData * @param array $addData
* @param string $remoteResponse * @param string $remoteResponse
@ -523,17 +527,17 @@ class serendipity_event_spamblock_bee extends serendipity_event
*/ */
function reject(&$eventData, &$addData, $remoteResponse, $logResponse = NULL) { function reject(&$eventData, &$addData, $remoteResponse, $logResponse = NULL) {
global $serendipity; global $serendipity;
if (!empty($logResponse)) { if (!empty($logResponse)) {
$this->spamlog($eventData['id'], 'REJECTED', $logResponse, $addData); $this->spamlog($eventData['id'], 'REJECTED', $logResponse, $addData);
} }
$eventData = array('allow_comments' => false); $eventData = array('allow_comments' => false);
$serendipity['csuccess'] = 'false'; $serendipity['csuccess'] = 'false';
$serendipity['messagestack']['comments'][] = $remoteResponse; $serendipity['messagestack']['comments'][] = $remoteResponse;
$this->log(print_r($serendipity['messagestack'], true)); $this->log(print_r($serendipity['messagestack'], true));
} }
/** /**
* Moderate a comment with optional log entry * Moderate a comment with optional log entry
* @param array $eventData * @param array $eventData
@ -544,7 +548,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
*/ */
function moderate(&$eventData, &$addData, $remoteResponse, $logResponse = NULL) { function moderate(&$eventData, &$addData, $remoteResponse, $logResponse = NULL) {
global $serendipity; global $serendipity;
if (!empty($logResponse)) { if (!empty($logResponse)) {
$this->spamlog($eventData['id'], 'MODERATE', $logResponse, $addData); $this->spamlog($eventData['id'], 'MODERATE', $logResponse, $addData);
} }
@ -555,38 +559,38 @@ class serendipity_event_spamblock_bee extends serendipity_event
$this->log(print_r($serendipity['messagestack'], true)); $this->log(print_r($serendipity['messagestack'], true));
} }
/** /**
* Produce JSON string with the correct for fetching via Ajax. * Produce JSON string with the correct for fetching via Ajax.
* *
* @return string The generated JSON string * @return string The generated JSON string
*/ */
function produceCaptchaAnswerJson() { function produceCaptchaAnswerJson() {
$answer = $this->getCaptchaAnswer(); $answer = $this->getCaptchaAnswer();
$scrambleKey = rand(); $scrambleKey = rand();
if (!isset($answer['answer'])) { if (!isset($answer['answer'])) {
$answer = array('answer' => 'ERROR'); $answer = array('answer' => 'ERROR');
} else { } else {
$answer['answer'] = rawurlencode($this->xorScramble($answer['answer'], $scrambleKey)); $answer['answer'] = rawurlencode($this->xorScramble($answer['answer'], $scrambleKey));
$answer['scrambleKey'] = $scrambleKey; $answer['scrambleKey'] = $scrambleKey;
} }
return json_encode($answer); return json_encode($answer);
} }
/** /**
* Write the Honey Pot and Captcha field to the output buffer. * Write the Honey Pot and Captcha field to the output buffer.
* *
* @param array $eventData * @param array $eventData
* @param array $addData * @param array $addData
*/ */
function printCommentEditExtras(&$eventData, &$addData) { function printCommentEditExtras(&$eventData, &$addData) {
global $serendipity; global $serendipity;
// Don't put extras on admin menu. They are not working there: ...or other backend forms like guestbook // Don't put extras on admin menu. They are not working there: ...or other backend forms like guestbook
if ((isset($eventData['GET']['action']) && $eventData['GET']['action']=='admin') || (int)$serendipity['serendipityUserlevel'] >= (int)USERLEVEL_ADMIN) return; if ((isset($eventData['GET']['action']) && $eventData['GET']['action']=='admin') || (int)$serendipity['serendipityUserlevel'] >= (int)USERLEVEL_ADMIN) return;
// Honeypot // Honeypot
if (serendipity_db_bool($this->useHoneyPot)) { if (serendipity_db_bool($this->useHoneyPot)) {
echo '<div id="serendipity_comment_phone" class="serendipity_commentDirection comment_phone_input">' . "\n"; echo '<div id="serendipity_comment_phone" class="serendipity_commentDirection comment_phone_input">' . "\n";
@ -598,12 +602,12 @@ class serendipity_event_spamblock_bee extends serendipity_event
// Captcha // Captcha
if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $this->hiddenCaptchaHandle) { if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $this->hiddenCaptchaHandle) {
$question = $this->generateCaptchaQuestion(); $question = $this->generateCaptchaQuestion();
echo '<div id="serendipity_comment_beecaptcha" class="form_field">' . "\n"; echo '<div id="serendipity_comment_beecaptcha" class="form_field">' . "\n";
echo ' <label for="bee_captcha">'. $question. '</label>' . "\n"; echo ' <label for="bee_captcha">'. $question. '</label>' . "\n";
echo ' <input class="" type="text" id="bee_captcha" name="serendipity[beecaptcha]" size="10" value="" placeholder=""/>' . "\n"; echo ' <input class="" type="text" id="bee_captcha" name="serendipity[beecaptcha]" size="10" value="" placeholder=""/>' . "\n";
echo "</div>\n"; echo "</div>\n";
if ($this->answerRetrievalMethod == 'smarty' || $this->answerRetrievalMethod == 'smarty_enc') { if ($this->answerRetrievalMethod == 'smarty' || $this->answerRetrievalMethod == 'smarty_enc') {
$answer = $this->getCaptchaAnswer(); $answer = $this->getCaptchaAnswer();
if ($this->answerRetrievalMethod == 'smarty_enc') { if ($this->answerRetrievalMethod == 'smarty_enc') {
@ -611,12 +615,12 @@ class serendipity_event_spamblock_bee extends serendipity_event
$answer['answer'] = $this->xorScramble($answer['answer'], $scrambleKey); $answer['answer'] = $this->xorScramble($answer['answer'], $scrambleKey);
$serendipity['smarty']->assign('beeCaptchaScrambleKey', $scrambleKey); $serendipity['smarty']->assign('beeCaptchaScrambleKey', $scrambleKey);
} }
$serendipity['smarty']->assign('beeCaptchaAnswer', $answer['answer']); $serendipity['smarty']->assign('beeCaptchaAnswer', $answer['answer']);
} }
} }
} }
/** /**
* If retrieval method != 'smarty' and the hidden Captcha is turned on, * If retrieval method != 'smarty' and the hidden Captcha is turned on,
* print the needed JavaScript for filling out and hiding the Captcha to the buffer. * print the needed JavaScript for filling out and hiding the Captcha to the buffer.
@ -625,16 +629,16 @@ class serendipity_event_spamblock_bee extends serendipity_event
if ($this->answerRetrievalMethod == 'smarty' || $this->answerRetrievalMethod == 'smarty_enc') { if ($this->answerRetrievalMethod == 'smarty' || $this->answerRetrievalMethod == 'smarty_enc') {
return; return;
} }
global $serendipity; global $serendipity;
if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $this->hiddenCaptchaHandle) { if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $this->hiddenCaptchaHandle) {
$path = $this->path = $this->get_config('plugin_path', $serendipity['serendipityHTTPPath'] . 'plugins/serendipity_event_spamblock_bee/'); $path = $this->path = $this->get_config('plugin_path', $serendipity['serendipityHTTPPath'] . 'plugins/serendipity_event_spamblock_bee/');
$answer = $this->getCaptchaAnswer(); $answer = $this->getCaptchaAnswer();
$answer = $answer['answer']; $answer = $answer['answer'];
$jsProperties = array('method' => $this->answerRetrievalMethod); $jsProperties = array('method' => $this->answerRetrievalMethod);
if ($this->answerRetrievalMethod == 'json') { if ($this->answerRetrievalMethod == 'json') {
$jsProperties['url'] = $serendipity['baseURL'] . 'index.php/plugin/spamblockbeecaptcha'; $jsProperties['url'] = $serendipity['baseURL'] . 'index.php/plugin/spamblockbeecaptcha';
echo '<script>var spamBeeData = ' . json_encode($jsProperties) . ';</script>' . "\n"; echo '<script>var spamBeeData = ' . json_encode($jsProperties) . ';</script>' . "\n";
@ -645,53 +649,53 @@ class serendipity_event_spamblock_bee extends serendipity_event
$jsProperties['answer'] = is_numeric($answer) ? $answer : trim($answer); $jsProperties['answer'] = is_numeric($answer) ? $answer : trim($answer);
} }
unset($jsProperties['pattern']); unset($jsProperties['pattern']);
if ($this->answerRetrievalMethod == 'default') { if ($this->answerRetrievalMethod == 'default') {
// Do some weird obfuscation stuff to the JS code // Do some weird obfuscation stuff to the JS code
$spamBeeVar = $this->generateUniqueVarName(array()); $spamBeeVar = $this->generateUniqueVarName(array());
// Shuffle array but preserve keys // Shuffle array but preserve keys
$jsPropertiesKeys = array_keys($jsProperties); $jsPropertiesKeys = array_keys($jsProperties);
shuffle($jsPropertiesKeys); shuffle($jsPropertiesKeys);
$jsProperties = array_merge(array_flip($jsPropertiesKeys) , $jsProperties); $jsProperties = array_merge(array_flip($jsPropertiesKeys) , $jsProperties);
echo '<script>var spamBeeData = function() { var ' . $spamBeeVar . ' = {};'; echo '<script>var spamBeeData = function() { var ' . $spamBeeVar . ' = {};';
$jsVars = array(); $jsVars = array();
$existingKeys = array($spamBeeVar); $existingKeys = array($spamBeeVar);
foreach($jsProperties as $property => $value) { foreach($jsProperties as $property => $value) {
$varName = $this->generateUniqueVarName($existingKeys); $varName = $this->generateUniqueVarName($existingKeys);
$jsVars[$varName] = $property; $jsVars[$varName] = $property;
$existingKeys[] = $varName; $existingKeys[] = $varName;
// URL encode all characters to make values appear almost equal // URL encode all characters to make values appear almost equal
$encVal = ''; $encVal = '';
$valLength = mb_strlen($value); $valLength = mb_strlen($value);
for ($i = 0; $i < $valLength; ++$i) { for ($i = 0; $i < $valLength; ++$i) {
$encVal .= '%' . bin2hex(mb_substr($value, $i, 1)); $encVal .= '%' . bin2hex(mb_substr($value, $i, 1));
} }
echo 'var ' . $varName . " = unescape('" . $encVal . "');"; echo 'var ' . $varName . " = unescape('" . $encVal . "');";
} }
foreach ($jsVars as $var => $value) { foreach ($jsVars as $var => $value) {
echo $spamBeeVar . "['" . $value . "'] = " . $var . ';'; echo $spamBeeVar . "['" . $value . "'] = " . $var . ';';
} }
echo 'return ' . $spamBeeVar . '; }();'; echo 'return ' . $spamBeeVar . '; }();';
echo "</script>\n"; echo "</script>\n";
} }
echo '<script src="' . $path . 'serendipity_event_spamblock_bee.js"></script>'; echo '<script src="' . $path . 'serendipity_event_spamblock_bee.js"></script>' . "\n";
} }
} }
/** /**
* Generate a unique random variable name. Used for generating obfuscated * Generate a unique random variable name. Used for generating obfuscated
* JS code. To make sure, the name is really unique, pass an array of all * JS code. To make sure, the name is really unique, pass an array of all
* variable names already existing to this function. * variable names already existing to this function.
* Returns an empty string if no unique variable name could be generated. * Returns an empty string if no unique variable name could be generated.
* *
* @param array $existingVarNames * @param array $existingVarNames
* @param int $length * @param int $length
* @return string * @return string
@ -699,11 +703,11 @@ class serendipity_event_spamblock_bee extends serendipity_event
function generateUniqueVarName($existingVarNames, $length = 5) { function generateUniqueVarName($existingVarNames, $length = 5) {
$varName = ''; $varName = '';
$pool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_'; $pool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_';
$attempts = 0; $attempts = 0;
for ($i = 0; $i < $length; ++$i) { for ($i = 0; $i < $length; ++$i) {
$varName .= $pool[rand(0, strlen($pool) - 1)]; $varName .= $pool[rand(0, strlen($pool) - 1)];
// If variable name has been generated, but is not unique, start over again // If variable name has been generated, but is not unique, start over again
if ($i == ($length - 1) && in_array($varName, $existingVarNames)) { if ($i == ($length - 1) && in_array($varName, $existingVarNames)) {
// If we already have 10 attempts, give up and return empty string (should not happen) // If we already have 10 attempts, give up and return empty string (should not happen)
@ -714,14 +718,14 @@ class serendipity_event_spamblock_bee extends serendipity_event
++$attempts; ++$attempts;
} }
} }
return $varName; return $varName;
} }
/** /**
* If retrieval method != 'json' and the hidden Captcha is enabled, print * If retrieval method != 'json' and the hidden Captcha is enabled, print
* the needed CSS for hiding it to the output buffer. * the needed CSS for hiding it to the output buffer.
* *
* @param array $eventData * @param array $eventData
* @param array $addData * @param array $addData
*/ */
@ -729,7 +733,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
if ($this->answerRetrievalMethod == 'smarty' || $this->answerRetrievalMethod == 'smarty_enc') { if ($this->answerRetrievalMethod == 'smarty' || $this->answerRetrievalMethod == 'smarty_enc') {
return; return;
} }
global $serendipity; global $serendipity;
// Hide and reveal classes by @yellowled used be the RSS chooser: // Hide and reveal classes by @yellowled used be the RSS chooser:
@ -745,9 +749,9 @@ class serendipity_event_spamblock_bee extends serendipity_event
position: absolute; position: absolute;
width: 1px; width: 1px;
} }
<?php <?php
} }
if (!(strpos($eventData, '.comment_phone_input'))) { if (!(strpos($eventData, '.comment_phone_input'))) {
?> ?>
.comment_phone_input { .comment_phone_input {
@ -758,7 +762,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
<?php <?php
} }
} }
function hashString( $what ) { function hashString( $what ) {
$installation_secret = $this->get_config('installation_secret'); $installation_secret = $this->get_config('installation_secret');
if (empty($installation_secret)) { if (empty($installation_secret)) {
@ -767,10 +771,10 @@ class serendipity_event_spamblock_bee extends serendipity_event
} }
return md5($installation_secret . ':' . $what); return md5($installation_secret . ':' . $what);
} }
/** /**
* Generate the question for the Captcha and save the answer to the session. * Generate the question for the Captcha and save the answer to the session.
* *
* @return string the question * @return string the question
*/ */
function generateCaptchaQuestion() { function generateCaptchaQuestion() {
@ -785,22 +789,22 @@ class serendipity_event_spamblock_bee extends serendipity_event
return $question['question']; return $question['question'];
} }
} }
if ($this->captchaQuestionType == 'math') { if ($this->captchaQuestionType == 'math') {
$captchaData = $this->generateCaptchaMathProblem(); $captchaData = $this->generateCaptchaMathProblem();
$this->setCaptchaAnswer($captchaData['answer']); $this->setCaptchaAnswer($captchaData['answer']);
$method = PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_PLUS; $method = PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_PLUS;
if ($captchaData['operator'] == '-') { if ($captchaData['operator'] == '-') {
$method = PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_MINUS; $method = PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_MINUS;
} }
return PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_QUEST . ' ' . return PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_QUEST . ' ' .
$this->generateNumberString($captchaData['n1']) . " " . $this->generateNumberString($captchaData['n1']) . " " .
$method . " " . $this->generateNumberString($captchaData['n2']) . '?'; $method . " " . $this->generateNumberString($captchaData['n2']) . '?';
} }
} }
/** /**
* Get correct answer for current Captcha question. * Get correct answer for current Captcha question.
* This method returns an array with one or two indices: * This method returns an array with one or two indices:
@ -809,37 +813,37 @@ class serendipity_event_spamblock_bee extends serendipity_event
* 'pattern' => the pattern for matching the answer (only present * 'pattern' => the pattern for matching the answer (only present
* when regExp matching is turned on) * when regExp matching is turned on)
* ) * )
* *
* @return array * @return array
*/ */
function getCaptchaAnswer() { function getCaptchaAnswer() {
if (!isset($this->captchaAnswer['answer']) && isset($_SESSION['spamblockbee']['captcha'])) { if (!isset($this->captchaAnswer['answer']) && isset($_SESSION['spamblockbee']['captcha'])) {
$this->captchaAnswer = $_SESSION['spamblockbee']['captcha']; $this->captchaAnswer = $_SESSION['spamblockbee']['captcha'];
} }
// If for some reason RegExp matching is on, but no pattern is present, // If for some reason RegExp matching is on, but no pattern is present,
// turn off RegExp matching // turn off RegExp matching
if ($this->useRegularExpressions && !isset($this->captchaAnswer['pattern'])) { if ($this->useRegularExpressions && !isset($this->captchaAnswer['pattern'])) {
$this->useRegularExpressions = false; $this->useRegularExpressions = false;
} }
return $this->captchaAnswer; return $this->captchaAnswer;
} }
/** /**
* Save the answer for the Captcha question. * Save the answer for the Captcha question.
* Call this method when you have changed the question. * Call this method when you have changed the question.
* If RegExp matching is turned on, pass a string in the format /pattern/:answer. * If RegExp matching is turned on, pass a string in the format /pattern/:answer.
* *
* @param string $answer * @param string $answer
*/ */
function setCaptchaAnswer($answer) { function setCaptchaAnswer($answer) {
$answer = array('answer' => $answer); $answer = array('answer' => $answer);
// Split answer into array if RegExp matching is on // Split answer into array if RegExp matching is on
if ($this->captchaQuestionType == 'custom' && $this->useRegularExpressions) { if ($this->captchaQuestionType == 'custom' && $this->useRegularExpressions) {
$delimiterIndex = strrpos($answer['answer'], ':'); $delimiterIndex = strrpos($answer['answer'], ':');
if ($delimiterIndex !== false) { if ($delimiterIndex !== false) {
$answer = array( $answer = array(
'pattern' => substr($answer['answer'], 0, $delimiterIndex), 'pattern' => substr($answer['answer'], 0, $delimiterIndex),
@ -850,20 +854,20 @@ class serendipity_event_spamblock_bee extends serendipity_event
$this->useRegularExpressions = false; $this->useRegularExpressions = false;
} }
} }
$this->captchaAnswer = $answer; $this->captchaAnswer = $answer;
$_SESSION['spamblockbee']['captcha'] = $this->captchaAnswer; $_SESSION['spamblockbee']['captcha'] = $this->captchaAnswer;
} }
/** /**
* Generate a simple arithmetic problem for use in the Captcha. * Generate a simple arithmetic problem for use in the Captcha.
* Returns an array containing the operator, the operands and the result. * Returns an array containing the operator, the operands and the result.
* *
* @return array * @return array
*/ */
function generateCaptchaMathProblem() { function generateCaptchaMathProblem() {
$result = array(); $result = array();
$number1 = rand(0,9); $number1 = rand(0,9);
$number2 = rand(0,9); $number2 = rand(0,9);
if (($number1 + $number2) > 10 ) { if (($number1 + $number2) > 10 ) {
@ -878,7 +882,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
$result['n2'] = $number1; $result['n2'] = $number1;
$result['n1'] = $number2; $result['n1'] = $number2;
$result['answer'] = $number2 - $number1; $result['answer'] = $number2 - $number1;
} }
} else { } else {
// Add them // Add them
$result['operator'] = '+'; $result['operator'] = '+';
@ -886,13 +890,13 @@ class serendipity_event_spamblock_bee extends serendipity_event
$result['n2'] = $number2; $result['n2'] = $number2;
$result['answer'] = $number1 + $number2; $result['answer'] = $number1 + $number2;
} }
return $result; return $result;
} }
/** /**
* Turn numbers between 0 and 10 into words. * Turn numbers between 0 and 10 into words.
* *
* @param int $number * @param int $number
* @return string * @return string
*/ */
@ -913,7 +917,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
default: return "ERROR"; default: return "ERROR";
} }
} }
/** /**
* Select a random question from the list of custom questions. * Select a random question from the list of custom questions.
* Returns an array with to indices: * Returns an array with to indices:
@ -921,7 +925,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
* 'question' => the selected question * 'question' => the selected question
* 'answer' => the answer for that question * 'answer' => the answer for that question
* ) * )
* *
* @return array * @return array
*/ */
function selectRandomCustomCaptchaQuestion() { function selectRandomCustomCaptchaQuestion() {
@ -930,31 +934,31 @@ class serendipity_event_spamblock_bee extends serendipity_event
if (empty($questions) || empty($answers)) { if (empty($questions) || empty($answers)) {
return null; return null;
} }
$questions = preg_split('/(?:\r?\n|\r)/', $questions); $questions = preg_split('/(?:\r?\n|\r)/', $questions);
$answers = preg_split('/(?:\r?\n|\r)/', $answers); $answers = preg_split('/(?:\r?\n|\r)/', $answers);
// ignore questions without answer // ignore questions without answer
if (count($questions) > count($answers)) { if (count($questions) > count($answers)) {
array_splice($questions, count($answers)); array_splice($questions, count($answers));
} }
// if no questions left // if no questions left
if (!count($questions)) { if (!count($questions)) {
return null; return null;
} }
$questionIndex = rand(0, count($questions) - 1); $questionIndex = rand(0, count($questions) - 1);
return array( return array(
'question' => trim($questions[$questionIndex]), 'question' => trim($questions[$questionIndex]),
'answer' => trim($answers[$questionIndex]) 'answer' => trim($answers[$questionIndex])
); );
} }
/** /**
* Scramble a UTF-8 string with a simple XOR cipher in order * Scramble a UTF-8 string with a simple XOR cipher in order
* to perform string obfuscation. * to perform string obfuscation.
* *
* @param string $string * @param string $string
* @param int $key * @param int $key
* @return string * @return string
@ -962,45 +966,45 @@ class serendipity_event_spamblock_bee extends serendipity_event
function xorScramble($string, $key) { function xorScramble($string, $key) {
$scrambled = ''; $scrambled = '';
$length = mb_strlen($string, 'UTF-8'); $length = mb_strlen($string, 'UTF-8');
for ($i = 0; $i < $length; ++$i) { for ($i = 0; $i < $length; ++$i) {
$chr = mb_substr($string, $i, 1, 'UTF-8'); $chr = mb_substr($string, $i, 1, 'UTF-8');
$ord = $this->ordUtf8($chr); $ord = $this->ordUtf8($chr);
$scrambled .= $this->chrUtf8($ord ^ $key); $scrambled .= $this->chrUtf8($ord ^ $key);
} }
return $scrambled; return $scrambled;
} }
/** /**
* Multi-byte safe UTF-8 version of chr() * Multi-byte safe UTF-8 version of chr()
* Thanks to http://pastebin.com/fmiSnNin * Thanks to http://pastebin.com/fmiSnNin
* *
* @param int $ord * @param int $ord
* @return string * @return string
*/ */
function chrUtf8($ord) function chrUtf8($ord)
{ {
return mb_convert_encoding(pack('n', $ord) , 'UTF-8', 'UTF-16BE'); return mb_convert_encoding(pack('n', $ord) , 'UTF-8', 'UTF-16BE');
} }
/** /**
* Multi-byte safe UTF-8 version of ord(). * Multi-byte safe UTF-8 version of ord().
* Thanks to http://pastebin.com/fmiSnNin * Thanks to http://pastebin.com/fmiSnNin
* Returns -1 on error. * Returns -1 on error.
* *
* @param string $chr * @param string $chr
* @return int * @return int
*/ */
function ordUtf8($chr) function ordUtf8($chr)
{ {
// Return value of ord() if only single-byte // Return value of ord() if only single-byte
if (strlen($chr) == 1) { if (strlen($chr) == 1) {
return ord($chr); return ord($chr);
} }
$codePoint = ord($chr[0]); $codePoint = ord($chr[0]);
if ($codePoint <= 0x7f) { if ($codePoint <= 0x7f) {
return $codePoint; return $codePoint;
} else if ($codePoint < 0xc2) { } else if ($codePoint < 0xc2) {
@ -1015,7 +1019,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
return -1; return -1;
} }
} }
/** /**
* Log spam to file * Log spam to file
* @param string $message * @param string $message
@ -1027,7 +1031,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
fflush($fp); fflush($fp);
fclose($fp); fclose($fp);
} }
/** /**
* Log spam to database * Log spam to database
* @param string $id * @param string $id
@ -1037,10 +1041,10 @@ class serendipity_event_spamblock_bee extends serendipity_event
*/ */
function spamlog($id, $switch, $reason, $addData) { function spamlog($id, $switch, $reason, $addData) {
global $serendipity; global $serendipity;
$method = $this->get_config('spamlogtype', 'none'); $method = $this->get_config('spamlogtype', 'none');
$logfile = $this->get_config('spamlogfile', $serendipity['serendipityPath'] . 'spamblock.log'); $logfile = $this->get_config('spamlogfile', $serendipity['serendipityPath'] . 'spamblock.log');
switch($method) { switch($method) {
case 'file': case 'file':
if (empty($logfile)) { if (empty($logfile)) {
@ -1098,5 +1102,5 @@ class serendipity_event_spamblock_bee extends serendipity_event
serendipity_db_schema_import($q); serendipity_db_schema_import($q);
break; break;
} }
} }
} }

View file

@ -1,4 +1,5 @@
<?php <?php
// Probe for a language include with constants. Still include defines later on, if some constants were missing // Probe for a language include with constants. Still include defines later on, if some constants were missing
$probelang = dirname(__FILE__) . '/' . $serendipity['charset'] . 'lang_' . $serendipity['lang'] . '.inc.php'; $probelang = dirname(__FILE__) . '/' . $serendipity['charset'] . 'lang_' . $serendipity['lang'] . '.inc.php';
if (file_exists($probelang)) { if (file_exists($probelang)) {
@ -9,9 +10,8 @@ include dirname(__FILE__) . '/version.inc.php';
class serendipity_plugin_spamblock_bee extends serendipity_plugin { class serendipity_plugin_spamblock_bee extends serendipity_plugin {
var $title = PLUGIN_SPAMBLOCK_BEE_TITLE; var $title = PLUGIN_SPAMBLOCK_BEE_TITLE;
var $cache_file = null; var $cache_file = null;
function introspect(&$propbag) { function introspect(&$propbag) {
$this->title = $this->get_config('title', $this->title); $this->title = $this->get_config('title', $this->title);
@ -26,14 +26,14 @@ class serendipity_plugin_spamblock_bee extends serendipity_plugin {
)); ));
$propbag->add('version', PLUGIN_SPAMBLOCK_BEE_VERSION); // setup via version.inc.php $propbag->add('version', PLUGIN_SPAMBLOCK_BEE_VERSION); // setup via version.inc.php
$propbag->add('groups', array('STATISTICS')); $propbag->add('groups', array('STATISTICS'));
$configuration = array('title', 'db_search_pattern', 'days', 'loggedin_only', 'cachemin'); $configuration = array('title', 'db_search_pattern', 'days', 'loggedin_only', 'cachemin');
$propbag->add('configuration', $configuration ); $propbag->add('configuration', $configuration );
} }
function introspect_config_item($name, &$propbag) { function introspect_config_item($name, &$propbag) {
global $serendipity; global $serendipity;
@ -49,13 +49,13 @@ class serendipity_plugin_spamblock_bee extends serendipity_plugin {
$propbag->add('name', PLUGIN_SPAMBLOCK_BEE_DAYS); $propbag->add('name', PLUGIN_SPAMBLOCK_BEE_DAYS);
$propbag->add('description', PLUGIN_SPAMBLOCK_BEE_DAYS_DESC); $propbag->add('description', PLUGIN_SPAMBLOCK_BEE_DAYS_DESC);
$propbag->add('default','1,7,30'); $propbag->add('default','1,7,30');
break; break;
case 'db_search_pattern': case 'db_search_pattern':
$propbag->add('type', 'text'); $propbag->add('type', 'text');
$propbag->add('name', PLUGIN_SPAMBLOCK_BEE_DBSEARCHES); $propbag->add('name', PLUGIN_SPAMBLOCK_BEE_DBSEARCHES);
$propbag->add('description', PLUGIN_SPAMBLOCK_BEE_DBSEARCHES_DESC); $propbag->add('description', PLUGIN_SPAMBLOCK_BEE_DBSEARCHES_DESC);
$propbag->add('rows', 4); $propbag->add('rows', 4);
$propbag->add('default', $propbag->add('default',
'Honeypot:BEE Honeypot% 'Honeypot:BEE Honeypot%
HiddenCaptcha:BEE HiddenCaptcha% HiddenCaptcha:BEE HiddenCaptcha%
Bayes:%Bayes%' Bayes:%Bayes%'
@ -65,8 +65,8 @@ Bayes:%Bayes%'
$propbag->add('type', 'string'); $propbag->add('type', 'string');
$propbag->add('name', PLUGIN_SPAMBLOCK_BEE_CACHEMINS); $propbag->add('name', PLUGIN_SPAMBLOCK_BEE_CACHEMINS);
$propbag->add('description', PLUGIN_SPAMBLOCK_BEE_CACHEMINS_DESC); $propbag->add('description', PLUGIN_SPAMBLOCK_BEE_CACHEMINS_DESC);
$propbag->add('default','10'); // 10min $propbag->add('default', '10'); // 10min
break; break;
case 'loggedin_only': case 'loggedin_only':
$propbag->add('type', 'boolean'); $propbag->add('type', 'boolean');
$propbag->add('name', PLUGIN_SPAMBLOCK_BEE_LOGGEDIN); $propbag->add('name', PLUGIN_SPAMBLOCK_BEE_LOGGEDIN);
@ -78,47 +78,47 @@ Bayes:%Bayes%'
} }
return TRUE; return TRUE;
} }
function generate_content(&$title) { function generate_content(&$title) {
global $serendipity; global $serendipity;
if (serendipity_db_bool($this->get_config('loggedin_only', TRUE))) { if (serendipity_db_bool($this->get_config('loggedin_only', TRUE))) {
// show content if logged on only // show content if logged on only
if ($_SESSION['serendipityAuthedUser'] != true) return; if ($_SESSION['serendipityAuthedUser'] != true) return;
} }
$title = $this->get_config('title', $this->title); $title = $this->get_config('title', $this->title);
$statsCached = $this->loadCachedStats(); $statsCached = $this->loadCachedStats();
$stats = array(); $stats = array();
if (!empty($statsCached)) { if (!empty($statsCached)) {
$stats = $statsCached; $stats = $statsCached;
} }
$days = explode(',', $this->get_config('days')); $days = explode(',', $this->get_config('days'));
$searches = preg_split('/(?:\r?\n|\r)/', $this->get_config('db_search_pattern')); $searches = preg_split('/(?:\r?\n|\r)/', $this->get_config('db_search_pattern'));
$todayAtMidnight = mktime(0,0,0, date("n"), date("j"), date("Y")); $todayAtMidnight = mktime(0,0,0, date("n"), date("j"), date("Y"));
$timestampDay = 60 * 60 * 24; $timestampDay = 60 * 60 * 24;
$statsString = ''; $statsString = '';
foreach ($days as $day) { foreach ($days as $day) {
if (!isset($stats[$day])) { if (!isset($stats[$day])) {
$stats[$day] = array(); $stats[$day] = array();
} }
$timestamp = $todayAtMidnight - ($timestampDay * (trim($day) -1)); $timestamp = $todayAtMidnight - ($timestampDay * (trim($day) -1));
if ($day==1) { if ($day==1) {
$statsString .= '<b>' . PLUGIN_SPAMBLOCK_BEE_TODAY . '</b> <br>'; $statsString .= '<b>' . PLUGIN_SPAMBLOCK_BEE_TODAY . '</b> <br>';
} else { } else {
$statsString .= '<b>' . sprintf(PLUGIN_SPAMBLOCK_BEE_LAST_X_DAYS, $day) . '</b><br>'; $statsString .= '<b>' . sprintf(PLUGIN_SPAMBLOCK_BEE_LAST_X_DAYS, $day) . '</b><br>';
} }
foreach ($searches as $search) { foreach ($searches as $search) {
$singleSearch = explode(':', $search, 2); $singleSearch = explode(':', $search, 2);
$singleSearch[0] = trim($singleSearch[0]); $singleSearch[0] = trim($singleSearch[0]);
$singleSearch[1] = trim($singleSearch[1]); $singleSearch[1] = trim($singleSearch[1]);
if (empty($statsCached)) { if (empty($statsCached)) {
$sql = "SELECT COUNT(*) as total FROM {$serendipity['dbPrefix']}spamblocklog WHERE reason like '%s' and timestamp>%d;"; $sql = "SELECT COUNT(*) as total FROM {$serendipity['dbPrefix']}spamblocklog WHERE reason like '%s' and timestamp>%d;";
$singleSql = sprintf($sql, serendipity_db_escape_string($singleSearch[1]), $timestamp); $singleSql = sprintf($sql, serendipity_db_escape_string($singleSearch[1]), $timestamp);
@ -129,21 +129,21 @@ Bayes:%Bayes%'
$stats[$day][$singleSearch[1]] = 0; $stats[$day][$singleSearch[1]] = 0;
} }
} }
$searchResult = isset($stats[$day][$singleSearch[1]]) ? $stats[$day][$singleSearch[1]] : 0; $searchResult = isset($stats[$day][$singleSearch[1]]) ? $stats[$day][$singleSearch[1]] : 0;
if ($searchResult) { if ($searchResult) {
$statsString .= "{$singleSearch[0]}: {$searchResult}<br>"; $statsString .= "{$singleSearch[0]}: {$searchResult}<br>";
} }
} }
} }
if (empty($statsCached)) { if (empty($statsCached)) {
$this->cacheStats($stats); $this->cacheStats($stats);
} }
echo $statsString; echo $statsString;
} }
function loadCachedStats() { function loadCachedStats() {
$cacheFile = $this->getCacheFilename(); $cacheFile = $this->getCacheFilename();
$cachesecs = $this->get_config('cachemin', '10') * 60; $cachesecs = $this->get_config('cachemin', '10') * 60;
@ -159,6 +159,7 @@ Bayes:%Bayes%'
} }
return array(); return array();
} }
function cacheStats($stats) { function cacheStats($stats) {
$stats = serialize($stats); $stats = serialize($stats);
$cacheFile = $this->getCacheFilename(); $cacheFile = $this->getCacheFilename();
@ -166,7 +167,7 @@ Bayes:%Bayes%'
fputs($fh, $stats, strlen($stats)); fputs($fh, $stats, strlen($stats));
fclose($fh); fclose($fh);
} }
/** /**
* Returns the cache file name * Returns the cache file name
*/ */
@ -177,12 +178,12 @@ Bayes:%Bayes%'
} }
return $this->cache_file; return $this->cache_file;
} }
function cleanup() { function cleanup() {
$cacheFile = $this->getCacheFilename(); $cacheFile = $this->getCacheFilename();
if (file_exists($cacheFile)) { if (file_exists($cacheFile)) {
unlink($cacheFile); unlink($cacheFile);
} }
} }
} }

View file

@ -1,2 +1,2 @@
<?php <?php
@define('PLUGIN_SPAMBLOCK_BEE_VERSION', '1.2.8'); @define('PLUGIN_SPAMBLOCK_BEE_VERSION', '1.2.9');