Author: PeterThoeny Date: 2005-10-29 16:17:08 -0700 (Sat, 29 Oct 2005) New Revision: 7223 Added: twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/pub/ twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/pub/TWiki/ twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/pub/TWiki/BlackListPlugin/ twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/pub/TWiki/BlackListPlugin/.htaccess twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/templates/ twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/templates/oopsblacklist.tmpl Modified: twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/data/TWiki/BlackListPlugin.txt twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/lib/TWiki/Plugins/BlackListPlugin.pm twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/lib/TWiki/Plugins/BlackListPlugin/MANIFEST Log: Item796: BlackListPlugin version 29 Oct 2005 Modified: twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/data/TWiki/BlackListPlugin.txt =================================================================== --- twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/data/TWiki/BlackListPlugin.txt 2005-10-29 22:37:28 UTC (rev 7222) +++ twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/data/TWiki/BlackListPlugin.txt 2005-10-29 23:17:08 UTC (rev 7223) @@ -1,13 +1,17 @@ -%META:TOPICINFO{author="PeterThoeny" date="1106382775" format="1.0" version="1.5"}% +%META:TOPICINFO{author="PeterThoeny" date="1130572729" format="1.0" version="1.7"}% ---+ Black List Plugin -This is a simple utility to keep black sheep away from a public TWiki site. The site can be protected against excessive page access (e.g. by bad robots) and suspicious activities of users, such as multiple registrations or rapid topic updates indicating Wiki:WikiSpam. +This is a simple utility to keep black sheep away from a public TWiki site. The site can be protected against excessive page access (e.g. by bad robots), suspicious activities of users, such as multiple registrations or rapid topic updates indicating Wiki:WikiSpam, or saving text with wiki-spam. The Plugin monitors activities by IP address and uses three IP address lists to protect the TWiki site: * WHITELIST: Manually maintained list of users who should never get on the BANLIST * BLACKLIST: Manually maintained list of malicious users * BANLIST: Automatically updated list of users with suspicious activities +On topic save, text is compared to a known list of spam patterns. If wiki-spam is identified, topic save is cancelled, an error message is shown, and the IP address is put on the BANLIST. Two wiki-spam lists are used: + * Local SPAMLIST: Manually maintained list of spam patterns + * Public wiki-spam list: Big list of wiki-spam patterns, retrieved from external web site + Users on the BLACKLIST and BANLIST will have every page access delayed by one minute and will get an error message. To fight Wiki-spam, the Plugin can also add a =rel="nofollow"= parameter to external URLs. Search engines will not follow links that have this parameter, taking away the incentive to add spam to TWiki. @@ -16,40 +20,76 @@ Plugin settings are stored as preferences variables. To reference a plugin setting write ==%<nop><plugin>_<setting>%==, i.e. ==%<nop>INTERWIKIPLUGIN_SHORTDESCRIPTION%== +__General settings:__ * One line description, is shown in the %TWIKIWEB%.TextFormattingRules topic: * Set SHORTDESCRIPTION = Utility to keep malicious users away from a public TWiki site * Debug plugin: (See output in =data/debug.txt=) * Set DEBUG = 0 + * Log access of blacklist and spam list activities: (0 or 1) + * Set LOGACCESS = 1 + + * Access restriction: + * #Set ALLOWTOPICCHANGE = %MAINWEB%.TWikiAdminGroup + +__WHITELIST, BLACKLIST and BANLIST settings:__ * WHITELIST: Comma delimited list of IP addresses; possible to use partial addresses ending in a dot * Set WHITELIST = 127.0.0.1 * BLACKLIST: Comma delimited list of IP addresses; possible to use partial addresses ending in a dot * Set BLACKLIST = 203.88.152., 203.88.155., 219.65.75. -<form name="banlist" action="%SCRIPTURL%/viewauth%SCRIPTSUFFIX%/%WEB%/%TOPIC%" method="get"> +#BanList +<form name="banlist" action="%SCRIPTURL%/viewauth%SCRIPTSUFFIX%/%WEB%/%TOPIC%#BanList" method="post"> * BANLIST: Automatically updated list of IP addresses based on BANLIST configuration - * <select name="action"> <option></option> <option value="ban_add">Add</option> <option value="ban_remove">Remove</option> </select> IP address <input type="text" name="ip" size="16" value="" /> <input type="submit" value="Submit" /> %BLACKLIST{ action="%URLPARAM{action}%" value="%URLPARAM{ip}%" }% + * Action: <select name="action"> <option></option> <option value="ban_add">Add</option> <option value="ban_remove">Remove</option> </select> IP address <input type="text" name="ip" size="16" value="" /> <input type="submit" value="Submit" /> %BLACKLIST{ action="%URLPARAM{action}%" value="%URLPARAM{ip}%" }% * Current list: %BLACKLIST{ action="ban_show" }% </form> - * BANLIST configuration, comma delimited list of: Points for registration, points for each save and upload, points for view raw, points for other actions like view, threshold to add to BANLIST, measured over time (in seconds) + * BANLIST configuration, comma delimited list of: + 1 points for registration + 2 Points for each save and upload + 3 Points for view raw + 4 Points for other actions like view + 5 Threshold to add to BANLIST + 6 Measured over time (in seconds) * Set BANLISTCONFIG = 20, 5, 1, 20, 120, 300 * Your current score: %BLACKLIST{ action="user_score" }% for IP address %REMOTE_ADDR% * Message for users on BLACKLIST and BANLIST: - * Set BLACKLISTMESSAGE = You are black listed at the %WIKITOOLNAME% web site due to excessive access or suspicious activities. Please contact site administrator %WIKIWEBMASTER% if you got on the list by mistake. Black listed IP addresses will be submitted to major blacklist databases. + * Set BLACKLISTMESSAGE = Your IP address %REMOTE_ADDR% is black listed at the %WIKITOOLNAME% web site due to excessive access or suspicious activities. Please contact site administrator %WIKIWEBMASTER% if you got on the list by mistake. +__Wiki-spam filtering settings:__ + * Filter wiki-spam on topic save based on SPAMLIST: (0 or 1) + * Set FILTERWIKISPAM = 1 + + * Comma separated list of Web.Topics to exclude from wiki-spam filtering: + * Set SPAMEXCLUDETOPICS = + +#SpamList +<form name="spamlist" action="%SCRIPTURL%/viewauth%SCRIPTSUFFIX%/%WEB%/%TOPIC%#SpamList" method="post"> + * Local SPAMLIST: Manually maintained list of spam patterns (NOTE: *Must be* %TWIKIWEB%.RegularExpression patterns) + * Action: <select name="action"> <option></option> <option value="spam_add">Add</option> <option value="spam_remove">Remove</option> </select> wiki-spam regex pattern <code>http://.*?</code><input type="text" name="spam" size="32" value="" /> <input type="submit" value="Submit" /> %BLACKLIST{ action="%URLPARAM{action}%" value="%URLPARAM{spam}%" }% + * Current list: %BLACKLIST{ action="spam_show" }% +</form> + + * Public wiki-spam list: Big list of wiki-spam patterns, retrieved from external web site (thanks to <nop>MoinMoin's [[http://moinmoin.wikiwikiweb.de/AntiSpamGlobalSolution][AntiSpamGlobalSolution]] wiki-spam list) + * Set SPAMLISTURL = http://arch.thinkmo.de/cgi-bin/spam-merge + + * Cache refresh time (in minutes) for public wiki-merge pattern list: + * Set SPAMLISTREFRESH = 60 + + * Cache refresh time (in minutes) for internal wiki-spam regular expression cache: + * Set SPAMREGEXREFRESH = 10 + + * Message for users trying to save text with wiki-spam: + * Set WIKISPAMMESSAGE = Wiki-spam detected, "%WIKISPAMWORD%" is a banned word and cannot be saved. Your IP address %REMOTE_ADDR% is black listed at the %WIKITOOLNAME% web site due to suspicious activities. Please contact site administrator %WIKIWEBMASTER% if you got on the list by mistake. + +__Nofollow link setting:__ * Add a =rel="nofollow"= parameter to external URLs. Use this is to fight Wiki-spam. Search engines will not follow the link if a URL has a nofollow parameter, such as =<a href="http://spammer.com/" rel="nofollow">=. Specify topic age in hours for which the nofollow parameter should appear (set it to a value that gives you enough time to remove spam); set it to -1 to add the nofollow parameter unconditionally to external URLs; or 0 to disable: (-1, 0, 1...N) * Set NOFOLLOWAGE = -1 - * Log access of users on BLACKLIST and BANLIST: (1 or 0) - * Set LOGACCESS = 1 - - * Access restriction - * Set ALLOWTOPICCHANGE = %MAINWEB%.TWikiAdminGroup - ---++ Plugin Installation Instructions __Note:__ You do not need to install anything on the browser to use this plugin. The following instructions are for the administrator who installs the plugin on the server where TWiki is running. @@ -60,7 +100,11 @@ | ==data/TWiki/%TOPIC%.txt== | Plugin topic | | ==data/TWiki/%TOPIC%.txt,v== | Plugin topic repository | | ==lib/TWiki/Plugins/%TOPIC%.pm== | Plugin Perl module | - * (Dakar) Visit =configure= in your TWiki installation, and enable the plugin in the {Plugins} section. + | ==pub/TWiki/%TOPIC%/.htaccess== | Apache access control to protect pub dir | + | ==templates/oopsblacklist.tmpl== | Generic oops message | + * Write protect this Plugin topics by removing the # hash sign from the ALLOWTOPICCHANGE setting + * Make sure =pub/TWiki/%TOPIC%/= is writable by the CGI user (typically =nobody=) + * Dakar release only: Run the configure utility in your browser to enable the Plugin * Test if the installation was successful: 1 Using above form, add the IP address of one of your workstations to the BANLIST 1 Access TWiki from that workstation @@ -68,16 +112,13 @@ * else, you should get an 500 Internal Server Error for other scripts 1 On a different workstation, remove the IP address of the test workstation from the BANLIST - ----++ Known Issues - - * The BANLIST does not work until you add and remove an IP address using above form - ---++ Plugin Info | Plugin Author: | TWiki:Main/PeterThoeny | -| Plugin Version: | 22 Jan 2005 (V1.004) | +| Plugin Version: | 29 Oct 2005 | | Change History: | <!-- versions below in reverse order --> | +| 29 Oct 2005: | Added wiki-spam handling to prevent topic save with wiki-spam | +| 27 Oct 2005: | For BANLIST, add/remove multiple IP addresses at once, contributed by TWiki:Main.MichaelDaum | | 22 Jan 2005: | Added NOFOLLOWAGE handling | | 19 Jan 2005: | Added score for "view raw" to address e-mail harvester issue | | 05 Apr 2004: | Fixed bug in event log (requiring update of earlier Plugin versions); doc updates | @@ -86,11 +127,13 @@ | CPAN Dependencies: | none | | Other Dependencies: | none | | Perl Version: | 5.005 | +| License: | GPL ([[http://www.gnu.org/copyleft/gpl.html][GNU General Public License]]) | | TWiki:Plugins/Benchmark: | %TWIKIWEB%.GoodStyle 99%, %TWIKIWEB%.FormattedSearch 99%, %TOPIC% 97% | | Plugin Home: | http://TWiki.org/cgi-bin/view/Plugins/%TOPIC% | | Feedback: | http://TWiki.org/cgi-bin/view/Plugins/%TOPIC%Dev | +| Appraisal: | http://TWiki.org/cgi-bin/view/Plugins/%TOPIC%Appraisal | __Related Topics:__ %TWIKIWEB%.TWikiPreferences, %TWIKIWEB%.TWikiPlugins, [[http://directory.google.com/Top/Computers/Internet/Abuse/Spam/Blacklists/][Google blacklist directory]] --- TWiki:Main/PeterThoeny - 22 Jan 2005 +-- TWiki:Main/PeterThoeny - 29 Oct 2005 Modified: twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/lib/TWiki/Plugins/BlackListPlugin/MANIFEST =================================================================== --- twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/lib/TWiki/Plugins/BlackListPlugin/MANIFEST 2005-10-29 22:37:28 UTC (rev 7222) +++ twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/lib/TWiki/Plugins/BlackListPlugin/MANIFEST 2005-10-29 23:17:08 UTC (rev 7223) @@ -1,2 +1,4 @@ data/TWiki/BlackListPlugin.txt NEW lib/TWiki/Plugins/BlackListPlugin.pm NEW +pub/TWiki/BlackListPlugin/.htaccess NEW +templates/oopsblacklist.tmpl NEW Modified: twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/lib/TWiki/Plugins/BlackListPlugin.pm =================================================================== --- twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/lib/TWiki/Plugins/BlackListPlugin.pm 2005-10-29 22:37:28 UTC (rev 7222) +++ twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/lib/TWiki/Plugins/BlackListPlugin.pm 2005-10-29 23:17:08 UTC (rev 7223) @@ -17,7 +17,6 @@ # http://www.gnu.org/copyleft/gpl.html # - # ========================= package TWiki::Plugins::BlackListPlugin; @@ -57,6 +56,18 @@ } # ========================= +sub writeDebug +{ + TWiki::Func::writeDebug("$pluginName - " . $_[0]) if $debug; +} + +# ========================= +sub writeDebugTimes +{ + TWiki::Func::writeDebugTimes("$pluginName - " . $_[0]) if $debug; +} + +# ========================= sub initPlugin { ( $topic, $web, $user, $installWeb ) = @_; @@ -106,13 +117,14 @@ $isBlackSheep = 0; $userScore = "N/A"; if( ( $remoteAddr ) && ( $remoteAddr !~ /^$whiteList/ ) ) { - if( $remoteAddr =~ /^$blackRE/ ) { + if( $blackRE ne "()" && $remoteAddr =~ /^$blackRE/ ) { # already a black sheep $isBlackSheep = 1; } else { # check for new candidate of black sheep - my( $c1, $c2, $c3, $c4, $c5, $c6 ) = split( /, */, TWiki::Func::getPreferencesValue( "\U$pluginName\E_BANLISTCONFIG" ) ); + my( $c1, $c2, $c3, $c4, $c5, $c6 ) = + split( /,\s*/, TWiki::Func::getPreferencesValue( "\U$pluginName\E_BANLISTCONFIG" ) ); $cfg{ "ptReg" } = $c1 || 10; $cfg{ "ptChg" } = $c2 || 5; $cfg{ "ptView" } = $c3 || 1; @@ -121,7 +133,7 @@ $cfg{ "period" } = $c6 || 300; $userScore = _handleEventLog( $remoteAddr, $scriptName, $queryString ); - TWiki::Func::writeDebug( "- TWiki::Plugins::${pluginName}::initPlugin() score: $userScore" ) if $debug; + writeDebug( "initPlugin() score: $userScore" ); if( $userScore > $cfg{ "ptLimit" } ) { $isBlackSheep = 1; _handleBanList( "add", $remoteAddr ); @@ -135,17 +147,24 @@ _writeLog( $scriptName ); # sleep for one minute sleep 60 unless( $debug ); - if( $scriptName =~ /view/ ) { - # view script: show message later in commonTagsHandler + if( $scriptName =~ /oops/ ) { + # show oops message normal } else { - # other scripts: force a "500 Internal Server Error" error - exit 1; + # other scripts: redirect to oops message + my $cgiQuery = TWiki::Func::getCgiQuery(); + unless( $cgiQuery ) { + exit 1; # Force a "500 Internal Server Error" error + } + my $msg = TWiki::Func::getPreferencesValue( "\U$pluginName\E_BLACKLISTMESSAGE" ) || + "You are black listed at %WIKITOOLNAME%."; + my $url = TWiki::Func::getOopsUrl( $web, $topic, "oopsblacklist", $msg ); + print $cgiQuery->redirect( $url ); + exit 0; # should never reach this } } # Plugin correctly initialized - TWiki::Func::writeDebug( "- TWiki::Plugins::${pluginName}::initPlugin( $web.$topic ) is OK, " - . "whiteList $whiteList, blackRE $blackRE" ) if $debug; + writeDebug( "initPlugin( $web.$topic ) is OK, whiteList $whiteList, blackRE $blackRE" ); return 1; } @@ -154,14 +173,8 @@ { ### my ( $text, $topic, $web ) = @_; # do not uncomment, use $_[0], $_[1]... instead - TWiki::Func::writeDebug( "- ${pluginName}::commonTagsHandler( $_[2].$_[1] )" ) if $debug; + writeDebug( "commonTagsHandler( $_[2].$_[1] )" ); - if( $isBlackSheep ) { - my $message = TWiki::Func::getPreferencesValue( "\U$pluginName\E_BLACKLISTMESSAGE" ) || - "You are black listed at %WIKITOOLNAME%. " - . "In addition, your IP address will be submitted to major blacklist databases."; - $_[0] = $message; - } $_[0] =~ s/%BLACKLIST{(.*?)}%/_handleBlackList( $1, $_[2], $_[1] )/geo; } @@ -170,7 +183,7 @@ { ### my ( $text ) = @_; # do not uncomment, use $_[0] instead - TWiki::Func::writeDebug( "- ${pluginName}::endRenderingHandler( $web.$topic )" ) if $debug; + writeDebug( "endRenderingHandler( $web.$topic )" ); # This handler is called by getRenderedVersion just after the line loop, that is, # after almost all XHTML rendering of a topic. <nop> tags are removed after this. @@ -180,38 +193,227 @@ } # ========================= +sub beforeSaveHandler +{ +### my ( $text, $topic, $web ) = @_; # do not uncomment, use $_[0], $_[1]... instead + + writeDebug( "beforeSaveHandler( $_[2].$_[1] )" ); + # This handler is called by TWiki::Store::saveTopic just before the save action. + + # Bail out unless spam filtering is enabled + return unless( TWiki::Func::getPreferencesFlag( "\U$pluginName\E_FILTERWIKISPAM" ) ); + + # Bail out for excluded topics + my @arr = split( /,\s*/, TWiki::Func::getPreferencesValue( "\U$pluginName\E_SPAMEXCLUDETOPICS" ) ); + foreach( @arr ) { + return if( ( /^(.*)/ ) && ( $1 eq "$_[2].$_[1]" ) ); + } + + my $spamListRegex = _getSpamListRegex(); + if( $_[0] =~ /$spamListRegex/ ) { + my $badword = $1; + my $cgiQuery = TWiki::Func::getCgiQuery(); + if( $cgiQuery ) { + my $remoteAddr = $ENV{'REMOTE_ADDR'} || ""; + _handleBanList( "add", $remoteAddr ); + _writeLog( "SPAMLIST add: $remoteAddr, spam '$badword'" ); + + my $msg = TWiki::Func::getPreferencesValue( "\U$pluginName\E_WIKISPAMMESSAGE" ) || + "Spam detected, '%WIKISPAMWORD%' is a banned word and cannot be saved."; + $msg =~ s/%WIKISPAMWORD%/$badword/; + $url = TWiki::Func::getOopsUrl( $web, $topic, "oopsblacklist", $msg ); + print $cgiQuery->redirect( $url ); + exit 0; # should never reach this + } + # else (unlikely case) force a "500 Internal Server Error" error + exit 1; + } +} + +# ========================= +sub _getSpamListRegex +{ + my $refresh = TWiki::Func::getPreferencesValue( "\U$pluginName\E_SPAMREGEXREFRESH" ) || 5; + $refresh =~ s/.*?([0-9]+).*/$1/s || 5; + $refresh = 1 if( $refresh < 1 ); + + my $cacheFile = _makeFileName( "spam_regex" ); + if( ( -e $cacheFile ) && ( ( time() - (stat(_))[9] ) <= ( $refresh * 60 ) ) ) { + # return cached version if it exists and isn't too old + return TWiki::Func::readFile( $cacheFile ); + } + + # merge public and local spam list + my $text = _getSpamMergeText() . "\n" . _handleSpamList( "read", "" ); + $text =~ s/<[^>]*//go; # strip <tags> + $text =~ s/ *\#.*//go; # strip comments + $text =~ s/^[\n\r]+//os; + $text =~ s/[\n\r]+$//os; + $text =~ s/[\n\r]+/\|/gos; # build regex + $text = "http://[\\w\\.\\-:\\@/]*?($text)"; + TWiki::Func::saveFile( $cacheFile, $text ); + return $text; +} + +# ========================= +sub _getSpamMergeText +{ + my $url = TWiki::Func::getPreferencesValue( "\U$pluginName\E_SPAMLISTURL" ) || + 'http://arch.thinkmo.de/cgi-bin/spam-merge'; + my $refresh = 30; # minutes + my $refresh = TWiki::Func::getPreferencesValue( "\U$pluginName\E_SPAMLISTREFRESH" ) || 15; + $refresh =~ s/.*?([0-9]+).*/$1/s || 15; + $refresh = 10 if( $refresh < 10 ); + + my $cacheFile = _makeFileName( "spam_merge" ); + if( ( -e $cacheFile ) && ( ( time() - (stat(_))[9] ) <= ( $refresh * 60 ) ) ) { + # return cached version if it exists and isn't too old + return TWiki::Func::readFile( $cacheFile ); + } + + $url =~ /http\:\/\/(.*?)(\/.*)/; + my $host = $1; + my $port = 0; + my $path = $2; + # figure out how to get to TWiki::Net which is wide open in Cairo and before, + # but Dakar uses the session object. + my $text = $TWiki::Plugins::SESSION->{net} + ? $TWiki::Plugins::SESSION->{net}->getUrl( $host, $port, $path ) + : TWiki::Net::getUrl( $host, $port, $path ); + + if( $text =~ /text\/plain\s*ERROR\: (.*)/s ) { + my $msg = $1; + $msg =~ s/[\n\r]/ /gos; + TWiki::Func::writeDebug( "- $pluginName ERROR: Can't read $url ($msg)" ); + return "#ERROR: Can't read $url ($msg)"; + } + if( $text =~ /HTTP\/[0-9\.]+\s*([0-9]+)\s*([^\n]*)/s ) { + unless( $1 == 200 ) { + TWiki::Func::writeDebug( "- $pluginName ERROR: Can't read $url ($1 $2)" ); + return "#ERROR: Can't read $url ($1 $2)"; + } + } + $text =~ s/\r\n/\n/gos; + $text =~ s/\r/\n/gos; + $text =~ s/^.*?\n\n(.*)/$1/os; # strip header + unless( $text =~ /.{128}/ ) { + # spam-merge file is too short, possibly temporary read error + TWiki::Func::writeDebug( "- $pluginName WARNING: Content of $url is too short, using old cache" ); + TWiki::Func::saveFile( _makeFileName( "spam_merge_err" ), $text ); + $text = TWiki::Func::readFile( $cacheFile ); # read old cache content + } + TWiki::Func::saveFile( $cacheFile, $text ); + return $text; +} + + +# ========================= +sub _handleSpamList +{ + my ( $theAction, $theValue ) = @_; + my $fileName = _makeFileName( "spam_list", 0 ); + writeDebug( "_handleSpamList( Action: $theAction, value: $theValue, file: $fileName )" ); + my $text = TWiki::Func::readFile( $fileName ) || "# The spam-list is a generated file, do not edit\n"; + if( $theAction eq "read" ) { + $text =~ s/^\#[^\n]*\n//s; + return $text; + } + + my @errorMessages; + my @infoMessages; + foreach my $item (split( /,\s*/, $theValue )) { + $item =~ s/^\s+//; + $item =~ s/\s+$//; + + if( $theAction eq "add" ) { + if( $text =~ /\n\Q$item\E\n/s ) { + push @infoMessages, "Warning: Spam pattern '$item' is already on the list"; + next; + } + $text .= "$item\n"; + push @infoMessages, "Note: Added spam pattern '$item'"; + unlink( _makeFileName( "spam_regex" ) ); # remove cache + + } elsif( $theAction eq "remove" ) { + unless( ( $item ) && ( $text =~ s/(\n)\Q$item\E\n/$1/s ) ) { + push @errorMessages, "Error: Spam pattern '$item' not found"; + next; + } + push @infoMessages, "Note: Removed spam pattern '$item'"; + unlink( _makeFileName( "spam_regex" ) ); # remove cache + + } else { + # never reach + return "Error: invalid action '$theAction'"; + } + } + + if (@errorMessages) { + writeDebug("spamlist=$text"); + return '<div class="twikiAlert">' . join("<br /> ", @errorMessages) . '</div>'; + + } else { + if (@infoMessages) { + # SMELL: overwrites a concurrent save + writeDebug("spamlist=$text"); + TWiki::Func::saveFile( $fileName, $text ); + return '<br />' . join( "<br /> ", @infoMessages ); + + } else { + return 'Error: done nothing'; + } + } +} + +# ========================= sub _handleBlackList { my( $theAttributes, $theWeb, $theTopic ) = @_; - my $action = &TWiki::Func::extractNameValuePair( $theAttributes, "action" ); - my $value = &TWiki::Func::extractNameValuePair( $theAttributes, "value" ); + my $action = TWiki::Func::extractNameValuePair( $theAttributes, "action" ); + my $value = TWiki::Func::extractNameValuePair( $theAttributes, "value" ); my $text = ""; + + writeDebug( "_handleBlackList( Action: $action, value: $value, topic: $theWeb.$theTopic )" ); if( $action eq "ban_show" ) { $text = _handleBanList( "read", "" ); $text =~ s/[\n\r]+$//os; $text =~ s/[\n\r]+/, /gos; + } elsif( $action eq "spam_show" ) { + $text = _handleSpamList( "read", "" ); + $text =~ s/[\n\r]+$//os; + $text =~ s/[\n\r]+/, /gos; + } elsif( $action eq "user_score" ) { $text = $userScore; - } elsif( $action =~ /^(ban_add|ban_remove)$/ ) { + } elsif( $action =~ /^(ban_add|ban_remove|spam_add|spam_remove)$/ ) { + my $anchor = "#BanList"; if( "$theWeb.$theTopic" eq "$installWeb.$pluginName" ) { my $wikiName = &TWiki::Func::userToWikiName( $user ); - if( &TWiki::Func::checkAccessPermission( "CHANGE", $wikiName, "", $pluginName, $installWeb ) ) { + if( TWiki::Func::checkAccessPermission( "CHANGE", $wikiName, "", $pluginName, $installWeb ) ) { if( $action eq "ban_add" ) { - $text = _handleBanList( "add", $value ); + $text .= _handleBanList( "add", $value ); _writeLog( "BANLIST add: $value, by user" ); + } elsif( $action eq "ban_remove" ) { + $text .= _handleBanList( "remove", $value ); + _writeLog( "BANLIST delete: $value by user" ); + } elsif( $action eq "spam_add" ) { + $text .= _handleSpamList( "add", $value ); + $anchor = "#SpamList"; + _writeLog( "SPAMLIST add: $value, by user" ); } else { - $text = _handleBanList( "remove", $value ); - _writeLog( "BANLIST delete: $value by user" ); + $text .= _handleSpamList( "remove", $value ); + $anchor = "#SpamList"; + _writeLog( "SPAMLIST delete: $value by user" ); } } else { - $text = "Error: You do not have permission to add IP addresses"; + $text = "Error: You do not have permission to maintain the list"; } } else { - $text = "Error: For use on $installWeb.$pluginName topic only"; + $text = "Error: For use on $installWeb.$pluginName topic only"; } - $text .= " [ [[$theWeb.$theTopic][OK]] ]"; + $text .= " [ [[$theWeb.$theTopic$anchor][OK]] ]"; } return $text; } @@ -219,36 +421,68 @@ # ========================= sub _handleBanList { - my ( $theAction, $theIP ) = @_; + my ( $theAction, $theIPs ) = @_; my $fileName = _makeFileName( "ban_list", 0 ); - TWiki::Func::writeDebug( "- ${pluginName}::_handleBanList( Action: $theAction, IP: $theIP, file: $fileName )" ) if $debug; + writeDebug( "_handleBanList( Action: $theAction, IP: $theIPs, file: $fileName )" ); my $text = TWiki::Func::readFile( $fileName ) || "# The ban-list is a generated file, do not edit\n"; if( $theAction eq "read" ) { $text =~ s/^\#[^\n]*\n//s; return $text; + } - } elsif( $theAction eq "add" ) { + my @errorMessages; + my @infoMessages; + foreach my $theIP (split( /,\s*/, $theIPs )) { + $theIP =~ s/^\s+//; + $theIP =~ s/\s+$//; + + if( $theAction eq "add" ) { unless( ( $theIP ) && ( $theIP =~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ ) ) { - return "Error: Invalid IP address $theIP"; + push @errorMessages, "Error: Invalid IP address '$theIP'"; + next; } - if( $text =~ s/(\n)\Q$theIP\E\n/$1/s ) { - return "Error: IP address $theIP is already on the list"; + + if( $text =~ /\n\Q$theIP\E\n/s ) { + push @infoMessages, "Warning: IP address '$theIP' is already on the list"; + next; } + $text .= "$theIP\n"; + + push @infoMessages, "Note: Added IP address '$theIP'"; + + } elsif( $theAction eq "remove" ) { + unless( ( $theIP ) && ( $text =~ s/(\n)\Q$theIP\E\n/$1/s ) ) { + push @errorMessages, "Error: IP address '$theIP' not found"; + next; + } + push @infoMessages, "Note: Removed IP address '$theIP'"; + + } else { + # never reach + return "Error: invalid action '$theAction'"; + } + } + + if (@errorMessages) { + writeDebug("banlist=$text"); + return '<div class="twikiAlert">' . join("<br /> ", @errorMessages) . '</div>'; + + } else { + if (@infoMessages) { + # SMELL: overwrites a concurrent save + writeDebug("banlist=$text"); TWiki::Func::saveFile( $fileName, $text ); unless( -e "$fileName" ) { # assuming save failed because of missing dir _makeFileDir(); TWiki::Func::saveFile( $fileName, $text ); } - return "Note: Added IP address $theIP"; + return '<br />' . join( "<br /> ", @infoMessages ); - } elsif( $theAction eq "remove" ) { - unless( ( $theIP ) && ( $text =~ s/(\n)\Q$theIP\E\n/$1/s ) ) { - return "Error: IP address $theIP not found"; - } - TWiki::Func::saveFile( $fileName, $text ); - return "Note: Removed IP address $theIP"; + } else { + return 'Error: done nothing'; + } } } @@ -259,7 +493,7 @@ # read/update/save event logs my $fileName = _makeFileName( "event_log" ); - TWiki::Func::writeDebug( "- ${pluginName}::_handleEventLog( IP: $theIP, type: $theType, query: $theQueryString )" ) if $debug; + writeDebug( "_handleEventLog( IP: $theIP, type: $theType, query: $theQueryString )" ); my $text = TWiki::Func::readFile( $fileName ) || "# The event-list is a generated file, do not edit\n"; my $time = time(); $text .= "$time, $theIP, $theType"; @@ -335,7 +569,7 @@ if( TWiki::Func::getPreferencesFlag( "\U$pluginName\E_LOGACCESS" ) ) { # FIXME: Call to unofficial function TWiki::Store::writeLog( "blacklist", "$web.$topic", $theText ); - ##TWiki::Func::writeDebug( "BLACKLIST access by $remoteAddr, $web/$topic, $theText" ); + writeDebug( "BLACKLIST access by $remoteAddr, $web/$topic, $theText" ); } } Added: twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/pub/TWiki/BlackListPlugin/.htaccess =================================================================== --- twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/pub/TWiki/BlackListPlugin/.htaccess 2005-10-29 22:37:28 UTC (rev 7222) +++ twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/pub/TWiki/BlackListPlugin/.htaccess 2005-10-29 23:17:08 UTC (rev 7223) @@ -0,0 +1,3 @@ +<Files "*"> + deny from all +</Files> Added: twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/templates/oopsblacklist.tmpl =================================================================== --- twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/templates/oopsblacklist.tmpl 2005-10-29 22:37:28 UTC (rev 7222) +++ twiki/branches/DEVELOP/twikiplugins/BlackListPlugin/templates/oopsblacklist.tmpl 2005-10-29 23:17:08 UTC (rev 7223) @@ -0,0 +1,10 @@ +%TMPL:INCLUDE{"twiki"}% +%TMPL:DEF{"titleaction"}%(oops) %TMPL:END% +%TMPL:DEF{"webaction"}% *Attention* %TMPL:END% +%TMPL:DEF{"heading"}%Blacklisted IP address%TMPL:END% +%TMPL:DEF{"message"}% +%PARAM1% +%TMPL:END% +%TMPL:DEF{"topicaction"}% + [[http://en.wikipedia.org/wiki/Link_spam][OK]] %TMPL:END% +%TMPL:P{"oops"}% |