<?php if (!defined('PmWiki')) exit();
/*  Copyright 2004 Patrick R. Michaud (pmichaud@pobox.com)
    This file is blocklist.php for PmWiki, you can redistribute it 
    and/or modify it under the terms of the GNU General Public License 
    as published by the Free Software Foundation; either version 2 of 
    the License, or (at your option) any later version.  
    
*/

/**
    This script adds blocklisting capabilities to PmWiki.  By default,
    it searches through Main.Blocklist (or whatever pages are specified
    by the $BlocklistPages variable) looking for strings of the form
    "block:something" to be excluded from any posting to the site.
    For example, if Main.Blocklist contains "block:spam.com", then
    any posting to "spam.com" will be disallowed.
    
    Crisses Modifications:
    Looks for patterns that begin with "block:" and
    match to the end of the line or the next "block:", which allows for phrases.  If you add
    block:no spam
    Then posts that contain the phrase "no spam" will be blocked.
    Works with punctuation, also.  Phrases are trimmed, so "block: no spam" works also.

    In addition, the script scans the Main.Blocklist page for 
    IP addresses of the form "a.b.c.d", "a.b.c.*", or "a.b.*.*" and blocks 
    any postings coming from a host matching the IP address or
    address range.

    To use the script, simply place it in the cookbook/ directory
    and add the following line to local/config.php:

        include_once('cookbook/blocklist.php');

    The script also sets the variables $Blocklisted and $WhyBlockedFmt
    so that the administrator can take actions other than simply blocking
    the post.  $Blocklisted counts the number of "offenses" in the attemped post.
    $WhyBlockedFmt is an array containing string messages for
    the reasons the post was blocked.  This makes for easy emails to an admin
    or data transfer to a security script.


USAGE -- this is how I tested it in config.php:
    
    
// ------- blocklist settings -------

// Only bother with this info if this is an edit action
if ($action=='edit') {

    // get the IP address of the poster
    $editip = $_SERVER["REMOTE_ADDR"];
    
    //Uncomment for poster to see a message on the edit page about why they have been blocked.
    # $EnableWhyBlocked = "1";

    // run the script
    include_once('cookbook/blocklist.php');

    // if the script found blocked content
    if ($Blocklisted) {
        
        // compose the contents portion of the email to be sent:

       	// give the page URL posted to
        $contents = "The poster was calling: $ScriptUrl/$pagename\n"; // page URL

        // give the IP address
        $contents .= "Blocked poster is at $editip.\n";
        
        // Let the admin know why the post is blocked
        $contents .= "$WhyBlockedFmt\n\n";
        
        // Give the original post contents, in case it's legit, has
        // additional content that ought to be blocked, or needs
        // to be reported to someone.
        $contents .= "The body of the attempted post was:\n\n" . @$_POST['text'];
        
        // If you want to have the end of the webserver logfile attached, use these lines
        // (If you don't have permissions for reading the mail log, comment out these lines and
        //	report website abuse to your system's administrator.)
        $weblog = '/var/log/httpd/access_log';
        $contents .= "\n\n------Access Log Tail------\n" . `tail -n 20 $weblog`;
        
        
        // compose the email with the following attributes:
        
        // comma-separated addresses to send the mail to:
        $emailto = "crisses"; 
        
        // the subject line to send it out with (includes a "score" for how bad it is):
        $subjectline = "Blocked $WikiTitle Post (Score $Blocklisted)";
        
        // email address the mail should be from:
        $mailfrom = "mantra@eclectictech.net";
            
        // This line sends the mail.  Do not alter (unless you know what you
        //  are doing).  Comment out if you do not want emails regarding blocked posts.
        mail("$emailto","$subjectline","$contents","$mailfrom");

    }
}

// ------- End blocklist settings -------


Other variables available:

$BlocklistPages is an array of the pages that are used for the blocklist.  Default = "Site.Blocklist", and if it exists it includes "Main.Blocklist".  User defining this overrides either file as default.

$BlocklistMessageFmt is the format for the "you've been blocked" statement on the edit screen.

$BlockIPMessageFmt is the message format preceeding the blocked IP address in mails and on the edit screen.

$BlockTextMessageFmt is the message format preceeding the blocked text content in mails and on the edit screen.

$EnableWhyBlocked is a switch for whether or not to display reasons a post is blocked to the user on the edit screen.  For security purposes, the default is off (0).

*/

//define the blocklist version number
define(BLOCKLIST_VERSION, '2.3.1');

$page_name = ResolvePageName($pagename);


