### Eclipse Workspace Patch 1.0
#P Redmine-trunk
Index: lib/akismet.rb
===================================================================
--- lib/akismet.rb	(revision 0)
+++ lib/akismet.rb	(revision 0)
@@ -0,0 +1,134 @@
+# Akismet
+#
+# Author:: David Czarnecki
+# Copyright:: Copyright (c) 2005 - David Czarnecki
+# License:: BSD
+# Modified by Dieter Komendera, Sparkling Studios:
+#   append blog= to data string (Akismet said it is required)
+#   changed require 'net/HTTP' to require 'net/http' (to work for me unter GNU/Linux)
+# Akismet Key - 4ebc7e35c512
+class Akismet
+
+  require 'net/http'
+  require 'uri'
+  
+  STANDARD_HEADERS = {
+    'User-Agent' => 'Akismet Ruby API/1.0',
+    'Content-Type' => 'application/x-www-form-urlencoded'
+  }
+  
+  # Create a new instance of the Akismet class
+  #
+  # apiKey 
+  #   Your Akismet API key
+  # blog 
+  #   The blog associated with your api key
+  def initialize(apiKey, blog)
+    @apiKey = apiKey
+    @blog = blog
+  end
+  
+  # Set proxy information 
+  #
+  # proxyHost
+  #   Hostname for the proxy to use
+  # proxyPort
+  #   Port for the proxy
+  def setProxy(proxyHost, proxyPort) 
+    @proxyPort = proxyPort
+    @proxyHost = proxyHost
+  end
+  
+  def valid?
+    (!@apiKey.blank? && !@blog.blank? && verifyAPIKey)
+  end
+    
+  # Call to check and verify your API key.
+  def verifyAPIKey
+    http = Net::HTTP.new('rest.akismet.com', 80, @proxyHost, @proxyPort)
+    path = '/1.1/verify-key'
+    
+    data="key=#{@apiKey}&blog=#{@blog}"
+    
+    resp, data = http.post(path, data, STANDARD_HEADERS)
+    (data == "valid")
+  end
+ 
+  # Internal call to Akismet. Prepares the data for posting to the Akismet service.
+  #
+  # akismet_function
+  #   The Akismet function that should be called
+  # user_ip (required)
+  #    IP address of the comment submitter.
+  # user_agent (required)
+  #    User agent information.
+  # referrer (note spelling)
+  #    The content of the HTTP_REFERER header should be sent here.
+  # permalink
+  #    The permanent location of the entry the comment was submitted to.
+  # comment_type
+  #    May be blank, comment, trackback, pingback, or a made up value like "registration".
+  # comment_author
+  #    Submitted name with the comment
+  # comment_author_email
+  #    Submitted email address
+  # comment_author_url
+  #    Commenter URL.
+  # comment_content
+  #    The content that was submitted.
+  # Other server enviroment variables
+  #    In PHP there is an array of enviroment variables called $_SERVER which contains information about the web server itself as well as a key/value for every HTTP header sent with the request. This data is highly useful to Akismet as how the submited content interacts with the server can be very telling, so please include as much information as possible.  
+  def callAkismet(akismet_function, user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+    http = Net::HTTP.new("#{@apiKey}.rest.akismet.com", 80, @proxyHost, @proxyPort)
+    path = "/1.1/#{akismet_function}"        
+    
+    data = "blog=#{@blog}&user_ip=#{user_ip}&user_agent=#{user_agent}&referrer=#{referrer}&permalink=#{permalink}&comment_type=#{comment_type}&comment_author=#{comment_author}&comment_author_email=#{comment_author_email}&comment_author_url=#{comment_author_url}&comment_content=#{comment_content}"
+    if (other != nil) 
+      other.each_pair {|key, value| data.concat("&#{key}=#{value}")}
+    end
+            
+    resp, data = http.post(path, data, STANDARD_HEADERS)
+
+    return (data != "false")
+  end
+  
+  protected :callAkismet
+
+  # This is basically the core of everything. This call takes a number of arguments and characteristics about the submitted content and then returns a thumbs up or thumbs down. Almost everything is optional, but performance can drop dramatically if you exclude certain elements.
+  #
+  # user_ip (required)
+  #    IP address of the comment submitter.
+  # user_agent (required)
+  #    User agent information.
+  # referrer (note spelling)
+  #    The content of the HTTP_REFERER header should be sent here.
+  # permalink
+  #    The permanent location of the entry the comment was submitted to.
+  # comment_type
+  #    May be blank, comment, trackback, pingback, or a made up value like "registration".
+  # comment_author
+  #    Submitted name with the comment
+  # comment_author_email
+  #    Submitted email address
+  # comment_author_url
+  #    Commenter URL.
+  # comment_content
+  #    The content that was submitted.
+  # Other server enviroment variables
+  #    In PHP there is an array of enviroment variables called $_SERVER which contains information about the web server itself as well as a key/value for every HTTP header sent with the request. This data is highly useful to Akismet as how the submited content interacts with the server can be very telling, so please include as much information as possible.
+  def commentCheck(user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+    return callAkismet('comment-check', user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+  end
+  
+  # This call is for submitting comments that weren't marked as spam but should have been. It takes identical arguments as comment check.
+  # The call parameters are the same as for the #commentCheck method.
+  def submitSpam(user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+    callAkismet('submit-spam', user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+  end
+  
+  # This call is intended for the marking of false positives, things that were incorrectly marked as spam. It takes identical arguments as comment check and submit spam.
+  # The call parameters are the same as for the #commentCheck method.
+  def submitHam(user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+    callAkismet('submit-ham', user_ip, user_agent, referrer, permalink, comment_type, comment_author, comment_author_email, comment_author_url, comment_content, other)
+  end
+end
Index: db/migrate/20090515104800_add_spam_columns.rb
===================================================================
--- db/migrate/20090515104800_add_spam_columns.rb	(revision 0)
+++ db/migrate/20090515104800_add_spam_columns.rb	(revision 0)
@@ -0,0 +1,9 @@
+class AddSpamColumns < ActiveRecord::Migration
+  def self.up
+    add_column :messages, :spam, :boolean, :default => false
+  end
+
+  def self.down
+    remove_column :messages, :spam
+  end
+end
Index: config/settings.yml
===================================================================
--- config/settings.yml	(revision 2744)
+++ config/settings.yml	(working copy)
@@ -153,3 +153,5 @@
   default: 0
 openid:
   default: 0
+akismet_api_key:
+  default: 
\ No newline at end of file
Index: app/views/settings/_general.rhtml
===================================================================
--- app/views/settings/_general.rhtml	(revision 2744)
+++ app/views/settings/_general.rhtml	(working copy)
@@ -38,6 +38,9 @@
 
 <p><label><%= l(:setting_diff_max_lines_displayed) %></label>
 <%= text_field_tag 'settings[diff_max_lines_displayed]', Setting.diff_max_lines_displayed, :size => 6 %></p>
+
+<p><label><%= l(:setting_akismet_api_key) %></label>
+<%= text_field_tag 'settings[akismet_api_key]', Setting.akismet_api_key, :size => 20 %></p>
 </div>
 
 <%= submit_tag l(:button_save) %>
Index: app/models/message.rb
===================================================================
--- app/models/message.rb	(revision 2744)
+++ app/models/message.rb	(working copy)
@@ -45,6 +45,7 @@
   def validate_on_create
     # Can not reply to a locked topic
     errors.add_to_base 'Topic is locked' if root.locked? && self != root
+    errors.add_to_base 'Spam blocked' if spam?
   end
   
   def after_create
@@ -87,4 +88,28 @@
   def add_author_as_watcher
     Watcher.create(:watchable => self.root, :user => author)
   end
+  
+  def akismet
+    @akismet ||= Akismet.new(Setting.akismet_api_key, "<<<URL FOR THE SITE?>>>")
+  end
+  
+  # If the Akismet details are valid, and Akismet thinks this is a non-spam
+  # comment, this method will return true
+  def spam?
+    if akismet.valid?
+      # We do the negation because true means spam, false means ham
+      akismet.commentCheck(
+        request.remote_ip,              # remote IP
+        request.env['HTTP_USER_AGENT'], # user agent
+        request.env['HTTP_REFERER'],    # http referer
+        'comment',                      # comment type
+        self.author.name.to_s,          # author name
+        self.author.email,              # author email
+        self.content,                   # comment text
+        {}                              # other
+      )
+    else
+      true
+    end
+  end
 end

