<?php

    /*
    Copyright 2006 Paul Westbrook (paul@westbrooks.org)
    
    This program is free software; 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 program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    */

	lt_include( PLOG_CLASS_PATH."class/security/pipelinefilter.class.php" );

    
    $HttpBLError = false;

    // custom error code that will be returned to the pipeline whenever an
    // error is found... Be careful so as to not to have two different modules
    // use the same code!!
    define( "HTTPBL_SPAM", 820 );

	class HttpBLFilter extends PipelineFilter 
	{

    	function HttpBLFilter( $pipelineRequest )
        {
        	$this->PipelineFilter( $pipelineRequest );
        }

        function filter()
        {
           global $HttpBLError;
           
           $HttpBLError = false;

        	// get some info
            $blogInfo = $this->_pipelineRequest->getBlogInfo();
            $request  = $this->_pipelineRequest->getHttpRequest();

        	// check if this section has been enabled or disabled
            $blogSettings = $blogInfo->getSettings();
		    $pluginEnabled = $blogSettings->getValue( "plugin_httpbl_enabled" );
            if( !$pluginEnabled) {
            	// if not, nothing to do here...
                //_debug("ip address filter not enabled! quitting...<br/>");
            	return new PipelineResult();
            }

            // we only have to filter the contents if the user is posting a comment
            // so there's no point in doing anything else if that's not the case
            if(( $request->getValue( "op" ) != "AddComment" ) && ( $request->getValue( "op" ) != "AddTrackback" )) {
            	$result = new PipelineResult();
                return $result;
            }

            // if this is already rejected, there is no reason to do anything here
            if ( $this->_pipelineRequest->getRejectedState() )
                return new PipelineResult();
                
  
            lt_include( PLOG_CLASS_PATH."class/net/client.class.php" );	
            $clientIp = Client::getIp();
 
            $apiKey = $blogSettings->getValue( "plugin_httpbl_api_key" );
            
            $maxReportAge = $blogSettings->getValue( "plugin_httpbl_max_stale" );
            


            // perform the dns query
            if(!$this->checkHttpBLForIP($apiKey, $maxReportAge, $clientIp)) {
               $locale = $blogInfo->getLocale();
               $result = new PipelineResult( false, HTTPBL_SPAM, $locale->tr("error_comment_spam_throw_away") );
               return $result; 
            }


            // if everything went fine, we can say so by returning
            // a positive PipelineResult object
            $result = new PipelineResult( true );
            
            return $result;
        }
        

        function checkHttpBLForIP( $apiKey, $maxReportAge, $clientIP )
        {
            $rev = array_reverse(explode('.', $clientIP));
        
            $lookup = $apiKey . '.' . implode('.', $rev) . '.' . 'dnsbl.httpbl.org.';
        
            $resultIp = $this->fixed_gethostbyname($lookup);
            
            if ($resultIp == false)
            {
                // This host is not in the http:bl, so let it through
                return true;
            }
            
            // Split the response
            $splitResponse = explode('.', $resultIp);
            
            // first make sure that the first octet is 127, if it is not
            // the query wasn't formatted correctly.  Give the client the benefit of 
            // the doubt
            if ($splitResponse[0] != 127)
            {
                return true;
            }
            
            // The second octet has information about how stale their information is
            // the range is 0 - 255 days
            // XXX this should be a setting that the user can configure how stale the data
            // can be when they consider a client to be spam
            
            // The thrid octet is the "threat score" for the ip address.  The range is 
            // 0 - 255 where 255 is extremely threatening and 0 is where no score has been 
            // assigned
            // XXX the user should be able to specify the minimum threat score that should be used
            
            // The forth octet is the type of visitor, in a bit set.  
            //  0	Search Engine
            //  1	Suspicious
            //  2	Harvester
            //  4	Comment Spammer
            // XXX There should be check boxes which types of clients they want to block
            // but for now, assume that if anything marked as a comment spammer is to be blocked
            
            if (($splitResponse[3] & 4) == 4)
            {
                // Make sure that the last report is with in the age range
                // that the user has specified
                if ($splitResponse[1] <= $maxReportAge)
                {
                    return false;               
                }
            }
            
        
            return true ;
        }
        
        
        function fixed_gethostbyname ($host) {
            // Try the lookup as normal...
            $ip = gethostbyname($host);
            // ...but if it fails, FALSE is returned instead of the unresolved host
            if ($ip != $host) { return $ip; } else return false;
        }


    }

?>
