| [ Index ] |
|
Code source de Serendipity 1.2 |
1 <?php # $Id: serendipity_event_spamblock.php 1845 2007-08-13 08:03:44Z garvinhicking $ 2 3 4 if (IN_serendipity !== true) { 5 die ("Don't hack!"); 6 } 7 8 // Probe for a language include with constants. Still include defines later on, if some constants were missing 9 $probelang = dirname(__FILE__) . '/' . $serendipity['charset'] . 'lang_' . $serendipity['lang'] . '.inc.php'; 10 if (file_exists($probelang)) { 11 include $probelang; 12 } 13 14 include dirname(__FILE__) . '/lang_en.inc.php'; 15 16 /* BC - TODO: Remove for 0.8 final */ 17 if (!function_exists('serendipity_serverOffsetHour')) { 18 function serendipity_serverOffsetHour() { 19 return time(); 20 } 21 } 22 23 class serendipity_event_spamblock extends serendipity_event 24 { 25 var $filter_defaults; 26 27 function introspect(&$propbag) 28 { 29 global $serendipity; 30 31 $this->title = PLUGIN_EVENT_SPAMBLOCK_TITLE; 32 33 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_TITLE); 34 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_DESC); 35 $propbag->add('stackable', false); 36 $propbag->add('author', 'Garvin Hicking, Sebastian Nohn'); 37 $propbag->add('requirements', array( 38 'serendipity' => '0.8', 39 'smarty' => '2.6.7', 40 'php' => '4.1.0' 41 )); 42 $propbag->add('version', '1.68'); 43 $propbag->add('event_hooks', array( 44 'frontend_saveComment' => true, 45 'external_plugin' => true, 46 'frontend_comment' => true, 47 'fetchcomments' => true, 48 'backend_comments_top' => true, 49 'backend_view_comment' => true 50 )); 51 $propbag->add('configuration', array( 52 'killswitch', 53 'hide_for_authors', 54 'bodyclone', 55 'entrytitle', 56 'ipflood', 57 'csrf', 58 'captchas', 59 'captchas_ttl', 60 'captcha_color', 61 'forcemoderation', 62 'forcemoderation_treat', 63 'forcemoderationt', 64 'forcemoderationt_treat', 65 'disable_api_comments', 66 'trackback_check_url', 67 'links_moderate', 68 'links_reject', 69 'contentfilter_activate', 70 'contentfilter_urls', 71 'contentfilter_authors', 72 'contentfilter_words', 73 'contentfilter_emails', 74 'bloggdeblacklist', 75 'akismet', 76 'akismet_filter', 77 'hide_email', 78 'checkmail', 79 'required_fields', 80 'automagic_htaccess', 81 'logtype', 82 'logfile')); 83 $propbag->add('groups', array('ANTISPAM')); 84 85 $this->filter_defaults = array( 86 'authors' => 'casino;phentermine;credit;loans;poker', 87 'emails' => '', 88 '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', 89 'words' => 'very good site!;Real good stuff!' 90 ); 91 } 92 93 function introspect_config_item($name, &$propbag) 94 { 95 global $serendipity; 96 97 switch($name) { 98 case 'disable_api_comments': 99 $propbag->add('type', 'radio'); 100 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_API_COMMENTS); 101 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_API_COMMENTS_DESC); 102 $propbag->add('default', 'none'); 103 $propbag->add('radio', array( 104 'value' => array('moderate', 'reject', 'none'), 105 'desc' => array(PLUGIN_EVENT_SPAMBLOCK_API_MODERATE, PLUGIN_EVENT_SPAMBLOCK_API_REJECT, NONE) 106 )); 107 $propbag->add('radio_per_row', '1'); 108 109 break; 110 111 case 'trackback_check_url': 112 $propbag->add('type', 'boolean'); 113 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_TRACKBACKURL); 114 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_TRACKBACKURL_DESC); 115 $propbag->add('default', false); 116 break; 117 118 case 'automagic_htaccess': 119 $propbag->add('type', 'boolean'); 120 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_HTACCESS); 121 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_HTACCESS_DESC); 122 $propbag->add('default', false); 123 break; 124 125 case 'hide_email': 126 $propbag->add('type', 'boolean'); 127 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_HIDE_EMAIL); 128 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_HIDE_EMAIL_DESC); 129 $propbag->add('default', false); 130 break; 131 132 case 'csrf': 133 $propbag->add('type', 'boolean'); 134 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_CSRF); 135 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_CSRF_DESC); 136 $propbag->add('default', true); 137 break; 138 139 case 'entrytitle': 140 $propbag->add('type', 'boolean'); 141 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FILTER_TITLE); 142 $propbag->add('description', ''); 143 $propbag->add('default', false); 144 break; 145 146 case 'checkmail': 147 $propbag->add('type', 'boolean'); 148 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_CHECKMAIL); 149 $propbag->add('description', ''); 150 $propbag->add('default', false); 151 break; 152 153 case 'required_fields': 154 $propbag->add('type', 'string'); 155 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_REQUIRED_FIELDS); 156 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_REQUIRED_FIELDS_DESC); 157 $propbag->add('default', ''); 158 break; 159 160 case 'bodyclone': 161 $propbag->add('type', 'boolean'); 162 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BODYCLONE); 163 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BODYCLONE_DESC); 164 $propbag->add('default', true); 165 break; 166 167 case 'captchas': 168 $propbag->add('type', 'radio'); 169 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_CAPTCHAS); 170 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_CAPTCHAS_DESC); 171 $propbag->add('default', 'yes'); 172 $propbag->add('radio', array( 173 'value' => array(true, 'no', 'scramble'), 174 'desc' => array(YES, NO, PLUGIN_EVENT_SPAMBLOCK_CAPTCHAS_SCRAMBLE) 175 )); 176 break; 177 178 case 'hide_for_authors': 179 $_groups =& serendipity_getAllGroups(); 180 $groups = array( 181 'all' => ALL_AUTHORS, 182 'none' => NONE 183 ); 184 185 foreach($_groups AS $group) { 186 $groups[$group['confkey']] = $group['confvalue']; 187 } 188 189 $propbag->add('type', 'multiselect'); 190 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_HIDE); 191 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_HIDE_DESC); 192 $propbag->add('select_values', $groups); 193 $propbag->add('select_size', 5); 194 $propbag->add('default', 'all'); 195 break; 196 197 case 'killswitch': 198 $propbag->add('type', 'boolean'); 199 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_KILLSWITCH); 200 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_KILLSWITCH_DESC); 201 $propbag->add('default', false); 202 break; 203 204 case 'contentfilter_activate': 205 $propbag->add('type', 'radio'); 206 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FILTER_ACTIVATE); 207 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_FILTER_ACTIVATE_DESC); 208 $propbag->add('default', 'moderate'); 209 $propbag->add('radio', array( 210 'value' => array('moderate', 'reject', 'none'), 211 'desc' => array(PLUGIN_EVENT_SPAMBLOCK_API_MODERATE, PLUGIN_EVENT_SPAMBLOCK_API_REJECT, NONE) 212 )); 213 $propbag->add('radio_per_row', '1'); 214 215 break; 216 217 case 'bloggdeblacklist': 218 $propbag->add('type', 'radio'); 219 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BLOGG_SPAMLIST); 220 $propbag->add('description', ''); 221 $propbag->add('default', 'none'); 222 $propbag->add('radio', array( 223 'value' => array('moderate', 'reject', 'none'), 224 'desc' => array(PLUGIN_EVENT_SPAMBLOCK_API_MODERATE, PLUGIN_EVENT_SPAMBLOCK_API_REJECT, NONE) 225 )); 226 $propbag->add('radio_per_row', '1'); 227 228 break; 229 230 case 'akismet': 231 $propbag->add('type', 'string'); 232 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_AKISMET); 233 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_AKISMET_DESC); 234 $propbag->add('default', ''); 235 $propbag->add('radio', array( 236 'value' => array('moderate', 'reject', 'none'), 237 'desc' => array(PLUGIN_EVENT_SPAMBLOCK_API_MODERATE, PLUGIN_EVENT_SPAMBLOCK_API_REJECT, NONE) 238 )); 239 $propbag->add('radio_per_row', '1'); 240 241 break; 242 243 case 'akismet_filter': 244 $propbag->add('type', 'radio'); 245 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_AKISMET_FILTER); 246 $propbag->add('description', ''); 247 $propbag->add('default', 'reject'); 248 $propbag->add('radio', array( 249 'value' => array('moderate', 'reject', 'none'), 250 'desc' => array(PLUGIN_EVENT_SPAMBLOCK_API_MODERATE, PLUGIN_EVENT_SPAMBLOCK_API_REJECT, NONE) 251 )); 252 $propbag->add('radio_per_row', '1'); 253 254 break; 255 256 case 'contentfilter_urls': 257 $propbag->add('type', 'text'); 258 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FILTER_URLS); 259 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_FILTER_URLS_DESC); 260 $propbag->add('default', $this->filter_defaults['urls']); 261 break; 262 263 case 'contentfilter_authors': 264 $propbag->add('type', 'text'); 265 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FILTER_AUTHORS); 266 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_FILTER_AUTHORS_DESC); 267 $propbag->add('default', $this->filter_defaults['authors']); 268 break; 269 270 case 'contentfilter_words': 271 $propbag->add('type', 'text'); 272 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FILTER_WORDS); 273 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_FILTER_AUTHORS_DESC); 274 $propbag->add('default', $this->filter_defaults['words']); 275 break; 276 277 case 'contentfilter_emails': 278 $propbag->add('type', 'text'); 279 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FILTER_EMAILS); 280 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_FILTER_AUTHORS_DESC); 281 $propbag->add('default', $this->filter_defaults['emails']); 282 break; 283 284 case 'logfile': 285 $propbag->add('type', 'string'); 286 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_LOGFILE); 287 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_LOGFILE_DESC); 288 $propbag->add('default', $serendipity['serendipityPath'] . 'spamblock.log'); 289 break; 290 291 case 'logtype': 292 $propbag->add('type', 'radio'); 293 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_LOGTYPE); 294 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_LOGTYPE_DESC); 295 $propbag->add('default', 'db'); 296 $propbag->add('radio', array( 297 'value' => array('file', 'db', 'none'), 298 'desc' => array(PLUGIN_EVENT_SPAMBLOCK_LOGTYPE_FILE, PLUGIN_EVENT_SPAMBLOCK_LOGTYPE_DB, PLUGIN_EVENT_SPAMBLOCK_LOGTYPE_NONE) 299 )); 300 $propbag->add('radio_per_row', '1'); 301 302 break; 303 304 case 'ipflood': 305 $propbag->add('type', 'string'); 306 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_IPFLOOD); 307 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_IPFLOOD_DESC); 308 $propbag->add('default', 0); 309 break; 310 311 case 'captchas_ttl': 312 $propbag->add('type', 'string'); 313 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_CAPTCHAS_TTL); 314 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_CAPTCHAS_TTL_DESC); 315 $propbag->add('default', '7'); 316 break; 317 318 case 'captcha_color': 319 $propbag->add('type', 'string'); 320 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_CAPTCHA_COLOR); 321 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_CAPTCHA_COLOR_DESC); 322 $propbag->add('default', '255,255,255'); 323 $propbag->add('validate', '@^[0-9]{1,3},[0-9]{1,3},[0-9]{1,3}$@'); 324 break; 325 326 case 'forcemoderation': 327 $propbag->add('type', 'string'); 328 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FORCEMODERATION); 329 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_FORCEMODERATION_DESC); 330 $propbag->add('default', '30'); 331 break; 332 333 case 'forcemoderation_treat': 334 $propbag->add('type', 'radio'); 335 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FORCEMODERATION_TREAT); 336 $propbag->add('description', ''); 337 $propbag->add('default', 'moderate'); 338 $propbag->add('radio', array( 339 'value' => array('moderate', 'reject'), 340 'desc' => array(PLUGIN_EVENT_SPAMBLOCK_API_MODERATE, PLUGIN_EVENT_SPAMBLOCK_API_REJECT) 341 )); 342 $propbag->add('radio_per_row', '1'); 343 break; 344 345 case 'forcemoderationt': 346 $propbag->add('type', 'string'); 347 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FORCEMODERATIONT); 348 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_FORCEMODERATIONT_DESC); 349 $propbag->add('default', '30'); 350 break; 351 352 case 'forcemoderationt_treat': 353 $propbag->add('type', 'radio'); 354 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_FORCEMODERATIONT_TREAT); 355 $propbag->add('description', ''); 356 $propbag->add('default', 'moderate'); 357 $propbag->add('radio', array( 358 'value' => array('moderate', 'reject'), 359 'desc' => array(PLUGIN_EVENT_SPAMBLOCK_API_MODERATE, PLUGIN_EVENT_SPAMBLOCK_API_REJECT) 360 )); 361 $propbag->add('radio_per_row', '1'); 362 break; 363 364 case 'links_moderate': 365 $propbag->add('type', 'string'); 366 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_LINKS_MODERATE); 367 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_LINKS_MODERATE_DESC); 368 $propbag->add('default', '7'); 369 break; 370 371 case 'links_reject': 372 $propbag->add('type', 'string'); 373 $propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_LINKS_REJECT); 374 $propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_LINKS_REJECT_DESC); 375 $propbag->add('default', '13'); 376 break; 377 378 default: 379 return false; 380 } 381 382 return true; 383 } 384 385 function htaccess_update($new_ip) { 386 global $serendipity; 387 388 serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}spamblock_htaccess (ip, timestamp) VALUES ('" . serendipity_db_escape_string($new_ip) . "', '" . time() . "')"); 389 390 $q = "SELECT ip FROM {$serendipity['dbPrefix']}spamblock_htaccess WHERE timestamp > " . (time() - 86400*2) . " GROUP BY ip"; 391 $rows = serendipity_db_query($q, false, 'assoc'); 392 393 $deny = array(); 394 foreach($rows AS $row) { 395 $deny[] = $row['ip']; 396 } 397 398 $hta = $serendipity['serendipityPath'] . '.htaccess'; 399 if (file_exists($hta) && is_writable($hta)) { 400 $htaccess = file_get_contents($hta); 401 $fp = @fopen($hta, 'w'); 402 if (!$fp) { 403 return false; 404 } else { 405 // Check if an old htaccess file existed and try to preserve its contents. Otherwise completely wipe the file. 406 if ($htaccess != '' && preg_match('@^(.*)#SPAMDENY.*Deny From.+#/SPAMDENY(.*)$@imsU', $htaccess, $match)) { 407 // Code outside from s9y-code was found. 408 $content = trim($match[1]) . "\n#SPAMDENY\nDeny From " . implode(',', $deny) . "\n#/SPAMDENY\n" . trim($match[2]); 409 } else { 410 $content = trim($htaccess) . "\n#SPAMDENY\nDeny From " . implode(',', $deny) . "\n#/SPAMDENY\n"; 411 } 412 fwrite($fp, $content); 413 fclose($fp); 414 return true; 415 } 416 } 417 } 418 419 function &getBlacklist($where, $api_key = '', &$eventData, &$addData) { 420 global $serendipity; 421 422 $ret = false; 423 require_once S9Y_PEAR_PATH . 'HTTP/Request.php'; 424 if (function_exists('serendipity_request_start')) serendipity_request_start(); 425 426 switch($where) { 427 case 'akismet.com': 428 $this->log($this->logfile, $eventData['id'], 'AKISMET_SAFETY', 'Akismet verification takes place', $addData); 429 $ret = array(); 430 $data = array( 431 'blog' => $serendipity['baseURL'], 432 'user_agent' => $_SERVER['HTTP_USER_AGENT'], 433 'referrer' => $_SERVER['HTTP_REFERER'], 434 'user_ip' => $_SERVER['REMOTE_ADDR'] != getenv('SERVER_ADDR') ? $_SERVER['REMOTE_ADDR'] : getenv('HTTP_X_FORWARDED_FOR'), 435 'permalink' => serendipity_archiveURL($eventData['id'], $eventData['title'], 'serendipityHTTPPath', true, array('timestamp' => $eventData['timestamp'])), 436 'comment_type' => ($addData['type'] == 'NORMAL' ? 'comment' : 'trackback'), 437 'comment_author' => $addData['name'], 438 'comment_author_email' => $addData['email'], 439 'comment_author_url' => $addData['url'], 440 'comment_content' => $addData['comment'] 441 ); 442 $opt = array( 443 'method' => 'POST', 444 'http' => '1.1', 445 'timeout' => 20, 446 'allowRedirects' => true, 447 'maxRedirects' => 3, 448 'readTimeout' => array(5,0), 449 ); 450 451 $req = &new HTTP_Request( 452 'http://rest.akismet.com/1.1/verify-key', 453 $opt 454 ); 455 456 $req->addPostData('key', $api_key); 457 $req->addPostData('blog', $serendipity['baseURL']); 458 459 if (PEAR::isError($req->sendRequest()) || $req->getResponseCode() != '200') { 460 $ret['is_spam'] = false; 461 $ret['message'] = 'API Verification Request failed'; 462 $this->log($this->logfile, $eventData['id'], 'API_ERROR', 'Akismet HTTP verification request failed.', $addData); 463 break; 464 } else { 465 // Fetch response 466 $reqdata = $req->getResponseBody(); 467 } 468 469 if (!preg_match('@valid@i', $reqdata)) { 470 $ret['is_spam'] = false; 471 $ret['message'] = 'API Verification failed'; 472 $this->log($this->logfile, $eventData['id'], 'API_ERROR', 'Akismet API verification failed: ' . $reqdata, $addData); 473 break; 474 } 475 476 $req = &new HTTP_Request( 477 'http://' . $api_key . '.rest.akismet.com/1.1/comment-check', 478 $opt 479 ); 480 481 foreach($data AS $key => $value) { 482 $req->addPostData($key, $value); 483 } 484 485 if (PEAR::isError($req->sendRequest()) || $req->getResponseCode() != '200') { 486 $ret['is_spam'] = false; 487 $ret['message'] = 'Akismet Request failed'; 488 $this->log($this->logfile, $eventData['id'], 'API_ERROR', 'Akismet HTTP request failed.', $addData); 489 break; 490 } else { 491 // Fetch response 492 $reqdata = $req->getResponseBody(); 493 } 494 495 if (preg_match('@true@i', $reqdata)) { 496 $ret['is_spam'] = true; 497 $ret['message'] = $reqdata; 498 $this->log($this->logfile, $eventData['id'], 'AKISMET_PASS', 'Passed Akismet verification', $addData); 499 } elseif (preg_match('@false@i', $reqdata)) { 500 $ret['is_spam'] = false; 501 $ret['message'] = $reqdata; 502 $this->log($this->logfile, $eventData['id'], 'AKISMET_SPAM', 'Akismet API returned spam', $addData); 503 } else { 504 $ret['is_spam'] = false; 505 $ret['message'] = 'Akismet API failure'; 506 $this->log($this->logfile, $eventData['id'], 'API_ERROR', 'Akismet API failure: ' . $reqdata, $addData); 507 } 508 509 break; 510 511 case 'blogg.de': 512 $target = $serendipity['serendipityPath'] . PATH_SMARTY_COMPILE . '/blogg.de.blacklist.txt'; 513 $timeout = 3600; // One hour 514 515 if (file_exists($target) && filemtime($target) > time()-$timeout) { 516 $data = file_get_contents($target); 517 } else { 518 $data = ''; 519 520 $req = &new HTTP_Request('http://spam.blogg.de/blacklist.txt'); 521 522 if (PEAR::isError($req->sendRequest()) || $req->getResponseCode() != '200') { 523 if (file_exists($target) && filesize($target) > 0) { 524 $data = file_get_contents($target); 525 } 526 } else { 527 // Fetch file 528 $data = $req->getResponseBody(); 529 $fp = @fopen($target, 'w'); 530 531 if ($fp) { 532 fwrite($fp, $data); 533 fclose($fp); 534 } 535 } 536 } 537 538 $blacklist = explode("\n", $data); 539 $ret =& $blacklist; 540 541 default: 542 break; 543 } 544 545 if (function_exists('serendipity_request_end')) serendipity_request_end(); 546 return $ret; 547 } 548 549 function checkScheme() { 550 global $serendipity; 551 552 $dbversion = $this->get_config('dbversion', '1'); 553 554 if ($dbversion == '1') { 555 $q = "CREATE TABLE {$serendipity['dbPrefix']}spamblocklog ( 556 timestamp int(10) {UNSIGNED} default null, 557 type varchar(255), 558 reason text, 559 entry_id int(10) {UNSIGNED} not null default '0', 560 author varchar(80), 561 email varchar(200), 562 url varchar(200), 563 useragent varchar(255), 564 ip varchar(15), 565 referer varchar(255), 566 body text)"; 567 $sql = serendipity_db_schema_import($q); 568 569 $q = "CREATE INDEX kspamidx ON {$serendipity['dbPrefix']}spamblocklog (timestamp);"; 570 $sql = serendipity_db_schema_import($q); 571 572 $q = "CREATE INDEX kspamtypeidx ON {$serendipity['dbPrefix']}spamblocklog (type);"; 573 $sql = serendipity_db_schema_import($q); 574 575 $q = "CREATE INDEX kspamentryidx ON {$serendipity['dbPrefix']}spamblocklog (entry_id);"; 576 $sql = serendipity_db_schema_import($q); 577 578 $q = "CREATE TABLE {$serendipity['dbPrefix']}spamblock_htaccess ( 579 timestamp int(10) {UNSIGNED} default null, 580 ip varchar(15))"; 581 $sql = serendipity_db_schema_import($q); 582 583 $q = "CREATE INDEX kshtaidx ON {$serendipity['dbPrefix']}spamblock_htaccess (timestamp);"; 584 $sql = serendipity_db_schema_import($q); 585 586 $this->set_config('dbversion', '2'); 587 } 588 589 return true; 590 } 591 592 function generate_content(&$title) { 593 $title = $this->title; 594 } 595 596 // This method will be called on "fatal" spam errors that are unlikely to occur accidentally by users. 597 // Their IPs will be constantly blocked. 598 function IsHardcoreSpammer() { 599 global $serendipity; 600 601 if (serendipity_db_bool($this->get_config('automagic_htaccess'))) { 602 $this->htaccess_update($_SERVER['REMOTE_ADDR']); 603 } 604 } 605 606 // Checks whether the current author is contained in one of the gorups that need no spam checking 607 function inGroup() { 608 global $serendipity; 609 610 $checkgroups = explode('^', $this->get_config('hide_for_authors')); 611 612 if (!isset($serendipity['authorid']) || !is_array($checkgroups)) { 613 return false; 614 } 615 616 $mygroups =& serendipity_getGroups($serendipity['authorid'], true); 617 if (!is_array($mygroups)) { 618 return false; 619 } 620 621 foreach($checkgroups AS $key => $groupid) { 622 if ($groupid == 'all') { 623 return true; 624 } elseif (in_array($groupid, $mygroups)) { 625 return true; 626 } 627 } 628 629 return false; 630 } 631 632 function example() { 633 echo '<div id="captchabox" style="margin: 10px; padding: 5px; border: 1px solid black">' . PLUGIN_EVENT_SPAMBLOCK_LOOK . '<br />'; 634 $this->show_captcha(); 635 echo '</div>'; 636 } 637 638 function show_captcha($use_gd = false) { 639 global $serendipity; 640 641 if ($use_gd || (function_exists('imagettftext') && function_exists('imagejpeg'))) { 642 $max_char = 5; 643 $min_char = 3; 644 $use_gd = true; 645 } else { 646 $max_char = $min_char = 5; 647 $use_gd = false; 648 } 649 650 if ($use_gd) { 651 printf('<img src="%s" onclick="this.src=this.src + \'1\'" title="%s" alt="CAPTCHA" class="captcha" />', 652 $serendipity['baseURL'] . ($serendipity['rewrite'] == 'none' ? $serendipity['indexFile'] . '?/' : '') . 'plugin/captcha_' . md5(time()), 653 htmlspecialchars(PLUGIN_EVENT_SPAMBLOCK_CAPTCHAS_USERDESC2) 654 ); 655 } else { 656 $bgcolors = explode(',', $this->get_config('captcha_color', '255,0,255')); 657 $hexval = '#' . dechex(trim($bgcolors[0])) . dechex(trim($bgcolors[1])) . dechex(trim($bgcolors[2])); 658 $this->random_string($max_char, $min_char); 659 echo '<div class="serendipity_comment_captcha_image" style="background-color: ' . $hexval . '">'; 660 for ($i = 1; $i <= $max_char; $i++) { 661 printf('<img src="%s" title="%s" alt="CAPTCHA ' . $i . '" class="captcha" />', 662 $serendipity['baseURL'] . ($serendipity['rewrite'] == 'none' ? $serendipity['indexFile'] . '?/' : '') . 'plugin/captcha_' . $i . '_' . md5(time()), 663 htmlspecialchars(PLUGIN_EVENT_SPAMBLOCK_CAPTCHAS_USERDESC2) 664 ); 665 } 666 echo '</div>'; 667 } 668 } 669 670 function event_hook($event, &$bag, &$eventData, $addData = null) { 671 global $serendipity; 672 673 $hooks = &$bag->get('event_hooks'); 674 675 if (isset($hooks[$event])) { 676 $captchas_ttl = $this->get_config('captchas_ttl', 7); 677 $_captchas = $this->get_config('captchas', 'yes'); 678 $captchas = ($_captchas !== 'no' && ($_captchas === 'yes' || $_captchas === 'scramble' || serendipity_db_bool($_captchas))); 679 680 // Check if the entry is older than the allowed amount of time. Enforce kaptchas if that is true 681 // of if kaptchas are activated for every entry 682 $show_captcha = ($captchas && isset($eventData['timestamp']) && ($captchas_ttl < 1 || ($eventData['timestamp'] < (time() - ($captchas_ttl*60*60*24)))) ? true : false); 683 684 // Plugins can override with custom captchas 685 if (isset($serendipity['plugins']['disable_internal_captcha'])) { 686 $show_captcha = false; 687 } 688 689 $forcemoderation = $this->get_config('forcemoderation', 60); 690 $forcemoderation_treat = $this->get_config('forcemoderation_treat', 'moderate'); 691 $forcemoderationt = $this->get_config('forcemoderationt', 60); 692 $forcemoderationt_treat = $this->get_config('forcemoderationt_treat', 'moderate'); 693 694 $links_moderate = $this->get_config('links_moderate', 10); 695 $links_reject = $this->get_config('links_reject', 20); 696 697 if (function_exists('imagettftext') && function_exists('imagejpeg')) { 698 $max_char = 5; 699 $min_char = 3; 700 $use_gd = true; 701 } else { 702 $max_char = $min_char = 5; 703 $use_gd = false; 704 } 705 706 switch($event) { 707 case 'fetchcomments': 708 if (is_array($eventData) && !$_SESSION['serendipityAuthedUser'] && serendipity_db_bool($this->get_config('hide_email', false))) { 709 // 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) 710 @reset($eventData); 711 while(list($idx, $comment) = each($eventData)) { 712 $eventData[$idx]['no_email'] = true; 713 } 714 } 715 break; 716 717 case 'frontend_saveComment': 718 if (!is_array($eventData) || serendipity_db_bool($eventData['allow_comments'])) { 719 $this->checkScheme(); 720 721 $serendipity['csuccess'] = 'true'; 722 $logfile = $this->logfile = $this->get_config('logfile', $serendipity['serendipityPath'] . 'spamblock.log'); 723 $required_fields = $this->get_config('required_fields', ''); 724 725 // Check CSRF [comments only, cannot be applied to trackbacks] 726 if ($addData['type'] == 'NORMAL' && serendipity_db_bool($this->get_config('csrf', true))) { 727 if (!serendipity_checkFormToken(false)) { 728 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_CSRF_REASON, $addData); 729 $eventData = array('allow_comments' => false); 730 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_CSRF_REASON; 731 } 732 } 733 734 // Check required fields 735 if ($addData['type'] == 'NORMAL' && !empty($required_fields)) { 736 $required_field_list = explode(',', $required_fields); 737 foreach($required_field_list as $required_field) { 738 $required_field = trim($required_field); 739 if (empty($addData[$required_field])) { 740 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_REQUIRED_FIELD, $addData); 741 $eventData = array('allow_comments' => false); 742 $serendipity['messagestack']['comments'][] = sprintf(PLUGIN_EVENT_SPAMBLOCK_REASON_REQUIRED_FIELD, $required_field); 743 return false; 744 } 745 } 746 } 747 748 /* 749 if ($addData['type'] != 'NORMAL' && empty($addData['name'])) { 750 $eventData = array('allow_coments' => false); 751 $this->log($logfile, $eventData['id'], 'INVALIDGARV', 'INVALIDGARV', $addData); 752 return false; 753 } 754 */ 755 756 // Check whether to allow comments from registered authors 757 if (serendipity_userLoggedIn() && $this->inGroup()) { 758 return true; 759 } 760 761 // Check if entry title is the same as comment body 762 if (serendipity_db_bool($this->get_config('entrytitle')) && trim($eventData['title']) == trim($addData['comment'])) { 763 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_TITLE, $addData); 764 $eventData = array('allow_comments' => false); 765 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; 766 return false; 767 } 768 769 // Check for global emergency moderation 770 if ($this->get_config('killswitch', false) === true) { 771 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_KILLSWITCH, $addData); 772 $eventData = array('allow_comments' => false); 773 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_KILLSWITCH; 774 return false; 775 } 776 777 // Check for not allowing trackbacks/wfwcomments 778 if ( ($addData['type'] != 'NORMAL' || $addData['source'] == 'API') && 779 $this->get_config('disable_api_comments', 'none') != 'none') { 780 if ($this->get_config('disable_api_comments') == 'reject') { 781 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_API, $addData); 782 $eventData = array('allow_comments' => false); 783 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_REASON_API; 784 return false; 785 } elseif ($this->get_config('disable_api_comments') == 'moderate') { 786 $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_REASON_API, $addData); 787 $eventData['moderate_comments'] = true; 788 $serendipity['csuccess'] = 'moderate'; 789 $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_REASON_API; 790 } 791 } 792 793 // Filter Akismet Blacklist? 794 $akismet_apikey = $this->get_config('akismet'); 795 $akismet = $this->get_config('akismet_filter'); 796 if (!empty($akismet_apikey) && ($akismet == 'moderate' || $akismet == 'reject')) { 797 $spam = $this->getBlacklist('akismet.com', $akismet_apikey, $eventData, $addData); 798 if ($spam['is_spam'] !== false) { 799 $this->IsHardcoreSpammer(); 800 if ($akismet == 'moderate') { 801 $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_REASON_AKISMET_SPAMLIST . ': ' . $spam['message'], $addData); 802 $eventData['moderate_comments'] = true; 803 $serendipity['csuccess'] = 'moderate'; 804 $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY . ' (Akismet)'; 805 } else { 806 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_AKISMET_SPAMLIST . ': ' . $spam['message'], $addData); 807 $eventData = array('allow_comments' => false); 808 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; 809 return false; 810 } 811 } 812 } 813 814 // Check Trackback URLs? 815 if ($addData['type'] == 'TRACKBACK' && serendipity_db_bool($this->get_config('trackback_check_url'))) { 816 require_once S9Y_PEAR_PATH . 'HTTP/Request.php'; 817 818 if (function_exists('serendipity_request_start')) serendipity_request_start(); 819 $req = &new HTTP_Request($addData['url'], array('allowRedirects' => true, 'maxRedirects' => 5, 'readTimeout' => array(5,0))); 820 $is_valid = false; 821 if (PEAR::isError($req->sendRequest()) || $req->getResponseCode() != '200') { 822 $is_valid = false; 823 } else { 824 $fdata = $req->getResponseBody(); 825 826 // Check if the target page contains a link to our blog 827 if (preg_match('@' . preg_quote($serendipity['baseURL'], '@') . '@i', $fdata)) { 828 $is_valid = true; 829 } else { 830 $is_valid = false; 831 } 832 } 833 if (function_exists('serendipity_request_end')) serendipity_request_end(); 834 835 if ($is_valid === false) { 836 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_TRACKBACKURL, $addData); 837 $eventData = array('allow_comments' => false); 838 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_REASON_TRACKBACKURL; 839 return false; 840 } 841 } 842 843 // Check for word filtering 844 if ($filter_type = $this->get_config('contentfilter_activate', 'moderate')) { 845 846 // Filter authors names 847 $filter_authors = explode(';', $this->get_config('contentfilter_authors', $this->filter_defaults['authors'])); 848 if (is_array($filter_authors)) { 849 foreach($filter_authors AS $filter_author) { 850 if (empty($filter_author)) { 851 continue; 852 } 853 if (preg_match('@(' . $filter_author . ')@i', $addData['name'], $wordmatch)) { 854 if ($filter_type == 'moderate') { 855 $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_FILTER_AUTHORS . ': ' . $wordmatch[1], $addData); 856 $eventData['moderate_comments'] = true; 857 $serendipity['csuccess'] = 'moderate'; 858 $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY . ' (' . PLUGIN_EVENT_SPAMBLOCK_FILTER_AUTHORS . ': ' . $wordmatch[1] . ')'; 859 } else { 860 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_FILTER_AUTHORS . ': ' . $wordmatch[1], $addData); 861 $eventData = array('allow_comments' => false); 862 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; 863 return false; 864 } 865 } 866 } 867 } 868 869 // Filter URL 870 $filter_urls = explode(';', $this->get_config('contentfilter_urls', $this->filter_defaults['urls'])); 871 if (is_array($filter_urls)) { 872 foreach($filter_urls AS $filter_url) { 873 if (empty($filter_url)) { 874 continue; 875 } 876 if (preg_match('@(' . $filter_url . ')@i', $addData['url'], $wordmatch)) { 877 if ($filter_type == 'moderate') { 878 $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_FILTER_URLS . ': ' . $wordmatch[1], $addData); 879 $eventData['moderate_comments'] = true; 880 $serendipity['csuccess'] = 'moderate'; 881 $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY . ' (' . PLUGIN_EVENT_SPAMBLOCK_FILTER_URLS . ': ' . $wordmatch[1] . ')'; 882 } else { 883 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_FILTER_URLS . ': ' . $wordmatch[1], $addData); 884 $eventData = array('allow_comments' => false); 885 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; 886 return false; 887 } 888 } 889 } 890 } 891 892 // Filter Content 893 $filter_bodys = explode(';', $this->get_config('contentfilter_words', $this->filter_defaults['words'])); 894 if (is_array($filter_bodys)) { 895 foreach($filter_bodys AS $filter_body) { 896 if (empty($filter_body)) { 897 continue; 898 } 899 if (preg_match('@(' . $filter_body . ')@i', $addData['comment'], $wordmatch)) { 900 if ($filter_type == 'moderate') { 901 $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_FILTER_WORDS . ': ' . $wordmatch[1], $addData); 902 $eventData['moderate_comments'] = true; 903 $serendipity['csuccess'] = 'moderate'; 904 $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY . ' (' . PLUGIN_EVENT_SPAMBLOCK_FILTER_WORDS . ': ' . $wordmatch[1] . ')'; 905 } else { 906 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_FILTER_WORDS . ': ' . $wordmatch[1], $addData); 907 $eventData = array('allow_comments' => false); 908 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; 909 return false; 910 } 911 } 912 } 913 } 914 915 // Filter Emails 916 $filter_emails = explode(';', $this->get_config('contentfilter_emails', $this->filter_defaults['emails'])); 917 if (is_array($filter_emails)) { 918 foreach($filter_emails AS $filter_email) { 919 if (empty($filter_email)) { 920 continue; 921 } 922 if (preg_match('@(' . $filter_email . ')@i', $addData['email'], $wordmatch)) { 923 $this->IsHardcoreSpammer(); 924 if ($filter_type == 'moderate') { 925 $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_FILTER_EMAILS . ': ' . $wordmatch[1], $addData); 926 $eventData['moderate_comments'] = true; 927 $serendipity['csuccess'] = 'moderate'; 928 $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY . ' (' . PLUGIN_EVENT_SPAMBLOCK_FILTER_EMAILS . ': ' . $wordmatch[1] . ')'; 929 } else { 930 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_FILTER_EMAILS . ': ' . $wordmatch[1], $addData); 931 $eventData = array('allow_comments' => false); 932 $serendipity['messagestack']['emails'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; 933 return false; 934 } 935 } 936 } 937 } 938 } // Content filtering end 939 940 // Filter Blogg.de Blacklist? 941 $bloggdeblacklist = $this->get_config('bloggdeblacklist'); 942 if ($bloggdeblacklist == 'moderate' || $bloggdeblacklist == 'reject') { 943 $domains = $this->getBlacklist('blogg.de', '', $eventData, $addData); 944 if (is_array($domains)) { 945 foreach($domains AS $domain) { 946 $domain = trim($domain); 947 if (empty($domain)) { 948 continue; 949 } 950 951 if (preg_match('@' . preg_quote($domain) . '@i', $addData['url'])) { 952 $this->IsHardcoreSpammer(); 953 if ($bloggdeblacklist == 'moderate') { 954 $this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_REASON_BLOGG_SPAMLIST . ': ' . $domain, $addData); 955 $eventData['moderate_comments'] = true; 956 $serendipity['csuccess'] = 'moderate'; 957 $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY . ' (' . PLUGIN_EVENT_SPAMBLOCK_REASON_BLOGG_SPAMLIST . ')'; 958 } else { 959 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_BLOGG_SPAMLIST . ': ' . $domain, $addData); 960 $eventData = array('allow_comments' => false); 961 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; 962 return false; 963 } 964 } 965 } 966 } 967 } 968 969 // Check for maximum number of links before rejecting 970 $link_count = substr_count(strtolower($addData['comment']), 'http://'); 971 if ($links_reject > 0 && $link_count > $links_reject) { 972 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_LINKS_REJECT, $addData); 973 $eventData = array('allow_comments' => false); 974 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; 975 return false; 976 } 977 978 // Captcha checking 979 if ($show_captcha && $addData['type'] == 'NORMAL') { 980 if (!isset($_SESSION['spamblock']['captcha']) || !isset($serendipity['POST']['captcha']) || strtolower($serendipity['POST']['captcha']) != strtolower($_SESSION['spamblock']['captcha'])) { 981 $this->log($logfile, $eventData['id'], 'REJECTED', sprintf(PLUGIN_EVENT_SPAMBLOCK_REASON_CAPTCHAS, $serendipity['POST']['captcha'], $_SESSION['spamblock']['captcha']), $addData); 982 $eventData = array('allow_comments' => false); 983 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_CAPTCHAS; 984 return false; 985 } else { 986 // DEBUG 987 // $this->log($logfile, $eventData['id'], 'REJECTED', 'Captcha passed: ' . $serendipity['POST']['captcha'] . ' / ' . $_SESSION['spamblock']['captcha'] . ' // Source: ' . $_SERVER['REQUEST_URI'], $addData); 988 } 989 } else { 990 // DEBUG 991 // $this->log($logfile, $eventData['id'], 'REJECTED', 'Captcha not needed: ' . $serendipity['POST']['captcha'] . ' / ' . $_SESSION['spamblock']['captcha'] . ' // Source: ' . $_SERVER['REQUEST_URI'], $addData); 992 } 993 994 // Check for forced comment moderation 995 if ($addData['type'] == 'NORMAL' && $forcemoderation > 0 && $eventData['timestamp'] < (time() - ($forcemoderation * 60 * 60 * 24))) { 996 $this->log($logfile, $eventData['id'], $forcemoderation_treat, PLUGIN_EVENT_SPAMBLOCK_REASON_FORCEMODERATION, $addData); 997 if ($forcemoderation_treat == 'reject') { 998 $eventData = array('allow_comments' => false); 999 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_REASON_FORCEMODERATION; 1000 return false; 1001 } else { 1002 $eventData['moderate_comments'] = true; 1003 $serendipity['csuccess'] = 'moderate'; 1004 $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_REASON_FORCEMODERATION; 1005 } 1006 } 1007 1008 // Check for forced trackback moderation 1009 if ($addData['type'] != 'NORMAL' && $forcemoderationt > 0 && $eventData['timestamp'] < (time() - ($forcemoderationt * 60 * 60 * 24))) { 1010 $this->log($logfile, $eventData['id'], $forcemoderationt_treat, PLUGIN_EVENT_SPAMBLOCK_REASON_FORCEMODERATION, $addData); 1011 if ($forcemoderationt_treat == 'reject') { 1012 $eventData = array('allow_comments' => false); 1013 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_REASON_FORCEMODERATION; 1014 return false; 1015 } else { 1016 $eventData['moderate_comments'] = true; 1017 $serendipity['csuccess'] = 'moderate'; 1018 $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_REASON_FORCEMODERATION; 1019 } 1020 } 1021 1022 // Check for maximum number of links before forcing moderation 1023 if ($links_moderate > 0 && $link_count > $links_moderate) { 1024 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_LINKS_MODERATE, $addData); 1025 $eventData['moderate_comments'] = true; 1026 $serendipity['csuccess'] = 'moderate'; 1027 $serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_REASON_LINKS_MODERATE; 1028 } 1029 1030 // Check for identical comments. We allow to bypass trackbacks from our server to our own blog. 1031 if ( $this->get_config('bodyclone', true) === true && $_SERVER['REMOTE_ADDR'] != $_SERVER['SERVER_ADDR']) { 1032 $query = "SELECT count(id) AS counter FROM {$serendipity['dbPrefix']}comments WHERE body = '" . serendipity_db_escape_string($addData['comment']) . "'"; 1033 $row = serendipity_db_query($query, true); 1034 if (is_array($row) && $row['counter'] > 0) { 1035 $this->IsHardcoreSpammer(); 1036 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_BODYCLONE, $addData); 1037 $eventData = array('allow_comments' => false); 1038 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY; 1039 return false; 1040 } 1041 } 1042 1043 // Check last IP 1044 if ($addData['type'] == 'NORMAL' && $this->get_config('ipflood', 2) != 0 ) { 1045 $query = "SELECT max(timestamp) AS last_post FROM {$serendipity['dbPrefix']}comments WHERE ip = '" . serendipity_db_escape_string($_SERVER['REMOTE_ADDR']) . "'"; 1046 $row = serendipity_db_query($query, true); 1047 if (is_array($row) && $row['last_post'] > (time() - $this->get_config('ipflood', 2)*60)) { 1048 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_IPFLOOD, $addData); 1049 $eventData = array('allow_comments' => false); 1050 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_IP; 1051 return false; 1052 } 1053 } 1054 1055 // Check invalid email 1056 if ($addData['type'] == 'NORMAL' && serendipity_db_bool($this->get_config('checkmail', false))) { 1057 if (!empty($addData['email']) && strstr($addData['email'], '@') === false) { 1058 $this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_CHECKMAIL, $addData); 1059 $eventData = array('allow_comments' => false); 1060 $serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_REASON_CHECKMAIL; 1061 return false; 1062 } 1063 } 1064 1065 if ($eventData['moderate_comments'] == true) { 1066 return false; 1067 } 1068 } 1069 1070 return true; 1071 break; 1072 1073 case 'frontend_comment': 1074 if (serendipity_db_bool($this->get_config('hide_email', false))) { 1075 echo '<div class="serendipity_commentDirection serendipity_comment_spamblock">' . PLUGIN_EVENT_SPAMBLOCK_HIDE_EMAIL_NOTICE . '</div>'; 1076 } 1077 1078 if (serendipity_db_bool($this->get_config('csrf', true))) { 1079 echo serendipity_setFormToken('form'); 1080 } 1081 1082 // Check whether to allow comments from registered authors 1083 if (serendipity_userLoggedIn() && $this->inGroup()) { 1084 return true; 1085 } 1086 1087 if ($show_captcha) { 1088 echo '<div class="serendipity_commentDirection serendipity_comment_captcha">'; 1089 if (!isset($serendipity['POST']['preview']) || strtolower($serendipity['POST']['captcha'] != strtolower($_SESSION['spamblock']['captcha']))) { 1090 echo '<br />' . PLUGIN_EVENT_SPAMBLOCK_CAPTCHAS_USERDESC . '<br />'; 1091 $this->show_captcha($use_gd); 1092 echo '<br />'; 1093 echo '<label for="captcha">'. PLUGIN_EVENT_SPAMBLOCK_CAPTCHAS_USERDESC3 . '</label><br /><input class="input_textbox" type="text" size="5" name="serendipity[captcha]" value="" id="captcha" />'; 1094 } elseif (isset($serendipity['POST']['captcha'])) { 1095 echo '<input type="hidden" name="serendipity[captcha]" value="' . htmlspecialchars($serendipity['POST']['captcha']) . '" />'; 1096 } 1097 echo '</div>'; 1098 } 1099 1100 return true; 1101 break; 1102 1103 1104 case 'external_plugin': 1105 $parts = explode('_', $eventData); 1106 if (!empty($parts[1])) { 1107 $param = (int) $parts[1]; 1108 } else { 1109 $param = null; 1110 } 1111 1112 $methods = array('captcha'); 1113 1114 if (!in_array($parts[0], $methods)) { 1115 return; 1116 } 1117 1118 list($musec, $msec) = explode(' ', microtime()); 1119 $srand = (float) $msec + ((float) $musec * 100000); 1120 srand($srand); 1121 mt_srand($srand); 1122 $width = 120; 1123 $height = 40; 1124 1125 $bgcolors = explode(',', $this->get_config('captcha_color', '255,255,255')); 1126 $fontfiles = array('Vera.ttf', 'VeraSe.ttf', 'chumbly.ttf', '36daysago.ttf'); 1127 1128 if ($use_gd) { 1129 $strings = $this->random_string($max_char, $min_char); 1130 $fontname = $fontfiles[array_rand($fontfiles)]; 1131 $font = $serendipity['serendipityPath'] . 'plugins/serendipity_event_spamblock/' . $fontname; 1132 1133 if (!file_exists($font)) { 1134 // Search in shared plugin directory 1135 $font = S9Y_INCLUDE_PATH . 'plugins/serendipity_event_spamblock/' . $fontname; 1136 } 1137 1138 if (!file_exists($font)) { 1139 die(PLUGIN_EVENT_SPAMBLOCK_ERROR_NOTTF); 1140 } 1141 1142 header('Content-Type: image/jpeg'); 1143 $image = imagecreate($width, $height); 1144 $bgcol = imagecolorallocate($image, trim($bgcolors[0]), trim($bgcolors[1]), trim($bgcolors[2])); 1145 // imagettftext($image, 10, 1, 1, 15, imagecolorallocate($image, 255, 255, 255), $font, 'String: ' . $string); 1146 1147 $pos_x = 5; 1148 foreach($strings AS $idx => $charidx) { 1149 $color = imagecolorallocate($image, mt_rand(50, 235), mt_rand(50, 235), mt_rand(50,235)); 1150 $size = mt_rand(15, 21); 1151 $angle = mt_rand(-20, 20); 1152 $pos_y = ceil($height - (mt_rand($size/3, $size/2))); 1153 1154 imagettftext( 1155 $image, 1156 $size, 1157 $angle, 1158 $pos_x, 1159 $pos_y, 1160 $color, 1161 $font, 1162 $this->chars[$charidx] 1163 ); 1164 1165 $pos_x = $pos_x + $size + 2; 1166 1167 } 1168 1169 if ($_captchas === 'scramble') { 1170 $line_diff = mt_rand(5, 15); 1171 $pixel_col = imagecolorallocate($image, trim($bgcolors[0])-mt_rand(10,50), trim($bgcolors[1])-mt_rand(10,50), trim($bgcolors[2])-mt_rand(10,50)); 1172 for ($y = $line_diff; $y < $height; $y += $line_diff) { 1173 $row_diff = mt_rand(5, 15); 1174 for ($x = $row_diff; $x < $width; $x+= $row_diff) { 1175 imagerectangle($image, $x, $y, $x+1, $y+1, $pixel_col); 1176 } 1177 } 1178 } 1179 imagejpeg($image, '', 90); 1180 imagedestroy($image); 1181 } else { 1182 header('Content-Type: image/png'); 1183 $output_char = strtolower($_SESSION['spamblock']['captcha']{$parts[1] - 1}); 1184 $cap = $serendipity['serendipityPath'] . 'plugins/serendipity_event_spamblock/captcha_' . $output_char . '.png'; 1185 if (!file_exists($cap)) { 1186 $cap = S9Y_INCLUDE_PATH . 'plugins/serendipity_event_spamblock/captcha_' . $output_char . '.png'; 1187 } 1188 1189 if (file_exists($cap)) { 1190 echo file_get_contents($cap); 1191 } 1192 } 1193 return true; 1194 break; 1195 1196 case 'backend_comments_top': 1197 1198 // Add Author to blacklist. If already filtered, it will be removed from the filter. (AKA "Toggle") 1199 if (isset($serendipity['GET']['spamBlockAuthor'])) { 1200 $item = $this->getComment('author', $serendipity['GET']['spamBlockAuthor']); 1201 $items = &$this->checkFilter('authors', $item, true); 1202 $this->set_config('contentfilter_authors', implode(';', $items)); 1203 } 1204 1205 // Add URL to blacklist. If already filtered, it will be removed from the filter. (AKA "Toggle") 1206 if (isset($serendipity['GET']['spamBlockURL'])) { 1207 $item = $this->getComment('url', $serendipity['GET']['spamBlockURL']); 1208 $items = &$this->checkFilter('urls', $item, true); 1209 $this->set_config('contentfilter_urls', implode(';', $items)); 1210 } 1211 1212 // Add E-mail to blacklist. If already filtered, it will be removed from the filter. (AKA "Toggle") 1213 if (isset($serendipity['GET']['spamBlockEmail'])) { 1214 $item = $this->getComment('email', $serendipity['GET']['spamBlockEmail']); 1215 $items = &$this->checkFilter('emails', $item, true); 1216 $this->set_config('contentfilter_emails', implode(';', $items)); 1217 } 1218 1219 echo ' - ' . WORD_OR . ' - <a class="serendipityPrettyButton" href="serendipity_admin.php?serendipity[adminModule]=plugins&serendipity[plugin_to_conf]=' . $this->instance . '">' . PLUGIN_EVENT_SPAMBLOCK_CONFIG . '</a>'; 1220 return true; 1221 break; 1222 1223 case 'backend_view_comment': 1224 $author_is_filtered = $this->checkFilter('authors', $eventData['author']); 1225 $clink1 = 'clink1' . $eventData['id']; 1226 $clink2 = 'clink2' . $eventData['id']; 1227 1228 $eventData['action_author'] .= ' <a id="' . $clink1 . '" class="serendipityIconLink" title="' . ($author_is_filtered ? PLUGIN_EVENT_SPAMBLOCK_REMOVE_AUTHOR : PLUGIN_EVENT_SPAMBLOCK_ADD_AUTHOR) . '" href="serendipity_admin.php?serendipity[adminModule]=comments&serendipity[spamBlockAuthor]=' . $eventData['id'] . $addData . '#' . $clink1 . '"><img src="' . serendipity_getTemplateFile('admin/img/' . ($author_is_filtered ? 'un' : '') . 'configure.png') . '" alt="" /></a>'; 1229 1230 if (!empty($eventData['url'])) { 1231 $url_is_filtered = $this->checkFilter('urls', $eventData['url']); 1232 $eventData['action_url'] .= ' <a id="' . $clink2 . '" class="serendipityIconLink" title="' . ($url_is_filtered ? PLUGIN_EVENT_SPAMBLOCK_REMOVE_URL : PLUGIN_EVENT_SPAMBLOCK_ADD_URL) . '" href="serendipity_admin.php?serendipity[adminModule]=comments&serendipity[spamBlockURL]=' . $eventData['id'] . $addData . '#' . $clink2 . '"><img src="' . serendipity_getTemplateFile('admin/img/' . ($url_is_filtered ? 'un' : '') . 'configure.png') . '" alt="" /></a>'; 1233 } 1234 1235 if (!empty($eventData['email'])) { 1236 $email_is_filtered = $this->checkFilter('emails', $eventData['email']); 1237 $eventData['action_email'] .= ' <a id="' . $clink2 . '" class="serendipityIconLink" title="' . ($email_is_filtered ? PLUGIN_EVENT_SPAMBLOCK_REMOVE_EMAIL : PLUGIN_EVENT_SPAMBLOCK_ADD_EMAIL) . '" href="serendipity_admin.php?serendipity[adminModule]=comments&serendipity[spamBlockEmail]=' . $eventData['id'] . $addData . '#' . $clink2 . '"><img src="' . serendipity_getTemplateFile('admin/img/' . ($email_is_filtered ? 'un' : '') . 'configure.png') . '" alt="" /></a>'; 1238 } 1239 1240 return true; 1241 break; 1242 1243 default: 1244 return false; 1245 break; 1246 } 1247 } else { 1248 return false; 1249 } 1250 } 1251 1252 function &checkFilter($what, $match, $getItems = false) { 1253 $items = explode(';', $this->get_config('contentfilter_' . $what, $this->filter_defaults[$what])); 1254 1255 $filtered = false; 1256 if (is_array($items)) { 1257 foreach($items AS $key => $item) { 1258 if (empty($match)) { 1259 continue; 1260 } 1261 1262 if (empty($item)) { 1263 unset($items[$key]); 1264 continue; 1265 } 1266 1267 if (preg_match('@' . $item . '@', $match)) { 1268 $filtered = true; 1269 unset($items[$key]); 1270 } 1271 } 1272 } 1273 1274 if ($getItems) { 1275 if (!$filtered && !empty($match)) { 1276 $items[] = preg_quote($match, '@'); 1277 } 1278 1279 return $items; 1280 } 1281 1282 return $filtered; 1283 } 1284 1285 function getComment($key, $id) { 1286 global $serendipity; 1287 $c = serendipity_db_query("SELECT $key FROM {$serendipity['dbPrefix']}comments WHERE id = '" . (int)$id . "'", true, 'assoc'); 1288 1289 if (!is_array($c) || !isset($c[$key])) { 1290 return false; 1291 } 1292 1293 return $c[$key]; 1294 } 1295 1296 function random_string($max_char, $min_char) { 1297 $this->chars = array(2, 3, 4, 7, 9); // 1, 5, 6 and 8 may look like characters. 1298 $this->chars = array_merge($this->chars, array('A','B','C','D','E','F','H','J','K','L','M','N','P','Q','R','T','U','V','W','X','Y','Z')); // I, O, S may look like numbers 1299 1300 $strings = array_rand($this->chars, mt_rand($min_char, $max_char)); 1301 $string = ''; 1302 foreach($strings AS $idx => $charidx) { 1303 $string .= $this->chars[$charidx]; 1304 } 1305 $_SESSION['spamblock'] = array('captcha' => $string); 1306 1307 return $strings; 1308 } 1309 1310 function log($logfile, $id, $switch, $reason, $comment) { 1311 global $serendipity; 1312 1313 $method = $this->get_config('logtype'); 1314 1315 switch($method) { 1316 case 'file': 1317 if (empty($logfile)) { 1318 return; 1319 } 1320 1321 $fp = @fopen($logfile, 'a+'); 1322 if (!is_resource($fp)) { 1323 return; 1324 } 1325 1326 fwrite($fp, sprintf( 1327 '[%s] - [%s: %s] - [#%s, Name "%s", E-Mail "%s", URL "%s", User-Agent "%s", IP %s] - [%s]' . "\n", 1328 date('Y-m-d H:i:s', serendipity_serverOffsetHour()), 1329 $switch, 1330 $reason, 1331 $id, 1332 str_replace("\n", ' ', $comment['name']), 1333 str_replace("\n", ' ', $comment['email']), 1334 str_replace("\n", ' ', $comment['url']), 1335 str_replace("\n", ' ', $_SERVER['HTTP_USER_AGENT']), 1336 $_SERVER['REMOTE_ADDR'], 1337 str_replace("\n", ' ', $comment['comment']) 1338 )); 1339 1340 fclose($fp); 1341 break; 1342 1343 case 'none': 1344 return; 1345 break; 1346 1347 case 'db': 1348 default: 1349 $q = sprintf("INSERT INTO {$serendipity['dbPrefix']}spamblocklog 1350 (timestamp, type, reason, entry_id, author, email, url, useragent, ip, referer, body) 1351 VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", 1352 1353 serendipity_serverOffsetHour(), 1354 serendipity_db_escape_string($switch), 1355 serendipity_db_escape_string($reason), 1356 serendipity_db_escape_string($id), 1357 serendipity_db_escape_string($comment['name']), 1358 serendipity_db_escape_string($comment['email']), 1359 serendipity_db_escape_string($comment['url']), 1360 substr(serendipity_db_escape_string($_SERVER['HTTP_USER_AGENT']), 0, 255), 1361 serendipity_db_escape_string($_SERVER['REMOTE_ADDR']), 1362 substr(serendipity_db_escape_string(isset($_SESSION['HTTP_REFERER']) ? $_SESSION['HTTP_REFERER'] : $_SERVER['HTTP_REFERER']), 0, 255), 1363 serendipity_db_escape_string($comment['comment']) 1364 ); 1365 1366 serendipity_db_query($q); 1367 break; 1368 } 1369 } 1370 } 1371 1372 /* vim: set sts=4 ts=4 expandtab : */
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Sat Nov 24 09:00:37 2007 | par Balluche grâce à PHPXref 0.7 |
|