em-spamc 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'eventmachine'
4
+
5
+ group :development do
6
+ gem 'bundler'
7
+ gem 'jeweler', '~> 1.8.4'
8
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Scott Tadman, The Working Group Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,8 @@
1
+ #em-spamc
2
+
3
+ SpamAssassin Connection for EventMachine
4
+
5
+ ##Copyright
6
+
7
+ Copyright (c) 2013 Scott Tadman, Stephan Leroux, The Working Group Inc.
8
+ See LICENSE.txt for further details.
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+
6
+ begin
7
+ Bundler.setup(:default, :development)
8
+ rescue Bundler::BundlerError => e
9
+ $stderr.puts e.message
10
+ $stderr.puts "Run `bundle install` to install missing gems"
11
+ exit e.status_code
12
+ end
13
+
14
+ require 'rake'
15
+ require 'jeweler'
16
+
17
+ Jeweler::Tasks.new do |gem|
18
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
19
+ gem.name = "em-spamc"
20
+ gem.homepage = "http://github.com/twg/em-spamc"
21
+ gem.license = "MIT"
22
+ gem.summary = %Q{SpamAssassin Connection for EventMachine}
23
+ gem.description = %Q{Implementation of the "spamc" protocol for EventMachine}
24
+ gem.email = "github@tadman.ca"
25
+ gem.authors = [ "Scott Tadman", "Stephan Leroux" ]
26
+ # dependencies defined in Gemfile
27
+ end
28
+
29
+ Jeweler::RubygemsDotOrgTasks.new
30
+
31
+ require 'rake/testtask'
32
+
33
+ Rake::TestTask.new(:test) do |test|
34
+ test.libs << 'lib' << 'test'
35
+ test.pattern = 'test/**/test_*.rb'
36
+ test.verbose = true
37
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
data/em-spamc.gemspec ADDED
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "em-spamc"
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Scott Tadman", "Stephan Leroux"]
12
+ s.date = "2013-04-17"
13
+ s.description = "Implementation of the \"spamc\" protocol for EventMachine"
14
+ s.email = "github@tadman.ca"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "LICENSE.txt",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "em-spamc.gemspec",
27
+ "lib/em-spamc.rb",
28
+ "lib/em_spamc.rb",
29
+ "lib/em_spamc/connection.rb",
30
+ "lib/em_spamc/header_parser.rb",
31
+ "lib/em_spamc/report_parser.rb",
32
+ "lib/em_spamc/result.rb",
33
+ "test/config/spamassassin.yml",
34
+ "test/config/spamassassin.yml.example",
35
+ "test/examples/messages/sample.txt",
36
+ "test/examples/messages/zero-score.txt",
37
+ "test/examples/reports/spam-report.txt",
38
+ "test/examples/reports/zero-report.txt",
39
+ "test/examples/sample.txt",
40
+ "test/helper.rb",
41
+ "test/unit/test_em_spamc.rb",
42
+ "test/unit/test_em_spamc_connection.rb",
43
+ "test/unit/test_em_spamc_header_parser.rb",
44
+ "test/unit/test_em_spamc_report_parser.rb",
45
+ "test/unit/test_em_spamc_result.rb"
46
+ ]
47
+ s.homepage = "http://github.com/twg/em-spamc"
48
+ s.licenses = ["MIT"]
49
+ s.require_paths = ["lib"]
50
+ s.rubygems_version = "1.8.25"
51
+ s.summary = "SpamAssassin Connection for EventMachine"
52
+
53
+ if s.respond_to? :specification_version then
54
+ s.specification_version = 3
55
+
56
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
+ s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
58
+ s.add_development_dependency(%q<bundler>, [">= 0"])
59
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
60
+ else
61
+ s.add_dependency(%q<eventmachine>, [">= 0"])
62
+ s.add_dependency(%q<bundler>, [">= 0"])
63
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
64
+ end
65
+ else
66
+ s.add_dependency(%q<eventmachine>, [">= 0"])
67
+ s.add_dependency(%q<bundler>, [">= 0"])
68
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
69
+ end
70
+ end
71
+
data/lib/em-spamc.rb ADDED
@@ -0,0 +1 @@
1
+ require 'em_spamc'
data/lib/em_spamc.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'fiber'
2
+
3
+ module EmSpamc
4
+ autoload(:Connection, 'em_spamc/connection')
5
+ autoload(:Result, 'em_spamc/result')
6
+ autoload(:ReportParser, 'em_spamc/report_parser')
7
+ autoload(:HeaderParser, 'em_spamc/header_parser')
8
+ end
@@ -0,0 +1,105 @@
1
+ require 'fiber'
2
+ require 'eventmachine'
3
+
4
+ class EmSpamc::Connection < EventMachine::Connection
5
+ # == Constants ============================================================
6
+
7
+ PROTO_VERSION = 'SPAMC/1.2'.freeze
8
+
9
+ DEFAULT_OPTIONS = {
10
+ :host => 'localhost',
11
+ :port => 783
12
+ }.freeze
13
+
14
+ # == Properties ===========================================================
15
+
16
+ attr_reader :error
17
+
18
+ # == Class Methods ========================================================
19
+
20
+ def self.check(message, options = nil)
21
+ request('CHECK', message, options)
22
+ end
23
+
24
+ def self.symbols(message, options = nil)
25
+ request('SYMBOLS', message, options)
26
+ end
27
+
28
+ def self.report(message, options = nil)
29
+ request('REPORT', message, options)
30
+ end
31
+
32
+ def self.ping(options = nil)
33
+ request('PING', nil, options)
34
+ end
35
+
36
+ def self.request(command, message, options = nil)
37
+ if (options)
38
+ options = DEFAULT_OPTIONS.merge(options)
39
+ else
40
+ options = DEFAULT_OPTIONS
41
+ end
42
+
43
+ EventMachine.connect(
44
+ options[:host],
45
+ options[:port],
46
+ self,
47
+ [
48
+ Fiber.current,
49
+ command,
50
+ message
51
+ ]
52
+ )
53
+
54
+ Fiber.yield
55
+ end
56
+
57
+ # == Instance Methods =====================================================
58
+
59
+ def initialize(options)
60
+ @fiber = options[0]
61
+ @command = options[1]
62
+ @message = options[2] && (options[2].gsub(/\r?\n/, "\r\n") + "\r\n")
63
+ end
64
+
65
+ # EventMachine hook that's engaged after the client is initialized.
66
+ def post_init
67
+ if (error?)
68
+ @fiber.resume
69
+ else
70
+ send_data("#{@command} #{PROTO_VERSION}\r\n")
71
+
72
+ if (@message)
73
+ send_data("Content-length: #{@message.bytesize}\r\n")
74
+ send_data("\r\n")
75
+ send_data(@message)
76
+ else
77
+ send_data("\r\n")
78
+ end
79
+ end
80
+ end
81
+
82
+ def receive_data(data)
83
+ @data ||= ''
84
+ @data << data
85
+ end
86
+
87
+ def unbind
88
+ result = EmSpamc::Result.new
89
+
90
+ if (@data)
91
+ result.headers = EmSpamc::HeaderParser.parse(@data)
92
+
93
+ case (@command)
94
+ when 'REPORT'
95
+ result.report = EmSpamc::ReportParser.parse(@data)
96
+ end
97
+ end
98
+
99
+ @fiber.resume(result)
100
+ end
101
+
102
+ def error?
103
+ !!@error
104
+ end
105
+ end
@@ -0,0 +1,27 @@
1
+ class EmSpamc::HeaderParser
2
+ def self.parse(data)
3
+ lines = data.split(/\r?\n/)
4
+ headers = { }
5
+
6
+ lines.each do |line|
7
+ case (line)
8
+ when /^SPAMD\/(\d+\.\d+) (.*)/
9
+ code, message = $2.split(/\s+/)
10
+
11
+ headers[:version] = $1
12
+ headers[:code] = code.match(/\d/) ? code.to_i : code
13
+ headers[:message] = message
14
+ when /^(\S+): (.*)\s;\s([0-9]*.[0-9]*)/
15
+ headers[:spam] = $2 == 'True'
16
+ headers[:score] = $3.to_f
17
+ end
18
+ end
19
+
20
+ # Special case: if we only have one header line back, means score of 0
21
+ if (lines.length == 1)
22
+ headers.merge!(:score => 0.0, :spam => false)
23
+ end
24
+
25
+ headers
26
+ end
27
+ end
@@ -0,0 +1,37 @@
1
+ class EmSpamc::ReportParser
2
+ LINE_REGEXP = /-$/
3
+ RULE_REGEXP = /[0-9]*[.][0-9]\s\w*\s/
4
+
5
+ def self.parse(data)
6
+ split_data = data.split(LINE_REGEXP)
7
+
8
+ # Only status line is back - no report returned, no rules
9
+ if (split_data.length == 1)
10
+ return [ ]
11
+ end
12
+
13
+ last_part = split_data[1].sub(/^[\n\r]./,'').chomp.chomp
14
+
15
+ points_rules = last_part.gsub(RULE_REGEXP).collect do |sub|
16
+ sub.chomp(' ')
17
+ end
18
+
19
+ rule_texts = last_part.split(RULE_REGEXP).collect do |text|
20
+ text.delete("\n").squeeze.chomp(' ').sub(/^\s/, '')
21
+ end
22
+
23
+ rules = [ ]
24
+
25
+ points_rules.each_with_index do |points_rule, i|
26
+ split = points_rule.split(' ')
27
+
28
+ rules << {
29
+ :pts => split[0].to_f,
30
+ :rule => split[1],
31
+ :text => rule_texts[i + 1]
32
+ }
33
+ end
34
+
35
+ rules
36
+ end
37
+ end
@@ -0,0 +1,30 @@
1
+ class EmSpamc::Result
2
+ # == Constants ============================================================
3
+
4
+ ATTRIBUTES = [
5
+ :headers,
6
+ :threshold,
7
+ :tags,
8
+ :report
9
+ ].freeze
10
+
11
+ # == Instance Methods =====================================================
12
+
13
+ def initialize(attributes = nil)
14
+ @attributes = attributes || { }
15
+ end
16
+
17
+ ATTRIBUTES.each do |name|
18
+ define_method(name) do
19
+ @attributes[name]
20
+ end
21
+
22
+ define_method(:"#{name}=") do |value|
23
+ @attributes[name] = value
24
+ end
25
+ end
26
+
27
+ def spam?
28
+ headers and headers[:spam]
29
+ end
30
+ end
@@ -0,0 +1,4 @@
1
+ #host: 127.0.0.1
2
+ #port: 9000
3
+ host: scan1.blue.postageapp.com
4
+ port: 49000
@@ -0,0 +1,2 @@
1
+ host: 127.0.0.1
2
+ port: 783
@@ -0,0 +1,5 @@
1
+ From: Example Person <example@example.com>
2
+ To: Someone <someone@example.net>
3
+ Subject: Harmless Example Message
4
+
5
+ This is not a very exciting message, but here it is.
@@ -0,0 +1,23 @@
1
+ Delivered-To: test@test.com
2
+ Received: by 10.180.189.141 with SMTP id gi13csp63398wic;
3
+ Thu, 14 Mar 2013 08:46:03 -0700 (PDT)
4
+ Return-Path: <sender@testapp.com>
5
+ Received: from outbound6.mailer.testapp.com (outbound6.mailer.testapp.com. [199.87.247.245])
6
+ by mx.google.com with ESMTPS id ep2si2060874qeb.27.2013.03.14.08.46.02
7
+ (version=TLSv1 cipher=RC4-SHA bits=128/128);
8
+ Thu, 14 Mar 2013 08:46:03 -0700 (PDT)
9
+ Received-SPF: pass (google.com: domain of sender@testapp.com designates 199.87.247.245 as permitted sender) client-ip=199.87.247.245;
10
+ Authentication-Results: mx.google.com;
11
+ spf=pass (google.com: domain of sender@testapp.com designates 199.87.247.245 as permitted sender) smtp.mail=sender@testapp.com
12
+ Date: Thu, 14 Mar 2013 15:46:02 +0000
13
+ To: person@test.com
14
+ Message-ID: <17ee1aab2074416831081825744deffb026f0dfe@outbound6.mailer.testapp.com>
15
+ Subject: Test Message RECIPIENT SUBJECT person@test.com
16
+ Mime-Version: 1.0
17
+ Content-Type: text/plain
18
+ Content-Transfer-Encoding: 7bit
19
+ Auto-Submitted: auto-generated
20
+ From: sender@testapp.com
21
+
22
+ Hey!
23
+
@@ -0,0 +1,27 @@
1
+ SPAMD/1.1 0 EX_OK
2
+ Spam: True ; 18.1 / 5.0
3
+
4
+ Spam detection software, running on the system "scan1.blue.postageapp.com", has
5
+ identified this incoming email as possible spam. The original message
6
+ has been attached to this so you can view it (if it isn't spam) or label
7
+ similar future email. If you have any questions, see
8
+ the administrator of that system for details.
9
+
10
+ Content preview: I found a way to "get even" with the financial market! I've
11
+ stumbled to this page: http://advertismenmore.com/ This dude is just crazy,
12
+ he will teach you how you can make up to $87 every minute! No string attached
13
+ and zero risk for you! and the best fact, its 100% free [...]
14
+
15
+ Content analysis details: (18.1 points, 5.0 required)
16
+
17
+ pts rule name description
18
+ ---- ---------------------- --------------------------------------------------
19
+ 3.6 RCVD_IN_PBL RBL: Received via a relay in Spamhaus PBL
20
+ [91.115.88.37 listed in zen.spamhaus.org]
21
+ 0.7 RCVD_IN_XBL RBL: Received via a relay in Spamhaus XBL
22
+ 0.0 RCVD_IN_DNSWL_BLOCKED RBL: ADMINISTRATOR NOTICE: The query to DNSWL
23
+ was blocked. See
24
+ http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block
25
+ for more information.
26
+ [91.115.88.37 listed in list.dnswl.org]
27
+ 1.6 RCVD_IN_BRBL_LASTEXT RBL: RCVD_IN_BRBL_LASTEXT
@@ -0,0 +1 @@
1
+ SPAMD/1.1 0 EX_OK
@@ -0,0 +1,5 @@
1
+ From: Example Person <example@example.com>
2
+ To: Someone <someone@example.net>
3
+ Subject: Harmless Example Message
4
+
5
+ This is not a very exciting message, but here it is.
data/test/helper.rb ADDED
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+
12
+ require 'yaml'
13
+ require 'test/unit'
14
+
15
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
16
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
17
+
18
+ require 'em-spamc'
19
+ require 'eventmachine'
20
+
21
+ class Test::Unit::TestCase
22
+ def example_message(name)
23
+ message_path = File.expand_path("examples/messages/#{name}.txt", File.dirname(__FILE__))
24
+
25
+ File.open(message_path).read
26
+ end
27
+
28
+ def example_report(name)
29
+ message_path = File.expand_path("examples/reports/#{name}.txt", File.dirname(__FILE__))
30
+
31
+ File.open(message_path).read
32
+ end
33
+
34
+ def connection_options
35
+ config_file = File.expand_path('config/spamassassin.yml', File.dirname(__FILE__))
36
+
37
+ Hash[
38
+ YAML.load(File.open(config_file)).collect do |key, value|
39
+ [ key.to_sym, value ]
40
+ end
41
+ ]
42
+ end
43
+
44
+ def eventmachine
45
+ EventMachine.run do
46
+ Fiber.new do
47
+ yield
48
+ EventMachine.stop_event_loop
49
+ end.resume
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,4 @@
1
+ require_relative '../helper'
2
+
3
+ class TestEmSpamc < Test::Unit::TestCase
4
+ end
@@ -0,0 +1,44 @@
1
+ require_relative '../helper'
2
+
3
+ class TestEmSpamcConnection < Test::Unit::TestCase
4
+ def test_ping
5
+ eventmachine do
6
+ result = EmSpamc::Connection.ping(connection_options)
7
+
8
+ assert result
9
+ end
10
+ end
11
+
12
+ def test_symbols
13
+ eventmachine do
14
+ message = example_message(:sample)
15
+ result = EmSpamc::Connection.symbols(message, connection_options)
16
+
17
+ assert result
18
+ end
19
+ end
20
+
21
+ def test_report
22
+ eventmachine do
23
+ message = example_message(:sample)
24
+
25
+ result = EmSpamc::Connection.report(message, connection_options)
26
+
27
+ assert result
28
+ assert result.report
29
+ assert result.headers[:score]
30
+
31
+ assert_equal result.spam?, false
32
+ end
33
+ end
34
+
35
+ def test_check
36
+ eventmachine do
37
+ message = example_message(:sample)
38
+
39
+ result = EmSpamc::Connection.check(message)
40
+
41
+ assert result
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,18 @@
1
+ require_relative '../helper'
2
+
3
+ class TestEmSpamcHeaderParser < Test::Unit::TestCase
4
+ def test_score_header
5
+ report = example_report('spam-report')
6
+ headers = EmSpamc::HeaderParser.parse(report)
7
+ assert_equal headers[:spam], true
8
+ assert headers[:score] >= 0
9
+ end
10
+
11
+ def test_zero_header
12
+ report = example_report('zero-report')
13
+ headers = EmSpamc::HeaderParser.parse(report)
14
+ assert headers[:score]
15
+ assert_equal headers[:score], 0
16
+ assert_equal headers[:spam], false
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ require_relative '../helper'
2
+
3
+ class TestEmSpamcReportParser < Test::Unit::TestCase
4
+ def test_parsing_spam
5
+ eventmachine do
6
+ report = example_report('spam-report')
7
+ rules = EmSpamc::ReportParser.parse(report)
8
+ assert rules
9
+ end
10
+ end
11
+
12
+ def test_parsing_zero
13
+ eventmachine do
14
+ report = example_report('zero-report')
15
+ rules = EmSpamc::ReportParser.parse(report)
16
+ assert rules
17
+ assert_equal rules, []
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require_relative '../helper'
2
+
3
+ class TestEmSpamcConnection < Test::Unit::TestCase
4
+ def test_defaults
5
+ result = EmSpamc::Result.new
6
+
7
+ assert_equal nil, result.headers
8
+ assert_equal nil, result.report
9
+ end
10
+
11
+ def test_assignment
12
+ result = EmSpamc::Result.new
13
+
14
+ assert_equal nil, result.headers
15
+
16
+ result.headers = { :code => 0 }
17
+
18
+ assert_equal 0, result.headers[:code]
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-spamc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Scott Tadman
9
+ - Stephan Leroux
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-04-17 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: eventmachine
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: bundler
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: jeweler
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.8.4
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: 1.8.4
63
+ description: Implementation of the "spamc" protocol for EventMachine
64
+ email: github@tadman.ca
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files:
68
+ - LICENSE.txt
69
+ - README.md
70
+ files:
71
+ - .document
72
+ - Gemfile
73
+ - LICENSE.txt
74
+ - README.md
75
+ - Rakefile
76
+ - VERSION
77
+ - em-spamc.gemspec
78
+ - lib/em-spamc.rb
79
+ - lib/em_spamc.rb
80
+ - lib/em_spamc/connection.rb
81
+ - lib/em_spamc/header_parser.rb
82
+ - lib/em_spamc/report_parser.rb
83
+ - lib/em_spamc/result.rb
84
+ - test/config/spamassassin.yml
85
+ - test/config/spamassassin.yml.example
86
+ - test/examples/messages/sample.txt
87
+ - test/examples/messages/zero-score.txt
88
+ - test/examples/reports/spam-report.txt
89
+ - test/examples/reports/zero-report.txt
90
+ - test/examples/sample.txt
91
+ - test/helper.rb
92
+ - test/unit/test_em_spamc.rb
93
+ - test/unit/test_em_spamc_connection.rb
94
+ - test/unit/test_em_spamc_header_parser.rb
95
+ - test/unit/test_em_spamc_report_parser.rb
96
+ - test/unit/test_em_spamc_result.rb
97
+ homepage: http://github.com/twg/em-spamc
98
+ licenses:
99
+ - MIT
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ! '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 1.8.25
119
+ signing_key:
120
+ specification_version: 3
121
+ summary: SpamAssassin Connection for EventMachine
122
+ test_files: []