// Set default for $BlocklistPages to Site/Blocklist &/OR Main/Blocklist.  
// If customized, remove values that don't exist.
if (!isset($BlocklistPages)) {
	if (PageExists('Site.Blocklist')) $BlocklistPages[] = 'Site.Blocklist';
	if (PageExists('Main.Blocklist')) $BlocklistPages[] = 'Main.Blocklist';
} else {
	foreach ($BlocklistPages as $key=>$value) {
		if (! PageExists($value)) unset($BlocklistPages[$key]);
	}
}
//print_r($BlocklistPages);

//print_r($_POST['text']);

# This script only applies to editing (or the commentbox cookbook script), so 
# if we're not editing or commenting, just return.  Partly borrowed from cmsb-blocklist.php.
# Not sure why he added "diff" to the actions that this runs on.  
if (! ($action == 'edit' || $action == 'comment')
	|| (count($BlocklistPages) < 1)
	|| in_array ($page_name, $BlocklistPages) ) { return; }

//Now that we know we're supposed to be here, set remaining variables
SDV($BlocklistMessageFmt, "<h3 class='wikimessage'>$[This post has been blocked by the administrator]</h3>");
SDV($BlockIPMessageFmt, "Remote server matches blocked server IP: ");
SDV($BlockTextMessageFmt, "Content matches blocklist content: ");
SDV($EnableWhyBlocked, "0"); // default is to not let them know
SDV($EnableBlocklistRegex, "0"); // enables Perl-compatible regular expression search

// Set the block counter
$Blocklisted = 0;

// check each blocklist page
foreach((array)$BlocklistPages as $oneblockpage) {
	$pn = FmtPageName($oneblockpage,$pagename);
	$page = ReadPage($pn);
	
	// Prep for IP checking
	list($ipa,$ipb,$ipc,$ipd) = explode('.', $_SERVER['REMOTE_ADDR']);
	
	// Check ip address against page text
	if (preg_match("/(\\D$ipa\\.$ipb\\.(\\D|$ipc)\\.($ipd)?\\D)/", $page['text'], $matchedip)) {
		$Blocklisted++; // increment counter
		$WhyBlockedFmt .= $BlockIPMessageFmt . $matchedip[1] . "\n"; // add a reason why the page is blocked
	}
  
  // When not tallying reasons, only run through the terms if the IP is not blocked
  if (!(($EnableWhyBlocked == 0) && ($Blocklisted > 0))) {
		
		//catch and fix multiple block:'s on a single line and leading whitespace 
		//(allows legacy format)
		$blocktext = preg_replace('/\\s*block:/', "\nblock:", $page['text']);
		// Make an array of text that follows "block:" incl spaces & punct
		preg_match_all('/block:\s*(.+)/', $blocktext, $blockterms);
	
		//compares entries in the blocklist to the post content
		// Check posted page content against blocklist
		// Note:  matches only on first, not repeated, offenses-per-criteria
		// If score/reasons are not being tallied, break out.
		foreach ($blockterms[1] as $blockitem){
			if (stristr(@$_POST['text'], $blockitem)) {
				$Blocklisted++;
				if ($EnableWhyBlocked == 0) break;
				$WhyBlockedFmt .= $BlockTextMessageFmt . $blockitem . "\n";
			}
		}
	}

	
  // Check Regex only if necessary
  if (($EnableBlocklistRegex == 1) && !(($EnableWhyBlocked == 0) && ($Blocklisted > 0))) {
		
		//catch and fix multiple block:'s on a single line and leading whitespace 
		//(allows legacy format)
		$blocktext = preg_replace('/\\s*regex:/', "\nregex:", $blocktext);

		// Make an array of text that follows "regex:" incl spaces & punct
		preg_match_all('/regex:(.+)/', $blocktext, $regexterms);
	
		//compares entries in the regex list to the post content
		// Note:  matches only on first, not repeated, offenses-per-criteria
		// If score/reasons are not being tallied, break out.
		foreach ($regexterms[1] as $oneregex){
			if (preg_match($oneregex, @$_POST['text'], $regexmatch)) {
				$Blocklisted++;
				if ($EnableWhyBlocked == 0) break;
				$WhyBlockedFmt .= $BlockTextMessageFmt . $regexmatch[0] . "\n";
			}
		}
	}

} //foreach ((array)$BlocklistPages)

// if the posting is blocked, then disallow the post 
if ($Blocklisted) { 
	$EnablePost = 0; 
	unset($_POST['post']); 
	unset($_POST['postattr']);
	unset($_POST['postedit']);
	// let the poster know they've been blocked
	$MessagesFmt[] .= $BlocklistMessageFmt;
	// if required, provide reasons
	if ($EnableWhyBlocked == 1) $MessagesFmt[] = '<pre class="blocklistmessage">' . $WhyBlockedFmt . "</pre>";
}

?>