Skip to content

Commit

Permalink
Refactor project structure. Added parser for report output for rules
Browse files Browse the repository at this point in the history
  • Loading branch information
sleroux committed Mar 11, 2013
1 parent 72f228b commit 00bf9ad
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 104 deletions.
3 changes: 2 additions & 1 deletion Rakefile
Expand Up @@ -18,7 +18,8 @@ Jeweler::Tasks.new do |gem|
gem.summary = %Q{Gem provides a direct ruby interface to spamd running on localhost or remotely}
gem.description = %Q{This gem makes it easy for developers to hand a body of text to spam assassin and ask get it's spam score, spam report etc. Supports the full Spamc protocol.}
gem.email = "kjp@brightleafsoftware.com"
gem.authors = ["Kevin Poorman"]
gem.authors = ["Kevin Poorman", "Stephan Leroux"]
gem.files.include 'lib/RubySpamAssassin/**.rb'
# Include your dependencies below. Runtime dependencies are required when using your gem,
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
Expand Down
11 changes: 7 additions & 4 deletions RubySpamAssassin.gemspec
Expand Up @@ -5,11 +5,11 @@

Gem::Specification.new do |s|
s.name = "RubySpamAssassin"
s.version = "1.0.2"
s.version = "1.0.3"

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Kevin Poorman"]
s.date = "2013-01-22"
s.authors = ["Kevin Poorman", "Stephan Leroux"]
s.date = "2013-03-11"
s.description = "This gem makes it easy for developers to hand a body of text to spam assassin and ask get it's spam score, spam report etc. Supports the full Spamc protocol."
s.email = "kjp@brightleafsoftware.com"
s.extra_rdoc_files = [
Expand All @@ -30,13 +30,16 @@ Gem::Specification.new do |s|
"features/step_definitions/RubySpamAssassin_steps.rb",
"features/support/env.rb",
"lib/RubySpamAssassin.rb",
"lib/RubySpamAssassin/report_parser.rb",
"lib/RubySpamAssassin/spam_client.rb",
"lib/RubySpamAssassin/spam_result.rb",
"spec/RubySpamAssassin_spec.rb",
"spec/spec_helper.rb"
]
s.homepage = "http://noeticpenguin.github.com/RubySpamAssassin/"
s.licenses = ["MIT"]
s.require_paths = ["lib"]
s.rubygems_version = "1.8.24"
s.rubygems_version = "1.8.23"
s.summary = "Gem provides a direct ruby interface to spamd running on localhost or remotely"

if s.respond_to? :specification_version then
Expand Down
2 changes: 1 addition & 1 deletion VERSION
@@ -1 +1 @@
1.0.2
1.0.3
101 changes: 3 additions & 98 deletions lib/RubySpamAssassin.rb
@@ -1,100 +1,5 @@
module RubySpamAssassin
class SpamResult

attr_accessor :response_version,
:response_code,
:response_message,
:spam,
:score,
:threshold,
:tags,
:report,
:content_length

#returns true if the message was spam, otherwise false
def spam?
(@spam == "True" || @spam == "Yes") ? true : false
end
end

class SpamClient

require 'socket'
require 'timeout'

def initialize(host="localhost", port=783, timeout=5)
@port = port
@host = host
@timeout =timeout
@socket = TCPSocket.open(@host, @port)
end

def reconnect
@socket = @socket || TCPSocket.open(@host, @port)
end

def send_symbol(message)
protocol_response = send_message("SYMBOLS", message)
result = process_headers protocol_response[0...2]
result.tags = protocol_response[3...-1].join(" ").split(',')
end

def check(message)
protocol_response = send_message("CHECK", message)
result = process_headers protocol_response[0...2]
end

def report(message)
protocol_response = send_message("REPORT", message)
result = process_headers protocol_response[0...2]
result.report = protocol_response[3..-1].join
end

def report_ifspam(message)
result = report(message).spam?
end

def skip
protocol_response = send_message("SKIP", message)
end

def ping
protocol_response = send_message("PING", message)
result = process_headers protocol_response[0]
end

alias :process :report

private
def send_message(command, message)
length = message.length
@socket.write(command + " SPAMC/1.2\r\n")
@socket.write("Content-length: " + length.to_s + "\r\n\r\n")
@socket.write(message)
@socket.shutdown(1) #have to shutdown sending side to get response
response = @socket.readlines
@socket.close #might as well close it now

response
end

def process_headers(headers)
result = SpamResult.new
headers.each do |line|
case line.chomp
when /(.+)\/(.+) (.+) (.+)/ then
result.response_version = $2
result.response_code = $3
result.response_message = $4
when /^Spam: (.+) ; (.+) . (.+)$/ then
result.score = $2
result.spam = $1
result.threshold = $3
when /Content-length: (.+)/ then
result.content_length = $1
end
end
result
end
end
autoload(:SpamClient, "RubySpamAssassin/spam_client")
autoload(:SpamResult, "RubySpamAssassin/spam_result")
autoload(:ReportParser, "RubySpamAssassin/report_parser")
end
21 changes: 21 additions & 0 deletions lib/RubySpamAssassin/report_parser.rb
@@ -0,0 +1,21 @@
class RubySpamAssassin::ReportParser
LINE_REGEXP = /-$/
RULE_REGEXP = /[0-9]*[.][0-9]\s\w*\s/

def self.parse(report_text)
last_part = report_text.split(LINE_REGEXP)[1].sub(/^[\n\r]./,'').chomp.chomp
pts_rules = last_part.gsub(RULE_REGEXP).collect { |sub| sub.chomp(' ') }
rule_texts = last_part.split(RULE_REGEXP).collect { |text| text.delete("\n").squeeze.chomp(' ').sub(/^\s/, '') }

rules = []
pts_rules.each_with_index do |pts_rule, i|
rules << {
:pts => pts_rule.split(' ')[0],
:rule => pts_rule.split(' ')[1],
:text => rule_texts[i + 1]
}
end

rules
end
end
81 changes: 81 additions & 0 deletions lib/RubySpamAssassin/spam_client.rb
@@ -0,0 +1,81 @@
class RubySpamAssassin::SpamClient
require 'socket'
require 'timeout'

def initialize(host="localhost", port=783, timeout=5)
@port = port
@host = host
@timeout =timeout
@socket = TCPSocket.open(@host, @port)
end

def reconnect
@socket = @socket || TCPSocket.open(@host, @port)
end

def send_symbol(message)
protocol_response = send_message("SYMBOLS", message)
result = process_headers protocol_response[0...2]
result.tags = protocol_response[3...-1].join(" ").split(',')
end

def check(message)
protocol_response = send_message("CHECK", message)
result = process_headers protocol_response[0...2]
end

def report(message)
protocol_response = send_message("REPORT", message)
result = process_headers protocol_response[0...2]
result.report = protocol_response[3..-1].join
result.rules = RubySpamAssassin::ReportParser.parse(result.report)
result
end

def report_ifspam(message)
result = report(message).spam?
end

def skip
protocol_response = send_message("SKIP", message)
end

def ping
protocol_response = send_message("PING", message)
result = process_headers protocol_response[0]
end

alias :process :report

private
def send_message(command, message)
length = message.length
@socket.write(command + " SPAMC/1.2\r\n")
@socket.write("Content-length: " + length.to_s + "\r\n\r\n")
@socket.write(message)
@socket.shutdown(1) #have to shutdown sending side to get response
response = @socket.readlines
@socket.close #might as well close it now

response
end

def process_headers(headers)
result = RubySpamAssassin::SpamResult.new
headers.each do |line|
case line.chomp
when /(.+)\/(.+) (.+) (.+)/ then
result.response_version = $2
result.response_code = $3
result.response_message = $4
when /^Spam: (.+) ; (.+) . (.+)$/ then
result.score = $2
result.spam = $1
result.threshold = $3
when /Content-length: (.+)/ then
result.content_length = $1
end
end
result
end
end
17 changes: 17 additions & 0 deletions lib/RubySpamAssassin/spam_result.rb
@@ -0,0 +1,17 @@
class RubySpamAssassin::SpamResult
attr_accessor :response_version,
:response_code,
:response_message,
:spam,
:score,
:threshold,
:tags,
:report,
:content_length,
:rules

#returns true if the message was spam, otherwise false
def spam?
(@spam == "True" || @spam == "Yes") ? true : false
end
end

0 comments on commit 00bf9ad

Please sign in to comment.