Bad Behavior plugin for flatpress

Current mood: 
Bad behavior (also known as BB2) is a great anti-spam-robot plugin available for wordpress, drupal and some other software. I did a trip on wordpress for a while before returning back to flatpress and I decided to “bring” the plugin with me.

Actually the porting wasn’t that hard to do, but getting away from SQL database for logging was another issue:P So, for anyone out there wanting this plugin in the fp-environment here it is! But a bit of php-knowledge will help even if it’s kinda straight on:)

Installation instructions:
  1. First get the original package from Bad Behaviors download page
  2. Go register at the Project Honeypot site to get your key for the http:BL IP-checking.
  3. On your server, in the directory fp-plugins: Make a new directory named “bad-behavior” (Without quotes, and mind the spelling!
  4. Unzip the previously downloaded original package in this new directory. You will get some other plugin files and a directory with core files now.
  5. Make a new file named “plugin.bad-behavior.php” among the other plugin files (which you don’t need and can delete if you like) and put this code in it:
    <?php
    /*
    Plugin Name: Bad Behavior
    Plugin URI: http://macmathan.info/category/bad-behavior/
    Description: Bad Behavior - detects and blocks unwanted Web accesses
    Author: David MacMathan
    Version: 0.2.0.29
    Author URI: http://macmathan.info/
    */ 
    
    /*
    Bad Behavior - detects and blocks unwanted Web accesses
    Copyright (C) 2005-2006 Michael Hampton
    
    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.
    
    As a special exemption, you may link this program with any of the
    programs listed below, regardless of the license terms of those
    programs, and distribute the resulting program, without including the
    source code for such programs: ExpressionEngine
    
    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., 675 Mass Ave, Cambridge, MA 02139, USA.
    
    Please report any problems to badbots AT ioerror DOT us
    */
    
    ###############################################################################
    ###############################################################################
    $bb2_mtime = explode(" ", microtime());        //added from wp-plugin
    $bb2_timer_start = $bb2_mtime[1] + $bb2_mtime[0];  //added from wp-plugin
    
    
    define('BB2_CWD', dirname(__FILE__));
    
    // Settings you can adjust for Bad Behavior.
    // Most of these are unused in non-database mode.
    function bb2_read_settings() {
    $settings = array(
        'log_table' => 'bad_behavior',
        'display_stats' => false,     // Default true, needs DB to work.
        'strict' => true,      // Default false, true for paranoids :)
        'verbose' => false,
        'logging' => true,     // Turn off if you don't have DB or flatfile log patch.
        'httpbl_key' => '',        // Insert http:BL key here
        'httpbl_threat' => '25',
        'httpbl_maxage' => '30',
        'offsite_forms' => false,
        'log_file' => '/bb2.log'  // path to your flat file log file relative server root - NOTE: you should create an empty starter file if you use logging
    );
    return $settings;
    }
    
    // Bad Behavior callback functions.
    
    // Return current time in the format preferred by your database.
    function bb2_db_date() {
        return gmdate('Y-m-d H:i:s'); // Example is MySQL format
    }
    
    // Return affected rows from most recent query.
    function bb2_db_affected_rows() {
        return false;
    }
    
    // Escape a string for database usage
    function bb2_db_escape($string) {
        // return mysql_real_escape_string($string);
        return $string;    // No-op when database not in use.
    }
    
    // Return the number of rows in a particular query.
    function bb2_db_num_rows($result) {
        if ($result !== FALSE)
            return count($result);
        return 0;
    }
    
    // Run a query and return the results, if any.
    // Should return FALSE if an error occurred.
    // Bad Behavior will use the return value here in other callbacks.
    function bb2_db_query($query) {
        return FALSE;
    }
    
    // Return all rows in a particular query.
    // Should contain an array of all rows generated by calling mysql_fetch_assoc()
    // or equivalent and appending the result of each call to an array.
    function bb2_db_rows($result) {
        return $result;
    }
    
    // Return emergency contact email address.
    function bb2_email() {
        return "example@example.com";  // You need to change this.
    }
    
    
    // write settings to database
    function bb2_write_settings($settings) {
        return false;
    }
    
    // installation
    function bb2_install() {
        return false;
    }
    
    // Screener
    // Insert this into the <head> section of your HTML through a template call
    // or whatever is appropriate. This is optional we'll fall back to cookies
    // if you don't use it.
    function bb2_insert_head() {
        global $bb2_timer_total;      //added from wp-plugin
        global $bb2_javascript;
        echo "\n<!-- Bad Behaviour run time: ". number_format(1000 * $bb2_timer_total, 3)." ms -->\n"; //added from wp
        echo $bb2_javascript;
    }
    
    // Display stats? This is optional.
    function bb2_insert_stats($force = false) {
        $settings = bb2_read_settings();
    
        if ($force || $settings['display_stats']) {
            $blocked = bb2_db_query("SELECT COUNT(*) FROM " . $settings['log_table'] . " WHERE `key` NOT LIKE '00000000'");
            if ($blocked !== FALSE) {
                echo sprintf('<p><a href="http://www.bad-behavior.ioerror.us/">%1$s</a> %2$s <strong>%3$s</strong> %4$s</p>', __('Bad Behavior'), __('has blocked'), $blocked[0]["COUNT(*)"], __('access attempts in the last 7 days.'));
            }
        }
    }
    
    // Return the top-level relative path of wherever we are (for cookies)
    // You should provide in $url the top-level URL for your site.
    function bb2_relative_path() {
        //$url = parse_url(get_bloginfo('url'));
        //return $url['path'] . '/';
        return '/';
    }
    
    // Calls inward to Bad Behavor itself.
    require_once(BB2_CWD . "/bad-behavior/version.inc.php");
    require_once(BB2_CWD . "/bad-behavior/core.inc.php");
    bb2_install();  // FIXME: see above
    
    bb2_start(bb2_read_settings());
    
    add_action('wp_head', 'bb2_insert_head'); // hook to fp
    //add_action('wp-footer','bb2_insert_stats'); // Use only with DB active, no need else
    
    $bb2_mtime = explode(" ", microtime());        //added from wp-plugin
    $bb2_timer_stop = $bb2_mtime[1] + $bb2_mtime[0];   //added from wp-plugin
    $bb2_timer_total = $bb2_timer_stop - $bb2_timer_start;     //added from wp-plugin
    
    ?>
    
  6. I wanted some logging without SQL database involved (hey it’s flatpress isn’t it? :O) and therefore I had to hack the core a little. You don’t need to do this unless you want some logging feature, and that file will grow so keep an eye on it’s size. Nice for debugging/verification issues though. Here is a diff, if you don’t know how a .diff-file works, then don’t use it:) :
    --- core.inc.php.orig  2009-09-25 00:08:00.000000000 +0200
    +++ core.inc.php   2009-10-11 12:18:18.000000000 +0200
    @@ -8,6 +8,7 @@
     define('BB2_COOKIE', 'bb2_screener_');
     
     require_once(BB2_CORE . "/functions.inc.php");
    +require_once(BB2_CORE . "/responses.inc.php");
     
     // Our log table structure
     function bb2_table_structure($name)
    @@ -39,19 +40,32 @@
       $request_uri = bb2_db_escape($package['request_uri']);
       $server_protocol = bb2_db_escape($package['server_protocol']);
       $user_agent = bb2_db_escape($package['user_agent']);
    -   $headers = "$request_method $request_uri $server_protocol\n";
    +   $headers = "";
       foreach ($package['headers'] as $h => $v) {
    -       $headers .= bb2_db_escape("$h: $v\n");
    +       if ($h == "Cookie") {
    +           $headers .= bb2_db_escape("$h: $v, ");
    +       }
       }
       $request_entity = "";
       if (!strcasecmp($request_method, "POST")) {
           foreach ($package['request_entity'] as $h => $v) {
    -           $request_entity .= bb2_db_escape("$h: $v\n");
    +           if ($h == "Cookie") {
    +               $request_entity .= bb2_db_escape("$h: $v, ");
    +           }
           }
       }
    -   return "INSERT INTO `" . bb2_db_escape($settings['log_table']) . "`
    -       (`ip`, `date`, `request_method`, `request_uri`, `server_protocol`, `http_headers`, `user_agent`, `request_entity`, `key`) VALUES
    -       ('$ip', '$date', '$request_method', '$request_uri', '$server_protocol', '$headers', '$user_agent', '$request_entity', '$key')";
    +   // Create support key
    +   $ip_split = explode(".", $_SERVER['REMOTE_ADDR']);
    +   $ip_hex = "";
    +   foreach ($ip_split as $octet) {
    +       $ip_hex .= str_pad(dechex($octet), 2, 0, STR_PAD_LEFT);
    +   }
    +   $support_key = implode("-", str_split("$ip_hex$key", 4));
    +$response = bb2_get_response($key);
    +$logFile = $_SERVER['DOCUMENT_ROOT'] . $settings['log_file'];
    +$logData = $date."|".$support_key."|".$request_method."|".$server_protocol."|".$ip."|".$request_uri."|".$response['log']."|".$user_agent."|".$headers."|".$request_entity."\n";
    +@file_put_contents($logFile,$logData,FILE_APPEND|LOCK_EX);
    +return;
     }
     
     // Kill 'em all!
    
    Note that You will NEED to create the empty log file at the place specified in the plugin module. I have mine in the server root, and protected with .htaccess so it isn’t readable, you can put it where it’s suitable for you;)
  7. Double check the settings in the plugin file. Your key, log settings, trigger levels and everything else… It’s (hopefully) reasonably documented in the code above.
  8. Log in to your flatpress admin area and activate the plugin.
  9. Sit back and enjoy the fact that many spammers and malicious morons now are blocked at your site, and do check back on the log file once in a while if you use one.
  10. Remember that Bad Behavior needs updates once in a while. Check Bad Behaviors download page from time to time, or get on the mailing list or RSS feed there so you stay up to date! (This plugin should survive most core updates, except that you will need to repatch the flat file log function if the core.inc.php is changed in the original package.)